@wipcomputer/memory-crystal 0.7.29 → 0.7.32

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 (146) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/SKILL.md +1 -1
  3. package/package.json +1 -1
  4. package/scripts/migrate-lance-to-sqlite.mjs +2 -1
  5. package/_trash/RELEASE-NOTES-v0-7-23.md +0 -48
  6. package/_trash/RELEASE-NOTES-v0-7-25.md +0 -24
  7. package/_trash/RELEASE-NOTES-v0-7-26.md +0 -7
  8. package/_trash/RELEASE-NOTES-v0-7-28.md +0 -31
  9. package/_trash/RELEASE-NOTES-v0-7-29.md +0 -28
  10. package/_trash/RELEASE-NOTES-v0-7-4.md +0 -64
  11. package/_trash/RELEASE-NOTES-v0-7-5.md +0 -19
  12. package/dist/bridge.d.ts +0 -7
  13. package/dist/bridge.js +0 -14
  14. package/dist/bulk-copy.d.ts +0 -17
  15. package/dist/bulk-copy.js +0 -90
  16. package/dist/cc-hook.d.ts +0 -8
  17. package/dist/cc-hook.js +0 -368
  18. package/dist/cc-poller.d.ts +0 -1
  19. package/dist/cc-poller.js +0 -550
  20. package/dist/chunk-25LXQJ4Z.js +0 -110
  21. package/dist/chunk-2DRXIRQW.js +0 -97
  22. package/dist/chunk-2ZNH5F6E.js +0 -1281
  23. package/dist/chunk-3G3SFYYI.js +0 -288
  24. package/dist/chunk-3RG5ZIWI.js +0 -10
  25. package/dist/chunk-3S6TI23B.js +0 -97
  26. package/dist/chunk-3VFIJYS4.js +0 -818
  27. package/dist/chunk-52QE3YI3.js +0 -1169
  28. package/dist/chunk-57RP3DIN.js +0 -1205
  29. package/dist/chunk-5HSZ4W2P.js +0 -62
  30. package/dist/chunk-645IPXW3.js +0 -290
  31. package/dist/chunk-7A7ELD4C.js +0 -1205
  32. package/dist/chunk-7FYY4GZM.js +0 -1205
  33. package/dist/chunk-7IUE7ODU.js +0 -254
  34. package/dist/chunk-7RMLKZIS.js +0 -108
  35. package/dist/chunk-AA3OPP4Z.js +0 -432
  36. package/dist/chunk-AEWLSYPH.js +0 -72
  37. package/dist/chunk-ASSZDR6I.js +0 -108
  38. package/dist/chunk-AYRJVWUC.js +0 -1205
  39. package/dist/chunk-CCYI5O3D.js +0 -148
  40. package/dist/chunk-D3I3ZSE2.js +0 -411
  41. package/dist/chunk-DACSKLY6.js +0 -219
  42. package/dist/chunk-DW5B4BL7.js +0 -108
  43. package/dist/chunk-EKSACBTJ.js +0 -1070
  44. package/dist/chunk-EXEZZADG.js +0 -248
  45. package/dist/chunk-F3Y7EL7K.js +0 -83
  46. package/dist/chunk-FBQWSDPC.js +0 -1328
  47. package/dist/chunk-FHRZNOMW.js +0 -1205
  48. package/dist/chunk-IM7N24MT.js +0 -129
  49. package/dist/chunk-IPNYIXFK.js +0 -1178
  50. package/dist/chunk-J7MRSZIO.js +0 -167
  51. package/dist/chunk-JITKI2OI.js +0 -106
  52. package/dist/chunk-JWZXYVET.js +0 -1068
  53. package/dist/chunk-KCQUXVYT.js +0 -108
  54. package/dist/chunk-KOQ43OX6.js +0 -1281
  55. package/dist/chunk-KYVWO6ZM.js +0 -1069
  56. package/dist/chunk-L3VHARQH.js +0 -413
  57. package/dist/chunk-LBWDS6BE.js +0 -288
  58. package/dist/chunk-LOVAHSQV.js +0 -411
  59. package/dist/chunk-LQOYCAGG.js +0 -446
  60. package/dist/chunk-LWAIPJ2W.js +0 -146
  61. package/dist/chunk-M5DHKW7M.js +0 -127
  62. package/dist/chunk-MBKCIJHM.js +0 -1328
  63. package/dist/chunk-MK42FMEG.js +0 -147
  64. package/dist/chunk-MOBMYHKL.js +0 -1205
  65. package/dist/chunk-MPLTNMRG.js +0 -67
  66. package/dist/chunk-NIJCVN3O.js +0 -147
  67. package/dist/chunk-NZCFSZQ7.js +0 -1205
  68. package/dist/chunk-O2UITJGH.js +0 -465
  69. package/dist/chunk-OCRA44AZ.js +0 -108
  70. package/dist/chunk-P3KJR66H.js +0 -117
  71. package/dist/chunk-PEK6JH65.js +0 -432
  72. package/dist/chunk-PJ6FFKEX.js +0 -77
  73. package/dist/chunk-PLUBBZYR.js +0 -800
  74. package/dist/chunk-PNKVD2UK.js +0 -26
  75. package/dist/chunk-PSQZURHO.js +0 -229
  76. package/dist/chunk-SGL6ISBJ.js +0 -1061
  77. package/dist/chunk-SJABZZT5.js +0 -97
  78. package/dist/chunk-TD3P3K32.js +0 -1199
  79. package/dist/chunk-TMDZJJKV.js +0 -288
  80. package/dist/chunk-UNHVZB5G.js +0 -411
  81. package/dist/chunk-VAFTWSTE.js +0 -1061
  82. package/dist/chunk-VNFXFQBB.js +0 -217
  83. package/dist/chunk-X3GVFKSJ.js +0 -1205
  84. package/dist/chunk-XZ3S56RQ.js +0 -1061
  85. package/dist/chunk-Y72C7F6O.js +0 -148
  86. package/dist/chunk-YLICP577.js +0 -1205
  87. package/dist/chunk-YX6AXLVK.js +0 -159
  88. package/dist/chunk-ZCQYHTNU.js +0 -146
  89. package/dist/cli.d.ts +0 -1
  90. package/dist/cli.js +0 -1105
  91. package/dist/cloud-crystal.js +0 -6
  92. package/dist/core.d.ts +0 -232
  93. package/dist/core.js +0 -12
  94. package/dist/crypto.d.ts +0 -20
  95. package/dist/crypto.js +0 -27
  96. package/dist/crystal-capture.sh +0 -29
  97. package/dist/crystal-serve.d.ts +0 -4
  98. package/dist/crystal-serve.js +0 -252
  99. package/dist/dev-update-SZ2Z4WCQ.js +0 -6
  100. package/dist/discover.d.ts +0 -30
  101. package/dist/discover.js +0 -177
  102. package/dist/doctor.d.ts +0 -9
  103. package/dist/doctor.js +0 -334
  104. package/dist/dream-weaver.d.ts +0 -8
  105. package/dist/dream-weaver.js +0 -56
  106. package/dist/file-sync.d.ts +0 -48
  107. package/dist/file-sync.js +0 -18
  108. package/dist/installer.d.ts +0 -61
  109. package/dist/installer.js +0 -676
  110. package/dist/ldm-backup.sh +0 -116
  111. package/dist/ldm.d.ts +0 -50
  112. package/dist/ldm.js +0 -32
  113. package/dist/mcp-server.d.ts +0 -1
  114. package/dist/mcp-server.js +0 -265
  115. package/dist/migrate.d.ts +0 -1
  116. package/dist/migrate.js +0 -89
  117. package/dist/mirror-sync.d.ts +0 -1
  118. package/dist/mirror-sync.js +0 -159
  119. package/dist/oc-backfill.d.ts +0 -19
  120. package/dist/oc-backfill.js +0 -74
  121. package/dist/openclaw.d.ts +0 -5
  122. package/dist/openclaw.js +0 -423
  123. package/dist/pair.d.ts +0 -4
  124. package/dist/pair.js +0 -75
  125. package/dist/poller.d.ts +0 -1
  126. package/dist/poller.js +0 -634
  127. package/dist/role.d.ts +0 -24
  128. package/dist/role.js +0 -13
  129. package/dist/search-pipeline-4K4OJSSS.js +0 -255
  130. package/dist/search-pipeline-4PRS6LI7.js +0 -280
  131. package/dist/search-pipeline-7UJMXPLO.js +0 -280
  132. package/dist/search-pipeline-DQTRLGBH.js +0 -74
  133. package/dist/search-pipeline-HNG37REH.js +0 -282
  134. package/dist/search-pipeline-IZFPLBUB.js +0 -280
  135. package/dist/search-pipeline-MID6F26Q.js +0 -73
  136. package/dist/search-pipeline-N52JZFNN.js +0 -282
  137. package/dist/search-pipeline-OPB2PRQQ.js +0 -280
  138. package/dist/search-pipeline-VXTE5HAD.js +0 -262
  139. package/dist/search-pipeline-XHFKADRG.js +0 -73
  140. package/dist/staging.d.ts +0 -29
  141. package/dist/staging.js +0 -21
  142. package/dist/summarize.d.ts +0 -19
  143. package/dist/summarize.js +0 -10
  144. package/dist/worker-demo.js +0 -186
  145. package/dist/worker-mcp.js +0 -404
  146. package/dist/worker.js +0 -137
