mulmoclaude 0.6.0 → 0.6.2

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 (172) hide show
  1. package/bin/mulmoclaude.js +1 -1
  2. package/client/assets/PluginScopedRoot-YjvQq0Nn.js +3 -0
  3. package/client/assets/{html2canvas-CDGcmOD3-BbPeutDg.js → html2canvas-CDGcmOD3-Bkf2uOth.js} +1 -1
  4. package/client/assets/{index-BbgSjFQ8.js → index-BwrlMMHr.js} +178 -141
  5. package/client/assets/index-CvvNuegU.css +2 -0
  6. package/client/assets/{index.es-DqtpmBm8-DJdTPdnc.js → index.es-DqtpmBm8-D9mAh_KQ.js} +1 -1
  7. package/client/assets/material-symbols-outlined-BOZVWuR3.woff2 +0 -0
  8. package/client/assets/runtime-protocol-vue-C1To4M3t.js +1 -0
  9. package/client/index.html +7 -6
  10. package/package.json +9 -7
  11. package/server/accounting/eventPublisher.ts +2 -1
  12. package/server/accounting/snapshotCache.ts +2 -1
  13. package/server/agent/activeTools.ts +16 -6
  14. package/server/agent/backend/claude-code.ts +1 -0
  15. package/server/agent/backend/types.ts +3 -0
  16. package/server/agent/config.ts +25 -2
  17. package/server/agent/index.ts +6 -0
  18. package/server/agent/mcp-server.ts +9 -6
  19. package/server/agent/mcp-tools/index.ts +15 -2
  20. package/server/agent/mcp-tools/notify.ts +20 -2
  21. package/server/agent/prompt.ts +37 -24
  22. package/server/api/routes/accounting.ts +31 -24
  23. package/server/api/routes/agent.ts +2 -2
  24. package/server/api/routes/config-refresh.ts +49 -0
  25. package/server/api/routes/config.ts +86 -68
  26. package/server/api/routes/files.ts +41 -17
  27. package/server/api/routes/hookLog.ts +95 -0
  28. package/server/api/routes/news.ts +39 -52
  29. package/server/api/routes/notifier.ts +14 -19
  30. package/server/api/routes/pdf.ts +2 -2
  31. package/server/api/routes/photo-locations.ts +79 -0
  32. package/server/api/routes/plugins.ts +11 -0
  33. package/server/api/routes/presentSvg.ts +107 -0
  34. package/server/api/routes/scheduler.ts +100 -98
  35. package/server/api/routes/schedulerTasks.ts +98 -95
  36. package/server/api/routes/sessions.ts +22 -27
  37. package/server/api/routes/sources.ts +45 -43
  38. package/server/api/routes/wiki/history.ts +6 -15
  39. package/server/api/routes/wiki.ts +73 -276
  40. package/server/events/file-change.ts +3 -2
  41. package/server/events/session-store/index.ts +2 -1
  42. package/server/index.ts +130 -8
  43. package/server/notifier/store.ts +3 -3
  44. package/server/plugins/preset-list.ts +16 -5
  45. package/server/plugins/runtime.ts +2 -2
  46. package/server/system/config.ts +138 -16
  47. package/server/utils/asyncHandler.ts +75 -0
  48. package/server/utils/exif.ts +321 -0
  49. package/server/utils/files/accounting-io.ts +19 -20
  50. package/server/utils/files/attachment-store.ts +69 -12
  51. package/server/utils/files/journal-io.ts +2 -1
  52. package/server/utils/files/json.ts +8 -1
  53. package/server/utils/files/reference-dirs-io.ts +2 -3
  54. package/server/utils/files/scheduler-overrides-io.ts +2 -3
  55. package/server/utils/files/svg-store.ts +27 -0
  56. package/server/utils/files/user-tasks-io.ts +2 -3
  57. package/server/utils/regex.ts +3 -12
  58. package/server/utils/text.ts +29 -0
  59. package/server/workspace/chat-index/summarizer.ts +5 -3
  60. package/server/workspace/cooking-recipes/migrate.ts +125 -0
  61. package/server/workspace/custom-dirs.ts +2 -2
  62. package/server/workspace/hooks/dispatcher.mjs +300 -0
  63. package/server/workspace/hooks/dispatcher.ts +55 -0
  64. package/server/workspace/hooks/handlers/configRefresh.ts +38 -0
  65. package/server/workspace/hooks/handlers/skillBridge.ts +223 -0
  66. package/server/workspace/hooks/handlers/wikiSnapshot.ts +43 -0
  67. package/server/workspace/hooks/provision.ts +222 -0
  68. package/server/workspace/hooks/shared/sidecar.ts +124 -0
  69. package/server/workspace/hooks/shared/stdin.ts +60 -0
  70. package/server/workspace/hooks/shared/workspace.ts +13 -0
  71. package/server/workspace/journal/dailyPass.ts +1 -6
  72. package/server/workspace/memory/io.ts +1 -34
  73. package/server/workspace/memory/migrate.ts +2 -1
  74. package/server/workspace/memory/snapshot.ts +26 -0
  75. package/server/workspace/memory/topic-io.ts +1 -18
  76. package/server/workspace/paths.ts +16 -0
  77. package/server/workspace/photo-locations/index.ts +149 -0
  78. package/server/workspace/photo-locations/list.ts +124 -0
  79. package/server/workspace/skills-preset/mc-cooking-coach/SKILL.md +217 -0
  80. package/server/workspace/skills-preset/mc-manage-automations/SKILL.md +119 -0
  81. package/server/workspace/skills-preset/mc-manage-skills/SKILL.md +128 -0
  82. package/server/workspace/skills-preset/mc-manage-sources/SKILL.md +106 -0
  83. package/server/workspace/skills-preset.ts +2 -1
  84. package/server/workspace/wiki-pages/io.ts +2 -1
  85. package/src/App.vue +78 -3
  86. package/src/components/ChatInput.vue +7 -8
  87. package/src/components/FileContentHeader.vue +1 -6
  88. package/src/components/FileDropOverlay.vue +18 -0
  89. package/src/components/NewsView.vue +2 -1
  90. package/src/components/RolesView.vue +14 -5
  91. package/src/components/SettingsMapTab.vue +140 -0
  92. package/src/components/SettingsMcpTab.vue +15 -10
  93. package/src/components/SettingsModal.vue +138 -112
  94. package/src/components/SettingsModelTab.vue +121 -0
  95. package/src/components/SettingsPhotosTab.vue +118 -0
  96. package/src/components/SourcesManager.vue +4 -3
  97. package/src/components/StackView.vue +43 -12
  98. package/src/composables/useContentDisplay.ts +16 -0
  99. package/src/composables/useFileDropZone.ts +148 -0
  100. package/src/composables/useImageErrorRepair.ts +29 -19
  101. package/src/composables/useSkillsList.ts +2 -1
  102. package/src/config/apiRoutes.ts +24 -0
  103. package/src/config/roles.ts +121 -70
  104. package/src/config/systemFileDescriptors.ts +2 -2
  105. package/src/config/toolNames.ts +26 -0
  106. package/src/index.css +26 -0
  107. package/src/lang/de.ts +70 -1
  108. package/src/lang/en.ts +69 -1
  109. package/src/lang/es.ts +69 -1
  110. package/src/lang/fr.ts +69 -1
  111. package/src/lang/ja.ts +69 -1
  112. package/src/lang/ko.ts +68 -1
  113. package/src/lang/pt-BR.ts +69 -1
  114. package/src/lang/zh.ts +67 -1
  115. package/src/lib/wiki-page/index-parse.ts +221 -0
  116. package/src/lib/wiki-page/link.ts +62 -0
  117. package/src/lib/wiki-page/lint.ts +105 -0
  118. package/src/lib/wiki-page/paths.ts +35 -0
  119. package/src/lib/wiki-page/slug.ts +28 -40
  120. package/src/main.ts +8 -0
  121. package/src/plugins/_extras.ts +6 -2
  122. package/src/plugins/_generated/metas.ts +4 -0
  123. package/src/plugins/_generated/registrations.ts +4 -0
  124. package/src/plugins/_generated/server-bindings.ts +6 -0
  125. package/src/plugins/accounting/Preview.vue +3 -6
  126. package/src/plugins/accounting/View.vue +2 -1
  127. package/src/plugins/accounting/components/AccountsModal.vue +3 -2
  128. package/src/plugins/accounting/components/JournalEntryForm.vue +2 -1
  129. package/src/plugins/accounting/components/JournalList.vue +2 -1
  130. package/src/plugins/accounting/components/OpeningBalancesForm.vue +2 -1
  131. package/src/plugins/accounting/currencies.ts +13 -0
  132. package/src/plugins/manageRoles/View.vue +16 -5
  133. package/src/plugins/manageSkills/View.vue +12 -4
  134. package/src/plugins/markdown/View.vue +6 -0
  135. package/src/plugins/photoLocations/View.vue +231 -0
  136. package/src/plugins/photoLocations/definition.ts +47 -0
  137. package/src/plugins/photoLocations/index.ts +38 -0
  138. package/src/plugins/photoLocations/meta.ts +35 -0
  139. package/src/plugins/presentMulmoScript/View.vue +76 -7
  140. package/src/plugins/presentMulmoScript/helpers.ts +15 -0
  141. package/src/plugins/presentSVG/Preview.vue +56 -0
  142. package/src/plugins/presentSVG/View.vue +465 -0
  143. package/src/plugins/presentSVG/definition.ts +29 -0
  144. package/src/plugins/presentSVG/index.ts +49 -0
  145. package/src/plugins/presentSVG/meta.ts +14 -0
  146. package/src/plugins/scheduler/View.vue +3 -7
  147. package/src/plugins/skill/View.vue +15 -16
  148. package/src/plugins/spreadsheet/View.vue +4 -0
  149. package/src/plugins/wiki/View.vue +1 -1
  150. package/src/plugins/wiki/helpers.ts +23 -5
  151. package/src/plugins/wiki/route.ts +12 -11
  152. package/src/tools/runtimeLoader.ts +75 -9
  153. package/src/utils/dom/iframeHeightClamp.ts +42 -0
  154. package/src/utils/format/bytes.ts +41 -0
  155. package/src/utils/format/date.ts +14 -2
  156. package/src/utils/image/imageRepairInlineScript.ts +192 -41
  157. package/src/utils/markdown/sanitize.ts +68 -0
  158. package/src/utils/markdown/setup.ts +36 -0
  159. package/src/utils/markdown/wikiEmbedHandlers.ts +170 -0
  160. package/src/utils/markdown/wikiEmbeds.ts +141 -0
  161. package/src/utils/markdown/workspaceLinkify.ts +73 -0
  162. package/src/utils/path/workspaceLinkRouter.ts +17 -1
  163. package/client/assets/index-ECD0lgIv.css +0 -2
  164. package/client/assets/material-symbols-outlined-BLDfUw-_.woff2 +0 -0
  165. package/client/assets/runtime-protocol-vue-6WYa8hAs.js +0 -1
  166. package/server/workspace/wiki-history/hook/snapshot.mjs +0 -98
  167. package/server/workspace/wiki-history/hook/snapshot.ts +0 -135
  168. package/server/workspace/wiki-history/provision.ts +0 -181
  169. /package/client/assets/{chunk-D8eiyYIV-C1eAZMzz.js → chunk-D8eiyYIV-CAXpUwLd.js} +0 -0
  170. /package/client/assets/{purify.es-Fx1Nqyry-BSVNht6S.js → purify.es-Fx1Nqyry-Dwtk-9WZ.js} +0 -0
  171. /package/client/assets/{typeof-DBp4T-Ny-C2xoZtcz.js → typeof-DBp4T-Ny-CSr8wx1e.js} +0 -0
  172. /package/client/assets/{vue-1e_vz2LW.js → vue-C8UuIO9J.js} +0 -0
