aimeat 2.4.0 → 2.6.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 (308) hide show
  1. package/dist/.env.example +33 -0
  2. package/dist/build-stamp.json +13 -0
  3. package/dist/locales/en.json +186 -20
  4. package/dist/locales/fi.json +186 -20
  5. package/dist/public/components/VoiceRecorder.js +116 -0
  6. package/dist/public/connection-done.html +53 -0
  7. package/dist/public/css/components/voice-recorder.css +66 -0
  8. package/dist/public/css/views/inbox.css +41 -0
  9. package/dist/public/css/views/profile.css +171 -95
  10. package/dist/public/css/views/transparency.css +5 -0
  11. package/dist/public/js/services/audio-recorder.js +171 -0
  12. package/dist/public/js/services/messages.js +21 -2
  13. package/dist/public/spa.html +3 -0
  14. package/dist/public/views/landing.js +4 -0
  15. package/dist/public/views/profile/access-tab/connections.js +264 -0
  16. package/dist/public/views/profile/access-tab.js +2 -0
  17. package/dist/public/views/profile/ai-transparency-card.js +22 -17
  18. package/dist/public/views/profile/inbox-tab/components.js +60 -10
  19. package/dist/public/views/profile/inbox-tab/helpers.js +33 -0
  20. package/dist/public/views/profile/inbox-tab/panels.js +6 -1
  21. package/dist/public/views/profile/inbox-tab/use-voice.js +50 -0
  22. package/dist/public/views/profile/inbox-tab/voice-parts.js +111 -0
  23. package/dist/public/views/profile/inbox-tab.js +18 -24
  24. package/dist/public/views/profile/openrouter/budget-panel.js +224 -0
  25. package/dist/public/views/profile/openrouter/model-picker.js +150 -0
  26. package/dist/public/views/profile/openrouter/pricing.js +139 -0
  27. package/dist/public/views/profile/openrouter-settings.js +425 -488
  28. package/dist/public/views/transparency.js +50 -0
  29. package/dist/scripts/build-sdk-libs.d.ts.map +1 -1
  30. package/dist/scripts/build-sdk-libs.js +1 -0
  31. package/dist/scripts/build-sdk-libs.js.map +1 -1
  32. package/dist/scripts/check-ai-disclosure.d.ts.map +1 -1
  33. package/dist/scripts/check-ai-disclosure.js +182 -0
  34. package/dist/scripts/check-ai-disclosure.js.map +1 -1
  35. package/dist/src/cli/connect/ai-provenance-carry.d.ts +247 -0
  36. package/dist/src/cli/connect/ai-provenance-carry.d.ts.map +1 -0
  37. package/dist/src/cli/connect/ai-provenance-carry.js +334 -0
  38. package/dist/src/cli/connect/ai-provenance-carry.js.map +1 -0
  39. package/dist/src/cli/connect/api-client.d.ts +11 -0
  40. package/dist/src/cli/connect/api-client.d.ts.map +1 -1
  41. package/dist/src/cli/connect/api-client.js +2 -0
  42. package/dist/src/cli/connect/api-client.js.map +1 -1
  43. package/dist/src/cli/connect/mcp/local-server.d.ts.map +1 -1
  44. package/dist/src/cli/connect/mcp/local-server.js +11 -0
  45. package/dist/src/cli/connect/mcp/local-server.js.map +1 -1
  46. package/dist/src/cli/connect/mcp/tools/agent-messages.d.ts +2 -0
  47. package/dist/src/cli/connect/mcp/tools/agent-messages.d.ts.map +1 -1
  48. package/dist/src/cli/connect/mcp/tools/agent-messages.js +5 -2
  49. package/dist/src/cli/connect/mcp/tools/agent-messages.js.map +1 -1
  50. package/dist/src/cli/connect/mcp/tools/agent-tasks.d.ts +2 -0
  51. package/dist/src/cli/connect/mcp/tools/agent-tasks.d.ts.map +1 -1
  52. package/dist/src/cli/connect/mcp/tools/agent-tasks.js +5 -2
  53. package/dist/src/cli/connect/mcp/tools/agent-tasks.js.map +1 -1
  54. package/dist/src/cli/connect/mcp/tools/apps.d.ts +3 -0
  55. package/dist/src/cli/connect/mcp/tools/apps.d.ts.map +1 -1
  56. package/dist/src/cli/connect/mcp/tools/apps.js +13 -5
  57. package/dist/src/cli/connect/mcp/tools/apps.js.map +1 -1
  58. package/dist/src/cli/connect/mcp/tools/boards.d.ts +3 -0
  59. package/dist/src/cli/connect/mcp/tools/boards.d.ts.map +1 -1
  60. package/dist/src/cli/connect/mcp/tools/boards.js +5 -2
  61. package/dist/src/cli/connect/mcp/tools/boards.js.map +1 -1
  62. package/dist/src/cli/connect/mcp/tools/core.d.ts +5 -0
  63. package/dist/src/cli/connect/mcp/tools/core.d.ts.map +1 -1
  64. package/dist/src/cli/connect/mcp/tools/core.js +25 -5
  65. package/dist/src/cli/connect/mcp/tools/core.js.map +1 -1
  66. package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts +2 -0
  67. package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts.map +1 -1
  68. package/dist/src/cli/connect/mcp/tools/dm-messages.js +11 -6
  69. package/dist/src/cli/connect/mcp/tools/dm-messages.js.map +1 -1
  70. package/dist/src/cli/connect/mcp/tools/exchange.d.ts +2 -0
  71. package/dist/src/cli/connect/mcp/tools/exchange.d.ts.map +1 -1
  72. package/dist/src/cli/connect/mcp/tools/exchange.js +8 -2
  73. package/dist/src/cli/connect/mcp/tools/exchange.js.map +1 -1
  74. package/dist/src/cli/connect/mcp/tools/knowledge.d.ts +3 -0
  75. package/dist/src/cli/connect/mcp/tools/knowledge.d.ts.map +1 -1
  76. package/dist/src/cli/connect/mcp/tools/knowledge.js +7 -3
  77. package/dist/src/cli/connect/mcp/tools/knowledge.js.map +1 -1
  78. package/dist/src/cli/connect/mcp/tools/organisms.d.ts +2 -0
  79. package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
  80. package/dist/src/cli/connect/mcp/tools/organisms.js +7 -2
  81. package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
  82. package/dist/src/cli/connect/mcp/tools/workspaces.d.ts +3 -0
  83. package/dist/src/cli/connect/mcp/tools/workspaces.d.ts.map +1 -1
  84. package/dist/src/cli/connect/mcp/tools/workspaces.js +8 -2
  85. package/dist/src/cli/connect/mcp/tools/workspaces.js.map +1 -1
  86. package/dist/src/cli/connect/tool-call-defs-core.d.ts +3 -0
  87. package/dist/src/cli/connect/tool-call-defs-core.d.ts.map +1 -1
  88. package/dist/src/cli/connect/tool-call-defs-core.js +7 -0
  89. package/dist/src/cli/connect/tool-call-defs-core.js.map +1 -1
  90. package/dist/src/cli/connect/tool-call.d.ts +3 -0
  91. package/dist/src/cli/connect/tool-call.d.ts.map +1 -1
  92. package/dist/src/cli/connect/tool-call.js +11 -1
  93. package/dist/src/cli/connect/tool-call.js.map +1 -1
  94. package/dist/src/config-types.d.ts +14 -0
  95. package/dist/src/config-types.d.ts.map +1 -1
  96. package/dist/src/config.d.ts.map +1 -1
  97. package/dist/src/config.js +10 -0
  98. package/dist/src/config.js.map +1 -1
  99. package/dist/src/data/public-pages.d.ts +12 -0
  100. package/dist/src/data/public-pages.d.ts.map +1 -1
  101. package/dist/src/data/public-pages.js +12 -0
  102. package/dist/src/data/public-pages.js.map +1 -1
  103. package/dist/src/generated/api-types.d.ts +969 -85
  104. package/dist/src/generated/api-types.d.ts.map +1 -1
  105. package/dist/src/mcp/ai-provenance-input.d.ts +45 -2
  106. package/dist/src/mcp/ai-provenance-input.d.ts.map +1 -1
  107. package/dist/src/mcp/ai-provenance-input.js +48 -2
  108. package/dist/src/mcp/ai-provenance-input.js.map +1 -1
  109. package/dist/src/mcp/apps.d.ts +7 -0
  110. package/dist/src/mcp/apps.d.ts.map +1 -1
  111. package/dist/src/mcp/apps.js +20 -2
  112. package/dist/src/mcp/apps.js.map +1 -1
  113. package/dist/src/models/ai-provenance-schemas.d.ts +7 -1
  114. package/dist/src/models/ai-provenance-schemas.d.ts.map +1 -1
  115. package/dist/src/models/ai-provenance-schemas.js +11 -1
  116. package/dist/src/models/ai-provenance-schemas.js.map +1 -1
  117. package/dist/src/models/connection-schemas.d.ts +203 -0
  118. package/dist/src/models/connection-schemas.d.ts.map +1 -0
  119. package/dist/src/models/connection-schemas.js +33 -0
  120. package/dist/src/models/connection-schemas.js.map +1 -0
  121. package/dist/src/models/message-schemas.d.ts +40 -0
  122. package/dist/src/models/message-schemas.d.ts.map +1 -1
  123. package/dist/src/models/message-schemas.js +23 -0
  124. package/dist/src/models/message-schemas.js.map +1 -1
  125. package/dist/src/routes/ai-provenance.d.ts +6 -0
  126. package/dist/src/routes/ai-provenance.d.ts.map +1 -1
  127. package/dist/src/routes/ai-provenance.js +34 -1
  128. package/dist/src/routes/ai-provenance.js.map +1 -1
  129. package/dist/src/routes/ai-transparency.d.ts +6 -1
  130. package/dist/src/routes/ai-transparency.d.ts.map +1 -1
  131. package/dist/src/routes/ai-transparency.js +17 -3
  132. package/dist/src/routes/ai-transparency.js.map +1 -1
  133. package/dist/src/routes/ai.d.ts +5 -0
  134. package/dist/src/routes/ai.d.ts.map +1 -1
  135. package/dist/src/routes/ai.js +83 -0
  136. package/dist/src/routes/ai.js.map +1 -1
  137. package/dist/src/routes/app-grants.d.ts.map +1 -1
  138. package/dist/src/routes/app-grants.js +4 -0
  139. package/dist/src/routes/app-grants.js.map +1 -1
  140. package/dist/src/routes/apps/drafts.d.ts +3 -0
  141. package/dist/src/routes/apps/drafts.d.ts.map +1 -1
  142. package/dist/src/routes/apps/drafts.js +13 -0
  143. package/dist/src/routes/apps/drafts.js.map +1 -1
  144. package/dist/src/routes/apps/publish.d.ts +5 -0
  145. package/dist/src/routes/apps/publish.d.ts.map +1 -1
  146. package/dist/src/routes/apps/publish.js +21 -5
  147. package/dist/src/routes/apps/publish.js.map +1 -1
  148. package/dist/src/routes/connections.d.ts +35 -0
  149. package/dist/src/routes/connections.d.ts.map +1 -0
  150. package/dist/src/routes/connections.js +406 -0
  151. package/dist/src/routes/connections.js.map +1 -0
  152. package/dist/src/routes/libs.js +1 -1
  153. package/dist/src/routes/libs.js.map +1 -1
  154. package/dist/src/routes/messages.d.ts +5 -0
  155. package/dist/src/routes/messages.d.ts.map +1 -1
  156. package/dist/src/routes/messages.js +92 -0
  157. package/dist/src/routes/messages.js.map +1 -1
  158. package/dist/src/routes/openrouter.d.ts +6 -1
  159. package/dist/src/routes/openrouter.d.ts.map +1 -1
  160. package/dist/src/routes/openrouter.js +37 -7
  161. package/dist/src/routes/openrouter.js.map +1 -1
  162. package/dist/src/routes/upload.d.ts +5 -0
  163. package/dist/src/routes/upload.d.ts.map +1 -1
  164. package/dist/src/routes/upload.js +32 -0
  165. package/dist/src/routes/upload.js.map +1 -1
  166. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  167. package/dist/src/server-bootstrap/routes-loader.js +2 -0
  168. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  169. package/dist/src/services/ai-completion.d.ts +48 -0
  170. package/dist/src/services/ai-completion.d.ts.map +1 -1
  171. package/dist/src/services/ai-completion.js +112 -72
  172. package/dist/src/services/ai-completion.js.map +1 -1
  173. package/dist/src/services/ai-disclosure.d.ts +6 -0
  174. package/dist/src/services/ai-disclosure.d.ts.map +1 -1
  175. package/dist/src/services/ai-disclosure.js +20 -2
  176. package/dist/src/services/ai-disclosure.js.map +1 -1
  177. package/dist/src/services/ai-provenance-page.d.ts +59 -0
  178. package/dist/src/services/ai-provenance-page.d.ts.map +1 -0
  179. package/dist/src/services/ai-provenance-page.js +185 -0
  180. package/dist/src/services/ai-provenance-page.js.map +1 -0
  181. package/dist/src/services/ai-provenance.d.ts +6 -0
  182. package/dist/src/services/ai-provenance.d.ts.map +1 -1
  183. package/dist/src/services/ai-provenance.js +5 -0
  184. package/dist/src/services/ai-provenance.js.map +1 -1
  185. package/dist/src/services/ai-transcription.d.ts +66 -0
  186. package/dist/src/services/ai-transcription.d.ts.map +1 -0
  187. package/dist/src/services/ai-transcription.js +90 -0
  188. package/dist/src/services/ai-transcription.js.map +1 -0
  189. package/dist/src/services/app-publish.d.ts.map +1 -1
  190. package/dist/src/services/app-publish.js +16 -1
  191. package/dist/src/services/app-publish.js.map +1 -1
  192. package/dist/src/services/build-app-prompt.d.ts.map +1 -1
  193. package/dist/src/services/build-app-prompt.js +2 -1
  194. package/dist/src/services/build-app-prompt.js.map +1 -1
  195. package/dist/src/services/connections/attach.d.ts +62 -0
  196. package/dist/src/services/connections/attach.d.ts.map +1 -0
  197. package/dist/src/services/connections/attach.js +168 -0
  198. package/dist/src/services/connections/attach.js.map +1 -0
  199. package/dist/src/services/connections/credential.d.ts +39 -0
  200. package/dist/src/services/connections/credential.d.ts.map +1 -0
  201. package/dist/src/services/connections/credential.js +66 -0
  202. package/dist/src/services/connections/credential.js.map +1 -0
  203. package/dist/src/services/connections/instance.d.ts +65 -0
  204. package/dist/src/services/connections/instance.d.ts.map +1 -0
  205. package/dist/src/services/connections/instance.js +144 -0
  206. package/dist/src/services/connections/instance.js.map +1 -0
  207. package/dist/src/services/connections/oauth.d.ts +77 -0
  208. package/dist/src/services/connections/oauth.d.ts.map +1 -0
  209. package/dist/src/services/connections/oauth.js +290 -0
  210. package/dist/src/services/connections/oauth.js.map +1 -0
  211. package/dist/src/services/connections/providers.d.ts +118 -0
  212. package/dist/src/services/connections/providers.d.ts.map +1 -0
  213. package/dist/src/services/connections/providers.js +260 -0
  214. package/dist/src/services/connections/providers.js.map +1 -0
  215. package/dist/src/services/connections/publish-gate.d.ts +103 -0
  216. package/dist/src/services/connections/publish-gate.d.ts.map +1 -0
  217. package/dist/src/services/connections/publish-gate.js +0 -0
  218. package/dist/src/services/connections/publish-gate.js.map +1 -0
  219. package/dist/src/services/connections/publish.d.ts +60 -0
  220. package/dist/src/services/connections/publish.d.ts.map +1 -0
  221. package/dist/src/services/connections/publish.js +340 -0
  222. package/dist/src/services/connections/publish.js.map +1 -0
  223. package/dist/src/services/connections/refresh.d.ts +55 -0
  224. package/dist/src/services/connections/refresh.d.ts.map +1 -0
  225. package/dist/src/services/connections/refresh.js +307 -0
  226. package/dist/src/services/connections/refresh.js.map +1 -0
  227. package/dist/src/services/markdown-negotiation.d.ts +17 -0
  228. package/dist/src/services/markdown-negotiation.d.ts.map +1 -1
  229. package/dist/src/services/markdown-negotiation.js +15 -0
  230. package/dist/src/services/markdown-negotiation.js.map +1 -1
  231. package/dist/src/services/message-send.d.ts.map +1 -1
  232. package/dist/src/services/message-send.js +16 -2
  233. package/dist/src/services/message-send.js.map +1 -1
  234. package/dist/src/services/openrouter.d.ts +67 -1
  235. package/dist/src/services/openrouter.d.ts.map +1 -1
  236. package/dist/src/services/openrouter.js +102 -11
  237. package/dist/src/services/openrouter.js.map +1 -1
  238. package/dist/src/services/upload-token.d.ts +5 -1
  239. package/dist/src/services/upload-token.d.ts.map +1 -1
  240. package/dist/src/services/upload-token.js +13 -1
  241. package/dist/src/services/upload-token.js.map +1 -1
  242. package/dist/src/services/workflow/engine-util.d.ts +18 -0
  243. package/dist/src/services/workflow/engine-util.d.ts.map +1 -1
  244. package/dist/src/services/workflow/engine-util.js +27 -0
  245. package/dist/src/services/workflow/engine-util.js.map +1 -1
  246. package/dist/src/services/workflow/engine.d.ts.map +1 -1
  247. package/dist/src/services/workflow/engine.js +4 -2
  248. package/dist/src/services/workflow/engine.js.map +1 -1
  249. package/dist/src/storage/interface.d.ts +2 -1
  250. package/dist/src/storage/interface.d.ts.map +1 -1
  251. package/dist/src/storage/providers/postgres-kysely/db-types.d.ts +65 -0
  252. package/dist/src/storage/providers/postgres-kysely/db-types.d.ts.map +1 -1
  253. package/dist/src/storage/providers/postgres-kysely/index.d.ts.map +1 -1
  254. package/dist/src/storage/providers/postgres-kysely/index.js +2 -1
  255. package/dist/src/storage/providers/postgres-kysely/index.js.map +1 -1
  256. package/dist/src/storage/providers/postgres-kysely/methods/connections.d.ts +44 -0
  257. package/dist/src/storage/providers/postgres-kysely/methods/connections.d.ts.map +1 -0
  258. package/dist/src/storage/providers/postgres-kysely/methods/connections.js +308 -0
  259. package/dist/src/storage/providers/postgres-kysely/methods/connections.js.map +1 -0
  260. package/dist/src/storage/providers/postgres-kysely/methods/node-infra.d.ts.map +1 -1
  261. package/dist/src/storage/providers/postgres-kysely/methods/node-infra.js +4 -2
  262. package/dist/src/storage/providers/postgres-kysely/methods/node-infra.js.map +1 -1
  263. package/dist/src/storage/providers/postgres-kysely/migrations/0021_connections.sql +116 -0
  264. package/dist/src/storage/providers/postgres-kysely/migrations/0022_provider_clients.sql +36 -0
  265. package/dist/src/storage/providers/postgres-kysely/migrations/0023_nonce_payload.sql +13 -0
  266. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  267. package/dist/src/storage/providers/sqlite/index.js +2 -1
  268. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  269. package/dist/src/storage/providers/sqlite/methods/community.d.ts.map +1 -1
  270. package/dist/src/storage/providers/sqlite/methods/community.js +2 -1
  271. package/dist/src/storage/providers/sqlite/methods/community.js.map +1 -1
  272. package/dist/src/storage/providers/sqlite/methods/connections.d.ts +60 -0
  273. package/dist/src/storage/providers/sqlite/methods/connections.d.ts.map +1 -0
  274. package/dist/src/storage/providers/sqlite/methods/connections.js +287 -0
  275. package/dist/src/storage/providers/sqlite/methods/connections.js.map +1 -0
  276. package/dist/src/storage/providers/sqlite/schema-tables-1.d.ts.map +1 -1
  277. package/dist/src/storage/providers/sqlite/schema-tables-1.js +4 -0
  278. package/dist/src/storage/providers/sqlite/schema-tables-1.js.map +1 -1
  279. package/dist/src/storage/providers/sqlite/schema-tables-3.d.ts.map +1 -1
  280. package/dist/src/storage/providers/sqlite/schema-tables-3.js +106 -0
  281. package/dist/src/storage/providers/sqlite/schema-tables-3.js.map +1 -1
  282. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  283. package/dist/src/storage/providers/sqlite/schema.js +2 -0
  284. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  285. package/dist/src/storage/repositories/connection.repository.d.ts +124 -0
  286. package/dist/src/storage/repositories/connection.repository.d.ts.map +1 -0
  287. package/dist/src/storage/repositories/connection.repository.js +2 -0
  288. package/dist/src/storage/repositories/connection.repository.js.map +1 -0
  289. package/dist/src/storage/types/agents-messaging.d.ts +19 -0
  290. package/dist/src/storage/types/agents-messaging.d.ts.map +1 -1
  291. package/dist/src/storage/types/agents-messaging.js.map +1 -1
  292. package/dist/src/storage/types/auth.d.ts +8 -1
  293. package/dist/src/storage/types/auth.d.ts.map +1 -1
  294. package/dist/src/utils/build-stamp.d.ts +56 -0
  295. package/dist/src/utils/build-stamp.d.ts.map +1 -0
  296. package/dist/src/utils/build-stamp.js +142 -0
  297. package/dist/src/utils/build-stamp.js.map +1 -0
  298. package/dist/src/utils/messaging.d.ts +14 -0
  299. package/dist/src/utils/messaging.d.ts.map +1 -1
  300. package/dist/src/utils/messaging.js +0 -0
  301. package/dist/src/utils/messaging.js.map +1 -1
  302. package/dist/static/sdk-libs/connect/index.js +199 -0
  303. package/dist/static/sdk-libs/connect/notes.js +47 -0
  304. package/dist/static/sdk-libs/connect/panel.js +238 -0
  305. package/dist/static/sdk-libs/data/index.js +63 -11
  306. package/dist/static/sdk-libs/dist/aimeat-connect.js +357 -0
  307. package/dist/static/sdk-libs/dist/aimeat-data.js +40 -9
  308. package/package.json +2 -2
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @file pricing.js
3
+ * @description Pure pricing + ranking helpers for the AI provider model pickers. No Preact, no DOM,
4
+ * no network — everything here is a function of the model list the provider returned, which is what
5
+ * makes it the one place the pricing rules can be read and checked.
6
+ *
7
+ * THE RULE THAT MATTERS: chat models are priced per token and can be shown as $/M. Audio models
8
+ * cannot. Measured 2026-08-01, `openai/whisper-large-v3` reports pricing.prompt 0.0015 on Together
9
+ * and 0.111 on Groq — per minute and per hour respectively, with nothing in the API distinguishing
10
+ * them. So audioPriceLabel() shows the reported number WITHOUT inventing a unit, and the settings
11
+ * page pairs it with a real measured cost from a test transcription. A derived "$/min" here would be
12
+ * confidently wrong for whole providers.
13
+ * @structure perMillion / fmtUsdPerMillion / chatPriceLabel / audioPriceLabel / isFree /
14
+ * acceptsImages / modelPageUrl / rankModels
15
+ * @usage import { chatPriceLabel, rankModels } from './pricing.js';
16
+ * @version-history
17
+ * v1.0.0 — 2026-08-01 — Extracted from the settings panel as part of the OpenRouter settings rework.
18
+ */
19
+
20
+ /** A per-token price as dollars per million tokens, or null when the provider gave no number. */
21
+ export function perMillion(v) {
22
+ const n = Number(v);
23
+ return Number.isFinite(n) ? n * 1e6 : null;
24
+ }
25
+
26
+ /** Format a per-million figure. Sub-dollar prices need three decimals to say anything at all. */
27
+ export function fmtUsdPerMillion(n) {
28
+ if (n === null || !Number.isFinite(n)) return null;
29
+ if (n === 0) return '0';
30
+ return n < 1 ? `$${n.toFixed(3)}` : `$${n.toFixed(2)}`;
31
+ }
32
+
33
+ /** true when the model costs nothing to prompt (17 of the 336 catalogue models at time of writing). */
34
+ export function isFree(model) {
35
+ return Number(model?.pricing?.prompt) === 0 && Number(model?.pricing?.completion || 0) === 0;
36
+ }
37
+
38
+ /** Whether the model reads images. Read from what it declares, never guessed from its name. */
39
+ export function acceptsImages(model) {
40
+ return Array.isArray(model?.input_modalities) && model.input_modalities.includes('image');
41
+ }
42
+
43
+ /** Whether the model reads audio directly in a chat turn (a different thing from an STT model). */
44
+ export function acceptsAudio(model) {
45
+ return Array.isArray(model?.input_modalities) && model.input_modalities.includes('audio');
46
+ }
47
+
48
+ /**
49
+ * "$5.00 / M in · $25.00 / M out" for a chat model, or null when the provider reported no pricing
50
+ * (LM Studio and self-hosted endpoints do not).
51
+ */
52
+ export function chatPriceLabel(model, t) {
53
+ const inUsd = fmtUsdPerMillion(perMillion(model?.pricing?.prompt));
54
+ if (inUsd === null) return null;
55
+ if (isFree(model)) return t('profile.openrouter.price.free');
56
+ const outUsd = fmtUsdPerMillion(perMillion(model?.pricing?.completion));
57
+ const inPart = t('profile.openrouter.price.perMillionIn', { usd: inUsd });
58
+ return outUsd === null ? inPart : `${inPart} · ${t('profile.openrouter.price.perMillionOut', { usd: outUsd })}`;
59
+ }
60
+
61
+ /**
62
+ * The reported price of an audio model, stated as a bare number.
63
+ *
64
+ * Deliberately unitless: see the file header. The caller shows this next to a link to the model page,
65
+ * where the unit is written out, and next to the measured cost of a real transcription.
66
+ */
67
+ export function audioPriceLabel(model, t) {
68
+ const raw = model?.pricing?.prompt;
69
+ const n = Number(raw);
70
+ if (!Number.isFinite(n)) return null;
71
+ if (n === 0) return t('profile.openrouter.price.free');
72
+ // Trailing zeros stripped: 0.00150 reads as a different number from 0.0015 to a human eye.
73
+ return t('profile.openrouter.price.audioReported', { value: String(Number(n.toPrecision(6))) });
74
+ }
75
+
76
+ /** The model's page on OpenRouter. Only meaningful for OpenRouter itself. */
77
+ export function modelPageUrl(modelId) {
78
+ return `https://openrouter.ai/${modelId}`;
79
+ }
80
+
81
+ /** Context length as "200 k", or null. */
82
+ export function contextLabel(model) {
83
+ const n = Number(model?.context_length);
84
+ if (!Number.isFinite(n) || n <= 0) return null;
85
+ return n >= 1000 ? `${Math.round(n / 1000)} k` : String(n);
86
+ }
87
+
88
+ /**
89
+ * The short "recommended" group shown above the full list.
90
+ *
91
+ * Derived from the live catalogue by rule, never a hardcoded list of slugs: the catalogue changes
92
+ * weekly, and a pinned list would rot silently into recommending models that no longer exist.
93
+ *
94
+ * - transcription: the whole list is small (13 models), so it is simply ordered by reported price.
95
+ * - vision: image-capable models, cheapest input first.
96
+ * - chat: the largest context in each of three price bands (free / under $1 per M / above), which
97
+ * surfaces one sensible default per budget rather than 336 undifferentiated rows.
98
+ */
99
+ export function rankModels(models, modality) {
100
+ const list = Array.isArray(models) ? models.slice() : [];
101
+ const priceOf = (m) => {
102
+ const n = Number(m?.pricing?.prompt);
103
+ return Number.isFinite(n) ? n : Number.POSITIVE_INFINITY;
104
+ };
105
+
106
+ if (modality === 'transcription' || modality === 'speech') {
107
+ return list.sort((a, b) => priceOf(a) - priceOf(b));
108
+ }
109
+
110
+ if (modality === 'vision') {
111
+ return list.filter(acceptsImages).sort((a, b) => priceOf(a) - priceOf(b));
112
+ }
113
+
114
+ const byContext = (a, b) => (Number(b.context_length) || 0) - (Number(a.context_length) || 0);
115
+ const bands = [
116
+ list.filter((m) => isFree(m)),
117
+ list.filter((m) => !isFree(m) && perMillion(m?.pricing?.prompt) !== null && perMillion(m.pricing.prompt) < 1),
118
+ list.filter((m) => perMillion(m?.pricing?.prompt) !== null && perMillion(m.pricing.prompt) >= 1),
119
+ ];
120
+ const picked = [];
121
+ const seen = new Set();
122
+ for (const band of bands) {
123
+ for (const m of band.sort(byContext).slice(0, 2)) {
124
+ if (!seen.has(m.id)) { seen.add(m.id); picked.push(m); }
125
+ }
126
+ }
127
+ // The free router is a genuine default (no vendor pinned) and is worth offering even when the
128
+ // banding above did not surface it.
129
+ const free = list.find((m) => m.id === 'openrouter/free');
130
+ if (free && !seen.has(free.id)) picked.unshift(free);
131
+ return picked;
132
+ }
133
+
134
+ /** Case-insensitive match over id and display name. */
135
+ export function matchesQuery(model, query) {
136
+ const q = String(query || '').trim().toLowerCase();
137
+ if (!q) return true;
138
+ return String(model.id).toLowerCase().includes(q) || String(model.name || '').toLowerCase().includes(q);
139
+ }