@personaai/runtime 0.5.2 → 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 +4 -4
- package/dist/index.cjs +231 -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 +231 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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,
|
|
@@ -1348,23 +1406,86 @@ function createRuntime(options) {
|
|
|
1348
1406
|
if (!options.baseUrl) throw new Error('createRuntime: "baseUrl" is required');
|
|
1349
1407
|
if (!options.credential) throw new Error('createRuntime: "credential" is required');
|
|
1350
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
|
+
);
|
|
1351
1413
|
const capabilities = resolveCapabilities(options.capabilities);
|
|
1352
1414
|
const routes = buildRoutes(capabilities);
|
|
1353
1415
|
const mode = resolveMode(options);
|
|
1354
1416
|
const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 15e3;
|
|
1355
1417
|
const runGraceMs = options.runGraceMs ?? DEFAULT_RUN_GRACE_MS;
|
|
1356
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
|
+
});
|
|
1357
1438
|
const runs = /* @__PURE__ */ new Map();
|
|
1358
|
-
const evictionTimer = setInterval(
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
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);
|
|
1362
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
|
+
}
|
|
1363
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));
|
|
1364
1475
|
try {
|
|
1365
1476
|
const path = stripMountPath(request.path, options.mountPath);
|
|
1477
|
+
logger.trace("stripMountPath", {
|
|
1478
|
+
originalPath: request.path,
|
|
1479
|
+
mountPath: options.mountPath ?? "",
|
|
1480
|
+
strippedPath: path
|
|
1481
|
+
});
|
|
1366
1482
|
const match = matchRoute(routes, request.method, path);
|
|
1367
1483
|
if (match.kind === "not-found") {
|
|
1484
|
+
logger.warn("route not found", {
|
|
1485
|
+
method: request.method,
|
|
1486
|
+
path: request.path,
|
|
1487
|
+
strippedPath: path
|
|
1488
|
+
});
|
|
1368
1489
|
throw new RuntimeHttpError(
|
|
1369
1490
|
404,
|
|
1370
1491
|
"NOT_FOUND",
|
|
@@ -1372,49 +1493,144 @@ function createRuntime(options) {
|
|
|
1372
1493
|
);
|
|
1373
1494
|
}
|
|
1374
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
|
+
});
|
|
1375
1502
|
const err = new RuntimeHttpError(
|
|
1376
1503
|
405,
|
|
1377
1504
|
"METHOD_NOT_ALLOWED",
|
|
1378
1505
|
`${request.method} not allowed on ${request.path}. Allowed: ${match.allowed.join(", ")}.`
|
|
1379
1506
|
);
|
|
1380
|
-
const
|
|
1381
|
-
|
|
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(", ") } };
|
|
1382
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
|
+
});
|
|
1383
1534
|
let userId = null;
|
|
1384
1535
|
if (match.route.requiresAuth !== false) {
|
|
1536
|
+
logger.debug("resolving user", { path: request.path });
|
|
1385
1537
|
try {
|
|
1386
1538
|
const resolved = await options.resolveUser(request);
|
|
1387
1539
|
userId = typeof resolved === "string" && resolved.length > 0 ? resolved : null;
|
|
1388
|
-
|
|
1540
|
+
logger.trace("resolveUser result", { userId: userId ?? null, hasResult: !!userId });
|
|
1541
|
+
} catch (err) {
|
|
1389
1542
|
userId = null;
|
|
1543
|
+
logger.warn("resolveUser threw", {
|
|
1544
|
+
path: request.path,
|
|
1545
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1546
|
+
});
|
|
1390
1547
|
}
|
|
1391
1548
|
if (userId === null) {
|
|
1549
|
+
logger.warn("unauthorized \u2014 could not resolve user", {
|
|
1550
|
+
method: request.method,
|
|
1551
|
+
path: request.path
|
|
1552
|
+
});
|
|
1392
1553
|
throw new RuntimeHttpError(
|
|
1393
1554
|
401,
|
|
1394
1555
|
"UNAUTHORIZED",
|
|
1395
1556
|
"Could not resolve an authenticated user for this request."
|
|
1396
1557
|
);
|
|
1397
1558
|
}
|
|
1559
|
+
logger.debug("user resolved", { userId });
|
|
1560
|
+
} else {
|
|
1561
|
+
logger.debug("skipping auth for public route", { path: request.path });
|
|
1398
1562
|
}
|
|
1399
1563
|
const resolvedRequest = { ...request, userId };
|
|
1400
|
-
const client = createClientForRequest(options, userId);
|
|
1401
|
-
|
|
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, {
|
|
1402
1573
|
client,
|
|
1403
1574
|
hooks: options.hooks,
|
|
1404
1575
|
mode,
|
|
1405
1576
|
params: match.params,
|
|
1406
1577
|
heartbeatIntervalMs,
|
|
1407
1578
|
runs,
|
|
1408
|
-
capabilities
|
|
1579
|
+
capabilities,
|
|
1580
|
+
logger: routeLogger
|
|
1409
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
|
|
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;
|
|
1410
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 });
|
|
1411
1625
|
return errorToResponse(err, mode);
|
|
1412
1626
|
}
|
|
1413
1627
|
}
|
|
1414
1628
|
return {
|
|
1415
1629
|
handle,
|
|
1416
1630
|
close() {
|
|
1631
|
+
logger.debug("runtime close", { trackedRuns: runs.size });
|
|
1417
1632
|
clearInterval(evictionTimer);
|
|
1633
|
+
logger.info("runtime closed", { trackedRuns: runs.size });
|
|
1418
1634
|
}
|
|
1419
1635
|
};
|
|
1420
1636
|
}
|