libre-webui 0.24.1 → 0.25.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 (191) hide show
  1. package/README.md +26 -15
  2. package/backend/dist/index.d.ts.map +1 -1
  3. package/backend/dist/index.js +9 -0
  4. package/backend/dist/index.js.map +1 -1
  5. package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
  6. package/backend/dist/persistence/postgresMigrationRegistry.js +2 -0
  7. package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
  8. package/backend/dist/persistence/postgresPersonalAutomationsMigration.d.ts +8 -0
  9. package/backend/dist/persistence/postgresPersonalAutomationsMigration.d.ts.map +1 -0
  10. package/backend/dist/persistence/postgresPersonalAutomationsMigration.js +98 -0
  11. package/backend/dist/persistence/postgresPersonalAutomationsMigration.js.map +1 -0
  12. package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
  13. package/backend/dist/persistence/postgresResourceRepositories.js +274 -0
  14. package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
  15. package/backend/dist/persistence/resourceTypes.d.ts +83 -2
  16. package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
  17. package/backend/dist/persistence/resourceTypes.js.map +1 -1
  18. package/backend/dist/persistence/sqliteMigrations.d.ts +1 -0
  19. package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
  20. package/backend/dist/persistence/sqliteMigrations.js +181 -0
  21. package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
  22. package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
  23. package/backend/dist/persistence/sqliteResourceRepositories.js +233 -0
  24. package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
  25. package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
  26. package/backend/dist/persistence/sqliteToPostgresMigration.js +41 -0
  27. package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
  28. package/backend/dist/platform/jobs/domainJobContracts.d.ts +3 -0
  29. package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
  30. package/backend/dist/platform/jobs/domainJobContracts.js +3 -0
  31. package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
  32. package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
  33. package/backend/dist/platform/jobs/domainJobHandlers.js +101 -1
  34. package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
  35. package/backend/dist/routes/automations.d.ts +3 -0
  36. package/backend/dist/routes/automations.d.ts.map +1 -0
  37. package/backend/dist/routes/automations.js +232 -0
  38. package/backend/dist/routes/automations.js.map +1 -0
  39. package/backend/dist/routes/calendar.d.ts +3 -0
  40. package/backend/dist/routes/calendar.d.ts.map +1 -0
  41. package/backend/dist/routes/calendar.js +206 -0
  42. package/backend/dist/routes/calendar.js.map +1 -0
  43. package/backend/dist/routes/chat.d.ts.map +1 -1
  44. package/backend/dist/routes/chat.js +42 -3
  45. package/backend/dist/routes/chat.js.map +1 -1
  46. package/backend/dist/routes/ollama.d.ts.map +1 -1
  47. package/backend/dist/routes/ollama.js +4 -1
  48. package/backend/dist/routes/ollama.js.map +1 -1
  49. package/backend/dist/routes/search.d.ts.map +1 -1
  50. package/backend/dist/routes/search.js +5 -3
  51. package/backend/dist/routes/search.js.map +1 -1
  52. package/backend/dist/services/automationSchedulerService.d.ts +28 -0
  53. package/backend/dist/services/automationSchedulerService.d.ts.map +1 -0
  54. package/backend/dist/services/automationSchedulerService.js +191 -0
  55. package/backend/dist/services/automationSchedulerService.js.map +1 -0
  56. package/backend/dist/services/automationService.d.ts +53 -0
  57. package/backend/dist/services/automationService.d.ts.map +1 -0
  58. package/backend/dist/services/automationService.js +224 -0
  59. package/backend/dist/services/automationService.js.map +1 -0
  60. package/backend/dist/services/chatGenerationService.d.ts.map +1 -1
  61. package/backend/dist/services/chatGenerationService.js +7 -1
  62. package/backend/dist/services/chatGenerationService.js.map +1 -1
  63. package/backend/dist/services/chatService.d.ts +1 -0
  64. package/backend/dist/services/chatService.d.ts.map +1 -1
  65. package/backend/dist/services/chatService.js +24 -0
  66. package/backend/dist/services/chatService.js.map +1 -1
  67. package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -1
  68. package/backend/dist/services/durableChatGenerationService.js +8 -2
  69. package/backend/dist/services/durableChatGenerationService.js.map +1 -1
  70. package/backend/dist/services/ollamaService.d.ts.map +1 -1
  71. package/backend/dist/services/ollamaService.js +3 -1
  72. package/backend/dist/services/ollamaService.js.map +1 -1
  73. package/backend/dist/services/titleGenerationService.d.ts.map +1 -1
  74. package/backend/dist/services/titleGenerationService.js +5 -1
  75. package/backend/dist/services/titleGenerationService.js.map +1 -1
  76. package/backend/dist/services/webSearchPlanService.d.ts +18 -0
  77. package/backend/dist/services/webSearchPlanService.d.ts.map +1 -0
  78. package/backend/dist/services/webSearchPlanService.js +160 -0
  79. package/backend/dist/services/webSearchPlanService.js.map +1 -0
  80. package/backend/dist/services/webSearchService.d.ts +9 -3
  81. package/backend/dist/services/webSearchService.d.ts.map +1 -1
  82. package/backend/dist/services/webSearchService.js +8 -2
  83. package/backend/dist/services/webSearchService.js.map +1 -1
  84. package/backend/dist/storage.d.ts +7 -1
  85. package/backend/dist/storage.d.ts.map +1 -1
  86. package/backend/dist/storage.js +65 -1
  87. package/backend/dist/storage.js.map +1 -1
  88. package/backend/dist/types/index.d.ts +72 -0
  89. package/backend/dist/types/index.d.ts.map +1 -1
  90. package/backend/dist/types/index.js.map +1 -1
  91. package/backend/dist/utils/automationSchedule.d.ts +26 -0
  92. package/backend/dist/utils/automationSchedule.d.ts.map +1 -0
  93. package/backend/dist/utils/automationSchedule.js +231 -0
  94. package/backend/dist/utils/automationSchedule.js.map +1 -0
  95. package/backend/dist/utils/resourceLimits.d.ts +7 -0
  96. package/backend/dist/utils/resourceLimits.d.ts.map +1 -1
  97. package/backend/dist/utils/resourceLimits.js +7 -0
  98. package/backend/dist/utils/resourceLimits.js.map +1 -1
  99. package/backend/dist/websocketServer.d.ts.map +1 -1
  100. package/backend/dist/websocketServer.js +8 -2
  101. package/backend/dist/websocketServer.js.map +1 -1
  102. package/backend/package.json +1 -1
  103. package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
  104. package/frontend/dist/assets/index-6o-Hc2wS.js +41 -0
  105. package/frontend/dist/assets/index-D-pM957b.css +1 -0
  106. package/frontend/dist/index.html +12 -12
  107. package/frontend/dist/js/{ArtifactContainer-B-hs0F5K.js → ArtifactContainer-CpUi6ekF.js} +1 -1
  108. package/frontend/dist/js/{ArtifactDemoPage-QFtJw-u4.js → ArtifactDemoPage-BeNs3gHp.js} +1 -1
  109. package/frontend/dist/js/{ArtifactSandboxFrame-S9g_q47-.js → ArtifactSandboxFrame-qsj_Y3LV.js} +2 -2
  110. package/frontend/dist/js/ArtifactSlideOutPanel-CSFRluWA.js +1 -0
  111. package/frontend/dist/js/AutomationsPage-KUeWoZNv.js +1 -0
  112. package/frontend/dist/js/CalendarPage-D9I972w8.js +1 -0
  113. package/frontend/dist/js/ChatPage-CmBx4oAL.js +16 -0
  114. package/frontend/dist/js/FirstTimeSetup-Df0YJni3.js +1 -0
  115. package/frontend/dist/js/GalleryPage-bLCrdJN_.js +1 -0
  116. package/frontend/dist/js/HomePage-Uc6PEg-U.js +1 -0
  117. package/frontend/dist/js/LibreClawPage-CG63DHg1.js +1 -0
  118. package/frontend/dist/js/{ModelManagerModals-DBmm_slu.js → ModelManagerModals-DcLGYN3l.js} +1 -1
  119. package/frontend/dist/js/ModelSelector-yxJC64rM.js +1 -0
  120. package/frontend/dist/js/ModelsPage-1R_TxgmJ.js +2 -0
  121. package/frontend/dist/js/{NotesPage-C7bPU_KF.js → NotesPage-L65j5HoW.js} +1 -1
  122. package/frontend/dist/js/PersonasPage-CKeBpHIH.js +13 -0
  123. package/frontend/dist/js/PluginUsagePage-BX1SKMbW.js +1 -0
  124. package/frontend/dist/js/{RichMessageContent-D0mK9vRA.js → RichMessageContent-DiySVYGB.js} +1 -1
  125. package/frontend/dist/js/SettingsModal-68SG1uZ2.js +1 -0
  126. package/frontend/dist/js/SystemPage-CytpZiQV.js +1 -0
  127. package/frontend/dist/js/UserManagementPage-BzJBvZiS.js +1 -0
  128. package/frontend/dist/js/WorkPage-C8gr0QJe.js +6 -0
  129. package/frontend/dist/js/api-BDtqBtqw.js +14 -0
  130. package/frontend/dist/js/{appStore-DXlmTjIC.js → appStore-0JnCUlEq.js} +2 -2
  131. package/frontend/dist/js/{ar-DW79oSyN.js → ar-CSpi37Gr.js} +2 -2
  132. package/frontend/dist/js/{authStore-B97lJ5Ee.js → authStore-DbMdvNgJ.js} +3 -3
  133. package/frontend/dist/js/{babel-D24oDI9y.js → babel-BsYoxD-7.js} +1 -1
  134. package/frontend/dist/js/{bn-BEAmE7VH.js → bn-CyH1BeDD.js} +2 -2
  135. package/frontend/dist/js/calendarDates-Co3o_05Y.js +1 -0
  136. package/frontend/dist/js/chatStore-CHQ2uINS.js +3 -0
  137. package/frontend/dist/js/{config-CmEh9o8U.js → config-DSOYQ9a3.js} +1 -1
  138. package/frontend/dist/js/{cs-irpRMzRo.js → cs-Cv2NfRK5.js} +2 -2
  139. package/frontend/dist/js/{da-cgilTVxc.js → da-D6D6IvPK.js} +2 -2
  140. package/frontend/dist/js/{de-BU-eH3gU.js → de-BNoJJTrT.js} +2 -2
  141. package/frontend/dist/js/{es-B2LRpZ0p.js → es-kzo9C7q_.js} +2 -2
  142. package/frontend/dist/js/{estree-CZO8RS52.js → estree-BcnBcOAx.js} +7 -7
  143. package/frontend/dist/js/{fr-Cd5JM-oQ.js → fr-CjHYvJRp.js} +2 -2
  144. package/frontend/dist/js/{hi-CvOhdtiK.js → hi-BU98AdCQ.js} +2 -2
  145. package/frontend/dist/js/{html-C31pxhBG.js → html-DkY55Jys.js} +10 -10
  146. package/frontend/dist/js/{id-BtRK5nR_.js → id-CpdedejX.js} +2 -2
  147. package/frontend/dist/js/{is-CnEqwgRF.js → is-Cp5dDWBH.js} +2 -2
  148. package/frontend/dist/js/{it-CI6WjId6.js → it-CuH_cK4U.js} +2 -2
  149. package/frontend/dist/js/{ja-B6gFOHi6.js → ja-BWXXXiHn.js} +2 -2
  150. package/frontend/dist/js/{ko-DuAhnmMf.js → ko-DQgv2M1t.js} +2 -2
  151. package/frontend/dist/js/{markdown-C1CEqXpL.js → markdown-IZSI9DXw.js} +6 -6
  152. package/frontend/dist/js/{messageCodeStyles-BqQ6kOSU.js → messageCodeStyles-BFNR9Wv6.js} +2 -2
  153. package/frontend/dist/js/{ms-C8BIP7MZ.js → ms-DCcRoIcX.js} +2 -2
  154. package/frontend/dist/js/{nl-DnEdspER.js → nl-CbKR1_EU.js} +2 -2
  155. package/frontend/dist/js/{pl-Ddyyvbse.js → pl-CRyJZccn.js} +2 -2
  156. package/frontend/dist/js/{postcss-Bx9CTcIc.js → postcss-BJ0okz7n.js} +1 -1
  157. package/frontend/dist/js/{pt-BYuzm70F.js → pt-BXYmHFGH.js} +2 -2
  158. package/frontend/dist/js/{router-vendor-CbPDks39.js → router-vendor-DGFGM3EP.js} +1 -1
  159. package/frontend/dist/js/{ru-CmSPFxPX.js → ru-M9SyZG4b.js} +2 -2
  160. package/frontend/dist/js/{standalone-Dz0pkxn0.js → standalone-Csw3jfJ_.js} +5 -5
  161. package/frontend/dist/js/{sv-Dd6SQ9f1.js → sv-DJ486LTU.js} +2 -2
  162. package/frontend/dist/js/{th-CpH8745B.js → th-Ch7gKtZv.js} +2 -2
  163. package/frontend/dist/js/{tr-DuVD75iJ.js → tr-ClehWhSC.js} +2 -2
  164. package/frontend/dist/js/{typescript-DeXPhp90.js → typescript-CnF4UVx7.js} +2 -2
  165. package/frontend/dist/js/{ui-CO0DPqrI.js → ui-CC-K19wO.js} +2 -2
  166. package/frontend/dist/js/ui-vendor-B1UgO3vd.js +177 -0
  167. package/frontend/dist/js/{uk-B0MU-M8k.js → uk-BqAd5zHF.js} +2 -2
  168. package/frontend/dist/js/{vi-BLmFNp_z.js → vi-D9gjlDVB.js} +2 -2
  169. package/frontend/dist/js/{workApi-C4QtuhcE.js → workApi-C3uy9EDT.js} +3 -3
  170. package/frontend/dist/js/{yaml-DioTxVLA.js → yaml-DzT5hBSH.js} +5 -5
  171. package/frontend/dist/js/{zh-BQu0TLhw.js → zh-DKTyHkiR.js} +2 -2
  172. package/package.json +2 -2
  173. package/frontend/dist/assets/index-CDQ12KBq.js +0 -34
  174. package/frontend/dist/assets/index-DkFWzmFD.css +0 -1
  175. package/frontend/dist/js/ArtifactSlideOutPanel-D0UOo8RW.js +0 -1
  176. package/frontend/dist/js/ChatPage-fdd8500b.js +0 -23
  177. package/frontend/dist/js/FirstTimeSetup-BTjafGOq.js +0 -1
  178. package/frontend/dist/js/GalleryPage-hR9AJ6b-.js +0 -1
  179. package/frontend/dist/js/HomePage-JvTzq-Pp.js +0 -1
  180. package/frontend/dist/js/LibreClawPage-CLJvtXft.js +0 -1
  181. package/frontend/dist/js/ModelSelector-A-viVFEc.js +0 -1
  182. package/frontend/dist/js/ModelsPage-C4Rm-TZF.js +0 -2
  183. package/frontend/dist/js/PersonasPage-DpvxbFzv.js +0 -13
  184. package/frontend/dist/js/PluginUsagePage-laF-M2X9.js +0 -1
  185. package/frontend/dist/js/SettingsModal-DNEQ5sTt.js +0 -1
  186. package/frontend/dist/js/SystemPage-Xr7XJv8c.js +0 -1
  187. package/frontend/dist/js/UserManagementPage-DGmbGTC2.js +0 -1
  188. package/frontend/dist/js/WorkPage-DNKFD9gS.js +0 -6
  189. package/frontend/dist/js/api-DGVZHSkS.js +0 -14
  190. package/frontend/dist/js/chatStore-zilBUd1p.js +0 -3
  191. package/frontend/dist/js/ui-vendor-mqjSfAaS.js +0 -177
