@inkeep/create-agents 0.1.8 → 0.1.9
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/README.md +5 -8
- package/dist/index.js +1 -7
- package/dist/utils.d.ts +40 -0
- package/dist/utils.js +1055 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@ Create an Inkeep Agent Framework directory with multi-service architecture.
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Interactive mode
|
|
9
|
-
|
|
9
|
+
npx create-agents
|
|
10
10
|
|
|
11
11
|
# With directory name
|
|
12
|
-
|
|
12
|
+
npx create-agents my-agent-directory
|
|
13
13
|
|
|
14
14
|
# With options
|
|
15
|
-
npx create-agents my-agent-directory
|
|
15
|
+
npx create-agents my-agent-directory --project-id my-project --openai-key sk-... --anthropic-key sk-ant-...
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
@@ -22,7 +22,7 @@ npx create-agents my-agent-directory --tenant-id default --project-id my-project
|
|
|
22
22
|
### Interactive Mode
|
|
23
23
|
Run without arguments for an interactive setup experience:
|
|
24
24
|
```bash
|
|
25
|
-
|
|
25
|
+
npx create-agents
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
You'll be prompted for:
|
|
@@ -35,17 +35,14 @@ You'll be prompted for:
|
|
|
35
35
|
### Direct Mode
|
|
36
36
|
Specify options directly:
|
|
37
37
|
```bash
|
|
38
|
-
pnpm create-agents my-agent-directory --
|
|
38
|
+
pnpm create-agents my-agent-directory --project-id my-project-id --anthropic-key sk-ant-... --openai-key sk-...
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
## Options
|
|
42
42
|
|
|
43
|
-
- `--tenant-id <tenant-id>` - Your Inkeep tenant ID
|
|
44
43
|
- `--project-id <project-id>` - Project identifier for your agents
|
|
45
44
|
- `--openai-key <openai-key>` - OpenAI API key (optional)
|
|
46
45
|
- `--anthropic-key <anthropic-key>` - Anthropic API key (recommended)
|
|
47
|
-
- `--manage-api-port <port>` - Manage API port (default: 3002)
|
|
48
|
-
- `--run-api-port <port>` - Run API port (default: 3003)
|
|
49
46
|
|
|
50
47
|
## What's Created
|
|
51
48
|
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program } from 'commander';
|
|
3
|
-
import { createAgents } from '
|
|
3
|
+
import { createAgents } from './utils.js';
|
|
4
4
|
program
|
|
5
5
|
.name('create-agents')
|
|
6
6
|
.description('Create an Inkeep Agent Framework directory')
|
|
7
7
|
.version('0.1.0')
|
|
8
8
|
.argument('[directory-name]', 'Name of the directory')
|
|
9
|
-
.option('--tenant-id <tenant-id>', 'Tenant ID')
|
|
10
9
|
.option('--project-id <project-id>', 'Project ID')
|
|
11
10
|
.option('--openai-key <openai-key>', 'OpenAI API key')
|
|
12
11
|
.option('--anthropic-key <anthropic-key>', 'Anthropic API key')
|
|
13
|
-
.option('--manage-api-port <port>', 'Management API port', '3002')
|
|
14
|
-
.option('--run-api-port <port>', 'Run API port', '3003')
|
|
15
12
|
.parse();
|
|
16
13
|
async function main() {
|
|
17
14
|
const options = program.opts();
|
|
@@ -21,10 +18,7 @@ async function main() {
|
|
|
21
18
|
dirName: directoryName,
|
|
22
19
|
openAiKey: options.openaiKey,
|
|
23
20
|
anthropicKey: options.anthropicKey,
|
|
24
|
-
tenantId: options.tenantId,
|
|
25
21
|
projectId: options.projectId,
|
|
26
|
-
manageApiPort: options.manageApiPort,
|
|
27
|
-
runApiPort: options.runApiPort,
|
|
28
22
|
});
|
|
29
23
|
}
|
|
30
24
|
catch (error) {
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const defaultDualModelConfigurations: {
|
|
2
|
+
base: {
|
|
3
|
+
model: string;
|
|
4
|
+
};
|
|
5
|
+
structuredOutput: {
|
|
6
|
+
model: string;
|
|
7
|
+
};
|
|
8
|
+
summarizer: {
|
|
9
|
+
model: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare const defaultOpenaiModelConfigurations: {
|
|
13
|
+
base: {
|
|
14
|
+
model: string;
|
|
15
|
+
};
|
|
16
|
+
structuredOutput: {
|
|
17
|
+
model: string;
|
|
18
|
+
};
|
|
19
|
+
summarizer: {
|
|
20
|
+
model: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export declare const defaultAnthropicModelConfigurations: {
|
|
24
|
+
base: {
|
|
25
|
+
model: string;
|
|
26
|
+
};
|
|
27
|
+
structuredOutput: {
|
|
28
|
+
model: string;
|
|
29
|
+
};
|
|
30
|
+
summarizer: {
|
|
31
|
+
model: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export declare const createAgents: (args?: {
|
|
35
|
+
projectId?: string;
|
|
36
|
+
dirName?: string;
|
|
37
|
+
openAiKey?: string;
|
|
38
|
+
anthropicKey?: string;
|
|
39
|
+
}) => Promise<void>;
|
|
40
|
+
export declare function createCommand(dirName?: string, options?: any): Promise<void>;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,1055 @@
|
|
|
1
|
+
import color from 'picocolors';
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import { exec } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
export const defaultDualModelConfigurations = {
|
|
9
|
+
base: {
|
|
10
|
+
model: 'anthropic/claude-sonnet-4-20250514',
|
|
11
|
+
},
|
|
12
|
+
structuredOutput: {
|
|
13
|
+
model: 'openai/gpt-4.1-mini-2025-04-14',
|
|
14
|
+
},
|
|
15
|
+
summarizer: {
|
|
16
|
+
model: 'openai/gpt-4.1-nano-2025-04-14',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export const defaultOpenaiModelConfigurations = {
|
|
20
|
+
base: {
|
|
21
|
+
model: 'openai/gpt-5-2025-08-07',
|
|
22
|
+
},
|
|
23
|
+
structuredOutput: {
|
|
24
|
+
model: 'openai/gpt-4.1-mini-2025-04-14',
|
|
25
|
+
},
|
|
26
|
+
summarizer: {
|
|
27
|
+
model: 'openai/gpt-4.1-nano-2025-04-14',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export const defaultAnthropicModelConfigurations = {
|
|
31
|
+
base: {
|
|
32
|
+
model: 'anthropic/claude-sonnet-4-20250514',
|
|
33
|
+
},
|
|
34
|
+
structuredOutput: {
|
|
35
|
+
model: 'anthropic/claude-sonnet-4-20250514',
|
|
36
|
+
},
|
|
37
|
+
summarizer: {
|
|
38
|
+
model: 'anthropic/claude-sonnet-4-20250514',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export const createAgents = async (args = {}) => {
|
|
42
|
+
let { projectId, dirName, openAiKey, anthropicKey } = args;
|
|
43
|
+
const tenantId = 'default';
|
|
44
|
+
const manageApiPort = '3002';
|
|
45
|
+
const runApiPort = '3003';
|
|
46
|
+
p.intro(color.inverse(' Create Agents Directory '));
|
|
47
|
+
// Prompt for directory name if not provided
|
|
48
|
+
if (!dirName) {
|
|
49
|
+
const dirResponse = await p.text({
|
|
50
|
+
message: 'What do you want to name your agents directory?',
|
|
51
|
+
placeholder: 'agents',
|
|
52
|
+
defaultValue: 'agents',
|
|
53
|
+
validate: (value) => {
|
|
54
|
+
if (!value || value.trim() === '') {
|
|
55
|
+
return 'Directory name is required';
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
if (p.isCancel(dirResponse)) {
|
|
61
|
+
p.cancel('Operation cancelled');
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
dirName = dirResponse;
|
|
65
|
+
}
|
|
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
|
+
}
|
|
79
|
+
// If keys aren't provided via CLI args, prompt for provider selection and keys
|
|
80
|
+
if (!anthropicKey && !openAiKey) {
|
|
81
|
+
const providerChoice = await p.select({
|
|
82
|
+
message: 'Which AI provider(s) would you like to use?',
|
|
83
|
+
options: [
|
|
84
|
+
{ value: 'both', label: 'Both Anthropic and OpenAI (recommended)' },
|
|
85
|
+
{ value: 'anthropic', label: 'Anthropic only' },
|
|
86
|
+
{ value: 'openai', label: 'OpenAI only' },
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
if (p.isCancel(providerChoice)) {
|
|
90
|
+
p.cancel('Operation cancelled');
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
// Prompt for keys based on selection
|
|
94
|
+
if (providerChoice === 'anthropic' || providerChoice === 'both') {
|
|
95
|
+
const anthropicKeyResponse = await p.text({
|
|
96
|
+
message: 'Enter your Anthropic API key:',
|
|
97
|
+
placeholder: 'sk-ant-...',
|
|
98
|
+
validate: (value) => {
|
|
99
|
+
if (!value || value.trim() === '') {
|
|
100
|
+
return 'Anthropic API key is required';
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
if (p.isCancel(anthropicKeyResponse)) {
|
|
106
|
+
p.cancel('Operation cancelled');
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
anthropicKey = anthropicKeyResponse;
|
|
110
|
+
}
|
|
111
|
+
if (providerChoice === 'openai' || providerChoice === 'both') {
|
|
112
|
+
const openAiKeyResponse = await p.text({
|
|
113
|
+
message: 'Enter your OpenAI API key:',
|
|
114
|
+
placeholder: 'sk-...',
|
|
115
|
+
validate: (value) => {
|
|
116
|
+
if (!value || value.trim() === '') {
|
|
117
|
+
return 'OpenAI API key is required';
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
if (p.isCancel(openAiKeyResponse)) {
|
|
123
|
+
p.cancel('Operation cancelled');
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
openAiKey = openAiKeyResponse;
|
|
127
|
+
}
|
|
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: '',
|
|
148
|
+
});
|
|
149
|
+
if (p.isCancel(openAiKeyResponse)) {
|
|
150
|
+
p.cancel('Operation cancelled');
|
|
151
|
+
process.exit(0);
|
|
152
|
+
}
|
|
153
|
+
openAiKey = openAiKeyResponse || undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
let defaultModelSettings = {};
|
|
157
|
+
if (anthropicKey && openAiKey) {
|
|
158
|
+
defaultModelSettings = defaultDualModelConfigurations;
|
|
159
|
+
}
|
|
160
|
+
else if (anthropicKey) {
|
|
161
|
+
defaultModelSettings = defaultAnthropicModelConfigurations;
|
|
162
|
+
}
|
|
163
|
+
else if (openAiKey) {
|
|
164
|
+
defaultModelSettings = defaultOpenaiModelConfigurations;
|
|
165
|
+
}
|
|
166
|
+
const s = p.spinner();
|
|
167
|
+
s.start('Creating directory structure...');
|
|
168
|
+
try {
|
|
169
|
+
const directoryPath = path.resolve(process.cwd(), dirName);
|
|
170
|
+
// Check if directory already exists
|
|
171
|
+
if (await fs.pathExists(directoryPath)) {
|
|
172
|
+
s.stop();
|
|
173
|
+
const overwrite = await p.confirm({
|
|
174
|
+
message: `Directory ${dirName} already exists. Do you want to overwrite it?`,
|
|
175
|
+
});
|
|
176
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
177
|
+
p.cancel('Operation cancelled');
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
s.start('Cleaning existing directory...');
|
|
181
|
+
await fs.emptyDir(directoryPath);
|
|
182
|
+
}
|
|
183
|
+
// Create the project directory
|
|
184
|
+
await fs.ensureDir(directoryPath);
|
|
185
|
+
process.chdir(directoryPath);
|
|
186
|
+
const config = {
|
|
187
|
+
dirName,
|
|
188
|
+
tenantId,
|
|
189
|
+
projectId,
|
|
190
|
+
openAiKey,
|
|
191
|
+
anthropicKey,
|
|
192
|
+
manageApiPort: manageApiPort || '3002',
|
|
193
|
+
runApiPort: runApiPort || '3003',
|
|
194
|
+
modelSettings: defaultModelSettings,
|
|
195
|
+
};
|
|
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);
|
|
202
|
+
// Create environment files
|
|
203
|
+
s.message('Setting up environment files...');
|
|
204
|
+
await createEnvironmentFiles(config);
|
|
205
|
+
// Create service files
|
|
206
|
+
s.message('Creating service files...');
|
|
207
|
+
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
|
+
// Install dependencies
|
|
215
|
+
s.message('Installing dependencies (this may take a while)...');
|
|
216
|
+
await installDependencies();
|
|
217
|
+
// Setup database
|
|
218
|
+
s.message('Setting up database...');
|
|
219
|
+
await setupDatabase();
|
|
220
|
+
// Setup project in database
|
|
221
|
+
s.message('Setting up project in database...');
|
|
222
|
+
await setupProjectInDatabase();
|
|
223
|
+
s.stop();
|
|
224
|
+
// Success message with next steps
|
|
225
|
+
p.note(`${color.green('✓')} Project created at: ${color.cyan(directoryPath)}\n\n` +
|
|
226
|
+
`${color.yellow('Ready to go!')}\n\n` +
|
|
227
|
+
`${color.green('✓')} Project created in file system\n` +
|
|
228
|
+
`${color.green('✓')} Database configured\n` +
|
|
229
|
+
`${color.green('✓')} Project added to database\n\n` +
|
|
230
|
+
`${color.yellow('Next steps:')}\n` +
|
|
231
|
+
` cd ${dirName}\n` +
|
|
232
|
+
` pnpm dev # Start development servers\n\n` +
|
|
233
|
+
`${color.yellow('Available services:')}\n` +
|
|
234
|
+
` • Manage API: http://localhost:${manageApiPort || '3002'}\n` +
|
|
235
|
+
` • Run API: http://localhost:${runApiPort || '3003'}\n` +
|
|
236
|
+
` • Manage UI: Available with management API\n` +
|
|
237
|
+
`\n${color.yellow('Configuration:')}\n` +
|
|
238
|
+
` • Edit .env for environment variables\n` +
|
|
239
|
+
` • Edit src/${projectId}/weather.graph.ts for agent definitions\n` +
|
|
240
|
+
` • Use 'inkeep push' to deploy agents to the platform\n` +
|
|
241
|
+
` • Use 'inkeep chat' to test your agents locally\n`, 'Ready to go!');
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
s.stop();
|
|
245
|
+
p.cancel(`Error creating directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
async function createWorkspaceStructure(projectId) {
|
|
250
|
+
// 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
|
|
376
|
+
}
|
|
377
|
+
async function createEnvironmentFiles(config) {
|
|
378
|
+
// Root .env file
|
|
379
|
+
const envContent = `# Environment
|
|
380
|
+
ENVIRONMENT=development
|
|
381
|
+
|
|
382
|
+
# Database
|
|
383
|
+
DB_FILE_NAME=file:./local.db
|
|
384
|
+
|
|
385
|
+
# AI Provider Keys
|
|
386
|
+
ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
|
|
387
|
+
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
|
+
|
|
398
|
+
`;
|
|
399
|
+
await fs.writeFile('.env', envContent);
|
|
400
|
+
// Create .env.example
|
|
401
|
+
const envExample = envContent.replace(/=.+$/gm, '=');
|
|
402
|
+
await fs.writeFile('.env.example', envExample);
|
|
403
|
+
// Create setup script
|
|
404
|
+
await createSetupScript(config);
|
|
405
|
+
// Create dev-setup script
|
|
406
|
+
await createDevSetupScript(config);
|
|
407
|
+
// Create .env files for each API service
|
|
408
|
+
const runApiEnvContent = `# Environment
|
|
409
|
+
ENVIRONMENT=development
|
|
410
|
+
|
|
411
|
+
# Database (relative path from API directory)
|
|
412
|
+
DB_FILE_NAME=file:../../local.db
|
|
413
|
+
|
|
414
|
+
# AI Provider Keys
|
|
415
|
+
ANTHROPIC_API_KEY=${config.anthropicKey || 'your-anthropic-key-here'}
|
|
416
|
+
OPENAI_API_KEY=${config.openAiKey || 'your-openai-key-here'}
|
|
417
|
+
|
|
418
|
+
AGENTS_RUN_API_URL=http://localhost:${config.runApiPort}
|
|
419
|
+
`;
|
|
420
|
+
const manageApiEnvContent = `# Environment
|
|
421
|
+
ENVIRONMENT=development
|
|
422
|
+
|
|
423
|
+
# Database (relative path from API directory)
|
|
424
|
+
DB_FILE_NAME=file:../../local.db
|
|
425
|
+
|
|
426
|
+
AGENTS_MANAGE_API_URL=http://localhost:${config.manageApiPort}
|
|
427
|
+
`;
|
|
428
|
+
await fs.writeFile('apps/manage-api/.env', manageApiEnvContent);
|
|
429
|
+
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.dirName}';
|
|
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
|
+
}
|
|
645
|
+
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
|
+
// Create .env file for the project directory (for inkeep CLI commands)
|
|
712
|
+
const projectEnvContent = `# Environment
|
|
713
|
+
ENVIRONMENT=development
|
|
714
|
+
|
|
715
|
+
# Database (relative path from project directory)
|
|
716
|
+
DB_FILE_NAME=file:../../local.db
|
|
717
|
+
`;
|
|
718
|
+
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
|
+
}
|
|
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
|
|
988
|
+
|
|
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);
|
|
992
|
+
}
|
|
993
|
+
async function installDependencies() {
|
|
994
|
+
await execAsync('pnpm install');
|
|
995
|
+
}
|
|
996
|
+
async function setupProjectInDatabase() {
|
|
997
|
+
const s = p.spinner();
|
|
998
|
+
s.start('🚀 Starting development servers and setting up database...');
|
|
999
|
+
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');
|
|
1012
|
+
// Kill the dev servers and their child processes
|
|
1013
|
+
if (devProcess.pid) {
|
|
1014
|
+
try {
|
|
1015
|
+
// Kill the entire process group
|
|
1016
|
+
process.kill(-devProcess.pid, 'SIGTERM');
|
|
1017
|
+
// Wait a moment for graceful shutdown
|
|
1018
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1019
|
+
// Force kill if still running
|
|
1020
|
+
try {
|
|
1021
|
+
process.kill(-devProcess.pid, 'SIGKILL');
|
|
1022
|
+
}
|
|
1023
|
+
catch {
|
|
1024
|
+
// Process already terminated
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
catch (error) {
|
|
1028
|
+
// Process might already be dead, that's fine
|
|
1029
|
+
console.log('Note: Dev servers may still be running in background');
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
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
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
async function setupDatabase() {
|
|
1041
|
+
try {
|
|
1042
|
+
// Run drizzle-kit push to create database file and apply schema
|
|
1043
|
+
await execAsync('pnpm db:push');
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
throw new Error(`Failed to setup database: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
// Export the command function for the CLI
|
|
1050
|
+
export async function createCommand(dirName, options) {
|
|
1051
|
+
await createAgents({
|
|
1052
|
+
dirName,
|
|
1053
|
+
...options,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/create-agents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Create an Inkeep Agent Framework project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,10 +28,13 @@
|
|
|
28
28
|
"author": "Inkeep <support@inkeep.com>",
|
|
29
29
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
30
30
|
"dependencies": {
|
|
31
|
+
"@clack/prompts": "^0.11.0",
|
|
31
32
|
"commander": "^12.0.0",
|
|
32
|
-
"
|
|
33
|
+
"fs-extra": "^11.0.0",
|
|
34
|
+
"picocolors": "^1.0.0"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
37
|
+
"@types/fs-extra": "^11.0.0",
|
|
35
38
|
"@types/node": "^20.12.0",
|
|
36
39
|
"tsx": "^4.7.0",
|
|
37
40
|
"typescript": "^5.4.0",
|