dank-ai 1.0.41 → 1.0.45

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.
@@ -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
- console.log('[Prompt Agent] LLM Response:', {
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
- conversationId: data.conversationId,
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');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dank-ai",
3
- "version": "1.0.41",
3
+ "version": "1.0.45",
4
4
  "description": "Dank Agent Service - Docker-based AI agent orchestration platform",
5
5
  "main": "lib/index.js",
6
6
  "exports": {