@memorax/memorax-code 0.1.2 → 0.1.4

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 (242) hide show
  1. package/README.md +16 -12
  2. package/bin/memorax-code-npm-preinstall.mjs +7 -2
  3. package/bin/memorax-code-opencode.mjs +4 -0
  4. package/bin/memorax-code-plugin-postinstall.mjs +342 -93
  5. package/docs/configuration.md +148 -35
  6. package/docs/troubleshooting.md +73 -13
  7. package/lib/dsh-plugin-install.mjs +22 -0
  8. package/lib/memorax-code-adapter-common/src/hooks/capture-cwd-hook.mjs +21 -14
  9. package/lib/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +26 -7
  10. package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +86 -95
  11. package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-policy.mjs +58 -0
  12. package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs +2 -2
  13. package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-job-supervisor.mjs +1 -1
  14. package/lib/memorax-code-adapter-common/src/windows-cli-invocation.mjs +13 -1
  15. package/lib/memorax-code-backend/dist/app/backend-server.js +210 -0
  16. package/lib/memorax-code-backend/dist/{server-observability.js → app/memory-observability.js} +4 -4
  17. package/lib/memorax-code-backend/dist/{backend-state.js → app/state.js} +1 -1
  18. package/lib/memorax-code-backend/dist/{claude-adapter-lifecycle.js → clients/claude/lifecycle.js} +2 -2
  19. package/lib/memorax-code-backend/dist/{claude-memory-hook-runtime.js → clients/claude/memory-hook-runtime.js} +7 -7
  20. package/lib/memorax-code-backend/dist/clients/codex/lifecycle.js +131 -0
  21. package/lib/memorax-code-backend/dist/{codex-memory-hook-runtime.js → clients/codex/memory-hook-runtime.js} +9 -9
  22. package/lib/memorax-code-backend/dist/{codex-plugin-hooks.js → clients/codex/plugin-hooks.js} +1 -1
  23. package/lib/memorax-code-backend/dist/{codex-plugin-install.js → clients/codex/plugin-install.js} +6 -6
  24. package/lib/memorax-code-backend/dist/clients/dsh/lifecycle.js +75 -0
  25. package/lib/memorax-code-backend/dist/clients/dsh/memory-hook-runtime.js +258 -0
  26. package/lib/memorax-code-backend/dist/clients/dsh/session-turn.js +249 -0
  27. package/lib/memorax-code-backend/dist/clients/opencode/lifecycle.js +54 -0
  28. package/lib/memorax-code-backend/dist/clients/opencode/memory-hook-runtime.js +277 -0
  29. package/lib/memorax-code-backend/dist/clients/opencode/message-turn.js +78 -0
  30. package/lib/memorax-code-backend/dist/codex-adapter-lifecycle.js +1 -131
  31. package/lib/memorax-code-backend/dist/{memorax-code-config.js → config/memorax-code.js} +32 -4
  32. package/lib/memorax-code-backend/dist/{server-cli.js → entrypoints/backend-cli.js} +137 -37
  33. package/lib/memorax-code-backend/dist/jsonl-append.js +1 -75
  34. package/lib/memorax-code-backend/dist/{active-client-selection.js → lifecycle/active-clients.js} +12 -1
  35. package/lib/memorax-code-backend/dist/{backend-service-cleanup.js → lifecycle/backend/cleanup.js} +1 -1
  36. package/lib/memorax-code-backend/dist/{backend-service-record.js → lifecycle/backend/record.js} +1 -1
  37. package/lib/memorax-code-backend/dist/{backend-service-result.js → lifecycle/backend/result.js} +2 -2
  38. package/lib/memorax-code-backend/dist/{service.js → lifecycle/backend/service.js} +16 -16
  39. package/lib/memorax-code-backend/dist/{backend-token-record.js → lifecycle/backend/token-record.js} +1 -1
  40. package/lib/memorax-code-backend/dist/{backend-service-token.js → lifecycle/backend/token.js} +2 -2
  41. package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +109 -0
  42. package/lib/memorax-code-backend/dist/{client-selection.js → lifecycle/client-selection.js} +11 -5
  43. package/lib/memorax-code-backend/dist/{backend-lifecycle-lock.js → lifecycle/lock.js} +1 -1
  44. package/lib/memorax-code-backend/dist/{memorax-code-lifecycle.js → lifecycle/orchestrator.js} +325 -39
  45. package/lib/memorax-code-backend/dist/memorax-cli.js +1 -1
  46. package/lib/memorax-code-backend/dist/memorax-code.js +1 -1
  47. package/lib/memorax-code-backend/dist/{automatic-memory-retrieval.js → memory/automatic-retrieval.js} +3 -3
  48. package/lib/memorax-code-backend/dist/{automatic-memory-writeback.js → memory/automatic-writeback.js} +75 -21
  49. package/lib/memorax-code-backend/dist/{memory-cli.js → memory/cli.js} +23 -10
  50. package/lib/memorax-code-backend/dist/{memory-hook-command.js → memory/hook-command.js} +156 -9
  51. package/lib/memorax-code-backend/dist/memory/observability.js +1 -0
  52. package/lib/memorax-code-backend/dist/memory/payload-redaction.js +212 -0
  53. package/lib/memorax-code-backend/dist/{memory-reminder-trace-recorder.js → memory/reminder-trace-recorder.js} +20 -7
  54. package/lib/memorax-code-backend/dist/{repository-memory-context.js → memory/repository-session.js} +2 -2
  55. package/lib/memorax-code-backend/dist/{memory-service.js → memory/service.js} +27 -5
  56. package/lib/memorax-code-backend/dist/{memory-turn-coordinator.js → memory/turn-coordinator.js} +2 -1
  57. package/lib/memorax-code-backend/dist/{memory-writeback-buffer.js → memory/writeback-buffer.js} +3 -3
  58. package/lib/memorax-code-backend/dist/{memory-writeback-chunk.js → memory/writeback-chunk.js} +1 -1
  59. package/lib/memorax-code-backend/dist/{memory-writeback-reconciler.js → memory/writeback-reconciler.js} +6 -6
  60. package/lib/memorax-code-backend/dist/{memory-writeback-task-projection.js → memory/writeback-task-projection.js} +4 -4
  61. package/lib/memorax-code-backend/dist/{memorax-adapter.js → provider/memorax/adapter.js} +5 -5
  62. package/lib/memorax-code-backend/dist/{memorax-config.js → provider/memorax/config.js} +4 -4
  63. package/lib/memorax-code-backend/dist/{memorax-http.js → provider/memorax/http.js} +1 -1
  64. package/lib/memorax-code-backend/dist/{repo-memory-readiness.js → repository/readiness.js} +3 -3
  65. package/lib/memorax-code-backend/dist/server.js +3 -212
  66. package/lib/memorax-code-backend/dist/service-entrypoint.js +1 -1
  67. package/lib/memorax-code-backend/dist/{debug-log.js → shared/debug-log.js} +1 -1
  68. package/lib/memorax-code-backend/dist/shared/jsonl-append.js +75 -0
  69. package/lib/memorax-code-backend/dist/shared/windows-cli-invocation.js +148 -0
  70. package/lib/memorax-code-backend/dist/{trace-config.js → trace/config.js} +43 -5
  71. package/lib/memorax-code-backend/dist/{trace-context.js → trace/context.js} +53 -2
  72. package/lib/memorax-code-backend/dist/{trace-store.js → trace/store.js} +3 -3
  73. package/lib/memorax-code-backend/dist/{server-health.js → transport/http/health.js} +1 -1
  74. package/lib/memorax-code-backend/dist/{server-memory-hook.js → transport/http/memory-hook.js} +3 -3
  75. package/lib/memorax-code-backend/dist/{server-http.js → transport/http/request.js} +1 -1
  76. package/lib/memorax-code-backend/dist/{claude-local-transcript.js → viewer/history/claude-transcript.js} +3 -3
  77. package/lib/memorax-code-backend/dist/{memory-viewer-session-title.js → viewer/history/session-title.js} +1 -1
  78. package/lib/memorax-code-backend/dist/{server-memory-viewer.js → viewer/http/public-routes.js} +15 -15
  79. package/lib/memorax-code-backend/dist/viewer/model.js +1 -0
  80. package/lib/memorax-code-backend/dist/{memory-viewer-activity.js → viewer/projection/activity.js} +2 -2
  81. package/lib/memorax-code-backend/dist/{memory-viewer-history-projection.js → viewer/projection/history.js} +1 -1
  82. package/lib/memorax-code-backend/dist/{memory-viewer-observability.js → viewer/projection/observability.js} +1 -1
  83. package/lib/memorax-code-backend/dist/{memory-viewer-user-projection.js → viewer/projection/user.js} +1 -1
  84. package/lib/memorax-code-backend/dist/{memory-viewer-writeback-status.js → viewer/projection/writeback-status.js} +1 -1
  85. package/lib/memorax-code-backend/dist/{memory-viewer-store.js → viewer/store.js} +59 -32
  86. package/lib/memorax-code-backend/dist/{memory-viewer-user-html.js → viewer/ui/user-html.js} +11 -10
  87. package/lib/memorax-code-backend/dist/windows-cli-invocation.js +1 -148
  88. package/lib/memorax-code-backend/package.json +3 -3
  89. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  90. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  91. package/lib/memorax-code-claude-adapter/package.json +1 -1
  92. package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +11 -2
  93. package/lib/memorax-code-claude-adapter/skills/memorax-code/defaults.json +8 -5
  94. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  95. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +37 -9
  96. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/personal-read.md +1 -1
  97. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/personal-write.md +33 -13
  98. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-build.md +62 -13
  99. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-read.md +11 -4
  100. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-templates.md +179 -59
  101. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-update.md +18 -7
  102. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/collect_all.py +122 -39
  103. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/detect_updates.py +73 -10
  104. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/github_resource_facets.py +9 -1
  105. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +9 -1
  106. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/validate_memory.py +23 -1
  107. package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
  108. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  109. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  110. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/capture-cwd-hook.mjs +21 -14
  111. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +26 -7
  112. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +86 -95
  113. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-policy.mjs +58 -0
  114. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs +2 -2
  115. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-job-supervisor.mjs +1 -1
  116. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/windows-cli-invocation.mjs +13 -1
  117. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  118. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +11 -2
  119. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/defaults.json +8 -5
  120. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  121. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +37 -9
  122. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/personal-read.md +1 -1
  123. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/personal-write.md +33 -13
  124. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-build.md +62 -13
  125. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-read.md +11 -4
  126. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-templates.md +179 -59
  127. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-update.md +18 -7
  128. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/collect_all.py +122 -39
  129. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/detect_updates.py +73 -10
  130. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/github_resource_facets.py +9 -1
  131. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +9 -1
  132. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/validate_memory.py +23 -1
  133. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
  134. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  135. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  136. package/lib/memorax-code-codex-adapter/package.json +1 -1
  137. package/lib/memorax-code-codex-adapter/runtime-hooks/memory-skill-reminder.mjs +10 -2
  138. package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +11 -2
  139. package/lib/memorax-code-codex-adapter/skills/memorax-code/defaults.json +8 -5
  140. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  141. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +37 -9
  142. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/personal-read.md +1 -1
  143. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/personal-write.md +33 -13
  144. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-build.md +62 -13
  145. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-read.md +11 -4
  146. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-templates.md +179 -59
  147. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-update.md +18 -7
  148. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/collect_all.py +122 -39
  149. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/detect_updates.py +73 -10
  150. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/github_resource_facets.py +9 -1
  151. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +9 -1
  152. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/validate_memory.py +23 -1
  153. package/lib/memorax-code-dsh-adapter/cordis.patch.yml +10 -0
  154. package/lib/memorax-code-dsh-adapter/hooks/repo-memory-job.mjs +68 -0
  155. package/lib/memorax-code-dsh-adapter/package.json +69 -0
  156. package/lib/memorax-code-dsh-adapter/skills/memorax-code/SKILL.md +91 -0
  157. package/lib/memorax-code-dsh-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  158. package/lib/memorax-code-dsh-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  159. package/lib/memorax-code-dsh-adapter/skills/memorax-code/defaults.json +12 -0
  160. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +86 -0
  161. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +91 -0
  162. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/personal-read.md +46 -0
  163. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/personal-write.md +120 -0
  164. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-build.md +319 -0
  165. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-read.md +103 -0
  166. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  167. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-update.md +127 -0
  168. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  169. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  170. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  171. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  172. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  173. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  174. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  175. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  176. package/lib/memorax-code-dsh-adapter/src/backend-client.mjs +119 -0
  177. package/lib/memorax-code-dsh-adapter/src/dsh-message.mjs +32 -0
  178. package/lib/memorax-code-dsh-adapter/src/dsh-version.mjs +18 -0
  179. package/lib/memorax-code-dsh-adapter/src/http-client.mjs +55 -0
  180. package/lib/memorax-code-dsh-adapter/src/index.mjs +72 -0
  181. package/lib/memorax-code-dsh-adapter/src/personal-context-worker.mjs +48 -0
  182. package/lib/memorax-code-dsh-adapter/src/personal-context.mjs +114 -0
  183. package/lib/memorax-code-dsh-adapter/src/plugin.mjs +724 -0
  184. package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +1358 -0
  185. package/lib/memorax-code-dsh-adapter/src/protocol.mjs +145 -0
  186. package/lib/memorax-code-dsh-adapter/src/runtime-state.mjs +132 -0
  187. package/lib/memorax-code-opencode-adapter/hooks/repo-memory-job.mjs +37 -0
  188. package/lib/memorax-code-opencode-adapter/package.json +10 -0
  189. package/lib/memorax-code-opencode-adapter/skills/memorax-code/SKILL.md +91 -0
  190. package/lib/memorax-code-opencode-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  191. package/lib/memorax-code-opencode-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  192. package/lib/memorax-code-opencode-adapter/skills/memorax-code/defaults.json +12 -0
  193. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +86 -0
  194. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +91 -0
  195. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/personal-read.md +46 -0
  196. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/personal-write.md +120 -0
  197. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-build.md +319 -0
  198. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-read.md +103 -0
  199. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  200. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-update.md +127 -0
  201. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  202. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  203. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  204. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  205. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  206. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  207. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  208. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  209. package/lib/memorax-code-opencode-adapter/src/adapter-paths.mjs +37 -0
  210. package/lib/memorax-code-opencode-adapter/src/cli.mjs +260 -0
  211. package/lib/memorax-code-opencode-adapter/src/diagnostics.mjs +31 -0
  212. package/lib/memorax-code-opencode-adapter/src/plugin-install.mjs +673 -0
  213. package/lib/memorax-code-opencode-adapter/src/plugin.mjs +539 -0
  214. package/lib/memorax-code-opencode-adapter/src/repo-memory-server-runner.mjs +442 -0
  215. package/lib/run-entrypoint.mjs +6 -0
  216. package/package.json +7 -3
  217. package/lib/memorax-code-backend/dist/client-plugin-removal.js +0 -67
  218. /package/lib/memorax-code-backend/dist/{claude-transcript-turn.js → clients/claude/transcript-turn.js} +0 -0
  219. /package/lib/memorax-code-backend/dist/{codex-effective-prompt.js → clients/codex/effective-prompt.js} +0 -0
  220. /package/lib/memorax-code-backend/dist/{codex-rollout-turn.js → clients/codex/rollout-turn.js} +0 -0
  221. /package/lib/memorax-code-backend/dist/{codex-session-turn-index.js → clients/codex/session-turn-index.js} +0 -0
  222. /package/lib/memorax-code-backend/dist/{codex-workspace-links.js → clients/codex/workspace-links.js} +0 -0
  223. /package/lib/memorax-code-backend/dist/{backend-env.js → config/backend-env.js} +0 -0
  224. /package/lib/memorax-code-backend/dist/{proxy-env.js → config/proxy-env.js} +0 -0
  225. /package/lib/memorax-code-backend/dist/{entrypoint.js → entrypoints/main.js} +0 -0
  226. /package/lib/memorax-code-backend/dist/{backend-process.js → lifecycle/backend/process.js} +0 -0
  227. /package/lib/memorax-code-backend/dist/{backend-shutdown-request.js → lifecycle/backend/shutdown-request.js} +0 -0
  228. /package/lib/memorax-code-backend/dist/{backend-status.js → lifecycle/backend/status.js} +0 -0
  229. /package/lib/memorax-code-backend/dist/{adapter-lifecycle-participant.js → lifecycle/contracts.js} +0 -0
  230. /package/lib/memorax-code-backend/dist/{install-watchdog.js → lifecycle/install-watchdog.js} +0 -0
  231. /package/lib/memorax-code-backend/dist/{memory-observability.js → lifecycle/participant.js} +0 -0
  232. /package/lib/memorax-code-backend/dist/{memory-project.js → memory/project.js} +0 -0
  233. /package/lib/memorax-code-backend/dist/{repository-memory-scope.js → repository/scope.js} +0 -0
  234. /package/lib/memorax-code-backend/dist/{incremental-jsonl-projection.js → shared/incremental-jsonl-projection.js} +0 -0
  235. /package/lib/memorax-code-backend/dist/{utils.js → shared/record.js} +0 -0
  236. /package/lib/memorax-code-backend/dist/{server-json.js → transport/http/json.js} +0 -0
  237. /package/lib/memorax-code-backend/dist/{memory-viewer-data-projection-cache.js → viewer/projection/cache.js} +0 -0
  238. /package/lib/memorax-code-backend/dist/{memory-viewer-event-identity.js → viewer/projection/event-identity.js} +0 -0
  239. /package/lib/memorax-code-backend/dist/{memory-viewer-redaction.js → viewer/projection/redaction.js} +0 -0
  240. /package/lib/memorax-code-backend/dist/{memory-viewer-turn-reference.js → viewer/projection/turn-reference.js} +0 -0
  241. /package/lib/memorax-code-backend/dist/{memory-viewer-icon.js → viewer/ui/icon.js} +0 -0
  242. /package/lib/memorax-code-backend/dist/{memory-viewer-logo.js → viewer/ui/logo.js} +0 -0