@@ -0,0 +1,232 @@
1
+ /*
2
+ * Libre WebUI
3
+ * Copyright (C) 2025 Kroonen AI, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at:
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import express from 'express';
18
+ import automationService from '../services/automationService.js';
19
+ import automationSchedulerService from '../services/automationSchedulerService.js';
20
+ import { authenticate } from '../middleware/auth.js';
21
+ import { ResourcePolicyError } from '../utils/resourceLimits.js';
22
+ import { InvalidTriggerError, occurrencesBetween, } from '../utils/automationSchedule.js';
23
+ const router = express.Router();
24
+ router.use(authenticate);
25
+ const userIdOf = (req) => req.user?.userId || 'default';
26
+ /** Occurrence projections span at most 62 days per request. */
27
+ const MAX_OCCURRENCE_RANGE_MS = 62 * 24 * 60 * 60 * 1000;
28
+ function sendAutomationError(res, error, fallback) {
29
+ if (error instanceof ResourcePolicyError ||
30
+ error instanceof InvalidTriggerError) {
31
+ const statusCode = error instanceof ResourcePolicyError ? error.statusCode : 400;
32
+ res.status(statusCode).json({ success: false, error: error.message });
33
+ return;
34
+ }
35
+ res.status(500).json({
36
+ success: false,
37
+ error: error instanceof Error ? error.message : fallback,
38
+ });
39
+ }
40
+ function readEpoch(value, field) {
41
+ const parsed = typeof value === 'string' ? Number(value) : value;
42
+ if (typeof parsed !== 'number' ||
43
+ !Number.isSafeInteger(parsed) ||
44
+ parsed < 0) {
45
+ throw new ResourcePolicyError(`${field} must be an epoch-ms integer`, 400);
46
+ }
47
+ return parsed;
48
+ }
49
+ function readAutomationBody(body) {
50
+ const provider = typeof body.provider === 'string' && body.provider
51
+ ? body.provider
52
+ : undefined;
53
+ const model = typeof body.model === 'string' && body.model ? body.model : undefined;
54
+ return {
55
+ name: typeof body.name === 'string' ? body.name : '',
56
+ instructions: typeof body.instructions === 'string' ? body.instructions : '',
57
+ triggers: body.triggers,
58
+ ...(provider ? { provider } : {}),
59
+ ...(model ? { model } : {}),
60
+ notify: body.notify === 'off' ? 'off' : 'app',
61
+ };
62
+ }
63
+ router.get('/', async (req, res) => {
64
+ try {
65
+ res.json({
66
+ success: true,
67
+ data: await automationService.getAutomations(userIdOf(req)),
68
+ });
69
+ }
70
+ catch (error) {
71
+ sendAutomationError(res, error, 'Failed to load automations');
72
+ }
73
+ });
74
+ router.post('/', async (req, res) => {
75
+ try {
76
+ const automation = await automationService.createAutomation(readAutomationBody((req.body ?? {})), userIdOf(req));
77
+ res.json({ success: true, data: automation });
78
+ }
79
+ catch (error) {
80
+ sendAutomationError(res, error, 'Failed to create automation');
81
+ }
82
+ });
83
+ /** Upcoming computed occurrences across the caller's active automations. */
84
+ router.get('/occurrences', async (req, res) => {
85
+ try {
86
+ const from = readEpoch(req.query.from, 'from');
87
+ const to = readEpoch(req.query.to, 'to');
88
+ if (to <= from || to - from > MAX_OCCURRENCE_RANGE_MS) {
89
+ throw new ResourcePolicyError('The requested range must be positive and span at most 62 days', 400);
90
+ }
91
+ const automations = await automationService.getAutomations(userIdOf(req));
92
+ const occurrences = automations
93
+ .filter(automation => automation.status === 'active')
94
+ .flatMap(automation => occurrencesBetween(automation.triggers, from, to, 200).map(at => ({
95
+ automationId: automation.id,
96
+ name: automation.name,
97
+ at,
98
+ })))
99
+ .sort((left, right) => left.at - right.at);
100
+ res.json({ success: true, data: occurrences });
101
+ }
102
+ catch (error) {
103
+ sendAutomationError(res, error, 'Failed to project occurrences');
104
+ }
105
+ });
106
+ router.get('/runs', async (req, res) => {
107
+ try {
108
+ const options = {};
109
+ if (typeof req.query.automationId === 'string') {
110
+ options.automationId = req.query.automationId;
111
+ }
112
+ if (req.query.from !== undefined) {
113
+ options.from = readEpoch(req.query.from, 'from');
114
+ }
115
+ if (req.query.to !== undefined) {
116
+ options.to = readEpoch(req.query.to, 'to');
117
+ }
118
+ res.json({
119
+ success: true,
120
+ data: await automationService.listRuns(userIdOf(req), options),
121
+ });
122
+ }
123
+ catch (error) {
124
+ sendAutomationError(res, error, 'Failed to load automation runs');
125
+ }
126
+ });
127
+ router.get('/runs/summary', async (req, res) => {
128
+ try {
129
+ res.json({
130
+ success: true,
131
+ data: await automationService.runsSummary(userIdOf(req)),
132
+ });
133
+ }
134
+ catch (error) {
135
+ sendAutomationError(res, error, 'Failed to load the run summary');
136
+ }
137
+ });
138
+ router.post('/runs/seen', async (req, res) => {
139
+ try {
140
+ const marked = await automationService.markRunsSeen(userIdOf(req));
141
+ res.json({ success: true, data: { marked } });
142
+ }
143
+ catch (error) {
144
+ sendAutomationError(res, error, 'Failed to mark runs as seen');
145
+ }
146
+ });
147
+ router.get('/:automationId', async (req, res) => {
148
+ try {
149
+ const automation = await automationService.getAutomation(req.params.automationId, userIdOf(req));
150
+ if (!automation) {
151
+ res.status(404).json({
152
+ success: false,
153
+ error: 'Automation not found',
154
+ });
155
+ return;
156
+ }
157
+ res.json({ success: true, data: automation });
158
+ }
159
+ catch (error) {
160
+ sendAutomationError(res, error, 'Failed to load the automation');
161
+ }
162
+ });
163
+ router.put('/:automationId', async (req, res) => {
164
+ try {
165
+ const automation = await automationService.updateAutomation(req.params.automationId, readAutomationBody((req.body ?? {})), userIdOf(req));
166
+ if (!automation) {
167
+ res.status(404).json({
168
+ success: false,
169
+ error: 'Automation not found',
170
+ });
171
+ return;
172
+ }
173
+ res.json({ success: true, data: automation });
174
+ }
175
+ catch (error) {
176
+ sendAutomationError(res, error, 'Failed to update the automation');
177
+ }
178
+ });
179
+ router.delete('/:automationId', async (req, res) => {
180
+ try {
181
+ const deleted = await automationService.deleteAutomation(req.params.automationId, userIdOf(req));
182
+ if (!deleted) {
183
+ res.status(404).json({
184
+ success: false,
185
+ error: 'Automation not found',
186
+ });
187
+ return;
188
+ }
189
+ res.json({ success: true, message: 'Automation deleted' });
190
+ }
191
+ catch (error) {
192
+ sendAutomationError(res, error, 'Failed to delete the automation');
193
+ }
194
+ });
195
+ const setStatus = async (req, res, status) => {
196
+ try {
197
+ const automation = await automationService.setAutomationStatus(req.params.automationId, userIdOf(req), status);
198
+ if (!automation) {
199
+ res.status(404).json({
200
+ success: false,
201
+ error: 'Automation not found',
202
+ });
203
+ return;
204
+ }
205
+ res.json({ success: true, data: automation });
206
+ }
207
+ catch (error) {
208
+ sendAutomationError(res, error, 'Failed to change the automation status');
209
+ }
210
+ };
211
+ router.post('/:automationId/pause', (req, res) => setStatus(req, res, 'paused'));
212
+ router.post('/:automationId/resume', (req, res) => setStatus(req, res, 'active'));
213
+ router.post('/:automationId/run', async (req, res) => {
214
+ try {
215
+ const userId = userIdOf(req);
216
+ const automation = await automationService.getAutomation(req.params.automationId, userId);
217
+ if (!automation) {
218
+ res.status(404).json({
219
+ success: false,
220
+ error: 'Automation not found',
221
+ });
222
+ return;
223
+ }
224
+ const runId = await automationSchedulerService.runNow(automation.id, userId);
225
+ res.status(202).json({ success: true, data: { runId } });
226
+ }
227
+ catch (error) {
228
+ sendAutomationError(res, error, 'Failed to start the automation run');
229
+ }
230
+ });
231
+ export default router;
232
+ //# sourceMappingURL=automations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automations.js","sourceRoot":"","sources":["../../src/routes/automations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
3
+ //# sourceMappingURL=calendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../src/routes/calendar.ts"],"names":[],"mappings":"AAiCA,QAAA,MAAM,MAAM,4CAAmB,CAAC;eAqPjB,MAAM"}
@@ -0,0 +1,206 @@
1
+ /*
2
+ * Libre WebUI
3
+ * Copyright (C) 2025 Kroonen AI, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at:
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import express from 'express';
18
+ import { v4 as uuidv4 } from 'uuid';
19
+ import storageService from '../storage.js';
20
+ import { authenticate } from '../middleware/auth.js';
21
+ import { MAX_CALENDAR_EVENT_NOTES_LENGTH, MAX_CALENDAR_EVENT_TITLE_LENGTH, ResourcePolicyError, } from '../utils/resourceLimits.js';
22
+ import { InvalidTriggerError, occurrencesBetween, validateTriggers, } from '../utils/automationSchedule.js';
23
+ const router = express.Router();
24
+ router.use(authenticate);
25
+ const userIdOf = (req) => req.user?.userId || 'default';
26
+ /** A calendar range query may span at most 13 months. */
27
+ const MAX_RANGE_MS = 13 * 32 * 24 * 60 * 60 * 1000;
28
+ function sendCalendarError(res, error, fallback) {
29
+ if (error instanceof ResourcePolicyError ||
30
+ error instanceof InvalidTriggerError) {
31
+ const statusCode = error instanceof ResourcePolicyError ? error.statusCode : 400;
32
+ res.status(statusCode).json({ success: false, error: error.message });
33
+ return;
34
+ }
35
+ res.status(500).json({
36
+ success: false,
37
+ error: error instanceof Error ? error.message : fallback,
38
+ });
39
+ }
40
+ function readTextField(value, field, maximum, required) {
41
+ if (value === undefined || value === null || value === '') {
42
+ if (required) {
43
+ throw new ResourcePolicyError(`${field} is required`, 400);
44
+ }
45
+ return undefined;
46
+ }
47
+ if (typeof value !== 'string') {
48
+ throw new ResourcePolicyError(`${field} must be a string`, 400);
49
+ }
50
+ if (value.length > maximum) {
51
+ throw new ResourcePolicyError(`${field} exceeds the maximum length of ${maximum} characters`, 400);
52
+ }
53
+ return value;
54
+ }
55
+ function readEpoch(value, field) {
56
+ const parsed = typeof value === 'string' ? Number(value) : value;
57
+ if (typeof parsed !== 'number' ||
58
+ !Number.isSafeInteger(parsed) ||
59
+ parsed < 0) {
60
+ throw new ResourcePolicyError(`${field} must be an epoch-ms integer`, 400);
61
+ }
62
+ return parsed;
63
+ }
64
+ function readRange(req) {
65
+ const from = readEpoch(req.query.from, 'from');
66
+ const to = readEpoch(req.query.to, 'to');
67
+ if (to <= from || to - from > MAX_RANGE_MS) {
68
+ throw new ResourcePolicyError('The requested range must be positive and span at most 13 months', 400);
69
+ }
70
+ return { from, to };
71
+ }
72
+ function readEventBody(body, existing) {
73
+ const title = readTextField(body.title, 'title', MAX_CALENDAR_EVENT_TITLE_LENGTH, existing === undefined) ??
74
+ existing?.title ??
75
+ '';
76
+ const notes = body.notes !== undefined
77
+ ? readTextField(body.notes, 'notes', MAX_CALENDAR_EVENT_NOTES_LENGTH, false)
78
+ : existing?.notes;
79
+ const startAt = body.startAt !== undefined
80
+ ? readEpoch(body.startAt, 'startAt')
81
+ : existing?.startAt;
82
+ if (startAt === undefined) {
83
+ throw new ResourcePolicyError('startAt is required', 400);
84
+ }
85
+ const endAt = body.endAt !== undefined && body.endAt !== null
86
+ ? readEpoch(body.endAt, 'endAt')
87
+ : body.endAt === null
88
+ ? undefined
89
+ : existing?.endAt;
90
+ if (endAt !== undefined && endAt < startAt) {
91
+ throw new ResourcePolicyError('endAt must not precede startAt', 400);
92
+ }
93
+ const allDay = body.allDay !== undefined
94
+ ? body.allDay === true
95
+ : (existing?.allDay ?? false);
96
+ const recurrence = body.recurrence !== undefined
97
+ ? body.recurrence === null
98
+ ? undefined
99
+ : validateTriggers([body.recurrence], 1)[0]
100
+ : existing?.recurrence;
101
+ return {
102
+ title,
103
+ ...(notes !== undefined ? { notes } : {}),
104
+ startAt,
105
+ ...(endAt !== undefined ? { endAt } : {}),
106
+ allDay,
107
+ ...(recurrence !== undefined ? { recurrence } : {}),
108
+ };
109
+ }
110
+ /** Project a recurring event's occurrences into [from, to). */
111
+ function expandRecurring(event, from, to) {
112
+ if (!event.recurrence)
113
+ return [];
114
+ const duration = event.endAt !== undefined ? event.endAt - event.startAt : 0;
115
+ const effectiveFrom = Math.max(from, event.startAt);
116
+ if (effectiveFrom >= to)
117
+ return [];
118
+ return occurrencesBetween([event.recurrence], effectiveFrom, to)
119
+ .filter(occurrence => occurrence !== event.startAt)
120
+ .map(occurrence => ({
121
+ ...event,
122
+ id: `${event.id}:${occurrence}`,
123
+ baseEventId: event.id,
124
+ startAt: occurrence,
125
+ ...(event.endAt !== undefined ? { endAt: occurrence + duration } : {}),
126
+ }));
127
+ }
128
+ router.get('/events', async (req, res) => {
129
+ try {
130
+ const userId = userIdOf(req);
131
+ const { from, to } = readRange(req);
132
+ const [inRange, recurring] = await Promise.all([
133
+ storageService.getCalendarEventsBetween(from, to, userId),
134
+ storageService.getRecurringCalendarEvents(userId),
135
+ ]);
136
+ const seen = new Set(inRange.map(event => event.id));
137
+ const expanded = recurring.flatMap(event => expandRecurring(event, from, to).filter(occurrence => !seen.has(occurrence.id)));
138
+ const events = [...inRange, ...expanded].sort((left, right) => left.startAt - right.startAt);
139
+ res.json({ success: true, data: events });
140
+ }
141
+ catch (error) {
142
+ sendCalendarError(res, error, 'Failed to load calendar events');
143
+ }
144
+ });
145
+ router.post('/events', async (req, res) => {
146
+ try {
147
+ const now = Date.now();
148
+ const event = {
149
+ id: uuidv4(),
150
+ ...readEventBody((req.body ?? {})),
151
+ createdAt: now,
152
+ updatedAt: now,
153
+ };
154
+ await storageService.saveCalendarEvent(event, userIdOf(req));
155
+ res.json({ success: true, data: event });
156
+ }
157
+ catch (error) {
158
+ sendCalendarError(res, error, 'Failed to create calendar event');
159
+ }
160
+ });
161
+ router.put('/events/:eventId', async (req, res) => {
162
+ try {
163
+ const userId = userIdOf(req);
164
+ const existing = await storageService.getCalendarEvent(req.params.eventId, userId);
165
+ if (!existing) {
166
+ res.status(404).json({
167
+ success: false,
168
+ error: 'Calendar event not found',
169
+ });
170
+ return;
171
+ }
172
+ const event = {
173
+ ...existing,
174
+ ...readEventBody((req.body ?? {}), existing),
175
+ id: existing.id,
176
+ createdAt: existing.createdAt,
177
+ updatedAt: Date.now(),
178
+ };
179
+ await storageService.saveCalendarEvent(event, userId);
180
+ res.json({ success: true, data: event });
181
+ }
182
+ catch (error) {
183
+ sendCalendarError(res, error, 'Failed to update calendar event');
184
+ }
185
+ });
186
+ router.delete('/events/:eventId', async (req, res) => {
187
+ try {
188
+ const deleted = await storageService.deleteCalendarEvent(req.params.eventId, userIdOf(req));
189
+ if (!deleted) {
190
+ res.status(404).json({
191
+ success: false,
192
+ error: 'Calendar event not found',
193
+ });
194
+ return;
195
+ }
196
+ res.json({
197
+ success: true,
198
+ message: 'Calendar event deleted',
199
+ });
200
+ }
201
+ catch (error) {
202
+ sendCalendarError(res, error, 'Failed to delete calendar event');
203
+ }
204
+ });
205
+ export default router;
206
+ //# sourceMappingURL=calendar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.js","sourceRoot":"","sources":["../../src/routes/calendar.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"AA8IA,QAAA,MAAM,MAAM,4CAAmB,CAAC;eAoxDjB,MAAM"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"AA6IA,QAAA,MAAM,MAAM,4CAAmB,CAAC;eA+zDjB,MAAM"}
@@ -26,7 +26,8 @@ import { authenticate, requireAdmin, } from '../middleware/auth.js';
26
26
  import { getCompactionConfig, setCompactionConfig, } from '../services/contextCompactionService.js';
