@dotsetlabs/bellwether 0.10.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 (403) hide show
  1. package/CHANGELOG.md +291 -0
  2. package/LICENSE +21 -0
  3. package/README.md +739 -0
  4. package/dist/auth/credentials.d.ts +64 -0
  5. package/dist/auth/credentials.js +218 -0
  6. package/dist/auth/index.d.ts +6 -0
  7. package/dist/auth/index.js +6 -0
  8. package/dist/auth/keychain.d.ts +64 -0
  9. package/dist/auth/keychain.js +268 -0
  10. package/dist/baseline/ab-testing.d.ts +80 -0
  11. package/dist/baseline/ab-testing.js +236 -0
  12. package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
  13. package/dist/baseline/ai-compatibility-scorer.js +606 -0
  14. package/dist/baseline/calibration.d.ts +77 -0
  15. package/dist/baseline/calibration.js +136 -0
  16. package/dist/baseline/category-matching.d.ts +85 -0
  17. package/dist/baseline/category-matching.js +289 -0
  18. package/dist/baseline/change-impact-analyzer.d.ts +98 -0
  19. package/dist/baseline/change-impact-analyzer.js +592 -0
  20. package/dist/baseline/comparator.d.ts +64 -0
  21. package/dist/baseline/comparator.js +916 -0
  22. package/dist/baseline/confidence.d.ts +55 -0
  23. package/dist/baseline/confidence.js +122 -0
  24. package/dist/baseline/converter.d.ts +61 -0
  25. package/dist/baseline/converter.js +585 -0
  26. package/dist/baseline/dependency-analyzer.d.ts +89 -0
  27. package/dist/baseline/dependency-analyzer.js +567 -0
  28. package/dist/baseline/deprecation-tracker.d.ts +133 -0
  29. package/dist/baseline/deprecation-tracker.js +322 -0
  30. package/dist/baseline/diff.d.ts +55 -0
  31. package/dist/baseline/diff.js +1584 -0
  32. package/dist/baseline/documentation-scorer.d.ts +205 -0
  33. package/dist/baseline/documentation-scorer.js +466 -0
  34. package/dist/baseline/embeddings.d.ts +118 -0
  35. package/dist/baseline/embeddings.js +251 -0
  36. package/dist/baseline/error-analyzer.d.ts +198 -0
  37. package/dist/baseline/error-analyzer.js +721 -0
  38. package/dist/baseline/evaluation/evaluator.d.ts +42 -0
  39. package/dist/baseline/evaluation/evaluator.js +323 -0
  40. package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
  41. package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
  42. package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
  43. package/dist/baseline/evaluation/golden-dataset.js +717 -0
  44. package/dist/baseline/evaluation/index.d.ts +15 -0
  45. package/dist/baseline/evaluation/index.js +15 -0
  46. package/dist/baseline/evaluation/types.d.ts +186 -0
  47. package/dist/baseline/evaluation/types.js +8 -0
  48. package/dist/baseline/external-dependency-detector.d.ts +181 -0
  49. package/dist/baseline/external-dependency-detector.js +524 -0
  50. package/dist/baseline/golden-output.d.ts +162 -0
  51. package/dist/baseline/golden-output.js +636 -0
  52. package/dist/baseline/health-scorer.d.ts +174 -0
  53. package/dist/baseline/health-scorer.js +451 -0
  54. package/dist/baseline/incremental-checker.d.ts +97 -0
  55. package/dist/baseline/incremental-checker.js +174 -0
  56. package/dist/baseline/index.d.ts +31 -0
  57. package/dist/baseline/index.js +42 -0
  58. package/dist/baseline/migration-generator.d.ts +137 -0
  59. package/dist/baseline/migration-generator.js +554 -0
  60. package/dist/baseline/migrations.d.ts +60 -0
  61. package/dist/baseline/migrations.js +197 -0
  62. package/dist/baseline/performance-tracker.d.ts +214 -0
  63. package/dist/baseline/performance-tracker.js +577 -0
  64. package/dist/baseline/pr-comment-generator.d.ts +117 -0
  65. package/dist/baseline/pr-comment-generator.js +546 -0
  66. package/dist/baseline/response-fingerprint.d.ts +127 -0
  67. package/dist/baseline/response-fingerprint.js +728 -0
  68. package/dist/baseline/response-schema-tracker.d.ts +129 -0
  69. package/dist/baseline/response-schema-tracker.js +420 -0
  70. package/dist/baseline/risk-scorer.d.ts +54 -0
  71. package/dist/baseline/risk-scorer.js +434 -0
  72. package/dist/baseline/saver.d.ts +89 -0
  73. package/dist/baseline/saver.js +554 -0
  74. package/dist/baseline/scenario-generator.d.ts +151 -0
  75. package/dist/baseline/scenario-generator.js +905 -0
  76. package/dist/baseline/schema-compare.d.ts +86 -0
  77. package/dist/baseline/schema-compare.js +557 -0
  78. package/dist/baseline/schema-evolution.d.ts +189 -0
  79. package/dist/baseline/schema-evolution.js +467 -0
  80. package/dist/baseline/semantic.d.ts +203 -0
  81. package/dist/baseline/semantic.js +908 -0
  82. package/dist/baseline/synonyms.d.ts +60 -0
  83. package/dist/baseline/synonyms.js +386 -0
  84. package/dist/baseline/telemetry.d.ts +165 -0
  85. package/dist/baseline/telemetry.js +294 -0
  86. package/dist/baseline/test-pruner.d.ts +120 -0
  87. package/dist/baseline/test-pruner.js +387 -0
  88. package/dist/baseline/types.d.ts +449 -0
  89. package/dist/baseline/types.js +5 -0
  90. package/dist/baseline/version.d.ts +138 -0
  91. package/dist/baseline/version.js +206 -0
  92. package/dist/cache/index.d.ts +5 -0
  93. package/dist/cache/index.js +5 -0
  94. package/dist/cache/response-cache.d.ts +151 -0
  95. package/dist/cache/response-cache.js +287 -0
  96. package/dist/ci/index.d.ts +60 -0
  97. package/dist/ci/index.js +342 -0
  98. package/dist/cli/commands/auth.d.ts +12 -0
  99. package/dist/cli/commands/auth.js +352 -0
  100. package/dist/cli/commands/badge.d.ts +3 -0
  101. package/dist/cli/commands/badge.js +74 -0
  102. package/dist/cli/commands/baseline-accept.d.ts +15 -0
  103. package/dist/cli/commands/baseline-accept.js +178 -0
  104. package/dist/cli/commands/baseline-migrate.d.ts +12 -0
  105. package/dist/cli/commands/baseline-migrate.js +164 -0
  106. package/dist/cli/commands/baseline.d.ts +14 -0
  107. package/dist/cli/commands/baseline.js +449 -0
  108. package/dist/cli/commands/beta.d.ts +10 -0
  109. package/dist/cli/commands/beta.js +231 -0
  110. package/dist/cli/commands/check.d.ts +11 -0
  111. package/dist/cli/commands/check.js +820 -0
  112. package/dist/cli/commands/cloud/badge.d.ts +3 -0
  113. package/dist/cli/commands/cloud/badge.js +74 -0
  114. package/dist/cli/commands/cloud/diff.d.ts +6 -0
  115. package/dist/cli/commands/cloud/diff.js +79 -0
  116. package/dist/cli/commands/cloud/history.d.ts +6 -0
  117. package/dist/cli/commands/cloud/history.js +102 -0
  118. package/dist/cli/commands/cloud/link.d.ts +9 -0
  119. package/dist/cli/commands/cloud/link.js +119 -0
  120. package/dist/cli/commands/cloud/login.d.ts +7 -0
  121. package/dist/cli/commands/cloud/login.js +499 -0
  122. package/dist/cli/commands/cloud/projects.d.ts +6 -0
  123. package/dist/cli/commands/cloud/projects.js +44 -0
  124. package/dist/cli/commands/cloud/shared.d.ts +7 -0
  125. package/dist/cli/commands/cloud/shared.js +42 -0
  126. package/dist/cli/commands/cloud/teams.d.ts +8 -0
  127. package/dist/cli/commands/cloud/teams.js +169 -0
  128. package/dist/cli/commands/cloud/upload.d.ts +8 -0
  129. package/dist/cli/commands/cloud/upload.js +181 -0
  130. package/dist/cli/commands/contract.d.ts +11 -0
  131. package/dist/cli/commands/contract.js +280 -0
  132. package/dist/cli/commands/discover.d.ts +3 -0
  133. package/dist/cli/commands/discover.js +82 -0
  134. package/dist/cli/commands/eval.d.ts +9 -0
  135. package/dist/cli/commands/eval.js +187 -0
  136. package/dist/cli/commands/explore.d.ts +11 -0
  137. package/dist/cli/commands/explore.js +437 -0
  138. package/dist/cli/commands/feedback.d.ts +9 -0
  139. package/dist/cli/commands/feedback.js +174 -0
  140. package/dist/cli/commands/golden.d.ts +12 -0
  141. package/dist/cli/commands/golden.js +407 -0
  142. package/dist/cli/commands/history.d.ts +10 -0
  143. package/dist/cli/commands/history.js +202 -0
  144. package/dist/cli/commands/init.d.ts +9 -0
  145. package/dist/cli/commands/init.js +219 -0
  146. package/dist/cli/commands/interview.d.ts +3 -0
  147. package/dist/cli/commands/interview.js +903 -0
  148. package/dist/cli/commands/link.d.ts +10 -0
  149. package/dist/cli/commands/link.js +169 -0
  150. package/dist/cli/commands/login.d.ts +7 -0
  151. package/dist/cli/commands/login.js +499 -0
  152. package/dist/cli/commands/preset.d.ts +33 -0
  153. package/dist/cli/commands/preset.js +297 -0
  154. package/dist/cli/commands/profile.d.ts +33 -0
  155. package/dist/cli/commands/profile.js +286 -0
  156. package/dist/cli/commands/registry.d.ts +11 -0
  157. package/dist/cli/commands/registry.js +146 -0
  158. package/dist/cli/commands/shared.d.ts +79 -0
  159. package/dist/cli/commands/shared.js +196 -0
  160. package/dist/cli/commands/teams.d.ts +8 -0
  161. package/dist/cli/commands/teams.js +169 -0
  162. package/dist/cli/commands/test.d.ts +9 -0
  163. package/dist/cli/commands/test.js +500 -0
  164. package/dist/cli/commands/upload.d.ts +8 -0
  165. package/dist/cli/commands/upload.js +223 -0
  166. package/dist/cli/commands/validate-config.d.ts +6 -0
  167. package/dist/cli/commands/validate-config.js +35 -0
  168. package/dist/cli/commands/verify.d.ts +11 -0
  169. package/dist/cli/commands/verify.js +283 -0
  170. package/dist/cli/commands/watch.d.ts +12 -0
  171. package/dist/cli/commands/watch.js +253 -0
  172. package/dist/cli/index.d.ts +3 -0
  173. package/dist/cli/index.js +178 -0
  174. package/dist/cli/interactive.d.ts +47 -0
  175. package/dist/cli/interactive.js +216 -0
  176. package/dist/cli/output/terminal-reporter.d.ts +19 -0
  177. package/dist/cli/output/terminal-reporter.js +104 -0
  178. package/dist/cli/output.d.ts +226 -0
  179. package/dist/cli/output.js +438 -0
  180. package/dist/cli/utils/env.d.ts +5 -0
  181. package/dist/cli/utils/env.js +14 -0
  182. package/dist/cli/utils/progress.d.ts +59 -0
  183. package/dist/cli/utils/progress.js +206 -0
  184. package/dist/cli/utils/server-context.d.ts +10 -0
  185. package/dist/cli/utils/server-context.js +36 -0
  186. package/dist/cloud/auth.d.ts +144 -0
  187. package/dist/cloud/auth.js +374 -0
  188. package/dist/cloud/client.d.ts +24 -0
  189. package/dist/cloud/client.js +65 -0
  190. package/dist/cloud/http-client.d.ts +38 -0
  191. package/dist/cloud/http-client.js +215 -0
  192. package/dist/cloud/index.d.ts +23 -0
  193. package/dist/cloud/index.js +25 -0
  194. package/dist/cloud/mock-client.d.ts +107 -0
  195. package/dist/cloud/mock-client.js +545 -0
  196. package/dist/cloud/types.d.ts +515 -0
  197. package/dist/cloud/types.js +15 -0
  198. package/dist/config/defaults.d.ts +160 -0
  199. package/dist/config/defaults.js +169 -0
  200. package/dist/config/loader.d.ts +24 -0
  201. package/dist/config/loader.js +122 -0
  202. package/dist/config/template.d.ts +42 -0
  203. package/dist/config/template.js +647 -0
  204. package/dist/config/validator.d.ts +2112 -0
  205. package/dist/config/validator.js +658 -0
  206. package/dist/constants/cloud.d.ts +107 -0
  207. package/dist/constants/cloud.js +110 -0
  208. package/dist/constants/core.d.ts +521 -0
  209. package/dist/constants/core.js +556 -0
  210. package/dist/constants/testing.d.ts +1283 -0
  211. package/dist/constants/testing.js +1568 -0
  212. package/dist/constants.d.ts +10 -0
  213. package/dist/constants.js +10 -0
  214. package/dist/contract/index.d.ts +6 -0
  215. package/dist/contract/index.js +5 -0
  216. package/dist/contract/validator.d.ts +177 -0
  217. package/dist/contract/validator.js +574 -0
  218. package/dist/cost/index.d.ts +6 -0
  219. package/dist/cost/index.js +5 -0
  220. package/dist/cost/tracker.d.ts +134 -0
  221. package/dist/cost/tracker.js +313 -0
  222. package/dist/discovery/discovery.d.ts +16 -0
  223. package/dist/discovery/discovery.js +173 -0
  224. package/dist/discovery/types.d.ts +51 -0
  225. package/dist/discovery/types.js +2 -0
  226. package/dist/docs/agents.d.ts +3 -0
  227. package/dist/docs/agents.js +995 -0
  228. package/dist/docs/contract.d.ts +51 -0
  229. package/dist/docs/contract.js +1681 -0
  230. package/dist/docs/generator.d.ts +4 -0
  231. package/dist/docs/generator.js +4 -0
  232. package/dist/docs/html-reporter.d.ts +9 -0
  233. package/dist/docs/html-reporter.js +757 -0
  234. package/dist/docs/index.d.ts +10 -0
  235. package/dist/docs/index.js +11 -0
  236. package/dist/docs/junit-reporter.d.ts +18 -0
  237. package/dist/docs/junit-reporter.js +210 -0
  238. package/dist/docs/report.d.ts +14 -0
  239. package/dist/docs/report.js +44 -0
  240. package/dist/docs/sarif-reporter.d.ts +19 -0
  241. package/dist/docs/sarif-reporter.js +335 -0
  242. package/dist/docs/shared.d.ts +35 -0
  243. package/dist/docs/shared.js +162 -0
  244. package/dist/docs/templates.d.ts +12 -0
  245. package/dist/docs/templates.js +76 -0
  246. package/dist/errors/index.d.ts +6 -0
  247. package/dist/errors/index.js +6 -0
  248. package/dist/errors/retry.d.ts +92 -0
  249. package/dist/errors/retry.js +323 -0
  250. package/dist/errors/types.d.ts +321 -0
  251. package/dist/errors/types.js +584 -0
  252. package/dist/index.d.ts +32 -0
  253. package/dist/index.js +32 -0
  254. package/dist/interview/dependency-resolver.d.ts +11 -0
  255. package/dist/interview/dependency-resolver.js +32 -0
  256. package/dist/interview/interviewer.d.ts +232 -0
  257. package/dist/interview/interviewer.js +1939 -0
  258. package/dist/interview/mock-response-generator.d.ts +7 -0
  259. package/dist/interview/mock-response-generator.js +102 -0
  260. package/dist/interview/orchestrator.d.ts +237 -0
  261. package/dist/interview/orchestrator.js +1296 -0
  262. package/dist/interview/rate-limiter.d.ts +15 -0
  263. package/dist/interview/rate-limiter.js +55 -0
  264. package/dist/interview/response-validator.d.ts +10 -0
  265. package/dist/interview/response-validator.js +132 -0
  266. package/dist/interview/schema-inferrer.d.ts +8 -0
  267. package/dist/interview/schema-inferrer.js +71 -0
  268. package/dist/interview/schema-test-generator.d.ts +71 -0
  269. package/dist/interview/schema-test-generator.js +834 -0
  270. package/dist/interview/smart-value-generator.d.ts +155 -0
  271. package/dist/interview/smart-value-generator.js +554 -0
  272. package/dist/interview/stateful-test-runner.d.ts +19 -0
  273. package/dist/interview/stateful-test-runner.js +106 -0
  274. package/dist/interview/types.d.ts +561 -0
  275. package/dist/interview/types.js +2 -0
  276. package/dist/llm/anthropic.d.ts +41 -0
  277. package/dist/llm/anthropic.js +355 -0
  278. package/dist/llm/client.d.ts +123 -0
  279. package/dist/llm/client.js +42 -0
  280. package/dist/llm/factory.d.ts +38 -0
  281. package/dist/llm/factory.js +145 -0
  282. package/dist/llm/fallback.d.ts +140 -0
  283. package/dist/llm/fallback.js +379 -0
  284. package/dist/llm/index.d.ts +18 -0
  285. package/dist/llm/index.js +15 -0
  286. package/dist/llm/ollama.d.ts +37 -0
  287. package/dist/llm/ollama.js +330 -0
  288. package/dist/llm/openai.d.ts +25 -0
  289. package/dist/llm/openai.js +320 -0
  290. package/dist/llm/token-budget.d.ts +161 -0
  291. package/dist/llm/token-budget.js +395 -0
  292. package/dist/logging/logger.d.ts +70 -0
  293. package/dist/logging/logger.js +130 -0
  294. package/dist/metrics/collector.d.ts +106 -0
  295. package/dist/metrics/collector.js +547 -0
  296. package/dist/metrics/index.d.ts +7 -0
  297. package/dist/metrics/index.js +7 -0
  298. package/dist/metrics/prometheus.d.ts +20 -0
  299. package/dist/metrics/prometheus.js +241 -0
  300. package/dist/metrics/types.d.ts +209 -0
  301. package/dist/metrics/types.js +5 -0
  302. package/dist/persona/builtins.d.ts +54 -0
  303. package/dist/persona/builtins.js +219 -0
  304. package/dist/persona/index.d.ts +8 -0
  305. package/dist/persona/index.js +8 -0
  306. package/dist/persona/loader.d.ts +30 -0
  307. package/dist/persona/loader.js +190 -0
  308. package/dist/persona/types.d.ts +144 -0
  309. package/dist/persona/types.js +5 -0
  310. package/dist/persona/validation.d.ts +94 -0
  311. package/dist/persona/validation.js +332 -0
  312. package/dist/prompts/index.d.ts +5 -0
  313. package/dist/prompts/index.js +5 -0
  314. package/dist/prompts/templates.d.ts +180 -0
  315. package/dist/prompts/templates.js +431 -0
  316. package/dist/registry/client.d.ts +49 -0
  317. package/dist/registry/client.js +191 -0
  318. package/dist/registry/index.d.ts +7 -0
  319. package/dist/registry/index.js +6 -0
  320. package/dist/registry/types.d.ts +140 -0
  321. package/dist/registry/types.js +6 -0
  322. package/dist/scenarios/evaluator.d.ts +43 -0
  323. package/dist/scenarios/evaluator.js +206 -0
  324. package/dist/scenarios/index.d.ts +10 -0
  325. package/dist/scenarios/index.js +9 -0
  326. package/dist/scenarios/loader.d.ts +20 -0
  327. package/dist/scenarios/loader.js +285 -0
  328. package/dist/scenarios/types.d.ts +153 -0
  329. package/dist/scenarios/types.js +8 -0
  330. package/dist/security/index.d.ts +17 -0
  331. package/dist/security/index.js +18 -0
  332. package/dist/security/payloads.d.ts +61 -0
  333. package/dist/security/payloads.js +268 -0
  334. package/dist/security/security-tester.d.ts +42 -0
  335. package/dist/security/security-tester.js +582 -0
  336. package/dist/security/types.d.ts +166 -0
  337. package/dist/security/types.js +8 -0
  338. package/dist/transport/base-transport.d.ts +59 -0
  339. package/dist/transport/base-transport.js +38 -0
  340. package/dist/transport/http-transport.d.ts +67 -0
  341. package/dist/transport/http-transport.js +238 -0
  342. package/dist/transport/mcp-client.d.ts +141 -0
  343. package/dist/transport/mcp-client.js +496 -0
  344. package/dist/transport/sse-transport.d.ts +88 -0
  345. package/dist/transport/sse-transport.js +316 -0
  346. package/dist/transport/stdio-transport.d.ts +43 -0
  347. package/dist/transport/stdio-transport.js +238 -0
  348. package/dist/transport/types.d.ts +125 -0
  349. package/dist/transport/types.js +16 -0
  350. package/dist/utils/concurrency.d.ts +123 -0
  351. package/dist/utils/concurrency.js +213 -0
  352. package/dist/utils/formatters.d.ts +16 -0
  353. package/dist/utils/formatters.js +37 -0
  354. package/dist/utils/index.d.ts +8 -0
  355. package/dist/utils/index.js +8 -0
  356. package/dist/utils/jsonpath.d.ts +87 -0
  357. package/dist/utils/jsonpath.js +326 -0
  358. package/dist/utils/markdown.d.ts +113 -0
  359. package/dist/utils/markdown.js +265 -0
  360. package/dist/utils/network.d.ts +14 -0
  361. package/dist/utils/network.js +17 -0
  362. package/dist/utils/sanitize.d.ts +92 -0
  363. package/dist/utils/sanitize.js +191 -0
  364. package/dist/utils/semantic.d.ts +194 -0
  365. package/dist/utils/semantic.js +1051 -0
  366. package/dist/utils/smart-truncate.d.ts +94 -0
  367. package/dist/utils/smart-truncate.js +361 -0
  368. package/dist/utils/timeout.d.ts +153 -0
  369. package/dist/utils/timeout.js +205 -0
  370. package/dist/utils/yaml-parser.d.ts +58 -0
  371. package/dist/utils/yaml-parser.js +86 -0
  372. package/dist/validation/index.d.ts +32 -0
  373. package/dist/validation/index.js +32 -0
  374. package/dist/validation/semantic-test-generator.d.ts +50 -0
  375. package/dist/validation/semantic-test-generator.js +176 -0
  376. package/dist/validation/semantic-types.d.ts +66 -0
  377. package/dist/validation/semantic-types.js +94 -0
  378. package/dist/validation/semantic-validator.d.ts +38 -0
  379. package/dist/validation/semantic-validator.js +340 -0
  380. package/dist/verification/index.d.ts +6 -0
  381. package/dist/verification/index.js +5 -0
  382. package/dist/verification/types.d.ts +133 -0
  383. package/dist/verification/types.js +5 -0
  384. package/dist/verification/verifier.d.ts +30 -0
  385. package/dist/verification/verifier.js +309 -0
  386. package/dist/version.d.ts +19 -0
  387. package/dist/version.js +48 -0
  388. package/dist/workflow/auto-generator.d.ts +27 -0
  389. package/dist/workflow/auto-generator.js +513 -0
  390. package/dist/workflow/discovery.d.ts +40 -0
  391. package/dist/workflow/discovery.js +195 -0
  392. package/dist/workflow/executor.d.ts +82 -0
  393. package/dist/workflow/executor.js +611 -0
  394. package/dist/workflow/index.d.ts +10 -0
  395. package/dist/workflow/index.js +10 -0
  396. package/dist/workflow/loader.d.ts +24 -0
  397. package/dist/workflow/loader.js +194 -0
  398. package/dist/workflow/state-tracker.d.ts +98 -0
  399. package/dist/workflow/state-tracker.js +424 -0
  400. package/dist/workflow/types.d.ts +337 -0
  401. package/dist/workflow/types.js +5 -0
  402. package/package.json +94 -0
  403. package/schemas/bellwether-check.schema.json +651 -0
