@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
@@ -99,111 +99,43 @@ export interface BrandConfig {
99
99
 
100
100
  /** Feature bullet points shown on the billing plans page. */
101
101
  planFeatures: string[];
102
+
103
+ /** Instance detail tabs to hide for this brand (e.g. ["plugins", "channels", "friends"]). */
104
+ hiddenInstanceTabs: string[];
102
105
  }
103
106
 
104
107
  /**
105
- * Build default config from NEXT_PUBLIC_BRAND_* env vars, falling
106
- * back to generic "Platform" values. Each brand deployment sets
107
- * its env vars in .env (or .env.local) and the config picks them up
108
- * at build time no code changes required.
109
- */
110
- /**
111
- * Parse NEXT_PUBLIC_BRAND_NAV_ITEMS env var.
112
- * Format: JSON array of {label, href} objects.
113
- * Example: '[{"label":"Home","href":"/"},{"label":"Settings","href":"/settings"}]'
114
- * Returns null if unset or invalid (falls back to defaults).
108
+ * Static defaults used before initBrandConfig() fetches from the core API.
109
+ * These are intentionally minimal the real config comes from the DB.
110
+ * No process.env references. Brand shells call setBrandConfig() or
111
+ * initBrandConfig() to populate from the core API.
115
112
  */
