@desplega.ai/agent-swarm 1.158.0 → 1.159.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 (272) hide show
  1. package/dist/{acp-adapter-xaq4n6pc.js → acp-adapter-qhch0n2v.js} +1 -1
  2. package/dist/{actions-p9hz5yat.js → actions-dtsh1mfb.js} +33 -29
  3. package/dist/{anthropic-messages-51pz26qe.js → anthropic-messages-f2h06k64.js} +8 -3
  4. package/dist/{app-g26wdcz6.js → app-y1b9srva.js} +10 -9
  5. package/dist/{assistant-wydfqkb3.js → assistant-31hwy5xj.js} +34 -30
  6. package/dist/{azure-openai-responses-8gksqtmz.js → azure-openai-responses-0k1mfax5.js} +12 -13
  7. package/dist/{boot-reembed-se8zmdcp.js → boot-reembed-48d2qpkt.js} +11 -11
  8. package/dist/{boot-reembed-g5ckg9yb.js → boot-reembed-w0bryhkr.js} +12 -12
  9. package/dist/{boot-scrub-logs-wfxqan61.js → boot-scrub-logs-t65wbrn7.js} +9 -8
  10. package/dist/{boot-seeds-s3q01xj3.js → boot-seeds-h7c32hvm.js} +12 -11
  11. package/dist/{claude-adapter-tv2x4f36.js → claude-adapter-1t6611ne.js} +26 -21
  12. package/dist/{claude-managed-adapter-41b2s7fd.js → claude-managed-adapter-werp659j.js} +4 -2
  13. package/dist/{claude-managed-setup-60g8aazx.js → claude-managed-setup-y6rdgbc1.js} +11 -2
  14. package/dist/claude-sdk-session-zhkyk8yw.js +29319 -0
  15. package/dist/{cli-vj6h9kc2.js → cli-0m7wct0d.js} +59 -1
  16. package/dist/{cli-wscdvcqz.js → cli-0n2e4x49.js} +1 -1
  17. package/dist/cli-0xqde53b.js +176 -0
  18. package/dist/{cli-xberxa1r.js → cli-10ym38qc.js} +2 -2
  19. package/dist/{cli-5b6b62tz.js → cli-13hw4z91.js} +1 -1
  20. package/dist/{cli-rqz8qbw7.js → cli-20035trh.js} +50 -18
  21. package/dist/{cli-k0g1sjzm.js → cli-31nkw3g3.js} +3 -3
  22. package/dist/cli-3gvrr8vj.js +1422 -0
  23. package/dist/{cli-c07r9a2m.js → cli-48527fpw.js} +9 -8
  24. package/dist/{cli-24k806sc.js → cli-4aj25szq.js} +2 -2
  25. package/dist/{cli-7gtvvpbh.js → cli-4tvgz99m.js} +7 -7
  26. package/dist/cli-503ya656.js +455 -0
  27. package/dist/{cli-cw9zck8j.js → cli-5f2hq128.js} +8 -4
  28. package/dist/{cli-s8eet254.js → cli-68bmme5p.js} +11078 -27833
  29. package/dist/{cli-ftfvggzx.js → cli-79aq9cgb.js} +794 -563
  30. package/dist/cli-7cxw8b60.js +14910 -0
  31. package/dist/cli-7kyk9jr3.js +22 -0
  32. package/dist/cli-7zttzetj.js +324 -0
  33. package/dist/{cli-y95wp3tg.js → cli-96qd5psa.js} +1 -1
  34. package/dist/{cli-g8djp6qe.js → cli-9yg65c1j.js} +22 -0
  35. package/dist/{cli-3gmcwfyx.js → cli-ardpzhjm.js} +5 -5
  36. package/dist/{cli-7s3syk22.js → cli-b6thtzac.js} +5 -5
  37. package/dist/{cli-00w2c66p.js → cli-bgv70kym.js} +13 -2
  38. package/dist/{cli-j55n64b6.js → cli-bv0a38v9.js} +163 -44
  39. package/dist/cli-ca2w7zv7.js +115 -0
  40. package/dist/cli-cpvsaqsw.js +333 -0
  41. package/dist/cli-cygyqj3b.js +754 -0
  42. package/dist/{cli-x24pshtq.js → cli-dvjpkkjd.js} +6 -6
  43. package/dist/{cli-5d10vd2m.js → cli-e0ndxhtk.js} +4 -4
  44. package/dist/{cli-4198qc9v.js → cli-e4xnc0qp.js} +15 -3
  45. package/dist/{cli-rjehvrts.js → cli-eg7cdg68.js} +8 -8
  46. package/dist/cli-ejpygnqz.js +3815 -0
  47. package/dist/{cli-fgqxwrpv.js → cli-fjtp3463.js} +1631 -127
  48. package/dist/{cli-zt1227k1.js → cli-fkqhbg8g.js} +5 -3
  49. package/dist/{cli-47m6dkd3.js → cli-g9df2m37.js} +409 -168
  50. package/dist/cli-gzyh0j0g.js +8455 -0
  51. package/dist/cli-hgqfttfc.js +183 -0
  52. package/dist/cli-hj65wsts.js +17755 -0
  53. package/dist/cli-j9z8mfx3.js +476 -0
  54. package/dist/{cli-aehm1rbq.js → cli-kc3dp84d.js} +1135 -61
  55. package/dist/{cli-h38x0qhg.js → cli-kdnehe0t.js} +4 -3
  56. package/dist/{cli-20cj37vt.js → cli-m4excttz.js} +136 -2216
  57. package/dist/{cli-8qmme6vd.js → cli-mg24hkt4.js} +2 -2
  58. package/dist/{cli-xhaeg2nc.js → cli-mzkqpxee.js} +1 -1
  59. package/dist/{cli-q67y4wzm.js → cli-phsaj4xq.js} +24 -2
  60. package/dist/{cli-pw9rj5t9.js → cli-pqetb83f.js} +1 -176
  61. package/dist/{cli-rj19hc34.js → cli-qjxy52w7.js} +1 -1
  62. package/dist/{cli-53sz8gkz.js → cli-qv4jv8bx.js} +1 -1
  63. package/dist/cli-qvnnx4ct.js +1051 -0
  64. package/dist/cli-rb9m6rpe.js +4980 -0
  65. package/dist/{cli-tt9k8r35.js → cli-rfezr056.js} +476 -909
  66. package/dist/{cli-ggm92wcn.js → cli-rgjprct3.js} +1 -0
  67. package/dist/{cli-9httdpyq.js → cli-rrqmqr1s.js} +1 -1
  68. package/dist/cli-rykfmgwg.js +102 -0
  69. package/dist/{cli-eetrvv91.js → cli-t3p77qk1.js} +1 -1
  70. package/dist/{cli-68w09sjw.js → cli-vn0qv9cj.js} +25 -88
  71. package/dist/{cli-987hg0aa.js → cli-vwbbaecp.js} +2 -2
  72. package/dist/cli-wd9hz40d.js +69 -0
  73. package/dist/{cli-j57qkrpq.js → cli-wgjx0q33.js} +13 -13
  74. package/dist/cli-xgpf00sk.js +2149 -0
  75. package/dist/{cli-mcnhgnn1.js → cli-ykk5jaax.js} +184 -26
  76. package/dist/{cli-aky8tctm.js → cli-z9x0sh1t.js} +3 -3
  77. package/dist/{cli-1hkrch3k.js → cli-zw65mab6.js} +5 -5
  78. package/dist/cli.js +38 -29
  79. package/dist/cloudflare-workers-ai-system-one-1sg586qv.js +46 -0
  80. package/dist/{codex-adapter-cnm8wk11.js → codex-adapter-jqfqqa21.js} +21 -14
  81. package/dist/{codex-hook-9hptmdhj.js → codex-hook-5eys3dya.js} +2 -2
  82. package/dist/{codex-session-runner-gfg547zj.js → codex-session-runner-zd341dsm.js} +21 -14
  83. package/dist/{commands-e3bn0p75.js → commands-vfsysz4s.js} +9 -8
  84. package/dist/{db-03v9kzr2.js → db-s3znhvbn.js} +15 -10
  85. package/dist/{dispatcher-nrmg8yht.js → dispatcher-5xn0c7rf.js} +32 -28
  86. package/dist/{dsh-adapter-k03nfqa6.js → dsh-adapter-894mgfvs.js} +3 -3
  87. package/dist/{e2b-93x1k0j5.js → e2b-ez07znam.js} +4 -3
  88. package/dist/execute-71ysgv35.js +965 -0
  89. package/dist/{google-generative-ai-bxmv50nc.js → google-generative-ai-1nfcdphp.js} +5 -4
  90. package/dist/{google-vertex-809af514.js → google-vertex-1tqcd7b1.js} +5 -4
  91. package/dist/{handlers-aqcedbyb.js → handlers-5w4jwycr.js} +35 -31
  92. package/dist/{hook-7vkskkre.js → hook-853bxrr6.js} +16 -13
  93. package/dist/{hook-ybvmrmhq.js → hook-z0hbxj85.js} +16 -13
  94. package/dist/{http-2eqa8t24.js → http-akc3wy3m.js} +600 -123
  95. package/dist/{index-f6cryxwe.js → index-4a5njnm1.js} +2 -59
  96. package/dist/{index-a3vn96n8.js → index-5sz16ksh.js} +187 -47
  97. package/dist/{index-c6zsv1bd.js → index-bjmjh9h4.js} +11 -11
  98. package/dist/{index-chgdkq36.js → index-cd151qkq.js} +32 -28
  99. package/dist/{index-x71bvq0p.js → index-k661dzzr.js} +33 -29
  100. package/dist/{index-1yks3qcr.js → index-ttpbhnjg.js} +32 -28
  101. package/dist/{keepalive-vtkf5n99.js → keepalive-8p9rpvz5.js} +11 -10
  102. package/dist/{lead-h3sp357x.js → lead-jp69z5h3.js} +49 -45
  103. package/dist/llama-cpp-classify-t2tccams.js +335 -0
  104. package/dist/{maintenance-jzeqtk3q.js → maintenance-fwx4ev0k.js} +12 -12
  105. package/dist/{mistral-conversations-zx0kn554.js → mistral-conversations-bhvwqcnk.js} +14 -17
  106. package/dist/{oauth-refresh-sweep-qp3dzmdp.js → oauth-refresh-sweep-cwcq1ctx.js} +10 -9
  107. package/dist/{onboard-tmsp0qbz.js → onboard-2b4q0t84.js} +6 -5
  108. package/dist/{openai-codex-responses-4vjqzvjp.js → openai-codex-responses-nekthgks.js} +21 -8
  109. package/dist/{openai-completions-9hjrqbw8.js → openai-completions-hxtzwj0s.js} +8 -11
  110. package/dist/{openai-responses-7b1pn0xt.js → openai-responses-w4r118w7.js} +22 -17
  111. package/dist/{opencode-adapter-x6x2da1z.js → opencode-adapter-zz7xvjj6.js} +6 -4
  112. package/dist/{openrouter-images-9f9ek4rw.js → openrouter-images-b6sqqjgn.js} +4 -6
  113. package/dist/{otel-impl-9mz73q65.js → otel-impl-g8rs4vwr.js} +1 -1
  114. package/dist/{pi-messages-txpjj39q.js → pi-messages-vxdt8v7j.js} +2 -1
  115. package/dist/{pi-mono-adapter-m6dvv0dz.js → pi-mono-adapter-n0me28wt.js} +29 -17
  116. package/dist/pricing-refresh-0v77385v.js +39 -0
  117. package/dist/{rbac-roles-c9wqz83x.js → rbac-roles-krjyazbj.js} +10 -9
  118. package/dist/{render-v2-gryrh9h8.js → render-v2-3wjsp9aw.js} +10 -9
  119. package/dist/runtime-ktjysacx.js +24 -0
  120. package/dist/{setup-q9720bt6.js → setup-yeqryke6.js} +6 -5
  121. package/dist/typesafe-system-one-96960jxp.js +27 -0
  122. package/dist/{virtual-modules-3htpgn8x.js → virtual-modules-dzdeq1sf.js} +21 -13
  123. package/dist/{work-objects-b76x7deb.js → work-objects-3pakpkw0.js} +35 -31
  124. package/dist/{worker-6vrxbv41.js → worker-85testgj.js} +49 -45
  125. package/openapi.json +863 -14
  126. package/package.json +9 -8
  127. package/src/be/db/agents.ts +20 -0
  128. package/src/be/db/tasks/human-free.ts +442 -0
  129. package/src/be/db/tasks/read.ts +56 -1
  130. package/src/be/db/tasks/write.ts +107 -11
  131. package/src/be/db.ts +89 -165
  132. package/src/be/harness-model-support.ts +182 -0
  133. package/src/be/human-free-drain.ts +67 -0
  134. package/src/be/migrations/182_task_human_free_flag.sql +63 -0
  135. package/src/be/migrations/183_human_free_reclassify_queue.sql +25 -0
  136. package/src/be/migrations/184_model_catalog.sql +120 -0
  137. package/src/be/model-catalog-store.ts +644 -0
  138. package/src/be/model-tier-keys.ts +31 -0
  139. package/src/be/model-tier-resolution.ts +489 -0
  140. package/src/be/model-validation.ts +112 -0
  141. package/src/be/models-catalog.ts +61 -7
  142. package/src/be/modelsdev-cache.json +82 -0
  143. package/src/be/modelsdev-cache.ts +2 -0
  144. package/src/be/pricing-refresh.ts +154 -12
  145. package/src/be/scripts/typecheck.ts +15 -0
  146. package/src/be/seed-scripts/catalog/task-failure-audit.ts +61 -9
  147. package/src/be/swarm-config-guard.ts +6 -0
  148. package/src/cli.tsx +10 -0
  149. package/src/commands/claude-managed-setup.ts +3 -1
  150. package/src/commands/runner.ts +76 -2
  151. package/src/extensions/contract-types.generated.ts +1 -1
  152. package/src/heartbeat/heartbeat.ts +64 -15
  153. package/src/heartbeat/templates.ts +2 -2
  154. package/src/http/agents.ts +49 -1
  155. package/src/http/index.ts +8 -0
  156. package/src/http/models-catalog.ts +357 -8
  157. package/src/http/poll.ts +45 -2
  158. package/src/http/schedules.ts +36 -6
  159. package/src/http/session-data.ts +2 -5
  160. package/src/http/stats.ts +6 -8
  161. package/src/http/tasks.ts +26 -1
  162. package/src/http/usage-cache.ts +56 -15
  163. package/src/http/workflows.ts +35 -8
  164. package/src/prompts/session-templates.ts +1 -0
  165. package/src/providers/claude-managed-models.ts +47 -161
  166. package/src/providers/codex-adapter.ts +4 -4
  167. package/src/providers/codex-models.ts +65 -198
  168. package/src/providers/reasoning-effort.ts +44 -140
  169. package/src/rbac/legacy-policy.ts +25 -0
  170. package/src/rbac/permissions.ts +10 -0
  171. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  172. package/src/scripts-runtime/types/stdlib.d.ts +15 -0
  173. package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
  174. package/src/scripts-runtime/types/swarm-sdk.d.ts +15 -0
  175. package/src/server.ts +6 -0
  176. package/src/slack/message-text.ts +56 -2
  177. package/src/slack/thread-buffer.ts +10 -2
  178. package/src/tests/agents-list-model-display.test.ts +4 -6
  179. package/src/tests/base-prompt.test.ts +3 -0
  180. package/src/tests/claude-adapter.test.ts +10 -0
  181. package/src/tests/claude-managed-adapter.test.ts +12 -18
  182. package/src/tests/codex-adapter.test.ts +2 -1
  183. package/src/tests/codex-gpt6-release.test.ts +66 -5
  184. package/src/tests/context-window.test.ts +7 -5
  185. package/src/tests/db-extraction-contract.test.ts +1 -0
  186. package/src/tests/db-task-read-contract.test.ts +5 -1
  187. package/src/tests/db-task-write-contract.test.ts +4 -1
  188. package/src/tests/docs-index-footprint.test.js +3 -3
  189. package/src/tests/explicit-model-validation.test.ts +332 -0
  190. package/src/tests/harness-model-support.test.ts +374 -0
  191. package/src/tests/heartbeat-checklist-config.test.ts +71 -0
  192. package/src/tests/heartbeat-checklist.test.ts +4 -2
  193. package/src/tests/heartbeat-orphan-resume.test.ts +48 -0
  194. package/src/tests/human-free-flag.test.ts +279 -0
  195. package/src/tests/human-free-oracle.ts +57 -0
  196. package/src/tests/human-free-reclassify.test.ts +669 -0
  197. package/src/tests/migration-046-budgets.test.ts +3 -3
  198. package/src/tests/model-catalog-migration.test.ts +134 -0
  199. package/src/tests/model-catalog-table.test.ts +249 -0
  200. package/src/tests/model-catalog-write-access.test.ts +387 -0
  201. package/src/tests/model-tier-resolution.test.ts +519 -0
  202. package/src/tests/models-catalog.test.ts +1 -1
  203. package/src/tests/multi-runtime-enabled-stats.test.ts +65 -1
  204. package/src/tests/package-publish.test.ts +137 -36
  205. package/src/tests/pricing-routes.test.ts +2 -2
  206. package/src/tests/rbac-engine.test.ts +42 -2
  207. package/src/tests/reasoning-effort.test.ts +109 -0
  208. package/src/tests/refresh-modelsdev-carryforward.test.ts +5 -5
  209. package/src/tests/runtime-model-catalog.test.ts +86 -0
  210. package/src/tests/script-connections.test.ts +5 -0
  211. package/src/tests/seed-scripts.test.ts +89 -0
  212. package/src/tests/session-costs.test.ts +14 -11
  213. package/src/tests/slack-message-text.test.ts +101 -0
  214. package/src/tests/slack-thread-buffer-context.test.ts +93 -0
  215. package/src/tests/task-cascade-fail.test.ts +148 -2
  216. package/src/tests/tool-annotations.test.ts +1 -1
  217. package/src/tests/usage-cache.test.ts +166 -0
  218. package/src/tests/workflow-executors.test.ts +5 -2
  219. package/src/tests/workflow-system-one-decision-review.test.ts +1021 -0
  220. package/src/tests/workflow-system-one-decision-save-warnings.test.ts +365 -0
  221. package/src/tests/workflow-system-one-decision.test.ts +2063 -0
  222. package/src/tools/model-catalog/index.ts +137 -0
  223. package/src/tools/schedules/create-schedule.ts +16 -1
  224. package/src/tools/schedules/patch-schedule.ts +17 -0
  225. package/src/tools/schedules/update-schedule.ts +17 -0
  226. package/src/tools/send-task.ts +14 -0
  227. package/src/tools/task-action.ts +12 -0
  228. package/src/tools/tool-config.ts +3 -1
  229. package/src/tools/workflows/create-workflow.ts +9 -2
  230. package/src/tools/workflows/patch-workflow-node.ts +17 -7
  231. package/src/tools/workflows/patch-workflow.ts +9 -2
  232. package/src/tools/workflows/update-workflow.ts +9 -2
  233. package/src/types.ts +30 -1
  234. package/src/utils/context-window.ts +47 -53
  235. package/src/utils/harness-cli-version.ts +115 -0
  236. package/src/utils/harness-model-error.ts +33 -0
  237. package/src/utils/runtime-model-catalog.ts +137 -0
  238. package/src/workflows/approval-resolution.ts +39 -0
  239. package/src/workflows/definition.ts +7 -0
  240. package/src/workflows/engine.ts +69 -1
  241. package/src/workflows/executors/agent-task.ts +9 -0
  242. package/src/workflows/executors/base.ts +53 -0
  243. package/src/workflows/executors/human-in-the-loop.ts +8 -8
  244. package/src/workflows/executors/index.ts +1 -0
  245. package/src/workflows/executors/registry.ts +2 -0
  246. package/src/workflows/executors/system-one-decision.ts +1232 -0
  247. package/src/workflows/executors/system-one-providers.ts +301 -0
  248. package/src/workflows/executors/workflow-llm.ts +13 -1
  249. package/src/workflows/readiness.ts +86 -0
  250. package/src/workflows/recovery.ts +12 -12
  251. package/src/workflows/resume.ts +23 -9
  252. package/templates/schedules/daily-blocker-digest/content.md +4 -5
  253. package/templates/skills/agent-browser/SKILL.md +6 -5
  254. package/templates/skills/agent-browser/content.md +6 -5
  255. package/templates/skills/heartbeat-runbook/config.json +1 -1
  256. package/templates/skills/heartbeat-runbook/content.md +2 -2
  257. package/templates/skills/swarm-scripts/SKILL.md +1 -1
  258. package/templates/skills/swarm-scripts/content.md +1 -1
  259. package/templates/skills/workflow-iterate/content.md +81 -1
  260. package/templates/workflows/claude-code-changelog-watch/content.md +2 -0
  261. package/templates/workflows/pr-review-status-sweep/content.md +2 -0
  262. package/dist/claude-sdk-session-t442qz6f.js +0 -39108
  263. package/dist/cli-0xjkj1j7.js +0 -61
  264. package/dist/cli-65hjmev5.js +0 -1141
  265. package/dist/cli-aw1jwp1r.js +0 -14075
  266. package/dist/cli-h967nb6r.js +0 -6603
  267. package/dist/cli-vapyk8sw.js +0 -20
  268. package/dist/cli-ws6rqycm.js +0 -247
  269. package/dist/pricing-refresh-v1cbr9te.js +0 -27
  270. package/src/providers/modelsdev-reasoning.json +0 -6453
  271. package/dist/{codex-login-x3jk4etn.js → codex-login-he9vrn90.js} +3 -3
  272. package/dist/{index-f24115ge.js → index-2wsdsnmm.js} +3 -3
