@bcmyguest/openmem 0.0.0-stage → 0.8.0

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 (230) hide show
  1. package/.agents/skills/openmem/SKILL.md +34 -0
  2. package/LICENSE +190 -0
  3. package/README.md +140 -2
  4. package/bin/hindsight-project-index +530 -0
  5. package/cli/coding-agent/access-policy.mjs +38 -0
  6. package/cli/coding-agent/agents.mjs +34 -0
  7. package/cli/coding-agent/bank-from-cwd.mjs +140 -0
  8. package/cli/coding-agent/capabilities.mjs +34 -0
  9. package/cli/coding-agent/context-store.mjs +81 -0
  10. package/cli/coding-agent/context-trim.mjs +897 -0
  11. package/cli/coding-agent/file-io.mjs +72 -0
  12. package/cli/coding-agent/hook-harnesses.mjs +88 -0
  13. package/cli/coding-agent/hook-runner.mjs +284 -0
  14. package/cli/coding-agent/install-access.mjs +119 -0
  15. package/cli/coding-agent/install-context-proxy.mjs +333 -0
  16. package/cli/coding-agent/install-rtk.mjs +60 -0
  17. package/cli/coding-agent/install-shim.mjs +201 -0
  18. package/cli/coding-agent/opencode-config.mjs +107 -0
  19. package/cli/coding-agent/opencode-plugin.mjs +132 -0
  20. package/cli/coding-agent/opencode2-plugin/index.js +299 -0
  21. package/cli/coding-agent/recall-frame.mjs +29 -0
  22. package/cli/coding-agent/remove-upstream-access.mjs +122 -0
  23. package/cli/coding-agent/runtime.mjs +40 -0
  24. package/cli/coding-agent/session-db.mjs +626 -0
  25. package/cli/coding-agent/session-facade.mjs +86 -0
  26. package/cli/coding-agent/toml-edit.mjs +161 -0
  27. package/cli/cognee-mcp.mjs +260 -0
  28. package/cli/context-management.mjs +254 -0
  29. package/cli/feed-sync.mjs +27 -0
  30. package/cli/install-coding-agent.mjs +298 -0
  31. package/cli/install-memory-mcp.mjs +426 -0
  32. package/cli/is-main.mjs +18 -0
  33. package/cli/mcp/context-sql-runner.mjs +58 -0
  34. package/cli/mcp/context-store.mjs +670 -0
  35. package/cli/mcp/context.mjs +61 -0
  36. package/cli/mcp/export/cognee-dataset-name.mjs +12 -0
  37. package/cli/mcp/export/cognee.mjs +119 -0
  38. package/cli/mcp/export/hindsight.mjs +48 -0
  39. package/cli/mcp/export/index.mjs +26 -0
  40. package/cli/mcp/export/mem0.mjs +31 -0
  41. package/cli/mcp/knowledge.mjs +333 -0
  42. package/cli/mcp/llm-synthesize.mjs +50 -0
  43. package/cli/mcp/mem0-scope-id.mjs +22 -0
  44. package/cli/mcp/memory-facade.mjs +521 -0
  45. package/cli/mcp/memory-reflect-consolidate.mjs +120 -0
  46. package/cli/mcp/notebooklm-banks.mjs +21 -0
  47. package/cli/mcp/notebooklm-hindsight-rest.mjs +45 -0
  48. package/cli/mcp/notebooklm-provider.mjs +225 -0
  49. package/cli/mcp/pmm-records.mjs +23 -0
  50. package/cli/mcp/portability.mjs +158 -0
  51. package/cli/mcp/stdio.mjs +65 -0
  52. package/cli/mcp/unified-mcp-install.mjs +65 -0
  53. package/cli/mem0-mcp.mjs +108 -0
  54. package/cli/notebooklm-mcp.mjs +236 -0
  55. package/cli/openmem-mcp.mjs +38 -0
  56. package/cli/openmem.mjs +222 -0
  57. package/cli/roots.mjs +85 -0
  58. package/cli/schedule.mjs +176 -0
  59. package/cli/stack.mjs +438 -0
  60. package/cli/switch-memory-provider.mjs +358 -0
  61. package/cli/wiring-report.mjs +125 -0
  62. package/compose.bench.yaml +12 -0
  63. package/compose.yaml +227 -0
  64. package/config/coding-agent.json +13 -0
  65. package/config/hindsight.env.example +26 -0
  66. package/config/index.yml +54 -0
  67. package/config/litellm.lemond.yaml +40 -0
  68. package/config/litellm.ollama.yaml +33 -0
  69. package/config/notebooklm.yml +33 -0
  70. package/config/openmem.toml.example +124 -0
  71. package/docs/OPENMEM.md +23 -0
  72. package/docs/architecture.md +245 -0
  73. package/docs/assets/README.md +10 -0
  74. package/docs/assets/setup-demo.gif +0 -0
  75. package/docs/coding-agent-hooks.md +75 -0
  76. package/docs/coding-agent-scopes.md +22 -0
  77. package/docs/coding-agents.md +71 -0
  78. package/docs/cognee.md +37 -0
  79. package/docs/compatibility.md +80 -0
  80. package/docs/concepts.md +135 -0
  81. package/docs/context-management.md +255 -0
  82. package/docs/context-proxy.md +115 -0
  83. package/docs/context-research.md +215 -0
  84. package/docs/context-store.md +277 -0
  85. package/docs/hindsight.md +77 -0
  86. package/docs/index-sources-roadmap.md +38 -0
  87. package/docs/index-sources.md +39 -0
  88. package/docs/indexer-sinks.md +122 -0
  89. package/docs/indexing.md +57 -0
  90. package/docs/manual-stack-setup.md +74 -0
  91. package/docs/mcp-install-guide.md +4 -0
  92. package/docs/mcp-setup.md +104 -0
  93. package/docs/mem0.md +141 -0
  94. package/docs/memory-portability.md +73 -0
  95. package/docs/memory-providers.md +246 -0
  96. package/docs/meridian-litellm.md +198 -0
  97. package/docs/model-configuration.md +106 -0
  98. package/docs/notebooklm.md +83 -0
  99. package/docs/npx-install.md +172 -0
  100. package/docs/openmem-config.md +194 -0
  101. package/docs/provider-wizard-hooks.md +222 -0
  102. package/docs/quickstart.md +107 -0
  103. package/docs/setup-wizard.md +117 -0
  104. package/docs/sources-feeds.md +39 -0
  105. package/docs/sources-readwise.md +23 -0
  106. package/docs/sources-zotero.md +22 -0
  107. package/glama.json +4 -0
  108. package/indexer/Dockerfile +38 -0
  109. package/indexer/code_outline.py +146 -0
  110. package/indexer/feed_sync.py +107 -0
  111. package/indexer/index_config.py +290 -0
  112. package/indexer/pytest.ini +5 -0
  113. package/indexer/sinks/__init__.py +148 -0
  114. package/indexer/sinks/_http.py +44 -0
  115. package/indexer/sinks/cognee.py +329 -0
  116. package/indexer/sinks/hindsight.py +107 -0
  117. package/indexer/sinks/mem0.py +247 -0
  118. package/indexer/watch.py +240 -0
  119. package/package.json +96 -4
  120. package/providers/_template/compose.yaml +52 -0
  121. package/providers/cognee/OPERATOR.md +82 -0
  122. package/providers/cognee/compose.yaml +89 -0
  123. package/providers/cognee/lib/clack.mjs +157 -0
  124. package/providers/cognee/lib/coding-agent.mjs +26 -0
  125. package/providers/cognee/lib/cognee-mcp-install.mjs +153 -0
  126. package/providers/cognee/lib/config.mjs +98 -0
  127. package/providers/cognee/lib/env-file.mjs +194 -0
  128. package/providers/cognee/lib/env-setup.mjs +83 -0
  129. package/providers/cognee/lib/llm-env.mjs +382 -0
  130. package/providers/cognee/lib/ports.mjs +25 -0
  131. package/providers/cognee/lib/smoke.mjs +246 -0
  132. package/providers/cognee/lib/stack-env.mjs +134 -0
  133. package/providers/cognee/lib/state.mjs +101 -0
  134. package/providers/cognee/lib/summary.mjs +56 -0
  135. package/providers/cognee/lib/validate.mjs +97 -0
  136. package/providers/cognee/manifest.json +56 -0
  137. package/providers/cognee/wizard.mjs +134 -0
  138. package/providers/graphiti/manifest.json +15 -0
  139. package/providers/hindsight/lib/clack.mjs +347 -0
  140. package/providers/hindsight/lib/coding-agent.mjs +204 -0
  141. package/providers/hindsight/lib/config.mjs +79 -0
  142. package/providers/hindsight/lib/env-setup.mjs +72 -0
  143. package/providers/hindsight/lib/litellm-routes.mjs +196 -0
  144. package/providers/hindsight/lib/meridian.mjs +307 -0
  145. package/providers/hindsight/lib/model-backend.mjs +243 -0
  146. package/providers/hindsight/lib/notebooklm-mcp-install.mjs +127 -0
  147. package/providers/hindsight/lib/ollama-compose.mjs +51 -0
  148. package/providers/hindsight/lib/ports.mjs +30 -0
  149. package/providers/hindsight/lib/probe.mjs +54 -0
  150. package/providers/hindsight/lib/stack-env.mjs +125 -0
  151. package/providers/hindsight/lib/state.mjs +80 -0
  152. package/providers/hindsight/lib/summary.mjs +57 -0
  153. package/providers/hindsight/lib/validate.mjs +81 -0
  154. package/providers/hindsight/manifest.json +58 -0
  155. package/providers/hindsight/wizard.mjs +150 -0
  156. package/providers/letta/manifest.json +29 -0
  157. package/providers/mem0/OPERATOR.md +143 -0
  158. package/providers/mem0/compose.yaml +79 -0
  159. package/providers/mem0/lib/coding-agent.mjs +58 -0
  160. package/providers/mem0/lib/config.mjs +45 -0
  161. package/providers/mem0/lib/configure.mjs +201 -0
  162. package/providers/mem0/lib/dotenv.mjs +71 -0
  163. package/providers/mem0/lib/env-file.mjs +110 -0
  164. package/providers/mem0/lib/http.mjs +69 -0
  165. package/providers/mem0/lib/litellm.mjs +457 -0
  166. package/providers/mem0/lib/mcp-install.mjs +165 -0
  167. package/providers/mem0/lib/secret.mjs +30 -0
  168. package/providers/mem0/lib/smoke.mjs +179 -0
  169. package/providers/mem0/lib/stack-env.mjs +255 -0
  170. package/providers/mem0/lib/state.mjs +67 -0
  171. package/providers/mem0/lib/summary.mjs +88 -0
  172. package/providers/mem0/lib/url.mjs +106 -0
  173. package/providers/mem0/lib/validate.mjs +158 -0
  174. package/providers/mem0/manifest.json +47 -0
  175. package/providers/mem0/wizard.mjs +146 -0
  176. package/providers/zep/manifest.json +29 -0
  177. package/proxy/config.yaml +33 -0
  178. package/proxy/openmem_context/__init__.py +6 -0
  179. package/proxy/openmem_context/adapters.py +504 -0
  180. package/proxy/openmem_context/compress.py +53 -0
  181. package/proxy/openmem_context/core.py +606 -0
  182. package/proxy/openmem_context/embed.py +212 -0
  183. package/proxy/openmem_context/recall.py +49 -0
  184. package/proxy/openmem_context/session.py +70 -0
  185. package/proxy/openmem_context/sessiondb.py +276 -0
  186. package/proxy/openmem_context/settings.py +134 -0
  187. package/proxy/openmem_context/store.py +129 -0
  188. package/proxy/openmem_context/tokens.py +123 -0
  189. package/proxy/openmem_context/ttl.py +78 -0
  190. package/proxy/openmem_context_hook.py +34 -0
  191. package/scripts/check-pack.mjs +219 -0
  192. package/scripts/gen-masks.mjs +83 -0
  193. package/scripts/gen-masks.sh +4 -0
  194. package/scripts/promotion-provider-smoke.mjs +34 -0
  195. package/scripts/run-setup.sh +30 -0
  196. package/scripts/setup.mjs +107 -0
  197. package/scripts/setup.sh +5 -0
  198. package/scripts/spin-up.sh +45 -0
  199. package/scripts/wizard.sh +5 -0
  200. package/server.json +34 -0
  201. package/sync/Dockerfile +27 -0
  202. package/sync/memory_backend.py +87 -0
  203. package/sync/notebooklm_sync.py +664 -0
  204. package/wizard/bin/pm.mjs +97 -0
  205. package/wizard/bin/setup.mjs +33 -0
  206. package/wizard/lib/compose-version.mjs +32 -0
  207. package/wizard/lib/compose.mjs +70 -0
  208. package/wizard/lib/context-step.mjs +140 -0
  209. package/wizard/lib/curl.mjs +103 -0
  210. package/wizard/lib/docker-build.mjs +148 -0
  211. package/wizard/lib/exec.mjs +41 -0
  212. package/wizard/lib/indexer.mjs +6 -0
  213. package/wizard/lib/litellm-env.mjs +55 -0
  214. package/wizard/lib/load-machine-env.mjs +4 -0
  215. package/wizard/lib/log.mjs +32 -0
  216. package/wizard/lib/notebooklm-agent-access.mjs +92 -0
  217. package/wizard/lib/notebooklm.mjs +214 -0
  218. package/wizard/lib/parse-args.mjs +87 -0
  219. package/wizard/lib/paths.mjs +39 -0
  220. package/wizard/lib/pm-config.mjs +371 -0
  221. package/wizard/lib/ports.mjs +94 -0
  222. package/wizard/lib/prompts.mjs +85 -0
  223. package/wizard/lib/provider-wizard/helpers.mjs +98 -0
  224. package/wizard/lib/provider-wizard/run-steps.mjs +129 -0
  225. package/wizard/lib/providers/registry.mjs +262 -0
  226. package/wizard/lib/run-clack.mjs +108 -0
  227. package/wizard/lib/run-wizard.mjs +750 -0
  228. package/wizard/lib/validate-setup.mjs +62 -0
  229. package/wizard/lib/wizard-action.mjs +34 -0
  230. package/wizard/lib/wizard-conf.mjs +71 -0
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Pack-content gate: what `npm publish` would ship must contain the runtime files and
4
+ * nothing machine-specific. Used by CI (`npm run pack:check`) and by test_cli_package.mjs.
5
+ *
6
+ * node scripts/check-pack.mjs inspect `npm pack --dry-run`
7
+ * node scripts/check-pack.mjs --tarball X.tgz inspect a packed tarball (extracted to a tmp dir)
8
+ * node scripts/check-pack.mjs --json machine-readable result
9
+ *
10
+ * Exit 1 when any problem is found. Leak findings are reported, never silently allowed;
11
+ * the only allowlist is ALLOWED_PATH_MENTIONS below.
12
+ */
13
+ import { spawnSync } from "node:child_process";
14
+ import fs from "node:fs";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ import { fileURLToPath } from "node:url";
18
+ import { listShippedFiles } from "../cli/stack.mjs";
19
+
20
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
21
+
22
+ /** Files the installed package cannot run without. */
23
+ export const REQUIRED = [
24
+ "package.json",
25
+ "README.md",
26
+ "cli/openmem.mjs",
27
+ "cli/notebooklm-mcp.mjs",
28
+ "cli/cognee-mcp.mjs",
29
+ "cli/mem0-mcp.mjs",
30
+ "cli/install-memory-mcp.mjs",
31
+ "cli/switch-memory-provider.mjs",
32
+ "cli/stack.mjs",
33
+ "cli/roots.mjs",
34
+ ".agents/skills/openmem/SKILL.md",
35
+ "wizard/bin/setup.mjs",
36
+ "wizard/bin/pm.mjs",
37
+ "wizard/lib/run-wizard.mjs",
38
+ "wizard/lib/parse-args.mjs",
39
+ "providers/hindsight/manifest.json",
40
+ "providers/hindsight/wizard.mjs",
41
+ "compose.yaml",
42
+ "compose.bench.yaml",
43
+ "indexer/Dockerfile",
44
+ "indexer/watch.py",
45
+ "bin/hindsight-project-index",
46
+ "sync/Dockerfile",
47
+ "config/index.yml",
48
+ "config/notebooklm.yml",
49
+ "config/hindsight.env.example",
50
+ "config/litellm.lemond.yaml",
51
+ "config/openmem.toml.example",
52
+ "scripts/setup.mjs",
53
+ "scripts/gen-masks.mjs",
54
+ ];
55
+
56
+ /** Paths that must never be packed. Tested against the posix path inside the package. */
57
+ export const FORBIDDEN = [
58
+ [/(^|\/)\.env$/, ".env"],
59
+ [/(^|\/)\.env\.(?!example$)/, ".env.*"],
60
+ [/(^|\/)machine\.env$/, "machine.env"],
61
+ [/(^|\/)hindsight\.env$/, "hindsight.env"],
62
+ [/(^|\/)litellm\.env$/, "litellm.env"],
63
+ [/(^|\/)compose\.override\.ya?ml$/, "compose.override.yaml"],
64
+ [/(^|\/)\.build-hash$/, ".build-hash"],
65
+ [/(^|\/)\.pm-stack-manifest\.json$/, "stack manifest"],
66
+ [/\.pyc$/, "*.pyc"],
67
+ [/(^|\/)__pycache__(\/|$)/, "__pycache__"],
68
+ [/(^|\/)node_modules(\/|$)/, "node_modules"],
69
+ [/(^|\/)\.codegraph(\/|$)/, ".codegraph"],
70
+ [/(^|\/)\.git(\/|$)/, ".git"],
71
+ [/(^|\/)\.agents\/(?!skills\/openmem\/)/, ".agents except openmem skill"],
72
+ [/^site(\/|$)/, "site/"],
73
+ [/ROADMAP/, "ROADMAP*"],
74
+ [/WAVE2/, "WAVE2*"],
75
+ [/\.(tgz|tar|log)$/, "archives/logs"],
76
+ ];
77
+
78
+ /**
79
+ * Operator path markers. Container-internal home of the Hindsight image is not a leak.
80
+ */
81
+ const LEAK_RE = /\/(?:home|Users)\/[A-Za-z0-9._-]*/g;
82
+ export const ALLOWED_PATH_MENTIONS = [/^\/home\/hindsight$/];
83
+
84
+ /**
85
+ * @param {string} cwd
86
+ * @param {string[]} args
87
+ */
88
+ function npm(cwd, args) {
89
+ const r = spawnSync("npm", args, { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
90
+ if (r.status !== 0) throw new Error(`npm ${args.join(" ")} failed: ${r.stderr || r.stdout}`);
91
+ return r.stdout;
92
+ }
93
+
94
+ /**
95
+ * @param {string} root
96
+ * @returns {{ files: string[], dir: string, cleanup: () => void }}
97
+ */
98
+ function listFromDryRun(root) {
99
+ const out = npm(root, ["pack", "--dry-run", "--json", "--ignore-scripts"]);
100
+ const j = JSON.parse(out);
101
+ return { files: j[0].files.map((/** @type {{path: string}} */ f) => f.path), dir: root, cleanup() {} };
102
+ }
103
+
104
+ /**
105
+ * @param {string} tgz
106
+ * @returns {{ files: string[], dir: string, cleanup: () => void }}
107
+ */
108
+ function listFromTarball(tgz) {
109
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "pm-pack-"));
110
+ const r = spawnSync("tar", ["-xzf", path.resolve(tgz), "-C", tmp], { encoding: "utf8" });
111
+ if (r.status !== 0) throw new Error(`tar failed: ${r.stderr}`);
112
+ const dir = path.join(tmp, "package");
113
+ /** @type {string[]} */
114
+ const files = [];
115
+ const walk = (/** @type {string} */ rel) => {
116
+ for (const e of fs.readdirSync(path.join(dir, rel), { withFileTypes: true })) {
117
+ const child = rel ? `${rel}/${e.name}` : e.name;
118
+ if (e.isDirectory()) walk(child);
119
+ else files.push(child);
120
+ }
121
+ };
122
+ walk("");
123
+ return { files: files.sort(), dir, cleanup: () => fs.rmSync(tmp, { recursive: true, force: true }) };
124
+ }
125
+
126
+ /** @param {Buffer} buf */
127
+ function isText(buf) {
128
+ return !buf.subarray(0, 8000).includes(0);
129
+ }
130
+
131
+ /**
132
+ * @typedef {{ ok: boolean, count: number, topLevel: Record<string, number>, problems: string[], files: string[] }} PackResult
133
+ */
134
+
135
+ /**
136
+ * Check a list of packed files against the gate.
137
+ *
138
+ * @param {{ files: string[], dir: string, license?: string, root?: string }} input
139
+ * @returns {PackResult}
140
+ */
141
+ export function evaluatePack({ files, dir, license, root = ROOT }) {
142
+ /** @type {string[]} */
143
+ const problems = [];
144
+ const set = new Set(files);
145
+
146
+ for (const req of REQUIRED) {
147
+ if (!set.has(req)) problems.push(`missing required file: ${req}`);
148
+ }
149
+ if (license && license !== "UNLICENSED" && !set.has("LICENSE")) {
150
+ problems.push(`package.json license is ${license} but LICENSE is not packed`);
151
+ }
152
+ // Everything the stack materializer copies must be in the package.
153
+ for (const rel of listShippedFiles(root)) {
154
+ if (!set.has(rel)) problems.push(`stack asset is not packed (update package.json "files"): ${rel}`);
155
+ }
156
+ for (const f of files) {
157
+ for (const [re, label] of FORBIDDEN) {
158
+ if (re.test(f)) problems.push(`forbidden file packed (${label}): ${f}`);
159
+ }
160
+ }
161
+ for (const f of files) {
162
+ const abs = path.join(dir, f);
163
+ let buf;
164
+ try {
165
+ buf = fs.readFileSync(abs);
166
+ } catch {
167
+ problems.push(`cannot read packed file: ${f}`);
168
+ continue;
169
+ }
170
+ if (!isText(buf)) continue;
171
+ const lines = buf.toString("utf8").split("\n");
172
+ lines.forEach((line, i) => {
173
+ for (const m of line.matchAll(LEAK_RE)) {
174
+ if (ALLOWED_PATH_MENTIONS.some((re) => re.test(m[0]))) continue;
175
+ problems.push(`machine path "${m[0]}" in ${f}:${i + 1}`);
176
+ }
177
+ });
178
+ }
179
+
180
+ /** @type {Record<string, number>} */
181
+ const topLevel = {};
182
+ for (const f of files) {
183
+ const top = f.includes("/") ? `${f.split("/")[0]}/` : f;
184
+ topLevel[top] = (topLevel[top] || 0) + 1;
185
+ }
186
+ return { ok: problems.length === 0, count: files.length, topLevel, problems, files };
187
+ }
188
+
189
+ /**
190
+ * @param {{ root?: string, tarball?: string }} [opts]
191
+ * @returns {PackResult}
192
+ */
193
+ export function checkPack({ root = ROOT, tarball } = {}) {
194
+ const listing = tarball ? listFromTarball(tarball) : listFromDryRun(root);
195
+ try {
196
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
197
+ return evaluatePack({ files: listing.files, dir: listing.dir, license: pkg.license, root });
198
+ } finally {
199
+ listing.cleanup();
200
+ }
201
+ }
202
+
203
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
204
+ const argv = process.argv.slice(2);
205
+ const ti = argv.indexOf("--tarball");
206
+ const tarball = ti >= 0 ? argv[ti + 1] : undefined;
207
+ const result = checkPack({ tarball });
208
+ if (argv.includes("--json")) {
209
+ console.log(JSON.stringify(result, null, 2));
210
+ } else {
211
+ const dirs = Object.entries(result.topLevel)
212
+ .map(([k, v]) => `${k} ${v}`)
213
+ .join(", ");
214
+ console.log(`pack: ${result.count} files (${dirs})`);
215
+ for (const p of result.problems) console.log(`FAIL ${p}`);
216
+ if (result.ok) console.log("pack check passed");
217
+ }
218
+ process.exit(result.ok ? 0 : 1);
219
+ }
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ // compose.override.yaml belongs to the mutable stack root (PM_STACK_ROOT), not the code root.
8
+ const repo = path.resolve(
9
+ process.env.PM_STACK_ROOT || path.join(path.dirname(fileURLToPath(import.meta.url)), ".."),
10
+ );
11
+ const home = process.env.HOME || os.homedir();
12
+ const out = path.join(repo, "compose.override.yaml");
13
+
14
+ const dirs = [
15
+ ".ssh",
16
+ ".gnupg",
17
+ ".config/hindsight",
18
+ // Provider env files with API keys (mem0.env, ...). The indexer never reads it.
19
+ ".config/personal-memory",
20
+ ".config/notebooklm",
21
+ ".notebooklm",
22
+ ".aws",
23
+ ".config/gcloud",
24
+ ".config/gh",
25
+ ".config/glab-cli",
26
+ ".kube",
27
+ ".docker",
28
+ ".mozilla",
29
+ ".config/google-chrome",
30
+ ".config/chromium",
31
+ ".password-store",
32
+ ".local/share/keyrings",
33
+ ".config/fnox",
34
+ ".local/share/fnox",
35
+ ".secrets",
36
+ ];
37
+ const files = [
38
+ ".codex/auth.json",
39
+ ".claude/.credentials.json",
40
+ ".claude.json",
41
+ ".netrc",
42
+ ".git-credentials",
43
+ ".npmrc",
44
+ ".pypirc",
45
+ ];
46
+
47
+ const tmpfsLines = [];
48
+ for (const d of dirs) {
49
+ if (fs.existsSync(path.join(home, d)) && fs.statSync(path.join(home, d)).isDirectory()) {
50
+ tmpfsLines.push(` - "${path.join(home, d)}"`);
51
+ }
52
+ }
53
+ const volumeLines = [];
54
+ for (const f of files) {
55
+ if (fs.existsSync(path.join(home, f)) && fs.statSync(path.join(home, f)).isFile()) {
56
+ volumeLines.push(` - "/dev/null:${path.join(home, f)}:ro"`);
57
+ }
58
+ }
59
+ const userIndex = path.join(home, ".config/hindsight/index.yml");
60
+ const envLines = [];
61
+ if (fs.existsSync(userIndex)) {
62
+ volumeLines.push(` - "${userIndex}:/etc/hindsight/index.yml:ro"`);
63
+ envLines.push(" HINDSIGHT_INDEX_CONFIG: /etc/hindsight/index.yml");
64
+ }
65
+
66
+ const parts = [
67
+ "# Generated by scripts/gen-masks.mjs for this host. Do not commit.",
68
+ "services:",
69
+ " indexer:",
70
+ ];
71
+ if (tmpfsLines.length) {
72
+ parts.push(" tmpfs:", ...tmpfsLines);
73
+ }
74
+ if (volumeLines.length) {
75
+ parts.push(" volumes:", ...volumeLines);
76
+ }
77
+ if (envLines.length) {
78
+ parts.push(" environment:", ...envLines);
79
+ }
80
+ parts.push("");
81
+
82
+ fs.writeFileSync(out, parts.join("\n"));
83
+ console.log(`wrote ${out}`);
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+ exec node "${SCRIPT_DIR}/gen-masks.mjs"
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Promotion bar for experimental → supported: run contract tests; optional live Docker stacks.
4
+ * CI runs the Node tests only. Maintainers: MEM0_PROMOTION=1 COGNEE_PROMOTION=1 with stacks up.
5
+ */
6
+ import { spawnSync } from "node:child_process";
7
+ import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
+ const tests = [
12
+ "scripts/tests/test_memory_facade.mjs",
13
+ "scripts/tests/test_memory_portability.mjs",
14
+ "scripts/tests/test_compose_profiles.mjs",
15
+ "scripts/tests/test_coding_agents.mjs",
16
+ "scripts/tests/test_wiring_report.mjs",
17
+ "scripts/tests/test_install_coding_agent.mjs",
18
+ "scripts/tests/test_session_shim.mjs",
19
+ ];
20
+
21
+ let failed = 0;
22
+ for (const t of tests) {
23
+ const r = spawnSync(process.execPath, [path.join(root, t)], { stdio: "inherit" });
24
+ if (r.status !== 0) failed++;
25
+ }
26
+
27
+ if (process.env.MEM0_PROMOTION === "1") {
28
+ console.log("MEM0_PROMOTION=1: run live mem0 stack + wizard smoke manually (see providers/mem0/OPERATOR.md)");
29
+ }
30
+ if (process.env.COGNEE_PROMOTION === "1") {
31
+ console.log("COGNEE_PROMOTION=1: run live Cognee cognify+search manually (see docs/cognee.md)");
32
+ }
33
+
34
+ process.exit(failed ? 1 : 0);
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Non-interactive checkout bootstrap (CI, remote shells without a TTY).
3
+ # Prefer: npx openmem or cd checkout && npx . setup
4
+ set -euo pipefail
5
+ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ cd "${REPO}"
7
+
8
+ npm install --no-fund --no-audit
9
+
10
+ if [ -f "${HOME}/.config/hindsight/wizard.conf" ]; then
11
+ exec npx . setup --yes --no-smoke "$@"
12
+ fi
13
+
14
+ if [ -t 0 ] && [ -t 1 ]; then
15
+ exec npx . setup --no-smoke "$@"
16
+ fi
17
+
18
+ cat <<EOF
19
+ openmem checkout: ${REPO}
20
+
21
+ Run the wizard on a terminal (Docker, provider, indexing):
22
+
23
+ cd ${REPO}
24
+ mkdir -p ~/.config/personal-memory
25
+ cp config/openmem.toml.example ~/.config/personal-memory/config.toml
26
+ npx . setup
27
+
28
+ Or from npm (no clone): npx openmem
29
+ EOF
30
+ exit 0
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Write the stack .env (HOST_UID, HOST_GID and provider entries) and compose.override.yaml.
4
+ *
5
+ * PM_STACK_ROOT (default: this checkout) is the mutable stack directory; the provider
6
+ * comes from MEMORY_PROVIDER (default: hindsight) and is loaded from the read-only code
7
+ * root (PM_PACKAGE_ROOT, else this checkout). A provider contributes through optional
8
+ * exports of providers/<id>/wizard.mjs:
9
+ * prepareStackEnv({ repo, home, env }) -> { entries, drop, report }
10
+ * migrateEnv({ repo, home, env }) (for --migrate-env)
11
+ * setupNotices({ repo, home, env }) -> string[]
12
+ */
13
+ import fs from "node:fs";
14
+ import os from "node:os";
15
+ import path from "node:path";
16
+ import { spawnSync } from "node:child_process";
17
+ import { fileURLToPath, pathToFileURL } from "node:url";
18
+ import {
19
+ DEFAULT_PROVIDER_ID,
20
+ loadManifest,
21
+ } from "../wizard/lib/providers/registry.mjs";
22
+ import { wizardModulePath } from "../wizard/lib/provider-wizard/run-steps.mjs";
23
+
24
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
25
+ const packageRoot = path.resolve(process.env.PM_PACKAGE_ROOT || path.join(scriptDir, ".."));
26
+ const repo = path.resolve(process.env.PM_STACK_ROOT || packageRoot);
27
+ const home = process.env.HOME || os.homedir();
28
+ const envFile = path.join(repo, ".env");
29
+ const tmp = `${envFile}.tmp`;
30
+ const migrate = process.argv.includes("--migrate-env");
31
+ process.env.PM_PACKAGE_ROOT = packageRoot;
32
+
33
+ const providerId = process.env.MEMORY_PROVIDER || DEFAULT_PROVIDER_ID;
34
+ const manifest = loadManifest(providerId);
35
+ if (!manifest) {
36
+ console.error(`unknown memory provider '${providerId}' (see providers/*/manifest.json)`);
37
+ process.exit(1);
38
+ }
39
+ const modFile = wizardModulePath(manifest);
40
+ const provider = modFile && fs.existsSync(modFile) ? await import(pathToFileURL(modFile).href) : {};
41
+ const opts = { repo, home, env: process.env };
42
+
43
+ /** @type {{ entries: [string, string][], drop: string[], report: string[] }} */
44
+ let spec = { entries: [], drop: [], report: [] };
45
+ try {
46
+ fs.mkdirSync(path.join(home, ".config/hindsight"), { recursive: true, mode: 0o700 });
47
+ if (typeof provider.prepareStackEnv === "function") {
48
+ spec = { ...spec, ...(await provider.prepareStackEnv(opts)) };
49
+ }
50
+ } catch (e) {
51
+ console.error(/** @type {Error} */ (e).message);
52
+ process.exit(1);
53
+ }
54
+
55
+ // Context-proxy settings from config.toml ([context_proxy], [compaction]) for compose; keys the
56
+ // config leaves unset keep any hand-written .env line. Shell env still overrides at `compose up`.
57
+ /** @type {Record<string, string>} */
58
+ let proxyEnv = {};
59
+ try {
60
+ // Imported here: pm-config needs npm dependencies, and setup.mjs also runs from bare checkouts.
61
+ const { contextProxyStackEnvFromPmConfig, loadPmConfig } = await import("../wizard/lib/pm-config.mjs");
62
+ proxyEnv = contextProxyStackEnvFromPmConfig(loadPmConfig(home, repo).merged);
63
+ } catch (e) {
64
+ console.warn(`skipped context-proxy settings: ${/** @type {Error} */ (e).message}`);
65
+ }
66
+
67
+ const dropRe = new RegExp(`^(${["HOST_UID", "HOST_GID", ...spec.drop, ...Object.keys(proxyEnv)].join("|")})=`);
68
+
69
+ const kept = [];
70
+ if (fs.existsSync(envFile)) {
71
+ for (const line of fs.readFileSync(envFile, "utf8").split("\n")) {
72
+ if (line && !dropRe.test(line)) kept.push(line);
73
+ }
74
+ }
75
+ const uid = os.userInfo().uid;
76
+ const gid = os.userInfo().gid;
77
+ kept.push(`HOST_UID=${uid}`, `HOST_GID=${gid}`);
78
+ for (const [key, value] of spec.entries) kept.push(`${key}=${value}`);
79
+ for (const [key, value] of Object.entries(proxyEnv)) kept.push(`${key}=${value}`);
80
+ fs.mkdirSync(repo, { recursive: true });
81
+ fs.writeFileSync(tmp, `${kept.filter(Boolean).join("\n")}\n`, { mode: 0o644 });
82
+ fs.renameSync(tmp, envFile);
83
+ console.log(`wrote ${envFile}: ${[`HOST_UID=${uid}`, `HOST_GID=${gid}`, ...spec.report].join(" ")}`);
84
+
85
+ const gen = spawnSync(process.execPath, [path.join(scriptDir, "gen-masks.mjs")], {
86
+ stdio: "inherit",
87
+ env: { ...process.env, PM_STACK_ROOT: repo },
88
+ });
89
+ if (gen.status !== 0) process.exit(gen.status ?? 1);
90
+
91
+ if (migrate) {
92
+ if (typeof provider.migrateEnv !== "function") {
93
+ console.error(`provider ${providerId} has no --migrate-env step`);
94
+ process.exit(1);
95
+ }
96
+ try {
97
+ await provider.migrateEnv(opts);
98
+ } catch (e) {
99
+ console.error(/** @type {Error} */ (e).message);
100
+ process.exit(1);
101
+ }
102
+ process.exit(0);
103
+ }
104
+
105
+ if (typeof provider.setupNotices === "function") {
106
+ for (const line of await provider.setupNotices(opts)) console.log(line);
107
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Cross-platform wrapper; logic lives in scripts/setup.mjs.
3
+ set -euo pipefail
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ exec node "${SCRIPT_DIR}/setup.mjs" "$@"
@@ -0,0 +1,45 @@
1
+ #!/bin/sh
2
+ # openmem: test pack/install and optional GitHub release (clean subshell).
3
+ set -eu
4
+ ROOT=$(cd "$(dirname "$0")/.." && pwd)
5
+ LOG="${RUN_LOG:-/tmp/openmem-spin-$(date +%Y%m%d-%H%M%S).log}"
6
+
7
+ usage() {
8
+ cat <<EOF
9
+ Usage: sh scripts/spin-up.sh <target>
10
+
11
+ test npm test (pack + install + coding-agent wiring)
12
+ release merge wip/revamp -> main if needed, run Prepare release workflow
13
+ help
14
+
15
+ Log: \$RUN_LOG ($LOG)
16
+ EOF
17
+ }
18
+
19
+ run() {
20
+ (cd "$ROOT" && "$@") 2>&1 | tee "$LOG"
21
+ echo "log: $LOG"
22
+ }
23
+
24
+ target=${1:-help}
25
+ case $target in
26
+ help|-h|--help) usage; exit 0 ;;
27
+ test) run npm test ;;
28
+ release)
29
+ run git fetch origin main wip/revamp
30
+ branch=$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)
31
+ if [ "$branch" != main ]; then
32
+ git -C "$ROOT" checkout main
33
+ git -C "$ROOT" pull origin main
34
+ git -C "$ROOT" merge --no-edit origin/wip/revamp
35
+ git -C "$ROOT" push origin main
36
+ fi
37
+ gh workflow run "Prepare release" --repo bcmyguest/openmem -f dry_run=false
38
+ echo "Prepare release dispatched; watch: gh run list --repo bcmyguest/openmem --workflow Release"
39
+ ;;
40
+ *)
41
+ echo "unknown: $target" >&2
42
+ usage >&2
43
+ exit 2
44
+ ;;
45
+ esac
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Deprecated: use ./setup or node wizard/bin/setup.mjs. Kept for scripts that still invoke wizard.sh.
3
+ set -euo pipefail
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ exec node "${SCRIPT_DIR}/../wizard/bin/setup.mjs" "$@"
package/server.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.bcmyguest/openmem",
4
+ "title": "OpenMem",
5
+ "description": "Memory interface for coding agents \u2014 stable memory_* tools over Hindsight, mem0, or Cognee.",
6
+ "version": "0.6.4",
7
+ "packages": [
8
+ {
9
+ "registryType": "npm",
10
+ "identifier": "@bcmyguest/openmem",
11
+ "version": "0.6.4",
12
+ "transport": {
13
+ "type": "stdio"
14
+ },
15
+ "packageArguments": [],
16
+ "environmentVariables": [
17
+ {
18
+ "name": "MEMORY_PROVIDER",
19
+ "description": "Active backend: hindsight (default), mem0, or cognee",
20
+ "isRequired": false
21
+ },
22
+ {
23
+ "name": "PM_STACK_ROOT",
24
+ "description": "OpenMem stack directory (compose project, .env)",
25
+ "isRequired": false
26
+ }
27
+ ]
28
+ }
29
+ ],
30
+ "repository": {
31
+ "url": "https://github.com/bcmyguest/openmem",
32
+ "source": "github"
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ # NotebookLM -> Hindsight sync (opt-in compose profile "notebooklm"). No browser: login runs on the host.
2
+ FROM python:3.12-slim
3
+
4
+ # Pinned: the unofficial notebooklm-py client changes without notice. google-auth and requests serve the optional
5
+ # enterprise backend only.
6
+ # [markdown] pulls markdownify — sources.get_fulltext(..., output_format="markdown") needs it.
7
+ RUN pip install --no-cache-dir 'notebooklm-py[markdown]==0.8.5' pyyaml==6.0.3 google-auth==2.62.0 requests==2.34.2
8
+
9
+ COPY sync/notebooklm_sync.py sync/memory_backend.py /opt/notebooklm/
10
+ COPY indexer/sinks/ /opt/indexer/sinks/
11
+ # Shipped default config. The per-machine override is mounted at /etc/notebooklm/override.yml by compose.
12
+ COPY config/notebooklm.yml /opt/config/notebooklm.yml
13
+ RUN chmod 0755 /opt/notebooklm/notebooklm_sync.py
14
+
15
+ # The compose user is the host's UID/GID, which has no passwd entry here. HOME must be writable and must not be the
16
+ # host's home. The state directory is 0777 so a fresh named volume (which copies the image's mode) is writable.
17
+ RUN mkdir -p /data/state /data/notebooklm /tmp/nlm-home && chmod 0777 /data/state /data/notebooklm /tmp/nlm-home
18
+ ENV PYTHONUNBUFFERED=1 \
19
+ PYTHONNOUSERSITE=1 \
20
+ PYTHONPATH=/opt/indexer \
21
+ HOME=/tmp/nlm-home \
22
+ NOTEBOOKLM_HOME=/data/notebooklm \
23
+ NOTEBOOKLM_STATE=/data/state/state.json \
24
+ HINDSIGHT_URL=http://hindsight:8888 \
25
+ MEMORY_SINK=hindsight
26
+
27
+ CMD ["python3", "/opt/notebooklm/notebooklm_sync.py"]