@openchambery/web 1.19.4 → 1.19.5-beta.10

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 (172) hide show
  1. package/dist/assets/{AgentAvatar-CVTgW_rx.js → AgentAvatar-BceI_qBC.js} +1 -1
  2. package/dist/assets/{AssistantView-D73E__f8.js → AssistantView-lSnZ7082.js} +1 -1
  3. package/dist/assets/{ChatContainer-pWolXKsP.js → ChatContainer-DJLqUdGE.js} +39 -39
  4. package/dist/assets/ContextPanel-CfO8NpOL.js +285 -0
  5. package/dist/assets/{DiagramView-BJBf7KGE.js → DiagramView-_LIlKVbO.js} +1 -1
  6. package/dist/assets/{FadeInOnReveal-Cy3sf2M5.js → FadeInOnReveal-Cj9ZC3Ab.js} +1 -1
  7. package/dist/assets/{Icon-BLWVqBee.js → Icon-Du1LX11Q.js} +1 -1
  8. package/dist/assets/MarkdownRendererImpl-DpapNgFo.js +7 -0
  9. package/dist/assets/{MobileDetailNavigation-CUq86QUr.js → MobileDetailNavigation-Ct_3mr5K.js} +1 -1
  10. package/dist/assets/{MobileShareBridge-BMolTtSL.js → MobileShareBridge-7LjBYxHH.js} +2 -2
  11. package/dist/assets/MobileSurface-CuEJc-ya.js +2 -0
  12. package/dist/assets/{MultiRunWindow-Bv3moZF8.js → MultiRunWindow-BEM2Ucj4.js} +1 -1
  13. package/dist/assets/{OnboardingScreen-CTNi2zuA.js → OnboardingScreen-Czusf7rR.js} +1 -1
  14. package/dist/assets/{OverlayScrollbar-GQgJusKL.js → OverlayScrollbar-oOLMqq2s.js} +1 -1
  15. package/dist/assets/SettingsView-DUeyhTBn.js +25 -0
  16. package/dist/assets/{SettingsWindow-BMtwRXID.js → SettingsWindow-Qa8HXF_v.js} +1 -1
  17. package/dist/assets/{TerminalView-DwzG7zBO.js → TerminalView-CBSreprw.js} +1 -1
  18. package/dist/assets/{ToolOutputDialog-iYAB6WiK.js → ToolOutputDialog-CRIU0XUQ.js} +1 -1
  19. package/dist/assets/{angular-html-BXZQHcPZ.js → angular-html-CtgxlIZT.js} +1 -1
  20. package/dist/assets/{angular-ts-D8x7dmpg.js → angular-ts-DrvebXRV.js} +1 -1
  21. package/dist/assets/{apl-ugbjmfzm.js → apl-ct3-l_ov.js} +1 -1
  22. package/dist/assets/appThemeRegistry-CvvscooO.js +2 -0
  23. package/dist/assets/{astro-BrfzL78y.js → astro-9KCX_vNx.js} +1 -1
  24. package/dist/assets/{blade-D4cYRUdW.js → blade-DHzjLhcB.js} +1 -1
  25. package/dist/assets/{button-CeCkEpvn.js → button-p6vbd3r6.js} +1 -1
  26. package/dist/assets/{c-CMzMVpWc.js → c-CEfQ88z4.js} +1 -1
  27. package/dist/assets/{clipboard-BXC1vZwE.js → clipboard-Ca2PSbr_.js} +1 -1
  28. package/dist/assets/{cobol-DthQPUuS.js → cobol-ffQsCDPB.js} +1 -1
  29. package/dist/assets/{coffee-8Pqo9WtM.js → coffee-DtRj345i.js} +1 -1
  30. package/dist/assets/{collapsible-B-aagADh.js → collapsible-Cimoz0kx.js} +1 -1
  31. package/dist/assets/{cpp-D18L7odM.js → cpp-NWnna9VM.js} +1 -1
  32. package/dist/assets/{crystal-9GeLf5Ic.js → crystal-CnnqEH_e.js} +1 -1
  33. package/dist/assets/{css-CCmJvSl6.js → css-BRwHOW3t.js} +1 -1
  34. package/dist/assets/{desktopHosts-BrrYPqKq.js → desktopHosts-DceeVBky.js} +1 -1
  35. package/dist/assets/{desktopRelayRestore-DR60DATT.js → desktopRelayRestore-B0MRdR5y.js} +1 -1
  36. package/dist/assets/{diagram-Cxma_IKI.js → diagram-DX_MPRs3.js} +1 -1
  37. package/dist/assets/{dialog-wraJHZQ2.js → dialog-B2ieeCSR.js} +1 -1
  38. package/dist/assets/{edge-BNU68miL.js → edge-BmL4YNvW.js} +1 -1
  39. package/dist/assets/{elixir-Clm-oycI.js → elixir-m9Mcv-AW.js} +1 -1
  40. package/dist/assets/{elm-DyuJMdF4.js → elm-Dazsuz2c.js} +1 -1
  41. package/dist/assets/{erb-BGQIoPeF.js → erb-CJ1X7LGG.js} +1 -1
  42. package/dist/assets/{es-DQqFAaWT.js → es-Bj9HwM-0.js} +3 -3
  43. package/dist/assets/{fileQueries-BJKLdYos.js → fileQueries-BDld6h3B.js} +1 -1
  44. package/dist/assets/{fr-CNfGRpc1.js → fr-BgEHZGug.js} +3 -3
  45. package/dist/assets/{git-rebase-ZHVbPCu8.js → git-rebase-DHqm8iFM.js} +1 -1
  46. package/dist/assets/{gitApi-C6XQwHcm.js → gitApi-BVB4hH6A.js} +3 -3
  47. package/dist/assets/{glimmer-js-Dx29eTyZ.js → glimmer-js-529w2Uvl.js} +1 -1
  48. package/dist/assets/{glimmer-ts-7AFohuRx.js → glimmer-ts-B38MdnnB.js} +1 -1
  49. package/dist/assets/{glsl-Nbih-1pG.js → glsl-IFfKD25X.js} +1 -1
  50. package/dist/assets/{graphql-IOG_4fSc.js → graphql-m-L9qWWB.js} +1 -1
  51. package/dist/assets/{hack-DyZU34CD.js → hack-S2S7pq0v.js} +1 -1
  52. package/dist/assets/{haml-mwEldSxy.js → haml-BE8HBwbH.js} +1 -1
  53. package/dist/assets/{handlebars-BRbCg-TR.js → handlebars-DPMWj4Da.js} +1 -1
  54. package/dist/assets/{html-derivative-DpusPW9p.js → html-derivative-D0yehinc.js} +1 -1
  55. package/dist/assets/{html-DH50wxss.js → html-gfIVmLgx.js} +1 -1
  56. package/dist/assets/{http-fM2Ft62a.js → http-Yj1uQSnx.js} +1 -1
  57. package/dist/assets/{hurl-C7ZRA11y.js → hurl-CiAC6gLf.js} +1 -1
  58. package/dist/assets/{input-CyG05w-V.js → input-gaFVNq1P.js} +1 -1
  59. package/dist/assets/insertionBoundaries-Cpj9r_o6.js +4 -0
  60. package/dist/assets/{ja-CQqehnVn.js → ja-clyxf6re.js} +3 -3
  61. package/dist/assets/{java-CuLv1JzP.js → java-lPyr66Qw.js} +1 -1
  62. package/dist/assets/{javascript-e9R9mij1.js → javascript-BlmKDQ-I.js} +1 -1
  63. package/dist/assets/{jinja-BqL4dpCe.js → jinja-DrvjNn4y.js} +1 -1
  64. package/dist/assets/{jison-BU1E246U.js → jison-BZ-k3PlS.js} +1 -1
  65. package/dist/assets/{json-CWUAz4dE.js → json-CaQi8cq5.js} +1 -1
  66. package/dist/assets/{jsx-Tb9QFw4P.js → jsx-kfzB3oTi.js} +1 -1
  67. package/dist/assets/{julia-C6q7nYBJ.js → julia-Bup0jpIM.js} +1 -1
  68. package/dist/assets/{just-CLHVCPQG.js → just-XMsX5iKL.js} +1 -1
  69. package/dist/assets/{ko-kmwi8UJD.js → ko-BZb3gnuq.js} +3 -3
  70. package/dist/assets/{latex-DzR96v9w.js → latex-CHhOHp9u.js} +1 -1
  71. package/dist/assets/{liquid-UjsTutWK.js → liquid-C90FVKxw.js} +1 -1
  72. package/dist/assets/{lua-laWCoC7H.js → lua-BXPwTBAD.js} +1 -1
  73. package/dist/assets/main-BsdsPsHT.js +29 -0
  74. package/dist/assets/{main-OM6EVqFo.js → main-CQLsl_QE.js} +2 -2
  75. package/dist/assets/markdown-shiki.worker-woF2hOAN.js +349 -0
  76. package/dist/assets/{markdown-worker-DqO-GnVj.js → markdown-worker-CoO82KYF.js} +4 -4
  77. package/dist/assets/{markdownTheme-H7_7lbuh.js → markdownTheme-St8zxDvJ.js} +1 -1
  78. package/dist/assets/{marko-Cw81uHQK.js → marko-oFkW-Ef7.js} +1 -1
  79. package/dist/assets/{mdc-5VRIrvpo.js → mdc-Dt17Fv3G.js} +1 -1
  80. package/dist/assets/{miniChat-CWJejsNH.js → miniChat-w-S3l9bR.js} +2 -2
  81. package/dist/assets/mobile-CHDToW-w.js +2 -0
  82. package/dist/assets/multirun-CMEZHI5g.js +4 -0
  83. package/dist/assets/{nginx-OjbRtDTh.js → nginx-LfNI-F2k.js} +1 -1
  84. package/dist/assets/{nim-BqzUQFfL.js → nim-so0j2_Ky.js} +1 -1
  85. package/dist/assets/{perl-Bba2cEgf.js → perl-DQcGQU9O.js} +1 -1
  86. package/dist/assets/{persistence-DE9RnVcA.js → persistence-CLUawIlm.js} +1 -1
  87. package/dist/assets/{php-CkVgF6HF.js → php-Dae1IcCM.js} +1 -1
  88. package/dist/assets/{pl-Blv5o2QA.js → pl-CwZzSvrm.js} +3 -3
  89. package/dist/assets/{platform-MjT8iqKB.js → platform-BrncW9ab.js} +1 -1
  90. package/dist/assets/{projectMeta-DCJQ6I3m.js → projectMeta-ZD-iXSOT.js} +2 -2
  91. package/dist/assets/{pt-BR-BK74t-Bd.js → pt-BR-6ali2BQX.js} +3 -3
  92. package/dist/assets/{pug-Dj44SzNY.js → pug-CaHd5xEa.js} +1 -1
  93. package/dist/assets/{qml-DETHcUPx.js → qml-lLqImktD.js} +1 -1
  94. package/dist/assets/{r-CrbPkwLJ.js → r-C2VuzPsf.js} +1 -1
  95. package/dist/assets/{radio-MAnQ7sDW.js → radio-B-nm8u03.js} +1 -1
  96. package/dist/assets/{razor-56IyEPA5.js → razor-DTpzsAil.js} +1 -1
  97. package/dist/assets/{regexp-BEMDgR1N.js → regexp-DJeMtopQ.js} +1 -1
  98. package/dist/assets/renderElectronMiniChatApp-65t0A6BD.js +2 -0
  99. package/dist/assets/renderMobileApp-a3Joz-yn.js +25 -0
  100. package/dist/assets/{rst-C8BEzC8U.js → rst-c6kvN9mK.js} +1 -1
  101. package/dist/assets/{ruby-DWyeL44t.js → ruby-B68kNTjT.js} +1 -1
  102. package/dist/assets/{runtimeConfig-C2Vygqku.js → runtimeConfig-BGuiq1aJ.js} +1 -1
  103. package/dist/assets/runtimeEndpointReset-HC9CW_hT.js +4 -0
  104. package/dist/assets/{sas-Dt-MhHv8.js → sas-Br3BRi6f.js} +1 -1
  105. package/dist/assets/{scss-jmTM-KWO.js → scss-ugvUHawi.js} +1 -1
  106. package/dist/assets/{shellscript-BgI_n_Bv.js → shellscript-Ci5rJl0t.js} +1 -1
  107. package/dist/assets/{shellsession-DhmUadx_.js → shellsession-Dxn1WKgk.js} +1 -1
  108. package/dist/assets/{shikiHighlight-Bpy8sf5r.js → shikiHighlight-DFiqYozo.js} +1 -1
  109. package/dist/assets/{sortable-tabs-strip-BdbKDDAP.js → sortable-tabs-strip-CxLE1X9x.js} +1 -1
  110. package/dist/assets/{soy-qfQbh7dQ.js → soy-kgnQSRVZ.js} +1 -1
  111. package/dist/assets/{sql-CwyNz0Gh.js → sql-Cx3XaVwp.js} +1 -1
  112. package/dist/assets/src-D-LxNwlu.css +2 -0
  113. package/dist/assets/{stata-D9MlkA-a.js → stata-B5YhL3cp.js} +1 -1
  114. package/dist/assets/{store-UweBeN2y.js → store-BvrCRq6U.js} +3 -3
  115. package/dist/assets/{surrealql-xR93a923.js → surrealql-B8ysTfdK.js} +1 -1
  116. package/dist/assets/{svelte-BA7ahdky.js → svelte-D5-K3GME.js} +1 -1
  117. package/dist/assets/{templ-CxpzFYl8.js → templ-n2DDBrat.js} +1 -1
  118. package/dist/assets/{terminalApi-Bnx0KvaV.js → terminalApi-D2OzR0I3.js} +1 -1
  119. package/dist/assets/{tex-CePKgOWg.js → tex-CpNhXIXy.js} +1 -1
  120. package/dist/assets/{tooltip-B__x-HDJ.js → tooltip-D1a6rKLA.js} +1 -1
  121. package/dist/assets/{ts-tags-DP2fsj53.js → ts-tags-CrtENrUj.js} +1 -1
  122. package/dist/assets/{tsx-CI52QFY8.js → tsx-DEnzboO-.js} +1 -1
  123. package/dist/assets/{twig-BjoebDgo.js → twig-B7PeheP0.js} +1 -1
  124. package/dist/assets/{typescript-cpHrN9Ya.js → typescript-CUlPIybL.js} +1 -1
  125. package/dist/assets/{uk-6I-KXy3R.js → uk-qRqJs4GS.js} +3 -3
  126. package/dist/assets/{url-CTuw7Rjf.js → url-zwuNq3GS.js} +1 -1
  127. package/dist/assets/{useAppFontEffects-BVVxQm3q.js → useAppFontEffects-pF6rKdKM.js} +4 -4
  128. package/dist/assets/{useAssistantUIStore-D2K7ksGj.js → useAssistantUIStore-D699Sbdb.js} +1 -1
  129. package/dist/assets/{useDesktopWindowControlsLayout-BR2bI_26.js → useDesktopWindowControlsLayout-DH7Om-wY.js} +1 -1
  130. package/dist/assets/{useEffectiveDirectory-DdS6rQm9.js → useEffectiveDirectory-BvZFK9wU.js} +1 -1
  131. package/dist/assets/{useFontPreferences-CWZnNRjz.js → useFontPreferences-F2QPNE2V.js} +1 -1
  132. package/dist/assets/{useMobileNavigationStore-DFSb2gzX.js → useMobileNavigationStore-CGOeJb9D.js} +10 -10
  133. package/dist/assets/{useSessionAutoCleanup-BNC1lVkC.js → useSessionAutoCleanup-lBn8oZ8f.js} +1 -1
  134. package/dist/assets/{useUIStore-DOxLVeGb.js → useUIStore-BXjMQrel.js} +1 -1
  135. package/dist/assets/{useWorkerHighlightedLines-B9mnTmvs.js → useWorkerHighlightedLines-Bc1NJlYO.js} +2 -2
  136. package/dist/assets/{utils-C2R0YoC5.js → utils-D94hxQYq.js} +1 -1
  137. package/dist/assets/{vue-DK5KPkZ5.js → vue-C6ZZy2AI.js} +1 -1
  138. package/dist/assets/{vue-html-BlUa8woS.js → vue-html-DsvGlIY2.js} +1 -1
  139. package/dist/assets/{vue-vine-D3hlJyLi.js → vue-vine-C8VLalyn.js} +1 -1
  140. package/dist/assets/{xml-CspvrcYx.js → xml-ChNx2zKg.js} +1 -1
  141. package/dist/assets/{xsl-D10hdnv5.js → xsl-yWKkpZli.js} +1 -1
  142. package/dist/assets/{yaml-C00-kDDU.js → yaml-CENNGtdf.js} +1 -1
  143. package/dist/assets/{zh-CN-DwlzS4yK.js → zh-CN-CwZ0adcY.js} +3 -3
  144. package/dist/assets/{zh-TW-p0VriyfT.js → zh-TW-DC0jiCzA.js} +3 -3
  145. package/dist/index.html +9 -9
  146. package/dist/mini-chat.html +9 -9
  147. package/dist/mobile.html +9 -9
  148. package/package.json +1 -1
  149. package/server/index.js +40 -3
  150. package/server/lib/notifications/DOCUMENTATION.md +2 -2
  151. package/server/lib/notifications/push-runtime.js +4 -14
  152. package/server/lib/notifications/runtime.js +11 -20
  153. package/server/lib/notifications/runtime.test.js +4 -6
  154. package/server/lib/opencode/core-routes.js +56 -23
  155. package/server/lib/opencode/core-routes.test.js +206 -6
  156. package/server/lib/relay/DOCUMENTATION.md +2 -1
  157. package/server/lib/relay/service.js +223 -71
  158. package/server/lib/relay/service.test.js +96 -13
  159. package/dist/assets/ContextPanel-GBq5lSES.js +0 -285
  160. package/dist/assets/MarkdownRendererImpl-BUXY9iuu.js +0 -7
  161. package/dist/assets/MobileSurface-DDRPrlAq.js +0 -2
  162. package/dist/assets/SettingsView-Ip_jVXoD.js +0 -25
  163. package/dist/assets/appThemeRegistry-rHYlrhb5.js +0 -2
  164. package/dist/assets/insertionBoundaries-DG_1_ENK.js +0 -4
  165. package/dist/assets/main-MkZ3xcbE.js +0 -29
  166. package/dist/assets/markdown-shiki.worker-CKKjvdhc.js +0 -12
  167. package/dist/assets/mobile-D-ubuubW.js +0 -2
  168. package/dist/assets/multirun-YB7NFEGX.js +0 -4
  169. package/dist/assets/renderElectronMiniChatApp-BPZ5LfbX.js +0 -2
  170. package/dist/assets/renderMobileApp-pqjbEzlN.js +0 -23
  171. package/dist/assets/runtimeEndpointReset-CuW2UDPM.js +0 -4
  172. package/dist/assets/src-eGuNWxHc.css +0 -2
