@davesheffer/hunch 1.32.8 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/cli/index.js +2 -0
- package/dist/cli/serve.js +28 -2
- package/dist/cli/state.d.ts +3 -0
- package/dist/cli/state.js +150 -0
- package/dist/client/state.d.ts +82 -14
- package/dist/client/state.js +16 -2
- package/dist/client/stateProof.d.ts +4 -0
- package/dist/client/stateProof.js +17 -0
- package/dist/constitution/behaviorEvaluator.js +1 -1
- package/dist/constitution/schema.d.ts +2 -2
- package/dist/core/automaticReviewMemory.d.ts +5 -0
- package/dist/core/conventionDelivery.d.ts +8 -0
- package/dist/core/conventionDelivery.js +52 -0
- package/dist/core/fieldProvenance.d.ts +8 -0
- package/dist/core/fieldProvenance.js +72 -0
- package/dist/core/recordVisibility.d.ts +9 -0
- package/dist/core/recordVisibility.js +25 -0
- package/dist/core/stateCanonical.d.ts +3 -0
- package/dist/core/stateCanonical.js +34 -0
- package/dist/core/stateContract.d.ts +122 -7
- package/dist/core/stateContract.js +26 -31
- package/dist/core/stateDelivery.d.ts +3 -3
- package/dist/core/stateDelivery.js +10 -1
- package/dist/core/stateHttp.d.ts +280 -0
- package/dist/core/stateHttp.js +17 -0
- package/dist/core/stateProof.d.ts +13 -0
- package/dist/core/stateProof.js +34 -0
- package/dist/core/stateRecords.d.ts +127 -0
- package/dist/core/stateRecords.js +48 -0
- package/dist/core/types.d.ts +146 -4
- package/dist/core/types.js +8 -2
- package/dist/extractors/git.js +3 -10
- package/dist/mcp/server.js +10 -4
- package/dist/serve/app.d.ts +2 -0
- package/dist/serve/app.js +71 -30
- package/dist/serve/config.d.ts +16 -0
- package/dist/serve/config.js +27 -7
- package/dist/serve/operator.d.ts +4 -0
- package/dist/serve/operator.js +223 -0
- package/dist/serve/stateProof.d.ts +15 -0
- package/dist/serve/stateProof.js +105 -0
- package/dist/store/changeLedger.d.ts +6 -0
- package/dist/store/hunchStore.d.ts +4 -2
- package/dist/store/hunchStore.js +18 -19
- package/dist/store/stateAccess.d.ts +13 -0
- package/dist/store/stateAccess.js +85 -0
- package/dist/store/stateBinding.d.ts +13 -18
- package/dist/store/stateBinding.js +161 -52
- package/dist/store/stateCapture.js +10 -2
- package/dist/store/stateError.d.ts +12 -0
- package/dist/store/stateError.js +12 -0
- package/dist/store/statePartition.d.ts +9 -0
- package/dist/store/statePartition.js +30 -0
- package/package.json +5 -1
- package/server.json +2 -2
package/dist/core/types.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { ProvenanceSchema, isCredentialFreeText, type Provenance } from "./provenance.js";
|
|
10
|
-
import { type ActionReceipt, type Commitment, type DerivedState, type ExternalEntity, type StateRelationship } from "./stateRecords.js";
|
|
10
|
+
import { type Convention, type ActionReceipt, type Commitment, type DerivedState, type ExternalEntity, type StateRelationship } from "./stateRecords.js";
|
|
11
11
|
export { ProvenanceSchema, isCredentialFreeText };
|
|
12
12
|
export type { Provenance };
|
|
13
13
|
export declare const ComponentKind: z.ZodEnum<{
|
|
@@ -335,6 +335,11 @@ export declare const PremiseSchema: z.ZodObject<{
|
|
|
335
335
|
}, z.core.$strip>;
|
|
336
336
|
export type Premise = z.infer<typeof PremiseSchema>;
|
|
337
337
|
export declare const DecisionSchema: z.ZodObject<{
|
|
338
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
339
|
+
owner: z.ZodString;
|
|
340
|
+
readers: z.ZodArray<z.ZodString>;
|
|
341
|
+
writers: z.ZodArray<z.ZodString>;
|
|
342
|
+
}, z.core.$strict>>;
|
|
338
343
|
id: z.ZodString;
|
|
339
344
|
title: z.ZodString;
|
|
340
345
|
topic: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
@@ -416,6 +421,11 @@ export declare const BugLineageSchema: z.ZodObject<{
|
|
|
416
421
|
export type BugLineage = z.infer<typeof BugLineageSchema>;
|
|
417
422
|
/** A bug with root cause and lineage (introduced → fixed → recurred). */
|
|
418
423
|
export declare const BugSchema: z.ZodObject<{
|
|
424
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
425
|
+
owner: z.ZodString;
|
|
426
|
+
readers: z.ZodArray<z.ZodString>;
|
|
427
|
+
writers: z.ZodArray<z.ZodString>;
|
|
428
|
+
}, z.core.$strict>>;
|
|
419
429
|
id: z.ZodString;
|
|
420
430
|
title: z.ZodString;
|
|
421
431
|
symptom: z.ZodDefault<z.ZodString>;
|
|
@@ -452,6 +462,11 @@ export declare const BugSchema: z.ZodObject<{
|
|
|
452
462
|
export type Bug = z.infer<typeof BugSchema>;
|
|
453
463
|
/** An invariant the system must respect. */
|
|
454
464
|
export declare const ConstraintSchema: z.ZodObject<{
|
|
465
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
466
|
+
owner: z.ZodString;
|
|
467
|
+
readers: z.ZodArray<z.ZodString>;
|
|
468
|
+
writers: z.ZodArray<z.ZodString>;
|
|
469
|
+
}, z.core.$strict>>;
|
|
455
470
|
id: z.ZodString;
|
|
456
471
|
type: z.ZodDefault<z.ZodEnum<{
|
|
457
472
|
security: "security";
|
|
@@ -526,6 +541,11 @@ export type Runbook = z.infer<typeof RunbookSchema>;
|
|
|
526
541
|
* never enters any block path. Lifecycle is `triage`, not valid-time: a finding is
|
|
527
542
|
* resolved/stale-marked, never superseded. */
|
|
528
543
|
export declare const FindingSchema: z.ZodObject<{
|
|
544
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
545
|
+
owner: z.ZodString;
|
|
546
|
+
readers: z.ZodArray<z.ZodString>;
|
|
547
|
+
writers: z.ZodArray<z.ZodString>;
|
|
548
|
+
}, z.core.$strict>>;
|
|
529
549
|
id: z.ZodString;
|
|
530
550
|
title: z.ZodString;
|
|
531
551
|
observation: z.ZodDefault<z.ZodString>;
|
|
@@ -538,8 +558,8 @@ export declare const FindingSchema: z.ZodObject<{
|
|
|
538
558
|
critical: "critical";
|
|
539
559
|
}>>;
|
|
540
560
|
triage: z.ZodDefault<z.ZodEnum<{
|
|
541
|
-
open: "open";
|
|
542
561
|
stale: "stale";
|
|
562
|
+
open: "open";
|
|
543
563
|
"accepted-risk": "accepted-risk";
|
|
544
564
|
scheduled: "scheduled";
|
|
545
565
|
resolved: "resolved";
|
|
@@ -590,7 +610,7 @@ export declare function assertLandscapeDriftCandidate(value: unknown): asserts v
|
|
|
590
610
|
/** Convert one valid external observation into advisory Hunch memory, never graph authority. */
|
|
591
611
|
export declare function landscapeDriftCandidateFinding(value: unknown): Finding;
|
|
592
612
|
/** The entity collections, keyed by their on-disk directory name. */
|
|
593
|
-
export declare const ENTITY_KINDS: readonly ["components", "resources", "edges", "symbols", "decisions", "bugs", "constraints", "runbooks", "findings", "receipts", "commitments", "derived", "entities", "relationships"];
|
|
613
|
+
export declare const ENTITY_KINDS: readonly ["components", "resources", "edges", "symbols", "decisions", "bugs", "constraints", "runbooks", "findings", "receipts", "commitments", "derived", "entities", "relationships", "conventions"];
|
|
594
614
|
export type EntityKind = (typeof ENTITY_KINDS)[number];
|
|
595
615
|
export declare const SCHEMAS: {
|
|
596
616
|
readonly components: z.ZodObject<{
|
|
@@ -753,6 +773,11 @@ export declare const SCHEMAS: {
|
|
|
753
773
|
last_changed: z.ZodDefault<z.ZodString>;
|
|
754
774
|
}, z.core.$strip>;
|
|
755
775
|
readonly decisions: z.ZodObject<{
|
|
776
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
777
|
+
owner: z.ZodString;
|
|
778
|
+
readers: z.ZodArray<z.ZodString>;
|
|
779
|
+
writers: z.ZodArray<z.ZodString>;
|
|
780
|
+
}, z.core.$strict>>;
|
|
756
781
|
id: z.ZodString;
|
|
757
782
|
title: z.ZodString;
|
|
758
783
|
topic: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
@@ -823,6 +848,11 @@ export declare const SCHEMAS: {
|
|
|
823
848
|
date: z.ZodString;
|
|
824
849
|
}, z.core.$strip>;
|
|
825
850
|
readonly bugs: z.ZodObject<{
|
|
851
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
852
|
+
owner: z.ZodString;
|
|
853
|
+
readers: z.ZodArray<z.ZodString>;
|
|
854
|
+
writers: z.ZodArray<z.ZodString>;
|
|
855
|
+
}, z.core.$strict>>;
|
|
826
856
|
id: z.ZodString;
|
|
827
857
|
title: z.ZodString;
|
|
828
858
|
symptom: z.ZodDefault<z.ZodString>;
|
|
@@ -857,6 +887,11 @@ export declare const SCHEMAS: {
|
|
|
857
887
|
}, z.core.$strip>;
|
|
858
888
|
}, z.core.$strip>;
|
|
859
889
|
readonly constraints: z.ZodObject<{
|
|
890
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
891
|
+
owner: z.ZodString;
|
|
892
|
+
readers: z.ZodArray<z.ZodString>;
|
|
893
|
+
writers: z.ZodArray<z.ZodString>;
|
|
894
|
+
}, z.core.$strict>>;
|
|
860
895
|
id: z.ZodString;
|
|
861
896
|
type: z.ZodDefault<z.ZodEnum<{
|
|
862
897
|
security: "security";
|
|
@@ -919,6 +954,11 @@ export declare const SCHEMAS: {
|
|
|
919
954
|
date: z.ZodString;
|
|
920
955
|
}, z.core.$strip>;
|
|
921
956
|
readonly findings: z.ZodObject<{
|
|
957
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
958
|
+
owner: z.ZodString;
|
|
959
|
+
readers: z.ZodArray<z.ZodString>;
|
|
960
|
+
writers: z.ZodArray<z.ZodString>;
|
|
961
|
+
}, z.core.$strict>>;
|
|
922
962
|
id: z.ZodString;
|
|
923
963
|
title: z.ZodString;
|
|
924
964
|
observation: z.ZodDefault<z.ZodString>;
|
|
@@ -931,8 +971,8 @@ export declare const SCHEMAS: {
|
|
|
931
971
|
critical: "critical";
|
|
932
972
|
}>>;
|
|
933
973
|
triage: z.ZodDefault<z.ZodEnum<{
|
|
934
|
-
open: "open";
|
|
935
974
|
stale: "stale";
|
|
975
|
+
open: "open";
|
|
936
976
|
"accepted-risk": "accepted-risk";
|
|
937
977
|
scheduled: "scheduled";
|
|
938
978
|
resolved: "resolved";
|
|
@@ -950,7 +990,76 @@ export declare const SCHEMAS: {
|
|
|
950
990
|
last_verified: z.ZodOptional<z.ZodString>;
|
|
951
991
|
}, z.core.$strip>;
|
|
952
992
|
}, z.core.$strip>;
|
|
993
|
+
readonly conventions: z.ZodObject<{
|
|
994
|
+
schema: z.ZodLiteral<"nuryel.convention/1">;
|
|
995
|
+
id: z.ZodString;
|
|
996
|
+
scope: z.ZodObject<{
|
|
997
|
+
kind: z.ZodEnum<{
|
|
998
|
+
repository: "repository";
|
|
999
|
+
organization: "organization";
|
|
1000
|
+
team: "team";
|
|
1001
|
+
user: "user";
|
|
1002
|
+
}>;
|
|
1003
|
+
id: z.ZodString;
|
|
1004
|
+
}, z.core.$strict>;
|
|
1005
|
+
key: z.ZodString;
|
|
1006
|
+
value: z.ZodString;
|
|
1007
|
+
status: z.ZodEnum<{
|
|
1008
|
+
stale: "stale";
|
|
1009
|
+
proposed: "proposed";
|
|
1010
|
+
accepted: "accepted";
|
|
1011
|
+
withdrawn: "withdrawn";
|
|
1012
|
+
}>;
|
|
1013
|
+
sources: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1014
|
+
kind: z.ZodLiteral<"record">;
|
|
1015
|
+
id: z.ZodString;
|
|
1016
|
+
record_hash: z.ZodString;
|
|
1017
|
+
scope: z.ZodOptional<z.ZodObject<{
|
|
1018
|
+
kind: z.ZodEnum<{
|
|
1019
|
+
repository: "repository";
|
|
1020
|
+
organization: "organization";
|
|
1021
|
+
team: "team";
|
|
1022
|
+
user: "user";
|
|
1023
|
+
}>;
|
|
1024
|
+
id: z.ZodString;
|
|
1025
|
+
}, z.core.$strict>>;
|
|
1026
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1027
|
+
kind: z.ZodLiteral<"external">;
|
|
1028
|
+
ref: z.ZodObject<{
|
|
1029
|
+
system: z.ZodString;
|
|
1030
|
+
object_type: z.ZodString;
|
|
1031
|
+
object_key: z.ZodString;
|
|
1032
|
+
version: z.ZodOptional<z.ZodString>;
|
|
1033
|
+
content_hash: z.ZodOptional<z.ZodString>;
|
|
1034
|
+
observed_at: z.ZodString;
|
|
1035
|
+
locator: z.ZodOptional<z.ZodString>;
|
|
1036
|
+
}, z.core.$strict>;
|
|
1037
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1038
|
+
kind: z.ZodLiteral<"schema">;
|
|
1039
|
+
name: z.ZodString;
|
|
1040
|
+
fingerprint: z.ZodString;
|
|
1041
|
+
}, z.core.$strict>], "kind">>;
|
|
1042
|
+
valid_from: z.ZodString;
|
|
1043
|
+
valid_to: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
1044
|
+
review_by: z.ZodString;
|
|
1045
|
+
provenance: z.ZodObject<{
|
|
1046
|
+
source: z.ZodString;
|
|
1047
|
+
confidence: z.ZodNumber;
|
|
1048
|
+
evidence: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1049
|
+
last_verified: z.ZodOptional<z.ZodString>;
|
|
1050
|
+
}, z.core.$strip>;
|
|
1051
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
1052
|
+
owner: z.ZodString;
|
|
1053
|
+
readers: z.ZodArray<z.ZodString>;
|
|
1054
|
+
writers: z.ZodArray<z.ZodString>;
|
|
1055
|
+
}, z.core.$strict>>;
|
|
1056
|
+
}, z.core.$strict>;
|
|
953
1057
|
readonly receipts: z.ZodObject<{
|
|
1058
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
1059
|
+
owner: z.ZodString;
|
|
1060
|
+
readers: z.ZodArray<z.ZodString>;
|
|
1061
|
+
writers: z.ZodArray<z.ZodString>;
|
|
1062
|
+
}, z.core.$strict>>;
|
|
954
1063
|
schema: z.ZodLiteral<"nuryel.receipt/1">;
|
|
955
1064
|
id: z.ZodString;
|
|
956
1065
|
scope: z.ZodObject<{
|
|
@@ -1023,6 +1132,11 @@ export declare const SCHEMAS: {
|
|
|
1023
1132
|
}, z.core.$strip>;
|
|
1024
1133
|
}, z.core.$strict>;
|
|
1025
1134
|
readonly commitments: z.ZodObject<{
|
|
1135
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
1136
|
+
owner: z.ZodString;
|
|
1137
|
+
readers: z.ZodArray<z.ZodString>;
|
|
1138
|
+
writers: z.ZodArray<z.ZodString>;
|
|
1139
|
+
}, z.core.$strict>>;
|
|
1026
1140
|
schema: z.ZodLiteral<"nuryel.commitment/1">;
|
|
1027
1141
|
id: z.ZodString;
|
|
1028
1142
|
scope: z.ZodObject<{
|
|
@@ -1065,6 +1179,11 @@ export declare const SCHEMAS: {
|
|
|
1065
1179
|
}, z.core.$strip>;
|
|
1066
1180
|
}, z.core.$strict>;
|
|
1067
1181
|
readonly derived: z.ZodObject<{
|
|
1182
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
1183
|
+
owner: z.ZodString;
|
|
1184
|
+
readers: z.ZodArray<z.ZodString>;
|
|
1185
|
+
writers: z.ZodArray<z.ZodString>;
|
|
1186
|
+
}, z.core.$strict>>;
|
|
1068
1187
|
schema: z.ZodLiteral<"nuryel.derived/1">;
|
|
1069
1188
|
id: z.ZodString;
|
|
1070
1189
|
scope: z.ZodObject<{
|
|
@@ -1108,6 +1227,18 @@ export declare const SCHEMAS: {
|
|
|
1108
1227
|
name: z.ZodString;
|
|
1109
1228
|
fingerprint: z.ZodString;
|
|
1110
1229
|
}, z.core.$strict>], "kind">>;
|
|
1230
|
+
field_provenance: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1231
|
+
selector: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1232
|
+
kind: z.ZodLiteral<"json_pointer">;
|
|
1233
|
+
path: z.ZodString;
|
|
1234
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1235
|
+
kind: z.ZodLiteral<"text">;
|
|
1236
|
+
start: z.ZodNumber;
|
|
1237
|
+
end: z.ZodNumber;
|
|
1238
|
+
}, z.core.$strict>], "kind">;
|
|
1239
|
+
value_hash: z.ZodString;
|
|
1240
|
+
dependency_hashes: z.ZodArray<z.ZodString>;
|
|
1241
|
+
}, z.core.$strict>>>;
|
|
1111
1242
|
transform_version: z.ZodString;
|
|
1112
1243
|
computed_at: z.ZodString;
|
|
1113
1244
|
valid_to: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
@@ -1142,6 +1273,11 @@ export declare const SCHEMAS: {
|
|
|
1142
1273
|
}, z.core.$strip>;
|
|
1143
1274
|
}, z.core.$strict>;
|
|
1144
1275
|
readonly entities: z.ZodObject<{
|
|
1276
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
1277
|
+
owner: z.ZodString;
|
|
1278
|
+
readers: z.ZodArray<z.ZodString>;
|
|
1279
|
+
writers: z.ZodArray<z.ZodString>;
|
|
1280
|
+
}, z.core.$strict>>;
|
|
1145
1281
|
schema: z.ZodLiteral<"nuryel.entity/1">;
|
|
1146
1282
|
id: z.ZodString;
|
|
1147
1283
|
kind: z.ZodString;
|
|
@@ -1181,6 +1317,11 @@ export declare const SCHEMAS: {
|
|
|
1181
1317
|
updated_at: z.ZodString;
|
|
1182
1318
|
}, z.core.$strict>;
|
|
1183
1319
|
readonly relationships: z.ZodObject<{
|
|
1320
|
+
visibility: z.ZodOptional<z.ZodObject<{
|
|
1321
|
+
owner: z.ZodString;
|
|
1322
|
+
readers: z.ZodArray<z.ZodString>;
|
|
1323
|
+
writers: z.ZodArray<z.ZodString>;
|
|
1324
|
+
}, z.core.$strict>>;
|
|
1184
1325
|
schema: z.ZodLiteral<"nuryel.relationship/1">;
|
|
1185
1326
|
id: z.ZodString;
|
|
1186
1327
|
from: z.ZodString;
|
|
@@ -1228,6 +1369,7 @@ export type EntityFor = {
|
|
|
1228
1369
|
constraints: Constraint;
|
|
1229
1370
|
runbooks: Runbook;
|
|
1230
1371
|
findings: Finding;
|
|
1372
|
+
conventions: Convention;
|
|
1231
1373
|
receipts: ActionReceipt;
|
|
1232
1374
|
commitments: Commitment;
|
|
1233
1375
|
derived: DerivedState;
|
package/dist/core/types.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RecordVisibilitySchema } from "./recordVisibility.js";
|
|
1
2
|
/**
|
|
2
3
|
* Core entity schema for the Project Hunch (DESIGN.md §3).
|
|
3
4
|
*
|
|
@@ -9,7 +10,7 @@ import { z } from "zod";
|
|
|
9
10
|
import { createHash } from "node:crypto";
|
|
10
11
|
import { findingId, resourceId, resourceRelationshipId } from "./ids.js";
|
|
11
12
|
import { ProvenanceSchema, SENSITIVE_METADATA_KEY, isCredentialFreeText } from "./provenance.js";
|
|
12
|
-
import { ActionReceiptSchema, CommitmentSchema, DerivedStateSchema, ExternalEntitySchema, StateRelationshipSchema, } from "./stateRecords.js";
|
|
13
|
+
import { ConventionSchema, ActionReceiptSchema, CommitmentSchema, DerivedStateSchema, ExternalEntitySchema, StateRelationshipSchema, } from "./stateRecords.js";
|
|
13
14
|
// Provenance and the credential-free text check live in the leaf module ./provenance.js so
|
|
14
15
|
// record schemas registered below can import them without a cycle; re-exported unchanged.
|
|
15
16
|
export { ProvenanceSchema, isCredentialFreeText };
|
|
@@ -323,6 +324,7 @@ export const PremiseSchema = z.object({
|
|
|
323
324
|
path: ["under"],
|
|
324
325
|
});
|
|
325
326
|
export const DecisionSchema = z.object({
|
|
327
|
+
visibility: RecordVisibilitySchema.optional(),
|
|
326
328
|
id: z.string().describe("dec_*"),
|
|
327
329
|
title: z.string(),
|
|
328
330
|
// Decision-grounding anchor: the join key that relates a doc section, a decision,
|
|
@@ -372,6 +374,7 @@ export const BugLineageSchema = z.object({
|
|
|
372
374
|
});
|
|
373
375
|
/** A bug with root cause and lineage (introduced → fixed → recurred). */
|
|
374
376
|
export const BugSchema = z.object({
|
|
377
|
+
visibility: RecordVisibilitySchema.optional(),
|
|
375
378
|
id: z.string().describe("bug_*"),
|
|
376
379
|
title: z.string(),
|
|
377
380
|
symptom: z.string().default(""),
|
|
@@ -388,6 +391,7 @@ export const BugSchema = z.object({
|
|
|
388
391
|
});
|
|
389
392
|
/** An invariant the system must respect. */
|
|
390
393
|
export const ConstraintSchema = z.object({
|
|
394
|
+
visibility: RecordVisibilitySchema.optional(),
|
|
391
395
|
id: z.string().describe("con_*"),
|
|
392
396
|
type: z.enum(["security", "performance", "correctness", "architecture", "compliance"]).default("correctness"),
|
|
393
397
|
statement: z.string(),
|
|
@@ -448,6 +452,7 @@ export const RunbookSchema = z.object({
|
|
|
448
452
|
* never enters any block path. Lifecycle is `triage`, not valid-time: a finding is
|
|
449
453
|
* resolved/stale-marked, never superseded. */
|
|
450
454
|
export const FindingSchema = z.object({
|
|
455
|
+
visibility: RecordVisibilitySchema.optional(),
|
|
451
456
|
id: z.string().describe("fnd_*"),
|
|
452
457
|
title: z.string(),
|
|
453
458
|
observation: z.string().default("").describe("what was observed, in plain words"),
|
|
@@ -594,7 +599,7 @@ export function landscapeDriftCandidateFinding(value) {
|
|
|
594
599
|
// loads exactly as before, and an older build ignores directories it does not know.
|
|
595
600
|
export const ENTITY_KINDS = [
|
|
596
601
|
"components", "resources", "edges", "symbols", "decisions", "bugs", "constraints", "runbooks", "findings",
|
|
597
|
-
"receipts", "commitments", "derived", "entities", "relationships",
|
|
602
|
+
"receipts", "commitments", "derived", "entities", "relationships", "conventions",
|
|
598
603
|
];
|
|
599
604
|
export const SCHEMAS = {
|
|
600
605
|
components: ComponentSchema,
|
|
@@ -606,6 +611,7 @@ export const SCHEMAS = {
|
|
|
606
611
|
constraints: ConstraintSchema,
|
|
607
612
|
runbooks: RunbookSchema,
|
|
608
613
|
findings: FindingSchema,
|
|
614
|
+
conventions: ConventionSchema,
|
|
609
615
|
receipts: ActionReceiptSchema,
|
|
610
616
|
commitments: CommitmentSchema,
|
|
611
617
|
derived: DerivedStateSchema,
|
package/dist/extractors/git.js
CHANGED
|
@@ -1838,16 +1838,9 @@ export function isLinkedWorktree(cwd) {
|
|
|
1838
1838
|
const own = gitSafe(["rev-parse", "--absolute-git-dir"], cwd);
|
|
1839
1839
|
if (!common || !own)
|
|
1840
1840
|
return false;
|
|
1841
|
-
//
|
|
1842
|
-
//
|
|
1843
|
-
|
|
1844
|
-
const norm = (p) => { try {
|
|
1845
|
-
return realpathSync(p);
|
|
1846
|
-
}
|
|
1847
|
-
catch {
|
|
1848
|
-
return resolve(p);
|
|
1849
|
-
} };
|
|
1850
|
-
return norm(own) !== norm(common);
|
|
1841
|
+
// Git can spell the same directory differently: /var vs /private/var on
|
|
1842
|
+
// macOS, or long vs 8.3/case variants on Windows. Compare physical identity.
|
|
1843
|
+
return !sameFilesystemEntry(own, common);
|
|
1851
1844
|
}
|
|
1852
1845
|
/** Current branch name (e.g. "main", "feat/x"), or "" in detached HEAD / non-repo.
|
|
1853
1846
|
* Stamped onto auto-captured decisions so branch-scoped work stays filterable. */
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { conventionSupplements } from '../core/conventionDelivery.js';
|
|
2
|
+
import { fieldCitationText } from "../core/fieldProvenance.js";
|
|
1
3
|
/**
|
|
2
4
|
* MCP server — the structured two-way API into the Hunch (DESIGN.md §7 / App. A).
|
|
3
5
|
* Exposes read tools (query/why/bug_lineage/check_constraints/get_dependents) and
|
|
@@ -1102,7 +1104,7 @@ export function buildServerWithRootControl(initialRoot, options = {}) {
|
|
|
1102
1104
|
decisionCorpus: store.recs("decisions"),
|
|
1103
1105
|
historical: !!asOf,
|
|
1104
1106
|
profile: profile ?? "builder",
|
|
1105
|
-
supplements: [...(dnaSupplement ? [dnaSupplement] : []), ...stateGrounding],
|
|
1107
|
+
supplements: [...(dnaSupplement ? [dnaSupplement] : []), ...stateGrounding, ...(asOf ? [] : conventionSupplements(store.recs("conventions")))],
|
|
1106
1108
|
};
|
|
1107
1109
|
// Task-phrase input ("improve retrieval ranking") resolves no file/symbol and
|
|
1108
1110
|
// used to return an empty brief while the graph held the answer — fall back to
|
|
@@ -1822,7 +1824,7 @@ export function buildServerWithRootControl(initialRoot, options = {}) {
|
|
|
1822
1824
|
const r = (response.records ?? {})[ref.id] ?? {};
|
|
1823
1825
|
const g = (k) => { const v = r[k]; return typeof v === "string" ? v : v == null ? "" : JSON.stringify(v); };
|
|
1824
1826
|
if (ref.facet === "derived")
|
|
1825
|
-
return `- ${label} derived ${ref.id} · computed ${g("computed_at")} · ${r.dependencies?.length ?? 0} dependencies\n ${g("content").slice(0, 1200)}`;
|
|
1827
|
+
return `- ${label} derived ${ref.id} · computed ${g("computed_at")} · ${r.dependencies?.length ?? 0} dependencies\n ${g("content").slice(0, 1200)}${fieldCitationText(r)}`;
|
|
1826
1828
|
if (ref.facet === "commitments")
|
|
1827
1829
|
return `- ${label} commitment ${ref.id} · ${g("status")} · due ${g("due")} · owner ${g("owner")}: ${g("title")}${r.closed_by ? ` · closed by ${g("closed_by")}` : ""}`;
|
|
1828
1830
|
if (ref.facet === "receipts") {
|
|
@@ -1858,7 +1860,11 @@ export function buildServerWithRootControl(initialRoot, options = {}) {
|
|
|
1858
1860
|
: sor.observed_truncated ? ['- More observations exist; read this subject with observed_page:{} in one partition, then follow next_cursor.'] : []),
|
|
1859
1861
|
...(sor.invalidated_by.length ? [`- invalidated by: ${sor.invalidated_by.join(", ")}`] : [])].join("\n") || "(nothing on record for this subject)"
|
|
1860
1862
|
: "";
|
|
1861
|
-
|
|
1863
|
+
const conventionText = response.conventions ? '\n\nExplicit conventions (advisory; no scope takes precedence):\n' + response.conventions.items.map(item => {
|
|
1864
|
+
const record = response.records?.[item.ref.id];
|
|
1865
|
+
return `- ${item.ref.scope.kind}/${item.ref.scope.id} · ${item.key} · ${record?.status}/${item.currentness}${item.conflict ? ' · CONFLICT' : ''}: ${String(record?.value ?? '').slice(0, 300)} (${item.ref.id})`;
|
|
1866
|
+
}).join('\n') + (response.conventions.truncated ? '\nMore conventions exist; this view is incomplete.' : '') : '';
|
|
1867
|
+
return stateResult(`${response.receipt_id} · ${summary}${deniedNote}${stateText ? `\n\nState of record:\n${stateText}` : ""}\n\n${envelope.text}${conventionText}`, response);
|
|
1862
1868
|
}
|
|
1863
1869
|
catch (e) {
|
|
1864
1870
|
return stateRefusal(e);
|
|
@@ -1866,7 +1872,7 @@ export function buildServerWithRootControl(initialRoot, options = {}) {
|
|
|
1866
1872
|
});
|
|
1867
1873
|
server.registerTool("nuryel_write", {
|
|
1868
1874
|
title: "nuryel.state/1 write — provenance + idempotency in, durability out",
|
|
1869
|
-
description: "Write one record into a facet (receipts, commitments, derived, entities, relationships, or the legacy decisions/constraints/bugs/findings). The record must carry provenance; the request must carry an idempotency_key — a replay returns the original, a reused key with a different payload is refused. Ids are derived from the record's facts, never chosen. A second live decision on a topic is refused with the incumbent named; pass supersedes to replace it explicitly. organization/team/user partitions never ride a repository: they require an overlay. To show an existing captured observation under another subject without copying it, write a relationship type observation_about with from=observation id, to=subject, observation_hash, lifecycle=active, reason and hashed external evidence of the explicit association. Retire the relationship to unlink; reactivation requires expected_version.",
|
|
1875
|
+
description: "Write one record into a facet (receipts, commitments, derived, entities, relationships, or the legacy decisions/constraints/bugs/findings). The record must carry provenance; the request must carry an idempotency_key — a replay returns the original, a reused key with a different payload is refused. Ids are derived from the record's facts, never chosen. A second live decision on a topic is refused with the incumbent named; pass supersedes to replace it explicitly. organization/team/user partitions never ride a repository: they require an overlay. To show an existing captured observation under another subject without copying it, write a relationship type observation_about with from=observation id, to=subject, observation_hash, lifecycle=active, reason and hashed external evidence of the explicit association. Retire the relationship to unlink; reactivation requires expected_version. With capability nuryel.field-provenance/1, a derived record may carry field_provenance: [{selector:{kind:json_pointer,path:/field} or {kind:text,start:0,end:10}, value_hash:stateHash(selected scalar or text), dependency_hashes:[stateHash(existing dependency)]}]. Text offsets count Unicode code points, end exclusive. Citations are writer-supplied traceability, not verified support; negotiate support in every shared reader before writing them. With nuryel.record-visibility/1, records may carry visibility:{owner:principal-id,readers:[ids],writers:[ids]}; the owner is implicit in both lists, and writers must be readers. Owner-only audience changes require expected_version, including supersession. Restricted records require a dedicated partition; local stdio principal assertions assume trusted callers.",
|
|
1870
1876
|
inputSchema: { ...WriteRequestSchema.omit({ schema: true }).shape, cwd: cwdHintField },
|
|
1871
1877
|
outputSchema: WriteResultSchema.shape,
|
|
1872
1878
|
}, async ({ cwd: _cwd, ...input }) => {
|
package/dist/serve/app.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export declare class HttpProblem extends Error {
|
|
|
27
27
|
constructor(status: number, code: string, message: string, extra?: Record<string, unknown>);
|
|
28
28
|
}
|
|
29
29
|
export interface ServeOptions {
|
|
30
|
+
/** Required for key-bound credentials when supplying an in-memory configuration. */
|
|
31
|
+
authStateDir?: string;
|
|
30
32
|
version?: string;
|
|
31
33
|
/** Injectable for tests: how a partition's store is opened. */
|
|
32
34
|
openStore?: (root: string) => HunchStore;
|
package/dist/serve/app.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { StateProofError, verifyStateProof } from './stateProof.js';
|
|
3
|
+
import { STATE_PROOF_CAPABILITY } from '../core/stateProof.js';
|
|
1
4
|
/**
|
|
2
5
|
* `hunch serve` — the HTTP binding of nuryel.state/1, and the served product's partition host.
|
|
3
6
|
*
|
|
@@ -21,11 +24,12 @@ import { hunchPaths } from "../core/paths.js";
|
|
|
21
24
|
import { flushCapture } from "../integrations/sync.js";
|
|
22
25
|
import { StateRefusal, capabilities, mergeReadResponses, readState, recordsState, stateHomeFor, subscribeState, writeState } from "../store/stateBinding.js";
|
|
23
26
|
import { STATE_READ_VERSION, STATE_RECORDS_VERSION, STATE_SUBSCRIBE_VERSION, STATE_WRITE_VERSION, ReadScopesSchema, ScopeSchema, scopePath } from "../core/stateContract.js";
|
|
24
|
-
import { partitionFor,
|
|
27
|
+
import { partitionFor, resolveCredential, readServeConfig } from "./config.js";
|
|
25
28
|
import { WriteLockTimeout, withWriteLock } from "./writelock.js";
|
|
26
29
|
import { HUNCH_VERSION } from "../core/version.js";
|
|
27
30
|
import { captureState, captureBatchState } from "../store/stateCapture.js";
|
|
28
31
|
import { STATE_CAPTURE_VERSION, STATE_CAPTURE_BATCH_VERSION } from "../core/stateContract.js";
|
|
32
|
+
import { operatorHtml, operatorCss, operatorJs } from "./operator.js";
|
|
29
33
|
export const BODY_LIMIT_BYTES = 1024 * 1024;
|
|
30
34
|
export const PROBLEM_TYPE = "https://www.hunchmemory.com/problems/nuryel.state/1/";
|
|
31
35
|
export class HttpProblem extends Error {
|
|
@@ -46,16 +50,6 @@ const problem = (status, code, message, extra = {}) => new HttpProblem(status, c
|
|
|
46
50
|
const REFUSAL_STATUS = {
|
|
47
51
|
"outside-grants": 403, unsupported: 400, malformed: 400, identity: 422, conflict: 409, idempotency: 409, "no-partition-home": 404,
|
|
48
52
|
};
|
|
49
|
-
function bearerToken(req) {
|
|
50
|
-
const header = req.headers.authorization;
|
|
51
|
-
if (!header)
|
|
52
|
-
return undefined;
|
|
53
|
-
const trimmed = header.trim();
|
|
54
|
-
if (!/^bearer /i.test(trimmed))
|
|
55
|
-
return undefined;
|
|
56
|
-
const token = trimmed.slice("bearer ".length).trim();
|
|
57
|
-
return token || undefined;
|
|
58
|
-
}
|
|
59
53
|
async function readBody(req) {
|
|
60
54
|
const declared = req.headers["content-length"];
|
|
61
55
|
if (declared !== undefined) {
|
|
@@ -95,9 +89,11 @@ function parseScopeParam(value) {
|
|
|
95
89
|
}
|
|
96
90
|
export function createServeApp(config, opts = {}) {
|
|
97
91
|
const version = opts.version ?? HUNCH_VERSION;
|
|
92
|
+
const configFile = config.file;
|
|
93
|
+
const authStateDir = opts.authStateDir ?? (configFile ? resolve(configFile + ".auth") : undefined);
|
|
98
94
|
const stores = new Map();
|
|
99
|
-
const storeFor = (scope) => {
|
|
100
|
-
const partition = partitionFor(
|
|
95
|
+
const storeFor = (scope, activeConfig) => {
|
|
96
|
+
const partition = partitionFor(activeConfig, scope);
|
|
101
97
|
if (!partition)
|
|
102
98
|
throw problem(404, "no-partition", `this server does not serve ${scopePath(scope)}`);
|
|
103
99
|
let store = stores.get(partition.root);
|
|
@@ -128,19 +124,59 @@ export function createServeApp(config, opts = {}) {
|
|
|
128
124
|
};
|
|
129
125
|
const server = createServer(async (req, res) => {
|
|
130
126
|
try {
|
|
127
|
+
let activeConfig;
|
|
128
|
+
try {
|
|
129
|
+
activeConfig = configFile ? readServeConfig(configFile) : config;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
throw problem(503, 'configuration-unavailable', 'server configuration is unavailable; authentication is refused');
|
|
133
|
+
}
|
|
134
|
+
const requestStore = (scope) => storeFor(scope, activeConfig);
|
|
131
135
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
136
|
+
// Public shell only: all workspace data still uses the authenticated state routes below.
|
|
137
|
+
const asset = new Map([["/operator", [operatorHtml, "text/html"]], ["/operator/", [operatorHtml, "text/html"]], ["/operator.css", [operatorCss, "text/css"]], ["/operator.js", [operatorJs, "text/javascript"]]]).get(url.pathname);
|
|
138
|
+
if (asset && req.method === "GET") {
|
|
139
|
+
res.writeHead(200, { "content-type": `${asset[1]}; charset=utf-8`, "content-length": Buffer.byteLength(asset[0]), "cache-control": "no-store", "x-content-type-options": "nosniff", "referrer-policy": "no-referrer", "content-security-policy": "default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" });
|
|
140
|
+
return res.end(asset[0]);
|
|
141
|
+
}
|
|
132
142
|
if (url.pathname === "/nuryel/v1/health" && req.method === "GET") {
|
|
133
|
-
return send(res, 200, { ok: true, version, protocol: "nuryel.state/1", partitions:
|
|
143
|
+
return send(res, 200, { ok: true, version, protocol: "nuryel.state/1", partitions: activeConfig.partitions.map((p) => scopePath(p.scope)) });
|
|
144
|
+
}
|
|
145
|
+
const authorization = /^(Bearer|DPoP) ([^\s]+)$/i.exec(req.headers.authorization ?? '');
|
|
146
|
+
const credential = resolveCredential(activeConfig, authorization?.[2]);
|
|
147
|
+
if (!credential)
|
|
148
|
+
throw problem(401, 'unauthorized', 'valid credentials are required');
|
|
149
|
+
const countHeader = (name) => req.rawHeaders.filter((header, index) => index % 2 === 0 && header.toLowerCase() === name).length;
|
|
150
|
+
if (countHeader('authorization') !== 1 || countHeader('dpop') > 1)
|
|
151
|
+
throw problem(401, 'invalid_dpop_proof', 'ambiguous authentication headers');
|
|
152
|
+
if (credential.proof_key) {
|
|
153
|
+
if (authorization[1].toLowerCase() !== 'dpop')
|
|
154
|
+
throw problem(401, 'invalid_dpop_proof', 'this credential requires DPoP proof; bearer fallback is disabled');
|
|
155
|
+
if (!activeConfig.public_origin || !authStateDir)
|
|
156
|
+
throw problem(503, 'proof-state-unavailable', 'key-bound authentication requires a public origin and persistent proof state');
|
|
157
|
+
try {
|
|
158
|
+
await verifyStateProof({ proof: typeof req.headers.dpop === 'string' ? req.headers.dpop : undefined, key: credential.proof_key, method: req.method ?? '', url: activeConfig.public_origin + url.pathname, token: authorization[2], stateDir: authStateDir });
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
if (error instanceof StateProofError) {
|
|
162
|
+
res.setHeader('WWW-Authenticate', `DPoP error="${error.code}", algs="EdDSA"`);
|
|
163
|
+
if (error.nonce)
|
|
164
|
+
res.setHeader('DPoP-Nonce', error.nonce);
|
|
165
|
+
throw problem(401, error.code, error.message);
|
|
166
|
+
}
|
|
167
|
+
throw problem(503, 'proof-state-unavailable', 'proof replay state is unavailable; authentication is refused');
|
|
168
|
+
}
|
|
134
169
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
170
|
+
else if (authorization[1].toLowerCase() !== 'bearer' || req.headers.dpop !== undefined)
|
|
171
|
+
throw problem(401, 'invalid_dpop_proof', 'credential is not bound to a proof key');
|
|
172
|
+
const principal = { id: credential.id, kind: credential.kind, grants: credential.grants, ...(credential.display ? { display: credential.display } : {}) };
|
|
138
173
|
if (url.pathname === "/nuryel/v1/capabilities" && req.method === "GET") {
|
|
139
174
|
const scope = parseScopeParam(url.searchParams.get("scope")) ?? principal.grants[0];
|
|
140
175
|
if (!principal.grants.some((g) => scopePath(g) === scopePath(scope)))
|
|
141
176
|
throw problem(403, "outside-grants", `scope ${scopePath(scope)} is outside the principal's grants`);
|
|
142
|
-
const { store } =
|
|
143
|
-
|
|
177
|
+
const { store } = requestStore(scope);
|
|
178
|
+
const offered = capabilities(store);
|
|
179
|
+
return send(res, 200, { ...offered, capabilities: [...offered.capabilities, STATE_PROOF_CAPABILITY], principal: { id: principal.id, kind: principal.kind, grants: principal.grants } });
|
|
144
180
|
}
|
|
145
181
|
if (req.method !== "POST")
|
|
146
182
|
throw problem(405, "method-not-allowed", `${req.method} is not allowed on ${url.pathname}`);
|
|
@@ -148,13 +184,15 @@ export function createServeApp(config, opts = {}) {
|
|
|
148
184
|
// The body never names the principal: the token did.
|
|
149
185
|
delete body.principal;
|
|
150
186
|
delete body.schema;
|
|
187
|
+
// Only authenticated grants select stores for cross-partition source visibility.
|
|
188
|
+
const accessOptions = { additionalStores: principal.grants.map(grant => requestStore(grant).store) };
|
|
151
189
|
if (url.pathname === "/nuryel/v1/read") {
|
|
152
190
|
const scope = requireScope(principal, body);
|
|
153
191
|
if (body.observed_page !== undefined && body.scopes !== undefined)
|
|
154
192
|
throw problem(400, 'malformed', 'observation pages require a single partition without scopes');
|
|
155
|
-
const { store } =
|
|
193
|
+
const { store } = requestStore(scope);
|
|
156
194
|
if (body.scopes === undefined) {
|
|
157
|
-
const { response, envelope } = readState(store, { schema: STATE_READ_VERSION, principal, ...body });
|
|
195
|
+
const { response, envelope } = readState(store, { schema: STATE_READ_VERSION, principal, ...body }, accessOptions);
|
|
158
196
|
return send(res, 200, { ...response, envelope });
|
|
159
197
|
}
|
|
160
198
|
// Union read. The primary `scope` was gated above as always; every extra scope is
|
|
@@ -170,46 +208,49 @@ export function createServeApp(config, opts = {}) {
|
|
|
170
208
|
for (const s of requested.data)
|
|
171
209
|
if (isGranted(s) && scopePath(s) !== scopePath(scope) && !others.has(scopePath(s)))
|
|
172
210
|
others.set(scopePath(s), s);
|
|
173
|
-
const primary = readState(store, { schema: STATE_READ_VERSION, principal, ...rest, scope });
|
|
174
|
-
const merged = mergeReadResponses(primary.response, [...others.values()].map((other) => readState(
|
|
211
|
+
const primary = readState(store, { schema: STATE_READ_VERSION, principal, ...rest, scope }, accessOptions);
|
|
212
|
+
const merged = mergeReadResponses(primary.response, [...others.values()].map((other) => readState(requestStore(other).store, { schema: STATE_READ_VERSION, principal, ...rest, scope: other }, accessOptions).response), ungranted);
|
|
175
213
|
return send(res, 200, { ...merged, envelope: primary.envelope });
|
|
176
214
|
}
|
|
177
215
|
if (url.pathname === "/nuryel/v1/write") {
|
|
178
216
|
const scope = requireScope(principal, body);
|
|
179
|
-
const { store, root } =
|
|
217
|
+
const { store, root } = requestStore(scope);
|
|
180
218
|
const { hunchDir } = stateHomeFor(store, scope);
|
|
181
219
|
const result = await withWriteLock(hunchDir, () => writeState(store, { schema: STATE_WRITE_VERSION, principal, ...body }, {
|
|
220
|
+
...accessOptions,
|
|
182
221
|
flush: (isPrivate, message) => flushCapture(store, hunchPaths(root).hunch, isPrivate, message),
|
|
183
222
|
}));
|
|
184
223
|
return send(res, result.outcome === "created" ? 201 : 200, result);
|
|
185
224
|
}
|
|
186
225
|
if (url.pathname === "/nuryel/v1/capture") {
|
|
187
226
|
const scope = requireScope(principal, body);
|
|
188
|
-
const { store, root } =
|
|
227
|
+
const { store, root } = requestStore(scope);
|
|
189
228
|
const { hunchDir } = stateHomeFor(store, scope);
|
|
190
229
|
const result = await withWriteLock(hunchDir, () => captureState(store, { schema: STATE_CAPTURE_VERSION, principal, ...body }, {
|
|
230
|
+
...accessOptions,
|
|
191
231
|
flush: (isPrivate, message) => flushCapture(store, hunchPaths(root).hunch, isPrivate, message),
|
|
192
232
|
}));
|
|
193
233
|
return send(res, result.outcome === "created" ? 201 : 200, result);
|
|
194
234
|
}
|
|
195
235
|
if (url.pathname === "/nuryel/v1/capture-batch") {
|
|
196
236
|
const scope = requireScope(principal, body);
|
|
197
|
-
const { store, root } =
|
|
237
|
+
const { store, root } = requestStore(scope);
|
|
198
238
|
const { hunchDir } = stateHomeFor(store, scope);
|
|
199
239
|
const result = await withWriteLock(hunchDir, () => captureBatchState(store, { schema: STATE_CAPTURE_BATCH_VERSION, principal, ...body }, {
|
|
240
|
+
...accessOptions,
|
|
200
241
|
flush: (isPrivate, message) => flushCapture(store, hunchPaths(root).hunch, isPrivate, message),
|
|
201
242
|
}));
|
|
202
243
|
return send(res, 200, result);
|
|
203
244
|
}
|
|
204
245
|
if (url.pathname === "/nuryel/v1/subscribe") {
|
|
205
246
|
const scope = requireScope(principal, body);
|
|
206
|
-
const { store } =
|
|
207
|
-
return send(res, 200, subscribeState(store, { schema: STATE_SUBSCRIBE_VERSION, principal, ...body }));
|
|
247
|
+
const { store } = requestStore(scope);
|
|
248
|
+
return send(res, 200, subscribeState(store, { schema: STATE_SUBSCRIBE_VERSION, principal, ...body }, accessOptions));
|
|
208
249
|
}
|
|
209
250
|
if (url.pathname === "/nuryel/v1/records") {
|
|
210
251
|
const scope = requireScope(principal, body);
|
|
211
|
-
const { store } =
|
|
212
|
-
return send(res, 200, recordsState(store, { schema: STATE_RECORDS_VERSION, principal, ...body }));
|
|
252
|
+
const { store } = requestStore(scope);
|
|
253
|
+
return send(res, 200, recordsState(store, { schema: STATE_RECORDS_VERSION, principal, ...body }, accessOptions));
|
|
213
254
|
}
|
|
214
255
|
throw problem(404, "not-found", `${url.pathname} is not a nuryel.state/1 route`);
|
|
215
256
|
}
|