27
27
  import { extractStatistics } from '../utils/generationUtils.js';
28
28
  import agentCliService from '../services/agentCliService.js';
29
- import { buildWebSearchEnhancedContent, isWebSearchAvailable, userCanUseWebSearch, webSearch as runWebSearch, } from '../services/webSearchService.js';
29
+ import { buildWebSearchEnhancedContent, isWebSearchAvailable, userCanUseWebSearch, } from '../services/webSearchService.js';
30
+ import { runPlannedWebSearch } from '../services/webSearchPlanService.js';
30
31
  import { userModel } from '../models/userModel.js';
31
32
  import chatGenerationService from '../services/chatGenerationService.js';
32
33
  import preferencesService from '../services/preferencesService.js';
@@ -268,6 +269,34 @@ router.put('/sessions/:sessionId/messages/:messageId', async (req, res) => {
268
269
  });
269
270
  }
270
271
  });
272
+ // Truncate a session at a message: drop it and everything after it
273
+ // (edit-and-resend). The metadata PUT deliberately ignores `messages`,
274
+ // so this is the only way a client can shorten the server-side history.
275
+ router.post('/sessions/:sessionId/messages/:messageId/truncate', async (req, res) => {
276
+ try {
277
+ const sessionId = req.params.sessionId;
278
+ const messageId = req.params.messageId;
279
+ const userId = req.user?.userId || 'default';
280
+ const updatedSession = await chatService.truncateMessagesFrom(sessionId, messageId, userId);
281
+ if (!updatedSession) {
282
+ res.status(404).json({
283
+ success: false,
284
+ error: 'Session or message not found',
285
+ });
286
+ return;
287
+ }
288
+ res.json({
289
+ success: true,
290
+ data: updatedSession,
291
+ });
292
+ }
293
+ catch (error) {
294
+ res.status(500).json({
295
+ success: false,
296
+ error: getErrorMessage(error, 'Failed to truncate session'),
297
+ });
298
+ }
299
+ });
271
300
  // Delete a chat session
