misoai-web 1.5.6 → 1.5.7

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 (75) hide show
  1. package/dist/es/agent.js +124 -21
  2. package/dist/es/agent.js.map +1 -1
  3. package/dist/es/bridge-mode-browser.js +10 -9
  4. package/dist/es/bridge-mode-browser.js.map +1 -1
  5. package/dist/es/bridge-mode.js +126 -23
  6. package/dist/es/bridge-mode.js.map +1 -1
  7. package/dist/es/chrome-extension.js +132 -28
  8. package/dist/es/chrome-extension.js.map +1 -1
  9. package/dist/es/index.js +140 -21
  10. package/dist/es/index.js.map +1 -1
  11. package/dist/es/midscene-playground.js +124 -21
  12. package/dist/es/midscene-playground.js.map +1 -1
  13. package/dist/es/midscene-server.js.map +1 -1
  14. package/dist/es/playground.js +124 -21
  15. package/dist/es/playground.js.map +1 -1
  16. package/dist/es/playwright-report.js +1 -1
  17. package/dist/es/playwright-report.js.map +1 -1
  18. package/dist/es/playwright.js +140 -21
  19. package/dist/es/playwright.js.map +1 -1
  20. package/dist/es/puppeteer-agent-launcher.js +124 -21
  21. package/dist/es/puppeteer-agent-launcher.js.map +1 -1
  22. package/dist/es/puppeteer.js +124 -21
  23. package/dist/es/puppeteer.js.map +1 -1
  24. package/dist/es/ui-utils.js.map +1 -1
  25. package/dist/es/utils.js +7 -4
  26. package/dist/es/utils.js.map +1 -1
  27. package/dist/es/yaml.js +24 -0
  28. package/dist/es/yaml.js.map +1 -1
  29. package/dist/lib/agent.js +122 -19
  30. package/dist/lib/agent.js.map +1 -1
  31. package/dist/lib/bridge-mode-browser.js +10 -9
  32. package/dist/lib/bridge-mode-browser.js.map +1 -1
  33. package/dist/lib/bridge-mode.js +124 -21
  34. package/dist/lib/bridge-mode.js.map +1 -1
  35. package/dist/lib/chrome-extension.js +130 -26
  36. package/dist/lib/chrome-extension.js.map +1 -1
  37. package/dist/lib/index.js +138 -19
  38. package/dist/lib/index.js.map +1 -1
  39. package/dist/lib/midscene-playground.js +122 -19
  40. package/dist/lib/midscene-playground.js.map +1 -1
  41. package/dist/lib/midscene-server.js.map +1 -1
  42. package/dist/lib/playground.js +122 -19
  43. package/dist/lib/playground.js.map +1 -1
  44. package/dist/lib/playwright-report.js +1 -1
  45. package/dist/lib/playwright-report.js.map +1 -1
  46. package/dist/lib/playwright.js +138 -19
  47. package/dist/lib/playwright.js.map +1 -1
  48. package/dist/lib/puppeteer-agent-launcher.js +122 -19
  49. package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
  50. package/dist/lib/puppeteer.js +122 -19
  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 +7 -4
  54. package/dist/lib/utils.js.map +1 -1
  55. package/dist/lib/yaml.js +24 -0
  56. package/dist/lib/yaml.js.map +1 -1
  57. package/dist/types/agent.d.ts +10 -7
  58. package/dist/types/bridge-mode-browser.d.ts +2 -3
  59. package/dist/types/bridge-mode.d.ts +2 -3
  60. package/dist/types/{browser-aec1055d.d.ts → browser-9b472ffb.d.ts} +1 -1
  61. package/dist/types/chrome-extension.d.ts +2 -3
  62. package/dist/types/index.d.ts +1 -2
  63. package/dist/types/midscene-server.d.ts +1 -2
  64. package/dist/types/{page-86ab0fe1.d.ts → page-ed0ecb44.d.ts} +19 -9
  65. package/dist/types/playground.d.ts +2 -3
  66. package/dist/types/playwright.d.ts +9 -2
  67. package/dist/types/puppeteer-agent-launcher.d.ts +1 -2
  68. package/dist/types/puppeteer.d.ts +6 -5
  69. package/dist/types/ui-utils.d.ts +1 -1
  70. package/dist/types/utils.d.ts +1 -2
  71. package/dist/types/yaml.d.ts +1 -2
  72. package/iife-script/htmlElement.js +51 -73
  73. package/iife-script/htmlElementDebug.js +33 -54
  74. package/package.json +23 -23
  75. package/LICENSE +0 -21
