agentgui 1.0.723 → 1.0.724

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.
@@ -1,188 +1,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;
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;
@@ -49,10 +49,9 @@ 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
53
52
  activeExecutions.set(id, { sessionId: session.id });
54
53
 
55
- res.json({ sessionId: session.id, pid: proc.pid });
54
+ res.json({ sessionId: session.id });
56
55
  } catch (e) {
57
56
  res.status(500).json({ error: e.message });
58
57
  }
@@ -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,7 +8,8 @@ export default {
8
8
  dependencies: ['database'],
9
9
 
10
10
  async init(config, plugins) {
11
- const db = plugins.get('database');
11
+ const dbPlugin = plugins.get('database');
12
+ const db = dbPlugin?.api || {};
12
13
  const activeSessions = new Map();
13
14
  const pendingMessages = new Map();
14
15
  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} up-to-date`);
41
+ log(`${tool.id} v${status.installedVersion || 'unknown'} 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.723",
3
+ "version": "1.0.724",
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',
4482
+ 'tool_status_update', 'tool_update_available',
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: app, baseUrl: BASE_URL, logger: console, env: process.env });
5260
+ await pluginLoader.loadAllPlugins({ router: expressApp, 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 (app[method]) app[method](fullPath, route.handler);
5269
+ if (expressApp[method]) expressApp[method](fullPath, route.handler);
5270
5270
  }
5271
5271
  }
5272
5272
  console.log(`[PLUGINS] Loaded extensions: ${names.join(', ')}`);