@inkeep/agents-manage-api 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 (110) hide show
  1. package/README.md +176 -0
  2. package/dist/ManagementServer.d.ts +28 -0
  3. package/dist/ManagementServer.d.ts.map +1 -0
  4. package/dist/ManagementServer.js +41 -0
  5. package/dist/__tests__/setup.d.ts +2 -0
  6. package/dist/__tests__/setup.d.ts.map +1 -0
  7. package/dist/__tests__/setup.js +26 -0
  8. package/dist/__tests__/utils/testProject.d.ts +18 -0
  9. package/dist/__tests__/utils/testProject.d.ts.map +1 -0
  10. package/dist/__tests__/utils/testProject.js +26 -0
  11. package/dist/__tests__/utils/testRequest.d.ts +2 -0
  12. package/dist/__tests__/utils/testRequest.d.ts.map +1 -0
  13. package/dist/__tests__/utils/testRequest.js +11 -0
  14. package/dist/__tests__/utils/testTenant.d.ts +64 -0
  15. package/dist/__tests__/utils/testTenant.d.ts.map +1 -0
  16. package/dist/__tests__/utils/testTenant.js +71 -0
  17. package/dist/app.d.ts +4 -0
  18. package/dist/app.d.ts.map +1 -0
  19. package/dist/app.js +140 -0
  20. package/dist/data/conversations.d.ts +59 -0
  21. package/dist/data/conversations.d.ts.map +1 -0
  22. package/dist/data/conversations.js +216 -0
  23. package/dist/data/db/clean.d.ts +6 -0
  24. package/dist/data/db/clean.d.ts.map +1 -0
  25. package/dist/data/db/clean.js +77 -0
  26. package/dist/data/db/dbClient.d.ts +3 -0
  27. package/dist/data/db/dbClient.d.ts.map +1 -0
  28. package/dist/data/db/dbClient.js +13 -0
  29. package/dist/data/graphFull.d.ts +11 -0
  30. package/dist/data/graphFull.d.ts.map +1 -0
  31. package/dist/data/graphFull.js +90 -0
  32. package/dist/data/graphFullClient.d.ts +22 -0
  33. package/dist/data/graphFullClient.d.ts.map +1 -0
  34. package/dist/data/graphFullClient.js +189 -0
  35. package/dist/data/tools.d.ts +81 -0
  36. package/dist/data/tools.d.ts.map +1 -0
  37. package/dist/data/tools.js +266 -0
  38. package/dist/env.d.ts +41 -0
  39. package/dist/env.d.ts.map +1 -0
  40. package/dist/env.js +59 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +42 -0
  44. package/dist/logger.d.ts +4 -0
  45. package/dist/logger.d.ts.map +1 -0
  46. package/dist/logger.js +32 -0
  47. package/dist/middleware/auth.d.ts +12 -0
  48. package/dist/middleware/auth.d.ts.map +1 -0
  49. package/dist/middleware/auth.js +36 -0
  50. package/dist/openapi.d.ts +2 -0
  51. package/dist/openapi.d.ts.map +1 -0
  52. package/dist/openapi.js +38 -0
  53. package/dist/routes/agentArtifactComponents.d.ts +4 -0
  54. package/dist/routes/agentArtifactComponents.d.ts.map +1 -0
  55. package/dist/routes/agentArtifactComponents.js +230 -0
  56. package/dist/routes/agentDataComponents.d.ts +4 -0
  57. package/dist/routes/agentDataComponents.d.ts.map +1 -0
  58. package/dist/routes/agentDataComponents.js +225 -0
  59. package/dist/routes/agentGraph.d.ts +4 -0
  60. package/dist/routes/agentGraph.d.ts.map +1 -0
  61. package/dist/routes/agentGraph.js +289 -0
  62. package/dist/routes/agentRelations.d.ts +4 -0
  63. package/dist/routes/agentRelations.d.ts.map +1 -0
  64. package/dist/routes/agentRelations.js +290 -0
  65. package/dist/routes/agentToolRelations.d.ts +4 -0
  66. package/dist/routes/agentToolRelations.d.ts.map +1 -0
  67. package/dist/routes/agentToolRelations.js +342 -0
  68. package/dist/routes/agents.d.ts +4 -0
  69. package/dist/routes/agents.d.ts.map +1 -0
  70. package/dist/routes/agents.js +213 -0
  71. package/dist/routes/apiKeys.d.ts +4 -0
  72. package/dist/routes/apiKeys.d.ts.map +1 -0
  73. package/dist/routes/apiKeys.js +236 -0
  74. package/dist/routes/artifactComponents.d.ts +4 -0
  75. package/dist/routes/artifactComponents.d.ts.map +1 -0
  76. package/dist/routes/artifactComponents.js +202 -0
  77. package/dist/routes/contextConfigs.d.ts +4 -0
  78. package/dist/routes/contextConfigs.d.ts.map +1 -0
  79. package/dist/routes/contextConfigs.js +181 -0
  80. package/dist/routes/credentials.d.ts +4 -0
  81. package/dist/routes/credentials.d.ts.map +1 -0
  82. package/dist/routes/credentials.js +219 -0
  83. package/dist/routes/dataComponents.d.ts +4 -0
  84. package/dist/routes/dataComponents.d.ts.map +1 -0
  85. package/dist/routes/dataComponents.js +188 -0
  86. package/dist/routes/externalAgents.d.ts +4 -0
  87. package/dist/routes/externalAgents.d.ts.map +1 -0
  88. package/dist/routes/externalAgents.js +216 -0
  89. package/dist/routes/graphFull.d.ts +4 -0
  90. package/dist/routes/graphFull.d.ts.map +1 -0
  91. package/dist/routes/graphFull.js +248 -0
  92. package/dist/routes/index.d.ts +4 -0
  93. package/dist/routes/index.d.ts.map +1 -0
  94. package/dist/routes/index.js +37 -0
  95. package/dist/routes/oauth.d.ts +14 -0
  96. package/dist/routes/oauth.d.ts.map +1 -0
  97. package/dist/routes/oauth.js +191 -0
  98. package/dist/routes/projects.d.ts +4 -0
  99. package/dist/routes/projects.d.ts.map +1 -0
  100. package/dist/routes/projects.js +221 -0
  101. package/dist/routes/tools.d.ts +4 -0
  102. package/dist/routes/tools.d.ts.map +1 -0
  103. package/dist/routes/tools.js +547 -0
  104. package/dist/utils/auth-detection.d.ts +22 -0
  105. package/dist/utils/auth-detection.d.ts.map +1 -0
  106. package/dist/utils/auth-detection.js +149 -0
  107. package/dist/utils/oauth-service.d.ts +88 -0
  108. package/dist/utils/oauth-service.d.ts.map +1 -0
  109. package/dist/utils/oauth-service.js +240 -0
  110. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # Inkeep Management API
