borgmcp-server 0.1.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.
Files changed (71) hide show
  1. package/LICENSE +105 -0
  2. package/NOTICE +8 -0
  3. package/README.md +119 -0
  4. package/SECURITY.md +38 -0
  5. package/THIRD_PARTY_NOTICES.md +35 -0
  6. package/dist/bootstrap.d.ts +18 -0
  7. package/dist/bootstrap.js +104 -0
  8. package/dist/bootstrap.js.map +1 -0
  9. package/dist/cli.d.ts +7 -0
  10. package/dist/cli.js +96 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/coordination-api.d.ts +25 -0
  13. package/dist/coordination-api.js +457 -0
  14. package/dist/coordination-api.js.map +1 -0
  15. package/dist/credentials.d.ts +58 -0
  16. package/dist/credentials.js +244 -0
  17. package/dist/credentials.js.map +1 -0
  18. package/dist/enrollment.d.ts +17 -0
  19. package/dist/enrollment.js +122 -0
  20. package/dist/enrollment.js.map +1 -0
  21. package/dist/https-server.d.ts +94 -0
  22. package/dist/https-server.js +814 -0
  23. package/dist/https-server.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +2 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/main.d.ts +3 -0
  28. package/dist/main.js +67 -0
  29. package/dist/main.js.map +1 -0
  30. package/dist/migrations.d.ts +8 -0
  31. package/dist/migrations.js +366 -0
  32. package/dist/migrations.js.map +1 -0
  33. package/dist/network-policy.d.ts +13 -0
  34. package/dist/network-policy.js +49 -0
  35. package/dist/network-policy.js.map +1 -0
  36. package/dist/operator-error.d.ts +3 -0
  37. package/dist/operator-error.js +63 -0
  38. package/dist/operator-error.js.map +1 -0
  39. package/dist/principal.d.ts +32 -0
  40. package/dist/principal.js +64 -0
  41. package/dist/principal.js.map +1 -0
  42. package/dist/protocol-draft.d.ts +2 -0
  43. package/dist/protocol-draft.js +31 -0
  44. package/dist/protocol-draft.js.map +1 -0
  45. package/dist/service.d.ts +61 -0
  46. package/dist/service.js +455 -0
  47. package/dist/service.js.map +1 -0
  48. package/dist/start-options.d.ts +2 -0
  49. package/dist/start-options.js +46 -0
  50. package/dist/start-options.js.map +1 -0
  51. package/dist/store.d.ts +327 -0
  52. package/dist/store.js +1729 -0
  53. package/dist/store.js.map +1 -0
  54. package/npm-shrinkwrap.json +1942 -0
  55. package/package.json +60 -0
  56. package/src/bootstrap.ts +127 -0
  57. package/src/cli.ts +102 -0
  58. package/src/coordination-api.ts +508 -0
  59. package/src/credentials.ts +319 -0
  60. package/src/enrollment.ts +156 -0
  61. package/src/https-server.ts +962 -0
  62. package/src/index.ts +3 -0
  63. package/src/main.ts +73 -0
  64. package/src/migrations.ts +394 -0
  65. package/src/network-policy.ts +65 -0
  66. package/src/operator-error.ts +97 -0
  67. package/src/principal.ts +106 -0
  68. package/src/protocol-draft.ts +32 -0
  69. package/src/service.ts +525 -0
  70. package/src/start-options.ts +46 -0
  71. package/src/store.ts +2316 -0
