@cardstack/boxel-cli 0.13.0-unstable.4 → 0.13.0-unstable.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1058) hide show
  1. package/bundled-realms/pretui/components/doc-report.gts +2 -2
  2. package/bundled-realms/pretui/components/doc-report.usage.gts +3 -3
  3. package/bundled-realms/pretui/components/form-field.md +1 -1
  4. package/bundled-realms/pretui/components/icon-button.md +1 -1
  5. package/bundled-realms/pretui/components/popup.md +1 -1
  6. package/bundled-realms/pretui/components/switch.gts +13 -0
  7. package/bundled-realms/pretui/components/switch.md +5 -3
  8. package/bundled-realms/pretui/components/theme-frame.gts +49 -22
  9. package/bundled-realms/pretui/components/tooltip.gts +141 -42
  10. package/bundled-realms/pretui/components/tooltip.md +10 -12
  11. package/bundled-realms/pretui/components/tooltip.usage.gts +3 -5
  12. package/bundled-realms/pretui/docs/structure.md +1 -1
  13. package/bundled-realms/pretui/internal/overlay.gts +4 -2
  14. package/bundled-realms/pretui/pretui-css.gts +6 -2
  15. package/bundled-realms/pretui/theme-scoped-css.gts +262 -0
  16. package/bundled-test-harness/assets/account-switch-failed-test-DwAZq6Sc.js +45 -0
  17. package/bundled-test-harness/assets/adapter-C_6pnsx1.js +2 -0
  18. package/bundled-test-harness/assets/add-field-to-card-definition-tool-test-zMYtJWBv.js +112 -0
  19. package/bundled-test-harness/assets/ai-assistant-create-workspace-test-CwhAV-Ow.js +130 -0
  20. package/bundled-test-harness/assets/ai-assistant-panel-relogin-test-DNgclkGJ.js +46 -0
  21. package/bundled-test-harness/assets/ai-assistant-test-DGYFiRTb.js +2223 -0
  22. package/bundled-test-harness/assets/ai-function-generation-test-Dj6WzKkh.js +1176 -0
  23. package/bundled-test-harness/assets/ai-module-creation-test-C4zhhxaZ.js +151 -0
  24. package/bundled-test-harness/assets/aibot-message-BShcljQ9.js +1312 -0
  25. package/bundled-test-harness/assets/app-DiEYqT6T.js +14146 -0
  26. package/bundled-test-harness/assets/apply-markdown-edit-test-CPhfeg1i.js +230 -0
  27. package/bundled-test-harness/assets/ask-user-picker-NVRkhKwt.js +370 -0
  28. package/bundled-test-harness/assets/ask-user-picker-test-SsZqFm9W.js +39 -0
  29. package/bundled-test-harness/assets/assistant-transcript-test-DesuBCIB.js +57 -0
  30. package/bundled-test-harness/assets/audio-metadata-extractor-test-8JM6oZXG.js +1176 -0
  31. package/bundled-test-harness/assets/authed-fetch-test-yPTMe-9V.js +73 -0
  32. package/bundled-test-harness/assets/avif-image-def-test-Kyknj908.js +187 -0
  33. package/bundled-test-harness/assets/basic-test-D9OrrMaq.js +139 -0
  34. package/bundled-test-harness/assets/binary-upload-dedupe-test-C0RFmHma.js +308 -0
  35. package/bundled-test-harness/assets/bot-registration-test-Bk2lTaCW.js +97 -0
  36. package/bundled-test-harness/assets/box-test-BbV9sMyU.js +50 -0
  37. package/bundled-test-harness/assets/brand-guide-attachments-test-z4wTsKbI.js +84 -0
  38. package/bundled-test-harness/assets/brand-guide-custom-css-test-BlSJE8xo.js +196 -0
  39. package/bundled-test-harness/assets/brand-guide-edit-test-DJ1OQ6IB.js +140 -0
  40. package/bundled-test-harness/assets/brand-logo-mark-field-test-ux7eetkO.js +86 -0
  41. package/bundled-test-harness/assets/broken-link-template-test-CqB3MWyw.js +192 -0
  42. package/bundled-test-harness/assets/bundled-base-modules-test-B2oNLZSJ.js +54 -0
  43. package/bundled-test-harness/assets/bundled-base-oracle-test-DelPfFjn.js +37 -0
  44. package/bundled-test-harness/assets/buried-stack-item-scroll-test-Ixsl1tS0.js +236 -0
  45. package/bundled-test-harness/assets/button-BvYQm_A1.js +141 -0
  46. package/bundled-test-harness/assets/bxl-cyclic-graph-test-p23WCLRW.js +47 -0
  47. package/bundled-test-harness/assets/bxl-expression-test-CVTQYT_C.js +250 -0
  48. package/bundled-test-harness/assets/bxl-indexing-test-D_A7X2sr.js +212 -0
  49. package/bundled-test-harness/assets/bxl-platform-module-test-Cltfyybp.js +71 -0
  50. package/bundled-test-harness/assets/can-invoke-test-CVfE1l1V.js +455 -0
  51. package/bundled-test-harness/assets/can-read-realm-test-CIz6WX_K.js +56 -0
  52. package/bundled-test-harness/assets/cancel-indexing-job-test-0LlrOgYK.js +69 -0
  53. package/bundled-test-harness/assets/capture-test-diHIWmh0.js +360 -0
  54. package/bundled-test-harness/assets/card-api-test-Ci9b0ghP.js +198 -0
  55. package/bundled-test-harness/assets/card-basics-test-RKQoQWZD.js +5364 -0
  56. package/bundled-test-harness/assets/card-chooser-paging-test-BN21sDpP.js +235 -0
  57. package/bundled-test-harness/assets/card-chooser-test-C9jDiIgM.js +393 -0
  58. package/bundled-test-harness/assets/card-context-search-results-KmkXJbmC.js +30 -0
  59. package/bundled-test-harness/assets/card-context-search-results-test-CpMr8OlX.js +184 -0
  60. package/bundled-test-harness/assets/card-def-field-def-relationships-test-D_30xak6.js +562 -0
  61. package/bundled-test-harness/assets/card-def-menu-items-test-ohuHc0b4.js +228 -0
  62. package/bundled-test-harness/assets/card-delete-test-BlsD9Xw4.js +466 -0
  63. package/bundled-test-harness/assets/card-error-3q1o3i0e.js +72 -0
  64. package/bundled-test-harness/assets/card-error-awaiting-index-test-DPgYKfbF.js +53 -0
  65. package/bundled-test-harness/assets/card-info-thumbnail-picker-test-CVfeJyjt.js +219 -0
  66. package/bundled-test-harness/assets/card-playground-test-D-7bi4PQ.js +1491 -0
  67. package/bundled-test-harness/assets/card-prerender-DemTfK_a.js +12939 -0
  68. package/bundled-test-harness/assets/card-thunk-error-test-DCBh1vRT.js +48 -0
  69. package/bundled-test-harness/assets/card-type-service-test-DXPrfVYk.js +161 -0
  70. package/bundled-test-harness/assets/check-correctness-test-DjE7r1X2.js +1093 -0
  71. package/bundled-test-harness/assets/check-domain-availability-test-U10xSKbY.js +98 -0
  72. package/bundled-test-harness/assets/chip-0r_r9SC8.js +44 -0
  73. package/bundled-test-harness/assets/choreo-CZUXQKjf.js +1110 -0
  74. package/bundled-test-harness/assets/choreo-D8RcLuoq.js +2 -0
  75. package/bundled-test-harness/assets/choreo-feature-reel-test-DRGloCAJ.js +155 -0
  76. package/bundled-test-harness/assets/choreo-gallery-build-order-test-BR-r7gaw.js +436 -0
  77. package/bundled-test-harness/assets/choreo-gallery-compositor-test-CWRLfcNT.js +399 -0
  78. package/bundled-test-harness/assets/choreo-gallery-cover-test-BT05gNKC.js +214 -0
  79. package/bundled-test-harness/assets/choreo-gallery-crossing-stage-test-WJ7FmjYi.js +133 -0
  80. package/bundled-test-harness/assets/choreo-gallery-demos-alive-test-C-yxdVT_.js +42 -0
  81. package/bundled-test-harness/assets/choreo-gallery-drift-test-HX1mK3od.js +620 -0
  82. package/bundled-test-harness/assets/choreo-gallery-escort-test-jZEVk_V4.js +187 -0
  83. package/bundled-test-harness/assets/choreo-gallery-films-test-DLDQzsxb.js +140 -0
  84. package/bundled-test-harness/assets/choreo-gallery-filter-test-BJv8KEeP.js +46 -0
  85. package/bundled-test-harness/assets/choreo-gallery-fold-test-BssKC8A8.js +180 -0
  86. package/bundled-test-harness/assets/choreo-gallery-guide-pages-test-DS41eNNV.js +114 -0
  87. package/bundled-test-harness/assets/choreo-gallery-guides-test-DTj7yEKp.js +321 -0
  88. package/bundled-test-harness/assets/choreo-gallery-hang-test-BqJLmeFF.js +426 -0
  89. package/bundled-test-harness/assets/choreo-gallery-highlight-test-B1Q7-ICm.js +60 -0
  90. package/bundled-test-harness/assets/choreo-gallery-inline-edit-test-7My9lVam.js +370 -0
  91. package/bundled-test-harness/assets/choreo-gallery-interruption-test-BlIAaGrw.js +176 -0
  92. package/bundled-test-harness/assets/choreo-gallery-lone-stack-test-C9NnYIP8.js +104 -0
  93. package/bundled-test-harness/assets/choreo-gallery-navigation-test-CWzXkvbW.js +193 -0
  94. package/bundled-test-harness/assets/choreo-gallery-playhead-transport-test-CwdiZWeI.js +111 -0
  95. package/bundled-test-harness/assets/choreo-gallery-poplayout-subtree-test-D55fxwTn.js +127 -0
  96. package/bundled-test-harness/assets/choreo-gallery-presence-modes-test-D8Wxv0lb.js +100 -0
  97. package/bundled-test-harness/assets/choreo-gallery-presentation-test-BLoWfp6G.js +157 -0
  98. package/bundled-test-harness/assets/choreo-gallery-reduced-motion-test-D5XxTBDS.js +145 -0
  99. package/bundled-test-harness/assets/choreo-gallery-sheet-standalone-test-DrHTTvss.js +89 -0
  100. package/bundled-test-harness/assets/choreo-gallery-sheet-test-BFVdnM_b.js +48 -0
  101. package/bundled-test-harness/assets/choreo-gallery-site-test-BhEBHdvI.js +237 -0
  102. package/bundled-test-harness/assets/choreo-gallery-stage-DcKKsdcu.js +256 -0
  103. package/bundled-test-harness/assets/choreo-gallery-stage-keyboard-test-Fcc6SC3F.js +102 -0
  104. package/bundled-test-harness/assets/choreo-gallery-stages-test-Cb-ADpxK.js +35 -0
  105. package/bundled-test-harness/assets/choreo-gallery-subdivision-test-DMJ6C9FW.js +138 -0
  106. package/bundled-test-harness/assets/choreo-gallery-table-plan-test-CAjSUdLJ.js +144 -0
  107. package/bundled-test-harness/assets/choreo-gallery-theater-test-B-3IZMj9.js +252 -0
  108. package/bundled-test-harness/assets/choreo-gallery-tuning-test-BVxK5Ry_.js +95 -0
  109. package/bundled-test-harness/assets/choreo-gallery-widget-room-test-uWBH1EKE.js +88 -0
  110. package/bundled-test-harness/assets/choreo-gallery-wires-test-Bo4Sd1Vn.js +148 -0
  111. package/bundled-test-harness/assets/client-telemetry-test-DFM4mML2.js +340 -0
  112. package/bundled-test-harness/assets/clip-DY6N6K67.js +92 -0
  113. package/bundled-test-harness/assets/clip-DzXoPdp-.js +2 -0
  114. package/bundled-test-harness/assets/code-block-DRE8KQZQ.js +502 -0
  115. package/bundled-test-harness/assets/code-ref-test-CDcqE8_C.js +163 -0
  116. package/bundled-test-harness/assets/code-submode-availability-test-DPTqZYYm.js +198 -0
  117. package/bundled-test-harness/assets/code-submode-test-AHV4mzJ4.js +1839 -0
  118. package/bundled-test-harness/assets/codeblocks-test-D6ijVvEL.js +418 -0
  119. package/bundled-test-harness/assets/codemirror-editor-test-BeAMVivu.js +1512 -0
  120. package/bundled-test-harness/assets/codemirror-embed-toolbar-test-Bnb7_KUT.js +366 -0
  121. package/bundled-test-harness/assets/color-field-test-CR6gGEqE.js +212 -0
  122. package/bundled-test-harness/assets/compaction-test-CjEsY0uG.js +150 -0
  123. package/bundled-test-harness/assets/computed-test-DtyZGgdc.js +1320 -0
  124. package/bundled-test-harness/assets/container-BYGZImLU.css +5101 -0
  125. package/bundled-test-harness/assets/container-CBZZ8uFw.js +10925 -0
  126. package/bundled-test-harness/assets/content-preview-components-test-BztI4fz-.js +462 -0
  127. package/bundled-test-harness/assets/copy-and-edit-test-Cqw7kcur.js +292 -0
  128. package/bundled-test-harness/assets/copy-card-test-D5m3eVEH.js +218 -0
  129. package/bundled-test-harness/assets/copy-file-to-realm-test-FPY-6Ddm.js +148 -0
  130. package/bundled-test-harness/assets/copy-source-test-9pJD-qey.js +56 -0
  131. package/bundled-test-harness/assets/create-file-test-DjMf88u_.js +1086 -0
  132. package/bundled-test-harness/assets/create-file-test-XbgOqg38.js +464 -0
  133. package/bundled-test-harness/assets/create-listing-modal-test-De7b4vu3.js +273 -0
  134. package/bundled-test-harness/assets/create-listing-pr-request-test-CBtIV0Z8.js +117 -0
  135. package/bundled-test-harness/assets/create-spec-test-4xR0gM7E.js +256 -0
  136. package/bundled-test-harness/assets/create-submission-workflow-test-DQpWrpGT.js +132 -0
  137. package/bundled-test-harness/assets/create-workspace-test-BQAudgpS.js +104 -0
  138. package/bundled-test-harness/assets/credential-fields-test-DXKBdbZD.js +200 -0
  139. package/bundled-test-harness/assets/csv-file-def-test-Cnp4y834.js +102 -0
  140. package/bundled-test-harness/assets/dashboard-CmuOhIYC.js +3628 -0
  141. package/bundled-test-harness/assets/dashboard-CzN49x74.css +2400 -0
  142. package/bundled-test-harness/assets/dashboard-add-workspace-test-T32hWB-g.js +50 -0
  143. package/bundled-test-harness/assets/dashboard-agents-test-BN5maCgQ.js +334 -0
  144. package/bundled-test-harness/assets/dashboard-archive-test-BzwUte_Z.js +128 -0
  145. package/bundled-test-harness/assets/dashboard-delete-test-D2Gcp81V.js +163 -0
  146. package/bundled-test-harness/assets/dashboard-duplicate-offer-test-BgKNIr35.js +71 -0
  147. package/bundled-test-harness/assets/dashboard-duplicate-test-D7JEWylc.js +222 -0
  148. package/bundled-test-harness/assets/dashboard-get-started-test-DOGPzGoA.js +374 -0
  149. package/bundled-test-harness/assets/dashboard-ir2bakJS.js +142 -0
  150. package/bundled-test-harness/assets/dashboard-pinned-cards-test-Bn_P9xJU.js +220 -0
  151. package/bundled-test-harness/assets/dashboard-publish-test-64sGQ0nG.js +70 -0
  152. package/bundled-test-harness/assets/dashboard-reindex-test-DJjYq1ct.js +115 -0
  153. package/bundled-test-harness/assets/dashboard-section-rail-test-ZkAhWu5V.js +169 -0
  154. package/bundled-test-harness/assets/dashboard-state-test-BbkpPB5L.js +162 -0
  155. package/bundled-test-harness/assets/dashboard-test-CBLdPrSt.js +545 -0
  156. package/bundled-test-harness/assets/dashboard-unreachable-notice-test-DFT3R1Zx.js +62 -0
  157. package/bundled-test-harness/assets/dashboard-workspace-test-DqBdCFo2.js +537 -0
  158. package/bundled-test-harness/assets/dashboard-workspaces-section-test-CH1HDfxR.js +410 -0
  159. package/bundled-test-harness/assets/data-file-def-preview-test-CNsZwQEJ.js +155 -0
  160. package/bundled-test-harness/assets/date-time-fields-test-CEFaZTnF.js +359 -0
  161. package/bundled-test-harness/assets/debug-message-test-Ck3BfFv3.js +117 -0
  162. package/bundled-test-harness/assets/declared-captures-test-C-0yPRU7.js +879 -0
  163. package/bundled-test-harness/assets/delete-workspace-test-CZOwGy78.js +102 -0
  164. package/bundled-test-harness/assets/diffing-test-BEKI905O.js +210 -0
  165. package/bundled-test-harness/assets/download-file-to-realm-test-C1kAGDll.js +119 -0
  166. package/bundled-test-harness/assets/editor-test-C9x5ZY6g.js +688 -0
  167. package/bundled-test-harness/assets/edits-D76-aanh.js +651 -0
  168. package/bundled-test-harness/assets/email-field-test-LGkrB_2B.js +75 -0
  169. package/bundled-test-harness/assets/enum-field-test-CPyV1J-m.js +935 -0
  170. package/bundled-test-harness/assets/error-display-test-bWuUtfIG.js +343 -0
  171. package/bundled-test-harness/assets/evaluate-module-test-CbS5h8t3.js +91 -0
  172. package/bundled-test-harness/assets/execute-atomic-operations-test-B4Kfco0k.js +113 -0
  173. package/bundled-test-harness/assets/externals-k-2ZBepK.js +4393 -0
  174. package/bundled-test-harness/assets/fallback-banner-live-delete-test-CuFiH1D8.js +207 -0
  175. package/bundled-test-harness/assets/fallback-banner-test-CqSRN7D8.js +225 -0
  176. package/bundled-test-harness/assets/fallback-banner-test-DQkrFE4-.js +53 -0
  177. package/bundled-test-harness/assets/fallback-models-test-BWekNxC0.js +161 -0
  178. package/bundled-test-harness/assets/fetch-card-json-test-DULUqUEu.js +61 -0
  179. package/bundled-test-harness/assets/field-configuration-test-BXZq28xa.js +551 -0
  180. package/bundled-test-harness/assets/field-markdown-domain-test-DdXCifs6.js +616 -0
  181. package/bundled-test-harness/assets/field-markdown-oversize-test-DouPPTHX.js +88 -0
  182. package/bundled-test-harness/assets/field-markdown-primitives-test-DSRGJeyl.js +365 -0
  183. package/bundled-test-harness/assets/field-markdown-specialized-test-DlTYLl_s.js +534 -0
  184. package/bundled-test-harness/assets/field-playground-test-NhS4VvoN.js +894 -0
  185. package/bundled-test-harness/assets/field-support-predicates-test-DWpyT1Qf.js +167 -0
  186. package/bundled-test-harness/assets/file-attachment-test-LwZf-BSY.js +796 -0
  187. package/bundled-test-harness/assets/file-chooser-panel-test-DuAqD9bn.js +153 -0
  188. package/bundled-test-harness/assets/file-chooser-test-CjaXXwpT.js +504 -0
  189. package/bundled-test-harness/assets/file-def-format-templates-test-BDs4OmHq.js +380 -0
  190. package/bundled-test-harness/assets/file-def-live-reload-test-BqTdnq-W.js +124 -0
  191. package/bundled-test-harness/assets/file-def-menu-items-test-DqdINXsD.js +138 -0
  192. package/bundled-test-harness/assets/file-def-navigation-test-BPGr0SQy.js +109 -0
  193. package/bundled-test-harness/assets/file-def-serialization-test-CvjW-FlC.js +89 -0
  194. package/bundled-test-harness/assets/file-def-test-C-Yv_NjS.js +216 -0
  195. package/bundled-test-harness/assets/file-preview-live-reload-test-MzwGwasU.js +310 -0
  196. package/bundled-test-harness/assets/file-resource-primitives-test-D3GmfqNs.js +255 -0
  197. package/bundled-test-harness/assets/file-tree-from-index-test-CfldnUgm.js +96 -0
  198. package/bundled-test-harness/assets/file-tree-test--0ZCPRe9.js +851 -0
  199. package/bundled-test-harness/assets/file-view-model-test-BDIAeBV8.js +346 -0
  200. package/bundled-test-harness/assets/filedef-export-shape-test-B2tgU404.js +61 -0
  201. package/bundled-test-harness/assets/film-Bf73ypzi.js +4074 -0
  202. package/bundled-test-harness/assets/film-C54lhaiU.js +2 -0
  203. package/bundled-test-harness/assets/film-DPcMEHYu.js +13 -0
  204. package/bundled-test-harness/assets/flac-audio-def-test-ByoXMoQ1.js +143 -0
  205. package/bundled-test-harness/assets/font-metadata-extractor-test-hawLTDTG.js +415 -0
  206. package/bundled-test-harness/assets/formatted-aibot-message-test-BahBj3x8.js +109 -0
  207. package/bundled-test-harness/assets/formatted-user-message-test-CodbrU3t.js +43 -0
  208. package/bundled-test-harness/assets/freestyle-test-faQhSciz.js +17 -0
  209. package/bundled-test-harness/assets/full-reindex-realm-test-CE5RJTIC.js +101 -0
  210. package/bundled-test-harness/assets/garbage-collection-test-v-3sev77.js +403 -0
  211. package/bundled-test-harness/assets/general-test-DKZPBtSb.js +923 -0
  212. package/bundled-test-harness/assets/generate-example-cards-one-shot-test-CucWyrkH.js +65 -0
  213. package/bundled-test-harness/assets/generic-file-def-preview-test-_EDRsebi.js +71 -0
  214. package/bundled-test-harness/assets/get-available-realm-identifiers-test-CV023yek.js +47 -0
  215. package/bundled-test-harness/assets/get-catalog-realm-identifiers-test--DOu3DyV.js +47 -0
  216. package/bundled-test-harness/assets/get-default-writable-realm-test-Bic8jl6R.js +54 -0
  217. package/bundled-test-harness/assets/get-published-realms-test-DrA5YcFN.js +71 -0
  218. package/bundled-test-harness/assets/get-realm-of-resource-identifier-test-jfU6SIWn.js +54 -0
  219. package/bundled-test-harness/assets/get-relationship-membership-state-test-Ccy2Z7MN.js +1308 -0
  220. package/bundled-test-harness/assets/get-user-system-card-test-Bmq2lmWv.js +67 -0
  221. package/bundled-test-harness/assets/gif-image-def-test-N1mMQC-X.js +188 -0
  222. package/bundled-test-harness/assets/glimmer-motion-and-choreo-in-cards-test-BkUDjiTs.js +160 -0
  223. package/bundled-test-harness/assets/glimmer-motion-and-choreo-test-DsFWCzTs.js +74 -0
  224. package/bundled-test-harness/assets/gts-file-def-test-BL1jpQ9J.js +100 -0
  225. package/bundled-test-harness/assets/head-format-preview-test-BVCaPQqc.js +158 -0
  226. package/bundled-test-harness/assets/helpers-Ds68JxHe.js +5 -0
  227. package/bundled-test-harness/assets/host-mode-service-B4bisRdR.js +262 -0
  228. package/bundled-test-harness/assets/host-mode-test-BmkN2PCJ.js +831 -0
  229. package/bundled-test-harness/assets/host-submode-test-C6SxQ796.js +1392 -0
  230. package/bundled-test-harness/assets/html-component-test-CNQ9wF0Q.js +65 -0
  231. package/bundled-test-harness/assets/html-def-test-BcaCVVQq.js +160 -0
  232. package/bundled-test-harness/assets/html-meta-extractor-test-BU1hjkTp.js +60 -0
  233. package/bundled-test-harness/assets/hydratable-card-test-CX6MWiti.js +426 -0
  234. package/bundled-test-harness/assets/icon-button-C1zH38Nc.js +67 -0
  235. package/bundled-test-harness/assets/image-metadata-extractor-test-CakSBPZ_.js +1109 -0
  236. package/bundled-test-harness/assets/index-changes-test-D8RtgfyG.js +367 -0
  237. package/bundled-test-harness/assets/index-query-engine-test-BETfoYOK.js +2593 -0
  238. package/bundled-test-harness/assets/index-writer-test-B_bR2fjw.js +3040 -0
  239. package/bundled-test-harness/assets/inspector-test-BwPXJj1S.js +2058 -0
  240. package/bundled-test-harness/assets/instance-filter-matcher-test-BSVCg59b.js +788 -0
  241. package/bundled-test-harness/assets/instantiate-card-test-BVQhZjpq.js +164 -0
  242. package/bundled-test-harness/assets/interact-submode-creation-and-permissions-test-DzQLG41H.js +475 -0
  243. package/bundled-test-harness/assets/interact-submode-setup-C5bKXq73.js +566 -0
  244. package/bundled-test-harness/assets/invalidate-realm-identifiers-test-Cd3dpjhB.js +99 -0
  245. package/bundled-test-harness/assets/invite-user-to-room-test-Brabz1tt.js +142 -0
  246. package/bundled-test-harness/assets/invoke-card-operation-test-C1Q1FUf4.js +333 -0
  247. package/bundled-test-harness/assets/isolated-render-test-BLJfS7BU.js +51 -0
  248. package/bundled-test-harness/assets/job-scoped-doc-cache-test-Bgv5woaB.js +181 -0
  249. package/bundled-test-harness/assets/job-scoped-instance-residency-test-C_bB6KpR.js +103 -0
  250. package/bundled-test-harness/assets/jpg-image-def-test-gdLIKocD.js +697 -0
  251. package/bundled-test-harness/assets/json-file-def-test-CPYMRBY-.js +95 -0
  252. package/bundled-test-harness/assets/key-notices-badge-test-DemslfLw.js +155 -0
  253. package/bundled-test-harness/assets/key-readiness-test-5tq5JycL.js +190 -0
  254. package/bundled-test-harness/assets/linksto-broken-link-placeholder-test-BbhDZ2PH.js +398 -0
  255. package/bundled-test-harness/assets/linksto-error-sentinel-test-C2t7fFPi.js +238 -0
  256. package/bundled-test-harness/assets/linksto-many-broken-link-placeholder-test--U_UvUty.js +354 -0
  257. package/bundled-test-harness/assets/linksto-many-per-slot-access-test-Dj3Xy0ZF.js +303 -0
  258. package/bundled-test-harness/assets/linksto-sentinel-reload-test-D0dK4Jjs.js +197 -0
  259. package/bundled-test-harness/assets/linksto-sentinel-roundtrip-test---IYiXok.js +260 -0
  260. package/bundled-test-harness/assets/linksto-singular-js-access-contract-test-CT04MhES.js +196 -0
  261. package/bundled-test-harness/assets/lint-and-fix-test-aadgx7rX.js +51 -0
  262. package/bundled-test-harness/assets/live-test-BGij_vuc.js +100 -0
  263. package/bundled-test-harness/assets/live-test-CppvValf.js +2 -0
  264. package/bundled-test-harness/assets/load-default-skills-test-Cp2m_ebp.js +74 -0
  265. package/bundled-test-harness/assets/loader-parallel-prefetch-test-vU_26Jqu.js +257 -0
  266. package/bundled-test-harness/assets/loader-test-CTaw8K_B.js +482 -0
  267. package/bundled-test-harness/assets/loading-test-DGGt4qZZ.js +63 -0
  268. package/bundled-test-harness/assets/m4a-audio-def-test-DAiReOXH.js +190 -0
  269. package/bundled-test-harness/assets/main-bXHwJ0CN.js +5 -0
  270. package/bundled-test-harness/assets/markdown-embed-chooser-modal-test-CIXq9FqR.js +470 -0
  271. package/bundled-test-harness/assets/markdown-embed-chooser-test-zFGh9WfP.js +334 -0
  272. package/bundled-test-harness/assets/markdown-embed-preview-pane-test-Byn_WFwY.js +447 -0
  273. package/bundled-test-harness/assets/markdown-embed-preview-test-Y3iFMttg.js +368 -0
  274. package/bundled-test-harness/assets/markdown-fallback-test-Br1TPkLZ.js +316 -0
  275. package/bundled-test-harness/assets/markdown-file-def-test-Crm2MAnq.js +380 -0
  276. package/bundled-test-harness/assets/markdown-helpers-test-CZ9Vvywc.js +188 -0
  277. package/bundled-test-harness/assets/markdown-highlighting-test-CFMkoVDl.js +244 -0
  278. package/bundled-test-harness/assets/markdown-preview-lOh-MyqF.js +72 -0
  279. package/bundled-test-harness/assets/markdown-preview-test-B8w0fFIy.js +238 -0
  280. package/bundled-test-harness/assets/markdown-skill-frontmatter-test-aH6HQly1.js +146 -0
  281. package/bundled-test-harness/assets/markdown-skill-search-test-CiNLQs-5.js +80 -0
  282. package/bundled-test-harness/assets/matrix-service-account-switch-sessions-test-DaA8w1Jj.js +63 -0
  283. package/bundled-test-harness/assets/matrix-service-boot-assembly-test-21GIlf55.js +300 -0
  284. package/bundled-test-harness/assets/matrix-service-personal-realm-test-jn9DUENm.js +125 -0
  285. package/bundled-test-harness/assets/matrix-service-realm-servers-test-eNZVJakl.js +55 -0
  286. package/bundled-test-harness/assets/matrix-service-realms-list-updated-test-Cabm8_dY.js +132 -0
  287. package/bundled-test-harness/assets/matrix-service-relogin-test-C77EFgjc.js +59 -0
  288. package/bundled-test-harness/assets/matrix-service-sliding-sync-test-BgM0fPiO.js +35 -0
  289. package/bundled-test-harness/assets/menu-Cl50TSFe.js +3561 -0
  290. package/bundled-test-harness/assets/message-service-subscription-test-CfEqH2jP.js +110 -0
  291. package/bundled-test-harness/assets/message-service-test-DsTzH1In.js +144 -0
  292. package/bundled-test-harness/assets/message-tool-test-C5xVfTtj.js +33 -0
  293. package/bundled-test-harness/assets/midi-audio-def-test-goLRCNCZ.js +182 -0
  294. package/bundled-test-harness/assets/migrate-skill-test-yPCbVfxG.js +161 -0
  295. package/bundled-test-harness/assets/mini-YXjrY_tH.js +1404 -0
  296. package/bundled-test-harness/assets/mini-card-chooser-test-DrllikHB.js +272 -0
  297. package/bundled-test-harness/assets/mini-file-chooser-test-BfqayhmI.js +201 -0
  298. package/bundled-test-harness/assets/mock-matrix-C7BrPS6S.js +2 -0
  299. package/bundled-test-harness/assets/mock-matrix-FjsXmU7-.js +15107 -0
  300. package/bundled-test-harness/assets/modal-BgaZavcs.js +322 -0
  301. package/bundled-test-harness/assets/model-cost-tier-test-BGg-sJML.js +174 -0
  302. package/bundled-test-harness/assets/model-file-extract-test-CjEBupY5.js +206 -0
  303. package/bundled-test-harness/assets/module-transpile-cache-test-XW6DDsmz.js +67 -0
  304. package/bundled-test-harness/assets/monaco-test-DkoRuFiQ.js +100 -0
  305. package/bundled-test-harness/assets/mp3-audio-def-test-v369NNsQ.js +133 -0
  306. package/bundled-test-harness/assets/multiple-stacks-test-CJpYPWdM.js +208 -0
  307. package/bundled-test-harness/assets/nested-query-field-render-test-CfDrBcUN.js +249 -0
  308. package/bundled-test-harness/assets/no-stacks-test-MmsJeLpz.js +48 -0
  309. package/bundled-test-harness/assets/number-field-test-A7B2r0M5.js +184 -0
  310. package/bundled-test-harness/assets/office-file-def-test-BNRvFmCB.js +263 -0
  311. package/bundled-test-harness/assets/office-metadata-extractor-test-zlDoEIEp.js +218 -0
  312. package/bundled-test-harness/assets/office-poster-capture-test-DdFEaOxR.js +203 -0
  313. package/bundled-test-harness/assets/ogg-audio-def-test-kk91BzQY.js +196 -0
  314. package/bundled-test-harness/assets/open-create-listing-modal-test-D1Dl1pov.js +131 -0
  315. package/bundled-test-harness/assets/open-image-in-new-tab-test-DyCmdMyO.js +52 -0
  316. package/bundled-test-harness/assets/open-secure-window-test-dz1vPudF.js +253 -0
  317. package/bundled-test-harness/assets/open-workspace-test-BvTcskzP.js +46 -0
  318. package/bundled-test-harness/assets/operation-lowering-test-omU66bkJ.js +2957 -0
  319. package/bundled-test-harness/assets/operations-invocation-test-BMj42rQe.js +523 -0
  320. package/bundled-test-harness/assets/operations-optimistic-test-KJLyzT8v.js +203 -0
  321. package/bundled-test-harness/assets/operations-query-test-J3HcnaoK.js +459 -0
  322. package/bundled-test-harness/assets/operations-store-reconciliation-test-CkbGBMBQ.js +406 -0
  323. package/bundled-test-harness/assets/operations-test-C7zXXpRW.js +2518 -0
  324. package/bundled-test-harness/assets/operator-mode-acceptance-test-BPolI_RD.js +1195 -0
  325. package/bundled-test-harness/assets/operator-mode-card-chooser-test-DjTQgizt.js +1036 -0
  326. package/bundled-test-harness/assets/operator-mode-links-test-DSEqy5Db.js +797 -0
  327. package/bundled-test-harness/assets/operator-mode-test-CB7BNDNE.js +696 -0
  328. package/bundled-test-harness/assets/operator-mode-ui-test-C1OhF1il.js +1045 -0
  329. package/bundled-test-harness/assets/overlay-D2rQi8lJ.js +427 -0
  330. package/bundled-test-harness/assets/overlay-menu-items-test-CPN31yBJ.js +331 -0
  331. package/bundled-test-harness/assets/overlays-B-qMEPhY.js +2 -0
  332. package/bundled-test-harness/assets/overlays-DSf2je0c.js +75 -0
  333. package/bundled-test-harness/assets/overlays-detached-element-test-C73nMXes.js +56 -0
  334. package/bundled-test-harness/assets/panel-DjrC7DxA.js +15124 -0
  335. package/bundled-test-harness/assets/past-sessions-test-BSIS2Hlq.js +321 -0
  336. package/bundled-test-harness/assets/patch-fields-test-Bp2IE0Jw.js +1014 -0
  337. package/bundled-test-harness/assets/patch-instance-test-DssuRGKP.js +348 -0
  338. package/bundled-test-harness/assets/pdf-file-def-test-Barj9ffD.js +111 -0
  339. package/bundled-test-harness/assets/pdf-metadata-extractor-test-B7KnXcIm.js +130 -0
  340. package/bundled-test-harness/assets/permission-checking-realm-test-BD4O0nen.js +306 -0
  341. package/bundled-test-harness/assets/persist-module-inspector-view-test-BIdbkdPr.js +60 -0
  342. package/bundled-test-harness/assets/persist-scroll-position-test-Bhb7wJiy.js +254 -0
  343. package/bundled-test-harness/assets/phone-number-field-test-C2yuCrXY.js +73 -0
  344. package/bundled-test-harness/assets/pill-menu-test-BIp91YXi.js +60 -0
  345. package/bundled-test-harness/assets/plate-CJq1veVA.js +41 -0
  346. package/bundled-test-harness/assets/plate-CwAvOUfL.js +2 -0
  347. package/bundled-test-harness/assets/player-Digk7omr.js +2 -0
  348. package/bundled-test-harness/assets/player-vsjosoN_.js +82 -0
  349. package/bundled-test-harness/assets/playground-B02e8uqB.js +58 -0
  350. package/bundled-test-harness/assets/plural-field-components-test-C1acuN-A.js +198 -0
  351. package/bundled-test-harness/assets/png-image-def-test-B9QeVo1f.js +266 -0
  352. package/bundled-test-harness/assets/policy-audit-confirmation-test-CeVquvc_.js +155 -0
  353. package/bundled-test-harness/assets/policy-scoped-search-test-SFPiIqR3.js +339 -0
  354. package/bundled-test-harness/assets/prerender-file-extract-test-HSJKAHyO.js +346 -0
  355. package/bundled-test-harness/assets/prerender-html-test-BTAcFCme.js +670 -0
  356. package/bundled-test-harness/assets/prerender-meta-test-Csyz2b21.js +614 -0
  357. package/bundled-test-harness/assets/prerender-module-test-DpTYAbmI.js +409 -0
  358. package/bundled-test-harness/assets/prerender-persistence-block-test-BhIt-7kK.js +124 -0
  359. package/bundled-test-harness/assets/prerender-pre-model-error-test-DxKPQKqx.js +61 -0
  360. package/bundled-test-harness/assets/prerender-serialize-cycle-test-C8V5YWQO.js +134 -0
  361. package/bundled-test-harness/assets/prerendered-html-read-test-Cq7JVGwg.js +191 -0
  362. package/bundled-test-harness/assets/pretui-components-test-Ducr9fLT.css +743 -0
  363. package/bundled-test-harness/assets/pretui-components-test-Hs0uyIUQ.js +992 -0
  364. package/bundled-test-harness/assets/pretui-primitives-CRs8NRm0.js +69 -0
  365. package/bundled-test-harness/assets/preview-format-test-cCqLrfdY.js +103 -0
  366. package/bundled-test-harness/assets/preview-test-BMqJSoR1.js +474 -0
  367. package/bundled-test-harness/assets/process-card-test-578tfDvr.js +94 -0
  368. package/bundled-test-harness/assets/profile-settings-modal-CVnNl7Yu.js +990 -0
  369. package/bundled-test-harness/assets/publish-realm-COoeWexp.js +82 -0
  370. package/bundled-test-harness/assets/publish-realm-test-a5nkx0Ov.js +210 -0
  371. package/bundled-test-harness/assets/query-field-barrier-test-qX3xVPfk.js +113 -0
  372. package/bundled-test-harness/assets/query-field-error-sentinel-test-V6JDPFLX.js +201 -0
  373. package/bundled-test-harness/assets/query-field-membership-status-test-CK7eDf-K.js +323 -0
  374. package/bundled-test-harness/assets/query-field-page-optin-test-B5YBHUvU.js +126 -0
  375. package/bundled-test-harness/assets/query-field-schema-test-D4SjOGlK.js +231 -0
  376. package/bundled-test-harness/assets/query-field-search-throttle-test-BVkm1-D8.js +154 -0
  377. package/bundled-test-harness/assets/query-field-stale-prerender-test-9g7hcD91.js +152 -0
  378. package/bundled-test-harness/assets/query-fields-test-CQYqhzpN.js +535 -0
  379. package/bundled-test-harness/assets/quick-look-test-CE0o6lJ4.js +139 -0
  380. package/bundled-test-harness/assets/read-card-for-ai-assistant-test-D0mJorwH.js +91 -0
  381. package/bundled-test-harness/assets/read-file-for-ai-assistant-test-B_F_2GEB.js +45 -0
  382. package/bundled-test-harness/assets/read-source-test-BgJV4B4F.js +67 -0
  383. package/bundled-test-harness/assets/read-text-file-test-DaeLzg4u.js +124 -0
  384. package/bundled-test-harness/assets/realm-C_a9su1j.js +2858 -0
  385. package/bundled-test-harness/assets/realm-api-keys-D5USFOoS.js +2 -0
  386. package/bundled-test-harness/assets/realm-api-keys-m_VOcixC.js +578 -0
  387. package/bundled-test-harness/assets/realm-api-keys-test-BVs7f5Dz.js +109 -0
  388. package/bundled-test-harness/assets/realm-api-keys-test-C_uZnTmq.js +588 -0
  389. package/bundled-test-harness/assets/realm-config-m5eccPW8.js +833 -0
  390. package/bundled-test-harness/assets/realm-config-policy-standing-test-Ber-IXpc.js +290 -0
  391. package/bundled-test-harness/assets/realm-config-routing-rule-edit-test-3Xqt5VlM.js +274 -0
  392. package/bundled-test-harness/assets/realm-config-settings-test-CkalhbDF.js +187 -0
  393. package/bundled-test-harness/assets/realm-field-test-B_PUoqnk.js +100 -0
  394. package/bundled-test-harness/assets/realm-indexing-test-8iLjeV_e.js +4380 -0
  395. package/bundled-test-harness/assets/realm-policy-fitted-test-Bptvra0U.js +206 -0
  396. package/bundled-test-harness/assets/realm-policy-test-DwG0qqXE.js +1141 -0
  397. package/bundled-test-harness/assets/realm-querying-test-Dvw26A6Q.js +1067 -0
  398. package/bundled-test-harness/assets/realm-server-form-agnostic-test-DrXQH0UP.js +96 -0
  399. package/bundled-test-harness/assets/realm-server-subscriber-test-Dkj23vQK.js +46 -0
  400. package/bundled-test-harness/assets/realm-service-info-test-CdRlDhj-.js +67 -0
  401. package/bundled-test-harness/assets/realm-test-DGYN48Qx.js +2364 -0
  402. package/bundled-test-harness/assets/realm-token-refresh-test-CEWATL9h.js +153 -0
  403. package/bundled-test-harness/assets/realm-url-tooltip-test-8ZjTNPuy.js +78 -0
  404. package/bundled-test-harness/assets/reasoning-test-BV49AYg2.js +221 -0
  405. package/bundled-test-harness/assets/recent-files-test-Dc6L6Dm4.js +450 -0
  406. package/bundled-test-harness/assets/reindex-realm-test-CFRSt39u.js +117 -0
  407. package/bundled-test-harness/assets/relationship-loading-state-test-FKbtOFUr.js +290 -0
  408. package/bundled-test-harness/assets/remix-card-test-CKGC_Sgq.js +58 -0
  409. package/bundled-test-harness/assets/rendered-markdown-DXiDN91F.js +292 -0
  410. package/bundled-test-harness/assets/rendered-markdown-test-n3MlXmmu.js +587 -0
  411. package/bundled-test-harness/assets/resident-instance-reuse-test-BjdhE6CM.js +82 -0
  412. package/bundled-test-harness/assets/result-tile-o6dtjgX4.js +144 -0
  413. package/bundled-test-harness/assets/rich-markdown-field-test-VXr6l20h.js +1647 -0
  414. package/bundled-test-harness/assets/room-message-test-BrjJ2wMs.js +484 -0
  415. package/bundled-test-harness/assets/run-P_7TsreC.js +4017 -0
  416. package/bundled-test-harness/assets/run-realm-code-DsBUf1si.js +2964 -0
  417. package/bundled-test-harness/assets/run-realm-code-test-cS0DzaKo.js +3844 -0
  418. package/bundled-test-harness/assets/sanitize-module-list-test-CvuOOiUY.js +85 -0
  419. package/bundled-test-harness/assets/save-card-skip-index-wait-test-fpJ1eq32.js +89 -0
  420. package/bundled-test-harness/assets/schema-editor-test-NkKjCgyf.js +785 -0
  421. package/bundled-test-harness/assets/schema-generation-test-Dmg3lWl7.js +28 -0
  422. package/bundled-test-harness/assets/scrolling-test-B0GNYGYc.js +412 -0
  423. package/bundled-test-harness/assets/search-adorn-test-CZebk7_y.js +136 -0
  424. package/bundled-test-harness/assets/search-and-choose-test-Bd4NZUup.js +154 -0
  425. package/bundled-test-harness/assets/search-correlation-id-test-DXKlEO55.js +139 -0
  426. package/bundled-test-harness/assets/search-entries-result-test-QQawA8Fm.js +234 -0
  427. package/bundled-test-harness/assets/search-entries-test-3DlqGPQq.js +1486 -0
  428. package/bundled-test-harness/assets/search-entries-tool-test-D1TvDE9D.js +504 -0
  429. package/bundled-test-harness/assets/search-google-images-test-DqsoQGr1.js +166 -0
  430. package/bundled-test-harness/assets/search-results-DGrNaPxh.js +266 -0
  431. package/bundled-test-harness/assets/search-results-test-CqVHYNRt.js +578 -0
  432. package/bundled-test-harness/assets/search-selection-menu-test-D6bIjKiH.js +108 -0
  433. package/bundled-test-harness/assets/search-sheet-files-test-Bf37TQXo.js +126 -0
  434. package/bundled-test-harness/assets/search-sheet-persistence-test-CV4_XdxR.js +131 -0
  435. package/bundled-test-harness/assets/search-test-Dlb-wg3a.js +2249 -0
  436. package/bundled-test-harness/assets/search-tool-test-BJLgVMoL.js +156 -0
  437. package/bundled-test-harness/assets/searchable-option-test-CcbfMqIL.js +322 -0
  438. package/bundled-test-harness/assets/searchable-search-doc-test-yH9fLGNS.js +1271 -0
  439. package/bundled-test-harness/assets/section-rail-CNFCebtT.js +113 -0
  440. package/bundled-test-harness/assets/section-rail-test-KNJAWWl6.js +256 -0
  441. package/bundled-test-harness/assets/secure-sign-in-Dyljp0Th.js +174 -0
  442. package/bundled-test-harness/assets/secure-sign-in-test-QnqNuTP3.js +373 -0
  443. package/bundled-test-harness/assets/segmented-control-DxJdoJ4Z.js +173 -0
  444. package/bundled-test-harness/assets/select-BiQTg2ts.js +175 -0
  445. package/bundled-test-harness/assets/send-ai-assistant-message-test-CAn7M2dZ.js +52 -0
  446. package/bundled-test-harness/assets/send-bot-trigger-event-test-qBu5E_jy.js +56 -0
  447. package/bundled-test-harness/assets/send-request-via-proxy-test-CLuwLBq_.js +380 -0
  448. package/bundled-test-harness/assets/sending-test-H0AaBSgi.js +659 -0
  449. package/bundled-test-harness/assets/serialization-rri-form-audit-test-hMJukiKt.js +136 -0
  450. package/bundled-test-harness/assets/serialization-test-W5e3wKbz.js +8758 -0
  451. package/bundled-test-harness/assets/set-user-system-card-test-CjgWLkNV.js +59 -0
  452. package/bundled-test-harness/assets/setup-CssMNvui.js +772 -0
  453. package/bundled-test-harness/assets/setup-D8YXvK-p.js +2 -0
  454. package/bundled-test-harness/assets/shared-link-target-reuse-test-D42UO8DC.js +207 -0
  455. package/bundled-test-harness/assets/show-card-test-CHEaS514.js +365 -0
  456. package/bundled-test-harness/assets/signed-capture-link-test-BgEu0F-S.js +132 -0
  457. package/bundled-test-harness/assets/single-stack-test-DYcu_v7Z.js +652 -0
  458. package/bundled-test-harness/assets/skills-test-ChmKhADX.js +648 -0
  459. package/bundled-test-harness/assets/skip-boot-index-guard-test-BzRIncDN.js +46 -0
  460. package/bundled-test-harness/assets/spec-test-BYvFR6Xb.js +1077 -0
  461. package/bundled-test-harness/assets/spinner-Bx9x55NA.js +49 -0
  462. package/bundled-test-harness/assets/src-C1hYFgVl.js +216 -0
  463. package/bundled-test-harness/assets/src-CIlLx7Lu.js +5 -0
  464. package/bundled-test-harness/assets/stick-to-bottom-resize-test-DMHsAG0v.js +56 -0
  465. package/bundled-test-harness/assets/store-add-test-D_9IHQjZ.js +50 -0
  466. package/bundled-test-harness/assets/store-module-rebuild-test-BRGZoLRV.js +454 -0
  467. package/bundled-test-harness/assets/store-search-test-3G8JHuj2.js +175 -0
  468. package/bundled-test-harness/assets/store-test-BiKKDqqW.js +3672 -0
  469. package/bundled-test-harness/assets/structured-theme-test-11TNsgUW.js +377 -0
  470. package/bundled-test-harness/assets/summarize-session-test-CwzD_ZbD.js +172 -0
  471. package/bundled-test-harness/assets/svg-image-def-test-zV6aMK9q.js +167 -0
  472. package/bundled-test-harness/assets/switch-submode-test-DQU2cP5_.js +168 -0
  473. package/bundled-test-harness/assets/sync-openrouter-models-test-xrXVaC2H.js +97 -0
  474. package/bundled-test-harness/assets/test-support-BGl3rthw.js +3 -0
  475. package/bundled-test-harness/assets/test-support-BcBv__98.js +99 -0
  476. package/bundled-test-harness/assets/tests-CmM9imwB.js +3598 -0
  477. package/bundled-test-harness/assets/text-file-def-test-Cyq78UFY.js +94 -0
  478. package/bundled-test-harness/assets/text-input-validator-test-DBj7TaM_.js +156 -0
  479. package/bundled-test-harness/assets/text-suggestion-test-C4XAqWqY.js +340 -0
  480. package/bundled-test-harness/assets/theme-card-test-dVNz4tgU.js +621 -0
  481. package/bundled-test-harness/assets/theme-dashboard-nav-test-DYSn5pqo.js +196 -0
  482. package/bundled-test-harness/assets/theme-scheme-island-test-C1WpkfEP.js +264 -0
  483. package/bundled-test-harness/assets/theme-scoped-css-parity-test-BVluppsE.js +176 -0
  484. package/bundled-test-harness/assets/titles-DCvAhECF.js +52 -0
  485. package/bundled-test-harness/assets/titles-ZPeArtS1.js +2 -0
  486. package/bundled-test-harness/assets/to-device-streaming-test-BJ4J1cXe.js +163 -0
  487. package/bundled-test-harness/assets/token-usage-test-WUf4r4fQ.js +160 -0
  488. package/bundled-test-harness/assets/tools-J_Knr_W6.js +774 -0
  489. package/bundled-test-harness/assets/tools-calling-test-CfqZX0Ed.js +200 -0
  490. package/bundled-test-harness/assets/tools-test-B4x58ZmI.js +1624 -0
  491. package/bundled-test-harness/assets/tools-test-Cvau5219.js +3204 -0
  492. package/bundled-test-harness/assets/tooltip-B0rEgyMW.css +66 -0
  493. package/bundled-test-harness/assets/tooltip-DvUMUgVX.js +195 -0
  494. package/bundled-test-harness/assets/transform-cards-test-CYQbZeD9.js +341 -0
  495. package/bundled-test-harness/assets/ts-file-def-preview-test-m39f8Zio.js +95 -0
  496. package/bundled-test-harness/assets/ts-file-def-test-xIcL5xWe.js +92 -0
  497. package/bundled-test-harness/assets/ttf-font-def-test-ClQ2SzFF.js +206 -0
  498. package/bundled-test-harness/assets/turn-settling-test-DZoL2mRT.js +160 -0
  499. package/bundled-test-harness/assets/turn-summary-Bmt7ZxTS.js +1039 -0
  500. package/bundled-test-harness/assets/unpublish-realm-test-0ToTJczx.js +102 -0
  501. package/bundled-test-harness/assets/update-room-skills-test-o_j8NOUV.js +240 -0
  502. package/bundled-test-harness/assets/use-ai-assistant-test-D7HFpxKJ.js +384 -0
  503. package/bundled-test-harness/assets/user-message-BAOt1hS3.js +262 -0
  504. package/bundled-test-harness/assets/validate-realm-test-e3xIbNLB.js +67 -0
  505. package/bundled-test-harness/assets/video-metadata-extractor-test-D8naLSlg.js +520 -0
  506. package/bundled-test-harness/assets/view-visually-test-BuhGau5E.js +335 -0
  507. package/bundled-test-harness/assets/virtual-network-reset-test-CgaFNGPi.js +96 -0
  508. package/bundled-test-harness/assets/wav-audio-def-test-CWG_UdCh.js +160 -0
  509. package/bundled-test-harness/assets/webp-image-def-test-sN4yw8Zu.js +226 -0
  510. package/bundled-test-harness/assets/workspace-0LfPqbYB.js +445 -0
  511. package/bundled-test-harness/assets/workspace-a11y-test-DQxYDRAP.js +197 -0
  512. package/bundled-test-harness/assets/workspace-actions-jGJSfl8M.js +588 -0
  513. package/bundled-test-harness/assets/workspace-behavior-test-BnF-bmBN.js +338 -0
  514. package/bundled-test-harness/assets/workspace-delete-multiple-test-D-rxPDuT.js +212 -0
  515. package/bundled-test-harness/assets/workspace-delete-summary-test-C2-Mve5R.js +63 -0
  516. package/bundled-test-harness/assets/workspace-hosting-test-BvDKdlOb.js +138 -0
  517. package/bundled-test-harness/assets/workspace-indexing-test-B34T3kqD.js +71 -0
  518. package/bundled-test-harness/assets/workspace-test-CJivgv-m.js +51 -0
  519. package/bundled-test-harness/assets/workspace-test-CuSRpPnQ.js +385 -0
  520. package/bundled-test-harness/assets/workspace-timestamp-labels-test-aWgjdbCp.js +68 -0
  521. package/bundled-test-harness/assets/workspace-unit-test-DHMUf2KT.js +122 -0
  522. package/bundled-test-harness/assets/write-binary-file-test-P-hVQuin.js +124 -0
  523. package/bundled-test-harness/assets/write-text-file-test-fXvJueDd.js +191 -0
  524. package/bundled-test-harness/assets/zip-archive-test-C2P5CUSR.js +285 -0
  525. package/bundled-test-harness/assets/zip-file-def-test-BfJZAmGt.js +164 -0
  526. package/bundled-test-harness/boxel-ui-checksum.txt +1 -1
  527. package/bundled-test-harness/index.html +48 -48
  528. package/bundled-test-harness/tests/index.html +50 -50
  529. package/bundled-types/boxel-ui/helpers/theme-scoped-css.ts +4 -0
  530. package/bundled-types/choreo/choreo.gts +58 -2
  531. package/bundled-types/choreo/compile.ts +5 -0
  532. package/bundled-types/choreo/layout-probe.ts +100 -0
  533. package/bundled-types/choreo/run.ts +99 -4
  534. package/bundled-types/host-app/components/operator-mode/dashboard/get-started.gts +152 -7
  535. package/bundled-types/host-app/components/operator-mode/dashboard/index.gts +2 -0
  536. package/bundled-types/host-app/components/operator-mode/dashboard/workspace.gts +5 -3
  537. package/bundled-types/host-app/components/operator-mode/dashboard-modals/workspace-modals.gts +5 -22
  538. package/bundled-types/host-app/components/operator-mode/host-submode/publishing-realm-popover.gts +1 -1
  539. package/bundled-types/host-app/components/operator-mode/host-submode.gts +111 -37
  540. package/bundled-types/host-app/components/operator-mode/motion/stack-motion.gts +92 -33
  541. package/bundled-types/host-app/components/operator-mode/publish-realm-modal.gts +1 -0
  542. package/bundled-types/host-app/components/operator-mode/stack-item.gts +2 -2
  543. package/bundled-types/host-app/components/operator-mode/stack.gts +36 -25
  544. package/bundled-types/host-app/components/operator-mode/submode-layout.gts +2 -2
  545. package/bundled-types/host-app/lib/modal-focus-trap.ts +33 -0
  546. package/bundled-types/host-app/lib/workspace-actions.ts +21 -0
  547. package/bundled-types/host-app/services/host-mode-service.ts +39 -0
  548. package/bundled-types/host-app/services/realm.ts +14 -0
  549. package/bundled-types/host-tests/helpers/dashboard.ts +67 -0
  550. package/package.json +1 -1
  551. package/bundled-test-harness/assets/account-switch-failed-test-FytUFiPO.js +0 -45
  552. package/bundled-test-harness/assets/adapter-DvmPwSZv.js +0 -2
  553. package/bundled-test-harness/assets/add-field-to-card-definition-tool-test-DN525jiY.js +0 -112
  554. package/bundled-test-harness/assets/ai-assistant-create-workspace-test-q3tKqVJQ.js +0 -130
  555. package/bundled-test-harness/assets/ai-assistant-panel-relogin-test-DgeXcCyi.js +0 -46
  556. package/bundled-test-harness/assets/ai-assistant-test-lDIGPKFP.js +0 -2223
  557. package/bundled-test-harness/assets/ai-function-generation-test-D5GbLyvL.js +0 -1176
  558. package/bundled-test-harness/assets/ai-module-creation-test-I3-j2beD.js +0 -151
  559. package/bundled-test-harness/assets/aibot-message-Dmsjifs6.js +0 -1312
  560. package/bundled-test-harness/assets/app-BS9_-DhN.js +0 -14146
  561. package/bundled-test-harness/assets/apply-markdown-edit-test-4zlwt0Db.js +0 -230
  562. package/bundled-test-harness/assets/ask-user-picker-Dr-0qpNr.js +0 -370
  563. package/bundled-test-harness/assets/ask-user-picker-test-633AFdmS.js +0 -39
  564. package/bundled-test-harness/assets/assistant-transcript-test-BgRnLvMg.js +0 -57
  565. package/bundled-test-harness/assets/audio-metadata-extractor-test-4xsQqL-0.js +0 -1176
  566. package/bundled-test-harness/assets/authed-fetch-test-663mT5_N.js +0 -73
  567. package/bundled-test-harness/assets/avif-image-def-test-h01BPIJy.js +0 -187
  568. package/bundled-test-harness/assets/basic-test-DiD6T-8Q.js +0 -139
  569. package/bundled-test-harness/assets/binary-upload-dedupe-test-CkjMPO_e.js +0 -308
  570. package/bundled-test-harness/assets/bot-registration-test-CFZHpwdj.js +0 -97
  571. package/bundled-test-harness/assets/box-test-C3pSy5ED.js +0 -50
  572. package/bundled-test-harness/assets/brand-guide-attachments-test-EoC_u7Vc.js +0 -84
  573. package/bundled-test-harness/assets/brand-guide-custom-css-test-Dn3fvblr.js +0 -196
  574. package/bundled-test-harness/assets/brand-guide-edit-test-NeiT3MyK.js +0 -140
  575. package/bundled-test-harness/assets/brand-logo-mark-field-test-C2A1EnBB.js +0 -86
  576. package/bundled-test-harness/assets/broken-link-template-test-CWn6V5zZ.js +0 -192
  577. package/bundled-test-harness/assets/bundled-base-modules-test-D7mLzpsw.js +0 -54
  578. package/bundled-test-harness/assets/bundled-base-oracle-test-I7xCId_k.js +0 -37
  579. package/bundled-test-harness/assets/buried-stack-item-scroll-test-DFBev0h-.js +0 -236
  580. package/bundled-test-harness/assets/button-BeSuhveJ.js +0 -207
  581. package/bundled-test-harness/assets/bxl-cyclic-graph-test-DPykwvvx.js +0 -47
  582. package/bundled-test-harness/assets/bxl-expression-test-LBWJiFb9.js +0 -250
  583. package/bundled-test-harness/assets/bxl-indexing-test-BuZmLk7y.js +0 -212
  584. package/bundled-test-harness/assets/bxl-platform-module-test-BNIvlkCM.js +0 -71
  585. package/bundled-test-harness/assets/can-invoke-test-t-bFIIeX.js +0 -455
  586. package/bundled-test-harness/assets/can-read-realm-test-2Q_MzRT9.js +0 -56
  587. package/bundled-test-harness/assets/cancel-indexing-job-test-QeYxEukn.js +0 -69
  588. package/bundled-test-harness/assets/capture-test-CJmJG8YR.js +0 -360
  589. package/bundled-test-harness/assets/card-api-test-B43I510H.js +0 -198
  590. package/bundled-test-harness/assets/card-basics-test-CBClzP2y.js +0 -5364
  591. package/bundled-test-harness/assets/card-chooser-paging-test-HEVzf6pR.js +0 -235
  592. package/bundled-test-harness/assets/card-chooser-test-GfjBRPFI.js +0 -393
  593. package/bundled-test-harness/assets/card-context-search-results-CoSr4EdS.js +0 -30
  594. package/bundled-test-harness/assets/card-context-search-results-test-BLMlZu9t.js +0 -184
  595. package/bundled-test-harness/assets/card-def-field-def-relationships-test-DEBOzKan.js +0 -562
  596. package/bundled-test-harness/assets/card-def-menu-items-test-88_h8o0D.js +0 -228
  597. package/bundled-test-harness/assets/card-delete-test-DKom1phy.js +0 -466
  598. package/bundled-test-harness/assets/card-error-awaiting-index-test-DEwgFZMP.js +0 -53
  599. package/bundled-test-harness/assets/card-error-r6_cXst9.js +0 -72
  600. package/bundled-test-harness/assets/card-info-thumbnail-picker-test-B7LiY15e.js +0 -219
  601. package/bundled-test-harness/assets/card-playground-test-CgyIJHui.js +0 -1491
  602. package/bundled-test-harness/assets/card-prerender-rPfzpIOU.js +0 -12939
  603. package/bundled-test-harness/assets/card-thunk-error-test-L0pvg4Ce.js +0 -48
  604. package/bundled-test-harness/assets/card-type-service-test-B2dhqxSD.js +0 -161
  605. package/bundled-test-harness/assets/check-correctness-test-DgqaLlCW.js +0 -1093
  606. package/bundled-test-harness/assets/check-domain-availability-test-B0hJHY5l.js +0 -98
  607. package/bundled-test-harness/assets/chip-D01NW3WH.js +0 -44
  608. package/bundled-test-harness/assets/choreo-D2Jl79RU.js +0 -1019
  609. package/bundled-test-harness/assets/choreo-_E8SOEe-.js +0 -2
  610. package/bundled-test-harness/assets/choreo-feature-reel-test-WLjsZDmS.js +0 -155
  611. package/bundled-test-harness/assets/choreo-gallery-build-order-test-6jnS8PsS.js +0 -436
  612. package/bundled-test-harness/assets/choreo-gallery-compositor-test-CbUZsVB6.js +0 -399
  613. package/bundled-test-harness/assets/choreo-gallery-cover-test-CbvAStgk.js +0 -214
  614. package/bundled-test-harness/assets/choreo-gallery-crossing-stage-test-tdjvUSa6.js +0 -133
  615. package/bundled-test-harness/assets/choreo-gallery-demos-alive-test-CsnM3hQK.js +0 -42
  616. package/bundled-test-harness/assets/choreo-gallery-drift-test-DG2LQTa7.js +0 -620
  617. package/bundled-test-harness/assets/choreo-gallery-escort-test-mn3-qXP1.js +0 -187
  618. package/bundled-test-harness/assets/choreo-gallery-films-test-BuktaC0D.js +0 -140
  619. package/bundled-test-harness/assets/choreo-gallery-filter-test-Dd-zpps0.js +0 -46
  620. package/bundled-test-harness/assets/choreo-gallery-fold-test-CWc1ijGc.js +0 -180
  621. package/bundled-test-harness/assets/choreo-gallery-guide-pages-test-tk2QmYPU.js +0 -114
  622. package/bundled-test-harness/assets/choreo-gallery-guides-test-DVbf1zM4.js +0 -321
  623. package/bundled-test-harness/assets/choreo-gallery-hang-test-B7EFp2dx.js +0 -426
  624. package/bundled-test-harness/assets/choreo-gallery-highlight-test-BFgbdesP.js +0 -60
  625. package/bundled-test-harness/assets/choreo-gallery-inline-edit-test-DJb8FC5R.js +0 -370
  626. package/bundled-test-harness/assets/choreo-gallery-interruption-test-DWyMXo5D.js +0 -176
  627. package/bundled-test-harness/assets/choreo-gallery-lone-stack-test-D3b8LsJ6.js +0 -104
  628. package/bundled-test-harness/assets/choreo-gallery-navigation-test-CjjU6Rvn.js +0 -193
  629. package/bundled-test-harness/assets/choreo-gallery-playhead-transport-test-Le_h3QWU.js +0 -111
  630. package/bundled-test-harness/assets/choreo-gallery-poplayout-subtree-test-Cp-DlWj4.js +0 -127
  631. package/bundled-test-harness/assets/choreo-gallery-presence-modes-test-DayS4GCJ.js +0 -100
  632. package/bundled-test-harness/assets/choreo-gallery-presentation-test-DeAfuFS7.js +0 -157
  633. package/bundled-test-harness/assets/choreo-gallery-reduced-motion-test-BgPWpggq.js +0 -145
  634. package/bundled-test-harness/assets/choreo-gallery-sheet-standalone-test-PzyoMNYv.js +0 -89
  635. package/bundled-test-harness/assets/choreo-gallery-sheet-test-CxaiNBkL.js +0 -48
  636. package/bundled-test-harness/assets/choreo-gallery-site-test-6xtSntS5.js +0 -237
  637. package/bundled-test-harness/assets/choreo-gallery-stage-E6Q5r-zU.js +0 -256
  638. package/bundled-test-harness/assets/choreo-gallery-stage-keyboard-test-C7aWMr9a.js +0 -102
  639. package/bundled-test-harness/assets/choreo-gallery-stages-test-CDWWgC61.js +0 -35
  640. package/bundled-test-harness/assets/choreo-gallery-subdivision-test-Bkp15XJv.js +0 -138
  641. package/bundled-test-harness/assets/choreo-gallery-table-plan-test-CcGRi_3O.js +0 -144
  642. package/bundled-test-harness/assets/choreo-gallery-theater-test-Qph5pLT_.js +0 -252
  643. package/bundled-test-harness/assets/choreo-gallery-tuning-test-C4DhFLVA.js +0 -95
  644. package/bundled-test-harness/assets/choreo-gallery-widget-room-test-LK493r1M.js +0 -88
  645. package/bundled-test-harness/assets/choreo-gallery-wires-test-B7SeUCc8.js +0 -148
  646. package/bundled-test-harness/assets/client-telemetry-test-DGmHzAFx.js +0 -340
  647. package/bundled-test-harness/assets/clip-BpYQPVz_.js +0 -2
  648. package/bundled-test-harness/assets/clip-CNaYbIwL.js +0 -92
  649. package/bundled-test-harness/assets/code-block-Cp2CJo5-.js +0 -502
  650. package/bundled-test-harness/assets/code-ref-test-C8JBHDb-.js +0 -163
  651. package/bundled-test-harness/assets/code-submode-availability-test-CzG1Tv6L.js +0 -198
  652. package/bundled-test-harness/assets/code-submode-test-BclVWjm5.js +0 -1839
  653. package/bundled-test-harness/assets/codeblocks-test-CmQlyxN7.js +0 -418
  654. package/bundled-test-harness/assets/codemirror-editor-test-B74H_yn8.js +0 -1512
  655. package/bundled-test-harness/assets/codemirror-embed-toolbar-test-Bz6VEysU.js +0 -366
  656. package/bundled-test-harness/assets/color-field-test-DMCPQKIP.js +0 -212
  657. package/bundled-test-harness/assets/compaction-test-saQCLBrQ.js +0 -150
  658. package/bundled-test-harness/assets/computed-test-C05HliJC.js +0 -1320
  659. package/bundled-test-harness/assets/container-Cqcwrd-m.js +0 -10857
  660. package/bundled-test-harness/assets/container-h3Wc26qn.css +0 -5097
  661. package/bundled-test-harness/assets/content-preview-components-test-DZPj4_CU.js +0 -462
  662. package/bundled-test-harness/assets/copy-and-edit-test-DiXTQNTF.js +0 -292
  663. package/bundled-test-harness/assets/copy-card-test-ys7GquUY.js +0 -218
  664. package/bundled-test-harness/assets/copy-file-to-realm-test-Gn0Wn4vT.js +0 -148
  665. package/bundled-test-harness/assets/copy-source-test-BTLrOmPm.js +0 -56
  666. package/bundled-test-harness/assets/create-file-test-BCeIW-Rt.js +0 -464
  667. package/bundled-test-harness/assets/create-file-test-DbHQ5JsS.js +0 -1086
  668. package/bundled-test-harness/assets/create-listing-modal-test-CeHc4ter.js +0 -273
  669. package/bundled-test-harness/assets/create-listing-pr-request-test-DXbJNfMh.js +0 -117
  670. package/bundled-test-harness/assets/create-spec-test-BDIZoyHT.js +0 -256
  671. package/bundled-test-harness/assets/create-submission-workflow-test-Bix5J-3A.js +0 -132
  672. package/bundled-test-harness/assets/create-workspace-test-BmNnLHwC.js +0 -104
  673. package/bundled-test-harness/assets/credential-fields-test-NK1F4QWn.js +0 -200
  674. package/bundled-test-harness/assets/csv-file-def-test-BSfs4TTP.js +0 -102
  675. package/bundled-test-harness/assets/dashboard-C4T7ffKY.js +0 -3576
  676. package/bundled-test-harness/assets/dashboard-CFeHuP5-.js +0 -81
  677. package/bundled-test-harness/assets/dashboard-DQlpW-ux.css +0 -2392
  678. package/bundled-test-harness/assets/dashboard-add-workspace-test--rr6objf.js +0 -50
  679. package/bundled-test-harness/assets/dashboard-agents-test-D1y8H4xH.js +0 -334
  680. package/bundled-test-harness/assets/dashboard-archive-test-bN4EQaXv.js +0 -128
  681. package/bundled-test-harness/assets/dashboard-delete-test-BvsASb8B.js +0 -163
  682. package/bundled-test-harness/assets/dashboard-duplicate-offer-test-DEsd5ZzZ.js +0 -71
  683. package/bundled-test-harness/assets/dashboard-duplicate-test-BnR0sTgB.js +0 -222
  684. package/bundled-test-harness/assets/dashboard-get-started-test-DG4uUXLv.js +0 -246
  685. package/bundled-test-harness/assets/dashboard-pinned-cards-test-AoGGVRAS.js +0 -220
  686. package/bundled-test-harness/assets/dashboard-reindex-test-BN9a96Xp.js +0 -115
  687. package/bundled-test-harness/assets/dashboard-section-rail-test-B3OT9V2T.js +0 -169
  688. package/bundled-test-harness/assets/dashboard-state-test-BY0Zqrmj.js +0 -162
  689. package/bundled-test-harness/assets/dashboard-test-C13oNpWt.js +0 -545
  690. package/bundled-test-harness/assets/dashboard-unreachable-notice-test-DZIV4x5j.js +0 -62
  691. package/bundled-test-harness/assets/dashboard-workspace-test-CBnOQViI.js +0 -537
  692. package/bundled-test-harness/assets/dashboard-workspaces-section-test-KU8ZVOYp.js +0 -396
  693. package/bundled-test-harness/assets/data-file-def-preview-test-CI5gcdtK.js +0 -155
  694. package/bundled-test-harness/assets/date-time-fields-test-D2CpR_Pb.js +0 -359
  695. package/bundled-test-harness/assets/debug-message-test-Df1kHesw.js +0 -117
  696. package/bundled-test-harness/assets/declared-captures-test-hQ43zuox.js +0 -879
  697. package/bundled-test-harness/assets/delete-workspace-test-BhfMKsix.js +0 -102
  698. package/bundled-test-harness/assets/diffing-test-CHRh55mC.js +0 -210
  699. package/bundled-test-harness/assets/download-file-to-realm-test-CC1HURCi.js +0 -119
  700. package/bundled-test-harness/assets/editor-test-DkeCv28f.js +0 -688
  701. package/bundled-test-harness/assets/edits-BSJqtUXM.js +0 -651
  702. package/bundled-test-harness/assets/email-field-test-DihK0suO.js +0 -75
  703. package/bundled-test-harness/assets/enum-field-test-Gja3bzt5.js +0 -935
  704. package/bundled-test-harness/assets/error-display-test-GhYZu7cp.js +0 -343
  705. package/bundled-test-harness/assets/evaluate-module-test-D_t4CiUq.js +0 -91
  706. package/bundled-test-harness/assets/execute-atomic-operations-test-xtou8Zeg.js +0 -113
  707. package/bundled-test-harness/assets/externals-B02-sD_L.js +0 -4393
  708. package/bundled-test-harness/assets/fallback-banner-live-delete-test-DtxzNXdE.js +0 -207
  709. package/bundled-test-harness/assets/fallback-banner-test-BfmiQ_68.js +0 -53
  710. package/bundled-test-harness/assets/fallback-banner-test-DLtTdmvn.js +0 -225
  711. package/bundled-test-harness/assets/fallback-models-test-BxNJxhfd.js +0 -161
  712. package/bundled-test-harness/assets/fetch-card-json-test-DofKbzvC.js +0 -61
  713. package/bundled-test-harness/assets/field-configuration-test-SUoPaXWy.js +0 -551
  714. package/bundled-test-harness/assets/field-markdown-domain-test-BwtR0dPP.js +0 -616
  715. package/bundled-test-harness/assets/field-markdown-oversize-test-D8UJcBc3.js +0 -88
  716. package/bundled-test-harness/assets/field-markdown-primitives-test-D5_ZdoDO.js +0 -365
  717. package/bundled-test-harness/assets/field-markdown-specialized-test-Dpd5hs04.js +0 -534
  718. package/bundled-test-harness/assets/field-playground-test-CzhYzKGv.js +0 -894
  719. package/bundled-test-harness/assets/field-support-predicates-test-BSvVOFth.js +0 -167
  720. package/bundled-test-harness/assets/file-attachment-test-fM-yFedm.js +0 -796
  721. package/bundled-test-harness/assets/file-chooser-panel-test-CKj8YTdY.js +0 -153
  722. package/bundled-test-harness/assets/file-chooser-test-Bs8jFXqa.js +0 -504
  723. package/bundled-test-harness/assets/file-def-format-templates-test-CxrRoWoD.js +0 -380
  724. package/bundled-test-harness/assets/file-def-live-reload-test-X_8YEkve.js +0 -124
  725. package/bundled-test-harness/assets/file-def-menu-items-test-Dx879o_o.js +0 -138
  726. package/bundled-test-harness/assets/file-def-navigation-test-MMI_EEO1.js +0 -109
  727. package/bundled-test-harness/assets/file-def-serialization-test-B5lguBQD.js +0 -89
  728. package/bundled-test-harness/assets/file-def-test-Di9QWcrj.js +0 -216
  729. package/bundled-test-harness/assets/file-preview-live-reload-test-aqx93A7c.js +0 -310
  730. package/bundled-test-harness/assets/file-resource-primitives-test-a5abpcSB.js +0 -255
  731. package/bundled-test-harness/assets/file-tree-from-index-test-CnruGxJ8.js +0 -96
  732. package/bundled-test-harness/assets/file-tree-test-BCyWDdSE.js +0 -851
  733. package/bundled-test-harness/assets/file-view-model-test-BfpLa01Y.js +0 -346
  734. package/bundled-test-harness/assets/filedef-export-shape-test-CQ9XbrzS.js +0 -61
  735. package/bundled-test-harness/assets/film-CL1zj_bJ.js +0 -2
  736. package/bundled-test-harness/assets/film-Dq_quZ31.js +0 -13
  737. package/bundled-test-harness/assets/film-xbkQa060.js +0 -4074
  738. package/bundled-test-harness/assets/flac-audio-def-test-BmVp9dMx.js +0 -143
  739. package/bundled-test-harness/assets/focus-Dm4wkM72.js +0 -306
  740. package/bundled-test-harness/assets/font-metadata-extractor-test-OWEh9_MS.js +0 -415
  741. package/bundled-test-harness/assets/formatted-aibot-message-test-BpHsWpuV.js +0 -109
  742. package/bundled-test-harness/assets/formatted-user-message-test-DZW5TAr0.js +0 -43
  743. package/bundled-test-harness/assets/freestyle-test-D_uaPU_3.js +0 -17
  744. package/bundled-test-harness/assets/full-reindex-realm-test-C6vHL5kC.js +0 -101
  745. package/bundled-test-harness/assets/garbage-collection-test-D-Ao4t2n.js +0 -403
  746. package/bundled-test-harness/assets/general-test-PSGI8W0z.js +0 -923
  747. package/bundled-test-harness/assets/generate-example-cards-one-shot-test-J-m-0E6W.js +0 -65
  748. package/bundled-test-harness/assets/generic-file-def-preview-test-BAsOhTYU.js +0 -71
  749. package/bundled-test-harness/assets/get-available-realm-identifiers-test-D5PBOr97.js +0 -47
  750. package/bundled-test-harness/assets/get-catalog-realm-identifiers-test-NM9UURHE.js +0 -47
  751. package/bundled-test-harness/assets/get-default-writable-realm-test-BxXc9a3J.js +0 -54
  752. package/bundled-test-harness/assets/get-published-realms-test-C8TJEc4-.js +0 -71
  753. package/bundled-test-harness/assets/get-realm-of-resource-identifier-test-Dt8CB6lS.js +0 -54
  754. package/bundled-test-harness/assets/get-relationship-membership-state-test-9_8x3myJ.js +0 -1308
  755. package/bundled-test-harness/assets/get-user-system-card-test-k0UO-uba.js +0 -67
  756. package/bundled-test-harness/assets/gif-image-def-test-BWrqPGDX.js +0 -188
  757. package/bundled-test-harness/assets/glimmer-motion-and-choreo-in-cards-test-BgG8QI9E.js +0 -160
  758. package/bundled-test-harness/assets/glimmer-motion-and-choreo-test-BKOvNyCN.js +0 -74
  759. package/bundled-test-harness/assets/gts-file-def-test-BCRHwDeZ.js +0 -100
  760. package/bundled-test-harness/assets/head-format-preview-test-DnfUhniI.js +0 -158
  761. package/bundled-test-harness/assets/helpers-DklkegO_.js +0 -5
  762. package/bundled-test-harness/assets/host-mode-service-vOfN0smp.js +0 -232
  763. package/bundled-test-harness/assets/host-mode-test-CCCJFz6_.js +0 -831
  764. package/bundled-test-harness/assets/host-submode-test-CtCfbuPA.js +0 -1342
  765. package/bundled-test-harness/assets/html-component-test-CWyhb5ZX.js +0 -65
  766. package/bundled-test-harness/assets/html-def-test-Cb8C2lUx.js +0 -160
  767. package/bundled-test-harness/assets/html-meta-extractor-test-BHT9Rtir.js +0 -60
  768. package/bundled-test-harness/assets/hydratable-card-test-DpgpdesD.js +0 -426
  769. package/bundled-test-harness/assets/icon-button-ABCGVF3C.js +0 -67
  770. package/bundled-test-harness/assets/image-metadata-extractor-test-ClhPTouq.js +0 -1109
  771. package/bundled-test-harness/assets/index-changes-test-CgnVr3LH.js +0 -367
  772. package/bundled-test-harness/assets/index-query-engine-test-CQTwju5_.js +0 -2593
  773. package/bundled-test-harness/assets/index-writer-test-IoVOoZT2.js +0 -3040
  774. package/bundled-test-harness/assets/inspector-test-BN2Ieb77.js +0 -2058
  775. package/bundled-test-harness/assets/instance-filter-matcher-test-Bq-_Sy89.js +0 -788
  776. package/bundled-test-harness/assets/instantiate-card-test-DEeDTzFV.js +0 -164
  777. package/bundled-test-harness/assets/interact-submode-creation-and-permissions-test-DP6kOyJS.js +0 -475
  778. package/bundled-test-harness/assets/interact-submode-setup-B0_cHqH-.js +0 -566
  779. package/bundled-test-harness/assets/invalidate-realm-identifiers-test-9uUD91Pw.js +0 -99
  780. package/bundled-test-harness/assets/invite-user-to-room-test-CcDA45Ac.js +0 -142
  781. package/bundled-test-harness/assets/invoke-card-operation-test-DmUtgueM.js +0 -333
  782. package/bundled-test-harness/assets/isolated-render-test-B2eXNHu6.js +0 -51
  783. package/bundled-test-harness/assets/job-scoped-doc-cache-test-DF9D7cOc.js +0 -181
  784. package/bundled-test-harness/assets/job-scoped-instance-residency-test-CSS8xUU5.js +0 -103
  785. package/bundled-test-harness/assets/jpg-image-def-test-voECMTht.js +0 -697
  786. package/bundled-test-harness/assets/json-file-def-test-8Y4KLjUC.js +0 -95
  787. package/bundled-test-harness/assets/key-notices-badge-test-d1mYqx0e.js +0 -155
  788. package/bundled-test-harness/assets/key-readiness-test-BtCg7PCk.js +0 -190
  789. package/bundled-test-harness/assets/linksto-broken-link-placeholder-test-CyCMbJnN.js +0 -398
  790. package/bundled-test-harness/assets/linksto-error-sentinel-test-BDW572-v.js +0 -238
  791. package/bundled-test-harness/assets/linksto-many-broken-link-placeholder-test-B4wM97hD.js +0 -354
  792. package/bundled-test-harness/assets/linksto-many-per-slot-access-test-BhJmtJ0_.js +0 -303
  793. package/bundled-test-harness/assets/linksto-sentinel-reload-test-BpBQ5V_i.js +0 -197
  794. package/bundled-test-harness/assets/linksto-sentinel-roundtrip-test-pkBBl4e9.js +0 -260
  795. package/bundled-test-harness/assets/linksto-singular-js-access-contract-test-Bb6wjUdD.js +0 -196
  796. package/bundled-test-harness/assets/lint-and-fix-test-CcMLn-co.js +0 -51
  797. package/bundled-test-harness/assets/live-test-AyBnq7BX.js +0 -2
  798. package/bundled-test-harness/assets/live-test-CgnAcIDw.js +0 -100
  799. package/bundled-test-harness/assets/load-default-skills-test-BIOwtx0Y.js +0 -74
  800. package/bundled-test-harness/assets/loader-parallel-prefetch-test-D5_oRSER.js +0 -257
  801. package/bundled-test-harness/assets/loader-test-DSc_jR1c.js +0 -482
  802. package/bundled-test-harness/assets/loading-test-8pldx7S-.js +0 -63
  803. package/bundled-test-harness/assets/m4a-audio-def-test-BDI5apzM.js +0 -190
  804. package/bundled-test-harness/assets/main-5kHyCkdm.js +0 -5
  805. package/bundled-test-harness/assets/markdown-embed-chooser-modal-test-Wud0s7WZ.js +0 -470
  806. package/bundled-test-harness/assets/markdown-embed-chooser-test-DVzzS-0C.js +0 -334
  807. package/bundled-test-harness/assets/markdown-embed-preview-pane-test-B5W_iqsi.js +0 -447
  808. package/bundled-test-harness/assets/markdown-embed-preview-test-CZF_s0fB.js +0 -368
  809. package/bundled-test-harness/assets/markdown-fallback-test-fT6BS4W7.js +0 -316
  810. package/bundled-test-harness/assets/markdown-file-def-test-B1aTzazR.js +0 -380
  811. package/bundled-test-harness/assets/markdown-helpers-test-gNKZf7Mw.js +0 -188
  812. package/bundled-test-harness/assets/markdown-highlighting-test-Du5_CQmD.js +0 -244
  813. package/bundled-test-harness/assets/markdown-preview-h-_aJhRc.js +0 -72
  814. package/bundled-test-harness/assets/markdown-preview-test-DJvoVsXw.js +0 -238
  815. package/bundled-test-harness/assets/markdown-skill-frontmatter-test-VwJi3Paj.js +0 -146
  816. package/bundled-test-harness/assets/markdown-skill-search-test-ysCdf0xM.js +0 -80
  817. package/bundled-test-harness/assets/matrix-service-account-switch-sessions-test-K2JuMy36.js +0 -63
  818. package/bundled-test-harness/assets/matrix-service-boot-assembly-test-CkdL_-os.js +0 -300
  819. package/bundled-test-harness/assets/matrix-service-personal-realm-test-Dy1DultK.js +0 -125
  820. package/bundled-test-harness/assets/matrix-service-realm-servers-test-DHDDKuEg.js +0 -55
  821. package/bundled-test-harness/assets/matrix-service-realms-list-updated-test-DcTbZpMl.js +0 -132
  822. package/bundled-test-harness/assets/matrix-service-relogin-test-D3NQktoa.js +0 -59
  823. package/bundled-test-harness/assets/matrix-service-sliding-sync-test-6CAflWOA.js +0 -35
  824. package/bundled-test-harness/assets/menu-kUSaJIPq.js +0 -3562
  825. package/bundled-test-harness/assets/message-service-subscription-test-DaQdgDWi.js +0 -110
  826. package/bundled-test-harness/assets/message-service-test-BsGEgM_m.js +0 -144
  827. package/bundled-test-harness/assets/message-tool-test-BB_4kw1V.js +0 -33
  828. package/bundled-test-harness/assets/midi-audio-def-test-BbbkFjqf.js +0 -182
  829. package/bundled-test-harness/assets/migrate-skill-test-X9xImDuq.js +0 -161
  830. package/bundled-test-harness/assets/mini-AwF31elJ.js +0 -1404
  831. package/bundled-test-harness/assets/mini-card-chooser-test-BZtrEiWu.js +0 -272
  832. package/bundled-test-harness/assets/mini-file-chooser-test-MIHW3kCI.js +0 -201
  833. package/bundled-test-harness/assets/mock-matrix-Dd8k0DNa.js +0 -15107
  834. package/bundled-test-harness/assets/mock-matrix-DhPN_34j.js +0 -2
  835. package/bundled-test-harness/assets/modal-LTh3_oHK.js +0 -322
  836. package/bundled-test-harness/assets/model-cost-tier-test-h1IkU9mp.js +0 -174
  837. package/bundled-test-harness/assets/model-file-extract-test-ntSo0wZF.js +0 -206
  838. package/bundled-test-harness/assets/module-transpile-cache-test-CDeRaaeP.js +0 -67
  839. package/bundled-test-harness/assets/monaco-test-C0Fa744D.js +0 -100
  840. package/bundled-test-harness/assets/mp3-audio-def-test-B4j4qR9H.js +0 -133
  841. package/bundled-test-harness/assets/multiple-stacks-test-BhA25ywL.js +0 -208
  842. package/bundled-test-harness/assets/nested-query-field-render-test-S9Y8YNKB.js +0 -249
  843. package/bundled-test-harness/assets/no-stacks-test-B7VciWyg.js +0 -48
  844. package/bundled-test-harness/assets/number-field-test-CS74lDPN.js +0 -184
  845. package/bundled-test-harness/assets/office-file-def-test-2cKc1pmd.js +0 -263
  846. package/bundled-test-harness/assets/office-metadata-extractor-test-DC8Fo-OW.js +0 -218
  847. package/bundled-test-harness/assets/office-poster-capture-test-Ba0_5UrT.js +0 -203
  848. package/bundled-test-harness/assets/ogg-audio-def-test-CIPQ1d4w.js +0 -196
  849. package/bundled-test-harness/assets/open-create-listing-modal-test-bmQkY5Zl.js +0 -131
  850. package/bundled-test-harness/assets/open-image-in-new-tab-test-DBD7_vMH.js +0 -52
  851. package/bundled-test-harness/assets/open-secure-window-test-BgeuYAqz.js +0 -253
  852. package/bundled-test-harness/assets/open-workspace-test-DRHGljV0.js +0 -46
  853. package/bundled-test-harness/assets/operation-lowering-test-CH1c0pSH.js +0 -2957
  854. package/bundled-test-harness/assets/operations-invocation-test-B1YkCLrv.js +0 -523
  855. package/bundled-test-harness/assets/operations-optimistic-test-DAbtOwsC.js +0 -203
  856. package/bundled-test-harness/assets/operations-query-test-9p1ZIDe-.js +0 -459
  857. package/bundled-test-harness/assets/operations-store-reconciliation-test-D0EQh3w2.js +0 -406
  858. package/bundled-test-harness/assets/operations-test-BQhlrfe3.js +0 -2518
  859. package/bundled-test-harness/assets/operator-mode-acceptance-test-DtHnonEb.js +0 -1195
  860. package/bundled-test-harness/assets/operator-mode-card-chooser-test-DQUxZxaq.js +0 -1036
  861. package/bundled-test-harness/assets/operator-mode-links-test-BJ26HllS.js +0 -797
  862. package/bundled-test-harness/assets/operator-mode-test-DIPDI0qt.js +0 -696
  863. package/bundled-test-harness/assets/operator-mode-ui-test-CfA-fAh4.js +0 -1045
  864. package/bundled-test-harness/assets/overlay-DecPh3nt.js +0 -123
  865. package/bundled-test-harness/assets/overlay-menu-items-test-C6XqjLtu.js +0 -331
  866. package/bundled-test-harness/assets/overlays-Ctp8ePzZ.js +0 -75
  867. package/bundled-test-harness/assets/overlays-CzWpSg5T.js +0 -2
  868. package/bundled-test-harness/assets/overlays-detached-element-test-DvL_8Bb5.js +0 -56
  869. package/bundled-test-harness/assets/panel-CImumikB.js +0 -15124
  870. package/bundled-test-harness/assets/past-sessions-test-N6riGN_I.js +0 -321
  871. package/bundled-test-harness/assets/patch-fields-test-D30_-KwQ.js +0 -1014
  872. package/bundled-test-harness/assets/patch-instance-test-C5rGscZq.js +0 -348
  873. package/bundled-test-harness/assets/pdf-file-def-test-DtiqGRvZ.js +0 -111
  874. package/bundled-test-harness/assets/pdf-metadata-extractor-test-FU02KmdV.js +0 -130
  875. package/bundled-test-harness/assets/permission-checking-realm-test-DqdNJDwU.js +0 -306
  876. package/bundled-test-harness/assets/persist-module-inspector-view-test-BHOAgPVy.js +0 -60
  877. package/bundled-test-harness/assets/persist-scroll-position-test-qtmS518r.js +0 -254
  878. package/bundled-test-harness/assets/phone-number-field-test-DhdaM7uL.js +0 -73
  879. package/bundled-test-harness/assets/pill-menu-test-C9Mrrepl.js +0 -60
  880. package/bundled-test-harness/assets/plate-9n-sXyCw.js +0 -41
  881. package/bundled-test-harness/assets/plate-BnZcAk-_.js +0 -2
  882. package/bundled-test-harness/assets/player-Cu5b4LEJ.js +0 -82
  883. package/bundled-test-harness/assets/player-unk2mkxV.js +0 -2
  884. package/bundled-test-harness/assets/playground-BwA8wFGK.js +0 -58
  885. package/bundled-test-harness/assets/plural-field-components-test-CFNQKETg.js +0 -198
  886. package/bundled-test-harness/assets/png-image-def-test-DG5Vhlqb.js +0 -266
  887. package/bundled-test-harness/assets/policy-audit-confirmation-test-yfKU47LR.js +0 -155
  888. package/bundled-test-harness/assets/policy-scoped-search-test-BjQRDYqR.js +0 -339
  889. package/bundled-test-harness/assets/prerender-file-extract-test-CEU4SaAZ.js +0 -346
  890. package/bundled-test-harness/assets/prerender-html-test-CGB3ClCS.js +0 -670
  891. package/bundled-test-harness/assets/prerender-meta-test-xqc4fW_0.js +0 -614
  892. package/bundled-test-harness/assets/prerender-module-test-CI1TmZw-.js +0 -409
  893. package/bundled-test-harness/assets/prerender-persistence-block-test-Da0dJO_N.js +0 -124
  894. package/bundled-test-harness/assets/prerender-pre-model-error-test-BVLyhk8R.js +0 -61
  895. package/bundled-test-harness/assets/prerender-serialize-cycle-test-glJFfxHU.js +0 -134
  896. package/bundled-test-harness/assets/prerendered-html-read-test-Mp4L0VHQ.js +0 -191
  897. package/bundled-test-harness/assets/pretui-components-test-BBpA9UnA.js +0 -991
  898. package/bundled-test-harness/assets/pretui-components-test-BqFq7rw_.css +0 -730
  899. package/bundled-test-harness/assets/preview-format-test-C55Bwcur.js +0 -103
  900. package/bundled-test-harness/assets/preview-test-CTAhRGU8.js +0 -474
  901. package/bundled-test-harness/assets/process-card-test-UjqpuxB7.js +0 -94
  902. package/bundled-test-harness/assets/profile-settings-modal-C8bCgTMK.js +0 -989
  903. package/bundled-test-harness/assets/publish-realm-ixUB4FPK.js +0 -82
  904. package/bundled-test-harness/assets/publish-realm-test-CrrsdL4W.js +0 -210
  905. package/bundled-test-harness/assets/query-field-barrier-test-DjivFE93.js +0 -113
  906. package/bundled-test-harness/assets/query-field-error-sentinel-test-0HwnV4Mw.js +0 -201
  907. package/bundled-test-harness/assets/query-field-membership-status-test-C8yTTBkc.js +0 -323
  908. package/bundled-test-harness/assets/query-field-page-optin-test-D4l_GcII.js +0 -126
  909. package/bundled-test-harness/assets/query-field-schema-test-DDbcNNm5.js +0 -231
  910. package/bundled-test-harness/assets/query-field-search-throttle-test-CXp50Ax0.js +0 -154
  911. package/bundled-test-harness/assets/query-field-stale-prerender-test-DErzt-Cv.js +0 -152
  912. package/bundled-test-harness/assets/query-fields-test-Dz67fdSl.js +0 -535
  913. package/bundled-test-harness/assets/quick-look-test-B1ijDsh_.js +0 -139
  914. package/bundled-test-harness/assets/read-card-for-ai-assistant-test-DJxZ16H_.js +0 -91
  915. package/bundled-test-harness/assets/read-file-for-ai-assistant-test-BoRQkZ8c.js +0 -45
  916. package/bundled-test-harness/assets/read-source-test-Bl-NjchO.js +0 -67
  917. package/bundled-test-harness/assets/read-text-file-test-CP37CZoo.js +0 -124
  918. package/bundled-test-harness/assets/realm-CaK5mkT-.js +0 -2852
  919. package/bundled-test-harness/assets/realm-api-keys-BNUFWbGR.js +0 -579
  920. package/bundled-test-harness/assets/realm-api-keys-CkshxhXC.js +0 -2
  921. package/bundled-test-harness/assets/realm-api-keys-test-BP8xifjn.js +0 -109
  922. package/bundled-test-harness/assets/realm-api-keys-test-CwdK9YXp.js +0 -588
  923. package/bundled-test-harness/assets/realm-config-iKwE3Htp.js +0 -833
  924. package/bundled-test-harness/assets/realm-config-policy-standing-test-BgVEIDnB.js +0 -290
  925. package/bundled-test-harness/assets/realm-config-routing-rule-edit-test-BoJoKzmX.js +0 -274
  926. package/bundled-test-harness/assets/realm-config-settings-test-Kxw-GHye.js +0 -187
  927. package/bundled-test-harness/assets/realm-field-test-CdGOxrFb.js +0 -100
  928. package/bundled-test-harness/assets/realm-indexing-test-CHtsBV2b.js +0 -4380
  929. package/bundled-test-harness/assets/realm-policy-fitted-test--DhNZSdC.js +0 -206
  930. package/bundled-test-harness/assets/realm-policy-test-D7714iVa.js +0 -1141
  931. package/bundled-test-harness/assets/realm-querying-test-Cj1ekZ0H.js +0 -1067
  932. package/bundled-test-harness/assets/realm-server-form-agnostic-test-CZFPNJfD.js +0 -96
  933. package/bundled-test-harness/assets/realm-server-subscriber-test-C86WmBp0.js +0 -46
  934. package/bundled-test-harness/assets/realm-service-info-test-CdgcwKDR.js +0 -67
  935. package/bundled-test-harness/assets/realm-test-DPoLsvKz.js +0 -2364
  936. package/bundled-test-harness/assets/realm-token-refresh-test-Ch35Siwi.js +0 -153
  937. package/bundled-test-harness/assets/realm-url-tooltip-test-x8xWOENK.js +0 -78
  938. package/bundled-test-harness/assets/reasoning-test-BxpE8Jvf.js +0 -221
  939. package/bundled-test-harness/assets/recent-files-test-DTmEzPLq.js +0 -450
  940. package/bundled-test-harness/assets/reindex-realm-test-BQ8KULn4.js +0 -117
  941. package/bundled-test-harness/assets/relationship-loading-state-test-BJoArPWf.js +0 -290
  942. package/bundled-test-harness/assets/remix-card-test-DsTA1KoG.js +0 -58
  943. package/bundled-test-harness/assets/rendered-markdown-DvukulDO.js +0 -292
  944. package/bundled-test-harness/assets/rendered-markdown-test-DxGptXSV.js +0 -587
  945. package/bundled-test-harness/assets/resident-instance-reuse-test-ZWnVu8uj.js +0 -82
  946. package/bundled-test-harness/assets/result-tile-CPyI6Vmv.js +0 -144
  947. package/bundled-test-harness/assets/rich-markdown-field-test-CdygoXIO.js +0 -1647
  948. package/bundled-test-harness/assets/room-message-test-D3NfMqH0.js +0 -484
  949. package/bundled-test-harness/assets/run-D_Px28eF.js +0 -3956
  950. package/bundled-test-harness/assets/run-realm-code-qWCZxmJ7.js +0 -2964
  951. package/bundled-test-harness/assets/run-realm-code-test-UzyeEJq6.js +0 -3844
  952. package/bundled-test-harness/assets/sanitize-module-list-test-Babq44bP.js +0 -85
  953. package/bundled-test-harness/assets/save-card-skip-index-wait-test-BcELXlQ_.js +0 -89
  954. package/bundled-test-harness/assets/schema-editor-test-CEvruZbt.js +0 -785
  955. package/bundled-test-harness/assets/schema-generation-test-hYAMdCrn.js +0 -28
  956. package/bundled-test-harness/assets/scrolling-test-DVIsh-_1.js +0 -412
  957. package/bundled-test-harness/assets/search-adorn-test-CkVhqFd0.js +0 -136
  958. package/bundled-test-harness/assets/search-and-choose-test-DUhOB3FV.js +0 -154
  959. package/bundled-test-harness/assets/search-correlation-id-test-Drn4r26N.js +0 -139
  960. package/bundled-test-harness/assets/search-entries-result-test-DTCRJ_wc.js +0 -234
  961. package/bundled-test-harness/assets/search-entries-test-CE0B47pw.js +0 -1486
  962. package/bundled-test-harness/assets/search-entries-tool-test-C4aG0ckM.js +0 -504
  963. package/bundled-test-harness/assets/search-google-images-test-LyhcB2f3.js +0 -166
  964. package/bundled-test-harness/assets/search-results-D4pSne0x.js +0 -266
  965. package/bundled-test-harness/assets/search-results-test-CRZHhqhn.js +0 -578
  966. package/bundled-test-harness/assets/search-selection-menu-test-B7Y3E_nj.js +0 -108
  967. package/bundled-test-harness/assets/search-sheet-files-test-C2y534g8.js +0 -126
  968. package/bundled-test-harness/assets/search-sheet-persistence-test-DePC59Bk.js +0 -131
  969. package/bundled-test-harness/assets/search-test-Dy8NxFkU.js +0 -2249
  970. package/bundled-test-harness/assets/search-tool-test-lctDaMH3.js +0 -156
  971. package/bundled-test-harness/assets/searchable-option-test-CE64M8oM.js +0 -322
  972. package/bundled-test-harness/assets/searchable-search-doc-test-C6u3mVvT.js +0 -1271
  973. package/bundled-test-harness/assets/section-rail-B_UQX00B.js +0 -113
  974. package/bundled-test-harness/assets/section-rail-test-DIda7MIw.js +0 -256
  975. package/bundled-test-harness/assets/secure-sign-in-CqqqfQG8.js +0 -174
  976. package/bundled-test-harness/assets/secure-sign-in-test-DimbVr_W.js +0 -373
  977. package/bundled-test-harness/assets/segmented-control-iePxSdTS.js +0 -173
  978. package/bundled-test-harness/assets/select-tNs6_xwW.js +0 -175
  979. package/bundled-test-harness/assets/send-ai-assistant-message-test-2GVrMF9Z.js +0 -52
  980. package/bundled-test-harness/assets/send-bot-trigger-event-test-B_3dKfGN.js +0 -56
  981. package/bundled-test-harness/assets/send-request-via-proxy-test-_clNWyYc.js +0 -380
  982. package/bundled-test-harness/assets/sending-test-DCPFXUVg.js +0 -659
  983. package/bundled-test-harness/assets/serialization-rri-form-audit-test-BI0zCs8Y.js +0 -136
  984. package/bundled-test-harness/assets/serialization-test-bjdtVlZL.js +0 -8758
  985. package/bundled-test-harness/assets/set-user-system-card-test-BUv1vL08.js +0 -59
  986. package/bundled-test-harness/assets/setup-BW1mmC7C.js +0 -772
  987. package/bundled-test-harness/assets/setup-DsIcFT-T.js +0 -2
  988. package/bundled-test-harness/assets/shared-link-target-reuse-test-9NeJEjkb.js +0 -207
  989. package/bundled-test-harness/assets/show-card-test-mfyLwusD.js +0 -365
  990. package/bundled-test-harness/assets/signed-capture-link-test-BOtKNCAM.js +0 -132
  991. package/bundled-test-harness/assets/single-stack-test-B4MGjaLk.js +0 -652
  992. package/bundled-test-harness/assets/skills-test-BuP-Yzk1.js +0 -648
  993. package/bundled-test-harness/assets/skip-boot-index-guard-test-Cmrx2-K7.js +0 -46
  994. package/bundled-test-harness/assets/spec-test-BhZDdhVQ.js +0 -1077
  995. package/bundled-test-harness/assets/spinner-CpIBUG9m.js +0 -49
  996. package/bundled-test-harness/assets/src-DGHttmW-.js +0 -216
  997. package/bundled-test-harness/assets/src-DaNDSbp7.js +0 -5
  998. package/bundled-test-harness/assets/stick-to-bottom-resize-test-Dpy3OVum.js +0 -56
  999. package/bundled-test-harness/assets/store-add-test-_IYS8o74.js +0 -50
  1000. package/bundled-test-harness/assets/store-module-rebuild-test-B5VMaICy.js +0 -454
  1001. package/bundled-test-harness/assets/store-search-test-C2VZabrH.js +0 -175
  1002. package/bundled-test-harness/assets/store-test-D6PObxoD.js +0 -3672
  1003. package/bundled-test-harness/assets/structured-theme-test-BHjVeMf-.js +0 -377
  1004. package/bundled-test-harness/assets/summarize-session-test-BFtTuhe0.js +0 -172
  1005. package/bundled-test-harness/assets/svg-image-def-test-BL6lkWUX.js +0 -167
  1006. package/bundled-test-harness/assets/switch-submode-test-6Uv3bZMl.js +0 -168
  1007. package/bundled-test-harness/assets/sync-openrouter-models-test-Dp9E05tL.js +0 -97
  1008. package/bundled-test-harness/assets/test-support-DYm8XZ0E.js +0 -3
  1009. package/bundled-test-harness/assets/test-support-DfnhiIMV.js +0 -99
  1010. package/bundled-test-harness/assets/tests-CZk0Iag2.js +0 -3596
  1011. package/bundled-test-harness/assets/text-file-def-test-CHdC0yIU.js +0 -94
  1012. package/bundled-test-harness/assets/text-input-validator-test-B9ZR8XPU.js +0 -156
  1013. package/bundled-test-harness/assets/text-suggestion-test-CGLspKFQ.js +0 -340
  1014. package/bundled-test-harness/assets/theme-card-test-CXZvSEvG.js +0 -621
  1015. package/bundled-test-harness/assets/theme-dashboard-nav-test-CkEE0ILk.js +0 -196
  1016. package/bundled-test-harness/assets/theme-scheme-island-test-URXTjipF.js +0 -264
  1017. package/bundled-test-harness/assets/titles-C6enAdIv.js +0 -2
  1018. package/bundled-test-harness/assets/titles-Dft1HoUT.js +0 -52
  1019. package/bundled-test-harness/assets/to-device-streaming-test-D33ZOc9o.js +0 -163
  1020. package/bundled-test-harness/assets/token-usage-test-BHQlU4J4.js +0 -160
  1021. package/bundled-test-harness/assets/tools-CrudqfS3.js +0 -774
  1022. package/bundled-test-harness/assets/tools-calling-test-jo7IREau.js +0 -200
  1023. package/bundled-test-harness/assets/tools-test-D1wMfUa0.js +0 -1624
  1024. package/bundled-test-harness/assets/tools-test-Dn1LYsXp.js +0 -3204
  1025. package/bundled-test-harness/assets/tooltip-8Wyz4IIn.css +0 -68
  1026. package/bundled-test-harness/assets/tooltip-CqwLzRz5.js +0 -134
  1027. package/bundled-test-harness/assets/transform-cards-test-BPhwPFtp.js +0 -341
  1028. package/bundled-test-harness/assets/ts-file-def-preview-test-Bq3zTgw8.js +0 -95
  1029. package/bundled-test-harness/assets/ts-file-def-test-GLswMl7O.js +0 -92
  1030. package/bundled-test-harness/assets/ttf-font-def-test-DQulvUWs.js +0 -206
  1031. package/bundled-test-harness/assets/turn-settling-test-BspmOUQP.js +0 -160
  1032. package/bundled-test-harness/assets/turn-summary-9A8nvC0-.js +0 -1039
  1033. package/bundled-test-harness/assets/unpublish-realm-test-SkYPEhDz.js +0 -102
  1034. package/bundled-test-harness/assets/update-room-skills-test-cTxb-WU6.js +0 -240
  1035. package/bundled-test-harness/assets/use-ai-assistant-test-uaqW1T_A.js +0 -384
  1036. package/bundled-test-harness/assets/user-message-BudkIP-3.js +0 -262
  1037. package/bundled-test-harness/assets/validate-realm-test-DxrGgH9n.js +0 -67
  1038. package/bundled-test-harness/assets/video-metadata-extractor-test-B2pe-Iqa.js +0 -520
  1039. package/bundled-test-harness/assets/view-visually-test-PNon6fUo.js +0 -335
  1040. package/bundled-test-harness/assets/virtual-network-reset-test-uYJLaQHf.js +0 -96
  1041. package/bundled-test-harness/assets/wav-audio-def-test-F4OrFFR4.js +0 -160
  1042. package/bundled-test-harness/assets/webp-image-def-test-B0a_-Vya.js +0 -226
  1043. package/bundled-test-harness/assets/workspace-Cyftysy9.js +0 -440
  1044. package/bundled-test-harness/assets/workspace-a11y-test-sKiPDcbl.js +0 -197
  1045. package/bundled-test-harness/assets/workspace-actions-BuawjFmZ.js +0 -561
  1046. package/bundled-test-harness/assets/workspace-behavior-test-CKa-pb3T.js +0 -338
  1047. package/bundled-test-harness/assets/workspace-delete-multiple-test-Dt2weG5R.js +0 -212
  1048. package/bundled-test-harness/assets/workspace-delete-summary-test-CkqgFqZ8.js +0 -63
  1049. package/bundled-test-harness/assets/workspace-hosting-test-Ci7EEtHP.js +0 -138
  1050. package/bundled-test-harness/assets/workspace-indexing-test-BPVxxY6W.js +0 -71
  1051. package/bundled-test-harness/assets/workspace-test-CWr7vFbg.js +0 -385
  1052. package/bundled-test-harness/assets/workspace-test-INNpQovc.js +0 -51
  1053. package/bundled-test-harness/assets/workspace-timestamp-labels-test-CigQb5wr.js +0 -68
  1054. package/bundled-test-harness/assets/workspace-unit-test-BwVKpuXT.js +0 -122
  1055. package/bundled-test-harness/assets/write-binary-file-test-BKP3gv1k.js +0 -124
  1056. package/bundled-test-harness/assets/write-text-file-test-B6ijTKTa.js +0 -191
  1057. package/bundled-test-harness/assets/zip-archive-test-C-PBzkTn.js +0 -285
  1058. package/bundled-test-harness/assets/zip-file-def-test-FyCzJT-9.js +0 -164
