libre-webui 0.17.0 → 0.19.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 (438) hide show
  1. package/README.md +30 -17
  2. package/backend/dist/db.d.ts.map +1 -1
  3. package/backend/dist/db.js +195 -6
  4. package/backend/dist/db.js.map +1 -1
  5. package/backend/dist/index.d.ts.map +1 -1
  6. package/backend/dist/index.js +48 -19
  7. package/backend/dist/index.js.map +1 -1
  8. package/backend/dist/middleware/auth.d.ts.map +1 -1
  9. package/backend/dist/middleware/auth.js +35 -3
  10. package/backend/dist/middleware/auth.js.map +1 -1
  11. package/backend/dist/middleware/index.d.ts.map +1 -1
  12. package/backend/dist/models/personaModel.d.ts.map +1 -1
  13. package/backend/dist/models/personaModel.js.map +1 -1
  14. package/backend/dist/models/userModel.d.ts +24 -0
  15. package/backend/dist/models/userModel.d.ts.map +1 -1
  16. package/backend/dist/models/userModel.js +92 -24
  17. package/backend/dist/models/userModel.js.map +1 -1
  18. package/backend/dist/routes/agentCli.d.ts.map +1 -1
  19. package/backend/dist/routes/agentCli.js +4 -2
  20. package/backend/dist/routes/agentCli.js.map +1 -1
  21. package/backend/dist/routes/auth.d.ts.map +1 -1
  22. package/backend/dist/routes/auth.js +118 -55
  23. package/backend/dist/routes/auth.js.map +1 -1
  24. package/backend/dist/routes/chat.d.ts.map +1 -1
  25. package/backend/dist/routes/chat.js +106 -3
  26. package/backend/dist/routes/chat.js.map +1 -1
  27. package/backend/dist/routes/documents.d.ts.map +1 -1
  28. package/backend/dist/routes/documents.js +179 -41
  29. package/backend/dist/routes/documents.js.map +1 -1
  30. package/backend/dist/routes/embeddings.d.ts.map +1 -1
  31. package/backend/dist/routes/embeddings.js +2 -2
  32. package/backend/dist/routes/embeddings.js.map +1 -1
  33. package/backend/dist/routes/huggingfaceHub.d.ts.map +1 -1
  34. package/backend/dist/routes/huggingfaceHub.js +3 -1
  35. package/backend/dist/routes/huggingfaceHub.js.map +1 -1
  36. package/backend/dist/routes/imageGen.d.ts.map +1 -1
  37. package/backend/dist/routes/imageGen.js +2 -0
  38. package/backend/dist/routes/imageGen.js.map +1 -1
  39. package/backend/dist/routes/libreClaw.d.ts.map +1 -1
  40. package/backend/dist/routes/media.d.ts +3 -0
  41. package/backend/dist/routes/media.d.ts.map +1 -0
  42. package/backend/dist/routes/media.js +429 -0
  43. package/backend/dist/routes/media.js.map +1 -0
  44. package/backend/dist/routes/notes.d.ts +3 -0
  45. package/backend/dist/routes/notes.d.ts.map +1 -0
  46. package/backend/dist/routes/notes.js +121 -0
  47. package/backend/dist/routes/notes.js.map +1 -0
  48. package/backend/dist/routes/ollama.d.ts.map +1 -1
  49. package/backend/dist/routes/ollama.js +17 -85
  50. package/backend/dist/routes/ollama.js.map +1 -1
  51. package/backend/dist/routes/personas.d.ts.map +1 -1
  52. package/backend/dist/routes/personas.js +2 -0
  53. package/backend/dist/routes/personas.js.map +1 -1
  54. package/backend/dist/routes/plugins.d.ts.map +1 -1
  55. package/backend/dist/routes/plugins.js +28 -0
  56. package/backend/dist/routes/plugins.js.map +1 -1
  57. package/backend/dist/routes/preferences.d.ts.map +1 -1
  58. package/backend/dist/routes/systemDiagnostics.d.ts +3 -0
  59. package/backend/dist/routes/systemDiagnostics.d.ts.map +1 -0
  60. package/backend/dist/routes/systemDiagnostics.js +49 -0
  61. package/backend/dist/routes/systemDiagnostics.js.map +1 -0
  62. package/backend/dist/routes/tts.d.ts.map +1 -1
  63. package/backend/dist/routes/tts.js +4 -0
  64. package/backend/dist/routes/tts.js.map +1 -1
  65. package/backend/dist/routes/users.d.ts.map +1 -1
  66. package/backend/dist/routes/users.js +79 -0
  67. package/backend/dist/routes/users.js.map +1 -1
  68. package/backend/dist/routes/work.d.ts.map +1 -1
  69. package/backend/dist/routes/work.js +84 -0
  70. package/backend/dist/routes/work.js.map +1 -1
  71. package/backend/dist/services/agentCliService.d.ts +37 -4
  72. package/backend/dist/services/agentCliService.d.ts.map +1 -1
  73. package/backend/dist/services/agentCliService.js +229 -10
  74. package/backend/dist/services/agentCliService.js.map +1 -1
  75. package/backend/dist/services/assistantCompletionService.d.ts.map +1 -1
  76. package/backend/dist/services/authService.d.ts +15 -9
  77. package/backend/dist/services/authService.d.ts.map +1 -1
  78. package/backend/dist/services/authService.js +39 -29
  79. package/backend/dist/services/authService.js.map +1 -1
  80. package/backend/dist/services/chatGenerationService.d.ts.map +1 -1
  81. package/backend/dist/services/chatGenerationService.js +3 -3
  82. package/backend/dist/services/chatGenerationService.js.map +1 -1
  83. package/backend/dist/services/chatRequestService.d.ts +8 -2
  84. package/backend/dist/services/chatRequestService.d.ts.map +1 -1
  85. package/backend/dist/services/chatRequestService.js +30 -3
  86. package/backend/dist/services/chatRequestService.js.map +1 -1
  87. package/backend/dist/services/chatService.d.ts +6 -1
  88. package/backend/dist/services/chatService.d.ts.map +1 -1
  89. package/backend/dist/services/chatService.js +54 -1
  90. package/backend/dist/services/chatService.js.map +1 -1
  91. package/backend/dist/services/codexOAuthService.d.ts +22 -0
  92. package/backend/dist/services/codexOAuthService.d.ts.map +1 -0
  93. package/backend/dist/services/codexOAuthService.js +176 -0
  94. package/backend/dist/services/codexOAuthService.js.map +1 -0
  95. package/backend/dist/services/documentService.d.ts +17 -10
  96. package/backend/dist/services/documentService.d.ts.map +1 -1
  97. package/backend/dist/services/documentService.js +79 -58
  98. package/backend/dist/services/documentService.js.map +1 -1
  99. package/backend/dist/services/embeddingService.d.ts.map +1 -1
  100. package/backend/dist/services/encryptionService.d.ts.map +1 -1
  101. package/backend/dist/services/encryptionService.js +18 -8
  102. package/backend/dist/services/encryptionService.js.map +1 -1
  103. package/backend/dist/services/followUpService.d.ts +44 -0
  104. package/backend/dist/services/followUpService.d.ts.map +1 -0
  105. package/backend/dist/services/followUpService.js +123 -0
  106. package/backend/dist/services/followUpService.js.map +1 -0
  107. package/backend/dist/services/galleryService.d.ts +23 -1
  108. package/backend/dist/services/galleryService.d.ts.map +1 -1
  109. package/backend/dist/services/galleryService.js +125 -8
  110. package/backend/dist/services/galleryService.js.map +1 -1
  111. package/backend/dist/services/libreClawService.d.ts.map +1 -1
  112. package/backend/dist/services/mediaGenerationJobService.d.ts +25 -0
  113. package/backend/dist/services/mediaGenerationJobService.d.ts.map +1 -0
  114. package/backend/dist/services/mediaGenerationJobService.js +82 -0
  115. package/backend/dist/services/mediaGenerationJobService.js.map +1 -0
  116. package/backend/dist/services/memoryService.d.ts.map +1 -1
  117. package/backend/dist/services/memoryService.js.map +1 -1
  118. package/backend/dist/services/mutationEngineService.d.ts.map +1 -1
  119. package/backend/dist/services/oauthSecurity.d.ts +14 -0
  120. package/backend/dist/services/oauthSecurity.d.ts.map +1 -0
  121. package/backend/dist/services/oauthSecurity.js +101 -0
  122. package/backend/dist/services/oauthSecurity.js.map +1 -0
  123. package/backend/dist/services/ollamaService.d.ts +8 -2
  124. package/backend/dist/services/ollamaService.d.ts.map +1 -1
  125. package/backend/dist/services/ollamaService.js +40 -2
  126. package/backend/dist/services/ollamaService.js.map +1 -1
  127. package/backend/dist/services/personaService.d.ts.map +1 -1
  128. package/backend/dist/services/personaService.js.map +1 -1
  129. package/backend/dist/services/pluginActivationService.d.ts.map +1 -1
  130. package/backend/dist/services/pluginActivationService.js.map +1 -1
  131. package/backend/dist/services/pluginAudioGenerationService.d.ts +34 -0
  132. package/backend/dist/services/pluginAudioGenerationService.d.ts.map +1 -0
  133. package/backend/dist/services/pluginAudioGenerationService.js +211 -0
  134. package/backend/dist/services/pluginAudioGenerationService.js.map +1 -0
  135. package/backend/dist/services/pluginCapabilityRegistryService.d.ts.map +1 -1
  136. package/backend/dist/services/pluginCapabilityRegistryService.js +6 -0
  137. package/backend/dist/services/pluginCapabilityRegistryService.js.map +1 -1
  138. package/backend/dist/services/pluginCredentialsService.d.ts.map +1 -1
  139. package/backend/dist/services/pluginEmbeddingService.d.ts +2 -0
  140. package/backend/dist/services/pluginEmbeddingService.d.ts.map +1 -1
  141. package/backend/dist/services/pluginEmbeddingService.js +66 -35
  142. package/backend/dist/services/pluginEmbeddingService.js.map +1 -1
  143. package/backend/dist/services/pluginImageGenerationService.d.ts +2 -0
  144. package/backend/dist/services/pluginImageGenerationService.d.ts.map +1 -1
  145. package/backend/dist/services/pluginImageGenerationService.js +63 -38
  146. package/backend/dist/services/pluginImageGenerationService.js.map +1 -1
  147. package/backend/dist/services/pluginService.d.ts +43 -1
  148. package/backend/dist/services/pluginService.d.ts.map +1 -1
  149. package/backend/dist/services/pluginService.js +441 -71
  150. package/backend/dist/services/pluginService.js.map +1 -1
  151. package/backend/dist/services/pluginTTSService.d.ts +2 -0
  152. package/backend/dist/services/pluginTTSService.d.ts.map +1 -1
  153. package/backend/dist/services/pluginTTSService.js +27 -2
  154. package/backend/dist/services/pluginTTSService.js.map +1 -1
  155. package/backend/dist/services/pluginUsageService.d.ts +100 -0
  156. package/backend/dist/services/pluginUsageService.d.ts.map +1 -0
  157. package/backend/dist/services/pluginUsageService.js +291 -0
  158. package/backend/dist/services/pluginUsageService.js.map +1 -0
  159. package/backend/dist/services/pluginVariablesService.d.ts.map +1 -1
  160. package/backend/dist/services/pluginVariablesService.js.map +1 -1
  161. package/backend/dist/services/pluginVideoGenerationService.d.ts +45 -0
  162. package/backend/dist/services/pluginVideoGenerationService.d.ts.map +1 -0
  163. package/backend/dist/services/pluginVideoGenerationService.js +198 -0
  164. package/backend/dist/services/pluginVideoGenerationService.js.map +1 -0
  165. package/backend/dist/services/preferencesService.d.ts.map +1 -1
  166. package/backend/dist/services/preferencesService.js +16 -1
  167. package/backend/dist/services/preferencesService.js.map +1 -1
  168. package/backend/dist/services/registrationPolicy.d.ts +5 -0
  169. package/backend/dist/services/registrationPolicy.d.ts.map +1 -0
  170. package/backend/dist/services/registrationPolicy.js +21 -0
  171. package/backend/dist/services/registrationPolicy.js.map +1 -0
  172. package/backend/dist/services/simpleGitHubOAuth.d.ts +1 -1
  173. package/backend/dist/services/simpleGitHubOAuth.d.ts.map +1 -1
  174. package/backend/dist/services/simpleGitHubOAuth.js +13 -13
  175. package/backend/dist/services/simpleGitHubOAuth.js.map +1 -1
  176. package/backend/dist/services/simpleHuggingFaceOAuth.d.ts +1 -1
  177. package/backend/dist/services/simpleHuggingFaceOAuth.d.ts.map +1 -1
  178. package/backend/dist/services/simpleHuggingFaceOAuth.js +12 -17
  179. package/backend/dist/services/simpleHuggingFaceOAuth.js.map +1 -1
  180. package/backend/dist/services/systemDiagnosticsService.d.ts +117 -0
  181. package/backend/dist/services/systemDiagnosticsService.d.ts.map +1 -0
  182. package/backend/dist/services/systemDiagnosticsService.js +326 -0
  183. package/backend/dist/services/systemDiagnosticsService.js.map +1 -0
  184. package/backend/dist/services/titleGenerationService.d.ts.map +1 -1
  185. package/backend/dist/services/turnstileService.d.ts +2 -1
  186. package/backend/dist/services/turnstileService.d.ts.map +1 -1
  187. package/backend/dist/services/turnstileService.js +28 -2
  188. package/backend/dist/services/turnstileService.js.map +1 -1
  189. package/backend/dist/services/userService.d.ts +1 -0
  190. package/backend/dist/services/userService.js +1 -1
  191. package/backend/dist/services/workAgentService.d.ts.map +1 -1
  192. package/backend/dist/services/workAgentService.js.map +1 -1
  193. package/backend/dist/services/workEventService.d.ts.map +1 -1
  194. package/backend/dist/services/workEventService.js.map +1 -1
  195. package/backend/dist/services/workHostWorkspaceService.d.ts.map +1 -1
  196. package/backend/dist/services/workModelProviderService.d.ts +3 -0
  197. package/backend/dist/services/workModelProviderService.d.ts.map +1 -1
  198. package/backend/dist/services/workModelProviderService.js +67 -10
  199. package/backend/dist/services/workModelProviderService.js.map +1 -1
  200. package/backend/dist/services/workPreviewProxyService.d.ts +27 -0
  201. package/backend/dist/services/workPreviewProxyService.d.ts.map +1 -0
  202. package/backend/dist/services/workPreviewProxyService.js +514 -0
  203. package/backend/dist/services/workPreviewProxyService.js.map +1 -0
  204. package/backend/dist/services/workRuntimeService.d.ts +28 -7
  205. package/backend/dist/services/workRuntimeService.d.ts.map +1 -1
  206. package/backend/dist/services/workRuntimeService.js +287 -8
  207. package/backend/dist/services/workRuntimeService.js.map +1 -1
  208. package/backend/dist/services/workTaskService.d.ts.map +1 -1
  209. package/backend/dist/services/workTaskService.js.map +1 -1
  210. package/backend/dist/services/workTerminalService.d.ts +1 -1
  211. package/backend/dist/services/workTerminalService.d.ts.map +1 -1
  212. package/backend/dist/services/workTerminalService.js.map +1 -1
  213. package/backend/dist/storage.d.ts +12 -1
  214. package/backend/dist/storage.d.ts.map +1 -1
  215. package/backend/dist/storage.js +183 -9
  216. package/backend/dist/storage.js.map +1 -1
  217. package/backend/dist/storageMappers.d.ts +13 -0
  218. package/backend/dist/storageMappers.d.ts.map +1 -1
  219. package/backend/dist/storageMappers.js +5 -0
  220. package/backend/dist/storageMappers.js.map +1 -1
  221. package/backend/dist/types/index.d.ts +106 -1
  222. package/backend/dist/types/index.d.ts.map +1 -1
  223. package/backend/dist/types/index.js.map +1 -1
  224. package/backend/dist/types/work.d.ts +31 -0
  225. package/backend/dist/types/work.d.ts.map +1 -1
  226. package/backend/dist/utils/chatContext.js.map +1 -1
  227. package/backend/dist/utils/chatDocumentContext.d.ts +1 -1
  228. package/backend/dist/utils/chatDocumentContext.d.ts.map +1 -1
  229. package/backend/dist/utils/chatDocumentContext.js +9 -4
  230. package/backend/dist/utils/chatDocumentContext.js.map +1 -1
  231. package/backend/dist/utils/chatProviderSelection.d.ts.map +1 -1
  232. package/backend/dist/utils/generationUtils.d.ts.map +1 -1
  233. package/backend/dist/utils/hash.d.ts.map +1 -1
  234. package/backend/dist/utils/hash.js +13 -2
  235. package/backend/dist/utils/hash.js.map +1 -1
  236. package/backend/dist/utils/logger.d.ts +1 -1
  237. package/backend/dist/utils/logger.d.ts.map +1 -1
  238. package/backend/dist/utils/ollamaStreaming.d.ts +6 -2
  239. package/backend/dist/utils/ollamaStreaming.d.ts.map +1 -1
  240. package/backend/dist/utils/ollamaStreaming.js +12 -2
  241. package/backend/dist/utils/ollamaStreaming.js.map +1 -1
  242. package/backend/dist/utils/openAIResponsesAdapter.js.map +1 -1
  243. package/backend/dist/utils/packagePaths.js.map +1 -1
  244. package/backend/dist/utils/pluginChatAdapter.d.ts.map +1 -1
  245. package/backend/dist/utils/pluginChatAdapter.js +11 -4
  246. package/backend/dist/utils/pluginChatAdapter.js.map +1 -1
  247. package/backend/dist/utils/pluginChatContext.js.map +1 -1
  248. package/backend/dist/utils/pluginDefinitionTrust.d.ts.map +1 -1
  249. package/backend/dist/utils/pluginDefinitionTrust.js +2 -1
  250. package/backend/dist/utils/pluginDefinitionTrust.js.map +1 -1
  251. package/backend/dist/utils/pluginStreamAdapter.d.ts +10 -1
  252. package/backend/dist/utils/pluginStreamAdapter.d.ts.map +1 -1
  253. package/backend/dist/utils/pluginStreamAdapter.js +30 -23
  254. package/backend/dist/utils/pluginStreamAdapter.js.map +1 -1
  255. package/backend/dist/utils/pluginValidation.d.ts +4 -3
  256. package/backend/dist/utils/pluginValidation.d.ts.map +1 -1
  257. package/backend/dist/utils/pluginValidation.js +47 -2
  258. package/backend/dist/utils/pluginValidation.js.map +1 -1
  259. package/backend/dist/utils/resourceLimits.d.ts +10 -0
  260. package/backend/dist/utils/resourceLimits.d.ts.map +1 -0
  261. package/backend/dist/utils/resourceLimits.js +29 -0
  262. package/backend/dist/utils/resourceLimits.js.map +1 -0
  263. package/backend/dist/utils/thinkingPhaseTimer.d.ts +21 -0
  264. package/backend/dist/utils/thinkingPhaseTimer.d.ts.map +1 -0
  265. package/backend/dist/utils/thinkingPhaseTimer.js +84 -0
  266. package/backend/dist/utils/thinkingPhaseTimer.js.map +1 -0
  267. package/backend/dist/utils/webpageFetcher.d.ts +25 -0
  268. package/backend/dist/utils/webpageFetcher.d.ts.map +1 -0
  269. package/backend/dist/utils/webpageFetcher.js +231 -0
  270. package/backend/dist/utils/webpageFetcher.js.map +1 -0
  271. package/backend/dist/utils/websocketMessages.js.map +1 -1
  272. package/backend/dist/utils/workGit.d.ts +14 -0
  273. package/backend/dist/utils/workGit.d.ts.map +1 -0
  274. package/backend/dist/utils/workGit.js +207 -0
  275. package/backend/dist/utils/workGit.js.map +1 -0
  276. package/backend/dist/websocketServer.d.ts +1 -0
  277. package/backend/dist/websocketServer.d.ts.map +1 -1
  278. package/backend/dist/websocketServer.js +110 -25
  279. package/backend/dist/websocketServer.js.map +1 -1
  280. package/backend/dist/workTerminalServer.d.ts.map +1 -1
  281. package/backend/dist/workTerminalServer.js +3 -1
  282. package/backend/dist/workTerminalServer.js.map +1 -1
  283. package/backend/package.json +8 -7
  284. package/bin/cli.js +2 -2
  285. package/frontend/dist/assets/index-CFhy8YLA.css +1 -0
  286. package/frontend/dist/assets/index-_lG_lwU-.js +164 -0
  287. package/frontend/dist/assets/markdown-math-ClQbWekD.css +1 -0
  288. package/frontend/dist/assets/xterm-DHcfr8qj.css +1 -0
  289. package/frontend/dist/index.html +41 -16
  290. package/frontend/dist/js/ArtifactContainer-CjpvtR8G.js +1 -0
  291. package/frontend/dist/js/{ArtifactDemoPage-B4qsvUnF.js → ArtifactDemoPage-yrPXLBQo.js} +1 -1
  292. package/frontend/dist/js/ArtifactSlideOutPanel-DdsL2AOQ.js +1 -0
  293. package/frontend/dist/js/{Button-CRwzOaDZ.js → Button-X9TT9gTD.js} +1 -1
  294. package/frontend/dist/js/ChatPage-D9lWGM8g.js +16 -0
  295. package/frontend/dist/js/FirstTimeSetup-CwLPbHkI.js +1 -0
  296. package/frontend/dist/js/GalleryPage-C9DTdo_-.js +1 -0
  297. package/frontend/dist/js/HomePage-iF44c34e.js +1 -0
  298. package/frontend/dist/js/LibreClawPage-B9wWZIz6.js +1 -0
  299. package/frontend/dist/js/{ModelManagerModals-DMoEav1l.js → ModelManagerModals-D9j9RECi.js} +1 -1
  300. package/frontend/dist/js/ModelSelector-B0RKEJwC.js +1 -0
  301. package/frontend/dist/js/ModelsPage-DJXoIOs8.js +2 -0
  302. package/frontend/dist/js/NotesPage-BTNH6JR2.js +1 -0
  303. package/frontend/dist/js/{OptimizedSyntaxHighlighter-Ci9sRc78.js → OptimizedSyntaxHighlighter-CWYUn9Oe.js} +1 -1
  304. package/frontend/dist/js/PersonasPage--nqoJ-wA.js +13 -0
  305. package/frontend/dist/js/PluginUsagePage-BHwm-BBr.js +1 -0
  306. package/frontend/dist/js/{RichMessageContent-BJ49VN9j.js → RichMessageContent-DYRrXJ75.js} +3 -3
  307. package/frontend/dist/js/SettingsModal-y1Nzg55O.js +1 -0
  308. package/frontend/dist/js/SystemPage-CHez8h3p.js +1 -0
  309. package/frontend/dist/js/UserManagementPage-aJhKXQxG.js +1 -0
  310. package/frontend/dist/js/WorkPage-Ccla0MDm.js +5 -0
  311. package/frontend/dist/js/addon-fit-DIOBYJe3.js +1 -0
  312. package/frontend/dist/js/api-DVP8xiYT.js +19 -0
  313. package/frontend/dist/js/appStore-Cw7PmG-J.js +2 -0
  314. package/frontend/dist/js/ar-ONXFvOB1.js +2 -0
  315. package/frontend/dist/js/artifactHtml-C0Hu9I1u.js +41 -0
  316. package/frontend/dist/js/authStore-gv_MgDKr.js +3 -0
  317. package/frontend/dist/js/babel-CmENXdZw.js +16 -0
  318. package/frontend/dist/js/bn-BLhREPm6.js +2 -0
  319. package/frontend/dist/js/chatStore-DmDbolTg.js +2 -0
  320. package/frontend/dist/js/cs-Bg8E9fG5.js +2 -0
  321. package/frontend/dist/js/da-ODCU8w4x.js +2 -0
  322. package/frontend/dist/js/de-nuGHRJ5N.js +2 -0
  323. package/frontend/dist/js/es-DcN_cwum.js +2 -0
  324. package/frontend/dist/js/estree-DJwQLE88.js +44 -0
  325. package/frontend/dist/js/fr-C-L_xGr5.js +2 -0
  326. package/frontend/dist/js/hi-DUEtwA9G.js +2 -0
  327. package/frontend/dist/js/html-DdAOfeK4.js +25 -0
  328. package/frontend/dist/js/i18n-xir4Zz7C.js +2 -0
  329. package/frontend/dist/js/i18next-DgmgtwsV.js +1 -0
  330. package/frontend/dist/js/id-DNTYhti_.js +2 -0
  331. package/frontend/dist/js/imageGenModels-Dx_QMLJQ.js +1 -0
  332. package/frontend/dist/js/is-Bx28ymbQ.js +2 -0
  333. package/frontend/dist/js/it-BjW-JLlS.js +2 -0
  334. package/frontend/dist/js/ja-uThj4Q1w.js +2 -0
  335. package/frontend/dist/js/ko-DqgmMYXK.js +2 -0
  336. package/frontend/dist/js/logger-DSXjF4ye.js +1 -0
  337. package/frontend/dist/js/markdown-RaVsONor.js +70 -0
  338. package/frontend/dist/js/markdown-core-DHnR5Hw8.js +16 -0
  339. package/frontend/dist/js/markdown-math-B7FlYsmr.js +261 -0
  340. package/frontend/dist/js/messageCodeStyles-cTX0rQGK.js +2 -0
  341. package/frontend/dist/js/ms-R9kt_P0T.js +2 -0
  342. package/frontend/dist/js/nl-BjmtEgpK.js +2 -0
  343. package/frontend/dist/js/pl-C8_gU9r1.js +2 -0
  344. package/frontend/dist/js/postcss-B5WIGhAY.js +62 -0
  345. package/frontend/dist/js/pt-7BJForUD.js +2 -0
  346. package/frontend/dist/js/react-vendor-DPNXrO-8.js +9 -0
  347. package/frontend/dist/js/{rolldown-runtime-QTnfLwEv.js → rolldown-runtime-hePW80VL.js} +1 -1
  348. package/frontend/dist/js/router-vendor-Ca9zqOqT.js +1 -0
  349. package/frontend/dist/js/ru-D2HmhY6Y.js +2 -0
  350. package/frontend/dist/js/{standalone-DmLfI4Qj.js → standalone-B_aPlu7o.js} +8 -8
  351. package/frontend/dist/js/sv-zny3sDBM.js +2 -0
  352. package/frontend/dist/js/syntax-highlight-Ca9bHsaz.js +8 -0
  353. package/frontend/dist/js/th-CAzEKRz8.js +2 -0
  354. package/frontend/dist/js/tr-BmYgtmUo.js +2 -0
  355. package/frontend/dist/js/{typescript-Bcq62agC.js → typescript-Dz4OUZSl.js} +10 -10
  356. package/frontend/dist/js/ui-DDlcz2oy.js +2 -0
  357. package/frontend/dist/js/ui-vendor-D_pYT79f.js +177 -0
  358. package/frontend/dist/js/uk-eiYZ0uRq.js +2 -0
  359. package/frontend/dist/js/useTranslation-CdeLZqSW.js +1 -0
  360. package/frontend/dist/js/utils-BhpNrGnR.js +1 -0
  361. package/frontend/dist/js/utils-vendor-1zob7nK9.js +9 -0
  362. package/frontend/dist/js/vi-C7SVAWpO.js +2 -0
  363. package/frontend/dist/js/welcomePrompts-to5mv_gx.js +1 -0
  364. package/frontend/dist/js/xterm-Da5ppSVt.js +18 -0
  365. package/frontend/dist/js/{yaml-mLy9chOK.js → yaml-QD4znPfZ.js} +20 -20
  366. package/frontend/dist/js/zh-2Q3cQv3s.js +2 -0
  367. package/package.json +20 -13
  368. package/plugins/codex-oauth.json +19 -0
  369. package/plugins/openrouter.json +89 -0
  370. package/backend/dist/services/systemSettingsService.d.ts +0 -12
  371. package/backend/dist/services/systemSettingsService.d.ts.map +0 -1
  372. package/backend/dist/services/systemSettingsService.js +0 -70
  373. package/backend/dist/services/systemSettingsService.js.map +0 -1
  374. package/frontend/dist/assets/index-BqI0kezz.css +0 -1
  375. package/frontend/dist/assets/index-CRC6g6bL.js +0 -3
  376. package/frontend/dist/assets/markdown-math-CYlHEDRF.css +0 -1
  377. package/frontend/dist/assets/xterm-CmMoOwwu.css +0 -1
  378. package/frontend/dist/js/ArtifactContainer-Cdy-973y.js +0 -1
  379. package/frontend/dist/js/ArtifactSlideOutPanel-CmKgQ4eD.js +0 -1
  380. package/frontend/dist/js/ChatPage-CYT69oBR.js +0 -16
  381. package/frontend/dist/js/FirstTimeSetup-C8LdLP5J.js +0 -1
  382. package/frontend/dist/js/GalleryPage-Bm89pnS8.js +0 -1
  383. package/frontend/dist/js/HomePage-CeCYnMUG.js +0 -1
  384. package/frontend/dist/js/LibreClawPage-DGMEV6dm.js +0 -1
  385. package/frontend/dist/js/ModelSelector-DXPdGdQ_.js +0 -1
  386. package/frontend/dist/js/ModelsPage-D-FIy2x-.js +0 -2
  387. package/frontend/dist/js/PersonasPage-CIrEVFo5.js +0 -13
  388. package/frontend/dist/js/SettingsModal-CxtOXPY7.js +0 -1
  389. package/frontend/dist/js/UserManagementPage-BfyYGGFQ.js +0 -1
  390. package/frontend/dist/js/WorkPage-C7bW6Hxx.js +0 -4
  391. package/frontend/dist/js/addon-fit-Cj0_dfHF.js +0 -1
  392. package/frontend/dist/js/api-DCD41-l9.js +0 -19
  393. package/frontend/dist/js/appStore-Bw-8Exsx.js +0 -2
  394. package/frontend/dist/js/ar-BAHlIvy0.js +0 -2
  395. package/frontend/dist/js/artifactHtml-B7Oy2e__.js +0 -27
  396. package/frontend/dist/js/authStore-mOYH_AXI.js +0 -4
  397. package/frontend/dist/js/babel-BnLuavNv.js +0 -16
  398. package/frontend/dist/js/bn-BxquDVbd.js +0 -2
  399. package/frontend/dist/js/cs-C4DoKzRm.js +0 -2
  400. package/frontend/dist/js/da-CFslpGTw.js +0 -2
  401. package/frontend/dist/js/de-ChudoRh2.js +0 -2
  402. package/frontend/dist/js/demoMode-BhEpMVh5.js +0 -1
  403. package/frontend/dist/js/es-B36fefE-.js +0 -2
  404. package/frontend/dist/js/estree-0vwU39qX.js +0 -44
  405. package/frontend/dist/js/fr-BSVeTM5W.js +0 -2
  406. package/frontend/dist/js/hi-BGoFbl5V.js +0 -2
  407. package/frontend/dist/js/html-CApr2Bvi.js +0 -25
  408. package/frontend/dist/js/id-BfYs2p_j.js +0 -2
  409. package/frontend/dist/js/is-DZ1MS-5A.js +0 -2
  410. package/frontend/dist/js/it-CXtrPCG1.js +0 -2
  411. package/frontend/dist/js/ja-FPJ_NK_y.js +0 -2
  412. package/frontend/dist/js/ko-DVwD4ZZF.js +0 -2
  413. package/frontend/dist/js/logger-DbNs3-DK.js +0 -1
  414. package/frontend/dist/js/markdown-C78Qc6xu.js +0 -70
  415. package/frontend/dist/js/markdown-core-GI9BPwrB.js +0 -16
  416. package/frontend/dist/js/markdown-math-E4O1gcxj.js +0 -261
  417. package/frontend/dist/js/ms-PKPP0RX3.js +0 -2
  418. package/frontend/dist/js/nl-DWM5HJZ8.js +0 -2
  419. package/frontend/dist/js/pl-B9va8Tuf.js +0 -2
  420. package/frontend/dist/js/postcss-DSDkwgFE.js +0 -62
  421. package/frontend/dist/js/pt-P0IQi-Zr.js +0 -2
  422. package/frontend/dist/js/react-vendor-DyG_ODzn.js +0 -9
  423. package/frontend/dist/js/router-vendor-BjDI9mRe.js +0 -1
  424. package/frontend/dist/js/ru-Ca2bxYnc.js +0 -2
  425. package/frontend/dist/js/sv-DI4RDwyZ.js +0 -2
  426. package/frontend/dist/js/syntax-highlight-pM1xnNpY.js +0 -8
  427. package/frontend/dist/js/th-DN0bDS9d.js +0 -2
  428. package/frontend/dist/js/tr-ChqMv5Q4.js +0 -2
  429. package/frontend/dist/js/ui-vendor-DohPFHDW.js +0 -177
  430. package/frontend/dist/js/uk-Cr51h_XQ.js +0 -2
  431. package/frontend/dist/js/useBaseQuery-BAOlhjar.js +0 -1
  432. package/frontend/dist/js/useQuery-CcRqxveh.js +0 -1
  433. package/frontend/dist/js/useTranslation-Cs6iTP4m.js +0 -1
  434. package/frontend/dist/js/utils-DmZD5uA3.js +0 -1
  435. package/frontend/dist/js/utils-vendor-Cbaj0BC5.js +0 -9
  436. package/frontend/dist/js/vi-D4OhoVMH.js +0 -2
  437. package/frontend/dist/js/xterm-DKl1QLiw.js +0 -7
  438. package/frontend/dist/js/zh-Bvau7hlR.js +0 -2
