verybot 0.1.8
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/README.md +167 -0
- package/dist/aliases/store.d.ts +21 -0
- package/dist/aliases/store.js +148 -0
- package/dist/aliases/types.d.ts +6 -0
- package/dist/aliases/types.js +1 -0
- package/dist/brain/agent-registry.d.ts +96 -0
- package/dist/brain/agent-registry.js +141 -0
- package/dist/brain/agent.d.ts +167 -0
- package/dist/brain/agent.js +932 -0
- package/dist/brain/channel-store.d.ts +27 -0
- package/dist/brain/channel-store.js +78 -0
- package/dist/brain/compaction.d.ts +37 -0
- package/dist/brain/compaction.js +214 -0
- package/dist/brain/context.d.ts +43 -0
- package/dist/brain/context.js +139 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +24 -0
- package/dist/brain/loop.js +318 -0
- package/dist/brain/mcp-adapter.d.ts +43 -0
- package/dist/brain/mcp-adapter.js +244 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +14 -0
- package/dist/brain/providers.js +85 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +111 -0
- package/dist/brain/run-tools.d.ts +50 -0
- package/dist/brain/run-tools.js +136 -0
- package/dist/brain/session-key.d.ts +23 -0
- package/dist/brain/session-key.js +41 -0
- package/dist/brain/session-state.d.ts +36 -0
- package/dist/brain/session-state.js +51 -0
- package/dist/brain/session-store.d.ts +50 -0
- package/dist/brain/session-store.js +207 -0
- package/dist/brain/session.d.ts +32 -0
- package/dist/brain/session.js +75 -0
- package/dist/brain/task-subscriber.d.ts +56 -0
- package/dist/brain/task-subscriber.js +317 -0
- package/dist/brain/user-content.d.ts +16 -0
- package/dist/brain/user-content.js +32 -0
- package/dist/brain/utils.d.ts +4 -0
- package/dist/brain/utils.js +26 -0
- package/dist/brain/worker-coordinator.d.ts +25 -0
- package/dist/brain/worker-coordinator.js +83 -0
- package/dist/channels/commands.d.ts +50 -0
- package/dist/channels/commands.js +132 -0
- package/dist/channels/discord/channel.d.ts +29 -0
- package/dist/channels/discord/channel.js +159 -0
- package/dist/channels/discord/markdown.d.ts +19 -0
- package/dist/channels/discord/markdown.js +62 -0
- package/dist/channels/manager.d.ts +29 -0
- package/dist/channels/manager.js +100 -0
- package/dist/channels/slack/channel.d.ts +37 -0
- package/dist/channels/slack/channel.js +227 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +32 -0
- package/dist/channels/specs.js +99 -0
- package/dist/channels/telegram/channel.d.ts +29 -0
- package/dist/channels/telegram/channel.js +182 -0
- package/dist/channels/telegram/markdown.d.ts +17 -0
- package/dist/channels/telegram/markdown.js +66 -0
- package/dist/channels/types.d.ts +26 -0
- package/dist/channels/types.js +1 -0
- package/dist/channels/whatsapp/channel.d.ts +34 -0
- package/dist/channels/whatsapp/channel.js +276 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/claude-login.d.ts +5 -0
- package/dist/cli/claude-login.js +47 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.js +96 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/context-manager.d.ts +28 -0
- package/dist/computer/browser/context-manager.js +78 -0
- package/dist/computer/browser/manager.d.ts +91 -0
- package/dist/computer/browser/manager.js +344 -0
- package/dist/computer/browser/profile-badge.d.ts +13 -0
- package/dist/computer/browser/profile-badge.js +67 -0
- package/dist/computer/browser/screenshot.d.ts +5 -0
- package/dist/computer/browser/screenshot.js +21 -0
- package/dist/computer/browser/snapshot.d.ts +30 -0
- package/dist/computer/browser/snapshot.js +242 -0
- package/dist/computer/browser/tools.d.ts +5 -0
- package/dist/computer/browser/tools.js +167 -0
- package/dist/computer/browser/types.d.ts +26 -0
- package/dist/computer/browser/types.js +1 -0
- package/dist/computer/desktop/adapter.d.ts +25 -0
- package/dist/computer/desktop/adapter.js +11 -0
- package/dist/computer/desktop/macos.d.ts +24 -0
- package/dist/computer/desktop/macos.js +223 -0
- package/dist/computer/desktop/tools.d.ts +25 -0
- package/dist/computer/desktop/tools.js +114 -0
- package/dist/config/agent-config.d.ts +55 -0
- package/dist/config/agent-config.js +16 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +112 -0
- package/dist/config/model-spec.d.ts +8 -0
- package/dist/config/model-spec.js +66 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +110 -0
- package/dist/config.js +259 -0
- package/dist/control-ui/assets/index-Cbl7G5Sc.css +1 -0
- package/dist/control-ui/assets/index-Cu1P4C62.js +266 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
- package/dist/control-ui/index.html +14 -0
- package/dist/control-ui/vite.svg +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +11 -0
- package/dist/gateway/broadcast.d.ts +5 -0
- package/dist/gateway/broadcast.js +33 -0
- package/dist/gateway/methods/aliases.d.ts +17 -0
- package/dist/gateway/methods/aliases.js +22 -0
- package/dist/gateway/methods/chat.d.ts +33 -0
- package/dist/gateway/methods/chat.js +37 -0
- package/dist/gateway/methods/config.d.ts +14 -0
- package/dist/gateway/methods/config.js +24 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/playbooks.d.ts +45 -0
- package/dist/gateway/methods/playbooks.js +488 -0
- package/dist/gateway/methods/prompt-templates.d.ts +27 -0
- package/dist/gateway/methods/prompt-templates.js +106 -0
- package/dist/gateway/methods/scheduler.d.ts +62 -0
- package/dist/gateway/methods/scheduler.js +129 -0
- package/dist/gateway/methods/sessions.d.ts +44 -0
- package/dist/gateway/methods/sessions.js +111 -0
- package/dist/gateway/methods/system.d.ts +12 -0
- package/dist/gateway/methods/system.js +39 -0
- package/dist/gateway/methods/tasks.d.ts +40 -0
- package/dist/gateway/methods/tasks.js +151 -0
- package/dist/gateway/methods/teams.d.ts +69 -0
- package/dist/gateway/methods/teams.js +376 -0
- package/dist/gateway/methods/tools.d.ts +6 -0
- package/dist/gateway/methods/tools.js +7 -0
- package/dist/gateway/methods/whatsapp.d.ts +19 -0
- package/dist/gateway/methods/whatsapp.js +35 -0
- package/dist/gateway/rpc.d.ts +38 -0
- package/dist/gateway/rpc.js +79 -0
- package/dist/gateway/server.d.ts +9 -0
- package/dist/gateway/server.js +137 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +254 -0
- package/dist/integrations/github.d.ts +7 -0
- package/dist/integrations/github.js +133 -0
- package/dist/integrations/mcp.d.ts +7 -0
- package/dist/integrations/mcp.js +106 -0
- package/dist/integrations/registry.d.ts +47 -0
- package/dist/integrations/registry.js +332 -0
- package/dist/integrations/scanner.d.ts +10 -0
- package/dist/integrations/scanner.js +122 -0
- package/dist/integrations/twitter.d.ts +10 -0
- package/dist/integrations/twitter.js +120 -0
- package/dist/integrations/types.d.ts +72 -0
- package/dist/integrations/types.js +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +104 -0
- package/dist/markdown/chunk.d.ts +9 -0
- package/dist/markdown/chunk.js +52 -0
- package/dist/markdown/ir.d.ts +37 -0
- package/dist/markdown/ir.js +529 -0
- package/dist/markdown/render.d.ts +22 -0
- package/dist/markdown/render.js +148 -0
- package/dist/markdown/table-render.d.ts +43 -0
- package/dist/markdown/table-render.js +219 -0
- package/dist/markdown/tables.d.ts +17 -0
- package/dist/markdown/tables.js +27 -0
- package/dist/memory/embedding.d.ts +16 -0
- package/dist/memory/embedding.js +66 -0
- package/dist/memory/explicit.d.ts +16 -0
- package/dist/memory/explicit.js +29 -0
- package/dist/memory/extractor.d.ts +13 -0
- package/dist/memory/extractor.js +82 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/session-learning.d.ts +23 -0
- package/dist/memory/session-learning.js +55 -0
- package/dist/memory/store.d.ts +36 -0
- package/dist/memory/store.js +334 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +28 -0
- package/dist/paths.js +48 -0
- package/dist/prompt-templates/builtins/index.d.ts +4 -0
- package/dist/prompt-templates/builtins/index.js +5 -0
- package/dist/prompt-templates/builtins/planner.d.ts +4 -0
- package/dist/prompt-templates/builtins/planner.js +77 -0
- package/dist/prompt-templates/store.d.ts +45 -0
- package/dist/prompt-templates/store.js +224 -0
- package/dist/prompt-templates/types.d.ts +10 -0
- package/dist/prompt-templates/types.js +1 -0
- package/dist/scheduler/connected-channels.d.ts +24 -0
- package/dist/scheduler/connected-channels.js +57 -0
- package/dist/scheduler/scheduler.d.ts +22 -0
- package/dist/scheduler/scheduler.js +132 -0
- package/dist/scheduler/store.d.ts +27 -0
- package/dist/scheduler/store.js +205 -0
- package/dist/scheduler/types.d.ts +29 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/security/command-validator.d.ts +22 -0
- package/dist/security/command-validator.js +160 -0
- package/dist/security/docker-sandbox.d.ts +48 -0
- package/dist/security/docker-sandbox.js +218 -0
- package/dist/security/env-filter.d.ts +8 -0
- package/dist/security/env-filter.js +41 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/prompt.d.ts +6 -0
- package/dist/skills/prompt.js +17 -0
- package/dist/skills/read-tool.d.ts +7 -0
- package/dist/skills/read-tool.js +24 -0
- package/dist/skills/scanner.d.ts +6 -0
- package/dist/skills/scanner.js +73 -0
- package/dist/skills/types.d.ts +15 -0
- package/dist/skills/types.js +1 -0
- package/dist/tasks/inline-attachment-content.d.ts +9 -0
- package/dist/tasks/inline-attachment-content.js +64 -0
- package/dist/tasks/store.d.ts +112 -0
- package/dist/tasks/store.js +519 -0
- package/dist/tasks/types.d.ts +129 -0
- package/dist/tasks/types.js +80 -0
- package/dist/teams/status-config.d.ts +8 -0
- package/dist/teams/status-config.js +40 -0
- package/dist/teams/store.d.ts +111 -0
- package/dist/teams/store.js +671 -0
- package/dist/teams/types.d.ts +30 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +18 -0
- package/dist/tools/bash.js +64 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +20 -0
- package/dist/tools/delegate.js +299 -0
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +335 -0
- package/dist/tools/integration-toggle.d.ts +14 -0
- package/dist/tools/integration-toggle.js +47 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/memory.js +59 -0
- package/dist/tools/prompt-templates.d.ts +7 -0
- package/dist/tools/prompt-templates.js +133 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +9 -0
- package/dist/tools/schedule.d.ts +8 -0
- package/dist/tools/schedule.js +219 -0
- package/dist/tools/speak.d.ts +10 -0
- package/dist/tools/speak.js +56 -0
- package/dist/tools/tasks.d.ts +67 -0
- package/dist/tools/tasks.js +288 -0
- package/dist/tools/teams.d.ts +22 -0
- package/dist/tools/teams.js +470 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.js +22 -0
- package/dist/tts/edge.d.ts +10 -0
- package/dist/tts/edge.js +60 -0
- package/dist/tts/speak.d.ts +12 -0
- package/dist/tts/speak.js +81 -0
- package/dist/tts/transcribe.d.ts +5 -0
- package/dist/tts/transcribe.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +22 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +13 -0
- package/package.json +102 -0
- package/verybot.js +2 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { emit } from "../../events.js";
|
|
4
|
+
import { PLAYBOOK_DIR, PLAYBOOKS_DIR } from "../../paths.js";
|
|
5
|
+
const INDEX_FILENAME = "index.yaml";
|
|
6
|
+
const README_FILENAME = "README.md";
|
|
7
|
+
const SCRIPTS_DIRNAME = "scripts";
|
|
8
|
+
const DEFAULT_README = "# New Playbook\n\n## When to use\n- TODO\n\n## Steps\n- TODO\n";
|
|
9
|
+
const PLAYBOOK_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9 _-]{0,63}$/;
|
|
10
|
+
function normalizeScriptPath(rawPath) {
|
|
11
|
+
if (typeof rawPath !== "string")
|
|
12
|
+
return null;
|
|
13
|
+
const normalized = rawPath.trim().replace(/\\/g, "/");
|
|
14
|
+
if (!normalized)
|
|
15
|
+
return null;
|
|
16
|
+
if (normalized.startsWith("/"))
|
|
17
|
+
return null;
|
|
18
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
19
|
+
if (segments.length === 0)
|
|
20
|
+
return null;
|
|
21
|
+
if (segments.some((segment) => segment === "." || segment === ".."))
|
|
22
|
+
return null;
|
|
23
|
+
return segments.join("/");
|
|
24
|
+
}
|
|
25
|
+
function normalizeScriptCodeFiles(value) {
|
|
26
|
+
if (!Array.isArray(value))
|
|
27
|
+
return [];
|
|
28
|
+
const files = [];
|
|
29
|
+
const seenPaths = new Set();
|
|
30
|
+
for (const item of value) {
|
|
31
|
+
if (!item || typeof item !== "object")
|
|
32
|
+
continue;
|
|
33
|
+
const candidate = item;
|
|
34
|
+
const path = normalizeScriptPath(candidate.path);
|
|
35
|
+
if (!path || seenPaths.has(path))
|
|
36
|
+
continue;
|
|
37
|
+
const content = typeof candidate.content === "string" ? candidate.content : "";
|
|
38
|
+
files.push({ path, content });
|
|
39
|
+
seenPaths.add(path);
|
|
40
|
+
}
|
|
41
|
+
return files;
|
|
42
|
+
}
|
|
43
|
+
function getIndexPath() {
|
|
44
|
+
return join(PLAYBOOK_DIR, INDEX_FILENAME);
|
|
45
|
+
}
|
|
46
|
+
function sanitizePlaybookName(rawName) {
|
|
47
|
+
if (typeof rawName !== "string")
|
|
48
|
+
throw new Error("playbook name is required");
|
|
49
|
+
const trimmed = rawName.trim();
|
|
50
|
+
const sanitized = basename(trimmed);
|
|
51
|
+
if (!sanitized || sanitized === "." || sanitized === "..") {
|
|
52
|
+
throw new Error("Invalid playbook name");
|
|
53
|
+
}
|
|
54
|
+
if (!PLAYBOOK_NAME_PATTERN.test(sanitized)) {
|
|
55
|
+
throw new Error("Playbook name must be alphanumeric (spaces/hyphens/underscores allowed, max 64 chars)");
|
|
56
|
+
}
|
|
57
|
+
return sanitized;
|
|
58
|
+
}
|
|
59
|
+
function resolvePlaybookDir(name) {
|
|
60
|
+
const target = resolve(PLAYBOOKS_DIR, name);
|
|
61
|
+
const parent = resolve(PLAYBOOKS_DIR);
|
|
62
|
+
if (!target.startsWith(`${parent}/`)) {
|
|
63
|
+
throw new Error("Invalid playbook path");
|
|
64
|
+
}
|
|
65
|
+
return target;
|
|
66
|
+
}
|
|
67
|
+
function isNonEmptyArray(value) {
|
|
68
|
+
return Array.isArray(value) && value.length > 0;
|
|
69
|
+
}
|
|
70
|
+
function normalizeStringArray(value) {
|
|
71
|
+
if (!Array.isArray(value))
|
|
72
|
+
return [];
|
|
73
|
+
const result = [];
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
for (const item of value) {
|
|
76
|
+
if (typeof item !== "string")
|
|
77
|
+
continue;
|
|
78
|
+
const trimmed = item.trim();
|
|
79
|
+
if (!trimmed)
|
|
80
|
+
continue;
|
|
81
|
+
const key = trimmed.toLowerCase();
|
|
82
|
+
if (seen.has(key))
|
|
83
|
+
continue;
|
|
84
|
+
seen.add(key);
|
|
85
|
+
result.push(trimmed);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
function parseYamlScalar(value) {
|
|
90
|
+
const trimmed = value.trim();
|
|
91
|
+
if (!trimmed)
|
|
92
|
+
return "";
|
|
93
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) {
|
|
94
|
+
return trimmed
|
|
95
|
+
.slice(1, -1)
|
|
96
|
+
.replace(/\\n/g, "\n")
|
|
97
|
+
.replace(/\\"/g, '"')
|
|
98
|
+
.replace(/\\\\/g, "\\");
|
|
99
|
+
}
|
|
100
|
+
if (trimmed.startsWith("'") && trimmed.endsWith("'") && trimmed.length >= 2) {
|
|
101
|
+
return trimmed.slice(1, -1).replace(/''/g, "'");
|
|
102
|
+
}
|
|
103
|
+
return trimmed;
|
|
104
|
+
}
|
|
105
|
+
function parseInlineYamlList(value) {
|
|
106
|
+
const trimmed = value.trim();
|
|
107
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]"))
|
|
108
|
+
return [];
|
|
109
|
+
const body = trimmed.slice(1, -1).trim();
|
|
110
|
+
if (!body)
|
|
111
|
+
return [];
|
|
112
|
+
return body
|
|
113
|
+
.split(",")
|
|
114
|
+
.map((item) => parseYamlScalar(item))
|
|
115
|
+
.map((item) => item.trim())
|
|
116
|
+
.filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
function lineIndent(line) {
|
|
119
|
+
return line.length - line.trimStart().length;
|
|
120
|
+
}
|
|
121
|
+
function parseYamlBlockList(lines, startIndex, parentIndent) {
|
|
122
|
+
const values = [];
|
|
123
|
+
let idx = startIndex;
|
|
124
|
+
while (idx < lines.length) {
|
|
125
|
+
const line = lines[idx];
|
|
126
|
+
if (!line.trim()) {
|
|
127
|
+
idx++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const indent = lineIndent(line);
|
|
131
|
+
if (indent <= parentIndent)
|
|
132
|
+
break;
|
|
133
|
+
const itemMatch = line.match(/^\s*-\s*(.*)$/);
|
|
134
|
+
if (!itemMatch)
|
|
135
|
+
break;
|
|
136
|
+
const parsed = parseYamlScalar(itemMatch[1]);
|
|
137
|
+
if (parsed)
|
|
138
|
+
values.push(parsed);
|
|
139
|
+
idx++;
|
|
140
|
+
}
|
|
141
|
+
return { values, nextIndex: idx };
|
|
142
|
+
}
|
|
143
|
+
function parseIndexYaml(raw) {
|
|
144
|
+
const lines = raw.replace(/\r\n/g, "\n").split("\n");
|
|
145
|
+
const entries = [];
|
|
146
|
+
let idx = 0;
|
|
147
|
+
while (idx < lines.length) {
|
|
148
|
+
const line = lines[idx];
|
|
149
|
+
const nameMatch = line.match(/^\s*-\s*name:\s*(.*)$/);
|
|
150
|
+
if (!nameMatch) {
|
|
151
|
+
idx++;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const name = parseYamlScalar(nameMatch[1]);
|
|
155
|
+
const entry = {
|
|
156
|
+
name,
|
|
157
|
+
description: "",
|
|
158
|
+
triggers: [],
|
|
159
|
+
tags: [],
|
|
160
|
+
};
|
|
161
|
+
idx++;
|
|
162
|
+
while (idx < lines.length) {
|
|
163
|
+
const current = lines[idx];
|
|
164
|
+
if (/^\s*-\s*name:\s*/.test(current))
|
|
165
|
+
break;
|
|
166
|
+
const descriptionMatch = current.match(/^\s*description:\s*(.*)$/);
|
|
167
|
+
if (descriptionMatch) {
|
|
168
|
+
entry.description = parseYamlScalar(descriptionMatch[1]);
|
|
169
|
+
idx++;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const triggersMatch = current.match(/^\s*triggers:\s*(.*)$/);
|
|
173
|
+
if (triggersMatch) {
|
|
174
|
+
const inline = triggersMatch[1].trim();
|
|
175
|
+
if (inline) {
|
|
176
|
+
entry.triggers = parseInlineYamlList(inline);
|
|
177
|
+
idx++;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const parsed = parseYamlBlockList(lines, idx + 1, lineIndent(current));
|
|
181
|
+
entry.triggers = parsed.values;
|
|
182
|
+
idx = parsed.nextIndex;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const tagsMatch = current.match(/^\s*tags:\s*(.*)$/);
|
|
186
|
+
if (tagsMatch) {
|
|
187
|
+
const inline = tagsMatch[1].trim();
|
|
188
|
+
if (inline) {
|
|
189
|
+
entry.tags = parseInlineYamlList(inline);
|
|
190
|
+
idx++;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const parsed = parseYamlBlockList(lines, idx + 1, lineIndent(current));
|
|
194
|
+
entry.tags = parsed.values;
|
|
195
|
+
idx = parsed.nextIndex;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
idx++;
|
|
199
|
+
}
|
|
200
|
+
if (entry.name) {
|
|
201
|
+
entries.push({
|
|
202
|
+
name: entry.name,
|
|
203
|
+
description: entry.description,
|
|
204
|
+
triggers: normalizeStringArray(entry.triggers),
|
|
205
|
+
tags: normalizeStringArray(entry.tags),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return entries;
|
|
210
|
+
}
|
|
211
|
+
function yamlEscape(value) {
|
|
212
|
+
if (/[:\n\r#"'\[\]{}|>&*!%@`]/.test(value) || value.trim() !== value) {
|
|
213
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
|
|
214
|
+
}
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
function serializeIndexYaml(entries) {
|
|
218
|
+
if (entries.length === 0)
|
|
219
|
+
return "playbooks: []\n";
|
|
220
|
+
const lines = ["playbooks:"];
|
|
221
|
+
for (const entry of entries) {
|
|
222
|
+
lines.push(` - name: ${yamlEscape(entry.name)}`);
|
|
223
|
+
lines.push(` description: ${yamlEscape(entry.description)}`);
|
|
224
|
+
if (isNonEmptyArray(entry.triggers)) {
|
|
225
|
+
lines.push(" triggers:");
|
|
226
|
+
for (const trigger of entry.triggers) {
|
|
227
|
+
lines.push(` - ${yamlEscape(trigger)}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
lines.push(" triggers: []");
|
|
232
|
+
}
|
|
233
|
+
if (isNonEmptyArray(entry.tags)) {
|
|
234
|
+
lines.push(" tags:");
|
|
235
|
+
for (const tag of entry.tags) {
|
|
236
|
+
lines.push(` - ${yamlEscape(tag)}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
lines.push(" tags: []");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return `${lines.join("\n")}\n`;
|
|
244
|
+
}
|
|
245
|
+
async function exists(path) {
|
|
246
|
+
try {
|
|
247
|
+
await stat(path);
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async function readIndexEntries() {
|
|
255
|
+
const indexPath = getIndexPath();
|
|
256
|
+
const raw = await readFile(indexPath, "utf-8").catch(() => "");
|
|
257
|
+
if (!raw.trim())
|
|
258
|
+
return [];
|
|
259
|
+
return parseIndexYaml(raw);
|
|
260
|
+
}
|
|
261
|
+
async function writeIndexEntries(entries) {
|
|
262
|
+
await mkdir(PLAYBOOK_DIR, { recursive: true });
|
|
263
|
+
const indexPath = getIndexPath();
|
|
264
|
+
await writeFile(indexPath, serializeIndexYaml(entries), "utf-8");
|
|
265
|
+
}
|
|
266
|
+
async function listFilesRecursive(root) {
|
|
267
|
+
const files = [];
|
|
268
|
+
async function walk(current) {
|
|
269
|
+
const entries = await readdir(current, { withFileTypes: true }).catch(() => []);
|
|
270
|
+
for (const entry of entries) {
|
|
271
|
+
const abs = join(current, entry.name);
|
|
272
|
+
if (entry.isDirectory()) {
|
|
273
|
+
await walk(abs);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (!entry.isFile())
|
|
277
|
+
continue;
|
|
278
|
+
const relPath = relative(root, abs).replace(/\\/g, "/");
|
|
279
|
+
files.push(relPath);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (await exists(root)) {
|
|
283
|
+
await walk(root);
|
|
284
|
+
}
|
|
285
|
+
return files.sort((a, b) => a.localeCompare(b));
|
|
286
|
+
}
|
|
287
|
+
async function readDiskPlaybook(name) {
|
|
288
|
+
const dir = resolvePlaybookDir(name);
|
|
289
|
+
if (!(await exists(dir)))
|
|
290
|
+
return null;
|
|
291
|
+
const readmePath = join(dir, README_FILENAME);
|
|
292
|
+
const scriptsDir = join(dir, SCRIPTS_DIRNAME);
|
|
293
|
+
const readmeExists = await exists(readmePath);
|
|
294
|
+
const readme = readmeExists ? await readFile(readmePath, "utf-8") : "";
|
|
295
|
+
const scriptFiles = await listFilesRecursive(scriptsDir);
|
|
296
|
+
const scriptCodeFiles = await Promise.all(scriptFiles.map(async (path) => {
|
|
297
|
+
const content = await readFile(join(scriptsDir, path), "utf-8").catch(() => "");
|
|
298
|
+
return { path, content };
|
|
299
|
+
}));
|
|
300
|
+
return { readmeExists, readme, scriptFiles, scriptCodeFiles };
|
|
301
|
+
}
|
|
302
|
+
function findIndexEntry(entries, name) {
|
|
303
|
+
return entries.find((entry) => entry.name === name);
|
|
304
|
+
}
|
|
305
|
+
function normalizeIndexEntry(params) {
|
|
306
|
+
return {
|
|
307
|
+
name: params.name,
|
|
308
|
+
description: typeof params.description === "string" ? params.description.trim() : "",
|
|
309
|
+
triggers: normalizeStringArray(params.triggers),
|
|
310
|
+
tags: normalizeStringArray(params.tags),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
async function listDiskPlaybookNames() {
|
|
314
|
+
const entries = await readdir(PLAYBOOKS_DIR, { withFileTypes: true }).catch(() => []);
|
|
315
|
+
return entries
|
|
316
|
+
.filter((entry) => entry.isDirectory())
|
|
317
|
+
.map((entry) => entry.name)
|
|
318
|
+
.sort((a, b) => a.localeCompare(b));
|
|
319
|
+
}
|
|
320
|
+
export function playbookMethods() {
|
|
321
|
+
return {
|
|
322
|
+
"playbooks.list": async () => {
|
|
323
|
+
const indexEntries = await readIndexEntries();
|
|
324
|
+
const indexMap = new Map(indexEntries.map((entry) => [entry.name, entry]));
|
|
325
|
+
const diskNames = await listDiskPlaybookNames();
|
|
326
|
+
const names = new Set([...indexMap.keys(), ...diskNames]);
|
|
327
|
+
const sortedNames = Array.from(names).sort((a, b) => a.localeCompare(b));
|
|
328
|
+
const playbooks = await Promise.all(sortedNames.map(async (name) => {
|
|
329
|
+
const indexEntry = indexMap.get(name);
|
|
330
|
+
const diskEntry = await readDiskPlaybook(name);
|
|
331
|
+
return {
|
|
332
|
+
name,
|
|
333
|
+
description: indexEntry?.description ?? "",
|
|
334
|
+
triggers: indexEntry?.triggers ?? [],
|
|
335
|
+
tags: indexEntry?.tags ?? [],
|
|
336
|
+
inIndex: Boolean(indexEntry),
|
|
337
|
+
onDisk: Boolean(diskEntry),
|
|
338
|
+
readmeExists: diskEntry?.readmeExists ?? false,
|
|
339
|
+
scriptCount: diskEntry?.scriptFiles.length ?? 0,
|
|
340
|
+
};
|
|
341
|
+
}));
|
|
342
|
+
return { playbooks };
|
|
343
|
+
},
|
|
344
|
+
"playbooks.get": async (params) => {
|
|
345
|
+
const payload = (params ?? {});
|
|
346
|
+
const name = sanitizePlaybookName(payload.name);
|
|
347
|
+
const indexEntries = await readIndexEntries();
|
|
348
|
+
const indexEntry = findIndexEntry(indexEntries, name);
|
|
349
|
+
const diskEntry = await readDiskPlaybook(name);
|
|
350
|
+
if (!indexEntry && !diskEntry) {
|
|
351
|
+
throw new Error(`Playbook not found: ${name}`);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
playbook: {
|
|
355
|
+
name,
|
|
356
|
+
description: indexEntry?.description ?? "",
|
|
357
|
+
triggers: indexEntry?.triggers ?? [],
|
|
358
|
+
tags: indexEntry?.tags ?? [],
|
|
359
|
+
inIndex: Boolean(indexEntry),
|
|
360
|
+
onDisk: Boolean(diskEntry),
|
|
361
|
+
readmeExists: diskEntry?.readmeExists ?? false,
|
|
362
|
+
readme: diskEntry?.readme ?? "",
|
|
363
|
+
scriptFiles: diskEntry?.scriptFiles ?? [],
|
|
364
|
+
scriptCodeFiles: diskEntry?.scriptCodeFiles ?? [],
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
},
|
|
368
|
+
"playbooks.create": async (params) => {
|
|
369
|
+
const payload = (params ?? {});
|
|
370
|
+
const name = sanitizePlaybookName(payload.name);
|
|
371
|
+
const readme = typeof payload.readme === "string" ? payload.readme : DEFAULT_README;
|
|
372
|
+
const indexEntries = await readIndexEntries();
|
|
373
|
+
if (findIndexEntry(indexEntries, name)) {
|
|
374
|
+
throw new Error(`Playbook already exists in index: ${name}`);
|
|
375
|
+
}
|
|
376
|
+
const playbookDir = resolvePlaybookDir(name);
|
|
377
|
+
if (await exists(playbookDir)) {
|
|
378
|
+
throw new Error(`Playbook directory already exists: ${name}`);
|
|
379
|
+
}
|
|
380
|
+
const normalized = normalizeIndexEntry({
|
|
381
|
+
name,
|
|
382
|
+
description: payload.description,
|
|
383
|
+
triggers: payload.triggers,
|
|
384
|
+
tags: payload.tags,
|
|
385
|
+
});
|
|
386
|
+
const nextEntries = [...indexEntries, normalized].sort((a, b) => a.name.localeCompare(b.name));
|
|
387
|
+
await mkdir(playbookDir, { recursive: true });
|
|
388
|
+
await mkdir(join(playbookDir, SCRIPTS_DIRNAME), { recursive: true });
|
|
389
|
+
await writeFile(join(playbookDir, README_FILENAME), readme, "utf-8");
|
|
390
|
+
await writeIndexEntries(nextEntries);
|
|
391
|
+
emit("playbookChange", { action: "created", name });
|
|
392
|
+
return { status: "ok" };
|
|
393
|
+
},
|
|
394
|
+
"playbooks.update": async (params) => {
|
|
395
|
+
const payload = (params ?? {});
|
|
396
|
+
const name = sanitizePlaybookName(payload.name);
|
|
397
|
+
const hasReadme = typeof payload.readme === "string";
|
|
398
|
+
const readme = hasReadme ? payload.readme : null;
|
|
399
|
+
const scriptCodeFiles = normalizeScriptCodeFiles(payload.scriptCodeFiles);
|
|
400
|
+
const indexEntries = await readIndexEntries();
|
|
401
|
+
const normalized = normalizeIndexEntry({
|
|
402
|
+
name,
|
|
403
|
+
description: payload.description,
|
|
404
|
+
triggers: payload.triggers,
|
|
405
|
+
tags: payload.tags,
|
|
406
|
+
});
|
|
407
|
+
const existingIndex = findIndexEntry(indexEntries, name);
|
|
408
|
+
const nextEntries = existingIndex
|
|
409
|
+
? indexEntries.map((entry) => (entry.name === name ? normalized : entry))
|
|
410
|
+
: [...indexEntries, normalized].sort((a, b) => a.name.localeCompare(b.name));
|
|
411
|
+
const playbookDir = resolvePlaybookDir(name);
|
|
412
|
+
await mkdir(playbookDir, { recursive: true });
|
|
413
|
+
const scriptsDir = join(playbookDir, SCRIPTS_DIRNAME);
|
|
414
|
+
await mkdir(scriptsDir, { recursive: true });
|
|
415
|
+
const readmePath = join(playbookDir, README_FILENAME);
|
|
416
|
+
if (readme !== null) {
|
|
417
|
+
await writeFile(readmePath, readme, "utf-8");
|
|
418
|
+
}
|
|
419
|
+
else if (!(await exists(readmePath))) {
|
|
420
|
+
await writeFile(readmePath, DEFAULT_README, "utf-8");
|
|
421
|
+
}
|
|
422
|
+
for (const scriptFile of scriptCodeFiles) {
|
|
423
|
+
const targetScriptPath = resolve(scriptsDir, scriptFile.path);
|
|
424
|
+
if (!targetScriptPath.startsWith(`${resolve(scriptsDir)}/`)) {
|
|
425
|
+
throw new Error(`Invalid script path: ${scriptFile.path}`);
|
|
426
|
+
}
|
|
427
|
+
await mkdir(dirname(targetScriptPath), { recursive: true });
|
|
428
|
+
await writeFile(targetScriptPath, scriptFile.content, "utf-8");
|
|
429
|
+
}
|
|
430
|
+
await writeIndexEntries(nextEntries);
|
|
431
|
+
emit("playbookChange", { action: "updated", name });
|
|
432
|
+
return { status: "ok" };
|
|
433
|
+
},
|
|
434
|
+
"playbooks.rename": async (params) => {
|
|
435
|
+
const payload = (params ?? {});
|
|
436
|
+
const name = sanitizePlaybookName(payload.name);
|
|
437
|
+
const newName = sanitizePlaybookName(payload.newName);
|
|
438
|
+
if (name === newName)
|
|
439
|
+
return { status: "ok" };
|
|
440
|
+
const indexEntries = await readIndexEntries();
|
|
441
|
+
const oldEntry = findIndexEntry(indexEntries, name);
|
|
442
|
+
const newEntry = findIndexEntry(indexEntries, newName);
|
|
443
|
+
if (newEntry)
|
|
444
|
+
throw new Error(`Playbook already exists: ${newName}`);
|
|
445
|
+
const oldDir = resolvePlaybookDir(name);
|
|
446
|
+
const newDir = resolvePlaybookDir(newName);
|
|
447
|
+
const oldDirExists = await exists(oldDir);
|
|
448
|
+
const newDirExists = await exists(newDir);
|
|
449
|
+
if (!oldEntry && !oldDirExists) {
|
|
450
|
+
throw new Error(`Playbook not found: ${name}`);
|
|
451
|
+
}
|
|
452
|
+
if (newDirExists) {
|
|
453
|
+
throw new Error(`Playbook directory already exists: ${newName}`);
|
|
454
|
+
}
|
|
455
|
+
if (oldDirExists) {
|
|
456
|
+
await mkdir(PLAYBOOKS_DIR, { recursive: true });
|
|
457
|
+
await rename(oldDir, newDir);
|
|
458
|
+
}
|
|
459
|
+
const nextEntries = oldEntry
|
|
460
|
+
? indexEntries
|
|
461
|
+
.map((entry) => (entry.name === name ? { ...entry, name: newName } : entry))
|
|
462
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
463
|
+
: [...indexEntries, { name: newName, description: "", triggers: [], tags: [] }]
|
|
464
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
465
|
+
await writeIndexEntries(nextEntries);
|
|
466
|
+
emit("playbookChange", { action: "renamed", name, newName });
|
|
467
|
+
return { status: "ok" };
|
|
468
|
+
},
|
|
469
|
+
"playbooks.delete": async (params) => {
|
|
470
|
+
const payload = (params ?? {});
|
|
471
|
+
const name = sanitizePlaybookName(payload.name);
|
|
472
|
+
const indexEntries = await readIndexEntries();
|
|
473
|
+
const nextEntries = indexEntries.filter((entry) => entry.name !== name);
|
|
474
|
+
const hadIndexEntry = nextEntries.length !== indexEntries.length;
|
|
475
|
+
const playbookDir = resolvePlaybookDir(name);
|
|
476
|
+
const hadDirectory = await exists(playbookDir);
|
|
477
|
+
if (!hadIndexEntry && !hadDirectory) {
|
|
478
|
+
throw new Error(`Playbook not found: ${name}`);
|
|
479
|
+
}
|
|
480
|
+
if (hadDirectory) {
|
|
481
|
+
await rm(playbookDir, { recursive: true, force: true });
|
|
482
|
+
}
|
|
483
|
+
await writeIndexEntries(nextEntries);
|
|
484
|
+
emit("playbookChange", { action: "deleted", name });
|
|
485
|
+
return { status: "ok" };
|
|
486
|
+
},
|
|
487
|
+
};
|
|
488
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PromptTemplateStore } from "../../prompt-templates/store.js";
|
|
2
|
+
export declare function promptTemplateMethods(promptTemplateStore: PromptTemplateStore): {
|
|
3
|
+
/** List all prompt templates. */
|
|
4
|
+
"promptTemplates.list": () => Promise<{
|
|
5
|
+
promptTemplates: import("../../prompt-templates/types.js").PromptTemplate[];
|
|
6
|
+
}>;
|
|
7
|
+
/** Get a single prompt template by id. */
|
|
8
|
+
"promptTemplates.get": (params: unknown) => Promise<{
|
|
9
|
+
promptTemplate: import("../../prompt-templates/types.js").PromptTemplate;
|
|
10
|
+
}>;
|
|
11
|
+
/** Create a new prompt template. */
|
|
12
|
+
"promptTemplates.create": (params: unknown) => Promise<{
|
|
13
|
+
promptTemplate: import("../../prompt-templates/types.js").PromptTemplate;
|
|
14
|
+
}>;
|
|
15
|
+
/** Fork an existing template into a user-editable copy. */
|
|
16
|
+
"promptTemplates.fork": (params: unknown) => Promise<{
|
|
17
|
+
promptTemplate: import("../../prompt-templates/types.js").PromptTemplate;
|
|
18
|
+
}>;
|
|
19
|
+
/** Update an existing prompt template (rejects builtins). */
|
|
20
|
+
"promptTemplates.update": (params: unknown) => Promise<{
|
|
21
|
+
promptTemplate: import("../../prompt-templates/types.js").PromptTemplate;
|
|
22
|
+
}>;
|
|
23
|
+
/** Delete a prompt template (rejects builtins). */
|
|
24
|
+
"promptTemplates.delete": (params: unknown) => Promise<{
|
|
25
|
+
status: string;
|
|
26
|
+
}>;
|
|
27
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { emit } from "../../events.js";
|
|
2
|
+
import { MAX_TEMPLATE_NAME_LENGTH, MAX_TEMPLATE_CONTENT_LENGTH } from "../../prompt-templates/store.js";
|
|
3
|
+
const VALID_ROLES = new Set(["orchestrator", "worker"]);
|
|
4
|
+
export function promptTemplateMethods(promptTemplateStore) {
|
|
5
|
+
return {
|
|
6
|
+
/** List all prompt templates. */
|
|
7
|
+
"promptTemplates.list": async () => {
|
|
8
|
+
return { promptTemplates: promptTemplateStore.listPromptTemplates() };
|
|
9
|
+
},
|
|
10
|
+
/** Get a single prompt template by id. */
|
|
11
|
+
"promptTemplates.get": async (params) => {
|
|
12
|
+
const { id } = (params ?? {});
|
|
13
|
+
if (!id || typeof id !== "string")
|
|
14
|
+
throw new Error("id is required");
|
|
15
|
+
const promptTemplate = promptTemplateStore.getPromptTemplateById(id);
|
|
16
|
+
if (!promptTemplate)
|
|
17
|
+
throw new Error(`Template not found: ${id}`);
|
|
18
|
+
return { promptTemplate };
|
|
19
|
+
},
|
|
20
|
+
/** Create a new prompt template. */
|
|
21
|
+
"promptTemplates.create": async (params) => {
|
|
22
|
+
const p = (params ?? {});
|
|
23
|
+
const name = p.name;
|
|
24
|
+
const role = p.role;
|
|
25
|
+
const content = p.content;
|
|
26
|
+
if (!name || typeof name !== "string")
|
|
27
|
+
throw new Error("name is required");
|
|
28
|
+
if (name.trim().length === 0)
|
|
29
|
+
throw new Error("name cannot be empty");
|
|
30
|
+
if (name.length > MAX_TEMPLATE_NAME_LENGTH)
|
|
31
|
+
throw new Error(`name exceeds maximum length of ${MAX_TEMPLATE_NAME_LENGTH}`);
|
|
32
|
+
if (!role || !VALID_ROLES.has(role))
|
|
33
|
+
throw new Error("role must be 'orchestrator' or 'worker'");
|
|
34
|
+
if (typeof content !== "string")
|
|
35
|
+
throw new Error("content is required");
|
|
36
|
+
if (content.length > MAX_TEMPLATE_CONTENT_LENGTH)
|
|
37
|
+
throw new Error(`content exceeds maximum length of ${MAX_TEMPLATE_CONTENT_LENGTH}`);
|
|
38
|
+
const promptTemplate = promptTemplateStore.createPromptTemplate({
|
|
39
|
+
name: name.trim(),
|
|
40
|
+
description: typeof p.description === "string" ? p.description : "",
|
|
41
|
+
role: role,
|
|
42
|
+
content,
|
|
43
|
+
});
|
|
44
|
+
emit("promptTemplateChange", { action: "created", promptTemplate });
|
|
45
|
+
return { promptTemplate };
|
|
46
|
+
},
|
|
47
|
+
/** Fork an existing template into a user-editable copy. */
|
|
48
|
+
"promptTemplates.fork": async (params) => {
|
|
49
|
+
const p = (params ?? {});
|
|
50
|
+
const id = p.id;
|
|
51
|
+
if (!id || typeof id !== "string")
|
|
52
|
+
throw new Error("id is required");
|
|
53
|
+
if (p.name !== undefined && (typeof p.name !== "string" || p.name.trim().length === 0)) {
|
|
54
|
+
throw new Error("name cannot be empty");
|
|
55
|
+
}
|
|
56
|
+
if (typeof p.name === "string" && p.name.length > MAX_TEMPLATE_NAME_LENGTH) {
|
|
57
|
+
throw new Error(`name exceeds maximum length of ${MAX_TEMPLATE_NAME_LENGTH}`);
|
|
58
|
+
}
|
|
59
|
+
if (p.description !== undefined && typeof p.description !== "string") {
|
|
60
|
+
throw new Error("description must be a string");
|
|
61
|
+
}
|
|
62
|
+
const promptTemplate = promptTemplateStore.forkPromptTemplate(id, {
|
|
63
|
+
name: typeof p.name === "string" ? p.name.trim() : undefined,
|
|
64
|
+
description: typeof p.description === "string" ? p.description : undefined,
|
|
65
|
+
});
|
|
66
|
+
if (!promptTemplate)
|
|
67
|
+
throw new Error(`Template not found: ${id}`);
|
|
68
|
+
emit("promptTemplateChange", { action: "created", promptTemplate });
|
|
69
|
+
return { promptTemplate };
|
|
70
|
+
},
|
|
71
|
+
/** Update an existing prompt template (rejects builtins). */
|
|
72
|
+
"promptTemplates.update": async (params) => {
|
|
73
|
+
const p = (params ?? {});
|
|
74
|
+
const id = p.id;
|
|
75
|
+
if (!id || typeof id !== "string")
|
|
76
|
+
throw new Error("id is required");
|
|
77
|
+
if (p.name !== undefined && (typeof p.name !== "string" || p.name.trim().length === 0)) {
|
|
78
|
+
throw new Error("name cannot be empty");
|
|
79
|
+
}
|
|
80
|
+
if (p.role !== undefined && !VALID_ROLES.has(p.role)) {
|
|
81
|
+
throw new Error("role must be 'orchestrator' or 'worker'");
|
|
82
|
+
}
|
|
83
|
+
const promptTemplate = promptTemplateStore.updatePromptTemplate(id, {
|
|
84
|
+
name: typeof p.name === "string" ? p.name.trim() : undefined,
|
|
85
|
+
description: typeof p.description === "string" ? p.description : undefined,
|
|
86
|
+
role: VALID_ROLES.has(p.role) ? p.role : undefined,
|
|
87
|
+
content: typeof p.content === "string" ? p.content : undefined,
|
|
88
|
+
});
|
|
89
|
+
if (!promptTemplate)
|
|
90
|
+
throw new Error(`Template not found: ${id}`);
|
|
91
|
+
emit("promptTemplateChange", { action: "updated", promptTemplate });
|
|
92
|
+
return { promptTemplate };
|
|
93
|
+
},
|
|
94
|
+
/** Delete a prompt template (rejects builtins). */
|
|
95
|
+
"promptTemplates.delete": async (params) => {
|
|
96
|
+
const { id } = (params ?? {});
|
|
97
|
+
if (!id || typeof id !== "string")
|
|
98
|
+
throw new Error("id is required");
|
|
99
|
+
const deleted = promptTemplateStore.deletePromptTemplate(id);
|
|
100
|
+
if (!deleted)
|
|
101
|
+
throw new Error(`Template not found: ${id}`);
|
|
102
|
+
emit("promptTemplateChange", { action: "deleted", id });
|
|
103
|
+
return { status: "ok" };
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Agent } from "../../brain/agent.js";
|
|
2
|
+
import type { ScheduleStore } from "../../scheduler/store.js";
|
|
3
|
+
import type { ConnectedChannelRegistry } from "../../scheduler/connected-channels.js";
|
|
4
|
+
import type { RpcContext } from "../rpc.js";
|
|
5
|
+
export declare function schedulerMethods(getAgent: () => Agent, getScheduleStore: () => ScheduleStore, getConnectedChannels: () => ConnectedChannelRegistry, ctx?: RpcContext): {
|
|
6
|
+
/** Connect a WS client to a team's scheduler. Returns session history + connection key. */
|
|
7
|
+
"scheduler.connect": (params: {
|
|
8
|
+
teamId: string;
|
|
9
|
+
}) => Promise<{
|
|
10
|
+
connectionKey: string;
|
|
11
|
+
messages: import("ai").ModelMessage[];
|
|
12
|
+
}>;
|
|
13
|
+
/** Disconnect a WS client from a team's scheduler. */
|
|
14
|
+
"scheduler.disconnect": (params: {
|
|
15
|
+
teamId: string;
|
|
16
|
+
connectionKey: string;
|
|
17
|
+
}) => Promise<{
|
|
18
|
+
status: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Send a message to the team's scheduler session. */
|
|
21
|
+
"scheduler.send": (params: {
|
|
22
|
+
teamId: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
status: string;
|
|
26
|
+
reply: string;
|
|
27
|
+
}>;
|
|
28
|
+
/** Get scheduler session history for a team. */
|
|
29
|
+
"scheduler.history": (params: {
|
|
30
|
+
teamId: string;
|
|
31
|
+
}) => Promise<{
|
|
32
|
+
messages: import("ai").ModelMessage[];
|
|
33
|
+
}>;
|
|
34
|
+
/** List schedules for a team. */
|
|
35
|
+
"scheduler.list": (params: {
|
|
36
|
+
teamId: string;
|
|
37
|
+
status?: string;
|
|
38
|
+
}) => Promise<{
|
|
39
|
+
schedules: import("../../scheduler/types.js").Schedule[];
|
|
40
|
+
}>;
|
|
41
|
+
/** Pause an active schedule. */
|
|
42
|
+
"scheduler.pause": (params: {
|
|
43
|
+
teamId: string;
|
|
44
|
+
scheduleId: string;
|
|
45
|
+
}) => Promise<{
|
|
46
|
+
status: string;
|
|
47
|
+
}>;
|
|
48
|
+
/** Resume a paused schedule. */
|
|
49
|
+
"scheduler.resume": (params: {
|
|
50
|
+
teamId: string;
|
|
51
|
+
scheduleId: string;
|
|
52
|
+
}) => Promise<{
|
|
53
|
+
status: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Delete a schedule. */
|
|
56
|
+
"scheduler.delete": (params: {
|
|
57
|
+
teamId: string;
|
|
58
|
+
scheduleId: string;
|
|
59
|
+
}) => Promise<{
|
|
60
|
+
status: string;
|
|
61
|
+
}>;
|
|
62
|
+
};
|