package/dist/index.html CHANGED
@@ -552,25 +552,25 @@
552
552
  opacity: 0.72;
553
553
  }
554
554
  </style>
555
- <script type="module" crossorigin src="/assets/main-OM6EVqFo.js"></script>
555
+ <script type="module" crossorigin src="/assets/main-CQLsl_QE.js"></script>
556
556
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
557
557
  <link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
558
558
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
559
- <link rel="modulepreload" crossorigin href="/assets/useUIStore-DOxLVeGb.js">
559
+ <link rel="modulepreload" crossorigin href="/assets/useUIStore-BXjMQrel.js">
560
560
  <link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
561
561
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
562
562
  <link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
563
- <link rel="modulepreload" crossorigin href="/assets/persistence-DE9RnVcA.js">
564
- <link rel="modulepreload" crossorigin href="/assets/desktopHosts-BrrYPqKq.js">
565
- <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-DR60DATT.js">
563
+ <link rel="modulepreload" crossorigin href="/assets/persistence-CLUawIlm.js">
564
+ <link rel="modulepreload" crossorigin href="/assets/desktopHosts-DceeVBky.js">
565
+ <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-B0MRdR5y.js">
566
566
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-C07HQypa.js">
567
567
  <link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
568
- <link rel="modulepreload" crossorigin href="/assets/platform-MjT8iqKB.js">
569
- <link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
568
+ <link rel="modulepreload" crossorigin href="/assets/platform-BrncW9ab.js">
569
+ <link rel="modulepreload" crossorigin href="/assets/terminalApi-D2OzR0I3.js">
570
570
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
571
571
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
572
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-C2Vygqku.js">
573
- <link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
572
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-BGuiq1aJ.js">
573
+ <link rel="stylesheet" crossorigin href="/assets/src-D-LxNwlu.css">
574
574
  </head>