@@ -0,0 +1,508 @@
1
+ import type { Principal } from "./principal.js";
2
+ import { assertServerDerivedPrincipal } from "./principal.js";
3
+ import type { CredentialAuthority } from "./credentials.js";
4
+ import {
5
+ CursorExpiredError,
6
+ AttachConflictError,
7
+ AccessDeniedError,
8
+ CreateCubeConflictError,
9
+ ScopedStoreError,
10
+ StorageCapacityError,
11
+ type EnrichedActivityRecord,
12
+ type LogCursor,
13
+ type StoreRuntime,
14
+ } from "./store.js";
15
+
16
+ export interface CoordinationRequest {
17
+ readonly method: string;
18
+ readonly path: string;
19
+ readonly principal: Principal;
20
+ readonly body?: unknown;
21
+ readonly cursor?: string;
22
+ readonly signal: AbortSignal;
23
+ }
24
+
25
+ export interface CoordinationResponse {
26
+ readonly status: number;
27
+ readonly body?: unknown;
28
+ readonly stream?: AsyncIterable<string>;
29
+ }
30
+
31
+ interface RequestEnvelope {
32
+ readonly requestId: string;
33
+ readonly payload: Record<string, unknown>;
34
+ }
35
+
36
+ interface ReplayBarrier {
37
+ readonly reached: Promise<void>;
38
+ readonly release: Promise<void>;
39
+ readonly markReached: () => void;
40
+ }
41
+
42
+ export class CoordinationApi {
43
+ readonly #runtime: StoreRuntime;
44
+ readonly #authority: CredentialAuthority;
45
+ #replayBarrier: ReplayBarrier | undefined;
46
+
47
+ constructor(runtime: StoreRuntime, authority: CredentialAuthority) {
48
+ this.#runtime = runtime;
49
+ this.#authority = authority;
50
+ }
51
+
52
+ armReplayTransition(): { readonly reached: Promise<void>; readonly release: () => void } {
53
+ let markReached!: () => void;
54
+ let release!: () => void;
55
+ const reached = new Promise<void>((resolve) => { markReached = resolve; });
56
+ const released = new Promise<void>((resolve) => { release = resolve; });
57
+ this.#replayBarrier = { reached, release: released, markReached };
58
+ return {
59
+ reached,
60
+ release: () => {
61
+ release();
62
+ },
63
+ };
64
+ }
65
+
66
+ async handle(request: CoordinationRequest): Promise<CoordinationResponse> {
67
+ assertServerDerivedPrincipal(request.principal);
68
+ const authentication = request.principal;
69
+
70
+ if (request.path === "/api/client/attach" && request.method === "POST") {
71
+ const requestId = safeRequestId(request.body);
72
+ try {
73
+ const envelope = decodeEnvelope(request.body);
74
+ exactKeys(envelope.payload, ["cube_id", "role_id", "retry_key"], ["prior_drone_id"]);
75
+ const priorDroneId = envelope.payload["prior_drone_id"] === undefined
76
+ ? undefined
77
+ : requiredUuid(envelope.payload, "prior_drone_id");
78
+ const attachment = this.#authority.attachSeat(
79
+ this.#runtime.forPrincipal(authentication),
80
+ {
81
+ cubeId: requiredUuid(envelope.payload, "cube_id"),
82
+ roleId: requiredUuid(envelope.payload, "role_id"),
83
+ retryKey: requiredUuid(envelope.payload, "retry_key"),
84
+ ...(priorDroneId === undefined ? {} : { priorDroneId }),
85
+ },
86
+ );
87
+ return success(201, envelope.requestId, {
88
+ cube: attachment.cube,
89
+ role: attachment.role,
90
+ drone: attachment.drone,
91
+ session: {
92
+ token: attachment.credential,
93
+ expires_at: attachment.expiresAt,
94
+ generation: attachment.generation,
95
+ },
96
+ reattached: attachment.reattached,
97
+ });
98
+ } catch (error) {
99
+ if (error instanceof AttachConflictError) {
100
+ return failure(409, "INVALID_INPUT", "The attach request conflicts.", requestId);
101
+ }
102
+ if (error instanceof ScopedStoreError) {
103
+ return failure(404, "NOT_FOUND", error.message, requestId);
104
+ }
105
+ if (error instanceof StorageCapacityError) {
106
+ return failure(507, "CAPACITY_EXCEEDED", error.message, requestId);
107
+ }
108
+ if (error instanceof InputError || error instanceof TypeError || error instanceof RangeError) {
109
+ return failure(400, "INVALID_INPUT", "Invalid protocol request.", requestId);
110
+ }
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ if (request.path === "/api/cubes" && request.method === "GET") {
116
+ const cubes = this.#runtime.forPrincipal(authentication).listCubes().map((cube) => ({
117
+ id: cube.id,
118
+ owner_id: cube.ownerId,
119
+ name: cube.name,
120
+ cube_directive: cube.directive,
121
+ created_at: cube.createdAt,
122
+ updated_at: cube.updatedAt,
123
+ }));
124
+ return success(200, "cubes-read", { cubes });
125
+ }
126
+
127
+ if (request.path === "/api/cubes" && request.method === "POST") {
128
+ const requestId = safeRequestId(request.body);
129
+ try {
130
+ const envelope = decodeEnvelope(request.body);
131
+ exactKeys(envelope.payload, ["retry_key", "name", "template"]);
132
+ const template = envelope.payload["template"];
133
+ if (template !== "default") throw new InputError();
134
+ const created = this.#runtime.forPrincipal(authentication).createCube({
135
+ retryKey: requiredUuid(envelope.payload, "retry_key"),
136
+ name: requiredPresentationName(envelope.payload, "name"),
137
+ template,
138
+ });
139
+ return success(201, envelope.requestId, {
140
+ cube_id: created.cubeId,
141
+ human_seat_role_id: created.humanSeatRoleId,
142
+ default_worker_role_id: created.defaultWorkerRoleId,
143
+ access: created.access,
144
+ });
145
+ } catch (error) {
146
+ if (error instanceof AccessDeniedError) {
147
+ return failure(403, "ACCESS_DENIED", error.message, requestId);
148
+ }
149
+ if (error instanceof CreateCubeConflictError) {
150
+ return failure(409, "INVALID_INPUT", "The cube creation request conflicts.", requestId);
151
+ }
152
+ if (error instanceof StorageCapacityError) {
153
+ return failure(507, "CAPACITY_EXCEEDED", error.message, requestId);
154
+ }
155
+ if (error instanceof InputError || error instanceof TypeError || error instanceof RangeError) {
156
+ return failure(400, "INVALID_INPUT", "Invalid protocol request.", requestId);
157
+ }
158
+ throw error;
159
+ }
160
+ }
161
+
162
+ const match = /^\/api\/cubes\/([0-9a-f-]{36})(?:\/(roles|drones|logs|acks|decisions|stream))?$/u
163
+ .exec(request.path);
164
+ if (match === null) return failure(404, "NOT_FOUND", "The requested resource was not found.");
165
+ const cubeId = match[1]!;
166
+ if (!uuidPattern.test(cubeId)) {
167
+ return failure(404, "NOT_FOUND", "The requested resource was not found.");
168
+ }
169
+ const resource = match[2];
170
+ const store = this.#runtime.forPrincipal(authentication);
171
+
172
+ try {
173
+ if (resource === undefined && request.method === "GET") {
174
+ const cube = store.getCube(cubeId);
175
+ if (cube === null) throw new ScopedStoreError();
176
+ return success(200, "cube-read", {
177
+ cube: {
178
+ id: cube.id,
179
+ owner_id: cube.ownerId,
180
+ name: cube.name,
181
+ cube_directive: cube.directive,
182
+ created_at: cube.createdAt,
183
+ updated_at: cube.updatedAt,
184
+ },
185
+ });
186
+ }
187
+ if (resource === "roles" && request.method === "GET") {
188
+ return success(200, "roles-read", { roles: store.listRoles(cubeId) });
189
+ }
190
+ if (resource === "drones" && request.method === "GET") {
191
+ return success(200, "drones-read", { drones: store.listDrones(cubeId) });
192
+ }
193
+ if (resource === "logs" && request.method === "POST") {
194
+ const envelope = decodeEnvelope(request.body);
195
+ exactKeys(envelope.payload, ["message"], ["visibility", "recipientDroneIds"]);
196
+ const message = requiredString(envelope.payload, "message", 10_240);
197
+ const visibility = optionalVisibility(envelope.payload["visibility"]);
198
+ const recipientDroneIds = optionalUuidArray(envelope.payload["recipientDroneIds"]);
199
+ if (((visibility ?? "broadcast") === "broadcast" && recipientDroneIds !== undefined) ||
200
+ (visibility === "direct" && (recipientDroneIds?.length ?? 0) === 0)) {
201
+ throw new InputError();
202
+ }
203
+ const entry = store.appendLog(cubeId, {
204
+ message,
205
+ ...(visibility === undefined ? {} : { visibility }),
206
+ ...(recipientDroneIds === undefined ? {} : { recipientDroneIds }),
207
+ });
208
+ return success(201, envelope.requestId, { entry });
209
+ }
210
+ if (resource === "logs" && request.method === "PUT") {
211
+ const envelope = decodeEnvelope(request.body);
212
+ exactKeys(envelope.payload, ["cursor"], ["limit"]);
213
+ const cursor = decodeCursor(envelope.payload["cursor"]);
214
+ const limit = optionalLimit(envelope.payload["limit"]);
215
+ return success(200, envelope.requestId, store.readLog(cubeId, cursor, limit));
216
+ }
217
+ if (resource === "acks" && request.method === "POST") {
218
+ const envelope = decodeEnvelope(request.body);
219
+ const entryId = requiredUuid(envelope.payload, "entry_id");
220
+ const kind = envelope.payload["kind"];
221
+ if (kind !== "ack" && kind !== "claim") throw new InputError();
222
+ exactKeys(envelope.payload, ["entry_id", "kind"]);
223
+ store.acknowledge(cubeId, entryId, kind);
224
+ return { status: 204 };
225
+ }
226
+ if (resource === "decisions" && request.method === "POST") {
227
+ const envelope = decodeEnvelope(request.body);
228
+ exactKeys(envelope.payload, ["topic", "decision"], ["rationale"]);
229
+ const topic = requiredString(envelope.payload, "topic", 120);
230
+ const decision = requiredString(envelope.payload, "decision", 100_000);
231
+ const rationale = optionalString(envelope.payload, "rationale", 100_000);
232
+ return success(201, envelope.requestId, {
233
+ decision: store.recordDecision(cubeId, {
234
+ topic,
235
+ decision,
236
+ ...(rationale === undefined ? {} : { rationale }),
237
+ }),
238
+ });
239
+ }
240
+ if (resource === "decisions" && request.method === "PUT") {
241
+ const envelope = decodeEnvelope(request.body);
242
+ exactKeys(envelope.payload, []);
243
+ return success(200, envelope.requestId, { decisions: store.listDecisions(cubeId) });
244
+ }
245
+ if (resource === "stream" && request.method === "GET") {
246
+ return await this.#openStream(authentication, cubeId, decodeOpaqueCursor(request.cursor), request.signal);
247
+ }
248
+ return failure(405, "INVALID_INPUT", "Method not allowed.");
249
+ } catch (error) {
250
+ if (error instanceof CursorExpiredError) {
251
+ return failure(410, "CURSOR_EXPIRED", error.message);
252
+ }
253
+ if (error instanceof ScopedStoreError) {
254
+ return failure(404, "NOT_FOUND", error.message);
255
+ }
256
+ if (error instanceof StorageCapacityError) {
257
+ return failure(507, "CAPACITY_EXCEEDED", error.message, safeRequestId(request.body));
258
+ }
259
+ if (error instanceof InputError || error instanceof TypeError || error instanceof RangeError) {
260
+ return failure(400, "INVALID_INPUT", "Invalid protocol request.", safeRequestId(request.body));
261
+ }
262
+ throw error;
263
+ }
264
+ }
265
+
266
+ async #openStream(
267
+ principal: Principal,
268
+ cubeId: string,
269
+ cursor: LogCursor | null,
270
+ requestSignal: AbortSignal,
271
+ ): Promise<CoordinationResponse> {
272
+ const store = this.#runtime.forPrincipal(principal);
273
+ const session = this.#authority.registerLiveSession(principal);
274
+ const signal = AbortSignal.any([requestSignal, session.signal]);
275
+ let unsubscribe = (): void => undefined;
276
+ const queue = new AsyncStringQueue(() => {
277
+ unsubscribe();
278
+ session.release();
279
+ });
280
+ const pending: EnrichedActivityRecord[] = [];
281
+ let live = false;
282
+ try {
283
+ unsubscribe = store.subscribeActivity(cubeId, (entry) => {
284
+ if (store.getCube(cubeId) === null) {
285
+ queue.close();
286
+ return;
287
+ }
288
+ if (live) queue.push(encodeLogEvent(entry));
289
+ else if (pending.length >= 200) queue.close();
290
+ else pending.push(entry);
291
+ });
292
+ signal.addEventListener("abort", () => queue.close(), { once: true });
293
+ const replay = store.readLog(cubeId, cursor, 200).entries;
294
+ const barrier = this.#replayBarrier;
295
+ this.#replayBarrier = undefined;
296
+ if (barrier !== undefined) {
297
+ // Signal only after replay is captured and the live listener is installed.
298
+ barrier.markReached();
299
+ await barrier.release;
300
+ }
301
+ const seen = new Set(replay.map(cursorKey));
302
+ for (const entry of replay) queue.push(encodeLogEvent(entry));
303
+ for (const entry of pending) {
304
+ if (!seen.has(cursorKey(entry)) && afterCursor(entry, cursor)) {
305
+ seen.add(cursorKey(entry));
306
+ queue.push(encodeLogEvent(entry));
307
+ }
308
+ }
309
+ queue.push(`event: bookmark\ndata: ${JSON.stringify({
310
+ as_of: new Date().toISOString(),
311
+ replay_complete: true,
312
+ })}\n\n`);
313
+ live = true;
314
+ return { status: 200, stream: queue };
315
+ } catch (error) {
316
+ queue.close();
317
+ if (error instanceof CursorExpiredError) return failure(410, "CURSOR_EXPIRED", error.message);
318
+ if (error instanceof ScopedStoreError) return failure(404, "NOT_FOUND", error.message);
319
+ throw error;
320
+ }
321
+ }
322
+ }
323
+
324
+ class InputError extends Error {}
325
+
326
+ class AsyncStringQueue implements AsyncIterable<string> {
327
+ readonly #values: string[] = [];
328
+ readonly #waiters: Array<(result: IteratorResult<string>) => void> = [];
329
+ readonly #cleanup: () => void;
330
+ #closed = false;
331
+
332
+ constructor(cleanup: () => void) {
333
+ this.#cleanup = cleanup;
334
+ }
335
+
336
+ push(value: string): void {
337
+ if (this.#closed) return;
338
+ if (this.#values.length >= 200) {
339
+ this.close();
340
+ return;
341
+ }
342
+ const waiter = this.#waiters.shift();
343
+ if (waiter === undefined) this.#values.push(value);
344
+ else waiter({ value, done: false });
345
+ }
346
+
347
+ close(): void {
348
+ if (this.#closed) return;
349
+ this.#closed = true;
350
+ this.#cleanup();
351
+ for (const waiter of this.#waiters.splice(0)) waiter({ value: undefined, done: true });
352
+ }
353
+
354
+ [Symbol.asyncIterator](): AsyncIterator<string> {
355
+ return {
356
+ next: async () => {
357
+ const value = this.#values.shift();
358
+ if (value !== undefined) return { value, done: false };
359
+ if (this.#closed) return { value: undefined, done: true };
360
+ return new Promise<IteratorResult<string>>((resolve) => this.#waiters.push(resolve));
361
+ },
362
+ return: async () => {
363
+ this.close();
364
+ return { value: undefined, done: true };
365
+ },
366
+ };
367
+ }
368
+ }
369
+
370
+ function decodeEnvelope(value: unknown): RequestEnvelope {
371
+ const record = object(value);
372
+ exactKeys(record, ["protocol_version", "request_id", "payload"]);
373
+ if (record["protocol_version"] !== "1") throw new InputError();
374
+ const requestId = record["request_id"];
375
+ if (typeof requestId !== "string" || !/^[A-Za-z0-9._-]{8,128}$/u.test(requestId)) {
376
+ throw new InputError();
377
+ }
378
+ return { requestId, payload: object(record["payload"]) };
379
+ }
380
+
381
+ function object(value: unknown): Record<string, unknown> {
382
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new InputError();
383
+ return value as Record<string, unknown>;
384
+ }
385
+
386
+ function exactKeys(record: Record<string, unknown>, required: string[], optional: string[] = []): void {
387
+ const allowed = new Set([...required, ...optional]);
388
+ if (required.some((key) => !Object.hasOwn(record, key)) ||
389
+ Object.keys(record).some((key) => !allowed.has(key))) throw new InputError();
390
+ }
391
+
392
+ function requiredString(record: Record<string, unknown>, key: string, maxBytes: number): string {
393
+ const value = record[key];
394
+ if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value) > maxBytes) {
395
+ throw new InputError();
396
+ }
397
+ return value;
398
+ }
399
+
400
+ function requiredPresentationName(record: Record<string, unknown>, key: string): string {
401
+ const value = requiredString(record, key, 120);
402
+ if (!/^[A-Za-z0-9][A-Za-z0-9 ._-]*$/u.test(value)) throw new InputError();
403
+ return value;
404
+ }
405
+
406
+ function optionalString(
407
+ record: Record<string, unknown>,
408
+ key: string,
409
+ maxBytes: number,
410
+ ): string | undefined {
411
+ const value = record[key];
412
+ if (value === undefined) return undefined;
413
+ return requiredString(record, key, maxBytes);
414
+ }
415
+
416
+ function requiredUuid(record: Record<string, unknown>, key: string): string {
417
+ const value = record[key];
418
+ if (typeof value !== "string" || !uuidPattern.test(value)) throw new InputError();
419
+ return value.toLowerCase();
420
+ }
421
+
422
+ function optionalVisibility(value: unknown): "broadcast" | "direct" | undefined {
423
+ if (value === undefined) return undefined;
424
+ if (value !== "broadcast" && value !== "direct") throw new InputError();
425
+ return value;
426
+ }
427
+
428
+ function optionalUuidArray(value: unknown): string[] | undefined {
429
+ if (value === undefined) return undefined;
430
+ if (!Array.isArray(value) || value.length > 100 ||
431
+ value.some((item) => typeof item !== "string" || !uuidPattern.test(item))) throw new InputError();
432
+ return value as string[];
433
+ }
434
+
435
+ function optionalLimit(value: unknown): number {
436
+ if (value === undefined) return 100;
437
+ if (!Number.isSafeInteger(value) || (value as number) < 1 || (value as number) > 500) {
438
+ throw new InputError();
439
+ }
440
+ return value as number;
441
+ }
442
+
443
+ function decodeCursor(value: unknown): LogCursor | null {
444
+ if (value === null) return null;
445
+ const record = object(value);
446
+ exactKeys(record, ["id", "created_at"]);
447
+ const id = requiredUuid(record, "id");
448
+ const createdAt = record["created_at"];
449
+ if (typeof createdAt !== "string" || !timestampPattern.test(createdAt) ||
450
+ new Date(createdAt).toISOString() !== createdAt) throw new InputError();
451
+ return { id, created_at: createdAt };
452
+ }
453
+
454
+ function decodeOpaqueCursor(value: string | undefined): LogCursor | null {
455
+ if (value === undefined) return null;
456
+ try {
457
+ return decodeCursor(JSON.parse(Buffer.from(value, "base64url").toString("utf8")));
458
+ } catch {
459
+ throw new InputError();
460
+ }
461
+ }
462
+
463
+ function safeRequestId(value: unknown): string | undefined {
464
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
465
+ const requestId = (value as Record<string, unknown>)["request_id"];
466
+ return typeof requestId === "string" && /^[A-Za-z0-9._-]{8,128}$/u.test(requestId)
467
+ ? requestId
468
+ : undefined;
469
+ }
470
+
471
+ function success(status: number, requestId: string, payload: unknown): CoordinationResponse {
472
+ return { status, body: { protocol_version: "1", request_id: requestId, payload } };
473
+ }
474
+
475
+ function failure(
476
+ status: number,
477
+ code: string,
478
+ message: string,
479
+ requestId?: string,
480
+ ): CoordinationResponse {
481
+ return {
482
+ status,
483
+ body: {
484
+ protocol_version: "1",
485
+ ...(requestId === undefined ? {} : { request_id: requestId }),
486
+ error: { code, message },
487
+ },
488
+ };
489
+ }
490
+
491
+ function encodeLogEvent(entry: EnrichedActivityRecord): string {
492
+ return `event: log\nid: ${entry.id}\ndata: ${JSON.stringify({
493
+ cursor: { id: entry.id, created_at: entry.created_at },
494
+ entry,
495
+ })}\n\n`;
496
+ }
497
+
498
+ function cursorKey(entry: EnrichedActivityRecord): string {
499
+ return `${entry.created_at}\0${entry.id}`;
500
+ }
501
+
502
+ function afterCursor(entry: EnrichedActivityRecord, cursor: LogCursor | null): boolean {
503
+ return cursor === null || entry.created_at > cursor.created_at ||
504
+ (entry.created_at === cursor.created_at && entry.id > cursor.id);
505
+ }
506
+
507
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
508
+ const timestampPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;