@wipcomputer/memory-crystal 0.7.34-alpha.2 → 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/dist/crypto.js CHANGED
@@ -1,17 +1,156 @@
1
- import {
2
- RELAY_KEY_PATH,
3
- decodePairingString,
4
- decrypt,
5
- decryptJSON,
6
- encodePairingString,
7
- encrypt,
8
- encryptFile,
9
- encryptJSON,
10
- generateRelayKey,
11
- hashBuffer,
12
- loadRelayKey
13
- } from "./chunk-D3MACYZ4.js";
14
- import "./chunk-DFQ72B7M.js";
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,
@@ -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
- import { existsSync, readFileSync } from "fs";
10
- import { join } from "path";
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 = existsSync(paths.crystalDb);
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 = join(paths.state, `dream-weaver-${agentId}.json`);
154
- if (existsSync(wmPath)) {
205
+ const wmPath = join2(paths.state, `dream-weaver-${agentId}.json`);
206
+ if (existsSync2(wmPath)) {
155
207
  try {
156
- const wm = JSON.parse(readFileSync(wmPath, "utf-8"));
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 = join(paths.state, "role.json");
163
- if (existsSync(rolePath)) {
214
+ const rolePath = join2(paths.state, "role.json");
215
+ if (existsSync2(rolePath)) {
164
216
  try {
165
- const r = JSON.parse(readFileSync(rolePath, "utf-8"));
217
+ const r = JSON.parse(readFileSync2(rolePath, "utf-8"));
166
218
  role = r.role || "core";
167
219
  } catch {
168
220
  }