@agent-spaces/server 0.3.65 → 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 +8 -0
- package/dist/adapters/codex-function-tool-bridge.js +152 -0
- package/dist/adapters/codex-runtime.js +23 -4
- package/dist/adapters/git.js +25 -3
- package/dist/adapters/hermes-runtime.js +813 -0
- 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 +71 -2
- package/dist/agents/pull-request-agent.js +115 -0
- package/dist/agents/scheduler-agent.js +1 -1
- package/dist/agents/title-generator-agent.js +120 -0
- package/dist/app.js +61 -4
- package/dist/middleware/auth.js +1 -1
- package/dist/package.json +2 -1
- package/dist/routes/agent-sse.js +70 -6
- 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 +17 -5
- package/dist/routes/notification.js +10 -0
- package/dist/routes/plugin.js +69 -0
- package/dist/routes/version.js +95 -0
- package/dist/routes/workflow-hook.js +71 -0
- package/dist/routes/workflow.js +282 -7
- package/dist/routes/workspace.js +13 -4
- package/dist/routes/worktree.js +93 -0
- 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/notification-center.js +9 -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 +69 -0
- package/dist/services/workflow-command-runner.js +24 -0
- package/dist/services/workflow-trigger-service.js +137 -0
- package/dist/services/workflow.js +219 -39
- package/dist/services/workspace.js +23 -1
- package/dist/services/worktree.js +207 -0
- 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/storage/worktree-store.js +33 -0
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +4 -4
- package/dist/web/__next._full.txt +21 -21
- package/dist/web/__next._head.txt +4 -4
- package/dist/web/__next._index.txt +13 -13
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/{08x85faoz58i7.js → 0-92u-tiuy40a.js} +4 -4
- package/dist/web/_next/static/chunks/{0db14450oqzhg.js → 0-yizd8osmas2.js} +1 -1
- package/dist/web/_next/static/chunks/0.ln~3mnv8.69.js +1 -0
- package/dist/web/_next/static/chunks/{11cchuwoupmbf.js → 01y3e9pemva7b.js} +10 -10
- package/dist/web/_next/static/chunks/02l~tmzrgny.~.js +1 -0
- package/dist/web/_next/static/chunks/03f1bwuynjdmk.js +1 -0
- package/dist/web/_next/static/chunks/04cb4kc9r2pd1.js +1 -0
- package/dist/web/_next/static/chunks/04de6ha5_e3pp.js +1 -0
- package/dist/web/_next/static/chunks/09he0p79ov1rn.js +1 -0
- package/dist/web/_next/static/chunks/09vpocs4zuc8h.js +1 -0
- package/dist/web/_next/static/chunks/0_o2exgr0e_nh.js +1 -0
- package/dist/web/_next/static/chunks/{0xbaakj0dfc0..js → 0_y9nskc-xt2k.js} +1 -1
- package/dist/web/_next/static/chunks/0clnj-1fi6u8j.js +3 -0
- package/dist/web/_next/static/chunks/0gv40by0zzxs_.js +1 -0
- package/dist/web/_next/static/chunks/0iusr8y88.kjg.js +1 -0
- package/dist/web/_next/static/chunks/{0-a.-hl3wv99k.js → 0j-8418fxvkd1.js} +1 -1
- package/dist/web/_next/static/chunks/0j.wxxyoclma5.js +1 -0
- package/dist/web/_next/static/chunks/{0.6b64sy0g5.3.js → 0jq.16sozqik..js} +1 -1
- package/dist/web/_next/static/chunks/0jw8xdk_fpnp0.css +1 -0
- package/dist/web/_next/static/chunks/0l1lkd-wcotma.js +1 -0
- package/dist/web/_next/static/chunks/{0u6h-hqc_i52e.js → 0ncxf_zr4n839.js} +1 -1
- package/dist/web/_next/static/chunks/0ndo7um~v7vco.js +1 -0
- package/dist/web/_next/static/chunks/0ngk.~kan8wq_.js +1 -0
- package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +1 -0
- package/dist/web/_next/static/chunks/0rt9bofnn6qvn.js +1 -0
- package/dist/web/_next/static/chunks/0t64.9jd00hio.js +3 -0
- package/dist/web/_next/static/chunks/0taui9--5n6-5.js +1 -0
- package/dist/web/_next/static/chunks/0ty39169lh~z3.js +1 -0
- package/dist/web/_next/static/chunks/0v8nof~waedlu.js +1 -0
- package/dist/web/_next/static/chunks/{0-pi.67w.ymsr.js → 0vcfoagnpf.aw.js} +1 -1
- package/dist/web/_next/static/chunks/{0vis~lx0nxgn-.js → 0wwwq.gyyz0vg.js} +3 -3
- package/dist/web/_next/static/chunks/0x5lak8yz7pm7.js +1 -0
- package/dist/web/_next/static/chunks/0xon_.4-o0_zq.js +7 -0
- package/dist/web/_next/static/chunks/0zjnrbt7inime.js +1 -0
- package/dist/web/_next/static/chunks/0zwebpitvpuv1.js +1 -0
- package/dist/web/_next/static/chunks/0~02o4g-o6n~m.js +1 -0
- package/dist/web/_next/static/chunks/{0w7.tb29958zj.js → 0~fjezp.k4qk3.js} +1 -1
- package/dist/web/_next/static/chunks/0~sh93re1-_rp.js +1 -0
- package/dist/web/_next/static/chunks/10_niiq0k6o-p.js +5 -0
- package/dist/web/_next/static/chunks/12tjr0_2ccrtq.js +2 -0
- package/dist/web/_next/static/chunks/12x5.go_x9mfk.js +1 -0
- package/dist/web/_next/static/chunks/{0l2w0qz1ea7ql.js → 12~5dk9i9d16t.js} +2 -2
- package/dist/web/_next/static/chunks/13iye4rv5p31b.js +1 -0
- package/dist/web/_next/static/chunks/{01~quun6sefgl.js → 13~y-b.b9rrw4.js} +1 -1
- package/dist/web/_next/static/chunks/151wzhb5q0lo5.js +1 -0
- package/dist/web/_not-found/__next._full.txt +19 -19
- package/dist/web/_not-found/__next._head.txt +4 -4
- package/dist/web/_not-found/__next._index.txt +13 -13
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dist/web/_not-found/__next._not-found.txt +3 -3
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +19 -19
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +21 -21
- package/dist/web/login/__next._full.txt +21 -21
- package/dist/web/login/__next._head.txt +4 -4
- package/dist/web/login/__next._index.txt +13 -13
- package/dist/web/login/__next._tree.txt +2 -2
- package/dist/web/login/__next.login.__PAGE__.txt +4 -4
- package/dist/web/login/__next.login.txt +3 -3
- package/dist/web/login.html +1 -1
- package/dist/web/login.txt +21 -21
- package/dist/web/settings/__next._full.txt +23 -23
- package/dist/web/settings/__next._head.txt +4 -4
- package/dist/web/settings/__next._index.txt +13 -13
- package/dist/web/settings/__next._tree.txt +2 -2
- package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
- package/dist/web/settings/__next.settings.txt +5 -5
- package/dist/web/settings/agents/__next._full.txt +23 -23
- package/dist/web/settings/agents/__next._head.txt +4 -4
- package/dist/web/settings/agents/__next._index.txt +13 -13
- package/dist/web/settings/agents/__next._tree.txt +2 -2
- package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
- package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
- package/dist/web/settings/agents/__next.settings.txt +5 -5
- package/dist/web/settings/agents.html +1 -1
- package/dist/web/settings/agents.txt +23 -23
- package/dist/web/settings/mcps/__next._full.txt +23 -23
- package/dist/web/settings/mcps/__next._head.txt +4 -4
- package/dist/web/settings/mcps/__next._index.txt +13 -13
- package/dist/web/settings/mcps/__next._tree.txt +2 -2
- package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
- package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
- package/dist/web/settings/mcps/__next.settings.txt +5 -5
- package/dist/web/settings/mcps.html +1 -1
- package/dist/web/settings/mcps.txt +23 -23
- package/dist/web/settings/models/__next._full.txt +23 -23
- package/dist/web/settings/models/__next._head.txt +4 -4
- package/dist/web/settings/models/__next._index.txt +13 -13
- package/dist/web/settings/models/__next._tree.txt +2 -2
- package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
- package/dist/web/settings/models/__next.settings.models.txt +3 -3
- package/dist/web/settings/models/__next.settings.txt +5 -5
- package/dist/web/settings/models.html +1 -1
- package/dist/web/settings/models.txt +23 -23
- package/dist/web/settings/output-styles/__next._full.txt +23 -23
- package/dist/web/settings/output-styles/__next._head.txt +4 -4
- package/dist/web/settings/output-styles/__next._index.txt +13 -13
- package/dist/web/settings/output-styles/__next._tree.txt +2 -2
- package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
- package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
- package/dist/web/settings/output-styles/__next.settings.txt +5 -5
- package/dist/web/settings/output-styles.html +1 -1
- package/dist/web/settings/output-styles.txt +23 -23
- package/dist/web/settings/prompts/__next._full.txt +23 -23
- package/dist/web/settings/prompts/__next._head.txt +4 -4
- package/dist/web/settings/prompts/__next._index.txt +13 -13
- package/dist/web/settings/prompts/__next._tree.txt +2 -2
- package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
- package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
- package/dist/web/settings/prompts/__next.settings.txt +5 -5
- package/dist/web/settings/prompts.html +1 -1
- package/dist/web/settings/prompts.txt +23 -23
- package/dist/web/settings/providers/__next._full.txt +23 -23
- package/dist/web/settings/providers/__next._head.txt +4 -4
- package/dist/web/settings/providers/__next._index.txt +13 -13
- package/dist/web/settings/providers/__next._tree.txt +2 -2
- package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
- package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
- package/dist/web/settings/providers/__next.settings.txt +5 -5
- package/dist/web/settings/providers.html +1 -1
- package/dist/web/settings/providers.txt +23 -23
- package/dist/web/settings/skills/__next._full.txt +23 -23
- package/dist/web/settings/skills/__next._head.txt +4 -4
- package/dist/web/settings/skills/__next._index.txt +13 -13
- package/dist/web/settings/skills/__next._tree.txt +2 -2
- package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
- package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
- package/dist/web/settings/skills/__next.settings.txt +5 -5
- package/dist/web/settings/skills.html +1 -1
- package/dist/web/settings/skills.txt +23 -23
- package/dist/web/settings/tools/__next._full.txt +23 -23
- package/dist/web/settings/tools/__next._head.txt +4 -4
- package/dist/web/settings/tools/__next._index.txt +13 -13
- package/dist/web/settings/tools/__next._tree.txt +2 -2
- package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
- package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
- package/dist/web/settings/tools/__next.settings.txt +5 -5
- package/dist/web/settings/tools.html +1 -1
- package/dist/web/settings/tools.txt +23 -23
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +23 -23
- package/dist/web/workflows/__next._full.txt +22 -22
- package/dist/web/workflows/__next._head.txt +4 -4
- package/dist/web/workflows/__next._index.txt +13 -13
- package/dist/web/workflows/__next._tree.txt +2 -2
- package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
- package/dist/web/workflows/__next.workflows.txt +3 -3
- package/dist/web/workflows.html +1 -1
- package/dist/web/workflows.txt +22 -22
- package/dist/web/workspace/_/__next._full.txt +20 -20
- package/dist/web/workspace/_/__next._head.txt +4 -4
- package/dist/web/workspace/_/__next._index.txt +13 -13
- package/dist/web/workspace/_/__next._tree.txt +2 -2
- package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.txt +3 -3
- package/dist/web/workspace/_.html +1 -1
- package/dist/web/workspace/_.txt +20 -20
- package/dist/web/workspaces/__next._full.txt +21 -21
- package/dist/web/workspaces/__next._head.txt +4 -4
- package/dist/web/workspaces/__next._index.txt +13 -13
- package/dist/web/workspaces/__next._tree.txt +2 -2
- package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
- package/dist/web/workspaces/__next.workspaces.txt +3 -3
- package/dist/web/workspaces.html +1 -1
- package/dist/web/workspaces.txt +21 -21
- package/dist/ws/agent-prompt.js +36 -3
- package/dist/ws/agent-runner.js +30 -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 +13 -1
- package/public/avatars/user.jpg +0 -0
- package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +0 -1
- package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +0 -1
- package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +0 -1
- package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +0 -1
- package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +0 -1
- package/dist/web/_next/static/chunks/02j4sr9psucf-.js +0 -1
- package/dist/web/_next/static/chunks/06si8l0doqe8p.js +0 -1
- package/dist/web/_next/static/chunks/08kfx8on9oty~.js +0 -1
- package/dist/web/_next/static/chunks/08ugbvzr20drp.js +0 -2
- package/dist/web/_next/static/chunks/0_cqojbge48kq.js +0 -5
- package/dist/web/_next/static/chunks/0d6~~lv0-6kex.js +0 -3
- package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +0 -1
- package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +0 -1
- package/dist/web/_next/static/chunks/0kbvoxla7f751.js +0 -1
- package/dist/web/_next/static/chunks/0paaat8y96ayc.js +0 -7
- package/dist/web/_next/static/chunks/0pq318f8xgafg.js +0 -1
- package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +0 -1
- package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +0 -1
- package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +0 -3
- package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +0 -1
- package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +0 -1
- package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +0 -1
- package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +0 -1
- package/dist/web/_next/static/chunks/1452eibz62gs6.css +0 -1
- package/dist/web/_next/static/chunks/16zlreiems5cn.js +0 -1
- package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +0 -1
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_ssgManifest.js +0 -0
package/dist/services/search.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// packages/server/src/services/search.ts
|
|
2
|
-
import {
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
3
|
import { readdir, stat, readFile } from 'node:fs/promises';
|
|
4
|
-
import { join, relative } from 'node:path';
|
|
4
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { minimatch } from 'minimatch';
|
|
6
6
|
import { resolvePath } from './file.js';
|
|
7
7
|
import { createGitignoreFilter } from './gitignore.js';
|
|
@@ -13,8 +13,15 @@ function isBinaryPath(path) {
|
|
|
13
13
|
const ext = path.split('.').pop()?.toLowerCase() || '';
|
|
14
14
|
return BINARY_EXTENSIONS.has(`.${ext}`) || ext === path;
|
|
15
15
|
}
|
|
16
|
+
function toWorkspaceRelativePath(basePath, path) {
|
|
17
|
+
const absPath = isAbsolute(path) ? resolve(path) : resolve(basePath, path);
|
|
18
|
+
const relPath = relative(basePath, absPath);
|
|
19
|
+
if (relPath.startsWith('..') || isAbsolute(relPath))
|
|
20
|
+
return null;
|
|
21
|
+
return relPath;
|
|
22
|
+
}
|
|
16
23
|
function searchWithRipgrep(basePath, options) {
|
|
17
|
-
const args = ['
|
|
24
|
+
const args = ['--json', '--max-count', String(options.maxResults || 200)];
|
|
18
25
|
if (!options.caseSensitive)
|
|
19
26
|
args.push('-i');
|
|
20
27
|
if (options.regex) {
|
|
@@ -25,9 +32,10 @@ function searchWithRipgrep(basePath, options) {
|
|
|
25
32
|
}
|
|
26
33
|
if (options.filePattern)
|
|
27
34
|
args.push('--glob', options.filePattern);
|
|
28
|
-
args.push('--', '.'
|
|
35
|
+
args.push('--', '.');
|
|
29
36
|
try {
|
|
30
|
-
const output =
|
|
37
|
+
const output = execFileSync('rg', args, {
|
|
38
|
+
cwd: basePath,
|
|
31
39
|
encoding: 'utf-8',
|
|
32
40
|
maxBuffer: 50 * 1024 * 1024,
|
|
33
41
|
timeout: 30000,
|
|
@@ -40,7 +48,9 @@ function searchWithRipgrep(basePath, options) {
|
|
|
40
48
|
const parsed = JSON.parse(line);
|
|
41
49
|
if (parsed.type !== 'match')
|
|
42
50
|
continue;
|
|
43
|
-
const filePath =
|
|
51
|
+
const filePath = toWorkspaceRelativePath(basePath, parsed.data.path.text);
|
|
52
|
+
if (!filePath)
|
|
53
|
+
continue;
|
|
44
54
|
const text = parsed.data.lines.text.trimEnd();
|
|
45
55
|
const sub = parsed.data.submatches[0];
|
|
46
56
|
if (!sub)
|
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()) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
let localVersion = '';
|
|
6
|
+
let cachedLatest = null;
|
|
7
|
+
const CACHE_TTL = 3600_000; // 1 hour
|
|
8
|
+
export function getLocalVersion() {
|
|
9
|
+
if (localVersion !== '')
|
|
10
|
+
return localVersion;
|
|
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
|
+
}
|
|
26
|
+
}
|
|
27
|
+
localVersion = '0.0.0';
|
|
28
|
+
return localVersion;
|
|
29
|
+
}
|
|
30
|
+
export function getCachedLatest() {
|
|
31
|
+
if (cachedLatest && Date.now() - cachedLatest.ts < CACHE_TTL) {
|
|
32
|
+
return cachedLatest.version;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
export async function fetchLatestVersion(forceRefresh = false) {
|
|
37
|
+
if (!forceRefresh && cachedLatest && Date.now() - cachedLatest.ts < CACHE_TTL) {
|
|
38
|
+
return cachedLatest.version;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const res = await fetch('https://registry.npmjs.org/@agent-spaces/server', {
|
|
42
|
+
signal: AbortSignal.timeout(8000),
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok)
|
|
45
|
+
return cachedLatest?.version ?? null;
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
const latest = data?.['dist-tags']?.latest;
|
|
48
|
+
if (latest) {
|
|
49
|
+
cachedLatest = { version: latest, ts: Date.now() };
|
|
50
|
+
}
|
|
51
|
+
return latest ?? cachedLatest?.version ?? null;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return cachedLatest?.version ?? null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function isNewerVersion(latest, local) {
|
|
58
|
+
const parse = (v) => v.replace(/^v/, '').split('.').map(Number);
|
|
59
|
+
const a = parse(latest);
|
|
60
|
+
const b = parse(local);
|
|
61
|
+
for (let i = 0; i < 3; i++) {
|
|
62
|
+
if ((a[i] || 0) > (b[i] || 0))
|
|
63
|
+
return true;
|
|
64
|
+
if ((a[i] || 0) < (b[i] || 0))
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { getWorkspace } from '../storage/workspace-store.js';
|
|
3
|
+
export async function executeCommandNode(workspaceId, node) {
|
|
4
|
+
const workspace = getWorkspace(workspaceId);
|
|
5
|
+
const data = node.data;
|
|
6
|
+
const cwd = data.cwd || workspace?.boundDirs?.[0] || process.cwd();
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
exec(data.script, {
|
|
9
|
+
cwd,
|
|
10
|
+
env: { ...process.env, ...data.env },
|
|
11
|
+
shell: data.shell || undefined,
|
|
12
|
+
timeout: 300_000,
|
|
13
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
14
|
+
}, (error, stdout, stderr) => {
|
|
15
|
+
resolve({
|
|
16
|
+
success: !error,
|
|
17
|
+
exitCode: error ? (typeof error.code === 'number' ? error.code : 1) : 0,
|
|
18
|
+
stdout: stdout || '',
|
|
19
|
+
stderr: stderr || '',
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=workflow-command-runner.js.map
|
|
@@ -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,12 +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')
|
|
83
|
+
return node;
|
|
84
|
+
const agentData = getAgentData(node);
|
|
85
|
+
if (!agentData)
|
|
86
|
+
return node;
|
|
87
|
+
const agent = agentMap.get(agentData.agentConfigId);
|
|
76
88
|
if (!agent) {
|
|
77
89
|
invalidIds.push(node.id);
|
|
78
90
|
return node;
|
|
@@ -89,39 +101,43 @@ function resolveStaleRoles(nodes) {
|
|
|
89
101
|
});
|
|
90
102
|
return { nodes: resolved, invalidIds };
|
|
91
103
|
}
|
|
92
|
-
//
|
|
93
|
-
export function listWorkflows() {
|
|
94
|
-
return
|
|
104
|
+
// ---- Workflow CRUD ----
|
|
105
|
+
export function listWorkflows(folderId) {
|
|
106
|
+
return store.listWorkflows(folderId);
|
|
95
107
|
}
|
|
96
108
|
export function getWorkflow(workflowId) {
|
|
97
|
-
return
|
|
109
|
+
return store.getWorkflow(workflowId);
|
|
98
110
|
}
|
|
99
111
|
export function createWorkflow(input) {
|
|
100
|
-
const now =
|
|
112
|
+
const now = Date.now();
|
|
101
113
|
const nodes = input.nodes ?? [];
|
|
102
114
|
const edges = input.edges ?? [];
|
|
103
115
|
const { nodes: resolvedNodes, invalidIds } = resolveStaleRoles(nodes);
|
|
104
116
|
if (invalidIds.length > 0) {
|
|
105
117
|
throw new Error(`Invalid agent references in nodes: ${invalidIds.join(', ')}`);
|
|
106
118
|
}
|
|
107
|
-
const
|
|
119
|
+
const workflow = {
|
|
108
120
|
id: uuid(),
|
|
109
121
|
name: input.name,
|
|
122
|
+
folderId: input.folderId ?? null,
|
|
123
|
+
icon: input.icon,
|
|
110
124
|
description: input.description,
|
|
125
|
+
tags: input.tags,
|
|
111
126
|
nodes: resolvedNodes,
|
|
112
127
|
edges,
|
|
113
|
-
viewport: input.viewport,
|
|
114
128
|
createdAt: now,
|
|
115
129
|
updatedAt: now,
|
|
130
|
+
triggers: input.triggers,
|
|
131
|
+
groups: input.groups,
|
|
116
132
|
};
|
|
117
|
-
const error = validateDAG(
|
|
133
|
+
const error = validateDAG(workflow);
|
|
118
134
|
if (error)
|
|
119
135
|
throw new Error(error);
|
|
120
|
-
|
|
121
|
-
return
|
|
136
|
+
store.createWorkflow(workflow);
|
|
137
|
+
return workflow;
|
|
122
138
|
}
|
|
123
139
|
export function updateWorkflow(workflowId, updates) {
|
|
124
|
-
const existing =
|
|
140
|
+
const existing = store.getWorkflow(workflowId);
|
|
125
141
|
if (!existing)
|
|
126
142
|
throw new Error('Workflow not found');
|
|
127
143
|
let nodes = updates.nodes ?? existing.nodes;
|
|
@@ -138,27 +154,27 @@ export function updateWorkflow(workflowId, updates) {
|
|
|
138
154
|
nodes,
|
|
139
155
|
id: existing.id,
|
|
140
156
|
createdAt: existing.createdAt,
|
|
141
|
-
updatedAt:
|
|
157
|
+
updatedAt: Date.now(),
|
|
142
158
|
};
|
|
143
159
|
if (updates.nodes || updates.edges) {
|
|
144
160
|
const error = validateDAG(updated);
|
|
145
161
|
if (error)
|
|
146
162
|
throw new Error(error);
|
|
147
163
|
}
|
|
148
|
-
|
|
164
|
+
store.updateWorkflow(updated);
|
|
149
165
|
return updated;
|
|
150
166
|
}
|
|
151
167
|
export function deleteWorkflow(workflowId) {
|
|
152
|
-
const existing =
|
|
168
|
+
const existing = store.getWorkflow(workflowId);
|
|
153
169
|
if (!existing)
|
|
154
170
|
throw new Error('Workflow not found');
|
|
155
|
-
|
|
171
|
+
store.deleteWorkflow(workflowId);
|
|
156
172
|
}
|
|
157
173
|
export function duplicateWorkflow(workflowId) {
|
|
158
|
-
const existing =
|
|
174
|
+
const existing = store.getWorkflow(workflowId);
|
|
159
175
|
if (!existing)
|
|
160
176
|
throw new Error('Workflow not found');
|
|
161
|
-
const now =
|
|
177
|
+
const now = Date.now();
|
|
162
178
|
const duplicated = {
|
|
163
179
|
...existing,
|
|
164
180
|
id: uuid(),
|
|
@@ -166,9 +182,124 @@ export function duplicateWorkflow(workflowId) {
|
|
|
166
182
|
createdAt: now,
|
|
167
183
|
updatedAt: now,
|
|
168
184
|
};
|
|
169
|
-
|
|
185
|
+
store.createWorkflow(duplicated);
|
|
170
186
|
return duplicated;
|
|
171
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
|
+
}
|
|
172
303
|
export function mapWorkflowToTaskDrafts(template) {
|
|
173
304
|
const dependsOn = new Map();
|
|
174
305
|
for (const node of template.nodes) {
|
|
@@ -177,27 +308,76 @@ export function mapWorkflowToTaskDrafts(template) {
|
|
|
177
308
|
for (const edge of template.edges) {
|
|
178
309
|
dependsOn.get(edge.target)?.push(edge.source);
|
|
179
310
|
}
|
|
180
|
-
return template.nodes.map(node =>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
311
|
+
return template.nodes.map(node => {
|
|
312
|
+
const label = getLabel(node);
|
|
313
|
+
if (node.type === 'command') {
|
|
314
|
+
return {
|
|
315
|
+
key: node.id,
|
|
316
|
+
title: label,
|
|
317
|
+
description: `Run command: ${label}`,
|
|
318
|
+
agentConfigId: undefined,
|
|
319
|
+
dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
|
|
320
|
+
sandboxDirs: undefined,
|
|
321
|
+
commandNode: node,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const agentData = getAgentData(node);
|
|
325
|
+
const taskTitle = (agentData?.taskTitleTemplate || label);
|
|
326
|
+
const taskDesc = agentData?.taskDescriptionTemplate || `Task assigned to ${label} (${agentData?.role || 'unknown'})`;
|
|
327
|
+
return {
|
|
328
|
+
key: node.id,
|
|
329
|
+
title: taskTitle,
|
|
330
|
+
description: taskDesc,
|
|
331
|
+
agentConfigId: agentData?.agentConfigId,
|
|
332
|
+
dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
|
|
333
|
+
sandboxDirs: undefined,
|
|
334
|
+
};
|
|
335
|
+
});
|
|
188
336
|
}
|
|
189
|
-
//
|
|
337
|
+
// ---- Run-time Validation ----
|
|
190
338
|
export function validateWorkflowForRun(_workspaceId, template, memberAgentIds) {
|
|
191
339
|
const agentMap = new Map(listTemplates().map((a) => [a.id, a]));
|
|
192
340
|
for (const node of template.nodes) {
|
|
193
|
-
|
|
341
|
+
if (node.type === 'command')
|
|
342
|
+
continue;
|
|
343
|
+
const agentData = getAgentData(node);
|
|
344
|
+
if (!agentData)
|
|
345
|
+
continue;
|
|
346
|
+
const label = getLabel(node);
|
|
347
|
+
const agent = agentMap.get(agentData.agentConfigId);
|
|
194
348
|
if (!agent)
|
|
195
|
-
return `Agent "${
|
|
349
|
+
return `Agent "${label}" (${agentData.agentConfigId}) no longer exists`;
|
|
196
350
|
if (!agent.enabled)
|
|
197
351
|
return `Agent "${agent.name}" is disabled`;
|
|
198
|
-
if (!memberAgentIds.has(
|
|
352
|
+
if (!memberAgentIds.has(agentData.agentConfigId))
|
|
199
353
|
return `Agent "${agent.name}" is not in the issue channel members`;
|
|
200
354
|
}
|
|
201
355
|
return null;
|
|
202
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
|
+
}
|
|
203
383
|
//# sourceMappingURL=workflow.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { v4 as uuid } from 'uuid';
|
|
2
2
|
import { listWorkspaces, getWorkspace, createWorkspace, updateWorkspace, deleteWorkspace } from '../storage/workspace-store.js';
|
|
3
|
+
import { listWorktrees } from '../storage/worktree-store.js';
|
|
3
4
|
import { ensureDir } from '../storage/json-store.js';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
6
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
@@ -21,7 +22,14 @@ function initAgentspace(agentspaceDir) {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
export function getAll() {
|
|
24
|
-
|
|
25
|
+
const workspaces = listWorkspaces();
|
|
26
|
+
const workspaceIds = new Set(workspaces.map((workspace) => workspace.id));
|
|
27
|
+
const worktreeWorkspaces = workspaces
|
|
28
|
+
.filter((workspace) => !workspace.isWorktree)
|
|
29
|
+
.flatMap((workspace) => listWorktrees(workspace.id)
|
|
30
|
+
.filter((worktree) => worktree.status === 'active' && !workspaceIds.has(worktree.id))
|
|
31
|
+
.map(worktreeToWorkspace));
|
|
32
|
+
return [...workspaces, ...worktreeWorkspaces];
|
|
25
33
|
}
|
|
26
34
|
export function getById(id) {
|
|
27
35
|
return getWorkspace(id);
|
|
@@ -72,4 +80,18 @@ export function remove(id) {
|
|
|
72
80
|
deleteWorkspace(id);
|
|
73
81
|
return true;
|
|
74
82
|
}
|
|
83
|
+
function worktreeToWorkspace(worktree) {
|
|
84
|
+
return {
|
|
85
|
+
id: worktree.id,
|
|
86
|
+
name: `${worktree.name} (Worktree)`,
|
|
87
|
+
boundDirs: [worktree.path],
|
|
88
|
+
agentspaceDir: join(worktree.path, '.agentspace'),
|
|
89
|
+
isWorktree: true,
|
|
90
|
+
parentWorkspaceId: worktree.workspaceId,
|
|
91
|
+
createdAt: worktree.createdAt,
|
|
92
|
+
updatedAt: worktree.updatedAt,
|
|
93
|
+
activeChannels: [],
|
|
94
|
+
activeIssues: [],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
75
97
|
//# sourceMappingURL=workspace.js.map
|