agentgui 1.0.721 → 1.0.723
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/lib/jsonl-watcher.js +79 -22
- package/lib/plugin-loader.js +188 -162
- package/lib/plugins/agents-plugin.js +2 -1
- package/lib/plugins/database-plugin.js +1 -1
- package/lib/plugins/stream-plugin.js +1 -2
- package/lib/tool-provisioner.js +1 -1
- package/package.json +1 -1
- package/server.js +3 -3
package/lib/jsonl-watcher.js
CHANGED
|
@@ -12,10 +12,11 @@ export class JsonlWatcher {
|
|
|
12
12
|
this._owned = ownedSessionIds;
|
|
13
13
|
this._tails = new Map();
|
|
14
14
|
this._convMap = new Map();
|
|
15
|
-
this.
|
|
15
|
+
this._emitted = new Map();
|
|
16
16
|
this._timers = new Map();
|
|
17
17
|
this._seqs = new Map();
|
|
18
18
|
this._streaming = new Set();
|
|
19
|
+
this._sessions = new Map();
|
|
19
20
|
this._watcher = null;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -42,7 +43,8 @@ export class JsonlWatcher {
|
|
|
42
43
|
this._convMap.delete(sid);
|
|
43
44
|
this._seqs.delete(sid);
|
|
44
45
|
this._streaming.delete(sid);
|
|
45
|
-
|
|
46
|
+
this._sessions.delete(sid);
|
|
47
|
+
for (const key of [...this._emitted.keys()]) if (key.startsWith(`${sid}:`)) this._emitted.delete(key);
|
|
46
48
|
for (const [fp, s] of this._tails.entries()) {
|
|
47
49
|
if (!fp.includes(sid)) continue;
|
|
48
50
|
if (s.fd !== null) try { fs.closeSync(s.fd); } catch (_) {}
|
|
@@ -56,8 +58,9 @@ export class JsonlWatcher {
|
|
|
56
58
|
removeAllConversations() {
|
|
57
59
|
for (const s of this._tails.values()) if (s.fd !== null) try { fs.closeSync(s.fd); } catch (_) {}
|
|
58
60
|
for (const t of this._timers.values()) clearTimeout(t);
|
|
59
|
-
this._tails.clear(); this._convMap.clear(); this.
|
|
61
|
+
this._tails.clear(); this._convMap.clear(); this._emitted.clear();
|
|
60
62
|
this._timers.clear(); this._seqs.clear(); this._streaming.clear();
|
|
63
|
+
this._sessions.clear();
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
_scanDir(dir, depth) {
|
|
@@ -114,7 +117,8 @@ export class JsonlWatcher {
|
|
|
114
117
|
if (this._convMap.has(sid)) return this._convMap.get(sid);
|
|
115
118
|
const found = this._q.getConversations().find(c => c.claudeSessionId === sid);
|
|
116
119
|
if (found) { this._convMap.set(sid, found.id); return found.id; }
|
|
117
|
-
if (e.type
|
|
120
|
+
if (e.type === 'queue-operation' || e.type === 'last-prompt') return null;
|
|
121
|
+
if (e.type === 'user' && e.isMeta) return null;
|
|
118
122
|
const cwd = e.cwd || process.cwd();
|
|
119
123
|
const branch = e.gitBranch || '';
|
|
120
124
|
const base = path.basename(cwd);
|
|
@@ -128,28 +132,60 @@ export class JsonlWatcher {
|
|
|
128
132
|
|
|
129
133
|
_seq(sid) { const n = (this._seqs.get(sid) || 0) + 1; this._seqs.set(sid, n); return n; }
|
|
130
134
|
|
|
135
|
+
_dbSession(cid, sid) {
|
|
136
|
+
if (this._sessions.has(sid)) return this._sessions.get(sid);
|
|
137
|
+
const sess = this._q.createSession(cid);
|
|
138
|
+
this._q.updateSession(sess.id, { status: 'active' });
|
|
139
|
+
this._sessions.set(sid, sess.id);
|
|
140
|
+
return sess.id;
|
|
141
|
+
}
|
|
142
|
+
|
|
131
143
|
_emit(cid, sid, block, role, extra) {
|
|
132
|
-
|
|
144
|
+
const dbSid = this._dbSession(cid, sid);
|
|
145
|
+
const seq = this._seq(sid);
|
|
146
|
+
try { this._q.createChunk(dbSid, cid, seq, block.type || 'unknown', block); } catch (_) {}
|
|
147
|
+
this._bc({ type: 'streaming_progress', sessionId: dbSid, conversationId: cid, block, blockRole: role, seq, timestamp: Date.now(), ...extra });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_emitBlocks(cid, sid, blocks, role) {
|
|
151
|
+
for (const b of blocks) {
|
|
152
|
+
if (!b || !b.type) continue;
|
|
153
|
+
this._emit(cid, sid, b, role);
|
|
154
|
+
}
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
_startStreaming(cid, sid) {
|
|
136
158
|
if (this._streaming.has(sid)) return;
|
|
137
159
|
this._streaming.add(sid);
|
|
138
|
-
this.
|
|
160
|
+
const dbSid = this._dbSession(cid, sid);
|
|
161
|
+
this._q.setIsStreaming(cid, true);
|
|
162
|
+
this._bc({ type: 'streaming_start', sessionId: dbSid, conversationId: cid, agentId: 'cli-claude', timestamp: Date.now() });
|
|
139
163
|
}
|
|
140
164
|
|
|
141
165
|
_endStreaming(cid, sid) {
|
|
142
166
|
if (!this._streaming.has(sid)) return;
|
|
143
167
|
this._streaming.delete(sid);
|
|
144
|
-
|
|
168
|
+
const dbSid = this._sessions.get(sid);
|
|
169
|
+
if (dbSid) {
|
|
170
|
+
try { this._q.updateSession(dbSid, { status: 'completed', completed_at: Date.now() }); } catch (_) {}
|
|
171
|
+
}
|
|
172
|
+
this._q.setIsStreaming(cid, false);
|
|
173
|
+
this._bc({ type: 'streaming_complete', sessionId: dbSid || sid, conversationId: cid, agentId: 'cli-claude', eventCount: 0, seq: this._seq(sid), timestamp: Date.now() });
|
|
174
|
+
this._sessions.delete(sid);
|
|
145
175
|
}
|
|
146
176
|
|
|
147
177
|
_route(cid, sid, e) {
|
|
148
|
-
if (e.type === 'queue-operation' || (e.type === 'user' && e.isMeta)) return;
|
|
178
|
+
if (e.type === 'queue-operation' || e.type === 'last-prompt' || (e.type === 'user' && e.isMeta)) return;
|
|
179
|
+
|
|
180
|
+
if (e.isApiErrorMessage && e.error === 'rate_limit') {
|
|
181
|
+
this._bc({ type: 'streaming_error', sessionId: this._sessions.get(sid) || sid, conversationId: cid, error: 'Rate limit hit', recoverable: true, timestamp: Date.now() });
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
149
184
|
|
|
150
185
|
if (e.type === 'system') {
|
|
151
186
|
if (e.subtype === 'init') { this._startStreaming(cid, sid); return; }
|
|
152
187
|
if (e.subtype === 'turn_duration' || e.subtype === 'stop_hook_summary') { this._endStreaming(cid, sid); return; }
|
|
188
|
+
this._startStreaming(cid, sid);
|
|
153
189
|
this._emit(cid, sid, { type: 'system', subtype: e.subtype, model: e.model, cwd: e.cwd, tools: e.tools, preTokens: e.compactMetadata?.preTokens }, 'system');
|
|
154
190
|
return;
|
|
155
191
|
}
|
|
@@ -157,32 +193,53 @@ export class JsonlWatcher {
|
|
|
157
193
|
if (e.type === 'assistant' && e.message?.content) {
|
|
158
194
|
this._startStreaming(cid, sid);
|
|
159
195
|
const key = `${sid}:${e.message.id}`;
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
196
|
+
const prev = this._emitted.get(key) || 0;
|
|
197
|
+
const content = e.message.content;
|
|
198
|
+
const newBlocks = content.slice(prev);
|
|
199
|
+
if (newBlocks.length > 0) {
|
|
200
|
+
this._emitted.set(key, content.length);
|
|
201
|
+
this._emitBlocks(cid, sid, newBlocks, 'assistant');
|
|
202
|
+
}
|
|
203
|
+
if (e.message.stop_reason) this._emitted.delete(key);
|
|
165
204
|
return;
|
|
166
205
|
}
|
|
167
206
|
|
|
168
207
|
if (e.type === 'user' && e.message?.content) {
|
|
169
208
|
if (e.isCompactSummary) { this._emit(cid, sid, { type: 'compact_summary', content: e.message.content }, 'system'); return; }
|
|
170
209
|
this._startStreaming(cid, sid);
|
|
171
|
-
const
|
|
210
|
+
const content = e.message.content;
|
|
211
|
+
const textParts = Array.isArray(content)
|
|
212
|
+
? content.filter(b => b.type === 'text' && b.text && !b.text.startsWith('<task-notification>')).map(b => b.text).join('\n')
|
|
213
|
+
: (typeof content === 'string' && !content.startsWith('<task-notification>') ? content : '');
|
|
214
|
+
if (textParts) {
|
|
215
|
+
try { this._q.createMessage(cid, 'user', textParts); } catch (_) {}
|
|
216
|
+
}
|
|
217
|
+
const blocks = Array.isArray(content) ? content : [];
|
|
172
218
|
for (const b of blocks) if (b.type === 'tool_result') this._emit(cid, sid, b, 'tool_result');
|
|
173
219
|
return;
|
|
174
220
|
}
|
|
175
221
|
|
|
176
222
|
if (e.type === 'progress') {
|
|
177
223
|
this._startStreaming(cid, sid);
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
224
|
+
const d = e.data || {};
|
|
225
|
+
if (d.type === 'agent_progress') {
|
|
226
|
+
const inner = d.message?.message || d.message;
|
|
227
|
+
const content = inner?.content;
|
|
228
|
+
if (Array.isArray(content)) {
|
|
229
|
+
const role = inner.role === 'user' ? 'tool_result' : 'assistant';
|
|
230
|
+
this._emitBlocks(cid, sid, content, role);
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (d.type === 'hook_progress') {
|
|
235
|
+
this._emit(cid, sid, { type: 'hook_progress', hookEvent: d.hookEvent, hookName: d.hookName }, 'system');
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (d.type === 'mcp_progress') {
|
|
239
|
+
this._emit(cid, sid, { type: 'mcp_progress', status: d.status, serverName: d.serverName, toolName: d.toolName }, 'system');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
this._emit(cid, sid, { type: d.type || e.subtype || 'progress', content: e.content || d }, 'progress');
|
|
186
243
|
return;
|
|
187
244
|
}
|
|
188
245
|
|
package/lib/plugin-loader.js
CHANGED
|
@@ -1,162 +1,188 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
13
|
-
this.
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
return
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error(`Failed to load plugin ${
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
1
|
+
// Plugin loader - manages registry, dependencies, hot reload, error isolation
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
|
|
7
|
+
class PluginLoader extends EventEmitter {
|
|
8
|
+
constructor(pluginDir) {
|
|
9
|
+
super();
|
|
10
|
+
this.pluginDir = pluginDir;
|
|
11
|
+
this.registry = new Map(); // name => plugin module
|
|
12
|
+
this.instances = new Map(); // name => initialized plugin state
|
|
13
|
+
this.states = new Map(); // name => { routes, wsHandlers, api, ... }
|
|
14
|
+
this.watchers = new Map(); // name => file watcher
|
|
15
|
+
this.errorCounts = new Map(); // name => { count, firstTime }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Load plugin module from disk
|
|
19
|
+
async loadPlugin(name) {
|
|
20
|
+
const filePath = path.join(this.pluginDir, `${name}.js`);
|
|
21
|
+
if (!fs.existsSync(filePath)) {
|
|
22
|
+
throw new Error(`Plugin file not found: ${filePath}`);
|
|
23
|
+
}
|
|
24
|
+
// Clear module cache for hot reload (ES modules use import cache differently)
|
|
25
|
+
const fileUrl = `file://${filePath}?v=${Date.now()}`;
|
|
26
|
+
try {
|
|
27
|
+
const plugin = await import(fileUrl);
|
|
28
|
+
this.registry.set(name, plugin.default || plugin);
|
|
29
|
+
return plugin.default || plugin;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`Failed to load plugin ${name}:`, error.message);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Initialize plugin and all dependencies
|
|
37
|
+
async initializePlugin(name, config) {
|
|
38
|
+
const plugin = this.registry.get(name);
|
|
39
|
+
if (!plugin) {
|
|
40
|
+
throw new Error(`Plugin ${name} not found in registry`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check if already initialized
|
|
44
|
+
if (this.instances.has(name)) {
|
|
45
|
+
return this.instances.get(name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Initialize dependencies first
|
|
49
|
+
for (const depName of (plugin.dependencies || [])) {
|
|
50
|
+
if (!this.instances.has(depName)) {
|
|
51
|
+
await this.initializePlugin(depName, config);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Initialize this plugin
|
|
56
|
+
try {
|
|
57
|
+
const result = await plugin.init(config, this.instances);
|
|
58
|
+
this.instances.set(name, result);
|
|
59
|
+
return result;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(`[PluginLoader] Error initializing ${name}:`, error.message);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get initialized plugin result
|
|
67
|
+
get(name) {
|
|
68
|
+
return this.instances.get(name);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Hot reload a plugin
|
|
72
|
+
async reloadPlugin(name) {
|
|
73
|
+
const plugin = this.registry.get(name);
|
|
74
|
+
if (!plugin) {
|
|
75
|
+
console.warn(`[PluginLoader] Cannot reload ${name}: not found`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const state = this.instances.get(name);
|
|
80
|
+
if (!state) {
|
|
81
|
+
console.warn(`[PluginLoader] Cannot reload ${name}: not initialized`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// Stop old instance
|
|
87
|
+
if (state.stop) await state.stop();
|
|
88
|
+
|
|
89
|
+
// Reload plugin module
|
|
90
|
+
this.loadPlugin(name);
|
|
91
|
+
const reloadedPlugin = this.registry.get(name);
|
|
92
|
+
|
|
93
|
+
// Reinitialize with preserved state
|
|
94
|
+
const newState = await reloadedPlugin.reload(state);
|
|
95
|
+
this.instances.set(name, newState);
|
|
96
|
+
this.emit('reload', { name, success: true });
|
|
97
|
+
console.log(`[PluginLoader] Reloaded plugin: ${name}`);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`[PluginLoader] Error reloading ${name}:`, error.message);
|
|
100
|
+
this.emit('reload', { name, success: false, error: error.message });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Watch a plugin file for changes
|
|
105
|
+
watchPlugin(name, callback) {
|
|
106
|
+
const filePath = path.join(this.pluginDir, `${name}.js`);
|
|
107
|
+
if (this.watchers.has(name)) {
|
|
108
|
+
return; // Already watching
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const watcher = fs.watch(filePath, async (eventType) => {
|
|
112
|
+
if (eventType === 'change') {
|
|
113
|
+
setTimeout(() => callback(name), 100); // Debounce
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.watchers.set(name, watcher);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Stop watching a plugin
|
|
121
|
+
unwatchPlugin(name) {
|
|
122
|
+
const watcher = this.watchers.get(name);
|
|
123
|
+
if (watcher) {
|
|
124
|
+
watcher.close();
|
|
125
|
+
this.watchers.delete(name);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Load all plugins from directory
|
|
130
|
+
async loadAllPlugins(config) {
|
|
131
|
+
if (!fs.existsSync(this.pluginDir)) {
|
|
132
|
+
fs.mkdirSync(this.pluginDir, { recursive: true });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const files = fs.readdirSync(this.pluginDir).filter(f => f.endsWith('.js'));
|
|
137
|
+
for (const file of files) {
|
|
138
|
+
const name = file.replace('.js', '');
|
|
139
|
+
try {
|
|
140
|
+
await this.loadPlugin(name);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error(`[PluginLoader] Failed to load ${name}:`, error.message);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Initialize in dependency order
|
|
147
|
+
const sorted = this.topologicalSort();
|
|
148
|
+
for (const name of sorted) {
|
|
149
|
+
try {
|
|
150
|
+
await this.initializePlugin(name, config);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(`[PluginLoader] Failed to initialize ${name}:`, error.message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Topological sort by dependencies
|
|
158
|
+
topologicalSort() {
|
|
159
|
+
const visited = new Set(), result = [];
|
|
160
|
+
const visit = (name) => {
|
|
161
|
+
if (visited.has(name)) return;
|
|
162
|
+
visited.add(name);
|
|
163
|
+
for (const dep of (this.registry.get(name)?.dependencies || [])) { if (this.registry.has(dep)) visit(dep); }
|
|
164
|
+
result.push(name);
|
|
165
|
+
};
|
|
166
|
+
for (const name of this.registry.keys()) visit(name);
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Graceful shutdown
|
|
171
|
+
async shutdown() {
|
|
172
|
+
const sorted = this.topologicalSort().reverse();
|
|
173
|
+
for (const name of sorted) {
|
|
174
|
+
const state = this.instances.get(name);
|
|
175
|
+
if (state && state.stop) {
|
|
176
|
+
try {
|
|
177
|
+
await state.stop();
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error(`[PluginLoader] Error stopping ${name}:`, error.message);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
this.unwatchPlugin(name);
|
|
183
|
+
}
|
|
184
|
+
this.instances.clear();
|
|
185
|
+
this.registry.clear();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export default PluginLoader;
|
|
@@ -49,9 +49,10 @@ export default {
|
|
|
49
49
|
if (!agent) return res.status(404).json({ error: 'Agent not found' });
|
|
50
50
|
|
|
51
51
|
const session = stream.api.createSession(id);
|
|
52
|
+
// Use runClaudeWithStreaming instead
|
|
52
53
|
activeExecutions.set(id, { sessionId: session.id });
|
|
53
54
|
|
|
54
|
-
res.json({ sessionId: session.id });
|
|
55
|
+
res.json({ sessionId: session.id, pid: proc.pid });
|
|
55
56
|
} catch (e) {
|
|
56
57
|
res.status(500).json({ error: e.message });
|
|
57
58
|
}
|
|
@@ -8,8 +8,7 @@ export default {
|
|
|
8
8
|
dependencies: ['database'],
|
|
9
9
|
|
|
10
10
|
async init(config, plugins) {
|
|
11
|
-
const
|
|
12
|
-
const db = dbPlugin?.api || {};
|
|
11
|
+
const db = plugins.get('database');
|
|
13
12
|
const activeSessions = new Map();
|
|
14
13
|
const pendingMessages = new Map();
|
|
15
14
|
const rateLimitState = new Map();
|
package/lib/tool-provisioner.js
CHANGED
|
@@ -38,7 +38,7 @@ export async function autoProvision(TOOLS, statusCache, install, broadcast) {
|
|
|
38
38
|
broadcast({ type: 'tool_update_failed', toolId: tool.id, data: result });
|
|
39
39
|
}
|
|
40
40
|
} else {
|
|
41
|
-
log(`${tool.id} v${status.installedVersion
|
|
41
|
+
log(`${tool.id} v${status.installedVersion} up-to-date`);
|
|
42
42
|
broadcast({ type: 'tool_status_update', toolId: tool.id, data: { installed: true, isUpToDate: true, installedVersion: status.installedVersion, status: 'installed' } });
|
|
43
43
|
}
|
|
44
44
|
} catch (err) {
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -4479,7 +4479,7 @@ const BROADCAST_TYPES = new Set([
|
|
|
4479
4479
|
'streaming_start', 'streaming_progress', 'streaming_complete', 'streaming_error',
|
|
4480
4480
|
'tool_install_started', 'tool_install_progress', 'tool_install_complete', 'tool_install_failed',
|
|
4481
4481
|
'tool_update_progress', 'tool_update_complete', 'tool_update_failed',
|
|
4482
|
-
'tool_status_update',
|
|
4482
|
+
'tool_status_update',
|
|
4483
4483
|
'tools_update_started', 'tools_update_complete', 'tools_refresh_complete',
|
|
4484
4484
|
'pm2_monit_update', 'pm2_monitoring_started', 'pm2_monitoring_stopped',
|
|
4485
4485
|
'pm2_list_response', 'pm2_start_response', 'pm2_stop_response',
|
|
@@ -5257,7 +5257,7 @@ import PluginLoader from './lib/plugin-loader.js';
|
|
|
5257
5257
|
const pluginLoader = new PluginLoader(path.join(__dirname, 'lib', 'plugins'));
|
|
5258
5258
|
async function loadPluginExtensions() {
|
|
5259
5259
|
try {
|
|
5260
|
-
await pluginLoader.loadAllPlugins({ router:
|
|
5260
|
+
await pluginLoader.loadAllPlugins({ router: app, baseUrl: BASE_URL, logger: console, env: process.env });
|
|
5261
5261
|
const names = Array.from(pluginLoader.registry.keys());
|
|
5262
5262
|
if (names.length > 0) {
|
|
5263
5263
|
for (const name of names) {
|
|
@@ -5266,7 +5266,7 @@ async function loadPluginExtensions() {
|
|
|
5266
5266
|
for (const route of state.routes) {
|
|
5267
5267
|
const fullPath = BASE_URL + route.path;
|
|
5268
5268
|
const method = (route.method || 'GET').toLowerCase();
|
|
5269
|
-
if (
|
|
5269
|
+
if (app[method]) app[method](fullPath, route.handler);
|
|
5270
5270
|
}
|
|
5271
5271
|
}
|
|
5272
5272
|
console.log(`[PLUGINS] Loaded extensions: ${names.join(', ')}`);
|