@@ -0,0 +1,88 @@
1
+ import type { JSONRPCMessage } from './types.js';
2
+ import { BaseTransport, type BaseTransportConfig } from './base-transport.js';
3
+ /**
4
+ * Configuration for SSE Transport.
5
+ */
6
+ export interface SSETransportConfig extends BaseTransportConfig {
7
+ /** Base URL of the MCP server (e.g., https://api.example.com/mcp) */
8
+ baseUrl: string;
9
+ /** Optional session ID for authenticated connections */
10
+ sessionId?: string;
11
+ /** Custom headers to include in requests */
12
+ headers?: Record<string, string>;
13
+ /** Reconnect delay in milliseconds (default: 1000) */
14
+ reconnectDelay?: number;
15
+ /** Maximum reconnect attempts (default: 5) */
16
+ maxReconnectAttempts?: number;
17
+ /** Request timeout in milliseconds (default: 30000) */
18
+ timeout?: number;
19
+ }
20
+ /**
21
+ * SSETransport connects to MCP servers over HTTP using Server-Sent Events.
22
+ *
23
+ * This transport is used for remote MCP servers that expose an SSE endpoint.
24
+ * Messages from the server are received via SSE, while requests are sent
25
+ * via HTTP POST.
26
+ *
27
+ * Expected server endpoints:
28
+ * - GET {baseUrl}/sse - SSE endpoint for receiving messages
29
+ * - POST {baseUrl}/message - Endpoint for sending messages
30
+ */
31
+ export declare class SSETransport extends BaseTransport {
32
+ private eventSource;
33
+ private abortController;
34
+ private connected;
35
+ private reconnectAttempts;
36
+ private readonly baseUrl;
37
+ private readonly sessionId?;
38
+ private readonly headers;
39
+ private readonly reconnectDelay;
40
+ private readonly maxReconnectAttempts;
41
+ private readonly timeout;
42
+ private messageEndpoint;
43
+ /** Timer ID for reconnection delay - allows cancellation */
44
+ private reconnectTimer;
45
+ /** Flag to prevent reconnection after close() is called */
46
+ private isClosing;
47
+ /** Maximum backoff delay in milliseconds */
48
+ private readonly maxBackoffDelay;
49
+ constructor(config: SSETransportConfig);
50
+ /**
51
+ * Connect to the SSE endpoint.
52
+ */
53
+ connect(): Promise<void>;
54
+ /**
55
+ * Handle an incoming SSE message.
56
+ */
57
+ private handleSSEMessage;
58
+ /**
59
+ * Handle reconnection after a connection error.
60
+ *
61
+ * RELIABILITY IMPROVEMENTS:
62
+ * - Prevents unbounded recursion with max attempts check
63
+ * - Uses capped exponential backoff
64
+ * - Clears reconnect timer on close
65
+ * - Checks isClosing flag to prevent reconnection after close()
66
+ * - Explicitly closes EventSource on max attempts
67
+ */
68
+ private handleReconnect;
69
+ /**
70
+ * Send a JSON-RPC message to the server via HTTP POST.
71
+ */
72
+ send(message: JSONRPCMessage): void;
73
+ /**
74
+ * Close the SSE connection.
75
+ *
76
+ * RELIABILITY: Properly cleans up all resources including:
77
+ * - EventSource connection
78
+ * - Pending HTTP requests (via abort controller)
79
+ * - Reconnection timer
80
+ * - Sets isClosing flag to prevent reconnection attempts
81
+ */
82
+ close(): void;
83
+ /**
84
+ * Check if the transport is connected.
85
+ */
86
+ isConnected(): boolean;
87
+ }
88
+ //# sourceMappingURL=sse-transport.d.ts.map
@@ -0,0 +1,316 @@
1
+ import { BaseTransport } from './base-transport.js';
2
+ import { TIME_CONSTANTS, TIMEOUTS } from '../constants.js';
3
+ import { isLocalhost } from '../utils/index.js';
4
+ /**
5
+ * Validate that a URL uses HTTPS in production contexts.
6
+ * Allows HTTP only for localhost/127.0.0.1 for local development.
7
+ */
8
+ function validateSecureUrl(url) {
9
+ try {
10
+ const parsed = new URL(url);
11
+ if (parsed.protocol !== 'https:' && !isLocalhost(parsed.hostname)) {
12
+ throw new Error(`SSE transport requires HTTPS for remote servers. ` +
13
+ `Got: ${parsed.protocol}//. Use HTTPS to protect session tokens in transit.`);
14
+ }
15
+ }
16
+ catch (error) {
17
+ if (error instanceof Error && error.message.includes('SSE transport')) {
18
+ throw error;
19
+ }
20
+ throw new Error(`Invalid SSE URL: ${url}`);
21
+ }
22
+ }
23
+ /**
24
+ * SSETransport connects to MCP servers over HTTP using Server-Sent Events.
25
+ *
26
+ * This transport is used for remote MCP servers that expose an SSE endpoint.
27
+ * Messages from the server are received via SSE, while requests are sent
28
+ * via HTTP POST.
29
+ *
30
+ * Expected server endpoints:
31
+ * - GET {baseUrl}/sse - SSE endpoint for receiving messages
32
+ * - POST {baseUrl}/message - Endpoint for sending messages
33
+ */
34
+ export class SSETransport extends BaseTransport {
35
+ eventSource = null;
36
+ abortController = null;
37
+ connected = false;
38
+ reconnectAttempts = 0;
39
+ baseUrl;
40
+ sessionId;
41
+ headers;
42
+ reconnectDelay;
43
+ maxReconnectAttempts;
44
+ timeout;
45
+ messageEndpoint = null;
46
+ /** Timer ID for reconnection delay - allows cancellation */
47
+ reconnectTimer = null;
48
+ /** Flag to prevent reconnection after close() is called */
49
+ isClosing = false;
50
+ /** Maximum backoff delay in milliseconds */
51
+ maxBackoffDelay = TIME_CONSTANTS.SSE_MAX_BACKOFF;
52
+ constructor(config) {
53
+ super(config);
54
+ this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
55
+ this.sessionId = config.sessionId;
56
+ this.headers = config.headers ?? {};
57
+ this.reconnectDelay = config.reconnectDelay ?? TIME_CONSTANTS.SSE_RECONNECT_DELAY;
58
+ this.maxReconnectAttempts = config.maxReconnectAttempts ?? 5;
59
+ this.timeout = config.timeout ?? TIMEOUTS.DEFAULT;
60
+ // Add session ID to headers if provided
61
+ if (this.sessionId) {
62
+ this.headers['X-Session-Id'] = this.sessionId;
63
+ }
64
+ }
65
+ /**
66
+ * Connect to the SSE endpoint.
67
+ */
68
+ async connect() {
69
+ if (this.connected) {
70
+ return;
71
+ }
72
+ // SECURITY: Validate URL uses HTTPS for remote servers
73
+ // This protects session tokens that may be passed via URL parameters
74
+ validateSecureUrl(this.baseUrl);
75
+ // Reset closing flag on fresh connection
76
+ this.isClosing = false;
77
+ // EventSource is available in browsers natively and in Node.js 18+.
78
+ // We use globalThis to check availability at runtime, requiring `any` cast
79
+ // because TypeScript's lib.dom.d.ts doesn't type globalThis.EventSource.
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ const EventSourceImpl = globalThis.EventSource;
82
+ if (!EventSourceImpl) {
83
+ throw new Error('EventSource is not available. ' +
84
+ 'SSE transport requires Node.js 18+ or a browser environment. ' +
85
+ 'For older Node.js versions, consider using streamable-http transport instead.');
86
+ }
87
+ return new Promise((resolve, reject) => {
88
+ const sseUrl = `${this.baseUrl}/sse`;
89
+ this.log('Connecting to SSE endpoint', { url: sseUrl });
90
+ try {
91
+ // Create EventSource - headers need to be passed via URL params or custom implementation
92
+ // Note: Standard EventSource doesn't support custom headers
93
+ // For authenticated endpoints, session ID should be passed via URL param
94
+ let url = sseUrl;
95
+ if (this.sessionId) {
96
+ const separator = url.includes('?') ? '&' : '?';
97
+ url = `${url}${separator}sessionId=${encodeURIComponent(this.sessionId)}`;
98
+ }
99
+ this.eventSource = new EventSourceImpl(url);
100
+ this.eventSource.onopen = () => {
101
+ this.log('SSE connection opened');
102
+ this.connected = true;
103
+ this.reconnectAttempts = 0;
104
+ resolve();
105
+ };
106
+ this.eventSource.onmessage = (event) => {
107
+ this.handleSSEMessage(event);
108
+ };
109
+ // Handle specific event types from MCP SSE protocol
110
+ this.eventSource.addEventListener('endpoint', (event) => {
111
+ // Server tells us where to send messages
112
+ const messageEvent = event;
113
+ this.messageEndpoint = messageEvent.data;
114
+ this.log('Received message endpoint', { endpoint: this.messageEndpoint ?? '' });
115
+ });
116
+ this.eventSource.addEventListener('message', (event) => {
117
+ this.handleSSEMessage(event);
118
+ });
119
+ this.eventSource.onerror = (error) => {
120
+ this.log('SSE error', { type: error.type });
121
+ if (!this.connected) {
122
+ // Connection failed on initial connect
123
+ reject(new Error('Failed to connect to SSE endpoint'));
124
+ return;
125
+ }
126
+ // Handle reconnection for established connections
127
+ this.handleReconnect();
128
+ };
129
+ }
130
+ catch (error) {
131
+ reject(error);
132
+ }
133
+ });
134
+ }
135
+ /**
136
+ * Handle an incoming SSE message.
137
+ */
138
+ handleSSEMessage(event) {
139
+ try {
140
+ const data = event.data;
141
+ // Skip empty messages or heartbeats
142
+ if (!data || data === ':') {
143
+ return;
144
+ }
145
+ this.log('Received SSE message', { data });
146
+ const message = JSON.parse(data);
147
+ this.emit('message', message);
148
+ }
149
+ catch (error) {
150
+ this.log('Failed to parse SSE message', { error: error instanceof Error ? error.message : String(error) });
151
+ // Don't emit error for parse failures - just log
152
+ }
153
+ }
154
+ /**
155
+ * Handle reconnection after a connection error.
156
+ *
157
+ * RELIABILITY IMPROVEMENTS:
158
+ * - Prevents unbounded recursion with max attempts check
159
+ * - Uses capped exponential backoff
160
+ * - Clears reconnect timer on close
161
+ * - Checks isClosing flag to prevent reconnection after close()
162
+ * - Explicitly closes EventSource on max attempts
163
+ */
164
+ handleReconnect() {
165
+ // Don't reconnect if we're closing
166
+ if (this.isClosing) {
167
+ this.log('Reconnection skipped - transport is closing');
168
+ return;
169
+ }
170
+ // Check max attempts BEFORE incrementing
171
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
172
+ this.log('Max reconnect attempts reached', { attempts: this.reconnectAttempts });
173
+ this.connected = false;
174
+ // Explicitly close the EventSource to clean up resources
175
+ if (this.eventSource) {
176
+ try {
177
+ this.eventSource.close();
178
+ }
179
+ catch {
180
+ // Ignore close errors
181
+ }
182
+ this.eventSource = null;
183
+ }
184
+ this.emit('error', new Error(`Max reconnection attempts (${this.maxReconnectAttempts}) exceeded`));
185
+ this.emit('close');
186
+ return;
187
+ }
188
+ this.reconnectAttempts++;
189
+ // Exponential backoff with cap
190
+ const exponentialDelay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
191
+ const delay = Math.min(exponentialDelay, this.maxBackoffDelay);
192
+ this.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
193
+ // Clear any existing reconnect timer
194
+ if (this.reconnectTimer) {
195
+ clearTimeout(this.reconnectTimer);
196
+ }
197
+ // Store the timer so it can be cancelled by close()
198
+ this.reconnectTimer = setTimeout(() => {
199
+ this.reconnectTimer = null;
200
+ // Double-check we're not closing before attempting reconnect
201
+ if (this.isClosing) {
202
+ return;
203
+ }
204
+ // Non-recursive approach: use .then/.catch instead of async/await in setTimeout
205
+ this.connect()
206
+ .then(() => {
207
+ this.log('Reconnection successful');
208
+ })
209
+ .catch((error) => {
210
+ this.log('Reconnection failed', { error: error.message });
211
+ // Schedule next attempt (not recursive - just schedules another timer)
212
+ this.handleReconnect();
213
+ });
214
+ }, delay);
215
+ }
216
+ /**
217
+ * Send a JSON-RPC message to the server via HTTP POST.
218
+ */
219
+ send(message) {
220
+ if (!this.connected) {
221
+ this.emit('error', new Error('Transport not connected'));
222
+ return;
223
+ }
224
+ // Use the endpoint provided by the server, or default to /message
225
+ const endpoint = this.messageEndpoint || `${this.baseUrl}/message`;
226
+ this.log('Sending message', { endpoint, message });
227
+ // Create a new abort controller for this request
228
+ this.abortController = new AbortController();
229
+ const timeoutId = setTimeout(() => {
230
+ this.abortController?.abort();
231
+ }, this.timeout);
232
+ fetch(endpoint, {
233
+ method: 'POST',
234
+ headers: {
235
+ 'Content-Type': 'application/json',
236
+ ...this.headers,
237
+ },
238
+ body: JSON.stringify(message),
239
+ signal: this.abortController.signal,
240
+ })
241
+ .then(async (response) => {
242
+ clearTimeout(timeoutId);
243
+ if (!response.ok) {
244
+ const errorText = await response.text().catch(() => 'Unknown error');
245
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
246
+ }
247
+ // Some servers may return a response directly (for request/response pattern)
248
+ const contentType = response.headers.get('content-type');
249
+ if (contentType?.includes('application/json')) {
250
+ const responseData = await response.json();
251
+ if (responseData && typeof responseData === 'object') {
252
+ this.emit('message', responseData);
253
+ }
254
+ }
255
+ })
256
+ .catch((error) => {
257
+ clearTimeout(timeoutId);
258
+ if (error.name === 'AbortError') {
259
+ this.emit('error', new Error('Request timeout'));
260
+ }
261
+ else {
262
+ this.emit('error', error);
263
+ }
264
+ });
265
+ }
266
+ /**
267
+ * Close the SSE connection.
268
+ *
269
+ * RELIABILITY: Properly cleans up all resources including:
270
+ * - EventSource connection
271
+ * - Pending HTTP requests (via abort controller)
272
+ * - Reconnection timer
273
+ * - Sets isClosing flag to prevent reconnection attempts
274
+ */
275
+ close() {
276
+ this.log('Closing SSE transport');
277
+ // Set closing flag FIRST to prevent any reconnection attempts
278
+ this.isClosing = true;
279
+ this.connected = false;
280
+ // Cancel any pending reconnection timer
281
+ if (this.reconnectTimer) {
282
+ clearTimeout(this.reconnectTimer);
283
+ this.reconnectTimer = null;
284
+ }
285
+ // Close the EventSource connection
286
+ if (this.eventSource) {
287
+ try {
288
+ this.eventSource.close();
289
+ }
290
+ catch {
291
+ // Ignore close errors
292
+ }
293
+ this.eventSource = null;
294
+ }
295
+ // Abort any in-flight HTTP requests
296
+ if (this.abortController) {
297
+ try {
298
+ this.abortController.abort();
299
+ }
300
+ catch {
301
+ // Ignore abort errors
302
+ }
303
+ this.abortController = null;
304
+ }
305
+ this.messageEndpoint = null;
306
+ this.reconnectAttempts = 0;
307
+ this.emit('close');
308
+ }
309
+ /**
310
+ * Check if the transport is connected.
311
+ */
312
+ isConnected() {
313
+ return this.connected;
314
+ }
315
+ }
316
+ //# sourceMappingURL=sse-transport.js.map
@@ -0,0 +1,43 @@
1
+ import type { Readable, Writable } from 'stream';
2
+ import type { JSONRPCMessage } from './types.js';
3
+ import { BaseTransport, type BaseTransportConfig } from './base-transport.js';
4
+ /**
5
+ * Configuration for StdioTransport.
6
+ */
7
+ export interface StdioTransportConfig extends BaseTransportConfig {
8
+ /** Maximum message size in bytes (default: 10MB) */
9
+ maxMessageSize?: number;
10
+ /** Maximum buffer size in bytes (default: 20MB) */
11
+ maxBufferSize?: number;
12
+ /** Maximum header size in bytes (default: 8KB) */
13
+ maxHeaderSize?: number;
14
+ /** Use newline-delimited JSON instead of Content-Length framing */
15
+ useNewlineDelimited?: boolean;
16
+ }
17
+ /**
18
+ * StdioTransport handles JSON-RPC message framing over stdio streams.
19
+ * Uses Content-Length header protocol for message boundaries.
20
+ *
21
+ * Adapted from Overwatch's MCPTransport for client-side usage.
22
+ */
23
+ export declare class StdioTransport extends BaseTransport {
24
+ private input;
25
+ private output;
26
+ private buffer;
27
+ private contentLength;
28
+ private readonly maxMessageSize;
29
+ private readonly maxBufferSize;
30
+ private readonly maxHeaderSize;
31
+ private readonly useNewlineDelimited;
32
+ private connected;
33
+ constructor(input: Readable, output: Writable, config?: StdioTransportConfig);
34
+ /**
35
+ * Check if the transport is connected.
36
+ */
37
+ isConnected(): boolean;
38
+ private setupInputHandler;
39
+ private processBuffer;
40
+ send(message: JSONRPCMessage): void;
41
+ close(): void;
42
+ }
43
+ //# sourceMappingURL=stdio-transport.d.ts.map
@@ -0,0 +1,238 @@
1
+ import { BaseTransport } from './base-transport.js';
2
+ import { DISPLAY_LIMITS } from '../constants.js';
3
+ const DEFAULT_MAX_MESSAGE_SIZE = 10 * 1024 * 1024; // 10MB
4
+ const DEFAULT_MAX_BUFFER_SIZE = 20 * 1024 * 1024; // 20MB
5
+ const DEFAULT_MAX_HEADER_SIZE = 8 * 1024; // 8KB
6
+ /**
7
+ * StdioTransport handles JSON-RPC message framing over stdio streams.
8
+ * Uses Content-Length header protocol for message boundaries.
9
+ *
10
+ * Adapted from Overwatch's MCPTransport for client-side usage.
11
+ */
12
+ export class StdioTransport extends BaseTransport {
13
+ input;
14
+ output;
15
+ buffer = '';
16
+ contentLength = null;
17
+ maxMessageSize;
18
+ maxBufferSize;
19
+ maxHeaderSize;
20
+ useNewlineDelimited;
21
+ connected = true;
22
+ constructor(input, output, config) {
23
+ super(config);
24
+ this.input = input;
25
+ this.output = output;
26
+ this.maxMessageSize = config?.maxMessageSize ?? DEFAULT_MAX_MESSAGE_SIZE;
27
+ this.maxBufferSize = config?.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;
28
+ this.maxHeaderSize = config?.maxHeaderSize ?? DEFAULT_MAX_HEADER_SIZE;
29
+ this.useNewlineDelimited = config?.useNewlineDelimited ?? true; // Default to newline-delimited
30
+ this.setupInputHandler();
31
+ }
32
+ /**
33
+ * Check if the transport is connected.
34
+ */
35
+ isConnected() {
36
+ return this.connected;
37
+ }
38
+ setupInputHandler() {
39
+ this.input.on('data', (chunk) => {
40
+ if (this.debug) {
41
+ this.logger.debug({ preview: chunk.toString('utf-8').substring(0, DISPLAY_LIMITS.TRANSPORT_INPUT_PREVIEW) }, 'Raw input received');
42
+ }
43
+ const newSize = this.buffer.length + chunk.length;
44
+ if (newSize > this.maxBufferSize) {
45
+ this.emit('error', new Error(`Buffer size limit exceeded: ${newSize} > ${this.maxBufferSize} bytes.`));
46
+ this.buffer = '';
47
+ this.contentLength = null;
48
+ return;
49
+ }
50
+ this.buffer += chunk.toString('utf-8');
51
+ this.processBuffer();
52
+ });
53
+ this.input.on('end', () => {
54
+ this.emit('close');
55
+ });
56
+ this.input.on('error', (error) => {
57
+ this.emit('error', error);
58
+ });
59
+ }
60
+ processBuffer() {
61
+ // eslint-disable-next-line no-constant-condition
62
+ while (true) {
63
+ if (this.contentLength === null) {
64
+ // First, try to find Content-Length header
65
+ const headerEnd = this.buffer.indexOf('\r\n\r\n');
66
+ // If no header found, try newline-delimited JSON
67
+ if (headerEnd === -1) {
68
+ // Check if we have a complete line (newline-delimited JSON)
69
+ const newlineIndex = this.buffer.indexOf('\n');
70
+ if (newlineIndex === -1) {
71
+ // No complete message yet
72
+ if (this.buffer.length > this.maxHeaderSize) {
73
+ this.emit('error', new Error(`Buffer size limit exceeded without finding message boundary`));
74
+ this.buffer = '';
75
+ }
76
+ return;
77
+ }
78
+ // Process newline-delimited JSON
79
+ const line = this.buffer.substring(0, newlineIndex).trim();
80
+ this.buffer = this.buffer.substring(newlineIndex + 1);
81
+ if (line.length > this.maxMessageSize) {
82
+ this.emit('error', new Error(`Message size limit exceeded: ${line.length} > ${this.maxMessageSize} bytes.`));
83
+ continue;
84
+ }
85
+ if (line) {
86
+ try {
87
+ const message = JSON.parse(line);
88
+ this.emit('message', message);
89
+ }
90
+ catch (error) {
91
+ // Emit error for invalid JSON for consistent behavior with Content-Length mode
92
+ this.emit('error', new Error(`Invalid JSON in newline-delimited message: ${error instanceof Error ? error.message : String(error)}`));
93
+ }
94
+ }
95
+ continue;
96
+ }
97
+ const header = this.buffer.substring(0, headerEnd);
98
+ const match = header.match(/Content-Length: (\d+)/i);
99
+ if (!match) {
100
+ // Header found but no Content-Length, try as newline-delimited
101
+ const newlineIndex = this.buffer.indexOf('\n');
102
+ if (newlineIndex === -1)
103
+ return;
104
+ const line = this.buffer.substring(0, newlineIndex).trim();
105
+ this.buffer = this.buffer.substring(newlineIndex + 1);
106
+ if (line.length > this.maxMessageSize) {
107
+ this.emit('error', new Error(`Message size limit exceeded: ${line.length} > ${this.maxMessageSize} bytes.`));
108
+ continue;
109
+ }
110
+ if (line) {
111
+ try {
112
+ const message = JSON.parse(line);
113
+ this.emit('message', message);
114
+ }
115
+ catch (error) {
116
+ // Emit error for invalid JSON for consistent behavior with Content-Length mode
117
+ this.emit('error', new Error(`Invalid JSON in newline-delimited message: ${error instanceof Error ? error.message : String(error)}`));
118
+ }
119
+ }
120
+ continue;
121
+ }
122
+ const contentLength = parseInt(match[1], 10);
123
+ if (!Number.isFinite(contentLength) || contentLength < 0) {
124
+ this.emit('error', new Error(`Invalid Content-Length: ${match[1]}. Must be a positive integer.`));
125
+ this.buffer = this.buffer.substring(headerEnd + 4);
126
+ continue;
127
+ }
128
+ if (contentLength > this.maxMessageSize) {
129
+ this.emit('error', new Error(`Content-Length ${contentLength} exceeds maximum allowed size of ${this.maxMessageSize} bytes.`));
130
+ this.buffer = this.buffer.substring(headerEnd + 4);
131
+ if (this.buffer.length >= contentLength) {
132
+ this.buffer = this.buffer.substring(contentLength);
133
+ }
134
+ else {
135
+ this.buffer = '';
136
+ }
137
+ continue;
138
+ }
139
+ this.contentLength = contentLength;
140
+ this.buffer = this.buffer.substring(headerEnd + 4);
141
+ }
142
+ if (this.buffer.length < this.contentLength)
143
+ return;
144
+ const content = this.buffer.substring(0, this.contentLength);
145
+ this.buffer = this.buffer.substring(this.contentLength);
146
+ this.contentLength = null;
147
+ try {
148
+ const message = JSON.parse(content);
149
+ this.emit('message', message);
150
+ }
151
+ catch (e) {
152
+ this.emit('error', new Error(`Invalid JSON: ${e}`));
153
+ }
154
+ }
155
+ }
156
+ send(message) {
157
+ const content = JSON.stringify(message);
158
+ const writeData = (data) => {
159
+ try {
160
+ const success = this.output.write(data);
161
+ if (!success) {
162
+ // Handle backpressure - wait for drain event
163
+ this.output.once('drain', () => {
164
+ this.logger.debug('Output stream drained');
165
+ });
166
+ }
167
+ }
168
+ catch (error) {
169
+ // Emit error for broken pipe (EPIPE) or other write failures
170
+ this.emit('error', new Error(`Failed to write to output stream: ${error instanceof Error ? error.message : String(error)}`));
171
+ }
172
+ };
173
+ if (this.useNewlineDelimited) {
174
+ // Newline-delimited JSON format
175
+ if (this.debug) {
176
+ this.logger.debug({ format: 'newline', content }, 'Sending message');
177
+ }
178
+ writeData(content + '\n');
179
+ }
180
+ else {
181
+ // Content-Length framing
182
+ const header = `Content-Length: ${Buffer.byteLength(content)}\r\n\r\n`;
183
+ if (this.debug) {
184
+ this.logger.debug({ format: 'content-length', content: header + content }, 'Sending message');
185
+ }
186
+ writeData(header + content);
187
+ }
188
+ }
189
+ close() {
190
+ if (!this.connected) {
191
+ return; // Already closed
192
+ }
193
+ this.connected = false;
194
+ this.buffer = '';
195
+ this.contentLength = null;
196
+ // Remove listeners first to prevent further event processing
197
+ this.input.removeAllListeners();
198
+ // Destroy input stream to release file descriptor
199
+ // Use try-catch in case stream is already destroyed or in bad state
200
+ try {
201
+ if (!this.input.destroyed) {
202
+ this.input.destroy();
203
+ this.logger.debug('Input stream destroyed');
204
+ }
205
+ }
206
+ catch (error) {
207
+ this.logger.warn({ error: error instanceof Error ? error.message : String(error) }, 'Error destroying input stream');
208
+ }
209
+ // End output stream gracefully, then destroy after timeout
210
+ try {
211
+ if (!this.output.destroyed && this.output.writable) {
212
+ // Give output stream time to flush
213
+ const destroyTimeout = setTimeout(() => {
214
+ try {
215
+ if (!this.output.destroyed) {
216
+ this.output.destroy();
217
+ this.logger.debug('Output stream destroyed after timeout');
218
+ }
219
+ }
220
+ catch {
221
+ // Ignore errors during forced cleanup
222
+ }
223
+ }, 1000);
224
+ this.output.end(() => {
225
+ clearTimeout(destroyTimeout);
226
+ this.logger.debug('Output stream ended gracefully');
227
+ });
228
+ }
229
+ }
230
+ catch (error) {
231
+ this.logger.warn({ error: error instanceof Error ? error.message : String(error) }, 'Error ending output stream');
232
+ }
233
+ // Emit close before removing listeners so handlers can react
234
+ this.emit('close');
235
+ this.removeAllListeners();
236
+ }
237
+ }
238
+ //# sourceMappingURL=stdio-transport.js.map