@inkeep/agents-manage-api 0.0.0-dev-20250910232631
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/LICENSE.md +49 -0
- package/README.md +176 -0
- package/dist/__tests__/setup.d.ts +2 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +26 -0
- package/dist/__tests__/utils/testProject.d.ts +18 -0
- package/dist/__tests__/utils/testProject.d.ts.map +1 -0
- package/dist/__tests__/utils/testProject.js +26 -0
- package/dist/__tests__/utils/testRequest.d.ts +2 -0
- package/dist/__tests__/utils/testRequest.d.ts.map +1 -0
- package/dist/__tests__/utils/testRequest.js +11 -0
- package/dist/__tests__/utils/testTenant.d.ts +64 -0
- package/dist/__tests__/utils/testTenant.d.ts.map +1 -0
- package/dist/__tests__/utils/testTenant.js +71 -0
- package/dist/app.d.ts +5 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +151 -0
- package/dist/data/conversations.d.ts +59 -0
- package/dist/data/conversations.d.ts.map +1 -0
- package/dist/data/conversations.js +216 -0
- package/dist/data/db/clean.d.ts +6 -0
- package/dist/data/db/clean.d.ts.map +1 -0
- package/dist/data/db/clean.js +77 -0
- package/dist/data/db/dbClient.d.ts +3 -0
- package/dist/data/db/dbClient.d.ts.map +1 -0
- package/dist/data/db/dbClient.js +13 -0
- package/dist/data/graphFull.d.ts +11 -0
- package/dist/data/graphFull.d.ts.map +1 -0
- package/dist/data/graphFull.js +90 -0
- package/dist/data/graphFullClient.d.ts +22 -0
- package/dist/data/graphFullClient.d.ts.map +1 -0
- package/dist/data/graphFullClient.js +189 -0
- package/dist/data/tools.d.ts +82 -0
- package/dist/data/tools.d.ts.map +1 -0
- package/dist/data/tools.js +271 -0
- package/dist/env.d.ts +41 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +59 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +32 -0
- package/dist/middleware/auth.d.ts +12 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +36 -0
- package/dist/openapi.d.ts +2 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +38 -0
- package/dist/routes/agentArtifactComponents.d.ts +4 -0
- package/dist/routes/agentArtifactComponents.d.ts.map +1 -0
- package/dist/routes/agentArtifactComponents.js +228 -0
- package/dist/routes/agentDataComponents.d.ts +4 -0
- package/dist/routes/agentDataComponents.d.ts.map +1 -0
- package/dist/routes/agentDataComponents.js +224 -0
- package/dist/routes/agentGraph.d.ts +4 -0
- package/dist/routes/agentGraph.d.ts.map +1 -0
- package/dist/routes/agentGraph.js +287 -0
- package/dist/routes/agentRelations.d.ts +4 -0
- package/dist/routes/agentRelations.d.ts.map +1 -0
- package/dist/routes/agentRelations.js +289 -0
- package/dist/routes/agentToolRelations.d.ts +4 -0
- package/dist/routes/agentToolRelations.d.ts.map +1 -0
- package/dist/routes/agentToolRelations.js +342 -0
- package/dist/routes/agents.d.ts +4 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +212 -0
- package/dist/routes/apiKeys.d.ts +4 -0
- package/dist/routes/apiKeys.d.ts.map +1 -0
- package/dist/routes/apiKeys.js +235 -0
- package/dist/routes/artifactComponents.d.ts +4 -0
- package/dist/routes/artifactComponents.d.ts.map +1 -0
- package/dist/routes/artifactComponents.js +201 -0
- package/dist/routes/contextConfigs.d.ts +4 -0
- package/dist/routes/contextConfigs.d.ts.map +1 -0
- package/dist/routes/contextConfigs.js +180 -0
- package/dist/routes/credentials.d.ts +10 -0
- package/dist/routes/credentials.d.ts.map +1 -0
- package/dist/routes/credentials.js +218 -0
- package/dist/routes/dataComponents.d.ts +4 -0
- package/dist/routes/dataComponents.d.ts.map +1 -0
- package/dist/routes/dataComponents.js +187 -0
- package/dist/routes/externalAgents.d.ts +4 -0
- package/dist/routes/externalAgents.d.ts.map +1 -0
- package/dist/routes/externalAgents.js +215 -0
- package/dist/routes/graphFull.d.ts +4 -0
- package/dist/routes/graphFull.d.ts.map +1 -0
- package/dist/routes/graphFull.js +247 -0
- package/dist/routes/index.d.ts +4 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +37 -0
- package/dist/routes/oauth.d.ts +21 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +191 -0
- package/dist/routes/projects.d.ts +4 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +220 -0
- package/dist/routes/tools.d.ts +11 -0
- package/dist/routes/tools.d.ts.map +1 -0
- package/dist/routes/tools.js +554 -0
- package/dist/utils/auth-detection.d.ts +22 -0
- package/dist/utils/auth-detection.d.ts.map +1 -0
- package/dist/utils/auth-detection.js +149 -0
- package/dist/utils/oauth-service.d.ts +88 -0
- package/dist/utils/oauth-service.d.ts.map +1 -0
- package/dist/utils/oauth-service.js +240 -0
- package/package.json +69 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Inkeep SDK – Elastic License 2.0 with Supplemental Terms
|
|
2
|
+
|
|
3
|
+
NOTE: The Inkeep SDK is licensed under the Elastic License 2.0 (ELv2), subject to Supplemental Terms included in [SUPPLEMENTAL_TERMS.md](SUPPLEMENTAL_TERMS.md). In the event of conflict, the Supplemental Terms control.
|
|
4
|
+
|
|
5
|
+
# Elastic License 2.0
|
|
6
|
+
|
|
7
|
+
## Acceptance
|
|
8
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
9
|
+
|
|
10
|
+
## Copyright License
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below.
|
|
12
|
+
|
|
13
|
+
## Limitations
|
|
14
|
+
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
|
|
15
|
+
|
|
16
|
+
You may not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key.
|
|
17
|
+
|
|
18
|
+
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
|
19
|
+
|
|
20
|
+
## Patents
|
|
21
|
+
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
|
22
|
+
|
|
23
|
+
## Notices
|
|
24
|
+
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
|
|
25
|
+
|
|
26
|
+
If you modify the software, you must include in any modified copies of the software prominent notices stating that you have modified the software.
|
|
27
|
+
|
|
28
|
+
## No Other Rights
|
|
29
|
+
These terms do not imply any licenses other than those expressly granted in these terms.
|
|
30
|
+
|
|
31
|
+
## Termination
|
|
32
|
+
If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your licenses will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently.
|
|
33
|
+
|
|
34
|
+
## No Liability
|
|
35
|
+
***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
|
|
36
|
+
|
|
37
|
+
## Definitions
|
|
38
|
+
The **licensor** is the entity offering these terms, and the **software** is the software the licensor makes available under these terms, including any portion of it.
|
|
39
|
+
|
|
40
|
+
**you** refers to the individual or entity agreeing to these terms.
|
|
41
|
+
|
|
42
|
+
**your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
|
43
|
+
|
|
44
|
+
**your licenses** are all the licenses granted to you for the software under these terms.
|
|
45
|
+
|
|
46
|
+
**use** means anything you do with the software requiring one of your licenses.
|
|
47
|
+
|
|
48
|
+
**trademark** means trademarks, service marks, and similar rights.
|
|
49
|
+
|
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 agents-run-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 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getLogger } from '@inkeep/agents-core';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
3
|
+
import { migrate } from 'drizzle-orm/libsql/migrator';
|
|
4
|
+
import { afterAll, afterEach, beforeAll } from 'vitest';
|
|
5
|
+
import dbClient from '../data/db/dbClient';
|
|
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';
|
|
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 @@
|
|
|
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 '../../index';
|
|
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';
|
|
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';
|
|
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';
|
|
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';
|
|
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';
|
|
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';
|
|
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,5 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { CredentialStoreRegistry, ServerConfig } from '@inkeep/agents-core';
|
|
3
|
+
declare function createManagementHono(serverConfig: ServerConfig, credentialStores: CredentialStoreRegistry): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
4
|
+
export { createManagementHono };
|
|
5
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAkBjF,iBAAS,oBAAoB,CAC3B,YAAY,EAAE,YAAY,EAC1B,gBAAgB,EAAE,uBAAuB,8EAqK1C;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
|
|
3
|
+
import { handleApiError } from '@inkeep/agents-core';
|
|
4
|
+
import { cors } from 'hono/cors';
|
|
5
|
+
import { HTTPException } from 'hono/http-exception';
|
|
6
|
+
import { requestId } from 'hono/request-id';
|
|
7
|
+
import { pinoLogger } from 'hono-pino';
|
|
8
|
+
import { getLogger } from './logger';
|
|
9
|
+
import { apiKeyAuth } from './middleware/auth';
|
|
10
|
+
import { setupOpenAPIRoutes } from './openapi';
|
|
11
|
+
import crudRoutes from './routes/index';
|
|
12
|
+
import oauthRoutes from './routes/oauth';
|
|
13
|
+
function createManagementHono(serverConfig, credentialStores) {
|
|
14
|
+
const app = new OpenAPIHono();
|
|
15
|
+
// Request ID middleware
|
|
16
|
+
app.use('*', requestId());
|
|
17
|
+
// Server config and credential stores middleware
|
|
18
|
+
app.use('*', async (c, next) => {
|
|
19
|
+
c.set('serverConfig', serverConfig);
|
|
20
|
+
c.set('credentialStores', credentialStores);
|
|
21
|
+
return next();
|
|
22
|
+
});
|
|
23
|
+
// Logging middleware
|
|
24
|
+
app.use(pinoLogger({
|
|
25
|
+
pino: getLogger(),
|
|
26
|
+
http: {
|
|
27
|
+
onResLevel(c) {
|
|
28
|
+
if (c.res.status >= 500) {
|
|
29
|
+
return 'error';
|
|
30
|
+
}
|
|
31
|
+
return 'info';
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
}));
|
|
35
|
+
// Error handling
|
|
36
|
+
app.onError(async (err, c) => {
|
|
37
|
+
const isExpectedError = err instanceof HTTPException;
|
|
38
|
+
const status = isExpectedError ? err.status : 500;
|
|
39
|
+
const requestId = c.get('requestId') || 'unknown';
|
|
40
|
+
// Zod validation error detection
|
|
41
|
+
let zodIssues;
|
|
42
|
+
if (err && typeof err === 'object') {
|
|
43
|
+
if (err.cause && Array.isArray(err.cause.issues)) {
|
|
44
|
+
zodIssues = err.cause.issues;
|
|
45
|
+
}
|
|
46
|
+
else if (Array.isArray(err.issues)) {
|
|
47
|
+
zodIssues = err.issues;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (status === 400 && Array.isArray(zodIssues)) {
|
|
51
|
+
c.status(400);
|
|
52
|
+
c.header('Content-Type', 'application/problem+json');
|
|
53
|
+
c.header('X-Content-Type-Options', 'nosniff');
|
|
54
|
+
return c.json({
|
|
55
|
+
type: 'https://docs.inkeep.com/agents-api/errors#bad_request',
|
|
56
|
+
title: 'Validation Failed',
|
|
57
|
+
status: 400,
|
|
58
|
+
detail: 'Request validation failed',
|
|
59
|
+
errors: zodIssues.map((issue) => ({
|
|
60
|
+
detail: issue.message,
|
|
61
|
+
pointer: issue.path ? `/${issue.path.join('/')}` : undefined,
|
|
62
|
+
name: issue.path ? issue.path.join('.') : undefined,
|
|
63
|
+
reason: issue.message,
|
|
64
|
+
})),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (status >= 500) {
|
|
68
|
+
if (!isExpectedError) {
|
|
69
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
70
|
+
const errorStack = err instanceof Error ? err.stack : undefined;
|
|
71
|
+
getLogger().error({
|
|
72
|
+
error: err,
|
|
73
|
+
message: errorMessage,
|
|
74
|
+
stack: errorStack,
|
|
75
|
+
path: c.req.path,
|
|
76
|
+
requestId,
|
|
77
|
+
}, 'Unexpected server error occurred');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
getLogger().error({
|
|
81
|
+
error: err,
|
|
82
|
+
path: c.req.path,
|
|
83
|
+
requestId,
|
|
84
|
+
status,
|
|
85
|
+
}, 'Server error occurred');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (isExpectedError) {
|
|
89
|
+
try {
|
|
90
|
+
const response = err.getResponse();
|
|
91
|
+
return response;
|
|
92
|
+
}
|
|
93
|
+
catch (responseError) {
|
|
94
|
+
getLogger().error({ error: responseError }, 'Error while handling HTTPException response');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const { status: respStatus, title, detail, instance } = await handleApiError(err, requestId);
|
|
98
|
+
c.status(respStatus);
|
|
99
|
+
c.header('Content-Type', 'application/problem+json');
|
|
100
|
+
c.header('X-Content-Type-Options', 'nosniff');
|
|
101
|
+
return c.json({
|
|
102
|
+
type: 'https://docs.inkeep.com/agents-api/errors#internal_server_error',
|
|
103
|
+
title,
|
|
104
|
+
status: respStatus,
|
|
105
|
+
detail,
|
|
106
|
+
...(instance && { instance }),
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
// CORS middleware
|
|
110
|
+
app.use('*', cors({
|
|
111
|
+
origin: (origin) => {
|
|
112
|
+
if (!origin)
|
|
113
|
+
return origin;
|
|
114
|
+
return origin.startsWith('http://localhost:') || origin.startsWith('https://localhost:')
|
|
115
|
+
? origin
|
|
116
|
+
: null;
|
|
117
|
+
},
|
|
118
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
|
|
119
|
+
allowHeaders: ['*'],
|
|
120
|
+
exposeHeaders: ['Content-Length'],
|
|
121
|
+
maxAge: 86400,
|
|
122
|
+
credentials: true,
|
|
123
|
+
}));
|
|
124
|
+
// Health check endpoint
|
|
125
|
+
app.openapi(createRoute({
|
|
126
|
+
method: 'get',
|
|
127
|
+
path: '/health',
|
|
128
|
+
tags: ['health'],
|
|
129
|
+
summary: 'Health check',
|
|
130
|
+
description: 'Check if the management service is healthy',
|
|
131
|
+
responses: {
|
|
132
|
+
204: {
|
|
133
|
+
description: 'Service is healthy',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
}), (c) => {
|
|
137
|
+
return c.body(null, 204);
|
|
138
|
+
});
|
|
139
|
+
// API Key authentication middleware for protected routes
|
|
140
|
+
app.use('/tenants/*', apiKeyAuth());
|
|
141
|
+
// Mount CRUD routes for all entities
|
|
142
|
+
app.route('/tenants/:tenantId/crud', crudRoutes);
|
|
143
|
+
// Mount OAuth routes - global OAuth callback endpoint
|
|
144
|
+
app.route('/oauth', oauthRoutes);
|
|
145
|
+
// Setup OpenAPI documentation endpoints (/openapi.json and /docs)
|
|
146
|
+
setupOpenAPIRoutes(app);
|
|
147
|
+
const baseApp = new Hono();
|
|
148
|
+
baseApp.route('/', app);
|
|
149
|
+
return baseApp;
|
|
150
|
+
}
|
|
151
|
+
export { createManagementHono };
|