mulmoclaude 0.1.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 (408) hide show
  1. package/README.md +44 -0
  2. package/bin/mulmoclaude.js +202 -0
  3. package/bin/prepare-dist.js +93 -0
  4. package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
  5. package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
  6. package/client/assets/index-D8rhwXLq.js +4906 -0
  7. package/client/assets/index-KNLBjwuh.css +1 -0
  8. package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
  9. package/client/assets/material-icons-Dr0goTwe.woff +0 -0
  10. package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
  11. package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
  12. package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
  13. package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
  14. package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
  15. package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
  16. package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
  17. package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
  18. package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
  19. package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
  20. package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
  21. package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
  22. package/client/index.html +28 -0
  23. package/package.json +66 -0
  24. package/server/agent/attachmentConverter.ts +270 -0
  25. package/server/agent/config.ts +414 -0
  26. package/server/agent/index.ts +260 -0
  27. package/server/agent/mcp-server.ts +412 -0
  28. package/server/agent/mcp-tools/index.ts +63 -0
  29. package/server/agent/mcp-tools/x.ts +188 -0
  30. package/server/agent/plugin-names.ts +75 -0
  31. package/server/agent/prompt.ts +349 -0
  32. package/server/agent/resumeFailover.ts +129 -0
  33. package/server/agent/sandboxMounts.ts +329 -0
  34. package/server/agent/stream.ts +194 -0
  35. package/server/api/auth/bearerAuth.ts +61 -0
  36. package/server/api/auth/token.ts +98 -0
  37. package/server/api/csrfGuard.ts +85 -0
  38. package/server/api/routes/agent.ts +478 -0
  39. package/server/api/routes/chart.ts +98 -0
  40. package/server/api/routes/chat-index.ts +46 -0
  41. package/server/api/routes/config.ts +258 -0
  42. package/server/api/routes/dispatchResponse.ts +79 -0
  43. package/server/api/routes/files.ts +812 -0
  44. package/server/api/routes/html.ts +101 -0
  45. package/server/api/routes/image.ts +169 -0
  46. package/server/api/routes/mulmo-script.ts +712 -0
  47. package/server/api/routes/mulmoScriptValidate.ts +101 -0
  48. package/server/api/routes/notifications.ts +69 -0
  49. package/server/api/routes/pdf.ts +163 -0
  50. package/server/api/routes/plugins.ts +276 -0
  51. package/server/api/routes/presentHtml.ts +48 -0
  52. package/server/api/routes/roles.ts +125 -0
  53. package/server/api/routes/scheduler.ts +153 -0
  54. package/server/api/routes/schedulerHandlers.ts +151 -0
  55. package/server/api/routes/schedulerTasks.ts +163 -0
  56. package/server/api/routes/sessions.ts +294 -0
  57. package/server/api/routes/sessionsCursor.ts +59 -0
  58. package/server/api/routes/skills.ts +195 -0
  59. package/server/api/routes/sources.ts +540 -0
  60. package/server/api/routes/todos.ts +263 -0
  61. package/server/api/routes/todosColumnsHandlers.ts +347 -0
  62. package/server/api/routes/todosHandlers.ts +274 -0
  63. package/server/api/routes/todosItemsHandlers.ts +386 -0
  64. package/server/api/routes/wiki/pageIndex.ts +53 -0
  65. package/server/api/routes/wiki.ts +363 -0
  66. package/server/api/sandboxStatus.ts +64 -0
  67. package/server/events/notifications.ts +160 -0
  68. package/server/events/pub-sub/index.ts +45 -0
  69. package/server/events/relay-client.ts +288 -0
  70. package/server/events/scheduler-adapter.ts +302 -0
  71. package/server/events/session-store/index.ts +492 -0
  72. package/server/events/task-manager/index.ts +181 -0
  73. package/server/index.ts +572 -0
  74. package/server/system/config.ts +243 -0
  75. package/server/system/credentials.ts +220 -0
  76. package/server/system/docker.ts +97 -0
  77. package/server/system/env.ts +109 -0
  78. package/server/system/logger/config.ts +112 -0
  79. package/server/system/logger/formatters.ts +40 -0
  80. package/server/system/logger/index.ts +53 -0
  81. package/server/system/logger/rotation.ts +37 -0
  82. package/server/system/logger/sinks.ts +101 -0
  83. package/server/system/logger/types.ts +29 -0
  84. package/server/utils/date.ts +57 -0
  85. package/server/utils/errors.ts +7 -0
  86. package/server/utils/fetch.ts +27 -0
  87. package/server/utils/files/atomic.ts +125 -0
  88. package/server/utils/files/html-io.ts +20 -0
  89. package/server/utils/files/image-store.ts +66 -0
  90. package/server/utils/files/index.ts +45 -0
  91. package/server/utils/files/journal-io.ts +213 -0
  92. package/server/utils/files/json.ts +69 -0
  93. package/server/utils/files/markdown-store.ts +33 -0
  94. package/server/utils/files/naming.ts +50 -0
  95. package/server/utils/files/reference-dirs-io.ts +45 -0
  96. package/server/utils/files/roles-io.ts +45 -0
  97. package/server/utils/files/safe.ts +106 -0
  98. package/server/utils/files/scheduler-io.ts +20 -0
  99. package/server/utils/files/scheduler-overrides-io.ts +64 -0
  100. package/server/utils/files/session-io.ts +136 -0
  101. package/server/utils/files/spreadsheet-store.ts +63 -0
  102. package/server/utils/files/todos-io.ts +29 -0
  103. package/server/utils/files/user-tasks-io.ts +25 -0
  104. package/server/utils/files/workspace-io.ts +221 -0
  105. package/server/utils/gemini.ts +59 -0
  106. package/server/utils/gitignore.ts +69 -0
  107. package/server/utils/http.ts +15 -0
  108. package/server/utils/httpError.ts +61 -0
  109. package/server/utils/id.ts +16 -0
  110. package/server/utils/json.ts +83 -0
  111. package/server/utils/logBackgroundError.ts +22 -0
  112. package/server/utils/markdown.ts +82 -0
  113. package/server/utils/request.ts +29 -0
  114. package/server/utils/slug.ts +50 -0
  115. package/server/utils/spawn.ts +62 -0
  116. package/server/utils/time.ts +34 -0
  117. package/server/utils/types.ts +47 -0
  118. package/server/workspace/chat-index/index.ts +153 -0
  119. package/server/workspace/chat-index/indexer.ts +209 -0
  120. package/server/workspace/chat-index/paths.ts +34 -0
  121. package/server/workspace/chat-index/summarizer.ts +247 -0
  122. package/server/workspace/chat-index/types.ts +38 -0
  123. package/server/workspace/custom-dirs.ts +220 -0
  124. package/server/workspace/helps/business.md +104 -0
  125. package/server/workspace/helps/github.md +23 -0
  126. package/server/workspace/helps/index.md +60 -0
  127. package/server/workspace/helps/mulmoscript.md +249 -0
  128. package/server/workspace/helps/sandbox.md +90 -0
  129. package/server/workspace/helps/spreadsheet.md +43 -0
  130. package/server/workspace/helps/telegram.md +135 -0
  131. package/server/workspace/helps/wiki.md +131 -0
  132. package/server/workspace/journal/archivist.ts +386 -0
  133. package/server/workspace/journal/dailyPass.ts +743 -0
  134. package/server/workspace/journal/diff.ts +71 -0
  135. package/server/workspace/journal/index.ts +185 -0
  136. package/server/workspace/journal/indexFile.ts +136 -0
  137. package/server/workspace/journal/linkRewrite.ts +4 -0
  138. package/server/workspace/journal/memoryExtractor.ts +130 -0
  139. package/server/workspace/journal/optimizationPass.ts +160 -0
  140. package/server/workspace/journal/paths.ts +76 -0
  141. package/server/workspace/journal/state.ts +125 -0
  142. package/server/workspace/paths.ts +158 -0
  143. package/server/workspace/reference-dirs.ts +252 -0
  144. package/server/workspace/roles.ts +37 -0
  145. package/server/workspace/skills/discovery.ts +125 -0
  146. package/server/workspace/skills/index.ts +10 -0
  147. package/server/workspace/skills/parser.ts +144 -0
  148. package/server/workspace/skills/paths.ts +41 -0
  149. package/server/workspace/skills/scheduler.ts +149 -0
  150. package/server/workspace/skills/types.ts +30 -0
  151. package/server/workspace/skills/user-tasks.ts +257 -0
  152. package/server/workspace/skills/writer.ts +189 -0
  153. package/server/workspace/sources/arxivDiscovery.ts +182 -0
  154. package/server/workspace/sources/classifier.ts +268 -0
  155. package/server/workspace/sources/fetchers/arxiv.ts +170 -0
  156. package/server/workspace/sources/fetchers/github.ts +106 -0
  157. package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
  158. package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
  159. package/server/workspace/sources/fetchers/index.ts +71 -0
  160. package/server/workspace/sources/fetchers/registerAll.ts +15 -0
  161. package/server/workspace/sources/fetchers/rss.ts +141 -0
  162. package/server/workspace/sources/fetchers/rssParser.ts +295 -0
  163. package/server/workspace/sources/httpFetcher.ts +230 -0
  164. package/server/workspace/sources/interests.ts +120 -0
  165. package/server/workspace/sources/paths.ts +110 -0
  166. package/server/workspace/sources/pipeline/dedup.ts +60 -0
  167. package/server/workspace/sources/pipeline/fetch.ts +136 -0
  168. package/server/workspace/sources/pipeline/index.ts +249 -0
  169. package/server/workspace/sources/pipeline/notify.ts +72 -0
  170. package/server/workspace/sources/pipeline/plan.ts +66 -0
  171. package/server/workspace/sources/pipeline/summarize.ts +189 -0
  172. package/server/workspace/sources/pipeline/write.ts +185 -0
  173. package/server/workspace/sources/rateLimiter.ts +148 -0
  174. package/server/workspace/sources/registry.ts +326 -0
  175. package/server/workspace/sources/robots.ts +271 -0
  176. package/server/workspace/sources/sourceState.ts +135 -0
  177. package/server/workspace/sources/taxonomy.ts +74 -0
  178. package/server/workspace/sources/types.ts +144 -0
  179. package/server/workspace/sources/urls.ts +112 -0
  180. package/server/workspace/tool-trace/classify.ts +114 -0
  181. package/server/workspace/tool-trace/index.ts +250 -0
  182. package/server/workspace/tool-trace/writeSearch.ts +98 -0
  183. package/server/workspace/wiki-backlinks/index.ts +107 -0
  184. package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
  185. package/server/workspace/workspace.ts +66 -0
  186. package/src/App.vue +720 -0
  187. package/src/assets/mulmo_bw.png +0 -0
  188. package/src/components/CanvasViewToggle.vue +27 -0
  189. package/src/components/ChatAttachmentPreview.vue +45 -0
  190. package/src/components/ChatImagePreview.vue +17 -0
  191. package/src/components/ChatInput.vue +208 -0
  192. package/src/components/FileContentHeader.vue +49 -0
  193. package/src/components/FileContentRenderer.vue +162 -0
  194. package/src/components/FileTree.vue +115 -0
  195. package/src/components/FileTreePane.vue +85 -0
  196. package/src/components/FilesView.vue +206 -0
  197. package/src/components/LockStatusPopup.vue +111 -0
  198. package/src/components/NotificationBell.vue +131 -0
  199. package/src/components/NotificationToast.vue +72 -0
  200. package/src/components/PluginLauncher.vue +138 -0
  201. package/src/components/RightSidebar.vue +113 -0
  202. package/src/components/RoleSelector.vue +64 -0
  203. package/src/components/SessionHistoryPanel.vue +176 -0
  204. package/src/components/SessionTabBar.vue +81 -0
  205. package/src/components/SettingsMcpTab.vue +350 -0
  206. package/src/components/SettingsModal.vue +275 -0
  207. package/src/components/SettingsReferenceDirsTab.vue +173 -0
  208. package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
  209. package/src/components/SidebarHeader.vue +69 -0
  210. package/src/components/StackView.vue +360 -0
  211. package/src/components/SuggestionsPanel.vue +65 -0
  212. package/src/components/TodoExplorer.vue +358 -0
  213. package/src/components/ToolResultsPanel.vue +77 -0
  214. package/src/components/todo/TodoAddDialog.vue +131 -0
  215. package/src/components/todo/TodoEditDialog.vue +47 -0
  216. package/src/components/todo/TodoEditPanel.vue +113 -0
  217. package/src/components/todo/TodoKanbanView.vue +249 -0
  218. package/src/components/todo/TodoListView.vue +79 -0
  219. package/src/components/todo/TodoTableView.vue +177 -0
  220. package/src/composables/useActiveSession.ts +40 -0
  221. package/src/composables/useAppApi.ts +45 -0
  222. package/src/composables/useCanvasViewMode.ts +121 -0
  223. package/src/composables/useChatScroll.ts +47 -0
  224. package/src/composables/useClickOutside.ts +26 -0
  225. package/src/composables/useClipboardCopy.ts +44 -0
  226. package/src/composables/useContentDisplay.ts +52 -0
  227. package/src/composables/useDebugBeat.ts +23 -0
  228. package/src/composables/useDynamicFavicon.ts +115 -0
  229. package/src/composables/useEventListeners.ts +42 -0
  230. package/src/composables/useExpandedDirs.ts +64 -0
  231. package/src/composables/useFaviconState.ts +30 -0
  232. package/src/composables/useFileSelection.ts +115 -0
  233. package/src/composables/useFileSortMode.ts +24 -0
  234. package/src/composables/useFileTree.ts +85 -0
  235. package/src/composables/useFreshPluginData.ts +89 -0
  236. package/src/composables/useHealth.ts +38 -0
  237. package/src/composables/useImeAwareEnter.ts +57 -0
  238. package/src/composables/useKeyNavigation.ts +60 -0
  239. package/src/composables/useMarkdownLinkHandler.ts +46 -0
  240. package/src/composables/useMarkdownMode.ts +17 -0
  241. package/src/composables/useMcpTools.ts +71 -0
  242. package/src/composables/useMergedSessions.ts +27 -0
  243. package/src/composables/useNotifications.ts +90 -0
  244. package/src/composables/usePdfDownload.ts +60 -0
  245. package/src/composables/usePendingCalls.ts +77 -0
  246. package/src/composables/usePubSub.ts +85 -0
  247. package/src/composables/useRightSidebar.ts +23 -0
  248. package/src/composables/useRoles.ts +34 -0
  249. package/src/composables/useSandboxStatus.ts +67 -0
  250. package/src/composables/useSelectedResult.ts +49 -0
  251. package/src/composables/useSessionDerived.ts +51 -0
  252. package/src/composables/useSessionHistory.ts +81 -0
  253. package/src/composables/useSessionSync.ts +57 -0
  254. package/src/composables/useViewLayout.ts +55 -0
  255. package/src/config/apiRoutes.ts +173 -0
  256. package/src/config/pubsubChannels.ts +45 -0
  257. package/src/config/roles.ts +335 -0
  258. package/src/config/schedulerActions.ts +25 -0
  259. package/src/config/toolNames.ts +71 -0
  260. package/src/config/workspacePaths.ts +24 -0
  261. package/src/index.css +107 -0
  262. package/src/main.ts +25 -0
  263. package/src/plugins/canvas/Preview.vue +13 -0
  264. package/src/plugins/canvas/View.vue +333 -0
  265. package/src/plugins/canvas/definition.ts +38 -0
  266. package/src/plugins/canvas/index.ts +36 -0
  267. package/src/plugins/chart/Preview.vue +49 -0
  268. package/src/plugins/chart/View.vue +143 -0
  269. package/src/plugins/chart/definition.ts +58 -0
  270. package/src/plugins/chart/index.ts +52 -0
  271. package/src/plugins/editImage/Preview.vue +13 -0
  272. package/src/plugins/editImage/View.vue +13 -0
  273. package/src/plugins/editImage/definition.ts +27 -0
  274. package/src/plugins/editImage/index.ts +36 -0
  275. package/src/plugins/generateImage/Preview.vue +13 -0
  276. package/src/plugins/generateImage/View.vue +33 -0
  277. package/src/plugins/generateImage/definition.ts +32 -0
  278. package/src/plugins/generateImage/index.ts +56 -0
  279. package/src/plugins/manageRoles/Preview.vue +49 -0
  280. package/src/plugins/manageRoles/View.vue +525 -0
  281. package/src/plugins/manageRoles/definition.ts +43 -0
  282. package/src/plugins/manageRoles/index.ts +47 -0
  283. package/src/plugins/manageSkills/Preview.vue +21 -0
  284. package/src/plugins/manageSkills/View.vue +321 -0
  285. package/src/plugins/manageSkills/definition.ts +49 -0
  286. package/src/plugins/manageSkills/index.ts +49 -0
  287. package/src/plugins/manageSource/Preview.vue +33 -0
  288. package/src/plugins/manageSource/View.vue +697 -0
  289. package/src/plugins/manageSource/definition.ts +63 -0
  290. package/src/plugins/manageSource/index.ts +66 -0
  291. package/src/plugins/markdown/Preview.vue +77 -0
  292. package/src/plugins/markdown/View.vue +476 -0
  293. package/src/plugins/markdown/definition.ts +50 -0
  294. package/src/plugins/markdown/index.ts +36 -0
  295. package/src/plugins/presentHtml/Preview.vue +25 -0
  296. package/src/plugins/presentHtml/View.vue +52 -0
  297. package/src/plugins/presentHtml/definition.ts +27 -0
  298. package/src/plugins/presentHtml/helpers.ts +72 -0
  299. package/src/plugins/presentHtml/index.ts +41 -0
  300. package/src/plugins/presentMulmoScript/Preview.vue +23 -0
  301. package/src/plugins/presentMulmoScript/View.vue +1166 -0
  302. package/src/plugins/presentMulmoScript/definition.ts +95 -0
  303. package/src/plugins/presentMulmoScript/helpers.ts +162 -0
  304. package/src/plugins/presentMulmoScript/index.ts +40 -0
  305. package/src/plugins/scheduler/Preview.vue +67 -0
  306. package/src/plugins/scheduler/TasksTab.vue +205 -0
  307. package/src/plugins/scheduler/View.vue +565 -0
  308. package/src/plugins/scheduler/definition.ts +57 -0
  309. package/src/plugins/scheduler/index.ts +45 -0
  310. package/src/plugins/scheduler/viewModes.ts +26 -0
  311. package/src/plugins/spreadsheet/Preview.vue +29 -0
  312. package/src/plugins/spreadsheet/View.vue +997 -0
  313. package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
  314. package/src/plugins/spreadsheet/definition.ts +121 -0
  315. package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
  316. package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
  317. package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
  318. package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
  319. package/src/plugins/spreadsheet/engine/engine.ts +176 -0
  320. package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
  321. package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
  322. package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
  323. package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
  324. package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
  325. package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
  326. package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
  327. package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
  328. package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
  329. package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
  330. package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
  331. package/src/plugins/spreadsheet/engine/index.ts +27 -0
  332. package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
  333. package/src/plugins/spreadsheet/engine/parser.ts +143 -0
  334. package/src/plugins/spreadsheet/engine/registry.ts +150 -0
  335. package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
  336. package/src/plugins/spreadsheet/engine/types.ts +64 -0
  337. package/src/plugins/spreadsheet/index.ts +36 -0
  338. package/src/plugins/textResponse/Preview.vue +94 -0
  339. package/src/plugins/textResponse/View.vue +503 -0
  340. package/src/plugins/textResponse/definition.ts +34 -0
  341. package/src/plugins/textResponse/index.ts +27 -0
  342. package/src/plugins/textResponse/plugin.ts +29 -0
  343. package/src/plugins/textResponse/samples.ts +97 -0
  344. package/src/plugins/textResponse/types.ts +11 -0
  345. package/src/plugins/todo/Preview.vue +63 -0
  346. package/src/plugins/todo/View.vue +364 -0
  347. package/src/plugins/todo/composables/useTodos.ts +177 -0
  348. package/src/plugins/todo/definition.ts +45 -0
  349. package/src/plugins/todo/index.ts +61 -0
  350. package/src/plugins/todo/labels.ts +163 -0
  351. package/src/plugins/todo/priority.ts +98 -0
  352. package/src/plugins/todo/viewModes.ts +19 -0
  353. package/src/plugins/ui-image/ImagePreview.vue +23 -0
  354. package/src/plugins/ui-image/ImageView.vue +34 -0
  355. package/src/plugins/ui-image/index.ts +3 -0
  356. package/src/plugins/ui-image/types.ts +4 -0
  357. package/src/plugins/wiki/Preview.vue +65 -0
  358. package/src/plugins/wiki/View.vue +342 -0
  359. package/src/plugins/wiki/definition.ts +25 -0
  360. package/src/plugins/wiki/helpers.ts +59 -0
  361. package/src/plugins/wiki/index.ts +52 -0
  362. package/src/router/guards.ts +61 -0
  363. package/src/router/index.ts +50 -0
  364. package/src/tools/index.ts +52 -0
  365. package/src/tools/types.ts +27 -0
  366. package/src/types/events.ts +16 -0
  367. package/src/types/fileTree.ts +13 -0
  368. package/src/types/notification.ts +67 -0
  369. package/src/types/session.ts +116 -0
  370. package/src/types/sse.ts +90 -0
  371. package/src/types/toolCallHistory.ts +13 -0
  372. package/src/utils/agent/eventDispatch.ts +74 -0
  373. package/src/utils/agent/request.ts +55 -0
  374. package/src/utils/agent/toolCalls.ts +62 -0
  375. package/src/utils/api.ts +218 -0
  376. package/src/utils/canvas/viewMode.ts +46 -0
  377. package/src/utils/dom/authTokenMeta.ts +20 -0
  378. package/src/utils/dom/clickOutside.ts +11 -0
  379. package/src/utils/dom/externalLink.ts +57 -0
  380. package/src/utils/dom/scrollable.ts +24 -0
  381. package/src/utils/errors.ts +11 -0
  382. package/src/utils/files/expandedDirs.ts +25 -0
  383. package/src/utils/files/filename.ts +12 -0
  384. package/src/utils/files/sortChildren.ts +20 -0
  385. package/src/utils/filesPreview/schedulerPreview.ts +38 -0
  386. package/src/utils/filesPreview/todoPreview.ts +40 -0
  387. package/src/utils/format/date.ts +85 -0
  388. package/src/utils/format/frontmatter.ts +80 -0
  389. package/src/utils/format/jsonSyntax.ts +109 -0
  390. package/src/utils/html/previewCsp.ts +65 -0
  391. package/src/utils/image/resolve.ts +8 -0
  392. package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
  393. package/src/utils/markdown/extractFirstH1.ts +39 -0
  394. package/src/utils/notification/dispatch.ts +22 -0
  395. package/src/utils/path/relativeLink.ts +130 -0
  396. package/src/utils/role/icon.ts +20 -0
  397. package/src/utils/role/merge.ts +10 -0
  398. package/src/utils/role/plugins.ts +12 -0
  399. package/src/utils/session/mergeSessions.ts +103 -0
  400. package/src/utils/session/seedRoleDefault.ts +35 -0
  401. package/src/utils/session/sessionEntries.ts +121 -0
  402. package/src/utils/session/sessionFactory.ts +22 -0
  403. package/src/utils/session/sessionHelpers.ts +99 -0
  404. package/src/utils/tools/dedup.ts +17 -0
  405. package/src/utils/tools/mcp.ts +33 -0
  406. package/src/utils/tools/pendingCalls.ts +16 -0
  407. package/src/utils/tools/result.ts +40 -0
  408. package/src/utils/types.ts +44 -0
