praxis-agent 0.1.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 (395) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +534 -0
  3. package/dist/application/background-agent-manager.d.ts +66 -0
  4. package/dist/application/background-agent-manager.d.ts.map +1 -0
  5. package/dist/application/background-agent-manager.js +362 -0
  6. package/dist/application/background-agent-manager.js.map +1 -0
  7. package/dist/application/background-bash-manager.d.ts +54 -0
  8. package/dist/application/background-bash-manager.d.ts.map +1 -0
  9. package/dist/application/background-bash-manager.js +354 -0
  10. package/dist/application/background-bash-manager.js.map +1 -0
  11. package/dist/application/background-task-id.d.ts +5 -0
  12. package/dist/application/background-task-id.d.ts.map +1 -0
  13. package/dist/application/background-task-id.js +25 -0
  14. package/dist/application/background-task-id.js.map +1 -0
  15. package/dist/application/claude-project-purge.d.ts +50 -0
  16. package/dist/application/claude-project-purge.d.ts.map +1 -0
  17. package/dist/application/claude-project-purge.js +459 -0
  18. package/dist/application/claude-project-purge.js.map +1 -0
  19. package/dist/application/managed-worktree.d.ts +15 -0
  20. package/dist/application/managed-worktree.d.ts.map +1 -0
  21. package/dist/application/managed-worktree.js +127 -0
  22. package/dist/application/managed-worktree.js.map +1 -0
  23. package/dist/application/model-compactor.d.ts +8 -0
  24. package/dist/application/model-compactor.d.ts.map +1 -0
  25. package/dist/application/model-compactor.js +70 -0
  26. package/dist/application/model-compactor.js.map +1 -0
  27. package/dist/application/scheduled-prompt-manager.d.ts +69 -0
  28. package/dist/application/scheduled-prompt-manager.d.ts.map +1 -0
  29. package/dist/application/scheduled-prompt-manager.js +387 -0
  30. package/dist/application/scheduled-prompt-manager.js.map +1 -0
  31. package/dist/application/session-service.d.ts +142 -0
  32. package/dist/application/session-service.d.ts.map +1 -0
  33. package/dist/application/session-service.js +1690 -0
  34. package/dist/application/session-service.js.map +1 -0
  35. package/dist/application/session-worktree.d.ts +55 -0
  36. package/dist/application/session-worktree.d.ts.map +1 -0
  37. package/dist/application/session-worktree.js +311 -0
  38. package/dist/application/session-worktree.js.map +1 -0
  39. package/dist/application/subagent-service.d.ts +98 -0
  40. package/dist/application/subagent-service.d.ts.map +1 -0
  41. package/dist/application/subagent-service.js +1305 -0
  42. package/dist/application/subagent-service.js.map +1 -0
  43. package/dist/application/tool-use-summary.d.ts +8 -0
  44. package/dist/application/tool-use-summary.d.ts.map +1 -0
  45. package/dist/application/tool-use-summary.js +53 -0
  46. package/dist/application/tool-use-summary.js.map +1 -0
  47. package/dist/application/top-level-agent-manager.d.ts +58 -0
  48. package/dist/application/top-level-agent-manager.d.ts.map +1 -0
  49. package/dist/application/top-level-agent-manager.js +907 -0
  50. package/dist/application/top-level-agent-manager.js.map +1 -0
  51. package/dist/application/workflow-manager.d.ts +61 -0
  52. package/dist/application/workflow-manager.d.ts.map +1 -0
  53. package/dist/application/workflow-manager.js +582 -0
  54. package/dist/application/workflow-manager.js.map +1 -0
  55. package/dist/application/workflow-meta.d.ts +18 -0
  56. package/dist/application/workflow-meta.d.ts.map +1 -0
  57. package/dist/application/workflow-meta.js +146 -0
  58. package/dist/application/workflow-meta.js.map +1 -0
  59. package/dist/application/workflow-runtime.d.ts +29 -0
  60. package/dist/application/workflow-runtime.d.ts.map +1 -0
  61. package/dist/application/workflow-runtime.js +176 -0
  62. package/dist/application/workflow-runtime.js.map +1 -0
  63. package/dist/application/workflow-worktree.d.ts +9 -0
  64. package/dist/application/workflow-worktree.d.ts.map +1 -0
  65. package/dist/application/workflow-worktree.js +11 -0
  66. package/dist/application/workflow-worktree.js.map +1 -0
  67. package/dist/cli/agents-dashboard.d.ts +37 -0
  68. package/dist/cli/agents-dashboard.d.ts.map +1 -0
  69. package/dist/cli/agents-dashboard.js +408 -0
  70. package/dist/cli/agents-dashboard.js.map +1 -0
  71. package/dist/cli/controls.d.ts +15 -0
  72. package/dist/cli/controls.d.ts.map +1 -0
  73. package/dist/cli/controls.js +219 -0
  74. package/dist/cli/controls.js.map +1 -0
  75. package/dist/cli/debug.d.ts +13 -0
  76. package/dist/cli/debug.d.ts.map +1 -0
  77. package/dist/cli/debug.js +37 -0
  78. package/dist/cli/debug.js.map +1 -0
  79. package/dist/cli/interactive.d.ts +63 -0
  80. package/dist/cli/interactive.d.ts.map +1 -0
  81. package/dist/cli/interactive.js +600 -0
  82. package/dist/cli/interactive.js.map +1 -0
  83. package/dist/cli/protocol.d.ts +251 -0
  84. package/dist/cli/protocol.d.ts.map +1 -0
  85. package/dist/cli/protocol.js +2146 -0
  86. package/dist/cli/protocol.js.map +1 -0
  87. package/dist/cli.d.ts +116 -0
  88. package/dist/cli.d.ts.map +1 -0
  89. package/dist/cli.js +3901 -0
  90. package/dist/cli.js.map +1 -0
  91. package/dist/compatibility/claude/compaction.d.ts +19 -0
  92. package/dist/compatibility/claude/compaction.d.ts.map +1 -0
  93. package/dist/compatibility/claude/compaction.js +82 -0
  94. package/dist/compatibility/claude/compaction.js.map +1 -0
  95. package/dist/compatibility/claude/context.d.ts +24 -0
  96. package/dist/compatibility/claude/context.d.ts.map +1 -0
  97. package/dist/compatibility/claude/context.js +92 -0
  98. package/dist/compatibility/claude/context.js.map +1 -0
  99. package/dist/compatibility/claude/dynamic-context.d.ts +20 -0
  100. package/dist/compatibility/claude/dynamic-context.d.ts.map +1 -0
  101. package/dist/compatibility/claude/dynamic-context.js +168 -0
  102. package/dist/compatibility/claude/dynamic-context.js.map +1 -0
  103. package/dist/compatibility/claude/file-history.d.ts +14 -0
  104. package/dist/compatibility/claude/file-history.d.ts.map +1 -0
  105. package/dist/compatibility/claude/file-history.js +340 -0
  106. package/dist/compatibility/claude/file-history.js.map +1 -0
  107. package/dist/compatibility/claude/file-resources.d.ts +28 -0
  108. package/dist/compatibility/claude/file-resources.d.ts.map +1 -0
  109. package/dist/compatibility/claude/file-resources.js +168 -0
  110. package/dist/compatibility/claude/file-resources.js.map +1 -0
  111. package/dist/compatibility/claude/fork.d.ts +9 -0
  112. package/dist/compatibility/claude/fork.d.ts.map +1 -0
  113. package/dist/compatibility/claude/fork.js +225 -0
  114. package/dist/compatibility/claude/fork.js.map +1 -0
  115. package/dist/compatibility/claude/history.d.ts +4 -0
  116. package/dist/compatibility/claude/history.d.ts.map +1 -0
  117. package/dist/compatibility/claude/history.js +86 -0
  118. package/dist/compatibility/claude/history.js.map +1 -0
  119. package/dist/compatibility/claude/ownership.d.ts +87 -0
  120. package/dist/compatibility/claude/ownership.d.ts.map +1 -0
  121. package/dist/compatibility/claude/ownership.js +100 -0
  122. package/dist/compatibility/claude/ownership.js.map +1 -0
  123. package/dist/compatibility/claude/paths.d.ts +17 -0
  124. package/dist/compatibility/claude/paths.d.ts.map +1 -0
  125. package/dist/compatibility/claude/paths.js +54 -0
  126. package/dist/compatibility/claude/paths.js.map +1 -0
  127. package/dist/compatibility/claude/pr-links.d.ts +25 -0
  128. package/dist/compatibility/claude/pr-links.d.ts.map +1 -0
  129. package/dist/compatibility/claude/pr-links.js +80 -0
  130. package/dist/compatibility/claude/pr-links.js.map +1 -0
  131. package/dist/compatibility/claude/projection.d.ts +11 -0
  132. package/dist/compatibility/claude/projection.d.ts.map +1 -0
  133. package/dist/compatibility/claude/projection.js +325 -0
  134. package/dist/compatibility/claude/projection.js.map +1 -0
  135. package/dist/compatibility/claude/schema.d.ts +18 -0
  136. package/dist/compatibility/claude/schema.d.ts.map +1 -0
  137. package/dist/compatibility/claude/schema.js +983 -0
  138. package/dist/compatibility/claude/schema.js.map +1 -0
  139. package/dist/compatibility/claude/shared-resources.d.ts +51 -0
  140. package/dist/compatibility/claude/shared-resources.d.ts.map +1 -0
  141. package/dist/compatibility/claude/shared-resources.js +382 -0
  142. package/dist/compatibility/claude/shared-resources.js.map +1 -0
  143. package/dist/compatibility/claude/sidechain.d.ts +50 -0
  144. package/dist/compatibility/claude/sidechain.d.ts.map +1 -0
  145. package/dist/compatibility/claude/sidechain.js +90 -0
  146. package/dist/compatibility/claude/sidechain.js.map +1 -0
  147. package/dist/compatibility/claude/tool-links.d.ts +13 -0
  148. package/dist/compatibility/claude/tool-links.d.ts.map +1 -0
  149. package/dist/compatibility/claude/tool-links.js +61 -0
  150. package/dist/compatibility/claude/tool-links.js.map +1 -0
  151. package/dist/compatibility/claude/translation.d.ts +71 -0
  152. package/dist/compatibility/claude/translation.d.ts.map +1 -0
  153. package/dist/compatibility/claude/translation.js +443 -0
  154. package/dist/compatibility/claude/translation.js.map +1 -0
  155. package/dist/compatibility/claude/workflow-replay.d.ts +10 -0
  156. package/dist/compatibility/claude/workflow-replay.d.ts.map +1 -0
  157. package/dist/compatibility/claude/workflow-replay.js +66 -0
  158. package/dist/compatibility/claude/workflow-replay.js.map +1 -0
  159. package/dist/compatibility/claude/workflow.d.ts +33 -0
  160. package/dist/compatibility/claude/workflow.d.ts.map +1 -0
  161. package/dist/compatibility/claude/workflow.js +76 -0
  162. package/dist/compatibility/claude/workflow.js.map +1 -0
  163. package/dist/core/compaction.d.ts +16 -0
  164. package/dist/core/compaction.d.ts.map +1 -0
  165. package/dist/core/compaction.js +2 -0
  166. package/dist/core/compaction.js.map +1 -0
  167. package/dist/core/context-budget.d.ts +28 -0
  168. package/dist/core/context-budget.d.ts.map +1 -0
  169. package/dist/core/context-budget.js +108 -0
  170. package/dist/core/context-budget.js.map +1 -0
  171. package/dist/core/context.d.ts +16 -0
  172. package/dist/core/context.d.ts.map +1 -0
  173. package/dist/core/context.js +13 -0
  174. package/dist/core/context.js.map +1 -0
  175. package/dist/core/runtime.d.ts +394 -0
  176. package/dist/core/runtime.d.ts.map +1 -0
  177. package/dist/core/runtime.js +507 -0
  178. package/dist/core/runtime.js.map +1 -0
  179. package/dist/core/usage.d.ts +26 -0
  180. package/dist/core/usage.d.ts.map +1 -0
  181. package/dist/core/usage.js +133 -0
  182. package/dist/core/usage.js.map +1 -0
  183. package/dist/extensions/claude-extension-tools.d.ts +16 -0
  184. package/dist/extensions/claude-extension-tools.d.ts.map +1 -0
  185. package/dist/extensions/claude-extension-tools.js +78 -0
  186. package/dist/extensions/claude-extension-tools.js.map +1 -0
  187. package/dist/extensions/claude-extensions.d.ts +41 -0
  188. package/dist/extensions/claude-extensions.d.ts.map +1 -0
  189. package/dist/extensions/claude-extensions.js +270 -0
  190. package/dist/extensions/claude-extensions.js.map +1 -0
  191. package/dist/hooks/claude-hook-tools.d.ts +26 -0
  192. package/dist/hooks/claude-hook-tools.d.ts.map +1 -0
  193. package/dist/hooks/claude-hook-tools.js +129 -0
  194. package/dist/hooks/claude-hook-tools.js.map +1 -0
  195. package/dist/hooks/claude-hooks.d.ts +87 -0
  196. package/dist/hooks/claude-hooks.d.ts.map +1 -0
  197. package/dist/hooks/claude-hooks.js +411 -0
  198. package/dist/hooks/claude-hooks.js.map +1 -0
  199. package/dist/maintenance/doctor.d.ts +33 -0
  200. package/dist/maintenance/doctor.d.ts.map +1 -0
  201. package/dist/maintenance/doctor.js +391 -0
  202. package/dist/maintenance/doctor.js.map +1 -0
  203. package/dist/maintenance/self-update.d.ts +31 -0
  204. package/dist/maintenance/self-update.d.ts.map +1 -0
  205. package/dist/maintenance/self-update.js +81 -0
  206. package/dist/maintenance/self-update.js.map +1 -0
  207. package/dist/mcp/claude-mcp-management.d.ts +28 -0
  208. package/dist/mcp/claude-mcp-management.d.ts.map +1 -0
  209. package/dist/mcp/claude-mcp-management.js +169 -0
  210. package/dist/mcp/claude-mcp-management.js.map +1 -0
  211. package/dist/mcp/claude-mcp-oauth.d.ts +113 -0
  212. package/dist/mcp/claude-mcp-oauth.d.ts.map +1 -0
  213. package/dist/mcp/claude-mcp-oauth.js +1053 -0
  214. package/dist/mcp/claude-mcp-oauth.js.map +1 -0
  215. package/dist/mcp/claude-mcp-tools.d.ts +98 -0
  216. package/dist/mcp/claude-mcp-tools.d.ts.map +1 -0
  217. package/dist/mcp/claude-mcp-tools.js +1246 -0
  218. package/dist/mcp/claude-mcp-tools.js.map +1 -0
  219. package/dist/mcp/praxis-mcp-server.d.ts +35 -0
  220. package/dist/mcp/praxis-mcp-server.d.ts.map +1 -0
  221. package/dist/mcp/praxis-mcp-server.js +520 -0
  222. package/dist/mcp/praxis-mcp-server.js.map +1 -0
  223. package/dist/permissions/claude-auto-classifier.d.ts +20 -0
  224. package/dist/permissions/claude-auto-classifier.d.ts.map +1 -0
  225. package/dist/permissions/claude-auto-classifier.js +140 -0
  226. package/dist/permissions/claude-auto-classifier.js.map +1 -0
  227. package/dist/permissions/claude-permission-resolver.d.ts +30 -0
  228. package/dist/permissions/claude-permission-resolver.d.ts.map +1 -0
  229. package/dist/permissions/claude-permission-resolver.js +334 -0
  230. package/dist/permissions/claude-permission-resolver.js.map +1 -0
  231. package/dist/persistence/claude-job-store.d.ts +68 -0
  232. package/dist/persistence/claude-job-store.d.ts.map +1 -0
  233. package/dist/persistence/claude-job-store.js +311 -0
  234. package/dist/persistence/claude-job-store.js.map +1 -0
  235. package/dist/persistence/claude-scheduled-task-store.d.ts +29 -0
  236. package/dist/persistence/claude-scheduled-task-store.d.ts.map +1 -0
  237. package/dist/persistence/claude-scheduled-task-store.js +163 -0
  238. package/dist/persistence/claude-scheduled-task-store.js.map +1 -0
  239. package/dist/persistence/claude-sidechain-store.d.ts +18 -0
  240. package/dist/persistence/claude-sidechain-store.d.ts.map +1 -0
  241. package/dist/persistence/claude-sidechain-store.js +107 -0
  242. package/dist/persistence/claude-sidechain-store.js.map +1 -0
  243. package/dist/persistence/claude-task-store.d.ts +72 -0
  244. package/dist/persistence/claude-task-store.d.ts.map +1 -0
  245. package/dist/persistence/claude-task-store.js +440 -0
  246. package/dist/persistence/claude-task-store.js.map +1 -0
  247. package/dist/persistence/claude-transcript-store.d.ts +85 -0
  248. package/dist/persistence/claude-transcript-store.d.ts.map +1 -0
  249. package/dist/persistence/claude-transcript-store.js +385 -0
  250. package/dist/persistence/claude-transcript-store.js.map +1 -0
  251. package/dist/persistence/claude-workflow-store.d.ts +41 -0
  252. package/dist/persistence/claude-workflow-store.d.ts.map +1 -0
  253. package/dist/persistence/claude-workflow-store.js +263 -0
  254. package/dist/persistence/claude-workflow-store.js.map +1 -0
  255. package/dist/persistence/in-memory-sidechain-store.d.ts +12 -0
  256. package/dist/persistence/in-memory-sidechain-store.d.ts.map +1 -0
  257. package/dist/persistence/in-memory-sidechain-store.js +30 -0
  258. package/dist/persistence/in-memory-sidechain-store.js.map +1 -0
  259. package/dist/persistence/in-memory-transcript-store.d.ts +15 -0
  260. package/dist/persistence/in-memory-transcript-store.d.ts.map +1 -0
  261. package/dist/persistence/in-memory-transcript-store.js +95 -0
  262. package/dist/persistence/in-memory-transcript-store.js.map +1 -0
  263. package/dist/platform/atomic-write.d.ts +5 -0
  264. package/dist/platform/atomic-write.d.ts.map +1 -0
  265. package/dist/platform/atomic-write.js +25 -0
  266. package/dist/platform/atomic-write.js.map +1 -0
  267. package/dist/platform/bounded-process-runner.d.ts +28 -0
  268. package/dist/platform/bounded-process-runner.d.ts.map +1 -0
  269. package/dist/platform/bounded-process-runner.js +209 -0
  270. package/dist/platform/bounded-process-runner.js.map +1 -0
  271. package/dist/platform/claude-version.d.ts +5 -0
  272. package/dist/platform/claude-version.d.ts.map +1 -0
  273. package/dist/platform/claude-version.js +17 -0
  274. package/dist/platform/claude-version.js.map +1 -0
  275. package/dist/platform/command-shell.d.ts +3 -0
  276. package/dist/platform/command-shell.d.ts.map +1 -0
  277. package/dist/platform/command-shell.js +15 -0
  278. package/dist/platform/command-shell.js.map +1 -0
  279. package/dist/platform/exclusive-file-lease.d.ts +15 -0
  280. package/dist/platform/exclusive-file-lease.d.ts.map +1 -0
  281. package/dist/platform/exclusive-file-lease.js +265 -0
  282. package/dist/platform/exclusive-file-lease.js.map +1 -0
  283. package/dist/platform/sensitive-data.d.ts +8 -0
  284. package/dist/platform/sensitive-data.d.ts.map +1 -0
  285. package/dist/platform/sensitive-data.js +138 -0
  286. package/dist/platform/sensitive-data.js.map +1 -0
  287. package/dist/platform/tmux-worktree.d.ts +30 -0
  288. package/dist/platform/tmux-worktree.d.ts.map +1 -0
  289. package/dist/platform/tmux-worktree.js +138 -0
  290. package/dist/platform/tmux-worktree.js.map +1 -0
  291. package/dist/plugins/claude-plugin-eval-discovery.d.ts +20 -0
  292. package/dist/plugins/claude-plugin-eval-discovery.d.ts.map +1 -0
  293. package/dist/plugins/claude-plugin-eval-discovery.js +128 -0
  294. package/dist/plugins/claude-plugin-eval-discovery.js.map +1 -0
  295. package/dist/plugins/claude-plugin-eval-graders.d.ts +49 -0
  296. package/dist/plugins/claude-plugin-eval-graders.d.ts.map +1 -0
  297. package/dist/plugins/claude-plugin-eval-graders.js +137 -0
  298. package/dist/plugins/claude-plugin-eval-graders.js.map +1 -0
  299. package/dist/plugins/claude-plugin-eval-runner.d.ts +50 -0
  300. package/dist/plugins/claude-plugin-eval-runner.d.ts.map +1 -0
  301. package/dist/plugins/claude-plugin-eval-runner.js +189 -0
  302. package/dist/plugins/claude-plugin-eval-runner.js.map +1 -0
  303. package/dist/plugins/claude-plugin-eval-schema.d.ts +80 -0
  304. package/dist/plugins/claude-plugin-eval-schema.d.ts.map +1 -0
  305. package/dist/plugins/claude-plugin-eval-schema.js +436 -0
  306. package/dist/plugins/claude-plugin-eval-schema.js.map +1 -0
  307. package/dist/plugins/claude-plugin-eval.d.ts +66 -0
  308. package/dist/plugins/claude-plugin-eval.d.ts.map +1 -0
  309. package/dist/plugins/claude-plugin-eval.js +438 -0
  310. package/dist/plugins/claude-plugin-eval.js.map +1 -0
  311. package/dist/plugins/claude-plugin-maintenance.d.ts +39 -0
  312. package/dist/plugins/claude-plugin-maintenance.d.ts.map +1 -0
  313. package/dist/plugins/claude-plugin-maintenance.js +336 -0
  314. package/dist/plugins/claude-plugin-maintenance.js.map +1 -0
  315. package/dist/plugins/claude-plugin-marketplace.d.ts +97 -0
  316. package/dist/plugins/claude-plugin-marketplace.d.ts.map +1 -0
  317. package/dist/plugins/claude-plugin-marketplace.js +1433 -0
  318. package/dist/plugins/claude-plugin-marketplace.js.map +1 -0
  319. package/dist/plugins/claude-plugin-mcpb.d.ts +71 -0
  320. package/dist/plugins/claude-plugin-mcpb.d.ts.map +1 -0
  321. package/dist/plugins/claude-plugin-mcpb.js +885 -0
  322. package/dist/plugins/claude-plugin-mcpb.js.map +1 -0
  323. package/dist/plugins/claude-plugin-runtime.d.ts +120 -0
  324. package/dist/plugins/claude-plugin-runtime.d.ts.map +1 -0
  325. package/dist/plugins/claude-plugin-runtime.js +1263 -0
  326. package/dist/plugins/claude-plugin-runtime.js.map +1 -0
  327. package/dist/providers/anthropic-compatible.d.ts +35 -0
  328. package/dist/providers/anthropic-compatible.d.ts.map +1 -0
  329. package/dist/providers/anthropic-compatible.js +733 -0
  330. package/dist/providers/anthropic-compatible.js.map +1 -0
  331. package/dist/providers/environment.d.ts +14 -0
  332. package/dist/providers/environment.d.ts.map +1 -0
  333. package/dist/providers/environment.js +67 -0
  334. package/dist/providers/environment.js.map +1 -0
  335. package/dist/providers/fallback-provider.d.ts +17 -0
  336. package/dist/providers/fallback-provider.d.ts.map +1 -0
  337. package/dist/providers/fallback-provider.js +70 -0
  338. package/dist/providers/fallback-provider.js.map +1 -0
  339. package/dist/providers/openai-compatible.d.ts +30 -0
  340. package/dist/providers/openai-compatible.d.ts.map +1 -0
  341. package/dist/providers/openai-compatible.js +409 -0
  342. package/dist/providers/openai-compatible.js.map +1 -0
  343. package/dist/tools/claude-interactive-tools.d.ts +58 -0
  344. package/dist/tools/claude-interactive-tools.d.ts.map +1 -0
  345. package/dist/tools/claude-interactive-tools.js +390 -0
  346. package/dist/tools/claude-interactive-tools.js.map +1 -0
  347. package/dist/tools/claude-lsp-formatters.d.ts +2 -0
  348. package/dist/tools/claude-lsp-formatters.d.ts.map +1 -0
  349. package/dist/tools/claude-lsp-formatters.js +291 -0
  350. package/dist/tools/claude-lsp-formatters.js.map +1 -0
  351. package/dist/tools/claude-lsp-tool.d.ts +22 -0
  352. package/dist/tools/claude-lsp-tool.d.ts.map +1 -0
  353. package/dist/tools/claude-lsp-tool.js +744 -0
  354. package/dist/tools/claude-lsp-tool.js.map +1 -0
  355. package/dist/tools/claude-scheduled-tools.d.ts +20 -0
  356. package/dist/tools/claude-scheduled-tools.d.ts.map +1 -0
  357. package/dist/tools/claude-scheduled-tools.js +344 -0
  358. package/dist/tools/claude-scheduled-tools.js.map +1 -0
  359. package/dist/tools/claude-task-tools.d.ts +30 -0
  360. package/dist/tools/claude-task-tools.d.ts.map +1 -0
  361. package/dist/tools/claude-task-tools.js +503 -0
  362. package/dist/tools/claude-task-tools.js.map +1 -0
  363. package/dist/tools/claude-user-message.d.ts +16 -0
  364. package/dist/tools/claude-user-message.d.ts.map +1 -0
  365. package/dist/tools/claude-user-message.js +98 -0
  366. package/dist/tools/claude-user-message.js.map +1 -0
  367. package/dist/tools/claude-workflow-tools.d.ts +27 -0
  368. package/dist/tools/claude-workflow-tools.d.ts.map +1 -0
  369. package/dist/tools/claude-workflow-tools.js +257 -0
  370. package/dist/tools/claude-workflow-tools.js.map +1 -0
  371. package/dist/tools/claude-worktree-tools.d.ts +17 -0
  372. package/dist/tools/claude-worktree-tools.d.ts.map +1 -0
  373. package/dist/tools/claude-worktree-tools.js +135 -0
  374. package/dist/tools/claude-worktree-tools.js.map +1 -0
  375. package/dist/tools/filtered-tool-registry.d.ts +16 -0
  376. package/dist/tools/filtered-tool-registry.d.ts.map +1 -0
  377. package/dist/tools/filtered-tool-registry.js +51 -0
  378. package/dist/tools/filtered-tool-registry.js.map +1 -0
  379. package/dist/tools/glob.d.ts +9 -0
  380. package/dist/tools/glob.d.ts.map +1 -0
  381. package/dist/tools/glob.js +88 -0
  382. package/dist/tools/glob.js.map +1 -0
  383. package/dist/tools/local-tools.d.ts +47 -0
  384. package/dist/tools/local-tools.d.ts.map +1 -0
  385. package/dist/tools/local-tools.js +1082 -0
  386. package/dist/tools/local-tools.js.map +1 -0
  387. package/dist/tools/notebook.d.ts +22 -0
  388. package/dist/tools/notebook.d.ts.map +1 -0
  389. package/dist/tools/notebook.js +154 -0
  390. package/dist/tools/notebook.js.map +1 -0
  391. package/dist/tools/web.d.ts +48 -0
  392. package/dist/tools/web.d.ts.map +1 -0
  393. package/dist/tools/web.js +726 -0
  394. package/dist/tools/web.js.map +1 -0
  395. package/package.json +145 -0
