@davesheffer/hunch 1.26.0 → 1.26.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/dist/cli/index.js +10 -2
- package/dist/cli/serve.js +15 -8
- package/dist/core/stateContract.js +3 -0
- package/dist/extractors/git.js +5 -1
- package/dist/mcp/server.js +46 -10
- package/dist/serve/config.js +10 -0
- package/dist/store/stateBinding.js +13 -7
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { join, relative, dirname, basename, resolve, isAbsolute } from "node:pat
|
|
|
20
20
|
import { tmpdir } from "node:os";
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
22
|
import { Command } from "commander";
|
|
23
|
-
import { hunchPaths, hunchPathsForDir, findRoot, toPosixTarget } from "../core/paths.js";
|
|
23
|
+
import { hunchPaths, hunchPathsForDir, findRoot, toPosixTarget, isDir } from "../core/paths.js";
|
|
24
24
|
import { writeFileAtomic } from "../core/io.js";
|
|
25
25
|
import { looksLikeCorrection, CORRECTION_NUDGE } from "../core/correction.js";
|
|
26
26
|
import { HUNCH_VERSION } from "../core/version.js";
|
|
@@ -5160,8 +5160,16 @@ function printAutoReviewPlan(plan) {
|
|
|
5160
5160
|
program
|
|
5161
5161
|
.command("mcp")
|
|
5162
5162
|
.description("Start the MCP server over stdio (Claude Code connects here).")
|
|
5163
|
-
.
|
|
5163
|
+
.option("--root <dir>", "serve exactly this store or served partition and ignore the client's workspace roots and cwd hints")
|
|
5164
|
+
.action(async (opts) => {
|
|
5164
5165
|
const { startServer } = await import("../mcp/server.js");
|
|
5166
|
+
if (opts.root) {
|
|
5167
|
+
const pinned = resolve(opts.root);
|
|
5168
|
+
if (!isDir(join(pinned, ".hunch")))
|
|
5169
|
+
throw new Error(`--root ${pinned} has no .hunch/ store`);
|
|
5170
|
+
await startServer(pinned, { pinned: true });
|
|
5171
|
+
return;
|
|
5172
|
+
}
|
|
5165
5173
|
await startServer(process.cwd());
|
|
5166
5174
|
});
|
|
5167
5175
|
// ---- migrate (schema versioning) ------------------------------------------
|
package/dist/cli/serve.js
CHANGED
|
@@ -11,12 +11,13 @@ function parseScopeArg(value) {
|
|
|
11
11
|
return parsed.data;
|
|
12
12
|
}
|
|
13
13
|
export function registerServeCommands(program) {
|
|
14
|
+
const DEFAULT_CONFIG = "hunch-serve.json";
|
|
14
15
|
const serve = program.command("serve")
|
|
15
16
|
.description("Serve nuryel.state/1 over HTTP for organization / team / user / repository partitions (binds 127.0.0.1; put it behind SSH or a reverse proxy)")
|
|
16
|
-
.option("--config <file>",
|
|
17
|
+
.option("--config <file>", `serve config (nuryel.serve-config/1); default ${DEFAULT_CONFIG}`)
|
|
17
18
|
.option("--port <n>", "override the configured port")
|
|
18
19
|
.action((opts) => {
|
|
19
|
-
const config = readServeConfig(resolve(opts.config));
|
|
20
|
+
const config = readServeConfig(resolve(opts.config ?? DEFAULT_CONFIG));
|
|
20
21
|
const port = opts.port ? Number(opts.port) : config.port;
|
|
21
22
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
22
23
|
throw new Error(`invalid port ${opts.port}`);
|
|
@@ -34,7 +35,7 @@ export function registerServeCommands(program) {
|
|
|
34
35
|
.description("Declare a partition directory and mint a principal token (printed once; only its hash is stored)")
|
|
35
36
|
.requiredOption("--partition <kind:id>", "the scope this directory IS, e.g. user:david or organization:ylm")
|
|
36
37
|
.requiredOption("--root <dir>", "directory whose .hunch/ holds the partition (created if missing)")
|
|
37
|
-
.option("--config <file>",
|
|
38
|
+
.option("--config <file>", `serve config to create or extend; default ${DEFAULT_CONFIG}`)
|
|
38
39
|
.option("--principal <id>", "principal to add or rotate, granted this partition")
|
|
39
40
|
.option("--kind <kind>", "principal kind: human | agent | service", "agent")
|
|
40
41
|
.option("--grant <kind:id...>", "additional partitions to grant the principal (must be served by this config)")
|
|
@@ -43,22 +44,28 @@ export function registerServeCommands(program) {
|
|
|
43
44
|
.action((opts) => {
|
|
44
45
|
if (!["human", "agent", "service"].includes(opts.kind))
|
|
45
46
|
throw new Error("--kind must be human, agent or service");
|
|
47
|
+
// `serve` and `serve init` both take --config; Commander hands an option written after
|
|
48
|
+
// `init` to whichever command claims it first, and that was the parent — so 1.26.0's
|
|
49
|
+
// `serve init --config X` silently wrote the default file into the cwd. Read both.
|
|
50
|
+
const parent = serve.opts();
|
|
51
|
+
const configFile = resolve(parent.config ?? opts.config ?? DEFAULT_CONFIG);
|
|
52
|
+
const port = parent.port ?? opts.port;
|
|
46
53
|
const scope = parseScopeArg(opts.partition);
|
|
47
54
|
const grants = [scope, ...(opts.grant ?? []).map(parseScopeArg)];
|
|
48
55
|
const result = initServeConfig({
|
|
49
|
-
file:
|
|
56
|
+
file: configFile, scope, root: resolve(opts.root),
|
|
50
57
|
...(opts.principal ? { principal: { id: opts.principal, kind: opts.kind, grants } } : {}),
|
|
51
|
-
...(
|
|
58
|
+
...(port ? { port: Number(port) } : {}),
|
|
52
59
|
});
|
|
53
60
|
if (opts.json) {
|
|
54
|
-
console.log(JSON.stringify({ config:
|
|
61
|
+
console.log(JSON.stringify({ config: configFile, partition: result.partition, token: result.token }));
|
|
55
62
|
return;
|
|
56
63
|
}
|
|
57
64
|
console.log(`partition ${scopePath(scope)} → ${result.partition.root}`);
|
|
58
|
-
console.log(`config: ${
|
|
65
|
+
console.log(`config: ${configFile} (${result.config.partitions.length} partition(s), ${result.config.principals.length} principal(s))`);
|
|
59
66
|
if (result.token)
|
|
60
67
|
console.log(`token for ${opts.principal} (shown once — only its sha256 is stored): ${result.token}`);
|
|
61
|
-
console.log(`start: hunch serve --config ${
|
|
68
|
+
console.log(`start: hunch serve --config ${configFile}`);
|
|
62
69
|
});
|
|
63
70
|
}
|
|
64
71
|
//# sourceMappingURL=serve.js.map
|
|
@@ -88,6 +88,9 @@ export const ReadResponseSchema = z.object({
|
|
|
88
88
|
state_of_record: StateOfRecordSchema.nullable(),
|
|
89
89
|
/** Scopes the principal asked about but is not granted — named, never silently dropped. */
|
|
90
90
|
denied_scopes: z.array(ScopeSchema).max(64).default([]),
|
|
91
|
+
/** The records behind every ref in `state_of_record`, by id, so a consumer can answer from
|
|
92
|
+
* the drawer without a second lookup. Additive; absent when there is no subject. */
|
|
93
|
+
records: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
91
94
|
}).strict();
|
|
92
95
|
export const WriteRequestSchema = z.object({
|
|
93
96
|
schema: z.literal(STATE_WRITE_VERSION),
|
package/dist/extractors/git.js
CHANGED
|
@@ -818,7 +818,11 @@ function stagedMemoryPaths(hunchDir, env, allowMemoryDeletions = false) {
|
|
|
818
818
|
function isDerivedStoreArtifact(relativeName) {
|
|
819
819
|
return /^[^/]+\.sqlite[^/]*$/i.test(relativeName)
|
|
820
820
|
|| relativeName.split("/").some((segment) => segment.includes(".tmp"))
|
|
821
|
-
|| relativeName === "events.log"
|
|
821
|
+
|| relativeName === "events.log"
|
|
822
|
+
// `hunch serve` flushes INSIDE its cross-process write lock, so the lock file is always
|
|
823
|
+
// staged alongside the record; treating it as a violation made every served write skip
|
|
824
|
+
// the commit quietly and report durability "local" forever (1.26.0/1.26.1).
|
|
825
|
+
|| relativeName === "write.lock";
|
|
822
826
|
}
|
|
823
827
|
/** Enumerate ordinary JSON files already contained under an overlay. Push-capable
|
|
824
828
|
* stores force-add this exact allowlist so remote .gitignore, info/exclude, or an
|
package/dist/mcp/server.js
CHANGED
|
@@ -22,6 +22,7 @@ import { knownRepoDeps } from "../synthesis/tripwires.js";
|
|
|
22
22
|
import { refreshExistingGrounding } from "../integrations/providers.js";
|
|
23
23
|
import { revParse, asOfDate, revExists, lastChangeDate, rangeFiles, rangeDiff, commitFiles, commitDiff, stagedFiles, stagedDiff, workingFiles, workingDiff, pullHunchStatus, sameRemoteUrl, currentBranch } from "../extractors/git.js";
|
|
24
24
|
import { flushCapture, flushMemoryHome, pinSharedRemote } from "../integrations/sync.js";
|
|
25
|
+
import { withWriteLock } from "../serve/writelock.js";
|
|
25
26
|
import { advertisedTeamRemoteContract, ensureTeamOverlay, overlayMatchesTeamRemote, readTeamConfig, teamRemoteContract, teamSharedRef } from "../integrations/team.js";
|
|
26
27
|
import { formatStructure } from "../core/format.js";
|
|
27
28
|
import { diagnoseIssueCorrectionStage, formatCorrectionStageDiagnostic } from "../core/correctionStage.js";
|
|
@@ -517,7 +518,8 @@ function prepareRoot(root, explicitOverlay, requireIndex) {
|
|
|
517
518
|
throw error;
|
|
518
519
|
}
|
|
519
520
|
}
|
|
520
|
-
export function buildServerWithRootControl(initialRoot) {
|
|
521
|
+
export function buildServerWithRootControl(initialRoot, options = {}) {
|
|
522
|
+
const pinned = options.pinned === true;
|
|
521
523
|
const explicitOverlay = !!process.env.HUNCH_PRIVATE_DIR?.trim();
|
|
522
524
|
const initial = prepareRoot(initialRoot, explicitOverlay, false);
|
|
523
525
|
let root = initial.root;
|
|
@@ -670,7 +672,9 @@ export function buildServerWithRootControl(initialRoot) {
|
|
|
670
672
|
// sole in-flight request — re-homing under a concurrent request would tear its
|
|
671
673
|
// root/store out from under it, so that case is refused rather than risked.
|
|
672
674
|
const cwdHint = extractCwdHint(args[0]);
|
|
673
|
-
|
|
675
|
+
// A pinned root is the whole point of `hunch mcp --root`: a served partition must not
|
|
676
|
+
// follow the caller's working directory into some other checkout.
|
|
677
|
+
if (cwdHint !== undefined && !pinned) {
|
|
674
678
|
const target = canonicalRootPath(findRoot(cwdHint));
|
|
675
679
|
if (target !== canonicalRootPath(root)) {
|
|
676
680
|
if (activeRequests) {
|
|
@@ -1710,7 +1714,31 @@ export function buildServerWithRootControl(initialRoot) {
|
|
|
1710
1714
|
? `subject ${sor.subject}: current ${sor.current.length} · in force ${sor.in_force.length} · done ${sor.done.length} · depends on ${sor.depends_on.length} · invalidated by ${sor.invalidated_by.length}`
|
|
1711
1715
|
: "no subject — delivery envelope only";
|
|
1712
1716
|
const deniedNote = response.denied_scopes.length ? `\ndenied scopes: ${response.denied_scopes.map((s) => `${s.kind}/${s.id}`).join(", ")}` : "";
|
|
1713
|
-
|
|
1717
|
+
// Render the state of record itself, not only its refs: a consumer answers from this text.
|
|
1718
|
+
const line = (label, ref) => {
|
|
1719
|
+
const r = (response.records ?? {})[ref.id] ?? {};
|
|
1720
|
+
const g = (k) => { const v = r[k]; return typeof v === "string" ? v : v == null ? "" : JSON.stringify(v); };
|
|
1721
|
+
if (ref.facet === "derived")
|
|
1722
|
+
return `- ${label} derived ${ref.id} · computed ${g("computed_at")} · ${r.dependencies?.length ?? 0} dependencies\n ${g("content").slice(0, 1200)}`;
|
|
1723
|
+
if (ref.facet === "commitments")
|
|
1724
|
+
return `- ${label} commitment ${ref.id} · ${g("status")} · due ${g("due")} · owner ${g("owner")}: ${g("title")}`;
|
|
1725
|
+
if (ref.facet === "receipts") {
|
|
1726
|
+
const t = (r.target ?? {});
|
|
1727
|
+
return `- ${label} receipt ${ref.id} · ${g("action_kind")} on ${String(t.system ?? "")} ${String(t.object_type ?? "")}:${String(t.object_key ?? "")} · ${g("state")} at ${g("occurred_at")} by ${g("actor")}`;
|
|
1728
|
+
}
|
|
1729
|
+
if (ref.facet === "decisions")
|
|
1730
|
+
return `- ${label} decision ${ref.id} · ${g("status")}: ${g("title")}`;
|
|
1731
|
+
if (ref.facet === "constraints")
|
|
1732
|
+
return `- ${label} constraint ${ref.id} · ${g("severity")}: ${g("statement")}`;
|
|
1733
|
+
if (ref.facet === "entities")
|
|
1734
|
+
return `- ${label} entity ${ref.id} · ${g("kind")} ${g("name")} · ${g("lifecycle")}`;
|
|
1735
|
+
return `- ${label} ${ref.facet} ${ref.id}`;
|
|
1736
|
+
};
|
|
1737
|
+
const stateText = sor
|
|
1738
|
+
? [...sor.current.map((r) => line("current", r)), ...sor.in_force.map((r) => line("in force", r)), ...sor.done.map((r) => line("done", r)),
|
|
1739
|
+
...(sor.invalidated_by.length ? [`- invalidated by: ${sor.invalidated_by.join(", ")}`] : [])].join("\n") || "(nothing on record for this subject)"
|
|
1740
|
+
: "";
|
|
1741
|
+
return stateResult(`${response.receipt_id} · ${summary}${deniedNote}${stateText ? `\n\nState of record:\n${stateText}` : ""}\n\n${envelope.text}`, response);
|
|
1714
1742
|
}
|
|
1715
1743
|
catch (e) {
|
|
1716
1744
|
return stateRefusal(e);
|
|
@@ -1723,9 +1751,11 @@ export function buildServerWithRootControl(initialRoot) {
|
|
|
1723
1751
|
outputSchema: WriteResultSchema.shape,
|
|
1724
1752
|
}, async ({ cwd: _cwd, ...input }) => {
|
|
1725
1753
|
try {
|
|
1726
|
-
|
|
1754
|
+
// Same cross-process lock `hunch serve` takes: a second agent writing over stdio must
|
|
1755
|
+
// not race the HTTP server between the ledger read and the record write.
|
|
1756
|
+
const result = await withWriteLock(hunchPaths(root).hunch, () => writeState(store, { schema: STATE_WRITE_VERSION, ...input }, {
|
|
1727
1757
|
flush: (isPrivate, message) => flushCapture(store, hunchPaths(root).hunch, isPrivate, message, startupTeamRoute ?? undefined),
|
|
1728
|
-
});
|
|
1758
|
+
}));
|
|
1729
1759
|
return stateResult(`${result.outcome} ${result.record_id} (${result.durability}) ${result.record_hash}`, result);
|
|
1730
1760
|
}
|
|
1731
1761
|
catch (e) {
|
|
@@ -2290,7 +2320,9 @@ export function buildServerWithRootControl(initialRoot) {
|
|
|
2290
2320
|
return {
|
|
2291
2321
|
server,
|
|
2292
2322
|
getRoot: () => root,
|
|
2293
|
-
setRoot
|
|
2323
|
+
setRoot: (next) => { if (!pinned)
|
|
2324
|
+
setRoot(next); },
|
|
2325
|
+
pinned,
|
|
2294
2326
|
cancelPendingRoot: () => { pendingRoot = null; },
|
|
2295
2327
|
};
|
|
2296
2328
|
}
|
|
@@ -2309,6 +2341,8 @@ function provLine(record) {
|
|
|
2309
2341
|
/** Query client roots after initialization and follow later list changes.
|
|
2310
2342
|
* Generation ordering prevents a slow stale roots/list response from winning. */
|
|
2311
2343
|
export function wireClientRoots(control, fallback) {
|
|
2344
|
+
if (control.pinned)
|
|
2345
|
+
return; // `hunch mcp --root`: the client's workspace is not this server's store
|
|
2312
2346
|
let generation = 0;
|
|
2313
2347
|
const syncRoots = async () => {
|
|
2314
2348
|
const mine = ++generation;
|
|
@@ -2346,12 +2380,14 @@ export function wireClientRoots(control, fallback) {
|
|
|
2346
2380
|
control.server.server.setNotificationHandler(RootsListChangedNotificationSchema, async () => { await syncRoots(); });
|
|
2347
2381
|
}
|
|
2348
2382
|
/** Start the stdio server (called by `hunch mcp`). */
|
|
2349
|
-
export async function startServer(cwd = process.cwd()) {
|
|
2350
|
-
const fallback = findRoot(cwd);
|
|
2351
|
-
const control = buildServerWithRootControl(fallback);
|
|
2383
|
+
export async function startServer(cwd = process.cwd(), options = {}) {
|
|
2384
|
+
const fallback = options.pinned ? cwd : findRoot(cwd);
|
|
2385
|
+
const control = buildServerWithRootControl(fallback, options);
|
|
2352
2386
|
wireClientRoots(control, fallback);
|
|
2353
2387
|
const transport = new StdioServerTransport();
|
|
2354
2388
|
await control.server.connect(transport);
|
|
2355
|
-
console.error(
|
|
2389
|
+
console.error(control.pinned
|
|
2390
|
+
? `[hunch-mcp] serving Hunch over stdio (pinned root ${control.getRoot()}; client roots ignored)`
|
|
2391
|
+
: `[hunch-mcp] serving Hunch over stdio (spawn root ${control.getRoot()}; resolving client roots…)`);
|
|
2356
2392
|
}
|
|
2357
2393
|
//# sourceMappingURL=server.js.map
|
package/dist/serve/config.js
CHANGED
|
@@ -34,6 +34,11 @@ export const ServeConfigSchema = z.object({
|
|
|
34
34
|
partitions: z.array(PartitionConfigSchema).min(1).max(256),
|
|
35
35
|
principals: z.array(PrincipalConfigSchema).max(1024).default([]),
|
|
36
36
|
}).strict();
|
|
37
|
+
export const PARTITION_GITIGNORE = [
|
|
38
|
+
"# hunch serve partition — derived runtime artifacts (regenerable from .hunch/*.json)",
|
|
39
|
+
".hunch/*.sqlite", ".hunch/*.sqlite-shm", ".hunch/*.sqlite-wal", ".hunch/*.sqlite-journal",
|
|
40
|
+
".hunch/**/*.tmp*", ".hunch/write.lock", ".hunch/.hunch-commit.lock", ".hunch/local.json", ".hunch/events.log", "",
|
|
41
|
+
].join("\n");
|
|
37
42
|
export function hashToken(token) {
|
|
38
43
|
return createHash("sha256").update(token, "utf8").digest("hex");
|
|
39
44
|
}
|
|
@@ -104,6 +109,11 @@ export function initServeConfig(opts) {
|
|
|
104
109
|
const manifest = resolve(hunchDir, "manifest.json");
|
|
105
110
|
if (!existsSync(manifest))
|
|
106
111
|
writeFileAtomic(manifest, JSON.stringify({ schema_version: 3 }, null, 2) + "\n");
|
|
112
|
+
// A served partition is meant to be its own git repository: keep the derived index, temp
|
|
113
|
+
// files and locks out of it so every auto-commit is records + ledger only.
|
|
114
|
+
const ignore = resolve(root, ".gitignore");
|
|
115
|
+
if (!existsSync(ignore))
|
|
116
|
+
writeFileAtomic(ignore, PARTITION_GITIGNORE);
|
|
107
117
|
const partitions = existing ? existing.partitions.filter((p) => scopePath(p.scope) !== scopePath(opts.scope)) : [];
|
|
108
118
|
const partition = { scope: opts.scope, root };
|
|
109
119
|
partitions.push(partition);
|
|
@@ -118,6 +118,7 @@ export function readState(store, input) {
|
|
|
118
118
|
profile: request.profile ?? "builder",
|
|
119
119
|
});
|
|
120
120
|
let stateOfRecord = null;
|
|
121
|
+
const records = {};
|
|
121
122
|
const denied = new Map();
|
|
122
123
|
if (request.subject !== undefined) {
|
|
123
124
|
const subject = request.subject;
|
|
@@ -135,6 +136,10 @@ export function readState(store, input) {
|
|
|
135
136
|
}
|
|
136
137
|
return scope;
|
|
137
138
|
};
|
|
139
|
+
const keep = (facet, record, scope) => {
|
|
140
|
+
records[record.id] = record;
|
|
141
|
+
return refOf(facet, record, scope);
|
|
142
|
+
};
|
|
138
143
|
if (facets.has("decisions"))
|
|
139
144
|
for (const d of store.recs("decisions")) {
|
|
140
145
|
if (d.topic !== subject && d.id !== subject)
|
|
@@ -143,7 +148,7 @@ export function readState(store, input) {
|
|
|
143
148
|
if (!scope)
|
|
144
149
|
continue;
|
|
145
150
|
if (isLive(d))
|
|
146
|
-
current.push(
|
|
151
|
+
current.push(keep("decisions", d, scope));
|
|
147
152
|
}
|
|
148
153
|
if (facets.has("constraints"))
|
|
149
154
|
for (const c of store.recs("constraints")) {
|
|
@@ -153,7 +158,7 @@ export function readState(store, input) {
|
|
|
153
158
|
if (!scope)
|
|
154
159
|
continue;
|
|
155
160
|
if (c.status === "active" && c.valid_to == null)
|
|
156
|
-
inForce.push(
|
|
161
|
+
inForce.push(keep("constraints", c, scope));
|
|
157
162
|
}
|
|
158
163
|
if (facets.has("receipts"))
|
|
159
164
|
for (const r of store.recs("receipts")) {
|
|
@@ -164,7 +169,7 @@ export function readState(store, input) {
|
|
|
164
169
|
if (!scope)
|
|
165
170
|
continue;
|
|
166
171
|
if (r.state === "succeeded" || r.state === "verified")
|
|
167
|
-
done.push(
|
|
172
|
+
done.push(keep("receipts", r, scope));
|
|
168
173
|
if (r.invalidates.includes(subject))
|
|
169
174
|
invalidatedBy.add(r.id);
|
|
170
175
|
}
|
|
@@ -176,7 +181,7 @@ export function readState(store, input) {
|
|
|
176
181
|
if (!scope)
|
|
177
182
|
continue;
|
|
178
183
|
if ((c.status === "open" || c.status === "waiting") && c.valid_to == null)
|
|
179
|
-
inForce.push(
|
|
184
|
+
inForce.push(keep("commitments", c, scope));
|
|
180
185
|
}
|
|
181
186
|
if (facets.has("derived"))
|
|
182
187
|
for (const d of store.recs("derived")) {
|
|
@@ -186,7 +191,7 @@ export function readState(store, input) {
|
|
|
186
191
|
if (!scope)
|
|
187
192
|
continue;
|
|
188
193
|
if (d.state === "current" && d.valid_to == null) {
|
|
189
|
-
current.push(
|
|
194
|
+
current.push(keep("derived", d, scope));
|
|
190
195
|
dependsOn.push(...d.dependencies);
|
|
191
196
|
}
|
|
192
197
|
}
|
|
@@ -198,7 +203,7 @@ export function readState(store, input) {
|
|
|
198
203
|
if (!scope)
|
|
199
204
|
continue;
|
|
200
205
|
if (e.lifecycle === "active")
|
|
201
|
-
current.push(
|
|
206
|
+
current.push(keep("entities", e, scope));
|
|
202
207
|
}
|
|
203
208
|
if (facets.has("relationships"))
|
|
204
209
|
for (const r of store.recs("relationships")) {
|
|
@@ -207,7 +212,7 @@ export function readState(store, input) {
|
|
|
207
212
|
const scope = admit("relationships", r);
|
|
208
213
|
if (!scope)
|
|
209
214
|
continue;
|
|
210
|
-
current.push(
|
|
215
|
+
current.push(keep("relationships", r, scope));
|
|
211
216
|
}
|
|
212
217
|
stateOfRecord = { subject, current, in_force: inForce, done, depends_on: dependsOn, invalidated_by: [...invalidatedBy].sort() };
|
|
213
218
|
}
|
|
@@ -217,6 +222,7 @@ export function readState(store, input) {
|
|
|
217
222
|
scope: request.scope,
|
|
218
223
|
state_of_record: stateOfRecord,
|
|
219
224
|
denied_scopes: [...denied.values()],
|
|
225
|
+
...(stateOfRecord ? { records } : {}),
|
|
220
226
|
});
|
|
221
227
|
assertReadWithinGrants(request.principal, response);
|
|
222
228
|
return { response, envelope };
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://www.hunchmemory.com",
|
|
10
|
-
"version": "1.26.
|
|
10
|
+
"version": "1.26.2",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@davesheffer/hunch",
|
|
16
|
-
"version": "1.26.
|
|
16
|
+
"version": "1.26.2",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|