@@ -0,0 +1,288 @@
1
+ // MulmoBridge Relay WebSocket client.
2
+ //
3
+ // Connects to the Relay (Cloudflare Workers) and forwards incoming
4
+ // platform messages to the chat-service relay function. Handles
5
+ // reconnection with exponential backoff.
6
+ //
7
+ // NOTE: packages/relay/src/client.ts is a parallel implementation
8
+ // for browser/edge environments using the global WebSocket API.
9
+ // This module uses the `ws` npm package for Node.js. If you change
10
+ // reconnection logic or URL handling here, check the other file too.
11
+
12
+ import WebSocket from "ws";
13
+ import type { ChatService } from "@mulmobridge/chat-service";
14
+ import { ONE_SECOND_MS } from "../utils/time.js";
15
+
16
+ type RelayFn = ChatService["relay"];
17
+
18
+ // ── Types ────────────────────────────────────────────────────
19
+
20
+ interface RelayMessage {
21
+ id: string;
22
+ platform: string;
23
+ senderId: string;
24
+ chatId: string;
25
+ text: string;
26
+ receivedAt: string;
27
+ replyToken?: string;
28
+ }
29
+
30
+ interface RelayResponse {
31
+ platform: string;
32
+ chatId: string;
33
+ text: string;
34
+ replyToken?: string;
35
+ }
36
+
37
+ interface Logger {
38
+ info(prefix: string, msg: string, data?: unknown): void;
39
+ warn(prefix: string, msg: string, data?: unknown): void;
40
+ error(prefix: string, msg: string, data?: unknown): void;
41
+ }
42
+
43
+ export interface RelayClientDeps {
44
+ relayUrl: string;
45
+ relayToken: string;
46
+ relay: RelayFn;
47
+ logger: Logger;
48
+ }
49
+
50
+ export interface RelayClientHandle {
51
+ disconnect(): void;
52
+ }
53
+
54
+ // ── Constants ────────────────────────────────────────────────
55
+
56
+ const LOG_PREFIX = "relay-client";
57
+ const TRANSPORT_ID = "relay";
58
+ const MIN_RECONNECT_MS = ONE_SECOND_MS;
59
+ const MAX_RECONNECT_MS = 30 * ONE_SECOND_MS;
60
+ const MAX_RESPONSE_QUEUE = 100;
61
+
62
+ // Close codes that indicate a permanent/terminal error — no point
63
+ // reconnecting until the configuration is fixed.
64
+ const TERMINAL_CLOSE_CODES = new Set([
65
+ 1008, // Policy violation (e.g. auth rejected)
66
+ 4401, // Custom: unauthorized
67
+ 4403, // Custom: forbidden
68
+ ]);
69
+
70
+ // ── Helpers ─────────────────────────────────────────────────
71
+
72
+ function isRelayMessage(value: unknown): value is RelayMessage {
73
+ if (!value || typeof value !== "object") return false;
74
+ const obj = value as Record<string, unknown>;
75
+ return (
76
+ typeof obj.id === "string" &&
77
+ typeof obj.platform === "string" &&
78
+ typeof obj.chatId === "string" &&
79
+ typeof obj.text === "string" &&
80
+ obj.text !== "" &&
81
+ obj.chatId !== ""
82
+ );
83
+ }
84
+
85
+ // ── Factory ─────────────────────────────────────────────────
86
+
87
+ export function connectRelay(deps: RelayClientDeps): RelayClientHandle {
88
+ const { relayUrl, relayToken, relay, logger } = deps;
89
+
90
+ let ws: WebSocket | null = null;
91
+ let reconnectMs = MIN_RECONNECT_MS;
92
+ let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
93
+ let stopped = false;
94
+ const responseQueue: RelayResponse[] = [];
95
+
96
+ function buildUrl(): string {
97
+ const url = new URL(relayUrl);
98
+ url.searchParams.set("token", relayToken);
99
+ return url.toString();
100
+ }
101
+
102
+ function connect(): void {
103
+ if (stopped) return;
104
+
105
+ try {
106
+ ws = new WebSocket(buildUrl());
107
+ } catch (err) {
108
+ logger.error(LOG_PREFIX, "failed to create WebSocket", {
109
+ error: err instanceof Error ? err.message : String(err),
110
+ });
111
+ scheduleReconnect();
112
+ return;
113
+ }
114
+
115
+ ws.on("open", () => {
116
+ logger.info(LOG_PREFIX, "connected", { url: relayUrl });
117
+ reconnectMs = MIN_RECONNECT_MS;
118
+ flushResponseQueue();
119
+ });
120
+
121
+ ws.on("message", (data) => {
122
+ handleMessage(String(data));
123
+ });
124
+
125
+ ws.on("close", (code, reason) => {
126
+ ws = null;
127
+ if (TERMINAL_CLOSE_CODES.has(code)) {
128
+ logger.error(LOG_PREFIX, "terminal close, not reconnecting", {
129
+ code,
130
+ reason: String(reason),
131
+ });
132
+ return;
133
+ }
134
+ logger.info(LOG_PREFIX, "disconnected", {
135
+ code,
136
+ reason: String(reason),
137
+ });
138
+ scheduleReconnect();
139
+ });
140
+
141
+ ws.on("error", (err) => {
142
+ logger.warn(LOG_PREFIX, "connection error", {
143
+ error: err.message,
144
+ });
145
+ // close event will follow, triggering reconnect
146
+ });
147
+ }
148
+
149
+ function scheduleReconnect(): void {
150
+ if (stopped) return;
151
+ logger.info(LOG_PREFIX, "reconnecting", { delayMs: reconnectMs });
152
+ reconnectTimer = setTimeout(() => {
153
+ reconnectTimer = null;
154
+ connect();
155
+ }, reconnectMs);
156
+ reconnectMs = Math.min(reconnectMs * 2, MAX_RECONNECT_MS);
157
+ }
158
+
159
+ async function handleMessage(raw: string): Promise<void> {
160
+ let parsed: unknown;
161
+ try {
162
+ parsed = JSON.parse(raw);
163
+ } catch {
164
+ logger.warn(LOG_PREFIX, "invalid JSON from relay", {
165
+ length: raw.length,
166
+ });
167
+ return;
168
+ }
169
+
170
+ if (!isRelayMessage(parsed)) {
171
+ logger.warn(LOG_PREFIX, "malformed relay message");
172
+ return;
173
+ }
174
+ const msg = parsed;
175
+
176
+ logger.info(LOG_PREFIX, "message received", {
177
+ id: msg.id,
178
+ platform: msg.platform,
179
+ chatId: msg.chatId,
180
+ textLength: msg.text.length,
181
+ });
182
+
183
+ // Double-underscore separator avoids collisions — platform names
184
+ // are from a fixed set (PLATFORMS constant) and none contain "__".
185
+ // Single "-" is unsafe because "google-chat" + "X" collides with
186
+ // "google" + "chat-X".
187
+ const externalChatId = `${msg.platform}__${msg.chatId}`;
188
+
189
+ try {
190
+ const result = await relay({
191
+ transportId: TRANSPORT_ID,
192
+ externalChatId,
193
+ text: msg.text,
194
+ });
195
+
196
+ const replyText = result.kind === "ok" ? result.reply : `Error: ${result.message}`;
197
+
198
+ sendResponse({
199
+ platform: msg.platform,
200
+ chatId: msg.chatId,
201
+ text: replyText,
202
+ replyToken: msg.replyToken,
203
+ });
204
+ } catch (err) {
205
+ logger.error(LOG_PREFIX, "relay processing failed", {
206
+ id: msg.id,
207
+ error: err instanceof Error ? err.message : String(err),
208
+ });
209
+ sendResponse({
210
+ platform: msg.platform,
211
+ chatId: msg.chatId,
212
+ text: "Error: failed to process message",
213
+ replyToken: msg.replyToken,
214
+ });
215
+ }
216
+ }
217
+
218
+ function enqueueResponse(response: RelayResponse): void {
219
+ if (responseQueue.length >= MAX_RESPONSE_QUEUE) {
220
+ logger.error(LOG_PREFIX, "response queue full, dropping oldest", {
221
+ platform: response.platform,
222
+ chatId: response.chatId,
223
+ });
224
+ responseQueue.shift();
225
+ }
226
+ responseQueue.push(response);
227
+ }
228
+
229
+ function trySend(response: RelayResponse): boolean {
230
+ if (!ws || ws.readyState !== WebSocket.OPEN) return false;
231
+ try {
232
+ ws.send(JSON.stringify(response), (err) => {
233
+ if (err && !stopped) {
234
+ logger.warn(LOG_PREFIX, "send failed, requeueing", {
235
+ platform: response.platform,
236
+ chatId: response.chatId,
237
+ error: err.message,
238
+ });
239
+ enqueueResponse(response);
240
+ }
241
+ });
242
+ return true;
243
+ } catch {
244
+ return false;
245
+ }
246
+ }
247
+
248
+ function sendResponse(response: RelayResponse): void {
249
+ if (trySend(response)) return;
250
+ enqueueResponse(response);
251
+ logger.error(LOG_PREFIX, "response queued (not connected)", {
252
+ platform: response.platform,
253
+ chatId: response.chatId,
254
+ queueSize: responseQueue.length,
255
+ });
256
+ }
257
+
258
+ function flushResponseQueue(): void {
259
+ if (responseQueue.length === 0) return;
260
+ logger.info(LOG_PREFIX, "flushing response queue", {
261
+ count: responseQueue.length,
262
+ });
263
+ while (responseQueue.length > 0) {
264
+ if (!ws || ws.readyState !== WebSocket.OPEN) break;
265
+ const response = responseQueue[0]!;
266
+ if (!trySend(response)) break;
267
+ responseQueue.shift();
268
+ }
269
+ }
270
+
271
+ function disconnect(): void {
272
+ stopped = true;
273
+ if (reconnectTimer) {
274
+ clearTimeout(reconnectTimer);
275
+ reconnectTimer = null;
276
+ }
277
+ if (ws) {
278
+ ws.close(1000, "shutdown");
279
+ ws = null;
280
+ }
281
+ logger.info(LOG_PREFIX, "stopped");
282
+ }
283
+
284
+ // Start immediately
285
+ connect();
286
+
287
+ return { disconnect };
288
+ }
@@ -0,0 +1,302 @@
1
+ // Adapter that wires the pure scheduler library (server/utils/scheduler/)
2
+ // to the MulmoClaude runtime. Registers system tasks, runs catch-up on
3
+ // startup, and persists execution state + logs.
4
+ //
5
+ // Deliberately thin — all complex logic lives in the scheduler library.
6
+ // This file's job is I/O binding and MulmoClaude-specific plumbing.
7
+
8
+ import { existsSync } from "fs";
9
+ import { readFile, appendFile, mkdir } from "fs/promises";
10
+ import path from "path";
11
+ import { workspacePath } from "../workspace/workspace.js";
12
+ import { writeFileAtomic } from "../utils/files/atomic.js";
13
+ import { log } from "../system/logger/index.js";
14
+ import { errorMessage } from "../utils/errors.js";
15
+ import { ONE_SECOND_MS } from "../utils/time.js";
16
+ import type { ITaskManager, TaskDefinition } from "./task-manager/index.js";
17
+ import {
18
+ type TaskSchedule,
19
+ type TaskExecutionState,
20
+ type TaskLogEntry,
21
+ type CatchUpTask,
22
+ type TaskTrigger,
23
+ emptyState,
24
+ computeCatchUpPlan,
25
+ nextWindowAfter,
26
+ loadState,
27
+ updateAndSave,
28
+ appendLogEntry,
29
+ queryLog,
30
+ SCHEDULE_TYPES,
31
+ TASK_RESULTS,
32
+ TASK_TRIGGERS,
33
+ MISSED_RUN_POLICIES,
34
+ type StateMap,
35
+ type StateDeps,
36
+ type LogDeps,
37
+ } from "@receptron/task-scheduler";
38
+
39
+ // ── Paths ────────────────────────────────────────────────────────
40
+
41
+ const SCHEDULER_CONFIG_DIR = "config/scheduler";
42
+ const SCHEDULER_DATA_DIR = "data/scheduler/logs";
43
+
44
+ function stateFilePath(root = workspacePath): string {
45
+ return path.join(root, SCHEDULER_CONFIG_DIR, "state.json");
46
+ }
47
+
48
+ function logsDir(root = workspacePath): string {
49
+ return path.join(root, SCHEDULER_DATA_DIR);
50
+ }
51
+
52
+ // ── I/O deps (real filesystem) ───────────────────────────────────
53
+
54
+ const stateDeps: StateDeps = {
55
+ readFile: (p: string) => readFile(p, "utf-8"),
56
+ writeFileAtomic: (p: string, content: string) => writeFileAtomic(p, content),
57
+ exists: existsSync,
58
+ };
59
+
60
+ const logDeps: LogDeps = {
61
+ appendFile: (p: string, content: string) => appendFile(p, content),
62
+ readFile: (p: string) => readFile(p, "utf-8"),
63
+ exists: existsSync,
64
+ ensureDir: (p: string) => mkdir(p, { recursive: true }).then(() => {}),
65
+ };
66
+
67
+ // ── System task registry ─────────────────────────────────────────
68
+
69
+ export interface SystemTaskDef {
70
+ id: string;
71
+ name: string;
72
+ description: string;
73
+ schedule: TaskDefinition["schedule"];
74
+ missedRunPolicy: typeof MISSED_RUN_POLICIES.skip | typeof MISSED_RUN_POLICIES.runOnce | typeof MISSED_RUN_POLICIES.runAll;
75
+ run: () => Promise<void>;
76
+ }
77
+
78
+ // ── Public API ───────────────────────────────────────────────────
79
+
80
+ let stateMap: StateMap = new Map();
81
+ const systemTasks: SystemTaskDef[] = [];
82
+ let taskManagerRef: ITaskManager | null = null;
83
+
84
+ /**
85
+ * Initialize the scheduler adapter. Call once at server startup
86
+ * AFTER the task-manager is created but BEFORE `taskManager.start()`.
87
+ */
88
+ export async function initScheduler(taskManager: ITaskManager, tasks: SystemTaskDef[]): Promise<void> {
89
+ await mkdir(path.dirname(stateFilePath()), { recursive: true });
90
+ await mkdir(logsDir(), { recursive: true });
91
+
92
+ stateMap = await loadState(stateFilePath(), stateDeps);
93
+ systemTasks.length = 0;
94
+ systemTasks.push(...tasks);
95
+ taskManagerRef = taskManager;
96
+
97
+ // Run catch-up
98
+ const catchUpTasks: CatchUpTask[] = tasks.map((t) => ({
99
+ id: t.id,
100
+ name: t.name,
101
+ schedule: toCoreSchedule(t.schedule),
102
+ missedRunPolicy: t.missedRunPolicy,
103
+ enabled: true,
104
+ }));
105
+ const plan = computeCatchUpPlan(catchUpTasks, stateMap, Date.now());
106
+
107
+ for (const skip of plan.skipped) {
108
+ log.info("scheduler", "catch-up skipped", {
109
+ taskId: skip.taskId,
110
+ windows: skip.windowCount,
111
+ });
112
+ await safeUpdateState(skip.taskId, { lastRunAt: skip.lastWindow });
113
+ }
114
+
115
+ if (plan.runs.length > 0) {
116
+ log.info("scheduler", "catch-up enqueued", {
117
+ runs: plan.runs.length,
118
+ });
119
+ for (const run of plan.runs) {
120
+ const task = tasks.find((t) => t.id === run.taskId);
121
+ if (!task) continue;
122
+ await executeAndLog(task, run.context.scheduledFor, TASK_TRIGGERS.catchUp);
123
+ }
124
+ }
125
+
126
+ // Register with task-manager for ongoing ticks
127
+ for (const task of tasks) {
128
+ taskManager.registerTask({
129
+ id: task.id,
130
+ description: task.description,
131
+ schedule: task.schedule,
132
+ run: async () => {
133
+ const windowIso = computeCurrentWindow(task);
134
+ await executeAndLog(task, windowIso, TASK_TRIGGERS.scheduled);
135
+ },
136
+ });
137
+ }
138
+
139
+ log.info("scheduler", "initialized", {
140
+ tasks: tasks.map((t) => t.id),
141
+ stateEntries: stateMap.size,
142
+ });
143
+ }
144
+
145
+ /** Apply a schedule override to a running system task.
146
+ * Updates the in-memory task definition, the task-manager, and
147
+ * recalculates nextScheduledAt in persisted state. */
148
+ export async function applyScheduleOverride(taskId: string, schedule: SystemTaskDef["schedule"]): Promise<boolean> {
149
+ const task = systemTasks.find((t) => t.id === taskId);
150
+ if (!task || !taskManagerRef) return false;
151
+ if (!taskManagerRef.updateSchedule(taskId, schedule)) return false;
152
+ task.schedule = schedule;
153
+
154
+ // Recalculate next window so the UI reflects the new schedule
155
+ const nextScheduledAt = computeNextScheduled(task);
156
+ await safeUpdateState(taskId, { nextScheduledAt });
157
+
158
+ return true;
159
+ }
160
+
161
+ /** Query execution logs — used by API routes. */
162
+ export async function getSchedulerLogs(opts: { since?: string; taskId?: string; limit?: number }): Promise<TaskLogEntry[]> {
163
+ return queryLog(logsDir(), opts, logDeps);
164
+ }
165
+
166
+ /** Get all task states — used by API routes. */
167
+ export function getSchedulerTasks(): Array<{
168
+ id: string;
169
+ name: string;
170
+ description: string;
171
+ schedule: TaskDefinition["schedule"];
172
+ missedRunPolicy: string;
173
+ state: TaskExecutionState;
174
+ }> {
175
+ return systemTasks.map((t) => ({
176
+ id: t.id,
177
+ name: t.name,
178
+ description: t.description,
179
+ schedule: t.schedule,
180
+ missedRunPolicy: t.missedRunPolicy,
181
+ state: stateMap.get(t.id) ?? emptyState(t.id),
182
+ }));
183
+ }
184
+
185
+ // ── Internal ─────────────────────────────────────────────────────
186
+
187
+ async function executeAndLog(task: SystemTaskDef, scheduledFor: string, trigger: TaskTrigger): Promise<void> {
188
+ const startedAt = new Date().toISOString();
189
+ const startMs = Date.now();
190
+ let errMsg: string | null = null;
191
+ try {
192
+ await task.run();
193
+ } catch (err) {
194
+ errMsg = errorMessage(err);
195
+ log.error("scheduler", "task failed", {
196
+ taskId: task.id,
197
+ error: errMsg,
198
+ });
199
+ }
200
+ const durationMs = Date.now() - startMs;
201
+ // Persistence is best-effort — never let disk failures propagate
202
+ // to the tick loop or abort startup catch-up.
203
+ await safePersist(task, scheduledFor, startedAt, durationMs, trigger, errMsg);
204
+ }
205
+
206
+ /** Best-effort persistence — state and log are independent. A failure
207
+ * in one does not block the other, and neither propagates upward. */
208
+ async function safePersist(
209
+ task: SystemTaskDef,
210
+ scheduledFor: string,
211
+ startedAt: string,
212
+ durationMs: number,
213
+ trigger: TaskTrigger,
214
+ errMsg: string | null,
215
+ ): Promise<void> {
216
+ const isSuccess = errMsg === null;
217
+ const currentState = stateMap.get(task.id);
218
+ try {
219
+ await updateAndSave(
220
+ stateFilePath(),
221
+ stateMap,
222
+ task.id,
223
+ {
224
+ lastRunAt: scheduledFor,
225
+ lastRunResult: isSuccess ? TASK_RESULTS.success : TASK_RESULTS.error,
226
+ lastRunDurationMs: durationMs,
227
+ lastErrorMessage: errMsg,
228
+ consecutiveFailures: isSuccess ? 0 : (currentState?.consecutiveFailures ?? 0) + 1,
229
+ totalRuns: (currentState?.totalRuns ?? 0) + 1,
230
+ nextScheduledAt: computeNextScheduled(task),
231
+ },
232
+ stateDeps,
233
+ );
234
+ } catch (err) {
235
+ log.warn("scheduler", "state persistence failed", {
236
+ taskId: task.id,
237
+ error: String(err),
238
+ });
239
+ }
240
+ try {
241
+ await appendLogEntry(
242
+ logsDir(),
243
+ {
244
+ taskId: task.id,
245
+ taskName: task.name,
246
+ scheduledFor,
247
+ startedAt,
248
+ completedAt: new Date().toISOString(),
249
+ result: isSuccess ? TASK_RESULTS.success : TASK_RESULTS.error,
250
+ durationMs,
251
+ trigger,
252
+ ...(errMsg !== null && { errorMessage: errMsg }),
253
+ },
254
+ logDeps,
255
+ );
256
+ } catch (err) {
257
+ log.warn("scheduler", "log persistence failed", {
258
+ taskId: task.id,
259
+ error: String(err),
260
+ });
261
+ }
262
+ }
263
+
264
+ /** Safe state update — swallows errors. */
265
+ async function safeUpdateState(taskId: string, patch: Partial<TaskExecutionState>): Promise<void> {
266
+ try {
267
+ await updateAndSave(stateFilePath(), stateMap, taskId, patch, stateDeps);
268
+ } catch (err) {
269
+ log.warn("scheduler", "state update failed", {
270
+ taskId,
271
+ error: String(err),
272
+ });
273
+ }
274
+ }
275
+
276
+ /** Compute the window boundary that the current tick belongs to.
277
+ * For scheduled runs, this is the epoch-aligned window — not the
278
+ * wall-clock time of execution. This keeps lastRunAt consistent
279
+ * with catch-up's window-based accounting. */
280
+ function computeCurrentWindow(task: SystemTaskDef): string {
281
+ const coreSchedule = toCoreSchedule(task.schedule);
282
+ // The window that just fired is the latest one at or before now.
283
+ const nowMs = Date.now();
284
+ const windowMs = nextWindowAfter(coreSchedule, nowMs - (coreSchedule.type === SCHEDULE_TYPES.interval ? coreSchedule.intervalSec * ONE_SECOND_MS : 0));
285
+ return windowMs !== null && windowMs <= nowMs ? new Date(windowMs).toISOString() : new Date(nowMs).toISOString();
286
+ }
287
+
288
+ function computeNextScheduled(task: SystemTaskDef): string | null {
289
+ const coreSchedule = toCoreSchedule(task.schedule);
290
+ const next = nextWindowAfter(coreSchedule, Date.now() + 1);
291
+ return next !== null ? new Date(next).toISOString() : null;
292
+ }
293
+
294
+ function toCoreSchedule(schedule: TaskDefinition["schedule"]): TaskSchedule {
295
+ if (schedule.type === SCHEDULE_TYPES.interval) {
296
+ return {
297
+ type: SCHEDULE_TYPES.interval,
298
+ intervalSec: Math.round(schedule.intervalMs / ONE_SECOND_MS),
299
+ };
300
+ }
301
+ return schedule;
302
+ }