@@ -19,13 +19,17 @@ import path from 'path';
19
19
  import { createHash } from 'crypto';
20
20
  import sanitize from 'sanitize-filename';
21
21
  import axios from 'axios';
22
+ import codexOAuthService, { CODEX_OAUTH_PLUGIN_ID, } from './codexOAuthService.js';
23
+ import { userModel } from '../models/userModel.js';
22
24
  import pluginCredentialsService from './pluginCredentialsService.js';
23
25
  import pluginVariablesService from './pluginVariablesService.js';
24
26
  import pluginActivationService from './pluginActivationService.js';
27
+ import { PluginAudioGenerationService } from './pluginAudioGenerationService.js';
25
28
  import { PluginCapabilityRegistryService } from './pluginCapabilityRegistryService.js';
26
29
  import { PluginEmbeddingService } from './pluginEmbeddingService.js';
27
30
  import { PluginImageGenerationService } from './pluginImageGenerationService.js';
28
31
  import { PluginTTSService } from './pluginTTSService.js';
32
+ import { PluginVideoGenerationService } from './pluginVideoGenerationService.js';
29
33
  import { applyPluginDefinitionPolicy, buildPluginChatPayload, convertProviderResponse, getOpenAICompatibleSamplingParameters, resolvePluginChatParameters, toOpenAICompatibleMessages, } from '../utils/pluginChatAdapter.js';