@@ -0,0 +1,1053 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { createServer } from 'node:http';
3
+ import { execFile, spawn } from 'node:child_process';
4
+ import { readFile, rm } from 'node:fs/promises';
5
+ import { homedir, userInfo } from 'node:os';
6
+ import { isAbsolute, join, resolve } from 'node:path';
7
+ import { promisify } from 'node:util';
8
+ import { auth, } from '@modelcontextprotocol/sdk/client/auth.js';
9
+ import { writeFileAtomically } from '../platform/atomic-write.js';
10
+ import { ExclusiveFileLease } from '../platform/exclusive-file-lease.js';
11
+ const execFileAsync = promisify(execFile);
12
+ const CREDENTIAL_SERVICE = 'Claude Code-credentials';
13
+ const MAX_CREDENTIAL_BYTES = 2 * 1024 * 1024;
14
+ const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
15
+ const LOCK_WAIT_MS = 10_000;
16
+ function isRecord(value) {
17
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
18
+ }
19
+ function stringValue(value) {
20
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
21
+ }
22
+ function validExpiresAt(value) {
23
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
24
+ }
25
+ function normalizeRecord(value) {
26
+ if (!isRecord(value))
27
+ return undefined;
28
+ const serverName = stringValue(value.serverName);
29
+ const serverUrl = stringValue(value.serverUrl);
30
+ if (!serverName || !serverUrl)
31
+ return undefined;
32
+ const discovery = isRecord(value.discoveryState)
33
+ ? {
34
+ ...(stringValue(value.discoveryState.authorizationServerUrl)
35
+ ? {
36
+ authorizationServerUrl: value.discoveryState
37
+ .authorizationServerUrl,
38
+ }
39
+ : {}),
40
+ ...(typeof value.discoveryState.oauthMetadataFound === 'boolean'
41
+ ? { oauthMetadataFound: value.discoveryState.oauthMetadataFound }
42
+ : {}),
43
+ }
44
+ : undefined;
45
+ return {
46
+ serverName,
47
+ serverUrl,
48
+ accessToken: stringValue(value.accessToken) ?? '',
49
+ ...(discovery === undefined ? {} : { discoveryState: discovery }),
50
+ ...(stringValue(value.clientId)
51
+ ? { clientId: value.clientId }
52
+ : {}),
53
+ ...(stringValue(value.clientSecret)
54
+ ? { clientSecret: value.clientSecret }
55
+ : {}),
56
+ ...(stringValue(value.redirectUri)
57
+ ? { redirectUri: value.redirectUri }
58
+ : {}),
59
+ ...(stringValue(value.refreshToken)
60
+ ? { refreshToken: value.refreshToken }
61
+ : {}),
62
+ ...(validExpiresAt(value.expiresAt) === undefined
63
+ ? {}
64
+ : { expiresAt: value.expiresAt }),
65
+ ...(stringValue(value.scope) ? { scope: value.scope } : {}),
66
+ };
67
+ }
68
+ function stableServerConfig(server) {
69
+ return JSON.stringify({
70
+ type: server.type,
71
+ url: server.url,
72
+ headers: server.headers ?? {},
73
+ });
74
+ }
75
+ export function mcpOAuthRecordKey(server) {
76
+ const digest = createHash('sha256')
77
+ .update(stableServerConfig(server))
78
+ .digest('hex')
79
+ .slice(0, 16);
80
+ return `${server.name}|${digest}`;
81
+ }
82
+ export function mcpOAuthCredentialService(configRoot) {
83
+ const canonical = resolve(configRoot);
84
+ const defaultRoot = resolve(join(homedir(), '.claude'));
85
+ if (canonical === defaultRoot)
86
+ return CREDENTIAL_SERVICE;
87
+ const digest = createHash('sha256')
88
+ .update(canonical)
89
+ .digest('hex')
90
+ .slice(0, 8);
91
+ return `${CREDENTIAL_SERVICE}-${digest}`;
92
+ }
93
+ function credentialPath(configRoot) {
94
+ return join(resolve(configRoot), '.credentials.json');
95
+ }
96
+ function parseEnvelope(source) {
97
+ let value;
98
+ try {
99
+ value = JSON.parse(source);
100
+ }
101
+ catch (error) {
102
+ throw new Error('Invalid MCP OAuth credential JSON', { cause: error });
103
+ }
104
+ if (!isRecord(value))
105
+ throw new Error('MCP OAuth credentials must be an object');
106
+ const envelope = { ...value };
107
+ if (envelope.mcpOAuth !== undefined && !isRecord(envelope.mcpOAuth)) {
108
+ throw new Error('MCP OAuth credentials mcpOAuth must be an object');
109
+ }
110
+ return envelope;
111
+ }
112
+ function sourceHash(source) {
113
+ return createHash('sha256').update(source).digest('hex');
114
+ }
115
+ function isSourceHash(value) {
116
+ return typeof value === 'string' && /^[a-f0-9]{64}$/u.test(value);
117
+ }
118
+ function parsePluginSecretTransactionJournal(value) {
119
+ if (!isRecord(value) ||
120
+ value.version !== 1 ||
121
+ typeof value.settingsPath !== 'string' ||
122
+ !isAbsolute(value.settingsPath) ||
123
+ !(value.settingsBeforeHash === null ||
124
+ isSourceHash(value.settingsBeforeHash)) ||
125
+ !isSourceHash(value.settingsAfterHash) ||
126
+ !isRecord(value.snapshot) ||
127
+ !isRecord(value.snapshot.envelope) ||
128
+ (value.snapshot.fileSource !== undefined &&
129
+ typeof value.snapshot.fileSource !== 'string') ||
130
+ (value.snapshot.keychainEnvelope !== undefined &&
131
+ !isRecord(value.snapshot.keychainEnvelope)) ||
132
+ !isRecord(value.nextEnvelope)) {
133
+ throw new Error('Invalid plugin configuration transaction journal');
134
+ }
135
+ return value;
136
+ }
137
+ function credentialAccount() {
138
+ try {
139
+ return userInfo().username;
140
+ }
141
+ catch {
142
+ return process.env.USER ?? 'user';
143
+ }
144
+ }
145
+ async function readFileEnvelope(path) {
146
+ try {
147
+ const source = await readFile(path, 'utf8');
148
+ if (Buffer.byteLength(source) > MAX_CREDENTIAL_BYTES) {
149
+ throw new Error('MCP OAuth credential file exceeds 2 MiB');
150
+ }
151
+ return parseEnvelope(source);
152
+ }
153
+ catch (error) {
154
+ if (error.code === 'ENOENT')
155
+ return {};
156
+ throw error;
157
+ }
158
+ }
159
+ async function readFileEnvelopeSource(path) {
160
+ try {
161
+ const source = await readFile(path, 'utf8');
162
+ if (Buffer.byteLength(source) > MAX_CREDENTIAL_BYTES) {
163
+ throw new Error('MCP OAuth credential file exceeds 2 MiB');
164
+ }
165
+ return source;
166
+ }
167
+ catch (error) {
168
+ if (error.code === 'ENOENT')
169
+ return undefined;
170
+ throw error;
171
+ }
172
+ }
173
+ async function readKeychainEnvelope(service) {
174
+ try {
175
+ const result = await execFileAsync('security', ['find-generic-password', '-a', credentialAccount(), '-s', service, '-w'], { maxBuffer: MAX_CREDENTIAL_BYTES });
176
+ return parseEnvelope(result.stdout);
177
+ }
178
+ catch (error) {
179
+ const code = error.code;
180
+ const detail = [
181
+ String(error),
182
+ String(error.stderr ?? ''),
183
+ ].join('\n');
184
+ if (code === 'ENOENT' ||
185
+ /could not be found|SecKeychainSearchCopyNext/u.test(detail)) {
186
+ return undefined;
187
+ }
188
+ throw error;
189
+ }
190
+ }
191
+ async function writeKeychainEnvelope(service, envelope) {
192
+ const source = JSON.stringify(envelope);
193
+ if (Buffer.byteLength(source) > MAX_CREDENTIAL_BYTES) {
194
+ throw new Error('MCP OAuth credentials exceed 2 MiB');
195
+ }
196
+ await new Promise((resolvePromise, rejectPromise) => {
197
+ const child = spawn('security', [
198
+ 'add-generic-password',
199
+ '-U',
200
+ '-a',
201
+ credentialAccount(),
202
+ '-s',
203
+ service,
204
+ '-w',
205
+ ], { stdio: ['pipe', 'ignore', 'ignore'] });
206
+ let settled = false;
207
+ const rejectSafely = (code) => {
208
+ if (settled)
209
+ return;
210
+ settled = true;
211
+ const error = new Error('Unable to update macOS Keychain credentials');
212
+ if (code !== undefined)
213
+ error.code = code;
214
+ rejectPromise(error);
215
+ };
216
+ child.once('error', (error) => rejectSafely(error.code));
217
+ child.once('close', (code) => {
218
+ if (settled)
219
+ return;
220
+ settled = true;
221
+ if (code === 0)
222
+ resolvePromise();
223
+ else
224
+ rejectPromise(new Error('Unable to update macOS Keychain credentials'));
225
+ });
226
+ child.stdin.once('error', () => rejectSafely());
227
+ child.stdin.end(`${source}\n`);
228
+ });
229
+ }
230
+ async function deleteKeychainEnvelope(service) {
231
+ try {
232
+ await execFileAsync('security', ['delete-generic-password', '-a', credentialAccount(), '-s', service], { maxBuffer: MAX_CREDENTIAL_BYTES });
233
+ }
234
+ catch (error) {
235
+ const code = error.code;
236
+ const detail = [
237
+ String(error),
238
+ String(error.stderr ?? ''),
239
+ ].join('\n');
240
+ if (code === 'ENOENT' ||
241
+ /could not be found|SecKeychainSearchCopyNext/u.test(detail)) {
242
+ return;
243
+ }
244
+ throw error;
245
+ }
246
+ }
247
+ export class ClaudeMcpOAuthStore {
248
+ configRoot;
249
+ useKeychain;
250
+ service;
251
+ lease;
252
+ constructor(options) {
253
+ this.configRoot = resolve(options.configRoot);
254
+ this.useKeychain =
255
+ options.useKeychain ??
256
+ (process.platform === 'darwin' &&
257
+ process.env.PRAXIS_MCP_OAUTH_STORE !== 'file');
258
+ this.service = mcpOAuthCredentialService(this.configRoot);
259
+ this.lease = new ExclusiveFileLease(join(this.configRoot, '.mcp-oauth.lock'));
260
+ }
261
+ async read(server) {
262
+ return this.withLock(async () => {
263
+ const envelope = await this.readEnvelope();
264
+ return normalizeRecord(envelope.mcpOAuth?.[mcpOAuthRecordKey(server)]);
265
+ });
266
+ }
267
+ async mutate(server, update) {
268
+ return this.withLock(async () => {
269
+ const envelope = await this.readEnvelope();
270
+ const entries = isRecord(envelope.mcpOAuth)
271
+ ? { ...envelope.mcpOAuth }
272
+ : {};
273
+ const key = mcpOAuthRecordKey(server);
274
+ const current = normalizeRecord(entries[key]);
275
+ const next = update(current);
276
+ if (next === undefined)
277
+ delete entries[key];
278
+ else
279
+ entries[key] = next;
280
+ if (Object.keys(entries).length === 0)
281
+ delete envelope.mcpOAuth;
282
+ else
283
+ envelope.mcpOAuth = entries;
284
+ await this.writeEnvelope(envelope);
285
+ return next;
286
+ });
287
+ }
288
+ async clear(server) {
289
+ await this.mutate(server, () => undefined);
290
+ }
291
+ async readClientSecret(server) {
292
+ return this.withLock(async () => {
293
+ const envelope = await this.readEnvelope();
294
+ return stringValue(envelope.mcpOAuthClientConfig?.[mcpOAuthRecordKey(server)]
295
+ ?.clientSecret);
296
+ });
297
+ }
298
+ async saveClientSecret(server, clientSecret) {
299
+ await this.withLock(async () => {
300
+ const envelope = await this.readEnvelope();
301
+ const existing = isRecord(envelope.mcpOAuthClientConfig)
302
+ ? { ...envelope.mcpOAuthClientConfig }
303
+ : {};
304
+ const key = mcpOAuthRecordKey(server);
305
+ const current = isRecord(existing[key]) ? existing[key] : {};
306
+ existing[key] = { ...current, clientSecret };
307
+ envelope.mcpOAuthClientConfig = existing;
308
+ await this.writeEnvelope(envelope);
309
+ });
310
+ }
311
+ async readPluginSecrets(pluginId) {
312
+ return this.withLock(async () => {
313
+ const envelope = await this.readEnvelope();
314
+ const stored = envelope.pluginSecrets?.[pluginId];
315
+ if (!isRecord(stored))
316
+ return {};
317
+ return Object.fromEntries(Object.entries(stored).filter((entry) => typeof entry[1] === 'string'));
318
+ });
319
+ }
320
+ async updatePluginSecrets(pluginId, values, removeKeys = []) {
321
+ await this.withLock(async () => {
322
+ const envelope = await this.readEnvelope();
323
+ const all = isRecord(envelope.pluginSecrets)
324
+ ? { ...envelope.pluginSecrets }
325
+ : {};
326
+ const stored = all[pluginId];
327
+ const current = isRecord(stored)
328
+ ? Object.fromEntries(Object.entries(stored).filter((entry) => typeof entry[1] === 'string'))
329
+ : {};
330
+ const before = JSON.stringify(current);
331
+ for (const key of removeKeys)
332
+ delete current[key];
333
+ Object.assign(current, values);
334
+ if (JSON.stringify(current) === before)
335
+ return;
336
+ if (Object.keys(current).length === 0)
337
+ delete all[pluginId];
338
+ else
339
+ all[pluginId] = current;
340
+ if (Object.keys(all).length === 0)
341
+ delete envelope.pluginSecrets;
342
+ else {
343
+ envelope.pluginSecrets = all;
344
+ }
345
+ await this.writeEnvelope(envelope);
346
+ });
347
+ }
348
+ async updatePluginSecretsTransaction(updates, prepareCommit) {
349
+ await this.withLock(async () => {
350
+ const snapshot = await this.captureCredentialSnapshot();
351
+ const original = snapshot.envelope;
352
+ const all = isRecord(original.pluginSecrets)
353
+ ? { ...original.pluginSecrets }
354
+ : {};
355
+ let changed = false;
356
+ for (const update of updates) {
357
+ const stored = all[update.pluginId];
358
+ const current = isRecord(stored)
359
+ ? Object.fromEntries(Object.entries(stored).filter((entry) => typeof entry[1] === 'string'))
360
+ : {};
361
+ const before = JSON.stringify(current);
362
+ for (const key of update.removeKeys ?? [])
363
+ delete current[key];
364
+ Object.assign(current, update.values);
365
+ if (JSON.stringify(current) === before)
366
+ continue;
367
+ changed = true;
368
+ if (Object.keys(current).length === 0)
369
+ delete all[update.pluginId];
370
+ else
371
+ all[update.pluginId] = current;
372
+ }
373
+ const next = { ...original };
374
+ if (Object.keys(all).length === 0)
375
+ delete next.pluginSecrets;
376
+ else {
377
+ next.pluginSecrets = all;
378
+ }
379
+ const commit = await prepareCommit();
380
+ const journal = {
381
+ version: 1,
382
+ settingsPath: resolve(commit.path),
383
+ settingsBeforeHash: commit.beforeSource === undefined
384
+ ? null
385
+ : sourceHash(commit.beforeSource),
386
+ settingsAfterHash: sourceHash(commit.afterSource),
387
+ snapshot,
388
+ nextEnvelope: next,
389
+ };
390
+ try {
391
+ if (changed) {
392
+ await this.writePluginSecretTransactionJournal(journal);
393
+ await this.writeEnvelope(next);
394
+ }
395
+ const committed = await writeFileAtomically(journal.settingsPath, commit.afterSource);
396
+ if (!committed)
397
+ throw new Error('Plugin settings write was interrupted');
398
+ if (changed)
399
+ await this.deletePluginSecretTransactionJournal();
400
+ }
401
+ catch (error) {
402
+ if (changed) {
403
+ try {
404
+ await this.recoverPluginSecretTransaction(journal);
405
+ }
406
+ catch (rollbackError) {
407
+ throw new AggregateError([error, rollbackError], 'Plugin configuration failed and credential rollback failed');
408
+ }
409
+ }
410
+ throw error;
411
+ }
412
+ });
413
+ }
414
+ async deletePluginSecrets(pluginId) {
415
+ await this.withLock(async () => {
416
+ const envelope = await this.readEnvelope();
417
+ if (!isRecord(envelope.pluginSecrets))
418
+ return;
419
+ const prefix = `${pluginId}/`;
420
+ const remaining = Object.fromEntries(Object.entries(envelope.pluginSecrets).filter(([key]) => key !== pluginId && !key.startsWith(prefix)));
421
+ if (Object.keys(remaining).length ===
422
+ Object.keys(envelope.pluginSecrets).length)
423
+ return;
424
+ if (Object.keys(remaining).length === 0)
425
+ delete envelope.pluginSecrets;
426
+ else {
427
+ envelope.pluginSecrets = remaining;
428
+ }
429
+ await this.writeEnvelope(envelope);
430
+ });
431
+ }
432
+ async readEnvelope() {
433
+ if (this.useKeychain) {
434
+ const keychain = await readKeychainEnvelope(this.service);
435
+ if (keychain !== undefined)
436
+ return keychain;
437
+ }
438
+ return readFileEnvelope(credentialPath(this.configRoot));
439
+ }
440
+ pluginSecretTransactionPath() {
441
+ return join(this.configRoot, 'praxis', 'transactions', 'plugin-config.json');
442
+ }
443
+ pluginSecretTransactionService() {
444
+ return `${this.service}-plugin-config-transaction`;
445
+ }
446
+ async readPluginSecretTransactionJournal() {
447
+ if (this.useKeychain) {
448
+ const stored = await readKeychainEnvelope(this.pluginSecretTransactionService());
449
+ if (stored !== undefined)
450
+ return parsePluginSecretTransactionJournal(stored);
451
+ }
452
+ try {
453
+ const source = await readFile(this.pluginSecretTransactionPath(), 'utf8');
454
+ if (Buffer.byteLength(source) > MAX_CREDENTIAL_BYTES) {
455
+ throw new Error('Plugin configuration transaction exceeds 2 MiB');
456
+ }
457
+ return parsePluginSecretTransactionJournal(JSON.parse(source));
458
+ }
459
+ catch (error) {
460
+ if (error.code === 'ENOENT')
461
+ return undefined;
462
+ if (error instanceof SyntaxError) {
463
+ throw new Error('Invalid plugin configuration transaction journal', {
464
+ cause: error,
465
+ });
466
+ }
467
+ throw error;
468
+ }
469
+ }
470
+ async writePluginSecretTransactionJournal(journal) {
471
+ const source = `${JSON.stringify(journal, null, 2)}\n`;
472
+ if (Buffer.byteLength(source) > MAX_CREDENTIAL_BYTES) {
473
+ throw new Error('Plugin configuration transaction exceeds 2 MiB');
474
+ }
475
+ if (this.useKeychain) {
476
+ try {
477
+ await writeKeychainEnvelope(this.pluginSecretTransactionService(), journal);
478
+ await rm(this.pluginSecretTransactionPath(), { force: true });
479
+ return;
480
+ }
481
+ catch (error) {
482
+ if (error.code !== 'ENOENT')
483
+ throw error;
484
+ }
485
+ }
486
+ const committed = await writeFileAtomically(this.pluginSecretTransactionPath(), source);
487
+ if (!committed)
488
+ throw new Error('Plugin configuration transaction write was interrupted');
489
+ }
490
+ async deletePluginSecretTransactionJournal() {
491
+ const failures = [];
492
+ if (this.useKeychain) {
493
+ try {
494
+ await deleteKeychainEnvelope(this.pluginSecretTransactionService());
495
+ }
496
+ catch (error) {
497
+ failures.push(error);
498
+ }
499
+ }
500
+ try {
501
+ await rm(this.pluginSecretTransactionPath(), { force: true });
502
+ }
503
+ catch (error) {
504
+ failures.push(error);
505
+ }
506
+ if (failures.length === 1)
507
+ throw failures[0];
508
+ if (failures.length > 1) {
509
+ throw new AggregateError(failures, 'Plugin configuration transaction cleanup failed');
510
+ }
511
+ }
512
+ async recoverPluginSecretTransaction(knownJournal) {
513
+ const journal = knownJournal ?? (await this.readPluginSecretTransactionJournal());
514
+ if (journal === undefined)
515
+ return;
516
+ let settingsSource;
517
+ try {
518
+ settingsSource = await readFile(journal.settingsPath, 'utf8');
519
+ }
520
+ catch (error) {
521
+ const code = error.code;
522
+ if (code !== 'ENOENT' && code !== 'ENOTDIR')
523
+ throw error;
524
+ }
525
+ const settingsHash = settingsSource === undefined ? null : sourceHash(settingsSource);
526
+ if (settingsHash === journal.settingsAfterHash) {
527
+ await this.writeEnvelope(journal.nextEnvelope);
528
+ }
529
+ else if (settingsHash === journal.settingsBeforeHash) {
530
+ await this.restoreCredentialSnapshot(journal.snapshot);
531
+ }
532
+ else {
533
+ throw new Error(`Plugin configuration transaction conflicts with ${journal.settingsPath}`);
534
+ }
535
+ await this.deletePluginSecretTransactionJournal();
536
+ }
537
+ async captureCredentialSnapshot() {
538
+ const fileSource = await readFileEnvelopeSource(credentialPath(this.configRoot));
539
+ if (!this.useKeychain) {
540
+ return {
541
+ envelope: fileSource === undefined ? {} : parseEnvelope(fileSource),
542
+ ...(fileSource === undefined ? {} : { fileSource }),
543
+ };
544
+ }
545
+ const keychainEnvelope = await readKeychainEnvelope(this.service);
546
+ return {
547
+ envelope: keychainEnvelope ??
548
+ (fileSource === undefined ? {} : parseEnvelope(fileSource)),
549
+ ...(fileSource === undefined ? {} : { fileSource }),
550
+ ...(keychainEnvelope === undefined ? {} : { keychainEnvelope }),
551
+ };
552
+ }
553
+ async restoreCredentialSnapshot(snapshot) {
554
+ const failures = [];
555
+ if (this.useKeychain) {
556
+ try {
557
+ if (snapshot.keychainEnvelope === undefined) {
558
+ await deleteKeychainEnvelope(this.service);
559
+ }
560
+ else {
561
+ await writeKeychainEnvelope(this.service, snapshot.keychainEnvelope);
562
+ }
563
+ }
564
+ catch (error) {
565
+ failures.push(error);
566
+ }
567
+ }
568
+ try {
569
+ const path = credentialPath(this.configRoot);
570
+ if (snapshot.fileSource === undefined) {
571
+ await rm(path, { force: true });
572
+ }
573
+ else {
574
+ const committed = await writeFileAtomically(path, snapshot.fileSource);
575
+ if (!committed)
576
+ throw new Error('MCP OAuth credential rollback was interrupted');
577
+ }
578
+ }
579
+ catch (error) {
580
+ failures.push(error);
581
+ }
582
+ if (failures.length === 1)
583
+ throw failures[0];
584
+ if (failures.length > 1)
585
+ throw new AggregateError(failures, 'Credential rollback failed');
586
+ }
587
+ async writeEnvelope(envelope) {
588
+ if (this.useKeychain) {
589
+ try {
590
+ await writeKeychainEnvelope(this.service, envelope);
591
+ return;
592
+ }
593
+ catch (error) {
594
+ if (error.code !== 'ENOENT')
595
+ throw error;
596
+ }
597
+ }
598
+ const committed = await writeFileAtomically(credentialPath(this.configRoot), `${JSON.stringify(envelope, null, 2)}\n`);
599
+ if (!committed)
600
+ throw new Error('MCP OAuth credential write was interrupted');
601
+ }
602
+ async withLock(operation) {
603
+ const started = Date.now();
604
+ let lock = await this.lease.tryAcquire();
605
+ while (!lock) {
606
+ if (Date.now() - started >= LOCK_WAIT_MS) {
607
+ throw new Error('MCP OAuth credential store is locked');
608
+ }
609
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
610
+ lock = await this.lease.tryAcquire();
611
+ }
612
+ try {
613
+ await this.recoverPluginSecretTransaction();
614
+ return await operation();
615
+ }
616
+ finally {
617
+ await lock.release();
618
+ }
619
+ }
620
+ }
621
+ function clientMetadata(serverName, redirectUri) {
622
+ return {
623
+ client_name: `Praxis (${serverName})`,
624
+ redirect_uris: [redirectUri],
625
+ response_types: ['code'],
626
+ grant_types: ['authorization_code', 'refresh_token'],
627
+ token_endpoint_auth_method: 'none',
628
+ };
629
+ }
630
+ export async function readMcpClientSecret() {
631
+ const envSecret = process.env.MCP_CLIENT_SECRET;
632
+ if (envSecret)
633
+ return envSecret;
634
+ if (!process.stdin.isTTY) {
635
+ throw new Error('No TTY available to prompt for client secret. Set MCP_CLIENT_SECRET env var instead.');
636
+ }
637
+ return new Promise((resolvePromise, rejectPromise) => {
638
+ process.stderr.write('Enter OAuth client secret: ');
639
+ process.stdin.setRawMode?.(true);
640
+ let secret = '';
641
+ const onData = (chunk) => {
642
+ const value = chunk.toString();
643
+ if (value === '\n' || value === '\r') {
644
+ process.stdin.setRawMode?.(false);
645
+ process.stdin.removeListener('data', onData);
646
+ process.stderr.write('\n');
647
+ resolvePromise(secret);
648
+ }
649
+ else if (value === '\u0003') {
650
+ process.stdin.setRawMode?.(false);
651
+ process.stdin.removeListener('data', onData);
652
+ rejectPromise(new Error('Cancelled'));
653
+ }
654
+ else if (value === '\u007f' || value === '\b') {
655
+ secret = secret.slice(0, -1);
656
+ }
657
+ else {
658
+ secret += value;
659
+ }
660
+ };
661
+ process.stdin.on('data', onData);
662
+ });
663
+ }
664
+ export class ClaudeMcpOAuthProvider {
665
+ store;
666
+ server;
667
+ redirectUri;
668
+ onAuthorization;
669
+ configuredClient;
670
+ codeVerifierValue;
671
+ stateValue;
672
+ recordValue;
673
+ constructor(store, server, redirectUri, onAuthorization, record, configuredClient) {
674
+ this.store = store;
675
+ this.server = server;
676
+ this.redirectUri = redirectUri;
677
+ this.onAuthorization = onAuthorization;
678
+ this.configuredClient = configuredClient;
679
+ this.recordValue = record;
680
+ }
681
+ get redirectUrl() {
682
+ return this.redirectUri;
683
+ }
684
+ get clientMetadata() {
685
+ return clientMetadata(this.server.name, this.redirectUri);
686
+ }
687
+ async state() {
688
+ this.stateValue ??= randomBytes(32).toString('base64url');
689
+ return this.stateValue;
690
+ }
691
+ authorizationState() {
692
+ if (!this.stateValue)
693
+ throw new Error('MCP OAuth authorization state is unavailable');
694
+ return this.stateValue;
695
+ }
696
+ clientInformation() {
697
+ if (this.recordValue?.clientId &&
698
+ this.recordValue.redirectUri === this.redirectUri) {
699
+ return {
700
+ client_id: this.recordValue.clientId,
701
+ ...(this.recordValue.clientSecret
702
+ ? { client_secret: this.recordValue.clientSecret }
703
+ : {}),
704
+ redirect_uris: [this.recordValue.redirectUri ?? this.redirectUri],
705
+ token_endpoint_auth_method: 'none',
706
+ };
707
+ }
708
+ if (!this.configuredClient)
709
+ return undefined;
710
+ return {
711
+ client_id: this.configuredClient.clientId,
712
+ ...(this.configuredClient.clientSecret
713
+ ? { client_secret: this.configuredClient.clientSecret }
714
+ : {}),
715
+ redirect_uris: [this.redirectUri],
716
+ token_endpoint_auth_method: 'none',
717
+ };
718
+ }
719
+ async saveClientInformation(information) {
720
+ if (typeof information.client_id !== 'string') {
721
+ throw new Error('MCP OAuth client registration returned no client_id');
722
+ }
723
+ this.recordValue = await this.store.mutate(this.server, (current) => ({
724
+ serverName: this.server.name,
725
+ serverUrl: this.server.url,
726
+ accessToken: current?.accessToken ?? '',
727
+ ...(current?.discoveryState === undefined
728
+ ? {}
729
+ : { discoveryState: current.discoveryState }),
730
+ clientId: information.client_id,
731
+ ...(typeof information.client_secret === 'string'
732
+ ? { clientSecret: information.client_secret }
733
+ : {}),
734
+ redirectUri: this.redirectUri,
735
+ ...(current?.refreshToken ? { refreshToken: current.refreshToken } : {}),
736
+ ...(current?.expiresAt === undefined
737
+ ? {}
738
+ : { expiresAt: current.expiresAt }),
739
+ ...(current?.scope ? { scope: current.scope } : {}),
740
+ }));
741
+ }
742
+ async tokens() {
743
+ const record = this.recordValue ?? (await this.store.read(this.server));
744
+ this.recordValue = record;
745
+ if (!record?.accessToken)
746
+ return undefined;
747
+ const expiresIn = record.expiresAt === undefined
748
+ ? undefined
749
+ : Math.max(0, Math.floor((record.expiresAt - Date.now()) / 1000));
750
+ return {
751
+ access_token: record.accessToken,
752
+ token_type: 'Bearer',
753
+ ...(record.refreshToken ? { refresh_token: record.refreshToken } : {}),
754
+ ...(expiresIn === undefined ? {} : { expires_in: expiresIn }),
755
+ ...(record.scope ? { scope: record.scope } : {}),
756
+ };
757
+ }
758
+ async saveTokens(tokens) {
759
+ if (!tokens.access_token)
760
+ throw new Error('MCP OAuth token response had no access_token');
761
+ const expiresAt = typeof tokens.expires_in === 'number'
762
+ ? Date.now() + Math.max(0, tokens.expires_in) * 1000
763
+ : undefined;
764
+ this.recordValue = await this.store.mutate(this.server, (current) => ({
765
+ serverName: this.server.name,
766
+ serverUrl: this.server.url,
767
+ accessToken: tokens.access_token,
768
+ ...(current?.discoveryState === undefined
769
+ ? {}
770
+ : { discoveryState: current.discoveryState }),
771
+ ...(current?.clientId ? { clientId: current.clientId } : {}),
772
+ ...(current?.clientSecret ? { clientSecret: current.clientSecret } : {}),
773
+ redirectUri: current?.redirectUri ?? this.redirectUri,
774
+ ...(tokens.refresh_token
775
+ ? { refreshToken: tokens.refresh_token }
776
+ : current?.refreshToken
777
+ ? { refreshToken: current.refreshToken }
778
+ : {}),
779
+ ...(expiresAt === undefined ? {} : { expiresAt }),
780
+ ...(tokens.scope
781
+ ? { scope: tokens.scope }
782
+ : current?.scope
783
+ ? { scope: current.scope }
784
+ : {}),
785
+ }));
786
+ }
787
+ async redirectToAuthorization(url) {
788
+ await this.onAuthorization(url);
789
+ }
790
+ saveCodeVerifier(codeVerifier) {
791
+ this.codeVerifierValue = codeVerifier;
792
+ }
793
+ codeVerifier() {
794
+ if (!this.codeVerifierValue)
795
+ throw new Error('MCP OAuth code verifier is unavailable');
796
+ return this.codeVerifierValue;
797
+ }
798
+ async saveDiscoveryState(state) {
799
+ this.recordValue = await this.store.mutate(this.server, (current) => ({
800
+ serverName: this.server.name,
801
+ serverUrl: this.server.url,
802
+ accessToken: current?.accessToken ?? '',
803
+ discoveryState: {
804
+ ...(state.authorizationServerUrl
805
+ ? { authorizationServerUrl: String(state.authorizationServerUrl) }
806
+ : {}),
807
+ oauthMetadataFound: state.authorizationServerMetadata !== undefined,
808
+ },
809
+ ...(current?.clientId ? { clientId: current.clientId } : {}),
810
+ ...(current?.clientSecret ? { clientSecret: current.clientSecret } : {}),
811
+ ...(current?.redirectUri
812
+ ? { redirectUri: current.redirectUri }
813
+ : { redirectUri: this.redirectUri }),
814
+ ...(current?.refreshToken ? { refreshToken: current.refreshToken } : {}),
815
+ ...(current?.expiresAt === undefined
816
+ ? {}
817
+ : { expiresAt: current.expiresAt }),
818
+ ...(current?.scope ? { scope: current.scope } : {}),
819
+ }));
820
+ }
821
+ async discoveryState() {
822
+ const state = this.recordValue?.discoveryState;
823
+ if (!state?.authorizationServerUrl)
824
+ return undefined;
825
+ return { authorizationServerUrl: state.authorizationServerUrl };
826
+ }
827
+ async invalidateCredentials(scope) {
828
+ if (scope === 'all') {
829
+ this.recordValue = undefined;
830
+ await this.store.clear(this.server);
831
+ return;
832
+ }
833
+ this.recordValue = await this.store.mutate(this.server, (current) => {
834
+ if (!current)
835
+ return undefined;
836
+ if (scope === 'tokens') {
837
+ const next = { ...current, accessToken: '' };
838
+ delete next.refreshToken;
839
+ delete next.expiresAt;
840
+ delete next.scope;
841
+ return next;
842
+ }
843
+ if (scope === 'client') {
844
+ const next = { ...current };
845
+ delete next.clientId;
846
+ delete next.clientSecret;
847
+ return next;
848
+ }
849
+ if (scope === 'discovery') {
850
+ const next = { ...current };
851
+ delete next.discoveryState;
852
+ return next;
853
+ }
854
+ if (scope === 'verifier')
855
+ this.codeVerifierValue = undefined;
856
+ return current;
857
+ });
858
+ }
859
+ }
860
+ export function mcpOAuthServerIdentity(name, config) {
861
+ const type = config.type === 'sse'
862
+ ? 'sse'
863
+ : config.type === 'http' || config.url !== undefined
864
+ ? 'http'
865
+ : undefined;
866
+ if (!type || typeof config.url !== 'string' || config.url.length === 0) {
867
+ throw new Error(`MCP server ${name} must use HTTP or SSE transport for OAuth`);
868
+ }
869
+ const headers = isStringRecord(config.headers) ? config.headers : undefined;
870
+ const oauth = isRecord(config.oauth) ? config.oauth : undefined;
871
+ const clientId = stringValue(oauth?.clientId);
872
+ const callbackPort = typeof oauth?.callbackPort === 'number' &&
873
+ Number.isFinite(oauth.callbackPort)
874
+ ? oauth.callbackPort
875
+ : undefined;
876
+ return {
877
+ name,
878
+ type,
879
+ url: config.url,
880
+ ...(headers ? { headers } : {}),
881
+ ...(clientId ? { clientId } : {}),
882
+ ...(callbackPort === undefined ? {} : { callbackPort }),
883
+ };
884
+ }
885
+ function isStringRecord(value) {
886
+ return (isRecord(value) &&
887
+ Object.values(value).every((item) => typeof item === 'string'));
888
+ }
889
+ async function listenCallbackServer(port = 0) {
890
+ let callbackOrigin = 'http://localhost';
891
+ let resolveCallback = () => undefined;
892
+ let rejectCallback = () => undefined;
893
+ const callback = new Promise((resolvePromise, rejectPromise) => {
894
+ resolveCallback = resolvePromise;
895
+ rejectCallback = rejectPromise;
896
+ });
897
+ const server = createServer((request, response) => {
898
+ const url = new URL(request.url ?? '/', 'http://localhost');
899
+ if (url.pathname !== '/callback') {
900
+ response.writeHead(404);
901
+ response.end('Not found');
902
+ return;
903
+ }
904
+ const callbackUrl = `${callbackOrigin}${url.pathname}${url.search}`;
905
+ response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
906
+ response.end('<html><body>Praxis authorization received. You may close this tab.</body></html>');
907
+ resolveCallback(callbackUrl);
908
+ });
909
+ server.on('error', (error) => rejectCallback(error));
910
+ await new Promise((resolvePromise, rejectPromise) => {
911
+ server.once('listening', () => resolvePromise());
912
+ server.once('error', rejectPromise);
913
+ server.listen(port, '127.0.0.1');
914
+ });
915
+ const address = server.address();
916
+ if (!address || typeof address === 'string') {
917
+ await new Promise((resolvePromise) => server.close(() => resolvePromise()));
918
+ throw new Error('MCP OAuth callback server did not expose a port');
919
+ }
920
+ callbackOrigin = `http://localhost:${address.port}`;
921
+ return {
922
+ server,
923
+ redirectUri: `http://localhost:${address.port}/callback`,
924
+ wait: () => callback,
925
+ };
926
+ }
927
+ async function closeServer(server) {
928
+ await new Promise((resolvePromise) => server.close(() => resolvePromise()));
929
+ }
930
+ async function defaultOpenBrowser(url) {
931
+ const command = process.platform === 'darwin'
932
+ ? 'open'
933
+ : process.platform === 'win32'
934
+ ? 'cmd'
935
+ : 'xdg-open';
936
+ const args = process.platform === 'win32'
937
+ ? ['/c', 'start', '', url.toString()]
938
+ : [url.toString()];
939
+ await execFileAsync(command, args, { timeout: 10_000 }).catch(() => undefined);
940
+ }
941
+ async function firstInputLine(readRedirectUrl) {
942
+ const value = await readRedirectUrl();
943
+ if (value.trim().length === 0)
944
+ throw new Error('MCP OAuth redirect URL is required');
945
+ return value.trim();
946
+ }
947
+ function callbackParameter(url, name) {
948
+ const values = url.searchParams.getAll(name);
949
+ if (values.length > 1) {
950
+ throw new Error(`MCP OAuth redirect URL contained multiple ${name} values`);
951
+ }
952
+ return values[0];
953
+ }
954
+ function validateAuthorizationCallback(value, redirectUri, expectedState) {
955
+ let actual;
956
+ try {
957
+ actual = new URL(value);
958
+ }
959
+ catch (error) {
960
+ throw new Error('MCP OAuth redirect URL is invalid', { cause: error });
961
+ }
962
+ const expected = new URL(redirectUri);
963
+ if (actual.origin !== expected.origin ||
964
+ actual.pathname !== expected.pathname) {
965
+ throw new Error('MCP OAuth redirect URL does not match the local callback');
966
+ }
967
+ const state = callbackParameter(actual, 'state');
968
+ if (!state || state !== expectedState) {
969
+ throw new Error('MCP OAuth redirect state did not match the authorization request');
970
+ }
971
+ return actual;
972
+ }
973
+ export async function authenticateMcpServer(options) {
974
+ const callback = await listenCallbackServer(options.callbackPort ?? options.server.callbackPort);
975
+ const store = new ClaudeMcpOAuthStore({ configRoot: options.configRoot });
976
+ const existing = await store.read(options.server);
977
+ const clientId = options.clientId ?? options.server.clientId;
978
+ const clientSecret = options.clientSecret ??
979
+ (clientId ? await store.readClientSecret(options.server) : undefined);
980
+ const provider = new ClaudeMcpOAuthProvider(store, options.server, callback.redirectUri, async (url) => {
981
+ options.write(`Visit this URL to authorize:\n ${url}\n`);
982
+ if (!options.noBrowser) {
983
+ await (options.openBrowser ?? defaultOpenBrowser)(url);
984
+ }
985
+ }, existing, clientId
986
+ ? {
987
+ clientId,
988
+ ...(clientSecret ? { clientSecret } : {}),
989
+ }
990
+ : undefined);
991
+ try {
992
+ const result = await auth(provider, { serverUrl: options.server.url });
993
+ if (result === 'REDIRECT') {
994
+ const input = options.noBrowser
995
+ ? options.readRedirectUrl
996
+ ? firstInputLine(options.readRedirectUrl)
997
+ : callback.wait()
998
+ : callback.wait();
999
+ let timeout;
1000
+ let redirectUrl;
1001
+ try {
1002
+ redirectUrl = await Promise.race([
1003
+ input,
1004
+ new Promise((_, reject) => {
1005
+ timeout = setTimeout(() => reject(new Error('MCP OAuth authorization timed out')), LOGIN_TIMEOUT_MS);
1006
+ }),
1007
+ ]);
1008
+ }
1009
+ finally {
1010
+ if (timeout)
1011
+ clearTimeout(timeout);
1012
+ }
1013
+ const parsed = validateAuthorizationCallback(redirectUrl, callback.redirectUri, provider.authorizationState());
1014
+ const error = callbackParameter(parsed, 'error');
1015
+ if (error) {
1016
+ throw new Error(callbackParameter(parsed, 'error_description') ?? error);
1017
+ }
1018
+ const code = callbackParameter(parsed, 'code');
1019
+ if (!code)
1020
+ throw new Error('MCP OAuth redirect URL did not contain a code');
1021
+ const completed = await auth(provider, {
1022
+ serverUrl: options.server.url,
1023
+ authorizationCode: code,
1024
+ });
1025
+ if (completed !== 'AUTHORIZED') {
1026
+ throw new Error('MCP OAuth token exchange did not complete');
1027
+ }
1028
+ }
1029
+ return 'AUTHORIZED';
1030
+ }
1031
+ finally {
1032
+ await closeServer(callback.server);
1033
+ }
1034
+ }
1035
+ export async function loadMcpOAuthProvider(configRoot, server) {
1036
+ const store = new ClaudeMcpOAuthStore({ configRoot });
1037
+ const record = await store.read(server);
1038
+ if (!record || (!record.accessToken && !record.refreshToken))
1039
+ return undefined;
1040
+ const redirectUri = record.redirectUri ?? 'http://localhost:0/callback';
1041
+ const clientSecret = server.clientId
1042
+ ? await store.readClientSecret(server)
1043
+ : undefined;
1044
+ return new ClaudeMcpOAuthProvider(store, server, redirectUri, async () => {
1045
+ throw new Error(`MCP server ${server.name} requires authentication; run praxis mcp login ${server.name}`);
1046
+ }, record, server.clientId
1047
+ ? {
1048
+ clientId: server.clientId,
1049
+ ...(clientSecret ? { clientSecret } : {}),
1050
+ }
1051
+ : undefined);
1052
+ }
1053
+ //# sourceMappingURL=claude-mcp-oauth.js.map