dank-ai 1.0.39 → 1.0.42
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 +429 -1393
- package/lib/agent.js +82 -6
- package/lib/cli/init.js +25 -1
- package/lib/cli/production-build.js +3 -1
- package/lib/cli/run.js +3 -1
- package/lib/docker/manager.js +165 -10
- package/lib/index.js +8 -0
- package/lib/plugins/base.js +324 -0
- package/lib/plugins/config.js +171 -0
- package/lib/plugins/events.js +186 -0
- package/lib/plugins/index.js +29 -0
- package/lib/plugins/manager.js +258 -0
- package/lib/plugins/registry.js +268 -0
- package/lib/project.js +15 -8
- package/package.json +1 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Manager - Integrates plugins with DankAgent
|
|
3
|
+
*
|
|
4
|
+
* Manages plugin lifecycle, integrates plugin handlers and tools with agents,
|
|
5
|
+
* handles plugin-to-plugin communication, and manages plugin state persistence.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { PluginRegistry } = require('./registry');
|
|
9
|
+
const { PluginEventSystem } = require('./events');
|
|
10
|
+
|
|
11
|
+
class PluginManager {
|
|
12
|
+
constructor(agent) {
|
|
13
|
+
this.agent = agent;
|
|
14
|
+
this.registry = new PluginRegistry();
|
|
15
|
+
this.plugins = new Map(); // name -> plugin instance
|
|
16
|
+
this.eventRouter = PluginEventSystem.createEventRouter();
|
|
17
|
+
this.isInitialized = false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Add a plugin to the agent
|
|
22
|
+
*/
|
|
23
|
+
async addPlugin(name, config = {}) {
|
|
24
|
+
// If name is a string, try to load from npm or local path
|
|
25
|
+
if (typeof name === 'string') {
|
|
26
|
+
// Check if it's already loaded
|
|
27
|
+
if (this.plugins.has(name)) {
|
|
28
|
+
throw new Error(`Plugin '${name}' is already loaded`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Try to load from npm first (if it looks like a package name)
|
|
32
|
+
if (name.startsWith('dank-plugin-') || name.includes('/')) {
|
|
33
|
+
try {
|
|
34
|
+
const { name: pluginName, PluginClass } = await this.registry.loadFromNpm(name);
|
|
35
|
+
const plugin = await this.registry.create(pluginName, config);
|
|
36
|
+
return await this._registerPlugin(pluginName, plugin);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
// If npm load fails, try as local path
|
|
39
|
+
if (name.includes('/') || name.endsWith('.js')) {
|
|
40
|
+
const { name: pluginName, PluginClass } = await this.registry.loadFromPath(name, { name });
|
|
41
|
+
const plugin = await this.registry.create(pluginName, config);
|
|
42
|
+
return await this._registerPlugin(pluginName, plugin);
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// Assume it's a registered plugin name
|
|
48
|
+
const plugin = await this.registry.create(name, config);
|
|
49
|
+
return await this._registerPlugin(name, plugin);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
// name is a PluginBase instance
|
|
53
|
+
const plugin = name;
|
|
54
|
+
const pluginName = plugin.name;
|
|
55
|
+
|
|
56
|
+
if (this.plugins.has(pluginName)) {
|
|
57
|
+
throw new Error(`Plugin '${pluginName}' is already loaded`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return await this._registerPlugin(pluginName, plugin);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Register a plugin instance
|
|
66
|
+
*/
|
|
67
|
+
async _registerPlugin(name, plugin) {
|
|
68
|
+
// Set agent context
|
|
69
|
+
plugin.setAgentContext({
|
|
70
|
+
agent: this.agent,
|
|
71
|
+
agentId: this.agent.id,
|
|
72
|
+
agentName: this.agent.name,
|
|
73
|
+
toolRegistry: this.agent.toolRegistry,
|
|
74
|
+
toolExecutor: this.agent.toolExecutor
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Set plugin manager reference
|
|
78
|
+
plugin.setPluginManager(this);
|
|
79
|
+
|
|
80
|
+
// Register plugin tools with agent
|
|
81
|
+
const tools = plugin.getTools();
|
|
82
|
+
for (const tool of tools) {
|
|
83
|
+
this.agent.toolRegistry.register(tool.name, {
|
|
84
|
+
description: tool.description || `Tool from plugin '${name}'`,
|
|
85
|
+
parameters: tool.parameters || {},
|
|
86
|
+
handler: tool.handler,
|
|
87
|
+
category: tool.category || 'plugin',
|
|
88
|
+
version: tool.version || '1.0.0',
|
|
89
|
+
timeout: tool.timeout || 30000,
|
|
90
|
+
retries: tool.retries || 1,
|
|
91
|
+
async: tool.async !== false,
|
|
92
|
+
cacheable: tool.cacheable || false,
|
|
93
|
+
cacheTime: tool.cacheTime || 0,
|
|
94
|
+
metadata: {
|
|
95
|
+
...tool.metadata,
|
|
96
|
+
plugin: name
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Register plugin handlers with agent
|
|
102
|
+
for (const [eventType, handlers] of plugin.handlers) {
|
|
103
|
+
for (const handlerObj of handlers) {
|
|
104
|
+
this.agent.addHandler(eventType, handlerObj.handler);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Set up event routing
|
|
109
|
+
this.eventRouter.route(`plugin:${name}:*`, this.agent);
|
|
110
|
+
this.eventRouter.route(`plugin:${name}:*`, plugin);
|
|
111
|
+
|
|
112
|
+
// Store plugin
|
|
113
|
+
this.plugins.set(name, plugin);
|
|
114
|
+
|
|
115
|
+
// Don't start plugin here - it will be started at runtime
|
|
116
|
+
// Starting plugins during build would try to connect to databases/services
|
|
117
|
+
// which is not available during Docker build
|
|
118
|
+
// Plugins will be started when the agent container starts
|
|
119
|
+
|
|
120
|
+
return plugin;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Add multiple plugins
|
|
125
|
+
*/
|
|
126
|
+
async addPlugins(plugins) {
|
|
127
|
+
// Resolve dependencies first
|
|
128
|
+
const pluginNames = Object.keys(plugins);
|
|
129
|
+
const resolved = this.registry.resolveDependencies(pluginNames);
|
|
130
|
+
|
|
131
|
+
// Load plugins in dependency order
|
|
132
|
+
for (const name of resolved) {
|
|
133
|
+
await this.addPlugin(name, plugins[name]);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get a plugin by name
|
|
141
|
+
*/
|
|
142
|
+
getPlugin(name) {
|
|
143
|
+
return this.plugins.get(name);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get all plugins
|
|
148
|
+
*/
|
|
149
|
+
getAllPlugins() {
|
|
150
|
+
return Array.from(this.plugins.values());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if plugin is loaded
|
|
155
|
+
*/
|
|
156
|
+
hasPlugin(name) {
|
|
157
|
+
return this.plugins.has(name);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Remove a plugin
|
|
162
|
+
*/
|
|
163
|
+
async removePlugin(name) {
|
|
164
|
+
const plugin = this.plugins.get(name);
|
|
165
|
+
|
|
166
|
+
if (!plugin) {
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Stop plugin
|
|
171
|
+
await plugin.stop();
|
|
172
|
+
|
|
173
|
+
// Remove plugin tools from agent
|
|
174
|
+
const tools = plugin.getTools();
|
|
175
|
+
for (const tool of tools) {
|
|
176
|
+
// Note: ToolRegistry doesn't have an unregister method yet
|
|
177
|
+
// This would need to be added if we want to fully remove tools
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Remove event routes
|
|
181
|
+
this.eventRouter.unroute(`plugin:${name}:*`, this.agent);
|
|
182
|
+
this.eventRouter.unroute(`plugin:${name}:*`, plugin);
|
|
183
|
+
|
|
184
|
+
// Remove from registry
|
|
185
|
+
await this.registry.unload(name);
|
|
186
|
+
|
|
187
|
+
// Remove from plugins map
|
|
188
|
+
this.plugins.delete(name);
|
|
189
|
+
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Remove all plugins
|
|
195
|
+
*/
|
|
196
|
+
async removeAllPlugins() {
|
|
197
|
+
const names = Array.from(this.plugins.keys());
|
|
198
|
+
for (const name of names) {
|
|
199
|
+
await this.removePlugin(name);
|
|
200
|
+
}
|
|
201
|
+
return this;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Start all plugins
|
|
206
|
+
* This should be called at runtime when the agent container starts
|
|
207
|
+
*/
|
|
208
|
+
async startAll() {
|
|
209
|
+
for (const plugin of this.plugins.values()) {
|
|
210
|
+
if (plugin.status === 'initialized' || plugin.status === 'stopped') {
|
|
211
|
+
await plugin.start();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Stop all plugins
|
|
219
|
+
*/
|
|
220
|
+
async stopAll() {
|
|
221
|
+
for (const plugin of this.plugins.values()) {
|
|
222
|
+
if (plugin.status !== 'stopped') {
|
|
223
|
+
await plugin.stop();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get plugin manager metadata
|
|
231
|
+
*/
|
|
232
|
+
getMetadata() {
|
|
233
|
+
return {
|
|
234
|
+
plugins: Array.from(this.plugins.values()).map(p => p.getMetadata()),
|
|
235
|
+
registry: this.registry.getMetadata(),
|
|
236
|
+
eventRoutes: this.eventRouter.getRoutes()
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Emit event to all plugins
|
|
242
|
+
*/
|
|
243
|
+
emitToPlugins(eventName, data) {
|
|
244
|
+
for (const plugin of this.plugins.values()) {
|
|
245
|
+
plugin.emit(eventName, data);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Route event through event router
|
|
251
|
+
*/
|
|
252
|
+
routeEvent(eventName, data) {
|
|
253
|
+
this.eventRouter.emit(eventName, data);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
module.exports = { PluginManager };
|
|
258
|
+
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Registry - Discovery, loading, and lifecycle management
|
|
3
|
+
*
|
|
4
|
+
* Manages plugin discovery, loading from npm packages or local paths,
|
|
5
|
+
* validates plugin schemas, and tracks plugin dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { PluginBase } = require('./base');
|
|
11
|
+
const { PluginConfig } = require('./config');
|
|
12
|
+
|
|
13
|
+
class PluginRegistry {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.plugins = new Map(); // name -> plugin instance
|
|
16
|
+
this.pluginClasses = new Map(); // name -> Plugin class
|
|
17
|
+
this.loadedPaths = new Map(); // name -> path
|
|
18
|
+
this.dependencies = new Map(); // name -> [dependencies]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Register a plugin class
|
|
23
|
+
*/
|
|
24
|
+
register(name, PluginClass, options = {}) {
|
|
25
|
+
if (!name || typeof name !== 'string') {
|
|
26
|
+
throw new Error('Plugin name must be a non-empty string');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!PluginClass || !(PluginClass.prototype instanceof PluginBase)) {
|
|
30
|
+
throw new Error(`Plugin '${name}' must extend PluginBase`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.pluginClasses.set(name, PluginClass);
|
|
34
|
+
|
|
35
|
+
if (options.dependencies) {
|
|
36
|
+
this.dependencies.set(name, options.dependencies);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load plugin from npm package
|
|
44
|
+
*/
|
|
45
|
+
async loadFromNpm(packageName, config = {}) {
|
|
46
|
+
try {
|
|
47
|
+
// Try to require the package
|
|
48
|
+
let PluginClass;
|
|
49
|
+
try {
|
|
50
|
+
const pluginModule = require(packageName);
|
|
51
|
+
PluginClass = pluginModule.default || pluginModule;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// If not installed, try to install it
|
|
54
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Plugin package '${packageName}' not found. ` +
|
|
57
|
+
`Install it with: npm install ${packageName}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Validate it's a PluginBase subclass
|
|
64
|
+
if (!PluginClass || !(PluginClass.prototype instanceof PluginBase)) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Package '${packageName}' does not export a PluginBase subclass`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Extract plugin name from package or use package name
|
|
71
|
+
const pluginName = PluginClass.name || packageName.replace(/^dank-plugin-/, '');
|
|
72
|
+
|
|
73
|
+
this.pluginClasses.set(pluginName, PluginClass);
|
|
74
|
+
this.loadedPaths.set(pluginName, `npm:${packageName}`);
|
|
75
|
+
|
|
76
|
+
return { name: pluginName, PluginClass };
|
|
77
|
+
} catch (error) {
|
|
78
|
+
throw new Error(`Failed to load plugin from npm package '${packageName}': ${error.message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Load plugin from local file path
|
|
84
|
+
*/
|
|
85
|
+
async loadFromPath(filePath, config = {}) {
|
|
86
|
+
try {
|
|
87
|
+
const resolvedPath = path.resolve(filePath);
|
|
88
|
+
|
|
89
|
+
if (!(await fs.pathExists(resolvedPath))) {
|
|
90
|
+
throw new Error(`Plugin file not found: ${resolvedPath}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Clear require cache for this file
|
|
94
|
+
delete require.cache[require.resolve(resolvedPath)];
|
|
95
|
+
|
|
96
|
+
const pluginModule = require(resolvedPath);
|
|
97
|
+
const PluginClass = pluginModule.default || pluginModule;
|
|
98
|
+
|
|
99
|
+
if (!PluginClass || !(PluginClass.prototype instanceof PluginBase)) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`File '${resolvedPath}' does not export a PluginBase subclass`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const pluginName = config.name || PluginClass.name || path.basename(resolvedPath, '.js');
|
|
106
|
+
|
|
107
|
+
this.pluginClasses.set(pluginName, PluginClass);
|
|
108
|
+
this.loadedPaths.set(pluginName, resolvedPath);
|
|
109
|
+
|
|
110
|
+
return { name: pluginName, PluginClass };
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw new Error(`Failed to load plugin from path '${filePath}': ${error.message}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create plugin instance
|
|
118
|
+
*/
|
|
119
|
+
async create(name, config = {}) {
|
|
120
|
+
const PluginClass = this.pluginClasses.get(name);
|
|
121
|
+
|
|
122
|
+
if (!PluginClass) {
|
|
123
|
+
throw new Error(`Plugin '${name}' not registered. Load it first with loadFromNpm() or loadFromPath()`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Inject environment variables into config
|
|
127
|
+
const injectedConfig = PluginConfig.injectEnvVars(config);
|
|
128
|
+
|
|
129
|
+
// Create plugin instance
|
|
130
|
+
const plugin = new PluginClass(injectedConfig);
|
|
131
|
+
|
|
132
|
+
// Validate plugin config if plugin has validateConfig method
|
|
133
|
+
if (typeof plugin.validateConfig === 'function') {
|
|
134
|
+
plugin.validateConfig(injectedConfig);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Initialize plugin
|
|
138
|
+
await plugin.init();
|
|
139
|
+
|
|
140
|
+
this.plugins.set(name, plugin);
|
|
141
|
+
|
|
142
|
+
return plugin;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get plugin instance
|
|
147
|
+
*/
|
|
148
|
+
get(name) {
|
|
149
|
+
return this.plugins.get(name);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get all plugin instances
|
|
154
|
+
*/
|
|
155
|
+
getAll() {
|
|
156
|
+
return Array.from(this.plugins.values());
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if plugin is registered
|
|
161
|
+
*/
|
|
162
|
+
has(name) {
|
|
163
|
+
return this.pluginClasses.has(name);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if plugin is loaded (has instance)
|
|
168
|
+
*/
|
|
169
|
+
isLoaded(name) {
|
|
170
|
+
return this.plugins.has(name);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get plugin class
|
|
175
|
+
*/
|
|
176
|
+
getClass(name) {
|
|
177
|
+
return this.pluginClasses.get(name);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get plugin dependencies
|
|
182
|
+
*/
|
|
183
|
+
getDependencies(name) {
|
|
184
|
+
return this.dependencies.get(name) || [];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Resolve plugin dependencies (topological sort)
|
|
189
|
+
*/
|
|
190
|
+
resolveDependencies(pluginNames) {
|
|
191
|
+
const resolved = [];
|
|
192
|
+
const visited = new Set();
|
|
193
|
+
const visiting = new Set();
|
|
194
|
+
|
|
195
|
+
const visit = (name) => {
|
|
196
|
+
if (visiting.has(name)) {
|
|
197
|
+
throw new Error(`Circular dependency detected involving plugin '${name}'`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (visited.has(name)) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
visiting.add(name);
|
|
205
|
+
|
|
206
|
+
const deps = this.getDependencies(name);
|
|
207
|
+
for (const dep of deps) {
|
|
208
|
+
if (!this.has(dep)) {
|
|
209
|
+
throw new Error(`Plugin '${name}' depends on '${dep}' which is not registered`);
|
|
210
|
+
}
|
|
211
|
+
visit(dep);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
visiting.delete(name);
|
|
215
|
+
visited.add(name);
|
|
216
|
+
resolved.push(name);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
for (const name of pluginNames) {
|
|
220
|
+
visit(name);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return resolved;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Unload plugin
|
|
228
|
+
*/
|
|
229
|
+
async unload(name) {
|
|
230
|
+
const plugin = this.plugins.get(name);
|
|
231
|
+
|
|
232
|
+
if (plugin) {
|
|
233
|
+
await plugin.destroy();
|
|
234
|
+
this.plugins.delete(name);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Unload all plugins
|
|
242
|
+
*/
|
|
243
|
+
async unloadAll() {
|
|
244
|
+
const names = Array.from(this.plugins.keys());
|
|
245
|
+
for (const name of names) {
|
|
246
|
+
await this.unload(name);
|
|
247
|
+
}
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get registry metadata
|
|
253
|
+
*/
|
|
254
|
+
getMetadata() {
|
|
255
|
+
return {
|
|
256
|
+
registered: Array.from(this.pluginClasses.keys()),
|
|
257
|
+
loaded: Array.from(this.plugins.keys()).map(name => ({
|
|
258
|
+
name,
|
|
259
|
+
status: this.plugins.get(name).status,
|
|
260
|
+
path: this.loadedPaths.get(name)
|
|
261
|
+
})),
|
|
262
|
+
dependencies: Object.fromEntries(this.dependencies)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
module.exports = { PluginRegistry };
|
|
268
|
+
|
package/lib/project.js
CHANGED
|
@@ -66,6 +66,9 @@ class DankProject {
|
|
|
66
66
|
* This file defines your AI agents and their configurations.
|
|
67
67
|
* Run 'dank run' to start all defined agents.
|
|
68
68
|
*
|
|
69
|
+
* NPM PACKAGES: You can import any npm package at the top of this file
|
|
70
|
+
* and use it in your handlers. Just make sure packages are in your package.json.
|
|
71
|
+
*
|
|
69
72
|
* IMPORTANT: Agent IDs (UUIDv4)
|
|
70
73
|
* ==============================
|
|
71
74
|
* Each agent has a unique UUIDv4 identifier that is generated when you initialize
|
|
@@ -76,6 +79,10 @@ class DankProject {
|
|
|
76
79
|
* locked in and owned by your account
|
|
77
80
|
*/
|
|
78
81
|
|
|
82
|
+
// Import npm packages - these will be available in your handlers
|
|
83
|
+
const axios = require('axios');
|
|
84
|
+
const { format } = require('date-fns');
|
|
85
|
+
|
|
79
86
|
const { createAgent } = require('${requirePath}');
|
|
80
87
|
|
|
81
88
|
// Agent IDs - Generated UUIDv4 identifiers for each agent
|
|
@@ -119,17 +126,17 @@ module.exports = {
|
|
|
119
126
|
prompt: enhancedPrompt
|
|
120
127
|
};
|
|
121
128
|
})
|
|
122
|
-
.addHandler('request_output', (data) => {
|
|
123
|
-
|
|
129
|
+
.addHandler('request_output', async (data) => {
|
|
130
|
+
// Example: Using imported packages in handlers
|
|
131
|
+
const timestamp = format(new Date(), 'yyyy-MM-dd HH:mm:ss');
|
|
132
|
+
console.log(\`[\${timestamp}] LLM Response:\`, {
|
|
124
133
|
prompt: data.prompt,
|
|
125
|
-
finalPrompt: data.finalPrompt,
|
|
126
|
-
promptModified: data.promptModified,
|
|
127
134
|
response: data.response,
|
|
128
|
-
|
|
129
|
-
processingTime: data.processingTime,
|
|
130
|
-
usage: data.usage,
|
|
131
|
-
model: data.model
|
|
135
|
+
processingTime: data.processingTime
|
|
132
136
|
});
|
|
137
|
+
|
|
138
|
+
// Example: Make HTTP requests with axios (uncomment to use)
|
|
139
|
+
// await axios.post('https://your-api.com/log', { response: data.response });
|
|
133
140
|
})
|
|
134
141
|
.addHandler('request_output:end', (data) => {
|
|
135
142
|
console.log('[Prompt Agent] Completed in:', data.processingTime + 'ms');
|