@cats-inc/cats-platform 0.1.21 → 0.3.4

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 (548) hide show
  1. package/.env.example +3 -7
  2. package/README.md +35 -0
  3. package/build/desktop/bootstrapNavigation.d.ts.map +1 -1
  4. package/build/desktop/bootstrapNavigation.js +6 -13
  5. package/build/desktop/bootstrapNavigation.js.map +1 -1
  6. package/build/desktop/bootstrapPage.d.ts.map +1 -1
  7. package/build/desktop/bootstrapPage.js +63 -86
  8. package/build/desktop/bootstrapPage.js.map +1 -1
  9. package/build/desktop/cliInventoryProbe.d.ts +11 -2
  10. package/build/desktop/cliInventoryProbe.d.ts.map +1 -1
  11. package/build/desktop/cliInventoryProbe.js +19 -13
  12. package/build/desktop/cliInventoryProbe.js.map +1 -1
  13. package/build/desktop/config.d.ts +0 -4
  14. package/build/desktop/config.d.ts.map +1 -1
  15. package/build/desktop/config.js +1 -6
  16. package/build/desktop/config.js.map +1 -1
  17. package/build/desktop/contracts.d.ts +12 -2
  18. package/build/desktop/contracts.d.ts.map +1 -1
  19. package/build/desktop/contracts.js +1 -2
  20. package/build/desktop/contracts.js.map +1 -1
  21. package/build/desktop/hostVersion.d.ts.map +1 -1
  22. package/build/desktop/hostVersion.js +3 -5
  23. package/build/desktop/hostVersion.js.map +1 -1
  24. package/build/desktop/main.js +205 -256
  25. package/build/desktop/main.js.map +1 -1
  26. package/build/desktop/packaging.d.ts +2 -0
  27. package/build/desktop/packaging.d.ts.map +1 -1
  28. package/build/desktop/packaging.js +34 -14
  29. package/build/desktop/packaging.js.map +1 -1
  30. package/build/desktop/preload.cjs +9 -0
  31. package/build/desktop/preload.cjs.map +1 -1
  32. package/build/desktop/processSupervisor.d.ts.map +1 -1
  33. package/build/desktop/processSupervisor.js +3 -0
  34. package/build/desktop/processSupervisor.js.map +1 -1
  35. package/build/desktop/providerManager.d.ts +46 -0
  36. package/build/desktop/providerManager.d.ts.map +1 -0
  37. package/build/desktop/providerManager.js +220 -0
  38. package/build/desktop/providerManager.js.map +1 -0
  39. package/build/desktop/providerSelection.d.ts +38 -0
  40. package/build/desktop/providerSelection.d.ts.map +1 -0
  41. package/build/desktop/providerSelection.js +118 -0
  42. package/build/desktop/providerSelection.js.map +1 -0
  43. package/build/desktop/readiness.d.ts +2 -0
  44. package/build/desktop/readiness.d.ts.map +1 -1
  45. package/build/desktop/readiness.js +14 -33
  46. package/build/desktop/readiness.js.map +1 -1
  47. package/build/desktop/setupAssets.d.ts.map +1 -1
  48. package/build/desktop/setupAssets.js +28 -14
  49. package/build/desktop/setupAssets.js.map +1 -1
  50. package/build/desktop/setupAudit.d.ts +4 -3
  51. package/build/desktop/setupAudit.d.ts.map +1 -1
  52. package/build/desktop/setupAudit.js +18 -34
  53. package/build/desktop/setupAudit.js.map +1 -1
  54. package/build/desktop/setupBridge.js +1 -1
  55. package/build/desktop/setupBridge.js.map +1 -1
  56. package/build/desktop/setupPrerequisites.d.ts +11 -0
  57. package/build/desktop/setupPrerequisites.d.ts.map +1 -0
  58. package/build/desktop/setupPrerequisites.js +47 -0
  59. package/build/desktop/setupPrerequisites.js.map +1 -0
  60. package/build/desktop/updateDialog.d.ts +27 -1
  61. package/build/desktop/updateDialog.d.ts.map +1 -1
  62. package/build/desktop/updateDialog.js +85 -5
  63. package/build/desktop/updateDialog.js.map +1 -1
  64. package/build/renderer/assets/ActiveSessionPermissionChip-B49WcZBJ.js +1 -0
  65. package/build/renderer/assets/App-BpxZB5ym.js +2 -0
  66. package/build/renderer/assets/App-DYTUfAl6.js +1 -0
  67. package/build/renderer/assets/App-DxsV-tKS.css +1 -0
  68. package/build/renderer/assets/{App-DKOSn0o_.js → App-boB4WrmU.js} +1 -1
  69. package/build/renderer/assets/WorkspaceAppRoutes-CPqz85iw.js +1 -0
  70. package/build/renderer/assets/app-C52wwCGz.js +85 -0
  71. package/build/renderer/assets/app-CqnsY5ed.css +1 -0
  72. package/build/renderer/assets/{client-BdGHAUoK.js → client-DREGSZqQ.js} +1 -1
  73. package/build/renderer/assets/conversationSidebarRecentEntries-DK0iWS73.js +6 -0
  74. package/build/renderer/assets/{screenshotOverlay-DfBcolX-.js → screenshotOverlay-EF07cXvw.js} +1 -1
  75. package/build/renderer/assets/{useDraftSessionChips-DCIId0eY.js → useDraftSessionChips-BdZ_B1gy.js} +1 -1
  76. package/build/renderer/desktop/overlay/index.html +2 -2
  77. package/build/renderer/index.html +3 -3
  78. package/build/server/app/renderer/auth/api.d.ts.map +1 -1
  79. package/build/server/app/renderer/auth/api.js +4 -1
  80. package/build/server/app/renderer/auth/api.js.map +1 -1
  81. package/build/server/app/renderer/providerCatalogClient.d.ts.map +1 -1
  82. package/build/server/app/renderer/providerCatalogClient.js +34 -12
  83. package/build/server/app/renderer/providerCatalogClient.js.map +1 -1
  84. package/build/server/app/renderer/providerClientInvalidation.d.ts +6 -0
  85. package/build/server/app/renderer/providerClientInvalidation.d.ts.map +1 -0
  86. package/build/server/app/renderer/providerClientInvalidation.js +12 -0
  87. package/build/server/app/renderer/providerClientInvalidation.js.map +1 -0
  88. package/build/server/app/renderer/providerReadLoop.d.ts +5 -0
  89. package/build/server/app/renderer/providerReadLoop.d.ts.map +1 -0
  90. package/build/server/app/renderer/providerReadLoop.js +52 -0
  91. package/build/server/app/renderer/providerReadLoop.js.map +1 -0
  92. package/build/server/app/renderer/providerRegistryClient.d.ts +3 -2
  93. package/build/server/app/renderer/providerRegistryClient.d.ts.map +1 -1
  94. package/build/server/app/renderer/providerRegistryClient.js +51 -53
  95. package/build/server/app/renderer/providerRegistryClient.js.map +1 -1
  96. package/build/server/app/server/appPackageRoutes.d.ts +2 -0
  97. package/build/server/app/server/appPackageRoutes.d.ts.map +1 -1
  98. package/build/server/app/server/appPackageRoutes.js +105 -0
  99. package/build/server/app/server/appPackageRoutes.js.map +1 -1
  100. package/build/server/app/server/authGatePolicy.js +14 -1
  101. package/build/server/app/server/authGatePolicy.js.map +1 -1
  102. package/build/server/app/server/contracts.d.ts +15 -0
  103. package/build/server/app/server/contracts.d.ts.map +1 -1
  104. package/build/server/app/server/dependencies.d.ts.map +1 -1
  105. package/build/server/app/server/dependencies.js +42 -1
  106. package/build/server/app/server/dependencies.js.map +1 -1
  107. package/build/server/app/server/goldenPathStartupResume.d.ts +55 -0
  108. package/build/server/app/server/goldenPathStartupResume.d.ts.map +1 -0
  109. package/build/server/app/server/goldenPathStartupResume.js +109 -0
  110. package/build/server/app/server/goldenPathStartupResume.js.map +1 -0
  111. package/build/server/app/server/platformSetupRoutes.d.ts.map +1 -1
  112. package/build/server/app/server/platformSetupRoutes.js +1 -13
  113. package/build/server/app/server/platformSetupRoutes.js.map +1 -1
  114. package/build/server/app/server/polling.d.ts.map +1 -1
  115. package/build/server/app/server/polling.js +2 -0
  116. package/build/server/app/server/polling.js.map +1 -1
  117. package/build/server/app/server/requestRouter.d.ts.map +1 -1
  118. package/build/server/app/server/requestRouter.js +30 -0
  119. package/build/server/app/server/requestRouter.js.map +1 -1
  120. package/build/server/app/server/runtimeSurfaceProxy.d.ts.map +1 -1
  121. package/build/server/app/server/runtimeSurfaceProxy.js +4 -2
  122. package/build/server/app/server/runtimeSurfaceProxy.js.map +1 -1
  123. package/build/server/app/server/startupRecovery.d.ts.map +1 -1
  124. package/build/server/app/server/startupRecovery.js +5 -0
  125. package/build/server/app/server/startupRecovery.js.map +1 -1
  126. package/build/server/app/server/subscribeRoutes.d.ts.map +1 -1
  127. package/build/server/app/server/subscribeRoutes.js +5 -3
  128. package/build/server/app/server/subscribeRoutes.js.map +1 -1
  129. package/build/server/app/server/telegramCommandSurface.d.ts +29 -0
  130. package/build/server/app/server/telegramCommandSurface.d.ts.map +1 -0
  131. package/build/server/app/server/telegramCommandSurface.js +123 -0
  132. package/build/server/app/server/telegramCommandSurface.js.map +1 -0
  133. package/build/server/app/server/transportWorkGoldenPath.d.ts +82 -0
  134. package/build/server/app/server/transportWorkGoldenPath.d.ts.map +1 -0
  135. package/build/server/app/server/transportWorkGoldenPath.js +307 -0
  136. package/build/server/app/server/transportWorkGoldenPath.js.map +1 -0
  137. package/build/server/config.d.ts +21 -0
  138. package/build/server/config.d.ts.map +1 -1
  139. package/build/server/config.js +12 -0
  140. package/build/server/config.js.map +1 -1
  141. package/build/server/design/components/providerModelFieldsSupport.d.ts +5 -24
  142. package/build/server/design/components/providerModelFieldsSupport.d.ts.map +1 -1
  143. package/build/server/design/components/providerModelFieldsSupport.js +65 -70
  144. package/build/server/design/components/providerModelFieldsSupport.js.map +1 -1
  145. package/build/server/design/components/useProviderCatalogState.d.ts +2 -2
  146. package/build/server/design/components/useProviderCatalogState.d.ts.map +1 -1
  147. package/build/server/design/components/useProviderCatalogState.js +71 -151
  148. package/build/server/design/components/useProviderCatalogState.js.map +1 -1
  149. package/build/server/design/components/useProviderModelFieldActions.js +2 -2
  150. package/build/server/design/components/useProviderModelFieldActions.js.map +1 -1
  151. package/build/server/design/components/useProviderRegistryState.d.ts +1 -7
  152. package/build/server/design/components/useProviderRegistryState.d.ts.map +1 -1
  153. package/build/server/design/components/useProviderRegistryState.js +41 -104
  154. package/build/server/design/components/useProviderRegistryState.js.map +1 -1
  155. package/build/server/design/components/useProviderTargetReconciliation.d.ts +1 -0
  156. package/build/server/design/components/useProviderTargetReconciliation.d.ts.map +1 -1
  157. package/build/server/design/components/useProviderTargetReconciliation.js +3 -2
  158. package/build/server/design/components/useProviderTargetReconciliation.js.map +1 -1
  159. package/build/server/index.js +4 -0
  160. package/build/server/index.js.map +1 -1
  161. package/build/server/platform/apps/packageInstaller.d.ts +19 -0
  162. package/build/server/platform/apps/packageInstaller.d.ts.map +1 -0
  163. package/build/server/platform/apps/packageInstaller.js +92 -0
  164. package/build/server/platform/apps/packageInstaller.js.map +1 -0
  165. package/build/server/platform/apps/registry.d.ts +6 -0
  166. package/build/server/platform/apps/registry.d.ts.map +1 -1
  167. package/build/server/platform/apps/registry.js +24 -0
  168. package/build/server/platform/apps/registry.js.map +1 -1
  169. package/build/server/platform/apps/renderer.d.ts +6 -0
  170. package/build/server/platform/apps/renderer.d.ts.map +1 -0
  171. package/build/server/platform/apps/renderer.js +18 -0
  172. package/build/server/platform/apps/renderer.js.map +1 -0
  173. package/build/server/platform/orchestration/entitySubscriptions/channel.d.ts +2 -1
  174. package/build/server/platform/orchestration/entitySubscriptions/channel.d.ts.map +1 -1
  175. package/build/server/platform/orchestration/entitySubscriptions/channel.js +3 -18
  176. package/build/server/platform/orchestration/entitySubscriptions/channel.js.map +1 -1
  177. package/build/server/platform/orchestration/entitySubscriptions/index.d.ts +1 -0
  178. package/build/server/platform/orchestration/entitySubscriptions/index.d.ts.map +1 -1
  179. package/build/server/platform/orchestration/entitySubscriptions/index.js.map +1 -1
  180. package/build/server/platform/runtime/deliveryClient.d.ts +111 -0
  181. package/build/server/platform/runtime/deliveryClient.d.ts.map +1 -0
  182. package/build/server/platform/runtime/deliveryClient.js +289 -0
  183. package/build/server/platform/runtime/deliveryClient.js.map +1 -0
  184. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.d.ts +1 -0
  185. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.d.ts.map +1 -1
  186. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.js +10 -0
  187. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.js.map +1 -1
  188. package/build/server/platform/supervision/runtimeBoundary.d.ts +11 -1
  189. package/build/server/platform/supervision/runtimeBoundary.d.ts.map +1 -1
  190. package/build/server/platform/supervision/runtimeBoundary.js +2 -2
  191. package/build/server/platform/supervision/runtimeBoundary.js.map +1 -1
  192. package/build/server/platform/transports/telegram/bridge.d.ts +18 -0
  193. package/build/server/platform/transports/telegram/bridge.d.ts.map +1 -1
  194. package/build/server/platform/transports/telegram/bridge.js +176 -0
  195. package/build/server/platform/transports/telegram/bridge.js.map +1 -1
  196. package/build/server/platform/transports/telegram/commandPort.d.ts +40 -0
  197. package/build/server/platform/transports/telegram/commandPort.d.ts.map +1 -0
  198. package/build/server/platform/transports/telegram/commandPort.js +17 -0
  199. package/build/server/platform/transports/telegram/commandPort.js.map +1 -0
  200. package/build/server/platform/transports/telegram/commandRouter.d.ts +22 -0
  201. package/build/server/platform/transports/telegram/commandRouter.d.ts.map +1 -1
  202. package/build/server/platform/transports/telegram/commandRouter.js.map +1 -1
  203. package/build/server/platform/transports/telegram/commands/index.d.ts.map +1 -1
  204. package/build/server/platform/transports/telegram/commands/index.js +38 -0
  205. package/build/server/platform/transports/telegram/commands/index.js.map +1 -1
  206. package/build/server/platform/transports/telegram/ingressDispatch.d.ts +56 -0
  207. package/build/server/platform/transports/telegram/ingressDispatch.d.ts.map +1 -0
  208. package/build/server/platform/transports/telegram/ingressDispatch.js +93 -0
  209. package/build/server/platform/transports/telegram/ingressDispatch.js.map +1 -0
  210. package/build/server/platform/transports/telegram/polling.d.ts +34 -0
  211. package/build/server/platform/transports/telegram/polling.d.ts.map +1 -1
  212. package/build/server/platform/transports/telegram/polling.js +86 -9
  213. package/build/server/platform/transports/telegram/polling.js.map +1 -1
  214. package/build/server/platform/transports/telegram/relay/index.d.ts +3 -0
  215. package/build/server/platform/transports/telegram/relay/index.d.ts.map +1 -1
  216. package/build/server/platform/transports/telegram/relay/index.js +5 -1
  217. package/build/server/platform/transports/telegram/relay/index.js.map +1 -1
  218. package/build/server/platform/transports/telegram/relay/ingress.d.ts +2 -0
  219. package/build/server/platform/transports/telegram/relay/ingress.d.ts.map +1 -1
  220. package/build/server/platform/transports/telegram/relay/ingress.js +1 -1
  221. package/build/server/platform/transports/telegram/relay/ingress.js.map +1 -1
  222. package/build/server/platform/transports/work-delivery/actionTokens.d.ts +66 -0
  223. package/build/server/platform/transports/work-delivery/actionTokens.d.ts.map +1 -0
  224. package/build/server/platform/transports/work-delivery/actionTokens.js +145 -0
  225. package/build/server/platform/transports/work-delivery/actionTokens.js.map +1 -0
  226. package/build/server/platform/transports/work-delivery/contracts.d.ts +172 -0
  227. package/build/server/platform/transports/work-delivery/contracts.d.ts.map +1 -0
  228. package/build/server/platform/transports/work-delivery/contracts.js +26 -0
  229. package/build/server/platform/transports/work-delivery/contracts.js.map +1 -0
  230. package/build/server/platform/transports/work-delivery/deliveryGates.d.ts +32 -0
  231. package/build/server/platform/transports/work-delivery/deliveryGates.d.ts.map +1 -0
  232. package/build/server/platform/transports/work-delivery/deliveryGates.js +45 -0
  233. package/build/server/platform/transports/work-delivery/deliveryGates.js.map +1 -0
  234. package/build/server/platform/transports/work-delivery/inboundClassification.d.ts +31 -0
  235. package/build/server/platform/transports/work-delivery/inboundClassification.d.ts.map +1 -0
  236. package/build/server/platform/transports/work-delivery/inboundClassification.js +52 -0
  237. package/build/server/platform/transports/work-delivery/inboundClassification.js.map +1 -0
  238. package/build/server/platform/transports/work-delivery/outbox.d.ts +84 -0
  239. package/build/server/platform/transports/work-delivery/outbox.d.ts.map +1 -0
  240. package/build/server/platform/transports/work-delivery/outbox.js +270 -0
  241. package/build/server/platform/transports/work-delivery/outbox.js.map +1 -0
  242. package/build/server/platform/transports/work-delivery/permissionEnvelope.d.ts +57 -0
  243. package/build/server/platform/transports/work-delivery/permissionEnvelope.d.ts.map +1 -0
  244. package/build/server/platform/transports/work-delivery/permissionEnvelope.js +74 -0
  245. package/build/server/platform/transports/work-delivery/permissionEnvelope.js.map +1 -0
  246. package/build/server/platform/transports/work-delivery/port.d.ts +60 -0
  247. package/build/server/platform/transports/work-delivery/port.d.ts.map +1 -0
  248. package/build/server/platform/transports/work-delivery/port.js +15 -0
  249. package/build/server/platform/transports/work-delivery/port.js.map +1 -0
  250. package/build/server/platform/transports/work-delivery/proposal.d.ts +31 -0
  251. package/build/server/platform/transports/work-delivery/proposal.d.ts.map +1 -0
  252. package/build/server/platform/transports/work-delivery/proposal.js +59 -0
  253. package/build/server/platform/transports/work-delivery/proposal.js.map +1 -0
  254. package/build/server/platform/transports/work-delivery/readiness.d.ts +61 -0
  255. package/build/server/platform/transports/work-delivery/readiness.d.ts.map +1 -0
  256. package/build/server/platform/transports/work-delivery/readiness.js +123 -0
  257. package/build/server/platform/transports/work-delivery/readiness.js.map +1 -0
  258. package/build/server/platform/transports/work-delivery/stageProjection.d.ts +43 -0
  259. package/build/server/platform/transports/work-delivery/stageProjection.d.ts.map +1 -0
  260. package/build/server/platform/transports/work-delivery/stageProjection.js +132 -0
  261. package/build/server/platform/transports/work-delivery/stageProjection.js.map +1 -0
  262. package/build/server/platform/transports/work-delivery/stateStore.d.ts +21 -0
  263. package/build/server/platform/transports/work-delivery/stateStore.d.ts.map +1 -0
  264. package/build/server/platform/transports/work-delivery/stateStore.js +193 -0
  265. package/build/server/platform/transports/work-delivery/stateStore.js.map +1 -0
  266. package/build/server/platform/transports/work-delivery/telemetry.d.ts +49 -0
  267. package/build/server/platform/transports/work-delivery/telemetry.d.ts.map +1 -0
  268. package/build/server/platform/transports/work-delivery/telemetry.js +80 -0
  269. package/build/server/platform/transports/work-delivery/telemetry.js.map +1 -0
  270. package/build/server/products/chat/api/contracts.d.ts +10 -0
  271. package/build/server/products/chat/api/contracts.d.ts.map +1 -1
  272. package/build/server/products/chat/api/resources/channelRoutes.d.ts.map +1 -1
  273. package/build/server/products/chat/api/resources/channelRoutes.js +27 -1
  274. package/build/server/products/chat/api/resources/channelRoutes.js.map +1 -1
  275. package/build/server/products/chat/api/routeSupport.d.ts +17 -1
  276. package/build/server/products/chat/api/routeSupport.d.ts.map +1 -1
  277. package/build/server/products/chat/api/routeSupport.js +27 -4
  278. package/build/server/products/chat/api/routeSupport.js.map +1 -1
  279. package/build/server/products/chat/api/setupRoutes.d.ts.map +1 -1
  280. package/build/server/products/chat/api/setupRoutes.js +23 -8
  281. package/build/server/products/chat/api/setupRoutes.js.map +1 -1
  282. package/build/server/products/chat/state/model/channelExecutionTargets.d.ts +15 -0
  283. package/build/server/products/chat/state/model/channelExecutionTargets.d.ts.map +1 -1
  284. package/build/server/products/chat/state/model/channelExecutionTargets.js +25 -0
  285. package/build/server/products/chat/state/model/channelExecutionTargets.js.map +1 -1
  286. package/build/server/products/chat/state/model/index.d.ts +1 -1
  287. package/build/server/products/chat/state/model/index.d.ts.map +1 -1
  288. package/build/server/products/chat/state/model/index.js +1 -1
  289. package/build/server/products/chat/state/model/index.js.map +1 -1
  290. package/build/server/products/chat/state/model/readModels.d.ts +1 -0
  291. package/build/server/products/chat/state/model/readModels.d.ts.map +1 -1
  292. package/build/server/products/chat/state/model/readModels.js +2 -1
  293. package/build/server/products/chat/state/model/readModels.js.map +1 -1
  294. package/build/server/products/code/api/relayRoutes.d.ts.map +1 -1
  295. package/build/server/products/code/api/relayRoutes.js +36 -7
  296. package/build/server/products/code/api/relayRoutes.js.map +1 -1
  297. package/build/server/products/code/state/relayState.d.ts +2 -1
  298. package/build/server/products/code/state/relayState.d.ts.map +1 -1
  299. package/build/server/products/code/state/relayState.js +7 -7
  300. package/build/server/products/code/state/relayState.js.map +1 -1
  301. package/build/server/products/shared/api/workspaceContracts.d.ts +5 -0
  302. package/build/server/products/shared/api/workspaceContracts.d.ts.map +1 -1
  303. package/build/server/products/shared/renderer/api/appShell.d.ts +1 -0
  304. package/build/server/products/shared/renderer/api/appShell.d.ts.map +1 -1
  305. package/build/server/products/shared/renderer/api/appShell.js +9 -0
  306. package/build/server/products/shared/renderer/api/appShell.js.map +1 -1
  307. package/build/server/products/work/api/goldenPathProjection.d.ts +156 -0
  308. package/build/server/products/work/api/goldenPathProjection.d.ts.map +1 -0
  309. package/build/server/products/work/api/goldenPathProjection.js +252 -0
  310. package/build/server/products/work/api/goldenPathProjection.js.map +1 -0
  311. package/build/server/products/work/api/index.d.ts +54 -1
  312. package/build/server/products/work/api/index.d.ts.map +1 -1
  313. package/build/server/products/work/api/index.js +182 -4
  314. package/build/server/products/work/api/index.js.map +1 -1
  315. package/build/server/products/work/api/projection.d.ts +7 -1
  316. package/build/server/products/work/api/projection.d.ts.map +1 -1
  317. package/build/server/products/work/api/projection.js +7 -1
  318. package/build/server/products/work/api/projection.js.map +1 -1
  319. package/build/server/products/work/shared/apiPaths.d.ts +8 -0
  320. package/build/server/products/work/shared/apiPaths.d.ts.map +1 -1
  321. package/build/server/products/work/shared/apiPaths.js +8 -0
  322. package/build/server/products/work/shared/apiPaths.js.map +1 -1
  323. package/build/server/products/work/shared/workGoldenPathMessages.d.ts +83 -0
  324. package/build/server/products/work/shared/workGoldenPathMessages.d.ts.map +1 -0
  325. package/build/server/products/work/shared/workGoldenPathMessages.js +211 -0
  326. package/build/server/products/work/shared/workGoldenPathMessages.js.map +1 -0
  327. package/build/server/products/work/shared/workGoldenPathMetadata.d.ts +45 -0
  328. package/build/server/products/work/shared/workGoldenPathMetadata.d.ts.map +1 -0
  329. package/build/server/products/work/shared/workGoldenPathMetadata.js +122 -0
  330. package/build/server/products/work/shared/workGoldenPathMetadata.js.map +1 -0
  331. package/build/server/products/work/state/workCompletionEvidence.d.ts +42 -0
  332. package/build/server/products/work/state/workCompletionEvidence.d.ts.map +1 -0
  333. package/build/server/products/work/state/workCompletionEvidence.js +59 -0
  334. package/build/server/products/work/state/workCompletionEvidence.js.map +1 -0
  335. package/build/server/products/work/state/workExecutionTaskDelegate.d.ts +16 -0
  336. package/build/server/products/work/state/workExecutionTaskDelegate.d.ts.map +1 -1
  337. package/build/server/products/work/state/workExecutionTaskDelegate.js +91 -68
  338. package/build/server/products/work/state/workExecutionTaskDelegate.js.map +1 -1
  339. package/build/server/products/work/state/workGoldenPathAdmission.d.ts +78 -0
  340. package/build/server/products/work/state/workGoldenPathAdmission.d.ts.map +1 -0
  341. package/build/server/products/work/state/workGoldenPathAdmission.js +249 -0
  342. package/build/server/products/work/state/workGoldenPathAdmission.js.map +1 -0
  343. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.d.ts +33 -0
  344. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.d.ts.map +1 -0
  345. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.js +161 -0
  346. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.js.map +1 -0
  347. package/build/server/products/work/state/workGoldenPathIntake.d.ts +34 -0
  348. package/build/server/products/work/state/workGoldenPathIntake.d.ts.map +1 -0
  349. package/build/server/products/work/state/workGoldenPathIntake.js +50 -0
  350. package/build/server/products/work/state/workGoldenPathIntake.js.map +1 -0
  351. package/build/server/products/work/state/workGoldenPathLifecycle.d.ts +44 -0
  352. package/build/server/products/work/state/workGoldenPathLifecycle.d.ts.map +1 -0
  353. package/build/server/products/work/state/workGoldenPathLifecycle.js +146 -0
  354. package/build/server/products/work/state/workGoldenPathLifecycle.js.map +1 -0
  355. package/build/server/products/work/state/workGoldenPathPublish.d.ts +84 -0
  356. package/build/server/products/work/state/workGoldenPathPublish.d.ts.map +1 -0
  357. package/build/server/products/work/state/workGoldenPathPublish.js +488 -0
  358. package/build/server/products/work/state/workGoldenPathPublish.js.map +1 -0
  359. package/build/server/products/work/state/workGoldenPathRunner.d.ts +121 -0
  360. package/build/server/products/work/state/workGoldenPathRunner.d.ts.map +1 -0
  361. package/build/server/products/work/state/workGoldenPathRunner.js +447 -0
  362. package/build/server/products/work/state/workGoldenPathRunner.js.map +1 -0
  363. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.d.ts +74 -0
  364. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.d.ts.map +1 -0
  365. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.js +239 -0
  366. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.js.map +1 -0
  367. package/build/server/products/work/state/workGoldenPathService.d.ts +152 -0
  368. package/build/server/products/work/state/workGoldenPathService.d.ts.map +1 -0
  369. package/build/server/products/work/state/workGoldenPathService.js +1057 -0
  370. package/build/server/products/work/state/workGoldenPathService.js.map +1 -0
  371. package/build/server/products/work/state/workGoldenPathTelegramPort.d.ts +87 -0
  372. package/build/server/products/work/state/workGoldenPathTelegramPort.d.ts.map +1 -0
  373. package/build/server/products/work/state/workGoldenPathTelegramPort.js +220 -0
  374. package/build/server/products/work/state/workGoldenPathTelegramPort.js.map +1 -0
  375. package/build/server/runtime/client.d.ts +13 -0
  376. package/build/server/runtime/client.d.ts.map +1 -1
  377. package/build/server/runtime/client.js +44 -1
  378. package/build/server/runtime/client.js.map +1 -1
  379. package/build/server/runtime/setup.d.ts +14 -4
  380. package/build/server/runtime/setup.d.ts.map +1 -1
  381. package/build/server/runtime/setup.js +17 -19
  382. package/build/server/runtime/setup.js.map +1 -1
  383. package/build/server/runtime/usageSnapshot.d.ts +3 -0
  384. package/build/server/runtime/usageSnapshot.d.ts.map +1 -0
  385. package/build/server/runtime/usageSnapshot.js +69 -0
  386. package/build/server/runtime/usageSnapshot.js.map +1 -0
  387. package/build/server/server/routes/providerCapabilityBootstrap.d.ts +69 -0
  388. package/build/server/server/routes/providerCapabilityBootstrap.d.ts.map +1 -0
  389. package/build/server/server/routes/providerCapabilityBootstrap.js +165 -0
  390. package/build/server/server/routes/providerCapabilityBootstrap.js.map +1 -0
  391. package/build/server/server/routes/providers.d.ts.map +1 -1
  392. package/build/server/server/routes/providers.js +133 -131
  393. package/build/server/server/routes/providers.js.map +1 -1
  394. package/build/server/server/routes/telegram.d.ts +9 -0
  395. package/build/server/server/routes/telegram.d.ts.map +1 -1
  396. package/build/server/server/routes/telegram.js +79 -72
  397. package/build/server/server/routes/telegram.js.map +1 -1
  398. package/build/server/shared/catsAppManifest.d.ts +3 -1
  399. package/build/server/shared/catsAppManifest.d.ts.map +1 -1
  400. package/build/server/shared/catsAppManifest.js +2 -0
  401. package/build/server/shared/catsAppManifest.js.map +1 -1
  402. package/build/server/shared/desktopRecoveryBridge.d.ts +2 -1
  403. package/build/server/shared/desktopRecoveryBridge.d.ts.map +1 -1
  404. package/build/server/shared/desktopRecoveryBridge.js.map +1 -1
  405. package/build/server/shared/executionLabel.d.ts.map +1 -1
  406. package/build/server/shared/executionLabel.js +8 -1
  407. package/build/server/shared/executionLabel.js.map +1 -1
  408. package/build/server/shared/i18n/catalogs/en.d.ts.map +1 -1
  409. package/build/server/shared/i18n/catalogs/en.js +198 -3
  410. package/build/server/shared/i18n/catalogs/en.js.map +1 -1
  411. package/build/server/shared/i18n/catalogs/zh-TW.d.ts.map +1 -1
  412. package/build/server/shared/i18n/catalogs/zh-TW.js +197 -3
  413. package/build/server/shared/i18n/catalogs/zh-TW.js.map +1 -1
  414. package/build/server/shared/i18n/index.d.ts +192 -0
  415. package/build/server/shared/i18n/index.d.ts.map +1 -1
  416. package/build/server/shared/i18n/messageKeys.d.ts +192 -0
  417. package/build/server/shared/i18n/messageKeys.d.ts.map +1 -1
  418. package/build/server/shared/i18n/messageKeys.js +192 -0
  419. package/build/server/shared/i18n/messageKeys.js.map +1 -1
  420. package/build/server/shared/platformSetupOperation.d.ts +2 -0
  421. package/build/server/shared/platformSetupOperation.d.ts.map +1 -0
  422. package/build/server/shared/platformSetupOperation.js +9 -0
  423. package/build/server/shared/platformSetupOperation.js.map +1 -0
  424. package/build/server/shared/providerCatalog.d.ts +5 -2
  425. package/build/server/shared/providerCatalog.d.ts.map +1 -1
  426. package/build/server/shared/providerCatalog.js +6 -0
  427. package/build/server/shared/providerCatalog.js.map +1 -1
  428. package/build/server/shared/providerCatalogData.d.ts +154 -77
  429. package/build/server/shared/providerCatalogData.d.ts.map +1 -1
  430. package/build/server/shared/providerCatalogData.js +73 -36
  431. package/build/server/shared/providerCatalogData.js.map +1 -1
  432. package/build/server/shared/providerCatalogInstances.d.ts.map +1 -1
  433. package/build/server/shared/providerCatalogInstances.js +3 -0
  434. package/build/server/shared/providerCatalogInstances.js.map +1 -1
  435. package/build/server/shared/providerModelLabelRegistry.d.ts +29 -0
  436. package/build/server/shared/providerModelLabelRegistry.d.ts.map +1 -0
  437. package/build/server/shared/providerModelLabelRegistry.js +49 -0
  438. package/build/server/shared/providerModelLabelRegistry.js.map +1 -0
  439. package/build/server/shared/providerRegistryWarnings.d.ts +4 -0
  440. package/build/server/shared/providerRegistryWarnings.d.ts.map +1 -1
  441. package/build/server/shared/providerRegistryWarnings.js +7 -0
  442. package/build/server/shared/providerRegistryWarnings.js.map +1 -1
  443. package/build/server/shared/providerSelection.d.ts.map +1 -1
  444. package/build/server/shared/providerSelection.js +10 -13
  445. package/build/server/shared/providerSelection.js.map +1 -1
  446. package/build/server/shared/runtimeSetup.d.ts +17 -4
  447. package/build/server/shared/runtimeSetup.d.ts.map +1 -1
  448. package/build/server/shared/runtimeStatusPresentation.js +1 -1
  449. package/build/server/shared/runtimeStatusPresentation.js.map +1 -1
  450. package/package.json +32 -3
  451. package/packages/app-sdk/browser.d.ts +51 -0
  452. package/packages/app-sdk/browser.js +48 -0
  453. package/packages/app-sdk/package.d.ts +14 -0
  454. package/packages/app-sdk/package.js +131 -0
  455. package/packages/app-sdk/package.json +7 -0
  456. package/packages/provider-setup/manager.d.ts +79 -0
  457. package/packages/provider-setup/manager.js +441 -0
  458. package/scripts/README.md +66 -26
  459. package/scripts/build-desktop-installer.mjs +139 -16
  460. package/scripts/build-test-ui.mjs +1 -1
  461. package/scripts/bundle-server.mjs +23 -10
  462. package/scripts/linux/build-desktop-package.sh +6 -6
  463. package/scripts/linux/install-antigravity.sh +0 -0
  464. package/scripts/linux/install-devin.sh +0 -0
  465. package/scripts/linux/install-github-cli.sh +5 -0
  466. package/scripts/linux/install-meta-cli.sh +8 -0
  467. package/scripts/linux/install-node.sh +9 -3
  468. package/scripts/linux/node-cli-common.sh +87 -48
  469. package/scripts/linux/provider-cli-common.sh +178 -130
  470. package/scripts/linux/provider-version-common.sh +247 -0
  471. package/scripts/linux/sync-agent-skills.sh +27 -5
  472. package/scripts/linux/test-linux-package-smoke.sh +6 -3
  473. package/scripts/linux/upgrade-cli-tools.sh +1 -1
  474. package/scripts/linux/verified-npm-update.sh +20 -0
  475. package/scripts/macos/build-desktop-package.sh +5 -5
  476. package/scripts/macos/install-antigravity.sh +0 -0
  477. package/scripts/macos/install-devin.sh +0 -0
  478. package/scripts/macos/install-github-cli.sh +5 -0
  479. package/scripts/macos/install-meta-cli.sh +8 -0
  480. package/scripts/macos/install-node.sh +9 -3
  481. package/scripts/macos/node-cli-common.sh +87 -48
  482. package/scripts/macos/provider-cli-common.sh +178 -130
  483. package/scripts/macos/provider-version-common.sh +247 -0
  484. package/scripts/macos/sync-agent-skills.sh +27 -5
  485. package/scripts/macos/test-macos-package-smoke.sh +6 -3
  486. package/scripts/macos/upgrade-cli-tools.sh +1 -1
  487. package/scripts/macos/verified-npm-update.sh +20 -0
  488. package/scripts/package-desktop.mjs +15 -2
  489. package/scripts/testing/check-usage-app.mts +266 -0
  490. package/scripts/testing/conversation-navigation-smoke-electron.cjs +134 -0
  491. package/scripts/testing/conversation-navigation-smoke-renderer.tsx +102 -0
  492. package/scripts/testing/conversation-navigation-smoke.mjs +67 -0
  493. package/scripts/testing/usage-app-smoke-electron.cjs +10 -0
  494. package/scripts/testing/usage-app-smoke-renderer.tsx +8 -0
  495. package/scripts/validate-release-assets.mjs +4 -1
  496. package/scripts/verify-desktop-app-bundle.mjs +108 -0
  497. package/scripts/windows/Build-DesktopPackage.ps1 +9 -6
  498. package/scripts/windows/Install-Antigravity.ps1 +26 -3
  499. package/scripts/windows/Install-ClaudeCode.ps1 +25 -7
  500. package/scripts/windows/Install-CursorAgent.ps1 +45 -30
  501. package/scripts/windows/Install-Devin.ps1 +16 -47
  502. package/scripts/windows/Install-GitHubCli.ps1 +4 -0
  503. package/scripts/windows/Install-Goose.ps1 +18 -1
  504. package/scripts/windows/Install-Grok.ps1 +14 -1
  505. package/scripts/windows/Install-Junie.ps1 +32 -11
  506. package/scripts/windows/Install-KiroCli.ps1 +37 -13
  507. package/scripts/windows/Install-MetaCli.ps1 +463 -0
  508. package/scripts/windows/Install-Node.ps1 +6 -1
  509. package/scripts/windows/Install-Ollama.ps1 +4 -1
  510. package/scripts/windows/Setup-NodeGlobalPrefix.ps1 +10 -0
  511. package/scripts/windows/Sync-AgentSkills.ps1 +29 -6
  512. package/scripts/windows/Test-WindowsInstallerSmoke.ps1 +4 -1
  513. package/scripts/windows/_HiddenProcess.ps1 +14 -3
  514. package/scripts/windows/_NativeInstallerSupport.ps1 +308 -0
  515. package/scripts/windows/_NpmCliInstaller.ps1 +35 -14
  516. package/scripts/windows/_PackagedUninstall.ps1 +9 -1
  517. package/scripts/windows/_VerifiedNpmUpdate.ps1 +25 -0
  518. package/build/desktop/updateNotifications.d.ts +0 -64
  519. package/build/desktop/updateNotifications.d.ts.map +0 -1
  520. package/build/desktop/updateNotifications.js +0 -143
  521. package/build/desktop/updateNotifications.js.map +0 -1
  522. package/build/renderer/assets/ActiveSessionPermissionChip-DImsuR9T.js +0 -1
  523. package/build/renderer/assets/App-BfOvMP2m.js +0 -1
  524. package/build/renderer/assets/App-DGwkRFZl.css +0 -1
  525. package/build/renderer/assets/App-DWd7I3Z2.js +0 -2
  526. package/build/renderer/assets/WorkspaceAppRoutes-CrbTA9En.js +0 -1
  527. package/build/renderer/assets/app-BTww3Hl4.js +0 -43
  528. package/build/renderer/assets/app-CPxWQwLA.css +0 -1
  529. package/build/renderer/assets/conversationSidebarRecentEntries-QuaOPxCb.js +0 -6
  530. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.d.ts +0 -48
  531. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.d.ts.map +0 -1
  532. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.js +0 -535
  533. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.js.map +0 -1
  534. package/build/server/design/components/useProviderRegistryAutoRecheck.d.ts +0 -13
  535. package/build/server/design/components/useProviderRegistryAutoRecheck.d.ts.map +0 -1
  536. package/build/server/design/components/useProviderRegistryAutoRecheck.js +0 -63
  537. package/build/server/design/components/useProviderRegistryAutoRecheck.js.map +0 -1
  538. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.d.ts +0 -19
  539. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.d.ts.map +0 -1
  540. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.js +0 -53
  541. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.js.map +0 -1
  542. package/build/server/products/shared/renderer/api/providers.d.ts +0 -17
  543. package/build/server/products/shared/renderer/api/providers.d.ts.map +0 -1
  544. package/build/server/products/shared/renderer/api/providers.js +0 -50
  545. package/build/server/products/shared/renderer/api/providers.js.map +0 -1
  546. package/scripts/linux/install-aider.sh +0 -8
  547. package/scripts/macos/install-aider.sh +0 -8
  548. package/scripts/windows/Install-Aider.ps1 +0 -368
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env node
2
+ // Integration-only: built App archive + real host surface/route + fixture runtime.
3
+ // Usage: node --import tsx scripts/testing/check-usage-app.mts --apps-lock <path>
4
+ // Requires CATS_TEST_PLAYWRIGHT_MODULE (absolute module file) and CATS_TEST_BROWSER_EXECUTABLE.
5
+ import assert from 'node:assert/strict';
6
+ import { createServer } from 'node:http';
7
+ import { mkdtemp, mkdir, readFile } from 'node:fs/promises';
8
+ import { tmpdir } from 'node:os';
9
+ import path from 'node:path';
10
+ import { fileURLToPath, pathToFileURL } from 'node:url';
11
+ import { build } from 'esbuild';
12
+ import { resolveAppLock } from '#cats-app-package';
13
+ import { installRendererPackage } from '../../src/platform/apps/packageInstaller.js';
14
+ import { FileCatsAppRegistry } from '../../src/platform/apps/registry.js';
15
+ import { resolveCatsAppStoragePathsFromChatState } from '../../src/platform/apps/paths.js';
16
+ import { projectUsageSnapshot, projectUsageQuotaRefresh } from '../../src/runtime/usageSnapshot.js';
17
+ import { toPlatformInstalledAppDescriptor } from '../../src/platform/apps/envelope.js';
18
+ import { routeRequest } from '../../src/app/server/requestRouter.js';
19
+ import type { ResolvedServerDependencies } from '../../src/app/server/contracts.js';
20
+ import { loadConfig } from '../../src/config.js';
21
+ import { createDefaultCoreState } from '../../src/core/model/index.js';
22
+ import { MemoryCoreStore } from '../../src/core/store.js';
23
+ import { createEmptyPlatformAuthState, createFirstAdminLocalAuthState, MemoryPlatformAuthStore } from '../../src/platform/auth/index.js';
24
+
25
+ const args = process.argv.slice(2);
26
+ if (args.includes('--help')) {
27
+ process.stdout.write('Usage: node --import tsx scripts/testing/check-usage-app.mts --apps-lock <path> [--renderer-root <built renderer directory>] [--electron] [--check-loading-recovery]\nRequires CATS_TEST_PLAYWRIGHT_MODULE and CATS_TEST_BROWSER_EXECUTABLE; no real user state is read.\n');
28
+ process.exit(0);
29
+ }
30
+ const lockPath = args[args.indexOf('--apps-lock') + 1];
31
+ const rendererRoot = args.includes('--renderer-root') ? path.resolve(args[args.indexOf('--renderer-root') + 1]!) : null;
32
+ if (args.includes('--electron') && args.includes('--check-loading-recovery')) {
33
+ throw new Error('Loading fault injection requires Chromium/Edge. Run Electron functional checks without --check-loading-recovery.');
34
+ }
35
+ if (args.indexOf('--apps-lock') < 0 || !lockPath || !process.env.CATS_TEST_PLAYWRIGHT_MODULE || !process.env.CATS_TEST_BROWSER_EXECUTABLE) throw new Error('App lock and isolated browser test inputs are required.');
36
+ const root = await mkdtemp(path.join(tmpdir(), 'cats-usage-browser-'));
37
+ const chatStatePath = path.join(root, 'state', 'chat-state.local.json');
38
+ const apps = await resolveAppLock(path.resolve(lockPath));
39
+ const usage = apps.find((app) => app.id === 'cats.usage');
40
+ if (!usage) throw new Error('The selected lock must contain a built Usage package.');
41
+ const entryPath = rendererRoot ? '/apps/cats.usage' : `/?appVersion=${encodeURIComponent(usage.version)}`;
42
+ await installRendererPackage({ chatStatePath, bytes: usage.bytes, pin: usage, source: 'local-package', enable: true });
43
+ const registry = new FileCatsAppRegistry({ registryPath: resolveCatsAppStoragePathsFromChatState(chatStatePath).registryPath });
44
+ const bundle = rendererRoot ? null : await build({ entryPoints: [fileURLToPath(new URL('./usage-app-smoke-renderer.tsx', import.meta.url))], bundle: true,
45
+ format: 'esm', platform: 'browser', write: false, jsx: 'automatic', define: { 'process.env.NODE_ENV': '"production"' } });
46
+ const js = bundle?.outputFiles[0]!.contents;
47
+ const installed = (await registry.getInstalledApp(usage.id))!;
48
+ const hostEnvelope = {
49
+ app: { name: 'cats-platform', stage: 'phase-2-shell', runtimeBoundary: 'cats-runtime' },
50
+ products: [], installedApps: [toPlatformInstalledAppDescriptor(installed)],
51
+ desktop: { startAtLogin: false, openWindowOnStartup: true, systemTrayEnabled: true,
52
+ mobilePairing: { enabled: false, transport: 'tailscale', installId: null, displayName: null } },
53
+ language: { uiLanguagePreference: 'zh-TW' },
54
+ lobby: { animationMode: 'reduced', cats: [] },
55
+ runtime: { baseUrl: 'http://127.0.0.1', reachable: true, status: 'ok', service: 'cats-runtime' },
56
+ runtimeSetup: { source: 'runtime', bootstrapRequired: false, status: 'ready', stateStatus: 'ready', summary: 'Ready',
57
+ scannedAt: null, lastManualScanAt: null, appliedAt: null, providerCount: 0, availableCount: 0,
58
+ providersReadyToApply: [], providersNeedingAttention: [], suggestedProviders: [], canRunManualScan: true, canApply: false, error: null },
59
+ metadata: { generatedAt: new Date().toISOString(), host: '127.0.0.1', port: 0 },
60
+ bootstrapAttemptId: null, scopeId: 'isolated-usage-smoke', setupCompleteAt: new Date().toISOString(),
61
+ ownerDisplayName: 'Test', ownerAvatarColor: null, ownerAvatarUrl: null, lastProductSurface: null, guideCat: null,
62
+ };
63
+ let offline = false; let epoch = 'fixture-epoch'; let stale = false; let reads = 0;
64
+ const queries: string[] = [];
65
+ const timestamp = () => new Date().toISOString();
66
+ const totals = (totalTokens: number | null, currency = 'USD') => ({ observations: totalTokens === null ? 0 : 1,
67
+ inputTokens: totalTokens === null ? null : Math.floor(totalTokens * 0.8), outputTokens: totalTokens === null ? null : Math.ceil(totalTokens * 0.2),
68
+ totalTokens, costs: totalTokens === null ? [] : [{ currency, amount: 0.12, observations: 1 }],
69
+ confidence: { reported: totalTokens === null ? 0 : 1, aggregated: 0, estimated: 0, unknown: 0 }, lastObservedAt: timestamp() });
70
+ const runtimeClient = { async getUsageSnapshot() {
71
+ reads++; if (offline) throw new Error('Fixture offline');
72
+ const observedAt = new Date(Date.now() - (stale ? 600_000 : 30_000)).toISOString();
73
+ const resetsAt = new Date(Date.now() + (stale ? -60_000 : 3_600_000)).toISOString();
74
+ const targets = ['claude', 'codex', 'copilot', 'antigravity', 'kiro'].map((provider, index) => {
75
+ const queried = queries.includes(provider);
76
+ const source = queried ? { claude: 'claude.get_usage', codex: 'codex.account/rateLimits/read',
77
+ copilot: 'copilot.account.getQuota', antigravity: 'antigravity.usage' }[provider]
78
+ : index === 0 ? 'claude.rate_limit_event' : index === 1 ? 'codex.account/rateLimits/updated' : null;
79
+ const windows = queried && provider === 'copilot' ? [
80
+ { id: 'premium_interactions', unit: 'requests', used: 0, limit: 1500, remaining: 1500, usedPercent: 0, resetsAt },
81
+ { id: 'chat', unit: 'requests', used: 0, unlimited: true, resetsAt },
82
+ ] : queried ? [{ id: provider === 'antigravity' ? 'gemini_models_weekly' : 'seven_day', unit: 'percent',
83
+ usedPercent: provider === 'claude' ? 6 : 22, windowMinutes: 10080, resetsAt }]
84
+ : index < 2 ? [{ id: index ? 'primary' : 'five_hour', unit: 'percent', usedPercent: index ? 60 : 25, resetsAt, windowMinutes: 300 }] : [];
85
+ return { provider, instance: 'default', backend: 'cli', usage: totals(index >= 2 ? null : 12500 + index * 1000, index ? 'EUR' : 'USD'), guardrails: [],
86
+ quota: { status: index === 4 ? 'unsupported' : windows.length ? 'available' : 'unavailable', freshness: stale ? 'stale' : 'fresh',
87
+ source, observedAt: windows.length ? observedAt : null, accountId: null, accountLinkage: 'unverified',
88
+ automaticRefresh: false, refreshSupported: index < 4, windows } };
89
+ });
90
+ return { schemaVersion: 1, generatedAt: timestamp(), runtime: { status: 'available', epoch },
91
+ coverage: { mode: 'memory', scope: 'runtime_observed_results', startedAt: observedAt, retainedRecords: 2, droppedRecords: 0, droppedQuotaTargets: 0, truncated: false, historyAvailable: false },
92
+ totals: { ...totals(26000), costs: [{ currency: 'USD', amount: 0.12 }, { currency: 'EUR', amount: 0.12 }] }, targets,
93
+ sessions: targets.slice(0, 2).map((target, index) => ({ ...target, sessionId: `fixture-session-${index + 1}` })), incidents: [], guardrails: [] };
94
+ }, async refreshUsageQuota(target: { provider: string; instance: string }) {
95
+ assert.equal(target.instance, 'default'); assert.ok(['codex', 'copilot', 'claude', 'antigravity'].includes(target.provider));
96
+ queries.push(target.provider);
97
+ return { status: 'updated', nextRefreshAt: null, snapshot: await this.getUsageSnapshot() };
98
+ } };
99
+ const sessionSecret = 'isolated-usage-smoke-session-secret';
100
+ const auth = await createFirstAdminLocalAuthState({ state: createEmptyPlatformAuthState(), displayName: 'Test',
101
+ identifier: 'test@example.test', password: 'isolated-fixture-password', sessionSecret, sessionTtlMs: 600_000 });
102
+ const core = createDefaultCoreState(); core.setupCompleteAt = new Date().toISOString();
103
+ const dependencies = { shared: {
104
+ config: loadConfig({ CATS_HOME_DIR: root, CATS_PLATFORM_DIR: root, CATS_CHAT_STATE_PATH: chatStatePath,
105
+ CATS_AUTH_SESSION_SECRET: sessionSecret }),
106
+ coreStore: new MemoryCoreStore(core), authStore: new MemoryPlatformAuthStore(auth.state),
107
+ runtimeClient: { getUsageSnapshot: async () => projectUsageSnapshot(await runtimeClient.getUsageSnapshot()),
108
+ refreshUsageQuota: async (target: { provider: string; instance: string }) => projectUsageQuotaRefresh(await runtimeClient.refreshUsageQuota(target)) },
109
+ }, chat: {}, work: {}, code: {} } as unknown as ResolvedServerDependencies;
110
+ // Use the same temp path as the package installer regardless of config environment aliases.
111
+ dependencies.shared.config.chatStatePath = chatStatePath;
112
+ const server = createServer(async (request, response) => {
113
+ try {
114
+ const url = new URL(request.url ?? '/', 'http://127.0.0.1');
115
+ if (rendererRoot && url.pathname === '/api/app-shell') {
116
+ response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify(hostEnvelope)); return;
117
+ }
118
+ if (url.pathname === '/smoke.js') { response.writeHead(200, { 'content-type': 'text/javascript' }); response.end(js); return; }
119
+ if (url.pathname.startsWith('/api/')) {
120
+ if (url.pathname.startsWith('/api/apps/') || url.pathname === '/api/auth/status') { await routeRequest(request, response, dependencies); return; }
121
+ response.writeHead(404); response.end(); return;
122
+ }
123
+ if (rendererRoot) {
124
+ const asset = url.pathname.startsWith('/assets/') ? url.pathname.slice(1) : 'index.html';
125
+ const assetPath = path.resolve(rendererRoot, asset);
126
+ if (!assetPath.startsWith(`${rendererRoot}${path.sep}`)) { response.writeHead(404); response.end(); return; }
127
+ const contentType = asset.endsWith('.js') ? 'text/javascript' : asset.endsWith('.css') ? 'text/css' : 'text/html';
128
+ response.writeHead(200, { 'content-type': contentType, 'cache-control': 'no-store' });
129
+ response.end(await readFile(assetPath)); return;
130
+ }
131
+ response.writeHead(200, { 'content-type': 'text/html', 'cache-control': 'no-store' });
132
+ response.end('<!doctype html><html><head><meta name="viewport" content="width=device-width, initial-scale=1"></head><body style="margin:0;background:#f4f5f2"><div id="root"></div><script type="module" src="/smoke.js"></script></body></html>');
133
+ } catch { response.writeHead(500); response.end('Fixture failure'); }
134
+ });
135
+ await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
136
+ const address = server.address();
137
+ if (!address || typeof address === 'string') throw new Error('No fixture port');
138
+ const { chromium, _electron } = await import(pathToFileURL(process.env.CATS_TEST_PLAYWRIGHT_MODULE).href);
139
+ const electronMode = args.includes('--electron');
140
+ await mkdir(path.join(root, 'electron-profile'), { recursive: true });
141
+ const browser = electronMode
142
+ ? await _electron.launch({ executablePath: process.env.CATS_TEST_BROWSER_EXECUTABLE,
143
+ env: { ...process.env, CATS_USAGE_SMOKE_PROFILE_DIR: path.join(root, 'electron-profile') },
144
+ args: [fileURLToPath(new URL('./usage-app-smoke-electron.cjs', import.meta.url))] })
145
+ : await chromium.launch({ executablePath: process.env.CATS_TEST_BROWSER_EXECUTABLE, headless: true });
146
+ try {
147
+ const page = electronMode ? await browser.firstWindow()
148
+ : await browser.newPage({ viewport: { width: 1440, height: 1200 }, locale: 'zh-TW' });
149
+ await page.context().addCookies([{ name: 'cats_session', value: auth.session.token, url: `http://127.0.0.1:${address.port}` }]);
150
+ const errors: string[] = []; page.on('pageerror', (error: Error) => errors.push(error.message));
151
+ await page.goto(`http://127.0.0.1:${address.port}${entryPath}`);
152
+ const frame = page.frameLocator('iframe[title="Usage"]');
153
+ try { await frame.getByText('75% 剩餘', { exact: true }).waitFor({ timeout: 15_000 }); }
154
+ catch (error) {
155
+ process.stderr.write(`${JSON.stringify({ errors, body: await page.locator('body').innerText(), secure: await page.evaluate(() => isSecureContext) })}\n`);
156
+ throw error;
157
+ }
158
+ assert.equal(await page.locator('iframe').getAttribute('sandbox'), 'allow-scripts');
159
+ process.stdout.write('Usage iframe rendered and received the fixture snapshot.\n');
160
+ assert.equal(await frame.getByText('尚未支援', { exact: true }).count(), 1);
161
+ assert.equal(queries.length, 0, 'Opening the App never queries a CLI');
162
+ assert.equal(await frame.locator('[data-query-provider="kiro"]').count(), 0);
163
+ for (const provider of ['copilot', 'claude', 'antigravity']) {
164
+ await new Promise((resolve) => setTimeout(resolve, 1100));
165
+ await frame.locator(`[data-query-provider="${provider}"]`).click();
166
+ try { await frame.getByText(/已透過 .* CLI 取得額度回報/).first().waitFor({ timeout: 5000 }); }
167
+ catch (error) {
168
+ process.stderr.write(`${JSON.stringify({ queries, provider, body: await page.locator('body').innerText(),
169
+ frame: await frame.locator('body').innerText({ timeout: 1000 }).catch(() => 'Frame unavailable'), errors })}\n`);
170
+ throw error;
171
+ }
172
+ const expected = provider === 'copilot' ? /1,500 requests/ : provider === 'claude' ? /^94% 剩餘$/ : /^78% 剩餘$/;
173
+ await frame.getByText(expected).first().waitFor();
174
+ }
175
+ assert.deepEqual(queries, ['copilot', 'claude', 'antigravity']);
176
+ await frame.getByText('不限額', { exact: true }).waitFor();
177
+ assert.equal(await frame.locator('.stat .cost').innerText(), 'USD 0.12\nEUR 0.12');
178
+ const innerFrame = page.frames().find((candidate: { parentFrame(): unknown }) => candidate.parentFrame() !== null);
179
+ assert.ok(innerFrame);
180
+ await innerFrame.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))));
181
+ const frameBounds = await page.locator('iframe').boundingBox();
182
+ assert.ok(frameBounds && frameBounds.width >= 800 && frameBounds.height >= 520, 'Dashboard has a usable desktop viewport');
183
+ const screenshotDir = path.resolve('build', 'usage-smoke'); await mkdir(screenshotDir, { recursive: true });
184
+ if (!electronMode) {
185
+ // Viewport/frame captures keep the opaque iframe's paint surface intact.
186
+ // Chromium full-page resizing can mispaint that surface even with correct DOM bounds.
187
+ await page.screenshot({ path: path.join(screenshotDir, 'usage-desktop.png') });
188
+ await page.locator('iframe').screenshot({ path: path.join(screenshotDir, 'usage-frame.png') });
189
+ }
190
+ await frame.locator('#provider').selectOption('copilot');
191
+ assert.equal(await frame.locator('.stat').first().locator('strong').innerText(), '—');
192
+ await frame.locator('#provider').selectOption('claude');
193
+ assert.equal(await frame.locator('.provider').count(), 1);
194
+ assert.equal(await frame.locator('tbody tr').count(), 1);
195
+ await frame.locator('#provider').selectOption('');
196
+ const refresh = async () => {
197
+ // The real host deliberately permits at most one UI request per second.
198
+ await new Promise((resolve) => setTimeout(resolve, 1100));
199
+ const before = reads;
200
+ await frame.getByRole('button', { name: '重新整理', exact: true }).click();
201
+ await page.waitForFunction(() => document.querySelector('iframe') !== null);
202
+ for (let attempt = 0; reads === before && attempt < 30; attempt++) await new Promise((resolve) => setTimeout(resolve, 50));
203
+ assert.ok(reads > before, 'Refresh reached the real host route');
204
+ };
205
+ offline = true; await refresh();
206
+ await frame.getByText('Runtime 無法連線 · 以下保留最後快照', { exact: true }).waitFor();
207
+ assert.equal(await frame.getByText('94% 剩餘', { exact: true }).count(), 1);
208
+ offline = false; stale = true; await refresh();
209
+ await frame.getByText('已過回報的重設時間;目前沒有可靠的未來重設時間', { exact: true }).first().waitFor();
210
+ assert.equal(await frame.getByText('94% 剩餘', { exact: true }).count(), 1);
211
+ epoch = 'fixture-restarted'; await refresh();
212
+ await frame.getByText('Runtime 已重新啟動,本次用量觀測區間已重設。', { exact: true }).waitFor();
213
+ await page.setViewportSize({ width: 390, height: 844 });
214
+ if (!electronMode) await page.screenshot({ path: path.join(screenshotDir, 'usage-mobile.png'), fullPage: true });
215
+ assert.equal(await innerFrame.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth), true);
216
+ assert.equal(await innerFrame.evaluate(() => { try { return !!parent.document.body; } catch { return false; } }), false);
217
+ assert.equal(await innerFrame.evaluate(async () => { try { await fetch('/api/apps'); return true; } catch { return false; } }), false);
218
+ await registry.updateAppState('cats.usage', { installState: 'disabled' });
219
+ await new Promise((resolve) => setTimeout(resolve, 1100));
220
+ await frame.getByRole('button', { name: '重新整理', exact: true }).click();
221
+ await page.getByRole('alert').waitFor();
222
+ assert.equal(await page.locator('iframe').count(), 0);
223
+ await registry.updateAppState('cats.usage', { installState: 'enabled' });
224
+ offline = false; stale = false;
225
+ if (rendererRoot) {
226
+ await page.getByRole('button', { name: '返回大廳', exact: true }).click();
227
+ await page.getByRole('button', { name: /Usage/ }).click();
228
+ await page.frameLocator('iframe[title="Usage"]').getByText('94% 剩餘', { exact: true }).waitFor();
229
+ }
230
+ if (args.includes('--check-loading-recovery')) {
231
+ await page.clock.install();
232
+ const rendererPattern = '**/api/apps/cats.usage/renderer?*';
233
+ let releaseRequest!: () => void;
234
+ await page.route(rendererPattern, async (route: any) => {
235
+ await new Promise<void>((resolve) => { releaseRequest = resolve; });
236
+ await route.abort().catch(() => {}); // The host timeout may already have aborted it.
237
+ });
238
+ await page.goto(`http://127.0.0.1:${address.port}${entryPath}`);
239
+ await page.getByRole('status').filter({ hasText: '載入 Usage' }).waitFor();
240
+ await page.clock.fastForward(15_001);
241
+ await page.getByRole('alert').filter({ hasText: '載入逾時' }).waitFor();
242
+ await page.screenshot({ path: path.join(screenshotDir, 'usage-loading-timeout.png') });
243
+ await page.unroute(rendererPattern);
244
+ releaseRequest();
245
+ await page.getByRole('button', { name: '重新載入', exact: true }).click();
246
+ await page.frameLocator('iframe[title="Usage"]').getByText('94% 剩餘', { exact: true }).waitFor();
247
+ await page.route(rendererPattern, async (route: any) => {
248
+ const response = await route.fetch();
249
+ await route.fulfill({ response, json: { ...await response.json(), sdk: '' } });
250
+ });
251
+ await page.goto(`http://127.0.0.1:${address.port}${entryPath}`);
252
+ await page.locator('iframe[title="Usage"]').waitFor();
253
+ await page.clock.fastForward(15_001);
254
+ await page.getByRole('alert').filter({ hasText: '載入逾時' }).waitFor();
255
+ assert.equal(await page.locator('iframe').count(), 0);
256
+ await page.unroute(rendererPattern);
257
+ await page.getByRole('button', { name: '重新載入', exact: true }).click();
258
+ await page.frameLocator('iframe[title="Usage"]').getByText('94% 剩餘', { exact: true }).waitFor();
259
+ }
260
+ assert.deepEqual(errors, []);
261
+ process.stdout.write(`${JSON.stringify({ ok: true, reads, queries, rendererRoot, electronMode, loadingRecovery: args.includes('--check-loading-recovery'),
262
+ app: { id: usage.id, version: usage.version, sha256: usage.sha256 }, screenshotDir: electronMode ? null : screenshotDir, isolatedRegistry: root }, null, 2)}\n`);
263
+ } finally {
264
+ await browser.close();
265
+ await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
266
+ }
@@ -0,0 +1,134 @@
1
+ // Usage: invoked by conversation-navigation-smoke.mjs; never run against real state.
2
+ // CATS_NAV_SMOKE_URL selects the fixture server, PROFILE isolates Electron state,
3
+ // and OUTPUT selects the screenshot/result directory. No Desktop supervisor loads.
4
+ const { app, BrowserWindow } = require('electron');
5
+ const { writeFile } = require('node:fs/promises');
6
+ const path = require('node:path');
7
+ app.setPath('userData', process.env.CATS_NAV_SMOKE_PROFILE);
8
+ app.whenReady().then(async () => {
9
+ const window = new BrowserWindow({ width: 1440, height: 1000, show: false,
10
+ webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false, backgroundThrottling: false, offscreen: true } });
11
+ window.webContents.setFrameRate(60);
12
+ const errors = [];
13
+ window.webContents.on('console-message', (event) => { if (event.level === 'error') errors.push(event.message); });
14
+ try {
15
+ await window.loadURL(process.env.CATS_NAV_SMOKE_URL);
16
+ const result = await window.webContents.executeJavaScript(`(async () => {
17
+ const wait = async (predicate, label) => {
18
+ const started = performance.now();
19
+ while (!predicate()) {
20
+ if (performance.now() - started > 10000) throw new Error('Timeout: ' + label + '\\n' + document.body.innerText);
21
+ await new Promise(resolve => setTimeout(resolve, 10));
22
+ }
23
+ await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
24
+ };
25
+ await wait(() => document.body.innerText.includes('Transcript 1'), 'initial Chat render');
26
+ const fixture = window.__navigationSmoke;
27
+ const click = (title) => {
28
+ const row = [...document.querySelectorAll('button,a,[role="button"],span')].find(el => el.textContent.trim() === title);
29
+ if (!row) throw new Error('Missing navigation control: ' + title);
30
+ row.click();
31
+ };
32
+ fixture.click = click;
33
+ fixture.wait = wait;
34
+ fixture.delays.shell = 60000;
35
+ fixture.delays.preference = 2000;
36
+ const timings = [];
37
+ const checks = [];
38
+ for (const index of [1, 2, 3]) {
39
+ const start = performance.now(); click('Conversation ' + (index + 1));
40
+ await wait(() => document.body.innerText.includes('Transcript ' + (index + 1)), 'cold conversation ' + index);
41
+ timings.push({ kind: 'cold', conversation: index + 1, ms: performance.now() - start });
42
+ }
43
+ fixture.delays.snapshot = 2000;
44
+ for (const index of [0, 2, 1, 3, 0]) {
45
+ const start = performance.now(); click('Conversation ' + (index + 1));
46
+ await wait(() => document.body.innerText.includes('Transcript ' + (index + 1)), 'warm conversation ' + index);
47
+ const ms = performance.now() - start;
48
+ if (ms >= 1500) throw new Error('Warm navigation waited for a delayed response: ' + ms);
49
+ timings.push({ kind: 'warm', conversation: index + 1, ms });
50
+ }
51
+ fixture.pushReply(fixture.ids[0], 'Background reply received');
52
+ await wait(() => document.body.innerText.includes('Background reply received'), 'subscription update');
53
+ click('Conversation 2');
54
+ await wait(() => document.body.innerText.includes('Transcript 2'), 'leave updated conversation');
55
+ click('Conversation 1');
56
+ await wait(() => document.body.innerText.includes('Background reply received'), 'retained updated transcript');
57
+ const type = (value) => {
58
+ const input = document.querySelector('textarea');
59
+ if (!input) throw new Error('Missing composer textarea');
60
+ Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value').set.call(input, value);
61
+ input.dispatchEvent(new Event('input', { bubbles: true }));
62
+ };
63
+ type('Keep conversation one draft');
64
+ await wait(() => document.querySelector('textarea')?.value === 'Keep conversation one draft', 'composer edit');
65
+ click('New Chat');
66
+ await wait(() => location.pathname.endsWith('/new'), 'new chat');
67
+ if (document.querySelector('textarea')?.value) throw new Error('New chat inherited conversation draft');
68
+ click('Conversation 1');
69
+ await wait(() => document.querySelector('textarea')?.value === 'Keep conversation one draft', 'restore conversation draft after New Chat');
70
+ checks.push('cold/warm A/B/C/D navigation', 'background update retention', 'conversation draft after New Chat');
71
+ fixture.delays.snapshot = 20;
72
+ for (const serverClose of [false, true]) {
73
+ const body = serverClose ? 'Recovered server read failure' : 'Recovered disconnected stream';
74
+ fixture.disconnect(fixture.ids[0], serverClose);
75
+ if (!document.body.innerText.includes('Transcript 1')) throw new Error('Disconnect cleared retained transcript');
76
+ fixture.pushReply(fixture.ids[0], body, false);
77
+ await wait(() => document.body.innerText.includes(body), body);
78
+ }
79
+ checks.push('network and server-error reconnection');
80
+ click('Group Chat');
81
+ await wait(() => location.pathname.includes('/new'), 'group draft');
82
+ checks.push({ mode: 'group draft', path: location.pathname, text: document.body.innerText });
83
+ click('Parallel Chat');
84
+ await wait(() => location.pathname.includes('/new'), 'parallel draft');
85
+ checks.push({ mode: 'parallel draft', path: location.pathname, text: document.body.innerText });
86
+ click('group fixture');
87
+ await wait(() => document.body.innerText.includes('group transcript'), 'group room');
88
+ checks.push({ mode: 'group room', text: document.body.innerText });
89
+ click('Fixture Cat Two');
90
+ await wait(() => document.body.innerText.includes('direct transcript'), 'direct lane');
91
+ checks.push({ mode: 'direct lane', text: document.body.innerText });
92
+ click('Conversation 1');
93
+ await wait(() => document.querySelector('textarea')?.value === 'Keep conversation one draft', 'draft after other chat modes');
94
+ return { timings, checks, calls: fixture.calls, text: document.body.innerText };
95
+ })()`);
96
+ result.errors = errors;
97
+ await writeFile(path.join(process.env.CATS_NAV_SMOKE_OUTPUT, 'result.json'), JSON.stringify(result, null, 2));
98
+ await writeFile(path.join(process.env.CATS_NAV_SMOKE_OUTPUT, 'chat.png'), (await window.webContents.capturePage()).toPNG());
99
+ for (const [control, marker, name] of [
100
+ ['Group Chat', 'Add another model to collaborate', 'group-draft'],
101
+ ['Parallel Chat', 'Follows lead', 'parallel-draft'],
102
+ ['group fixture', 'group transcript', 'group-room'],
103
+ ['Fixture Cat Two', 'direct transcript', 'direct-lane'],
104
+ ]) {
105
+ await window.webContents.executeJavaScript(`(async () => {
106
+ const fixture = window.__navigationSmoke;
107
+ fixture.click(${JSON.stringify(control)});
108
+ await fixture.wait(() => document.body.innerText.includes(${JSON.stringify(marker)}), ${JSON.stringify(name)});
109
+ })()`);
110
+ await writeFile(path.join(process.env.CATS_NAV_SMOKE_OUTPUT, `${name}.png`), (await window.webContents.capturePage()).toPNG());
111
+ }
112
+ await window.webContents.executeJavaScript(`(async () => {
113
+ document.querySelector('.recentOverflowButton').click();
114
+ await window.__navigationSmoke.wait(() => document.querySelector('.recentOverflowMenu'), 'sidebar overflow');
115
+ const menu = document.querySelector('.recentOverflowMenu');
116
+ const bounds = menu.getBoundingClientRect();
117
+ const topElement = document.elementFromPoint(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
118
+ if (!menu.contains(topElement)) throw new Error('Sidebar overflow is obscured');
119
+ })()`);
120
+ await writeFile(path.join(process.env.CATS_NAV_SMOKE_OUTPUT, 'sidebar-overflow.png'), (await window.webContents.capturePage()).toPNG());
121
+ if (errors.length) throw new Error(errors.join('\n'));
122
+ window.webContents.stopPainting();
123
+ window.destroy();
124
+ app.quit();
125
+ } catch (error) {
126
+ await writeFile(path.join(process.env.CATS_NAV_SMOKE_OUTPUT, 'failure.png'), (await window.webContents.capturePage()).toPNG());
127
+ console.error(error);
128
+ console.error(errors.join('\n'));
129
+ window.webContents.stopPainting();
130
+ window.destroy();
131
+ process.exitCode = 1;
132
+ app.quit();
133
+ }
134
+ });
@@ -0,0 +1,102 @@
1
+ // Isolated production Chat renderer. All services below use synthetic fixture data.
2
+ import React from 'react';
3
+ import { createRoot } from 'react-dom/client';
4
+ import { BrowserRouter, Route, Routes } from 'react-router-dom';
5
+ import { QueryClientProvider } from '@tanstack/react-query';
6
+ import { GuideCatPlacementProvider } from '../../src/app/renderer/GuideCatPlacementProvider.js';
7
+ import { sharedQueryClient } from '../../src/products/shared/renderer/queryClient.js';
8
+ import type { AppShellPayload } from '../../src/products/shared/api/workspaceContracts.js';
9
+ import type { ChannelSubscriptionState } from '../../src/products/shared/renderer/entitySubscriptionChannelDispatcher.js';
10
+ import '../../src/design/index.css';
11
+
12
+ const fixture = await (await fetch('/fixture.json')).json();
13
+ const ids: string[] = fixture.ids;
14
+ const payloads = fixture.payloads as Record<string, AppShellPayload>;
15
+ const snapshots = fixture.snapshots as Record<string, ChannelSubscriptionState>;
16
+ const calls: Array<{ url: string; method: string; at: number }> = [];
17
+ const sources = new Set<FixtureEventSource>();
18
+ const delays = { shell: 0, preference: 2_000, snapshot: 20 };
19
+ let selected = ids[0];
20
+ let shellCalls = 0;
21
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
22
+
23
+ class FixtureEventSource extends EventTarget {
24
+ onerror: (() => void) | null = null;
25
+ closed = false;
26
+ constructor(readonly url: string) {
27
+ super(); sources.add(this);
28
+ calls.push({ url, method: 'SSE', at: performance.now() });
29
+ const params = new URL(url, location.origin).searchParams;
30
+ const id = params.get('id');
31
+ if (params.get('kind') === 'channel' && id) {
32
+ void sleep(delays.snapshot).then(() => {
33
+ if (!this.closed) this.emit('snapshot', { kind: 'channel', id, version: 1, state: snapshots[id] });
34
+ });
35
+ }
36
+ }
37
+ emit(type: string, data: unknown) { this.dispatchEvent(new MessageEvent(type, { data: JSON.stringify(data) })); }
38
+ close() { this.closed = true; sources.delete(this); }
39
+ }
40
+ Object.defineProperty(globalThis, 'EventSource', { value: FixtureEventSource, configurable: true });
41
+ globalThis.fetch = async (input, init) => {
42
+ const url = String(input); calls.push({ url, method: init?.method ?? 'GET', at: performance.now() });
43
+ if (url === '/api/app-shell') {
44
+ if (shellCalls++ > 0) await sleep(delays.shell);
45
+ return Response.json(payloads[selected]);
46
+ }
47
+ if (url === '/api/preferences') {
48
+ await sleep(delays.preference);
49
+ selected = JSON.parse(String(init?.body)).selectedChannelId ?? selected;
50
+ return Response.json({ preferences: { selectedChannelId: selected } });
51
+ }
52
+ if (url === '/api/core') return Response.json(fixture.core);
53
+ if (url === '/api/core/approvals') return Response.json({ approvals: [] });
54
+ if (url === '/api/providers') return Response.json({ state: 'ready', warnings: [], providers: ids.map((id, index) => {
55
+ const channel = snapshots[id].selectedChannel;
56
+ return { id: channel.pendingProvider, label: channel.pendingProvider,
57
+ defaultModel: channel.pendingModel, defaultInstance: 'native', defaultBackend: 'cli',
58
+ instances: [{ id: 'native', label: 'Native', target: 'cli/native', backend: 'cli', default: true }],
59
+ modelsPath: `/api/providers/${channel.pendingProvider}/models` };
60
+ }) });
61
+ const provider = /^\/api\/providers\/([^/]+)\/models/u.exec(url)?.[1];
62
+ if (provider) {
63
+ const channel = Object.values(snapshots).find((snapshot) => snapshot.selectedChannel.pendingProvider === provider)!.selectedChannel;
64
+ const model = { id: channel.pendingModel, label: channel.pendingModel, default: true };
65
+ const catalog = { provider, backend: 'cli', instance: 'cli/native', source: 'dynamic', cache: null,
66
+ defaultModel: channel.pendingModel, models: [model], entries: [model], presets: [], warnings: [],
67
+ defaultSelection: channel.pendingModelSelection, support: { tier: 'full', notes: [] },
68
+ controls: [{ key: `${provider}.reasoning_effort`, label: 'Reasoning effort', kind: 'enum', scope: 'both',
69
+ values: ['low', 'medium', 'high', 'max'].map((value) => ({ value, label: value })) }] };
70
+ return Response.json({ catalog });
71
+ }
72
+ if (/\/api\/channels\/[^/]+$/u.test(url) && init?.method === 'PATCH') {
73
+ const id = url.split('/').at(-1)!;
74
+ Object.assign(snapshots[id].selectedChannel, JSON.parse(String(init.body)));
75
+ return Response.json({ channel: payloads[id].chat.channels.find((channel) => channel.id === id) });
76
+ }
77
+ return Response.json({});
78
+ };
79
+ (window as any).__navigationSmoke = { ids, calls, delays, payloads, snapshots,
80
+ disconnect(id: string, serverClose = false) {
81
+ for (const source of sources) {
82
+ if (!source.url.includes(`id=${id}`)) continue;
83
+ if (serverClose) source.emit('close', { reason: 'Temporary fixture read failure', retryable: true });
84
+ else source.onerror?.();
85
+ }
86
+ },
87
+ pushReply(id: string, body: string, notify = true) {
88
+ const state = snapshots[id];
89
+ state.selectedChannel.messages.push({ ...state.selectedChannel.messages.at(-1)!, id: crypto.randomUUID(), body });
90
+ for (const source of sources) {
91
+ if (notify && source.url.includes(`id=${id}`)) source.emit('patch', { kind: 'channel', id, version: 1, patch: { kind: 'message.appended', state } });
92
+ }
93
+ } };
94
+ const { default: App } = await import('../../src/products/chat/renderer/App.js');
95
+ createRoot(document.getElementById('root')!).render(
96
+ <QueryClientProvider client={sharedQueryClient}><BrowserRouter useTransitions={false}>
97
+ <GuideCatPlacementProvider guideCat={null} placement="docked" floatingAnchor={null}
98
+ sidecarMode="auto" onPersistSeen={() => {}} onCommit={() => {}}>
99
+ <Routes><Route path="/chat/*" element={<App />} /></Routes>
100
+ </GuideCatPlacementProvider>
101
+ </BrowserRouter></QueryClientProvider>,
102
+ );
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Usage: node scripts/testing/conversation-navigation-smoke.mjs [--help]
3
+ * Builds an isolated production Chat renderer and verifies navigation in hidden
4
+ * Electron/Chromium. All records and services are synthetic; user data is untouched.
5
+ * Writes build/conversation-navigation-smoke/result.json and a screenshot.
6
+ */
7
+ import { build } from 'esbuild';
8
+ import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
9
+ import { createServer } from 'node:http';
10
+ import { spawn } from 'node:child_process';
11
+ import { once } from 'node:events';
12
+ import { tmpdir } from 'node:os';
13
+ import path from 'node:path';
14
+ import { pathToFileURL } from 'node:url';
15
+ import electron from 'electron';
16
+
17
+ if (process.argv.includes('--help')) {
18
+ console.log('Usage: node scripts/testing/conversation-navigation-smoke.mjs\nSynthetic Chat navigation verification in hidden Electron. Results: build/conversation-navigation-smoke/.');
19
+ process.exit(0);
20
+ }
21
+ const out = path.resolve('build/conversation-navigation-smoke');
22
+ const temporary = await mkdtemp(path.join(tmpdir(), 'cats-navigation-browser-'));
23
+ await mkdir(out, { recursive: true });
24
+ await build({ entryPoints: ['tests/fixtures/conversationNavigation.ts'], outfile: path.join(out, 'fixture.mjs'),
25
+ platform: 'node', format: 'esm', bundle: true, packages: 'external' });
26
+ await build({ entryPoints: ['scripts/testing/conversation-navigation-smoke-renderer.tsx'], outfile: path.join(out, 'renderer.js'),
27
+ platform: 'browser', format: 'esm', bundle: true, define: { 'process.env.NODE_ENV': '"production"' },
28
+ loader: { '.woff2': 'file', '.woff': 'file', '.png': 'file', '.svg': 'file' } });
29
+ const { createConversationNavigationFixture } = await import(pathToFileURL(path.join(out, 'fixture.mjs')));
30
+ const fixture = await createConversationNavigationFixture(temporary, true);
31
+ const allIds = fixture.state.channels.map((channel) => channel.id);
32
+ const body = JSON.stringify({ ids: fixture.ids, payloads: Object.fromEntries(allIds.map((id) => [id, fixture.payload(id)])),
33
+ snapshots: Object.fromEntries(allIds.map((id) => [id, fixture.snapshot(id)])), core: await fixture.store.readCore() });
34
+ const server = createServer(async (request, response) => {
35
+ try {
36
+ if (request.url === '/fixture.json') { response.setHeader('content-type', 'application/json'); response.end(body); return; }
37
+ const name = path.basename(new URL(request.url, 'http://localhost').pathname);
38
+ if (/\.(?:js|css|woff2?|png|svg)$/u.test(name)) {
39
+ response.setHeader('content-type', name.endsWith('.js') ? 'text/javascript' : name.endsWith('.css') ? 'text/css' : 'application/octet-stream');
40
+ response.end(await readFile(path.join(out, name))); return;
41
+ }
42
+ response.setHeader('content-type', 'text/html');
43
+ response.end('<!doctype html><html><head><link rel="stylesheet" href="/renderer.css"></head><body><div id="root"></div><script type="module" src="/renderer.js"></script></body></html>');
44
+ } catch (error) { response.writeHead(500); response.end(String(error)); }
45
+ });
46
+ server.listen(0, '127.0.0.1');
47
+ await once(server, 'listening');
48
+ const url = `http://127.0.0.1:${server.address().port}/chat/chats/${fixture.ids[0]}`;
49
+ const childEnv = { ...process.env, CATS_NAV_SMOKE_URL: url,
50
+ CATS_NAV_SMOKE_PROFILE: path.join(temporary, 'electron'), CATS_NAV_SMOKE_OUTPUT: out };
51
+ delete childEnv.ELECTRON_RUN_AS_NODE;
52
+ const child = spawn(electron, ['scripts/testing/conversation-navigation-smoke-electron.cjs'], {
53
+ windowsHide: true, stdio: 'inherit', env: childEnv,
54
+ });
55
+ try {
56
+ const [code] = await once(child, 'exit');
57
+ if (code !== 0) throw new Error(`Navigation browser verification exited ${code}`);
58
+ const result = JSON.parse(await readFile(path.join(out, 'result.json'), 'utf8'));
59
+ console.log(JSON.stringify({ timings: result.timings,
60
+ checks: result.checks.map((check) => typeof check === 'string' ? check : check.mode), errors: result.errors }, null, 2));
61
+ } finally {
62
+ server.closeAllConnections();
63
+ await new Promise((resolve) => server.close(resolve));
64
+ if (path.dirname(path.resolve(temporary)) !== path.resolve(tmpdir())
65
+ || !path.basename(temporary).startsWith('cats-navigation-browser-')) throw new Error('Unexpected smoke cleanup path');
66
+ await rm(temporary, { recursive: true, force: true });
67
+ }
@@ -0,0 +1,10 @@
1
+ // Isolated Electron fixture: no Desktop supervisor, user profile, or live services.
2
+ const { app, BrowserWindow } = require('electron');
3
+ app.setPath('userData', process.env.CATS_USAGE_SMOKE_PROFILE_DIR);
4
+ app.whenReady().then(async () => {
5
+ globalThis.testWindow = new BrowserWindow({ width: 1440, height: 1200, show: false,
6
+ webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true, backgroundThrottling: false },
7
+ });
8
+ await globalThis.testWindow.loadURL('about:blank');
9
+ });
10
+ app.on('window-all-closed', () => app.quit());
@@ -0,0 +1,8 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import { AppRendererSurface } from '../../src/app/renderer/AppRendererSurface.js';
3
+ import { installCsrfFetch } from '../../src/app/renderer/csrfFetch.js';
4
+
5
+ installCsrfFetch();
6
+ createRoot(document.getElementById('root')!).render(<AppRendererSurface appId="cats.usage" version={new URLSearchParams(location.search).get('appVersion') ?? ''}
7
+ title="Usage" locale={new URLSearchParams(location.search).get('locale') ?? 'zh-TW'}
8
+ onLobby={() => { document.body.textContent = 'Returned to lobby'; }} />);
@@ -34,7 +34,10 @@ const PROJECT_ROOT = resolve(dirname(SCRIPT_PATH), '..');
34
34
  */
35
35
  export const DESKTOP_RELEASE_MATRIX = [
36
36
  { platform: 'windows', formats: ['nsis'], arches: ['x64'] },
37
- // macOS ships x64 only; Apple Silicon runs it under Rosetta 2.
37
+ // macOS ships x64 only: there is no macOS arm64 test environment, so a
38
+ // universal build would ship a slice nobody executed before release. Apple
39
+ // Silicon runs this under Rosetta 2. SPEC-111 section 8 records the exit
40
+ // condition for revisiting it.
38
41
  { platform: 'macos', formats: ['dmg', 'zip'], arches: ['x64'] },
39
42
  // Linux ships an arm64 .deb. electron-updater installs it with dpkg, which
40
43
  // needs elevation -- unlike the per-user Windows path.