30
34
  import { streamAnthropicResponse, streamOpenAICompatibleResponse, streamOpenAIResponsesResponse, } from '../utils/pluginStreamAdapter.js';
31
35
  import { applyModelEndpointTemplate, assertSafePluginEndpoint, buildPluginAuthHeaders, buildPluginModelDiscoveryHeaders, pluginRequiresApiKey, resolvePluginApiConfig, resolvePluginEndpoint, resolvePluginModelsEndpoint, validatePluginModel, } from '../utils/pluginValidation.js';
@@ -34,6 +38,7 @@ import { resolveBundledPluginsDir } from '../utils/packagePaths.js';
34
38
  import { getDatabaseSafe } from '../db.js';
35
39
  import { createOpenAIResponsesStateScope, createPluginCredentialFingerprint, OPENAI_RESPONSES_INCOMPLETE_REASON_METADATA_KEY, } from '../utils/openAIResponsesAdapter.js';
36
40
  import { getPluginConnectionVariableNames } from '../utils/pluginConnectionVariables.js';
41
+ import pluginUsageService from './pluginUsageService.js';
37
42
  import { getPluginDefinitionFingerprint, matchesBundledPluginTrustAnchor, } from '../utils/pluginDefinitionTrust.js';
38
43
  const logger = createLogger('plugins');