575
575
  <body class="h-full bg-background text-foreground">
576
576
  <!-- Keep the startup layer outside #root. React replaces every child of
@@ -74,25 +74,25 @@
74
74
  }
75
75
  </style>
76
76
 
77
- <script type="module" crossorigin src="/assets/miniChat-CWJejsNH.js"></script>
77
+ <script type="module" crossorigin src="/assets/miniChat-w-S3l9bR.js"></script>
78
78
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
79
79
  <link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
80
80
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
81
- <link rel="modulepreload" crossorigin href="/assets/useUIStore-DOxLVeGb.js">
81
+ <link rel="modulepreload" crossorigin href="/assets/useUIStore-BXjMQrel.js">
82
82
  <link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
83
83
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
84
84
  <link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
85
- <link rel="modulepreload" crossorigin href="/assets/persistence-DE9RnVcA.js">
86
- <link rel="modulepreload" crossorigin href="/assets/desktopHosts-BrrYPqKq.js">
87
- <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-DR60DATT.js">
85
+ <link rel="modulepreload" crossorigin href="/assets/persistence-CLUawIlm.js">
86
+ <link rel="modulepreload" crossorigin href="/assets/desktopHosts-DceeVBky.js">
87
+ <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-B0MRdR5y.js">
88
88
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-C07HQypa.js">
89
89
  <link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
