@inkeep/create-agents 0.0.0-dev-20260224090320 → 0.0.0-dev-20260224144914

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.
@@ -62,13 +62,15 @@ describe('create-agents quickstart e2e', () => {
62
62
  ]);
63
63
  console.log('Directory structure verified');
64
64
  // Verify .env file has required variables
65
+ // After createEnvironmentFiles(), .env is a copy of .env.example with CLI-prompted
66
+ // values injected. Secrets (JWT keys, signing secret, etc.) remain as placeholders
67
+ // until setup-dev runs generateSecrets().
65
68
  console.log('Verifying .env file...');
66
69
  await verifyFile(path.join(projectDir, '.env'), [
67
70
  /ENVIRONMENT=development/,
68
71
  /INKEEP_AGENTS_MANAGE_DATABASE_URL=postgresql:\/\/appuser:password@localhost:5432\/inkeep_agents/,
69
72
  /INKEEP_AGENTS_RUN_DATABASE_URL=postgresql:\/\/appuser:password@localhost:5433\/inkeep_agents/,
70
- /INKEEP_AGENTS_API_URL="http:\/\/127\.0\.0\.1:3002"/,
71
- /INKEEP_AGENTS_JWT_SIGNING_SECRET=\w+/,
73
+ /INKEEP_AGENTS_API_URL=http:\/\/localhost:3002/,
72
74
  ]);
73
75
  console.log('.env file verified');
74
76
  // Verify inkeep.config.ts was created
