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.
@@ -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._frags = new Map();
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
- for (const key of [...this._frags.keys()]) if (key.startsWith(`${sid}:`)) this._frags.delete(key);
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._frags.clear();
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 !== 'user' || e.isMeta) return null;
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
- this._bc({ type: 'streaming_progress', sessionId: sid, conversationId: cid, block, blockRole: role, seq: this._seq(sid), timestamp: Date.now(), ...extra });
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._bc({ type: 'streaming_start', sessionId: sid, conversationId: cid, agentId: 'cli-claude', timestamp: Date.now() });
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
- this._bc({ type: 'streaming_complete', sessionId: sid, conversationId: cid, agentId: 'cli-claude', eventCount: 0, seq: this._seq(sid), timestamp: Date.now() });
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 prevCount = this._frags.get(key)?.message?.content?.length || 0;
161
- const newBlocks = e.message.content.slice(prevCount);
162
- if (e.message.stop_reason === null || e.message.stop_reason === undefined) this._frags.set(key, e);
163
- else this._frags.delete(key);
164
- for (const b of newBlocks) this._emit(cid, sid, b, 'assistant');
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 blocks = Array.isArray(e.message.content) ? e.message.content : [];
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 dataType = e.data?.type || e.subtype || 'progress';
179
- const content = dataType === 'agent_progress' ? e.data?.message : (e.data || e.content);
180
- this._emit(cid, sid, { type: dataType, agentId: e.agentId, content }, 'progress');
181
- return;
182
- }
183
-
184
- if (e.isApiErrorMessage && e.error === 'rate_limit') {
185
- this._bc({ type: 'streaming_error', sessionId: sid, conversationId: cid, error: 'Rate limit hit', recoverable: true, timestamp: Date.now() });
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
 
@@ -1,162 +1,188 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { EventEmitter } from 'events';
4
-
5
- class PluginLoader extends EventEmitter {
6
- constructor(pluginDir) {
7
- super();
8
- this.pluginDir = pluginDir;
9
- this.registry = new Map();
10
- this.instances = new Map();
11
- this.states = new Map();
12
- this.watchers = new Map();
13
- this.errorCounts = new Map();
14
- this.fileMap = new Map();
15
- }
16
-
17
- async loadPlugin(fileName) {
18
- const filePath = path.join(this.pluginDir, `${fileName}.js`);
19
- if (!fs.existsSync(filePath)) {
20
- throw new Error(`Plugin file not found: ${filePath}`);
21
- }
22
- const fileUrl = `file://${filePath}?v=${Date.now()}`;
23
- try {
24
- const plugin = await import(fileUrl);
25
- const mod = plugin.default || plugin;
26
- const regName = mod.name || fileName;
27
- this.registry.set(regName, mod);
28
- this.fileMap.set(regName, fileName);
29
- return mod;
30
- } catch (error) {
31
- console.error(`Failed to load plugin ${fileName}:`, error.message);
32
- throw error;
33
- }
34
- }
35
-
36
- async initializePlugin(name, config) {
37
- const plugin = this.registry.get(name);
38
- if (!plugin) {
39
- throw new Error(`Plugin ${name} not found in registry`);
40
- }
41
- if (this.instances.has(name)) {
42
- return this.instances.get(name);
43
- }
44
- for (const depName of (plugin.dependencies || [])) {
45
- if (!this.instances.has(depName)) {
46
- await this.initializePlugin(depName, config);
47
- }
48
- }
49
- try {
50
- const result = await plugin.init(config, this.instances);
51
- this.instances.set(name, result);
52
- return result;
53
- } catch (error) {
54
- console.error(`[PluginLoader] Error initializing ${name}:`, error.message);
55
- throw error;
56
- }
57
- }
58
-
59
- get(name) {
60
- return this.instances.get(name);
61
- }
62
-
63
- async reloadPlugin(name) {
64
- const plugin = this.registry.get(name);
65
- if (!plugin) {
66
- console.warn(`[PluginLoader] Cannot reload ${name}: not found`);
67
- return;
68
- }
69
- const state = this.instances.get(name);
70
- if (!state) {
71
- console.warn(`[PluginLoader] Cannot reload ${name}: not initialized`);
72
- return;
73
- }
74
- try {
75
- if (state.stop) await state.stop();
76
- const fileName = this.fileMap.get(name) || name;
77
- await this.loadPlugin(fileName);
78
- const reloadedPlugin = this.registry.get(name);
79
- const newState = await reloadedPlugin.reload(state);
80
- this.instances.set(name, newState);
81
- this.emit('reload', { name, success: true });
82
- console.log(`[PluginLoader] Reloaded plugin: ${name}`);
83
- } catch (error) {
84
- console.error(`[PluginLoader] Error reloading ${name}:`, error.message);
85
- this.emit('reload', { name, success: false, error: error.message });
86
- }
87
- }
88
-
89
- watchPlugin(name, callback) {
90
- const fileName = this.fileMap.get(name) || name;
91
- const filePath = path.join(this.pluginDir, `${fileName}.js`);
92
- if (this.watchers.has(name)) return;
93
- const watcher = fs.watch(filePath, async (eventType) => {
94
- if (eventType === 'change') {
95
- setTimeout(() => callback(name), 100);
96
- }
97
- });
98
- this.watchers.set(name, watcher);
99
- }
100
-
101
- unwatchPlugin(name) {
102
- const watcher = this.watchers.get(name);
103
- if (watcher) {
104
- watcher.close();
105
- this.watchers.delete(name);
106
- }
107
- }
108
-
109
- async loadAllPlugins(config) {
110
- if (!fs.existsSync(this.pluginDir)) {
111
- fs.mkdirSync(this.pluginDir, { recursive: true });
112
- return;
113
- }
114
- const files = fs.readdirSync(this.pluginDir).filter(f => f.endsWith('.js'));
115
- for (const file of files) {
116
- const fileName = file.replace('.js', '');
117
- try {
118
- await this.loadPlugin(fileName);
119
- } catch (error) {
120
- console.error(`[PluginLoader] Failed to load ${fileName}:`, error.message);
121
- }
122
- }
123
- const sorted = this.topologicalSort();
124
- for (const name of sorted) {
125
- try {
126
- await this.initializePlugin(name, config);
127
- } catch (error) {
128
- console.error(`[PluginLoader] Failed to initialize ${name}:`, error.message);
129
- }
130
- }
131
- }
132
-
133
- topologicalSort() {
134
- const visited = new Set(), result = [];
135
- const visit = (name) => {
136
- if (visited.has(name)) return;
137
- visited.add(name);
138
- for (const dep of (this.registry.get(name)?.dependencies || [])) { if (this.registry.has(dep)) visit(dep); }
139
- result.push(name);
140
- };
141
- for (const name of this.registry.keys()) visit(name);
142
- return result;
143
- }
144
-
145
- async shutdown() {
146
- const sorted = this.topologicalSort().reverse();
147
- for (const name of sorted) {
148
- const state = this.instances.get(name);
149
- if (state && state.stop) {
150
- try {
151
- await state.stop();
152
- } catch (error) {
153
- console.error(`[PluginLoader] Error stopping ${name}:`, error.message);
154
- }
155
- }
156
- this.unwatchPlugin(name);
157
- }
158
- this.instances.clear();
159
- this.registry.clear();
160
- }
161
- }
162
- export default PluginLoader;
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
  }
@@ -1,6 +1,6 @@
1
1
  // Database plugin - SQLite init, schema, checkpoint recovery
2
2
 
3
- import * as dbModule from '../../database.js';
3
+ import * as dbModule from '../database.js';
4
4
 
5
5
  export default {
6
6
  name: 'database',
@@ -8,8 +8,7 @@ export default {
8
8
  dependencies: ['database'],
9
9
 
10
10
  async init(config, plugins) {
11
- const dbPlugin = plugins.get('database');
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();
@@ -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 || 'unknown'} up-to-date`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.721",
3
+ "version": "1.0.723",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
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', 'tool_update_available',
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: expressApp, baseUrl: BASE_URL, logger: console, env: process.env });
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 (expressApp[method]) expressApp[method](fullPath, route.handler);
5269
+ if (app[method]) app[method](fullPath, route.handler);
5270
5270
  }
5271
5271
  }
5272
5272
  console.log(`[PLUGINS] Loaded extensions: ${names.join(', ')}`);