@wingman-ai/gateway 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 (418) hide show
  1. package/.wingman/agents/README.md +161 -0
  2. package/.wingman/agents/coding/agent.md +147 -0
  3. package/.wingman/agents/coding/implementor.md +56 -0
  4. package/.wingman/agents/main/agent.md +19 -0
  5. package/.wingman/agents/researcher/agent.md +62 -0
  6. package/.wingman/agents/stock-trader/agent.md +223 -0
  7. package/.wingman/agents/stock-trader/chain-curator.md +24 -0
  8. package/.wingman/agents/stock-trader/goal-translator.md +42 -0
  9. package/.wingman/agents/stock-trader/guardrails-veto.md +11 -0
  10. package/.wingman/agents/stock-trader/path-planner.md +23 -0
  11. package/.wingman/agents/stock-trader/regime-analyst.md +15 -0
  12. package/.wingman/agents/stock-trader/risk.md +20 -0
  13. package/.wingman/agents/stock-trader/selection.md +22 -0
  14. package/.wingman/agents/stock-trader/strategy-composer.md +38 -0
  15. package/.wingman/agents/wingman/agent.json +12 -0
  16. package/bin/wingman +7 -0
  17. package/dist/agent/config/agentConfig.cjs +95 -0
  18. package/dist/agent/config/agentConfig.d.ts +187 -0
  19. package/dist/agent/config/agentConfig.js +52 -0
  20. package/dist/agent/config/agentLoader.cjs +242 -0
  21. package/dist/agent/config/agentLoader.d.ts +42 -0
  22. package/dist/agent/config/agentLoader.js +208 -0
  23. package/dist/agent/config/mcpClientManager.cjs +168 -0
  24. package/dist/agent/config/mcpClientManager.d.ts +41 -0
  25. package/dist/agent/config/mcpClientManager.js +134 -0
  26. package/dist/agent/config/modelFactory.cjs +175 -0
  27. package/dist/agent/config/modelFactory.d.ts +33 -0
  28. package/dist/agent/config/modelFactory.js +141 -0
  29. package/dist/agent/config/toolRegistry.cjs +111 -0
  30. package/dist/agent/config/toolRegistry.d.ts +25 -0
  31. package/dist/agent/config/toolRegistry.js +71 -0
  32. package/dist/agent/middleware/additional-messages.cjs +60 -0
  33. package/dist/agent/middleware/additional-messages.d.ts +7 -0
  34. package/dist/agent/middleware/additional-messages.js +26 -0
  35. package/dist/agent/middleware/hooks/executor.cjs +137 -0
  36. package/dist/agent/middleware/hooks/executor.d.ts +52 -0
  37. package/dist/agent/middleware/hooks/executor.js +103 -0
  38. package/dist/agent/middleware/hooks/input-builder.cjs +55 -0
  39. package/dist/agent/middleware/hooks/input-builder.d.ts +15 -0
  40. package/dist/agent/middleware/hooks/input-builder.js +21 -0
  41. package/dist/agent/middleware/hooks/matcher.cjs +59 -0
  42. package/dist/agent/middleware/hooks/matcher.d.ts +27 -0
  43. package/dist/agent/middleware/hooks/matcher.js +22 -0
  44. package/dist/agent/middleware/hooks/merger.cjs +54 -0
  45. package/dist/agent/middleware/hooks/merger.d.ts +18 -0
  46. package/dist/agent/middleware/hooks/merger.js +20 -0
  47. package/dist/agent/middleware/hooks/types.cjs +62 -0
  48. package/dist/agent/middleware/hooks/types.d.ts +82 -0
  49. package/dist/agent/middleware/hooks/types.js +19 -0
  50. package/dist/agent/middleware/hooks.cjs +79 -0
  51. package/dist/agent/middleware/hooks.d.ts +19 -0
  52. package/dist/agent/middleware/hooks.js +45 -0
  53. package/dist/agent/middleware/media-compat.cjs +80 -0
  54. package/dist/agent/middleware/media-compat.d.ts +7 -0
  55. package/dist/agent/middleware/media-compat.js +46 -0
  56. package/dist/agent/tests/agentConfig.test.cjs +132 -0
  57. package/dist/agent/tests/agentConfig.test.d.ts +1 -0
  58. package/dist/agent/tests/agentConfig.test.js +126 -0
  59. package/dist/agent/tests/agentLoader.test.cjs +235 -0
  60. package/dist/agent/tests/agentLoader.test.d.ts +1 -0
  61. package/dist/agent/tests/agentLoader.test.js +229 -0
  62. package/dist/agent/tests/modelFactory.test.cjs +114 -0
  63. package/dist/agent/tests/modelFactory.test.d.ts +1 -0
  64. package/dist/agent/tests/modelFactory.test.js +108 -0
  65. package/dist/agent/tests/test-agent-loader.cjs +33 -0
  66. package/dist/agent/tests/test-agent-loader.d.ts +1 -0
  67. package/dist/agent/tests/test-agent-loader.js +27 -0
  68. package/dist/agent/tests/test-subagent-loading.cjs +99 -0
  69. package/dist/agent/tests/test-subagent-loading.d.ts +1 -0
  70. package/dist/agent/tests/test-subagent-loading.js +93 -0
  71. package/dist/agent/tests/toolRegistry.test.cjs +109 -0
  72. package/dist/agent/tests/toolRegistry.test.d.ts +1 -0
  73. package/dist/agent/tests/toolRegistry.test.js +103 -0
  74. package/dist/agent/tools/code_search.cjs +108 -0
  75. package/dist/agent/tools/code_search.d.ts +24 -0
  76. package/dist/agent/tools/code_search.js +74 -0
  77. package/dist/agent/tools/command_execute.cjs +136 -0
  78. package/dist/agent/tools/command_execute.d.ts +12 -0
  79. package/dist/agent/tools/command_execute.js +99 -0
  80. package/dist/agent/tools/git_status.cjs +126 -0
  81. package/dist/agent/tools/git_status.d.ts +15 -0
  82. package/dist/agent/tools/git_status.js +92 -0
  83. package/dist/agent/tools/internet_search.cjs +93 -0
  84. package/dist/agent/tools/internet_search.d.ts +25 -0
  85. package/dist/agent/tools/internet_search.js +56 -0
  86. package/dist/agent/tools/think.cjs +53 -0
  87. package/dist/agent/tools/think.d.ts +26 -0
  88. package/dist/agent/tools/think.js +16 -0
  89. package/dist/agent/tools/web_crawler.cjs +180 -0
  90. package/dist/agent/tools/web_crawler.d.ts +31 -0
  91. package/dist/agent/tools/web_crawler.js +143 -0
  92. package/dist/agent/utils.cjs +54 -0
  93. package/dist/agent/utils.d.ts +1 -0
  94. package/dist/agent/utils.js +10 -0
  95. package/dist/cli/commands/agent.cjs +169 -0
  96. package/dist/cli/commands/agent.d.ts +15 -0
  97. package/dist/cli/commands/agent.js +125 -0
  98. package/dist/cli/commands/gateway.cjs +601 -0
  99. package/dist/cli/commands/gateway.d.ts +12 -0
  100. package/dist/cli/commands/gateway.js +567 -0
  101. package/dist/cli/commands/init.cjs +681 -0
  102. package/dist/cli/commands/init.d.ts +10 -0
  103. package/dist/cli/commands/init.js +634 -0
  104. package/dist/cli/commands/provider.cjs +208 -0
  105. package/dist/cli/commands/provider.d.ts +5 -0
  106. package/dist/cli/commands/provider.js +174 -0
  107. package/dist/cli/commands/skill.cjs +145 -0
  108. package/dist/cli/commands/skill.d.ts +10 -0
  109. package/dist/cli/commands/skill.js +111 -0
  110. package/dist/cli/config/loader.cjs +143 -0
  111. package/dist/cli/config/loader.d.ts +14 -0
  112. package/dist/cli/config/loader.js +109 -0
  113. package/dist/cli/config/schema.cjs +262 -0
  114. package/dist/cli/config/schema.d.ts +268 -0
  115. package/dist/cli/config/schema.js +213 -0
  116. package/dist/cli/core/agentInvoker.cjs +284 -0
  117. package/dist/cli/core/agentInvoker.d.ts +77 -0
  118. package/dist/cli/core/agentInvoker.js +247 -0
  119. package/dist/cli/core/commandHandler.cjs +257 -0
  120. package/dist/cli/core/commandHandler.d.ts +62 -0
  121. package/dist/cli/core/commandHandler.js +223 -0
  122. package/dist/cli/core/database/bunSqliteAdapter.cjs +87 -0
  123. package/dist/cli/core/database/bunSqliteAdapter.d.ts +34 -0
  124. package/dist/cli/core/database/bunSqliteAdapter.js +53 -0
  125. package/dist/cli/core/loggerBridge.cjs +42 -0
  126. package/dist/cli/core/loggerBridge.d.ts +8 -0
  127. package/dist/cli/core/loggerBridge.js +8 -0
  128. package/dist/cli/core/outputManager.cjs +106 -0
  129. package/dist/cli/core/outputManager.d.ts +43 -0
  130. package/dist/cli/core/outputManager.js +72 -0
  131. package/dist/cli/core/sessionManager.cjs +535 -0
  132. package/dist/cli/core/sessionManager.d.ts +111 -0
  133. package/dist/cli/core/sessionManager.js +486 -0
  134. package/dist/cli/core/streamParser.cjs +328 -0
  135. package/dist/cli/core/streamParser.d.ts +42 -0
  136. package/dist/cli/core/streamParser.js +288 -0
  137. package/dist/cli/index.cjs +211 -0
  138. package/dist/cli/index.d.ts +2 -0
  139. package/dist/cli/index.js +205 -0
  140. package/dist/cli/services/skillRepository.cjs +178 -0
  141. package/dist/cli/services/skillRepository.d.ts +35 -0
  142. package/dist/cli/services/skillRepository.js +144 -0
  143. package/dist/cli/services/skillService.cjs +336 -0
  144. package/dist/cli/services/skillService.d.ts +48 -0
  145. package/dist/cli/services/skillService.js +302 -0
  146. package/dist/cli/types/gateway.cjs +18 -0
  147. package/dist/cli/types/gateway.d.ts +18 -0
  148. package/dist/cli/types/gateway.js +0 -0
  149. package/dist/cli/types/init.cjs +18 -0
  150. package/dist/cli/types/init.d.ts +13 -0
  151. package/dist/cli/types/init.js +0 -0
  152. package/dist/cli/types/provider.cjs +18 -0
  153. package/dist/cli/types/provider.d.ts +9 -0
  154. package/dist/cli/types/provider.js +0 -0
  155. package/dist/cli/types/skill.cjs +18 -0
  156. package/dist/cli/types/skill.d.ts +71 -0
  157. package/dist/cli/types/skill.js +0 -0
  158. package/dist/cli/types.cjs +18 -0
  159. package/dist/cli/types.d.ts +175 -0
  160. package/dist/cli/types.js +0 -0
  161. package/dist/cli/ui/AgentOutput.cjs +82 -0
  162. package/dist/cli/ui/AgentOutput.d.ts +8 -0
  163. package/dist/cli/ui/AgentOutput.js +38 -0
  164. package/dist/cli/ui/App.cjs +285 -0
  165. package/dist/cli/ui/App.d.ts +6 -0
  166. package/dist/cli/ui/App.js +241 -0
  167. package/dist/cli/ui/ErrorDisplay.cjs +65 -0
  168. package/dist/cli/ui/ErrorDisplay.d.ts +8 -0
  169. package/dist/cli/ui/ErrorDisplay.js +21 -0
  170. package/dist/cli/ui/LogDisplay.cjs +74 -0
  171. package/dist/cli/ui/LogDisplay.d.ts +13 -0
  172. package/dist/cli/ui/LogDisplay.js +30 -0
  173. package/dist/cli/ui/SessionListDisplay.cjs +135 -0
  174. package/dist/cli/ui/SessionListDisplay.d.ts +9 -0
  175. package/dist/cli/ui/SessionListDisplay.js +91 -0
  176. package/dist/cli/ui/blockHelpers.cjs +80 -0
  177. package/dist/cli/ui/blockHelpers.d.ts +21 -0
  178. package/dist/cli/ui/blockHelpers.js +40 -0
  179. package/dist/cli/ui/components/ToolCallDisplay.cjs +207 -0
  180. package/dist/cli/ui/components/ToolCallDisplay.d.ts +7 -0
  181. package/dist/cli/ui/components/ToolCallDisplay.js +162 -0
  182. package/dist/cli/ui/components/ToolResultDisplay.cjs +86 -0
  183. package/dist/cli/ui/components/ToolResultDisplay.d.ts +8 -0
  184. package/dist/cli/ui/components/ToolResultDisplay.js +42 -0
  185. package/dist/cli/ui/toolDisplayHelpers.cjs +112 -0
  186. package/dist/cli/ui/toolDisplayHelpers.d.ts +3 -0
  187. package/dist/cli/ui/toolDisplayHelpers.js +72 -0
  188. package/dist/gateway/adapters/discord.cjs +298 -0
  189. package/dist/gateway/adapters/discord.d.ts +42 -0
  190. package/dist/gateway/adapters/discord.js +246 -0
  191. package/dist/gateway/auth.cjs +94 -0
  192. package/dist/gateway/auth.d.ts +36 -0
  193. package/dist/gateway/auth.js +60 -0
  194. package/dist/gateway/broadcast.cjs +131 -0
  195. package/dist/gateway/broadcast.d.ts +76 -0
  196. package/dist/gateway/broadcast.js +97 -0
  197. package/dist/gateway/client.cjs +282 -0
  198. package/dist/gateway/client.d.ts +141 -0
  199. package/dist/gateway/client.js +248 -0
  200. package/dist/gateway/daemon.cjs +195 -0
  201. package/dist/gateway/daemon.d.ts +67 -0
  202. package/dist/gateway/daemon.js +161 -0
  203. package/dist/gateway/discovery/index.cjs +72 -0
  204. package/dist/gateway/discovery/index.d.ts +3 -0
  205. package/dist/gateway/discovery/index.js +3 -0
  206. package/dist/gateway/discovery/mdns.cjs +221 -0
  207. package/dist/gateway/discovery/mdns.d.ts +37 -0
  208. package/dist/gateway/discovery/mdns.js +177 -0
  209. package/dist/gateway/discovery/tailscale.cjs +140 -0
  210. package/dist/gateway/discovery/tailscale.d.ts +31 -0
  211. package/dist/gateway/discovery/tailscale.js +106 -0
  212. package/dist/gateway/discovery/types.cjs +18 -0
  213. package/dist/gateway/discovery/types.d.ts +41 -0
  214. package/dist/gateway/discovery/types.js +0 -0
  215. package/dist/gateway/env.cjs +45 -0
  216. package/dist/gateway/env.d.ts +2 -0
  217. package/dist/gateway/env.js +8 -0
  218. package/dist/gateway/hooks/loader.cjs +137 -0
  219. package/dist/gateway/hooks/loader.d.ts +10 -0
  220. package/dist/gateway/hooks/loader.js +103 -0
  221. package/dist/gateway/hooks/registry.cjs +128 -0
  222. package/dist/gateway/hooks/registry.d.ts +13 -0
  223. package/dist/gateway/hooks/registry.js +94 -0
  224. package/dist/gateway/hooks/types.cjs +58 -0
  225. package/dist/gateway/hooks/types.d.ts +50 -0
  226. package/dist/gateway/hooks/types.js +18 -0
  227. package/dist/gateway/http/agents.cjs +280 -0
  228. package/dist/gateway/http/agents.d.ts +2 -0
  229. package/dist/gateway/http/agents.js +246 -0
  230. package/dist/gateway/http/fs.cjs +81 -0
  231. package/dist/gateway/http/fs.d.ts +2 -0
  232. package/dist/gateway/http/fs.js +47 -0
  233. package/dist/gateway/http/providers.cjs +120 -0
  234. package/dist/gateway/http/providers.d.ts +2 -0
  235. package/dist/gateway/http/providers.js +86 -0
  236. package/dist/gateway/http/routines.cjs +196 -0
  237. package/dist/gateway/http/routines.d.ts +20 -0
  238. package/dist/gateway/http/routines.js +159 -0
  239. package/dist/gateway/http/sessions.cjs +241 -0
  240. package/dist/gateway/http/sessions.d.ts +2 -0
  241. package/dist/gateway/http/sessions.js +207 -0
  242. package/dist/gateway/http/types.cjs +18 -0
  243. package/dist/gateway/http/types.d.ts +25 -0
  244. package/dist/gateway/http/types.js +0 -0
  245. package/dist/gateway/http/voice.cjs +167 -0
  246. package/dist/gateway/http/voice.d.ts +2 -0
  247. package/dist/gateway/http/voice.js +133 -0
  248. package/dist/gateway/http/webhooks.cjs +353 -0
  249. package/dist/gateway/http/webhooks.d.ts +22 -0
  250. package/dist/gateway/http/webhooks.js +313 -0
  251. package/dist/gateway/index.cjs +119 -0
  252. package/dist/gateway/index.d.ts +8 -0
  253. package/dist/gateway/index.js +9 -0
  254. package/dist/gateway/node.cjs +218 -0
  255. package/dist/gateway/node.d.ts +112 -0
  256. package/dist/gateway/node.js +184 -0
  257. package/dist/gateway/router.cjs +85 -0
  258. package/dist/gateway/router.d.ts +9 -0
  259. package/dist/gateway/router.js +51 -0
  260. package/dist/gateway/rpcClient.cjs +152 -0
  261. package/dist/gateway/rpcClient.d.ts +24 -0
  262. package/dist/gateway/rpcClient.js +118 -0
  263. package/dist/gateway/server.cjs +1175 -0
  264. package/dist/gateway/server.d.ts +185 -0
  265. package/dist/gateway/server.js +1138 -0
  266. package/dist/gateway/transport/http.cjs +153 -0
  267. package/dist/gateway/transport/http.d.ts +25 -0
  268. package/dist/gateway/transport/http.js +119 -0
  269. package/dist/gateway/transport/index.cjs +40 -0
  270. package/dist/gateway/transport/index.d.ts +3 -0
  271. package/dist/gateway/transport/index.js +3 -0
  272. package/dist/gateway/transport/types.cjs +18 -0
  273. package/dist/gateway/transport/types.d.ts +59 -0
  274. package/dist/gateway/transport/types.js +0 -0
  275. package/dist/gateway/transport/websocket.cjs +132 -0
  276. package/dist/gateway/transport/websocket.d.ts +21 -0
  277. package/dist/gateway/transport/websocket.js +98 -0
  278. package/dist/gateway/types.cjs +18 -0
  279. package/dist/gateway/types.d.ts +215 -0
  280. package/dist/gateway/types.js +0 -0
  281. package/dist/gateway/validation.cjs +225 -0
  282. package/dist/gateway/validation.d.ts +157 -0
  283. package/dist/gateway/validation.js +158 -0
  284. package/dist/index.cjs +95 -0
  285. package/dist/index.d.ts +6 -0
  286. package/dist/index.js +6 -0
  287. package/dist/logger.cjs +270 -0
  288. package/dist/logger.d.ts +54 -0
  289. package/dist/logger.js +215 -0
  290. package/dist/providers/copilot.cjs +148 -0
  291. package/dist/providers/copilot.d.ts +3 -0
  292. package/dist/providers/copilot.js +114 -0
  293. package/dist/providers/credentials.cjs +154 -0
  294. package/dist/providers/credentials.d.ts +26 -0
  295. package/dist/providers/credentials.js +99 -0
  296. package/dist/providers/oauth.cjs +279 -0
  297. package/dist/providers/oauth.d.ts +13 -0
  298. package/dist/providers/oauth.js +245 -0
  299. package/dist/providers/registry.cjs +138 -0
  300. package/dist/providers/registry.d.ts +32 -0
  301. package/dist/providers/registry.js +98 -0
  302. package/dist/tests/additionalMessageMiddleware.test.cjs +45 -0
  303. package/dist/tests/additionalMessageMiddleware.test.d.ts +1 -0
  304. package/dist/tests/additionalMessageMiddleware.test.js +39 -0
  305. package/dist/tests/agent-config-voice.test.cjs +25 -0
  306. package/dist/tests/agent-config-voice.test.d.ts +1 -0
  307. package/dist/tests/agent-config-voice.test.js +19 -0
  308. package/dist/tests/agentInvokerAttachments.test.cjs +67 -0
  309. package/dist/tests/agentInvokerAttachments.test.d.ts +1 -0
  310. package/dist/tests/agentInvokerAttachments.test.js +61 -0
  311. package/dist/tests/attachments-utils.test.cjs +46 -0
  312. package/dist/tests/attachments-utils.test.d.ts +1 -0
  313. package/dist/tests/attachments-utils.test.js +40 -0
  314. package/dist/tests/bunSqliteAdapter.test.cjs +265 -0
  315. package/dist/tests/bunSqliteAdapter.test.d.ts +1 -0
  316. package/dist/tests/bunSqliteAdapter.test.js +259 -0
  317. package/dist/tests/candleRange.test.cjs +48 -0
  318. package/dist/tests/candleRange.test.d.ts +1 -0
  319. package/dist/tests/candleRange.test.js +42 -0
  320. package/dist/tests/cli-config-loader.test.cjs +364 -0
  321. package/dist/tests/cli-config-loader.test.d.ts +1 -0
  322. package/dist/tests/cli-config-loader.test.js +358 -0
  323. package/dist/tests/cli-init.test.cjs +82 -0
  324. package/dist/tests/cli-init.test.d.ts +1 -0
  325. package/dist/tests/cli-init.test.js +76 -0
  326. package/dist/tests/discord-adapter.test.cjs +55 -0
  327. package/dist/tests/discord-adapter.test.d.ts +1 -0
  328. package/dist/tests/discord-adapter.test.js +49 -0
  329. package/dist/tests/gateway.test.cjs +319 -0
  330. package/dist/tests/gateway.test.d.ts +1 -0
  331. package/dist/tests/gateway.test.js +313 -0
  332. package/dist/tests/hooks-matcher.test.cjs +309 -0
  333. package/dist/tests/hooks-matcher.test.d.ts +1 -0
  334. package/dist/tests/hooks-matcher.test.js +303 -0
  335. package/dist/tests/hooks-merger.test.cjs +528 -0
  336. package/dist/tests/hooks-merger.test.d.ts +1 -0
  337. package/dist/tests/hooks-merger.test.js +522 -0
  338. package/dist/tests/integration/agent-invocation.integration.test.cjs +264 -0
  339. package/dist/tests/integration/agent-invocation.integration.test.d.ts +1 -0
  340. package/dist/tests/integration/agent-invocation.integration.test.js +258 -0
  341. package/dist/tests/integration/finnhub-candles.integration.test.cjs +98 -0
  342. package/dist/tests/integration/finnhub-candles.integration.test.d.ts +1 -0
  343. package/dist/tests/integration/finnhub-candles.integration.test.js +92 -0
  344. package/dist/tests/logger.test.cjs +353 -0
  345. package/dist/tests/logger.test.d.ts +1 -0
  346. package/dist/tests/logger.test.js +347 -0
  347. package/dist/tests/mediaCompatibilityMiddleware.test.cjs +106 -0
  348. package/dist/tests/mediaCompatibilityMiddleware.test.d.ts +1 -0
  349. package/dist/tests/mediaCompatibilityMiddleware.test.js +100 -0
  350. package/dist/tests/routines-api.test.cjs +107 -0
  351. package/dist/tests/routines-api.test.d.ts +1 -0
  352. package/dist/tests/routines-api.test.js +101 -0
  353. package/dist/tests/sessionMessageAttachments.test.cjs +108 -0
  354. package/dist/tests/sessionMessageAttachments.test.d.ts +1 -0
  355. package/dist/tests/sessionMessageAttachments.test.js +102 -0
  356. package/dist/tests/sessionMessageRole.test.cjs +44 -0
  357. package/dist/tests/sessionMessageRole.test.d.ts +1 -0
  358. package/dist/tests/sessionMessageRole.test.js +38 -0
  359. package/dist/tests/sessionStateMessages.test.cjs +72 -0
  360. package/dist/tests/sessionStateMessages.test.d.ts +1 -0
  361. package/dist/tests/sessionStateMessages.test.js +66 -0
  362. package/dist/tests/sessions-api.test.cjs +68 -0
  363. package/dist/tests/sessions-api.test.d.ts +1 -0
  364. package/dist/tests/sessions-api.test.js +62 -0
  365. package/dist/tests/technicalIndicators.test.cjs +82 -0
  366. package/dist/tests/technicalIndicators.test.d.ts +1 -0
  367. package/dist/tests/technicalIndicators.test.js +76 -0
  368. package/dist/tests/toolDisplayHelpers.test.cjs +43 -0
  369. package/dist/tests/toolDisplayHelpers.test.d.ts +1 -0
  370. package/dist/tests/toolDisplayHelpers.test.js +37 -0
  371. package/dist/tests/voice-config.test.cjs +35 -0
  372. package/dist/tests/voice-config.test.d.ts +1 -0
  373. package/dist/tests/voice-config.test.js +29 -0
  374. package/dist/tests/yahooCandles.test.cjs +111 -0
  375. package/dist/tests/yahooCandles.test.d.ts +1 -0
  376. package/dist/tests/yahooCandles.test.js +105 -0
  377. package/dist/tools/finance/candleRange.cjs +71 -0
  378. package/dist/tools/finance/candleRange.d.ts +21 -0
  379. package/dist/tools/finance/candleRange.js +28 -0
  380. package/dist/tools/finance/optionsAnalytics.cjs +222 -0
  381. package/dist/tools/finance/optionsAnalytics.d.ts +44 -0
  382. package/dist/tools/finance/optionsAnalytics.js +188 -0
  383. package/dist/tools/finance/optionsAnalytics.test.cjs +128 -0
  384. package/dist/tools/finance/optionsAnalytics.test.d.ts +1 -0
  385. package/dist/tools/finance/optionsAnalytics.test.js +122 -0
  386. package/dist/tools/finance/technicalIndicators.cjs +111 -0
  387. package/dist/tools/finance/technicalIndicators.d.ts +15 -0
  388. package/dist/tools/finance/technicalIndicators.js +68 -0
  389. package/dist/tools/finance/yahooCandles.cjs +125 -0
  390. package/dist/tools/finance/yahooCandles.d.ts +41 -0
  391. package/dist/tools/finance/yahooCandles.js +85 -0
  392. package/dist/tools/mcp-finance.cjs +649 -0
  393. package/dist/tools/mcp-finance.d.ts +1 -0
  394. package/dist/tools/mcp-finance.js +631 -0
  395. package/dist/types/agents.cjs +18 -0
  396. package/dist/types/agents.d.ts +11 -0
  397. package/dist/types/agents.js +0 -0
  398. package/dist/types/hooks.cjs +18 -0
  399. package/dist/types/hooks.d.ts +82 -0
  400. package/dist/types/hooks.js +0 -0
  401. package/dist/types/mcp.cjs +86 -0
  402. package/dist/types/mcp.d.ts +107 -0
  403. package/dist/types/mcp.js +40 -0
  404. package/dist/types/voice.cjs +103 -0
  405. package/dist/types/voice.d.ts +117 -0
  406. package/dist/types/voice.js +51 -0
  407. package/dist/utils/attachments.cjs +46 -0
  408. package/dist/utils/attachments.d.ts +7 -0
  409. package/dist/utils/attachments.js +12 -0
  410. package/dist/voice/config.cjs +52 -0
  411. package/dist/voice/config.d.ts +8 -0
  412. package/dist/voice/config.js +18 -0
  413. package/dist/webui/assets/index-BA0HaStz.css +1 -0
  414. package/dist/webui/assets/index-NHgTZsWN.js +112 -0
  415. package/dist/webui/assets/wingman_icon-DOy91UEF.webp +0 -0
  416. package/dist/webui/assets/wingman_logo-Cogyt3qm.webp +0 -0
  417. package/dist/webui/index.html +19 -0
  418. package/package.json +130 -0
