@mnemoai/core 1.1.0 → 1.1.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 (220) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +7 -0
  4. package/dist/cli.js.map +7 -0
  5. package/dist/index.d.ts +128 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/{index.ts → dist/index.js} +526 -1333
  8. package/dist/index.js.map +7 -0
  9. package/dist/src/access-tracker.d.ts +97 -0
  10. package/dist/src/access-tracker.d.ts.map +1 -0
  11. package/dist/src/access-tracker.js +184 -0
  12. package/dist/src/access-tracker.js.map +7 -0
  13. package/dist/src/adapters/chroma.d.ts +31 -0
  14. package/dist/src/adapters/chroma.d.ts.map +1 -0
  15. package/{src/adapters/chroma.ts → dist/src/adapters/chroma.js} +45 -107
  16. package/dist/src/adapters/chroma.js.map +7 -0
  17. package/dist/src/adapters/lancedb.d.ts +29 -0
  18. package/dist/src/adapters/lancedb.d.ts.map +1 -0
  19. package/{src/adapters/lancedb.ts → dist/src/adapters/lancedb.js} +41 -109
  20. package/dist/src/adapters/lancedb.js.map +7 -0
  21. package/dist/src/adapters/pgvector.d.ts +33 -0
  22. package/dist/src/adapters/pgvector.d.ts.map +1 -0
  23. package/{src/adapters/pgvector.ts → dist/src/adapters/pgvector.js} +42 -104
  24. package/dist/src/adapters/pgvector.js.map +7 -0
  25. package/dist/src/adapters/qdrant.d.ts +34 -0
  26. package/dist/src/adapters/qdrant.d.ts.map +1 -0
  27. package/dist/src/adapters/qdrant.js +132 -0
  28. package/dist/src/adapters/qdrant.js.map +7 -0
  29. package/dist/src/adaptive-retrieval.d.ts +14 -0
  30. package/dist/src/adaptive-retrieval.d.ts.map +1 -0
  31. package/dist/src/adaptive-retrieval.js +52 -0
  32. package/dist/src/adaptive-retrieval.js.map +7 -0
  33. package/dist/src/audit-log.d.ts +56 -0
  34. package/dist/src/audit-log.d.ts.map +1 -0
  35. package/dist/src/audit-log.js +139 -0
  36. package/dist/src/audit-log.js.map +7 -0
  37. package/dist/src/chunker.d.ts +45 -0
  38. package/dist/src/chunker.d.ts.map +1 -0
  39. package/dist/src/chunker.js +157 -0
  40. package/dist/src/chunker.js.map +7 -0
  41. package/dist/src/config.d.ts +70 -0
  42. package/dist/src/config.d.ts.map +1 -0
  43. package/dist/src/config.js +142 -0
  44. package/dist/src/config.js.map +7 -0
  45. package/dist/src/decay-engine.d.ts +73 -0
  46. package/dist/src/decay-engine.d.ts.map +1 -0
  47. package/dist/src/decay-engine.js +119 -0
  48. package/dist/src/decay-engine.js.map +7 -0
  49. package/dist/src/embedder.d.ts +94 -0
  50. package/dist/src/embedder.d.ts.map +1 -0
  51. package/{src/embedder.ts → dist/src/embedder.js} +119 -317
  52. package/dist/src/embedder.js.map +7 -0
  53. package/dist/src/extraction-prompts.d.ts +12 -0
  54. package/dist/src/extraction-prompts.d.ts.map +1 -0
  55. package/dist/src/extraction-prompts.js +311 -0
  56. package/dist/src/extraction-prompts.js.map +7 -0
  57. package/dist/src/license.d.ts +29 -0
  58. package/dist/src/license.d.ts.map +1 -0
  59. package/{src/license.ts → dist/src/license.js} +42 -113
  60. package/dist/src/license.js.map +7 -0
  61. package/dist/src/llm-client.d.ts +23 -0
  62. package/dist/src/llm-client.d.ts.map +1 -0
  63. package/{src/llm-client.ts → dist/src/llm-client.js} +22 -55
  64. package/dist/src/llm-client.js.map +7 -0
  65. package/dist/src/logger.d.ts +33 -0
  66. package/dist/src/logger.d.ts.map +1 -0
  67. package/dist/src/logger.js +35 -0
  68. package/dist/src/logger.js.map +7 -0
  69. package/dist/src/mcp-server.d.ts +16 -0
  70. package/dist/src/mcp-server.d.ts.map +1 -0
  71. package/{src/mcp-server.ts → dist/src/mcp-server.js} +81 -181
  72. package/dist/src/mcp-server.js.map +7 -0
  73. package/dist/src/memory-categories.d.ts +40 -0
  74. package/dist/src/memory-categories.d.ts.map +1 -0
  75. package/dist/src/memory-categories.js +33 -0
  76. package/dist/src/memory-categories.js.map +7 -0
  77. package/dist/src/memory-upgrader.d.ts +71 -0
  78. package/dist/src/memory-upgrader.d.ts.map +1 -0
  79. package/dist/src/memory-upgrader.js +238 -0
  80. package/dist/src/memory-upgrader.js.map +7 -0
  81. package/dist/src/migrate.d.ts +47 -0
  82. package/dist/src/migrate.d.ts.map +1 -0
  83. package/{src/migrate.ts → dist/src/migrate.js} +57 -165
  84. package/dist/src/migrate.js.map +7 -0
  85. package/dist/src/mnemo.d.ts +67 -0
  86. package/dist/src/mnemo.d.ts.map +1 -0
  87. package/dist/src/mnemo.js +66 -0
  88. package/dist/src/mnemo.js.map +7 -0
  89. package/dist/src/noise-filter.d.ts +23 -0
  90. package/dist/src/noise-filter.d.ts.map +1 -0
  91. package/dist/src/noise-filter.js +62 -0
  92. package/dist/src/noise-filter.js.map +7 -0
  93. package/dist/src/noise-prototypes.d.ts +40 -0
  94. package/dist/src/noise-prototypes.d.ts.map +1 -0
  95. package/dist/src/noise-prototypes.js +116 -0
  96. package/dist/src/noise-prototypes.js.map +7 -0
  97. package/dist/src/observability.d.ts +16 -0
  98. package/dist/src/observability.d.ts.map +1 -0
  99. package/dist/src/observability.js +53 -0
  100. package/dist/src/observability.js.map +7 -0
  101. package/dist/src/query-tracker.d.ts +27 -0
  102. package/dist/src/query-tracker.d.ts.map +1 -0
  103. package/dist/src/query-tracker.js +32 -0
  104. package/dist/src/query-tracker.js.map +7 -0
  105. package/dist/src/reflection-event-store.d.ts +44 -0
  106. package/dist/src/reflection-event-store.d.ts.map +1 -0
  107. package/dist/src/reflection-event-store.js +50 -0
  108. package/dist/src/reflection-event-store.js.map +7 -0
  109. package/dist/src/reflection-item-store.d.ts +58 -0
  110. package/dist/src/reflection-item-store.d.ts.map +1 -0
  111. package/dist/src/reflection-item-store.js +69 -0
  112. package/dist/src/reflection-item-store.js.map +7 -0
  113. package/dist/src/reflection-mapped-metadata.d.ts +47 -0
  114. package/dist/src/reflection-mapped-metadata.d.ts.map +1 -0
  115. package/dist/src/reflection-mapped-metadata.js +40 -0
  116. package/dist/src/reflection-mapped-metadata.js.map +7 -0
  117. package/dist/src/reflection-metadata.d.ts +11 -0
  118. package/dist/src/reflection-metadata.d.ts.map +1 -0
  119. package/dist/src/reflection-metadata.js +24 -0
  120. package/dist/src/reflection-metadata.js.map +7 -0
  121. package/dist/src/reflection-ranking.d.ts +13 -0
  122. package/dist/src/reflection-ranking.d.ts.map +1 -0
  123. package/{src/reflection-ranking.ts → dist/src/reflection-ranking.js} +12 -21
  124. package/dist/src/reflection-ranking.js.map +7 -0
  125. package/dist/src/reflection-retry.d.ts +30 -0
  126. package/dist/src/reflection-retry.d.ts.map +1 -0
  127. package/{src/reflection-retry.ts → dist/src/reflection-retry.js} +24 -64
  128. package/dist/src/reflection-retry.js.map +7 -0
  129. package/dist/src/reflection-slices.d.ts +42 -0
  130. package/dist/src/reflection-slices.d.ts.map +1 -0
  131. package/{src/reflection-slices.ts → dist/src/reflection-slices.js} +60 -136
  132. package/dist/src/reflection-slices.js.map +7 -0
  133. package/dist/src/reflection-store.d.ts +85 -0
  134. package/dist/src/reflection-store.d.ts.map +1 -0
  135. package/dist/src/reflection-store.js +407 -0
  136. package/dist/src/reflection-store.js.map +7 -0
  137. package/dist/src/resonance-state.d.ts +19 -0
  138. package/dist/src/resonance-state.d.ts.map +1 -0
  139. package/{src/resonance-state.ts → dist/src/resonance-state.js} +13 -42
  140. package/dist/src/resonance-state.js.map +7 -0
  141. package/dist/src/retriever.d.ts +228 -0
  142. package/dist/src/retriever.d.ts.map +1 -0
  143. package/dist/src/retriever.js +1006 -0
  144. package/dist/src/retriever.js.map +7 -0
  145. package/dist/src/scopes.d.ts +58 -0
  146. package/dist/src/scopes.d.ts.map +1 -0
  147. package/dist/src/scopes.js +252 -0
  148. package/dist/src/scopes.js.map +7 -0
  149. package/dist/src/self-improvement-files.d.ts +20 -0
  150. package/dist/src/self-improvement-files.d.ts.map +1 -0
  151. package/{src/self-improvement-files.ts → dist/src/self-improvement-files.js} +24 -49
  152. package/dist/src/self-improvement-files.js.map +7 -0
  153. package/dist/src/semantic-gate.d.ts +24 -0
  154. package/dist/src/semantic-gate.d.ts.map +1 -0
  155. package/dist/src/semantic-gate.js +86 -0
  156. package/dist/src/semantic-gate.js.map +7 -0
  157. package/dist/src/session-recovery.d.ts +9 -0
  158. package/dist/src/session-recovery.d.ts.map +1 -0
  159. package/{src/session-recovery.ts → dist/src/session-recovery.js} +40 -57
  160. package/dist/src/session-recovery.js.map +7 -0
  161. package/dist/src/smart-extractor.d.ts +107 -0
  162. package/dist/src/smart-extractor.d.ts.map +1 -0
  163. package/{src/smart-extractor.ts → dist/src/smart-extractor.js} +130 -383
  164. package/dist/src/smart-extractor.js.map +7 -0
  165. package/dist/src/smart-metadata.d.ts +103 -0
  166. package/dist/src/smart-metadata.d.ts.map +1 -0
  167. package/dist/src/smart-metadata.js +361 -0
  168. package/dist/src/smart-metadata.js.map +7 -0
  169. package/dist/src/storage-adapter.d.ts +102 -0
  170. package/dist/src/storage-adapter.d.ts.map +1 -0
  171. package/dist/src/storage-adapter.js +22 -0
  172. package/dist/src/storage-adapter.js.map +7 -0
  173. package/dist/src/store.d.ts +108 -0
  174. package/dist/src/store.d.ts.map +1 -0
  175. package/dist/src/store.js +939 -0
  176. package/dist/src/store.js.map +7 -0
  177. package/dist/src/tier-manager.d.ts +57 -0
  178. package/dist/src/tier-manager.d.ts.map +1 -0
  179. package/dist/src/tier-manager.js +80 -0
  180. package/dist/src/tier-manager.js.map +7 -0
  181. package/dist/src/tools.d.ts +43 -0
  182. package/dist/src/tools.d.ts.map +1 -0
  183. package/dist/src/tools.js +1075 -0
  184. package/dist/src/tools.js.map +7 -0
  185. package/dist/src/wal-recovery.d.ts +30 -0
  186. package/dist/src/wal-recovery.d.ts.map +1 -0
  187. package/{src/wal-recovery.ts → dist/src/wal-recovery.js} +26 -79
  188. package/dist/src/wal-recovery.js.map +7 -0
  189. package/package.json +21 -2
  190. package/openclaw.plugin.json +0 -815
  191. package/src/access-tracker.ts +0 -341
  192. package/src/adapters/README.md +0 -78
  193. package/src/adapters/qdrant.ts +0 -191
  194. package/src/adaptive-retrieval.ts +0 -90
  195. package/src/audit-log.ts +0 -238
  196. package/src/chunker.ts +0 -254
  197. package/src/config.ts +0 -271
  198. package/src/decay-engine.ts +0 -238
  199. package/src/extraction-prompts.ts +0 -339
  200. package/src/memory-categories.ts +0 -71
  201. package/src/memory-upgrader.ts +0 -388
  202. package/src/mnemo.ts +0 -142
  203. package/src/noise-filter.ts +0 -97
  204. package/src/noise-prototypes.ts +0 -164
  205. package/src/observability.ts +0 -81
  206. package/src/query-tracker.ts +0 -57
  207. package/src/reflection-event-store.ts +0 -98
  208. package/src/reflection-item-store.ts +0 -112
  209. package/src/reflection-mapped-metadata.ts +0 -84
  210. package/src/reflection-metadata.ts +0 -23
  211. package/src/reflection-store.ts +0 -602
  212. package/src/retriever.ts +0 -1510
  213. package/src/scopes.ts +0 -375
  214. package/src/semantic-gate.ts +0 -121
  215. package/src/smart-metadata.ts +0 -561
  216. package/src/storage-adapter.ts +0 -153
  217. package/src/store.ts +0 -1330
  218. package/src/tier-manager.ts +0 -189
  219. package/src/tools.ts +0 -1292
  220. package/test/core.test.mjs +0 -301
