superlocalmemory 3.6.23 → 3.7.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 (301) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +271 -71
  3. package/bin/slm-npm +43 -89
  4. package/ide/configs/antigravity-mcp.json +2 -2
  5. package/ide/configs/chatgpt-desktop-mcp.json +1 -1
  6. package/ide/configs/claude-desktop-mcp.json +2 -2
  7. package/ide/configs/windsurf-mcp.json +2 -2
  8. package/ide/hooks/context-hook.js +6 -2
  9. package/ide/hooks/post-recall-hook.js +7 -3
  10. package/ide/hooks/tool-event-hook.sh +2 -1
  11. package/package.json +18 -10
  12. package/plugin/.claude-plugin/plugin.json +1 -1
  13. package/plugin/_GENERATED.md +1 -1
  14. package/plugin/agents/slm-memory-advisor.md +1 -1
  15. package/plugin/requirements.txt +1 -1
  16. package/plugin/skills/slm-session/SKILL.md +1 -1
  17. package/plugin-src/rules/AGENTS.md +1 -1
  18. package/pyproject.toml +40 -8
  19. package/scripts/postinstall-interactive.js +17 -94
  20. package/scripts/postinstall.js +185 -258
  21. package/scripts/preuninstall.js +9 -50
  22. package/src/superlocalmemory/__init__.py +2 -2
  23. package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
  24. package/src/superlocalmemory/attribution/signer.py +34 -19
  25. package/src/superlocalmemory/attribution/watermark.py +1 -1
  26. package/src/superlocalmemory/cli/_lazy_init.py +3 -5
  27. package/src/superlocalmemory/cli/commands.py +453 -191
  28. package/src/superlocalmemory/cli/context_commands.py +5 -4
  29. package/src/superlocalmemory/cli/daemon.py +282 -187
  30. package/src/superlocalmemory/cli/db_migrate.py +3 -1
  31. package/src/superlocalmemory/cli/diagnostics_cmd.py +28 -0
  32. package/src/superlocalmemory/cli/evidence_cmd.py +103 -0
  33. package/src/superlocalmemory/cli/ingest_cmd.py +7 -3
  34. package/src/superlocalmemory/cli/main.py +128 -31
  35. package/src/superlocalmemory/cli/pending_store.py +54 -38
  36. package/src/superlocalmemory/cli/scale_engine_cmd.py +37 -0
  37. package/src/superlocalmemory/cli/service_installer.py +57 -52
  38. package/src/superlocalmemory/cli/setup_wizard.py +142 -88
  39. package/src/superlocalmemory/cli/version_banner.py +2 -1
  40. package/src/superlocalmemory/code_graph/config.py +3 -1
  41. package/src/superlocalmemory/core/backend_orchestrator.py +81 -21
  42. package/src/superlocalmemory/core/config.py +65 -20
  43. package/src/superlocalmemory/core/consolidation_engine.py +9 -7
  44. package/src/superlocalmemory/core/context_cache.py +56 -8
  45. package/src/superlocalmemory/core/derivation_lineage.py +246 -0
  46. package/src/superlocalmemory/core/embedding_worker.py +32 -20
  47. package/src/superlocalmemory/core/embeddings.py +54 -18
  48. package/src/superlocalmemory/core/engine.py +150 -104
  49. package/src/superlocalmemory/core/engine_ingestion.py +513 -0
  50. package/src/superlocalmemory/core/engine_wiring.py +2 -0
  51. package/src/superlocalmemory/core/evidence_bundle.py +526 -0
  52. package/src/superlocalmemory/core/fact_consolidator.py +5 -11
  53. package/src/superlocalmemory/core/graph_analyzer.py +2 -2
  54. package/src/superlocalmemory/core/health_monitor.py +4 -2
  55. package/src/superlocalmemory/core/ingestion_command.py +636 -0
  56. package/src/superlocalmemory/core/injection.py +69 -18
  57. package/src/superlocalmemory/core/lifecycle_state.py +153 -0
  58. package/src/superlocalmemory/core/maintenance.py +1 -1
  59. package/src/superlocalmemory/core/maintenance_scheduler.py +51 -35
  60. package/src/superlocalmemory/core/mutations.py +143 -0
  61. package/src/superlocalmemory/core/platform_utils.py +7 -4
  62. package/src/superlocalmemory/core/ram_lock.py +16 -5
  63. package/src/superlocalmemory/core/rate_limit.py +1 -1
  64. package/src/superlocalmemory/core/recall_pipeline.py +60 -101
  65. package/src/superlocalmemory/core/recall_worker.py +76 -59
  66. package/src/superlocalmemory/core/registry.py +1 -1
  67. package/src/superlocalmemory/core/scale_engine.py +293 -0
  68. package/src/superlocalmemory/core/score_contract.py +62 -0
  69. package/src/superlocalmemory/core/security_primitives.py +3 -1
  70. package/src/superlocalmemory/core/slm_disabled.py +3 -5
  71. package/src/superlocalmemory/core/store_pipeline.py +172 -40
  72. package/src/superlocalmemory/core/tier_manager.py +32 -20
  73. package/src/superlocalmemory/core/worker_pool.py +13 -4
  74. package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
  75. package/src/superlocalmemory/dynamics/eap_scheduler.py +10 -3
  76. package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
  77. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
  78. package/src/superlocalmemory/encoding/auto_linker.py +1 -1
  79. package/src/superlocalmemory/encoding/cognitive_consolidator.py +7 -16
  80. package/src/superlocalmemory/encoding/consolidator.py +22 -5
  81. package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
  82. package/src/superlocalmemory/encoding/foresight.py +2 -0
  83. package/src/superlocalmemory/encoding/graph_builder.py +1 -1
  84. package/src/superlocalmemory/encoding/temporal_parser.py +2 -0
  85. package/src/superlocalmemory/evaluation/__init__.py +13 -0
  86. package/src/superlocalmemory/evaluation/calibration.py +308 -0
  87. package/src/superlocalmemory/evolution/skill_evolver.py +2 -1
  88. package/src/superlocalmemory/graph/cozo_backend.py +256 -23
  89. package/src/superlocalmemory/hooks/_outcome_common.py +21 -11
  90. package/src/superlocalmemory/hooks/antigravity_adapter.py +10 -31
  91. package/src/superlocalmemory/hooks/auto_invoker.py +25 -27
  92. package/src/superlocalmemory/hooks/auto_recall.py +31 -6
  93. package/src/superlocalmemory/hooks/auto_recall_hook.py +13 -33
  94. package/src/superlocalmemory/hooks/before_web_hook.py +9 -7
  95. package/src/superlocalmemory/hooks/claude_code_hooks.py +123 -35
  96. package/src/superlocalmemory/hooks/codex_assets.py +59 -0
  97. package/src/superlocalmemory/hooks/codex_hooks.py +186 -0
  98. package/src/superlocalmemory/hooks/context_payload.py +1 -1
  99. package/src/superlocalmemory/hooks/copilot_adapter.py +9 -24
  100. package/src/superlocalmemory/hooks/cursor_adapter.py +10 -32
  101. package/src/superlocalmemory/hooks/hook_daemon.py +4 -2
  102. package/src/superlocalmemory/hooks/hook_handlers.py +219 -32
  103. package/src/superlocalmemory/hooks/memory_protocol.py +5 -3
  104. package/src/superlocalmemory/hooks/post_tool_async_hook.py +4 -2
  105. package/src/superlocalmemory/hooks/session_registry.py +15 -8
  106. package/src/superlocalmemory/hooks/stop_outcome_hook.py +10 -6
  107. package/src/superlocalmemory/hooks/topic_shift_hook.py +42 -12
  108. package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -14
  109. package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +19 -11
  110. package/src/superlocalmemory/infra/auth_middleware.py +38 -5
  111. package/src/superlocalmemory/infra/backup.py +7 -5
  112. package/src/superlocalmemory/infra/cloud_backup.py +18 -8
  113. package/src/superlocalmemory/infra/daemon_identity.py +248 -0
  114. package/src/superlocalmemory/infra/data_root.py +199 -0
  115. package/src/superlocalmemory/infra/event_bus.py +3 -1
  116. package/src/superlocalmemory/infra/local_diagnostics.py +327 -0
  117. package/src/superlocalmemory/infra/process_reaper.py +23 -0
  118. package/src/superlocalmemory/ingestion/adapter_manager.py +27 -9
  119. package/src/superlocalmemory/ingestion/base_adapter.py +25 -31
  120. package/src/superlocalmemory/ingestion/calendar_adapter.py +13 -4
  121. package/src/superlocalmemory/ingestion/credentials.py +14 -7
  122. package/src/superlocalmemory/ingestion/gmail_adapter.py +13 -4
  123. package/src/superlocalmemory/ingestion/transcript_adapter.py +7 -2
  124. package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
  125. package/src/superlocalmemory/learning/ensemble.py +11 -0
  126. package/src/superlocalmemory/learning/entity_compiler.py +1 -1
  127. package/src/superlocalmemory/learning/feedback.py +1 -1
  128. package/src/superlocalmemory/learning/forgetting_scheduler.py +12 -7
  129. package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
  130. package/src/superlocalmemory/learning/ranker.py +4 -1
  131. package/src/superlocalmemory/learning/source_quality.py +1 -1
  132. package/src/superlocalmemory/learning/trigram_index.py +3 -2
  133. package/src/superlocalmemory/llm/backbone.py +1 -1
  134. package/src/superlocalmemory/math/ebbinghaus.py +1 -1
  135. package/src/superlocalmemory/math/fisher.py +1 -1
  136. package/src/superlocalmemory/math/fisher_quantized.py +1 -1
  137. package/src/superlocalmemory/math/hopfield.py +1 -1
  138. package/src/superlocalmemory/math/langevin.py +1 -1
  139. package/src/superlocalmemory/math/polar_quant.py +3 -4
  140. package/src/superlocalmemory/math/qjl.py +1 -1
  141. package/src/superlocalmemory/math/sheaf.py +1 -1
  142. package/src/superlocalmemory/math/turbo_quant.py +3 -2
  143. package/src/superlocalmemory/mcp/_daemon_proxy.py +12 -11
  144. package/src/superlocalmemory/mcp/_pool_adapter.py +27 -0
  145. package/src/superlocalmemory/mcp/http_transport.py +53 -0
  146. package/src/superlocalmemory/mcp/server.py +39 -13
  147. package/src/superlocalmemory/mcp/shared.py +69 -3
  148. package/src/superlocalmemory/mcp/tools_active.py +141 -31
  149. package/src/superlocalmemory/mcp/tools_core.py +128 -29
  150. package/src/superlocalmemory/mcp/tools_evolution.py +5 -7
  151. package/src/superlocalmemory/mcp/tools_learning.py +42 -2
  152. package/src/superlocalmemory/mcp/tools_mesh.py +7 -23
  153. package/src/superlocalmemory/mcp/tools_optimize.py +8 -1
  154. package/src/superlocalmemory/mcp/tools_v28.py +23 -2
  155. package/src/superlocalmemory/mcp/tools_v3.py +26 -1
  156. package/src/superlocalmemory/mcp/tools_v33.py +56 -17
  157. package/src/superlocalmemory/mesh/broker.py +2 -0
  158. package/src/superlocalmemory/mesh/remote_sync.py +50 -12
  159. package/src/superlocalmemory/optimize/cache/manager.py +77 -1
  160. package/src/superlocalmemory/optimize/cache/semantic.py +23 -3
  161. package/src/superlocalmemory/optimize/compress/ccr.py +4 -0
  162. package/src/superlocalmemory/optimize/compress/router.py +6 -1
  163. package/src/superlocalmemory/optimize/config/__init__.py +5 -0
  164. package/src/superlocalmemory/optimize/config/store.py +6 -4
  165. package/src/superlocalmemory/optimize/proxy/_helpers.py +15 -5
  166. package/src/superlocalmemory/optimize/proxy/capture.py +3 -2
  167. package/src/superlocalmemory/optimize/proxy/server.py +2 -2
  168. package/src/superlocalmemory/optimize/storage/db.py +12 -12
  169. package/src/superlocalmemory/retrieval/agentic.py +1 -1
  170. package/src/superlocalmemory/retrieval/ann_index.py +1 -1
  171. package/src/superlocalmemory/retrieval/bm25_channel.py +35 -11
  172. package/src/superlocalmemory/retrieval/bridge_discovery.py +73 -8
  173. package/src/superlocalmemory/retrieval/engine.py +169 -79
  174. package/src/superlocalmemory/retrieval/entity_channel.py +289 -67
  175. package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
  176. package/src/superlocalmemory/retrieval/fusion.py +1 -1
  177. package/src/superlocalmemory/retrieval/hopfield_channel.py +118 -30
  178. package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
  179. package/src/superlocalmemory/retrieval/quantization_aware_search.py +16 -10
  180. package/src/superlocalmemory/retrieval/reranker.py +56 -20
  181. package/src/superlocalmemory/retrieval/scope_policy.py +85 -0
  182. package/src/superlocalmemory/retrieval/semantic_channel.py +122 -14
  183. package/src/superlocalmemory/retrieval/spreading_activation.py +141 -25
  184. package/src/superlocalmemory/retrieval/strategy.py +1 -1
  185. package/src/superlocalmemory/retrieval/temporal_channel.py +30 -15
  186. package/src/superlocalmemory/retrieval/vector_store.py +1 -1
  187. package/src/superlocalmemory/server/api.py +10 -7
  188. package/src/superlocalmemory/server/bandit_loops.py +4 -2
  189. package/src/superlocalmemory/server/recall_serializer.py +24 -0
  190. package/src/superlocalmemory/server/route_mutations.py +84 -0
  191. package/src/superlocalmemory/server/routes/agents.py +8 -6
  192. package/src/superlocalmemory/server/routes/brain.py +14 -12
  193. package/src/superlocalmemory/server/routes/chat.py +29 -12
  194. package/src/superlocalmemory/server/routes/data_io.py +55 -24
  195. package/src/superlocalmemory/server/routes/helpers.py +8 -63
  196. package/src/superlocalmemory/server/routes/ingest.py +53 -36
  197. package/src/superlocalmemory/server/routes/memories.py +104 -43
  198. package/src/superlocalmemory/server/routes/mesh.py +31 -0
  199. package/src/superlocalmemory/server/routes/profiles.py +26 -4
  200. package/src/superlocalmemory/server/routes/tiers.py +43 -11
  201. package/src/superlocalmemory/server/routes/timeline.py +5 -1
  202. package/src/superlocalmemory/server/routes/v3_api.py +76 -21
  203. package/src/superlocalmemory/server/security_middleware.py +1 -1
  204. package/src/superlocalmemory/server/unified_daemon.py +680 -293
  205. package/src/superlocalmemory/server/write_identity.py +147 -0
  206. package/src/superlocalmemory/storage/access_log.py +4 -3
  207. package/src/superlocalmemory/storage/database.py +118 -25
  208. package/src/superlocalmemory/storage/migration_runner.py +84 -1
  209. package/src/superlocalmemory/storage/migration_v33.py +1 -1
  210. package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +6 -60
  211. package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +120 -0
  212. package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +54 -0
  213. package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +52 -0
  214. package/src/superlocalmemory/storage/migrations/__init__.py +5 -0
  215. package/src/superlocalmemory/storage/models.py +16 -0
  216. package/src/superlocalmemory/storage/quantized_store.py +20 -3
  217. package/src/superlocalmemory/storage/v2_migrator.py +5 -3
  218. package/src/superlocalmemory/ui/favicon.svg +5 -0
  219. package/src/superlocalmemory/ui/index.html +1 -0
  220. package/src/superlocalmemory/ui/js/compliance.js +1 -1
  221. package/src/superlocalmemory/ui/js/core.js +49 -8
  222. package/src/superlocalmemory/ui/js/dashboard.js +23 -2
  223. package/src/superlocalmemory/ui/js/feedback.js +1 -1
  224. package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
  225. package/src/superlocalmemory/ui/js/graph-ui.js +1 -1
  226. package/src/superlocalmemory/ui/js/lifecycle.js +1 -1
  227. package/src/superlocalmemory/ui/js/ng-mesh.js +15 -49
  228. package/src/superlocalmemory/ui/js/settings.js +4 -2
  229. package/src/superlocalmemory/vector/lancedb_backend.py +57 -9
  230. package/bin/slm +0 -59
  231. package/bin/slm.bat +0 -77
  232. package/bin/slm.cmd +0 -5
  233. package/ide/integrations/langchain/README.md +0 -106
  234. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +0 -9
  235. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +0 -201
  236. package/ide/integrations/langchain/pyproject.toml +0 -38
  237. package/ide/integrations/langchain/tests/__init__.py +0 -3
  238. package/ide/integrations/langchain/tests/test_chat_message_history.py +0 -215
  239. package/ide/integrations/langchain/tests/test_security.py +0 -117
  240. package/ide/integrations/llamaindex/README.md +0 -81
  241. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +0 -9
  242. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +0 -316
  243. package/ide/integrations/llamaindex/pyproject.toml +0 -43
  244. package/ide/integrations/llamaindex/tests/__init__.py +0 -3
  245. package/ide/integrations/llamaindex/tests/test_chat_store.py +0 -294
  246. package/ide/integrations/llamaindex/tests/test_security.py +0 -241
  247. package/plugin-src/.mcp.json +0 -12
  248. package/plugin-src/agents/slm-memory-advisor.md +0 -44
  249. package/plugin-src/agents/slm-optimize-advisor.md +0 -38
  250. package/plugin-src/hooks/.gitkeep +0 -0
  251. package/plugin-src/hooks/hooks.json +0 -23
  252. package/plugin-src/manifest.json +0 -25
  253. package/plugin-src/requirements.txt +0 -1
  254. package/plugin-src/rules/CLAUDE.md.fragment +0 -44
  255. package/plugin-src/scripts/ensure-venv.bat +0 -122
  256. package/plugin-src/scripts/ensure-venv.sh +0 -105
  257. package/plugin-src/scripts/slm-launch +0 -15
  258. package/plugin-src/scripts/slm-launch.bat +0 -17
  259. package/plugin-src/settings.json +0 -16
  260. package/plugin-src/skills/slm-cache/SKILL.md +0 -140
  261. package/plugin-src/skills/slm-compress/SKILL.md +0 -143
  262. package/plugin-src/skills/slm-graph/SKILL.md +0 -300
  263. package/plugin-src/skills/slm-recall/SKILL.md +0 -204
  264. package/plugin-src/skills/slm-remember/SKILL.md +0 -194
  265. package/plugin-src/skills/slm-session/SKILL.md +0 -207
  266. package/plugin-src/skills/slm-status/SKILL.md +0 -149
  267. package/scripts/__tests__/build-plugin.test.mjs +0 -613
  268. package/scripts/_savings_math.py +0 -270
  269. package/scripts/build-dmg.sh +0 -417
  270. package/scripts/build-plugin.js +0 -742
  271. package/scripts/build-slm-hook.ps1 +0 -40
  272. package/scripts/build-slm-hook.sh +0 -45
  273. package/scripts/build_entry.py +0 -452
  274. package/scripts/ci/stage5b_gate.sh +0 -50
  275. package/scripts/dogfood_savings.py +0 -490
  276. package/scripts/generate-thumbnails.py +0 -218
  277. package/scripts/install-skills.ps1 +0 -4
  278. package/scripts/install-skills.sh +0 -5
  279. package/scripts/install.ps1 +0 -701
  280. package/scripts/install.sh +0 -1015
  281. package/scripts/postinstall_binary.js +0 -287
  282. package/scripts/prepack.js +0 -33
  283. package/scripts/release_manifest.py +0 -273
  284. package/scripts/slm-hook.spec +0 -56
  285. package/scripts/start-dashboard.ps1 +0 -52
  286. package/scripts/start-dashboard.sh +0 -41
  287. package/scripts/sync-wiki.ps1 +0 -127
  288. package/scripts/sync-wiki.sh +0 -82
  289. package/scripts/test-dmg.sh +0 -161
  290. package/scripts/test-npm-package.ps1 +0 -252
  291. package/scripts/test-npm-package.sh +0 -207
  292. package/scripts/verify-install.ps1 +0 -294
  293. package/scripts/verify-install.sh +0 -266
  294. package/scripts/verify-v27.ps1 +0 -301
  295. package/scripts/verify-v27.sh +0 -233
  296. package/src/superlocalmemory.egg-info/PKG-INFO +0 -516
  297. package/src/superlocalmemory.egg-info/SOURCES.txt +0 -529
  298. package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
  299. package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
  300. package/src/superlocalmemory.egg-info/requires.txt +0 -71
  301. package/src/superlocalmemory.egg-info/top_level.txt +0 -1