@@ -0,0 +1,1358 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import {
4
+ copyFileSync,
5
+ existsSync,
6
+ lstatSync,
7
+ mkdirSync,
8
+ readFileSync,
9
+ readdirSync,
10
+ renameSync,
11
+ rmSync,
12
+ } from "node:fs";
13
+ import { createRequire } from "node:module";
14
+ import { homedir } from "node:os";
15
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
16
+ import { isDeepStrictEqual } from "node:util";
17
+ import { fileURLToPath, pathToFileURL } from "node:url";
18
+
19
+ import {
20
+ DSH_TESTED_VERSIONS,
21
+ isTestedDshVersion,
22
+ parseDshVersion,
23
+ } from "./dsh-version.mjs";
24
+ import {
25
+ requireDshRuntimeAuthority,
26
+ requireEnabledDshRuntime,
27
+ } from "./runtime-state.mjs";
28
+
29
+ const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
30
+ const ADAPTER_ROOT = resolve(MODULE_DIR, "..");
31
+ const stagedCommonRoot = resolve(ADAPTER_ROOT, "../memorax-code-adapter-common/src");
32
+ const sourceCommonRoot = resolve(MODULE_DIR, "../../memorax-code-adapter-common/src");
33
+ const commonRoot = existsSync(join(stagedCommonRoot, "config-utils.mjs"))
34
+ ? stagedCommonRoot
35
+ : sourceCommonRoot;
36
+ const stagedSkillRoot = resolve(ADAPTER_ROOT, "skills/memorax-code");
37
+ const sourceSkillRoot = resolve(MODULE_DIR, "../../memorax-code-codex-adapter/skills/memorax-code");
38
+ const skillRoot = existsSync(join(stagedSkillRoot, "SKILL.md"))
39
+ ? stagedSkillRoot
40
+ : sourceSkillRoot;
41
+ const {
42
+ atomicWriteJson,
43
+ readAdapterState,
44
+ withJsonFileLockAsync,
45
+ } = await import(pathToFileURL(join(commonRoot, "config-utils.mjs")).href);
46
+ const { resolveWindowsCliInvocation } = await import(
47
+ pathToFileURL(join(commonRoot, "windows-cli-invocation.mjs")).href
48
+ );
49
+
50
+ const STATE_VERSION = 1;
51
+ const RUNTIME = "dsh";
52
+ const ADAPTER_PACKAGE_NAME = "@memorax-code/dsh-memorax-code";
53
+ const HEADLESS_PROFILE_NAME = "headless";
54
+ const HEADLESS_BUNDLE_NAME = "@deepseek-ai/dsh-headless";
55
+ const LEGACY_ADAPTER_PACKAGE_NAMES = Object.freeze(["@memorax-code/dsh-adapter"]);
56
+ const MANAGED_ADAPTER_PACKAGE_NAMES = Object.freeze([
57
+ ADAPTER_PACKAGE_NAME,
58
+ ...LEGACY_ADAPTER_PACKAGE_NAMES,
59
+ ]);
60
+ const DSH_VERSION_TIMEOUT_MS = 10_000;
61
+ const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
62
+ const DEFAULT_LIFECYCLE_LOCK_TIMEOUT_MS = 600_000;
63
+ const PACKAGE_METADATA_FILE = ".memorax-code-package.json";
64
+ const PROFILE_BUNDLE_FILES = Object.freeze([
65
+ PACKAGE_METADATA_FILE,
66
+ "cordis.patch.yml",
67
+ "hooks/repo-memory-job.mjs",
68
+ "skills/memorax-code/SKILL.md",
69
+ "skills/memorax-code/agents/claude.yaml",
70
+ "skills/memorax-code/agents/openai.yaml",
71
+ "skills/memorax-code/defaults.json",
72
+ "skills/memorax-code/references/memorax-add.md",
73
+ "skills/memorax-code/references/memorax-search.md",
74
+ "skills/memorax-code/references/personal-read.md",
75
+ "skills/memorax-code/references/personal-write.md",
76
+ "skills/memorax-code/references/repo-build.md",
77
+ "skills/memorax-code/references/repo-read.md",
78
+ "skills/memorax-code/references/repo-templates.md",
79
+ "skills/memorax-code/references/repo-update.md",
80
+ "skills/memorax-code/scripts/collect_all.py",
81
+ "skills/memorax-code/scripts/detect_updates.py",
82
+ "skills/memorax-code/scripts/git_commit_facets.py",
83
+ "skills/memorax-code/scripts/github_resource_facets.py",
84
+ "skills/memorax-code/scripts/gitlab_resource_facets.py",
85
+ "skills/memorax-code/scripts/prepare_repo_memory.py",
86
+ "skills/memorax-code/scripts/user_profile_memory.py",
87
+ "skills/memorax-code/scripts/validate_memory.py",
88
+ "src/index.mjs",
89
+ "src/backend-client.mjs",
90
+ "src/dsh-message.mjs",
91
+ "src/dsh-version.mjs",
92
+ "src/http-client.mjs",
93
+ "src/personal-context-worker.mjs",
94
+ "src/personal-context.mjs",
95
+ "src/plugin.mjs",
96
+ "src/protocol.mjs",
97
+ "src/runtime-state.mjs",
98
+ "memorax-code-adapter-common/src/backend-connection.mjs",
99
+ "memorax-code-adapter-common/src/config-utils.mjs",
100
+ "memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs",
101
+ "memorax-code-adapter-common/src/hooks/memory-skill-reminder-policy.mjs",
102
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs",
103
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-job-context.mjs",
104
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-job-marker.mjs",
105
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-job-supervisor.mjs",
106
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs",
107
+ "memorax-code-adapter-common/src/repo-memory/repo-procedure-memory-context.mjs",
108
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-update-policy-evaluator.mjs",
109
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-update-policy.mjs",
110
+ "memorax-code-adapter-common/src/repo-memory/repo-user-profile-context.mjs",
111
+ "memorax-code-adapter-common/src/runtime-record.mjs",
112
+ "memorax-code-adapter-common/src/windows-cli-invocation.mjs",
113
+ ]);
114
+
115
+ export function discoverDshProfiles(options = {}) {
116
+ const paths = resolvePaths(options);
117
+ let entries;
118
+ try {
119
+ entries = readdirSync(paths.profilesRoot, { withFileTypes: true });
120
+ } catch (error) {
121
+ if (error?.code === "ENOENT") return [];
122
+ throw error;
123
+ }
124
+ return entries
125
+ .filter((entry) => entry.isDirectory() && validProfileName(entry.name))
126
+ .map((entry) => readProfile(entry.name, join(paths.profilesRoot, entry.name)))
127
+ .filter(Boolean)
128
+ .sort((left, right) => left.name.localeCompare(right.name));
129
+ }
130
+
131
+ export function collectDshAdapterStatus(options = {}) {
132
+ try {
133
+ const paths = resolvePaths(options);
134
+ const state = readAdapterState(paths.statePath);
135
+ const stateProblem = validateState(state, paths);
136
+ const discoveredProfiles = discoverDshProfiles({ ...options, dshHome: paths.dshHome });
137
+ const managedNames = new Set(stateProblem ? [] : state?.profiles ?? []);
138
+ const profiles = projectProfileStatus(
139
+ discoveredProfiles,
140
+ managedNames,
141
+ true,
142
+ state?.runtimeBundleRoot,
143
+ stateProblem ? undefined : state,
144
+ );
145
+ const managed = Boolean(state) && !stateProblem;
146
+ const installed = profiles.length > 0
147
+ && profiles.every((profile) => profile.managed && profile.exists && profile.installed)
148
+ && managedProfilesHaveInstalledHeadlessBundle(discoveredProfiles, managedNames);
149
+ const base = {
150
+ integration: "plugin",
151
+ managed,
152
+ installed,
153
+ enabled: false,
154
+ profiles,
155
+ };
156
+ if (stateProblem) {
157
+ return { ok: false, ...base, reason: stateProblem.reason };
158
+ }
159
+ if (!state && profiles.length === 0) {
160
+ return { ok: true, ...base, skipped: true, reason: "no_existing_profiles" };
161
+ }
162
+
163
+ const dshCommand = resolveDshCommand(options, paths, state);
164
+ const compatibility = inspectDshCompatibility(options, paths, dshCommand);
165
+ const version = compatibility.dshVersion;
166
+ const versionStatus = {
167
+ dshVersionTested: compatibility.dshVersionTested,
168
+ testedDshVersions: [...DSH_TESTED_VERSIONS],
169
+ };
170
+ if (compatibility.reason === "dsh_version_unavailable") {
171
+ return {
172
+ ok: false,
173
+ ...base,
174
+ compatible: false,
175
+ reason: compatibility.reason,
176
+ };
177
+ }
178
+ if (compatibility.compatible !== true) {
179
+ return {
180
+ ok: true,
181
+ ...base,
182
+ ...(version ? { version } : {}),
183
+ compatible: false,
184
+ ...versionStatus,
185
+ skipped: true,
186
+ reason: compatibility.reason,
187
+ };
188
+ }
189
+ if (!state) {
190
+ return {
191
+ ok: true,
192
+ ...base,
193
+ version,
194
+ compatible: true,
195
+ ...versionStatus,
196
+ skipped: true,
197
+ reason: "not_managed",
198
+ };
199
+ }
200
+ if (state.enabled === true) {
201
+ try {
202
+ requireEnabledDshRuntime(state.runtimeBundleRoot);
203
+ } catch {
204
+ return {
205
+ ok: false,
206
+ ...base,
207
+ version,
208
+ compatible: true,
209
+ ...versionStatus,
210
+ reason: "runtime_authority_invalid",
211
+ };
212
+ }
213
+ }
214
+ return {
215
+ ok: true,
216
+ ...base,
217
+ enabled: state.enabled === true && installed,
218
+ version,
219
+ compatible: true,
220
+ ...versionStatus,
221
+ ...(state.enabled !== true
222
+ ? { reason: "disabled" }
223
+ : !installed
224
+ ? { reason: "profile_drift" }
225
+ : {}),
226
+ };
227
+ } catch {
228
+ return {
229
+ ok: false,
230
+ integration: "plugin",
231
+ managed: false,
232
+ installed: false,
233
+ enabled: false,
234
+ profiles: [],
235
+ reason: "dsh_status_unavailable",
236
+ };
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Serialize one product lifecycle command with adapter recovery and DSH state
242
+ * mutation. The callback must use the supplied unlocked operations only.
243
+ */
244
+ export function withDshPluginLifecycleLock(options = {}, operation) {
245
+ if (typeof operation !== "function") {
246
+ throw new TypeError("DSH lifecycle lock requires an operation");
247
+ }
248
+ const paths = resolvePaths(options);
249
+ return withJsonFileLockAsync(paths.statePath, () => operation(Object.freeze({
250
+ status: () => readDshPluginStatusUnlocked(paths, options),
251
+ ensureInstalled: (overrides = {}) => ensureDshPluginInstalledUnlocked(
252
+ paths,
253
+ { ...options, ...overrides },
254
+ ),
255
+ activate: () => activateDshPluginInstallationUnlocked(paths, options),
256
+ quiesce: () => quiesceDshPluginInstallationUnlocked(paths),
257
+ disable: () => disableDshPluginInstallationUnlocked(paths, options, false),
258
+ remove: () => disableDshPluginInstallationUnlocked(paths, options, true),
259
+ })), {
260
+ timeoutMs: DEFAULT_LIFECYCLE_LOCK_TIMEOUT_MS,
261
+ });
262
+ }
263
+
264
+ function readDshPluginStatusUnlocked(paths, options) {
265
+ const state = readAdapterState(paths.statePath);
266
+ const stateProblem = validateState(state, paths);
267
+ if (stateProblem) return { ...stateProblem, action: "dsh-plugin-status" };
268
+ if (!state) {
269
+ return {
270
+ ok: true,
271
+ action: "dsh-plugin-status",
272
+ runtime: RUNTIME,
273
+ installed: false,
274
+ enabled: false,
275
+ managed: false,
276
+ profiles: [],
277
+ };
278
+ }
279
+
280
+ const profiles = discoverDshProfiles({ ...options, dshHome: paths.dshHome });
281
+ const managedProfiles = projectProfileStatus(
282
+ profiles,
283
+ new Set(state.profiles),
284
+ false,
285
+ state.runtimeBundleRoot,
286
+ state,
287
+ );
288
+ const installed = state.profiles.length > 0
289
+ && managedProfiles.every((profile) => profile.installed)
290
+ && managedProfilesHaveInstalledHeadlessBundle(profiles, new Set(state.profiles));
291
+ return {
292
+ ok: true,
293
+ action: "dsh-plugin-status",
294
+ runtime: RUNTIME,
295
+ installed,
296
+ enabled: state.enabled === true && installed,
297
+ managed: true,
298
+ authorityEnabled: state.enabled === true,
299
+ revision: state.updatedAt,
300
+ profiles: managedProfiles,
301
+ };
302
+ }
303
+
304
+ function ensureDshPluginInstalledUnlocked(paths, options) {
305
+ const state = readAdapterState(paths.statePath);
306
+ const stateProblem = validateState(state, paths);
307
+ if (stateProblem) return { ...stateProblem, action: "dsh-plugin-install" };
308
+ const claimedProfiles = (state?.profiles ?? []).map((name) => (
309
+ inspectProfile(name, join(paths.profilesRoot, name))
310
+ ));
311
+ const manifestProblem = managedProfileManifestProblem(claimedProfiles);
312
+ if (manifestProblem) return manifestProblem;
313
+
314
+ const profiles = discoverDshProfiles({ ...options, dshHome: paths.dshHome });
315
+ if (profiles.length === 0) {
316
+ if (state) atomicWriteJson(paths.statePath, disabledState(state, []));
317
+ return {
318
+ ok: !state,
319
+ action: "dsh-plugin-install",
320
+ runtime: RUNTIME,
321
+ installed: false,
322
+ enabled: false,
323
+ managed: Boolean(state),
324
+ ...(!state ? { skipped: true } : {}),
325
+ reason: "no_existing_profiles",
326
+ detectedProfiles: [],
327
+ };
328
+ }
329
+
330
+ const dshCommand = resolveDshCommand(options, paths, state);
331
+ const compatibility = inspectDshCompatibility(options, paths, dshCommand);
332
+ if (compatibility.compatible !== true) {
333
+ const nextState = state?.enabled === true
334
+ ? disabledState(state, state.profiles)
335
+ : state;
336
+ if (nextState && nextState !== state) atomicWriteJson(paths.statePath, nextState);
337
+ return {
338
+ ok: false,
339
+ action: "dsh-plugin-install",
340
+ runtime: RUNTIME,
341
+ installed: false,
342
+ enabled: false,
343
+ managed: Boolean(state),
344
+ detectedProfiles: profiles.map((profile) => profile.name),
345
+ ...compatibility,
346
+ };
347
+ }
348
+
349
+ const previouslyManaged = new Set(claimedProfiles
350
+ .filter((profile) => profile.status !== "directory_missing")
351
+ .map((profile) => profile.name));
352
+ const conflicts = profiles
353
+ .filter((profile) => profileMentionsAdapter(profile) && !previouslyManaged.has(profile.name))
354
+ .map((profile) => profile.name);
355
+ if (conflicts.length > 0) {
356
+ return {
357
+ ok: false,
358
+ action: "dsh-plugin-install",
359
+ runtime: RUNTIME,
360
+ reason: "profile_plugin_conflict",
361
+ profiles: conflicts,
362
+ };
363
+ }
364
+
365
+ let workerProfileName = profiles.find(profileHasInstalledHeadlessBundle)?.name;
366
+ let targetProfiles = profiles;
367
+ let initializeWorkerProfile = false;
368
+ if (!workerProfileName) {
369
+ const headlessPath = join(paths.profilesRoot, HEADLESS_PROFILE_NAME);
370
+ const headless = inspectProfile(HEADLESS_PROFILE_NAME, headlessPath);
371
+ if (headless.status === "manifest_unreadable") {
372
+ return {
373
+ ok: false,
374
+ action: "dsh-plugin-install",
375
+ runtime: RUNTIME,
376
+ reason: "profile_manifest_unreadable",
377
+ profiles: [HEADLESS_PROFILE_NAME],
378
+ };
379
+ }
380
+ if (headless.status === "valid") {
381
+ return {
382
+ ok: false,
383
+ action: "dsh-plugin-install",
384
+ runtime: RUNTIME,
385
+ reason: "headless_profile_not_capable",
386
+ profiles: [HEADLESS_PROFILE_NAME],
387
+ };
388
+ }
389
+ workerProfileName = HEADLESS_PROFILE_NAME;
390
+ initializeWorkerProfile = true;
391
+ targetProfiles = [
392
+ ...profiles,
393
+ { name: HEADLESS_PROFILE_NAME, path: headlessPath },
394
+ ].sort((left, right) => left.name.localeCompare(right.name));
395
+ }
396
+
397
+ const memoraxCodeCommand = resolveMemoraxCodeCommand(options.memoraxCodeCommand);
398
+ const metadata = {
399
+ version: 1,
400
+ memoraxCodeCommand,
401
+ memoraxCodeHome: paths.memoraxCodeHome,
402
+ dshHome: paths.dshHome,
403
+ dshCommand,
404
+ dshVersion: compatibility.dshVersion,
405
+ sourceAdapterRoot: paths.adapterRoot,
406
+ };
407
+ const runtimeBundleRoot = materializeRuntimeBundle(paths, metadata);
408
+
409
+ const now = new Date().toISOString();
410
+ const pendingState = {
411
+ version: STATE_VERSION,
412
+ runtime: RUNTIME,
413
+ integration: "plugin",
414
+ enabled: false,
415
+ dshHome: paths.dshHome,
416
+ memoraxCodeHome: paths.memoraxCodeHome,
417
+ adapterRoot: paths.adapterRoot,
418
+ runtimeBundleRoot,
419
+ memoraxCodeCommand,
420
+ dshCommand,
421
+ dshVersion: compatibility.dshVersion,
422
+ // Preserve prior claims and claim each target before invoking DSH so an
423
+ // interrupted or partial native add remains repairable and removable.
424
+ profiles: [...new Set([
425
+ ...previouslyManaged,
426
+ ...targetProfiles.map((profile) => profile.name),
427
+ ])].sort(),
428
+ updatedAt: now,
429
+ };
430
+ atomicWriteJson(paths.statePath, pendingState);
431
+
432
+ const installedProfiles = [];
433
+ const failedProfiles = [];
434
+ const mutatedProfiles = [];
435
+ for (const profile of targetProfiles) {
436
+ let current = inspectProfile(profile.name, profile.path);
437
+ if (current.status !== "valid"
438
+ && !(initializeWorkerProfile
439
+ && profile.name === workerProfileName
440
+ && current.status === "directory_missing")) {
441
+ failedProfiles.push(current.status === "directory_missing"
442
+ ? { name: profile.name, reason: "profile_disappeared" }
443
+ : profileManifestFailure(profile.name));
444
+ continue;
445
+ }
446
+ if (!profileHasInstalledAdapter(current.profile, runtimeBundleRoot, pendingState)) {
447
+ const result = runDsh(options, paths, [
448
+ "plugin",
449
+ "--profile",
450
+ profile.name,
451
+ "add",
452
+ `file:${runtimeBundleRoot}`,
453
+ ], dshCommand);
454
+ current = inspectProfile(profile.name, profile.path);
455
+ if (previouslyManaged.has(profile.name)
456
+ || (current.status === "valid" && profileMentionsAdapter(current.profile))) {
457
+ mutatedProfiles.push(profile.name);
458
+ }
459
+ if (result.status !== 0
460
+ || result.error
461
+ || current.status !== "valid"
462
+ || !profileHasInstalledAdapter(current.profile, runtimeBundleRoot, pendingState)
463
+ || (profile.name === workerProfileName
464
+ && !profileHasInstalledHeadlessBundle(current.profile))) {
465
+ failedProfiles.push(profileMutationFailure(
466
+ profile.name,
467
+ result,
468
+ current,
469
+ profile.name === workerProfileName
470
+ ? "headless_profile_not_capable"
471
+ : "dsh_bundle_not_activated",
472
+ ));
473
+ continue;
474
+ }
475
+ }
476
+
477
+ if (LEGACY_ADAPTER_PACKAGE_NAMES.some((packageName) => (
478
+ profileMentionsPackage(current.profile, packageName)
479
+ ))) {
480
+ mutatedProfiles.push(profile.name);
481
+ const cleanup = removeProfileAdapterPackages(
482
+ paths,
483
+ options,
484
+ profile.name,
485
+ LEGACY_ADAPTER_PACKAGE_NAMES,
486
+ dshCommand,
487
+ );
488
+ current = cleanup.profile;
489
+ if (cleanup.failure) {
490
+ failedProfiles.push(cleanup.failure);
491
+ continue;
492
+ }
493
+ }
494
+
495
+ if (current.status === "valid"
496
+ && profileHasInstalledAdapter(current.profile, runtimeBundleRoot, pendingState)
497
+ && (profile.name !== workerProfileName
498
+ || profileHasInstalledHeadlessBundle(current.profile))) {
499
+ installedProfiles.push(profile.name);
500
+ } else {
501
+ failedProfiles.push(profileMutationFailure(
502
+ profile.name,
503
+ { status: 0 },
504
+ current,
505
+ "dsh_bundle_not_activated",
506
+ ));
507
+ }
508
+ }
509
+
510
+ const finalProfiles = pendingState.profiles.map((name) => (
511
+ inspectProfile(name, join(paths.profilesRoot, name))
512
+ ));
513
+ for (const profile of finalProfiles) {
514
+ if (failedProfiles.some((failure) => failure.name === profile.name)) continue;
515
+ if (profile.status === "manifest_unreadable") {
516
+ failedProfiles.push(profileManifestFailure(profile.name));
517
+ } else if (profile.status === "valid"
518
+ && !profileHasInstalledAdapter(profile.profile, runtimeBundleRoot, pendingState)) {
519
+ failedProfiles.push({ name: profile.name, reason: "dsh_bundle_not_activated" });
520
+ }
521
+ }
522
+ if (!finalProfiles.some((profile) => (
523
+ profile.status === "valid" && profileHasInstalledHeadlessBundle(profile.profile)
524
+ )) && !failedProfiles.some((failure) => failure.name === workerProfileName)) {
525
+ failedProfiles.push({ name: workerProfileName, reason: "headless_profile_not_capable" });
526
+ }
527
+ const managedProfiles = finalProfiles
528
+ .filter((profile) => profile.status !== "directory_missing")
529
+ .map((profile) => profile.name);
530
+ if (failedProfiles.length > 0 && state) {
531
+ const rollback = rollbackDshPluginReconciliation(
532
+ paths,
533
+ options,
534
+ state,
535
+ mutatedProfiles,
536
+ dshCommand,
537
+ );
538
+ const failureReason = pluginManagerFailureReason(failedProfiles);
539
+ return {
540
+ ok: false,
541
+ action: "dsh-plugin-install",
542
+ runtime: RUNTIME,
543
+ installed: false,
544
+ enabled: rollback.authorityRestored && state.enabled,
545
+ managed: true,
546
+ detectedProfiles: profiles.map((profile) => profile.name),
547
+ dshVersion: compatibility.dshVersion,
548
+ dshVersionTested: compatibility.dshVersionTested,
549
+ testedDshVersions: [...DSH_TESTED_VERSIONS],
550
+ installedProfiles,
551
+ failedProfiles,
552
+ ...(failureReason ? { reason: failureReason } : {}),
553
+ ...(rollback.failedProfiles.length > 0
554
+ ? { rollbackFailedProfiles: rollback.failedProfiles }
555
+ : {}),
556
+ };
557
+ }
558
+ const enabled = options.enabled !== false
559
+ && failedProfiles.length === 0
560
+ && managedProfiles.length > 0;
561
+ const nextState = {
562
+ ...pendingState,
563
+ enabled,
564
+ profiles: managedProfiles,
565
+ updatedAt: new Date().toISOString(),
566
+ };
567
+ atomicWriteJson(paths.statePath, nextState);
568
+ if (failedProfiles.length === 0 && managedProfiles.length > 0) {
569
+ cleanupRuntimeGenerations(paths.runtimeRoot, runtimeBundleRoot);
570
+ }
571
+ const failureReason = pluginManagerFailureReason(failedProfiles);
572
+
573
+ return {
574
+ ok: failedProfiles.length === 0,
575
+ action: "dsh-plugin-install",
576
+ runtime: RUNTIME,
577
+ installed: failedProfiles.length === 0 && managedProfiles.length > 0,
578
+ enabled,
579
+ managed: true,
580
+ detectedProfiles: profiles.map((profile) => profile.name),
581
+ dshVersion: compatibility.dshVersion,
582
+ dshVersionTested: compatibility.dshVersionTested,
583
+ testedDshVersions: [...DSH_TESTED_VERSIONS],
584
+ installedProfiles,
585
+ failedProfiles,
586
+ ...(failureReason ? { reason: failureReason } : {}),
587
+ };
588
+ }
589
+
590
+ function rollbackDshPluginReconciliation(paths, options, state, mutatedProfiles, dshCommand) {
591
+ const previouslyManaged = new Set(state.profiles);
592
+ const previousPackageName = runtimeAdapterPackageName(state.runtimeBundleRoot)
593
+ ?? ADAPTER_PACKAGE_NAME;
594
+ const replacementPackageNames = MANAGED_ADAPTER_PACKAGE_NAMES
595
+ .filter((packageName) => packageName !== previousPackageName);
596
+ const rollbackFailedProfiles = [];
597
+ const residualProfiles = [];
598
+ const managedMutationResults = new Map();
599
+ const recordFailure = (failure) => {
600
+ if (!rollbackFailedProfiles.some(({ name }) => name === failure.name)) {
601
+ rollbackFailedProfiles.push(failure);
602
+ }
603
+ };
604
+ for (const name of [...new Set(mutatedProfiles)]) {
605
+ const profilePath = join(paths.profilesRoot, name);
606
+ const before = inspectProfile(name, profilePath);
607
+ if (previouslyManaged.has(name)) {
608
+ const result = runDsh(options, paths, [
609
+ "plugin",
610
+ "--profile",
611
+ name,
612
+ "add",
613
+ `file:${state.runtimeBundleRoot}`,
614
+ ], dshCommand);
615
+ managedMutationResults.set(name, result);
616
+ const restored = inspectProfile(name, profilePath);
617
+ if (result.status === 0
618
+ && !result.error
619
+ && restored.status === "valid"
620
+ && profileHasAdapter(restored.profile, previousPackageName)) {
621
+ const cleanup = removeProfileAdapterPackages(
622
+ paths,
623
+ options,
624
+ name,
625
+ replacementPackageNames,
626
+ dshCommand,
627
+ );
628
+ if (cleanup.failure) recordFailure(cleanup.failure);
629
+ }
630
+ continue;
631
+ }
632
+ if (before.status === "directory_missing"
633
+ || (before.status === "valid" && !profileMentionsAdapter(before.profile))) {
634
+ continue;
635
+ }
636
+ const cleanup = removeProfileAdapterPackages(
637
+ paths,
638
+ options,
639
+ name,
640
+ MANAGED_ADAPTER_PACKAGE_NAMES,
641
+ dshCommand,
642
+ );
643
+ if (cleanup.profile.status !== "directory_missing"
644
+ && (cleanup.profile.status !== "valid"
645
+ || profileMentionsAdapter(cleanup.profile.profile))) {
646
+ residualProfiles.push(name);
647
+ }
648
+ if (cleanup.failure) recordFailure(cleanup.failure);
649
+ }
650
+ const rollbackState = residualProfiles.length > 0
651
+ ? {
652
+ ...state,
653
+ profiles: [...new Set([...state.profiles, ...residualProfiles])].sort(),
654
+ updatedAt: new Date().toISOString(),
655
+ }
656
+ : state;
657
+ const verificationState = rollbackState.enabled
658
+ ? { ...rollbackState, enabled: false }
659
+ : rollbackState;
660
+ atomicWriteJson(paths.statePath, verificationState);
661
+ let authorityRestored = true;
662
+ for (const name of state.profiles) {
663
+ const profile = inspectProfile(name, join(paths.profilesRoot, name));
664
+ if (profile.status === "valid"
665
+ && profileHasInstalledAdapter(
666
+ profile.profile,
667
+ state.runtimeBundleRoot,
668
+ verificationState,
669
+ )
670
+ && !replacementPackageNames.some((packageName) => (
671
+ profileMentionsPackage(profile.profile, packageName)
672
+ ))) {
673
+ continue;
674
+ }
675
+ authorityRestored = false;
676
+ recordFailure(profileMutationFailure(
677
+ name,
678
+ managedMutationResults.get(name) ?? { status: 0 },
679
+ profile,
680
+ "dsh_bundle_not_restored",
681
+ ));
682
+ }
683
+ if (authorityRestored) atomicWriteJson(paths.statePath, rollbackState);
684
+ return { authorityRestored, failedProfiles: rollbackFailedProfiles };
685
+ }
686
+
687
+ function activateDshPluginInstallationUnlocked(paths, options) {
688
+ const state = readAdapterState(paths.statePath);
689
+ const stateProblem = validateState(state, paths);
690
+ if (stateProblem) return { ...stateProblem, action: "dsh-plugin-activate" };
691
+ if (!state) return notManaged(paths, "dsh-plugin-activate");
692
+ const profiles = discoverDshProfiles({ ...options, dshHome: paths.dshHome });
693
+ const profileByName = new Map(profiles.map((profile) => [profile.name, profile]));
694
+ if (state.profiles.length === 0
695
+ || state.profiles.some((name) => (
696
+ !profileHasInstalledAdapter(profileByName.get(name), state.runtimeBundleRoot, state)
697
+ ))
698
+ || !managedProfilesHaveInstalledHeadlessBundle(profiles, new Set(state.profiles))) {
699
+ return {
700
+ ok: false,
701
+ action: "dsh-plugin-activate",
702
+ runtime: RUNTIME,
703
+ reason: "managed_profiles_not_installed",
704
+ };
705
+ }
706
+ const nextState = { ...state, enabled: true, updatedAt: new Date().toISOString() };
707
+ atomicWriteJson(paths.statePath, nextState);
708
+ return {
709
+ ok: true,
710
+ action: "dsh-plugin-activate",
711
+ runtime: RUNTIME,
712
+ installed: true,
713
+ enabled: true,
714
+ managed: true,
715
+ profiles: [...state.profiles],
716
+ };
717
+ }
718
+
719
+ function quiesceDshPluginInstallationUnlocked(paths) {
720
+ const state = readAdapterState(paths.statePath);
721
+ const stateProblem = validateState(state, paths);
722
+ if (stateProblem) return { ...stateProblem, action: "dsh-plugin-quiesce" };
723
+ if (!state) return notManaged(paths, "dsh-plugin-quiesce");
724
+ const nextState = disabledState(state, state.profiles);
725
+ atomicWriteJson(paths.statePath, nextState);
726
+ return {
727
+ ok: true,
728
+ action: "dsh-plugin-quiesce",
729
+ runtime: RUNTIME,
730
+ installed: state.profiles.length > 0,
731
+ enabled: false,
732
+ managed: true,
733
+ authorityEnabled: false,
734
+ previouslyEnabled: state.enabled === true,
735
+ revision: nextState.updatedAt,
736
+ profiles: [...state.profiles],
737
+ };
738
+ }
739
+
740
+ function disableDshPluginInstallationUnlocked(paths, options, removeState) {
741
+ const state = readAdapterState(paths.statePath);
742
+ const action = removeState ? "dsh-plugin-remove" : "dsh-plugin-disable";
743
+ const stateProblem = validateState(state, paths);
744
+ if (stateProblem) return { ...stateProblem, action };
745
+ if (!state) return notManaged(paths, action);
746
+
747
+ const disabled = disabledState(state, state.profiles);
748
+ atomicWriteJson(paths.statePath, disabled);
749
+ const dshCommand = resolveDshCommand(options, paths, state);
750
+ const removedProfiles = [];
751
+ const failedProfiles = [];
752
+ for (const name of state.profiles) {
753
+ const before = inspectProfile(name, join(paths.profilesRoot, name));
754
+ if (before.status === "directory_missing") {
755
+ removedProfiles.push(name);
756
+ continue;
757
+ }
758
+ if (before.status === "manifest_unreadable") {
759
+ failedProfiles.push(profileManifestFailure(name));
760
+ continue;
761
+ }
762
+ if (!profileMentionsAdapter(before.profile)) {
763
+ removedProfiles.push(name);
764
+ continue;
765
+ }
766
+ const cleanup = removeProfileAdapterPackages(
767
+ paths,
768
+ options,
769
+ name,
770
+ MANAGED_ADAPTER_PACKAGE_NAMES,
771
+ dshCommand,
772
+ );
773
+ if (cleanup.failure) failedProfiles.push(cleanup.failure);
774
+ else removedProfiles.push(name);
775
+ }
776
+
777
+ if (failedProfiles.length > 0) {
778
+ atomicWriteJson(paths.statePath, disabledState(disabled, failedProfiles.map((profile) => profile.name).sort()));
779
+ const failureReason = pluginManagerFailureReason(failedProfiles);
780
+ return {
781
+ ok: false,
782
+ action,
783
+ runtime: RUNTIME,
784
+ enabled: false,
785
+ removedProfiles,
786
+ failedProfiles,
787
+ ...(failureReason ? { reason: failureReason } : {}),
788
+ };
789
+ }
790
+
791
+ if (removeState) {
792
+ rmSync(paths.statePath, { force: true });
793
+ rmSync(paths.runtimeRoot, { recursive: true, force: true });
794
+ }
795
+ else atomicWriteJson(paths.statePath, disabledState(disabled, []));
796
+ return {
797
+ ok: true,
798
+ action,
799
+ runtime: RUNTIME,
800
+ removed: removeState,
801
+ installed: false,
802
+ enabled: false,
803
+ managed: !removeState,
804
+ removedProfiles,
805
+ };
806
+ }
807
+
808
+ function removeProfileAdapterPackages(paths, options, name, packageNames, dshCommand) {
809
+ const profilePath = join(paths.profilesRoot, name);
810
+ let profile = inspectProfile(name, profilePath);
811
+ let failure;
812
+ for (const packageName of packageNames) {
813
+ if (profile.status === "directory_missing") break;
814
+ if (profile.status !== "valid") {
815
+ failure ??= profileManifestFailure(name);
816
+ break;
817
+ }
818
+ if (!profileMentionsPackage(profile.profile, packageName)) continue;
819
+ const result = runDsh(
820
+ options,
821
+ paths,
822
+ ["plugin", "--profile", name, "remove", packageName],
823
+ dshCommand,
824
+ );
825
+ profile = inspectProfile(name, profilePath);
826
+ if (result.status !== 0
827
+ || result.error
828
+ || (profile.status !== "directory_missing"
829
+ && (profile.status !== "valid"
830
+ || profileMentionsPackage(profile.profile, packageName)))) {
831
+ failure ??= profileMutationFailure(
832
+ name,
833
+ result,
834
+ profile,
835
+ "dsh_bundle_not_removed",
836
+ );
837
+ }
838
+ }
839
+ if (!failure
840
+ && profile.status !== "directory_missing"
841
+ && (profile.status !== "valid"
842
+ || packageNames.some((packageName) => (
843
+ profileMentionsPackage(profile.profile, packageName)
844
+ )))) {
845
+ failure = profileMutationFailure(name, { status: 0 }, profile, "dsh_bundle_not_removed");
846
+ }
847
+ return { profile, failure };
848
+ }
849
+
850
+ function materializeRuntimeBundle(paths, metadata) {
851
+ const manifest = readJsonObject(join(paths.adapterRoot, "package.json"));
852
+ if (manifest?.name !== ADAPTER_PACKAGE_NAME
853
+ || !nonEmpty(manifest.version)
854
+ || manifest.main !== "src/index.mjs"
855
+ || !isDeepStrictEqual(manifest.exports, { ".": "./src/index.mjs" })
856
+ || !isDeepStrictEqual(manifest.files, PROFILE_BUNDLE_FILES)) {
857
+ throw new Error("MemoraX Code DSH adapter source manifest is invalid");
858
+ }
859
+
860
+ const sourceFiles = ["package.json", ...PROFILE_BUNDLE_FILES]
861
+ .filter((relativePath) => relativePath !== PACKAGE_METADATA_FILE)
862
+ .map((relativePath) => {
863
+ const path = bundleSourcePath(paths, relativePath);
864
+ if (!lstatSync(path).isFile()) {
865
+ throw new Error(`MemoraX Code DSH runtime source is not a file: ${relativePath}`);
866
+ }
867
+ return { relativePath, path, content: readFileSync(path) };
868
+ });
869
+ const generation = runtimeGenerationId(metadata, sourceFiles);
870
+ const runtimeBundleRoot = join(paths.runtimeRoot, generation);
871
+ const runtimeMetadata = { ...metadata, runtimeBundleRoot };
872
+ if (runtimeBundleMatches(runtimeBundleRoot, runtimeMetadata, sourceFiles)) {
873
+ return runtimeBundleRoot;
874
+ }
875
+
876
+ mkdirSync(paths.runtimeRoot, { recursive: true, mode: 0o700 });
877
+ rmSync(runtimeBundleRoot, { recursive: true, force: true });
878
+ const temporaryRoot = join(paths.runtimeRoot, `.${generation}.${randomUUID()}.tmp`);
879
+ try {
880
+ mkdirSync(temporaryRoot, { mode: 0o700 });
881
+ for (const { relativePath, path: sourcePath } of sourceFiles) {
882
+ const destinationPath = join(temporaryRoot, relativePath);
883
+ mkdirSync(dirname(destinationPath), { recursive: true, mode: 0o700 });
884
+ copyFileSync(sourcePath, destinationPath);
885
+ }
886
+ atomicWriteJson(join(temporaryRoot, PACKAGE_METADATA_FILE), runtimeMetadata);
887
+ renameSync(temporaryRoot, runtimeBundleRoot);
888
+ return runtimeBundleRoot;
889
+ } catch (error) {
890
+ rmSync(temporaryRoot, { recursive: true, force: true });
891
+ throw error;
892
+ }
893
+ }
894
+
895
+ function runtimeGenerationId(metadata, sourceFiles) {
896
+ const hash = createHash("sha256");
897
+ hash.update(JSON.stringify(metadata));
898
+ for (const { relativePath, content } of sourceFiles) {
899
+ hash.update("\0");
900
+ hash.update(relativePath);
901
+ hash.update("\0");
902
+ hash.update(content);
903
+ }
904
+ return hash.digest("hex");
905
+ }
906
+
907
+ function runtimeBundleMatches(root, metadata, sourceFiles) {
908
+ try {
909
+ if (!lstatSync(root).isDirectory()
910
+ || !isDeepStrictEqual(readJsonObject(join(root, PACKAGE_METADATA_FILE)), metadata)) {
911
+ return false;
912
+ }
913
+ return sourceFiles.every(({ relativePath, content }) => {
914
+ const path = join(root, relativePath);
915
+ return lstatSync(path).isFile() && readFileSync(path).equals(content);
916
+ });
917
+ } catch {
918
+ return false;
919
+ }
920
+ }
921
+
922
+ function bundleSourcePath(paths, relativePath) {
923
+ const commonPrefix = "memorax-code-adapter-common/src/";
924
+ if (relativePath.startsWith(commonPrefix)) {
925
+ return join(commonRoot, relativePath.slice(commonPrefix.length));
926
+ }
927
+ const skillPrefix = "skills/memorax-code/";
928
+ return relativePath.startsWith(skillPrefix)
929
+ ? join(skillRoot, relativePath.slice(skillPrefix.length))
930
+ : join(paths.adapterRoot, relativePath);
931
+ }
932
+
933
+ function cleanupRuntimeGenerations(runtimeRoot, activeRoot) {
934
+ let entries;
935
+ try {
936
+ entries = readdirSync(runtimeRoot, { withFileTypes: true });
937
+ } catch {
938
+ return;
939
+ }
940
+ for (const entry of entries) {
941
+ if (!entry.isDirectory()) continue;
942
+ const candidate = resolve(runtimeRoot, entry.name);
943
+ if (candidate === resolve(activeRoot)) continue;
944
+ try {
945
+ rmSync(candidate, { recursive: true, force: true });
946
+ } catch {
947
+ // The active generation is authoritative; stale cleanup is best effort.
948
+ }
949
+ }
950
+ }
951
+
952
+ function resolvePaths(options) {
953
+ const env = options.env ?? process.env;
954
+ const homeDir = options.homeDir ?? homedir();
955
+ const memoraxCodeHome = resolveHomePath(
956
+ options.memoraxCodeHome ?? nonEmpty(env.MEMORAX_CODE_HOME) ?? join(homeDir, ".memorax-code"),
957
+ homeDir,
958
+ );
959
+ const adapterRoot = resolve(options.adapterRoot ?? ADAPTER_ROOT);
960
+ const statePath = join(memoraxCodeHome, "adapters", RUNTIME, "state.json");
961
+ const runtimeRoot = join(memoraxCodeHome, "adapters", RUNTIME, "runtime", "generations");
962
+ const configuredDshHome = options.dshHome ?? nonEmpty(env.DSH_HOME);
963
+ const dshHome = configuredDshHome === undefined
964
+ ? persistedDshHome({ statePath, memoraxCodeHome, adapterRoot, runtimeRoot }, homeDir)
965
+ ?? resolveHomePath(join(homeDir, ".dsh"), homeDir)
966
+ : resolveHomePath(configuredDshHome, homeDir);
967
+ return {
968
+ env,
969
+ dshHome,
970
+ memoraxCodeHome,
971
+ adapterRoot,
972
+ runtimeRoot,
973
+ profilesRoot: join(dshHome, "profiles"),
974
+ statePath,
975
+ };
976
+ }
977
+
978
+ function persistedDshHome(paths, homeDir) {
979
+ const state = readAdapterState(paths.statePath);
980
+ const value = nonEmpty(state?.dshHome);
981
+ if (!value) return undefined;
982
+ const dshHome = resolveHomePath(value, homeDir);
983
+ return validateState(state, { ...paths, dshHome }) ? undefined : dshHome;
984
+ }
985
+
986
+ function validateState(state, paths) {
987
+ if (!state) return undefined;
988
+ if (state.unreadable) return { ok: false, runtime: RUNTIME, reason: "state_unreadable", statePath: paths.statePath };
989
+ if (state.version !== STATE_VERSION
990
+ || state.runtime !== RUNTIME
991
+ || state.integration !== "plugin"
992
+ || typeof state.enabled !== "boolean"
993
+ || resolve(state.dshHome ?? "") !== paths.dshHome
994
+ || resolve(state.memoraxCodeHome ?? "") !== paths.memoraxCodeHome
995
+ || typeof state.adapterRoot !== "string"
996
+ || !isPathInside(state.runtimeBundleRoot, paths.runtimeRoot)
997
+ || typeof state.memoraxCodeCommand !== "string"
998
+ || typeof state.dshCommand !== "string"
999
+ || !parseDshVersion(state.dshVersion)
1000
+ || !timestampString(state.updatedAt)
1001
+ || !Array.isArray(state.profiles)
1002
+ || !state.profiles.every(validProfileName)) {
1003
+ return { ok: false, runtime: RUNTIME, reason: "state_invalid", statePath: paths.statePath };
1004
+ }
1005
+ return undefined;
1006
+ }
1007
+
1008
+ function disabledState(state, profiles) {
1009
+ return {
1010
+ ...state,
1011
+ enabled: false,
1012
+ profiles,
1013
+ updatedAt: new Date().toISOString(),
1014
+ };
1015
+ }
1016
+
1017
+ function notManaged(paths, action) {
1018
+ return {
1019
+ ok: true,
1020
+ action,
1021
+ runtime: RUNTIME,
1022
+ installed: false,
1023
+ enabled: false,
1024
+ managed: false,
1025
+ skipped: true,
1026
+ reason: "not_managed",
1027
+ };
1028
+ }
1029
+
1030
+ function resolveMemoraxCodeCommand(value) {
1031
+ const configured = nonEmpty(value);
1032
+ const stagedCommand = resolve(ADAPTER_ROOT, "..", "..", "bin", "memorax-code.mjs");
1033
+ const sourceCommand = resolve(
1034
+ ADAPTER_ROOT,
1035
+ "..",
1036
+ "..",
1037
+ "npm",
1038
+ "memorax-code",
1039
+ "bin",
1040
+ "memorax-code.mjs",
1041
+ );
1042
+ return configured && !configured.includes("/") && !configured.includes("\\")
1043
+ ? configured
1044
+ : resolve(configured ?? (existsSync(stagedCommand) ? stagedCommand : sourceCommand));
1045
+ }
1046
+
1047
+ function resolveDshCommand(options, paths, state) {
1048
+ const command = nonEmpty(options.dshCommand)
1049
+ ?? nonEmpty(paths.env.MEMORAX_CODE_DSH_COMMAND)
1050
+ ?? nonEmpty(state?.dshCommand)
1051
+ ?? "dsh";
1052
+ return command.includes("/") || command.includes("\\") ? resolve(command) : command;
1053
+ }
1054
+
1055
+ function runDsh(options, paths, args, command) {
1056
+ const env = { ...paths.env, DSH_HOME: paths.dshHome };
1057
+ let executable;
1058
+ try {
1059
+ executable = resolveWindowsCliInvocation(command, args, {
1060
+ ...options.windowsCliResolution,
1061
+ env,
1062
+ });
1063
+ } catch (error) {
1064
+ return { status: 1, error };
1065
+ }
1066
+ const invocation = {
1067
+ command: executable.command,
1068
+ args: executable.args,
1069
+ // The install watchdog may run a preloaded cleanup after npm has already
1070
+ // removed the adapter files. DSH plugin removal itself only needs DSH_HOME.
1071
+ cwd: existsSync(paths.adapterRoot) ? paths.adapterRoot : paths.dshHome,
1072
+ env,
1073
+ timeout: args.length === 1 && args[0] === "--version"
1074
+ ? DSH_VERSION_TIMEOUT_MS
1075
+ : DEFAULT_COMMAND_TIMEOUT_MS,
1076
+ };
1077
+ if (typeof options.runDsh === "function") {
1078
+ try {
1079
+ return options.runDsh(invocation) ?? { status: 1 };
1080
+ } catch (error) {
1081
+ return { status: 1, error };
1082
+ }
1083
+ }
1084
+ return spawnSync(invocation.command, invocation.args, {
1085
+ cwd: invocation.cwd,
1086
+ env: invocation.env,
1087
+ encoding: "utf8",
1088
+ stdio: ["ignore", "pipe", "pipe"],
1089
+ timeout: invocation.timeout,
1090
+ windowsHide: true,
1091
+ });
1092
+ }
1093
+
1094
+ function inspectDshCompatibility(options, paths, command) {
1095
+ const result = runDsh(options, paths, ["--version"], command);
1096
+ if (result.status !== 0 || result.error) {
1097
+ return {
1098
+ compatible: false,
1099
+ reason: "dsh_version_unavailable",
1100
+ testedDshVersions: [...DSH_TESTED_VERSIONS],
1101
+ };
1102
+ }
1103
+ const output = parseDshVersion(result.stdout);
1104
+ if (!output) {
1105
+ return {
1106
+ compatible: false,
1107
+ reason: "dsh_version_unavailable",
1108
+ testedDshVersions: [...DSH_TESTED_VERSIONS],
1109
+ };
1110
+ }
1111
+ return {
1112
+ compatible: true,
1113
+ dshVersion: output,
1114
+ dshVersionTested: isTestedDshVersion(output),
1115
+ testedDshVersions: [...DSH_TESTED_VERSIONS],
1116
+ };
1117
+ }
1118
+
1119
+ function commandFailure(name, result, contractReason) {
1120
+ return {
1121
+ name,
1122
+ reason: contractReason
1123
+ ? contractReason
1124
+ : /pnpm not found on PATH/i.test(String(result.stderr ?? ""))
1125
+ ? "pnpm_not_found"
1126
+ : result.error?.code === "ENOENT"
1127
+ ? "dsh_not_found"
1128
+ : "dsh_command_failed",
1129
+ status: Number.isInteger(result.status) ? result.status : undefined,
1130
+ };
1131
+ }
1132
+
1133
+ function pluginManagerFailureReason(failures) {
1134
+ return failures.some((failure) => failure.reason === "pnpm_not_found")
1135
+ ? "pnpm_not_found"
1136
+ : undefined;
1137
+ }
1138
+
1139
+ function managedProfileManifestProblem(claimedProfiles) {
1140
+ const profiles = claimedProfiles
1141
+ .filter((profile) => profile.status === "manifest_unreadable")
1142
+ .map((profile) => profile.name);
1143
+ return profiles.length > 0
1144
+ ? {
1145
+ ok: false,
1146
+ action: "dsh-plugin-install",
1147
+ runtime: RUNTIME,
1148
+ reason: "profile_manifest_unreadable",
1149
+ profiles,
1150
+ }
1151
+ : undefined;
1152
+ }
1153
+
1154
+ function profileManifestFailure(name) {
1155
+ return { name, reason: "profile_manifest_unreadable" };
1156
+ }
1157
+
1158
+ function profileMutationFailure(name, result, profile, contractReason) {
1159
+ return result.status === 0 && !result.error && profile.status === "manifest_unreadable"
1160
+ ? profileManifestFailure(name)
1161
+ : commandFailure(name, result, result.status === 0 ? contractReason : undefined);
1162
+ }
1163
+
1164
+ function readProfile(name, path) {
1165
+ const result = inspectProfile(name, path);
1166
+ return result.status === "valid" ? result.profile : undefined;
1167
+ }
1168
+
1169
+ function inspectProfile(name, path) {
1170
+ try {
1171
+ if (!lstatSync(path).isDirectory()) return { status: "manifest_unreadable", name };
1172
+ } catch (error) {
1173
+ return { status: error?.code === "ENOENT" ? "directory_missing" : "manifest_unreadable", name };
1174
+ }
1175
+ try {
1176
+ const manifest = JSON.parse(readFileSync(join(path, "package.json"), "utf8"));
1177
+ const bundles = manifest?.dsh?.profile?.bundles;
1178
+ if (!Array.isArray(bundles) || !bundles.every((value) => typeof value === "string")) {
1179
+ return { status: "manifest_unreadable", name };
1180
+ }
1181
+ return {
1182
+ status: "valid",
1183
+ name,
1184
+ profile: {
1185
+ name,
1186
+ path,
1187
+ dependencies: manifest.dependencies && typeof manifest.dependencies === "object"
1188
+ ? manifest.dependencies
1189
+ : {},
1190
+ bundles,
1191
+ },
1192
+ };
1193
+ } catch {
1194
+ return { status: "manifest_unreadable", name };
1195
+ }
1196
+ }
1197
+
1198
+ function profileMentionsAdapter(profile) {
1199
+ return MANAGED_ADAPTER_PACKAGE_NAMES.some((packageName) => (
1200
+ profileMentionsPackage(profile, packageName)
1201
+ ));
1202
+ }
1203
+
1204
+ function profileMentionsPackage(profile, packageName) {
1205
+ return Boolean(profile
1206
+ && (Object.hasOwn(profile.dependencies, packageName)
1207
+ || profile.bundles.includes(packageName)));
1208
+ }
1209
+
1210
+ function profileHasAdapter(profile, packageName = ADAPTER_PACKAGE_NAME) {
1211
+ return Boolean(profile
1212
+ && Object.hasOwn(profile.dependencies, packageName)
1213
+ && profile.bundles.includes(packageName));
1214
+ }
1215
+
1216
+ function managedProfilesHaveInstalledHeadlessBundle(profiles, managedNames) {
1217
+ return profiles.some((profile) => (
1218
+ managedNames.has(profile.name) && profileHasInstalledHeadlessBundle(profile)
1219
+ ));
1220
+ }
1221
+
1222
+ function profileHasInstalledHeadlessBundle(profile) {
1223
+ if (!profile?.bundles.includes(HEADLESS_BUNDLE_NAME)) return false;
1224
+ try {
1225
+ // DSH exposes built-in bundles through the Profile's shared resolution tree.
1226
+ const requireFromProfile = createRequire(join(profile.path, "package.json"));
1227
+ readFileSync(requireFromProfile.resolve(HEADLESS_BUNDLE_NAME), "utf8");
1228
+ return true;
1229
+ } catch {
1230
+ return false;
1231
+ }
1232
+ }
1233
+
1234
+ function profileHasInstalledAdapter(
1235
+ profile,
1236
+ runtimeBundleRoot,
1237
+ state,
1238
+ ) {
1239
+ if (!state || !nonEmpty(runtimeBundleRoot)) return false;
1240
+ try {
1241
+ const sourceManifest = readJsonObject(join(runtimeBundleRoot, "package.json"));
1242
+ const packageName = sourceManifest?.name;
1243
+ if (!MANAGED_ADAPTER_PACKAGE_NAMES.includes(packageName)
1244
+ || !profileHasAdapter(profile, packageName)
1245
+ || !nonEmpty(sourceManifest.version)) return false;
1246
+
1247
+ const requireFromProfile = createRequire(join(profile.path, "package.json"));
1248
+ const packageRoot = (requireFromProfile.resolve.paths(packageName) ?? [])
1249
+ .map((searchPath) => join(searchPath, packageName))
1250
+ .find((candidate) => existsSync(join(candidate, "package.json")));
1251
+ if (!packageRoot) return false;
1252
+
1253
+ const installedManifest = readJsonObject(join(packageRoot, "package.json"));
1254
+ const sourcePatch = sourceManifest?.dsh?.bundle?.patch;
1255
+ if (installedManifest?.name !== packageName
1256
+ || installedManifest.version !== sourceManifest.version
1257
+ || installedManifest.main !== sourceManifest.main
1258
+ || !isDeepStrictEqual(installedManifest.exports, sourceManifest.exports)
1259
+ || typeof sourcePatch !== "string"
1260
+ || !sourcePatch
1261
+ || installedManifest?.dsh?.bundle?.patch !== sourcePatch) return false;
1262
+
1263
+ readFileSync(join(packageRoot, sourcePatch), "utf8");
1264
+ readFileSync(requireFromProfile.resolve(packageName), "utf8");
1265
+ const authority = requireDshRuntimeAuthority(packageRoot);
1266
+ return authority.enabled === state.enabled
1267
+ && authority.sourceAdapterRoot === resolve(state.adapterRoot)
1268
+ && authority.runtimeBundleRoot === resolve(state.runtimeBundleRoot)
1269
+ && authority.runtimeBundleRoot === resolve(runtimeBundleRoot)
1270
+ && authority.memoraxCodeHome === resolve(state.memoraxCodeHome)
1271
+ && authority.dshHome === resolve(state.dshHome)
1272
+ && authority.memoraxCodeCommand === state.memoraxCodeCommand
1273
+ && authority.dshCommand === state.dshCommand
1274
+ && authority.dshVersion === state.dshVersion
1275
+ && isDeepStrictEqual(authority.profiles, state.profiles)
1276
+ && authority.revision === state.updatedAt;
1277
+ } catch {
1278
+ return false;
1279
+ }
1280
+ }
1281
+
1282
+ function readJsonObject(path) {
1283
+ const value = JSON.parse(readFileSync(path, "utf8"));
1284
+ return value !== null && typeof value === "object" && !Array.isArray(value)
1285
+ ? value
1286
+ : undefined;
1287
+ }
1288
+
1289
+ function runtimeAdapterPackageName(runtimeBundleRoot) {
1290
+ try {
1291
+ const packageName = readJsonObject(join(runtimeBundleRoot, "package.json"))?.name;
1292
+ return MANAGED_ADAPTER_PACKAGE_NAMES.includes(packageName) ? packageName : undefined;
1293
+ } catch {
1294
+ return undefined;
1295
+ }
1296
+ }
1297
+
1298
+ function projectProfileStatus(
1299
+ discoveredProfiles,
1300
+ managedNames,
1301
+ includeUnmanaged,
1302
+ runtimeBundleRoot,
1303
+ state,
1304
+ ) {
1305
+ const profileByName = new Map(discoveredProfiles.map((profile) => [profile.name, profile]));
1306
+ const names = includeUnmanaged
1307
+ ? new Set([...managedNames, ...profileByName.keys()])
1308
+ : managedNames;
1309
+ return [...names]
1310
+ .sort((left, right) => left.localeCompare(right))
1311
+ .map((name) => {
1312
+ const profile = profileByName.get(name);
1313
+ return {
1314
+ name,
1315
+ ...(includeUnmanaged ? { managed: managedNames.has(name) } : {}),
1316
+ exists: Boolean(profile),
1317
+ installed: profileHasInstalledAdapter(profile, runtimeBundleRoot, state),
1318
+ };
1319
+ });
1320
+ }
1321
+
1322
+ function validProfileName(value) {
1323
+ return typeof value === "string"
1324
+ && value.length > 0
1325
+ && value !== "."
1326
+ && value !== ".."
1327
+ && value !== "node_modules"
1328
+ && !value.includes("/")
1329
+ && !value.includes("\\");
1330
+ }
1331
+
1332
+ function resolveHomePath(value, homeDir) {
1333
+ const normalized = String(value);
1334
+ if (normalized === "~") return resolve(homeDir);
1335
+ if (normalized.startsWith("~/") || normalized.startsWith("~\\")) {
1336
+ return resolve(homeDir, normalized.slice(2));
1337
+ }
1338
+ return resolve(normalized);
1339
+ }
1340
+
1341
+ function nonEmpty(value) {
1342
+ const normalized = String(value ?? "").trim();
1343
+ return normalized || undefined;
1344
+ }
1345
+
1346
+ function timestampString(value) {
1347
+ const normalized = nonEmpty(value);
1348
+ return normalized && Number.isFinite(Date.parse(normalized)) ? normalized : undefined;
1349
+ }
1350
+
1351
+ function isPathInside(value, parent) {
1352
+ if (typeof value !== "string" || !value.trim()) return false;
1353
+ const child = relative(resolve(parent), resolve(value));
1354
+ return child !== ""
1355
+ && child !== ".."
1356
+ && !child.startsWith(`..${sep}`)
1357
+ && !isAbsolute(child);
1358
+ }