@@ -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 {\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
+ {"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,MAeA,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 | 'RightClick'\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"]}
package/dist/es/utils.js CHANGED
@@ -22,7 +22,8 @@ var WebElementInfo = class {
22
22
  id,
23
23
  attributes,
24
24
  indexId,
25
- xpaths
25
+ xpaths,
26
+ isVisible
26
27
  }) {
27
28
  this.content = content;
28
29
  this.rect = rect;
@@ -35,6 +36,7 @@ var WebElementInfo = class {
35
36
  this.attributes = attributes;
36
37
  this.indexId = indexId;
37
38
  this.xpaths = xpaths;
39
+ this.isVisible = isVisible;
38
40
  }
39
41
  };
40
42
 
@@ -57,14 +59,15 @@ async function parseContextFromWebPage(page, _opt) {
57
59
  })
58
60
  ]);
59
61
  const webTree = traverseTree(tree, (elementInfo) => {
60
- const { rect, id, content, attributes, locator, indexId } = elementInfo;
62
+ const { rect, id, content, attributes, locator, indexId, isVisible } = elementInfo;
61
63
  return new WebElementInfo({
62
64
  rect,
63
65
  locator,
64
66
  id,
65
67
  content,
66
68
  attributes,
67
- indexId
69
+ indexId,
70
+ isVisible
68
71
  });
69
72
  });
70
73
  assert(screenshotBase64, "screenshotBase64 is required");
@@ -132,7 +135,7 @@ function generateCacheId(fileName) {
132
135
  }
133
136
  var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";
