@inkeep/create-agents 0.2.1 → 0.3.0

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.
@@ -1,8 +1,8 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
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(['weather-graph', 'chatbot', 'data-analysis']);
51
+ vi.mocked(getAvailableTemplates).mockResolvedValue([
52
+ 'weather-project',
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: '' });
@@ -68,29 +72,27 @@ describe('createAgents - Template and Project ID Logic', () => {
68
72
  processChdirSpy.mockRestore();
69
73
  });
70
74
  describe('Default behavior (no template or customProjectId)', () => {
71
- it('should use weather-graph as default template and project ID', async () => {
75
+ it('should use weather-project as default template and project ID', async () => {
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
- // Should clone base template and weather-graph template
81
+ // Should clone base template and weather-project template
78
82
  expect(cloneTemplate).toHaveBeenCalledTimes(2);
79
83
  expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/create-agents-template', expect.any(String));
80
- expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/agents-cookbook/templates/weather-graph', 'src/weather-graph');
84
+ expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/agents-cookbook/template-projects/weather-project', 'src/weather-project');
81
85
  // Should not call getAvailableTemplates since no template validation needed
82
86
  expect(getAvailableTemplates).not.toHaveBeenCalled();
83
87
  });
84
- it('should create project with weather-graph as project ID', async () => {
88
+ it('should create project with weather-project as project ID', async () => {
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
- expect(fs.writeFile).toHaveBeenCalledWith('src/weather-graph/inkeep.config.ts', expect.stringContaining('projectId: "weather-graph"'));
95
+ expect(fs.writeFile).toHaveBeenCalledWith('src/weather-project/inkeep.config.ts', expect.stringContaining('projectId: "weather-project"'));
94
96
  });
95
97
  });
96
98
  describe('Template provided', () => {
@@ -99,37 +101,39 @@ 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();
106
108
  // Should clone base template and the specified template
107
109
  expect(cloneTemplate).toHaveBeenCalledTimes(2);
108
110
  expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/create-agents-template', expect.any(String));
109
- 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));
111
+ expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/agents-cookbook/template-projects/chatbot', 'src/chatbot');
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 () => {
115
- vi.mocked(getAvailableTemplates).mockResolvedValue(['weather-graph', 'chatbot']);
115
+ vi.mocked(getAvailableTemplates).mockResolvedValue(['weather-project', 'chatbot']);
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(['weather-graph', 'chatbot', 'data-analysis']);
125
+ vi.mocked(getAvailableTemplates).mockResolvedValue([
126
+ 'weather-project',
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
- expect(cancelCall).toContain('weather-graph');
136
+ expect(cancelCall).toContain('weather-project');
133
137
  expect(cancelCall).toContain('chatbot');
134
138
  expect(cancelCall).toContain('data-analysis');
135
139
  });
@@ -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,22 +174,25 @@ 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(['my-complex-template', 'another-template']);
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
- expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/agents-cookbook/templates/my-complex-template', 'src/my-complex-template');
188
+ expect(cloneTemplate).toHaveBeenCalledWith('https://github.com/inkeep/agents-cookbook/template-projects/my-complex-template', 'src/my-complex-template');
184
189
  });
185
190
  it('should handle custom project IDs with special characters', async () => {
186
191
  await createAgents({
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');
@@ -232,6 +237,10 @@ function setupDefaultMocks() {
232
237
  vi.mocked(fs.pathExists).mockResolvedValue(false);
233
238
  vi.mocked(fs.ensureDir).mockResolvedValue(undefined);
234
239
  vi.mocked(fs.writeFile).mockResolvedValue(undefined);
235
- vi.mocked(getAvailableTemplates).mockResolvedValue(['weather-graph', 'chatbot', 'data-analysis']);
240
+ vi.mocked(getAvailableTemplates).mockResolvedValue([
241
+ 'weather-project',
242
+ 'chatbot',
243
+ 'data-analysis',
244
+ ]);
236
245
  vi.mocked(cloneTemplate).mockResolvedValue(undefined);
237
246
  }
package/dist/templates.js CHANGED
@@ -1,5 +1,5 @@
1
- import fs from 'fs-extra';
2
1
  import degit from 'degit';
2
+ import fs from 'fs-extra';
3
3
  //Duplicating function here so we dont have to add a dependency on the agents-cli package
4
4
  export async function cloneTemplate(templatePath, targetPath) {
5
5
  await fs.mkdir(targetPath, { recursive: true });
@@ -14,9 +14,7 @@ export async function cloneTemplate(templatePath, targetPath) {
14
14
  }
15
15
  export async function getAvailableTemplates() {
16
16
  // Fetch the list of templates from your repo
17
- const response = await fetch('https://api.github.com/repos/inkeep/agents-cookbook/contents/templates');
17
+ const response = await fetch('https://api.github.com/repos/inkeep/agents-cookbook/contents/template-projects');
18
18
  const contents = await response.json();
19
- return contents
20
- .filter((item) => item.type === 'dir')
21
- .map((item) => item.name);
19
+ return contents.filter((item) => item.type === 'dir').map((item) => item.name);
22
20
  }
package/dist/utils.js CHANGED
@@ -66,8 +66,8 @@ export const createAgents = async (args = {}) => {
66
66
  }
67
67
  else {
68
68
  // No template or custom project ID provided - use defaults
69
- projectId = 'weather-graph';
70
- templateName = 'weather-graph';
69
+ projectId = 'weather-project';
70
+ templateName = 'weather-project';
71
71
  }
72
72
  p.intro(color.inverse(' Create Agents Directory '));
73
73
  // Prompt for directory name if not provided
@@ -93,11 +93,11 @@ export const createAgents = async (args = {}) => {
93
93
  // If keys aren't provided via CLI args, prompt for provider selection and keys
94
94
  if (!anthropicKey && !openAiKey) {
95
95
  const providerChoice = await p.select({
96
- message: 'Which AI provider(s) would you like to use?',
96
+ message: 'Which AI provider would you like to use?',
97
97
  options: [
98
- { value: 'anthropic', label: 'Anthropic only' },
99
- { value: 'openai', label: 'OpenAI only' },
100
- { value: 'google', label: 'Google only' },
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)) {
@@ -172,7 +172,7 @@ export const createAgents = async (args = {}) => {
172
172
  try {
173
173
  const agentsTemplateRepo = 'https://github.com/inkeep/create-agents-template';
174
174
  const projectTemplateRepo = templateName
175
- ? `https://github.com/inkeep/agents-cookbook/templates/${templateName}`
175
+ ? `https://github.com/inkeep/agents-cookbook/template-projects/${templateName}`
176
176
  : null;
177
177
  const directoryPath = path.resolve(process.cwd(), dirName);
178
178
  // Check if directory already exists
@@ -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,29 @@ async function createEnvironmentFiles(config) {
272
270
  ENVIRONMENT=development
273
271
 
274
272
  # Database
275
- DB_FILE_NAME=file:./local.db
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
279
 
290
- # Database (relative path from API directory)
291
- DB_FILE_NAME=file:../../local.db
280
+ # Inkeep API URLs
281
+ INKEEP_AGENTS_MANAGE_API_URL="http://localhost:3002"
282
+ INKEEP_AGENTS_RUN_API_URL="http://localhost:3003"
292
283
 
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'}
284
+ # SigNoz Configuration
285
+ SIGNOZ_URL=your-signoz-url-here
286
+ SIGNOZ_API_KEY=your-signoz-api-key-here
297
287
 
298
- AGENTS_RUN_API_URL=http://localhost:${config.runApiPort}
299
- `;
300
- const manageApiEnvContent = `# Environment
301
- ENVIRONMENT=development
302
-
303
- # Database (relative path from API directory)
304
- DB_FILE_NAME=file:../../local.db
288
+ # OTEL Configuration
289
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://ingest.us.signoz.cloud:443/v1/traces
290
+ OTEL_EXPORTER_OTLP_TRACES_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
305
291
 
306
- AGENTS_MANAGE_API_URL=http://localhost:${config.manageApiPort}
292
+ # Nango Configuration
293
+ NANGO_SECRET_KEY=
307
294
  `;
308
- await fs.writeFile('apps/manage-api/.env', manageApiEnvContent);
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
315
-
316
- # Database (relative path from project directory)
317
- DB_FILE_NAME=file:../../local.db
318
- `;
319
- await fs.writeFile(`src/${config.projectId}/.env`, projectEnvContent);
295
+ await fs.writeFile('.env', envContent);
320
296
  }
321
297
  async function createInkeepConfig(config) {
322
298
  const inkeepConfig = `import { defineConfig } from '@inkeep/agents-cli/config';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/create-agents",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Create an Inkeep Agent Framework project",
5
5
  "type": "module",
6
6
  "bin": {