@@ -0,0 +1,79 @@
1
+ // REST endpoint for the photo-locations plugin (#1222 PR-B).
2
+ //
3
+ // Single POST dispatch — same convention as accounting / scheduler.
4
+ // LLM tool calls flow Claude → MCP → here; the View also POSTs
5
+ // here directly for refreshes triggered by the
6
+ // `photoLocations:locations-changed` pubsub event.
7
+ //
8
+ // Two kinds for v1: `list` (every sidecar, newest-first) and
9
+ // `count` (scalar). `extractExif` and `rescan` ride a follow-up.
10
+
11
+ import { Router, Request, Response } from "express";
12
+
13
+ import { API_ROUTES } from "../../../src/config/apiRoutes.js";
14
+ import { PHOTO_LOCATIONS_KINDS } from "../../../src/plugins/photoLocations/definition.js";
15
+ import { bindRoute } from "../../utils/router.js";
16
+ import { listAllSidecars, countAllSidecars, type ListedSidecar } from "../../workspace/photo-locations/list.js";
17
+ import { log } from "../../system/logger/index.js";
18
+
19
+ const router = Router();
20
+
21
+ interface DispatchBody {
22
+ kind?: unknown;
23
+ }
24
+
25
+ interface DispatchToolResult {
26
+ kind: "photo-locations";
27
+ message: string;
28
+ data: { locations: ListedSidecar[]; total: number } | { total: number };
29
+ }
30
+
31
+ interface ErrorResponse {
32
+ error: string;
33
+ }
34
+
35
+ async function handleList(): Promise<DispatchToolResult> {
36
+ const locations = await listAllSidecars();
37
+ return {
38
+ kind: "photo-locations",
39
+ message: `${locations.length} captured photo location${locations.length === 1 ? "" : "s"}`,
40
+ data: { locations, total: locations.length },
41
+ };
42
+ }
43
+
44
+ async function handleCount(): Promise<DispatchToolResult> {
45
+ const total = await countAllSidecars();
46
+ return {
47
+ kind: "photo-locations",
48
+ message: `${total} captured photo location${total === 1 ? "" : "s"}`,
49
+ data: { total },
50
+ };
51
+ }
52
+
53
+ bindRoute(
54
+ router,
55
+ API_ROUTES.photoLocations.dispatch,
56
+ async (req: Request<object, unknown, DispatchBody>, res: Response<DispatchToolResult | ErrorResponse>) => {
57
+ const { kind } = req.body ?? {};
58
+ if (typeof kind !== "string") {
59
+ res.status(400).json({ error: "request body must include a string `kind` field" });
60
+ return;
61
+ }
62
+ try {
63
+ if (kind === PHOTO_LOCATIONS_KINDS.list) {
64
+ res.json(await handleList());
65
+ return;
66
+ }
67
+ if (kind === PHOTO_LOCATIONS_KINDS.count) {
68
+ res.json(await handleCount());
69
+ return;
70
+ }
71
+ res.status(400).json({ error: `unknown kind: ${kind}` });
72
+ } catch (err) {
73
+ log.error("photo-locations-route", "dispatch failed", { kind, error: String(err) });
74
+ res.status(500).json({ error: "internal error" });
75
+ }
76
+ },
77
+ );
78
+
79
+ export default router;
@@ -5,6 +5,7 @@ import { executeQuiz } from "@mulmochat-plugin/quiz";
5
5
  import { executeForm } from "../../../src/plugins/presentForm/plugin.js";
