@inkeep/create-agents 0.2.1 → 0.2.2
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/dist/__tests__/utils.test.js +29 -24
- package/dist/utils.js +16 -42
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { cloneTemplate, getAvailableTemplates } from '../templates';
|
|
3
5
|
import { createAgents } from '../utils';
|
|
4
|
-
import { getAvailableTemplates, cloneTemplate } from '../templates';
|
|
5
|
-
import * as p from '@clack/prompts';
|
|
6
6
|
// Mock all dependencies
|
|
7
7
|
vi.mock('fs-extra');
|
|
8
8
|
vi.mock('../templates');
|
|
@@ -48,7 +48,11 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
48
48
|
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
|
49
49
|
vi.mocked(fs.remove).mockResolvedValue(undefined);
|
|
50
50
|
// Mock templates
|
|
51
|
-
vi.mocked(getAvailableTemplates).mockResolvedValue([
|
|
51
|
+
vi.mocked(getAvailableTemplates).mockResolvedValue([
|
|
52
|
+
'weather-graph',
|
|
53
|
+
'chatbot',
|
|
54
|
+
'data-analysis',
|
|
55
|
+
]);
|
|
52
56
|
vi.mocked(cloneTemplate).mockResolvedValue(undefined);
|
|
53
57
|
// Mock util.promisify to return a mock exec function
|
|
54
58
|
const mockExecAsync = vi.fn().mockResolvedValue({ stdout: '', stderr: '' });
|
|
@@ -72,7 +76,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
72
76
|
await createAgents({
|
|
73
77
|
dirName: 'test-dir',
|
|
74
78
|
openAiKey: 'test-openai-key',
|
|
75
|
-
anthropicKey: 'test-anthropic-key'
|
|
79
|
+
anthropicKey: 'test-anthropic-key',
|
|
76
80
|
});
|
|
77
81
|
// Should clone base template and weather-graph template
|
|
78
82
|
expect(cloneTemplate).toHaveBeenCalledTimes(2);
|
|
@@ -85,10 +89,8 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
85
89
|
await createAgents({
|
|
86
90
|
dirName: 'test-dir',
|
|
87
91
|
openAiKey: 'test-openai-key',
|
|
88
|
-
anthropicKey: 'test-anthropic-key'
|
|
92
|
+
anthropicKey: 'test-anthropic-key',
|
|
89
93
|
});
|
|
90
|
-
// Check that .env file is created with correct project ID path
|
|
91
|
-
expect(fs.writeFile).toHaveBeenCalledWith('src/weather-graph/.env', expect.any(String));
|
|
92
94
|
// Check that inkeep.config.ts is created with correct project ID
|
|
93
95
|
expect(fs.writeFile).toHaveBeenCalledWith('src/weather-graph/inkeep.config.ts', expect.stringContaining('projectId: "weather-graph"'));
|
|
94
96
|
});
|
|
@@ -99,7 +101,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
99
101
|
dirName: 'test-dir',
|
|
100
102
|
template: 'chatbot',
|
|
101
103
|
openAiKey: 'test-openai-key',
|
|
102
|
-
anthropicKey: 'test-anthropic-key'
|
|
104
|
+
anthropicKey: 'test-anthropic-key',
|
|
103
105
|
});
|
|
104
106
|
// Should validate template exists
|
|
105
107
|
expect(getAvailableTemplates).toHaveBeenCalled();
|
|
@@ -107,8 +109,6 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
107
109
|
expect(cloneTemplate).toHaveBeenCalledTimes(2);
|
|
108
110
|
expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/create-agents-template', expect.any(String));
|
|
109
111
|
expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/agents-cookbook/templates/chatbot', 'src/chatbot');
|
|
110
|
-
// Should create config files with template name as project ID
|
|
111
|
-
expect(fs.writeFile).toHaveBeenCalledWith('src/chatbot/.env', expect.any(String));
|
|
112
112
|
expect(fs.writeFile).toHaveBeenCalledWith('src/chatbot/inkeep.config.ts', expect.stringContaining('projectId: "chatbot"'));
|
|
113
113
|
});
|
|
114
114
|
it('should exit with error when template does not exist', async () => {
|
|
@@ -116,17 +116,21 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
116
116
|
await expect(createAgents({
|
|
117
117
|
dirName: 'test-dir',
|
|
118
118
|
template: 'non-existent-template',
|
|
119
|
-
openAiKey: 'test-openai-key'
|
|
119
|
+
openAiKey: 'test-openai-key',
|
|
120
120
|
})).rejects.toThrow('process.exit called');
|
|
121
121
|
expect(p.cancel).toHaveBeenCalledWith(expect.stringContaining('Template "non-existent-template" not found'));
|
|
122
122
|
expect(processExitSpy).toHaveBeenCalledWith(0);
|
|
123
123
|
});
|
|
124
124
|
it('should show available templates when invalid template is provided', async () => {
|
|
125
|
-
vi.mocked(getAvailableTemplates).mockResolvedValue([
|
|
125
|
+
vi.mocked(getAvailableTemplates).mockResolvedValue([
|
|
126
|
+
'weather-graph',
|
|
127
|
+
'chatbot',
|
|
128
|
+
'data-analysis',
|
|
129
|
+
]);
|
|
126
130
|
await expect(createAgents({
|
|
127
131
|
dirName: 'test-dir',
|
|
128
132
|
template: 'invalid',
|
|
129
|
-
openAiKey: 'test-openai-key'
|
|
133
|
+
openAiKey: 'test-openai-key',
|
|
130
134
|
})).rejects.toThrow('process.exit called');
|
|
131
135
|
const cancelCall = vi.mocked(p.cancel).mock.calls[0][0];
|
|
132
136
|
expect(cancelCall).toContain('weather-graph');
|
|
@@ -140,7 +144,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
140
144
|
dirName: 'test-dir',
|
|
141
145
|
customProjectId: 'my-custom-project',
|
|
142
146
|
openAiKey: 'test-openai-key',
|
|
143
|
-
anthropicKey: 'test-anthropic-key'
|
|
147
|
+
anthropicKey: 'test-anthropic-key',
|
|
144
148
|
});
|
|
145
149
|
// Should clone base template but NOT project template
|
|
146
150
|
expect(cloneTemplate).toHaveBeenCalledTimes(1);
|
|
@@ -149,8 +153,6 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
149
153
|
expect(getAvailableTemplates).not.toHaveBeenCalled();
|
|
150
154
|
// Should create empty project directory
|
|
151
155
|
expect(fs.ensureDir).toHaveBeenCalledWith('src/my-custom-project');
|
|
152
|
-
// Should create config files with custom project ID
|
|
153
|
-
expect(fs.writeFile).toHaveBeenCalledWith('src/my-custom-project/.env', expect.any(String));
|
|
154
156
|
expect(fs.writeFile).toHaveBeenCalledWith('src/my-custom-project/inkeep.config.ts', expect.stringContaining('projectId: "my-custom-project"'));
|
|
155
157
|
});
|
|
156
158
|
it('should prioritize custom project ID over template if both are provided', async () => {
|
|
@@ -159,7 +161,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
159
161
|
template: 'chatbot',
|
|
160
162
|
customProjectId: 'my-custom-project',
|
|
161
163
|
openAiKey: 'test-openai-key',
|
|
162
|
-
anthropicKey: 'test-anthropic-key'
|
|
164
|
+
anthropicKey: 'test-anthropic-key',
|
|
163
165
|
});
|
|
164
166
|
// Should only clone base template, not project template
|
|
165
167
|
expect(cloneTemplate).toHaveBeenCalledTimes(1);
|
|
@@ -172,12 +174,15 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
172
174
|
});
|
|
173
175
|
describe('Edge cases and validation', () => {
|
|
174
176
|
it('should handle template names with hyphens correctly', async () => {
|
|
175
|
-
vi.mocked(getAvailableTemplates).mockResolvedValue([
|
|
177
|
+
vi.mocked(getAvailableTemplates).mockResolvedValue([
|
|
178
|
+
'my-complex-template',
|
|
179
|
+
'another-template',
|
|
180
|
+
]);
|
|
176
181
|
await createAgents({
|
|
177
182
|
dirName: 'test-dir',
|
|
178
183
|
template: 'my-complex-template',
|
|
179
184
|
openAiKey: 'test-key',
|
|
180
|
-
anthropicKey: 'test-key'
|
|
185
|
+
anthropicKey: 'test-key',
|
|
181
186
|
});
|
|
182
187
|
expect(cloneTemplate).toHaveBeenCalledTimes(2);
|
|
183
188
|
expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/agents-cookbook/templates/my-complex-template', 'src/my-complex-template');
|
|
@@ -187,7 +192,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
187
192
|
dirName: 'test-dir',
|
|
188
193
|
customProjectId: 'my_project-123',
|
|
189
194
|
openAiKey: 'test-key',
|
|
190
|
-
anthropicKey: 'test-key'
|
|
195
|
+
anthropicKey: 'test-key',
|
|
191
196
|
});
|
|
192
197
|
expect(fs.ensureDir).toHaveBeenCalledWith('src/my_project-123');
|
|
193
198
|
expect(fs.writeFile).toHaveBeenCalledWith('src/my_project-123/inkeep.config.ts', expect.stringContaining('projectId: "my_project-123"'));
|
|
@@ -197,7 +202,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
197
202
|
await createAgents({
|
|
198
203
|
dirName: 'dir1',
|
|
199
204
|
openAiKey: 'key',
|
|
200
|
-
anthropicKey: 'key'
|
|
205
|
+
anthropicKey: 'key',
|
|
201
206
|
});
|
|
202
207
|
expect(fs.ensureDir).toHaveBeenCalledWith('src');
|
|
203
208
|
// Reset mocks
|
|
@@ -208,7 +213,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
208
213
|
dirName: 'dir2',
|
|
209
214
|
template: 'chatbot',
|
|
210
215
|
openAiKey: 'key',
|
|
211
|
-
anthropicKey: 'key'
|
|
216
|
+
anthropicKey: 'key',
|
|
212
217
|
});
|
|
213
218
|
expect(fs.ensureDir).toHaveBeenCalledWith('src');
|
|
214
219
|
// Reset mocks
|
|
@@ -219,7 +224,7 @@ describe('createAgents - Template and Project ID Logic', () => {
|
|
|
219
224
|
dirName: 'dir3',
|
|
220
225
|
customProjectId: 'custom',
|
|
221
226
|
openAiKey: 'key',
|
|
222
|
-
anthropicKey: 'key'
|
|
227
|
+
anthropicKey: 'key',
|
|
223
228
|
});
|
|
224
229
|
expect(fs.ensureDir).toHaveBeenCalledWith('src');
|
|
225
230
|
expect(fs.ensureDir).toHaveBeenCalledWith('src/custom');
|
package/dist/utils.js
CHANGED
|
@@ -95,9 +95,9 @@ export const createAgents = async (args = {}) => {
|
|
|
95
95
|
const providerChoice = await p.select({
|
|
96
96
|
message: 'Which AI provider(s) would you like to use?',
|
|
97
97
|
options: [
|
|
98
|
-
{ value: 'anthropic', label: 'Anthropic
|
|
99
|
-
{ value: 'openai', label: 'OpenAI
|
|
100
|
-
{ value: 'google', label: 'Google
|
|
98
|
+
{ value: 'anthropic', label: 'Anthropic' },
|
|
99
|
+
{ value: 'openai', label: 'OpenAI' },
|
|
100
|
+
{ value: 'google', label: 'Google' },
|
|
101
101
|
],
|
|
102
102
|
});
|
|
103
103
|
if (p.isCancel(providerChoice)) {
|
|
@@ -199,6 +199,7 @@ export const createAgents = async (args = {}) => {
|
|
|
199
199
|
projectId,
|
|
200
200
|
openAiKey,
|
|
201
201
|
anthropicKey,
|
|
202
|
+
googleKey,
|
|
202
203
|
manageApiPort: manageApiPort || '3002',
|
|
203
204
|
runApiPort: runApiPort || '3003',
|
|
204
205
|
modelSettings: defaultModelSettings,
|
|
@@ -223,9 +224,6 @@ export const createAgents = async (args = {}) => {
|
|
|
223
224
|
// create or overwrite inkeep.config.ts
|
|
224
225
|
s.message('Creating inkeep.config.ts...');
|
|
225
226
|
await createInkeepConfig(config);
|
|
226
|
-
// Create service files
|
|
227
|
-
s.message('Creating service files...');
|
|
228
|
-
await createServiceFiles(config);
|
|
229
227
|
// Install dependencies
|
|
230
228
|
s.message('Installing dependencies (this may take a while)...');
|
|
231
229
|
await installDependencies();
|
|
@@ -272,51 +270,27 @@ async function createEnvironmentFiles(config) {
|
|
|
272
270
|
ENVIRONMENT=development
|
|
273
271
|
|
|
274
272
|
# Database
|
|
275
|
-
DB_FILE_NAME=file
|
|
273
|
+
DB_FILE_NAME=file:${process.cwd()}/local.db
|
|
276
274
|
|
|
277
275
|
# AI Provider Keys
|
|
278
276
|
ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
|
|
279
277
|
OPENAI_API_KEY=${config.openAiKey || 'your-openai-key-here'}
|
|
280
278
|
GOOGLE_GENERATIVE_AI_API_KEY=${config.googleKey || 'your-google-key-here'}
|
|
281
|
-
`;
|
|
282
|
-
await fs.writeFile('.env', envContent);
|
|
283
|
-
// Create .env.example
|
|
284
|
-
const envExample = envContent.replace(/=.+$/gm, '=');
|
|
285
|
-
await fs.writeFile('.env.example', envExample);
|
|
286
|
-
// Create .env files for each API service
|
|
287
|
-
const runApiEnvContent = `# Environment
|
|
288
|
-
ENVIRONMENT=development
|
|
289
|
-
|
|
290
|
-
# Database (relative path from API directory)
|
|
291
|
-
DB_FILE_NAME=file:../../local.db
|
|
292
|
-
|
|
293
|
-
# AI Provider Keys
|
|
294
|
-
ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
|
|
295
|
-
OPENAI_API_KEY=${config.openAiKey || 'your-openai-key-here'}
|
|
296
|
-
GOOGLE_GENERATIVE_AI_API_KEY=${config.googleKey || 'your-google-key-here'}
|
|
297
|
-
|
|
298
|
-
AGENTS_RUN_API_URL=http://localhost:${config.runApiPort}
|
|
299
|
-
`;
|
|
300
|
-
const manageApiEnvContent = `# Environment
|
|
301
|
-
ENVIRONMENT=development
|
|
302
279
|
|
|
303
|
-
#
|
|
304
|
-
|
|
280
|
+
# Inkeep API URLs
|
|
281
|
+
INKEEP_AGENTS_MANAGE_API_URL="http://localhost:3002"
|
|
282
|
+
INKEEP_AGENTS_RUN_API_URL="http://localhost:3003"
|
|
305
283
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
await fs.writeFile('apps/run-api/.env', runApiEnvContent);
|
|
310
|
-
}
|
|
311
|
-
async function createServiceFiles(config) {
|
|
312
|
-
// Create .env file for the project directory (for inkeep CLI commands)
|
|
313
|
-
const projectEnvContent = `# Environment
|
|
314
|
-
ENVIRONMENT=development
|
|
284
|
+
# SigNoz
|
|
285
|
+
SIGNOZ_URL=your-signoz-url-here
|
|
286
|
+
SIGNOZ_API_KEY=your-signoz-api-key-here
|
|
315
287
|
|
|
316
|
-
#
|
|
317
|
-
|
|
288
|
+
# Nango
|
|
289
|
+
NANGO_HOST=your-nango-host-here
|
|
290
|
+
NANGO_CONNECT_BASE_URL=your-nango-connect-base-url-here
|
|
291
|
+
NANGO_SECRET_KEY=
|
|
318
292
|
`;
|
|
319
|
-
await fs.writeFile(
|
|
293
|
+
await fs.writeFile('.env', envContent);
|
|
320
294
|
}
|
|
321
295
|
async function createInkeepConfig(config) {
|
|
322
296
|
const inkeepConfig = `import { defineConfig } from '@inkeep/agents-cli/config';
|