39
44
  const readPositiveIntEnv = (name, fallback) => {
@@ -77,6 +82,7 @@ function getPluginRoutingAuthProjection(plugin) {
77
82
  return {
78
83
  name,
79
84
  endpoint: capability.endpoint ?? null,
85
+ models_endpoint: capability.models_endpoint ?? null,
80
86
  endpoint_variable: config.endpoint_variable ?? capability.endpoint_variable ?? null,
81
87
  };
82
88
  });
@@ -118,6 +124,10 @@ export class PluginService {
118
124
  this.discoveredModelsUpdatedAt = new Map();
119
125
  this.discoveryAttemptedAt = new Map();
120
126
  this.inflightDiscovery = new Map();
127
+ this.discoveredCapabilityModelsCache = new Map();
128
+ this.discoveredCapabilityModelsUpdatedAt = new Map();
129
+ this.capabilityDiscoveryAttemptedAt = new Map();
130
+ this.inflightCapabilityDiscovery = new Map();
121
131
  this.bundledRoutingProjectionCache = new Map();
122
132
  this.bundledPluginsDir = resolveBundledPluginsDir(import.meta.url);
123
133
  this.legacyPluginsDir = path.join(process.cwd(), 'plugins');
@@ -134,6 +144,7 @@ export class PluginService {
134
144
  getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
135
145
  getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
136
146
  validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
147
+ recordUsage: usage => pluginUsageService.record(usage),
137
148
  });