90
- <link rel="modulepreload" crossorigin href="/assets/platform-MjT8iqKB.js">
91
- <link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
90
+ <link rel="modulepreload" crossorigin href="/assets/platform-BrncW9ab.js">
91
+ <link rel="modulepreload" crossorigin href="/assets/terminalApi-D2OzR0I3.js">
92
92
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
93
93
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
94
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-C2Vygqku.js">
95
- <link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
94
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-BGuiq1aJ.js">
95
+ <link rel="stylesheet" crossorigin href="/assets/src-D-LxNwlu.css">
96
96
  </head>
97
97
  <body class="h-full bg-background text-foreground">
98
98
  <div id="root" class="h-full">
package/dist/mobile.html CHANGED
@@ -63,25 +63,25 @@
63
63
  })();
64
64
  </script>
65
65
 
66
- <script type="module" crossorigin src="/assets/mobile-D-ubuubW.js"></script>
66
+ <script type="module" crossorigin src="/assets/mobile-CHDToW-w.js"></script>
67
67
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
68
68
  <link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
69
69
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
70
- <link rel="modulepreload" crossorigin href="/assets/useUIStore-DOxLVeGb.js">
70
+ <link rel="modulepreload" crossorigin href="/assets/useUIStore-BXjMQrel.js">
71
71
  <link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
