@inkeep/agents-cli 0.1.5 → 0.1.6

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.
Files changed (59) hide show
  1. package/SUPPLEMENTAL_TERMS.md +40 -0
  2. package/dist/commands/create.d.ts +12 -0
  3. package/dist/commands/create.js +865 -0
  4. package/dist/config.d.ts +4 -4
  5. package/dist/index.js +5306 -20407
  6. package/package.json +15 -6
  7. package/dist/__tests__/api.test.d.ts +0 -1
  8. package/dist/__tests__/api.test.js +0 -257
  9. package/dist/__tests__/cli.test.d.ts +0 -1
  10. package/dist/__tests__/cli.test.js +0 -153
  11. package/dist/__tests__/commands/config.test.d.ts +0 -1
  12. package/dist/__tests__/commands/config.test.js +0 -154
  13. package/dist/__tests__/commands/init.test.d.ts +0 -1
  14. package/dist/__tests__/commands/init.test.js +0 -186
  15. package/dist/__tests__/commands/pull.test.d.ts +0 -1
  16. package/dist/__tests__/commands/pull.test.js +0 -54
  17. package/dist/__tests__/commands/push-spinner.test.d.ts +0 -1
  18. package/dist/__tests__/commands/push-spinner.test.js +0 -127
  19. package/dist/__tests__/commands/push.test.d.ts +0 -1
  20. package/dist/__tests__/commands/push.test.js +0 -265
  21. package/dist/__tests__/config-validation.test.d.ts +0 -1
  22. package/dist/__tests__/config-validation.test.js +0 -98
  23. package/dist/__tests__/package.test.d.ts +0 -1
  24. package/dist/__tests__/package.test.js +0 -82
  25. package/dist/__tests__/utils/json-comparator.test.d.ts +0 -1
  26. package/dist/__tests__/utils/json-comparator.test.js +0 -174
  27. package/dist/__tests__/utils/ts-loader.test.d.ts +0 -1
  28. package/dist/__tests__/utils/ts-loader.test.js +0 -232
  29. package/dist/api.d.ts +0 -23
  30. package/dist/api.js +0 -140
  31. package/dist/commands/chat-enhanced.d.ts +0 -7
  32. package/dist/commands/chat-enhanced.js +0 -396
  33. package/dist/commands/chat.d.ts +0 -5
  34. package/dist/commands/chat.js +0 -125
  35. package/dist/commands/config.d.ts +0 -6
  36. package/dist/commands/config.js +0 -128
  37. package/dist/commands/init.d.ts +0 -5
  38. package/dist/commands/init.js +0 -171
  39. package/dist/commands/list-graphs.d.ts +0 -6
  40. package/dist/commands/list-graphs.js +0 -131
  41. package/dist/commands/pull.d.ts +0 -15
  42. package/dist/commands/pull.js +0 -305
  43. package/dist/commands/pull.llm-generate.d.ts +0 -10
  44. package/dist/commands/pull.llm-generate.js +0 -184
  45. package/dist/commands/push.d.ts +0 -6
  46. package/dist/commands/push.js +0 -268
  47. package/dist/exports.d.ts +0 -2
  48. package/dist/exports.js +0 -2
  49. package/dist/index.js.map +0 -7
  50. package/dist/types/config.d.ts +0 -9
  51. package/dist/types/config.js +0 -3
  52. package/dist/types/graph.d.ts +0 -10
  53. package/dist/types/graph.js +0 -1
  54. package/dist/utils/json-comparator.d.ts +0 -60
  55. package/dist/utils/json-comparator.js +0 -222
  56. package/dist/utils/mcp-runner.d.ts +0 -6
  57. package/dist/utils/mcp-runner.js +0 -147
  58. package/dist/utils/ts-loader.d.ts +0 -5
  59. package/dist/utils/ts-loader.js +0 -145
