@xopcai/xopc 0.0.146 → 0.0.147

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 (376) hide show
  1. package/README.md +2 -2
  2. package/dist/browser-ext/manifest.json +1 -1
  3. package/dist/extensions/demo-memory/src/index.js +6 -4
  4. package/dist/extensions/demo-memory/src/index.js.map +1 -1
  5. package/dist/extensions/telegram/xopc.extension.json +1 -1
  6. package/dist/gateway/static/root/assets/Combination-14nmIgxH.js +41 -0
  7. package/dist/gateway/static/root/assets/activity-store-DXeSSlw1.js +1 -0
  8. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-gBVDdd-Q.js → agent-avatar-dicebear-CFdJKSjY.js} +1 -1
  9. package/dist/gateway/static/root/assets/agents-DKjbRpbA.js +2 -0
  10. package/dist/gateway/static/root/assets/api-Bs69B5zA.js +1 -0
  11. package/dist/gateway/static/root/assets/{app-management-settings-panel-Dd5dmRWY.js → app-management-settings-panel-ag1ViXT_.js} +1 -1
  12. package/dist/gateway/static/root/assets/{appearance-settings-D89pY61C.js → appearance-settings-C6iMpwOM.js} +1 -1
  13. package/dist/gateway/static/root/assets/apps-page-CpX3lBFF.js +1 -0
  14. package/dist/gateway/static/root/assets/{archive-plugin-BZj1tk3p.js → archive-plugin-Dwi3mIBy.js} +1 -1
  15. package/dist/gateway/static/root/assets/{attachment-load-18eo6l3m.js → attachment-load-BaCDoq40.js} +1 -1
  16. package/dist/gateway/static/root/assets/{automation-suggestion-card-F1g3MckR.js → automation-suggestion-card-CwqtFgnw.js} +1 -1
  17. package/dist/gateway/static/root/assets/automations-page-DlupOxFQ.js +6 -0
  18. package/dist/gateway/static/root/assets/{binary-plugins-jOYMquLP.js → binary-plugins-DoAYhsxa.js} +1 -1
  19. package/dist/gateway/static/root/assets/{block-editor-CD77nXJU.js → block-editor-Bw72pO7S.js} +31 -31
  20. package/dist/gateway/static/root/assets/browser-settings-page-BwLoUBcZ.js +8 -0
  21. package/dist/gateway/static/root/assets/capability-presets-api-vdbAR65Q.js +1 -0
  22. package/dist/gateway/static/root/assets/capability-presets-settings-panel-BAsKbJgm.js +2 -0
  23. package/dist/gateway/static/root/assets/{channel-recipient-api-fTlTnWB4.js → channel-recipient-api-rTG26jpV.js} +1 -1
  24. package/dist/gateway/static/root/assets/channels-settings-CxFWy0cE.js +1 -0
  25. package/dist/gateway/static/root/assets/channels-status-swr-OKlqTtKJ.js +1 -0
  26. package/dist/gateway/static/root/assets/{connector-logo-g9f07qO6.js → connector-logo-DIMP4NWY.js} +1 -1
  27. package/dist/gateway/static/root/assets/connectors-page-BjQQwX62.js +2 -0
  28. package/dist/gateway/static/root/assets/{desktop-pet-DwPVWi3D.js → desktop-pet-kQq2e0BN.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-settings-Dm1N7RRX.js → desktop-pet-settings-B6vtozMY.js} +1 -1
  30. package/dist/gateway/static/root/assets/{directory-picker-path-field-hZghTKie.js → directory-picker-path-field-Dn5-e3v4.js} +1 -1
  31. package/dist/gateway/static/root/assets/dist-C230izr8.js +1 -0
  32. package/dist/gateway/static/root/assets/dist-CxGUk8J-.js +1 -0
  33. package/dist/gateway/static/root/assets/dreaming-settings-BzwIujPG.js +3 -0
  34. package/dist/gateway/static/root/assets/extension-debug-page-D_e_hsEy.js +1 -0
  35. package/dist/gateway/static/root/assets/{extension-page-otKJCjKP.js → extension-page-D15CWHBd.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-settings-page-ifHzMfT-.js → extension-settings-page-D4P0n1om.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-api-BQX5GGl2.js → gateway-config-api-B4qZctjG.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-settings-BfXo0j0O.js → gateway-settings-BvndV7TP.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-DtoLihQE.js → gateway-startup-retry-BpgMunEb.js} +1 -1
  40. package/dist/gateway/static/root/assets/{goal-create-dialog-C_Qw_QgW.js → goal-create-dialog-DrVHKXHq.js} +1 -1
  41. package/dist/gateway/static/root/assets/goal-detail-page-PHlCwv68.js +4 -0
  42. package/dist/gateway/static/root/assets/{goals-config-api-DQJ5mBXB.js → goals-config-api-B6YJB_ct.js} +1 -1
  43. package/dist/gateway/static/root/assets/goals-page-Hg9uyO6e.js +2 -0
  44. package/dist/gateway/static/root/assets/{goals-settings-CQX8zWKe.js → goals-settings-BROWCIcL.js} +1 -1
  45. package/dist/gateway/static/root/assets/{heartbeat-settings-yy_Pt_24.js → heartbeat-settings-CoCwIOIg.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-CxkNzw3e.js +259 -0
  47. package/dist/gateway/static/root/assets/index-DjCSOyaN.css +1 -0
  48. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-Bm_AIYQ7.js +1 -0
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-BJWFcxwG.js +2 -0
  50. package/dist/gateway/static/root/assets/local-apps-page-BsiZMrEf.js +1 -0
  51. package/dist/gateway/static/root/assets/{locale-store-CiM-D9jA.js → locale-store-Bp8joZck.js} +1 -1
  52. package/dist/gateway/static/root/assets/logs-page-DOmK61ey.js +7 -0
  53. package/dist/gateway/static/root/assets/{markdown-split-BYasrWPk.js → markdown-split-Dy7QpaAz.js} +1 -1
  54. package/dist/gateway/static/root/assets/{media-plugins--cJpmHc2.js → media-plugins-3Lxliy9D.js} +1 -1
  55. package/dist/gateway/static/root/assets/messages-CQGpD-dC.js +5 -0
  56. package/dist/gateway/static/root/assets/models-hub-panel-DGCZom4o.js +2 -0
  57. package/dist/gateway/static/root/assets/{notes-page-Digm3EgB.js → notes-page-BJtT4vSf.js} +1 -1
  58. package/dist/gateway/static/root/assets/notes-workbench-B_bN7cOR.js +4 -0
  59. package/dist/gateway/static/root/assets/{page-tabs-CGVMJEW0.js → page-tabs-DKB6RDre.js} +1 -1
  60. package/dist/gateway/static/root/assets/{preview-open-alternatives-BBAkU4xW.js → preview-open-alternatives-Br_YhYHp.js} +1 -1
  61. package/dist/gateway/static/root/assets/project-detail-page-Dq8JouJj.js +1 -0
  62. package/dist/gateway/static/root/assets/projects-page-DvfL7fSe.js +1 -0
  63. package/dist/gateway/static/root/assets/remote-access-hub-DqwMXSXP.js +1 -0
  64. package/dist/gateway/static/root/assets/{save-bar-controls-D4B469dh.js → save-bar-controls-BgIE9BBh.js} +1 -1
  65. package/dist/gateway/static/root/assets/{schedule-field-Vy4OjH0i.js → schedule-field-BXDP-oMz.js} +1 -1
  66. package/dist/gateway/static/root/assets/{schema-form-CPYzgmby.js → schema-form-Dhv-SmjG.js} +1 -1
  67. package/dist/gateway/static/root/assets/sessions-page-B_mOZkQk.js +1 -0
  68. package/dist/gateway/static/root/assets/settings-advanced-gate-BHGmH6YS.js +1 -0
  69. package/dist/gateway/static/root/assets/{settings-collapsible-section-BB5y9vwh.js → settings-collapsible-section-CUTExnlK.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-form-section-BfzJkVHd.js → settings-form-section-CmnDhsf0.js} +1 -1
  71. package/dist/gateway/static/root/assets/{settings-loading-skeleton-DM8iH25s.js → settings-loading-skeleton-B79dq-XY.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-page-h5Alz_fA.js → settings-page-Db8uAv3X.js} +1 -1
  73. package/dist/gateway/static/root/assets/{settings-page-layout-DjbdpK2j.js → settings-page-layout-DaFwstQw.js} +1 -1
  74. package/dist/gateway/static/root/assets/setup-status-panel-BUSEj0KK.js +1 -0
  75. package/dist/gateway/static/root/assets/{share-preview-page-DxozU_zz.js → share-preview-page-Bi00uXzV.js} +1 -1
  76. package/dist/gateway/static/root/assets/shares-settings-DzsTYfuX.js +1 -0
  77. package/dist/gateway/static/root/assets/skill-reload-api-DSsdU081.js +1 -0
  78. package/dist/gateway/static/root/assets/skills-page-DeB1tQZL.js +2 -0
  79. package/dist/gateway/static/root/assets/{system-settings-panel-DNi9Ofuj.js → system-settings-panel-Cxaph7hW.js} +1 -1
  80. package/dist/gateway/static/root/assets/{theme-store-C9Dxcppm.js → theme-store-eCTN1RpG.js} +1 -1
  81. package/dist/gateway/static/root/assets/{typed-models-lib-B6Nw3iFM.js → typed-models-lib-CcZ_tyFY.js} +1 -1
  82. package/dist/gateway/static/root/assets/{use-channel-catalog-DE9cpn4G.js → use-channel-catalog-C4PWbNoi.js} +1 -1
  83. package/dist/gateway/static/root/assets/user-context-page-QTVMx_hG.js +1 -0
  84. package/dist/gateway/static/root/assets/{user-profile-fields-editor-DVBShNYF.js → user-profile-fields-editor-CPOO9Jz5.js} +1 -1
  85. package/dist/gateway/static/root/assets/{user-profile-settings-panel-CC0sf3vS.js → user-profile-settings-panel-C8cgtSF7.js} +1 -1
  86. package/dist/gateway/static/root/assets/{utils-DnsuE4YP.js → utils-CiZK2KwH.js} +1 -1
  87. package/dist/gateway/static/root/assets/voice-api-key-field-b-YZtvUu.js +1 -0
  88. package/dist/gateway/static/root/assets/work-discovery-page-4zVDOl2G.js +1 -0
  89. package/dist/gateway/static/root/assets/work-item-detail-page-CHe2tfkc.js +1 -0
  90. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DXc_pbVG.js → workflow-run-setup-panel-8SLCYeXE.js} +2 -2
  91. package/dist/gateway/static/root/assets/workflows-page-DEOFqiWn.js +11 -0
  92. package/dist/gateway/static/root/index.html +10 -9
  93. package/dist/package.js +1 -1
  94. package/dist/src/activity/types.d.ts +1 -1
  95. package/dist/src/agent/agent-instance-gateway.d.ts +1 -1
  96. package/dist/src/agent/agent-manager.d.ts +1 -1
  97. package/dist/src/agent/agent-manager.js +5 -4
  98. package/dist/src/agent/agent-manager.js.map +1 -1
  99. package/dist/src/agent/background-review/coordinator.js +0 -1
  100. package/dist/src/agent/background-review/coordinator.js.map +1 -1
  101. package/dist/src/agent/background-review/settings.d.ts +1 -1
  102. package/dist/src/agent/background-review/settings.js +10 -17
  103. package/dist/src/agent/background-review/settings.js.map +1 -1
  104. package/dist/src/agent/bootstrap/filter-bootstrap-files.d.ts +1 -1
  105. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +1 -1
  106. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  107. package/dist/src/agent/bootstrap/load-bootstrap-files.js +2 -3
  108. package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
  109. package/dist/src/agent/code-intelligence/binary.js +1 -1
  110. package/dist/src/agent/context/project-context.d.ts +14 -0
  111. package/dist/src/agent/context/project-context.js +23 -1
  112. package/dist/src/agent/context/project-context.js.map +1 -1
  113. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  114. package/dist/src/agent/context/workspace.d.ts +1 -2
  115. package/dist/src/agent/context/workspace.js +2 -4
  116. package/dist/src/agent/context/workspace.js.map +1 -1
  117. package/dist/src/agent/embedded/run-for-session.js +1 -2
  118. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  119. package/dist/src/agent/embedded/run-turn.js +6 -1
  120. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  121. package/dist/src/agent/embedded/types.d.ts +17 -0
  122. package/dist/src/agent/memory/builtin-memory-store.d.ts +2 -2
  123. package/dist/src/agent/memory/builtin-memory-store.js +2 -2
  124. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  125. package/dist/src/agent/memory/builtin-provider.d.ts +1 -3
  126. package/dist/src/agent/memory/builtin-provider.js +21 -48
  127. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  128. package/dist/src/agent/memory/context/planner.js +48 -9
  129. package/dist/src/agent/memory/context/planner.js.map +1 -1
  130. package/dist/src/agent/memory/context/types.d.ts +7 -1
  131. package/dist/src/agent/memory/context-fence.js +1 -1
  132. package/dist/src/agent/memory/context-fence.js.map +1 -1
  133. package/dist/src/agent/memory/create-memory-manager.js +2 -9
  134. package/dist/src/agent/memory/create-memory-manager.js.map +1 -1
  135. package/dist/src/agent/memory/dreaming/config.js +4 -9
  136. package/dist/src/agent/memory/dreaming/config.js.map +1 -1
  137. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js +2 -2
  138. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js.map +1 -1
  139. package/dist/src/agent/memory/dreaming/scope.d.ts +2 -3
  140. package/dist/src/agent/memory/dreaming/scope.js +8 -8
  141. package/dist/src/agent/memory/dreaming/scope.js.map +1 -1
  142. package/dist/src/agent/memory/manager.d.ts +1 -5
  143. package/dist/src/agent/memory/manager.js +2 -23
  144. package/dist/src/agent/memory/manager.js.map +1 -1
  145. package/dist/src/agent/memory/memory-config.d.ts +3 -3
  146. package/dist/src/agent/memory/memory-config.js +9 -14
  147. package/dist/src/agent/memory/memory-config.js.map +1 -1
  148. package/dist/src/agent/memory/types.d.ts +9 -5
  149. package/dist/src/agent/memory/understanding/service.js +21 -8
  150. package/dist/src/agent/memory/understanding/service.js.map +1 -1
  151. package/dist/src/agent/memory/understanding/types.d.ts +5 -0
  152. package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -1
  153. package/dist/src/agent/memory/user-context-coordinator.js +6 -2
  154. package/dist/src/agent/memory/user-context-coordinator.js.map +1 -1
  155. package/dist/src/agent/orchestration/agent-orchestrator.js +2 -2
  156. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  157. package/dist/src/agent/prompt/memory/index.d.ts +1 -2
  158. package/dist/src/agent/prompt/memory/index.js +6 -7
  159. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  160. package/dist/src/agent/prompt/sections/tooling.js +1 -1
  161. package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
  162. package/dist/src/agent/service/direct-turn-helpers.js +13 -2
  163. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  164. package/dist/src/agent/tools/curated-memory-tool.js +4 -4
  165. package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
  166. package/dist/src/agent/tools/factory.d.ts +1 -1
  167. package/dist/src/agent/tools/factory.js +2 -2
  168. package/dist/src/agent/tools/factory.js.map +1 -1
  169. package/dist/src/agent/tools/memory-tool.js +1 -1
  170. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  171. package/dist/src/agent/workflow/workflow-child-tools.js +2 -3
  172. package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -1
  173. package/dist/src/agent/workspace-runtime/registry.d.ts +5 -5
  174. package/dist/src/agent/workspace-runtime/registry.js +6 -11
  175. package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
  176. package/dist/src/agent-manifest/index.js +2 -2
  177. package/dist/src/agent-manifest/prompt.js +1 -7
  178. package/dist/src/agent-manifest/prompt.js.map +1 -1
  179. package/dist/src/agent-manifest/schema.d.ts +0 -598
  180. package/dist/src/agent-manifest/schema.js +2 -143
  181. package/dist/src/agent-manifest/schema.js.map +1 -1
  182. package/dist/src/agent-manifest/validator.js +0 -12
  183. package/dist/src/agent-manifest/validator.js.map +1 -1
  184. package/dist/src/agent-runtime/memory-runtime.d.ts +2 -2
  185. package/dist/src/agent-runtime/memory-runtime.js +8 -7
  186. package/dist/src/agent-runtime/memory-runtime.js.map +1 -1
  187. package/dist/src/agent-runtime/runtime-profile.d.ts +2 -0
  188. package/dist/src/agent-runtime/runtime-profile.js +1 -1
  189. package/dist/src/agent-runtime/runtime-profile.js.map +1 -1
  190. package/dist/src/chat-commands/agent-edit.js +1 -2
  191. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  192. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  193. package/dist/src/cli/commands/init.js +0 -22
  194. package/dist/src/cli/commands/init.js.map +1 -1
  195. package/dist/src/cli/templates.d.ts +1 -1
  196. package/dist/src/cli/templates.js +1 -16
  197. package/dist/src/cli/templates.js.map +1 -1
  198. package/dist/src/commands/agents.config.js +0 -10
  199. package/dist/src/commands/agents.config.js.map +1 -1
  200. package/dist/src/config/index.js +2 -2
  201. package/dist/src/config/paths.d.ts +0 -1
  202. package/dist/src/config/paths.js +1 -2
  203. package/dist/src/config/paths.js.map +1 -1
  204. package/dist/src/config/schema.d.ts +118 -532
  205. package/dist/src/config/schema.js +32 -26
  206. package/dist/src/config/schema.js.map +1 -1
  207. package/dist/src/extensions/loader.js +2 -1
  208. package/dist/src/extensions/loader.js.map +1 -1
  209. package/dist/src/gateway/agents-admin.d.ts +0 -2
  210. package/dist/src/gateway/agents-admin.js +0 -6
  211. package/dist/src/gateway/agents-admin.js.map +1 -1
  212. package/dist/src/gateway/capability-presets-admin.d.ts +0 -2
  213. package/dist/src/gateway/capability-presets-admin.js.map +1 -1
  214. package/dist/src/gateway/chat-stream/mapper.js +26 -0
  215. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  216. package/dist/src/gateway/chat-stream/protocol.d.ts +16 -1
  217. package/dist/src/gateway/dreaming-automation-reconciler.d.ts +0 -1
  218. package/dist/src/gateway/dreaming-automation-reconciler.js +41 -63
  219. package/dist/src/gateway/dreaming-automation-reconciler.js.map +1 -1
  220. package/dist/src/gateway/hono/app.js +3 -1
  221. package/dist/src/gateway/hono/app.js.map +1 -1
  222. package/dist/src/gateway/hono/lib/config-payload.d.ts +0 -130
  223. package/dist/src/gateway/hono/lib/config-payload.js +0 -1
  224. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  225. package/dist/src/gateway/hono/routes/agents.js +6 -12
  226. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  227. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +6 -0
  228. package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
  229. package/dist/src/gateway/hono/routes/capability-presets.js +2 -2
  230. package/dist/src/gateway/hono/routes/capability-presets.js.map +1 -1
  231. package/dist/src/gateway/hono/routes/dreaming.js +9 -20
  232. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  233. package/dist/src/gateway/hono/routes/index.js +4 -0
  234. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  235. package/dist/src/gateway/hono/routes/local-apps.d.ts +5 -0
  236. package/dist/src/gateway/hono/routes/local-apps.js +99 -0
  237. package/dist/src/gateway/hono/routes/local-apps.js.map +1 -0
  238. package/dist/src/gateway/hono/routes/memory.js +48 -65
  239. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  240. package/dist/src/gateway/hono/routes/projects.js +2 -2
  241. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  242. package/dist/src/gateway/hono/routes/work-discovery.d.ts +3 -0
  243. package/dist/src/gateway/hono/routes/work-discovery.js +150 -0
  244. package/dist/src/gateway/hono/routes/work-discovery.js.map +1 -0
  245. package/dist/src/gateway/hono/routes/you.d.ts +3 -0
  246. package/dist/src/gateway/hono/routes/you.js +358 -91
  247. package/dist/src/gateway/hono/routes/you.js.map +1 -1
  248. package/dist/src/gateway/service.d.ts +3 -0
  249. package/dist/src/gateway/service.js +12 -0
  250. package/dist/src/gateway/service.js.map +1 -1
  251. package/dist/src/knowledge/connected-knowledge-pipeline.js +9 -5
  252. package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -1
  253. package/dist/src/local-apps/acceptance.d.ts +30 -0
  254. package/dist/src/local-apps/acceptance.js +93 -0
  255. package/dist/src/local-apps/acceptance.js.map +1 -0
  256. package/dist/src/local-apps/index.d.ts +5 -0
  257. package/dist/src/local-apps/index.js +4 -0
  258. package/dist/src/local-apps/preview-runtime-bridge.d.ts +4 -0
  259. package/dist/src/local-apps/preview-runtime-bridge.js +171 -0
  260. package/dist/src/local-apps/preview-runtime-bridge.js.map +1 -0
  261. package/dist/src/local-apps/runtime-entry.d.ts +8 -0
  262. package/dist/src/local-apps/runtime-entry.js +25 -0
  263. package/dist/src/local-apps/runtime-entry.js.map +1 -0
  264. package/dist/src/local-apps/scaffold.d.ts +8 -0
  265. package/dist/src/local-apps/scaffold.js +122 -0
  266. package/dist/src/local-apps/scaffold.js.map +1 -0
  267. package/dist/src/local-apps/service.d.ts +37 -0
  268. package/dist/src/local-apps/service.js +553 -0
  269. package/dist/src/local-apps/service.js.map +1 -0
  270. package/dist/src/local-apps/store.d.ts +44 -0
  271. package/dist/src/local-apps/store.js +195 -0
  272. package/dist/src/local-apps/store.js.map +1 -0
  273. package/dist/src/local-apps/types.d.ts +105 -0
  274. package/dist/src/local-apps/types.js +1 -0
  275. package/dist/src/storage/sqlite/index.d.ts +3 -2
  276. package/dist/src/storage/sqlite/index.js +4 -3
  277. package/dist/src/storage/sqlite/memory-index-repository.d.ts +2 -3
  278. package/dist/src/storage/sqlite/memory-index-repository.js +16 -20
  279. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
  280. package/dist/src/storage/sqlite/memory-records-repository.d.ts +18 -10
  281. package/dist/src/storage/sqlite/memory-records-repository.js +79 -34
  282. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  283. package/dist/src/storage/sqlite/memory-reference-consent-repository.d.ts +27 -0
  284. package/dist/src/storage/sqlite/memory-reference-consent-repository.js +109 -0
  285. package/dist/src/storage/sqlite/memory-reference-consent-repository.js.map +1 -0
  286. package/dist/src/storage/sqlite/migrations/038_user_owned_memory.sql +68 -0
  287. package/dist/src/storage/sqlite/migrations/039_memory_reference_consents.sql +19 -0
  288. package/dist/src/storage/sqlite/migrations/040_local_apps.sql +32 -0
  289. package/dist/src/storage/sqlite/migrations/041_local_app_release_lifecycle.sql +17 -0
  290. package/dist/src/storage/sqlite/migrations/042_work_discovery.sql +41 -0
  291. package/dist/src/storage/sqlite/migrations/043_local_app_acceptance_runs.sql +16 -0
  292. package/dist/src/storage/sqlite/migrations/044_repair_work_discovery_foreign_keys.sql +75 -0
  293. package/dist/src/storage/sqlite/migrations/045_extension_ui_grants.sql +13 -0
  294. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  295. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  296. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  297. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  298. package/dist/src/storage/sqlite/schema.js +1 -1
  299. package/dist/src/storage/sqlite/schema.sql +20 -0
  300. package/dist/src/user-context/config.d.ts +227 -0
  301. package/dist/src/user-context/config.js +158 -0
  302. package/dist/src/user-context/config.js.map +1 -0
  303. package/dist/src/user-context/import.d.ts +1 -1
  304. package/dist/src/user-context/import.js +6 -1
  305. package/dist/src/user-context/import.js.map +1 -1
  306. package/dist/src/user-context/owner.d.ts +5 -0
  307. package/dist/src/user-context/owner.js +9 -0
  308. package/dist/src/user-context/owner.js.map +1 -0
  309. package/dist/src/user-context/projection.d.ts +11 -9
  310. package/dist/src/user-context/projection.js +74 -47
  311. package/dist/src/user-context/projection.js.map +1 -1
  312. package/dist/src/user-context/runtime.d.ts +13 -0
  313. package/dist/src/user-context/runtime.js +36 -0
  314. package/dist/src/user-context/runtime.js.map +1 -0
  315. package/dist/src/work-discovery/analyzer.d.ts +11 -0
  316. package/dist/src/work-discovery/analyzer.js +171 -0
  317. package/dist/src/work-discovery/analyzer.js.map +1 -0
  318. package/dist/src/work-discovery/index.d.ts +5 -0
  319. package/dist/src/work-discovery/index.js +5 -0
  320. package/dist/src/work-discovery/probe.d.ts +12 -0
  321. package/dist/src/work-discovery/probe.js +335 -0
  322. package/dist/src/work-discovery/probe.js.map +1 -0
  323. package/dist/src/work-discovery/repository.d.ts +11 -0
  324. package/dist/src/work-discovery/repository.js +100 -0
  325. package/dist/src/work-discovery/repository.js.map +1 -0
  326. package/dist/src/work-discovery/service.d.ts +30 -0
  327. package/dist/src/work-discovery/service.js +387 -0
  328. package/dist/src/work-discovery/service.js.map +1 -0
  329. package/dist/src/work-discovery/types.d.ts +114 -0
  330. package/dist/src/work-discovery/types.js +1 -0
  331. package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
  332. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  333. package/package.json +4 -3
  334. package/skills/engineering/build-xopc-local-app/SKILL.md +45 -0
  335. package/skills/engineering/build-xopc-local-app/references/app-contract.md +33 -0
  336. package/skills/engineering/build-xopc-local-app/scripts/validate-local-app.mjs +72 -0
  337. package/dist/gateway/static/root/assets/Combination-DolwnuKi.js +0 -41
  338. package/dist/gateway/static/root/assets/activity-center-store-3l9ahUqb.js +0 -1
  339. package/dist/gateway/static/root/assets/agents-BLp5ItD3.js +0 -2
  340. package/dist/gateway/static/root/assets/apps-page-CgOy905b.js +0 -1
  341. package/dist/gateway/static/root/assets/automations-page-Bdj0A_IC.js +0 -6
  342. package/dist/gateway/static/root/assets/browser-settings-page-D73ClpC2.js +0 -8
  343. package/dist/gateway/static/root/assets/capability-presets-api-DZwIrY_P.js +0 -1
  344. package/dist/gateway/static/root/assets/capability-presets-settings-panel-Dw2uinRX.js +0 -2
  345. package/dist/gateway/static/root/assets/channels-settings-g9TZdnQh.js +0 -1
  346. package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +0 -1
  347. package/dist/gateway/static/root/assets/connectors-page-CJLsXBB0.js +0 -2
  348. package/dist/gateway/static/root/assets/dist-C99RGfAH.js +0 -1
  349. package/dist/gateway/static/root/assets/dreaming-settings-B_LcHWcv.js +0 -3
  350. package/dist/gateway/static/root/assets/extension-debug-page-BqguEetG.js +0 -1
  351. package/dist/gateway/static/root/assets/goal-detail-page-BnUrr9_O.js +0 -4
  352. package/dist/gateway/static/root/assets/goals-page-BgDCoiG0.js +0 -2
  353. package/dist/gateway/static/root/assets/index-BJGusbyV.js +0 -259
  354. package/dist/gateway/static/root/assets/index-CSLF84-Q.css +0 -1
  355. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-D1rVyDdf.js +0 -1
  356. package/dist/gateway/static/root/assets/logs-page-C35Xa_mx.js +0 -7
  357. package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +0 -5
  358. package/dist/gateway/static/root/assets/models-hub-panel-CqehWxbT.js +0 -2
  359. package/dist/gateway/static/root/assets/notes-workbench-CCuL7qlg.js +0 -4
  360. package/dist/gateway/static/root/assets/project-detail-page-DTWPEtSE.js +0 -1
  361. package/dist/gateway/static/root/assets/projects-page-NnJpsIAD.js +0 -1
  362. package/dist/gateway/static/root/assets/remote-access-hub-wbk1GCvg.js +0 -1
  363. package/dist/gateway/static/root/assets/sessions-page-Do0ZNuqM.js +0 -1
  364. package/dist/gateway/static/root/assets/settings-advanced-gate-HeYP_JCr.js +0 -1
  365. package/dist/gateway/static/root/assets/setup-status-panel-bjbH7U6x.js +0 -1
  366. package/dist/gateway/static/root/assets/shares-settings-Cl_bE9gQ.js +0 -1
  367. package/dist/gateway/static/root/assets/skill-reload-api-DcqbKYgB.js +0 -1
  368. package/dist/gateway/static/root/assets/skills-page-D_Xkjxtr.js +0 -2
  369. package/dist/gateway/static/root/assets/user-context-page-BtU2sQoK.js +0 -1
  370. package/dist/gateway/static/root/assets/voice-api-key-field-BIcSMf0O.js +0 -1
  371. package/dist/gateway/static/root/assets/work-item-detail-page-ChCKymS7.js +0 -1
  372. package/dist/gateway/static/root/assets/workflows-page-CheHRVox.js +0 -11
  373. package/dist/src/agent/context/workspace-templates/MEMORY.md +0 -33
  374. package/dist/src/agent/memory/access-policy.d.ts +0 -12
  375. package/dist/src/agent/memory/access-policy.js +0 -44
  376. package/dist/src/agent/memory/access-policy.js.map +0 -1