134
137
  function replaceIllegalPathCharsAndSpace(str) {
135
- return str.replace(/[/\\:*?"<>| ]/g, "-");
138
+ return str.replace(/[:*?"<>| ]/g, "-");
136
139
  }
137
140
  function forceClosePopup(page, debug) {
138
141
  page.on("popup", async (popup) => {
@@ -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';\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"]}
1
+ {"version":3,"mappings":";AAQA,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,0BAA0B,mBAAmB;AAEtD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAEhC,SAAS,QAAQ,QAAQ,YAAY;AACrC,OAAO,WAAW;;;ACVX,IAAM,iBAAN,MAA4C;AAAA,EAsBjD,YAAY;AAAA,IACV;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAaG;AACD,SAAK,UAAU;AACf,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,MACZ,KAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,CAAC;AAAA,MACrC,KAAK,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AAAA,IACvC;AAEA,SAAK,UAAU;AACf,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AACF;;;ADxCA,eAAsB,wBACpB,MACA,MACuB;AACvB,SAAO,MAAM,kBAAkB;AAC/B,MAAK,KAAoB,sBAAsB;AAC7C,WAAO,MAAO,KAAa,qBAAqB;AAAA,EAClD;AACA,QAAM,MAAM,MAAM,KAAK,IAAI;AAC3B,yBAAuB,EAAE,SAAS,IAAI,CAAC;AAEvC,MAAI;AACJ,MAAI;AAEJ,QAAM,QAAQ,IAAI;AAAA,IAChB,KAAK,iBAAiB,EAAE,KAAK,CAAC,WAAW;AACvC,yBAAmB;AAAA,IACrB,CAAC;AAAA,IACD,KAAK,oBAAoB,EAAE,KAAK,OAAO,aAAa;AAClD,aAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AAED,QAAM,UAAU,aAAa,MAAO,CAAC,gBAAgB;AACnD,UAAM,EAAE,MAAM,IAAI,SAAS,YAAY,SAAS,SAAS,UAAU,IACjE;AACF,WAAO,IAAI,eAAe;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO,kBAAmB,8BAA8B;AAExD,QAAM,eAAe,WAAW,OAAO;AACvC,QAAM,OAAO,MAAM,KAAK,KAAK;AAE7B,MAAI,KAAK,OAAO,KAAK,MAAM,GAAG;AAE5B,uBAAmB,MAAM,gBAAgB,kBAAkB;AAAA,MACzD,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EAEH;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,eAAe,MAAM,OAAO;AAC1C,QAAM,gBAAgB,YAAY,wBAAwB;AAC1D,QAAM,qBAAqB,MAAM,EAAE,OAAO,qBAAqB;AAE/D,QAAM,WAAW,KAAK,EAAE,UAAU,GAAG,CAAC;AACtC,SAAO,GAAG,iBAAiB,GAAG,IAAI,kBAAkB,IAAI,QAAQ;AAClE;AAEO,SAAS,eAAe,UAAkB;AAC/C,SAAO,mCAAmC,QAAQ,EAAE;AACtD;AAMO,SAAS,wBAAwB,OAAgC;AACtE,QAAM,QAAQ,IAAI,MAAM;AACxB,QAAM,aAAa,SAAS,MAAM;AAClC,QAAM,SAAS,QAAQ,IAAI,KAAK;AAChC,MAAI,YAAY;AACd,UAAM,aAAa,WAAW,MAAM,IAAI;AACxC,eAAW,QAAQ,YAAY;AAC7B,UACE,KAAK,SAAS,QAAQ,KACtB,KAAK,SAAS,QAAQ,KACtB,KAAK,SAAS,KAAK,KACnB,KAAK,SAAS,KAAK,GACnB;AACA,cAAM,QAAQ,KAAK,MAAM,uCAAuC;AAChE,YAAI,QAAQ,CAAC,GAAG;AACd,gBAAM,iBAAiB,MAAM,CAAC,EAC3B,QAAQ,QAAQ,EAAE,EAClB,KAAK,EACL,QAAQ,OAAO,EAAE;AACpB,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,gBAAgB,oBAAI,IAAoB;AAEvC,SAAS,gBAAgB,UAA2B;AACzD,MAAI,WAAW,YAAY,wBAAwB;AACnD,MAAI,CAAC,UAAU;AACb,eAAW,KAAK;AAChB,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,IAAI,QAAQ,GAAG;AAC/B,UAAM,eAAe,cAAc,IAAI,QAAQ;AAC/C,QAAI,iBAAiB,QAAW;AAC9B,oBAAc,IAAI,UAAU,eAAe,CAAC;AAAA,IAC9C;AAAA,EACF,OAAO;AACL,kBAAc,IAAI,UAAU,CAAC;AAAA,EAC/B;AACA,SAAO,GAAG,QAAQ,IAAI,cAAc,IAAI,QAAQ,CAAC;AACnD;AAEO,IAAM,yCACX;AAEK,SAAS,gCAAgC,KAAa;AAE3D,SAAO,IAAI,QAAQ,eAAe,GAAG;AACvC;AAEO,SAAS,gBACd,MACA,OACA;AACA,OAAK,GAAG,SAAS,OAAO,UAAU;AAChC,QAAI,CAAC,OAAO;AACV,cAAQ,KAAK,yDAAyD;AACtE;AAAA,IACF;AACA,UAAM,MAAM,MAAO,MAAwB,IAAI;AAC/C,YAAQ,IAAI,iBAAiB,GAAG,EAAE;AAClC,QAAI,CAAE,MAAwB,SAAS,GAAG;AACxC,UAAI;AACF,cAAO,MAAwB,MAAM;AAAA,MACvC,SAAS,OAAO;AACd,cAAM,yBAAyB,GAAG,YAAY,KAAK,EAAE;AAAA,MACvD;AAAA,IACF,OAAO;AACL,YAAM,uCAAuC,GAAG,EAAE;AAAA,IACpD;AAEA,QAAI,CAAC,KAAK,SAAS,GAAG;AACpB,UAAI;AACF,cAAM,KAAK,KAAK,GAAG;AAAA,MACrB,SAAS,OAAO;AACd,cAAM,kBAAkB,GAAG,YAAY,KAAK,EAAE;AAAA,MAChD;AAAA,IACF,OAAO;AACL,YAAM,qCAAqC,GAAG,EAAE;AAAA,IAClD;AAAA,EACF,CAAC;AACH;AAEO,SAAS,qBACd,iBACA,MACA;AACA,MAAI,CAAC,iBAAiB;AACpB,WAAO;AAAA,EACT;AACA,MAAI,gBAAgB,IAAI;AACtB,WAAO,qBAAqB,gBAAgB,EAAE;AAAA,EAChD;AAEA,MAAI,gBAAgB,MAAM;AACxB,UAAM,iBAAiB;AAAA,MACrB,GAAG,KAAK,OAAO,gBAAgB,KAAK,CAAC,IAAI,gBAAgB,KAAK,CAAC,KAAK,CAAC;AAAA,MACrE,GAAG,KAAK,OAAO,gBAAgB,KAAK,CAAC,IAAI,gBAAgB,KAAK,CAAC,KAAK,CAAC;AAAA,IACvE;AACA,QAAI,UAAU,iCAAiC,MAAM,cAAc;AAEnE,QAAI,CAAC,SAAS;AACZ,gBAAU,0BAA0B,cAAc;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT","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, isVisible } =\n elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n isVisible,\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 // Only replace characters that are illegal in filenames, but preserve path separators\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 id: string;\n\n indexId: number;\n\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n\n xpaths?: string[];\n\n isVisible: boolean;\n\n constructor({\n content,\n rect,\n // page,\n locator,\n id,\n attributes,\n indexId,\n xpaths,\n isVisible,\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 isVisible: boolean;\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 this.isVisible = isVisible;\n }\n}\n"]}
package/dist/es/yaml.js CHANGED
@@ -101,6 +101,10 @@ var ScriptPlayer = class {
101
101
  } else if ("aiQuery" in flowItem) {
102
102
  const queryTask = flowItem;
103
103
  const prompt = queryTask.aiQuery;
104
+ const options = {
105
+ domIncluded: queryTask.domIncluded,
106
+ screenshotIncluded: queryTask.screenshotIncluded
107
+ };
104
108
  assert(prompt, "missing prompt for aiQuery");
105
109
  assert(
106
110
  typeof prompt === "string",
@@ -111,6 +115,10 @@ var ScriptPlayer = class {
111
115
  } else if ("aiNumber" in flowItem) {
112
116
  const numberTask = flowItem;
113
117
  const prompt = numberTask.aiNumber;
118
+ const options = {
119
+ domIncluded: numberTask.domIncluded,
120
+ screenshotIncluded: numberTask.screenshotIncluded
121
+ };
114
122
  assert(prompt, "missing prompt for number");
115
123
  assert(
116
124
  typeof prompt === "string",
@@ -121,6 +129,10 @@ var ScriptPlayer = class {
121
129
  } else if ("aiString" in flowItem) {
122
130
  const stringTask = flowItem;
123
131
  const prompt = stringTask.aiString;
132
+ const options = {
133
+ domIncluded: stringTask.domIncluded,
134
+ screenshotIncluded: stringTask.screenshotIncluded
135
+ };
124
136
  assert(prompt, "missing prompt for string");
125
137
  assert(
126
138
  typeof prompt === "string",
@@ -131,6 +143,10 @@ var ScriptPlayer = class {
131
143
  } else if ("aiBoolean" in flowItem) {
132
144
  const booleanTask = flowItem;
133
145
  const prompt = booleanTask.aiBoolean;
146
+ const options = {
147
+ domIncluded: booleanTask.domIncluded,
148
+ screenshotIncluded: booleanTask.screenshotIncluded
149
+ };
134
150
  assert(prompt, "missing prompt for boolean");
135
151
  assert(
136
152
  typeof prompt === "string",
@@ -173,6 +189,9 @@ var ScriptPlayer = class {
173
189
  } else if ("aiTap" in flowItem) {
174
190
  const tapTask = flowItem;
175
191
  await agent.aiTap(tapTask.aiTap, tapTask);
192
+ } else if ("aiRightClick" in flowItem) {
193
+ const rightClickTask = flowItem;
194
+ await agent.aiRightClick(rightClickTask.aiRightClick, rightClickTask);
176
195
  } else if ("aiHover" in flowItem) {
177
196
  const hoverTask = flowItem;
178
197
  await agent.aiHover(hoverTask.aiHover, hoverTask);
@@ -195,6 +214,11 @@ var ScriptPlayer = class {
195
214
  evaluateJavaScriptTask.javascript
196
215
  );
197
216
  this.setResult(evaluateJavaScriptTask.name, result);
217
+ } else if ("logScreenshot" in flowItem) {
218
+ const logScreenshotTask = flowItem;
219
+ await agent.logScreenshot(logScreenshotTask.logScreenshot, {
220
+ content: logScreenshotTask.content || ""
221
+ });
198
222
  } else {
199
223
  throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);
200
224
  }
@@ -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';\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"]}
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 MidsceneYamlFlowItemAIRightClick,\n MidsceneYamlFlowItemAIScroll,\n MidsceneYamlFlowItemAITap,\n MidsceneYamlFlowItemAIWaitFor,\n MidsceneYamlFlowItemEvaluateJavaScript,\n MidsceneYamlFlowItemLogScreenshot,\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 const options = {\n domIncluded: queryTask.domIncluded,\n screenshotIncluded: queryTask.screenshotIncluded,\n };\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 const options = {\n domIncluded: numberTask.domIncluded,\n screenshotIncluded: numberTask.screenshotIncluded,\n };\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 const options = {\n domIncluded: stringTask.domIncluded,\n screenshotIncluded: stringTask.screenshotIncluded,\n };\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 const options = {\n domIncluded: booleanTask.domIncluded,\n screenshotIncluded: booleanTask.screenshotIncluded,\n };\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 (\n 'aiRightClick' in (flowItem as MidsceneYamlFlowItemAIRightClick)\n ) {\n const rightClickTask = flowItem as MidsceneYamlFlowItemAIRightClick;\n await agent.aiRightClick(rightClickTask.aiRightClick, rightClickTask);\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 if (\n 'logScreenshot' in (flowItem as MidsceneYamlFlowItemLogScreenshot)\n ) {\n const logScreenshotTask = flowItem as MidsceneYamlFlowItemLogScreenshot;\n await agent.logScreenshot(logScreenshotTask.logScreenshot, {\n content: logScreenshotTask.content || '',\n });\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
@@ -139,6 +139,10 @@ var ScriptPlayer = class {
139
139
  } else if ("aiQuery" in flowItem) {
140
140
  const queryTask = flowItem;
141
141
  const prompt = queryTask.aiQuery;
142
+ const options = {
143
+ domIncluded: queryTask.domIncluded,
144
+ screenshotIncluded: queryTask.screenshotIncluded
145
+ };
142
146
  (0, import_utils.assert)(prompt, "missing prompt for aiQuery");
143
147
  (0, import_utils.assert)(
144
148
  typeof prompt === "string",
@@ -149,6 +153,10 @@ var ScriptPlayer = class {
149
153
  } else if ("aiNumber" in flowItem) {
150
154
  const numberTask = flowItem;
151
155
  const prompt = numberTask.aiNumber;
156
+ const options = {
157
+ domIncluded: numberTask.domIncluded,
158
+ screenshotIncluded: numberTask.screenshotIncluded
159
+ };
152
160
  (0, import_utils.assert)(prompt, "missing prompt for number");
153
161
  (0, import_utils.assert)(
154
162
  typeof prompt === "string",
@@ -159,6 +167,10 @@ var ScriptPlayer = class {
159
167
  } else if ("aiString" in flowItem) {
160
168
  const stringTask = flowItem;
161
169
  const prompt = stringTask.aiString;
170
+ const options = {
171
+ domIncluded: stringTask.domIncluded,
172
+ screenshotIncluded: stringTask.screenshotIncluded
173
+ };
162
174
  (0, import_utils.assert)(prompt, "missing prompt for string");
163
175
  (0, import_utils.assert)(
164
176
  typeof prompt === "string",
@@ -169,6 +181,10 @@ var ScriptPlayer = class {
169
181
  } else if ("aiBoolean" in flowItem) {
170
182
  const booleanTask = flowItem;
171
183
  const prompt = booleanTask.aiBoolean;
184
+ const options = {
185
+ domIncluded: booleanTask.domIncluded,
186
+ screenshotIncluded: booleanTask.screenshotIncluded
187
+ };
172
188
  (0, import_utils.assert)(prompt, "missing prompt for boolean");
173
189
  (0, import_utils.assert)(
174
190
  typeof prompt === "string",
@@ -211,6 +227,9 @@ var ScriptPlayer = class {
211
227
  } else if ("aiTap" in flowItem) {
212
228
  const tapTask = flowItem;
213
229
  await agent.aiTap(tapTask.aiTap, tapTask);
230
+ } else if ("aiRightClick" in flowItem) {
231
+ const rightClickTask = flowItem;
232
+ await agent.aiRightClick(rightClickTask.aiRightClick, rightClickTask);
214
233
  } else if ("aiHover" in flowItem) {
215
234
  const hoverTask = flowItem;
216
235
  await agent.aiHover(hoverTask.aiHover, hoverTask);
@@ -233,6 +252,11 @@ var ScriptPlayer = class {
233
252
  evaluateJavaScriptTask.javascript
234
253
  );
235
254
  this.setResult(evaluateJavaScriptTask.name, result);
255
+ } else if ("logScreenshot" in flowItem) {
256
+ const logScreenshotTask = flowItem;
257
+ await agent.logScreenshot(logScreenshotTask.logScreenshot, {
258
+ content: logScreenshotTask.content || ""
259
+ });
236
260
  } else {
237
261
  throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);
238
262
  }
@@ -481,7 +505,8 @@ var WebElementInfo = class {
481
505
  id,
482
506
  attributes,
483
507
  indexId,
484
- xpaths
508
+ xpaths,
509
+ isVisible
485
510
  }) {
486
511
  this.content = content;
487
512
  this.rect = rect;
@@ -494,6 +519,7 @@ var WebElementInfo = class {
494
519
  this.attributes = attributes;
495
520
  this.indexId = indexId;
496
521
  this.xpaths = xpaths;
522
+ this.isVisible = isVisible;
497
523
  }
498
524
  };
499
525
 
@@ -516,14 +542,15 @@ async function parseContextFromWebPage(page, _opt) {
516
542
  })
517
543
  ]);
518
544
  const webTree = (0, import_extractor.traverseTree)(tree, (elementInfo) => {
519
- const { rect, id, content, attributes, locator, indexId } = elementInfo;
545
+ const { rect, id, content, attributes, locator, indexId, isVisible } = elementInfo;
520
546
  return new WebElementInfo({
521
547
  rect,
522
548
  locator,
523
549
  id,
524
550
  content,
525
551
  attributes,
526
- indexId
552
+ indexId,
553
+ isVisible
527
554
  });
528
555
  });
529
556
  (0, import_utils4.assert)(screenshotBase64, "screenshotBase64 is required");
@@ -553,7 +580,7 @@ function printReportMsg(filepath) {
553
580
  (0, import_utils4.logMsg)(`Midscene - report file updated: ${filepath}`);
554
581
  }
555
582
  function replaceIllegalPathCharsAndSpace(str) {
556
- return str.replace(/[/\\:*?"<>| ]/g, "-");
583
+ return str.replace(/[:*?"<>| ]/g, "-");
557
584
  }
558
585
  function matchElementFromPlan(planLocateParam, tree) {
559
586
  if (!planLocateParam) {
@@ -839,10 +866,10 @@ var PageTaskExecutor = class {
839
866
  if (!taskParam || !taskParam.value) {
840
867
  return;
841
868
  }
842
- await this.page.keyboard.type(taskParam.value);
843
- } else {
844
- await this.page.keyboard.type(taskParam.value);
845
869
  }
870
+ await this.page.keyboard.type(taskParam.value, {
871
+ autoDismissKeyboard: taskParam.autoDismissKeyboard
872
+ });
846
873
  }
847
874
  };
848
875
  tasks.push(taskActionInput);
@@ -871,6 +898,22 @@ var PageTaskExecutor = class {
871
898
  }
872
899
  };
873
900
  tasks.push(taskActionTap);
901
+ } else if (plan2.type === "RightClick") {
902
+ const taskActionRightClick = {
903
+ type: "Action",
904
+ subType: "RightClick",
905
+ thought: plan2.thought,
906
+ locate: plan2.locate,
907
+ executor: async (param, { element }) => {
908
+ (0, import_utils6.assert)(element, "Element not found, cannot right click");
909
+ await this.page.mouse.click(
910
+ element.center[0],
911
+ element.center[1],
912
+ { button: "right" }
913
+ );
914
+ }
915
+ };
916
+ tasks.push(taskActionRightClick);
874
917
  } else if (plan2.type === "Drag") {
875
918
  const taskActionDrag = {
876
919
  type: "Action",
@@ -1399,7 +1442,7 @@ var PageTaskExecutor = class {
1399
1442
  executor: taskExecutor
1400
1443
  };
1401
1444
  }
1402
- async createTypeQueryTask(type, demand) {
1445
+ async createTypeQueryTask(type, demand, opt) {
1403
1446
  const taskExecutor = new import_misoai_core.Executor(
1404
1447
  taskTitleStr(
1405
1448
  type,
@@ -1430,7 +1473,10 @@ var PageTaskExecutor = class {
1430
1473
  result: `${type}, ${demand}`
1431
1474
  };
1432
1475
  }
1433
- const { data, usage } = await this.insight.extract(demandInput);
1476
+ const { data, usage } = await this.insight.extract(
1477
+ demandInput,
1478
+ opt
1479
+ );
1434
1480
  let outputResult = data;
1435
1481
  if (ifTypeRestricted) {
1436
1482
  (0, import_utils6.assert)(data?.result !== void 0, "No result in query data");
@@ -1450,17 +1496,17 @@ var PageTaskExecutor = class {
1450
1496
  executor: taskExecutor
1451
1497
  };
1452
1498
  }
1453
- async query(demand) {
1454
- return this.createTypeQueryTask("Query", demand);
1499
+ async query(demand, opt) {
1500
+ return this.createTypeQueryTask("Query", demand, opt);
1455
1501
  }
1456
- async boolean(prompt) {
1457
- return this.createTypeQueryTask("Boolean", prompt);
1502
+ async boolean(prompt, opt) {
1503
+ return this.createTypeQueryTask("Boolean", prompt, opt);
1458
1504
  }
1459
- async number(prompt) {
1460
- return this.createTypeQueryTask("Number", prompt);
1505
+ async number(prompt, opt) {
1506
+ return this.createTypeQueryTask("Number", prompt, opt);
1461
1507
  }
1462
- async string(prompt) {
1463
- return this.createTypeQueryTask("String", prompt);
1508
+ async string(prompt, opt) {
1509
+ return this.createTypeQueryTask("String", prompt, opt);
1464
1510
  }
1465
1511
  async assert(assertion) {
1466
1512
  const description = `assert: ${assertion}`;
@@ -1596,7 +1642,7 @@ function buildPlans(type, locateParam, param) {
1596
1642
  param: locateParam,
1597
1643
  thought: ""
1598
1644
  } : null;
1599
- if (type === "Tap" || type === "Hover") {
1645
+ if (type === "Tap" || type === "Hover" || type === "RightClick") {
1600
1646
  (0, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
1601
1647
  (0, import_utils8.assert)(locatePlan, `missing locate info for action "${type}"`);
1602
1648
  const tapPlan = {
@@ -1676,7 +1722,7 @@ var import_js_yaml3 = __toESM(require("js-yaml"));
1676
1722
  var import_semver = __toESM(require("semver"));
1677
1723
 
1678
1724
  // package.json
1679
- var version = "1.5.6";
1725
+ var version = "1.0.3";
1680
1726
 
1681
1727
  // src/common/task-cache.ts
1682
1728
  var debug3 = (0, import_logger3.getDebug)("cache");
@@ -1807,8 +1853,14 @@ cache file: ${cacheFile}`
1807
1853
  return;
1808
1854
  }
1809
1855
  try {
1856
+ const dir = (0, import_node_path2.dirname)(this.cacheFilePath);
1857
+ if (!(0, import_node_fs2.existsSync)(dir)) {
1858
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
1859
+ debug3("created cache directory: %s", dir);
1860
+ }
1810
1861
  const yamlData = import_js_yaml3.default.dump(this.cache);
1811
1862
  (0, import_node_fs2.writeFileSync)(this.cacheFilePath, yamlData);
1863
+ debug3("cache flushed to file: %s", this.cacheFilePath);
1812
1864
  } catch (err) {
1813
1865
  debug3(
1814
1866
  "write cache to file failed, path: %s, error: %s",
@@ -2066,6 +2118,23 @@ var PageAgent = class {
2066
2118
  metadata
2067
2119
  };
2068
2120
  }
2121
+ async aiRightClick(locatePrompt, opt) {
2122
+ const detailedLocateParam = this.buildDetailedLocateParam(
2123
+ locatePrompt,
2124
+ opt
2125
+ );
2126
+ const plans = buildPlans("RightClick", detailedLocateParam);
2127
+ const { executor, output } = await this.taskExecutor.runPlans(
2128
+ taskTitleStr("RightClick", locateParamStr(detailedLocateParam)),
2129
+ plans,
2130
+ { cacheable: opt?.cacheable }
2131
+ );
2132
+ const metadata = this.afterTaskRunning(executor);
2133
+ return {
2134
+ result: output,
2135
+ metadata
2136
+ };
2137
+ }
2069
2138
  async aiInput(value, locatePrompt, opt) {
2070
2139
  (0, import_utils12.assert)(
2071
2140
  typeof value === "string",
@@ -2497,6 +2566,40 @@ ${errors}`);
2497
2566
  }
2498
2567
  throw new Error("evaluateJavaScript is not supported in current agent");
2499
2568
  }
2569
+ async logScreenshot(title, options) {
2570
+ const screenshotTitle = title || "untitled";
2571
+ const content = options?.content || "";
2572
+ const screenshot = await this.page.screenshotBase64?.();
2573
+ if (screenshot) {
2574
+ const executionDump = {
2575
+ name: screenshotTitle,
2576
+ description: content,
2577
+ tasks: [{
2578
+ type: "Screenshot",
2579
+ subType: "log",
2580
+ status: "finished",
2581
+ executor: null,
2582
+ param: {
2583
+ title: screenshotTitle,
2584
+ content
2585
+ },
2586
+ output: {
2587
+ screenshot
2588
+ },
2589
+ thought: `Logged screenshot: ${screenshotTitle}`,
2590
+ timing: {
2591
+ start: Date.now(),
2592
+ end: Date.now(),
2593
+ cost: 0
2594
+ }
2595
+ }],
2596
+ sdkVersion: "1.0.0",
2597
+ logTime: Date.now(),
2598
+ model_name: "screenshot"
2599
+ };
2600
+ this.appendExecutionDump(executionDump);
2601
+ }
2602
+ }
2500
2603
  async destroy() {
2501
2604
  await this.page.destroy();
2502
2605
  }