@personaai/runtime 0.5.1 → 0.5.3
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/README.md +421 -416
- package/dist/index.cjs +243 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +243 -15
- package/dist/index.js.map +1 -1
- package/package.json +71 -71
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatMessageInput } from '@personaai/sdk';
|
|
1
|
+
import { ChatMessageInput, LogLevel, Logger } from '@personaai/sdk';
|
|
2
2
|
|
|
3
3
|
type RuntimeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
4
4
|
/** A single uploaded file — present on `RuntimeRequest.file` for a multipart `POST /files`. */
|
|
@@ -220,6 +220,10 @@ interface CreateRuntimeOptions {
|
|
|
220
220
|
maxTrackedRuns?: number;
|
|
221
221
|
/** Opt-in switches for Project-level admin surface. See {@link RuntimeCapabilities} — everything defaults to off. */
|
|
222
222
|
capabilities?: RuntimeCapabilities;
|
|
223
|
+
/** Log level for the runtime — off by default. Overrides the global level set via `setLogLevel()` from `@personaai/sdk`. */
|
|
224
|
+
logLevel?: LogLevel;
|
|
225
|
+
/** Custom logger instance — when provided, `logLevel` is ignored. */
|
|
226
|
+
logger?: Logger;
|
|
223
227
|
}
|
|
224
228
|
interface Runtime {
|
|
225
229
|
handle(request: RuntimeRequest): Promise<RuntimeResponse>;
|
|
@@ -242,6 +246,6 @@ declare class RuntimeHttpError extends Error {
|
|
|
242
246
|
constructor(status: number, code: string, message: string);
|
|
243
247
|
}
|
|
244
248
|
|
|
245
|
-
declare const RUNTIME_VERSION = "0.5.
|
|
249
|
+
declare const RUNTIME_VERSION = "0.5.3";
|
|
246
250
|
|
|
247
251
|
export { type CreateRuntimeOptions, type ErrorContext, type FileUploadContext, type MemoryWriteContext, RUNTIME_VERSION, type ResolveUser, type RunContext, type RunResult, type Runtime, type RuntimeBinaryResponse, type RuntimeBufferedResponse, type RuntimeCapabilities, type RuntimeHooks, RuntimeHttpError, type RuntimeMethod, type RuntimeRequest, type RuntimeResponse, type RuntimeStreamResponse, type RuntimeUploadedFile, type ThreadCreateContext, type ToolCallContext, createRuntime };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatMessageInput } from '@personaai/sdk';
|
|
1
|
+
import { ChatMessageInput, LogLevel, Logger } from '@personaai/sdk';
|
|
2
2
|
|
|
3
3
|
type RuntimeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
4
4
|
/** A single uploaded file — present on `RuntimeRequest.file` for a multipart `POST /files`. */
|
|
@@ -220,6 +220,10 @@ interface CreateRuntimeOptions {
|
|
|
220
220
|
maxTrackedRuns?: number;
|
|
221
221
|
/** Opt-in switches for Project-level admin surface. See {@link RuntimeCapabilities} — everything defaults to off. */
|
|
222
222
|
capabilities?: RuntimeCapabilities;
|
|
223
|
+
/** Log level for the runtime — off by default. Overrides the global level set via `setLogLevel()` from `@personaai/sdk`. */
|
|
224
|
+
logLevel?: LogLevel;
|
|
225
|
+
/** Custom logger instance — when provided, `logLevel` is ignored. */
|
|
226
|
+
logger?: Logger;
|
|
223
227
|
}
|
|
224
228
|
interface Runtime {
|
|
225
229
|
handle(request: RuntimeRequest): Promise<RuntimeResponse>;
|
|
@@ -242,6 +246,6 @@ declare class RuntimeHttpError extends Error {
|
|
|
242
246
|
constructor(status: number, code: string, message: string);
|
|
243
247
|
}
|
|
244
248
|
|
|
245
|
-
declare const RUNTIME_VERSION = "0.5.
|
|
249
|
+
declare const RUNTIME_VERSION = "0.5.3";
|
|
246
250
|
|
|
247
251
|
export { type CreateRuntimeOptions, type ErrorContext, type FileUploadContext, type MemoryWriteContext, RUNTIME_VERSION, type ResolveUser, type RunContext, type RunResult, type Runtime, type RuntimeBinaryResponse, type RuntimeBufferedResponse, type RuntimeCapabilities, type RuntimeHooks, RuntimeHttpError, type RuntimeMethod, type RuntimeRequest, type RuntimeResponse, type RuntimeStreamResponse, type RuntimeUploadedFile, type ThreadCreateContext, type ToolCallContext, createRuntime };
|
package/dist/index.js
CHANGED
|
@@ -41,12 +41,14 @@ function stripMountPath(path, mountPath) {
|
|
|
41
41
|
|
|
42
42
|
// src/client-factory.ts
|
|
43
43
|
import { PersonaClient } from "@personaai/sdk";
|
|
44
|
-
function createClientForRequest(options, userId) {
|
|
44
|
+
function createClientForRequest(options, userId, logger) {
|
|
45
|
+
const clientLogger = logger ? logger.child("sdk") : void 0;
|
|
45
46
|
return new PersonaClient({
|
|
46
47
|
baseUrl: options.baseUrl,
|
|
47
48
|
credential: options.credential,
|
|
48
49
|
externalUserId: userId ?? void 0,
|
|
49
|
-
fetch: options.fetch
|
|
50
|
+
fetch: options.fetch,
|
|
51
|
+
logger: clientLogger
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -105,6 +107,9 @@ function errorToResponse(err, mode) {
|
|
|
105
107
|
};
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
// src/runtime.ts
|
|
111
|
+
import { createLogger } from "@personaai/sdk";
|
|
112
|
+
|
|
108
113
|
// src/runRegistry.ts
|
|
109
114
|
var DEFAULT_RUN_GRACE_MS = 5 * 60 * 1e3;
|
|
110
115
|
var DEFAULT_MAX_TRACKED_RUNS = 1e3;
|
|
@@ -126,7 +131,7 @@ function evictStaleRuns(runs, now, graceMs = DEFAULT_RUN_GRACE_MS, maxTracked =
|
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
// src/version.ts
|
|
129
|
-
var RUNTIME_VERSION = "0.5.
|
|
134
|
+
var RUNTIME_VERSION = "0.5.3";
|
|
130
135
|
|
|
131
136
|
// src/routes/health.ts
|
|
132
137
|
var alwaysOnCapabilities = {
|
|
@@ -448,7 +453,16 @@ function parseChatBody(body) {
|
|
|
448
453
|
};
|
|
449
454
|
}
|
|
450
455
|
var chatRoute = async (request, ctx) => {
|
|
456
|
+
const logger = ctx.logger.child("chat");
|
|
457
|
+
logger.debug("chatRoute start", { userId: request.userId, hasBody: !!request.body });
|
|
451
458
|
const body = parseChatBody(request.body);
|
|
459
|
+
logger.trace("chatRoute body", {
|
|
460
|
+
agentId: body.agentId,
|
|
461
|
+
threadId: body.threadId,
|
|
462
|
+
messageCount: body.messages?.length ?? 0,
|
|
463
|
+
hasResume: !!body.resume,
|
|
464
|
+
hasContextOverride: !!body.contextOverride
|
|
465
|
+
});
|
|
452
466
|
const userId = request.userId;
|
|
453
467
|
const runCtx = {
|
|
454
468
|
userId,
|
|
@@ -457,7 +471,10 @@ var chatRoute = async (request, ctx) => {
|
|
|
457
471
|
threadId: body.threadId,
|
|
458
472
|
messages: body.messages
|
|
459
473
|
};
|
|
474
|
+
logger.debug("beforeRun hook", { agentId: body.agentId, threadId: body.threadId });
|
|
460
475
|
await ctx.hooks?.beforeRun?.(runCtx);
|
|
476
|
+
logger.trace("beforeRun completed", { agentId: body.agentId });
|
|
477
|
+
logger.debug("starting chat stream", { agentId: body.agentId, threadId: body.threadId });
|
|
461
478
|
const stream = ctx.client.chat.stream(body.agentId, {
|
|
462
479
|
messages: body.messages,
|
|
463
480
|
threadId: body.threadId,
|
|
@@ -465,9 +482,28 @@ var chatRoute = async (request, ctx) => {
|
|
|
465
482
|
contextOverride: body.contextOverride
|
|
466
483
|
});
|
|
467
484
|
const runId = crypto.randomUUID();
|
|
485
|
+
logger.info("chat run created", {
|
|
486
|
+
runId,
|
|
487
|
+
agentId: body.agentId,
|
|
488
|
+
threadId: body.threadId,
|
|
489
|
+
userId
|
|
490
|
+
});
|
|
468
491
|
const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
|
|
469
492
|
ctx.runs.set(runId, driver);
|
|
470
|
-
|
|
493
|
+
logger.debug("run driver registered", { runId, trackedRuns: ctx.runs.size });
|
|
494
|
+
try {
|
|
495
|
+
await driver.waitForFirstFrame();
|
|
496
|
+
logger.debug("chat run first frame ready", { runId });
|
|
497
|
+
} catch (err) {
|
|
498
|
+
logger.warn("chat run failed before first frame", {
|
|
499
|
+
runId,
|
|
500
|
+
agentId: body.agentId,
|
|
501
|
+
error: err instanceof Error ? err.message : String(err)
|
|
502
|
+
});
|
|
503
|
+
ctx.runs.delete(runId);
|
|
504
|
+
throw err;
|
|
505
|
+
}
|
|
506
|
+
logger.info("chat stream ready", { runId, agentId: body.agentId });
|
|
471
507
|
return {
|
|
472
508
|
kind: "stream",
|
|
473
509
|
status: 200,
|
|
@@ -500,22 +536,44 @@ function parseArchitectBody(body) {
|
|
|
500
536
|
};
|
|
501
537
|
}
|
|
502
538
|
var architectRoute = async (request, ctx) => {
|
|
539
|
+
const logger = ctx.logger.child("architect");
|
|
540
|
+
logger.debug("architectRoute start", { userId: request.userId });
|
|
503
541
|
const body = parseArchitectBody(request.body);
|
|
542
|
+
logger.trace("architectRoute body", {
|
|
543
|
+
messageCount: body.messages?.length ?? 0,
|
|
544
|
+
hasResume: !!body.resume
|
|
545
|
+
});
|
|
504
546
|
const userId = request.userId;
|
|
505
547
|
const runCtx = {
|
|
506
548
|
userId,
|
|
507
549
|
kind: "architect",
|
|
508
550
|
messages: body.messages
|
|
509
551
|
};
|
|
552
|
+
logger.debug("beforeRun hook (architect)", { userId });
|
|
510
553
|
await ctx.hooks?.beforeRun?.(runCtx);
|
|
554
|
+
logger.trace("beforeRun completed (architect)");
|
|
555
|
+
logger.debug("starting architect stream");
|
|
511
556
|
const stream = ctx.client.architect.stream({
|
|
512
557
|
messages: body.messages,
|
|
513
558
|
resume: body.resume
|
|
514
559
|
});
|
|
515
560
|
const runId = crypto.randomUUID();
|
|
561
|
+
logger.info("architect run created", { runId, userId });
|
|
516
562
|
const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
|
|
517
563
|
ctx.runs.set(runId, driver);
|
|
518
|
-
|
|
564
|
+
logger.debug("architect driver registered", { runId, trackedRuns: ctx.runs.size });
|
|
565
|
+
try {
|
|
566
|
+
await driver.waitForFirstFrame();
|
|
567
|
+
logger.debug("architect first frame ready", { runId });
|
|
568
|
+
} catch (err) {
|
|
569
|
+
logger.warn("architect run failed before first frame", {
|
|
570
|
+
runId,
|
|
571
|
+
error: err instanceof Error ? err.message : String(err)
|
|
572
|
+
});
|
|
573
|
+
ctx.runs.delete(runId);
|
|
574
|
+
throw err;
|
|
575
|
+
}
|
|
576
|
+
logger.info("architect stream ready", { runId });
|
|
519
577
|
return {
|
|
520
578
|
kind: "stream",
|
|
521
579
|
status: 200,
|
|
@@ -664,6 +722,13 @@ var listAgents = async (request, ctx) => {
|
|
|
664
722
|
});
|
|
665
723
|
return json(200, items);
|
|
666
724
|
};
|
|
725
|
+
var getAgentMcpConnections = async (request, ctx) => {
|
|
726
|
+
const connections = await ctx.client.agents.getMcpConnections(
|
|
727
|
+
requireParam(ctx.params, "id"),
|
|
728
|
+
request.query.returnTo
|
|
729
|
+
);
|
|
730
|
+
return json(200, connections);
|
|
731
|
+
};
|
|
667
732
|
var createAgent = async (request, ctx) => {
|
|
668
733
|
const body = requireBodyObject(request.body);
|
|
669
734
|
const input = {
|
|
@@ -1224,6 +1289,11 @@ function buildRoutes(capabilities) {
|
|
|
1224
1289
|
method: "DELETE",
|
|
1225
1290
|
pattern: ["mcps", ":id", "oauth", "owner", "connection"],
|
|
1226
1291
|
handler: disconnectOwnerConnection
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
method: "GET",
|
|
1295
|
+
pattern: ["agents", ":id", "mcp-connections"],
|
|
1296
|
+
handler: getAgentMcpConnections
|
|
1227
1297
|
}
|
|
1228
1298
|
];
|
|
1229
1299
|
if (capabilities.agentsWrite) {
|
|
@@ -1336,23 +1406,86 @@ function createRuntime(options) {
|
|
|
1336
1406
|
if (!options.baseUrl) throw new Error('createRuntime: "baseUrl" is required');
|
|
1337
1407
|
if (!options.credential) throw new Error('createRuntime: "credential" is required');
|
|
1338
1408
|
if (!options.resolveUser) throw new Error('createRuntime: "resolveUser" is required');
|
|
1409
|
+
const logger = options.logger ?? createLogger(
|
|
1410
|
+
"runtime",
|
|
1411
|
+
options.logLevel !== void 0 ? { level: options.logLevel } : void 0
|
|
1412
|
+
);
|
|
1339
1413
|
const capabilities = resolveCapabilities(options.capabilities);
|
|
1340
1414
|
const routes = buildRoutes(capabilities);
|
|
1341
1415
|
const mode = resolveMode(options);
|
|
1342
1416
|
const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 15e3;
|
|
1343
1417
|
const runGraceMs = options.runGraceMs ?? DEFAULT_RUN_GRACE_MS;
|
|
1344
1418
|
const maxTrackedRuns = options.maxTrackedRuns ?? DEFAULT_MAX_TRACKED_RUNS;
|
|
1419
|
+
logger.debug("runtime init", {
|
|
1420
|
+
mode,
|
|
1421
|
+
mountPath: options.mountPath ?? "",
|
|
1422
|
+
capabilities,
|
|
1423
|
+
heartbeatIntervalMs,
|
|
1424
|
+
runGraceMs,
|
|
1425
|
+
maxTrackedRuns
|
|
1426
|
+
});
|
|
1427
|
+
const enabledCaps = Object.entries(capabilities).filter(([, v]) => v).map(([k]) => k);
|
|
1428
|
+
logger.info("runtime created", {
|
|
1429
|
+
mode,
|
|
1430
|
+
mountPath: options.mountPath ?? "",
|
|
1431
|
+
enabledCapabilities: enabledCaps.length ? enabledCaps : ["(core only)"]
|
|
1432
|
+
});
|
|
1433
|
+
logger.trace("runtime config", {
|
|
1434
|
+
baseUrl: options.baseUrl,
|
|
1435
|
+
hasHooks: !!options.hooks,
|
|
1436
|
+
routeCount: routes.length
|
|
1437
|
+
});
|
|
1345
1438
|
const runs = /* @__PURE__ */ new Map();
|
|
1346
|
-
const evictionTimer = setInterval(
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1439
|
+
const evictionTimer = setInterval(() => {
|
|
1440
|
+
const before = runs.size;
|
|
1441
|
+
evictStaleRuns(runs, Date.now(), runGraceMs, maxTrackedRuns);
|
|
1442
|
+
if (runs.size !== before) {
|
|
1443
|
+
logger.debug("run eviction sweep", { before, after: runs.size, runGraceMs, maxTrackedRuns });
|
|
1444
|
+
} else {
|
|
1445
|
+
logger.trace("run eviction sweep \u2014 no evictions", { before });
|
|
1446
|
+
}
|
|
1447
|
+
}, 6e4);
|
|
1350
1448
|
evictionTimer.unref?.();
|
|
1449
|
+
function getRequestPreview(req) {
|
|
1450
|
+
const preview = {
|
|
1451
|
+
method: req.method,
|
|
1452
|
+
path: req.path,
|
|
1453
|
+
hasQuery: req.query && Object.keys(req.query).length > 0,
|
|
1454
|
+
hasBody: req.body !== void 0,
|
|
1455
|
+
hasFile: !!req.file,
|
|
1456
|
+
fileCount: req.files?.length ?? 0,
|
|
1457
|
+
userId: req.userId ?? null
|
|
1458
|
+
};
|
|
1459
|
+
if (req.headers) {
|
|
1460
|
+
const redacted = {};
|
|
1461
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
1462
|
+
if (v === void 0) continue;
|
|
1463
|
+
if (k.toLowerCase() === "authorization") redacted[k] = "***";
|
|
1464
|
+
else redacted[k] = v;
|
|
1465
|
+
}
|
|
1466
|
+
preview.headers = redacted;
|
|
1467
|
+
}
|
|
1468
|
+
if (req.query && Object.keys(req.query).length) preview.query = req.query;
|
|
1469
|
+
return preview;
|
|
1470
|
+
}
|
|
1351
1471
|
async function handle(request) {
|
|
1472
|
+
const startMs = Date.now();
|
|
1473
|
+
logger.debug("handle start", { method: request.method, path: request.path });
|
|
1474
|
+
logger.trace("handle request details", getRequestPreview(request));
|
|
1352
1475
|
try {
|
|
1353
1476
|
const path = stripMountPath(request.path, options.mountPath);
|
|
1477
|
+
logger.trace("stripMountPath", {
|
|
1478
|
+
originalPath: request.path,
|
|
1479
|
+
mountPath: options.mountPath ?? "",
|
|
1480
|
+
strippedPath: path
|
|
1481
|
+
});
|
|
1354
1482
|
const match = matchRoute(routes, request.method, path);
|
|
1355
1483
|
if (match.kind === "not-found") {
|
|
1484
|
+
logger.warn("route not found", {
|
|
1485
|
+
method: request.method,
|
|
1486
|
+
path: request.path,
|
|
1487
|
+
strippedPath: path
|
|
1488
|
+
});
|
|
1356
1489
|
throw new RuntimeHttpError(
|
|
1357
1490
|
404,
|
|
1358
1491
|
"NOT_FOUND",
|
|
@@ -1360,49 +1493,144 @@ function createRuntime(options) {
|
|
|
1360
1493
|
);
|
|
1361
1494
|
}
|
|
1362
1495
|
if (match.kind === "method-not-allowed") {
|
|
1496
|
+
logger.warn("method not allowed", {
|
|
1497
|
+
method: request.method,
|
|
1498
|
+
path: request.path,
|
|
1499
|
+
strippedPath: path,
|
|
1500
|
+
allowed: match.allowed
|
|
1501
|
+
});
|
|
1363
1502
|
const err = new RuntimeHttpError(
|
|
1364
1503
|
405,
|
|
1365
1504
|
"METHOD_NOT_ALLOWED",
|
|
1366
1505
|
`${request.method} not allowed on ${request.path}. Allowed: ${match.allowed.join(", ")}.`
|
|
1367
1506
|
);
|
|
1368
|
-
const
|
|
1369
|
-
|
|
1507
|
+
const response2 = errorToResponse(err, mode);
|
|
1508
|
+
logger.debug("handle completed \u2014 405", {
|
|
1509
|
+
method: request.method,
|
|
1510
|
+
path: request.path,
|
|
1511
|
+
allowed: match.allowed,
|
|
1512
|
+
durationMs: Date.now() - startMs
|
|
1513
|
+
});
|
|
1514
|
+
return { ...response2, headers: { ...response2.headers, Allow: match.allowed.join(", ") } };
|
|
1370
1515
|
}
|
|
1516
|
+
logger.info("route matched", {
|
|
1517
|
+
method: request.method,
|
|
1518
|
+
path: request.path,
|
|
1519
|
+
strippedPath: path,
|
|
1520
|
+
pattern: match.route.pattern.join("/"),
|
|
1521
|
+
params: match.params,
|
|
1522
|
+
requiresAuth: match.route.requiresAuth !== false
|
|
1523
|
+
});
|
|
1524
|
+
logger.debug("route matched", {
|
|
1525
|
+
method: request.method,
|
|
1526
|
+
path,
|
|
1527
|
+
pattern: match.route.pattern,
|
|
1528
|
+
params: match.params
|
|
1529
|
+
});
|
|
1530
|
+
logger.trace("route handler", {
|
|
1531
|
+
handler: match.route.handler.name || "anonymous",
|
|
1532
|
+
pattern: match.route.pattern
|
|
1533
|
+
});
|
|
1371
1534
|
let userId = null;
|
|
1372
1535
|
if (match.route.requiresAuth !== false) {
|
|
1536
|
+
logger.debug("resolving user", { path: request.path });
|
|
1373
1537
|
try {
|
|
1374
1538
|
const resolved = await options.resolveUser(request);
|
|
1375
1539
|
userId = typeof resolved === "string" && resolved.length > 0 ? resolved : null;
|
|
1376
|
-
|
|
1540
|
+
logger.trace("resolveUser result", { userId: userId ?? null, hasResult: !!userId });
|
|
1541
|
+
} catch (err) {
|
|
1377
1542
|
userId = null;
|
|
1543
|
+
logger.warn("resolveUser threw", {
|
|
1544
|
+
path: request.path,
|
|
1545
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1546
|
+
});
|
|
1378
1547
|
}
|
|
1379
1548
|
if (userId === null) {
|
|
1549
|
+
logger.warn("unauthorized \u2014 could not resolve user", {
|
|
1550
|
+
method: request.method,
|
|
1551
|
+
path: request.path
|
|
1552
|
+
});
|
|
1380
1553
|
throw new RuntimeHttpError(
|
|
1381
1554
|
401,
|
|
1382
1555
|
"UNAUTHORIZED",
|
|
1383
1556
|
"Could not resolve an authenticated user for this request."
|
|
1384
1557
|
);
|
|
1385
1558
|
}
|
|
1559
|
+
logger.debug("user resolved", { userId });
|
|
1560
|
+
} else {
|
|
1561
|
+
logger.debug("skipping auth for public route", { path: request.path });
|
|
1386
1562
|
}
|
|
1387
1563
|
const resolvedRequest = { ...request, userId };
|
|
1388
|
-
const client = createClientForRequest(options, userId);
|
|
1389
|
-
|
|
1564
|
+
const client = createClientForRequest(options, userId, logger);
|
|
1565
|
+
const routeLogger = logger.child("route");
|
|
1566
|
+
logger.debug("handler start", {
|
|
1567
|
+
method: request.method,
|
|
1568
|
+
path: request.path,
|
|
1569
|
+
handler: match.route.handler.name || "anonymous",
|
|
1570
|
+
userId
|
|
1571
|
+
});
|
|
1572
|
+
const response = await match.route.handler(resolvedRequest, {
|
|
1390
1573
|
client,
|
|
1391
1574
|
hooks: options.hooks,
|
|
1392
1575
|
mode,
|
|
1393
1576
|
params: match.params,
|
|
1394
1577
|
heartbeatIntervalMs,
|
|
1395
1578
|
runs,
|
|
1396
|
-
capabilities
|
|
1579
|
+
capabilities,
|
|
1580
|
+
logger: routeLogger
|
|
1581
|
+
});
|
|
1582
|
+
const durationMs = Date.now() - startMs;
|
|
1583
|
+
logger.info("handle succeeded", {
|
|
1584
|
+
method: request.method,
|
|
1585
|
+
path: request.path,
|
|
1586
|
+
status: response.status,
|
|
1587
|
+
kind: response.kind,
|
|
1588
|
+
durationMs,
|
|
1589
|
+
userId
|
|
1590
|
+
});
|
|
1591
|
+
logger.debug("handle completed", {
|
|
1592
|
+
method: request.method,
|
|
1593
|
+
path: request.path,
|
|
1594
|
+
status: response.status,
|
|
1595
|
+
durationMs
|
|
1397
1596
|
});
|
|
1597
|
+
logger.trace("handle response details", {
|
|
1598
|
+
method: request.method,
|
|
1599
|
+
path: request.path,
|
|
1600
|
+
status: response.status,
|
|
1601
|
+
headers: response.headers,
|
|
1602
|
+
kind: response.kind,
|
|
1603
|
+
durationMs
|
|
1604
|
+
});
|
|
1605
|
+
return response;
|
|
1398
1606
|
} catch (err) {
|
|
1607
|
+
const durationMs = Date.now() - startMs;
|
|
1608
|
+
const isHttpError = err instanceof RuntimeHttpError;
|
|
1609
|
+
const meta = {
|
|
1610
|
+
method: request.method,
|
|
1611
|
+
path: request.path,
|
|
1612
|
+
durationMs,
|
|
1613
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1614
|
+
code: isHttpError ? err.code : void 0,
|
|
1615
|
+
status: isHttpError ? err.status : void 0
|
|
1616
|
+
};
|
|
1617
|
+
if (isHttpError && err.status >= 500) {
|
|
1618
|
+
logger.error("handle failed \u2014 server error", meta);
|
|
1619
|
+
} else if (isHttpError && err.status >= 400) {
|
|
1620
|
+
logger.warn("handle failed \u2014 client error", meta);
|
|
1621
|
+
} else {
|
|
1622
|
+
logger.error("handle failed \u2014 unexpected error", meta);
|
|
1623
|
+
}
|
|
1624
|
+
logger.trace("handle error details", { error: err, durationMs });
|
|
1399
1625
|
return errorToResponse(err, mode);
|
|
1400
1626
|
}
|
|
1401
1627
|
}
|
|
1402
1628
|
return {
|
|
1403
1629
|
handle,
|
|
1404
1630
|
close() {
|
|
1631
|
+
logger.debug("runtime close", { trackedRuns: runs.size });
|
|
1405
1632
|
clearInterval(evictionTimer);
|
|
1633
|
+
logger.info("runtime closed", { trackedRuns: runs.size });
|
|
1406
1634
|
}
|
|
1407
1635
|
};
|
|
1408
1636
|
}
|