@@ -0,0 +1,93 @@
1
+ import { join } from "node:path";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ //#region src/local-apps/acceptance.ts
4
+ const MAX_SCENARIOS = 10;
5
+ const MAX_STEPS = 20;
6
+ const MAX_TEXT_LENGTH = 500;
7
+ const TEST_ID_PATTERN = /^[A-Za-z0-9_-]{1,80}$/;
8
+ function requiredText(value, label, maxLength = MAX_TEXT_LENGTH) {
9
+ if (typeof value !== "string" || !value.trim() || value.length > maxLength) throw new Error(`${label} must be a non-empty string up to ${maxLength} characters`);
10
+ return value;
11
+ }
12
+ function boundedText(value, label) {
13
+ if (typeof value !== "string" || value.length > MAX_TEXT_LENGTH) throw new Error(`${label} must be a string up to ${MAX_TEXT_LENGTH} characters`);
14
+ return value;
15
+ }
16
+ function testId(value, label) {
17
+ const id = requiredText(value, label, 80);
18
+ if (!TEST_ID_PATTERN.test(id)) throw new Error(`${label} must use only letters, numbers, dashes, and underscores`);
19
+ return id;
20
+ }
21
+ function parseStep(value, label) {
22
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`);
23
+ const step = value;
24
+ const hasOnly = (...keys) => Object.keys(step).every((key) => keys.includes(key));
25
+ if (step.action === "click" && hasOnly("action", "target")) return {
26
+ action: "click",
27
+ target: testId(step.target, `${label}.target`)
28
+ };
29
+ if (step.action === "fill" && hasOnly("action", "target", "value")) return {
30
+ action: "fill",
31
+ target: testId(step.target, `${label}.target`),
32
+ value: boundedText(step.value, `${label}.value`)
33
+ };
34
+ if (step.assert === "text_visible" && hasOnly("assert", "text")) return {
35
+ assert: "text_visible",
36
+ text: requiredText(step.text, `${label}.text`)
37
+ };
38
+ if (step.assert === "element_exists" && hasOnly("assert", "target")) return {
39
+ assert: "element_exists",
40
+ target: testId(step.target, `${label}.target`)
41
+ };
42
+ if (step.assert === "value_equals" && hasOnly("assert", "target", "value")) return {
43
+ assert: "value_equals",
44
+ target: testId(step.target, `${label}.target`),
45
+ value: boundedText(step.value, `${label}.value`)
46
+ };
47
+ throw new Error(`${label} uses an unsupported action or assertion`);
48
+ }
49
+ function parseLocalAppAcceptanceConfig(value) {
50
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Acceptance config must be an object");
51
+ const config = value;
52
+ if (config.schemaVersion !== 1) throw new Error("Acceptance config schemaVersion must be 1");
53
+ if (!Array.isArray(config.scenarios) || config.scenarios.length > MAX_SCENARIOS) throw new Error(`Acceptance config must contain at most ${MAX_SCENARIOS} scenarios`);
54
+ const ids = /* @__PURE__ */ new Set();
55
+ return {
56
+ schemaVersion: 1,
57
+ scenarios: config.scenarios.map((value, scenarioIndex) => {
58
+ const label = `Acceptance scenario ${scenarioIndex + 1}`;
59
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`);
60
+ const scenario = value;
61
+ const id = testId(scenario.id, `${label}.id`);
62
+ if (ids.has(id)) throw new Error(`Acceptance scenario id is duplicated: ${id}`);
63
+ ids.add(id);
64
+ if (!Array.isArray(scenario.steps) || !scenario.steps.length || scenario.steps.length > MAX_STEPS) throw new Error(`${label} must contain 1 to ${MAX_STEPS} steps`);
65
+ return {
66
+ id,
67
+ name: requiredText(scenario.name, `${label}.name`, 120),
68
+ steps: scenario.steps.map((step, stepIndex) => parseStep(step, `${label} step ${stepIndex + 1}`))
69
+ };
70
+ })
71
+ };
72
+ }
73
+ function readLocalAppAcceptanceConfig(root) {
74
+ const path = join(root, ".xopc", "acceptance.json");
75
+ if (!existsSync(path)) return {
76
+ schemaVersion: 1,
77
+ scenarios: []
78
+ };
79
+ let value;
80
+ try {
81
+ value = JSON.parse(readFileSync(path, "utf8"));
82
+ } catch (error) {
83
+ throw new Error(`Acceptance config is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
84
+ }
85
+ return parseLocalAppAcceptanceConfig(value);
86
+ }
87
+ function encodeLocalAppAcceptanceConfig(config) {
88
+ return Buffer.from(JSON.stringify(config), "utf8").toString("base64url");
89
+ }
90
+ //#endregion
91
+ export { encodeLocalAppAcceptanceConfig, parseLocalAppAcceptanceConfig, readLocalAppAcceptanceConfig };
92
+
93
+ //# sourceMappingURL=acceptance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptance.js","names":[],"sources":["../../../src/local-apps/acceptance.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst MAX_SCENARIOS = 10;\nconst MAX_STEPS = 20;\nconst MAX_TEXT_LENGTH = 500;\nconst TEST_ID_PATTERN = /^[A-Za-z0-9_-]{1,80}$/;\n\nexport type LocalAppAcceptanceStep =\n | { action: 'click'; target: string }\n | { action: 'fill'; target: string; value: string }\n | { assert: 'text_visible'; text: string }\n | { assert: 'element_exists'; target: string }\n | { assert: 'value_equals'; target: string; value: string };\n\nexport interface LocalAppAcceptanceScenario {\n id: string;\n name: string;\n steps: LocalAppAcceptanceStep[];\n}\n\nexport interface LocalAppAcceptanceConfig {\n schemaVersion: 1;\n scenarios: LocalAppAcceptanceScenario[];\n}\n\nfunction requiredText(value: unknown, label: string, maxLength = MAX_TEXT_LENGTH): string {\n if (typeof value !== 'string' || !value.trim() || value.length > maxLength) {\n throw new Error(`${label} must be a non-empty string up to ${maxLength} characters`);\n }\n return value;\n}\n\nfunction boundedText(value: unknown, label: string): string {\n if (typeof value !== 'string' || value.length > MAX_TEXT_LENGTH) {\n throw new Error(`${label} must be a string up to ${MAX_TEXT_LENGTH} characters`);\n }\n return value;\n}\n\nfunction testId(value: unknown, label: string): string {\n const id = requiredText(value, label, 80);\n if (!TEST_ID_PATTERN.test(id)) {\n throw new Error(`${label} must use only letters, numbers, dashes, and underscores`);\n }\n return id;\n}\n\nfunction parseStep(value: unknown, label: string): LocalAppAcceptanceStep {\n if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${label} must be an object`);\n const step = value as Record<string, unknown>;\n const hasOnly = (...keys: string[]) => Object.keys(step).every((key) => keys.includes(key));\n if (step.action === 'click' && hasOnly('action', 'target')) {\n return { action: 'click', target: testId(step.target, `${label}.target`) };\n }\n if (step.action === 'fill' && hasOnly('action', 'target', 'value')) {\n return { action: 'fill', target: testId(step.target, `${label}.target`), value: boundedText(step.value, `${label}.value`) };\n }\n if (step.assert === 'text_visible' && hasOnly('assert', 'text')) {\n return { assert: 'text_visible', text: requiredText(step.text, `${label}.text`) };\n }\n if (step.assert === 'element_exists' && hasOnly('assert', 'target')) {\n return { assert: 'element_exists', target: testId(step.target, `${label}.target`) };\n }\n if (step.assert === 'value_equals' && hasOnly('assert', 'target', 'value')) {\n return { assert: 'value_equals', target: testId(step.target, `${label}.target`), value: boundedText(step.value, `${label}.value`) };\n }\n throw new Error(`${label} uses an unsupported action or assertion`);\n}\n\nexport function parseLocalAppAcceptanceConfig(value: unknown): LocalAppAcceptanceConfig {\n if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Acceptance config must be an object');\n const config = value as Record<string, unknown>;\n if (config.schemaVersion !== 1) throw new Error('Acceptance config schemaVersion must be 1');\n if (!Array.isArray(config.scenarios) || config.scenarios.length > MAX_SCENARIOS) {\n throw new Error(`Acceptance config must contain at most ${MAX_SCENARIOS} scenarios`);\n }\n const ids = new Set<string>();\n const scenarios = config.scenarios.map((value, scenarioIndex) => {\n const label = `Acceptance scenario ${scenarioIndex + 1}`;\n if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${label} must be an object`);\n const scenario = value as Record<string, unknown>;\n const id = testId(scenario.id, `${label}.id`);\n if (ids.has(id)) throw new Error(`Acceptance scenario id is duplicated: ${id}`);\n ids.add(id);\n if (!Array.isArray(scenario.steps) || !scenario.steps.length || scenario.steps.length > MAX_STEPS) {\n throw new Error(`${label} must contain 1 to ${MAX_STEPS} steps`);\n }\n return {\n id,\n name: requiredText(scenario.name, `${label}.name`, 120),\n steps: scenario.steps.map((step, stepIndex) => parseStep(step, `${label} step ${stepIndex + 1}`)),\n };\n });\n return { schemaVersion: 1, scenarios };\n}\n\nexport function readLocalAppAcceptanceConfig(root: string): LocalAppAcceptanceConfig {\n const path = join(root, '.xopc', 'acceptance.json');\n if (!existsSync(path)) return { schemaVersion: 1, scenarios: [] };\n let value: unknown;\n try {\n value = JSON.parse(readFileSync(path, 'utf8'));\n } catch (error) {\n throw new Error(`Acceptance config is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);\n }\n return parseLocalAppAcceptanceConfig(value);\n}\n\nexport function encodeLocalAppAcceptanceConfig(config: LocalAppAcceptanceConfig): string {\n return Buffer.from(JSON.stringify(config), 'utf8').toString('base64url');\n}\n"],"mappings":";;;AAGA,MAAM,gBAAgB;AACtB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,kBAAkB;AAoBxB,SAAS,aAAa,OAAgB,OAAe,YAAY,iBAAyB;AACxF,KAAI,OAAO,UAAU,YAAY,CAAC,MAAM,MAAM,IAAI,MAAM,SAAS,UAC/D,OAAM,IAAI,MAAM,GAAG,MAAM,oCAAoC,UAAU,aAAa;AAEtF,QAAO;;AAGT,SAAS,YAAY,OAAgB,OAAuB;AAC1D,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,gBAC9C,OAAM,IAAI,MAAM,GAAG,MAAM,0BAA0B,gBAAgB,aAAa;AAElF,QAAO;;AAGT,SAAS,OAAO,OAAgB,OAAuB;CACrD,MAAM,KAAK,aAAa,OAAO,OAAO,GAAG;AACzC,KAAI,CAAC,gBAAgB,KAAK,GAAG,CAC3B,OAAM,IAAI,MAAM,GAAG,MAAM,0DAA0D;AAErF,QAAO;;AAGT,SAAS,UAAU,OAAgB,OAAuC;AACxE,KAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,oBAAoB;CAC9G,MAAM,OAAO;CACb,MAAM,WAAW,GAAG,SAAmB,OAAO,KAAK,KAAK,CAAC,OAAO,QAAQ,KAAK,SAAS,IAAI,CAAC;AAC3F,KAAI,KAAK,WAAW,WAAW,QAAQ,UAAU,SAAS,CACxD,QAAO;EAAE,QAAQ;EAAS,QAAQ,OAAO,KAAK,QAAQ,GAAG,MAAM,SAAS;EAAE;AAE5E,KAAI,KAAK,WAAW,UAAU,QAAQ,UAAU,UAAU,QAAQ,CAChE,QAAO;EAAE,QAAQ;EAAQ,QAAQ,OAAO,KAAK,QAAQ,GAAG,MAAM,SAAS;EAAE,OAAO,YAAY,KAAK,OAAO,GAAG,MAAM,QAAQ;EAAE;AAE7H,KAAI,KAAK,WAAW,kBAAkB,QAAQ,UAAU,OAAO,CAC7D,QAAO;EAAE,QAAQ;EAAgB,MAAM,aAAa,KAAK,MAAM,GAAG,MAAM,OAAO;EAAE;AAEnF,KAAI,KAAK,WAAW,oBAAoB,QAAQ,UAAU,SAAS,CACjE,QAAO;EAAE,QAAQ;EAAkB,QAAQ,OAAO,KAAK,QAAQ,GAAG,MAAM,SAAS;EAAE;AAErF,KAAI,KAAK,WAAW,kBAAkB,QAAQ,UAAU,UAAU,QAAQ,CACxE,QAAO;EAAE,QAAQ;EAAgB,QAAQ,OAAO,KAAK,QAAQ,GAAG,MAAM,SAAS;EAAE,OAAO,YAAY,KAAK,OAAO,GAAG,MAAM,QAAQ;EAAE;AAErI,OAAM,IAAI,MAAM,GAAG,MAAM,0CAA0C;;AAGrE,SAAgB,8BAA8B,OAA0C;AACtF,KAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CAAE,OAAM,IAAI,MAAM,sCAAsC;CACvH,MAAM,SAAS;AACf,KAAI,OAAO,kBAAkB,EAAG,OAAM,IAAI,MAAM,4CAA4C;AAC5F,KAAI,CAAC,MAAM,QAAQ,OAAO,UAAU,IAAI,OAAO,UAAU,SAAS,cAChE,OAAM,IAAI,MAAM,0CAA0C,cAAc,YAAY;CAEtF,MAAM,sBAAM,IAAI,KAAa;AAiB7B,QAAO;EAAE,eAAe;EAAG,WAhBT,OAAO,UAAU,KAAK,OAAO,kBAAkB;GAC/D,MAAM,QAAQ,uBAAuB,gBAAgB;AACrD,OAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,oBAAoB;GAC9G,MAAM,WAAW;GACjB,MAAM,KAAK,OAAO,SAAS,IAAI,GAAG,MAAM,KAAK;AAC7C,OAAI,IAAI,IAAI,GAAG,CAAE,OAAM,IAAI,MAAM,yCAAyC,KAAK;AAC/E,OAAI,IAAI,GAAG;AACX,OAAI,CAAC,MAAM,QAAQ,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,UAAU,SAAS,MAAM,SAAS,UACtF,OAAM,IAAI,MAAM,GAAG,MAAM,qBAAqB,UAAU,QAAQ;AAElE,UAAO;IACL;IACA,MAAM,aAAa,SAAS,MAAM,GAAG,MAAM,QAAQ,IAAI;IACvD,OAAO,SAAS,MAAM,KAAK,MAAM,cAAc,UAAU,MAAM,GAAG,MAAM,QAAQ,YAAY,IAAI,CAAC;IAClG;IAEiC;EAAE;;AAGxC,SAAgB,6BAA6B,MAAwC;CACnF,MAAM,OAAO,KAAK,MAAM,SAAS,kBAAkB;AACnD,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO;EAAE,eAAe;EAAG,WAAW,EAAE;EAAE;CACjE,IAAI;AACJ,KAAI;AACF,UAAQ,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;UACvC,OAAO;AACd,QAAM,IAAI,MAAM,wCAAwC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAAG;;AAEnH,QAAO,8BAA8B,MAAM;;AAG7C,SAAgB,+BAA+B,QAA0C;AACvF,QAAO,OAAO,KAAK,KAAK,UAAU,OAAO,EAAE,OAAO,CAAC,SAAS,YAAY"}
@@ -0,0 +1,5 @@
1
+ export { LocalAppService, type LocalAppServiceOptions } from './service.js';
2
+ export { LocalAppStore } from './store.js';
3
+ export { encodeLocalAppAcceptanceConfig, parseLocalAppAcceptanceConfig, readLocalAppAcceptanceConfig, } from './acceptance.js';
4
+ export type { LocalAppAcceptanceConfig, LocalAppAcceptanceScenario, LocalAppAcceptanceStep, } from './acceptance.js';
5
+ export type { CreateLocalAppInput, LocalApp, LocalAppAcceptanceCheck, LocalAppAcceptanceScenarioSummary, LocalAppAcceptanceRun, LocalAppDetail, LocalAppInstallationState, LocalAppChangedFile, LocalAppValidationIssue, LocalAppValidationResult, LocalAppPreviewTarget, LocalAppRelease, LocalAppReleaseHealth, LocalAppStatus, LocalAppUiGrant, RecordLocalAppAcceptanceInput, } from './types.js';
@@ -0,0 +1,4 @@
1
+ import { LocalAppStore } from "./store.js";
2
+ import { encodeLocalAppAcceptanceConfig, parseLocalAppAcceptanceConfig, readLocalAppAcceptanceConfig } from "./acceptance.js";
3
+ import { LocalAppService } from "./service.js";
4
+ export { LocalAppService, LocalAppStore, encodeLocalAppAcceptanceConfig, parseLocalAppAcceptanceConfig, readLocalAppAcceptanceConfig };
@@ -0,0 +1,4 @@
1
+ import type { LocalAppAcceptanceConfig } from './acceptance.js';
2
+ export declare const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = "(() => {\n let hostPort = null;\n const pending = [];\n const send = (type, detail) => {\n const message = { source: 'xopc-local-app-preview', version: 1, type, detail };\n if (hostPort) hostPort.postMessage(message);\n else pending.push(message);\n };\n const connect = (event) => {\n const message = event.data;\n if (event.source !== parent || message?.source !== 'xopc-local-app-host' || message?.version !== 1 || message?.type !== 'connect' || !event.ports[0]) return;\n event.stopImmediatePropagation();\n removeEventListener('message', connect, true);\n hostPort = event.ports[0];\n hostPort.start();\n for (const queued of pending.splice(0)) hostPort.postMessage(queued);\n };\n addEventListener('message', connect, true);\n const text = (value) => String(value ?? '').slice(0, 500);\n let runtimeFailure = '';\n const visible = (element) => {\n const style = getComputedStyle(element);\n const rect = element.getBoundingClientRect();\n return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0 && rect.width > 0 && rect.height > 0;\n };\n const accessibleName = (element) => {\n const labelledBy = text(element.getAttribute('aria-labelledby'));\n const labelledText = labelledBy.split(/\\s+/).map((id) => document.getElementById(id)?.textContent).filter(Boolean).join(' ');\n return text(element.getAttribute('aria-label') || labelledText || element.labels?.[0]?.textContent || element.textContent || element.getAttribute('alt') || element.getAttribute('title') || element.getAttribute('placeholder') || element.getAttribute('name')).trim();\n };\n const target = (id) => Array.from(document.querySelectorAll('[data-xopc-test-id]'))\n .find((element) => element.getAttribute('data-xopc-test-id') === id);\n const pause = () => new Promise((resolve) => setTimeout(resolve, 50));\n const readCriteria = () => {\n const encoded = document.querySelector('meta[name=\"xopc-local-app-acceptance\"]')?.content;\n if (!encoded) return { schemaVersion: 1, scenarios: [] };\n const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const bytes = Uint8Array.from(atob(padded), (char) => char.charCodeAt(0));\n return JSON.parse(new TextDecoder().decode(bytes));\n };\n addEventListener('error', (event) => {\n runtimeFailure = text(event.message || 'A preview resource failed to load');\n send('error', {\n kind: 'script_error',\n message: runtimeFailure,\n filename: text(event.filename),\n line: Number(event.lineno) || undefined,\n column: Number(event.colno) || undefined,\n });\n }, true);\n addEventListener('unhandledrejection', (event) => {\n runtimeFailure = text(event.reason instanceof Error ? event.reason.message : event.reason);\n send('error', { kind: 'unhandled_rejection', message: runtimeFailure });\n });\n const runAcceptance = () => {\n const checks = [];\n const bodyReady = Boolean(document.body);\n checks.push({ id: 'document', status: bodyReady ? 'passed' : 'failed', message: bodyReady ? 'Preview document loaded' : 'Preview document has no body' });\n const contentNodes = bodyReady ? Array.from(document.body.querySelectorAll('*')).slice(0, 300) : [];\n const hasVisibleContent = contentNodes.some((element) => visible(element) && (text(element.textContent).trim() || /^(IMG|SVG|CANVAS|VIDEO)$/.test(element.tagName)));\n checks.push({ id: 'content', status: hasVisibleContent ? 'passed' : 'failed', message: hasVisibleContent ? 'Visible content rendered' : 'No visible content was rendered' });\n const controls = bodyReady ? Array.from(document.body.querySelectorAll('a[href], button, input, select, textarea, [role=\"button\"], [tabindex]')).filter((element) => visible(element) && !element.disabled && element.getAttribute('aria-disabled') !== 'true') : [];\n const unnamed = controls.filter((element) => !accessibleName(element));\n let interactionStatus = controls.length ? 'passed' : 'skipped';\n let interactionMessage = controls.length ? controls.length + ' interactive control(s) are discoverable' : 'No interactive controls to exercise';\n if (unnamed.length) {\n interactionStatus = 'failed';\n interactionMessage = unnamed.length + ' interactive control(s) need an accessible name';\n } else if (controls.length) {\n const previous = document.activeElement;\n controls[0].focus({ preventScroll: true });\n if (document.activeElement !== controls[0]) {\n interactionStatus = 'failed';\n interactionMessage = 'The first interactive control could not receive focus';\n }\n if (previous && typeof previous.focus === 'function') previous.focus({ preventScroll: true });\n else controls[0].blur();\n }\n checks.push({ id: 'interaction', status: interactionStatus, message: interactionMessage });\n send('acceptance', {\n status: checks.some((check) => check.status === 'failed') ? 'failed' : 'passed',\n checks,\n interactiveCount: controls.length,\n });\n };\n const runCriteria = async () => {\n const params = new URLSearchParams(location.search);\n if (params.get('xopcAcceptance') !== '1') return;\n let config;\n try {\n config = readCriteria();\n } catch (error) {\n send('criteria', { status: 'failed', scenarioCount: 0, scenarios: [{ id: 'config', name: 'Acceptance config', status: 'failed', message: text(error instanceof Error ? error.message : error) }] });\n return;\n }\n const requestedScenario = params.get('xopcScenario');\n const selectedScenarios = requestedScenario\n ? (config.scenarios || []).filter((scenario) => scenario.id === requestedScenario)\n : config.scenarios || [];\n if (requestedScenario && !selectedScenarios.length) {\n send('criteria', { status: 'failed', scenarioCount: 1, scenarios: [{ id: requestedScenario, name: requestedScenario, status: 'failed', message: 'Requested scenario was not found' }] });\n return;\n }\n const scenarios = [];\n for (const scenario of selectedScenarios) {\n let failure = '';\n for (let index = 0; index < scenario.steps.length; index += 1) {\n const step = scenario.steps[index];\n try {\n if (step.action === 'click') {\n const element = target(step.target);\n if (!(element instanceof HTMLElement)) throw new Error('Target \"' + step.target + '\" was not found');\n if ('disabled' in element && element.disabled || element.getAttribute('aria-disabled') === 'true') throw new Error('Target \"' + step.target + '\" is disabled');\n element.click();\n await pause();\n } else if (step.action === 'fill') {\n const element = target(step.target);\n if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) throw new Error('Target \"' + step.target + '\" cannot be filled');\n const prototype = element instanceof HTMLInputElement ? HTMLInputElement.prototype : element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLSelectElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;\n if (setter) setter.call(element, step.value);\n else element.value = step.value;\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n await pause();\n } else if (step.assert === 'text_visible') {\n const found = Array.from(document.body.querySelectorAll('*')).some((element) => visible(element) && text(element.textContent).includes(step.text));\n if (!found) throw new Error('Visible text was not found: \"' + step.text + '\"');\n } else if (step.assert === 'element_exists') {\n if (!target(step.target)) throw new Error('Target \"' + step.target + '\" was not found');\n } else if (step.assert === 'value_equals') {\n const element = target(step.target);\n if (!element || !('value' in element) || element.value !== step.value) throw new Error('Target \"' + step.target + '\" does not have the expected value');\n }\n if (runtimeFailure) throw new Error(runtimeFailure);\n } catch (error) {\n failure = 'Step ' + (index + 1) + ': ' + text(error instanceof Error ? error.message : error);\n break;\n }\n }\n scenarios.push({ id: scenario.id, name: scenario.name, status: failure ? 'failed' : 'passed', message: failure || 'Scenario passed' });\n }\n send('criteria', {\n status: scenarios.some((scenario) => scenario.status === 'failed') ? 'failed' : 'passed',\n scenarioCount: scenarios.length,\n scenarios,\n });\n };\n const loaded = () => {\n send('ready', { readyState: document.readyState });\n const run = () => { runAcceptance(); void runCriteria(); };\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => requestAnimationFrame(run));\n else setTimeout(run, 0);\n };\n if (document.readyState === 'complete') queueMicrotask(loaded);\n else addEventListener('load', loaded, { once: true });\n})();";
3
+ export declare const LOCAL_APP_RUNTIME_BRIDGE_HASH: string;
4
+ export declare function injectLocalAppRuntimeBridge(html: string, acceptance?: LocalAppAcceptanceConfig): string;
@@ -0,0 +1,171 @@
1
+ import { encodeLocalAppAcceptanceConfig } from "./acceptance.js";
2
+ import { createHash } from "node:crypto";
3
+ //#region src/local-apps/preview-runtime-bridge.ts
4
+ const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = `(() => {
5
+ let hostPort = null;
6
+ const pending = [];
7
+ const send = (type, detail) => {
8
+ const message = { source: 'xopc-local-app-preview', version: 1, type, detail };
9
+ if (hostPort) hostPort.postMessage(message);
10
+ else pending.push(message);
11
+ };
12
+ const connect = (event) => {
13
+ const message = event.data;
14
+ if (event.source !== parent || message?.source !== 'xopc-local-app-host' || message?.version !== 1 || message?.type !== 'connect' || !event.ports[0]) return;
15
+ event.stopImmediatePropagation();
16
+ removeEventListener('message', connect, true);
17
+ hostPort = event.ports[0];
18
+ hostPort.start();
19
+ for (const queued of pending.splice(0)) hostPort.postMessage(queued);
20
+ };
21
+ addEventListener('message', connect, true);
22
+ const text = (value) => String(value ?? '').slice(0, 500);
23
+ let runtimeFailure = '';
24
+ const visible = (element) => {
25
+ const style = getComputedStyle(element);
26
+ const rect = element.getBoundingClientRect();
27
+ return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0 && rect.width > 0 && rect.height > 0;
28
+ };
29
+ const accessibleName = (element) => {
30
+ const labelledBy = text(element.getAttribute('aria-labelledby'));
31
+ const labelledText = labelledBy.split(/\\s+/).map((id) => document.getElementById(id)?.textContent).filter(Boolean).join(' ');
32
+ return text(element.getAttribute('aria-label') || labelledText || element.labels?.[0]?.textContent || element.textContent || element.getAttribute('alt') || element.getAttribute('title') || element.getAttribute('placeholder') || element.getAttribute('name')).trim();
33
+ };
34
+ const target = (id) => Array.from(document.querySelectorAll('[data-xopc-test-id]'))
35
+ .find((element) => element.getAttribute('data-xopc-test-id') === id);
36
+ const pause = () => new Promise((resolve) => setTimeout(resolve, 50));
37
+ const readCriteria = () => {
38
+ const encoded = document.querySelector('meta[name="xopc-local-app-acceptance"]')?.content;
39
+ if (!encoded) return { schemaVersion: 1, scenarios: [] };
40
+ const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
41
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
42
+ const bytes = Uint8Array.from(atob(padded), (char) => char.charCodeAt(0));
43
+ return JSON.parse(new TextDecoder().decode(bytes));
44
+ };
45
+ addEventListener('error', (event) => {
46
+ runtimeFailure = text(event.message || 'A preview resource failed to load');
47
+ send('error', {
48
+ kind: 'script_error',
49
+ message: runtimeFailure,
50
+ filename: text(event.filename),
51
+ line: Number(event.lineno) || undefined,
52
+ column: Number(event.colno) || undefined,
53
+ });
54
+ }, true);
55
+ addEventListener('unhandledrejection', (event) => {
56
+ runtimeFailure = text(event.reason instanceof Error ? event.reason.message : event.reason);
57
+ send('error', { kind: 'unhandled_rejection', message: runtimeFailure });
58
+ });
59
+ const runAcceptance = () => {
60
+ const checks = [];
61
+ const bodyReady = Boolean(document.body);
62
+ checks.push({ id: 'document', status: bodyReady ? 'passed' : 'failed', message: bodyReady ? 'Preview document loaded' : 'Preview document has no body' });
63
+ const contentNodes = bodyReady ? Array.from(document.body.querySelectorAll('*')).slice(0, 300) : [];
64
+ const hasVisibleContent = contentNodes.some((element) => visible(element) && (text(element.textContent).trim() || /^(IMG|SVG|CANVAS|VIDEO)$/.test(element.tagName)));
65
+ checks.push({ id: 'content', status: hasVisibleContent ? 'passed' : 'failed', message: hasVisibleContent ? 'Visible content rendered' : 'No visible content was rendered' });
66
+ const controls = bodyReady ? Array.from(document.body.querySelectorAll('a[href], button, input, select, textarea, [role="button"], [tabindex]')).filter((element) => visible(element) && !element.disabled && element.getAttribute('aria-disabled') !== 'true') : [];
67
+ const unnamed = controls.filter((element) => !accessibleName(element));
68
+ let interactionStatus = controls.length ? 'passed' : 'skipped';
69
+ let interactionMessage = controls.length ? controls.length + ' interactive control(s) are discoverable' : 'No interactive controls to exercise';
70
+ if (unnamed.length) {
71
+ interactionStatus = 'failed';
72
+ interactionMessage = unnamed.length + ' interactive control(s) need an accessible name';
73
+ } else if (controls.length) {
74
+ const previous = document.activeElement;
75
+ controls[0].focus({ preventScroll: true });
76
+ if (document.activeElement !== controls[0]) {
77
+ interactionStatus = 'failed';
78
+ interactionMessage = 'The first interactive control could not receive focus';
79
+ }
80
+ if (previous && typeof previous.focus === 'function') previous.focus({ preventScroll: true });
81
+ else controls[0].blur();
82
+ }
83
+ checks.push({ id: 'interaction', status: interactionStatus, message: interactionMessage });
84
+ send('acceptance', {
85
+ status: checks.some((check) => check.status === 'failed') ? 'failed' : 'passed',
86
+ checks,
87
+ interactiveCount: controls.length,
88
+ });
89
+ };
90
+ const runCriteria = async () => {
91
+ const params = new URLSearchParams(location.search);
92
+ if (params.get('xopcAcceptance') !== '1') return;
93
+ let config;
94
+ try {
95
+ config = readCriteria();
96
+ } catch (error) {
97
+ send('criteria', { status: 'failed', scenarioCount: 0, scenarios: [{ id: 'config', name: 'Acceptance config', status: 'failed', message: text(error instanceof Error ? error.message : error) }] });
98
+ return;
99
+ }
100
+ const requestedScenario = params.get('xopcScenario');
101
+ const selectedScenarios = requestedScenario
102
+ ? (config.scenarios || []).filter((scenario) => scenario.id === requestedScenario)
103
+ : config.scenarios || [];
104
+ if (requestedScenario && !selectedScenarios.length) {
105
+ send('criteria', { status: 'failed', scenarioCount: 1, scenarios: [{ id: requestedScenario, name: requestedScenario, status: 'failed', message: 'Requested scenario was not found' }] });
106
+ return;
107
+ }
108
+ const scenarios = [];
109
+ for (const scenario of selectedScenarios) {
110
+ let failure = '';
111
+ for (let index = 0; index < scenario.steps.length; index += 1) {
112
+ const step = scenario.steps[index];
113
+ try {
114
+ if (step.action === 'click') {
115
+ const element = target(step.target);
116
+ if (!(element instanceof HTMLElement)) throw new Error('Target "' + step.target + '" was not found');
117
+ if ('disabled' in element && element.disabled || element.getAttribute('aria-disabled') === 'true') throw new Error('Target "' + step.target + '" is disabled');
118
+ element.click();
119
+ await pause();
120
+ } else if (step.action === 'fill') {
121
+ const element = target(step.target);
122
+ if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) throw new Error('Target "' + step.target + '" cannot be filled');
123
+ const prototype = element instanceof HTMLInputElement ? HTMLInputElement.prototype : element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLSelectElement.prototype;
124
+ const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;
125
+ if (setter) setter.call(element, step.value);
126
+ else element.value = step.value;
127
+ element.dispatchEvent(new Event('input', { bubbles: true }));
128
+ element.dispatchEvent(new Event('change', { bubbles: true }));
129
+ await pause();
130
+ } else if (step.assert === 'text_visible') {
131
+ const found = Array.from(document.body.querySelectorAll('*')).some((element) => visible(element) && text(element.textContent).includes(step.text));
132
+ if (!found) throw new Error('Visible text was not found: "' + step.text + '"');
133
+ } else if (step.assert === 'element_exists') {
134
+ if (!target(step.target)) throw new Error('Target "' + step.target + '" was not found');
135
+ } else if (step.assert === 'value_equals') {
136
+ const element = target(step.target);
137
+ if (!element || !('value' in element) || element.value !== step.value) throw new Error('Target "' + step.target + '" does not have the expected value');
138
+ }
139
+ if (runtimeFailure) throw new Error(runtimeFailure);
140
+ } catch (error) {
141
+ failure = 'Step ' + (index + 1) + ': ' + text(error instanceof Error ? error.message : error);
142
+ break;
143
+ }
144
+ }
145
+ scenarios.push({ id: scenario.id, name: scenario.name, status: failure ? 'failed' : 'passed', message: failure || 'Scenario passed' });
146
+ }
147
+ send('criteria', {
148
+ status: scenarios.some((scenario) => scenario.status === 'failed') ? 'failed' : 'passed',
149
+ scenarioCount: scenarios.length,
150
+ scenarios,
151
+ });
152
+ };
153
+ const loaded = () => {
154
+ send('ready', { readyState: document.readyState });
155
+ const run = () => { runAcceptance(); void runCriteria(); };
156
+ if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => requestAnimationFrame(run));
157
+ else setTimeout(run, 0);
158
+ };
159
+ if (document.readyState === 'complete') queueMicrotask(loaded);
160
+ else addEventListener('load', loaded, { once: true });
161
+ })();`;
162
+ const LOCAL_APP_RUNTIME_BRIDGE_HASH = createHash("sha256").update(LOCAL_APP_RUNTIME_BRIDGE_SCRIPT).digest("base64");
163
+ function injectLocalAppRuntimeBridge(html, acceptance) {
164
+ const additions = [acceptance ? `<meta name="xopc-local-app-acceptance" content="${encodeLocalAppAcceptanceConfig(acceptance)}">` : "", `<script data-xopc-runtime-bridge>${LOCAL_APP_RUNTIME_BRIDGE_SCRIPT}<\/script>`].filter(Boolean).join("\n");
165
+ if (/<head(?:\s[^>]*)?>/i.test(html)) return html.replace(/<head(?:\s[^>]*)?>/i, (head) => `${head}\n${additions}`);
166
+ return `${additions}\n${html}`;
167
+ }
168
+ //#endregion
169
+ export { LOCAL_APP_RUNTIME_BRIDGE_HASH, LOCAL_APP_RUNTIME_BRIDGE_SCRIPT, injectLocalAppRuntimeBridge };
170
+
171
+ //# sourceMappingURL=preview-runtime-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-runtime-bridge.js","names":[],"sources":["../../../src/local-apps/preview-runtime-bridge.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport type { LocalAppAcceptanceConfig } from './acceptance.js';\nimport { encodeLocalAppAcceptanceConfig } from './acceptance.js';\n\nexport const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = `(() => {\n let hostPort = null;\n const pending = [];\n const send = (type, detail) => {\n const message = { source: 'xopc-local-app-preview', version: 1, type, detail };\n if (hostPort) hostPort.postMessage(message);\n else pending.push(message);\n };\n const connect = (event) => {\n const message = event.data;\n if (event.source !== parent || message?.source !== 'xopc-local-app-host' || message?.version !== 1 || message?.type !== 'connect' || !event.ports[0]) return;\n event.stopImmediatePropagation();\n removeEventListener('message', connect, true);\n hostPort = event.ports[0];\n hostPort.start();\n for (const queued of pending.splice(0)) hostPort.postMessage(queued);\n };\n addEventListener('message', connect, true);\n const text = (value) => String(value ?? '').slice(0, 500);\n let runtimeFailure = '';\n const visible = (element) => {\n const style = getComputedStyle(element);\n const rect = element.getBoundingClientRect();\n return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0 && rect.width > 0 && rect.height > 0;\n };\n const accessibleName = (element) => {\n const labelledBy = text(element.getAttribute('aria-labelledby'));\n const labelledText = labelledBy.split(/\\\\s+/).map((id) => document.getElementById(id)?.textContent).filter(Boolean).join(' ');\n return text(element.getAttribute('aria-label') || labelledText || element.labels?.[0]?.textContent || element.textContent || element.getAttribute('alt') || element.getAttribute('title') || element.getAttribute('placeholder') || element.getAttribute('name')).trim();\n };\n const target = (id) => Array.from(document.querySelectorAll('[data-xopc-test-id]'))\n .find((element) => element.getAttribute('data-xopc-test-id') === id);\n const pause = () => new Promise((resolve) => setTimeout(resolve, 50));\n const readCriteria = () => {\n const encoded = document.querySelector('meta[name=\"xopc-local-app-acceptance\"]')?.content;\n if (!encoded) return { schemaVersion: 1, scenarios: [] };\n const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const bytes = Uint8Array.from(atob(padded), (char) => char.charCodeAt(0));\n return JSON.parse(new TextDecoder().decode(bytes));\n };\n addEventListener('error', (event) => {\n runtimeFailure = text(event.message || 'A preview resource failed to load');\n send('error', {\n kind: 'script_error',\n message: runtimeFailure,\n filename: text(event.filename),\n line: Number(event.lineno) || undefined,\n column: Number(event.colno) || undefined,\n });\n }, true);\n addEventListener('unhandledrejection', (event) => {\n runtimeFailure = text(event.reason instanceof Error ? event.reason.message : event.reason);\n send('error', { kind: 'unhandled_rejection', message: runtimeFailure });\n });\n const runAcceptance = () => {\n const checks = [];\n const bodyReady = Boolean(document.body);\n checks.push({ id: 'document', status: bodyReady ? 'passed' : 'failed', message: bodyReady ? 'Preview document loaded' : 'Preview document has no body' });\n const contentNodes = bodyReady ? Array.from(document.body.querySelectorAll('*')).slice(0, 300) : [];\n const hasVisibleContent = contentNodes.some((element) => visible(element) && (text(element.textContent).trim() || /^(IMG|SVG|CANVAS|VIDEO)$/.test(element.tagName)));\n checks.push({ id: 'content', status: hasVisibleContent ? 'passed' : 'failed', message: hasVisibleContent ? 'Visible content rendered' : 'No visible content was rendered' });\n const controls = bodyReady ? Array.from(document.body.querySelectorAll('a[href], button, input, select, textarea, [role=\"button\"], [tabindex]')).filter((element) => visible(element) && !element.disabled && element.getAttribute('aria-disabled') !== 'true') : [];\n const unnamed = controls.filter((element) => !accessibleName(element));\n let interactionStatus = controls.length ? 'passed' : 'skipped';\n let interactionMessage = controls.length ? controls.length + ' interactive control(s) are discoverable' : 'No interactive controls to exercise';\n if (unnamed.length) {\n interactionStatus = 'failed';\n interactionMessage = unnamed.length + ' interactive control(s) need an accessible name';\n } else if (controls.length) {\n const previous = document.activeElement;\n controls[0].focus({ preventScroll: true });\n if (document.activeElement !== controls[0]) {\n interactionStatus = 'failed';\n interactionMessage = 'The first interactive control could not receive focus';\n }\n if (previous && typeof previous.focus === 'function') previous.focus({ preventScroll: true });\n else controls[0].blur();\n }\n checks.push({ id: 'interaction', status: interactionStatus, message: interactionMessage });\n send('acceptance', {\n status: checks.some((check) => check.status === 'failed') ? 'failed' : 'passed',\n checks,\n interactiveCount: controls.length,\n });\n };\n const runCriteria = async () => {\n const params = new URLSearchParams(location.search);\n if (params.get('xopcAcceptance') !== '1') return;\n let config;\n try {\n config = readCriteria();\n } catch (error) {\n send('criteria', { status: 'failed', scenarioCount: 0, scenarios: [{ id: 'config', name: 'Acceptance config', status: 'failed', message: text(error instanceof Error ? error.message : error) }] });\n return;\n }\n const requestedScenario = params.get('xopcScenario');\n const selectedScenarios = requestedScenario\n ? (config.scenarios || []).filter((scenario) => scenario.id === requestedScenario)\n : config.scenarios || [];\n if (requestedScenario && !selectedScenarios.length) {\n send('criteria', { status: 'failed', scenarioCount: 1, scenarios: [{ id: requestedScenario, name: requestedScenario, status: 'failed', message: 'Requested scenario was not found' }] });\n return;\n }\n const scenarios = [];\n for (const scenario of selectedScenarios) {\n let failure = '';\n for (let index = 0; index < scenario.steps.length; index += 1) {\n const step = scenario.steps[index];\n try {\n if (step.action === 'click') {\n const element = target(step.target);\n if (!(element instanceof HTMLElement)) throw new Error('Target \"' + step.target + '\" was not found');\n if ('disabled' in element && element.disabled || element.getAttribute('aria-disabled') === 'true') throw new Error('Target \"' + step.target + '\" is disabled');\n element.click();\n await pause();\n } else if (step.action === 'fill') {\n const element = target(step.target);\n if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) throw new Error('Target \"' + step.target + '\" cannot be filled');\n const prototype = element instanceof HTMLInputElement ? HTMLInputElement.prototype : element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLSelectElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;\n if (setter) setter.call(element, step.value);\n else element.value = step.value;\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n await pause();\n } else if (step.assert === 'text_visible') {\n const found = Array.from(document.body.querySelectorAll('*')).some((element) => visible(element) && text(element.textContent).includes(step.text));\n if (!found) throw new Error('Visible text was not found: \"' + step.text + '\"');\n } else if (step.assert === 'element_exists') {\n if (!target(step.target)) throw new Error('Target \"' + step.target + '\" was not found');\n } else if (step.assert === 'value_equals') {\n const element = target(step.target);\n if (!element || !('value' in element) || element.value !== step.value) throw new Error('Target \"' + step.target + '\" does not have the expected value');\n }\n if (runtimeFailure) throw new Error(runtimeFailure);\n } catch (error) {\n failure = 'Step ' + (index + 1) + ': ' + text(error instanceof Error ? error.message : error);\n break;\n }\n }\n scenarios.push({ id: scenario.id, name: scenario.name, status: failure ? 'failed' : 'passed', message: failure || 'Scenario passed' });\n }\n send('criteria', {\n status: scenarios.some((scenario) => scenario.status === 'failed') ? 'failed' : 'passed',\n scenarioCount: scenarios.length,\n scenarios,\n });\n };\n const loaded = () => {\n send('ready', { readyState: document.readyState });\n const run = () => { runAcceptance(); void runCriteria(); };\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => requestAnimationFrame(run));\n else setTimeout(run, 0);\n };\n if (document.readyState === 'complete') queueMicrotask(loaded);\n else addEventListener('load', loaded, { once: true });\n})();`;\n\nexport const LOCAL_APP_RUNTIME_BRIDGE_HASH = createHash('sha256')\n .update(LOCAL_APP_RUNTIME_BRIDGE_SCRIPT)\n .digest('base64');\n\nexport function injectLocalAppRuntimeBridge(\n html: string,\n acceptance?: LocalAppAcceptanceConfig,\n): string {\n const meta = acceptance\n ? `<meta name=\"xopc-local-app-acceptance\" content=\"${encodeLocalAppAcceptanceConfig(acceptance)}\">`\n : '';\n const script = `<script data-xopc-runtime-bridge>${LOCAL_APP_RUNTIME_BRIDGE_SCRIPT}</script>`;\n const additions = [meta, script].filter(Boolean).join('\\n');\n if (/<head(?:\\s[^>]*)?>/i.test(html)) {\n return html.replace(/<head(?:\\s[^>]*)?>/i, (head) => `${head}\\n${additions}`);\n }\n return `${additions}\\n${html}`;\n}\n"],"mappings":";;;AAKA,MAAa,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+J/C,MAAa,gCAAgC,WAAW,SAAS,CAC9D,OAAO,gCAAgC,CACvC,OAAO,SAAS;AAEnB,SAAgB,4BACd,MACA,YACQ;CAKR,MAAM,YAAY,CAJL,aACT,mDAAmD,+BAA+B,WAAW,CAAC,MAC9F,IAEqB,oCAD0B,gCAAgC,YACnD,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK;AAC3D,KAAI,sBAAsB,KAAK,KAAK,CAClC,QAAO,KAAK,QAAQ,wBAAwB,SAAS,GAAG,KAAK,IAAI,YAAY;AAE/E,QAAO,GAAG,UAAU,IAAI"}
@@ -0,0 +1,8 @@
1
+ export declare const LOCAL_APP_RUNTIME_ENTRY = ".xopc/runtime/local-ui.js";
2
+ /**
3
+ * Local UI Apps use host-defined runtime bytes so generated projects cannot execute
4
+ * arbitrary Node.js in the gateway process through the extension `main` entry.
5
+ */
6
+ export declare const LOCAL_APP_RUNTIME_SOURCE = "export default Object.freeze({});\n";
7
+ /** Exact legacy scaffold bytes accepted only to keep existing Phase 1 drafts upgradeable. */
8
+ export declare function legacyLocalAppRuntimeSource(extensionId: string, name: string): string;
@@ -0,0 +1,25 @@
1
+ //#region src/local-apps/runtime-entry.ts
2
+ const LOCAL_APP_RUNTIME_ENTRY = ".xopc/runtime/local-ui.js";
3
+ /**
4
+ * Local UI Apps use host-defined runtime bytes so generated projects cannot execute
5
+ * arbitrary Node.js in the gateway process through the extension `main` entry.
6
+ */
7
+ const LOCAL_APP_RUNTIME_SOURCE = `export default Object.freeze({});
8
+ `;
9
+ /** Exact legacy scaffold bytes accepted only to keep existing Phase 1 drafts upgradeable. */
10
+ function legacyLocalAppRuntimeSource(extensionId, name) {
11
+ return `export default {
12
+ id: ${JSON.stringify(extensionId)},
13
+ name: ${JSON.stringify(name)},
14
+ version: '0.1.0',
15
+ kind: 'utility',
16
+ register(api) {
17
+ api.logger.info('Local app registered');
18
+ },
19
+ };
20
+ `;
21
+ }
22
+ //#endregion
23
+ export { LOCAL_APP_RUNTIME_ENTRY, LOCAL_APP_RUNTIME_SOURCE, legacyLocalAppRuntimeSource };
24
+
25
+ //# sourceMappingURL=runtime-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-entry.js","names":[],"sources":["../../../src/local-apps/runtime-entry.ts"],"sourcesContent":["export const LOCAL_APP_RUNTIME_ENTRY = '.xopc/runtime/local-ui.js';\n\n/**\n * Local UI Apps use host-defined runtime bytes so generated projects cannot execute\n * arbitrary Node.js in the gateway process through the extension `main` entry.\n */\nexport const LOCAL_APP_RUNTIME_SOURCE = `export default Object.freeze({});\n`;\n\n/** Exact legacy scaffold bytes accepted only to keep existing Phase 1 drafts upgradeable. */\nexport function legacyLocalAppRuntimeSource(extensionId: string, name: string): string {\n return `export default {\n id: ${JSON.stringify(extensionId)},\n name: ${JSON.stringify(name)},\n version: '0.1.0',\n kind: 'utility',\n register(api) {\n api.logger.info('Local app registered');\n },\n};\n`;\n}\n"],"mappings":";AAAA,MAAa,0BAA0B;;;;;AAMvC,MAAa,2BAA2B;;;AAIxC,SAAgB,4BAA4B,aAAqB,MAAsB;AACrF,QAAO;QACD,KAAK,UAAU,YAAY,CAAC;UAC1B,KAAK,UAAU,KAAK,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare function scaffoldLocalApp(input: {
2
+ workspaceRoot: string;
3
+ extensionId: string;
4
+ name: string;
5
+ idea: string;
6
+ description?: string;
7
+ }): void;
8
+ export declare function readLocalAppPermissions(workspaceRoot: string): string[];
@@ -0,0 +1,122 @@
1
+ import { LOCAL_APP_RUNTIME_ENTRY, LOCAL_APP_RUNTIME_SOURCE } from "./runtime-entry.js";
2
+ import { dirname, join } from "node:path";
3
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ //#region src/local-apps/scaffold.ts
5
+ const LOCAL_APP_PERMISSIONS = ["theme", "storage"];
6
+ function writeText(root, relativePath, content) {
7
+ const target = join(root, relativePath);
8
+ mkdirSync(dirname(target), { recursive: true });
9
+ writeFileSync(target, content, "utf8");
10
+ }
11
+ function htmlEscape(value) {
12
+ return value.replace(/[&<>"']/g, (char) => ({
13
+ "&": "&amp;",
14
+ "<": "&lt;",
15
+ ">": "&gt;",
16
+ "\"": "&quot;",
17
+ "'": "&#39;"
18
+ })[char]);
19
+ }
20
+ function scaffoldLocalApp(input) {
21
+ const { workspaceRoot, extensionId, name, idea, description } = input;
22
+ const manifest = {
23
+ id: extensionId,
24
+ name,
25
+ description: description || idea,
26
+ version: "0.1.0",
27
+ kind: "utility",
28
+ main: LOCAL_APP_RUNTIME_ENTRY,
29
+ ui: {
30
+ main: "ui/index.html",
31
+ permissions: [...LOCAL_APP_PERMISSIONS],
32
+ contributions: { pages: [{
33
+ id: "app",
34
+ title: name,
35
+ path: `/extensions/${extensionId}`,
36
+ entrypoint: "ui/index.html",
37
+ showInNav: true,
38
+ navIcon: "layout-dashboard"
39
+ }] }
40
+ },
41
+ engines: { xopc: ">=0.0.106 <1.0.0" }
42
+ };
43
+ writeText(workspaceRoot, "xopc.extension.json", `${JSON.stringify(manifest, null, 2)}\n`);
44
+ writeText(workspaceRoot, "package.json", `${JSON.stringify({
45
+ name: extensionId,
46
+ private: true,
47
+ version: "0.1.0",
48
+ type: "module"
49
+ }, null, 2)}\n`);
50
+ writeText(workspaceRoot, LOCAL_APP_RUNTIME_ENTRY, LOCAL_APP_RUNTIME_SOURCE);
51
+ writeText(workspaceRoot, "APP_BRIEF.md", `# ${name}\n\n${idea}\n\n## Product constraints\n\n- Keep the app local to this XOPC installation.\n- Preserve the extension id: \`${extensionId}\`.\n- Phase 1 is UI-only. Use only the declared theme and storage permissions.\n- Do not edit the manifest \`main\` field or \`${LOCAL_APP_RUNTIME_ENTRY}\`; xopc owns the trusted runtime entry.\n- Keep critical user journeys in \`.xopc/acceptance.json\`. Target only elements marked with \`data-xopc-test-id\`.\n- Run the local-app validation script before asking the user to install an update.\n`);
52
+ writeText(workspaceRoot, ".xopc/app.json", `${JSON.stringify({
53
+ schemaVersion: 1,
54
+ extensionId,
55
+ capabilityLevel: "ui",
56
+ entrypoint: "ui/index.html"
57
+ }, null, 2)}\n`);
58
+ writeText(workspaceRoot, ".xopc/acceptance.json", `${JSON.stringify({
59
+ schemaVersion: 1,
60
+ scenarios: [{
61
+ id: "start-app",
62
+ name: "Start the app",
63
+ steps: [{
64
+ action: "click",
65
+ target: "primary-action"
66
+ }, {
67
+ assert: "text_visible",
68
+ text: "应用已就绪"
69
+ }]
70
+ }]
71
+ }, null, 2)}\n`);
72
+ writeText(workspaceRoot, "ui/index.html", `<!doctype html>
73
+ <html lang="zh-CN">
74
+ <head>
75
+ <meta charset="UTF-8" />
76
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
+ <title>${htmlEscape(name)}</title>
78
+ <link rel="stylesheet" href="./styles.css" />
79
+ </head>
80
+ <body>
81
+ <main class="app-shell">
82
+ <section class="hero">
83
+ <h1>${htmlEscape(name)}</h1>
84
+ <p>${htmlEscape(description || idea)}</p>
85
+ <button id="primary-action" data-xopc-test-id="primary-action" type="button">开始使用</button>
86
+ <p id="feedback" data-xopc-test-id="feedback" class="feedback" aria-live="polite"></p>
87
+ </section>
88
+ </main>
89
+ <script type="module" src="./app.js"><\/script>
90
+ </body>
91
+ </html>
92
+ `);
93
+ writeText(workspaceRoot, "ui/styles.css", `:root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
94
+ * { box-sizing: border-box; }
95
+ body { margin: 0; min-height: 100vh; background: #f8fafc; color: #0f172a; }
96
+ .app-shell { min-height: 100vh; display: grid; place-items: center; padding: 32px; }
97
+ .hero { width: min(680px, 100%); padding: 40px; border: 1px solid #e2e8f0; border-radius: 16px; background: #fff; }
98
+ h1 { margin: 0 0 12px; font-size: clamp(32px, 6vw, 56px); line-height: 1.05; letter-spacing: -.03em; text-wrap: balance; }
99
+ p { color: #475569; line-height: 1.7; }
100
+ button { margin-top: 18px; border: 0; border-radius: 10px; padding: 11px 18px; background: #2563eb; color: #fff; font: inherit; font-weight: 650; cursor: pointer; }
101
+ .feedback { min-height: 24px; font-size: 14px; }
102
+ @media (prefers-color-scheme: dark) { body { background: #0f172a; color: #f8fafc; } .hero { background: #111827; border-color: #334155; } p { color: #cbd5e1; } }
103
+ `);
104
+ writeText(workspaceRoot, "ui/app.js", `const button = document.querySelector('#primary-action');
105
+ const feedback = document.querySelector('#feedback');
106
+ button?.addEventListener('click', () => {
107
+ feedback.textContent = '应用已就绪。接下来可以在 Project 中继续告诉 Coder 你的想法。';
108
+ });
109
+ `);
110
+ }
111
+ function readLocalAppPermissions(workspaceRoot) {
112
+ try {
113
+ const raw = JSON.parse(readFileSync(join(workspaceRoot, "xopc.extension.json"), "utf8"));
114
+ return Array.isArray(raw.ui?.permissions) ? raw.ui.permissions.filter((permission) => typeof permission === "string") : [];
115
+ } catch {
116
+ return [];
117
+ }
118
+ }
119
+ //#endregion
120
+ export { readLocalAppPermissions, scaffoldLocalApp };
121
+
122
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","names":[],"sources":["../../../src/local-apps/scaffold.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { LOCAL_APP_RUNTIME_ENTRY, LOCAL_APP_RUNTIME_SOURCE } from './runtime-entry.js';\n\nconst LOCAL_APP_PERMISSIONS = ['theme', 'storage'] as const;\n\nfunction writeText(root: string, relativePath: string, content: string): void {\n const target = join(root, relativePath);\n mkdirSync(dirname(target), { recursive: true });\n writeFileSync(target, content, 'utf8');\n}\n\nfunction htmlEscape(value: string): string {\n return value.replace(/[&<>\"']/g, (char) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n })[char]!);\n}\n\nexport function scaffoldLocalApp(input: {\n workspaceRoot: string;\n extensionId: string;\n name: string;\n idea: string;\n description?: string;\n}): void {\n const { workspaceRoot, extensionId, name, idea, description } = input;\n const manifest = {\n id: extensionId,\n name,\n description: description || idea,\n version: '0.1.0',\n kind: 'utility',\n main: LOCAL_APP_RUNTIME_ENTRY,\n ui: {\n main: 'ui/index.html',\n permissions: [...LOCAL_APP_PERMISSIONS],\n contributions: {\n pages: [{\n id: 'app',\n title: name,\n path: `/extensions/${extensionId}`,\n entrypoint: 'ui/index.html',\n showInNav: true,\n navIcon: 'layout-dashboard',\n }],\n },\n },\n engines: { xopc: '>=0.0.106 <1.0.0' },\n };\n\n writeText(workspaceRoot, 'xopc.extension.json', `${JSON.stringify(manifest, null, 2)}\\n`);\n writeText(workspaceRoot, 'package.json', `${JSON.stringify({\n name: extensionId,\n private: true,\n version: '0.1.0',\n type: 'module',\n }, null, 2)}\\n`);\n writeText(workspaceRoot, LOCAL_APP_RUNTIME_ENTRY, LOCAL_APP_RUNTIME_SOURCE);\n writeText(workspaceRoot, 'APP_BRIEF.md', `# ${name}\\n\\n${idea}\\n\\n## Product constraints\\n\\n- Keep the app local to this XOPC installation.\\n- Preserve the extension id: \\`${extensionId}\\`.\\n- Phase 1 is UI-only. Use only the declared theme and storage permissions.\\n- Do not edit the manifest \\`main\\` field or \\`${LOCAL_APP_RUNTIME_ENTRY}\\`; xopc owns the trusted runtime entry.\\n- Keep critical user journeys in \\`.xopc/acceptance.json\\`. Target only elements marked with \\`data-xopc-test-id\\`.\\n- Run the local-app validation script before asking the user to install an update.\\n`);\n writeText(workspaceRoot, '.xopc/app.json', `${JSON.stringify({\n schemaVersion: 1,\n extensionId,\n capabilityLevel: 'ui',\n entrypoint: 'ui/index.html',\n }, null, 2)}\\n`);\n writeText(workspaceRoot, '.xopc/acceptance.json', `${JSON.stringify({\n schemaVersion: 1,\n scenarios: [{\n id: 'start-app',\n name: 'Start the app',\n steps: [\n { action: 'click', target: 'primary-action' },\n { assert: 'text_visible', text: '应用已就绪' },\n ],\n }],\n }, null, 2)}\\n`);\n writeText(workspaceRoot, 'ui/index.html', `<!doctype html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${htmlEscape(name)}</title>\n <link rel=\"stylesheet\" href=\"./styles.css\" />\n</head>\n<body>\n <main class=\"app-shell\">\n <section class=\"hero\">\n <h1>${htmlEscape(name)}</h1>\n <p>${htmlEscape(description || idea)}</p>\n <button id=\"primary-action\" data-xopc-test-id=\"primary-action\" type=\"button\">开始使用</button>\n <p id=\"feedback\" data-xopc-test-id=\"feedback\" class=\"feedback\" aria-live=\"polite\"></p>\n </section>\n </main>\n <script type=\"module\" src=\"./app.js\"></script>\n</body>\n</html>\n`);\n writeText(workspaceRoot, 'ui/styles.css', `:root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }\n* { box-sizing: border-box; }\nbody { margin: 0; min-height: 100vh; background: #f8fafc; color: #0f172a; }\n.app-shell { min-height: 100vh; display: grid; place-items: center; padding: 32px; }\n.hero { width: min(680px, 100%); padding: 40px; border: 1px solid #e2e8f0; border-radius: 16px; background: #fff; }\nh1 { margin: 0 0 12px; font-size: clamp(32px, 6vw, 56px); line-height: 1.05; letter-spacing: -.03em; text-wrap: balance; }\np { color: #475569; line-height: 1.7; }\nbutton { margin-top: 18px; border: 0; border-radius: 10px; padding: 11px 18px; background: #2563eb; color: #fff; font: inherit; font-weight: 650; cursor: pointer; }\n.feedback { min-height: 24px; font-size: 14px; }\n@media (prefers-color-scheme: dark) { body { background: #0f172a; color: #f8fafc; } .hero { background: #111827; border-color: #334155; } p { color: #cbd5e1; } }\n`);\n writeText(workspaceRoot, 'ui/app.js', `const button = document.querySelector('#primary-action');\nconst feedback = document.querySelector('#feedback');\nbutton?.addEventListener('click', () => {\n feedback.textContent = '应用已就绪。接下来可以在 Project 中继续告诉 Coder 你的想法。';\n});\n`);\n}\n\nexport function readLocalAppPermissions(workspaceRoot: string): string[] {\n try {\n const raw = JSON.parse(readFileSync(join(workspaceRoot, 'xopc.extension.json'), 'utf8')) as {\n ui?: { permissions?: unknown };\n };\n return Array.isArray(raw.ui?.permissions)\n ? raw.ui.permissions.filter((permission): permission is string => typeof permission === 'string')\n : [];\n } catch {\n return [];\n }\n}\n"],"mappings":";;;;AAKA,MAAM,wBAAwB,CAAC,SAAS,UAAU;AAElD,SAAS,UAAU,MAAc,cAAsB,SAAuB;CAC5E,MAAM,SAAS,KAAK,MAAM,aAAa;AACvC,WAAU,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,eAAc,QAAQ,SAAS,OAAO;;AAGxC,SAAS,WAAW,OAAuB;AACzC,QAAO,MAAM,QAAQ,aAAa,UAAU;EAC1C,KAAK;EAAS,KAAK;EAAQ,KAAK;EAAQ,MAAK;EAAU,KAAK;EAC7D,EAAE,MAAO;;AAGZ,SAAgB,iBAAiB,OAMxB;CACP,MAAM,EAAE,eAAe,aAAa,MAAM,MAAM,gBAAgB;CAChE,MAAM,WAAW;EACf,IAAI;EACJ;EACA,aAAa,eAAe;EAC5B,SAAS;EACT,MAAM;EACN,MAAM;EACN,IAAI;GACF,MAAM;GACN,aAAa,CAAC,GAAG,sBAAsB;GACvC,eAAe,EACb,OAAO,CAAC;IACN,IAAI;IACJ,OAAO;IACP,MAAM,eAAe;IACrB,YAAY;IACZ,WAAW;IACX,SAAS;IACV,CAAC,EACH;GACF;EACD,SAAS,EAAE,MAAM,oBAAoB;EACtC;AAED,WAAU,eAAe,uBAAuB,GAAG,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC,IAAI;AACzF,WAAU,eAAe,gBAAgB,GAAG,KAAK,UAAU;EACzD,MAAM;EACN,SAAS;EACT,SAAS;EACT,MAAM;EACP,EAAE,MAAM,EAAE,CAAC,IAAI;AAChB,WAAU,eAAe,yBAAyB,yBAAyB;AAC3E,WAAU,eAAe,gBAAgB,KAAK,KAAK,MAAM,KAAK,gHAAgH,YAAY,kIAAkI,wBAAwB,qPAAqP;AACzkB,WAAU,eAAe,kBAAkB,GAAG,KAAK,UAAU;EAC3D,eAAe;EACf;EACA,iBAAiB;EACjB,YAAY;EACb,EAAE,MAAM,EAAE,CAAC,IAAI;AAChB,WAAU,eAAe,yBAAyB,GAAG,KAAK,UAAU;EAClE,eAAe;EACf,WAAW,CAAC;GACV,IAAI;GACJ,MAAM;GACN,OAAO,CACL;IAAE,QAAQ;IAAS,QAAQ;IAAkB,EAC7C;IAAE,QAAQ;IAAgB,MAAM;IAAS,CAC1C;GACF,CAAC;EACH,EAAE,MAAM,EAAE,CAAC,IAAI;AAChB,WAAU,eAAe,iBAAiB;;;;;WAKjC,WAAW,KAAK,CAAC;;;;;;YAMhB,WAAW,KAAK,CAAC;WAClB,WAAW,eAAe,KAAK,CAAC;;;;;;;;EAQzC;AACA,WAAU,eAAe,iBAAiB;;;;;;;;;;EAU1C;AACA,WAAU,eAAe,aAAa;;;;;EAKtC;;AAGF,SAAgB,wBAAwB,eAAiC;AACvE,KAAI;EACF,MAAM,MAAM,KAAK,MAAM,aAAa,KAAK,eAAe,sBAAsB,EAAE,OAAO,CAAC;AAGxF,SAAO,MAAM,QAAQ,IAAI,IAAI,YAAY,GACrC,IAAI,GAAG,YAAY,QAAQ,eAAqC,OAAO,eAAe,SAAS,GAC/F,EAAE;SACA;AACN,SAAO,EAAE"}
@@ -0,0 +1,37 @@
1
+ import type { Config } from '../config/schema.js';
2
+ import type { ExtensionLoader } from '../extensions/index.js';
3
+ import type { ProjectService } from '../projects/index.js';
4
+ import { LocalAppStore } from './store.js';
5
+ import type { CreateLocalAppInput, LocalApp, LocalAppAcceptanceRun, LocalAppDetail, LocalAppPreviewTarget, LocalAppValidationResult, LocalAppUiGrant, RecordLocalAppAcceptanceInput } from './types.js';
6
+ export interface LocalAppServiceOptions {
7
+ projects: ProjectService;
8
+ workspaceRoot: string;
9
+ getConfig: () => Config;
10
+ saveConfig: (config: Config) => Promise<{
11
+ saved: boolean;
12
+ error?: string;
13
+ }>;
14
+ getExtensionLoader: () => ExtensionLoader | null;
15
+ emit: (type: string, payload: unknown) => void;
16
+ }
17
+ export declare class LocalAppService {
18
+ private readonly options;
19
+ private readonly store;
20
+ constructor(options: LocalAppServiceOptions, store?: LocalAppStore);
21
+ list(): LocalApp[];
22
+ get(id: string): LocalAppDetail | null;
23
+ getUiGrant(extensionId: string): LocalAppUiGrant;
24
+ grantUiPermissions(extensionId: string): LocalAppUiGrant;
25
+ create(input: CreateLocalAppInput): LocalAppDetail;
26
+ resolvePreview(previewToken: string): LocalAppPreviewTarget | null;
27
+ validate(id: string): LocalAppValidationResult;
28
+ recordAcceptance(id: string, input: RecordLocalAppAcceptanceInput): LocalAppAcceptanceRun;
29
+ private releaseRoot;
30
+ private refreshExtensions;
31
+ private assertInstalledRuntime;
32
+ private activateArtifact;
33
+ install(id: string): Promise<LocalAppDetail>;
34
+ rollback(id: string, releaseId: string): Promise<LocalAppDetail>;
35
+ setEnabled(id: string, enabled: boolean): Promise<LocalAppDetail>;
36
+ uninstall(id: string): Promise<LocalAppDetail>;
37
+ }