72
72
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
73
73
  <link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
74
- <link rel="modulepreload" crossorigin href="/assets/persistence-DE9RnVcA.js">
75
- <link rel="modulepreload" crossorigin href="/assets/desktopHosts-BrrYPqKq.js">
76
- <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-DR60DATT.js">
74
+ <link rel="modulepreload" crossorigin href="/assets/persistence-CLUawIlm.js">
75
+ <link rel="modulepreload" crossorigin href="/assets/desktopHosts-DceeVBky.js">
76
+ <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-B0MRdR5y.js">
77
77
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-C07HQypa.js">
78
78
  <link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
79
- <link rel="modulepreload" crossorigin href="/assets/platform-MjT8iqKB.js">
80
- <link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
79
+ <link rel="modulepreload" crossorigin href="/assets/platform-BrncW9ab.js">
80
+ <link rel="modulepreload" crossorigin href="/assets/terminalApi-D2OzR0I3.js">
81
81
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
82
82
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
83
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-C2Vygqku.js">
84
- <link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
83
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-BGuiq1aJ.js">
84
+ <link rel="stylesheet" crossorigin href="/assets/src-D-LxNwlu.css">
85
85
  </head>
86
86
  <body class="h-full bg-background text-foreground">
87
87
  <div id="root" class="h-full"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openchambery/web",
3
- "version": "1.19.4",
3
+ "version": "1.19.5-beta.10",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./server/index.js",
package/server/index.js CHANGED
@@ -342,8 +342,6 @@ const updateUiVisibility = (token, visible, platform) => {
342
342
  if (visible === true) clearPendingPushBadge();
343
343
  return pushRuntime.updateUiVisibility(token, visible, platform);
344
344
  };
345
- const isAnyUiVisible = (...args) => pushRuntime.isAnyUiVisible(...args);
346
- const isAnyInteractiveClientVisible = (...args) => pushRuntime.isAnyInteractiveClientVisible(...args);
347
345
  const isUiVisible = (...args) => pushRuntime.isUiVisible(...args);
348
346
  const ensurePushInitialized = (...args) => pushRuntime.ensurePushInitialized(...args);
349
347
  const setPushInitialized = (...args) => pushRuntime.setPushInitialized(...args);
@@ -664,7 +662,6 @@ const notificationTriggerRuntime = createNotificationTriggerRuntime({
664
662
  sendPushToAllUiSessions,
665
663
  sendApnsToAllUiSessions,
666
664
  sendLiveActivityEnd,
667
- isAnyInteractiveClientVisible,
668
665
  buildOpenCodeUrl,
669
666
  getOpenCodeAuthHeaders,
670
667
  });