package/dist/installer.js DELETED
@@ -1,676 +0,0 @@
1
- import {
2
- deployBackupScript,
3
- deployCaptureScript,
4
- getAgentId,
5
- installCron,
6
- ldmPaths,
7
- loadAgentConfig,
8
- saveAgentConfig,
9
- scaffoldLdm
10
- } from "./chunk-EXEZZADG.js";
11
-
12
- // src/installer.ts
13
- import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, copyFileSync, readdirSync, statSync } from "fs";
14
- import { join, dirname } from "path";
15
- import { execSync } from "child_process";
16
- import { fileURLToPath } from "url";
17
- var HOME = process.env.HOME || "";
18
- var LDM_ROOT = join(HOME, ".ldm");
19
- var OC_ROOT = join(HOME, ".openclaw");
20
- var CC_SETTINGS = join(HOME, ".claude", "settings.json");
21
- var CC_MCP = join(HOME, ".claude", ".mcp.json");
22
- var OC_MCP = join(OC_ROOT, ".mcp.json");
23
- function readVersion(pkgPath) {
24
- try {
25
- if (existsSync(pkgPath)) {
26
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
27
- return pkg.version || null;
28
- }
29
- } catch {
30
- }
31
- return null;
32
- }
33
- function getRepoRoot() {
34
- const thisDir = dirname(fileURLToPath(import.meta.url));
35
- let dir = thisDir;
36
- for (let i = 0; i < 5; i++) {
37
- const pkgPath = join(dir, "package.json");
38
- if (existsSync(pkgPath)) {
39
- try {
40
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
41
- if (pkg.name === "@wipcomputer/memory-crystal") return dir;
42
- } catch {
43
- }
44
- }
45
- dir = dirname(dir);
46
- }
47
- return dirname(thisDir);
48
- }
49
- function semverCompare(a, b) {
50
- const pa = a.split(".").map(Number);
51
- const pb = b.split(".").map(Number);
52
- for (let i = 0; i < 3; i++) {
53
- if ((pa[i] || 0) > (pb[i] || 0)) return 1;
54
- if ((pa[i] || 0) < (pb[i] || 0)) return -1;
55
- }
56
- return 0;
57
- }
58
- function getLatestNpmVersion() {
59
- const names = ["@wipcomputer/memory-crystal", "memory-crystal"];
60
- for (const name of names) {
61
- try {
62
- const v = execSync(`npm view ${name} version 2>/dev/null`, { encoding: "utf-8", timeout: 1e4 }).trim();
63
- if (v) return v;
64
- } catch {
65
- }
66
- }
67
- return null;
68
- }
69
- function detectInstallState() {
70
- const ldmExtDir = join(LDM_ROOT, "extensions", "memory-crystal");
71
- const ocExtDir = join(OC_ROOT, "extensions", "memory-crystal");
72
- const paths = ldmPaths();
73
- const installedVersion = readVersion(join(ldmExtDir, "package.json"));
74
- const repoRoot = getRepoRoot();
75
- let repoVersion = readVersion(join(repoRoot, "package.json")) || "0.0.0";
76
- const npmVersion = getLatestNpmVersion();
77
- if (npmVersion && semverCompare(npmVersion, repoVersion) > 0) repoVersion = npmVersion;
78
- const ccHookDeployed = existsSync(join(ldmExtDir, "dist", "cc-hook.js"));
79
- let ccHookConfigured = false;
80
- try {
81
- if (existsSync(CC_SETTINGS)) {
82
- const settings = JSON.parse(readFileSync(CC_SETTINGS, "utf-8"));
83
- const stopHooks = settings?.hooks?.Stop;
84
- if (Array.isArray(stopHooks)) {
85
- ccHookConfigured = stopHooks.some((entry) => {
86
- const hooks = entry?.hooks;
87
- if (!Array.isArray(hooks)) return false;
88
- return hooks.some((h) => h?.command?.includes("memory-crystal") && h?.command?.includes("cc-hook"));
89
- });
90
- }
91
- }
92
- } catch {
93
- }
94
- let mcpRegistered = false;
95
- for (const mcpPath of [CC_MCP, OC_MCP, join(process.cwd(), ".mcp.json")]) {
96
- try {
97
- if (existsSync(mcpPath)) {
98
- const config = JSON.parse(readFileSync(mcpPath, "utf-8"));
99
- if (config?.mcpServers?.["memory-crystal"]) {
100
- mcpRegistered = true;
101
- break;
102
- }
103
- }
104
- } catch {
105
- }
106
- }
107
- if (!mcpRegistered) {
108
- try {
109
- execSync("claude mcp get memory-crystal 2>/dev/null", { encoding: "utf-8", timeout: 5e3, stdio: "pipe" });
110
- mcpRegistered = true;
111
- } catch {
112
- }
113
- }
114
- const ocDetected = existsSync(join(OC_ROOT, "openclaw.json"));
115
- const ocPluginDeployed = existsSync(join(ocExtDir, "dist", "openclaw.js"));
116
- let cronInstalled = false;
117
- try {
118
- const crontab = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
119
- cronInstalled = crontab.includes("crystal-capture");
120
- } catch {
121
- }
122
- const role = "standalone";
123
- const relayKeyExists = existsSync(join(LDM_ROOT, "secrets", "crystal-relay-key"));
124
- return {
125
- ldmExists: existsSync(LDM_ROOT),
126
- crystalDbExists: existsSync(paths.crystalDb),
127
- ccHookDeployed,
128
- ccHookConfigured,
129
- mcpRegistered,
130
- ocDetected,
131
- ocPluginDeployed,
132
- cronInstalled,
133
- installedVersion,
134
- repoVersion,
135
- needsUpdate: installedVersion !== null && installedVersion !== repoVersion,
136
- role,
137
- relayKeyExists
138
- };
139
- }
140
- function deployToLdm() {
141
- const repoRoot = getRepoRoot();
142
- const sourceDir = join(repoRoot, "dist");
143
- const extDir = join(LDM_ROOT, "extensions", "memory-crystal");
144
- const destDist = join(extDir, "dist");
145
- if (!existsSync(sourceDir)) {
146
- throw new Error(`dist/ not found at ${sourceDir}. Run "npm run build" first.`);
147
- }
148
- mkdirSync(destDist, { recursive: true });
149
- const distFiles = readdirSync(sourceDir);
150
- for (const file of distFiles) {
151
- const srcPath = join(sourceDir, file);
152
- const destPath = join(destDist, file);
153
- const stat = statSync(srcPath);
154
- if (stat.isFile()) {
155
- copyFileSync(srcPath, destPath);
156
- } else if (stat.isDirectory()) {
157
- cpSync(srcPath, destPath, { recursive: true });
158
- }
159
- }
160
- copyFileSync(join(repoRoot, "package.json"), join(extDir, "package.json"));
161
- const pluginJson = join(repoRoot, "openclaw.plugin.json");
162
- if (existsSync(pluginJson)) {
163
- copyFileSync(pluginJson, join(extDir, "openclaw.plugin.json"));
164
- }
165
- const skillsDir = join(repoRoot, "skills");
166
- if (existsSync(skillsDir)) {
167
- cpSync(skillsDir, join(extDir, "skills"), { recursive: true });
168
- }
169
- const version = readVersion(join(extDir, "package.json")) || "unknown";
170
- return { extensionDir: extDir, version };
171
- }
172
- function installLdmDeps() {
173
- const extDir = join(LDM_ROOT, "extensions", "memory-crystal");
174
- if (!existsSync(join(extDir, "package.json"))) {
175
- throw new Error("package.json not found in LDM extension dir. Deploy first.");
176
- }
177
- execSync("npm install --omit=dev", {
178
- cwd: extDir,
179
- encoding: "utf-8",
180
- stdio: "pipe",
181
- timeout: 12e4
182
- });
183
- }
184
- function deployToOpenClaw() {
185
- const repoRoot = getRepoRoot();
186
- const sourceDir = join(repoRoot, "dist");
187
- const extDir = join(OC_ROOT, "extensions", "memory-crystal");
188
- const destDist = join(extDir, "dist");
189
- if (!existsSync(sourceDir)) {
190
- throw new Error(`dist/ not found at ${sourceDir}. Run "npm run build" first.`);
191
- }
192
- mkdirSync(destDist, { recursive: true });
193
- const distFiles = readdirSync(sourceDir);
194
- for (const file of distFiles) {
195
- const srcPath = join(sourceDir, file);
196
- const destPath = join(destDist, file);
197
- const stat = statSync(srcPath);
198
- if (stat.isFile()) {
199
- copyFileSync(srcPath, destPath);
200
- } else if (stat.isDirectory()) {
201
- cpSync(srcPath, destPath, { recursive: true });
202
- }
203
- }
204
- copyFileSync(join(repoRoot, "package.json"), join(extDir, "package.json"));
205
- const pluginJson = join(repoRoot, "openclaw.plugin.json");
206
- if (existsSync(pluginJson)) {
207
- copyFileSync(pluginJson, join(extDir, "openclaw.plugin.json"));
208
- }
209
- const skillsDir = join(repoRoot, "skills");
210
- if (existsSync(skillsDir)) {
211
- cpSync(skillsDir, join(extDir, "skills"), { recursive: true });
212
- }
213
- const version = readVersion(join(extDir, "package.json")) || "unknown";
214
- return { extensionDir: extDir, version };
215
- }
216
- function installOcDeps() {
217
- const extDir = join(OC_ROOT, "extensions", "memory-crystal");
218
- if (!existsSync(join(extDir, "package.json"))) {
219
- throw new Error("package.json not found in OC extension dir. Deploy first.");
220
- }
221
- execSync("npm install --omit=dev", {
222
- cwd: extDir,
223
- encoding: "utf-8",
224
- stdio: "pipe",
225
- timeout: 12e4
226
- });
227
- }
228
- function configureCCHook() {
229
- const hookCommand = `node ${join(LDM_ROOT, "extensions", "memory-crystal", "dist", "cc-hook.js")}`;
230
- let settings = {};
231
- if (existsSync(CC_SETTINGS)) {
232
- try {
233
- settings = JSON.parse(readFileSync(CC_SETTINGS, "utf-8"));
234
- } catch {
235
- throw new Error(`~/.claude/settings.json exists but is not valid JSON. Fix it manually before proceeding.`);
236
- }
237
- }
238
- if (!settings.hooks) settings.hooks = {};
239
- if (!Array.isArray(settings.hooks.Stop)) settings.hooks.Stop = [];
240
- const existingIdx = settings.hooks.Stop.findIndex((entry) => {
241
- const hooks = entry?.hooks;
242
- if (!Array.isArray(hooks)) return false;
243
- return hooks.some((h) => h?.command?.includes("memory-crystal") || h?.command?.includes("cc-hook"));
244
- });
245
- const hookEntry = {
246
- hooks: [{
247
- type: "command",
248
- command: hookCommand,
249
- timeout: 30
250
- }]
251
- };
252
- if (existingIdx >= 0) {
253
- settings.hooks.Stop[existingIdx] = hookEntry;
254
- } else {
255
- settings.hooks.Stop.push(hookEntry);
256
- }
257
- mkdirSync(join(HOME, ".claude"), { recursive: true });
258
- writeFileSync(CC_SETTINGS, JSON.stringify(settings, null, 2) + "\n");
259
- }
260
- function registerMCPServer() {
261
- const mcpServerPath = join(LDM_ROOT, "extensions", "memory-crystal", "dist", "mcp-server.js");
262
- const addCmd = `claude mcp add --scope user -e OPENCLAW_HOME=${OC_ROOT} memory-crystal -- node "${mcpServerPath}"`;
263
- try {
264
- execSync(addCmd, {
265
- encoding: "utf-8",
266
- stdio: "pipe",
267
- timeout: 15e3
268
- });
269
- return;
270
- } catch (err) {
271
- const output = (err.stderr || "") + (err.stdout || "");
272
- if (output.includes("already exists")) {
273
- try {
274
- execSync("claude mcp remove memory-crystal --scope user", { encoding: "utf-8", stdio: "pipe", timeout: 1e4 });
275
- execSync(addCmd, { encoding: "utf-8", stdio: "pipe", timeout: 15e3 });
276
- } catch {
277
- }
278
- return;
279
- }
280
- }
281
- let config = {};
282
- if (existsSync(CC_MCP)) {
283
- try {
284
- config = JSON.parse(readFileSync(CC_MCP, "utf-8"));
285
- } catch {
286
- }
287
- }
288
- if (!config.mcpServers) config.mcpServers = {};
289
- config.mcpServers["memory-crystal"] = {
290
- command: "node",
291
- args: [mcpServerPath],
292
- env: { OPENCLAW_HOME: OC_ROOT }
293
- };
294
- mkdirSync(join(HOME, ".claude"), { recursive: true });
295
- writeFileSync(CC_MCP, JSON.stringify(config, null, 2) + "\n");
296
- }
297
- function registerOcMCPServer() {
298
- const mcpServerPath = join(OC_ROOT, "extensions", "memory-crystal", "dist", "mcp-server.js");
299
- let config = {};
300
- if (existsSync(OC_MCP)) {
301
- try {
302
- config = JSON.parse(readFileSync(OC_MCP, "utf-8"));
303
- } catch {
304
- }
305
- }
306
- if (!config.mcpServers) config.mcpServers = {};
307
- config.mcpServers["memory-crystal"] = {
308
- command: "node",
309
- args: [mcpServerPath],
310
- env: { OPENCLAW_HOME: OC_ROOT }
311
- };
312
- writeFileSync(OC_MCP, JSON.stringify(config, null, 2) + "\n");
313
- }
314
- function backupCrystalDb() {
315
- const paths = ldmPaths();
316
- const dbPath = paths.crystalDb;
317
- if (!existsSync(dbPath)) {
318
- throw new Error(`crystal.db not found at ${dbPath}`);
319
- }
320
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
321
- const backupPath = `${dbPath}.pre-update-${timestamp}`;
322
- copyFileSync(dbPath, backupPath);
323
- const walPath = dbPath + "-wal";
324
- const shmPath = dbPath + "-shm";
325
- if (existsSync(walPath)) copyFileSync(walPath, backupPath + "-wal");
326
- if (existsSync(shmPath)) copyFileSync(shmPath, backupPath + "-shm");
327
- const origSize = statSync(dbPath).size;
328
- const backupSize = statSync(backupPath).size;
329
- if (backupSize !== origSize) {
330
- throw new Error(`Backup size mismatch: original ${origSize}, backup ${backupSize}`);
331
- }
332
- return backupPath;
333
- }
334
- async function verifyCrystalDbReadable() {
335
- const paths = ldmPaths();
336
- const dbPath = paths.crystalDb;
337
- if (!existsSync(dbPath)) return;
338
- const { default: Database } = await import("better-sqlite3");
339
- const db = new Database(dbPath, { readonly: true });
340
- try {
341
- const row = db.prepare("SELECT COUNT(*) as count FROM chunks").get();
342
- if (typeof row.count !== "number") {
343
- throw new Error("chunks table returned unexpected data");
344
- }
345
- const tables = db.prepare(
346
- "SELECT name FROM sqlite_master WHERE type='table'"
347
- ).all();
348
- const tableNames = tables.map((t) => t.name);
349
- if (!tableNames.includes("chunks")) {
350
- throw new Error("chunks table missing from database");
351
- }
352
- } finally {
353
- db.close();
354
- }
355
- }
356
- function formatUpdateSummary(oldVersion, newVersion) {
357
- const lines = [];
358
- lines.push(`Updating v${oldVersion} -> v${newVersion}`);
359
- lines.push("");
360
- lines.push("What will be updated:");
361
- lines.push(" - Code in ~/.ldm/extensions/memory-crystal/dist/");
362
- lines.push(" - Skills in ~/.ldm/extensions/memory-crystal/skills/");
363
- lines.push(" - package.json (version tracking)");
364
- lines.push("");
365
- lines.push("What will NOT be touched:");
366
- lines.push(" - ~/.ldm/memory/crystal.db (your data)");
367
- lines.push(" - ~/.ldm/state/* (watermarks, role)");
368
- lines.push(" - ~/.ldm/secrets/* (relay key)");
369
- lines.push(" - ~/.ldm/agents/* (agent data)");
370
- return lines.join("\n");
371
- }
372
- function ldmCliAvailable() {
373
- try {
374
- execSync("ldm --version", { stdio: "pipe", timeout: 5e3 });
375
- return true;
376
- } catch {
377
- return false;
378
- }
379
- }
380
- function bootstrapLdmOs(steps) {
381
- try {
382
- steps.push("Installing LDM OS infrastructure...");
383
- execSync("npm install -g @wipcomputer/wip-ldm-os", { stdio: "pipe", timeout: 12e4 });
384
- execSync("ldm --version", { stdio: "pipe", timeout: 5e3 });
385
- steps.push("LDM OS installed.");
386
- return true;
387
- } catch {
388
- steps.push("LDM OS install skipped (npm offline or permissions issue). Using standalone.");
389
- return false;
390
- }
391
- }
392
- function runLdmInstall(repoDir) {
393
- const steps = [];
394
- try {
395
- execSync("ldm init --yes --none", { stdio: "pipe", timeout: 3e4 });
396
- steps.push("LDM initialized via ldm CLI");
397
- } catch (err) {
398
- const msg = (err.stderr || err.message || "").toString().trim();
399
- if (!msg.includes("already")) {
400
- steps.push(`ldm init warning: ${msg.slice(0, 120)}`);
401
- }
402
- }
403
- try {
404
- execSync(`ldm install "${repoDir}"`, { stdio: "pipe", timeout: 6e4 });
405
- steps.push("Generic deployment handled by ldm install (extensions, MCP, hooks)");
406
- return { ok: true, steps };
407
- } catch (err) {
408
- const msg = (err.stderr || err.message || "").toString().trim();
409
- steps.push(`ldm install failed: ${msg.slice(0, 200)}`);
410
- return { ok: false, steps };
411
- }
412
- }
413
- async function runInstallOrUpdate(options) {
414
- const agentId = options.agentId || getAgentId();
415
- const state = detectInstallState();
416
- const steps = [];
417
- const deployedTo = [];
418
- let dbStatus = "none";
419
- let chunkCount = 0;
420
- const isFresh = !state.ldmExists || state.installedVersion === null;
421
- const isUpdate = !isFresh && state.needsUpdate;
422
- if (!isFresh && !isUpdate) {
423
- return {
424
- action: "up-to-date",
425
- version: state.repoVersion,
426
- deployedTo: [],
427
- steps: [`Already at v${state.repoVersion}. Nothing to do.`]
428
- };
429
- }
430
- if (isUpdate && state.installedVersion) {
431
- const npmV = getLatestNpmVersion();
432
- if (npmV && semverCompare(npmV, state.installedVersion) > 0) {
433
- steps.push(`Upgrading v${state.installedVersion} -> v${npmV} via npm...`);
434
- try {
435
- execSync("npm install -g @wipcomputer/memory-crystal 2>&1", { encoding: "utf-8", timeout: 6e4, stdio: "pipe" });
436
- steps.push(`Installed @wipcomputer/memory-crystal@${npmV}`);
437
- steps.push("Continuing with updated code...");
438
- } catch (err) {
439
- steps.push(`npm upgrade failed: ${err.message}. Continuing with local code.`);
440
- }
441
- }
442
- }
443
- let hasLdmCli = ldmCliAvailable();
444
- if (!hasLdmCli) {
445
- hasLdmCli = bootstrapLdmOs(steps);
446
- }
447
- let ldmDelegated = false;
448
- if (hasLdmCli) {
449
- steps.push("LDM OS detected. Using ldm install for deployment...");
450
- const repoRoot = getRepoRoot();
451
- const delegateResult = runLdmInstall(repoRoot);
452
- steps.push(...delegateResult.steps);
453
- if (delegateResult.ok) {
454
- ldmDelegated = true;
455
- const ldmExtDir = join(LDM_ROOT, "extensions", "memory-crystal");
456
- if (existsSync(ldmExtDir)) deployedTo.push(ldmExtDir);
457
- const ocExtDir = join(OC_ROOT, "extensions", "memory-crystal");
458
- if (existsSync(ocExtDir)) deployedTo.push(ocExtDir);
459
- }
460
- }
461
- if (ldmDelegated) {
462
- steps.push("Scaffold + agent config handled by ldm CLI");
463
- } else {
464
- scaffoldLdm(agentId);
465
- steps.push(`LDM scaffolded for agent "${agentId}"`);
466
- const existingCfg = loadAgentConfig(agentId);
467
- if (existingCfg && !existingCfg.agentId) {
468
- existingCfg.agentId = agentId;
469
- saveAgentConfig(agentId, existingCfg);
470
- steps.push(`Added agentId "${agentId}" to existing config.json`);
471
- } else if (!existingCfg) {
472
- const harness = agentId.startsWith("oc-") ? "openclaw" : "claude-code-cli";
473
- saveAgentConfig(agentId, {
474
- agentId,
475
- agent: agentId.startsWith("oc-") ? agentId.replace(/^oc-/, "").replace(/-[^-]+$/, "") : "cc",
476
- harness,
477
- created: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
478
- });
479
- steps.push(`Created config.json for agent "${agentId}"`);
480
- }
481
- }
482
- if (state.crystalDbExists) {
483
- try {
484
- const { default: Database } = await import("better-sqlite3");
485
- const db = new Database(ldmPaths().crystalDb, { readonly: true });
486
- const row = db.prepare("SELECT COUNT(*) as count FROM chunks").get();
487
- chunkCount = row.count;
488
- db.close();
489
- dbStatus = "existing";
490
- steps.push(`Existing database found: ${chunkCount.toLocaleString()} chunks in crystal.db`);
491
- } catch {
492
- dbStatus = "existing";
493
- steps.push("Existing database found (could not read chunk count)");
494
- }
495
- try {
496
- const backupPath = backupCrystalDb();
497
- steps.push(`Database backed up to ${backupPath}`);
498
- } catch (err) {
499
- steps.push(`Database backup FAILED: ${err.message}`);
500
- return {
501
- action: "up-to-date",
502
- version: state.repoVersion,
503
- deployedTo: [],
504
- steps: [...steps, "Aborted. Fix the backup issue before retrying."],
505
- dbStatus
506
- };
507
- }
508
- try {
509
- await verifyCrystalDbReadable();
510
- steps.push("Database read verification passed");
511
- } catch (err) {
512
- steps.push(`Database read verification FAILED: ${err.message}`);
513
- return {
514
- action: "up-to-date",
515
- version: state.repoVersion,
516
- deployedTo: [],
517
- steps: [...steps, "Aborted. New code cannot read existing database."],
518
- dbStatus
519
- };
520
- }
521
- } else if (options.importDb) {
522
- const importPath = options.importDb;
523
- if (!existsSync(importPath)) {
524
- steps.push(`Import path not found: ${importPath}`);
525
- } else {
526
- try {
527
- const paths = ldmPaths();
528
- mkdirSync(join(paths.root, "memory"), { recursive: true });
529
- copyFileSync(importPath, paths.crystalDb);
530
- const { default: Database } = await import("better-sqlite3");
531
- const db = new Database(paths.crystalDb, { readonly: true });
532
- const row = db.prepare("SELECT COUNT(*) as count FROM chunks").get();
533
- chunkCount = row.count;
534
- db.close();
535
- dbStatus = "imported";
536
- steps.push(`Database imported: ${chunkCount.toLocaleString()} chunks from ${importPath}`);
537
- } catch (err) {
538
- steps.push(`Database import failed: ${err.message}`);
539
- }
540
- }
541
- } else {
542
- dbStatus = "fresh";
543
- steps.push("No existing database. A new one will be created on first capture.");
544
- }
545
- if (ldmDelegated) {
546
- const repoRoot = getRepoRoot();
547
- const ldmExtDir = join(LDM_ROOT, "extensions", "memory-crystal");
548
- if (existsSync(ldmExtDir)) copyFileSync(join(repoRoot, "package.json"), join(ldmExtDir, "package.json"));
549
- const ocExtDir = join(OC_ROOT, "extensions", "memory-crystal");
550
- if (existsSync(ocExtDir)) copyFileSync(join(repoRoot, "package.json"), join(ocExtDir, "package.json"));
551
- steps.push(`Version synced to v${readVersion(join(repoRoot, "package.json")) || "unknown"}`);
552
- }
553
- if (!ldmDelegated) {
554
- const ldmResult = deployToLdm();
555
- steps.push(`Code deployed to ${ldmResult.extensionDir}`);
556
- deployedTo.push(ldmResult.extensionDir);
557
- try {
558
- installLdmDeps();
559
- steps.push("Dependencies installed (LDM)");
560
- } catch (err) {
561
- steps.push(`Dependencies install failed (LDM): ${err.message}`);
562
- }
563
- try {
564
- configureCCHook();
565
- steps.push("CC Stop hook configured in ~/.claude/settings.json");
566
- } catch (err) {
567
- steps.push(`CC Stop hook config failed: ${err.message}`);
568
- }
569
- if (!state.mcpRegistered || isUpdate) {
570
- try {
571
- registerMCPServer();
572
- steps.push("MCP server registered with Claude Code");
573
- } catch (err) {
574
- steps.push(`MCP registration failed: ${err.message}`);
575
- }
576
- } else {
577
- steps.push("MCP server already registered");
578
- }
579
- }
580
- try {
581
- deployCaptureScript();
582
- steps.push("Capture script deployed");
583
- } catch (err) {
584
- steps.push(`Capture script failed: ${err.message}`);
585
- }
586
- if (!state.cronInstalled || isFresh) {
587
- try {
588
- installCron();
589
- steps.push("Cron job installed (every minute)");
590
- } catch (err) {
591
- steps.push(`Cron install failed: ${err.message}`);
592
- }
593
- } else {
594
- steps.push("Cron job already installed");
595
- }
596
- try {
597
- deployBackupScript();
598
- steps.push("Backup script deployed");
599
- } catch (err) {
600
- steps.push(`Backup script failed: ${err.message}`);
601
- }
602
- if (!ldmDelegated && state.ocDetected) {
603
- try {
604
- const ocResult = deployToOpenClaw();
605
- steps.push(`OC plugin deployed to ${ocResult.extensionDir}`);
606
- deployedTo.push(ocResult.extensionDir);
607
- } catch (err) {
608
- steps.push(`OC plugin deploy failed: ${err.message}`);
609
- }
610
- try {
611
- installOcDeps();
612
- steps.push("Dependencies installed (OC)");
613
- } catch (err) {
614
- steps.push(`Dependencies install failed (OC): ${err.message}`);
615
- }
616
- try {
617
- registerOcMCPServer();
618
- steps.push("OC MCP server config updated");
619
- } catch (err) {
620
- steps.push(`OC MCP config failed: ${err.message}`);
621
- }
622
- }
623
- if (options.role === "core") {
624
- try {
625
- const { promoteToCore } = await import("./role.js");
626
- promoteToCore();
627
- steps.push("Role set to Core");
628
- } catch (err) {
629
- steps.push(`Role setup failed: ${err.message}`);
630
- }
631
- } else if (options.role === "node") {
632
- try {
633
- const { demoteToNode } = await import("./role.js");
634
- demoteToNode();
635
- steps.push("Role set to Node");
636
- } catch (err) {
637
- steps.push(`Role setup failed: ${err.message}`);
638
- }
639
- }
640
- if (options.pairCode) {
641
- try {
642
- const { pairReceive } = await import("./pair.js");
643
- pairReceive(options.pairCode);
644
- steps.push("Pairing code accepted");
645
- } catch (err) {
646
- steps.push(`Pairing failed: ${err.message}`);
647
- }
648
- }
649
- if (hasLdmCli) {
650
- steps.push('Tip: Run "ldm install" to see more components you can add.');
651
- } else if (!ldmDelegated) {
652
- steps.push("Tip: Install LDM OS for more components: npm install -g @wipcomputer/wip-ldm-os");
653
- }
654
- return {
655
- action: isFresh ? "installed" : "updated",
656
- version: state.repoVersion,
657
- deployedTo,
658
- steps,
659
- dbStatus,
660
- chunkCount
661
- };
662
- }
663
- export {
664
- backupCrystalDb,
665
- configureCCHook,
666
- deployToLdm,
667
- deployToOpenClaw,
668
- detectInstallState,
669
- formatUpdateSummary,
670
- installLdmDeps,
671
- installOcDeps,
672
- registerMCPServer,
673
- registerOcMCPServer,
674
- runInstallOrUpdate,
675
- verifyCrystalDbReadable
676
- };