@@ -0,0 +1,1138 @@
1
+ import { NodeManager } from "./node.js";
2
+ import { BroadcastGroupManager } from "./broadcast.js";
3
+ import { GatewayAuth } from "./auth.js";
4
+ import { validateGatewayMessage } from "./validation.js";
5
+ import { getGatewayTokenFromEnv } from "./env.js";
6
+ import { MDNSDiscoveryService, TailscaleDiscoveryService } from "./discovery/index.js";
7
+ import { createLogger } from "../logger.js";
8
+ import { WingmanConfigLoader } from "../cli/config/loader.js";
9
+ import { GatewayRouter } from "./router.js";
10
+ import { OutputManager } from "../cli/core/outputManager.js";
11
+ import { AgentInvoker } from "../cli/core/agentInvoker.js";
12
+ import { SessionManager } from "../cli/core/sessionManager.js";
13
+ import { handleAgentsApi } from "./http/agents.js";
14
+ import { handleFsApi } from "./http/fs.js";
15
+ import { handleProvidersApi } from "./http/providers.js";
16
+ import { handleVoiceApi } from "./http/voice.js";
17
+ import { handleSessionsApi } from "./http/sessions.js";
18
+ import { createWebhookStore, handleWebhookInvoke, handleWebhooksApi } from "./http/webhooks.js";
19
+ import { createRoutineStore, handleRoutinesApi } from "./http/routines.js";
20
+ import { DiscordGatewayAdapter } from "./adapters/discord.js";
21
+ import { InternalHookRegistry } from "./hooks/registry.js";
22
+ import { homedir } from "node:os";
23
+ import { dirname, isAbsolute, join, normalize, sep } from "node:path";
24
+ import { existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
25
+ import { fileURLToPath } from "node:url";
26
+ function _define_property(obj, key, value) {
27
+ if (key in obj) Object.defineProperty(obj, key, {
28
+ value: value,
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true
32
+ });
33
+ else obj[key] = value;
34
+ return obj;
35
+ }
36
+ class GatewayServer {
37
+ async start() {
38
+ if (void 0 === globalThis.Bun) throw new Error("Gateway server requires Bun runtime. Start with `bun ./bin/wingman gateway start`.");
39
+ this.startedAt = Date.now();
40
+ this.internalHooks = new InternalHookRegistry(this.getHttpContext(), this.wingmanConfig.hooks);
41
+ await this.internalHooks.load();
42
+ this.server = Bun.serve({
43
+ port: this.config.port,
44
+ hostname: this.config.host,
45
+ fetch: async (req, server)=>{
46
+ const url = new URL(req.url);
47
+ if ("/health" === url.pathname) return this.handleHealthCheck();
48
+ if ("/stats" === url.pathname) return this.handleStats();
49
+ if ("/bridge/send" === url.pathname) return this.handleBridgeSend(req);
50
+ if ("/bridge/poll" === url.pathname) return this.handleBridgePoll(req);
51
+ if ("/ws" === url.pathname) {
52
+ const tailscaleUser = req.headers.get("tailscale-user-login") || req.headers.get("ts-user-login") || void 0;
53
+ const upgraded = server.upgrade(req, {
54
+ data: {
55
+ nodeId: "",
56
+ tailscaleUser
57
+ }
58
+ });
59
+ if (!upgraded) return new Response("WebSocket upgrade failed", {
60
+ status: 400
61
+ });
62
+ return;
63
+ }
64
+ const webhookResponse = await handleWebhookInvoke(this.getHttpContext(), this.webhookStore, req, url);
65
+ if (webhookResponse) return webhookResponse;
66
+ if (this.controlUiSamePort) return this.handleUiRequest(req);
67
+ return new Response("Not Found", {
68
+ status: 404
69
+ });
70
+ },
71
+ websocket: {
72
+ open: (ws)=>this.handleOpen(ws),
73
+ message: (ws, message)=>this.handleMessage(ws, message),
74
+ close: (ws)=>this.handleClose(ws),
75
+ drain: (ws)=>this.handleDrain(ws)
76
+ }
77
+ });
78
+ this.startPingInterval();
79
+ if (this.controlUiEnabled && !this.controlUiSamePort) {
80
+ if (!this.uiDistDir) this.log("warn", "Control UI is enabled but build assets were not found. Run `bun run webui:build`.");
81
+ this.uiServer = Bun.serve({
82
+ port: this.controlUiPort,
83
+ hostname: this.config.host,
84
+ fetch: (req)=>this.handleUiRequest(req)
85
+ });
86
+ this.log("info", `Control UI started on ${this.config.host}:${this.controlUiPort}`);
87
+ } else if (this.controlUiEnabled) {
88
+ if (!this.uiDistDir) this.log("warn", "Control UI is enabled but build assets were not found. Run `bun run webui:build`.");
89
+ this.log("info", `Control UI available on ${this.config.host}:${this.config.port}`);
90
+ }
91
+ if (this.config.discovery?.enabled) await this.startDiscovery();
92
+ await this.startAdapters();
93
+ this.log("info", `Gateway started on ${this.config.host}:${this.config.port}`);
94
+ this.internalHooks?.emit({
95
+ type: "gateway",
96
+ action: "startup",
97
+ timestamp: new Date()
98
+ });
99
+ }
100
+ async stop() {
101
+ await this.stopAdapters();
102
+ if (this.discoveryService) {
103
+ await this.discoveryService.stopAnnouncing();
104
+ this.discoveryService = null;
105
+ }
106
+ if (this.pingInterval) {
107
+ clearInterval(this.pingInterval);
108
+ this.pingInterval = null;
109
+ }
110
+ if (this.server) {
111
+ this.server.stop();
112
+ this.server = null;
113
+ }
114
+ if (this.uiServer) {
115
+ this.uiServer.stop();
116
+ this.uiServer = null;
117
+ }
118
+ this.log("info", "Gateway stopped");
119
+ }
120
+ async startAdapters() {
121
+ const discordConfig = this.wingmanConfig.gateway?.adapters?.discord;
122
+ if (discordConfig?.enabled) {
123
+ const resolvedHost = "0.0.0.0" === this.config.host || "::" === this.config.host ? "127.0.0.1" : this.config.host;
124
+ const url = discordConfig.gatewayUrl || `ws://${resolvedHost}:${this.config.port}/ws`;
125
+ const token = discordConfig.gatewayToken || (this.config.auth?.mode === "token" ? this.config.authToken : void 0);
126
+ const password = discordConfig.gatewayPassword || (this.config.auth?.mode === "password" ? this.config.auth?.password : void 0);
127
+ this.discordAdapter = new DiscordGatewayAdapter({
128
+ enabled: discordConfig.enabled,
129
+ token: discordConfig.token,
130
+ mentionOnly: discordConfig.mentionOnly ?? true,
131
+ allowBots: discordConfig.allowBots ?? false,
132
+ allowedGuilds: discordConfig.allowedGuilds ?? [],
133
+ allowedChannels: discordConfig.allowedChannels ?? [],
134
+ channelSessions: discordConfig.channelSessions ?? {},
135
+ sessionCommand: discordConfig.sessionCommand || "!session",
136
+ gatewayUrl: discordConfig.gatewayUrl,
137
+ gatewayToken: discordConfig.gatewayToken,
138
+ gatewayPassword: discordConfig.gatewayPassword,
139
+ responseChunkSize: discordConfig.responseChunkSize || 1900
140
+ }, {
141
+ url,
142
+ token,
143
+ password
144
+ }, this.logger);
145
+ try {
146
+ await this.discordAdapter.start();
147
+ this.log("info", "Discord adapter started");
148
+ } catch (error) {
149
+ this.log("error", "Failed to start Discord adapter", error);
150
+ this.discordAdapter = null;
151
+ }
152
+ }
153
+ }
154
+ async stopAdapters() {
155
+ if (this.discordAdapter) {
156
+ await this.discordAdapter.stop();
157
+ this.discordAdapter = null;
158
+ }
159
+ }
160
+ async startDiscovery() {
161
+ const discovery = this.config.discovery;
162
+ if (!discovery) return;
163
+ const discoveryConfig = {
164
+ name: discovery.name,
165
+ port: this.config.port,
166
+ requireAuth: this.auth.isAuthRequired(),
167
+ capabilities: [
168
+ "broadcast",
169
+ "direct",
170
+ "groups"
171
+ ],
172
+ version: "1.0.0",
173
+ transport: "0.0.0.0" === this.config.host ? "ws" : "wss"
174
+ };
175
+ if ("mdns" === discovery.method) {
176
+ this.discoveryService = new MDNSDiscoveryService();
177
+ await this.discoveryService.announce(discoveryConfig);
178
+ this.log("info", `mDNS discovery started: ${discovery.name}`);
179
+ } else if ("tailscale" === discovery.method) {
180
+ this.discoveryService = new TailscaleDiscoveryService();
181
+ await this.discoveryService.announce(discoveryConfig);
182
+ this.log("info", `Tailscale discovery started: ${discovery.name}`);
183
+ }
184
+ }
185
+ handleOpen(ws) {
186
+ this.log("debug", "New WebSocket connection");
187
+ }
188
+ handleMessage(ws, message) {
189
+ try {
190
+ const parsed = JSON.parse(message.toString());
191
+ const validation = validateGatewayMessage(parsed);
192
+ if (!validation.success) {
193
+ this.log("warn", "Invalid message format", validation.error);
194
+ this.sendError(ws, "INVALID_MESSAGE", validation.error);
195
+ return;
196
+ }
197
+ const msg = validation.data;
198
+ const nodeId = ws.data.nodeId;
199
+ if ("connect" === msg.type) return void this.handleConnect(ws, msg);
200
+ if ("req:agent" === msg.type) return void this.handleAgentRequest(ws, msg);
201
+ if (nodeId && "register" !== msg.type && "ping" !== msg.type && "pong" !== msg.type) {
202
+ if (this.nodeManager.isRateLimited(nodeId)) return void this.sendError(ws, "RATE_LIMITED", "Too many messages. Please slow down.");
203
+ this.nodeManager.recordMessage(nodeId);
204
+ }
205
+ this.messagesProcessed++;
206
+ this.log("debug", `Received message: ${msg.type}`, msg);
207
+ switch(msg.type){
208
+ case "register":
209
+ this.handleRegister(ws, msg);
210
+ break;
211
+ case "session_subscribe":
212
+ this.handleSessionSubscribe(ws, msg);
213
+ break;
214
+ case "session_unsubscribe":
215
+ this.handleSessionUnsubscribe(ws, msg);
216
+ break;
217
+ case "unregister":
218
+ this.handleUnregister(ws, msg);
219
+ break;
220
+ case "join_group":
221
+ this.handleJoinGroup(ws, msg);
222
+ break;
223
+ case "leave_group":
224
+ this.handleLeaveGroup(ws, msg);
225
+ break;
226
+ case "broadcast":
227
+ this.handleBroadcast(ws, msg);
228
+ break;
229
+ case "direct":
230
+ this.handleDirect(ws, msg);
231
+ break;
232
+ case "ping":
233
+ this.handlePing(ws, msg);
234
+ break;
235
+ case "pong":
236
+ this.handlePong(ws, msg);
237
+ break;
238
+ default:
239
+ this.sendError(ws, "UNKNOWN_MESSAGE_TYPE", "Unknown message type");
240
+ }
241
+ } catch (error) {
242
+ this.log("error", "Failed to process message", error);
243
+ this.sendError(ws, "INVALID_MESSAGE", "Invalid message format");
244
+ }
245
+ }
246
+ handleClose(ws) {
247
+ const nodeId = ws.data.nodeId;
248
+ if (nodeId) {
249
+ this.groupManager.removeNodeFromAllGroups(nodeId);
250
+ this.nodeManager.unregisterNode(nodeId);
251
+ this.log("info", `Node disconnected: ${nodeId}`);
252
+ }
253
+ this.connectedClients.delete(ws);
254
+ this.clearSessionSubscriptions(ws);
255
+ }
256
+ handleDrain(ws) {
257
+ this.log("debug", "WebSocket drained");
258
+ }
259
+ handleConnect(ws, msg) {
260
+ if (!msg.id) return void this.sendError(ws, "INVALID_CONNECT", "Missing connect request id");
261
+ if (!msg.client) return void this.sendMessage(ws, {
262
+ type: "res",
263
+ id: msg.id,
264
+ ok: false,
265
+ payload: "missing client info",
266
+ timestamp: Date.now()
267
+ });
268
+ const allowed = this.auth.validate(msg.auth, ws.data.tailscaleUser);
269
+ if (!allowed) {
270
+ this.sendMessage(ws, {
271
+ type: "res",
272
+ id: msg.id,
273
+ ok: false,
274
+ payload: "authentication failed",
275
+ timestamp: Date.now()
276
+ });
277
+ ws.close();
278
+ return;
279
+ }
280
+ ws.data.clientId = msg.client.instanceId;
281
+ ws.data.clientType = msg.client.clientType;
282
+ ws.data.authenticated = true;
283
+ this.connectedClients.add(ws);
284
+ this.sendMessage(ws, {
285
+ type: "res",
286
+ id: msg.id,
287
+ ok: true,
288
+ payload: "gateway-ready",
289
+ timestamp: Date.now()
290
+ });
291
+ }
292
+ async handleAgentRequest(ws, msg) {
293
+ if (!msg.id) return void this.sendError(ws, "INVALID_REQUEST", "Missing request id");
294
+ if (!ws.data.authenticated) return void this.sendAgentError(ws, msg.id, "Client is not authenticated");
295
+ const payload = msg.payload;
296
+ const content = "string" == typeof payload?.content ? payload.content : "";
297
+ const attachments = Array.isArray(payload?.attachments) ? payload.attachments : [];
298
+ const hasContent = content.trim().length > 0;
299
+ const hasAttachments = attachments.length > 0;
300
+ if (!hasContent && !hasAttachments) return void this.sendAgentError(ws, msg.id, "Missing agent content");
301
+ const agentId = this.router.selectAgent(payload.agentId, payload.routing);
302
+ if (!agentId) return void this.sendAgentError(ws, msg.id, "No agent matched the request");
303
+ const sessionKey = payload.sessionKey || this.router.buildSessionKey(agentId, payload.routing);
304
+ const sessionManager = await this.getSessionManager(agentId);
305
+ const existingSession = sessionManager.getSession(sessionKey);
306
+ const session = existingSession || sessionManager.getOrCreateSession(sessionKey, agentId);
307
+ const workdir = session.metadata?.workdir ?? null;
308
+ const defaultOutputDir = this.resolveDefaultOutputDir(agentId);
309
+ const preview = hasContent ? content.trim() : buildAttachmentPreview(attachments);
310
+ sessionManager.updateSession(session.id, {
311
+ lastMessagePreview: preview.substring(0, 200)
312
+ });
313
+ if (!existingSession) this.internalHooks?.emit({
314
+ type: "session",
315
+ action: "start",
316
+ timestamp: new Date(),
317
+ agentId,
318
+ sessionKey,
319
+ routing: payload.routing
320
+ });
321
+ this.internalHooks?.emit({
322
+ type: "message",
323
+ action: "received",
324
+ timestamp: new Date(),
325
+ agentId,
326
+ sessionKey,
327
+ routing: payload.routing,
328
+ payload: {
329
+ content,
330
+ attachments
331
+ }
332
+ });
333
+ const sessionMessage = {
334
+ type: "event:agent",
335
+ id: msg.id,
336
+ payload: this.attachSessionContext({
337
+ type: "session-message",
338
+ role: "user",
339
+ content,
340
+ attachments,
341
+ routing: payload.routing
342
+ }, sessionKey, agentId),
343
+ timestamp: Date.now()
344
+ };
345
+ this.broadcastSessionEvent(sessionKey, sessionMessage, ws);
346
+ this.broadcastToClients(sessionMessage, {
347
+ exclude: ws,
348
+ clientType: "webui",
349
+ skipSessionId: sessionKey
350
+ });
351
+ const outputManager = new OutputManager("interactive");
352
+ const outputHandler = (event)=>{
353
+ const payloadWithSession = this.attachSessionContext(event, sessionKey, agentId);
354
+ const baseMessage = {
355
+ type: "event:agent",
356
+ id: msg.id,
357
+ payload: payloadWithSession,
358
+ timestamp: Date.now()
359
+ };
360
+ this.sendMessage(ws, {
361
+ ...baseMessage,
362
+ clientId: ws.data.clientId
363
+ });
364
+ this.broadcastSessionEvent(sessionKey, baseMessage, ws);
365
+ };
366
+ outputManager.on("output-event", outputHandler);
367
+ const workspace = this.resolveAgentWorkspace(agentId);
368
+ const invoker = new AgentInvoker({
369
+ workspace,
370
+ configDir: this.configDir,
371
+ outputManager,
372
+ logger: this.logger,
373
+ sessionManager,
374
+ workdir,
375
+ defaultOutputDir
376
+ });
377
+ try {
378
+ await invoker.invokeAgent(agentId, content, sessionKey, attachments);
379
+ const updated = sessionManager.getSession(sessionKey);
380
+ if (updated) sessionManager.updateSession(sessionKey, {
381
+ messageCount: updated.messageCount + 1
382
+ });
383
+ } catch (error) {
384
+ this.logger.error("Agent invocation failed", error);
385
+ } finally{
386
+ outputManager.off("output-event", outputHandler);
387
+ }
388
+ }
389
+ handleRegister(ws, msg) {
390
+ const payload = msg.payload;
391
+ if (!this.auth.validate({
392
+ token: payload.token
393
+ }, ws.data.tailscaleUser)) {
394
+ this.sendError(ws, "AUTH_FAILED", "Authentication failed");
395
+ ws.close();
396
+ return;
397
+ }
398
+ const node = this.nodeManager.registerNode(ws, payload.name, payload.capabilities, payload.sessionId, payload.agentName);
399
+ if (!node) {
400
+ this.sendError(ws, "MAX_NODES_REACHED", "Maximum nodes reached");
401
+ ws.close();
402
+ return;
403
+ }
404
+ this.sendMessage(ws, {
405
+ type: "ack",
406
+ nodeId: node.id,
407
+ payload: {
408
+ nodeId: node.id,
409
+ name: node.name,
410
+ sessionId: node.sessionId,
411
+ agentName: node.agentName
412
+ },
413
+ timestamp: Date.now()
414
+ });
415
+ const sessionInfo = node.sessionId ? ` (session: ${node.sessionId})` : "";
416
+ this.log("info", `Node registered: ${node.id} (${node.name})${sessionInfo}`);
417
+ }
418
+ handleSessionSubscribe(ws, msg) {
419
+ if (!ws.data.authenticated) return void this.sendError(ws, "AUTH_REQUIRED", "Client is not authenticated");
420
+ const payload = msg.payload;
421
+ const sessionId = "string" == typeof payload?.sessionId ? payload.sessionId.trim() : "";
422
+ if (!sessionId) return void this.sendError(ws, "INVALID_REQUEST", "Missing sessionId");
423
+ this.addSessionSubscription(ws, sessionId);
424
+ this.sendMessage(ws, {
425
+ type: "ack",
426
+ payload: {
427
+ action: "session_subscribe",
428
+ sessionId
429
+ },
430
+ timestamp: Date.now()
431
+ });
432
+ }
433
+ handleSessionUnsubscribe(ws, msg) {
434
+ if (!ws.data.authenticated) return void this.sendError(ws, "AUTH_REQUIRED", "Client is not authenticated");
435
+ const payload = msg.payload;
436
+ const sessionId = "string" == typeof payload?.sessionId ? payload.sessionId.trim() : "";
437
+ if (!sessionId) return void this.sendError(ws, "INVALID_REQUEST", "Missing sessionId");
438
+ this.removeSessionSubscription(ws, sessionId);
439
+ this.sendMessage(ws, {
440
+ type: "ack",
441
+ payload: {
442
+ action: "session_unsubscribe",
443
+ sessionId
444
+ },
445
+ timestamp: Date.now()
446
+ });
447
+ }
448
+ handleUnregister(ws, msg) {
449
+ const nodeId = ws.data.nodeId;
450
+ if (nodeId) {
451
+ this.groupManager.removeNodeFromAllGroups(nodeId);
452
+ this.nodeManager.unregisterNode(nodeId);
453
+ this.log("info", `Node unregistered: ${nodeId}`);
454
+ }
455
+ }
456
+ handleJoinGroup(ws, msg) {
457
+ const nodeId = ws.data.nodeId;
458
+ if (!nodeId) return void this.sendError(ws, "NOT_REGISTERED", "Node not registered");
459
+ const payload = msg.payload;
460
+ let group;
461
+ if (payload.groupId) group = this.groupManager.getGroup(payload.groupId);
462
+ else if (payload.groupName) group = payload.createIfNotExists ? this.groupManager.getOrCreateGroup(payload.groupName, nodeId, payload.description) : this.groupManager.getGroupByName(payload.groupName);
463
+ if (!group) return void this.sendError(ws, "GROUP_NOT_FOUND", "Group not found");
464
+ this.groupManager.addNodeToGroup(group.id, nodeId);
465
+ this.nodeManager.addNodeToGroup(nodeId, group.id);
466
+ this.sendMessage(ws, {
467
+ type: "ack",
468
+ nodeId,
469
+ groupId: group.id,
470
+ payload: {
471
+ groupId: group.id,
472
+ groupName: group.name
473
+ },
474
+ timestamp: Date.now()
475
+ });
476
+ this.log("info", `Node ${nodeId} joined group ${group.name}`);
477
+ }
478
+ handleLeaveGroup(ws, msg) {
479
+ const nodeId = ws.data.nodeId;
480
+ if (!nodeId || !msg.groupId) return void this.sendError(ws, "INVALID_REQUEST", "Invalid leave group request");
481
+ this.groupManager.removeNodeFromGroup(msg.groupId, nodeId);
482
+ this.nodeManager.removeNodeFromGroup(nodeId, msg.groupId);
483
+ this.sendMessage(ws, {
484
+ type: "ack",
485
+ nodeId,
486
+ groupId: msg.groupId,
487
+ timestamp: Date.now()
488
+ });
489
+ this.log("info", `Node ${nodeId} left group ${msg.groupId}`);
490
+ }
491
+ handleBroadcast(ws, msg) {
492
+ const nodeId = ws.data.nodeId;
493
+ if (!nodeId) return void this.sendError(ws, "NOT_REGISTERED", "Node not registered");
494
+ const payload = msg.payload;
495
+ const members = this.groupManager.getGroupMembers(payload.groupId);
496
+ const recipients = members.filter((id)=>id !== nodeId);
497
+ const broadcastMsg = {
498
+ type: "broadcast",
499
+ nodeId,
500
+ groupId: payload.groupId,
501
+ payload: payload.message,
502
+ timestamp: Date.now()
503
+ };
504
+ const sent = this.nodeManager.broadcastToNodes(recipients, broadcastMsg);
505
+ this.log("debug", `Broadcast to ${sent}/${recipients.length} nodes in group ${payload.groupId}`);
506
+ }
507
+ handleDirect(ws, msg) {
508
+ const nodeId = ws.data.nodeId;
509
+ if (!nodeId) return void this.sendError(ws, "NOT_REGISTERED", "Node not registered");
510
+ const payload = msg.payload;
511
+ const directMsg = {
512
+ type: "direct",
513
+ nodeId,
514
+ targetNodeId: payload.targetNodeId,
515
+ payload: payload.message,
516
+ timestamp: Date.now()
517
+ };
518
+ const sent = this.nodeManager.sendToNode(payload.targetNodeId, directMsg);
519
+ if (!sent) this.sendError(ws, "NODE_NOT_FOUND", "Target node not found");
520
+ }
521
+ handlePing(ws, msg) {
522
+ const nodeId = ws.data.nodeId;
523
+ if (nodeId) this.nodeManager.updatePing(nodeId);
524
+ this.sendMessage(ws, {
525
+ type: "pong",
526
+ timestamp: Date.now()
527
+ });
528
+ }
529
+ handlePong(ws, msg) {
530
+ const nodeId = ws.data.nodeId;
531
+ if (nodeId) this.nodeManager.updatePing(nodeId);
532
+ }
533
+ sendMessage(ws, message) {
534
+ try {
535
+ ws.send(JSON.stringify(message));
536
+ } catch (error) {
537
+ this.log("error", "Failed to send message", error);
538
+ }
539
+ }
540
+ sendError(ws, code, message) {
541
+ const errorPayload = {
542
+ code,
543
+ message
544
+ };
545
+ this.sendMessage(ws, {
546
+ type: "error",
547
+ payload: errorPayload,
548
+ timestamp: Date.now()
549
+ });
550
+ }
551
+ sendAgentError(ws, requestId, message) {
552
+ this.sendMessage(ws, {
553
+ type: "event:agent",
554
+ id: requestId,
555
+ payload: {
556
+ type: "agent-error",
557
+ error: message,
558
+ timestamp: new Date().toISOString()
559
+ },
560
+ timestamp: Date.now()
561
+ });
562
+ }
563
+ attachSessionContext(event, sessionId, agentId) {
564
+ if (event && "object" == typeof event && !Array.isArray(event)) return {
565
+ ...event,
566
+ sessionId,
567
+ agentId
568
+ };
569
+ return {
570
+ type: "agent-event",
571
+ data: event,
572
+ sessionId,
573
+ agentId
574
+ };
575
+ }
576
+ broadcastSessionEvent(sessionId, message, exclude) {
577
+ const subscribers = this.sessionSubscriptions.get(sessionId);
578
+ if (!subscribers || 0 === subscribers.size) return 0;
579
+ let sent = 0;
580
+ for (const ws of subscribers)if (!exclude || ws !== exclude) {
581
+ this.sendMessage(ws, message);
582
+ sent++;
583
+ }
584
+ return sent;
585
+ }
586
+ broadcastToClients(message, options) {
587
+ let sent = 0;
588
+ for (const ws of this.connectedClients)if (!options?.exclude || ws !== options.exclude) {
589
+ if (!options?.clientType || ws.data.clientType === options.clientType) {
590
+ if (options?.skipSessionId) {
591
+ const subscribers = this.sessionSubscriptions.get(options.skipSessionId);
592
+ if (subscribers?.has(ws)) continue;
593
+ }
594
+ this.sendMessage(ws, message);
595
+ sent++;
596
+ }
597
+ }
598
+ return sent;
599
+ }
600
+ addSessionSubscription(ws, sessionId) {
601
+ let subscribers = this.sessionSubscriptions.get(sessionId);
602
+ if (!subscribers) {
603
+ subscribers = new Set();
604
+ this.sessionSubscriptions.set(sessionId, subscribers);
605
+ }
606
+ subscribers.add(ws);
607
+ let socketSessions = this.socketSubscriptions.get(ws);
608
+ if (!socketSessions) {
609
+ socketSessions = new Set();
610
+ this.socketSubscriptions.set(ws, socketSessions);
611
+ }
612
+ socketSessions.add(sessionId);
613
+ }
614
+ removeSessionSubscription(ws, sessionId) {
615
+ const subscribers = this.sessionSubscriptions.get(sessionId);
616
+ if (subscribers) {
617
+ subscribers.delete(ws);
618
+ if (0 === subscribers.size) this.sessionSubscriptions.delete(sessionId);
619
+ }
620
+ const socketSessions = this.socketSubscriptions.get(ws);
621
+ if (socketSessions) {
622
+ socketSessions.delete(sessionId);
623
+ if (0 === socketSessions.size) this.socketSubscriptions.delete(ws);
624
+ }
625
+ }
626
+ clearSessionSubscriptions(ws) {
627
+ const sessions = this.socketSubscriptions.get(ws);
628
+ if (!sessions) return;
629
+ for (const sessionId of sessions){
630
+ const subscribers = this.sessionSubscriptions.get(sessionId);
631
+ if (subscribers) {
632
+ subscribers.delete(ws);
633
+ if (0 === subscribers.size) this.sessionSubscriptions.delete(sessionId);
634
+ }
635
+ }
636
+ this.socketSubscriptions.delete(ws);
637
+ }
638
+ resolveStateDir() {
639
+ const configured = this.config.stateDir || this.wingmanConfig.gateway?.stateDir;
640
+ const fallback = join(homedir(), ".wingman");
641
+ const raw = configured || fallback;
642
+ if (raw.startsWith("~/")) return join(homedir(), raw.slice(2));
643
+ if (isAbsolute(raw)) return raw;
644
+ return join(this.workspace, raw);
645
+ }
646
+ resolveAgentWorkspace(agentId) {
647
+ const agents = this.wingmanConfig.agents?.list || [];
648
+ const match = agents.find((agent)=>agent.id === agentId);
649
+ return match?.workspace || this.workspace;
650
+ }
651
+ resolveConfigDirPath() {
652
+ return isAbsolute(this.configDir) ? this.configDir : join(this.workspace, this.configDir);
653
+ }
654
+ getHttpContext() {
655
+ return {
656
+ workspace: this.workspace,
657
+ configDir: this.configDir,
658
+ getWingmanConfig: ()=>this.wingmanConfig,
659
+ setWingmanConfig: (config)=>{
660
+ this.wingmanConfig = config;
661
+ },
662
+ persistWingmanConfig: ()=>this.persistWingmanConfig(),
663
+ router: this.router,
664
+ setRouter: (router)=>{
665
+ this.router = router;
666
+ },
667
+ auth: this.auth,
668
+ logger: this.logger,
669
+ getSessionManager: (agentId)=>this.getSessionManager(agentId),
670
+ resolveConfigDirPath: ()=>this.resolveConfigDirPath(),
671
+ resolveOutputRoot: ()=>this.resolveOutputRoot(),
672
+ resolveDefaultOutputDir: (agentId)=>this.resolveDefaultOutputDir(agentId),
673
+ resolveAgentWorkspace: (agentId)=>this.resolveAgentWorkspace(agentId),
674
+ resolveFsRoots: ()=>this.resolveFsRoots(),
675
+ resolveFsPath: (path)=>this.resolveFsPath(path),
676
+ isPathWithinRoots: (path, roots)=>this.isPathWithinRoots(path, roots),
677
+ getBuiltInTools: ()=>this.getBuiltInTools()
678
+ };
679
+ }
680
+ persistWingmanConfig() {
681
+ const configDir = this.resolveConfigDirPath();
682
+ mkdirSync(configDir, {
683
+ recursive: true
684
+ });
685
+ const configPath = join(configDir, "wingman.config.json");
686
+ writeFileSync(configPath, JSON.stringify(this.wingmanConfig, null, 2));
687
+ }
688
+ getBuiltInTools() {
689
+ return [
690
+ "ls",
691
+ "read_file",
692
+ "write_file",
693
+ "edit_file",
694
+ "glob",
695
+ "grep"
696
+ ];
697
+ }
698
+ resolveOutputRoot() {
699
+ const root = join(this.resolveStateDir(), "outputs");
700
+ mkdirSync(root, {
701
+ recursive: true
702
+ });
703
+ return root;
704
+ }
705
+ resolveDefaultOutputDir(agentId) {
706
+ const root = this.resolveOutputRoot();
707
+ const dir = join(root, agentId);
708
+ mkdirSync(dir, {
709
+ recursive: true
710
+ });
711
+ return dir;
712
+ }
713
+ resolveFsRoots() {
714
+ const configured = this.wingmanConfig.gateway?.fsRoots || [];
715
+ const candidates = configured.length > 0 ? configured : [
716
+ this.resolveOutputRoot()
717
+ ];
718
+ const resolved = candidates.map((entry)=>this.resolveFsPath(entry)).filter((entry)=>{
719
+ try {
720
+ return existsSync(entry) && statSync(entry).isDirectory();
721
+ } catch {
722
+ return false;
723
+ }
724
+ });
725
+ return Array.from(new Set(resolved));
726
+ }
727
+ resolveFsPath(raw) {
728
+ if ("~" === raw) return normalize(homedir());
729
+ if (raw.startsWith("~/")) return normalize(join(homedir(), raw.slice(2)));
730
+ if (isAbsolute(raw)) return normalize(raw);
731
+ return normalize(join(this.workspace, raw));
732
+ }
733
+ isPathWithinRoots(pathname, roots) {
734
+ const normalized = normalize(pathname);
735
+ return roots.some((root)=>{
736
+ const normalizedRoot = normalize(root);
737
+ return normalized === normalizedRoot || normalized.startsWith(normalizedRoot + sep);
738
+ });
739
+ }
740
+ resolveControlUiDir() {
741
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
742
+ const candidates = [
743
+ join(this.workspace, "dist", "webui"),
744
+ join(moduleDir, "..", "webui"),
745
+ join(moduleDir, "..", "..", "dist", "webui")
746
+ ];
747
+ for (const candidate of candidates)try {
748
+ if (existsSync(candidate) && statSync(candidate).isDirectory() && existsSync(join(candidate, "index.html"))) return candidate;
749
+ } catch {
750
+ continue;
751
+ }
752
+ return null;
753
+ }
754
+ async getSessionManager(agentId) {
755
+ const existing = this.sessionManagers.get(agentId);
756
+ if (existing) return existing;
757
+ const stateDir = this.resolveStateDir();
758
+ const sessionsDir = join(stateDir, "agents", agentId, "sessions");
759
+ mkdirSync(sessionsDir, {
760
+ recursive: true
761
+ });
762
+ const dbPath = join(sessionsDir, "wingman.db");
763
+ const manager = new SessionManager(dbPath);
764
+ await manager.initialize();
765
+ this.sessionManagers.set(agentId, manager);
766
+ return manager;
767
+ }
768
+ handleHealthCheck() {
769
+ const stats = this.getStats();
770
+ const health = {
771
+ status: "healthy",
772
+ version: "1.0.0",
773
+ stats,
774
+ timestamp: Date.now()
775
+ };
776
+ return new Response(JSON.stringify(health, null, 2), {
777
+ headers: {
778
+ "Content-Type": "application/json"
779
+ }
780
+ });
781
+ }
782
+ handleStats() {
783
+ const stats = {
784
+ gateway: this.getStats(),
785
+ nodes: this.nodeManager.getStats(),
786
+ groups: this.groupManager.getStats()
787
+ };
788
+ return new Response(JSON.stringify(stats, null, 2), {
789
+ headers: {
790
+ "Content-Type": "application/json"
791
+ }
792
+ });
793
+ }
794
+ getStats() {
795
+ const nodeStats = this.nodeManager.getStats();
796
+ return {
797
+ uptime: Date.now() - this.startedAt,
798
+ totalNodes: nodeStats.totalNodes,
799
+ totalGroups: this.groupManager.getStats().totalGroups,
800
+ messagesProcessed: this.messagesProcessed,
801
+ startedAt: this.startedAt,
802
+ activeSessions: nodeStats.activeSessions,
803
+ sessionNodes: nodeStats.sessionNodes
804
+ };
805
+ }
806
+ startPingInterval() {
807
+ if (this.pingInterval) clearInterval(this.pingInterval);
808
+ this.pingInterval = setInterval(()=>{
809
+ const nodes = this.nodeManager.getAllNodes();
810
+ for (const node of nodes)this.sendMessage(node.ws, {
811
+ type: "ping",
812
+ timestamp: Date.now()
813
+ });
814
+ const removed = this.nodeManager.removeStaleNodes(this.config.pingTimeout);
815
+ if (removed > 0) this.log("info", `Removed ${removed} stale nodes`);
816
+ }, this.config.pingInterval);
817
+ }
818
+ log(level, message, data) {
819
+ if ("silent" === level) return;
820
+ const args = void 0 === data ? [] : [
821
+ data
822
+ ];
823
+ switch(level){
824
+ case "debug":
825
+ this.logger.debug(message, ...args);
826
+ break;
827
+ case "info":
828
+ this.logger.info(message, ...args);
829
+ break;
830
+ case "warn":
831
+ this.logger.warn(message, ...args);
832
+ break;
833
+ case "error":
834
+ this.logger.error(message, ...args);
835
+ break;
836
+ }
837
+ }
838
+ async handleUiRequest(req) {
839
+ const url = new URL(req.url);
840
+ const ctx = this.getHttpContext();
841
+ const webhookResponse = await handleWebhookInvoke(ctx, this.webhookStore, req, url);
842
+ if (webhookResponse) return webhookResponse;
843
+ if (url.pathname.startsWith("/api/")) {
844
+ if ("/api/config" === url.pathname) {
845
+ const agents = this.wingmanConfig.agents?.list?.map((agent)=>({
846
+ id: agent.id,
847
+ name: agent.name,
848
+ default: agent.default
849
+ })) || [];
850
+ const defaultAgentId = this.router.selectAgent();
851
+ return new Response(JSON.stringify({
852
+ gatewayHost: this.config.host,
853
+ gatewayPort: this.config.port,
854
+ requireAuth: this.auth.isAuthRequired(),
855
+ defaultAgentId,
856
+ outputRoot: this.resolveOutputRoot(),
857
+ voice: this.wingmanConfig.voice,
858
+ agents
859
+ }, null, 2), {
860
+ headers: {
861
+ "Content-Type": "application/json"
862
+ }
863
+ });
864
+ }
865
+ const apiResponse = await handleWebhooksApi(ctx, this.webhookStore, req, url) || await handleRoutinesApi(ctx, this.routineStore, req, url) || await handleAgentsApi(ctx, req, url) || await handleProvidersApi(ctx, req, url) || await handleVoiceApi(ctx, req, url) || await handleFsApi(ctx, req, url) || await handleSessionsApi(ctx, req, url);
866
+ if (apiResponse) return apiResponse;
867
+ if ("/api/health" === url.pathname) return this.handleHealthCheck();
868
+ if ("/api/stats" === url.pathname) return this.handleStats();
869
+ return new Response("Not Found", {
870
+ status: 404
871
+ });
872
+ }
873
+ if ("GET" !== req.method) return new Response("Method Not Allowed", {
874
+ status: 405
875
+ });
876
+ if ("/ui" === url.pathname || "/ui/" === url.pathname) return new Response(null, {
877
+ status: 302,
878
+ headers: {
879
+ Location: "/"
880
+ }
881
+ });
882
+ if (!this.uiDistDir) return new Response("Control UI build not found", {
883
+ status: 404
884
+ });
885
+ const relativePath = "/" === url.pathname || "/index.html" === url.pathname ? "index.html" : url.pathname.replace(/^\/+/, "");
886
+ const candidate = normalize(join(this.uiDistDir, relativePath));
887
+ if (!candidate.startsWith(this.uiDistDir)) return new Response("Invalid path", {
888
+ status: 400
889
+ });
890
+ if (!existsSync(candidate) || !statSync(candidate).isFile()) {
891
+ const isAssetRequest = relativePath.startsWith("assets/") || relativePath.includes(".");
892
+ if (!isAssetRequest) {
893
+ const indexPath = normalize(join(this.uiDistDir, "index.html"));
894
+ if (existsSync(indexPath) && statSync(indexPath).isFile()) return new Response(Bun.file(indexPath), {
895
+ headers: {
896
+ "Cache-Control": "no-store",
897
+ "Content-Type": "text/html; charset=utf-8"
898
+ }
899
+ });
900
+ }
901
+ return new Response("Not Found", {
902
+ status: 404
903
+ });
904
+ }
905
+ const file = Bun.file(candidate);
906
+ const headers = {
907
+ "Cache-Control": "no-store"
908
+ };
909
+ if ("index.html" === relativePath) headers["Content-Type"] = "text/html; charset=utf-8";
910
+ return new Response(file, {
911
+ headers
912
+ });
913
+ }
914
+ getAuth() {
915
+ return this.auth;
916
+ }
917
+ getConfig() {
918
+ return this.config;
919
+ }
920
+ async handleBridgeSend(req) {
921
+ try {
922
+ const message = await req.json();
923
+ const validation = validateGatewayMessage(message);
924
+ if (!validation.success) return new Response(JSON.stringify({
925
+ error: validation.error
926
+ }), {
927
+ status: 400,
928
+ headers: {
929
+ "Content-Type": "application/json"
930
+ }
931
+ });
932
+ const validatedMessage = validation.data;
933
+ if ("register" === validatedMessage.type) {
934
+ const payload = validatedMessage.payload;
935
+ const nodeId = this.generateNodeId();
936
+ this.bridgeQueues.set(nodeId, []);
937
+ const response = {
938
+ type: "registered",
939
+ nodeId,
940
+ timestamp: Date.now()
941
+ };
942
+ this.log("info", `HTTP bridge node registered: ${payload.name}`);
943
+ return new Response(JSON.stringify(response), {
944
+ status: 200,
945
+ headers: {
946
+ "Content-Type": "application/json"
947
+ }
948
+ });
949
+ }
950
+ const nodeId = validatedMessage.nodeId;
951
+ if (!nodeId) return new Response(JSON.stringify({
952
+ error: "nodeId required"
953
+ }), {
954
+ status: 400,
955
+ headers: {
956
+ "Content-Type": "application/json"
957
+ }
958
+ });
959
+ this.messagesProcessed++;
960
+ return new Response(JSON.stringify({
961
+ success: true
962
+ }), {
963
+ status: 200,
964
+ headers: {
965
+ "Content-Type": "application/json"
966
+ }
967
+ });
968
+ } catch (error) {
969
+ return new Response(JSON.stringify({
970
+ error: error instanceof Error ? error.message : "Unknown error"
971
+ }), {
972
+ status: 500,
973
+ headers: {
974
+ "Content-Type": "application/json"
975
+ }
976
+ });
977
+ }
978
+ }
979
+ async handleBridgePoll(req) {
980
+ try {
981
+ const nodeId = req.headers.get("X-Node-ID");
982
+ if (!nodeId) return new Response(JSON.stringify({
983
+ error: "X-Node-ID header required"
984
+ }), {
985
+ status: 400,
986
+ headers: {
987
+ "Content-Type": "application/json"
988
+ }
989
+ });
990
+ const queue = this.bridgeQueues.get(nodeId);
991
+ if (!queue) return new Response(JSON.stringify({
992
+ error: "Node not registered"
993
+ }), {
994
+ status: 404,
995
+ headers: {
996
+ "Content-Type": "application/json"
997
+ }
998
+ });
999
+ if (queue.length > 0) {
1000
+ const messages = [
1001
+ ...queue
1002
+ ];
1003
+ queue.length = 0;
1004
+ return new Response(JSON.stringify(messages), {
1005
+ status: 200,
1006
+ headers: {
1007
+ "Content-Type": "application/json"
1008
+ }
1009
+ });
1010
+ }
1011
+ const messages = await new Promise((resolve)=>{
1012
+ const timeout = setTimeout(()=>{
1013
+ this.bridgePollWaiters.delete(nodeId);
1014
+ resolve([]);
1015
+ }, 30000);
1016
+ this.bridgePollWaiters.set(nodeId, (msgs)=>{
1017
+ clearTimeout(timeout);
1018
+ resolve(msgs);
1019
+ });
1020
+ });
1021
+ return new Response(JSON.stringify(messages), {
1022
+ status: 200,
1023
+ headers: {
1024
+ "Content-Type": "application/json"
1025
+ }
1026
+ });
1027
+ } catch (error) {
1028
+ return new Response(JSON.stringify({
1029
+ error: error instanceof Error ? error.message : "Unknown error"
1030
+ }), {
1031
+ status: 500,
1032
+ headers: {
1033
+ "Content-Type": "application/json"
1034
+ }
1035
+ });
1036
+ }
1037
+ }
1038
+ generateNodeId() {
1039
+ return `node-${Date.now()}-${Math.random().toString(36).substring(7)}`;
1040
+ }
1041
+ constructor(config = {}){
1042
+ _define_property(this, "config", void 0);
1043
+ _define_property(this, "nodeManager", void 0);
1044
+ _define_property(this, "groupManager", void 0);
1045
+ _define_property(this, "auth", void 0);
1046
+ _define_property(this, "server", null);
1047
+ _define_property(this, "startedAt", 0);
1048
+ _define_property(this, "messagesProcessed", 0);
1049
+ _define_property(this, "pingInterval", null);
1050
+ _define_property(this, "discoveryService", null);
1051
+ _define_property(this, "logger", void 0);
1052
+ _define_property(this, "wingmanConfig", void 0);
1053
+ _define_property(this, "router", void 0);
1054
+ _define_property(this, "sessionManagers", new Map());
1055
+ _define_property(this, "workspace", void 0);
1056
+ _define_property(this, "configDir", void 0);
1057
+ _define_property(this, "uiServer", null);
1058
+ _define_property(this, "controlUiEnabled", false);
1059
+ _define_property(this, "controlUiPort", 18790);
1060
+ _define_property(this, "controlUiSamePort", false);
1061
+ _define_property(this, "uiDistDir", null);
1062
+ _define_property(this, "webhookStore", void 0);
1063
+ _define_property(this, "routineStore", void 0);
1064
+ _define_property(this, "internalHooks", null);
1065
+ _define_property(this, "discordAdapter", null);
1066
+ _define_property(this, "sessionSubscriptions", new Map());
1067
+ _define_property(this, "socketSubscriptions", new Map());
1068
+ _define_property(this, "connectedClients", new Set());
1069
+ _define_property(this, "bridgeQueues", new Map());
1070
+ _define_property(this, "bridgePollWaiters", new Map());
1071
+ this.workspace = config.workspace || process.cwd();
1072
+ this.configDir = config.configDir || ".wingman";
1073
+ const configLoader = new WingmanConfigLoader(this.configDir, this.workspace);
1074
+ this.wingmanConfig = configLoader.loadConfig();
1075
+ this.router = new GatewayRouter(this.wingmanConfig);
1076
+ this.webhookStore = createWebhookStore(()=>this.resolveConfigDirPath());
1077
+ this.routineStore = createRoutineStore(()=>this.resolveConfigDirPath());
1078
+ const gatewayDefaults = this.wingmanConfig.gateway;
1079
+ const envToken = getGatewayTokenFromEnv();
1080
+ const authFromConfig = config.auth?.mode === "token" ? {
1081
+ ...config.auth,
1082
+ token: config.auth.token ?? envToken
1083
+ } : config.auth;
1084
+ const legacyToken = config.authToken ?? envToken;
1085
+ const authConfig = authFromConfig ? authFromConfig : config.requireAuth || config.authToken ? {
1086
+ mode: "token",
1087
+ token: legacyToken
1088
+ } : gatewayDefaults.auth;
1089
+ const resolvedAuthToken = authConfig?.mode === "token" ? authConfig.token ?? legacyToken : void 0;
1090
+ this.config = {
1091
+ port: config.port ?? gatewayDefaults.port ?? 18789,
1092
+ host: config.host || gatewayDefaults.host || "127.0.0.1",
1093
+ authToken: resolvedAuthToken,
1094
+ requireAuth: config.requireAuth ?? false,
1095
+ auth: authConfig,
1096
+ stateDir: config.stateDir || gatewayDefaults.stateDir,
1097
+ workspace: this.workspace,
1098
+ configDir: this.configDir,
1099
+ maxNodes: config.maxNodes || 1000,
1100
+ pingInterval: config.pingInterval || 30000,
1101
+ pingTimeout: config.pingTimeout || 60000,
1102
+ logLevel: config.logLevel || "info",
1103
+ discovery: config.discovery
1104
+ };
1105
+ this.nodeManager = new NodeManager(this.config.maxNodes);
1106
+ this.groupManager = new BroadcastGroupManager();
1107
+ this.logger = createLogger(this.config.logLevel);
1108
+ const initialTokens = this.config.authToken ? [
1109
+ this.config.authToken
1110
+ ] : [];
1111
+ this.auth = new GatewayAuth(this.config.auth || {
1112
+ mode: "none"
1113
+ }, initialTokens);
1114
+ const controlUi = this.wingmanConfig.gateway?.controlUi;
1115
+ this.controlUiEnabled = controlUi?.enabled ?? false;
1116
+ this.controlUiPort = controlUi?.port || 18790;
1117
+ this.controlUiSamePort = this.controlUiEnabled && this.controlUiPort === this.config.port;
1118
+ this.uiDistDir = this.controlUiEnabled ? this.resolveControlUiDir() : null;
1119
+ }
1120
+ }
1121
+ function buildAttachmentPreview(attachments) {
1122
+ if (!attachments || 0 === attachments.length) return "Attachment";
1123
+ let hasAudio = false;
1124
+ let hasImage = false;
1125
+ for (const attachment of attachments)if (isAudioAttachment(attachment)) hasAudio = true;
1126
+ else hasImage = true;
1127
+ const count = attachments.length;
1128
+ if (hasAudio && hasImage) return count > 1 ? "Media attachments" : "Media attachment";
1129
+ if (hasAudio) return count > 1 ? "Audio attachments" : "Audio attachment";
1130
+ return count > 1 ? "Image attachments" : "Image attachment";
1131
+ }
1132
+ function isAudioAttachment(attachment) {
1133
+ if ("audio" === attachment.kind) return true;
1134
+ if (attachment.mimeType?.startsWith("audio/")) return true;
1135
+ if (attachment.dataUrl?.startsWith("data:audio/")) return true;
1136
+ return false;
1137
+ }
1138
+ export { GatewayServer };