@wopr-network/platform-ui-core 1.27.8 → 1.27.9

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 (353) hide show
  1. package/next.config.ts +1 -2
  2. package/package.json +17 -17
  3. package/src/__tests__/account-switcher.test.tsx +21 -20
  4. package/src/__tests__/activity-page.test.tsx +2 -6
  5. package/src/__tests__/add-payment-method-dialog.test.tsx +9 -32
  6. package/src/__tests__/admin-api.test.ts +1 -6
  7. package/src/__tests__/admin-gpu-api.test.ts +1 -3
  8. package/src/__tests__/admin-marketplace-api.test.ts +1 -4
  9. package/src/__tests__/admin-middleware.test.ts +76 -83
  10. package/src/__tests__/affiliate-dashboard.test.tsx +3 -3
  11. package/src/__tests__/api-401-redirect.test.ts +46 -9
  12. package/src/__tests__/api-client.test.ts +3 -5
  13. package/src/__tests__/api-config.test.ts +22 -42
  14. package/src/__tests__/api-fleet-resources.test.ts +1 -2
  15. package/src/__tests__/api-fleet-trpc.test.ts +2 -8
  16. package/src/__tests__/api-null-guards.test.ts +3 -1
  17. package/src/__tests__/audit-log-table-pagination.test.tsx +2 -6
  18. package/src/__tests__/auth-password-reset.test.tsx +7 -21
  19. package/src/__tests__/auth-redirect.test.tsx +8 -2
  20. package/src/__tests__/auth.test.tsx +25 -23
  21. package/src/__tests__/auto-topup-card.test.tsx +4 -12
  22. package/src/__tests__/backups-tab.test.tsx +3 -4
  23. package/src/__tests__/billing-layout-nav-hidden.test.tsx +5 -37
  24. package/src/__tests__/billing-payment-org-invoices.test.tsx +2 -18
  25. package/src/__tests__/billing.test.tsx +8 -39
  26. package/src/__tests__/bot-settings/resources-tab.test.tsx +1 -3
  27. package/src/__tests__/bot-settings/storage-tab.test.tsx +1 -3
  28. package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +1 -3
  29. package/src/__tests__/bot-settings-restart.test.tsx +1 -3
  30. package/src/__tests__/bot-settings.test.tsx +2 -6
  31. package/src/__tests__/brand.test.ts +6 -26
  32. package/src/__tests__/buy-credits-panel.test.tsx +1 -3
  33. package/src/__tests__/buy-crypto-credits-panel.test.tsx +101 -119
  34. package/src/__tests__/capability-conflicts.test.ts +2 -8
  35. package/src/__tests__/capability-resolver.test.tsx +2 -12
  36. package/src/__tests__/channel-wizard.test.tsx +4 -17
  37. package/src/__tests__/chat/chat-panel.test.tsx +1 -4
  38. package/src/__tests__/chat-store.test.ts +5 -15
  39. package/src/__tests__/command-center.test.tsx +10 -12
  40. package/src/__tests__/compliance-retention-edit.test.tsx +3 -6
  41. package/src/__tests__/confirmation-tracker.test.tsx +3 -18
  42. package/src/__tests__/coupon-input.test.tsx +1 -3
  43. package/src/__tests__/create-instance.test.tsx +1 -3
  44. package/src/__tests__/credit-balance.test.tsx +4 -12
  45. package/src/__tests__/credits.test.tsx +32 -85
  46. package/src/__tests__/email-verification-banner.test.tsx +2 -6
  47. package/src/__tests__/error-boundaries.test.tsx +0 -1
  48. package/src/__tests__/fetch-pricing.test.ts +2 -1
  49. package/src/__tests__/field-oauth.test.tsx +2 -6
  50. package/src/__tests__/fixtures/mock-manifests-data.js +1 -3
  51. package/src/__tests__/fixtures/mock-manifests.ts +2 -4
  52. package/src/__tests__/fleet-health-timestamp.test.tsx +1 -8
  53. package/src/__tests__/fleet-health-update.test.tsx +1 -8
  54. package/src/__tests__/gpu-dashboard.test.tsx +2 -6
  55. package/src/__tests__/instance-detail.test.tsx +3 -9
  56. package/src/__tests__/instance-list.test.tsx +1 -5
  57. package/src/__tests__/layout-snapshots.test.tsx +64 -11
  58. package/src/__tests__/marketplace-admin.test.tsx +2 -6
  59. package/src/__tests__/marketplace.test.tsx +11 -35
  60. package/src/__tests__/merge-api-rates.test.ts +1 -6
  61. package/src/__tests__/middleware.test.ts +32 -219
  62. package/src/__tests__/next-config-headers.test.ts +1 -3
  63. package/src/__tests__/notifications.test.tsx +4 -11
  64. package/src/__tests__/oauth-buttons.test.tsx +36 -59
  65. package/src/__tests__/oauth-error-mapping.test.tsx +2 -6
  66. package/src/__tests__/observability.test.tsx +23 -36
  67. package/src/__tests__/onboarding-page.test.tsx +4 -6
  68. package/src/__tests__/org-billing-api.test.tsx +1 -6
  69. package/src/__tests__/plugin-install-flow.test.tsx +28 -58
  70. package/src/__tests__/plugin-registry.test.tsx +3 -11
  71. package/src/__tests__/plugin-tool-sync.test.ts +1 -3
  72. package/src/__tests__/plugins-catalog-error.test.tsx +2 -6
  73. package/src/__tests__/plugins-toggle-race.test.tsx +3 -5
  74. package/src/__tests__/portfolio-chart.test.tsx +2 -6
  75. package/src/__tests__/promotion-form.test.tsx +2 -6
  76. package/src/__tests__/promotions-list.test.tsx +1 -3
  77. package/src/__tests__/provider-key-api.test.ts +2 -1
  78. package/src/__tests__/resend-verification-button.test.tsx +8 -24
  79. package/src/__tests__/secrets-audit-pagination.test.tsx +1 -3
  80. package/src/__tests__/settings.test.tsx +11 -21
  81. package/src/__tests__/setup-checklist.test.tsx +3 -9
  82. package/src/__tests__/setup.ts +25 -6
  83. package/src/__tests__/snapshot-api.test.ts +2 -1
  84. package/src/__tests__/step-superpowers.test.tsx +1 -3
  85. package/src/__tests__/tenant-context.test.tsx +1 -6
  86. package/src/__tests__/tenant-keys-api.test.ts +3 -4
  87. package/src/__tests__/tenant-table-pagination.test.tsx +2 -6
  88. package/src/__tests__/terminal-log-cleanup.test.tsx +0 -1
  89. package/src/__tests__/transaction-history.test.tsx +190 -238
  90. package/src/__tests__/trpc-types.test.ts +2 -6
  91. package/src/__tests__/use-chat.test.ts +1 -3
  92. package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +1 -4
  93. package/src/__tests__/use-sidecar-bridge.test.tsx +105 -0
  94. package/src/__tests__/use-webmcp.test.ts +1 -3
  95. package/src/__tests__/validate-elevenlabs-key.test.ts +2 -1
  96. package/src/__tests__/verify-page.test.tsx +4 -13
  97. package/src/__tests__/verify-redirect.test.tsx +2 -6
  98. package/src/app/(auth)/error.tsx +1 -7
  99. package/src/app/(auth)/forgot-password/page.tsx +4 -18
  100. package/src/app/(auth)/login/page.tsx +5 -22
  101. package/src/app/(auth)/reset-password/page.tsx +2 -12
  102. package/src/app/(auth)/signup/page.tsx +10 -44
  103. package/src/app/(auth)/verify/page.tsx +47 -0
  104. package/src/app/(dashboard)/billing/credits/page.tsx +14 -67
  105. package/src/app/(dashboard)/billing/error.tsx +2 -10
  106. package/src/app/(dashboard)/billing/layout.tsx +12 -62
  107. package/src/app/(dashboard)/billing/payment/page.tsx +17 -68
  108. package/src/app/(dashboard)/billing/plans/page.tsx +3 -9
  109. package/src/app/(dashboard)/billing/usage/hosted/page.tsx +8 -25
  110. package/src/app/(dashboard)/billing/usage/page.tsx +63 -103
  111. package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +9 -27
  112. package/src/app/(dashboard)/changesets/[id]/error.tsx +2 -6
  113. package/src/app/(dashboard)/changesets/error.tsx +1 -7
  114. package/src/app/(dashboard)/chat/page.tsx +2 -6
  115. package/src/app/(dashboard)/dashboard/network/page.tsx +5 -19
  116. package/src/app/(dashboard)/error.tsx +1 -7
  117. package/src/app/(dashboard)/layout.tsx +15 -36
  118. package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +14 -51
  119. package/src/app/(dashboard)/marketplace/error.tsx +1 -7
  120. package/src/app/(dashboard)/marketplace/page.tsx +6 -27
  121. package/src/app/(dashboard)/not-found.tsx +2 -5
  122. package/src/app/(dashboard)/onboarding/page.tsx +5 -22
  123. package/src/app/(dashboard)/settings/account/page.tsx +1 -6
  124. package/src/app/(dashboard)/settings/activity/page.tsx +8 -34
  125. package/src/app/(dashboard)/settings/api-keys/page.tsx +15 -60
  126. package/src/app/(dashboard)/settings/brain/page.tsx +9 -31
  127. package/src/app/(dashboard)/settings/error.tsx +2 -10
  128. package/src/app/(dashboard)/settings/notifications/page.tsx +2 -6
  129. package/src/app/(dashboard)/settings/org/page.tsx +13 -56
  130. package/src/app/(dashboard)/settings/page.tsx +1 -0
  131. package/src/app/(dashboard)/settings/profile/page.tsx +126 -73
  132. package/src/app/(dashboard)/settings/providers/page.tsx +21 -78
  133. package/src/app/(dashboard)/settings/secrets/page.tsx +13 -58
  134. package/src/app/(dashboard)/settings/security/page.tsx +31 -111
  135. package/src/app/admin/email-templates/email-templates-client.tsx +15 -58
  136. package/src/app/admin/error.tsx +1 -7
  137. package/src/app/admin/fleet-updates/error.tsx +1 -7
  138. package/src/app/admin/fleet-updates/fleet-updates-client.tsx +10 -50
  139. package/src/app/admin/layout.tsx +4 -0
  140. package/src/app/admin/payment-methods/page.tsx +9 -38
  141. package/src/app/admin/products/error.tsx +2 -7
  142. package/src/app/admin/products/page.tsx +1 -4
  143. package/src/app/admin/promotions/[id]/page.tsx +9 -38
  144. package/src/app/admin/promotions/page.tsx +9 -36
  145. package/src/app/admin/rate-overrides/page.tsx +9 -45
  146. package/src/app/auth/callback/[provider]/page.tsx +1 -8
  147. package/src/app/auth/verify/page.tsx +9 -36
  148. package/src/app/channels/error.tsx +2 -10
  149. package/src/app/channels/layout.tsx +9 -0
  150. package/src/app/channels/page.tsx +8 -20
  151. package/src/app/channels/setup/[plugin]/page.tsx +3 -5
  152. package/src/app/error.tsx +1 -7
  153. package/src/app/fleet/error.tsx +1 -7
  154. package/src/app/fleet/layout.tsx +5 -0
  155. package/src/app/fleet/settings/page.tsx +1 -3
  156. package/src/app/global-error.tsx +2 -10
  157. package/src/app/globals.css +1 -4
  158. package/src/app/instances/[id]/instance-detail-client.tsx +51 -125
  159. package/src/app/instances/error.tsx +2 -10
  160. package/src/app/instances/instance-list-client.tsx +20 -69
  161. package/src/app/instances/layout.tsx +9 -0
  162. package/src/app/instances/new/create-instance-client.tsx +10 -31
  163. package/src/app/layout.tsx +2 -10
  164. package/src/app/not-found.tsx +1 -3
  165. package/src/app/page.tsx +1 -2
  166. package/src/app/plugins/error.tsx +2 -10
  167. package/src/app/plugins/layout.tsx +5 -0
  168. package/src/app/plugins/page.tsx +16 -48
  169. package/src/app/pricing/error.tsx +1 -7
  170. package/src/app/privacy/page.tsx +93 -150
  171. package/src/app/status/error.tsx +1 -7
  172. package/src/app/terms/page.tsx +89 -144
  173. package/src/components/account-switcher.tsx +25 -52
  174. package/src/components/admin/accounting-dashboard.tsx +1 -3
  175. package/src/components/admin/admin-guard.tsx +1 -3
  176. package/src/components/admin/admin-nav.tsx +1 -3
  177. package/src/components/admin/affiliate-dashboard.tsx +25 -94
  178. package/src/components/admin/audit-log-table.tsx +13 -49
  179. package/src/components/admin/billing-health-dashboard.tsx +7 -25
  180. package/src/components/admin/bulk-actions-bar.test.tsx +1 -7
  181. package/src/components/admin/bulk-actions-bar.tsx +1 -3
  182. package/src/components/admin/bulk-export-dialog.test.tsx +1 -7
  183. package/src/components/admin/bulk-export-dialog.tsx +6 -32
  184. package/src/components/admin/bulk-grant-dialog.test.tsx +2 -6
  185. package/src/components/admin/bulk-grant-dialog.tsx +4 -15
  186. package/src/components/admin/bulk-preview-dialog.tsx +3 -12
  187. package/src/components/admin/bulk-reactivate-dialog.tsx +1 -7
  188. package/src/components/admin/bulk-select-all-banner.tsx +1 -6
  189. package/src/components/admin/bulk-suspend-dialog.tsx +5 -12
  190. package/src/components/admin/bulk-undo-toast.tsx +1 -2
  191. package/src/components/admin/compliance-dashboard.tsx +31 -101
  192. package/src/components/admin/gpu-dashboard.tsx +21 -70
  193. package/src/components/admin/grant-credits-dialog.tsx +4 -17
  194. package/src/components/admin/incident-dashboard.tsx +10 -25
  195. package/src/components/admin/inference-dashboard.tsx +14 -54
  196. package/src/components/admin/marketplace-admin.tsx +18 -60
  197. package/src/components/admin/migrations-dashboard.tsx +9 -42
  198. package/src/components/admin/onboarding-dashboard.tsx +14 -64
  199. package/src/components/admin/pool-config-dashboard.tsx +4 -10
  200. package/src/components/admin/products/fleet-form.tsx +2 -11
  201. package/src/components/admin/products/nav-editor.tsx +3 -10
  202. package/src/components/admin/promotions/promotion-form.tsx +9 -42
  203. package/src/components/admin/roles-dashboard.tsx +7 -34
  204. package/src/components/admin/suspend-dialog.tsx +4 -11
  205. package/src/components/admin/tenant-notes-panel.tsx +1 -3
  206. package/src/components/admin/tenant-row-actions.tsx +4 -20
  207. package/src/components/admin/tenant-table.tsx +12 -49
  208. package/src/components/auth/auth-redirect.tsx +11 -3
  209. package/src/components/auth/email-verification-result-banner.tsx +1 -3
  210. package/src/components/auth/resend-verification-button.tsx +2 -10
  211. package/src/components/auth/wopr-wordmark.tsx +1 -3
  212. package/src/components/billing/add-payment-method-dialog.tsx +1 -2
  213. package/src/components/billing/affiliate-dashboard.tsx +4 -16
  214. package/src/components/billing/amount-selector.tsx +1 -3
  215. package/src/components/billing/auto-topup-card.tsx +2 -11
  216. package/src/components/billing/buy-credits-panel.tsx +4 -14
  217. package/src/components/billing/byok-callout.tsx +6 -8
  218. package/src/components/billing/confirmation-tracker.tsx +4 -14
  219. package/src/components/billing/credit-balance-badge.tsx +22 -0
  220. package/src/components/billing/credit-balance.tsx +3 -9
  221. package/src/components/billing/crypto-checkout.tsx +5 -24
  222. package/src/components/billing/degraded-state-banner.tsx +1 -3
  223. package/src/components/billing/deposit-view.tsx +301 -41
  224. package/src/components/billing/dividend-banner.tsx +1 -3
  225. package/src/components/billing/dividend-eligibility.tsx +3 -12
  226. package/src/components/billing/dividend-pool-stats.tsx +6 -20
  227. package/src/components/billing/first-dividend-dialog.tsx +2 -2
  228. package/src/components/billing/org-billing-page.tsx +8 -31
  229. package/src/components/billing/payment-method-picker.tsx +2 -10
  230. package/src/components/billing/suspension-banner.tsx +2 -7
  231. package/src/components/billing/transaction-history.tsx +10 -58
  232. package/src/components/billing/unified-checkout.tsx +547 -0
  233. package/src/components/bot-settings/backups-tab.tsx +9 -33
  234. package/src/components/bot-settings/bot-settings-client.tsx +32 -134
  235. package/src/components/bot-settings/resources-tab.tsx +2 -9
  236. package/src/components/bot-settings/storage-tab.tsx +19 -48
  237. package/src/components/bot-settings/vps-info-panel.tsx +3 -11
  238. package/src/components/bot-settings/vps-upgrade-card.tsx +3 -4
  239. package/src/components/brand-hydrator.tsx +13 -0
  240. package/src/components/channel-wizard/field-interactive.tsx +1 -3
  241. package/src/components/channel-wizard/field-qr.tsx +10 -39
  242. package/src/components/channel-wizard/step-renderer.tsx +5 -28
  243. package/src/components/channel-wizard/wizard.tsx +6 -31
  244. package/src/components/chat/chat-message.tsx +1 -4
  245. package/src/components/chat/chat-panel.tsx +4 -18
  246. package/src/components/chat/chat-widget.tsx +3 -14
  247. package/src/components/dashboard/command-center.tsx +15 -61
  248. package/src/components/fleet/update-settings-card.tsx +7 -23
  249. package/src/components/instance-update-banner.tsx +130 -0
  250. package/src/components/instances/friends-tab.test.tsx +2 -9
  251. package/src/components/instances/friends-tab.tsx +18 -74
  252. package/src/components/instances/update-available-badge.tsx +2 -11
  253. package/src/components/landing/hero.tsx +3 -9
  254. package/src/components/landing/landing-page.tsx +1 -3
  255. package/src/components/landing/portfolio-chart.tsx +4 -9
  256. package/src/components/landing/story-sections.tsx +1 -3
  257. package/src/components/landing/terminal-sequence.tsx +4 -17
  258. package/src/components/marketplace/empty-state.tsx +2 -6
  259. package/src/components/marketplace/first-visit-hero.tsx +1 -3
  260. package/src/components/marketplace/install-wizard.tsx +20 -77
  261. package/src/components/marketplace/marketplace-tabs.tsx +1 -4
  262. package/src/components/marketplace/plugin-card.tsx +2 -9
  263. package/src/components/marketplace/superpower-content.tsx +1 -3
  264. package/src/components/marketplace/terminal-search.tsx +2 -8
  265. package/src/components/oauth-buttons.tsx +29 -14
  266. package/src/components/observability/fleet-health.tsx +5 -18
  267. package/src/components/observability/health-overview.tsx +7 -20
  268. package/src/components/observability/logs-viewer.tsx +8 -32
  269. package/src/components/observability/metrics-dashboard.tsx +2 -15
  270. package/src/components/onboarding/fallback-setup.tsx +6 -25
  271. package/src/components/onboarding/setup-checklist.tsx +18 -51
  272. package/src/components/onboarding/step-superpowers.tsx +1 -4
  273. package/src/components/plugin-setup/setup-chat-panel.tsx +6 -22
  274. package/src/components/pricing/dividend-calculator.tsx +6 -12
  275. package/src/components/pricing/dividend-stats.tsx +5 -17
  276. package/src/components/pricing/pricing-page.tsx +17 -36
  277. package/src/components/settings/create-org-wizard.tsx +2 -5
  278. package/src/components/sidebar.tsx +7 -42
  279. package/src/components/sidecar-frame.tsx +78 -0
  280. package/src/components/status/status-page.tsx +6 -28
  281. package/src/components/ui/alert-dialog.tsx +8 -25
  282. package/src/components/ui/badge.tsx +2 -8
  283. package/src/components/ui/banner.tsx +1 -6
  284. package/src/components/ui/card.tsx +5 -24
  285. package/src/components/ui/checkbox.tsx +1 -5
  286. package/src/components/ui/collapsible.tsx +3 -8
  287. package/src/components/ui/dialog.tsx +4 -10
  288. package/src/components/ui/dropdown-menu.tsx +9 -18
  289. package/src/components/ui/form.tsx +2 -16
  290. package/src/components/ui/popover.tsx +3 -23
  291. package/src/components/ui/progress.tsx +1 -5
  292. package/src/components/ui/radio-group.tsx +3 -15
  293. package/src/components/ui/select.tsx +4 -17
  294. package/src/components/ui/sheet.tsx +5 -19
  295. package/src/components/ui/skeleton.tsx +1 -7
  296. package/src/components/ui/table.tsx +5 -22
  297. package/src/components/ui/tabs.tsx +3 -13
  298. package/src/components/ui/tooltip.tsx +1 -1
  299. package/src/components/unified-sidebar.tsx +493 -0
  300. package/src/hooks/__tests__/use-fleet-sse.test.ts +1 -4
  301. package/src/hooks/__tests__/use-save-queue.test.ts +2 -8
  302. package/src/hooks/use-credit-balance.ts +27 -0
  303. package/src/hooks/use-my-org-role.ts +1 -3
  304. package/src/hooks/use-plugin-registry.ts +8 -14
  305. package/src/hooks/use-plugin-setup-chat.ts +2 -5
  306. package/src/hooks/use-sidecar-bridge.tsx +148 -0
  307. package/src/hooks/use-webmcp.ts +1 -4
  308. package/src/lib/__tests__/admin-api.test.ts +1 -3
  309. package/src/lib/__tests__/api-bot-crud.test.ts +8 -18
  310. package/src/lib/__tests__/api-fetch.test.ts +4 -16
  311. package/src/lib/__tests__/org-billing-api.test.ts +1 -3
  312. package/src/lib/__tests__/pricing-data.test.ts +0 -8
  313. package/src/lib/__tests__/settings-api.test.ts +1 -3
  314. package/src/lib/admin-affiliate-api.ts +2 -7
  315. package/src/lib/admin-api.ts +6 -26
  316. package/src/lib/admin-incident-api.ts +11 -19
  317. package/src/lib/admin-marketplace-api.ts +1 -5
  318. package/src/lib/api-config.test.ts +5 -50
  319. package/src/lib/api.ts +143 -122
  320. package/src/lib/auth-client.ts +1 -2
  321. package/src/lib/bot-settings-data.ts +11 -36
  322. package/src/lib/brand-config.ts +56 -115
  323. package/src/lib/brand.ts +2 -15
  324. package/src/lib/chat/use-chat.ts +2 -7
  325. package/src/lib/cost-comparison-data.test.ts +1 -3
  326. package/src/lib/cost-comparison-data.ts +1 -4
  327. package/src/lib/errors.ts +1 -4
  328. package/src/lib/fetch-utils.test.ts +26 -9
  329. package/src/lib/fetch-utils.ts +40 -11
  330. package/src/lib/logger.ts +2 -0
  331. package/src/lib/marketplace-data.ts +3 -11
  332. package/src/lib/oauth-errors.ts +2 -4
  333. package/src/lib/onboarding-data.ts +3 -11
  334. package/src/lib/org-api.ts +2 -10
  335. package/src/lib/org-billing-api.ts +5 -19
  336. package/src/lib/plugin/tool-definitions.ts +1 -2
  337. package/src/lib/require-auth.ts +57 -0
  338. package/src/lib/settings-api.ts +1 -4
  339. package/src/lib/sidecar-routes.ts +43 -0
  340. package/src/lib/trpc-server.ts +49 -0
  341. package/src/lib/trpc-types.ts +4 -6
  342. package/src/lib/trpc.tsx +12 -4
  343. package/src/lib/validate-redirect-url.ts +1 -4
  344. package/src/lib/webmcp/marketplace-onboarding-tools.ts +6 -16
  345. package/src/lib/webmcp/register.ts +1 -4
  346. package/src/lib/webmcp/tools.ts +2 -9
  347. package/src/proxy.ts +35 -212
  348. package/src/types/missing-deps.d.ts +2 -8
  349. package/tsconfig.json +1 -8
  350. package/biome.json +0 -52
  351. package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +0 -741
  352. package/src/__tests__/billing-byok-callout.test.tsx +0 -76
  353. package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +0 -112
