@wipcomputer/memory-crystal 0.7.30 → 0.7.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +1 -1
- package/cloud/wrangler.toml +30 -0
- package/dist/bulk-copy.js +1 -1
- package/dist/cc-hook.js +3 -3
- package/dist/cc-poller.js +2 -2
- package/dist/chunk-2GBYLMEF.js +1385 -0
- package/dist/chunk-437F27T6.js +97 -0
- package/dist/chunk-5I7GMRDN.js +146 -0
- package/dist/chunk-CGIDSAJB.js +288 -0
- package/dist/chunk-D3MACYZ4.js +108 -0
- package/dist/chunk-DFQ72B7M.js +248 -0
- package/dist/chunk-NX647OM3.js +310 -0
- package/dist/cli.js +62 -7
- package/dist/core.d.ts +22 -2
- package/dist/core.js +1 -1
- package/dist/crypto.js +2 -2
- package/dist/crystal-serve.js +3 -3
- package/dist/doctor.js +12 -4
- package/dist/dream-weaver.js +2 -2
- package/dist/file-sync.js +3 -3
- package/dist/installer.js +99 -3
- package/dist/ldm.js +1 -1
- package/dist/llm-XXLYPIOF.js +16 -0
- package/dist/mcp-server.js +17 -5
- package/dist/migrate.js +1 -1
- package/dist/mirror-sync.js +4 -4
- package/dist/mlx-setup-XKU67WCT.js +289 -0
- package/dist/openclaw.js +16 -5
- package/dist/pair.js +2 -2
- package/dist/poller.js +5 -5
- package/dist/role.js +2 -2
- package/dist/search-pipeline-CBV25NX7.js +99 -0
- package/dist/staging.js +2 -2
- package/package.json +15 -1
- package/.env.example +0 -20
- package/.publish-skill.json +0 -1
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/.env.example +0 -20
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/.publish-skill.json +0 -1
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/CHANGELOG.md +0 -1297
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/CLA.md +0 -19
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/LICENSE +0 -52
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/README-ENTERPRISE.md +0 -226
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/README.md +0 -151
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/RELAY.md +0 -199
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/SKILL.md +0 -462
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/TECHNICAL.md +0 -656
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-23.md +0 -48
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-25.md +0 -24
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-26.md +0 -7
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-28.md +0 -31
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-29.md +0 -28
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-4.md +0 -64
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-5.md +0 -19
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/cloud/README.md +0 -116
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/cloud/docs/gpt-system-instructions.md +0 -69
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/cloud/migrations/0001_init.sql +0 -52
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/migrations/0001_init.sql +0 -51
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/migrations/0002_cloud_storage.sql +0 -49
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/openclaw.plugin.json +0 -11
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/package-lock.json +0 -4169
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/package.json +0 -61
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/crystal-capture.sh +0 -29
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/deploy-cloud.sh +0 -153
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/ldm-backup.sh +0 -116
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/migrate-lance-to-sqlite.mjs +0 -218
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/skills/memory/SKILL.md +0 -438
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/wrangler-demo.toml +0 -8
- package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/wrangler-mcp.toml +0 -24
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/.env.example +0 -20
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/.publish-skill.json +0 -1
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/CHANGELOG.md +0 -1297
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/CLA.md +0 -19
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/LICENSE +0 -52
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/README-ENTERPRISE.md +0 -226
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/README.md +0 -151
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/RELAY.md +0 -199
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/RELEASE-NOTES-v0.7.30.md +0 -29
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/SKILL.md +0 -462
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/TECHNICAL.md +0 -656
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-23.md +0 -48
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-25.md +0 -24
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-26.md +0 -7
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-28.md +0 -31
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-29.md +0 -28
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-4.md +0 -64
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-5.md +0 -19
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/cloud/README.md +0 -116
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/cloud/docs/gpt-system-instructions.md +0 -69
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/cloud/migrations/0001_init.sql +0 -52
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/migrations/0001_init.sql +0 -51
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/migrations/0002_cloud_storage.sql +0 -49
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/openclaw.plugin.json +0 -11
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/package-lock.json +0 -4169
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/package.json +0 -61
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/crystal-capture.sh +0 -29
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/deploy-cloud.sh +0 -153
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/ldm-backup.sh +0 -116
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/migrate-lance-to-sqlite.mjs +0 -218
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/skills/memory/SKILL.md +0 -438
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/wrangler-demo.toml +0 -8
- package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/wrangler-mcp.toml +0 -24
- package/CHANGELOG.md +0 -1329
- package/README-ENTERPRISE.md +0 -226
- package/RELAY.md +0 -199
- package/_trash/RELEASE-NOTES-v0-7-23.md +0 -48
- package/_trash/RELEASE-NOTES-v0-7-25.md +0 -24
- package/_trash/RELEASE-NOTES-v0-7-26.md +0 -7
- package/_trash/RELEASE-NOTES-v0-7-28.md +0 -31
- package/_trash/RELEASE-NOTES-v0-7-29.md +0 -28
- package/_trash/RELEASE-NOTES-v0-7-4.md +0 -64
- package/_trash/RELEASE-NOTES-v0-7-5.md +0 -19
- package/_trash/RELEASE-NOTES-v0.7.30.md +0 -29
- package/wrangler-demo.toml +0 -8
- package/wrangler-mcp.toml +0 -24
|
@@ -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
|
+
};
|
package/dist/openclaw.js
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Crystal,
|
|
3
3
|
resolveConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2GBYLMEF.js";
|
|
5
5
|
import {
|
|
6
6
|
ensureLdm,
|
|
7
7
|
resolveStatePath,
|
|
8
8
|
stateWritePath
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DFQ72B7M.js";
|
|
10
10
|
|
|
11
11
|
// src/dev-update.ts
|
|
12
12
|
import { execSync } from "child_process";
|
|
13
13
|
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
14
14
|
import { join, basename } from "path";
|
|
15
15
|
var HOME = process.env.HOME || "";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
function resolveWorkspace() {
|
|
17
|
+
const configPath = join(HOME, ".ldm", "config.json");
|
|
18
|
+
if (existsSync(configPath)) {
|
|
19
|
+
try {
|
|
20
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
21
|
+
if (config.workspace) return config.workspace;
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return join(HOME, "wipcomputerinc");
|
|
26
|
+
}
|
|
27
|
+
var TEAM_DIR = join(resolveWorkspace(), "team");
|
|
28
|
+
var CC_REPOS = join(TEAM_DIR, "cc-mini", "repos");
|
|
29
|
+
var LESA_REPOS = join(TEAM_DIR, "L\u0113sa", "repos");
|
|
19
30
|
var DEV_UPDATES_DIR = join(CC_REPOS, "wip-dev-updates");
|
|
20
31
|
var LAST_RUN_PATH = resolveStatePath("dev-update-last-run.json");
|
|
21
32
|
function loadLastRun() {
|
package/dist/pair.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
encodePairingString,
|
|
5
5
|
generateRelayKey,
|
|
6
6
|
loadRelayKey
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-D3MACYZ4.js";
|
|
8
|
+
import "./chunk-DFQ72B7M.js";
|
|
9
9
|
|
|
10
10
|
// src/pair.ts
|
|
11
11
|
import { existsSync, writeFileSync, mkdirSync, chmodSync } from "fs";
|
package/dist/poller.js
CHANGED
|
@@ -3,28 +3,28 @@ import {
|
|
|
3
3
|
ensureStaging,
|
|
4
4
|
isNewAgent,
|
|
5
5
|
markReady
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5I7GMRDN.js";
|
|
7
7
|
import {
|
|
8
8
|
generateSessionSummary,
|
|
9
9
|
writeSummaryFile
|
|
10
10
|
} from "./chunk-Y72C7F6O.js";
|
|
11
11
|
import {
|
|
12
12
|
pushFileSync
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CGIDSAJB.js";
|
|
14
14
|
import {
|
|
15
15
|
Crystal,
|
|
16
16
|
resolveConfig
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-2GBYLMEF.js";
|
|
18
18
|
import {
|
|
19
19
|
decryptJSON,
|
|
20
20
|
encryptJSON,
|
|
21
21
|
loadRelayKey
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-D3MACYZ4.js";
|
|
23
23
|
import {
|
|
24
24
|
ensureLdm,
|
|
25
25
|
resolveStatePath,
|
|
26
26
|
stateWritePath
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-DFQ72B7M.js";
|
|
28
28
|
|
|
29
29
|
// src/poller.ts
|
|
30
30
|
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from "fs";
|
package/dist/role.js
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectProvider,
|
|
3
|
+
expandQuery,
|
|
4
|
+
rerankResults
|
|
5
|
+
} from "./chunk-NX647OM3.js";
|
|
6
|
+
|
|
7
|
+
// src/search-pipeline.ts
|
|
8
|
+
var STRONG_SIGNAL_MIN_SCORE = 0.85;
|
|
9
|
+
var STRONG_SIGNAL_MIN_GAP = 0.15;
|
|
10
|
+
var DEFAULT_CANDIDATE_LIMIT = 40;
|
|
11
|
+
async function deepSearch(crystal, query, options = {}) {
|
|
12
|
+
const limit = options.limit || 5;
|
|
13
|
+
const candidateLimit = options.candidateLimit || DEFAULT_CANDIDATE_LIMIT;
|
|
14
|
+
const intent = options.intent;
|
|
15
|
+
const filter = options.filter;
|
|
16
|
+
const explain = options.explain || false;
|
|
17
|
+
const provider = await detectProvider();
|
|
18
|
+
if (provider.provider === "none") {
|
|
19
|
+
return crystal.search(query, limit, filter);
|
|
20
|
+
}
|
|
21
|
+
const db = crystal.sqliteDb;
|
|
22
|
+
if (!db) return crystal.search(query, limit, filter);
|
|
23
|
+
const sinceDate = filter?.since ? crystal.parseSince(filter.since) : void 0;
|
|
24
|
+
const untilDate = filter?.until ? crystal.parseSince(filter.until) : void 0;
|
|
25
|
+
const internalFilter = { ...filter, sinceDate, untilDate };
|
|
26
|
+
const initialFts = crystal.searchFTS(query, 20, internalFilter);
|
|
27
|
+
const topScore = initialFts[0]?.score ?? 0;
|
|
28
|
+
const secondScore = initialFts[1]?.score ?? 0;
|
|
29
|
+
const hasStrongSignal = !intent && initialFts.length > 0 && topScore >= STRONG_SIGNAL_MIN_SCORE && topScore - secondScore >= STRONG_SIGNAL_MIN_GAP;
|
|
30
|
+
const expanded = hasStrongSignal ? [] : await expandQuery(query, intent);
|
|
31
|
+
const allResultLists = [];
|
|
32
|
+
if (initialFts.length > 0) allResultLists.push(initialFts);
|
|
33
|
+
const [queryEmbedding] = await crystal.embed([query]);
|
|
34
|
+
const originalVec = crystal.searchVec(queryEmbedding, 30, internalFilter);
|
|
35
|
+
if (originalVec.length > 0) allResultLists.push(originalVec);
|
|
36
|
+
for (const variation of expanded) {
|
|
37
|
+
if (variation.type === "lex") {
|
|
38
|
+
const ftsResults = crystal.searchFTS(variation.text, 20, internalFilter);
|
|
39
|
+
if (ftsResults.length > 0) allResultLists.push(ftsResults);
|
|
40
|
+
} else {
|
|
41
|
+
const [embedding] = await crystal.embed([variation.text]);
|
|
42
|
+
const vecResults = crystal.searchVec(embedding, 20, internalFilter);
|
|
43
|
+
if (vecResults.length > 0) allResultLists.push(vecResults);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const weights = allResultLists.map((_, i) => i < 2 ? 2 : 1);
|
|
47
|
+
const fused = crystal.reciprocalRankFusion(allResultLists, weights);
|
|
48
|
+
const candidates = fused.slice(0, candidateLimit);
|
|
49
|
+
if (candidates.length === 0) return [];
|
|
50
|
+
const ftsScoreMap = /* @__PURE__ */ new Map();
|
|
51
|
+
const vecScoreMap = /* @__PURE__ */ new Map();
|
|
52
|
+
if (explain) {
|
|
53
|
+
for (const r of initialFts) ftsScoreMap.set(r.text.slice(0, 200), r.score);
|
|
54
|
+
for (const r of originalVec) vecScoreMap.set(r.text.slice(0, 200), r.score);
|
|
55
|
+
}
|
|
56
|
+
const passages = candidates.map((c) => c.text.slice(0, 500));
|
|
57
|
+
const rerankQuery = intent ? `${intent}: ${query}` : query;
|
|
58
|
+
const reranked = await rerankResults(rerankQuery, passages);
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
const blended = reranked.map((r) => {
|
|
61
|
+
const candidate = candidates[r.index];
|
|
62
|
+
if (!candidate) return null;
|
|
63
|
+
const rrfRank = r.index + 1;
|
|
64
|
+
let rrfWeight;
|
|
65
|
+
if (rrfRank <= 3) rrfWeight = 0.75;
|
|
66
|
+
else if (rrfRank <= 10) rrfWeight = 0.6;
|
|
67
|
+
else rrfWeight = 0.4;
|
|
68
|
+
const rrfScore = 1 / rrfRank;
|
|
69
|
+
const blendedScore = rrfWeight * rrfScore + (1 - rrfWeight) * r.score;
|
|
70
|
+
const ageDays = candidate.created_at ? (now - new Date(candidate.created_at).getTime()) / 864e5 : 0;
|
|
71
|
+
const recency = candidate.created_at ? crystal.recencyWeight(ageDays) : 1;
|
|
72
|
+
const finalScore = blendedScore * recency;
|
|
73
|
+
const freshness = candidate.created_at ? crystal.freshnessLabel(ageDays) : void 0;
|
|
74
|
+
const result = {
|
|
75
|
+
...candidate,
|
|
76
|
+
score: finalScore,
|
|
77
|
+
freshness
|
|
78
|
+
};
|
|
79
|
+
if (explain) {
|
|
80
|
+
const dedup = candidate.text.slice(0, 200);
|
|
81
|
+
result.explain = {
|
|
82
|
+
fts_score: ftsScoreMap.get(dedup),
|
|
83
|
+
vec_score: vecScoreMap.get(dedup),
|
|
84
|
+
rrf_rank: rrfRank,
|
|
85
|
+
rrf_score: rrfScore,
|
|
86
|
+
rerank_score: r.score,
|
|
87
|
+
recency_weight: recency,
|
|
88
|
+
final_score: finalScore
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}).filter((r) => r !== null);
|
|
93
|
+
const sorted = blended.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
94
|
+
const topNormScore = sorted[0]?.score || 1;
|
|
95
|
+
return sorted.map((r) => ({ ...r, score: Math.min(r.score / topNormScore * 0.95, 0.95) }));
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
deepSearch
|
|
99
|
+
};
|
package/dist/staging.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wipcomputer/memory-crystal",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.33",
|
|
4
4
|
"description": "Sovereign memory system — local-first with ephemeral encrypted relay. Your memory, your machine, your rules.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,21 @@
|
|
|
17
17
|
"crystal": "dist/cli.js",
|
|
18
18
|
"crystal-mcp": "dist/mcp-server.js"
|
|
19
19
|
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/",
|
|
22
|
+
"scripts/",
|
|
23
|
+
"skills/",
|
|
24
|
+
"migrations/",
|
|
25
|
+
"cloud/",
|
|
26
|
+
"openclaw.plugin.json",
|
|
27
|
+
"SKILL.md",
|
|
28
|
+
"TECHNICAL.md",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"CLA.md"
|
|
32
|
+
],
|
|
20
33
|
"scripts": {
|
|
34
|
+
"prepublishOnly": "npm run build",
|
|
21
35
|
"build": "tsup src/core.ts src/cli.ts src/mcp-server.ts src/openclaw.ts src/migrate.ts src/cc-hook.ts src/cc-poller.ts src/crypto.ts src/pair.ts src/poller.ts src/mirror-sync.ts src/file-sync.ts src/ldm.ts src/summarize.ts src/role.ts src/doctor.ts src/bridge.ts src/discover.ts src/bulk-copy.ts src/oc-backfill.ts src/dream-weaver.ts src/crystal-serve.ts src/staging.ts src/installer.ts --format esm --dts --outDir dist && tsup src/worker.ts --format esm --outDir dist --no-dts && cp scripts/crystal-capture.sh scripts/ldm-backup.sh dist/",
|
|
22
36
|
"build:local": "tsup src/core.ts src/cli.ts src/mcp-server.ts src/openclaw.ts src/migrate.ts src/cc-hook.ts src/cc-poller.ts src/crypto.ts src/pair.ts src/poller.ts src/mirror-sync.ts src/file-sync.ts src/ldm.ts src/summarize.ts src/role.ts src/doctor.ts src/bridge.ts src/discover.ts src/bulk-copy.ts src/oc-backfill.ts src/dream-weaver.ts src/crystal-serve.ts src/staging.ts src/installer.ts --format esm --dts --outDir dist",
|
|
23
37
|
"build:worker": "tsup src/worker.ts --format esm --outDir dist --no-dts",
|
package/.env.example
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Memory Crystal — Environment Config
|
|
2
|
-
# Copy to ~/.openclaw/memory-crystal/.env and fill in your keys.
|
|
3
|
-
# Or skip this and use 1Password (keys auto-resolve from "Agent Secrets" vault).
|
|
4
|
-
|
|
5
|
-
# Embedding provider: openai (default), ollama, or google
|
|
6
|
-
# CRYSTAL_EMBEDDING_PROVIDER=openai
|
|
7
|
-
|
|
8
|
-
# OpenAI (required if provider is openai)
|
|
9
|
-
OPENAI_API_KEY=sk-...
|
|
10
|
-
|
|
11
|
-
# Google (required if provider is google)
|
|
12
|
-
# GOOGLE_API_KEY=AIza...
|
|
13
|
-
|
|
14
|
-
# Ollama (only if provider is ollama — runs locally, no key needed)
|
|
15
|
-
# CRYSTAL_OLLAMA_HOST=http://localhost:11434
|
|
16
|
-
# CRYSTAL_OLLAMA_MODEL=nomic-embed-text
|
|
17
|
-
|
|
18
|
-
# Cloud mirror (Phase 2)
|
|
19
|
-
# CRYSTAL_REMOTE_URL=https://memory-crystal.your-worker.workers.dev
|
|
20
|
-
# CRYSTAL_REMOTE_TOKEN=your-token
|
package/.publish-skill.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "name": "wip-memory-crystal" }
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Memory Crystal — Environment Config
|
|
2
|
-
# Copy to ~/.openclaw/memory-crystal/.env and fill in your keys.
|
|
3
|
-
# Or skip this and use 1Password (keys auto-resolve from "Agent Secrets" vault).
|
|
4
|
-
|
|
5
|
-
# Embedding provider: openai (default), ollama, or google
|
|
6
|
-
# CRYSTAL_EMBEDDING_PROVIDER=openai
|
|
7
|
-
|
|
8
|
-
# OpenAI (required if provider is openai)
|
|
9
|
-
OPENAI_API_KEY=sk-...
|
|
10
|
-
|
|
11
|
-
# Google (required if provider is google)
|
|
12
|
-
# GOOGLE_API_KEY=AIza...
|
|
13
|
-
|
|
14
|
-
# Ollama (only if provider is ollama — runs locally, no key needed)
|
|
15
|
-
# CRYSTAL_OLLAMA_HOST=http://localhost:11434
|
|
16
|
-
# CRYSTAL_OLLAMA_MODEL=nomic-embed-text
|
|
17
|
-
|
|
18
|
-
# Cloud mirror (Phase 2)
|
|
19
|
-
# CRYSTAL_REMOTE_URL=https://memory-crystal.your-worker.workers.dev
|
|
20
|
-
# CRYSTAL_REMOTE_TOKEN=your-token
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "name": "wip-memory-crystal" }
|