@oddessentials/agent-guild 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/bin/agent-guild-report.mjs +134 -0
  4. package/bin/agent-guild.mjs +240 -0
  5. package/config/providers.default.json +124 -0
  6. package/examples/claude-code-settings.json +48 -0
  7. package/examples/codex-hooks.json +45 -0
  8. package/examples/gemini-settings.json +61 -0
  9. package/examples/grok-hooks.json +55 -0
  10. package/examples/providers.json +27 -0
  11. package/node_modules/@xterm/addon-fit/LICENSE +19 -0
  12. package/node_modules/@xterm/addon-fit/README.md +24 -0
  13. package/node_modules/@xterm/addon-fit/lib/addon-fit.js +2 -0
  14. package/node_modules/@xterm/addon-fit/lib/addon-fit.js.map +1 -0
  15. package/node_modules/@xterm/addon-fit/lib/addon-fit.mjs +18 -0
  16. package/node_modules/@xterm/addon-fit/lib/addon-fit.mjs.map +7 -0
  17. package/node_modules/@xterm/addon-fit/package.json +26 -0
  18. package/node_modules/@xterm/addon-fit/src/FitAddon.ts +92 -0
  19. package/node_modules/@xterm/addon-fit/typings/addon-fit.d.ts +55 -0
  20. package/node_modules/@xterm/addon-serialize/README.md +42 -0
  21. package/node_modules/@xterm/addon-serialize/lib/addon-serialize.js +2 -0
  22. package/node_modules/@xterm/addon-serialize/lib/addon-serialize.js.map +1 -0
  23. package/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs +19 -0
  24. package/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs.map +7 -0
  25. package/node_modules/@xterm/addon-serialize/package.json +29 -0
  26. package/node_modules/@xterm/addon-serialize/src/SerializeAddon.ts +695 -0
  27. package/node_modules/@xterm/addon-serialize/typings/addon-serialize.d.ts +116 -0
  28. package/node_modules/@xterm/addon-web-links/LICENSE +19 -0
  29. package/node_modules/@xterm/addon-web-links/README.md +21 -0
  30. package/node_modules/@xterm/addon-web-links/lib/addon-web-links.js +2 -0
  31. package/node_modules/@xterm/addon-web-links/lib/addon-web-links.js.map +1 -0
  32. package/node_modules/@xterm/addon-web-links/lib/addon-web-links.mjs +18 -0
  33. package/node_modules/@xterm/addon-web-links/lib/addon-web-links.mjs.map +7 -0
  34. package/node_modules/@xterm/addon-web-links/package.json +26 -0
  35. package/node_modules/@xterm/addon-web-links/src/WebLinkProvider.ts +199 -0
  36. package/node_modules/@xterm/addon-web-links/src/WebLinksAddon.ts +58 -0
  37. package/node_modules/@xterm/addon-web-links/typings/addon-web-links.d.ts +57 -0
  38. package/node_modules/@xterm/headless/README.md +31 -0
  39. package/node_modules/@xterm/headless/lib-headless/xterm-headless.js +2 -0
  40. package/node_modules/@xterm/headless/lib-headless/xterm-headless.js.map +1 -0
  41. package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs +46 -0
  42. package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs.map +7 -0
  43. package/node_modules/@xterm/headless/package.json +29 -0
  44. package/node_modules/@xterm/headless/typings/xterm-headless.d.ts +1385 -0
  45. package/node_modules/@xterm/xterm/LICENSE +21 -0
  46. package/node_modules/@xterm/xterm/README.md +243 -0
  47. package/node_modules/@xterm/xterm/css/xterm.css +285 -0
  48. package/node_modules/@xterm/xterm/lib/xterm.js +2 -0
  49. package/node_modules/@xterm/xterm/lib/xterm.js.map +1 -0
  50. package/node_modules/@xterm/xterm/lib/xterm.mjs +53 -0
  51. package/node_modules/@xterm/xterm/lib/xterm.mjs.map +7 -0
  52. package/node_modules/@xterm/xterm/package.json +111 -0
  53. package/node_modules/@xterm/xterm/src/browser/AccessibilityManager.ts +435 -0
  54. package/node_modules/@xterm/xterm/src/browser/Clipboard.ts +93 -0
  55. package/node_modules/@xterm/xterm/src/browser/ColorContrastCache.ts +34 -0
  56. package/node_modules/@xterm/xterm/src/browser/CoreBrowserTerminal.ts +1339 -0
  57. package/node_modules/@xterm/xterm/src/browser/Linkifier.ts +403 -0
  58. package/node_modules/@xterm/xterm/src/browser/LocalizableStrings.ts +23 -0
  59. package/node_modules/@xterm/xterm/src/browser/OscLinkProvider.ts +129 -0
  60. package/node_modules/@xterm/xterm/src/browser/RenderDebouncer.ts +84 -0
  61. package/node_modules/@xterm/xterm/src/browser/TimeBasedDebouncer.ts +86 -0
  62. package/node_modules/@xterm/xterm/src/browser/Types.ts +226 -0
  63. package/node_modules/@xterm/xterm/src/browser/Viewport.ts +192 -0
  64. package/node_modules/@xterm/xterm/src/browser/decorations/BufferDecorationRenderer.ts +139 -0
  65. package/node_modules/@xterm/xterm/src/browser/decorations/ColorZoneStore.ts +117 -0
  66. package/node_modules/@xterm/xterm/src/browser/decorations/OverviewRulerRenderer.ts +214 -0
  67. package/node_modules/@xterm/xterm/src/browser/input/CompositionHelper.ts +248 -0
  68. package/node_modules/@xterm/xterm/src/browser/input/Mouse.ts +54 -0
  69. package/node_modules/@xterm/xterm/src/browser/input/MoveToCell.ts +251 -0
  70. package/node_modules/@xterm/xterm/src/browser/public/Terminal.ts +275 -0
  71. package/node_modules/@xterm/xterm/src/browser/renderer/dom/DomRenderer.ts +542 -0
  72. package/node_modules/@xterm/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts +546 -0
  73. package/node_modules/@xterm/xterm/src/browser/renderer/dom/WidthCache.ts +167 -0
  74. package/node_modules/@xterm/xterm/src/browser/renderer/shared/Constants.ts +6 -0
  75. package/node_modules/@xterm/xterm/src/browser/renderer/shared/RendererUtils.ts +95 -0
  76. package/node_modules/@xterm/xterm/src/browser/renderer/shared/SelectionRenderModel.ts +93 -0
  77. package/node_modules/@xterm/xterm/src/browser/renderer/shared/Types.ts +84 -0
  78. package/node_modules/@xterm/xterm/src/browser/selection/SelectionModel.ts +144 -0
  79. package/node_modules/@xterm/xterm/src/browser/selection/Types.ts +15 -0
  80. package/node_modules/@xterm/xterm/src/browser/services/CharSizeService.ts +127 -0
  81. package/node_modules/@xterm/xterm/src/browser/services/CharacterJoinerService.ts +339 -0
  82. package/node_modules/@xterm/xterm/src/browser/services/CoreBrowserService.ts +137 -0
  83. package/node_modules/@xterm/xterm/src/browser/services/LinkProviderService.ts +28 -0
  84. package/node_modules/@xterm/xterm/src/browser/services/MouseService.ts +46 -0
  85. package/node_modules/@xterm/xterm/src/browser/services/RenderService.ts +376 -0
  86. package/node_modules/@xterm/xterm/src/browser/services/SelectionService.ts +1039 -0
  87. package/node_modules/@xterm/xterm/src/browser/services/Services.ts +158 -0
  88. package/node_modules/@xterm/xterm/src/browser/services/ThemeService.ts +198 -0
  89. package/node_modules/@xterm/xterm/src/browser/shared/Constants.ts +8 -0
  90. package/node_modules/@xterm/xterm/src/common/CircularList.ts +241 -0
  91. package/node_modules/@xterm/xterm/src/common/Clone.ts +23 -0
  92. package/node_modules/@xterm/xterm/src/common/Color.ts +376 -0
  93. package/node_modules/@xterm/xterm/src/common/CoreTerminal.ts +283 -0
  94. package/node_modules/@xterm/xterm/src/common/InputHandler.ts +3495 -0
  95. package/node_modules/@xterm/xterm/src/common/MultiKeyMap.ts +42 -0
  96. package/node_modules/@xterm/xterm/src/common/Platform.ts +44 -0
  97. package/node_modules/@xterm/xterm/src/common/SortedList.ts +194 -0
  98. package/node_modules/@xterm/xterm/src/common/TaskQueue.ts +166 -0
  99. package/node_modules/@xterm/xterm/src/common/TypedArrayUtils.ts +17 -0
  100. package/node_modules/@xterm/xterm/src/common/Types.ts +552 -0
  101. package/node_modules/@xterm/xterm/src/common/WindowsMode.ts +27 -0
  102. package/node_modules/@xterm/xterm/src/common/buffer/AttributeData.ts +211 -0
  103. package/node_modules/@xterm/xterm/src/common/buffer/Buffer.ts +662 -0
  104. package/node_modules/@xterm/xterm/src/common/buffer/BufferLine.ts +551 -0
  105. package/node_modules/@xterm/xterm/src/common/buffer/BufferRange.ts +13 -0
  106. package/node_modules/@xterm/xterm/src/common/buffer/BufferReflow.ts +226 -0
  107. package/node_modules/@xterm/xterm/src/common/buffer/BufferSet.ts +134 -0
  108. package/node_modules/@xterm/xterm/src/common/buffer/CellData.ts +94 -0
  109. package/node_modules/@xterm/xterm/src/common/buffer/Constants.ts +157 -0
  110. package/node_modules/@xterm/xterm/src/common/buffer/Marker.ts +43 -0
  111. package/node_modules/@xterm/xterm/src/common/buffer/Types.ts +52 -0
  112. package/node_modules/@xterm/xterm/src/common/data/Charsets.ts +256 -0
  113. package/node_modules/@xterm/xterm/src/common/data/EscapeSequences.ts +153 -0
  114. package/node_modules/@xterm/xterm/src/common/input/Keyboard.ts +373 -0
  115. package/node_modules/@xterm/xterm/src/common/input/TextDecoder.ts +346 -0
  116. package/node_modules/@xterm/xterm/src/common/input/UnicodeV6.ts +145 -0
  117. package/node_modules/@xterm/xterm/src/common/input/WriteBuffer.ts +247 -0
  118. package/node_modules/@xterm/xterm/src/common/input/XParseColor.ts +80 -0
  119. package/node_modules/@xterm/xterm/src/common/parser/Constants.ts +58 -0
  120. package/node_modules/@xterm/xterm/src/common/parser/DcsParser.ts +192 -0
  121. package/node_modules/@xterm/xterm/src/common/parser/EscapeSequenceParser.ts +792 -0
  122. package/node_modules/@xterm/xterm/src/common/parser/OscParser.ts +238 -0
  123. package/node_modules/@xterm/xterm/src/common/parser/Params.ts +229 -0
  124. package/node_modules/@xterm/xterm/src/common/parser/Types.ts +275 -0
  125. package/node_modules/@xterm/xterm/src/common/public/AddonManager.ts +53 -0
  126. package/node_modules/@xterm/xterm/src/common/public/BufferApiView.ts +35 -0
  127. package/node_modules/@xterm/xterm/src/common/public/BufferLineApiView.ts +29 -0
  128. package/node_modules/@xterm/xterm/src/common/public/BufferNamespaceApi.ts +36 -0
  129. package/node_modules/@xterm/xterm/src/common/public/ParserApi.ts +37 -0
  130. package/node_modules/@xterm/xterm/src/common/public/UnicodeApi.ts +27 -0
  131. package/node_modules/@xterm/xterm/src/common/services/BufferService.ts +154 -0
  132. package/node_modules/@xterm/xterm/src/common/services/CharsetService.ts +34 -0
  133. package/node_modules/@xterm/xterm/src/common/services/CoreMouseService.ts +365 -0
  134. package/node_modules/@xterm/xterm/src/common/services/CoreService.ts +92 -0
  135. package/node_modules/@xterm/xterm/src/common/services/DecorationService.ts +140 -0
  136. package/node_modules/@xterm/xterm/src/common/services/InstantiationService.ts +85 -0
  137. package/node_modules/@xterm/xterm/src/common/services/LogService.ts +124 -0
  138. package/node_modules/@xterm/xterm/src/common/services/OptionsService.ts +212 -0
  139. package/node_modules/@xterm/xterm/src/common/services/OscLinkService.ts +115 -0
  140. package/node_modules/@xterm/xterm/src/common/services/ServiceRegistry.ts +49 -0
  141. package/node_modules/@xterm/xterm/src/common/services/Services.ts +396 -0
  142. package/node_modules/@xterm/xterm/src/common/services/UnicodeService.ts +111 -0
  143. package/node_modules/@xterm/xterm/src/vs/base/browser/browser.ts +141 -0
  144. package/node_modules/@xterm/xterm/src/vs/base/browser/canIUse.ts +49 -0
  145. package/node_modules/@xterm/xterm/src/vs/base/browser/dom.ts +2369 -0
  146. package/node_modules/@xterm/xterm/src/vs/base/browser/fastDomNode.ts +316 -0
  147. package/node_modules/@xterm/xterm/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
  148. package/node_modules/@xterm/xterm/src/vs/base/browser/iframe.ts +135 -0
  149. package/node_modules/@xterm/xterm/src/vs/base/browser/keyboardEvent.ts +213 -0
  150. package/node_modules/@xterm/xterm/src/vs/base/browser/mouseEvent.ts +229 -0
  151. package/node_modules/@xterm/xterm/src/vs/base/browser/touch.ts +372 -0
  152. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
  153. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
  154. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +720 -0
  155. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
  156. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
  157. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
  158. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
  159. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
  160. package/node_modules/@xterm/xterm/src/vs/base/browser/ui/widget.ts +57 -0
  161. package/node_modules/@xterm/xterm/src/vs/base/browser/window.ts +14 -0
  162. package/node_modules/@xterm/xterm/src/vs/base/common/arrays.ts +887 -0
  163. package/node_modules/@xterm/xterm/src/vs/base/common/arraysFind.ts +202 -0
  164. package/node_modules/@xterm/xterm/src/vs/base/common/assert.ts +71 -0
  165. package/node_modules/@xterm/xterm/src/vs/base/common/async.ts +1992 -0
  166. package/node_modules/@xterm/xterm/src/vs/base/common/cancellation.ts +148 -0
  167. package/node_modules/@xterm/xterm/src/vs/base/common/charCode.ts +450 -0
  168. package/node_modules/@xterm/xterm/src/vs/base/common/collections.ts +140 -0
  169. package/node_modules/@xterm/xterm/src/vs/base/common/decorators.ts +130 -0
  170. package/node_modules/@xterm/xterm/src/vs/base/common/equals.ts +146 -0
  171. package/node_modules/@xterm/xterm/src/vs/base/common/errors.ts +303 -0
  172. package/node_modules/@xterm/xterm/src/vs/base/common/event.ts +1778 -0
  173. package/node_modules/@xterm/xterm/src/vs/base/common/functional.ts +32 -0
  174. package/node_modules/@xterm/xterm/src/vs/base/common/hash.ts +316 -0
  175. package/node_modules/@xterm/xterm/src/vs/base/common/iterator.ts +159 -0
  176. package/node_modules/@xterm/xterm/src/vs/base/common/keyCodes.ts +526 -0
  177. package/node_modules/@xterm/xterm/src/vs/base/common/keybindings.ts +284 -0
  178. package/node_modules/@xterm/xterm/src/vs/base/common/lazy.ts +47 -0
  179. package/node_modules/@xterm/xterm/src/vs/base/common/lifecycle.ts +801 -0
  180. package/node_modules/@xterm/xterm/src/vs/base/common/linkedList.ts +142 -0
  181. package/node_modules/@xterm/xterm/src/vs/base/common/map.ts +202 -0
  182. package/node_modules/@xterm/xterm/src/vs/base/common/numbers.ts +98 -0
  183. package/node_modules/@xterm/xterm/src/vs/base/common/observable.ts +76 -0
  184. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/api.ts +31 -0
  185. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/autorun.ts +281 -0
  186. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/base.ts +489 -0
  187. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/debugName.ts +145 -0
  188. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/derived.ts +428 -0
  189. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
  190. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/logging.ts +328 -0
  191. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/promise.ts +209 -0
  192. package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/utils.ts +610 -0
  193. package/node_modules/@xterm/xterm/src/vs/base/common/platform.ts +281 -0
  194. package/node_modules/@xterm/xterm/src/vs/base/common/scrollable.ts +522 -0
  195. package/node_modules/@xterm/xterm/src/vs/base/common/sequence.ts +34 -0
  196. package/node_modules/@xterm/xterm/src/vs/base/common/stopwatch.ts +43 -0
  197. package/node_modules/@xterm/xterm/src/vs/base/common/strings.ts +557 -0
  198. package/node_modules/@xterm/xterm/src/vs/base/common/symbols.ts +9 -0
  199. package/node_modules/@xterm/xterm/src/vs/base/common/uint.ts +59 -0
  200. package/node_modules/@xterm/xterm/src/vs/patches/nls.ts +90 -0
  201. package/node_modules/@xterm/xterm/src/vs/typings/base-common.d.ts +20 -0
  202. package/node_modules/@xterm/xterm/src/vs/typings/require.d.ts +42 -0
  203. package/node_modules/@xterm/xterm/src/vs/typings/vscode-globals-nls.d.ts +36 -0
  204. package/node_modules/@xterm/xterm/src/vs/typings/vscode-globals-product.d.ts +33 -0
  205. package/node_modules/@xterm/xterm/typings/xterm.d.ts +1957 -0
  206. package/node_modules/node-addon-api/LICENSE.md +9 -0
  207. package/node_modules/node-addon-api/README.md +319 -0
  208. package/node_modules/node-addon-api/common.gypi +20 -0
  209. package/node_modules/node-addon-api/except.gypi +25 -0
  210. package/node_modules/node-addon-api/index.js +12 -0
  211. package/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  212. package/node_modules/node-addon-api/napi-inl.h +6607 -0
  213. package/node_modules/node-addon-api/napi.h +3201 -0
  214. package/node_modules/node-addon-api/node_addon_api.gyp +32 -0
  215. package/node_modules/node-addon-api/node_api.gyp +9 -0
  216. package/node_modules/node-addon-api/noexcept.gypi +26 -0
  217. package/node_modules/node-addon-api/nothing.c +0 -0
  218. package/node_modules/node-addon-api/package-support.json +21 -0
  219. package/node_modules/node-addon-api/package.json +480 -0
  220. package/node_modules/node-addon-api/tools/README.md +73 -0
  221. package/node_modules/node-addon-api/tools/check-napi.js +99 -0
  222. package/node_modules/node-addon-api/tools/clang-format.js +71 -0
  223. package/node_modules/node-addon-api/tools/conversion.js +301 -0
  224. package/node_modules/node-addon-api/tools/eslint-format.js +79 -0
  225. package/node_modules/node-pty/LICENSE +69 -0
  226. package/node_modules/node-pty/README.md +164 -0
  227. package/node_modules/node-pty/binding.gyp +150 -0
  228. package/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
  229. package/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  230. package/node_modules/node-pty/lib/index.js +52 -0
  231. package/node_modules/node-pty/lib/interfaces.js +7 -0
  232. package/node_modules/node-pty/lib/shared/conout.js +10 -0
  233. package/node_modules/node-pty/lib/terminal.js +190 -0
  234. package/node_modules/node-pty/lib/types.js +7 -0
  235. package/node_modules/node-pty/lib/unixTerminal.js +351 -0
  236. package/node_modules/node-pty/lib/utils.js +38 -0
  237. package/node_modules/node-pty/lib/windowsConoutConnection.js +131 -0
  238. package/node_modules/node-pty/lib/windowsPtyAgent.js +344 -0
  239. package/node_modules/node-pty/lib/windowsTerminal.js +209 -0
  240. package/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  241. package/node_modules/node-pty/package.json +66 -0
  242. package/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  243. package/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  244. package/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  245. package/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  246. package/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
  247. package/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
  248. package/node_modules/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  249. package/node_modules/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  250. package/node_modules/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  251. package/node_modules/node-pty/prebuilds/win32-arm64/conpty.pdb +0 -0
  252. package/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  253. package/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
  254. package/node_modules/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  255. package/node_modules/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  256. package/node_modules/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  257. package/node_modules/node-pty/prebuilds/win32-x64/conpty.pdb +0 -0
  258. package/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  259. package/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
  260. package/node_modules/node-pty/scripts/post-install.js +76 -0
  261. package/node_modules/node-pty/scripts/prebuild.js +34 -0
  262. package/node_modules/node-pty/src/unix/pty.cc +876 -0
  263. package/node_modules/node-pty/src/unix/spawn-helper.cc +23 -0
  264. package/node_modules/node-pty/src/win/conpty.cc +616 -0
  265. package/node_modules/node-pty/src/win/conpty.h +41 -0
  266. package/node_modules/node-pty/src/win/conpty_console_list.cc +44 -0
  267. package/node_modules/node-pty/src/win/path_util.cc +95 -0
  268. package/node_modules/node-pty/src/win/path_util.h +26 -0
  269. package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-arm64/OpenConsole.exe +0 -0
  270. package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-arm64/conpty.dll +0 -0
  271. package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-x64/OpenConsole.exe +0 -0
  272. package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-x64/conpty.dll +0 -0
  273. package/node_modules/node-pty/typings/node-pty.d.ts +215 -0
  274. package/node_modules/ws/LICENSE +20 -0
  275. package/node_modules/ws/README.md +548 -0
  276. package/node_modules/ws/browser.js +8 -0
  277. package/node_modules/ws/index.js +22 -0
  278. package/node_modules/ws/lib/buffer-util.js +131 -0
  279. package/node_modules/ws/lib/constants.js +19 -0
  280. package/node_modules/ws/lib/event-target.js +292 -0
  281. package/node_modules/ws/lib/extension.js +203 -0
  282. package/node_modules/ws/lib/limiter.js +55 -0
  283. package/node_modules/ws/lib/permessage-deflate.js +530 -0
  284. package/node_modules/ws/lib/receiver.js +743 -0
  285. package/node_modules/ws/lib/sender.js +607 -0
  286. package/node_modules/ws/lib/stream.js +161 -0
  287. package/node_modules/ws/lib/subprotocol.js +62 -0
  288. package/node_modules/ws/lib/validation.js +152 -0
  289. package/node_modules/ws/lib/websocket-server.js +562 -0
  290. package/node_modules/ws/lib/websocket.js +1421 -0
  291. package/node_modules/ws/package.json +74 -0
  292. package/node_modules/ws/wrapper.mjs +21 -0
  293. package/package.json +52 -0
  294. package/src/manager/command-resolver.mjs +140 -0
  295. package/src/manager/config.mjs +94 -0
  296. package/src/manager/install-channels.mjs +268 -0
  297. package/src/manager/main.mjs +136 -0
  298. package/src/manager/providers.mjs +601 -0
  299. package/src/manager/report-shims.mjs +104 -0
  300. package/src/manager/server.mjs +421 -0
  301. package/src/manager/session-manager.mjs +306 -0
  302. package/src/manager/session.mjs +531 -0
  303. package/src/manager/shell-env.mjs +150 -0
  304. package/src/manager/usage.mjs +535 -0
  305. package/src/manager/versions.mjs +62 -0
  306. package/src/report/hooks.mjs +150 -0
  307. package/web/app.js +916 -0
  308. package/web/art/art.css +186 -0
  309. package/web/art/characters/anthropic/idle.png +0 -0
  310. package/web/art/characters/anthropic/locked.png +0 -0
  311. package/web/art/characters/anthropic/working.png +0 -0
  312. package/web/art/characters/google/idle.png +0 -0
  313. package/web/art/characters/google/locked.png +0 -0
  314. package/web/art/characters/google/working.png +0 -0
  315. package/web/art/characters/openai/idle.png +0 -0
  316. package/web/art/characters/openai/locked.png +0 -0
  317. package/web/art/characters/openai/working.png +0 -0
  318. package/web/art/characters/shell/idle.png +0 -0
  319. package/web/art/characters/shell/locked.png +0 -0
  320. package/web/art/characters/shell/working.png +0 -0
  321. package/web/art/characters/xai/idle.png +0 -0
  322. package/web/art/characters/xai/locked.png +0 -0
  323. package/web/art/characters/xai/working.png +0 -0
  324. package/web/art/familiars/aether.png +0 -0
  325. package/web/art/familiars/flame.png +0 -0
  326. package/web/art/familiars/leaf.png +0 -0
  327. package/web/art/familiars/night.png +0 -0
  328. package/web/art/icons/anthropic.png +0 -0
  329. package/web/art/icons/google.png +0 -0
  330. package/web/art/icons/openai.png +0 -0
  331. package/web/art/icons/shell.png +0 -0
  332. package/web/art/icons/xai.png +0 -0
  333. package/web/art/page.png +0 -0
  334. package/web/art/ui/frame.png +0 -0
  335. package/web/art/ui/gem-mana.png +0 -0
  336. package/web/art/ui/gem-vitality.png +0 -0
  337. package/web/art/ui/guild-crest.png +0 -0
  338. package/web/art/ui/level-medallion.png +0 -0
  339. package/web/art/ui/padlock.png +0 -0
  340. package/web/icons/README.md +8 -0
  341. package/web/index.html +154 -0
  342. package/web/styles.css +226 -0
