@chances-ai/engine 24.0.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 (389) hide show
  1. package/dist/agents/discover.d.ts +30 -0
  2. package/dist/agents/discover.d.ts.map +1 -0
  3. package/dist/agents/discover.js +183 -0
  4. package/dist/agents/discover.js.map +1 -0
  5. package/dist/agents/index.d.ts +20 -0
  6. package/dist/agents/index.d.ts.map +1 -0
  7. package/dist/agents/index.js +52 -0
  8. package/dist/agents/index.js.map +1 -0
  9. package/dist/agents/parse.d.ts +61 -0
  10. package/dist/agents/parse.d.ts.map +1 -0
  11. package/dist/agents/parse.js +527 -0
  12. package/dist/agents/parse.js.map +1 -0
  13. package/dist/agents/types.d.ts +52 -0
  14. package/dist/agents/types.d.ts.map +1 -0
  15. package/dist/agents/types.js +8 -0
  16. package/dist/agents/types.js.map +1 -0
  17. package/dist/ai/adapters/ai-sdk-stream.d.ts +19 -0
  18. package/dist/ai/adapters/ai-sdk-stream.d.ts.map +1 -0
  19. package/dist/ai/adapters/ai-sdk-stream.js +125 -0
  20. package/dist/ai/adapters/ai-sdk-stream.js.map +1 -0
  21. package/dist/ai/adapters/ai-sdk.d.ts +56 -0
  22. package/dist/ai/adapters/ai-sdk.d.ts.map +1 -0
  23. package/dist/ai/adapters/ai-sdk.js +112 -0
  24. package/dist/ai/adapters/ai-sdk.js.map +1 -0
  25. package/dist/ai/adapters/mock.d.ts +13 -0
  26. package/dist/ai/adapters/mock.d.ts.map +1 -0
  27. package/dist/ai/adapters/mock.js +54 -0
  28. package/dist/ai/adapters/mock.js.map +1 -0
  29. package/dist/ai/adapters/openai-compatible.d.ts +23 -0
  30. package/dist/ai/adapters/openai-compatible.d.ts.map +1 -0
  31. package/dist/ai/adapters/openai-compatible.js +45 -0
  32. package/dist/ai/adapters/openai-compatible.js.map +1 -0
  33. package/dist/ai/cost.d.ts +3 -0
  34. package/dist/ai/cost.d.ts.map +1 -0
  35. package/dist/ai/cost.js +5 -0
  36. package/dist/ai/cost.js.map +1 -0
  37. package/dist/ai/index.d.ts +12 -0
  38. package/dist/ai/index.d.ts.map +1 -0
  39. package/dist/ai/index.js +11 -0
  40. package/dist/ai/index.js.map +1 -0
  41. package/dist/ai/known-models.d.ts +20 -0
  42. package/dist/ai/known-models.d.ts.map +1 -0
  43. package/dist/ai/known-models.js +129 -0
  44. package/dist/ai/known-models.js.map +1 -0
  45. package/dist/ai/registry.d.ts +12 -0
  46. package/dist/ai/registry.d.ts.map +1 -0
  47. package/dist/ai/registry.js +24 -0
  48. package/dist/ai/registry.js.map +1 -0
  49. package/dist/ai/retry.d.ts +11 -0
  50. package/dist/ai/retry.d.ts.map +1 -0
  51. package/dist/ai/retry.js +14 -0
  52. package/dist/ai/retry.js.map +1 -0
  53. package/dist/ai/router.d.ts +25 -0
  54. package/dist/ai/router.d.ts.map +1 -0
  55. package/dist/ai/router.js +36 -0
  56. package/dist/ai/router.js.map +1 -0
  57. package/dist/ai/setup.d.ts +23 -0
  58. package/dist/ai/setup.d.ts.map +1 -0
  59. package/dist/ai/setup.js +47 -0
  60. package/dist/ai/setup.js.map +1 -0
  61. package/dist/ai/summarizer.d.ts +24 -0
  62. package/dist/ai/summarizer.d.ts.map +1 -0
  63. package/dist/ai/summarizer.js +56 -0
  64. package/dist/ai/summarizer.js.map +1 -0
  65. package/dist/ai/types.d.ts +83 -0
  66. package/dist/ai/types.d.ts.map +1 -0
  67. package/dist/ai/types.js +2 -0
  68. package/dist/ai/types.js.map +1 -0
  69. package/dist/core/compaction/circuit-breaker.d.ts +32 -0
  70. package/dist/core/compaction/circuit-breaker.d.ts.map +1 -0
  71. package/dist/core/compaction/circuit-breaker.js +42 -0
  72. package/dist/core/compaction/circuit-breaker.js.map +1 -0
  73. package/dist/core/compaction/compactor.d.ts +75 -0
  74. package/dist/core/compaction/compactor.d.ts.map +1 -0
  75. package/dist/core/compaction/compactor.js +261 -0
  76. package/dist/core/compaction/compactor.js.map +1 -0
  77. package/dist/core/compaction/estimate.d.ts +39 -0
  78. package/dist/core/compaction/estimate.d.ts.map +1 -0
  79. package/dist/core/compaction/estimate.js +74 -0
  80. package/dist/core/compaction/estimate.js.map +1 -0
  81. package/dist/core/compaction/index.d.ts +5 -0
  82. package/dist/core/compaction/index.d.ts.map +1 -0
  83. package/dist/core/compaction/index.js +5 -0
  84. package/dist/core/compaction/index.js.map +1 -0
  85. package/dist/core/compaction/prune.d.ts +43 -0
  86. package/dist/core/compaction/prune.d.ts.map +1 -0
  87. package/dist/core/compaction/prune.js +51 -0
  88. package/dist/core/compaction/prune.js.map +1 -0
  89. package/dist/core/engine.d.ts +268 -0
  90. package/dist/core/engine.d.ts.map +1 -0
  91. package/dist/core/engine.js +767 -0
  92. package/dist/core/engine.js.map +1 -0
  93. package/dist/core/index.d.ts +6 -0
  94. package/dist/core/index.d.ts.map +1 -0
  95. package/dist/core/index.js +6 -0
  96. package/dist/core/index.js.map +1 -0
  97. package/dist/core/task-tool.d.ts +175 -0
  98. package/dist/core/task-tool.d.ts.map +1 -0
  99. package/dist/core/task-tool.js +901 -0
  100. package/dist/core/task-tool.js.map +1 -0
  101. package/dist/core/workspace-query.d.ts +83 -0
  102. package/dist/core/workspace-query.d.ts.map +1 -0
  103. package/dist/core/workspace-query.js +217 -0
  104. package/dist/core/workspace-query.js.map +1 -0
  105. package/dist/core/worktree/active-marker.d.ts +31 -0
  106. package/dist/core/worktree/active-marker.d.ts.map +1 -0
  107. package/dist/core/worktree/active-marker.js +109 -0
  108. package/dist/core/worktree/active-marker.js.map +1 -0
  109. package/dist/core/worktree/create.d.ts +40 -0
  110. package/dist/core/worktree/create.d.ts.map +1 -0
  111. package/dist/core/worktree/create.js +121 -0
  112. package/dist/core/worktree/create.js.map +1 -0
  113. package/dist/core/worktree/errors.d.ts +7 -0
  114. package/dist/core/worktree/errors.d.ts.map +1 -0
  115. package/dist/core/worktree/errors.js +11 -0
  116. package/dist/core/worktree/errors.js.map +1 -0
  117. package/dist/core/worktree/gc.d.ts +39 -0
  118. package/dist/core/worktree/gc.d.ts.map +1 -0
  119. package/dist/core/worktree/gc.js +146 -0
  120. package/dist/core/worktree/gc.js.map +1 -0
  121. package/dist/core/worktree/git.d.ts +53 -0
  122. package/dist/core/worktree/git.d.ts.map +1 -0
  123. package/dist/core/worktree/git.js +166 -0
  124. package/dist/core/worktree/git.js.map +1 -0
  125. package/dist/core/worktree/index.d.ts +8 -0
  126. package/dist/core/worktree/index.d.ts.map +1 -0
  127. package/dist/core/worktree/index.js +8 -0
  128. package/dist/core/worktree/index.js.map +1 -0
  129. package/dist/core/worktree/paths.d.ts +26 -0
  130. package/dist/core/worktree/paths.d.ts.map +1 -0
  131. package/dist/core/worktree/paths.js +57 -0
  132. package/dist/core/worktree/paths.js.map +1 -0
  133. package/dist/core/worktree/slug.d.ts +6 -0
  134. package/dist/core/worktree/slug.d.ts.map +1 -0
  135. package/dist/core/worktree/slug.js +21 -0
  136. package/dist/core/worktree/slug.js.map +1 -0
  137. package/dist/local-vault/file-store.d.ts +64 -0
  138. package/dist/local-vault/file-store.d.ts.map +1 -0
  139. package/dist/local-vault/file-store.js +225 -0
  140. package/dist/local-vault/file-store.js.map +1 -0
  141. package/dist/local-vault/index.d.ts +57 -0
  142. package/dist/local-vault/index.d.ts.map +1 -0
  143. package/dist/local-vault/index.js +68 -0
  144. package/dist/local-vault/index.js.map +1 -0
  145. package/dist/local-vault/keychain.d.ts +58 -0
  146. package/dist/local-vault/keychain.d.ts.map +1 -0
  147. package/dist/local-vault/keychain.js +200 -0
  148. package/dist/local-vault/keychain.js.map +1 -0
  149. package/dist/local-vault/mutex.d.ts +20 -0
  150. package/dist/local-vault/mutex.d.ts.map +1 -0
  151. package/dist/local-vault/mutex.js +44 -0
  152. package/dist/local-vault/mutex.js.map +1 -0
  153. package/dist/local-vault/passphrase.d.ts +30 -0
  154. package/dist/local-vault/passphrase.d.ts.map +1 -0
  155. package/dist/local-vault/passphrase.js +72 -0
  156. package/dist/local-vault/passphrase.js.map +1 -0
  157. package/dist/lsp/config.d.ts +34 -0
  158. package/dist/lsp/config.d.ts.map +1 -0
  159. package/dist/lsp/config.js +68 -0
  160. package/dist/lsp/config.js.map +1 -0
  161. package/dist/lsp/detect.d.ts +7 -0
  162. package/dist/lsp/detect.d.ts.map +1 -0
  163. package/dist/lsp/detect.js +78 -0
  164. package/dist/lsp/detect.js.map +1 -0
  165. package/dist/lsp/errors.d.ts +11 -0
  166. package/dist/lsp/errors.d.ts.map +1 -0
  167. package/dist/lsp/errors.js +11 -0
  168. package/dist/lsp/errors.js.map +1 -0
  169. package/dist/lsp/formatters.d.ts +147 -0
  170. package/dist/lsp/formatters.d.ts.map +1 -0
  171. package/dist/lsp/formatters.js +259 -0
  172. package/dist/lsp/formatters.js.map +1 -0
  173. package/dist/lsp/index.d.ts +31 -0
  174. package/dist/lsp/index.d.ts.map +1 -0
  175. package/dist/lsp/index.js +31 -0
  176. package/dist/lsp/index.js.map +1 -0
  177. package/dist/lsp/instance.d.ts +72 -0
  178. package/dist/lsp/instance.d.ts.map +1 -0
  179. package/dist/lsp/instance.js +489 -0
  180. package/dist/lsp/instance.js.map +1 -0
  181. package/dist/lsp/lazy-load.d.ts +27 -0
  182. package/dist/lsp/lazy-load.d.ts.map +1 -0
  183. package/dist/lsp/lazy-load.js +57 -0
  184. package/dist/lsp/lazy-load.js.map +1 -0
  185. package/dist/lsp/manager.d.ts +59 -0
  186. package/dist/lsp/manager.d.ts.map +1 -0
  187. package/dist/lsp/manager.js +242 -0
  188. package/dist/lsp/manager.js.map +1 -0
  189. package/dist/lsp/ops.d.ts +13 -0
  190. package/dist/lsp/ops.d.ts.map +1 -0
  191. package/dist/lsp/ops.js +225 -0
  192. package/dist/lsp/ops.js.map +1 -0
  193. package/dist/lsp/rpc.d.ts +47 -0
  194. package/dist/lsp/rpc.d.ts.map +1 -0
  195. package/dist/lsp/rpc.js +134 -0
  196. package/dist/lsp/rpc.js.map +1 -0
  197. package/dist/lsp/safe-uri.d.ts +18 -0
  198. package/dist/lsp/safe-uri.d.ts.map +1 -0
  199. package/dist/lsp/safe-uri.js +96 -0
  200. package/dist/lsp/safe-uri.js.map +1 -0
  201. package/dist/lsp/types.d.ts +70 -0
  202. package/dist/lsp/types.d.ts.map +1 -0
  203. package/dist/lsp/types.js +16 -0
  204. package/dist/lsp/types.js.map +1 -0
  205. package/dist/mcp/bridge.d.ts +57 -0
  206. package/dist/mcp/bridge.d.ts.map +1 -0
  207. package/dist/mcp/bridge.js +98 -0
  208. package/dist/mcp/bridge.js.map +1 -0
  209. package/dist/mcp/category.d.ts +22 -0
  210. package/dist/mcp/category.d.ts.map +1 -0
  211. package/dist/mcp/category.js +11 -0
  212. package/dist/mcp/category.js.map +1 -0
  213. package/dist/mcp/client.d.ts +228 -0
  214. package/dist/mcp/client.d.ts.map +1 -0
  215. package/dist/mcp/client.js +352 -0
  216. package/dist/mcp/client.js.map +1 -0
  217. package/dist/mcp/content.d.ts +86 -0
  218. package/dist/mcp/content.d.ts.map +1 -0
  219. package/dist/mcp/content.js +147 -0
  220. package/dist/mcp/content.js.map +1 -0
  221. package/dist/mcp/env.d.ts +19 -0
  222. package/dist/mcp/env.d.ts.map +1 -0
  223. package/dist/mcp/env.js +50 -0
  224. package/dist/mcp/env.js.map +1 -0
  225. package/dist/mcp/host.d.ts +199 -0
  226. package/dist/mcp/host.d.ts.map +1 -0
  227. package/dist/mcp/host.js +530 -0
  228. package/dist/mcp/host.js.map +1 -0
  229. package/dist/mcp/index.d.ts +18 -0
  230. package/dist/mcp/index.d.ts.map +1 -0
  231. package/dist/mcp/index.js +17 -0
  232. package/dist/mcp/index.js.map +1 -0
  233. package/dist/mcp/load-mcp-host.d.ts +32 -0
  234. package/dist/mcp/load-mcp-host.d.ts.map +1 -0
  235. package/dist/mcp/load-mcp-host.js +49 -0
  236. package/dist/mcp/load-mcp-host.js.map +1 -0
  237. package/dist/mcp/oauth/callback-server.d.ts +73 -0
  238. package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
  239. package/dist/mcp/oauth/callback-server.js +280 -0
  240. package/dist/mcp/oauth/callback-server.js.map +1 -0
  241. package/dist/mcp/oauth/config-hash.d.ts +24 -0
  242. package/dist/mcp/oauth/config-hash.d.ts.map +1 -0
  243. package/dist/mcp/oauth/config-hash.js +55 -0
  244. package/dist/mcp/oauth/config-hash.js.map +1 -0
  245. package/dist/mcp/oauth/error-normalize.d.ts +39 -0
  246. package/dist/mcp/oauth/error-normalize.d.ts.map +1 -0
  247. package/dist/mcp/oauth/error-normalize.js +91 -0
  248. package/dist/mcp/oauth/error-normalize.js.map +1 -0
  249. package/dist/mcp/oauth/provider.d.ts +190 -0
  250. package/dist/mcp/oauth/provider.d.ts.map +1 -0
  251. package/dist/mcp/oauth/provider.js +305 -0
  252. package/dist/mcp/oauth/provider.js.map +1 -0
  253. package/dist/mcp/oauth/refresh-coalescer.d.ts +46 -0
  254. package/dist/mcp/oauth/refresh-coalescer.d.ts.map +1 -0
  255. package/dist/mcp/oauth/refresh-coalescer.js +77 -0
  256. package/dist/mcp/oauth/refresh-coalescer.js.map +1 -0
  257. package/dist/mcp/oauth/sdk-shapes.d.ts +77 -0
  258. package/dist/mcp/oauth/sdk-shapes.d.ts.map +1 -0
  259. package/dist/mcp/oauth/sdk-shapes.js +21 -0
  260. package/dist/mcp/oauth/sdk-shapes.js.map +1 -0
  261. package/dist/mcp/parse.d.ts +28 -0
  262. package/dist/mcp/parse.d.ts.map +1 -0
  263. package/dist/mcp/parse.js +209 -0
  264. package/dist/mcp/parse.js.map +1 -0
  265. package/dist/mcp/prompts.d.ts +31 -0
  266. package/dist/mcp/prompts.d.ts.map +1 -0
  267. package/dist/mcp/prompts.js +71 -0
  268. package/dist/mcp/prompts.js.map +1 -0
  269. package/dist/mcp/redact.d.ts +62 -0
  270. package/dist/mcp/redact.d.ts.map +1 -0
  271. package/dist/mcp/redact.js +87 -0
  272. package/dist/mcp/redact.js.map +1 -0
  273. package/dist/mcp/resources.d.ts +70 -0
  274. package/dist/mcp/resources.d.ts.map +1 -0
  275. package/dist/mcp/resources.js +170 -0
  276. package/dist/mcp/resources.js.map +1 -0
  277. package/dist/mcp/types.d.ts +123 -0
  278. package/dist/mcp/types.d.ts.map +1 -0
  279. package/dist/mcp/types.js +2 -0
  280. package/dist/mcp/types.js.map +1 -0
  281. package/dist/memory/frontmatter.d.ts +18 -0
  282. package/dist/memory/frontmatter.d.ts.map +1 -0
  283. package/dist/memory/frontmatter.js +81 -0
  284. package/dist/memory/frontmatter.js.map +1 -0
  285. package/dist/memory/index.d.ts +5 -0
  286. package/dist/memory/index.d.ts.map +1 -0
  287. package/dist/memory/index.js +5 -0
  288. package/dist/memory/index.js.map +1 -0
  289. package/dist/memory/store.d.ts +44 -0
  290. package/dist/memory/store.d.ts.map +1 -0
  291. package/dist/memory/store.js +237 -0
  292. package/dist/memory/store.js.map +1 -0
  293. package/dist/memory/tools.d.ts +11 -0
  294. package/dist/memory/tools.d.ts.map +1 -0
  295. package/dist/memory/tools.js +159 -0
  296. package/dist/memory/tools.js.map +1 -0
  297. package/dist/memory/types.d.ts +32 -0
  298. package/dist/memory/types.d.ts.map +1 -0
  299. package/dist/memory/types.js +20 -0
  300. package/dist/memory/types.js.map +1 -0
  301. package/dist/plugin-api/index.d.ts +167 -0
  302. package/dist/plugin-api/index.d.ts.map +1 -0
  303. package/dist/plugin-api/index.js +151 -0
  304. package/dist/plugin-api/index.js.map +1 -0
  305. package/dist/plugin-logger/index.d.ts +21 -0
  306. package/dist/plugin-logger/index.d.ts.map +1 -0
  307. package/dist/plugin-logger/index.js +59 -0
  308. package/dist/plugin-logger/index.js.map +1 -0
  309. package/dist/session/index.d.ts +125 -0
  310. package/dist/session/index.d.ts.map +1 -0
  311. package/dist/session/index.js +202 -0
  312. package/dist/session/index.js.map +1 -0
  313. package/dist/tools/approval.d.ts +33 -0
  314. package/dist/tools/approval.d.ts.map +1 -0
  315. package/dist/tools/approval.js +53 -0
  316. package/dist/tools/approval.js.map +1 -0
  317. package/dist/tools/builtins/_shared.d.ts +94 -0
  318. package/dist/tools/builtins/_shared.d.ts.map +1 -0
  319. package/dist/tools/builtins/_shared.js +246 -0
  320. package/dist/tools/builtins/_shared.js.map +1 -0
  321. package/dist/tools/builtins/ask-user-question.d.ts +27 -0
  322. package/dist/tools/builtins/ask-user-question.d.ts.map +1 -0
  323. package/dist/tools/builtins/ask-user-question.js +191 -0
  324. package/dist/tools/builtins/ask-user-question.js.map +1 -0
  325. package/dist/tools/builtins/bash.d.ts +3 -0
  326. package/dist/tools/builtins/bash.d.ts.map +1 -0
  327. package/dist/tools/builtins/bash.js +158 -0
  328. package/dist/tools/builtins/bash.js.map +1 -0
  329. package/dist/tools/builtins/diff.d.ts +3 -0
  330. package/dist/tools/builtins/diff.d.ts.map +1 -0
  331. package/dist/tools/builtins/diff.js +83 -0
  332. package/dist/tools/builtins/diff.js.map +1 -0
  333. package/dist/tools/builtins/edit.d.ts +3 -0
  334. package/dist/tools/builtins/edit.d.ts.map +1 -0
  335. package/dist/tools/builtins/edit.js +40 -0
  336. package/dist/tools/builtins/edit.js.map +1 -0
  337. package/dist/tools/builtins/glob.d.ts +3 -0
  338. package/dist/tools/builtins/glob.d.ts.map +1 -0
  339. package/dist/tools/builtins/glob.js +37 -0
  340. package/dist/tools/builtins/glob.js.map +1 -0
  341. package/dist/tools/builtins/grep.d.ts +3 -0
  342. package/dist/tools/builtins/grep.d.ts.map +1 -0
  343. package/dist/tools/builtins/grep.js +81 -0
  344. package/dist/tools/builtins/grep.js.map +1 -0
  345. package/dist/tools/builtins/lsp.d.ts +3 -0
  346. package/dist/tools/builtins/lsp.d.ts.map +1 -0
  347. package/dist/tools/builtins/lsp.js +102 -0
  348. package/dist/tools/builtins/lsp.js.map +1 -0
  349. package/dist/tools/builtins/pty.d.ts +64 -0
  350. package/dist/tools/builtins/pty.d.ts.map +1 -0
  351. package/dist/tools/builtins/pty.js +536 -0
  352. package/dist/tools/builtins/pty.js.map +1 -0
  353. package/dist/tools/builtins/read.d.ts +3 -0
  354. package/dist/tools/builtins/read.d.ts.map +1 -0
  355. package/dist/tools/builtins/read.js +18 -0
  356. package/dist/tools/builtins/read.js.map +1 -0
  357. package/dist/tools/builtins/web-fetch.d.ts +4 -0
  358. package/dist/tools/builtins/web-fetch.d.ts.map +1 -0
  359. package/dist/tools/builtins/web-fetch.js +353 -0
  360. package/dist/tools/builtins/web-fetch.js.map +1 -0
  361. package/dist/tools/builtins/write.d.ts +3 -0
  362. package/dist/tools/builtins/write.d.ts.map +1 -0
  363. package/dist/tools/builtins/write.js +48 -0
  364. package/dist/tools/builtins/write.js.map +1 -0
  365. package/dist/tools/builtins.d.ts +9 -0
  366. package/dist/tools/builtins.d.ts.map +1 -0
  367. package/dist/tools/builtins.js +29 -0
  368. package/dist/tools/builtins.js.map +1 -0
  369. package/dist/tools/diff.d.ts +18 -0
  370. package/dist/tools/diff.d.ts.map +1 -0
  371. package/dist/tools/diff.js +57 -0
  372. package/dist/tools/diff.js.map +1 -0
  373. package/dist/tools/index.d.ts +10 -0
  374. package/dist/tools/index.d.ts.map +1 -0
  375. package/dist/tools/index.js +9 -0
  376. package/dist/tools/index.js.map +1 -0
  377. package/dist/tools/permission.d.ts +120 -0
  378. package/dist/tools/permission.d.ts.map +1 -0
  379. package/dist/tools/permission.js +208 -0
  380. package/dist/tools/permission.js.map +1 -0
  381. package/dist/tools/registry.d.ts +12 -0
  382. package/dist/tools/registry.d.ts.map +1 -0
  383. package/dist/tools/registry.js +19 -0
  384. package/dist/tools/registry.js.map +1 -0
  385. package/dist/tools/types.d.ts +244 -0
  386. package/dist/tools/types.d.ts.map +1 -0
  387. package/dist/tools/types.js +15 -0
  388. package/dist/tools/types.js.map +1 -0
  389. package/package.json +109 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-normalize.js","sourceRoot":"","sources":["../../../src/mcp/oauth/error-normalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * (3.7) `ChancesOAuthClientProvider` — chances' implementation of the SDK's