@@ -1419,6 +1416,29 @@ async function main(options = {}) {
1419
1416
  // Re-evaluate the relay lifecycle after pairing/device changes (a revoked or
1420
1417
  // redeemed device can flip relay demand on or off).
1421
1418
  reconcileRelay: () => (relayServiceInstance ? relayServiceInstance.reconcile() : Promise.resolve()),
1419
+ // Canonical endpoints the host relay currently uses (primary first).
1420
+ // Pairing-session requests from paired desktop clients may carry a relayUrl
1421
+ // only when it matches one of these (an echo of an advertised endpoint,
1422
+ // never a re-point or an extension).
1423
+ getEffectiveRelayUrls: async () => {
1424
+ if (!relayServiceInstance) return [];
1425
+ try {
1426
+ const status = await relayServiceInstance.getStatus();
1427
+ return Array.isArray(status?.relayUrls) ? status.relayUrls : [];
1428
+ } catch {
1429
+ return [];
1430
+ }
1431
+ },
1432
+ // Every configured relay endpoint as a pairing candidate (multi-relay),
1433
+ // for the connection-candidates refresh.
1434
+ getRelayPairingCandidates: async () => {
1435
+ if (!relayServiceInstance) return [];
1436
+ try {
1437
+ return await relayServiceInstance.getPairingCandidates();
1438
+ } catch {
1439
+ return [];
1440
+ }
1441
+ },
1422
1442
  getPairingTransports: async (req) => {
1423
1443
  const transports = resolvePairingTransports(req);
1424
1444
  if (!relayServiceInstance || !transports.relayAvailable) return transports;
@@ -1426,6 +1446,7 @@ async function main(options = {}) {
1426
1446
  return {
1427
1447
  ...transports,
1428
1448
  relayUrl: relay.relayUrl,
1449
+ relayUrls: relay.relayUrls,
1429
1450
  relayUrlLocked: relay.relayUrlLocked,
1430
1451
  };
1431
1452
  },
@@ -1516,6 +1537,22 @@ async function main(options = {}) {
1516
1537
  onRelayUrlChanged: async () => {
1517
1538
  await apnsRuntime.reRegisterAllTokens();
1518
1539
  },
1540
+ // Owner gate for relay-endpoint management: UI session or the local desktop
1541
+ // shell. Paired remote clients (desktops included) are NOT endpoint owners
1542
+ // — adding/removing endpoints is the same trust boundary as re-pointing
1543
+ // the primary endpoint.
1544
+ isOwnerRequest: async (req, res) => {
1545
+ if (typeof uiAuthController?.resolveAuthContext !== 'function') return false;
1546
+ try {
1547
+ const context = await uiAuthController.resolveAuthContext(req, res, { allowClientAuth: true, allowUrlToken: false });
1548
+ if (context?.type === 'session') return true;
1549
+ if (context?.type === 'client') {
1550
+ return context.client?.clientKind === 'desktop-local';
1551
+ }
1552
+ } catch {
1553
+ }
1554
+ return false;
1555
+ },
1519
1556
  });
1520
1557
  relayServiceInstance = relayService;
1521
1558
  relayService.registerRoutes(app);
@@ -52,7 +52,7 @@ This module provides notification message preparation utilities for the web serv
52
52
  - session meta cache for child-session and small-model suppression
53
53
  - template resolution and fallback behavior
54
54
  - native notification fanout and web push payload fanout
55
- - push suppression while any fresh UI visibility heartbeat reports a focused client
55
+ - push always fans out to every subscribed surface; another client's visibility never suppresses delivery
56
56
 
57
57
  ### Push runtime API (push-runtime.js)
58
58
  - `createPushRuntime(dependencies)`: creates runtime for web push and UI visibility state.
@@ -62,7 +62,7 @@ This module provides notification message preparation utilities for the web serv
62
62
  - `setPushInitialized(value)`
63
63
  - `addOrUpdatePushSubscription(uiSessionToken, subscription, userAgent)`
64
64
  - `removePushSubscription(uiSessionToken, endpoint)`
65
- - `sendPushToAllUiSessions(payload, options?)`
65
+ - `sendPushToAllUiSessions(payload)`
66
66
  - `updateUiVisibility(token, visible)`
67
67
  - `isAnyUiVisible()`
68
68
  - `isUiVisible(token)`
@@ -221,8 +221,7 @@ export const createPushRuntime = (deps) => {
221
221
  }
222
222
  };
223
223
 