@@ -0,0 +1,148 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
5
+
6
+ import { fromSidecarPath } from "@/lib/sidecar-routes";
7
+
8
+ // Types matching the sidecar's postMessage protocol
9
+
10
+ export interface SidebarAgent {
11
+ id: string;
12
+ name: string;
13
+ status: string;
14
+ icon: string | null;
15
+ liveRun: boolean;
16
+ liveRunCount: number;
17
+ pauseReason: string | null;
18
+ }
19
+
20
+ export interface SidebarProject {
21
+ id: string;
22
+ name: string;
23
+ urlKey: string;
24
+ color: string | null;
25
+ }
26
+
27
+ export interface SidecarSidebarData {
28
+ companyName: string;
29
+ companyIssuePrefix: string;
30
+ brandColor: string | null;
31
+ projects: SidebarProject[];
32
+ agents: SidebarAgent[];
33
+ inboxBadge: number;
34
+ failedRuns: number;
35
+ liveRunCount: number;
36
+ /** Whether the Projects feature is exposed in this deployment. Older
37
+ * sidecar versions don't send this — undefined means "fall back to
38
+ * showing the section so we don't break standalone installs". */
39
+ showProjects?: boolean;
40
+ }
41
+
42
+ interface SidecarBridgeState {
43
+ ready: boolean;
44
+ sidebarData: SidecarSidebarData | null;
45
+ currentSidecarPath: string | null;
46
+ navigate: (path: string) => void;
47
+ command: (action: string) => void;
48
+ setIframeRef: (iframe: HTMLIFrameElement | null) => void;
49
+ }
50
+
51
+ const SidecarBridgeContext = createContext<SidecarBridgeState>({
52
+ ready: false,
53
+ sidebarData: null,
54
+ currentSidecarPath: null,
55
+ navigate: () => {},
56
+ command: () => {},
57
+ setIframeRef: () => {},
58
+ });
59
+
60
+ export function useSidecarBridge() {
61
+ return useContext(SidecarBridgeContext);
62
+ }
63
+
64
+ export function SidecarBridgeProvider({ children }: { children: ReactNode }) {
65
+ const iframeRef = useRef<HTMLIFrameElement | null>(null);
66
+ const [ready, setReady] = useState(false);
67
+ const [sidebarData, setSidebarData] = useState<SidecarSidebarData | null>(null);
68
+ const [currentSidecarPath, setCurrentSidecarPath] = useState<string | null>(null);
69
+
70
+ const setIframeRef = useCallback((iframe: HTMLIFrameElement | null) => {
71
+ iframeRef.current = iframe;
72
+ }, []);
73
+
74
+ const postToSidecar = useCallback((message: unknown) => {
75
+ if (!iframeRef.current?.contentWindow) return;
76
+ iframeRef.current.contentWindow.postMessage(message, window.location.origin);
77
+ }, []);
78
+
79
+ const navigate = useCallback(
80
+ (path: string) => {
81
+ postToSidecar({ type: "navigate", path });
82
+ },
83
+ [postToSidecar],
84
+ );
85
+
86
+ const command = useCallback(
87
+ (action: string) => {
88
+ postToSidecar({ type: "command", action });
89
+ },
90
+ [postToSidecar],
91
+ );
92
+
93
+ useEffect(() => {
94
+ function onMessage(event: MessageEvent) {
95
+ if (event.origin !== window.location.origin) return;
96
+ const data = event.data;
97
+ if (!data || typeof data.type !== "string") return;
98
+
99
+ switch (data.type) {
100
+ case "ready": {
101
+ // Initial deep-link is now passed through the iframe src
102
+ // (`?initial-path=…` → SidecarFrame). Forwarding on ready would
103
+ // race with the sidecar's CompanyRootRedirect Navigate and
104
+ // sometimes lose the deep link.
105
+ setReady(true);
106
+ break;
107
+ }
108
+ case "routeChanged": {
109
+ const platformPath = fromSidecarPath(data.path);
110
+ setCurrentSidecarPath(platformPath);
111
+ const current = window.location.pathname + window.location.search;
112
+ if (current !== platformPath) {
113
+ window.history.replaceState(null, "", platformPath);
114
+ }
115
+ break;
116
+ }
117
+ case "sidebarData":
118
+ setSidebarData(data.payload);
119
+ break;
120
+ case "toast":
121
+ import("sonner").then(({ toast }) => {
122
+ if (data.level === "error") toast.error(data.message);
123
+ else if (data.level === "success") toast.success(data.message);
124
+ else toast.info(data.message);
125
+ });
126
+ break;
127
+ }
128
+ }
129
+
130
+ window.addEventListener("message", onMessage);
131
+ return () => window.removeEventListener("message", onMessage);
132
+ }, []);
133
+
134
+ return (
135
+ <SidecarBridgeContext.Provider
136
+ value={{
137
+ ready,
138
+ sidebarData,
139
+ currentSidecarPath,
140
+ navigate,
141
+ command,
142
+ setIframeRef,
143
+ }}
144
+ >
145
+ {children}
146
+ </SidecarBridgeContext.Provider>
147
+ );
148
+ }
@@ -17,10 +17,7 @@ export function useWebMCP(): void {
17
17
  const registeredRef = useRef(false);
18
18
  const router = useRouter();
19
19
 
20
- const confirm = useCallback(
21
- (message: string): Promise<boolean> => Promise.resolve(window.confirm(message)),
22
- [],
23
- );
20
+ const confirm = useCallback((message: string): Promise<boolean> => Promise.resolve(window.confirm(message)), []);
24
21
 
25
22
  useEffect(() => {
26
23
  const isAuthenticated = session?.user != null;
@@ -167,9 +167,7 @@ describe("getTenantNotes", () => {
167
167
  afterEach(() => vi.clearAllMocks());
168
168
 
169
169
  it("unwraps notes array from response", async () => {
170
- const notes = [
171
- { id: "n-1", tenant_id: "t-1", admin_user: "admin", content: "Note", created_at: 1000 },
172
- ];
170
+ const notes = [{ id: "n-1", tenant_id: "t-1", admin_user: "admin", content: "Note", created_at: 1000 }];
173
171
  mockNotesListQuery.mockResolvedValue({ notes });
174
172
 
175
173
  const result = await getTenantNotes("t-1");
@@ -1,14 +1,12 @@
1
1
  import { afterEach, describe, expect, it, vi } from "vitest";
2
2
 
3
3
  // Use vi.hoisted so mock fns are available inside vi.mock factory (which gets hoisted)
4
- const { mockListInstances, mockGetInstance, mockCreateInstance, mockControlInstance } = vi.hoisted(
5
- () => ({
6
- mockListInstances: vi.fn(),
7
- mockGetInstance: vi.fn(),
8
- mockCreateInstance: vi.fn(),
9
- mockControlInstance: vi.fn(),
10
- }),
11
- );
4
+ const { mockListInstances, mockGetInstance, mockCreateInstance, mockControlInstance } = vi.hoisted(() => ({
5
+ mockListInstances: vi.fn(),
6
+ mockGetInstance: vi.fn(),
7
+ mockCreateInstance: vi.fn(),
8
+ mockControlInstance: vi.fn(),
9
+ }));
12
10
 
13
11
  vi.mock("@/lib/trpc", () => ({
14
12
  trpcVanilla: {
@@ -66,13 +64,7 @@ vi.mock("@/lib/tenant-context", () => ({
66
64
  getActiveTenantId: vi.fn(() => "tenant-123"),
67
65
  }));
68
66
 
69
- import {
70
- controlInstance,
71
- createInstance,
72
- deployInstance,
73
- getInstance,
74
- listInstances,
75
- } from "@/lib/api";
67
+ import { controlInstance, createInstance, deployInstance, getInstance, listInstances } from "@/lib/api";
76
68
 
77
69
  describe("listInstances", () => {
78
70
  afterEach(() => {
@@ -315,9 +307,7 @@ describe("deployInstance", () => {
315
307
 
316
308
  await deployInstance({ name: "EnvBot", env: { FOO: "bar" } });
317
309
 
318
- expect(mockCreateInstance).toHaveBeenCalledWith(
319
- expect.objectContaining({ env: { FOO: "bar" } }),
320
- );
310
+ expect(mockCreateInstance).toHaveBeenCalledWith(expect.objectContaining({ env: { FOO: "bar" } }));
321
311
  });
322
312
 
323
313
  it("defaults description to empty string", async () => {
@@ -53,10 +53,7 @@ describe("apiFetch", () => {
53
53
  await apiFetch("/v1/test");
54
54
 
55
55
  expect(mockFetch).toHaveBeenCalledOnce();
56
- const [url, init] = mockFetch.mock.calls[0] as [
57
- string,
58
- RequestInit & { headers: Record<string, string> },
59
- ];
56
+ const [url, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
60
57
  expect(url).toBe("http://localhost:3001/api/v1/test");
61
58
  expect(init.credentials).toBe("include");
62
59
  expect(init.headers["Content-Type"]).toBe("application/json");
@@ -74,10 +71,7 @@ describe("apiFetch", () => {
74
71
  const { apiFetch } = await import("@/lib/api");
75
72
  await apiFetch("/v1/bots");
76
73
 
77
- const [, init] = mockFetch.mock.calls[0] as [
78
- string,
79
- RequestInit & { headers: Record<string, string> },
80
- ];
74
+ const [, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
81
75
  expect(init.headers["x-tenant-id"]).toBe("tenant-abc");
82
76
  });
83
77
 
@@ -93,10 +87,7 @@ describe("apiFetch", () => {
93
87
  const { apiFetch } = await import("@/lib/api");
94
88
  await apiFetch("/v1/bots");
95
89
 
96
- const [, init] = mockFetch.mock.calls[0] as [
97
- string,
98
- RequestInit & { headers: Record<string, string> },
99
- ];
90
+ const [, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
100
91
  expect(init.headers["x-tenant-id"]).toBeUndefined();
101
92
  });
102
93
 
@@ -170,10 +161,7 @@ describe("apiFetch", () => {
170
161
  body: JSON.stringify({ name: "test" }),
171
162
  });
172
163
 
173
- const [, init] = mockFetch.mock.calls[0] as [
174
- string,
175
- RequestInit & { headers: Record<string, string> },
176
- ];
164
+ const [, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
177
165
  expect(init.method).toBe("POST");
178
166
  expect(init.headers["X-Custom"]).toBe("value");
179
167
  expect(init.headers["Content-Type"]).toBe("application/json");
@@ -193,9 +193,7 @@ describe("createOrgTopupCheckout", () => {
193
193
 
194
194
  it("propagates tRPC errors", async () => {
195
195
  mockOrgTopupCheckoutMutate.mockRejectedValue(new Error("Invalid price"));
196
- await expect(
197
- createOrgTopupCheckout("org-1", "bad-price", "http://s", "http://c"),
198
- ).rejects.toThrow("Invalid price");
196
+ await expect(createOrgTopupCheckout("org-1", "bad-price", "http://s", "http://c")).rejects.toThrow("Invalid price");
199
197
  });
200
198
  });
201
199
 
@@ -3,10 +3,6 @@ import { capabilityMeta, pricingData } from "@/lib/pricing-data";
3
3
 
4
4
  describe("pricing-data", () => {
5
5
  describe("pricingData", () => {
6
- it("matches snapshot", () => {
7
- expect(pricingData).toMatchSnapshot();
8
- });
9
-
10
6
  it("has expected bot price", () => {
11
7
  expect(pricingData.bot_price).toEqual({ amount: 5, period: "month" });
12
8
  });
@@ -21,10 +17,6 @@ describe("pricing-data", () => {
21
17
  });
22
18
 
23
19
  describe("capabilityMeta", () => {
24
- it("matches snapshot", () => {
25
- expect(capabilityMeta).toMatchSnapshot();
26
- });
27
-
28
20
  it("maps all expected backend keys", () => {
29
21
  expect(Object.keys(capabilityMeta).sort()).toEqual(["image_gen", "llm", "sms", "stt", "tts"]);
30
22
  });
@@ -111,9 +111,7 @@ describe("updateNotificationPreferences", () => {
111
111
 
112
112
  it("propagates tRPC errors", async () => {
113
113
  mockUpdateNotificationPreferencesMutate.mockRejectedValue(new Error("Forbidden"));
114
- await expect(updateNotificationPreferences({ billing_receipts: true })).rejects.toThrow(
115
- "Forbidden",
116
- );
114
+ await expect(updateNotificationPreferences({ billing_receipts: true })).rejects.toThrow("Forbidden");
117
115
  });
118
116
  });
119
117
 
@@ -33,10 +33,7 @@ export async function getAffiliateSuppressions(
33
33
  return trpcVanilla.admin.affiliateSuppressions.query({ limit, offset });
34
34
  }
35
35
 
36
- export async function getAffiliateVelocity(
37
- capReferrals = 20,
38
- capCredits = 20000,
39
- ): Promise<VelocityReferrer[]> {
36
+ export async function getAffiliateVelocity(capReferrals = 20, capCredits = 20000): Promise<VelocityReferrer[]> {
40
37
  return trpcVanilla.admin.affiliateVelocity.query({ capReferrals, capCredits });
41
38
  }
42
39
 
@@ -44,8 +41,6 @@ export async function getAffiliateFingerprintClusters(): Promise<FingerprintClus
44
41
  return trpcVanilla.admin.affiliateFingerprintClusters.query(undefined);
45
42
  }
46
43
 
47
- export async function blockAffiliateFingerprint(
48
- fingerprint: string,
49
- ): Promise<{ success: boolean }> {
44
+ export async function blockAffiliateFingerprint(fingerprint: string): Promise<{ success: boolean }> {
50
45
  return trpcVanilla.admin.affiliateBlockFingerprint.mutate({ fingerprint });
51
46
  }
@@ -109,19 +109,11 @@ export async function reactivateTenant(tenantId: string): Promise<void> {
109
109
  await trpcVanilla.admin.reactivateTenant.mutate({ tenantId });
110
110
  }
111
111
 
112
- export async function grantCredits(
113
- tenantId: string,
114
- amount_cents: number,
115
- reason: string,
116
- ): Promise<void> {
112
+ export async function grantCredits(tenantId: string, amount_cents: number, reason: string): Promise<void> {
117
113
  await trpcVanilla.admin.creditsGrant.mutate({ tenantId, amount_cents, reason });
118
114
  }
119
115
 
120
- export async function refundCredits(
121
- tenantId: string,
122
- amount_cents: number,
123
- reason: string,
124
- ): Promise<void> {
116
+ export async function refundCredits(tenantId: string, amount_cents: number, reason: string): Promise<void> {
125
117
  await trpcVanilla.admin.creditsRefund.mutate({ tenantId, amount_cents, reason });
126
118
  }
127
119
 
@@ -156,10 +148,7 @@ export async function getTransactions(
156
148
  return trpcVanilla.admin.creditsTransactions.query({ tenantId, ...filters });
157
149
  }
158
150
 
159
- export async function getTenantUsageByCapability(
160
- tenantId: string,
161
- days = 30,
162
- ): Promise<UsageSummary[]> {
151
+ export async function getTenantUsageByCapability(tenantId: string, days = 30): Promise<UsageSummary[]> {
163
152
  const result = await trpcVanilla.admin.tenantUsageByCapability.query({ tenantId, days });
164
153
  return result.usage;
165
154
  }
@@ -172,11 +161,7 @@ export async function getUsersList(params?: {
172
161
  return trpcVanilla.admin.usersList.query(params ?? {});
173
162
  }
174
163
 
175
- export async function bulkGrantCredits(
176
- tenantIds: string[],
177
- amountCents: number,
178
- reason: string,
179
- ): Promise<void> {
164
+ export async function bulkGrantCredits(tenantIds: string[], amountCents: number, reason: string): Promise<void> {
180
165
  await trpcVanilla.admin.bulkGrant.mutate({ tenantIds, amountCents, reason });
181
166
  }
182
167
 
@@ -310,16 +295,11 @@ export async function getMigrationSnapshots(tenantId: string): Promise<Migration
310
295
  return result.snapshots;
311
296
  }
312
297
 
313
- export async function restoreMigrationSnapshot(
314
- tenantId: string,
315
- snapshotId: string,
316
- ): Promise<void> {
298
+ export async function restoreMigrationSnapshot(tenantId: string, snapshotId: string): Promise<void> {
317
299
  await trpcVanilla.admin.migrationRestore.mutate({ tenantId, snapshotId });
318
300
  }
319
301
 
320
- export async function getMigrationRestoreHistory(
321
- tenantId: string,
322
- ): Promise<MigrationRestoreRecord[]> {
302
+ export async function getMigrationRestoreHistory(tenantId: string): Promise<MigrationRestoreRecord[]> {
323
303
  const result = await trpcVanilla.admin.migrationRestoreHistory.query({ tenantId });
324
304
  return result.history;
325
305
  }
@@ -67,14 +67,11 @@ export interface PostmortemInput {
67
67
  // --- API calls ---
68
68
 
69
69
  export async function classifyIncidentSeverity(signals: SeveritySignals): Promise<SeverityResult> {
70
- const result = await apiFetch<{ success: boolean; error?: string } & SeverityResult>(
71
- "/admin/incidents/severity",
72
- {
73
- method: "POST",
74
- headers: { "Content-Type": "application/json" },
75
- body: JSON.stringify(signals),
76
- },
77
- );
70
+ const result = await apiFetch<{ success: boolean; error?: string } & SeverityResult>("/admin/incidents/severity", {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json" },
73
+ body: JSON.stringify(signals),
74
+ });
78
75
  if (!result.success) throw new Error(result.error ?? "Request failed");
79
76
  return result;
80
77
  }
@@ -91,9 +88,7 @@ export async function getResponseProcedure(severity: IncidentSeverity): Promise<
91
88
  return result.procedure;
92
89
  }
93
90
 
94
- export async function getCommunicationTemplates(
95
- context: CommunicateContext,
96
- ): Promise<CommunicationTemplates> {
91
+ export async function getCommunicationTemplates(context: CommunicateContext): Promise<CommunicationTemplates> {
97
92
  const result = await apiFetch<{
98
93
  success: boolean;
99
94
  templates: CommunicationTemplates;
@@ -108,14 +103,11 @@ export async function getCommunicationTemplates(
108
103
  }
109
104
 
110
105
  export async function generatePostmortem(input: PostmortemInput): Promise<string> {
111
- const result = await apiFetch<{ success: boolean; report: string; error?: string }>(
112
- "/admin/incidents/postmortem",
113
- {
114
- method: "POST",
115
- headers: { "Content-Type": "application/json" },
116
- body: JSON.stringify(input),
117
- },
118
- );
106
+ const result = await apiFetch<{ success: boolean; report: string; error?: string }>("/admin/incidents/postmortem", {
107
+ method: "POST",
108
+ headers: { "Content-Type": "application/json" },
109
+ body: JSON.stringify(input),
110
+ });
119
111
  if (!result.success) throw new Error(result.error ?? "Request failed");
120
112
  return result.report;
121
113
  }
@@ -70,11 +70,7 @@ export async function addPluginByNpm(req: AddPluginRequest): Promise<AdminPlugin
70
70
  }
71
71
 
72
72
  export async function reorderPlugins(orderedIds: string[]): Promise<void> {
73
- await Promise.all(
74
- orderedIds.map((id, i) =>
75
- trpcVanilla.adminMarketplace.updatePlugin.mutate({ id, sort_order: i }),
76
- ),
77
- );
73
+ await Promise.all(orderedIds.map((id, i) => trpcVanilla.adminMarketplace.updatePlugin.mutate({ id, sort_order: i })));
78
74
  }
79
75
 
80
76
  export async function deletePlugin(id: string): Promise<void> {
@@ -6,64 +6,19 @@ describe("api-config", () => {
6
6
  vi.resetModules();
7
7
  });
8
8
 
9
- describe("production URL validation", () => {
10
- it("throws when NEXT_PUBLIC_API_URL contains localhost in production runtime", async () => {
11
- vi.stubEnv("NODE_ENV", "production");
12
- vi.stubEnv("NEXT_RUNTIME", "nodejs");
13
- vi.stubEnv("NEXT_PUBLIC_API_URL", "http://localhost:3001");
14
- vi.resetModules();
15
- await expect(import("./api-config")).rejects.toThrow(/internal hostname/i);
16
- });
17
-
18
- it("throws when NEXT_PUBLIC_API_URL contains a Docker-internal hostname in production runtime", async () => {
19
- vi.stubEnv("NODE_ENV", "production");
20
- vi.stubEnv("NEXT_RUNTIME", "nodejs");
21
- vi.stubEnv("NEXT_PUBLIC_API_URL", "http://platform-api:3001");
22
- vi.resetModules();
23
- await expect(import("./api-config")).rejects.toThrow(/internal hostname/i);
24
- });
25
-
26
- it("throws when NEXT_PUBLIC_API_URL is unset in production runtime", async () => {
27
- vi.stubEnv("NODE_ENV", "production");
28
- vi.stubEnv("NEXT_RUNTIME", "nodejs");
29
- vi.stubEnv("NEXT_PUBLIC_API_URL", undefined as unknown as string);
30
- vi.resetModules();
31
- await expect(import("./api-config")).rejects.toThrow(/internal hostname/i);
32
- });
33
-
34
- it("throws when NEXT_PUBLIC_API_URL uses http (not https) in production runtime", async () => {
35
- vi.stubEnv("NODE_ENV", "production");
36
- vi.stubEnv("NEXT_RUNTIME", "nodejs");
37
- vi.stubEnv("NEXT_PUBLIC_API_URL", "http://api.example.com");
38
- vi.resetModules();
39
- await expect(import("./api-config")).rejects.toThrow(/https/i);
40
- });
41
-
42
- it("allows https public URL in production runtime", async () => {
43
- vi.stubEnv("NODE_ENV", "production");
44
- vi.stubEnv("NEXT_RUNTIME", "nodejs");
45
- vi.stubEnv("NEXT_PUBLIC_API_URL", "https://api.example.com");
46
- vi.resetModules();
47
- const mod = await import("./api-config");
48
- expect(mod.PLATFORM_BASE_URL).toBe("https://api.example.com");
49
- });
50
-
51
- it("skips validation during build time (no NEXT_RUNTIME)", async () => {
52
- vi.stubEnv("NODE_ENV", "production");
53
- vi.stubEnv("NEXT_RUNTIME", undefined as unknown as string);
9
+ describe("URL resolution", () => {
10
+ it("uses NEXT_PUBLIC_API_URL directly when set", async () => {
54
11
  vi.stubEnv("NEXT_PUBLIC_API_URL", "http://localhost:3001");
55
12
  vi.resetModules();
56
13
  const mod = await import("./api-config");
57
14
  expect(mod.PLATFORM_BASE_URL).toBe("http://localhost:3001");
58
15
  });
59
16
 
60
- it("skips validation in development", async () => {
61
- vi.stubEnv("NODE_ENV", "development");
62
- vi.stubEnv("NEXT_RUNTIME", "nodejs");
63
- vi.stubEnv("NEXT_PUBLIC_API_URL", "http://localhost:3001");
17
+ it("accepts https URL", async () => {
18
+ vi.stubEnv("NEXT_PUBLIC_API_URL", "https://api.example.com");
64
19
  vi.resetModules();
65
20
  const mod = await import("./api-config");
66
- expect(mod.PLATFORM_BASE_URL).toBe("http://localhost:3001");
21
+ expect(mod.PLATFORM_BASE_URL).toBe("https://api.example.com");
67
22
  });
68
23
  });
69
24