@mulmoclaude/core 0.27.0 → 0.29.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/assets/helps/collection-skills.md +4 -2
- package/assets/helps/wiki.md +47 -2
- package/dist/collection/core/ontologyGraph.d.ts +6 -2
- package/dist/collection/index.cjs +4 -4
- package/dist/collection/index.cjs.map +1 -1
- package/dist/collection/index.js +4 -4
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/registry/server/index.cjs +2 -2
- package/dist/collection/registry/server/index.js +2 -2
- package/dist/collection/server/backendAvailability.d.ts +5 -0
- package/dist/collection/server/csvStore.d.ts +0 -5
- package/dist/collection/server/index.cjs +4 -2
- package/dist/collection/server/index.d.ts +1 -0
- package/dist/collection/server/index.js +3 -3
- package/dist/collection/server/manageTool.d.ts +4 -0
- package/dist/collection/server/schemaDocs.d.ts +21 -0
- package/dist/collection/server/store.d.ts +37 -0
- package/dist/collection/server/watchFs.d.ts +17 -0
- package/dist/collection-watchers/index.cjs +114 -203
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.d.ts +1 -1
- package/dist/collection-watchers/index.js +114 -202
- package/dist/collection-watchers/index.js.map +1 -1
- package/dist/collection-watchers/watcher.d.ts +8 -13
- package/dist/{discovery-B9Vfojyy.js → discovery-BbsJwVEq.js} +128 -15
- package/dist/discovery-BbsJwVEq.js.map +1 -0
- package/dist/{discovery-BxbJy2VN.cjs → discovery-Bklck7Ck.cjs} +137 -12
- package/dist/discovery-Bklck7Ck.cjs.map +1 -0
- package/dist/feeds/server/index.cjs +2 -2
- package/dist/feeds/server/index.js +2 -2
- package/dist/google/index.cjs +1 -1
- package/dist/google/index.js +1 -1
- package/dist/{promptSafety-0ZKHX-6J.cjs → promptSafety-BFt2g_wn.cjs} +2 -2
- package/dist/promptSafety-BFt2g_wn.cjs.map +1 -0
- package/dist/{promptSafety-RE1SPxBN.js → promptSafety-cZIeiZtB.js} +3 -3
- package/dist/promptSafety-cZIeiZtB.js.map +1 -0
- package/dist/remote-view/index.cjs +8 -0
- package/dist/remote-view/index.cjs.map +1 -1
- package/dist/remote-view/index.d.ts +7 -0
- package/dist/remote-view/index.js +8 -1
- package/dist/remote-view/index.js.map +1 -1
- package/dist/{server-DOGKfUuD.cjs → server-CghbYZFY.cjs} +199 -22
- package/dist/server-CghbYZFY.cjs.map +1 -0
- package/dist/{server-sGNe-msA.js → server-DlG6ydHO.js} +200 -23
- package/dist/server-DlG6ydHO.js.map +1 -0
- package/dist/whisper/client.cjs.map +1 -1
- package/dist/whisper/client.js.map +1 -1
- package/package.json +1 -1
- package/dist/discovery-B9Vfojyy.js.map +0 -1
- package/dist/discovery-BxbJy2VN.cjs.map +0 -1
- package/dist/promptSafety-0ZKHX-6J.cjs.map +0 -1
- package/dist/promptSafety-RE1SPxBN.js.map +0 -1
- package/dist/server-DOGKfUuD.cjs.map +0 -1
- package/dist/server-sGNe-msA.js.map +0 -1
|
@@ -640,6 +640,18 @@ ${dataJson}
|
|
|
640
640
|
${templateText}`;
|
|
641
641
|
}
|
|
642
642
|
//#endregion
|
|
643
|
+
//#region src/collection/server/backendAvailability.ts
|
|
644
|
+
/** Thrown by a store when its engine or session cannot serve the request. */
|
|
645
|
+
var BackendUnavailableError = class extends Error {
|
|
646
|
+
constructor(message) {
|
|
647
|
+
super(message);
|
|
648
|
+
this.name = "BackendUnavailableError";
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
function isBackendUnavailable(err) {
|
|
652
|
+
return err instanceof BackendUnavailableError;
|
|
653
|
+
}
|
|
654
|
+
//#endregion
|
|
643
655
|
//#region src/collection/core/queryZ.ts
|
|
644
656
|
/** Result-column aliases double as SQL identifiers and JSON keys — keep
|
|
645
657
|
* them to a conservative identifier charset so neither side needs
|
|
@@ -861,13 +873,23 @@ function decodeCsvRecordId(itemId) {
|
|
|
861
873
|
* (date-only when the clock is exactly UTC midnight, matching the `date`
|
|
862
874
|
* field contract), exotic DuckDB values → their string form. Pure +
|
|
863
875
|
* exported for unit tests. */
|
|
876
|
+
/** `JSON.stringify` restricted to what a CSV cell can survive. Returns the
|
|
877
|
+
* serialised value, or `String(value)` when serialisation is impossible —
|
|
878
|
+
* losing the content of one cell is bad, failing the entire query is worse. */
|
|
879
|
+
function safeJsonCell(value) {
|
|
880
|
+
try {
|
|
881
|
+
return JSON.stringify(value, (_key, entry) => typeof entry === "bigint" ? entry.toString() : entry) ?? String(value);
|
|
882
|
+
} catch {
|
|
883
|
+
return String(value);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
864
886
|
function normalizeCsvValue(value) {
|
|
865
887
|
if (typeof value === "bigint") return value <= BigInt(Number.MAX_SAFE_INTEGER) && value >= BigInt(-Number.MAX_SAFE_INTEGER) ? Number(value) : value.toString();
|
|
866
888
|
if (value instanceof Date) {
|
|
867
889
|
const iso = value.toISOString();
|
|
868
890
|
return iso.endsWith("T00:00:00.000Z") ? iso.slice(0, 10) : iso;
|
|
869
891
|
}
|
|
870
|
-
if (value !== null && typeof value === "object") return
|
|
892
|
+
if (value !== null && typeof value === "object") return safeJsonCell(value);
|
|
871
893
|
return value;
|
|
872
894
|
}
|
|
873
895
|
/** One raw DuckDB row → a CollectionItem, or null when the key cell is
|
|
@@ -878,10 +900,11 @@ function normalizeCsvValue(value) {
|
|
|
878
900
|
function csvRowToItem(row, primaryKey) {
|
|
879
901
|
const normalized = Object.fromEntries(Object.entries(row).map(([key, value]) => [key, normalizeCsvValue(value)]));
|
|
880
902
|
const rawKey = normalized[primaryKey];
|
|
881
|
-
|
|
903
|
+
const keyText = require_ids.fieldTextOrNull(rawKey);
|
|
904
|
+
if (keyText === null || keyText === "") return null;
|
|
882
905
|
return {
|
|
883
906
|
...normalized,
|
|
884
|
-
[primaryKey]: encodeCsvRecordId(
|
|
907
|
+
[primaryKey]: encodeCsvRecordId(keyText)
|
|
885
908
|
};
|
|
886
909
|
}
|
|
887
910
|
/** Dedupe by record id, LAST row wins (matches `csvRead`'s last-match
|
|
@@ -1039,7 +1062,7 @@ async function duckDbInstance() {
|
|
|
1039
1062
|
return await instancePromise;
|
|
1040
1063
|
} catch (err) {
|
|
1041
1064
|
instancePromise = null;
|
|
1042
|
-
throw new
|
|
1065
|
+
throw new BackendUnavailableError(`DuckDB is unavailable on this host (@duckdb/node-api failed to load: ${String(err)}) — dataSource collections cannot be read`);
|
|
1043
1066
|
}
|
|
1044
1067
|
}
|
|
1045
1068
|
async function queryCsv(sql, params) {
|
|
@@ -1124,6 +1147,89 @@ async function csvRunQuery(absPath, primaryKey, query, workspaceRoot) {
|
|
|
1124
1147
|
return (await queryCsv(sql, [utf8Path, ...params])).map((row) => Object.fromEntries(Object.entries(row).map(([key, value]) => [key, normalizeCsvValue(value)])));
|
|
1125
1148
|
}
|
|
1126
1149
|
//#endregion
|
|
1150
|
+
//#region src/collection/server/watchFs.ts
|
|
1151
|
+
/** An atomic file replace (editor save, `mv` over the target) surfaces as
|
|
1152
|
+
* 2-3 events. Collapse them so one user action reports one change. */
|
|
1153
|
+
var REPLACE_DEBOUNCE_MS = 300;
|
|
1154
|
+
/** The path to hand `watch()`, with Windows 8.3 short names resolved away.
|
|
1155
|
+
*
|
|
1156
|
+
* ReadDirectoryChangesW reports filenames against the LONG path, but a watch
|
|
1157
|
+
* opened on a short path (`C:\Users\RUNNER~1\…` — what `os.tmpdir()` returns
|
|
1158
|
+
* on GitHub's Windows runners) keeps the short form. libuv's
|
|
1159
|
+
* `assert(!_wcsnicmp(filename, dir, dirlen))` in `src/win/fs-event.c` then
|
|
1160
|
+
* aborts the PROCESS on the first event — a native assert, so neither
|
|
1161
|
+
* `watcher.on("error")` nor a try/catch can contain it.
|
|
1162
|
+
*
|
|
1163
|
+
* POSIX is deliberately left alone: `realpath` there also collapses symlinks
|
|
1164
|
+
* (`/var` → `/private/var` on macOS), which we neither need nor want to
|
|
1165
|
+
* change. A failure falls back to the original path — worst case we are no
|
|
1166
|
+
* worse off than before. */
|
|
1167
|
+
function watchablePath(dir) {
|
|
1168
|
+
if (process.platform !== "win32") return dir;
|
|
1169
|
+
try {
|
|
1170
|
+
return node_fs.realpathSync.native(dir);
|
|
1171
|
+
} catch {
|
|
1172
|
+
return dir;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
/** Watch `dir`, reporting each accepted filename. `accept` decides what is
|
|
1176
|
+
* noise; a null filename always passes (the platform didn't tell us which
|
|
1177
|
+
* file, so the caller must assume the worst). */
|
|
1178
|
+
async function watchDirectory(dir, accept, onHit) {
|
|
1179
|
+
try {
|
|
1180
|
+
await (0, node_fs_promises.mkdir)(dir, { recursive: true });
|
|
1181
|
+
const watcher = (0, node_fs.watch)(watchablePath(dir), { persistent: false }, (_eventType, rawFilename) => {
|
|
1182
|
+
const filename = rawFilename === null ? null : String(rawFilename);
|
|
1183
|
+
if (filename !== null && !accept(filename)) return;
|
|
1184
|
+
onHit(filename);
|
|
1185
|
+
});
|
|
1186
|
+
watcher.on("error", (err) => {
|
|
1187
|
+
log.warn("collections", "fs watch error", {
|
|
1188
|
+
dir,
|
|
1189
|
+
error: String(err)
|
|
1190
|
+
});
|
|
1191
|
+
});
|
|
1192
|
+
return { close: () => watcher.close() };
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
log.warn("collections", "fs watch start failed", {
|
|
1195
|
+
dir,
|
|
1196
|
+
error: String(err)
|
|
1197
|
+
});
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/** Watch the single file `absPath` by watching its PARENT directory, so an
|
|
1202
|
+
* atomic replace can't strand the watch on a dead inode. `alsoAccept`
|
|
1203
|
+
* widens the filter beyond the exact basename (sqlite's `-wal`/`-journal`
|
|
1204
|
+
* sidecars). Reports are debounced: one replace, one call. */
|
|
1205
|
+
async function watchSingleFile(absPath, alsoAccept, onChange) {
|
|
1206
|
+
const dir = node_path.default.dirname(absPath);
|
|
1207
|
+
const base = node_path.default.basename(absPath);
|
|
1208
|
+
let timer = null;
|
|
1209
|
+
const fire = () => {
|
|
1210
|
+
if (timer) clearTimeout(timer);
|
|
1211
|
+
timer = setTimeout(() => {
|
|
1212
|
+
timer = null;
|
|
1213
|
+
onChange();
|
|
1214
|
+
}, REPLACE_DEBOUNCE_MS);
|
|
1215
|
+
timer.unref?.();
|
|
1216
|
+
};
|
|
1217
|
+
const handle = await watchDirectory(dir, (filename) => filename === base || alsoAccept(base, filename), fire);
|
|
1218
|
+
if (!handle) return null;
|
|
1219
|
+
return { close: () => {
|
|
1220
|
+
if (timer) clearTimeout(timer);
|
|
1221
|
+
timer = null;
|
|
1222
|
+
handle.close();
|
|
1223
|
+
} };
|
|
1224
|
+
}
|
|
1225
|
+
/** An `FsWatchHandle` as a bare unsubscribe — `null` straight through, so an
|
|
1226
|
+
* unarmed watch stays distinguishable from an armed one. Lives here rather
|
|
1227
|
+
* than beside the store contract so both `store.ts` and the backends it
|
|
1228
|
+
* registers can reach it without importing each other. */
|
|
1229
|
+
function closerFor(handle) {
|
|
1230
|
+
return handle === null ? null : () => handle.close();
|
|
1231
|
+
}
|
|
1232
|
+
//#endregion
|
|
1127
1233
|
//#region src/collection/server/storePage.ts
|
|
1128
1234
|
/** Project `fields` (+ the primary key, always) out of each record. Thin
|
|
1129
1235
|
* server-typed alias over the shared isomorphic `projectRecordFields`
|
|
@@ -1151,7 +1257,7 @@ var sqliteModule = null;
|
|
|
1151
1257
|
function loadSqlite() {
|
|
1152
1258
|
sqliteModule ??= import("node:sqlite").then((mod) => mod, (err) => {
|
|
1153
1259
|
sqliteModule = null;
|
|
1154
|
-
throw new
|
|
1260
|
+
throw new BackendUnavailableError(`sqlite storage needs the node:sqlite module (Node.js >= 22.5) — this runtime cannot load it: ${String(err)}`);
|
|
1155
1261
|
});
|
|
1156
1262
|
return sqliteModule;
|
|
1157
1263
|
}
|
|
@@ -1388,7 +1494,8 @@ function sqliteStoreFor(collection, opts) {
|
|
|
1388
1494
|
delete: (itemId) => sqliteDelete(file, itemId, {
|
|
1389
1495
|
workspaceRoot: root(),
|
|
1390
1496
|
slug
|
|
1391
|
-
})
|
|
1497
|
+
}),
|
|
1498
|
+
watch: async (onChange) => closerFor(await watchSingleFile(file, (base, name) => name.startsWith(base), () => onChange({ kind: "collection" })))
|
|
1392
1499
|
};
|
|
1393
1500
|
}
|
|
1394
1501
|
//#endregion
|
|
@@ -1398,8 +1505,8 @@ function sqliteStoreFor(collection, opts) {
|
|
|
1398
1505
|
* is filesystem-dependent; paging needs determinism. */
|
|
1399
1506
|
function sortByRecordId(items, primaryKey) {
|
|
1400
1507
|
return [...items].sort((left, right) => {
|
|
1401
|
-
const leftId =
|
|
1402
|
-
const rightId =
|
|
1508
|
+
const leftId = require_ids.fieldText(left[primaryKey]);
|
|
1509
|
+
const rightId = require_ids.fieldText(right[primaryKey]);
|
|
1403
1510
|
if (leftId < rightId) return -1;
|
|
1404
1511
|
return leftId > rightId ? 1 : 0;
|
|
1405
1512
|
});
|
|
@@ -1435,7 +1542,8 @@ function csvStoreFor(collection, opts) {
|
|
|
1435
1542
|
list: () => listAll().then((result) => result.items),
|
|
1436
1543
|
page: (pageOpts = {}) => listAll().then((result) => pageFromFullRead(result.items, pageOpts, key, result.truncated)),
|
|
1437
1544
|
read: (itemId) => file === void 0 ? Promise.resolve(null) : csvRead(file, key, itemId, opts.workspaceRoot),
|
|
1438
|
-
query: (query) => file === void 0 ? Promise.resolve([]) : csvRunQuery(file, key, query, opts.workspaceRoot)
|
|
1545
|
+
query: (query) => file === void 0 ? Promise.resolve([]) : csvRunQuery(file, key, query, opts.workspaceRoot),
|
|
1546
|
+
...file === void 0 ? {} : { watch: async (onChange) => closerFor(await watchSingleFile(file, () => false, () => onChange({ kind: "collection" }))) }
|
|
1439
1547
|
};
|
|
1440
1548
|
}
|
|
1441
1549
|
/** The classic file store over `<dataDir>/<itemId>.json` records. */
|
|
@@ -1458,7 +1566,11 @@ function fileStoreFor(collection, opts) {
|
|
|
1458
1566
|
...ioOpts,
|
|
1459
1567
|
refuseOverwrite: writeOpts.refuseOverwrite
|
|
1460
1568
|
}),
|
|
1461
|
-
delete: (itemId) => deleteItem(collection.dataDir, itemId, ioOpts)
|
|
1569
|
+
delete: (itemId) => deleteItem(collection.dataDir, itemId, ioOpts),
|
|
1570
|
+
watch: async (onChange) => closerFor(await watchDirectory(collection.dataDir, (name) => name.endsWith(".json") && !name.startsWith("."), (filename) => onChange(filename === null ? { kind: "collection" } : {
|
|
1571
|
+
kind: "item",
|
|
1572
|
+
itemId: filename.slice(0, -5)
|
|
1573
|
+
})))
|
|
1462
1574
|
};
|
|
1463
1575
|
}
|
|
1464
1576
|
var storeFactories = /* @__PURE__ */ new Map([
|
|
@@ -2034,7 +2146,8 @@ function fieldDrivenFromFieldCarried(schema) {
|
|
|
2034
2146
|
if (set && Object.prototype.hasOwnProperty.call(set, driven.fromField)) {
|
|
2035
2147
|
const raw = set[driven.fromField];
|
|
2036
2148
|
if (raw === void 0 || raw === null || raw === "") return false;
|
|
2037
|
-
|
|
2149
|
+
const key = require_ids.fieldTextOrNull(raw);
|
|
2150
|
+
return key !== null && Object.prototype.hasOwnProperty.call(driven.map, key);
|
|
2038
2151
|
}
|
|
2039
2152
|
return (carry ?? []).includes(driven.fromField);
|
|
2040
2153
|
}
|
|
@@ -2509,6 +2622,12 @@ function toDetail(collection) {
|
|
|
2509
2622
|
};
|
|
2510
2623
|
}
|
|
2511
2624
|
//#endregion
|
|
2625
|
+
Object.defineProperty(exports, "BackendUnavailableError", {
|
|
2626
|
+
enumerable: true,
|
|
2627
|
+
get: function() {
|
|
2628
|
+
return BackendUnavailableError;
|
|
2629
|
+
}
|
|
2630
|
+
});
|
|
2512
2631
|
Object.defineProperty(exports, "CollectionQueryZ", {
|
|
2513
2632
|
enumerable: true,
|
|
2514
2633
|
get: function() {
|
|
@@ -2659,6 +2778,12 @@ Object.defineProperty(exports, "getWorkspaceRoot", {
|
|
|
2659
2778
|
return getWorkspaceRoot;
|
|
2660
2779
|
}
|
|
2661
2780
|
});
|
|
2781
|
+
Object.defineProperty(exports, "isBackendUnavailable", {
|
|
2782
|
+
enumerable: true,
|
|
2783
|
+
get: function() {
|
|
2784
|
+
return isBackendUnavailable;
|
|
2785
|
+
}
|
|
2786
|
+
});
|
|
2662
2787
|
Object.defineProperty(exports, "isContainedInRoot", {
|
|
2663
2788
|
enumerable: true,
|
|
2664
2789
|
get: function() {
|
|
@@ -2852,4 +2977,4 @@ Object.defineProperty(exports, "writeItem", {
|
|
|
2852
2977
|
}
|
|
2853
2978
|
});
|
|
2854
2979
|
|
|
2855
|
-
//# sourceMappingURL=discovery-
|
|
2980
|
+
//# sourceMappingURL=discovery-Bklck7Ck.cjs.map
|