@agent-spaces/server 0.3.67 → 0.4.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/dist/adapters/agent-runtime.js +4 -0
- package/dist/adapters/codex-function-tool-bridge.js +9 -1
- package/dist/adapters/git.js +13 -2
- package/dist/adapters/hermes-runtime.js +711 -108
- package/dist/adapters/langchain-runtime.js +384 -21
- package/dist/adapters/oh-my-pi-runtime.js +858 -0
- package/dist/adapters/open-agent-sdk-runtime.js +202 -5
- package/dist/adapters/open-agent-sdk-runtime.test.js +35 -0
- package/dist/agents/agent-message-parts.js +52 -2
- package/dist/agents/issue-task-controller.js +4 -2
- package/dist/agents/title-generator-agent.js +120 -0
- package/dist/app.js +59 -6
- package/dist/routes/agent-sse.js +70 -7
- package/dist/routes/channel.js +23 -2
- package/dist/routes/chat-run.js +191 -0
- package/dist/routes/chat.js +142 -0
- package/dist/routes/command.js +16 -0
- package/dist/routes/data.js +189 -0
- package/dist/routes/git.js +10 -1
- package/dist/routes/import.js +199 -0
- package/dist/routes/issue.js +16 -4
- package/dist/routes/plugin.js +69 -0
- package/dist/routes/version.js +2 -1
- package/dist/routes/workflow-hook.js +71 -0
- package/dist/routes/workflow.js +282 -7
- package/dist/routes/workspace.js +13 -4
- package/dist/services/agent.js +123 -36
- package/dist/services/ai-text.js +185 -0
- package/dist/services/builtin-tools/index.js +1 -0
- package/dist/services/builtin-tools/workflow-editor-tools.js +509 -0
- package/dist/services/builtin-tools/workflow-exec-tools.js +320 -0
- package/dist/services/chat.js +134 -0
- package/dist/services/command-process-manager.js +16 -0
- package/dist/services/execution-manager.js +1346 -0
- package/dist/services/generated-title.js +59 -0
- package/dist/services/gitignore.js +22 -18
- package/dist/services/interaction-manager.js +114 -0
- package/dist/services/issue-retry.js +25 -0
- package/dist/services/output-style.js +8 -1
- package/dist/services/plugin.js +257 -0
- package/dist/services/prompt-template.js +8 -1
- package/dist/services/pty.js +20 -0
- package/dist/services/search.js +16 -6
- package/dist/services/skill.js +2 -0
- package/dist/services/version.js +17 -7
- package/dist/services/workflow-command-runner.js +5 -4
- package/dist/services/workflow-trigger-service.js +137 -0
- package/dist/services/workflow.js +199 -36
- package/dist/storage/agent-store.js +8 -0
- package/dist/storage/chat-store.js +151 -0
- package/dist/storage/database-store.js +6 -0
- package/dist/storage/json-store.js +2 -1
- package/dist/storage/kanban-store.js +6 -0
- package/dist/storage/workflow-store.js +386 -22
- package/dist/ws/agent-prompt.js +6 -2
- package/dist/ws/agent-runner.js +29 -18
- package/dist/ws/chat-handler.js +89 -0
- package/dist/ws/connection-manager.js +49 -2
- package/dist/ws/execution-channels.js +88 -0
- package/dist/ws/handler.js +32 -5
- package/dist/ws/message-parts.js +130 -12
- package/dist/ws/terminal-handler.js +26 -0
- package/package.json +12 -1
- package/public/avatars/user.jpg +0 -0
package/dist/services/skill.js
CHANGED
|
@@ -168,6 +168,8 @@ export function importSkillFromStore(storePath, group) {
|
|
|
168
168
|
const candidates = [
|
|
169
169
|
join(process.cwd(), 'public', 'skills', storePath),
|
|
170
170
|
join(import.meta.dirname ?? '', '..', 'public', 'skills', storePath),
|
|
171
|
+
join(process.cwd(), 'packages', 'agents', 'skills', storePath),
|
|
172
|
+
join(import.meta.dirname ?? '', '..', '..', '..', 'agents', 'skills', storePath),
|
|
171
173
|
];
|
|
172
174
|
for (const c of candidates) {
|
|
173
175
|
if (existsSync(c) && statSync(c).isDirectory()) {
|
package/dist/services/version.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -8,13 +8,23 @@ const CACHE_TTL = 3600_000; // 1 hour
|
|
|
8
8
|
export function getLocalVersion() {
|
|
9
9
|
if (localVersion !== '')
|
|
10
10
|
return localVersion;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const packageJsonPaths = [
|
|
12
|
+
join(__dirname, '..', '..', 'package.json'),
|
|
13
|
+
join(__dirname, '..', 'package.json'),
|
|
14
|
+
];
|
|
15
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
16
|
+
if (!existsSync(packageJsonPath))
|
|
17
|
+
continue;
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
20
|
+
localVersion = pkg.version || '0.0.0';
|
|
21
|
+
return localVersion;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Try the next package.json candidate before falling back.
|
|
25
|
+
}
|
|
17
26
|
}
|
|
27
|
+
localVersion = '0.0.0';
|
|
18
28
|
return localVersion;
|
|
19
29
|
}
|
|
20
30
|
export function getCachedLatest() {
|
|
@@ -2,12 +2,13 @@ import { exec } from 'child_process';
|
|
|
2
2
|
import { getWorkspace } from '../storage/workspace-store.js';
|
|
3
3
|
export async function executeCommandNode(workspaceId, node) {
|
|
4
4
|
const workspace = getWorkspace(workspaceId);
|
|
5
|
-
const
|
|
5
|
+
const data = node.data;
|
|
6
|
+
const cwd = data.cwd || workspace?.boundDirs?.[0] || process.cwd();
|
|
6
7
|
return new Promise((resolve) => {
|
|
7
|
-
exec(
|
|
8
|
+
exec(data.script, {
|
|
8
9
|
cwd,
|
|
9
|
-
env: { ...process.env, ...
|
|
10
|
-
shell:
|
|
10
|
+
env: { ...process.env, ...data.env },
|
|
11
|
+
shell: data.shell || undefined,
|
|
11
12
|
timeout: 300_000,
|
|
12
13
|
maxBuffer: 10 * 1024 * 1024,
|
|
13
14
|
}, (error, stdout, stderr) => {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as store from '../storage/workflow-store.js';
|
|
2
|
+
export class WorkflowTriggerService {
|
|
3
|
+
port;
|
|
4
|
+
cronJobs = new Map();
|
|
5
|
+
hookIndex = new Map();
|
|
6
|
+
nodeCron = null;
|
|
7
|
+
executionManager = null;
|
|
8
|
+
constructor(port = 3100) {
|
|
9
|
+
this.port = port;
|
|
10
|
+
try {
|
|
11
|
+
this.nodeCron = require('node-cron');
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// node-cron not available
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
setExecutionManager(em) {
|
|
18
|
+
this.executionManager = em;
|
|
19
|
+
}
|
|
20
|
+
async start() {
|
|
21
|
+
const workflows = store.listWorkflows();
|
|
22
|
+
for (const wf of workflows) {
|
|
23
|
+
this.registerTriggers(wf);
|
|
24
|
+
}
|
|
25
|
+
console.log(`[TriggerService] Started. ${this.cronJobs.size} cron jobs, ${this.hookIndex.size} hooks registered`);
|
|
26
|
+
}
|
|
27
|
+
reloadWorkflow(workflowId) {
|
|
28
|
+
this.clearTriggersForWorkflow(workflowId);
|
|
29
|
+
const wf = store.getWorkflow(workflowId);
|
|
30
|
+
if (wf)
|
|
31
|
+
this.registerTriggers(wf);
|
|
32
|
+
}
|
|
33
|
+
removeWorkflow(workflowId) {
|
|
34
|
+
this.clearTriggersForWorkflow(workflowId);
|
|
35
|
+
}
|
|
36
|
+
getHookBindings(hookName) {
|
|
37
|
+
return Array.from(this.hookIndex.get(hookName) ?? []);
|
|
38
|
+
}
|
|
39
|
+
getHookConflicts(hookName, excludeWorkflowId) {
|
|
40
|
+
const bindings = this.hookIndex.get(hookName) ?? new Set();
|
|
41
|
+
const ids = Array.from(bindings)
|
|
42
|
+
.map(b => b.workflowId)
|
|
43
|
+
.filter(id => id !== excludeWorkflowId);
|
|
44
|
+
return { conflictWorkflowIds: [...new Set(ids)] };
|
|
45
|
+
}
|
|
46
|
+
getHookUrl(hookName) {
|
|
47
|
+
return `http://localhost:${this.port}/api/workflows/hook/${hookName}`;
|
|
48
|
+
}
|
|
49
|
+
validateCron(cronExpr) {
|
|
50
|
+
if (!this.nodeCron)
|
|
51
|
+
return { valid: false, nextRuns: [], error: 'node-cron not available' };
|
|
52
|
+
if (!this.nodeCron.validate(cronExpr)) {
|
|
53
|
+
return { valid: false, nextRuns: [], error: 'Invalid cron expression' };
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const CronExpressionParser = require('cron-parser');
|
|
57
|
+
const interval = CronExpressionParser.parse(cronExpr);
|
|
58
|
+
const nextRuns = [];
|
|
59
|
+
for (let i = 0; i < 5; i++) {
|
|
60
|
+
const iso = interval.next().toISOString();
|
|
61
|
+
if (iso)
|
|
62
|
+
nextRuns.push(iso);
|
|
63
|
+
}
|
|
64
|
+
return { valid: true, nextRuns };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
return { valid: false, nextRuns: [], error: err.message };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
stop() {
|
|
71
|
+
for (const [, task] of this.cronJobs) {
|
|
72
|
+
task.stop();
|
|
73
|
+
}
|
|
74
|
+
this.cronJobs.clear();
|
|
75
|
+
this.hookIndex.clear();
|
|
76
|
+
}
|
|
77
|
+
registerTriggers(wf) {
|
|
78
|
+
if (!wf.triggers)
|
|
79
|
+
return;
|
|
80
|
+
for (const trigger of wf.triggers) {
|
|
81
|
+
if (!trigger.enabled)
|
|
82
|
+
continue;
|
|
83
|
+
if (trigger.type === 'cron') {
|
|
84
|
+
this.registerCronJob(wf.id, trigger);
|
|
85
|
+
}
|
|
86
|
+
else if (trigger.type === 'hook') {
|
|
87
|
+
this.registerHookBinding(wf.id, trigger);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
registerCronJob(workflowId, trigger) {
|
|
92
|
+
if (!this.nodeCron)
|
|
93
|
+
return;
|
|
94
|
+
const key = `${workflowId}:${trigger.id}`;
|
|
95
|
+
try {
|
|
96
|
+
const task = this.nodeCron.schedule(trigger.cron, () => {
|
|
97
|
+
console.log(`[TriggerService] Cron fired for workflow ${workflowId}`);
|
|
98
|
+
if (this.executionManager) {
|
|
99
|
+
this.executionManager.execute({ workflowId }, '__cron__').catch((err) => {
|
|
100
|
+
console.error(`[TriggerService] Cron execution failed for ${workflowId}: ${err.message}`);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}, { timezone: trigger.timezone });
|
|
104
|
+
this.cronJobs.set(key, task);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
console.error(`[TriggerService] Invalid cron "${trigger.cron}" for workflow ${workflowId}: ${err.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
registerHookBinding(workflowId, trigger) {
|
|
111
|
+
let bindings = this.hookIndex.get(trigger.hookName);
|
|
112
|
+
if (!bindings) {
|
|
113
|
+
bindings = new Set();
|
|
114
|
+
this.hookIndex.set(trigger.hookName, bindings);
|
|
115
|
+
}
|
|
116
|
+
bindings.add({ workflowId, triggerId: trigger.id });
|
|
117
|
+
}
|
|
118
|
+
clearTriggersForWorkflow(workflowId) {
|
|
119
|
+
for (const [key, task] of this.cronJobs) {
|
|
120
|
+
if (key.startsWith(`${workflowId}:`)) {
|
|
121
|
+
task.stop();
|
|
122
|
+
this.cronJobs.delete(key);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const [hookName, bindings] of this.hookIndex) {
|
|
126
|
+
for (const binding of bindings) {
|
|
127
|
+
if (binding.workflowId === workflowId) {
|
|
128
|
+
bindings.delete(binding);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (bindings.size === 0) {
|
|
132
|
+
this.hookIndex.delete(hookName);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=workflow-trigger-service.js.map
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
// packages/server/src/services/workflow.ts
|
|
2
|
-
// NOTE: All storage functions are synchronous (matching json-store pattern).
|
|
3
|
-
// Service functions are also synchronous where they only call sync storage.
|
|
4
|
-
// Route handlers wrap in async for Express compatibility.
|
|
5
2
|
import { v4 as uuid } from 'uuid';
|
|
6
|
-
import * as
|
|
3
|
+
import * as store from '../storage/workflow-store.js';
|
|
7
4
|
import { listTemplates } from './agent.js';
|
|
8
|
-
|
|
5
|
+
function getAgentData(node) {
|
|
6
|
+
if (node.type !== 'agent')
|
|
7
|
+
return null;
|
|
8
|
+
return node.data;
|
|
9
|
+
}
|
|
10
|
+
function getLabel(node) {
|
|
11
|
+
return node.data.label || node.label;
|
|
12
|
+
}
|
|
13
|
+
// ---- DAG Validation ----
|
|
9
14
|
function topologicalSort(nodes, edges) {
|
|
10
15
|
const adj = new Map();
|
|
11
16
|
const inDegree = new Map();
|
|
@@ -14,8 +19,10 @@ function topologicalSort(nodes, edges) {
|
|
|
14
19
|
inDegree.set(node.id, 0);
|
|
15
20
|
}
|
|
16
21
|
for (const edge of edges) {
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
const source = edge.source;
|
|
23
|
+
const target = edge.target;
|
|
24
|
+
adj.get(source).push(target);
|
|
25
|
+
inDegree.set(target, (inDegree.get(target) ?? 0) + 1);
|
|
19
26
|
}
|
|
20
27
|
const queue = [];
|
|
21
28
|
for (const [id, deg] of inDegree) {
|
|
@@ -67,14 +74,17 @@ export function validateDAG(template) {
|
|
|
67
74
|
}
|
|
68
75
|
return null;
|
|
69
76
|
}
|
|
70
|
-
//
|
|
77
|
+
// ---- Role Staleness Resolution ----
|
|
71
78
|
function resolveStaleRoles(nodes) {
|
|
72
79
|
const agentMap = new Map(listTemplates().map(a => [a.id, a]));
|
|
73
80
|
const invalidIds = [];
|
|
74
81
|
const resolved = nodes.map(node => {
|
|
75
82
|
if (node.type === 'command')
|
|
76
83
|
return node;
|
|
77
|
-
const
|
|
84
|
+
const agentData = getAgentData(node);
|
|
85
|
+
if (!agentData)
|
|
86
|
+
return node;
|
|
87
|
+
const agent = agentMap.get(agentData.agentConfigId);
|
|
78
88
|
if (!agent) {
|
|
79
89
|
invalidIds.push(node.id);
|
|
80
90
|
return node;
|
|
@@ -91,39 +101,43 @@ function resolveStaleRoles(nodes) {
|
|
|
91
101
|
});
|
|
92
102
|
return { nodes: resolved, invalidIds };
|
|
93
103
|
}
|
|
94
|
-
//
|
|
95
|
-
export function listWorkflows() {
|
|
96
|
-
return
|
|
104
|
+
// ---- Workflow CRUD ----
|
|
105
|
+
export function listWorkflows(folderId) {
|
|
106
|
+
return store.listWorkflows(folderId);
|
|
97
107
|
}
|
|
98
108
|
export function getWorkflow(workflowId) {
|
|
99
|
-
return
|
|
109
|
+
return store.getWorkflow(workflowId);
|
|
100
110
|
}
|
|
101
111
|
export function createWorkflow(input) {
|
|
102
|
-
const now =
|
|
112
|
+
const now = Date.now();
|
|
103
113
|
const nodes = input.nodes ?? [];
|
|
104
114
|
const edges = input.edges ?? [];
|
|
105
115
|
const { nodes: resolvedNodes, invalidIds } = resolveStaleRoles(nodes);
|
|
106
116
|
if (invalidIds.length > 0) {
|
|
107
117
|
throw new Error(`Invalid agent references in nodes: ${invalidIds.join(', ')}`);
|
|
108
118
|
}
|
|
109
|
-
const
|
|
119
|
+
const workflow = {
|
|
110
120
|
id: uuid(),
|
|
111
121
|
name: input.name,
|
|
122
|
+
folderId: input.folderId ?? null,
|
|
123
|
+
icon: input.icon,
|
|
112
124
|
description: input.description,
|
|
125
|
+
tags: input.tags,
|
|
113
126
|
nodes: resolvedNodes,
|
|
114
127
|
edges,
|
|
115
|
-
viewport: input.viewport,
|
|
116
128
|
createdAt: now,
|
|
117
129
|
updatedAt: now,
|
|
130
|
+
triggers: input.triggers,
|
|
131
|
+
groups: input.groups,
|
|
118
132
|
};
|
|
119
|
-
const error = validateDAG(
|
|
133
|
+
const error = validateDAG(workflow);
|
|
120
134
|
if (error)
|
|
121
135
|
throw new Error(error);
|
|
122
|
-
|
|
123
|
-
return
|
|
136
|
+
store.createWorkflow(workflow);
|
|
137
|
+
return workflow;
|
|
124
138
|
}
|
|
125
139
|
export function updateWorkflow(workflowId, updates) {
|
|
126
|
-
const existing =
|
|
140
|
+
const existing = store.getWorkflow(workflowId);
|
|
127
141
|
if (!existing)
|
|
128
142
|
throw new Error('Workflow not found');
|
|
129
143
|
let nodes = updates.nodes ?? existing.nodes;
|
|
@@ -140,27 +154,27 @@ export function updateWorkflow(workflowId, updates) {
|
|
|
140
154
|
nodes,
|
|
141
155
|
id: existing.id,
|
|
142
156
|
createdAt: existing.createdAt,
|
|
143
|
-
updatedAt:
|
|
157
|
+
updatedAt: Date.now(),
|
|
144
158
|
};
|
|
145
159
|
if (updates.nodes || updates.edges) {
|
|
146
160
|
const error = validateDAG(updated);
|
|
147
161
|
if (error)
|
|
148
162
|
throw new Error(error);
|
|
149
163
|
}
|
|
150
|
-
|
|
164
|
+
store.updateWorkflow(updated);
|
|
151
165
|
return updated;
|
|
152
166
|
}
|
|
153
167
|
export function deleteWorkflow(workflowId) {
|
|
154
|
-
const existing =
|
|
168
|
+
const existing = store.getWorkflow(workflowId);
|
|
155
169
|
if (!existing)
|
|
156
170
|
throw new Error('Workflow not found');
|
|
157
|
-
|
|
171
|
+
store.deleteWorkflow(workflowId);
|
|
158
172
|
}
|
|
159
173
|
export function duplicateWorkflow(workflowId) {
|
|
160
|
-
const existing =
|
|
174
|
+
const existing = store.getWorkflow(workflowId);
|
|
161
175
|
if (!existing)
|
|
162
176
|
throw new Error('Workflow not found');
|
|
163
|
-
const now =
|
|
177
|
+
const now = Date.now();
|
|
164
178
|
const duplicated = {
|
|
165
179
|
...existing,
|
|
166
180
|
id: uuid(),
|
|
@@ -168,9 +182,124 @@ export function duplicateWorkflow(workflowId) {
|
|
|
168
182
|
createdAt: now,
|
|
169
183
|
updatedAt: now,
|
|
170
184
|
};
|
|
171
|
-
|
|
185
|
+
store.createWorkflow(duplicated);
|
|
172
186
|
return duplicated;
|
|
173
187
|
}
|
|
188
|
+
// ---- Folder CRUD ----
|
|
189
|
+
export function listFolders() {
|
|
190
|
+
return store.listWorkflowFolders();
|
|
191
|
+
}
|
|
192
|
+
export function createFolder(input) {
|
|
193
|
+
const folders = store.listWorkflowFolders();
|
|
194
|
+
const siblings = folders.filter(f => f.parentId === (input.parentId ?? null));
|
|
195
|
+
const folder = {
|
|
196
|
+
id: uuid(),
|
|
197
|
+
name: input.name,
|
|
198
|
+
parentId: input.parentId ?? null,
|
|
199
|
+
order: siblings.length > 0 ? Math.max(...siblings.map(f => f.order)) + 1 : 0,
|
|
200
|
+
createdAt: Date.now(),
|
|
201
|
+
};
|
|
202
|
+
store.createWorkflowFolder(folder);
|
|
203
|
+
return folder;
|
|
204
|
+
}
|
|
205
|
+
export function updateFolder(id, updates) {
|
|
206
|
+
store.updateWorkflowFolder(id, updates);
|
|
207
|
+
}
|
|
208
|
+
export function deleteFolder(id) {
|
|
209
|
+
store.deleteWorkflowFolder(id);
|
|
210
|
+
}
|
|
211
|
+
// ---- Version CRUD ----
|
|
212
|
+
export function listVersions(workflowId) {
|
|
213
|
+
return store.listVersions(workflowId);
|
|
214
|
+
}
|
|
215
|
+
export function createVersion(workflowId, name) {
|
|
216
|
+
const workflow = store.getWorkflow(workflowId);
|
|
217
|
+
if (!workflow)
|
|
218
|
+
throw new Error('Workflow not found');
|
|
219
|
+
const version = {
|
|
220
|
+
id: uuid(),
|
|
221
|
+
workflowId,
|
|
222
|
+
name: name || `v${store.listVersions(workflowId).length + 1}`,
|
|
223
|
+
snapshot: {
|
|
224
|
+
nodes: JSON.parse(JSON.stringify(workflow.nodes)),
|
|
225
|
+
edges: JSON.parse(JSON.stringify(workflow.edges)),
|
|
226
|
+
},
|
|
227
|
+
createdAt: Date.now(),
|
|
228
|
+
};
|
|
229
|
+
store.addVersion(workflowId, version);
|
|
230
|
+
return version;
|
|
231
|
+
}
|
|
232
|
+
export function getVersion(workflowId, versionId) {
|
|
233
|
+
return store.getVersion(workflowId, versionId);
|
|
234
|
+
}
|
|
235
|
+
export function deleteVersion(workflowId, versionId) {
|
|
236
|
+
store.deleteVersion(workflowId, versionId);
|
|
237
|
+
}
|
|
238
|
+
export function clearVersions(workflowId) {
|
|
239
|
+
store.clearVersions(workflowId);
|
|
240
|
+
}
|
|
241
|
+
// ---- Execution Log CRUD ----
|
|
242
|
+
export function listExecutionLogs(workflowId) {
|
|
243
|
+
return store.listExecutionLogs(workflowId);
|
|
244
|
+
}
|
|
245
|
+
export function listAllExecutionLogs(limit) {
|
|
246
|
+
return store.listAllExecutionLogs(limit);
|
|
247
|
+
}
|
|
248
|
+
export function getExecutionLog(workflowId, logId) {
|
|
249
|
+
return store.getExecutionLog(workflowId, logId);
|
|
250
|
+
}
|
|
251
|
+
export function deleteExecutionLog(workflowId, logId) {
|
|
252
|
+
store.deleteExecutionLog(workflowId, logId);
|
|
253
|
+
}
|
|
254
|
+
export function clearExecutionLogs(workflowId) {
|
|
255
|
+
store.clearExecutionLogs(workflowId);
|
|
256
|
+
}
|
|
257
|
+
// ---- Staging ----
|
|
258
|
+
export function loadStaging(workflowId) {
|
|
259
|
+
return store.loadStaging(workflowId);
|
|
260
|
+
}
|
|
261
|
+
export function saveStaging(workflowId, nodes) {
|
|
262
|
+
store.saveStaging(workflowId, nodes);
|
|
263
|
+
}
|
|
264
|
+
export function clearStaging(workflowId) {
|
|
265
|
+
store.clearStaging(workflowId);
|
|
266
|
+
}
|
|
267
|
+
// ---- Operation History ----
|
|
268
|
+
export function loadOperationHistory(workflowId) {
|
|
269
|
+
return store.loadOperationHistory(workflowId);
|
|
270
|
+
}
|
|
271
|
+
export function saveOperationHistory(workflowId, entries) {
|
|
272
|
+
store.saveOperationHistory(workflowId, entries);
|
|
273
|
+
}
|
|
274
|
+
export function clearOperationHistory(workflowId) {
|
|
275
|
+
store.clearOperationHistory(workflowId);
|
|
276
|
+
}
|
|
277
|
+
// ---- Workflow Agent Chat ----
|
|
278
|
+
export function loadChat(workflowId) {
|
|
279
|
+
return store.loadChat(workflowId);
|
|
280
|
+
}
|
|
281
|
+
export function saveChat(workflowId, messages) {
|
|
282
|
+
store.saveChat(workflowId, messages);
|
|
283
|
+
}
|
|
284
|
+
export function clearChat(workflowId) {
|
|
285
|
+
store.clearChat(workflowId);
|
|
286
|
+
}
|
|
287
|
+
// ---- Plugin Config Schemes ----
|
|
288
|
+
export function listPluginSchemes(workflowId, pluginId) {
|
|
289
|
+
return store.listPluginSchemes(workflowId, pluginId);
|
|
290
|
+
}
|
|
291
|
+
export function readPluginScheme(workflowId, pluginId, schemeName) {
|
|
292
|
+
return store.readPluginScheme(workflowId, pluginId, schemeName);
|
|
293
|
+
}
|
|
294
|
+
export function createPluginScheme(workflowId, pluginId, schemeName) {
|
|
295
|
+
store.savePluginScheme(workflowId, pluginId, schemeName, {});
|
|
296
|
+
}
|
|
297
|
+
export function savePluginScheme(workflowId, pluginId, schemeName, data) {
|
|
298
|
+
store.savePluginScheme(workflowId, pluginId, schemeName, data);
|
|
299
|
+
}
|
|
300
|
+
export function deletePluginScheme(workflowId, pluginId, schemeName) {
|
|
301
|
+
store.deletePluginScheme(workflowId, pluginId, schemeName);
|
|
302
|
+
}
|
|
174
303
|
export function mapWorkflowToTaskDrafts(template) {
|
|
175
304
|
const dependsOn = new Map();
|
|
176
305
|
for (const node of template.nodes) {
|
|
@@ -180,41 +309,75 @@ export function mapWorkflowToTaskDrafts(template) {
|
|
|
180
309
|
dependsOn.get(edge.target)?.push(edge.source);
|
|
181
310
|
}
|
|
182
311
|
return template.nodes.map(node => {
|
|
312
|
+
const label = getLabel(node);
|
|
183
313
|
if (node.type === 'command') {
|
|
184
314
|
return {
|
|
185
315
|
key: node.id,
|
|
186
|
-
title:
|
|
187
|
-
description: `Run command: ${
|
|
316
|
+
title: label,
|
|
317
|
+
description: `Run command: ${label}`,
|
|
188
318
|
agentConfigId: undefined,
|
|
189
319
|
dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
|
|
190
320
|
sandboxDirs: undefined,
|
|
191
321
|
commandNode: node,
|
|
192
322
|
};
|
|
193
323
|
}
|
|
324
|
+
const agentData = getAgentData(node);
|
|
325
|
+
const taskTitle = (agentData?.taskTitleTemplate || label);
|
|
326
|
+
const taskDesc = agentData?.taskDescriptionTemplate || `Task assigned to ${label} (${agentData?.role || 'unknown'})`;
|
|
194
327
|
return {
|
|
195
328
|
key: node.id,
|
|
196
|
-
title:
|
|
197
|
-
description:
|
|
198
|
-
agentConfigId:
|
|
329
|
+
title: taskTitle,
|
|
330
|
+
description: taskDesc,
|
|
331
|
+
agentConfigId: agentData?.agentConfigId,
|
|
199
332
|
dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
|
|
200
333
|
sandboxDirs: undefined,
|
|
201
334
|
};
|
|
202
335
|
});
|
|
203
336
|
}
|
|
204
|
-
//
|
|
337
|
+
// ---- Run-time Validation ----
|
|
205
338
|
export function validateWorkflowForRun(_workspaceId, template, memberAgentIds) {
|
|
206
339
|
const agentMap = new Map(listTemplates().map((a) => [a.id, a]));
|
|
207
340
|
for (const node of template.nodes) {
|
|
208
341
|
if (node.type === 'command')
|
|
209
342
|
continue;
|
|
210
|
-
const
|
|
343
|
+
const agentData = getAgentData(node);
|
|
344
|
+
if (!agentData)
|
|
345
|
+
continue;
|
|
346
|
+
const label = getLabel(node);
|
|
347
|
+
const agent = agentMap.get(agentData.agentConfigId);
|
|
211
348
|
if (!agent)
|
|
212
|
-
return `Agent "${
|
|
349
|
+
return `Agent "${label}" (${agentData.agentConfigId}) no longer exists`;
|
|
213
350
|
if (!agent.enabled)
|
|
214
351
|
return `Agent "${agent.name}" is disabled`;
|
|
215
|
-
if (!memberAgentIds.has(
|
|
352
|
+
if (!memberAgentIds.has(agentData.agentConfigId))
|
|
216
353
|
return `Agent "${agent.name}" is not in the issue channel members`;
|
|
217
354
|
}
|
|
218
355
|
return null;
|
|
219
356
|
}
|
|
357
|
+
// ---- Cron Validation ----
|
|
358
|
+
export function validateCron(cronExpr) {
|
|
359
|
+
try {
|
|
360
|
+
const { validate } = require('node-cron');
|
|
361
|
+
if (!validate(cronExpr)) {
|
|
362
|
+
return { valid: false, nextRuns: [], error: 'Invalid cron expression' };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
// If node-cron validate fails to load, continue with cron-parser
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const CronExpressionParser = require('cron-parser');
|
|
370
|
+
const interval = CronExpressionParser.parse(cronExpr);
|
|
371
|
+
const nextRuns = [];
|
|
372
|
+
for (let i = 0; i < 5; i++) {
|
|
373
|
+
const iso = interval.next().toISOString();
|
|
374
|
+
if (iso)
|
|
375
|
+
nextRuns.push(iso);
|
|
376
|
+
}
|
|
377
|
+
return { valid: true, nextRuns };
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
return { valid: false, nextRuns: [], error: err.message };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
220
383
|
//# sourceMappingURL=workflow.js.map
|
|
@@ -149,6 +149,7 @@ export function getAgentUsageDashboard(days = 30) {
|
|
|
149
149
|
dailyMap.set(key, {
|
|
150
150
|
date: key,
|
|
151
151
|
label: date.toLocaleDateString('en-US', { weekday: 'short' }),
|
|
152
|
+
requests: 0,
|
|
152
153
|
inputTokens: 0,
|
|
153
154
|
outputTokens: 0,
|
|
154
155
|
totalTokens: 0,
|
|
@@ -161,6 +162,7 @@ export function getAgentUsageDashboard(days = 30) {
|
|
|
161
162
|
for (const record of records) {
|
|
162
163
|
const day = dailyMap.get(record.completedAt.slice(0, 10));
|
|
163
164
|
if (day) {
|
|
165
|
+
day.requests += 1;
|
|
164
166
|
day.inputTokens += record.inputTokens + record.cachedInputTokens;
|
|
165
167
|
day.outputTokens += record.outputTokens + record.reasoningTokens;
|
|
166
168
|
day.totalTokens += record.totalTokens;
|
|
@@ -323,4 +325,10 @@ function findConfiguredModelCost(model) {
|
|
|
323
325
|
outputPerMillion: toMoney(configured.cost.outputPerMillion),
|
|
324
326
|
};
|
|
325
327
|
}
|
|
328
|
+
export function closeDb() {
|
|
329
|
+
if (db) {
|
|
330
|
+
db.close();
|
|
331
|
+
db = null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
326
334
|
//# sourceMappingURL=agent-store.js.map
|