@inkeep/create-agents 0.1.10 → 0.2.1

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/utils.js CHANGED
@@ -1,19 +1,20 @@
1
- import color from 'picocolors';
2
1
  import * as p from '@clack/prompts';
3
- import fs from 'fs-extra';
4
2
  import { exec } from 'child_process';
5
- import { promisify } from 'util';
3
+ import fs from 'fs-extra';
6
4
  import path from 'path';
5
+ import color from 'picocolors';
6
+ import { promisify } from 'util';
7
+ import { cloneTemplate, getAvailableTemplates } from './templates.js';
7
8
  const execAsync = promisify(exec);
8
- export const defaultDualModelConfigurations = {
9
+ export const defaultGoogleModelConfigurations = {
9
10
  base: {
10
- model: 'anthropic/claude-sonnet-4-20250514',
11
+ model: 'google/gemini-2.5-flash',
11
12
  },
12
13
  structuredOutput: {
13
- model: 'openai/gpt-4.1-mini-2025-04-14',
14
+ model: 'google/gemini-2.5-flash-lite',
14
15
  },
15
16
  summarizer: {
16
- model: 'openai/gpt-4.1-nano-2025-04-14',
17
+ model: 'google/gemini-2.5-flash-lite',
17
18
  },
18
19
  };
19
20
  export const defaultOpenaiModelConfigurations = {
@@ -32,17 +33,42 @@ export const defaultAnthropicModelConfigurations = {
32
33
  model: 'anthropic/claude-sonnet-4-20250514',
33
34
  },
34
35
  structuredOutput: {
35
- model: 'anthropic/claude-sonnet-4-20250514',
36
+ model: 'anthropic/claude-3-5-haiku-20241022',
36
37
  },
37
38
  summarizer: {
38
- model: 'anthropic/claude-sonnet-4-20250514',
39
+ model: 'anthropic/claude-3-5-haiku-20241022',
39
40
  },
40
41
  };
41
42
  export const createAgents = async (args = {}) => {
42
- let { projectId, dirName, openAiKey, anthropicKey } = args;
43
+ let { dirName, openAiKey, anthropicKey, googleKey, template, customProjectId } = args;
43
44
  const tenantId = 'default';
44
45
  const manageApiPort = '3002';
45
46
  const runApiPort = '3003';
47
+ let projectId;
48
+ let templateName;
49
+ // Determine project ID and template based on user input
50
+ if (customProjectId) {
51
+ // User provided custom project ID - use it as-is, no template needed
52
+ projectId = customProjectId;
53
+ templateName = ''; // No template will be cloned
54
+ }
55
+ else if (template) {
56
+ // User provided template - validate it exists and use template name as project ID
57
+ const availableTemplates = await getAvailableTemplates();
58
+ if (!availableTemplates.includes(template)) {
59
+ p.cancel(`${color.red('✗')} Template "${template}" not found\n\n` +
60
+ `${color.yellow('Available templates:')}\n` +
61
+ ` • ${availableTemplates.join('\n • ')}\n`);
62
+ process.exit(0);
63
+ }
64
+ projectId = template;
65
+ templateName = template;
66
+ }
67
+ else {
68
+ // No template or custom project ID provided - use defaults
69
+ projectId = 'weather-graph';
70
+ templateName = 'weather-graph';
71
+ }
46
72
  p.intro(color.inverse(' Create Agents Directory '));
47
73
  // Prompt for directory name if not provided
48
74
  if (!dirName) {
@@ -63,27 +89,15 @@ export const createAgents = async (args = {}) => {
63
89
  }
64
90
  dirName = dirResponse;
65
91
  }
66
- // Prompt for project ID
67
- if (!projectId) {
68
- const projectIdResponse = await p.text({
69
- message: 'Enter your project ID:',
70
- placeholder: '(default)',
71
- defaultValue: 'default',
72
- });
73
- if (p.isCancel(projectIdResponse)) {
74
- p.cancel('Operation cancelled');
75
- process.exit(0);
76
- }
77
- projectId = projectIdResponse;
78
- }
92
+ // Project ID is already determined above based on template/customProjectId logic
79
93
  // If keys aren't provided via CLI args, prompt for provider selection and keys
80
94
  if (!anthropicKey && !openAiKey) {
81
95
  const providerChoice = await p.select({
82
96
  message: 'Which AI provider(s) would you like to use?',
83
97
  options: [
84
- { value: 'both', label: 'Both Anthropic and OpenAI (recommended)' },
85
98
  { value: 'anthropic', label: 'Anthropic only' },
86
99
  { value: 'openai', label: 'OpenAI only' },
100
+ { value: 'google', label: 'Google only' },
87
101
  ],
88
102
  });
89
103
  if (p.isCancel(providerChoice)) {
@@ -91,7 +105,7 @@ export const createAgents = async (args = {}) => {
91
105
  process.exit(0);
92
106
  }
93
107
  // Prompt for keys based on selection
94
- if (providerChoice === 'anthropic' || providerChoice === 'both') {
108
+ if (providerChoice === 'anthropic') {
95
109
  const anthropicKeyResponse = await p.text({
96
110
  message: 'Enter your Anthropic API key:',
97
111
  placeholder: 'sk-ant-...',
@@ -108,7 +122,7 @@ export const createAgents = async (args = {}) => {
108
122
  }
109
123
  anthropicKey = anthropicKeyResponse;
110
124
  }
111
- if (providerChoice === 'openai' || providerChoice === 'both') {
125
+ else if (providerChoice === 'openai') {
112
126
  const openAiKeyResponse = await p.text({
113
127
  message: 'Enter your OpenAI API key:',
114
128
  placeholder: 'sk-...',
@@ -125,47 +139,41 @@ export const createAgents = async (args = {}) => {
125
139
  }
126
140
  openAiKey = openAiKeyResponse;
127
141
  }
128
- }
129
- else {
130
- // If some keys are provided via CLI args, prompt for missing ones
131
- if (!anthropicKey) {
132
- const anthropicKeyResponse = await p.text({
133
- message: 'Enter your Anthropic API key (optional):',
134
- placeholder: 'sk-ant-...',
135
- defaultValue: '',
136
- });
137
- if (p.isCancel(anthropicKeyResponse)) {
138
- p.cancel('Operation cancelled');
139
- process.exit(0);
140
- }
141
- anthropicKey = anthropicKeyResponse || undefined;
142
- }
143
- if (!openAiKey) {
144
- const openAiKeyResponse = await p.text({
145
- message: 'Enter your OpenAI API key (optional):',
146
- placeholder: 'sk-...',
147
- defaultValue: '',
142
+ else if (providerChoice === 'google') {
143
+ const googleKeyResponse = await p.text({
144
+ message: 'Enter your Google API key:',
145
+ placeholder: 'AIzaSy...',
146
+ validate: (value) => {
147
+ if (!value || value.trim() === '') {
148
+ return 'Google API key is required';
149
+ }
150
+ return undefined;
151
+ },
148
152
  });
149
- if (p.isCancel(openAiKeyResponse)) {
153
+ if (p.isCancel(googleKeyResponse)) {
150
154
  p.cancel('Operation cancelled');
151
155
  process.exit(0);
152
156
  }
153
- openAiKey = openAiKeyResponse || undefined;
157
+ googleKey = googleKeyResponse;
154
158
  }
155
159
  }
156
160
  let defaultModelSettings = {};
157
- if (anthropicKey && openAiKey) {
158
- defaultModelSettings = defaultDualModelConfigurations;
159
- }
160
- else if (anthropicKey) {
161
+ if (anthropicKey) {
161
162
  defaultModelSettings = defaultAnthropicModelConfigurations;
162
163
  }
163
164
  else if (openAiKey) {
164
165
  defaultModelSettings = defaultOpenaiModelConfigurations;
165
166
  }
167
+ else if (googleKey) {
168
+ defaultModelSettings = defaultGoogleModelConfigurations;
169
+ }
166
170
  const s = p.spinner();
167
171
  s.start('Creating directory structure...');
168
172
  try {
173
+ const agentsTemplateRepo = 'https://github.com/inkeep/create-agents-template';
174
+ const projectTemplateRepo = templateName
175
+ ? `https://github.com/inkeep/agents-cookbook/templates/${templateName}`
176
+ : null;
169
177
  const directoryPath = path.resolve(process.cwd(), dirName);
170
178
  // Check if directory already exists
171
179
  if (await fs.pathExists(directoryPath)) {
@@ -180,8 +188,10 @@ export const createAgents = async (args = {}) => {
180
188
  s.start('Cleaning existing directory...');
181
189
  await fs.emptyDir(directoryPath);
182
190
  }
183
- // Create the project directory
184
- await fs.ensureDir(directoryPath);
191
+ // Clone the template repository
192
+ s.message('Building template...');
193
+ await cloneTemplate(agentsTemplateRepo, directoryPath);
194
+ // Change to the project directory
185
195
  process.chdir(directoryPath);
186
196
  const config = {
187
197
  dirName,
@@ -192,25 +202,30 @@ export const createAgents = async (args = {}) => {
192
202
  manageApiPort: manageApiPort || '3002',
193
203
  runApiPort: runApiPort || '3003',
194
204
  modelSettings: defaultModelSettings,
205
+ customProject: customProjectId ? true : false,
195
206
  };
196
- // Create workspace structure
197
- s.message('Setting up workspace structure...');
198
- await createWorkspaceStructure(projectId);
199
- // Setup package configurations
200
- s.message('Creating package configurations...');
201
- await setupPackageConfigurations(dirName);
207
+ // Create workspace structure for project-specific files
208
+ s.message('Setting up project structure...');
209
+ await createWorkspaceStructure();
202
210
  // Create environment files
203
211
  s.message('Setting up environment files...');
204
212
  await createEnvironmentFiles(config);
213
+ // Create project template folder (only if template is specified)
214
+ if (projectTemplateRepo) {
215
+ s.message('Creating project template folder...');
216
+ const templateTargetPath = `src/${projectId}`;
217
+ await cloneTemplate(projectTemplateRepo, templateTargetPath);
218
+ }
219
+ else {
220
+ s.message('Creating empty project folder...');
221
+ await fs.ensureDir(`src/${projectId}`);
222
+ }
223
+ // create or overwrite inkeep.config.ts
224
+ s.message('Creating inkeep.config.ts...');
225
+ await createInkeepConfig(config);
205
226
  // Create service files
206
227
  s.message('Creating service files...');
207
228
  await createServiceFiles(config);
208
- // Create documentation
209
- s.message('Creating documentation...');
210
- await createDocumentation(config);
211
- // Create turbo config
212
- s.message('Setting up Turbo...');
213
- await createTurboConfig();
214
229
  // Install dependencies
215
230
  s.message('Installing dependencies (this may take a while)...');
216
231
  await installDependencies();
@@ -218,8 +233,9 @@ export const createAgents = async (args = {}) => {
218
233
  s.message('Setting up database...');
219
234
  await setupDatabase();
220
235
  // Setup project in database
221
- s.message('Setting up project in database...');
222
- await setupProjectInDatabase();
236
+ s.message('Pushing project...');
237
+ await setupProjectInDatabase(config);
238
+ s.message('Project setup complete!');
223
239
  s.stop();
224
240
  // Success message with next steps
225
241
  p.note(`${color.green('✓')} Project created at: ${color.cyan(directoryPath)}\n\n` +
@@ -236,7 +252,7 @@ export const createAgents = async (args = {}) => {
236
252
  ` • Manage UI: Available with management API\n` +
237
253
  `\n${color.yellow('Configuration:')}\n` +
238
254
  ` • Edit .env for environment variables\n` +
239
- ` • Edit src/${projectId}/weather.graph.ts for agent definitions\n` +
255
+ ` • Edit files in src/${projectId}/ for agent definitions\n` +
240
256
  ` • Use 'inkeep push' to deploy agents to the platform\n` +
241
257
  ` • Use 'inkeep chat' to test your agents locally\n`, 'Ready to go!');
242
258
  }
@@ -246,133 +262,9 @@ export const createAgents = async (args = {}) => {
246
262
  process.exit(1);
247
263
  }
248
264
  };
249
- async function createWorkspaceStructure(projectId) {
265
+ async function createWorkspaceStructure() {
250
266
  // Create the workspace directory structure
251
- await fs.ensureDir(`src/${projectId}`);
252
- await fs.ensureDir('apps/manage-api/src');
253
- await fs.ensureDir('apps/run-api/src');
254
- await fs.ensureDir('apps/shared');
255
- await fs.ensureDir('scripts');
256
- }
257
- async function setupPackageConfigurations(dirName) {
258
- // Root package.json (workspace root)
259
- const rootPackageJson = {
260
- name: dirName,
261
- version: '0.1.0',
262
- description: 'An Inkeep Agent Framework directory',
263
- private: true,
264
- type: 'module',
265
- scripts: {
266
- dev: 'turbo dev',
267
- 'db:push': 'drizzle-kit push',
268
- setup: 'node scripts/setup.js',
269
- 'dev:setup': 'node scripts/dev-setup.js',
270
- start: 'pnpm dev:setup'
271
- },
272
- dependencies: {},
273
- devDependencies: {
274
- '@biomejs/biome': '^1.8.0',
275
- '@inkeep/agents-cli': '^0.1.1',
276
- 'drizzle-kit': '^0.31.4',
277
- tsx: '^4.19.0',
278
- turbo: '^2.5.5',
279
- "concurrently": '^8.2.0',
280
- 'wait-on': '^8.0.0',
281
- },
282
- engines: {
283
- node: '>=22.x',
284
- },
285
- packageManager: 'pnpm@10.10.0',
286
- };
287
- await fs.writeJson('package.json', rootPackageJson, { spaces: 2 });
288
- // Create pnpm-workspace.yaml for pnpm workspaces
289
- const pnpmWorkspace = `packages:
290
- - "apps/*"
291
- `;
292
- await fs.writeFile('pnpm-workspace.yaml', pnpmWorkspace);
293
- // Add shared dependencies to root package.json
294
- rootPackageJson.dependencies = {
295
- '@inkeep/agents-core': '^0.1.0',
296
- '@inkeep/agents-sdk': '^0.1.0',
297
- dotenv: '^16.0.0',
298
- zod: '^4.1.5',
299
- };
300
- await fs.writeJson('package.json', rootPackageJson, { spaces: 2 });
301
- // Manage API package
302
- const manageApiPackageJson = {
303
- name: `@${dirName}/manage-api`,
304
- version: '0.1.0',
305
- description: 'Manage API for agents',
306
- type: 'module',
307
- scripts: {
308
- build: 'tsc',
309
- dev: 'tsx watch src/index.ts',
310
- start: 'node dist/index.js',
311
- },
312
- dependencies: {
313
- '@inkeep/agents-manage-api': '^0.1.1',
314
- '@inkeep/agents-core': '^0.1.0',
315
- '@hono/node-server': '^1.14.3',
316
- },
317
- devDependencies: {
318
- '@types/node': '^20.12.0',
319
- tsx: '^4.19.0',
320
- typescript: '^5.4.0',
321
- },
322
- engines: {
323
- node: '>=22.x',
324
- },
325
- };
326
- await fs.writeJson('apps/manage-api/package.json', manageApiPackageJson, { spaces: 2 });
327
- // Run API package
328
- const runApiPackageJson = {
329
- name: `@${dirName}/run-api`,
330
- version: '0.1.0',
331
- description: 'Run API for agents',
332
- type: 'module',
333
- scripts: {
334
- dev: 'tsx watch src/index.ts',
335
- start: 'node dist/index.js',
336
- },
337
- dependencies: {
338
- '@inkeep/agents-run-api': '^0.1.1',
339
- '@inkeep/agents-core': '^0.1.0',
340
- '@hono/node-server': '^1.14.3',
341
- },
342
- devDependencies: {
343
- '@types/node': '^20.12.0',
344
- tsx: '^4.19.0',
345
- typescript: '^5.4.0',
346
- },
347
- engines: {
348
- node: '>=22.x',
349
- },
350
- };
351
- await fs.writeJson('apps/run-api/package.json', runApiPackageJson, { spaces: 2 });
352
- // TypeScript configs for API services
353
- const apiTsConfig = {
354
- compilerOptions: {
355
- target: 'ES2022',
356
- module: 'ESNext',
357
- moduleResolution: 'bundler',
358
- strict: true,
359
- esModuleInterop: true,
360
- skipLibCheck: true,
361
- forceConsistentCasingInFileNames: true,
362
- declaration: true,
363
- outDir: './dist',
364
- rootDir: '..',
365
- allowImportingTsExtensions: false,
366
- resolveJsonModule: true,
367
- isolatedModules: true,
368
- noEmit: false,
369
- },
370
- include: ['src/**/*', '../shared/**/*'],
371
- exclude: ['node_modules', 'dist', '**/*.test.ts'],
372
- };
373
- await fs.writeJson('apps/manage-api/tsconfig.json', apiTsConfig, { spaces: 2 });
374
- await fs.writeJson('apps/run-api/tsconfig.json', apiTsConfig, { spaces: 2 });
375
- // No tsconfig needed for UI since we're using the packaged version
267
+ await fs.ensureDir(`src`);
376
268
  }
377
269
  async function createEnvironmentFiles(config) {
378
270
  // Root .env file
@@ -385,25 +277,12 @@ DB_FILE_NAME=file:./local.db
385
277
  # AI Provider Keys
386
278
  ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
387
279
  OPENAI_API_KEY=${config.openAiKey || 'your-openai-key-here'}
388
-
389
- # Logging
390
- LOG_LEVEL=debug
391
-
392
- # Service Ports
393
- MANAGE_API_PORT=${config.manageApiPort}
394
- RUN_API_PORT=${config.runApiPort}
395
-
396
- # UI Configuration (for dashboard)
397
-
280
+ GOOGLE_GENERATIVE_AI_API_KEY=${config.googleKey || 'your-google-key-here'}
398
281
  `;
399
282
  await fs.writeFile('.env', envContent);
400
283
  // Create .env.example
401
284
  const envExample = envContent.replace(/=.+$/gm, '=');
402
285
  await fs.writeFile('.env.example', envExample);
403
- // Create setup script
404
- await createSetupScript(config);
405
- // Create dev-setup script
406
- await createDevSetupScript(config);
407
286
  // Create .env files for each API service
408
287
  const runApiEnvContent = `# Environment
409
288
  ENVIRONMENT=development
@@ -414,6 +293,7 @@ DB_FILE_NAME=file:../../local.db
414
293
  # AI Provider Keys
415
294
  ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
416
295
  OPENAI_API_KEY=${config.openAiKey || 'your-openai-key-here'}
296
+ GOOGLE_GENERATIVE_AI_API_KEY=${config.googleKey || 'your-google-key-here'}
417
297
 
418
298
  AGENTS_RUN_API_URL=http://localhost:${config.runApiPort}
419
299
  `;
@@ -427,287 +307,8 @@ AGENTS_MANAGE_API_URL=http://localhost:${config.manageApiPort}
427
307
  `;
428
308
  await fs.writeFile('apps/manage-api/.env', manageApiEnvContent);
429
309
  await fs.writeFile('apps/run-api/.env', runApiEnvContent);
430
- // Create .gitignore
431
- const gitignore = `# Dependencies
432
- node_modules/
433
- .pnpm-store/
434
-
435
- # Environment variables
436
- .env
437
- .env.local
438
- .env.development.local
439
- .env.test.local
440
- .env.production.local
441
-
442
- # Build outputs
443
- dist/
444
- build/
445
- .next/
446
- .turbo/
447
-
448
- # Logs
449
- *.log
450
- logs/
451
-
452
- # Database
453
- *.db
454
- *.sqlite
455
- *.sqlite3
456
-
457
- # IDE
458
- .vscode/
459
- .idea/
460
- *.swp
461
- *.swo
462
-
463
- # OS
464
- .DS_Store
465
- Thumbs.db
466
-
467
- # Coverage
468
- coverage/
469
- .nyc_output/
470
-
471
- # Temporary files
472
- *.tmp
473
- *.temp
474
- .cache/
475
-
476
- # Runtime data
477
- pids/
478
- *.pid
479
- *.seed
480
- *.pid.lock
481
- `;
482
- await fs.writeFile('.gitignore', gitignore);
483
- // Create biome.json
484
- const biomeConfig = {
485
- linter: {
486
- enabled: true,
487
- rules: {
488
- recommended: true,
489
- },
490
- },
491
- formatter: {
492
- enabled: true,
493
- indentStyle: 'space',
494
- indentWidth: 2,
495
- },
496
- organizeImports: {
497
- enabled: true,
498
- },
499
- javascript: {
500
- formatter: {
501
- semicolons: 'always',
502
- quoteStyle: 'single',
503
- },
504
- },
505
- };
506
- await fs.writeJson('biome.json', biomeConfig, { spaces: 2 });
507
- }
508
- async function createSetupScript(config) {
509
- const setupScriptContent = `#!/usr/bin/env node
510
-
511
- import { createDatabaseClient, createProject, getProject } from '@inkeep/agents-core';
512
- import dotenv from 'dotenv';
513
-
514
- // Load environment variables
515
- dotenv.config();
516
-
517
- const dbUrl = process.env.DB_FILE_NAME || 'file:local.db';
518
- const tenantId = '${config.tenantId}';
519
- const projectId = '${config.projectId}';
520
- const projectName = '${config.projectId}';
521
- const projectDescription = 'Generated Inkeep Agents project';
522
-
523
- async function setupProject() {
524
- console.log('🚀 Setting up your Inkeep Agents project...');
525
-
526
- try {
527
- const dbClient = createDatabaseClient({ url: dbUrl });
528
-
529
- // Check if project already exists
530
- console.log('📋 Checking if project already exists...');
531
- try {
532
- const existingProject = await getProject(dbClient)({
533
- id: projectId,
534
- tenantId: tenantId
535
- });
536
-
537
- if (existingProject) {
538
- console.log('✅ Project already exists in database:', existingProject.name);
539
- console.log('🎯 Project ID:', projectId);
540
- console.log('🏢 Tenant ID:', tenantId);
541
- return;
542
- }
543
- } catch (error) {
544
- // Project doesn't exist, continue with creation
545
- }
546
-
547
- // Create the project in the database
548
- console.log('📦 Creating project in database...');
549
- await createProject(dbClient)({
550
- id: projectId,
551
- tenantId: tenantId,
552
- name: projectName,
553
- description: projectDescription,
554
- models: ${JSON.stringify(config.modelSettings, null, 2)},
555
- });
556
-
557
- console.log('✅ Project created successfully!');
558
- console.log('🎯 Project ID:', projectId);
559
- console.log('🏢 Tenant ID:', tenantId);
560
- console.log('');
561
- console.log('🎉 Setup complete! Your development servers are running.');
562
- console.log('');
563
- console.log('📋 Available URLs:');
564
- console.log(' - Management UI: http://localhost:${config.manageApiPort}');
565
- console.log(' - Runtime API: http://localhost:${config.runApiPort}');
566
- console.log('');
567
- console.log('🚀 Ready to build agents!');
568
-
569
- } catch (error) {
570
- console.error('❌ Failed to setup project:', error);
571
- process.exit(1);
572
- }
573
- }
574
-
575
- setupProject();
576
- `;
577
- await fs.writeFile('scripts/setup.js', setupScriptContent);
578
- // Make the script executable
579
- await fs.chmod('scripts/setup.js', 0o755);
580
- }
581
- async function createDevSetupScript(config) {
582
- const devSetupScriptContent = `#!/usr/bin/env node
583
-
584
- import { spawn } from 'child_process';
585
- import { promisify } from 'util';
586
- import { exec } from 'child_process';
587
-
588
- const execAsync = promisify(exec);
589
-
590
- async function devSetup() {
591
- console.log('🚀 Starting Inkeep Agents development environment...');
592
- console.log('');
593
-
594
- try {
595
- // Start development servers in background
596
- console.log('📡 Starting development servers...');
597
- const devProcess = spawn('pnpm', ['dev'], {
598
- stdio: ['pipe', 'pipe', 'pipe'],
599
- detached: false
600
- });
601
-
602
- // Give servers time to start
603
- console.log('⏳ Waiting for servers to start...');
604
- await new Promise(resolve => setTimeout(resolve, 5000));
605
-
606
- console.log('');
607
- console.log('📦 Servers are ready! Setting up project in database...');
608
-
609
- // Run the setup script
610
- await execAsync('pnpm setup');
611
-
612
- console.log('');
613
- console.log('🎉 Development environment is ready!');
614
- console.log('');
615
- console.log('📋 Available URLs:');
616
- console.log(\` - Management UI: http://localhost:${config.manageApiPort}\`);
617
- console.log(\` - Runtime API: http://localhost:${config.runApiPort}\`);
618
- console.log('');
619
- console.log('✨ The servers will continue running. Press Ctrl+C to stop.');
620
-
621
- // Keep the script running so servers don't terminate
622
- process.on('SIGINT', () => {
623
- console.log('\\n👋 Shutting down development servers...');
624
- devProcess.kill();
625
- process.exit(0);
626
- });
627
-
628
- // Wait for the dev process to finish or be killed
629
- devProcess.on('close', (code) => {
630
- console.log(\`Development servers stopped with code \${code}\`);
631
- process.exit(code);
632
- });
633
-
634
- } catch (error) {
635
- console.error('❌ Failed to start development environment:', error.message);
636
- process.exit(1);
637
- }
638
- }
639
-
640
- devSetup();
641
- `;
642
- await fs.writeFile('scripts/dev-setup.js', devSetupScriptContent);
643
- await fs.chmod('scripts/dev-setup.js', 0o755);
644
310
  }
645
311
  async function createServiceFiles(config) {
646
- const agentsGraph = `import { agent, agentGraph, mcpTool } from '@inkeep/agents-sdk';
647
-
648
- // MCP Tools
649
- const forecastWeatherTool = mcpTool({
650
- id: 'fUI2riwrBVJ6MepT8rjx0',
651
- name: 'Forecast weather',
652
- serverUrl: 'https://weather-forecast-mcp.vercel.app/mcp',
653
- });
654
-
655
- const geocodeAddressTool = mcpTool({
656
- id: 'fdxgfv9HL7SXlfynPx8hf',
657
- name: 'Geocode address',
658
- serverUrl: 'https://geocoder-mcp.vercel.app/mcp',
659
- });
660
-
661
- // Agents
662
- const weatherAssistant = agent({
663
- id: 'weather-assistant',
664
- name: 'Weather assistant',
665
- description: 'Responsible for routing between the geocoder agent and weather forecast agent',
666
- prompt:
667
- 'You are a helpful assistant. When the user asks about the weather in a given location, first ask the geocoder agent for the coordinates, and then pass those coordinates to the weather forecast agent to get the weather forecast',
668
- canDelegateTo: () => [weatherForecaster, geocoderAgent],
669
- });
670
-
671
- const weatherForecaster = agent({
672
- id: 'weather-forecaster',
673
- name: 'Weather forecaster',
674
- description:
675
- 'This agent is responsible for taking in coordinates and returning the forecast for the weather at that location',
676
- prompt:
677
- 'You are a helpful assistant responsible for taking in coordinates and returning the forecast for that location using your forecasting tool',
678
- canUse: () => [forecastWeatherTool],
679
- });
680
-
681
- const geocoderAgent = agent({
682
- id: 'geocoder-agent',
683
- name: 'Geocoder agent',
684
- description: 'Responsible for converting location or address into coordinates',
685
- prompt:
686
- 'You are a helpful assistant responsible for converting location or address into coordinates using your geocode tool',
687
- canUse: () => [geocodeAddressTool],
688
- });
689
-
690
- // Agent Graph
691
- export const weatherGraph = agentGraph({
692
- id: 'weather-graph',
693
- name: 'Weather graph',
694
- defaultAgent: weatherAssistant,
695
- agents: () => [weatherAssistant, weatherForecaster, geocoderAgent],
696
- });`;
697
- await fs.writeFile(`src/${config.projectId}/weather.graph.ts`, agentsGraph);
698
- // Inkeep config (if using CLI)
699
- const inkeepConfig = `import { defineConfig } from '@inkeep/agents-cli/config';
700
-
701
- const config = defineConfig({
702
- tenantId: "${config.tenantId}",
703
- projectId: "${config.projectId}",
704
- agentsManageApiUrl: \`http://localhost:\${process.env.MANAGE_API_PORT || '3002'}\`,
705
- agentsRunApiUrl: \`http://localhost:\${process.env.RUN_API_PORT || '3003'}\`,
706
- modelSettings: ${JSON.stringify(config.modelSettings, null, 2)},
707
- });
708
-
709
- export default config;`;
710
- await fs.writeFile(`src/${config.projectId}/inkeep.config.ts`, inkeepConfig);
711
312
  // Create .env file for the project directory (for inkeep CLI commands)
712
313
  const projectEnvContent = `# Environment
713
314
  ENVIRONMENT=development
@@ -716,306 +317,61 @@ ENVIRONMENT=development
716
317
  DB_FILE_NAME=file:../../local.db
717
318
  `;
718
319
  await fs.writeFile(`src/${config.projectId}/.env`, projectEnvContent);
719
- // Shared credential stores
720
- const credentialStoresFile = `import {
721
- InMemoryCredentialStore,
722
- createNangoCredentialStore,
723
- createKeyChainStore,
724
- } from '@inkeep/agents-core';
725
-
726
- // Shared credential stores configuration for all services
727
- export const credentialStores = [
728
- new InMemoryCredentialStore('memory-default'),
729
- ...(process.env.NANGO_SECRET_KEY
730
- ? [
731
- createNangoCredentialStore('nango-default', {
732
- apiUrl: process.env.NANGO_HOST || 'https://api.nango.dev',
733
- secretKey: process.env.NANGO_SECRET_KEY,
734
- }),
735
- ]
736
- : []),
737
- createKeyChainStore('keychain-default'),
738
- ];
739
- `;
740
- await fs.writeFile('apps/shared/credential-stores.ts', credentialStoresFile);
741
- // Manage API
742
- const manageApiIndex = `import { serve } from '@hono/node-server';
743
- import { createManagementApp } from '@inkeep/agents-manage-api';
744
- import { getLogger } from '@inkeep/agents-core';
745
- import { credentialStores } from '../../shared/credential-stores.js';
746
-
747
- const logger = getLogger('management-api');
748
-
749
- // Create the Hono app
750
- const app = createManagementApp({
751
- serverConfig: {
752
- port: Number(process.env.MANAGE_API_PORT) || 3002,
753
- serverOptions: {
754
- requestTimeout: 60000,
755
- keepAliveTimeout: 60000,
756
- keepAlive: true,
757
- },
758
- },
759
- credentialStores,
760
- });
761
-
762
- const port = Number(process.env.MANAGE_API_PORT) || 3002;
763
-
764
- // Start the server using @hono/node-server
765
- serve(
766
- {
767
- fetch: app.fetch,
768
- port,
769
- },
770
- (info) => {
771
- logger.info({}, \`📝 Management API running on http://localhost:\${info.port}\`);
772
- logger.info({}, \`📝 OpenAPI documentation available at http://localhost:\${info.port}/openapi.json\`);
773
- }
774
- );`;
775
- await fs.writeFile('apps/manage-api/src/index.ts', manageApiIndex);
776
- // Run API
777
- const runApiIndex = `import { serve } from '@hono/node-server';
778
- import { createExecutionApp } from '@inkeep/agents-run-api';
779
- import { credentialStores } from '../../shared/credential-stores.js';
780
- import { getLogger } from '@inkeep/agents-core';
781
-
782
- const logger = getLogger('execution-api');
783
-
784
-
785
- // Create the Hono app
786
- const app = createExecutionApp({
787
- serverConfig: {
788
- port: Number(process.env.RUN_API_PORT) || 3003,
789
- serverOptions: {
790
- requestTimeout: 120000,
791
- keepAliveTimeout: 60000,
792
- keepAlive: true,
793
- },
794
- },
795
- credentialStores,
796
- });
797
-
798
- const port = Number(process.env.RUN_API_PORT) || 3003;
799
-
800
- // Start the server using @hono/node-server
801
- serve(
802
- {
803
- fetch: app.fetch,
804
- port,
805
- },
806
- (info) => {
807
- logger.info({}, \`📝 Run API running on http://localhost:\${info.port}\`);
808
- logger.info({}, \`📝 OpenAPI documentation available at http://localhost:\${info.port}/openapi.json\`);
809
- }
810
- );`;
811
- await fs.writeFile('apps/run-api/src/index.ts', runApiIndex);
812
- // Database configuration
813
- const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
814
-
815
- export default defineConfig({
816
- schema: 'node_modules/@inkeep/agents-core/dist/db/schema.js',
817
- dialect: 'sqlite',
818
- dbCredentials: {
819
- url: process.env.DB_FILE_NAME || 'file:./local.db'
820
- },
821
- });`;
822
- await fs.writeFile('drizzle.config.ts', drizzleConfig);
823
- }
824
- async function createTurboConfig() {
825
- const turboConfig = {
826
- $schema: 'https://turbo.build/schema.json',
827
- ui: 'tui',
828
- globalDependencies: ['**/.env', '**/.env.local', '**/.env.*'],
829
- globalEnv: [
830
- 'NODE_ENV',
831
- 'CI',
832
- 'ANTHROPIC_API_KEY',
833
- 'OPENAI_API_KEY',
834
- 'ENVIRONMENT',
835
- 'DB_FILE_NAME',
836
- 'MANAGE_API_PORT',
837
- 'RUN_API_PORT',
838
- 'LOG_LEVEL',
839
- 'NANGO_SECRET_KEY',
840
- ],
841
- tasks: {
842
- build: {
843
- dependsOn: ['^build'],
844
- inputs: ['$TURBO_DEFAULT$', '.env*'],
845
- outputs: ['dist/**', 'build/**', '.next/**', '!.next/cache/**'],
846
- },
847
- dev: {
848
- cache: false,
849
- persistent: true,
850
- },
851
- start: {
852
- dependsOn: ['build'],
853
- cache: false,
854
- },
855
- 'db:push': {
856
- cache: false,
857
- inputs: ['drizzle.config.ts', 'src/data/db/schema.ts'],
858
- },
859
- },
860
- };
861
- await fs.writeJson('turbo.json', turboConfig, { spaces: 2 });
862
320
  }
863
- async function createDocumentation(config) {
864
- const readme = `# ${config.dirName}
865
-
866
- An Inkeep Agent Framework project with multi-service architecture.
867
-
868
- ## Architecture
869
-
870
- This project follows a workspace structure with the following services:
871
-
872
- - **Agents Manage API** (Port 3002): Agent configuration and managemen
873
- - Handles entity management and configuration endpoints.
874
- - **Agents Run API** (Port 3003): Agent execution and chat processing
875
- - Handles agent communication. You can interact with your agents either over MCP from an MCP client or through our React UI components library
876
- - **Agents Manage UI** (Port 3000): Web interface available via \`inkeep dev\`
877
- - The agent framework visual builder. From the builder you can create, manage and visualize all your graphs.
878
-
879
- ## Quick Start
880
- 1. **Install the Inkeep CLI:**
881
- \`\`\`bash
882
- pnpm install -g @inkeep/agents-cli
883
- \`\`\`
884
-
885
- 1. **Start services:**
886
- \`\`\`bash
887
- # Start Agents Manage API and Agents Run API
888
- pnpm dev
889
-
890
- # Start the Dashboard
891
- inkeep dev
892
- \`\`\`
893
-
894
- 3. **Deploy your first agent graph:**
895
- \`\`\`bash
896
- # Navigate to your project's graph directory
897
- cd src/${config.projectId}/
898
-
899
- # Push the weather graph to create it
900
- inkeep push weather.graph.ts
901
- \`\`\`
902
- - Follow the prompts to create the project and graph
903
- - Click on the \"View graph in UI:\" link to see the graph in the management dashboard
904
-
905
- ## Project Structure
906
-
907
- \`\`\`
908
- ${config.dirName}/
909
- ├── src/
910
- │ ├── /${config.projectId} # Agent configurations
911
- ├── apps/
912
- │ ├── manage-api/ # Agents Manage API service
913
- │ ├── run-api/ # Agents Run API service
914
- │ └── shared/ # Shared code between API services
915
- │ └── credential-stores.ts # Shared credential store configuration
916
- ├── turbo.json # Turbo configuration
917
- ├── pnpm-workspace.yaml # pnpm workspace configuration
918
- └── package.json # Root package configuration
919
- \`\`\`
920
-
921
- ## Configuration
922
-
923
- ### Environment Variables
924
-
925
- Environment variables are defined in the following places:
926
-
927
- - \`apps/manage-api/.env\`: Agents Manage API environment variables
928
- - \`apps/run-api/.env\`: Agents Run API environment variables
929
- - \`src/${config.projectId}/.env\`: Inkeep CLI environment variables
930
- - \`.env\`: Root environment variables
931
-
932
- To change the API keys used by your agents modify \`apps/run-api/.env\`. You are required to define at least one LLM provider key.
933
-
934
- \`\`\`bash
935
- # AI Provider Keys
936
- ANTHROPIC_API_KEY=your-anthropic-key-here
937
- OPENAI_API_KEY=your-openai-key-here
938
- \`\`\`
939
-
940
-
941
-
942
- ### Agent Configuration
943
-
944
- Your graphs are defined in \`src/${config.projectId}/weather.graph.ts\`. The default setup includes:
945
-
946
- - **Weather Graph**: A graph that can forecast the weather in a given location.
947
-
948
- Your inkeep configuration is defined in \`src/${config.projectId}/inkeep.config.ts\`. The inkeep configuration is used to configure defaults for the inkeep CLI. The configuration includes:
949
-
950
- - \`tenantId\`: The tenant ID
951
- - \`projectId\`: The project ID
952
- - \`agentsManageApiUrl\`: The Manage API URL
953
- - \`agentsRunApiUrl\`: The Run API URL
954
-
955
-
956
- ## Development
957
-
958
- ### Updating Your Agents
959
-
960
- 1. Edit \`src/${config.projectId}/weather.graph.ts\`
961
- 2. Push the graph to the platform to update: \`inkeep pus weather.graph.ts\`
962
-
963
- ### API Documentation
964
-
965
- Once services are running, view the OpenAPI documentation:
966
-
967
- - Manage API: http://localhost:${config.manageApiPort}/docs
968
- - Run API: http://localhost:${config.runApiPort}/docs
969
-
970
- ## Learn More
971
-
972
- - [Inkeep Documentation](https://docs.inkeep.com)
973
-
974
- ## Troubleshooting
975
-
976
- ## Inkeep CLI commands
977
-
978
- - Ensure you are runnning commands from \`cd src/${config.projectId}\`.
979
- - Validate the \`inkeep.config.ts\` file has the correct api urls.
980
- - Validate that the \`.env\` file in \`src/${config.projectId}\` has the correct \`DB_FILE_NAME\`.
981
-
982
- ### Services won't start
983
-
984
- 1. Ensure all dependencies are installed: \`pnpm install\`
985
- 2. Check that ports 3000-3003 are available
986
-
987
- ### Agents won't respond
321
+ async function createInkeepConfig(config) {
322
+ const inkeepConfig = `import { defineConfig } from '@inkeep/agents-cli/config';
988
323
 
989
- 1. Ensure that the Agents Run API is running and includes a valid Anthropic or OpenAI API key in its .env file
990
- `;
991
- await fs.writeFile('README.md', readme);
324
+ const config = defineConfig({
325
+ tenantId: "${config.tenantId}",
326
+ projectId: "${config.projectId}",
327
+ agentsManageApiUrl: 'http://localhost:3002',
328
+ agentsRunApiUrl: 'http://localhost:3003',
329
+ modelSettings: ${JSON.stringify(config.modelSettings, null, 2)},
330
+ });
331
+
332
+ export default config;`;
333
+ await fs.writeFile(`src/${config.projectId}/inkeep.config.ts`, inkeepConfig);
334
+ if (config.customProject) {
335
+ const customIndexContent = `import { project } from '@inkeep/agents-sdk';
336
+
337
+ export const myProject = project({
338
+ id: "${config.projectId}",
339
+ name: "${config.projectId}",
340
+ description: "",
341
+ graphs: () => [],
342
+ });`;
343
+ await fs.writeFile(`src/${config.projectId}/index.ts`, customIndexContent);
344
+ }
992
345
  }
993
346
  async function installDependencies() {
994
347
  await execAsync('pnpm install');
995
348
  }
996
- async function setupProjectInDatabase() {
997
- const s = p.spinner();
998
- s.start('🚀 Starting development servers and setting up database...');
349
+ async function setupProjectInDatabase(config) {
350
+ // Start development servers in background
351
+ const { spawn } = await import('child_process');
352
+ const devProcess = spawn('pnpm', ['dev:apis'], {
353
+ stdio: ['pipe', 'pipe', 'pipe'],
354
+ detached: true, // Detach so we can kill the process group
355
+ cwd: process.cwd(),
356
+ });
357
+ // Give servers time to start
358
+ await new Promise((resolve) => setTimeout(resolve, 5000));
359
+ // Run inkeep push
999
360
  try {
1000
- // Start development servers in background
1001
- const { spawn } = await import('child_process');
1002
- const devProcess = spawn('pnpm', ['dev'], {
1003
- stdio: ['pipe', 'pipe', 'pipe'],
1004
- detached: true, // Detach so we can kill the process group
1005
- cwd: process.cwd()
1006
- });
1007
- // Give servers time to start
1008
- await new Promise(resolve => setTimeout(resolve, 5000));
1009
- s.message('📦 Servers ready! Creating project in database...');
1010
- // Run the database setup
1011
- await execAsync('node scripts/setup.js');
361
+ // Suppress all output
362
+ const { stdout, stderr } = await execAsync(`pnpm inkeep push --project src/${config.projectId}`);
363
+ }
364
+ catch (error) {
365
+ //Continue despite error - user can setup project manually
366
+ }
367
+ finally {
1012
368
  // Kill the dev servers and their child processes
1013
369
  if (devProcess.pid) {
1014
370
  try {
1015
371
  // Kill the entire process group
1016
372
  process.kill(-devProcess.pid, 'SIGTERM');
1017
373
  // Wait a moment for graceful shutdown
1018
- await new Promise(resolve => setTimeout(resolve, 1000));
374
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1019
375
  // Force kill if still running
1020
376
  try {
1021
377
  process.kill(-devProcess.pid, 'SIGKILL');
@@ -1029,18 +385,13 @@ async function setupProjectInDatabase() {
1029
385
  console.log('Note: Dev servers may still be running in background');
1030
386
  }
1031
387
  }
1032
- s.stop('✅ Project successfully created and configured in database!');
1033
- }
1034
- catch (error) {
1035
- s.stop('❌ Failed to setup project in database');
1036
- console.error('Setup error:', error);
1037
- // Continue anyway - user can run setup manually
1038
388
  }
1039
389
  }
1040
390
  async function setupDatabase() {
1041
391
  try {
1042
392
  // Run drizzle-kit push to create database file and apply schema
1043
393
  await execAsync('pnpm db:push');
394
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1044
395
  }
1045
396
  catch (error) {
1046
397
  throw new Error(`Failed to setup database: ${error instanceof Error ? error.message : 'Unknown error'}`);