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.
- package/bin/mulmoclaude.js +1 -1
- package/client/assets/PluginScopedRoot-YjvQq0Nn.js +3 -0
- package/client/assets/{html2canvas-CDGcmOD3-BbPeutDg.js → html2canvas-CDGcmOD3-Bkf2uOth.js} +1 -1
- package/client/assets/{index-BbgSjFQ8.js → index-BwrlMMHr.js} +178 -141
- package/client/assets/index-CvvNuegU.css +2 -0
- package/client/assets/{index.es-DqtpmBm8-DJdTPdnc.js → index.es-DqtpmBm8-D9mAh_KQ.js} +1 -1
- package/client/assets/material-symbols-outlined-BOZVWuR3.woff2 +0 -0
- package/client/assets/runtime-protocol-vue-C1To4M3t.js +1 -0
- package/client/index.html +7 -6
- package/package.json +9 -7
- package/server/accounting/eventPublisher.ts +2 -1
- package/server/accounting/snapshotCache.ts +2 -1
- package/server/agent/activeTools.ts +16 -6
- package/server/agent/backend/claude-code.ts +1 -0
- package/server/agent/backend/types.ts +3 -0
- package/server/agent/config.ts +25 -2
- package/server/agent/index.ts +6 -0
- package/server/agent/mcp-server.ts +9 -6
- package/server/agent/mcp-tools/index.ts +15 -2
- package/server/agent/mcp-tools/notify.ts +20 -2
- package/server/agent/prompt.ts +37 -24
- package/server/api/routes/accounting.ts +31 -24
- package/server/api/routes/agent.ts +2 -2
- package/server/api/routes/config-refresh.ts +49 -0
- package/server/api/routes/config.ts +86 -68
- package/server/api/routes/files.ts +41 -17
- package/server/api/routes/hookLog.ts +95 -0
- package/server/api/routes/news.ts +39 -52
- package/server/api/routes/notifier.ts +14 -19
- package/server/api/routes/pdf.ts +2 -2
- package/server/api/routes/photo-locations.ts +79 -0
- package/server/api/routes/plugins.ts +11 -0
- package/server/api/routes/presentSvg.ts +107 -0
- package/server/api/routes/scheduler.ts +100 -98
- package/server/api/routes/schedulerTasks.ts +98 -95
- package/server/api/routes/sessions.ts +22 -27
- package/server/api/routes/sources.ts +45 -43
- package/server/api/routes/wiki/history.ts +6 -15
- package/server/api/routes/wiki.ts +73 -276
- package/server/events/file-change.ts +3 -2
- package/server/events/session-store/index.ts +2 -1
- package/server/index.ts +130 -8
- package/server/notifier/store.ts +3 -3
- package/server/plugins/preset-list.ts +16 -5
- package/server/plugins/runtime.ts +2 -2
- package/server/system/config.ts +138 -16
- package/server/utils/asyncHandler.ts +75 -0
- package/server/utils/exif.ts +321 -0
- package/server/utils/files/accounting-io.ts +19 -20
- package/server/utils/files/attachment-store.ts +69 -12
- package/server/utils/files/journal-io.ts +2 -1
- package/server/utils/files/json.ts +8 -1
- package/server/utils/files/reference-dirs-io.ts +2 -3
- package/server/utils/files/scheduler-overrides-io.ts +2 -3
- package/server/utils/files/svg-store.ts +27 -0
- package/server/utils/files/user-tasks-io.ts +2 -3
- package/server/utils/regex.ts +3 -12
- package/server/utils/text.ts +29 -0
- package/server/workspace/chat-index/summarizer.ts +5 -3
- package/server/workspace/cooking-recipes/migrate.ts +125 -0
- package/server/workspace/custom-dirs.ts +2 -2
- package/server/workspace/hooks/dispatcher.mjs +300 -0
- package/server/workspace/hooks/dispatcher.ts +55 -0
- package/server/workspace/hooks/handlers/configRefresh.ts +38 -0
- package/server/workspace/hooks/handlers/skillBridge.ts +223 -0
- package/server/workspace/hooks/handlers/wikiSnapshot.ts +43 -0
- package/server/workspace/hooks/provision.ts +222 -0
- package/server/workspace/hooks/shared/sidecar.ts +124 -0
- package/server/workspace/hooks/shared/stdin.ts +60 -0
- package/server/workspace/hooks/shared/workspace.ts +13 -0
- package/server/workspace/journal/dailyPass.ts +1 -6
- package/server/workspace/memory/io.ts +1 -34
- package/server/workspace/memory/migrate.ts +2 -1
- package/server/workspace/memory/snapshot.ts +26 -0
- package/server/workspace/memory/topic-io.ts +1 -18
- package/server/workspace/paths.ts +16 -0
- package/server/workspace/photo-locations/index.ts +149 -0
- package/server/workspace/photo-locations/list.ts +124 -0
- package/server/workspace/skills-preset/mc-cooking-coach/SKILL.md +217 -0
- package/server/workspace/skills-preset/mc-manage-automations/SKILL.md +119 -0
- package/server/workspace/skills-preset/mc-manage-skills/SKILL.md +128 -0
- package/server/workspace/skills-preset/mc-manage-sources/SKILL.md +106 -0
- package/server/workspace/skills-preset.ts +2 -1
- package/server/workspace/wiki-pages/io.ts +2 -1
- package/src/App.vue +78 -3
- package/src/components/ChatInput.vue +7 -8
- package/src/components/FileContentHeader.vue +1 -6
- package/src/components/FileDropOverlay.vue +18 -0
- package/src/components/NewsView.vue +2 -1
- package/src/components/RolesView.vue +14 -5
- package/src/components/SettingsMapTab.vue +140 -0
- package/src/components/SettingsMcpTab.vue +15 -10
- package/src/components/SettingsModal.vue +138 -112
- package/src/components/SettingsModelTab.vue +121 -0
- package/src/components/SettingsPhotosTab.vue +118 -0
- package/src/components/SourcesManager.vue +4 -3
- package/src/components/StackView.vue +43 -12
- package/src/composables/useContentDisplay.ts +16 -0
- package/src/composables/useFileDropZone.ts +148 -0
- package/src/composables/useImageErrorRepair.ts +29 -19
- package/src/composables/useSkillsList.ts +2 -1
- package/src/config/apiRoutes.ts +24 -0
- package/src/config/roles.ts +121 -70
- package/src/config/systemFileDescriptors.ts +2 -2
- package/src/config/toolNames.ts +26 -0
- package/src/index.css +26 -0
- package/src/lang/de.ts +70 -1
- package/src/lang/en.ts +69 -1
- package/src/lang/es.ts +69 -1
- package/src/lang/fr.ts +69 -1
- package/src/lang/ja.ts +69 -1
- package/src/lang/ko.ts +68 -1
- package/src/lang/pt-BR.ts +69 -1
- package/src/lang/zh.ts +67 -1
- package/src/lib/wiki-page/index-parse.ts +221 -0
- package/src/lib/wiki-page/link.ts +62 -0
- package/src/lib/wiki-page/lint.ts +105 -0
- package/src/lib/wiki-page/paths.ts +35 -0
- package/src/lib/wiki-page/slug.ts +28 -40
- package/src/main.ts +8 -0
- package/src/plugins/_extras.ts +6 -2
- package/src/plugins/_generated/metas.ts +4 -0
- package/src/plugins/_generated/registrations.ts +4 -0
- package/src/plugins/_generated/server-bindings.ts +6 -0
- package/src/plugins/accounting/Preview.vue +3 -6
- package/src/plugins/accounting/View.vue +2 -1
- package/src/plugins/accounting/components/AccountsModal.vue +3 -2
- package/src/plugins/accounting/components/JournalEntryForm.vue +2 -1
- package/src/plugins/accounting/components/JournalList.vue +2 -1
- package/src/plugins/accounting/components/OpeningBalancesForm.vue +2 -1
- package/src/plugins/accounting/currencies.ts +13 -0
- package/src/plugins/manageRoles/View.vue +16 -5
- package/src/plugins/manageSkills/View.vue +12 -4
- package/src/plugins/markdown/View.vue +6 -0
- package/src/plugins/photoLocations/View.vue +231 -0
- package/src/plugins/photoLocations/definition.ts +47 -0
- package/src/plugins/photoLocations/index.ts +38 -0
- package/src/plugins/photoLocations/meta.ts +35 -0
- package/src/plugins/presentMulmoScript/View.vue +76 -7
- package/src/plugins/presentMulmoScript/helpers.ts +15 -0
- package/src/plugins/presentSVG/Preview.vue +56 -0
- package/src/plugins/presentSVG/View.vue +465 -0
- package/src/plugins/presentSVG/definition.ts +29 -0
- package/src/plugins/presentSVG/index.ts +49 -0
- package/src/plugins/presentSVG/meta.ts +14 -0
- package/src/plugins/scheduler/View.vue +3 -7
- package/src/plugins/skill/View.vue +15 -16
- package/src/plugins/spreadsheet/View.vue +4 -0
- package/src/plugins/wiki/View.vue +1 -1
- package/src/plugins/wiki/helpers.ts +23 -5
- package/src/plugins/wiki/route.ts +12 -11
- package/src/tools/runtimeLoader.ts +75 -9
- package/src/utils/dom/iframeHeightClamp.ts +42 -0
- package/src/utils/format/bytes.ts +41 -0
- package/src/utils/format/date.ts +14 -2
- package/src/utils/image/imageRepairInlineScript.ts +192 -41
- package/src/utils/markdown/sanitize.ts +68 -0
- package/src/utils/markdown/setup.ts +36 -0
- package/src/utils/markdown/wikiEmbedHandlers.ts +170 -0
- package/src/utils/markdown/wikiEmbeds.ts +141 -0
- package/src/utils/markdown/workspaceLinkify.ts +73 -0
- package/src/utils/path/workspaceLinkRouter.ts +17 -1
- package/client/assets/index-ECD0lgIv.css +0 -2
- package/client/assets/material-symbols-outlined-BLDfUw-_.woff2 +0 -0
- package/client/assets/runtime-protocol-vue-6WYa8hAs.js +0 -1
- package/server/workspace/wiki-history/hook/snapshot.mjs +0 -98
- package/server/workspace/wiki-history/hook/snapshot.ts +0 -135
- package/server/workspace/wiki-history/provision.ts +0 -181
- /package/client/assets/{chunk-D8eiyYIV-C1eAZMzz.js → chunk-D8eiyYIV-CAXpUwLd.js} +0 -0
- /package/client/assets/{purify.es-Fx1Nqyry-BSVNht6S.js → purify.es-Fx1Nqyry-Dwtk-9WZ.js} +0 -0
- /package/client/assets/{typeof-DBp4T-Ny-C2xoZtcz.js → typeof-DBp4T-Ny-CSr8wx1e.js} +0 -0
- /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
|
|
14
|
-
import {
|
|
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
|
-
|
|
51
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
})
|
|
81
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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;
|