@@ -1,132 +1,79 @@
1
- // SPDX-License-Identifier: MIT
2
- /**
3
- * Mnemo Pro License Validation
4
- *
5
- * Two modes:
6
- * 1. MNEMO_PRO_KEY — pre-activated key (offline, machine-bound)
7
- * 2. MNEMO_LICENSE_TOKEN — auto-activate on first run (online, one-time)
8
- *
9
- * Machine fingerprint: SHA-256(hostname + arch + cpuModel + platform)
10
- * Indie keys are bound to one machine. Team/Enterprise keys are per-seat.
11
- */
12
-
13
1
  import { verify, createPublicKey, createHash } from "node:crypto";
14
2
  import { hostname, arch, cpus, platform } from "node:os";
15
3
  import { homedir } from "node:os";
16
4
  import { join } from "node:path";
17
5
  import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
18
-
19
- // Ed25519 public key (DER/SPKI, base64) — safe to publish
20
- const PUBLIC_KEY_B64 =
21
- "MCowBQYDK2VwAyEAe8cshR0FAlDoILPw0aW1AyUNGbQXSOZaQKEZ7T2mXV8=";
22
-
23
- const ACTIVATION_URL =
24
- process.env.MNEMO_ACTIVATION_URL || "https://activation.m-nemo.ai";
25
-
6
+ import { log } from "./logger.js";
7
+ const PUBLIC_KEY_B64 = "MCowBQYDK2VwAyEAe8cshR0FAlDoILPw0aW1AyUNGbQXSOZaQKEZ7T2mXV8=";
8
+ const ACTIVATION_URL = process.env.MNEMO_ACTIVATION_URL || "https://activation.m-nemo.ai";
26
9
  const KEY_CACHE_PATH = join(homedir(), ".mnemo", "pro-key.json");
