dank-ai 1.0.32 → 1.0.34

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/agent.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  const Joi = require('joi');
9
- const { DEFAULT_CONFIG, SUPPORTED_LLMS, DOCKER_CONFIG } = require('./constants');
9
+ const { DEFAULT_CONFIG, SUPPORTED_LLMS, DOCKER_CONFIG, INSTANCE_TYPES } = require('./constants');
10
10
  const { ToolRegistry, ToolExecutor } = require('./tools');
11
11
  const builtinTools = require('./tools/builtin');
12
12
 
@@ -28,6 +28,11 @@ class DankAgent {
28
28
  if (config.enableBuiltinTools !== false) {
29
29
  this.registerBuiltinTools();
30
30
  }
31
+
32
+ // Ensure instanceType has a default value if not set
33
+ if (!this.config.instanceType) {
34
+ this.config.instanceType = 'small';
35
+ }
31
36
  }
32
37
 
33
38
  /**
@@ -59,24 +64,53 @@ class DankAgent {
59
64
  }
60
65
 
61
66
  /**
62
- * Set resource limits for the container
67
+ * Set instance type (any string value - validated by backend)
68
+ * @param {string} instanceType - Instance type string
63
69
  */
64
- setResources(resources) {
65
- const schema = Joi.object({
66
- memory: Joi.string().pattern(/^\d+[mMgG]$/).default('512m'),
67
- cpu: Joi.number().min(0.1).max(8).default(1),
68
- timeout: Joi.number().min(1000).default(30000)
69
- });
70
-
71
- const { error, value } = schema.validate(resources);
72
- if (error) {
73
- throw new Error(`Invalid resource configuration: ${error.message}`);
70
+ setInstanceType(instanceType) {
71
+ if (!instanceType || typeof instanceType !== 'string') {
72
+ throw new Error('Instance type must be a non-empty string');
74
73
  }
75
74
 
76
- this.config.resources = { ...this.config.resources, ...value };
75
+ // Store the instance type as-is (no validation - backend handles valid types)
76
+ this.config.instanceType = instanceType.trim();
77
77
  return this;
78
78
  }
79
79
 
80
+ /**
81
+ * @deprecated Use setInstanceType() instead. This method is kept for backward compatibility.
82
+ * Set resource limits for the container
83
+ */
84
+ setResources(resources) {
85
+ // Map old resources format to instance type
86
+ if (resources && typeof resources === 'object') {
87
+ const memory = resources.memory || '512m';
88
+ const cpu = resources.cpu || 1;
89
+
90
+ // Try to map to an instance type
91
+ let instanceType = 'small'; // default
92
+
93
+ if (memory === '512m' && cpu === 1) {
94
+ instanceType = 'small';
95
+ } else if (memory === '1g' && cpu === 2) {
96
+ instanceType = 'medium';
97
+ } else if (memory === '2g' && cpu === 2) {
98
+ instanceType = 'large';
99
+ } else if (memory === '4g' && cpu === 4) {
100
+ instanceType = 'xlarge';
101
+ } else {
102
+ // For custom configurations, default to small and log a warning
103
+ console.warn(`⚠️ setResources() is deprecated. Using 'small' instance type. Please use setInstanceType('small'|'medium'|'large'|'xlarge') instead.`);
104
+ instanceType = 'small';
105
+ }
106
+
107
+ return this.setInstanceType(instanceType);
108
+ }
109
+
110
+ // If no resources provided, default to small
111
+ return this.setInstanceType('small');
112
+ }
113
+
80
114
  /**
81
115
  * Set Docker configuration including base image
82
116
  */
@@ -116,27 +150,33 @@ class DankAgent {
116
150
  */
117
151
  setPromptingServer(options = {}) {
118
152
  const schema = Joi.object({
119
- protocol: Joi.string().valid('websocket', 'tcp', 'http').default('http'),
120
153
  port: Joi.number().min(1000).max(65535).default(3000),
121
154
  authentication: Joi.boolean().default(false),
122
155
  maxConnections: Joi.number().min(1).max(1000).default(50),
123
- timeout: Joi.number().min(1000).default(30000)
156
+ timeout: Joi.number().min(1000).default(30000),
157
+ protocol: Joi.string().valid('http').optional() // Allow but ignore for backward compatibility
124
158
  });
125
159
 
126
- const { error, value } = schema.validate(options);
160
+ const { error, value } = schema.validate(options, {
161
+ stripUnknown: true // Strip unknown fields after validation
162
+ });
127
163
  if (error) {
128
164
  throw new Error(`Invalid prompting server configuration: ${error.message}`);
129
165
  }
166
+
167
+ // Remove protocol from value if present (we always use HTTP now)
168
+ const { protocol, ...cleanValue } = value;
130
169
 
131
170
  // Set the port in docker config
132
- this.config.docker = { ...this.config.docker, port: value.port };
171
+ this.config.docker = { ...this.config.docker, port: cleanValue.port };
133
172
 
134
- // Set the communication config (excluding port)
135
- const { port, ...communicationConfig } = value;
173
+ // Set the communication config (always HTTP, excluding port)
174
+ const { port, ...communicationConfig } = cleanValue;
136
175
  this.config.communication = {
137
176
  ...this.config.communication,
138
177
  directPrompting: {
139
178
  enabled: true,
179
+ protocol: 'http', // Always HTTP
140
180
  ...communicationConfig
141
181
  }
142
182
  };
@@ -162,6 +202,8 @@ class DankAgent {
162
202
 
163
203
  /**
164
204
  * Enable HTTP server with Express.js
205
+ * Optional: Use this to configure HTTP options (port, CORS, rate limiting, etc.)
206
+ * HTTP auto-enables when routes are added, so this is only needed for custom configuration
165
207
  */
166
208
  enableHttp(options = {}) {
167
209
  const schema = Joi.object({
@@ -186,11 +228,15 @@ class DankAgent {
186
228
  throw new Error(`Invalid HTTP configuration: ${error.message}`);
187
229
  }
188
230
 
231
+ // Preserve existing routes and middleware if HTTP was already auto-enabled
232
+ const existingRoutes = this.config.http?.routes || new Map();
233
+ const existingMiddleware = this.config.http?.middleware || [];
234
+
189
235
  this.config.http = {
190
236
  enabled: true,
191
237
  ...value,
192
- routes: new Map(),
193
- middleware: []
238
+ routes: existingRoutes,
239
+ middleware: existingMiddleware
194
240
  };
195
241
 
196
242
  return this;
@@ -198,10 +244,24 @@ class DankAgent {
198
244
 
199
245
  /**
200
246
  * Add HTTP route handler
247
+ * Auto-enables HTTP server if not already enabled
201
248
  */
202
249
  addRoute(method, path, handler, options = {}) {
250
+ // Auto-enable HTTP if not already enabled
203
251
  if (!this.config.http || !this.config.http.enabled) {
204
- throw new Error('HTTP server must be enabled before adding routes. Call enableHttp() first.');
252
+ this.config.http = {
253
+ enabled: true,
254
+ port: 3000,
255
+ host: '0.0.0.0',
256
+ cors: true,
257
+ rateLimit: {
258
+ windowMs: 15 * 60 * 1000,
259
+ max: 100,
260
+ message: 'Too many requests'
261
+ },
262
+ routes: new Map(),
263
+ middleware: []
264
+ };
205
265
  }
206
266
 
207
267
  if (typeof handler !== 'function') {
@@ -215,6 +275,11 @@ class DankAgent {
215
275
  throw new Error(`Invalid HTTP method: ${method}. Valid methods: ${validMethods.join(', ')}`);
216
276
  }
217
277
 
278
+ // Ensure routes Map exists
279
+ if (!this.config.http.routes) {
280
+ this.config.http.routes = new Map();
281
+ }
282
+
218
283
  const routeKey = `${upperMethod}:${path}`;
219
284
 
220
285
  if (!this.config.http.routes.has(routeKey)) {
@@ -239,10 +304,24 @@ class DankAgent {
239
304
 
240
305
  /**
241
306
  * Add HTTP middleware
307
+ * Auto-enables HTTP server if not already enabled
242
308
  */
243
309
  addMiddleware(middleware, options = {}) {
310
+ // Auto-enable HTTP if not already enabled
244
311
  if (!this.config.http || !this.config.http.enabled) {
245
- throw new Error('HTTP server must be enabled before adding middleware. Call enableHttp() first.');
312
+ this.config.http = {
313
+ enabled: true,
314
+ port: 3000,
315
+ host: '0.0.0.0',
316
+ cors: true,
317
+ rateLimit: {
318
+ windowMs: 15 * 60 * 1000,
319
+ max: 100,
320
+ message: 'Too many requests'
321
+ },
322
+ routes: new Map(),
323
+ middleware: []
324
+ };
246
325
  }
247
326
 
248
327
  if (typeof middleware !== 'function') {
@@ -434,7 +513,7 @@ class DankAgent {
434
513
  ...this.config.communication,
435
514
  directPrompting: {
436
515
  enabled: finalEnabled,
437
- protocol: currentDirectPrompting?.protocol || 'websocket',
516
+ protocol: 'http', // Always HTTP
438
517
  authentication: currentDirectPrompting?.authentication || false,
439
518
  maxConnections: currentDirectPrompting?.maxConnections || 100,
440
519
  timeout: currentDirectPrompting?.timeout || 30000
@@ -570,11 +649,7 @@ class DankAgent {
570
649
 
571
650
  prompt: Joi.string().optional(),
572
651
 
573
- resources: Joi.object({
574
- memory: Joi.string().pattern(/^\d+[mMgG]$/).default('512m'),
575
- cpu: Joi.number().min(0.1).max(8).default(1),
576
- timeout: Joi.number().min(1000).default(30000)
577
- }).default({}),
652
+ instanceType: Joi.string().default('small'), // Any string allowed - backend validates
578
653
 
579
654
  docker: Joi.object({
580
655
  baseImage: Joi.string().default(`${DOCKER_CONFIG.baseImagePrefix}:${DOCKER_CONFIG.defaultTag}`),
@@ -584,7 +659,7 @@ class DankAgent {
584
659
  communication: Joi.object({
585
660
  directPrompting: Joi.object({
586
661
  enabled: Joi.boolean().default(false),
587
- protocol: Joi.string().valid('websocket', 'tcp', 'http').default('http'),
662
+ protocol: Joi.string().valid('http').default('http'), // Always HTTP, kept for backward compatibility
588
663
  authentication: Joi.boolean().default(false),
589
664
  maxConnections: Joi.number().min(1).max(1000).default(50),
590
665
  timeout: Joi.number().min(1000).default(30000)
package/lib/cli/init.js CHANGED
@@ -320,20 +320,7 @@ Handle LLM interactions and modify prompts/responses:
320
320
  })
321
321
  \`\`\`
322
322
 
323
- ### 2. HTTP API Events (\`tool:http-server\`)
324
- Handle HTTP requests and responses:
325
-
326
- \`\`\`javascript
327
- .addHandler('tool:http-server:call', (data) => {
328
- console.log('HTTP request received:', data.method, data.path);
329
- })
330
-
331
- .addHandler('tool:http-server:response', (data) => {
332
- console.log('HTTP response sent:', data.statusCode);
333
- })
334
- \`\`\`
335
-
336
- ### 3. System Events
323
+ ### 2. System Events
337
324
  Handle agent lifecycle and errors:
338
325
 
339
326
  \`\`\`javascript
@@ -14,7 +14,6 @@ const analytics = require('../analytics');
14
14
  function extractAgentMetadata(agent, buildOptions, imageName) {
15
15
  const config = agent.config || {};
16
16
  const dockerConfig = config.docker || {};
17
- const resources = config.resources || {};
18
17
  const communication = config.communication || {};
19
18
  const directPrompting = communication.directPrompting || {};
20
19
  const httpConfig = config.http || {};
@@ -39,12 +38,10 @@ function extractAgentMetadata(agent, buildOptions, imageName) {
39
38
  timeout: directPrompting.timeout || 30000
40
39
  } : null;
41
40
 
42
- // Extract resources configuration
43
- const resourcesConfig = {
44
- memory: resources.memory || '512m',
45
- cpu: resources.cpu || 1,
46
- timeout: resources.timeout || 30000
47
- };
41
+ // Extract resources configuration from instance type
42
+ const { AgentConfig } = require('../config');
43
+ const instanceType = config.instanceType || 'small';
44
+ const resourcesConfig = AgentConfig.getResourcesFromInstanceType(instanceType);
48
45
 
49
46
  // Extract HTTP server configuration if enabled
50
47
  const httpServer = httpConfig.enabled ? {
@@ -159,7 +156,8 @@ async function productionBuildCommand(options) {
159
156
  namespace: options.namespace || agentImageConfig.namespace,
160
157
  tagByAgent: Boolean(options.tagByAgent || agentImageConfig.tagByAgent),
161
158
  force: options.force || false,
162
- push: options.push || false
159
+ push: options.push || false,
160
+ baseImageOverride: options.baseImageOverride || null
163
161
  };
164
162
 
165
163
  const result = await dockerManager.buildProductionImage(agent, buildOptions);
package/lib/config.js CHANGED
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  const Joi = require('joi');
6
- const { SUPPORTED_LLMS } = require('./constants');
6
+ const { SUPPORTED_LLMS, INSTANCE_TYPES } = require('./constants');
7
7
 
8
8
  class AgentConfig {
9
9
  /**
@@ -76,28 +76,24 @@ class AgentConfig {
76
76
  }
77
77
 
78
78
  /**
79
- * Validate resource configuration
79
+ * Get resources (memory, cpu) from instance type
80
+ * @param {string} instanceType - Instance type (any string, defaults to 'small' if not found)
81
+ * @returns {Object} Resources object with memory and cpu
80
82
  */
81
- static validateResources(resources) {
82
- const schema = Joi.object({
83
- memory: Joi.string()
84
- .pattern(/^\d+[mMgG]$/)
85
- .default('512m')
86
- .messages({
87
- 'string.pattern.base': 'Memory must be in format like "512m" or "1g"'
88
- }),
89
- cpu: Joi.number().min(0.1).max(32).default(1),
90
- timeout: Joi.number().min(1000).default(30000),
91
- maxRestarts: Joi.number().min(0).default(3),
92
- healthCheckInterval: Joi.number().min(1000).default(10000)
93
- });
94
-
95
- const { error, value } = schema.validate(resources);
96
- if (error) {
97
- throw new Error(`Invalid resource configuration: ${error.message}`);
83
+ static getResourcesFromInstanceType(instanceType) {
84
+ const normalizedType = (instanceType || 'small').toLowerCase().trim();
85
+ const instanceConfig = INSTANCE_TYPES[normalizedType];
86
+
87
+ // If instance type not found in mapping, default to 'small'
88
+ // Backend will handle the actual resource allocation
89
+ if (!instanceConfig) {
90
+ return INSTANCE_TYPES.small; // Default to small resources
98
91
  }
99
-
100
- return value;
92
+
93
+ return {
94
+ memory: instanceConfig.memory,
95
+ cpu: instanceConfig.cpu
96
+ };
101
97
  }
102
98
 
103
99
  /**
@@ -143,11 +139,13 @@ class AgentConfig {
143
139
  }
144
140
 
145
141
  // Add HTTP server environment variables
146
- if (agent.config.http && agent.config.http.enabled) {
142
+ // HTTP is enabled if explicitly enabled OR if routes exist (auto-enabled)
143
+ const hasRoutes = agent.config.http?.routes && agent.config.http.routes.size > 0;
144
+ if (agent.config.http && (agent.config.http.enabled || hasRoutes)) {
147
145
  env.HTTP_ENABLED = 'true';
148
- env.HTTP_PORT = agent.config.http.port.toString();
149
- env.HTTP_HOST = agent.config.http.host;
150
- env.HTTP_CORS = agent.config.http.cors.toString();
146
+ env.HTTP_PORT = (agent.config.http.port || 3000).toString();
147
+ env.HTTP_HOST = agent.config.http.host || '0.0.0.0';
148
+ env.HTTP_CORS = (agent.config.http.cors !== false).toString();
151
149
 
152
150
  if (agent.config.http.rateLimit) {
153
151
  env.HTTP_RATE_LIMIT = 'true';
@@ -158,20 +156,23 @@ class AgentConfig {
158
156
  }
159
157
 
160
158
  // Add direct prompting environment variables
159
+ // If setPromptingServer was called, it sets directPrompting.enabled = true
160
+ // The main HTTP server always runs, and /prompt endpoint is available if setPromptingServer was called
161
161
  if (agent.config.communication?.directPrompting?.enabled) {
162
162
  env.DIRECT_PROMPTING_ENABLED = 'true';
163
- env.DIRECT_PROMPTING_PROTOCOL = agent.config.communication.directPrompting.protocol || 'websocket';
164
- env.DIRECT_PROMPTING_MAX_CONNECTIONS = agent.config.communication.directPrompting.maxConnections?.toString() || '100';
165
- env.DIRECT_PROMPTING_AUTHENTICATION = agent.config.communication.directPrompting.authentication?.toString() || 'false';
166
- env.DIRECT_PROMPTING_TIMEOUT = agent.config.communication.directPrompting.timeout?.toString() || '30000';
163
+ env.DIRECT_PROMPTING_PROTOCOL = 'http'; // Always HTTP
164
+ env.DIRECT_PROMPTING_MAX_CONNECTIONS = agent.config.communication.directPrompting?.maxConnections?.toString() || '100';
165
+ env.DIRECT_PROMPTING_AUTHENTICATION = agent.config.communication.directPrompting?.authentication?.toString() || 'false';
166
+ env.DIRECT_PROMPTING_TIMEOUT = agent.config.communication.directPrompting?.timeout?.toString() || '30000';
167
167
  } else {
168
168
  env.DIRECT_PROMPTING_ENABLED = 'false';
169
169
  }
170
170
 
171
- // Add main Docker port
172
- if (agent.config.docker?.port) {
173
- env.DOCKER_PORT = agent.config.docker.port.toString();
174
- }
171
+ // Add main Docker port (always set, defaults to 3000 if not specified)
172
+ const dockerPort = agent.config.docker?.port || 3000;
173
+ env.DOCKER_PORT = dockerPort.toString();
174
+ // Health check uses the same port as the main agent port
175
+ env.HEALTH_PORT = dockerPort.toString();
175
176
 
176
177
  return env;
177
178
  }
package/lib/constants.js CHANGED
@@ -19,15 +19,31 @@ const DEFAULT_CONFIG = {
19
19
  maxTokens: 1000
20
20
  },
21
21
  prompt: 'You are a helpful AI assistant.',
22
- resources: {
23
- memory: '512m',
24
- cpu: 1,
25
- timeout: 30000
26
- },
22
+ instanceType: 'small',
27
23
  environment: {},
28
24
  custom: {}
29
25
  };
30
26
 
27
+ // Instance type mappings: instanceType -> { memory, cpu }
28
+ const INSTANCE_TYPES = {
29
+ small: {
30
+ memory: '512m',
31
+ cpu: 1
32
+ },
33
+ medium: {
34
+ memory: '1g',
35
+ cpu: 2
36
+ },
37
+ large: {
38
+ memory: '2g',
39
+ cpu: 2
40
+ },
41
+ xlarge: {
42
+ memory: '4g',
43
+ cpu: 4
44
+ }
45
+ };
46
+
31
47
  const DOCKER_CONFIG = {
32
48
  baseImage: 'deltadarkly/dank-agent-base',
33
49
  baseImagePrefix: 'deltadarkly/dank-agent-base',
@@ -54,5 +70,6 @@ module.exports = {
54
70
  SUPPORTED_LLMS,
55
71
  DEFAULT_CONFIG,
56
72
  DOCKER_CONFIG,
57
- AGENT_EVENTS
73
+ AGENT_EVENTS,
74
+ INSTANCE_TYPES
58
75
  };