3
+ * `OAuthClientProvider` interface (`@modelcontextprotocol/sdk/client/auth.js`).
4
+ *
5
+ * One provider instance per OAuth-configured MCP server. Holds:
6
+ * - A **generation snapshot** captured at construction. Late-arriving
7
+ * `save*` calls from an in-flight refresh that started BEFORE a logout
8
+ * are dropped when the snapshot is older than the vault's current
9
+ * generation (codex Round-1 MUST-FIX #5).
10
+ * - A 30-second in-memory `tokens()` cache, invalidated on every `save*`
11
+ * / `invalidateCredentials` and on detected staleness.
12
+ * - A reference to the per-server `Redactor` so newly-discovered secrets
13
+ * (access tokens, refresh tokens, PKCE verifier) join the scrub set
14
+ * AT SAVE TIME — so any subsequent thrown error is already scrubbed
15
+ * when it surfaces (codex Round-1 SHOULD-FIX redactor extension).
16
+ *
17
+ * **Hot path (`tokens()`):** vault read → generation check → freshness
18
+ * check. We deliberately do NOT proactively refresh from inside
19
+ * `tokens()` in v1 — the SDK's `auth()` orchestrator handles 401-driven
20
+ * refresh and the `RefreshCoalescer` infrastructure is wired at the
21
+ * host-level dispose/logout-abort seam, ready for a 3.7.1 follow-up that
22
+ * adds proactive refresh if real-world parallel-call refresh storms
23
+ * become an observed problem. Keeping V1 simple matches the SDK's own
24
+ * stance (the SDK doesn't coalesce either) and avoids a complex
25
+ * refresh-inside-tokens() path that re-discovers metadata on the hot
26
+ * path.
27
+ *
28
+ * **Write path:** every `save*` and `invalidateCredentials` routes
29
+ * through `vault.mergeMutate(key, mutator)` — the per-key in-process
30
+ * mutex serialises read-modify-write and prevents the partial-update
31
+ * clobber from codex Round-1 MUST-FIX #1.
32
+ *
33
+ * Construction-time DI lets unit tests substitute a fake vault, a fake
34
+ * callback server, and a deterministic clock.
35
+ */
36
+ import type { OAuthClientInformationFull, OAuthClientInformationMixed, OAuthClientMetadata, OAuthTokens } from "./sdk-shapes.js";
37
+ import type { LoopbackCallbackServer } from "./callback-server.js";
38
+ import type { HttpMcpConfig } from "../types.js";
39
+ /** SDK's `OAuthDiscoveryState` shape narrowed to what we accept on save.
40
+ * We strip the metadata bags (§ 4.2) so the keychain stays well under
41
+ * the 4 KiB Windows limit. */
42
+ export interface OAuthDiscoveryStateInput {
43
+ authorizationServerUrl: string;
44
+ authorizationServerMetadata?: unknown;
45
+ resourceMetadata?: unknown;
46
+ resourceMetadataUrl?: string;
47
+ }
48
+ /** What the SDK gets back when it asks for the cached discovery state.
49
+ * We only carry URLs; the SDK will re-fetch full metadata on demand. */
50
+ export interface OAuthDiscoveryStateOutput {
51
+ authorizationServerUrl: string;
52
+ resourceMetadataUrl?: string;
53
+ }
54
+ /** Sub-set of the host-supplied logger interface this module touches. */
55
+ interface Logger {
56
+ debug(msg: string): void;
57
+ info(msg: string): void;
58
+ warn(msg: string): void;
59
+ error(msg: string): void;
60
+ }
61
+ /**
62
+ * Vault payload shape — one record per (serverName, configHash). Versioned
63
+ * for forward-compat. All sub-records use the SDK's snake_case shapes so
64
+ * the round-trip back to `tokens()` / `clientInformation()` requires no
65
+ * field renaming.
66
+ */
67
+ export interface VaultPayload {
68
+ v: 1;
69
+ /** The 16-hex `makeConfigHash(serverConfig)` slice. Cross-checked at
70
+ * read time — a payload whose stored hash differs from the current
71
+ * config's hash is from a previous config and must be ignored. */
72
+ configHash: string;
73
+ /** Monotonic counter — see codex Round-1 MUST-FIX #5. Logout and
74
+ * `invalidateCredentials("all")` bump it; stale providers' writes
75
+ * drop via the mergeMutate generation check. */
76
+ generation: number;
77
+ /** Stored client info from DCR. SDK's `OAuthClientInformationFull` shape. */
78
+ client?: OAuthClientInformationFull;
79
+ /** Stored tokens. SDK's `OAuthTokens` shape PLUS an absolute `expires_at`
80
+ * (epoch ms) computed at save time from the SDK-supplied `expires_in`
81
+ * relative seconds — relative values aren't usable for expiry comparison
82
+ * once they leave the IdP context. */
83
+ tokens?: OAuthTokens & {
84
+ expires_at?: number;
85
+ };
86
+ /** Cached discovery URLs. **No metadata bodies** — § 4.2. */
87
+ discovery?: {
88
+ authorizationServerUrl: string;
89
+ resourceMetadataUrl?: string;
90
+ /** Epoch ms — used for the 24h TTL on cached URLs. */
91
+ discoveredAt: number;
92
+ };
93
+ /** Transient PKCE verifier. Carries `createdAt` so a stale `/mcp login`
94
+ * that crashed mid-flow can't surface its old verifier 15+ minutes
95
+ * later (codex Round-1 SHOULD-FIX Q3). */
96
+ verifier?: {
97
+ value: string;
98
+ createdAt: number;
99
+ };
100
+ }
101
+ /** The narrow vault surface this provider depends on. Lets tests substitute
102
+ * an in-memory map without standing up the real LocalVault. */
103
+ export interface ProviderVault {
104
+ read<T>(key: string): Promise<T | undefined>;
105
+ mergeMutate<T>(key: string, mutator: (current: T | undefined) => T | undefined | Promise<T | undefined>, allowDelete?: boolean): Promise<void>;
106
+ }
107
+ /** Per-server redactor — the host owns one of these and passes it in so
108
+ * newly-discovered secrets join the scrub set in real time. Mirrored from
109
+ * `../redact.ts#RuntimeRedactor` to avoid a runtime dep on the redact
110
+ * module from this file (which lives under `oauth/`). */
111
+ interface RuntimeRedactor {
112
+ (msg: string): string;
113
+ add(secrets: readonly string[]): void;
114
+ }
115
+ export interface ProviderOptions {
116
+ serverName: string;
117
+ serverConfig: HttpMcpConfig;
118
+ vault: ProviderVault;
119
+ callbackServer: LoopbackCallbackServer;
120
+ redactor: RuntimeRedactor;
121
+ logger: Logger;
122
+ /** Platform-agnostic browser launcher. Default no-op for tests; the
123
+ * slash command supplies the real `open` / `xdg-open` / `start.exe` shim. */
124
+ openBrowser: (url: URL) => Promise<void>;
125
+ /** Test seam — defaults to `Date.now`. */
126
+ now?: () => number;
127
+ }
128
+ export declare class ChancesOAuthClientProvider {
129
+ private readonly opts;
130
+ private readonly snapshotGen;
131
+ private readonly vaultKey;
132
+ private staleFlag;
133
+ private cache;
134
+ /**
135
+ * Use `ChancesOAuthClientProvider.create({...})` instead — the factory
136
+ * captures the vault's current generation BEFORE any caller can call
137
+ * `saveTokens`/`invalidateCredentials` etc. Constructing manually is a
138
+ * test-only path that requires passing `snapshotGen` explicitly.
139
+ */
140
+ constructor(opts: ProviderOptions, snapshotGen: number);
141
+ /**
142
+ * Async factory — reads the vault to capture the current generation
143
+ * AT CONSTRUCTION TIME, satisfying the codex Round-1 MUST-FIX #5
144
+ * invariant. Any later `save*` whose snapshot is older than the
145
+ * vault's current generation gets dropped by `merge()`.
146
+ */
147
+ static create(opts: ProviderOptions): Promise<ChancesOAuthClientProvider>;
148
+ /** Read-only test seam — the gen captured at construction. */
149
+ get snapshotGeneration(): number;
150
+ /** Set when a vault read reveals the provider's snapshot is older
151
+ * than the current vault generation. Once set, the provider refuses
152
+ * to write — fresh provider needed (host restart). */
153
+ get isStale(): boolean;
154
+ get redirectUrl(): URL | undefined;
155
+ get clientMetadata(): OAuthClientMetadata;
156
+ /** SDK contract: a 32-byte base64url string. Independent of the PKCE
157
+ * verifier (codex Round-1 MUST-FIX #2) — state lives in the browser
158
+ * URL, verifier never leaves the chances process. */
159
+ state(): string;
160
+ clientInformation(): Promise<OAuthClientInformationMixed | undefined>;
161
+ saveClientInformation(info: OAuthClientInformationMixed): Promise<void>;
162
+ tokens(): Promise<OAuthTokens | undefined>;
163
+ saveTokens(tokens: OAuthTokens): Promise<void>;
164
+ redirectToAuthorization(url: URL): Promise<void>;
165
+ saveCodeVerifier(verifier: string): Promise<void>;
166
+ codeVerifier(): Promise<string>;
167
+ saveDiscoveryState(state: OAuthDiscoveryStateInput): Promise<void>;
168
+ discoveryState(): Promise<OAuthDiscoveryStateOutput | undefined>;
169
+ invalidateCredentials(scope: "all" | "client" | "tokens" | "verifier" | "discovery"): Promise<void>;
170
+ /**
171
+ * Wraps `vault.mergeMutate` with the generation-staleness guard. If
172
+ * the current vault payload's generation is greater than our captured
173
+ * snapshot, the provider is stale (its parent host should already have
174
+ * been restarted) and we DROP the write rather than resurrect cleared
175
+ * credentials. (codex Round-1 MUST-FIX #5)
176
+ */
177
+ private merge;
178
+ /** Returns the current payload or a fresh empty one with this server's
179
+ * configHash baked in. Centralised so every mutator starts from the
180
+ * same baseline. */
181
+ private seed;
182
+ /** SDK's `OAuthClientInformationMixed` is either `OAuthClientInformation`
183
+ * (preset/config) or `OAuthClientInformationFull` (DCR response). We
184
+ * store the FULL shape — preset entries lack the DCR metadata but
185
+ * have empty `redirect_uris` which is fine for refresh-token flows. */
186
+ private toFullClient;
187
+ private now;
188
+ }
189
+ export {};
190
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/mcp/oauth/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * (3.7) `ChancesOAuthClientProvider` — chances' implementation of the SDK's
3
+ * `OAuthClientProvider` interface (`@modelcontextprotocol/sdk/client/auth.js`).
4
+ *
5
+ * One provider instance per OAuth-configured MCP server. Holds:
6
+ * - A **generation snapshot** captured at construction. Late-arriving
7
+ * `save*` calls from an in-flight refresh that started BEFORE a logout
8
+ * are dropped when the snapshot is older than the vault's current
9
+ * generation (codex Round-1 MUST-FIX #5).
10
+ * - A 30-second in-memory `tokens()` cache, invalidated on every `save*`
11
+ * / `invalidateCredentials` and on detected staleness.
12
+ * - A reference to the per-server `Redactor` so newly-discovered secrets
13
+ * (access tokens, refresh tokens, PKCE verifier) join the scrub set
14
+ * AT SAVE TIME — so any subsequent thrown error is already scrubbed
15
+ * when it surfaces (codex Round-1 SHOULD-FIX redactor extension).
16
+ *
17
+ * **Hot path (`tokens()`):** vault read → generation check → freshness
18
+ * check. We deliberately do NOT proactively refresh from inside
19
+ * `tokens()` in v1 — the SDK's `auth()` orchestrator handles 401-driven
20
+ * refresh and the `RefreshCoalescer` infrastructure is wired at the
21
+ * host-level dispose/logout-abort seam, ready for a 3.7.1 follow-up that
22
+ * adds proactive refresh if real-world parallel-call refresh storms
23
+ * become an observed problem. Keeping V1 simple matches the SDK's own
24
+ * stance (the SDK doesn't coalesce either) and avoids a complex
25
+ * refresh-inside-tokens() path that re-discovers metadata on the hot
26
+ * path.
27
+ *
28
+ * **Write path:** every `save*` and `invalidateCredentials` routes
29
+ * through `vault.mergeMutate(key, mutator)` — the per-key in-process
30
+ * mutex serialises read-modify-write and prevents the partial-update
31
+ * clobber from codex Round-1 MUST-FIX #1.
32
+ *
33
+ * Construction-time DI lets unit tests substitute a fake vault, a fake
34
+ * callback server, and a deterministic clock.
35
+ */
36
+ import { randomBytes } from "node:crypto";
37
+ import { makeVaultKey } from "./config-hash.js";
38
+ /** RFC 7591 DCR registration request — sent on first-flow auth when the
39
+ * IdP supports DCR AND we don't have a pre-registered `clientId`. */
40
+ const CHANCES_CLIENT_METADATA = {
41
+ client_name: "chances-cli",
42
+ client_uri: "https://github.com/chances-ai/cli",
43
+ redirect_uris: [],
44
+ grant_types: ["authorization_code", "refresh_token"],
45
+ response_types: ["code"],
46
+ token_endpoint_auth_method: "none",
47
+ };
48
+ const VERIFIER_TTL_MS = 15 * 60 * 1000;
49
+ const DISCOVERY_TTL_MS = 24 * 60 * 60 * 1000;
50
+ const TOKEN_CACHE_TTL_MS = 30 * 1000;
51
+ const FAILED_AT_CACHE_TTL_MS = 30 * 1000;
52
+ export class ChancesOAuthClientProvider {
53
+ opts;
54
+ snapshotGen;
55
+ vaultKey;
56
+ staleFlag = false;
57
+ cache;
58
+ /**
59
+ * Use `ChancesOAuthClientProvider.create({...})` instead — the factory
60
+ * captures the vault's current generation BEFORE any caller can call
61
+ * `saveTokens`/`invalidateCredentials` etc. Constructing manually is a
62
+ * test-only path that requires passing `snapshotGen` explicitly.
63
+ */
64
+ constructor(opts, snapshotGen) {
65
+ this.opts = opts;
66
+ this.snapshotGen = snapshotGen;
67
+ this.vaultKey = makeVaultKey(opts.serverName, opts.serverConfig);
68
+ }
69
+ /**
70
+ * Async factory — reads the vault to capture the current generation
71
+ * AT CONSTRUCTION TIME, satisfying the codex Round-1 MUST-FIX #5
72
+ * invariant. Any later `save*` whose snapshot is older than the
73
+ * vault's current generation gets dropped by `merge()`.
74
+ */
75
+ static async create(opts) {
76
+ const key = makeVaultKey(opts.serverName, opts.serverConfig);
77
+ let snapshot = 0;
78
+ try {
79
+ const current = await opts.vault.read(key);
80
+ snapshot = current?.generation ?? 0;
81
+ }
82
+ catch (e) {
83
+ opts.logger.warn(`oauth provider '${opts.serverName}': snapshot read failed: ${opts.redactor(e.message)}`);
84
+ }
85
+ return new ChancesOAuthClientProvider(opts, snapshot);
86
+ }
87
+ /** Read-only test seam — the gen captured at construction. */
88
+ get snapshotGeneration() {
89
+ return this.snapshotGen;
90
+ }
91
+ /** Set when a vault read reveals the provider's snapshot is older
92
+ * than the current vault generation. Once set, the provider refuses
93
+ * to write — fresh provider needed (host restart). */
94
+ get isStale() {
95
+ return this.staleFlag;
96
+ }
97
+ // ─── OAuthClientProvider — required methods ──────────────────────────
98
+ get redirectUrl() {
99
+ try {
100
+ return this.opts.callbackServer.redirectUrl;
101
+ }
102
+ catch {
103
+ // `bind()` hasn't run yet — the SDK calls this before our flow has
104
+ // bound the callback. Returning undefined is the documented contract.
105
+ return undefined;
106
+ }
107
+ }
108
+ get clientMetadata() {
109
+ return CHANCES_CLIENT_METADATA;
110
+ }
111
+ /** SDK contract: a 32-byte base64url string. Independent of the PKCE
112
+ * verifier (codex Round-1 MUST-FIX #2) — state lives in the browser
113
+ * URL, verifier never leaves the chances process. */
114
+ state() {
115
+ return randomBytes(32).toString("base64url");
116
+ }
117
+ async clientInformation() {
118
+ const payload = await this.opts.vault.read(this.vaultKey);
119
+ // Codex Q5 resolution order: stored DCR result first, then config-provided.
120
+ if (payload?.client)
121
+ return payload.client;
122
+ const cfg = this.opts.serverConfig.auth;
123
+ if (cfg?.kind === "oauth" && cfg.clientId) {
124
+ return {
125
+ client_id: cfg.clientId,
126
+ client_secret: cfg.clientSecret,
127
+ };
128
+ }
129
+ return undefined;
130
+ }
131
+ async saveClientInformation(info) {
132
+ if (info.client_secret)
133
+ this.opts.redactor.add([info.client_secret]);
134
+ await this.merge((current) => ({
135
+ ...this.seed(current),
136
+ client: this.toFullClient(info),
137
+ }));
138
+ }
139
+ async tokens() {
140
+ const now = this.now();
141
+ // 1. In-memory cache.
142
+ if (this.cache) {
143
+ if (this.cache.kind === "tokens" && now - this.cache.cachedAt <= TOKEN_CACHE_TTL_MS) {
144
+ return this.cache.value;
145
+ }
146
+ if (this.cache.kind === "fail" && now - this.cache.cachedAt <= FAILED_AT_CACHE_TTL_MS) {
147
+ return undefined;
148
+ }
149
+ // TTL expired — drop and re-read.
150
+ this.cache = undefined;
151
+ }
152
+ // 2. Vault read.
153
+ const payload = await this.opts.vault.read(this.vaultKey);
154
+ if (!payload)
155
+ return undefined;
156
+ // 3. Generation guard — payload bumped under us → this provider is
157
+ // stale and must NOT return resurrected credentials.
158
+ if (payload.generation > this.snapshotGen) {
159
+ this.staleFlag = true;
160
+ this.cache = { kind: "fail", cachedAt: now };
161
+ return undefined;
162
+ }
163
+ if (!payload.tokens)
164
+ return undefined;
165
+ // 5. Add the token's strings to the redactor (defence-in-depth — they
166
+ // may have been added at save time, but a freshly-loaded provider
167
+ // starting from an existing vault hasn't seen the save yet).
168
+ this.opts.redactor.add([payload.tokens.access_token, payload.tokens.refresh_token].filter((s) => !!s));
169
+ // 6. Cache + return. We hand the SDK the snake_case bag as-is.
170
+ this.cache = { kind: "tokens", value: payload.tokens, cachedAt: now };
171
+ return payload.tokens;
172
+ }
173
+ async saveTokens(tokens) {
174
+ // Register the runtime secret BEFORE any persistence error can
175
+ // surface its value in a thrown message.
176
+ this.opts.redactor.add([tokens.access_token, tokens.refresh_token].filter((s) => !!s));
177
+ const expiresAt = typeof tokens.expires_in === "number" && Number.isFinite(tokens.expires_in)
178
+ ? this.now() + tokens.expires_in * 1000
179
+ : undefined;
180
+ await this.merge((current) => {
181
+ const seeded = this.seed(current);
182
+ // Preserve existing refresh_token when the IdP didn't return a new
183
+ // one — the SDK's `refreshAuthorization` already preserves it but
184
+ // a defensive save() might not. RFC 6749 §6 says "the
185
+ // authorization server MAY issue a new refresh token" (i.e. MAY,
186
+ // not MUST), so an absent refresh_token means "reuse the old one".
187
+ const refresh_token = tokens.refresh_token ?? seeded.tokens?.refresh_token;
188
+ return {
189
+ ...seeded,
190
+ tokens: { ...tokens, refresh_token, expires_at: expiresAt },
191
+ };
192
+ });
193
+ // Invalidate in-memory cache so the next tokens() call observes the write.
194
+ this.cache = undefined;
195
+ }
196
+ async redirectToAuthorization(url) {
197
+ await this.opts.openBrowser(url);
198
+ }
199
+ async saveCodeVerifier(verifier) {
200
+ this.opts.redactor.add([verifier]);
201
+ await this.merge((current) => ({
202
+ ...this.seed(current),
203
+ verifier: { value: verifier, createdAt: this.now() },
204
+ }));
205
+ }
206
+ async codeVerifier() {
207
+ const payload = await this.opts.vault.read(this.vaultKey);
208
+ const v = payload?.verifier;
209
+ if (!v)
210
+ throw new Error("OAuth provider: no PKCE verifier in storage (flow not started)");
211
+ if (this.now() - v.createdAt > VERIFIER_TTL_MS) {
212
+ throw new Error("OAuth provider: stored PKCE verifier expired (>15 min) — restart the flow");
213
+ }
214
+ return v.value;
215
+ }
216
+ // ─── OAuthClientProvider — optional methods ──────────────────────────
217
+ async saveDiscoveryState(state) {
218
+ // § 4.2: strip metadata bodies; keep URLs.
219
+ await this.merge((current) => ({
220
+ ...this.seed(current),
221
+ discovery: {
222
+ authorizationServerUrl: state.authorizationServerUrl,
223
+ resourceMetadataUrl: state.resourceMetadataUrl,
224
+ discoveredAt: this.now(),
225
+ },
226
+ }));
227
+ }
228
+ async discoveryState() {
229
+ const payload = await this.opts.vault.read(this.vaultKey);
230
+ const d = payload?.discovery;
231
+ if (!d)
232
+ return undefined;
233
+ if (this.now() - d.discoveredAt > DISCOVERY_TTL_MS)
234
+ return undefined;
235
+ return {
236
+ authorizationServerUrl: d.authorizationServerUrl,
237
+ resourceMetadataUrl: d.resourceMetadataUrl,
238
+ };
239
+ }
240
+ async invalidateCredentials(scope) {
241
+ this.cache = undefined;
242
+ await this.merge((current) => {
243
+ const seeded = this.seed(current);
244
+ switch (scope) {
245
+ case "all":
246
+ return {
247
+ v: 1,
248
+ configHash: seeded.configHash,
249
+ generation: seeded.generation + 1,
250
+ };
251
+ case "tokens":
252
+ return { ...seeded, tokens: undefined };
253
+ case "client":
254
+ return { ...seeded, client: undefined };
255
+ case "verifier":
256
+ return { ...seeded, verifier: undefined };
257
+ case "discovery":
258
+ return { ...seeded, discovery: undefined };
259
+ }
260
+ });
261
+ }
262
+ // ─── Internal helpers ────────────────────────────────────────────────
263
+ /**
264
+ * Wraps `vault.mergeMutate` with the generation-staleness guard. If
265
+ * the current vault payload's generation is greater than our captured
266
+ * snapshot, the provider is stale (its parent host should already have
267
+ * been restarted) and we DROP the write rather than resurrect cleared
268
+ * credentials. (codex Round-1 MUST-FIX #5)
269
+ */
270
+ async merge(mutator) {
271
+ await this.opts.vault.mergeMutate(this.vaultKey, (current) => {
272
+ if (current && current.generation > this.snapshotGen) {
273
+ this.staleFlag = true;
274
+ this.opts.logger.debug(`oauth provider '${this.opts.serverName}': dropping save (snapshot gen ${this.snapshotGen} < vault gen ${current.generation})`);
275
+ return current;
276
+ }
277
+ return mutator(current);
278
+ });
279
+ }
280
+ /** Returns the current payload or a fresh empty one with this server's
281
+ * configHash baked in. Centralised so every mutator starts from the
282
+ * same baseline. */
283
+ seed(current) {
284
+ if (current)
285
+ return current;
286
+ return {
287
+ v: 1,
288
+ configHash: this.vaultKey.split("|")[2] ?? "",
289
+ generation: this.snapshotGen,
290
+ };
291
+ }
292
+ /** SDK's `OAuthClientInformationMixed` is either `OAuthClientInformation`
293
+ * (preset/config) or `OAuthClientInformationFull` (DCR response). We
294
+ * store the FULL shape — preset entries lack the DCR metadata but
295
+ * have empty `redirect_uris` which is fine for refresh-token flows. */
296
+ toFullClient(info) {
297
+ if ("redirect_uris" in info)
298
+ return info;
299
+ return { redirect_uris: [], ...info };
300
+ }
301
+ now() {
302
+ return (this.opts.now ?? Date.now)();
303
+ }
304
+ }
305
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/mcp/oauth/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * (3.7) Per-server in-flight refresh coalescer.
3
+ *
4
+ * Without this, oh-my-pi (`commit 37fe5b93b`) hit a real bug: two tool
5
+ * calls fire HTTP requests simultaneously, both get 401 with the same
6
+ * just-expired token, both trigger `refreshAuthorization()`. The first
7
+ * succeeds and rotates the refresh_token; the second presents the now-
8
+ * rotated old refresh_token and gets `invalid_grant` — credential
9
+ * permanently broken, user must re-auth.
10
+ *
11
+ * Fix: keep one in-flight refresh promise per server; awaiters share it.
12
+ *
13
+ * **(codex Round-1 MUST-FIX #4)** Two bugs in the original sketch:
14
+ * 1. Sync throw in `refreshFn` would leave a permanently rejected
15
+ * promise in the map (set happened AFTER IIFE construction).
16
+ * Fix: `Promise.resolve().then(refreshFn)` so sync throws go
17
+ * THROUGH the promise.
18
+ * 2. `inFlight.delete` without identity check could delete a sibling
19
+ * cycle's promise on misordered cleanup. Fix: only delete when
20
+ * the slot still holds OUR promise.
21
+ *
22
+ * Also adds `abort(serverName)`: fires an AbortController so an
23
+ * in-flight refresh can be cancelled by `/mcp logout` (codex Round-1
24
+ * MUST-FIX #4). The cancelled fetch unwinds before saveTokens runs;
25
+ * the generation check (see `provider.ts`) is the belt-and-braces
26
+ * defence for the race where the fetch slipped past the abort signal.
27
+ */
28
+ export declare class RefreshCoalescer<T> {
29
+ private readonly inFlight;
30
+ /**
31
+ * Coalesces concurrent refresh requests for the same `serverName`.
32
+ * The first call invokes `refreshFn(abortSignal)`; subsequent calls
33
+ * during the in-flight window receive the SAME promise. The slot
34
+ * clears after settlement so the next call kicks off a fresh refresh.
35
+ */
36
+ refresh(serverName: string, refreshFn: (signal: AbortSignal) => Promise<T | undefined>): Promise<T | undefined>;
37
+ /** Fires the AbortController for an in-flight refresh. Idempotent —
38
+ * no-op when nothing's in flight. Used by `/mcp logout` to stop a
39
+ * refresh from writing tokens AFTER the credential was cleared. */
40
+ abort(serverName: string): void;
41
+ /** Test seam / `/mcp status` lookup. */
42
+ isInFlight(serverName: string): boolean;
43
+ /** Number of distinct servers currently refreshing. */
44
+ size(): number;
45
+ }
46
+ //# sourceMappingURL=refresh-coalescer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-coalescer.d.ts","sourceRoot":"","sources":["../../../src/mcp/oauth/refresh-coalescer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,qBAAa,gBAAgB,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D;;;;;OAKG;IACG,OAAO,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GACzD,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IA2BzB;;wEAEoE;IACpE,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAI/B,wCAAwC;IACxC,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIvC,uDAAuD;IACvD,IAAI,IAAI,MAAM;CAGf"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * (3.7) Per-server in-flight refresh coalescer.
3
+ *
4
+ * Without this, oh-my-pi (`commit 37fe5b93b`) hit a real bug: two tool
5
+ * calls fire HTTP requests simultaneously, both get 401 with the same
6
+ * just-expired token, both trigger `refreshAuthorization()`. The first
7
+ * succeeds and rotates the refresh_token; the second presents the now-
8
+ * rotated old refresh_token and gets `invalid_grant` — credential
9
+ * permanently broken, user must re-auth.
10
+ *
11
+ * Fix: keep one in-flight refresh promise per server; awaiters share it.
12
+ *
13
+ * **(codex Round-1 MUST-FIX #4)** Two bugs in the original sketch:
14
+ * 1. Sync throw in `refreshFn` would leave a permanently rejected
15
+ * promise in the map (set happened AFTER IIFE construction).
16
+ * Fix: `Promise.resolve().then(refreshFn)` so sync throws go
17
+ * THROUGH the promise.
18
+ * 2. `inFlight.delete` without identity check could delete a sibling
19
+ * cycle's promise on misordered cleanup. Fix: only delete when
20
+ * the slot still holds OUR promise.
21
+ *
22
+ * Also adds `abort(serverName)`: fires an AbortController so an
23
+ * in-flight refresh can be cancelled by `/mcp logout` (codex Round-1
24
+ * MUST-FIX #4). The cancelled fetch unwinds before saveTokens runs;
25
+ * the generation check (see `provider.ts`) is the belt-and-braces
26
+ * defence for the race where the fetch slipped past the abort signal.
27
+ */
28
+ export class RefreshCoalescer {
29
+ inFlight = new Map();
30
+ /**
31
+ * Coalesces concurrent refresh requests for the same `serverName`.
32
+ * The first call invokes `refreshFn(abortSignal)`; subsequent calls
33
+ * during the in-flight window receive the SAME promise. The slot
34
+ * clears after settlement so the next call kicks off a fresh refresh.
35
+ */
36
+ async refresh(serverName, refreshFn) {
37
+ const existing = this.inFlight.get(serverName);
38
+ if (existing)
39
+ return existing.promise;
40
+ const abort = new AbortController();
41
+ // Promise.resolve().then so a synchronous throw inside refreshFn
42
+ // surfaces through the promise rather than escaping back to the
43
+ // caller of `refresh()`. Without this, sync-throw before the first
44
+ // `await` would bypass our error handling entirely.
45
+ let entry;
46
+ const promise = Promise.resolve()
47
+ .then(() => refreshFn(abort.signal))
48
+ .finally(() => {
49
+ // Identity-check delete: only remove the slot if WE are still
50
+ // there. Defends against pathological orderings where another
51
+ // cycle slotted in under the same key (e.g. logout → relogin
52
+ // → new refresh all in one tick).
53
+ const current = this.inFlight.get(serverName);
54
+ if (current && current === entry) {
55
+ this.inFlight.delete(serverName);
56
+ }
57
+ });
58
+ entry = { promise, abort };
59
+ this.inFlight.set(serverName, entry);
60
+ return promise;
61
+ }
62
+ /** Fires the AbortController for an in-flight refresh. Idempotent —
63
+ * no-op when nothing's in flight. Used by `/mcp logout` to stop a
64
+ * refresh from writing tokens AFTER the credential was cleared. */
65
+ abort(serverName) {
66
+ this.inFlight.get(serverName)?.abort.abort();
67
+ }
68
+ /** Test seam / `/mcp status` lookup. */
69
+ isInFlight(serverName) {
70
+ return this.inFlight.has(serverName);
71
+ }
72
+ /** Number of distinct servers currently refreshing. */
73
+ size() {
74
+ return this.inFlight.size;
75
+ }
76
+ }
77
+ //# sourceMappingURL=refresh-coalescer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-coalescer.js","sourceRoot":"","sources":["../../../src/mcp/oauth/refresh-coalescer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,MAAM,OAAO,gBAAgB;IACV,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE3D;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CACX,UAAkB,EAClB,SAA0D;QAE1D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC,OAAO,CAAC;QAEtC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;QACpC,iEAAiE;QACjE,gEAAgE;QAChE,mEAAmE;QACnE,oDAAoD;QACpD,IAAI,KAAkB,CAAC;QACvB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE;aAC9B,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;aACnC,OAAO,CAAC,GAAG,EAAE;YACZ,8DAA8D;YAC9D,8DAA8D;YAC9D,6DAA6D;YAC7D,kCAAkC;YAClC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC9C,IAAI,OAAO,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;gBACjC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACnC,CAAC;QACH,CAAC,CAAC,CAAC;QACL,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACrC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;wEAEoE;IACpE,KAAK,CAAC,UAAkB;QACtB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,wCAAwC;IACxC,UAAU,CAAC,UAAkB;QAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,uDAAuD;IACvD,IAAI;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;CACF"}