27
-
28
- let _cachedResult: boolean | null = null;
29
- let _cachedPayload: LicensePayload | null = null;
10
+ let _cachedResult = null;
11
+ let _cachedPayload = null;
30
12
  let _warnedOnce = false;
31
-
32
- export interface LicensePayload {
33
- licensee: string;
34
- email: string;
35
- plan: "indie" | "team" | "enterprise";
36
- issued: string;
37
- expires: string;
38
- machine_id?: string;
39
- }
40
-
41
- // ── Machine Fingerprint ──
42
-
43
- export function getMachineFingerprint(): string {
13
+ function getMachineFingerprint() {
44
14
  const cpu = cpus()[0]?.model || "unknown";
45
15
  const raw = `${hostname()}:${arch()}:${cpu}:${platform()}`;
46
16
  return createHash("sha256").update(raw).digest("hex");
47
17
  }
48
-
49
- // ── Key Verification (offline) ──
50
-
51
- function verifyKey(key: string): LicensePayload | null {
18
+ function verifyKey(key) {
52
19
  const dotIdx = key.indexOf(".");
53
20
  if (dotIdx < 1) return null;
54
-
55
21
  try {
56
22
  const payloadBuf = Buffer.from(key.slice(0, dotIdx), "base64");
57
23
  const signatureBuf = Buffer.from(key.slice(dotIdx + 1), "base64");
58
-
59
24
  const pubKeyObj = createPublicKey({
60
25
  key: Buffer.from(PUBLIC_KEY_B64, "base64"),
61
26
  format: "der",
62
- type: "spki",
27
+ type: "spki"
63
28
  });
64
-
65
29
  const valid = verify(null, payloadBuf, pubKeyObj, signatureBuf);
66
30
  if (!valid) return null;
67
-
68
- const payload: LicensePayload = JSON.parse(payloadBuf.toString("utf8"));
69
-
70
- // Check expiry
31
+ const payload = JSON.parse(payloadBuf.toString("utf8"));
71
32
  if (payload.expires) {
72
33
  if (new Date(payload.expires).getTime() < Date.now()) return null;
73
34
  }
74
-
75
- // Check machine binding (if present in payload)
76
35
  if (payload.machine_id) {
77
36
  const localFP = getMachineFingerprint();
78
37
  if (payload.machine_id !== localFP) return null;
79
38
  }
80
-
81
39
  return payload;
82
40
  } catch {
83
41
  return null;
84
42
  }
85
43
  }
86
-
87
- // ── Auto-Activation (online, one-time) ──
88
-
89
- async function autoActivate(token: string): Promise<string | null> {
44
+ async function autoActivate(token) {
90
45
  try {
91
46
  const machine_id = getMachineFingerprint();
92
47
  const resp = await fetch(`${ACTIVATION_URL}/activate`, {
93
48
  method: "POST",
94
49
  headers: { "Content-Type": "application/json" },
95
50
  body: JSON.stringify({ token, machine_id }),
96
- signal: AbortSignal.timeout(10000),
51
+ signal: AbortSignal.timeout(1e4)
97
52
  });
98
-
99
53
  if (!resp.ok) {
100
- const err = await resp.json().catch(() => ({})) as any;
54
+ const err = await resp.json().catch(() => ({}));
101
55
  if (resp.status === 409) {
102
- console.warn(
103
- `[mnemo] License token already activated on another device. ` +
104
- `Visit https://m-nemo.ai/pro/migrate to transfer.`
56
+ log.warn(
57
+ `License token already activated on another device. Visit https://m-nemo.ai/pro/migrate to transfer.`
105
58
  );
106
59
  } else {
107
- console.warn(`[mnemo] Activation failed: ${err.error || resp.status}`);
60
+ log.warn(`Activation failed: ${err.error || resp.status}`);
108
61
  }
109
62
  return null;
110
63
  }
111
-
112
- const { key } = await resp.json() as { key: string };
113
-
114
- // Cache the activated key locally
64
+ const { key } = await resp.json();
115
65
  try {
116
66
  mkdirSync(join(homedir(), ".mnemo"), { recursive: true });
117
- writeFileSync(KEY_CACHE_PATH, JSON.stringify({ key, token, activated: new Date().toISOString() }));
118
- } catch { /* non-fatal */ }
119
-
67
+ writeFileSync(KEY_CACHE_PATH, JSON.stringify({ key, token, activated: (/* @__PURE__ */ new Date()).toISOString() }));
68
+ } catch {
69
+ }
120
70
  return key;
121
71
  } catch (err) {
122
- console.warn(`[mnemo] Activation request failed (offline?): ${err}`);
72
+ log.warn(`Activation request failed (offline?): ${err}`);
123
73
  return null;
124
74
  }
125
75
  }
126
-
127
- // ── Load cached key from disk ──
128
-
129
- function loadCachedKey(): string | null {
76
+ function loadCachedKey() {
130
77
  try {
131
78
  const data = JSON.parse(readFileSync(KEY_CACHE_PATH, "utf8"));
132
79
  return data.key || null;
@@ -134,13 +81,8 @@ function loadCachedKey(): string | null {
134
81
  return null;
135
82
  }
136
83
  }
137
-
138
- // ── Main entry point ──
139
-
140
- export function isProLicensed(): boolean {
84
+ function isProLicensed() {
141
85
  if (_cachedResult !== null) return _cachedResult;
142
-
143
- // Priority 1: explicit MNEMO_PRO_KEY env var
144
86
  const explicitKey = process.env.MNEMO_PRO_KEY?.trim();
145
87
  if (explicitKey) {
146
88
  const payload = verifyKey(explicitKey);
@@ -150,8 +92,6 @@ export function isProLicensed(): boolean {
150
92
  return true;
151
93
  }
152
94
  }
153
-
154
- // Priority 2: cached key from previous activation
155
95
  const cachedKey = loadCachedKey();
156
96
  if (cachedKey) {
157
97
  const payload = verifyKey(cachedKey);
@@ -161,35 +101,25 @@ export function isProLicensed(): boolean {
161
101
  return true;
162
102
  }
163
103
  }
164
-
165
- // Priority 3: auto-activate with token (async — won't block first call)
166
104
  const token = process.env.MNEMO_LICENSE_TOKEN?.trim();
167
105
  if (token) {
168
- // Fire and forget — next process start will pick up cached key
169
106
  autoActivate(token).then((key) => {
170
107
  if (key) {
171
108
  const payload = verifyKey(key);
172
109
  if (payload) {
173
110
  _cachedPayload = payload;
174
111
  _cachedResult = true;
175
- console.log(`[mnemo] Pro license activated for ${payload.licensee} (${payload.plan})`);
112
+ log.info(`Pro license activated for ${payload.licensee} (${payload.plan})`);
176
113
  }
177
114
  }
178
- }).catch(() => {});
115
+ }).catch(() => {
116
+ });
179
117
  }
180
-
181
118
  _cachedResult = false;
182
119
  return false;
183
120
  }
184
-
185
- /**
186
- * Async version — waits for activation to complete if token is present.
187
- * Use this during plugin initialization.
188
- */
189
- export async function ensureProLicense(): Promise<boolean> {
121
+ async function ensureProLicense() {
190
122
  if (_cachedResult !== null) return _cachedResult;
191
-
192
- // Check explicit key
193
123
  const explicitKey = process.env.MNEMO_PRO_KEY?.trim();
194
124
  if (explicitKey) {
195
125
  const payload = verifyKey(explicitKey);
@@ -199,8 +129,6 @@ export async function ensureProLicense(): Promise<boolean> {
199
129
  return true;
200
130
  }
201
131
  }
202
-
203
- // Check cached key
204
132
  const cachedKey = loadCachedKey();
205
133
  if (cachedKey) {
206
134
  const payload = verifyKey(cachedKey);
@@ -210,8 +138,6 @@ export async function ensureProLicense(): Promise<boolean> {
210
138
  return true;
211
139
  }
212
140
  }
213
-
214
- // Try auto-activate with token
215
141
  const token = process.env.MNEMO_LICENSE_TOKEN?.trim();
216
142
  if (token) {
217
143
  const key = await autoActivate(token);
@@ -220,39 +146,42 @@ export async function ensureProLicense(): Promise<boolean> {
220
146
  if (payload) {
221
147
  _cachedPayload = payload;
222
148
  _cachedResult = true;
223
- console.log(`[mnemo] Pro license activated for ${payload.licensee} (${payload.plan})`);
149
+ log.info(`Pro license activated for ${payload.licensee} (${payload.plan})`);
224
150
  return true;
225
151
  }
226
152
  }
227
153
  }
228
-
229
154
  _cachedResult = false;
230
155
  return false;
231
156
  }
232
-
233
- export function getLicenseInfo(): LicensePayload | null {
157
+ function getLicenseInfo() {
234
158
  isProLicensed();
235
159
  return _cachedPayload;
236
160
  }
237
-
238
- export function requirePro(featureName: string): boolean {
161
+ function requirePro(featureName) {
239
162
  if (isProLicensed()) return true;
240
-
241
163
  if (!_warnedOnce) {
242
- console.warn(
243
- `[mnemo] Pro features disabled set MNEMO_PRO_KEY or MNEMO_LICENSE_TOKEN to enable. ` +
244
- `Core functionality is fully available. https://m-nemo.ai/pro`,
164
+ log.warn(
165
+ `Pro features disabled \u2014 set MNEMO_PRO_KEY or MNEMO_LICENSE_TOKEN to enable. Core functionality is fully available. https://m-nemo.ai/pro`
245
166
  );
246
167
  _warnedOnce = true;
247
168
  }
248
169
  if (process.env.MNEMO_DEBUG) {
249
- console.debug(`[mnemo] Pro feature skipped: ${featureName}`);
170
+ log.debug(`Pro feature skipped: ${featureName}`);
250
171
  }
251
172
  return false;
252
173
  }
253
-
254
- export function _resetLicenseCache(): void {
174
+ function _resetLicenseCache() {
255
175
  _cachedResult = null;
256
176
  _cachedPayload = null;
257
177
  _warnedOnce = false;
258
178
  }
179
+ export {
180
+ _resetLicenseCache,
181
+ ensureProLicense,
182
+ getLicenseInfo,
183
+ getMachineFingerprint,
184
+ isProLicensed,
185
+ requirePro
186
+ };
187
+ //# sourceMappingURL=license.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/license.ts"],
4
+ "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Mnemo Pro License Validation\n *\n * Two modes:\n * 1. MNEMO_PRO_KEY \u2014 pre-activated key (offline, machine-bound)\n * 2. MNEMO_LICENSE_TOKEN \u2014 auto-activate on first run (online, one-time)\n *\n * Machine fingerprint: SHA-256(hostname + arch + cpuModel + platform)\n * Indie keys are bound to one machine. Team/Enterprise keys are per-seat.\n */\n\nimport { verify, createPublicKey, createHash } from \"node:crypto\";\nimport { hostname, arch, cpus, platform } from \"node:os\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { log } from \"./logger.js\";\n\n// Ed25519 public key (DER/SPKI, base64) \u2014 safe to publish\nconst PUBLIC_KEY_B64 =\n \"MCowBQYDK2VwAyEAe8cshR0FAlDoILPw0aW1AyUNGbQXSOZaQKEZ7T2mXV8=\";\n\nconst ACTIVATION_URL =\n process.env.MNEMO_ACTIVATION_URL || \"https://activation.m-nemo.ai\";\n\nconst KEY_CACHE_PATH = join(homedir(), \".mnemo\", \"pro-key.json\");\n\nlet _cachedResult: boolean | null = null;\nlet _cachedPayload: LicensePayload | null = null;\nlet _warnedOnce = false;\n\nexport interface LicensePayload {\n licensee: string;\n email: string;\n plan: \"indie\" | \"team\" | \"enterprise\";\n issued: string;\n expires: string;\n machine_id?: string;\n}\n\n// \u2500\u2500 Machine Fingerprint \u2500\u2500\n\nexport function getMachineFingerprint(): string {\n const cpu = cpus()[0]?.model || \"unknown\";\n const raw = `${hostname()}:${arch()}:${cpu}:${platform()}`;\n return createHash(\"sha256\").update(raw).digest(\"hex\");\n}\n\n// \u2500\u2500 Key Verification (offline) \u2500\u2500\n\nfunction verifyKey(key: string): LicensePayload | null {\n const dotIdx = key.indexOf(\".\");\n if (dotIdx < 1) return null;\n\n try {\n const payloadBuf = Buffer.from(key.slice(0, dotIdx), \"base64\");\n const signatureBuf = Buffer.from(key.slice(dotIdx + 1), \"base64\");\n\n const pubKeyObj = createPublicKey({\n key: Buffer.from(PUBLIC_KEY_B64, \"base64\"),\n format: \"der\",\n type: \"spki\",\n });\n\n const valid = verify(null, payloadBuf, pubKeyObj, signatureBuf);\n if (!valid) return null;\n\n const payload: LicensePayload = JSON.parse(payloadBuf.toString(\"utf8\"));\n\n // Check expiry\n if (payload.expires) {\n if (new Date(payload.expires).getTime() < Date.now()) return null;\n }\n\n // Check machine binding (if present in payload)\n if (payload.machine_id) {\n const localFP = getMachineFingerprint();\n if (payload.machine_id !== localFP) return null;\n }\n\n return payload;\n } catch {\n return null;\n }\n}\n\n// \u2500\u2500 Auto-Activation (online, one-time) \u2500\u2500\n\nasync function autoActivate(token: string): Promise<string | null> {\n try {\n const machine_id = getMachineFingerprint();\n const resp = await fetch(`${ACTIVATION_URL}/activate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token, machine_id }),\n signal: AbortSignal.timeout(10000),\n });\n\n if (!resp.ok) {\n const err = await resp.json().catch(() => ({})) as any;\n if (resp.status === 409) {\n log.warn(\n `License token already activated on another device. ` +\n `Visit https://m-nemo.ai/pro/migrate to transfer.`\n );\n } else {\n log.warn(`Activation failed: ${err.error || resp.status}`);\n }\n return null;\n }\n\n const { key } = await resp.json() as { key: string };\n\n // Cache the activated key locally\n try {\n mkdirSync(join(homedir(), \".mnemo\"), { recursive: true });\n writeFileSync(KEY_CACHE_PATH, JSON.stringify({ key, token, activated: new Date().toISOString() }));\n } catch { /* non-fatal */ }\n\n return key;\n } catch (err) {\n log.warn(`Activation request failed (offline?): ${err}`);\n return null;\n }\n}\n\n// \u2500\u2500 Load cached key from disk \u2500\u2500\n\nfunction loadCachedKey(): string | null {\n try {\n const data = JSON.parse(readFileSync(KEY_CACHE_PATH, \"utf8\"));\n return data.key || null;\n } catch {\n return null;\n }\n}\n\n// \u2500\u2500 Main entry point \u2500\u2500\n\nexport function isProLicensed(): boolean {\n if (_cachedResult !== null) return _cachedResult;\n\n // Priority 1: explicit MNEMO_PRO_KEY env var\n const explicitKey = process.env.MNEMO_PRO_KEY?.trim();\n if (explicitKey) {\n const payload = verifyKey(explicitKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Priority 2: cached key from previous activation\n const cachedKey = loadCachedKey();\n if (cachedKey) {\n const payload = verifyKey(cachedKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Priority 3: auto-activate with token (async \u2014 won't block first call)\n const token = process.env.MNEMO_LICENSE_TOKEN?.trim();\n if (token) {\n // Fire and forget \u2014 next process start will pick up cached key\n autoActivate(token).then((key) => {\n if (key) {\n const payload = verifyKey(key);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n log.info(`Pro license activated for ${payload.licensee} (${payload.plan})`);\n }\n }\n }).catch(() => {});\n }\n\n _cachedResult = false;\n return false;\n}\n\n/**\n * Async version \u2014 waits for activation to complete if token is present.\n * Use this during plugin initialization.\n */\nexport async function ensureProLicense(): Promise<boolean> {\n if (_cachedResult !== null) return _cachedResult;\n\n // Check explicit key\n const explicitKey = process.env.MNEMO_PRO_KEY?.trim();\n if (explicitKey) {\n const payload = verifyKey(explicitKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Check cached key\n const cachedKey = loadCachedKey();\n if (cachedKey) {\n const payload = verifyKey(cachedKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Try auto-activate with token\n const token = process.env.MNEMO_LICENSE_TOKEN?.trim();\n if (token) {\n const key = await autoActivate(token);\n if (key) {\n const payload = verifyKey(key);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n log.info(`Pro license activated for ${payload.licensee} (${payload.plan})`);\n return true;\n }\n }\n }\n\n _cachedResult = false;\n return false;\n}\n\nexport function getLicenseInfo(): LicensePayload | null {\n isProLicensed();\n return _cachedPayload;\n}\n\nexport function requirePro(featureName: string): boolean {\n if (isProLicensed()) return true;\n\n if (!_warnedOnce) {\n log.warn(\n `Pro features disabled \u2014 set MNEMO_PRO_KEY or MNEMO_LICENSE_TOKEN to enable. ` +\n `Core functionality is fully available. https://m-nemo.ai/pro`,\n );\n _warnedOnce = true;\n }\n if (process.env.MNEMO_DEBUG) {\n log.debug(`Pro feature skipped: ${featureName}`);\n }\n return false;\n}\n\nexport function _resetLicenseCache(): void {\n _cachedResult = null;\n _cachedPayload = null;\n _warnedOnce = false;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * LLM Client for memory extraction and dedup decisions.
3
+ * Uses OpenAI-compatible API (reuses the embedding provider config).
4
+ */
5
+ export interface LlmClientConfig {
6
+ apiKey: string;
7
+ model: string;
8
+ baseURL?: string;
9
+ timeoutMs?: number;
10
+ log?: (msg: string) => void;
11
+ }
12
+ export interface LlmClient {
13
+ /** Send a prompt and parse the JSON response. Returns null on failure. */
14
+ completeJson<T>(prompt: string, label?: string): Promise<T | null>;
15
+ }
16
+ /**
17
+ * Extract JSON from an LLM response that may be wrapped in markdown fences
18
+ * or contain surrounding text.
19
+ */
20
+ declare function extractJsonFromResponse(text: string): string | null;
21
+ export declare function createLlmClient(config: LlmClientConfig): LlmClient;
22
+ export { extractJsonFromResponse };
23
+ //# sourceMappingURL=llm-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/llm-client.ts"],"names":[],"mappings":"AACA;;;GAGG;AAIH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACpE;AAED;;;GAGG;AACH,iBAAS,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B5D;AAQD,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA+DlE;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
@@ -1,39 +1,11 @@
1
- // SPDX-License-Identifier: MIT
2
- /**
3
- * LLM Client for memory extraction and dedup decisions.
4
- * Uses OpenAI-compatible API (reuses the embedding provider config).
5
- */
6
-
7
1
  import OpenAI from "openai";
8
-
9
- export interface LlmClientConfig {
10
- apiKey: string;
11
- model: string;
12
- baseURL?: string;
13
- timeoutMs?: number;
14
- log?: (msg: string) => void;
15
- }
16
-
17
- export interface LlmClient {
18
- /** Send a prompt and parse the JSON response. Returns null on failure. */
19
- completeJson<T>(prompt: string, label?: string): Promise<T | null>;
20
- }
21
-
22
- /**
23
- * Extract JSON from an LLM response that may be wrapped in markdown fences
24
- * or contain surrounding text.
25
- */
26
- function extractJsonFromResponse(text: string): string | null {
27
- // Try markdown code fence first (```json ... ``` or ``` ... ```)
2
+ function extractJsonFromResponse(text) {
28
3
  const fenceMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
29
4
  if (fenceMatch) {
30
5
  return fenceMatch[1].trim();
31
6
  }
32
-
33
- // Try balanced brace extraction
34
7
  const firstBrace = text.indexOf("{");
35
8
  if (firstBrace === -1) return null;
36
-
37
9
  let depth = 0;
38
10
  let lastBrace = -1;
39
11
  for (let i = firstBrace; i < text.length; i++) {
@@ -46,80 +18,75 @@ function extractJsonFromResponse(text: string): string | null {
46
18
  }
47
19
  }
48
20
  }
49
-
50
21
  if (lastBrace === -1) return null;
51
22
  return text.substring(firstBrace, lastBrace + 1);
52
23
  }
53
-
54
- function previewText(value: string, maxLen = 200): string {
24
+ function previewText(value, maxLen = 200) {
55
25
  const normalized = value.replace(/\s+/g, " ").trim();
56
26
  if (normalized.length <= maxLen) return normalized;
57
27
  return `${normalized.slice(0, maxLen - 3)}...`;
58
28
  }
59
-
60
- export function createLlmClient(config: LlmClientConfig): LlmClient {
29
+ function createLlmClient(config) {
61
30
  const client = new OpenAI({
62
31
  apiKey: config.apiKey,
63
32
  baseURL: config.baseURL,
64
- timeout: config.timeoutMs ?? 30000,
33
+ timeout: config.timeoutMs ?? 3e4
34
+ });
35
+ const log = config.log ?? (() => {
65
36
  });
66
- const log = config.log ?? (() => {});
67
-
68
37
  return {
69
- async completeJson<T>(prompt: string, label = "generic"): Promise<T | null> {
38
+ async completeJson(prompt, label = "generic") {
70
39
  try {
71
40
  const response = await client.chat.completions.create({
72
41
  model: config.model,
73
42
  messages: [
74
43
  {
75
44
  role: "system",
76
- content:
77
- "You are a memory extraction assistant. Always respond with valid JSON only.",
45
+ content: "You are a memory extraction assistant. Always respond with valid JSON only."
78
46
  },
79
- { role: "user", content: prompt },
47
+ { role: "user", content: prompt }
80
48
  ],
81
- temperature: 0.1,
49
+ temperature: 0.1
82
50
  });
83
-
84
51
  const raw = response.choices?.[0]?.message?.content;
85
52
  if (!raw) {
86
53
  log(
87
- `mnemo: llm-client [${label}] empty response content from model ${config.model}`,
54
+ `mnemo: llm-client [${label}] empty response content from model ${config.model}`
88
55
  );
89
56
  return null;
90
57
  }
91
58
  if (typeof raw !== "string") {
92
59
  log(
93
- `mnemo: llm-client [${label}] non-string response content type=${Array.isArray(raw) ? "array" : typeof raw} from model ${config.model}`,
60
+ `mnemo: llm-client [${label}] non-string response content type=${Array.isArray(raw) ? "array" : typeof raw} from model ${config.model}`
94
61
  );
95
62
  return null;
96
63
  }
97
-
98
64
  const jsonStr = extractJsonFromResponse(raw);
99
65
  if (!jsonStr) {
100
66
  log(
101
- `mnemo: llm-client [${label}] no JSON object found (chars=${raw.length}, preview=${JSON.stringify(previewText(raw))})`,
67
+ `mnemo: llm-client [${label}] no JSON object found (chars=${raw.length}, preview=${JSON.stringify(previewText(raw))})`
102
68
  );
103
69
  return null;
104
70
  }
105
-
106
71
  try {
107
- return JSON.parse(jsonStr) as T;
72
+ return JSON.parse(jsonStr);
108
73
  } catch (err) {
109
74
  log(
110
- `mnemo: llm-client [${label}] JSON.parse failed: ${err instanceof Error ? err.message : String(err)} (jsonChars=${jsonStr.length}, jsonPreview=${JSON.stringify(previewText(jsonStr))})`,
75
+ `mnemo: llm-client [${label}] JSON.parse failed: ${err instanceof Error ? err.message : String(err)} (jsonChars=${jsonStr.length}, jsonPreview=${JSON.stringify(previewText(jsonStr))})`
111
76
  );
112
77
  return null;
113
78
  }
114
79
  } catch (err) {
115
- // Graceful degradation — return null so caller can fall back
116
80
  log(
117
- `mnemo: llm-client [${label}] request failed for model ${config.model}: ${err instanceof Error ? err.message : String(err)}`,
81
+ `mnemo: llm-client [${label}] request failed for model ${config.model}: ${err instanceof Error ? err.message : String(err)}`
118
82
  );
119
83
  return null;
120
84
  }
121
- },
85
+ }
122
86
  };
123
87
  }
124
-
125
- export { extractJsonFromResponse };
88
+ export {
89
+ createLlmClient,
90
+ extractJsonFromResponse
91
+ };
92
+ //# sourceMappingURL=llm-client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/llm-client.ts"],
4
+ "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * LLM Client for memory extraction and dedup decisions.\n * Uses OpenAI-compatible API (reuses the embedding provider config).\n */\n\nimport OpenAI from \"openai\";\n\nexport interface LlmClientConfig {\n apiKey: string;\n model: string;\n baseURL?: string;\n timeoutMs?: number;\n log?: (msg: string) => void;\n}\n\nexport interface LlmClient {\n /** Send a prompt and parse the JSON response. Returns null on failure. */\n completeJson<T>(prompt: string, label?: string): Promise<T | null>;\n}\n\n/**\n * Extract JSON from an LLM response that may be wrapped in markdown fences\n * or contain surrounding text.\n */\nfunction extractJsonFromResponse(text: string): string | null {\n // Try markdown code fence first (```json ... ``` or ``` ... ```)\n const fenceMatch = text.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)```/);\n if (fenceMatch) {\n return fenceMatch[1].trim();\n }\n\n // Try balanced brace extraction\n const firstBrace = text.indexOf(\"{\");\n if (firstBrace === -1) return null;\n\n let depth = 0;\n let lastBrace = -1;\n for (let i = firstBrace; i < text.length; i++) {\n if (text[i] === \"{\") depth++;\n else if (text[i] === \"}\") {\n depth--;\n if (depth === 0) {\n lastBrace = i;\n break;\n }\n }\n }\n\n if (lastBrace === -1) return null;\n return text.substring(firstBrace, lastBrace + 1);\n}\n\nfunction previewText(value: string, maxLen = 200): string {\n const normalized = value.replace(/\\s+/g, \" \").trim();\n if (normalized.length <= maxLen) return normalized;\n return `${normalized.slice(0, maxLen - 3)}...`;\n}\n\nexport function createLlmClient(config: LlmClientConfig): LlmClient {\n const client = new OpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n timeout: config.timeoutMs ?? 30000,\n });\n const log = config.log ?? (() => {});\n\n return {\n async completeJson<T>(prompt: string, label = \"generic\"): Promise<T | null> {\n try {\n const response = await client.chat.completions.create({\n model: config.model,\n messages: [\n {\n role: \"system\",\n content:\n \"You are a memory extraction assistant. Always respond with valid JSON only.\",\n },\n { role: \"user\", content: prompt },\n ],\n temperature: 0.1,\n });\n\n const raw = response.choices?.[0]?.message?.content;\n if (!raw) {\n log(\n `mnemo: llm-client [${label}] empty response content from model ${config.model}`,\n );\n return null;\n }\n if (typeof raw !== \"string\") {\n log(\n `mnemo: llm-client [${label}] non-string response content type=${Array.isArray(raw) ? \"array\" : typeof raw} from model ${config.model}`,\n );\n return null;\n }\n\n const jsonStr = extractJsonFromResponse(raw);\n if (!jsonStr) {\n log(\n `mnemo: llm-client [${label}] no JSON object found (chars=${raw.length}, preview=${JSON.stringify(previewText(raw))})`,\n );\n return null;\n }\n\n try {\n return JSON.parse(jsonStr) as T;\n } catch (err) {\n log(\n `mnemo: llm-client [${label}] JSON.parse failed: ${err instanceof Error ? err.message : String(err)} (jsonChars=${jsonStr.length}, jsonPreview=${JSON.stringify(previewText(jsonStr))})`,\n );\n return null;\n }\n } catch (err) {\n // Graceful degradation \u2014 return null so caller can fall back\n log(\n `mnemo: llm-client [${label}] request failed for model ${config.model}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return null;\n }\n },\n };\n}\n\nexport { extractJsonFromResponse };\n"],
5
+ "mappings": "AAMA,OAAO,YAAY;AAmBnB,SAAS,wBAAwB,MAA6B;AAE5D,QAAM,aAAa,KAAK,MAAM,iCAAiC;AAC/D,MAAI,YAAY;AACd,WAAO,WAAW,CAAC,EAAE,KAAK;AAAA,EAC5B;AAGA,QAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,MAAI,eAAe,GAAI,QAAO;AAE9B,MAAI,QAAQ;AACZ,MAAI,YAAY;AAChB,WAAS,IAAI,YAAY,IAAI,KAAK,QAAQ,KAAK;AAC7C,QAAI,KAAK,CAAC,MAAM,IAAK;AAAA,aACZ,KAAK,CAAC,MAAM,KAAK;AACxB;AACA,UAAI,UAAU,GAAG;AACf,oBAAY;AACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,GAAI,QAAO;AAC7B,SAAO,KAAK,UAAU,YAAY,YAAY,CAAC;AACjD;AAEA,SAAS,YAAY,OAAe,SAAS,KAAa;AACxD,QAAM,aAAa,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACnD,MAAI,WAAW,UAAU,OAAQ,QAAO;AACxC,SAAO,GAAG,WAAW,MAAM,GAAG,SAAS,CAAC,CAAC;AAC3C;AAEO,SAAS,gBAAgB,QAAoC;AAClE,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO,aAAa;AAAA,EAC/B,CAAC;AACD,QAAM,MAAM,OAAO,QAAQ,MAAM;AAAA,EAAC;AAElC,SAAO;AAAA,IACL,MAAM,aAAgB,QAAgB,QAAQ,WAA8B;AAC1E,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,UACpD,OAAO,OAAO;AAAA,UACd,UAAU;AAAA,YACR;AAAA,cACE,MAAM;AAAA,cACN,SACE;AAAA,YACJ;AAAA,YACA,EAAE,MAAM,QAAQ,SAAS,OAAO;AAAA,UAClC;AAAA,UACA,aAAa;AAAA,QACf,CAAC;AAED,cAAM,MAAM,SAAS,UAAU,CAAC,GAAG,SAAS;AAC5C,YAAI,CAAC,KAAK;AACR;AAAA,YACE,sBAAsB,KAAK,uCAAuC,OAAO,KAAK;AAAA,UAChF;AACA,iBAAO;AAAA,QACT;AACA,YAAI,OAAO,QAAQ,UAAU;AAC3B;AAAA,YACE,sBAAsB,KAAK,sCAAsC,MAAM,QAAQ,GAAG,IAAI,UAAU,OAAO,GAAG,eAAe,OAAO,KAAK;AAAA,UACvI;AACA,iBAAO;AAAA,QACT;AAEA,cAAM,UAAU,wBAAwB,GAAG;AAC3C,YAAI,CAAC,SAAS;AACZ;AAAA,YACE,sBAAsB,KAAK,iCAAiC,IAAI,MAAM,aAAa,KAAK,UAAU,YAAY,GAAG,CAAC,CAAC;AAAA,UACrH;AACA,iBAAO;AAAA,QACT;AAEA,YAAI;AACF,iBAAO,KAAK,MAAM,OAAO;AAAA,QAC3B,SAAS,KAAK;AACZ;AAAA,YACE,sBAAsB,KAAK,wBAAwB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,eAAe,QAAQ,MAAM,iBAAiB,KAAK,UAAU,YAAY,OAAO,CAAC,CAAC;AAAA,UACvL;AACA,iBAAO;AAAA,QACT;AAAA,MACF,SAAS,KAAK;AAEZ;AAAA,UACE,sBAAsB,KAAK,8BAA8B,OAAO,KAAK,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC5H;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Mnemo Logger — Unified logging interface
3
+ *
4
+ * Replaces scattered console.log/warn/error with a structured logger.
5
+ * Supports log levels, prefixes, and external logger injection.
6
+ *
7
+ * Usage:
8
+ * import { log } from "./logger.js";
9
+ * log.info("message");
10
+ * log.warn("something wrong");
11
+ * log.debug("verbose detail"); // only when MNEMO_DEBUG=1
12
+ */
13
+ export type LogLevel = "debug" | "info" | "warn" | "error";
14
+ export interface Logger {
15
+ debug(msg: string, ...args: unknown[]): void;
16
+ info(msg: string, ...args: unknown[]): void;
17
+ warn(msg: string, ...args: unknown[]): void;
18
+ error(msg: string, ...args: unknown[]): void;
19
+ }
20
+ /**
21
+ * Get the current logger instance.
22
+ */
23
+ export declare const log: Logger;
24
+ /**
25
+ * Replace the default logger with a custom implementation.
26
+ * Useful for integrating with OpenClaw's api.logger or external logging services.
27
+ */
28
+ export declare function setLogger(logger: Logger): void;
29
+ /**
30
+ * Reset to the default console logger.
31
+ */
32
+ export declare function resetLogger(): void;
33
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAC9C;AAwBD;;GAEG;AACH,eAAO,MAAM,GAAG,EAAE,MAKjB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
@@ -0,0 +1,35 @@
1
+ const PREFIX = "[mnemo]";
2
+ const isDebug = () => !!process.env.MNEMO_DEBUG;
3
+ const defaultLogger = {
4
+ debug(msg, ...args) {
5
+ if (isDebug()) console.debug(PREFIX, msg, ...args);
6
+ },
7
+ info(msg, ...args) {
8
+ console.log(PREFIX, msg, ...args);
9
+ },
10
+ warn(msg, ...args) {
11
+ console.warn(PREFIX, msg, ...args);
12
+ },
13
+ error(msg, ...args) {
14
+ console.error(PREFIX, msg, ...args);
15
+ }
16
+ };
17
+ let _logger = defaultLogger;
18
+ const log = {
19
+ debug: (msg, ...args) => _logger.debug(msg, ...args),
20
+ info: (msg, ...args) => _logger.info(msg, ...args),
21
+ warn: (msg, ...args) => _logger.warn(msg, ...args),
22
+ error: (msg, ...args) => _logger.error(msg, ...args)
23
+ };
24
+ function setLogger(logger) {
25
+ _logger = logger;
26
+ }
27
+ function resetLogger() {
28
+ _logger = defaultLogger;
29
+ }
30
+ export {
31
+ log,
32
+ resetLogger,
33
+ setLogger
34
+ };
35
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/logger.ts"],
4
+ "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Mnemo Logger \u2014 Unified logging interface\n *\n * Replaces scattered console.log/warn/error with a structured logger.\n * Supports log levels, prefixes, and external logger injection.\n *\n * Usage:\n * import { log } from \"./logger.js\";\n * log.info(\"message\");\n * log.warn(\"something wrong\");\n * log.debug(\"verbose detail\"); // only when MNEMO_DEBUG=1\n */\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface Logger {\n debug(msg: string, ...args: unknown[]): void;\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n}\n\nconst PREFIX = \"[mnemo]\";\n\nconst isDebug = () => !!process.env.MNEMO_DEBUG;\n\n/** Default console-based logger */\nconst defaultLogger: Logger = {\n debug(msg, ...args) {\n if (isDebug()) console.debug(PREFIX, msg, ...args);\n },\n info(msg, ...args) {\n console.log(PREFIX, msg, ...args);\n },\n warn(msg, ...args) {\n console.warn(PREFIX, msg, ...args);\n },\n error(msg, ...args) {\n console.error(PREFIX, msg, ...args);\n },\n};\n\nlet _logger: Logger = defaultLogger;\n\n/**\n * Get the current logger instance.\n */\nexport const log: Logger = {\n debug: (msg, ...args) => _logger.debug(msg, ...args),\n info: (msg, ...args) => _logger.info(msg, ...args),\n warn: (msg, ...args) => _logger.warn(msg, ...args),\n error: (msg, ...args) => _logger.error(msg, ...args),\n};\n\n/**\n * Replace the default logger with a custom implementation.\n * Useful for integrating with OpenClaw's api.logger or external logging services.\n */\nexport function setLogger(logger: Logger): void {\n _logger = logger;\n}\n\n/**\n * Reset to the default console logger.\n */\nexport function resetLogger(): void {\n _logger = defaultLogger;\n}\n"],
5
+ "mappings": "AAuBA,MAAM,SAAS;AAEf,MAAM,UAAU,MAAM,CAAC,CAAC,QAAQ,IAAI;AAGpC,MAAM,gBAAwB;AAAA,EAC5B,MAAM,QAAQ,MAAM;AAClB,QAAI,QAAQ,EAAG,SAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,EACnD;AAAA,EACA,KAAK,QAAQ,MAAM;AACjB,YAAQ,IAAI,QAAQ,KAAK,GAAG,IAAI;AAAA,EAClC;AAAA,EACA,KAAK,QAAQ,MAAM;AACjB,YAAQ,KAAK,QAAQ,KAAK,GAAG,IAAI;AAAA,EACnC;AAAA,EACA,MAAM,QAAQ,MAAM;AAClB,YAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,EACpC;AACF;AAEA,IAAI,UAAkB;AAKf,MAAM,MAAc;AAAA,EACzB,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,KAAK,GAAG,IAAI;AAAA,EACnD,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,KAAK,GAAG,IAAI;AAAA,EACjD,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,KAAK,GAAG,IAAI;AAAA,EACjD,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,KAAK,GAAG,IAAI;AACrD;AAMO,SAAS,UAAU,QAAsB;AAC9C,YAAU;AACZ;AAKO,SAAS,cAAoB;AAClC,YAAU;AACZ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * MCP Server for Mnemo
3
+ *
4
+ * Exposes memory tools (search, store, delete, update, list, stats) over
5
+ * stdio JSON-RPC so Claude Code can call them directly without going through
6
+ * the OpenClaw gateway.
7
+ *
8
+ * Usage:
9
+ * node --import jiti/register src/mcp-server.ts
10
+ *
11
+ * Register with Claude Code:
12
+ * claude mcp add memory -s user -- node --import jiti/register \
13
+ * /path/to/mnemo/src/mcp-server.ts
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/mcp-server.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;GAaG"}