@@ -0,0 +1,3844 @@
1
+ import { s as __toESM } from "./chunk-Rp0pCe6z.js";
2
+ import { Br as testRealmURL, Co as SupportedMimeType, Fn as APP_BOXEL_ACTIVE_LLM, fr as CAPTURE_SOURCE_URL_PREFIX, nr as APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, pr as DEFAULT_FALLBACK_MODELS, yr as LOCAL_SOURCE_URL_PREFIX } from "./runtime-common-BTaLJEw-.js";
3
+ import { M as waitUntil, T as settled } from "./dist-BOAdzfkP.js";
4
+ import { r as runRealmCode, t as RunRealmCodeTool } from "./run-realm-code-DsBUf1si.js";
5
+ import { t as require_qunit } from "./qunit-C-Foo4Nd.js";
6
+ import { F as setupLocalIndexing, L as setupRealmCacheTeardown, P as setupIntegrationTestRealm, R as setupRealmServerEndpoints, Tt as testRealmSecretSeed, Z as withCachedRealmSetup, bt as resetCatalogRealmURL, lt as setupRenderingTest, t as setupMockMatrix, wt as createJWT, xt as setCatalogRealmURL } from "./mock-matrix-FjsXmU7-.js";
7
+ import { r as getService } from "./dist-CPfIHBla.js";
8
+ //#region tests/integration/tools/run-realm-code-test.gts
9
+ var import_qunit = /* @__PURE__ */ __toESM(require_qunit());
10
+ var otherRealmURL = "http://test-realm/test2/";
11
+ function knowReadableWorkspace(identifier, permissions = ["read"]) {
12
+ getService("realm").getOrCreateRealmResource(identifier, createJWT({
13
+ user: "@testuser:localhost",
14
+ realm: identifier,
15
+ sessionRoom: "boxel-session-room-id",
16
+ permissions,
17
+ realmServerURL: "http://test-realm/"
18
+ }, "1d", testRealmSecretSeed), { exact: true });
19
+ }
20
+ function recipeJSON(name, attributes, chef, module = "../recipe", helpers = []) {
21
+ let relationships = {};
22
+ if (chef) relationships.chef = { links: { self: chef } };
23
+ helpers.forEach((helper, index) => {
24
+ relationships[`helpers.${index}`] = { links: { self: helper } };
25
+ });
26
+ return `${JSON.stringify({ data: {
27
+ type: "card",
28
+ attributes,
29
+ ...Object.keys(relationships).length ? { relationships } : {},
30
+ meta: { adoptsFrom: {
31
+ module,
32
+ name
33
+ } }
34
+ } }, null, 2)}\n`;
35
+ }
36
+ var UNINDEXED_PET = JSON.stringify({ data: {
37
+ type: "card",
38
+ attributes: { name: "Biscuit" },
39
+ meta: { adoptsFrom: {
40
+ module: "../pet",
41
+ name: "Pet"
42
+ } }
43
+ } });
44
+ function readCardScript(path) {
45
+ return `try { return await realm.cards.get(${JSON.stringify(path)}); } catch (e) { return e.message; }`;
46
+ }
47
+ var PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";
48
+ (0, import_qunit.module)("Integration | tools | run-realm-code", function(hooks) {
49
+ setupRenderingTest(hooks);
50
+ setupLocalIndexing(hooks);
51
+ let mockMatrixUtils = setupMockMatrix(hooks, {
52
+ activeRealms: [testRealmURL, otherRealmURL],
53
+ autostart: true
54
+ });
55
+ let captureRequests = [];
56
+ setupRealmServerEndpoints(hooks, [{
57
+ route: "_capture",
58
+ getResponse: async (req) => {
59
+ captureRequests.push(await req.clone().json());
60
+ return new Response(JSON.stringify({ data: {
61
+ type: "capture-result",
62
+ attributes: {
63
+ status: "ready",
64
+ base64: PNG_BASE64,
65
+ width: 1,
66
+ height: 1,
67
+ contentType: "image/png"
68
+ }
69
+ } }), {
70
+ status: 201,
71
+ headers: { "Content-Type": "application/vnd.api+json" }
72
+ });
73
+ }
74
+ }]);
75
+ setupRealmCacheTeardown(hooks);
76
+ hooks.beforeEach(function() {
77
+ captureRequests = [];
78
+ });
79
+ let adapter;
80
+ let testRealm;
81
+ hooks.beforeEach(async function() {
82
+ ({adapter, realm: testRealm} = await withCachedRealmSetup(async () => setupIntegrationTestRealm({
83
+ mockMatrixUtils,
84
+ contents: {
85
+ "task.json": `{
86
+ "title": "Old title",
87
+ "count": 1
88
+ }
89
+ `,
90
+ "notes/first.md": "# First\n",
91
+ "images/existing.png": Uint8Array.from(atob(PNG_BASE64), (char) => char.charCodeAt(0)),
92
+ "docs/report.docx": Uint8Array.from([
93
+ 80,
94
+ 75,
95
+ 3,
96
+ 4,
97
+ 20,
98
+ 0,
99
+ 6,
100
+ 0,
101
+ 255,
102
+ 254,
103
+ 156
104
+ ]),
105
+ "docs/README": "Plain text with no extension\n",
106
+ "docs/replacement.txt": "A replacement character: �\n",
107
+ "docs/latin1.txt": Uint8Array.from([
108
+ 99,
109
+ 97,
110
+ 102,
111
+ 233,
112
+ 10
113
+ ]),
114
+ "unformatted.gts": `import { CardDef } from "@cardstack/base/card-api"
115
+ export class Unformatted extends CardDef {}
116
+ `,
117
+ "skills/test-skill/SKILL.md": `---
118
+ name: Test Skill
119
+ description: A skill a script turns on
120
+ boxel:
121
+ kind: skill
122
+ ---
123
+ # Test Skill
124
+ `,
125
+ "pet.gts": `
126
+ import { CardDef, field, contains } from '@cardstack/base/card-api';
127
+ import StringField from '@cardstack/base/string';
128
+
129
+ export class Pet extends CardDef {
130
+ static displayName = 'Pet';
131
+ @field name = contains(StringField);
132
+ }
133
+ `,
134
+ "Pet/mango.json": { data: {
135
+ type: "card",
136
+ attributes: { name: "Mango" },
137
+ meta: { adoptsFrom: {
138
+ module: "../pet",
139
+ name: "Pet"
140
+ } }
141
+ } },
142
+ "Spec/pet.json": { data: {
143
+ type: "card",
144
+ attributes: {
145
+ cardTitle: "Pet",
146
+ cardDescription: "A pet with a name",
147
+ specType: "card",
148
+ readMe: "# Pet\n\nUse Pet for any animal with a name.",
149
+ ref: {
150
+ module: "../pet",
151
+ name: "Pet"
152
+ }
153
+ },
154
+ meta: { adoptsFrom: {
155
+ module: "@cardstack/base/spec",
156
+ name: "Spec"
157
+ } }
158
+ } },
159
+ "Plain/base-typed.json": { data: {
160
+ type: "card",
161
+ meta: { adoptsFrom: {
162
+ module: "@cardstack/base/card-api",
163
+ name: "CardDef"
164
+ } }
165
+ } },
166
+ "recipe.gts": `
167
+ import { contains, field, linksTo, linksToMany, CardDef } from "@cardstack/base/card-api";
168
+ import StringField from "@cardstack/base/string";
169
+
170
+ export class Chef extends CardDef {
171
+ static displayName = 'Chef';
172
+ @field name = contains(StringField);
173
+ @field cardTitle = contains(StringField, {
174
+ computeVia: function (this: Chef) {
175
+ return this.name;
176
+ },
177
+ });
178
+ }
179
+
180
+ export class Recipe extends CardDef {
181
+ static displayName = 'Recipe';
182
+ @field name = contains(StringField);
183
+ @field cuisine = contains(StringField);
184
+ @field chef = linksTo(Chef);
185
+ @field helpers = linksToMany(Chef);
186
+ @field cardTitle = contains(StringField, {
187
+ computeVia: function (this: Recipe) {
188
+ return 'Recipe: ' + this.name;
189
+ },
190
+ });
191
+ }
192
+ `,
193
+ "person.gts": `
194
+ import { contains, field, linksTo, CardDef } from "@cardstack/base/card-api";
195
+ import StringField from "@cardstack/base/string";
196
+
197
+ export class Person extends CardDef {
198
+ @field firstName = contains(StringField);
199
+ @field friend = linksTo(() => Person);
200
+ @field cardTitle = contains(StringField, {
201
+ computeVia: function (this: Person) {
202
+ return this.firstName;
203
+ },
204
+ });
205
+ }
206
+ `,
207
+ "Chef/ana.json": recipeJSON("Chef", { name: "Ana" }),
208
+ "Recipe/pancakes.json": recipeJSON("Recipe", {
209
+ name: "Pancakes",
210
+ cuisine: "unknown"
211
+ }, "../Chef/ana"),
212
+ "Recipe/ramen.json": recipeJSON("Recipe", {
213
+ name: "Ramen",
214
+ cuisine: "Japanese"
215
+ }, void 0, "../recipe", ["../Chef/ana"]),
216
+ "Recipe/desserts/tiramisu.json": recipeJSON("Recipe", {
217
+ name: "Tiramisu",
218
+ cuisine: "unknown"
219
+ }, void 0, "../../recipe"),
220
+ "people/hassan.json": { data: {
221
+ attributes: { firstName: "Hassan" },
222
+ relationships: { friend: { links: { self: "./mango" } } },
223
+ meta: { adoptsFrom: {
224
+ module: "../person",
225
+ name: "Person"
226
+ } }
227
+ } },
228
+ "people/mango.json": { data: {
229
+ attributes: { firstName: "Mango" },
230
+ meta: { adoptsFrom: {
231
+ module: "../person",
232
+ name: "Person"
233
+ } }
234
+ } }
235
+ }
236
+ })));
237
+ await withCachedRealmSetup(async () => setupIntegrationTestRealm({
238
+ mockMatrixUtils,
239
+ realmURL: otherRealmURL,
240
+ contents: { "Recipe/elsewhere.json": recipeJSON("Recipe", {
241
+ name: "Elsewhere",
242
+ cuisine: "unknown"
243
+ }, void 0, `${testRealmURL}recipe`) }
244
+ }));
245
+ await getService("realm").login(testRealmURL);
246
+ });
247
+ (0, import_qunit.test)("realm.capture attaches a capture to the result and tells the script only what it needs", async function(assert) {
248
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
249
+ realm: testRealmURL,
250
+ roomId: "!room:example.com",
251
+ code: `return await realm.capture('task.json');`
252
+ });
253
+ assert.strictEqual(captureRequests.length, 1, "one capture request");
254
+ assert.strictEqual(captureRequests[0].data.attributes.fileURL, `${testRealmURL}task.json`);
255
+ assert.deepEqual(JSON.parse(result.scriptResult), {
256
+ path: "task.json",
257
+ kind: "file",
258
+ format: "isolated",
259
+ viewportWidth: 1280,
260
+ viewportHeight: 800,
261
+ width: 1,
262
+ height: 1,
263
+ attached: true
264
+ });
265
+ assert.strictEqual(result.captures.length, 1, "the capture rides the result");
266
+ assert.strictEqual(result.captures[0].contentType, "image/png");
267
+ assert.ok(result.captures[0].url, "the capture is uploaded to the room");
268
+ });
269
+ (0, import_qunit.test)("Stop ends a run that waits on realm.ask", async function(assert) {
270
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
271
+ let asked = false;
272
+ let startedAt = 0;
273
+ let askedAfterMs;
274
+ command.onAsk = () => {
275
+ asked = true;
276
+ askedAfterMs = Date.now() - startedAt;
277
+ return new Promise(() => void 0);
278
+ };
279
+ startedAt = Date.now();
280
+ let run = command.execute({
281
+ realm: testRealmURL,
282
+ roomId: "!room:example.com",
283
+ code: `await realm.ask({ question: 'Which one?', options: [{ label: 'A' }, { label: 'B' }] });
284
+ return 'answered';`
285
+ });
286
+ let endedBeforeAsk;
287
+ run.then(() => {
288
+ if (!asked) endedBeforeAsk = "it returned";
289
+ }, (error) => {
290
+ if (!asked) endedBeforeAsk = `it failed: ${error instanceof Error ? error.message : String(error)}`;
291
+ });
292
+ await waitUntil(() => asked || endedBeforeAsk !== void 0, {
293
+ timeout: 1e4,
294
+ timeoutMessage: "the script did not ask within 10000ms"
295
+ });
296
+ assert.true(asked, asked ? `the script asked ${askedAfterMs}ms after the run started` : `the run ended ${Date.now() - startedAt}ms after it started, before the script asked: ${endedBeforeAsk}`);
297
+ command.stop();
298
+ await assert.rejects(run, /stopped/i, "the run ends without an answer");
299
+ });
300
+ function askThroughToolService(command, callId) {
301
+ let toolService = getService("tool-service");
302
+ command.onAsk = (request) => toolService.askFromScript(callId, request);
303
+ }
304
+ (0, import_qunit.test)("a reader's pick reaches the script as its value and its label", async function(assert) {
305
+ let toolService = getService("tool-service");
306
+ let command = new RunRealmCodeTool(toolService.toolContext);
307
+ askThroughToolService(command, "ask-call");
308
+ let run = command.execute({
309
+ realm: testRealmURL,
310
+ roomId: "!room:example.com",
311
+ code: `return await realm.ask({ question: 'Which one?', options: [{ label: 'Alpha', value: 'a' }, { label: 'Beta', value: 'b' }] });`
312
+ });
313
+ await waitUntil(() => toolService.scriptAsks.has("ask-call"), { timeout: 1e4 });
314
+ let ask = toolService.scriptAsks.get("ask-call");
315
+ assert.strictEqual(ask.request.question, "Which one?");
316
+ ask.answer("b");
317
+ let result = await run;
318
+ assert.deepEqual(JSON.parse(result.scriptResult), {
319
+ answer: "b",
320
+ label: "Beta"
321
+ });
322
+ assert.false(toolService.scriptAsks.has("ask-call"), "the answered question is gone");
323
+ });
324
+ (0, import_qunit.test)("Stop through the tool service withdraws the run's question", async function(assert) {
325
+ let toolService = getService("tool-service");
326
+ let command = new RunRealmCodeTool(toolService.toolContext);
327
+ askThroughToolService(command, "stopped-ask-call");
328
+ toolService.stoppableTools.set("stopped-ask-call", {
329
+ roomId: "!room:example.com",
330
+ stop: () => command.stop()
331
+ });
332
+ let run = command.execute({
333
+ realm: testRealmURL,
334
+ roomId: "!room:example.com",
335
+ code: `await realm.ask({ question: 'Which one?', options: [{ label: 'A' }, { label: 'B' }] });
336
+ return 'answered';`
337
+ });
338
+ await waitUntil(() => toolService.scriptAsks.has("stopped-ask-call"), { timeout: 1e4 });
339
+ toolService.stopTool("stopped-ask-call");
340
+ assert.false(toolService.scriptAsks.has("stopped-ask-call"), "the picker is withdrawn at once");
341
+ await assert.rejects(run, /stopped|ended/i, "the run ends without an answer");
342
+ });
343
+ (0, import_qunit.test)("a question nobody answers ends the run when its wait runs out", async function(assert) {
344
+ let toolService = getService("tool-service");
345
+ toolService.__setWaitsForTesting({ scriptAskMaxMs: 50 });
346
+ let command = new RunRealmCodeTool(toolService.toolContext);
347
+ askThroughToolService(command, "unanswered-ask-call");
348
+ let run = command.execute({
349
+ realm: testRealmURL,
350
+ roomId: "!room:example.com",
351
+ code: `await realm.ask({ question: 'Which one?', options: [{ label: 'A' }, { label: 'B' }] });
352
+ return 'answered';`
353
+ });
354
+ await assert.rejects(run, /No answer came within 50 ms/, "the run ends without an answer");
355
+ assert.false(toolService.scriptAsks.has("unanswered-ask-call"), "the picker is withdrawn");
356
+ });
357
+ (0, import_qunit.test)("a run waiting on the reader's pick outlasts the tool deadline", async function(assert) {
358
+ let toolService = getService("tool-service");
359
+ let matrixService = getService("matrix-service");
360
+ let deadlineMs = 500;
361
+ toolService.__setWaitsForTesting({ toolExecuteTimeoutMs: deadlineMs });
362
+ let results = [];
363
+ let earlyFailures = [];
364
+ let originalSend = matrixService.sendToolResultEvent;
365
+ let originalUpdate = matrixService.updateSkillsAndToolsIfNeeded;
366
+ let startedAt = 0;
367
+ let askedAfterMs;
368
+ let originalGetInputType = RunRealmCodeTool.prototype.getInputType;
369
+ let originalExecute = RunRealmCodeTool.prototype.execute;
370
+ RunRealmCodeTool.prototype.getInputType = async () => void 0;
371
+ RunRealmCodeTool.prototype.execute = async function() {
372
+ askedAfterMs = Date.now() - startedAt;
373
+ await this.onAsk({
374
+ question: "Which one?",
375
+ kind: "options",
376
+ allowOther: false,
377
+ options: [{
378
+ label: "A",
379
+ value: "a"
380
+ }, {
381
+ label: "B",
382
+ value: "b"
383
+ }]
384
+ });
385
+ };
386
+ matrixService.sendToolResultEvent = async (args) => {
387
+ results.push({
388
+ toolCallId: args.toolCallId,
389
+ status: args.status
390
+ });
391
+ if (args.failureReason) earlyFailures.push(args.failureReason);
392
+ };
393
+ matrixService.updateSkillsAndToolsIfNeeded = async () => void 0;
394
+ let running;
395
+ try {
396
+ let call = {
397
+ id: "waiting-call",
398
+ name: "runRealmCode",
399
+ eventId: "$bot",
400
+ message: { roomId: "!room:example.com" },
401
+ codeRef: {
402
+ module: "@cardstack/boxel-host/tools/run-realm-code",
403
+ name: "default"
404
+ },
405
+ arguments: { attributes: {
406
+ realm: testRealmURL,
407
+ code: "ignored"
408
+ } },
409
+ isResolving: false,
410
+ resolve: async () => void 0
411
+ };
412
+ startedAt = Date.now();
413
+ running = toolService.run.perform(call, { auto: true });
414
+ await waitUntil(() => toolService.scriptAsks.has("waiting-call"), { timeout: 1e4 });
415
+ await new Promise((resolve) => setTimeout(resolve, startedAt + deadlineMs + 300 - Date.now()));
416
+ assert.deepEqual(results, [], `the run is still waiting on the pick (asked ${askedAfterMs}ms into a ${deadlineMs}ms deadline; early results: ${earlyFailures.join("; ") || "none"})`);
417
+ toolService.scriptAsks.get("waiting-call").answer("b");
418
+ await running;
419
+ assert.deepEqual(results, [{
420
+ toolCallId: "waiting-call",
421
+ status: "applied"
422
+ }], `the run finishes with its answer instead of timing out (failures: ${earlyFailures.join("; ") || "none"})`);
423
+ } finally {
424
+ toolService.scriptAsks.get("waiting-call")?.cancel();
425
+ await Promise.resolve(running).catch(() => void 0);
426
+ RunRealmCodeTool.prototype.getInputType = originalGetInputType;
427
+ RunRealmCodeTool.prototype.execute = originalExecute;
428
+ matrixService.sendToolResultEvent = originalSend;
429
+ matrixService.updateSkillsAndToolsIfNeeded = originalUpdate;
430
+ }
431
+ });
432
+ (0, import_qunit.test)("the result attaches the files the run saved, then the captures it took", async function(assert) {
433
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
434
+ let result = await command.execute({
435
+ realm: testRealmURL,
436
+ roomId: "!room:example.com",
437
+ code: `await realm.fs.writeText('seen.json', '{}');
438
+ return await realm.capture('seen.json');`
439
+ });
440
+ let attachments = command.resultAttachments(result);
441
+ assert.deepEqual(attachments.map((file) => file.sourceUrl), [`${testRealmURL}seen.json`, result.captures[0].sourceUrl], "the saved file, then the capture");
442
+ assert.strictEqual(attachments[0].url, void 0, "the saved file is uploaded from the realm when the result is sent");
443
+ assert.strictEqual(attachments[1].url, result.captures[0].url, "the capture rides as the media already uploaded");
444
+ assert.true(attachments[1].sourceUrl.startsWith(CAPTURE_SOURCE_URL_PREFIX), `the capture is named by a capture source URL, which keeps it out of correctness checks: ${attachments[1].sourceUrl}`);
445
+ });
446
+ (0, import_qunit.test)("an edit that lint takes back is reported as unchanged, not saved", async function(assert) {
447
+ let toolService = getService("tool-service");
448
+ let cardService = getService("card-service");
449
+ let command = new RunRealmCodeTool(toolService.toolContext);
450
+ let fileUrl = new URL(`${testRealmURL}tidy.gts`);
451
+ let unusedImport = "\nimport NumberField from '@cardstack/base/number';";
452
+ let passThrough = adapter.lintStub;
453
+ adapter.lintStub = async (request) => ({
454
+ output: (await request.text()).replace(unusedImport, ""),
455
+ fixed: true,
456
+ messages: []
457
+ });
458
+ await command.execute({
459
+ realm: testRealmURL,
460
+ roomId: "!room:example.com",
461
+ code: `await realm.fs.writeText('tidy.gts', "import StringField from '@cardstack/base/string';\\nimport { CardDef, field, contains } from '@cardstack/base/card-api';\\n\\nexport class Tidy extends CardDef {\\n @field name = contains(StringField);\\n}\\n");`
462
+ });
463
+ let before = (await cardService.getSource(fileUrl)).content;
464
+ let result = await command.execute({
465
+ realm: testRealmURL,
466
+ roomId: "!room:example.com",
467
+ code: `await realm.fs.replace(
468
+ 'tidy.gts',
469
+ "import StringField from '@cardstack/base/string';",
470
+ "import StringField from '@cardstack/base/string';\\nimport NumberField from '@cardstack/base/number';",
471
+ );`
472
+ });
473
+ assert.deepEqual(result.files.map((file) => [file.fileUrl, file.status]), [[fileUrl.href, "unchanged"]]);
474
+ assert.deepEqual(command.resultAttachments(result), [], "an unchanged file is not attached, so no correctness check runs on it");
475
+ assert.strictEqual((await cardService.getSource(fileUrl)).content, before, "the file was not written");
476
+ adapter.lintStub = passThrough;
477
+ });
478
+ (0, import_qunit.test)("opens the card the script returns to show", async function(assert) {
479
+ let toolService = getService("tool-service");
480
+ let operatorModeStateService = getService("operator-mode-state-service");
481
+ operatorModeStateService.restore({
482
+ stacks: [[]],
483
+ submode: "interact"
484
+ });
485
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
486
+ realm: testRealmURL,
487
+ roomId: "!room:example.com",
488
+ code: `await realm.fs.writeText('Pet/biscuit.json', JSON.stringify({
489
+ data: {
490
+ attributes: { name: 'Biscuit' },
491
+ meta: { adoptsFrom: { module: '../pet', name: 'Pet' } },
492
+ },
493
+ }));
494
+ return { show: 'Pet/biscuit' };`
495
+ });
496
+ assert.strictEqual(result.files[0]?.status, "saved");
497
+ assert.strictEqual(result.shownCard, `${testRealmURL}Pet/biscuit`);
498
+ assert.notOk(result.showError);
499
+ let shownIds = operatorModeStateService.state.stacks.flat().map((item) => item.id);
500
+ assert.true(shownIds.includes(`${testRealmURL}Pet/biscuit`), "the card is open on a stack");
501
+ });
502
+ (0, import_qunit.test)("show opens a file that is not a card in code mode", async function(assert) {
503
+ let toolService = getService("tool-service");
504
+ let operatorModeStateService = getService("operator-mode-state-service");
505
+ operatorModeStateService.restore({
506
+ stacks: [[]],
507
+ submode: "interact"
508
+ });
509
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
510
+ realm: testRealmURL,
511
+ roomId: "!room:example.com",
512
+ code: `return { show: 'notes/first.md' };`
513
+ });
514
+ assert.strictEqual(result.shownCard, `${testRealmURL}notes/first.md`);
515
+ assert.notOk(result.showError);
516
+ assert.strictEqual(operatorModeStateService.state.submode, "code");
517
+ assert.strictEqual(operatorModeStateService.state.codePath, `${testRealmURL}notes/first.md`);
518
+ });
519
+ (0, import_qunit.test)("a card it cannot show does not fail the run", async function(assert) {
520
+ let toolService = getService("tool-service");
521
+ let operatorModeStateService = getService("operator-mode-state-service");
522
+ operatorModeStateService.restore({
523
+ stacks: [[]],
524
+ submode: "interact"
525
+ });
526
+ let command = new RunRealmCodeTool(toolService.toolContext);
527
+ let result = await command.execute({
528
+ realm: testRealmURL,
529
+ roomId: "!room:example.com",
530
+ code: `await realm.fs.writeText('seen.json', '{}');
531
+ return { show: 'Pet/missing' };`
532
+ });
533
+ assert.strictEqual(result.files[0]?.status, "saved", "files still report");
534
+ assert.notOk(result.shownCard);
535
+ assert.true(/Not found: .*Pet\/missing/.test(result.showError ?? ""), `says why: ${result.showError}`);
536
+ let outside = await command.execute({
537
+ realm: testRealmURL,
538
+ roomId: "!room:example.com",
539
+ code: `return { show: 'https://example.com/card' };`
540
+ });
541
+ assert.true(/Path is outside this realm/.test(outside.showError ?? ""), `refuses another realm: ${outside.showError}`);
542
+ assert.deepEqual(operatorModeStateService.state.stacks.flat(), [], "nothing is opened");
543
+ });
544
+ (0, import_qunit.test)("realm.capture refuses a fourth capture in one run", async function(assert) {
545
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
546
+ await assert.rejects(command.execute({
547
+ realm: testRealmURL,
548
+ roomId: "!room:example.com",
549
+ code: `for (let i = 0; i < 4; i++) { await realm.capture('task.json'); }`
550
+ }), /realm\.capture may run at most 3 times in one run/);
551
+ assert.strictEqual(captureRequests.length, 3, "three captures were taken");
552
+ });
553
+ (0, import_qunit.test)("realm.capture captures a card by its path or its id, with the options it is given", async function(assert) {
554
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
555
+ realm: testRealmURL,
556
+ roomId: "!room:example.com",
557
+ code: `return [
558
+ await realm.capture('Pet/mango'),
559
+ await realm.capture('${testRealmURL}Pet/mango', {
560
+ format: 'embedded',
561
+ viewportWidth: 600,
562
+ viewportHeight: 400,
563
+ target: '.name',
564
+ }),
565
+ ];`
566
+ });
567
+ assert.strictEqual(captureRequests.length, 2, "two capture requests");
568
+ let [byPath, byId] = captureRequests.map((r) => r.data.attributes);
569
+ assert.strictEqual(byPath.cardId, `${testRealmURL}Pet/mango`);
570
+ assert.strictEqual(byId.cardId, `${testRealmURL}Pet/mango`);
571
+ assert.strictEqual(byId.format, "embedded");
572
+ assert.deepEqual(byId.captureSpec, {
573
+ viewport: {
574
+ width: 600,
575
+ height: 400
576
+ },
577
+ target: ".name"
578
+ });
579
+ let [first, second] = JSON.parse(result.scriptResult);
580
+ assert.strictEqual(first.path, "Pet/mango");
581
+ assert.strictEqual(first.kind, "card");
582
+ assert.strictEqual(second.path, "Pet/mango", "a URL in the run realm is named by its path");
583
+ assert.strictEqual(second.format, "embedded");
584
+ assert.strictEqual(second.viewportWidth, 600);
585
+ assert.strictEqual(second.viewportHeight, 400);
586
+ assert.strictEqual(second.target, ".name");
587
+ assert.strictEqual(first.target, void 0, "a page capture has none");
588
+ assert.strictEqual(result.captures.length, 2);
589
+ });
590
+ (0, import_qunit.test)("realm.capture refuses a URL outside every workspace", async function(assert) {
591
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
592
+ await assert.rejects(command.execute({
593
+ realm: testRealmURL,
594
+ roomId: "!room:example.com",
595
+ code: `await realm.capture('https://example.com/page.html');`
596
+ }), /not in a workspace the user can read/);
597
+ assert.strictEqual(captureRequests.length, 0, "no capture is attempted");
598
+ });
599
+ (0, import_qunit.test)("realm.capture refuses a file attached to the chat, and says how to see it again", async function(assert) {
600
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
601
+ let attachment = `${LOCAL_SOURCE_URL_PREFIX}00000000-0000-4000-8000-000000000001/photo.jpg`;
602
+ await assert.rejects(command.execute({
603
+ realm: testRealmURL,
604
+ roomId: "!room:example.com",
605
+ code: `return await realm.capture('${attachment}');`
606
+ }), (error) => error.message.includes(`Cannot capture ${attachment}`) && error.message.includes("attach it again"));
607
+ assert.strictEqual(captureRequests.length, 0, "no capture is attempted");
608
+ });
609
+ (0, import_qunit.test)("realm.capture refuses the image a capture took, and says to capture what it shows", async function(assert) {
610
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
611
+ let captured = `${CAPTURE_SOURCE_URL_PREFIX}0000000000000001/mango.png`;
612
+ await assert.rejects(command.execute({
613
+ realm: testRealmURL,
614
+ roomId: "!room:example.com",
615
+ code: `return await realm.capture('${captured}');`
616
+ }), (error) => error.message.includes(`Cannot capture ${captured}`) && error.message.includes("capture the card or file it shows") && !error.message.includes("attach it again"));
617
+ assert.strictEqual(captureRequests.length, 0, "no capture is attempted");
618
+ });
619
+ (0, import_qunit.test)("the description says realm.capture refuses a file attached to the chat", async function(assert) {
620
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
621
+ assert.true(command.description.includes(`a ${LOCAL_SOURCE_URL_PREFIX} URL`), "the description names the prefix of a file attached from the computer");
622
+ assert.true(command.description.includes("so realm.capture refuses it"), "a model choosing a capture target reads that such a file is not one");
623
+ });
624
+ (0, import_qunit.test)("realm.capture finds the workspace of a prefixed id, whatever form the workspace is known by", async function(assert) {
625
+ let { virtualNetwork } = getService("network");
626
+ virtualNetwork.addRealmMapping("@test/mapped/", "http://test-realm/mapped/");
627
+ try {
628
+ knowReadableWorkspace("@test/mapped/");
629
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
630
+ realm: testRealmURL,
631
+ roomId: "!room:example.com",
632
+ code: `return await realm.capture('@test/mapped/Pet/mango');`
633
+ });
634
+ assert.strictEqual(captureRequests.length, 1, "one capture request");
635
+ let { realmURL, cardId } = captureRequests[0].data.attributes;
636
+ assert.strictEqual(realmURL, "http://test-realm/mapped/");
637
+ assert.strictEqual(cardId, "http://test-realm/mapped/Pet/mango");
638
+ assert.strictEqual(JSON.parse(result.scriptResult).path, "@test/mapped/Pet/mango", "the capture is named by the id the script gave");
639
+ } finally {
640
+ virtualNetwork.removeRealmMapping("@test/mapped/");
641
+ }
642
+ });
643
+ (0, import_qunit.test)("realm.capture asks a workspace nested under the run realm for its own card, and names it by its URL", async function(assert) {
644
+ let nestedRealmURL = `${testRealmURL}nested/`;
645
+ knowReadableWorkspace(nestedRealmURL);
646
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
647
+ realm: testRealmURL,
648
+ roomId: "!room:example.com",
649
+ code: `return [
650
+ await realm.capture('${nestedRealmURL}Pet/mango'),
651
+ await realm.capture('${testRealmURL}Pet/mango'),
652
+ ];`
653
+ });
654
+ assert.deepEqual(captureRequests.map((r) => r.data.attributes.realmURL), [nestedRealmURL, testRealmURL], "each capture is asked of the deepest workspace that holds it");
655
+ let [nested, own] = JSON.parse(result.scriptResult);
656
+ assert.strictEqual(nested.path, `${nestedRealmURL}Pet/mango`, "a card in the nested workspace keeps its URL");
657
+ assert.strictEqual(own.path, "Pet/mango");
658
+ });
659
+ (0, import_qunit.test)("realm.cards.get returns a card as data, with computed fields and absolute URLs", async function(assert) {
660
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
661
+ let result = await command.execute({
662
+ realm: testRealmURL,
663
+ roomId: "!room:example.com",
664
+ code: `return {
665
+ bare: await realm.cards.get('people/hassan'),
666
+ withExtension: await realm.cards.get('people/hassan.json'),
667
+ byURL: await realm.cards.get(${JSON.stringify(`${testRealmURL}people/hassan`)}),
668
+ };`
669
+ });
670
+ assert.deepEqual(result.files, [], "reading a card saves nothing");
671
+ let { bare, withExtension, byURL } = JSON.parse(result.scriptResult);
672
+ assert.strictEqual(bare.path, "people/hassan.json");
673
+ assert.strictEqual(bare.id, `${testRealmURL}people/hassan`);
674
+ assert.deepEqual(bare.type, {
675
+ module: `${testRealmURL}person`,
676
+ name: "Person"
677
+ });
678
+ assert.strictEqual(bare.attributes.firstName, "Hassan");
679
+ assert.strictEqual(bare.attributes.cardTitle, "Hassan", "computed fields are included");
680
+ assert.deepEqual(bare.relationships, { friend: `${testRealmURL}people/mango` }, "links are keyed by field path, as realm.cards.search returns them");
681
+ assert.deepEqual(withExtension, bare, "a .json path reads the same card");
682
+ assert.deepEqual(byURL, bare, "a URL reads the same card");
683
+ let afterEdit = await command.execute({
684
+ realm: testRealmURL,
685
+ roomId: "!room:example.com",
686
+ code: `await realm.cards.get('people/hassan');
687
+ await realm.fs.replace('people/hassan.json', '"Hassan"', '"Hassan Abdel"');
688
+ return await realm.cards.get('people/hassan');`
689
+ });
690
+ assert.strictEqual(JSON.parse(afterEdit.scriptResult).attributes.firstName, "Hassan Abdel", "a card read after an edit in the same run shows the edit");
691
+ await assert.rejects(command.execute({
692
+ realm: testRealmURL,
693
+ roomId: "!room:example.com",
694
+ code: `await realm.cards.get('people/missing');`
695
+ }), /Card not found/);
696
+ await assert.rejects(command.execute({
697
+ realm: testRealmURL,
698
+ roomId: "!room:example.com",
699
+ code: `await realm.cards.get('https://example.com/card');`
700
+ }), /Path is outside this realm/);
701
+ });
702
+ (0, import_qunit.test)("realm.cards.get says to read a card that is not indexed again only while the workspace is indexing, and names the call that indexes it otherwise", async function(assert) {
703
+ await adapter.write("Pet/unindexed.json", UNINDEXED_PET);
704
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
705
+ let messageService = getService("message-service");
706
+ messageService.relayRealmEvent({
707
+ eventName: "index",
708
+ indexType: "incremental-index-initiation",
709
+ updatedFile: `${testRealmURL}task.json`,
710
+ realmURL: testRealmURL
711
+ });
712
+ let whileIndexing;
713
+ try {
714
+ whileIndexing = await command.execute({
715
+ realm: testRealmURL,
716
+ roomId: "!room:example.com",
717
+ code: readCardScript("Pet/unindexed")
718
+ });
719
+ } finally {
720
+ messageService.relayRealmEvent({
721
+ eventName: "index",
722
+ indexType: "incremental",
723
+ invalidations: [],
724
+ realmURL: testRealmURL
725
+ });
726
+ }
727
+ assert.strictEqual(JSON.parse(whileIndexing.scriptResult), `Card is not indexed yet: ${testRealmURL}Pet/unindexed; read it again in a moment. If it is still not indexed after a few reads, index the file with realm.index.invalidate('Pet/unindexed.json')`, "while a pass runs, the row may still come, so the card is read again");
728
+ let idle = await command.execute({
729
+ realm: testRealmURL,
730
+ roomId: "!room:example.com",
731
+ code: readCardScript("Pet/unindexed.json")
732
+ });
733
+ assert.strictEqual(JSON.parse(idle.scriptResult), `Card is not indexed: ${testRealmURL}Pet/unindexed. Its file Pet/unindexed.json is in the workspace, and the workspace is not indexing, so reading the card again will not find it. Index the file with realm.index.invalidate('Pet/unindexed.json'), then read the card again. Do not edit the card to make the workspace index it`, "with no pass running, the error names the call that indexes the file");
734
+ let recovered = await command.execute({
735
+ realm: testRealmURL,
736
+ roomId: "!room:example.com",
737
+ code: `await realm.index.invalidate('Pet/unindexed.json');
738
+ return await realm.cards.get('Pet/unindexed');`
739
+ });
740
+ assert.strictEqual(JSON.parse(recovered.scriptResult).attributes.name, "Biscuit", "the call the error names indexes the card, and the card reads");
741
+ assert.deepEqual(recovered.files, [], "no file was edited to get there");
742
+ });
743
+ (0, import_qunit.test)("realm.cards.get names the error of a card that failed to index", async function(assert) {
744
+ await testRealm.write("Pet/orphan.json", JSON.stringify({ data: {
745
+ type: "card",
746
+ attributes: { name: "Orphan" },
747
+ meta: { adoptsFrom: {
748
+ module: "../no-such-module",
749
+ name: "Pet"
750
+ } }
751
+ } }));
752
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
753
+ realm: testRealmURL,
754
+ roomId: "!room:example.com",
755
+ code: readCardScript("Pet/orphan")
756
+ });
757
+ let message = JSON.parse(result.scriptResult);
758
+ assert.true(message.startsWith(`Card failed to index: ${testRealmURL}Pet/orphan: `), message);
759
+ assert.true(/no-such-module/.test(message), `the index error's own text is in the message: ${message}`);
760
+ assert.true(message.endsWith("Reading it again gives the same error until its cause is fixed, in Pet/orphan.json, in the definition it adopts from, or in a card it links to"), message);
761
+ });
762
+ (0, import_qunit.test)("realm.cards.get says a path that names a file is not a card", async function(assert) {
763
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
764
+ realm: testRealmURL,
765
+ roomId: "!room:example.com",
766
+ code: `let messages = [];
767
+ for (let path of ['images/existing.png', 'notes/first.md']) {
768
+ try { await realm.cards.get(path); } catch (e) { messages.push(e.message); }
769
+ }
770
+ return messages;`
771
+ });
772
+ assert.deepEqual(JSON.parse(result.scriptResult), ["Not a card: images/existing.png is a file, so realm.cards.get cannot read it; read it with realm.fs.readText if it is text, or look at it with realm.capture", "Not a card: notes/first.md is a file, so realm.cards.get cannot read it; read it with realm.fs.readText if it is text, or look at it with realm.capture"]);
773
+ });
774
+ (0, import_qunit.test)("realm.cards.get does not blame the card for a read that failed for another reason", async function(assert) {
775
+ let cardURL = `${testRealmURL}Pet/mango`;
776
+ let cardService = getService("card-service");
777
+ let originalFetchJSON = cardService.fetchJSON.bind(cardService);
778
+ cardService.fetchJSON = async (url, args) => {
779
+ if (String(url) === cardURL) {
780
+ let err = /* @__PURE__ */ new Error("realm failure");
781
+ err.status = 500;
782
+ err.responseText = JSON.stringify({ errors: [{
783
+ id: cardURL,
784
+ status: 500,
785
+ title: "Internal Server Error",
786
+ message: "the database is unavailable"
787
+ }] });
788
+ throw err;
789
+ }
790
+ return originalFetchJSON(url, args);
791
+ };
792
+ let result;
793
+ try {
794
+ result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
795
+ realm: testRealmURL,
796
+ roomId: "!room:example.com",
797
+ code: readCardScript("Pet/mango")
798
+ });
799
+ } finally {
800
+ delete cardService.fetchJSON;
801
+ }
802
+ assert.strictEqual(JSON.parse(result.scriptResult), `Unable to read card ${cardURL}: the database is unavailable`);
803
+ });
804
+ (0, import_qunit.test)("the tool description names the call that indexes a card that is not indexed, and rules out editing the card for it", function(assert) {
805
+ let { description } = new RunRealmCodeTool(getService("tool-service").toolContext);
806
+ assert.true(description.includes("realm.index.invalidate('<card path>.json')"), description);
807
+ assert.true(description.includes("never edit a card's data to make the workspace index it"), description);
808
+ });
809
+ (0, import_qunit.test)("replays a replacement against the source that was read", async function(assert) {
810
+ let toolService = getService("tool-service");
811
+ let cardService = getService("card-service");
812
+ let command = new RunRealmCodeTool(toolService.toolContext);
813
+ let fileUrl = `${testRealmURL}task.json`;
814
+ let result = await command.execute({
815
+ realm: testRealmURL,
816
+ roomId: "!room:example.com",
817
+ code: `await realm.fs.replace(${JSON.stringify(fileUrl)}, '"Old title"', '"New title"');`
818
+ });
819
+ assert.strictEqual(result.files[0]?.status, "saved");
820
+ let source = await cardService.getSource(new URL(fileUrl));
821
+ assert.strictEqual(source.content, `{
822
+ "title": "New title",
823
+ "count": 1
824
+ }
825
+ `);
826
+ });
827
+ (0, import_qunit.test)("creates a declared source file", async function(assert) {
828
+ let toolService = getService("tool-service");
829
+ let cardService = getService("card-service");
830
+ let command = new RunRealmCodeTool(toolService.toolContext);
831
+ let fileUrl = `${testRealmURL}new-task.json`;
832
+ let result = await command.execute({
833
+ realm: testRealmURL,
834
+ roomId: "!room:example.com",
835
+ code: `await realm.fs.writeText(${JSON.stringify(fileUrl)}, '{\\n "title": "New task"\\n}\\n');`
836
+ });
837
+ assert.strictEqual(result.files[0]?.status, "saved");
838
+ let source = await cardService.getSource(new URL(fileUrl));
839
+ assert.strictEqual(source.status, 200);
840
+ assert.strictEqual(source.content, "{\n \"title\": \"New task\"\n}\n");
841
+ });
842
+ (0, import_qunit.test)("realm.fs.writeBytes saves the decoded bytes of a new binary file", async function(assert) {
843
+ let toolService = getService("tool-service");
844
+ let network = getService("network");
845
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
846
+ realm: testRealmURL,
847
+ roomId: "!room:example.com",
848
+ code: `let saved = await realm.fs.writeBytes('images/dot.png', ${JSON.stringify(PNG_BASE64)});
849
+ return { saved, exists: await realm.fs.exists('images/dot.png') };`
850
+ });
851
+ let expected = Uint8Array.from(atob(PNG_BASE64), (char) => char.charCodeAt(0));
852
+ assert.deepEqual(JSON.parse(result.scriptResult), {
853
+ saved: {
854
+ path: "images/dot.png",
855
+ size: expected.byteLength,
856
+ saved: true
857
+ },
858
+ exists: true
859
+ });
860
+ assert.deepEqual(result.files.map((file) => [
861
+ file.fileUrl,
862
+ file.status,
863
+ file.detail
864
+ ]), [[
865
+ `${testRealmURL}images/dot.png`,
866
+ "saved",
867
+ "Binary file saved."
868
+ ]]);
869
+ let response = await network.authedFetch(new URL("images/dot.png", testRealmURL));
870
+ assert.strictEqual(response.status, 200);
871
+ assert.deepEqual(Array.from(new Uint8Array(await response.arrayBuffer())), Array.from(expected), "the realm holds the decoded bytes");
872
+ });
873
+ (0, import_qunit.test)("realm.fs.writeBytes accepts a base64 data URL", async function(assert) {
874
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
875
+ realm: testRealmURL,
876
+ roomId: "!room:example.com",
877
+ code: `return await realm.fs.writeBytes('dot.png', ${JSON.stringify(`data:image/png;base64,${PNG_BASE64}`)});`
878
+ });
879
+ assert.true(JSON.parse(result.scriptResult).saved);
880
+ });
881
+ (0, import_qunit.test)("realm.fs.writeBytes refuses a file that exists, and content that is not base64", async function(assert) {
882
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
883
+ await assert.rejects(command.execute({
884
+ realm: testRealmURL,
885
+ roomId: "!room:example.com",
886
+ code: `await realm.fs.writeBytes('task.json', ${JSON.stringify(PNG_BASE64)});`
887
+ }), /File already exists/, "refuses an existing text file");
888
+ await command.execute({
889
+ realm: testRealmURL,
890
+ roomId: "!room:example.com",
891
+ code: `await realm.fs.writeBytes('dot.png', ${JSON.stringify(PNG_BASE64)});`
892
+ });
893
+ await assert.rejects(command.execute({
894
+ realm: testRealmURL,
895
+ roomId: "!room:example.com",
896
+ code: `await realm.fs.writeBytes('dot.png', ${JSON.stringify(PNG_BASE64)});`
897
+ }), /File already exists/, "refuses an existing binary file");
898
+ await assert.rejects(command.execute({
899
+ realm: testRealmURL,
900
+ roomId: "!room:example.com",
901
+ code: `await realm.fs.writeBytes('bad.png', 'not base64!');`
902
+ }), /not valid base64/);
903
+ await assert.rejects(command.execute({
904
+ realm: testRealmURL,
905
+ roomId: "!room:example.com",
906
+ code: `await realm.fs.readText('dot.png');`
907
+ }), /is a binary file/, "readText names a binary file instead of failing on its status");
908
+ await command.execute({
909
+ realm: testRealmURL,
910
+ roomId: "!room:example.com",
911
+ code: `await realm.fs.writeBytes('report.xlsx', ${JSON.stringify(PNG_BASE64)});`
912
+ });
913
+ await assert.rejects(command.execute({
914
+ realm: testRealmURL,
915
+ roomId: "!room:example.com",
916
+ code: `await realm.fs.replace('report.xlsx', 'a', 'b');`
917
+ }), /is a binary file/, "replace refuses a vendor binary type");
918
+ });
919
+ (0, import_qunit.test)("realm.fs.writeBytes refuses a file over the size limit", async function(assert) {
920
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
921
+ await assert.rejects(command.execute({
922
+ realm: testRealmURL,
923
+ roomId: "!room:example.com",
924
+ code: `await realm.fs.writeBytes('big.bin', 'AAAA'.repeat(700000));`
925
+ }), /at most 2000000 bytes per file.*No file was saved/);
926
+ });
927
+ (0, import_qunit.test)("realm.fs.delete deletes a file the same run created, and the result reports it", async function(assert) {
928
+ let toolService = getService("tool-service");
929
+ let cardService = getService("card-service");
930
+ let command = new RunRealmCodeTool(toolService.toolContext);
931
+ let result = await command.execute({
932
+ realm: testRealmURL,
933
+ roomId: "!room:example.com",
934
+ code: `await realm.fs.writeText('plan.md', '# Plan\\n');
935
+ await realm.fs.writeText('kept.md', '# Kept\\n');
936
+ await realm.fs.writeBytes('dot.png', ${JSON.stringify(PNG_BASE64)});
937
+ let deleted = [
938
+ await realm.fs.delete('plan.md'),
939
+ await realm.fs.delete('dot.png'),
940
+ ];
941
+ return {
942
+ deleted,
943
+ planExists: await realm.fs.exists('plan.md'),
944
+ dotExists: await realm.fs.exists('dot.png'),
945
+ };`
946
+ });
947
+ assert.deepEqual(JSON.parse(result.scriptResult), {
948
+ deleted: [{
949
+ path: "plan.md",
950
+ deleted: true
951
+ }, {
952
+ path: "dot.png",
953
+ deleted: true
954
+ }],
955
+ planExists: false,
956
+ dotExists: false
957
+ });
958
+ assert.deepEqual(result.files.map((file) => [file.fileUrl, file.status]), [
959
+ [`${testRealmURL}kept.md`, "saved"],
960
+ [`${testRealmURL}plan.md`, "deleted"],
961
+ [`${testRealmURL}dot.png`, "deleted"]
962
+ ]);
963
+ assert.deepEqual(command.resultAttachments(result).map((attachment) => attachment.sourceUrl), [`${testRealmURL}kept.md`], "a deleted file is not attached to the result");
964
+ for (let path of ["plan.md", "dot.png"]) {
965
+ let source = await cardService.getSource(new URL(path, testRealmURL));
966
+ assert.strictEqual(source.status, 404, `${path} is not in the realm`);
967
+ }
968
+ });
969
+ (0, import_qunit.test)("realm.fs.delete refuses a binary file the run created when someone replaced it", async function(assert) {
970
+ let toolService = getService("tool-service");
971
+ let network = getService("network");
972
+ let command = new RunRealmCodeTool(toolService.toolContext);
973
+ let dotURL = `${testRealmURL}dot.png`;
974
+ let replacement = Uint8Array.from([
975
+ 137,
976
+ 80,
977
+ 78,
978
+ 71,
979
+ 255,
980
+ 254
981
+ ]);
982
+ let real = network.authedFetch;
983
+ Object.defineProperty(network, "authedFetch", {
984
+ configurable: true,
985
+ value: async (url, init) => {
986
+ let response = await real(url, init);
987
+ if (url === dotURL && init?.method === "POST" && response.ok) {
988
+ let replaced = await real(dotURL, {
989
+ method: "POST",
990
+ headers: { "Content-Type": "application/octet-stream" },
991
+ body: new Blob([replacement])
992
+ });
993
+ assert.true(replaced.ok, "the collaborator replaced the file");
994
+ }
995
+ return response;
996
+ }
997
+ });
998
+ let result = await command.execute({
999
+ realm: testRealmURL,
1000
+ roomId: "!room:example.com",
1001
+ code: `await realm.fs.writeBytes('dot.png', ${JSON.stringify(PNG_BASE64)});
1002
+ try {
1003
+ await realm.fs.delete('dot.png');
1004
+ return 'deleted';
1005
+ } catch (e) {
1006
+ return e.message;
1007
+ }`
1008
+ });
1009
+ assert.true(/File changed while the script ran: .*dot\.png/.test(JSON.parse(result.scriptResult)), `the delete is refused: ${result.scriptResult}`);
1010
+ let response = await real(dotURL, { headers: { Accept: SupportedMimeType.CardSource } });
1011
+ assert.strictEqual(response.status, 200, "dot.png is still in the realm");
1012
+ assert.deepEqual(Array.from(new Uint8Array(await response.arrayBuffer())), Array.from(replacement), "the collaborator's bytes are kept");
1013
+ });
1014
+ (0, import_qunit.test)("a script that deletes a file always waits for the user", function(assert) {
1015
+ assert.true(RunRealmCodeTool.neverAutoExecutesFor({ code: `await realm.fs.delete('notes/first.md');` }), "a run that names realm.fs.delete waits for a click");
1016
+ assert.true(RunRealmCodeTool.neverAutoExecutesFor({ code: `await realm.fs.writeText('a.md', 'x'); await realm.fs.delete('a.md');` }), "a run that deletes only its own file also waits for a click");
1017
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code: `await realm.fs.writeText('a.md', 'x');` }), "a run that deletes nothing keeps the room mode");
1018
+ });
1019
+ (0, import_qunit.test)("realm.fs.delete deletes files that were there before the run, and the result reports them", async function(assert) {
1020
+ let toolService = getService("tool-service");
1021
+ let cardService = getService("card-service");
1022
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
1023
+ realm: testRealmURL,
1024
+ roomId: "!room:example.com",
1025
+ code: `await realm.fs.readText('notes/first.md');
1026
+ await realm.fs.replace('task.json', '"Old title"', '"New title"');
1027
+ await realm.fs.writeText('plan.md', '# Plan\\n');
1028
+ let deleted = [
1029
+ await realm.fs.delete('notes/first.md'),
1030
+ await realm.fs.delete('task.json'),
1031
+ await realm.fs.delete('images/existing.png'),
1032
+ await realm.fs.delete('plan.md'),
1033
+ ];
1034
+ let refused = [];
1035
+ try { await realm.fs.delete('notes/first.md'); } catch (e) { refused.push(e.message); }
1036
+ try { await realm.fs.delete('missing.md'); } catch (e) { refused.push(e.message); }
1037
+ try { await realm.fs.delete('notes/'); } catch (e) { refused.push(e.message); }
1038
+ return { deleted, refused };`
1039
+ });
1040
+ let { deleted, refused } = JSON.parse(result.scriptResult);
1041
+ assert.deepEqual(deleted, [
1042
+ {
1043
+ path: "notes/first.md",
1044
+ deleted: true
1045
+ },
1046
+ {
1047
+ path: "task.json",
1048
+ deleted: true
1049
+ },
1050
+ {
1051
+ path: "images/existing.png",
1052
+ deleted: true
1053
+ },
1054
+ {
1055
+ path: "plan.md",
1056
+ deleted: true
1057
+ }
1058
+ ]);
1059
+ assert.true(/this run already deleted it: .*notes\/first\.md/.test(refused[0]), `a file the run already deleted: ${refused[0]}`);
1060
+ assert.true(/File not found: .*missing\.md/.test(refused[1]), `a file that does not exist: ${refused[1]}`);
1061
+ assert.true(/Path must be relative to the realm root: notes\//.test(refused[2]), `a folder: ${refused[2]}`);
1062
+ assert.deepEqual(result.files.map((file) => [
1063
+ file.fileUrl,
1064
+ file.status,
1065
+ file.detail
1066
+ ]), [
1067
+ [
1068
+ `${testRealmURL}plan.md`,
1069
+ "deleted",
1070
+ "Created by this run and then deleted."
1071
+ ],
1072
+ [
1073
+ `${testRealmURL}notes/first.md`,
1074
+ "deleted",
1075
+ "Existed before this run; deleted."
1076
+ ],
1077
+ [
1078
+ `${testRealmURL}task.json`,
1079
+ "deleted",
1080
+ "Existed before this run; deleted."
1081
+ ],
1082
+ [
1083
+ `${testRealmURL}images/existing.png`,
1084
+ "deleted",
1085
+ "Existed before this run; deleted."
1086
+ ]
1087
+ ]);
1088
+ for (let path of [
1089
+ "notes/first.md",
1090
+ "task.json",
1091
+ "images/existing.png"
1092
+ ]) {
1093
+ let source = await cardService.getSource(new URL(path, testRealmURL));
1094
+ assert.strictEqual(source.status, 404, `${path} is not in the realm`);
1095
+ }
1096
+ });
1097
+ (0, import_qunit.test)("realm.fs.delete refuses a file someone changed after the script read it", async function(assert) {
1098
+ let toolService = getService("tool-service");
1099
+ let cardService = getService("card-service");
1100
+ let network = getService("network");
1101
+ let command = new RunRealmCodeTool(toolService.toolContext);
1102
+ let noteURL = `${testRealmURL}notes/first.md`;
1103
+ let real = network.authedFetch;
1104
+ let changed = false;
1105
+ Object.defineProperty(network, "authedFetch", {
1106
+ configurable: true,
1107
+ value: async (url, init) => {
1108
+ let response = await real(url, init);
1109
+ if (url === noteURL && !init?.method && !changed) {
1110
+ changed = true;
1111
+ let replaced = await real(noteURL, {
1112
+ method: "POST",
1113
+ headers: { "Content-Type": "application/octet-stream" },
1114
+ body: new Blob(["# Changed\n"])
1115
+ });
1116
+ assert.true(replaced.ok, "the collaborator changed the file");
1117
+ }
1118
+ return response;
1119
+ }
1120
+ });
1121
+ let result = await command.execute({
1122
+ realm: testRealmURL,
1123
+ roomId: "!room:example.com",
1124
+ code: `await realm.fs.readText('notes/first.md');
1125
+ try {
1126
+ await realm.fs.delete('notes/first.md');
1127
+ return 'deleted';
1128
+ } catch (e) {
1129
+ return e.message;
1130
+ }`
1131
+ });
1132
+ assert.true(/File changed while the script ran: .*notes\/first\.md/.test(JSON.parse(result.scriptResult)), `the delete is refused: ${result.scriptResult}`);
1133
+ let source = await cardService.getSource(new URL(noteURL));
1134
+ assert.strictEqual(source.status, 200, "the file is still in the realm");
1135
+ assert.strictEqual(source.content, "# Changed\n", "the change is kept");
1136
+ });
1137
+ (0, import_qunit.test)("a script that reaches realm.fs.delete another way is refused", async function(assert) {
1138
+ let cardService = getService("card-service");
1139
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1140
+ let code = `await realm.fs['de' + 'lete']('notes/first.md');`;
1141
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code }), "this run would not wait for a click");
1142
+ await assert.rejects(command.execute({
1143
+ realm: testRealmURL,
1144
+ roomId: "!room:example.com",
1145
+ code
1146
+ }), /must be called by that name/);
1147
+ let source = await cardService.getSource(new URL("notes/first.md", testRealmURL));
1148
+ assert.strictEqual(source.status, 200, "the file is still in the realm");
1149
+ });
1150
+ (0, import_qunit.test)("a failed run names the files from before the run that it deleted", async function(assert) {
1151
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1152
+ await assert.rejects(command.execute({
1153
+ realm: testRealmURL,
1154
+ roomId: "!room:example.com",
1155
+ code: `await realm.fs.delete('notes/first.md');
1156
+ await realm.fs.readText('missing.md');`
1157
+ }), /Files that existed before this run and that it already deleted: .*notes\/first\.md/);
1158
+ });
1159
+ (0, import_qunit.test)("a failed run names the files it created and deleted", async function(assert) {
1160
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1161
+ await assert.rejects(command.execute({
1162
+ realm: testRealmURL,
1163
+ roomId: "!room:example.com",
1164
+ code: `await realm.fs.writeText('plan.md', 'x');
1165
+ await realm.fs.delete('plan.md');
1166
+ await realm.fs.readText('missing.md');`
1167
+ }), /No file was saved\. Files this run created and already deleted: .*plan\.md/);
1168
+ });
1169
+ (0, import_qunit.test)("realm.fs.copy copies a file and leaves the source as it was", async function(assert) {
1170
+ let toolService = getService("tool-service");
1171
+ let cardService = getService("card-service");
1172
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
1173
+ realm: testRealmURL,
1174
+ roomId: "!room:example.com",
1175
+ code: `return await realm.fs.copy('notes/first.md', 'notes/second.md');`
1176
+ });
1177
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1178
+ path: "notes/second.md",
1179
+ saved: true
1180
+ });
1181
+ assert.deepEqual(result.files.map((file) => [file?.fileUrl, file?.status]), [[`${testRealmURL}notes/second.md`, "saved"]]);
1182
+ let copy = await cardService.getSource(new URL(`${testRealmURL}notes/second.md`));
1183
+ assert.strictEqual(copy.content, "# First\n");
1184
+ let source = await cardService.getSource(new URL(`${testRealmURL}notes/first.md`));
1185
+ assert.strictEqual(source.content, "# First\n", "the source is unchanged");
1186
+ });
1187
+ (0, import_qunit.test)("realm.fs.copy of a card instance in the same folder loads as the same card type", async function(assert) {
1188
+ let toolService = getService("tool-service");
1189
+ let store = getService("store");
1190
+ await new RunRealmCodeTool(toolService.toolContext).execute({
1191
+ realm: testRealmURL,
1192
+ roomId: "!room:example.com",
1193
+ code: `await realm.fs.copy('Pet/mango.json', 'Pet/mango-copy.json');
1194
+ await realm.fs.replace('Pet/mango-copy.json', '"Mango"', '"Mango Two"');`
1195
+ });
1196
+ let copy = await store.get(`${testRealmURL}Pet/mango-copy`);
1197
+ assert.strictEqual(copy.constructor.displayName, "Pet");
1198
+ assert.strictEqual(copy.name, "Mango Two");
1199
+ });
1200
+ (0, import_qunit.test)("realm.fs.copy refuses an existing target, a missing source, and paths outside the realm", async function(assert) {
1201
+ let toolService = getService("tool-service");
1202
+ let cardService = getService("card-service");
1203
+ let command = new RunRealmCodeTool(toolService.toolContext);
1204
+ let run = (code) => command.execute({
1205
+ realm: testRealmURL,
1206
+ roomId: "!room:example.com",
1207
+ code
1208
+ });
1209
+ await assert.rejects(run(`await realm.fs.copy('notes/first.md', 'task.json');`), /File already exists: .*task\.json/);
1210
+ let target = await cardService.getSource(new URL(`${testRealmURL}task.json`));
1211
+ assert.true(target.content.includes("\"Old title\""), "the existing target is unchanged");
1212
+ await assert.rejects(run(`await realm.fs.copy('nope.md', 'copy.md');`), /File not found: .*nope\.md/);
1213
+ await assert.rejects(run(`await realm.fs.copy('notes/first.md', '../elsewhere.md');`), /Path must be relative to the realm root/);
1214
+ await assert.rejects(run(`await realm.fs.copy('https://example.com/other/first.md', 'copy.md');`), /Path is outside this realm/);
1215
+ });
1216
+ (0, import_qunit.test)("realm.fs.copy refuses files that are not UTF-8 text", async function(assert) {
1217
+ let toolService = getService("tool-service");
1218
+ let cardService = getService("card-service");
1219
+ let command = new RunRealmCodeTool(toolService.toolContext);
1220
+ for (let [from, to] of [
1221
+ ["images/existing.png", "images/existing-copy.png"],
1222
+ ["docs/report.docx", "docs/report-copy.docx"],
1223
+ ["docs/latin1.txt", "docs/latin1-copy.txt"]
1224
+ ]) {
1225
+ await assert.rejects(command.execute({
1226
+ realm: testRealmURL,
1227
+ roomId: "!room:example.com",
1228
+ code: `await realm.fs.copy('${from}', '${to}');`
1229
+ }), /is a binary file/, `refuses ${from}`);
1230
+ let copy = await cardService.getSource(new URL(`${testRealmURL}${to}`));
1231
+ assert.strictEqual(copy.status, 404, `no copy of ${from} is saved`);
1232
+ }
1233
+ });
1234
+ (0, import_qunit.test)("realm.fs.copy copies UTF-8 text byte for byte, whatever its content type", async function(assert) {
1235
+ let toolService = getService("tool-service");
1236
+ let network = getService("network");
1237
+ let command = new RunRealmCodeTool(toolService.toolContext);
1238
+ let bytesOf = async (path) => {
1239
+ let response = await network.authedFetch(`${testRealmURL}${path}`, { headers: { Accept: "application/vnd.card+source" } });
1240
+ return Array.from(new Uint8Array(await response.arrayBuffer()));
1241
+ };
1242
+ await command.execute({
1243
+ realm: testRealmURL,
1244
+ roomId: "!room:example.com",
1245
+ code: `await realm.fs.copy('docs/README', 'docs/README-copy');
1246
+ await realm.fs.copy('docs/replacement.txt', 'docs/replacement-copy.txt');
1247
+ await realm.fs.copy('unformatted.gts', 'unformatted-copy.gts');`
1248
+ });
1249
+ for (let path of [
1250
+ "docs/README",
1251
+ "docs/replacement.txt",
1252
+ "unformatted.gts"
1253
+ ]) {
1254
+ let copyPath = path.replace(/(\.[^./]+)?$/, "-copy$1");
1255
+ assert.deepEqual(await bytesOf(copyPath), await bytesOf(path), `${copyPath} has the same bytes as ${path}`);
1256
+ }
1257
+ });
1258
+ (0, import_qunit.test)("a binary file exists, and is not overwritten", async function(assert) {
1259
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1260
+ let run = (code) => command.execute({
1261
+ realm: testRealmURL,
1262
+ roomId: "!room:example.com",
1263
+ code
1264
+ });
1265
+ let result = await run(`return await realm.fs.exists('images/existing.png');`);
1266
+ assert.true(JSON.parse(result.scriptResult), "exists returns true");
1267
+ await assert.rejects(run(`await realm.fs.copy('notes/first.md', 'images/existing.png');`), /File already exists: .*existing\.png/);
1268
+ await assert.rejects(run(`await realm.fs.writeText('images/existing.png', 'text');`), /File already exists; use realm\.fs\.replace/);
1269
+ });
1270
+ (0, import_qunit.module)("realm.fs.download", function(hooks) {
1271
+ const sourceURL = "https://media.example/images/pet.png";
1272
+ let pngBytes = Uint8Array.from(atob(PNG_BASE64), (char) => char.charCodeAt(0));
1273
+ let sourceRequests;
1274
+ hooks.beforeEach(function() {
1275
+ sourceRequests = [];
1276
+ getService("network").virtualNetwork.mount(async (req) => {
1277
+ if (!req.url.startsWith("https://media.example/")) return null;
1278
+ sourceRequests.push(req.url);
1279
+ if (req.url === sourceURL) return new Response(pngBytes, {
1280
+ status: 200,
1281
+ headers: { "Content-Type": "image/png" }
1282
+ });
1283
+ if (req.url.endsWith("/nine.bin")) return new Response(new Uint8Array(9e6), {
1284
+ status: 200,
1285
+ headers: { "Content-Type": "application/octet-stream" }
1286
+ });
1287
+ if (req.url.endsWith("/large.bin")) return new Response(new Uint8Array(10000001), {
1288
+ status: 200,
1289
+ headers: { "Content-Type": "application/octet-stream" }
1290
+ });
1291
+ return new Response("Not found", { status: 404 });
1292
+ }, { prepend: true });
1293
+ });
1294
+ (0, import_qunit.test)("saves the file at a URL into the realm", async function(assert) {
1295
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1296
+ let code = `return await realm.fs.download('${sourceURL}', 'images/pet.png');`;
1297
+ assert.true(RunRealmCodeTool.neverAutoExecutesFor({ code }), "a run that names realm.fs.download waits for a click");
1298
+ let result = await command.execute({
1299
+ realm: testRealmURL,
1300
+ roomId: "!room:example.com",
1301
+ code
1302
+ });
1303
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1304
+ path: "images/pet.png",
1305
+ size: pngBytes.byteLength,
1306
+ saved: true
1307
+ });
1308
+ assert.deepEqual(result.files.map((file) => [
1309
+ file.fileUrl,
1310
+ file.status,
1311
+ file.detail
1312
+ ]), [[
1313
+ `${testRealmURL}images/pet.png`,
1314
+ "saved",
1315
+ "Binary file saved."
1316
+ ]]);
1317
+ let response = await getService("network").authedFetch(new URL("images/pet.png", testRealmURL));
1318
+ assert.strictEqual(response.status, 200);
1319
+ assert.deepEqual(Array.from(new Uint8Array(await response.arrayBuffer())), Array.from(pngBytes), "the realm holds the downloaded bytes");
1320
+ });
1321
+ (0, import_qunit.test)("realm.fs.delete removes a file the same run downloaded", async function(assert) {
1322
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1323
+ realm: testRealmURL,
1324
+ roomId: "!room:example.com",
1325
+ code: `await realm.fs.download('${sourceURL}', 'images/pet.png');
1326
+ return {
1327
+ deleted: await realm.fs.delete('images/pet.png'),
1328
+ exists: await realm.fs.exists('images/pet.png'),
1329
+ };`
1330
+ });
1331
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1332
+ deleted: {
1333
+ path: "images/pet.png",
1334
+ deleted: true
1335
+ },
1336
+ exists: false
1337
+ });
1338
+ assert.deepEqual(result.files.map((file) => [file.fileUrl, file.status]), [[`${testRealmURL}images/pet.png`, "deleted"]]);
1339
+ });
1340
+ (0, import_qunit.test)("refuses an existing file, a path with no extension, a source that is not http, and a failed download", async function(assert) {
1341
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1342
+ let run = (code) => command.execute({
1343
+ realm: testRealmURL,
1344
+ roomId: "!room:example.com",
1345
+ code
1346
+ });
1347
+ await assert.rejects(run(`await realm.fs.download('${sourceURL}', 'task.json');`), /File already exists/);
1348
+ await assert.rejects(run(`await realm.fs.download('${sourceURL}', 'images/pet');`), /needs a path with a file extension/);
1349
+ await assert.rejects(run(`await realm.fs.download('file:///etc/hosts', 'hosts.txt');`), /downloads only http and https URLs/);
1350
+ assert.deepEqual(sourceRequests, [], "nothing was fetched so far");
1351
+ await assert.rejects(run(`await realm.fs.download('https://media.example/missing.png', 'missing.png');`), /Unable to download https:\/\/media\.example\/missing\.png: 404.*No file was saved/);
1352
+ });
1353
+ (0, import_qunit.test)("refuses a file over the size limit", async function(assert) {
1354
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1355
+ await assert.rejects(command.execute({
1356
+ realm: testRealmURL,
1357
+ roomId: "!room:example.com",
1358
+ code: `await realm.fs.download('https://media.example/large.bin', 'large.bin');`
1359
+ }), /at most 10000000 bytes per file.*No file was saved/);
1360
+ });
1361
+ (0, import_qunit.test)("a download whose save fails still counts toward the per-run limit", async function(assert) {
1362
+ getService("network").virtualNetwork.mount(async (req) => {
1363
+ if (req.method === "POST" && req.url.startsWith(`${testRealmURL}failing/`)) return new Response("Server error", { status: 500 });
1364
+ return null;
1365
+ }, { prepend: true });
1366
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1367
+ realm: testRealmURL,
1368
+ roomId: "!room:example.com",
1369
+ code: `let errors = [];
1370
+ for (let name of ['a', 'b', 'c']) {
1371
+ try {
1372
+ await realm.fs.download('https://media.example/nine.bin', 'failing/' + name + '.bin');
1373
+ } catch (error) {
1374
+ errors.push(error.message);
1375
+ }
1376
+ }
1377
+ return errors;`
1378
+ });
1379
+ let errors = JSON.parse(result.scriptResult);
1380
+ assert.strictEqual(errors.length, 3, "no save succeeded");
1381
+ assert.notOk(/per run/.test(errors[0]), errors[0]);
1382
+ assert.notOk(/per run/.test(errors[1]), errors[1]);
1383
+ assert.ok(/at most 25000000 bytes per run/.test(errors[2]), "the third download is over the bytes the failed saves left");
1384
+ });
1385
+ (0, import_qunit.test)("a download the script did not await fails the run and is not fetched", async function(assert) {
1386
+ getService("network").virtualNetwork.mount(async (req) => {
1387
+ if (req.method === "GET" && req.url === `${testRealmURL}late.png`) await new Promise((resolve) => setTimeout(resolve, 1e3));
1388
+ return null;
1389
+ }, { prepend: true });
1390
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1391
+ await assert.rejects(command.execute({
1392
+ realm: testRealmURL,
1393
+ roomId: "!room:example.com",
1394
+ code: `realm.fs.download('${sourceURL}', 'late.png');
1395
+ return 'done';`
1396
+ }), /await every realm call/);
1397
+ assert.deepEqual(sourceRequests, [], "nothing is fetched");
1398
+ let response = await getService("network").authedFetch(new URL("late.png", testRealmURL));
1399
+ assert.strictEqual(response.status, 404, "nothing is saved");
1400
+ });
1401
+ (0, import_qunit.test)("a script that reaches realm.fs.download another way is refused", async function(assert) {
1402
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1403
+ let code = `await realm.fs['down' + 'load']('${sourceURL}', 'pet.png');`;
1404
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code }), "this run would not wait for a click");
1405
+ await assert.rejects(command.execute({
1406
+ realm: testRealmURL,
1407
+ roomId: "!room:example.com",
1408
+ code
1409
+ }), /realm\.fs\.download must be called by that name/);
1410
+ assert.deepEqual(sourceRequests, [], "nothing is fetched");
1411
+ });
1412
+ });
1413
+ (0, import_qunit.test)("resolves paths against the realm root", async function(assert) {
1414
+ let toolService = getService("tool-service");
1415
+ let cardService = getService("card-service");
1416
+ let command = new RunRealmCodeTool(toolService.toolContext);
1417
+ let fileUrl = `${testRealmURL}task.json`;
1418
+ let result = await command.execute({
1419
+ realm: testRealmURL,
1420
+ roomId: "!room:example.com",
1421
+ code: `return await realm.fs.replace('task.json', '"count": 1', '"count": 2');`
1422
+ });
1423
+ assert.strictEqual(result.files[0]?.status, "saved");
1424
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1425
+ path: "task.json",
1426
+ matches: 1,
1427
+ saved: true
1428
+ });
1429
+ let source = await cardService.getSource(new URL(fileUrl));
1430
+ assert.true(source.content.includes("\"count\": 2"));
1431
+ });
1432
+ (0, import_qunit.test)("resolves paths against the realm root named without its trailing slash", async function(assert) {
1433
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1434
+ realm: testRealmURL.replace(/\/$/, ""),
1435
+ roomId: "!room:example.com",
1436
+ code: `return await realm.fs.readText('task.json');`
1437
+ });
1438
+ assert.true(JSON.parse(result.scriptResult).includes("\"count\""));
1439
+ });
1440
+ (0, import_qunit.test)("refuses a folder in the realm as the realm, and names the root and the path to use", async function(assert) {
1441
+ let toolService = getService("tool-service");
1442
+ let cardService = getService("card-service");
1443
+ let command = new RunRealmCodeTool(toolService.toolContext);
1444
+ let folder = `${testRealmURL}card-mutation/relink/`;
1445
+ for (let realm of [folder, folder.replace(/\/$/, "")]) await assert.rejects(command.execute({
1446
+ realm,
1447
+ roomId: "!room:example.com",
1448
+ code: `return await realm.fs.replace('task.json', '"count": 1', '"count": 2');`
1449
+ }), (error) => error.message === `${realm} is card-mutation/relink inside the workspace ${testRealmURL}, not its root. Pass ${testRealmURL} as realm, and name each file by its path from there: card-mutation/relink for that file, card-mutation/relink.json for that card, or card-mutation/relink/ and the file's name for a file in that folder`);
1450
+ let source = await cardService.getSource(new URL(`${testRealmURL}task.json`));
1451
+ assert.true(source.content.includes("\"count\": 1"), "the script does not run against the root");
1452
+ });
1453
+ (0, import_qunit.test)("refuses a file or card in the realm as the realm, and names it by its path from the root", async function(assert) {
1454
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1455
+ for (let local of [
1456
+ "Event/intro-to-pottery.json",
1457
+ "Event/intro-to-pottery",
1458
+ "docs/README"
1459
+ ]) {
1460
+ let realm = `${testRealmURL}${local}`;
1461
+ await assert.rejects(command.execute({
1462
+ realm,
1463
+ roomId: "!room:example.com",
1464
+ code: `return 'ran';`
1465
+ }), (error) => error.message === `${realm} is ${local} inside the workspace ${testRealmURL}, not its root. Pass ${testRealmURL} as realm, and name each file by its path from there: ${local} for that file, ${local}.json for that card, or ${local}/ and the file's name for a file in that folder`);
1466
+ }
1467
+ });
1468
+ (0, import_qunit.test)("refuses a realm with a malformed percent-encoding as one it cannot reach", async function(assert) {
1469
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1470
+ let realm = `${testRealmURL}folder%E0/`;
1471
+ await assert.rejects(command.execute({
1472
+ realm,
1473
+ roomId: "!room:example.com",
1474
+ code: `return 'ran';`
1475
+ }), (error) => error.message === `The current user cannot read or write ${realm}`);
1476
+ });
1477
+ (0, import_qunit.test)("reads files and cards named by the prefix of the realm it runs in", async function(assert) {
1478
+ let { virtualNetwork } = getService("network");
1479
+ let prefix = "@test/path-prefix/";
1480
+ virtualNetwork.addRealmMapping(prefix, testRealmURL);
1481
+ try {
1482
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1483
+ let result = await command.execute({
1484
+ realm: testRealmURL,
1485
+ roomId: "!room:example.com",
1486
+ code: `let byPrefix = await realm.cards.get('${prefix}people/hassan');
1487
+ let byURL = await realm.cards.get(${JSON.stringify(`${testRealmURL}people/hassan`)});
1488
+ let listByPrefix = await realm.fs.list('${prefix}people/');
1489
+ let listByURL = await realm.fs.list(${JSON.stringify(`${testRealmURL}people/`)});
1490
+ let rootByPrefix = await realm.fs.list('${prefix}');
1491
+ let rootByURL = await realm.fs.list('');
1492
+ return { byPrefix, byURL, listByPrefix, listByURL, rootByPrefix, rootByURL };`
1493
+ });
1494
+ let { byPrefix, byURL, listByPrefix, listByURL, rootByPrefix, rootByURL } = JSON.parse(result.scriptResult);
1495
+ assert.deepEqual(byPrefix, byURL, "a card reads the same by prefix");
1496
+ assert.deepEqual(listByPrefix, listByURL, "a folder lists the same by prefix");
1497
+ assert.deepEqual(rootByPrefix, rootByURL, "the realm root lists the same by prefix");
1498
+ await assert.rejects(command.execute({
1499
+ realm: testRealmURL,
1500
+ roomId: "!room:example.com",
1501
+ code: `await realm.cards.get('@test/unregistered/people/hassan');`
1502
+ }), /Path is outside this realm/);
1503
+ } finally {
1504
+ virtualNetwork.removeRealmMapping(prefix);
1505
+ }
1506
+ });
1507
+ (0, import_qunit.test)("refuses a folder of a realm named by a prefix, and names the root by that prefix", async function(assert) {
1508
+ let { virtualNetwork } = getService("network");
1509
+ let prefix = "@test/folder-input/";
1510
+ virtualNetwork.addRealmMapping(prefix, testRealmURL);
1511
+ try {
1512
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1513
+ let folder = `${prefix}card-mutation/relink/`;
1514
+ await assert.rejects(command.execute({
1515
+ realm: folder,
1516
+ roomId: "!room:example.com",
1517
+ code: `return 'ran';`
1518
+ }), (error) => error.message === `${folder} is card-mutation/relink inside the workspace ${prefix}, not its root. Pass ${prefix} as realm, and name each file by its path from there: card-mutation/relink for that file, card-mutation/relink.json for that card, or card-mutation/relink/ and the file's name for a file in that folder`);
1519
+ } finally {
1520
+ virtualNetwork.removeRealmMapping(prefix);
1521
+ }
1522
+ });
1523
+ (0, import_qunit.test)("runs in the realm root named with a query string", async function(assert) {
1524
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1525
+ realm: `${testRealmURL}?x=1`,
1526
+ roomId: "!room:example.com",
1527
+ code: `return await realm.fs.readText('task.json');`
1528
+ });
1529
+ assert.true(JSON.parse(result.scriptResult).includes("\"count\""));
1530
+ });
1531
+ (0, import_qunit.test)("runs in a workspace nested under the realm, learned after it", async function(assert) {
1532
+ let nestedRealmURL = `${testRealmURL}nested/`;
1533
+ knowReadableWorkspace(nestedRealmURL);
1534
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1535
+ realm: nestedRealmURL,
1536
+ roomId: "!room:example.com",
1537
+ code: `return 'ran';`
1538
+ });
1539
+ assert.strictEqual(JSON.parse(result.scriptResult), "ran");
1540
+ });
1541
+ (0, import_qunit.test)("asks a workspace nested under the realm for its own permissions", async function(assert) {
1542
+ let nestedRealmURL = `${testRealmURL}nested/`;
1543
+ knowReadableWorkspace(nestedRealmURL, []);
1544
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1545
+ await assert.rejects(command.execute({
1546
+ realm: nestedRealmURL,
1547
+ roomId: "!room:example.com",
1548
+ code: `return 'ran';`
1549
+ }), (error) => error.message === `The current user cannot read or write ${nestedRealmURL}`, "the readable outer realm does not answer for the nested one");
1550
+ });
1551
+ (0, import_qunit.test)("refuses paths outside the realm, and names the files it already saved", async function(assert) {
1552
+ let toolService = getService("tool-service");
1553
+ let cardService = getService("card-service");
1554
+ let command = new RunRealmCodeTool(toolService.toolContext);
1555
+ let fileUrl = `${testRealmURL}task.json`;
1556
+ await assert.rejects(command.execute({
1557
+ realm: testRealmURL,
1558
+ roomId: "!room:example.com",
1559
+ code: `await realm.fs.replace('task.json', '"count": 1', '"count": 2');
1560
+ await realm.fs.writeText('../elsewhere.json', '{}');`
1561
+ }), /Path must be relative to the realm root.*Files already saved by this run: .*task\.json/);
1562
+ let source = await cardService.getSource(new URL(fileUrl));
1563
+ assert.true(source.content.includes("\"count\": 2"));
1564
+ });
1565
+ (0, import_qunit.test)("reads a file inside the script and edits it from what it read", async function(assert) {
1566
+ let toolService = getService("tool-service");
1567
+ let cardService = getService("card-service");
1568
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
1569
+ realm: testRealmURL,
1570
+ roomId: "!room:example.com",
1571
+ code: `const task = JSON.parse(await realm.fs.readText('task.json'));
1572
+ await realm.fs.replace('task.json', '"count": ' + task.count, '"count": ' + (task.count + 1));
1573
+ return { exists: await realm.fs.exists('task.json'), missing: await realm.fs.exists('nope.json') };`
1574
+ });
1575
+ assert.strictEqual(result.files[0]?.status, "saved");
1576
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1577
+ exists: true,
1578
+ missing: false
1579
+ });
1580
+ let source = await cardService.getSource(new URL(`${testRealmURL}task.json`));
1581
+ assert.true(source.content.includes("\"count\": 2"));
1582
+ });
1583
+ (0, import_qunit.test)("each write is saved before the script goes on", async function(assert) {
1584
+ let toolService = getService("tool-service");
1585
+ let cardService = getService("card-service");
1586
+ let command = new RunRealmCodeTool(toolService.toolContext);
1587
+ await assert.rejects(command.execute({
1588
+ realm: testRealmURL,
1589
+ roomId: "!room:example.com",
1590
+ code: `await realm.fs.writeText('first.json', '{}');
1591
+ throw new Error('stop here');`
1592
+ }), /stop here.*Files already saved by this run: .*first\.json/);
1593
+ let source = await cardService.getSource(new URL(`${testRealmURL}first.json`));
1594
+ assert.strictEqual(source.status, 200);
1595
+ });
1596
+ (0, import_qunit.test)("a thrown message that ends a sentence gets no second period", async function(assert) {
1597
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1598
+ await assert.rejects(command.execute({
1599
+ realm: testRealmURL,
1600
+ roomId: "!room:example.com",
1601
+ code: `throw new Error('Could not find the styles.');`
1602
+ }), /Could not find the styles\. No file was saved\./);
1603
+ });
1604
+ (0, import_qunit.test)("a caught failed write does not fail the run", async function(assert) {
1605
+ let toolService = getService("tool-service");
1606
+ let cardService = getService("card-service");
1607
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
1608
+ realm: testRealmURL,
1609
+ roomId: "!room:example.com",
1610
+ code: `await realm.fs.replace('task.json', '"count": 1', '"count": 2');
1611
+ try { await realm.fs.replace('task.json', 'not there', 'x'); } catch (e) {}
1612
+ return 'done';`
1613
+ });
1614
+ assert.strictEqual(result.files.length, 1);
1615
+ assert.strictEqual(result.files[0]?.status, "saved");
1616
+ let source = await cardService.getSource(new URL(`${testRealmURL}task.json`));
1617
+ assert.true(source.content.includes("\"count\": 2"));
1618
+ });
1619
+ (0, import_qunit.test)("a caught failed read does not fail the run", async function(assert) {
1620
+ let toolService = getService("tool-service");
1621
+ let cardService = getService("card-service");
1622
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
1623
+ realm: testRealmURL,
1624
+ roomId: "!room:example.com",
1625
+ code: `let found = true;
1626
+ try { await realm.fs.readText('new-task.json'); } catch (e) { found = false; }
1627
+ if (!found) await realm.fs.writeText('new-task.json', '{}');
1628
+ return found;`
1629
+ });
1630
+ assert.strictEqual(result.files[0]?.status, "saved");
1631
+ let source = await cardService.getSource(new URL(`${testRealmURL}new-task.json`));
1632
+ assert.strictEqual(source.status, 200);
1633
+ });
1634
+ (0, import_qunit.test)("a realm call the script does not await fails the run, and the report says whether it saved", async function(assert) {
1635
+ let toolService = getService("tool-service");
1636
+ let cardService = getService("card-service");
1637
+ let command = new RunRealmCodeTool(toolService.toolContext);
1638
+ let error;
1639
+ try {
1640
+ await command.execute({
1641
+ realm: testRealmURL,
1642
+ roomId: "!room:example.com",
1643
+ code: `realm.fs.writeText('late.json', '{}');`
1644
+ });
1645
+ } catch (e) {
1646
+ error = e;
1647
+ }
1648
+ assert.ok(error, "the run rejected");
1649
+ assert.true(/await every realm call/.test(error?.message ?? ""), `the error says to await every realm call: ${error?.message}`);
1650
+ let reportedSaved = /Files already saved by this run: .*late\.json/.test(error?.message ?? "");
1651
+ let reportedNoneSaved = /No file was saved/.test(error?.message ?? "");
1652
+ assert.notStrictEqual(reportedSaved, reportedNoneSaved, `the report says either that late.json was saved or that nothing was: ${error?.message}`);
1653
+ await settled();
1654
+ let source = await cardService.getSource(new URL(`${testRealmURL}late.json`));
1655
+ assert.strictEqual(source.status, reportedSaved ? 200 : 404, reportedSaved ? "the file the report names as saved is in the realm" : "the report said nothing was saved, and nothing landed after it");
1656
+ });
1657
+ (0, import_qunit.test)("a script that never returns is stopped by the sandbox, not by the caller", async function(assert) {
1658
+ let error;
1659
+ try {
1660
+ await runRealmCode({
1661
+ code: "while (true) {}",
1662
+ realmURL: testRealmURL,
1663
+ timeoutMs: 500
1664
+ }, async () => null);
1665
+ } catch (e) {
1666
+ error = e;
1667
+ }
1668
+ assert.ok(error, "the run rejected");
1669
+ assert.strictEqual(error?.message, "interrupted");
1670
+ });
1671
+ (0, import_qunit.test)("lists a directory without counting toward the file limit", async function(assert) {
1672
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1673
+ let result = await command.execute({
1674
+ realm: testRealmURL,
1675
+ roomId: "!room:example.com",
1676
+ code: `return {
1677
+ root: await realm.fs.list(),
1678
+ notes: await realm.fs.list('notes'),
1679
+ notesWithSlash: await realm.fs.list('notes/'),
1680
+ notesByURL: await realm.fs.list(${JSON.stringify(`${testRealmURL}notes/`)}),
1681
+ };`
1682
+ });
1683
+ assert.deepEqual(result.files, [], "a listing saves nothing");
1684
+ let listed = JSON.parse(result.scriptResult);
1685
+ assert.deepEqual(listed.root.find((entry) => entry.name === "task.json"), {
1686
+ name: "task.json",
1687
+ path: "task.json",
1688
+ kind: "file"
1689
+ });
1690
+ assert.deepEqual(listed.root.find((entry) => entry.name === "notes"), {
1691
+ name: "notes",
1692
+ path: "notes/",
1693
+ kind: "directory"
1694
+ });
1695
+ let notes = [{
1696
+ name: "first.md",
1697
+ path: "notes/first.md",
1698
+ kind: "file"
1699
+ }];
1700
+ assert.deepEqual(listed.notes, notes);
1701
+ assert.deepEqual(listed.notesWithSlash, notes);
1702
+ assert.deepEqual(listed.notesByURL, notes);
1703
+ await assert.rejects(command.execute({
1704
+ realm: testRealmURL,
1705
+ roomId: "!room:example.com",
1706
+ code: `await realm.fs.list('../');`
1707
+ }), /Path must be relative to the realm root/);
1708
+ await assert.rejects(command.execute({
1709
+ realm: testRealmURL,
1710
+ roomId: "!room:example.com",
1711
+ code: `await realm.fs.list('missing');`
1712
+ }), /Directory not found/);
1713
+ });
1714
+ (0, import_qunit.test)("realm.entries.search runs several searches in one script and returns only what the script keeps", async function(assert) {
1715
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1716
+ realm: testRealmURL,
1717
+ roomId: "!room:example.com",
1718
+ code: `const found = {};
1719
+ for (const title of ['Pet', 'Unicorn']) {
1720
+ const { entries, total, truncated, incomplete } = await realm.entries.search(
1721
+ {
1722
+ filter: {
1723
+ on: { module: '@cardstack/base/spec', name: 'Spec' },
1724
+ eq: { cardTitle: title },
1725
+ },
1726
+ },
1727
+ { realms: [realm.current.url] },
1728
+ );
1729
+ found[title] = {
1730
+ total,
1731
+ truncated,
1732
+ incomplete,
1733
+ entries: entries.map((entry) => ({
1734
+ url: entry.url,
1735
+ kind: entry.kind,
1736
+ specType: entry.specType,
1737
+ refName: entry.ref?.name,
1738
+ readMe: entry.readMe,
1739
+ })),
1740
+ };
1741
+ }
1742
+ return found;`
1743
+ });
1744
+ assert.deepEqual(result.files, [], "a search saves nothing");
1745
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1746
+ Pet: {
1747
+ total: 1,
1748
+ truncated: false,
1749
+ incomplete: false,
1750
+ entries: [{
1751
+ url: `${testRealmURL}Spec/pet`,
1752
+ kind: "card",
1753
+ specType: "card",
1754
+ refName: "Pet",
1755
+ readMe: "# Pet\n\nUse Pet for any animal with a name."
1756
+ }]
1757
+ },
1758
+ Unicorn: {
1759
+ total: 0,
1760
+ truncated: false,
1761
+ incomplete: false,
1762
+ entries: []
1763
+ }
1764
+ });
1765
+ });
1766
+ (0, import_qunit.test)("realm.entries.search caps the entries at the limit and reports the rest", async function(assert) {
1767
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1768
+ realm: testRealmURL,
1769
+ roomId: "!room:example.com",
1770
+ code: `const { entries, total, truncated } = await realm.entries.search(
1771
+ {},
1772
+ { realms: [realm.current.url], scope: 'cards', limit: 1 },
1773
+ );
1774
+ return { count: entries.length, kind: entries[0].kind, more: total > 1, truncated };`
1775
+ });
1776
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1777
+ count: 1,
1778
+ kind: "card",
1779
+ more: true,
1780
+ truncated: true
1781
+ });
1782
+ });
1783
+ (0, import_qunit.test)("realm.entries.search finds the entries of a realm named by a registered prefix", async function(assert) {
1784
+ let { virtualNetwork } = getService("network");
1785
+ virtualNetwork.addRealmMapping("@test/mapped/", otherRealmURL);
1786
+ try {
1787
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1788
+ realm: testRealmURL,
1789
+ roomId: "!room:example.com",
1790
+ code: `const { entries, total, incomplete } = await realm.entries.search(
1791
+ {
1792
+ filter: {
1793
+ on: { module: ${JSON.stringify(`${testRealmURL}recipe`)}, name: 'Recipe' },
1794
+ eq: { name: 'Elsewhere' },
1795
+ },
1796
+ },
1797
+ { realms: ['@test/mapped'], scope: 'cards' },
1798
+ );
1799
+ return { total, incomplete, urls: entries.map((entry) => entry.url) };`
1800
+ });
1801
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1802
+ total: 1,
1803
+ incomplete: false,
1804
+ urls: [`${otherRealmURL}Recipe/elsewhere`]
1805
+ });
1806
+ } finally {
1807
+ virtualNetwork.removeRealmMapping("@test/mapped/");
1808
+ }
1809
+ });
1810
+ (0, import_qunit.test)("realm.entries.search sends a realm as the identifier given, once per realm", async function(assert) {
1811
+ let { virtualNetwork } = getService("network");
1812
+ let sentRealms = [];
1813
+ let spy = async (request) => {
1814
+ if (new URL(request.url).pathname.endsWith("/_federated-search")) sentRealms.push(JSON.parse(await request.clone().text()).realms);
1815
+ return null;
1816
+ };
1817
+ virtualNetwork.mount(spy, { prepend: true });
1818
+ try {
1819
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1820
+ await command.execute({
1821
+ realm: testRealmURL,
1822
+ roomId: "!room:example.com",
1823
+ code: `await realm.entries.search({}, { realms: ['@cardstack/base/', ${JSON.stringify(virtualNetwork.toURL("@cardstack/base/").href)}] });`
1824
+ });
1825
+ assert.deepEqual(sentRealms, [["@cardstack/base/"]], "the prefix goes out as given, and its URL, naming the same realm, is dropped");
1826
+ await command.execute({
1827
+ realm: testRealmURL,
1828
+ roomId: "!room:example.com",
1829
+ code: `await realm.entries.search({}, { realms: ['@cardstack/base/'] });`
1830
+ });
1831
+ assert.deepEqual(sentRealms[1], ["@cardstack/base/"], "the base prefix goes out as given");
1832
+ } finally {
1833
+ virtualNetwork.unmount(spy);
1834
+ }
1835
+ });
1836
+ (0, import_qunit.test)("realm.entries.search refuses a scoped identifier no realm is registered under", async function(assert) {
1837
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1838
+ await assert.rejects(command.execute({
1839
+ realm: testRealmURL,
1840
+ roomId: "!room:example.com",
1841
+ code: `await realm.entries.search({}, { realms: ['@nope/realm/'] });`
1842
+ }), (error) => error.message.includes("Unknown realm identifier \"@nope/realm/\"") && error.message.includes("@cardstack/catalog/"));
1843
+ await assert.rejects(command.execute({
1844
+ realm: testRealmURL,
1845
+ roomId: "!room:example.com",
1846
+ code: `await realm.entries.search({}, { realms: ['@cardstack/boxel-ui/'] });`
1847
+ }), /Unknown realm identifier "@cardstack\/boxel-ui\/"/);
1848
+ });
1849
+ (0, import_qunit.test)("realm.entries.search refuses a page and an unknown scope", async function(assert) {
1850
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1851
+ await assert.rejects(command.execute({
1852
+ realm: testRealmURL,
1853
+ roomId: "!room:example.com",
1854
+ code: `await realm.entries.search({ page: { number: 1 } });`
1855
+ }), /does not page; pass \{ limit \}/);
1856
+ await assert.rejects(command.execute({
1857
+ realm: testRealmURL,
1858
+ roomId: "!room:example.com",
1859
+ code: `await realm.entries.search({}, { scope: 'modules' });`
1860
+ }), /Invalid scope "modules"/);
1861
+ });
1862
+ function stubWorkspaceCreation() {
1863
+ let calls = [];
1864
+ let realmServer = getService("realm-server");
1865
+ realmServer.createRealm = async (args) => {
1866
+ calls.push(args);
1867
+ return new URL(`http://test-realm/testuser/${args.endpoint}/`);
1868
+ };
1869
+ getService("realm").ensureRealmMeta = async () => {};
1870
+ return calls;
1871
+ }
1872
+ (0, import_qunit.test)("realm.workspaces.create creates a workspace, returns its URL, and opens it after the run", async function(assert) {
1873
+ let createRealmCalls = stubWorkspaceCreation();
1874
+ let operatorModeStateService = getService("operator-mode-state-service");
1875
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1876
+ realm: testRealmURL,
1877
+ roomId: "!room:example.com",
1878
+ code: `let created = await realm.workspaces.create({ name: 'Sales Pipeline' });
1879
+ return { created, realmAfterCreate: realm.current.url };`
1880
+ });
1881
+ assert.deepEqual(createRealmCalls.map(({ endpoint, name }) => ({
1882
+ endpoint,
1883
+ name
1884
+ })), [{
1885
+ endpoint: "sales-pipeline",
1886
+ name: "Sales Pipeline"
1887
+ }], "the endpoint is derived from the name, as the create-workspace tool does");
1888
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1889
+ created: {
1890
+ url: "http://test-realm/testuser/sales-pipeline/",
1891
+ name: "Sales Pipeline"
1892
+ },
1893
+ realmAfterCreate: testRealmURL
1894
+ });
1895
+ assert.strictEqual(operatorModeStateService.state?.stacks[0]?.[0]?.id, "http://test-realm/testuser/sales-pipeline/index", "the new workspace is opened once the run ends");
1896
+ assert.deepEqual(result.createdWorkspaces, ["http://test-realm/testuser/sales-pipeline/"], "the result names the created workspace");
1897
+ });
1898
+ (0, import_qunit.test)("a failed run names the workspaces it created and opens none", async function(assert) {
1899
+ stubWorkspaceCreation();
1900
+ let operatorModeStateService = getService("operator-mode-state-service");
1901
+ let stackBefore = operatorModeStateService.state?.stacks[0]?.[0]?.id;
1902
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1903
+ await assert.rejects(command.execute({
1904
+ realm: testRealmURL,
1905
+ roomId: "!room:example.com",
1906
+ code: `await realm.workspaces.create({ name: 'First Try' });
1907
+ throw new Error('boom');`
1908
+ }), /boom.*Workspaces already created by this run: http:\/\/test-realm\/testuser\/first-try\//);
1909
+ assert.strictEqual(operatorModeStateService.state?.stacks[0]?.[0]?.id, stackBefore, "no workspace is opened after a failed run");
1910
+ });
1911
+ (0, import_qunit.test)("a run that saves files and creates a workspace stays in its realm", async function(assert) {
1912
+ stubWorkspaceCreation();
1913
+ let operatorModeStateService = getService("operator-mode-state-service");
1914
+ let stackBefore = operatorModeStateService.state?.stacks[0]?.[0]?.id;
1915
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1916
+ realm: testRealmURL,
1917
+ roomId: "!room:example.com",
1918
+ code: `await realm.fs.writeText('notes/new-workspace.txt', 'hello');
1919
+ await realm.workspaces.create({ name: 'Side Project' });`
1920
+ });
1921
+ assert.deepEqual(result.createdWorkspaces, ["http://test-realm/testuser/side-project/"]);
1922
+ assert.strictEqual(operatorModeStateService.state?.stacks[0]?.[0]?.id, stackBefore, "the new workspace is not opened, so the next run defaults to this realm");
1923
+ });
1924
+ (0, import_qunit.test)("realm.workspaces.create refuses options that are not strings", async function(assert) {
1925
+ let createRealmCalls = stubWorkspaceCreation();
1926
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1927
+ await assert.rejects(command.execute({
1928
+ realm: testRealmURL,
1929
+ roomId: "!room:example.com",
1930
+ code: `await realm.workspaces.create({ name: 42 });`
1931
+ }), /expects name to be a string/);
1932
+ assert.strictEqual(createRealmCalls.length, 0, "no realm is created");
1933
+ });
1934
+ function stubWorkspaceDeletion(ownedRealmURL) {
1935
+ let calls = [];
1936
+ getService("realm-server").deleteRealm = async (realmURL) => {
1937
+ calls.push(realmURL);
1938
+ };
1939
+ let realmService = getService("realm");
1940
+ let isRealmOwner = realmService.isRealmOwner.bind(realmService);
1941
+ realmService.isRealmOwner = (url) => url === ownedRealmURL || isRealmOwner(url);
1942
+ return calls;
1943
+ }
1944
+ (0, import_qunit.test)("a script that deletes a workspace always waits for the user", function(assert) {
1945
+ assert.true(RunRealmCodeTool.neverAutoExecutesFor({ code: `await realm.workspaces.delete('http://test-realm/testuser/old/');` }), "a run that names realm.workspaces.delete waits for a click");
1946
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code: `await realm.fs.writeText('a.json', '{}');` }), "other runs keep the room mode");
1947
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor(void 0), "a call whose input has not arrived yet is not judged");
1948
+ });
1949
+ (0, import_qunit.test)("realm.workspaces.delete deletes a workspace the user owns", async function(assert) {
1950
+ let oldWorkspace = "http://test-realm/testuser/old/";
1951
+ let deleteRealmCalls = stubWorkspaceDeletion(oldWorkspace);
1952
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1953
+ realm: testRealmURL,
1954
+ roomId: "!room:example.com",
1955
+ code: `return await realm.workspaces.delete('${oldWorkspace}');`
1956
+ });
1957
+ assert.deepEqual(deleteRealmCalls, [oldWorkspace]);
1958
+ assert.deepEqual(JSON.parse(result.scriptResult), {
1959
+ url: oldWorkspace,
1960
+ deleted: true
1961
+ });
1962
+ });
1963
+ (0, import_qunit.test)("the result names the deleted workspace when the script does not return it", async function(assert) {
1964
+ let oldWorkspace = "http://test-realm/testuser/old/";
1965
+ stubWorkspaceDeletion(oldWorkspace);
1966
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1967
+ realm: testRealmURL,
1968
+ roomId: "!room:example.com",
1969
+ code: `await realm.workspaces.delete('${oldWorkspace}');`
1970
+ });
1971
+ assert.deepEqual(result.deletedWorkspaces, [oldWorkspace]);
1972
+ });
1973
+ (0, import_qunit.test)("a failed run names the workspaces it deleted", async function(assert) {
1974
+ let oldWorkspace = "http://test-realm/testuser/old/";
1975
+ stubWorkspaceDeletion(oldWorkspace);
1976
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
1977
+ await assert.rejects(command.execute({
1978
+ realm: testRealmURL,
1979
+ roomId: "!room:example.com",
1980
+ code: `await realm.workspaces.delete('${oldWorkspace}');
1981
+ throw new Error('boom');`
1982
+ }), /boom.*Workspaces already deleted by this run: http:\/\/test-realm\/testuser\/old\//);
1983
+ });
1984
+ (0, import_qunit.test)("a workspace recreated at a URL the run deleted is opened", async function(assert) {
1985
+ let oldWorkspace = "http://test-realm/testuser/old/";
1986
+ stubWorkspaceDeletion(oldWorkspace);
1987
+ stubWorkspaceCreation();
1988
+ let operatorModeStateService = getService("operator-mode-state-service");
1989
+ await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
1990
+ realm: testRealmURL,
1991
+ roomId: "!room:example.com",
1992
+ code: `await realm.workspaces.delete('${oldWorkspace}');
1993
+ await realm.workspaces.create({ name: 'Old' });`
1994
+ });
1995
+ assert.strictEqual(operatorModeStateService.state?.stacks[0]?.[0]?.id, `${oldWorkspace}index`);
1996
+ });
1997
+ (0, import_qunit.test)("realm.workspaces.delete refuses a workspace the user does not own", async function(assert) {
1998
+ let deleteRealmCalls = stubWorkspaceDeletion("http://test-realm/testuser/old/");
1999
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2000
+ await assert.rejects(command.execute({
2001
+ realm: testRealmURL,
2002
+ roomId: "!room:example.com",
2003
+ code: `await realm.workspaces.delete('http://test-realm/someone-else/theirs/');`
2004
+ }), /the current user is not its owner/);
2005
+ assert.deepEqual(deleteRealmCalls, [], "no realm is deleted");
2006
+ });
2007
+ (0, import_qunit.test)("a script that reaches realm.workspaces.delete another way is refused", async function(assert) {
2008
+ let oldWorkspace = "http://test-realm/testuser/old/";
2009
+ let deleteRealmCalls = stubWorkspaceDeletion(oldWorkspace);
2010
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2011
+ let code = `let workspaces = realm.workspaces;
2012
+ await workspaces['del' + 'ete']('${oldWorkspace}');`;
2013
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code }), "this run would not wait for a click");
2014
+ await assert.rejects(command.execute({
2015
+ realm: testRealmURL,
2016
+ roomId: "!room:example.com",
2017
+ code
2018
+ }), /must be called by that name/);
2019
+ assert.deepEqual(deleteRealmCalls, [], "no realm is deleted");
2020
+ });
2021
+ function stubIndexing() {
2022
+ let calls = [];
2023
+ let realmService = getService("realm");
2024
+ realmService.invalidateUrls = async (realm, urls) => {
2025
+ calls.push({
2026
+ action: "invalidate",
2027
+ realm,
2028
+ urls
2029
+ });
2030
+ };
2031
+ realmService.reindex = async (realm) => {
2032
+ calls.push({
2033
+ action: "reindex",
2034
+ realm
2035
+ });
2036
+ };
2037
+ realmService.fullReindex = async (realm) => {
2038
+ calls.push({
2039
+ action: "fullReindex",
2040
+ realm
2041
+ });
2042
+ };
2043
+ realmService.cancelIndexingJob = async (realm) => {
2044
+ calls.push({
2045
+ action: "cancelJob",
2046
+ realm
2047
+ });
2048
+ };
2049
+ return calls;
2050
+ }
2051
+ (0, import_qunit.test)("realm.index.invalidate and realm.index.reindex refresh the index of the run realm without a click", async function(assert) {
2052
+ let calls = stubIndexing();
2053
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2054
+ let code = `return {
2055
+ invalidated: await realm.index.invalidate(['Pet/mango.json', '${testRealmURL}pet.gts']),
2056
+ one: await realm.index.invalidate('task.json'),
2057
+ reindex: await realm.index.reindex(),
2058
+ };`;
2059
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code }), "the run keeps the room mode");
2060
+ let result = await command.execute({
2061
+ realm: testRealmURL,
2062
+ roomId: "!room:example.com",
2063
+ code
2064
+ });
2065
+ assert.deepEqual(calls, [
2066
+ {
2067
+ action: "invalidate",
2068
+ realm: testRealmURL,
2069
+ urls: [`${testRealmURL}Pet/mango.json`, `${testRealmURL}pet.gts`]
2070
+ },
2071
+ {
2072
+ action: "invalidate",
2073
+ realm: testRealmURL,
2074
+ urls: [`${testRealmURL}task.json`]
2075
+ },
2076
+ {
2077
+ action: "reindex",
2078
+ realm: testRealmURL
2079
+ }
2080
+ ]);
2081
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2082
+ invalidated: { invalidated: ["Pet/mango.json", "pet.gts"] },
2083
+ one: { invalidated: ["task.json"] },
2084
+ reindex: { reindex: "started" }
2085
+ });
2086
+ assert.deepEqual(result.indexActions, [
2087
+ "invalidate Pet/mango.json, pet.gts",
2088
+ "invalidate task.json",
2089
+ "reindex"
2090
+ ]);
2091
+ });
2092
+ (0, import_qunit.test)("realm.index.invalidate refuses a path outside the realm and an empty list", async function(assert) {
2093
+ let calls = stubIndexing();
2094
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2095
+ realm: testRealmURL,
2096
+ roomId: "!room:example.com",
2097
+ code: `let refused = [];
2098
+ try { await realm.index.invalidate(['http://other-realm/a.json']); } catch (e) { refused.push(e.message); }
2099
+ try { await realm.index.invalidate([]); } catch (e) { refused.push(e.message); }
2100
+ return refused;`
2101
+ });
2102
+ let [outside, empty] = JSON.parse(result.scriptResult);
2103
+ assert.true(/Path is outside this realm/.test(outside), outside);
2104
+ assert.true(/expects a path or an array/.test(empty), empty);
2105
+ assert.deepEqual(calls, [], "nothing is invalidated");
2106
+ });
2107
+ (0, import_qunit.test)("a script that starts a full reindex or cancels an indexing job always waits for the user", async function(assert) {
2108
+ let calls = stubIndexing();
2109
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2110
+ for (let code of [`await realm.index.fullReindex();`, `await realm.index.cancelJob();`]) assert.true(RunRealmCodeTool.neverAutoExecutesFor({ code }), `waits for a click: ${code}`);
2111
+ let result = await command.execute({
2112
+ realm: testRealmURL,
2113
+ roomId: "!room:example.com",
2114
+ code: `return {
2115
+ cancel: await realm.index.cancelJob(),
2116
+ full: await realm.index.fullReindex(),
2117
+ };`
2118
+ });
2119
+ assert.deepEqual(calls, [{
2120
+ action: "cancelJob",
2121
+ realm: testRealmURL
2122
+ }, {
2123
+ action: "fullReindex",
2124
+ realm: testRealmURL
2125
+ }]);
2126
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2127
+ cancel: { cancelled: true },
2128
+ full: { fullReindex: "started" }
2129
+ });
2130
+ });
2131
+ (0, import_qunit.test)("a failed run names the index actions it already started", async function(assert) {
2132
+ let calls = stubIndexing();
2133
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2134
+ await assert.rejects(command.execute({
2135
+ realm: testRealmURL,
2136
+ roomId: "!room:example.com",
2137
+ code: `await realm.index.invalidate('task.json');
2138
+ await realm.index.fullReindex();
2139
+ throw new Error('boom');`
2140
+ }), /boom\. No file was saved\. Index actions already started by this run: invalidate task\.json; fullReindex/);
2141
+ assert.deepEqual(calls, [{
2142
+ action: "invalidate",
2143
+ realm: testRealmURL,
2144
+ urls: [`${testRealmURL}task.json`]
2145
+ }, {
2146
+ action: "fullReindex",
2147
+ realm: testRealmURL
2148
+ }]);
2149
+ });
2150
+ (0, import_qunit.test)("a script that reaches realm.index.fullReindex another way is refused", async function(assert) {
2151
+ let calls = stubIndexing();
2152
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2153
+ let code = `let index = realm.index;
2154
+ await index['full' + 'Reindex']();`;
2155
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code }), "this run would not wait for a click");
2156
+ await assert.rejects(command.execute({
2157
+ realm: testRealmURL,
2158
+ roomId: "!room:example.com",
2159
+ code
2160
+ }), /realm\.index\.fullReindex must be called by that name/);
2161
+ assert.deepEqual(calls, [], "no reindex starts");
2162
+ });
2163
+ (0, import_qunit.module)("room actions", function(hooks) {
2164
+ let roomId;
2165
+ let skillId = `${testRealmURL}skills/test-skill/SKILL.md`;
2166
+ let model = DEFAULT_FALLBACK_MODELS[0].modelId;
2167
+ hooks.beforeEach(async function() {
2168
+ await getService("matrix-service").ready;
2169
+ roomId = mockMatrixUtils.createAndJoinRoom({
2170
+ sender: "@testuser:localhost",
2171
+ name: "room-actions"
2172
+ });
2173
+ });
2174
+ (0, import_qunit.test)("room actions change the room after the script finishes", async function(assert) {
2175
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2176
+ let result = await command.execute({
2177
+ realm: testRealmURL,
2178
+ roomId,
2179
+ code: `return {
2180
+ skills: await room.enableSkills([${JSON.stringify(skillId)}]),
2181
+ model: await room.setModel(${JSON.stringify(model)}),
2182
+ };`
2183
+ });
2184
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2185
+ skills: {
2186
+ enable: [skillId],
2187
+ appliedAfterRun: true
2188
+ },
2189
+ model: {
2190
+ model,
2191
+ appliedAfterRun: true
2192
+ }
2193
+ });
2194
+ let skills = mockMatrixUtils.getRoomState(roomId, APP_BOXEL_ROOM_SKILLS_EVENT_TYPE);
2195
+ assert.deepEqual(skills.enabledSkillCards.map((file) => file.sourceUrl), [skillId], "the skill is enabled");
2196
+ assert.strictEqual(mockMatrixUtils.getRoomState(roomId, APP_BOXEL_ACTIVE_LLM)?.model, model, "the model is set");
2197
+ assert.deepEqual(result.roomChanges, [`Enabled skills: ${skillId}`, `Model set to ${model}`]);
2198
+ await command.execute({
2199
+ realm: testRealmURL,
2200
+ roomId,
2201
+ code: `await room.disableSkills([${JSON.stringify(skillId)}]);`
2202
+ });
2203
+ skills = mockMatrixUtils.getRoomState(roomId, APP_BOXEL_ROOM_SKILLS_EVENT_TYPE);
2204
+ assert.deepEqual(skills.enabledSkillCards, [], "the skill is disabled");
2205
+ assert.deepEqual(skills.disabledSkillCards.map((file) => file.sourceUrl), [skillId]);
2206
+ });
2207
+ (0, import_qunit.test)("a run that fails makes no room change", async function(assert) {
2208
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2209
+ await assert.rejects(command.execute({
2210
+ realm: testRealmURL,
2211
+ roomId,
2212
+ code: `await room.enableSkills([${JSON.stringify(skillId)}]);
2213
+ throw new Error('stop');`
2214
+ }), /stop\. No file was saved\. The room changes it asked for were not made\./);
2215
+ assert.notOk(mockMatrixUtils.getRoomEvents(roomId).some((event) => event.type === APP_BOXEL_ROOM_SKILLS_EVENT_TYPE), "the room skills are not changed");
2216
+ });
2217
+ (0, import_qunit.test)("room actions refuse a skill that does not exist and an unknown model", async function(assert) {
2218
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2219
+ await assert.rejects(command.execute({
2220
+ realm: testRealmURL,
2221
+ roomId,
2222
+ code: `await room.enableSkills([${JSON.stringify(`${testRealmURL}notes/first.md`)}]);`
2223
+ }), /Not a skill card or skill markdown file/);
2224
+ await assert.rejects(command.execute({
2225
+ realm: testRealmURL,
2226
+ roomId,
2227
+ code: `await room.setModel('no-such/model');`
2228
+ }), /Unknown model: no-such\/model/);
2229
+ });
2230
+ });
2231
+ (0, import_qunit.test)("realm.ui actions run in order after the script finishes", async function(assert) {
2232
+ let toolService = getService("tool-service");
2233
+ let operatorModeStateService = getService("operator-mode-state-service");
2234
+ let command = new RunRealmCodeTool(toolService.toolContext);
2235
+ let result = await command.execute({
2236
+ realm: testRealmURL,
2237
+ roomId: "!room:example.com",
2238
+ code: `let queued = await realm.ui.openFile('pet.gts');
2239
+ await realm.ui.previewFormat('Pet/mango', 'fitted');
2240
+ return queued;`
2241
+ });
2242
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2243
+ action: "openFile",
2244
+ runs: "after the script finishes"
2245
+ });
2246
+ assert.deepEqual(result.uiActions.map(({ action, target, status }) => ({
2247
+ action,
2248
+ target,
2249
+ status
2250
+ })), [{
2251
+ action: "openFile",
2252
+ target: `${testRealmURL}pet.gts`,
2253
+ status: "done"
2254
+ }, {
2255
+ action: "previewFormat",
2256
+ target: `${testRealmURL}Pet/mango (fitted)`,
2257
+ status: "done"
2258
+ }]);
2259
+ assert.strictEqual(operatorModeStateService.state.submode, "code");
2260
+ assert.strictEqual(operatorModeStateService.state.codePath, `${testRealmURL}pet.gts`);
2261
+ assert.strictEqual(operatorModeStateService.state.moduleInspector, "preview");
2262
+ let selection = getService("playground-panel-service").getSelection(`${testRealmURL}pet/Pet`);
2263
+ assert.strictEqual(selection?.cardId, `${testRealmURL}Pet/mango`);
2264
+ assert.strictEqual(selection?.format, "fitted");
2265
+ await command.execute({
2266
+ realm: testRealmURL,
2267
+ roomId: "!room:example.com",
2268
+ code: `await realm.ui.switchSubmode('interact');`
2269
+ });
2270
+ assert.strictEqual(operatorModeStateService.state.submode, "interact");
2271
+ });
2272
+ (0, import_qunit.test)("realm.ui.previewFormat uses the base module of a card whose type comes from base", async function(assert) {
2273
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2274
+ let baseModule = getService("network").virtualNetwork.toURL("@cardstack/base/card-api.gts").href;
2275
+ let detail = (await command.execute({
2276
+ realm: testRealmURL,
2277
+ roomId: "!room:example.com",
2278
+ code: `await realm.ui.previewFormat('Plain/base-typed', 'isolated');`
2279
+ })).uiActions[0]?.detail ?? "";
2280
+ assert.true(detail.includes(baseModule), `asks for ${baseModule}: ${detail}`);
2281
+ assert.false(detail.includes(`${testRealmURL}Plain/`), "does not join the module to the instance path");
2282
+ });
2283
+ (0, import_qunit.test)("realm.ui opens a file the same run wrote", async function(assert) {
2284
+ let toolService = getService("tool-service");
2285
+ let operatorModeStateService = getService("operator-mode-state-service");
2286
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
2287
+ realm: testRealmURL,
2288
+ roomId: "!room:example.com",
2289
+ code: `await realm.fs.writeText('notes/second.md', '# Second\\n');
2290
+ await realm.ui.openFile('notes/second.md');`
2291
+ });
2292
+ assert.strictEqual(result.uiActions[0]?.status, "done");
2293
+ assert.strictEqual(operatorModeStateService.state.codePath, `${testRealmURL}notes/second.md`);
2294
+ });
2295
+ (0, import_qunit.test)("realm.ui refuses a missing file, a path outside the realm, and a file that is not a card", async function(assert) {
2296
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2297
+ let run = (code) => command.execute({
2298
+ realm: testRealmURL,
2299
+ roomId: "!room:example.com",
2300
+ code
2301
+ });
2302
+ await assert.rejects(run(`await realm.ui.openFile('missing.gts');`), /File not found/);
2303
+ await assert.rejects(run(`await realm.ui.openFile('../elsewhere.gts');`), /Path must be relative to the realm root/);
2304
+ await assert.rejects(run(`await realm.ui.previewFormat('task', 'isolated');`), /Not a card instance/);
2305
+ await assert.rejects(run(`await realm.ui.previewFormat('Pet/mango', 'head');`), /one of these formats/);
2306
+ await assert.rejects(run(`await realm.ui.switchSubmode('host');`), /expects 'interact' or 'code'/);
2307
+ });
2308
+ (0, import_qunit.test)("a failed run does not do its realm.ui actions", async function(assert) {
2309
+ let toolService = getService("tool-service");
2310
+ let operatorModeStateService = getService("operator-mode-state-service");
2311
+ let command = new RunRealmCodeTool(toolService.toolContext);
2312
+ let submodeBefore = operatorModeStateService.state.submode;
2313
+ await assert.rejects(command.execute({
2314
+ realm: testRealmURL,
2315
+ roomId: "!room:example.com",
2316
+ code: `await realm.ui.openFile('pet.gts');
2317
+ throw new Error('stop here');`
2318
+ }), /stop here\. No file was saved\. The UI actions it asked for were not done\./);
2319
+ assert.strictEqual(operatorModeStateService.state.submode, submodeBefore);
2320
+ });
2321
+ (0, import_qunit.module)("realm.cards.update", function() {
2322
+ async function run(code) {
2323
+ return await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2324
+ realm: testRealmURL,
2325
+ roomId: "!room:example.com",
2326
+ code
2327
+ });
2328
+ }
2329
+ async function cardJSON(path) {
2330
+ let source = await getService("card-service").getSource(new URL(`${testRealmURL}${path}`));
2331
+ return JSON.parse(source.content);
2332
+ }
2333
+ (0, import_qunit.test)("changes the fields it is given and keeps the others", async function(assert) {
2334
+ let result = await run(`
2335
+ const updated = await realm.cards.update('Recipe/pancakes', { cuisine: 'French' });
2336
+ return updated;`);
2337
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2338
+ path: "Recipe/pancakes.json",
2339
+ fields: ["cuisine"],
2340
+ saves: "after the script finishes"
2341
+ });
2342
+ assert.deepEqual(result.files.map((file) => [file.fileUrl, file.status]), [[`${testRealmURL}Recipe/pancakes.json`, "saved"]]);
2343
+ let { data } = await cardJSON("Recipe/pancakes.json");
2344
+ assert.strictEqual(data.attributes.cuisine, "French");
2345
+ assert.strictEqual(data.attributes.name, "Pancakes");
2346
+ assert.strictEqual(data.relationships.chef.links.self, "../Chef/ana", "links are kept");
2347
+ });
2348
+ (0, import_qunit.test)("saves the update to a realm named by a prefix-form identifier", async function(assert) {
2349
+ let { virtualNetwork } = getService("network");
2350
+ let testRealmPrefix = "@test/cards-update/";
2351
+ virtualNetwork.addRealmMapping(testRealmPrefix, testRealmURL);
2352
+ try {
2353
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2354
+ realm: testRealmPrefix,
2355
+ roomId: "!room:example.com",
2356
+ code: `await realm.cards.update('Recipe/pancakes', { cuisine: 'Belgian' });`
2357
+ });
2358
+ assert.deepEqual(result.files.map((file) => file.status), ["saved"], "the update is saved");
2359
+ let { data } = await cardJSON("Recipe/pancakes.json");
2360
+ assert.strictEqual(data.attributes.cuisine, "Belgian");
2361
+ } finally {
2362
+ virtualNetwork.removeRealmMapping(testRealmPrefix);
2363
+ }
2364
+ });
2365
+ (0, import_qunit.test)("edits a long text field in the script without cutting it", async function(assert) {
2366
+ let long = Array.from({ length: 40 }, (_, n) => `Line ${n}: a \`code\` span, a \${placeholder} and "quotes".`).join("\n");
2367
+ assert.true(long.length > 500);
2368
+ await run(`await realm.cards.update('Pet/mango', { name: ${JSON.stringify(long)} });`);
2369
+ let result = await run(`
2370
+ const card = await realm.cards.get('Pet/mango');
2371
+ const name = card.attributes.name.replace('Line 0:', 'First line:');
2372
+ await realm.cards.update('Pet/mango', { name });
2373
+ return name.length;`);
2374
+ let expected = long.replace("Line 0:", "First line:");
2375
+ assert.strictEqual(JSON.parse(result.scriptResult), expected.length);
2376
+ let { data } = await cardJSON("Pet/mango.json");
2377
+ assert.strictEqual(data.attributes.name, expected);
2378
+ });
2379
+ (0, import_qunit.test)("updates more cards than the file limit in one run, and saves them all", async function(assert) {
2380
+ let cardService = getService("card-service");
2381
+ for (let n = 0; n < 25; n++) await cardService.saveSource(new URL(`${testRealmURL}Recipe/batch/r${n}.json`), recipeJSON("Recipe", {
2382
+ name: `R${n}`,
2383
+ cuisine: "batch"
2384
+ }, void 0, "../../recipe"), "editor");
2385
+ let result = await run(`
2386
+ for (let n = 0; n < 25; n++) {
2387
+ await realm.cards.update('Recipe/batch/r' + n, { cuisine: 'done' });
2388
+ }`);
2389
+ assert.strictEqual(result.files.length, 25, "the result names each card");
2390
+ for (let n of [0, 24]) {
2391
+ let { data } = await cardJSON(`Recipe/batch/r${n}.json`);
2392
+ assert.strictEqual(data.attributes.cuisine, "done");
2393
+ assert.strictEqual(data.attributes.name, `R${n}`);
2394
+ }
2395
+ });
2396
+ (0, import_qunit.test)("refuses an update that makes a card larger than the card size limit, counted in bytes", async function(assert) {
2397
+ let result = await run(`
2398
+ try {
2399
+ await realm.cards.update('Pet/mango', { name: '\u{1F600}'.repeat(200000) });
2400
+ } catch (error) {
2401
+ return error.message;
2402
+ }`);
2403
+ assert.true(/Card is too large after this update: .*Pet\/mango\.json/.test(JSON.parse(result.scriptResult)), result.scriptResult);
2404
+ assert.deepEqual(result.files, [], "nothing is saved");
2405
+ });
2406
+ (0, import_qunit.test)("two updates to the same card in one run both apply", async function(assert) {
2407
+ let result = await run(`
2408
+ await realm.cards.update('Recipe/ramen', { cuisine: 'Japanese street food' });
2409
+ await realm.cards.update('Recipe/ramen.json', { name: 'Shoyu ramen' });`);
2410
+ assert.strictEqual(result.files.length, 1);
2411
+ let { data } = await cardJSON("Recipe/ramen.json");
2412
+ assert.strictEqual(data.attributes.cuisine, "Japanese street food");
2413
+ assert.strictEqual(data.attributes.name, "Shoyu ramen");
2414
+ });
2415
+ (0, import_qunit.test)("a run that fails saves no card update, and says so", async function(assert) {
2416
+ await assert.rejects(run(`
2417
+ await realm.cards.update('Recipe/pancakes', { cuisine: 'French' });
2418
+ throw new Error('stop');`), /stop.*The card updates it asked for were not saved/);
2419
+ let { data } = await cardJSON("Recipe/pancakes.json");
2420
+ assert.strictEqual(data.attributes.cuisine, "unknown");
2421
+ });
2422
+ (0, import_qunit.test)("refuses a file that is not a card, attributes that are not an object, and realm.fs on an updated card", async function(assert) {
2423
+ let result = await run(`
2424
+ const errors = [];
2425
+ for (const call of [
2426
+ () => realm.cards.update('task.json', { title: 'x' }),
2427
+ () => realm.cards.update('Recipe/missing', { name: 'x' }),
2428
+ () => realm.cards.update('Recipe/pancakes', ['x']),
2429
+ () => realm.cards.update('Recipe/pancakes', 'x'),
2430
+ ]) {
2431
+ try { await call(); } catch (error) { errors.push(error.message); }
2432
+ }
2433
+ await realm.cards.update('Recipe/ramen', { cuisine: 'Ramen-ya' });
2434
+ try { await realm.fs.readText('Recipe/ramen.json'); } catch (error) { errors.push(error.message); }
2435
+ return errors;`);
2436
+ let errors = JSON.parse(result.scriptResult);
2437
+ assert.true(/Not a card instance: .*task\.json/.test(errors[0]), errors[0]);
2438
+ assert.true(/Card not found/.test(errors[1]), errors[1]);
2439
+ assert.true(/attributes object/.test(errors[2]), errors[2]);
2440
+ assert.true(/attributes object/.test(errors[3]), errors[3]);
2441
+ assert.true(/realm\.fs cannot read or change it in the same run/.test(errors[4]), errors[4]);
2442
+ let { data } = await cardJSON("Recipe/ramen.json");
2443
+ assert.strictEqual(data.attributes.cuisine, "Ramen-ya");
2444
+ });
2445
+ });
2446
+ (0, import_qunit.module)("realm.cards.search", function() {
2447
+ const recipeRef = `{ module: ${JSON.stringify(`${testRealmURL}recipe`)}, name: 'Recipe' }`;
2448
+ async function run(code) {
2449
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2450
+ realm: testRealmURL,
2451
+ roomId: "!room:example.com",
2452
+ code
2453
+ });
2454
+ return {
2455
+ result,
2456
+ value: JSON.parse(result.scriptResult)
2457
+ };
2458
+ }
2459
+ (0, import_qunit.test)("returns each card as plain data the script can use", async function(assert) {
2460
+ let { result, value } = await run(`return await realm.cards.search({ filter: { eq: { name: 'Pancakes' }, on: ${recipeRef} } });`);
2461
+ assert.deepEqual(result.files, [], "a search saves nothing");
2462
+ assert.strictEqual(value.total, 1);
2463
+ assert.false(value.truncated);
2464
+ assert.strictEqual(value.cards.length, 1);
2465
+ let [card] = value.cards;
2466
+ assert.strictEqual(card.id, `${testRealmURL}Recipe/pancakes`);
2467
+ assert.strictEqual(card.path, "Recipe/pancakes.json");
2468
+ assert.deepEqual(card.type, {
2469
+ module: `${testRealmURL}recipe`,
2470
+ name: "Recipe"
2471
+ });
2472
+ assert.strictEqual(card.attributes.name, "Pancakes");
2473
+ assert.strictEqual(card.attributes.cuisine, "unknown");
2474
+ assert.strictEqual(card.attributes.cardTitle, "Recipe: Pancakes", "computed values come from the index");
2475
+ assert.strictEqual(card.relationships.chef, `${testRealmURL}Chef/ana`, "a link is the full URL of the card it names");
2476
+ });
2477
+ (0, import_qunit.test)("a linksToMany field reports each link under its field path", async function(assert) {
2478
+ let { value } = await run(`return (await realm.cards.search({ filter: { eq: { name: 'Ramen' }, on: ${recipeRef} } })).cards[0].relationships;`);
2479
+ assert.strictEqual(value["helpers.0"], `${testRealmURL}Chef/ana`);
2480
+ });
2481
+ (0, import_qunit.test)("a script can search, then read and edit every card it found", async function(assert) {
2482
+ let { result, value } = await run(`
2483
+ const { cards } = await realm.cards.search({ filter: { eq: { cuisine: 'unknown' }, on: ${recipeRef} } });
2484
+ for (const card of cards) {
2485
+ await realm.fs.replace(card.path, '"cuisine": "unknown"', '"cuisine": "Italian"');
2486
+ }
2487
+ return cards.map((card) => card.path);`);
2488
+ assert.deepEqual(value, ["Recipe/desserts/tiramisu.json", "Recipe/pancakes.json"]);
2489
+ assert.deepEqual(result.files.map((file) => file.fileUrl), [`${testRealmURL}Recipe/desserts/tiramisu.json`, `${testRealmURL}Recipe/pancakes.json`]);
2490
+ let source = await getService("card-service").getSource(new URL(`${testRealmURL}Recipe/desserts/tiramisu.json`));
2491
+ assert.true(source.content.includes("\"cuisine\": \"Italian\""));
2492
+ });
2493
+ (0, import_qunit.test)("a type from a result is a type a query can search for", async function(assert) {
2494
+ let { value } = await run(`
2495
+ const { cards: [pancakes] } = await realm.cards.search({ filter: { eq: { name: 'Pancakes' }, on: ${recipeRef} } });
2496
+ const { cards } = await realm.cards.search({ filter: { type: pancakes.type } });
2497
+ return cards.map((card) => card.path);`);
2498
+ assert.deepEqual(value, [
2499
+ "Recipe/desserts/tiramisu.json",
2500
+ "Recipe/pancakes.json",
2501
+ "Recipe/ramen.json"
2502
+ ]);
2503
+ });
2504
+ (0, import_qunit.test)("pages through the results and says when a page cut the list", async function(assert) {
2505
+ let { value } = await run(`
2506
+ const first = await realm.cards.search({ filter: { type: ${recipeRef} }, page: { size: 2 } });
2507
+ const second = await realm.cards.search({ filter: { type: ${recipeRef} }, page: { size: 2, number: 1 } });
2508
+ return { first, second };`);
2509
+ assert.strictEqual(value.first.total, 3);
2510
+ assert.true(value.first.truncated);
2511
+ assert.deepEqual(value.first.cards.map((card) => card.path), ["Recipe/desserts/tiramisu.json", "Recipe/pancakes.json"]);
2512
+ assert.false(value.second.truncated, "no card matches past the last page");
2513
+ assert.deepEqual(value.second.cards.map((card) => card.path), ["Recipe/ramen.json"]);
2514
+ });
2515
+ (0, import_qunit.test)("searches only the realm the script runs in", async function(assert) {
2516
+ let { value } = await run(`return (await realm.cards.search({ filter: { type: ${recipeRef} } })).cards.map((card) => card.id);`);
2517
+ assert.false(value.some((id) => id.startsWith(otherRealmURL)), "another realm's cards are not returned");
2518
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2519
+ await assert.rejects(command.execute({
2520
+ realm: testRealmURL,
2521
+ roomId: "!room:example.com",
2522
+ code: `await realm.cards.search({ filter: { type: ${recipeRef} }, realms: [${JSON.stringify(otherRealmURL)}] });`
2523
+ }), /realm\.cards\.search searches only this realm/);
2524
+ });
2525
+ (0, import_qunit.test)("an invalid query rejects, and a script that catches it carries on", async function(assert) {
2526
+ let { value } = await run(`
2527
+ let message;
2528
+ try { await realm.cards.search({ filter: { type: ${recipeRef} }, limit: 10 }); } catch (e) { message = e.message; }
2529
+ return { message, found: (await realm.cards.search({ filter: { type: ${recipeRef} } })).total };`);
2530
+ assert.true(/unknown field in query: limit/.test(value.message), `the bad query was refused: ${value.message}`);
2531
+ assert.strictEqual(value.found, 3, "the next search still ran");
2532
+ });
2533
+ (0, import_qunit.test)("a search does not count toward the file limit", async function(assert) {
2534
+ let { value } = await run(`
2535
+ await realm.cards.search({ filter: { type: ${recipeRef} } });
2536
+ let found = 0;
2537
+ for (let i = 0; i < 20; i++) {
2538
+ if (!(await realm.fs.exists('missing-' + i + '.json'))) found++;
2539
+ }
2540
+ return found;`);
2541
+ assert.strictEqual(value, 20);
2542
+ });
2543
+ (0, import_qunit.test)("a search a realm did not answer fails instead of finding nothing", async function(assert) {
2544
+ let store = getService("store");
2545
+ let searchEntries = store.searchEntries;
2546
+ store.searchEntries = async () => ({
2547
+ data: [],
2548
+ meta: {
2549
+ page: { total: 0 },
2550
+ incomplete: true
2551
+ }
2552
+ });
2553
+ try {
2554
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2555
+ await assert.rejects(command.execute({
2556
+ realm: testRealmURL,
2557
+ roomId: "!room:example.com",
2558
+ code: `await realm.cards.search({ filter: { type: ${recipeRef} } });`
2559
+ }), /did not answer/);
2560
+ } finally {
2561
+ store.searchEntries = searchEntries;
2562
+ }
2563
+ });
2564
+ });
2565
+ });
2566
+ var PATIENT_MODULE = `
2567
+ import { contains, field, CardDef } from "@cardstack/base/card-api";
2568
+ import StringField from "@cardstack/base/string";
2569
+ import { operation, params } from "@cardstack/base/operations";
2570
+
2571
+ export class Patient extends CardDef {
2572
+ static displayName = 'Patient';
2573
+ @field name = contains(StringField);
2574
+ @field ward = contains(StringField);
2575
+ @field lastVitals = contains(StringField);
2576
+
2577
+ @operation static recordVitals = {
2578
+ base: 'transform',
2579
+ params: { reading: StringField },
2580
+ set: { lastVitals: params('reading') },
2581
+ };
2582
+
2583
+ @operation static transferTo = {
2584
+ base: 'transform',
2585
+ params: { ward: StringField },
2586
+ set: { ward: params('ward') },
2587
+ };
2588
+
2589
+ @operation static amend = { base: 'update' };
2590
+
2591
+ @operation static chart = { base: 'read' };
2592
+
2593
+ @operation static northWard = {
2594
+ base: 'query',
2595
+ query: { filter: { on: () => Patient, eq: { ward: 'north' } } },
2596
+ };
2597
+ }
2598
+ `;
2599
+ function patientFile(name, ward) {
2600
+ return { data: {
2601
+ type: "card",
2602
+ attributes: {
2603
+ name,
2604
+ ward,
2605
+ lastVitals: null
2606
+ },
2607
+ meta: { adoptsFrom: {
2608
+ module: "../patient",
2609
+ name: "Patient"
2610
+ } }
2611
+ } };
2612
+ }
2613
+ var readOnlyRealmURL = "http://test-realm/other/";
2614
+ (0, import_qunit.module)("Integration | tools | run-realm-code | read-only workspace", function(hooks) {
2615
+ setupRenderingTest(hooks);
2616
+ setupLocalIndexing(hooks);
2617
+ let mockMatrixUtils = setupMockMatrix(hooks, {
2618
+ autostart: true,
2619
+ activeRealms: [testRealmURL, readOnlyRealmURL],
2620
+ realmPermissions: {
2621
+ [testRealmURL]: ["read"],
2622
+ [readOnlyRealmURL]: ["read"]
2623
+ }
2624
+ });
2625
+ let captureRequests = [];
2626
+ setupRealmServerEndpoints(hooks, [{
2627
+ route: "_capture",
2628
+ getResponse: async (req) => {
2629
+ captureRequests.push(await req.clone().json());
2630
+ return new Response(JSON.stringify({ data: {
2631
+ type: "capture-result",
2632
+ attributes: {
2633
+ status: "ready",
2634
+ base64: PNG_BASE64,
2635
+ width: 1,
2636
+ height: 1,
2637
+ contentType: "image/png"
2638
+ }
2639
+ } }), {
2640
+ status: 201,
2641
+ headers: { "Content-Type": "application/vnd.api+json" }
2642
+ });
2643
+ }
2644
+ }]);
2645
+ setupRealmCacheTeardown(hooks);
2646
+ let adapter;
2647
+ hooks.beforeEach(async function() {
2648
+ captureRequests = [];
2649
+ await setupIntegrationTestRealm({
2650
+ mockMatrixUtils,
2651
+ realmURL: readOnlyRealmURL,
2652
+ enforcePermissions: true,
2653
+ permissions: { "*": ["read"] },
2654
+ contents: { "brand.md": "# Brand\n" }
2655
+ });
2656
+ ({adapter} = await setupIntegrationTestRealm({
2657
+ mockMatrixUtils,
2658
+ enforcePermissions: true,
2659
+ permissions: { "*": ["read"] },
2660
+ contents: {
2661
+ "task.json": "{ \"title\": \"Old title\" }\n",
2662
+ "patient.gts": PATIENT_MODULE,
2663
+ "Patient/ana.json": patientFile("Ana", "Cardiology"),
2664
+ "notes/first.md": "# First\n",
2665
+ "pet.gts": `
2666
+ import { CardDef, field, contains } from '@cardstack/base/card-api';
2667
+ import StringField from '@cardstack/base/string';
2668
+
2669
+ export class Pet extends CardDef {
2670
+ static displayName = 'Pet';
2671
+ @field name = contains(StringField);
2672
+ }
2673
+ `,
2674
+ "Pet/mango.json": { data: {
2675
+ type: "card",
2676
+ attributes: { name: "Mango" },
2677
+ meta: { adoptsFrom: {
2678
+ module: "../pet",
2679
+ name: "Pet"
2680
+ } }
2681
+ } }
2682
+ }
2683
+ }));
2684
+ await getService("realm").login(testRealmURL);
2685
+ await getService("realm").login(readOnlyRealmURL);
2686
+ });
2687
+ (0, import_qunit.test)("realm.capture captures in a workspace the user can only read, and in another workspace by URL", async function(assert) {
2688
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2689
+ realm: testRealmURL,
2690
+ roomId: "!room:example.com",
2691
+ code: `return [
2692
+ await realm.capture('notes/first.md'),
2693
+ await realm.capture('${readOnlyRealmURL}brand.md'),
2694
+ ];`
2695
+ });
2696
+ assert.deepEqual(captureRequests.map((r) => r.data.attributes.fileURL), [`${testRealmURL}notes/first.md`, `${readOnlyRealmURL}brand.md`]);
2697
+ assert.deepEqual(captureRequests.map((r) => r.data.attributes.realmURL), [testRealmURL, readOnlyRealmURL], "each capture is asked of the workspace that holds it");
2698
+ assert.deepEqual(JSON.parse(result.scriptResult).map((c) => c.path), ["notes/first.md", `${readOnlyRealmURL}brand.md`], "a capture in another workspace is named by its URL");
2699
+ assert.strictEqual(result.captures.length, 2);
2700
+ });
2701
+ (0, import_qunit.test)("a script reads a workspace the user can only read", async function(assert) {
2702
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2703
+ realm: testRealmURL,
2704
+ roomId: "!room:example.com",
2705
+ code: `return {
2706
+ text: await realm.fs.readText('task.json'),
2707
+ exists: await realm.fs.exists('task.json'),
2708
+ notes: await realm.fs.list('notes'),
2709
+ };`
2710
+ });
2711
+ assert.deepEqual(result.files, [], "nothing is saved");
2712
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2713
+ text: "{ \"title\": \"Old title\" }\n",
2714
+ exists: true,
2715
+ notes: [{
2716
+ name: "first.md",
2717
+ path: "notes/first.md",
2718
+ kind: "file"
2719
+ }]
2720
+ });
2721
+ });
2722
+ (0, import_qunit.test)("a script searches a workspace the user can only read", async function(assert) {
2723
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2724
+ realm: testRealmURL,
2725
+ roomId: "!room:example.com",
2726
+ code: `let found = await realm.cards.search({
2727
+ filter: { type: { module: ${JSON.stringify(`${testRealmURL}pet`)}, name: 'Pet' } },
2728
+ });
2729
+ return { total: found.total, paths: found.cards.map((card) => card.path) };`
2730
+ });
2731
+ assert.deepEqual(result.files, [], "nothing is saved");
2732
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2733
+ total: 1,
2734
+ paths: ["Pet/mango.json"]
2735
+ });
2736
+ });
2737
+ (0, import_qunit.test)("realm.operations is sent to the realm, which decides by its policy", async function(assert) {
2738
+ getService("operations");
2739
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2740
+ realm: testRealmURL,
2741
+ roomId: "!room:example.com",
2742
+ code: `try {
2743
+ await realm.operations.invoke('Patient/ana', 'transferTo', { ward: 'ICU' });
2744
+ return 'ran';
2745
+ } catch (e) {
2746
+ return e.message;
2747
+ }`
2748
+ });
2749
+ let message = JSON.parse(result.scriptResult);
2750
+ assert.notStrictEqual(message, "ran", "no policy grants the operation");
2751
+ assert.notOk(/You can only read this workspace/.test(message), `the runner does not refuse it before the realm does: ${message}`);
2752
+ });
2753
+ (0, import_qunit.test)("each write is refused, and the script goes on", async function(assert) {
2754
+ let toolService = getService("tool-service");
2755
+ let cardService = getService("card-service");
2756
+ let result = await new RunRealmCodeTool(toolService.toolContext).execute({
2757
+ realm: testRealmURL,
2758
+ roomId: "!room:example.com",
2759
+ code: `let refused = [];
2760
+ try { await realm.fs.writeText('new.json', '{}'); } catch (e) { refused.push(e.message); }
2761
+ try { await realm.fs.replace('task.json', 'Old', 'New'); } catch (e) { refused.push(e.message); }
2762
+ return { refused, text: await realm.fs.readText('task.json') };`
2763
+ });
2764
+ assert.deepEqual(result.files, [], "nothing is saved");
2765
+ let { refused, text } = JSON.parse(result.scriptResult);
2766
+ assert.strictEqual(refused.length, 2, "both writes are refused");
2767
+ for (let message of refused) assert.true(/You can only read this workspace/.test(message), `the refusal says why: ${message}`);
2768
+ assert.strictEqual(text, "{ \"title\": \"Old title\" }\n");
2769
+ let source = await cardService.getSource(new URL(`${testRealmURL}new.json`));
2770
+ assert.strictEqual(source.status, 404, "the new file is not in the realm");
2771
+ });
2772
+ (0, import_qunit.test)("a write the script does not catch fails the run, and the report says nothing was saved", async function(assert) {
2773
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2774
+ await assert.rejects(command.execute({
2775
+ realm: testRealmURL,
2776
+ roomId: "!room:example.com",
2777
+ code: `await realm.fs.writeText('new.json', '{}');`
2778
+ }), /You can only read this workspace.*No file was saved/);
2779
+ });
2780
+ (0, import_qunit.test)("realm.index calls are refused", async function(assert) {
2781
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2782
+ realm: testRealmURL,
2783
+ roomId: "!room:example.com",
2784
+ code: `let refused = [];
2785
+ try { await realm.index.invalidate('task.json'); } catch (e) { refused.push(e.message); }
2786
+ try { await realm.index.reindex(); } catch (e) { refused.push(e.message); }
2787
+ return refused;`
2788
+ });
2789
+ let refused = JSON.parse(result.scriptResult);
2790
+ assert.strictEqual(refused.length, 2, "both calls are refused");
2791
+ for (let message of refused) assert.true(/You can only read this workspace/.test(message), `the refusal says why: ${message}`);
2792
+ });
2793
+ (0, import_qunit.test)("a card that is not indexed asks for a user who can write, not for a call the run would refuse", async function(assert) {
2794
+ await adapter.write("Pet/unindexed.json", UNINDEXED_PET);
2795
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2796
+ realm: testRealmURL,
2797
+ roomId: "!room:example.com",
2798
+ code: readCardScript("Pet/unindexed")
2799
+ });
2800
+ assert.strictEqual(JSON.parse(result.scriptResult), `Card is not indexed: ${testRealmURL}Pet/unindexed. Its file Pet/unindexed.json is in the workspace, and the workspace is not indexing, so reading the card again will not find it. You can only read this workspace, so tell the user that Pet/unindexed.json needs indexing again by someone who can write to it`);
2801
+ });
2802
+ });
2803
+ (0, import_qunit.module)("Integration | tools | run-realm-code | images", function(hooks) {
2804
+ setupRenderingTest(hooks);
2805
+ setupLocalIndexing(hooks);
2806
+ let mockMatrixUtils = setupMockMatrix(hooks, {
2807
+ loggedInAs: "@testuser:localhost",
2808
+ activeRealms: [testRealmURL],
2809
+ autostart: true
2810
+ });
2811
+ const PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";
2812
+ let imageRequests = [];
2813
+ let abortedImageRequests = 0;
2814
+ setupRealmServerEndpoints(hooks, [{
2815
+ route: "_request-forward",
2816
+ getResponse: async (req) => {
2817
+ let body = await req.clone().json();
2818
+ let request = JSON.parse(body.requestBody);
2819
+ imageRequests.push({
2820
+ url: body.url,
2821
+ request
2822
+ });
2823
+ if (request.messages[0].content[0].text === "never answers") await new Promise((resolve, reject) => {
2824
+ let fallback = setTimeout(resolve, 2e4);
2825
+ let onAbort = () => {
2826
+ clearTimeout(fallback);
2827
+ abortedImageRequests += 1;
2828
+ reject(new DOMException("aborted", "AbortError"));
2829
+ };
2830
+ if (req.signal.aborted) onAbort();
2831
+ req.signal.addEventListener("abort", onAbort, { once: true });
2832
+ });
2833
+ return new Response(JSON.stringify({ choices: [{ message: {
2834
+ content: "",
2835
+ images: [{
2836
+ type: "image_url",
2837
+ image_url: { url: `data:image/png;base64,${PNG_BASE64}` }
2838
+ }]
2839
+ } }] }), {
2840
+ status: 200,
2841
+ headers: { "Content-Type": "application/json" }
2842
+ });
2843
+ }
2844
+ }]);
2845
+ setupRealmCacheTeardown(hooks);
2846
+ hooks.beforeEach(async function() {
2847
+ imageRequests = [];
2848
+ abortedImageRequests = 0;
2849
+ await setupIntegrationTestRealm({
2850
+ mockMatrixUtils,
2851
+ contents: {
2852
+ "task.json": "{ \"title\": \"Old title\" }\n",
2853
+ "photos/cat.jpg": "not really a jpeg"
2854
+ }
2855
+ });
2856
+ });
2857
+ (0, import_qunit.test)("a script that generates images always waits for the user, and gets more time", function(assert) {
2858
+ let code = `await realm.images.generate({ prompt: 'a cat', path: 'cat.png' });`;
2859
+ assert.true(RunRealmCodeTool.neverAutoExecutesFor({ code }), "a run that names realm.images.generate waits for a click");
2860
+ assert.true(RunRealmCodeTool.extraExecuteTimeMsFor({ code }) > 0, "the tool service gives the run more time");
2861
+ assert.strictEqual(RunRealmCodeTool.extraExecuteTimeMsFor({ code: `await realm.fs.writeText('a.json', '{}');` }), 0, "other runs keep the usual execute timeout");
2862
+ });
2863
+ (0, import_qunit.test)("realm.fs.delete removes an image the same run generated", async function(assert) {
2864
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2865
+ realm: testRealmURL,
2866
+ roomId: "!room:example.com",
2867
+ code: `await realm.images.generate({ prompt: 'A small dot', path: 'images/dot.png' });
2868
+ return {
2869
+ deleted: await realm.fs.delete('images/dot.png'),
2870
+ exists: await realm.fs.exists('images/dot.png'),
2871
+ };`
2872
+ });
2873
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2874
+ deleted: {
2875
+ path: "images/dot.png",
2876
+ deleted: true
2877
+ },
2878
+ exists: false
2879
+ });
2880
+ assert.deepEqual(result.files.map((file) => [file.fileUrl, file.status]), [[`${testRealmURL}images/dot.png`, "deleted"]]);
2881
+ });
2882
+ (0, import_qunit.test)("realm.images.generate saves the image and returns its path", async function(assert) {
2883
+ let toolService = getService("tool-service");
2884
+ let network = getService("network");
2885
+ let command = new RunRealmCodeTool(toolService.toolContext);
2886
+ await command.execute({
2887
+ realm: testRealmURL,
2888
+ roomId: "!room:example.com",
2889
+ code: `await realm.fs.writeBytes('source.png', ${JSON.stringify(PNG_BASE64)});`
2890
+ });
2891
+ let result = await command.execute({
2892
+ realm: testRealmURL,
2893
+ roomId: "!room:example.com",
2894
+ code: `return await realm.images.generate({
2895
+ prompt: 'A small dot',
2896
+ path: 'images/dot',
2897
+ sourceImageUrl: 'source.png',
2898
+ llmModel: 'test/image-model',
2899
+ });`
2900
+ });
2901
+ let expected = Uint8Array.from(atob(PNG_BASE64), (char) => char.charCodeAt(0));
2902
+ assert.deepEqual(JSON.parse(result.scriptResult), {
2903
+ path: "images/dot.png",
2904
+ contentType: "image/png",
2905
+ size: expected.byteLength,
2906
+ saved: true
2907
+ });
2908
+ assert.deepEqual(result.files.map((file) => [file.fileUrl, file.status]), [[`${testRealmURL}images/dot.png`, "saved"]]);
2909
+ assert.strictEqual(imageRequests.length, 1, "one image was requested");
2910
+ let [{ request }] = imageRequests;
2911
+ assert.strictEqual(request.model, "test/image-model");
2912
+ assert.deepEqual(request.messages[0].content, [{
2913
+ type: "text",
2914
+ text: "A small dot"
2915
+ }, {
2916
+ type: "image_url",
2917
+ image_url: { url: `data:image/png;base64,${PNG_BASE64}` }
2918
+ }]);
2919
+ let response = await network.authedFetch(new URL("images/dot.png", testRealmURL));
2920
+ assert.deepEqual(Array.from(new Uint8Array(await response.arrayBuffer())), Array.from(expected), "the realm holds the image bytes");
2921
+ });
2922
+ (0, import_qunit.test)("realm.images.generate refuses a fifth image in one run", async function(assert) {
2923
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2924
+ await assert.rejects(command.execute({
2925
+ realm: testRealmURL,
2926
+ roomId: "!room:example.com",
2927
+ code: `for (let i = 1; i <= 5; i++) {
2928
+ await realm.images.generate({ prompt: 'dot ' + i, path: 'dots/' + i + '.png' });
2929
+ }`
2930
+ }), /at most 4 images.*Files already saved by this run: .*dots\/4\.png/);
2931
+ assert.strictEqual(imageRequests.length, 4, "four images were requested");
2932
+ });
2933
+ (0, import_qunit.test)("realm.images.generate refuses an existing file before it asks the model", async function(assert) {
2934
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2935
+ await assert.rejects(command.execute({
2936
+ realm: testRealmURL,
2937
+ roomId: "!room:example.com",
2938
+ code: `await realm.images.generate({ prompt: 'a task', path: 'task.json' });`
2939
+ }), /File already exists/);
2940
+ assert.strictEqual(imageRequests.length, 0, "no image was requested");
2941
+ });
2942
+ (0, import_qunit.test)("realm.images.generate refuses a path whose image may be saved over an existing file, before it asks the model", async function(assert) {
2943
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2944
+ for (let path of ["photos/cat", "photos/cat.png"]) await assert.rejects(command.execute({
2945
+ realm: testRealmURL,
2946
+ roomId: "!room:example.com",
2947
+ code: `await realm.images.generate({ prompt: 'a cat', path: '${path}' });`
2948
+ }), /File already exists: .*photos\/cat\.jpg/, `${path} is refused`);
2949
+ assert.strictEqual(imageRequests.length, 0, "no image was requested");
2950
+ });
2951
+ (0, import_qunit.test)("realm.images.generate takes one file slot per image", async function(assert) {
2952
+ let result = await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
2953
+ realm: testRealmURL,
2954
+ roomId: "!room:example.com",
2955
+ code: `for (let i = 1; i <= 16; i++) {
2956
+ await realm.fs.exists('missing-' + i + '.json');
2957
+ }
2958
+ let paths = [];
2959
+ for (let i = 1; i <= 4; i++) {
2960
+ paths.push((await realm.images.generate({ prompt: 'dot ' + i, path: 'dots/' + i })).path);
2961
+ }
2962
+ return paths;`
2963
+ });
2964
+ assert.deepEqual(JSON.parse(result.scriptResult), [
2965
+ "dots/1.png",
2966
+ "dots/2.png",
2967
+ "dots/3.png",
2968
+ "dots/4.png"
2969
+ ]);
2970
+ });
2971
+ (0, import_qunit.test)("realm.images.generate refuses a source image outside this realm", async function(assert) {
2972
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2973
+ await assert.rejects(command.execute({
2974
+ realm: testRealmURL,
2975
+ roomId: "!room:example.com",
2976
+ code: `await realm.images.generate({
2977
+ prompt: 'a dot',
2978
+ path: 'dot.png',
2979
+ sourceImageUrl: 'http://other-realm.example/private/photo.png',
2980
+ });`
2981
+ }), /Path is outside this realm/);
2982
+ assert.strictEqual(imageRequests.length, 0, "no image was requested");
2983
+ });
2984
+ (0, import_qunit.test)("an image request still in flight when the run ends is stopped", async function(assert) {
2985
+ let cardService = getService("card-service");
2986
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
2987
+ let started = Date.now();
2988
+ await assert.rejects(command.execute({
2989
+ realm: testRealmURL,
2990
+ roomId: "!room:example.com",
2991
+ code: `realm.images.generate({ prompt: 'never answers', path: 'late.png' });`
2992
+ }), /await every realm call/);
2993
+ assert.true(Date.now() - started < 15e3, "the run reports without waiting for the image");
2994
+ if (imageRequests.length > 0) assert.strictEqual(abortedImageRequests, 1, "the image request was aborted");
2995
+ await settled();
2996
+ let source = await cardService.getSource(new URL(`${testRealmURL}late.png`));
2997
+ assert.strictEqual(source.status, 404, "no image was saved");
2998
+ });
2999
+ (0, import_qunit.test)("a script that reaches realm.images.generate another way is refused", async function(assert) {
3000
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
3001
+ let code = `let images = realm['ima' + 'ges'];
3002
+ await images.generate({ prompt: 'a dot', path: 'dot.png' });`;
3003
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code }), "this run would not wait for a click");
3004
+ await assert.rejects(command.execute({
3005
+ realm: testRealmURL,
3006
+ roomId: "!room:example.com",
3007
+ code
3008
+ }), /realm\.images\.generate must be called by that name/);
3009
+ assert.strictEqual(imageRequests.length, 0, "no image was requested");
3010
+ });
3011
+ });
3012
+ (0, import_qunit.module)("Integration | tools | run-realm-code | catalog-reuse", function(hooks) {
3013
+ setupRenderingTest(hooks);
3014
+ hooks.beforeEach(function() {
3015
+ setCatalogRealmURL(getService("network").virtualNetwork.toURL("@cardstack/catalog/").href);
3016
+ });
3017
+ hooks.afterEach(function() {
3018
+ resetCatalogRealmURL();
3019
+ });
3020
+ setupLocalIndexing(hooks);
3021
+ let mockMatrixUtils = setupMockMatrix(hooks, {
3022
+ loggedInAs: "@testuser:localhost",
3023
+ activeRealms: [testRealmURL],
3024
+ autostart: true
3025
+ });
3026
+ setupRealmCacheTeardown(hooks);
3027
+ hooks.beforeEach(async function() {
3028
+ await setupIntegrationTestRealm({
3029
+ mockMatrixUtils,
3030
+ contents: { "note.md": "# Note\n" }
3031
+ });
3032
+ });
3033
+ (0, import_qunit.test)("realm.entries.search leaves the catalog out unless the room enabled catalog-reuse", async function(assert) {
3034
+ await getService("matrix-service").ready;
3035
+ let roomId = mockMatrixUtils.createAndJoinRoom({
3036
+ sender: "@testuser:localhost",
3037
+ name: "catalog-reuse"
3038
+ });
3039
+ let { virtualNetwork } = getService("network");
3040
+ let sentRealms = [];
3041
+ let spy = async (request) => {
3042
+ if (new URL(request.url).pathname.endsWith("/_federated-search")) sentRealms.push(JSON.parse(await request.clone().text()).realms);
3043
+ return null;
3044
+ };
3045
+ virtualNetwork.mount(spy, { prepend: true });
3046
+ let catalogURL = virtualNetwork.toURL("@cardstack/catalog/").href;
3047
+ try {
3048
+ let realmServer = getService("realm-server");
3049
+ await realmServer.ready;
3050
+ let isCatalog = (realm) => realm === "@cardstack/catalog/" || realm === catalogURL;
3051
+ assert.ok(realmServer.availableRealmIdentifiers.some(isCatalog), "the catalog is one of the available realms");
3052
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
3053
+ let search = (code) => command.execute({
3054
+ realm: testRealmURL,
3055
+ roomId,
3056
+ code
3057
+ });
3058
+ await search(`return await realm.entries.search({}, { realms: ['@cardstack/catalog/'] });`);
3059
+ await search(`await realm.entries.search({}, { realms: ['@cardstack/catalog/', realm.current.url] });`);
3060
+ await search(`await realm.entries.search({});`);
3061
+ await search(`await realm.entries.search({}, { realms: [${JSON.stringify(`${catalogURL}components/`)}] });`);
3062
+ assert.strictEqual(sentRealms.length, 2, "a search of the catalog alone, or of a folder in it, is answered empty, without a request");
3063
+ assert.deepEqual(sentRealms[0], [testRealmURL], "a named catalog is dropped");
3064
+ assert.notOk(sentRealms[1].some(isCatalog), "the default realms leave the catalog out");
3065
+ mockMatrixUtils.simulateRemoteMessage(roomId, "@testuser:localhost", {
3066
+ msgtype: "m.text",
3067
+ body: "boxel-debug:feature:enable:catalog-reuse"
3068
+ });
3069
+ await settled();
3070
+ await search(`await realm.entries.search({}, { realms: ['@cardstack/catalog/'] });`);
3071
+ assert.deepEqual(sentRealms[2], ["@cardstack/catalog/"], "with the feature on, the catalog is searched");
3072
+ } finally {
3073
+ virtualNetwork.unmount(spy);
3074
+ }
3075
+ });
3076
+ (0, import_qunit.test)("realm.entries.search records each search that reaches the catalog in client telemetry", async function(assert) {
3077
+ await getService("matrix-service").ready;
3078
+ let roomId = mockMatrixUtils.createAndJoinRoom({
3079
+ sender: "@testuser:localhost",
3080
+ name: "catalog-reuse"
3081
+ });
3082
+ let { virtualNetwork } = getService("network");
3083
+ let catalogURL = virtualNetwork.toURL("@cardstack/catalog/").href;
3084
+ let federatedSearch = async (request) => {
3085
+ if (!new URL(request.url).pathname.endsWith("/_federated-search")) return null;
3086
+ let body = await request.clone().text();
3087
+ if (body.includes("boom")) return new Response("the catalog is down", { status: 500 });
3088
+ let { realms } = JSON.parse(body);
3089
+ let realmTotals = {};
3090
+ let data = [];
3091
+ let included = [];
3092
+ let found = (id, readMe = "") => {
3093
+ data.push({
3094
+ type: "entry",
3095
+ id,
3096
+ relationships: { item: { data: {
3097
+ type: "card",
3098
+ id
3099
+ } } }
3100
+ });
3101
+ included.push({
3102
+ type: "card",
3103
+ id,
3104
+ attributes: { readMe }
3105
+ });
3106
+ };
3107
+ for (let realm of realms) {
3108
+ let url = virtualNetwork.toURL(realm).href;
3109
+ if (url === catalogURL) {
3110
+ if (body.includes("silent")) continue;
3111
+ let huge = body.includes("huge");
3112
+ realmTotals[url] = body.includes("kanban") ? 2 : huge ? 1 : 0;
3113
+ if (realmTotals[url] > 0) found(`${catalogURL}Spec/board`, huge ? "x".repeat(11e5) : "");
3114
+ } else if (url === testRealmURL) {
3115
+ realmTotals[url] = body.includes("kanban") ? 1 : body.includes("notes") ? 3 : 0;
3116
+ if (realmTotals[url] > 0) found(`${testRealmURL}note`);
3117
+ } else realmTotals[url] = 0;
3118
+ }
3119
+ return new Response(JSON.stringify({
3120
+ data,
3121
+ included,
3122
+ meta: {
3123
+ page: { total: Object.values(realmTotals).reduce((a, b) => a + b, 0) },
3124
+ realmTotals,
3125
+ ...Object.keys(realmTotals).length < realms.length ? { incomplete: true } : {}
3126
+ }
3127
+ }), { headers: { "content-type": SupportedMimeType.CardJson } });
3128
+ };
3129
+ virtualNetwork.mount(federatedSearch, { prepend: true });
3130
+ let telemetry = getService("client-telemetry");
3131
+ let recorded = [];
3132
+ let recordEvent = telemetry.recordEvent;
3133
+ telemetry.recordEvent = (event) => {
3134
+ recorded.push(event);
3135
+ };
3136
+ try {
3137
+ await getService("realm-server").ready;
3138
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
3139
+ let search = (matches, realms) => command.execute({
3140
+ realm: testRealmURL,
3141
+ roomId,
3142
+ code: `await realm.entries.search(
3143
+ {
3144
+ filter: {
3145
+ on: { module: '@cardstack/base/spec', name: 'Spec' },
3146
+ matches: ${JSON.stringify(matches)},
3147
+ },
3148
+ },
3149
+ { realms: ${realms} },
3150
+ );`
3151
+ });
3152
+ await search("kanban", `['@cardstack/catalog/']`);
3153
+ assert.deepEqual(recorded, [], "a search the feature gate keeps out of the catalog is not recorded");
3154
+ mockMatrixUtils.simulateRemoteMessage(roomId, "@testuser:localhost", {
3155
+ msgtype: "m.text",
3156
+ body: "boxel-debug:feature:enable:catalog-reuse"
3157
+ });
3158
+ await waitUntil(() => getService("matrix-service").roomResources.get(roomId)?.events.some((event) => event.content?.body === "boxel-debug:feature:enable:catalog-reuse"));
3159
+ await search("kanban board", `['@cardstack/catalog/']`);
3160
+ await search("kanban", `['@cardstack/catalog/', realm.current.url]`);
3161
+ await search("notes", "[]");
3162
+ await search("kanban", "[realm.current.url]");
3163
+ await assert.rejects(search("boom", `['@cardstack/catalog/']`), /the catalog is down/);
3164
+ await search("silent", `['@cardstack/catalog/', realm.current.url]`);
3165
+ await assert.rejects(search("huge", `['@cardstack/catalog/']`), /too large to return/);
3166
+ let run = (code) => command.execute({
3167
+ realm: testRealmURL,
3168
+ roomId,
3169
+ code
3170
+ });
3171
+ await assert.rejects(run(`await realm.entries.search({}, { realms: ['@cardstack/catalog/'], scope: 'card' });`), /Invalid scope "card"/);
3172
+ await assert.rejects(run(`await realm.entries.search({}, { realms: ['@cardstack/catalog/', '@nope/realm/'] });`), /Unknown realm identifier "@nope\/realm\/"/);
3173
+ await run(`await realm.entries.search(
3174
+ {
3175
+ filter: {
3176
+ every: [
3177
+ { matches: ${JSON.stringify(`${"x".repeat(298)}😀${"y".repeat(100)}`)} },
3178
+ { not: { type: { module: '@cardstack/base/spec', name: 'Spec' } } },
3179
+ ],
3180
+ },
3181
+ },
3182
+ { realms: ['@cardstack/catalog/'] },
3183
+ );`);
3184
+ let spec = "@cardstack/base/spec#Spec";
3185
+ let event = (fields) => ({
3186
+ event_type: "catalog-search",
3187
+ tool: "run-realm-code",
3188
+ query_type: spec,
3189
+ error: "",
3190
+ realm: testRealmURL,
3191
+ ...fields
3192
+ });
3193
+ let [named, beside, everyRealm, failed, unanswered, tooLarge, excluding, ...rest] = recorded;
3194
+ assert.deepEqual(named, event({
3195
+ query_text: "kanban board",
3196
+ total: 2,
3197
+ returned: 1
3198
+ }), "a search that names the catalog is recorded with what it found");
3199
+ assert.deepEqual(beside, event({
3200
+ query_text: "kanban",
3201
+ total: 2,
3202
+ returned: 1
3203
+ }), "only what the catalog matched is counted, not the realm searched beside it");
3204
+ assert.deepEqual(everyRealm, event({
3205
+ query_text: "notes",
3206
+ total: 0,
3207
+ returned: 0
3208
+ }), "a search of every realm that matched only workspace cards found nothing in the catalog");
3209
+ let { error, ...failedRest } = failed;
3210
+ assert.deepEqual({
3211
+ ...failedRest,
3212
+ error: ""
3213
+ }, event({
3214
+ query_text: "boom",
3215
+ total: null,
3216
+ returned: null
3217
+ }), "a search that threw is recorded without counts");
3218
+ assert.true(error.includes("the catalog is down"), `the error is recorded: ${error}`);
3219
+ assert.deepEqual(unanswered, event({
3220
+ query_text: "silent",
3221
+ total: null,
3222
+ returned: null,
3223
+ error: "a catalog realm did not answer"
3224
+ }), "a catalog that did not answer is recorded as a failure, not as finding nothing");
3225
+ assert.deepEqual(tooLarge, event({
3226
+ query_text: "huge",
3227
+ total: 1,
3228
+ returned: 1,
3229
+ error: `The entry ${catalogURL}Spec/board is too large to return from a search`
3230
+ }), "an answer too large to return keeps its counts and records why the script got nothing");
3231
+ assert.deepEqual(excluding, event({
3232
+ query_text: `${"x".repeat(298)}😀…`,
3233
+ query_type: "",
3234
+ total: 0,
3235
+ returned: 0
3236
+ }), "a type the query excludes is not the type it searched for, and long text is cut without splitting a character");
3237
+ assert.deepEqual(rest, [], "a search of another realm only, or one refused before it was sent, is not recorded");
3238
+ } finally {
3239
+ telemetry.recordEvent = recordEvent;
3240
+ virtualNetwork.unmount(federatedSearch);
3241
+ }
3242
+ });
3243
+ (0, import_qunit.test)("realm.cards.search in the catalog finds nothing unless the room enabled catalog-reuse", async function(assert) {
3244
+ await getService("matrix-service").ready;
3245
+ let roomId = mockMatrixUtils.createAndJoinRoom({
3246
+ sender: "@testuser:localhost",
3247
+ name: "catalog-reuse"
3248
+ });
3249
+ let { virtualNetwork } = getService("network");
3250
+ let catalogURL = virtualNetwork.toURL("@cardstack/catalog/").href;
3251
+ let searches = 0;
3252
+ let catalog = async (request) => {
3253
+ let { pathname } = new URL(request.url);
3254
+ if (request.url === `${catalogURL}_info`) return new Response(JSON.stringify({ data: {
3255
+ id: catalogURL,
3256
+ type: "realm-info",
3257
+ attributes: { name: "Catalog" }
3258
+ } }), { headers: {
3259
+ "content-type": SupportedMimeType.RealmInfo,
3260
+ "x-boxel-realm-public-readable": "true"
3261
+ } });
3262
+ if (pathname.endsWith("_search") || pathname.endsWith("_federated-search")) {
3263
+ searches++;
3264
+ return new Response(JSON.stringify({
3265
+ data: [],
3266
+ meta: { page: { total: 0 } }
3267
+ }), { headers: { "content-type": SupportedMimeType.CardJson } });
3268
+ }
3269
+ return null;
3270
+ };
3271
+ virtualNetwork.mount(catalog, { prepend: true });
3272
+ try {
3273
+ await getService("realm").ensureRealmMeta(catalogURL);
3274
+ let command = new RunRealmCodeTool(getService("tool-service").toolContext);
3275
+ let search = async () => JSON.parse((await command.execute({
3276
+ realm: "@cardstack/catalog/",
3277
+ roomId,
3278
+ code: `return await realm.cards.search({});`
3279
+ })).scriptResult);
3280
+ assert.deepEqual(await search(), {
3281
+ cards: [],
3282
+ total: 0,
3283
+ truncated: false
3284
+ }, "the catalog answers empty");
3285
+ assert.strictEqual(searches, 0, "without a request");
3286
+ mockMatrixUtils.simulateRemoteMessage(roomId, "@testuser:localhost", {
3287
+ msgtype: "m.text",
3288
+ body: "boxel-debug:feature:enable:catalog-reuse"
3289
+ });
3290
+ await waitUntil(() => getService("matrix-service").roomResources.get(roomId)?.events.some((event) => event.content?.body === "boxel-debug:feature:enable:catalog-reuse"));
3291
+ await search();
3292
+ assert.strictEqual(searches, 1, "with the feature on, it is searched");
3293
+ } finally {
3294
+ virtualNetwork.unmount(catalog);
3295
+ }
3296
+ });
3297
+ });
3298
+ (0, import_qunit.module)("Integration | tools | run-realm-code | research", function(hooks) {
3299
+ setupRenderingTest(hooks);
3300
+ setupLocalIndexing(hooks);
3301
+ let mockMatrixUtils = setupMockMatrix(hooks, {
3302
+ loggedInAs: "@testuser:localhost",
3303
+ activeRealms: [testRealmURL],
3304
+ autostart: true
3305
+ });
3306
+ let forwarded = [];
3307
+ let answer;
3308
+ setupRealmServerEndpoints(hooks, [{
3309
+ route: "_request-forward",
3310
+ getResponse: async (req) => {
3311
+ let body = await req.clone().json();
3312
+ let request = {
3313
+ url: body.url,
3314
+ request: JSON.parse(body.requestBody)
3315
+ };
3316
+ forwarded.push(request);
3317
+ return answer(request);
3318
+ }
3319
+ }]);
3320
+ setupRealmCacheTeardown(hooks);
3321
+ function json(body, status = 200) {
3322
+ return new Response(JSON.stringify(body), {
3323
+ status,
3324
+ headers: { "Content-Type": "application/json" }
3325
+ });
3326
+ }
3327
+ function chat(content, extra = {}) {
3328
+ return json({
3329
+ model: "openai/gpt-4.1-mini",
3330
+ choices: [{
3331
+ finish_reason: "stop",
3332
+ message: {
3333
+ content,
3334
+ ...extra
3335
+ }
3336
+ }]
3337
+ });
3338
+ }
3339
+ const RECIPE_SCHEMA = {
3340
+ type: "object",
3341
+ additionalProperties: false,
3342
+ required: ["title", "servings"],
3343
+ properties: {
3344
+ title: { type: "string" },
3345
+ servings: {
3346
+ type: "integer",
3347
+ minimum: 1
3348
+ }
3349
+ }
3350
+ };
3351
+ async function run(code) {
3352
+ return await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
3353
+ realm: testRealmURL,
3354
+ roomId: "!room:example.com",
3355
+ code
3356
+ });
3357
+ }
3358
+ hooks.beforeEach(async function() {
3359
+ forwarded = [];
3360
+ answer = () => json({ error: { message: "no answer set" } }, 500);
3361
+ await setupIntegrationTestRealm({
3362
+ mockMatrixUtils,
3363
+ contents: { "task.json": "{ \"title\": \"Old title\" }\n" }
3364
+ });
3365
+ });
3366
+ (0, import_qunit.test)("research calls need no click, and a run that names one gets more time", function(assert) {
3367
+ for (let code of [
3368
+ `await realm.web.search('chili recipe');`,
3369
+ `await realm.web.read('https://example.com/');`,
3370
+ `await realm.resolve({});`
3371
+ ]) {
3372
+ assert.false(RunRealmCodeTool.neverAutoExecutesFor({ code }), `no click for ${code}`);
3373
+ assert.true(RunRealmCodeTool.extraExecuteTimeMsFor({ code }) > 0, `more time for ${code}`);
3374
+ }
3375
+ });
3376
+ (0, import_qunit.test)("realm.web.search returns the url_citation sources of an Exa search", async function(assert) {
3377
+ answer = () => chat("Here are some chili recipes.", { annotations: [
3378
+ {
3379
+ type: "url_citation",
3380
+ url_citation: {
3381
+ url: "https://www.budgetbytes.com/chili/",
3382
+ title: "Chili - Budget Bytes",
3383
+ content: "A classic beef chili."
3384
+ }
3385
+ },
3386
+ {
3387
+ type: "url_citation",
3388
+ url_citation: {
3389
+ url: "https://www.budgetbytes.com/chili/",
3390
+ title: "Duplicate",
3391
+ content: "Duplicate"
3392
+ }
3393
+ },
3394
+ {
3395
+ type: "file",
3396
+ file: { name: "ignored" }
3397
+ },
3398
+ {
3399
+ type: "url_citation",
3400
+ url_citation: { url: "https://example.com/chili" }
3401
+ }
3402
+ ] });
3403
+ let result = await run(`return await realm.web.search('beef chili recipe', { limit: 3, includeDomains: ['BudgetBytes.com'] });`);
3404
+ assert.deepEqual(JSON.parse(result.scriptResult), [{
3405
+ url: "https://www.budgetbytes.com/chili/",
3406
+ title: "Chili - Budget Bytes",
3407
+ snippet: "A classic beef chili."
3408
+ }, {
3409
+ url: "https://example.com/chili",
3410
+ title: "",
3411
+ snippet: ""
3412
+ }]);
3413
+ assert.strictEqual(forwarded.length, 1, "one request was forwarded");
3414
+ let [{ url, request }] = forwarded;
3415
+ assert.strictEqual(url, "https://openrouter.ai/api/v1/chat/completions");
3416
+ assert.deepEqual(request.plugins, [{
3417
+ id: "web",
3418
+ engine: "exa",
3419
+ max_results: 3,
3420
+ include_domains: ["budgetbytes.com"]
3421
+ }]);
3422
+ assert.strictEqual(request.messages.at(-1).content, "beef chili recipe", "the query is the user message");
3423
+ assert.strictEqual(result.activity.length, 1, "the search is in the run activity");
3424
+ assert.true(/Exa searched for "beef chili recipe".*2 results/.test(result.activity[0]), result.activity[0]);
3425
+ });
3426
+ (0, import_qunit.test)("realm.web.search refuses a limit outside 1 to 10 without a request", async function(assert) {
3427
+ await assert.rejects(run(`await realm.web.search('chili', { limit: 11 });`), /limit must be a whole number from 1 to 10/);
3428
+ assert.strictEqual(forwarded.length, 0, "nothing was forwarded");
3429
+ });
3430
+ (0, import_qunit.test)("realm.web.read returns the page markdown, capped, with its metadata", async function(assert) {
3431
+ let markdown = "# Chili\n" + "x".repeat(7e4);
3432
+ answer = () => json({
3433
+ success: true,
3434
+ data: {
3435
+ markdown,
3436
+ metadata: {
3437
+ title: "Chili",
3438
+ statusCode: 200,
3439
+ contentType: "text/html; charset=utf-8",
3440
+ sourceURL: "https://www.budgetbytes.com/chili/",
3441
+ ogImage: "https://www.budgetbytes.com/chili.jpg"
3442
+ }
3443
+ }
3444
+ });
3445
+ let result = await run(`let page = await realm.web.read('https://www.budgetbytes.com/chili/');
3446
+ return { url: page.url, length: page.markdown.length, start: page.markdown.slice(0, 7), truncated: page.truncated, metadata: page.metadata };`);
3447
+ assert.deepEqual(JSON.parse(result.scriptResult), {
3448
+ url: "https://www.budgetbytes.com/chili/",
3449
+ length: 64e3,
3450
+ start: "# Chili",
3451
+ truncated: true,
3452
+ metadata: {
3453
+ statusCode: 200,
3454
+ contentType: "text/html; charset=utf-8",
3455
+ title: "Chili",
3456
+ sourceURL: "https://www.budgetbytes.com/chili/"
3457
+ }
3458
+ });
3459
+ let [{ url, request }] = forwarded;
3460
+ assert.strictEqual(url, "https://api.firecrawl.dev/v2/scrape");
3461
+ assert.strictEqual(request.url, "https://www.budgetbytes.com/chili/");
3462
+ assert.deepEqual(request.formats, ["markdown"]);
3463
+ assert.true(request.onlyMainContent, "main content by default");
3464
+ assert.deepEqual(result.activity, ["Firecrawl read www.budgetbytes.com"]);
3465
+ });
3466
+ (0, import_qunit.test)("realm.web.read throws with the status and Firecrawl’s reason", async function(assert) {
3467
+ answer = ({ request }) => request.url.endsWith(".svg") ? json({
3468
+ success: false,
3469
+ code: "SCRAPE_UNSUPPORTED_FILE_ERROR",
3470
+ error: "The file type is not supported."
3471
+ }, 400) : json({
3472
+ success: false,
3473
+ error: "This website is no longer supported, we do not support this site."
3474
+ }, 403);
3475
+ let result = await run(`let errors = [];
3476
+ for (let url of ['https://www.seriouseats.com/chili', 'https://example.com/logo.svg']) {
3477
+ try {
3478
+ await realm.web.read(url);
3479
+ } catch (error) {
3480
+ errors.push(error.message);
3481
+ }
3482
+ }
3483
+ return errors;`);
3484
+ let [site, svg] = JSON.parse(result.scriptResult);
3485
+ assert.true(/HTTP 403/.test(site), site);
3486
+ assert.true(/we do not support this site/.test(site), site);
3487
+ assert.true(/HTTP 400/.test(svg), svg);
3488
+ assert.true(/SCRAPE_UNSUPPORTED_FILE_ERROR/.test(svg), svg);
3489
+ assert.strictEqual(result.activity.length, 2);
3490
+ assert.true(/^Firecrawl could not read www\.seriouseats\.com: .*we do not support this site/.test(result.activity[0]), result.activity[0]);
3491
+ });
3492
+ (0, import_qunit.test)("an uncaught realm.web.read failure fails the run with the reason", async function(assert) {
3493
+ answer = () => json({
3494
+ success: false,
3495
+ error: "we do not support this site"
3496
+ }, 403);
3497
+ await assert.rejects(run(`await realm.web.read('https://www.seriouseats.com/chili');`), /HTTP 403: we do not support this site/);
3498
+ });
3499
+ (0, import_qunit.test)("realm.resolve returns JSON that matches outputSchema", async function(assert) {
3500
+ answer = () => chat("{\"title\":\"Beef Chili\",\"servings\":6}");
3501
+ let result = await run(`return await realm.resolve({
3502
+ instruction: 'Normalize this recipe header.',
3503
+ input: { name: ' beef chili ', serves: 'six' },
3504
+ error: 'servings must be a number',
3505
+ outputSchema: ${JSON.stringify(RECIPE_SCHEMA)},
3506
+ });`);
3507
+ assert.deepEqual(JSON.parse(result.scriptResult), {
3508
+ output: {
3509
+ title: "Beef Chili",
3510
+ servings: 6
3511
+ },
3512
+ model: "openai/gpt-4.1-mini"
3513
+ });
3514
+ let [{ request }] = forwarded;
3515
+ assert.deepEqual(request.response_format.json_schema.schema, RECIPE_SCHEMA);
3516
+ assert.true(request.response_format.json_schema.strict, "strict output");
3517
+ assert.deepEqual(JSON.parse(request.messages[1].content), {
3518
+ instruction: "Normalize this recipe header.",
3519
+ input: {
3520
+ name: " beef chili ",
3521
+ serves: "six"
3522
+ },
3523
+ error: "servings must be a number"
3524
+ });
3525
+ });
3526
+ (0, import_qunit.test)("realm.resolve refuses an answer that does not match outputSchema", async function(assert) {
3527
+ answer = () => chat("{\"title\":\"Beef Chili\",\"servings\":0,\"notes\":\"x\"}");
3528
+ await assert.rejects(run(`await realm.resolve({
3529
+ instruction: 'Normalize this recipe header.',
3530
+ input: { name: 'beef chili' },
3531
+ outputSchema: ${JSON.stringify(RECIPE_SCHEMA)},
3532
+ });`), /does not match outputSchema/);
3533
+ });
3534
+ (0, import_qunit.test)("realm.resolve refuses an open schema without a request", async function(assert) {
3535
+ await assert.rejects(run(`await realm.resolve({
3536
+ instruction: 'Normalize this.',
3537
+ input: { name: 'beef chili' },
3538
+ outputSchema: { type: 'object', properties: { title: { type: 'string' } }, required: ['title'] },
3539
+ });`), /additionalProperties must be false/);
3540
+ assert.strictEqual(forwarded.length, 0, "nothing was forwarded");
3541
+ });
3542
+ (0, import_qunit.test)("realm.resolve may be called at most 3 times per run", async function(assert) {
3543
+ answer = () => chat("{\"title\":\"Chili\",\"servings\":4}");
3544
+ let result = await run(`let outcomes = [];
3545
+ for (let i = 0; i < 4; i++) {
3546
+ try {
3547
+ await realm.resolve({
3548
+ instruction: 'Normalize this recipe header.',
3549
+ input: { i },
3550
+ outputSchema: ${JSON.stringify(RECIPE_SCHEMA)},
3551
+ });
3552
+ outcomes.push('ok');
3553
+ } catch (error) {
3554
+ outcomes.push(error.message);
3555
+ }
3556
+ }
3557
+ return outcomes;`);
3558
+ let outcomes = JSON.parse(result.scriptResult);
3559
+ assert.deepEqual(outcomes.slice(0, 3), [
3560
+ "ok",
3561
+ "ok",
3562
+ "ok"
3563
+ ]);
3564
+ assert.true(/at most 3 times per run/.test(outcomes[3]), outcomes[3]);
3565
+ assert.strictEqual(forwarded.length, 3, "three requests were forwarded");
3566
+ });
3567
+ (0, import_qunit.test)("realm.resolve refuses a request for code, and an answer that holds code", async function(assert) {
3568
+ await assert.rejects(run(`await realm.resolve({
3569
+ instruction: 'Write a JavaScript function that parses this recipe.',
3570
+ input: { name: 'beef chili' },
3571
+ outputSchema: ${JSON.stringify(RECIPE_SCHEMA)},
3572
+ });`), /does not write code, scripts, markup or commands/);
3573
+ assert.strictEqual(forwarded.length, 0, "no model was asked");
3574
+ answer = () => chat("{\"title\":\"<script>alert(1)<\/script>\",\"servings\":4}");
3575
+ await assert.rejects(run(`await realm.resolve({
3576
+ instruction: 'Normalize this recipe header.',
3577
+ input: { name: 'beef chili' },
3578
+ outputSchema: ${JSON.stringify(RECIPE_SCHEMA)},
3579
+ });`), /the answer looked like code/);
3580
+ });
3581
+ (0, import_qunit.test)("realm.resolve takes data instructions and answers that only share words with code", async function(assert) {
3582
+ answer = () => chat(JSON.stringify({
3583
+ title: "Create an account first => then <b>log in</b>",
3584
+ servings: 4
3585
+ }));
3586
+ for (let instruction of [
3587
+ "Update the prices to USD.",
3588
+ "Normalize each address and return the ISO country code.",
3589
+ "Export the recipe header as a title and a serving count."
3590
+ ]) {
3591
+ let result = await run(`return await realm.resolve({
3592
+ instruction: ${JSON.stringify(instruction)},
3593
+ input: { name: 'beef chili' },
3594
+ outputSchema: ${JSON.stringify(RECIPE_SCHEMA)},
3595
+ });`);
3596
+ assert.strictEqual(JSON.parse(result.scriptResult).output.servings, 4, `answered: ${instruction}`);
3597
+ }
3598
+ });
3599
+ (0, import_qunit.test)("realm.web.search takes maxResults as an alias of limit, and includeContent", async function(assert) {
3600
+ answer = () => chat("Sources.", { annotations: [{
3601
+ type: "url_citation",
3602
+ url_citation: {
3603
+ url: "https://example.com/a",
3604
+ title: "A",
3605
+ content: "y".repeat(3e3)
3606
+ }
3607
+ }, {
3608
+ type: "url_citation",
3609
+ url_citation: {
3610
+ url: "http://192.168.1.10/private",
3611
+ title: "B"
3612
+ }
3613
+ }] });
3614
+ let result = await run(`let [found] = await realm.web.search('logos', { maxResults: 2, includeContent: true, tbs: 'qdr:m' });
3615
+ return { count: (await realm.web.search('logos', { limit: 2, maxResults: 2 })).length, snippet: found.snippet.length, content: found.content.length };`);
3616
+ assert.deepEqual(JSON.parse(result.scriptResult), {
3617
+ count: 1,
3618
+ snippet: 1e3,
3619
+ content: 3e3
3620
+ });
3621
+ assert.strictEqual(forwarded[0].request.plugins[0].max_results, 2);
3622
+ assert.true(forwarded[0].request.messages.at(-1).content.includes("qdr:m"), "the time filter reaches the model");
3623
+ await assert.rejects(run(`await realm.web.search('logos', { limit: 2, maxResults: 3 });`), /limit and maxResults must match/);
3624
+ });
3625
+ (0, import_qunit.test)("realm.web.read refuses local and private-network URLs without a request", async function(assert) {
3626
+ for (let url of [
3627
+ "http://localhost:4201/base/",
3628
+ "http://192.168.1.10/",
3629
+ "https://user:secret@example.com/"
3630
+ ]) await assert.rejects(run(`await realm.web.read('${url}');`), /cannot read URLs with credentials, local hosts or private-network addresses/, url);
3631
+ assert.strictEqual(forwarded.length, 0, "nothing was forwarded");
3632
+ });
3633
+ (0, import_qunit.test)("realm.activity records the steps of a multi-step script", async function(assert) {
3634
+ let result = await run(`await realm.activity({ message: 'Gather sources', current: 2, total: 4 });
3635
+ await realm.activity({ message: 'Write the card', phase: 'write' });
3636
+ return 1;`);
3637
+ assert.deepEqual(result.activity, ["Step 2 of 4 · Gather sources", "Write the card (write)"]);
3638
+ await assert.rejects(run(`await realm.activity({ message: 'Too far', current: 5, total: 4 });`), /current cannot be more than total/);
3639
+ });
3640
+ (0, import_qunit.test)("realm.plan names the steps ahead, and activity counts inside one", async function(assert) {
3641
+ let result = await run(`await realm.plan(['Search calendars', 'Read each page', 'Compare']);
3642
+ await realm.activity({ message: 'Read each page', current: 2, total: 3, count: 3, of: 5 });
3643
+ return 1;`);
3644
+ assert.deepEqual(result.activity, ["Plan · Search calendars · Read each page · Compare", "Step 2 of 3 · Read each page"]);
3645
+ await assert.rejects(run(`await realm.plan([]);`), /realm.plan expects 1 to 12 step names/);
3646
+ await assert.rejects(run(`await realm.activity({ message: 'Read', current: 1, total: 2, count: 6, of: 5 });`), /count cannot be more than of/);
3647
+ });
3648
+ function answerFixes(fix) {
3649
+ answer = ({ request }) => {
3650
+ let { code } = JSON.parse(request.messages[1].content);
3651
+ return chat(JSON.stringify({
3652
+ fixedCode: fix(code),
3653
+ summary: "added a missing ']'"
3654
+ }));
3655
+ };
3656
+ }
3657
+ const BROKEN = `let items = [1, 2, 3;
3658
+ return items.length;`;
3659
+ (0, import_qunit.test)("auto-fix patches a missing bracket and the run completes", async function(assert) {
3660
+ answerFixes((code) => code.replace("3;", "3];"));
3661
+ let result = await run(BROKEN);
3662
+ assert.strictEqual(result.scriptResult, "3", "the fixed script ran");
3663
+ assert.strictEqual(forwarded.length, 1, "one model call");
3664
+ let [{ url, request }] = forwarded;
3665
+ assert.strictEqual(url, "https://openrouter.ai/api/v1/chat/completions");
3666
+ assert.strictEqual(request.model, "openai/gpt-4.1-mini");
3667
+ let sent = JSON.parse(request.messages[1].content);
3668
+ assert.strictEqual(sent.code, BROKEN, "the model gets the script");
3669
+ assert.strictEqual(sent.error.name, "SyntaxError");
3670
+ assert.strictEqual(result.activity.length, 1);
3671
+ assert.true(/^Fixed a SyntaxError( at line \d+)?: added a missing '\]' \(auto-fix by openai\/gpt-4\.1-mini\)$/.test(result.activity[0]), result.activity[0]);
3672
+ assert.strictEqual(result.scriptFixes.length, 1);
3673
+ assert.true(result.scriptFixes[0].includes("- let items = [1, 2, 3;\n+ let items = [1, 2, 3];"), result.scriptFixes[0]);
3674
+ });
3675
+ (0, import_qunit.test)("auto-fix fixes a ReferenceError thrown before any realm call", async function(assert) {
3676
+ answerFixes((code) => code.replace("itmes", "items"));
3677
+ let result = await run(`let items = [1, 2];
3678
+ return itmes.length;`);
3679
+ assert.strictEqual(result.scriptResult, "2");
3680
+ assert.strictEqual(forwarded.length, 1);
3681
+ });
3682
+ (0, import_qunit.test)("a script that runs cleanly makes no auto-fix call", async function(assert) {
3683
+ let result = await run(`return 1 + 1;`);
3684
+ assert.strictEqual(result.scriptResult, "2");
3685
+ assert.strictEqual(forwarded.length, 0, "no model call");
3686
+ assert.deepEqual(result.scriptFixes, []);
3687
+ });
3688
+ (0, import_qunit.test)("an error after a realm call is not auto-fixed", async function(assert) {
3689
+ answerFixes((code) => code.replace("notDefined()", "null"));
3690
+ await assert.rejects(run(`await realm.fs.exists('task.json');
3691
+ notDefined();`), /ReferenceError/);
3692
+ assert.strictEqual(forwarded.length, 0, "no model call");
3693
+ });
3694
+ (0, import_qunit.test)("a fix that adds a realm call is rejected", async function(assert) {
3695
+ answerFixes((code) => code.replace("3;", "3];\nawait realm.fs.exists('task.json');"));
3696
+ await assert.rejects(run(BROKEN), /SyntaxError[\s\S]*rejected the fix: the fix added realm\.fs\.exists/);
3697
+ assert.strictEqual(forwarded.length, 1);
3698
+ });
3699
+ (0, import_qunit.test)("a fix that changes the arguments of a realm call is rejected", async function(assert) {
3700
+ answerFixes((code) => code.replace("3;", "3];").replace("delete('draft.json')", "delete('index.json')"));
3701
+ await assert.rejects(run(`let items = [1, 2, 3;
3702
+ await realm.fs.delete('draft.json');`), /rejected the fix: the fix changed a line that uses realm or room/);
3703
+ });
3704
+ (0, import_qunit.test)("a fix that changes too much is rejected", async function(assert) {
3705
+ answerFixes((code) => code.replace("3;", `3];\n${"items.push(0);\n".repeat(8)}`));
3706
+ await assert.rejects(run(BROKEN), /rejected the fix: the fix changed more than 5 lines/);
3707
+ });
3708
+ (0, import_qunit.test)("auto-fix stops after its attempt cap", async function(assert) {
3709
+ answerFixes((code) => code.includes("3;") ? code.replace("3;", "3}") : code.replace("3}", "3;"));
3710
+ await assert.rejects(run(BROKEN), /SyntaxError[\s\S]*auto-fixed the script/);
3711
+ assert.strictEqual(forwarded.length, 2, "two fixes were asked for");
3712
+ });
3713
+ });
3714
+ (0, import_qunit.module)("Integration | tools | run-realm-code | realm.operations", function(hooks) {
3715
+ setupRenderingTest(hooks);
3716
+ setupLocalIndexing(hooks);
3717
+ let mockMatrixUtils = setupMockMatrix(hooks, {
3718
+ loggedInAs: "@testuser:localhost",
3719
+ activeRealms: [testRealmURL],
3720
+ autostart: true
3721
+ });
3722
+ setupRealmCacheTeardown(hooks);
3723
+ let adapter;
3724
+ hooks.beforeEach(async function() {
3725
+ ({adapter} = await setupIntegrationTestRealm({
3726
+ mockMatrixUtils,
3727
+ contents: {
3728
+ "patient.gts": PATIENT_MODULE,
3729
+ "Patient/ana.json": patientFile("Ana", "Cardiology"),
3730
+ "Patient/ben.json": patientFile("Ben", "Cardiology"),
3731
+ "Patient/cleo.json": patientFile("Cleo", "Cardiology"),
3732
+ "Patient/dev.json": patientFile("Dev", "Oncology")
3733
+ }
3734
+ }));
3735
+ await getService("realm").login(testRealmURL);
3736
+ getService("operations");
3737
+ });
3738
+ async function run(code) {
3739
+ return await new RunRealmCodeTool(getService("tool-service").toolContext).execute({
3740
+ realm: testRealmURL,
3741
+ roomId: "!room:example.com",
3742
+ code
3743
+ });
3744
+ }
3745
+ async function attributesOf(path) {
3746
+ let file = await adapter.openFile(path);
3747
+ return JSON.parse(file.content).data.attributes;
3748
+ }
3749
+ (0, import_qunit.test)("realm.operations.invoke runs an operation the type declares", async function(assert) {
3750
+ let result = await run(`
3751
+ return await realm.operations.invoke('Patient/ana', 'transferTo', { ward: 'ICU' });`);
3752
+ let answer = JSON.parse(result.scriptResult);
3753
+ assert.strictEqual(answer.path, "Patient/ana.json");
3754
+ assert.strictEqual(answer.operation, "transferTo");
3755
+ assert.strictEqual(answer.id, `${testRealmURL}Patient/ana`);
3756
+ assert.ok(answer.version, "the answer has the version the card now holds");
3757
+ assert.strictEqual((await attributesOf("Patient/ana.json")).ward, "ICU");
3758
+ });
3759
+ (0, import_qunit.test)("realm.operations.invokeBatchOperation updates many patients in one batch", async function(assert) {
3760
+ let result = await run(`
3761
+ let found = await realm.cards.search({
3762
+ filter: {
3763
+ on: { module: ${JSON.stringify(`${testRealmURL}patient`)}, name: 'Patient' },
3764
+ eq: { ward: 'Cardiology' },
3765
+ },
3766
+ });
3767
+ let answers = await realm.operations.invokeBatchOperation(
3768
+ found.cards.map((patient) => ({
3769
+ path: patient.path,
3770
+ operation: 'recordVitals',
3771
+ payload: { reading: 'HR 72, BP 120/80' },
3772
+ })),
3773
+ );
3774
+ return answers.map((answer) => answer.path).sort();`);
3775
+ assert.deepEqual(JSON.parse(result.scriptResult), [
3776
+ "Patient/ana.json",
3777
+ "Patient/ben.json",
3778
+ "Patient/cleo.json"
3779
+ ]);
3780
+ for (let path of [
3781
+ "Patient/ana.json",
3782
+ "Patient/ben.json",
3783
+ "Patient/cleo.json"
3784
+ ]) assert.strictEqual((await attributesOf(path)).lastVitals, "HR 72, BP 120/80", `${path} has the reading`);
3785
+ assert.strictEqual((await attributesOf("Patient/dev.json")).lastVitals, null, "a patient of another ward is not changed");
3786
+ });
3787
+ (0, import_qunit.test)("a batch with one bad entry changes no card", async function(assert) {
3788
+ let result = await run(`
3789
+ try {
3790
+ await realm.operations.invokeBatchOperation([
3791
+ { path: 'Patient/ana', operation: 'recordVitals', payload: { reading: 'HR 80' } },
3792
+ { path: 'Patient/ben', operation: 'recordVitals', payload: {} },
3793
+ ]);
3794
+ return 'ran';
3795
+ } catch (e) {
3796
+ return e.message;
3797
+ }`);
3798
+ assert.strictEqual(JSON.parse(result.scriptResult), "operation \"recordVitals\" requires a value for params(\"reading\")");
3799
+ assert.strictEqual((await attributesOf("Patient/ana.json")).lastVitals, null);
3800
+ });
3801
+ (0, import_qunit.test)("an update-based operation patches the card, alone and in a batch", async function(assert) {
3802
+ await run(`
3803
+ await realm.operations.invoke('Patient/ana', 'amend', { attributes: { name: 'Ana B' } });
3804
+ await realm.operations.invokeBatchOperation([
3805
+ { path: 'Patient/ben', operation: 'amend', payload: { attributes: { name: 'Ben C' } } },
3806
+ { path: 'Patient/cleo', operation: 'recordVitals', payload: { reading: 'HR 64' } },
3807
+ ]);`);
3808
+ assert.strictEqual((await attributesOf("Patient/ana.json")).name, "Ana B");
3809
+ assert.strictEqual((await attributesOf("Patient/ben.json")).name, "Ben C");
3810
+ assert.strictEqual((await attributesOf("Patient/cleo.json")).lastVitals, "HR 64");
3811
+ });
3812
+ (0, import_qunit.test)("a read-based operation answers the document", async function(assert) {
3813
+ let result = await run(`
3814
+ let answer = await realm.operations.invoke('Patient/ana', 'chart');
3815
+ return { path: answer.path, id: answer.id, name: answer.document.data.attributes.name };`);
3816
+ assert.deepEqual(JSON.parse(result.scriptResult), {
3817
+ path: "Patient/ana.json",
3818
+ name: "Ana"
3819
+ });
3820
+ });
3821
+ (0, import_qunit.test)("an operation the type does not declare is refused with the ones it does", async function(assert) {
3822
+ let result = await run(`
3823
+ try {
3824
+ await realm.operations.invoke('Patient/ana', 'discharge');
3825
+ return 'ran';
3826
+ } catch (e) {
3827
+ return e.message;
3828
+ }`);
3829
+ assert.strictEqual(JSON.parse(result.scriptResult), "\"discharge\" is not an operation Patient declares. It declares: amend, chart, recordVitals, transferTo");
3830
+ });
3831
+ (0, import_qunit.test)("a query the type declares is refused with where to search instead", async function(assert) {
3832
+ let result = await run(`
3833
+ try {
3834
+ await realm.operations.invoke('Patient/ana', 'northWard');
3835
+ return 'ran';
3836
+ } catch (e) {
3837
+ return e.message;
3838
+ }`);
3839
+ let message = JSON.parse(result.scriptResult);
3840
+ assert.true(message.startsWith("\"northWard\" is a query operation"), `the refusal says what the name is: ${message}`);
3841
+ assert.true(message.includes("realm.cards.search"), `and where results are read instead: ${message}`);
3842
+ });
3843
+ });
3844
+ //#endregion