dank-ai 1.0.46 → 1.0.49
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 +72 -9
- package/docker/entrypoint.js +8 -8
- package/lib/cli/init.js +62 -0
- package/lib/docker/manager.js +149 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,7 @@ dank logs assistant --follow
|
|
|
70
70
|
```
|
|
71
71
|
my-project/
|
|
72
72
|
├── dank.config.js # Agent configuration
|
|
73
|
+
├── .dankignore # Build ignore patterns (optional)
|
|
73
74
|
├── agents/ # Custom agent code (optional)
|
|
74
75
|
│ └── example-agent.js
|
|
75
76
|
└── .dank/ # Generated files
|
|
@@ -243,7 +244,7 @@ agent
|
|
|
243
244
|
|
|
244
245
|
#### Passing Custom Data to Handlers
|
|
245
246
|
|
|
246
|
-
You can pass any custom data in the request body to the `/prompt` endpoint, and it will be available in your handlers via `data.
|
|
247
|
+
You can pass any custom data in the request body to the `/prompt` endpoint, and it will be available in your handlers via `data.params`. This enables powerful use cases like user authentication, conversation tracking, RAG (Retrieval-Augmented Generation), and custom lookups.
|
|
247
248
|
|
|
248
249
|
**Client Request:**
|
|
249
250
|
```javascript
|
|
@@ -264,9 +265,9 @@ You can pass any custom data in the request body to the `/prompt` endpoint, and
|
|
|
264
265
|
```javascript
|
|
265
266
|
agent
|
|
266
267
|
.addHandler('request_output:start', async (data) => {
|
|
267
|
-
// Access custom data via data.
|
|
268
|
-
const userId = data.
|
|
269
|
-
const conversationId = data.
|
|
268
|
+
// Access custom data via data.params
|
|
269
|
+
const userId = data.params.userId;
|
|
270
|
+
const conversationId = data.params.conversationId;
|
|
270
271
|
|
|
271
272
|
// Perform authentication
|
|
272
273
|
const user = await authenticateUser(userId);
|
|
@@ -287,16 +288,16 @@ agent
|
|
|
287
288
|
.addHandler('request_output', async (data) => {
|
|
288
289
|
// Log with user context
|
|
289
290
|
await logInteraction({
|
|
290
|
-
userId: data.
|
|
291
|
-
conversationId: data.
|
|
291
|
+
userId: data.params.userId,
|
|
292
|
+
conversationId: data.params.conversationId,
|
|
292
293
|
prompt: data.prompt,
|
|
293
294
|
response: data.response,
|
|
294
295
|
timestamp: data.timestamp
|
|
295
296
|
});
|
|
296
297
|
|
|
297
298
|
// Update user preferences based on interaction
|
|
298
|
-
if (data.
|
|
299
|
-
await updateUserPreferences(data.
|
|
299
|
+
if (data.params.userPreferences) {
|
|
300
|
+
await updateUserPreferences(data.params.userId, data.params.userPreferences);
|
|
300
301
|
}
|
|
301
302
|
});
|
|
302
303
|
```
|
|
@@ -495,6 +496,68 @@ Dank uses a layered Docker approach:
|
|
|
495
496
|
2. **Agent Images**: Extend base image with agent-specific code
|
|
496
497
|
3. **Containers**: Running instances with resource limits and networking
|
|
497
498
|
|
|
499
|
+
### Build File Management (.dankignore)
|
|
500
|
+
|
|
501
|
+
Dank automatically copies files from your project directory into Docker containers during the build process. Use `.dankignore` to control which files are included.
|
|
502
|
+
|
|
503
|
+
**Default Behavior:**
|
|
504
|
+
- If `.dankignore` doesn't exist: **All files** are copied to the container
|
|
505
|
+
- If `.dankignore` exists: Only files **not matching** the patterns are copied
|
|
506
|
+
|
|
507
|
+
**Creating `.dankignore`:**
|
|
508
|
+
```bash
|
|
509
|
+
# Created automatically during dank init
|
|
510
|
+
dank init my-project
|
|
511
|
+
|
|
512
|
+
# Or create manually
|
|
513
|
+
touch .dankignore
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Example `.dankignore`:**
|
|
517
|
+
```
|
|
518
|
+
# Security - Environment variables (IMPORTANT: Never commit .env files!)
|
|
519
|
+
.env
|
|
520
|
+
.env.*
|
|
521
|
+
*.key
|
|
522
|
+
*.pem
|
|
523
|
+
secrets/
|
|
524
|
+
|
|
525
|
+
# Dependencies (installed fresh in container)
|
|
526
|
+
node_modules/
|
|
527
|
+
|
|
528
|
+
# Version control
|
|
529
|
+
.git/
|
|
530
|
+
|
|
531
|
+
# Build artifacts
|
|
532
|
+
dist/
|
|
533
|
+
build/
|
|
534
|
+
coverage/
|
|
535
|
+
|
|
536
|
+
# OS files
|
|
537
|
+
.DS_Store
|
|
538
|
+
Thumbs.db
|
|
539
|
+
|
|
540
|
+
# IDE files
|
|
541
|
+
.vscode/
|
|
542
|
+
.idea/
|
|
543
|
+
|
|
544
|
+
# Logs
|
|
545
|
+
*.log
|
|
546
|
+
logs/
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Pattern Matching:**
|
|
550
|
+
- `node_modules` - Exact match
|
|
551
|
+
- `*.log` - Wildcard (matches any `.log` file)
|
|
552
|
+
- `dist/` - Directory pattern (matches `dist` directory and contents)
|
|
553
|
+
- `.env.*` - Pattern matching (matches `.env.local`, `.env.production`, etc.)
|
|
554
|
+
|
|
555
|
+
**Best Practices:**
|
|
556
|
+
- ✅ Always exclude `.env` files (security)
|
|
557
|
+
- ✅ Exclude `node_modules/` (dependencies installed in container)
|
|
558
|
+
- ✅ Exclude build artifacts (`dist/`, `build/`)
|
|
559
|
+
- ✅ Include source files, assets, and configuration needed at runtime
|
|
560
|
+
|
|
498
561
|
### Container Features
|
|
499
562
|
- **Isolated Environments**: Each agent runs in its own container
|
|
500
563
|
- **Resource Limits**: Memory and CPU constraints per agent
|
|
@@ -780,7 +843,7 @@ docker logs container-id
|
|
|
780
843
|
- **Authentication**: `docker login ghcr.io`
|
|
781
844
|
- **Push Permissions**: Check namespace permissions
|
|
782
845
|
- **Image Exists**: Use different tag or `--force`
|
|
783
|
-
- **Build Context**: Add `.
|
|
846
|
+
- **Build Context**: Add `.dankignore` file to control which files are copied
|
|
784
847
|
</details>
|
|
785
848
|
|
|
786
849
|
## 📦 Package Exports
|
package/docker/entrypoint.js
CHANGED
|
@@ -1218,12 +1218,12 @@ class AgentRuntime {
|
|
|
1218
1218
|
});
|
|
1219
1219
|
}
|
|
1220
1220
|
|
|
1221
|
-
// Build
|
|
1222
|
-
const
|
|
1221
|
+
// Build params object with all user-provided fields from request body (except prompt)
|
|
1222
|
+
const params = {
|
|
1223
1223
|
...requestBodyFields,
|
|
1224
1224
|
};
|
|
1225
1225
|
|
|
1226
|
-
const response = await this.processDirectPrompt(prompt,
|
|
1226
|
+
const response = await this.processDirectPrompt(prompt, params, {
|
|
1227
1227
|
protocol: "http",
|
|
1228
1228
|
clientIp: req.ip,
|
|
1229
1229
|
});
|
|
@@ -1251,7 +1251,7 @@ class AgentRuntime {
|
|
|
1251
1251
|
/**
|
|
1252
1252
|
* Process a direct prompt and emit events
|
|
1253
1253
|
*/
|
|
1254
|
-
async processDirectPrompt(prompt,
|
|
1254
|
+
async processDirectPrompt(prompt, params = {}, systemFields = {}) {
|
|
1255
1255
|
const startTime = Date.now();
|
|
1256
1256
|
let finalPrompt = prompt; // Declare outside try block so it's available in catch
|
|
1257
1257
|
|
|
@@ -1259,7 +1259,7 @@ class AgentRuntime {
|
|
|
1259
1259
|
// Emit request start event and allow handlers to modify the prompt
|
|
1260
1260
|
const startEventData = {
|
|
1261
1261
|
prompt,
|
|
1262
|
-
|
|
1262
|
+
params,
|
|
1263
1263
|
...systemFields, // protocol, clientIp, etc.
|
|
1264
1264
|
timestamp: new Date().toISOString(),
|
|
1265
1265
|
};
|
|
@@ -1302,7 +1302,7 @@ class AgentRuntime {
|
|
|
1302
1302
|
prompt,
|
|
1303
1303
|
finalPrompt, // Include the final prompt that was sent to LLM
|
|
1304
1304
|
response: response.content,
|
|
1305
|
-
|
|
1305
|
+
params,
|
|
1306
1306
|
...systemFields, // protocol, clientIp, etc.
|
|
1307
1307
|
usage: response.usage,
|
|
1308
1308
|
model: response.model,
|
|
@@ -1316,7 +1316,7 @@ class AgentRuntime {
|
|
|
1316
1316
|
prompt,
|
|
1317
1317
|
finalPrompt,
|
|
1318
1318
|
response: response.content,
|
|
1319
|
-
|
|
1319
|
+
params,
|
|
1320
1320
|
...systemFields, // protocol, clientIp, etc.
|
|
1321
1321
|
usage: response.usage,
|
|
1322
1322
|
model: response.model,
|
|
@@ -1347,7 +1347,7 @@ class AgentRuntime {
|
|
|
1347
1347
|
await this.emitEvent("request_output:error", {
|
|
1348
1348
|
prompt,
|
|
1349
1349
|
finalPrompt,
|
|
1350
|
-
|
|
1350
|
+
params,
|
|
1351
1351
|
...systemFields, // protocol, clientIp, etc.
|
|
1352
1352
|
error: error.message,
|
|
1353
1353
|
processingTime,
|
package/lib/cli/init.js
CHANGED
|
@@ -84,6 +84,9 @@ async function initCommand(projectName, options) {
|
|
|
84
84
|
// Create .gitignore
|
|
85
85
|
await createGitignore(project.projectPath);
|
|
86
86
|
|
|
87
|
+
// Create .dankignore
|
|
88
|
+
await createDankIgnore(project.projectPath);
|
|
89
|
+
|
|
87
90
|
// Create README.md
|
|
88
91
|
await createReadme(name, project.projectPath);
|
|
89
92
|
|
|
@@ -260,6 +263,65 @@ jspm_packages/
|
|
|
260
263
|
console.log(chalk.green(`Created .gitignore: ${gitignorePath}`));
|
|
261
264
|
}
|
|
262
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Create .dankignore file
|
|
268
|
+
*/
|
|
269
|
+
async function createDankIgnore(projectPath) {
|
|
270
|
+
const dankIgnore = `# Dank Build Ignore Patterns
|
|
271
|
+
# Files and directories to exclude from Docker builds
|
|
272
|
+
# This file is optional - if it doesn't exist, all files will be copied
|
|
273
|
+
|
|
274
|
+
# Security - Environment variables (IMPORTANT: Never commit .env files!)
|
|
275
|
+
.env
|
|
276
|
+
.env.*
|
|
277
|
+
*.key
|
|
278
|
+
*.pem
|
|
279
|
+
secrets/
|
|
280
|
+
|
|
281
|
+
# Dependencies (installed fresh in container)
|
|
282
|
+
node_modules/
|
|
283
|
+
|
|
284
|
+
# Version control
|
|
285
|
+
.git/
|
|
286
|
+
|
|
287
|
+
# Build artifacts
|
|
288
|
+
dist/
|
|
289
|
+
build/
|
|
290
|
+
coverage/
|
|
291
|
+
.nyc_output/
|
|
292
|
+
|
|
293
|
+
# OS files
|
|
294
|
+
.DS_Store
|
|
295
|
+
Thumbs.db
|
|
296
|
+
*.swp
|
|
297
|
+
*.swo
|
|
298
|
+
*~
|
|
299
|
+
|
|
300
|
+
# IDE files
|
|
301
|
+
.vscode/
|
|
302
|
+
.idea/
|
|
303
|
+
*.sublime-*
|
|
304
|
+
*.code-workspace
|
|
305
|
+
|
|
306
|
+
# Logs
|
|
307
|
+
*.log
|
|
308
|
+
logs/
|
|
309
|
+
|
|
310
|
+
# Dank framework
|
|
311
|
+
.dank/
|
|
312
|
+
.build-context-*
|
|
313
|
+
|
|
314
|
+
# Temporary files
|
|
315
|
+
*.tmp
|
|
316
|
+
*.temp
|
|
317
|
+
.cache/
|
|
318
|
+
`;
|
|
319
|
+
|
|
320
|
+
const dankIgnorePath = path.join(projectPath, '.dankignore');
|
|
321
|
+
await fs.writeFile(dankIgnorePath, dankIgnore, 'utf8');
|
|
322
|
+
console.log(chalk.green(`Created .dankignore: ${dankIgnorePath}`));
|
|
323
|
+
}
|
|
324
|
+
|
|
263
325
|
/**
|
|
264
326
|
* Create README.md for the project
|
|
265
327
|
*/
|
package/lib/docker/manager.js
CHANGED
|
@@ -1759,6 +1759,141 @@ class DockerManager {
|
|
|
1759
1759
|
* @param {object} options - Additional options
|
|
1760
1760
|
* @param {string} options.projectRoot - Root directory for package.json (defaults to process.cwd())
|
|
1761
1761
|
*/
|
|
1762
|
+
/**
|
|
1763
|
+
* Read .dankignore file if it exists
|
|
1764
|
+
* @param {string} projectDir - Project directory
|
|
1765
|
+
* @returns {string[]} Array of ignore patterns
|
|
1766
|
+
*/
|
|
1767
|
+
async readDankIgnore(projectDir) {
|
|
1768
|
+
const dankIgnorePath = path.join(projectDir, '.dankignore');
|
|
1769
|
+
|
|
1770
|
+
if (!(await fs.pathExists(dankIgnorePath))) {
|
|
1771
|
+
// No .dankignore = copy everything
|
|
1772
|
+
return [];
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
try {
|
|
1776
|
+
const content = await fs.readFile(dankIgnorePath, 'utf8');
|
|
1777
|
+
return content
|
|
1778
|
+
.split('\n')
|
|
1779
|
+
.map(line => line.trim())
|
|
1780
|
+
.filter(line => {
|
|
1781
|
+
// Remove comments and empty lines
|
|
1782
|
+
return line && !line.startsWith('#');
|
|
1783
|
+
});
|
|
1784
|
+
} catch (error) {
|
|
1785
|
+
this.logger.warn(`⚠️ Failed to read .dankignore: ${error.message}`);
|
|
1786
|
+
return [];
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/**
|
|
1791
|
+
* Check if a file path should be ignored based on .dankignore patterns
|
|
1792
|
+
* @param {string} filePath - Full path to the file
|
|
1793
|
+
* @param {string} projectDir - Project root directory
|
|
1794
|
+
* @param {string[]} ignorePatterns - Patterns from .dankignore
|
|
1795
|
+
* @returns {boolean} True if file should be ignored
|
|
1796
|
+
*/
|
|
1797
|
+
shouldIgnoreFile(filePath, projectDir, ignorePatterns) {
|
|
1798
|
+
if (ignorePatterns.length === 0) {
|
|
1799
|
+
return false; // No patterns = don't ignore anything
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// Get relative path from project directory
|
|
1803
|
+
const relativePath = path.relative(projectDir, filePath);
|
|
1804
|
+
const normalizedPath = relativePath.replace(/\\/g, '/'); // Normalize to forward slashes
|
|
1805
|
+
|
|
1806
|
+
// Also check just the filename for patterns like "*.log"
|
|
1807
|
+
const fileName = path.basename(filePath);
|
|
1808
|
+
|
|
1809
|
+
for (const pattern of ignorePatterns) {
|
|
1810
|
+
// Skip package.json and package-lock.json as they're handled separately
|
|
1811
|
+
if (pattern === 'package.json' || pattern === 'package-lock.json') {
|
|
1812
|
+
continue;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// Convert pattern to regex
|
|
1816
|
+
// Support:
|
|
1817
|
+
// - Exact matches: "node_modules"
|
|
1818
|
+
// - Wildcards: "*.log", ".env.*"
|
|
1819
|
+
// - Directory patterns: "dist/", "node_modules/"
|
|
1820
|
+
// - Path patterns: "secrets/*.key"
|
|
1821
|
+
|
|
1822
|
+
// Use a placeholder for * to avoid escaping issues
|
|
1823
|
+
const placeholder = '__WILDCARD_PLACEHOLDER__';
|
|
1824
|
+
let regexPattern = pattern.replace(/\*/g, placeholder);
|
|
1825
|
+
|
|
1826
|
+
// Escape special regex characters (except our placeholder)
|
|
1827
|
+
regexPattern = regexPattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
1828
|
+
|
|
1829
|
+
// Replace placeholder with .* for wildcard matching
|
|
1830
|
+
regexPattern = regexPattern.replace(new RegExp(placeholder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '.*');
|
|
1831
|
+
|
|
1832
|
+
// If pattern ends with /, it's a directory pattern
|
|
1833
|
+
const isDirectoryPattern = pattern.endsWith('/');
|
|
1834
|
+
|
|
1835
|
+
// For directory patterns, match anywhere in path
|
|
1836
|
+
// For file patterns, match full path or filename
|
|
1837
|
+
if (isDirectoryPattern) {
|
|
1838
|
+
// Remove trailing / from pattern for matching
|
|
1839
|
+
const dirPattern = regexPattern.replace(/\/$/, '');
|
|
1840
|
+
// Match if path contains this directory (at any level)
|
|
1841
|
+
const dirRegex = new RegExp(`(^|/)${dirPattern}(/|$)`);
|
|
1842
|
+
if (dirRegex.test(normalizedPath)) {
|
|
1843
|
+
return true;
|
|
1844
|
+
}
|
|
1845
|
+
} else {
|
|
1846
|
+
// File pattern - check full path and filename
|
|
1847
|
+
const fileRegex = new RegExp(`^${regexPattern}$`);
|
|
1848
|
+
if (fileRegex.test(normalizedPath) || fileRegex.test(fileName)) {
|
|
1849
|
+
return true;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
// Also check if pattern matches any path segment
|
|
1853
|
+
const pathSegments = normalizedPath.split('/');
|
|
1854
|
+
for (const segment of pathSegments) {
|
|
1855
|
+
if (fileRegex.test(segment)) {
|
|
1856
|
+
return true;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
/**
|
|
1866
|
+
* Recursively copy files respecting .dankignore patterns
|
|
1867
|
+
* @param {string} sourceDir - Source directory
|
|
1868
|
+
* @param {string} destDir - Destination directory
|
|
1869
|
+
* @param {string} projectDir - Project root (for relative path calculation)
|
|
1870
|
+
* @param {string[]} ignorePatterns - Patterns from .dankignore
|
|
1871
|
+
*/
|
|
1872
|
+
async copyFilesRecursive(sourceDir, destDir, projectDir, ignorePatterns) {
|
|
1873
|
+
const items = await fs.readdir(sourceDir);
|
|
1874
|
+
|
|
1875
|
+
for (const item of items) {
|
|
1876
|
+
const sourcePath = path.join(sourceDir, item);
|
|
1877
|
+
const destPath = path.join(destDir, item);
|
|
1878
|
+
|
|
1879
|
+
// Check if this file/directory should be ignored
|
|
1880
|
+
if (this.shouldIgnoreFile(sourcePath, projectDir, ignorePatterns)) {
|
|
1881
|
+
continue;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
const stat = await fs.stat(sourcePath);
|
|
1885
|
+
|
|
1886
|
+
if (stat.isDirectory()) {
|
|
1887
|
+
// Recursively copy directory
|
|
1888
|
+
await fs.ensureDir(destPath);
|
|
1889
|
+
await this.copyFilesRecursive(sourcePath, destPath, projectDir, ignorePatterns);
|
|
1890
|
+
} else if (stat.isFile()) {
|
|
1891
|
+
// Copy file
|
|
1892
|
+
await fs.copy(sourcePath, destPath);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1762
1897
|
async copyProjectFiles(projectDir, contextDir, options = {}) {
|
|
1763
1898
|
const agentCodeDir = path.join(contextDir, "agent-code");
|
|
1764
1899
|
await fs.ensureDir(agentCodeDir);
|
|
@@ -1783,53 +1918,22 @@ class DockerManager {
|
|
|
1783
1918
|
this.logger.warn(`⚠️ Failed to copy package files: ${error.message}`);
|
|
1784
1919
|
}
|
|
1785
1920
|
|
|
1786
|
-
//
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
'coverage',
|
|
1799
|
-
'.nyc_output',
|
|
1800
|
-
'package.json', // Already copied from project root
|
|
1801
|
-
'package-lock.json' // Already copied from project root
|
|
1802
|
-
];
|
|
1921
|
+
// Read .dankignore patterns from project root (optional - if file doesn't exist, copy everything)
|
|
1922
|
+
// Check both projectRoot and projectDir for .dankignore
|
|
1923
|
+
let ignorePatterns = await this.readDankIgnore(projectRoot);
|
|
1924
|
+
if (ignorePatterns.length === 0 && projectDir !== projectRoot) {
|
|
1925
|
+
ignorePatterns = await this.readDankIgnore(projectDir);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
if (ignorePatterns.length > 0) {
|
|
1929
|
+
this.logger.info(`📋 Using .dankignore with ${ignorePatterns.length} pattern(s)`);
|
|
1930
|
+
} else {
|
|
1931
|
+
this.logger.info(`📋 No .dankignore found - copying all files from ${projectDir}`);
|
|
1932
|
+
}
|
|
1803
1933
|
|
|
1804
1934
|
try {
|
|
1805
|
-
// Copy
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
for (const item of items) {
|
|
1809
|
-
const sourcePath = path.join(projectDir, item);
|
|
1810
|
-
const destPath = path.join(agentCodeDir, item);
|
|
1811
|
-
const stat = await fs.stat(sourcePath);
|
|
1812
|
-
|
|
1813
|
-
// Skip if matches ignore pattern
|
|
1814
|
-
const shouldIgnore = ignorePatterns.some(pattern => {
|
|
1815
|
-
if (pattern.includes('*')) {
|
|
1816
|
-
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
1817
|
-
return regex.test(item);
|
|
1818
|
-
}
|
|
1819
|
-
return item === pattern;
|
|
1820
|
-
});
|
|
1821
|
-
|
|
1822
|
-
if (shouldIgnore) {
|
|
1823
|
-
continue;
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
// Copy file or directory
|
|
1827
|
-
if (stat.isDirectory()) {
|
|
1828
|
-
await fs.copy(sourcePath, destPath);
|
|
1829
|
-
} else if (stat.isFile()) {
|
|
1830
|
-
await fs.copy(sourcePath, destPath);
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1935
|
+
// Copy files recursively, respecting .dankignore patterns
|
|
1936
|
+
await this.copyFilesRecursive(projectDir, agentCodeDir, projectDir, ignorePatterns);
|
|
1833
1937
|
|
|
1834
1938
|
this.logger.info(`📁 Copied project files from ${projectDir} to build context`);
|
|
1835
1939
|
} catch (error) {
|