138
149
  this.ttsService = new PluginTTSService({
139
150
  getAllPlugins: userId => this.getActivePlugins(userId),
@@ -141,6 +152,7 @@ export class PluginService {
141
152
  getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
142
153
  getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
143
154
  validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
155
+ recordUsage: usage => pluginUsageService.record(usage),
144
156
  });
145
157
  this.imageGenerationService = new PluginImageGenerationService({
146
158
  getAllPlugins: userId => this.getActivePlugins(userId),
@@ -148,6 +160,23 @@ export class PluginService {
148
160
  getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
149
161
  getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
150
162
  validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
163
+ recordUsage: usage => pluginUsageService.record(usage),
164
+ });
165
+ this.audioGenerationService = new PluginAudioGenerationService({
166
+ getAllPlugins: userId => this.getActivePlugins(userId),
167
+ getPlugin: (id, userId) => this.getPlugin(id, userId),
168
+ getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
169
+ getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
170
+ validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
171
+ recordUsage: usage => pluginUsageService.record(usage),
172
+ });
173
+ this.videoGenerationService = new PluginVideoGenerationService({
174
+ getAllPlugins: userId => this.getActivePlugins(userId),
175
+ getPlugin: (id, userId) => this.getPlugin(id, userId),
176
+ getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
177
+ getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
178
+ validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
179
+ recordUsage: usage => pluginUsageService.record(usage),
151
180
  });
152
181
  this.capabilityRegistryService = new PluginCapabilityRegistryService({
153
182
  getAllPlugins: userId => this.getActivePlugins(userId),
@@ -157,6 +186,65 @@ export class PluginService {
157
186
  discoveredModelsCacheKey(pluginId, userId) {
158
187
  return `${userId}:${pluginId}`;
159
188
  }
189
+ discoveredCapabilityModelsCacheKey(pluginId, capability, userId) {
190
+ return `${userId}:${pluginId}:${capability}`;
191
+ }
192
+ getDiscoveredCapabilityModels(pluginId, capability, userId) {
193
+ const effectiveUserId = userId || 'default';
194
+ const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, effectiveUserId);
195
+ if (this.discoveredCapabilityModelsCache.has(cacheKey)) {
196
+ return this.discoveredCapabilityModelsCache.get(cacheKey) || undefined;
197
+ }
198
+ const db = getDatabaseSafe();
199
+ if (!db) {
200
+ this.discoveredCapabilityModelsCache.set(cacheKey, null);
201
+ return undefined;
202
+ }
203
+ try {
204
+ const row = db
205
+ .prepare(`SELECT models_json, updated_at
206
+ FROM plugin_discovered_capability_models
207
+ WHERE user_id = ? AND plugin_id = ? AND capability = ?`)
208
+ .get(effectiveUserId, pluginId, capability);
209
+ if (!row) {
210
+ this.discoveredCapabilityModelsCache.set(cacheKey, null);
211
+ return undefined;
212
+ }
213
+ const parsed = JSON.parse(row.models_json);
214
+ const models = Array.isArray(parsed)
215
+ ? Array.from(new Set(parsed.filter((model) => typeof model === 'string' && model.length > 0)))
216
+ : [];
217
+ if (models.length === 0) {
218
+ this.discoveredCapabilityModelsCache.set(cacheKey, null);
219
+ return undefined;
220
+ }
221
+ this.discoveredCapabilityModelsCache.set(cacheKey, models);
222
+ this.discoveredCapabilityModelsUpdatedAt.set(cacheKey, row.updated_at);
223
+ return models;
224
+ }
225
+ catch (error) {
226
+ logger.warn('Failed to read discovered %s models for plugin %s:', capability, pluginId, error);
227
+ this.discoveredCapabilityModelsCache.set(cacheKey, null);
228
+ return undefined;
229
+ }
230
+ }
231
+ storeDiscoveredCapabilityModels(pluginId, capability, models, userId) {
232
+ const effectiveUserId = userId || 'default';
233
+ const uniqueModels = Array.from(new Set(models));
234
+ const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, effectiveUserId);
235
+ const discoveredAt = Date.now();
236
+ this.discoveredCapabilityModelsCache.set(cacheKey, uniqueModels);
237
+ this.discoveredCapabilityModelsUpdatedAt.set(cacheKey, discoveredAt);
238
+ const db = getDatabaseSafe();
239
+ if (!db)
240
+ return;
241
+ db.prepare(`INSERT INTO plugin_discovered_capability_models
242
+ (user_id, plugin_id, capability, models_json, updated_at)
243
+ VALUES (?, ?, ?, ?, ?)
244
+ ON CONFLICT(user_id, plugin_id, capability) DO UPDATE SET
245
+ models_json = excluded.models_json,
246
+ updated_at = excluded.updated_at`).run(effectiveUserId, pluginId, capability, JSON.stringify(uniqueModels), discoveredAt);
247
+ }
160
248
  getDiscoveredModels(pluginId, userId) {
161
249
  const effectiveUserId = userId || 'default';
162
250
  const cacheKey = this.discoveredModelsCacheKey(pluginId, effectiveUserId);
@@ -219,6 +307,7 @@ export class PluginService {
219
307
  }
220
308
  }
221
309
  clearDiscoveredModels(pluginId, userId) {
310
+ this.clearDiscoveredCapabilityModels(pluginId, userId);
222
311
  const db = getDatabaseSafe();
223
312
  if (userId) {
224
313
  const cacheKey = this.discoveredModelsCacheKey(pluginId, userId);
@@ -251,13 +340,58 @@ export class PluginService {
251
340
  logger.warn('Failed to clear discovered models for plugin %s:', pluginId, error);
252
341
  }
253
342
  }
343
+ clearDiscoveredCapabilityModels(pluginId, userId) {
344
+ const db = getDatabaseSafe();
345
+ if (userId) {
346
+ for (const capability of ['image', 'tts', 'audio', 'video']) {
347
+ const key = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId);
348
+ this.discoveredCapabilityModelsCache.delete(key);
349
+ this.discoveredCapabilityModelsUpdatedAt.delete(key);
350
+ this.capabilityDiscoveryAttemptedAt.delete(key);
351
+ }
352
+ if (db) {
353
+ db.prepare(`DELETE FROM plugin_discovered_capability_models
354
+ WHERE user_id = ? AND plugin_id = ?`).run(userId, pluginId);
355
+ }
356
+ return;
357
+ }
358
+ for (const key of this.discoveredCapabilityModelsCache.keys()) {
359
+ if (['image', 'tts', 'audio', 'video'].some(capability => key.endsWith(`:${pluginId}:${capability}`))) {
360
+ this.discoveredCapabilityModelsCache.delete(key);
361
+ this.discoveredCapabilityModelsUpdatedAt.delete(key);
362
+ this.capabilityDiscoveryAttemptedAt.delete(key);
363
+ }
364
+ }
365
+ if (db) {
366
+ db.prepare('DELETE FROM plugin_discovered_capability_models WHERE plugin_id = ?').run(pluginId);
367
+ }
368
+ }
254
369
  applyDiscoveredModels(plugin, userId) {
255
370
  if (!this.canUseStoredConnectionOverrides(userId) &&
256
371
  pluginVariablesService.hasStoredConnectionOverride(plugin.id, userId, plugin.variables, getPluginConnectionVariableNames(plugin))) {
257
372
  return plugin;
258
373
  }
259
374
  const models = this.getDiscoveredModels(plugin.id, userId);
260
- return models ? { ...plugin, model_map: [...models] } : plugin;
375
+ const capabilities = plugin.capabilities
376
+ ? { ...plugin.capabilities }
377
+ : undefined;
378
+ if (capabilities) {
379
+ for (const capability of ['image', 'tts', 'audio', 'video']) {
380
+ const definition = capabilities[capability];
381
+ const discovered = this.getDiscoveredCapabilityModels(plugin.id, capability, userId);
382
+ if (definition && discovered) {
383
+ capabilities[capability] = {
384
+ ...definition,
385
+ model_map: [...discovered],
386
+ };
387
+ }
388
+ }
389
+ }
390
+ return {
391
+ ...plugin,
392
+ ...(models ? { model_map: [...models] } : {}),
393
+ ...(capabilities ? { capabilities } : {}),
394
+ };
261
395
  }
262
396
  ensurePluginsDirectory() {
263
397
  if (!fs.existsSync(this.pluginsDir)) {
@@ -420,6 +554,15 @@ export class PluginService {
420
554
  * @returns The API key or null if not found
421
555
  */
422
556
  getApiKey(plugin, userId) {
557
+ if (plugin.id === CODEX_OAUTH_PLUGIN_ID) {
558
+ // Resolved from the server's Codex CLI sign-in, never user credentials.
559
+ // A writable same-ID definition must never receive the server user's
560
+ // OAuth bearer token, even when an administrator approved that file.
561
+ return this.usesTrustedBundledRouting(plugin) &&
562
+ matchesBundledPluginTrustAnchor(plugin)
563
+ ? codexOAuthService.getCachedAccessToken()
564
+ : null;
565
+ }
423
566
  const hasHonoredConnectionOverride = this.canUseStoredConnectionOverrides(userId) &&
424
567
  pluginVariablesService.hasStoredConnectionOverride(plugin.id, userId, plugin.variables, getPluginConnectionVariableNames(plugin));
425
568
  const usesTrustedBundledRouting = this.usesTrustedBundledRouting(plugin);
@@ -566,6 +709,98 @@ export class PluginService {
566
709
  new Promise(resolve => setTimeout(resolve, modelDiscoveryRefreshDeadlineMs()).unref?.()),
567
710
  ]);
568
711
  }
712
+ async discoverCapabilityModels(pluginId, capability, userId) {
713
+ const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId || 'default');
714
+ const inflight = this.inflightCapabilityDiscovery.get(cacheKey);
715
+ if (inflight)
716
+ return inflight;
717
+ const attempt = this.runCapabilityModelDiscovery(pluginId, capability, userId).finally(() => this.inflightCapabilityDiscovery.delete(cacheKey));
718
+ this.inflightCapabilityDiscovery.set(cacheKey, attempt);
719
+ return attempt;
720
+ }
721
+ isCapabilityModelDiscoveryDue(pluginId, capability, userId) {
722
+ const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId || 'default');
723
+ const attemptedAt = this.capabilityDiscoveryAttemptedAt.get(cacheKey);
724
+ if (attemptedAt && Date.now() - attemptedAt < modelDiscoveryRetryMs()) {
725
+ return false;
726
+ }
727
+ const models = this.getDiscoveredCapabilityModels(pluginId, capability, userId);
728
+ const updatedAt = this.discoveredCapabilityModelsUpdatedAt.get(cacheKey);
729
+ return (!models || !updatedAt || Date.now() - updatedAt >= modelDiscoveryTtlMs());
730
+ }
731
+ async refreshStaleCapabilityModels(capability, userId) {
732
+ const due = this.getActivePlugins(userId).filter(plugin => {
733
+ const definition = plugin.capabilities?.[capability];
734
+ return (Boolean(definition?.models_endpoint) &&
735
+ this.isCapabilityModelDiscoveryDue(plugin.id, capability, userId));
736
+ });
737
+ if (due.length === 0)
738
+ return;
739
+ const refreshes = Promise.all(due.map(plugin => this.discoverCapabilityModels(plugin.id, capability, userId).catch(() => undefined)));
740
+ await Promise.race([
741
+ refreshes,
742
+ new Promise(resolve => setTimeout(resolve, modelDiscoveryRefreshDeadlineMs()).unref?.()),
743
+ ]);
744
+ }
745
+ async runCapabilityModelDiscovery(pluginId, capability, userId) {
746
+ const plugin = this.getPlugin(pluginId, userId);
747
+ const definition = plugin?.capabilities?.[capability];
748
+ if (!plugin || !definition) {
749
+ return {
750
+ models: [],
751
+ outcome: 'unavailable',
752
+ reason: `Plugin has no ${capability} capability`,
753
+ };
754
+ }
755
+ if (!definition.models_endpoint) {
756
+ return { models: definition.model_map, outcome: 'unchanged' };
757
+ }
758
+ const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId || 'default');
759
+ this.capabilityDiscoveryAttemptedAt.set(cacheKey, Date.now());
760
+ const modelsEndpoint = definition.models_endpoint;
761
+ assertSafePluginEndpoint(modelsEndpoint, `${capability} model discovery endpoint`);
762
+ const apiKey = this.getApiKey(plugin, userId);
763
+ if (pluginRequiresApiKey(plugin) && !apiKey) {
764
+ return {
765
+ models: definition.model_map,
766
+ outcome: 'missing_credentials',
767
+ reason: this.describeMissingCredential(plugin),
768
+ };
769
+ }
770
+ try {
771
+ const response = await axios.get(modelsEndpoint, {
772
+ headers: buildPluginModelDiscoveryHeaders(plugin, apiKey, modelsEndpoint),
773
+ timeout: MODEL_DISCOVERY_REQUEST_TIMEOUT_MS,
774
+ maxRedirects: 0,
775
+ });
776
+ const data = response.data?.data;
777
+ const models = Array.isArray(data)
778
+ ? Array.from(new Set(data
779
+ .map((entry) => entry?.id)
780
+ .filter((id) => typeof id === 'string' && id.length > 0)))
781
+ : [];
782
+ if (models.length === 0) {
783
+ return {
784
+ models: definition.model_map,
785
+ outcome: 'unavailable',
786
+ reason: 'The provider returned no models',
787
+ };
788
+ }
789
+ const previous = definition.model_map;
790
+ this.storeDiscoveredCapabilityModels(pluginId, capability, models, userId);
791
+ return {
792
+ models,
793
+ outcome: JSON.stringify(previous) === JSON.stringify(models)
794
+ ? 'unchanged'
795
+ : 'updated',
796
+ };
797
+ }
798
+ catch (error) {
799
+ const reason = describeModelDiscoveryFailure(error);
800
+ logger.debug('[Plugin] %s model discovery unavailable for %s (%s)', capability, pluginId, reason);
801
+ return { models: definition.model_map, outcome: 'unavailable', reason };
802
+ }
803
+ }
569
804
  /**
570
805
  * Say why no key was usable. An environment key is deliberately ignored for a
571
806
  * provider whose definition was installed into the writable plugins directory,
@@ -602,7 +837,7 @@ export class PluginService {
602
837
  reason: this.describeMissingCredential(plugin),
603
838
  };
604
839
  }
605
- const headers = buildPluginModelDiscoveryHeaders(plugin, apiKey);
840
+ const headers = buildPluginModelDiscoveryHeaders(plugin, apiKey, modelsEndpoint);
606
841
  try {
607
842
  const response = await axios.get(modelsEndpoint, {
608
843
  headers,
@@ -689,7 +924,20 @@ export class PluginService {
689
924
  logger.error(`Failed to read plugins directory ${pluginsDir}:`, error);
690
925
  }
691
926
  }
692
- return Array.from(plugins.values());
927
+ return Array.from(plugins.values()).filter(plugin => this.isPluginVisibleToUser(plugin, userId));
928
+ }
929
+ /**
930
+ * The codex-oauth plugin rides the server user's ChatGPT sign-in, so it is
931
+ * administrator-only and hidden entirely when no sign-in exists.
932
+ */
933
+ isPluginVisibleToUser(plugin, userId) {
934
+ if (plugin.id !== CODEX_OAUTH_PLUGIN_ID)
935
+ return true;
936
+ if (!codexOAuthService.isAvailable())
937
+ return false;
938
+ if (!userId)
939
+ return false;
940
+ return userModel.getUserById(userId)?.role === 'admin';
693
941
  }
694
942
  // Get a specific plugin by ID
695
943
  getPlugin(id, userId) {
@@ -707,7 +955,8 @@ export class PluginService {
707
955
  const parsedPlugin = JSON.parse(content);
708
956
  if (this.validatePlugin(parsedPlugin) &&
709
957
  parsedPlugin.id === sanitizedId &&
710
- this.isPluginDefinitionApproved(parsedPlugin, filePath)) {
958
+ this.isPluginDefinitionApproved(parsedPlugin, filePath) &&
959
+ this.isPluginVisibleToUser(parsedPlugin, userId)) {
711
960
  const plugin = applyPluginDefinitionPolicy(parsedPlugin);
712
961
  plugin.active = this.isPluginActive(plugin.id, userId);
713
962
  return this.applyDiscoveredModels(plugin, userId);
@@ -723,6 +972,9 @@ export class PluginService {
723
972
  if (!this.validatePlugin(pluginData)) {
724
973
  throw new Error('Invalid plugin structure');
725
974
  }
975
+ if (pluginData.id === CODEX_OAUTH_PLUGIN_ID) {
976
+ throw new Error('The bundled Codex OAuth plugin ID is reserved');
977
+ }
726
978
  if (!this.canUseStoredConnectionOverrides(approvedByUserId)) {
727
979
  throw new Error('Administrator approval is required');
728
980
  }
@@ -810,7 +1062,12 @@ export class PluginService {
810
1062
  }
811
1063
  // Wait for discovery so the activation response and the UI's first reload
812
1064
  // observe the same user-scoped model catalog.
813
- await this.discoverModels(id, userId).catch(() => { });
1065
+ await Promise.all([
1066
+ this.discoverModels(id, userId).catch(() => []),
1067
+ ...['image', 'tts', 'audio', 'video']
1068
+ .filter(capability => plugin.capabilities?.[capability]?.models_endpoint)
1069
+ .map(capability => this.discoverCapabilityModels(id, capability, userId).catch(() => undefined)),
1070
+ ]);
814
1071
  return true;
815
1072
  }
816
1073
  // Deactivate a specific plugin
@@ -886,6 +1143,20 @@ export class PluginService {
886
1143
  if (!activePlugin.model_map.includes(model)) {
887
1144
  throw new Error(`Model ${model} is not supported by plugin ${activePlugin.id}`);
888
1145
  }
1146
+ if (activePlugin.id === CODEX_OAUTH_PLUGIN_ID) {
1147
+ await codexOAuthService.ensureFreshToken();
1148
+ // The codex endpoint only answers as an SSE stream; aggregate it here
1149
+ // so non-streaming callers still get a complete response.
1150
+ let aggregated = '';
1151
+ for await (const chunk of this.executePluginStreamRequest(model, messages, options, userId, activePlugin.id)) {
1152
+ if (chunk.type === 'content' && chunk.content) {
1153
+ aggregated += chunk.content;
1154
+ }
1155
+ }
1156
+ return {
1157
+ choices: [{ message: { role: 'assistant', content: aggregated } }],
1158
+ };
1159
+ }
889
1160
  const pluginVars = this.getPluginVariables(activePlugin, userId);
890
1161
  const { apiMode, endpoint: effectiveEndpoint } = resolvePluginApiConfig(activePlugin, pluginVars);
891
1162
  const processedEndpoint = applyModelEndpointTemplate(effectiveEndpoint, model);
@@ -897,18 +1168,45 @@ export class PluginService {
897
1168
  const providerStateScope = apiMode === 'responses'
898
1169
  ? createOpenAIResponsesStateScope(activePlugin.id, model, processedEndpoint, createPluginCredentialFingerprint(apiKey))
899
1170
  : undefined;
900
- const headers = buildPluginAuthHeaders(activePlugin, apiKey);
1171
+ const headers = buildPluginAuthHeaders(activePlugin, apiKey, processedEndpoint);
901
1172
  const { payload, headers: payloadHeaders } = buildPluginChatPayload(activePlugin, model, messages, options, pluginVars, false, apiMode, providerStateScope);
902
1173
  Object.assign(headers, payloadHeaders);
1174
+ const startedAt = Date.now();
903
1175
  try {
904
1176
  const response = await axios.post(processedEndpoint, payload, {
905
1177
  headers,
906
1178
  timeout: 60000, // 60 second timeout
907
1179
  maxRedirects: 0,
908
1180
  });
909
- return convertProviderResponse(activePlugin, response.data, model, apiMode, providerStateScope);
1181
+ const normalized = convertProviderResponse(activePlugin, response.data, model, apiMode, providerStateScope);
1182
+ pluginUsageService.record({
1183
+ userId,
1184
+ pluginId: activePlugin.id,
1185
+ pluginName: activePlugin.name,
1186
+ capability: 'chat',
1187
+ model,
1188
+ status: 'success',
1189
+ durationMs: Date.now() - startedAt,
1190
+ tokens: normalized.usage
1191
+ ? {
1192
+ promptTokens: normalized.usage.prompt_tokens,
1193
+ completionTokens: normalized.usage.completion_tokens,
1194
+ totalTokens: normalized.usage.total_tokens,
1195
+ }
1196
+ : undefined,
1197
+ });
1198
+ return normalized;
910
1199
  }
911
1200
  catch (error) {
1201
+ pluginUsageService.record({
1202
+ userId,
1203
+ pluginId: activePlugin.id,
1204
+ pluginName: activePlugin.name,
1205
+ capability: 'chat',
1206
+ model,
1207
+ status: 'error',
1208
+ durationMs: Date.now() - startedAt,
1209
+ });
912
1210
  logger.error(`Plugin request failed for ${activePlugin.id}:`, error);
913
1211
  if (error && typeof error === 'object' && 'response' in error) {
914
1212
  const axiosError = error;
@@ -937,6 +1235,9 @@ export class PluginService {
937
1235
  if (!activePlugin.model_map.includes(model)) {
938
1236
  throw new Error(`Model ${model} is not supported by plugin ${activePlugin.id}`);
939
1237
  }
1238
+ if (activePlugin.id === CODEX_OAUTH_PLUGIN_ID) {
1239
+ await codexOAuthService.ensureFreshToken();
1240
+ }
940
1241
  const pluginVars = this.getPluginVariables(activePlugin, userId);
941
1242
  const { apiMode, endpoint: effectiveEndpoint } = resolvePluginApiConfig(activePlugin, pluginVars);
942
1243
  const processedEndpoint = applyModelEndpointTemplate(effectiveEndpoint, model);
@@ -948,7 +1249,7 @@ export class PluginService {
948
1249
  const providerStateScope = apiMode === 'responses'
949
1250
  ? createOpenAIResponsesStateScope(activePlugin.id, model, processedEndpoint, createPluginCredentialFingerprint(apiKey))
950
1251
  : undefined;
951
- const headers = buildPluginAuthHeaders(activePlugin, apiKey);
1252
+ const headers = buildPluginAuthHeaders(activePlugin, apiKey, processedEndpoint);
952
1253
  let payload;
953
1254
  if (activePlugin.id === 'anthropic' || apiMode === 'responses') {
954
1255
  const pluginRequest = buildPluginChatPayload(activePlugin, model, messages, options, pluginVars, true, apiMode, providerStateScope);
@@ -966,77 +1267,128 @@ export class PluginService {
966
1267
  stream: true,
967
1268
  };
968
1269
  }
969
- const response = await fetch(processedEndpoint, {
970
- method: 'POST',
971
- headers,
972
- body: JSON.stringify(payload),
973
- redirect: 'error',
974
- });
975
- if (activePlugin.id === 'anthropic') {
976
- yield* streamAnthropicResponse(response);
977
- }
978
- else if (apiMode === 'responses') {
979
- const contentType = response.headers.get('content-type') || '';
980
- if (!contentType.includes('text/event-stream')) {
981
- if (!response.ok) {
982
- const errorText = await response.text();
983
- throw new Error(`Plugin API error: ${response.status} - ${errorText.slice(0, 200)}`);
984
- }
985
- const normalized = convertProviderResponse(activePlugin, (await response.json()), model, apiMode, providerStateScope);
986
- const choice = normalized.choices[0];
987
- const message = choice?.message;
988
- if (typeof message?.content === 'string' && message.content) {
989
- yield { type: 'content', content: message.content };
990
- }
991
- if (typeof message?.reasoning_content === 'string' &&
992
- message.reasoning_content) {
993
- yield {
994
- type: 'reasoning',
995
- content: message.reasoning_content,
996
- };
997
- }
998
- for (const call of message?.tool_calls || []) {
1270
+ const startedAt = Date.now();
1271
+ let status = 'cancelled';
1272
+ let tokenUsage;
1273
+ const captureUsage = (chunk) => {
1274
+ if (chunk.type !== 'usage' || !chunk.usage)
1275
+ return;
1276
+ const { promptTokens, completionTokens, totalTokens } = chunk.usage;
1277
+ if (promptTokens === undefined &&
1278
+ completionTokens === undefined &&
1279
+ totalTokens === undefined) {
1280
+ return;
1281
+ }
1282
+ const prompt = promptTokens ?? 0;
1283
+ const completion = completionTokens ?? 0;
1284
+ tokenUsage = {
1285
+ promptTokens: prompt,
1286
+ completionTokens: completion,
1287
+ totalTokens: totalTokens ?? prompt + completion,
1288
+ };
1289
+ };
1290
+ const forward = async function* (chunks) {
1291
+ for await (const chunk of chunks) {
1292
+ captureUsage(chunk);
1293
+ yield chunk;
1294
+ }
1295
+ };
1296
+ try {
1297
+ const response = await fetch(processedEndpoint, {
1298
+ method: 'POST',
1299
+ headers,
1300
+ body: JSON.stringify(payload),
1301
+ redirect: 'error',
1302
+ });
1303
+ if (activePlugin.id === 'anthropic') {
1304
+ yield* forward(streamAnthropicResponse(response));
1305
+ }
1306
+ else if (apiMode === 'responses') {
1307
+ const contentType = response.headers.get('content-type') || '';
1308
+ // The codex endpoint streams SSE without any content-type header.
1309
+ const streamedAnyway = activePlugin.id === CODEX_OAUTH_PLUGIN_ID && response.ok;
1310
+ if (!contentType.includes('text/event-stream') && !streamedAnyway) {
1311
+ if (!response.ok) {
1312
+ const errorText = await response.text();
1313
+ throw new Error(`Plugin API error: ${response.status} - ${errorText.slice(0, 200)}`);
1314
+ }
1315
+ const normalized = convertProviderResponse(activePlugin, (await response.json()), model, apiMode, providerStateScope);
1316
+ const choice = normalized.choices[0];
1317
+ const message = choice?.message;
1318
+ if (typeof message?.content === 'string' && message.content) {
1319
+ yield { type: 'content', content: message.content };
1320
+ }
1321
+ if (typeof message?.reasoning_content === 'string' &&
1322
+ message.reasoning_content) {
1323
+ yield {
1324
+ type: 'reasoning',
1325
+ content: message.reasoning_content,
1326
+ };
1327
+ }
1328
+ for (const call of message?.tool_calls || []) {
1329
+ yield {
1330
+ type: 'tool_call',
1331
+ toolCall: {
1332
+ id: call.id,
1333
+ name: call.function.name,
1334
+ arguments: call.function.arguments,
1335
+ ...(call.providerMetadata
1336
+ ? { providerMetadata: call.providerMetadata }
1337
+ : {}),
1338
+ },
1339
+ };
1340
+ }
1341
+ if (normalized.usage) {
1342
+ const usageChunk = {
1343
+ type: 'usage',
1344
+ usage: {
1345
+ promptTokens: normalized.usage.prompt_tokens,
1346
+ completionTokens: normalized.usage.completion_tokens,
1347
+ totalTokens: normalized.usage.total_tokens,
1348
+ },
1349
+ };
1350
+ captureUsage(usageChunk);
1351
+ yield usageChunk;
1352
+ }
1353
+ const incompleteReason = typeof normalized.providerMetadata?.[OPENAI_RESPONSES_INCOMPLETE_REASON_METADATA_KEY] === 'string'
1354
+ ? normalized.providerMetadata[OPENAI_RESPONSES_INCOMPLETE_REASON_METADATA_KEY]
1355
+ : undefined;
999
1356
  yield {
1000
- type: 'tool_call',
1001
- toolCall: {
1002
- id: call.id,
1003
- name: call.function.name,
1004
- arguments: call.function.arguments,
1005
- ...(call.providerMetadata
1006
- ? { providerMetadata: call.providerMetadata }
1007
- : {}),
1008
- },
1357
+ type: 'done',
1358
+ ...(incompleteReason
1359
+ ? { doneReason: `incomplete:${incompleteReason}` }
1360
+ : {}),
1361
+ ...(normalized.providerMetadata
1362
+ ? { providerMetadata: normalized.providerMetadata }
1363
+ : {}),
1009
1364
  };
1010
1365
  }
1011
- if (normalized.usage) {
1012
- yield {
1013
- type: 'usage',
1014
- usage: {
1015
- promptTokens: normalized.usage.prompt_tokens,
1016
- completionTokens: normalized.usage.completion_tokens,
1017
- totalTokens: normalized.usage.total_tokens,
1018
- },
1019
- };
1366
+ else {
1367
+ yield* forward(streamOpenAIResponsesResponse(response, providerStateScope, {
1368
+ allowEmptyTerminalOutput: activePlugin.id === CODEX_OAUTH_PLUGIN_ID,
1369
+ }));
1020
1370
  }
1021
- const incompleteReason = typeof normalized.providerMetadata?.[OPENAI_RESPONSES_INCOMPLETE_REASON_METADATA_KEY] === 'string'
1022
- ? normalized.providerMetadata[OPENAI_RESPONSES_INCOMPLETE_REASON_METADATA_KEY]
1023
- : undefined;
1024
- yield {
1025
- type: 'done',
1026
- ...(incompleteReason
1027
- ? { doneReason: `incomplete:${incompleteReason}` }
1028
- : {}),
1029
- ...(normalized.providerMetadata
1030
- ? { providerMetadata: normalized.providerMetadata }
1031
- : {}),
1032
- };
1033
1371
  }
1034
1372
  else {
1035
- yield* streamOpenAIResponsesResponse(response, providerStateScope);
1373
+ yield* forward(streamOpenAICompatibleResponse(response));
1036
1374
  }
1375
+ status = 'success';
1037
1376
  }
1038
- else {
1039
- yield* streamOpenAICompatibleResponse(response);
1377
+ catch (error) {
1378
+ status = 'error';
1379
+ throw error;
1380
+ }
1381
+ finally {
1382
+ pluginUsageService.record({
1383
+ userId,
1384
+ pluginId: activePlugin.id,
1385
+ pluginName: activePlugin.name,
1386
+ capability: 'chat',
1387
+ model,
1388
+ status,
1389
+ durationMs: Date.now() - startedAt,
1390
+ tokens: tokenUsage,
1391
+ });
1040
1392
  }
1041
1393
  }
1042
1394
  // Validate plugin structure
@@ -1152,6 +1504,24 @@ export class PluginService {
1152
1504
  getPluginsByCapability(capabilityType, userId) {
1153
1505
  return this.capabilityRegistryService.getPluginsByCapability(capabilityType, userId);
1154
1506
  }
1507
+ getAvailableAudioGenModels(userId) {
1508
+ return this.audioGenerationService.getAvailableModels(userId);
1509
+ }
1510
+ executeAudioGenRequest(model, prompt, options) {
1511
+ return this.audioGenerationService.generate(model, prompt, options);
1512
+ }
1513
+ getAvailableVideoGenModels(userId) {
1514
+ return this.videoGenerationService.getAvailableModels(userId);
1515
+ }
1516
+ submitVideoGenRequest(model, prompt, options) {
1517
+ return this.videoGenerationService.submit(model, prompt, options);
1518
+ }
1519
+ pollVideoGenRequest(model, providerJobId, pluginId, userId) {
1520
+ return this.videoGenerationService.poll(model, providerJobId, pluginId, userId);
1521
+ }
1522
+ downloadVideoGenResult(model, providerJobId, pluginId, userId) {
1523
+ return this.videoGenerationService.download(model, providerJobId, pluginId, userId);
1524
+ }
1155
1525
  }
1156
1526
  export default new PluginService();
1157
1527
  //# sourceMappingURL=pluginService.js.map