@wipcomputer/memory-crystal 0.7.34-alpha.1 → 0.7.34-alpha.3
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/LICENSE +1 -1
- package/dist/bridge.js +64 -7
- package/dist/bulk-copy.js +67 -16
- package/dist/cc-hook.js +2291 -47
- package/dist/cc-poller.js +1967 -70
- package/dist/cli.js +4538 -139
- package/dist/core.js +1789 -6
- package/dist/crypto.js +153 -14
- package/dist/crystal-serve.js +64 -12
- package/dist/doctor.js +517 -52
- package/dist/dream-weaver.js +1755 -7
- package/dist/file-sync.js +407 -9
- package/dist/installer.js +840 -145
- package/dist/ldm.js +231 -16
- package/dist/mcp-server.js +1882 -17
- package/dist/migrate.js +1707 -11
- package/dist/mirror-sync.js +2052 -34
- package/dist/openclaw.js +1970 -69
- package/dist/pair.js +112 -16
- package/dist/poller.js +2275 -80
- package/dist/role.js +159 -7
- package/dist/staging.js +235 -10
- package/dist/summarize.js +142 -5
- package/package.json +3 -3
package/dist/crypto.js
CHANGED
|
@@ -1,17 +1,156 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
// src/crypto.ts
|
|
2
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
3
|
+
import { createCipheriv, createDecipheriv, createHmac, randomBytes, hkdfSync } from "crypto";
|
|
4
|
+
|
|
5
|
+
// src/ldm.ts
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, chmodSync, readdirSync } from "fs";
|
|
7
|
+
import { join, dirname } from "path";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
var HOME = process.env.HOME || "";
|
|
11
|
+
var LDM_ROOT = join(HOME, ".ldm");
|
|
12
|
+
function loadAgentConfig(id) {
|
|
13
|
+
const cfgPath = join(LDM_ROOT, "agents", id, "config.json");
|
|
14
|
+
try {
|
|
15
|
+
if (existsSync(cfgPath)) return JSON.parse(readFileSync(cfgPath, "utf-8"));
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function getAgentId(harnessHint) {
|
|
21
|
+
if (process.env.CRYSTAL_AGENT_ID) return process.env.CRYSTAL_AGENT_ID;
|
|
22
|
+
const agentsDir = join(LDM_ROOT, "agents");
|
|
23
|
+
if (existsSync(agentsDir)) {
|
|
24
|
+
try {
|
|
25
|
+
for (const d of readdirSync(agentsDir)) {
|
|
26
|
+
const cfg = loadAgentConfig(d);
|
|
27
|
+
if (!cfg || !cfg.agentId) continue;
|
|
28
|
+
if (!harnessHint) return cfg.agentId;
|
|
29
|
+
if (harnessHint === "claude-code" && cfg.harness === "claude-code-cli") return cfg.agentId;
|
|
30
|
+
if (harnessHint === "openclaw" && cfg.harness === "openclaw") return cfg.agentId;
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return harnessHint === "openclaw" ? "oc-lesa-mini" : "cc-mini";
|
|
36
|
+
}
|
|
37
|
+
function ldmPaths(agentId) {
|
|
38
|
+
const id = agentId || getAgentId();
|
|
39
|
+
const agentRoot = join(LDM_ROOT, "agents", id);
|
|
40
|
+
return {
|
|
41
|
+
root: LDM_ROOT,
|
|
42
|
+
bin: join(LDM_ROOT, "bin"),
|
|
43
|
+
secrets: join(LDM_ROOT, "secrets"),
|
|
44
|
+
state: join(LDM_ROOT, "state"),
|
|
45
|
+
config: join(LDM_ROOT, "config.json"),
|
|
46
|
+
crystalDb: join(LDM_ROOT, "memory", "crystal.db"),
|
|
47
|
+
crystalLance: join(LDM_ROOT, "memory", "lance"),
|
|
48
|
+
agentRoot,
|
|
49
|
+
transcripts: join(agentRoot, "memory", "transcripts"),
|
|
50
|
+
sessions: join(agentRoot, "memory", "sessions"),
|
|
51
|
+
daily: join(agentRoot, "memory", "daily"),
|
|
52
|
+
journals: join(agentRoot, "memory", "journals"),
|
|
53
|
+
workspace: join(agentRoot, "memory", "workspace")
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
var LEGACY_OC_DIR = join(HOME, ".openclaw");
|
|
57
|
+
function resolveSecretPath(filename) {
|
|
58
|
+
const paths = ldmPaths();
|
|
59
|
+
const ldmPath = join(paths.secrets, filename);
|
|
60
|
+
if (existsSync(ldmPath)) return ldmPath;
|
|
61
|
+
const legacyPath = join(LEGACY_OC_DIR, "secrets", filename);
|
|
62
|
+
if (existsSync(legacyPath)) return legacyPath;
|
|
63
|
+
return ldmPath;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/crypto.ts
|
|
67
|
+
import { createHash } from "crypto";
|
|
68
|
+
var KEY_PATH = process.env.CRYSTAL_RELAY_KEY_PATH || resolveSecretPath("crystal-relay-key");
|
|
69
|
+
function loadRelayKey() {
|
|
70
|
+
if (!existsSync2(KEY_PATH)) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Relay key not found at ${KEY_PATH}
|
|
73
|
+
Generate one: mkdir -p ~/.ldm/secrets && openssl rand -base64 32 > ~/.ldm/secrets/crystal-relay-key && chmod 600 ~/.ldm/secrets/crystal-relay-key
|
|
74
|
+
Or run: crystal pair`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const raw = readFileSync2(KEY_PATH, "utf-8").trim();
|
|
78
|
+
const key = Buffer.from(raw, "base64");
|
|
79
|
+
if (key.length !== 32) {
|
|
80
|
+
throw new Error(`Relay key must be 32 bytes (256 bits). Got ${key.length} bytes. Regenerate with: openssl rand -base64 32`);
|
|
81
|
+
}
|
|
82
|
+
return key;
|
|
83
|
+
}
|
|
84
|
+
function deriveSigningKey(masterKey) {
|
|
85
|
+
return Buffer.from(hkdfSync("sha256", masterKey, "", "crystal-relay-sign", 32));
|
|
86
|
+
}
|
|
87
|
+
function encrypt(plaintext, masterKey) {
|
|
88
|
+
const nonce = randomBytes(12);
|
|
89
|
+
const cipher = createCipheriv("aes-256-gcm", masterKey, nonce);
|
|
90
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
91
|
+
const tag = cipher.getAuthTag();
|
|
92
|
+
const signingKey = deriveSigningKey(masterKey);
|
|
93
|
+
const hmacData = Buffer.concat([nonce, ciphertext, tag]);
|
|
94
|
+
const hmac = createHmac("sha256", signingKey).update(hmacData).digest("hex");
|
|
95
|
+
return {
|
|
96
|
+
v: 1,
|
|
97
|
+
nonce: nonce.toString("base64"),
|
|
98
|
+
ciphertext: ciphertext.toString("base64"),
|
|
99
|
+
tag: tag.toString("base64"),
|
|
100
|
+
hmac
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function decrypt(payload, masterKey) {
|
|
104
|
+
if (payload.v !== 1) {
|
|
105
|
+
throw new Error(`Unknown payload version: ${payload.v}`);
|
|
106
|
+
}
|
|
107
|
+
const nonce = Buffer.from(payload.nonce, "base64");
|
|
108
|
+
const ciphertext = Buffer.from(payload.ciphertext, "base64");
|
|
109
|
+
const tag = Buffer.from(payload.tag, "base64");
|
|
110
|
+
const signingKey = deriveSigningKey(masterKey);
|
|
111
|
+
const hmacData = Buffer.concat([nonce, ciphertext, tag]);
|
|
112
|
+
const expectedHmac = createHmac("sha256", signingKey).update(hmacData).digest("hex");
|
|
113
|
+
if (payload.hmac !== expectedHmac) {
|
|
114
|
+
throw new Error("HMAC verification failed \u2014 blob rejected (tampered or wrong key)");
|
|
115
|
+
}
|
|
116
|
+
const decipher = createDecipheriv("aes-256-gcm", masterKey, nonce);
|
|
117
|
+
decipher.setAuthTag(tag);
|
|
118
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
119
|
+
}
|
|
120
|
+
function encryptJSON(data, masterKey) {
|
|
121
|
+
const plaintext = Buffer.from(JSON.stringify(data), "utf-8");
|
|
122
|
+
return encrypt(plaintext, masterKey);
|
|
123
|
+
}
|
|
124
|
+
function decryptJSON(payload, masterKey) {
|
|
125
|
+
const plaintext = decrypt(payload, masterKey);
|
|
126
|
+
return JSON.parse(plaintext.toString("utf-8"));
|
|
127
|
+
}
|
|
128
|
+
function encryptFile(filePath, masterKey) {
|
|
129
|
+
const plaintext = readFileSync2(filePath);
|
|
130
|
+
return encrypt(plaintext, masterKey);
|
|
131
|
+
}
|
|
132
|
+
var RELAY_KEY_PATH = KEY_PATH;
|
|
133
|
+
function generateRelayKey() {
|
|
134
|
+
return randomBytes(32);
|
|
135
|
+
}
|
|
136
|
+
function encodePairingString(key) {
|
|
137
|
+
if (key.length !== 32) throw new Error("Key must be 32 bytes");
|
|
138
|
+
return `mc1:${key.toString("base64")}`;
|
|
139
|
+
}
|
|
140
|
+
function decodePairingString(str) {
|
|
141
|
+
const trimmed = str.trim();
|
|
142
|
+
if (!trimmed.startsWith("mc1:")) {
|
|
143
|
+
throw new Error("Invalid pairing string (expected mc1: prefix)");
|
|
144
|
+
}
|
|
145
|
+
const key = Buffer.from(trimmed.slice(4), "base64");
|
|
146
|
+
if (key.length !== 32) {
|
|
147
|
+
throw new Error(`Invalid key length: expected 32 bytes, got ${key.length}`);
|
|
148
|
+
}
|
|
149
|
+
return key;
|
|
150
|
+
}
|
|
151
|
+
function hashBuffer(data) {
|
|
152
|
+
return createHash("sha256").update(data).digest("hex");
|
|
153
|
+
}
|
|
15
154
|
export {
|
|
16
155
|
RELAY_KEY_PATH,
|
|
17
156
|
decodePairingString,
|
package/dist/crystal-serve.js
CHANGED
|
@@ -1,13 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
ldmPaths
|
|
4
|
-
} from "./chunk-DFQ72B7M.js";
|
|
5
2
|
|
|
6
3
|
// src/crystal-serve.ts
|
|
7
4
|
import { createServer } from "http";
|
|
8
5
|
import { spawn } from "child_process";
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
|
|
7
|
+
// src/ldm.ts
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, chmodSync, readdirSync } from "fs";
|
|
9
|
+
import { join, dirname } from "path";
|
|
10
|
+
import { execSync } from "child_process";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
var HOME = process.env.HOME || "";
|
|
13
|
+
var LDM_ROOT = join(HOME, ".ldm");
|
|
14
|
+
function loadAgentConfig(id) {
|
|
15
|
+
const cfgPath = join(LDM_ROOT, "agents", id, "config.json");
|
|
16
|
+
try {
|
|
17
|
+
if (existsSync(cfgPath)) return JSON.parse(readFileSync(cfgPath, "utf-8"));
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function getAgentId(harnessHint) {
|
|
23
|
+
if (process.env.CRYSTAL_AGENT_ID) return process.env.CRYSTAL_AGENT_ID;
|
|
24
|
+
const agentsDir = join(LDM_ROOT, "agents");
|
|
25
|
+
if (existsSync(agentsDir)) {
|
|
26
|
+
try {
|
|
27
|
+
for (const d of readdirSync(agentsDir)) {
|
|
28
|
+
const cfg = loadAgentConfig(d);
|
|
29
|
+
if (!cfg || !cfg.agentId) continue;
|
|
30
|
+
if (!harnessHint) return cfg.agentId;
|
|
31
|
+
if (harnessHint === "claude-code" && cfg.harness === "claude-code-cli") return cfg.agentId;
|
|
32
|
+
if (harnessHint === "openclaw" && cfg.harness === "openclaw") return cfg.agentId;
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return harnessHint === "openclaw" ? "oc-lesa-mini" : "cc-mini";
|
|
38
|
+
}
|
|
39
|
+
function ldmPaths(agentId) {
|
|
40
|
+
const id = agentId || getAgentId();
|
|
41
|
+
const agentRoot = join(LDM_ROOT, "agents", id);
|
|
42
|
+
return {
|
|
43
|
+
root: LDM_ROOT,
|
|
44
|
+
bin: join(LDM_ROOT, "bin"),
|
|
45
|
+
secrets: join(LDM_ROOT, "secrets"),
|
|
46
|
+
state: join(LDM_ROOT, "state"),
|
|
47
|
+
config: join(LDM_ROOT, "config.json"),
|
|
48
|
+
crystalDb: join(LDM_ROOT, "memory", "crystal.db"),
|
|
49
|
+
crystalLance: join(LDM_ROOT, "memory", "lance"),
|
|
50
|
+
agentRoot,
|
|
51
|
+
transcripts: join(agentRoot, "memory", "transcripts"),
|
|
52
|
+
sessions: join(agentRoot, "memory", "sessions"),
|
|
53
|
+
daily: join(agentRoot, "memory", "daily"),
|
|
54
|
+
journals: join(agentRoot, "memory", "journals"),
|
|
55
|
+
workspace: join(agentRoot, "memory", "workspace")
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
var LEGACY_OC_DIR = join(HOME, ".openclaw");
|
|
59
|
+
|
|
60
|
+
// src/crystal-serve.ts
|
|
61
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
62
|
+
import { join as join2 } from "path";
|
|
11
63
|
var DEFAULT_PORT = 18790;
|
|
12
64
|
var AUTH_TOKEN = process.env.CRYSTAL_SERVE_TOKEN || "";
|
|
13
65
|
function checkAuth(req) {
|
|
@@ -147,22 +199,22 @@ async function handleProcess(req, res) {
|
|
|
147
199
|
}
|
|
148
200
|
function handleStatus(_req, res) {
|
|
149
201
|
const paths = ldmPaths();
|
|
150
|
-
const hasDb =
|
|
202
|
+
const hasDb = existsSync2(paths.crystalDb);
|
|
151
203
|
let lastDreamWeaver = null;
|
|
152
204
|
const agentId = process.env.CRYSTAL_AGENT_ID || "cc-mini";
|
|
153
|
-
const wmPath =
|
|
154
|
-
if (
|
|
205
|
+
const wmPath = join2(paths.state, `dream-weaver-${agentId}.json`);
|
|
206
|
+
if (existsSync2(wmPath)) {
|
|
155
207
|
try {
|
|
156
|
-
const wm = JSON.parse(
|
|
208
|
+
const wm = JSON.parse(readFileSync2(wmPath, "utf-8"));
|
|
157
209
|
lastDreamWeaver = wm.lastRunAt || null;
|
|
158
210
|
} catch {
|
|
159
211
|
}
|
|
160
212
|
}
|
|
161
213
|
let role = "core";
|
|
162
|
-
const rolePath =
|
|
163
|
-
if (
|
|
214
|
+
const rolePath = join2(paths.state, "role.json");
|
|
215
|
+
if (existsSync2(rolePath)) {
|
|
164
216
|
try {
|
|
165
|
-
const r = JSON.parse(
|
|
217
|
+
const r = JSON.parse(readFileSync2(rolePath, "utf-8"));
|
|
166
218
|
role = r.role || "core";
|
|
167
219
|
} catch {
|
|
168
220
|
}
|