@inkeep/agents-core 0.35.0 → 0.35.2
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/dist/auth/auth-schema.d.ts +1097 -0
- package/dist/auth/auth-schema.js +1 -0
- package/dist/auth/auth-validation-schemas.d.ts +1881 -0
- package/dist/auth/auth-validation-schemas.js +39 -0
- package/dist/auth/auth.d.ts +118 -0
- package/dist/auth/auth.js +95 -0
- package/dist/auth/permissions.d.ts +273 -0
- package/dist/auth/permissions.js +1 -0
- package/dist/chunk-4JZT4QEE.js +162 -0
- package/dist/chunk-F5WWOOIX.js +62 -0
- package/dist/{chunk-YZ5ZBVHJ.js → chunk-NFYCSHD3.js} +3 -81
- package/dist/chunk-NOPEANIU.js +82 -0
- package/dist/{chunk-J5AHY6M2.js → chunk-SPRTYWRV.js} +1 -1
- package/dist/{chunk-OP3KPT4T.js → chunk-TGESM3JG.js} +1 -160
- package/dist/{chunk-DYGTCLJO.js → chunk-VBCCPAZK.js} +1 -1
- package/dist/chunk-ZYSTJ4XY.js +948 -0
- package/dist/client-CPYOMZF2.d.ts +19 -0
- package/dist/client-exports.d.ts +5 -5
- package/dist/client-exports.js +4 -3
- package/dist/db/schema.d.ts +3 -2
- package/dist/db/schema.js +2 -1
- package/dist/index.d.ts +253 -398
- package/dist/index.js +1566 -2499
- package/dist/{schema-DQBYINXB.d.cts → schema-5N2lPWNV.d.ts} +3 -1096
- package/dist/types/index.d.ts +2 -2
- package/dist/{utility-Ct1UMzr_.d.cts → utility-DbltUp2Q.d.ts} +295 -295
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +2 -2
- package/package.json +17 -1
- package/dist/auth-detection-CGqhPDnj.d.cts +0 -435
- package/dist/client-exports.cjs +0 -2833
- package/dist/client-exports.d.cts +0 -289
- package/dist/constants/models.cjs +0 -40
- package/dist/constants/models.d.cts +0 -42
- package/dist/db/schema.cjs +0 -1090
- package/dist/db/schema.d.cts +0 -7
- package/dist/index.cjs +0 -227898
- package/dist/index.d.cts +0 -4893
- package/dist/props-validation-BMR1qNiy.d.cts +0 -15
- package/dist/schema-DlcSfZRM.d.ts +0 -6352
- package/dist/types/index.cjs +0 -39
- package/dist/types/index.d.cts +0 -132
- package/dist/utility-Ct1UMzr_.d.ts +0 -17079
- package/dist/utils/schema-conversion.cjs +0 -232
- package/dist/utils/schema-conversion.d.cts +0 -26
- package/dist/validation/index.cjs +0 -2930
- package/dist/validation/index.d.cts +0 -279
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
import { getLogger } from './chunk-W3L4M7FO.js';
|
|
2
|
+
import { CredentialStoreType, MCPTransportType } from './chunk-YFHT5M2R.js';
|
|
3
|
+
import { schema_exports, projects } from './chunk-NFYCSHD3.js';
|
|
4
|
+
import { organization } from './chunk-NOPEANIU.js';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import path, { dirname, join } from 'path';
|
|
8
|
+
import dotenv from 'dotenv';
|
|
9
|
+
import { expand } from 'dotenv-expand';
|
|
10
|
+
import { findUpSync } from 'find-up';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { customAlphabet } from 'nanoid';
|
|
13
|
+
import { scrypt, randomBytes, timingSafeEqual } from 'crypto';
|
|
14
|
+
import { promisify } from 'util';
|
|
15
|
+
import { z as z$1 } from '@hono/zod-openapi';
|
|
16
|
+
import { HTTPException } from 'hono/http-exception';
|
|
17
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
18
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
19
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
20
|
+
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
21
|
+
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
22
|
+
import { tool } from 'ai';
|
|
23
|
+
import { asyncExitHook, gracefulExit } from 'exit-hook';
|
|
24
|
+
import { match } from 'ts-pattern';
|
|
25
|
+
import { SignJWT, jwtVerify } from 'jose';
|
|
26
|
+
import { SpanStatusCode, trace } from '@opentelemetry/api';
|
|
27
|
+
import { fileURLToPath } from 'url';
|
|
28
|
+
import { PGlite } from '@electric-sql/pglite';
|
|
29
|
+
import { sql } from 'drizzle-orm';
|
|
30
|
+
import { drizzle } from 'drizzle-orm/pglite';
|
|
31
|
+
import { migrate } from 'drizzle-orm/pglite/migrator';
|
|
32
|
+
|
|
33
|
+
var loadEnvironmentFiles = () => {
|
|
34
|
+
const environmentFiles = [];
|
|
35
|
+
const currentEnv = path.resolve(process.cwd(), ".env");
|
|
36
|
+
if (fs.existsSync(currentEnv)) {
|
|
37
|
+
environmentFiles.push(currentEnv);
|
|
38
|
+
}
|
|
39
|
+
const rootEnv = findUpSync(".env", { cwd: path.dirname(process.cwd()) });
|
|
40
|
+
if (rootEnv) {
|
|
41
|
+
if (rootEnv !== currentEnv) {
|
|
42
|
+
environmentFiles.push(rootEnv);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const userConfigPath = path.join(os.homedir(), ".inkeep", "config");
|
|
46
|
+
if (fs.existsSync(userConfigPath)) {
|
|
47
|
+
dotenv.config({ path: userConfigPath, override: true, quiet: true });
|
|
48
|
+
}
|
|
49
|
+
if (environmentFiles.length > 0) {
|
|
50
|
+
dotenv.config({
|
|
51
|
+
path: environmentFiles,
|
|
52
|
+
override: false,
|
|
53
|
+
quiet: true
|
|
54
|
+
});
|
|
55
|
+
expand({ processEnv: process.env });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
loadEnvironmentFiles();
|
|
59
|
+
var envSchema = z.object({
|
|
60
|
+
ENVIRONMENT: z.enum(["development", "production", "pentest", "test"]).optional(),
|
|
61
|
+
DATABASE_URL: z.string().optional(),
|
|
62
|
+
POSTGRES_POOL_SIZE: z.string().optional(),
|
|
63
|
+
INKEEP_AGENTS_JWT_SIGNING_SECRET: z.string().min(32, "INKEEP_AGENTS_JWT_SIGNING_SECRET must be at least 32 characters").optional()
|
|
64
|
+
});
|
|
65
|
+
var parseEnv = () => {
|
|
66
|
+
try {
|
|
67
|
+
const parsedEnv = envSchema.parse(process.env);
|
|
68
|
+
return parsedEnv;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (error instanceof z.ZodError) {
|
|
71
|
+
const missingVars = error.issues.map((issue) => issue.path.join("."));
|
|
72
|
+
throw new Error(
|
|
73
|
+
`\u274C Invalid environment variables: ${missingVars.join(", ")}
|
|
74
|
+
${error.message}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var env = parseEnv();
|
|
81
|
+
|
|
82
|
+
// src/constants/execution-limits-shared/defaults.ts
|
|
83
|
+
var executionLimitsSharedDefaults = {
|
|
84
|
+
// MCP Tool Connection and Retry Behavior
|
|
85
|
+
// Model Context Protocol (MCP) enables agents to connect to external tools and services.
|
|
86
|
+
// These constants control connection timeouts and retry strategy with exponential backoff.
|
|
87
|
+
// CONNECTION_TIMEOUT_MS: Maximum wait time for initial MCP server connection
|
|
88
|
+
// MAX_RETRIES: Maximum number of connection retry attempts before failing
|
|
89
|
+
// INITIAL_RECONNECTION_DELAY_MS: Starting delay between retry attempts
|
|
90
|
+
// MAX_RECONNECTION_DELAY_MS: Maximum delay between retry attempts (after exponential growth)
|
|
91
|
+
// RECONNECTION_DELAY_GROWTH_FACTOR: Multiplier applied to delay after each failed retry (exponential backoff)
|
|
92
|
+
MCP_TOOL_CONNECTION_TIMEOUT_MS: 3e3,
|
|
93
|
+
// 3 seconds
|
|
94
|
+
MCP_TOOL_MAX_RETRIES: 3,
|
|
95
|
+
MCP_TOOL_MAX_RECONNECTION_DELAY_MS: 3e4,
|
|
96
|
+
// 30 seconds
|
|
97
|
+
MCP_TOOL_INITIAL_RECONNECTION_DELAY_MS: 1e3,
|
|
98
|
+
// 1 second
|
|
99
|
+
MCP_TOOL_RECONNECTION_DELAY_GROWTH_FACTOR: 1.5,
|
|
100
|
+
// Conversation History Context Window
|
|
101
|
+
// CONVERSATION_HISTORY_DEFAULT_LIMIT: Default number of recent messages to retrieve from conversation history.
|
|
102
|
+
// Used when fetching conversation context for status updates and other operations.
|
|
103
|
+
CONVERSATION_HISTORY_DEFAULT_LIMIT: 50,
|
|
104
|
+
// CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT: Maximum number of tokens from previous conversation messages
|
|
105
|
+
// to include in the LLM prompt. Prevents excessive token usage while maintaining relevant conversation context.
|
|
106
|
+
// Messages exceeding this limit are truncated from the beginning of the conversation.
|
|
107
|
+
// Increased from 4,000 to 8,000 to accommodate tool results in conversation history.
|
|
108
|
+
CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT: 8e3
|
|
109
|
+
};
|
|
110
|
+
loadEnvironmentFiles();
|
|
111
|
+
var constantsSchema = z.object(
|
|
112
|
+
Object.fromEntries(
|
|
113
|
+
Object.keys(executionLimitsSharedDefaults).map((key) => [
|
|
114
|
+
`AGENTS_${key}`,
|
|
115
|
+
z.coerce.number().optional()
|
|
116
|
+
])
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
var parseConstants = () => {
|
|
120
|
+
const envOverrides = constantsSchema.parse(process.env);
|
|
121
|
+
return Object.fromEntries(
|
|
122
|
+
Object.entries(executionLimitsSharedDefaults).map(([key, defaultValue]) => [
|
|
123
|
+
key,
|
|
124
|
+
envOverrides[`AGENTS_${key}`] ?? defaultValue
|
|
125
|
+
])
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
var constants = parseConstants();
|
|
129
|
+
var {
|
|
130
|
+
MCP_TOOL_CONNECTION_TIMEOUT_MS,
|
|
131
|
+
MCP_TOOL_MAX_RETRIES,
|
|
132
|
+
MCP_TOOL_MAX_RECONNECTION_DELAY_MS,
|
|
133
|
+
MCP_TOOL_INITIAL_RECONNECTION_DELAY_MS,
|
|
134
|
+
MCP_TOOL_RECONNECTION_DELAY_GROWTH_FACTOR,
|
|
135
|
+
CONVERSATION_HISTORY_DEFAULT_LIMIT,
|
|
136
|
+
CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT
|
|
137
|
+
} = constants;
|
|
138
|
+
|
|
139
|
+
// src/utils/credential-store-utils.ts
|
|
140
|
+
function getCredentialStoreLookupKeyFromRetrievalParams({
|
|
141
|
+
retrievalParams,
|
|
142
|
+
credentialStoreType
|
|
143
|
+
}) {
|
|
144
|
+
if (retrievalParams.key) {
|
|
145
|
+
return retrievalParams.key;
|
|
146
|
+
}
|
|
147
|
+
if (credentialStoreType === CredentialStoreType.nango) {
|
|
148
|
+
return JSON.stringify({
|
|
149
|
+
connectionId: retrievalParams.connectionId,
|
|
150
|
+
providerConfigKey: retrievalParams.providerConfigKey
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
var generateId = customAlphabet("abcdefghijklmnopqrstuvwxyz0123456789", 21);
|
|
156
|
+
function getConversationId() {
|
|
157
|
+
return generateId();
|
|
158
|
+
}
|
|
159
|
+
var scryptAsync = promisify(scrypt);
|
|
160
|
+
var logger = getLogger("api-key");
|
|
161
|
+
var API_KEY_LENGTH = 32;
|
|
162
|
+
var SALT_LENGTH = 32;
|
|
163
|
+
var KEY_LENGTH = 64;
|
|
164
|
+
var PUBLIC_ID_LENGTH = 12;
|
|
165
|
+
var PUBLIC_ID_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-";
|
|
166
|
+
var generatePublicId = customAlphabet(PUBLIC_ID_ALPHABET, PUBLIC_ID_LENGTH);
|
|
167
|
+
async function generateApiKey() {
|
|
168
|
+
const publicId = generatePublicId();
|
|
169
|
+
const secretBytes = randomBytes(API_KEY_LENGTH);
|
|
170
|
+
const secret = secretBytes.toString("base64url");
|
|
171
|
+
const key = `sk_${publicId}.${secret}`;
|
|
172
|
+
const keyPrefix = key.substring(0, 12);
|
|
173
|
+
const keyHash = await hashApiKey(key);
|
|
174
|
+
const id = generateId();
|
|
175
|
+
return {
|
|
176
|
+
id,
|
|
177
|
+
publicId,
|
|
178
|
+
key,
|
|
179
|
+
keyHash,
|
|
180
|
+
keyPrefix
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async function hashApiKey(key) {
|
|
184
|
+
const salt = randomBytes(SALT_LENGTH);
|
|
185
|
+
const hashedBuffer = await scryptAsync(key, salt, KEY_LENGTH);
|
|
186
|
+
const combined = Buffer.concat([salt, hashedBuffer]);
|
|
187
|
+
return combined.toString("base64");
|
|
188
|
+
}
|
|
189
|
+
async function validateApiKey(key, storedHash) {
|
|
190
|
+
try {
|
|
191
|
+
const combined = Buffer.from(storedHash, "base64");
|
|
192
|
+
const salt = combined.subarray(0, SALT_LENGTH);
|
|
193
|
+
const storedHashBuffer = combined.subarray(SALT_LENGTH);
|
|
194
|
+
const hashedBuffer = await scryptAsync(key, salt, KEY_LENGTH);
|
|
195
|
+
return timingSafeEqual(storedHashBuffer, hashedBuffer);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logger.error({ error }, "Error validating API key");
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function isApiKeyExpired(expiresAt) {
|
|
202
|
+
if (!expiresAt) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const expirationDate = new Date(expiresAt);
|
|
206
|
+
const now = /* @__PURE__ */ new Date();
|
|
207
|
+
return now > expirationDate;
|
|
208
|
+
}
|
|
209
|
+
function extractPublicId(key) {
|
|
210
|
+
try {
|
|
211
|
+
const parts = key.split(".");
|
|
212
|
+
if (parts.length !== 2) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
const prefixPart = parts[0];
|
|
216
|
+
const segments = prefixPart.split("_");
|
|
217
|
+
if (segments.length < 2) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const publicId = segments[segments.length - 1];
|
|
221
|
+
if (publicId.length !== 12) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
return publicId;
|
|
225
|
+
} catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function maskApiKey(keyPrefix) {
|
|
230
|
+
return `${keyPrefix}...`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/utils/date.ts
|
|
234
|
+
function normalizeDateString(dateString) {
|
|
235
|
+
if (typeof dateString !== "string") {
|
|
236
|
+
return dateString;
|
|
237
|
+
}
|
|
238
|
+
const pgTimestampPattern = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d{1,3})?$/;
|
|
239
|
+
if (pgTimestampPattern.test(dateString)) {
|
|
240
|
+
return dateString.replace(" ", "T") + "Z";
|
|
241
|
+
}
|
|
242
|
+
return dateString;
|
|
243
|
+
}
|
|
244
|
+
var ErrorCode = z$1.enum([
|
|
245
|
+
"bad_request",
|
|
246
|
+
"unauthorized",
|
|
247
|
+
"forbidden",
|
|
248
|
+
"not_found",
|
|
249
|
+
"conflict",
|
|
250
|
+
"internal_server_error",
|
|
251
|
+
"unprocessable_entity"
|
|
252
|
+
]);
|
|
253
|
+
var errorCodeToHttpStatus = {
|
|
254
|
+
bad_request: 400,
|
|
255
|
+
unauthorized: 401,
|
|
256
|
+
forbidden: 403,
|
|
257
|
+
not_found: 404,
|
|
258
|
+
conflict: 409,
|
|
259
|
+
unprocessable_entity: 422,
|
|
260
|
+
internal_server_error: 500
|
|
261
|
+
};
|
|
262
|
+
var ERROR_DOCS_BASE_URL = "https://docs.inkeep.com/agents-api/errors";
|
|
263
|
+
var problemDetailsSchema = z$1.object({
|
|
264
|
+
// type: z.string().url().openapi({
|
|
265
|
+
// description: "A URI reference that identifies the problem type.",
|
|
266
|
+
// example: `${ERROR_DOCS_BASE_URL}#not-found`,
|
|
267
|
+
// }),
|
|
268
|
+
title: z$1.string().openapi({
|
|
269
|
+
description: "A short, human-readable summary of the problem type.",
|
|
270
|
+
example: "Resource Not Found"
|
|
271
|
+
}),
|
|
272
|
+
status: z$1.number().int().openapi({
|
|
273
|
+
description: "The HTTP status code.",
|
|
274
|
+
example: 404
|
|
275
|
+
}),
|
|
276
|
+
detail: z$1.string().openapi({
|
|
277
|
+
description: "A human-readable explanation specific to this occurrence of the problem.",
|
|
278
|
+
example: "The requested resource was not found."
|
|
279
|
+
}),
|
|
280
|
+
instance: z$1.string().optional().openapi({
|
|
281
|
+
description: "A URI reference that identifies the specific occurrence of the problem.",
|
|
282
|
+
example: "/conversations/123"
|
|
283
|
+
}),
|
|
284
|
+
requestId: z$1.string().optional().openapi({
|
|
285
|
+
description: "A unique identifier for the request, useful for troubleshooting.",
|
|
286
|
+
example: "req_1234567890"
|
|
287
|
+
}),
|
|
288
|
+
code: ErrorCode.openapi({
|
|
289
|
+
description: "A short code indicating the error code returned.",
|
|
290
|
+
example: "not_found"
|
|
291
|
+
})
|
|
292
|
+
}).openapi("ProblemDetails");
|
|
293
|
+
var errorResponseSchema = z$1.object({
|
|
294
|
+
error: z$1.object({
|
|
295
|
+
code: ErrorCode.openapi({
|
|
296
|
+
description: "A short code indicating the error code returned.",
|
|
297
|
+
example: "not_found"
|
|
298
|
+
}),
|
|
299
|
+
message: z$1.string().openapi({
|
|
300
|
+
description: "A human readable error message.",
|
|
301
|
+
example: "The requested resource was not found."
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
}).openapi("ErrorResponse");
|
|
305
|
+
function createApiError({
|
|
306
|
+
code,
|
|
307
|
+
message,
|
|
308
|
+
instance,
|
|
309
|
+
requestId
|
|
310
|
+
}) {
|
|
311
|
+
const status = errorCodeToHttpStatus[code];
|
|
312
|
+
const title = getTitleFromCode(code);
|
|
313
|
+
const problemDetails = {
|
|
314
|
+
title,
|
|
315
|
+
status,
|
|
316
|
+
detail: message,
|
|
317
|
+
code,
|
|
318
|
+
...instance && { instance },
|
|
319
|
+
...requestId && { requestId }
|
|
320
|
+
};
|
|
321
|
+
const errorMessage = message.length > 100 ? `${message.substring(0, 97)}...` : message;
|
|
322
|
+
const responseBody = {
|
|
323
|
+
...problemDetails,
|
|
324
|
+
error: { code, message: errorMessage }
|
|
325
|
+
};
|
|
326
|
+
const res = new Response(JSON.stringify(responseBody), {
|
|
327
|
+
status,
|
|
328
|
+
headers: {
|
|
329
|
+
"Content-Type": "application/problem+json",
|
|
330
|
+
"X-Content-Type-Options": "nosniff",
|
|
331
|
+
...requestId && { "X-Request-ID": requestId }
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
return new HTTPException(status, { message, res });
|
|
335
|
+
}
|
|
336
|
+
async function handleApiError(error, requestId) {
|
|
337
|
+
if (error instanceof HTTPException) {
|
|
338
|
+
const errorData = error.getResponse();
|
|
339
|
+
const responseText = await errorData.text();
|
|
340
|
+
let responseJson;
|
|
341
|
+
try {
|
|
342
|
+
responseJson = JSON.parse(responseText);
|
|
343
|
+
if (requestId && !responseJson.requestId) {
|
|
344
|
+
responseJson.requestId = requestId;
|
|
345
|
+
}
|
|
346
|
+
} catch (_e) {
|
|
347
|
+
responseJson = {
|
|
348
|
+
// type: `${ERROR_DOCS_BASE_URL}#internal_server_error`,
|
|
349
|
+
title: "Internal Server Error",
|
|
350
|
+
status: error.status,
|
|
351
|
+
detail: `Error processing request: ${responseText || "Unknown error"}`,
|
|
352
|
+
code: "internal_server_error",
|
|
353
|
+
...requestId && { requestId },
|
|
354
|
+
error: {
|
|
355
|
+
code: "internal_server_error",
|
|
356
|
+
message: "An internal server error occurred"
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (error.status >= 500) {
|
|
361
|
+
getLogger("core").error(
|
|
362
|
+
{
|
|
363
|
+
error,
|
|
364
|
+
status: error.status,
|
|
365
|
+
message: responseJson.detail || responseJson.error.message,
|
|
366
|
+
requestId: responseJson.requestId || requestId || "unknown"
|
|
367
|
+
},
|
|
368
|
+
"API server error occurred"
|
|
369
|
+
);
|
|
370
|
+
} else {
|
|
371
|
+
getLogger("core").info(
|
|
372
|
+
{
|
|
373
|
+
error,
|
|
374
|
+
status: error.status,
|
|
375
|
+
code: responseJson.code,
|
|
376
|
+
message: responseJson.detail || responseJson.error.message,
|
|
377
|
+
requestId: responseJson.requestId || requestId || "unknown"
|
|
378
|
+
},
|
|
379
|
+
"API client error occurred"
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return responseJson;
|
|
383
|
+
}
|
|
384
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
385
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
386
|
+
getLogger("core").error(
|
|
387
|
+
{
|
|
388
|
+
error,
|
|
389
|
+
message: errorMessage,
|
|
390
|
+
stack: errorStack,
|
|
391
|
+
status: 500,
|
|
392
|
+
requestId: requestId || "unknown"
|
|
393
|
+
},
|
|
394
|
+
"Unhandled API error occurred"
|
|
395
|
+
);
|
|
396
|
+
const sanitizedErrorMessage = error instanceof Error ? error.message.replace(/\b(password|token|key|secret|auth)\b/gi, "[REDACTED]") : "Unknown error";
|
|
397
|
+
const problemDetails = {
|
|
398
|
+
// type: `${ERROR_DOCS_BASE_URL}#internal_server_error`,
|
|
399
|
+
title: "Internal Server Error",
|
|
400
|
+
status: 500,
|
|
401
|
+
detail: `Server error occurred: ${sanitizedErrorMessage}`,
|
|
402
|
+
code: "internal_server_error",
|
|
403
|
+
...requestId && { requestId },
|
|
404
|
+
error: {
|
|
405
|
+
code: "internal_server_error",
|
|
406
|
+
message: "An internal server error occurred. Please try again later."
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
return problemDetails;
|
|
410
|
+
}
|
|
411
|
+
function getTitleFromCode(code) {
|
|
412
|
+
switch (code) {
|
|
413
|
+
case "bad_request":
|
|
414
|
+
return "Bad Request";
|
|
415
|
+
case "unauthorized":
|
|
416
|
+
return "Unauthorized";
|
|
417
|
+
case "forbidden":
|
|
418
|
+
return "Forbidden";
|
|
419
|
+
case "not_found":
|
|
420
|
+
return "Not Found";
|
|
421
|
+
case "conflict":
|
|
422
|
+
return "Conflict";
|
|
423
|
+
case "unprocessable_entity":
|
|
424
|
+
return "Unprocessable Entity";
|
|
425
|
+
case "internal_server_error":
|
|
426
|
+
return "Internal Server Error";
|
|
427
|
+
default:
|
|
428
|
+
return "Error";
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
var toTitleCase = (str) => str?.replace(/\w+/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).replace(/\s+/g, "") ?? "";
|
|
432
|
+
var errorSchemaFactory = (code, description) => ({
|
|
433
|
+
description,
|
|
434
|
+
content: {
|
|
435
|
+
"application/problem+json": {
|
|
436
|
+
schema: problemDetailsSchema.extend({
|
|
437
|
+
code: z$1.literal(code).openapi({
|
|
438
|
+
description: "A short code indicating the error code returned.",
|
|
439
|
+
example: code
|
|
440
|
+
}),
|
|
441
|
+
detail: z$1.string().openapi({
|
|
442
|
+
description: "A detailed explanation specific to this occurrence of the problem, providing context and specifics about what went wrong.",
|
|
443
|
+
example: description
|
|
444
|
+
}),
|
|
445
|
+
title: z$1.string().openapi({
|
|
446
|
+
description: "A short, human-readable summary of the problem type.",
|
|
447
|
+
example: getTitleFromCode(code)
|
|
448
|
+
}),
|
|
449
|
+
// type: z.string().url().openapi({
|
|
450
|
+
// description: "A URI reference that identifies the problem type.",
|
|
451
|
+
// example: `${ERROR_DOCS_BASE_URL}#${code}`,
|
|
452
|
+
// }),
|
|
453
|
+
status: z$1.number().int().openapi({
|
|
454
|
+
description: "The HTTP status code.",
|
|
455
|
+
example: errorCodeToHttpStatus[code]
|
|
456
|
+
}),
|
|
457
|
+
error: z$1.object({
|
|
458
|
+
code: z$1.literal(code).openapi({
|
|
459
|
+
description: "A short code indicating the error code returned.",
|
|
460
|
+
example: code
|
|
461
|
+
}),
|
|
462
|
+
message: z$1.string().openapi({
|
|
463
|
+
description: "A concise error message suitable for display to end users. May be truncated if the full detail is long.",
|
|
464
|
+
example: description.length > 100 ? `${description.substring(0, 97)}...` : description
|
|
465
|
+
})
|
|
466
|
+
}).openapi({
|
|
467
|
+
description: "Legacy error format for backward compatibility."
|
|
468
|
+
})
|
|
469
|
+
}).openapi(toTitleCase(description))
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
var commonCreateErrorResponses = {
|
|
474
|
+
400: errorSchemaFactory("bad_request", "Bad Request"),
|
|
475
|
+
401: errorSchemaFactory("unauthorized", "Unauthorized"),
|
|
476
|
+
403: errorSchemaFactory("forbidden", "Forbidden"),
|
|
477
|
+
422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
|
|
478
|
+
500: errorSchemaFactory("internal_server_error", "Internal Server Error")
|
|
479
|
+
};
|
|
480
|
+
var commonUpdateErrorResponses = {
|
|
481
|
+
400: errorSchemaFactory("bad_request", "Bad Request"),
|
|
482
|
+
401: errorSchemaFactory("unauthorized", "Unauthorized"),
|
|
483
|
+
403: errorSchemaFactory("forbidden", "Forbidden"),
|
|
484
|
+
404: errorSchemaFactory("not_found", "Not Found"),
|
|
485
|
+
422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
|
|
486
|
+
500: errorSchemaFactory("internal_server_error", "Internal Server Error")
|
|
487
|
+
};
|
|
488
|
+
var commonGetErrorResponses = {
|
|
489
|
+
400: errorSchemaFactory("bad_request", "Bad Request"),
|
|
490
|
+
401: errorSchemaFactory("unauthorized", "Unauthorized"),
|
|
491
|
+
403: errorSchemaFactory("forbidden", "Forbidden"),
|
|
492
|
+
404: errorSchemaFactory("not_found", "Not Found"),
|
|
493
|
+
422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
|
|
494
|
+
500: errorSchemaFactory("internal_server_error", "Internal Server Error")
|
|
495
|
+
};
|
|
496
|
+
var commonDeleteErrorResponses = {
|
|
497
|
+
400: errorSchemaFactory("bad_request", "Bad Request"),
|
|
498
|
+
401: errorSchemaFactory("unauthorized", "Unauthorized"),
|
|
499
|
+
403: errorSchemaFactory("forbidden", "Forbidden"),
|
|
500
|
+
404: errorSchemaFactory("not_found", "Not Found"),
|
|
501
|
+
422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
|
|
502
|
+
500: errorSchemaFactory("internal_server_error", "Internal Server Error")
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// src/utils/execution.ts
|
|
506
|
+
function createExecutionContext(params) {
|
|
507
|
+
return {
|
|
508
|
+
apiKey: params.apiKey,
|
|
509
|
+
tenantId: params.tenantId,
|
|
510
|
+
projectId: params.projectId,
|
|
511
|
+
agentId: params.agentId,
|
|
512
|
+
baseUrl: params.baseUrl || process.env.API_URL || "http://localhost:3003",
|
|
513
|
+
apiKeyId: params.apiKeyId
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function getRequestExecutionContext(c) {
|
|
517
|
+
const executionContext = c.get("executionContext");
|
|
518
|
+
if (!executionContext) {
|
|
519
|
+
throw new Error("No execution context available. API key authentication is required.");
|
|
520
|
+
}
|
|
521
|
+
return executionContext;
|
|
522
|
+
}
|
|
523
|
+
var McpClient = class {
|
|
524
|
+
name;
|
|
525
|
+
client;
|
|
526
|
+
timeout;
|
|
527
|
+
transport;
|
|
528
|
+
serverConfig;
|
|
529
|
+
connected = false;
|
|
530
|
+
constructor(opts) {
|
|
531
|
+
this.name = opts.name;
|
|
532
|
+
this.timeout = opts.timeout || DEFAULT_REQUEST_TIMEOUT_MSEC;
|
|
533
|
+
this.serverConfig = opts.server;
|
|
534
|
+
this.client = new Client(
|
|
535
|
+
{ name: opts.name, version: opts.version || "1.0.0" },
|
|
536
|
+
{ capabilities: opts.capabilities || {} }
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
isConnected() {
|
|
540
|
+
return this.connected;
|
|
541
|
+
}
|
|
542
|
+
async connect() {
|
|
543
|
+
if (this.connected) return;
|
|
544
|
+
await match(this.serverConfig).with({ type: MCPTransportType.streamableHttp }, (config) => this.connectHttp(config)).with({ type: MCPTransportType.sse }, (config) => this.connectSSE(config)).exhaustive();
|
|
545
|
+
this.connected = true;
|
|
546
|
+
const close = this.client.onclose;
|
|
547
|
+
this.client.onclose = () => {
|
|
548
|
+
this.connected = false;
|
|
549
|
+
if (typeof close === "function") {
|
|
550
|
+
close();
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
asyncExitHook(() => this.disconnect(), { wait: 5e3 });
|
|
554
|
+
process.on("SIGTERM", () => gracefulExit());
|
|
555
|
+
}
|
|
556
|
+
async connectSSE(config) {
|
|
557
|
+
const url = typeof config.url === "string" ? config.url : config.url.toString();
|
|
558
|
+
this.transport = new SSEClientTransport(new URL(url), {
|
|
559
|
+
eventSourceInit: config.eventSourceInit,
|
|
560
|
+
requestInit: {
|
|
561
|
+
headers: config.headers || {}
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
await this.client.connect(this.transport, {
|
|
565
|
+
timeout: config.timeout ?? this.timeout
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
async connectHttp(config) {
|
|
569
|
+
const { url, requestInit } = config;
|
|
570
|
+
const mergedRequestInit = {
|
|
571
|
+
...requestInit,
|
|
572
|
+
headers: {
|
|
573
|
+
...requestInit?.headers || {},
|
|
574
|
+
...config.headers || {}
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
const urlString = typeof url === "string" ? url : url.toString();
|
|
578
|
+
this.transport = new StreamableHTTPClientTransport(new URL(urlString), {
|
|
579
|
+
requestInit: mergedRequestInit,
|
|
580
|
+
reconnectionOptions: {
|
|
581
|
+
maxRetries: MCP_TOOL_MAX_RETRIES,
|
|
582
|
+
maxReconnectionDelay: MCP_TOOL_MAX_RECONNECTION_DELAY_MS,
|
|
583
|
+
initialReconnectionDelay: MCP_TOOL_INITIAL_RECONNECTION_DELAY_MS,
|
|
584
|
+
reconnectionDelayGrowFactor: MCP_TOOL_RECONNECTION_DELAY_GROWTH_FACTOR,
|
|
585
|
+
...config.reconnectionOptions
|
|
586
|
+
},
|
|
587
|
+
sessionId: config.sessionId
|
|
588
|
+
});
|
|
589
|
+
await this.client.connect(this.transport, { timeout: MCP_TOOL_CONNECTION_TIMEOUT_MS });
|
|
590
|
+
}
|
|
591
|
+
async disconnect() {
|
|
592
|
+
if (!this.transport) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
await this.transport.close();
|
|
597
|
+
} catch (e) {
|
|
598
|
+
console.error(e);
|
|
599
|
+
throw e;
|
|
600
|
+
} finally {
|
|
601
|
+
this.transport = void 0;
|
|
602
|
+
this.connected = false;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
validateSelectedTools(tools, activeTools) {
|
|
606
|
+
if (!activeTools) return;
|
|
607
|
+
for (const item of activeTools) {
|
|
608
|
+
if (tools.includes(item)) continue;
|
|
609
|
+
console.warn(`[Tools] Tool ${item} not found in tools`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async selectTools() {
|
|
613
|
+
const { tools } = await this.client.listTools({ timeout: this.timeout });
|
|
614
|
+
const { selectedTools, activeTools } = this.serverConfig;
|
|
615
|
+
let availableTools;
|
|
616
|
+
if (activeTools === void 0) {
|
|
617
|
+
availableTools = tools;
|
|
618
|
+
} else if (activeTools.length === 0) {
|
|
619
|
+
return [];
|
|
620
|
+
} else {
|
|
621
|
+
availableTools = tools.filter((tool2) => activeTools.includes(tool2.name));
|
|
622
|
+
}
|
|
623
|
+
if (selectedTools === void 0) {
|
|
624
|
+
return availableTools;
|
|
625
|
+
}
|
|
626
|
+
if (selectedTools.length === 0) {
|
|
627
|
+
return [];
|
|
628
|
+
}
|
|
629
|
+
const toolNames = availableTools.map((tool2) => tool2.name);
|
|
630
|
+
this.validateSelectedTools(toolNames, selectedTools);
|
|
631
|
+
return availableTools.filter((tool2) => selectedTools.includes(tool2.name));
|
|
632
|
+
}
|
|
633
|
+
async tools() {
|
|
634
|
+
const tools = await this.selectTools();
|
|
635
|
+
const results = {};
|
|
636
|
+
for (const def of tools) {
|
|
637
|
+
try {
|
|
638
|
+
const createZodSchema = (inputSchema) => {
|
|
639
|
+
if (!inputSchema || !inputSchema.properties) {
|
|
640
|
+
return z.object({});
|
|
641
|
+
}
|
|
642
|
+
const zodProperties = {};
|
|
643
|
+
for (const [key, prop] of Object.entries(inputSchema.properties)) {
|
|
644
|
+
const propDef = prop;
|
|
645
|
+
let zodType;
|
|
646
|
+
switch (propDef.type) {
|
|
647
|
+
case "string":
|
|
648
|
+
zodType = z.string();
|
|
649
|
+
break;
|
|
650
|
+
case "number":
|
|
651
|
+
zodType = z.number();
|
|
652
|
+
break;
|
|
653
|
+
case "boolean":
|
|
654
|
+
zodType = z.boolean();
|
|
655
|
+
break;
|
|
656
|
+
case "array":
|
|
657
|
+
zodType = z.array(z.any());
|
|
658
|
+
break;
|
|
659
|
+
default:
|
|
660
|
+
zodType = z.any();
|
|
661
|
+
}
|
|
662
|
+
if (propDef.description) {
|
|
663
|
+
zodType = zodType.describe(propDef.description);
|
|
664
|
+
}
|
|
665
|
+
const isRequired = inputSchema.required?.includes(key);
|
|
666
|
+
if (!isRequired) {
|
|
667
|
+
zodType = zodType.optional();
|
|
668
|
+
}
|
|
669
|
+
zodProperties[key] = zodType;
|
|
670
|
+
}
|
|
671
|
+
return z.object(zodProperties);
|
|
672
|
+
};
|
|
673
|
+
const schema = createZodSchema(def.inputSchema);
|
|
674
|
+
const createdTool = tool({
|
|
675
|
+
id: `${this.name}.${def.name}`,
|
|
676
|
+
description: def.description || "",
|
|
677
|
+
inputSchema: schema,
|
|
678
|
+
execute: async (context) => {
|
|
679
|
+
const result = await this.client.callTool(
|
|
680
|
+
{ name: def.name, arguments: context },
|
|
681
|
+
CallToolResultSchema,
|
|
682
|
+
{ timeout: this.timeout }
|
|
683
|
+
);
|
|
684
|
+
return result;
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
if (def.name) {
|
|
688
|
+
results[def.name] = createdTool;
|
|
689
|
+
}
|
|
690
|
+
} catch (e) {
|
|
691
|
+
console.error(`Error creating tool ${def.name}:`, e);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return results;
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
var logger2 = getLogger("service-token-auth");
|
|
698
|
+
function getJwtSecret() {
|
|
699
|
+
const secret = env.INKEEP_AGENTS_JWT_SIGNING_SECRET;
|
|
700
|
+
const dev_secret = "insecure-dev-secret-change-in-production-min-32-chars";
|
|
701
|
+
if (!secret) {
|
|
702
|
+
if (env.ENVIRONMENT === "production") {
|
|
703
|
+
throw new Error(
|
|
704
|
+
"INKEEP_AGENTS_JWT_SIGNING_SECRET environment variable is required in production"
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
logger2.warn(
|
|
708
|
+
{},
|
|
709
|
+
"INKEEP_AGENTS_JWT_SIGNING_SECRET not set, using insecure default. DO NOT USE IN PRODUCTION!"
|
|
710
|
+
);
|
|
711
|
+
return new TextEncoder().encode(dev_secret);
|
|
712
|
+
}
|
|
713
|
+
return new TextEncoder().encode(secret);
|
|
714
|
+
}
|
|
715
|
+
async function generateServiceToken(params) {
|
|
716
|
+
const secret = getJwtSecret();
|
|
717
|
+
try {
|
|
718
|
+
const token = await new SignJWT({
|
|
719
|
+
tenantId: params.tenantId,
|
|
720
|
+
projectId: params.projectId
|
|
721
|
+
}).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuer("inkeep-agents").setSubject(params.originAgentId).setAudience(params.targetAgentId).setIssuedAt().setExpirationTime("5m").sign(secret);
|
|
722
|
+
logger2.debug(
|
|
723
|
+
{
|
|
724
|
+
originAgentId: params.originAgentId,
|
|
725
|
+
targetAgentId: params.targetAgentId,
|
|
726
|
+
tenantId: params.tenantId
|
|
727
|
+
},
|
|
728
|
+
"Generated team agent token"
|
|
729
|
+
);
|
|
730
|
+
return token;
|
|
731
|
+
} catch (error) {
|
|
732
|
+
logger2.error({ error }, "Failed to generate service token");
|
|
733
|
+
throw new Error("Failed to generate service token");
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
async function verifyServiceToken(token) {
|
|
737
|
+
const secret = getJwtSecret();
|
|
738
|
+
try {
|
|
739
|
+
const { payload } = await jwtVerify(token, secret, {
|
|
740
|
+
issuer: "inkeep-agents",
|
|
741
|
+
algorithms: ["HS256"]
|
|
742
|
+
});
|
|
743
|
+
if (typeof payload.sub !== "string" || typeof payload.aud !== "string" || typeof payload.tenantId !== "string" || typeof payload.projectId !== "string") {
|
|
744
|
+
logger2.warn({ payload }, "Invalid service token: missing required claims");
|
|
745
|
+
return {
|
|
746
|
+
valid: false,
|
|
747
|
+
error: "Invalid token: missing required claims"
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
const validPayload = {
|
|
751
|
+
iss: payload.iss,
|
|
752
|
+
aud: payload.aud,
|
|
753
|
+
sub: payload.sub,
|
|
754
|
+
tenantId: payload.tenantId,
|
|
755
|
+
projectId: payload.projectId,
|
|
756
|
+
iat: payload.iat,
|
|
757
|
+
exp: payload.exp
|
|
758
|
+
};
|
|
759
|
+
logger2.debug(
|
|
760
|
+
{
|
|
761
|
+
originAgentId: validPayload.sub,
|
|
762
|
+
targetAgentId: validPayload.aud,
|
|
763
|
+
tenantId: validPayload.tenantId
|
|
764
|
+
},
|
|
765
|
+
"Successfully verified team agent token"
|
|
766
|
+
);
|
|
767
|
+
return {
|
|
768
|
+
valid: true,
|
|
769
|
+
payload: validPayload
|
|
770
|
+
};
|
|
771
|
+
} catch (error) {
|
|
772
|
+
if (error instanceof Error) {
|
|
773
|
+
logger2.warn({ error: error.message }, "Team agent token verification failed");
|
|
774
|
+
return {
|
|
775
|
+
valid: false,
|
|
776
|
+
error: error.message
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
logger2.warn({ error }, "Team agent token verification failed with unknown error");
|
|
780
|
+
return {
|
|
781
|
+
valid: false,
|
|
782
|
+
error: "Token verification failed"
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
function validateTenantId(payload, expectedTenantId) {
|
|
787
|
+
if (payload.tenantId !== expectedTenantId) {
|
|
788
|
+
logger2.warn(
|
|
789
|
+
{
|
|
790
|
+
tokenTenantId: payload.tenantId,
|
|
791
|
+
expectedTenantId,
|
|
792
|
+
originAgentId: payload.sub,
|
|
793
|
+
targetAgentId: payload.aud
|
|
794
|
+
},
|
|
795
|
+
"Cross-tenant delegation attempt detected"
|
|
796
|
+
);
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
function validateTargetAgent(payload, expectedTargetAgentId) {
|
|
802
|
+
if (payload.aud !== expectedTargetAgentId) {
|
|
803
|
+
logger2.warn(
|
|
804
|
+
{
|
|
805
|
+
tokenTargetAgentId: payload.aud,
|
|
806
|
+
expectedTargetAgentId,
|
|
807
|
+
originAgentId: payload.sub
|
|
808
|
+
},
|
|
809
|
+
"Token target agent mismatch"
|
|
810
|
+
);
|
|
811
|
+
return false;
|
|
812
|
+
}
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
async function verifyAuthorizationHeader(authHeader) {
|
|
816
|
+
if (!authHeader) {
|
|
817
|
+
return {
|
|
818
|
+
valid: false,
|
|
819
|
+
error: "Missing Authorization header"
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
if (!authHeader.startsWith("Bearer ")) {
|
|
823
|
+
return {
|
|
824
|
+
valid: false,
|
|
825
|
+
error: "Invalid Authorization header format. Expected: Bearer <token>"
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const token = authHeader.substring(7);
|
|
829
|
+
if (!token) {
|
|
830
|
+
return {
|
|
831
|
+
valid: false,
|
|
832
|
+
error: "Empty token in Authorization header"
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
return verifyServiceToken(token);
|
|
836
|
+
}
|
|
837
|
+
var logger3 = getLogger("tracer");
|
|
838
|
+
var createNoOpSpan = () => ({
|
|
839
|
+
setAttributes: () => ({}),
|
|
840
|
+
recordException: () => ({}),
|
|
841
|
+
setStatus: () => ({}),
|
|
842
|
+
addEvent: () => ({}),
|
|
843
|
+
end: () => {
|
|
844
|
+
},
|
|
845
|
+
isRecording: () => false,
|
|
846
|
+
setAttribute: () => ({}),
|
|
847
|
+
updateName: () => ({}),
|
|
848
|
+
spanContext: () => ({
|
|
849
|
+
traceId: "00000000000000000000000000000000",
|
|
850
|
+
spanId: "0000000000000000",
|
|
851
|
+
traceFlags: 0
|
|
852
|
+
}),
|
|
853
|
+
addLink: () => ({}),
|
|
854
|
+
addLinks: () => ({})
|
|
855
|
+
});
|
|
856
|
+
var noopTracer = {
|
|
857
|
+
startActiveSpan(_name, arg1, arg2, arg3) {
|
|
858
|
+
const fn = typeof arg1 === "function" ? arg1 : typeof arg2 === "function" ? arg2 : arg3;
|
|
859
|
+
if (!fn) throw new Error("No callback function provided");
|
|
860
|
+
return fn(createNoOpSpan());
|
|
861
|
+
},
|
|
862
|
+
startSpan(_name, _options) {
|
|
863
|
+
return createNoOpSpan();
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
function setSpanWithError(span, error, logger4, logMessage) {
|
|
867
|
+
span.recordException(error);
|
|
868
|
+
span.setStatus({
|
|
869
|
+
code: SpanStatusCode.ERROR,
|
|
870
|
+
message: error.message
|
|
871
|
+
});
|
|
872
|
+
if (logger4 && logMessage) {
|
|
873
|
+
logger4.error({ error: error.message }, logMessage);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function getTracer(serviceName, serviceVersion) {
|
|
877
|
+
try {
|
|
878
|
+
return trace.getTracer(serviceName, serviceVersion);
|
|
879
|
+
} catch (_error) {
|
|
880
|
+
logger3.debug({}, "OpenTelemetry tracer not available, using no-op tracer");
|
|
881
|
+
return noopTracer;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
var FILENAME = fileURLToPath(import.meta.url);
|
|
885
|
+
var DIRNAME = dirname(FILENAME);
|
|
886
|
+
async function createTestDatabaseClient(drizzleDir) {
|
|
887
|
+
const client = new PGlite();
|
|
888
|
+
const db = drizzle(client, { schema: schema_exports });
|
|
889
|
+
try {
|
|
890
|
+
if (!drizzleDir) {
|
|
891
|
+
drizzleDir = join(DIRNAME, "../../drizzle");
|
|
892
|
+
}
|
|
893
|
+
await migrate(db, { migrationsFolder: drizzleDir });
|
|
894
|
+
} catch (error) {
|
|
895
|
+
console.error("Failed to initialize test database schema:", error);
|
|
896
|
+
throw error;
|
|
897
|
+
}
|
|
898
|
+
return db;
|
|
899
|
+
}
|
|
900
|
+
async function cleanupTestDatabase(db) {
|
|
901
|
+
try {
|
|
902
|
+
const result = await db.execute(
|
|
903
|
+
sql.raw(`
|
|
904
|
+
SELECT tablename
|
|
905
|
+
FROM pg_tables
|
|
906
|
+
WHERE schemaname = 'public'
|
|
907
|
+
`)
|
|
908
|
+
);
|
|
909
|
+
const tables = result.rows.map((row) => row.tablename);
|
|
910
|
+
if (tables.length === 0) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const tableList = tables.map((t) => `"${t}"`).join(", ");
|
|
914
|
+
await db.execute(sql.raw(`TRUNCATE TABLE ${tableList} RESTART IDENTITY CASCADE`));
|
|
915
|
+
} catch (error) {
|
|
916
|
+
console.debug("Could not clean test database:", error);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
async function closeTestDatabase(db) {
|
|
920
|
+
try {
|
|
921
|
+
if ("close" in db && typeof db.close === "function") {
|
|
922
|
+
db.close();
|
|
923
|
+
}
|
|
924
|
+
} catch (error) {
|
|
925
|
+
console.debug("Error closing database:", error);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
async function createTestOrganization(db, tenantId) {
|
|
929
|
+
const slug = tenantId.replace(/^test-tenant-/, "").substring(0, 50);
|
|
930
|
+
await db.insert(organization).values({
|
|
931
|
+
id: tenantId,
|
|
932
|
+
name: `Test Organization ${tenantId}`,
|
|
933
|
+
slug,
|
|
934
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
935
|
+
metadata: null
|
|
936
|
+
}).onConflictDoNothing();
|
|
937
|
+
}
|
|
938
|
+
async function createTestProject(db, tenantId, projectId = "default") {
|
|
939
|
+
await createTestOrganization(db, tenantId);
|
|
940
|
+
await db.insert(projects).values({
|
|
941
|
+
tenantId,
|
|
942
|
+
id: projectId,
|
|
943
|
+
name: `Test Project ${projectId}`,
|
|
944
|
+
description: `Test project for ${projectId}`
|
|
945
|
+
}).onConflictDoNothing();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
export { CONVERSATION_HISTORY_DEFAULT_LIMIT, CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT, ERROR_DOCS_BASE_URL, ErrorCode, MCP_TOOL_CONNECTION_TIMEOUT_MS, MCP_TOOL_INITIAL_RECONNECTION_DELAY_MS, MCP_TOOL_MAX_RECONNECTION_DELAY_MS, MCP_TOOL_MAX_RETRIES, MCP_TOOL_RECONNECTION_DELAY_GROWTH_FACTOR, McpClient, cleanupTestDatabase, closeTestDatabase, commonCreateErrorResponses, commonDeleteErrorResponses, commonGetErrorResponses, commonUpdateErrorResponses, createApiError, createExecutionContext, createTestDatabaseClient, createTestOrganization, createTestProject, env, errorResponseSchema, errorSchemaFactory, executionLimitsSharedDefaults, extractPublicId, generateApiKey, generateId, generateServiceToken, getConversationId, getCredentialStoreLookupKeyFromRetrievalParams, getRequestExecutionContext, getTracer, handleApiError, hashApiKey, isApiKeyExpired, loadEnvironmentFiles, maskApiKey, normalizeDateString, problemDetailsSchema, setSpanWithError, validateApiKey, validateTargetAgent, validateTenantId, verifyAuthorizationHeader, verifyServiceToken };
|