@marktoflow/gui 2.0.0-alpha.5 → 2.0.1
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 +48 -180
- package/client.log +0 -0
- package/dist/client/assets/index-DQeR1ew6.css +1 -0
- package/dist/client/assets/index-LbIVPHbD.js +833 -0
- package/dist/client/assets/index-LbIVPHbD.js.map +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/marktoflow-logo.png +0 -0
- package/dist/server/index.js +31 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/admin.js +95 -0
- package/dist/server/routes/admin.js.map +1 -0
- package/dist/server/routes/ai.js +2 -2
- package/dist/server/routes/ai.js.map +1 -1
- package/dist/server/routes/collaboration.js +104 -0
- package/dist/server/routes/collaboration.js.map +1 -0
- package/dist/server/routes/execute.js +181 -14
- package/dist/server/routes/execute.js.map +1 -1
- package/dist/server/routes/form.js +160 -0
- package/dist/server/routes/form.js.map +1 -0
- package/dist/server/routes/settings.js +90 -0
- package/dist/server/routes/settings.js.map +1 -0
- package/dist/server/routes/templates.js +106 -0
- package/dist/server/routes/templates.js.map +1 -0
- package/dist/server/routes/versions.js +101 -0
- package/dist/server/routes/versions.js.map +1 -0
- package/dist/server/services/AIService.js +85 -2
- package/dist/server/services/AIService.js.map +1 -1
- package/dist/server/services/ExecutionManager.js +571 -0
- package/dist/server/services/ExecutionManager.js.map +1 -0
- package/dist/server/services/VersionService.js +65 -0
- package/dist/server/services/VersionService.js.map +1 -0
- package/dist/server/services/WorkflowService.js +8 -2
- package/dist/server/services/WorkflowService.js.map +1 -1
- package/dist/server/services/agents/copilot-provider.js +32 -0
- package/dist/server/services/agents/copilot-provider.js.map +1 -1
- package/dist/server/websocket/index.js +42 -0
- package/dist/server/websocket/index.js.map +1 -1
- package/dist/shared/constants.js +9 -0
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/settings.js +51 -0
- package/dist/shared/settings.js.map +1 -0
- package/package.json +14 -10
- package/public/marktoflow-logo.png +0 -0
- package/server.log +0 -0
- package/tests/integration/fixtures/test-workflow.md +6 -0
- package/.turbo/turbo-build.log +0 -42
- package/dist/client/assets/index-CM44OayM.js +0 -704
- package/dist/client/assets/index-CM44OayM.js.map +0 -1
- package/dist/client/assets/index-Dru63gi6.css +0 -1
- package/marktoflow-gui-2.0.0-alpha.5.tgz +0 -0
- package/playwright.config.ts +0 -27
- package/postcss.config.js +0 -6
- package/src/client/App.tsx +0 -520
- package/src/client/components/Canvas/Canvas.tsx +0 -425
- package/src/client/components/Canvas/ExecutionOverlay.tsx +0 -935
- package/src/client/components/Canvas/ForEachNode.tsx +0 -152
- package/src/client/components/Canvas/IfElseNode.tsx +0 -141
- package/src/client/components/Canvas/NodeContextMenu.tsx +0 -192
- package/src/client/components/Canvas/OutputNode.tsx +0 -111
- package/src/client/components/Canvas/ParallelNode.tsx +0 -157
- package/src/client/components/Canvas/StepNode.tsx +0 -106
- package/src/client/components/Canvas/SubWorkflowNode.tsx +0 -141
- package/src/client/components/Canvas/SwitchNode.tsx +0 -185
- package/src/client/components/Canvas/Toolbar.tsx +0 -227
- package/src/client/components/Canvas/TransformNode.tsx +0 -194
- package/src/client/components/Canvas/TriggerNode.tsx +0 -128
- package/src/client/components/Canvas/TryCatchNode.tsx +0 -164
- package/src/client/components/Canvas/WhileNode.tsx +0 -161
- package/src/client/components/Canvas/index.ts +0 -24
- package/src/client/components/Debug/VariableInspector.tsx +0 -148
- package/src/client/components/Editor/InputsEditor.tsx +0 -458
- package/src/client/components/Editor/NewStepWizard.tsx +0 -344
- package/src/client/components/Editor/StepEditor.tsx +0 -532
- package/src/client/components/Editor/YamlEditor.tsx +0 -160
- package/src/client/components/Panels/PropertiesPanel.tsx +0 -589
- package/src/client/components/Prompt/ChangePreview.tsx +0 -281
- package/src/client/components/Prompt/PromptHistoryPanel.tsx +0 -209
- package/src/client/components/Prompt/PromptInput.tsx +0 -110
- package/src/client/components/Settings/ProviderSwitcher.tsx +0 -228
- package/src/client/components/Sidebar/ImportDialog.tsx +0 -257
- package/src/client/components/Sidebar/Sidebar.tsx +0 -362
- package/src/client/components/common/Breadcrumb.tsx +0 -40
- package/src/client/components/common/Button.tsx +0 -68
- package/src/client/components/common/ContextMenu.tsx +0 -202
- package/src/client/components/common/KeyboardShortcuts.tsx +0 -149
- package/src/client/components/common/Modal.tsx +0 -93
- package/src/client/components/common/Tabs.tsx +0 -57
- package/src/client/components/common/ThemeToggle.tsx +0 -63
- package/src/client/components/index.ts +0 -32
- package/src/client/hooks/index.ts +0 -4
- package/src/client/hooks/useAIPrompt.ts +0 -108
- package/src/client/hooks/useCanvas.ts +0 -247
- package/src/client/hooks/useWebSocket.ts +0 -164
- package/src/client/hooks/useWorkflow.ts +0 -138
- package/src/client/main.tsx +0 -10
- package/src/client/stores/agentStore.ts +0 -109
- package/src/client/stores/canvasStore.ts +0 -348
- package/src/client/stores/editorStore.ts +0 -133
- package/src/client/stores/executionStore.ts +0 -502
- package/src/client/stores/index.ts +0 -4
- package/src/client/stores/layoutStore.ts +0 -103
- package/src/client/stores/navigationStore.ts +0 -49
- package/src/client/stores/promptStore.ts +0 -113
- package/src/client/stores/themeStore.ts +0 -75
- package/src/client/stores/workflowStore.ts +0 -185
- package/src/client/styles/globals.css +0 -452
- package/src/client/utils/cn.ts +0 -9
- package/src/client/utils/index.ts +0 -4
- package/src/client/utils/platform.ts +0 -46
- package/src/client/utils/serviceIcons.tsx +0 -97
- package/src/client/utils/stepValidation.ts +0 -155
- package/src/client/utils/workflowToGraph.ts +0 -523
- package/src/server/index.ts +0 -137
- package/src/server/routes/ai.ts +0 -91
- package/src/server/routes/execute.ts +0 -71
- package/src/server/routes/executions.ts +0 -136
- package/src/server/routes/tools.ts +0 -970
- package/src/server/routes/workflows.ts +0 -147
- package/src/server/services/AIService.ts +0 -105
- package/src/server/services/FileWatcher.ts +0 -69
- package/src/server/services/WorkflowService.ts +0 -601
- package/src/server/services/agents/claude-code-provider.ts +0 -320
- package/src/server/services/agents/claude-provider.ts +0 -248
- package/src/server/services/agents/codex-provider.ts +0 -398
- package/src/server/services/agents/copilot-provider.ts +0 -311
- package/src/server/services/agents/demo-provider.ts +0 -184
- package/src/server/services/agents/index.ts +0 -31
- package/src/server/services/agents/ollama-provider.ts +0 -267
- package/src/server/services/agents/prompts.ts +0 -509
- package/src/server/services/agents/registry.ts +0 -310
- package/src/server/services/agents/types.ts +0 -146
- package/src/server/websocket/index.ts +0 -117
- package/src/shared/constants.ts +0 -180
- package/src/shared/types.ts +0 -179
- package/tailwind.config.ts +0 -73
- package/tests/e2e/app.spec.ts +0 -90
- package/tests/e2e/canvas.spec.ts +0 -128
- package/tests/e2e/workflow.spec.ts +0 -185
- package/tests/integration/api.test.ts +0 -452
- package/tests/integration/testApp.ts +0 -31
- package/tests/setup.ts +0 -72
- package/tests/unit/ForEachNode.test.tsx +0 -308
- package/tests/unit/IfElseNode.test.tsx +0 -235
- package/tests/unit/ParallelNode.test.tsx +0 -344
- package/tests/unit/SwitchNode.test.tsx +0 -327
- package/tests/unit/TransformNode.test.tsx +0 -386
- package/tests/unit/TryCatchNode.test.tsx +0 -243
- package/tests/unit/WhileNode.test.tsx +0 -230
- package/tests/unit/agentStore.test.ts +0 -218
- package/tests/unit/canvasStore.test.ts +0 -502
- package/tests/unit/codexProvider.test.ts +0 -399
- package/tests/unit/components.test.tsx +0 -151
- package/tests/unit/executionStore.test.ts +0 -567
- package/tests/unit/layoutStore.test.ts +0 -194
- package/tests/unit/navigationStore.test.ts +0 -152
- package/tests/unit/platform.test.ts +0 -118
- package/tests/unit/serviceIcons.test.ts +0 -197
- package/tests/unit/stepValidation.test.ts +0 -226
- package/tests/unit/themeStore.test.ts +0 -141
- package/tests/unit/workflowToGraph.test.ts +0 -311
- package/tsconfig.json +0 -29
- package/tsconfig.server.json +0 -28
- package/vite.config.ts +0 -31
- package/vitest.config.ts +0 -26
package/src/server/index.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import express from 'express';
|
|
4
|
-
import cors from 'cors';
|
|
5
|
-
import { createServer, type Server } from 'http';
|
|
6
|
-
import { Server as SocketIOServer } from 'socket.io';
|
|
7
|
-
import { join } from 'path';
|
|
8
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
9
|
-
import { StateStore } from '@marktoflow/core';
|
|
10
|
-
import { workflowRoutes } from './routes/workflows.js';
|
|
11
|
-
import { aiRoutes } from './routes/ai.js';
|
|
12
|
-
import { executeRoutes } from './routes/execute.js';
|
|
13
|
-
import { toolsRoutes } from './routes/tools.js';
|
|
14
|
-
import { executionRoutes } from './routes/executions.js';
|
|
15
|
-
import { setupWebSocket } from './websocket/index.js';
|
|
16
|
-
import { FileWatcher } from './services/FileWatcher.js';
|
|
17
|
-
|
|
18
|
-
export interface ServerOptions {
|
|
19
|
-
port?: number;
|
|
20
|
-
workflowDir?: string;
|
|
21
|
-
staticDir?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let httpServer: Server | null = null;
|
|
25
|
-
let fileWatcher: FileWatcher | null = null;
|
|
26
|
-
let stateStore: StateStore | null = null;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Get the StateStore instance
|
|
30
|
-
*/
|
|
31
|
-
export function getStateStore(): StateStore {
|
|
32
|
-
if (!stateStore) {
|
|
33
|
-
throw new Error('StateStore not initialized. Call startServer() first.');
|
|
34
|
-
}
|
|
35
|
-
return stateStore;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Start the GUI server programmatically
|
|
40
|
-
*/
|
|
41
|
-
export async function startServer(options: ServerOptions = {}): Promise<Server> {
|
|
42
|
-
const PORT = options.port || parseInt(process.env.PORT || '3001', 10);
|
|
43
|
-
const WORKFLOW_DIR = options.workflowDir || process.env.WORKFLOW_DIR || process.cwd();
|
|
44
|
-
const STATIC_DIR = options.staticDir || process.env.STATIC_DIR;
|
|
45
|
-
|
|
46
|
-
// Initialize StateStore
|
|
47
|
-
const stateDir = join(WORKFLOW_DIR, '.marktoflow', 'state');
|
|
48
|
-
mkdirSync(stateDir, { recursive: true });
|
|
49
|
-
stateStore = new StateStore(join(stateDir, 'workflow-state.db'));
|
|
50
|
-
|
|
51
|
-
const app = express();
|
|
52
|
-
httpServer = createServer(app);
|
|
53
|
-
const io = new SocketIOServer(httpServer, {
|
|
54
|
-
cors: {
|
|
55
|
-
origin: ['http://localhost:5173', 'http://localhost:3000', `http://localhost:${PORT}`],
|
|
56
|
-
methods: ['GET', 'POST'],
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Middleware
|
|
61
|
-
app.use(cors());
|
|
62
|
-
app.use(express.json());
|
|
63
|
-
|
|
64
|
-
// Routes
|
|
65
|
-
app.use('/api/workflows', workflowRoutes);
|
|
66
|
-
app.use('/api/ai', aiRoutes);
|
|
67
|
-
app.use('/api/execute', executeRoutes);
|
|
68
|
-
app.use('/api/executions', executionRoutes);
|
|
69
|
-
app.use('/api/tools', toolsRoutes);
|
|
70
|
-
|
|
71
|
-
// Health check
|
|
72
|
-
app.get('/api/health', (_req, res) => {
|
|
73
|
-
res.json({ status: 'ok', version: '2.0.0-alpha.2' });
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Serve static files if static dir is provided
|
|
77
|
-
if (STATIC_DIR && existsSync(STATIC_DIR)) {
|
|
78
|
-
app.use(express.static(STATIC_DIR));
|
|
79
|
-
// SPA fallback
|
|
80
|
-
app.get('*', (_req, res) => {
|
|
81
|
-
res.sendFile(join(STATIC_DIR, 'index.html'));
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// WebSocket
|
|
86
|
-
setupWebSocket(io);
|
|
87
|
-
|
|
88
|
-
// File watcher for live updates
|
|
89
|
-
fileWatcher = new FileWatcher(WORKFLOW_DIR, io);
|
|
90
|
-
|
|
91
|
-
return new Promise((resolve) => {
|
|
92
|
-
httpServer!.listen(PORT, () => {
|
|
93
|
-
console.log(`
|
|
94
|
-
╔══════════════════════════════════════════════════════════╗
|
|
95
|
-
║ ║
|
|
96
|
-
║ Marktoflow GUI Server ║
|
|
97
|
-
║ ║
|
|
98
|
-
║ Server: http://localhost:${String(PORT).padEnd(25)}║
|
|
99
|
-
║ Workflows: ${WORKFLOW_DIR.slice(0, 40).padEnd(40)}║
|
|
100
|
-
║ ║
|
|
101
|
-
╚══════════════════════════════════════════════════════════╝
|
|
102
|
-
`);
|
|
103
|
-
resolve(httpServer!);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Stop the GUI server
|
|
110
|
-
*/
|
|
111
|
-
export function stopServer(): void {
|
|
112
|
-
if (fileWatcher) {
|
|
113
|
-
fileWatcher.stop();
|
|
114
|
-
fileWatcher = null;
|
|
115
|
-
}
|
|
116
|
-
if (stateStore) {
|
|
117
|
-
stateStore.close();
|
|
118
|
-
stateStore = null;
|
|
119
|
-
}
|
|
120
|
-
if (httpServer) {
|
|
121
|
-
httpServer.close();
|
|
122
|
-
httpServer = null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Graceful shutdown
|
|
127
|
-
process.on('SIGINT', () => {
|
|
128
|
-
console.log('\nShutting down...');
|
|
129
|
-
stopServer();
|
|
130
|
-
process.exit(0);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Auto-start if run directly
|
|
134
|
-
const isDirectRun = process.argv[1]?.endsWith('index.js') || process.argv[1]?.endsWith('index.ts');
|
|
135
|
-
if (isDirectRun) {
|
|
136
|
-
startServer();
|
|
137
|
-
}
|
package/src/server/routes/ai.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { Router, type Router as RouterType } from 'express';
|
|
2
|
-
import { AIService } from '../services/AIService.js';
|
|
3
|
-
|
|
4
|
-
const router: RouterType = Router();
|
|
5
|
-
const aiService = new AIService();
|
|
6
|
-
|
|
7
|
-
// Process AI prompt
|
|
8
|
-
router.post('/prompt', async (req, res) => {
|
|
9
|
-
try {
|
|
10
|
-
const { prompt, workflow } = req.body;
|
|
11
|
-
|
|
12
|
-
if (!prompt) {
|
|
13
|
-
return res.status(400).json({ error: 'Prompt is required' });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const result = await aiService.processPrompt(prompt, workflow);
|
|
17
|
-
res.json(result);
|
|
18
|
-
} catch (error) {
|
|
19
|
-
res.status(500).json({
|
|
20
|
-
error: 'Failed to process prompt',
|
|
21
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Get prompt history
|
|
27
|
-
router.get('/history', async (_req, res) => {
|
|
28
|
-
try {
|
|
29
|
-
const history = await aiService.getHistory();
|
|
30
|
-
res.json({ history });
|
|
31
|
-
} catch (error) {
|
|
32
|
-
res.status(500).json({
|
|
33
|
-
error: 'Failed to get history',
|
|
34
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Get AI suggestions for current context
|
|
40
|
-
router.post('/suggestions', async (req, res) => {
|
|
41
|
-
try {
|
|
42
|
-
const { workflow, selectedStepId } = req.body;
|
|
43
|
-
const suggestions = await aiService.getSuggestions(workflow, selectedStepId);
|
|
44
|
-
res.json({ suggestions });
|
|
45
|
-
} catch (error) {
|
|
46
|
-
res.status(500).json({
|
|
47
|
-
error: 'Failed to get suggestions',
|
|
48
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Get available AI providers and status
|
|
54
|
-
router.get('/providers', async (_req, res) => {
|
|
55
|
-
try {
|
|
56
|
-
const status = aiService.getStatus();
|
|
57
|
-
res.json(status);
|
|
58
|
-
} catch (error) {
|
|
59
|
-
res.status(500).json({
|
|
60
|
-
error: 'Failed to get providers',
|
|
61
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Set active AI provider
|
|
67
|
-
router.post('/providers/:providerId', async (req, res) => {
|
|
68
|
-
try {
|
|
69
|
-
const { providerId } = req.params;
|
|
70
|
-
const { apiKey, baseUrl, model } = req.body;
|
|
71
|
-
|
|
72
|
-
const success = await aiService.setProvider(providerId, { apiKey, baseUrl, model });
|
|
73
|
-
|
|
74
|
-
if (success) {
|
|
75
|
-
const status = aiService.getStatus();
|
|
76
|
-
res.json({ success: true, status });
|
|
77
|
-
} else {
|
|
78
|
-
res.status(400).json({
|
|
79
|
-
error: 'Failed to set provider',
|
|
80
|
-
message: `Provider "${providerId}" is not available or failed to initialize`,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
res.status(500).json({
|
|
85
|
-
error: 'Failed to set provider',
|
|
86
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
export { router as aiRoutes };
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { Router, type Router as RouterType } from 'express';
|
|
2
|
-
|
|
3
|
-
const router: RouterType = Router();
|
|
4
|
-
|
|
5
|
-
// Note: Specific routes must come before catch-all routes
|
|
6
|
-
|
|
7
|
-
// Get execution status
|
|
8
|
-
router.get('/status/:runId', async (req, res) => {
|
|
9
|
-
try {
|
|
10
|
-
const { runId } = req.params;
|
|
11
|
-
|
|
12
|
-
// TODO: Get actual execution status from state store
|
|
13
|
-
|
|
14
|
-
res.json({
|
|
15
|
-
runId,
|
|
16
|
-
status: 'running',
|
|
17
|
-
currentStep: 'step-1',
|
|
18
|
-
progress: 50,
|
|
19
|
-
});
|
|
20
|
-
} catch (error) {
|
|
21
|
-
res.status(500).json({
|
|
22
|
-
error: 'Failed to get execution status',
|
|
23
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Cancel execution
|
|
29
|
-
router.post('/cancel/:runId', async (req, res) => {
|
|
30
|
-
try {
|
|
31
|
-
const { runId } = req.params;
|
|
32
|
-
|
|
33
|
-
// TODO: Cancel actual execution
|
|
34
|
-
|
|
35
|
-
res.json({
|
|
36
|
-
runId,
|
|
37
|
-
status: 'cancelled',
|
|
38
|
-
});
|
|
39
|
-
} catch (error) {
|
|
40
|
-
res.status(500).json({
|
|
41
|
-
error: 'Failed to cancel execution',
|
|
42
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Execute a workflow (catch-all route - must be last)
|
|
48
|
-
router.post('/:path(*)', async (req, res) => {
|
|
49
|
-
try {
|
|
50
|
-
const workflowPath = decodeURIComponent((req.params as Record<string, string>)['path(*)']);
|
|
51
|
-
const { inputs, dryRun } = req.body;
|
|
52
|
-
|
|
53
|
-
// TODO: Integrate with @marktoflow/core WorkflowEngine
|
|
54
|
-
// For now, return a placeholder response
|
|
55
|
-
|
|
56
|
-
res.json({
|
|
57
|
-
runId: `run-${Date.now()}`,
|
|
58
|
-
status: 'started',
|
|
59
|
-
workflowPath,
|
|
60
|
-
inputs,
|
|
61
|
-
dryRun: dryRun || false,
|
|
62
|
-
});
|
|
63
|
-
} catch (error) {
|
|
64
|
-
res.status(500).json({
|
|
65
|
-
error: 'Failed to execute workflow',
|
|
66
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
export { router as executeRoutes };
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API routes for execution history
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Router, type Request, type Response } from 'express';
|
|
6
|
-
import { getStateStore } from '../index.js';
|
|
7
|
-
|
|
8
|
-
export const executionRoutes = Router();
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* GET /api/executions
|
|
12
|
-
* List all executions with optional filtering
|
|
13
|
-
*/
|
|
14
|
-
executionRoutes.get('/', (req: Request, res: Response) => {
|
|
15
|
-
try {
|
|
16
|
-
const stateStore = getStateStore();
|
|
17
|
-
const { workflowId, status, limit, offset } = req.query;
|
|
18
|
-
|
|
19
|
-
// Helper to extract string from query param
|
|
20
|
-
const getString = (val: unknown): string | undefined => {
|
|
21
|
-
if (typeof val === 'string') return val;
|
|
22
|
-
if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'string') return val[0];
|
|
23
|
-
return undefined;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const workflowIdStr = getString(workflowId);
|
|
27
|
-
const statusStr = getString(status);
|
|
28
|
-
const limitNum = parseInt(getString(limit) || '50', 10);
|
|
29
|
-
const offsetNum = getString(offset) ? parseInt(getString(offset)!, 10) : undefined;
|
|
30
|
-
|
|
31
|
-
const executions = stateStore.listExecutions({
|
|
32
|
-
workflowId: workflowIdStr,
|
|
33
|
-
status: statusStr as any,
|
|
34
|
-
limit: limitNum,
|
|
35
|
-
offset: offsetNum,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
res.json(executions);
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.error('Error listing executions:', error);
|
|
41
|
-
res.status(500).json({
|
|
42
|
-
error: 'Failed to list executions',
|
|
43
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* GET /api/executions/:runId
|
|
50
|
-
* Get details for a specific execution
|
|
51
|
-
*/
|
|
52
|
-
executionRoutes.get('/:runId', (req: Request, res: Response) => {
|
|
53
|
-
try {
|
|
54
|
-
const stateStore = getStateStore();
|
|
55
|
-
const runId = Array.isArray(req.params.runId) ? req.params.runId[0] : req.params.runId;
|
|
56
|
-
|
|
57
|
-
const execution = stateStore.getExecution(runId);
|
|
58
|
-
if (!execution) {
|
|
59
|
-
res.status(404).json({ error: 'Execution not found' });
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
res.json(execution);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.error('Error getting execution:', error);
|
|
66
|
-
res.status(500).json({
|
|
67
|
-
error: 'Failed to get execution',
|
|
68
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* GET /api/executions/:runId/checkpoints
|
|
75
|
-
* Get checkpoints for a specific execution
|
|
76
|
-
*/
|
|
77
|
-
executionRoutes.get('/:runId/checkpoints', (req: Request, res: Response) => {
|
|
78
|
-
try {
|
|
79
|
-
const stateStore = getStateStore();
|
|
80
|
-
const runId = Array.isArray(req.params.runId) ? req.params.runId[0] : req.params.runId;
|
|
81
|
-
|
|
82
|
-
const checkpoints = stateStore.getCheckpoints(runId);
|
|
83
|
-
res.json(checkpoints);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error('Error getting checkpoints:', error);
|
|
86
|
-
res.status(500).json({
|
|
87
|
-
error: 'Failed to get checkpoints',
|
|
88
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* GET /api/executions/:runId/stats
|
|
95
|
-
* Get execution statistics
|
|
96
|
-
*/
|
|
97
|
-
executionRoutes.get('/:runId/stats', (req: Request, res: Response) => {
|
|
98
|
-
try {
|
|
99
|
-
const stateStore = getStateStore();
|
|
100
|
-
const runId = Array.isArray(req.params.runId) ? req.params.runId[0] : req.params.runId;
|
|
101
|
-
|
|
102
|
-
const execution = stateStore.getExecution(runId);
|
|
103
|
-
if (!execution) {
|
|
104
|
-
res.status(404).json({ error: 'Execution not found' });
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const checkpoints = stateStore.getCheckpoints(runId);
|
|
109
|
-
const completedSteps = checkpoints.filter((c) => c.status === 'completed').length;
|
|
110
|
-
const failedSteps = checkpoints.filter((c) => c.status === 'failed').length;
|
|
111
|
-
|
|
112
|
-
const stats = {
|
|
113
|
-
runId,
|
|
114
|
-
workflowId: execution.workflowId,
|
|
115
|
-
status: execution.status,
|
|
116
|
-
totalSteps: execution.totalSteps,
|
|
117
|
-
completedSteps,
|
|
118
|
-
failedSteps,
|
|
119
|
-
currentStep: execution.currentStep,
|
|
120
|
-
duration:
|
|
121
|
-
execution.completedAt && execution.startedAt
|
|
122
|
-
? execution.completedAt.getTime() - execution.startedAt.getTime()
|
|
123
|
-
: null,
|
|
124
|
-
startedAt: execution.startedAt,
|
|
125
|
-
completedAt: execution.completedAt,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
res.json(stats);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error('Error getting execution stats:', error);
|
|
131
|
-
res.status(500).json({
|
|
132
|
-
error: 'Failed to get execution stats',
|
|
133
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
});
|