@@ -167,7 +169,7 @@ describe('create-agents quickstart e2e', () => {
167
169
  try {
168
170
  const signupRes = await fetch(`${manageApiUrl}/api/auth/sign-up/email`, {
169
171
  method: 'POST',
170
- headers: { 'Content-Type': 'application/json', Origin: manageApiUrl },
172
+ headers: { 'Content-Type': 'application/json', Origin: dashboardApiUrl },
171
173
  body: JSON.stringify({
172
174
  email: 'admin@example.com',
173
175
  password: 'adminADMIN!@12',
@@ -211,7 +213,7 @@ describe('create-agents quickstart e2e', () => {
211
213
  try {
212
214
  const loginTestRes = await fetch(`${manageApiUrl}/api/auth/sign-in/email`, {
213
215
  method: 'POST',
214
- headers: { 'Content-Type': 'application/json', Origin: manageApiUrl },
216
+ headers: { 'Content-Type': 'application/json', Origin: dashboardApiUrl },
215
217
  body: JSON.stringify({
216
218
  email: 'admin@example.com',
217
219
  password: 'adminADMIN!@12',
@@ -222,11 +222,15 @@ export async function waitForServerReady(url, timeout) {
222
222
  }
223
223
  export async function startDashboardServer(projectDir, env = {}) {
224
224
  const manageUiPkgJson = path.join(projectDir, 'node_modules/@inkeep/agents-manage-ui/package.json');
225
- const manageUiRoot = path.dirname(manageUiPkgJson);
225
+ // Resolve symlinks so linked packages (link:) point to the actual monorepo directory
226
+ const manageUiRoot = await fs.realpath(path.dirname(manageUiPkgJson));
226
227
  const standaloneDir = path.join(manageUiRoot, '.next/standalone/agents-manage-ui');
227
228
  const serverEntry = path.join(standaloneDir, 'server.js');
228
229
  if (!(await fs.pathExists(serverEntry))) {
229
- throw new Error(`Dashboard standalone server not found at ${serverEntry}`);
230
+ const originalPath = path.dirname(manageUiPkgJson);
231
+ throw new Error(`Dashboard standalone server not found at ${serverEntry}` +
232
+ (originalPath !== manageUiRoot ? ` (symlink resolved from ${originalPath})` : '') +
233
+ `. Ensure the package is built with 'output: standalone' (run turbo build).`);
230
234
  }
231
235
  const child = fork(serverEntry, [], {
232
236
  cwd: standaloneDir,
@@ -29,6 +29,34 @@ const mockSpinner = {
29
29
  stop: vi.fn().mockReturnThis(),
30
30
  message: vi.fn().mockReturnThis(),
31
31
  };
32
+ const mockEnvExample = [
33
+ 'ENVIRONMENT=development',
34
+ 'NODE_ENV=development',
35
+ 'LOG_LEVEL=info',
36
+ 'INKEEP_AGENTS_MANAGE_DATABASE_URL=postgresql://appuser:password@localhost:5432/inkeep_agents',
37
+ 'INKEEP_AGENTS_RUN_DATABASE_URL=postgresql://appuser:password@localhost:5433/inkeep_agents',
38
+ 'INKEEP_AGENTS_API_URL=http://localhost:3002',
39
+ 'PUBLIC_INKEEP_AGENTS_API_URL=http://localhost:3002',
40
+ 'TENANT_ID=default',
41
+ 'ANTHROPIC_API_KEY=',
42
+ 'OPENAI_API_KEY=',
43
+ 'GOOGLE_GENERATIVE_AI_API_KEY=',
44
+ 'AZURE_API_KEY=',
45
+ 'DEFAULT_PROJECT_ID=',
46
+ 'NANGO_SECRET_KEY=',
47
+ 'NANGO_SERVER_URL=http://localhost:3050',
48
+ 'SIGNOZ_URL=http://localhost:3080',
49
+ 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:14318/v1/traces',
50
+ 'INKEEP_AGENTS_MANAGE_UI_USERNAME=admin@example.com',
51
+ 'INKEEP_AGENTS_MANAGE_UI_PASSWORD=adminADMIN!@12',
52
+ 'BETTER_AUTH_SECRET=your-secret-key-change-in-production',
53
+ 'SPICEDB_ENDPOINT=localhost:50051',
54
+ 'SPICEDB_PRESHARED_KEY=dev-secret-key',
55
+ 'INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET=test-bypass-secret-for-ci',
56
+ '# INKEEP_AGENTS_JWT_SIGNING_SECRET=',
57
+ '# INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=',
58
+ '# INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY=',
59
+ ].join('\n');
32
60
  describe('createAgents - Template and Project ID Logic', () => {
33
61
  let processExitSpy;
34
62
  let processChdirSpy;
@@ -64,6 +92,7 @@ describe('createAgents - Template and Project ID Logic', () => {
64
92
  vi.mocked(fs.writeFile).mockResolvedValue(undefined);
65
93
  vi.mocked(fs.writeJson).mockResolvedValue(undefined);
66
94
  vi.mocked(fs.readJson).mockResolvedValue({});
95
+ vi.mocked(fs.readFile).mockResolvedValue(mockEnvExample);
67
96
  vi.mocked(fs.mkdir).mockResolvedValue(undefined);
68
97
  vi.mocked(fs.remove).mockResolvedValue(undefined);
69
98
  // Mock templates
@@ -331,6 +360,49 @@ describe('createAgents - Template and Project ID Logic', () => {
331
360
  ]));
332
361
  });
333
362
  });
363
+ describe('Environment file generation', () => {
364
+ it('should contain INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET from .env.example', async () => {
365
+ await createAgents({
366
+ dirName: 'test-dir',
367
+ openAiKey: 'test-openai-key',
368
+ anthropicKey: 'test-anthropic-key',
369
+ });
370
+ expect(fs.writeFile).toHaveBeenCalledWith('.env', expect.stringContaining('INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET=test-bypass-secret-for-ci'));
371
+ });
372
+ it('should inject CLI-prompted API keys into the .env', async () => {
373
+ await createAgents({
374
+ dirName: 'test-dir',
375
+ openAiKey: 'sk-openai-123',
376
+ anthropicKey: 'sk-ant-456',
377
+ googleKey: 'google-789',
378
+ azureKey: 'azure-abc',
379
+ });
380
+ expect(fs.writeFile).toHaveBeenCalledWith('.env', expect.stringContaining('ANTHROPIC_API_KEY=sk-ant-456'));
381
+ expect(fs.writeFile).toHaveBeenCalledWith('.env', expect.stringContaining('OPENAI_API_KEY=sk-openai-123'));
382
+ expect(fs.writeFile).toHaveBeenCalledWith('.env', expect.stringContaining('GOOGLE_GENERATIVE_AI_API_KEY=google-789'));
383
+ expect(fs.writeFile).toHaveBeenCalledWith('.env', expect.stringContaining('AZURE_API_KEY=azure-abc'));
384
+ });
385
+ it('should use localhost URLs (not 127.0.0.1)', async () => {
386
+ await createAgents({
387
+ dirName: 'test-dir',
388
+ openAiKey: 'test-key',
389
+ anthropicKey: 'test-key',
390
+ });
391
+ expect(fs.writeFile).toHaveBeenCalledWith('.env', expect.stringContaining('PUBLIC_INKEEP_AGENTS_API_URL=http://localhost:3002'));
392
+ });
393
+ it('should not generate any secrets inline', async () => {
394
+ await createAgents({
395
+ dirName: 'test-dir',
396
+ openAiKey: 'test-key',
397
+ anthropicKey: 'test-key',
398
+ });
399
+ const envWriteCall = vi.mocked(fs.writeFile).mock.calls.find((call) => call[0] === '.env');
400
+ const envContent = envWriteCall?.[1];
401
+ expect(envContent).toContain('BETTER_AUTH_SECRET=your-secret-key-change-in-production');
402
+ expect(envContent).toContain('INKEEP_AGENTS_MANAGE_UI_PASSWORD=adminADMIN!@12');
403
+ expect(envContent).toContain('# INKEEP_AGENTS_JWT_SIGNING_SECRET=');
404
+ });
405
+ });
334
406
  describe('Security - Password input for API keys', () => {
335
407
  it('should use password input instead of text input for API keys', async () => {
336
408
  // Mock the select to return 'anthropic' to trigger the API key prompt
@@ -388,9 +460,9 @@ function setupDefaultMocks() {
388
460
  vi.mocked(fs.writeFile).mockResolvedValue(undefined);
389
461
  vi.mocked(fs.writeJson).mockResolvedValue(undefined);
390
462
  vi.mocked(fs.readJson).mockResolvedValue({});
463
+ vi.mocked(fs.readFile).mockResolvedValue(mockEnvExample);
391
464
  vi.mocked(getAvailableTemplates).mockResolvedValue(['event-planner', 'chatbot', 'data-analysis']);
392
465
  vi.mocked(cloneTemplate).mockResolvedValue(undefined);
393
466
  vi.mocked(cloneTemplateLocal).mockResolvedValue(undefined);
394
- // Reset mockExecAsync for tests that clear mocks
395
467
  mockExecAsync.mockResolvedValue({ stdout: '', stderr: '' });
396
468
  }
package/dist/utils.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { exec } from 'node:child_process';
2
- import crypto from 'node:crypto';
3
2
  import os from 'node:os';
4
3
  import path from 'node:path';
5
4
  import { promisify } from 'node:util';
@@ -397,8 +396,8 @@ export const createAgents = async (args = {}) => {
397
396
  ` pnpm setup-dev\n` +
398
397
  ` pnpm dev\n\n` +
399
398
  `${color.yellow('2. Explore:')}\n` +
400
- ` • Dashboard: http://127.0.0.1:3000\n` +
401
- ` • Agents API: http://127.0.0.1:3002\n\n` +
399
+ ` • Dashboard: http://localhost:3000\n` +
400
+ ` • Agents API: http://localhost:3002\n\n` +
402
401
  `${color.yellow('3. Customize:')}\n` +
403
402
  ` • Edit your agents in src/projects/\n` +
404
403
  ` • Use 'inkeep push' to apply`, 'Next steps 🚀');
@@ -413,80 +412,29 @@ async function createWorkspaceStructure() {
413
412
  await fs.ensureDir(`src`);
414
413
  }
415
414
  async function createEnvironmentFiles(config) {
416
- // Convert to forward slashes for cross-platform SQLite URI compatibility
417
- const jwtSigningSecret = crypto.randomBytes(32).toString('hex');
418
- const betterAuthSecret = crypto.randomBytes(32).toString('hex');
419
- const manageUiPassword = crypto.randomBytes(6).toString('base64url');
420
- // Generate RSA key pair for temporary JWT tokens
421
- let tempJwtPrivateKey = '';
422
- let tempJwtPublicKey = '';
415
+ let envExampleContent;
423
416
  try {
424
- const { generateKeyPairSync } = await import('node:crypto');
425
- const { privateKey, publicKey } = generateKeyPairSync('rsa', {
426
- modulusLength: 2048,
427
- publicKeyEncoding: { type: 'spki', format: 'pem' },
428
- privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
429
- });
430
- tempJwtPrivateKey = Buffer.from(privateKey).toString('base64');
431
- tempJwtPublicKey = Buffer.from(publicKey).toString('base64');
417
+ envExampleContent = await fs.readFile('.env.example', 'utf-8');
432
418
  }
433
419
  catch {
434
- console.warn('Warning: Failed to generate JWT keys. Playground may not work.');
435
- console.warn('You can manually generate keys later with: pnpm run generate-jwt-keys');
420
+ throw new Error('Could not read .env.example from the template. The template may be corrupted — try running the command again.');
436
421
  }
437
- const envContent = `# Environment
438
- ENVIRONMENT=development
439
-
440
- # Database Configuration (Split Database Setup)
441
- # Management entities database uses DoltgreSQL on port 5432 for version control features
442
- INKEEP_AGENTS_MANAGE_DATABASE_URL=postgresql://appuser:password@localhost:5432/inkeep_agents
443
- # Runtime entities database uses PostgreSQL on port 5433 for runtime operations
444
- INKEEP_AGENTS_RUN_DATABASE_URL=postgresql://appuser:password@localhost:5433/inkeep_agents
445
-
446
- # AI Provider Keys
447
- ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
448
- OPENAI_API_KEY=${config.openAiKey || 'your-openai-key-here'}
449
- GOOGLE_GENERATIVE_AI_API_KEY=${config.googleKey || 'your-google-key-here'}
450
- AZURE_API_KEY=${config.azureKey || 'your-azure-key-here'}
451
-
452
- # Inkeep API URLs
453
- # Internal URLs (server-side, Docker internal networking)
454
- # Using 127.0.0.1 instead of localhost to avoid IPv6/IPv4 resolution issues
455
- INKEEP_AGENTS_API_URL="http://127.0.0.1:3002"
456
-
457
- # Public URLs (client-side, browser accessible)
458
- PUBLIC_INKEEP_AGENTS_API_URL="http://127.0.0.1:3002"
459
-
460
- # SigNoz Configuration
461
- SIGNOZ_URL=your-signoz-url-here
462
- SIGNOZ_API_KEY=your-signoz-api-key-here
463
-
464
- # OTEL Configuration
465
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://ingest.us.signoz.cloud:443/v1/traces
466
- OTEL_EXPORTER_OTLP_TRACES_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
467
-
468
- # Nango Configuration
469
- NANGO_SECRET_KEY=
470
-
471
- # JWT Signing Secret
472
- INKEEP_AGENTS_JWT_SIGNING_SECRET=${jwtSigningSecret}
473
-
474
- # Temporary JWT Keys for Playground
475
- INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=${tempJwtPrivateKey}
476
- INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY=${tempJwtPublicKey}
477
-
478
- # initial project information
479
- DEFAULT_PROJECT_ID=${config.projectId}
480
-
481
- # Auth Configuration
482
- INKEEP_AGENTS_MANAGE_UI_USERNAME=admin@example.com
483
- INKEEP_AGENTS_MANAGE_UI_PASSWORD=${manageUiPassword}
484
- BETTER_AUTH_SECRET=${betterAuthSecret}
485
- SPICEDB_ENDPOINT=localhost:50051
486
- SPICEDB_PRESHARED_KEY=dev-secret-key
487
-
488
- `;
489
- await fs.writeFile('.env', envContent);
422
+ const lines = envExampleContent.split('\n');
423
+ const injections = {
424
+ ANTHROPIC_API_KEY: config.anthropicKey || '',
425
+ OPENAI_API_KEY: config.openAiKey || '',
426
+ GOOGLE_GENERATIVE_AI_API_KEY: config.googleKey || '',
427
+ AZURE_API_KEY: config.azureKey || '',
428
+ DEFAULT_PROJECT_ID: config.projectId,
429
+ };
430
+ for (let i = 0; i < lines.length; i++) {
431
+ for (const [varName, value] of Object.entries(injections)) {
432
+ if (lines[i].startsWith(`${varName}=`)) {
433
+ lines[i] = `${varName}=${value}`;
434
+ }
435
+ }
436
+ }
437
+ await fs.writeFile('.env', lines.join('\n'));
490
438
  }
491
439
  async function createInkeepConfig(config) {
492
440
  const inkeepConfig = `import { defineConfig } from '@inkeep/agents-cli/config';
@@ -494,8 +442,7 @@ async function createInkeepConfig(config) {
494
442
  const config = defineConfig({
495
443
  tenantId: "${config.tenantId}",
496
444
  agentsApi: {
497
- // Using 127.0.0.1 instead of localhost to avoid IPv6/IPv4 resolution issues
498
- url: 'http://127.0.0.1:3002',
445
+ url: 'http://localhost:3002',
499
446
  },
500
447
  });
501
448
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/create-agents",
3
- "version": "0.0.0-dev-20260224090320",
3
+ "version": "0.0.0-dev-20260224144914",
4
4
  "description": "Create an Inkeep Agent Framework project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,7 @@
33
33
  "degit": "^2.8.4",
34
34
  "fs-extra": "^11.0.0",
35
35
  "picocolors": "^1.0.0",
36
- "@inkeep/agents-core": "0.0.0-dev-20260224090320"
36
+ "@inkeep/agents-core": "0.0.0-dev-20260224144914"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/degit": "^2.8.6",
@@ -67,8 +67,8 @@
67
67
  "test": "vitest --run src/__tests__ --exclude src/__tests__/e2e/** --passWithNoTests",
68
68
  "test:watch": "vitest src/__tests__ --exclude src/__tests__/e2e/**",
69
69
  "test:unit": "vitest --run src/__tests__ --exclude src/__tests__/e2e/**",
70
- "test:e2e": "vitest --run src/__tests__/e2e",
71
- "test:e2e:watch": "vitest src/__tests__/e2e",
70
+ "test:e2e": "turbo run build --filter=@inkeep/create-agents --filter=@inkeep/agents-api --filter=@inkeep/agents-cli --filter=@inkeep/agents-manage-ui && vitest --run src/__tests__/e2e",
71
+ "test:e2e:watch": "turbo run build --filter=@inkeep/create-agents --filter=@inkeep/agents-api --filter=@inkeep/agents-cli --filter=@inkeep/agents-manage-ui && vitest src/__tests__/e2e",
72
72
  "test:all": "vitest --run --passWithNoTests",
73
73
  "typecheck": "tsc --noEmit"
74
74
  }