@@ -0,0 +1,104 @@
1
+ // Launchers that make the bare `agent-guild-report` command work inside every
2
+ // session, without `npm link` or a global install.
3
+ //
4
+ // The coding tools run their hook commands through a shell (sh or Git Bash
5
+ // for Claude Code, a login shell for Codex CLI, bash or PowerShell for
6
+ // Gemini CLI, sh or PowerShell for Grok Build) and look the command up on
7
+ // the PATH they inherit from the terminal. The manager writes these shims
8
+ // into its data folder at every start and puts that folder first on each
9
+ // session's PATH. They run the reporter with the manager's own Node.js, and
10
+ // fall back to a `node` on the hook shell's PATH when that binary has moved
11
+ // since the manager started (a version manager switched versions).
12
+ //
13
+ // On Windows there is deliberately no .ps1 shim: PowerShell prefers a .ps1
14
+ // over the PATHEXT extensions, and the default Restricted execution policy
15
+ // refuses to run it. Gemini CLI and Grok Build run hooks in PowerShell without
16
+ // bypassing that policy, so they would fail; with only a .cmd present,
17
+ // PowerShell falls through to it and cmd.exe runs it with no policy involved.
18
+ // cmd.exe reads a batch file in the OEM code page, so no path may be written
19
+ // into the .cmd: the script's path lives in a loader module next to it, as
20
+ // an ASCII file: URL, and the Node.js path in AGENT_GUILD_NODE, which the
21
+ // manager sets in every session.
22
+
23
+ import fs from 'node:fs';
24
+ import path from 'node:path';
25
+
26
+ export const SHIM_NAME = 'agent-guild-report';
27
+ export const LOADER_NAME = 'agent-guild-report-loader.mjs';
28
+
29
+ /** Quote for double quotes in sh: backslash, double quote, dollar and backtick. */
30
+ function shQuote(value) {
31
+ return `"${value.replace(/[\\"$`]/g, (c) => `\\${c}`)}"`;
32
+ }
33
+
34
+ /** A file: URL with every segment percent-encoded, like url.pathToFileURL. */
35
+ export function fileUrl(file, platform = process.platform) {
36
+ const posix = platform === 'win32' ? `/${file.replace(/\\/g, '/')}` : file;
37
+ return `file://${posix.split('/').map((s) => encodeURIComponent(s).replace(/%3A/gi, ':')).join('/')}`;
38
+ }
39
+
40
+ /**
41
+ * The shim texts for one platform. `execPath` is the Node.js binary and
42
+ * `script` the absolute path of bin/agent-guild-report.mjs. Git Bash accepts
43
+ * forward slashes in Windows paths, and a backslash would be an escape in
44
+ * the sh shim, so the sh shim gets forward slashes on Windows.
45
+ */
46
+ export function shimContents({ execPath, script, platform = process.platform }) {
47
+ const forSh = (p) => (platform === 'win32' ? p.replace(/\\/g, '/') : p);
48
+ const files = {
49
+ [SHIM_NAME]: [
50
+ '#!/bin/sh',
51
+ `n=${shQuote(forSh(execPath))}`,
52
+ '[ -x "$n" ] || n=node',
53
+ `exec "$n" ${shQuote(forSh(script))} "$@"`,
54
+ '',
55
+ ].join('\n'),
56
+ };
57
+ if (platform === 'win32') {
58
+ files[LOADER_NAME] = `import ${JSON.stringify(fileUrl(script, platform))};\n`;
59
+ files[`${SHIM_NAME}.cmd`] = [
60
+ '@ECHO OFF',
61
+ 'IF EXIST "%AGENT_GUILD_NODE%" GOTO manager',
62
+ `node "%~dp0${LOADER_NAME}" %*`,
63
+ 'EXIT /B %ERRORLEVEL%',
64
+ ':manager',
65
+ `"%AGENT_GUILD_NODE%" "%~dp0${LOADER_NAME}" %*`,
66
+ '',
67
+ ].join('\r\n');
68
+ }
69
+ return files;
70
+ }
71
+
72
+ /**
73
+ * Write the shims into `dir` (created if needed), replacing stale ones
74
+ * atomically so a hook that is starting meanwhile still runs a whole file.
75
+ * Returns `dir`. Throws when the folder is not writable; the caller decides
76
+ * whether that is fatal.
77
+ */
78
+ export function writeReportShims({ dir, execPath = process.execPath, script, platform = process.platform }) {
79
+ fs.mkdirSync(dir, { recursive: true });
80
+ const files = shimContents({ execPath, script, platform });
81
+ for (const [name, contents] of Object.entries(files)) {
82
+ const file = path.join(dir, name);
83
+ const tmp = `${file}.${process.pid}.tmp`;
84
+ fs.writeFileSync(tmp, contents, { mode: 0o755 });
85
+ try { fs.chmodSync(tmp, 0o755); } catch { /* Windows */ }
86
+ fs.renameSync(tmp, file);
87
+ }
88
+ // A .ps1 from an earlier version would take precedence in PowerShell.
89
+ try { fs.unlinkSync(path.join(dir, `${SHIM_NAME}.ps1`)); } catch { /* none */ }
90
+ return dir;
91
+ }
92
+
93
+ /**
94
+ * `env` with `dir` first on PATH. Windows environments may spell the
95
+ * variable "Path"; whichever spelling exists is kept. Elsewhere names are
96
+ * case-sensitive and only PATH counts.
97
+ */
98
+ export function prependPath(env, dir, { platform = process.platform } = {}) {
99
+ if (!dir) return env;
100
+ const delimiter = platform === 'win32' ? ';' : ':';
101
+ const key = platform === 'win32' ? Object.keys(env).find((k) => k.toUpperCase() === 'PATH') || 'PATH' : 'PATH';
102
+ const current = env[key] ? String(env[key]).split(delimiter).filter((entry) => entry && entry !== dir) : [];
103
+ return { ...env, [key]: [dir, ...current].join(delimiter) };
104
+ }
@@ -0,0 +1,421 @@
1
+ // Local HTTP + WebSocket API. The web page is only one client of this API;
2
+ // see docs/api.md for the contract other front ends (e.g. Unreal Engine) use.
3
+
4
+ import http from 'node:http';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { createRequire } from 'node:module';
8
+ import { WebSocketServer } from 'ws';
9
+ import { timingSafeEqualString } from './session-manager.mjs';
10
+
11
+ const require = createRequire(import.meta.url);
12
+ const API = '/api/v1';
13
+ const MAX_BODY = 64 * 1024;
14
+ const MAX_WS_PAYLOAD = 1024 * 1024;
15
+ const SLOW_CLIENT_BYTES = 8 * 1024 * 1024;
16
+
17
+ const MIME = {
18
+ '.html': 'text/html; charset=utf-8',
19
+ '.js': 'text/javascript; charset=utf-8',
20
+ '.mjs': 'text/javascript; charset=utf-8',
21
+ '.css': 'text/css; charset=utf-8',
22
+ '.svg': 'image/svg+xml',
23
+ '.png': 'image/png',
24
+ '.ico': 'image/x-icon',
25
+ '.json': 'application/json; charset=utf-8',
26
+ };
27
+
28
+ function vendorFiles() {
29
+ const pkgDir = (name) => path.dirname(require.resolve(`${name}/package.json`));
30
+ return {
31
+ '/vendor/xterm/xterm.js': path.join(pkgDir('@xterm/xterm'), 'lib/xterm.js'),
32
+ '/vendor/xterm/xterm.css': path.join(pkgDir('@xterm/xterm'), 'css/xterm.css'),
33
+ '/vendor/xterm/addon-fit.js': path.join(pkgDir('@xterm/addon-fit'), 'lib/addon-fit.js'),
34
+ '/vendor/xterm/addon-web-links.js': path.join(pkgDir('@xterm/addon-web-links'), 'lib/addon-web-links.js'),
35
+ };
36
+ }
37
+
38
+ const SECURITY_HEADERS = {
39
+ 'X-Content-Type-Options': 'nosniff',
40
+ 'X-Frame-Options': 'DENY',
41
+ 'Referrer-Policy': 'no-referrer',
42
+ 'Cross-Origin-Resource-Policy': 'same-origin',
43
+ 'Content-Security-Policy':
44
+ "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; " +
45
+ "connect-src 'self' ws://127.0.0.1:* ws://localhost:*; frame-ancestors 'none'; base-uri 'none'; form-action 'none'",
46
+ };
47
+
48
+ class HttpError extends Error {
49
+ constructor(status, message, code) {
50
+ super(message);
51
+ this.status = status;
52
+ this.code = code;
53
+ }
54
+ }
55
+
56
+ function sendJson(res, status, body) {
57
+ const payload = JSON.stringify(body);
58
+ res.writeHead(status, {
59
+ ...SECURITY_HEADERS,
60
+ 'Content-Type': 'application/json; charset=utf-8',
61
+ 'Cache-Control': 'no-store',
62
+ 'Content-Length': Buffer.byteLength(payload),
63
+ });
64
+ res.end(payload);
65
+ }
66
+
67
+ function readJsonBody(req) {
68
+ return new Promise((resolve, reject) => {
69
+ let size = 0;
70
+ const chunks = [];
71
+ let tooLarge = false;
72
+ req.on('data', (chunk) => {
73
+ if (tooLarge) return; // drain the rest so the 413 response can be read
74
+ size += chunk.length;
75
+ if (size > MAX_BODY) {
76
+ tooLarge = true;
77
+ chunks.length = 0;
78
+ reject(new HttpError(413, 'request body too large', 'too_large'));
79
+ return;
80
+ }
81
+ chunks.push(chunk);
82
+ });
83
+ req.on('end', () => {
84
+ if (tooLarge) return;
85
+ if (chunks.length === 0) return resolve({});
86
+ try {
87
+ const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'));
88
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
89
+ reject(new HttpError(400, 'request body must be a JSON object', 'bad_json'));
90
+ } else resolve(parsed);
91
+ } catch {
92
+ reject(new HttpError(400, 'request body is not valid JSON', 'bad_json'));
93
+ }
94
+ });
95
+ req.on('error', reject);
96
+ });
97
+ }
98
+
99
+ export function createManagerServer({
100
+ manager,
101
+ registry,
102
+ usage,
103
+ token,
104
+ host = '127.0.0.1',
105
+ port = 0,
106
+ webDir,
107
+ version = '0.0.0',
108
+ extraOrigins = [],
109
+ onShutdownRequest = () => {},
110
+ }) {
111
+ const startedAt = new Date().toISOString();
112
+ const vendor = vendorFiles();
113
+ let boundPort = port;
114
+
115
+ const allowedHosts = () => new Set([`127.0.0.1:${boundPort}`, `localhost:${boundPort}`, `[::1]:${boundPort}`]);
116
+ const allowedOrigins = () =>
117
+ new Set([`http://127.0.0.1:${boundPort}`, `http://localhost:${boundPort}`, `http://[::1]:${boundPort}`, ...extraOrigins]);
118
+
119
+ /** Blocks DNS-rebinding (Host) and cross-site browser requests (Origin). */
120
+ function checkRequestSource(req) {
121
+ const hostHeader = String(req.headers.host || '').toLowerCase();
122
+ if (!allowedHosts().has(hostHeader)) throw new HttpError(403, 'forbidden host', 'forbidden_host');
123
+ const origin = req.headers.origin;
124
+ if (origin !== undefined && origin !== 'null' && !allowedOrigins().has(origin)) {
125
+ throw new HttpError(403, 'forbidden origin', 'forbidden_origin');
126
+ }
127
+ if (origin === 'null') throw new HttpError(403, 'forbidden origin', 'forbidden_origin');
128
+ }
129
+
130
+ function requestToken(req, url, { allowQuery = false } = {}) {
131
+ const auth = req.headers.authorization || '';
132
+ if (auth.startsWith('Bearer ')) return auth.slice(7).trim();
133
+ if (allowQuery) return url.searchParams.get('token');
134
+ return null;
135
+ }
136
+
137
+ function requireAuth(req, url, opts) {
138
+ if (!timingSafeEqualString(requestToken(req, url, opts), token)) {
139
+ throw new HttpError(401, 'missing or invalid API token', 'unauthorized');
140
+ }
141
+ }
142
+
143
+ function serveFile(res, file, { cache = false } = {}) {
144
+ fs.readFile(file, (err, data) => {
145
+ if (err) return sendJson(res, 404, { error: { code: 'not_found', message: 'not found' } });
146
+ res.writeHead(200, {
147
+ ...SECURITY_HEADERS,
148
+ 'Content-Type': MIME[path.extname(file).toLowerCase()] || 'application/octet-stream',
149
+ 'Cache-Control': cache ? 'public, max-age=3600' : 'no-cache',
150
+ 'Content-Length': data.length,
151
+ });
152
+ res.end(data);
153
+ });
154
+ }
155
+
156
+ function serveStatic(req, res, pathname) {
157
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
158
+ throw new HttpError(405, 'method not allowed', 'method_not_allowed');
159
+ }
160
+ if (vendor[pathname]) return serveFile(res, vendor[pathname], { cache: true });
161
+ let rel;
162
+ try {
163
+ rel = pathname === '/' ? 'index.html' : decodeURIComponent(pathname).replace(/^\/+/, '');
164
+ } catch {
165
+ throw new HttpError(400, 'malformed path', 'bad_path');
166
+ }
167
+ if (rel.includes('\0')) throw new HttpError(404, 'not found', 'not_found');
168
+ const file = path.resolve(webDir, rel);
169
+ if (file !== webDir && !file.startsWith(webDir + path.sep)) {
170
+ throw new HttpError(404, 'not found', 'not_found');
171
+ }
172
+ return serveFile(res, file);
173
+ }
174
+
175
+ async function handleApi(req, res, url) {
176
+ const route = url.pathname.slice(API.length) || '/';
177
+ const method = req.method;
178
+
179
+ if (route === '/health' && method === 'GET') {
180
+ return sendJson(res, 200, { ok: true, name: 'agent-guild', version, pid: process.pid });
181
+ }
182
+
183
+ // Agent and model reports may authenticate with the per-session report
184
+ // token that the manager injects into each tool's environment.
185
+ const reportMatch = route.match(/^\/sessions\/([a-f0-9]+)\/(agents|model)$/);
186
+ if (reportMatch && method === 'POST') {
187
+ const [, id, kind] = reportMatch;
188
+ const body = await readJsonBody(req);
189
+ const auth = {
190
+ trusted: timingSafeEqualString(requestToken(req, url), token),
191
+ reportToken: req.headers['x-agent-guild-report-token'],
192
+ };
193
+ if (kind === 'agents') return sendJson(res, 200, { agent: manager.reportAgent(id, body, auth) });
194
+ return sendJson(res, 200, { model: manager.reportModel(id, body, auth) });
195
+ }
196
+
197
+ requireAuth(req, url);
198
+
199
+ if (route === '/info' && method === 'GET') {
200
+ return sendJson(res, 200, {
201
+ name: 'agent-guild',
202
+ version,
203
+ pid: process.pid,
204
+ platform: process.platform,
205
+ startedAt,
206
+ warnings: registry.warnings,
207
+ });
208
+ }
209
+ if (route === '/providers' && method === 'GET') {
210
+ registry.refreshVersions().catch(() => {});
211
+ return sendJson(res, 200, { providers: registry.list() });
212
+ }
213
+ if (route === '/providers/reload' && method === 'POST') {
214
+ registry.reload();
215
+ registry.refreshVersions({ force: true }).catch(() => {});
216
+ return sendJson(res, 200, { providers: registry.list(), warnings: registry.warnings });
217
+ }
218
+ if (route === '/usage' && method === 'GET') {
219
+ return sendJson(res, 200, { usage: await usage.all() });
220
+ }
221
+ const installMatch = route.match(/^\/providers\/([a-z0-9][a-z0-9_-]{0,31})\/install$/);
222
+ if (installMatch && method === 'POST') {
223
+ const body = await readJsonBody(req);
224
+ const session = await manager.install(installMatch[1], { force: body.force === true });
225
+ return sendJson(res, 201, { session: session.toJSON() });
226
+ }
227
+ if (route === '/sessions' && method === 'GET') {
228
+ return sendJson(res, 200, { sessions: manager.list() });
229
+ }
230
+ if (route === '/sessions' && method === 'POST') {
231
+ const body = await readJsonBody(req);
232
+ const session = manager.create(body);
233
+ return sendJson(res, 201, { session: session.toJSON() });
234
+ }
235
+ if (route === '/shutdown' && method === 'POST') {
236
+ // Stopping the manager ends every session, so a client must say
237
+ // `force` while any is running. The same guard serves every front end.
238
+ const body = await readJsonBody(req);
239
+ const running = manager.runningCount();
240
+ if (running > 0 && body.force !== true) {
241
+ const err = new HttpError(409, `${running} session(s) are running; stopping the manager ends them`, 'sessions_running');
242
+ err.running = running;
243
+ throw err;
244
+ }
245
+ // Refuse new sessions from this moment, before the shutdown itself
246
+ // runs: a session accepted in between would be ended without warning.
247
+ manager.closing = true;
248
+ sendJson(res, 202, { ok: true, running });
249
+ // Tell every client first, so a second page shows "stopped" rather
250
+ // than "not reachable" when its socket drops.
251
+ broadcast({ type: 'manager.stopping', running });
252
+ setImmediate(onShutdownRequest);
253
+ return undefined;
254
+ }
255
+
256
+ const sessionMatch = route.match(/^\/sessions\/([a-f0-9]+)(\/stop)?$/);
257
+ if (sessionMatch) {
258
+ const [, id, action] = sessionMatch;
259
+ if (!action && method === 'GET') return sendJson(res, 200, { session: manager.get(id).toJSON() });
260
+ if (!action && method === 'PATCH') {
261
+ const body = await readJsonBody(req);
262
+ const session = manager.get(id);
263
+ if (typeof body.name !== 'string' || !body.name.trim()) {
264
+ throw new HttpError(400, 'name must be a non-empty string', 'bad_name');
265
+ }
266
+ session.rename(body.name);
267
+ return sendJson(res, 200, { session: session.toJSON() });
268
+ }
269
+ if (!action && method === 'DELETE') {
270
+ manager.remove(id);
271
+ return sendJson(res, 200, { ok: true });
272
+ }
273
+ if (action === '/stop' && method === 'POST') {
274
+ return sendJson(res, 200, { session: manager.stop(id).toJSON() });
275
+ }
276
+ }
277
+ throw new HttpError(404, `no route for ${method} ${url.pathname}`, 'not_found');
278
+ }
279
+
280
+ const server = http.createServer(async (req, res) => {
281
+ let url;
282
+ try {
283
+ url = new URL(req.url, 'http://localhost');
284
+ checkRequestSource(req);
285
+ if (url.pathname === API || url.pathname.startsWith(API + '/')) await handleApi(req, res, url);
286
+ else serveStatic(req, res, url.pathname);
287
+ } catch (err) {
288
+ const status = err.status || 500;
289
+ if (status >= 500) console.error('[server]', err);
290
+ if (!res.headersSent) {
291
+ const error = { code: err.code || 'error', message: err.message };
292
+ if (err.running !== undefined) error.running = err.running;
293
+ sendJson(res, status, { error });
294
+ }
295
+ }
296
+ });
297
+
298
+ // ---- WebSockets -------------------------------------------------------
299
+
300
+ const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_WS_PAYLOAD });
301
+ const eventClients = new Set();
302
+
303
+ const safeSend = (ws, message) => {
304
+ if (ws.readyState !== ws.OPEN) return;
305
+ if (ws.bufferedAmount > SLOW_CLIENT_BYTES) {
306
+ // The client can reconnect and will receive a fresh snapshot.
307
+ ws.close(4008, 'client too slow');
308
+ return;
309
+ }
310
+ ws.send(JSON.stringify(message));
311
+ };
312
+
313
+ const broadcast = (event) => {
314
+ for (const ws of eventClients) safeSend(ws, event);
315
+ };
316
+
317
+ /** Send a last event to every events client and wait, briefly, for it to leave. */
318
+ function farewell(event, { timeoutMs = 1000 } = {}) {
319
+ const payload = JSON.stringify(event);
320
+ const sends = [...eventClients]
321
+ .filter((ws) => ws.readyState === ws.OPEN)
322
+ .map((ws) => new Promise((resolve) => ws.send(payload, () => resolve())));
323
+ if (sends.length === 0) return Promise.resolve();
324
+ let timer;
325
+ return Promise.race([
326
+ Promise.all(sends),
327
+ new Promise((resolve) => { timer = setTimeout(resolve, timeoutMs); }),
328
+ ]).then(() => clearTimeout(timer));
329
+ }
330
+ manager.on('event', broadcast);
331
+ registry.on('updated', () => broadcast({ type: 'providers.updated', providers: registry.list() }));
332
+
333
+ function handleEvents(ws) {
334
+ eventClients.add(ws);
335
+ safeSend(ws, { type: 'hello', version, sessions: manager.list() });
336
+ ws.on('close', () => eventClients.delete(ws));
337
+ ws.on('message', () => { /* events socket is server -> client only */ });
338
+ }
339
+
340
+ function handleTerminal(ws, id) {
341
+ let session;
342
+ try {
343
+ session = manager.get(id);
344
+ } catch (err) {
345
+ safeSend(ws, { type: 'error', code: 'not_found', message: err.message });
346
+ ws.close(4404, 'session not found');
347
+ return;
348
+ }
349
+ const detach = session.attach((message) => {
350
+ safeSend(ws, message);
351
+ if (message.type === 'removed') ws.close(4410, 'session removed');
352
+ });
353
+ ws.on('close', detach);
354
+ ws.on('message', (raw, isBinary) => {
355
+ if (isBinary) return;
356
+ let msg;
357
+ try { msg = JSON.parse(raw.toString('utf8')); } catch { return; }
358
+ if (msg.type === 'input' && typeof msg.data === 'string') session.write(msg.data);
359
+ else if (msg.type === 'resize') session.resize(msg.cols, msg.rows);
360
+ });
361
+ }
362
+
363
+ server.on('upgrade', (req, socket, head) => {
364
+ const reject = (status, text) => {
365
+ socket.write(`HTTP/1.1 ${status} ${text}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`);
366
+ socket.destroy();
367
+ };
368
+ let url;
369
+ try {
370
+ url = new URL(req.url, 'http://localhost');
371
+ checkRequestSource(req);
372
+ requireAuth(req, url, { allowQuery: true });
373
+ } catch (err) {
374
+ return reject(err.status || 400, err.status === 401 ? 'Unauthorized' : 'Forbidden');
375
+ }
376
+ const termMatch = url.pathname.match(/^\/api\/v1\/sessions\/([a-f0-9]+)\/terminal$/);
377
+ if (url.pathname !== `${API}/events` && !termMatch) return reject(404, 'Not Found');
378
+ wss.handleUpgrade(req, socket, head, (ws) => {
379
+ ws.isAlive = true;
380
+ ws.on('pong', () => { ws.isAlive = true; });
381
+ ws.on('error', () => {});
382
+ if (termMatch) handleTerminal(ws, termMatch[1]);
383
+ else handleEvents(ws);
384
+ });
385
+ });
386
+
387
+ const heartbeat = setInterval(() => {
388
+ for (const ws of wss.clients) {
389
+ if (!ws.isAlive) { ws.terminate(); continue; }
390
+ ws.isAlive = false;
391
+ try { ws.ping(); } catch { /* closing */ }
392
+ }
393
+ }, 30000);
394
+ heartbeat.unref();
395
+
396
+ return {
397
+ server,
398
+ get port() { return boundPort; },
399
+ get url() { return `http://127.0.0.1:${boundPort}`; },
400
+ listen() {
401
+ return new Promise((resolve, reject) => {
402
+ server.once('error', reject);
403
+ server.listen(port, host, () => {
404
+ server.off('error', reject);
405
+ boundPort = server.address().port;
406
+ resolve(boundPort);
407
+ });
408
+ });
409
+ },
410
+ /** @param {{ notice?: object }} [opts] a final event for the events clients */
411
+ async close({ notice } = {}) {
412
+ if (notice) await farewell(notice);
413
+ clearInterval(heartbeat);
414
+ for (const ws of wss.clients) ws.terminate();
415
+ return new Promise((resolve) => {
416
+ server.close(() => resolve());
417
+ server.closeAllConnections?.();
418
+ });
419
+ },
420
+ };
421
+ }