224
- const sendPushToAllUiSessions = async (payload, options = {}) => {
225
- const requireNoSse = options.requireNoSse === true;
224
+ const sendPushToAllUiSessions = async (payload) => {
226
225
  const store = await readPushSubscriptionsFromDisk();
227
226
  const sessions = store.subscriptionsBySession || {};
228
227
  const subscriptionsByEndpoint = new Map();
@@ -239,14 +238,6 @@ export const createPushRuntime = (deps) => {
239
238
  }
240
239
 
241
240
  await Promise.all(Array.from(subscriptionsByEndpoint.values()).map(async (sub) => {
242
- if (requireNoSse) {
243
- // Mobile PWA subscriptions follow the same presence model as native push: suppress only
244
- // when an interactive (desktop/web) client is visible. The phone PWA's own foreground is
245
- // handled in the service worker (focused-client check), so it won't double-notify.
246
- // Non-mobile (desktop/web) subscriptions keep the existing any-visible gate.
247
- const suppressed = isMobilePlatform(sub.platform) ? isAnyInteractiveClientVisible() : isAnyUiVisible();
248
- if (suppressed) return;
249
- }
250
241
  await sendPushToSubscription(sub, payload);
251
242
  }));
252
243
  };
@@ -278,10 +269,9 @@ export const createPushRuntime = (deps) => {
278
269
  return false;
279
270
  };
280
271
 
281
- // True when at least one NON-mobile client (desktop/web/vscode) is currently visible. Used to
282
- // suppress native push to the phone: an active desktop already shows the notification, so the
283
- // phone doesn't need it. Deliberately based on the desktop's visibility (reliable), never the
284
- // phone's own (a backgrounded WKWebView can't report "hidden" before iOS suspends it).
272
+ // True when at least one NON-mobile client (desktop/web/vscode) is currently visible.
273
+ // Not a push gate delivery always fans out. Kept as a visibility query for tests and
274
+ // diagnostics. Deliberately based on the desktop's visibility, never the phone's own.
285
275
  const isAnyInteractiveClientVisible = () => {
286
276
  const now = Date.now();
287
277
  pruneUiVisibility(now);
@@ -12,7 +12,6 @@ export const createNotificationTriggerRuntime = (deps) => {
12
12
  sendPushToAllUiSessions,
13
13
  sendApnsToAllUiSessions,
14
14
  sendLiveActivityEnd,
15
- isAnyInteractiveClientVisible,
16
15
  buildOpenCodeUrl,
17
16
  getOpenCodeAuthHeaders,
18
17
  } = deps;
@@ -68,25 +67,17 @@ export const createNotificationTriggerRuntime = (deps) => {
68
67
  };
69
68
 
70
69
  // Fan a notification out to every delivery channel: browser web-push (full templated
71
- // payload) and native iOS APNs (generic model-based text). Both share the dedup tag and
72
- // `requireNoSse` focus gate; a failure in one channel must not block the other.
73
- const fanoutPush = (payload, options) => {
74
- // Presence-aware routing: if any interactive (non-mobile) client — desktop/web/vscode — is
75
- // currently visible, it already shows the in-app notification, so skip the native push to the
76
- // phone. Gated on the desktop's visibility (reliable), never the phone's own. When we skip we
77
- // also skip toApnsGenericPayload, so the badge isn't incremented for an undelivered push.
78
- const interactiveVisible = isAnyInteractiveClientVisible?.() === true;
79
- return Promise.all([
80
- Promise.resolve(sendPushToAllUiSessions?.(payload, options)).catch((error) => {
81
- console.warn('[Push] web-push fanout failed:', error?.message ?? error);
82
- }),
83
- interactiveVisible
84
- ? Promise.resolve()
85
- : Promise.resolve(sendApnsToAllUiSessions?.(toApnsGenericPayload(payload), options)).catch((error) => {
86
- console.warn('[APNs] fanout failed:', error?.message ?? error);
87
- }),
88
- ]);
89
- };
70
+ // payload) and native iOS APNs (generic model-based text). Both share the dedup tag;
71
+ // a failure in one channel must not block the other. Visibility on any other client
72
+ // never suppresses delivery every subscribed surface gets the push.
73
+ const fanoutPush = (payload, options) => Promise.all([
74
+ Promise.resolve(sendPushToAllUiSessions?.(payload, options)).catch((error) => {
75
+ console.warn('[Push] web-push fanout failed:', error?.message ?? error);
76
+ }),
77
+ Promise.resolve(sendApnsToAllUiSessions?.(toApnsGenericPayload(payload), options)).catch((error) => {
78
+ console.warn('[APNs] fanout failed:', error?.message ?? error);
79
+ }),
80
+ ]);
90
81
 
91
82
  let getIsWindowFocused = typeof deps.getIsWindowFocused === 'function'
92
83
  ? deps.getIsWindowFocused
@@ -28,7 +28,6 @@ const createRuntime = (overrides = {}) => {
28
28
  sendPushToAllUiSessions,
29
29
  sendApnsToAllUiSessions,
30
30
  sendLiveActivityEnd,
31
- isAnyInteractiveClientVisible: () => false,
32
31
  buildOpenCodeUrl: (path) => `http://opencode.test${path}`,
33
32
  getOpenCodeAuthHeaders: () => ({}),
34
33
  getIsWindowFocused: () => false,
@@ -224,14 +223,13 @@ describe('notification trigger live activity end', () => {
224
223
  expect(sendApnsToAllUiSessions).not.toHaveBeenCalled();
225
224
  });
226
225
 
227
- it('ends the live activity even when an interactive client is visible', async () => {
226
+ it('still sends native push when another client is visible', async () => {
228
227
  vi.stubGlobal('fetch', vi.fn(async () => rootSessionResponse()));
229
- const { runtime, sendLiveActivityEnd, sendApnsToAllUiSessions } = createRuntime({
230
- isAnyInteractiveClientVisible: () => true,
231
- });
228
+ const { runtime, sendLiveActivityEnd, sendApnsToAllUiSessions, sendPushToAllUiSessions } = createRuntime();
232
229
  await runtime.maybeSendPushForTrigger(completionPayload());
233
230
  expect(sendLiveActivityEnd).toHaveBeenCalledWith({ sessionId: 'ses_root', status: 'complete' });
234
- expect(sendApnsToAllUiSessions).not.toHaveBeenCalled();
231
+ expect(sendPushToAllUiSessions).toHaveBeenCalledTimes(1);
232
+ expect(sendApnsToAllUiSessions).toHaveBeenCalledTimes(1);
235
233
  });
236
234
 
237
235
  it('suppresses live activity end for child and small-model sessions', async () => {
@@ -390,6 +390,16 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
390
390
  // hostEncPubJwk, priority }) when the host relay is enabled, else null.
391
391
  // Injected lazily because the relay service is constructed after these routes.
392
392
  getRelayPairingCandidate = async () => null,
393
+ // All configured relay endpoints (canonical ws(s)://…), primary first, or
394
+ // [] when relay status is unavailable. Gates remote-bearer pairing requests
395
+ // that carry a relayUrl: paired desktops may only use an endpoint the host
396
+ // already has (echo of any configured endpoint, never a re-point).
397
+ getEffectiveRelayUrls = async () => [],
398
+ // Candidates for EVERY configured relay endpoint (multi-relay), or null /
399
+ // a non-array when only the single-endpoint accessor is available. Feeds
400
+ // the connection-candidates refresh so paired devices learn the full
401
+ // reachable endpoint set.
402
+ getRelayPairingCandidates = async () => null,
393
403
  // Re-evaluate the relay lifecycle after pairing/device changes.
394
404
  reconcileRelay = async () => {},
395
405
  // Returns { local, lan, relayAvailable } — the direct transport URLs the
@@ -424,6 +434,15 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
424
434
  }
425
435
  };
426
436
 
437
+ // Which paired clients may act as operators of this server's device fleet:
438
+ // create pairing links, list every device, and revoke other devices. The
439
+ // desktop shell's local client is the in-process operator; a paired `desktop`
440
+ // client is granted the same trust at pairing time (parity with the SSH
441
+ // attach flow, where the connecting desktop authenticates as the operator).
442
+ // Mobile (and any other) paired clients stay scoped to their own record.
443
+ const canManageRemoteClients = (client) =>
444
+ client?.clientKind === 'desktop-local' || client?.clientKind === 'desktop';
445
+
427
446
  const runWithClientManagementAuth = async (req, res, next, handler) => {
428
447
  try {
429
448
  if (typeof uiAuthController.resolveAuthContext === 'function') {
@@ -458,7 +477,7 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
458
477
  }
459
478
  if (context?.type === 'client') {
460
479
  const client = await clientRecordFromAuthContext(context);
461
- if (client?.clientKind === 'desktop-local') {
480
+ if (canManageRemoteClients(client)) {
462
481
  await handler({ ...context, client });
463
482
  return;
464
483
  }
@@ -779,10 +798,10 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
779
798
  await runWithClientManagementAuth(req, res, next, async (authContext) => {
780
799
  if (authContext.type === 'client') {
781
800
  const client = await clientRecordFromAuthContext(authContext);
782
- // The desktop shell's local client is the trusted operator of this
783
- // server; it manages devices just like a browser UI session. Every
784
- // other client token is scoped to its own record.
785
- if (client?.clientKind !== 'desktop-local') {
801
+ // Operator clients (local desktop shell, paired desktops) manage the
802
+ // whole device fleet; every other client token is scoped to its own
803
+ // record.
804
+ if (!canManageRemoteClients(client)) {
786
805
  return res.json({ clients: client ? [client] : [] });
787
806
  }
788
807
  }
@@ -807,9 +826,9 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
807
826
  await runWithClientManagementAuth(req, res, next, async (authContext) => {
808
827
  if (authContext.type === 'client') {
809
828
  const actingClient = await clientRecordFromAuthContext(authContext);
810
- // The desktop shell's local client manages every device; other client
811
- // tokens may only revoke themselves.
812
- if (actingClient?.clientKind !== 'desktop-local') {
829
+ // Operator clients (local desktop shell, paired desktops) may revoke
830
+ // any device; other client tokens may only revoke themselves.
831
+ if (!canManageRemoteClients(actingClient)) {
813
832
  const clientId = clientIdFromAuthContext(authContext);
814
833
  if (!clientId || clientId !== req.params?.id) {
815
834
  return res.status(403).json({ revoked: false, error: 'Client tokens can only revoke themselves' });
@@ -829,9 +848,10 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
829
848
  await runWithClientManagementAuth(req, res, next, async (authContext) => {
830
849
  if (authContext.type === 'client') {
831
850
  const actingClient = await clientRecordFromAuthContext(authContext);
832
- // Purging revoked devices is a whole-server management action; only the
833
- // trusted desktop shell client (or a UI session) may do it.
834
- if (actingClient?.clientKind !== 'desktop-local') {
851
+ // Purging revoked devices is a whole-server management action; only
852
+ // operator clients (local desktop shell, paired desktops) or a UI
853
+ // session may do it.
854
+ if (!canManageRemoteClients(actingClient)) {
835
855
  return res.status(403).json({ purged: 0, error: 'Client tokens cannot purge revoked devices' });
836
856
  }
837
857
  }
@@ -843,22 +863,25 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
843
863
 
844
864
  app.post('/api/client-auth/pairing/sessions', express.json({ limit: '64kb' }), async (req, res, next) => {
845
865
  await runWithClientCreateAuth(req, res, next, async (authContext) => {
846
- // Changing the Host Relay endpoint is an owner-UI or local desktop-shell
847
- // action. Paired remote clients never reach this handler (create-auth only
848
- // admits session + desktop-local); keep the kind check so a future auth
849
- // widening cannot re-point the Host relay from a remote bearer.
850
- if (req.body?.relayUrl !== undefined && authContext?.type === 'client') {
851
- const actingClient = authContext.client || await clientRecordFromAuthContext(authContext);
852
- if (actingClient?.clientKind !== 'desktop-local') {
853
- return res.status(403).json({ error: 'Only the owner UI session can set a custom Relay endpoint' });
854
- }
855
- }
856
866
  const requestedRelayUrl = req.body?.relayUrl === undefined
857
867
  ? undefined
858
868
  : normalizeRequestedRelayUrl(req.body.relayUrl);
859
869
  if (req.body?.relayUrl !== undefined && !requestedRelayUrl) {
860
870
  return res.status(400).json({ error: 'Relay URL must use ws:// or wss://' });
861
871
  }
872
+ // Changing the Host Relay endpoint set is an owner-UI or local
873
+ // desktop-shell action. Paired desktop operators reach this handler too,
874
+ // but may only name an endpoint the host already uses — anything else
875
+ // would re-point (or extend) the Host relay from a remote bearer.
876
+ if (requestedRelayUrl !== undefined && authContext?.type === 'client') {
877
+ const actingClient = authContext.client || await clientRecordFromAuthContext(authContext);
878
+ if (actingClient?.clientKind !== 'desktop-local') {
879
+ const effectiveRelayUrls = await getEffectiveRelayUrls();
880
+ if (!Array.isArray(effectiveRelayUrls) || !effectiveRelayUrls.includes(requestedRelayUrl)) {
881
+ return res.status(403).json({ error: 'Only the owner UI session can set a custom Relay endpoint' });
882
+ }
883
+ }
884
+ }
862
885
  const candidates = await pairingServerCandidates(req, {
863
886
  preferredServerUrl: req.body?.serverUrl,
864
887
  includeRelay: typeof req.body?.includeRelay === 'boolean' ? req.body.includeRelay : undefined,
@@ -906,8 +929,18 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
906
929
  if (normalized) candidates.push({ type: 'lan', url: normalized, priority: 10 });
907
930
  }
908
931
  try {
909
- const relayCandidate = await getRelayPairingCandidate({ ensureEnabled: false });
910
- if (relayCandidate) candidates.push(relayCandidate);
932
+ // Multi-relay: a paired device learns EVERY configured endpoint so a
933
+ // lost/expensive domain can fall back to the other (the client still
934
+ // keeps its per-connection instance identity — serverId + relayUrl).
935
+ const relayCandidates = await getRelayPairingCandidates();
936
+ if (Array.isArray(relayCandidates)) {
937
+ for (const relayCandidate of relayCandidates) {
938
+ if (relayCandidate) candidates.push(relayCandidate);
939
+ }
940
+ } else {
941
+ const relayCandidate = await getRelayPairingCandidate({ ensureEnabled: false });
942
+ if (relayCandidate) candidates.push(relayCandidate);
943
+ }
911
944
  } catch {
912
945
  // Relay status failure must not break the direct-candidate refresh.
913
946
  }