@wopr-network/platform-ui-core 1.0.0

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 (543) hide show
  1. package/.env.paperclip +18 -0
  2. package/.env.wopr +18 -0
  3. package/README.md +36 -0
  4. package/biome.json +52 -0
  5. package/next.config.ts +45 -0
  6. package/package.json +84 -0
  7. package/postcss.config.mjs +7 -0
  8. package/public/file.svg +1 -0
  9. package/public/globe.svg +1 -0
  10. package/public/window.svg +1 -0
  11. package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +741 -0
  12. package/src/__tests__/account-page-redirect.test.tsx +73 -0
  13. package/src/__tests__/account-switcher.test.tsx +85 -0
  14. package/src/__tests__/activity-page.test.tsx +176 -0
  15. package/src/__tests__/add-payment-method-dialog.test.tsx +160 -0
  16. package/src/__tests__/admin-api.test.ts +244 -0
  17. package/src/__tests__/admin-gpu-api.test.ts +188 -0
  18. package/src/__tests__/admin-guard.test.tsx +79 -0
  19. package/src/__tests__/admin-marketplace-api.test.ts +179 -0
  20. package/src/__tests__/admin-middleware.test.ts +157 -0
  21. package/src/__tests__/admin-tenant-table.test.tsx +95 -0
  22. package/src/__tests__/affiliate-dashboard.test.tsx +178 -0
  23. package/src/__tests__/api-401-redirect.test.ts +78 -0
  24. package/src/__tests__/api-client.test.ts +316 -0
  25. package/src/__tests__/api-config.test.ts +89 -0
  26. package/src/__tests__/api-control-instance.test.ts +69 -0
  27. package/src/__tests__/api-fleet-resources.test.ts +52 -0
  28. package/src/__tests__/api-fleet-trpc.test.ts +252 -0
  29. package/src/__tests__/api-get-instance-config.test.ts +41 -0
  30. package/src/__tests__/api-null-guards.test.ts +244 -0
  31. package/src/__tests__/api-rename-instance.test.ts +60 -0
  32. package/src/__tests__/api-update-instance-config.test.ts +60 -0
  33. package/src/__tests__/audit-log-table-pagination.test.tsx +136 -0
  34. package/src/__tests__/auth-client.test.ts +87 -0
  35. package/src/__tests__/auth-password-reset.test.tsx +435 -0
  36. package/src/__tests__/auth-redirect.test.tsx +60 -0
  37. package/src/__tests__/auth.test.tsx +269 -0
  38. package/src/__tests__/auto-topup-card.test.tsx +257 -0
  39. package/src/__tests__/backups-tab.test.tsx +221 -0
  40. package/src/__tests__/billing-byok-callout.test.tsx +76 -0
  41. package/src/__tests__/billing-layout-nav-hidden.test.tsx +47 -0
  42. package/src/__tests__/billing-payment-org-invoices.test.tsx +123 -0
  43. package/src/__tests__/billing.test.tsx +509 -0
  44. package/src/__tests__/bot-settings/resources-tab.test.tsx +119 -0
  45. package/src/__tests__/bot-settings/storage-tab.test.tsx +80 -0
  46. package/src/__tests__/bot-settings/vps-info-panel.test.tsx +108 -0
  47. package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +52 -0
  48. package/src/__tests__/bot-settings-data-control.test.ts +49 -0
  49. package/src/__tests__/bot-settings-restart.test.tsx +149 -0
  50. package/src/__tests__/bot-settings.test.tsx +678 -0
  51. package/src/__tests__/brand.test.ts +335 -0
  52. package/src/__tests__/buy-credits-panel.test.tsx +249 -0
  53. package/src/__tests__/buy-crypto-credits-panel.test.tsx +178 -0
  54. package/src/__tests__/capability-conflicts.test.ts +88 -0
  55. package/src/__tests__/capability-resolver.test.tsx +173 -0
  56. package/src/__tests__/changeset-detail.test.tsx +156 -0
  57. package/src/__tests__/channel-setup-logger.test.ts +22 -0
  58. package/src/__tests__/channel-setup-toast.test.tsx +60 -0
  59. package/src/__tests__/channel-wizard.test.tsx +505 -0
  60. package/src/__tests__/chat/ambient-dot.test.tsx +35 -0
  61. package/src/__tests__/chat/chat-input.test.tsx +78 -0
  62. package/src/__tests__/chat/chat-message.test.tsx +45 -0
  63. package/src/__tests__/chat/chat-panel.test.tsx +111 -0
  64. package/src/__tests__/chat/chat-widget.test.tsx +82 -0
  65. package/src/__tests__/chat-store.test.ts +87 -0
  66. package/src/__tests__/command-center.test.tsx +246 -0
  67. package/src/__tests__/compliance-retention-edit.test.tsx +134 -0
  68. package/src/__tests__/coupon-input.test.tsx +119 -0
  69. package/src/__tests__/create-instance.test.tsx +96 -0
  70. package/src/__tests__/create-org-wizard.test.tsx +200 -0
  71. package/src/__tests__/credit-balance.test.tsx +103 -0
  72. package/src/__tests__/credits.test.tsx +376 -0
  73. package/src/__tests__/csrf-middleware.test.ts +198 -0
  74. package/src/__tests__/degraded-state-banner.test.tsx +130 -0
  75. package/src/__tests__/dividend-calculator.test.tsx +20 -0
  76. package/src/__tests__/dividend-stats.test.tsx +64 -0
  77. package/src/__tests__/dividend.test.tsx +169 -0
  78. package/src/__tests__/dockerfile.test.ts +110 -0
  79. package/src/__tests__/email-verification-banner.test.tsx +64 -0
  80. package/src/__tests__/env-example.test.ts +25 -0
  81. package/src/__tests__/error-boundaries.test.tsx +64 -0
  82. package/src/__tests__/fetch-pricing.test.ts +121 -0
  83. package/src/__tests__/field-oauth.test.tsx +302 -0
  84. package/src/__tests__/fixtures/mock-manifests-data.js +372 -0
  85. package/src/__tests__/fixtures/mock-manifests.ts +24 -0
  86. package/src/__tests__/fleet-health-timestamp.test.tsx +101 -0
  87. package/src/__tests__/fleet-health-update.test.tsx +83 -0
  88. package/src/__tests__/format-credit.test.ts +58 -0
  89. package/src/__tests__/gpu-dashboard.test.tsx +236 -0
  90. package/src/__tests__/hosted-usage-date-range.test.tsx +54 -0
  91. package/src/__tests__/instance-detail.test.tsx +571 -0
  92. package/src/__tests__/instance-list.test.tsx +230 -0
  93. package/src/__tests__/landing-hero.test.tsx +27 -0
  94. package/src/__tests__/landing-nav.test.tsx +24 -0
  95. package/src/__tests__/layout-snapshots.test.tsx +167 -0
  96. package/src/__tests__/logger.test.ts +54 -0
  97. package/src/__tests__/login-page-redirect.test.tsx +142 -0
  98. package/src/__tests__/manifest-validation.test.ts +126 -0
  99. package/src/__tests__/marketplace-admin.test.tsx +151 -0
  100. package/src/__tests__/marketplace.test.tsx +609 -0
  101. package/src/__tests__/merge-api-rates.test.ts +70 -0
  102. package/src/__tests__/middleware.test.ts +690 -0
  103. package/src/__tests__/network-page.test.tsx +100 -0
  104. package/src/__tests__/next-config-headers.test.ts +28 -0
  105. package/src/__tests__/not-found.test.tsx +26 -0
  106. package/src/__tests__/notifications.test.tsx +128 -0
  107. package/src/__tests__/oauth-buttons.test.tsx +101 -0
  108. package/src/__tests__/oauth-error-mapping.test.tsx +97 -0
  109. package/src/__tests__/observability.test.tsx +541 -0
  110. package/src/__tests__/onboarding-data.test.ts +363 -0
  111. package/src/__tests__/onboarding-page.test.tsx +113 -0
  112. package/src/__tests__/onboarding-store.test.ts +121 -0
  113. package/src/__tests__/org-billing-api.test.tsx +70 -0
  114. package/src/__tests__/org-billing-null-guards.test.ts +64 -0
  115. package/src/__tests__/org-billing-page.test.tsx +124 -0
  116. package/src/__tests__/plugin-definition.test.ts +43 -0
  117. package/src/__tests__/plugin-install-flow.test.tsx +535 -0
  118. package/src/__tests__/plugin-registry.test.tsx +475 -0
  119. package/src/__tests__/plugin-setup/setup-chat-panel.test.ts +142 -0
  120. package/src/__tests__/plugin-setup/use-plugin-setup-chat.test.ts +49 -0
  121. package/src/__tests__/plugin-tool-definitions.test.ts +51 -0
  122. package/src/__tests__/plugin-tool-sync.test.ts +59 -0
  123. package/src/__tests__/portfolio-chart.test.tsx +24 -0
  124. package/src/__tests__/pricing.test.tsx +107 -0
  125. package/src/__tests__/promotion-form.test.tsx +180 -0
  126. package/src/__tests__/promotions-list.test.tsx +194 -0
  127. package/src/__tests__/provider-key-api.test.ts +134 -0
  128. package/src/__tests__/resend-verification-button.test.tsx +104 -0
  129. package/src/__tests__/sanitize-redirect-url.test.ts +47 -0
  130. package/src/__tests__/secrets-audit-pagination.test.tsx +139 -0
  131. package/src/__tests__/settings.test.tsx +937 -0
  132. package/src/__tests__/setup-checklist.test.tsx +274 -0
  133. package/src/__tests__/setup.ts +82 -0
  134. package/src/__tests__/smoke.test.tsx +10 -0
  135. package/src/__tests__/snapshot-api.test.ts +104 -0
  136. package/src/__tests__/status-api.test.ts +46 -0
  137. package/src/__tests__/status-badge.test.tsx +33 -0
  138. package/src/__tests__/status-colors.test.ts +83 -0
  139. package/src/__tests__/status-page.test.tsx +86 -0
  140. package/src/__tests__/step-superpowers.test.tsx +218 -0
  141. package/src/__tests__/story-sections.test.tsx +24 -0
  142. package/src/__tests__/superpower-content-sanitize.test.tsx +87 -0
  143. package/src/__tests__/superpower-content.test.tsx +44 -0
  144. package/src/__tests__/suspension-banner.test.tsx +140 -0
  145. package/src/__tests__/tenant-context.test.tsx +146 -0
  146. package/src/__tests__/tenant-keys-api.test.ts +114 -0
  147. package/src/__tests__/tenant-table-pagination.test.tsx +124 -0
  148. package/src/__tests__/terminal-log-cleanup.test.tsx +51 -0
  149. package/src/__tests__/terminal-sequence.test.tsx +28 -0
  150. package/src/__tests__/transaction-history.test.tsx +325 -0
  151. package/src/__tests__/trpc-types.test.ts +102 -0
  152. package/src/__tests__/use-capability-meta.test.ts +161 -0
  153. package/src/__tests__/use-chat.test.ts +616 -0
  154. package/src/__tests__/use-has-org.test.ts +44 -0
  155. package/src/__tests__/use-image-status.test.ts +77 -0
  156. package/src/__tests__/use-pagination-params.test.ts +88 -0
  157. package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +53 -0
  158. package/src/__tests__/use-webmcp.test.ts +119 -0
  159. package/src/__tests__/validate-elevenlabs-key.test.ts +95 -0
  160. package/src/__tests__/validate-redirect-url.test.ts +61 -0
  161. package/src/__tests__/verify-page.test.tsx +140 -0
  162. package/src/__tests__/verify-redirect.test.tsx +41 -0
  163. package/src/__tests__/verify-result-banner.test.tsx +66 -0
  164. package/src/__tests__/webmcp-feature-detect.test.ts +54 -0
  165. package/src/__tests__/webmcp-hook.test.tsx +72 -0
  166. package/src/__tests__/webmcp-marketplace-onboarding-tools.test.ts +185 -0
  167. package/src/__tests__/webmcp-register.test.ts +103 -0
  168. package/src/__tests__/webmcp-set-provider.test.ts +47 -0
  169. package/src/__tests__/webmcp-tools.test.ts +348 -0
  170. package/src/app/(auth)/error.tsx +72 -0
  171. package/src/app/(auth)/forgot-password/page.tsx +137 -0
  172. package/src/app/(auth)/layout.tsx +14 -0
  173. package/src/app/(auth)/loading.tsx +26 -0
  174. package/src/app/(auth)/login/page.tsx +188 -0
  175. package/src/app/(auth)/reset-password/page.tsx +169 -0
  176. package/src/app/(auth)/signup/page.tsx +309 -0
  177. package/src/app/(dashboard)/billing/credits/page.tsx +209 -0
  178. package/src/app/(dashboard)/billing/error.tsx +72 -0
  179. package/src/app/(dashboard)/billing/layout.tsx +73 -0
  180. package/src/app/(dashboard)/billing/loading.tsx +41 -0
  181. package/src/app/(dashboard)/billing/payment/page.tsx +639 -0
  182. package/src/app/(dashboard)/billing/plans/page.tsx +58 -0
  183. package/src/app/(dashboard)/billing/referrals/page.tsx +7 -0
  184. package/src/app/(dashboard)/billing/usage/hosted/page.tsx +348 -0
  185. package/src/app/(dashboard)/billing/usage/page.tsx +663 -0
  186. package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +400 -0
  187. package/src/app/(dashboard)/changesets/[id]/error.tsx +57 -0
  188. package/src/app/(dashboard)/changesets/[id]/loading.tsx +23 -0
  189. package/src/app/(dashboard)/changesets/[id]/page.tsx +10 -0
  190. package/src/app/(dashboard)/changesets/error.tsx +72 -0
  191. package/src/app/(dashboard)/changesets/page.tsx +10 -0
  192. package/src/app/(dashboard)/chat/page.tsx +74 -0
  193. package/src/app/(dashboard)/dashboard/bots/[id]/settings/page.tsx +10 -0
  194. package/src/app/(dashboard)/dashboard/network/page.tsx +97 -0
  195. package/src/app/(dashboard)/dashboard/page.tsx +13 -0
  196. package/src/app/(dashboard)/error.tsx +72 -0
  197. package/src/app/(dashboard)/layout.tsx +113 -0
  198. package/src/app/(dashboard)/loading.tsx +27 -0
  199. package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +548 -0
  200. package/src/app/(dashboard)/marketplace/error.tsx +72 -0
  201. package/src/app/(dashboard)/marketplace/loading.tsx +27 -0
  202. package/src/app/(dashboard)/marketplace/page.tsx +268 -0
  203. package/src/app/(dashboard)/not-found.tsx +46 -0
  204. package/src/app/(dashboard)/onboarding/page.tsx +267 -0
  205. package/src/app/(dashboard)/settings/account/page.tsx +132 -0
  206. package/src/app/(dashboard)/settings/activity/page.tsx +280 -0
  207. package/src/app/(dashboard)/settings/api-keys/page.tsx +530 -0
  208. package/src/app/(dashboard)/settings/brain/page.tsx +412 -0
  209. package/src/app/(dashboard)/settings/error.tsx +72 -0
  210. package/src/app/(dashboard)/settings/layout.tsx +114 -0
  211. package/src/app/(dashboard)/settings/loading.tsx +31 -0
  212. package/src/app/(dashboard)/settings/notifications/page.tsx +216 -0
  213. package/src/app/(dashboard)/settings/org/page.tsx +617 -0
  214. package/src/app/(dashboard)/settings/profile/page.tsx +510 -0
  215. package/src/app/(dashboard)/settings/providers/page.tsx +842 -0
  216. package/src/app/(dashboard)/settings/secrets/page.tsx +658 -0
  217. package/src/app/(dashboard)/settings/security/page.tsx +1133 -0
  218. package/src/app/admin/accounting/loading.tsx +32 -0
  219. package/src/app/admin/accounting/page.tsx +5 -0
  220. package/src/app/admin/affiliates/loading.tsx +32 -0
  221. package/src/app/admin/affiliates/page.tsx +5 -0
  222. package/src/app/admin/audit/loading.tsx +32 -0
  223. package/src/app/admin/audit/page.tsx +5 -0
  224. package/src/app/admin/billing-health/loading.tsx +17 -0
  225. package/src/app/admin/billing-health/page.tsx +10 -0
  226. package/src/app/admin/compliance/page.tsx +5 -0
  227. package/src/app/admin/error.tsx +72 -0
  228. package/src/app/admin/gpu/loading.tsx +38 -0
  229. package/src/app/admin/gpu/page.tsx +5 -0
  230. package/src/app/admin/incidents/page.tsx +10 -0
  231. package/src/app/admin/inference/loading.tsx +32 -0
  232. package/src/app/admin/inference/page.tsx +5 -0
  233. package/src/app/admin/layout.tsx +44 -0
  234. package/src/app/admin/loading.tsx +32 -0
  235. package/src/app/admin/marketplace/loading.tsx +32 -0
  236. package/src/app/admin/marketplace/page.tsx +5 -0
  237. package/src/app/admin/migrations/loading.tsx +22 -0
  238. package/src/app/admin/migrations/page.tsx +5 -0
  239. package/src/app/admin/onboarding/loading.tsx +18 -0
  240. package/src/app/admin/onboarding/page.tsx +5 -0
  241. package/src/app/admin/promotions/[id]/edit/loading.tsx +16 -0
  242. package/src/app/admin/promotions/[id]/edit/page.tsx +56 -0
  243. package/src/app/admin/promotions/[id]/loading.tsx +15 -0
  244. package/src/app/admin/promotions/[id]/page.tsx +311 -0
  245. package/src/app/admin/promotions/loading.tsx +21 -0
  246. package/src/app/admin/promotions/new/loading.tsx +16 -0
  247. package/src/app/admin/promotions/new/page.tsx +12 -0
  248. package/src/app/admin/promotions/page.tsx +266 -0
  249. package/src/app/admin/rate-overrides/loading.tsx +17 -0
  250. package/src/app/admin/rate-overrides/page.tsx +290 -0
  251. package/src/app/admin/roles/loading.tsx +27 -0
  252. package/src/app/admin/roles/page.tsx +5 -0
  253. package/src/app/admin/tenants/loading.tsx +32 -0
  254. package/src/app/admin/tenants/page.tsx +5 -0
  255. package/src/app/apple-icon.tsx +32 -0
  256. package/src/app/auth/callback/[provider]/page.tsx +104 -0
  257. package/src/app/auth/verify/page.tsx +224 -0
  258. package/src/app/channels/error.tsx +72 -0
  259. package/src/app/channels/loading.tsx +29 -0
  260. package/src/app/channels/page.tsx +262 -0
  261. package/src/app/channels/setup/[plugin]/page.tsx +136 -0
  262. package/src/app/error.tsx +72 -0
  263. package/src/app/favicon.ico +0 -0
  264. package/src/app/fleet/error.tsx +72 -0
  265. package/src/app/fleet/health/page.tsx +9 -0
  266. package/src/app/fleet/layout.tsx +14 -0
  267. package/src/app/fleet/loading.tsx +33 -0
  268. package/src/app/fleet/page.tsx +5 -0
  269. package/src/app/global-error.tsx +96 -0
  270. package/src/app/globals.css +251 -0
  271. package/src/app/icon.svg +4 -0
  272. package/src/app/instances/[id]/instance-detail-client.tsx +1298 -0
  273. package/src/app/instances/[id]/page.tsx +10 -0
  274. package/src/app/instances/error.tsx +72 -0
  275. package/src/app/instances/instance-list-client.tsx +540 -0
  276. package/src/app/instances/loading.tsx +33 -0
  277. package/src/app/instances/new/create-instance-client.tsx +377 -0
  278. package/src/app/instances/new/page.tsx +9 -0
  279. package/src/app/instances/page.tsx +9 -0
  280. package/src/app/layout.tsx +83 -0
  281. package/src/app/not-found.tsx +38 -0
  282. package/src/app/og/route.tsx +50 -0
  283. package/src/app/page.tsx +39 -0
  284. package/src/app/plugins/error.tsx +72 -0
  285. package/src/app/plugins/layout.tsx +14 -0
  286. package/src/app/plugins/loading.tsx +30 -0
  287. package/src/app/plugins/page.tsx +555 -0
  288. package/src/app/pricing/error.tsx +72 -0
  289. package/src/app/pricing/loading.tsx +25 -0
  290. package/src/app/pricing/page.tsx +20 -0
  291. package/src/app/privacy/page.tsx +406 -0
  292. package/src/app/robots.ts +9 -0
  293. package/src/app/sitemap.ts +11 -0
  294. package/src/app/status/error.tsx +72 -0
  295. package/src/app/status/loading.tsx +21 -0
  296. package/src/app/status/page.tsx +20 -0
  297. package/src/app/terms/page.tsx +414 -0
  298. package/src/components/account-switcher.tsx +82 -0
  299. package/src/components/admin/accounting-dashboard.tsx +190 -0
  300. package/src/components/admin/admin-guard.tsx +36 -0
  301. package/src/components/admin/admin-nav.tsx +71 -0
  302. package/src/components/admin/affiliate-dashboard.tsx +564 -0
  303. package/src/components/admin/audit-log-table.tsx +336 -0
  304. package/src/components/admin/billing-health-dashboard.test.tsx +40 -0
  305. package/src/components/admin/billing-health-dashboard.tsx +416 -0
  306. package/src/components/admin/bulk-actions-bar.test.tsx +92 -0
  307. package/src/components/admin/bulk-actions-bar.tsx +80 -0
  308. package/src/components/admin/bulk-export-dialog.test.tsx +75 -0
  309. package/src/components/admin/bulk-export-dialog.tsx +189 -0
  310. package/src/components/admin/bulk-grant-dialog.test.tsx +81 -0
  311. package/src/components/admin/bulk-grant-dialog.tsx +147 -0
  312. package/src/components/admin/bulk-preview-dialog.test.tsx +72 -0
  313. package/src/components/admin/bulk-preview-dialog.tsx +106 -0
  314. package/src/components/admin/bulk-reactivate-dialog.test.tsx +51 -0
  315. package/src/components/admin/bulk-reactivate-dialog.tsx +55 -0
  316. package/src/components/admin/bulk-select-all-banner.test.tsx +36 -0
  317. package/src/components/admin/bulk-select-all-banner.tsx +44 -0
  318. package/src/components/admin/bulk-suspend-dialog.test.tsx +77 -0
  319. package/src/components/admin/bulk-suspend-dialog.tsx +129 -0
  320. package/src/components/admin/bulk-undo-toast.test.tsx +66 -0
  321. package/src/components/admin/bulk-undo-toast.tsx +121 -0
  322. package/src/components/admin/compliance-dashboard.tsx +1341 -0
  323. package/src/components/admin/gpu-dashboard.tsx +552 -0
  324. package/src/components/admin/grant-credits-dialog.tsx +121 -0
  325. package/src/components/admin/incident-dashboard.test.tsx +44 -0
  326. package/src/components/admin/incident-dashboard.tsx +717 -0
  327. package/src/components/admin/inference-dashboard.tsx +415 -0
  328. package/src/components/admin/marketplace-admin.tsx +765 -0
  329. package/src/components/admin/migrations-dashboard.tsx +404 -0
  330. package/src/components/admin/onboarding-dashboard.tsx +422 -0
  331. package/src/components/admin/promotions/promotion-form.tsx +440 -0
  332. package/src/components/admin/roles-dashboard.tsx +278 -0
  333. package/src/components/admin/suspend-dialog.tsx +98 -0
  334. package/src/components/admin/tenant-notes-panel.tsx +134 -0
  335. package/src/components/admin/tenant-row-actions.tsx +78 -0
  336. package/src/components/admin/tenant-table.tsx +339 -0
  337. package/src/components/auth/auth-error.tsx +22 -0
  338. package/src/components/auth/auth-redirect.tsx +18 -0
  339. package/src/components/auth/auth-shell.tsx +25 -0
  340. package/src/components/auth/email-verification-banner.tsx +25 -0
  341. package/src/components/auth/email-verification-result-banner.tsx +70 -0
  342. package/src/components/auth/resend-verification-button.tsx +94 -0
  343. package/src/components/auth/wopr-wordmark.tsx +19 -0
  344. package/src/components/billing/add-payment-method-dialog.tsx +267 -0
  345. package/src/components/billing/affiliate-dashboard.tsx +300 -0
  346. package/src/components/billing/auto-topup-card.tsx +432 -0
  347. package/src/components/billing/buy-credits-panel.tsx +180 -0
  348. package/src/components/billing/buy-crypto-credits-panel.tsx +96 -0
  349. package/src/components/billing/byok-callout.tsx +87 -0
  350. package/src/components/billing/coupon-input.tsx +86 -0
  351. package/src/components/billing/credit-balance.tsx +79 -0
  352. package/src/components/billing/degraded-state-banner.tsx +95 -0
  353. package/src/components/billing/dividend-banner.tsx +97 -0
  354. package/src/components/billing/dividend-eligibility.tsx +86 -0
  355. package/src/components/billing/dividend-pool-stats.tsx +86 -0
  356. package/src/components/billing/first-dividend-dialog.tsx +109 -0
  357. package/src/components/billing/low-balance-banner.tsx +50 -0
  358. package/src/components/billing/org-billing-page.tsx +360 -0
  359. package/src/components/billing/suspension-banner.tsx +53 -0
  360. package/src/components/billing/transaction-history.tsx +239 -0
  361. package/src/components/bot-settings/__tests__/bot-settings-client.test.tsx +205 -0
  362. package/src/components/bot-settings/backups-tab.tsx +377 -0
  363. package/src/components/bot-settings/bot-settings-client.tsx +1712 -0
  364. package/src/components/bot-settings/resources-tab.tsx +203 -0
  365. package/src/components/bot-settings/storage-tab.tsx +248 -0
  366. package/src/components/bot-settings/vps-info-panel.tsx +132 -0
  367. package/src/components/bot-settings/vps-upgrade-card.tsx +110 -0
  368. package/src/components/capability/CapabilityResolver.tsx +113 -0
  369. package/src/components/channel-wizard/field-interactive.tsx +48 -0
  370. package/src/components/channel-wizard/field-oauth.tsx +181 -0
  371. package/src/components/channel-wizard/field-paste.tsx +47 -0
  372. package/src/components/channel-wizard/field-qr.tsx +302 -0
  373. package/src/components/channel-wizard/index.ts +6 -0
  374. package/src/components/channel-wizard/step-renderer.tsx +103 -0
  375. package/src/components/channel-wizard/wizard.tsx +200 -0
  376. package/src/components/chat/ambient-dot.tsx +32 -0
  377. package/src/components/chat/chat-input.tsx +56 -0
  378. package/src/components/chat/chat-message.tsx +36 -0
  379. package/src/components/chat/chat-panel.tsx +138 -0
  380. package/src/components/chat/chat-widget.tsx +41 -0
  381. package/src/components/chat/index.ts +5 -0
  382. package/src/components/dashboard/command-center.tsx +614 -0
  383. package/src/components/instances/friends-tab.test.tsx +265 -0
  384. package/src/components/instances/friends-tab.tsx +721 -0
  385. package/src/components/landing/hero.tsx +53 -0
  386. package/src/components/landing/landing-nav.tsx +21 -0
  387. package/src/components/landing/landing-page.tsx +71 -0
  388. package/src/components/landing/portfolio-chart.tsx +349 -0
  389. package/src/components/landing/story-sections.tsx +50 -0
  390. package/src/components/landing/terminal-lines.ts +99 -0
  391. package/src/components/landing/terminal-sequence.tsx +453 -0
  392. package/src/components/landing/typing-effect.tsx +43 -0
  393. package/src/components/marketplace/category-filter.tsx +61 -0
  394. package/src/components/marketplace/empty-state.tsx +61 -0
  395. package/src/components/marketplace/featured-heroes.tsx +84 -0
  396. package/src/components/marketplace/first-visit-hero.tsx +110 -0
  397. package/src/components/marketplace/index.ts +9 -0
  398. package/src/components/marketplace/install-wizard.tsx +782 -0
  399. package/src/components/marketplace/marketplace-tabs.tsx +54 -0
  400. package/src/components/marketplace/plugin-card.tsx +129 -0
  401. package/src/components/marketplace/superpower-card.tsx +104 -0
  402. package/src/components/marketplace/superpower-content.tsx +117 -0
  403. package/src/components/marketplace/terminal-search.tsx +67 -0
  404. package/src/components/oauth-buttons.tsx +75 -0
  405. package/src/components/observability/fleet-health.tsx +370 -0
  406. package/src/components/observability/health-overview.tsx +246 -0
  407. package/src/components/observability/logs-viewer.tsx +215 -0
  408. package/src/components/observability/metrics-dashboard.tsx +288 -0
  409. package/src/components/onboarding/fallback-setup.tsx +137 -0
  410. package/src/components/onboarding/index.ts +3 -0
  411. package/src/components/onboarding/setup-checklist.tsx +333 -0
  412. package/src/components/onboarding/step-superpowers.tsx +122 -0
  413. package/src/components/plugin-setup/index.ts +1 -0
  414. package/src/components/plugin-setup/setup-chat-panel.tsx +188 -0
  415. package/src/components/pricing/dividend-calculator.tsx +47 -0
  416. package/src/components/pricing/dividend-stats.tsx +117 -0
  417. package/src/components/pricing/pricing-page.tsx +229 -0
  418. package/src/components/settings/create-org-wizard.tsx +225 -0
  419. package/src/components/sidebar.tsx +202 -0
  420. package/src/components/status/status-page.tsx +209 -0
  421. package/src/components/status-badge.tsx +28 -0
  422. package/src/components/theme-provider.tsx +8 -0
  423. package/src/components/ui/alert-dialog.tsx +141 -0
  424. package/src/components/ui/badge.tsx +47 -0
  425. package/src/components/ui/banner.tsx +36 -0
  426. package/src/components/ui/button.tsx +64 -0
  427. package/src/components/ui/card.tsx +75 -0
  428. package/src/components/ui/checkbox.tsx +52 -0
  429. package/src/components/ui/collapsible.tsx +31 -0
  430. package/src/components/ui/credit-detailed.tsx +33 -0
  431. package/src/components/ui/dialog.tsx +143 -0
  432. package/src/components/ui/dropdown-menu.tsx +228 -0
  433. package/src/components/ui/form.tsx +151 -0
  434. package/src/components/ui/input.tsx +21 -0
  435. package/src/components/ui/label.tsx +21 -0
  436. package/src/components/ui/popover.tsx +74 -0
  437. package/src/components/ui/progress.tsx +28 -0
  438. package/src/components/ui/radio-group.tsx +45 -0
  439. package/src/components/ui/select.tsx +175 -0
  440. package/src/components/ui/separator.tsx +28 -0
  441. package/src/components/ui/sheet.tsx +125 -0
  442. package/src/components/ui/skeleton.tsx +15 -0
  443. package/src/components/ui/switch.tsx +35 -0
  444. package/src/components/ui/table.tsx +92 -0
  445. package/src/components/ui/tabs.tsx +81 -0
  446. package/src/components/ui/textarea.tsx +18 -0
  447. package/src/components/ui/tooltip.tsx +44 -0
  448. package/src/config/provider-docs.ts +17 -0
  449. package/src/hooks/__tests__/use-async.test.ts +127 -0
  450. package/src/hooks/__tests__/use-count-up.test.ts +129 -0
  451. package/src/hooks/__tests__/use-debounce.test.ts +105 -0
  452. package/src/hooks/__tests__/use-fleet-sse.test.ts +216 -0
  453. package/src/hooks/__tests__/use-local-storage.test.ts +74 -0
  454. package/src/hooks/__tests__/use-mobile.test.ts +86 -0
  455. package/src/hooks/__tests__/use-save-queue.test.ts +159 -0
  456. package/src/hooks/use-async.ts +54 -0
  457. package/src/hooks/use-capability-meta.ts +99 -0
  458. package/src/hooks/use-count-up.ts +23 -0
  459. package/src/hooks/use-debounce.ts +12 -0
  460. package/src/hooks/use-fleet-sse.ts +47 -0
  461. package/src/hooks/use-has-org.ts +18 -0
  462. package/src/hooks/use-image-status.ts +36 -0
  463. package/src/hooks/use-local-storage.ts +36 -0
  464. package/src/hooks/use-mobile.ts +17 -0
  465. package/src/hooks/use-page-context.ts +24 -0
  466. package/src/hooks/use-pagination-params.ts +30 -0
  467. package/src/hooks/use-plugin-registry.ts +247 -0
  468. package/src/hooks/use-plugin-setup-chat.ts +211 -0
  469. package/src/hooks/use-save-queue.ts +54 -0
  470. package/src/hooks/use-webmcp.ts +40 -0
  471. package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +112 -0
  472. package/src/lib/__tests__/admin-api.test.ts +487 -0
  473. package/src/lib/__tests__/api-bot-crud.test.ts +391 -0
  474. package/src/lib/__tests__/api-fetch.test.ts +196 -0
  475. package/src/lib/__tests__/bot-settings-data.test.ts +352 -0
  476. package/src/lib/__tests__/org-api.test.ts +281 -0
  477. package/src/lib/__tests__/org-billing-api.test.ts +242 -0
  478. package/src/lib/__tests__/pricing-data.test.ts +32 -0
  479. package/src/lib/__tests__/settings-api.test.ts +272 -0
  480. package/src/lib/admin-affiliate-api.ts +51 -0
  481. package/src/lib/admin-api.ts +325 -0
  482. package/src/lib/admin-compliance-api.ts +127 -0
  483. package/src/lib/admin-gpu-api.ts +82 -0
  484. package/src/lib/admin-incident-api.ts +121 -0
  485. package/src/lib/admin-inference-api.ts +47 -0
  486. package/src/lib/admin-marketplace-api.ts +97 -0
  487. package/src/lib/api-config.test.ts +111 -0
  488. package/src/lib/api-config.ts +65 -0
  489. package/src/lib/api-errors.test.ts +43 -0
  490. package/src/lib/api.ts +2011 -0
  491. package/src/lib/auth-client.ts +11 -0
  492. package/src/lib/bot-settings-data.ts +342 -0
  493. package/src/lib/brand-config.ts +145 -0
  494. package/src/lib/brand.ts +669 -0
  495. package/src/lib/changeset-api.ts +29 -0
  496. package/src/lib/changeset-types.ts +56 -0
  497. package/src/lib/channel-manifests.ts +50 -0
  498. package/src/lib/chat/chat-context.tsx +70 -0
  499. package/src/lib/chat/chat-store.ts +62 -0
  500. package/src/lib/chat/types.ts +35 -0
  501. package/src/lib/chat/use-chat.ts +255 -0
  502. package/src/lib/cost-comparison-data.test.ts +95 -0
  503. package/src/lib/cost-comparison-data.ts +54 -0
  504. package/src/lib/errors.test.ts +64 -0
  505. package/src/lib/errors.ts +52 -0
  506. package/src/lib/fetch-utils.test.ts +57 -0
  507. package/src/lib/fetch-utils.ts +25 -0
  508. package/src/lib/format-credit.test.ts +66 -0
  509. package/src/lib/format-credit.ts +24 -0
  510. package/src/lib/format.test.ts +62 -0
  511. package/src/lib/format.ts +17 -0
  512. package/src/lib/logger.ts +28 -0
  513. package/src/lib/marketplace-data.ts +346 -0
  514. package/src/lib/oauth-errors.ts +19 -0
  515. package/src/lib/onboarding-data.ts +1265 -0
  516. package/src/lib/onboarding-store.ts +233 -0
  517. package/src/lib/org-api.ts +74 -0
  518. package/src/lib/org-billing-api.ts +81 -0
  519. package/src/lib/page-prompts.test.ts +32 -0
  520. package/src/lib/page-prompts.ts +23 -0
  521. package/src/lib/plugin/index.ts +32 -0
  522. package/src/lib/plugin/tool-definitions.ts +306 -0
  523. package/src/lib/pricing-data.ts +115 -0
  524. package/src/lib/promotions-types.ts +58 -0
  525. package/src/lib/settings-api.ts +63 -0
  526. package/src/lib/status-colors.ts +38 -0
  527. package/src/lib/tenant-context.tsx +134 -0
  528. package/src/lib/trpc-types.ts +173 -0
  529. package/src/lib/trpc.tsx +86 -0
  530. package/src/lib/utils.test.ts +55 -0
  531. package/src/lib/utils.ts +18 -0
  532. package/src/lib/validate-redirect-url.ts +39 -0
  533. package/src/lib/webmcp/feature-detect.ts +13 -0
  534. package/src/lib/webmcp/marketplace-onboarding-tools.ts +202 -0
  535. package/src/lib/webmcp/register.ts +44 -0
  536. package/src/lib/webmcp/tools.ts +422 -0
  537. package/src/proxy.ts +258 -0
  538. package/src/types/missing-deps.d.ts +160 -0
  539. package/src/types/motion-dom.d.ts +162 -0
  540. package/src/types/vitest-matchers.d.ts +40 -0
  541. package/src/types/web-mcp.d.ts +22 -0
  542. package/tsconfig.json +34 -0
  543. package/vitest.config.ts +26 -0