@@ -0,0 +1,1422 @@
1
+ import {
2
+ FileAuthStorageBackend,
3
+ JSON_RPC_ERROR_CODES,
4
+ LATEST_PROTOCOL_VERSION,
5
+ McpAuthRequiredError,
6
+ McpClient,
7
+ McpError,
8
+ McpHttpError,
9
+ McpSessionExpiredError,
10
+ StdioTransport,
11
+ StreamableHttpTransport,
12
+ isMcpAppResource,
13
+ isObject,
14
+ require_proper_lockfile,
15
+ resolveConfigValueOrThrow,
16
+ resolveHeadersOrThrow
17
+ } from "./cli-ejpygnqz.js";
18
+ import {
19
+ APP_NAME,
20
+ VERSION,
21
+ getAgentDir
22
+ } from "./cli-hj65wsts.js";
23
+ import {
24
+ oauthErrorHtml,
25
+ oauthSuccessHtml
26
+ } from "./cli-rykfmgwg.js";
27
+ import {
28
+ __toESM
29
+ } from "./cli-bc05qbss.js";
30
+
31
+ // node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/runtime.js
32
+ import { homedir } from "node:os";
33
+ import { basename, join as join2, resolve } from "node:path";
34
+ import { pathToFileURL } from "node:url";
35
+
36
+ // node_modules/@earendil-works/pi-mcp/dist/oauth/callback.js
37
+ import { createServer } from "node:http";
38
+ function plainText(page) {
39
+ if (page.ok)
40
+ return "Authorization complete. You may close this window.";
41
+ return page.details ? `${page.message}
42
+
43
+ ${page.details}` : page.message;
44
+ }
45
+
46
+ class OAuthCallbackServer {
47
+ redirectUrl;
48
+ server;
49
+ path;
50
+ timeoutMs;
51
+ renderPage;
52
+ pending = new Map;
53
+ constructor(server, redirectUrl, path, timeoutMs, renderPage) {
54
+ this.server = server;
55
+ this.redirectUrl = redirectUrl;
56
+ this.path = path;
57
+ this.timeoutMs = timeoutMs;
58
+ this.renderPage = renderPage;
59
+ }
60
+ static async listen(options = {}) {
61
+ const host = options.host ?? "127.0.0.1";
62
+ const redirectHost = options.redirectHost ?? host;
63
+ const path = options.path ?? "/callback";
64
+ let instance;
65
+ const server = createServer((request, response) => instance?.handle(request.url ?? "/", response));
66
+ await new Promise((resolve, reject) => {
67
+ server.once("error", reject);
68
+ server.listen(options.port ?? 0, host, () => {
69
+ server.off("error", reject);
70
+ resolve();
71
+ });
72
+ });
73
+ const address = server.address();
74
+ if (!address || typeof address === "string")
75
+ throw new Error("OAuth callback server did not bind to TCP");
76
+ instance = new OAuthCallbackServer(server, `http://${redirectHost.includes(":") ? `[${redirectHost}]` : redirectHost}:${address.port}${path}`, path, options.timeoutMs ?? 5 * 60000, options.renderPage);
77
+ return instance;
78
+ }
79
+ waitForCallback(state) {
80
+ if (this.pending.has(state))
81
+ throw new Error("OAuth state is already pending");
82
+ return new Promise((resolve, reject) => {
83
+ const timer = setTimeout(() => {
84
+ this.pending.delete(state);
85
+ reject(new Error("OAuth callback timed out"));
86
+ }, this.timeoutMs);
87
+ this.pending.set(state, { resolve, reject, timer });
88
+ });
89
+ }
90
+ async close() {
91
+ for (const pending of this.pending.values()) {
92
+ clearTimeout(pending.timer);
93
+ pending.reject(new Error("OAuth callback server closed"));
94
+ }
95
+ this.pending.clear();
96
+ await new Promise((resolve, reject) => {
97
+ this.server.close((error) => error ? reject(error) : resolve());
98
+ });
99
+ }
100
+ reply(response, status, page) {
101
+ if (this.renderPage) {
102
+ response.writeHead(status, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
103
+ response.end(this.renderPage(page));
104
+ } else {
105
+ response.writeHead(status, { "content-type": "text/plain; charset=utf-8" }).end(plainText(page));
106
+ }
107
+ }
108
+ handle(rawUrl, response) {
109
+ const url = new URL(rawUrl, this.redirectUrl);
110
+ if (url.pathname !== this.path) {
111
+ this.reply(response, 404, { ok: false, message: "Not found" });
112
+ return;
113
+ }
114
+ const state = url.searchParams.get("state");
115
+ const pending = state ? this.pending.get(state) : undefined;
116
+ if (!state || !pending) {
117
+ this.reply(response, 400, { ok: false, message: "Invalid or expired OAuth state" });
118
+ return;
119
+ }
120
+ clearTimeout(pending.timer);
121
+ this.pending.delete(state);
122
+ const error = url.searchParams.get("error");
123
+ if (error) {
124
+ const description = url.searchParams.get("error_description") ?? error;
125
+ pending.reject(new Error(description));
126
+ this.reply(response, 200, {
127
+ ok: false,
128
+ message: "Authorization failed. You may close this window.",
129
+ details: description
130
+ });
131
+ return;
132
+ }
133
+ const code = url.searchParams.get("code");
134
+ if (!code) {
135
+ pending.reject(new Error("OAuth callback did not include an authorization code"));
136
+ this.reply(response, 400, { ok: false, message: "Missing authorization code" });
137
+ return;
138
+ }
139
+ const iss = url.searchParams.get("iss");
140
+ pending.resolve({ code, state, ...iss ? { iss } : {} });
141
+ this.reply(response, 200, { ok: true });
142
+ }
143
+ }
144
+ // node_modules/@earendil-works/pi-mcp/dist/oauth/errors.js
145
+ class OAuthError extends Error {
146
+ code;
147
+ errorUri;
148
+ constructor(code, message, errorUri) {
149
+ super(message || code);
150
+ this.name = "OAuthError";
151
+ this.code = code;
152
+ this.errorUri = errorUri;
153
+ }
154
+ }
155
+
156
+ class OAuthIssuerMismatchError extends Error {
157
+ expected;
158
+ received;
159
+ constructor(expected, received) {
160
+ super(`OAuth issuer mismatch: expected ${JSON.stringify(expected)}, received ${JSON.stringify(received)}`);
161
+ this.name = "OAuthIssuerMismatchError";
162
+ this.expected = expected;
163
+ this.received = received;
164
+ }
165
+ }
166
+
167
+ class OAuthInsecureEndpointError extends Error {
168
+ endpoint;
169
+ constructor(endpoint) {
170
+ super(`Refusing to send OAuth credentials to non-HTTPS endpoint ${endpoint}`);
171
+ this.name = "OAuthInsecureEndpointError";
172
+ this.endpoint = endpoint;
173
+ }
174
+ }
175
+
176
+ class OAuthRegistrationError extends Error {
177
+ status;
178
+ body;
179
+ constructor(status, body) {
180
+ super(`OAuth dynamic client registration failed with status ${status}: ${body}`);
181
+ this.name = "OAuthRegistrationError";
182
+ this.status = status;
183
+ this.body = body;
184
+ }
185
+ }
186
+
187
+ class McpOAuthAuthorizationRequiredError extends Error {
188
+ constructor() {
189
+ super("MCP OAuth authorization requires user interaction");
190
+ this.name = "McpOAuthAuthorizationRequiredError";
191
+ }
192
+ }
193
+
194
+ // node_modules/@earendil-works/pi-mcp/dist/oauth/types.js
195
+ function object(value, name) {
196
+ if (!isObject(value))
197
+ throw new Error(`Invalid ${name}`);
198
+ return value;
199
+ }
200
+ function compact(value) {
201
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
202
+ }
203
+ function requiredString(value, name) {
204
+ if (typeof value !== "string" || value.length === 0)
205
+ throw new Error(`Invalid ${name}`);
206
+ return value;
207
+ }
208
+ function optionalString(value, name) {
209
+ if (value === undefined)
210
+ return;
211
+ return requiredString(value, name);
212
+ }
213
+ function optionalStrings(value, name) {
214
+ if (value === undefined)
215
+ return;
216
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string"))
217
+ throw new Error(`Invalid ${name}`);
218
+ return [...value];
219
+ }
220
+ function safeUrl(value, name) {
221
+ const text = requiredString(value, name);
222
+ const url = new URL(text);
223
+ if (["javascript:", "data:", "vbscript:"].includes(url.protocol))
224
+ throw new Error(`Invalid ${name}`);
225
+ return text;
226
+ }
227
+ function optionalUrl(value, name) {
228
+ return value === undefined ? undefined : safeUrl(value, name);
229
+ }
230
+ function parseProtectedResourceMetadata(value) {
231
+ const input = object(value, "OAuth protected resource metadata");
232
+ return compact({
233
+ ...input,
234
+ resource: safeUrl(input.resource, "OAuth protected resource metadata resource"),
235
+ authorization_servers: optionalStrings(input.authorization_servers, "authorization_servers")?.map((url) => safeUrl(url, "authorization server URL")),
236
+ scopes_supported: optionalStrings(input.scopes_supported, "scopes_supported")
237
+ });
238
+ }
239
+ function parseAuthorizationServerMetadata(value) {
240
+ const input = object(value, "authorization server metadata");
241
+ const responseTypes = optionalStrings(input.response_types_supported, "response_types_supported");
242
+ if (!responseTypes)
243
+ throw new Error("Invalid response_types_supported");
244
+ return compact({
245
+ ...input,
246
+ issuer: safeUrl(input.issuer, "authorization server issuer"),
247
+ authorization_endpoint: safeUrl(input.authorization_endpoint, "authorization endpoint"),
248
+ token_endpoint: safeUrl(input.token_endpoint, "token endpoint"),
249
+ registration_endpoint: optionalUrl(input.registration_endpoint, "registration endpoint"),
250
+ scopes_supported: optionalStrings(input.scopes_supported, "scopes_supported"),
251
+ response_types_supported: responseTypes,
252
+ grant_types_supported: optionalStrings(input.grant_types_supported, "grant_types_supported"),
253
+ token_endpoint_auth_methods_supported: optionalStrings(input.token_endpoint_auth_methods_supported, "token_endpoint_auth_methods_supported"),
254
+ code_challenge_methods_supported: optionalStrings(input.code_challenge_methods_supported, "code_challenge_methods_supported"),
255
+ client_id_metadata_document_supported: typeof input.client_id_metadata_document_supported === "boolean" ? input.client_id_metadata_document_supported : undefined
256
+ });
257
+ }
258
+ function parseOAuthTokens(value) {
259
+ const input = object(value, "OAuth token response");
260
+ const expires = input.expires_in === undefined ? undefined : Number(input.expires_in);
261
+ if (expires !== undefined && !Number.isFinite(expires))
262
+ throw new Error("Invalid expires_in");
263
+ return compact({
264
+ access_token: requiredString(input.access_token, "access_token"),
265
+ token_type: requiredString(input.token_type, "token_type"),
266
+ expires_in: expires,
267
+ scope: optionalString(input.scope, "scope"),
268
+ refresh_token: optionalString(input.refresh_token, "refresh_token"),
269
+ id_token: optionalString(input.id_token, "id_token")
270
+ });
271
+ }
272
+ function parseClientInformation(value) {
273
+ const input = object(value, "OAuth client registration response");
274
+ return compact({
275
+ ...input,
276
+ client_id: requiredString(input.client_id, "client_id"),
277
+ client_secret: optionalString(input.client_secret, "client_secret"),
278
+ client_id_issued_at: typeof input.client_id_issued_at === "number" ? input.client_id_issued_at : undefined,
279
+ client_secret_expires_at: typeof input.client_secret_expires_at === "number" ? input.client_secret_expires_at : undefined,
280
+ redirect_uris: optionalStrings(input.redirect_uris, "redirect_uris") ?? []
281
+ });
282
+ }
283
+
284
+ // node_modules/@earendil-works/pi-mcp/dist/oauth/discovery.js
285
+ function discard(response) {
286
+ response?.body?.cancel().catch(() => {});
287
+ }
288
+ function isDiscoveryMiss(status) {
289
+ return status >= 400 && status < 500 || status === 502;
290
+ }
291
+ function pathSuffix(pathname) {
292
+ return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
293
+ }
294
+ function field(header, name) {
295
+ const match = header.match(new RegExp(String.raw`(?:^|[,\s])${name}=(?:"([^"]*)"|([^\s,]+))`, "i"));
296
+ return match?.[1] ?? match?.[2];
297
+ }
298
+ function parseWwwAuthenticate(header) {
299
+ if (!header)
300
+ return {};
301
+ const scheme = header.trimStart().split(/\s+/, 1)[0]?.toLowerCase();
302
+ if (scheme !== "bearer" && scheme !== "dpop")
303
+ return {};
304
+ const resourceMetadata = field(header, "resource_metadata");
305
+ let resourceMetadataUrl;
306
+ if (resourceMetadata) {
307
+ try {
308
+ resourceMetadataUrl = new URL(resourceMetadata);
309
+ } catch {}
310
+ }
311
+ return {
312
+ resourceMetadataUrl,
313
+ scope: field(header, "scope"),
314
+ error: field(header, "error"),
315
+ errorDescription: field(header, "error_description")
316
+ };
317
+ }
318
+ async function fetchMetadata(url, fetch, protocolVersion) {
319
+ return fetch(url, {
320
+ headers: { Accept: "application/json", "MCP-Protocol-Version": protocolVersion }
321
+ });
322
+ }
323
+ async function discoverProtectedResourceMetadata(serverUrl, options = {}) {
324
+ const server = new URL(serverUrl);
325
+ const fetch = options.fetch ?? globalThis.fetch;
326
+ const version = options.protocolVersion ?? LATEST_PROTOCOL_VERSION;
327
+ let response = await fetchMetadata(options.resourceMetadataUrl ? new URL(options.resourceMetadataUrl) : new URL(`/.well-known/oauth-protected-resource${pathSuffix(server.pathname)}`, server.origin), fetch, version);
328
+ if (!options.resourceMetadataUrl && server.pathname !== "/" && isDiscoveryMiss(response.status)) {
329
+ discard(response);
330
+ response = await fetchMetadata(new URL("/.well-known/oauth-protected-resource", server.origin), fetch, version);
331
+ }
332
+ if (!response.ok) {
333
+ discard(response);
334
+ throw new Error(`HTTP ${response.status} loading OAuth protected resource metadata`);
335
+ }
336
+ return parseProtectedResourceMetadata(await response.json());
337
+ }
338
+ function buildAuthorizationServerDiscoveryUrls(authorizationServerUrl) {
339
+ const issuer = new URL(authorizationServerUrl);
340
+ const path = pathSuffix(issuer.pathname);
341
+ const urls = [
342
+ { url: new URL(`/.well-known/oauth-authorization-server${path}`, issuer.origin), type: "oauth" },
343
+ { url: new URL(`/.well-known/openid-configuration${path}`, issuer.origin), type: "oidc" }
344
+ ];
345
+ if (path)
346
+ urls.push({ url: new URL(`${path}/.well-known/openid-configuration`, issuer.origin), type: "oidc" });
347
+ return urls;
348
+ }
349
+ async function discoverAuthorizationServerMetadata(authorizationServerUrl, options = {}) {
350
+ const fetch = options.fetch ?? globalThis.fetch;
351
+ for (const { url } of buildAuthorizationServerDiscoveryUrls(authorizationServerUrl)) {
352
+ const response = await fetchMetadata(url, fetch, options.protocolVersion ?? LATEST_PROTOCOL_VERSION);
353
+ if (!response.ok) {
354
+ discard(response);
355
+ if (isDiscoveryMiss(response.status))
356
+ continue;
357
+ throw new Error(`HTTP ${response.status} loading authorization server metadata from ${url}`);
358
+ }
359
+ const metadata = parseAuthorizationServerMetadata(await response.json());
360
+ if (!options.skipIssuerValidation) {
361
+ const expected = String(authorizationServerUrl);
362
+ const trim = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
363
+ if (trim(metadata.issuer) !== trim(expected))
364
+ throw new OAuthIssuerMismatchError(expected, metadata.issuer);
365
+ }
366
+ return metadata;
367
+ }
368
+ return;
369
+ }
370
+ async function discoverOAuthServerInfo(serverUrl, options = {}) {
371
+ let resourceMetadata;
372
+ try {
373
+ resourceMetadata = await discoverProtectedResourceMetadata(serverUrl, {
374
+ resourceMetadataUrl: options.resourceMetadataUrl,
375
+ fetch: options.fetch
376
+ });
377
+ } catch (error) {
378
+ if (error instanceof TypeError)
379
+ throw error;
380
+ }
381
+ const authorizationServerUrl = resourceMetadata?.authorization_servers?.[0] ?? String(new URL("/", serverUrl));
382
+ return {
383
+ authorizationServerUrl,
384
+ authorizationServerMetadata: await discoverAuthorizationServerMetadata(authorizationServerUrl, {
385
+ fetch: options.fetch,
386
+ skipIssuerValidation: options.skipIssuerValidation
387
+ }),
388
+ resourceMetadata
389
+ };
390
+ }
391
+ function resourceUrlFromServerUrl(value) {
392
+ const url = new URL(value);
393
+ url.hash = "";
394
+ return url;
395
+ }
396
+ function selectResource(serverUrl, metadata) {
397
+ if (!metadata)
398
+ return;
399
+ const requested = resourceUrlFromServerUrl(serverUrl);
400
+ const configured = new URL(metadata.resource);
401
+ if (requested.origin !== configured.origin) {
402
+ throw new Error(`Protected resource ${metadata.resource} does not match MCP server ${requested}`);
403
+ }
404
+ const requestedPath = requested.pathname.endsWith("/") ? requested.pathname : `${requested.pathname}/`;
405
+ const configuredPath = configured.pathname.endsWith("/") ? configured.pathname : `${configured.pathname}/`;
406
+ if (!requestedPath.startsWith(configuredPath)) {
407
+ throw new Error(`Protected resource ${metadata.resource} does not match MCP server ${requested}`);
408
+ }
409
+ return metadata.resource;
410
+ }
411
+ // node_modules/@earendil-works/pi-mcp/dist/oauth/flow.js
412
+ function loopback(hostname) {
413
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname === "::1";
414
+ }
415
+ function secureEndpoint(value) {
416
+ const url = new URL(value);
417
+ if (url.protocol !== "https:" && !loopback(url.hostname))
418
+ throw new OAuthInsecureEndpointError(url.href);
419
+ return url;
420
+ }
421
+ function selectClientAuthMethod(information, supported) {
422
+ const hinted = "token_endpoint_auth_method" in information ? information.token_endpoint_auth_method : undefined;
423
+ if (hinted && ["client_secret_basic", "client_secret_post", "none"].includes(hinted) && (supported.length === 0 || supported.includes(hinted))) {
424
+ return hinted;
425
+ }
426
+ if (supported.length === 0)
427
+ return information.client_secret ? "client_secret_basic" : "none";
428
+ if (information.client_secret && supported.includes("client_secret_basic"))
429
+ return "client_secret_basic";
430
+ if (information.client_secret && supported.includes("client_secret_post"))
431
+ return "client_secret_post";
432
+ if (supported.includes("none"))
433
+ return "none";
434
+ return information.client_secret ? "client_secret_post" : "none";
435
+ }
436
+ function applyClientAuthentication(method, information, headers, params) {
437
+ if (method === "client_secret_basic") {
438
+ if (!information.client_secret)
439
+ throw new Error("client_secret_basic requires a client secret");
440
+ headers.set("Authorization", `Basic ${Buffer.from(`${information.client_id}:${information.client_secret}`).toString("base64")}`);
441
+ } else {
442
+ params.set("client_id", information.client_id);
443
+ if (method === "client_secret_post" && information.client_secret)
444
+ params.set("client_secret", information.client_secret);
445
+ }
446
+ }
447
+ async function pkce() {
448
+ const bytes = crypto.getRandomValues(new Uint8Array(32));
449
+ const verifier = Buffer.from(bytes).toString("base64url");
450
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
451
+ return { verifier, challenge: Buffer.from(digest).toString("base64url") };
452
+ }
453
+ async function startAuthorization(authorizationServerUrl, options) {
454
+ const metadata = options.metadata;
455
+ if (metadata && !metadata.response_types_supported.includes("code")) {
456
+ throw new Error("Authorization server does not support authorization codes");
457
+ }
458
+ if (metadata?.code_challenge_methods_supported && !metadata.code_challenge_methods_supported.includes("S256")) {
459
+ throw new Error("Authorization server does not support PKCE S256");
460
+ }
461
+ const url = new URL(metadata?.authorization_endpoint ?? new URL("/authorize", authorizationServerUrl));
462
+ const { verifier, challenge } = await pkce();
463
+ url.searchParams.set("response_type", "code");
464
+ url.searchParams.set("client_id", options.clientInformation.client_id);
465
+ url.searchParams.set("code_challenge", challenge);
466
+ url.searchParams.set("code_challenge_method", "S256");
467
+ url.searchParams.set("redirect_uri", String(options.redirectUrl));
468
+ if (options.state)
469
+ url.searchParams.set("state", options.state);
470
+ if (options.scope)
471
+ url.searchParams.set("scope", options.scope);
472
+ if (options.scope?.split(/\s+/).includes("offline_access"))
473
+ url.searchParams.set("prompt", "consent");
474
+ if (options.resource)
475
+ url.searchParams.set("resource", options.resource);
476
+ return { authorizationUrl: url, codeVerifier: verifier };
477
+ }
478
+ async function tokenRequest(authorizationServerUrl, options, params) {
479
+ const url = secureEndpoint(options.metadata?.token_endpoint ?? new URL("/token", authorizationServerUrl));
480
+ const headers = new Headers({ Accept: "application/json", "content-type": "application/x-www-form-urlencoded" });
481
+ if (options.resource)
482
+ params.set("resource", options.resource);
483
+ if (options.addClientAuthentication) {
484
+ await options.addClientAuthentication(headers, params, url, options.metadata);
485
+ } else {
486
+ applyClientAuthentication(selectClientAuthMethod(options.clientInformation, options.metadata?.token_endpoint_auth_methods_supported ?? []), options.clientInformation, headers, params);
487
+ }
488
+ const response = await (options.fetch ?? globalThis.fetch)(url, { method: "POST", headers, body: params });
489
+ const text = await response.text();
490
+ let value;
491
+ try {
492
+ value = JSON.parse(text);
493
+ } catch {}
494
+ if (isObject(value) && typeof value.error === "string") {
495
+ throw new OAuthError(value.error, typeof value.error_description === "string" ? value.error_description : value.error, typeof value.error_uri === "string" ? value.error_uri : undefined);
496
+ }
497
+ if (!response.ok)
498
+ throw new OAuthError("server_error", `HTTP ${response.status}: ${text}`);
499
+ return parseOAuthTokens(value);
500
+ }
501
+ async function registerClient(authorizationServerUrl, options) {
502
+ const endpoint = options.metadata?.registration_endpoint;
503
+ if (options.metadata && !endpoint)
504
+ throw new Error("Authorization server does not support dynamic client registration");
505
+ const response = await (options.fetch ?? globalThis.fetch)(new URL(endpoint ?? new URL("/register", authorizationServerUrl)), {
506
+ method: "POST",
507
+ headers: { Accept: "application/json", "content-type": "application/json" },
508
+ body: JSON.stringify({ ...options.clientMetadata, ...options.scope ? { scope: options.scope } : {} })
509
+ });
510
+ if (!response.ok)
511
+ throw new OAuthRegistrationError(response.status, await response.text());
512
+ return parseClientInformation(await response.json());
513
+ }
514
+ async function exchangeAuthorizationCode(authorizationServerUrl, options) {
515
+ return tokenRequest(authorizationServerUrl, options, new URLSearchParams({
516
+ grant_type: "authorization_code",
517
+ code: options.code,
518
+ code_verifier: options.codeVerifier,
519
+ redirect_uri: String(options.redirectUrl)
520
+ }));
521
+ }
522
+ async function refreshAuthorization(authorizationServerUrl, options) {
523
+ const tokens = await tokenRequest(authorizationServerUrl, options, new URLSearchParams({ grant_type: "refresh_token", refresh_token: options.refreshToken }));
524
+ return { refresh_token: options.refreshToken, ...tokens };
525
+ }
526
+ async function runFlow(provider, options) {
527
+ const cached = await provider.discoveryState?.();
528
+ const discovered = cached?.authorizationServerUrl ? {
529
+ authorizationServerUrl: cached.authorizationServerUrl,
530
+ authorizationServerMetadata: cached.authorizationServerMetadata ?? await discoverAuthorizationServerMetadata(cached.authorizationServerUrl, {
531
+ fetch: options.fetch,
532
+ skipIssuerValidation: options.skipIssuerValidation
533
+ }),
534
+ resourceMetadata: cached.resourceMetadata
535
+ } : await discoverOAuthServerInfo(options.serverUrl, {
536
+ resourceMetadataUrl: options.resourceMetadataUrl,
537
+ fetch: options.fetch,
538
+ skipIssuerValidation: options.skipIssuerValidation
539
+ });
540
+ await provider.saveDiscoveryState?.({
541
+ ...discovered,
542
+ ...options.resourceMetadataUrl ? { resourceMetadataUrl: options.resourceMetadataUrl.href } : {}
543
+ });
544
+ const metadata = discovered.authorizationServerMetadata;
545
+ const resource = selectResource(options.serverUrl, discovered.resourceMetadata);
546
+ const scope = options.scope ?? discovered.resourceMetadata?.scopes_supported?.join(" ") ?? provider.clientMetadata.scope;
547
+ let client = await provider.clientInformation();
548
+ if (!client) {
549
+ if (options.authorizationCode)
550
+ throw new Error("OAuth client information is missing during code exchange");
551
+ if (metadata?.client_id_metadata_document_supported && provider.clientMetadataUrl) {
552
+ const url = new URL(provider.clientMetadataUrl);
553
+ if (url.protocol !== "https:" || url.pathname === "/")
554
+ throw new Error("Invalid OAuth client metadata URL");
555
+ client = { client_id: provider.clientMetadataUrl };
556
+ await provider.saveClientInformation?.(client);
557
+ } else {
558
+ if (!provider.saveClientInformation)
559
+ throw new Error("OAuth client information cannot be persisted");
560
+ client = await registerClient(discovered.authorizationServerUrl, {
561
+ metadata,
562
+ clientMetadata: provider.clientMetadata,
563
+ scope,
564
+ fetch: options.fetch
565
+ });
566
+ await provider.saveClientInformation(client);
567
+ }
568
+ }
569
+ const tokenOptions = {
570
+ metadata,
571
+ clientInformation: client,
572
+ resource,
573
+ addClientAuthentication: provider.addClientAuthentication,
574
+ fetch: options.fetch
575
+ };
576
+ if (options.authorizationCode) {
577
+ const tokens = await exchangeAuthorizationCode(discovered.authorizationServerUrl, {
578
+ ...tokenOptions,
579
+ code: options.authorizationCode,
580
+ codeVerifier: await provider.codeVerifier(),
581
+ redirectUrl: provider.redirectUrl
582
+ });
583
+ await provider.saveTokens(tokens);
584
+ return "AUTHORIZED";
585
+ }
586
+ const existing = options.skipRefresh ? undefined : await provider.tokens();
587
+ if (existing?.refresh_token) {
588
+ try {
589
+ const tokens = await refreshAuthorization(discovered.authorizationServerUrl, {
590
+ ...tokenOptions,
591
+ refreshToken: existing.refresh_token
592
+ });
593
+ await provider.saveTokens(tokens);
594
+ return "AUTHORIZED";
595
+ } catch (error) {
596
+ if (error instanceof OAuthInsecureEndpointError)
597
+ throw error;
598
+ if (error instanceof OAuthError && error.code !== "server_error")
599
+ throw error;
600
+ }
601
+ }
602
+ const state = await provider.state?.();
603
+ const authorization = await startAuthorization(discovered.authorizationServerUrl, {
604
+ metadata,
605
+ clientInformation: client,
606
+ redirectUrl: provider.redirectUrl,
607
+ scope,
608
+ state,
609
+ resource
610
+ });
611
+ await provider.saveCodeVerifier(authorization.codeVerifier);
612
+ await provider.redirectToAuthorization(authorization.authorizationUrl);
613
+ return "REDIRECT";
614
+ }
615
+ async function authorizeMcp(provider, options) {
616
+ try {
617
+ return await runFlow(provider, options);
618
+ } catch (error) {
619
+ if (error instanceof OAuthError && ["invalid_client", "unauthorized_client"].includes(error.code)) {
620
+ await provider.invalidateCredentials?.("all");
621
+ return runFlow(provider, options);
622
+ }
623
+ if (error instanceof OAuthError && error.code === "invalid_grant") {
624
+ await provider.invalidateCredentials?.("tokens");
625
+ return runFlow(provider, options);
626
+ }
627
+ throw error;
628
+ }
629
+ }
630
+ // node_modules/@earendil-works/pi-mcp/dist/oauth/provider.js
631
+ class MemoryOAuthStateStore {
632
+ value;
633
+ load() {
634
+ return this.value === undefined ? undefined : structuredClone(this.value);
635
+ }
636
+ save(state) {
637
+ this.value = structuredClone(state);
638
+ }
639
+ }
640
+
641
+ class McpOAuthProvider {
642
+ redirectUrl;
643
+ clientMetadata;
644
+ serverUrl;
645
+ configuredClient;
646
+ store;
647
+ onRedirect;
648
+ writes = Promise.resolve();
649
+ constructor(options) {
650
+ this.serverUrl = String(new URL(options.serverUrl));
651
+ this.redirectUrl = String(options.redirectUrl);
652
+ this.clientMetadata = {
653
+ ...options.clientMetadata,
654
+ redirect_uris: options.clientMetadata.redirect_uris ?? [this.redirectUrl],
655
+ grant_types: options.clientMetadata.grant_types ?? ["authorization_code", "refresh_token"],
656
+ response_types: options.clientMetadata.response_types ?? ["code"],
657
+ token_endpoint_auth_method: options.clientMetadata.token_endpoint_auth_method ?? (options.clientSecret ? "client_secret_post" : "none")
658
+ };
659
+ this.configuredClient = options.clientId ? { client_id: options.clientId, ...options.clientSecret ? { client_secret: options.clientSecret } : {} } : undefined;
660
+ this.store = options.store ?? new MemoryOAuthStateStore;
661
+ this.onRedirect = options.onRedirect;
662
+ }
663
+ async state() {
664
+ const existing = (await this.load()).oauthState;
665
+ if (existing)
666
+ return existing;
667
+ const state = Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("hex");
668
+ await this.update((value) => ({ ...value, oauthState: state }));
669
+ return state;
670
+ }
671
+ async clientInformation() {
672
+ return this.configuredClient ?? (await this.load()).clientInformation;
673
+ }
674
+ async saveClientInformation(information) {
675
+ if (this.configuredClient)
676
+ return;
677
+ await this.update((value) => ({ ...value, clientInformation: information }));
678
+ }
679
+ async tokens() {
680
+ return (await this.load()).tokens;
681
+ }
682
+ async saveTokens(tokens) {
683
+ const expiresAt = tokens.expires_in === undefined ? undefined : Date.now() + tokens.expires_in * 1000;
684
+ await this.update((value) => {
685
+ const next = { ...value, tokens };
686
+ if (expiresAt === undefined)
687
+ delete next.tokensExpireAt;
688
+ else
689
+ next.tokensExpireAt = expiresAt;
690
+ return next;
691
+ });
692
+ }
693
+ async redirectToAuthorization(url) {
694
+ await this.onRedirect(url);
695
+ }
696
+ async saveCodeVerifier(verifier) {
697
+ await this.update((value) => ({ ...value, codeVerifier: verifier }));
698
+ }
699
+ async codeVerifier() {
700
+ const verifier = (await this.load()).codeVerifier;
701
+ if (!verifier)
702
+ throw new Error("No OAuth PKCE code verifier is stored");
703
+ return verifier;
704
+ }
705
+ async invalidateCredentials(kind) {
706
+ await this.update((value) => {
707
+ const next = { ...value };
708
+ if (kind === "all" || kind === "client")
709
+ delete next.clientInformation;
710
+ if (kind === "all" || kind === "tokens") {
711
+ delete next.tokens;
712
+ delete next.tokensExpireAt;
713
+ }
714
+ if (kind === "all" || kind === "verifier")
715
+ delete next.codeVerifier;
716
+ if (kind === "all" || kind === "discovery")
717
+ delete next.discovery;
718
+ if (kind === "all")
719
+ delete next.oauthState;
720
+ return next;
721
+ });
722
+ }
723
+ async saveDiscoveryState(discovery) {
724
+ await this.update((value) => ({ ...value, discovery }));
725
+ }
726
+ async discoveryState() {
727
+ return (await this.load()).discovery;
728
+ }
729
+ async load() {
730
+ await this.writes;
731
+ return this.own(await this.store.load());
732
+ }
733
+ async update(update) {
734
+ this.writes = this.writes.then(async () => {
735
+ await this.store.save(update(this.own(await this.store.load())));
736
+ });
737
+ await this.writes;
738
+ }
739
+ own(state) {
740
+ return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };
741
+ }
742
+ }
743
+ // node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/oauth.js
744
+ import { createHash } from "node:crypto";
745
+ import { mkdirSync } from "node:fs";
746
+ import { join } from "node:path";
747
+ var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
748
+ var CALLBACK_HOST = "127.0.0.1";
749
+ var CALLBACK_PATH = "/callback";
750
+ var FALLBACK_REDIRECT_URL = `http://${CALLBACK_HOST}${CALLBACK_PATH}`;
751
+ var REFRESH_SKEW_MS = 30000;
752
+ var REFRESH_REQUEST_TIMEOUT_MS = 15000;
753
+ var REFRESH_LOCK_STALE_MS = 20000;
754
+ var REFRESH_LOCK_WAIT_MS = 25000;
755
+ var REFRESH_LOCK_RETRY_MS = 100;
756
+ function callbackSettings(settings) {
757
+ const url = new URL(settings.callbackUrl ?? `http://${CALLBACK_HOST}${CALLBACK_PATH}`);
758
+ const address = url.hostname.replace(/^\[|\]$/g, "");
759
+ const port = url.port ? Number(url.port) : settings.callbackPort;
760
+ let fixedRedirectUrl;
761
+ if (url.port)
762
+ fixedRedirectUrl = settings.callbackUrl;
763
+ else if (port !== undefined) {
764
+ url.port = String(port);
765
+ fixedRedirectUrl = url.href;
766
+ }
767
+ return {
768
+ host: address === "localhost" ? CALLBACK_HOST : address,
769
+ redirectHost: address,
770
+ port,
771
+ path: url.pathname,
772
+ fixedRedirectUrl
773
+ };
774
+ }
775
+ function mergeScopes(...scopes) {
776
+ const merged = [...new Set(scopes.flatMap((scope) => scope?.split(/\s+/).filter(Boolean) ?? []))];
777
+ return merged.length > 0 ? merged.join(" ") : undefined;
778
+ }
779
+ function parseStates(content) {
780
+ if (!content?.trim())
781
+ return {};
782
+ const parsed = JSON.parse(content);
783
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
784
+ }
785
+
786
+ class McpOAuthCredentialStore {
787
+ backend;
788
+ lockDir;
789
+ constructor(backend, lockDir) {
790
+ this.backend = backend ?? new FileAuthStorageBackend(join(getAgentDir(), "mcp-auth.json"));
791
+ this.lockDir = backend ? lockDir : getAgentDir();
792
+ }
793
+ forServer(serverUrl) {
794
+ const key = String(new URL(serverUrl));
795
+ return {
796
+ load: () => this.read()[key],
797
+ save: (state) => this.write((states) => {
798
+ states[key] = state;
799
+ }),
800
+ withRefreshLock: (fn) => this.withRefreshLock(key, fn)
801
+ };
802
+ }
803
+ async withRefreshLock(key, fn) {
804
+ if (!this.lockDir)
805
+ return fn();
806
+ mkdirSync(this.lockDir, { recursive: true, mode: 448 });
807
+ const hash = createHash("sha256").update(key).digest("hex").slice(0, 16);
808
+ const release = await import_proper_lockfile.default.lock(join(this.lockDir, `mcp-auth-refresh-${hash}`), {
809
+ realpath: false,
810
+ stale: REFRESH_LOCK_STALE_MS,
811
+ retries: {
812
+ retries: REFRESH_LOCK_WAIT_MS / REFRESH_LOCK_RETRY_MS,
813
+ factor: 1,
814
+ minTimeout: REFRESH_LOCK_RETRY_MS,
815
+ maxTimeout: REFRESH_LOCK_RETRY_MS
816
+ },
817
+ onCompromised: () => {}
818
+ });
819
+ try {
820
+ return await fn();
821
+ } finally {
822
+ await release().catch(() => {
823
+ return;
824
+ });
825
+ }
826
+ }
827
+ tokens(serverUrl) {
828
+ return this.read()[String(new URL(serverUrl))]?.tokens;
829
+ }
830
+ remove(serverUrl) {
831
+ const key = String(new URL(serverUrl));
832
+ if (!(key in this.read()))
833
+ return false;
834
+ this.write((states) => {
835
+ delete states[key];
836
+ });
837
+ return true;
838
+ }
839
+ read() {
840
+ return this.backend.withLock((current) => ({ result: parseStates(current) }));
841
+ }
842
+ write(update) {
843
+ this.backend.withLock((current) => {
844
+ const states = parseStates(current);
845
+ update(states);
846
+ return { result: undefined, next: `${JSON.stringify(states, null, 2)}
847
+ ` };
848
+ });
849
+ }
850
+ }
851
+ function registeredRedirectUrls(client) {
852
+ return client && "redirect_uris" in client ? client.redirect_uris : [];
853
+ }
854
+ function createProvider(serverUrl, store, settings, redirectUrl, onRedirect) {
855
+ return new McpOAuthProvider({
856
+ serverUrl,
857
+ redirectUrl,
858
+ clientMetadata: { client_name: APP_NAME },
859
+ clientId: settings.clientId,
860
+ clientSecret: settings.clientSecret,
861
+ store,
862
+ onRedirect
863
+ });
864
+ }
865
+ function createMcpAuthProvider(options) {
866
+ const { serverUrl, store } = options;
867
+ let refreshing;
868
+ const refresh = (staleToken, fetch = globalThis.fetch, challenge) => {
869
+ refreshing ??= store.withRefreshLock(async () => {
870
+ const state = await store.load();
871
+ if (state?.tokens?.access_token !== staleToken)
872
+ return;
873
+ if (!state?.tokens?.refresh_token)
874
+ throw new McpOAuthAuthorizationRequiredError;
875
+ const settings = options.settings();
876
+ const redirectUrl = callbackSettings(settings).fixedRedirectUrl ?? registeredRedirectUrls(state.clientInformation)[0] ?? FALLBACK_REDIRECT_URL;
877
+ const provider = createProvider(serverUrl, store, settings, redirectUrl, () => {});
878
+ const result = await authorizeMcp(provider, {
879
+ serverUrl,
880
+ resourceMetadataUrl: challenge?.resourceMetadataUrl,
881
+ scope: challenge?.scope,
882
+ fetch: (input, init) => fetch(input, { ...init, signal: AbortSignal.timeout(REFRESH_REQUEST_TIMEOUT_MS) })
883
+ });
884
+ if (result === "REDIRECT")
885
+ throw new McpOAuthAuthorizationRequiredError;
886
+ }).finally(() => {
887
+ refreshing = undefined;
888
+ });
889
+ return refreshing;
890
+ };
891
+ return {
892
+ token: async () => {
893
+ await refreshing?.catch(() => {
894
+ return;
895
+ });
896
+ const state = await store.load();
897
+ const token = state?.tokens?.access_token;
898
+ const expired = state?.tokensExpireAt !== undefined && state.tokensExpireAt - REFRESH_SKEW_MS <= Date.now();
899
+ if (!expired || !state?.tokens?.refresh_token)
900
+ return token;
901
+ await refresh(token).catch(() => {
902
+ return;
903
+ });
904
+ return (await store.load())?.tokens?.access_token;
905
+ },
906
+ onUnauthorized: async (context) => {
907
+ const challenge = parseWwwAuthenticate(context.response.headers.get("www-authenticate"));
908
+ options.onChallenge(challenge);
909
+ if (challenge.error === "insufficient_scope")
910
+ throw new McpOAuthAuthorizationRequiredError;
911
+ await refresh(context.token, context.fetch, challenge);
912
+ },
913
+ settled: async () => {
914
+ await refreshing?.catch(() => {
915
+ return;
916
+ });
917
+ }
918
+ };
919
+ }
920
+
921
+ class McpSignInCancelledError extends Error {
922
+ constructor() {
923
+ super("Sign-in cancelled");
924
+ this.name = "McpSignInCancelledError";
925
+ }
926
+ }
927
+ function codeFromRedirectUrl(input, state) {
928
+ let url;
929
+ try {
930
+ url = new URL(input.trim());
931
+ } catch {
932
+ throw new Error("Expected the full redirect URL from the browser address bar");
933
+ }
934
+ const error = url.searchParams.get("error");
935
+ if (error)
936
+ throw new Error(url.searchParams.get("error_description") ?? error);
937
+ if (url.searchParams.get("state") !== state)
938
+ throw new Error("The redirect URL belongs to a different sign-in");
939
+ const code = url.searchParams.get("code");
940
+ if (!code)
941
+ throw new Error("The redirect URL does not contain an authorization code");
942
+ return code;
943
+ }
944
+ async function waitForAuthorizationCode(callback, state, prompt) {
945
+ const controller = new AbortController;
946
+ const fromBrowser = callback.waitForCallback(state).then((result) => result.code);
947
+ const fromUser = prompt.promptForRedirectUrl(controller.signal).then((input) => {
948
+ if (!input?.trim())
949
+ throw new McpSignInCancelledError;
950
+ return codeFromRedirectUrl(input, state);
951
+ });
952
+ try {
953
+ return await Promise.race([fromBrowser, fromUser]);
954
+ } finally {
955
+ controller.abort();
956
+ fromBrowser.catch(() => {
957
+ return;
958
+ });
959
+ fromUser.catch(() => {
960
+ return;
961
+ });
962
+ }
963
+ }
964
+ async function listenForCallback(settings, port, required) {
965
+ const options = {
966
+ host: settings.host,
967
+ redirectHost: settings.redirectHost,
968
+ path: settings.path,
969
+ renderPage: (page) => page.ok ? oauthSuccessHtml("Signed in to the MCP server. You may now close this page.") : oauthErrorHtml(page.message, page.details)
970
+ };
971
+ try {
972
+ return await OAuthCallbackServer.listen({ ...options, port: port ?? 0 });
973
+ } catch (error) {
974
+ if (required || port === undefined)
975
+ throw error;
976
+ return OAuthCallbackServer.listen(options);
977
+ }
978
+ }
979
+ async function signInMcpServer(options) {
980
+ const { serverUrl, store, settings } = options;
981
+ const stored = await store.load();
982
+ const callbackOptions = callbackSettings(settings);
983
+ const registered = registeredRedirectUrls(stored?.clientInformation)[0];
984
+ const preferredPort = callbackOptions.port ?? (registered ? Number(new URL(registered).port) || undefined : undefined);
985
+ const callback = await listenForCallback(callbackOptions, preferredPort, callbackOptions.port !== undefined);
986
+ const redirectUrl = callbackOptions.fixedRedirectUrl ?? callback.redirectUrl;
987
+ try {
988
+ if (stored) {
989
+ const next = { ...stored };
990
+ delete next.oauthState;
991
+ if (!settings.clientId && !registeredRedirectUrls(stored.clientInformation).includes(redirectUrl)) {
992
+ delete next.clientInformation;
993
+ delete next.tokens;
994
+ delete next.tokensExpireAt;
995
+ }
996
+ await store.save(next);
997
+ }
998
+ let authorizationUrl;
999
+ const provider = createProvider(serverUrl, store, settings, redirectUrl, (url) => {
1000
+ authorizationUrl = url;
1001
+ });
1002
+ const flow = {
1003
+ serverUrl,
1004
+ resourceMetadataUrl: options.challenge?.resourceMetadataUrl,
1005
+ scope: mergeScopes(settings.scope, options.challenge?.scope)
1006
+ };
1007
+ const skipRefresh = options.challenge?.error === "insufficient_scope";
1008
+ if (await authorizeMcp(provider, { ...flow, skipRefresh }) === "AUTHORIZED")
1009
+ return;
1010
+ if (!authorizationUrl)
1011
+ throw new Error("OAuth flow did not produce an authorization URL");
1012
+ const state = await provider.state();
1013
+ options.prompt.showAuthorizationUrl(authorizationUrl);
1014
+ const code = await waitForAuthorizationCode(callback, state, options.prompt);
1015
+ await authorizeMcp(provider, { ...flow, authorizationCode: code });
1016
+ } finally {
1017
+ await callback.close();
1018
+ }
1019
+ }
1020
+
1021
+ // node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/log.js
1022
+ import { appendFileSync, mkdirSync as mkdirSync2, renameSync, statSync } from "node:fs";
1023
+ import { dirname } from "node:path";
1024
+ var MAX_LOG_BYTES = 5 * 1024 * 1024;
1025
+ function isRecord(value) {
1026
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1027
+ }
1028
+ function formatData(data) {
1029
+ if (typeof data === "string")
1030
+ return data;
1031
+ try {
1032
+ return JSON.stringify(data) ?? String(data);
1033
+ } catch {
1034
+ return String(data);
1035
+ }
1036
+ }
1037
+ function formatMcpLogMessage(server, params, now = new Date) {
1038
+ const message = isRecord(params) ? params : { data: params };
1039
+ const level = typeof message.level === "string" ? message.level : "info";
1040
+ const logger = typeof message.logger === "string" && message.logger ? ` ${message.logger}:` : "";
1041
+ const text = formatData(message.data).replace(/\r?\n/g, `
1042
+ `);
1043
+ return `${now.toISOString()} [${server}] ${level}${logger} ${text}
1044
+ `;
1045
+ }
1046
+
1047
+ class McpServerLog {
1048
+ path;
1049
+ size;
1050
+ constructor(path) {
1051
+ this.path = path;
1052
+ }
1053
+ write(server, params) {
1054
+ const line = formatMcpLogMessage(server, params);
1055
+ try {
1056
+ if (this.size === undefined) {
1057
+ mkdirSync2(dirname(this.path), { recursive: true });
1058
+ this.size = this.currentSize();
1059
+ }
1060
+ if (this.size > MAX_LOG_BYTES) {
1061
+ if (this.currentSize() > MAX_LOG_BYTES)
1062
+ renameSync(this.path, `${this.path}.1`);
1063
+ this.size = this.currentSize();
1064
+ }
1065
+ appendFileSync(this.path, line);
1066
+ this.size += Buffer.byteLength(line);
1067
+ } catch {}
1068
+ }
1069
+ currentSize() {
1070
+ try {
1071
+ return statSync(this.path).size;
1072
+ } catch {
1073
+ return 0;
1074
+ }
1075
+ }
1076
+ }
1077
+
1078
+ // node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/runtime.js
1079
+ var DEFAULT_TIMEOUT_SECONDS = 60;
1080
+ var STDERR_TAIL_CHARS = 2000;
1081
+ var CONNECT_RETRY_DELAYS_MS = [250, 1000];
1082
+ function errorMessage(error) {
1083
+ return error instanceof Error ? error.message : String(error);
1084
+ }
1085
+ function isTransientError(error) {
1086
+ if (error instanceof McpHttpError) {
1087
+ return error.status === 408 || error.status === 429 || error.status >= 500 && error.status !== 501;
1088
+ }
1089
+ return error instanceof TypeError;
1090
+ }
1091
+ function signInRequiredMessage(name) {
1092
+ return `MCP server "${name}" requires sign-in. Run /mcp to sign in.`;
1093
+ }
1094
+ function usesOAuth(entry) {
1095
+ const { config } = entry;
1096
+ if (!("url" in config))
1097
+ return false;
1098
+ return !Object.keys(config.headers ?? {}).some((header) => header.toLowerCase() === "authorization");
1099
+ }
1100
+ function expandHome(value) {
1101
+ if (value === "~")
1102
+ return homedir();
1103
+ if (value.startsWith("~/") || process.platform === "win32" && value.startsWith("~\\")) {
1104
+ return join2(homedir(), value.slice(2));
1105
+ }
1106
+ return value;
1107
+ }
1108
+ function createDefaultTransport(entry, cwd, authProvider) {
1109
+ const { config, name } = entry;
1110
+ if ("url" in config) {
1111
+ return new StreamableHttpTransport({
1112
+ url: config.url,
1113
+ headers: resolveHeadersOrThrow(config.headers, `MCP server "${name}"`),
1114
+ authProvider
1115
+ });
1116
+ }
1117
+ const env = {};
1118
+ for (const [key, value] of Object.entries(config.env ?? {})) {
1119
+ env[key] = resolveConfigValueOrThrow(value, `MCP server "${name}" env "${key}"`);
1120
+ }
1121
+ return new StdioTransport({
1122
+ command: expandHome(config.command),
1123
+ args: config.args?.map(expandHome),
1124
+ cwd: resolve(cwd, expandHome(config.cwd ?? ".")),
1125
+ env,
1126
+ stderr: "pipe"
1127
+ });
1128
+ }
1129
+ async function withoutTemplates(list, empty) {
1130
+ try {
1131
+ return await list();
1132
+ } catch (error) {
1133
+ if (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound)
1134
+ return empty;
1135
+ throw error;
1136
+ }
1137
+ }
1138
+ function listTemplates(client, options = {}) {
1139
+ return withoutTemplates(() => client.listResourceTemplates(options), []);
1140
+ }
1141
+ async function fetchResources(client) {
1142
+ const [resources, resourceTemplates] = await Promise.all([
1143
+ client.listResources().catch(() => []),
1144
+ listTemplates(client).catch(() => [])
1145
+ ]);
1146
+ return {
1147
+ resources: resources.filter((resource) => !isMcpAppResource(resource)),
1148
+ resourceTemplates: resourceTemplates.filter((template) => !isMcpAppResource(template))
1149
+ };
1150
+ }
1151
+
1152
+ class McpServerConnection {
1153
+ entry;
1154
+ state = "connecting";
1155
+ error;
1156
+ tools = [];
1157
+ hasResources = false;
1158
+ resources = [];
1159
+ resourceTemplates = [];
1160
+ instructions;
1161
+ challenge;
1162
+ client;
1163
+ opening;
1164
+ closed = false;
1165
+ stderrTail;
1166
+ cwd;
1167
+ createTransport;
1168
+ authProvider;
1169
+ onTools;
1170
+ onChange;
1171
+ log;
1172
+ constructor(options) {
1173
+ this.entry = options.entry;
1174
+ this.cwd = options.cwd;
1175
+ this.createTransport = options.createTransport;
1176
+ this.onTools = options.onTools;
1177
+ this.onChange = options.onChange;
1178
+ this.log = options.log;
1179
+ const url = this.oauthUrl;
1180
+ this.authProvider = url ? createMcpAuthProvider({
1181
+ serverUrl: url,
1182
+ store: options.credentials.forServer(url),
1183
+ settings: () => this.oauthSettings(),
1184
+ onChallenge: (challenge) => {
1185
+ this.challenge = challenge;
1186
+ }
1187
+ }) : undefined;
1188
+ }
1189
+ get name() {
1190
+ return this.entry.name;
1191
+ }
1192
+ get timeoutMs() {
1193
+ return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;
1194
+ }
1195
+ get oauthUrl() {
1196
+ return usesOAuth(this.entry) && "url" in this.entry.config ? this.entry.config.url : undefined;
1197
+ }
1198
+ oauthSettings() {
1199
+ const oauth = "url" in this.entry.config ? this.entry.config.oauth : undefined;
1200
+ if (!oauth)
1201
+ return {};
1202
+ return {
1203
+ clientId: oauth.clientId,
1204
+ clientSecret: oauth.clientSecret === undefined ? undefined : resolveConfigValueOrThrow(oauth.clientSecret, `MCP server "${this.entry.name}" oauth.clientSecret`),
1205
+ callbackPort: oauth.callbackPort,
1206
+ callbackUrl: oauth.callbackUrl,
1207
+ scope: oauth.scope
1208
+ };
1209
+ }
1210
+ getClient() {
1211
+ if (this.closed)
1212
+ return Promise.reject(new Error(`MCP server "${this.entry.name}" is shut down`));
1213
+ if (this.client?.connectionState === "connected")
1214
+ return Promise.resolve(this.client);
1215
+ this.opening ??= this.open().finally(() => {
1216
+ this.opening = undefined;
1217
+ });
1218
+ return this.opening;
1219
+ }
1220
+ callTool(name, args, options) {
1221
+ return this.withClient((client) => client.callTool(name, args, options));
1222
+ }
1223
+ readResource(uri, options) {
1224
+ return this.withClient((client) => client.readResource(uri, options), true);
1225
+ }
1226
+ resourcesPage(cursor, options) {
1227
+ return this.withClient((client) => client.listResourcesPage(cursor, options), true);
1228
+ }
1229
+ resourceTemplatesPage(cursor, options) {
1230
+ return this.withClient((client) => withoutTemplates(() => client.listResourceTemplatesPage(cursor, options), { resourceTemplates: [] }), true);
1231
+ }
1232
+ allResources(options) {
1233
+ return this.withClient((client) => client.listResources(options), true);
1234
+ }
1235
+ allResourceTemplates(options) {
1236
+ return this.withClient((client) => listTemplates(client, options), true);
1237
+ }
1238
+ async withClient(run, readOnly = false) {
1239
+ for (let attempt = 1;; attempt++) {
1240
+ const client = await this.getClient();
1241
+ try {
1242
+ return await run(client);
1243
+ } catch (error) {
1244
+ if (readOnly && attempt === 1 && error instanceof McpHttpError && isTransientError(error)) {
1245
+ await new Promise((resolve2) => setTimeout(resolve2, CONNECT_RETRY_DELAYS_MS[0]));
1246
+ continue;
1247
+ }
1248
+ if (error instanceof McpSessionExpiredError && attempt === 1) {
1249
+ if (this.client === client)
1250
+ this.client = undefined;
1251
+ continue;
1252
+ }
1253
+ if (!this.needsSignIn(error))
1254
+ throw error;
1255
+ await this.dropClient(client);
1256
+ this.markNeedsAuth();
1257
+ throw new Error(signInRequiredMessage(this.entry.name));
1258
+ }
1259
+ }
1260
+ }
1261
+ async reconnect() {
1262
+ await this.opening?.catch(() => {
1263
+ return;
1264
+ });
1265
+ if (this.client)
1266
+ await this.dropClient(this.client);
1267
+ await this.getClient();
1268
+ }
1269
+ async signOut() {
1270
+ await this.opening?.catch(() => {
1271
+ return;
1272
+ });
1273
+ if (this.client)
1274
+ await this.dropClient(this.client);
1275
+ if (!this.closed)
1276
+ this.markNeedsAuth();
1277
+ }
1278
+ needsSignIn(error) {
1279
+ return error instanceof McpOAuthAuthorizationRequiredError || this.oauthUrl !== undefined && error instanceof McpAuthRequiredError;
1280
+ }
1281
+ markNeedsAuth() {
1282
+ this.state = "needs-auth";
1283
+ this.error = undefined;
1284
+ this.changed();
1285
+ }
1286
+ changed() {
1287
+ this.onChange?.(this);
1288
+ }
1289
+ async dropClient(client) {
1290
+ if (this.client === client)
1291
+ this.client = undefined;
1292
+ await client.close().catch(() => {
1293
+ return;
1294
+ });
1295
+ }
1296
+ async open() {
1297
+ this.state = "connecting";
1298
+ this.changed();
1299
+ const retries = "url" in this.entry.config ? CONNECT_RETRY_DELAYS_MS : [];
1300
+ for (let attempt = 0;; attempt++) {
1301
+ this.stderrTail = undefined;
1302
+ try {
1303
+ return await this.connectOnce();
1304
+ } catch (error) {
1305
+ const delay = retries[attempt];
1306
+ if (this.closed || delay === undefined || !isTransientError(error)) {
1307
+ throw this.connectFailed(error);
1308
+ }
1309
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
1310
+ if (this.closed)
1311
+ throw this.connectFailed(error);
1312
+ }
1313
+ }
1314
+ }
1315
+ async connectOnce() {
1316
+ const client = new McpClient({
1317
+ name: "pi",
1318
+ version: VERSION,
1319
+ requestTimeoutMs: this.timeoutMs,
1320
+ roots: [{ uri: pathToFileURL(this.cwd).href, name: basename(this.cwd) }]
1321
+ });
1322
+ const log = this.log;
1323
+ if (log)
1324
+ client.onNotification("notifications/message", (params) => log.write(this.entry.name, params));
1325
+ let transport;
1326
+ try {
1327
+ transport = this.createTransport(this.entry, this.cwd, this.authProvider);
1328
+ await client.connect(transport);
1329
+ client.onNotification("notifications/tools/list_changed", () => {
1330
+ this.refreshTools(client);
1331
+ });
1332
+ client.onNotification("notifications/resources/list_changed", () => {
1333
+ this.refreshResources(client);
1334
+ });
1335
+ const stdio = transport instanceof StdioTransport ? transport : undefined;
1336
+ client.onClose(() => this.handleClientClose(client, stdio));
1337
+ const hasResources = client.serverCapabilities?.resources !== undefined;
1338
+ const [tools, resources] = await Promise.all([
1339
+ client.serverCapabilities?.tools ? client.listTools() : [],
1340
+ hasResources ? fetchResources(client) : { resources: [], resourceTemplates: [] }
1341
+ ]);
1342
+ if (this.closed)
1343
+ throw new Error("shut down while connecting");
1344
+ if (client.connectionState !== "connected")
1345
+ throw new Error("connection closed during setup");
1346
+ this.client = client;
1347
+ this.tools = tools;
1348
+ this.hasResources = hasResources;
1349
+ this.resources = resources.resources;
1350
+ this.resourceTemplates = resources.resourceTemplates;
1351
+ this.instructions = client.instructions?.trim() || undefined;
1352
+ this.state = "connected";
1353
+ this.error = undefined;
1354
+ this.onTools(this);
1355
+ this.changed();
1356
+ return client;
1357
+ } catch (error) {
1358
+ await client.close().catch(() => {
1359
+ return;
1360
+ });
1361
+ if (transport instanceof StdioTransport) {
1362
+ this.stderrTail = transport.stderr.trim().slice(-STDERR_TAIL_CHARS) || undefined;
1363
+ }
1364
+ throw error;
1365
+ }
1366
+ }
1367
+ connectFailed(error) {
1368
+ if (this.needsSignIn(error) && !this.closed) {
1369
+ this.markNeedsAuth();
1370
+ return new Error(signInRequiredMessage(this.entry.name));
1371
+ }
1372
+ this.state = this.closed ? "closed" : "failed";
1373
+ this.error = this.stderrTail ? `${errorMessage(error)}
1374
+ ${this.stderrTail}` : errorMessage(error);
1375
+ this.changed();
1376
+ return new Error(`MCP server "${this.entry.name}" failed to connect: ${this.error}`);
1377
+ }
1378
+ handleClientClose(client, stdio) {
1379
+ if (this.client !== client || this.closed)
1380
+ return;
1381
+ this.client = undefined;
1382
+ this.state = "disconnected";
1383
+ const stderr = stdio?.stderr.trim().slice(-STDERR_TAIL_CHARS);
1384
+ this.error = stderr ? `Connection closed
1385
+ ${stderr}` : "Connection closed";
1386
+ this.changed();
1387
+ }
1388
+ async refreshTools(client) {
1389
+ try {
1390
+ const tools = await client.listTools();
1391
+ if (this.client !== client || this.closed)
1392
+ return;
1393
+ this.tools = tools;
1394
+ this.onTools(this);
1395
+ } catch (error) {
1396
+ this.error = `Failed to refresh tools: ${errorMessage(error)}`;
1397
+ }
1398
+ this.changed();
1399
+ }
1400
+ async refreshResources(client) {
1401
+ const { resources, resourceTemplates } = await fetchResources(client);
1402
+ if (this.client !== client || this.closed)
1403
+ return;
1404
+ this.resources = resources;
1405
+ this.resourceTemplates = resourceTemplates;
1406
+ this.onTools(this);
1407
+ this.changed();
1408
+ }
1409
+ async close() {
1410
+ this.closed = true;
1411
+ this.state = "closed";
1412
+ this.changed();
1413
+ const client = this.client;
1414
+ this.client = undefined;
1415
+ await client?.close().catch(() => {
1416
+ return;
1417
+ });
1418
+ await this.authProvider?.settled();
1419
+ }
1420
+ }
1421
+
1422
+ export { McpOAuthCredentialStore, McpSignInCancelledError, signInMcpServer, McpServerLog, createDefaultTransport, McpServerConnection };