@eidentic/server 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +23 -7
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +23 -7
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -2085,6 +2085,18 @@ function createServer2(opts) {
|
|
|
2085
2085
|
});
|
|
2086
2086
|
const trustProxy = opts.trustProxy ?? false;
|
|
2087
2087
|
const getClientKey = opts.getClientKey ? opts.getClientKey : (c) => defaultGetClientKey(c, trustProxy);
|
|
2088
|
+
const onAuditEvent = opts.onAuditEvent;
|
|
2089
|
+
const emitAudit = (event) => {
|
|
2090
|
+
if (!onAuditEvent) return;
|
|
2091
|
+
try {
|
|
2092
|
+
onAuditEvent(event);
|
|
2093
|
+
} catch {
|
|
2094
|
+
}
|
|
2095
|
+
};
|
|
2096
|
+
const unauthorized = (c) => {
|
|
2097
|
+
emitAudit({ type: "auth.failure", at: Date.now(), route: c.req.path });
|
|
2098
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
2099
|
+
};
|
|
2088
2100
|
let _draining = false;
|
|
2089
2101
|
const app = new import_hono.Hono({ strict: true });
|
|
2090
2102
|
const base = opts.basePath ?? "";
|
|
@@ -2113,6 +2125,7 @@ function createServer2(opts) {
|
|
|
2113
2125
|
if (retryAfterSec !== void 0) {
|
|
2114
2126
|
c.header("Retry-After", String(retryAfterSec));
|
|
2115
2127
|
}
|
|
2128
|
+
emitAudit({ type: "ratelimit.exceeded", at: Date.now(), principalId: clientKey, route: c.req.path });
|
|
2116
2129
|
return c.json({ error: "rate_limited", retryAfterMs: rl.retryAfterMs }, 429);
|
|
2117
2130
|
}
|
|
2118
2131
|
await next();
|
|
@@ -2150,6 +2163,7 @@ function createServer2(opts) {
|
|
|
2150
2163
|
streamQuotaKey = getQuotaKey(principal, agentId);
|
|
2151
2164
|
const qc = await quota.check(streamQuotaKey);
|
|
2152
2165
|
if (!qc.ok) {
|
|
2166
|
+
emitAudit({ type: "quota.exceeded", at: Date.now(), scopeKey: streamQuotaKey, ...qc.reason !== void 0 ? { reason: qc.reason } : {} });
|
|
2153
2167
|
return c.json({ error: "quota_exceeded", reason: qc.reason, usage: qc.usage }, 402);
|
|
2154
2168
|
}
|
|
2155
2169
|
if (qc.warn) c.header("X-Eidentic-Quota-Warning", "soft-limit");
|
|
@@ -2256,6 +2270,7 @@ function createServer2(opts) {
|
|
|
2256
2270
|
if (!rl.ok) {
|
|
2257
2271
|
const retryAfterSec = rl.retryAfterMs !== void 0 ? Math.ceil(rl.retryAfterMs / 1e3) : void 0;
|
|
2258
2272
|
if (retryAfterSec !== void 0) c.header("Retry-After", String(retryAfterSec));
|
|
2273
|
+
emitAudit({ type: "ratelimit.exceeded", at: Date.now(), principalId: rlKey, route: c.req.path });
|
|
2259
2274
|
return c.json({ error: "rate_limited", retryAfterMs: rl.retryAfterMs }, 429);
|
|
2260
2275
|
}
|
|
2261
2276
|
return null;
|
|
@@ -2265,7 +2280,7 @@ function createServer2(opts) {
|
|
|
2265
2280
|
(0, import_body_limit.bodyLimit)({ maxSize: BODY_LIMIT }),
|
|
2266
2281
|
async (c) => {
|
|
2267
2282
|
const principal = await runAuth(auth, c.req.raw);
|
|
2268
|
-
if (principal === null) return c
|
|
2283
|
+
if (principal === null) return unauthorized(c);
|
|
2269
2284
|
const agentId = c.req.param("agentId");
|
|
2270
2285
|
const rlErr = await checkPostAuthRateLimit(c, principal, agentId);
|
|
2271
2286
|
if (rlErr) return rlErr;
|
|
@@ -2318,7 +2333,7 @@ function createServer2(opts) {
|
|
|
2318
2333
|
(0, import_body_limit.bodyLimit)({ maxSize: BODY_LIMIT }),
|
|
2319
2334
|
async (c) => {
|
|
2320
2335
|
const principal = await runAuth(auth, c.req.raw);
|
|
2321
|
-
if (principal === null) return c
|
|
2336
|
+
if (principal === null) return unauthorized(c);
|
|
2322
2337
|
const agentId = c.req.param("agentId");
|
|
2323
2338
|
const rlErr = await checkPostAuthRateLimit(c, principal, agentId);
|
|
2324
2339
|
if (rlErr) return rlErr;
|
|
@@ -2367,7 +2382,7 @@ function createServer2(opts) {
|
|
|
2367
2382
|
(0, import_body_limit.bodyLimit)({ maxSize: BODY_LIMIT }),
|
|
2368
2383
|
async (c) => {
|
|
2369
2384
|
const principal = await runAuth(auth, c.req.raw);
|
|
2370
|
-
if (principal === null) return c
|
|
2385
|
+
if (principal === null) return unauthorized(c);
|
|
2371
2386
|
const agentId = c.req.param("agentId");
|
|
2372
2387
|
const rlErr = await checkPostAuthRateLimit(c, principal, agentId);
|
|
2373
2388
|
if (rlErr) return rlErr;
|
|
@@ -2416,6 +2431,7 @@ function createServer2(opts) {
|
|
|
2416
2431
|
asyncQuotaKey = getQuotaKey(principal, agentId);
|
|
2417
2432
|
const qc = await quota.check(asyncQuotaKey);
|
|
2418
2433
|
if (!qc.ok) {
|
|
2434
|
+
emitAudit({ type: "quota.exceeded", at: Date.now(), scopeKey: asyncQuotaKey, ...qc.reason !== void 0 ? { reason: qc.reason } : {} });
|
|
2419
2435
|
return c.json({ error: "quota_exceeded", reason: qc.reason, usage: qc.usage }, 402);
|
|
2420
2436
|
}
|
|
2421
2437
|
asyncQuotaReservation = qc.reservation;
|
|
@@ -2507,7 +2523,7 @@ function createServer2(opts) {
|
|
|
2507
2523
|
r.get("/v1/agents/:agentId/runs/:runId/status", async (c) => {
|
|
2508
2524
|
const principal = await runAuth(auth, c.req.raw);
|
|
2509
2525
|
if (principal === null) {
|
|
2510
|
-
return c
|
|
2526
|
+
return unauthorized(c);
|
|
2511
2527
|
}
|
|
2512
2528
|
const agentId = c.req.param("agentId");
|
|
2513
2529
|
const agent = resolve(agentId);
|
|
@@ -2538,7 +2554,7 @@ function createServer2(opts) {
|
|
|
2538
2554
|
r.get("/v1/agents/:agentId/sessions/:sessionId/events", async (c) => {
|
|
2539
2555
|
const principal = await runAuth(auth, c.req.raw);
|
|
2540
2556
|
if (principal === null) {
|
|
2541
|
-
return c
|
|
2557
|
+
return unauthorized(c);
|
|
2542
2558
|
}
|
|
2543
2559
|
const agentId = c.req.param("agentId");
|
|
2544
2560
|
const agent = resolve(agentId);
|
|
@@ -2567,7 +2583,7 @@ function createServer2(opts) {
|
|
|
2567
2583
|
r.get("/v1/workflows", async (c) => {
|
|
2568
2584
|
const principal = await runAuth(auth, c.req.raw);
|
|
2569
2585
|
if (principal === null) {
|
|
2570
|
-
return c
|
|
2586
|
+
return unauthorized(c);
|
|
2571
2587
|
}
|
|
2572
2588
|
const summaries = workflowRuns.list().filter((rec) => checkWorkflowOwnership(rec, principal)).map((rec) => ({
|
|
2573
2589
|
id: rec.id,
|
|
@@ -2582,7 +2598,7 @@ function createServer2(opts) {
|
|
|
2582
2598
|
r.get("/v1/workflows/:id", async (c) => {
|
|
2583
2599
|
const principal = await runAuth(auth, c.req.raw);
|
|
2584
2600
|
if (principal === null) {
|
|
2585
|
-
return c
|
|
2601
|
+
return unauthorized(c);
|
|
2586
2602
|
}
|
|
2587
2603
|
const id = c.req.param("id");
|
|
2588
2604
|
const rec = workflowRuns.get(id);
|
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono from 'hono';
|
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
3
|
import { cors } from 'hono/cors';
|
|
4
4
|
import { Agent } from '@eidentic/core';
|
|
5
|
-
import { RateLimiterPort, RateLimitResult, QuotaPort, QuotaLimits, QuotaCheck, StreamEvent, LoggerPort, Usage, CostBreakdown, AuthPrincipal, AuthPort } from '@eidentic/types';
|
|
5
|
+
import { RateLimiterPort, RateLimitResult, QuotaPort, QuotaLimits, QuotaCheck, StreamEvent, LoggerPort, Usage, CostBreakdown, AuthPrincipal, AuthPort, AuditSink } from '@eidentic/types';
|
|
6
6
|
export { AuthPort, AuthPrincipal, AuthRequest, QuotaCheck, QuotaLimits, QuotaPort, QuotaUsage, RateLimiterPort } from '@eidentic/types';
|
|
7
7
|
import { WorkflowResult, WorkflowRunOwner, RecordOptions, WorkflowRunError, WorkflowRunRegistry, StepTrace } from '@eidentic/workflow';
|
|
8
8
|
export { RecordOptions, WorkflowRunError, WorkflowRunOwner, WorkflowRunRegistry } from '@eidentic/workflow';
|
|
@@ -868,6 +868,14 @@ interface ServerOptions {
|
|
|
868
868
|
* `principal.apiKey ?? principal.userId ?? principal.orgId ?? "anonymous"`.
|
|
869
869
|
*/
|
|
870
870
|
quotaKey?: (principal: AuthPrincipal, agentId: string) => string;
|
|
871
|
+
/**
|
|
872
|
+
* Best-effort audit sink (§10.4/§15). When set, the server emits structured `AuditEvent`s for
|
|
873
|
+
* security-relevant rejections at the HTTP edge: `auth.failure` (401), `quota.exceeded` (402),
|
|
874
|
+
* and `ratelimit.exceeded` (429, both pre-auth and post-auth). This is the same `AuditEvent`
|
|
875
|
+
* stream `Agent` emits (`tool.call` / `permission.denied` / `erasure`) — wire the same sink to
|
|
876
|
+
* both for one unified audit log. A throwing sink is swallowed, never affecting a request.
|
|
877
|
+
*/
|
|
878
|
+
onAuditEvent?: AuditSink;
|
|
871
879
|
/**
|
|
872
880
|
* Maximum number of characters allowed in the `input` field of /query and /runs
|
|
873
881
|
* requests, and in a string `decision` on /resume requests.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono from 'hono';
|
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
3
|
import { cors } from 'hono/cors';
|
|
4
4
|
import { Agent } from '@eidentic/core';
|
|
5
|
-
import { RateLimiterPort, RateLimitResult, QuotaPort, QuotaLimits, QuotaCheck, StreamEvent, LoggerPort, Usage, CostBreakdown, AuthPrincipal, AuthPort } from '@eidentic/types';
|
|
5
|
+
import { RateLimiterPort, RateLimitResult, QuotaPort, QuotaLimits, QuotaCheck, StreamEvent, LoggerPort, Usage, CostBreakdown, AuthPrincipal, AuthPort, AuditSink } from '@eidentic/types';
|
|
6
6
|
export { AuthPort, AuthPrincipal, AuthRequest, QuotaCheck, QuotaLimits, QuotaPort, QuotaUsage, RateLimiterPort } from '@eidentic/types';
|
|
7
7
|
import { WorkflowResult, WorkflowRunOwner, RecordOptions, WorkflowRunError, WorkflowRunRegistry, StepTrace } from '@eidentic/workflow';
|
|
8
8
|
export { RecordOptions, WorkflowRunError, WorkflowRunOwner, WorkflowRunRegistry } from '@eidentic/workflow';
|
|
@@ -868,6 +868,14 @@ interface ServerOptions {
|
|
|
868
868
|
* `principal.apiKey ?? principal.userId ?? principal.orgId ?? "anonymous"`.
|
|
869
869
|
*/
|
|
870
870
|
quotaKey?: (principal: AuthPrincipal, agentId: string) => string;
|
|
871
|
+
/**
|
|
872
|
+
* Best-effort audit sink (§10.4/§15). When set, the server emits structured `AuditEvent`s for
|
|
873
|
+
* security-relevant rejections at the HTTP edge: `auth.failure` (401), `quota.exceeded` (402),
|
|
874
|
+
* and `ratelimit.exceeded` (429, both pre-auth and post-auth). This is the same `AuditEvent`
|
|
875
|
+
* stream `Agent` emits (`tool.call` / `permission.denied` / `erasure`) — wire the same sink to
|
|
876
|
+
* both for one unified audit log. A throwing sink is swallowed, never affecting a request.
|
|
877
|
+
*/
|
|
878
|
+
onAuditEvent?: AuditSink;
|
|
871
879
|
/**
|
|
872
880
|
* Maximum number of characters allowed in the `input` field of /query and /runs
|
|
873
881
|
* requests, and in a string `decision` on /resume requests.
|
package/dist/index.js
CHANGED
|
@@ -1007,6 +1007,18 @@ function createServer(opts) {
|
|
|
1007
1007
|
});
|
|
1008
1008
|
const trustProxy = opts.trustProxy ?? false;
|
|
1009
1009
|
const getClientKey = opts.getClientKey ? opts.getClientKey : (c) => defaultGetClientKey(c, trustProxy);
|
|
1010
|
+
const onAuditEvent = opts.onAuditEvent;
|
|
1011
|
+
const emitAudit = (event) => {
|
|
1012
|
+
if (!onAuditEvent) return;
|
|
1013
|
+
try {
|
|
1014
|
+
onAuditEvent(event);
|
|
1015
|
+
} catch {
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
const unauthorized = (c) => {
|
|
1019
|
+
emitAudit({ type: "auth.failure", at: Date.now(), route: c.req.path });
|
|
1020
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
1021
|
+
};
|
|
1010
1022
|
let _draining = false;
|
|
1011
1023
|
const app = new Hono({ strict: true });
|
|
1012
1024
|
const base = opts.basePath ?? "";
|
|
@@ -1035,6 +1047,7 @@ function createServer(opts) {
|
|
|
1035
1047
|
if (retryAfterSec !== void 0) {
|
|
1036
1048
|
c.header("Retry-After", String(retryAfterSec));
|
|
1037
1049
|
}
|
|
1050
|
+
emitAudit({ type: "ratelimit.exceeded", at: Date.now(), principalId: clientKey, route: c.req.path });
|
|
1038
1051
|
return c.json({ error: "rate_limited", retryAfterMs: rl.retryAfterMs }, 429);
|
|
1039
1052
|
}
|
|
1040
1053
|
await next();
|
|
@@ -1072,6 +1085,7 @@ function createServer(opts) {
|
|
|
1072
1085
|
streamQuotaKey = getQuotaKey(principal, agentId);
|
|
1073
1086
|
const qc = await quota.check(streamQuotaKey);
|
|
1074
1087
|
if (!qc.ok) {
|
|
1088
|
+
emitAudit({ type: "quota.exceeded", at: Date.now(), scopeKey: streamQuotaKey, ...qc.reason !== void 0 ? { reason: qc.reason } : {} });
|
|
1075
1089
|
return c.json({ error: "quota_exceeded", reason: qc.reason, usage: qc.usage }, 402);
|
|
1076
1090
|
}
|
|
1077
1091
|
if (qc.warn) c.header("X-Eidentic-Quota-Warning", "soft-limit");
|
|
@@ -1178,6 +1192,7 @@ function createServer(opts) {
|
|
|
1178
1192
|
if (!rl.ok) {
|
|
1179
1193
|
const retryAfterSec = rl.retryAfterMs !== void 0 ? Math.ceil(rl.retryAfterMs / 1e3) : void 0;
|
|
1180
1194
|
if (retryAfterSec !== void 0) c.header("Retry-After", String(retryAfterSec));
|
|
1195
|
+
emitAudit({ type: "ratelimit.exceeded", at: Date.now(), principalId: rlKey, route: c.req.path });
|
|
1181
1196
|
return c.json({ error: "rate_limited", retryAfterMs: rl.retryAfterMs }, 429);
|
|
1182
1197
|
}
|
|
1183
1198
|
return null;
|
|
@@ -1187,7 +1202,7 @@ function createServer(opts) {
|
|
|
1187
1202
|
bodyLimit({ maxSize: BODY_LIMIT }),
|
|
1188
1203
|
async (c) => {
|
|
1189
1204
|
const principal = await runAuth(auth, c.req.raw);
|
|
1190
|
-
if (principal === null) return c
|
|
1205
|
+
if (principal === null) return unauthorized(c);
|
|
1191
1206
|
const agentId = c.req.param("agentId");
|
|
1192
1207
|
const rlErr = await checkPostAuthRateLimit(c, principal, agentId);
|
|
1193
1208
|
if (rlErr) return rlErr;
|
|
@@ -1240,7 +1255,7 @@ function createServer(opts) {
|
|
|
1240
1255
|
bodyLimit({ maxSize: BODY_LIMIT }),
|
|
1241
1256
|
async (c) => {
|
|
1242
1257
|
const principal = await runAuth(auth, c.req.raw);
|
|
1243
|
-
if (principal === null) return c
|
|
1258
|
+
if (principal === null) return unauthorized(c);
|
|
1244
1259
|
const agentId = c.req.param("agentId");
|
|
1245
1260
|
const rlErr = await checkPostAuthRateLimit(c, principal, agentId);
|
|
1246
1261
|
if (rlErr) return rlErr;
|
|
@@ -1289,7 +1304,7 @@ function createServer(opts) {
|
|
|
1289
1304
|
bodyLimit({ maxSize: BODY_LIMIT }),
|
|
1290
1305
|
async (c) => {
|
|
1291
1306
|
const principal = await runAuth(auth, c.req.raw);
|
|
1292
|
-
if (principal === null) return c
|
|
1307
|
+
if (principal === null) return unauthorized(c);
|
|
1293
1308
|
const agentId = c.req.param("agentId");
|
|
1294
1309
|
const rlErr = await checkPostAuthRateLimit(c, principal, agentId);
|
|
1295
1310
|
if (rlErr) return rlErr;
|
|
@@ -1338,6 +1353,7 @@ function createServer(opts) {
|
|
|
1338
1353
|
asyncQuotaKey = getQuotaKey(principal, agentId);
|
|
1339
1354
|
const qc = await quota.check(asyncQuotaKey);
|
|
1340
1355
|
if (!qc.ok) {
|
|
1356
|
+
emitAudit({ type: "quota.exceeded", at: Date.now(), scopeKey: asyncQuotaKey, ...qc.reason !== void 0 ? { reason: qc.reason } : {} });
|
|
1341
1357
|
return c.json({ error: "quota_exceeded", reason: qc.reason, usage: qc.usage }, 402);
|
|
1342
1358
|
}
|
|
1343
1359
|
asyncQuotaReservation = qc.reservation;
|
|
@@ -1429,7 +1445,7 @@ function createServer(opts) {
|
|
|
1429
1445
|
r.get("/v1/agents/:agentId/runs/:runId/status", async (c) => {
|
|
1430
1446
|
const principal = await runAuth(auth, c.req.raw);
|
|
1431
1447
|
if (principal === null) {
|
|
1432
|
-
return c
|
|
1448
|
+
return unauthorized(c);
|
|
1433
1449
|
}
|
|
1434
1450
|
const agentId = c.req.param("agentId");
|
|
1435
1451
|
const agent = resolve(agentId);
|
|
@@ -1460,7 +1476,7 @@ function createServer(opts) {
|
|
|
1460
1476
|
r.get("/v1/agents/:agentId/sessions/:sessionId/events", async (c) => {
|
|
1461
1477
|
const principal = await runAuth(auth, c.req.raw);
|
|
1462
1478
|
if (principal === null) {
|
|
1463
|
-
return c
|
|
1479
|
+
return unauthorized(c);
|
|
1464
1480
|
}
|
|
1465
1481
|
const agentId = c.req.param("agentId");
|
|
1466
1482
|
const agent = resolve(agentId);
|
|
@@ -1489,7 +1505,7 @@ function createServer(opts) {
|
|
|
1489
1505
|
r.get("/v1/workflows", async (c) => {
|
|
1490
1506
|
const principal = await runAuth(auth, c.req.raw);
|
|
1491
1507
|
if (principal === null) {
|
|
1492
|
-
return c
|
|
1508
|
+
return unauthorized(c);
|
|
1493
1509
|
}
|
|
1494
1510
|
const summaries = workflowRuns.list().filter((rec) => checkWorkflowOwnership(rec, principal)).map((rec) => ({
|
|
1495
1511
|
id: rec.id,
|
|
@@ -1504,7 +1520,7 @@ function createServer(opts) {
|
|
|
1504
1520
|
r.get("/v1/workflows/:id", async (c) => {
|
|
1505
1521
|
const principal = await runAuth(auth, c.req.raw);
|
|
1506
1522
|
if (principal === null) {
|
|
1507
|
-
return c
|
|
1523
|
+
return unauthorized(c);
|
|
1508
1524
|
}
|
|
1509
1525
|
const id = c.req.param("id");
|
|
1510
1526
|
const rec = workflowRuns.get(id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eidentic/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"ai": "^6.0.0",
|
|
32
32
|
"cron-parser": "^5.5.0",
|
|
33
33
|
"hono": "^4.12.0",
|
|
34
|
-
"@eidentic/
|
|
35
|
-
"@eidentic/
|
|
36
|
-
"@eidentic/
|
|
34
|
+
"@eidentic/workflow": "0.1.2",
|
|
35
|
+
"@eidentic/core": "0.2.0",
|
|
36
|
+
"@eidentic/types": "0.2.0"
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
39
|
"@hono/node-server": "^2.0.0"
|