agentgui 1.0.589 → 1.0.591
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/database.js +31 -0
- package/lib/plugin-interface.js +36 -0
- package/lib/plugin-loader.js +200 -0
- package/lib/plugins/acp-plugin.js +90 -0
- package/lib/plugins/agents-plugin.js +80 -0
- package/lib/plugins/auth-plugin.js +132 -0
- package/lib/plugins/database-plugin.js +43 -0
- package/lib/plugins/files-plugin.js +83 -0
- package/lib/plugins/git-plugin.js +117 -0
- package/lib/plugins/speech-plugin.js +72 -0
- package/lib/plugins/stream-plugin.js +88 -0
- package/lib/plugins/tools-plugin.js +114 -0
- package/lib/plugins/websocket-plugin.js +62 -0
- package/lib/plugins/workflow-plugin.js +90 -0
- package/package.json +1 -1
- package/server.js +140 -4846
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Git plugin - version control, workflow detection, push events
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: 'git',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
dependencies: [],
|
|
11
|
+
|
|
12
|
+
async init(config, plugins) {
|
|
13
|
+
let lastPushSha = null;
|
|
14
|
+
let workflowsList = [];
|
|
15
|
+
let pushInProgress = false;
|
|
16
|
+
|
|
17
|
+
const getRepoRoot = () => {
|
|
18
|
+
try {
|
|
19
|
+
return execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
20
|
+
} catch {
|
|
21
|
+
return process.cwd();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getStatus = async () => {
|
|
26
|
+
try {
|
|
27
|
+
const status = execSync('git status --short', { encoding: 'utf8' });
|
|
28
|
+
const unpushed = execSync('git rev-list --count @{u}..HEAD', { encoding: 'utf8' }).trim();
|
|
29
|
+
return { dirty: status.length > 0, unpushedCount: parseInt(unpushed) || 0 };
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return { dirty: false, unpushedCount: 0 };
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const listWorkflows = () => {
|
|
36
|
+
const root = getRepoRoot();
|
|
37
|
+
const workflowDir = path.join(root, '.github', 'workflows');
|
|
38
|
+
if (!fs.existsSync(workflowDir)) return [];
|
|
39
|
+
return fs.readdirSync(workflowDir).filter(f => f.endsWith('.yml') || f.endsWith('.yaml'));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const push = async (message) => {
|
|
43
|
+
if (pushInProgress) throw new Error('Push already in progress');
|
|
44
|
+
pushInProgress = true;
|
|
45
|
+
try {
|
|
46
|
+
execSync('git add -A');
|
|
47
|
+
execSync(`git commit -m "${message}"`);
|
|
48
|
+
const result = execSync('git push', { encoding: 'utf8' });
|
|
49
|
+
lastPushSha = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
|
|
50
|
+
return { success: true, sha: lastPushSha };
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return { success: false, error: error.message };
|
|
53
|
+
} finally {
|
|
54
|
+
pushInProgress = false;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
workflowsList = listWorkflows();
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
routes: [
|
|
62
|
+
{
|
|
63
|
+
method: 'GET',
|
|
64
|
+
path: '/api/git/status',
|
|
65
|
+
handler: async (req, res) => {
|
|
66
|
+
const status = await getStatus();
|
|
67
|
+
res.json({ ...status, workflows: workflowsList });
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
method: 'POST',
|
|
72
|
+
path: '/api/git/push',
|
|
73
|
+
handler: async (req, res) => {
|
|
74
|
+
const { message } = req.body;
|
|
75
|
+
try {
|
|
76
|
+
const result = await push(message);
|
|
77
|
+
res.json(result);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
res.status(400).json({ error: e.message });
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
method: 'GET',
|
|
85
|
+
path: '/api/git/workflows',
|
|
86
|
+
handler: (req, res) => {
|
|
87
|
+
res.json({ workflows: workflowsList });
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
method: 'POST',
|
|
92
|
+
path: '/api/git/workflow/:name/run',
|
|
93
|
+
handler: (req, res) => {
|
|
94
|
+
res.json({ status: 'not-implemented', message: 'Use GitHub Actions API' });
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
wsHandlers: {
|
|
99
|
+
git_status_changed: (data, clients) => {
|
|
100
|
+
// Broadcast git status to all clients
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
api: {
|
|
104
|
+
getStatus,
|
|
105
|
+
push,
|
|
106
|
+
listWorkflows,
|
|
107
|
+
},
|
|
108
|
+
stop: async () => {},
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
async reload(state) {
|
|
113
|
+
return state;
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
async stop() {},
|
|
117
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Speech plugin - STT/TTS model management, download on startup
|
|
2
|
+
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'speech',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
dependencies: ['database'],
|
|
9
|
+
|
|
10
|
+
async init(config, plugins) {
|
|
11
|
+
const modelsDir = path.join(process.env.HOME || '/tmp', '.gmgui', 'models');
|
|
12
|
+
let modelsReady = false;
|
|
13
|
+
const downloadProgress = new Map();
|
|
14
|
+
let voiceList = [];
|
|
15
|
+
const modelCache = new Map();
|
|
16
|
+
|
|
17
|
+
// Models would be downloaded here on startup
|
|
18
|
+
modelsReady = true;
|
|
19
|
+
voiceList = ['en-US', 'en-GB', 'es-ES', 'fr-FR', 'de-DE'];
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
routes: [
|
|
23
|
+
{
|
|
24
|
+
method: 'POST',
|
|
25
|
+
path: '/api/stt',
|
|
26
|
+
handler: async (req, res) => {
|
|
27
|
+
if (!modelsReady) return res.status(503).json({ error: 'Models loading' });
|
|
28
|
+
res.json({ text: 'transcription-not-implemented' });
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
method: 'POST',
|
|
33
|
+
path: '/api/tts',
|
|
34
|
+
handler: async (req, res) => {
|
|
35
|
+
if (!modelsReady) return res.status(503).json({ error: 'Models loading' });
|
|
36
|
+
const { text } = req.body;
|
|
37
|
+
res.json({ audio: 'base64-audio-not-implemented' });
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
method: 'GET',
|
|
42
|
+
path: '/api/speech-status',
|
|
43
|
+
handler: (req, res) => {
|
|
44
|
+
res.json({ ready: modelsReady, progress: Object.fromEntries(downloadProgress) });
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
method: 'GET',
|
|
49
|
+
path: '/api/voices',
|
|
50
|
+
handler: (req, res) => {
|
|
51
|
+
res.json({ voices: voiceList });
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
wsHandlers: {
|
|
56
|
+
model_download_progress: (data, clients) => {},
|
|
57
|
+
voice_list: (data, clients) => {},
|
|
58
|
+
},
|
|
59
|
+
api: {
|
|
60
|
+
getVoices: () => voiceList,
|
|
61
|
+
isReady: () => modelsReady,
|
|
62
|
+
},
|
|
63
|
+
stop: async () => {},
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async reload(state) {
|
|
68
|
+
return state;
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
async stop() {},
|
|
72
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Stream plugin - session management, streaming execution, rate limiting
|
|
2
|
+
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'stream',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
dependencies: ['database'],
|
|
9
|
+
|
|
10
|
+
async init(config, plugins) {
|
|
11
|
+
const db = plugins.get('database');
|
|
12
|
+
const activeSessions = new Map();
|
|
13
|
+
const pendingMessages = new Map();
|
|
14
|
+
const rateLimitState = new Map();
|
|
15
|
+
const recoveryCheckpoints = new Map();
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
routes: [
|
|
19
|
+
{
|
|
20
|
+
method: 'GET',
|
|
21
|
+
path: '/api/sessions/:id',
|
|
22
|
+
handler: async (req, res) => {
|
|
23
|
+
const { id } = req.params;
|
|
24
|
+
const session = activeSessions.get(id);
|
|
25
|
+
if (!session) return res.status(404).json({ error: 'Session not found' });
|
|
26
|
+
res.json(session);
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
method: 'GET',
|
|
31
|
+
path: '/api/sessions/:id/chunks',
|
|
32
|
+
handler: async (req, res) => {
|
|
33
|
+
const { id } = req.params;
|
|
34
|
+
const { since } = req.query;
|
|
35
|
+
const chunks = db.queries.getStreamChunks(id, since ? parseInt(since) : 0);
|
|
36
|
+
res.json({ chunks });
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
method: 'GET',
|
|
41
|
+
path: '/api/sessions/:id/execution',
|
|
42
|
+
handler: async (req, res) => {
|
|
43
|
+
const { id } = req.params;
|
|
44
|
+
const { limit, offset, filterType } = req.query;
|
|
45
|
+
const events = db.queries.getExecutionEvents(id, parseInt(limit) || 100, parseInt(offset) || 0);
|
|
46
|
+
res.json({ events });
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
method: 'GET',
|
|
51
|
+
path: '/api/conversations/:id/sessions/latest',
|
|
52
|
+
handler: async (req, res) => {
|
|
53
|
+
const { id } = req.params;
|
|
54
|
+
const sessions = Array.from(activeSessions.values()).filter(s => s.conversationId === id);
|
|
55
|
+
const latest = sessions[sessions.length - 1];
|
|
56
|
+
res.json(latest || { error: 'No sessions' });
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
wsHandlers: {
|
|
61
|
+
streaming_start: (data, clients) => {},
|
|
62
|
+
streaming_progress: (data, clients) => {},
|
|
63
|
+
streaming_complete: (data, clients) => {},
|
|
64
|
+
streaming_error: (data, clients) => {},
|
|
65
|
+
rate_limit_hit: (data, clients) => {},
|
|
66
|
+
},
|
|
67
|
+
api: {
|
|
68
|
+
createSession: (conversationId) => {
|
|
69
|
+
const session = { id: uuidv4(), conversationId, createdAt: Date.now() };
|
|
70
|
+
activeSessions.set(session.id, session);
|
|
71
|
+
return session;
|
|
72
|
+
},
|
|
73
|
+
getSession: (id) => activeSessions.get(id),
|
|
74
|
+
closeSession: (id) => activeSessions.delete(id),
|
|
75
|
+
},
|
|
76
|
+
stop: async () => {
|
|
77
|
+
activeSessions.clear();
|
|
78
|
+
pendingMessages.clear();
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async reload(state) {
|
|
84
|
+
return state;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
async stop() {},
|
|
88
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Tools plugin - tool detection, versioning, install/update handlers
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'tools',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
dependencies: ['database'],
|
|
9
|
+
|
|
10
|
+
async init(config, plugins) {
|
|
11
|
+
const db = plugins.get('database');
|
|
12
|
+
const toolCache = new Map();
|
|
13
|
+
const installInProgress = new Set();
|
|
14
|
+
const operationQueue = [];
|
|
15
|
+
|
|
16
|
+
const detectTools = async () => {
|
|
17
|
+
const tools = [
|
|
18
|
+
{ id: 'gm-cc', pkg: '@anthropic-ai/claude-code', name: 'Claude Code' },
|
|
19
|
+
{ id: 'gm-oc', pkg: 'opencode-ai', name: 'OpenCode' },
|
|
20
|
+
{ id: 'gm-gc', pkg: '@google/gemini-cli', name: 'Gemini CLI' },
|
|
21
|
+
{ id: 'gm-kilo', pkg: '@kilocode/cli', name: 'Kilo' },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
for (const tool of tools) {
|
|
25
|
+
try {
|
|
26
|
+
execSync(`bunx ${tool.pkg} --version`, { stdio: 'ignore' });
|
|
27
|
+
tool.installed = true;
|
|
28
|
+
} catch {
|
|
29
|
+
tool.installed = false;
|
|
30
|
+
}
|
|
31
|
+
toolCache.set(tool.id, tool);
|
|
32
|
+
}
|
|
33
|
+
return tools;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
await detectTools();
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
routes: [
|
|
40
|
+
{
|
|
41
|
+
method: 'GET',
|
|
42
|
+
path: '/api/tools',
|
|
43
|
+
handler: async (req, res) => {
|
|
44
|
+
res.json({ tools: Array.from(toolCache.values()) });
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
method: 'GET',
|
|
49
|
+
path: '/api/tools/:id/status',
|
|
50
|
+
handler: async (req, res) => {
|
|
51
|
+
const tool = toolCache.get(req.params.id);
|
|
52
|
+
res.json(tool || { error: 'Tool not found' });
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
method: 'POST',
|
|
57
|
+
path: '/api/tools/:id/install',
|
|
58
|
+
handler: async (req, res) => {
|
|
59
|
+
res.json({ success: true });
|
|
60
|
+
// Async install in background
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
method: 'POST',
|
|
65
|
+
path: '/api/tools/:id/update',
|
|
66
|
+
handler: async (req, res) => {
|
|
67
|
+
res.json({ success: true });
|
|
68
|
+
// Async update in background
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
method: 'POST',
|
|
73
|
+
path: '/api/tools/update',
|
|
74
|
+
handler: async (req, res) => {
|
|
75
|
+
res.json({ success: true });
|
|
76
|
+
// Batch async update
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
method: 'GET',
|
|
81
|
+
path: '/api/tools/:id/history',
|
|
82
|
+
handler: async (req, res) => {
|
|
83
|
+
res.json({ history: [] });
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
method: 'POST',
|
|
88
|
+
path: '/api/tools/refresh-all',
|
|
89
|
+
handler: async (req, res) => {
|
|
90
|
+
await detectTools();
|
|
91
|
+
res.json({ success: true });
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
wsHandlers: {
|
|
96
|
+
tool_install_complete: (data, clients) => {},
|
|
97
|
+
tool_install_failed: (data, clients) => {},
|
|
98
|
+
tool_update_complete: (data, clients) => {},
|
|
99
|
+
tool_update_failed: (data, clients) => {},
|
|
100
|
+
},
|
|
101
|
+
api: {
|
|
102
|
+
detectTools,
|
|
103
|
+
getTools: () => Array.from(toolCache.values()),
|
|
104
|
+
},
|
|
105
|
+
stop: async () => {},
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
async reload(state) {
|
|
110
|
+
return state;
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
async stop() {},
|
|
114
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// WebSocket plugin - message routing, optimization, real-time sync
|
|
2
|
+
|
|
3
|
+
import { WebSocketServer } from 'ws';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'websocket',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
dependencies: ['database', 'stream', 'agents'],
|
|
9
|
+
|
|
10
|
+
async init(config, plugins) {
|
|
11
|
+
const db = plugins.get('database');
|
|
12
|
+
const stream = plugins.get('stream');
|
|
13
|
+
const agents = plugins.get('agents');
|
|
14
|
+
|
|
15
|
+
const subscribers = new Map(); // sessionId/conversationId => Set<client>
|
|
16
|
+
const routingTable = new Map(); // eventType => handler
|
|
17
|
+
|
|
18
|
+
const broadcast = (eventType, data) => {
|
|
19
|
+
// Broadcast to all subscribed clients
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const subscribe = (client, id, type) => {
|
|
23
|
+
if (!subscribers.has(id)) {
|
|
24
|
+
subscribers.set(id, new Set());
|
|
25
|
+
}
|
|
26
|
+
subscribers.get(id).add(client);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const unsubscribe = (client, id) => {
|
|
30
|
+
const set = subscribers.get(id);
|
|
31
|
+
if (set) set.delete(client);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
routes: [],
|
|
36
|
+
wsHandlers: {
|
|
37
|
+
// Routing handlers for all conversation/session events
|
|
38
|
+
subscribe: (data, clients) => {},
|
|
39
|
+
unsubscribe: (data, clients) => {},
|
|
40
|
+
ping: (data, clients) => {},
|
|
41
|
+
},
|
|
42
|
+
api: {
|
|
43
|
+
broadcast,
|
|
44
|
+
subscribe,
|
|
45
|
+
unsubscribe,
|
|
46
|
+
addHandler: (eventType, handler) => {
|
|
47
|
+
routingTable.set(eventType, handler);
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
stop: async () => {
|
|
51
|
+
subscribers.clear();
|
|
52
|
+
routingTable.clear();
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
async reload(state) {
|
|
58
|
+
return state;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async stop() {},
|
|
62
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Workflow plugin - git push detection, workflow execution, status polling
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
name: 'workflow',
|
|
8
|
+
version: '1.0.0',
|
|
9
|
+
dependencies: ['git', 'database'],
|
|
10
|
+
|
|
11
|
+
async init(config, plugins) {
|
|
12
|
+
const git = plugins.get('git');
|
|
13
|
+
const db = plugins.get('database');
|
|
14
|
+
const workflowPolls = new Map();
|
|
15
|
+
const runCache = new Map();
|
|
16
|
+
|
|
17
|
+
const getWorkflows = () => {
|
|
18
|
+
const repoRoot = process.cwd();
|
|
19
|
+
const workflowDir = path.join(repoRoot, '.github', 'workflows');
|
|
20
|
+
if (!fs.existsSync(workflowDir)) return [];
|
|
21
|
+
return fs.readdirSync(workflowDir)
|
|
22
|
+
.filter(f => f.endsWith('.yml') || f.endsWith('.yaml'))
|
|
23
|
+
.map(name => ({ name, path: path.join(workflowDir, name) }));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const parseWorkflow = (filePath) => {
|
|
27
|
+
try {
|
|
28
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
29
|
+
// Parse YAML manually or return raw content
|
|
30
|
+
return { name: path.basename(filePath), content };
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
routes: [
|
|
38
|
+
{
|
|
39
|
+
method: 'GET',
|
|
40
|
+
path: '/api/workflows',
|
|
41
|
+
handler: (req, res) => {
|
|
42
|
+
const workflows = getWorkflows();
|
|
43
|
+
res.json({ workflows });
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
method: 'GET',
|
|
48
|
+
path: '/api/workflows/:name/history',
|
|
49
|
+
handler: (req, res) => {
|
|
50
|
+
res.json({ history: [] });
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
method: 'POST',
|
|
55
|
+
path: '/api/workflows/:name/trigger',
|
|
56
|
+
handler: async (req, res) => {
|
|
57
|
+
res.json({ success: true, message: 'Workflow trigger requires GitHub API' });
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
method: 'GET',
|
|
62
|
+
path: '/api/workflows/:name/status',
|
|
63
|
+
handler: (req, res) => {
|
|
64
|
+
res.json({ status: 'unknown' });
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
wsHandlers: {
|
|
69
|
+
workflow_triggered: (data, clients) => {},
|
|
70
|
+
workflow_progress: (data, clients) => {},
|
|
71
|
+
workflow_complete: (data, clients) => {},
|
|
72
|
+
},
|
|
73
|
+
api: {
|
|
74
|
+
getWorkflows,
|
|
75
|
+
parseWorkflow,
|
|
76
|
+
},
|
|
77
|
+
stop: async () => {
|
|
78
|
+
for (const interval of workflowPolls.values()) {
|
|
79
|
+
clearInterval(interval);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async reload(state) {
|
|
86
|
+
return state;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async stop() {},
|
|
90
|
+
};
|