crewswarm 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -9
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js +1 -0
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js +1 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js +2 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
- package/apps/dashboard/dist/assets/{index-CF0aJRtC.css → index-D-sRshvg.css} +1 -1
- package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
- package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js +1 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
- package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js +1 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js +1 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +1 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
- package/apps/dashboard/dist/index.html +135 -15
- package/apps/dashboard/dist/index.html.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
- package/apps/vibe/README.md +2 -2
- package/apps/vibe/package.json +1 -1
- package/apps/vibe/server.mjs +101 -56
- package/crew-lead.mjs +34 -4
- package/lib/bridges/cli-executor.mjs +1 -1
- package/lib/bridges/gateway-ws.mjs +4 -0
- package/lib/browser/passthrough-stderr.js +1 -0
- package/lib/chat/project-messages.mjs +3 -5
- package/lib/cli-process-tracker.mjs +3 -2
- package/lib/contacts/identity-linker.mjs +1 -0
- package/lib/crew-judge/judge.mjs +19 -18
- package/lib/crew-lead/agent-manager.mjs +1 -1
- package/lib/crew-lead/background.mjs +14 -1
- package/lib/crew-lead/chat-handler.mjs +38 -1
- package/lib/crew-lead/http-server.mjs +106 -57
- package/lib/crew-lead/llm-caller.mjs +24 -8
- package/lib/crew-lead/prompts.mjs +14 -1
- package/lib/crew-lead/tools.mjs +3 -2
- package/lib/crew-lead/wave-dispatcher.mjs +19 -5
- package/lib/crew-lead/ws-router.mjs +219 -27
- package/lib/engines/crew-cli.mjs +1 -1
- package/lib/engines/engine-registry.mjs +14 -3
- package/lib/engines/rt-envelope.mjs +1 -0
- package/lib/engines/runners.mjs +28 -4
- package/lib/gemini-cli-passthrough-noise.mjs +1 -1
- package/lib/integrations/code-search.mjs +4 -3
- package/lib/memory/shared-adapter.mjs +23 -10
- package/lib/pipeline/manager.mjs +2 -1
- package/lib/runtime/config.mjs +1 -1
- package/lib/runtime/paths.mjs +12 -8
- package/lib/runtime/spending.mjs +2 -1
- package/package.json +42 -14
- package/scripts/capture-build-flow.mjs +118 -0
- package/scripts/coverage-report.mjs +209 -0
- package/scripts/coverage-summary.mjs +47 -0
- package/scripts/dashboard-validation.mjs +76 -0
- package/scripts/dashboard.mjs +1667 -551
- package/scripts/generate-openapi.mjs +683 -277
- package/scripts/live-bridge-matrix.mjs +79 -0
- package/scripts/live-cli-matrix.mjs +166 -0
- package/scripts/live-crewchat-check.mjs +42 -0
- package/scripts/live-engine-matrix.mjs +50 -0
- package/scripts/live-provider-failover-matrix.mjs +107 -0
- package/scripts/live-provider-matrix.mjs +228 -0
- package/scripts/restart-all-from-repo.sh +4 -4
- package/scripts/restart-service.sh +12 -9
- package/scripts/smoke-dispatch.mjs +4 -1
- package/scripts/test-blast-radius.mjs +204 -0
- package/scripts/test-report-summary.mjs +88 -0
- package/scripts/test-reporter.mjs +651 -0
- package/scripts/test-rerun.mjs +136 -0
- package/scripts/tmux-bridge +130 -0
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js +0 -1
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +0 -1
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
- package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js +0 -1
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js +0 -1
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
- package/apps/dashboard/index.html +0 -6529
- package/apps/dashboard/package.json +0 -15
- package/apps/dashboard/src/app.js +0 -2828
- package/apps/dashboard/src/app.js.br +0 -0
- package/apps/dashboard/src/app.js.gz +0 -0
- package/apps/dashboard/src/chat/chat-actions.js +0 -1847
- package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
- package/apps/dashboard/src/chat/unified-messages.js +0 -327
- package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
- package/apps/dashboard/src/cli-process.js +0 -208
- package/apps/dashboard/src/cli-process.js.br +0 -0
- package/apps/dashboard/src/cli-process.js.gz +0 -0
- package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
- package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
- package/apps/dashboard/src/core/api.js +0 -18
- package/apps/dashboard/src/core/api.js.br +0 -0
- package/apps/dashboard/src/core/dom.js +0 -228
- package/apps/dashboard/src/core/dom.js.br +0 -0
- package/apps/dashboard/src/core/state.js +0 -91
- package/apps/dashboard/src/core/state.js.br +0 -0
- package/apps/dashboard/src/core/task-manager.js +0 -134
- package/apps/dashboard/src/core/task-manager.js.br +0 -0
- package/apps/dashboard/src/orchestration-status.js +0 -127
- package/apps/dashboard/src/orchestration-status.js.br +0 -0
- package/apps/dashboard/src/setup-wizard.js +0 -562
- package/apps/dashboard/src/setup-wizard.js.br +0 -0
- package/apps/dashboard/src/styles.css +0 -2085
- package/apps/dashboard/src/styles.css.br +0 -0
- package/apps/dashboard/src/styles.css.gz +0 -0
- package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
- package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
- package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/comms-tab.js +0 -955
- package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
- package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/engines-tab.js +0 -175
- package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/memory-tab.js +0 -182
- package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/models-tab.js +0 -450
- package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
- package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js +0 -663
- package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
- package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
- package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/services-tab.js +0 -202
- package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/settings-tab.js +0 -861
- package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/skills-tab.js +0 -284
- package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/spending-tab.js +0 -173
- package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
- package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
- package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/usage-tab.js +0 -390
- package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/waves-tab.js +0 -238
- package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
- package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
- package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
- package/apps/vibe/.crew/cost.json +0 -17
- package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
- package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
- package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
- package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
- package/apps/vibe/.crew/sandbox.json +0 -7
- package/apps/vibe/.crew/session.json +0 -330
- package/apps/vibe/.crew/training-data.jsonl +0 -0
- package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
- package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
- package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
- package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
- package/apps/vibe/ARCHITECTURE.md +0 -3393
- package/apps/vibe/QUICK-REFERENCE.md +0 -211
- package/apps/vibe/ROADMAP.md +0 -41
- package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
- package/apps/vibe/VISUAL-GUIDE.md +0 -378
- package/apps/vibe/capture-demo.mjs +0 -160
- package/apps/vibe/capture-full-demo.mjs +0 -255
- package/apps/vibe/capture-quickstart.mjs +0 -256
- package/apps/vibe/capture-vibe-assets.mjs +0 -71
- package/apps/vibe/capture-vibe-video.mjs +0 -260
- package/apps/vibe/check-buttons.js +0 -41
- package/apps/vibe/diagnose.html +0 -106
- package/apps/vibe/fix-buttons.js +0 -103
- package/apps/vibe/index.html +0 -3404
- package/apps/vibe/package-lock.json +0 -920
- package/apps/vibe/scripts/studio-pty-host.py +0 -117
- package/apps/vibe/src/main.js +0 -2940
- package/apps/vibe/src/register-all-languages.js +0 -98
- package/apps/vibe/start-studio.sh +0 -11
- package/apps/vibe/test/accessibility-tests.js +0 -77
- package/apps/vibe/test/browser-performance-audit.mjs +0 -205
- package/apps/vibe/test/performance-tests.js +0 -120
- package/apps/vibe/test/security-tests.js +0 -213
- package/apps/vibe/tests/e2e.local.mjs +0 -54
- package/apps/vibe/tests/server.smoke.mjs +0 -106
- package/apps/vibe/update_website.mjs +0 -74
- package/apps/vibe/vite.config.js +0 -19
- package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
- package/lib/engines/rt-envelope.mjs.backup-current +0 -870
|
@@ -1,747 +0,0 @@
|
|
|
1
|
-
import { getJSON, postJSON } from "../core/api.js";
|
|
2
|
-
import { escHtml, showNotification } from "../core/dom.js";
|
|
3
|
-
|
|
4
|
-
let hideAllViews = () => {};
|
|
5
|
-
let setNavActive = () => {};
|
|
6
|
-
let knownAgents = [];
|
|
7
|
-
let knownSkills = [];
|
|
8
|
-
|
|
9
|
-
const wfState = {
|
|
10
|
-
selectedName: "",
|
|
11
|
-
list: [],
|
|
12
|
-
runStatus: {},
|
|
13
|
-
editorWorkflow: null,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const WORKFLOW_TEMPLATES = [
|
|
17
|
-
{
|
|
18
|
-
id: "daily-research",
|
|
19
|
-
name: "Daily Research Brief",
|
|
20
|
-
description: "Research, summarize, then QA-check every weekday morning.",
|
|
21
|
-
schedule: "0 9 * * 1-5",
|
|
22
|
-
stages: [
|
|
23
|
-
{
|
|
24
|
-
agent: "crew-researcher",
|
|
25
|
-
task: "Research top 5 updates for our current project and summarize key signals.",
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
agent: "crew-pm",
|
|
29
|
-
task: "Turn the research into a concise daily brief with priorities and risks.",
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
agent: "crew-qa",
|
|
33
|
-
task: "Review the brief for factual clarity and missing edge cases.",
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
id: "seo-content",
|
|
39
|
-
name: "SEO Content Pipeline",
|
|
40
|
-
description: "Generate SEO topic ideas, draft copy, then edit.",
|
|
41
|
-
schedule: "30 10 * * 1,3,5",
|
|
42
|
-
stages: [
|
|
43
|
-
{
|
|
44
|
-
agent: "crew-seo",
|
|
45
|
-
task: "Find one high-intent keyword cluster and propose a short content outline.",
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
agent: "crew-copywriter",
|
|
49
|
-
task: "Write a first draft from the outline. Keep it scannable and conversion-focused.",
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
agent: "crew-main",
|
|
53
|
-
task: "Polish the draft and produce final publish-ready copy.",
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
id: "code-health",
|
|
59
|
-
name: "Code Health Sweep",
|
|
60
|
-
description: "Automated PM->Coder->QA quality pass.",
|
|
61
|
-
schedule: "0 14 * * 1-5",
|
|
62
|
-
stages: [
|
|
63
|
-
{
|
|
64
|
-
agent: "crew-pm",
|
|
65
|
-
task: "Pick one high-value backlog item from project context and define acceptance criteria.",
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
agent: "crew-coder",
|
|
69
|
-
task: "Implement the scoped item in small safe changes and summarize files touched.",
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
agent: "crew-qa",
|
|
73
|
-
task: "Audit the changes, run tests, and report any regressions with severity.",
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
},
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
function emptyWorkflow(name = "") {
|
|
80
|
-
return {
|
|
81
|
-
name,
|
|
82
|
-
description: "",
|
|
83
|
-
enabled: false,
|
|
84
|
-
schedule: "",
|
|
85
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
86
|
-
stages: [{ agent: "crew-main", task: "", tool: "" }],
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function initWorkflowsTab(deps = {}) {
|
|
91
|
-
hideAllViews = deps.hideAllViews || hideAllViews;
|
|
92
|
-
setNavActive = deps.setNavActive || setNavActive;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export async function showWorkflows() {
|
|
96
|
-
hideAllViews();
|
|
97
|
-
document.getElementById("workflowsView")?.classList.add("active");
|
|
98
|
-
setNavActive("navWorkflows");
|
|
99
|
-
await loadAgents();
|
|
100
|
-
await loadSkills();
|
|
101
|
-
await loadWorkflowList();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function loadAgents() {
|
|
105
|
-
try {
|
|
106
|
-
const agents = await getJSON("/api/agents");
|
|
107
|
-
knownAgents = (agents || [])
|
|
108
|
-
.map((a) => (typeof a === "string" ? a : a.id || a.agent))
|
|
109
|
-
.filter(Boolean)
|
|
110
|
-
.sort();
|
|
111
|
-
} catch {
|
|
112
|
-
knownAgents = [];
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function loadSkills() {
|
|
117
|
-
try {
|
|
118
|
-
const data = await getJSON("/api/skills");
|
|
119
|
-
knownSkills = (data.skills || [])
|
|
120
|
-
.map((s) => ({
|
|
121
|
-
name: s.name || "",
|
|
122
|
-
description: s.description || "",
|
|
123
|
-
type: s.type || (s.url ? "api" : "knowledge"),
|
|
124
|
-
}))
|
|
125
|
-
.filter((s) => s.name)
|
|
126
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
127
|
-
} catch {
|
|
128
|
-
knownSkills = [];
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function loadWorkflowList() {
|
|
133
|
-
const listEl = document.getElementById("workflowList");
|
|
134
|
-
if (!listEl) return;
|
|
135
|
-
listEl.innerHTML =
|
|
136
|
-
'<div class="meta" style="padding:10px;">Loading workflows...</div>';
|
|
137
|
-
try {
|
|
138
|
-
const data = await getJSON("/api/workflows/list");
|
|
139
|
-
wfState.list = data.workflows || [];
|
|
140
|
-
renderWorkflowList();
|
|
141
|
-
if (wfState.selectedName) {
|
|
142
|
-
await loadWorkflowItem(wfState.selectedName);
|
|
143
|
-
} else {
|
|
144
|
-
renderWorkflowEditor(emptyWorkflow());
|
|
145
|
-
}
|
|
146
|
-
const tz =
|
|
147
|
-
data.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
148
|
-
const tzEl = document.getElementById("workflowTimezoneLabel");
|
|
149
|
-
if (tzEl) tzEl.textContent = `Local timezone: ${tz}`;
|
|
150
|
-
} catch (e) {
|
|
151
|
-
listEl.innerHTML = `<div class="meta" style="padding:10px;color:var(--red-hi);">Failed to load workflows: ${escHtml(e.message)}</div>`;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function renderWorkflowList() {
|
|
156
|
-
const listEl = document.getElementById("workflowList");
|
|
157
|
-
if (!listEl) return;
|
|
158
|
-
const items = wfState.list || [];
|
|
159
|
-
if (!items.length) {
|
|
160
|
-
listEl.innerHTML =
|
|
161
|
-
'<div class="meta" style="padding:10px;">No workflows yet.</div>';
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
listEl.innerHTML = items
|
|
165
|
-
.map((w) => {
|
|
166
|
-
const active =
|
|
167
|
-
w.name === wfState.selectedName
|
|
168
|
-
? "background:var(--bg-2);border-color:var(--accent);"
|
|
169
|
-
: "";
|
|
170
|
-
const schedule = w.schedule
|
|
171
|
-
? escHtml(w.schedule)
|
|
172
|
-
: '<span style="opacity:0.7;">no schedule</span>';
|
|
173
|
-
const running = w.runState?.running
|
|
174
|
-
? '<span style="color:var(--green-hi);font-size:11px;">running</span>'
|
|
175
|
-
: "";
|
|
176
|
-
return `
|
|
177
|
-
<button class="btn-ghost workflow-row" data-workflow-name="${escHtml(w.name)}" style="width:100%;text-align:left;display:flex;flex-direction:column;gap:4px;padding:10px;margin-bottom:8px;${active}">
|
|
178
|
-
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;">
|
|
179
|
-
<strong style="font-size:13px;">${escHtml(w.name)}</strong>
|
|
180
|
-
${running}
|
|
181
|
-
</div>
|
|
182
|
-
<div style="font-size:11px;color:var(--text-3);">${w.enabled ? "enabled" : "disabled"} · ${w.stageCount || 0} stage(s)</div>
|
|
183
|
-
<div style="font-size:11px;color:var(--text-2);font-family:monospace;">${schedule}</div>
|
|
184
|
-
</button>
|
|
185
|
-
`;
|
|
186
|
-
})
|
|
187
|
-
.join("");
|
|
188
|
-
listEl.querySelectorAll(".workflow-row").forEach((btn) => {
|
|
189
|
-
btn.addEventListener("click", async () => {
|
|
190
|
-
const name = btn.dataset.workflowName || "";
|
|
191
|
-
await loadWorkflowItem(name);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async function loadWorkflowItem(name) {
|
|
197
|
-
if (!name) return;
|
|
198
|
-
try {
|
|
199
|
-
const data = await getJSON(
|
|
200
|
-
`/api/workflows/item?name=${encodeURIComponent(name)}`,
|
|
201
|
-
);
|
|
202
|
-
wfState.selectedName = name;
|
|
203
|
-
wfState.runStatus = data.runState || {};
|
|
204
|
-
renderWorkflowList();
|
|
205
|
-
renderWorkflowEditor({ name, ...(data.workflow || {}) }, data);
|
|
206
|
-
await loadWorkflowLog(name);
|
|
207
|
-
} catch (e) {
|
|
208
|
-
showNotification(`Failed to load workflow: ${e.message}`, "error");
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function buildAgentOptions(selected) {
|
|
213
|
-
const defaults = [
|
|
214
|
-
"crew-main",
|
|
215
|
-
"crew-pm",
|
|
216
|
-
"crew-qa",
|
|
217
|
-
"crew-coder",
|
|
218
|
-
"crew-coder-front",
|
|
219
|
-
"crew-coder-back",
|
|
220
|
-
"crew-copywriter",
|
|
221
|
-
];
|
|
222
|
-
const merged = Array.from(
|
|
223
|
-
new Set([...(knownAgents || []), ...defaults, selected || ""]),
|
|
224
|
-
)
|
|
225
|
-
.filter(Boolean)
|
|
226
|
-
.sort();
|
|
227
|
-
return merged
|
|
228
|
-
.map(
|
|
229
|
-
(id) =>
|
|
230
|
-
`<option value="${escHtml(id)}" ${id === selected ? "selected" : ""}>${escHtml(id)}</option>`,
|
|
231
|
-
)
|
|
232
|
-
.join("");
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function scheduleHint(cronExpr) {
|
|
236
|
-
const v = String(cronExpr || "").trim();
|
|
237
|
-
if (!v) return "No schedule set. Add a cron expression or use a preset.";
|
|
238
|
-
const presetHints = {
|
|
239
|
-
"*/15 * * * *": "Runs every 15 minutes",
|
|
240
|
-
"0 * * * *": "Runs hourly at minute 0",
|
|
241
|
-
"0 9 * * 1-5": "Runs weekdays at 9:00",
|
|
242
|
-
"0 9 * * *": "Runs daily at 9:00",
|
|
243
|
-
"0 0 * * 1": "Runs every Monday at midnight",
|
|
244
|
-
"0 8 1 * *": "Runs monthly on day 1 at 8:00",
|
|
245
|
-
};
|
|
246
|
-
return presetHints[v] || "Custom cron schedule";
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function buildCronPresetButtons() {
|
|
250
|
-
const presets = [
|
|
251
|
-
{ label: "Every 15m", cron: "*/15 * * * *" },
|
|
252
|
-
{ label: "Hourly", cron: "0 * * * *" },
|
|
253
|
-
{ label: "Daily 9am", cron: "0 9 * * *" },
|
|
254
|
-
{ label: "Weekdays 9am", cron: "0 9 * * 1-5" },
|
|
255
|
-
{ label: "Weekly Mon", cron: "0 0 * * 1" },
|
|
256
|
-
{ label: "Monthly", cron: "0 8 1 * *" },
|
|
257
|
-
];
|
|
258
|
-
return presets
|
|
259
|
-
.map(
|
|
260
|
-
(p) =>
|
|
261
|
-
`<button class="btn-ghost wf-cron-preset" data-cron="${escHtml(p.cron)}" style="font-size:11px;padding:4px 8px;">${escHtml(p.label)}</button>`,
|
|
262
|
-
)
|
|
263
|
-
.join("");
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function renderTemplateCards() {
|
|
267
|
-
return WORKFLOW_TEMPLATES.map(
|
|
268
|
-
(t) => `
|
|
269
|
-
<div style="border:1px solid var(--border);border-radius:8px;padding:10px;background:var(--bg-2);">
|
|
270
|
-
<div style="font-size:12px;font-weight:700;">${escHtml(t.name)}</div>
|
|
271
|
-
<div style="font-size:11px;color:var(--text-3);margin-top:4px;">${escHtml(t.description)}</div>
|
|
272
|
-
<div style="font-size:11px;color:var(--text-2);font-family:monospace;margin-top:6px;">${escHtml(t.schedule)}</div>
|
|
273
|
-
<div style="margin-top:8px;">
|
|
274
|
-
<button class="btn-ghost wf-apply-template" data-template-id="${escHtml(t.id)}" style="font-size:11px;">Use Template</button>
|
|
275
|
-
</div>
|
|
276
|
-
</div>
|
|
277
|
-
`,
|
|
278
|
-
).join("");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function renderOptionsList() {
|
|
282
|
-
const agents = knownAgents.length
|
|
283
|
-
? knownAgents
|
|
284
|
-
.map((a) => `<code style="font-size:11px;">${escHtml(a)}</code>`)
|
|
285
|
-
.join(" ")
|
|
286
|
-
: '<span style="font-size:11px;color:var(--text-3);">No agents loaded</span>';
|
|
287
|
-
const skills = knownSkills.length
|
|
288
|
-
? knownSkills
|
|
289
|
-
.map(
|
|
290
|
-
(s) =>
|
|
291
|
-
`<div style="font-size:11px;line-height:1.4;"><code>${escHtml(s.name)}</code> <span style="color:var(--text-3);">(${escHtml(s.type)})</span></div>`,
|
|
292
|
-
)
|
|
293
|
-
.join("")
|
|
294
|
-
: '<span style="font-size:11px;color:var(--text-3);">No skills loaded</span>';
|
|
295
|
-
return { agents, skills };
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function renderWorkflowEditor(wf, meta = {}) {
|
|
299
|
-
const editor = document.getElementById("workflowEditor");
|
|
300
|
-
if (!editor) return;
|
|
301
|
-
const workflow = {
|
|
302
|
-
...emptyWorkflow(),
|
|
303
|
-
...wf,
|
|
304
|
-
};
|
|
305
|
-
const stages =
|
|
306
|
-
Array.isArray(workflow.stages) && workflow.stages.length
|
|
307
|
-
? workflow.stages
|
|
308
|
-
: emptyWorkflow().stages;
|
|
309
|
-
wfState.editorWorkflow = {
|
|
310
|
-
...workflow,
|
|
311
|
-
stages: stages.map((s) => ({ ...s })),
|
|
312
|
-
};
|
|
313
|
-
const cronCmd = meta.cronExample
|
|
314
|
-
? escHtml(meta.cronExample)
|
|
315
|
-
: `*/15 * * * * cd ${escHtml(window.location.pathname || ".")} && node scripts/run-scheduled-pipeline.mjs ${escHtml(workflow.name || "my-workflow")}`;
|
|
316
|
-
const optionsList = renderOptionsList();
|
|
317
|
-
editor.innerHTML = `
|
|
318
|
-
<div class="card" style="display:flex;flex-direction:column;gap:12px;">
|
|
319
|
-
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
|
320
|
-
<button id="wfOpenTemplateLibraryBtn" class="btn-ghost" style="font-size:12px;">📚 Job Library</button>
|
|
321
|
-
<button id="wfOpenSkillGuideBtn" class="btn-ghost" style="font-size:12px;">🧩 Skills & Agent Options</button>
|
|
322
|
-
<button id="wfOpenJsonEditorBtn" class="btn-ghost" style="font-size:12px;">{ } Advanced JSON</button>
|
|
323
|
-
</div>
|
|
324
|
-
|
|
325
|
-
<div style="display:flex;gap:10px;flex-wrap:wrap;">
|
|
326
|
-
<div style="flex:1;min-width:260px;">
|
|
327
|
-
<label style="font-size:12px;font-weight:600;">Name</label>
|
|
328
|
-
<input id="wfName" type="text" value="${escHtml(workflow.name || "")}" placeholder="daily-research" style="width:100%;margin-top:4px;padding:8px 10px;" />
|
|
329
|
-
</div>
|
|
330
|
-
<div style="flex:1;min-width:260px;">
|
|
331
|
-
<label style="font-size:12px;font-weight:600;">Description</label>
|
|
332
|
-
<input id="wfDescription" type="text" value="${escHtml(workflow.description || "")}" placeholder="What this workflow does" style="width:100%;margin-top:4px;padding:8px 10px;" />
|
|
333
|
-
</div>
|
|
334
|
-
</div>
|
|
335
|
-
|
|
336
|
-
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;">
|
|
337
|
-
<div style="min-width:280px;flex:1;">
|
|
338
|
-
<label style="font-size:12px;font-weight:600;">Cron Schedule</label>
|
|
339
|
-
<input id="wfSchedule" type="text" value="${escHtml(workflow.schedule || "")}" placeholder="0 9 * * 1-5" style="width:100%;margin-top:4px;padding:8px 10px;font-family:monospace;" />
|
|
340
|
-
<div style="font-size:11px;color:var(--text-3);margin-top:4px;">Format: minute hour day month weekday</div>
|
|
341
|
-
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;">${buildCronPresetButtons()}</div>
|
|
342
|
-
<div id="wfScheduleHint" style="font-size:11px;color:var(--text-2);margin-top:6px;">${escHtml(scheduleHint(workflow.schedule))}</div>
|
|
343
|
-
</div>
|
|
344
|
-
<label style="display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;padding-bottom:8px;">
|
|
345
|
-
<input id="wfEnabled" type="checkbox" ${workflow.enabled ? "checked" : ""} />
|
|
346
|
-
Enabled
|
|
347
|
-
</label>
|
|
348
|
-
</div>
|
|
349
|
-
|
|
350
|
-
<div id="workflowTimezoneLabel" style="font-size:11px;color:var(--text-3);"></div>
|
|
351
|
-
|
|
352
|
-
<div>
|
|
353
|
-
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">
|
|
354
|
-
<label style="font-size:12px;font-weight:600;">Stages (wave-like, runs top to bottom)</label>
|
|
355
|
-
<button id="wfAddStageBtn" class="btn-ghost" style="font-size:12px;">+ Add Stage</button>
|
|
356
|
-
</div>
|
|
357
|
-
<div id="wfStagesWrap" style="display:flex;flex-direction:column;gap:8px;">
|
|
358
|
-
${stages
|
|
359
|
-
.map(
|
|
360
|
-
(s, idx) => `
|
|
361
|
-
<div class="wf-stage-row" data-stage-index="${idx}" style="border:1px solid var(--border);border-radius:8px;padding:10px;background:var(--bg-2);">
|
|
362
|
-
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px;">
|
|
363
|
-
<strong style="font-size:12px;">Stage ${idx + 1}</strong>
|
|
364
|
-
<div style="display:flex;gap:6px;flex-wrap:wrap;">
|
|
365
|
-
<button class="btn-ghost wf-move-stage-up" data-stage-index="${idx}" style="font-size:11px;padding:4px 8px;">↑</button>
|
|
366
|
-
<button class="btn-ghost wf-move-stage-down" data-stage-index="${idx}" style="font-size:11px;padding:4px 8px;">↓</button>
|
|
367
|
-
<button class="btn-ghost wf-duplicate-stage" data-stage-index="${idx}" style="font-size:11px;padding:4px 8px;">Duplicate</button>
|
|
368
|
-
<button class="btn-red wf-remove-stage" data-stage-index="${idx}" style="font-size:11px;padding:4px 8px;">Remove</button>
|
|
369
|
-
</div>
|
|
370
|
-
</div>
|
|
371
|
-
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px;">
|
|
372
|
-
<div style="flex:1;min-width:220px;">
|
|
373
|
-
<label style="font-size:11px;font-weight:600;">Agent</label>
|
|
374
|
-
<select class="wf-agent" style="width:100%;margin-top:4px;padding:6px 8px;">${buildAgentOptions(s.agent)}</select>
|
|
375
|
-
</div>
|
|
376
|
-
<div style="width:180px;">
|
|
377
|
-
<label style="font-size:11px;font-weight:600;">Tool hint (optional)</label>
|
|
378
|
-
<input class="wf-tool" type="text" value="${escHtml(s.tool || "")}" placeholder="write_file" style="width:100%;margin-top:4px;padding:6px 8px;" />
|
|
379
|
-
</div>
|
|
380
|
-
</div>
|
|
381
|
-
<div>
|
|
382
|
-
<label style="font-size:11px;font-weight:600;">Task</label>
|
|
383
|
-
<textarea class="wf-task" rows="3" style="width:100%;margin-top:4px;padding:8px 10px;font-family:monospace;">${escHtml(s.task || "")}</textarea>
|
|
384
|
-
</div>
|
|
385
|
-
</div>
|
|
386
|
-
`,
|
|
387
|
-
)
|
|
388
|
-
.join("")}
|
|
389
|
-
</div>
|
|
390
|
-
</div>
|
|
391
|
-
|
|
392
|
-
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
|
393
|
-
<button id="wfSaveBtn" class="btn">Save Workflow</button>
|
|
394
|
-
<button id="wfRunBtn" class="btn-green">Run Now</button>
|
|
395
|
-
<button id="wfDeleteBtn" class="btn-red">Delete</button>
|
|
396
|
-
<button id="wfNewBtn" class="btn-ghost">New</button>
|
|
397
|
-
<button id="wfRefreshBtn" class="btn-ghost">Refresh</button>
|
|
398
|
-
</div>
|
|
399
|
-
|
|
400
|
-
<div>
|
|
401
|
-
<div style="font-size:11px;font-weight:600;color:var(--text-2);margin-bottom:4px;">Crontab example</div>
|
|
402
|
-
<code style="display:block;background:var(--bg-2);border:1px solid var(--border);padding:8px 10px;border-radius:6px;overflow:auto;white-space:nowrap;">${cronCmd}</code>
|
|
403
|
-
</div>
|
|
404
|
-
|
|
405
|
-
<div id="wfLibraryModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:12000;align-items:center;justify-content:center;padding:18px;">
|
|
406
|
-
<div style="width:min(960px,100%);max-height:85vh;overflow:auto;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:14px;">
|
|
407
|
-
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;">
|
|
408
|
-
<div style="font-size:14px;font-weight:700;">Workflow Job Library</div>
|
|
409
|
-
<button id="wfCloseLibraryBtn" class="btn-ghost" style="font-size:12px;">Close</button>
|
|
410
|
-
</div>
|
|
411
|
-
<div style="font-size:12px;color:var(--text-2);margin-bottom:10px;">Pick a starter template, then customize stages/tasks.</div>
|
|
412
|
-
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:10px;">${renderTemplateCards()}</div>
|
|
413
|
-
<hr style="border:none;border-top:1px solid var(--border);margin:14px 0;" />
|
|
414
|
-
<div style="font-size:12px;font-weight:700;margin-bottom:6px;">Available Agents</div>
|
|
415
|
-
<div style="display:flex;gap:6px;flex-wrap:wrap;">${optionsList.agents}</div>
|
|
416
|
-
<div style="font-size:12px;font-weight:700;margin:12px 0 6px;">Available Skills</div>
|
|
417
|
-
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:6px;">${optionsList.skills}</div>
|
|
418
|
-
</div>
|
|
419
|
-
</div>
|
|
420
|
-
|
|
421
|
-
<div id="wfJsonModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:12000;align-items:center;justify-content:center;padding:18px;">
|
|
422
|
-
<div style="width:min(920px,100%);max-height:85vh;overflow:auto;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:14px;">
|
|
423
|
-
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;">
|
|
424
|
-
<div style="font-size:14px;font-weight:700;">Advanced Workflow JSON</div>
|
|
425
|
-
<button id="wfCloseJsonBtn" class="btn-ghost" style="font-size:12px;">Close</button>
|
|
426
|
-
</div>
|
|
427
|
-
<div style="font-size:12px;color:var(--text-2);margin-bottom:8px;">Edit raw JSON. Supports both <code>stages</code> and <code>steps</code>.</div>
|
|
428
|
-
<textarea id="wfJsonTextarea" rows="18" style="width:100%;font-family:monospace;font-size:12px;padding:10px;border:1px solid var(--border);border-radius:8px;background:var(--bg-2);"></textarea>
|
|
429
|
-
<div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap;">
|
|
430
|
-
<button id="wfApplyJsonBtn" class="btn-green">Apply JSON</button>
|
|
431
|
-
</div>
|
|
432
|
-
</div>
|
|
433
|
-
</div>
|
|
434
|
-
</div>
|
|
435
|
-
`;
|
|
436
|
-
|
|
437
|
-
const tzEl = document.getElementById("workflowTimezoneLabel");
|
|
438
|
-
if (tzEl)
|
|
439
|
-
tzEl.textContent = `Local timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`;
|
|
440
|
-
|
|
441
|
-
wireWorkflowEditorEvents();
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function collectWorkflowFromForm(options = {}) {
|
|
445
|
-
const { includeIncompleteStages = false } = options;
|
|
446
|
-
const name = (document.getElementById("wfName")?.value || "").trim();
|
|
447
|
-
const description = (
|
|
448
|
-
document.getElementById("wfDescription")?.value || ""
|
|
449
|
-
).trim();
|
|
450
|
-
const schedule = (document.getElementById("wfSchedule")?.value || "").trim();
|
|
451
|
-
const enabled = !!document.getElementById("wfEnabled")?.checked;
|
|
452
|
-
const rows = Array.from(document.querySelectorAll(".wf-stage-row"));
|
|
453
|
-
const stages = rows
|
|
454
|
-
.map((row) => {
|
|
455
|
-
const agent = row.querySelector(".wf-agent")?.value?.trim() || "";
|
|
456
|
-
const tool = row.querySelector(".wf-tool")?.value?.trim() || "";
|
|
457
|
-
const task = row.querySelector(".wf-task")?.value?.trim() || "";
|
|
458
|
-
return { agent, task, ...(tool ? { tool } : {}) };
|
|
459
|
-
})
|
|
460
|
-
.filter((s) => includeIncompleteStages || (s.agent && s.task));
|
|
461
|
-
const existingSteps = Array.isArray(wfState.editorWorkflow?.steps)
|
|
462
|
-
? wfState.editorWorkflow.steps
|
|
463
|
-
: [];
|
|
464
|
-
return {
|
|
465
|
-
name,
|
|
466
|
-
workflow: {
|
|
467
|
-
description,
|
|
468
|
-
enabled,
|
|
469
|
-
schedule,
|
|
470
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
471
|
-
stages,
|
|
472
|
-
...(existingSteps.length ? { steps: existingSteps } : {}),
|
|
473
|
-
},
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function wireWorkflowEditorEvents() {
|
|
478
|
-
document
|
|
479
|
-
.getElementById("wfOpenTemplateLibraryBtn")
|
|
480
|
-
?.addEventListener("click", (e) => {
|
|
481
|
-
e.preventDefault();
|
|
482
|
-
const modal = document.getElementById("wfLibraryModal");
|
|
483
|
-
if (modal) modal.style.display = "flex";
|
|
484
|
-
});
|
|
485
|
-
document
|
|
486
|
-
.getElementById("wfOpenSkillGuideBtn")
|
|
487
|
-
?.addEventListener("click", (e) => {
|
|
488
|
-
e.preventDefault();
|
|
489
|
-
const modal = document.getElementById("wfLibraryModal");
|
|
490
|
-
if (modal) modal.style.display = "flex";
|
|
491
|
-
});
|
|
492
|
-
document
|
|
493
|
-
.getElementById("wfCloseLibraryBtn")
|
|
494
|
-
?.addEventListener("click", (e) => {
|
|
495
|
-
e.preventDefault();
|
|
496
|
-
const modal = document.getElementById("wfLibraryModal");
|
|
497
|
-
if (modal) modal.style.display = "none";
|
|
498
|
-
});
|
|
499
|
-
document.getElementById("wfLibraryModal")?.addEventListener("click", (e) => {
|
|
500
|
-
if (e.target?.id === "wfLibraryModal") {
|
|
501
|
-
e.currentTarget.style.display = "none";
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
document
|
|
505
|
-
.getElementById("wfOpenJsonEditorBtn")
|
|
506
|
-
?.addEventListener("click", (e) => {
|
|
507
|
-
e.preventDefault();
|
|
508
|
-
const current = collectWorkflowFromForm();
|
|
509
|
-
const raw = {
|
|
510
|
-
...current.workflow,
|
|
511
|
-
...(wfState.editorWorkflow?.steps
|
|
512
|
-
? { steps: wfState.editorWorkflow.steps }
|
|
513
|
-
: {}),
|
|
514
|
-
};
|
|
515
|
-
const ta = document.getElementById("wfJsonTextarea");
|
|
516
|
-
if (ta) ta.value = JSON.stringify(raw, null, 2);
|
|
517
|
-
const modal = document.getElementById("wfJsonModal");
|
|
518
|
-
if (modal) modal.style.display = "flex";
|
|
519
|
-
});
|
|
520
|
-
document.getElementById("wfCloseJsonBtn")?.addEventListener("click", (e) => {
|
|
521
|
-
e.preventDefault();
|
|
522
|
-
const modal = document.getElementById("wfJsonModal");
|
|
523
|
-
if (modal) modal.style.display = "none";
|
|
524
|
-
});
|
|
525
|
-
document.getElementById("wfJsonModal")?.addEventListener("click", (e) => {
|
|
526
|
-
if (e.target?.id === "wfJsonModal") {
|
|
527
|
-
e.currentTarget.style.display = "none";
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
document.getElementById("wfApplyJsonBtn")?.addEventListener("click", (e) => {
|
|
531
|
-
e.preventDefault();
|
|
532
|
-
try {
|
|
533
|
-
const ta = document.getElementById("wfJsonTextarea");
|
|
534
|
-
const parsed = JSON.parse(ta?.value || "{}");
|
|
535
|
-
const current = collectWorkflowFromForm();
|
|
536
|
-
const next = {
|
|
537
|
-
name: current.name,
|
|
538
|
-
description: parsed.description || current.workflow.description,
|
|
539
|
-
enabled: parsed.enabled ?? current.workflow.enabled,
|
|
540
|
-
schedule: parsed.schedule || current.workflow.schedule,
|
|
541
|
-
timezone:
|
|
542
|
-
parsed.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
543
|
-
stages:
|
|
544
|
-
Array.isArray(parsed.stages) && parsed.stages.length
|
|
545
|
-
? parsed.stages
|
|
546
|
-
: current.workflow.stages,
|
|
547
|
-
...(Array.isArray(parsed.steps) ? { steps: parsed.steps } : {}),
|
|
548
|
-
};
|
|
549
|
-
renderWorkflowEditor(next);
|
|
550
|
-
const modal = document.getElementById("wfJsonModal");
|
|
551
|
-
if (modal) modal.style.display = "none";
|
|
552
|
-
showNotification("Applied advanced JSON", "success");
|
|
553
|
-
} catch (err) {
|
|
554
|
-
showNotification(`Invalid JSON: ${err.message}`, "error");
|
|
555
|
-
}
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
document.querySelectorAll(".wf-apply-template").forEach((btn) => {
|
|
559
|
-
btn.addEventListener("click", (e) => {
|
|
560
|
-
e.preventDefault();
|
|
561
|
-
const templateId = btn.dataset.templateId || "";
|
|
562
|
-
const template = WORKFLOW_TEMPLATES.find((t) => t.id === templateId);
|
|
563
|
-
if (!template) return;
|
|
564
|
-
const current = collectWorkflowFromForm();
|
|
565
|
-
const next = {
|
|
566
|
-
name: current.name || template.id,
|
|
567
|
-
description: template.description,
|
|
568
|
-
enabled: current.workflow.enabled,
|
|
569
|
-
schedule: template.schedule,
|
|
570
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
571
|
-
stages: template.stages.map((s) => ({ ...s, tool: s.tool || "" })),
|
|
572
|
-
};
|
|
573
|
-
renderWorkflowEditor(next);
|
|
574
|
-
const modal = document.getElementById("wfLibraryModal");
|
|
575
|
-
if (modal) modal.style.display = "none";
|
|
576
|
-
showNotification(`Applied template: ${template.name}`, "success");
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
document.querySelectorAll(".wf-cron-preset").forEach((btn) => {
|
|
581
|
-
btn.addEventListener("click", (e) => {
|
|
582
|
-
e.preventDefault();
|
|
583
|
-
const cron = btn.dataset.cron || "";
|
|
584
|
-
const input = document.getElementById("wfSchedule");
|
|
585
|
-
const hint = document.getElementById("wfScheduleHint");
|
|
586
|
-
if (input) input.value = cron;
|
|
587
|
-
if (hint) hint.textContent = scheduleHint(cron);
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
document.getElementById("wfSchedule")?.addEventListener("input", (e) => {
|
|
591
|
-
const hint = document.getElementById("wfScheduleHint");
|
|
592
|
-
if (hint) hint.textContent = scheduleHint(e.target.value);
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
document.getElementById("wfAddStageBtn")?.addEventListener("click", (e) => {
|
|
596
|
-
e.preventDefault();
|
|
597
|
-
const current = collectWorkflowFromForm({ includeIncompleteStages: true });
|
|
598
|
-
current.workflow.stages.push({ agent: "crew-main", task: "", tool: "" });
|
|
599
|
-
renderWorkflowEditor({ name: current.name, ...current.workflow });
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
document.querySelectorAll(".wf-remove-stage").forEach((btn) => {
|
|
603
|
-
btn.addEventListener("click", (e) => {
|
|
604
|
-
e.preventDefault();
|
|
605
|
-
const idx = Number(btn.dataset.stageIndex || "-1");
|
|
606
|
-
const current = collectWorkflowFromForm({ includeIncompleteStages: true });
|
|
607
|
-
current.workflow.stages = current.workflow.stages.filter(
|
|
608
|
-
(_, i) => i !== idx,
|
|
609
|
-
);
|
|
610
|
-
if (!current.workflow.stages.length) {
|
|
611
|
-
current.workflow.stages = [{ agent: "crew-main", task: "", tool: "" }];
|
|
612
|
-
}
|
|
613
|
-
renderWorkflowEditor({ name: current.name, ...current.workflow });
|
|
614
|
-
});
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
document.querySelectorAll(".wf-move-stage-up").forEach((btn) => {
|
|
618
|
-
btn.addEventListener("click", (e) => {
|
|
619
|
-
e.preventDefault();
|
|
620
|
-
const idx = Number(btn.dataset.stageIndex || "-1");
|
|
621
|
-
if (idx <= 0) return;
|
|
622
|
-
const current = collectWorkflowFromForm({ includeIncompleteStages: true });
|
|
623
|
-
const [stage] = current.workflow.stages.splice(idx, 1);
|
|
624
|
-
current.workflow.stages.splice(idx - 1, 0, stage);
|
|
625
|
-
renderWorkflowEditor({ name: current.name, ...current.workflow });
|
|
626
|
-
});
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
document.querySelectorAll(".wf-move-stage-down").forEach((btn) => {
|
|
630
|
-
btn.addEventListener("click", (e) => {
|
|
631
|
-
e.preventDefault();
|
|
632
|
-
const idx = Number(btn.dataset.stageIndex || "-1");
|
|
633
|
-
const current = collectWorkflowFromForm({ includeIncompleteStages: true });
|
|
634
|
-
if (idx < 0 || idx >= current.workflow.stages.length - 1) return;
|
|
635
|
-
const [stage] = current.workflow.stages.splice(idx, 1);
|
|
636
|
-
current.workflow.stages.splice(idx + 1, 0, stage);
|
|
637
|
-
renderWorkflowEditor({ name: current.name, ...current.workflow });
|
|
638
|
-
});
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
document.querySelectorAll(".wf-duplicate-stage").forEach((btn) => {
|
|
642
|
-
btn.addEventListener("click", (e) => {
|
|
643
|
-
e.preventDefault();
|
|
644
|
-
const idx = Number(btn.dataset.stageIndex || "-1");
|
|
645
|
-
const current = collectWorkflowFromForm({ includeIncompleteStages: true });
|
|
646
|
-
if (idx < 0 || idx >= current.workflow.stages.length) return;
|
|
647
|
-
const source = current.workflow.stages[idx];
|
|
648
|
-
current.workflow.stages.splice(idx + 1, 0, { ...source });
|
|
649
|
-
renderWorkflowEditor({ name: current.name, ...current.workflow });
|
|
650
|
-
});
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
document
|
|
654
|
-
.getElementById("wfSaveBtn")
|
|
655
|
-
?.addEventListener("click", saveWorkflowFromForm);
|
|
656
|
-
document
|
|
657
|
-
.getElementById("wfRunBtn")
|
|
658
|
-
?.addEventListener("click", runWorkflowFromForm);
|
|
659
|
-
document
|
|
660
|
-
.getElementById("wfDeleteBtn")
|
|
661
|
-
?.addEventListener("click", deleteWorkflowFromForm);
|
|
662
|
-
document.getElementById("wfNewBtn")?.addEventListener("click", () => {
|
|
663
|
-
wfState.selectedName = "";
|
|
664
|
-
renderWorkflowEditor(emptyWorkflow());
|
|
665
|
-
const logEl = document.getElementById("workflowLog");
|
|
666
|
-
if (logEl) logEl.textContent = "";
|
|
667
|
-
});
|
|
668
|
-
document
|
|
669
|
-
.getElementById("wfRefreshBtn")
|
|
670
|
-
?.addEventListener("click", async () => {
|
|
671
|
-
await loadWorkflowList();
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
async function saveWorkflowFromForm() {
|
|
676
|
-
const payload = collectWorkflowFromForm();
|
|
677
|
-
if (!payload.name) {
|
|
678
|
-
showNotification("Workflow name is required", "error");
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
if (!payload.workflow.stages.length) {
|
|
682
|
-
showNotification("Add at least one stage", "error");
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
|
-
try {
|
|
686
|
-
await postJSON("/api/workflows/save", payload);
|
|
687
|
-
wfState.selectedName = payload.name;
|
|
688
|
-
showNotification(`Saved workflow: ${payload.name}`, "success");
|
|
689
|
-
await loadWorkflowList();
|
|
690
|
-
await loadWorkflowItem(payload.name);
|
|
691
|
-
} catch (e) {
|
|
692
|
-
showNotification(`Save failed: ${e.message}`, "error");
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
async function runWorkflowFromForm() {
|
|
697
|
-
const payload = collectWorkflowFromForm();
|
|
698
|
-
if (!payload.name) {
|
|
699
|
-
showNotification("Save workflow first (name required)", "warning");
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
try {
|
|
703
|
-
const data = await postJSON("/api/workflows/run", { name: payload.name });
|
|
704
|
-
showNotification(
|
|
705
|
-
`Started ${payload.name}${data.pid ? ` (pid ${data.pid})` : ""}`,
|
|
706
|
-
"success",
|
|
707
|
-
);
|
|
708
|
-
await loadWorkflowItem(payload.name);
|
|
709
|
-
} catch (e) {
|
|
710
|
-
showNotification(`Run failed: ${e.message}`, "error");
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
async function deleteWorkflowFromForm() {
|
|
715
|
-
const payload = collectWorkflowFromForm();
|
|
716
|
-
if (!payload.name) {
|
|
717
|
-
showNotification("No workflow selected", "warning");
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
if (!confirm(`Delete workflow "${payload.name}"?`)) return;
|
|
721
|
-
try {
|
|
722
|
-
await postJSON("/api/workflows/delete", { name: payload.name });
|
|
723
|
-
showNotification(`Deleted ${payload.name}`, "success");
|
|
724
|
-
wfState.selectedName = "";
|
|
725
|
-
await loadWorkflowList();
|
|
726
|
-
renderWorkflowEditor(emptyWorkflow());
|
|
727
|
-
const logEl = document.getElementById("workflowLog");
|
|
728
|
-
if (logEl) logEl.textContent = "";
|
|
729
|
-
} catch (e) {
|
|
730
|
-
showNotification(`Delete failed: ${e.message}`, "error");
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
async function loadWorkflowLog(name) {
|
|
735
|
-
const logEl = document.getElementById("workflowLog");
|
|
736
|
-
if (!logEl || !name) return;
|
|
737
|
-
try {
|
|
738
|
-
const data = await getJSON(
|
|
739
|
-
`/api/workflows/log?name=${encodeURIComponent(name)}&limit=120`,
|
|
740
|
-
);
|
|
741
|
-
const lines = data.lines || [];
|
|
742
|
-
logEl.textContent = lines.length ? lines.join("\n") : "No log lines yet.";
|
|
743
|
-
logEl.scrollTop = logEl.scrollHeight;
|
|
744
|
-
} catch (e) {
|
|
745
|
-
logEl.textContent = `Failed to load log: ${e.message}`;
|
|
746
|
-
}
|
|
747
|
-
}
|