dank-ai 1.0.6 → 1.0.9

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Docker-based AI Agent Orchestration Platform**
4
4
 
5
- Dank is a powerful Node.js service that allows you to define, deploy, and manage AI agents using Docker containers. Each agent runs in its own isolated environment with configurable resources, LLM providers, and custom handlers.
5
+ Dank is a powerful Node.js service that allows you to define, deploy, and manage AI agents using Docker containers. Each agent runs in its own isolated environment with configurable resources, LLM providers, and custom handlers. Built for production with comprehensive CI/CD support and Docker registry integration.
6
6
 
7
7
  ## ✨ Features
8
8
 
@@ -12,6 +12,8 @@ Dank is a powerful Node.js service that allows you to define, deploy, and manage
12
12
  - **šŸ“Š Real-time Monitoring**: Built-in health checks and status monitoring
13
13
  - **šŸ”§ Flexible Handlers**: Custom event handlers for agent outputs and errors
14
14
  - **šŸŽÆ CLI Interface**: Powerful command-line tools for agent management
15
+ - **šŸ—ļø Production Builds**: Build and push Docker images to registries with custom naming
16
+ - **šŸ”„ CI/CD Ready**: Seamless integration with GitHub Actions, GitLab CI, and other platforms
15
17
 
16
18
  ## šŸš€ Quick Start
17
19
 
@@ -123,6 +125,18 @@ dank logs assistant
123
125
  dank logs assistant --follow
124
126
  ```
125
127
 
128
+ ### 8. Build for production (optional)
129
+ ```bash
130
+ # Build production images with custom naming
131
+ dank build:prod
132
+
133
+ # Build and push to registry
134
+ dank build:prod --push
135
+
136
+ # Build with custom tag and registry
137
+ dank build:prod --tag v1.0.0 --registry ghcr.io --namespace myorg --push
138
+ ```
139
+
126
140
  ## šŸ“‹ CLI Commands
127
141
 
128
142
  ### Core Commands
@@ -138,9 +152,21 @@ dank logs [agent] # View agent logs
138
152
  ```bash
139
153
  dank init [name] # Initialize new project
140
154
  dank build # Build Docker images
155
+ dank build:prod # Build agent images with custom naming
141
156
  dank clean # Clean up Docker resources
142
157
  ```
143
158
 
159
+ ### Agent Image Build Commands
160
+ ```bash
161
+ dank build:prod # Build with agent image config
162
+ dank build:prod --push # Build and push to registry (CLI only)
163
+ dank build:prod --tag v1.0.0 # Build with custom tag
164
+ dank build:prod --registry ghcr.io # Build for specific registry
165
+ dank build:prod --force # Force rebuild without cache
166
+ ```
167
+
168
+ > **šŸ’” Push Control**: The `--push` option is the only way to push images to registries. Agent configuration defines naming, CLI controls pushing.
169
+
144
170
  ### Advanced Options
145
171
  ```bash
146
172
  dank run --detached # Run in background
@@ -150,6 +176,15 @@ dank status --watch # Live status monitoring
150
176
  dank logs --follow # Follow log output
151
177
  ```
152
178
 
179
+ ### Production Build Options
180
+ ```bash
181
+ dank build:prod --push # Build and push to registry
182
+ dank build:prod --tag v1.0.0 # Build with custom tag
183
+ dank build:prod --registry ghcr.io # Build for GitHub Container Registry
184
+ dank build:prod --namespace mycompany # Build with custom namespace
185
+ dank build:prod --force # Force rebuild without cache
186
+ ```
187
+
153
188
  ## šŸ¤– Agent Configuration
154
189
 
155
190
  ### Basic Agent Setup
@@ -541,6 +576,333 @@ Configure container resources:
541
576
  })
542
577
  ```
543
578
 