@@ -1,287 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * SuperLocalMemory v3.4.21 — slm-hook binary fetcher.
4
- *
5
- * LLD reference: .backup/active-brain/lld/LLD-06-windows-binary-and-legacy-migration.md §6.2
6
- *
7
- * Fails open: any network / SHA / unpack failure logs a warning and
8
- * exits 0. The dispatcher (bin/slm) falls back to the Python path so
9
- * the hook still works — just slower on Windows.
10
- *
11
- * Stdlib-only (node:https, node:crypto, node:fs, node:path, node:os).
12
- */
13
- 'use strict';
14
-
15
- const fs = require('node:fs');
16
- const fsp = require('node:fs/promises');
17
- const path = require('node:path');
18
- const os = require('node:os');
19
- const crypto = require('node:crypto');
20
- const https = require('node:https');
21
-
22
- const SLM_HOME = path.join(os.homedir(), '.superlocalmemory');
23
- const BIN_DIR = path.join(SLM_HOME, 'bin');
24
- const MANIFEST_BASE_URL = process.env.SLM_MANIFEST_BASE_URL ||
25
- 'https://github.com/qualixar/superlocalmemory/releases/download';
26
-
27
- // Map Node os.platform() / os.arch() -> canonical manifest tuple.
28
- const PLATFORM_MAP = {
29
- 'darwin': 'macos',
30
- 'linux': 'linux',
31
- 'win32': 'windows',
32
- };
33
- const ARCH_MAP = {
34
- 'arm64': 'arm64',
35
- 'x64': 'x86_64',
36
- 'x86_64': 'x86_64',
37
- };
38
-
39
- function canonicalPlatformArch(platform, arch) {
40
- const p = PLATFORM_MAP[platform] || null;
41
- const a = ARCH_MAP[arch] || null;
42
- return (p && a) ? { platform: p, arch: a } : null;
43
- }
44
-
45
- function logInfo(msg) { console.log(`[slm-hook] ${msg}`); }
46
- function logWarn(msg) { console.warn(`[slm-hook] ${msg}`); }
47
-
48
- // S8-SEC-03: redirect host allow-list. Previously any 3xx ``Location``
49
- // header would be followed blindly, so a compromised upstream (DNS
50
- // hijack, typosquat in the package registry) could redirect the postinstall
51
- // download to an attacker-controlled host and substitute the hook binary.
52
- // We only follow redirects back to GitHub's own release CDN.
53
- const ALLOWED_REDIRECT_HOSTS = new Set([
54
- 'github.com',
55
- 'api.github.com',
56
- 'codeload.github.com',
57
- 'objects.githubusercontent.com',
58
- 'raw.githubusercontent.com',
59
- 'release-assets.githubusercontent.com',
60
- ]);
61
-
62
- function redirectIsAllowed(rawLocation) {
63
- if (typeof rawLocation !== 'string' || rawLocation.length === 0) return false;
64
- try {
65
- const parsed = new URL(rawLocation);
66
- if (parsed.protocol !== 'https:') return false;
67
- return ALLOWED_REDIRECT_HOSTS.has(parsed.hostname);
68
- } catch (_err) {
69
- return false;
70
- }
71
- }
72
-
73
- function fetchJson(url) {
74
- return new Promise((resolve, reject) => {
75
- const req = https.get(url, (res) => {
76
- if (res.statusCode && res.statusCode >= 300 &&
77
- res.statusCode < 400 && res.headers.location) {
78
- if (!redirectIsAllowed(res.headers.location)) {
79
- reject(new Error('redirect host not allow-listed'));
80
- return;
81
- }
82
- // Single redirect hop — GitHub releases use redirects.
83
- fetchJson(res.headers.location).then(resolve, reject);
84
- return;
85
- }
86
- if (res.statusCode !== 200) {
87
- reject(new Error(`HTTP ${res.statusCode} for ${url}`));
88
- return;
89
- }
90
- const chunks = [];
91
- res.on('data', (c) => chunks.push(c));
92
- res.on('end', () => {
93
- try {
94
- resolve(JSON.parse(Buffer.concat(chunks).toString('utf8')));
95
- } catch (err) {
96
- reject(err);
97
- }
98
- });
99
- });
100
- req.on('error', reject);
101
- req.setTimeout(15000, () => {
102
- req.destroy(new Error('timeout fetching manifest'));
103
- });
104
- });
105
- }
106
-
107
- function downloadFile(url, destPath) {
108
- return new Promise((resolve, reject) => {
109
- const out = fs.createWriteStream(destPath);
110
- const req = https.get(url, (res) => {
111
- if (res.statusCode && res.statusCode >= 300 &&
112
- res.statusCode < 400 && res.headers.location) {
113
- if (!redirectIsAllowed(res.headers.location)) {
114
- out.close();
115
- fs.unlink(destPath, () => {});
116
- reject(new Error('redirect host not allow-listed'));
117
- return;
118
- }
119
- out.close();
120
- fs.unlink(destPath, () => {
121
- downloadFile(res.headers.location, destPath)
122
- .then(resolve, reject);
123
- });
124
- return;
125
- }
126
- if (res.statusCode !== 200) {
127
- out.close();
128
- fs.unlink(destPath, () => {});
129
- reject(new Error(`HTTP ${res.statusCode} for ${url}`));
130
- return;
131
- }
132
- res.pipe(out);
133
- out.on('finish', () => out.close(resolve));
134
- });
135
- req.on('error', (err) => {
136
- out.close();
137
- fs.unlink(destPath, () => {});
138
- reject(err);
139
- });
140
- req.setTimeout(60000, () => {
141
- req.destroy(new Error('timeout downloading asset'));
142
- });
143
- });
144
- }
145
-
146
- async function sha256File(filePath) {
147
- const h = crypto.createHash('sha256');
148
- const stream = fs.createReadStream(filePath);
149
- return new Promise((resolve, reject) => {
150
- stream.on('data', (c) => h.update(c));
151
- stream.on('end', () => resolve(h.digest('hex')));
152
- stream.on('error', reject);
153
- });
154
- }
155
-
156
- function pickAsset(manifest, platform, arch) {
157
- if (!manifest || !Array.isArray(manifest.assets)) return null;
158
- // Prefer setup.exe on Windows (Inno Setup) when present, fall back
159
- // to plain archive.
160
- const matches = manifest.assets.filter(a =>
161
- a.platform === platform && a.arch === arch);
162
- if (matches.length === 0) return null;
163
- // Deterministic ordering: setup.exe > .zip > .tar.gz
164
- matches.sort((a, b) => {
165
- const rank = (n) => n.endsWith('setup.exe') ? 0 :
166
- n.endsWith('.zip') ? 1 : 2;
167
- return rank(a.name) - rank(b.name);
168
- });
169
- return matches[0];
170
- }
171
-
172
- async function main() {
173
- try {
174
- const pkgJson = require(path.join(__dirname, '..', 'package.json'));
175
- const version = pkgJson.version;
176
- const url = `${MANIFEST_BASE_URL}/v${version}/manifest.json`;
177
-
178
- const pa = canonicalPlatformArch(os.platform(), os.arch());
179
- if (!pa) {
180
- logWarn(`unsupported platform ${os.platform()}/${os.arch()}; ` +
181
- 'Python fallback');
182
- return 0;
183
- }
184
-
185
- let manifest;
186
- try {
187
- manifest = await fetchJson(url);
188
- } catch (err) {
189
- logWarn(`manifest fetch failed: ${err.message}; Python fallback`);
190
- return 0;
191
- }
192
-
193
- const asset = pickAsset(manifest, pa.platform, pa.arch);
194
- if (!asset) {
195
- logWarn(`no prebuilt binary for ${pa.platform}/${pa.arch}; ` +
196
- 'Python fallback');
197
- return 0;
198
- }
199
-
200
- // S8-SEC-01 fix: the manifest is remote-attacker-controllable (even
201
- // when minisigned, the signer is a release process we don't fully
202
- // trust at the per-asset level). Reject any ``asset.name`` that
203
- // contains a path separator, traversal segments, or is empty —
204
- // and re-derive the safe filename from basename before joining.
205
- // Without this, a poisoned manifest can write arbitrary paths
206
- // (e.g., "../../.ssh/authorized_keys") even though SHA-256 validates
207
- // the bytes, because SHA only binds the content to the claimed name.
208
- const rawName = typeof asset.name === 'string' ? asset.name : '';
209
- if (!rawName
210
- || rawName.includes('/')
211
- || rawName.includes('\\')
212
- || rawName.includes('\0')
213
- || rawName === '.'
214
- || rawName === '..'
215
- || rawName.startsWith('.')) {
216
- logWarn(`rejected asset name (path-traversal guard): ` +
217
- `${JSON.stringify(rawName)}; Python fallback`);
218
- return 0;
219
- }
220
- const safeName = path.basename(rawName);
221
- if (safeName !== rawName) {
222
- logWarn(`asset name changed under basename sanitisation: ` +
223
- `${JSON.stringify(rawName)} -> ${JSON.stringify(safeName)}; ` +
224
- 'Python fallback');
225
- return 0;
226
- }
227
-
228
- await fsp.mkdir(BIN_DIR, { recursive: true });
229
- const tmpName = `${safeName}.part`;
230
- const tmpPath = path.join(BIN_DIR, tmpName);
231
- try {
232
- await downloadFile(asset.url, tmpPath);
233
- } catch (err) {
234
- logWarn(`download failed: ${err.message}; Python fallback`);
235
- await fsp.unlink(tmpPath).catch(() => {});
236
- return 0;
237
- }
238
-
239
- const actual = await sha256File(tmpPath);
240
- if (actual !== asset.sha256) {
241
- await fsp.unlink(tmpPath).catch(() => {});
242
- logWarn(`SHA-256 mismatch for ${asset.name}: expected ` +
243
- `${asset.sha256}, got ${actual}; Python fallback`);
244
- return 0;
245
- }
246
-
247
- const finalPath = path.join(BIN_DIR, safeName);
248
- // Belt-and-suspenders: after resolving the final path, assert it
249
- // sits under BIN_DIR. ``path.resolve`` normalises any '..' that
250
- // could have slipped through; if the result escapes BIN_DIR
251
- // (which it shouldn't after basename sanitisation, but it's
252
- // cheap insurance), refuse to install.
253
- const resolvedFinal = path.resolve(finalPath);
254
- const resolvedBin = path.resolve(BIN_DIR);
255
- if (!resolvedFinal.startsWith(resolvedBin + path.sep)
256
- && resolvedFinal !== resolvedBin) {
257
- await fsp.unlink(tmpPath).catch(() => {});
258
- logWarn(`path escape detected: ${resolvedFinal} not under ` +
259
- `${resolvedBin}; Python fallback`);
260
- return 0;
261
- }
262
- await fsp.rename(tmpPath, finalPath);
263
- logInfo(`✓ slm-hook asset fetched: ${asset.name} ` +
264
- `(${asset.signing})`);
265
- logInfo(` path: ${finalPath}`);
266
- logInfo(' Note: unpack step is platform-specific; ' +
267
- 'the installer runs it.');
268
- return 0;
269
- } catch (err) {
270
- logWarn(`unexpected error: ${err.message}; Python fallback`);
271
- return 0;
272
- }
273
- }
274
-
275
- // Exports for testing; direct execution runs main().
276
- module.exports = {
277
- canonicalPlatformArch,
278
- pickAsset,
279
- sha256File,
280
- MANIFEST_BASE_URL,
281
- main,
282
- };
283
-
284
- if (require.main === module) {
285
- main().then((code) => process.exit(code || 0),
286
- () => process.exit(0));
287
- }
@@ -1,33 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * SuperLocalMemory - Cross-platform prepack cleanup
4
- *
5
- * Removes __pycache__ directories and .pyc files before npm pack.
6
- * Works on Windows, macOS, and Linux.
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
-
12
- function removePycache(dir) {
13
- if (!fs.existsSync(dir)) return;
14
-
15
- const entries = fs.readdirSync(dir, { withFileTypes: true });
16
- for (const entry of entries) {
17
- const fullPath = path.join(dir, entry.name);
18
-
19
- if (entry.isDirectory()) {
20
- if (entry.name === '__pycache__' || entry.name === 'node_modules') {
21
- if (entry.name === '__pycache__') {
22
- fs.rmSync(fullPath, { recursive: true, force: true });
23
- }
24
- continue;
25
- }
26
- removePycache(fullPath);
27
- } else if (entry.isFile() && entry.name.endsWith('.pyc')) {
28
- fs.unlinkSync(fullPath);
29
- }
30
- }
31
- }
32
-
33
- removePycache(process.cwd());
@@ -1,273 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory v3.4.22 — LLD-06 §6.1
4
-
5
- """Generate the release ``manifest.json`` for ``slm-hook`` binaries.
6
-
7
- LLD reference: ``.backup/active-brain/lld/LLD-06-windows-binary-and-legacy-migration.md``
8
- Section 6.1 (Release manifest).
9
-
10
- Shape (per LLD-06 §6.1):
11
-
12
- {
13
- "version": "3.4.22",
14
- "released_at": "2026-04-17T00:00:00Z",
15
- "assets": [
16
- {
17
- "name": "slm-hook-macos-arm64.tar.gz",
18
- "url": "https://.../manifest.json",
19
- "size_bytes": 5312000,
20
- "sha256": "a7c3...",
21
- "signing": "apple-notarized"
22
- }, ...
23
- ],
24
- "manifest_sha256_self": "...",
25
- "manifest_signature": "minisign: placeholder"
26
- }
27
-
28
- Minisign signing is OUT of scope at Stage 6 — this module emits a
29
- placeholder and a ``manifest.minisig`` stub. Real signing is a CI job
30
- that layers on top (needs the private key in GitHub Secrets).
31
- """
32
- from __future__ import annotations
33
-
34
- import hashlib
35
- import json
36
- from dataclasses import dataclass, field
37
- from datetime import datetime, timezone
38
- from pathlib import Path
39
-
40
- # Signing envelope strings — one per expected platform. Must match the
41
- # values the dispatcher + postinstall check. Kept as constants so tests
42
- # can lock them.
43
- SIGNING_APPLE_NOTARIZED = "apple-notarized"
44
- SIGNING_AUTHENTICODE = "authenticode"
45
- SIGNING_UNSIGNED = "unsigned"
46
-
47
- # Default base URL used when the caller doesn't supply one. Kept in a
48
- # constant so tests can assert the release shape without a network call.
49
- DEFAULT_BASE_URL = (
50
- "https://github.com/qualixar/superlocalmemory/releases/download"
51
- )
52
-
53
- # Allowed platform/arch combos we ship binaries for. Any asset whose
54
- # filename does not encode one of these is rejected.
55
- _VALID_PLATFORMS: frozenset[tuple[str, str]] = frozenset({
56
- ("macos", "arm64"),
57
- ("macos", "x86_64"),
58
- ("linux", "x86_64"),
59
- ("linux", "arm64"),
60
- ("windows", "x86_64"),
61
- })
62
-
63
-
64
- @dataclass(frozen=True, slots=True)
65
- class AssetSpec:
66
- """Input spec for a single release artefact on disk."""
67
-
68
- path: Path
69
- platform: str
70
- arch: str
71
- signing: str
72
-
73
-
74
- @dataclass(frozen=True, slots=True)
75
- class Manifest:
76
- """In-memory representation of the manifest to be serialized."""
77
-
78
- version: str
79
- released_at: str
80
- assets: list[dict] = field(default_factory=list)
81
- manifest_sha256_self: str = ""
82
- manifest_signature: str = "minisign: placeholder"
83
-
84
-
85
- # ---------------------------------------------------------------------------
86
- # Helpers
87
- # ---------------------------------------------------------------------------
88
-
89
- def _sha256_file(path: Path) -> str:
90
- h = hashlib.sha256()
91
- with path.open("rb") as fh:
92
- for chunk in iter(lambda: fh.read(1024 * 1024), b""):
93
- h.update(chunk)
94
- return h.hexdigest()
95
-
96
-
97
- def _utcnow_iso() -> str:
98
- return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
99
-
100
-
101
- def _validate_platform(platform: str, arch: str) -> None:
102
- if (platform, arch) not in _VALID_PLATFORMS:
103
- raise ValueError(
104
- f"unsupported platform/arch: {platform}/{arch}"
105
- )
106
-
107
-
108
- def _validate_signing(signing: str) -> None:
109
- allowed = {SIGNING_APPLE_NOTARIZED, SIGNING_AUTHENTICODE,
110
- SIGNING_UNSIGNED}
111
- if signing not in allowed:
112
- raise ValueError(
113
- f"invalid signing value: {signing!r} (allowed: {allowed})"
114
- )
115
-
116
-
117
- def _asset_url(base_url: str, version: str, filename: str) -> str:
118
- base = base_url.rstrip("/")
119
- return f"{base}/v{version}/{filename}"
120
-
121
-
122
- # ---------------------------------------------------------------------------
123
- # Builders
124
- # ---------------------------------------------------------------------------
125
-
126
- def build_asset_entry(
127
- spec: AssetSpec,
128
- *,
129
- version: str,
130
- base_url: str,
131
- ) -> dict:
132
- """Compute a single manifest asset entry."""
133
- if not spec.path.is_file():
134
- raise FileNotFoundError(f"asset missing: {spec.path}")
135
- _validate_platform(spec.platform, spec.arch)
136
- _validate_signing(spec.signing)
137
-
138
- return {
139
- "name": spec.path.name,
140
- "url": _asset_url(base_url, version, spec.path.name),
141
- "size_bytes": spec.path.stat().st_size,
142
- "sha256": _sha256_file(spec.path),
143
- "signing": spec.signing,
144
- "platform": spec.platform,
145
- "arch": spec.arch,
146
- }
147
-
148
-
149
- def build_manifest(
150
- version: str,
151
- assets: list[AssetSpec],
152
- *,
153
- base_url: str = DEFAULT_BASE_URL,
154
- released_at: str | None = None,
155
- ) -> Manifest:
156
- """Assemble the full Manifest object (including self SHA)."""
157
- if not version:
158
- raise ValueError("version is required")
159
- if not assets:
160
- raise ValueError("at least one asset is required")
161
-
162
- entries: list[dict] = [
163
- build_asset_entry(spec, version=version, base_url=base_url)
164
- for spec in assets
165
- ]
166
- payload = {
167
- "version": version,
168
- "released_at": released_at or _utcnow_iso(),
169
- "assets": entries,
170
- }
171
- # Compute the manifest-self SHA over the canonical JSON WITHOUT the
172
- # self-SHA field (otherwise it would be recursive).
173
- serialized = json.dumps(payload, sort_keys=True,
174
- separators=(",", ":")).encode("utf-8")
175
- self_sha = hashlib.sha256(serialized).hexdigest()
176
-
177
- return Manifest(
178
- version=version,
179
- released_at=payload["released_at"],
180
- assets=entries,
181
- manifest_sha256_self=self_sha,
182
- manifest_signature="minisign: placeholder",
183
- )
184
-
185
-
186
- def serialize(manifest: Manifest) -> str:
187
- """Deterministic JSON serialization for on-disk storage."""
188
- doc = {
189
- "version": manifest.version,
190
- "released_at": manifest.released_at,
191
- "assets": manifest.assets,
192
- "manifest_sha256_self": manifest.manifest_sha256_self,
193
- "manifest_signature": manifest.manifest_signature,
194
- }
195
- return json.dumps(doc, indent=2, sort_keys=False) + "\n"
196
-
197
-
198
- def write_manifest(
199
- manifest: Manifest,
200
- dest_dir: Path,
201
- ) -> tuple[Path, Path]:
202
- """Write ``manifest.json`` + a stub ``manifest.minisig`` to ``dest_dir``.
203
-
204
- Returns the two paths (for caller convenience).
205
- """
206
- dest_dir.mkdir(parents=True, exist_ok=True)
207
- manifest_path = dest_dir / "manifest.json"
208
- sig_path = dest_dir / "manifest.minisig"
209
-
210
- manifest_path.write_text(serialize(manifest), encoding="utf-8")
211
- # Placeholder — real minisig produced by a separate CI job.
212
- sig_path.write_text(
213
- "untrusted comment: placeholder - real signature added by CI\n"
214
- f"manifest_sha256_self: {manifest.manifest_sha256_self}\n",
215
- encoding="utf-8",
216
- )
217
- return manifest_path, sig_path
218
-
219
-
220
- # ---------------------------------------------------------------------------
221
- # Validation helpers (used by tests + postinstall)
222
- # ---------------------------------------------------------------------------
223
-
224
- def validate_shape(doc: dict) -> list[str]:
225
- """Return a list of validation errors on a loaded manifest dict."""
226
- errors: list[str] = []
227
- for key in ("version", "released_at", "assets",
228
- "manifest_sha256_self", "manifest_signature"):
229
- if key not in doc:
230
- errors.append(f"missing top-level key: {key}")
231
-
232
- assets = doc.get("assets")
233
- if not isinstance(assets, list):
234
- errors.append("assets must be a list")
235
- return errors
236
- if not assets:
237
- errors.append("assets list is empty")
238
-
239
- for i, entry in enumerate(assets):
240
- if not isinstance(entry, dict):
241
- errors.append(f"asset[{i}] must be an object")
242
- continue
243
- for field_name in ("name", "url", "size_bytes",
244
- "sha256", "signing"):
245
- if field_name not in entry:
246
- errors.append(
247
- f"asset[{i}] missing field: {field_name}"
248
- )
249
- sha = entry.get("sha256", "")
250
- if not isinstance(sha, str) or len(sha) != 64:
251
- errors.append(f"asset[{i}] sha256 must be 64-char hex")
252
- signing = entry.get("signing")
253
- if signing is not None:
254
- try:
255
- _validate_signing(signing)
256
- except ValueError as exc:
257
- errors.append(f"asset[{i}] {exc}")
258
- return errors
259
-
260
-
261
- __all__ = (
262
- "AssetSpec",
263
- "Manifest",
264
- "SIGNING_APPLE_NOTARIZED",
265
- "SIGNING_AUTHENTICODE",
266
- "SIGNING_UNSIGNED",
267
- "DEFAULT_BASE_URL",
268
- "build_asset_entry",
269
- "build_manifest",
270
- "serialize",
271
- "write_manifest",
272
- "validate_shape",
273
- )
@@ -1,56 +0,0 @@
1
- # -*- mode: python ; coding: utf-8 -*-
2
- # LLD reference: .backup/active-brain/lld/LLD-06-windows-binary-and-legacy-migration.md §4.2
3
- # Mode: onedir (not onefile) — LLD-06 §4.1, verification claim 7.
4
- # console=False, upx=False, strip=True.
5
-
6
- block_cipher = None
7
-
8
- a = Analysis(
9
- ['../src/superlocalmemory/hook_binary_entry.py'],
10
- pathex=[],
11
- binaries=[],
12
- datas=[],
13
- hiddenimports=[],
14
- hookspath=[],
15
- runtime_hooks=[],
16
- excludes=[
17
- # Deny-list — anything imported here would blow up cold-start and size.
18
- 'torch', 'sentence_transformers', 'fastapi', 'uvicorn',
19
- 'numpy', 'scipy', 'lightgbm', 'onnxruntime', 'transformers',
20
- 'httpx', 'mcp', 'pydantic', 'lark',
21
- 'superlocalmemory.core.engine',
22
- 'superlocalmemory.core.embeddings',
23
- 'superlocalmemory.retrieval',
24
- 'superlocalmemory.learning',
25
- ],
26
- cipher=block_cipher,
27
- noarchive=False,
28
- )
29
- pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
30
-
31
- exe = EXE(
32
- pyz,
33
- a.scripts,
34
- [],
35
- exclude_binaries=True,
36
- name='slm-hook',
37
- debug=False,
38
- bootloader_ignore_signals=False,
39
- strip=True,
40
- upx=False, # UPX adds 30-60 ms cold-start + AV heuristics
41
- console=False, # no console flash on Windows
42
- disable_windowed_traceback=False,
43
- target_arch=None,
44
- codesign_identity=None, # handled in scripts/sign-macos.sh post-build
45
- entitlements_file=None,
46
- )
47
-
48
- coll = COLLECT(
49
- exe,
50
- a.binaries,
51
- a.zipfiles,
52
- a.datas,
53
- strip=True,
54
- upx=False,
55
- name='slm-hook',
56
- )
@@ -1,52 +0,0 @@
1
- # ============================================================================
2
- # SuperLocalMemory - Dashboard Startup Script (PowerShell)
3
- # Starts the web dashboard on http://localhost:8765
4
- # Copyright (c) 2026 Varun Pratap Bhardwaj
5
- # ============================================================================
6
-
7
- $ErrorActionPreference = "Stop"
8
-
9
- $SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
10
- Set-Location $SCRIPT_DIR
11
-
12
- Write-Host "=================================================================="
13
- Write-Host " SuperLocalMemory - Dashboard "
14
- Write-Host " by Varun Pratap Bhardwaj "
15
- Write-Host "=================================================================="
16
- Write-Host ""
17
-
18
- # Check if Python is available
19
- try {
20
- $pythonVersion = & python --version 2>&1
21
- Write-Host "Python: $pythonVersion"
22
- } catch {
23
- Write-Host "ERROR: Python not found in PATH" -ForegroundColor Red
24
- Write-Host "Install Python 3.8+ from https://www.python.org/downloads/" -ForegroundColor Yellow
25
- exit 1
26
- }
27
-
28
- # Check if FastAPI is installed
29
- try {
30
- & python -c "import fastapi" 2>$null
31
- } catch {
32
- Write-Host "WARNING: FastAPI not installed (optional dependency)" -ForegroundColor Yellow
33
- Write-Host ""
34
- Write-Host "To use the dashboard, install web dependencies:"
35
- Write-Host " pip install -r requirements-ui.txt"
36
- Write-Host ""
37
- Write-Host "Or install all features:"
38
- Write-Host " pip install -r requirements-full.txt"
39
- Write-Host ""
40
- exit 1
41
- }
42
-
43
- Write-Host "Starting dashboard server..." -ForegroundColor Green
44
- Write-Host ""
45
- Write-Host " Dashboard: http://localhost:8765"
46
- Write-Host " API Docs: http://localhost:8765/docs"
47
- Write-Host ""
48
- Write-Host "Press Ctrl+C to stop"
49
- Write-Host ""
50
-
51
- # Start server
52
- & python ui_server.py