misoai-web 1.0.5 → 1.5.6

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.
Files changed (72) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +9 -353
  3. package/bin/midscene-playground +2 -2
  4. package/dist/es/agent.js +9 -191
  5. package/dist/es/agent.js.map +1 -1
  6. package/dist/es/bridge-mode-browser.js +3 -3
  7. package/dist/es/bridge-mode-browser.js.map +1 -1
  8. package/dist/es/bridge-mode.js +11 -193
  9. package/dist/es/bridge-mode.js.map +1 -1
  10. package/dist/es/chrome-extension.js +10 -192
  11. package/dist/es/chrome-extension.js.map +1 -1
  12. package/dist/es/index.js +13 -195
  13. package/dist/es/index.js.map +1 -1
  14. package/dist/es/midscene-playground.js +9 -191
  15. package/dist/es/midscene-playground.js.map +1 -1
  16. package/dist/es/midscene-server.js.map +1 -1
  17. package/dist/es/playground.js +9 -191
  18. package/dist/es/playground.js.map +1 -1
  19. package/dist/es/playwright-report.js.map +1 -1
  20. package/dist/es/playwright.js +10 -192
  21. package/dist/es/playwright.js.map +1 -1
  22. package/dist/es/puppeteer-agent-launcher.js +13 -195
  23. package/dist/es/puppeteer-agent-launcher.js.map +1 -1
  24. package/dist/es/puppeteer.js +13 -195
  25. package/dist/es/puppeteer.js.map +1 -1
  26. package/dist/es/ui-utils.js.map +1 -1
  27. package/dist/es/utils.js.map +1 -1
  28. package/dist/es/yaml.js +5 -3
  29. package/dist/es/yaml.js.map +1 -1
  30. package/dist/lib/agent.js +9 -191
  31. package/dist/lib/agent.js.map +1 -1
  32. package/dist/lib/bridge-mode-browser.js +3 -3
  33. package/dist/lib/bridge-mode-browser.js.map +1 -1
  34. package/dist/lib/bridge-mode.js +11 -193
  35. package/dist/lib/bridge-mode.js.map +1 -1
  36. package/dist/lib/chrome-extension.js +10 -192
  37. package/dist/lib/chrome-extension.js.map +1 -1
  38. package/dist/lib/index.js +13 -195
  39. package/dist/lib/index.js.map +1 -1
  40. package/dist/lib/midscene-playground.js +9 -191
  41. package/dist/lib/midscene-playground.js.map +1 -1
  42. package/dist/lib/midscene-server.js.map +1 -1
  43. package/dist/lib/playground.js +9 -191
  44. package/dist/lib/playground.js.map +1 -1
  45. package/dist/lib/playwright-report.js.map +1 -1
  46. package/dist/lib/playwright.js +10 -192
  47. package/dist/lib/playwright.js.map +1 -1
  48. package/dist/lib/puppeteer-agent-launcher.js +13 -195
  49. package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
  50. package/dist/lib/puppeteer.js +13 -195
  51. package/dist/lib/puppeteer.js.map +1 -1
  52. package/dist/lib/ui-utils.js.map +1 -1
  53. package/dist/lib/utils.js.map +1 -1
  54. package/dist/lib/yaml.js +5 -3
  55. package/dist/lib/yaml.js.map +1 -1
  56. package/dist/types/agent.d.ts +1 -27
  57. package/dist/types/bridge-mode-browser.d.ts +2 -2
  58. package/dist/types/bridge-mode.d.ts +2 -2
  59. package/dist/types/{browser-a1877d18.d.ts → browser-aec1055d.d.ts} +1 -1
  60. package/dist/types/chrome-extension.d.ts +2 -2
  61. package/dist/types/index.d.ts +1 -1
  62. package/dist/types/midscene-server.d.ts +1 -1
  63. package/dist/types/{page-663ece08.d.ts → page-86ab0fe1.d.ts} +34 -34
  64. package/dist/types/playground.d.ts +2 -2
  65. package/dist/types/playwright.d.ts +1 -1
  66. package/dist/types/puppeteer-agent-launcher.d.ts +1 -1
  67. package/dist/types/puppeteer.d.ts +1 -1
  68. package/dist/types/utils.d.ts +1 -1
  69. package/dist/types/yaml.d.ts +1 -1
  70. package/iife-script/htmlElement.js +2 -2
  71. package/iife-script/htmlElementDebug.js +2 -2
  72. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"mappings":";AAWO,SAAS,QAAQ,MAAqB;AAC3C,SAAO,KAAK,WAAW,KAAK,YAAY,SACpC,GAAG,KAAK,IAAI,MAAM,KAAK,WAAW,EAAE,KACpC,KAAK;AACX;AAEO,SAAS,eACd,OAC0C;AAE1C,QAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAIlD,SAAO,KAAK,OAAO,CAAC,KAA+C,MAAM;AACvE,UAAM,cAAc,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,SAAS;AACpE,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,YAAY,CAAC,CAAC;AAAA,IACtD;AACA,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,OAAO,CAAC,CAAC;AAAA,IACjD;AACA,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,QAAQ,CAAC,CAAC;AAAA,IAClD;AACA,WAAO,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAAA,EAChC,GAAG,CAAC,CAAC;AACP;AAEO,SAAS,eAAe,QAA8B;AAC3D,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO,OAAO;AAChB;AAEO,SAAS,eAAe,aAAyC;AACtE,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AACA,SAAO,GAAG,YAAY,aAAa,MAAM,KAAK,YAAY,cAAc,MAAM,KAAK,YAAY,YAAY,kBAAkB;AAC/H;AAEO,SAAS,aACd,MAcA,QACA;AACA,MAAI,QAAQ;AACV,WAAO,GAAG,IAAI,MAAM,MAAM;AAAA,EAC5B;AACA,SAAO;AACT;AAEO,SAAS,SAAS,MAAqB;AAC5C,MAAI;AACJ,MAAI,KAAK,SAAS,YAAY;AAC5B,YAAS,MAAgC,OAAO;AAAA,EAClD;AAEA,MAAI,KAAK,SAAS,WAAW;AAC3B,YACG,MAAqC,OAAO,UAC5C,MAAqC,OAAO,MAC5C,MAAoC,OAAO,cAC3C,MAAwC,OAAO;AAAA,EACpD;AAEA,MAAI,KAAK,SAAS,UAAU;AAC1B,UAAM,SAAU,MAA8B;AAC9C,UAAM,YAAY,SAAS,eAAe,MAAM,IAAI;AAEpD,YAAQ,KAAK,WAAW;AACxB,QAAI,OAAQ,MAA8B,OAAO,WAAW,UAAU;AACpE,cAAQ,GAAI,MAA8B,OAAO,MAAM;AAAA,IACzD,WACE,OAAQ,MAA8B,OAAO,eAAe,UAC5D;AACA,cAAQ,eAAgB,MAA8B,KAAK;AAAA,IAC7D,WACE,OAAQ,MAA8B,OAAO,UAAU,aACvD;AACA,cAAS,MAA8B,OAAO;AAAA,IAChD;AAEA,QAAI,WAAW;AACb,UAAI,OAAO;AACT,gBAAQ,GAAG,SAAS,MAAM,KAAK;AAAA,MACjC,OAAO;AACL,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,UAAU;AAAa,WAAO;AACzC,SAAO,OAAO,UAAU,WACpB,QACA,KAAK,UAAU,OAAO,QAAW,CAAC;AACxC;AAEO,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","names":[],"ignoreList":[],"sources":["../../src/common/ui-utils.ts"],"sourcesContent":["import type {\r\n DetailedLocateParam,\r\n ExecutionTask,\r\n ExecutionTaskAction,\r\n ExecutionTaskInsightAssertion,\r\n ExecutionTaskInsightLocate,\r\n ExecutionTaskInsightQuery,\r\n ExecutionTaskPlanning,\r\n PlanningActionParamScroll,\r\n} from 'misoai-core';\r\n\r\nexport function typeStr(task: ExecutionTask) {\r\n return task.subType && task.subType !== 'Plan'\r\n ? `${task.type} / ${task.subType || ''}`\r\n : task.type;\r\n}\r\n\r\nexport function getKeyCommands(\r\n value: string | string[],\r\n): Array<{ key: string; command?: string }> {\r\n // Ensure value is an array of keys\r\n const keys = Array.isArray(value) ? value : [value];\r\n\r\n // Process each key to attach a corresponding command if needed, based on the presence of 'Meta' or 'Control' in the keys array.\r\n // ref: https://github.com/puppeteer/puppeteer/pull/9357/files#diff-32cf475237b000f980eb214a0a823e45a902bddb7d2426d677cae96397aa0ae4R94\r\n return keys.reduce((acc: Array<{ key: string; command?: string }>, k) => {\r\n const includeMeta = keys.includes('Meta') || keys.includes('Control');\r\n if (includeMeta && (k === 'a' || k === 'A')) {\r\n return acc.concat([{ key: k, command: 'SelectAll' }]);\r\n }\r\n if (includeMeta && (k === 'c' || k === 'C')) {\r\n return acc.concat([{ key: k, command: 'Copy' }]);\r\n }\r\n if (includeMeta && (k === 'v' || k === 'V')) {\r\n return acc.concat([{ key: k, command: 'Paste' }]);\r\n }\r\n return acc.concat([{ key: k }]);\r\n }, []);\r\n}\r\n\r\nexport function locateParamStr(locate?: DetailedLocateParam) {\r\n if (!locate) {\r\n return '';\r\n }\r\n\r\n if (typeof locate === 'string') {\r\n return locate;\r\n }\r\n\r\n return locate.prompt;\r\n}\r\n\r\nexport function scrollParamStr(scrollParam?: PlanningActionParamScroll) {\r\n if (!scrollParam) {\r\n return '';\r\n }\r\n return `${scrollParam.direction || 'down'}, ${scrollParam.scrollType || 'once'}, ${scrollParam.distance || 'distance-not-set'}`;\r\n}\r\n\r\nexport function taskTitleStr(\r\n type:\r\n | 'Tap'\r\n | 'Hover'\r\n | 'Input'\r\n | 'KeyboardPress'\r\n | 'Scroll'\r\n | 'Action'\r\n | 'Query'\r\n | 'Assert'\r\n | 'WaitFor'\r\n | 'Locate'\r\n | 'Boolean'\r\n | 'Number'\r\n | 'String',\r\n prompt: string,\r\n) {\r\n if (prompt) {\r\n return `${type} - ${prompt}`;\r\n }\r\n return type;\r\n}\r\n\r\nexport function paramStr(task: ExecutionTask) {\r\n let value: string | undefined | object;\r\n if (task.type === 'Planning') {\r\n value = (task as ExecutionTaskPlanning)?.param?.userInstruction;\r\n }\r\n\r\n if (task.type === 'Insight') {\r\n value =\r\n (task as ExecutionTaskInsightLocate)?.param?.prompt ||\r\n (task as ExecutionTaskInsightLocate)?.param?.id ||\r\n (task as ExecutionTaskInsightQuery)?.param?.dataDemand ||\r\n (task as ExecutionTaskInsightAssertion)?.param?.assertion;\r\n }\r\n\r\n if (task.type === 'Action') {\r\n const locate = (task as ExecutionTaskAction)?.locate;\r\n const locateStr = locate ? locateParamStr(locate) : '';\r\n\r\n value = task.thought || '';\r\n if (typeof (task as ExecutionTaskAction)?.param?.timeMs === 'number') {\r\n value = `${(task as ExecutionTaskAction)?.param?.timeMs}ms`;\r\n } else if (\r\n typeof (task as ExecutionTaskAction)?.param?.scrollType === 'string'\r\n ) {\r\n value = scrollParamStr((task as ExecutionTaskAction)?.param);\r\n } else if (\r\n typeof (task as ExecutionTaskAction)?.param?.value !== 'undefined'\r\n ) {\r\n value = (task as ExecutionTaskAction)?.param?.value;\r\n }\r\n\r\n if (locateStr) {\r\n if (value) {\r\n value = `${locateStr} - ${value}`;\r\n } else {\r\n value = locateStr;\r\n }\r\n }\r\n }\r\n\r\n if (typeof value === 'undefined') return '';\r\n return typeof value === 'string'\r\n ? value\r\n : JSON.stringify(value, undefined, 2);\r\n}\r\n\r\nexport const limitOpenNewTabScript = `\r\nif (!window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__) {\r\n window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__ = true;\r\n\r\n // Intercept the window.open method (only once)\r\n window.open = function(url) {\r\n console.log('Blocked window.open:', url);\r\n window.location.href = url;\r\n return null;\r\n };\r\n\r\n // Block all a tag clicks with target=\"_blank\" (only once)\r\n document.addEventListener('click', function(e) {\r\n const target = e.target.closest('a');\r\n if (target && target.target === '_blank') {\r\n e.preventDefault();\r\n console.log('Blocked new tab:', target.href);\r\n window.location.href = target.href;\r\n target.removeAttribute('target');\r\n }\r\n }, true);\r\n}\r\n`;\r\n"]}
