@kira905/dsh-memory-toolkit 0.0.0-stage → 0.1.1

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.
Files changed (132) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +661 -0
  3. package/README.en.md +110 -0
  4. package/README.md +102 -2
  5. package/dsh-memory-local/FORK-BASE.json +24 -0
  6. package/dsh-memory-local/README.md +113 -0
  7. package/dsh-memory-local/build.mjs +101 -0
  8. package/dsh-memory-local/cordis.patch.yml +20 -0
  9. package/dsh-memory-local/deploy.mjs +306 -0
  10. package/dsh-memory-local/entity-status.mjs +75 -0
  11. package/dsh-memory-local/lib/bridge.mjs +108 -0
  12. package/dsh-memory-local/lib/host-adapter.js +1171 -0
  13. package/dsh-memory-local/lib/index.d.ts +128 -0
  14. package/dsh-memory-local/lib/index.js +944 -0
  15. package/dsh-memory-local/lib/injector.mjs +400 -0
  16. package/dsh-memory-local/lib/memory-read.mjs +116 -0
  17. package/dsh-memory-local/lib/module-gate.mjs +68 -0
  18. package/dsh-memory-local/lib/pointer.mjs +152 -0
  19. package/dsh-memory-local/lib/prod-gate.mjs +121 -0
  20. package/dsh-memory-local/lib/recall-log.mjs +118 -0
  21. package/dsh-memory-local/lib/recall.mjs +192 -0
  22. package/dsh-memory-local/lib/schema-migrate.mjs +365 -0
  23. package/dsh-memory-local/lib/user-message.mjs +32 -0
  24. package/dsh-memory-local/lib/vectors-read.mjs +110 -0
  25. package/dsh-memory-local/package.json +51 -0
  26. package/dsh-memory-local/restore.mjs +259 -0
  27. package/dsh-memory-local/rollback-injector-fix.mjs +166 -0
  28. package/dsh-memory-local/src/bridge.mjs +108 -0
  29. package/dsh-memory-local/src/host-adapter.js +1171 -0
  30. package/dsh-memory-local/src/index.d.ts +128 -0
  31. package/dsh-memory-local/src/index.js +944 -0
  32. package/dsh-memory-local/src/injector.mjs +400 -0
  33. package/dsh-memory-local/src/memory-read.mjs +116 -0
  34. package/dsh-memory-local/src/module-gate.mjs +68 -0
  35. package/dsh-memory-local/src/pointer.mjs +152 -0
  36. package/dsh-memory-local/src/recall-log.mjs +118 -0
  37. package/dsh-memory-local/src/recall.mjs +192 -0
  38. package/dsh-memory-local/src/schema-migrate.mjs +365 -0
  39. package/dsh-memory-local/src/user-message.mjs +32 -0
  40. package/dsh-memory-local/src/vectors-read.mjs +110 -0
  41. package/dsh-memory-local/test/fixtures-s2b.mjs +216 -0
  42. package/dsh-memory-local/test/probe-entity.mjs +170 -0
  43. package/dsh-memory-local/test/regression.mjs +270 -0
  44. package/dsh-memory-local/test/s2b.test.mjs +568 -0
  45. package/dsh-memory-local/test/schema-migrate.test.mjs +334 -0
  46. package/dsh-memory-local/test/test-adapter-converge.mjs +127 -0
  47. package/dsh-memory-local/test/visible-injections.test.mjs +239 -0
  48. package/dsh-memory-local/verify-after-restart.mjs +181 -0
  49. package/lib/deploy-guard.mjs +391 -0
  50. package/lib/guard-http.mjs +93 -0
  51. package/lib/pitfall-form-map.mjs +130 -0
  52. package/lib/pitfall-score.mjs +185 -0
  53. package/lib/session-file-name.mjs +160 -0
  54. package/lib/zstd-frames.mjs +78 -0
  55. package/memory-db-tools/README.md +68 -0
  56. package/memory-db-tools/_test/test-memory-scope.mjs +302 -0
  57. package/memory-db-tools/audit-analyze.mjs +352 -0
  58. package/memory-db-tools/audit-dump-ids.mjs +245 -0
  59. package/memory-db-tools/audit-export.mjs +270 -0
  60. package/memory-db-tools/audit-hit-probe.mjs +227 -0
  61. package/memory-db-tools/audit-ledger-probe.mjs +236 -0
  62. package/memory-db-tools/audit-persona-overlap.mjs +277 -0
  63. package/memory-db-tools/audit-struct-probe.mjs +213 -0
  64. package/memory-db-tools/audit-verify-unchanged.mjs +276 -0
  65. package/memory-db-tools/check-memory-db.mjs +8 -0
  66. package/memory-db-tools/dump-memory-db.mjs +12 -0
  67. package/memory-db-tools/lib/memory-scope.mjs +446 -0
  68. package/memory-db-tools/list-memory-summary.mjs +477 -0
  69. package/memory-db-tools/memory-identity-check.mjs +101 -0
  70. package/memory-db-tools/memory-schema-migrate.mjs +290 -0
  71. package/memory-db-tools/memory-scope-fixture.mjs +197 -0
  72. package/memory-db-tools/read-memories.mjs +26 -0
  73. package/memory-db-tools/read-memory-db.mjs +20 -0
  74. package/memory-db-tools/refine-memories.mjs +247 -0
  75. package/memory-db-tools/show-mem-times.mjs +22 -0
  76. package/memory-engine/README.md +70 -0
  77. package/memory-engine/bench/bench.mjs +221 -0
  78. package/memory-engine/cli/distill-rollback.mjs +86 -0
  79. package/memory-engine/cli/distill.mjs +156 -0
  80. package/memory-engine/cli/doc-index-rebuild.mjs +155 -0
  81. package/memory-engine/cli/doc-index.mjs +48 -0
  82. package/memory-engine/cli/domain-tags.mjs +251 -0
  83. package/memory-engine/cli/pitfall-hook.mjs +91 -0
  84. package/memory-engine/cli/recall.mjs +132 -0
  85. package/memory-engine/cli/selfcheck.mjs +57 -0
  86. package/memory-engine/cli/serve-start.mjs +247 -0
  87. package/memory-engine/cli/sweep.mjs +58 -0
  88. package/memory-engine/cli/sync.mjs +69 -0
  89. package/memory-engine/lib/cli-util.mjs +197 -0
  90. package/memory-engine/lib/config.mjs +135 -0
  91. package/memory-engine/lib/dirty-ledger.mjs +174 -0
  92. package/memory-engine/lib/distill-rollback.mjs +178 -0
  93. package/memory-engine/lib/distill.mjs +898 -0
  94. package/memory-engine/lib/doc-index-store.mjs +325 -0
  95. package/memory-engine/lib/doc-index.mjs +529 -0
  96. package/memory-engine/lib/doc-scan.mjs +271 -0
  97. package/memory-engine/lib/doc-summarize.mjs +305 -0
  98. package/memory-engine/lib/domain-tags.mjs +429 -0
  99. package/memory-engine/lib/embedder.mjs +242 -0
  100. package/memory-engine/lib/hashes.mjs +12 -0
  101. package/memory-engine/lib/memory-source.mjs +127 -0
  102. package/memory-engine/lib/pitfall-source.mjs +170 -0
  103. package/memory-engine/lib/policy-source.mjs +371 -0
  104. package/memory-engine/lib/recall.mjs +331 -0
  105. package/memory-engine/lib/selfcheck.mjs +131 -0
  106. package/memory-engine/lib/sweep.mjs +159 -0
  107. package/memory-engine/lib/sync.mjs +180 -0
  108. package/memory-engine/lib/tick-hook.mjs +218 -0
  109. package/memory-engine/lib/vectors-store.mjs +292 -0
  110. package/memory-engine/package-lock.json +1086 -0
  111. package/memory-engine/package.json +22 -0
  112. package/memory-engine/serve.mjs +171 -0
  113. package/memory-engine/test/01-store.test.mjs +127 -0
  114. package/memory-engine/test/02-sync.test.mjs +196 -0
  115. package/memory-engine/test/03-sweep.test.mjs +145 -0
  116. package/memory-engine/test/04-recall.test.mjs +214 -0
  117. package/memory-engine/test/05-selfcheck.test.mjs +175 -0
  118. package/memory-engine/test/06-doc-index.test.mjs +928 -0
  119. package/memory-engine/test/07-domain-tags.test.mjs +298 -0
  120. package/memory-engine/test/08-distill.test.mjs +686 -0
  121. package/memory-engine/test/09-pitfall-source.test.mjs +365 -0
  122. package/memory-engine/test/10-tick-hook.test.mjs +252 -0
  123. package/memory-engine/test/11-policy-source.test.mjs +224 -0
  124. package/memory-engine/test/_tickhook-worker.mjs +56 -0
  125. package/memory-engine/test/fixtures.mjs +205 -0
  126. package/memory-engine/test/harness.mjs +73 -0
  127. package/memory-engine/test/run.mjs +35 -0
  128. package/memory-engine/tools/bridge-smoke.mjs +80 -0
  129. package/memory-engine/tools/fetch-model.mjs +128 -0
  130. package/memory-engine/tools/smoke-recall.mjs +76 -0
  131. package/package.json +53 -4
  132. package/persona-memory-audit.mjs +217 -0
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env node
2
+ // audit-verify-unchanged.mjs — 季度记忆库体检 · 收尾站:零改动自证(物理 sha + 逻辑指纹 + 逐条 diff)
3
+ // 固化自早期临时脚本(2026-09-25 体检工具链固化)
4
+ // · 去硬编码:库与基线路径改为 --db / --baseline(默认系统临时目录里的 memories.json)
5
+ // · 加 --expect-unchanged(有差异即非零退出,供体检/CI 断言)· --help / --self-test / --json
6
+ // · 收尾 process.exitCode
7
+ // 用途:体检开始前用 audit-export.mjs 冻结基线,体检结束跑本工具 —— 若库有增删改,逐条列出来。
8
+ // 只读:数据库以 { readOnly: true } 打开;只读基线与库,不写任何文件。
9
+
10
+ import fs from "node:fs";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import crypto from "node:crypto";
14
+ import { DatabaseSync } from "node:sqlite";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ export const SELF = fileURLToPath(import.meta.url);
18
+ export const TOOL = path.basename(SELF);
19
+
20
+ export function resolveDshHome() {
21
+ if (process.env.DSH_HOME) return process.env.DSH_HOME;
22
+ return path.join(process.env.USERPROFILE || os.homedir(), ".dsh");
23
+ }
24
+ export const DSH_HOME = resolveDshHome();
25
+ export const DEFAULT_DB = path.join(DSH_HOME, "memory", "memory.db");
26
+ export const DEFAULT_WORK_DIR = path.join(os.tmpdir(), "dsh-mem-audit");
27
+ export const DEFAULT_BASELINE = path.join(DEFAULT_WORK_DIR, "memories.json");
28
+
29
+ export function isMainEntry() {
30
+ const a = process.argv[1];
31
+ if (!a) return false;
32
+ try {
33
+ return fs.realpathSync(a) === fs.realpathSync(SELF);
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+
39
+ export function parseArgs(argv) {
40
+ const out = { _: [] };
41
+ for (let i = 0; i < argv.length; i++) {
42
+ const a = argv[i];
43
+ if (a === "--") {
44
+ out._.push(...argv.slice(i + 1));
45
+ break;
46
+ }
47
+ if (a.startsWith("--")) {
48
+ const eq = a.indexOf("=");
49
+ if (eq > 0) out[a.slice(2, eq)] = a.slice(eq + 1);
50
+ else {
51
+ const k = a.slice(2);
52
+ const next = argv[i + 1];
53
+ if (next !== undefined && !next.startsWith("--")) {
54
+ out[k] = next;
55
+ i++;
56
+ } else out[k] = true;
57
+ }
58
+ } else out._.push(a);
59
+ }
60
+ return out;
61
+ }
62
+
63
+ export function makeAsserter() {
64
+ let pass = 0,
65
+ total = 0;
66
+ const fails = [];
67
+ return {
68
+ ok(cond, label) {
69
+ total++;
70
+ if (cond) pass++;
71
+ else fails.push(label);
72
+ },
73
+ eq(actual, expected, label) {
74
+ this.ok(actual === expected, `${label}(期望 ${JSON.stringify(expected)},实际 ${JSON.stringify(actual)})`);
75
+ },
76
+ report() {
77
+ if (fails.length) {
78
+ console.log(`[self-test] FAIL ${pass}/${total}`);
79
+ for (const f of fails) console.log(` - ${f}`);
80
+ return 1;
81
+ }
82
+ console.log(`[self-test] PASS ${pass}/${total}`);
83
+ return 0;
84
+ },
85
+ };
86
+ }
87
+
88
+ export const sha256 = (buf) => crypto.createHash("sha256").update(buf).digest("hex").toUpperCase();
89
+
90
+ export function readRows(dbPath) {
91
+ const db = new DatabaseSync(dbPath, { readOnly: true });
92
+ try {
93
+ return db
94
+ .prepare("SELECT id, text, tags, pinned, created_at, updated_at FROM memories ORDER BY id")
95
+ .all()
96
+ .map((r) => ({
97
+ id: r.id,
98
+ text: r.text ?? "",
99
+ tags: r.tags ?? "",
100
+ pinned: r.pinned === 1,
101
+ created_at: r.created_at,
102
+ updated_at: r.updated_at,
103
+ }));
104
+ } finally {
105
+ db.close();
106
+ }
107
+ }
108
+
109
+ // ---------- 核心逻辑 ----------
110
+ export function verifyUnchanged(dbPath, baselinePath, baselineRows) {
111
+ const phys = sha256(fs.readFileSync(dbPath));
112
+ const st = fs.statSync(dbPath);
113
+ const now = readRows(dbPath);
114
+ const logical = sha256(now.map((r) => `${r.id}\u0000${r.pinned ? 1 : 0}\u0000${r.tags}\u0000${r.text}`).join("\u0001"));
115
+ const before = baselineRows ?? JSON.parse(fs.readFileSync(baselinePath, "utf8"));
116
+ const beforeMap = new Map(before.map((r) => [r.id, r]));
117
+ const added = now.filter((r) => !beforeMap.has(r.id)).map((r) => r.id);
118
+ const removed = before.filter((r) => !now.some((x) => x.id === r.id)).map((r) => r.id);
119
+ const changedIds = [];
120
+ for (const r of now) {
121
+ const b = beforeMap.get(r.id);
122
+ if (!b) continue;
123
+ if (b.text !== r.text || b.pinned !== r.pinned || b.tags !== r.tags) changedIds.push(r.id);
124
+ }
125
+ return {
126
+ db: dbPath,
127
+ baseline: baselinePath,
128
+ phys,
129
+ sizeBytes: st.size,
130
+ mtime: st.mtime.toISOString(),
131
+ count: now.length,
132
+ pinned: now.filter((r) => r.pinned).length,
133
+ logical,
134
+ baselineCount: before.length,
135
+ added,
136
+ removed,
137
+ changed: changedIds,
138
+ unchanged: added.length === 0 && removed.length === 0 && changedIds.length === 0,
139
+ };
140
+ }
141
+
142
+ const USAGE = `audit-verify-unchanged.mjs — 季度体检收尾站:memory.db 零改动自证
143
+
144
+ 用法:
145
+ node ${TOOL} [--db <path>] [--baseline <memories.json>] [--expect-unchanged] [--json]
146
+ node ${TOOL} --help
147
+ node ${TOOL} --self-test
148
+
149
+ 参数:
150
+ --db <path> 记忆库路径,默认 ${DEFAULT_DB}
151
+ --baseline <file> 基线快照(audit-export.mjs 的产物),默认 ${DEFAULT_BASELINE}
152
+ --expect-unchanged 有差异时以退出码 3 结束(供自动化断言);默认只报告不判失败
153
+ --json 以 JSON 输出
154
+
155
+ 输出:物理 sha256 / 文件大小 / 最后写入时间 / 条数 / pinned / 逻辑内容指纹 / 逐条 diff(新增·缺失·内容变更)
156
+
157
+ 退出码:
158
+ 0 成功(无 --expect-unchanged 时:即使有差异也算成功)
159
+ 1 --self-test 断言失败
160
+ 2 用法错误
161
+ 3 库或基线不存在;或 --expect-unchanged 下检出了差异
162
+ 4 运行错误
163
+
164
+ 只读:数据库以 { readOnly: true } 打开;只读基线;不写任何文件。
165
+ `;
166
+
167
+ export function main(argv = process.argv.slice(2)) {
168
+ const a = parseArgs(argv);
169
+ if (a.help) {
170
+ console.log(USAGE);
171
+ return 0;
172
+ }
173
+ if (typeof a["self-test"] !== "undefined") return selfTest();
174
+
175
+ const dbPath = typeof a.db === "string" ? path.resolve(a.db) : DEFAULT_DB;
176
+ const baselinePath = typeof a.baseline === "string" ? path.resolve(a.baseline) : DEFAULT_BASELINE;
177
+ if (!fs.existsSync(dbPath)) {
178
+ console.error(`[${TOOL}] 记忆库不存在:${dbPath}`);
179
+ return 3;
180
+ }
181
+ if (!fs.existsSync(baselinePath)) {
182
+ console.error(`[${TOOL}] 基线不存在:${baselinePath}(先跑 audit-export.mjs,或用 --baseline 指定)`);
183
+ return 3;
184
+ }
185
+ const r = verifyUnchanged(dbPath, baselinePath);
186
+
187
+ if (a.json) {
188
+ console.log(JSON.stringify(r, null, 1));
189
+ } else {
190
+ console.log(`文件: ${r.db}`);
191
+ console.log(`物理 sha256 = ${r.phys}`);
192
+ console.log(`大小 = ${r.sizeBytes} B | 最后写入 = ${r.mtime}`);
193
+ console.log(`条数 = ${r.count} | pinned = ${r.pinned}`);
194
+ console.log(`逻辑内容指纹 sha256 = ${r.logical}`);
195
+ console.log(`基线: ${r.baseline}(${r.baselineCount} 条)`);
196
+ console.log(
197
+ `对照基线: 新增=${r.added.length}${r.added.length ? " [" + r.added.join(",") + "]" : ""} 缺失=${r.removed.length}${r.removed.length ? " [" + r.removed.join(",") + "]" : ""} 内容变更=${r.changed.length}${r.changed.length ? " [" + r.changed.join(",") + "]" : ""}`,
198
+ );
199
+ console.log(r.unchanged ? "结论: 无改动(体检期间记忆库零写入)" : "结论: 检出改动(见上方逐条 diff)");
200
+ }
201
+ if (a["expect-unchanged"] === true && !r.unchanged) return 3;
202
+ return 0;
203
+ }
204
+
205
+ // ---------- self-test(无副作用:合成库 + 合成基线) ----------
206
+ export function selfTest() {
207
+ const t = makeAsserter();
208
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "audit-verify-selftest-"));
209
+ const dbFile = path.join(tmp, "fixture.db");
210
+ const baseOk = path.join(tmp, "baseline-ok.json");
211
+ const baseDrift = path.join(tmp, "baseline-drift.json");
212
+ try {
213
+ const db = new DatabaseSync(dbFile); // 合成夹具库(临时目录,非生产)
214
+ try {
215
+ db.exec(
216
+ "CREATE TABLE memories (id INTEGER PRIMARY KEY, text TEXT NOT NULL, tags TEXT, pinned INTEGER DEFAULT 0, created_at INTEGER, updated_at INTEGER)",
217
+ );
218
+ const ins = db.prepare("INSERT INTO memories (id, text, tags, pinned, created_at, updated_at) VALUES (?,?,?,?,?,?)");
219
+ ins.run(1, "第一条", "t1", 1, 1000, 1000);
220
+ ins.run(2, "第二条", "", 0, 2000, 2000);
221
+ } finally {
222
+ db.close();
223
+ }
224
+ const snap = readRows(dbFile).map((r) => ({ ...r }));
225
+ fs.writeFileSync(baseOk, JSON.stringify(snap), "utf8");
226
+ // 漂移基线:#2 内容不同、且多一条 #3
227
+ const drift = snap.map((r) => (r.id === 2 ? { ...r, text: "第二条(旧)" } : r));
228
+ drift.push({ id: 3, text: "基线多出的第三条", tags: "", pinned: false, created_at: 3000, updated_at: 3000 });
229
+ fs.writeFileSync(baseDrift, JSON.stringify(drift), "utf8");
230
+
231
+ const beforeDb = sha256(fs.readFileSync(dbFile));
232
+ const beforeBaseOk = fs.readFileSync(baseOk, "utf8");
233
+ const beforeBaseDrift = fs.readFileSync(baseDrift, "utf8");
234
+
235
+ const ok = verifyUnchanged(dbFile, baseOk);
236
+ t.eq(ok.count, 2, "条数");
237
+ t.eq(ok.pinned, 1, "pinned 计数");
238
+ t.eq(ok.unchanged, true, "一致基线 ⇒ unchanged=true");
239
+ t.eq(ok.added.length, 0, "无新增");
240
+ t.eq(ok.changed.length, 0, "无内容变更");
241
+ t.ok(/^[0-9A-F]{64}$/.test(ok.phys), "物理 sha256 形状");
242
+ t.ok(/^[0-9A-F]{64}$/.test(ok.logical), "逻辑指纹 sha256 形状");
243
+
244
+ const drifted = verifyUnchanged(dbFile, baseDrift);
245
+ t.eq(drifted.unchanged, false, "漂移基线 ⇒ unchanged=false");
246
+ t.eq(drifted.removed.length, 1, "检出缺失 1 条(#3 在基线里、库里没有)");
247
+ t.eq(drifted.removed[0], 3, "缺失 id = 3");
248
+ t.eq(drifted.changed.length, 1, "检出内容变更 1 条");
249
+ t.eq(drifted.changed[0], 2, "内容变更 id = 2");
250
+ t.eq(drifted.logical, ok.logical, "迁移不影响库本身:两次逻辑指纹一致");
251
+
252
+ // 自证只读
253
+ t.eq(sha256(fs.readFileSync(dbFile)), beforeDb, "合成库 sha256 前后一致(未写库)");
254
+ t.eq(fs.readFileSync(baseOk, "utf8"), beforeBaseOk, "基线文件(一致版)前后一致");
255
+ t.eq(fs.readFileSync(baseDrift, "utf8"), beforeBaseDrift, "基线文件(漂移版)前后一致");
256
+ const src = fs.readFileSync(SELF, "utf8");
257
+ const calls = src.split("// ---------- self-test")[0].match(/new DatabaseSync\([^)]*\)/g) ?? [];
258
+ t.ok(calls.length > 0, "主体逻辑段确实打开了数据库(探针真的在读库)");
259
+ t.ok(calls.every((s) => /readOnly: true/.test(s)), "主体逻辑段每次 new DatabaseSync( 都带 readOnly: true");
260
+ t.ok(!/fs\.writeFileSync/.test(src.split("// ---------- self-test")[0]), "主体逻辑段不含 fs.writeFileSync");
261
+ } catch (e) {
262
+ t.ok(false, `self-test 抛错:${e?.message ?? e}`);
263
+ } finally {
264
+ fs.rmSync(tmp, { recursive: true, force: true });
265
+ }
266
+ return t.report();
267
+ }
268
+
269
+ if (isMainEntry()) {
270
+ try {
271
+ process.exitCode = main();
272
+ } catch (e) {
273
+ console.error(`[${TOOL}] 运行错误:${e?.stack ?? e}`);
274
+ process.exitCode = 4;
275
+ }
276
+ }
@@ -0,0 +1,8 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ const db = new DatabaseSync(`${process.env.USERPROFILE.replaceAll("\\", "/")}/.dsh/memory/memory.db`, { readOnly: true });
3
+ const rows = db.prepare("SELECT id, pinned, tags FROM memories ORDER BY id").all();
4
+ const pinned = rows.filter(r => r.pinned).map(r => r.id);
5
+ console.log(`TOTAL: ${rows.length}`);
6
+ console.log(`IDS: ${rows.map(r => r.id).join(", ")}`);
7
+ console.log(`PINNED: ${pinned.join(", ")}`);
8
+ db.close();
@@ -0,0 +1,12 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+
3
+ const db = new DatabaseSync(`${process.env.USERPROFILE.replaceAll("\\", "/")}/.dsh/memory/memory.db`, { readOnly: true });
4
+
5
+ const rows = db.prepare("SELECT id, text, tags, pinned, created_at, updated_at FROM memories ORDER BY id").all();
6
+ console.log(`TOTAL: ${rows.length}`);
7
+ for (const r of rows) {
8
+ console.log(`\n--- #${r.id} [pinned=${r.pinned}] tags=${r.tags} created=${r.created_at} updated=${r.updated_at} ---`);
9
+ console.log(r.text);
10
+ }
11
+
12
+ db.close();