@@ -0,0 +1,1265 @@
1
+ import { PROVIDER_DOC_URLS } from "../config/provider-docs";
2
+ import { listMarketplacePlugins } from "./marketplace-data";
3
+
4
+ // --- Personalities ---
5
+
6
+ export interface Personality {
7
+ id: string;
8
+ name: string;
9
+ description: string;
10
+ }
11
+
12
+ export const personalities: Personality[] = [
13
+ { id: "helpful", name: "Helpful assistant", description: "Friendly and informative." },
14
+ { id: "creative", name: "Creative collaborator", description: "Imaginative and playful." },
15
+ { id: "code", name: "Code companion", description: "Technical and precise." },
16
+ { id: "custom", name: "Custom", description: "Describe your own personality." },
17
+ ];
18
+
19
+ // --- BYOK AI Provider choice ---
20
+
21
+ export type ByokAiProvider = "openai" | "openrouter";
22
+
23
+ export const openaiKeyField: OnboardingConfigField = {
24
+ key: "openai_api_key",
25
+ label: "OpenAI API Key",
26
+ secret: true,
27
+ placeholder: "sk-...",
28
+ helpUrl: PROVIDER_DOC_URLS.openai,
29
+ helpText: "Covers embeddings, search, and text generation.",
30
+ validation: { pattern: "^sk-", message: "Must start with sk-" },
31
+ };
32
+
33
+ export const openrouterKeyField: OnboardingConfigField = {
34
+ key: "openrouter_api_key",
35
+ label: "OpenRouter API Key",
36
+ secret: true,
37
+ placeholder: "sk-or-...",
38
+ helpUrl: PROVIDER_DOC_URLS.openrouter,
39
+ helpText: "Covers embeddings, search, and 200+ AI models.",
40
+ validation: { pattern: "^sk-or-", message: "Must start with sk-or-" },
41
+ };
42
+
43
+ /** Superpower IDs that share a single OpenAI/OpenRouter key */
44
+ export const AI_KEY_SUPERPOWER_IDS = ["memory", "search", "text-gen"] as const;
45
+
46
+ // --- Superpowers ---
47
+
48
+ export interface DiyCostData {
49
+ diyLabel: string;
50
+ diyCostPerMonth: string;
51
+ diyCostNumeric: number; // cents, for summing
52
+ accounts: string[];
53
+ apiKeys: string[];
54
+ hardware: string | null;
55
+ }
56
+
57
+ export interface Superpower {
58
+ id: string;
59
+ name: string;
60
+ tagline: string;
61
+ description: string;
62
+ icon: string;
63
+ color: string;
64
+ /** Whether this superpower needs an API key under BYOK mode */
65
+ requiresKey: boolean;
66
+ configFields: OnboardingConfigField[];
67
+ /** If true, this superpower uses the shared OpenAI/OpenRouter key */
68
+ usesAiKey?: boolean;
69
+ /** DIY cost metadata for the cost comparison step */
70
+ diyCostData?: DiyCostData;
71
+ }
72
+
73
+ export const superpowers: Superpower[] = [
74
+ {
75
+ id: "image-gen",
76
+ name: "ImageGen",
77
+ tagline: "/imagine anything",
78
+ description: "Generate images from text descriptions.",
79
+ icon: "Image",
80
+ color: "#F59E0B",
81
+ requiresKey: true,
82
+ configFields: [
83
+ {
84
+ key: "replicate_api_token",
85
+ label: "Replicate API Token",
86
+ secret: true,
87
+ placeholder: "r8_...",
88
+ helpUrl: PROVIDER_DOC_URLS.replicate,
89
+ helpText: "Used for image and video generation via Replicate.",
90
+ validation: { pattern: "^r8_", message: "Must start with r8_" },
91
+ },
92
+ ],
93
+ diyCostData: {
94
+ diyLabel: "Image generation API",
95
+ diyCostPerMonth: "$10-50/mo",
96
+ diyCostNumeric: 3000,
97
+ accounts: ["Replicate"],
98
+ apiKeys: ["Replicate API Token"],
99
+ hardware: null,
100
+ },
101
+ },
102
+ {
103
+ id: "video-gen",
104
+ name: "VideoGen",
105
+ tagline: "/video in any channel",
106
+ description: "Generate videos from text descriptions.",
107
+ icon: "Video",
108
+ color: "#EF4444",
109
+ requiresKey: true,
110
+ configFields: [
111
+ {
112
+ key: "replicate_api_token",
113
+ label: "Replicate API Token",
114
+ secret: true,
115
+ placeholder: "r8_...",
116
+ helpUrl: PROVIDER_DOC_URLS.replicate,
117
+ helpText: "Used for image and video generation via Replicate.",
118
+ validation: { pattern: "^r8_", message: "Must start with r8_" },
119
+ },
120
+ ],
121
+ diyCostData: {
122
+ diyLabel: "Video generation API",
123
+ diyCostPerMonth: "$20-100/mo",
124
+ diyCostNumeric: 6000,
125
+ accounts: ["Replicate"],
126
+ apiKeys: ["Replicate API Token"],
127
+ hardware: null,
128
+ },
129
+ },
130
+ {
131
+ id: "voice",
132
+ name: "Voice",
133
+ tagline: "Talk out loud",
134
+ description: "Speech-to-text and text-to-speech.",
135
+ icon: "Mic",
136
+ color: "#8B5CF6",
137
+ requiresKey: true,
138
+ configFields: [
139
+ {
140
+ key: "elevenlabs_api_key",
141
+ label: "ElevenLabs API Key",
142
+ secret: true,
143
+ placeholder: "Paste your ElevenLabs API key",
144
+ helpUrl: PROVIDER_DOC_URLS.elevenlabs,
145
+ helpText: "Used for text-to-speech synthesis.",
146
+ },
147
+ ],
148
+ diyCostData: {
149
+ diyLabel: "Voice synthesis API",
150
+ diyCostPerMonth: "$5-30/mo",
151
+ diyCostNumeric: 1500,
152
+ accounts: ["ElevenLabs"],
153
+ apiKeys: ["ElevenLabs API Key"],
154
+ hardware: null,
155
+ },
156
+ },
157
+ {
158
+ id: "memory",
159
+ name: "Memory",
160
+ tagline: "Remembers everything",
161
+ description: "Long-term memory with semantic search across conversations.",
162
+ icon: "Brain",
163
+ color: "#10B981",
164
+ requiresKey: true,
165
+ usesAiKey: true,
166
+ configFields: [openaiKeyField],
167
+ diyCostData: {
168
+ diyLabel: "Vector DB + embeddings",
169
+ diyCostPerMonth: "$10-40/mo",
170
+ diyCostNumeric: 2500,
171
+ accounts: ["OpenAI or OpenRouter"],
172
+ apiKeys: ["OpenAI/OpenRouter API Key"],
173
+ hardware: "Vector database (Pinecone, Qdrant, etc.)",
174
+ },
175
+ },
176
+ {
177
+ id: "search",
178
+ name: "Search",
179
+ tagline: "Web + docs",
180
+ description: "Search the web and documents for real-time information.",
181
+ icon: "Search",
182
+ color: "#3B82F6",
183
+ requiresKey: true,
184
+ usesAiKey: true,
185
+ configFields: [openaiKeyField],
186
+ diyCostData: {
187
+ diyLabel: "Web search API",
188
+ diyCostPerMonth: "$5-20/mo",
189
+ diyCostNumeric: 1200,
190
+ accounts: ["OpenAI or OpenRouter"],
191
+ apiKeys: ["OpenAI/OpenRouter API Key"],
192
+ hardware: null,
193
+ },
194
+ },
195
+ {
196
+ id: "text-gen",
197
+ name: "Text-gen",
198
+ tagline: "AI chat models",
199
+ description: "Access text generation models for chat and reasoning.",
200
+ icon: "MessageSquare",
201
+ color: "#6366F1",
202
+ requiresKey: true,
203
+ usesAiKey: true,
204
+ configFields: [openaiKeyField],
205
+ diyCostData: {
206
+ diyLabel: "LLM inference API",
207
+ diyCostPerMonth: "$20-200/mo",
208
+ diyCostNumeric: 10000,
209
+ accounts: ["OpenAI or OpenRouter"],
210
+ apiKeys: ["OpenAI/OpenRouter API Key"],
211
+ hardware: null,
212
+ },
213
+ },
214
+ ];
215
+
216
+ export interface PluginOption {
217
+ id: string;
218
+ name: string;
219
+ description: string;
220
+ icon: string;
221
+ color: string;
222
+ capabilities: string[];
223
+ requires?: string[];
224
+ configFields: OnboardingConfigField[];
225
+ /** DIY cost metadata for the cost comparison step */
226
+ diyCostData?: DiyCostData;
227
+ }
228
+
229
+ export interface OnboardingConfigField {
230
+ key: string;
231
+ label: string;
232
+ secret: boolean;
233
+ placeholder?: string;
234
+ helpUrl?: string;
235
+ helpText?: string;
236
+ validation?: {
237
+ pattern: string;
238
+ message: string;
239
+ };
240
+ }
241
+
242
+ export interface Preset {
243
+ id: string;
244
+ name: string;
245
+ description: string;
246
+ channels: string[];
247
+ providers: string[];
248
+ plugins: string[];
249
+ keyCount: number;
250
+ }
251
+
252
+ // --- Channels ---
253
+
254
+ // Onboarding-specific overlay per marketplace channel plugin.
255
+ // configFields use OnboardingConfigField (with helpUrl, helpText) which differs from
256
+ // marketplace configSchema fields. Kept separate intentionally — they serve
257
+ // the onboarding BYOK flow, not the marketplace install wizard.
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
+ > = {
263
+ discord: {
264
+ configFields: [
265
+ {
266
+ key: "discord_bot_token",
267
+ label: "Discord Bot Token",
268
+ secret: true,
269
+ placeholder: "Paste your Discord bot token",
270
+ helpUrl: PROVIDER_DOC_URLS.discord,
271
+ helpText: "Create an app in the Discord Developer Portal, then copy the bot token.",
272
+ validation: { pattern: "^[A-Za-z0-9_.-]+$", message: "Invalid token format" },
273
+ },
274
+ {
275
+ key: "discord_guild_id",
276
+ label: "Discord Server ID",
277
+ secret: false,
278
+ placeholder: "e.g. 123456789012345678",
279
+ helpText: "Right-click your server name and select Copy Server ID.",
280
+ validation: { pattern: "^\\d{17,20}$", message: "Must be a numeric server ID" },
281
+ },
282
+ ],
283
+ diyCostData: {
284
+ diyLabel: "Discord bot hosting",
285
+ diyCostPerMonth: "$5-20/mo",
286
+ diyCostNumeric: 1200,
287
+ accounts: ["Discord Developer Portal"],
288
+ apiKeys: ["Discord Bot Token"],
289
+ hardware: "VPS or cloud server",
290
+ },
291
+ },
292
+ slack: {
293
+ configFields: [
294
+ {
295
+ key: "slack_bot_token",
296
+ label: "Slack Bot Token",
297
+ secret: true,
298
+ placeholder: "xoxb-...",
299
+ helpUrl: PROVIDER_DOC_URLS.slack,
300
+ helpText: "Create a Slack app, add Bot Token Scopes, then install to workspace.",
301
+ validation: { pattern: "^xoxb-", message: "Must start with xoxb-" },
302
+ },
303
+ {
304
+ key: "slack_signing_secret",
305
+ label: "Slack Signing Secret",
306
+ secret: true,
307
+ placeholder: "Paste your signing secret",
308
+ helpText: "Found under Basic Information > App Credentials.",
309
+ },
310
+ ],
311
+ diyCostData: {
312
+ diyLabel: "Slack app hosting",
313
+ diyCostPerMonth: "$5-20/mo",
314
+ diyCostNumeric: 1200,
315
+ accounts: ["Slack API Portal"],
316
+ apiKeys: ["Slack Bot Token", "Slack Signing Secret"],
317
+ hardware: "VPS or cloud server",
318
+ },
319
+ },
320
+ telegram: {
321
+ configFields: [
322
+ {
323
+ key: "telegram_bot_token",
324
+ label: "Telegram Bot Token",
325
+ secret: true,
326
+ placeholder: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
327
+ helpUrl: PROVIDER_DOC_URLS.telegram,
328
+ helpText: "Message @BotFather on Telegram with /newbot to get a token.",
329
+ validation: { pattern: "^[0-9]+:[A-Za-z0-9_-]+$", message: "Invalid Telegram token" },
330
+ },
331
+ ],
332
+ diyCostData: {
333
+ diyLabel: "Telegram bot hosting",
334
+ diyCostPerMonth: "$5-20/mo",
335
+ diyCostNumeric: 1200,
336
+ accounts: ["Telegram BotFather"],
337
+ apiKeys: ["Telegram Bot Token"],
338
+ hardware: "VPS or cloud server",
339
+ },
340
+ },
341
+ };
342
+
343
+ // Channels present in onboarding but not yet in the marketplace
344
+ const ONBOARDING_ONLY_CHANNELS: PluginOption[] = [
345
+ {
346
+ id: "signal",
347
+ name: "Signal",
348
+ description: "Connect a Signal bot for secure messaging.",
349
+ icon: "Shield",
350
+ color: "#3A76F0",
351
+ capabilities: ["channel"],
352
+ configFields: [
353
+ {
354
+ key: "signal_phone",
355
+ label: "Signal Phone Number",
356
+ secret: false,
357
+ placeholder: "+1234567890",
358
+ helpText: "The phone number linked to your Signal account.",
359
+ validation: { pattern: "^\\+\\d{7,15}$", message: "Must be E.164 format" },
360
+ },
361
+ ],
362
+ diyCostData: {
363
+ diyLabel: "Signal bot hosting",
364
+ diyCostPerMonth: "$10-30/mo",
365
+ diyCostNumeric: 2000,
366
+ accounts: ["Signal account"],
367
+ apiKeys: ["Signal phone number"],
368
+ hardware: "Dedicated server (always-on)",
369
+ },
370
+ },
371
+ {
372
+ id: "whatsapp",
373
+ name: "WhatsApp",
374
+ description: "Connect via WhatsApp Business API.",
375
+ icon: "Phone",
376
+ color: "#25D366",
377
+ capabilities: ["channel"],
378
+ configFields: [
379
+ {
380
+ key: "whatsapp_token",
381
+ label: "WhatsApp API Token",
382
+ secret: true,
383
+ placeholder: "Paste your WhatsApp Business API token",
384
+ helpUrl: PROVIDER_DOC_URLS.whatsapp,
385
+ helpText: "Set up a WhatsApp Business account in Meta Developer Portal.",
386
+ },
387
+ ],
388
+ diyCostData: {
389
+ diyLabel: "WhatsApp Business API",
390
+ diyCostPerMonth: "$15-50/mo",
391
+ diyCostNumeric: 3000,
392
+ accounts: ["Meta Developer Portal", "WhatsApp Business"],
393
+ apiKeys: ["WhatsApp API Token"],
394
+ hardware: null,
395
+ },
396
+ },
397
+ {
398
+ id: "msteams",
399
+ name: "MS Teams",
400
+ description: "Connect a Microsoft Teams bot.",
401
+ icon: "Users",
402
+ color: "#6264A7",
403
+ capabilities: ["channel"],
404
+ configFields: [
405
+ {
406
+ key: "msteams_app_id",
407
+ label: "Teams App ID",
408
+ secret: false,
409
+ placeholder: "Paste your Teams app ID",
410
+ helpUrl: PROVIDER_DOC_URLS.msTeams,
411
+ helpText: "Register a bot in the Teams Developer Portal.",
412
+ },
413
+ {
414
+ key: "msteams_app_password",
415
+ label: "Teams App Password",
416
+ secret: true,
417
+ placeholder: "Paste your app password",
418
+ },
419
+ ],
420
+ diyCostData: {
421
+ diyLabel: "MS Teams bot hosting",
422
+ diyCostPerMonth: "$10-30/mo",
423
+ diyCostNumeric: 2000,
424
+ accounts: ["Microsoft Azure", "Teams Developer Portal"],
425
+ apiKeys: ["Teams App ID", "Teams App Password"],
426
+ hardware: null,
427
+ },
428
+ },
429
+ ];
430
+
431
+ // Static export kept for backward compat with sync callers.
432
+ // Only contains onboarding-only channels (e.g. web-ui) that don't require API data.
433
+ // For live channel data, use getChannelPlugins() which fetches from the marketplace API.
434
+ export const channelPlugins: PluginOption[] = [...ONBOARDING_ONLY_CHANNELS];
435
+
436
+ export async function getChannelPlugins(): Promise<PluginOption[]> {
437
+ const plugins = await listMarketplacePlugins();
438
+ const marketplaceChannels: PluginOption[] = plugins
439
+ .filter((m) => m.category === "channel")
440
+ .map((m) => ({
441
+ id: m.id,
442
+ name: m.name,
443
+ description: m.description,
444
+ icon: m.icon,
445
+ color: m.color,
446
+ capabilities: m.capabilities,
447
+ configFields: CHANNEL_OVERLAY[m.id]?.configFields ?? [],
448
+ diyCostData: CHANNEL_OVERLAY[m.id]?.diyCostData,
449
+ }));
450
+ return [...marketplaceChannels, ...ONBOARDING_ONLY_CHANNELS];
451
+ }
452
+
453
+ // --- Providers ---
454
+
455
+ // Onboarding-specific overlay per marketplace provider plugin.
456
+ // configFields use OnboardingConfigField (with helpUrl, helpText) which differs from
457
+ // marketplace configSchema fields.
458
+ const PROVIDER_OVERLAY: Record<string, { configFields: OnboardingConfigField[] }> = {
459
+ anthropic: {
460
+ configFields: [
461
+ {
462
+ key: "anthropic_api_key",
463
+ label: "Anthropic API Key",
464
+ secret: true,
465
+ placeholder: "sk-ant-...",
466
+ helpUrl: PROVIDER_DOC_URLS.anthropic,
467
+ helpText: "Get an API key from the Anthropic Console.",
468
+ validation: { pattern: "^sk-ant-", message: "Must start with sk-ant-" },
469
+ },
470
+ ],
471
+ },
472
+ openai: {
473
+ configFields: [
474
+ {
475
+ key: "openai_api_key",
476
+ label: "OpenAI API Key",
477
+ secret: true,
478
+ placeholder: "sk-...",
479
+ helpUrl: PROVIDER_DOC_URLS.openai,
480
+ helpText: "Get an API key from the OpenAI dashboard.",
481
+ validation: { pattern: "^sk-", message: "Must start with sk-" },
482
+ },
483
+ ],
484
+ },
485
+ kimi: {
486
+ configFields: [
487
+ {
488
+ key: "kimi_api_key",
489
+ label: "Kimi API Key",
490
+ secret: true,
491
+ placeholder: "Paste your Kimi API key",
492
+ helpUrl: PROVIDER_DOC_URLS.moonshot,
493
+ helpText: "Get an API key from the Moonshot Platform.",
494
+ },
495
+ ],
496
+ },
497
+ opencode: {
498
+ configFields: [
499
+ {
500
+ key: "opencode_endpoint",
501
+ label: "OpenCode Endpoint URL",
502
+ secret: false,
503
+ placeholder: "https://your-endpoint.example.com/v1",
504
+ helpText: "Your OpenAI-compatible inference endpoint.",
505
+ },
506
+ {
507
+ key: "opencode_api_key",
508
+ label: "OpenCode API Key",
509
+ secret: true,
510
+ placeholder: "Paste your API key",
511
+ },
512
+ ],
513
+ },
514
+ };
515
+
516
+ // Onboarding-specific overlay per marketplace optional plugin.
517
+ const PLUGIN_OVERLAY: Record<string, { configFields: OnboardingConfigField[] }> = {
518
+ "elevenlabs-tts": {
519
+ configFields: [
520
+ {
521
+ key: "elevenlabs_api_key",
522
+ label: "ElevenLabs API Key",
523
+ secret: true,
524
+ placeholder: "Paste your ElevenLabs API key",
525
+ helpUrl: PROVIDER_DOC_URLS.elevenlabsHome,
526
+ helpText: "Get an API key from ElevenLabs.",
527
+ },
528
+ ],
529
+ },
530
+ "deepgram-stt": {
531
+ configFields: [
532
+ {
533
+ key: "deepgram_api_key",
534
+ label: "Deepgram API Key",
535
+ secret: true,
536
+ placeholder: "Paste your Deepgram API key",
537
+ helpUrl: PROVIDER_DOC_URLS.deepgram,
538
+ helpText: "Get an API key from Deepgram Console.",
539
+ },
540
+ ],
541
+ },
542
+ github: {
543
+ configFields: [
544
+ {
545
+ key: "github_token",
546
+ label: "GitHub Personal Access Token",
547
+ secret: true,
548
+ placeholder: "ghp_...",
549
+ helpUrl: PROVIDER_DOC_URLS.github,
550
+ helpText: "Create a fine-grained personal access token on GitHub.",
551
+ validation: { pattern: "^gh[ps]_", message: "Must start with ghp_ or ghs_" },
552
+ },
553
+ ],
554
+ },
555
+ };
556
+
557
+ export const providerPlugins: PluginOption[] = [
558
+ {
559
+ id: "anthropic",
560
+ name: "Anthropic",
561
+ description: "Claude models for reasoning and conversation.",
562
+ icon: "Brain",
563
+ color: "#D4A574",
564
+ capabilities: ["provider"],
565
+ configFields: [
566
+ {
567
+ key: "anthropic_api_key",
568
+ label: "Anthropic API Key",
569
+ secret: true,
570
+ placeholder: "sk-ant-...",
571
+ helpUrl: PROVIDER_DOC_URLS.anthropic,
572
+ helpText: "Get an API key from the Anthropic Console.",
573
+ validation: { pattern: "^sk-ant-", message: "Must start with sk-ant-" },
574
+ },
575
+ ],
576
+ },
577
+ {
578
+ id: "openai",
579
+ name: "OpenAI",
580
+ description: "GPT models for text and image generation.",
581
+ icon: "Sparkles",
582
+ color: "#10A37F",
583
+ capabilities: ["provider"],
584
+ configFields: [
585
+ {
586
+ key: "openai_api_key",
587
+ label: "OpenAI API Key",
588
+ secret: true,
589
+ placeholder: "sk-...",
590
+ helpUrl: PROVIDER_DOC_URLS.openai,
591
+ helpText: "Get an API key from the OpenAI dashboard.",
592
+ validation: { pattern: "^sk-", message: "Must start with sk-" },
593
+ },
594
+ ],
595
+ },
596
+ {
597
+ id: "kimi",
598
+ name: "Kimi",
599
+ description: "Moonshot AI models for long-context tasks.",
600
+ icon: "Moon",
601
+ color: "#6C5CE7",
602
+ capabilities: ["provider"],
603
+ configFields: [
604
+ {
605
+ key: "kimi_api_key",
606
+ label: "Kimi API Key",
607
+ secret: true,
608
+ placeholder: "Paste your Kimi API key",
609
+ helpUrl: PROVIDER_DOC_URLS.moonshot,
610
+ helpText: "Get an API key from the Moonshot Platform.",
611
+ },
612
+ ],
613
+ },
614
+ {
615
+ id: "opencode",
616
+ name: "OpenCode",
617
+ description: "Open-source compatible inference endpoint.",
618
+ icon: "Code",
619
+ color: "#FF6B6B",
620
+ capabilities: ["provider"],
621
+ configFields: [
622
+ {
623
+ key: "opencode_endpoint",
624
+ label: "OpenCode Endpoint URL",
625
+ secret: false,
626
+ placeholder: "https://your-endpoint.example.com/v1",
627
+ helpText: "Your OpenAI-compatible inference endpoint.",
628
+ },
629
+ {
630
+ key: "opencode_api_key",
631
+ label: "OpenCode API Key",
632
+ secret: true,
633
+ placeholder: "Paste your API key",
634
+ },
635
+ ],
636
+ },
637
+ ];
638
+
639
+ // --- Optional Plugins ---
640
+
641
+ export interface PluginCategory {
642
+ id: string;
643
+ name: string;
644
+ plugins: PluginOption[];
645
+ }
646
+
647
+ export const pluginCategories: PluginCategory[] = [
648
+ {
649
+ id: "memory",
650
+ name: "Memory",
651
+ plugins: [
652
+ {
653
+ id: "semantic-memory",
654
+ name: "Semantic Memory Search",
655
+ description: "Long-term memory with vector search across conversations.",
656
+ icon: "Database",
657
+ color: "#8B5CF6",
658
+ capabilities: ["memory"],
659
+ configFields: [],
660
+ },
661
+ ],
662
+ },
663
+ {
664
+ id: "voice",
665
+ name: "Voice",
666
+ plugins: [
667
+ {
668
+ id: "elevenlabs-tts",
669
+ name: "ElevenLabs TTS",
670
+ description: "High-quality text-to-speech synthesis.",
671
+ icon: "Volume2",
672
+ color: "#000000",
673
+ capabilities: ["voice", "tts"],
674
+ configFields: [
675
+ {
676
+ key: "elevenlabs_api_key",
677
+ label: "ElevenLabs API Key",
678
+ secret: true,
679
+ placeholder: "Paste your ElevenLabs API key",
680
+ helpUrl: PROVIDER_DOC_URLS.elevenlabsHome,
681
+ helpText: "Get an API key from ElevenLabs.",
682
+ },
683
+ ],
684
+ },
685
+ {
686
+ id: "deepgram-stt",
687
+ name: "Deepgram STT",
688
+ description: "Fast, accurate speech-to-text transcription.",
689
+ icon: "Mic",
690
+ color: "#13EF93",
691
+ capabilities: ["voice", "stt"],
692
+ configFields: [
693
+ {
694
+ key: "deepgram_api_key",
695
+ label: "Deepgram API Key",
696
+ secret: true,
697
+ placeholder: "Paste your Deepgram API key",
698
+ helpUrl: PROVIDER_DOC_URLS.deepgram,
699
+ helpText: "Get an API key from Deepgram Console.",
700
+ },
701
+ ],
702
+ },
703
+ {
704
+ id: "openai-tts",
705
+ name: "OpenAI TTS",
706
+ description: "Text-to-speech via OpenAI.",
707
+ icon: "Volume2",
708
+ color: "#10A37F",
709
+ capabilities: ["voice", "tts"],
710
+ requires: ["openai"],
711
+ configFields: [],
712
+ },
713
+ {
714
+ id: "discord-voice",
715
+ name: "Discord Voice",
716
+ description: "Join Discord voice channels for live conversation.",
717
+ icon: "Headphones",
718
+ color: "#5865F2",
719
+ capabilities: ["voice", "channel-voice"],
720
+ requires: ["discord"],
721
+ configFields: [],
722
+ },
723
+ ],
724
+ },
725
+ {
726
+ id: "integration",
727
+ name: "Integration",
728
+ plugins: [
729
+ {
730
+ id: "webhooks",
731
+ name: "Webhooks",
732
+ description: "Send and receive webhooks for external integrations.",
733
+ icon: "Webhook",
734
+ color: "#F59E0B",
735
+ capabilities: ["integration"],
736
+ configFields: [],
737
+ },
738
+ {
739
+ id: "github",
740
+ name: "GitHub",
741
+ description: "GitHub integration for code review and issue tracking.",
742
+ icon: "GitBranch",
743
+ color: "#24292E",
744
+ capabilities: ["integration"],
745
+ configFields: [
746
+ {
747
+ key: "github_token",
748
+ label: "GitHub Personal Access Token",
749
+ secret: true,
750
+ placeholder: "ghp_...",
751
+ helpUrl: PROVIDER_DOC_URLS.github,
752
+ helpText: "Create a fine-grained personal access token on GitHub.",
753
+ validation: { pattern: "^gh[ps]_", message: "Must start with ghp_ or ghs_" },
754
+ },
755
+ ],
756
+ },
757
+ ],
758
+ },
759
+ {
760
+ id: "ui",
761
+ name: "UI",
762
+ plugins: [
763
+ {
764
+ id: "web-ui",
765
+ name: "Web UI",
766
+ description: "Browser-based chat interface for your bot.",
767
+ icon: "Globe",
768
+ color: "#3B82F6",
769
+ capabilities: ["ui"],
770
+ configFields: [],
771
+ },
772
+ ],
773
+ },
774
+ ];
775
+
776
+ export async function getProviderPlugins(): Promise<PluginOption[]> {
777
+ const plugins = await listMarketplacePlugins();
778
+ const marketplaceProviders: PluginOption[] = plugins
779
+ .filter((m) => m.category === "provider")
780
+ .map((m) => ({
781
+ id: m.id,
782
+ name: m.name,
783
+ description: m.description,
784
+ icon: m.icon,
785
+ color: m.color,
786
+ capabilities: m.capabilities,
787
+ configFields: PROVIDER_OVERLAY[m.id]?.configFields ?? [],
788
+ }));
789
+ const marketplaceIds = new Set(marketplaceProviders.map((p) => p.id));
790
+ const staticOnly = providerPlugins.filter((p) => !marketplaceIds.has(p.id));
791
+ return [...marketplaceProviders, ...staticOnly];
792
+ }
793
+
794
+ export async function getOptionalPlugins(): Promise<PluginCategory[]> {
795
+ const plugins = await listMarketplacePlugins();
796
+ const optionalCategories = ["memory", "voice", "integration", "ui"] as const;
797
+ const result: PluginCategory[] = [];
798
+ for (const catId of optionalCategories) {
799
+ const marketplaceInCat = plugins
800
+ .filter((m) => m.category === catId)
801
+ .map((m) => ({
802
+ id: m.id,
803
+ name: m.name,
804
+ description: m.description,
805
+ icon: m.icon,
806
+ color: m.color,
807
+ capabilities: m.capabilities,
808
+ configFields: PLUGIN_OVERLAY[m.id]?.configFields ?? [],
809
+ }));
810
+ const staticCat = pluginCategories.find((c) => c.id === catId);
811
+ const marketplaceIds = new Set(marketplaceInCat.map((p) => p.id));
812
+ const staticOnly = (staticCat?.plugins ?? []).filter((p) => !marketplaceIds.has(p.id));
813
+ const merged = [...marketplaceInCat, ...staticOnly];
814
+ if (merged.length > 0) {
815
+ result.push({
816
+ id: catId,
817
+ name: staticCat?.name ?? catId.charAt(0).toUpperCase() + catId.slice(1),
818
+ plugins: merged,
819
+ });
820
+ }
821
+ }
822
+ const knownCats = new Set<string>(optionalCategories);
823
+ const extraCats = new Set(
824
+ plugins
825
+ .filter(
826
+ (m) => !knownCats.has(m.category) && m.category !== "channel" && m.category !== "provider",
827
+ )
828
+ .map((m) => m.category),
829
+ );
830
+ for (const catId of extraCats) {
831
+ result.push({
832
+ id: catId,
833
+ name: catId.charAt(0).toUpperCase() + catId.slice(1),
834
+ plugins: plugins
835
+ .filter((m) => m.category === catId)
836
+ .map((m) => ({
837
+ id: m.id,
838
+ name: m.name,
839
+ description: m.description,
840
+ icon: m.icon,
841
+ color: m.color,
842
+ capabilities: m.capabilities,
843
+ configFields: [],
844
+ })),
845
+ });
846
+ }
847
+ return result;
848
+ }
849
+
850
+ // --- Presets ---
851
+
852
+ export const presets: Preset[] = [
853
+ {
854
+ id: "discord-ai-bot",
855
+ name: "Discord AI Bot",
856
+ description: "A Discord bot powered by Claude with memory.",
857
+ channels: ["discord"],
858
+ providers: ["anthropic"],
859
+ plugins: ["semantic-memory"],
860
+ keyCount: 2,
861
+ },
862
+ {
863
+ id: "slack-ai-assistant",
864
+ name: "Slack AI Assistant",
865
+ description: "A Slack app powered by Claude with memory.",
866
+ channels: ["slack"],
867
+ providers: ["anthropic"],
868
+ plugins: ["semantic-memory"],
869
+ keyCount: 2,
870
+ },
871
+ {
872
+ id: "multi-channel",
873
+ name: "Multi-Channel",
874
+ description: "Discord, Slack, and Telegram with Claude.",
875
+ channels: ["discord", "slack", "telegram"],
876
+ providers: ["anthropic"],
877
+ plugins: ["semantic-memory"],
878
+ keyCount: 4,
879
+ },
880
+ {
881
+ id: "voice-enabled",
882
+ name: "Voice-Enabled",
883
+ description: "Discord bot with voice chat via ElevenLabs and Deepgram.",
884
+ channels: ["discord"],
885
+ providers: ["anthropic"],
886
+ plugins: ["semantic-memory", "elevenlabs-tts", "deepgram-stt", "discord-voice"],
887
+ keyCount: 4,
888
+ },
889
+ {
890
+ id: "api-only",
891
+ name: "API Only",
892
+ description: "OpenAI-compatible API endpoint, no channels.",
893
+ channels: [],
894
+ providers: ["opencode"],
895
+ plugins: [],
896
+ keyCount: 1,
897
+ },
898
+ {
899
+ id: "custom",
900
+ name: "Custom",
901
+ description: "Full wizard: pick channels, providers, and plugins.",
902
+ channels: [],
903
+ providers: [],
904
+ plugins: [],
905
+ keyCount: 0,
906
+ },
907
+ ];
908
+
909
+ // --- Model catalog ---
910
+
911
+ export interface ModelOption {
912
+ id: string;
913
+ name: string;
914
+ provider: string;
915
+ providerId: string;
916
+ description: string;
917
+ costPerMessage: string;
918
+ hero: boolean;
919
+ recommended: boolean;
920
+ category: "reasoning" | "general" | "fast" | "code" | "vision" | "open-source";
921
+ }
922
+
923
+ export const heroModels: ModelOption[] = [
924
+ {
925
+ id: "anthropic/claude-sonnet-4-20250514",
926
+ name: "Claude Sonnet 4",
927
+ provider: "Anthropic",
928
+ providerId: "anthropic",
929
+ description: "Best at reasoning, coding, and conversation",
930
+ costPerMessage: "~$0.01/msg",
931
+ hero: true,
932
+ recommended: true,
933
+ category: "reasoning",
934
+ },
935
+ {
936
+ id: "openai/gpt-4o",
937
+ name: "GPT-4o",
938
+ provider: "OpenAI",
939
+ providerId: "openai",
940
+ description: "Great all-rounder, fast and reliable",
941
+ costPerMessage: "~$0.005/msg",
942
+ hero: true,
943
+ recommended: false,
944
+ category: "general",
945
+ },
946
+ {
947
+ id: "meta-llama/llama-3.3-70b-instruct",
948
+ name: "Llama 3.3 70B",
949
+ provider: "Meta",
950
+ providerId: "meta",
951
+ description: "Open source and fast, good for simple tasks",
952
+ costPerMessage: "~$0.001/msg",
953
+ hero: true,
954
+ recommended: false,
955
+ category: "open-source",
956
+ },
957
+ ];
958
+
959
+ export const additionalModels: ModelOption[] = [
960
+ {
961
+ id: "anthropic/claude-opus-4-20250514",
962
+ name: "Claude Opus 4",
963
+ provider: "Anthropic",
964
+ providerId: "anthropic",
965
+ description: "Most capable, deep reasoning",
966
+ costPerMessage: "~$0.08/msg",
967
+ hero: false,
968
+ recommended: false,
969
+ category: "reasoning",
970
+ },
971
+ {
972
+ id: "anthropic/claude-haiku-4-20250514",
973
+ name: "Claude Haiku 4",
974
+ provider: "Anthropic",
975
+ providerId: "anthropic",
976
+ description: "Fast and affordable",
977
+ costPerMessage: "~$0.001/msg",
978
+ hero: false,
979
+ recommended: false,
980
+ category: "fast",
981
+ },
982
+ {
983
+ id: "openai/gpt-4o-mini",
984
+ name: "GPT-4o Mini",
985
+ provider: "OpenAI",
986
+ providerId: "openai",
987
+ description: "Lightweight and fast",
988
+ costPerMessage: "~$0.001/msg",
989
+ hero: false,
990
+ recommended: false,
991
+ category: "fast",
992
+ },
993
+ {
994
+ id: "openai/o3",
995
+ name: "o3",
996
+ provider: "OpenAI",
997
+ providerId: "openai",
998
+ description: "Advanced reasoning model",
999
+ costPerMessage: "~$0.04/msg",
1000
+ hero: false,
1001
+ recommended: false,
1002
+ category: "reasoning",
1003
+ },
1004
+ {
1005
+ id: "google/gemini-2.5-pro",
1006
+ name: "Gemini 2.5 Pro",
1007
+ provider: "Google",
1008
+ providerId: "google",
1009
+ description: "Multimodal with long context",
1010
+ costPerMessage: "~$0.01/msg",
1011
+ hero: false,
1012
+ recommended: false,
1013
+ category: "vision",
1014
+ },
1015
+ {
1016
+ id: "google/gemini-2.5-flash",
1017
+ name: "Gemini 2.5 Flash",
1018
+ provider: "Google",
1019
+ providerId: "google",
1020
+ description: "Fast and cost-effective",
1021
+ costPerMessage: "~$0.002/msg",
1022
+ hero: false,
1023
+ recommended: false,
1024
+ category: "fast",
1025
+ },
1026
+ {
1027
+ id: "mistralai/mistral-large",
1028
+ name: "Mistral Large",
1029
+ provider: "Mistral",
1030
+ providerId: "mistral",
1031
+ description: "Strong multilingual model",
1032
+ costPerMessage: "~$0.008/msg",
1033
+ hero: false,
1034
+ recommended: false,
1035
+ category: "general",
1036
+ },
1037
+ {
1038
+ id: "mistralai/mistral-small",
1039
+ name: "Mistral Small",
1040
+ provider: "Mistral",
1041
+ providerId: "mistral",
1042
+ description: "Efficient and affordable",
1043
+ costPerMessage: "~$0.001/msg",
1044
+ hero: false,
1045
+ recommended: false,
1046
+ category: "fast",
1047
+ },
1048
+ {
1049
+ id: "cohere/command-r-plus",
1050
+ name: "Command R+",
1051
+ provider: "Cohere",
1052
+ providerId: "cohere",
1053
+ description: "Retrieval-augmented generation specialist",
1054
+ costPerMessage: "~$0.005/msg",
1055
+ hero: false,
1056
+ recommended: false,
1057
+ category: "general",
1058
+ },
1059
+ {
1060
+ id: "deepseek/deepseek-chat-v3",
1061
+ name: "DeepSeek V3",
1062
+ provider: "DeepSeek",
1063
+ providerId: "deepseek",
1064
+ description: "Strong reasoning at low cost",
1065
+ costPerMessage: "~$0.002/msg",
1066
+ hero: false,
1067
+ recommended: false,
1068
+ category: "reasoning",
1069
+ },
1070
+ {
1071
+ id: "meta-llama/llama-3.1-8b-instruct",
1072
+ name: "Llama 3.1 8B",
1073
+ provider: "Meta",
1074
+ providerId: "meta",
1075
+ description: "Small and very fast",
1076
+ costPerMessage: "~$0.0002/msg",
1077
+ hero: false,
1078
+ recommended: false,
1079
+ category: "fast",
1080
+ },
1081
+ {
1082
+ id: "qwen/qwen-2.5-72b-instruct",
1083
+ name: "Qwen 2.5 72B",
1084
+ provider: "Qwen",
1085
+ providerId: "qwen",
1086
+ description: "Multilingual powerhouse",
1087
+ costPerMessage: "~$0.003/msg",
1088
+ hero: false,
1089
+ recommended: false,
1090
+ category: "general",
1091
+ },
1092
+ ];
1093
+
1094
+ export const allModels: ModelOption[] = [...heroModels, ...additionalModels];
1095
+
1096
+ export const MODEL_COUNT = "200+";
1097
+
1098
+ // --- BYOK Providers ---
1099
+
1100
+ export interface ByokProvider {
1101
+ id: string;
1102
+ name: string;
1103
+ description: string;
1104
+ models: string;
1105
+ color: string;
1106
+ }
1107
+
1108
+ export const byokProviders: ByokProvider[] = [
1109
+ {
1110
+ id: "anthropic",
1111
+ name: "Anthropic",
1112
+ description: "Claude models directly",
1113
+ models: "Claude Opus, Sonnet, Haiku",
1114
+ color: "#D4A574",
1115
+ },
1116
+ {
1117
+ id: "openai",
1118
+ name: "OpenAI",
1119
+ description: "GPT-4, o1, GPT-4o directly",
1120
+ models: "GPT-4o, o1, o3",
1121
+ color: "#10A37F",
1122
+ },
1123
+ {
1124
+ id: "openrouter",
1125
+ name: "OpenRouter",
1126
+ description: "200+ models, one key",
1127
+ models: "All providers",
1128
+ color: "#6366F1",
1129
+ },
1130
+ {
1131
+ id: "google",
1132
+ name: "Google AI",
1133
+ description: "Gemini models directly",
1134
+ models: "Gemini 2.5 Pro, Flash",
1135
+ color: "#4285F4",
1136
+ },
1137
+ {
1138
+ id: "mistral",
1139
+ name: "Mistral",
1140
+ description: "Mistral models directly",
1141
+ models: "Large, Small, Codestral",
1142
+ color: "#FF7000",
1143
+ },
1144
+ {
1145
+ id: "groq",
1146
+ name: "Groq",
1147
+ description: "Ultra-fast inference",
1148
+ models: "Llama, Mixtral",
1149
+ color: "#F55036",
1150
+ },
1151
+ {
1152
+ id: "together",
1153
+ name: "Together",
1154
+ description: "Open-source model hosting",
1155
+ models: "Llama, Qwen, Mistral",
1156
+ color: "#0066FF",
1157
+ },
1158
+ ];
1159
+
1160
+ // --- Helpers ---
1161
+
1162
+ export function getAllPlugins(): PluginOption[] {
1163
+ return [...channelPlugins, ...providerPlugins, ...pluginCategories.flatMap((c) => c.plugins)];
1164
+ }
1165
+
1166
+ export function getPluginById(id: string): PluginOption | undefined {
1167
+ return getAllPlugins().find((p) => p.id === id);
1168
+ }
1169
+
1170
+ export function collectConfigFields(
1171
+ selectedChannels: string[],
1172
+ selectedProviders: string[],
1173
+ selectedPlugins: string[],
1174
+ ): OnboardingConfigField[] {
1175
+ const allIds = [...selectedChannels, ...selectedProviders, ...selectedPlugins];
1176
+ const allPlugins = getAllPlugins();
1177
+ const fields: OnboardingConfigField[] = [];
1178
+ const seen = new Set<string>();
1179
+
1180
+ for (const id of allIds) {
1181
+ const plugin = allPlugins.find((p) => p.id === id);
1182
+ if (!plugin) continue;
1183
+ for (const field of plugin.configFields) {
1184
+ if (!seen.has(field.key)) {
1185
+ seen.add(field.key);
1186
+ fields.push(field);
1187
+ }
1188
+ }
1189
+ }
1190
+
1191
+ return fields;
1192
+ }
1193
+
1194
+ export function resolveDependencies(
1195
+ selectedChannels: string[],
1196
+ selectedProviders: string[],
1197
+ selectedPlugins: string[],
1198
+ ): string[] {
1199
+ const allSelected = new Set([...selectedChannels, ...selectedProviders, ...selectedPlugins]);
1200
+ const allPlugins = getAllPlugins();
1201
+ const resolved = new Set(selectedPlugins);
1202
+
1203
+ for (const pluginId of resolved) {
1204
+ const plugin = allPlugins.find((p) => p.id === pluginId);
1205
+ if (!plugin?.requires) continue;
1206
+ for (const dep of plugin.requires) {
1207
+ if (!allSelected.has(dep)) {
1208
+ // Dependency not in channels/providers, add as plugin
1209
+ resolved.add(dep);
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ return [...resolved];
1215
+ }
1216
+
1217
+ /** Returns the config field for the shared AI key based on provider choice */
1218
+ export function getAiKeyField(provider: ByokAiProvider): OnboardingConfigField {
1219
+ return provider === "openrouter" ? openrouterKeyField : openaiKeyField;
1220
+ }
1221
+
1222
+ /** Returns superpowers that use the shared AI key */
1223
+ export function getAiKeySuperpowers(selectedIds: string[]): Superpower[] {
1224
+ return superpowers.filter((sp) => selectedIds.includes(sp.id) && sp.usesAiKey);
1225
+ }
1226
+
1227
+ /** Capability descriptions shown after key validation */
1228
+ export const AI_CAPABILITY_DESCRIPTIONS: Record<
1229
+ string,
1230
+ { label: string; openai: string; openrouter: string }
1231
+ > = {
1232
+ memory: {
1233
+ label: "Memory",
1234
+ openai: "Embeddings for long-term recall",
1235
+ openrouter: "Embeddings for long-term recall",
1236
+ },
1237
+ search: {
1238
+ label: "Search",
1239
+ openai: "Web and document search",
1240
+ openrouter: "Web and document search",
1241
+ },
1242
+ "text-gen": {
1243
+ label: "Text-gen",
1244
+ openai: "GPT-4o, o3 text generation",
1245
+ openrouter: "200+ AI models via OpenRouter",
1246
+ },
1247
+ };
1248
+
1249
+ export function validateField(field: OnboardingConfigField, value: string): string | null {
1250
+ if (!value.trim()) {
1251
+ return `${field.label} is required`;
1252
+ }
1253
+ if (field.validation) {
1254
+ try {
1255
+ const regex = new RegExp(field.validation.pattern);
1256
+ if (!regex.test(value)) {
1257
+ return field.validation.message;
1258
+ }
1259
+ } catch {
1260
+ // Invalid regex in field validation config — treat as validation failure
1261
+ return field.validation.message;
1262
+ }
1263
+ }
1264
+ return null;
1265
+ }