@inkeep/agents-cli 0.1.0

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 (75) hide show
  1. package/LICENSE.md +51 -0
  2. package/README.md +512 -0
  3. package/dist/__tests__/api.test.d.ts +1 -0
  4. package/dist/__tests__/api.test.js +257 -0
  5. package/dist/__tests__/cli.test.d.ts +1 -0
  6. package/dist/__tests__/cli.test.js +153 -0
  7. package/dist/__tests__/commands/config.test.d.ts +1 -0
  8. package/dist/__tests__/commands/config.test.js +154 -0
  9. package/dist/__tests__/commands/init.test.d.ts +1 -0
  10. package/dist/__tests__/commands/init.test.js +186 -0
  11. package/dist/__tests__/commands/pull-retry.test.d.ts +1 -0
  12. package/dist/__tests__/commands/pull-retry.test.js +156 -0
  13. package/dist/__tests__/commands/pull.test.d.ts +1 -0
  14. package/dist/__tests__/commands/pull.test.js +54 -0
  15. package/dist/__tests__/commands/push-spinner.test.d.ts +1 -0
  16. package/dist/__tests__/commands/push-spinner.test.js +127 -0
  17. package/dist/__tests__/commands/push.test.d.ts +1 -0
  18. package/dist/__tests__/commands/push.test.js +265 -0
  19. package/dist/__tests__/config-validation.test.d.ts +1 -0
  20. package/dist/__tests__/config-validation.test.js +106 -0
  21. package/dist/__tests__/package.test.d.ts +1 -0
  22. package/dist/__tests__/package.test.js +82 -0
  23. package/dist/__tests__/utils/json-comparator.test.d.ts +1 -0
  24. package/dist/__tests__/utils/json-comparator.test.js +174 -0
  25. package/dist/__tests__/utils/port-manager.test.d.ts +1 -0
  26. package/dist/__tests__/utils/port-manager.test.js +144 -0
  27. package/dist/__tests__/utils/ts-loader.test.d.ts +1 -0
  28. package/dist/__tests__/utils/ts-loader.test.js +233 -0
  29. package/dist/api.d.ts +23 -0
  30. package/dist/api.js +140 -0
  31. package/dist/commands/chat-enhanced.d.ts +7 -0
  32. package/dist/commands/chat-enhanced.js +396 -0
  33. package/dist/commands/chat.d.ts +5 -0
  34. package/dist/commands/chat.js +125 -0
  35. package/dist/commands/config.d.ts +6 -0
  36. package/dist/commands/config.js +128 -0
  37. package/dist/commands/init.d.ts +5 -0
  38. package/dist/commands/init.js +171 -0
  39. package/dist/commands/list-graphs.d.ts +6 -0
  40. package/dist/commands/list-graphs.js +131 -0
  41. package/dist/commands/mcp-list.d.ts +4 -0
  42. package/dist/commands/mcp-list.js +156 -0
  43. package/dist/commands/mcp-start-simple.d.ts +5 -0
  44. package/dist/commands/mcp-start-simple.js +193 -0
  45. package/dist/commands/mcp-start.d.ts +5 -0
  46. package/dist/commands/mcp-start.js +217 -0
  47. package/dist/commands/mcp-status.d.ts +1 -0
  48. package/dist/commands/mcp-status.js +96 -0
  49. package/dist/commands/mcp-stop.d.ts +5 -0
  50. package/dist/commands/mcp-stop.js +160 -0
  51. package/dist/commands/pull.d.ts +15 -0
  52. package/dist/commands/pull.js +313 -0
  53. package/dist/commands/pull.llm-generate.d.ts +10 -0
  54. package/dist/commands/pull.llm-generate.js +184 -0
  55. package/dist/commands/push.d.ts +6 -0
  56. package/dist/commands/push.js +268 -0
  57. package/dist/config.d.ts +43 -0
  58. package/dist/config.js +292 -0
  59. package/dist/exports.d.ts +2 -0
  60. package/dist/exports.js +2 -0
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +98 -0
  63. package/dist/types/config.d.ts +9 -0
  64. package/dist/types/config.js +3 -0
  65. package/dist/types/graph.d.ts +10 -0
  66. package/dist/types/graph.js +1 -0
  67. package/dist/utils/json-comparator.d.ts +60 -0
  68. package/dist/utils/json-comparator.js +222 -0
  69. package/dist/utils/mcp-runner.d.ts +6 -0
  70. package/dist/utils/mcp-runner.js +147 -0
  71. package/dist/utils/port-manager.d.ts +43 -0
  72. package/dist/utils/port-manager.js +92 -0
  73. package/dist/utils/ts-loader.d.ts +5 -0
  74. package/dist/utils/ts-loader.js +146 -0
  75. package/package.json +77 -0
