dank-ai 1.0.32 → 1.0.33

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.
@@ -1165,6 +1165,10 @@ class DockerManager {
1165
1165
  const imageName = `dank-agent-${normalizedName}`;
1166
1166
  this.logger.info(`Building image for agent: ${agent.name}`);
1167
1167
 
1168
+ // Finalize agent configuration before building
1169
+ // This ensures ports and other configs are properly set
1170
+ agent.finalize();
1171
+
1168
1172
  try {
1169
1173
  const buildContext = await this.createAgentBuildContext(agent);
1170
1174
  const dockerCmd = await this.resolveDockerCommand();
@@ -1196,6 +1200,17 @@ class DockerManager {
1196
1200
 
1197
1201
  /**
1198
1202
  * Build production image with custom naming and tagging
1203
+ *
1204
+ * @param {Object} agent - The agent to build
1205
+ * @param {Object} options - Build options
1206
+ * @param {string} [options.tag="latest"] - Image tag
1207
+ * @param {string} [options.registry] - Docker registry
1208
+ * @param {string} [options.namespace] - Docker namespace
1209
+ * @param {boolean} [options.tagByAgent=false] - Use agent name as tag
1210
+ * @param {boolean} [options.force=false] - Force rebuild without cache
1211
+ * @param {boolean} [options.push=false] - Push to registry after build
1212
+ * @param {string} [options.baseImageOverride=null] - Production-only: Override base image for all agents (replaces agent's configured base image)
1213
+ * @returns {Promise<Object>} Build result with imageName and pushed status
1199
1214
  */
1200
1215
  async buildProductionImage(agent, options = {}) {
1201
1216
  const {
@@ -1205,6 +1220,7 @@ class DockerManager {
1205
1220
  tagByAgent = false,
1206
1221
  force = false,
1207
1222
  push = false,
1223
+ baseImageOverride = null, // Production-only: override base image for all agents
1208
1224
  } = options;
1209
1225
 
1210
1226
  // Normalize all components
@@ -1231,8 +1247,20 @@ class DockerManager {
1231
1247
  `Building production image for agent: ${agent.name} -> ${imageName}`
1232
1248
  );
1233
1249
 
1250
+ // Finalize agent configuration before building
1251
+ // This ensures ports and other configs are properly set
1252
+ agent.finalize();
1253
+
1254
+ // Log base image override if provided
1255
+ if (baseImageOverride) {
1256
+ this.logger.info(`🔧 Production build: Using custom base image override: ${baseImageOverride}`);
1257
+ }
1258
+
1234
1259
  try {
1235
- const buildContext = await this.createAgentBuildContext(agent);
1260
+ const buildContext = await this.createAgentBuildContext(agent, {
1261
+ isProductionBuild: true,
1262
+ baseImageOverride: baseImageOverride
1263
+ });
1236
1264
  const dockerCmd = await this.resolveDockerCommand();
1237
1265
 
1238
1266
  const buildCommand = [
@@ -1309,6 +1337,53 @@ class DockerManager {
1309
1337
  return `{\n${handlersEntries}\n }`;
1310
1338
  }
1311
1339
 
1340
+ /**
1341
+ * Generate routes code from agent configuration
1342
+ */
1343
+ generateRoutesCode(agent) {
1344
+ const routes = {};
1345
+
1346
+ // Add routes from agent HTTP configuration
1347
+ if (agent.config?.http?.routes && agent.config.http.routes.size > 0) {
1348
+ for (const [routeKey, routeList] of agent.config.http.routes) {
1349
+ const [method, path] = routeKey.split(':');
1350
+ if (!routes[path]) {
1351
+ routes[path] = {};
1352
+ }
1353
+
1354
+ // Convert route handlers to string representations
1355
+ routeList.forEach((routeObj) => {
1356
+ if (routeObj && typeof routeObj.handler === "function") {
1357
+ const handlerStr = routeObj.handler.toString();
1358
+ routes[path][method.toLowerCase()] = handlerStr;
1359
+ }
1360
+ });
1361
+ }
1362
+ }
1363
+
1364
+ // Generate the JavaScript object code
1365
+ if (Object.keys(routes).length === 0) {
1366
+ return '{}';
1367
+ }
1368
+
1369
+ const routesEntries = Object.entries(routes)
1370
+ .map(([path, methods]) => {
1371
+ const methodsEntries = Object.entries(methods)
1372
+ .map(([method, handler]) => {
1373
+ return ` ${method}: ${handler}`;
1374
+ })
1375
+ .join(",\n");
1376
+ // Quote paths that contain special characters
1377
+ const quotedPath = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(path)
1378
+ ? path
1379
+ : JSON.stringify(path);
1380
+ return ` ${quotedPath}: {\n${methodsEntries}\n }`;
1381
+ })
1382
+ .join(",\n");
1383
+
1384
+ return `{\n${routesEntries}\n }`;
1385
+ }
1386
+
1312
1387
  /**
1313
1388
  * Start agent container
1314
1389
  */
@@ -1351,12 +1426,12 @@ class DockerManager {
1351
1426
  name: containerName,
1352
1427
  Env: this.prepareEnvironmentVariables(agent),
1353
1428
  HostConfig: {
1354
- Memory: AgentConfig.parseMemory(agent.config.resources.memory),
1355
- CpuQuota: Math.floor(agent.config.resources.cpu * 100000),
1429
+ Memory: AgentConfig.parseMemory(AgentConfig.getResourcesFromInstanceType(agent.config.instanceType).memory),
1430
+ CpuQuota: Math.floor(AgentConfig.getResourcesFromInstanceType(agent.config.instanceType).cpu * 100000),
1356
1431
  CpuPeriod: 100000,
1357
1432
  RestartPolicy: {
1358
1433
  Name: "on-failure",
1359
- MaximumRetryCount: agent.config.resources.maxRestarts || 3,
1434
+ MaximumRetryCount: 3, // Default max restarts
1360
1435
  },
1361
1436
  NetworkMode: this.networkName,
1362
1437
  ...this.preparePortConfiguration(agent),
@@ -1485,10 +1560,7 @@ class DockerManager {
1485
1560
  uptime: Date.now() - startTime.getTime(),
1486
1561
  health: containerData.State.Health?.Status || "unknown",
1487
1562
  restartCount: containerData.RestartCount,
1488
- resources: {
1489
- memory: agent.config.resources.memory,
1490
- cpu: agent.config.resources.cpu,
1491
- },
1563
+ resources: AgentConfig.getResourcesFromInstanceType(agent.config.instanceType),
1492
1564
  };
1493
1565
  } catch (error) {
1494
1566
  return { status: "error", error: error.message };
@@ -1548,7 +1620,7 @@ class DockerManager {
1548
1620
  /**
1549
1621
  * Create build context for agent
1550
1622
  */
1551
- async createAgentBuildContext(agent) {
1623
+ async createAgentBuildContext(agent, options = {}) {
1552
1624
  const contextDir = path.join(
1553
1625
  __dirname,
1554
1626
  `../../.build-context-${agent.name}`
@@ -1556,12 +1628,44 @@ class DockerManager {
1556
1628
  await fs.ensureDir(contextDir);
1557
1629
 
1558
1630
  // Get the base image for this agent
1559
- const baseImageName =
1560
- agent.config.docker?.baseImage || this.defaultBaseImageName;
1631
+ // Production builds can override the base image via baseImageOverride option
1632
+ let baseImageName;
1633
+ if (options.isProductionBuild && options.baseImageOverride) {
1634
+ baseImageName = options.baseImageOverride;
1635
+ this.logger.info(`🔧 Using production base image override: ${baseImageName} (instead of ${agent.config.docker?.baseImage || this.defaultBaseImageName})`);
1636
+ } else {
1637
+ baseImageName = agent.config.docker?.baseImage || this.defaultBaseImageName;
1638
+ }
1639
+
1640
+ // Generate environment variables
1641
+ // For production builds, these need to be embedded in the image
1642
+ // For normal builds, they're injected at container creation time via startAgent()
1643
+ const env = AgentConfig.generateContainerEnv(agent);
1644
+
1645
+ // Embed env vars in Dockerfile for production builds
1646
+ // For normal builds, env vars are injected at container creation time
1647
+ const isProductionBuild = options.isProductionBuild || false;
1648
+
1649
+ let envStatements = '';
1650
+ if (isProductionBuild) {
1651
+ // Embed environment variables in Dockerfile for production builds
1652
+ // This ensures they're available when the image is deployed elsewhere
1653
+ envStatements = Object.entries(env)
1654
+ .map(([key, value]) => {
1655
+ // Escape special characters in values for Dockerfile ENV
1656
+ const escapedValue = String(value).replace(/\$/g, '$$$$').replace(/"/g, '\\"');
1657
+ return `ENV ${key}="${escapedValue}"`;
1658
+ })
1659
+ .join('\n');
1660
+
1661
+ this.logger.info(`🔌 Production build: Embedding environment variables in Dockerfile`);
1662
+ this.logger.info(` - DOCKER_PORT: ${env.DOCKER_PORT || 'not set'}`);
1663
+ }
1561
1664
 
1562
1665
  // Create Dockerfile for agent
1563
1666
  const dockerfile = `FROM ${baseImageName}
1564
1667
  COPY agent-code/ /app/agent-code/
1668
+ ${envStatements}
1565
1669
  USER dankuser
1566
1670
  `;
1567
1671
 
@@ -1574,6 +1678,7 @@ USER dankuser
1574
1678
  // Create basic agent code structure
1575
1679
  // Generate handlers from agent configuration
1576
1680
  const handlersCode = this.generateHandlersCode(agent);
1681
+ const routesCode = this.generateRoutesCode(agent);
1577
1682
 
1578
1683
  const agentCode = `
1579
1684
  // Agent: ${agent.name}
@@ -1615,7 +1720,8 @@ module.exports = {
1615
1720
  }, 10000);
1616
1721
  },
1617
1722
 
1618
- handlers: ${handlersCode}
1723
+ handlers: ${handlersCode},
1724
+ routes: ${routesCode}
1619
1725
  };
1620
1726
  `;
1621
1727
 
@@ -1642,19 +1748,21 @@ module.exports = {
1642
1748
 
1643
1749
  // Always bind the main agent port
1644
1750
  const mainPort = agent.config.docker?.port || DOCKER_CONFIG.defaultPort;
1751
+ this.logger.info(`🔌 Binding main agent port: ${mainPort} (from docker.port: ${agent.config.docker?.port || 'default'})`);
1645
1752
  portBindings[`${mainPort}/tcp`] = [{ HostPort: mainPort.toString() }];
1646
1753
 
1647
- // Also bind HTTP port if HTTP is enabled and different from main port
1648
- if (agent.config.http && agent.config.http.enabled) {
1649
- const httpPort = agent.config.http.port;
1650
- if (httpPort !== mainPort) {
1754
+ // Only bind HTTP port if HTTP is explicitly enabled AND different from main port
1755
+ // Check both http.enabled and communication.httpApi.enabled to be safe
1756
+ const httpEnabled = agent.config.http?.enabled === true;
1757
+ const httpApiEnabled = agent.config.communication?.httpApi?.enabled === true;
1758
+ if (httpEnabled || httpApiEnabled) {
1759
+ const httpPort = agent.config.http?.port;
1760
+ if (httpPort && httpPort !== mainPort) {
1651
1761
  portBindings[`${httpPort}/tcp`] = [{ HostPort: httpPort.toString() }];
1652
1762
  }
1653
1763
  }
1654
1764
 
1655
- // Always bind health check port
1656
- const healthPort = DOCKER_CONFIG.healthCheckPort;
1657
- portBindings[`${healthPort}/tcp`] = [{ HostPort: healthPort.toString() }];
1765
+ // Health check uses the same port as the main agent port, so no separate binding needed
1658
1766
 
1659
1767
  portConfig.PortBindings = portBindings;
1660
1768
  return portConfig;
@@ -1670,17 +1778,18 @@ module.exports = {
1670
1778
  const mainPort = agent.config.docker?.port || DOCKER_CONFIG.defaultPort;
1671
1779
  exposedPorts[`${mainPort}/tcp`] = {};
1672
1780
 
1673
- // Also expose HTTP port if HTTP is enabled and different from main port
1674
- if (agent.config.http && agent.config.http.enabled) {
1675
- const httpPort = agent.config.http.port;
1676
- if (httpPort !== mainPort) {
1781
+ // Only expose HTTP port if HTTP is explicitly enabled AND different from main port
1782
+ // Check both http.enabled and communication.httpApi.enabled to be safe
1783
+ const httpEnabled = agent.config.http?.enabled === true;
1784
+ const httpApiEnabled = agent.config.communication?.httpApi?.enabled === true;
1785
+ if (httpEnabled || httpApiEnabled) {
1786
+ const httpPort = agent.config.http?.port;
1787
+ if (httpPort && httpPort !== mainPort) {
1677
1788
  exposedPorts[`${httpPort}/tcp`] = {};
1678
1789
  }
1679
1790
  }
1680
1791
 
1681
- // Always expose health check port
1682
- const healthPort = DOCKER_CONFIG.healthCheckPort;
1683
- exposedPorts[`${healthPort}/tcp`] = {};
1792
+ // Health check uses the same port as the main agent port, so no separate exposure needed
1684
1793
 
1685
1794
  return { ExposedPorts: exposedPorts };
1686
1795
  }
package/lib/project.js CHANGED
@@ -85,10 +85,7 @@ module.exports = {
85
85
  protocol: 'http',
86
86
  port: 3000
87
87
  })
88
- .setResources({
89
- memory: '512m',
90
- cpu: 1
91
- })
88
+ .setInstanceType('small')
92
89
  // Event handlers for prompt modification and response enhancement
93
90
  .addHandler('request_output:start', (data) => {
94
91
  console.log('[Prompt Agent] Processing prompt:', data.conversationId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dank-ai",
3
- "version": "1.0.32",
3
+ "version": "1.0.33",
4
4
  "description": "Dank Agent Service - Docker-based AI agent orchestration platform",
5
5
  "main": "lib/index.js",
6
6
  "exports": {