backpack-ontology 0.2.25 → 0.3.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 +153 -3
- package/dist/bin/backpack-benchmark.d.ts +3 -0
- package/dist/bin/backpack-benchmark.d.ts.map +1 -0
- package/dist/bin/backpack-benchmark.js +213 -0
- package/dist/bin/backpack-benchmark.js.map +1 -0
- package/dist/bin/backpack.js +3 -3
- package/dist/bin/backpack.js.map +1 -1
- package/dist/bin/init.js +8 -11
- package/dist/bin/init.js.map +1 -1
- package/dist/core/backpack.d.ts +69 -2
- package/dist/core/backpack.d.ts.map +1 -1
- package/dist/core/backpack.js +205 -3
- package/dist/core/backpack.js.map +1 -1
- package/dist/core/draft.d.ts +42 -0
- package/dist/core/draft.d.ts.map +1 -0
- package/dist/core/draft.js +232 -0
- package/dist/core/draft.js.map +1 -0
- package/dist/core/events.d.ts +114 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +375 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/graph.d.ts +3 -1
- package/dist/core/graph.d.ts.map +1 -1
- package/dist/core/graph.js +50 -0
- package/dist/core/graph.js.map +1 -1
- package/dist/core/hooks.d.ts +11 -3
- package/dist/core/hooks.d.ts.map +1 -1
- package/dist/core/hooks.js +53 -47
- package/dist/core/hooks.js.map +1 -1
- package/dist/core/normalize.d.ts +28 -0
- package/dist/core/normalize.d.ts.map +1 -0
- package/dist/core/normalize.js +133 -0
- package/dist/core/normalize.js.map +1 -0
- package/dist/core/remote-fetch.d.ts +50 -0
- package/dist/core/remote-fetch.d.ts.map +1 -0
- package/dist/core/remote-fetch.js +338 -0
- package/dist/core/remote-fetch.js.map +1 -0
- package/dist/core/remote-registry.d.ts +95 -0
- package/dist/core/remote-registry.d.ts.map +1 -0
- package/dist/core/remote-registry.js +296 -0
- package/dist/core/remote-registry.js.map +1 -0
- package/dist/core/remote-schema.d.ts +31 -0
- package/dist/core/remote-schema.d.ts.map +1 -0
- package/dist/core/remote-schema.js +252 -0
- package/dist/core/remote-schema.js.map +1 -0
- package/dist/core/role-audit.d.ts +20 -0
- package/dist/core/role-audit.d.ts.map +1 -0
- package/dist/core/role-audit.js +197 -0
- package/dist/core/role-audit.js.map +1 -0
- package/dist/core/telemetry.d.ts +2 -0
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js +16 -0
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/token-estimate.d.ts +16 -0
- package/dist/core/token-estimate.d.ts.map +1 -0
- package/dist/core/token-estimate.js +29 -0
- package/dist/core/token-estimate.js.map +1 -0
- package/dist/core/types.d.ts +30 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +9 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/bulk-tools.d.ts.map +1 -1
- package/dist/mcp/tools/bulk-tools.js +80 -12
- package/dist/mcp/tools/bulk-tools.js.map +1 -1
- package/dist/mcp/tools/edge-tools.d.ts.map +1 -1
- package/dist/mcp/tools/edge-tools.js +14 -18
- package/dist/mcp/tools/edge-tools.js.map +1 -1
- package/dist/mcp/tools/error-helpers.d.ts +16 -0
- package/dist/mcp/tools/error-helpers.d.ts.map +1 -0
- package/dist/mcp/tools/error-helpers.js +34 -0
- package/dist/mcp/tools/error-helpers.js.map +1 -0
- package/dist/mcp/tools/intelligence-tools.d.ts.map +1 -1
- package/dist/mcp/tools/intelligence-tools.js +28 -18
- package/dist/mcp/tools/intelligence-tools.js.map +1 -1
- package/dist/mcp/tools/node-tools.d.ts.map +1 -1
- package/dist/mcp/tools/node-tools.js +43 -40
- package/dist/mcp/tools/node-tools.js.map +1 -1
- package/dist/mcp/tools/ontology-tools.d.ts.map +1 -1
- package/dist/mcp/tools/ontology-tools.js +237 -12
- package/dist/mcp/tools/ontology-tools.js.map +1 -1
- package/dist/mcp/tools/remote-tools.d.ts +5 -0
- package/dist/mcp/tools/remote-tools.d.ts.map +1 -0
- package/dist/mcp/tools/remote-tools.js +295 -0
- package/dist/mcp/tools/remote-tools.js.map +1 -0
- package/dist/storage/backpack-app-backend.d.ts +1 -1
- package/dist/storage/backpack-app-backend.d.ts.map +1 -1
- package/dist/storage/backpack-app-backend.js +3 -1
- package/dist/storage/backpack-app-backend.js.map +1 -1
- package/dist/storage/event-sourced-backend.d.ts +174 -0
- package/dist/storage/event-sourced-backend.d.ts.map +1 -0
- package/dist/storage/event-sourced-backend.js +840 -0
- package/dist/storage/event-sourced-backend.js.map +1 -0
- package/dist/storage/json-file-backend.d.ts +1 -72
- package/dist/storage/json-file-backend.d.ts.map +1 -1
- package/dist/storage/json-file-backend.js +11 -549
- package/dist/storage/json-file-backend.js.map +1 -1
- package/package.json +3 -3
- package/hooks/auto-capture-prompt.md +0 -40
- package/hooks/hooks.json +0 -16
- package/hooks/suggest-viewer.sh +0 -4
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Local registry of remote learning graphs.
|
|
3
|
+
//
|
|
4
|
+
// Stores user subscriptions to graphs hosted at HTTPS URLs.
|
|
5
|
+
// Persists to ~/.local/share/backpack/remotes.json.
|
|
6
|
+
// Caches fetched graph contents to ~/.local/share/backpack/remote-cache/<name>.json.
|
|
7
|
+
//
|
|
8
|
+
// This module owns the registry file and the cache directory. It does
|
|
9
|
+
// NOT fetch URLs (that's remote-fetch.ts) and does NOT validate graph
|
|
10
|
+
// content (that's remote-schema.ts). It glues those together and
|
|
11
|
+
// persists state.
|
|
12
|
+
// ============================================================
|
|
13
|
+
import * as fs from "node:fs/promises";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import * as crypto from "node:crypto";
|
|
16
|
+
import { dataDir } from "./paths.js";
|
|
17
|
+
import { remoteFetch, RemoteFetchError } from "./remote-fetch.js";
|
|
18
|
+
import { validateRemoteGraph, RemoteSchemaError } from "./remote-schema.js";
|
|
19
|
+
const REGISTRY_VERSION = 1;
|
|
20
|
+
const NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
21
|
+
// --- Errors ---
|
|
22
|
+
export class RemoteRegistryError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(message, code) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = code;
|
|
27
|
+
this.name = "RemoteRegistryError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// --- Helpers ---
|
|
31
|
+
function emptyRegistry() {
|
|
32
|
+
return { version: REGISTRY_VERSION, remotes: [] };
|
|
33
|
+
}
|
|
34
|
+
function validateName(name) {
|
|
35
|
+
if (typeof name !== "string" || !NAME_RE.test(name)) {
|
|
36
|
+
throw new RemoteRegistryError(`invalid remote name '${name}': must match /^[a-z0-9][a-z0-9_-]{0,63}$/`, "INVALID_NAME");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function sha256Hex(s) {
|
|
40
|
+
return crypto.createHash("sha256").update(s).digest("hex");
|
|
41
|
+
}
|
|
42
|
+
// --- The registry class ---
|
|
43
|
+
export class RemoteRegistry {
|
|
44
|
+
baseDir;
|
|
45
|
+
constructor(baseDir) {
|
|
46
|
+
this.baseDir = baseDir ?? dataDir();
|
|
47
|
+
}
|
|
48
|
+
registryFile() {
|
|
49
|
+
return path.join(this.baseDir, "remotes.json");
|
|
50
|
+
}
|
|
51
|
+
cacheDir() {
|
|
52
|
+
return path.join(this.baseDir, "remote-cache");
|
|
53
|
+
}
|
|
54
|
+
cacheFile(name) {
|
|
55
|
+
validateName(name);
|
|
56
|
+
const resolved = path.resolve(this.cacheDir(), `${name}.json`);
|
|
57
|
+
const cacheRoot = path.resolve(this.cacheDir());
|
|
58
|
+
// Defense in depth: ensure the resolved path is inside the cache dir
|
|
59
|
+
if (!resolved.startsWith(cacheRoot + path.sep) && resolved !== cacheRoot) {
|
|
60
|
+
throw new RemoteRegistryError(`cache path escapes cache directory`, "PATH_TRAVERSAL");
|
|
61
|
+
}
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Ensures the base directory and cache directory exist.
|
|
66
|
+
* Safe to call multiple times.
|
|
67
|
+
*/
|
|
68
|
+
async initialize() {
|
|
69
|
+
await fs.mkdir(this.baseDir, { recursive: true });
|
|
70
|
+
await fs.mkdir(this.cacheDir(), { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Reads the registry file. Returns an empty registry if the file
|
|
74
|
+
* doesn't exist yet.
|
|
75
|
+
*/
|
|
76
|
+
async load() {
|
|
77
|
+
try {
|
|
78
|
+
const text = await fs.readFile(this.registryFile(), "utf8");
|
|
79
|
+
const parsed = JSON.parse(text);
|
|
80
|
+
if (typeof parsed !== "object" ||
|
|
81
|
+
parsed === null ||
|
|
82
|
+
parsed.version !== REGISTRY_VERSION ||
|
|
83
|
+
!Array.isArray(parsed.remotes)) {
|
|
84
|
+
throw new RemoteRegistryError("registry file is malformed", "CORRUPT_REGISTRY");
|
|
85
|
+
}
|
|
86
|
+
return parsed;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (err.code === "ENOENT") {
|
|
90
|
+
return emptyRegistry();
|
|
91
|
+
}
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Atomically writes the registry file.
|
|
97
|
+
*/
|
|
98
|
+
async save(reg) {
|
|
99
|
+
const tmp = `${this.registryFile()}.tmp`;
|
|
100
|
+
await fs.writeFile(tmp, JSON.stringify(reg, null, 2), "utf8");
|
|
101
|
+
await fs.rename(tmp, this.registryFile());
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* List all registered remotes.
|
|
105
|
+
*/
|
|
106
|
+
async list() {
|
|
107
|
+
const reg = await this.load();
|
|
108
|
+
return reg.remotes;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Look up a single remote by name.
|
|
112
|
+
*/
|
|
113
|
+
async get(name) {
|
|
114
|
+
validateName(name);
|
|
115
|
+
const reg = await this.load();
|
|
116
|
+
return reg.remotes.find((r) => r.name === name) ?? null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Register a new remote: validate the name, fetch the URL, validate
|
|
120
|
+
* the graph schema, write to cache, and append to the registry.
|
|
121
|
+
*
|
|
122
|
+
* Throws if the name already exists, if the URL fails to fetch, or
|
|
123
|
+
* if the schema validation fails.
|
|
124
|
+
*
|
|
125
|
+
* The caller is responsible for ensuring the name does not collide
|
|
126
|
+
* with a local graph (this module doesn't know about local graphs).
|
|
127
|
+
*/
|
|
128
|
+
async register(opts) {
|
|
129
|
+
validateName(opts.name);
|
|
130
|
+
await this.initialize();
|
|
131
|
+
const reg = await this.load();
|
|
132
|
+
if (reg.remotes.some((r) => r.name === opts.name)) {
|
|
133
|
+
throw new RemoteRegistryError(`remote '${opts.name}' is already registered`, "DUPLICATE_NAME");
|
|
134
|
+
}
|
|
135
|
+
// Fetch
|
|
136
|
+
let result;
|
|
137
|
+
try {
|
|
138
|
+
result = await remoteFetch(opts.url);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
if (err instanceof RemoteFetchError) {
|
|
142
|
+
throw new RemoteRegistryError(`fetch failed: ${err.message}`, err.code);
|
|
143
|
+
}
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
// Parse + validate
|
|
147
|
+
let parsed;
|
|
148
|
+
try {
|
|
149
|
+
parsed = JSON.parse(result.body);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
throw new RemoteRegistryError(`response is not valid JSON: ${err.message}`, "INVALID_JSON");
|
|
153
|
+
}
|
|
154
|
+
let validated;
|
|
155
|
+
try {
|
|
156
|
+
validated = validateRemoteGraph(parsed);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (err instanceof RemoteSchemaError) {
|
|
160
|
+
throw new RemoteRegistryError(`schema validation failed: ${err.message}`, "SCHEMA_ERROR");
|
|
161
|
+
}
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
// Write cache atomically
|
|
165
|
+
const cachePath = this.cacheFile(opts.name);
|
|
166
|
+
const cacheBody = JSON.stringify(validated.data);
|
|
167
|
+
const tmp = `${cachePath}.tmp`;
|
|
168
|
+
await fs.writeFile(tmp, cacheBody, "utf8");
|
|
169
|
+
await fs.rename(tmp, cachePath);
|
|
170
|
+
const now = new Date().toISOString();
|
|
171
|
+
const entry = {
|
|
172
|
+
name: opts.name,
|
|
173
|
+
url: opts.url,
|
|
174
|
+
source: opts.source,
|
|
175
|
+
addedAt: now,
|
|
176
|
+
lastFetched: now,
|
|
177
|
+
etag: result.etag,
|
|
178
|
+
sha256: sha256Hex(cacheBody),
|
|
179
|
+
pinned: !!opts.pin,
|
|
180
|
+
sizeBytes: cacheBody.length,
|
|
181
|
+
};
|
|
182
|
+
reg.remotes.push(entry);
|
|
183
|
+
await this.save(reg);
|
|
184
|
+
return entry;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Refetch a registered remote. Uses ETag for conditional GET.
|
|
188
|
+
*
|
|
189
|
+
* If pinned and the SHA256 changes, throws RemoteRegistryError with
|
|
190
|
+
* code "PIN_VIOLATION" without overwriting the cache.
|
|
191
|
+
*/
|
|
192
|
+
async refresh(name) {
|
|
193
|
+
validateName(name);
|
|
194
|
+
const reg = await this.load();
|
|
195
|
+
const existing = reg.remotes.find((r) => r.name === name);
|
|
196
|
+
if (!existing) {
|
|
197
|
+
throw new RemoteRegistryError(`remote '${name}' is not registered`, "NOT_FOUND");
|
|
198
|
+
}
|
|
199
|
+
let result;
|
|
200
|
+
try {
|
|
201
|
+
result = await remoteFetch(existing.url, {
|
|
202
|
+
ifNoneMatch: existing.etag ?? undefined,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
if (err instanceof RemoteFetchError) {
|
|
207
|
+
throw new RemoteRegistryError(`fetch failed: ${err.message}`, err.code);
|
|
208
|
+
}
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
if (result.notModified) {
|
|
212
|
+
existing.lastFetched = new Date().toISOString();
|
|
213
|
+
await this.save(reg);
|
|
214
|
+
return { entry: existing, changed: false, notModified: true };
|
|
215
|
+
}
|
|
216
|
+
let parsed;
|
|
217
|
+
try {
|
|
218
|
+
parsed = JSON.parse(result.body);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
throw new RemoteRegistryError(`response is not valid JSON: ${err.message}`, "INVALID_JSON");
|
|
222
|
+
}
|
|
223
|
+
let validated;
|
|
224
|
+
try {
|
|
225
|
+
validated = validateRemoteGraph(parsed);
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
if (err instanceof RemoteSchemaError) {
|
|
229
|
+
throw new RemoteRegistryError(`schema validation failed: ${err.message}`, "SCHEMA_ERROR");
|
|
230
|
+
}
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
233
|
+
const cacheBody = JSON.stringify(validated.data);
|
|
234
|
+
const newSha = sha256Hex(cacheBody);
|
|
235
|
+
const changed = newSha !== existing.sha256;
|
|
236
|
+
if (existing.pinned && changed) {
|
|
237
|
+
throw new RemoteRegistryError(`remote '${name}' is pinned and content has changed (was ${existing.sha256}, now ${newSha})`, "PIN_VIOLATION");
|
|
238
|
+
}
|
|
239
|
+
const cachePath = this.cacheFile(name);
|
|
240
|
+
const tmp = `${cachePath}.tmp`;
|
|
241
|
+
await fs.writeFile(tmp, cacheBody, "utf8");
|
|
242
|
+
await fs.rename(tmp, cachePath);
|
|
243
|
+
existing.lastFetched = new Date().toISOString();
|
|
244
|
+
existing.etag = result.etag;
|
|
245
|
+
existing.sha256 = newSha;
|
|
246
|
+
existing.sizeBytes = cacheBody.length;
|
|
247
|
+
await this.save(reg);
|
|
248
|
+
return { entry: existing, changed, notModified: false };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Remove a remote from the registry and delete its cache file.
|
|
252
|
+
*/
|
|
253
|
+
async unregister(name) {
|
|
254
|
+
validateName(name);
|
|
255
|
+
const reg = await this.load();
|
|
256
|
+
const idx = reg.remotes.findIndex((r) => r.name === name);
|
|
257
|
+
if (idx === -1) {
|
|
258
|
+
throw new RemoteRegistryError(`remote '${name}' is not registered`, "NOT_FOUND");
|
|
259
|
+
}
|
|
260
|
+
reg.remotes.splice(idx, 1);
|
|
261
|
+
await this.save(reg);
|
|
262
|
+
// Best-effort cache deletion
|
|
263
|
+
try {
|
|
264
|
+
await fs.unlink(this.cacheFile(name));
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
if (err.code !== "ENOENT") {
|
|
268
|
+
throw err;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Load the cached graph contents for a registered remote.
|
|
274
|
+
* Throws if the remote is not registered or the cache file is missing.
|
|
275
|
+
*/
|
|
276
|
+
async loadCached(name) {
|
|
277
|
+
validateName(name);
|
|
278
|
+
const reg = await this.load();
|
|
279
|
+
const entry = reg.remotes.find((r) => r.name === name);
|
|
280
|
+
if (!entry) {
|
|
281
|
+
throw new RemoteRegistryError(`remote '${name}' is not registered`, "NOT_FOUND");
|
|
282
|
+
}
|
|
283
|
+
let text;
|
|
284
|
+
try {
|
|
285
|
+
text = await fs.readFile(this.cacheFile(name), "utf8");
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
if (err.code === "ENOENT") {
|
|
289
|
+
throw new RemoteRegistryError(`cache file for '${name}' is missing — try refreshing`, "CACHE_MISSING");
|
|
290
|
+
}
|
|
291
|
+
throw err;
|
|
292
|
+
}
|
|
293
|
+
return JSON.parse(text);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=remote-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-registry.js","sourceRoot":"","sources":["../../src/core/remote-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { LearningGraphData } from "./types.js";
|
|
2
|
+
export declare const REMOTE_GRAPH_LIMITS: {
|
|
3
|
+
readonly maxNodes: 50000;
|
|
4
|
+
readonly maxEdges: 200000;
|
|
5
|
+
readonly maxPropertyKeys: 64;
|
|
6
|
+
readonly maxPropertyKeyLength: 128;
|
|
7
|
+
readonly maxPropertyStringLength: 16384;
|
|
8
|
+
readonly maxArrayLength: 256;
|
|
9
|
+
readonly maxIdLength: 256;
|
|
10
|
+
readonly maxTypeLength: 128;
|
|
11
|
+
readonly maxNameLength: 256;
|
|
12
|
+
readonly maxDescriptionLength: 4096;
|
|
13
|
+
};
|
|
14
|
+
export declare class RemoteSchemaError extends Error {
|
|
15
|
+
readonly path: string;
|
|
16
|
+
constructor(message: string, path: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validates a parsed JSON value against the remote graph schema.
|
|
20
|
+
* Throws RemoteSchemaError on any violation.
|
|
21
|
+
*
|
|
22
|
+
* The returned object is a fresh, type-safe LearningGraphData. Edges
|
|
23
|
+
* that reference node IDs not in the graph are dropped (not an error —
|
|
24
|
+
* remote graphs may legitimately have hanging references after partial
|
|
25
|
+
* exports). Use the `droppedEdges` count from the result if you care.
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateRemoteGraph(raw: unknown): {
|
|
28
|
+
data: LearningGraphData;
|
|
29
|
+
droppedEdges: number;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=remote-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-schema.d.ts","sourceRoot":"","sources":["../../src/core/remote-schema.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,iBAAiB,EAAc,MAAM,YAAY,CAAC;AAIhE,eAAO,MAAM,mBAAmB;;;;;;;;;;;CAWtB,CAAC;AAIX,qBAAa,iBAAkB,SAAQ,KAAK;aAGxB,IAAI,EAAE,MAAM;gBAD5B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM;CAK/B;AAwND;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG;IACjD,IAAI,EAAE,iBAAiB,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB,CAmGA"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Strict validation for remote learning graphs.
|
|
3
|
+
//
|
|
4
|
+
// Anything fetched from a third-party URL must pass through this
|
|
5
|
+
// validator before any other code touches it. The goal is to:
|
|
6
|
+
// 1. Reject malformed input that could crash the parser
|
|
7
|
+
// 2. Reject oversized graphs that could exhaust memory
|
|
8
|
+
// 3. Reject unknown structures that could carry future exploits
|
|
9
|
+
// 4. Coerce property values to a safe primitive subset
|
|
10
|
+
//
|
|
11
|
+
// This is intentionally STRICTER than the schema used for local
|
|
12
|
+
// graphs. Local graphs are trusted (the user wrote them); remote
|
|
13
|
+
// graphs are not.
|
|
14
|
+
// ============================================================
|
|
15
|
+
// --- Limits ---
|
|
16
|
+
export const REMOTE_GRAPH_LIMITS = {
|
|
17
|
+
maxNodes: 50_000,
|
|
18
|
+
maxEdges: 200_000,
|
|
19
|
+
maxPropertyKeys: 64,
|
|
20
|
+
maxPropertyKeyLength: 128,
|
|
21
|
+
maxPropertyStringLength: 16_384,
|
|
22
|
+
maxArrayLength: 256,
|
|
23
|
+
maxIdLength: 256,
|
|
24
|
+
maxTypeLength: 128,
|
|
25
|
+
maxNameLength: 256,
|
|
26
|
+
maxDescriptionLength: 4_096,
|
|
27
|
+
};
|
|
28
|
+
// --- Errors ---
|
|
29
|
+
export class RemoteSchemaError extends Error {
|
|
30
|
+
path;
|
|
31
|
+
constructor(message, path) {
|
|
32
|
+
super(`${path}: ${message}`);
|
|
33
|
+
this.path = path;
|
|
34
|
+
this.name = "RemoteSchemaError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// --- Validators ---
|
|
38
|
+
function isPlainObject(value) {
|
|
39
|
+
return (typeof value === "object" &&
|
|
40
|
+
value !== null &&
|
|
41
|
+
!Array.isArray(value) &&
|
|
42
|
+
Object.getPrototypeOf(value) === Object.prototype);
|
|
43
|
+
}
|
|
44
|
+
function validateString(value, path, maxLength, required = true) {
|
|
45
|
+
if (value === undefined || value === null) {
|
|
46
|
+
if (required) {
|
|
47
|
+
throw new RemoteSchemaError("missing required string", path);
|
|
48
|
+
}
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
if (typeof value !== "string") {
|
|
52
|
+
throw new RemoteSchemaError(`expected string, got ${typeof value}`, path);
|
|
53
|
+
}
|
|
54
|
+
if (value.length > maxLength) {
|
|
55
|
+
throw new RemoteSchemaError(`string exceeds max length ${maxLength} (got ${value.length})`, path);
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Validates a property value. Allowed: string, number (finite), boolean,
|
|
61
|
+
* or array of those primitives. Rejects nested objects, functions, symbols,
|
|
62
|
+
* bigints, and any non-finite numbers.
|
|
63
|
+
*/
|
|
64
|
+
function validatePropertyValue(value, path) {
|
|
65
|
+
if (value === null || value === undefined) {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
if (typeof value === "string") {
|
|
69
|
+
if (value.length > REMOTE_GRAPH_LIMITS.maxPropertyStringLength) {
|
|
70
|
+
throw new RemoteSchemaError(`property string exceeds max length ${REMOTE_GRAPH_LIMITS.maxPropertyStringLength}`, path);
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
if (typeof value === "number") {
|
|
75
|
+
if (!Number.isFinite(value)) {
|
|
76
|
+
throw new RemoteSchemaError("number must be finite", path);
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
if (typeof value === "boolean") {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(value)) {
|
|
84
|
+
if (value.length > REMOTE_GRAPH_LIMITS.maxArrayLength) {
|
|
85
|
+
throw new RemoteSchemaError(`array exceeds max length ${REMOTE_GRAPH_LIMITS.maxArrayLength}`, path);
|
|
86
|
+
}
|
|
87
|
+
return value.map((item, i) => {
|
|
88
|
+
if (item === null || item === undefined)
|
|
89
|
+
return item;
|
|
90
|
+
if (typeof item === "string") {
|
|
91
|
+
if (item.length > REMOTE_GRAPH_LIMITS.maxPropertyStringLength) {
|
|
92
|
+
throw new RemoteSchemaError(`array item string exceeds max length`, `${path}[${i}]`);
|
|
93
|
+
}
|
|
94
|
+
return item;
|
|
95
|
+
}
|
|
96
|
+
if (typeof item === "number") {
|
|
97
|
+
if (!Number.isFinite(item)) {
|
|
98
|
+
throw new RemoteSchemaError("number must be finite", `${path}[${i}]`);
|
|
99
|
+
}
|
|
100
|
+
return item;
|
|
101
|
+
}
|
|
102
|
+
if (typeof item === "boolean")
|
|
103
|
+
return item;
|
|
104
|
+
throw new RemoteSchemaError(`array items must be string, number, or boolean (got ${typeof item})`, `${path}[${i}]`);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
throw new RemoteSchemaError(`properties must be string, number, boolean, null, or array of those (got ${typeof value})`, path);
|
|
108
|
+
}
|
|
109
|
+
function validateProperties(value, path) {
|
|
110
|
+
if (value === undefined || value === null) {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
if (!isPlainObject(value)) {
|
|
114
|
+
throw new RemoteSchemaError("properties must be a plain object", path);
|
|
115
|
+
}
|
|
116
|
+
const keys = Object.keys(value);
|
|
117
|
+
if (keys.length > REMOTE_GRAPH_LIMITS.maxPropertyKeys) {
|
|
118
|
+
throw new RemoteSchemaError(`too many property keys (max ${REMOTE_GRAPH_LIMITS.maxPropertyKeys})`, path);
|
|
119
|
+
}
|
|
120
|
+
const out = {};
|
|
121
|
+
for (const key of keys) {
|
|
122
|
+
if (key.length > REMOTE_GRAPH_LIMITS.maxPropertyKeyLength) {
|
|
123
|
+
throw new RemoteSchemaError(`property key exceeds max length`, `${path}.${key.slice(0, 32)}...`);
|
|
124
|
+
}
|
|
125
|
+
// Reject prototype-pollution attempts
|
|
126
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
127
|
+
throw new RemoteSchemaError(`property key '${key}' is not allowed`, path);
|
|
128
|
+
}
|
|
129
|
+
out[key] = validatePropertyValue(value[key], `${path}.${key}`);
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
function validateNode(raw, index) {
|
|
134
|
+
const path = `nodes[${index}]`;
|
|
135
|
+
if (!isPlainObject(raw)) {
|
|
136
|
+
throw new RemoteSchemaError("node must be a plain object", path);
|
|
137
|
+
}
|
|
138
|
+
const id = validateString(raw.id, `${path}.id`, REMOTE_GRAPH_LIMITS.maxIdLength);
|
|
139
|
+
const type = validateString(raw.type, `${path}.type`, REMOTE_GRAPH_LIMITS.maxTypeLength);
|
|
140
|
+
const properties = validateProperties(raw.properties, `${path}.properties`);
|
|
141
|
+
const createdAt = validateString(raw.createdAt, `${path}.createdAt`, 64, false);
|
|
142
|
+
const updatedAt = validateString(raw.updatedAt, `${path}.updatedAt`, 64, false);
|
|
143
|
+
return {
|
|
144
|
+
id,
|
|
145
|
+
type,
|
|
146
|
+
properties,
|
|
147
|
+
createdAt: createdAt || new Date().toISOString(),
|
|
148
|
+
updatedAt: updatedAt || new Date().toISOString(),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function validateEdge(raw, index) {
|
|
152
|
+
const path = `edges[${index}]`;
|
|
153
|
+
if (!isPlainObject(raw)) {
|
|
154
|
+
throw new RemoteSchemaError("edge must be a plain object", path);
|
|
155
|
+
}
|
|
156
|
+
const id = validateString(raw.id, `${path}.id`, REMOTE_GRAPH_LIMITS.maxIdLength);
|
|
157
|
+
const type = validateString(raw.type, `${path}.type`, REMOTE_GRAPH_LIMITS.maxTypeLength);
|
|
158
|
+
const sourceId = validateString(raw.sourceId, `${path}.sourceId`, REMOTE_GRAPH_LIMITS.maxIdLength);
|
|
159
|
+
const targetId = validateString(raw.targetId, `${path}.targetId`, REMOTE_GRAPH_LIMITS.maxIdLength);
|
|
160
|
+
const properties = validateProperties(raw.properties, `${path}.properties`);
|
|
161
|
+
const createdAt = validateString(raw.createdAt, `${path}.createdAt`, 64, false);
|
|
162
|
+
const updatedAt = validateString(raw.updatedAt, `${path}.updatedAt`, 64, false);
|
|
163
|
+
return {
|
|
164
|
+
id,
|
|
165
|
+
type,
|
|
166
|
+
sourceId,
|
|
167
|
+
targetId,
|
|
168
|
+
properties,
|
|
169
|
+
createdAt: createdAt || new Date().toISOString(),
|
|
170
|
+
updatedAt: updatedAt || new Date().toISOString(),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Validates a parsed JSON value against the remote graph schema.
|
|
175
|
+
* Throws RemoteSchemaError on any violation.
|
|
176
|
+
*
|
|
177
|
+
* The returned object is a fresh, type-safe LearningGraphData. Edges
|
|
178
|
+
* that reference node IDs not in the graph are dropped (not an error —
|
|
179
|
+
* remote graphs may legitimately have hanging references after partial
|
|
180
|
+
* exports). Use the `droppedEdges` count from the result if you care.
|
|
181
|
+
*/
|
|
182
|
+
export function validateRemoteGraph(raw) {
|
|
183
|
+
if (!isPlainObject(raw)) {
|
|
184
|
+
throw new RemoteSchemaError("root must be a plain object", "$");
|
|
185
|
+
}
|
|
186
|
+
// Allowed top-level keys only
|
|
187
|
+
const allowedKeys = new Set(["metadata", "nodes", "edges"]);
|
|
188
|
+
for (const key of Object.keys(raw)) {
|
|
189
|
+
if (!allowedKeys.has(key)) {
|
|
190
|
+
throw new RemoteSchemaError(`unknown top-level key '${key}'`, "$");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// metadata
|
|
194
|
+
if (!isPlainObject(raw.metadata)) {
|
|
195
|
+
throw new RemoteSchemaError("metadata must be a plain object", "metadata");
|
|
196
|
+
}
|
|
197
|
+
const metaName = validateString(raw.metadata.name, "metadata.name", REMOTE_GRAPH_LIMITS.maxNameLength);
|
|
198
|
+
const metaDescription = validateString(raw.metadata.description, "metadata.description", REMOTE_GRAPH_LIMITS.maxDescriptionLength, false);
|
|
199
|
+
const metaCreatedAt = validateString(raw.metadata.createdAt, "metadata.createdAt", 64, false);
|
|
200
|
+
const metaUpdatedAt = validateString(raw.metadata.updatedAt, "metadata.updatedAt", 64, false);
|
|
201
|
+
// nodes
|
|
202
|
+
if (!Array.isArray(raw.nodes)) {
|
|
203
|
+
throw new RemoteSchemaError("nodes must be an array", "nodes");
|
|
204
|
+
}
|
|
205
|
+
if (raw.nodes.length > REMOTE_GRAPH_LIMITS.maxNodes) {
|
|
206
|
+
throw new RemoteSchemaError(`node count ${raw.nodes.length} exceeds max ${REMOTE_GRAPH_LIMITS.maxNodes}`, "nodes");
|
|
207
|
+
}
|
|
208
|
+
const nodes = [];
|
|
209
|
+
const nodeIds = new Set();
|
|
210
|
+
for (let i = 0; i < raw.nodes.length; i++) {
|
|
211
|
+
const node = validateNode(raw.nodes[i], i);
|
|
212
|
+
if (nodeIds.has(node.id)) {
|
|
213
|
+
throw new RemoteSchemaError(`duplicate node id '${node.id}'`, `nodes[${i}]`);
|
|
214
|
+
}
|
|
215
|
+
nodeIds.add(node.id);
|
|
216
|
+
nodes.push(node);
|
|
217
|
+
}
|
|
218
|
+
// edges
|
|
219
|
+
if (!Array.isArray(raw.edges)) {
|
|
220
|
+
throw new RemoteSchemaError("edges must be an array", "edges");
|
|
221
|
+
}
|
|
222
|
+
if (raw.edges.length > REMOTE_GRAPH_LIMITS.maxEdges) {
|
|
223
|
+
throw new RemoteSchemaError(`edge count ${raw.edges.length} exceeds max ${REMOTE_GRAPH_LIMITS.maxEdges}`, "edges");
|
|
224
|
+
}
|
|
225
|
+
const edges = [];
|
|
226
|
+
let droppedEdges = 0;
|
|
227
|
+
for (let i = 0; i < raw.edges.length; i++) {
|
|
228
|
+
const edge = validateEdge(raw.edges[i], i);
|
|
229
|
+
// Drop edges that reference nodes outside this graph — these are
|
|
230
|
+
// legitimate orphans, not malicious, but they break traversal
|
|
231
|
+
if (!nodeIds.has(edge.sourceId) || !nodeIds.has(edge.targetId)) {
|
|
232
|
+
droppedEdges++;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
edges.push(edge);
|
|
236
|
+
}
|
|
237
|
+
const now = new Date().toISOString();
|
|
238
|
+
return {
|
|
239
|
+
data: {
|
|
240
|
+
metadata: {
|
|
241
|
+
name: metaName,
|
|
242
|
+
description: metaDescription,
|
|
243
|
+
createdAt: metaCreatedAt || now,
|
|
244
|
+
updatedAt: metaUpdatedAt || now,
|
|
245
|
+
},
|
|
246
|
+
nodes,
|
|
247
|
+
edges,
|
|
248
|
+
},
|
|
249
|
+
droppedEdges,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=remote-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-schema.js","sourceRoot":"","sources":["../../src/core/remote-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Node } from "./types.js";
|
|
2
|
+
export interface RoleAuditCandidate {
|
|
3
|
+
nodeId: string;
|
|
4
|
+
type: string;
|
|
5
|
+
label: string;
|
|
6
|
+
reason: string;
|
|
7
|
+
suggestion: string;
|
|
8
|
+
}
|
|
9
|
+
export interface RoleAuditResult {
|
|
10
|
+
proceduralCandidates: RoleAuditCandidate[];
|
|
11
|
+
briefingCandidates: RoleAuditCandidate[];
|
|
12
|
+
summary: {
|
|
13
|
+
nodesScanned: number;
|
|
14
|
+
proceduralCount: number;
|
|
15
|
+
briefingCount: number;
|
|
16
|
+
cleanCount: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function auditRoles(nodes: Node[]): RoleAuditResult;
|
|
20
|
+
//# sourceMappingURL=role-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-audit.d.ts","sourceRoot":"","sources":["../../src/core/role-audit.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,oBAAoB,EAAE,kBAAkB,EAAE,CAAC;IAC3C,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;IACzC,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AA6KD,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,eAAe,CAwCzD"}
|