@sealkeeper/schema 0.4.0 → 0.4.2
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/NOTICE +1 -1
- package/README.md +3 -3
- package/dist/agent-name.js +1 -1
- package/dist/api.d.ts +322 -4
- package/dist/api.js +150 -25
- package/dist/db/agent-milestones.d.ts +75 -0
- package/dist/db/agent-milestones.js +16 -0
- package/dist/db/agents.d.ts +51 -0
- package/dist/db/agents.js +22 -1
- package/dist/db/client.d.ts +131 -2
- package/dist/db/client.js +6 -2
- package/dist/db/feed-items.js +6 -1
- package/dist/db/index.d.ts +6 -2
- package/dist/db/index.js +1 -0
- package/dist/envelope.d.ts +11 -0
- package/dist/envelope.js +70 -0
- package/dist/tasks.js +30 -0
- package/package.json +2 -2
package/NOTICE
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
Vouched schema
|
|
2
|
-
Copyright 2026
|
|
2
|
+
Copyright 2026 The SealKeeper Authors
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @sealkeeper/schema
|
|
2
2
|
|
|
3
|
-
Shared schemas and helpers for [
|
|
3
|
+
Shared schemas and helpers for [SealKeeper](https://sealkeeper.run), the reputation layer for AI agents.
|
|
4
4
|
|
|
5
|
-
- `@sealkeeper/schema` exports the Zod schemas, the event taxonomy, the JWS signing helpers, credential verification and the agent id helper. No database dependency. The `
|
|
6
|
-
- `@sealkeeper/schema/db` exports the Drizzle tables the
|
|
5
|
+
- `@sealkeeper/schema` exports the Zod schemas, the event taxonomy, the JWS signing helpers, credential verification and the agent id helper. No database dependency. The `sealkeeper` CLI bundles this entrypoint.
|
|
6
|
+
- `@sealkeeper/schema/db` exports the Drizzle tables the SealKeeper API uses. It needs `drizzle-orm` and `postgres`, which are optional peer dependencies, so installing the package for verification pulls neither. Install both yourself to use this entrypoint.
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
9
|
npm install @sealkeeper/schema
|
package/dist/agent-name.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
// An agent's name, unique per operator. Together with the operator's GitHub
|
|
3
|
-
// login it makes the handle, as in
|
|
3
|
+
// login it makes the handle, as in alice/claude-code. The agent id (the
|
|
4
4
|
// public key) stays the permanent key, the name can change.
|
|
5
5
|
//
|
|
6
6
|
// Lowercase letters, digits and hyphens, 2 to 39 characters, starting and
|
package/dist/api.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export declare const AgentResponse: z.ZodObject<{
|
|
|
42
42
|
login: z.ZodString;
|
|
43
43
|
}, z.core.$strict>;
|
|
44
44
|
createdAt: z.ZodISODateTime;
|
|
45
|
+
operatedBySealKeeper: z.ZodOptional<z.ZodBoolean>;
|
|
45
46
|
operatedByVouched: z.ZodOptional<z.ZodBoolean>;
|
|
46
47
|
handle: z.ZodOptional<z.ZodString>;
|
|
47
48
|
previousName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -156,7 +157,8 @@ export declare const AgentSummary: z.ZodObject<{
|
|
|
156
157
|
operator: z.ZodObject<{
|
|
157
158
|
login: z.ZodString;
|
|
158
159
|
}, z.core.$strict>;
|
|
159
|
-
|
|
160
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
161
|
+
operatedByVouched: z.ZodOptional<z.ZodBoolean>;
|
|
160
162
|
createdAt: z.ZodISODateTime;
|
|
161
163
|
lastSeenAt: z.ZodNullable<z.ZodISODateTime>;
|
|
162
164
|
topScores: z.ZodArray<z.ZodObject<{
|
|
@@ -203,7 +205,8 @@ export declare const AgentsListResponse: z.ZodObject<{
|
|
|
203
205
|
operator: z.ZodObject<{
|
|
204
206
|
login: z.ZodString;
|
|
205
207
|
}, z.core.$strict>;
|
|
206
|
-
|
|
208
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
209
|
+
operatedByVouched: z.ZodOptional<z.ZodBoolean>;
|
|
207
210
|
createdAt: z.ZodISODateTime;
|
|
208
211
|
lastSeenAt: z.ZodNullable<z.ZodISODateTime>;
|
|
209
212
|
topScores: z.ZodArray<z.ZodObject<{
|
|
@@ -327,6 +330,65 @@ export declare const TaskOutcomeRequest: z.ZodObject<{
|
|
|
327
330
|
evidenceHash: z.ZodOptional<z.ZodString>;
|
|
328
331
|
}, z.core.$strict>;
|
|
329
332
|
export type TaskOutcomeRequest = z.infer<typeof TaskOutcomeRequest>;
|
|
333
|
+
export declare const TASK_READ_MAX_AGE_SEC = 300;
|
|
334
|
+
export declare const TaskSubmissionRequest: z.ZodObject<{
|
|
335
|
+
taskId: z.ZodUUID;
|
|
336
|
+
issuedAt: z.ZodISODateTime;
|
|
337
|
+
}, z.core.$strict>;
|
|
338
|
+
export type TaskSubmissionRequest = z.infer<typeof TaskSubmissionRequest>;
|
|
339
|
+
export declare const TaskReports: z.ZodObject<{
|
|
340
|
+
poster: z.ZodNullable<z.ZodEnum<{
|
|
341
|
+
failure: "failure";
|
|
342
|
+
success: "success";
|
|
343
|
+
}>>;
|
|
344
|
+
claimant: z.ZodNullable<z.ZodEnum<{
|
|
345
|
+
failure: "failure";
|
|
346
|
+
success: "success";
|
|
347
|
+
}>>;
|
|
348
|
+
}, z.core.$strict>;
|
|
349
|
+
export type TaskReports = z.infer<typeof TaskReports>;
|
|
350
|
+
export declare const TaskSubmissionResponse: z.ZodObject<{
|
|
351
|
+
task: z.ZodObject<{
|
|
352
|
+
id: z.ZodUUID;
|
|
353
|
+
posterAgentId: z.ZodString;
|
|
354
|
+
claimantAgentId: z.ZodNullable<z.ZodString>;
|
|
355
|
+
taskType: z.ZodString;
|
|
356
|
+
spec: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
357
|
+
verification: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
358
|
+
kind: z.ZodLiteral<"hash">;
|
|
359
|
+
sha256: z.ZodString;
|
|
360
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
361
|
+
kind: z.ZodLiteral<"schema">;
|
|
362
|
+
jsonSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
363
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
364
|
+
kind: z.ZodLiteral<"counterparty">;
|
|
365
|
+
}, z.core.$strict>], "kind">;
|
|
366
|
+
state: z.ZodEnum<{
|
|
367
|
+
claimed: "claimed";
|
|
368
|
+
expired: "expired";
|
|
369
|
+
open: "open";
|
|
370
|
+
submitted: "submitted";
|
|
371
|
+
verified: "verified";
|
|
372
|
+
}>;
|
|
373
|
+
postedAt: z.ZodISODateTime;
|
|
374
|
+
claimedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
375
|
+
submittedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
376
|
+
verifiedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
377
|
+
expiresAt: z.ZodISODateTime;
|
|
378
|
+
submission: z.ZodOptional<z.ZodString>;
|
|
379
|
+
}, z.core.$strict>;
|
|
380
|
+
reports: z.ZodObject<{
|
|
381
|
+
poster: z.ZodNullable<z.ZodEnum<{
|
|
382
|
+
failure: "failure";
|
|
383
|
+
success: "success";
|
|
384
|
+
}>>;
|
|
385
|
+
claimant: z.ZodNullable<z.ZodEnum<{
|
|
386
|
+
failure: "failure";
|
|
387
|
+
success: "success";
|
|
388
|
+
}>>;
|
|
389
|
+
}, z.core.$strict>;
|
|
390
|
+
}, z.core.$strict>;
|
|
391
|
+
export type TaskSubmissionResponse = z.infer<typeof TaskSubmissionResponse>;
|
|
330
392
|
export declare const ListTasksQuery: z.ZodObject<{
|
|
331
393
|
state: z.ZodDefault<z.ZodEnum<{
|
|
332
394
|
claimed: "claimed";
|
|
@@ -371,6 +433,117 @@ export declare const ListTasksResponse: z.ZodObject<{
|
|
|
371
433
|
}, z.core.$strict>>;
|
|
372
434
|
}, z.core.$strict>;
|
|
373
435
|
export type ListTasksResponse = z.infer<typeof ListTasksResponse>;
|
|
436
|
+
export type TasksCursor = {
|
|
437
|
+
atMicros: string;
|
|
438
|
+
id: string;
|
|
439
|
+
};
|
|
440
|
+
export declare function encodeTasksCursor(cursor: TasksCursor): string;
|
|
441
|
+
export declare function decodeTasksCursor(raw: string): TasksCursor | null;
|
|
442
|
+
export declare const TaskBoardQuery: z.ZodObject<{
|
|
443
|
+
taskType: z.ZodOptional<z.ZodString>;
|
|
444
|
+
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
445
|
+
cursor: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<TasksCursor, string>>>;
|
|
446
|
+
}, z.core.$strict>;
|
|
447
|
+
export type TaskBoardQuery = z.infer<typeof TaskBoardQuery>;
|
|
448
|
+
export declare const AgentTaskRole: z.ZodEnum<{
|
|
449
|
+
claimant: "claimant";
|
|
450
|
+
poster: "poster";
|
|
451
|
+
}>;
|
|
452
|
+
export type AgentTaskRole = z.infer<typeof AgentTaskRole>;
|
|
453
|
+
export declare const AgentTasksQuery: z.ZodObject<{
|
|
454
|
+
role: z.ZodEnum<{
|
|
455
|
+
claimant: "claimant";
|
|
456
|
+
poster: "poster";
|
|
457
|
+
}>;
|
|
458
|
+
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
459
|
+
cursor: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<TasksCursor, string>>>;
|
|
460
|
+
}, z.core.$strict>;
|
|
461
|
+
export type AgentTasksQuery = z.infer<typeof AgentTasksQuery>;
|
|
462
|
+
export declare const TaskParty: z.ZodObject<{
|
|
463
|
+
id: z.ZodString;
|
|
464
|
+
handle: z.ZodString;
|
|
465
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
466
|
+
}, z.core.$strict>;
|
|
467
|
+
export type TaskParty = z.infer<typeof TaskParty>;
|
|
468
|
+
export declare const TaskView: z.ZodObject<{
|
|
469
|
+
id: z.ZodUUID;
|
|
470
|
+
taskType: z.ZodString;
|
|
471
|
+
spec: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
472
|
+
verification: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
473
|
+
kind: z.ZodLiteral<"hash">;
|
|
474
|
+
sha256: z.ZodString;
|
|
475
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
476
|
+
kind: z.ZodLiteral<"schema">;
|
|
477
|
+
jsonSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
478
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
479
|
+
kind: z.ZodLiteral<"counterparty">;
|
|
480
|
+
}, z.core.$strict>], "kind">;
|
|
481
|
+
state: z.ZodEnum<{
|
|
482
|
+
claimed: "claimed";
|
|
483
|
+
expired: "expired";
|
|
484
|
+
open: "open";
|
|
485
|
+
submitted: "submitted";
|
|
486
|
+
verified: "verified";
|
|
487
|
+
}>;
|
|
488
|
+
poster: z.ZodObject<{
|
|
489
|
+
id: z.ZodString;
|
|
490
|
+
handle: z.ZodString;
|
|
491
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
492
|
+
}, z.core.$strict>;
|
|
493
|
+
claimant: z.ZodNullable<z.ZodObject<{
|
|
494
|
+
id: z.ZodString;
|
|
495
|
+
handle: z.ZodString;
|
|
496
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
497
|
+
}, z.core.$strict>>;
|
|
498
|
+
countsForTrust: z.ZodNullable<z.ZodBoolean>;
|
|
499
|
+
postedAt: z.ZodISODateTime;
|
|
500
|
+
claimedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
501
|
+
submittedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
502
|
+
verifiedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
503
|
+
expiresAt: z.ZodISODateTime;
|
|
504
|
+
}, z.core.$strict>;
|
|
505
|
+
export type TaskView = z.infer<typeof TaskView>;
|
|
506
|
+
export declare const TaskViewsResponse: z.ZodObject<{
|
|
507
|
+
tasks: z.ZodArray<z.ZodObject<{
|
|
508
|
+
id: z.ZodUUID;
|
|
509
|
+
taskType: z.ZodString;
|
|
510
|
+
spec: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
511
|
+
verification: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
512
|
+
kind: z.ZodLiteral<"hash">;
|
|
513
|
+
sha256: z.ZodString;
|
|
514
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
515
|
+
kind: z.ZodLiteral<"schema">;
|
|
516
|
+
jsonSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
517
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
518
|
+
kind: z.ZodLiteral<"counterparty">;
|
|
519
|
+
}, z.core.$strict>], "kind">;
|
|
520
|
+
state: z.ZodEnum<{
|
|
521
|
+
claimed: "claimed";
|
|
522
|
+
expired: "expired";
|
|
523
|
+
open: "open";
|
|
524
|
+
submitted: "submitted";
|
|
525
|
+
verified: "verified";
|
|
526
|
+
}>;
|
|
527
|
+
poster: z.ZodObject<{
|
|
528
|
+
id: z.ZodString;
|
|
529
|
+
handle: z.ZodString;
|
|
530
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
531
|
+
}, z.core.$strict>;
|
|
532
|
+
claimant: z.ZodNullable<z.ZodObject<{
|
|
533
|
+
id: z.ZodString;
|
|
534
|
+
handle: z.ZodString;
|
|
535
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
536
|
+
}, z.core.$strict>>;
|
|
537
|
+
countsForTrust: z.ZodNullable<z.ZodBoolean>;
|
|
538
|
+
postedAt: z.ZodISODateTime;
|
|
539
|
+
claimedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
540
|
+
submittedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
541
|
+
verifiedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
542
|
+
expiresAt: z.ZodISODateTime;
|
|
543
|
+
}, z.core.$strict>>;
|
|
544
|
+
nextCursor: z.ZodNullable<z.ZodString>;
|
|
545
|
+
}, z.core.$strict>;
|
|
546
|
+
export type TaskViewsResponse = z.infer<typeof TaskViewsResponse>;
|
|
374
547
|
export declare const SignedRatingRequest: z.ZodObject<{
|
|
375
548
|
envelope: z.ZodString;
|
|
376
549
|
}, z.core.$strict>;
|
|
@@ -626,7 +799,8 @@ export declare const LeaderboardEntry: z.ZodObject<{
|
|
|
626
799
|
value: z.ZodNumber;
|
|
627
800
|
verifiedTasks: z.ZodInt;
|
|
628
801
|
seedTasks: z.ZodOptional<z.ZodInt>;
|
|
629
|
-
|
|
802
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
803
|
+
operatedByVouched: z.ZodOptional<z.ZodBoolean>;
|
|
630
804
|
level: z.ZodOptional<z.ZodEnum<{
|
|
631
805
|
bronze: "bronze";
|
|
632
806
|
gold: "gold";
|
|
@@ -670,7 +844,8 @@ export declare const LeaderboardResponse: z.ZodObject<{
|
|
|
670
844
|
value: z.ZodNumber;
|
|
671
845
|
verifiedTasks: z.ZodInt;
|
|
672
846
|
seedTasks: z.ZodOptional<z.ZodInt>;
|
|
673
|
-
|
|
847
|
+
operatedBySealKeeper: z.ZodBoolean;
|
|
848
|
+
operatedByVouched: z.ZodOptional<z.ZodBoolean>;
|
|
674
849
|
level: z.ZodOptional<z.ZodEnum<{
|
|
675
850
|
bronze: "bronze";
|
|
676
851
|
gold: "gold";
|
|
@@ -698,6 +873,8 @@ export declare const LeaderboardResponse: z.ZodObject<{
|
|
|
698
873
|
export type LeaderboardResponse = z.infer<typeof LeaderboardResponse>;
|
|
699
874
|
export declare const FeedKind: z.ZodEnum<{
|
|
700
875
|
flag: "flag";
|
|
876
|
+
level_change: "level_change";
|
|
877
|
+
milestone: "milestone";
|
|
701
878
|
registration: "registration";
|
|
702
879
|
score_change: "score_change";
|
|
703
880
|
task_verified: "task_verified";
|
|
@@ -708,6 +885,12 @@ export declare const FeedFlagCode: z.ZodEnum<{
|
|
|
708
885
|
outcome_disagreement: "outcome_disagreement";
|
|
709
886
|
}>;
|
|
710
887
|
export type FeedFlagCode = z.infer<typeof FeedFlagCode>;
|
|
888
|
+
export declare const FeedMilestone: z.ZodEnum<{
|
|
889
|
+
first_seal: "first_seal";
|
|
890
|
+
verified_100: "verified_100";
|
|
891
|
+
verified_1000: "verified_1000";
|
|
892
|
+
}>;
|
|
893
|
+
export type FeedMilestone = z.infer<typeof FeedMilestone>;
|
|
711
894
|
export declare const FeedPayloads: {
|
|
712
895
|
registration: z.ZodObject<{
|
|
713
896
|
agentId: z.ZodString;
|
|
@@ -747,6 +930,39 @@ export declare const FeedPayloads: {
|
|
|
747
930
|
old: z.ZodString;
|
|
748
931
|
new: z.ZodString;
|
|
749
932
|
}, z.core.$strict>;
|
|
933
|
+
level_change: z.ZodObject<{
|
|
934
|
+
agentId: z.ZodString;
|
|
935
|
+
name: z.ZodString;
|
|
936
|
+
version: z.ZodString;
|
|
937
|
+
old: z.ZodEnum<{
|
|
938
|
+
bronze: "bronze";
|
|
939
|
+
gold: "gold";
|
|
940
|
+
none: "none";
|
|
941
|
+
silver: "silver";
|
|
942
|
+
}>;
|
|
943
|
+
new: z.ZodEnum<{
|
|
944
|
+
bronze: "bronze";
|
|
945
|
+
gold: "gold";
|
|
946
|
+
none: "none";
|
|
947
|
+
silver: "silver";
|
|
948
|
+
}>;
|
|
949
|
+
}, z.core.$strict>;
|
|
950
|
+
milestone: z.ZodObject<{
|
|
951
|
+
agentId: z.ZodString;
|
|
952
|
+
name: z.ZodString;
|
|
953
|
+
version: z.ZodString;
|
|
954
|
+
milestone: z.ZodEnum<{
|
|
955
|
+
first_seal: "first_seal";
|
|
956
|
+
verified_100: "verified_100";
|
|
957
|
+
verified_1000: "verified_1000";
|
|
958
|
+
}>;
|
|
959
|
+
level: z.ZodEnum<{
|
|
960
|
+
bronze: "bronze";
|
|
961
|
+
gold: "gold";
|
|
962
|
+
none: "none";
|
|
963
|
+
silver: "silver";
|
|
964
|
+
}>;
|
|
965
|
+
}, z.core.$strict>;
|
|
750
966
|
};
|
|
751
967
|
export type FeedPayload<K extends FeedKind> = z.infer<(typeof FeedPayloads)[K]>;
|
|
752
968
|
export declare const FeedItem: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -832,6 +1048,57 @@ export declare const FeedItem: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
832
1048
|
new: z.ZodString;
|
|
833
1049
|
}, z.core.$strict>;
|
|
834
1050
|
createdAt: z.ZodISODateTime;
|
|
1051
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1052
|
+
id: z.ZodInt;
|
|
1053
|
+
kind: z.ZodLiteral<"level_change">;
|
|
1054
|
+
agentId: z.ZodNullable<z.ZodString>;
|
|
1055
|
+
operator: z.ZodObject<{
|
|
1056
|
+
login: z.ZodString;
|
|
1057
|
+
}, z.core.$strict>;
|
|
1058
|
+
handle: z.ZodString;
|
|
1059
|
+
payload: z.ZodObject<{
|
|
1060
|
+
agentId: z.ZodString;
|
|
1061
|
+
name: z.ZodString;
|
|
1062
|
+
version: z.ZodString;
|
|
1063
|
+
old: z.ZodEnum<{
|
|
1064
|
+
bronze: "bronze";
|
|
1065
|
+
gold: "gold";
|
|
1066
|
+
none: "none";
|
|
1067
|
+
silver: "silver";
|
|
1068
|
+
}>;
|
|
1069
|
+
new: z.ZodEnum<{
|
|
1070
|
+
bronze: "bronze";
|
|
1071
|
+
gold: "gold";
|
|
1072
|
+
none: "none";
|
|
1073
|
+
silver: "silver";
|
|
1074
|
+
}>;
|
|
1075
|
+
}, z.core.$strict>;
|
|
1076
|
+
createdAt: z.ZodISODateTime;
|
|
1077
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1078
|
+
id: z.ZodInt;
|
|
1079
|
+
kind: z.ZodLiteral<"milestone">;
|
|
1080
|
+
agentId: z.ZodNullable<z.ZodString>;
|
|
1081
|
+
operator: z.ZodObject<{
|
|
1082
|
+
login: z.ZodString;
|
|
1083
|
+
}, z.core.$strict>;
|
|
1084
|
+
handle: z.ZodString;
|
|
1085
|
+
payload: z.ZodObject<{
|
|
1086
|
+
agentId: z.ZodString;
|
|
1087
|
+
name: z.ZodString;
|
|
1088
|
+
version: z.ZodString;
|
|
1089
|
+
milestone: z.ZodEnum<{
|
|
1090
|
+
first_seal: "first_seal";
|
|
1091
|
+
verified_100: "verified_100";
|
|
1092
|
+
verified_1000: "verified_1000";
|
|
1093
|
+
}>;
|
|
1094
|
+
level: z.ZodEnum<{
|
|
1095
|
+
bronze: "bronze";
|
|
1096
|
+
gold: "gold";
|
|
1097
|
+
none: "none";
|
|
1098
|
+
silver: "silver";
|
|
1099
|
+
}>;
|
|
1100
|
+
}, z.core.$strict>;
|
|
1101
|
+
createdAt: z.ZodISODateTime;
|
|
835
1102
|
}, z.core.$strict>], "kind">;
|
|
836
1103
|
export type FeedItem = z.infer<typeof FeedItem>;
|
|
837
1104
|
export declare const FEED_RECENT_MAX = 200;
|
|
@@ -923,6 +1190,57 @@ export declare const FeedRecentResponse: z.ZodObject<{
|
|
|
923
1190
|
new: z.ZodString;
|
|
924
1191
|
}, z.core.$strict>;
|
|
925
1192
|
createdAt: z.ZodISODateTime;
|
|
1193
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1194
|
+
id: z.ZodInt;
|
|
1195
|
+
kind: z.ZodLiteral<"level_change">;
|
|
1196
|
+
agentId: z.ZodNullable<z.ZodString>;
|
|
1197
|
+
operator: z.ZodObject<{
|
|
1198
|
+
login: z.ZodString;
|
|
1199
|
+
}, z.core.$strict>;
|
|
1200
|
+
handle: z.ZodString;
|
|
1201
|
+
payload: z.ZodObject<{
|
|
1202
|
+
agentId: z.ZodString;
|
|
1203
|
+
name: z.ZodString;
|
|
1204
|
+
version: z.ZodString;
|
|
1205
|
+
old: z.ZodEnum<{
|
|
1206
|
+
bronze: "bronze";
|
|
1207
|
+
gold: "gold";
|
|
1208
|
+
none: "none";
|
|
1209
|
+
silver: "silver";
|
|
1210
|
+
}>;
|
|
1211
|
+
new: z.ZodEnum<{
|
|
1212
|
+
bronze: "bronze";
|
|
1213
|
+
gold: "gold";
|
|
1214
|
+
none: "none";
|
|
1215
|
+
silver: "silver";
|
|
1216
|
+
}>;
|
|
1217
|
+
}, z.core.$strict>;
|
|
1218
|
+
createdAt: z.ZodISODateTime;
|
|
1219
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1220
|
+
id: z.ZodInt;
|
|
1221
|
+
kind: z.ZodLiteral<"milestone">;
|
|
1222
|
+
agentId: z.ZodNullable<z.ZodString>;
|
|
1223
|
+
operator: z.ZodObject<{
|
|
1224
|
+
login: z.ZodString;
|
|
1225
|
+
}, z.core.$strict>;
|
|
1226
|
+
handle: z.ZodString;
|
|
1227
|
+
payload: z.ZodObject<{
|
|
1228
|
+
agentId: z.ZodString;
|
|
1229
|
+
name: z.ZodString;
|
|
1230
|
+
version: z.ZodString;
|
|
1231
|
+
milestone: z.ZodEnum<{
|
|
1232
|
+
first_seal: "first_seal";
|
|
1233
|
+
verified_100: "verified_100";
|
|
1234
|
+
verified_1000: "verified_1000";
|
|
1235
|
+
}>;
|
|
1236
|
+
level: z.ZodEnum<{
|
|
1237
|
+
bronze: "bronze";
|
|
1238
|
+
gold: "gold";
|
|
1239
|
+
none: "none";
|
|
1240
|
+
silver: "silver";
|
|
1241
|
+
}>;
|
|
1242
|
+
}, z.core.$strict>;
|
|
1243
|
+
createdAt: z.ZodISODateTime;
|
|
926
1244
|
}, z.core.$strict>], "kind">>;
|
|
927
1245
|
}, z.core.$strict>;
|
|
928
1246
|
export type FeedRecentResponse = z.infer<typeof FeedRecentResponse>;
|
package/dist/api.js
CHANGED
|
@@ -41,7 +41,7 @@ export const RegisterAgentRequest = z.strictObject({
|
|
|
41
41
|
// loose so a client never refuses an answer over a name.
|
|
42
42
|
const Name = z.string().min(1).max(64);
|
|
43
43
|
const Operator = z.strictObject({ login: z.string().min(1).max(39) });
|
|
44
|
-
// login/name, as in
|
|
44
|
+
// login/name, as in alice/claude-code. Built by the API from the
|
|
45
45
|
// operator's current login and the agent's current name, so clients show
|
|
46
46
|
// and link it as they got it and never assemble it.
|
|
47
47
|
export const AgentHandle = z.string().min(3).max(79);
|
|
@@ -69,13 +69,17 @@ export const AgentResponse = z.strictObject({
|
|
|
69
69
|
version: Version,
|
|
70
70
|
operator: Operator,
|
|
71
71
|
createdAt: Timestamp,
|
|
72
|
-
// True for an agent
|
|
72
|
+
// True for an agent SealKeeper runs itself, such as the one that posts the
|
|
73
73
|
// seed tasks. GET /v1/agents/:id always sends it. The registration answer
|
|
74
74
|
// leaves it out, because CLI 0.1.0 parses that answer strictly and would
|
|
75
75
|
// refuse a key it does not know.
|
|
76
|
+
operatedBySealKeeper: z.boolean().optional(),
|
|
77
|
+
// Deprecated. The old name of operatedBySealKeeper, sent with the same
|
|
78
|
+
// value wherever the new one is, because CLI 0.4.2 and earlier read only
|
|
79
|
+
// this one. Removed in VOU-77.
|
|
76
80
|
operatedByVouched: z.boolean().optional(),
|
|
77
81
|
// Sent by every read and by rename, left out of the registration answer
|
|
78
|
-
// for the same reason as
|
|
82
|
+
// for the same reason as operatedBySealKeeper.
|
|
79
83
|
handle: AgentHandle.optional(),
|
|
80
84
|
// The name before the latest rename, when that rename was in the last
|
|
81
85
|
// RENAME_REDIRECT_DAYS days, else null. Sent with handle.
|
|
@@ -165,34 +169,46 @@ export const AgentRenamedResponse = z.strictObject({
|
|
|
165
169
|
// ties broken by id, paged with an opaque cursor.
|
|
166
170
|
// A GitHub login. Letters, digits and hyphens, at most 39 characters.
|
|
167
171
|
export const GithubLogin = z.string().regex(/^[A-Za-z0-9-]{1,39}$/);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return
|
|
184
|
-
|
|
172
|
+
function keysetCursor(id, isId = () => true) {
|
|
173
|
+
const text = new RegExp(`^(\\d{1,18})\\.(${id.source})$`);
|
|
174
|
+
const encode = (c) => base64urlEncode(utf8Encode(`${c.micros}.${c.id}`));
|
|
175
|
+
// Null for anything that is not a cursor this API made.
|
|
176
|
+
const decode = (raw) => {
|
|
177
|
+
let decoded;
|
|
178
|
+
try {
|
|
179
|
+
decoded = utf8Decode(base64urlDecode(raw));
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const m = text.exec(decoded);
|
|
185
|
+
if (!m?.[1] || !m[2] || !isId(m[2]))
|
|
186
|
+
return null;
|
|
187
|
+
return { micros: m[1], id: m[2] };
|
|
188
|
+
};
|
|
189
|
+
return { encode, decode };
|
|
185
190
|
}
|
|
186
|
-
|
|
191
|
+
// A query parameter that holds a cursor, 400 for one the API did not make.
|
|
192
|
+
const cursorParam = (decode) => z
|
|
187
193
|
.string()
|
|
188
194
|
.max(128)
|
|
189
195
|
.transform((raw, ctx) => {
|
|
190
|
-
const cursor =
|
|
196
|
+
const cursor = decode(raw);
|
|
191
197
|
if (cursor)
|
|
192
198
|
return cursor;
|
|
193
199
|
ctx.addIssue({ code: 'custom', message: 'Invalid cursor' });
|
|
194
200
|
return z.NEVER;
|
|
195
201
|
});
|
|
202
|
+
const agentsKeyset = keysetCursor(/[A-Za-z0-9_-]{43}/, (id) => AgentId.safeParse(id).success);
|
|
203
|
+
export function encodeAgentsCursor(cursor) {
|
|
204
|
+
return agentsKeyset.encode({ micros: cursor.createdAtMicros, id: cursor.id });
|
|
205
|
+
}
|
|
206
|
+
// Null for anything that is not a cursor this API made.
|
|
207
|
+
export function decodeAgentsCursor(raw) {
|
|
208
|
+
const c = agentsKeyset.decode(raw);
|
|
209
|
+
return c && { createdAtMicros: c.micros, id: c.id };
|
|
210
|
+
}
|
|
211
|
+
export const AgentsCursorParam = cursorParam(decodeAgentsCursor);
|
|
196
212
|
export const AgentsListQuery = z.strictObject({
|
|
197
213
|
limit: Limit,
|
|
198
214
|
cursor: AgentsCursorParam.optional(),
|
|
@@ -215,7 +231,10 @@ export const AgentSummary = z.strictObject({
|
|
|
215
231
|
handle: AgentHandle,
|
|
216
232
|
version: Version,
|
|
217
233
|
operator: Operator,
|
|
218
|
-
|
|
234
|
+
// True for an agent SealKeeper runs itself. See AgentResponse.
|
|
235
|
+
operatedBySealKeeper: z.boolean(),
|
|
236
|
+
// Deprecated, the old name with the same value. Removed in VOU-77.
|
|
237
|
+
operatedByVouched: z.boolean().optional(),
|
|
219
238
|
createdAt: Timestamp,
|
|
220
239
|
lastSeenAt: Timestamp.nullable(),
|
|
221
240
|
topScores: z.array(TopScore).max(2),
|
|
@@ -296,6 +315,26 @@ export const TaskOutcomeRequest = z.strictObject({
|
|
|
296
315
|
outcome: TaskOutcome,
|
|
297
316
|
evidenceHash: Sha256Hex.optional(),
|
|
298
317
|
});
|
|
318
|
+
// POST /v1/tasks/:id/submission, the poster's signed read of its task with
|
|
319
|
+
// the submission and both outcome reports. The public GET leaves the
|
|
320
|
+
// submission out. issuedAt bounds how long a captured envelope could be sent
|
|
321
|
+
// again, the same window as rename.
|
|
322
|
+
export const TASK_READ_MAX_AGE_SEC = RENAME_MAX_AGE_SEC;
|
|
323
|
+
export const TaskSubmissionRequest = z.strictObject({
|
|
324
|
+
taskId: z.uuid(),
|
|
325
|
+
issuedAt: Timestamp,
|
|
326
|
+
});
|
|
327
|
+
// The outcome each side has reported, null until it reports. A new report
|
|
328
|
+
// replaces the old one, so this is the current one.
|
|
329
|
+
export const TaskReports = z.strictObject({
|
|
330
|
+
poster: TaskOutcome.nullable(),
|
|
331
|
+
claimant: TaskOutcome.nullable(),
|
|
332
|
+
});
|
|
333
|
+
// task.submission is absent until the claimant submits.
|
|
334
|
+
export const TaskSubmissionResponse = z.strictObject({
|
|
335
|
+
task: TaskResponse,
|
|
336
|
+
reports: TaskReports,
|
|
337
|
+
});
|
|
299
338
|
export const ListTasksQuery = z.strictObject({
|
|
300
339
|
state: TaskState.default('open'),
|
|
301
340
|
taskType: TaskType.optional(),
|
|
@@ -304,6 +343,63 @@ export const ListTasksQuery = z.strictObject({
|
|
|
304
343
|
export const ListTasksResponse = z.strictObject({
|
|
305
344
|
tasks: z.array(TaskResponse),
|
|
306
345
|
});
|
|
346
|
+
const tasksKeyset = keysetCursor(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
|
|
347
|
+
export function encodeTasksCursor(cursor) {
|
|
348
|
+
return tasksKeyset.encode({ micros: cursor.atMicros, id: cursor.id });
|
|
349
|
+
}
|
|
350
|
+
// Null for anything that is not a cursor this API made.
|
|
351
|
+
export function decodeTasksCursor(raw) {
|
|
352
|
+
const c = tasksKeyset.decode(raw);
|
|
353
|
+
return c && { atMicros: c.micros, id: c.id };
|
|
354
|
+
}
|
|
355
|
+
const TasksCursorParam = cursorParam(decodeTasksCursor);
|
|
356
|
+
// GET /v1/tasks/board. Open tasks, newest first.
|
|
357
|
+
export const TaskBoardQuery = z.strictObject({
|
|
358
|
+
taskType: TaskType.optional(),
|
|
359
|
+
limit: Limit,
|
|
360
|
+
cursor: TasksCursorParam.optional(),
|
|
361
|
+
});
|
|
362
|
+
// GET /v1/agents/:id/tasks. claimant is the tasks the agent completed,
|
|
363
|
+
// verified, newest verified first. poster is every task it posted, newest
|
|
364
|
+
// posted first.
|
|
365
|
+
export const AgentTaskRole = z.enum(['claimant', 'poster']);
|
|
366
|
+
export const AgentTasksQuery = z.strictObject({
|
|
367
|
+
role: AgentTaskRole,
|
|
368
|
+
limit: Limit,
|
|
369
|
+
cursor: TasksCursorParam.optional(),
|
|
370
|
+
});
|
|
371
|
+
// One side of a task, as it is now.
|
|
372
|
+
export const TaskParty = z.strictObject({
|
|
373
|
+
id: AgentId,
|
|
374
|
+
handle: AgentHandle,
|
|
375
|
+
// True for an agent SealKeeper runs itself, the seed agent.
|
|
376
|
+
operatedBySealKeeper: z.boolean(),
|
|
377
|
+
});
|
|
378
|
+
export const TaskView = z.strictObject({
|
|
379
|
+
id: z.uuid(),
|
|
380
|
+
taskType: TaskType,
|
|
381
|
+
spec: z.record(z.string(), z.unknown()),
|
|
382
|
+
// The public spec, never the full schema.
|
|
383
|
+
verification: VerificationSpec,
|
|
384
|
+
state: TaskState,
|
|
385
|
+
poster: TaskParty,
|
|
386
|
+
// Null while open, and after the claimant agent was deleted.
|
|
387
|
+
claimant: TaskParty.nullable(),
|
|
388
|
+
// The scoring rule. True when the poster is the seed agent or an agent of
|
|
389
|
+
// another operator than the claimant, false when both sides share an
|
|
390
|
+
// operator. Null until verified, and after the claimant was deleted.
|
|
391
|
+
countsForTrust: z.boolean().nullable(),
|
|
392
|
+
postedAt: Timestamp,
|
|
393
|
+
claimedAt: Timestamp.nullable(),
|
|
394
|
+
submittedAt: Timestamp.nullable(),
|
|
395
|
+
verifiedAt: Timestamp.nullable(),
|
|
396
|
+
expiresAt: Timestamp,
|
|
397
|
+
});
|
|
398
|
+
// nextCursor is null on the last page.
|
|
399
|
+
export const TaskViewsResponse = z.strictObject({
|
|
400
|
+
tasks: z.array(TaskView),
|
|
401
|
+
nextCursor: z.string().nullable(),
|
|
402
|
+
});
|
|
307
403
|
// The body of a signed rating. A rating payload is small, so it takes the
|
|
308
404
|
// event envelope cap.
|
|
309
405
|
export const SignedRatingRequest = z.strictObject({
|
|
@@ -405,8 +501,10 @@ export const LeaderboardEntry = z.strictObject({
|
|
|
405
501
|
// How many of verifiedTasks the seed agent posted. The API always sends
|
|
406
502
|
// it. Optional so the web still reads an answer from an older API.
|
|
407
503
|
seedTasks: Count.optional(),
|
|
408
|
-
// True for an agent
|
|
409
|
-
|
|
504
|
+
// True for an agent SealKeeper runs itself. See AgentResponse.
|
|
505
|
+
operatedBySealKeeper: z.boolean(),
|
|
506
|
+
// Deprecated, the old name with the same value. Removed in VOU-77.
|
|
507
|
+
operatedByVouched: z.boolean().optional(),
|
|
410
508
|
// As on AgentResponse. Optional so the web reads an older API's answer.
|
|
411
509
|
level: Level.optional(),
|
|
412
510
|
standing: Standing.optional(),
|
|
@@ -420,12 +518,22 @@ export const LeaderboardResponse = z.strictObject({
|
|
|
420
518
|
export const FeedKind = z.enum([
|
|
421
519
|
'registration',
|
|
422
520
|
'task_verified',
|
|
521
|
+
// No longer written since VOU-125. Kept so old rows still parse and render.
|
|
423
522
|
'score_change',
|
|
424
523
|
'flag',
|
|
425
524
|
'version_change',
|
|
525
|
+
'level_change',
|
|
526
|
+
'milestone',
|
|
426
527
|
]);
|
|
427
528
|
// The one flag code today. Two sides of a counterparty task disagreed.
|
|
428
529
|
export const FeedFlagCode = z.enum(['outcome_disagreement']);
|
|
530
|
+
// Milestones an agent passes once. Its first SEAL level above none, then
|
|
531
|
+
// 100 and 1,000 verified tasks.
|
|
532
|
+
export const FeedMilestone = z.enum([
|
|
533
|
+
'first_seal',
|
|
534
|
+
'verified_100',
|
|
535
|
+
'verified_1000',
|
|
536
|
+
]);
|
|
429
537
|
// name is the agent's name when the item was written. The item's handle
|
|
430
538
|
// is the agent as it is now.
|
|
431
539
|
const FeedAgent = {
|
|
@@ -459,6 +567,21 @@ export const FeedPayloads = {
|
|
|
459
567
|
old: Version,
|
|
460
568
|
new: Version,
|
|
461
569
|
}),
|
|
570
|
+
// The scoring run moved the level of the agent's current version, up or
|
|
571
|
+
// down. The first move above none is a first_seal milestone instead.
|
|
572
|
+
level_change: z.strictObject({
|
|
573
|
+
...FeedAgent,
|
|
574
|
+
version: Version,
|
|
575
|
+
old: Level,
|
|
576
|
+
new: Level,
|
|
577
|
+
}),
|
|
578
|
+
// level is the current version's level when the milestone was passed.
|
|
579
|
+
milestone: z.strictObject({
|
|
580
|
+
...FeedAgent,
|
|
581
|
+
version: Version,
|
|
582
|
+
milestone: FeedMilestone,
|
|
583
|
+
level: Level,
|
|
584
|
+
}),
|
|
462
585
|
};
|
|
463
586
|
const feedItemOf = (kind) => z.strictObject({
|
|
464
587
|
// The SSE event id. Ascending in insert order.
|
|
@@ -478,6 +601,8 @@ export const FeedItem = z.discriminatedUnion('kind', [
|
|
|
478
601
|
feedItemOf('score_change'),
|
|
479
602
|
feedItemOf('flag'),
|
|
480
603
|
feedItemOf('version_change'),
|
|
604
|
+
feedItemOf('level_change'),
|
|
605
|
+
feedItemOf('milestone'),
|
|
481
606
|
]);
|
|
482
607
|
export const FEED_RECENT_MAX = 200;
|
|
483
608
|
export const FeedRecentQuery = z.strictObject({
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export declare const agentMilestones: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
2
|
+
name: "agent_milestones";
|
|
3
|
+
schema: undefined;
|
|
4
|
+
columns: {
|
|
5
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
6
|
+
name: "created_at";
|
|
7
|
+
tableName: "agent_milestones";
|
|
8
|
+
dataType: "date";
|
|
9
|
+
columnType: "PgTimestamp";
|
|
10
|
+
data: Date;
|
|
11
|
+
driverParam: string;
|
|
12
|
+
notNull: true;
|
|
13
|
+
hasDefault: true;
|
|
14
|
+
isPrimaryKey: false;
|
|
15
|
+
isAutoincrement: false;
|
|
16
|
+
hasRuntimeDefault: false;
|
|
17
|
+
enumValues: undefined;
|
|
18
|
+
baseColumn: never;
|
|
19
|
+
identity: undefined;
|
|
20
|
+
generated: undefined;
|
|
21
|
+
}, {}, {}>;
|
|
22
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
23
|
+
name: "updated_at";
|
|
24
|
+
tableName: "agent_milestones";
|
|
25
|
+
dataType: "date";
|
|
26
|
+
columnType: "PgTimestamp";
|
|
27
|
+
data: Date;
|
|
28
|
+
driverParam: string;
|
|
29
|
+
notNull: true;
|
|
30
|
+
hasDefault: true;
|
|
31
|
+
isPrimaryKey: false;
|
|
32
|
+
isAutoincrement: false;
|
|
33
|
+
hasRuntimeDefault: false;
|
|
34
|
+
enumValues: undefined;
|
|
35
|
+
baseColumn: never;
|
|
36
|
+
identity: undefined;
|
|
37
|
+
generated: undefined;
|
|
38
|
+
}, {}, {}>;
|
|
39
|
+
agentId: import("drizzle-orm/pg-core").PgColumn<{
|
|
40
|
+
name: "agent_id";
|
|
41
|
+
tableName: "agent_milestones";
|
|
42
|
+
dataType: "string";
|
|
43
|
+
columnType: "PgText";
|
|
44
|
+
data: string;
|
|
45
|
+
driverParam: string;
|
|
46
|
+
notNull: true;
|
|
47
|
+
hasDefault: false;
|
|
48
|
+
isPrimaryKey: false;
|
|
49
|
+
isAutoincrement: false;
|
|
50
|
+
hasRuntimeDefault: false;
|
|
51
|
+
enumValues: [string, ...string[]];
|
|
52
|
+
baseColumn: never;
|
|
53
|
+
identity: undefined;
|
|
54
|
+
generated: undefined;
|
|
55
|
+
}, {}, {}>;
|
|
56
|
+
milestone: import("drizzle-orm/pg-core").PgColumn<{
|
|
57
|
+
name: "milestone";
|
|
58
|
+
tableName: "agent_milestones";
|
|
59
|
+
dataType: "string";
|
|
60
|
+
columnType: "PgText";
|
|
61
|
+
data: string;
|
|
62
|
+
driverParam: string;
|
|
63
|
+
notNull: true;
|
|
64
|
+
hasDefault: false;
|
|
65
|
+
isPrimaryKey: false;
|
|
66
|
+
isAutoincrement: false;
|
|
67
|
+
hasRuntimeDefault: false;
|
|
68
|
+
enumValues: [string, ...string[]];
|
|
69
|
+
baseColumn: never;
|
|
70
|
+
identity: undefined;
|
|
71
|
+
generated: undefined;
|
|
72
|
+
}, {}, {}>;
|
|
73
|
+
};
|
|
74
|
+
dialect: 'pg';
|
|
75
|
+
}>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { pgTable, primaryKey, text } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { agents } from './agents.js';
|
|
3
|
+
import { timestamps } from './timestamps.js';
|
|
4
|
+
// The feed milestones an agent has reached, one row per (agent, milestone).
|
|
5
|
+
// The scoring run writes a milestone to the feed only when it has no row
|
|
6
|
+
// here, so each is written once per agent, ever. Migration 0019 seeded it
|
|
7
|
+
// from standing, so an agent that already stood above none, or already
|
|
8
|
+
// passed a verified task count, before milestones existed is not told it
|
|
9
|
+
// did so for the first time.
|
|
10
|
+
export const agentMilestones = pgTable('agent_milestones', {
|
|
11
|
+
agentId: text('agent_id')
|
|
12
|
+
.notNull()
|
|
13
|
+
.references(() => agents.id),
|
|
14
|
+
milestone: text('milestone').notNull(),
|
|
15
|
+
...timestamps,
|
|
16
|
+
}, (t) => [primaryKey({ columns: [t.agentId, t.milestone] })]);
|
package/dist/db/agents.d.ts
CHANGED
|
@@ -138,6 +138,57 @@ export declare const agents: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
138
138
|
identity: undefined;
|
|
139
139
|
generated: undefined;
|
|
140
140
|
}, {}, {}>;
|
|
141
|
+
dirtySince: import("drizzle-orm/pg-core").PgColumn<{
|
|
142
|
+
name: "dirty_since";
|
|
143
|
+
tableName: "agents";
|
|
144
|
+
dataType: "date";
|
|
145
|
+
columnType: "PgTimestamp";
|
|
146
|
+
data: Date;
|
|
147
|
+
driverParam: string;
|
|
148
|
+
notNull: false;
|
|
149
|
+
hasDefault: true;
|
|
150
|
+
isPrimaryKey: false;
|
|
151
|
+
isAutoincrement: false;
|
|
152
|
+
hasRuntimeDefault: false;
|
|
153
|
+
enumValues: undefined;
|
|
154
|
+
baseColumn: never;
|
|
155
|
+
identity: undefined;
|
|
156
|
+
generated: undefined;
|
|
157
|
+
}, {}, {}>;
|
|
158
|
+
dirtyGen: import("drizzle-orm/pg-core").PgColumn<{
|
|
159
|
+
name: "dirty_gen";
|
|
160
|
+
tableName: "agents";
|
|
161
|
+
dataType: "number";
|
|
162
|
+
columnType: "PgBigInt53";
|
|
163
|
+
data: number;
|
|
164
|
+
driverParam: string | number;
|
|
165
|
+
notNull: true;
|
|
166
|
+
hasDefault: true;
|
|
167
|
+
isPrimaryKey: false;
|
|
168
|
+
isAutoincrement: false;
|
|
169
|
+
hasRuntimeDefault: false;
|
|
170
|
+
enumValues: undefined;
|
|
171
|
+
baseColumn: never;
|
|
172
|
+
identity: undefined;
|
|
173
|
+
generated: undefined;
|
|
174
|
+
}, {}, {}>;
|
|
175
|
+
recomputeAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
176
|
+
name: "recompute_at";
|
|
177
|
+
tableName: "agents";
|
|
178
|
+
dataType: "date";
|
|
179
|
+
columnType: "PgTimestamp";
|
|
180
|
+
data: Date;
|
|
181
|
+
driverParam: string;
|
|
182
|
+
notNull: false;
|
|
183
|
+
hasDefault: false;
|
|
184
|
+
isPrimaryKey: false;
|
|
185
|
+
isAutoincrement: false;
|
|
186
|
+
hasRuntimeDefault: false;
|
|
187
|
+
enumValues: undefined;
|
|
188
|
+
baseColumn: never;
|
|
189
|
+
identity: undefined;
|
|
190
|
+
generated: undefined;
|
|
191
|
+
}, {}, {}>;
|
|
141
192
|
};
|
|
142
193
|
dialect: 'pg';
|
|
143
194
|
}>;
|
package/dist/db/agents.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sql } from 'drizzle-orm';
|
|
2
|
-
import { check, jsonb, pgTable, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { bigint, check, index, jsonb, pgTable, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
|
|
3
3
|
import { operators } from './operators.js';
|
|
4
4
|
import { timestamps } from './timestamps.js';
|
|
5
5
|
// How a Drizzle table is declared.
|
|
@@ -31,10 +31,31 @@ export const agents = pgTable('agents', {
|
|
|
31
31
|
// sent again cannot move the version back.
|
|
32
32
|
versionIssuedAt: timestamp('version_issued_at', { withTimezone: true }),
|
|
33
33
|
card: jsonb('card'),
|
|
34
|
+
// When the agent was first marked dirty since the scoring run last
|
|
35
|
+
// cleared it. Every write that can move the agent's standing marks it,
|
|
36
|
+
// in the same transaction as that write, but a mark sets this only when
|
|
37
|
+
// it is null, so an agent that keeps being marked keeps its place in the
|
|
38
|
+
// run's queue. defaultNow, so a new agent starts dirty and migration
|
|
39
|
+
// 0018 backfilled every existing agent. Not a row timestamp, the scoring
|
|
40
|
+
// run owns it.
|
|
41
|
+
dirtySince: timestamp('dirty_since', { withTimezone: true }).defaultNow(),
|
|
42
|
+
// Bumped by every mark. The run reads it when it selects the agent and
|
|
43
|
+
// clears dirty_since only where it is unchanged, so a mark that commits
|
|
44
|
+
// mid run survives (migration 0019).
|
|
45
|
+
dirtyGen: bigint('dirty_gen', { mode: 'number' }).notNull().default(0),
|
|
46
|
+
// The earliest future moment time alone can change this agent's
|
|
47
|
+
// standing, a dormancy rung or evidence leaving the window. Written by
|
|
48
|
+
// the scoring run only, null when nothing is due.
|
|
49
|
+
recomputeAt: timestamp('recompute_at', { withTimezone: true }),
|
|
34
50
|
...timestamps,
|
|
35
51
|
}, (t) => [
|
|
36
52
|
check('agents_id_length', sql `length(${t.id}) = 43`),
|
|
37
53
|
// The handle login/name points at one agent. Names are lowercase, so a
|
|
38
54
|
// plain unique index is enough.
|
|
39
55
|
uniqueIndex('agents_operator_name_unique').on(t.operatorId, t.name),
|
|
56
|
+
// The scoring run selects agents that are dirty or due.
|
|
57
|
+
index('agents_recompute_at_idx').on(t.recomputeAt),
|
|
58
|
+
index('agents_dirty_since_idx')
|
|
59
|
+
.on(t.dirtySince)
|
|
60
|
+
.where(sql `${t.dirtySince} is not null`),
|
|
40
61
|
]);
|
package/dist/db/client.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import postgres from 'postgres';
|
|
2
|
-
export
|
|
2
|
+
export type DbOptions = {
|
|
3
|
+
max?: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function createDb(url: string, options?: DbOptions): import("drizzle-orm/postgres-js").PostgresJsDatabase<{
|
|
3
6
|
operators: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
4
7
|
name: "operators";
|
|
5
8
|
schema: undefined;
|
|
@@ -249,6 +252,57 @@ export declare function createDb(url: string): import("drizzle-orm/postgres-js")
|
|
|
249
252
|
identity: undefined;
|
|
250
253
|
generated: undefined;
|
|
251
254
|
}, {}, {}>;
|
|
255
|
+
dirtySince: import("drizzle-orm/pg-core").PgColumn<{
|
|
256
|
+
name: "dirty_since";
|
|
257
|
+
tableName: "agents";
|
|
258
|
+
dataType: "date";
|
|
259
|
+
columnType: "PgTimestamp";
|
|
260
|
+
data: Date;
|
|
261
|
+
driverParam: string;
|
|
262
|
+
notNull: false;
|
|
263
|
+
hasDefault: true;
|
|
264
|
+
isPrimaryKey: false;
|
|
265
|
+
isAutoincrement: false;
|
|
266
|
+
hasRuntimeDefault: false;
|
|
267
|
+
enumValues: undefined;
|
|
268
|
+
baseColumn: never;
|
|
269
|
+
identity: undefined;
|
|
270
|
+
generated: undefined;
|
|
271
|
+
}, {}, {}>;
|
|
272
|
+
dirtyGen: import("drizzle-orm/pg-core").PgColumn<{
|
|
273
|
+
name: "dirty_gen";
|
|
274
|
+
tableName: "agents";
|
|
275
|
+
dataType: "number";
|
|
276
|
+
columnType: "PgBigInt53";
|
|
277
|
+
data: number;
|
|
278
|
+
driverParam: string | number;
|
|
279
|
+
notNull: true;
|
|
280
|
+
hasDefault: true;
|
|
281
|
+
isPrimaryKey: false;
|
|
282
|
+
isAutoincrement: false;
|
|
283
|
+
hasRuntimeDefault: false;
|
|
284
|
+
enumValues: undefined;
|
|
285
|
+
baseColumn: never;
|
|
286
|
+
identity: undefined;
|
|
287
|
+
generated: undefined;
|
|
288
|
+
}, {}, {}>;
|
|
289
|
+
recomputeAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
290
|
+
name: "recompute_at";
|
|
291
|
+
tableName: "agents";
|
|
292
|
+
dataType: "date";
|
|
293
|
+
columnType: "PgTimestamp";
|
|
294
|
+
data: Date;
|
|
295
|
+
driverParam: string;
|
|
296
|
+
notNull: false;
|
|
297
|
+
hasDefault: false;
|
|
298
|
+
isPrimaryKey: false;
|
|
299
|
+
isAutoincrement: false;
|
|
300
|
+
hasRuntimeDefault: false;
|
|
301
|
+
enumValues: undefined;
|
|
302
|
+
baseColumn: never;
|
|
303
|
+
identity: undefined;
|
|
304
|
+
generated: undefined;
|
|
305
|
+
}, {}, {}>;
|
|
252
306
|
};
|
|
253
307
|
dialect: 'pg';
|
|
254
308
|
}>;
|
|
@@ -1811,6 +1865,81 @@ export declare function createDb(url: string): import("drizzle-orm/postgres-js")
|
|
|
1811
1865
|
};
|
|
1812
1866
|
dialect: 'pg';
|
|
1813
1867
|
}>;
|
|
1868
|
+
agentMilestones: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1869
|
+
name: "agent_milestones";
|
|
1870
|
+
schema: undefined;
|
|
1871
|
+
columns: {
|
|
1872
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1873
|
+
name: "created_at";
|
|
1874
|
+
tableName: "agent_milestones";
|
|
1875
|
+
dataType: "date";
|
|
1876
|
+
columnType: "PgTimestamp";
|
|
1877
|
+
data: Date;
|
|
1878
|
+
driverParam: string;
|
|
1879
|
+
notNull: true;
|
|
1880
|
+
hasDefault: true;
|
|
1881
|
+
isPrimaryKey: false;
|
|
1882
|
+
isAutoincrement: false;
|
|
1883
|
+
hasRuntimeDefault: false;
|
|
1884
|
+
enumValues: undefined;
|
|
1885
|
+
baseColumn: never;
|
|
1886
|
+
identity: undefined;
|
|
1887
|
+
generated: undefined;
|
|
1888
|
+
}, {}, {}>;
|
|
1889
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1890
|
+
name: "updated_at";
|
|
1891
|
+
tableName: "agent_milestones";
|
|
1892
|
+
dataType: "date";
|
|
1893
|
+
columnType: "PgTimestamp";
|
|
1894
|
+
data: Date;
|
|
1895
|
+
driverParam: string;
|
|
1896
|
+
notNull: true;
|
|
1897
|
+
hasDefault: true;
|
|
1898
|
+
isPrimaryKey: false;
|
|
1899
|
+
isAutoincrement: false;
|
|
1900
|
+
hasRuntimeDefault: false;
|
|
1901
|
+
enumValues: undefined;
|
|
1902
|
+
baseColumn: never;
|
|
1903
|
+
identity: undefined;
|
|
1904
|
+
generated: undefined;
|
|
1905
|
+
}, {}, {}>;
|
|
1906
|
+
agentId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1907
|
+
name: "agent_id";
|
|
1908
|
+
tableName: "agent_milestones";
|
|
1909
|
+
dataType: "string";
|
|
1910
|
+
columnType: "PgText";
|
|
1911
|
+
data: string;
|
|
1912
|
+
driverParam: string;
|
|
1913
|
+
notNull: true;
|
|
1914
|
+
hasDefault: false;
|
|
1915
|
+
isPrimaryKey: false;
|
|
1916
|
+
isAutoincrement: false;
|
|
1917
|
+
hasRuntimeDefault: false;
|
|
1918
|
+
enumValues: [string, ...string[]];
|
|
1919
|
+
baseColumn: never;
|
|
1920
|
+
identity: undefined;
|
|
1921
|
+
generated: undefined;
|
|
1922
|
+
}, {}, {}>;
|
|
1923
|
+
milestone: import("drizzle-orm/pg-core").PgColumn<{
|
|
1924
|
+
name: "milestone";
|
|
1925
|
+
tableName: "agent_milestones";
|
|
1926
|
+
dataType: "string";
|
|
1927
|
+
columnType: "PgText";
|
|
1928
|
+
data: string;
|
|
1929
|
+
driverParam: string;
|
|
1930
|
+
notNull: true;
|
|
1931
|
+
hasDefault: false;
|
|
1932
|
+
isPrimaryKey: false;
|
|
1933
|
+
isAutoincrement: false;
|
|
1934
|
+
hasRuntimeDefault: false;
|
|
1935
|
+
enumValues: [string, ...string[]];
|
|
1936
|
+
baseColumn: never;
|
|
1937
|
+
identity: undefined;
|
|
1938
|
+
generated: undefined;
|
|
1939
|
+
}, {}, {}>;
|
|
1940
|
+
};
|
|
1941
|
+
dialect: 'pg';
|
|
1942
|
+
}>;
|
|
1814
1943
|
deletedOperators: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1815
1944
|
name: "deleted_operators";
|
|
1816
1945
|
schema: undefined;
|
|
@@ -1887,7 +2016,7 @@ export declare function createDb(url: string): import("drizzle-orm/postgres-js")
|
|
|
1887
2016
|
dialect: 'pg';
|
|
1888
2017
|
}>;
|
|
1889
2018
|
}> & {
|
|
1890
|
-
$client: postgres.Sql<
|
|
2019
|
+
$client: postgres.Sql<{}>;
|
|
1891
2020
|
};
|
|
1892
2021
|
export type Db = ReturnType<typeof createDb>;
|
|
1893
2022
|
export type Tx = Parameters<Parameters<Db['transaction']>[0]>[0];
|
package/dist/db/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
2
2
|
import postgres from 'postgres';
|
|
3
|
+
import { agentMilestones } from './agent-milestones.js';
|
|
3
4
|
import { agentRenames } from './agent-renames.js';
|
|
4
5
|
import { agents } from './agents.js';
|
|
5
6
|
import { credentials } from './credentials.js';
|
|
@@ -27,8 +28,11 @@ const schema = {
|
|
|
27
28
|
quotaCounters,
|
|
28
29
|
feedItems,
|
|
29
30
|
agentRenames,
|
|
31
|
+
agentMilestones,
|
|
30
32
|
deletedOperators,
|
|
31
33
|
};
|
|
32
|
-
export function createDb(url) {
|
|
33
|
-
|
|
34
|
+
export function createDb(url, options = {}) {
|
|
35
|
+
const [conn, pgOptions] = pgArgs(url);
|
|
36
|
+
const max = options.max === undefined ? {} : { max: options.max };
|
|
37
|
+
return drizzle(postgres(conn, { ...pgOptions, ...max }), { schema });
|
|
34
38
|
}
|
package/dist/db/feed-items.js
CHANGED
|
@@ -13,4 +13,9 @@ export const feedItems = pgTable('feed_items', {
|
|
|
13
13
|
agentId: text('agent_id').references(() => agents.id),
|
|
14
14
|
payload: jsonb('payload').notNull(),
|
|
15
15
|
...timestamps,
|
|
16
|
-
}, (t) => [
|
|
16
|
+
}, (t) => [
|
|
17
|
+
index('feed_items_created_at_idx').on(t.createdAt),
|
|
18
|
+
// Milestones are written once per agent, and deleting an agent removes
|
|
19
|
+
// its items. Both look up by agent and kind.
|
|
20
|
+
index('feed_items_agent_id_kind_idx').on(t.agentId, t.kind),
|
|
21
|
+
]);
|
package/dist/db/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { agentMilestones } from './agent-milestones.js';
|
|
1
2
|
import type { agentRenames } from './agent-renames.js';
|
|
2
3
|
import type { credentials } from './credentials.js';
|
|
3
4
|
import type { deletedOperators } from './deleted-operators.js';
|
|
@@ -10,12 +11,13 @@ import type { scores } from './scores.js';
|
|
|
10
11
|
import type { standing } from './standing.js';
|
|
11
12
|
import type { taskOutcomes } from './task-outcomes.js';
|
|
12
13
|
import type { tasks } from './tasks.js';
|
|
13
|
-
export { and, asc, desc, eq, gt, inArray, isNotNull, isNull, lt, lte, max, ne, notInArray, or, sql, } from 'drizzle-orm';
|
|
14
|
+
export { and, asc, desc, eq, gt, inArray, isNotNull, isNull, lt, lte, max, ne, notInArray, or, type SQL, sql, } from 'drizzle-orm';
|
|
14
15
|
export { DrizzleQueryError } from 'drizzle-orm/errors';
|
|
16
|
+
export { agentMilestones } from './agent-milestones.js';
|
|
15
17
|
export { agentRenames } from './agent-renames.js';
|
|
16
18
|
export type { Agent, NewAgent } from './agents.js';
|
|
17
19
|
export { agents } from './agents.js';
|
|
18
|
-
export { createDb, type Db, type Tx } from './client.js';
|
|
20
|
+
export { createDb, type Db, type DbOptions, type Tx } from './client.js';
|
|
19
21
|
export { credentials } from './credentials.js';
|
|
20
22
|
export { deletedOperators } from './deleted-operators.js';
|
|
21
23
|
export { events } from './events.js';
|
|
@@ -52,5 +54,7 @@ export type AgentRename = typeof agentRenames.$inferSelect;
|
|
|
52
54
|
export type NewAgentRename = typeof agentRenames.$inferInsert;
|
|
53
55
|
export type FeedItem = typeof feedItems.$inferSelect;
|
|
54
56
|
export type NewFeedItem = typeof feedItems.$inferInsert;
|
|
57
|
+
export type AgentMilestone = typeof agentMilestones.$inferSelect;
|
|
58
|
+
export type NewAgentMilestone = typeof agentMilestones.$inferInsert;
|
|
55
59
|
export type DeletedOperator = typeof deletedOperators.$inferSelect;
|
|
56
60
|
export type NewDeletedOperator = typeof deletedOperators.$inferInsert;
|
package/dist/db/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { and, asc, desc, eq, gt, inArray, isNotNull, isNull, lt, lte, max, ne, n
|
|
|
4
4
|
// What every failed query throws. Its message holds the SQL and every bound
|
|
5
5
|
// param, so the API error log must never print it.
|
|
6
6
|
export { DrizzleQueryError } from 'drizzle-orm/errors';
|
|
7
|
+
export { agentMilestones } from './agent-milestones.js';
|
|
7
8
|
export { agentRenames } from './agent-renames.js';
|
|
8
9
|
export { agents } from './agents.js';
|
|
9
10
|
export { createDb } from './client.js';
|
package/dist/envelope.d.ts
CHANGED
|
@@ -14,3 +14,14 @@ export declare function verify(jws: string, publicKey: Uint8Array): Promise<{
|
|
|
14
14
|
header: JwsHeader;
|
|
15
15
|
payload: unknown;
|
|
16
16
|
}>;
|
|
17
|
+
export declare const Audience: z.ZodString;
|
|
18
|
+
export declare function audienceOf(apiUrl: string): string;
|
|
19
|
+
export declare function withAudience<T extends object>(payload: T, aud: string): T & {
|
|
20
|
+
aud: string;
|
|
21
|
+
};
|
|
22
|
+
export declare function signRequest(payload: object, privateKey: Uint8Array, kid: string, aud: string): Promise<string>;
|
|
23
|
+
export type AudienceCheck = {
|
|
24
|
+
result: 'match' | 'missing' | 'wrong';
|
|
25
|
+
payload: unknown;
|
|
26
|
+
};
|
|
27
|
+
export declare function readAudience(payload: unknown, accepted: readonly string[]): AudienceCheck;
|
package/dist/envelope.js
CHANGED
|
@@ -68,3 +68,73 @@ export async function verify(jws, publicKey) {
|
|
|
68
68
|
const header = parseHeader(parts.header);
|
|
69
69
|
return { header, payload: parseJson(parts.payload, 'payload') };
|
|
70
70
|
}
|
|
71
|
+
// Signed request audience (VOU-111). Every agent-signed request names the
|
|
72
|
+
// API it is meant for with `aud`, a top level key inside the signed payload,
|
|
73
|
+
// so a request captured for one API cannot be replayed to another. SEALs
|
|
74
|
+
// carry `iss` instead and do not use this.
|
|
75
|
+
function isHttpOrigin(v) {
|
|
76
|
+
try {
|
|
77
|
+
const url = new URL(v);
|
|
78
|
+
return ((url.protocol === 'http:' || url.protocol === 'https:') &&
|
|
79
|
+
url.origin === v);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// An origin with no trailing slash, no path, a lowercase host and no default
|
|
86
|
+
// port, such as https://api.sealkeeper.run.
|
|
87
|
+
export const Audience = z
|
|
88
|
+
.string()
|
|
89
|
+
.max(256)
|
|
90
|
+
.refine(isHttpOrigin, 'aud must be an origin such as https://api.sealkeeper.run');
|
|
91
|
+
// The audience for an API base URL, its origin.
|
|
92
|
+
export function audienceOf(apiUrl) {
|
|
93
|
+
let url;
|
|
94
|
+
try {
|
|
95
|
+
url = new URL(apiUrl);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new EnvelopeError('API URL is not a URL');
|
|
99
|
+
}
|
|
100
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
101
|
+
throw new EnvelopeError('API URL must be http or https');
|
|
102
|
+
}
|
|
103
|
+
if (url.origin === 'null') {
|
|
104
|
+
throw new EnvelopeError('API URL has no origin');
|
|
105
|
+
}
|
|
106
|
+
return url.origin;
|
|
107
|
+
}
|
|
108
|
+
// A copy of the payload with `aud` added. Never mutates the input.
|
|
109
|
+
export function withAudience(payload, aud) {
|
|
110
|
+
if (!Audience.safeParse(aud).success) {
|
|
111
|
+
throw new EnvelopeError('aud must be an origin');
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(payload)) {
|
|
114
|
+
throw new EnvelopeError('A signed request payload must be an object');
|
|
115
|
+
}
|
|
116
|
+
if (Object.hasOwn(payload, 'aud')) {
|
|
117
|
+
throw new EnvelopeError('Payload already has an aud');
|
|
118
|
+
}
|
|
119
|
+
return { ...payload, aud };
|
|
120
|
+
}
|
|
121
|
+
// How every agent-signed request is signed. The payload gains `aud` inside
|
|
122
|
+
// the signed bytes. `sign` stays for SEALs, which carry `iss`.
|
|
123
|
+
export async function signRequest(payload, privateKey, kid, aud) {
|
|
124
|
+
return sign(withAudience(payload, aud), privateKey, kid);
|
|
125
|
+
}
|
|
126
|
+
// Read `aud` off a verified payload. Call only after `verify`. The compare
|
|
127
|
+
// is exact, the received value is never normalised. A payload that is not a
|
|
128
|
+
// plain object comes back unchanged as missing, so the request schema still
|
|
129
|
+
// rejects it.
|
|
130
|
+
export function readAudience(payload, accepted) {
|
|
131
|
+
if (typeof payload !== 'object' ||
|
|
132
|
+
payload === null ||
|
|
133
|
+
Array.isArray(payload) ||
|
|
134
|
+
!Object.hasOwn(payload, 'aud')) {
|
|
135
|
+
return { result: 'missing', payload };
|
|
136
|
+
}
|
|
137
|
+
const { aud, ...rest } = payload;
|
|
138
|
+
const match = typeof aud === 'string' && accepted.includes(aud);
|
|
139
|
+
return { result: match ? 'match' : 'wrong', payload: rest };
|
|
140
|
+
}
|
package/dist/tasks.js
CHANGED
|
@@ -72,9 +72,39 @@ function redactSubschema(node) {
|
|
|
72
72
|
return node;
|
|
73
73
|
return isJsonObject(node) ? redactJsonSchema(node) : {};
|
|
74
74
|
}
|
|
75
|
+
const BOUNDS = [
|
|
76
|
+
'minimum',
|
|
77
|
+
'maximum',
|
|
78
|
+
'exclusiveMinimum',
|
|
79
|
+
'exclusiveMaximum',
|
|
80
|
+
];
|
|
81
|
+
const finite = (v) => typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
82
|
+
// True when the numeric bounds leave room for one value only, such as
|
|
83
|
+
// minimum 42 with maximum 42, or an integer above 41 and below 43. Those
|
|
84
|
+
// pin the answer as surely as const, so the redacted copy drops all four
|
|
85
|
+
// bounds (VOU-107). Not exported, so the schema package's exports stay
|
|
86
|
+
// the ones on npm.
|
|
87
|
+
function boundsPinOneValue(schema) {
|
|
88
|
+
const [min, max, xmin, xmax] = BOUNDS.map((k) => finite(schema[k]));
|
|
89
|
+
if (min !== undefined && min === max)
|
|
90
|
+
return true;
|
|
91
|
+
// Null never meets a numeric bound, so a nullable integer leaks the
|
|
92
|
+
// same one value as a plain integer.
|
|
93
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
94
|
+
const numeric = types.filter((t) => t !== 'null');
|
|
95
|
+
if (numeric.length === 0 || !numeric.every((t) => t === 'integer')) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const lo = Math.max(min === undefined ? -Infinity : Math.ceil(min), xmin === undefined ? -Infinity : Math.floor(xmin) + 1);
|
|
99
|
+
const hi = Math.min(max === undefined ? Infinity : Math.floor(max), xmax === undefined ? Infinity : Math.ceil(xmax) - 1);
|
|
100
|
+
return lo === hi;
|
|
101
|
+
}
|
|
75
102
|
export function redactJsonSchema(schema) {
|
|
103
|
+
const pinned = boundsPinOneValue(schema);
|
|
76
104
|
const out = {};
|
|
77
105
|
for (const [key, value] of Object.entries(schema)) {
|
|
106
|
+
if (pinned && BOUNDS.includes(key))
|
|
107
|
+
continue;
|
|
78
108
|
if (SHAPE_DATA.has(key)) {
|
|
79
109
|
out[key] = Array.isArray(value) ? [...value] : value;
|
|
80
110
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealkeeper/schema",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "Zod schemas, event taxonomy and signing helpers for
|
|
5
|
+
"description": "Zod schemas, event taxonomy and signing helpers for SealKeeper.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"homepage": "https://sealkeeper.run/seal/standard",
|
|
8
8
|
"repository": {
|