@@ -0,0 +1,865 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/create.ts
4
+ import color from "picocolors";
5
+ import * as p from "@clack/prompts";
6
+ import fs from "fs-extra";
7
+ import { exec } from "child_process";
8
+ import { promisify } from "util";
9
+ import path from "path";
10
+
11
+ // src/utils/model-config.ts
12
+ import inquirer from "inquirer";
13
+ var defaultDualModelConfigurations = {
14
+ base: {
15
+ model: "anthropic/claude-sonnet-4-20250514"
16
+ },
17
+ structuredOutput: {
18
+ model: "openai/gpt-4.1-mini-2025-04-14"
19
+ },
20
+ summarizer: {
21
+ model: "openai/gpt-4.1-nano-2025-04-14"
22
+ }
23
+ };
24
+ var defaultOpenaiModelConfigurations = {
25
+ base: {
26
+ model: "openai/gpt-5-2025-08-07"
27
+ },
28
+ structuredOutput: {
29
+ model: "openai/gpt-4.1-mini-2025-04-14"
30
+ },
31
+ summarizer: {
32
+ model: "openai/gpt-4.1-nano-2025-04-14"
33
+ }
34
+ };
35
+ var defaultAnthropicModelConfigurations = {
36
+ base: {
37
+ model: "anthropic/claude-sonnet-4-20250514"
38
+ },
39
+ structuredOutput: {
40
+ model: "anthropic/claude-sonnet-4-20250514"
41
+ },
42
+ summarizer: {
43
+ model: "anthropic/claude-sonnet-4-20250514"
44
+ }
45
+ };
46
+
47
+ // src/commands/create.ts
48
+ var execAsync = promisify(exec);
49
+ var createAgents = async (args = {}) => {
50
+ let { tenantId, projectId, dirName, openAiKey, anthropicKey, manageApiPort, runApiPort } = args;
51
+ p.intro(color.inverse(" Create Agents Directory "));
52
+ if (!dirName) {
53
+ const dirResponse = await p.text({
54
+ message: "What do you want to name your agents directory?",
55
+ placeholder: "agents",
56
+ defaultValue: "agents",
57
+ validate: (value) => {
58
+ if (!value || value.trim() === "") {
59
+ return "Directory name is required";
60
+ }
61
+ return void 0;
62
+ }
63
+ });
64
+ if (p.isCancel(dirResponse)) {
65
+ p.cancel("Operation cancelled");
66
+ process.exit(0);
67
+ }
68
+ dirName = dirResponse;
69
+ }
70
+ if (!tenantId) {
71
+ const tenantIdResponse = await p.text({
72
+ message: "Enter your tenant ID :",
73
+ placeholder: "(default)",
74
+ defaultValue: "default"
75
+ });
76
+ if (p.isCancel(tenantIdResponse)) {
77
+ p.cancel("Operation cancelled");
78
+ process.exit(0);
79
+ }
80
+ tenantId = tenantIdResponse;
81
+ }
82
+ if (!projectId) {
83
+ const projectIdResponse = await p.text({
84
+ message: "Enter your project ID:",
85
+ placeholder: "(default)",
86
+ defaultValue: "default"
87
+ });
88
+ if (p.isCancel(projectIdResponse)) {
89
+ p.cancel("Operation cancelled");
90
+ process.exit(0);
91
+ }
92
+ projectId = projectIdResponse;
93
+ }
94
+ if (!anthropicKey && !openAiKey) {
95
+ const providerChoice = await p.select({
96
+ message: "Which AI provider(s) would you like to use?",
97
+ options: [
98
+ { value: "both", label: "Both Anthropic and OpenAI (recommended)" },
99
+ { value: "anthropic", label: "Anthropic only" },
100
+ { value: "openai", label: "OpenAI only" }
101
+ ]
102
+ });
103
+ if (p.isCancel(providerChoice)) {
104
+ p.cancel("Operation cancelled");
105
+ process.exit(0);
106
+ }
107
+ if (providerChoice === "anthropic" || providerChoice === "both") {
108
+ const anthropicKeyResponse = await p.text({
109
+ message: "Enter your Anthropic API key:",
110
+ placeholder: "sk-ant-...",
111
+ validate: (value) => {
112
+ if (!value || value.trim() === "") {
113
+ return "Anthropic API key is required";
114
+ }
115
+ return void 0;
116
+ }
117
+ });
118
+ if (p.isCancel(anthropicKeyResponse)) {
119
+ p.cancel("Operation cancelled");
120
+ process.exit(0);
121
+ }
122
+ anthropicKey = anthropicKeyResponse;
123
+ }
124
+ if (providerChoice === "openai" || providerChoice === "both") {
125
+ const openAiKeyResponse = await p.text({
126
+ message: "Enter your OpenAI API key:",
127
+ placeholder: "sk-...",
128
+ validate: (value) => {
129
+ if (!value || value.trim() === "") {
130
+ return "OpenAI API key is required";
131
+ }
132
+ return void 0;
133
+ }
134
+ });
135
+ if (p.isCancel(openAiKeyResponse)) {
136
+ p.cancel("Operation cancelled");
137
+ process.exit(0);
138
+ }
139
+ openAiKey = openAiKeyResponse;
140
+ }
141
+ } else {
142
+ if (!anthropicKey) {
143
+ const anthropicKeyResponse = await p.text({
144
+ message: "Enter your Anthropic API key (optional):",
145
+ placeholder: "sk-ant-...",
146
+ defaultValue: ""
147
+ });
148
+ if (p.isCancel(anthropicKeyResponse)) {
149
+ p.cancel("Operation cancelled");
150
+ process.exit(0);
151
+ }
152
+ anthropicKey = anthropicKeyResponse || void 0;
153
+ }
154
+ if (!openAiKey) {
155
+ const openAiKeyResponse = await p.text({
156
+ message: "Enter your OpenAI API key (optional):",
157
+ placeholder: "sk-...",
158
+ defaultValue: ""
159
+ });
160
+ if (p.isCancel(openAiKeyResponse)) {
161
+ p.cancel("Operation cancelled");
162
+ process.exit(0);
163
+ }
164
+ openAiKey = openAiKeyResponse || void 0;
165
+ }
166
+ }
167
+ let defaultModelSettings = {};
168
+ if (anthropicKey && openAiKey) {
169
+ defaultModelSettings = defaultDualModelConfigurations;
170
+ } else if (anthropicKey) {
171
+ defaultModelSettings = defaultAnthropicModelConfigurations;
172
+ } else if (openAiKey) {
173
+ defaultModelSettings = defaultOpenaiModelConfigurations;
174
+ }
175
+ const s = p.spinner();
176
+ s.start("Creating directory structure...");
177
+ try {
178
+ const directoryPath = path.resolve(process.cwd(), dirName);
179
+ if (await fs.pathExists(directoryPath)) {
180
+ s.stop();
181
+ const overwrite = await p.confirm({
182
+ message: `Directory ${dirName} already exists. Do you want to overwrite it?`
183
+ });
184
+ if (p.isCancel(overwrite) || !overwrite) {
185
+ p.cancel("Operation cancelled");
186
+ process.exit(0);
187
+ }
188
+ s.start("Cleaning existing directory...");
189
+ await fs.emptyDir(directoryPath);
190
+ }
191
+ await fs.ensureDir(directoryPath);
192
+ process.chdir(directoryPath);
193
+ const config = {
194
+ dirName,
195
+ tenantId,
196
+ projectId,
197
+ openAiKey,
198
+ anthropicKey,
199
+ manageApiPort: manageApiPort || "3002",
200
+ runApiPort: runApiPort || "3003",
201
+ modelSettings: defaultModelSettings
202
+ };
203
+ s.message("Setting up workspace structure...");
204
+ await createWorkspaceStructure(projectId);
205
+ s.message("Creating package configurations...");
206
+ await setupPackageConfigurations(dirName);
207
+ s.message("Setting up environment files...");
208
+ await createEnvironmentFiles(config);
209
+ s.message("Creating service files...");
210
+ await createServiceFiles(config);
211
+ s.message("Creating documentation...");
212
+ await createDocumentation(config);
213
+ s.message("Setting up Turbo...");
214
+ await createTurboConfig();
215
+ s.message("Installing dependencies (this may take a while)...");
216
+ await installDependencies();
217
+ s.message("Setting up database...");
218
+ await setupDatabase();
219
+ s.stop();
220
+ p.note(
221
+ `${color.green("\u2713")} Project created at: ${color.cyan(directoryPath)}
222
+
223
+ ${color.yellow("Next steps:")}
224
+ cd ${dirName}
225
+ pnpm run dev (for APIs only)
226
+ inkeep dev (for APIs + Management Dashboard)
227
+
228
+ ${color.yellow("Available services:")}
229
+ \u2022 Management API: http://localhost:${manageApiPort || "3002"}
230
+ \u2022 Execution API: http://localhost:${runApiPort || "3003"}
231
+ \u2022 Management Dashboard: Available with 'inkeep dev'
232
+
233
+ ${color.yellow("Configuration:")}
234
+ \u2022 Edit .env for environment variables
235
+ \u2022 Edit src/${projectId}/weather.graph.ts for agent definitions
236
+ \u2022 Use 'inkeep push' to deploy agents to the platform
237
+ \u2022 Use 'inkeep chat' to test your agents locally
238
+ `,
239
+ "Ready to go!"
240
+ );
241
+ } catch (error) {
242
+ s.stop();
243
+ p.cancel(
244
+ `Error creating directory: ${error instanceof Error ? error.message : "Unknown error"}`
245
+ );
246
+ process.exit(1);
247
+ }
248
+ };
249
+ async function createWorkspaceStructure(projectId) {
250
+ await fs.ensureDir(`src/${projectId}`);
251
+ await fs.ensureDir("apps/manage-api/src");
252
+ await fs.ensureDir("apps/run-api/src");
253
+ await fs.ensureDir("apps/shared");
254
+ }
255
+ async function setupPackageConfigurations(dirName) {
256
+ const rootPackageJson = {
257
+ name: dirName,
258
+ version: "0.1.0",
259
+ description: "An Inkeep Agent Framework directory",
260
+ private: true,
261
+ type: "module",
262
+ scripts: {
263
+ dev: "turbo dev",
264
+ "db:push": "drizzle-kit push"
265
+ },
266
+ dependencies: {},
267
+ devDependencies: {
268
+ "@biomejs/biome": "^1.8.0",
269
+ "@inkeep/agents-cli": "^0.1.1",
270
+ "drizzle-kit": "^0.31.4",
271
+ tsx: "^4.19.0",
272
+ turbo: "^2.5.5"
273
+ },
274
+ engines: {
275
+ node: ">=22.x"
276
+ },
277
+ packageManager: "pnpm@10.10.0"
278
+ };
279
+ await fs.writeJson("package.json", rootPackageJson, { spaces: 2 });
280
+ const pnpmWorkspace = `packages:
281
+ - "apps/*"
282
+ `;
283
+ await fs.writeFile("pnpm-workspace.yaml", pnpmWorkspace);
284
+ rootPackageJson.dependencies = {
285
+ "@inkeep/agents-core": "^0.1.0",
286
+ "@inkeep/agents-sdk": "^0.1.0",
287
+ zod: "^4.1.5"
288
+ };
289
+ await fs.writeJson("package.json", rootPackageJson, { spaces: 2 });
290
+ const manageApiPackageJson = {
291
+ name: `@${dirName}/manage-api`,
292
+ version: "0.1.0",
293
+ description: "Management API for agents",
294
+ type: "module",
295
+ scripts: {
296
+ build: "tsc",
297
+ dev: "tsx watch src/index.ts",
298
+ start: "node dist/index.js"
299
+ },
300
+ dependencies: {
301
+ "@inkeep/agents-manage-api": "^0.1.1",
302
+ "@inkeep/agents-core": "^0.1.0",
303
+ "@hono/node-server": "^1.14.3"
304
+ },
305
+ devDependencies: {
306
+ "@types/node": "^20.12.0",
307
+ tsx: "^4.19.0",
308
+ typescript: "^5.4.0"
309
+ },
310
+ engines: {
311
+ node: ">=22.x"
312
+ }
313
+ };
314
+ await fs.writeJson("apps/manage-api/package.json", manageApiPackageJson, { spaces: 2 });
315
+ const runApiPackageJson = {
316
+ name: `@${dirName}/run-api`,
317
+ version: "0.1.0",
318
+ description: "Run API for agents",
319
+ type: "module",
320
+ scripts: {
321
+ dev: "tsx watch src/index.ts",
322
+ start: "node dist/index.js"
323
+ },
324
+ dependencies: {
325
+ "@inkeep/agents-run-api": "^0.1.1",
326
+ "@inkeep/agents-core": "^0.1.0",
327
+ "@hono/node-server": "^1.14.3"
328
+ },
329
+ devDependencies: {
330
+ "@types/node": "^20.12.0",
331
+ tsx: "^4.19.0",
332
+ typescript: "^5.4.0"
333
+ },
334
+ engines: {
335
+ node: ">=22.x"
336
+ }
337
+ };
338
+ await fs.writeJson("apps/run-api/package.json", runApiPackageJson, { spaces: 2 });
339
+ const apiTsConfig = {
340
+ compilerOptions: {
341
+ target: "ES2022",
342
+ module: "ESNext",
343
+ moduleResolution: "bundler",
344
+ strict: true,
345
+ esModuleInterop: true,
346
+ skipLibCheck: true,
347
+ forceConsistentCasingInFileNames: true,
348
+ declaration: true,
349
+ outDir: "./dist",
350
+ rootDir: "..",
351
+ allowImportingTsExtensions: false,
352
+ resolveJsonModule: true,
353
+ isolatedModules: true,
354
+ noEmit: false
355
+ },
356
+ include: ["src/**/*", "../shared/**/*"],
357
+ exclude: ["node_modules", "dist", "**/*.test.ts"]
358
+ };
359
+ await fs.writeJson("apps/manage-api/tsconfig.json", apiTsConfig, { spaces: 2 });
360
+ await fs.writeJson("apps/run-api/tsconfig.json", apiTsConfig, { spaces: 2 });
361
+ }
362
+ async function createEnvironmentFiles(config) {
363
+ const envContent = `# Environment
364
+ ENVIRONMENT=development
365
+
366
+ # Database
367
+ DB_FILE_NAME=file:./local.db
368
+
369
+ # AI Provider Keys
370
+ ANTHROPIC_API_KEY=${config.anthropicKey || "your-anthropic-key-here"}
371
+ OPENAI_API_KEY=${config.openAiKey || "your-openai-key-here"}
372
+
373
+ # Logging
374
+ LOG_LEVEL=debug
375
+
376
+ # Service Ports
377
+ MANAGE_API_PORT=${config.manageApiPort}
378
+ RUN_API_PORT=${config.runApiPort}
379
+
380
+ # UI Configuration (for dashboard)
381
+ NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL=http://localhost:${config.manageApiPort}
382
+ NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL=http://localhost:${config.runApiPort}
383
+ `;
384
+ await fs.writeFile(".env", envContent);
385
+ const envExample = envContent.replace(/=.+$/gm, "=");
386
+ await fs.writeFile(".env.example", envExample);
387
+ const runApiEnvContent = `# Environment
388
+ ENVIRONMENT=development
389
+
390
+ # Database (relative path from API directory)
391
+ DB_FILE_NAME=file:../../local.db
392
+
393
+ # AI Provider Keys
394
+ ANTHROPIC_API_KEY=${config.anthropicKey || "your-anthropic-key-here"}
395
+ OPENAI_API_KEY=${config.openAiKey || "your-openai-key-here"}
396
+ `;
397
+ const manageApiEnvContent = `# Environment
398
+ ENVIRONMENT=development
399
+
400
+ # Database (relative path from API directory)
401
+ DB_FILE_NAME=file:../../local.db
402
+ `;
403
+ await fs.writeFile("apps/manage-api/.env", manageApiEnvContent);
404
+ await fs.writeFile("apps/run-api/.env", runApiEnvContent);
405
+ const gitignore = `# Dependencies
406
+ node_modules/
407
+ .pnpm-store/
408
+
409
+ # Environment variables
410
+ .env
411
+ .env.local
412
+ .env.development.local
413
+ .env.test.local
414
+ .env.production.local
415
+
416
+ # Build outputs
417
+ dist/
418
+ build/
419
+ .next/
420
+ .turbo/
421
+
422
+ # Logs
423
+ *.log
424
+ logs/
425
+
426
+ # Database
427
+ *.db
428
+ *.sqlite
429
+ *.sqlite3
430
+
431
+ # IDE
432
+ .vscode/
433
+ .idea/
434
+ *.swp
435
+ *.swo
436
+
437
+ # OS
438
+ .DS_Store
439
+ Thumbs.db
440
+
441
+ # Coverage
442
+ coverage/
443
+ .nyc_output/
444
+
445
+ # Temporary files
446
+ *.tmp
447
+ *.temp
448
+ .cache/
449
+
450
+ # Runtime data
451
+ pids/
452
+ *.pid
453
+ *.seed
454
+ *.pid.lock
455
+ `;
456
+ await fs.writeFile(".gitignore", gitignore);
457
+ const biomeConfig = {
458
+ linter: {
459
+ enabled: true,
460
+ rules: {
461
+ recommended: true
462
+ }
463
+ },
464
+ formatter: {
465
+ enabled: true,
466
+ indentStyle: "space",
467
+ indentWidth: 2
468
+ },
469
+ organizeImports: {
470
+ enabled: true
471
+ },
472
+ javascript: {
473
+ formatter: {
474
+ semicolons: "always",
475
+ quoteStyle: "single"
476
+ }
477
+ }
478
+ };
479
+ await fs.writeJson("biome.json", biomeConfig, { spaces: 2 });
480
+ }
481
+ async function createServiceFiles(config) {
482
+ const agentsGraph = `import { agent, agentGraph, mcpTool } from '@inkeep/agents-sdk';
483
+
484
+ // MCP Tools
485
+ const forecastWeatherTool = mcpTool({
486
+ id: 'fUI2riwrBVJ6MepT8rjx0',
487
+ name: 'Forecast weather',
488
+ serverUrl: 'https://weather-forecast-mcp.vercel.app/mcp',
489
+ });
490
+
491
+ const geocodeAddressTool = mcpTool({
492
+ id: 'fdxgfv9HL7SXlfynPx8hf',
493
+ name: 'Geocode address',
494
+ serverUrl: 'https://geocoder-mcp.vercel.app/mcp',
495
+ });
496
+
497
+ // Agents
498
+ const weatherAssistant = agent({
499
+ id: 'weather-assistant',
500
+ name: 'Weather assistant',
501
+ description: 'Responsible for routing between the geocoder agent and weather forecast agent',
502
+ prompt:
503
+ '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',
504
+ canDelegateTo: () => [weatherForecaster, geocoderAgent],
505
+ });
506
+
507
+ const weatherForecaster = agent({
508
+ id: 'weather-forecaster',
509
+ name: 'Weather forecaster',
510
+ description:
511
+ 'This agent is responsible for taking in coordinates and returning the forecast for the weather at that location',
512
+ prompt:
513
+ 'You are a helpful assistant responsible for taking in coordinates and returning the forecast for that location using your forecasting tool',
514
+ canUse: () => [forecastWeatherTool],
515
+ });
516
+
517
+ const geocoderAgent = agent({
518
+ id: 'geocoder-agent',
519
+ name: 'Geocoder agent',
520
+ description: 'Responsible for converting location or address into coordinates',
521
+ prompt:
522
+ 'You are a helpful assistant responsible for converting location or address into coordinates using your geocode tool',
523
+ canUse: () => [geocodeAddressTool],
524
+ });
525
+
526
+ // Agent Graph
527
+ export const weatherGraph = agentGraph({
528
+ id: 'weather-graph',
529
+ name: 'Weather graph',
530
+ defaultAgent: weatherAssistant,
531
+ agents: () => [weatherAssistant, weatherForecaster, geocoderAgent],
532
+ });`;
533
+ await fs.writeFile(`src/${config.projectId}/weather.graph.ts`, agentsGraph);
534
+ const inkeepConfig = `import { defineConfig } from '@inkeep/agents-cli/config';
535
+
536
+ const config = defineConfig({
537
+ tenantId: "${config.tenantId}",
538
+ projectId: "${config.projectId}",
539
+ agentsManageApiUrl: \`http://localhost:\${process.env.MANAGE_API_PORT || '3002'}\`,
540
+ agentsRunApiUrl: \`http://localhost:\${process.env.RUN_API_PORT || '3003'}\`,
541
+ modelSettings: ${JSON.stringify(config.modelSettings, null, 2)},
542
+ });
543
+
544
+ export default config;`;
545
+ await fs.writeFile(`src/${config.projectId}/inkeep.config.ts`, inkeepConfig);
546
+ const projectEnvContent = `# Environment
547
+ ENVIRONMENT=development
548
+
549
+ # Database (relative path from project directory)
550
+ DB_FILE_NAME=file:../../local.db
551
+
552
+ # UI Configuration (for dashboard)
553
+ NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL=http://localhost:${config.manageApiPort}
554
+ NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL=http://localhost:${config.runApiPort}
555
+
556
+ `;
557
+ await fs.writeFile(`src/${config.projectId}/.env`, projectEnvContent);
558
+ const credentialStoresFile = `import {
559
+ InMemoryCredentialStore,
560
+ createNangoCredentialStore,
561
+ createKeyChainStore,
562
+ } from '@inkeep/agents-core';
563
+
564
+ // Shared credential stores configuration for all services
565
+ export const credentialStores = [
566
+ new InMemoryCredentialStore('memory-default'),
567
+ ...(process.env.NANGO_SECRET_KEY
568
+ ? [
569
+ createNangoCredentialStore('nango-default', {
570
+ apiUrl: process.env.NANGO_HOST || 'https://api.nango.dev',
571
+ secretKey: process.env.NANGO_SECRET_KEY,
572
+ }),
573
+ ]
574
+ : []),
575
+ createKeyChainStore('keychain-default'),
576
+ ];
577
+ `;
578
+ await fs.writeFile("apps/shared/credential-stores.ts", credentialStoresFile);
579
+ const manageApiIndex = `import { serve } from '@hono/node-server';
580
+ import { createManagementApp } from '@inkeep/agents-manage-api';
581
+ import { getLogger } from '@inkeep/agents-core';
582
+ import { credentialStores } from '../../shared/credential-stores.js';
583
+
584
+ const logger = getLogger('management-api');
585
+
586
+ // Create the Hono app
587
+ const app = createManagementApp({
588
+ serverConfig: {
589
+ port: Number(process.env.MANAGE_API_PORT) || 3002,
590
+ serverOptions: {
591
+ requestTimeout: 60000,
592
+ keepAliveTimeout: 60000,
593
+ keepAlive: true,
594
+ },
595
+ },
596
+ credentialStores,
597
+ });
598
+
599
+ const port = Number(process.env.MANAGE_API_PORT) || 3002;
600
+
601
+ // Start the server using @hono/node-server
602
+ serve(
603
+ {
604
+ fetch: app.fetch,
605
+ port,
606
+ },
607
+ (info) => {
608
+ logger.info({}, \`\u{1F4DD} Management API running on http://localhost:\${info.port}\`);
609
+ logger.info({}, \`\u{1F4DD} OpenAPI documentation available at http://localhost:\${info.port}/openapi.json\`);
610
+ }
611
+ );`;
612
+ await fs.writeFile("apps/manage-api/src/index.ts", manageApiIndex);
613
+ const runApiIndex = `import { serve } from '@hono/node-server';
614
+ import { createExecutionApp } from '@inkeep/agents-run-api';
615
+ import { credentialStores } from '../../shared/credential-stores.js';
616
+ import { getLogger } from '@inkeep/agents-core';
617
+
618
+ const logger = getLogger('execution-api');
619
+
620
+
621
+ // Create the Hono app
622
+ const app = createExecutionApp({
623
+ serverConfig: {
624
+ port: Number(process.env.RUN_API_PORT) || 3003,
625
+ serverOptions: {
626
+ requestTimeout: 120000,
627
+ keepAliveTimeout: 60000,
628
+ keepAlive: true,
629
+ },
630
+ },
631
+ credentialStores,
632
+ });
633
+
634
+ const port = Number(process.env.RUN_API_PORT) || 3003;
635
+
636
+ // Start the server using @hono/node-server
637
+ serve(
638
+ {
639
+ fetch: app.fetch,
640
+ port,
641
+ },
642
+ (info) => {
643
+ logger.info({}, \`\u{1F4DD} Execution API running on http://localhost:\${info.port}\`);
644
+ logger.info({}, \`\u{1F4DD} OpenAPI documentation available at http://localhost:\${info.port}/openapi.json\`);
645
+ }
646
+ );`;
647
+ await fs.writeFile("apps/run-api/src/index.ts", runApiIndex);
648
+ const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
649
+
650
+ export default defineConfig({
651
+ schema: 'node_modules/@inkeep/agents-core/dist/db/schema.js',
652
+ dialect: 'sqlite',
653
+ dbCredentials: {
654
+ url: process.env.DB_FILE_NAME || 'file:./local.db'
655
+ },
656
+ });`;
657
+ await fs.writeFile("drizzle.config.ts", drizzleConfig);
658
+ }
659
+ async function createTurboConfig() {
660
+ const turboConfig = {
661
+ $schema: "https://turbo.build/schema.json",
662
+ ui: "tui",
663
+ globalDependencies: ["**/.env", "**/.env.local", "**/.env.*"],
664
+ globalEnv: [
665
+ "NODE_ENV",
666
+ "CI",
667
+ "ANTHROPIC_API_KEY",
668
+ "OPENAI_API_KEY",
669
+ "ENVIRONMENT",
670
+ "DB_FILE_NAME",
671
+ "MANAGE_API_PORT",
672
+ "RUN_API_PORT",
673
+ "LOG_LEVEL",
674
+ "NANGO_SECRET_KEY"
675
+ ],
676
+ tasks: {
677
+ build: {
678
+ dependsOn: ["^build"],
679
+ inputs: ["$TURBO_DEFAULT$", ".env*"],
680
+ outputs: ["dist/**", "build/**", ".next/**", "!.next/cache/**"]
681
+ },
682
+ dev: {
683
+ cache: false,
684
+ persistent: true
685
+ },
686
+ start: {
687
+ dependsOn: ["build"],
688
+ cache: false
689
+ },
690
+ "db:push": {
691
+ cache: false,
692
+ inputs: ["drizzle.config.ts", "src/data/db/schema.ts"]
693
+ }
694
+ }
695
+ };
696
+ await fs.writeJson("turbo.json", turboConfig, { spaces: 2 });
697
+ }
698
+ async function createDocumentation(config) {
699
+ const readme = `# ${config.dirName}
700
+
701
+ An Inkeep Agent Framework project with multi-service architecture.
702
+
703
+ ## Architecture
704
+
705
+ This project follows a workspace structure with the following services:
706
+
707
+ - **Agents Management API** (Port 3002): Agent configuration and managemen
708
+ - Handles entity management and configuration endpoints.
709
+ - **Agents Run API** (Port 3003): Agent execution and chat processing
710
+ - Handles agent communication. You can interact with your agents either over MCP from an MCP client or through our React UI components library
711
+ - **Management Dashboard** (Port 3000): Web interface available via \`inkeep dev\`
712
+ - The agent framework visual builder. From the builder you can create, manage and visualize all your graphs.
713
+
714
+ ## Quick Start
715
+ 1. **Install the Inkeep CLI:**
716
+ \`\`\`bash
717
+ pnpm install -g @inkeep/agents-cli
718
+ \`\`\`
719
+
720
+ 1. **Start services:**
721
+ \`\`\`bash
722
+ # Start Agents Management API and Agents Run API
723
+ pnpm run dev
724
+
725
+ # Start Dashboard
726
+ inkeep dev
727
+ \`\`\`
728
+
729
+ 3. **Deploy your first agent graph:**
730
+ \`\`\`bash
731
+ # Navigate to your project's graph directory
732
+ cd src/${config.projectId}/
733
+
734
+ # Push the weather graph to create it
735
+ inkeep push weather.graph.ts
736
+ \`\`\`
737
+ - Follow the prompts to create the project and graph
738
+ - Click on the "View graph in UI:" link to see the graph in the management dashboard
739
+
740
+ ## Project Structure
741
+
742
+ \`\`\`
743
+ ${config.dirName}/
744
+ \u251C\u2500\u2500 src/
745
+ \u2502 \u251C\u2500\u2500 /${config.projectId} # Agent configurations
746
+ \u251C\u2500\u2500 apps/
747
+ \u2502 \u251C\u2500\u2500 manage-api/ # Agents Management API service
748
+ \u2502 \u251C\u2500\u2500 run-api/ # Agents Run API service
749
+ \u2502 \u2514\u2500\u2500 shared/ # Shared code between API services
750
+ \u2502 \u2514\u2500\u2500 credential-stores.ts # Shared credential store configuration
751
+ \u251C\u2500\u2500 turbo.json # Turbo configuration
752
+ \u251C\u2500\u2500 pnpm-workspace.yaml # pnpm workspace configuration
753
+ \u2514\u2500\u2500 package.json # Root package configuration
754
+ \`\`\`
755
+
756
+ ## Configuration
757
+
758
+ ### Environment Variables
759
+
760
+ Environment variables are defined in the following places:
761
+
762
+ - \`apps/manage-api/.env\`: Agents Management API environment variables
763
+ - \`apps/run-api/.env\`: Agents Run API environment variables
764
+ - \`src/${config.projectId}/.env\`: Inkeep CLI environment variables
765
+ - \`.env\`: Root environment variables
766
+
767
+ 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.
768
+
769
+ \`\`\`bash
770
+ # AI Provider Keys
771
+ ANTHROPIC_API_KEY=your-anthropic-key-here
772
+ OPENAI_API_KEY=your-openai-key-here
773
+ \`\`\`
774
+
775
+ To change the ports used by your services modify \`apps/manage-api/.env\` and \`apps/run-api/.env\` respectively:
776
+
777
+ \`\`\`bash
778
+ # Service port for apps/run-api
779
+ RUN_API_PORT=3003
780
+
781
+ # Service port for apps/manage-api
782
+ MANAGE_API_PORT
783
+ \`\`\`
784
+
785
+ After changing the API Service ports make sure that you modify the dashboard API urls from whichever directory you are running \`inkeep dev\`:
786
+
787
+ \`\`\`bash
788
+ # UI Configuration (for dashboard)
789
+ NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL=http://localhost:${config.manageApiPort}
790
+ NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL=http://localhost:${config.runApiPort}
791
+ \`\`\`
792
+
793
+ ### Agent Configuration
794
+
795
+ Your graphs are defined in \`src/${config.projectId}/weather.graph.ts\`. The default setup includes:
796
+
797
+ - **Weather Graph**: A graph that can forecast the weather in a given location.
798
+
799
+ 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:
800
+
801
+ - \`tenantId\`: The tenant ID
802
+ - \`projectId\`: The project ID
803
+ - \`agentsManageApiUrl\`: The management API URL
804
+ - \`agentsRunApiUrl\`: The execution API URL
805
+
806
+
807
+ ## Development
808
+
809
+ ### Updating Your Agents
810
+
811
+ 1. Edit \`src/${config.projectId}/weather.graph.ts\`
812
+ 2. Push the graph to the platform to update: \`inkeep pus weather.graph.ts\`
813
+
814
+ ### API Documentation
815
+
816
+ Once services are running, view the OpenAPI documentation:
817
+
818
+ - Management API: http://localhost:${config.manageApiPort}/docs
819
+ - Execution API: http://localhost:${config.runApiPort}/docs
820
+
821
+ ## Learn More
822
+
823
+ - [Inkeep Documentation](https://docs.inkeep.com)
824
+
825
+ ## Troubleshooting
826
+
827
+ ## Inkeep CLI commands
828
+
829
+ - Ensure you are runnning commands from \`cd src/${config.projectId}\`.
830
+ - Validate the \`inkeep.config.ts\` file has the correct api urls.
831
+ - Validate that the \`.env\` file in \`src/${config.projectId}\` has the correct \`DB_FILE_NAME\`.
832
+
833
+ ### Services won't start
834
+
835
+ 1. Ensure all dependencies are installed: \`pnpm install\`
836
+ 2. Check that ports 3000-3003 are available
837
+
838
+ ### Agents won't respond
839
+
840
+ 1. Ensure that the Agents Run API is running and includes a valid Anthropic or OpenAI API key in its .env file
841
+ `;
842
+ await fs.writeFile("README.md", readme);
843
+ }
844
+ async function installDependencies() {
845
+ await execAsync("pnpm install");
846
+ }
847
+ async function setupDatabase() {
848
+ try {
849
+ await execAsync("pnpm db:push");
850
+ } catch (error) {
851
+ throw new Error(
852
+ `Failed to setup database: ${error instanceof Error ? error.message : "Unknown error"}`
853
+ );
854
+ }
855
+ }
856
+ async function createCommand(dirName, options) {
857
+ await createAgents({
858
+ dirName,
859
+ ...options
860
+ });
861
+ }
862
+ export {
863
+ createAgents,
864
+ createCommand
865
+ };