2
+
3
+ The Management API is responsible for entity management and CRUD operations for the Inkeep Agent Framework. It provides the administrative layer for creating, configuring, and managing agents, graphs, tools, and projects.
4
+
5
+ ## Overview
6
+
7
+ This API handles the configuration and management layer:
8
+ - **Entity Management**: CRUD operations for all framework entities
9
+ - **Agent Configuration**: Agent creation, updates, and relationship management
10
+ - **Graph Management**: Agent graph definitions and topology
11
+ - **Tool Management**: MCP tool registration and configuration
12
+ - **Project Management**: Multi-tenant project organization
13
+
14
+ ## Architecture
15
+
16
+ ### Core Entities
17
+
18
+ - **Projects**: Top-level organizational units with tenant scoping
19
+ - **Agents**: Individual AI agents with instructions and capabilities
20
+ - **Agent Graphs**: Collections of agents with defined relationships
21
+ - **Agent Relations**: Transfer and delegation relationships between agents
22
+ - **Tools**: MCP servers and hosted tool configurations
23
+ - **Tasks**: Work units with hierarchical parent-child relationships
24
+
25
+ ## Development
26
+
27
+ ### Setup
28
+
29
+ If you do not have the db setup:
30
+
31
+ ```bash
32
+ cd packages/core
33
+ pnpm db:push
34
+ ```
35
+
36
+ Then:
37
+
38
+ ```bash
39
+ cd inkeep-execution-api
40
+ pnpm install
41
+ ```
42
+
43
+ ### Environment Variables
44
+ ```env
45
+ ENVIRONMENT=development|production|test
46
+ PORT=3002
47
+ DB_FILE_NAME=path/to/sqlite.db
48
+ LOG_LEVEL=debug|info|warn|error
49
+ ```
50
+
51
+ ### Development Commands
52
+ ```bash
53
+ pnpm dev # Start development server
54
+ pnpm test # Run test suite
55
+ pnpm test:coverage # Run tests with coverage
56
+ pnpm build # Build for production
57
+ pnpm lint # Run linting
58
+ pnpm format # Format code
59
+ ```
60
+
61
+ ### Database Operations
62
+ ```bash
63
+ pnpm db:push # Apply schema changes to SQLite
64
+ pnpm db:studio # Open Drizzle Studio for inspection
65
+ pnpm db:generate # Generate migration files
66
+ ```
67
+
68
+ ## Builder Patterns
69
+
70
+ The API provides builder patterns for programmatic entity creation:
71
+
72
+ ### Agent Builder
73
+ ```typescript
74
+ import { agent } from '@inkeep/agents-manage-api';
75
+
76
+ const qaAgent = agent({
77
+ id: 'qa-agent',
78
+ name: 'QA Agent',
79
+ instructions: 'Answer questions about our product',
80
+ canTransferTo: () => [routerAgent],
81
+ canDelegateTo: () => [searchAgent],
82
+ tools: { search: searchTool }
83
+ });
84
+ ```
85
+
86
+ ### Graph Builder
87
+ ```typescript
88
+ import { agentGraph } from '@inkeep/agents-manage-api';
89
+
90
+ const graph = agentGraph({
91
+ id: 'customer-support',
92
+ defaultAgent: routerAgent,
93
+ agents: [routerAgent, qaAgent, orderAgent]
94
+ });
95
+
96
+ await graph.init(); // Persist to database
97
+ ```
98
+
99
+ ### Tool Builder
100
+ ```typescript
101
+ import { tool } from '@inkeep/agents-manage-api';
102
+
103
+ const searchTool = tool({
104
+ id: 'search-tool',
105
+ name: 'Product Search',
106
+ type: 'mcp',
107
+ config: {
108
+ command: 'search-server',
109
+ args: ['--index', 'products']
110
+ }
111
+ });
112
+ ```
113
+
114
+ ## Database Schema
115
+
116
+ The API uses SQLite with Drizzle ORM:
117
+
118
+ ### Core Tables
119
+ - `projects` - Multi-tenant project organization
120
+ - `agents` - Agent definitions and instructions
121
+ - `agent_graphs` - Graph collections with default agents
122
+ - `agent_relations` - Transfer/delegation relationships
123
+ - `tools` - MCP tool configurations
124
+ - `tasks` - Execution history and hierarchy
125
+
126
+ ### Key Patterns
127
+ - **Multi-tenancy**: All entities scoped by `tenant_id`
128
+ - **Relationships**: Foreign keys with cascade deletes
129
+ - **Timestamps**: `created_at`/`updated_at` on all entities
130
+ - **JSON Columns**: Complex configurations stored as JSON
131
+
132
+ ## Validation
133
+
134
+ All API endpoints use Zod schemas for request/response validation:
135
+
136
+ ```typescript
137
+ const createAgentSchema = z.object({
138
+ id: z.string().min(1),
139
+ name: z.string().min(1),
140
+ instructions: z.string().min(1),
141
+ tenantId: z.string(),
142
+ projectId: z.string()
143
+ });
144
+ ```
145
+
146
+ ## Integration
147
+
148
+ ### With Execution API
149
+ The Management API provides the configuration that the Execution API reads during runtime. Changes to agents, graphs, or tools are immediately available to the execution layer.
150
+
151
+ ### With CLI
152
+ The Inkeep CLI uses the Management API for all `push` operations, creating and updating entities from configuration files.
153
+
154
+ ### With Agent Builder
155
+ The Agent Builder UI provides a web interface for all Management API operations, offering visual agent and graph creation.
156
+
157
+ ## Error Handling
158
+
159
+ - **Validation Errors**: Schema validation with detailed field errors
160
+ - **Constraint Errors**: Database constraint violations with helpful messages
161
+ - **Not Found Errors**: Resource-specific 404 responses
162
+ - **Conflict Errors**: Duplicate key and relationship conflicts
163
+
164
+ ## Security
165
+
166
+ - **Multi-tenant Isolation**: All queries scoped by tenant ID
167
+ - **Input Validation**: Comprehensive request sanitization
168
+ - **SQL Injection Prevention**: Parameterized queries via Drizzle ORM
169
+ - **Authorization**: Configurable API key authentication
170
+
171
+ ## Performance
172
+
173
+ - **Database Indexing**: Optimized indexes on frequently queried fields
174
+ - **Connection Pooling**: Efficient database connection management
175
+ - **Caching**: In-memory caching for frequently accessed entities
176
+ - **Parallel Operations**: Concurrent database operations where safe
@@ -0,0 +1,28 @@
1
+ import { BaseServer } from '@inkeep/agents-core';
2
+ import type { AgentFrameworkServerConfig } from '@inkeep/agents-core';
3
+ export declare const MANAGEMENT_API_PORT = 3002;
4
+ /**
5
+ * Management Server for CRUD operations, entity management, and OAuth
6
+ * Extends BaseServer with Management API specific functionality
7
+ */
8
+ export declare class ManagementServer extends BaseServer {
9
+ constructor(config?: AgentFrameworkServerConfig);
10
+ /**
11
+ * Initialize the Management API application
12
+ */
13
+ protected initialize(): Promise<void>;
14
+ /**
15
+ * Get default server options for Management API
16
+ */
17
+ protected getServerOptions(): {
18
+ port: number;
19
+ keepAliveTimeout: number;
20
+ keepAlive: boolean;
21
+ requestTimeout: number;
22
+ };
23
+ /**
24
+ * Custom cleanup for Management API before shutdown
25
+ */
26
+ protected beforeShutdown(): Promise<void>;
27
+ }
28
+ //# sourceMappingURL=ManagementServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ManagementServer.d.ts","sourceRoot":"","sources":["../src/ManagementServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAGtE,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;gBAClC,MAAM,GAAE,0BAA+B;IAInD;;OAEG;cACa,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3C;;OAEG;IACH,SAAS,CAAC,gBAAgB;;;;;;IAU1B;;OAEG;cACa,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;CAQhD"}
@@ -0,0 +1,41 @@
1
+ import { BaseServer } from '@inkeep/agents-core';
2
+ import { getLogger } from './logger.js';
3
+ import app from './app.js';
4
+ export const MANAGEMENT_API_PORT = 3002;
5
+ /**
6
+ * Management Server for CRUD operations, entity management, and OAuth
7
+ * Extends BaseServer with Management API specific functionality
8
+ */
9
+ export class ManagementServer extends BaseServer {
10
+ constructor(config = {}) {
11
+ super(config, getLogger('ManagementServer'));
12
+ }
13
+ /**
14
+ * Initialize the Management API application
15
+ */
16
+ async initialize() {
17
+ this.app = app;
18
+ this.logger.info('Management API initialized');
19
+ }
20
+ /**
21
+ * Get default server options for Management API
22
+ */
23
+ getServerOptions() {
24
+ return {
25
+ port: this.config.port || MANAGEMENT_API_PORT,
26
+ keepAliveTimeout: 60000,
27
+ keepAlive: true,
28
+ requestTimeout: 60000,
29
+ ...this.config.serverOptions,
30
+ };
31
+ }
32
+ /**
33
+ * Custom cleanup for Management API before shutdown
34
+ */
35
+ async beforeShutdown() {
36
+ this.logger.info('Performing Management API cleanup...');
37
+ // Add any Management API specific cleanup here
38
+ // e.g., close database connections, cleanup OAuth sessions, etc.
39
+ await super.beforeShutdown();
40
+ }
41
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { migrate } from 'drizzle-orm/libsql/migrator';
3
+ import { afterAll, afterEach, beforeAll } from 'vitest';
4
+ import dbClient from '../data/db/dbClient.js';
5
+ import { getLogger } from '@inkeep/agents-core';
6
+ // Initialize database schema for in-memory test databases using Drizzle migrations
7
+ beforeAll(async () => {
8
+ const logger = getLogger('Test Setup');
9
+ try {
10
+ logger.debug({}, 'Applying database migrations to in-memory test database');
11
+ // Temporarily disable foreign key constraints for tests due to composite key issues
12
+ await dbClient.run(sql `PRAGMA foreign_keys = OFF`);
13
+ await migrate(dbClient, { migrationsFolder: '../packages/agents-core/drizzle' });
14
+ logger.debug({}, 'Database migrations applied successfully');
15
+ }
16
+ catch (error) {
17
+ logger.error({ error }, 'Failed to apply database migrations');
18
+ throw error;
19
+ }
20
+ });
21
+ afterEach(() => {
22
+ // Any cleanup if needed
23
+ });
24
+ afterAll(() => {
25
+ // Any final cleanup if needed
26
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Ensures a project exists for a given tenant ID.
3
+ * This is needed because of foreign key constraints in the database.
4
+ *
5
+ * @param tenantId - The tenant ID
6
+ * @param projectId - The project ID (defaults to 'default')
7
+ * @returns Promise that resolves when the project is created
8
+ */
9
+ export declare function ensureTestProject(tenantId: string, projectId?: string): Promise<void>;
10
+ /**
11
+ * Creates multiple test projects for a tenant.
12
+ *
13
+ * @param tenantId - The tenant ID
14
+ * @param projectIds - Array of project IDs to create
15
+ * @returns Promise that resolves when all projects are created
16
+ */
17
+ export declare function ensureTestProjects(tenantId: string, projectIds: string[]): Promise<void>;
18
+ //# sourceMappingURL=testProject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testProject.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/testProject.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,SAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9F;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9F"}
@@ -0,0 +1,26 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import dbClient from '../../data/db/dbClient.js';
3
+ /**
4
+ * Ensures a project exists for a given tenant ID.
5
+ * This is needed because of foreign key constraints in the database.
6
+ *
7
+ * @param tenantId - The tenant ID
8
+ * @param projectId - The project ID (defaults to 'default')
9
+ * @returns Promise that resolves when the project is created
10
+ */
11
+ export async function ensureTestProject(tenantId, projectId = 'default') {
12
+ await dbClient.run(sql `
13
+ INSERT OR IGNORE INTO projects (tenant_id, id, name, description, created_at, updated_at)
14
+ VALUES (${tenantId}, ${projectId}, ${`Test Project ${projectId}`}, ${`Test project for ${projectId}`}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
15
+ `);
16
+ }
17
+ /**
18
+ * Creates multiple test projects for a tenant.
19
+ *
20
+ * @param tenantId - The tenant ID
21
+ * @param projectIds - Array of project IDs to create
22
+ * @returns Promise that resolves when all projects are created
23
+ */
24
+ export async function ensureTestProjects(tenantId, projectIds) {
25
+ await Promise.all(projectIds.map((projectId) => ensureTestProject(tenantId, projectId)));
26
+ }
@@ -0,0 +1,2 @@
1
+ export declare const makeRequest: (url: string, options?: RequestInit) => Promise<Response>;
2
+ //# sourceMappingURL=testRequest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testRequest.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/testRequest.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,EAAE,UAAS,WAAgB,sBAQvE,CAAC"}
@@ -0,0 +1,11 @@
1
+ import app from '../../app.js';
2
+ // Helper function to make requests with JSON headers
3
+ export const makeRequest = async (url, options = {}) => {
4
+ return app.request(url, {
5
+ ...options,
6
+ headers: {
7
+ 'Content-Type': 'application/json',
8
+ ...options.headers,
9
+ },
10
+ });
11
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Creates a unique tenant ID for test isolation.
3
+ *
4
+ * Each test run gets its own tenant to ensure parallel tests don't interfere with each other.
5
+ * The generated tenant ID follows the format: test-tenant-{prefix}-{uuid} or test-tenant-{uuid}
6
+ *
7
+ * @param prefix - Optional prefix to include in the tenant ID (e.g., test file name)
8
+ * @returns A unique tenant ID for test isolation
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { createTestTenantId } from './utils/testTenant.js';
13
+ *
14
+ * describe('My test suite', () => {
15
+ * const tenantId = createTestTenantId('agents');
16
+ *
17
+ * it('should work with isolated tenant', async () => {
18
+ * // Your test code using the unique tenant ID
19
+ * console.log(tenantId); // e.g., "test-tenant-agents-123e4567-e89b-12d3-a456-426614174000"
20
+ * });
21
+ * });
22
+ * ```
23
+ */
24
+ export declare function createTestTenantId(prefix?: string): string;
25
+ /**
26
+ * Creates multiple unique tenant IDs for test isolation.
27
+ *
28
+ * Useful when you need multiple tenants in a single test.
29
+ *
30
+ * @param count - Number of tenant IDs to generate
31
+ * @param prefix - Optional prefix to include in all tenant IDs
32
+ * @returns Array of unique tenant IDs
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * import { createTestTenantIds } from './utils/testTenant.js';
37
+ *
38
+ * describe('Multi-tenant test suite', () => {
39
+ * const [tenantA, tenantB] = createTestTenantIds(2, 'multi-tenant');
40
+ *
41
+ * it('should handle cross-tenant operations', async () => {
42
+ * // Test operations across different tenants
43
+ * });
44
+ * });
45
+ * ```
46
+ */
47
+ export declare function createTestTenantIds(count: number, prefix?: string): string[];
48
+ /**
49
+ * Checks if a tenant ID is a test tenant.
50
+ *
51
+ * @param tenantId - The tenant ID to check
52
+ * @returns True if the tenant ID is a test tenant
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * import { isTestTenant } from './utils/testTenant.js';
57
+ *
58
+ * const tenantId = createTestTenantId();
59
+ * console.log(isTestTenant(tenantId)); // true
60
+ * console.log(isTestTenant('production-tenant')); // false
61
+ * ```
62
+ */
63
+ export declare function isTestTenant(tenantId: string): boolean;
64
+ //# sourceMappingURL=testTenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testTenant.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/testTenant.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAE5E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEtD"}
@@ -0,0 +1,71 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ /**
3
+ * Creates a unique tenant ID for test isolation.
4
+ *
5
+ * Each test run gets its own tenant to ensure parallel tests don't interfere with each other.
6
+ * The generated tenant ID follows the format: test-tenant-{prefix}-{uuid} or test-tenant-{uuid}
7
+ *
8
+ * @param prefix - Optional prefix to include in the tenant ID (e.g., test file name)
9
+ * @returns A unique tenant ID for test isolation
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { createTestTenantId } from './utils/testTenant.js';
14
+ *
15
+ * describe('My test suite', () => {
16
+ * const tenantId = createTestTenantId('agents');
17
+ *
18
+ * it('should work with isolated tenant', async () => {
19
+ * // Your test code using the unique tenant ID
20
+ * console.log(tenantId); // e.g., "test-tenant-agents-123e4567-e89b-12d3-a456-426614174000"
21
+ * });
22
+ * });
23
+ * ```
24
+ */
25
+ export function createTestTenantId(prefix) {
26
+ const uuid = randomUUID();
27
+ return prefix ? `test-tenant-${prefix}-${uuid}` : `test-tenant-${uuid}`;
28
+ }
29
+ /**
30
+ * Creates multiple unique tenant IDs for test isolation.
31
+ *
32
+ * Useful when you need multiple tenants in a single test.
33
+ *
34
+ * @param count - Number of tenant IDs to generate
35
+ * @param prefix - Optional prefix to include in all tenant IDs
36
+ * @returns Array of unique tenant IDs
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { createTestTenantIds } from './utils/testTenant.js';
41
+ *
42
+ * describe('Multi-tenant test suite', () => {
43
+ * const [tenantA, tenantB] = createTestTenantIds(2, 'multi-tenant');
44
+ *
45
+ * it('should handle cross-tenant operations', async () => {
46
+ * // Test operations across different tenants
47
+ * });
48
+ * });
49
+ * ```
50
+ */
51
+ export function createTestTenantIds(count, prefix) {
52
+ return Array.from({ length: count }, () => createTestTenantId(prefix));
53
+ }
54
+ /**
55
+ * Checks if a tenant ID is a test tenant.
56
+ *
57
+ * @param tenantId - The tenant ID to check
58
+ * @returns True if the tenant ID is a test tenant
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * import { isTestTenant } from './utils/testTenant.js';
63
+ *
64
+ * const tenantId = createTestTenantId();
65
+ * console.log(isTestTenant(tenantId)); // true
66
+ * console.log(isTestTenant('production-tenant')); // false
67
+ * ```
68
+ */
69
+ export function isTestTenant(tenantId) {
70
+ return tenantId.startsWith('test-tenant-');
71
+ }
package/dist/app.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { OpenApiHonoWithExecutionContext } from '@inkeep/agents-core';
2
+ declare const app: OpenApiHonoWithExecutionContext;
3
+ export default app;
4
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAatE,QAAA,MAAM,GAAG,iCAAwC,CAAC;AAwJlD,eAAe,GAAG,CAAC"}
package/dist/app.js ADDED
@@ -0,0 +1,140 @@
1
+ import { createRoute } from '@hono/zod-openapi';
2
+ import { OpenApiHonoWithExecutionContext } from '@inkeep/agents-core';
3
+ import { cors } from 'hono/cors';
4
+ import { HTTPException } from 'hono/http-exception';
5
+ import { requestId } from 'hono/request-id';
6
+ import { pinoLogger } from 'hono-pino';
7
+ import { handleApiError } from '@inkeep/agents-core';
8
+ import { getLogger } from './logger.js';
9
+ import crudRoutes from './routes/index.js';
10
+ import { apiKeyAuth } from './middleware/auth.js';
11
+ import oauthRoutes from './routes/oauth.js';
12
+ import { setupOpenAPIRoutes } from './openapi.js';
13
+ const app = new OpenApiHonoWithExecutionContext();
14
+ // Request ID middleware
15
+ app.use('*', requestId());
16
+ // Logging middleware
17
+ app.use(pinoLogger({
18
+ pino: getLogger(),
19
+ http: {
20
+ onResLevel(c) {
21
+ if (c.res.status >= 500) {
22
+ return 'error';
23
+ }
24
+ return 'info';
25
+ },
26
+ },
27
+ }));
28
+ // Error handling
29
+ app.onError(async (err, c) => {
30
+ const isExpectedError = err instanceof HTTPException;
31
+ const status = isExpectedError ? err.status : 500;
32
+ const requestId = c.get('requestId') || 'unknown';
33
+ // Zod validation error detection
34
+ let zodIssues;
35
+ if (err && typeof err === 'object') {
36
+ if (err.cause && Array.isArray(err.cause.issues)) {
37
+ zodIssues = err.cause.issues;
38
+ }
39
+ else if (Array.isArray(err.issues)) {
40
+ zodIssues = err.issues;
41
+ }
42
+ }
43
+ if (status === 400 && Array.isArray(zodIssues)) {
44
+ c.status(400);
45
+ c.header('Content-Type', 'application/problem+json');
46
+ c.header('X-Content-Type-Options', 'nosniff');
47
+ return c.json({
48
+ type: 'https://docs.inkeep.com/agents-api/errors#bad_request',
49
+ title: 'Validation Failed',
50
+ status: 400,
51
+ detail: 'Request validation failed',
52
+ errors: zodIssues.map((issue) => ({
53
+ detail: issue.message,
54
+ pointer: issue.path ? `/${issue.path.join('/')}` : undefined,
55
+ name: issue.path ? issue.path.join('.') : undefined,
56
+ reason: issue.message,
57
+ })),
58
+ });
59
+ }
60
+ if (status >= 500) {
61
+ if (!isExpectedError) {
62
+ const errorMessage = err instanceof Error ? err.message : String(err);
63
+ const errorStack = err instanceof Error ? err.stack : undefined;
64
+ getLogger().error({
65
+ error: err,
66
+ message: errorMessage,
67
+ stack: errorStack,
68
+ path: c.req.path,
69
+ requestId,
70
+ }, 'Unexpected server error occurred');
71
+ }
72
+ else {
73
+ getLogger().error({
74
+ error: err,
75
+ path: c.req.path,
76
+ requestId,
77
+ status,
78
+ }, 'Server error occurred');
79
+ }
80
+ }
81
+ if (isExpectedError) {
82
+ try {
83
+ const response = err.getResponse();
84
+ return response;
85
+ }
86
+ catch (responseError) {
87
+ getLogger().error({ error: responseError }, 'Error while handling HTTPException response');
88
+ }
89
+ }
90
+ const { status: respStatus, title, detail, instance } = await handleApiError(err, requestId);
91
+ c.status(respStatus);
92
+ c.header('Content-Type', 'application/problem+json');
93
+ c.header('X-Content-Type-Options', 'nosniff');
94
+ return c.json({
95
+ type: 'https://docs.inkeep.com/agents-api/errors#internal_server_error',
96
+ title,
97
+ status: respStatus,
98
+ detail,
99
+ ...(instance && { instance }),
100
+ });
101
+ });
102
+ // CORS middleware
103
+ app.use('*', cors({
104
+ origin: (origin) => {
105
+ if (!origin)
106
+ return origin;
107
+ return origin.startsWith('http://localhost:') || origin.startsWith('https://localhost:')
108
+ ? origin
109
+ : null;
110
+ },
111
+ allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
112
+ allowHeaders: ['*'],
113
+ exposeHeaders: ['Content-Length'],
114
+ maxAge: 86400,
115
+ credentials: true,
116
+ }));
117
+ // Health check endpoint
118
+ app.openapi(createRoute({
119
+ method: 'get',
120
+ path: '/health',
121
+ tags: ['health'],
122
+ summary: 'Health check',
123
+ description: 'Check if the management service is healthy',
124
+ responses: {
125
+ 204: {
126
+ description: 'Service is healthy',
127
+ },
128
+ },
129
+ }), (c) => {
130
+ return c.body(null, 204);
131
+ });
132
+ // API Key authentication middleware for protected routes
133
+ app.use('/tenants/*', apiKeyAuth());
134
+ // Mount CRUD routes for all entities
135
+ app.route('/tenants/:tenantId/crud', crudRoutes);
136
+ // Mount OAuth routes - global OAuth callback endpoint
137
+ app.route('/oauth', oauthRoutes);
138
+ // Setup OpenAPI documentation endpoints (/openapi.json and /docs)
139
+ setupOpenAPIRoutes(app);
140
+ export default app;