579
+ ### Agent Image Configuration
580
+
581
+ Configure Docker image naming and registry settings for agent builds:
582
+
583
+ ```javascript
584
+ // Complete agent image configuration
585
+ .setAgentImageConfig({
586
+ registry: 'ghcr.io', // Docker registry URL
587
+ namespace: 'mycompany', // Organization/namespace
588
+ tag: 'v1.0.0' // Image tag
589
+ })
590
+ ```
591
+
592
+ #### šŸ—ļø **Agent Image Build Workflow**
593
+
594
+ The agent image build feature allows you to create properly tagged Docker images for deployment to container registries. This is essential for:
595
+
596
+ - **CI/CD Pipelines**: Automated builds and deployments
597
+ - **Container Orchestration**: Kubernetes, Docker Swarm, etc.
598
+ - **Multi-Environment Deployments**: Dev, staging, production
599
+ - **Version Management**: Semantic versioning with tags
600
+
601
+ > **šŸ“ Note**: Image pushing is controlled exclusively by the CLI `--push` option. Agent configuration only defines image naming (registry, namespace, tag) - not push behavior.
602
+
603
+ #### šŸ“‹ **Complete Agent Image Example**
604
+
605
+ ```javascript
606
+ const { createAgent } = require('dank');
607
+
608
+ module.exports = {
609
+ name: 'production-system',
610
+
611
+ agents: [
612
+ // Production-ready customer service agent
613
+ createAgent('customer-service')
614
+ .setLLM('openai', {
615
+ apiKey: process.env.OPENAI_API_KEY,
616
+ model: 'gpt-4',
617
+ temperature: 0.7
618
+ })
619
+ .setPrompt('You are a professional customer service representative.')
620
+ .setPromptingServer({
621
+ protocol: 'http',
622
+ port: 3000,
623
+ authentication: true,
624
+ maxConnections: 100
625
+ })
626
+ .setResources({
627
+ memory: '1g',
628
+ cpu: 2,
629
+ timeout: 60000
630
+ })
631
+ // Agent image configuration
632
+ .setAgentImageConfig({
633
+ registry: 'ghcr.io',
634
+ namespace: 'mycompany',
635
+ tag: 'v1.2.0'
636
+ })
637
+ .addHandler('request_output', (data) => {
638
+ // Log for production monitoring
639
+ console.log(`[${new Date().toISOString()}] Customer Service: ${data.response.substring(0, 100)}...`);
640
+ }),
641
+
642
+ // Data processing agent with different registry
643
+ createAgent('data-processor')
644
+ .setLLM('openai', {
645
+ apiKey: process.env.OPENAI_API_KEY,
646
+ model: 'gpt-4',
647
+ temperature: 0.1
648
+ })
649
+ .setPrompt('You are a data analysis expert.')
650
+ .setPromptingServer({
651
+ protocol: 'http',
652
+ port: 3001,
653
+ authentication: false,
654
+ maxConnections: 50
655
+ })
656
+ .setResources({
657
+ memory: '2g',
658
+ cpu: 4,
659
+ timeout: 120000
660
+ })
661
+ // Different agent image configuration
662
+ .setAgentImageConfig({
663
+ registry: 'docker.io',
664
+ namespace: 'mycompany',
665
+ tag: 'latest'
666
+ })
667
+ .addHandler('request_output', (data) => {
668
+ console.log(`[Data Processor] Analysis completed: ${data.processingTime}ms`);
669
+ })
670
+ ]
671
+ };
672
+ ```
673
+
674
+ #### šŸš€ **Production Build Commands**
675
+
676
+ **Basic Production Build:**
677
+ ```bash
678
+ # Build all agents with their image configuration
679
+ dank build:prod
680
+
681
+ # Build with custom configuration file
682
+ dank build:prod --config production.config.js
683
+ ```
684
+
685
+ **Registry and Tagging:**
686
+ ```bash
687
+ # Build with custom tag
688
+ dank build:prod --tag v2.1.0
689
+
690
+ # Build for GitHub Container Registry
691
+ dank build:prod --registry ghcr.io --namespace myorg
692
+
693
+ # Build for Docker Hub
694
+ dank build:prod --registry docker.io --namespace mycompany
695
+
696
+ # Build for private registry
697
+ dank build:prod --registry registry.company.com --namespace ai-agents
698
+ ```
699
+
700
+ **Push and Force Rebuild:**
701
+ ```bash
702
+ # Build and push to registry
703
+ dank build:prod --push
704
+
705
+ # Force rebuild without cache
706
+ dank build:prod --force
707
+
708
+ # Force rebuild and push
709
+ dank build:prod --force --push
710
+
711
+ # Build with custom tag and push
712
+ dank build:prod --tag release-2024.1 --push
713
+ ```
714
+
715
+ #### šŸ·ļø **Image Naming Convention**
716
+
717
+ **With Agent Configuration:**
718
+ - Format: `{registry}/{namespace}/{agent-name}:{tag}`
719
+ - Example: `ghcr.io/mycompany/customer-service:v1.2.0`
720
+
721
+ **With CLI Override:**
722
+ - CLI options override agent configuration
723
+ - Example: `dank build:prod --tag v2.0.0` overrides agent's tag
724
+
725
+ **Without Configuration:**
726
+ - Format: `{agent-name}:{tag}`
727
+ - Example: `customer-service:latest`
728
+
729
+ #### šŸ”§ **Registry Authentication**
730
+
731
+ **Docker Hub:**
732
+ ```bash
733
+ # Login to Docker Hub
734
+ docker login
735
+
736
+ # Build and push
737
+ dank build:prod --registry docker.io --namespace myusername --push
738
+ ```
739
+
740
+ **GitHub Container Registry:**
741
+ ```bash
742
+ # Login to GHCR
743
+ echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
744
+
745
+ # Build and push
746
+ dank build:prod --registry ghcr.io --namespace myorg --push
747
+ ```
748
+
749
+ **Private Registry:**
750
+ ```bash
751
+ # Login to private registry
752
+ docker login registry.company.com
753
+
754
+ # Build and push
755
+ dank build:prod --registry registry.company.com --namespace ai-agents --push
756
+ ```
757
+
758
+ #### šŸ“Š **Build Output Example**
759
+
760
+ ```bash
761
+ $ dank build:prod --push
762
+
763
+ šŸ—ļø Building production Docker images...
764
+
765
+ šŸ“¦ Building production image for agent: customer-service
766
+ info: Building production image for agent: customer-service -> ghcr.io/mycompany/customer-service:v1.2.0
767
+ Step 1/3 : FROM deltadarkly/dank-agent-base:latest
768
+ ---> 7b560f235fe3
769
+ Step 2/3 : COPY agent-code/ /app/agent-code/
770
+ ---> d766de6e95c4
771
+ Step 3/3 : USER dankuser
772
+ ---> Running in c773e808270c
773
+ Successfully built 43a664c636a2
774
+ Successfully tagged ghcr.io/mycompany/customer-service:v1.2.0
775
+ info: Production image 'ghcr.io/mycompany/customer-service:v1.2.0' built successfully
776
+ info: Pushing image to registry: ghcr.io/mycompany/customer-service:v1.2.0
777
+ info: Successfully pushed image: ghcr.io/mycompany/customer-service:v1.2.0
778
+ āœ… Successfully built: ghcr.io/mycompany/customer-service:v1.2.0
779
+ šŸš€ Successfully pushed: ghcr.io/mycompany/customer-service:v1.2.0
780
+
781
+ šŸ“Š Build Summary:
782
+ ================
783
+ āœ… Successful builds: 2
784
+ šŸš€ Pushed to registry: 2
785
+
786
+ šŸ“¦ Built Images:
787
+ - ghcr.io/mycompany/customer-service:v1.2.0
788
+ - docker.io/mycompany/data-processor:latest
789
+
790
+ šŸŽ‰ Production build completed successfully!
791
+ ```
792
+
793
+ #### šŸ”„ **CI/CD Integration**
794
+
795
+ **GitHub Actions Example:**
796
+ ```yaml
797
+ name: Build and Push Production Images
798
+
799
+ on:
800
+ push:
801
+ tags:
802
+ - 'v*'
803
+
804
+ jobs:
805
+ build:
806
+ runs-on: ubuntu-latest
807
+ steps:
808
+ - uses: actions/checkout@v3
809
+
810
+ - name: Setup Node.js
811
+ uses: actions/setup-node@v3
812
+ with:
813
+ node-version: '18'
814
+
815
+ - name: Install Dank
816
+ run: npm install -g dank-ai
817
+
818
+ - name: Login to GHCR
819
+ uses: docker/login-action@v2
820
+ with:
821
+ registry: ghcr.io
822
+ username: ${{ github.actor }}
823
+ password: ${{ secrets.GITHUB_TOKEN }}
824
+
825
+ - name: Build and Push Production Images
826
+ run: |
827
+ dank build:prod \
828
+ --registry ghcr.io \
829
+ --namespace ${{ github.repository_owner }} \
830
+ --tag ${{ github.ref_name }} \
831
+ --push
832
+ ```
833
+
834
+ **GitLab CI Example:**
835
+ ```yaml
836
+ build_production:
837
+ stage: build
838
+ image: node:18
839
+ before_script:
840
+ - npm install -g dank-ai
841
+ - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
842
+ script:
843
+ - dank build:prod --registry $CI_REGISTRY --namespace $CI_PROJECT_NAMESPACE --tag $CI_COMMIT_TAG --push
844
+ only:
845
+ - tags
846
+ ```
847
+
848
+ #### 🐳 **Docker Compose Integration**
849
+
850
+ Use your production images in Docker Compose:
851
+
852
+ ```yaml
853
+ version: '3.8'
854
+
855
+ services:
856
+ customer-service:
857
+ image: ghcr.io/mycompany/customer-service:v1.2.0
858
+ ports:
859
+ - "3000:3000"
860
+ environment:
861
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
862
+ restart: unless-stopped
863
+
864
+ data-processor:
865
+ image: docker.io/mycompany/data-processor:latest
866
+ ports:
867
+ - "3001:3001"
868
+ environment:
869
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
870
+ restart: unless-stopped
871
+ ```
872
+
873
+ #### 🚨 **Troubleshooting Production Builds**
874
+
875
+ **Common Issues:**
876
+
877
+ 1. **Registry Authentication:**
878
+ ```bash
879
+ # Error: authentication required
880
+ # Solution: Login to registry first
881
+ docker login ghcr.io
882
+ ```
883
+
884
+ 2. **Push Permissions:**
885
+ ```bash
886
+ # Error: denied: push access denied
887
+ # Solution: Check namespace permissions or use personal namespace
888
+ dank build:prod --namespace your-username --push
889
+ ```
890
+
891
+ 3. **Image Already Exists:**
892
+ ```bash
893
+ # Error: image already exists
894
+ # Solution: Use different tag or force rebuild
895
+ dank build:prod --tag v1.2.1 --push
896
+ ```
897
+
898
+ 4. **Build Context Issues:**
899
+ ```bash
900
+ # Error: build context too large
901
+ # Solution: Add .dockerignore file
902
+ echo "node_modules/" > .dockerignore
903
+ echo "*.log" >> .dockerignore
904
+ ```
905
+
544
906
  ## šŸ—ļø Project Structure