@@ -0,0 +1,268 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname, extname, resolve } from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+ import chalk from 'chalk';
6
+ import ora from 'ora';
7
+ import inquirer from 'inquirer';
8
+ import { createDatabaseClient, getProject, createProject } from '@inkeep/agents-core';
9
+ import { ManagementApiClient } from '../api.js';
10
+ import { validateConfiguration } from '../config.js';
11
+ export async function pushCommand(graphPath, options) {
12
+ const spinner = ora('Loading graph configuration...').start();
13
+ try {
14
+ // Resolve the absolute path
15
+ const absolutePath = resolve(process.cwd(), graphPath);
16
+ // Check if file exists
17
+ if (!existsSync(absolutePath)) {
18
+ spinner.fail('Graph file not found');
19
+ console.error(chalk.red(`File not found: ${absolutePath}`));
20
+ process.exit(1);
21
+ }
22
+ // Check if this is a TypeScript file
23
+ const ext = extname(absolutePath);
24
+ if (ext === '.ts') {
25
+ // For TypeScript files, we need to use tsx to run this entire command
26
+ // Check if we're already running under tsx
27
+ if (!process.env.TSX_RUNNING) {
28
+ // Stop spinner before spawning child process to avoid conflicts with interactive prompts
29
+ spinner.stop();
30
+ // Re-run this command with tsx
31
+ // Find the inkeep CLI executable
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = dirname(__filename);
34
+ const cliPath = resolve(__dirname, '../index.js');
35
+ const args = [cliPath, 'push', graphPath];
36
+ if (options.tenantId)
37
+ args.push('--tenant-id', options.tenantId);
38
+ if (options.managementApiUrl)
39
+ args.push('--management-api-url', options.managementApiUrl);
40
+ if (options.configFilePath)
41
+ args.push('--config-file-path', options.configFilePath);
42
+ const child = spawn('npx', ['tsx', ...args], {
43
+ cwd: process.cwd(),
44
+ stdio: 'inherit',
45
+ env: { ...process.env, TSX_RUNNING: '1' },
46
+ });
47
+ child.on('error', (error) => {
48
+ console.error(chalk.red('Failed to load TypeScript file'));
49
+ console.error(chalk.red('Error:'), error.message);
50
+ process.exit(1);
51
+ });
52
+ child.on('exit', (code) => {
53
+ process.exit(code || 0);
54
+ });
55
+ return;
56
+ }
57
+ }
58
+ // Now we can import the module (either JS directly, or TS via tsx)
59
+ const fileUrl = pathToFileURL(absolutePath).href;
60
+ const module = await import(fileUrl);
61
+ // Validate that exactly one graph is exported
62
+ const exports = Object.keys(module);
63
+ const graphExports = exports.filter((key) => {
64
+ const value = module[key];
65
+ // Check for AgentGraph-like objects (has required methods)
66
+ return (value &&
67
+ typeof value === 'object' &&
68
+ typeof value.init === 'function' &&
69
+ typeof value.getId === 'function' &&
70
+ typeof value.getName === 'function' &&
71
+ typeof value.getAgents === 'function');
72
+ });
73
+ if (graphExports.length === 0) {
74
+ spinner.fail('No AgentGraph exported from configuration file');
75
+ console.error(chalk.red('Configuration file must export at least one AgentGraph instance'));
76
+ process.exit(1);
77
+ }
78
+ if (graphExports.length > 1) {
79
+ spinner.fail('Multiple AgentGraphs exported from configuration file');
80
+ console.error(chalk.red('Configuration file must export exactly one AgentGraph instance'));
81
+ console.error(chalk.yellow('Found exports:'), graphExports.join(', '));
82
+ process.exit(1);
83
+ }
84
+ // Get the graph instance
85
+ const graphKey = graphExports[0];
86
+ const graph = module[graphKey];
87
+ spinner.text = 'Loading configuration...';
88
+ // Validate configuration
89
+ let config;
90
+ try {
91
+ config = await validateConfiguration(options.tenantId, options.managementApiUrl, undefined, // executionApiUrl not needed for push
92
+ options.configFilePath);
93
+ }
94
+ catch (error) {
95
+ spinner.fail('Configuration validation failed');
96
+ console.error(chalk.red(error.message));
97
+ process.exit(1);
98
+ }
99
+ spinner.succeed('Configuration loaded');
100
+ // Log configuration sources for debugging
101
+ console.log(chalk.gray('Configuration sources:'));
102
+ console.log(chalk.gray(` • Tenant ID: ${config.sources.tenantId}`));
103
+ console.log(chalk.gray(` • Project ID: ${config.sources.projectId}`));
104
+ console.log(chalk.gray(` • API URL: ${config.sources.managementApiUrl}`));
105
+ const tenantId = config.tenantId;
106
+ const projectId = config.projectId;
107
+ const managementApiUrl = config.managementApiUrl;
108
+ // Check if project exists in the database
109
+ spinner.text = 'Validating project...';
110
+ // Use local.db to check if project exists
111
+ let dbUrl = process.env.DB_FILE_NAME || 'local.db';
112
+ // Convert relative path to absolute path for libsql
113
+ if (dbUrl !== ':memory:' &&
114
+ !dbUrl.startsWith('file:') &&
115
+ !dbUrl.startsWith('libsql:') &&
116
+ !dbUrl.startsWith('http')) {
117
+ const absolutePath = resolve(process.cwd(), dbUrl);
118
+ // Validate database file exists
119
+ if (!existsSync(absolutePath)) {
120
+ spinner.fail(`Database file not found: ${absolutePath}`);
121
+ console.error(chalk.red('Please ensure the database file exists or set DB_FILE_NAME environment variable'));
122
+ process.exit(1);
123
+ }
124
+ dbUrl = `file:${absolutePath}`;
125
+ }
126
+ const dbClient = createDatabaseClient({ url: dbUrl });
127
+ const existingProject = await getProject(dbClient)({
128
+ scopes: { tenantId, projectId },
129
+ });
130
+ if (!existingProject) {
131
+ spinner.warn(`Project "${projectId}" does not exist`);
132
+ spinner.stop(); // Stop spinner before prompting
133
+ // Ask user if they want to create the project
134
+ const { shouldCreate } = await inquirer.prompt([
135
+ {
136
+ type: 'confirm',
137
+ name: 'shouldCreate',
138
+ message: `Project "${projectId}" does not exist. Would you like to create it?`,
139
+ default: true,
140
+ },
141
+ ]);
142
+ if (!shouldCreate) {
143
+ console.log(chalk.yellow('Push cancelled. Project must exist before pushing a graph.'));
144
+ process.exit(0);
145
+ }
146
+ // Prompt for project details
147
+ const { projectName, projectDescription } = await inquirer.prompt([
148
+ {
149
+ type: 'input',
150
+ name: 'projectName',
151
+ message: 'Enter a name for the project:',
152
+ default: projectId,
153
+ validate: (input) => {
154
+ if (!input || input.trim() === '') {
155
+ return 'Project name is required';
156
+ }
157
+ return true;
158
+ },
159
+ },
160
+ {
161
+ type: 'input',
162
+ name: 'projectDescription',
163
+ message: 'Enter a description for the project (optional):',
164
+ default: '',
165
+ },
166
+ ]);
167
+ // Create the project
168
+ spinner.start('Creating project...');
169
+ try {
170
+ await createProject(dbClient)({
171
+ id: projectId,
172
+ tenantId: tenantId,
173
+ name: projectName,
174
+ description: projectDescription || 'No description provided',
175
+ });
176
+ spinner.succeed(`Project "${projectName}" created successfully`);
177
+ }
178
+ catch (error) {
179
+ spinner.fail('Failed to create project');
180
+ console.error(chalk.red('Error:'), error.message);
181
+ process.exit(1);
182
+ }
183
+ }
184
+ else {
185
+ spinner.succeed(`Project "${existingProject.name || projectId}" validated`);
186
+ }
187
+ // Create API client with validated configuration (not used directly since graph handles its own API calls)
188
+ await ManagementApiClient.create(config.managementApiUrl, options.configFilePath, config.tenantId);
189
+ // Inject configuration into the graph
190
+ if (typeof graph.setConfig === 'function') {
191
+ graph.setConfig(tenantId, projectId, managementApiUrl);
192
+ }
193
+ spinner.start('Initializing graph...');
194
+ // Initialize the graph (this will push to the backend)
195
+ await graph.init();
196
+ // Get graph details
197
+ const graphId = graph.getId();
198
+ const graphName = graph.getName();
199
+ const stats = graph.getStats();
200
+ spinner.succeed(`Graph "${graphName}" (${graphId}) pushed successfully`);
201
+ // Validate for dangling resources
202
+ const agents = graph.getAgents();
203
+ const warnings = [];
204
+ // Check for agents not referenced in any relationships
205
+ for (const agent of agents) {
206
+ const agentName = agent.getName();
207
+ let isReferenced = false;
208
+ // Check if this agent is referenced by any other agent
209
+ for (const otherAgent of agents) {
210
+ if (otherAgent === agent)
211
+ continue;
212
+ // Check transfers (only for internal agents)
213
+ if (typeof otherAgent.getTransfers === 'function') {
214
+ const transfers = otherAgent.getTransfers();
215
+ if (transfers.some((h) => h.getName() === agentName)) {
216
+ isReferenced = true;
217
+ break;
218
+ }
219
+ }
220
+ // Check delegates (only for internal agents)
221
+ if (typeof otherAgent.getDelegates === 'function') {
222
+ const delegates = otherAgent.getDelegates();
223
+ if (delegates.some((d) => d.getName() === agentName)) {
224
+ isReferenced = true;
225
+ break;
226
+ }
227
+ }
228
+ }
229
+ // Check if it's the default agent
230
+ const defaultAgent = graph.getDefaultAgent();
231
+ if (defaultAgent && defaultAgent.getName() === agentName) {
232
+ isReferenced = true;
233
+ }
234
+ if (!isReferenced && agents.length > 1) {
235
+ warnings.push(`Agent "${agentName}" is not referenced in any transfer or delegation relationships`);
236
+ }
237
+ }
238
+ // Display warnings if any
239
+ if (warnings.length > 0) {
240
+ console.log(chalk.yellow('\n⚠️ Warnings:'));
241
+ warnings.forEach((warning) => {
242
+ console.log(chalk.yellow(` • ${warning}`));
243
+ });
244
+ }
245
+ // Display summary
246
+ console.log(chalk.cyan('\n📊 Graph Summary:'));
247
+ console.log(chalk.gray(` • Graph ID: ${graphId}`));
248
+ console.log(chalk.gray(` • Name: ${graphName}`));
249
+ console.log(chalk.gray(` • Agents: ${stats.agentCount}`));
250
+ console.log(chalk.gray(` • Tools: ${stats.toolCount}`));
251
+ console.log(chalk.gray(` • Relations: ${stats.relationCount}`));
252
+ // Provide next steps
253
+ console.log(chalk.green('\n✨ Next steps:'));
254
+ console.log(chalk.gray(` • Test your graph: inkeep chat ${graphId}`));
255
+ console.log(chalk.gray(` • View all graphs: inkeep list-graphs`));
256
+ console.log(chalk.gray(` • Get graph details: inkeep get-graph ${graphId}`));
257
+ // Force exit to avoid hanging due to OpenTelemetry or other background tasks
258
+ process.exit(0);
259
+ }
260
+ catch (error) {
261
+ spinner.fail('Failed to push graph');
262
+ console.error(chalk.red('Error:'), error.message);
263
+ if (error.stack && process.env.DEBUG) {
264
+ console.error(chalk.gray(error.stack));
265
+ }
266
+ process.exit(1);
267
+ }
268
+ }
@@ -0,0 +1,43 @@
1
+ import type { ModelSettings } from '@inkeep/agents-core';
2
+ export interface InkeepConfig {
3
+ tenantId?: string;
4
+ projectId?: string;
5
+ managementApiUrl?: string;
6
+ executionApiUrl?: string;
7
+ outputDirectory?: string;
8
+ modelSettings?: ModelSettings;
9
+ }
10
+ export interface ValidatedConfiguration {
11
+ tenantId: string;
12
+ projectId: string;
13
+ managementApiUrl: string;
14
+ executionApiUrl: string;
15
+ outputDirectory?: string;
16
+ modelSettings?: ModelSettings;
17
+ sources: {
18
+ tenantId: string;
19
+ projectId: string;
20
+ managementApiUrl: string;
21
+ executionApiUrl: string;
22
+ configFile?: string;
23
+ };
24
+ }
25
+ export declare function loadConfig(configPath?: string): Promise<InkeepConfig>;
26
+ export declare function getTenantId(configPath?: string): Promise<string | undefined>;
27
+ export declare function getProjectId(configPath?: string): Promise<string>;
28
+ export declare function getManagementApiUrl(overrideUrl?: string, configPath?: string): Promise<string>;
29
+ export declare function getExecutionApiUrl(overrideUrl?: string, configPath?: string): Promise<string>;
30
+ /**
31
+ * Validates configuration according to these rules:
32
+ * 1. If --config-file-path is provided, use it (cannot be combined with --tenant-id)
33
+ * 2. If --tenant-id AND --management-api-url AND --execution-api-url are provided, use them (cannot be combined with --config-file-path)
34
+ * 3. If only --management-api-url and --execution-api-url are provided, it overrides the managementApiUrl and executionApiUrl from default config
35
+ * 4. Otherwise, look for default config file (inkeep.config.ts)
36
+ *
37
+ * @param tenantIdFlag - tenantId from command line flag
38
+ * @param managementApiUrlFlag - managementApiUrl from command line flag
39
+ * @param executionApiUrlFlag - executionApiUrl from command line flag
40
+ * @param configFilePath - explicit path to config file
41
+ * @returns configuration with tenantId, managementApiUrl, and sources used
42
+ */
43
+ export declare function validateConfiguration(tenantIdFlag?: string, managementApiUrlFlag?: string, executionApiUrlFlag?: string, configFilePath?: string): Promise<ValidatedConfiguration>;
package/dist/config.js ADDED
@@ -0,0 +1,292 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import dotenv from 'dotenv';
5
+ // Load .env file from current directory if it exists
6
+ dotenv.config({ quiet: true });
7
+ /**
8
+ * Search for config file in current directory and parent directories
9
+ */
10
+ function findConfigFile(startPath = process.cwd()) {
11
+ let currentPath = resolve(startPath);
12
+ const root = '/';
13
+ const configNames = ['inkeep.config.ts', 'inkeep.config.js', '.inkeeprc.ts', '.inkeeprc.js'];
14
+ while (currentPath !== root) {
15
+ // Check for config files at this level
16
+ for (const configName of configNames) {
17
+ const configPath = join(currentPath, configName);
18
+ if (existsSync(configPath)) {
19
+ return configPath;
20
+ }
21
+ }
22
+ const parentPath = dirname(currentPath);
23
+ if (parentPath === currentPath) {
24
+ break; // Reached filesystem root
25
+ }
26
+ currentPath = parentPath;
27
+ }
28
+ return null;
29
+ }
30
+ async function loadConfigFromFile(configPath) {
31
+ let resolvedPath;
32
+ if (configPath) {
33
+ // User specified a config path
34
+ resolvedPath = resolve(process.cwd(), configPath);
35
+ if (!existsSync(resolvedPath)) {
36
+ throw new Error(`Config file not found: ${resolvedPath}`);
37
+ }
38
+ }
39
+ else {
40
+ // Search for config file
41
+ resolvedPath = findConfigFile();
42
+ if (!resolvedPath) {
43
+ // No config file found
44
+ return null;
45
+ }
46
+ }
47
+ try {
48
+ const fileUrl = pathToFileURL(resolvedPath).href;
49
+ const module = await import(fileUrl);
50
+ // Support both default export and named export
51
+ const config = module.default || module.config;
52
+ if (!config) {
53
+ throw new Error(`No config exported from ${resolvedPath}`);
54
+ }
55
+ return config;
56
+ }
57
+ catch (error) {
58
+ // If loading a TypeScript file fails, try using the ts-loader utility
59
+ try {
60
+ const { loadTypeScriptModule } = await import('./utils/ts-loader.js');
61
+ const module = await loadTypeScriptModule(resolvedPath);
62
+ return module.default || module.config || null;
63
+ }
64
+ catch {
65
+ console.warn(`Warning: Failed to load config file ${resolvedPath}:`, error);
66
+ return null;
67
+ }
68
+ }
69
+ }
70
+ export async function loadConfig(configPath) {
71
+ // Default config
72
+ const config = {
73
+ managementApiUrl: 'http://localhost:3002',
74
+ executionApiUrl: 'http://localhost:3003',
75
+ };
76
+ // Try to load from inkeep.config.ts or specified config file
77
+ const fileConfig = await loadConfigFromFile(configPath);
78
+ if (fileConfig) {
79
+ Object.assign(config, fileConfig);
80
+ }
81
+ // Override with environment variables if present
82
+ if (process.env.INKEEP_TENANT_ID) {
83
+ config.tenantId = process.env.INKEEP_TENANT_ID;
84
+ }
85
+ if (process.env.INKEEP_PROJECT_ID) {
86
+ config.projectId = process.env.INKEEP_PROJECT_ID;
87
+ }
88
+ if (process.env.INKEEP_MANAGEMENT_API_URL) {
89
+ config.managementApiUrl = process.env.INKEEP_MANAGEMENT_API_URL;
90
+ }
91
+ if (process.env.INKEEP_EXECUTION_API_URL) {
92
+ config.executionApiUrl = process.env.INKEEP_EXECUTION_API_URL;
93
+ }
94
+ if (process.env.INKEEP_EXECUTION_API_URL) {
95
+ config.executionApiUrl = process.env.INKEEP_EXECUTION_API_URL;
96
+ }
97
+ return config;
98
+ }
99
+ export async function getTenantId(configPath) {
100
+ const config = await loadConfig(configPath);
101
+ return config.tenantId;
102
+ }
103
+ export async function getProjectId(configPath) {
104
+ const config = await loadConfig(configPath);
105
+ return config.projectId || 'default';
106
+ }
107
+ export async function getManagementApiUrl(overrideUrl, configPath) {
108
+ // Priority: override > config/env > default
109
+ if (overrideUrl) {
110
+ return overrideUrl;
111
+ }
112
+ const config = await loadConfig(configPath);
113
+ return config.managementApiUrl || 'http://localhost:3002';
114
+ }
115
+ export async function getExecutionApiUrl(overrideUrl, configPath) {
116
+ // Priority: override > config/env > default
117
+ if (overrideUrl) {
118
+ return overrideUrl;
119
+ }
120
+ const config = await loadConfig(configPath);
121
+ return config.executionApiUrl || 'http://localhost:3003';
122
+ }
123
+ /**
124
+ * Validates configuration according to these rules:
125
+ * 1. If --config-file-path is provided, use it (cannot be combined with --tenant-id)
126
+ * 2. If --tenant-id AND --management-api-url AND --execution-api-url are provided, use them (cannot be combined with --config-file-path)
127
+ * 3. If only --management-api-url and --execution-api-url are provided, it overrides the managementApiUrl and executionApiUrl from default config
128
+ * 4. Otherwise, look for default config file (inkeep.config.ts)
129
+ *
130
+ * @param tenantIdFlag - tenantId from command line flag
131
+ * @param managementApiUrlFlag - managementApiUrl from command line flag
132
+ * @param executionApiUrlFlag - executionApiUrl from command line flag
133
+ * @param configFilePath - explicit path to config file
134
+ * @returns configuration with tenantId, managementApiUrl, and sources used
135
+ */
136
+ export async function validateConfiguration(tenantIdFlag, managementApiUrlFlag, executionApiUrlFlag, configFilePath) {
137
+ // Validation: Cannot combine --config-file-path with --tenant-id
138
+ if (configFilePath && tenantIdFlag) {
139
+ throw new Error('Invalid configuration combination:\n' +
140
+ 'Cannot use --config-file-path with --tenant-id.\n' +
141
+ 'Please use either:\n' +
142
+ ' 1. --config-file-path alone\n' +
143
+ ' 2. --tenant-id with --management-api-url and --execution-api-url\n' +
144
+ ' 3. Default config file (inkeep.config.ts)');
145
+ }
146
+ // Case 1: Explicit config file path provided
147
+ if (configFilePath) {
148
+ const config = await loadConfig(configFilePath);
149
+ const tenantId = config.tenantId;
150
+ const projectId = config.projectId || 'default';
151
+ const managementApiUrl = managementApiUrlFlag || config.managementApiUrl; // Allow ---management-api-url to override
152
+ const executionApiUrl = executionApiUrlFlag || config.executionApiUrl; // Allow --execution-api-url to override
153
+ if (!tenantId) {
154
+ throw new Error(`Tenant ID is missing from configuration file: ${configFilePath}\n` +
155
+ 'Please ensure your config file exports a valid configuration with tenantId.');
156
+ }
157
+ if (!managementApiUrl) {
158
+ throw new Error(`Management API URL is missing from configuration file: ${configFilePath}\n` +
159
+ 'Please ensure your config file exports a valid configuration with managementApiUrl.');
160
+ }
161
+ if (!executionApiUrl) {
162
+ throw new Error(`Execution API URL is missing from configuration file: ${configFilePath}\n` +
163
+ 'Please ensure your config file exports a valid configuration with executionApiUrl.');
164
+ }
165
+ const sources = {
166
+ tenantId: `config file (${configFilePath})`,
167
+ projectId: config.projectId ? `config file (${configFilePath})` : 'default',
168
+ managementApiUrl: managementApiUrlFlag
169
+ ? 'command-line flag (--management-api-url)'
170
+ : `config file (${configFilePath})`,
171
+ executionApiUrl: executionApiUrlFlag
172
+ ? 'command-line flag (--execution-api-url)'
173
+ : `config file (${configFilePath})`,
174
+ configFile: configFilePath,
175
+ };
176
+ return {
177
+ tenantId,
178
+ projectId,
179
+ managementApiUrl,
180
+ executionApiUrl,
181
+ modelSettings: config.modelSettings || undefined,
182
+ outputDirectory: config.outputDirectory,
183
+ sources,
184
+ };
185
+ }
186
+ // Case 2: Both --tenant-id and --management-api-url and --execution-api-url provided
187
+ if (tenantIdFlag && managementApiUrlFlag && executionApiUrlFlag) {
188
+ const sources = {
189
+ tenantId: 'command-line flag (--tenant-id)',
190
+ projectId: 'default',
191
+ managementApiUrl: 'command-line flag (--management-api-url)',
192
+ executionApiUrl: 'command-line flag (--execution-api-url)',
193
+ };
194
+ return {
195
+ tenantId: tenantIdFlag,
196
+ projectId: 'default',
197
+ managementApiUrl: managementApiUrlFlag,
198
+ executionApiUrl: executionApiUrlFlag,
199
+ modelSettings: undefined,
200
+ sources,
201
+ };
202
+ }
203
+ // Case 3: Only --tenant-id provided (invalid)
204
+ if (tenantIdFlag && !managementApiUrlFlag && !executionApiUrlFlag) {
205
+ throw new Error('Invalid configuration:\n' +
206
+ '--tenant-id requires --management-api-url and --execution-api-url to be provided as well.\n' +
207
+ 'Please provide both --tenant-id and --management-api-url and --execution-api-url together.');
208
+ }
209
+ // Case 4: Try to load from default config file
210
+ const config = await loadConfig();
211
+ const tenantId = config.tenantId;
212
+ const projectId = config.projectId || 'default';
213
+ const managementApiUrl = managementApiUrlFlag || config.managementApiUrl; // Allow --management-api-url to override
214
+ const executionApiUrl = executionApiUrlFlag || config.executionApiUrl; // Allow --execution-api-url to override
215
+ if (!tenantId) {
216
+ // Check if a default config file exists
217
+ const configFile = findConfigFile();
218
+ if (!configFile) {
219
+ throw new Error('No configuration found. Please use one of:\n' +
220
+ ' 1. Create "inkeep.config.ts" by running "inkeep init"\n' +
221
+ ' 2. Provide --config-file-path to specify a config file\n' +
222
+ ' 3. Provide both --tenant-id and --management-api-url and --execution-api-url flags\n' +
223
+ ' 4. Set INKEEP_TENANT_ID and INKEEP_API_URL environment variables');
224
+ }
225
+ else {
226
+ throw new Error(`Tenant ID is missing from configuration file: ${configFile}\n` +
227
+ 'Please either:\n' +
228
+ ' 1. Update your configuration file with a tenantId\n' +
229
+ ' 2. Provide both --tenant-id and --management-api-url and --execution-api-url flags\n' +
230
+ ' 3. Set INKEEP_TENANT_ID environment variable');
231
+ }
232
+ }
233
+ if (!managementApiUrl) {
234
+ throw new Error('Management API URL is missing. Please either:\n' +
235
+ ' 1. Provide --management-api-url flag\n' +
236
+ ' 2. Set INKEEP_MANAGEMENT_API_URL environment variable\n' +
237
+ ' 3. Add managementApiUrl to your configuration file');
238
+ }
239
+ if (!executionApiUrl) {
240
+ throw new Error('Execution API URL is missing. Please either:\n' +
241
+ ' 1. Provide --execution-api-url flag\n' +
242
+ ' 2. Set INKEEP_EXECUTION_API_URL environment variable\n' +
243
+ ' 3. Add executionApiUrl to your configuration file');
244
+ }
245
+ // Determine sources for Case 4
246
+ const configFile = findConfigFile();
247
+ let tenantIdSource = configFile ? `config file (${configFile})` : 'unknown';
248
+ let managementApiUrlSource = configFile ? `config file (${configFile})` : 'default';
249
+ let executionApiUrlSource = configFile ? `config file (${configFile})` : 'default';
250
+ // Check if environment variables were used
251
+ if (process.env.INKEEP_TENANT_ID === tenantId) {
252
+ tenantIdSource = 'environment variable (INKEEP_TENANT_ID)';
253
+ }
254
+ if (managementApiUrlFlag) {
255
+ managementApiUrlSource = 'command-line flag (--management-api-url)';
256
+ }
257
+ else if (process.env.INKEEP_MANAGEMENT_API_URL === managementApiUrl) {
258
+ managementApiUrlSource = 'environment variable (INKEEP_MANAGEMENT_API_URL)';
259
+ }
260
+ else if (managementApiUrl === 'http://localhost:3002' && !configFile) {
261
+ managementApiUrlSource = 'default value';
262
+ }
263
+ if (executionApiUrlFlag) {
264
+ executionApiUrlSource = 'command-line flag (--execution-api-url)';
265
+ }
266
+ else if (process.env.INKEEP_EXECUTION_API_URL === executionApiUrl) {
267
+ executionApiUrlSource = 'environment variable (INKEEP_EXECUTION_API_URL)';
268
+ }
269
+ else if (executionApiUrl === 'http://localhost:3003' && !configFile) {
270
+ executionApiUrlSource = 'default value';
271
+ }
272
+ const sources = {
273
+ tenantId: tenantIdSource,
274
+ projectId: config.projectId
275
+ ? configFile
276
+ ? `config file (${configFile})`
277
+ : 'config'
278
+ : 'default',
279
+ managementApiUrl: managementApiUrlSource,
280
+ executionApiUrl: executionApiUrlSource,
281
+ configFile: configFile || undefined,
282
+ };
283
+ return {
284
+ tenantId,
285
+ projectId,
286
+ managementApiUrl,
287
+ executionApiUrl,
288
+ modelSettings: config.modelSettings || undefined,
289
+ outputDirectory: config.outputDirectory,
290
+ sources,
291
+ };
292
+ }
@@ -0,0 +1,2 @@
1
+ export type { InkeepConfig } from './types/config.js';
2
+ export { defineConfig } from './types/config.js';
@@ -0,0 +1,2 @@
1
+ // Re-export configuration utilities for use in user's config files
2
+ export { defineConfig } from './types/config.js';
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};