@sema-agent/server 7.79.3 → 7.80.1
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/boot/stage-07-capability-layer.js +2 -1
- package/dist/capabilities/skills.d.ts +10 -0
- package/dist/capabilities/skills.js +3 -1
- package/dist/http/admission.js +3 -1
- package/dist/http/dispatch.js +28 -8
- package/dist/http/resume-legs.js +4 -3
- package/dist/http/route-ctx.d.ts +101 -4
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/route-table.d.ts +71 -43
- package/dist/http/route-table.js +111 -187
- package/dist/http/routes/a2a-serve.d.ts +18 -3
- package/dist/http/routes/a2a-serve.js +25 -23
- package/dist/http/routes/admin-config-refresh.d.ts +9 -2
- package/dist/http/routes/admin-config-refresh.js +32 -31
- package/dist/http/routes/admin-drain.d.ts +9 -2
- package/dist/http/routes/admin-drain.js +32 -31
- package/dist/http/routes/admin-memory-optout.d.ts +28 -2
- package/dist/http/routes/admin-memory-optout.js +27 -40
- package/dist/http/routes/adoption.d.ts +15 -2
- package/dist/http/routes/adoption.js +52 -52
- package/dist/http/routes/agents-roster.d.ts +9 -2
- package/dist/http/routes/agents-roster.js +82 -80
- package/dist/http/routes/approvals-assistant.d.ts +58 -5
- package/dist/http/routes/approvals-assistant.js +38 -39
- package/dist/http/routes/attachments.d.ts +22 -2
- package/dist/http/routes/attachments.js +81 -72
- package/dist/http/routes/capabilities.d.ts +21 -2
- package/dist/http/routes/capabilities.js +174 -170
- package/dist/http/routes/config-catalog.d.ts +9 -2
- package/dist/http/routes/config-catalog.js +28 -31
- package/dist/http/routes/devices.d.ts +24 -2
- package/dist/http/routes/devices.js +19 -21
- package/dist/http/routes/diagnostics.d.ts +9 -2
- package/dist/http/routes/diagnostics.js +41 -43
- package/dist/http/routes/fleet.d.ts +11 -2
- package/dist/http/routes/fleet.js +23 -22
- package/dist/http/routes/images.d.ts +63 -9
- package/dist/http/routes/images.js +53 -46
- package/dist/http/routes/leader.d.ts +20 -2
- package/dist/http/routes/leader.js +43 -40
- package/dist/http/routes/memory-bundle.d.ts +15 -2
- package/dist/http/routes/memory-bundle.js +38 -38
- package/dist/http/routes/memory-compliance.d.ts +16 -4
- package/dist/http/routes/memory-compliance.js +44 -45
- package/dist/http/routes/memory-consolidation.d.ts +14 -2
- package/dist/http/routes/memory-consolidation.js +66 -66
- package/dist/http/routes/memory-origin.d.ts +23 -4
- package/dist/http/routes/memory-origin.js +65 -65
- package/dist/http/routes/memory-policy.d.ts +21 -2
- package/dist/http/routes/memory-policy.js +119 -115
- package/dist/http/routes/notify-wake.d.ts +21 -11
- package/dist/http/routes/notify-wake.js +118 -116
- package/dist/http/routes/observability.d.ts +16 -2
- package/dist/http/routes/observability.js +54 -51
- package/dist/http/routes/retention-ops.d.ts +19 -2
- package/dist/http/routes/retention-ops.js +20 -22
- package/dist/http/routes/rules.d.ts +36 -2
- package/dist/http/routes/rules.js +22 -24
- package/dist/http/routes/run-memory-capture.d.ts +10 -3
- package/dist/http/routes/run-memory-capture.js +84 -84
- package/dist/http/routes/runs.d.ts +102 -22
- package/dist/http/routes/runs.js +79 -96
- package/dist/http/routes/session-sync.d.ts +41 -2
- package/dist/http/routes/session-sync.js +26 -24
- package/dist/http/routes/sessions-list.d.ts +16 -2
- package/dist/http/routes/sessions-list.js +78 -75
- package/dist/http/routes/sessions.d.ts +77 -2
- package/dist/http/routes/sessions.js +50 -47
- package/dist/http/routes/shared-memory.d.ts +19 -2
- package/dist/http/routes/shared-memory.js +65 -63
- package/dist/http/routes/side-query.d.ts +11 -2
- package/dist/http/routes/side-query.js +81 -79
- package/dist/http/routes/tasks.d.ts +17 -2
- package/dist/http/routes/tasks.js +18 -11
- package/dist/http/routes/trace-usage.d.ts +40 -2
- package/dist/http/routes/trace-usage.js +46 -35
- package/dist/http/routes/workflows.d.ts +35 -3
- package/dist/http/routes/workflows.js +29 -29
- package/dist/http/wire-types.d.ts +15 -9
- package/dist/parked-decide.d.ts +9 -0
- package/dist/parked-decide.js +3 -3
- package/dist/run-local.js +2 -1
- package/dist/spec-fields.js +33 -30
- package/dist/task-cwd.d.ts +21 -0
- package/dist/task-cwd.js +3 -0
- package/dist/trace/core-keyset-guard.d.ts +6 -1
- package/dist/trace/project.d.ts +20 -0
- package/dist/trace/project.js +15 -1
- package/package.json +2 -2
|
@@ -11,6 +11,7 @@ const SYNC_BLOB_MAX_BYTES = 64 * 1024 * 1024;
|
|
|
11
11
|
const SYNC_NDJSON_LINE_MAX_BYTES = 8 * 1024 * 1024;
|
|
12
12
|
const SYNC_IMPORT_BATCH = 500;
|
|
13
13
|
import { SHA256_HEX_RE } from "../../digest-form.js";
|
|
14
|
+
import { assertNever } from "../route-ctx.js";
|
|
14
15
|
export function createSessionSyncLocal() {
|
|
15
16
|
const pendingStagings = new Map();
|
|
16
17
|
const importLeases = new Map();
|
|
@@ -27,18 +28,12 @@ export function createSessionSyncLocal() {
|
|
|
27
28
|
};
|
|
28
29
|
return { pendingStagings, importLeases, touchImportLease, cleanupStaging };
|
|
29
30
|
}
|
|
30
|
-
export async function handleSessionSync(req, res,
|
|
31
|
-
const miss = { fell: false };
|
|
32
|
-
await handleSessionSyncBody(req, res, url, ctx, miss);
|
|
33
|
-
return !miss.fell;
|
|
34
|
-
}
|
|
35
|
-
async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
31
|
+
export async function handleSessionSync(req, res, match, ctx) {
|
|
36
32
|
const { deps } = ctx;
|
|
37
33
|
const { readJson, readRawBody, safeDecode, sessionOwnerScopeForWrite, rateLimited } = ctx.helpers;
|
|
38
34
|
const { pendingStagings, importLeases, touchImportLease, cleanupStaging } = ctx.local.sessionSync;
|
|
39
35
|
{
|
|
40
|
-
|
|
41
|
-
if (sm) {
|
|
36
|
+
{
|
|
42
37
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
43
38
|
if (!deps.backend || !ownerOf) {
|
|
44
39
|
sendError(res, 501, "capability.session_store_required", "session sync requires a durable session store");
|
|
@@ -49,7 +44,7 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
49
44
|
sendError(res, 401, "auth.unauthorized", "unauthorized");
|
|
50
45
|
return;
|
|
51
46
|
}
|
|
52
|
-
const sessionId = safeDecode(
|
|
47
|
+
const sessionId = safeDecode(match.params.sessionId);
|
|
53
48
|
if (sessionId === null || !isUuidShape(sessionId)) {
|
|
54
49
|
sendError(res, 400, "request.id_invalid", "invalid session id (must be a canonical uuid)");
|
|
55
50
|
return;
|
|
@@ -74,7 +69,7 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
74
69
|
}
|
|
75
70
|
return true;
|
|
76
71
|
};
|
|
77
|
-
if (
|
|
72
|
+
if (match.decl.id === "session-sync-manifest") {
|
|
78
73
|
if (!pullGateOk())
|
|
79
74
|
return;
|
|
80
75
|
const out = await exportSessionManifest(sessionId, deps.backend);
|
|
@@ -85,10 +80,10 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
85
80
|
sendJson(res, 200, { manifest: out.manifest });
|
|
86
81
|
return;
|
|
87
82
|
}
|
|
88
|
-
if (
|
|
83
|
+
if (match.decl.id === "session-sync-entries") {
|
|
89
84
|
if (!pullGateOk())
|
|
90
85
|
return;
|
|
91
|
-
const afterSeqRaw = new
|
|
86
|
+
const afterSeqRaw = new URLSearchParams(match.search).get("afterSeq");
|
|
92
87
|
let afterSeq;
|
|
93
88
|
if (afterSeqRaw !== null) {
|
|
94
89
|
const n = Number(afterSeqRaw);
|
|
@@ -127,10 +122,10 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
127
122
|
}
|
|
128
123
|
return;
|
|
129
124
|
}
|
|
130
|
-
if (
|
|
125
|
+
if (match.decl.id === "session-sync-history-blob") {
|
|
131
126
|
if (!pullGateOk())
|
|
132
127
|
return;
|
|
133
|
-
const hash = safeDecode(
|
|
128
|
+
const hash = safeDecode(match.params.hash);
|
|
134
129
|
if (hash === null) {
|
|
135
130
|
sendError(res, 400, "request.path_malformed", "invalid blob hash");
|
|
136
131
|
return;
|
|
@@ -159,12 +154,12 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
159
154
|
res.end(Buffer.from(bytes));
|
|
160
155
|
return;
|
|
161
156
|
}
|
|
162
|
-
if (
|
|
157
|
+
if (match.decl.id === "session-sync-blob-put") {
|
|
163
158
|
if (rateLimited(req, res))
|
|
164
159
|
return;
|
|
165
160
|
if (!pushGateOk())
|
|
166
161
|
return;
|
|
167
|
-
const hash = safeDecode(
|
|
162
|
+
const hash = safeDecode(match.params.hash);
|
|
168
163
|
if (hash === null || !SHA256_HEX_RE.test(hash)) {
|
|
169
164
|
sendError(res, 400, "request.id_invalid", "blob hash must be 64 lowercase-hex chars");
|
|
170
165
|
return;
|
|
@@ -191,7 +186,7 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
191
186
|
res.writeHead(204).end();
|
|
192
187
|
return;
|
|
193
188
|
}
|
|
194
|
-
if (
|
|
189
|
+
if (match.decl.id === "session-sync-import") {
|
|
195
190
|
if (!pushGateOk())
|
|
196
191
|
return;
|
|
197
192
|
if (deps.sessionMirrorRuling && !scope.fleetWide && scope.gateOwner !== null) {
|
|
@@ -353,8 +348,8 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
353
348
|
sendJson(res, 200, { stagingId, relation: relA.relation });
|
|
354
349
|
return;
|
|
355
350
|
}
|
|
356
|
-
if (
|
|
357
|
-
const stagingId = safeDecode(
|
|
351
|
+
if (match.decl.id === "session-sync-import-entries") {
|
|
352
|
+
const stagingId = safeDecode(match.params.stagingId);
|
|
358
353
|
if (stagingId === null) {
|
|
359
354
|
sendError(res, 400, "request.id_invalid", "invalid staging id");
|
|
360
355
|
return;
|
|
@@ -462,7 +457,7 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
462
457
|
if (pending.resolution !== undefined && overwriteWipeIncapable(deps.sessionPolicyStore)) {
|
|
463
458
|
await pending.handle.abort().catch(() => undefined);
|
|
464
459
|
cleanupStaging(stagingId);
|
|
465
|
-
sendError(res, 501, "capability.policy_purge_required", "overwrite-dst cannot proceed: this deployment's session-policy store has no deleteBySession purge
|
|
460
|
+
sendError(res, 501, "capability.policy_purge_required", "overwrite-dst cannot proceed: this deployment's session-policy store has no deleteBySession purge verb, so the destination's stale policy rows cannot be wiped and the import would land a MIXED state (the source's conversation plus the destination's leftover policy rows, which keep gating). Upgrade the policy store (every in-tree store carries deleteBySession since core 1.423), or re-run without resolution overwrite-dst");
|
|
466
461
|
return;
|
|
467
462
|
}
|
|
468
463
|
try {
|
|
@@ -507,7 +502,7 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
507
502
|
}
|
|
508
503
|
return;
|
|
509
504
|
}
|
|
510
|
-
if (
|
|
505
|
+
if (match.decl.id === "session-sync-plan") {
|
|
511
506
|
if (!pushGateOk())
|
|
512
507
|
return;
|
|
513
508
|
let body;
|
|
@@ -527,10 +522,17 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
|
|
|
527
522
|
sendJson(res, 200, relation);
|
|
528
523
|
return;
|
|
529
524
|
}
|
|
530
|
-
|
|
531
|
-
return;
|
|
525
|
+
return assertNever(match.decl.id);
|
|
532
526
|
}
|
|
533
527
|
}
|
|
534
|
-
miss.fell = true;
|
|
535
528
|
}
|
|
529
|
+
export const SESSION_SYNC_ROUTES = [
|
|
530
|
+
{ id: "session-sync-manifest", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/sync\/manifest$/, label: "/v1/sessions/:id/sync/$verb", methods: ["GET"] },
|
|
531
|
+
{ id: "session-sync-entries", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/sync\/entries$/, label: "/v1/sessions/:id/sync/$verb", methods: ["GET"] },
|
|
532
|
+
{ id: "session-sync-plan", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/sync\/plan$/, label: "/v1/sessions/:id/sync/$verb", methods: ["POST"] },
|
|
533
|
+
{ id: "session-sync-import", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/sync\/import$/, label: "/v1/sessions/:id/sync/import", methods: ["POST"] },
|
|
534
|
+
{ id: "session-sync-import-entries", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/sync\/import\/(?<stagingId>[^/]+)\/entries$/, label: "/v1/sessions/:id/sync/import", methods: ["POST"] },
|
|
535
|
+
{ id: "session-sync-blob-put", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/sync\/blobs\/(?<hash>[^/]+)$/, label: "/v1/sessions/:id/sync/blobs", methods: ["PUT"] },
|
|
536
|
+
{ id: "session-sync-history-blob", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/sync\/history\/blobs\/(?<hash>[^/]+)$/, label: "/v1/sessions/:id/sync/history/blobs/:hash", methods: ["GET"] },
|
|
537
|
+
];
|
|
536
538
|
//# sourceMappingURL=session-sync.js.map
|
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
* owner-scope 与 trace 列表同一权威模型(system/traceToken=fleet-wide,principal=钉住本人)。本域零可变状态。
|
|
6
6
|
*/
|
|
7
7
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
8
|
-
import type { RouteCtx } from "../route-ctx.js";
|
|
9
|
-
|
|
8
|
+
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
9
|
+
/** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。裸 `return;`
|
|
10
|
+
* 的语义原样 =「本域已应答」;走到函数尾才是「没匹配上」,置 `miss.fell` 交回装配器继续下一个域。
|
|
11
|
+
* 跨域的东西(deps / 助手 / 单域状态)一律在首行解构出来,于是下面每一行都逐字不变。 */
|
|
12
|
+
export declare function handleSessionsList(req: IncomingMessage, res: ServerResponse, match: RouteMatch<SessionsListRouteId>, ctx: RouteCtx): Promise<void>;
|
|
13
|
+
export declare const SESSIONS_LIST_ROUTES: readonly [{
|
|
14
|
+
readonly id: "sessions-list";
|
|
15
|
+
readonly path: "/v1/sessions";
|
|
16
|
+
readonly methods: readonly ["GET"];
|
|
17
|
+
}, {
|
|
18
|
+
readonly id: "sendfile-links";
|
|
19
|
+
readonly path: "/v1/sendfile-links";
|
|
20
|
+
readonly methods: readonly ["GET"];
|
|
21
|
+
}];
|
|
22
|
+
/** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
|
|
23
|
+
export type SessionsListRouteId = RouteIdsOf<typeof SESSIONS_LIST_ROUTES>;
|
|
10
24
|
//# sourceMappingURL=sessions-list.d.ts.map
|
|
@@ -1,91 +1,90 @@
|
|
|
1
1
|
import { isUuidV7, legacyUnownedAmnestyOpen } from "../../security.js";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { filterFingerprint } from "../cursor-fingerprint.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
await handleSessionsListBody(req, res, url, ctx, miss);
|
|
7
|
-
return !miss.fell;
|
|
8
|
-
}
|
|
9
|
-
async function handleSessionsListBody(req, res, url, ctx, miss) {
|
|
4
|
+
import { assertNever } from "../route-ctx.js";
|
|
5
|
+
export async function handleSessionsList(req, res, match, ctx) {
|
|
10
6
|
const { deps } = ctx;
|
|
11
7
|
const { isFleetWide, sessionOwnerScope } = ctx.helpers;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
if (req.method === "GET" && url === "/v1/sendfile-links") {
|
|
28
|
-
const { fleetWide, gateOwner } = sessionOwnerScope(req);
|
|
29
|
-
if (!fleetWide && gateOwner === null) {
|
|
30
|
-
sendError(res, 401, "auth.unauthorized", "unauthorized");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (!deps.sendFileLedger) {
|
|
34
|
-
sendError(res, 501, "capability.store_required", "sendfile ledger requires a store backend (DB_BACKEND=mysql|pg|local) and the SendUserFile issuer configured");
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
const query = new URL(req.url ?? "", "http://x").searchParams;
|
|
38
|
-
const limitRaw = query.get("limit");
|
|
39
|
-
if (limitRaw !== null && !(/^[0-9]+$/.test(limitRaw) && Number(limitRaw) >= 1 && Number(limitRaw) <= 100)) {
|
|
40
|
-
sendError(res, 400, "request.query_invalid", "limit must be a decimal integer in 1..100");
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const limit = limitRaw === null ? 50 : Number(limitRaw);
|
|
44
|
-
const beforeRaw = query.get("before");
|
|
45
|
-
if (beforeRaw !== null && !isUuidV7(beforeRaw)) {
|
|
46
|
-
sendError(res, 400, "request.id_invalid", "before must be a uuidv7 link id (the `id`/`nextBefore` of a previous page)");
|
|
8
|
+
switch (match.decl.id) {
|
|
9
|
+
case "sessions-list": {
|
|
10
|
+
const { fleetWide, gateOwner } = sessionOwnerScope(req);
|
|
11
|
+
if (!fleetWide && gateOwner === null) {
|
|
12
|
+
sendError(res, 401, "auth.unauthorized", "unauthorized");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const lister = deps.sessionStorage?.listSessions?.bind(deps.sessionStorage) ?? deps.runStore?.listSessions?.bind(deps.runStore);
|
|
16
|
+
if (!lister) {
|
|
17
|
+
sendError(res, 501, "capability.session_store_required", "session list requires a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const includeUnowned = !fleetWide && legacyUnownedAmnestyOpen(deps.config.requirePrincipal, gateOwner);
|
|
21
|
+
await handleSessionList(res, lister, new URLSearchParams(match.search), gateOwner, includeUnowned);
|
|
47
22
|
return;
|
|
48
23
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
24
|
+
case "sendfile-links": {
|
|
25
|
+
const { fleetWide, gateOwner } = sessionOwnerScope(req);
|
|
26
|
+
if (!fleetWide && gateOwner === null) {
|
|
27
|
+
sendError(res, 401, "auth.unauthorized", "unauthorized");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!deps.sendFileLedger) {
|
|
31
|
+
sendError(res, 501, "capability.store_required", "sendfile ledger requires a store backend (DB_BACKEND=mysql|pg|local) and the SendUserFile issuer configured");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const query = new URLSearchParams(match.search);
|
|
35
|
+
const limitRaw = query.get("limit");
|
|
36
|
+
if (limitRaw !== null && !(/^[0-9]+$/.test(limitRaw) && Number(limitRaw) >= 1 && Number(limitRaw) <= 100)) {
|
|
37
|
+
sendError(res, 400, "request.query_invalid", "limit must be a decimal integer in 1..100");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const limit = limitRaw === null ? 50 : Number(limitRaw);
|
|
41
|
+
const beforeRaw = query.get("before");
|
|
42
|
+
if (beforeRaw !== null && !isUuidV7(beforeRaw)) {
|
|
43
|
+
sendError(res, 400, "request.id_invalid", "before must be a uuidv7 link id (the `id`/`nextBefore` of a previous page)");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const before = beforeRaw ?? undefined;
|
|
47
|
+
let scope;
|
|
48
|
+
if (gateOwner !== null) {
|
|
49
|
+
scope = gateOwner;
|
|
58
50
|
}
|
|
59
51
|
else {
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
const scopeRaw = query.get("scope");
|
|
53
|
+
if (scopeRaw === null) {
|
|
54
|
+
scope = "_";
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const s = scopeRaw.trim();
|
|
58
|
+
if (s === "" || s.length > 190) {
|
|
59
|
+
sendError(res, 400, "request.query_invalid", "scope must be a non-blank tenant principal of at most 190 characters (the sendfile_link.scope column width)");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
scope = s;
|
|
64
63
|
}
|
|
65
|
-
scope = s;
|
|
66
64
|
}
|
|
65
|
+
const rows = await deps.sendFileLedger.listByScope(scope, limit + 1, before);
|
|
66
|
+
const hasMore = rows.length > limit;
|
|
67
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
68
|
+
sendJson(res, 200, {
|
|
69
|
+
links: page.map((r) => ({
|
|
70
|
+
id: r.id,
|
|
71
|
+
filename: r.filename,
|
|
72
|
+
size: r.size,
|
|
73
|
+
ttlSec: r.ttlSec,
|
|
74
|
+
track: r.track,
|
|
75
|
+
bucket: r.bucket,
|
|
76
|
+
key: r.objectKey,
|
|
77
|
+
createdAt: new Date(r.createdAt).toISOString(),
|
|
78
|
+
...(r.taskId !== undefined ? { taskId: r.taskId } : {}),
|
|
79
|
+
...(r.sessionId !== undefined ? { sessionId: r.sessionId } : {}),
|
|
80
|
+
})),
|
|
81
|
+
...(hasMore ? { nextBefore: page[page.length - 1].id } : {}),
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
67
84
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
71
|
-
sendJson(res, 200, {
|
|
72
|
-
links: page.map((r) => ({
|
|
73
|
-
id: r.id,
|
|
74
|
-
filename: r.filename,
|
|
75
|
-
size: r.size,
|
|
76
|
-
ttlSec: r.ttlSec,
|
|
77
|
-
track: r.track,
|
|
78
|
-
bucket: r.bucket,
|
|
79
|
-
key: r.objectKey,
|
|
80
|
-
createdAt: new Date(r.createdAt).toISOString(),
|
|
81
|
-
...(r.taskId !== undefined ? { taskId: r.taskId } : {}),
|
|
82
|
-
...(r.sessionId !== undefined ? { sessionId: r.sessionId } : {}),
|
|
83
|
-
})),
|
|
84
|
-
...(hasMore ? { nextBefore: page[page.length - 1].id } : {}),
|
|
85
|
-
});
|
|
86
|
-
return;
|
|
85
|
+
default:
|
|
86
|
+
return assertNever(match.decl.id);
|
|
87
87
|
}
|
|
88
|
-
miss.fell = true;
|
|
89
88
|
}
|
|
90
89
|
function encodeSessionCursor(c, f) {
|
|
91
90
|
return Buffer.from(JSON.stringify({ ...c, f })).toString("base64url");
|
|
@@ -123,4 +122,8 @@ async function handleSessionList(res, lister, query, forceOwner, includeUnowned
|
|
|
123
122
|
const nextCursor = hasMore && last ? encodeSessionCursor({ lastActivityAt: last.lastActivityAt, sessionId: last.sessionId }, f) : undefined;
|
|
124
123
|
sendJson(res, 200, { sessions: page, ...(nextCursor ? { nextCursor } : {}) });
|
|
125
124
|
}
|
|
125
|
+
export const SESSIONS_LIST_ROUTES = [
|
|
126
|
+
{ id: "sessions-list", path: "/v1/sessions", methods: ["GET"] },
|
|
127
|
+
{ id: "sendfile-links", path: "/v1/sendfile-links", methods: ["GET"] },
|
|
128
|
+
];
|
|
126
129
|
//# sourceMappingURL=sessions-list.js.map
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
15
15
|
import { type SessionMemoryStatus } from "@sema-agent/core";
|
|
16
|
-
import type { RouteCtx } from "../route-ctx.js";
|
|
16
|
+
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
17
17
|
/** 单域独占状态。每个 server 实例一份——模块级会把连接坑/缓存跨实例串味。 */
|
|
18
18
|
export interface SessionsLocal {
|
|
19
19
|
sseConnections: number;
|
|
@@ -46,5 +46,80 @@ export interface SessionsLocal {
|
|
|
46
46
|
}>;
|
|
47
47
|
}
|
|
48
48
|
export declare function createSessionsLocal(): SessionsLocal;
|
|
49
|
-
|
|
49
|
+
/** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(去缩进两格;sessions 另有一处见文件头说明)。
|
|
50
|
+
* 裸 `return;` 的语义原样 =「本域已应答」;走到函数尾才是「没匹配上」,置 `miss.fell` 交回装配器继续下一个域。 */
|
|
51
|
+
export declare function handleSessions(req: IncomingMessage, res: ServerResponse, match: RouteMatch<SessionsRouteId>, ctx: RouteCtx): Promise<void>;
|
|
52
|
+
export declare const SESSIONS_ROUTES: readonly [{
|
|
53
|
+
readonly id: "session-init";
|
|
54
|
+
readonly pattern: RegExp;
|
|
55
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
56
|
+
readonly methods: readonly ["GET"];
|
|
57
|
+
}, {
|
|
58
|
+
readonly id: "session-settings";
|
|
59
|
+
readonly pattern: RegExp;
|
|
60
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
61
|
+
readonly methods: readonly ["GET"];
|
|
62
|
+
}, {
|
|
63
|
+
readonly id: "session-mcp";
|
|
64
|
+
readonly pattern: RegExp;
|
|
65
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
66
|
+
readonly methods: readonly ["GET"];
|
|
67
|
+
}, {
|
|
68
|
+
readonly id: "session-a2a";
|
|
69
|
+
readonly pattern: RegExp;
|
|
70
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
71
|
+
readonly methods: readonly ["GET"];
|
|
72
|
+
}, {
|
|
73
|
+
readonly id: "session-head";
|
|
74
|
+
readonly pattern: RegExp;
|
|
75
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
76
|
+
readonly methods: readonly ["GET"];
|
|
77
|
+
}, {
|
|
78
|
+
readonly id: "session-events";
|
|
79
|
+
readonly pattern: RegExp;
|
|
80
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
81
|
+
readonly methods: readonly ["GET"];
|
|
82
|
+
}, {
|
|
83
|
+
readonly id: "session-fork";
|
|
84
|
+
readonly pattern: RegExp;
|
|
85
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
86
|
+
readonly methods: readonly ["POST"];
|
|
87
|
+
}, {
|
|
88
|
+
readonly id: "session-policy-get";
|
|
89
|
+
readonly pattern: RegExp;
|
|
90
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
91
|
+
readonly methods: readonly ["GET"];
|
|
92
|
+
}, {
|
|
93
|
+
readonly id: "session-policy-put";
|
|
94
|
+
readonly pattern: RegExp;
|
|
95
|
+
readonly label: "/v1/sessions/:id/$verb";
|
|
96
|
+
readonly methods: readonly ["PUT"];
|
|
97
|
+
}, {
|
|
98
|
+
readonly id: "session-memory-erase";
|
|
99
|
+
readonly pattern: RegExp;
|
|
100
|
+
readonly label: "/v1/sessions/:id/memory/erase";
|
|
101
|
+
readonly methods: readonly ["POST"];
|
|
102
|
+
}, {
|
|
103
|
+
readonly id: "session-memory-status";
|
|
104
|
+
readonly pattern: RegExp;
|
|
105
|
+
readonly label: "/v1/sessions/:id/memory-status";
|
|
106
|
+
readonly methods: readonly ["GET"];
|
|
107
|
+
}, {
|
|
108
|
+
readonly id: "session-workspace";
|
|
109
|
+
readonly pattern: RegExp;
|
|
110
|
+
readonly label: "/v1/sessions/:id/workspace";
|
|
111
|
+
readonly methods: readonly ["GET"];
|
|
112
|
+
}, {
|
|
113
|
+
readonly id: "session-detail";
|
|
114
|
+
readonly pattern: RegExp;
|
|
115
|
+
readonly label: "/v1/sessions/:id";
|
|
116
|
+
readonly methods: readonly ["GET"];
|
|
117
|
+
}, {
|
|
118
|
+
readonly id: "session-delete";
|
|
119
|
+
readonly pattern: RegExp;
|
|
120
|
+
readonly label: "/v1/sessions/:id";
|
|
121
|
+
readonly methods: readonly ["DELETE"];
|
|
122
|
+
}];
|
|
123
|
+
/** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
|
|
124
|
+
export type SessionsRouteId = RouteIdsOf<typeof SESSIONS_ROUTES>;
|
|
50
125
|
//# sourceMappingURL=sessions.d.ts.map
|