545
907
 
546
908
  ```
@@ -1306,7 +1668,7 @@ export NODE_ENV=production
1306
1668
  # 2. Build optimized images
1307
1669
  dank build --force
1308
1670
 
1309
- # 3. Start with production config
1671
+ # 3. Start with image config
1310
1672
  dank run --detached
1311
1673
 
1312
1674
  # 4. Monitor and scale as needed
package/bin/dank CHANGED
@@ -75,6 +75,21 @@ program
75
75
  await buildCommand(options);
76
76
  });
77
77
 
78
+ // Production build command - build and optionally push production images
79
+ program
80
+ .command('build:prod')
81
+ .description('Build production Docker images with custom naming and tagging')
82
+ .option('-c, --config <file>', 'Configuration file path', 'dank.config.js')
83
+ .option('--push', 'Push images to registry after building')
84
+ .option('--tag <tag>', 'Custom tag for images (default: latest)')
85
+ .option('--registry <registry>', 'Docker registry URL (e.g., docker.io, ghcr.io)')
86
+ .option('--namespace <namespace>', 'Docker namespace/organization')
87
+ .option('--force', 'Force rebuild without cache')
88
+ .action(async (options) => {
89
+ const { productionBuildCommand } = require('../lib/cli/production-build');
90
+ await productionBuildCommand(options);
91
+ });
92
+
78
93
  // Clean command - cleanup Docker resources
79
94
  program
80
95
  .command('clean')
package/lib/agent.js CHANGED
@@ -500,6 +500,27 @@ class DankAgent {
500
500
  return this;
501
501
  }
502
502
 
503
+ /**
504
+ * Set agent image configuration for Docker builds
505
+ */
506
+ setAgentImageConfig(options = {}) {
507
+ const schema = Joi.object({
508
+ registry: Joi.string().optional(),
509
+ namespace: Joi.string().optional(),
510
+ tag: Joi.string().default('latest')
511
+ });
512
+
513
+ const { error, value } = schema.validate(options);
514
+ if (error) {
515
+ throw new Error(`Invalid agent image configuration: ${error.message}`);
516
+ }
517
+
518
+ this.config.agentImage = value;
519
+ return this;
520
+ }
521
+
522
+
523
+
503
524
  /**
504
525
  * Get the complete agent configuration for serialization
505
526
  */
@@ -589,6 +610,12 @@ class DankAgent {
589
610
 
590
611
  environment: Joi.object().pattern(Joi.string(), Joi.string()).default({}),
591
612
 
613
+ agentImage: Joi.object({
614
+ registry: Joi.string().optional(),
615
+ namespace: Joi.string().optional(),
616
+ tag: Joi.string().default('latest')
617
+ }).optional(),
618
+
592
619
  custom: Joi.object().default({}),
593
620
 
594
621
  tools: Joi.object({
@@ -0,0 +1,110 @@
1
+ /**
2
+ * CLI Production Build Command - Build and optionally push production Docker images
3
+ */
4
+
5
+ const fs = require('fs-extra');
6
+ const path = require('path');
7
+ const chalk = require('chalk');
8
+ const { DockerManager } = require('../docker/manager');
9
+
10
+ async function productionBuildCommand(options) {
11
+ try {
12
+ console.log(chalk.yellow('šŸ—ļø Building production Docker images...\n'));
13
+
14
+ // Load configuration
15
+ const configPath = path.resolve(options.config);
16
+ if (!await fs.pathExists(configPath)) {
17
+ throw new Error(`Configuration file not found: ${configPath}`);
18
+ }
19
+
20
+ const config = require(configPath);
21
+ if (!config.agents || !Array.isArray(config.agents)) {
22
+ throw new Error('No agents found in configuration');
23
+ }
24
+
25
+ // Initialize Docker manager
26
+ const dockerManager = new DockerManager();
27
+ await dockerManager.initialize();
28
+
29
+ // Build production images for each agent
30
+ const buildResults = [];
31
+ for (const agent of config.agents) {
32
+ try {
33
+ console.log(chalk.blue(`šŸ“¦ Building production image for agent: ${agent.name}`));
34
+
35
+ // Use agent's image config if available, otherwise use CLI options
36
+ const agentImageConfig = agent.config?.agentImage || {};
37
+ const buildOptions = {
38
+ tag: options.tag || agentImageConfig.tag || 'latest',
39
+ registry: options.registry || agentImageConfig.registry,
40
+ namespace: options.namespace || agentImageConfig.namespace,
41
+ force: options.force || false,
42
+ push: options.push || false
43
+ };
44
+
45
+ const result = await dockerManager.buildProductionImage(agent, buildOptions);
46
+
47
+ buildResults.push({
48
+ agent: agent.name,
49
+ imageName: result.imageName,
50
+ success: true,
51
+ pushed: result.pushed || false
52
+ });
53
+
54
+ console.log(chalk.green(`āœ… Successfully built: ${result.imageName}`));
55
+ if (result.pushed) {
56
+ console.log(chalk.green(`šŸš€ Successfully pushed: ${result.imageName}`));
57
+ }
58
+
59
+ } catch (error) {
60
+ console.error(chalk.red(`āŒ Failed to build agent ${agent.name}:`), error.message);
61
+ buildResults.push({
62
+ agent: agent.name,
63
+ success: false,
64
+ error: error.message
65
+ });
66
+ }
67
+ }
68
+
69
+ // Print summary
70
+ console.log(chalk.yellow('\nšŸ“Š Build Summary:'));
71
+ console.log(chalk.gray('================'));
72
+
73
+ const successful = buildResults.filter(r => r.success);
74
+ const failed = buildResults.filter(r => !r.success);
75
+ const pushed = buildResults.filter(r => r.pushed);
76
+
77
+ console.log(chalk.green(`āœ… Successful builds: ${successful.length}`));
78
+ if (pushed.length > 0) {
79
+ console.log(chalk.blue(`šŸš€ Pushed to registry: ${pushed.length}`));
80
+ }
81
+ if (failed.length > 0) {
82
+ console.log(chalk.red(`āŒ Failed builds: ${failed.length}`));
83
+ }
84
+
85
+ // List built images
86
+ if (successful.length > 0) {
87
+ console.log(chalk.cyan('\nšŸ“¦ Built Images:'));
88
+ successful.forEach(result => {
89
+ console.log(chalk.gray(` - ${result.imageName}`));
90
+ });
91
+ }
92
+
93
+ // List failed builds
94
+ if (failed.length > 0) {
95
+ console.log(chalk.red('\nāŒ Failed Builds:'));
96
+ failed.forEach(result => {
97
+ console.log(chalk.gray(` - ${result.agent}: ${result.error}`));
98
+ });
99
+ process.exit(1);
100
+ }
101
+
102
+ console.log(chalk.green('\nšŸŽ‰ Production build completed successfully!'));
103
+
104
+ } catch (error) {
105
+ console.error(chalk.red('āŒ Production build failed:'), error.message);
106
+ process.exit(1);
107
+ }
108
+ }
109
+
110
+ module.exports = { productionBuildCommand };
@@ -413,6 +413,78 @@ class DockerManager {
413
413
  }
414
414
  }
415
415
 
416
+ /**
417
+ * Build production image with custom naming and tagging
418
+ */
419
+ async buildProductionImage(agent, options = {}) {
420
+ const {
421
+ tag = 'latest',
422
+ registry,
423
+ namespace,
424
+ force = false,
425
+ push = false
426
+ } = options;
427
+
428
+ // Construct production image name
429
+ let imageName = agent.name.toLowerCase();
430
+
431
+ // Add namespace if provided
432
+ if (namespace) {
433
+ imageName = `${namespace}/${imageName}`;
434
+ }
435
+
436
+ // Add registry if provided
437
+ if (registry) {
438
+ imageName = `${registry}/${imageName}`;
439
+ }
440
+
441
+ // Add tag
442
+ imageName = `${imageName}:${tag}`;
443
+
444
+ this.logger.info(`Building production image for agent: ${agent.name} -> ${imageName}`);
445
+
446
+ try {
447
+ const buildContext = await this.createAgentBuildContext(agent);
448
+
449
+ const stream = await this.docker.buildImage(buildContext, {
450
+ t: imageName,
451
+ dockerfile: 'Dockerfile',
452
+ nocache: force
453
+ });
454
+
455
+ await this.followBuildProgress(stream, `Production build for ${agent.name}`);
456
+
457
+ this.logger.info(`Production image '${imageName}' built successfully`);
458
+
459
+ // Clean up build context
460
+ await fs.remove(buildContext);
461
+
462
+ let pushed = false;
463
+
464
+ // Push to registry if requested
465
+ if (push) {
466
+ try {
467
+ this.logger.info(`Pushing image to registry: ${imageName}`);
468
+ const pushStream = await this.docker.getImage(imageName).push();
469
+ await this.followBuildProgress(pushStream, `Push ${imageName}`);
470
+ this.logger.info(`Successfully pushed image: ${imageName}`);
471
+ pushed = true;
472
+ } catch (pushError) {
473
+ this.logger.warn(`Failed to push image ${imageName}: ${pushError.message}`);
474
+ // Don't fail the build if push fails
475
+ }
476
+ }
477
+
478
+ return {
479
+ imageName,
480
+ pushed
481
+ };
482
+
483
+ } catch (error) {
484
+ throw new Error(`Failed to build production image: ${error.message}`);
485
+ }
486
+ }
487
+
416
488
  /**
417
489
  * Generate handlers code from agent configuration
418
490
  */
package/lib/project.js CHANGED
@@ -1,14 +1,11 @@
1
1
  /**
2
- * DankProject - Project Management Class
2
+ * Dank Project Management
3
3
  *
4
- * This class manages a collection of agents and provides
5
- * project-level configuration and operations.
4
+ * Handles project initialization, configuration generation, and scaffolding
6
5
  */
7
6
 
8
7
  const fs = require('fs-extra');
9
8
  const path = require('path');
10
- const yaml = require('js-yaml');
11
- const { DankAgent } = require('./agent');
12
9
 
13
10
  class DankProject {
14
11
  constructor(name, options = {}) {
@@ -16,99 +13,15 @@ class DankProject {
16
13
  this.options = {
17
14
  configFile: 'dank.config.js',
18
15
  agentsDir: 'agents',
19
- outputDir: '.dank',
16
+ outputDir: 'output',
17
+ template: 'basic',
20
18
  ...options
21
19
  };
22
-
23
- this.agents = new Map();
24
- this.projectPath = process.cwd();
25
- this.configPath = path.join(this.projectPath, this.options.configFile);
26
- this.createdAt = new Date().toISOString();
27
- }
28
-
29
- /**
30
- * Add an agent to the project
31
- */
32
- addAgent(agent) {
33
- if (!(agent instanceof DankAgent)) {
34
- throw new Error('Agent must be an instance of DankAgent');
35
- }
36
-
37
- if (this.agents.has(agent.name)) {
38
- throw new Error(`Agent with name '${agent.name}' already exists`);
39
- }
40
-
41
- this.agents.set(agent.name, agent);
42
- return this;
43
- }
44
-
45
- /**
46
- * Remove an agent from the project
47
- */
48
- removeAgent(name) {
49
- if (!this.agents.has(name)) {
50
- throw new Error(`Agent '${name}' not found`);
51
- }
52
-
53
- this.agents.delete(name);
54
- return this;
55
- }
56
-
57
- /**
58
- * Get an agent by name
59
- */
60
- getAgent(name) {
61
- return this.agents.get(name);
20
+ this.projectPath = path.resolve(process.cwd(), name);
62
21
  }
63
22
 
64
23
  /**
65
- * Get all agents
66
- */
67
- getAllAgents() {
68
- return Array.from(this.agents.values());
69
- }
70
-
71
- /**
72
- * Save project configuration to file
73
- */
74
- async save() {
75
- const config = this.toConfig();
76
-
77
- // Ensure output directory exists
78
- await fs.ensureDir(path.join(this.projectPath, this.options.outputDir));
79
-
80
- // Save as YAML for readability
81
- const yamlConfig = yaml.dump(config, {
82
- indent: 2,
83
- lineWidth: 120,
84
- noCompatMode: true
85
- });
86
-
87
- const configFile = path.join(this.projectPath, this.options.outputDir, 'project.yaml');
88
- await fs.writeFile(configFile, yamlConfig, 'utf8');
89
-
90
- console.log(`Project configuration saved to: ${configFile}`);
91
- return configFile;
92
- }
93
-
94
- /**
95
- * Load project configuration from file
96
- */
97
- async load() {
98
- const configFile = path.join(this.projectPath, this.options.outputDir, 'project.yaml');
99
-
100
- if (!(await fs.pathExists(configFile))) {
101
- throw new Error(`Project configuration not found: ${configFile}`);
102
- }
103
-
104
- const yamlContent = await fs.readFile(configFile, 'utf8');
105
- const config = yaml.load(yamlContent);
106
-
107
- return this.fromConfig(config);
108
- }
109
-
110
- /**
111
- * Initialize a new project structure
24
+ * Initialize project structure and create example files
112
25
  */
113
26
  async init() {
114
27
  const projectDir = this.projectPath;
@@ -126,68 +39,23 @@ class DankProject {
126
39
  console.log(`Created example configuration: ${configPath}`);
127
40
  }
128
41
 
129
- // Create example agent
130
- const exampleAgent = this._generateExampleAgent();
131
- const agentPath = path.join(projectDir, this.options.agentsDir, 'example-agent.js');
132
-
133
- if (!(await fs.pathExists(agentPath))) {
134
- await fs.writeFile(agentPath, exampleAgent, 'utf8');
135
- console.log(`Created example agent: ${agentPath}`);
136
- }
137
-
138
42
  console.log(`\\nDank project '${this.name}' initialized!`);
139
43
  console.log(`\\nNext steps:`);
140
44
  console.log(`1. Edit ${this.options.configFile} to configure your agents`);
141
45
  console.log(`2. Run 'dank run' to start your agents`);
142
46
 
143
- return this;
144
- }
145
-
146
- /**
147
- * Convert project to configuration object
148
- */
149
- toConfig() {
150
47
  return {
151
- name: this.name,
152
- version: '1.0.0',
153
- createdAt: this.createdAt,
154
- options: this.options,
155
- agents: Object.fromEntries(
156
- Array.from(this.agents.entries()).map(([name, agent]) => [
157
- name,
158
- agent.toConfig()
159
- ])
160
- )
48
+ projectPath: projectDir,
49
+ configFile: configPath
161
50
  };
162
51
  }
163
52
 
164
- /**
165
- * Create project from configuration object
166
- */
167
- fromConfig(config) {
168
- this.name = config.name;
169
- this.createdAt = config.createdAt;
170
- this.options = { ...this.options, ...config.options };
171
-
172
- // Restore agents
173
- this.agents.clear();
174
- if (config.agents) {
175
- Object.entries(config.agents).forEach(([name, agentConfig]) => {
176
- const agent = DankAgent.fromConfig(agentConfig);
177
- this.agents.set(name, agent);
178
- });
179
- }
180
-
181
- return this;
182
- }
183
-
184
53
  /**
185
54
  * Generate example configuration file
186
55
  */
187
56
  _generateExampleConfig() {
188
- // Detect if we're in development mode (inside the dank repo)
189
- const isDevelopment = this.projectPath.includes('/dank') &&
190
- fs.existsSync(path.join(this.projectPath, '../lib/index.js'));
57
+ // Check if we're in development mode (local lib directory exists)
58
+ const isDevelopment = fs.existsSync(path.join(this.projectPath, '../lib/index.js'));
191
59
 
192
60
  const requirePath = isDevelopment ? '../lib/index.js' : 'dank-ai';
193
61
 
@@ -205,6 +73,7 @@ module.exports = {
205
73
  name: '${this.name}',
206
74
 
207
75
  // Define your agents
76
+ // Each agent can have custom Docker image configuration for production builds
208
77
  agents: [
209
78
  // Example 1: Direct Prompting Agent with Event Handlers
210
79
  createAgent('prompt-agent')
@@ -358,6 +227,12 @@ module.exports = {
358
227
  memory: '2g',
359
228
  cpu: 2
360
229
  })
230
+ // Agent image configuration for Docker builds
231
+ .setAgentImageConfig({
232
+ registry: 'ghcr.io',
233
+ namespace: 'mycompany',
234
+ tag: 'latest'
235
+ })
361
236
  // HTTP API routes
362
237
  .get('/creative', (req, res) => {
363
238
  res.json({
@@ -401,119 +276,8 @@ module.exports = {
401
276
  })
402
277
  ]
403
278
  };
404
- `;
405
- }
406
-
407
- /**
408
- * Generate example agent file
409
- */
410
- _generateExampleAgent() {
411
- return `/**
412
- * Example Dank Agent
413
- *
414
- * This is an example of how to define a Dank agent with modern event handling.
415
- * You can create multiple agent files and import them in your config.
416
- */
417
-
418
- const { createAgent } = require('dank-ai');
419
-
420
- const exampleAgent = createAgent('example-agent')
421
- .setLLM('openai', {
422
- apiKey: process.env.OPENAI_API_KEY,
423
- model: 'gpt-3.5-turbo',
424
- temperature: 0.7
425
- })
426
- .setPrompt(\`
427
- You are a helpful AI assistant with the following capabilities:
428
- - Answer questions clearly and concisely
429
- - Provide code examples when appropriate
430
- - Be friendly and professional
431
- - Help with problem-solving and creative tasks
432
- \`)
433
- .setBaseImage('nodejs-20')
434
- .setPromptingServer({
435
- protocol: 'http',
436
- port: 3000
437
- })
438
- .setResources({
439
- memory: '512m',
440
- cpu: 1,
441
- timeout: 30000
442
- })
443
- // HTTP API routes
444
- .get('/info', (req, res) => {
445
- res.json({
446
- agent: 'example-agent',
447
- status: 'running',
448
- capabilities: ['direct-prompting', 'http-api'],
449
- timestamp: new Date().toISOString()
450
- });
451
- })
452
- .post('/chat', (req, res) => {
453
- res.json({
454
- message: 'Chat endpoint ready',
455
- data: req.body,
456
- timestamp: new Date().toISOString()
457
- });
458
- })
459
- // Event handlers for prompt processing
460
- .addHandler('request_output:start', (data) => {
461
- console.log(\`[\${new Date().toISOString()}] Processing prompt:\`, data.conversationId);
462
- console.log('Original prompt:', data.prompt);
463
-
464
- // Enhance the prompt
465
- const enhancedPrompt = \`[Enhanced] \${data.prompt}\\n\\nPlease provide a helpful and detailed response.\`;
466
-
467
- return {
468
- prompt: enhancedPrompt
469
- };
470
- })
471
- .addHandler('request_output', (data) => {
472
- console.log(\`[\${new Date().toISOString()}] LLM Response:\`, {
473
- conversationId: data.conversationId,
474
- promptModified: data.promptModified,
475
- processingTime: data.processingTime,
476
- model: data.model
477
- });
478
- })
479
- .addHandler('request_output:end', (data) => {
480
- console.log(\`[\${new Date().toISOString()}] Response completed in:\`, data.processingTime + 'ms');
481
-
482
- // Enhance the response
483
- const enhancedResponse = \`\${data.response}\\n\\n---\\nšŸ¤– Powered by Dank Framework\`;
484
-
485
- return {
486
- response: enhancedResponse
487
- };
488
- })
489
- .addHandler('request_output:error', (data) => {
490
- console.error(\`[\${new Date().toISOString()}] Error processing prompt:\`, data.error);
491
- })
492
- // HTTP tool event handlers
493
- .addHandler('tool:http-server:call', (data) => {
494
- console.log(\`[\${new Date().toISOString()}] HTTP Request:\`, {
495
- method: data.method,
496
- path: data.path,
497
- body: data.body
498
- });
499
- })
500
- .addHandler('tool:http-server:response', (data) => {
501
- console.log(\`[\${new Date().toISOString()}] HTTP Response:\`, {
502
- statusCode: data.statusCode,
503
- processingTime: data.processingTime
504
- });
505
- })
506
- // System event handlers
507
- .addHandler('output', (data) => {
508
- console.log(\`[\${new Date().toISOString()}] Agent output:\`, data);
509
- })
510
- .addHandler('error', (error) => {
511
- console.error(\`[\${new Date().toISOString()}] Agent error:\`, error);
512
- });
513
-
514
- module.exports = exampleAgent;
515
279
  `;
516
280
  }
517
281
  }
518
282
 
519
- module.exports = { DankProject };
283
+ module.exports = { DankProject };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dank-ai",
3
- "version": "1.0.6",
3
+ "version": "1.0.9",
4
4
  "description": "Dank Agent Service - Docker-based AI agent orchestration platform",
5
5
  "main": "lib/index.js",
6
6
  "exports": {