272
301
  router.delete('/sessions/:sessionId', async (req, res) => {
273
302
  try {
@@ -410,7 +439,12 @@ router.post('/sessions/:sessionId/generate', async (req, res) => {
410
439
  (await isWebSearchAvailable()) &&
411
440
  (await userCanUseWebSearch(await userModel.getUserById(userId)))) {
412
441
  try {
413
- const results = await runWebSearch(message, undefined, signal);
442
+ const { results } = await runPlannedWebSearch({
443
+ message,
444
+ session,
445
+ userId,
446
+ signal,
447
+ });
414
448
  if (results.length > 0) {
415
449
  enhancedContent = buildWebSearchEnhancedContent(enhancedContent, results, message);
416
450
  hasRelevantContext = true;
@@ -857,7 +891,12 @@ router.post('/sessions/:sessionId/generate/stream', async (req, res) => {
857
891
  (await userCanUseWebSearch(await userModel.getUserById(userId)))) {
858
892
  await emitDurable({ type: 'search', status: 'searching' });
859
893
  try {
860
- const results = await runWebSearch(message, undefined, signal);
894
+ const { results } = await runPlannedWebSearch({
895
+ message,
896
+ session,
897
+ userId,
898
+ signal,
899
+ });
861
900
  if (results.length > 0) {
862
901
  searchEnhancedContent = buildWebSearchEnhancedContent(documentContext.enhancedContent, results, message);
863
902
  webSearchSources = results.map(({ title, url }) => ({