clementine-agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +44 -0
- package/LICENSE +21 -0
- package/README.md +795 -0
- package/dist/agent/agent-manager.d.ts +69 -0
- package/dist/agent/agent-manager.js +441 -0
- package/dist/agent/assistant.d.ts +225 -0
- package/dist/agent/assistant.js +3888 -0
- package/dist/agent/auto-update.d.ts +32 -0
- package/dist/agent/auto-update.js +186 -0
- package/dist/agent/daily-planner.d.ts +24 -0
- package/dist/agent/daily-planner.js +379 -0
- package/dist/agent/execution-advisor.d.ts +10 -0
- package/dist/agent/execution-advisor.js +272 -0
- package/dist/agent/hooks.d.ts +45 -0
- package/dist/agent/hooks.js +564 -0
- package/dist/agent/insight-engine.d.ts +66 -0
- package/dist/agent/insight-engine.js +225 -0
- package/dist/agent/intent-classifier.d.ts +48 -0
- package/dist/agent/intent-classifier.js +214 -0
- package/dist/agent/link-extractor.d.ts +19 -0
- package/dist/agent/link-extractor.js +90 -0
- package/dist/agent/mcp-bridge.d.ts +62 -0
- package/dist/agent/mcp-bridge.js +435 -0
- package/dist/agent/metacognition.d.ts +66 -0
- package/dist/agent/metacognition.js +221 -0
- package/dist/agent/orchestrator.d.ts +81 -0
- package/dist/agent/orchestrator.js +790 -0
- package/dist/agent/profiles.d.ts +22 -0
- package/dist/agent/profiles.js +91 -0
- package/dist/agent/prompt-cache.d.ts +24 -0
- package/dist/agent/prompt-cache.js +68 -0
- package/dist/agent/prompt-evolver.d.ts +28 -0
- package/dist/agent/prompt-evolver.js +279 -0
- package/dist/agent/role-scaffolds.d.ts +28 -0
- package/dist/agent/role-scaffolds.js +433 -0
- package/dist/agent/safe-restart.d.ts +41 -0
- package/dist/agent/safe-restart.js +150 -0
- package/dist/agent/self-improve.d.ts +66 -0
- package/dist/agent/self-improve.js +1706 -0
- package/dist/agent/session-event-log.d.ts +114 -0
- package/dist/agent/session-event-log.js +233 -0
- package/dist/agent/skill-extractor.d.ts +72 -0
- package/dist/agent/skill-extractor.js +435 -0
- package/dist/agent/source-mods.d.ts +61 -0
- package/dist/agent/source-mods.js +230 -0
- package/dist/agent/source-preflight.d.ts +25 -0
- package/dist/agent/source-preflight.js +100 -0
- package/dist/agent/stall-guard.d.ts +62 -0
- package/dist/agent/stall-guard.js +109 -0
- package/dist/agent/strategic-planner.d.ts +60 -0
- package/dist/agent/strategic-planner.js +352 -0
- package/dist/agent/team-bus.d.ts +89 -0
- package/dist/agent/team-bus.js +556 -0
- package/dist/agent/team-router.d.ts +26 -0
- package/dist/agent/team-router.js +37 -0
- package/dist/agent/tool-loop-detector.d.ts +59 -0
- package/dist/agent/tool-loop-detector.js +242 -0
- package/dist/agent/workflow-runner.d.ts +36 -0
- package/dist/agent/workflow-runner.js +317 -0
- package/dist/agent/workflow-variables.d.ts +16 -0
- package/dist/agent/workflow-variables.js +62 -0
- package/dist/channels/discord-agent-bot.d.ts +101 -0
- package/dist/channels/discord-agent-bot.js +881 -0
- package/dist/channels/discord-bot-manager.d.ts +80 -0
- package/dist/channels/discord-bot-manager.js +262 -0
- package/dist/channels/discord-utils.d.ts +51 -0
- package/dist/channels/discord-utils.js +293 -0
- package/dist/channels/discord.d.ts +12 -0
- package/dist/channels/discord.js +1832 -0
- package/dist/channels/slack-agent-bot.d.ts +73 -0
- package/dist/channels/slack-agent-bot.js +320 -0
- package/dist/channels/slack-bot-manager.d.ts +66 -0
- package/dist/channels/slack-bot-manager.js +236 -0
- package/dist/channels/slack-utils.d.ts +39 -0
- package/dist/channels/slack-utils.js +189 -0
- package/dist/channels/slack.d.ts +11 -0
- package/dist/channels/slack.js +196 -0
- package/dist/channels/telegram.d.ts +10 -0
- package/dist/channels/telegram.js +235 -0
- package/dist/channels/webhook.d.ts +9 -0
- package/dist/channels/webhook.js +78 -0
- package/dist/channels/whatsapp.d.ts +11 -0
- package/dist/channels/whatsapp.js +181 -0
- package/dist/cli/chat.d.ts +14 -0
- package/dist/cli/chat.js +220 -0
- package/dist/cli/cron.d.ts +17 -0
- package/dist/cli/cron.js +552 -0
- package/dist/cli/dashboard.d.ts +15 -0
- package/dist/cli/dashboard.js +17677 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +2474 -0
- package/dist/cli/routes/delegations.d.ts +19 -0
- package/dist/cli/routes/delegations.js +154 -0
- package/dist/cli/routes/digest.d.ts +17 -0
- package/dist/cli/routes/digest.js +375 -0
- package/dist/cli/routes/goals.d.ts +14 -0
- package/dist/cli/routes/goals.js +258 -0
- package/dist/cli/routes/workflows.d.ts +18 -0
- package/dist/cli/routes/workflows.js +97 -0
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +619 -0
- package/dist/cli/tunnel.d.ts +35 -0
- package/dist/cli/tunnel.js +141 -0
- package/dist/config.d.ts +145 -0
- package/dist/config.js +278 -0
- package/dist/events/bus.d.ts +43 -0
- package/dist/events/bus.js +136 -0
- package/dist/gateway/cron-scheduler.d.ts +166 -0
- package/dist/gateway/cron-scheduler.js +1767 -0
- package/dist/gateway/delivery-queue.d.ts +30 -0
- package/dist/gateway/delivery-queue.js +110 -0
- package/dist/gateway/heartbeat-scheduler.d.ts +99 -0
- package/dist/gateway/heartbeat-scheduler.js +1298 -0
- package/dist/gateway/heartbeat.d.ts +3 -0
- package/dist/gateway/heartbeat.js +3 -0
- package/dist/gateway/lanes.d.ts +24 -0
- package/dist/gateway/lanes.js +76 -0
- package/dist/gateway/notifications.d.ts +29 -0
- package/dist/gateway/notifications.js +75 -0
- package/dist/gateway/router.d.ts +210 -0
- package/dist/gateway/router.js +1330 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1015 -0
- package/dist/memory/chunker.d.ts +28 -0
- package/dist/memory/chunker.js +226 -0
- package/dist/memory/consolidation.d.ts +44 -0
- package/dist/memory/consolidation.js +171 -0
- package/dist/memory/context-assembler.d.ts +50 -0
- package/dist/memory/context-assembler.js +149 -0
- package/dist/memory/embeddings.d.ts +38 -0
- package/dist/memory/embeddings.js +180 -0
- package/dist/memory/graph-store.d.ts +66 -0
- package/dist/memory/graph-store.js +613 -0
- package/dist/memory/mmr.d.ts +21 -0
- package/dist/memory/mmr.js +75 -0
- package/dist/memory/search.d.ts +26 -0
- package/dist/memory/search.js +67 -0
- package/dist/memory/store.d.ts +530 -0
- package/dist/memory/store.js +2022 -0
- package/dist/security/integrity.d.ts +24 -0
- package/dist/security/integrity.js +58 -0
- package/dist/security/patterns.d.ts +34 -0
- package/dist/security/patterns.js +110 -0
- package/dist/security/scanner.d.ts +32 -0
- package/dist/security/scanner.js +263 -0
- package/dist/tools/admin-tools.d.ts +12 -0
- package/dist/tools/admin-tools.js +1278 -0
- package/dist/tools/external-tools.d.ts +11 -0
- package/dist/tools/external-tools.js +1327 -0
- package/dist/tools/goal-tools.d.ts +9 -0
- package/dist/tools/goal-tools.js +159 -0
- package/dist/tools/mcp-server.d.ts +13 -0
- package/dist/tools/mcp-server.js +141 -0
- package/dist/tools/memory-tools.d.ts +10 -0
- package/dist/tools/memory-tools.js +568 -0
- package/dist/tools/session-tools.d.ts +6 -0
- package/dist/tools/session-tools.js +146 -0
- package/dist/tools/shared.d.ts +216 -0
- package/dist/tools/shared.js +340 -0
- package/dist/tools/team-tools.d.ts +6 -0
- package/dist/tools/team-tools.js +447 -0
- package/dist/tools/tool-meta.d.ts +34 -0
- package/dist/tools/tool-meta.js +133 -0
- package/dist/tools/vault-tools.d.ts +8 -0
- package/dist/tools/vault-tools.js +457 -0
- package/dist/types.d.ts +716 -0
- package/dist/types.js +16 -0
- package/dist/vault-migrations/0001-add-execution-framework.d.ts +10 -0
- package/dist/vault-migrations/0001-add-execution-framework.js +47 -0
- package/dist/vault-migrations/0002-add-agentic-communication.d.ts +12 -0
- package/dist/vault-migrations/0002-add-agentic-communication.js +79 -0
- package/dist/vault-migrations/0003-update-execution-pipeline-narration.d.ts +11 -0
- package/dist/vault-migrations/0003-update-execution-pipeline-narration.js +73 -0
- package/dist/vault-migrations/helpers.d.ts +14 -0
- package/dist/vault-migrations/helpers.js +44 -0
- package/dist/vault-migrations/runner.d.ts +14 -0
- package/dist/vault-migrations/runner.js +139 -0
- package/dist/vault-migrations/types.d.ts +42 -0
- package/dist/vault-migrations/types.js +9 -0
- package/install.sh +320 -0
- package/package.json +84 -0
- package/scripts/postinstall.js +125 -0
- package/vault/00-System/AGENTS.md +66 -0
- package/vault/00-System/CRON.md +71 -0
- package/vault/00-System/HEARTBEAT.md +58 -0
- package/vault/00-System/MEMORY.md +16 -0
- package/vault/00-System/SOUL.md +96 -0
- package/vault/05-Tasks/TASKS.md +19 -0
- package/vault/06-Templates/_Daily-Template.md +28 -0
- package/vault/06-Templates/_People-Template.md +22 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Goals API routes — extracted from dashboard.ts
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync, unlinkSync, } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import matter from 'gray-matter';
|
|
9
|
+
export function goalsRouter(deps) {
|
|
10
|
+
const router = Router();
|
|
11
|
+
const { goalsDir, cronRunsDir, vaultDir, cronFile, getGateway } = deps;
|
|
12
|
+
// List goals with contributions + delegations
|
|
13
|
+
router.get('/progress', (_req, res) => {
|
|
14
|
+
if (!existsSync(goalsDir)) {
|
|
15
|
+
res.json({ goals: [] });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const files = readdirSync(goalsDir).filter(f => f.endsWith('.json'));
|
|
20
|
+
const goals = files.map(f => {
|
|
21
|
+
try {
|
|
22
|
+
const goal = JSON.parse(readFileSync(path.join(goalsDir, f), 'utf-8'));
|
|
23
|
+
const agentContributions = {};
|
|
24
|
+
if (goal.linkedCronJobs?.length && existsSync(cronRunsDir)) {
|
|
25
|
+
for (const jobName of goal.linkedCronJobs) {
|
|
26
|
+
const safe = jobName.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
27
|
+
const logFile = path.join(cronRunsDir, `${safe}.jsonl`);
|
|
28
|
+
if (!existsSync(logFile))
|
|
29
|
+
continue;
|
|
30
|
+
const lines = readFileSync(logFile, 'utf-8').trim().split('\n').filter(Boolean);
|
|
31
|
+
for (const line of lines.slice(-20)) {
|
|
32
|
+
try {
|
|
33
|
+
const entry = JSON.parse(line);
|
|
34
|
+
const agent = entry.agentSlug || jobName;
|
|
35
|
+
if (!agentContributions[agent])
|
|
36
|
+
agentContributions[agent] = { runs: 0, successes: 0 };
|
|
37
|
+
agentContributions[agent].runs++;
|
|
38
|
+
if (entry.status === 'ok')
|
|
39
|
+
agentContributions[agent].successes++;
|
|
40
|
+
agentContributions[agent].lastRun = entry.finishedAt;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const delegationsDir = path.join(vaultDir, '00-System', 'agents');
|
|
49
|
+
const delegations = [];
|
|
50
|
+
if (existsSync(delegationsDir)) {
|
|
51
|
+
try {
|
|
52
|
+
for (const agentDir of readdirSync(delegationsDir)) {
|
|
53
|
+
const tasksDir = path.join(delegationsDir, agentDir, 'delegations');
|
|
54
|
+
if (!existsSync(tasksDir))
|
|
55
|
+
continue;
|
|
56
|
+
for (const tf of readdirSync(tasksDir).filter(tf => tf.endsWith('.json'))) {
|
|
57
|
+
try {
|
|
58
|
+
const task = JSON.parse(readFileSync(path.join(tasksDir, tf), 'utf-8'));
|
|
59
|
+
if (task.goalId === goal.id) {
|
|
60
|
+
delegations.push({ agent: task.toAgent || agentDir, task: task.task || tf, status: task.status || 'pending' });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch { /* ignore */ }
|
|
70
|
+
}
|
|
71
|
+
return { ...goal, agentContributions, delegations };
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}).filter(Boolean);
|
|
77
|
+
res.json({ goals });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
res.json({ goals: [] });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// Create goal
|
|
84
|
+
router.post('/', express.json(), (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
if (!existsSync(goalsDir))
|
|
87
|
+
mkdirSync(goalsDir, { recursive: true });
|
|
88
|
+
const id = Math.random().toString(16).slice(2, 10);
|
|
89
|
+
const { title, description, owner, priority, status, targetDate, linkedCronJobs, nextActions, blockers, reviewFrequency } = req.body;
|
|
90
|
+
if (!title) {
|
|
91
|
+
res.status(400).json({ ok: false, error: 'Title is required' });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const goal = {
|
|
95
|
+
id, title, description: description || '', status: status || 'active',
|
|
96
|
+
owner: owner || 'clementine', priority: priority || 'medium',
|
|
97
|
+
createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
|
|
98
|
+
progressNotes: [], nextActions: nextActions || [], blockers: blockers || [],
|
|
99
|
+
reviewFrequency: reviewFrequency || 'weekly', linkedCronJobs: linkedCronJobs || [],
|
|
100
|
+
targetDate: targetDate || undefined,
|
|
101
|
+
};
|
|
102
|
+
writeFileSync(path.join(goalsDir, `${id}.json`), JSON.stringify(goal, null, 2));
|
|
103
|
+
res.json({ ok: true, goal });
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
// Update goal
|
|
110
|
+
router.put('/:id', express.json(), (req, res) => {
|
|
111
|
+
try {
|
|
112
|
+
const goalPath = path.join(goalsDir, `${req.params.id}.json`);
|
|
113
|
+
if (!existsSync(goalPath)) {
|
|
114
|
+
res.status(404).json({ ok: false, error: 'Goal not found' });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const existing = JSON.parse(readFileSync(goalPath, 'utf-8'));
|
|
118
|
+
const { title, description, owner, priority, status, targetDate, linkedCronJobs, nextActions, blockers, reviewFrequency } = req.body;
|
|
119
|
+
if (title !== undefined)
|
|
120
|
+
existing.title = title;
|
|
121
|
+
if (description !== undefined)
|
|
122
|
+
existing.description = description;
|
|
123
|
+
if (owner !== undefined)
|
|
124
|
+
existing.owner = owner;
|
|
125
|
+
if (priority !== undefined)
|
|
126
|
+
existing.priority = priority;
|
|
127
|
+
if (status !== undefined)
|
|
128
|
+
existing.status = status;
|
|
129
|
+
if (targetDate !== undefined)
|
|
130
|
+
existing.targetDate = targetDate;
|
|
131
|
+
if (linkedCronJobs !== undefined)
|
|
132
|
+
existing.linkedCronJobs = linkedCronJobs;
|
|
133
|
+
if (nextActions !== undefined)
|
|
134
|
+
existing.nextActions = nextActions;
|
|
135
|
+
if (blockers !== undefined)
|
|
136
|
+
existing.blockers = blockers;
|
|
137
|
+
if (reviewFrequency !== undefined)
|
|
138
|
+
existing.reviewFrequency = reviewFrequency;
|
|
139
|
+
existing.updatedAt = new Date().toISOString();
|
|
140
|
+
writeFileSync(goalPath, JSON.stringify(existing, null, 2));
|
|
141
|
+
res.json({ ok: true, goal: existing });
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
// Delete goal
|
|
148
|
+
router.delete('/:id', (_req, res) => {
|
|
149
|
+
try {
|
|
150
|
+
const goalPath = path.join(goalsDir, `${_req.params.id}.json`);
|
|
151
|
+
if (!existsSync(goalPath)) {
|
|
152
|
+
res.status(404).json({ ok: false, error: 'Goal not found' });
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
unlinkSync(goalPath);
|
|
156
|
+
res.json({ ok: true });
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
// Generate cron proposals from goal
|
|
163
|
+
router.post('/:id/generate-crons', async (req, res) => {
|
|
164
|
+
try {
|
|
165
|
+
const goalPath = path.join(goalsDir, `${req.params.id}.json`);
|
|
166
|
+
if (!existsSync(goalPath)) {
|
|
167
|
+
res.status(404).json({ ok: false, error: 'Goal not found' });
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const goal = JSON.parse(readFileSync(goalPath, 'utf-8'));
|
|
171
|
+
const prompt = `You are analyzing a goal and proposing automated scheduled tasks (cron jobs) to make progress on it.
|
|
172
|
+
|
|
173
|
+
## Goal: ${goal.title}
|
|
174
|
+
${goal.description || ''}
|
|
175
|
+
|
|
176
|
+
## Current Status: ${goal.status || 'active'}
|
|
177
|
+
## Priority: ${goal.priority || 'medium'}
|
|
178
|
+
## Owner: ${goal.owner || 'clementine'}
|
|
179
|
+
${goal.nextActions?.length ? `## Next Actions:\n${goal.nextActions.map((a) => `- ${a}`).join('\n')}` : ''}
|
|
180
|
+
${goal.blockers?.length ? `## Blockers:\n${goal.blockers.map((b) => `- ${b}`).join('\n')}` : ''}
|
|
181
|
+
${goal.linkedCronJobs?.length ? `## Already Linked Cron Jobs:\n${goal.linkedCronJobs.map((j) => `- ${j}`).join('\n')}` : ''}
|
|
182
|
+
|
|
183
|
+
## Instructions
|
|
184
|
+
Propose 1-3 NEW cron jobs that would make automated progress on this goal. For each job, provide:
|
|
185
|
+
- name: A short slug (lowercase, hyphens, no spaces)
|
|
186
|
+
- schedule: A cron expression (e.g., "0 9 * * 1-5" for weekdays 9am)
|
|
187
|
+
- prompt: The detailed task prompt the agent should execute when this job runs
|
|
188
|
+
- tier: 1 (quick, under 10 min) or 2 (thorough, can take longer)
|
|
189
|
+
- rationale: Why this job helps the goal
|
|
190
|
+
|
|
191
|
+
Respond ONLY with valid JSON:
|
|
192
|
+
{"proposals":[{"name":"...","schedule":"...","prompt":"...","tier":1,"rationale":"..."}]}`;
|
|
193
|
+
const gw = await getGateway();
|
|
194
|
+
const response = await gw.handleMessage('dashboard:cron-gen', prompt);
|
|
195
|
+
let proposals = [];
|
|
196
|
+
try {
|
|
197
|
+
const jsonMatch = response.match(/\{[\s\S]*"proposals"[\s\S]*\}/);
|
|
198
|
+
if (jsonMatch) {
|
|
199
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
200
|
+
proposals = parsed.proposals || [];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
res.json({ ok: false, error: 'Could not parse LLM response', raw: response.slice(0, 1000) });
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
res.json({ ok: true, proposals, goalId: goal.id, goalTitle: goal.title });
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// Approve cron proposals
|
|
214
|
+
router.post('/:id/approve-crons', express.json(), (req, res) => {
|
|
215
|
+
try {
|
|
216
|
+
const goalPath = path.join(goalsDir, `${req.params.id}.json`);
|
|
217
|
+
if (!existsSync(goalPath)) {
|
|
218
|
+
res.status(404).json({ ok: false, error: 'Goal not found' });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const goal = JSON.parse(readFileSync(goalPath, 'utf-8'));
|
|
222
|
+
const crons = req.body.crons || [];
|
|
223
|
+
if (crons.length === 0) {
|
|
224
|
+
res.status(400).json({ ok: false, error: 'No crons to approve' });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const cronRaw = existsSync(cronFile) ? readFileSync(cronFile, 'utf-8') : '---\njobs: []\n---\n';
|
|
228
|
+
const parsed = matter(cronRaw);
|
|
229
|
+
const jobs = parsed.data.jobs || [];
|
|
230
|
+
const existingNames = new Set(jobs.map(j => j.name));
|
|
231
|
+
const added = [];
|
|
232
|
+
for (const c of crons) {
|
|
233
|
+
if (existingNames.has(c.name))
|
|
234
|
+
continue;
|
|
235
|
+
jobs.push({ name: c.name, schedule: c.schedule, prompt: c.prompt, enabled: true, tier: c.tier || 1 });
|
|
236
|
+
added.push(c.name);
|
|
237
|
+
}
|
|
238
|
+
if (added.length > 0) {
|
|
239
|
+
parsed.data.jobs = jobs;
|
|
240
|
+
writeFileSync(cronFile, matter.stringify(parsed.content, parsed.data));
|
|
241
|
+
if (!goal.linkedCronJobs)
|
|
242
|
+
goal.linkedCronJobs = [];
|
|
243
|
+
for (const name of added) {
|
|
244
|
+
if (!goal.linkedCronJobs.includes(name))
|
|
245
|
+
goal.linkedCronJobs.push(name);
|
|
246
|
+
}
|
|
247
|
+
goal.updatedAt = new Date().toISOString();
|
|
248
|
+
writeFileSync(goalPath, JSON.stringify(goal, null, 2));
|
|
249
|
+
}
|
|
250
|
+
res.json({ ok: true, added, skipped: crons.length - added.length });
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return router;
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=goals.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow API routes — extracted from dashboard.ts
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import type { Gateway } from '../../gateway/router.js';
|
|
6
|
+
export interface WorkflowsRouterDeps {
|
|
7
|
+
workflowsDir: string;
|
|
8
|
+
workflowRunsDir: string;
|
|
9
|
+
agentsBase: string;
|
|
10
|
+
getGateway: () => Promise<Gateway>;
|
|
11
|
+
broadcastEvent: (event: {
|
|
12
|
+
type: string;
|
|
13
|
+
data?: unknown;
|
|
14
|
+
}) => void;
|
|
15
|
+
cachedAsync: <T>(key: string, ttlMs: number, compute: () => Promise<T>) => Promise<T>;
|
|
16
|
+
}
|
|
17
|
+
export declare function workflowsRouter(deps: WorkflowsRouterDeps): Router;
|
|
18
|
+
//# sourceMappingURL=workflows.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow API routes — extracted from dashboard.ts
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
export function workflowsRouter(deps) {
|
|
9
|
+
const router = Router();
|
|
10
|
+
const { workflowsDir, workflowRunsDir, agentsBase, getGateway, broadcastEvent, cachedAsync } = deps;
|
|
11
|
+
router.get('/', async (_req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const workflows = await cachedAsync('workflows', 10_000, async () => {
|
|
14
|
+
const { parseAllWorkflows } = await import('../../agent/workflow-runner.js');
|
|
15
|
+
const wfs = [];
|
|
16
|
+
if (existsSync(workflowsDir)) {
|
|
17
|
+
for (const wf of parseAllWorkflows(workflowsDir)) {
|
|
18
|
+
wfs.push({ ...wf, scope: 'global' });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (existsSync(agentsBase)) {
|
|
22
|
+
for (const slug of readdirSync(agentsBase).filter(d => !d.startsWith('_'))) {
|
|
23
|
+
const wfDir = path.join(agentsBase, slug, 'workflows');
|
|
24
|
+
if (!existsSync(wfDir))
|
|
25
|
+
continue;
|
|
26
|
+
for (const wf of parseAllWorkflows(wfDir)) {
|
|
27
|
+
wfs.push({ ...wf, agentSlug: slug, scope: slug });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return wfs;
|
|
32
|
+
});
|
|
33
|
+
res.json({ ok: true, workflows });
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
router.post('/:name/run', express.json(), async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
const name = decodeURIComponent(req.params.name);
|
|
42
|
+
const { parseAllWorkflows } = await import('../../agent/workflow-runner.js');
|
|
43
|
+
const allWfs = [];
|
|
44
|
+
if (existsSync(workflowsDir))
|
|
45
|
+
allWfs.push(...parseAllWorkflows(workflowsDir));
|
|
46
|
+
if (existsSync(agentsBase)) {
|
|
47
|
+
for (const slug of readdirSync(agentsBase).filter(d => !d.startsWith('_'))) {
|
|
48
|
+
const wfDir = path.join(agentsBase, slug, 'workflows');
|
|
49
|
+
if (existsSync(wfDir))
|
|
50
|
+
allWfs.push(...parseAllWorkflows(wfDir));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const wf = allWfs.find(w => w.name === name);
|
|
54
|
+
if (!wf) {
|
|
55
|
+
res.status(404).json({ ok: false, error: 'Workflow not found: ' + name });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const inputs = req.body.inputs || {};
|
|
59
|
+
res.json({ ok: true, message: `Workflow '${name}' triggered` });
|
|
60
|
+
broadcastEvent({ type: 'workflow_triggered', data: { name } });
|
|
61
|
+
getGateway().then(gw => gw.handleWorkflow(wf, inputs)).then(result => {
|
|
62
|
+
broadcastEvent({ type: 'workflow_complete', data: { name, status: 'ok', preview: (result || '').slice(0, 300) } });
|
|
63
|
+
}).catch(err => {
|
|
64
|
+
broadcastEvent({ type: 'workflow_complete', data: { name, status: 'error', error: String(err) } });
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
router.get('/:name/runs', (_req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
const name = decodeURIComponent(_req.params.name);
|
|
74
|
+
const safe = name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
75
|
+
const logFile = path.join(workflowRunsDir, `${safe}.jsonl`);
|
|
76
|
+
if (!existsSync(logFile)) {
|
|
77
|
+
res.json({ ok: true, runs: [] });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const lines = readFileSync(logFile, 'utf-8').trim().split('\n').filter(Boolean);
|
|
81
|
+
const runs = lines.slice(-20).reverse().map(l => {
|
|
82
|
+
try {
|
|
83
|
+
return JSON.parse(l);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}).filter(Boolean);
|
|
89
|
+
res.json({ ok: true, runs });
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
res.status(500).json({ ok: false, error: String(e) });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return router;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=workflows.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive configuration wizard.
|
|
3
|
+
*
|
|
4
|
+
* Uses @inquirer/prompts for arrow-key selection, password masking,
|
|
5
|
+
* and checkbox multi-select. Replaces the old readline-based flow.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runSetup(): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=setup.d.ts.map
|