6
6
  import { executeOpenCanvas } from "../../../src/plugins/canvas/definition.js";
7
7
  import { executePresent3D } from "@gui-chat-plugin/present3d";
8
+ import { executeMapControl } from "@gui-chat-plugin/google-map";
8
9
  import { errorMessage } from "../../utils/errors.js";
9
10
  import { badRequest, serverError } from "../../utils/httpError.js";
10
11
  import { saveImage } from "../../utils/files/image-store.js";
@@ -267,6 +268,16 @@ router.post(
267
268
  wrapPluginExecute((req) => executePresent3D(null as never, req.body)),
268
269
  );
269
270
 
271
+ // mapControl — Google Map (showLocation / Places / Directions etc.)
272
+ // from `@gui-chat-plugin/google-map`. The package's `executeMapControl`
273
+ // returns the action descriptor; the rendered View — mounted host-side
274
+ // from `App.vue` — performs the actual Google Maps JS calls and
275
+ // receives the API key as a prop sourced from `AppSettings`.
276
+ router.post(
277
+ API_ROUTES.plugins.googleMap,
278
+ wrapPluginExecute((req) => executeMapControl(null as never, req.body)),
279
+ );
280
+
270
281
  // META aggregator diagnostics — boot-time host/plugin or plugin/plugin
