@wipcomputer/memory-crystal 0.7.34-alpha.2 → 0.7.34-alpha.4

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 (120) hide show
  1. package/LICENSE +1 -1
  2. package/dist/bridge.js +64 -7
  3. package/dist/bulk-copy.js +67 -16
  4. package/dist/cc-hook.js +2163 -62
  5. package/dist/cc-poller.js +1967 -70
  6. package/dist/cli.js +4538 -139
  7. package/dist/core.js +1789 -6
  8. package/dist/crypto.js +153 -14
  9. package/dist/crystal-serve.js +64 -12
  10. package/dist/doctor.js +517 -52
  11. package/dist/dream-weaver.js +1755 -7
  12. package/dist/file-sync.js +407 -9
  13. package/dist/installer.js +840 -145
  14. package/dist/ldm.js +231 -16
  15. package/dist/mcp-server.js +1882 -17
  16. package/dist/migrate.js +1707 -11
  17. package/dist/mirror-sync.js +2052 -34
  18. package/dist/openclaw.js +1895 -84
  19. package/dist/pair.js +112 -16
  20. package/dist/poller.js +2275 -80
  21. package/dist/role.js +159 -7
  22. package/dist/staging.js +235 -10
  23. package/dist/summarize.js +142 -5
  24. package/package.json +7 -4
  25. package/dist/chunk-25LXQJ4Z.js +0 -110
  26. package/dist/chunk-2DRXIRQW.js +0 -97
  27. package/dist/chunk-2GBYLMEF.js +0 -1385
  28. package/dist/chunk-2ZNH5F6E.js +0 -1281
  29. package/dist/chunk-3G3SFYYI.js +0 -288
  30. package/dist/chunk-3RG5ZIWI.js +0 -10
  31. package/dist/chunk-3S6TI23B.js +0 -97
  32. package/dist/chunk-3VFIJYS4.js +0 -818
  33. package/dist/chunk-437F27T6.js +0 -97
  34. package/dist/chunk-52QE3YI3.js +0 -1169
  35. package/dist/chunk-57RP3DIN.js +0 -1205
  36. package/dist/chunk-5HSZ4W2P.js +0 -62
  37. package/dist/chunk-5I7GMRDN.js +0 -146
  38. package/dist/chunk-645IPXW3.js +0 -290
  39. package/dist/chunk-7A7ELD4C.js +0 -1205
  40. package/dist/chunk-7FYY4GZM.js +0 -1205
  41. package/dist/chunk-7IUE7ODU.js +0 -254
  42. package/dist/chunk-7RMLKZIS.js +0 -108
  43. package/dist/chunk-AA3OPP4Z.js +0 -432
  44. package/dist/chunk-AEWLSYPH.js +0 -72
  45. package/dist/chunk-ASSZDR6I.js +0 -108
  46. package/dist/chunk-AYRJVWUC.js +0 -1205
  47. package/dist/chunk-CCYI5O3D.js +0 -148
  48. package/dist/chunk-CGIDSAJB.js +0 -288
  49. package/dist/chunk-D3I3ZSE2.js +0 -411
  50. package/dist/chunk-D3MACYZ4.js +0 -108
  51. package/dist/chunk-DACSKLY6.js +0 -219
  52. package/dist/chunk-DFQ72B7M.js +0 -248
  53. package/dist/chunk-DW5B4BL7.js +0 -108
  54. package/dist/chunk-EKSACBTJ.js +0 -1070
  55. package/dist/chunk-EXEZZADG.js +0 -248
  56. package/dist/chunk-F3Y7EL7K.js +0 -83
  57. package/dist/chunk-FBQWSDPC.js +0 -1328
  58. package/dist/chunk-FHRZNOMW.js +0 -1205
  59. package/dist/chunk-IM7N24MT.js +0 -129
  60. package/dist/chunk-IPNYIXFK.js +0 -1178
  61. package/dist/chunk-J7MRSZIO.js +0 -167
  62. package/dist/chunk-JITKI2OI.js +0 -106
  63. package/dist/chunk-JWZXYVET.js +0 -1068
  64. package/dist/chunk-KCQUXVYT.js +0 -108
  65. package/dist/chunk-KOQ43OX6.js +0 -1281
  66. package/dist/chunk-KYVWO6ZM.js +0 -1069
  67. package/dist/chunk-L3VHARQH.js +0 -413
  68. package/dist/chunk-LBWDS6BE.js +0 -288
  69. package/dist/chunk-LOVAHSQV.js +0 -411
  70. package/dist/chunk-LQOYCAGG.js +0 -446
  71. package/dist/chunk-LWAIPJ2W.js +0 -146
  72. package/dist/chunk-M5DHKW7M.js +0 -127
  73. package/dist/chunk-MBKCIJHM.js +0 -1328
  74. package/dist/chunk-MK42FMEG.js +0 -147
  75. package/dist/chunk-MOBMYHKL.js +0 -1205
  76. package/dist/chunk-MPLTNMRG.js +0 -67
  77. package/dist/chunk-NIJCVN3O.js +0 -147
  78. package/dist/chunk-NX647OM3.js +0 -310
  79. package/dist/chunk-NZCFSZQ7.js +0 -1205
  80. package/dist/chunk-O2UITJGH.js +0 -465
  81. package/dist/chunk-OCRA44AZ.js +0 -108
  82. package/dist/chunk-P3KJR66H.js +0 -117
  83. package/dist/chunk-PEK6JH65.js +0 -432
  84. package/dist/chunk-PJ6FFKEX.js +0 -77
  85. package/dist/chunk-PLUBBZYR.js +0 -800
  86. package/dist/chunk-PNKVD2UK.js +0 -26
  87. package/dist/chunk-PSQZURHO.js +0 -229
  88. package/dist/chunk-SGL6ISBJ.js +0 -1061
  89. package/dist/chunk-SJABZZT5.js +0 -97
  90. package/dist/chunk-TD3P3K32.js +0 -1199
  91. package/dist/chunk-TMDZJJKV.js +0 -288
  92. package/dist/chunk-UNHVZB5G.js +0 -411
  93. package/dist/chunk-VAFTWSTE.js +0 -1061
  94. package/dist/chunk-VNFXFQBB.js +0 -217
  95. package/dist/chunk-X3GVFKSJ.js +0 -1205
  96. package/dist/chunk-XZ3S56RQ.js +0 -1061
  97. package/dist/chunk-Y72C7F6O.js +0 -148
  98. package/dist/chunk-YLICP577.js +0 -1205
  99. package/dist/chunk-YX6AXLVK.js +0 -159
  100. package/dist/chunk-ZCQYHTNU.js +0 -146
  101. package/dist/cloud-crystal.js +0 -6
  102. package/dist/dev-update-SZ2Z4WCQ.js +0 -6
  103. package/dist/llm-XXLYPIOF.js +0 -16
  104. package/dist/mlx-setup-XKU67WCT.js +0 -289
  105. package/dist/search-pipeline-4K4OJSSS.js +0 -255
  106. package/dist/search-pipeline-4PRS6LI7.js +0 -280
  107. package/dist/search-pipeline-7UJMXPLO.js +0 -280
  108. package/dist/search-pipeline-CBV25NX7.js +0 -99
  109. package/dist/search-pipeline-DQTRLGBH.js +0 -74
  110. package/dist/search-pipeline-HNG37REH.js +0 -282
  111. package/dist/search-pipeline-IZFPLBUB.js +0 -280
  112. package/dist/search-pipeline-MID6F26Q.js +0 -73
  113. package/dist/search-pipeline-N52JZFNN.js +0 -282
  114. package/dist/search-pipeline-OPB2PRQQ.js +0 -280
  115. package/dist/search-pipeline-VXTE5HAD.js +0 -262
  116. package/dist/search-pipeline-XHFKADRG.js +0 -73
  117. package/dist/worker-demo.js +0 -186
  118. package/dist/worker-mcp.js +0 -404
  119. package/scripts/crystal-capture 2.sh +0 -29
  120. package/scripts/deploy-cloud 2.sh +0 -153
package/dist/installer.js CHANGED
@@ -1,29 +1,724 @@
1
- import {
2
- deployBackupScript,
3
- deployCaptureScript,
4
- getAgentId,
5
- installCron,
6
- ldmPaths,
7
- loadAgentConfig,
8
- saveAgentConfig,
9
- scaffoldLdm
10
- } from "./chunk-DFQ72B7M.js";
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
11
10
 
12
- // src/installer.ts
13
- import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, copyFileSync, readdirSync, statSync } from "fs";
11
+ // src/ldm.ts
12
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, chmodSync, readdirSync } from "fs";
14
13
  import { join, dirname } from "path";