116
- function parseNavItems(raw: string | undefined): Array<{ label: string; href: string }> | null {
117
- if (!raw) return null;
118
- try {
119
- const parsed = JSON.parse(raw);
120
- if (
121
- Array.isArray(parsed) &&
122
- parsed.every(
123
- (item: unknown) =>
124
- typeof item === "object" &&
125
- item !== null &&
126
- typeof (item as { label?: unknown }).label === "string" &&
127
- typeof (item as { href?: unknown }).href === "string",
128
- )
129
- ) {
130
- return parsed as Array<{ label: string; href: string }>;
131
- }
132
- } catch {
133
- // Invalid JSON — fall back to defaults
134
- }
135
- return null;
136
- }
137
-
138
- /** Parse a JSON string array env var. Returns null if unset/invalid. */
139
- function parseStringArray(raw: string | undefined): string[] | null {
140
- if (!raw) return null;
141
- try {
142
- const parsed = JSON.parse(raw);
143
- if (Array.isArray(parsed) && parsed.every((s: unknown) => typeof s === "string")) {
144
- return parsed as string[];
145
- }
146
- } catch {
147
- // Invalid JSON — fall back to defaults
148
- }
149
- return null;
150
- }
151
-
152
- function envDefaults(): BrandConfig {
153
- // Direct process.env.X access is required — Next.js Turbopack only inlines
154
- // NEXT_PUBLIC_* vars when accessed as literal dot-property references.
155
- // Dynamic access like process.env[key] is NOT inlined at build time.
156
- const productName = process.env.NEXT_PUBLIC_BRAND_PRODUCT_NAME || "Platform";
157
- const brandName = process.env.NEXT_PUBLIC_BRAND_NAME || "Platform";
158
- const storagePrefix = process.env.NEXT_PUBLIC_BRAND_STORAGE_PREFIX || "platform";
159
- const eventPrefix = process.env.NEXT_PUBLIC_BRAND_EVENT_PREFIX || storagePrefix;
113
+ function staticDefaults(): BrandConfig {
160
114
  return {
161
- productName,
162
- brandName,
163
- domain: process.env.NEXT_PUBLIC_BRAND_DOMAIN || "localhost",
164
- appDomain: process.env.NEXT_PUBLIC_BRAND_APP_DOMAIN || "localhost:3000",
165
- tagline: process.env.NEXT_PUBLIC_BRAND_TAGLINE || "Your platform, your rules.",
166
- emails: {
167
- privacy: process.env.NEXT_PUBLIC_BRAND_EMAIL_PRIVACY || "privacy@example.com",
168
- legal: process.env.NEXT_PUBLIC_BRAND_EMAIL_LEGAL || "legal@example.com",
169
- support: process.env.NEXT_PUBLIC_BRAND_EMAIL_SUPPORT || "support@example.com",
170
- },
171
- defaultImage: process.env.NEXT_PUBLIC_BRAND_DEFAULT_IMAGE || "",
172
- storagePrefix,
173
- eventPrefix,
174
- envVarPrefix: process.env.NEXT_PUBLIC_BRAND_ENV_PREFIX || storagePrefix.toUpperCase(),
175
- toolPrefix: process.env.NEXT_PUBLIC_BRAND_TOOL_PREFIX || storagePrefix,
176
- tenantCookieName: process.env.NEXT_PUBLIC_BRAND_TENANT_COOKIE || `${storagePrefix}_tenant_id`,
177
- companyLegalName: process.env.NEXT_PUBLIC_BRAND_COMPANY_LEGAL || "Platform Inc.",
178
- price: process.env.NEXT_PUBLIC_BRAND_PRICE || "",
179
- homePath: process.env.NEXT_PUBLIC_BRAND_HOME_PATH || "/marketplace",
180
- chatEnabled: process.env.NEXT_PUBLIC_BRAND_CHAT_ENABLED !== "false",
181
- dividendsEnabled: process.env.NEXT_PUBLIC_BRAND_DIVIDENDS_ENABLED === "true",
182
- planFeatures: parseStringArray(process.env.NEXT_PUBLIC_BRAND_PLAN_FEATURES) ?? [
183
- "Signup credit included",
184
- "All channels",
185
- "All plugins",
186
- "Hosted AI — no API keys needed",
187
- ],
188
- navItems: parseNavItems(process.env.NEXT_PUBLIC_BRAND_NAV_ITEMS) ?? [
189
- { label: "Dashboard", href: "/dashboard" },
190
- { label: "Chat", href: "/chat" },
191
- { label: "Marketplace", href: "/marketplace" },
192
- { label: "Channels", href: "/channels" },
193
- { label: "Plugins", href: "/plugins" },
194
- { label: "Instances", href: "/instances" },
195
- { label: "Changesets", href: "/changesets" },
196
- { label: "Network", href: "/dashboard/network" },
197
- { label: "Fleet Health", href: "/fleet/health" },
198
- { label: "Credits", href: "/billing/credits" },
199
- { label: "Billing", href: "/billing/plans" },
200
- { label: "Settings", href: "/settings/profile" },
201
- { label: "Admin", href: "/admin/tenants" },
202
- ],
115
+ productName: "Platform",
116
+ brandName: "Platform",
117
+ domain: "localhost",
118
+ appDomain: "localhost:3000",
119
+ tagline: "",
120
+ emails: { privacy: "", legal: "", support: "" },
121
+ defaultImage: "",
122
+ storagePrefix: "platform",
123
+ eventPrefix: "platform",
124
+ envVarPrefix: "PLATFORM",
125
+ toolPrefix: "platform",
126
+ tenantCookieName: "platform_tenant_id",
127
+ companyLegalName: "",
128
+ price: "",
129
+ homePath: "/",
130
+ chatEnabled: true,
131
+ dividendsEnabled: false,
132
+ planFeatures: [],
133
+ hiddenInstanceTabs: [],
134
+ navItems: [],
203
135
  };
204
136
  }
205
137
 
206
- let _config: BrandConfig = envDefaults();
138
+ let _config: BrandConfig = staticDefaults();
207
139
 
208
140
  /**
209
141
  * Set the brand configuration. Call once at app startup
@@ -214,7 +146,7 @@ let _config: BrandConfig = envDefaults();
214
146
  * from the new prefix unless explicitly provided.
215
147
  */
216
148
  export function setBrandConfig(config: Partial<BrandConfig>): void {
217
- const base = { ...envDefaults(), ...config };
149
+ const base = { ...staticDefaults(), ...config };
218
150
  // Re-derive prefix-dependent fields when storagePrefix is overridden
219
151
  // but the dependent fields were not explicitly provided.
220
152
  if (config.storagePrefix) {
@@ -276,31 +208,40 @@ export function envKey(suffix: string): string {
276
208
  }
277
209
 
278
210
  /**
279
- * Fetch brand config from the platform API and apply it.
280
- * Call once in root layout server component.
281
- * Falls back to env var defaults if API unavailable.
211
+ * Fetch brand config from core server and apply it.
212
+ * Call once in root layout server component with the product slug.
213
+ * Falls back to env var defaults if core is unavailable.
214
+ *
215
+ * Uses INTERNAL_API_URL (server-side, private network) with service token auth.
216
+ * The slug determines which product config is returned from the product table.
282
217
  *
283
- * @param apiBaseUrl - The platform API base URL (e.g. from NEXT_PUBLIC_API_URL)
218
+ * @param slug - Product slug (e.g. "paperclip", "wopr", "nemoclaw", "holyship")
284
219
  */
285
- export async function initBrandConfig(apiBaseUrl: string): Promise<void> {
220
+ export async function initBrandConfig(slug?: string): Promise<void> {
221
+ const productSlug = slug ?? process.env.PRODUCT_SLUG ?? process.env.NEXT_PUBLIC_PRODUCT_SLUG;
222
+ if (!productSlug) return; // No slug — env var defaults remain active
223
+
224
+ const coreUrl = process.env.INTERNAL_API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3001";
225
+ const serviceToken = process.env.CORE_SERVICE_TOKEN ?? "";
226
+
286
227
  try {
287
- const res = await fetch(`${apiBaseUrl}/trpc/product.getBrandConfig`, {
288
- credentials: "include",
228
+ const headers: Record<string, string> = {
229
+ "X-Product": productSlug,
230
+ };
231
+ if (serviceToken) {
232
+ headers.Authorization = `Bearer ${serviceToken}`;
233
+ }
234
+
235
+ const res = await fetch(`${coreUrl}/api/products/${encodeURIComponent(productSlug)}`, {
236
+ headers,
289
237
  next: { revalidate: 60 },
290
238
  });
291
239
  if (!res.ok) return;
292
- const text = await res.text();
293
- let json: unknown;
294
- try {
295
- json = JSON.parse(text);
296
- } catch {
297
- return; // Non-JSON response (proxy error, HTML page, etc.)
298
- }
299
- const data = (json as { result?: { data?: unknown } })?.result?.data;
240
+ const data = (await res.json()) as Partial<BrandConfig>;
300
241
  if (data) {
301
- setBrandConfig(data as Partial<BrandConfig>);
242
+ setBrandConfig(data);
302
243
  }
303
244
  } catch {
304
- // API unavailable — env var defaults remain active
245
+ // Core unavailable — env var defaults remain active
305
246
  }
306
247
  }
package/src/lib/brand.ts CHANGED
@@ -105,14 +105,7 @@ export const product = {
105
105
  ],
106
106
 
107
107
  /** What it is NOT. Never use these. */
108
- isNot: [
109
- "A chatbot",
110
- "An AI assistant",
111
- "An automation tool",
112
- "A platform",
113
- "A SaaS product",
114
- "An agent framework",
115
- ],
108
+ isNot: ["A chatbot", "An AI assistant", "An automation tool", "A platform", "A SaaS product", "An agent framework"],
116
109
 
117
110
  /** What it does. Always outcomes, never features. */
118
111
  does: [
@@ -527,13 +520,7 @@ export const animation = {
527
520
  */
528
521
  marketing: {
529
522
  allowed: ["typing-effect", "cursor-blink", "fade-in-on-scroll"],
530
- banned: [
531
- "parallax",
532
- "floating-elements",
533
- "scroll-jacking",
534
- "hover-bounce",
535
- "auto-playing-video",
536
- ],
523
+ banned: ["parallax", "floating-elements", "scroll-jacking", "hover-bounce", "auto-playing-video"],
537
524
  philosophy: "Let the words land. One animation per viewport. The restraint IS the brand.",
538
525
  },
539
526
 
@@ -83,14 +83,9 @@ export function useChat(): UseChatReturn {
83
83
  setMessages((prev) => {
84
84
  const existing = prev.find((m) => m.id === msgId);
85
85
  if (existing) {
86
- return prev.map((m) =>
87
- m.id === msgId ? { ...m, content: m.content + data.delta } : m,
88
- );
86
+ return prev.map((m) => (m.id === msgId ? { ...m, content: m.content + data.delta } : m));
89
87
  }
90
- return [
91
- ...prev,
92
- { id: msgId, role: "bot" as const, content: data.delta, timestamp: Date.now() },
93
- ];
88
+ return [...prev, { id: msgId, role: "bot" as const, content: data.delta, timestamp: Date.now() }];
94
89
  });
95
90
  } else if (data.type === "tool_call") {
96
91
  window.dispatchEvent(
@@ -41,9 +41,7 @@ describe("buildCostComparison", () => {
41
41
  });
42
42
 
43
43
  it("accepts superpowers in the second argument", () => {
44
- const superItem = DIY_COSTS.find((c) =>
45
- ["image-gen", "video-gen", "voice"].includes(c.capabilityId),
46
- );
44
+ const superItem = DIY_COSTS.find((c) => ["image-gen", "video-gen", "voice"].includes(c.capabilityId));
47
45
  if (!superItem) return;
48
46
 
49
47
  const result = buildCostComparison([], [superItem.capabilityId]);
@@ -25,10 +25,7 @@ export const DIY_COSTS: DiyCostItem[] = [
25
25
  .map((s) => ({ capabilityId: s.id, ...s.diyCostData })),
26
26
  ];
27
27
 
28
- export function buildCostComparison(
29
- selectedChannels: string[],
30
- selectedSuperpowers: string[],
31
- ): CostComparisonSummary {
28
+ export function buildCostComparison(selectedChannels: string[], selectedSuperpowers: string[]): CostComparisonSummary {
32
29
  const selectedIds = new Set([...selectedChannels, ...selectedSuperpowers]);
33
30
  const items = DIY_COSTS.filter((c) => selectedIds.has(c.capabilityId));
34
31
 
package/src/lib/errors.ts CHANGED
@@ -42,10 +42,7 @@ export class NetworkError extends AppError {
42
42
  * Extract a user-friendly message from any thrown value.
43
43
  * Use in catch blocks: `setError(toUserMessage(err))`
44
44
  */
45
- export function toUserMessage(
46
- err: unknown,
47
- fallback = "Something went wrong. Please try again.",
48
- ): string {
45
+ export function toUserMessage(err: unknown, fallback = "Something went wrong. Please try again."): string {
49
46
  if (err instanceof Error) return err.message;
50
47
  if (typeof err === "string") return err;
51
48
  return fallback;
@@ -1,6 +1,12 @@
1
1
  import { afterEach, describe, expect, it, vi } from "vitest";
2
2
  import { handleUnauthorized, UnauthorizedError } from "./fetch-utils";
3
3
 
4
+ function flushPromises() {
5
+ return new Promise<void>((resolve) => {
6
+ setTimeout(resolve, 0);
7
+ });
8
+ }
9
+
4
10
  describe("UnauthorizedError", () => {
5
11
  it("has default message", () => {
6
12
  const err = new UnauthorizedError();
@@ -21,32 +27,43 @@ describe("UnauthorizedError", () => {
21
27
  describe("handleUnauthorized", () => {
22
28
  afterEach(() => {
23
29
  vi.restoreAllMocks();
30
+ vi.unstubAllGlobals();
24
31
  });
25
32
 
26
33
  it("always throws UnauthorizedError", () => {
27
34
  expect(() => handleUnauthorized()).toThrow(UnauthorizedError);
28
35
  });
29
36
 
30
- it("sets window.location.href to login URL with callbackUrl", () => {
37
+ it("sets window.location.href to login URL with callbackUrl after async session check", async () => {
38
+ const mockLocation = {
39
+ pathname: "/dashboard",
40
+ search: "?tab=fleet",
41
+ href: "",
42
+ };
31
43
  Object.defineProperty(window, "location", {
32
- value: {
33
- pathname: "/dashboard",
34
- search: "?tab=fleet",
35
- href: "",
36
- },
44
+ value: mockLocation,
37
45
  writable: true,
38
46
  configurable: true,
39
47
  });
40
48
 
49
+ // Mock fetch to return expired session
50
+ vi.stubGlobal(
51
+ "fetch",
52
+ vi.fn().mockResolvedValue({
53
+ json: () => Promise.resolve({ session: null }),
54
+ }),
55
+ );
56
+
41
57
  try {
42
58
  handleUnauthorized();
43
59
  } catch {
44
60
  // expected
45
61
  }
46
62
 
47
- expect(window.location.href).toBe(
48
- "/login?reason=expired&callbackUrl=%2Fdashboard%3Ftab%3Dfleet",
49
- );
63
+ // The redirect happens asynchronously after session check
64
+ await flushPromises();
65
+
66
+ expect(mockLocation.href).toBe("/login?reason=expired&callbackUrl=%2Fdashboard%3Ftab%3Dfleet");
50
67
 
51
68
  Object.defineProperty(window, "location", {
52
69
  value: { pathname: "/", search: "", href: "" },
@@ -3,6 +3,8 @@
3
3
  * Thrown after triggering a redirect to /login, so call sites can
4
4
  * identify auth failures if they catch before the redirect completes.
5
5
  */
6
+ import { API_BASE_URL } from "./api-config";
7
+
6
8
  export class UnauthorizedError extends Error {
7
9
  constructor(message = "Session expired") {
8
10
  super(message);
@@ -10,21 +12,48 @@ export class UnauthorizedError extends Error {
10
12
  }
11
13
  }
12
14
 
15
+ /** Track whether we're already checking the session to avoid loops */
16
+ let sessionCheckInFlight = false;
17
+
13
18
  /**
14
- * Handle a 401 response by redirecting to /login with a callbackUrl.
15
- * Only runs on the client (checks `typeof window`).
16
- * Throws UnauthorizedError after initiating redirect.
19
+ * Handle a 401 response intelligently.
20
+ *
21
+ * Instead of blindly assuming "session expired" on every 401:
22
+ * 1. Check if the session is actually valid by hitting the auth endpoint.
23
+ * Uses API_BASE_URL (api.<domain>/api) — hitting the shell-relative
24
+ * `/api/auth/get-session` returns HTML (no shell route) → JSON parse
25
+ * fails → the catch arm would redirect every single 401, even when
26
+ * the session is fine. That broke any page that called an endpoint
27
+ * returning 401 for non-session reasons (e.g., billing/dividend/stats
28
+ * which requires a service token).
29
+ * 2. If session IS valid — the 401 was from a permission/routing issue, not expiry.
30
+ * Log a warning and throw (let the caller handle it) but DON'T redirect.
31
+ * 3. If session is truly expired — redirect to /login.
17
32
  *
18
- * Guards against redirect loops: if already on /login, does NOT redirect
19
- * again — just throws so the login page can handle the error state.
33
+ * Guards against redirect loops: if already on /login or already checking, just throws.
20
34
  */
21
35
  export function handleUnauthorized(): never {
22
- if (typeof window !== "undefined") {
23
- if (!window.location.pathname.startsWith("/login")) {
24
- const callbackUrl = window.location.pathname + window.location.search;
25
- const loginUrl = `/login?reason=expired&callbackUrl=${encodeURIComponent(callbackUrl)}`;
26
- window.location.href = loginUrl;
27
- }
36
+ if (typeof window !== "undefined" && !window.location.pathname.startsWith("/login") && !sessionCheckInFlight) {
37
+ sessionCheckInFlight = true;
38
+ fetch(`${API_BASE_URL}/auth/get-session`, { credentials: "include" })
39
+ .then((res) => res.json())
40
+ .then((data) => {
41
+ sessionCheckInFlight = false;
42
+ if (data?.session) {
43
+ // Session IS valid — 401 was a permission/routing issue, not expiry.
44
+ // Do NOT redirect. Caller's UnauthorizedError throw stands.
45
+ } else {
46
+ const callbackUrl = window.location.pathname + window.location.search;
47
+ const loginUrl = `/login?reason=expired&callbackUrl=${encodeURIComponent(callbackUrl)}`;
48
+ window.location.href = loginUrl;
49
+ }
50
+ })
51
+ .catch((_err) => {
52
+ sessionCheckInFlight = false;
53
+ const callbackUrl = window.location.pathname + window.location.search;
54
+ const loginUrl = `/login?reason=expired&callbackUrl=${encodeURIComponent(callbackUrl)}`;
55
+ window.location.href = loginUrl;
56
+ });
28
57
  }
29
58
  throw new UnauthorizedError();
30
59
  }
package/src/lib/logger.ts CHANGED
@@ -19,9 +19,11 @@ export interface Logger {
19
19
  export function logger(namespace: string): Logger {
20
20
  return {
21
21
  warn(message: string, ...args: unknown[]) {
22
+ // biome-ignore lint/suspicious/noConsole: logger is the approved console wrapper
22
23
  console.warn(`[${namespace}] ${message}`, ...args);
23
24
  },
24
25
  error(message: string, ...args: unknown[]) {
26
+ // biome-ignore lint/suspicious/noConsole: logger is the approved console wrapper
25
27
  console.error(`[${namespace}] ${message}`, ...args);
26
28
  },
27
29
  };
@@ -86,9 +86,7 @@ const pluginManifestSchema = z.object({
86
86
  configSchema: z.array(configSchemaFieldSchema).default([]),
87
87
  setup: z.array(setupStepSchema).default([]),
88
88
  installCount: z.number().default(0),
89
- changelog: z
90
- .array(z.object({ version: z.string(), date: z.string(), notes: z.string() }))
91
- .default([]),
89
+ changelog: z.array(z.object({ version: z.string(), date: z.string(), notes: z.string() })).default([]),
92
90
  connectionTest: z.object({ label: z.string(), endpoint: z.string() }).optional(),
93
91
  superpowerHeadline: z.string().optional(),
94
92
  superpowerTagline: z.string().optional(),
@@ -273,9 +271,7 @@ export async function installPlugin(
273
271
  }
274
272
 
275
273
  /** Fetch installed plugins for a bot, with enabled state. */
276
- export async function listInstalledPlugins(
277
- botId: string,
278
- ): Promise<{ pluginId: string; enabled: boolean }[]> {
274
+ export async function listInstalledPlugins(botId: string): Promise<{ pluginId: string; enabled: boolean }[]> {
279
275
  const data = await fleetFetch<{
280
276
  botId: string;
281
277
  plugins: { pluginId: string; enabled: boolean }[];
@@ -284,11 +280,7 @@ export async function listInstalledPlugins(
284
280
  }
285
281
 
286
282
  /** Toggle a plugin's enabled state on a bot. */
287
- export async function togglePluginEnabled(
288
- botId: string,
289
- pluginId: string,
290
- enabled: boolean,
291
- ): Promise<void> {
283
+ export async function togglePluginEnabled(botId: string, pluginId: string, enabled: boolean): Promise<void> {
292
284
  await fleetFetch(`/bots/${botId}/plugins/${pluginId}`, {
293
285
  method: "PATCH",
294
286
  body: JSON.stringify({ enabled }),
@@ -1,10 +1,8 @@
1
1
  const OAUTH_ERROR_MESSAGES: Record<string, string> = {
2
2
  access_denied: "Access was denied. Please try again.",
3
- account_already_linked:
4
- "An account with this email already exists. Sign in to link your account.",
3
+ account_already_linked: "An account with this email already exists. Sign in to link your account.",
5
4
  server_error: "The authentication server encountered an error. Please try again later.",
6
- temporarily_unavailable:
7
- "The authentication service is temporarily unavailable. Please try again later.",
5
+ temporarily_unavailable: "The authentication service is temporarily unavailable. Please try again later.",
8
6
  invalid_request: "The authentication request was invalid. Please try again.",
9
7
  unauthorized_client: "This application is not authorized. Please contact support.",
10
8
  unsupported_response_type: "Authentication configuration error. Please contact support.",
@@ -256,10 +256,7 @@ export interface Preset {
256
256
  // marketplace configSchema fields. Kept separate intentionally — they serve
257
257
  // the onboarding BYOK flow, not the marketplace install wizard.
258
258
  // diyCostData is co-located here so adding a new channel only requires one entry.
259
- const CHANNEL_OVERLAY: Record<
260
- string,
261
- { configFields: OnboardingConfigField[]; diyCostData?: DiyCostData }
262
- > = {
259
+ const CHANNEL_OVERLAY: Record<string, { configFields: OnboardingConfigField[]; diyCostData?: DiyCostData }> = {
263
260
  discord: {
264
261
  configFields: [
265
262
  {
@@ -822,9 +819,7 @@ export async function getOptionalPlugins(): Promise<PluginCategory[]> {
822
819
  const knownCats = new Set<string>(optionalCategories);
823
820
  const extraCats = new Set(
824
821
  plugins
825
- .filter(
826
- (m) => !knownCats.has(m.category) && m.category !== "channel" && m.category !== "provider",
827
- )
822
+ .filter((m) => !knownCats.has(m.category) && m.category !== "channel" && m.category !== "provider")
828
823
  .map((m) => m.category),
829
824
  );
830
825
  for (const catId of extraCats) {
@@ -1225,10 +1220,7 @@ export function getAiKeySuperpowers(selectedIds: string[]): Superpower[] {
1225
1220
  }
1226
1221
 
1227
1222
  /** Capability descriptions shown after key validation */
1228
- export const AI_CAPABILITY_DESCRIPTIONS: Record<
1229
- string,
1230
- { label: string; openai: string; openrouter: string }
1231
- > = {
1223
+ export const AI_CAPABILITY_DESCRIPTIONS: Record<string, { label: string; openai: string; openrouter: string }> = {
1232
1224
  memory: {
1233
1225
  label: "Memory",
1234
1226
  openai: "Embeddings for long-term recall",
@@ -25,11 +25,7 @@ export async function updateOrganization(
25
25
  return trpcVanilla.org.updateOrganization.mutate({ orgId, ...data });
26
26
  }
27
27
 
28
- export async function inviteMember(
29
- orgId: string,
30
- email: string,
31
- role: "admin" | "member",
32
- ): Promise<OrgInvite> {
28
+ export async function inviteMember(orgId: string, email: string, role: "admin" | "member"): Promise<OrgInvite> {
33
29
  const row = await trpcVanilla.org.inviteMember.mutate({ orgId, email, role });
34
30
  const typed = row as OrgInviteRow;
35
31
  return {
@@ -46,11 +42,7 @@ export async function revokeInvite(orgId: string, inviteId: string): Promise<voi
46
42
  await trpcVanilla.org.revokeInvite.mutate({ orgId, inviteId });
47
43
  }
48
44
 
49
- export async function changeRole(
50
- orgId: string,
51
- userId: string,
52
- role: "admin" | "member",
53
- ): Promise<void> {
45
+ export async function changeRole(orgId: string, userId: string, role: "admin" | "member"): Promise<void> {
54
46
  await trpcVanilla.org.changeRole.mutate({ orgId, userId, role });
55
47
  }
56
48
 
@@ -22,11 +22,8 @@ export interface OrgMemberUsageRow {
22
22
  export async function getOrgCreditBalance(_orgId: string): Promise<OrgCreditBalance> {
23
23
  const res = await trpcVanilla.billing.creditsBalance.query({});
24
24
  return {
25
- balance:
26
- (res?.balance_credits ?? (res as { balance_cents?: number })?.balance_cents ?? 0) / 100,
27
- dailyBurn:
28
- (res?.daily_burn_credits ?? (res as { daily_burn_cents?: number })?.daily_burn_cents ?? 0) /
29
- 100,
25
+ balance: (res?.balance_credits ?? (res as { balance_cents?: number })?.balance_cents ?? 0) / 100,
26
+ dailyBurn: (res?.daily_burn_credits ?? (res as { daily_burn_cents?: number })?.daily_burn_cents ?? 0) / 100,
30
27
  runway: (res as { runway_days?: number | null })?.runway_days ?? null,
31
28
  };
32
29
  }
@@ -66,26 +63,15 @@ export async function getOrgBillingInfo(_orgId: string) {
66
63
  }
67
64
  }
68
65
 
69
- export async function createOrgTopupCheckout(
70
- orgId: string,
71
- priceId: string,
72
- successUrl: string,
73
- cancelUrl: string,
74
- ) {
66
+ export async function createOrgTopupCheckout(orgId: string, priceId: string, successUrl: string, cancelUrl: string) {
75
67
  return trpcVanilla.org.orgTopupCheckout.mutate({ orgId, priceId, successUrl, cancelUrl });
76
68
  }
77
69
 
78
- export async function removeOrgPaymentMethod(
79
- orgId: string,
80
- paymentMethodId: string,
81
- ): Promise<{ removed: boolean }> {
70
+ export async function removeOrgPaymentMethod(orgId: string, paymentMethodId: string): Promise<{ removed: boolean }> {
82
71
  return trpcVanilla.org.orgRemovePaymentMethod.mutate({ orgId, paymentMethodId });
83
72
  }
84
73
 
85
- export async function setOrgDefaultPaymentMethod(
86
- orgId: string,
87
- paymentMethodId: string,
88
- ): Promise<void> {
74
+ export async function setOrgDefaultPaymentMethod(orgId: string, paymentMethodId: string): Promise<void> {
89
75
  await trpcVanilla.org.orgSetDefaultPaymentMethod.mutate({ orgId, paymentMethodId });
90
76
  }
91
77
 
@@ -232,8 +232,7 @@ export function getPlatformUIToolDefinitions(): ToolDefinition[] {
232
232
  // Plugin setup tools
233
233
  {
234
234
  name: "setup.begin",
235
- description:
236
- "Begin conversational setup for a plugin. Bot receives plugin ID and config schema.",
235
+ description: "Begin conversational setup for a plugin. Bot receives plugin ID and config schema.",
237
236
  inputSchema: {
238
237
  type: "object",
239
238
  properties: {