271
282
  // key collisions. The frontend fetches this once at mount so a tab
272
283
  // that opens after the boot-time `publishNotification` fired still
@@ -0,0 +1,107 @@
1
+ import { Router, Request, Response } from "express";
2
+ import { WORKSPACE_DIRS } from "../../workspace/paths.js";
3
+ import { writeWorkspaceText } from "../../utils/files/workspace-io.js";
4
+ import { buildArtifactPath } from "../../utils/files/naming.js";
5
+ import { overwriteSvg, isSvgPath } from "../../utils/files/svg-store.js";
6
+ import { errorMessage } from "../../utils/errors.js";
7
+ import { badRequest, serverError } from "../../utils/httpError.js";
8
+ import { API_ROUTES } from "../../../src/config/apiRoutes.js";
9
+ import { bindRoute } from "../../utils/router.js";
10
+ import { log } from "../../system/logger/index.js";
11
+ import { previewSnippet } from "../../utils/logPreview.js";
12
+ import { publishFileChange } from "../../events/file-change.js";
13
+
14
+ const router = Router();
15
+
16
+ interface PresentSvgBody {
17
+ svg: string;
18
+ title?: string;
19
+ }
20
+
21
+ interface PresentSvgSuccessResponse {
22
+ message: string;
23
+ instructions: string;
24
+ data: { title?: string; filePath: string };
25
+ }
26
+
27
+ interface PresentSvgErrorResponse {
28
+ error: string;
29
+ }
30
+
31
+ type PresentSvgResponse = PresentSvgSuccessResponse | PresentSvgErrorResponse;
32
+
33
+ bindRoute(router, API_ROUTES.svg.create, async (req: Request<object, unknown, PresentSvgBody>, res: Response<PresentSvgResponse>) => {
34
+ const { svg, title } = req.body;
35
+ log.info("svg", "present: start", {
36
+ titlePreview: typeof title === "string" ? previewSnippet(title) : undefined,
37
+ bytes: typeof svg === "string" ? svg.length : undefined,
38
+ });
39
+ if (!svg) {
40
+ log.warn("svg", "present: missing svg");
41
+ badRequest(res, "svg is required");
42
+ return;
43
+ }
44
+
45
+ try {
46
+ const filePath = buildArtifactPath(WORKSPACE_DIRS.svgs, title, ".svg", "drawing");
47
+ await writeWorkspaceText(filePath, svg);
48
+ log.info("svg", "present: ok", { filePath, bytes: svg.length });
49
+ void publishFileChange(filePath);
50
+ res.json({
51
+ message: `Saved SVG to ${filePath}`,
52
+ instructions: "Acknowledge that the SVG drawing has been presented to the user.",
53
+ data: { title, filePath },
54
+ });
55
+ } catch (err) {
56
+ log.error("svg", "present: threw", { error: errorMessage(err) });
57
+ serverError(res, errorMessage(err));
58
+ }
59
+ });
60
+
61
+ interface UpdateSvgBody {
62
+ relativePath: string;
63
+ svg: string;
64
+ }
65
+
66
+ interface UpdateSvgSuccessResponse {
67
+ path: string;
68
+ }
69
+
70
+ interface UpdateSvgErrorResponse {
71
+ error: string;
72
+ }
73
+
74
+ bindRoute(
75
+ router,
76
+ API_ROUTES.svg.update,
77
+ async (req: Request<object, unknown, UpdateSvgBody>, res: Response<UpdateSvgSuccessResponse | UpdateSvgErrorResponse>) => {
78
+ const { relativePath, svg } = req.body;
79
+ log.info("svg", "update: start", {
80
+ pathPreview: typeof relativePath === "string" ? previewSnippet(relativePath) : undefined,
81
+ bytes: typeof svg === "string" ? svg.length : undefined,
82
+ });
83
+ if (!svg) {
84
+ log.warn("svg", "update: missing svg");
85
+ badRequest(res, "svg is required");
86
+ return;
87
+ }
88
+ if (!relativePath || !isSvgPath(relativePath)) {
89
+ log.warn("svg", "update: invalid relativePath", {
90
+ pathPreview: typeof relativePath === "string" ? previewSnippet(relativePath) : undefined,
91
+ });
92
+ badRequest(res, "invalid svg relativePath");
93
+ return;
94
+ }
95
+ try {
96
+ await overwriteSvg(relativePath, svg);
97
+ log.info("svg", "update: ok", { pathPreview: previewSnippet(relativePath), bytes: svg.length });
98
+ void publishFileChange(relativePath);
99
+ res.json({ path: relativePath });
100
+ } catch (err) {
101
+ log.error("svg", "update: threw", { pathPreview: previewSnippet(relativePath), error: errorMessage(err) });
102
+ serverError(res, errorMessage(err));
103
+ }
104
+ },
105
+ );
106
+
107
+ export default router;
@@ -10,8 +10,8 @@ import { saveUserTasks } from "../../utils/files/user-tasks-io.js";
10
10
  import { startChat } from "./agent.js";