15
14
  import { execSync } from "child_process";
16
15
  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");
16
+ function loadAgentConfig(id) {
17
+ const cfgPath = join(LDM_ROOT, "agents", id, "config.json");
18
+ try {
19
+ if (existsSync(cfgPath)) return JSON.parse(readFileSync(cfgPath, "utf-8"));
20
+ } catch {
21
+ }
22
+ return null;
23
+ }
24
+ function saveAgentConfig(id, config) {
25
+ const dir = join(LDM_ROOT, "agents", id);
26
+ mkdirSync(dir, { recursive: true });
27
+ writeFileSync(join(dir, "config.json"), JSON.stringify(config, null, 2) + "\n");
28
+ }
29
+ function getAgentId(harnessHint) {
30
+ if (process.env.CRYSTAL_AGENT_ID) return process.env.CRYSTAL_AGENT_ID;
31
+ const agentsDir = join(LDM_ROOT, "agents");
32
+ if (existsSync(agentsDir)) {
33
+ try {
34
+ for (const d of readdirSync(agentsDir)) {
35
+ const cfg = loadAgentConfig(d);
36
+ if (!cfg || !cfg.agentId) continue;
37
+ if (!harnessHint) return cfg.agentId;
38
+ if (harnessHint === "claude-code" && cfg.harness === "claude-code-cli") return cfg.agentId;
39
+ if (harnessHint === "openclaw" && cfg.harness === "openclaw") return cfg.agentId;
40
+ }
41
+ } catch {
42
+ }
43
+ }
44
+ return harnessHint === "openclaw" ? "oc-lesa-mini" : "cc-mini";
45
+ }
46
+ function ldmPaths(agentId) {
47
+ const id = agentId || getAgentId();
48
+ const agentRoot = join(LDM_ROOT, "agents", id);
49
+ return {
50
+ root: LDM_ROOT,
51
+ bin: join(LDM_ROOT, "bin"),
52
+ secrets: join(LDM_ROOT, "secrets"),
53
+ state: join(LDM_ROOT, "state"),
54
+ config: join(LDM_ROOT, "config.json"),
55
+ crystalDb: join(LDM_ROOT, "memory", "crystal.db"),
56
+ crystalLance: join(LDM_ROOT, "memory", "lance"),
57
+ agentRoot,
58
+ transcripts: join(agentRoot, "memory", "transcripts"),
59
+ sessions: join(agentRoot, "memory", "sessions"),
60
+ daily: join(agentRoot, "memory", "daily"),
61
+ journals: join(agentRoot, "memory", "journals"),
62
+ workspace: join(agentRoot, "memory", "workspace")
63
+ };
64
+ }
65
+ function loadConfig() {
66
+ const configPath = join(LDM_ROOT, "config.json");
67
+ try {
68
+ if (existsSync(configPath)) {
69
+ return JSON.parse(readFileSync(configPath, "utf-8"));
70
+ }
71
+ } catch {
72
+ }
73
+ return null;
74
+ }
75
+ function saveConfig(config) {
76
+ const configPath = join(LDM_ROOT, "config.json");
77
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
78
+ }
79
+ function scaffoldLdm(agentId) {
80
+ const paths = ldmPaths(agentId);
81
+ mkdirSync(join(paths.root, "memory"), { recursive: true });
82
+ mkdirSync(paths.crystalLance, { recursive: true });
83
+ mkdirSync(paths.bin, { recursive: true });
84
+ mkdirSync(paths.secrets, { recursive: true, mode: 448 });
85
+ mkdirSync(paths.state, { recursive: true });
86
+ mkdirSync(paths.transcripts, { recursive: true });
87
+ mkdirSync(paths.sessions, { recursive: true });
88
+ mkdirSync(paths.daily, { recursive: true });
89
+ mkdirSync(paths.journals, { recursive: true });
90
+ mkdirSync(paths.workspace, { recursive: true });
91
+ const id = agentId || getAgentId();
92
+ let config = loadConfig();
93
+ if (!config) {
94
+ config = {
95
+ version: "1.0.0",
96
+ agents: [id],
97
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
98
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
99
+ };
100
+ } else {
101
+ if (!config.agents.includes(id)) {
102
+ config.agents.push(id);
103
+ }
104
+ config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
105
+ }
106
+ saveConfig(config);
107
+ return paths;
108
+ }
109
+ function deployCaptureScript() {
110
+ const paths = ldmPaths();
111
+ mkdirSync(paths.bin, { recursive: true });
112
+ const thisDir = dirname(fileURLToPath(import.meta.url));
113
+ let scriptSrc = join(thisDir, "crystal-capture.sh");
114
+ if (!existsSync(scriptSrc)) {
115
+ scriptSrc = join(thisDir, "..", "scripts", "crystal-capture.sh");
116
+ }
117
+ const scriptDest = join(paths.bin, "crystal-capture.sh");
118
+ if (!existsSync(scriptSrc)) {
119
+ throw new Error(`crystal-capture.sh not found at ${scriptSrc}`);
120
+ }
121
+ copyFileSync(scriptSrc, scriptDest);
122
+ chmodSync(scriptDest, 493);
123
+ return scriptDest;
124
+ }
125
+ function isCrystalCaptureLine(line) {
126
+ return line === CRON_TAG || line.includes("crystal-capture.sh") && line.startsWith("*");
127
+ }
128
+ function installCron() {
129
+ mkdirSync(join(HOME, ".ldm", "logs"), { recursive: true });
130
+ let existing = "";
131
+ try {
132
+ existing = execSync("crontab -l 2>/dev/null", { encoding: "utf8" });
133
+ } catch {
134
+ }
135
+ const lines = existing.split("\n").filter((line) => !isCrystalCaptureLine(line));
136
+ lines.push(CRON_TAG);
137
+ lines.push(CRON_ENTRY);
138
+ const newCrontab = lines.filter((l, i, arr) => !(l === "" && i === arr.length - 1)).join("\n") + "\n";
139
+ execSync("crontab -", { input: newCrontab, encoding: "utf8" });
140
+ }
141
+ function deployBackupScript() {
142
+ const paths = ldmPaths();
143
+ mkdirSync(paths.bin, { recursive: true });
144
+ const thisDir = dirname(fileURLToPath(import.meta.url));
145
+ let scriptSrc = join(thisDir, "ldm-backup.sh");
146
+ if (!existsSync(scriptSrc)) {
147
+ scriptSrc = join(thisDir, "..", "scripts", "ldm-backup.sh");
148
+ }
149
+ const scriptDest = join(paths.bin, "ldm-backup.sh");
150
+ if (!existsSync(scriptSrc)) {
151
+ throw new Error(`ldm-backup.sh not found at ${scriptSrc}`);
152
+ }
153
+ copyFileSync(scriptSrc, scriptDest);
154
+ chmodSync(scriptDest, 493);
155
+ return scriptDest;
156
+ }
157
+ function resolveStatePath(filename) {
158
+ const paths = ldmPaths();
159
+ const ldmPath = join(paths.state, filename);
160
+ if (existsSync(ldmPath)) return ldmPath;
161
+ const legacyPath = join(LEGACY_OC_DIR, "memory", filename);
162
+ if (existsSync(legacyPath)) return legacyPath;
163
+ return ldmPath;
164
+ }
165
+ function stateWritePath(filename) {
166
+ const paths = ldmPaths();
167
+ const dir = paths.state;
168
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
169
+ return join(dir, filename);
170
+ }
171
+ function resolveSecretPath(filename) {
172
+ const paths = ldmPaths();
173
+ const ldmPath = join(paths.secrets, filename);
174
+ if (existsSync(ldmPath)) return ldmPath;
175
+ const legacyPath = join(LEGACY_OC_DIR, "secrets", filename);
176
+ if (existsSync(legacyPath)) return legacyPath;
177
+ return ldmPath;
178
+ }
179
+ var HOME, LDM_ROOT, CRON_TAG, CRON_ENTRY, LEGACY_OC_DIR;
180
+ var init_ldm = __esm({
181
+ "src/ldm.ts"() {
182
+ "use strict";
183
+ HOME = process.env.HOME || "";
184
+ LDM_ROOT = join(HOME, ".ldm");
185
+ CRON_TAG = "# crystal-capture";
186
+ CRON_ENTRY = "* * * * * ~/.ldm/bin/crystal-capture.sh >> ~/.ldm/logs/crystal-capture.log 2>&1";
187
+ LEGACY_OC_DIR = join(HOME, ".openclaw");
188
+ }
189
+ });
190
+
191
+ // src/mlx-setup.ts
192
+ var mlx_setup_exports = {};
193
+ __export(mlx_setup_exports, {
194
+ MLX_CONFIG: () => MLX_CONFIG,
195
+ canRunMlx: () => canRunMlx,
196
+ createLaunchAgent: () => createLaunchAgent,
197
+ detectPlatform: () => detectPlatform,
198
+ doctorCheck: () => doctorCheck,
199
+ installMlxLm: () => installMlxLm,
200
+ isMlxLmInstalled: () => isMlxLmInstalled,
201
+ isServerRunning: () => isServerRunning,
202
+ setupMlx: () => setupMlx,
203
+ startServer: () => startServer,
204
+ stopServer: () => stopServer,
205
+ verifyServer: () => verifyServer
206
+ });
207
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
208
+ import { execSync as execSync2 } from "child_process";
209
+ import { join as join2 } from "path";
210
+ import { homedir } from "os";
211
+ function detectPlatform() {
212
+ const platform = process.platform;
213
+ const arch = process.arch;
214
+ if (platform === "darwin") {
215
+ return arch === "arm64" ? "apple-silicon" : "intel-mac";
216
+ }
217
+ if (platform === "linux") return "linux";
218
+ return "other";
219
+ }
220
+ function canRunMlx() {
221
+ return detectPlatform() === "apple-silicon";
222
+ }
223
+ function findPython() {
224
+ const candidates = ["python3", "/opt/homebrew/bin/python3", "/usr/local/bin/python3"];
225
+ for (const cmd of candidates) {
226
+ try {
227
+ const version = execSync2(`${cmd} --version 2>&1`, { encoding: "utf-8", timeout: 5e3 }).trim();
228
+ const match = version.match(/Python (\d+)\.(\d+)/);
229
+ if (match && parseInt(match[1]) >= 3 && parseInt(match[2]) >= 10) {
230
+ const realPath = execSync2(`which ${cmd} 2>/dev/null`, { encoding: "utf-8", timeout: 3e3 }).trim();
231
+ return realPath || cmd;
232
+ }
233
+ } catch {
234
+ }
235
+ }
236
+ return null;
237
+ }
238
+ function findInstaller() {
239
+ try {
240
+ execSync2("uv --version 2>/dev/null", { encoding: "utf-8", timeout: 3e3 });
241
+ return "uv";
242
+ } catch {
243
+ }
244
+ try {
245
+ execSync2("pip3 --version 2>/dev/null", { encoding: "utf-8", timeout: 3e3 });
246
+ return "pip3";
247
+ } catch {
248
+ }
249
+ return null;
250
+ }
251
+ function isMlxLmInstalled() {
252
+ try {
253
+ execSync2('python3 -c "import mlx_lm" 2>/dev/null', { timeout: 5e3 });
254
+ return true;
255
+ } catch {
256
+ return false;
257
+ }
258
+ }
259
+ function installMlxLm(steps) {
260
+ const installer = findInstaller();
261
+ if (!installer) {
262
+ steps.push("No pip3 or uv found. Cannot install mlx-lm.");
263
+ return false;
264
+ }
265
+ const cmd = installer === "uv" ? "uv pip install mlx-lm" : "pip3 install mlx-lm";
266
+ steps.push(`Installing mlx-lm via ${installer}...`);
267
+ try {
268
+ execSync2(cmd, { encoding: "utf-8", timeout: 12e4, stdio: "pipe" });
269
+ steps.push("mlx-lm installed successfully.");
270
+ return true;
271
+ } catch (err) {
272
+ if (installer === "pip3") {
273
+ try {
274
+ execSync2("pip3 install --user mlx-lm", { encoding: "utf-8", timeout: 12e4, stdio: "pipe" });
275
+ steps.push("mlx-lm installed (--user) successfully.");
276
+ return true;
277
+ } catch {
278
+ }
279
+ }
280
+ steps.push(`mlx-lm install failed: ${err.message.slice(0, 200)}`);
281
+ return false;
282
+ }
283
+ }
284
+ function isServerRunning() {
285
+ try {
286
+ const state = loadState();
287
+ const port = state?.port || MLX_PORT;
288
+ execSync2(`curl -s -o /dev/null -w "%{http_code}" http://localhost:${port}/v1/models`, {
289
+ encoding: "utf-8",
290
+ timeout: 3e3
291
+ });
292
+ return true;
293
+ } catch {
294
+ return false;
295
+ }
296
+ }
297
+ function createLaunchAgent(pythonPath, steps) {
298
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
299
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
300
+ <plist version="1.0">
301
+ <dict>
302
+ <key>Label</key>
303
+ <string>${MLX_PLIST_LABEL}</string>
304
+ <key>ProgramArguments</key>
305
+ <array>
306
+ <string>${pythonPath}</string>
307
+ <string>-m</string>
308
+ <string>mlx_lm.server</string>
309
+ <string>--model</string>
310
+ <string>${MLX_MODEL}</string>
311
+ <string>--port</string>
312
+ <string>${MLX_PORT}</string>
313
+ </array>
314
+ <key>RunAtLoad</key>
315
+ <true/>
316
+ <key>KeepAlive</key>
317
+ <true/>
318
+ <key>StandardOutPath</key>
319
+ <string>${MLX_LOG_PATH}</string>
320
+ <key>StandardErrorPath</key>
321
+ <string>${MLX_LOG_PATH}</string>
322
+ </dict>
323
+ </plist>`;
324
+ try {
325
+ writeFileSync2(MLX_PLIST_PATH, plist);
326
+ execSync2(`launchctl load "${MLX_PLIST_PATH}" 2>/dev/null`, { timeout: 5e3 });
327
+ steps.push(`LaunchAgent installed at ${MLX_PLIST_PATH}`);
328
+ steps.push(`MLX server will start on port ${MLX_PORT}`);
329
+ return true;
330
+ } catch (err) {
331
+ steps.push(`LaunchAgent install failed: ${err.message}`);
332
+ return false;
333
+ }
334
+ }
335
+ function startServer(steps) {
336
+ try {
337
+ execSync2(`launchctl kickstart -kp gui/$(id -u)/${MLX_PLIST_LABEL} 2>/dev/null`, { timeout: 1e4 });
338
+ steps.push("MLX server started.");
339
+ return true;
340
+ } catch {
341
+ steps.push("MLX server start failed. Check /tmp/mlx-server.log");
342
+ return false;
343
+ }
344
+ }
345
+ function stopServer() {
346
+ try {
347
+ execSync2(`launchctl unload "${MLX_PLIST_PATH}" 2>/dev/null`, { timeout: 5e3 });
348
+ return true;
349
+ } catch {
350
+ return false;
351
+ }
352
+ }
353
+ function loadState() {
354
+ try {
355
+ if (existsSync2(MLX_STATE_FILE)) {
356
+ return JSON.parse(readFileSync2(MLX_STATE_FILE, "utf-8"));
357
+ }
358
+ } catch {
359
+ }
360
+ return null;
361
+ }
362
+ function saveState(state) {
363
+ const dir = join2(HOME2, ".ldm", "state");
364
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
365
+ writeFileSync2(MLX_STATE_FILE, JSON.stringify(state, null, 2) + "\n");
366
+ }
367
+ async function verifyServer(steps) {
368
+ const state = loadState();
369
+ const port = state?.port || MLX_PORT;
370
+ for (let i = 0; i < 15; i++) {
371
+ try {
372
+ const resp = await fetch(`http://localhost:${port}/v1/models`, {
373
+ signal: AbortSignal.timeout(2e3)
374
+ });
375
+ if (resp.ok) {
376
+ const data = await resp.json();
377
+ const model = data?.data?.[0]?.id || "unknown";
378
+ steps.push(`MLX server verified: ${model} on port ${port}`);
379
+ return true;
380
+ }
381
+ } catch {
382
+ }
383
+ await new Promise((r) => setTimeout(r, 2e3));
384
+ }
385
+ steps.push("MLX server did not respond within 30 seconds. Check /tmp/mlx-server.log");
386
+ return false;
387
+ }
388
+ async function setupMlx(options) {
389
+ const steps = [];
390
+ const platform = detectPlatform();
391
+ if (platform !== "apple-silicon") {
392
+ steps.push(`Platform: ${platform}. MLX requires Apple Silicon. Skipping.`);
393
+ return { ok: false, steps };
394
+ }
395
+ steps.push("Platform: Apple Silicon detected.");
396
+ const pythonPath = findPython();
397
+ if (!pythonPath) {
398
+ steps.push("Python 3.10+ not found. Install via: brew install python3");
399
+ return { ok: false, steps };
400
+ }
401
+ steps.push(`Python: ${pythonPath}`);
402
+ if (!isMlxLmInstalled()) {
403
+ if (!options?.yes) {
404
+ steps.push("mlx-lm not installed. Run with --yes to auto-install, or: pip3 install mlx-lm");
405
+ return { ok: false, steps };
406
+ }
407
+ const installed = installMlxLm(steps);
408
+ if (!installed) return { ok: false, steps };
409
+ } else {
410
+ steps.push("mlx-lm: already installed.");
411
+ }
412
+ if (!existsSync2(MLX_PLIST_PATH)) {
413
+ const created = createLaunchAgent(pythonPath, steps);
414
+ if (!created) return { ok: false, steps };
415
+ } else {
416
+ steps.push("LaunchAgent: already installed.");
417
+ }
418
+ saveState({
419
+ installed: true,
420
+ port: MLX_PORT,
421
+ model: MLX_MODEL,
422
+ pythonPath,
423
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
424
+ });
425
+ if (!isServerRunning()) {
426
+ startServer(steps);
427
+ steps.push(`Waiting for model to load (~1.5 GB on first run)...`);
428
+ const verified = await verifyServer(steps);
429
+ if (!verified) {
430
+ steps.push("Server started but not yet responding. It may still be downloading the model.");
431
+ steps.push(`Check: tail -f ${MLX_LOG_PATH}`);
432
+ }
433
+ } else {
434
+ steps.push(`MLX server: already running on port ${MLX_PORT}`);
435
+ }
436
+ return { ok: true, steps };
437
+ }
438
+ function doctorCheck() {
439
+ const platform = detectPlatform();
440
+ if (platform !== "apple-silicon") {
441
+ return { status: "skip", detail: `${platform} (MLX requires Apple Silicon)` };
442
+ }
443
+ const state = loadState();
444
+ if (!state || !state.installed) {
445
+ return {
446
+ status: "warn",
447
+ detail: "not installed",
448
+ fix: "crystal init (will offer MLX setup)"
449
+ };
450
+ }
451
+ if (isServerRunning()) {
452
+ return { status: "ok", detail: `running on port ${state.port} (${state.model})` };
453
+ }
454
+ if (existsSync2(MLX_PLIST_PATH)) {
455
+ return {
456
+ status: "warn",
457
+ detail: "installed but not running",
458
+ fix: `launchctl kickstart -kp gui/$(id -u)/${MLX_PLIST_LABEL}`
459
+ };
460
+ }
461
+ return {
462
+ status: "warn",
463
+ detail: "installed but LaunchAgent missing",
464
+ fix: "crystal init (will recreate LaunchAgent)"
465
+ };
466
+ }
467
+ var HOME2, MLX_PORT, MLX_MODEL, MLX_STATE_FILE, MLX_PLIST_LABEL, MLX_PLIST_PATH, MLX_LOG_PATH, MLX_CONFIG;
468
+ var init_mlx_setup = __esm({
469
+ "src/mlx-setup.ts"() {
470
+ "use strict";
471
+ HOME2 = homedir();
472
+ MLX_PORT = 18791;
473
+ MLX_MODEL = "mlx-community/Qwen2.5-3B-Instruct-4bit";
474
+ MLX_STATE_FILE = join2(HOME2, ".ldm", "state", "mlx-server.json");
475
+ MLX_PLIST_LABEL = "ai.ldm.mlx-server";
476
+ MLX_PLIST_PATH = join2(HOME2, "Library", "LaunchAgents", `${MLX_PLIST_LABEL}.plist`);
477
+ MLX_LOG_PATH = "/tmp/mlx-server.log";
478
+ MLX_CONFIG = {
479
+ port: MLX_PORT,
480
+ model: MLX_MODEL,
481
+ plistPath: MLX_PLIST_PATH,
482
+ logPath: MLX_LOG_PATH,
483
+ stateFile: MLX_STATE_FILE
484
+ };
485
+ }
486
+ });
487
+
488
+ // src/role.ts
489
+ var role_exports = {};
490
+ __export(role_exports, {
491
+ demoteToNode: () => demoteToNode,
492
+ detectRole: () => detectRole,
493
+ loadRoleState: () => loadRoleState,
494
+ promoteToCore: () => promoteToCore
495
+ });
496
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
497
+ function loadRoleState() {
498
+ try {
499
+ const path = resolveStatePath(STATE_FILE);
500
+ if (existsSync3(path)) {
501
+ return JSON.parse(readFileSync3(path, "utf-8"));
502
+ }
503
+ } catch {
504
+ }
505
+ return null;
506
+ }
507
+ function saveRoleState(state) {
508
+ const writePath = stateWritePath(STATE_FILE);
509
+ writeFileSync3(writePath, JSON.stringify(state, null, 2) + "\n");
510
+ }
511
+ function hasLocalEmbeddingProvider() {
512
+ if (process.env.OPENAI_API_KEY) return true;
513
+ if (process.env.GOOGLE_API_KEY && process.env.CRYSTAL_EMBEDDING_PROVIDER === "google") return true;
514
+ if (process.env.CRYSTAL_EMBEDDING_PROVIDER === "ollama") return true;
515
+ return false;
516
+ }
517
+ function hasRelayKey() {
518
+ const keyPath = resolveSecretPath("crystal-relay-key");
519
+ return existsSync3(keyPath);
520
+ }
521
+ function detectRole() {
522
+ const agentId = getAgentId();
523
+ const paths = ldmPaths(agentId);
524
+ const relayUrl = process.env.CRYSTAL_RELAY_URL || null;
525
+ const relayToken = !!process.env.CRYSTAL_RELAY_TOKEN;
526
+ const relayKeyExists = hasRelayKey();
527
+ const localEmbeddings = hasLocalEmbeddingProvider();
528
+ const localDb = existsSync3(paths.crystalDb);
529
+ const state = loadRoleState();
530
+ if (state && state.override) {
531
+ return {
532
+ role: state.role,
533
+ source: "state-file",
534
+ relayUrl,
535
+ relayToken,
536
+ relayKeyExists,
537
+ agentId,
538
+ hasLocalEmbeddings: localEmbeddings,
539
+ hasLocalDb: localDb
540
+ };
541
+ }
542
+ let role = "core";
543
+ if ((relayUrl || relayKeyExists) && !localEmbeddings) {
544
+ role = "node";
545
+ } else if ((relayUrl || relayKeyExists) && localEmbeddings) {
546
+ role = "core";
547
+ }
548
+ return {
549
+ role,
550
+ source: "auto-detected",
551
+ relayUrl,
552
+ relayToken,
553
+ relayKeyExists,
554
+ agentId,
555
+ hasLocalEmbeddings: localEmbeddings,
556
+ hasLocalDb: localDb
557
+ };
558
+ }
559
+ function promoteToCore() {
560
+ saveRoleState({
561
+ role: "core",
562
+ override: true,
563
+ agentId: getAgentId(),
564
+ setAt: (/* @__PURE__ */ new Date()).toISOString()
565
+ });
566
+ }
567
+ function demoteToNode(relayUrl) {
568
+ saveRoleState({
569
+ role: "node",
570
+ override: true,
571
+ relayUrl,
572
+ agentId: getAgentId(),
573
+ setAt: (/* @__PURE__ */ new Date()).toISOString()
574
+ });
575
+ }
576
+ var STATE_FILE;
577
+ var init_role = __esm({
578
+ "src/role.ts"() {
579
+ "use strict";
580
+ init_ldm();
581
+ STATE_FILE = "crystal-role.json";
582
+ }
583
+ });
584
+
585
+ // src/crypto.ts
586
+ import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
587
+ import { createCipheriv, createDecipheriv, createHmac, randomBytes, hkdfSync } from "crypto";
588
+ import { createHash } from "crypto";
589
+ function loadRelayKey() {
590
+ if (!existsSync4(KEY_PATH)) {
591
+ throw new Error(
592
+ `Relay key not found at ${KEY_PATH}
593
+ Generate one: mkdir -p ~/.ldm/secrets && openssl rand -base64 32 > ~/.ldm/secrets/crystal-relay-key && chmod 600 ~/.ldm/secrets/crystal-relay-key
594
+ Or run: crystal pair`
595
+ );
596
+ }
597
+ const raw = readFileSync4(KEY_PATH, "utf-8").trim();
598
+ const key = Buffer.from(raw, "base64");
599
+ if (key.length !== 32) {
600
+ throw new Error(`Relay key must be 32 bytes (256 bits). Got ${key.length} bytes. Regenerate with: openssl rand -base64 32`);
601
+ }
602
+ return key;
603
+ }
604
+ function generateRelayKey() {
605
+ return randomBytes(32);
606
+ }
607
+ function encodePairingString(key) {
608
+ if (key.length !== 32) throw new Error("Key must be 32 bytes");
609
+ return `mc1:${key.toString("base64")}`;
610
+ }
611
+ function decodePairingString(str) {
612
+ const trimmed = str.trim();
613
+ if (!trimmed.startsWith("mc1:")) {
614
+ throw new Error("Invalid pairing string (expected mc1: prefix)");
615
+ }
616
+ const key = Buffer.from(trimmed.slice(4), "base64");
617
+ if (key.length !== 32) {
618
+ throw new Error(`Invalid key length: expected 32 bytes, got ${key.length}`);
619
+ }
620
+ return key;
621
+ }
622
+ var KEY_PATH, RELAY_KEY_PATH;
623
+ var init_crypto = __esm({
624
+ "src/crypto.ts"() {
625
+ "use strict";
626
+ init_ldm();
627
+ KEY_PATH = process.env.CRYSTAL_RELAY_KEY_PATH || resolveSecretPath("crystal-relay-key");
628
+ RELAY_KEY_PATH = KEY_PATH;
629
+ }
630
+ });
631
+
632
+ // src/pair.ts
633
+ var pair_exports = {};
634
+ __export(pair_exports, {
635
+ pairReceive: () => pairReceive,
636
+ pairShow: () => pairShow
637
+ });
638
+ import { existsSync as existsSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, chmodSync as chmodSync2 } from "fs";
639
+ import { dirname as dirname2 } from "path";
640
+ import qrcode from "qrcode-terminal";
641
+ function generateQR(text) {
642
+ return new Promise((resolve) => {
643
+ qrcode.generate(text, { small: true }, (code) => {
644
+ resolve(code);
645
+ });
646
+ });
647
+ }
648
+ function saveKey(key) {
649
+ const dir = dirname2(RELAY_KEY_PATH);
650
+ if (!existsSync5(dir)) {
651
+ mkdirSync4(dir, { recursive: true });
652
+ }
653
+ writeFileSync4(RELAY_KEY_PATH, key.toString("base64") + "\n", { mode: 384 });
654
+ try {
655
+ chmodSync2(RELAY_KEY_PATH, 384);
656
+ } catch {
657
+ }
658
+ }
659
+ async function pairShow() {
660
+ let key;
661
+ try {
662
+ key = loadRelayKey();
663
+ console.log("Relay key found.\n");
664
+ } catch {
665
+ console.log("No relay key found. Generating one...");
666
+ key = generateRelayKey();
667
+ saveKey(key);
668
+ console.log(`Key saved to ${RELAY_KEY_PATH}
669
+ `);
670
+ }
671
+ const pairingString = encodePairingString(key);
672
+ console.log("Scan this QR code from your other device:\n");
673
+ const qr = await generateQR(pairingString);
674
+ console.log(qr);
675
+ console.log("Or copy this pairing code:\n");
676
+ console.log(` ${pairingString}
677
+ `);
678
+ console.log("On the other device, run:");
679
+ console.log(` crystal pair --code ${pairingString}
680
+ `);
681
+ }
682
+ function pairReceive(code) {
683
+ const key = decodePairingString(code);
684
+ if (existsSync5(RELAY_KEY_PATH)) {
685
+ try {
686
+ const existing = loadRelayKey();
687
+ if (existing.equals(key)) {
688
+ console.log("This device already has the same key. Nothing to do.");
689
+ return;
690
+ }
691
+ console.log("Replacing existing relay key with new key from pairing code.");
692
+ } catch {
693
+ }
694
+ }
695
+ saveKey(key);
696
+ console.log(`Key received and saved to ${RELAY_KEY_PATH}`);
697
+ console.log("Relay encryption is now active on this device.");
698
+ }
699
+ var init_pair = __esm({
700
+ "src/pair.ts"() {
701
+ "use strict";
702
+ init_crypto();
703
+ }
704
+ });
705
+
706
+ // src/installer.ts
707
+ init_ldm();
708
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, cpSync, copyFileSync as copyFileSync2, readdirSync as readdirSync2, statSync } from "fs";
709
+ import { join as join3, dirname as dirname3 } from "path";
710
+ import { execSync as execSync3 } from "child_process";
711
+ import { fileURLToPath as fileURLToPath2 } from "url";
712
+ var HOME3 = process.env.HOME || "";
713
+ var LDM_ROOT2 = join3(HOME3, ".ldm");
714
+ var OC_ROOT = join3(HOME3, ".openclaw");
715
+ var CC_SETTINGS = join3(HOME3, ".claude", "settings.json");
716
+ var CC_MCP = join3(HOME3, ".claude", ".mcp.json");
717
+ var OC_MCP = join3(OC_ROOT, ".mcp.json");
23
718
  function readVersion(pkgPath) {
24
719
  try {
25
- if (existsSync(pkgPath)) {
26
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
720
+ if (existsSync6(pkgPath)) {
721
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
27
722
  return pkg.version || null;
28
723
  }
29
724
  } catch {
@@ -31,20 +726,20 @@ function readVersion(pkgPath) {
31
726
  return null;
32
727
  }
33
728
  function getRepoRoot() {
34
- const thisDir = dirname(fileURLToPath(import.meta.url));
729
+ const thisDir = dirname3(fileURLToPath2(import.meta.url));
35
730
  let dir = thisDir;
36
731
  for (let i = 0; i < 5; i++) {
37
- const pkgPath = join(dir, "package.json");
38
- if (existsSync(pkgPath)) {
732
+ const pkgPath = join3(dir, "package.json");
733
+ if (existsSync6(pkgPath)) {
39
734
  try {
40
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
735
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
41
736
  if (pkg.name === "@wipcomputer/memory-crystal") return dir;
42
737
  } catch {
43
738
  }
44
739
  }
45
- dir = dirname(dir);
740
+ dir = dirname3(dir);
46
741
  }
47
- return dirname(thisDir);
742
+ return dirname3(thisDir);
48
743
  }
49
744
  function semverCompare(a, b) {
50
745
  const pa = a.split(".").map(Number);
@@ -59,7 +754,7 @@ function getLatestNpmVersion() {
59
754
  const names = ["@wipcomputer/memory-crystal", "memory-crystal"];
60
755
  for (const name of names) {
61
756
  try {
62
- const v = execSync(`npm view ${name} version 2>/dev/null`, { encoding: "utf-8", timeout: 1e4 }).trim();
757
+ const v = execSync3(`npm view ${name} version 2>/dev/null`, { encoding: "utf-8", timeout: 1e4 }).trim();
63
758
  if (v) return v;
64
759
  } catch {
65
760
  }
@@ -67,19 +762,19 @@ function getLatestNpmVersion() {
67
762
  return null;
68
763
  }
69
764
  function detectInstallState() {
70
- const ldmExtDir = join(LDM_ROOT, "extensions", "memory-crystal");
71
- const ocExtDir = join(OC_ROOT, "extensions", "memory-crystal");
765
+ const ldmExtDir = join3(LDM_ROOT2, "extensions", "memory-crystal");
766
+ const ocExtDir = join3(OC_ROOT, "extensions", "memory-crystal");
72
767
  const paths = ldmPaths();
73
- const installedVersion = readVersion(join(ldmExtDir, "package.json"));
768
+ const installedVersion = readVersion(join3(ldmExtDir, "package.json"));
74
769
  const repoRoot = getRepoRoot();
75
- let repoVersion = readVersion(join(repoRoot, "package.json")) || "0.0.0";
770
+ let repoVersion = readVersion(join3(repoRoot, "package.json")) || "0.0.0";
76
771
  const npmVersion = getLatestNpmVersion();
77
772
  if (npmVersion && semverCompare(npmVersion, repoVersion) > 0) repoVersion = npmVersion;
78
- const ccHookDeployed = existsSync(join(ldmExtDir, "dist", "cc-hook.js"));
773
+ const ccHookDeployed = existsSync6(join3(ldmExtDir, "dist", "cc-hook.js"));
79
774
  let ccHookConfigured = false;
80
775
  try {
81
- if (existsSync(CC_SETTINGS)) {
82
- const settings = JSON.parse(readFileSync(CC_SETTINGS, "utf-8"));
776
+ if (existsSync6(CC_SETTINGS)) {
777
+ const settings = JSON.parse(readFileSync5(CC_SETTINGS, "utf-8"));
83
778
  const stopHooks = settings?.hooks?.Stop;
84
779
  if (Array.isArray(stopHooks)) {
85
780
  ccHookConfigured = stopHooks.some((entry) => {
@@ -92,10 +787,10 @@ function detectInstallState() {
92
787
  } catch {
93
788
  }
94
789
  let mcpRegistered = false;
95
- for (const mcpPath of [CC_MCP, OC_MCP, join(process.cwd(), ".mcp.json")]) {
790
+ for (const mcpPath of [CC_MCP, OC_MCP, join3(process.cwd(), ".mcp.json")]) {
96
791
  try {
97
- if (existsSync(mcpPath)) {
98
- const config = JSON.parse(readFileSync(mcpPath, "utf-8"));
792
+ if (existsSync6(mcpPath)) {
793
+ const config = JSON.parse(readFileSync5(mcpPath, "utf-8"));
99
794
  if (config?.mcpServers?.["memory-crystal"]) {
100
795
  mcpRegistered = true;
101
796
  break;
@@ -106,24 +801,24 @@ function detectInstallState() {
106
801
  }
107
802
  if (!mcpRegistered) {
108
803
  try {
109
- execSync("claude mcp get memory-crystal 2>/dev/null", { encoding: "utf-8", timeout: 5e3, stdio: "pipe" });
804
+ execSync3("claude mcp get memory-crystal 2>/dev/null", { encoding: "utf-8", timeout: 5e3, stdio: "pipe" });
110
805
  mcpRegistered = true;
111
806
  } catch {
112
807
  }
113
808
  }
114
- const ocDetected = existsSync(join(OC_ROOT, "openclaw.json"));
115
- const ocPluginDeployed = existsSync(join(ocExtDir, "dist", "openclaw.js"));
809
+ const ocDetected = existsSync6(join3(OC_ROOT, "openclaw.json"));
810
+ const ocPluginDeployed = existsSync6(join3(ocExtDir, "dist", "openclaw.js"));
116
811
  let cronInstalled = false;
117
812
  try {
118
- const crontab = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
813
+ const crontab = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
119
814
  cronInstalled = crontab.includes("crystal-capture");
120
815
  } catch {
121
816
  }
122
817
  const role = "core";
123
- const relayKeyExists = existsSync(join(LDM_ROOT, "secrets", "crystal-relay-key"));
818
+ const relayKeyExists = existsSync6(join3(LDM_ROOT2, "secrets", "crystal-relay-key"));
124
819
  return {
125
- ldmExists: existsSync(LDM_ROOT),
126
- crystalDbExists: existsSync(paths.crystalDb),
820
+ ldmExists: existsSync6(LDM_ROOT2),
821
+ crystalDbExists: existsSync6(paths.crystalDb),
127
822
  ccHookDeployed,
128
823
  ccHookConfigured,
129
824
  mcpRegistered,
@@ -139,42 +834,42 @@ function detectInstallState() {
139
834
  }
140
835
  function deployToLdm() {
141
836
  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)) {
837
+ const sourceDir = join3(repoRoot, "dist");
838
+ const extDir = join3(LDM_ROOT2, "extensions", "memory-crystal");
839
+ const destDist = join3(extDir, "dist");
840
+ if (!existsSync6(sourceDir)) {
146
841
  throw new Error(`dist/ not found at ${sourceDir}. Run "npm run build" first.`);
147
842
  }
148
- mkdirSync(destDist, { recursive: true });
149
- const distFiles = readdirSync(sourceDir);
843
+ mkdirSync5(destDist, { recursive: true });
844
+ const distFiles = readdirSync2(sourceDir);
150
845
  for (const file of distFiles) {
151
- const srcPath = join(sourceDir, file);
152
- const destPath = join(destDist, file);
846
+ const srcPath = join3(sourceDir, file);
847
+ const destPath = join3(destDist, file);
153
848
  const stat = statSync(srcPath);
154
849
  if (stat.isFile()) {
155
- copyFileSync(srcPath, destPath);
850
+ copyFileSync2(srcPath, destPath);
156
851
  } else if (stat.isDirectory()) {
157
852
  cpSync(srcPath, destPath, { recursive: true });
158
853
  }
159
854
  }
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"));
855
+ copyFileSync2(join3(repoRoot, "package.json"), join3(extDir, "package.json"));
856
+ const pluginJson = join3(repoRoot, "openclaw.plugin.json");
857
+ if (existsSync6(pluginJson)) {
858
+ copyFileSync2(pluginJson, join3(extDir, "openclaw.plugin.json"));
164
859
  }
165
- const skillsDir = join(repoRoot, "skills");
166
- if (existsSync(skillsDir)) {
167
- cpSync(skillsDir, join(extDir, "skills"), { recursive: true });
860
+ const skillsDir = join3(repoRoot, "skills");
861
+ if (existsSync6(skillsDir)) {
862
+ cpSync(skillsDir, join3(extDir, "skills"), { recursive: true });
168
863
  }
169
- const version = readVersion(join(extDir, "package.json")) || "unknown";
864
+ const version = readVersion(join3(extDir, "package.json")) || "unknown";
170
865
  return { extensionDir: extDir, version };
171
866
  }
172
867
  function installLdmDeps() {
173
- const extDir = join(LDM_ROOT, "extensions", "memory-crystal");
174
- if (!existsSync(join(extDir, "package.json"))) {
868
+ const extDir = join3(LDM_ROOT2, "extensions", "memory-crystal");
869
+ if (!existsSync6(join3(extDir, "package.json"))) {
175
870
  throw new Error("package.json not found in LDM extension dir. Deploy first.");
176
871
  }
177
- execSync("npm install --omit=dev", {
872
+ execSync3("npm install --omit=dev", {
178
873
  cwd: extDir,
179
874
  encoding: "utf-8",
180
875
  stdio: "pipe",
@@ -183,42 +878,42 @@ function installLdmDeps() {
183
878
  }
184
879
  function deployToOpenClaw() {
185
880
  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)) {
881
+ const sourceDir = join3(repoRoot, "dist");
882
+ const extDir = join3(OC_ROOT, "extensions", "memory-crystal");
883
+ const destDist = join3(extDir, "dist");
884
+ if (!existsSync6(sourceDir)) {
190
885
  throw new Error(`dist/ not found at ${sourceDir}. Run "npm run build" first.`);
191
886
  }
192
- mkdirSync(destDist, { recursive: true });
193
- const distFiles = readdirSync(sourceDir);
887
+ mkdirSync5(destDist, { recursive: true });
888
+ const distFiles = readdirSync2(sourceDir);
194
889
  for (const file of distFiles) {
195
- const srcPath = join(sourceDir, file);
196
- const destPath = join(destDist, file);
890
+ const srcPath = join3(sourceDir, file);
891
+ const destPath = join3(destDist, file);
197
892
  const stat = statSync(srcPath);
198
893
  if (stat.isFile()) {
199
- copyFileSync(srcPath, destPath);
894
+ copyFileSync2(srcPath, destPath);
200
895
  } else if (stat.isDirectory()) {
201
896
  cpSync(srcPath, destPath, { recursive: true });
202
897
  }
203
898
  }
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"));
899
+ copyFileSync2(join3(repoRoot, "package.json"), join3(extDir, "package.json"));
900
+ const pluginJson = join3(repoRoot, "openclaw.plugin.json");
901
+ if (existsSync6(pluginJson)) {
902
+ copyFileSync2(pluginJson, join3(extDir, "openclaw.plugin.json"));
208
903
  }
209
- const skillsDir = join(repoRoot, "skills");
210
- if (existsSync(skillsDir)) {
211
- cpSync(skillsDir, join(extDir, "skills"), { recursive: true });
904
+ const skillsDir = join3(repoRoot, "skills");
905
+ if (existsSync6(skillsDir)) {
906
+ cpSync(skillsDir, join3(extDir, "skills"), { recursive: true });
212
907
  }
213
- const version = readVersion(join(extDir, "package.json")) || "unknown";
908
+ const version = readVersion(join3(extDir, "package.json")) || "unknown";
214
909
  return { extensionDir: extDir, version };
215
910
  }
216
911
  function installOcDeps() {
217
- const extDir = join(OC_ROOT, "extensions", "memory-crystal");
218
- if (!existsSync(join(extDir, "package.json"))) {
912
+ const extDir = join3(OC_ROOT, "extensions", "memory-crystal");
913
+ if (!existsSync6(join3(extDir, "package.json"))) {
219
914
  throw new Error("package.json not found in OC extension dir. Deploy first.");
220
915
  }
221
- execSync("npm install --omit=dev", {
916
+ execSync3("npm install --omit=dev", {
222
917
  cwd: extDir,
223
918
  encoding: "utf-8",
224
919
  stdio: "pipe",
@@ -226,11 +921,11 @@ function installOcDeps() {
226
921
  });
227
922
  }
228
923
  function configureCCHook() {
229
- const hookCommand = `node ${join(LDM_ROOT, "extensions", "memory-crystal", "dist", "cc-hook.js")}`;
924
+ const hookCommand = `node ${join3(LDM_ROOT2, "extensions", "memory-crystal", "dist", "cc-hook.js")}`;
230
925
  let settings = {};
231
- if (existsSync(CC_SETTINGS)) {
926
+ if (existsSync6(CC_SETTINGS)) {
232
927
  try {
233
- settings = JSON.parse(readFileSync(CC_SETTINGS, "utf-8"));
928
+ settings = JSON.parse(readFileSync5(CC_SETTINGS, "utf-8"));
234
929
  } catch {
235
930
  throw new Error(`~/.claude/settings.json exists but is not valid JSON. Fix it manually before proceeding.`);
236
931
  }
@@ -254,14 +949,14 @@ function configureCCHook() {
254
949
  } else {
255
950
  settings.hooks.Stop.push(hookEntry);
256
951
  }
257
- mkdirSync(join(HOME, ".claude"), { recursive: true });
258
- writeFileSync(CC_SETTINGS, JSON.stringify(settings, null, 2) + "\n");
952
+ mkdirSync5(join3(HOME3, ".claude"), { recursive: true });
953
+ writeFileSync5(CC_SETTINGS, JSON.stringify(settings, null, 2) + "\n");
259
954
  }
260
955
  function registerMCPServer() {
261
- const mcpServerPath = join(LDM_ROOT, "extensions", "memory-crystal", "dist", "mcp-server.js");
956
+ const mcpServerPath = join3(LDM_ROOT2, "extensions", "memory-crystal", "dist", "mcp-server.js");
262
957
  const addCmd = `claude mcp add --scope user -e OPENCLAW_HOME=${OC_ROOT} memory-crystal -- node "${mcpServerPath}"`;
263
958
  try {
264
- execSync(addCmd, {
959
+ execSync3(addCmd, {
265
960
  encoding: "utf-8",
266
961
  stdio: "pipe",
267
962
  timeout: 15e3
@@ -271,17 +966,17 @@ function registerMCPServer() {
271
966
  const output = (err.stderr || "") + (err.stdout || "");
272
967
  if (output.includes("already exists")) {
273
968
  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 });
969
+ execSync3("claude mcp remove memory-crystal --scope user", { encoding: "utf-8", stdio: "pipe", timeout: 1e4 });
970
+ execSync3(addCmd, { encoding: "utf-8", stdio: "pipe", timeout: 15e3 });
276
971
  } catch {
277
972
  }
278
973
  return;
279
974
  }
280
975
  }
281
976
  let config = {};
282
- if (existsSync(CC_MCP)) {
977
+ if (existsSync6(CC_MCP)) {
283
978
  try {
284
- config = JSON.parse(readFileSync(CC_MCP, "utf-8"));
979
+ config = JSON.parse(readFileSync5(CC_MCP, "utf-8"));
285
980
  } catch {
286
981
  }
287
982
  }
@@ -291,15 +986,15 @@ function registerMCPServer() {
291
986
  args: [mcpServerPath],
292
987
  env: { OPENCLAW_HOME: OC_ROOT }
293
988
  };
294
- mkdirSync(join(HOME, ".claude"), { recursive: true });
295
- writeFileSync(CC_MCP, JSON.stringify(config, null, 2) + "\n");
989
+ mkdirSync5(join3(HOME3, ".claude"), { recursive: true });
990
+ writeFileSync5(CC_MCP, JSON.stringify(config, null, 2) + "\n");
296
991
  }
297
992
  function registerOcMCPServer() {
298
- const mcpServerPath = join(OC_ROOT, "extensions", "memory-crystal", "dist", "mcp-server.js");
993
+ const mcpServerPath = join3(OC_ROOT, "extensions", "memory-crystal", "dist", "mcp-server.js");
299
994
  let config = {};
300
- if (existsSync(OC_MCP)) {
995
+ if (existsSync6(OC_MCP)) {
301
996
  try {
302
- config = JSON.parse(readFileSync(OC_MCP, "utf-8"));
997
+ config = JSON.parse(readFileSync5(OC_MCP, "utf-8"));
303
998
  } catch {
304
999
  }
305
1000
  }
@@ -309,21 +1004,21 @@ function registerOcMCPServer() {
309
1004
  args: [mcpServerPath],
310
1005
  env: { OPENCLAW_HOME: OC_ROOT }
311
1006
  };
312
- writeFileSync(OC_MCP, JSON.stringify(config, null, 2) + "\n");
1007
+ writeFileSync5(OC_MCP, JSON.stringify(config, null, 2) + "\n");
313
1008
  }
314
1009
  function backupCrystalDb() {
315
1010
  const paths = ldmPaths();
316
1011
  const dbPath = paths.crystalDb;
317
- if (!existsSync(dbPath)) {
1012
+ if (!existsSync6(dbPath)) {
318
1013
  throw new Error(`crystal.db not found at ${dbPath}`);
319
1014
  }
320
1015
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
321
1016
  const backupPath = `${dbPath}.pre-update-${timestamp}`;
322
- copyFileSync(dbPath, backupPath);
1017
+ copyFileSync2(dbPath, backupPath);
323
1018
  const walPath = dbPath + "-wal";
324
1019
  const shmPath = dbPath + "-shm";
325
- if (existsSync(walPath)) copyFileSync(walPath, backupPath + "-wal");
326
- if (existsSync(shmPath)) copyFileSync(shmPath, backupPath + "-shm");
1020
+ if (existsSync6(walPath)) copyFileSync2(walPath, backupPath + "-wal");
1021
+ if (existsSync6(shmPath)) copyFileSync2(shmPath, backupPath + "-shm");
327
1022
  const origSize = statSync(dbPath).size;
328
1023
  const backupSize = statSync(backupPath).size;
329
1024
  if (backupSize !== origSize) {
@@ -334,7 +1029,7 @@ function backupCrystalDb() {
334
1029
  async function verifyCrystalDbReadable() {
335
1030
  const paths = ldmPaths();
336
1031
  const dbPath = paths.crystalDb;
337
- if (!existsSync(dbPath)) return;
1032
+ if (!existsSync6(dbPath)) return;
338
1033
  const { default: Database } = await import("better-sqlite3");
339
1034
  const db = new Database(dbPath, { readonly: true });
340
1035
  try {
@@ -371,7 +1066,7 @@ function formatUpdateSummary(oldVersion, newVersion) {
371
1066
  }
372
1067
  function ldmCliAvailable() {
373
1068
  try {
374
- execSync("ldm --version", { stdio: "pipe", timeout: 5e3 });
1069
+ execSync3("ldm --version", { stdio: "pipe", timeout: 5e3 });
375
1070
  return true;
376
1071
  } catch {
377
1072
  return false;
@@ -380,8 +1075,8 @@ function ldmCliAvailable() {
380
1075
  function bootstrapLdmOs(steps) {
381
1076
  try {
382
1077
  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 });
1078
+ execSync3("npm install -g @wipcomputer/wip-ldm-os", { stdio: "pipe", timeout: 12e4 });
1079
+ execSync3("ldm --version", { stdio: "pipe", timeout: 5e3 });
385
1080
  steps.push("LDM OS installed.");
386
1081
  return true;
387
1082
  } catch {
@@ -392,7 +1087,7 @@ function bootstrapLdmOs(steps) {
392
1087
  function runLdmInstall(repoDir) {
393
1088
  const steps = [];
394
1089
  try {
395
- execSync("ldm init --yes --none", { stdio: "pipe", timeout: 3e4 });
1090
+ execSync3("ldm init --yes --none", { stdio: "pipe", timeout: 3e4 });
396
1091
  steps.push("LDM initialized via ldm CLI");
397
1092
  } catch (err) {
398
1093
  const msg = (err.stderr || err.message || "").toString().trim();
@@ -401,7 +1096,7 @@ function runLdmInstall(repoDir) {
401
1096
  }
402
1097
  }
403
1098
  try {
404
- execSync(`ldm install "${repoDir}"`, { stdio: "pipe", timeout: 6e4 });
1099
+ execSync3(`ldm install "${repoDir}"`, { stdio: "pipe", timeout: 6e4 });
405
1100
  steps.push("Generic deployment handled by ldm install (extensions, MCP, hooks)");
406
1101
  return { ok: true, steps };
407
1102
  } catch (err) {
@@ -432,7 +1127,7 @@ async function runInstallOrUpdate(options) {
432
1127
  if (npmV && semverCompare(npmV, state.installedVersion) > 0) {
433
1128
  steps.push(`Upgrading v${state.installedVersion} -> v${npmV} via npm...`);
434
1129
  try {
435
- execSync("npm install -g @wipcomputer/memory-crystal 2>&1", { encoding: "utf-8", timeout: 6e4, stdio: "pipe" });
1130
+ execSync3("npm install -g @wipcomputer/memory-crystal 2>&1", { encoding: "utf-8", timeout: 6e4, stdio: "pipe" });
436
1131
  steps.push(`Installed @wipcomputer/memory-crystal@${npmV}`);
437
1132
  steps.push("Continuing with updated code...");
438
1133
  } catch (err) {
@@ -452,10 +1147,10 @@ async function runInstallOrUpdate(options) {
452
1147
  steps.push(...delegateResult.steps);
453
1148
  if (delegateResult.ok) {
454
1149
  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);
1150
+ const ldmExtDir = join3(LDM_ROOT2, "extensions", "memory-crystal");
1151
+ if (existsSync6(ldmExtDir)) deployedTo.push(ldmExtDir);
1152
+ const ocExtDir = join3(OC_ROOT, "extensions", "memory-crystal");
1153
+ if (existsSync6(ocExtDir)) deployedTo.push(ocExtDir);
459
1154
  }
460
1155
  }
461
1156
  if (ldmDelegated) {
@@ -520,13 +1215,13 @@ async function runInstallOrUpdate(options) {
520
1215
  }
521
1216
  } else if (options.importDb) {
522
1217
  const importPath = options.importDb;
523
- if (!existsSync(importPath)) {
1218
+ if (!existsSync6(importPath)) {
524
1219
  steps.push(`Import path not found: ${importPath}`);
525
1220
  } else {
526
1221
  try {
527
1222
  const paths = ldmPaths();
528
- mkdirSync(join(paths.root, "memory"), { recursive: true });
529
- copyFileSync(importPath, paths.crystalDb);
1223
+ mkdirSync5(join3(paths.root, "memory"), { recursive: true });
1224
+ copyFileSync2(importPath, paths.crystalDb);
530
1225
  const { default: Database } = await import("better-sqlite3");
531
1226
  const db = new Database(paths.crystalDb, { readonly: true });
532
1227
  const row = db.prepare("SELECT COUNT(*) as count FROM chunks").get();
@@ -544,11 +1239,11 @@ async function runInstallOrUpdate(options) {
544
1239
  }
545
1240
  if (ldmDelegated) {
546
1241
  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"}`);
1242
+ const ldmExtDir = join3(LDM_ROOT2, "extensions", "memory-crystal");
1243
+ if (existsSync6(ldmExtDir)) copyFileSync2(join3(repoRoot, "package.json"), join3(ldmExtDir, "package.json"));
1244
+ const ocExtDir = join3(OC_ROOT, "extensions", "memory-crystal");
1245
+ if (existsSync6(ocExtDir)) copyFileSync2(join3(repoRoot, "package.json"), join3(ocExtDir, "package.json"));
1246
+ steps.push(`Version synced to v${readVersion(join3(repoRoot, "package.json")) || "unknown"}`);
552
1247
  }
553
1248
  if (!ldmDelegated) {
554
1249
  const ldmResult = deployToLdm();
@@ -600,11 +1295,11 @@ async function runInstallOrUpdate(options) {
600
1295
  steps.push(`Backup script failed: ${err.message}`);
601
1296
  }
602
1297
  try {
603
- const { canRunMlx, isMlxLmInstalled, isServerRunning } = await import("./mlx-setup-XKU67WCT.js");
604
- if (canRunMlx()) {
605
- if (isServerRunning()) {
1298
+ const { canRunMlx: canRunMlx2, isMlxLmInstalled: isMlxLmInstalled2, isServerRunning: isServerRunning2 } = await Promise.resolve().then(() => (init_mlx_setup(), mlx_setup_exports));
1299
+ if (canRunMlx2()) {
1300
+ if (isServerRunning2()) {
606
1301
  steps.push("MLX LLM: already running");
607
- } else if (isMlxLmInstalled()) {
1302
+ } else if (isMlxLmInstalled2()) {
608
1303
  steps.push("MLX LLM: installed but not running. Start with: launchctl kickstart -kp gui/$(id -u)/ai.ldm.mlx-server");
609
1304
  } else {
610
1305
  steps.push('MLX LLM: Apple Silicon detected. Run "crystal mlx setup" to install local LLM for free, fast, offline search quality.');
@@ -653,16 +1348,16 @@ async function runInstallOrUpdate(options) {
653
1348
  }
654
1349
  if (options.role === "core") {
655
1350
  try {
656
- const { promoteToCore } = await import("./role.js");
657
- promoteToCore();
1351
+ const { promoteToCore: promoteToCore2 } = await Promise.resolve().then(() => (init_role(), role_exports));
1352
+ promoteToCore2();
658
1353
  steps.push("Role set to Core");
659
1354
  } catch (err) {
660
1355
  steps.push(`Role setup failed: ${err.message}`);
661
1356
  }
662
1357
  } else if (options.role === "node") {
663
1358
  try {
664
- const { demoteToNode } = await import("./role.js");
665
- demoteToNode();
1359
+ const { demoteToNode: demoteToNode2 } = await Promise.resolve().then(() => (init_role(), role_exports));
1360
+ demoteToNode2();
666
1361
  steps.push("Role set to Node");
667
1362
  } catch (err) {
668
1363
  steps.push(`Role setup failed: ${err.message}`);
@@ -670,24 +1365,24 @@ async function runInstallOrUpdate(options) {
670
1365
  }
671
1366
  if (options.pairCode) {
672
1367
  try {
673
- const { pairReceive } = await import("./pair.js");
674
- pairReceive(options.pairCode);
1368
+ const { pairReceive: pairReceive2 } = await Promise.resolve().then(() => (init_pair(), pair_exports));
1369
+ pairReceive2(options.pairCode);
675
1370
  steps.push("Pairing code accepted");
676
1371
  } catch (err) {
677
1372
  steps.push(`Pairing failed: ${err.message}`);
678
1373
  }
679
1374
  }
680
1375
  if (options.role === "node" || process.env.CRYSTAL_RELAY_URL) {
681
- const secretsDir = join(LDM_ROOT, "secrets");
682
- const envPath = join(secretsDir, "crystal-relay.env");
683
- if (!existsSync(envPath)) {
1376
+ const secretsDir = join3(LDM_ROOT2, "secrets");
1377
+ const envPath = join3(secretsDir, "crystal-relay.env");
1378
+ if (!existsSync6(envPath)) {
684
1379
  const relayUrl = "https://memory-crystal-relay.wipcomputer.workers.dev";
685
1380
  let token = "";
686
1381
  try {
687
- const saTokenPath = join(OC_ROOT, "secrets", "op-sa-token");
688
- if (existsSync(saTokenPath)) {
689
- const saToken = readFileSync(saTokenPath, "utf8").trim();
690
- token = execSync(
1382
+ const saTokenPath = join3(OC_ROOT, "secrets", "op-sa-token");
1383
+ if (existsSync6(saTokenPath)) {
1384
+ const saToken = readFileSync5(saTokenPath, "utf8").trim();
1385
+ token = execSync3(
691
1386
  `OP_SERVICE_ACCOUNT_TOKEN=${saToken} op item get "Memory Crystal Relay Auth Tokens" --vault "Agent Secrets" --fields label=${agentId}-token --reveal 2>/dev/null`,
692
1387
  { encoding: "utf8", timeout: 15e3 }
693
1388
  ).trim();
@@ -704,19 +1399,19 @@ async function runInstallOrUpdate(options) {
704
1399
  token = token.trim();
705
1400
  }
706
1401
  if (token) {
707
- mkdirSync(secretsDir, { recursive: true });
708
- writeFileSync(envPath, `export CRYSTAL_RELAY_URL=${relayUrl}
1402
+ mkdirSync5(secretsDir, { recursive: true });
1403
+ writeFileSync5(envPath, `export CRYSTAL_RELAY_URL=${relayUrl}
709
1404
  export CRYSTAL_RELAY_TOKEN=${token}
710
1405
  export CRYSTAL_AGENT_ID=${agentId}
711
1406
  `);
712
1407
  process.env.CRYSTAL_RELAY_URL = relayUrl;
713
1408
  process.env.CRYSTAL_RELAY_TOKEN = token;
714
1409
  steps.push("Relay config written to ~/.ldm/secrets/crystal-relay.env");
715
- const shellProfile = join(HOME, ".zshrc");
1410
+ const shellProfile = join3(HOME3, ".zshrc");
716
1411
  const sourceLine = `source ${envPath}`;
717
1412
  let alreadySourced = false;
718
1413
  try {
719
- alreadySourced = readFileSync(shellProfile, "utf8").includes(sourceLine);
1414
+ alreadySourced = readFileSync5(shellProfile, "utf8").includes(sourceLine);
720
1415
  } catch {
721
1416
  }
722
1417
  if (!alreadySourced && process.stdin.isTTY) {