@inkeep/create-agents 0.0.0-chat-to-edit-20251119071712

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.
@@ -0,0 +1,46 @@
1
+ export declare const defaultGoogleModelConfigurations: {
2
+ base: {
3
+ model: "google/gemini-2.5-flash";
4
+ };
5
+ structuredOutput: {
6
+ model: "google/gemini-2.5-flash-lite";
7
+ };
8
+ summarizer: {
9
+ model: "google/gemini-2.5-flash-lite";
10
+ };
11
+ };
12
+ export declare const defaultOpenaiModelConfigurations: {
13
+ base: {
14
+ model: "openai/gpt-4.1";
15
+ };
16
+ structuredOutput: {
17
+ model: "openai/gpt-4.1-mini";
18
+ };
19
+ summarizer: {
20
+ model: "openai/gpt-4.1-nano";
21
+ };
22
+ };
23
+ export declare const defaultAnthropicModelConfigurations: {
24
+ base: {
25
+ model: "anthropic/claude-sonnet-4-5";
26
+ };
27
+ structuredOutput: {
28
+ model: "anthropic/claude-sonnet-4-5";
29
+ };
30
+ summarizer: {
31
+ model: "anthropic/claude-sonnet-4-5";
32
+ };
33
+ };
34
+ export declare const createAgents: (args?: {
35
+ dirName?: string;
36
+ templateName?: string;
37
+ openAiKey?: string;
38
+ anthropicKey?: string;
39
+ googleKey?: string;
40
+ template?: string;
41
+ customProjectId?: string;
42
+ disableGit?: boolean;
43
+ localAgentsPrefix?: string;
44
+ localTemplatesPrefix?: string;
45
+ }) => Promise<void>;
46
+ export declare function createCommand(dirName?: string, options?: any): Promise<void>;
package/dist/utils.js ADDED
@@ -0,0 +1,460 @@
1
+ import { exec } from 'node:child_process';
2
+ import crypto from 'node:crypto';
3
+ import path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ import * as p from '@clack/prompts';
6
+ import { ANTHROPIC_MODELS, GOOGLE_MODELS, OPENAI_MODELS } from '@inkeep/agents-core';
7
+ import fs from 'fs-extra';
8
+ import color from 'picocolors';
9
+ import { cloneTemplate, cloneTemplateLocal, getAvailableTemplates, } from './templates.js';
10
+ // Shared validation utility
11
+ const DIRECTORY_VALIDATION = {
12
+ pattern: /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/,
13
+ reservedNames: /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i,
14
+ minLength: 1,
15
+ maxLength: 255,
16
+ validate(value) {
17
+ if (!value || value.trim() === '')
18
+ return 'Directory name is required';
19
+ if (value.length < this.minLength || value.length > this.maxLength) {
20
+ return `Directory name must be between ${this.minLength} and ${this.maxLength} characters`;
21
+ }
22
+ if (this.reservedNames.test(value)) {
23
+ return 'Directory name cannot be a reserved system name';
24
+ }
25
+ if (!this.pattern.test(value)) {
26
+ return 'Directory name can only contain letters, numbers, and hyphens (-), and underscores (_) and must start with a letter or number';
27
+ }
28
+ return undefined;
29
+ },
30
+ };
31
+ const agentsTemplateRepo = 'https://github.com/inkeep/agents/create-agents-template';
32
+ const projectTemplateRepo = 'https://github.com/inkeep/agents/agents-cookbook/template-projects';
33
+ const execAsync = promisify(exec);
34
+ const manageApiPort = '3002';
35
+ const runApiPort = '3003';
36
+ export const defaultGoogleModelConfigurations = {
37
+ base: {
38
+ model: GOOGLE_MODELS.GEMINI_2_5_FLASH,
39
+ },
40
+ structuredOutput: {
41
+ model: GOOGLE_MODELS.GEMINI_2_5_FLASH_LITE,
42
+ },
43
+ summarizer: {
44
+ model: GOOGLE_MODELS.GEMINI_2_5_FLASH_LITE,
45
+ },
46
+ };
47
+ export const defaultOpenaiModelConfigurations = {
48
+ base: {
49
+ model: OPENAI_MODELS.GPT_4_1,
50
+ },
51
+ structuredOutput: {
52
+ model: OPENAI_MODELS.GPT_4_1_MINI,
53
+ },
54
+ summarizer: {
55
+ model: OPENAI_MODELS.GPT_4_1_NANO,
56
+ },
57
+ };
58
+ export const defaultAnthropicModelConfigurations = {
59
+ base: {
60
+ model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5,
61
+ },
62
+ structuredOutput: {
63
+ model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5,
64
+ },
65
+ summarizer: {
66
+ model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5,
67
+ },
68
+ };
69
+ export const createAgents = async (args = {}) => {
70
+ let { dirName, openAiKey, anthropicKey, googleKey, template, customProjectId, disableGit, localAgentsPrefix, localTemplatesPrefix, } = args;
71
+ const tenantId = 'default';
72
+ let projectId;
73
+ let templateName;
74
+ if (customProjectId) {
75
+ projectId = customProjectId;
76
+ templateName = '';
77
+ }
78
+ else if (template) {
79
+ const availableTemplates = await getAvailableTemplates(localTemplatesPrefix);
80
+ if (!availableTemplates.includes(template)) {
81
+ p.cancel(`${color.red('✗')} Template "${template}" not found\n\n` +
82
+ `${color.yellow('Available templates:')}\n` +
83
+ ` • ${availableTemplates.join('\n • ')}\n`);
84
+ process.exit(0);
85
+ }
86
+ projectId = template;
87
+ templateName = template;
88
+ }
89
+ else {
90
+ projectId = 'activities-planner';
91
+ templateName = 'activities-planner';
92
+ }
93
+ p.intro(color.inverse(' Create Agents Directory '));
94
+ if (!dirName) {
95
+ const dirResponse = await p.text({
96
+ message: 'What do you want to name your agents directory?',
97
+ placeholder: 'agents',
98
+ defaultValue: 'agents',
99
+ validate: (value) => DIRECTORY_VALIDATION.validate(value),
100
+ });
101
+ if (p.isCancel(dirResponse)) {
102
+ p.cancel('Operation cancelled');
103
+ process.exit(0);
104
+ }
105
+ dirName = dirResponse;
106
+ }
107
+ else {
108
+ // Validate the provided dirName
109
+ const validationError = DIRECTORY_VALIDATION.validate(dirName);
110
+ if (validationError) {
111
+ throw new Error(validationError);
112
+ }
113
+ }
114
+ if (!anthropicKey && !openAiKey && !googleKey) {
115
+ const providerChoice = await p.select({
116
+ message: 'Which AI provider would you like to use?',
117
+ options: [
118
+ { value: 'anthropic', label: 'Anthropic' },
119
+ { value: 'openai', label: 'OpenAI' },
120
+ { value: 'google', label: 'Google' },
121
+ ],
122
+ });
123
+ if (p.isCancel(providerChoice)) {
124
+ p.cancel('Operation cancelled');
125
+ process.exit(0);
126
+ }
127
+ if (providerChoice === 'anthropic') {
128
+ const anthropicKeyResponse = await p.password({
129
+ message: 'Enter your Anthropic API key:',
130
+ validate: (value) => {
131
+ if (!value || value.trim() === '') {
132
+ return 'Anthropic API key is required';
133
+ }
134
+ return undefined;
135
+ },
136
+ });
137
+ if (p.isCancel(anthropicKeyResponse)) {
138
+ p.cancel('Operation cancelled');
139
+ process.exit(0);
140
+ }
141
+ anthropicKey = anthropicKeyResponse;
142
+ }
143
+ else if (providerChoice === 'openai') {
144
+ const openAiKeyResponse = await p.password({
145
+ message: 'Enter your OpenAI API key:',
146
+ validate: (value) => {
147
+ if (!value || value.trim() === '') {
148
+ return 'OpenAI API key is required';
149
+ }
150
+ return undefined;
151
+ },
152
+ });
153
+ if (p.isCancel(openAiKeyResponse)) {
154
+ p.cancel('Operation cancelled');
155
+ process.exit(0);
156
+ }
157
+ openAiKey = openAiKeyResponse;
158
+ }
159
+ else if (providerChoice === 'google') {
160
+ const googleKeyResponse = await p.password({
161
+ message: 'Enter your Google API key:',
162
+ validate: (value) => {
163
+ if (!value || value.trim() === '') {
164
+ return 'Google API key is required';
165
+ }
166
+ return undefined;
167
+ },
168
+ });
169
+ if (p.isCancel(googleKeyResponse)) {
170
+ p.cancel('Operation cancelled');
171
+ process.exit(0);
172
+ }
173
+ googleKey = googleKeyResponse;
174
+ }
175
+ }
176
+ let defaultModelSettings = {};
177
+ if (anthropicKey) {
178
+ defaultModelSettings = defaultAnthropicModelConfigurations;
179
+ }
180
+ else if (openAiKey) {
181
+ defaultModelSettings = defaultOpenaiModelConfigurations;
182
+ }
183
+ else if (googleKey) {
184
+ defaultModelSettings = defaultGoogleModelConfigurations;
185
+ }
186
+ if (Object.keys(defaultModelSettings).length === 0) {
187
+ p.cancel('Cannot continue without a model configuration for project. Please provide an API key for at least one AI provider.');
188
+ process.exit(1);
189
+ }
190
+ const s = p.spinner();
191
+ s.start('Creating directory structure...');
192
+ try {
193
+ const directoryPath = path.resolve(process.cwd(), dirName);
194
+ if (await fs.pathExists(directoryPath)) {
195
+ s.stop();
196
+ const overwrite = await p.confirm({
197
+ message: `Directory ${dirName} already exists. Do you want to overwrite it?`,
198
+ });
199
+ if (p.isCancel(overwrite) || !overwrite) {
200
+ p.cancel('Operation cancelled');
201
+ process.exit(0);
202
+ }
203
+ s.start('Cleaning existing directory...');
204
+ await fs.emptyDir(directoryPath);
205
+ }
206
+ s.message('Building template (this may take a while)...');
207
+ await cloneTemplateHelper({
208
+ targetPath: directoryPath,
209
+ localPrefix: localAgentsPrefix,
210
+ });
211
+ process.chdir(directoryPath);
212
+ const config = {
213
+ dirName,
214
+ tenantId,
215
+ projectId,
216
+ openAiKey,
217
+ anthropicKey,
218
+ googleKey,
219
+ modelSettings: defaultModelSettings,
220
+ customProject: !!customProjectId,
221
+ disableGit: disableGit,
222
+ };
223
+ s.message('Setting up project structure...');
224
+ await createWorkspaceStructure();
225
+ s.message('Setting up environment files...');
226
+ await createEnvironmentFiles(config);
227
+ if (templateName && templateName.length > 0) {
228
+ s.message('Creating project template folder...');
229
+ const templateTargetPath = `src/projects/${projectId}`;
230
+ const contentReplacements = [
231
+ {
232
+ filePath: 'index.ts',
233
+ replacements: {
234
+ models: defaultModelSettings,
235
+ },
236
+ },
237
+ ];
238
+ await cloneTemplateHelper({
239
+ templateName,
240
+ targetPath: templateTargetPath,
241
+ localPrefix: localTemplatesPrefix,
242
+ replacements: contentReplacements,
243
+ });
244
+ }
245
+ else {
246
+ s.message('Creating empty project folder...');
247
+ await fs.ensureDir(`src/projects/${projectId}`);
248
+ }
249
+ s.message('Creating inkeep.config.ts...');
250
+ await createInkeepConfig(config);
251
+ s.message('Installing dependencies (this may take a while)...');
252
+ await installDependencies();
253
+ if (!config.disableGit) {
254
+ await initializeGit();
255
+ }
256
+ await checkPortsAvailability();
257
+ s.stop();
258
+ p.note(`${color.green('✓')} Workspace created at: ${color.cyan(directoryPath)}\n\n` +
259
+ `${color.yellow('Next steps:')}\n` +
260
+ ` cd ${dirName}\n` +
261
+ ` pnpm setup # Setup project in database\n` +
262
+ ` pnpm dev # Start development servers\n\n` +
263
+ `${color.yellow('Available services:')}\n` +
264
+ ` • Manage API: http://localhost:3002\n` +
265
+ ` • Run API: http://localhost:3003\n` +
266
+ ` • Manage UI: Available with management API\n` +
267
+ `\n${color.yellow('Configuration:')}\n` +
268
+ ` • Edit .env for environment variables\n` +
269
+ ` • Edit files in src/projects/${projectId}/ for agent definitions\n` +
270
+ ` • Use 'inkeep push' to deploy agents to the platform\n`, 'Ready to go!');
271
+ }
272
+ catch (error) {
273
+ s.stop();
274
+ p.cancel(`Error creating directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
275
+ process.exit(1);
276
+ }
277
+ };
278
+ async function createWorkspaceStructure() {
279
+ await fs.ensureDir(`src`);
280
+ }
281
+ async function createEnvironmentFiles(config) {
282
+ // Convert to forward slashes for cross-platform SQLite URI compatibility
283
+ const jwtSigningSecret = crypto.randomBytes(32).toString('hex');
284
+ const envContent = `# Environment
285
+ ENVIRONMENT=development
286
+
287
+ # Database
288
+ DATABASE_URL=postgresql://appuser:password@localhost:5432/inkeep_agents
289
+
290
+ # AI Provider Keys
291
+ ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
292
+ OPENAI_API_KEY=${config.openAiKey || 'your-openai-key-here'}
293
+ GOOGLE_GENERATIVE_AI_API_KEY=${config.googleKey || 'your-google-key-here'}
294
+
295
+ # Inkeep API URLs
296
+ # Internal URLs (server-side, Docker internal networking)
297
+ INKEEP_AGENTS_MANAGE_API_URL="http://localhost:3002"
298
+ INKEEP_AGENTS_RUN_API_URL="http://localhost:3003"
299
+
300
+ # Public URLs (client-side, browser accessible)
301
+ PUBLIC_INKEEP_AGENTS_MANAGE_API_URL="http://localhost:3002"
302
+ PUBLIC_INKEEP_AGENTS_RUN_API_URL="http://localhost:3003"
303
+
304
+ # SigNoz Configuration
305
+ SIGNOZ_URL=your-signoz-url-here
306
+ SIGNOZ_API_KEY=your-signoz-api-key-here
307
+
308
+ # OTEL Configuration
309
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://ingest.us.signoz.cloud:443/v1/traces
310
+ OTEL_EXPORTER_OTLP_TRACES_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
311
+
312
+ # Nango Configuration
313
+ NANGO_SECRET_KEY=
314
+
315
+ # JWT Signing Secret
316
+ INKEEP_AGENTS_JWT_SIGNING_SECRET=${jwtSigningSecret}
317
+
318
+ # initial project information
319
+ DEFAULT_PROJECT_ID=${config.projectId}
320
+ `;
321
+ await fs.writeFile('.env', envContent);
322
+ }
323
+ async function createInkeepConfig(config) {
324
+ const inkeepConfig = `import { defineConfig } from '@inkeep/agents-cli/config';
325
+
326
+ const config = defineConfig({
327
+ tenantId: "${config.tenantId}",
328
+ agentsManageApi: {
329
+ url: 'http://localhost:3002',
330
+ },
331
+ agentsRunApi: {
332
+ url: 'http://localhost:3003',
333
+ },
334
+ });
335
+
336
+ export default config;`;
337
+ await fs.writeFile(`src/inkeep.config.ts`, inkeepConfig);
338
+ if (config.customProject) {
339
+ const customIndexContent = `import { project } from '@inkeep/agents-sdk';
340
+
341
+ export const myProject = project({
342
+ id: "${config.projectId}",
343
+ name: "${config.projectId}",
344
+ description: "",
345
+ agent: () => [],
346
+ models: ${JSON.stringify(config.modelSettings, null, 2)},
347
+ });`;
348
+ await fs.writeFile(`src/projects/${config.projectId}/index.ts`, customIndexContent);
349
+ }
350
+ }
351
+ async function installDependencies() {
352
+ try {
353
+ const { stderr } = await execAsync('pnpm install');
354
+ if (process.env.CI && stderr) {
355
+ console.log('pnpm install stderr:', stderr);
356
+ }
357
+ }
358
+ catch (error) {
359
+ // Capture and log full error details for debugging
360
+ console.error('pnpm install failed!');
361
+ console.error('Exit code:', error.code);
362
+ if (error.stdout) {
363
+ console.error('stdout:', error.stdout);
364
+ }
365
+ if (error.stderr) {
366
+ console.error('stderr:', error.stderr);
367
+ }
368
+ if (error.message) {
369
+ console.error('Error message:', error.message);
370
+ }
371
+ throw error;
372
+ }
373
+ }
374
+ async function initializeGit() {
375
+ try {
376
+ await execAsync('git init');
377
+ await execAsync('git add .');
378
+ await execAsync('git commit -m "Initial commit from inkeep/create-agents"');
379
+ }
380
+ catch (error) {
381
+ console.error('Error initializing git:', error instanceof Error ? error.message : 'Unknown error');
382
+ }
383
+ }
384
+ /**
385
+ * Check if a port is available
386
+ */
387
+ async function isPortAvailable(port) {
388
+ const net = await import('node:net');
389
+ return new Promise((resolve) => {
390
+ const server = net.createServer();
391
+ server.once('error', (err) => {
392
+ // Only treat EADDRINUSE as "port in use", other errors might be transient
393
+ resolve(err.code === 'EADDRINUSE' ? false : true);
394
+ });
395
+ server.once('listening', () => {
396
+ server.close(() => {
397
+ resolve(true);
398
+ });
399
+ });
400
+ server.listen(port, 'localhost');
401
+ });
402
+ }
403
+ /**
404
+ * Display port conflict error and exit
405
+ */
406
+ function displayPortConflictError(unavailablePorts) {
407
+ let errorMessage = '';
408
+ if (unavailablePorts.runApi) {
409
+ errorMessage += `${color.red(`Run API port ${runApiPort} is already in use`)}\n`;
410
+ }
411
+ if (unavailablePorts.manageApi) {
412
+ errorMessage += `${color.red(`Manage API port ${manageApiPort} is already in use`)}\n`;
413
+ }
414
+ p.cancel(`\n${color.red('✗ Port conflicts detected')}\n\n` +
415
+ `${errorMessage}\n` +
416
+ `${color.yellow('Please free up the ports and try again.')}\n`);
417
+ process.exit(1);
418
+ }
419
+ /**
420
+ * Check port availability and display errors if needed
421
+ */
422
+ async function checkPortsAvailability() {
423
+ const [runApiAvailable, manageApiAvailable] = await Promise.all([
424
+ isPortAvailable(Number(runApiPort)),
425
+ isPortAvailable(Number(manageApiPort)),
426
+ ]);
427
+ if (!runApiAvailable || !manageApiAvailable) {
428
+ displayPortConflictError({
429
+ runApi: !runApiAvailable,
430
+ manageApi: !manageApiAvailable,
431
+ });
432
+ }
433
+ }
434
+ async function cloneTemplateHelper(options) {
435
+ const { targetPath, templateName, localPrefix, replacements } = options;
436
+ // If local prefix is provided, use it to clone the template. This is useful for local development and testing.
437
+ if (localPrefix && localPrefix.length > 0) {
438
+ if (templateName) {
439
+ const fullTemplatePath = path.join(localPrefix, templateName);
440
+ await cloneTemplateLocal(fullTemplatePath, targetPath, replacements);
441
+ }
442
+ else {
443
+ await cloneTemplateLocal(localPrefix, targetPath, replacements);
444
+ }
445
+ }
446
+ else {
447
+ if (templateName) {
448
+ await cloneTemplate(`${projectTemplateRepo}/${templateName}`, targetPath, replacements);
449
+ }
450
+ else {
451
+ await cloneTemplate(agentsTemplateRepo, targetPath, replacements);
452
+ }
453
+ }
454
+ }
455
+ export async function createCommand(dirName, options) {
456
+ await createAgents({
457
+ dirName,
458
+ ...options,
459
+ });
460
+ }
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@inkeep/create-agents",
3
+ "version": "0.0.0-chat-to-edit-20251119071712",
4
+ "description": "Create an Inkeep Agent Framework project",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-agents": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE.md",
13
+ "SUPPLEMENTAL_TERMS.md"
14
+ ],
15
+ "keywords": [
16
+ "inkeep",
17
+ "agents",
18
+ "ai",
19
+ "framework",
20
+ "boilerplate",
21
+ "typescript",
22
+ "project",
23
+ "starter",
24
+ "cli",
25
+ "create",
26
+ "init"
27
+ ],
28
+ "author": "Inkeep <support@inkeep.com>",
29
+ "license": "SEE LICENSE IN LICENSE.md",
30
+ "dependencies": {
31
+ "@clack/prompts": "^0.11.0",
32
+ "commander": "^12.0.0",
33
+ "degit": "^2.8.4",
34
+ "drizzle-kit": "^0.31.5",
35
+ "fs-extra": "^11.0.0",
36
+ "picocolors": "^1.0.0",
37
+ "@inkeep/agents-core": "0.0.0-chat-to-edit-20251119071712"
38
+ },
39
+ "devDependencies": {
40
+ "@types/degit": "^2.8.6",
41
+ "@types/fs-extra": "^11.0.0",
42
+ "@types/node": "^20.12.0",
43
+ "execa": "^9.6.0",
44
+ "tsx": "^4.7.0",
45
+ "typescript": "^5.4.0",
46
+ "vitest": "^3.2.4"
47
+ },
48
+ "engines": {
49
+ "node": ">=22.0.0"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/inkeep/agents.git",
54
+ "directory": "packages/create-agents"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public",
58
+ "registry": "https://registry.npmjs.org/"
59
+ },
60
+ "scripts": {
61
+ "dev": "tsx src/index.ts",
62
+ "build": "tsc",
63
+ "lint": "biome lint src",
64
+ "lint:fix": "biome check --write .",
65
+ "format": "biome format --write .",
66
+ "test": "vitest --run src/__tests__ --exclude src/__tests__/e2e/** --passWithNoTests",
67
+ "test:watch": "vitest src/__tests__ --exclude src/__tests__/e2e/**",
68
+ "test:unit": "vitest --run src/__tests__ --exclude src/__tests__/e2e/**",
69
+ "test:e2e": "vitest --run src/__tests__/e2e",
70
+ "test:e2e:watch": "vitest src/__tests__/e2e",
71
+ "test:all": "vitest --run --passWithNoTests",
72
+ "typecheck": "tsc --noEmit"
73
+ }
74
+ }