11
11
  import { log } from "../../system/logger/index.js";
12
12
  import { SCHEDULER_ACTIONS, TASK_ACTIONS } from "../../../src/plugins/scheduler/actions.js";
13
- import { badRequest, notFound, serverError } from "../../utils/httpError.js";
14
- import { errorMessage } from "../../utils/errors.js";
13
+ import { badRequest, notFound } from "../../utils/httpError.js";
14
+ import { asyncHandler } from "../../utils/asyncHandler.js";
15
15
  import { makeUuid } from "../../utils/id.js";
16
16
 
17
17
  const router = Router();
@@ -47,118 +47,120 @@ interface SchedulerBody extends SchedulerActionInput {
47
47
  bindRoute(
48
48
  router,
49
49
  API_ROUTES.scheduler.dispatch,
50
- async (req: Request<object, unknown, SchedulerBody>, res: Response<DispatchSuccessResponse<ScheduledItem> | DispatchErrorResponse | unknown>) => {
51
- const { action, ...input } = req.body;
50
+ asyncHandler<Request<object, unknown, SchedulerBody>, Response<DispatchSuccessResponse<ScheduledItem> | DispatchErrorResponse | unknown>>(
51
+ "scheduler",
52
+ "Internal server error",
53
+ async (req, res) => {
54
+ const { action, ...input } = req.body;
52
55
 
53
- // Route task actions to the user-task subsystem
54
- if (TASK_ACTIONS.has(action)) {
55
- await handleTaskAction(action, input, res);
56
- return;
57
- }
56
+ // Route task actions to the user-task subsystem
57
+ if (TASK_ACTIONS.has(action)) {
58
+ await handleTaskAction(action, input, res);
59
+ return;
60
+ }
58
61
 
59
- // Calendar item actions (existing behavior)
60
- const items = loadItems();
61
- const result = dispatchScheduler(action, items, input);
62
- respondWithDispatchResult(res, result, {
63
- shouldPersist: action !== SCHEDULER_ACTIONS.show,
64
- instructions: "Display the updated scheduler to the user.",
65
- persist: saveItems,
66
- });
67
- },
62
+ // Calendar item actions (existing behavior)
63
+ const items = loadItems();
64
+ const result = dispatchScheduler(action, items, input);
65
+ respondWithDispatchResult(res, result, {
66
+ shouldPersist: action !== SCHEDULER_ACTIONS.show,
67
+ instructions: "Display the updated scheduler to the user.",
68
+ persist: saveItems,
69
+ });
70
+ },
71
+ ),
68
72
  );
69
73
 
70
74
  async function handleTaskAction(action: string, input: Record<string, unknown>, res: Response): Promise<void> {
71
75
  log.info("scheduler", "task action: start", { action });
72
- try {
73
- if (action === SCHEDULER_ACTIONS.listTasks) {
74
- const tasks = loadUserTasks();
75
- log.info("scheduler", "task action: listTasks ok", { tasks: tasks.length });
76
- res.json({
77
- uuid: makeUuid(),
78
- message: `${tasks.length} scheduled task(s) found.`,
79
- data: { tasks },
80
- });
81
- return;
82
- }
76
+ // Errors bubble up to the asyncHandler wrapper on the dispatch route,
77
+ // which logs at `log.error("scheduler", "handler threw", …)` and
78
+ // returns a generic 500. No inner try/catch needed here.
79
+ if (action === SCHEDULER_ACTIONS.listTasks) {
80
+ const tasks = loadUserTasks();
81
+ log.info("scheduler", "task action: listTasks ok", { tasks: tasks.length });
82
+ res.json({
83
+ uuid: makeUuid(),
84
+ message: `${tasks.length} scheduled task(s) found.`,
85
+ data: { tasks },
86
+ });
87
+ return;
88
+ }
83
89
 
84
- if (action === SCHEDULER_ACTIONS.createTask) {
85
- const result = validateAndCreate(input);
86
- if (result.kind === "error") {
87
- log.warn("scheduler", "task action: createTask validation failed", { error: result.error });
88
- badRequest(res, result.error);
89
- return;
90
- }
91
- const tasks = loadUserTasks();
92
- tasks.push(result.task);
93
- await saveUserTasks(tasks);
94
- await refreshUserTasks();
95
- log.info("scheduler", "task action: createTask ok", { id: result.task.id, name: result.task.name });
96
- res.json({
97
- uuid: makeUuid(),
98
- message: `Task "${result.task.name}" created and scheduled.`,
99
- data: { task: result.task },
100
- });
90
+ if (action === SCHEDULER_ACTIONS.createTask) {
91
+ const result = validateAndCreate(input);
92
+ if (result.kind === "error") {
93
+ log.warn("scheduler", "task action: createTask validation failed", { error: result.error });
94
+ badRequest(res, result.error);
101
95
  return;
102
96
  }
97
+ const tasks = loadUserTasks();
98
+ tasks.push(result.task);
99
+ await saveUserTasks(tasks);
100
+ await refreshUserTasks();
101
+ log.info("scheduler", "task action: createTask ok", { id: result.task.id, name: result.task.name });
102
+ res.json({
103
+ uuid: makeUuid(),
104
+ message: `Task "${result.task.name}" created and scheduled.`,
105
+ data: { task: result.task },
106
+ });
107
+ return;
108
+ }
103
109
 
104
- if (action === SCHEDULER_ACTIONS.deleteTask) {
105
- const taskId = typeof input.id === "string" ? input.id : "";
106
- const tasks = loadUserTasks();
107
- const idx = tasks.findIndex((task) => task.id === taskId);
108
- if (idx === -1) {
109
- log.warn("scheduler", "task action: deleteTask not found", { taskId });
110
- notFound(res, `task not found: ${taskId}`);
111
- return;
112
- }
113
- const { name } = tasks[idx];
114
- tasks.splice(idx, 1);
115
- await saveUserTasks(tasks);
116
- await refreshUserTasks();
117
- log.info("scheduler", "task action: deleteTask ok", { taskId, name });
118
- res.json({
119
- uuid: makeUuid(),
120
- message: `Task "${name}" deleted.`,
121
- data: { deleted: taskId },
122
- });
110
+ if (action === SCHEDULER_ACTIONS.deleteTask) {
111
+ const taskId = typeof input.id === "string" ? input.id : "";
112
+ const tasks = loadUserTasks();
113
+ const idx = tasks.findIndex((task) => task.id === taskId);
114
+ if (idx === -1) {
115
+ log.warn("scheduler", "task action: deleteTask not found", { taskId });
116
+ notFound(res, `task not found: ${taskId}`);
123
117
  return;
124
118
  }
119
+ const { name } = tasks[idx];
120
+ tasks.splice(idx, 1);
121
+ await saveUserTasks(tasks);
122
+ await refreshUserTasks();
123
+ log.info("scheduler", "task action: deleteTask ok", { taskId, name });
124
+ res.json({
125
+ uuid: makeUuid(),
126
+ message: `Task "${name}" deleted.`,
127
+ data: { deleted: taskId },
128
+ });
129
+ return;
130
+ }
125
131
 
126
- if (action === SCHEDULER_ACTIONS.runTask) {
127
- const taskId = typeof input.id === "string" ? input.id : "";
128
- const tasks = loadUserTasks();
129
- const task = tasks.find((candidate) => candidate.id === taskId);
130
- if (!task) {
131
- notFound(res, `task not found: ${taskId}`);
132
- return;
133
- }
134
- const chatSessionId = makeUuid();
135
- log.info("scheduler", "manual run via MCP", {
136
- name: task.name,
137
- chatSessionId,
138
- });
139
- startChat({
140
- message: task.prompt,
141
- roleId: task.roleId,
142
- chatSessionId,
143
- origin: SESSION_ORIGINS.scheduler,
144
- }).catch((err) => {
145
- log.error("scheduler", "manual run failed", {
146
- error: String(err),
147
- });
148
- });
149
- res.json({
150
- uuid: makeUuid(),
151
- message: `Task "${task.name}" triggered.`,
152
- data: { triggered: taskId, chatSessionId },
153
- });
132
+ if (action === SCHEDULER_ACTIONS.runTask) {
133
+ const taskId = typeof input.id === "string" ? input.id : "";
134
+ const tasks = loadUserTasks();
135
+ const task = tasks.find((candidate) => candidate.id === taskId);
136
+ if (!task) {
137
+ notFound(res, `task not found: ${taskId}`);
154
138
  return;
155
139
  }
156
-
157
- badRequest(res, `unknown task action: ${action}`);
158
- } catch (err) {
159
- log.error("scheduler", "task action failed", { error: errorMessage(err) });
160
- serverError(res, "Internal server error");
140
+ const chatSessionId = makeUuid();
141
+ log.info("scheduler", "manual run via MCP", {
142
+ name: task.name,
143
+ chatSessionId,
144
+ });
145
+ startChat({
146
+ message: task.prompt,
147
+ roleId: task.roleId,
148
+ chatSessionId,
149
+ origin: SESSION_ORIGINS.scheduler,
150
+ }).catch((err) => {
151
+ log.error("scheduler", "manual run failed", {
152
+ error: String(err),
153
+ });
154
+ });
155
+ res.json({
156
+ uuid: makeUuid(),
157
+ message: `Task "${task.name}" triggered.`,
158
+ data: { triggered: taskId, chatSessionId },
159
+ });
160
+ return;
161
161
  }
162
+
163
+ badRequest(res, `unknown task action: ${action}`);
162
164
  }
163
165
 
164
166
  export default router;