1
+ {"version":3,"mappings":";AAWO,SAAS,QAAQ,MAAqB;AAC3C,SAAO,KAAK,WAAW,KAAK,YAAY,SACpC,GAAG,KAAK,IAAI,MAAM,KAAK,WAAW,EAAE,KACpC,KAAK;AACX;AAEO,SAAS,eACd,OAC0C;AAE1C,QAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAIlD,SAAO,KAAK,OAAO,CAAC,KAA+C,MAAM;AACvE,UAAM,cAAc,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,SAAS;AACpE,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,YAAY,CAAC,CAAC;AAAA,IACtD;AACA,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,OAAO,CAAC,CAAC;AAAA,IACjD;AACA,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,QAAQ,CAAC,CAAC;AAAA,IAClD;AACA,WAAO,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAAA,EAChC,GAAG,CAAC,CAAC;AACP;AAEO,SAAS,eAAe,QAA8B;AAC3D,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO,OAAO;AAChB;AAEO,SAAS,eAAe,aAAyC;AACtE,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AACA,SAAO,GAAG,YAAY,aAAa,MAAM,KAAK,YAAY,cAAc,MAAM,KAAK,YAAY,YAAY,kBAAkB;AAC/H;AAEO,SAAS,aACd,MAcA,QACA;AACA,MAAI,QAAQ;AACV,WAAO,GAAG,IAAI,MAAM,MAAM;AAAA,EAC5B;AACA,SAAO;AACT;AAEO,SAAS,SAAS,MAAqB;AAC5C,MAAI;AACJ,MAAI,KAAK,SAAS,YAAY;AAC5B,YAAS,MAAgC,OAAO;AAAA,EAClD;AAEA,MAAI,KAAK,SAAS,WAAW;AAC3B,YACG,MAAqC,OAAO,UAC5C,MAAqC,OAAO,MAC5C,MAAoC,OAAO,cAC3C,MAAwC,OAAO;AAAA,EACpD;AAEA,MAAI,KAAK,SAAS,UAAU;AAC1B,UAAM,SAAU,MAA8B;AAC9C,UAAM,YAAY,SAAS,eAAe,MAAM,IAAI;AAEpD,YAAQ,KAAK,WAAW;AACxB,QAAI,OAAQ,MAA8B,OAAO,WAAW,UAAU;AACpE,cAAQ,GAAI,MAA8B,OAAO,MAAM;AAAA,IACzD,WACE,OAAQ,MAA8B,OAAO,eAAe,UAC5D;AACA,cAAQ,eAAgB,MAA8B,KAAK;AAAA,IAC7D,WACE,OAAQ,MAA8B,OAAO,UAAU,aACvD;AACA,cAAS,MAA8B,OAAO;AAAA,IAChD;AAEA,QAAI,WAAW;AACb,UAAI,OAAO;AACT,gBAAQ,GAAG,SAAS,MAAM,KAAK;AAAA,MACjC,OAAO;AACL,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,UAAU;AAAa,WAAO;AACzC,SAAO,OAAO,UAAU,WACpB,QACA,KAAK,UAAU,OAAO,QAAW,CAAC;AACxC;AAEO,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","names":[],"ignoreList":[],"sources":["../../src/common/ui-utils.ts"],"sourcesContent":["import type {\n DetailedLocateParam,\n ExecutionTask,\n ExecutionTaskAction,\n ExecutionTaskInsightAssertion,\n ExecutionTaskInsightLocate,\n ExecutionTaskInsightQuery,\n ExecutionTaskPlanning,\n PlanningActionParamScroll,\n} from 'misoai-core';\n\nexport function typeStr(task: ExecutionTask) {\n return task.subType && task.subType !== 'Plan'\n ? `${task.type} / ${task.subType || ''}`\n : task.type;\n}\n\nexport function getKeyCommands(\n value: string | string[],\n): Array<{ key: string; command?: string }> {\n // Ensure value is an array of keys\n const keys = Array.isArray(value) ? value : [value];\n\n // Process each key to attach a corresponding command if needed, based on the presence of 'Meta' or 'Control' in the keys array.\n // ref: https://github.com/puppeteer/puppeteer/pull/9357/files#diff-32cf475237b000f980eb214a0a823e45a902bddb7d2426d677cae96397aa0ae4R94\n return keys.reduce((acc: Array<{ key: string; command?: string }>, k) => {\n const includeMeta = keys.includes('Meta') || keys.includes('Control');\n if (includeMeta && (k === 'a' || k === 'A')) {\n return acc.concat([{ key: k, command: 'SelectAll' }]);\n }\n if (includeMeta && (k === 'c' || k === 'C')) {\n return acc.concat([{ key: k, command: 'Copy' }]);\n }\n if (includeMeta && (k === 'v' || k === 'V')) {\n return acc.concat([{ key: k, command: 'Paste' }]);\n }\n return acc.concat([{ key: k }]);\n }, []);\n}\n\nexport function locateParamStr(locate?: DetailedLocateParam) {\n if (!locate) {\n return '';\n }\n\n if (typeof locate === 'string') {\n return locate;\n }\n\n return locate.prompt;\n}\n\nexport function scrollParamStr(scrollParam?: PlanningActionParamScroll) {\n if (!scrollParam) {\n return '';\n }\n return `${scrollParam.direction || 'down'}, ${scrollParam.scrollType || 'once'}, ${scrollParam.distance || 'distance-not-set'}`;\n}\n\nexport function taskTitleStr(\n type:\n | 'Tap'\n | 'Hover'\n | 'Input'\n | 'KeyboardPress'\n | 'Scroll'\n | 'Action'\n | 'Query'\n | 'Assert'\n | 'WaitFor'\n | 'Locate'\n | 'Boolean'\n | 'Number'\n | 'String',\n prompt: string,\n) {\n if (prompt) {\n return `${type} - ${prompt}`;\n }\n return type;\n}\n\nexport function paramStr(task: ExecutionTask) {\n let value: string | undefined | object;\n if (task.type === 'Planning') {\n value = (task as ExecutionTaskPlanning)?.param?.userInstruction;\n }\n\n if (task.type === 'Insight') {\n value =\n (task as ExecutionTaskInsightLocate)?.param?.prompt ||\n (task as ExecutionTaskInsightLocate)?.param?.id ||\n (task as ExecutionTaskInsightQuery)?.param?.dataDemand ||\n (task as ExecutionTaskInsightAssertion)?.param?.assertion;\n }\n\n if (task.type === 'Action') {\n const locate = (task as ExecutionTaskAction)?.locate;\n const locateStr = locate ? locateParamStr(locate) : '';\n\n value = task.thought || '';\n if (typeof (task as ExecutionTaskAction)?.param?.timeMs === 'number') {\n value = `${(task as ExecutionTaskAction)?.param?.timeMs}ms`;\n } else if (\n typeof (task as ExecutionTaskAction)?.param?.scrollType === 'string'\n ) {\n value = scrollParamStr((task as ExecutionTaskAction)?.param);\n } else if (\n typeof (task as ExecutionTaskAction)?.param?.value !== 'undefined'\n ) {\n value = (task as ExecutionTaskAction)?.param?.value;\n }\n\n if (locateStr) {\n if (value) {\n value = `${locateStr} - ${value}`;\n } else {\n value = locateStr;\n }\n }\n }\n\n if (typeof value === 'undefined') return '';\n return typeof value === 'string'\n ? value\n : JSON.stringify(value, undefined, 2);\n}\n\nexport const limitOpenNewTabScript = `\nif (!window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__) {\n window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__ = true;\n\n // Intercept the window.open method (only once)\n window.open = function(url) {\n console.log('Blocked window.open:', url);\n window.location.href = url;\n return null;\n };\n\n // Block all a tag clicks with target=\"_blank\" (only once)\n document.addEventListener('click', function(e) {\n const target = e.target.closest('a');\n if (target && target.target === '_blank') {\n e.preventDefault();\n console.log('Blocked new tab:', target.href);\n window.location.href = target.href;\n target.removeAttribute('target');\n }\n }, true);\n}\n`;\n"]}
@@ -1 +1 @@
1
- {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/common/utils.ts","../../src/web-element.ts"],"sourcesContent":["import type { StaticPage } from '@/playground';\r\nimport type {\r\n BaseElement,\r\n ElementTreeNode,\r\n PlanningLocateParam,\r\n PlaywrightParserOpt,\r\n UIContext,\r\n} from 'misoai-core';\r\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\r\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\r\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\r\nimport type { ElementInfo } from 'misoai-shared/extractor';\r\nimport {\r\n generateElementByPosition,\r\n getNodeFromCacheList,\r\n traverseTree,\r\n treeToList,\r\n} from 'misoai-shared/extractor';\r\nimport { resizeImgBase64 } from 'misoai-shared/img';\r\nimport type { DebugFunction } from 'misoai-shared/logger';\r\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\r\nimport dayjs from 'dayjs';\r\nimport type { Page as PlaywrightPage } from 'playwright';\r\nimport type { Page as PuppeteerPage } from 'puppeteer';\r\nimport { WebElementInfo } from '../web-element';\r\nimport type { WebPage } from './page';\r\n\r\nexport type WebUIContext = UIContext<WebElementInfo> & {\r\n url: string;\r\n};\r\n\r\nexport async function parseContextFromWebPage(\r\n page: WebPage,\r\n _opt?: PlaywrightParserOpt,\r\n): Promise<WebUIContext> {\r\n assert(page, 'page is required');\r\n if ((page as StaticPage)._forceUsePageContext) {\r\n return await (page as any)._forceUsePageContext();\r\n }\r\n const url = await page.url();\r\n uploadTestInfoToServer({ testUrl: url });\r\n\r\n let screenshotBase64: string;\r\n let tree: ElementTreeNode<ElementInfo>;\r\n\r\n await Promise.all([\r\n page.screenshotBase64().then((base64) => {\r\n screenshotBase64 = base64;\r\n }),\r\n page.getElementsNodeTree().then(async (treeRoot) => {\r\n tree = treeRoot;\r\n }),\r\n ]);\r\n\r\n const webTree = traverseTree(tree!, (elementInfo) => {\r\n const { rect, id, content, attributes, locator, indexId } = elementInfo;\r\n return new WebElementInfo({\r\n rect,\r\n locator,\r\n id,\r\n content,\r\n attributes,\r\n indexId,\r\n });\r\n });\r\n\r\n assert(screenshotBase64!, 'screenshotBase64 is required');\r\n\r\n const elementsInfo = treeToList(webTree);\r\n const size = await page.size();\r\n\r\n if (size.dpr && size.dpr > 1) {\r\n // console.time('resizeImgBase64');\r\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\r\n width: size.width,\r\n height: size.height,\r\n });\r\n // console.timeEnd('resizeImgBase64');\r\n }\r\n\r\n return {\r\n content: elementsInfo!,\r\n tree: webTree,\r\n size,\r\n screenshotBase64: screenshotBase64!,\r\n url,\r\n };\r\n}\r\n\r\nexport function reportFileName(tag = 'web') {\r\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\r\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\r\n // ensure uniqueness at the same time\r\n const uniqueId = uuid().substring(0, 8);\r\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\r\n}\r\n\r\nexport function printReportMsg(filepath: string) {\r\n logMsg(`Midscene - report file updated: ${filepath}`);\r\n}\r\n\r\n/**\r\n * Get the current execution file name\r\n * @returns The name of the current execution file\r\n */\r\nexport function getCurrentExecutionFile(trace?: string): string | false {\r\n const error = new Error();\r\n const stackTrace = trace || error.stack;\r\n const pkgDir = process.cwd() || '';\r\n if (stackTrace) {\r\n const stackLines = stackTrace.split('\\n');\r\n for (const line of stackLines) {\r\n if (\r\n line.includes('.spec.') ||\r\n line.includes('.test.') ||\r\n line.includes('.ts') ||\r\n line.includes('.js')\r\n ) {\r\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\r\n if (match?.[1]) {\r\n const targetFileName = match[1]\r\n .replace(pkgDir, '')\r\n .trim()\r\n .replace('at ', '');\r\n return targetFileName;\r\n }\r\n }\r\n }\r\n }\r\n return false;\r\n}\r\n\r\nconst testFileIndex = new Map<string, number>();\r\n\r\nexport function generateCacheId(fileName?: string): string {\r\n let taskFile = fileName || getCurrentExecutionFile();\r\n if (!taskFile) {\r\n taskFile = uuid();\r\n console.warn(\r\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\r\n );\r\n }\r\n\r\n if (testFileIndex.has(taskFile)) {\r\n const currentIndex = testFileIndex.get(taskFile);\r\n if (currentIndex !== undefined) {\r\n testFileIndex.set(taskFile, currentIndex + 1);\r\n }\r\n } else {\r\n testFileIndex.set(taskFile, 1);\r\n }\r\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\r\n}\r\n\r\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\r\n 'NOT_IMPLEMENTED_AS_DESIGNED';\r\n\r\nexport function replaceIllegalPathCharsAndSpace(str: string) {\r\n return str.replace(/[/\\\\:*?\"<>| ]/g, '-');\r\n}\r\n\r\nexport function forceClosePopup(\r\n page: PuppeteerPage | PlaywrightPage,\r\n debug: DebugFunction,\r\n) {\r\n page.on('popup', async (popup) => {\r\n if (!popup) {\r\n console.warn('got a popup event, but the popup is not ready yet, skip');\r\n return;\r\n }\r\n const url = await (popup as PuppeteerPage).url();\r\n console.log(`Popup opened: ${url}`);\r\n if (!(popup as PuppeteerPage).isClosed()) {\r\n try {\r\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\r\n } catch (error) {\r\n debug(`failed to close popup ${url}, error: ${error}`);\r\n }\r\n } else {\r\n debug(`popup is already closed, skip close ${url}`);\r\n }\r\n\r\n if (!page.isClosed()) {\r\n try {\r\n await page.goto(url);\r\n } catch (error) {\r\n debug(`failed to goto ${url}, error: ${error}`);\r\n }\r\n } else {\r\n debug(`page is already closed, skip goto ${url}`);\r\n }\r\n });\r\n}\r\n\r\nexport function matchElementFromPlan(\r\n planLocateParam: PlanningLocateParam,\r\n tree: ElementTreeNode<BaseElement>,\r\n) {\r\n if (!planLocateParam) {\r\n return undefined;\r\n }\r\n if (planLocateParam.id) {\r\n return getNodeFromCacheList(planLocateParam.id);\r\n }\r\n\r\n if (planLocateParam.bbox) {\r\n const centerPosition = {\r\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\r\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\r\n };\r\n let element = elementByPositionWithElementInfo(tree, centerPosition);\r\n\r\n if (!element) {\r\n element = generateElementByPosition(centerPosition) as BaseElement;\r\n }\r\n\r\n return element;\r\n }\r\n\r\n return undefined;\r\n}\r\n","import type { BaseElement, Rect } from 'misoai-core';\r\nimport type { NodeType } from 'misoai-shared/constants';\r\nexport interface WebElementInfoType extends BaseElement {\r\n id: string;\r\n locator: string;\r\n attributes: {\r\n nodeType: NodeType;\r\n [key: string]: string;\r\n };\r\n}\r\n\r\nexport class WebElementInfo implements BaseElement {\r\n content: string;\r\n\r\n locator?: string;\r\n\r\n rect: Rect;\r\n\r\n center: [number, number];\r\n\r\n // page: WebPage;\r\n\r\n id: string;\r\n\r\n indexId: number;\r\n\r\n attributes: {\r\n nodeType: NodeType;\r\n [key: string]: string;\r\n };\r\n\r\n xpaths?: string[];\r\n\r\n constructor({\r\n content,\r\n rect,\r\n // page,\r\n locator,\r\n id,\r\n attributes,\r\n indexId,\r\n xpaths,\r\n }: {\r\n content: string;\r\n rect: Rect;\r\n // page: WebPage;\r\n locator?: string;\r\n id: string;\r\n attributes: {\r\n nodeType: NodeType;\r\n [key: string]: string;\r\n };\r\n indexId: number;\r\n xpaths?: string[];\r\n }) {\r\n this.content = content;\r\n this.rect = rect;\r\n this.center = [\r\n Math.floor(rect.left + rect.width / 2),\r\n Math.floor(rect.top + rect.height / 2),\r\n ];\r\n // this.page = page;\r\n this.locator = locator;\r\n this.id = id;\r\n this.attributes = attributes;\r\n this.indexId = indexId;\r\n this.xpaths = xpaths;\r\n }\r\n}\r\n"]}
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/common/utils.ts","../../src/web-element.ts"],"sourcesContent":["import type { StaticPage } from '@/playground';\nimport type {\n BaseElement,\n ElementTreeNode,\n PlanningLocateParam,\n PlaywrightParserOpt,\n UIContext,\n} from 'misoai-core';\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\nimport type { ElementInfo } from 'misoai-shared/extractor';\nimport {\n generateElementByPosition,\n getNodeFromCacheList,\n traverseTree,\n treeToList,\n} from 'misoai-shared/extractor';\nimport { resizeImgBase64 } from 'misoai-shared/img';\nimport type { DebugFunction } from 'misoai-shared/logger';\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\nimport dayjs from 'dayjs';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport type { Page as PuppeteerPage } from 'puppeteer';\nimport { WebElementInfo } from '../web-element';\nimport type { WebPage } from './page';\n\nexport type WebUIContext = UIContext<WebElementInfo> & {\n url: string;\n};\n\nexport async function parseContextFromWebPage(\n page: WebPage,\n _opt?: PlaywrightParserOpt,\n): Promise<WebUIContext> {\n assert(page, 'page is required');\n if ((page as StaticPage)._forceUsePageContext) {\n return await (page as any)._forceUsePageContext();\n }\n const url = await page.url();\n uploadTestInfoToServer({ testUrl: url });\n\n let screenshotBase64: string;\n let tree: ElementTreeNode<ElementInfo>;\n\n await Promise.all([\n page.screenshotBase64().then((base64) => {\n screenshotBase64 = base64;\n }),\n page.getElementsNodeTree().then(async (treeRoot) => {\n tree = treeRoot;\n }),\n ]);\n\n const webTree = traverseTree(tree!, (elementInfo) => {\n const { rect, id, content, attributes, locator, indexId } = elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n });\n });\n\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n const elementsInfo = treeToList(webTree);\n const size = await page.size();\n\n if (size.dpr && size.dpr > 1) {\n // console.time('resizeImgBase64');\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\n width: size.width,\n height: size.height,\n });\n // console.timeEnd('resizeImgBase64');\n }\n\n return {\n content: elementsInfo!,\n tree: webTree,\n size,\n screenshotBase64: screenshotBase64!,\n url,\n };\n}\n\nexport function reportFileName(tag = 'web') {\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\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 logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\n/**\n * Get the current execution file name\n * @returns The name of the current execution file\n */\nexport function getCurrentExecutionFile(trace?: string): string | false {\n const error = new Error();\n const stackTrace = trace || error.stack;\n const pkgDir = process.cwd() || '';\n if (stackTrace) {\n const stackLines = stackTrace.split('\\n');\n for (const line of stackLines) {\n if (\n line.includes('.spec.') ||\n line.includes('.test.') ||\n line.includes('.ts') ||\n line.includes('.js')\n ) {\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\n if (match?.[1]) {\n const targetFileName = match[1]\n .replace(pkgDir, '')\n .trim()\n .replace('at ', '');\n return targetFileName;\n }\n }\n }\n }\n return false;\n}\n\nconst testFileIndex = new Map<string, number>();\n\nexport function generateCacheId(fileName?: string): string {\n let taskFile = fileName || getCurrentExecutionFile();\n if (!taskFile) {\n taskFile = uuid();\n console.warn(\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\n );\n }\n\n if (testFileIndex.has(taskFile)) {\n const currentIndex = testFileIndex.get(taskFile);\n if (currentIndex !== undefined) {\n testFileIndex.set(taskFile, currentIndex + 1);\n }\n } else {\n testFileIndex.set(taskFile, 1);\n }\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\n}\n\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\n 'NOT_IMPLEMENTED_AS_DESIGNED';\n\nexport function replaceIllegalPathCharsAndSpace(str: string) {\n return str.replace(/[/\\\\:*?\"<>| ]/g, '-');\n}\n\nexport function forceClosePopup(\n page: PuppeteerPage | PlaywrightPage,\n debug: DebugFunction,\n) {\n page.on('popup', async (popup) => {\n if (!popup) {\n console.warn('got a popup event, but the popup is not ready yet, skip');\n return;\n }\n const url = await (popup as PuppeteerPage).url();\n console.log(`Popup opened: ${url}`);\n if (!(popup as PuppeteerPage).isClosed()) {\n try {\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\n } catch (error) {\n debug(`failed to close popup ${url}, error: ${error}`);\n }\n } else {\n debug(`popup is already closed, skip close ${url}`);\n }\n\n if (!page.isClosed()) {\n try {\n await page.goto(url);\n } catch (error) {\n debug(`failed to goto ${url}, error: ${error}`);\n }\n } else {\n debug(`page is already closed, skip goto ${url}`);\n }\n });\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParam,\n tree: ElementTreeNode<BaseElement>,\n) {\n if (!planLocateParam) {\n return undefined;\n }\n if (planLocateParam.id) {\n return getNodeFromCacheList(planLocateParam.id);\n }\n\n if (planLocateParam.bbox) {\n const centerPosition = {\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\n };\n let element = elementByPositionWithElementInfo(tree, centerPosition);\n\n if (!element) {\n element = generateElementByPosition(centerPosition) as BaseElement;\n }\n\n return element;\n }\n\n return undefined;\n}\n","import type { BaseElement, Rect } from 'misoai-core';\nimport type { NodeType } from 'misoai-shared/constants';\nexport interface WebElementInfoType extends BaseElement {\n id: string;\n locator: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n}\n\nexport class WebElementInfo implements BaseElement {\n content: string;\n\n locator?: string;\n\n rect: Rect;\n\n center: [number, number];\n\n // page: WebPage;\n\n id: string;\n\n indexId: number;\n\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n\n xpaths?: string[];\n\n constructor({\n content,\n rect,\n // page,\n locator,\n id,\n attributes,\n indexId,\n xpaths,\n }: {\n content: string;\n rect: Rect;\n // page: WebPage;\n locator?: string;\n id: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n indexId: number;\n xpaths?: string[];\n }) {\n this.content = content;\n this.rect = rect;\n this.center = [\n Math.floor(rect.left + rect.width / 2),\n Math.floor(rect.top + rect.height / 2),\n ];\n // this.page = page;\n this.locator = locator;\n this.id = id;\n this.attributes = attributes;\n this.indexId = indexId;\n this.xpaths = xpaths;\n }\n}\n"]}
package/dist/es/yaml.js CHANGED
@@ -13,10 +13,11 @@ var ScriptPlayer = class {
13
13
  this.unnamedResultIndex = 0;
14
14
  this.pageAgent = null;
15
15
  this.result = {};
16
+ const target = script.target || script.web || script.android;
16
17
  if (ifInBrowser) {
17
18
  this.output = void 0;
18
- } else if (script.target?.output) {
19
- this.output = resolve(process.cwd(), script.target.output);
19
+ } else if (target?.output) {
20
+ this.output = resolve(process.cwd(), target.output);
20
21
  } else {
21
22
  this.output = join(getMidsceneRunSubDir("output"), `${process.pid}.json`);
22
23
  }
@@ -90,12 +91,13 @@ var ScriptPlayer = class {
90
91
  } else if ("aiAssert" in flowItem) {
91
92
  const assertTask = flowItem;
92
93
  const prompt = assertTask.aiAssert;
94
+ const msg = assertTask.errorMessage;
93
95
  assert(prompt, "missing prompt for aiAssert");
94
96
  assert(
95
97
  typeof prompt === "string",
96
98
  "prompt for aiAssert must be a string"
97
99
  );
98
- await agent.aiAssert(prompt);
100
+ await agent.aiAssert(prompt, msg);
99
101
  } else if ("aiQuery" in flowItem) {
100
102
  const queryTask = flowItem;
101
103
  const prompt = queryTask.aiQuery;
@@ -1 +1 @@
1
- {"version":3,"mappings":"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","names":["resolve","assert","yaml"],"ignoreList":[],"sources":["../../src/yaml/player.ts","../../src/yaml/builder.ts","../../src/yaml/utils.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\r\nimport { dirname, join, resolve } from 'node:path';\r\nimport { assert, ifInBrowser } from 'misoai-shared/utils';\r\n\r\nimport type { PageAgent } from '@/common/agent';\r\nimport type {\r\n FreeFn,\r\n MidsceneYamlFlowItemAIAction,\r\n MidsceneYamlFlowItemAIAssert,\r\n MidsceneYamlFlowItemAIBoolean,\r\n MidsceneYamlFlowItemAIHover,\r\n MidsceneYamlFlowItemAIInput,\r\n MidsceneYamlFlowItemAIKeyboardPress,\r\n MidsceneYamlFlowItemAILocate,\r\n MidsceneYamlFlowItemAINString,\r\n MidsceneYamlFlowItemAINumber,\r\n MidsceneYamlFlowItemAIQuery,\r\n MidsceneYamlFlowItemAIScroll,\r\n MidsceneYamlFlowItemAITap,\r\n MidsceneYamlFlowItemAIWaitFor,\r\n MidsceneYamlFlowItemEvaluateJavaScript,\r\n MidsceneYamlFlowItemSleep,\r\n MidsceneYamlScript,\r\n MidsceneYamlScriptEnv,\r\n ScriptPlayerStatusValue,\r\n ScriptPlayerTaskStatus,\r\n} from 'misoai-core';\r\nimport { getMidsceneRunSubDir } from 'misoai-shared/common';\r\n\r\nexport class ScriptPlayer<T extends MidsceneYamlScriptEnv> {\r\n public currentTaskIndex?: number;\r\n public taskStatusList: ScriptPlayerTaskStatus[] = [];\r\n public status: ScriptPlayerStatusValue = 'init';\r\n public reportFile?: string | null;\r\n public result: Record<string, any>;\r\n private unnamedResultIndex = 0;\r\n public output?: string | null;\r\n public errorInSetup?: Error;\r\n private pageAgent: PageAgent | null = null;\r\n public agentStatusTip?: string;\r\n constructor(\r\n private script: MidsceneYamlScript,\r\n private setupAgent: (platform: T) => Promise<{\r\n agent: PageAgent;\r\n freeFn: FreeFn[];\r\n }>,\r\n public onTaskStatusChange?: (taskStatus: ScriptPlayerTaskStatus) => void,\r\n ) {\r\n this.result = {};\r\n\r\n if (ifInBrowser) {\r\n this.output = undefined;\r\n } else if (script.target?.output) {\r\n this.output = resolve(process.cwd(), script.target.output);\r\n } else {\r\n this.output = join(getMidsceneRunSubDir('output'), `${process.pid}.json`);\r\n }\r\n\r\n this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({\r\n ...task,\r\n index: taskIndex,\r\n status: 'init',\r\n totalSteps: task.flow?.length || 0,\r\n }));\r\n }\r\n\r\n private setResult(key: string | undefined, value: any) {\r\n const keyToUse = key || this.unnamedResultIndex++;\r\n if (this.result[keyToUse]) {\r\n console.warn(`result key ${keyToUse} already exists, will overwrite`);\r\n }\r\n this.result[keyToUse] = value;\r\n\r\n this.flushResult();\r\n }\r\n\r\n private setPlayerStatus(status: ScriptPlayerStatusValue, error?: Error) {\r\n this.status = status;\r\n this.errorInSetup = error;\r\n }\r\n\r\n private notifyCurrentTaskStatusChange(taskIndex?: number) {\r\n const taskIndexToNotify =\r\n typeof taskIndex === 'number' ? taskIndex : this.currentTaskIndex;\r\n\r\n if (typeof taskIndexToNotify !== 'number') {\r\n return;\r\n }\r\n\r\n const taskStatus = this.taskStatusList[taskIndexToNotify];\r\n if (this.onTaskStatusChange) {\r\n this.onTaskStatusChange(taskStatus);\r\n }\r\n }\r\n\r\n private async setTaskStatus(\r\n index: number,\r\n statusValue: ScriptPlayerStatusValue,\r\n error?: Error,\r\n ) {\r\n this.taskStatusList[index].status = statusValue;\r\n if (error) {\r\n this.taskStatusList[index].error = error;\r\n }\r\n\r\n this.notifyCurrentTaskStatusChange(index);\r\n }\r\n\r\n private setTaskIndex(taskIndex: number) {\r\n this.currentTaskIndex = taskIndex;\r\n }\r\n\r\n private flushResult() {\r\n if (Object.keys(this.result).length && this.output) {\r\n const output = resolve(process.cwd(), this.output);\r\n const outputDir = dirname(output);\r\n if (!existsSync(outputDir)) {\r\n mkdirSync(outputDir, { recursive: true });\r\n }\r\n writeFileSync(output, JSON.stringify(this.result, undefined, 2));\r\n }\r\n }\r\n\r\n async playTask(taskStatus: ScriptPlayerTaskStatus, agent: PageAgent) {\r\n const { flow } = taskStatus;\r\n assert(flow, 'missing flow in task');\r\n\r\n for (const flowItemIndex in flow) {\r\n const currentStep = Number.parseInt(flowItemIndex, 10);\r\n taskStatus.currentStep = currentStep;\r\n const flowItem = flow[flowItemIndex];\r\n if (\r\n 'aiAction' in (flowItem as MidsceneYamlFlowItemAIAction) ||\r\n 'ai' in (flowItem as MidsceneYamlFlowItemAIAction)\r\n ) {\r\n const actionTask = flowItem as MidsceneYamlFlowItemAIAction;\r\n const prompt = actionTask.aiAction || actionTask.ai;\r\n assert(prompt, 'missing prompt for ai (aiAction)');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiAction must be a string',\r\n );\r\n await agent.aiAction(prompt, {\r\n cacheable: actionTask.cacheable,\r\n });\r\n } else if ('aiAssert' in (flowItem as MidsceneYamlFlowItemAIAssert)) {\r\n const assertTask = flowItem as MidsceneYamlFlowItemAIAssert;\r\n const prompt = assertTask.aiAssert;\r\n assert(prompt, 'missing prompt for aiAssert');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiAssert must be a string',\r\n );\r\n await agent.aiAssert(prompt);\r\n } else if ('aiQuery' in (flowItem as MidsceneYamlFlowItemAIQuery)) {\r\n const queryTask = flowItem as MidsceneYamlFlowItemAIQuery;\r\n const prompt = queryTask.aiQuery;\r\n assert(prompt, 'missing prompt for aiQuery');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiQuery must be a string',\r\n );\r\n const queryResult = await agent.aiQuery(prompt);\r\n this.setResult(queryTask.name, queryResult);\r\n } else if ('aiNumber' in (flowItem as MidsceneYamlFlowItemAINumber)) {\r\n const numberTask = flowItem as MidsceneYamlFlowItemAINumber;\r\n const prompt = numberTask.aiNumber;\r\n assert(prompt, 'missing prompt for number');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for number must be a string',\r\n );\r\n const numberResult = await agent.aiNumber(prompt);\r\n this.setResult(numberTask.name, numberResult);\r\n } else if ('aiString' in (flowItem as MidsceneYamlFlowItemAINString)) {\r\n const stringTask = flowItem as MidsceneYamlFlowItemAINString;\r\n const prompt = stringTask.aiString;\r\n assert(prompt, 'missing prompt for string');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for string must be a string',\r\n );\r\n const stringResult = await agent.aiString(prompt);\r\n this.setResult(stringTask.name, stringResult);\r\n } else if ('aiBoolean' in (flowItem as MidsceneYamlFlowItemAIBoolean)) {\r\n const booleanTask = flowItem as MidsceneYamlFlowItemAIBoolean;\r\n const prompt = booleanTask.aiBoolean;\r\n assert(prompt, 'missing prompt for boolean');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for boolean must be a string',\r\n );\r\n const booleanResult = await agent.aiBoolean(prompt);\r\n this.setResult(booleanTask.name, booleanResult);\r\n } else if ('aiLocate' in (flowItem as MidsceneYamlFlowItemAILocate)) {\r\n const locateTask = flowItem as MidsceneYamlFlowItemAILocate;\r\n const prompt = locateTask.aiLocate;\r\n assert(prompt, 'missing prompt for aiLocate');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiLocate must be a string',\r\n );\r\n const locateResult = await agent.aiLocate(prompt);\r\n this.setResult(locateTask.name, locateResult);\r\n } else if ('aiWaitFor' in (flowItem as MidsceneYamlFlowItemAIWaitFor)) {\r\n const waitForTask = flowItem as MidsceneYamlFlowItemAIWaitFor;\r\n const prompt = waitForTask.aiWaitFor;\r\n assert(prompt, 'missing prompt for aiWaitFor');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiWaitFor must be a string',\r\n );\r\n const timeout = waitForTask.timeout;\r\n await agent.aiWaitFor(prompt, { timeoutMs: timeout });\r\n } else if ('sleep' in (flowItem as MidsceneYamlFlowItemSleep)) {\r\n const sleepTask = flowItem as MidsceneYamlFlowItemSleep;\r\n const ms = sleepTask.sleep;\r\n let msNumber = ms;\r\n if (typeof ms === 'string') {\r\n msNumber = Number.parseInt(ms, 10);\r\n }\r\n assert(\r\n msNumber && msNumber > 0,\r\n `ms for sleep must be greater than 0, but got ${ms}`,\r\n );\r\n await new Promise((resolve) => setTimeout(resolve, msNumber));\r\n } else if ('aiTap' in (flowItem as MidsceneYamlFlowItemAITap)) {\r\n const tapTask = flowItem as MidsceneYamlFlowItemAITap;\r\n await agent.aiTap(tapTask.aiTap, tapTask);\r\n } else if ('aiHover' in (flowItem as MidsceneYamlFlowItemAIHover)) {\r\n const hoverTask = flowItem as MidsceneYamlFlowItemAIHover;\r\n await agent.aiHover(hoverTask.aiHover, hoverTask);\r\n } else if ('aiInput' in (flowItem as MidsceneYamlFlowItemAIInput)) {\r\n // may be input empty string ''\r\n const inputTask = flowItem as MidsceneYamlFlowItemAIInput;\r\n await agent.aiInput(inputTask.aiInput, inputTask.locate, inputTask);\r\n } else if (\r\n 'aiKeyboardPress' in (flowItem as MidsceneYamlFlowItemAIKeyboardPress)\r\n ) {\r\n const keyboardPressTask =\r\n flowItem as MidsceneYamlFlowItemAIKeyboardPress;\r\n await agent.aiKeyboardPress(\r\n keyboardPressTask.aiKeyboardPress,\r\n keyboardPressTask.locate,\r\n keyboardPressTask,\r\n );\r\n } else if ('aiScroll' in (flowItem as MidsceneYamlFlowItemAIScroll)) {\r\n const scrollTask = flowItem as MidsceneYamlFlowItemAIScroll;\r\n await agent.aiScroll(scrollTask, scrollTask.locate, scrollTask);\r\n } else if (\r\n 'javascript' in (flowItem as MidsceneYamlFlowItemEvaluateJavaScript)\r\n ) {\r\n const evaluateJavaScriptTask =\r\n flowItem as MidsceneYamlFlowItemEvaluateJavaScript;\r\n\r\n const result = await agent.evaluateJavaScript(\r\n evaluateJavaScriptTask.javascript,\r\n );\r\n this.setResult(evaluateJavaScriptTask.name, result);\r\n } else {\r\n throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);\r\n }\r\n }\r\n this.reportFile = agent.reportFile;\r\n }\r\n\r\n async run() {\r\n const { target, web, android, tasks } = this.script;\r\n const webEnv = web || target;\r\n const androidEnv = android;\r\n const platform = webEnv || androidEnv;\r\n\r\n this.setPlayerStatus('running');\r\n\r\n let agent: PageAgent | null = null;\r\n let freeFn: FreeFn[] = [];\r\n try {\r\n const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(\r\n platform as T,\r\n );\r\n agent = newAgent;\r\n const originalOnTaskStartTip = agent.onTaskStartTip;\r\n agent.onTaskStartTip = (tip) => {\r\n if (this.status === 'running') {\r\n this.agentStatusTip = tip;\r\n }\r\n originalOnTaskStartTip?.(tip);\r\n };\r\n freeFn = [\r\n ...(newFreeFn || []),\r\n {\r\n name: 'restore-agent-onTaskStartTip',\r\n fn: () => {\r\n if (agent) {\r\n agent.onTaskStartTip = originalOnTaskStartTip;\r\n }\r\n },\r\n },\r\n ];\r\n } catch (e) {\r\n this.setPlayerStatus('error', e as Error);\r\n return;\r\n }\r\n this.pageAgent = agent;\r\n\r\n let taskIndex = 0;\r\n this.setPlayerStatus('running');\r\n let errorFlag = false;\r\n while (taskIndex < tasks.length) {\r\n const taskStatus = this.taskStatusList[taskIndex];\r\n this.setTaskStatus(taskIndex, 'running' as any);\r\n this.setTaskIndex(taskIndex);\r\n\r\n try {\r\n await this.playTask(taskStatus, this.pageAgent);\r\n this.setTaskStatus(taskIndex, 'done' as any);\r\n } catch (e) {\r\n this.setTaskStatus(taskIndex, 'error' as any, e as Error);\r\n\r\n if (taskStatus.continueOnError) {\r\n // nothing more to do\r\n } else {\r\n this.reportFile = agent.reportFile;\r\n errorFlag = true;\r\n break;\r\n }\r\n }\r\n this.reportFile = agent.reportFile;\r\n taskIndex++;\r\n }\r\n\r\n if (errorFlag) {\r\n this.setPlayerStatus('error');\r\n } else {\r\n this.setPlayerStatus('done');\r\n }\r\n this.agentStatusTip = '';\r\n\r\n // free the resources\r\n for (const fn of freeFn) {\r\n try {\r\n // console.log('freeing', fn.name);\r\n await fn.fn();\r\n // console.log('freed', fn.name);\r\n } catch (e) {\r\n // console.error('error freeing', fn.name, e);\r\n }\r\n }\r\n }\r\n}\r\n","import type {\r\n MidsceneYamlScript,\r\n MidsceneYamlScriptWebEnv,\r\n MidsceneYamlTask,\r\n} from 'misoai-core';\r\nimport yaml from 'js-yaml';\r\n\r\nexport function buildYaml(\r\n env: MidsceneYamlScriptWebEnv,\r\n tasks: MidsceneYamlTask[],\r\n) {\r\n const result: MidsceneYamlScript = {\r\n target: env,\r\n tasks,\r\n };\r\n\r\n return yaml.dump(result, {\r\n indent: 2,\r\n });\r\n}\r\n","import { assert } from 'misoai-shared/utils';\r\nimport yaml from 'js-yaml';\r\n\r\nimport type { MidsceneYamlScript } from 'misoai-core';\r\n\r\nfunction interpolateEnvVars(content: string): string {\r\n return content.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\r\n const value = process.env[envVar.trim()];\r\n if (value === undefined) {\r\n throw new Error(`Environment variable \"${envVar.trim()}\" is not defined`);\r\n }\r\n return value;\r\n });\r\n}\r\n\r\nexport function parseYamlScript(\r\n content: string,\r\n filePath?: string,\r\n ignoreCheckingTarget?: boolean,\r\n): MidsceneYamlScript {\r\n let processedContent = content;\r\n if (content.indexOf('android') !== -1 && content.match(/deviceId:\\s*(\\d+)/)) {\r\n let matchedDeviceId;\r\n processedContent = content.replace(\r\n /deviceId:\\s*(\\d+)/g,\r\n (match, deviceId) => {\r\n matchedDeviceId = deviceId;\r\n return `deviceId: '${deviceId}'`;\r\n },\r\n );\r\n console.warn(\r\n `please use string-style deviceId in yaml script, for example: deviceId: \"${matchedDeviceId}\"`,\r\n );\r\n }\r\n const interpolatedContent = interpolateEnvVars(processedContent);\r\n const obj = yaml.load(interpolatedContent, {\r\n schema: yaml.JSON_SCHEMA,\r\n }) as MidsceneYamlScript;\r\n\r\n const pathTip = filePath ? `, failed to load ${filePath}` : '';\r\n const android =\r\n typeof obj.android !== 'undefined'\r\n ? Object.assign({}, obj.android || {})\r\n : undefined;\r\n const webConfig = obj.web || obj.target; // no need to handle null case, because web has required parameters url\r\n const web =\r\n typeof webConfig !== 'undefined'\r\n ? Object.assign({}, webConfig || {})\r\n : undefined;\r\n\r\n if (!ignoreCheckingTarget) {\r\n // make sure at least one of target/web/android is provided\r\n assert(\r\n web || android,\r\n `at least one of \"target\", \"web\", or \"android\" properties is required in yaml script${pathTip}`,\r\n );\r\n\r\n // make sure only one of target/web/android is provided\r\n assert(\r\n (web && !android) || (!web && android),\r\n `only one of \"target\", \"web\", or \"android\" properties is allowed in yaml script${pathTip}`,\r\n );\r\n\r\n // make sure the config is valid\r\n if (web || android) {\r\n assert(\r\n typeof web === 'object' || typeof android === 'object',\r\n `property \"target/web/android\" must be an object${pathTip}`,\r\n );\r\n }\r\n }\r\n\r\n assert(obj.tasks, `property \"tasks\" is required in yaml script ${pathTip}`);\r\n assert(\r\n Array.isArray(obj.tasks),\r\n `property \"tasks\" must be an array in yaml script, but got ${obj.tasks}`,\r\n );\r\n return obj;\r\n}\r\n"]}
1
+ {"version":3,"mappings":"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","names":["resolve","assert","yaml"],"ignoreList":[],"sources":["../../src/yaml/player.ts","../../src/yaml/builder.ts","../../src/yaml/utils.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { assert, ifInBrowser } from 'misoai-shared/utils';\n\nimport type { PageAgent } from '@/common/agent';\nimport type {\n FreeFn,\n MidsceneYamlFlowItemAIAction,\n MidsceneYamlFlowItemAIAssert,\n MidsceneYamlFlowItemAIBoolean,\n MidsceneYamlFlowItemAIHover,\n MidsceneYamlFlowItemAIInput,\n MidsceneYamlFlowItemAIKeyboardPress,\n MidsceneYamlFlowItemAILocate,\n MidsceneYamlFlowItemAINString,\n MidsceneYamlFlowItemAINumber,\n MidsceneYamlFlowItemAIQuery,\n MidsceneYamlFlowItemAIScroll,\n MidsceneYamlFlowItemAITap,\n MidsceneYamlFlowItemAIWaitFor,\n MidsceneYamlFlowItemEvaluateJavaScript,\n MidsceneYamlFlowItemSleep,\n MidsceneYamlScript,\n MidsceneYamlScriptEnv,\n ScriptPlayerStatusValue,\n ScriptPlayerTaskStatus,\n} from 'misoai-core';\nimport { getMidsceneRunSubDir } from 'misoai-shared/common';\n\nexport class ScriptPlayer<T extends MidsceneYamlScriptEnv> {\n public currentTaskIndex?: number;\n public taskStatusList: ScriptPlayerTaskStatus[] = [];\n public status: ScriptPlayerStatusValue = 'init';\n public reportFile?: string | null;\n public result: Record<string, any>;\n private unnamedResultIndex = 0;\n public output?: string | null;\n public errorInSetup?: Error;\n private pageAgent: PageAgent | null = null;\n public agentStatusTip?: string;\n constructor(\n private script: MidsceneYamlScript,\n private setupAgent: (platform: T) => Promise<{\n agent: PageAgent;\n freeFn: FreeFn[];\n }>,\n public onTaskStatusChange?: (taskStatus: ScriptPlayerTaskStatus) => void,\n ) {\n this.result = {};\n\n const target = script.target || script.web || script.android;\n\n if (ifInBrowser) {\n this.output = undefined;\n } else if (target?.output) {\n this.output = resolve(process.cwd(), target.output);\n } else {\n this.output = join(getMidsceneRunSubDir('output'), `${process.pid}.json`);\n }\n\n this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({\n ...task,\n index: taskIndex,\n status: 'init',\n totalSteps: task.flow?.length || 0,\n }));\n }\n\n private setResult(key: string | undefined, value: any) {\n const keyToUse = key || this.unnamedResultIndex++;\n if (this.result[keyToUse]) {\n console.warn(`result key ${keyToUse} already exists, will overwrite`);\n }\n this.result[keyToUse] = value;\n\n this.flushResult();\n }\n\n private setPlayerStatus(status: ScriptPlayerStatusValue, error?: Error) {\n this.status = status;\n this.errorInSetup = error;\n }\n\n private notifyCurrentTaskStatusChange(taskIndex?: number) {\n const taskIndexToNotify =\n typeof taskIndex === 'number' ? taskIndex : this.currentTaskIndex;\n\n if (typeof taskIndexToNotify !== 'number') {\n return;\n }\n\n const taskStatus = this.taskStatusList[taskIndexToNotify];\n if (this.onTaskStatusChange) {\n this.onTaskStatusChange(taskStatus);\n }\n }\n\n private async setTaskStatus(\n index: number,\n statusValue: ScriptPlayerStatusValue,\n error?: Error,\n ) {\n this.taskStatusList[index].status = statusValue;\n if (error) {\n this.taskStatusList[index].error = error;\n }\n\n this.notifyCurrentTaskStatusChange(index);\n }\n\n private setTaskIndex(taskIndex: number) {\n this.currentTaskIndex = taskIndex;\n }\n\n private flushResult() {\n if (Object.keys(this.result).length && this.output) {\n const output = resolve(process.cwd(), this.output);\n const outputDir = dirname(output);\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n writeFileSync(output, JSON.stringify(this.result, undefined, 2));\n }\n }\n\n async playTask(taskStatus: ScriptPlayerTaskStatus, agent: PageAgent) {\n const { flow } = taskStatus;\n assert(flow, 'missing flow in task');\n\n for (const flowItemIndex in flow) {\n const currentStep = Number.parseInt(flowItemIndex, 10);\n taskStatus.currentStep = currentStep;\n const flowItem = flow[flowItemIndex];\n if (\n 'aiAction' in (flowItem as MidsceneYamlFlowItemAIAction) ||\n 'ai' in (flowItem as MidsceneYamlFlowItemAIAction)\n ) {\n const actionTask = flowItem as MidsceneYamlFlowItemAIAction;\n const prompt = actionTask.aiAction || actionTask.ai;\n assert(prompt, 'missing prompt for ai (aiAction)');\n assert(\n typeof prompt === 'string',\n 'prompt for aiAction must be a string',\n );\n await agent.aiAction(prompt, {\n cacheable: actionTask.cacheable,\n });\n } else if ('aiAssert' in (flowItem as MidsceneYamlFlowItemAIAssert)) {\n const assertTask = flowItem as MidsceneYamlFlowItemAIAssert;\n const prompt = assertTask.aiAssert;\n const msg = assertTask.errorMessage;\n assert(prompt, 'missing prompt for aiAssert');\n assert(\n typeof prompt === 'string',\n 'prompt for aiAssert must be a string',\n );\n await agent.aiAssert(prompt, msg);\n } else if ('aiQuery' in (flowItem as MidsceneYamlFlowItemAIQuery)) {\n const queryTask = flowItem as MidsceneYamlFlowItemAIQuery;\n const prompt = queryTask.aiQuery;\n assert(prompt, 'missing prompt for aiQuery');\n assert(\n typeof prompt === 'string',\n 'prompt for aiQuery must be a string',\n );\n const queryResult = await agent.aiQuery(prompt);\n this.setResult(queryTask.name, queryResult);\n } else if ('aiNumber' in (flowItem as MidsceneYamlFlowItemAINumber)) {\n const numberTask = flowItem as MidsceneYamlFlowItemAINumber;\n const prompt = numberTask.aiNumber;\n assert(prompt, 'missing prompt for number');\n assert(\n typeof prompt === 'string',\n 'prompt for number must be a string',\n );\n const numberResult = await agent.aiNumber(prompt);\n this.setResult(numberTask.name, numberResult);\n } else if ('aiString' in (flowItem as MidsceneYamlFlowItemAINString)) {\n const stringTask = flowItem as MidsceneYamlFlowItemAINString;\n const prompt = stringTask.aiString;\n assert(prompt, 'missing prompt for string');\n assert(\n typeof prompt === 'string',\n 'prompt for string must be a string',\n );\n const stringResult = await agent.aiString(prompt);\n this.setResult(stringTask.name, stringResult);\n } else if ('aiBoolean' in (flowItem as MidsceneYamlFlowItemAIBoolean)) {\n const booleanTask = flowItem as MidsceneYamlFlowItemAIBoolean;\n const prompt = booleanTask.aiBoolean;\n assert(prompt, 'missing prompt for boolean');\n assert(\n typeof prompt === 'string',\n 'prompt for boolean must be a string',\n );\n const booleanResult = await agent.aiBoolean(prompt);\n this.setResult(booleanTask.name, booleanResult);\n } else if ('aiLocate' in (flowItem as MidsceneYamlFlowItemAILocate)) {\n const locateTask = flowItem as MidsceneYamlFlowItemAILocate;\n const prompt = locateTask.aiLocate;\n assert(prompt, 'missing prompt for aiLocate');\n assert(\n typeof prompt === 'string',\n 'prompt for aiLocate must be a string',\n );\n const locateResult = await agent.aiLocate(prompt);\n this.setResult(locateTask.name, locateResult);\n } else if ('aiWaitFor' in (flowItem as MidsceneYamlFlowItemAIWaitFor)) {\n const waitForTask = flowItem as MidsceneYamlFlowItemAIWaitFor;\n const prompt = waitForTask.aiWaitFor;\n assert(prompt, 'missing prompt for aiWaitFor');\n assert(\n typeof prompt === 'string',\n 'prompt for aiWaitFor must be a string',\n );\n const timeout = waitForTask.timeout;\n await agent.aiWaitFor(prompt, { timeoutMs: timeout });\n } else if ('sleep' in (flowItem as MidsceneYamlFlowItemSleep)) {\n const sleepTask = flowItem as MidsceneYamlFlowItemSleep;\n const ms = sleepTask.sleep;\n let msNumber = ms;\n if (typeof ms === 'string') {\n msNumber = Number.parseInt(ms, 10);\n }\n assert(\n msNumber && msNumber > 0,\n `ms for sleep must be greater than 0, but got ${ms}`,\n );\n await new Promise((resolve) => setTimeout(resolve, msNumber));\n } else if ('aiTap' in (flowItem as MidsceneYamlFlowItemAITap)) {\n const tapTask = flowItem as MidsceneYamlFlowItemAITap;\n await agent.aiTap(tapTask.aiTap, tapTask);\n } else if ('aiHover' in (flowItem as MidsceneYamlFlowItemAIHover)) {\n const hoverTask = flowItem as MidsceneYamlFlowItemAIHover;\n await agent.aiHover(hoverTask.aiHover, hoverTask);\n } else if ('aiInput' in (flowItem as MidsceneYamlFlowItemAIInput)) {\n // may be input empty string ''\n const inputTask = flowItem as MidsceneYamlFlowItemAIInput;\n await agent.aiInput(inputTask.aiInput, inputTask.locate, inputTask);\n } else if (\n 'aiKeyboardPress' in (flowItem as MidsceneYamlFlowItemAIKeyboardPress)\n ) {\n const keyboardPressTask =\n flowItem as MidsceneYamlFlowItemAIKeyboardPress;\n await agent.aiKeyboardPress(\n keyboardPressTask.aiKeyboardPress,\n keyboardPressTask.locate,\n keyboardPressTask,\n );\n } else if ('aiScroll' in (flowItem as MidsceneYamlFlowItemAIScroll)) {\n const scrollTask = flowItem as MidsceneYamlFlowItemAIScroll;\n await agent.aiScroll(scrollTask, scrollTask.locate, scrollTask);\n } else if (\n 'javascript' in (flowItem as MidsceneYamlFlowItemEvaluateJavaScript)\n ) {\n const evaluateJavaScriptTask =\n flowItem as MidsceneYamlFlowItemEvaluateJavaScript;\n\n const result = await agent.evaluateJavaScript(\n evaluateJavaScriptTask.javascript,\n );\n this.setResult(evaluateJavaScriptTask.name, result);\n } else {\n throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);\n }\n }\n this.reportFile = agent.reportFile;\n }\n\n async run() {\n const { target, web, android, tasks } = this.script;\n const webEnv = web || target;\n const androidEnv = android;\n const platform = webEnv || androidEnv;\n\n this.setPlayerStatus('running');\n\n let agent: PageAgent | null = null;\n let freeFn: FreeFn[] = [];\n try {\n const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(\n platform as T,\n );\n agent = newAgent;\n const originalOnTaskStartTip = agent.onTaskStartTip;\n agent.onTaskStartTip = (tip) => {\n if (this.status === 'running') {\n this.agentStatusTip = tip;\n }\n originalOnTaskStartTip?.(tip);\n };\n freeFn = [\n ...(newFreeFn || []),\n {\n name: 'restore-agent-onTaskStartTip',\n fn: () => {\n if (agent) {\n agent.onTaskStartTip = originalOnTaskStartTip;\n }\n },\n },\n ];\n } catch (e) {\n this.setPlayerStatus('error', e as Error);\n return;\n }\n this.pageAgent = agent;\n\n let taskIndex = 0;\n this.setPlayerStatus('running');\n let errorFlag = false;\n while (taskIndex < tasks.length) {\n const taskStatus = this.taskStatusList[taskIndex];\n this.setTaskStatus(taskIndex, 'running' as any);\n this.setTaskIndex(taskIndex);\n\n try {\n await this.playTask(taskStatus, this.pageAgent);\n this.setTaskStatus(taskIndex, 'done' as any);\n } catch (e) {\n this.setTaskStatus(taskIndex, 'error' as any, e as Error);\n\n if (taskStatus.continueOnError) {\n // nothing more to do\n } else {\n this.reportFile = agent.reportFile;\n errorFlag = true;\n break;\n }\n }\n this.reportFile = agent.reportFile;\n taskIndex++;\n }\n\n if (errorFlag) {\n this.setPlayerStatus('error');\n } else {\n this.setPlayerStatus('done');\n }\n this.agentStatusTip = '';\n\n // free the resources\n for (const fn of freeFn) {\n try {\n // console.log('freeing', fn.name);\n await fn.fn();\n // console.log('freed', fn.name);\n } catch (e) {\n // console.error('error freeing', fn.name, e);\n }\n }\n }\n}\n","import type {\n MidsceneYamlScript,\n MidsceneYamlScriptWebEnv,\n MidsceneYamlTask,\n} from 'misoai-core';\nimport yaml from 'js-yaml';\n\nexport function buildYaml(\n env: MidsceneYamlScriptWebEnv,\n tasks: MidsceneYamlTask[],\n) {\n const result: MidsceneYamlScript = {\n target: env,\n tasks,\n };\n\n return yaml.dump(result, {\n indent: 2,\n });\n}\n","import { assert } from 'misoai-shared/utils';\nimport yaml from 'js-yaml';\n\nimport type { MidsceneYamlScript } from 'misoai-core';\n\nfunction interpolateEnvVars(content: string): string {\n return content.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\n const value = process.env[envVar.trim()];\n if (value === undefined) {\n throw new Error(`Environment variable \"${envVar.trim()}\" is not defined`);\n }\n return value;\n });\n}\n\nexport function parseYamlScript(\n content: string,\n filePath?: string,\n ignoreCheckingTarget?: boolean,\n): MidsceneYamlScript {\n let processedContent = content;\n if (content.indexOf('android') !== -1 && content.match(/deviceId:\\s*(\\d+)/)) {\n let matchedDeviceId;\n processedContent = content.replace(\n /deviceId:\\s*(\\d+)/g,\n (match, deviceId) => {\n matchedDeviceId = deviceId;\n return `deviceId: '${deviceId}'`;\n },\n );\n console.warn(\n `please use string-style deviceId in yaml script, for example: deviceId: \"${matchedDeviceId}\"`,\n );\n }\n const interpolatedContent = interpolateEnvVars(processedContent);\n const obj = yaml.load(interpolatedContent, {\n schema: yaml.JSON_SCHEMA,\n }) as MidsceneYamlScript;\n\n const pathTip = filePath ? `, failed to load ${filePath}` : '';\n const android =\n typeof obj.android !== 'undefined'\n ? Object.assign({}, obj.android || {})\n : undefined;\n const webConfig = obj.web || obj.target; // no need to handle null case, because web has required parameters url\n const web =\n typeof webConfig !== 'undefined'\n ? Object.assign({}, webConfig || {})\n : undefined;\n\n if (!ignoreCheckingTarget) {\n // make sure at least one of target/web/android is provided\n assert(\n web || android,\n `at least one of \"target\", \"web\", or \"android\" properties is required in yaml script${pathTip}`,\n );\n\n // make sure only one of target/web/android is provided\n assert(\n (web && !android) || (!web && android),\n `only one of \"target\", \"web\", or \"android\" properties is allowed in yaml script${pathTip}`,\n );\n\n // make sure the config is valid\n if (web || android) {\n assert(\n typeof web === 'object' || typeof android === 'object',\n `property \"target/web/android\" must be an object${pathTip}`,\n );\n }\n }\n\n assert(obj.tasks, `property \"tasks\" is required in yaml script ${pathTip}`);\n assert(\n Array.isArray(obj.tasks),\n `property \"tasks\" must be an array in yaml script, but got ${obj.tasks}`,\n );\n return obj;\n}\n"]}
package/dist/lib/agent.js CHANGED
@@ -51,10 +51,11 @@ var ScriptPlayer = class {
51
51
  this.unnamedResultIndex = 0;
52
52
  this.pageAgent = null;
53
53
  this.result = {};
54
+ const target = script.target || script.web || script.android;
54
55
  if (import_utils.ifInBrowser) {
55
56
  this.output = void 0;
56
- } else if (script.target?.output) {
57
- this.output = (0, import_node_path.resolve)(process.cwd(), script.target.output);
57
+ } else if (target?.output) {
58
+ this.output = (0, import_node_path.resolve)(process.cwd(), target.output);
58
59
  } else {
59
60
  this.output = (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
60
61
  }
@@ -128,12 +129,13 @@ var ScriptPlayer = class {
128
129
  } else if ("aiAssert" in flowItem) {
129
130
  const assertTask = flowItem;
130
131
  const prompt = assertTask.aiAssert;
132
+ const msg = assertTask.errorMessage;
131
133
  (0, import_utils.assert)(prompt, "missing prompt for aiAssert");
132
134
  (0, import_utils.assert)(
133
135
  typeof prompt === "string",
134
136
  "prompt for aiAssert must be a string"
135
137
  );
136
- await agent.aiAssert(prompt);
138
+ await agent.aiAssert(prompt, msg);
137
139
  } else if ("aiQuery" in flowItem) {
138
140
  const queryTask = flowItem;
139
141
  const prompt = queryTask.aiQuery;
@@ -1674,7 +1676,7 @@ var import_js_yaml3 = __toESM(require("js-yaml"));
1674
1676
  var import_semver = __toESM(require("semver"));
1675
1677
 
1676
1678
  // package.json
1677
- var version = "1.0.5";
1679
+ var version = "1.5.6";
1678
1680
 
1679
1681
  // src/common/task-cache.ts
1680
1682
  var debug3 = (0, import_logger3.getDebug)("cache");
@@ -1856,13 +1858,10 @@ var PageAgent = class {
1856
1858
  generateReport: true,
1857
1859
  autoPrintReportMsg: true,
1858
1860
  groupName: "Midscene Report",
1859
- groupDescription: "",
1860
- enableCumulativeContext: true,
1861
- autoClearContext: false
1861
+ groupDescription: ""
1862
1862
  },
1863
1863
  opts || {}
1864
1864
  );
1865
- this.initializeContextStore();
1866
1865
  if (this.page.pageType === "puppeteer" || this.page.pageType === "playwright") {
1867
1866
  this.page.waitForNavigationTimeout = this.opts.waitForNavigationTimeout || import_constants2.DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT;
1868
1867
  this.page.waitForNetworkIdleTimeout = this.opts.waitForNetworkIdleTimeout || import_constants2.DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;
@@ -1889,69 +1888,6 @@ var PageAgent = class {
1889
1888
  opts?.testId || this.page.pageType || "web"
1890
1889
  );
1891
1890
  }
1892
- /**
1893
- * Initialize context store for cumulative context functionality
1894
- */
1895
- async initializeContextStore() {
1896
- if (!this.opts.enableCumulativeContext) {
1897
- debug4("Cumulative context disabled via options");
1898
- return;
1899
- }
1900
- try {
1901
- const aiModel = await import("misoai-core/ai-model");
1902
- this.contextStore = aiModel.getContextStore();
1903
- debug4("Context store initialized successfully", {
1904
- autoClearContext: this.opts.autoClearContext,
1905
- testId: this.opts.testId
1906
- });
1907
- if (this.opts.autoClearContext) {
1908
- this.contextStore.clear();
1909
- debug4("Context store cleared due to autoClearContext option");
1910
- } else {
1911
- const existingData = this.contextStore.getAllData();
1912
- const existingSteps = this.contextStore.getRecentSteps(100).length;
1913
- debug4("Context store preserving existing data", {
1914
- existingDataKeys: Object.keys(existingData),
1915
- existingStepsCount: existingSteps
1916
- });
1917
- }
1918
- } catch (error) {
1919
- debug4("Failed to initialize context store:", error);
1920
- console.warn("⚠️ Could not initialize context store:", error);
1921
- }
1922
- }
1923
- /**
1924
- * Get the context store instance
1925
- */
1926
- getContextStore() {
1927
- return this.contextStore;
1928
- }
1929
- /**
1930
- * Clear the context store
1931
- */
1932
- clearContext() {
1933
- if (this.contextStore) {
1934
- this.contextStore.clear();
1935
- }
1936
- }
1937
- /**
1938
- * Get all stored data from context store
1939
- */
1940
- getStoredData() {
1941
- if (this.contextStore) {
1942
- return this.contextStore.getAllData();
1943
- }
1944
- return {};
1945
- }
1946
- /**
1947
- * Get step summary from context store
1948
- */
1949
- getStepSummary() {
1950
- if (this.contextStore) {
1951
- return this.contextStore.getStepSummary();
1952
- }
1953
- return "";
1954
- }
1955
1891
  async getUIContext(action) {
1956
1892
  if (action && (action === "extract" || action === "assert" || action === "captcha")) {
1957
1893
  return await parseContextFromWebPage(this.page, {
@@ -2187,31 +2123,6 @@ var PageAgent = class {
2187
2123
  };
2188
2124
  }
2189
2125
  async aiAction(taskPrompt, opt) {
2190
- if (this.opts.enableCumulativeContext && this.contextStore) {
2191
- try {
2192
- const originalPrompt = taskPrompt;
2193
- const processedPrompt = this.contextStore.replaceAllReferences(taskPrompt, "action");
2194
- if (originalPrompt !== processedPrompt) {
2195
- debug4("Context replacement in aiAction:", {
2196
- original: originalPrompt,
2197
- processed: processedPrompt,
2198
- storedData: this.contextStore.getAllData()
2199
- });
2200
- }
2201
- this.contextStore.addStep({
2202
- type: "action",
2203
- summary: `Action: ${processedPrompt}`,
2204
- prompt: processedPrompt
2205
- });
2206
- debug4("Added action step to context store:", {
2207
- stepNumber: this.contextStore.getRecentSteps(1)[0]?.stepNumber,
2208
- totalSteps: this.contextStore.getRecentSteps(100).length
2209
- });
2210
- taskPrompt = processedPrompt;
2211
- } catch (error) {
2212
- debug4("Context store operation failed:", error);
2213
- }
2214
- }
2215
2126
  const cacheable = opt?.cacheable;
2216
2127
  const isVlmUiTars = (0, import_env2.vlLocateMode)() === "vlm-ui-tars";
2217
2128
  const matchedCache = isVlmUiTars || cacheable === false ? void 0 : this.taskCache?.matchPlanCache(taskPrompt);
@@ -2258,75 +2169,7 @@ var PageAgent = class {
2258
2169
  };
2259
2170
  }
2260
2171
  async aiQuery(demand) {
2261
- let processedDemand = demand;
2262
- let storageKey;
2263
- try {
2264
- const aiModel = await import("misoai-core/ai-model");
2265
- const contextStore = aiModel.getContextStore();
2266
- if (typeof demand === "string") {
2267
- const storageInstruction = contextStore.parseStorageInstruction(demand);
2268
- if (storageInstruction) {
2269
- storageKey = storageInstruction.key;
2270
- processedDemand = storageInstruction.cleanText;
2271
- contextStore._pendingAliases = storageInstruction.aliases;
2272
- } else {
2273
- const storageMatch = demand.match(/store\s+(?:as\s+)?(\w+)/i);
2274
- if (storageMatch) {
2275
- storageKey = storageMatch[1];
2276
- processedDemand = demand.replace(/,?\s*store\s+(?:as\s+)?\w+/i, "").trim();
2277
- }
2278
- }
2279
- }
2280
- } catch (error) {
2281
- debug4("Context store not available:", error);
2282
- }
2283
- const { output, executor } = await this.taskExecutor.query(processedDemand);
2284
- if (this.opts.enableCumulativeContext && this.contextStore) {
2285
- if (storageKey && output) {
2286
- try {
2287
- const pendingAliases = this.contextStore._pendingAliases;
2288
- if (pendingAliases) {
2289
- this.contextStore.storeDataWithAliases(storageKey, output, pendingAliases, typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand));
2290
- delete this.contextStore._pendingAliases;
2291
- debug4("Stored query result with aliases:", {
2292
- key: storageKey,
2293
- value: output,
2294
- aliases: pendingAliases
2295
- });
2296
- } else {
2297
- this.contextStore.storeData(storageKey, output);
2298
- debug4("Stored query result:", {
2299
- key: storageKey,
2300
- value: output
2301
- });
2302
- }
2303
- this.contextStore.addStep({
2304
- type: "query",
2305
- summary: `Query: ${typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)} (stored as ${storageKey})`,
2306
- data: output,
2307
- prompt: typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)
2308
- });
2309
- debug4("Added query step to context store:", {
2310
- storageKey,
2311
- totalStoredItems: Object.keys(this.contextStore.getAllData()).length,
2312
- totalSteps: this.contextStore.getRecentSteps(100).length
2313
- });
2314
- } catch (error) {
2315
- debug4("Failed to store query result:", error);
2316
- }
2317
- } else {
2318
- try {
2319
- this.contextStore.addStep({
2320
- type: "query",
2321
- summary: `Query: ${typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)}`,
2322
- data: output,
2323
- prompt: typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)
2324
- });
2325
- } catch (error) {
2326
- debug4("Failed to add query step:", error);
2327
- }
2328
- }
2329
- }
2172
+ const { output, executor } = await this.taskExecutor.query(demand);
2330
2173
  const metadata = this.afterTaskRunning(executor);
2331
2174
  return {
2332
2175
  result: output,
@@ -2436,31 +2279,6 @@ var PageAgent = class {
2436
2279
  };
2437
2280
  }
2438
2281
  async aiAssert(assertion, msg, opt) {
2439
- let processedAssertion = assertion;
2440
- if (this.opts.enableCumulativeContext && this.contextStore) {
2441
- try {
2442
- const originalAssertion = assertion;
2443
- processedAssertion = this.contextStore.replaceAllReferences(assertion, "assertion");
2444
- if (originalAssertion !== processedAssertion) {
2445
- debug4("Context replacement in aiAssert:", {
2446
- original: originalAssertion,
2447
- processed: processedAssertion,
2448
- context: "assertion",
2449
- storedData: this.contextStore.getAllData()
2450
- });
2451
- }
2452
- this.contextStore.addStep({
2453
- type: "assertion",
2454
- summary: `Assertion: ${processedAssertion}`,
2455
- prompt: processedAssertion
2456
- });
2457
- debug4("Added assertion step to context store:", {
2458
- totalSteps: this.contextStore.getRecentSteps(100).length
2459
- });
2460
- } catch (error) {
2461
- debug4("Context store operation failed:", error);
2462
- }
2463
- }
2464
2282
  let currentUrl = "";
2465
2283
  if (this.page.url) {
2466
2284
  try {
@@ -2468,7 +2286,7 @@ var PageAgent = class {
2468
2286
  } catch (e) {
2469
2287
  }
2470
2288
  }
2471
- const assertionWithContext = currentUrl ? `For the page at URL "${currentUrl}", ${processedAssertion}` : processedAssertion;
2289
+ const assertionWithContext = currentUrl ? `For the page at URL "${currentUrl}", ${assertion}` : assertion;
2472
2290
  const { output, executor } = await this.taskExecutor.assert(assertionWithContext);
2473
2291
  const metadata = this.afterTaskRunning(executor, true);
2474
2292
  if (output && opt?.keepRawResponse) {