@wipcomputer/memory-crystal 0.7.32 → 0.7.34-alpha.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 (155) hide show
  1. package/SKILL.md +1 -1
  2. package/cloud/wrangler.toml +30 -0
  3. package/dist/bridge.d.ts +7 -0
  4. package/dist/bridge.js +14 -0
  5. package/dist/bulk-copy.d.ts +17 -0
  6. package/dist/bulk-copy.js +90 -0
  7. package/dist/cc-hook.d.ts +8 -0
  8. package/dist/cc-hook.js +368 -0
  9. package/dist/cc-poller.d.ts +1 -0
  10. package/dist/cc-poller.js +550 -0
  11. package/dist/chunk-25LXQJ4Z.js +110 -0
  12. package/dist/chunk-2DRXIRQW.js +97 -0
  13. package/dist/chunk-2GBYLMEF.js +1385 -0
  14. package/dist/chunk-2ZNH5F6E.js +1281 -0
  15. package/dist/chunk-3G3SFYYI.js +288 -0
  16. package/dist/chunk-3RG5ZIWI.js +10 -0
  17. package/dist/chunk-3S6TI23B.js +97 -0
  18. package/dist/chunk-3VFIJYS4.js +818 -0
  19. package/dist/chunk-437F27T6.js +97 -0
  20. package/dist/chunk-52QE3YI3.js +1169 -0
  21. package/dist/chunk-57RP3DIN.js +1205 -0
  22. package/dist/chunk-5HSZ4W2P.js +62 -0
  23. package/dist/chunk-5I7GMRDN.js +146 -0
  24. package/dist/chunk-645IPXW3.js +290 -0
  25. package/dist/chunk-7A7ELD4C.js +1205 -0
  26. package/dist/chunk-7FYY4GZM.js +1205 -0
  27. package/dist/chunk-7IUE7ODU.js +254 -0
  28. package/dist/chunk-7RMLKZIS.js +108 -0
  29. package/dist/chunk-AA3OPP4Z.js +432 -0
  30. package/dist/chunk-AEWLSYPH.js +72 -0
  31. package/dist/chunk-ASSZDR6I.js +108 -0
  32. package/dist/chunk-AYRJVWUC.js +1205 -0
  33. package/dist/chunk-CCYI5O3D.js +148 -0
  34. package/dist/chunk-CGIDSAJB.js +288 -0
  35. package/dist/chunk-D3I3ZSE2.js +411 -0
  36. package/dist/chunk-D3MACYZ4.js +108 -0
  37. package/dist/chunk-DACSKLY6.js +219 -0
  38. package/dist/chunk-DFQ72B7M.js +248 -0
  39. package/dist/chunk-DW5B4BL7.js +108 -0
  40. package/dist/chunk-EKSACBTJ.js +1070 -0
  41. package/dist/chunk-EXEZZADG.js +248 -0
  42. package/dist/chunk-F3Y7EL7K.js +83 -0
  43. package/dist/chunk-FBQWSDPC.js +1328 -0
  44. package/dist/chunk-FHRZNOMW.js +1205 -0
  45. package/dist/chunk-IM7N24MT.js +129 -0
  46. package/dist/chunk-IPNYIXFK.js +1178 -0
  47. package/dist/chunk-J7MRSZIO.js +167 -0
  48. package/dist/chunk-JITKI2OI.js +106 -0
  49. package/dist/chunk-JWZXYVET.js +1068 -0
  50. package/dist/chunk-KCQUXVYT.js +108 -0
  51. package/dist/chunk-KOQ43OX6.js +1281 -0
  52. package/dist/chunk-KYVWO6ZM.js +1069 -0
  53. package/dist/chunk-L3VHARQH.js +413 -0
  54. package/dist/chunk-LBWDS6BE.js +288 -0
  55. package/dist/chunk-LOVAHSQV.js +411 -0
  56. package/dist/chunk-LQOYCAGG.js +446 -0
  57. package/dist/chunk-LWAIPJ2W.js +146 -0
  58. package/dist/chunk-M5DHKW7M.js +127 -0
  59. package/dist/chunk-MBKCIJHM.js +1328 -0
  60. package/dist/chunk-MK42FMEG.js +147 -0
  61. package/dist/chunk-MOBMYHKL.js +1205 -0
  62. package/dist/chunk-MPLTNMRG.js +67 -0
  63. package/dist/chunk-NIJCVN3O.js +147 -0
  64. package/dist/chunk-NX647OM3.js +310 -0
  65. package/dist/chunk-NZCFSZQ7.js +1205 -0
  66. package/dist/chunk-O2UITJGH.js +465 -0
  67. package/dist/chunk-OCRA44AZ.js +108 -0
  68. package/dist/chunk-P3KJR66H.js +117 -0
  69. package/dist/chunk-PEK6JH65.js +432 -0
  70. package/dist/chunk-PJ6FFKEX.js +77 -0
  71. package/dist/chunk-PLUBBZYR.js +800 -0
  72. package/dist/chunk-PNKVD2UK.js +26 -0
  73. package/dist/chunk-PSQZURHO.js +229 -0
  74. package/dist/chunk-SGL6ISBJ.js +1061 -0
  75. package/dist/chunk-SJABZZT5.js +97 -0
  76. package/dist/chunk-TD3P3K32.js +1199 -0
  77. package/dist/chunk-TMDZJJKV.js +288 -0
  78. package/dist/chunk-UNHVZB5G.js +411 -0
  79. package/dist/chunk-VAFTWSTE.js +1061 -0
  80. package/dist/chunk-VNFXFQBB.js +217 -0
  81. package/dist/chunk-X3GVFKSJ.js +1205 -0
  82. package/dist/chunk-XZ3S56RQ.js +1061 -0
  83. package/dist/chunk-Y72C7F6O.js +148 -0
  84. package/dist/chunk-YLICP577.js +1205 -0
  85. package/dist/chunk-YX6AXLVK.js +159 -0
  86. package/dist/chunk-ZCQYHTNU.js +146 -0
  87. package/dist/cli.d.ts +1 -0
  88. package/dist/cli.js +1160 -0
  89. package/dist/cloud-crystal.js +6 -0
  90. package/dist/core.d.ts +252 -0
  91. package/dist/core.js +12 -0
  92. package/dist/crypto.d.ts +20 -0
  93. package/dist/crypto.js +27 -0
  94. package/dist/crystal-capture.sh +29 -0
  95. package/dist/crystal-serve.d.ts +4 -0
  96. package/dist/crystal-serve.js +252 -0
  97. package/dist/dev-update-SZ2Z4WCQ.js +6 -0
  98. package/dist/discover.d.ts +30 -0
  99. package/dist/discover.js +177 -0
  100. package/dist/doctor.d.ts +9 -0
  101. package/dist/doctor.js +342 -0
  102. package/dist/dream-weaver.d.ts +8 -0
  103. package/dist/dream-weaver.js +56 -0
  104. package/dist/file-sync.d.ts +48 -0
  105. package/dist/file-sync.js +18 -0
  106. package/dist/installer.d.ts +61 -0
  107. package/dist/installer.js +772 -0
  108. package/dist/ldm-backup.sh +116 -0
  109. package/dist/ldm.d.ts +50 -0
  110. package/dist/ldm.js +32 -0
  111. package/dist/llm-XXLYPIOF.js +16 -0
  112. package/dist/mcp-server.d.ts +1 -0
  113. package/dist/mcp-server.js +277 -0
  114. package/dist/migrate.d.ts +1 -0
  115. package/dist/migrate.js +89 -0
  116. package/dist/mirror-sync.d.ts +1 -0
  117. package/dist/mirror-sync.js +159 -0
  118. package/dist/mlx-setup-XKU67WCT.js +289 -0
  119. package/dist/oc-backfill.d.ts +19 -0
  120. package/dist/oc-backfill.js +74 -0
  121. package/dist/openclaw.d.ts +5 -0
  122. package/dist/openclaw.js +434 -0
  123. package/dist/pair.d.ts +4 -0
  124. package/dist/pair.js +75 -0
  125. package/dist/poller.d.ts +1 -0
  126. package/dist/poller.js +634 -0
  127. package/dist/role.d.ts +24 -0
  128. package/dist/role.js +13 -0
  129. package/dist/search-pipeline-4K4OJSSS.js +255 -0
  130. package/dist/search-pipeline-4PRS6LI7.js +280 -0
  131. package/dist/search-pipeline-7UJMXPLO.js +280 -0
  132. package/dist/search-pipeline-CBV25NX7.js +99 -0
  133. package/dist/search-pipeline-DQTRLGBH.js +74 -0
  134. package/dist/search-pipeline-HNG37REH.js +282 -0
  135. package/dist/search-pipeline-IZFPLBUB.js +280 -0
  136. package/dist/search-pipeline-MID6F26Q.js +73 -0
  137. package/dist/search-pipeline-N52JZFNN.js +282 -0
  138. package/dist/search-pipeline-OPB2PRQQ.js +280 -0
  139. package/dist/search-pipeline-VXTE5HAD.js +262 -0
  140. package/dist/search-pipeline-XHFKADRG.js +73 -0
  141. package/dist/staging.d.ts +29 -0
  142. package/dist/staging.js +21 -0
  143. package/dist/summarize.d.ts +19 -0
  144. package/dist/summarize.js +10 -0
  145. package/dist/worker-demo.js +186 -0
  146. package/dist/worker-mcp.js +404 -0
  147. package/dist/worker.js +137 -0
  148. package/package.json +15 -1
  149. package/.env.example +0 -20
  150. package/.publish-skill.json +0 -1
  151. package/CHANGELOG.md +0 -1372
  152. package/README-ENTERPRISE.md +0 -226
  153. package/RELAY.md +0 -199
  154. package/wrangler-demo.toml +0 -8
  155. package/wrangler-mcp.toml +0 -24
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ pullFileSync
4
+ } from "./chunk-CGIDSAJB.js";
5
+ import {
6
+ Crystal,
7
+ resolveConfig
8
+ } from "./chunk-2GBYLMEF.js";
9
+ import {
10
+ decryptJSON,
11
+ loadRelayKey
12
+ } from "./chunk-D3MACYZ4.js";
13
+ import {
14
+ ldmPaths,
15
+ resolveStatePath,
16
+ stateWritePath
17
+ } from "./chunk-DFQ72B7M.js";
18
+
19
+ // src/mirror-sync.ts
20
+ import { readFileSync, writeFileSync, existsSync } from "fs";
21
+ var RELAY_URL = process.env.CRYSTAL_RELAY_URL || "";
22
+ var RELAY_TOKEN = process.env.CRYSTAL_RELAY_TOKEN || "";
23
+ var _ldmPaths = ldmPaths();
24
+ var MIRROR_STATE_PATH = resolveStatePath("mirror-sync-state.json");
25
+ function loadState() {
26
+ try {
27
+ if (existsSync(MIRROR_STATE_PATH)) {
28
+ const state = JSON.parse(readFileSync(MIRROR_STATE_PATH, "utf-8"));
29
+ return {
30
+ lastSync: state.lastSync || null,
31
+ lastHash: state.lastHash || null,
32
+ lastSize: state.lastSize || null,
33
+ lastDeltaChunkId: state.lastDeltaChunkId || 0,
34
+ totalImported: state.totalImported || 0
35
+ };
36
+ }
37
+ } catch {
38
+ }
39
+ return { lastSync: null, lastHash: null, lastSize: null, lastDeltaChunkId: 0, totalImported: 0 };
40
+ }
41
+ function saveState(state) {
42
+ const writePath = stateWritePath("mirror-sync-state.json");
43
+ writeFileSync(writePath, JSON.stringify(state, null, 2));
44
+ }
45
+ async function pullDelta(force2) {
46
+ if (!RELAY_URL || !RELAY_TOKEN) {
47
+ throw new Error("CRYSTAL_RELAY_URL and CRYSTAL_RELAY_TOKEN must be set");
48
+ }
49
+ const relayKey = loadRelayKey();
50
+ const listResp = await fetch(`${RELAY_URL}/pickup/mirror`, {
51
+ headers: { "Authorization": `Bearer ${RELAY_TOKEN}` }
52
+ });
53
+ if (!listResp.ok) {
54
+ throw new Error(`Relay list failed: ${listResp.status} ${await listResp.text()}`);
55
+ }
56
+ const listData = await listResp.json();
57
+ if (listData.count === 0) {
58
+ process.stderr.write("[mirror-sync] no delta available\n");
59
+ return false;
60
+ }
61
+ const config = resolveConfig();
62
+ const crystal = new Crystal(config);
63
+ await crystal.init();
64
+ const state = loadState();
65
+ let totalImported = 0;
66
+ for (const blob of listData.blobs) {
67
+ try {
68
+ const blobResp = await fetch(`${RELAY_URL}/pickup/mirror/${blob.id}`, {
69
+ headers: { "Authorization": `Bearer ${RELAY_TOKEN}` }
70
+ });
71
+ if (!blobResp.ok) {
72
+ process.stderr.write(`[mirror-sync] failed to fetch blob ${blob.id}: ${blobResp.status}
73
+ `);
74
+ continue;
75
+ }
76
+ const encryptedText = await blobResp.text();
77
+ const encrypted = JSON.parse(encryptedText);
78
+ let delta;
79
+ try {
80
+ delta = decryptJSON(encrypted, relayKey);
81
+ } catch (err) {
82
+ process.stderr.write(`[mirror-sync] blob ${blob.id} failed verification: ${err.message} \u2014 DISCARDED
83
+ `);
84
+ await fetch(`${RELAY_URL}/confirm/mirror/${blob.id}`, {
85
+ method: "DELETE",
86
+ headers: { "Authorization": `Bearer ${RELAY_TOKEN}` }
87
+ });
88
+ continue;
89
+ }
90
+ if (!force2 && delta.maxId <= state.lastDeltaChunkId) {
91
+ process.stderr.write(`[mirror-sync] blob ${blob.id} already applied (maxId ${delta.maxId} <= watermark ${state.lastDeltaChunkId})
92
+ `);
93
+ await fetch(`${RELAY_URL}/confirm/mirror/${blob.id}`, {
94
+ method: "DELETE",
95
+ headers: { "Authorization": `Bearer ${RELAY_TOKEN}` }
96
+ });
97
+ continue;
98
+ }
99
+ const imported = crystal.importChunks(delta.chunks);
100
+ totalImported += imported;
101
+ if (delta.maxId > state.lastDeltaChunkId) {
102
+ state.lastDeltaChunkId = delta.maxId;
103
+ }
104
+ process.stderr.write(
105
+ `[mirror-sync] blob ${blob.id}: ${imported}/${delta.chunkCount} chunks imported (ID ${delta.sinceId + 1}..${delta.maxId}), pushed=${delta.pushedAt}
106
+ `
107
+ );
108
+ await fetch(`${RELAY_URL}/confirm/mirror/${blob.id}`, {
109
+ method: "DELETE",
110
+ headers: { "Authorization": `Bearer ${RELAY_TOKEN}` }
111
+ });
112
+ } catch (err) {
113
+ process.stderr.write(`[mirror-sync] error processing blob ${blob.id}: ${err.message}
114
+ `);
115
+ }
116
+ }
117
+ state.lastSync = (/* @__PURE__ */ new Date()).toISOString();
118
+ state.totalImported += totalImported;
119
+ saveState(state);
120
+ if (totalImported > 0) {
121
+ process.stderr.write(`[mirror-sync] done: ${totalImported} chunks imported, watermark=${state.lastDeltaChunkId}
122
+ `);
123
+ }
124
+ return totalImported > 0;
125
+ }
126
+ var args = process.argv.slice(2);
127
+ if (args.includes("--status")) {
128
+ const state = loadState();
129
+ const paths = ldmPaths();
130
+ const hasDb = existsSync(paths.crystalDb);
131
+ console.log("Mirror sync status:");
132
+ console.log(` Relay URL: ${RELAY_URL || "(not set)"}`);
133
+ console.log(` Local crystal: ${hasDb ? paths.crystalDb : "(none)"}`);
134
+ console.log(` Last sync: ${state.lastSync || "never"}`);
135
+ console.log(` Delta watermark: chunk ID ${state.lastDeltaChunkId}`);
136
+ console.log(` Total imported: ${state.totalImported}`);
137
+ process.exit(0);
138
+ }
139
+ var force = args.includes("--force");
140
+ pullDelta(force).then(async (updated) => {
141
+ try {
142
+ const { imported, deleted } = await pullFileSync();
143
+ if (imported > 0 || deleted > 0) {
144
+ process.stderr.write(`[mirror-sync] file sync: ${imported} imported, ${deleted} deleted
145
+ `);
146
+ }
147
+ } catch (err) {
148
+ process.stderr.write(`[mirror-sync] file sync failed (non-fatal): ${err.message}
149
+ `);
150
+ }
151
+ if (updated) {
152
+ process.stderr.write("[mirror-sync] done\n");
153
+ }
154
+ process.exit(0);
155
+ }).catch((err) => {
156
+ process.stderr.write(`[mirror-sync] error: ${err.message}
157
+ `);
158
+ process.exit(1);
159
+ });
@@ -0,0 +1,289 @@
1
+ // src/mlx-setup.ts
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
3
+ import { execSync } from "child_process";
4
+ import { join } from "path";
5
+ import { homedir } from "os";
6
+ var HOME = homedir();
7
+ var MLX_PORT = 18791;
8
+ var MLX_MODEL = "mlx-community/Qwen2.5-3B-Instruct-4bit";
9
+ var MLX_STATE_FILE = join(HOME, ".ldm", "state", "mlx-server.json");
10
+ var MLX_PLIST_LABEL = "ai.ldm.mlx-server";
11
+ var MLX_PLIST_PATH = join(HOME, "Library", "LaunchAgents", `${MLX_PLIST_LABEL}.plist`);
12
+ var MLX_LOG_PATH = "/tmp/mlx-server.log";
13
+ function detectPlatform() {
14
+ const platform = process.platform;
15
+ const arch = process.arch;
16
+ if (platform === "darwin") {
17
+ return arch === "arm64" ? "apple-silicon" : "intel-mac";
18
+ }
19
+ if (platform === "linux") return "linux";
20
+ return "other";
21
+ }
22
+ function canRunMlx() {
23
+ return detectPlatform() === "apple-silicon";
24
+ }
25
+ function findPython() {
26
+ const candidates = ["python3", "/opt/homebrew/bin/python3", "/usr/local/bin/python3"];
27
+ for (const cmd of candidates) {
28
+ try {
29
+ const version = execSync(`${cmd} --version 2>&1`, { encoding: "utf-8", timeout: 5e3 }).trim();
30
+ const match = version.match(/Python (\d+)\.(\d+)/);
31
+ if (match && parseInt(match[1]) >= 3 && parseInt(match[2]) >= 10) {
32
+ const realPath = execSync(`which ${cmd} 2>/dev/null`, { encoding: "utf-8", timeout: 3e3 }).trim();
33
+ return realPath || cmd;
34
+ }
35
+ } catch {
36
+ }
37
+ }
38
+ return null;
39
+ }
40
+ function findInstaller() {
41
+ try {
42
+ execSync("uv --version 2>/dev/null", { encoding: "utf-8", timeout: 3e3 });
43
+ return "uv";
44
+ } catch {
45
+ }
46
+ try {
47
+ execSync("pip3 --version 2>/dev/null", { encoding: "utf-8", timeout: 3e3 });
48
+ return "pip3";
49
+ } catch {
50
+ }
51
+ return null;
52
+ }
53
+ function isMlxLmInstalled() {
54
+ try {
55
+ execSync('python3 -c "import mlx_lm" 2>/dev/null', { timeout: 5e3 });
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+ function installMlxLm(steps) {
62
+ const installer = findInstaller();
63
+ if (!installer) {
64
+ steps.push("No pip3 or uv found. Cannot install mlx-lm.");
65
+ return false;
66
+ }
67
+ const cmd = installer === "uv" ? "uv pip install mlx-lm" : "pip3 install mlx-lm";
68
+ steps.push(`Installing mlx-lm via ${installer}...`);
69
+ try {
70
+ execSync(cmd, { encoding: "utf-8", timeout: 12e4, stdio: "pipe" });
71
+ steps.push("mlx-lm installed successfully.");
72
+ return true;
73
+ } catch (err) {
74
+ if (installer === "pip3") {
75
+ try {
76
+ execSync("pip3 install --user mlx-lm", { encoding: "utf-8", timeout: 12e4, stdio: "pipe" });
77
+ steps.push("mlx-lm installed (--user) successfully.");
78
+ return true;
79
+ } catch {
80
+ }
81
+ }
82
+ steps.push(`mlx-lm install failed: ${err.message.slice(0, 200)}`);
83
+ return false;
84
+ }
85
+ }
86
+ function isServerRunning() {
87
+ try {
88
+ const state = loadState();
89
+ const port = state?.port || MLX_PORT;
90
+ execSync(`curl -s -o /dev/null -w "%{http_code}" http://localhost:${port}/v1/models`, {
91
+ encoding: "utf-8",
92
+ timeout: 3e3
93
+ });
94
+ return true;
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+ function createLaunchAgent(pythonPath, steps) {
100
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
101
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
102
+ <plist version="1.0">
103
+ <dict>
104
+ <key>Label</key>
105
+ <string>${MLX_PLIST_LABEL}</string>
106
+ <key>ProgramArguments</key>
107
+ <array>
108
+ <string>${pythonPath}</string>
109
+ <string>-m</string>
110
+ <string>mlx_lm.server</string>
111
+ <string>--model</string>
112
+ <string>${MLX_MODEL}</string>
113
+ <string>--port</string>
114
+ <string>${MLX_PORT}</string>
115
+ </array>
116
+ <key>RunAtLoad</key>
117
+ <true/>
118
+ <key>KeepAlive</key>
119
+ <true/>
120
+ <key>StandardOutPath</key>
121
+ <string>${MLX_LOG_PATH}</string>
122
+ <key>StandardErrorPath</key>
123
+ <string>${MLX_LOG_PATH}</string>
124
+ </dict>
125
+ </plist>`;
126
+ try {
127
+ writeFileSync(MLX_PLIST_PATH, plist);
128
+ execSync(`launchctl load "${MLX_PLIST_PATH}" 2>/dev/null`, { timeout: 5e3 });
129
+ steps.push(`LaunchAgent installed at ${MLX_PLIST_PATH}`);
130
+ steps.push(`MLX server will start on port ${MLX_PORT}`);
131
+ return true;
132
+ } catch (err) {
133
+ steps.push(`LaunchAgent install failed: ${err.message}`);
134
+ return false;
135
+ }
136
+ }
137
+ function startServer(steps) {
138
+ try {
139
+ execSync(`launchctl kickstart -kp gui/$(id -u)/${MLX_PLIST_LABEL} 2>/dev/null`, { timeout: 1e4 });
140
+ steps.push("MLX server started.");
141
+ return true;
142
+ } catch {
143
+ steps.push("MLX server start failed. Check /tmp/mlx-server.log");
144
+ return false;
145
+ }
146
+ }
147
+ function stopServer() {
148
+ try {
149
+ execSync(`launchctl unload "${MLX_PLIST_PATH}" 2>/dev/null`, { timeout: 5e3 });
150
+ return true;
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+ function loadState() {
156
+ try {
157
+ if (existsSync(MLX_STATE_FILE)) {
158
+ return JSON.parse(readFileSync(MLX_STATE_FILE, "utf-8"));
159
+ }
160
+ } catch {
161
+ }
162
+ return null;
163
+ }
164
+ function saveState(state) {
165
+ const dir = join(HOME, ".ldm", "state");
166
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
167
+ writeFileSync(MLX_STATE_FILE, JSON.stringify(state, null, 2) + "\n");
168
+ }
169
+ async function verifyServer(steps) {
170
+ const state = loadState();
171
+ const port = state?.port || MLX_PORT;
172
+ for (let i = 0; i < 15; i++) {
173
+ try {
174
+ const resp = await fetch(`http://localhost:${port}/v1/models`, {
175
+ signal: AbortSignal.timeout(2e3)
176
+ });
177
+ if (resp.ok) {
178
+ const data = await resp.json();
179
+ const model = data?.data?.[0]?.id || "unknown";
180
+ steps.push(`MLX server verified: ${model} on port ${port}`);
181
+ return true;
182
+ }
183
+ } catch {
184
+ }
185
+ await new Promise((r) => setTimeout(r, 2e3));
186
+ }
187
+ steps.push("MLX server did not respond within 30 seconds. Check /tmp/mlx-server.log");
188
+ return false;
189
+ }
190
+ async function setupMlx(options) {
191
+ const steps = [];
192
+ const platform = detectPlatform();
193
+ if (platform !== "apple-silicon") {
194
+ steps.push(`Platform: ${platform}. MLX requires Apple Silicon. Skipping.`);
195
+ return { ok: false, steps };
196
+ }
197
+ steps.push("Platform: Apple Silicon detected.");
198
+ const pythonPath = findPython();
199
+ if (!pythonPath) {
200
+ steps.push("Python 3.10+ not found. Install via: brew install python3");
201
+ return { ok: false, steps };
202
+ }
203
+ steps.push(`Python: ${pythonPath}`);
204
+ if (!isMlxLmInstalled()) {
205
+ if (!options?.yes) {
206
+ steps.push("mlx-lm not installed. Run with --yes to auto-install, or: pip3 install mlx-lm");
207
+ return { ok: false, steps };
208
+ }
209
+ const installed = installMlxLm(steps);
210
+ if (!installed) return { ok: false, steps };
211
+ } else {
212
+ steps.push("mlx-lm: already installed.");
213
+ }
214
+ if (!existsSync(MLX_PLIST_PATH)) {
215
+ const created = createLaunchAgent(pythonPath, steps);
216
+ if (!created) return { ok: false, steps };
217
+ } else {
218
+ steps.push("LaunchAgent: already installed.");
219
+ }
220
+ saveState({
221
+ installed: true,
222
+ port: MLX_PORT,
223
+ model: MLX_MODEL,
224
+ pythonPath,
225
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
226
+ });
227
+ if (!isServerRunning()) {
228
+ startServer(steps);
229
+ steps.push(`Waiting for model to load (~1.5 GB on first run)...`);
230
+ const verified = await verifyServer(steps);
231
+ if (!verified) {
232
+ steps.push("Server started but not yet responding. It may still be downloading the model.");
233
+ steps.push(`Check: tail -f ${MLX_LOG_PATH}`);
234
+ }
235
+ } else {
236
+ steps.push(`MLX server: already running on port ${MLX_PORT}`);
237
+ }
238
+ return { ok: true, steps };
239
+ }
240
+ function doctorCheck() {
241
+ const platform = detectPlatform();
242
+ if (platform !== "apple-silicon") {
243
+ return { status: "skip", detail: `${platform} (MLX requires Apple Silicon)` };
244
+ }
245
+ const state = loadState();
246
+ if (!state || !state.installed) {
247
+ return {
248
+ status: "warn",
249
+ detail: "not installed",
250
+ fix: "crystal init (will offer MLX setup)"
251
+ };
252
+ }
253
+ if (isServerRunning()) {
254
+ return { status: "ok", detail: `running on port ${state.port} (${state.model})` };
255
+ }
256
+ if (existsSync(MLX_PLIST_PATH)) {
257
+ return {
258
+ status: "warn",
259
+ detail: "installed but not running",
260
+ fix: `launchctl kickstart -kp gui/$(id -u)/${MLX_PLIST_LABEL}`
261
+ };
262
+ }
263
+ return {
264
+ status: "warn",
265
+ detail: "installed but LaunchAgent missing",
266
+ fix: "crystal init (will recreate LaunchAgent)"
267
+ };
268
+ }
269
+ var MLX_CONFIG = {
270
+ port: MLX_PORT,
271
+ model: MLX_MODEL,
272
+ plistPath: MLX_PLIST_PATH,
273
+ logPath: MLX_LOG_PATH,
274
+ stateFile: MLX_STATE_FILE
275
+ };
276
+ export {
277
+ MLX_CONFIG,
278
+ canRunMlx,
279
+ createLaunchAgent,
280
+ detectPlatform,
281
+ doctorCheck,
282
+ installMlxLm,
283
+ isMlxLmInstalled,
284
+ isServerRunning,
285
+ setupMlx,
286
+ startServer,
287
+ stopServer,
288
+ verifyServer
289
+ };
@@ -0,0 +1,19 @@
1
+ interface ExtractedMessage {
2
+ role: string;
3
+ text: string;
4
+ timestamp: string;
5
+ sessionId: string;
6
+ }
7
+ /** Extract messages from an OpenClaw session JSONL file.
8
+ * Reads from lastByteOffset for incremental processing.
9
+ * Returns extracted messages and new byte offset. */
10
+ declare function extractOpenClawMessages(filePath: string, lastByteOffset?: number): {
11
+ messages: ExtractedMessage[];
12
+ newByteOffset: number;
13
+ };
14
+ /** Detect whether a JSONL file is OpenClaw format.
15
+ * Checks the first line for type:"session" with version field
16
+ * (OpenClaw uses this header; Claude Code does not). */
17
+ declare function isOpenClawJsonl(filePath: string): boolean;
18
+
19
+ export { type ExtractedMessage, extractOpenClawMessages, isOpenClawJsonl };
@@ -0,0 +1,74 @@
1
+ // src/oc-backfill.ts
2
+ import { existsSync, statSync, openSync, readSync, closeSync } from "fs";
3
+ function extractOpenClawMessages(filePath, lastByteOffset = 0) {
4
+ if (!existsSync(filePath)) {
5
+ return { messages: [], newByteOffset: 0 };
6
+ }
7
+ const fileSize = statSync(filePath).size;
8
+ if (lastByteOffset >= fileSize) {
9
+ return { messages: [], newByteOffset: fileSize };
10
+ }
11
+ const fd = openSync(filePath, "r");
12
+ const bufSize = fileSize - lastByteOffset;
13
+ const buf = Buffer.alloc(bufSize);
14
+ readSync(fd, buf, 0, bufSize, lastByteOffset);
15
+ closeSync(fd);
16
+ const lines = buf.toString("utf-8").split("\n").filter(Boolean);
17
+ const messages = [];
18
+ let sessionId = "unknown";
19
+ for (const line of lines) {
20
+ try {
21
+ const obj = JSON.parse(line);
22
+ if (obj.type === "session" && obj.id) {
23
+ sessionId = obj.id;
24
+ continue;
25
+ }
26
+ if (obj.type !== "message") continue;
27
+ const msg = obj.message;
28
+ if (!msg || !msg.role) continue;
29
+ if (msg.role === "system" || msg.role === "tool") continue;
30
+ let text = "";
31
+ if (typeof msg.content === "string") {
32
+ text = msg.content;
33
+ } else if (Array.isArray(msg.content)) {
34
+ const parts = [];
35
+ for (const block of msg.content) {
36
+ if (block.type === "text" && block.text) {
37
+ parts.push(block.text);
38
+ }
39
+ if (block.type === "thinking" && block.thinking) {
40
+ parts.push(`[thinking] ${block.thinking}`);
41
+ }
42
+ }
43
+ text = parts.join("\n\n");
44
+ }
45
+ if (text.length < 20) continue;
46
+ messages.push({
47
+ role: msg.role,
48
+ text,
49
+ timestamp: obj.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
50
+ sessionId: obj.id || sessionId
51
+ });
52
+ } catch {
53
+ }
54
+ }
55
+ return { messages, newByteOffset: fileSize };
56
+ }
57
+ function isOpenClawJsonl(filePath) {
58
+ try {
59
+ const fd = openSync(filePath, "r");
60
+ const buf = Buffer.alloc(Math.min(1024, statSync(filePath).size));
61
+ const bytesRead = readSync(fd, buf, 0, buf.length, 0);
62
+ closeSync(fd);
63
+ const firstLine = buf.toString("utf-8", 0, bytesRead).split("\n")[0];
64
+ if (!firstLine) return false;
65
+ const obj = JSON.parse(firstLine);
66
+ return obj.type === "session" && typeof obj.version === "number";
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+ export {
72
+ extractOpenClawMessages,
73
+ isOpenClawJsonl
74
+ };
@@ -0,0 +1,5 @@
1
+ declare const _default: {
2
+ register(api: any): void;
3
+ };
4
+
5
+ export { _default as default };