@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,92 @@
1
+ import { createServer } from 'node:net';
2
+ /**
3
+ * Manages dynamic port allocation for local MCP servers
4
+ */
5
+ export class PortManager {
6
+ static instance;
7
+ static BASE_PORT = 3100;
8
+ static MAX_PORT = 3200;
9
+ allocatedPorts = new Set();
10
+ constructor() { }
11
+ static getInstance() {
12
+ if (!PortManager.instance) {
13
+ PortManager.instance = new PortManager();
14
+ }
15
+ return PortManager.instance;
16
+ }
17
+ /**
18
+ * Allocate an available port in the configured range
19
+ */
20
+ async allocatePort(preferredPort) {
21
+ // If a preferred port is provided and available, use it
22
+ if (preferredPort) {
23
+ const isAvailable = await this.isPortAvailable(preferredPort);
24
+ if (isAvailable && !this.allocatedPorts.has(preferredPort)) {
25
+ this.allocatedPorts.add(preferredPort);
26
+ return preferredPort;
27
+ }
28
+ }
29
+ // Find next available port in range
30
+ for (let port = PortManager.BASE_PORT; port <= PortManager.MAX_PORT; port++) {
31
+ if (this.allocatedPorts.has(port)) {
32
+ continue;
33
+ }
34
+ const isAvailable = await this.isPortAvailable(port);
35
+ if (isAvailable) {
36
+ this.allocatedPorts.add(port);
37
+ return port;
38
+ }
39
+ }
40
+ throw new Error(`No available ports in range ${PortManager.BASE_PORT}-${PortManager.MAX_PORT}`);
41
+ }
42
+ /**
43
+ * Release a previously allocated port
44
+ */
45
+ releasePort(port) {
46
+ this.allocatedPorts.delete(port);
47
+ }
48
+ /**
49
+ * Release all allocated ports
50
+ */
51
+ releaseAll() {
52
+ this.allocatedPorts.clear();
53
+ }
54
+ /**
55
+ * Check if a specific port is available
56
+ */
57
+ async isPortAvailable(port) {
58
+ return new Promise((resolve) => {
59
+ const server = createServer();
60
+ server.once('error', () => {
61
+ resolve(false);
62
+ });
63
+ server.once('listening', () => {
64
+ server.close(() => {
65
+ resolve(true);
66
+ });
67
+ });
68
+ server.listen(port, '127.0.0.1');
69
+ });
70
+ }
71
+ /**
72
+ * Get list of currently allocated ports
73
+ */
74
+ getAllocatedPorts() {
75
+ return Array.from(this.allocatedPorts).sort((a, b) => a - b);
76
+ }
77
+ /**
78
+ * Get port allocation statistics
79
+ */
80
+ getStats() {
81
+ const totalPorts = PortManager.MAX_PORT - PortManager.BASE_PORT + 1;
82
+ return {
83
+ allocated: this.allocatedPorts.size,
84
+ available: totalPorts - this.allocatedPorts.size,
85
+ range: {
86
+ min: PortManager.BASE_PORT,
87
+ max: PortManager.MAX_PORT,
88
+ },
89
+ ports: this.getAllocatedPorts(),
90
+ };
91
+ }
92
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Loads a TypeScript module and returns its exports
3
+ * This works by creating a temporary loader script that tsx can execute
4
+ */
5
+ export declare function loadTypeScriptModule(filePath: string): Promise<any>;
@@ -0,0 +1,146 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ /**
7
+ * Loads a TypeScript module and returns its exports
8
+ * This works by creating a temporary loader script that tsx can execute
9
+ */
10
+ export async function loadTypeScriptModule(filePath) {
11
+ const tempDir = join(tmpdir(), 'inkeep-cli-loader');
12
+ mkdirSync(tempDir, { recursive: true });
13
+ const loaderPath = join(tempDir, 'loader.mjs');
14
+ const outputPath = join(tempDir, 'output.json');
15
+ // Create a loader script that will import the module and serialize its exports
16
+ const loaderScript = `
17
+ import { writeFileSync } from 'fs';
18
+
19
+ async function loadModule() {
20
+ try {
21
+ const module = await import('${pathToFileURL(filePath).href}');
22
+
23
+ // Serialize the module exports (functions can't be serialized, so we just mark them)
24
+ const serializable = {};
25
+
26
+ for (const [key, value] of Object.entries(module)) {
27
+ if (typeof value === 'function') {
28
+ // Mark functions
29
+ serializable[key] = { __type: 'function', name: value.name || key };
30
+ } else if (value && typeof value === 'object') {
31
+ // For objects, check if they have specific methods/properties
32
+ const obj = { __type: 'object' };
33
+
34
+ // Check for config-like properties (for inkeep.config.ts files)
35
+ if (value.tenantId) obj.tenantId = value.tenantId;
36
+ if (value.projectId) obj.projectId = value.projectId;
37
+ if (value.apiUrl) obj.apiUrl = value.apiUrl;
38
+ if (value.outputDirectory) obj.outputDirectory = value.outputDirectory;
39
+ if (value.modelSettings) obj.modelSettings = value.modelSettings;
40
+
41
+ // Check for tool-like properties
42
+ if (value.id) obj.id = value.id;
43
+ if (value.name) obj.name = value.name;
44
+ if (value.description) obj.description = value.description;
45
+ if (value.serverUrl) obj.serverUrl = value.serverUrl;
46
+ if (value.port) obj.port = value.port;
47
+ if (value.deployment) obj.deployment = value.deployment;
48
+ if (value.transport) obj.transport = value.transport;
49
+ if (typeof value.execute === 'function') obj.hasExecute = true;
50
+ if (typeof value.init === 'function') obj.hasInit = true;
51
+ if (typeof value.getServerUrl === 'function') obj.hasGetServerUrl = true;
52
+ if (typeof value.getId === 'function') {
53
+ try {
54
+ obj.graphId = value.getId();
55
+ } catch {}
56
+ }
57
+
58
+ // Check if it's an array
59
+ if (Array.isArray(value)) {
60
+ obj.__type = 'array';
61
+ obj.items = value.map(item => {
62
+ if (item && typeof item === 'object') {
63
+ const itemObj = {};
64
+ if (item.id) itemObj.id = item.id;
65
+ if (item.name) itemObj.name = item.name;
66
+ if (item.description) itemObj.description = item.description;
67
+ if (item.serverUrl) itemObj.serverUrl = item.serverUrl;
68
+ if (item.port) itemObj.port = item.port;
69
+ if (item.deployment) itemObj.deployment = item.deployment;
70
+ if (item.transport) itemObj.transport = item.transport;
71
+ if (typeof item.execute === 'function') itemObj.hasExecute = true;
72
+ if (typeof item.init === 'function') itemObj.hasInit = true;
73
+ if (typeof item.getServerUrl === 'function') itemObj.hasGetServerUrl = true;
74
+ return itemObj;
75
+ }
76
+ return item;
77
+ });
78
+ }
79
+
80
+ serializable[key] = obj;
81
+ } else {
82
+ serializable[key] = value;
83
+ }
84
+ }
85
+
86
+ writeFileSync('${outputPath}', JSON.stringify(serializable, null, 2));
87
+ process.exit(0);
88
+ } catch (error) {
89
+ console.error('Failed to load module:', error.message);
90
+ writeFileSync('${outputPath}', JSON.stringify({ __error: error.message }));
91
+ process.exit(1);
92
+ }
93
+ }
94
+
95
+ loadModule();
96
+ `;
97
+ try {
98
+ // Write the loader script
99
+ writeFileSync(loaderPath, loaderScript);
100
+ // Execute the loader script with tsx, passing environment variables
101
+ // Load the .env file from the graph directory if it exists
102
+ const graphDir = dirname(filePath);
103
+ const envPath = join(graphDir, '..', '.env');
104
+ const envVars = { ...process.env };
105
+ // If no environment variables are set, use test defaults
106
+ if (!process.env.ENVIRONMENT) {
107
+ envVars.ENVIRONMENT = 'test';
108
+ envVars.DB_FILE_NAME = ':memory:'; // Use in-memory DB for CLI testing
109
+ envVars.INKEEP_TENANT_ID = 'test-tenant';
110
+ envVars.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || 'test-key';
111
+ }
112
+ // Use node directly with tsx for better performance in CI
113
+ // Try to find tsx in node_modules first for better performance
114
+ const tsxPath = join(process.cwd(), 'node_modules', '.bin', 'tsx');
115
+ const command = require('fs').existsSync(tsxPath) ? tsxPath : 'tsx';
116
+ execFileSync(command, [loaderPath], {
117
+ cwd: process.cwd(), // Use cwd which is in the pnpm workspace
118
+ stdio: 'pipe',
119
+ encoding: 'utf-8',
120
+ env: envVars,
121
+ timeout: 15000, // Reduced to 15 second timeout for faster failure detection
122
+ windowsHide: true, // Hide console window on Windows
123
+ });
124
+ // Read the output
125
+ const outputJson = readFileSync(outputPath, 'utf-8');
126
+ const output = JSON.parse(outputJson);
127
+ // Clean up with retries to handle file locks
128
+ rmSync(tempDir, { recursive: true, force: true, maxRetries: 3 });
129
+ if (output.__error) {
130
+ throw new Error(output.__error);
131
+ }
132
+ return output;
133
+ }
134
+ catch (error) {
135
+ // Clean up on error
136
+ try {
137
+ rmSync(tempDir, { recursive: true, force: true, maxRetries: 3 });
138
+ }
139
+ catch { }
140
+ // Add more context to timeout errors
141
+ if (error.code === 'ETIMEDOUT' || error.message?.includes('timed out')) {
142
+ throw new Error(`TypeScript module loading timed out for ${filePath}: ${error.message}`);
143
+ }
144
+ throw error;
145
+ }
146
+ }
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@inkeep/agents-cli",
3
+ "version": "0.1.0",
4
+ "description": "Inkeep CLI tool",
5
+ "type": "module",
6
+ "main": "./dist/exports.js",
7
+ "bin": {
8
+ "inkeep": "./dist/index.js"
9
+ },
10
+ "exports": {
11
+ ".": "./dist/exports.js",
12
+ "./config": "./dist/exports.js"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "start": "node ./dist/index.js",
18
+ "test": "node -e \"process.exit(process.env.CI ? 0 : 1)\" && vitest --run --config vitest.config.ci.ts || vitest --run",
19
+ "test:debug": "vitest --run --reporter=verbose --no-coverage",
20
+ "test:watch": "vitest",
21
+ "test:coverage": "node -e \"process.exit(process.env.CI ? 0 : 1)\" && vitest --run --coverage --config vitest.config.ci.ts || vitest --run --coverage",
22
+ "test:ci": "vitest --run --config vitest.config.ci.ts",
23
+ "typecheck": "tsc --noEmit --project tsconfig.typecheck.json"
24
+ },
25
+ "keywords": [
26
+ "cli",
27
+ "inkeep",
28
+ "agent"
29
+ ],
30
+ "author": "Inkeep <support@inkeep.com>",
31
+ "license": "SEE LICENSE IN LICENSE.md",
32
+ "dependencies": {
33
+ "@ai-sdk/anthropic": "2.0.2",
34
+ "@ai-sdk/openai": "2.0.11",
35
+ "@babel/parser": "^7.23.0",
36
+ "@babel/types": "^7.23.0",
37
+ "@inkeep/agents-core": "workspace:*",
38
+ "@libsql/client": "^0.15.7",
39
+ "ai": "5.0.11",
40
+ "ast-types": "^0.14.2",
41
+ "chalk": "^5.3.0",
42
+ "cli-table3": "^0.6.3",
43
+ "commander": "^14.0.0",
44
+ "dotenv": "^17.2.1",
45
+ "drizzle-orm": "^0.37.0",
46
+ "inquirer": "^9.2.12",
47
+ "inquirer-autocomplete-prompt": "^3.0.1",
48
+ "ora": "^8.0.1",
49
+ "recast": "^0.23.0",
50
+ "ts-morph": "^26.0.0"
51
+ },
52
+ "devDependencies": {
53
+ "@types/inquirer": "^9.0.7",
54
+ "@types/node": "^20.10.0",
55
+ "@vitest/coverage-v8": "^3.2.4",
56
+ "tsx": "^4.20.5",
57
+ "typescript": "^5.9.2",
58
+ "vitest": "^3.2.4"
59
+ },
60
+ "engines": {
61
+ "node": ">=20.x"
62
+ },
63
+ "publishConfig": {
64
+ "access": "restricted",
65
+ "registry": "https://registry.npmjs.org/"
66
+ },
67
+ "files": [
68
+ "dist",
69
+ "README.md",
70
+ "LICENSE.md"
71
+ ],
72
+ "repository": {
73
+ "type": "git",
74
+ "url": "git+https://github.com/inkeep/agent-framework.git",
75
+ "directory": "agents-cli"
76
+ }
77
+ }