dank-ai 1.0.6 ā 1.0.7
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 +364 -2
- package/bin/dank +15 -0
- package/lib/agent.js +27 -0
- package/lib/cli/production-build.js +110 -0
- package/lib/docker/manager.js +72 -0
- package/package.json +1 -1
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
|
|
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 };
|
package/lib/docker/manager.js
CHANGED
|
@@ -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
|
*/
|