skybridge 0.0.0-dev.e341b6a → 0.0.0-dev.e378aa9

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 (320) hide show
  1. package/README.md +178 -0
  2. package/dist/cli/detect-port.d.ts +18 -0
  3. package/dist/cli/detect-port.js +61 -0
  4. package/dist/cli/detect-port.js.map +1 -0
  5. package/dist/cli/header.d.ts +4 -0
  6. package/dist/cli/header.js +6 -0
  7. package/dist/cli/header.js.map +1 -0
  8. package/dist/cli/run-command.d.ts +2 -0
  9. package/dist/cli/run-command.js +43 -0
  10. package/dist/cli/run-command.js.map +1 -0
  11. package/dist/cli/telemetry.d.ts +7 -0
  12. package/dist/cli/telemetry.js +123 -0
  13. package/dist/cli/telemetry.js.map +1 -0
  14. package/dist/cli/tunnel-control-server.d.ts +9 -0
  15. package/dist/cli/tunnel-control-server.js +31 -0
  16. package/dist/cli/tunnel-control-server.js.map +1 -0
  17. package/dist/cli/tunnel-control-server.test.js +39 -0
  18. package/dist/cli/tunnel-control-server.test.js.map +1 -0
  19. package/dist/cli/tunnel-handler.d.ts +3 -0
  20. package/dist/cli/tunnel-handler.js +48 -0
  21. package/dist/cli/tunnel-handler.js.map +1 -0
  22. package/dist/cli/tunnel-handler.test.js +105 -0
  23. package/dist/cli/tunnel-handler.test.js.map +1 -0
  24. package/dist/cli/tunnel.d.ts +57 -0
  25. package/dist/cli/tunnel.js +154 -0
  26. package/dist/cli/tunnel.js.map +1 -0
  27. package/dist/cli/tunnel.test.js +190 -0
  28. package/dist/cli/tunnel.test.js.map +1 -0
  29. package/dist/cli/types.d.ts +5 -0
  30. package/dist/cli/types.js +2 -0
  31. package/dist/cli/types.js.map +1 -0
  32. package/dist/cli/use-execute-steps.d.ts +11 -0
  33. package/dist/cli/use-execute-steps.js +36 -0
  34. package/dist/cli/use-execute-steps.js.map +1 -0
  35. package/dist/cli/use-messages.d.ts +3 -0
  36. package/dist/cli/use-messages.js +11 -0
  37. package/dist/cli/use-messages.js.map +1 -0
  38. package/dist/cli/use-nodemon.d.ts +2 -0
  39. package/dist/cli/use-nodemon.js +64 -0
  40. package/dist/cli/use-nodemon.js.map +1 -0
  41. package/dist/cli/use-tunnel.d.ts +14 -0
  42. package/dist/cli/use-tunnel.js +131 -0
  43. package/dist/cli/use-tunnel.js.map +1 -0
  44. package/dist/cli/use-typescript-check.d.ts +9 -0
  45. package/dist/cli/use-typescript-check.js +94 -0
  46. package/dist/cli/use-typescript-check.js.map +1 -0
  47. package/dist/commands/build.d.ts +9 -0
  48. package/dist/commands/build.js +67 -0
  49. package/dist/commands/build.js.map +1 -0
  50. package/dist/commands/dev.d.ts +11 -0
  51. package/dist/commands/dev.js +73 -0
  52. package/dist/commands/dev.js.map +1 -0
  53. package/dist/commands/start.d.ts +9 -0
  54. package/dist/commands/start.js +49 -0
  55. package/dist/commands/start.js.map +1 -0
  56. package/dist/commands/telemetry/disable.d.ts +5 -0
  57. package/dist/commands/telemetry/disable.js +14 -0
  58. package/dist/commands/telemetry/disable.js.map +1 -0
  59. package/dist/commands/telemetry/enable.d.ts +5 -0
  60. package/dist/commands/telemetry/enable.js +14 -0
  61. package/dist/commands/telemetry/enable.js.map +1 -0
  62. package/dist/commands/telemetry/status.d.ts +5 -0
  63. package/dist/commands/telemetry/status.js +14 -0
  64. package/dist/commands/telemetry/status.js.map +1 -0
  65. package/dist/server/asset-base-url-transform-plugin.d.ts +10 -0
  66. package/dist/server/asset-base-url-transform-plugin.js +33 -0
  67. package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
  68. package/dist/server/asset-base-url-transform-plugin.test.d.ts +1 -0
  69. package/dist/server/asset-base-url-transform-plugin.test.js +84 -0
  70. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
  71. package/dist/server/content-helpers.d.ts +27 -0
  72. package/dist/server/content-helpers.js +46 -0
  73. package/dist/server/content-helpers.js.map +1 -0
  74. package/dist/server/content-helpers.test.d.ts +1 -0
  75. package/dist/server/content-helpers.test.js +70 -0
  76. package/dist/server/content-helpers.test.js.map +1 -0
  77. package/dist/server/express.d.ts +15 -0
  78. package/dist/server/express.js +95 -0
  79. package/dist/server/express.js.map +1 -0
  80. package/dist/server/express.test.d.ts +1 -0
  81. package/dist/server/express.test.js +348 -0
  82. package/dist/server/express.test.js.map +1 -0
  83. package/dist/server/index.d.ts +5 -3
  84. package/dist/server/index.js +3 -2
  85. package/dist/server/index.js.map +1 -1
  86. package/dist/server/inferUtilityTypes.d.ts +6 -6
  87. package/dist/server/metric.d.ts +14 -0
  88. package/dist/server/metric.js +62 -0
  89. package/dist/server/metric.js.map +1 -0
  90. package/dist/server/middleware.d.ts +124 -0
  91. package/dist/server/middleware.js +93 -0
  92. package/dist/server/middleware.js.map +1 -0
  93. package/dist/server/middleware.test-d.d.ts +1 -0
  94. package/dist/server/middleware.test-d.js +75 -0
  95. package/dist/server/middleware.test-d.js.map +1 -0
  96. package/dist/server/middleware.test.d.ts +1 -0
  97. package/dist/server/middleware.test.js +493 -0
  98. package/dist/server/middleware.test.js.map +1 -0
  99. package/dist/server/server.d.ts +137 -65
  100. package/dist/server/server.js +395 -89
  101. package/dist/server/server.js.map +1 -1
  102. package/dist/server/templateHelper.d.ts +5 -5
  103. package/dist/server/templates/development.hbs +3 -57
  104. package/dist/server/templates/production.hbs +2 -3
  105. package/dist/server/tunnel-proxy-router.d.ts +7 -0
  106. package/dist/server/tunnel-proxy-router.js +110 -0
  107. package/dist/server/tunnel-proxy-router.js.map +1 -0
  108. package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
  109. package/dist/server/tunnel-proxy-router.test.js +229 -0
  110. package/dist/server/tunnel-proxy-router.test.js.map +1 -0
  111. package/dist/server/viewsDevServer.d.ts +14 -0
  112. package/dist/server/viewsDevServer.js +45 -0
  113. package/dist/server/viewsDevServer.js.map +1 -0
  114. package/dist/test/utils.d.ts +13 -21
  115. package/dist/test/utils.js +42 -37
  116. package/dist/test/utils.js.map +1 -1
  117. package/dist/test/view.test.d.ts +1 -0
  118. package/dist/test/view.test.js +523 -0
  119. package/dist/test/view.test.js.map +1 -0
  120. package/dist/version.d.ts +1 -0
  121. package/dist/version.js +5 -0
  122. package/dist/version.js.map +1 -0
  123. package/dist/web/bridges/apps-sdk/adaptor.d.ts +24 -0
  124. package/dist/web/bridges/apps-sdk/adaptor.js +93 -0
  125. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -0
  126. package/dist/web/bridges/apps-sdk/bridge.d.ts +10 -0
  127. package/dist/web/bridges/{apps-sdk-bridge.js → apps-sdk/bridge.js} +2 -2
  128. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -0
  129. package/dist/web/bridges/apps-sdk/index.d.ts +5 -0
  130. package/dist/web/bridges/apps-sdk/index.js +5 -0
  131. package/dist/web/bridges/apps-sdk/index.js.map +1 -0
  132. package/dist/web/bridges/apps-sdk/types.d.ts +126 -0
  133. package/dist/web/bridges/apps-sdk/types.js +10 -0
  134. package/dist/web/bridges/apps-sdk/types.js.map +1 -0
  135. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +2 -0
  136. package/dist/web/bridges/{hooks/use-apps-sdk-bridge.js → apps-sdk/use-apps-sdk-context.js} +3 -3
  137. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -0
  138. package/dist/web/bridges/get-adaptor.d.ts +2 -0
  139. package/dist/web/bridges/get-adaptor.js +8 -0
  140. package/dist/web/bridges/get-adaptor.js.map +1 -0
  141. package/dist/web/bridges/index.d.ts +5 -4
  142. package/dist/web/bridges/index.js +5 -4
  143. package/dist/web/bridges/index.js.map +1 -1
  144. package/dist/web/bridges/mcp-app/adaptor.d.ts +48 -0
  145. package/dist/web/bridges/mcp-app/adaptor.js +268 -0
  146. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -0
  147. package/dist/web/bridges/mcp-app/bridge.d.ts +26 -0
  148. package/dist/web/bridges/mcp-app/bridge.js +102 -0
  149. package/dist/web/bridges/mcp-app/bridge.js.map +1 -0
  150. package/dist/web/bridges/mcp-app/index.d.ts +4 -0
  151. package/dist/web/bridges/mcp-app/index.js +4 -0
  152. package/dist/web/bridges/mcp-app/index.js.map +1 -0
  153. package/dist/web/bridges/mcp-app/types.d.ts +8 -0
  154. package/dist/web/bridges/mcp-app/types.js +2 -0
  155. package/dist/web/bridges/mcp-app/types.js.map +1 -0
  156. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +7 -0
  157. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +7 -0
  158. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -0
  159. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.d.ts +1 -0
  160. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +26 -0
  161. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -0
  162. package/dist/web/bridges/types.d.ts +79 -27
  163. package/dist/web/bridges/use-host-context.d.ts +2 -0
  164. package/dist/web/bridges/use-host-context.js +8 -0
  165. package/dist/web/bridges/use-host-context.js.map +1 -0
  166. package/dist/web/components/modal-provider.d.ts +4 -0
  167. package/dist/web/components/modal-provider.js +45 -0
  168. package/dist/web/components/modal-provider.js.map +1 -0
  169. package/dist/web/create-store.js +21 -8
  170. package/dist/web/create-store.js.map +1 -1
  171. package/dist/web/create-store.test.js +112 -53
  172. package/dist/web/create-store.test.js.map +1 -1
  173. package/dist/web/data-llm.d.ts +1 -1
  174. package/dist/web/data-llm.js +7 -5
  175. package/dist/web/data-llm.js.map +1 -1
  176. package/dist/web/data-llm.test.js +131 -65
  177. package/dist/web/data-llm.test.js.map +1 -1
  178. package/dist/web/generate-helpers.d.ts +22 -19
  179. package/dist/web/generate-helpers.js +20 -18
  180. package/dist/web/generate-helpers.js.map +1 -1
  181. package/dist/web/generate-helpers.test-d.js +26 -26
  182. package/dist/web/generate-helpers.test-d.js.map +1 -1
  183. package/dist/web/helpers/state.d.ts +2 -2
  184. package/dist/web/helpers/state.js +17 -12
  185. package/dist/web/helpers/state.js.map +1 -1
  186. package/dist/web/helpers/state.test.js +9 -9
  187. package/dist/web/helpers/state.test.js.map +1 -1
  188. package/dist/web/hooks/index.d.ts +3 -3
  189. package/dist/web/hooks/index.js +2 -2
  190. package/dist/web/hooks/index.js.map +1 -1
  191. package/dist/web/hooks/test/utils.d.ts +8 -2
  192. package/dist/web/hooks/test/utils.js +37 -13
  193. package/dist/web/hooks/test/utils.js.map +1 -1
  194. package/dist/web/hooks/use-call-tool.d.ts +3 -2
  195. package/dist/web/hooks/use-call-tool.js +2 -2
  196. package/dist/web/hooks/use-call-tool.js.map +1 -1
  197. package/dist/web/hooks/use-call-tool.test-d.js +1 -1
  198. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
  199. package/dist/web/hooks/use-call-tool.test.js +3 -3
  200. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  201. package/dist/web/hooks/use-display-mode.d.ts +3 -3
  202. package/dist/web/hooks/use-display-mode.js +3 -4
  203. package/dist/web/hooks/use-display-mode.js.map +1 -1
  204. package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
  205. package/dist/web/hooks/use-display-mode.test-d.js +8 -0
  206. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
  207. package/dist/web/hooks/use-files.d.ts +3 -6
  208. package/dist/web/hooks/use-files.js +5 -2
  209. package/dist/web/hooks/use-files.js.map +1 -1
  210. package/dist/web/hooks/use-files.test.js +28 -3
  211. package/dist/web/hooks/use-files.test.js.map +1 -1
  212. package/dist/web/hooks/use-layout.d.ts +2 -2
  213. package/dist/web/hooks/use-layout.js +4 -4
  214. package/dist/web/hooks/use-layout.js.map +1 -1
  215. package/dist/web/hooks/use-layout.test.js +7 -6
  216. package/dist/web/hooks/use-layout.test.js.map +1 -1
  217. package/dist/web/hooks/use-open-external.d.ts +3 -1
  218. package/dist/web/hooks/use-open-external.js +3 -3
  219. package/dist/web/hooks/use-open-external.js.map +1 -1
  220. package/dist/web/hooks/use-open-external.test.js +27 -12
  221. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  222. package/dist/web/hooks/use-request-modal.d.ts +3 -3
  223. package/dist/web/hooks/use-request-modal.js +10 -8
  224. package/dist/web/hooks/use-request-modal.js.map +1 -1
  225. package/dist/web/hooks/use-request-modal.test.js +5 -1
  226. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  227. package/dist/web/hooks/use-send-follow-up-message.js +2 -2
  228. package/dist/web/hooks/use-set-open-in-app-url.d.ts +1 -0
  229. package/dist/web/hooks/use-set-open-in-app-url.js +8 -0
  230. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
  231. package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
  232. package/dist/web/hooks/use-set-open-in-app-url.test.js +43 -0
  233. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
  234. package/dist/web/hooks/use-tool-info.js +4 -4
  235. package/dist/web/hooks/use-tool-info.js.map +1 -1
  236. package/dist/web/hooks/use-tool-info.test.js +5 -5
  237. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  238. package/dist/web/hooks/use-user.d.ts +1 -1
  239. package/dist/web/hooks/use-user.js +20 -4
  240. package/dist/web/hooks/use-user.js.map +1 -1
  241. package/dist/web/hooks/use-user.test.js +33 -4
  242. package/dist/web/hooks/use-user.test.js.map +1 -1
  243. package/dist/web/hooks/use-view-state.d.ts +4 -0
  244. package/dist/web/hooks/use-view-state.js +32 -0
  245. package/dist/web/hooks/use-view-state.js.map +1 -0
  246. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  247. package/dist/web/hooks/use-view-state.test.js +177 -0
  248. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  249. package/dist/web/index.d.ts +1 -2
  250. package/dist/web/index.js +1 -2
  251. package/dist/web/index.js.map +1 -1
  252. package/dist/web/mount-view.d.ts +1 -0
  253. package/dist/web/{mount-widget.js → mount-view.js} +11 -3
  254. package/dist/web/mount-view.js.map +1 -0
  255. package/dist/web/plugin/plugin.d.ts +4 -1
  256. package/dist/web/plugin/plugin.js +142 -18
  257. package/dist/web/plugin/plugin.js.map +1 -1
  258. package/dist/web/plugin/scan-views.d.ts +16 -0
  259. package/dist/web/plugin/scan-views.js +88 -0
  260. package/dist/web/plugin/scan-views.js.map +1 -0
  261. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  262. package/dist/web/plugin/scan-views.test.js +99 -0
  263. package/dist/web/plugin/scan-views.test.js.map +1 -0
  264. package/dist/web/plugin/transform-data-llm.js +1 -1
  265. package/dist/web/plugin/transform-data-llm.js.map +1 -1
  266. package/dist/web/plugin/validate-view.d.ts +1 -0
  267. package/dist/web/plugin/validate-view.js +9 -0
  268. package/dist/web/plugin/validate-view.js.map +1 -0
  269. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  270. package/dist/web/plugin/validate-view.test.js +24 -0
  271. package/dist/web/plugin/validate-view.test.js.map +1 -0
  272. package/dist/web/proxy.js +0 -1
  273. package/dist/web/proxy.js.map +1 -1
  274. package/dist/web/types.d.ts +0 -133
  275. package/dist/web/types.js +0 -9
  276. package/dist/web/types.js.map +1 -1
  277. package/package.json +46 -27
  278. package/tsconfig.base.json +33 -0
  279. package/dist/server/widgetsDevServer.d.ts +0 -12
  280. package/dist/server/widgetsDevServer.js +0 -47
  281. package/dist/server/widgetsDevServer.js.map +0 -1
  282. package/dist/test/widget.test.js +0 -250
  283. package/dist/test/widget.test.js.map +0 -1
  284. package/dist/web/bridges/adaptors/apps-sdk-adaptor.d.ts +0 -13
  285. package/dist/web/bridges/adaptors/apps-sdk-adaptor.js +0 -33
  286. package/dist/web/bridges/adaptors/apps-sdk-adaptor.js.map +0 -1
  287. package/dist/web/bridges/adaptors/mcp-app-adaptor.d.ts +0 -16
  288. package/dist/web/bridges/adaptors/mcp-app-adaptor.js +0 -115
  289. package/dist/web/bridges/adaptors/mcp-app-adaptor.js.map +0 -1
  290. package/dist/web/bridges/apps-sdk-bridge.d.ts +0 -10
  291. package/dist/web/bridges/apps-sdk-bridge.js.map +0 -1
  292. package/dist/web/bridges/hooks/use-adaptor.d.ts +0 -2
  293. package/dist/web/bridges/hooks/use-adaptor.js +0 -8
  294. package/dist/web/bridges/hooks/use-adaptor.js.map +0 -1
  295. package/dist/web/bridges/hooks/use-apps-sdk-bridge.d.ts +0 -2
  296. package/dist/web/bridges/hooks/use-apps-sdk-bridge.js.map +0 -1
  297. package/dist/web/bridges/hooks/use-bridge.d.ts +0 -2
  298. package/dist/web/bridges/hooks/use-bridge.js +0 -8
  299. package/dist/web/bridges/hooks/use-bridge.js.map +0 -1
  300. package/dist/web/bridges/hooks/use-mcp-app-bridge.d.ts +0 -5
  301. package/dist/web/bridges/hooks/use-mcp-app-bridge.js +0 -7
  302. package/dist/web/bridges/hooks/use-mcp-app-bridge.js.map +0 -1
  303. package/dist/web/bridges/hooks/use-mcp-app-bridge.test.js +0 -41
  304. package/dist/web/bridges/hooks/use-mcp-app-bridge.test.js.map +0 -1
  305. package/dist/web/bridges/mcp-app-bridge.d.ts +0 -38
  306. package/dist/web/bridges/mcp-app-bridge.js +0 -162
  307. package/dist/web/bridges/mcp-app-bridge.js.map +0 -1
  308. package/dist/web/hooks/use-openai-global.d.ts +0 -3
  309. package/dist/web/hooks/use-openai-global.js +0 -6
  310. package/dist/web/hooks/use-openai-global.js.map +0 -1
  311. package/dist/web/hooks/use-widget-state.d.ts +0 -4
  312. package/dist/web/hooks/use-widget-state.js +0 -32
  313. package/dist/web/hooks/use-widget-state.js.map +0 -1
  314. package/dist/web/hooks/use-widget-state.test.js +0 -61
  315. package/dist/web/hooks/use-widget-state.test.js.map +0 -1
  316. package/dist/web/mount-widget.d.ts +0 -1
  317. package/dist/web/mount-widget.js.map +0 -1
  318. /package/dist/{test/widget.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
  319. /package/dist/{web/bridges/hooks/use-mcp-app-bridge.test.d.ts → cli/tunnel-handler.test.d.ts} +0 -0
  320. /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel.test.d.ts} +0 -0
package/README.md ADDED
@@ -0,0 +1,178 @@
1
+ <p align="center">
2
+ <a href="https://skybridge.tech">
3
+ <img alt="Skybridge" src="docs/images/skybridge-readme-banner-new.png" width="100%" />
4
+ </a>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>The full-stack React framework for MCP Apps and MCP Servers.</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://docs.skybridge.tech">Documentation</a> ·
13
+ <a href="https://docs.skybridge.tech/quickstart/create-new-app">Quickstart</a> ·
14
+ <a href="https://github.com/alpic-ai/skybridge/tree/main/examples">Examples</a> ·
15
+ <a href="https://docs.skybridge.tech/showcase">Showcase</a>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <img alt="Skybridge app preview" src="docs/images/skybridge-readme-preview.png" width="520" />
20
+ </p>
21
+
22
+ <p align="center">
23
+ <a href="https://www.npmjs.com/package/skybridge">
24
+ <picture>
25
+ <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/v/skybridge?color=77F5EE&amp;labelColor=161B22&amp;style=for-the-badge">
26
+ <img alt="npm version" src="https://img.shields.io/npm/v/skybridge?color=E3FAF7&amp;labelColor=F6F8FA&amp;style=for-the-badge">
27
+ </picture>
28
+ </a>
29
+ <a href="https://www.npmjs.com/package/skybridge">
30
+ <picture>
31
+ <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/dm/skybridge?color=D7FFC8&amp;labelColor=161B22&amp;style=for-the-badge">
32
+ <img alt="npm downloads" src="https://img.shields.io/npm/dm/skybridge?color=E8FBD9&amp;labelColor=F6F8FA&amp;style=for-the-badge">
33
+ </picture>
34
+ </a>
35
+ <a href="https://discord.com/invite/gNAazGueab">
36
+ <picture>
37
+ <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/Discord-community-77F5EE?style=for-the-badge&amp;logo=discord&amp;logoColor=77F5EE&amp;labelColor=161B22">
38
+ <img alt="Discord community" src="https://img.shields.io/badge/Discord-community-E3FAF7?style=for-the-badge&amp;logo=discord&amp;logoColor=5865F2&amp;labelColor=F6F8FA">
39
+ </picture>
40
+ </a>
41
+ <a href="https://github.com/alpic-ai/skybridge/blob/main/LICENSE">
42
+ <picture>
43
+ <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/license/alpic-ai/skybridge?color=D7FFC8&amp;labelColor=161B22&amp;style=for-the-badge">
44
+ <img alt="License: MIT" src="https://img.shields.io/github/license/alpic-ai/skybridge?color=E8FBD9&amp;labelColor=F6F8FA&amp;style=for-the-badge">
45
+ </picture>
46
+ </a>
47
+ </p>
48
+
49
+ ## Why Skybridge?
50
+
51
+ MCP Apps is an extension of the [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro) adding **rich, interactive UI views** to MCP servers. Building conversational Apps requires new UX design concepts, developer tooling and abstractions to build apps that make interactions between the user, the UI and the model seamless.
52
+
53
+ Skybridge is the answer to all the problems we ran into while building hundreds of production MCP Apps. Features include:
54
+
55
+ - **Delightful dev environment**: It provides a dev server with a local emulator, Hot Module Reload, and a permanent tunnel to connect your local app to Claude and ChatGPT.
56
+ - **Write once, run everywhere**: Skybridge abstracts implementation differences between MCP Clients, so your app runs seamlessly in Claude, ChatGPT, VSCode, and any other compatible MCP Apps client.
57
+ - **Agent-ready**: Powerful Skills, CLI, and programmatic DevTools APIs: it provides everything your coding agent needs to build MCP Apps end-to-end.
58
+ - **Type-safe end-to-end**: tRPC-style inference from MCP server tool definition to React view for type-safety end-to-end from server to frontend.
59
+ - **React-first**: Intuitive React Query-style hooks, with advanced state management.
60
+ - **Examples library**: Get started quickly with production-ready app examples for e-commerce, travel, SaaS, and others.
61
+
62
+ These companies chose Skybridge to deploy their apps on ChatGPT and Claude stores:
63
+
64
+ <p align="center">
65
+ <a href="https://www.datadoghq.com">
66
+ <picture>
67
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/user-logos/datadog-dark.svg">
68
+ <img src="docs/images/user-logos/datadog-light.svg" alt="Datadog" height="24">
69
+ </picture>
70
+ </a>
71
+ &nbsp;&nbsp;
72
+ <a href="https://bitmovin.com">
73
+ <picture>
74
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/user-logos/bitmovin-dark.svg">
75
+ <img src="docs/images/user-logos/bitmovin-light.svg" alt="Bitmovin" height="22">
76
+ </picture>
77
+ </a>
78
+ &nbsp;&nbsp;
79
+ <a href="https://www.evaneos.com">
80
+ <picture>
81
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/user-logos/evaneos-dark.svg">
82
+ <img src="docs/images/user-logos/evaneos-light.svg" alt="Evaneos" height="18">
83
+ </picture>
84
+ </a>
85
+ &nbsp;&nbsp;
86
+ <a href="https://www.touchstream.media">
87
+ <picture>
88
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/user-logos/touchstream-dark.svg">
89
+ <img src="docs/images/user-logos/touchstream-light.svg" alt="Touchstream" height="24">
90
+ </picture>
91
+ </a>
92
+ &nbsp;&nbsp;
93
+ <a href="https://www.cottages.com">
94
+ <picture>
95
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/user-logos/cottages-dark.svg">
96
+ <img src="docs/images/user-logos/cottages-light.svg" alt="Cottages.com" height="24">
97
+ </picture>
98
+ </a>
99
+ </p>
100
+
101
+ ## Get started
102
+
103
+ **For agents**
104
+
105
+ Install our [Skill](https://docs.skybridge.tech/devtools/skills) for MCP Apps and ChatGPT Apps:
106
+ ```bash
107
+ npx skills add alpic-ai/skybridge -s skybridge
108
+ ```
109
+ Once installed, if you ask your agent "_what skills do you have?_", it should mention the skybridge skill. Then, you can ask it to:
110
+
111
+ - _Create a new MCP App_
112
+ - _Migrate my MCP Server to Skybridge framework_
113
+ - _Add a new view to my MCP App_
114
+
115
+ **For humans**
116
+
117
+ Bootstrap a new project with:
118
+ ```bash
119
+ npm create skybridge my-app
120
+ ```
121
+ For full install instructions, read the [**Quickstart section**](https://docs.skybridge.tech/quickstart/create-new-app) of our documentation.
122
+
123
+ ## Documentation
124
+
125
+ The [Skybridge documentation](https://docs.skybridge.tech) covers the full lifecycle of building MCP Apps:
126
+
127
+ - [Fundamentals](https://docs.skybridge.tech/fundamentals): understand MCP Apps, ChatGPT Apps, and how Skybridge bridges both runtimes.
128
+ - [Core concepts](https://docs.skybridge.tech/concepts): learn data flow, LLM context sync, type safety, and fast local iteration.
129
+ - [Guides](https://docs.skybridge.tech/guides/fetching-data): build real app behavior with tools, views, state, and model communication.
130
+ - [API Reference](https://docs.skybridge.tech/api-reference): browse server APIs, React hooks, CLI commands, and runtime compatibility.
131
+
132
+ ## Deploy
133
+
134
+ Deploy Skybridge apps instantly on [Alpic](https://alpic.ai) to get scalable hosting, MCP Analytics, permanent tunnel, MCP auditing and app stores submission help, or self-host on any Node.js-compatible platform.
135
+
136
+ Read the [deployment guide](https://docs.skybridge.tech/quickstart/deploy) for the full production path.
137
+
138
+ ## Example templates
139
+
140
+ Explore all our example templates in the [Examples](https://docs.skybridge.tech/examples) section of the documentation.
141
+
142
+ | Preview | App | Description | Demo | Code |
143
+ | --- | --- | --- | --- | --- |
144
+ | <img src="docs/images/showcase-capitals.png" alt="Capitals Explorer" width="160" /> | Capitals Explorer | Interactive world map with geolocation, country information, and dynamic capital exploration. | [Try Demo](https://capitals.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/capitals) |
145
+ | <img src="docs/images/showcase-flight-booking.png" alt="Flight Booking" width="160" /> | Flight Booking | Flight search carousel with route details, pricing comparison, and external booking. | [Try Demo](https://flight-booking.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/flight-booking) |
146
+ | <img src="docs/images/showcase-ecommerce.png" alt="Ecommerce Carousel" width="160" /> | Ecommerce Carousel | Product carousel with persistent cart, localization, theme switching, and modal dialogs. | [Try Demo](https://ecommerce.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/ecom-carousel) |
147
+ | <img src="docs/images/showcase-example.png" alt="Everything" width="160" /> | Everything | Comprehensive playground showcasing all Skybridge hooks and features. | [Try Demo](https://everything.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/everything) |
148
+ | <img src="docs/images/showcase-investigation-game.png" alt="Investigation Game" width="160" /> | Investigation Game | Multi-screen mystery game with fullscreen mode and dynamic story progression. | [Try Demo](https://investigation-game.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/investigation-game) |
149
+ | <img src="docs/images/showcase-productivity.png" alt="Productivity" width="160" /> | Productivity | Data visualization dashboard demonstrating Skybridge capabilities for MCP Apps. | [Try Demo](https://productivity.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/productivity) |
150
+ | <img src="docs/images/showcase-times-up.png" alt="Time's Up" width="160" /> | Time's Up | Word-guessing party game where the user gives hints and the AI tries to guess. | [Try Demo](https://times-up.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/times-up) |
151
+ | <img src="docs/images/showcase-manifest-ui.png" alt="Manifest UI" width="160" /> | Manifest UI | Agentic component library example for rich AI-powered experiences. | [Try Demo](https://manifest-ui.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/manifest-ui) |
152
+ | <img src="docs/images/showcase-generative-ui.png" alt="Generative UI" width="160" /> | Generative UI | LLM-generated dynamic UIs with json-render and 36 pre-built shadcn/ui components. | [Try Demo](https://generative-ui.skybridge.tech/try) | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/generative-ui) |
153
+ | <img src="docs/images/showcase-magic-8-ball.png" alt="Magic 8-Ball" width="160" /> | Magic 8-Ball | Default starter app for the core Skybridge loop: input, server logic, and view rendering. | [Try Demo](https://magic-8-ball.skybridge.tech/try) | [View code](https://github.com/alpic-ai/apps-sdk-template) |
154
+ | <img src="docs/images/showcase-clerk.png" alt="Auth Clerk" width="160" /> | Auth — Clerk | Full OAuth authentication with Clerk and personalized coffee shop search. | — | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-clerk) |
155
+ | <img src="docs/images/showcase-workos.png" alt="Auth WorkOS AuthKit" width="160" /> | Auth — WorkOS AuthKit | Full OAuth authentication with WorkOS AuthKit and personalized coffee shop search. | — | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-workos) |
156
+ | <img src="docs/images/showcase-stytch.png" alt="Auth Stytch" width="160" /> | Auth — Stytch | Full OAuth authentication with Stytch and personalized coffee shop search. | — | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-stytch) |
157
+ | <img src="docs/images/showcase-auth0.png" alt="Auth Auth0" width="160" /> | Auth — Auth0 | Full OAuth authentication with Auth0 and personalized coffee shop search. | — | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-auth0) |
158
+ | <img src="docs/images/showcase-lumo.png" alt="Lumo Interactive AI Tutor" width="160" /> | Lumo — Interactive AI Tutor | Adaptive tutor with Mermaid diagrams, mind maps, quizzes, and fill-in-the-blank exercises. | [Try Demo](https://lumo-mcp-app-39519fdd.alpic.live/try) | [View code](https://github.com/connorads/lumo-mcp-app) |
159
+
160
+ ## Community & Contributing
161
+
162
+ We invite you to contribute and help improve Skybridge.
163
+
164
+ Here are a few ways you can get involved:
165
+
166
+ - **Reporting bugs**: If you run into a bug or unexpected behavior, please open an issue in [GitHub Issues](https://github.com/alpic-ai/skybridge/issues) with a clear reproduction.
167
+ - **Questions and Suggestions**: Need help building with Skybridge or have ideas to improve the framework, docs, examples, or developer experience? [Open an issue](https://github.com/alpic-ai/skybridge/issues) or share them on our [Discord](https://discord.com/invite/gNAazGueab).
168
+ - **Pull requests**: For code or documentation changes, please read the [Contributing Guide](https://github.com/alpic-ai/skybridge/blob/main/CONTRIBUTING.md) before opening a PR.
169
+
170
+ Skybridge is released under the [MIT License](https://github.com/alpic-ai/skybridge/blob/main/LICENSE).
171
+
172
+ ### Contributors
173
+
174
+ Built and maintained by [Harijoe](https://github.com/harijoe), [Fred Barthelet](https://github.com/fredericbarthelet), and the [Alpic](https://alpic.ai) team.
175
+
176
+ <a href="https://github.com/alpic-ai/skybridge/graphs/contributors">
177
+ <img src="https://contrib.rocks/image?repo=alpic-ai/skybridge" alt="Skybridge contributors">
178
+ </a>
@@ -0,0 +1,18 @@
1
+ export declare function resolvePort(flagPort?: number): Promise<{
2
+ port: number;
3
+ fallback: boolean;
4
+ envWarning?: undefined;
5
+ } | {
6
+ port: number;
7
+ fallback: boolean;
8
+ envWarning: string;
9
+ }>;
10
+ /**
11
+ * Returns the given port if available, otherwise lets the OS
12
+ * pick a free port via `listen(0)`.
13
+ *
14
+ * @param host - Bind address for the check. Pass `"localhost"` for
15
+ * services that bind to 127.0.0.1 (e.g. Vite HMR). Omit for
16
+ * services that bind to all interfaces (e.g. the HTTP server).
17
+ */
18
+ export declare function detectAvailablePort(startPort: number, host?: string): Promise<number>;
@@ -0,0 +1,61 @@
1
+ import net from "node:net";
2
+ const DEFAULT_PORT = 3000;
3
+ export async function resolvePort(flagPort) {
4
+ if (flagPort && flagPort > 1) {
5
+ return { port: flagPort, fallback: false };
6
+ }
7
+ const rawEnv = process.env.PORT;
8
+ if (rawEnv) {
9
+ const parsed = Number(rawEnv);
10
+ if (Number.isInteger(parsed) && parsed > 0) {
11
+ return { port: parsed, fallback: false };
12
+ }
13
+ return {
14
+ port: await detectAvailablePort(DEFAULT_PORT),
15
+ fallback: false,
16
+ envWarning: `Invalid PORT="${rawEnv}", ignoring and using default`,
17
+ };
18
+ }
19
+ const port = await detectAvailablePort(DEFAULT_PORT);
20
+ return { port, fallback: port !== DEFAULT_PORT };
21
+ }
22
+ /**
23
+ * Returns the given port if available, otherwise lets the OS
24
+ * pick a free port via `listen(0)`.
25
+ *
26
+ * @param host - Bind address for the check. Pass `"localhost"` for
27
+ * services that bind to 127.0.0.1 (e.g. Vite HMR). Omit for
28
+ * services that bind to all interfaces (e.g. the HTTP server).
29
+ */
30
+ export async function detectAvailablePort(startPort, host) {
31
+ const available = await isPortAvailable(startPort, host);
32
+ if (available) {
33
+ return startPort;
34
+ }
35
+ return new Promise((resolve, reject) => {
36
+ const server = net.createServer();
37
+ server.once("error", reject);
38
+ server.once("listening", () => {
39
+ const addr = server.address();
40
+ if (addr && typeof addr === "object") {
41
+ const { port } = addr;
42
+ server.close(() => resolve(port));
43
+ }
44
+ else {
45
+ server.close(() => reject(new Error("Failed to detect available port")));
46
+ }
47
+ });
48
+ server.listen(0, host);
49
+ });
50
+ }
51
+ function isPortAvailable(port, host) {
52
+ return new Promise((resolve) => {
53
+ const server = net.createServer();
54
+ server.once("error", () => resolve(false));
55
+ server.once("listening", () => {
56
+ server.close(() => resolve(true));
57
+ });
58
+ server.listen(port, host);
59
+ });
60
+ }
61
+ //# sourceMappingURL=detect-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-port.js","sourceRoot":"","sources":["../../src/cli/detect-port.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAiB;IACjD,IAAI,QAAQ,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7C,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IAChC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC3C,CAAC;QACD,OAAO;YACL,IAAI,EAAE,MAAM,mBAAmB,CAAC,YAAY,CAAC;YAC7C,QAAQ,EAAE,KAAK;YACf,UAAU,EAAE,iBAAiB,MAAM,+BAA+B;SACnE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC,YAAY,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAK,YAAY,EAAE,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,SAAiB,EACjB,IAAa;IAEb,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACzD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;QAElC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;YAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;gBACtB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACpC,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAChB,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,CACrD,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,IAAa;IAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;QAElC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;YAC5B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const Header: ({ version, children, }: {
2
+ version: string;
3
+ children?: React.ReactNode;
4
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ export const Header = ({ version, children, }) => {
4
+ return (_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\u26F0", " ", "Skybridge"] }), _jsxs(Text, { color: "cyan", children: [" v", version] }), children] }));
5
+ };
6
+ //# sourceMappingURL=header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/cli/header.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACrB,OAAO,EACP,QAAQ,GAIT,EAAE,EAAE;IACH,OAAO,CACL,MAAC,GAAG,IAAC,YAAY,EAAE,CAAC,aAClB,MAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,6BACnB,IAAI,iBACD,EACP,MAAC,IAAI,IAAC,KAAK,EAAC,MAAM,mBAAI,OAAO,IAAQ,EACpC,QAAQ,IACL,CACP,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type SpawnOptions } from "node:child_process";
2
+ export declare function runCommand(command: string, options?: SpawnOptions): Promise<void>;
@@ -0,0 +1,43 @@
1
+ import { spawn } from "node:child_process";
2
+ export function runCommand(command, options = {
3
+ stdio: ["ignore", "inherit", "inherit"],
4
+ }) {
5
+ return new Promise((resolve, reject) => {
6
+ const stdoutChunks = [];
7
+ const stderrChunks = [];
8
+ const proc = spawn(command, {
9
+ ...options,
10
+ shell: true,
11
+ });
12
+ if (proc.stdout) {
13
+ proc.stdout.on("data", (chunk) => {
14
+ stdoutChunks.push(chunk);
15
+ });
16
+ }
17
+ if (proc.stderr) {
18
+ proc.stderr.on("data", (chunk) => {
19
+ stderrChunks.push(chunk);
20
+ });
21
+ }
22
+ proc.on("close", (code) => {
23
+ if (code === 0) {
24
+ resolve();
25
+ }
26
+ else {
27
+ const stdoutOutput = Buffer.concat(stdoutChunks).toString();
28
+ const stderrOutput = Buffer.concat(stderrChunks).toString();
29
+ const allOutput = [stdoutOutput, stderrOutput]
30
+ .filter((output) => output.trim())
31
+ .join("\n");
32
+ const errorMessage = allOutput
33
+ ? `Command failed with exit code ${code}\n${allOutput}`
34
+ : `Command failed with exit code ${code}`;
35
+ reject(new Error(errorMessage));
36
+ }
37
+ });
38
+ proc.on("error", (error) => {
39
+ reject(error);
40
+ });
41
+ });
42
+ }
43
+ //# sourceMappingURL=run-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-command.js","sourceRoot":"","sources":["../../src/cli/run-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,UAAU,UAAU,CACxB,OAAe,EACf,UAAwB;IACtB,KAAK,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC;CACxC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE;YAC1B,GAAG,OAAO;YACV,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC/B,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC/B,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC5D,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC5D,MAAM,SAAS,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC;qBAC3C,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;qBACjC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACd,MAAM,YAAY,GAAG,SAAS;oBAC5B,CAAC,CAAC,iCAAiC,IAAI,KAAK,SAAS,EAAE;oBACvD,CAAC,CAAC,iCAAiC,IAAI,EAAE,CAAC;gBAC5C,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;YAClC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACzB,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Hook } from "@oclif/core";
2
+ export declare function isEnabled(): boolean;
3
+ export declare function isDebugMode(): boolean;
4
+ export declare function setEnabled(enabled: boolean): void;
5
+ export declare function getMachineId(): string;
6
+ declare const hook: Hook<"finally">;
7
+ export default hook;
@@ -0,0 +1,123 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import ci from "ci-info";
5
+ import { PostHog } from "posthog-node";
6
+ const POSTHOG_API_KEY = "phc_rQdkCYr0DO4NcZBQXZnUwsHAbau9zuNwKIpil9FQP6v";
7
+ const POSTHOG_HOST = "https://us.i.posthog.com";
8
+ const ENV_TELEMETRY_DISABLED = "SKYBRIDGE_TELEMETRY_DISABLED";
9
+ const ENV_TELEMETRY_DEBUG = "SKYBRIDGE_TELEMETRY_DEBUG";
10
+ const ENV_DO_NOT_TRACK = "DO_NOT_TRACK";
11
+ const GLOBAL_CONFIG_DIR = join(homedir(), ".skybridge");
12
+ const GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, "config.json");
13
+ let posthogClient = null;
14
+ function getPostHogClient() {
15
+ if (!posthogClient) {
16
+ posthogClient = new PostHog(POSTHOG_API_KEY, {
17
+ host: POSTHOG_HOST,
18
+ flushAt: 1,
19
+ flushInterval: 0,
20
+ });
21
+ }
22
+ return posthogClient;
23
+ }
24
+ function readJsonFile(filePath) {
25
+ try {
26
+ if (existsSync(filePath)) {
27
+ const content = readFileSync(filePath, "utf-8");
28
+ return JSON.parse(content);
29
+ }
30
+ }
31
+ catch {
32
+ // Ignore errors reading config
33
+ }
34
+ return null;
35
+ }
36
+ function writeJsonFile(filePath, data) {
37
+ try {
38
+ const dir = join(filePath, "..");
39
+ if (!existsSync(dir)) {
40
+ mkdirSync(dir, { recursive: true });
41
+ }
42
+ writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
43
+ }
44
+ catch {
45
+ // Ignore errors writing config
46
+ }
47
+ }
48
+ function getGlobalConfig() {
49
+ const existing = readJsonFile(GLOBAL_CONFIG_FILE);
50
+ if (existing?.machineId && existing?.telemetry !== undefined) {
51
+ return existing;
52
+ }
53
+ const config = {
54
+ machineId: existing?.machineId || crypto.randomUUID(),
55
+ telemetry: {
56
+ enabled: existing?.telemetry?.enabled ?? true,
57
+ },
58
+ };
59
+ writeJsonFile(GLOBAL_CONFIG_FILE, config);
60
+ return config;
61
+ }
62
+ export function isEnabled() {
63
+ if (process.env[ENV_TELEMETRY_DISABLED] === "1" ||
64
+ process.env[ENV_TELEMETRY_DISABLED]?.toLowerCase() === "true") {
65
+ return false;
66
+ }
67
+ if (process.env[ENV_DO_NOT_TRACK] === "1" ||
68
+ process.env[ENV_DO_NOT_TRACK]?.toLowerCase() === "true") {
69
+ return false;
70
+ }
71
+ if (ci.isCI) {
72
+ return true;
73
+ }
74
+ const config = getGlobalConfig();
75
+ return config.telemetry.enabled;
76
+ }
77
+ export function isDebugMode() {
78
+ return (process.env[ENV_TELEMETRY_DEBUG] === "1" ||
79
+ process.env[ENV_TELEMETRY_DEBUG]?.toLowerCase() === "true");
80
+ }
81
+ export function setEnabled(enabled) {
82
+ const config = getGlobalConfig();
83
+ config.telemetry.enabled = enabled;
84
+ writeJsonFile(GLOBAL_CONFIG_FILE, config);
85
+ }
86
+ export function getMachineId() {
87
+ if (ci.isCI) {
88
+ return ci.name ?? "unknown-ci";
89
+ }
90
+ return getGlobalConfig().machineId;
91
+ }
92
+ const hook = async ({ id: command, config: { version }, error, }) => {
93
+ if (!isEnabled()) {
94
+ return;
95
+ }
96
+ const event = {
97
+ version,
98
+ machineId: getMachineId(),
99
+ sessionId: crypto.randomUUID(),
100
+ isCI: ci.isCI,
101
+ nodeVersion: process.version,
102
+ platform: process.platform,
103
+ outcome: error ? "failure" : "success",
104
+ error: error?.message,
105
+ };
106
+ if (isDebugMode()) {
107
+ console.error("[Telemetry Debug] Would send event:", JSON.stringify(event, null, 2));
108
+ return;
109
+ }
110
+ try {
111
+ const client = getPostHogClient();
112
+ client.capture({
113
+ distinctId: event.machineId,
114
+ event: command,
115
+ properties: event,
116
+ });
117
+ }
118
+ catch {
119
+ // Silently ignore telemetry errors - never block CLI operation
120
+ }
121
+ };
122
+ export default hook;
123
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/cli/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,eAAe,GAAG,iDAAiD,CAAC;AAC1E,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAEhD,MAAM,sBAAsB,GAAG,8BAA8B,CAAC;AAC9D,MAAM,mBAAmB,GAAG,2BAA2B,CAAC;AACxD,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAExC,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACxD,MAAM,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;AAoBlE,IAAI,aAAa,GAAmB,IAAI,CAAC;AAEzC,SAAS,gBAAgB;IACvB,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,aAAa,GAAG,IAAI,OAAO,CAAC,eAAe,EAAE;YAC3C,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,CAAC;YACV,aAAa,EAAE,CAAC;SACjB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,SAAS,YAAY,CAAI,QAAgB;IACvC,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAChD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAC;QAClC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,IAAa;IACpD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,QAAQ,GAAG,YAAY,CAAe,kBAAkB,CAAC,CAAC;IAChE,IAAI,QAAQ,EAAE,SAAS,IAAI,QAAQ,EAAE,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAiB;QAC3B,SAAS,EAAE,QAAQ,EAAE,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE;QACrD,SAAS,EAAE;YACT,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,IAAI,IAAI;SAC9C;KACF,CAAC;IAEF,aAAa,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IACE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,KAAK,GAAG;QAC3C,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,EAAE,WAAW,EAAE,KAAK,MAAM,EAC7D,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IACE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,GAAG;QACrC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,WAAW,EAAE,KAAK,MAAM,EACvD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,OAAO,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,GAAG;QACxC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE,WAAW,EAAE,KAAK,MAAM,CAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,CAAC,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;IACnC,aAAa,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,EAAE,CAAC,IAAI,IAAI,YAAY,CAAC;IACjC,CAAC;IAED,OAAO,eAAe,EAAE,CAAC,SAAS,CAAC;AACrC,CAAC;AAED,MAAM,IAAI,GAAoB,KAAK,EAAE,EACnC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,EAAE,OAAO,EAAE,EACnB,KAAK,GACN,EAAE,EAAE;IACH,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAmB;QAC5B,OAAO;QACP,SAAS,EAAE,YAAY,EAAE;QACzB,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE;QAC9B,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,WAAW,EAAE,OAAO,CAAC,OAAO;QAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACtC,KAAK,EAAE,KAAK,EAAE,OAAO;KACtB,CAAC;IAEF,IAAI,WAAW,EAAE,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CACX,qCAAqC,EACrC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAC/B,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAClC,MAAM,CAAC,OAAO,CAAC;YACb,UAAU,EAAE,KAAK,CAAC,SAAS;YAC3B,KAAK,EAAE,OAAO;YACd,UAAU,EAAE,KAAK;SAClB,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,+DAA+D;IACjE,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type SpawnFn, TunnelManager } from "./tunnel.js";
2
+ export type TunnelControlServer = {
3
+ port: number;
4
+ manager: TunnelManager;
5
+ close: () => Promise<void>;
6
+ };
7
+ export declare function startTunnelControlServer(getPort: () => number, options?: {
8
+ spawn?: SpawnFn;
9
+ }): Promise<TunnelControlServer>;
@@ -0,0 +1,31 @@
1
+ import http from "node:http";
2
+ import { TunnelManager } from "./tunnel.js";
3
+ import { createTunnelHandler } from "./tunnel-handler.js";
4
+ export async function startTunnelControlServer(getPort, options) {
5
+ const manager = new TunnelManager({ getPort, spawn: options?.spawn });
6
+ const server = http.createServer(createTunnelHandler(manager));
7
+ await new Promise((resolve, reject) => {
8
+ server.once("error", reject);
9
+ server.listen(0, "127.0.0.1", () => {
10
+ server.off("error", reject);
11
+ resolve();
12
+ });
13
+ });
14
+ const address = server.address();
15
+ if (typeof address === "string" || address === null) {
16
+ server.close();
17
+ throw new Error("tunnel control server has no address");
18
+ }
19
+ return {
20
+ port: address.port,
21
+ manager,
22
+ close: () => new Promise((resolve, reject) => {
23
+ manager.stop();
24
+ // Force any in-flight SSE connections to drop so server.close()
25
+ // doesn't hang indefinitely on subscribers that never end on their own.
26
+ server.closeAllConnections();
27
+ server.close((err) => (err ? reject(err) : resolve()));
28
+ }),
29
+ };
30
+ }
31
+ //# sourceMappingURL=tunnel-control-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-control-server.js","sourceRoot":"","sources":["../../src/cli/tunnel-control-server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAgB,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAQ1D,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAqB,EACrB,OAA6B;IAE7B,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO;QACP,KAAK,EAAE,GAAG,EAAE,CACV,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACpC,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,gEAAgE;YAChE,wEAAwE;YACxE,MAAM,CAAC,mBAAmB,EAAE,CAAC;YAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC;KACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { startTunnelControlServer } from "./tunnel-control-server.js";
3
+ let openControl;
4
+ afterEach(async () => {
5
+ await openControl?.close();
6
+ openControl = undefined;
7
+ });
8
+ describe("startTunnelControlServer", () => {
9
+ it("listens on a random loopback port and serves /__skybridge/tunnel/events", async () => {
10
+ const control = await startTunnelControlServer(() => 3000);
11
+ openControl = control;
12
+ expect(control.port).toBeGreaterThan(0);
13
+ const res = await fetch(`http://127.0.0.1:${control.port}/__skybridge/tunnel/events`);
14
+ expect(res.headers.get("content-type")).toMatch(/text\/event-stream/);
15
+ const reader = res.body.getReader();
16
+ const { value } = await reader.read();
17
+ const chunk = new TextDecoder().decode(value);
18
+ expect(chunk).toContain("event: state");
19
+ expect(chunk).toContain('"status":"idle"');
20
+ await reader.cancel();
21
+ });
22
+ it("two concurrent control servers get different ports", async () => {
23
+ const a = await startTunnelControlServer(() => 3000);
24
+ const b = await startTunnelControlServer(() => 4000);
25
+ try {
26
+ expect(a.port).not.toBe(b.port);
27
+ }
28
+ finally {
29
+ await a.close();
30
+ await b.close();
31
+ }
32
+ });
33
+ it("close() shuts the listener", async () => {
34
+ const control = await startTunnelControlServer(() => 3000);
35
+ await control.close();
36
+ await expect(fetch(`http://127.0.0.1:${control.port}/__skybridge/tunnel/events`)).rejects.toThrow();
37
+ });
38
+ });
39
+ //# sourceMappingURL=tunnel-control-server.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-control-server.test.js","sourceRoot":"","sources":["../../src/cli/tunnel-control-server.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,IAAI,WAAuD,CAAC;AAC5D,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,WAAW,EAAE,KAAK,EAAE,CAAC;IAC3B,WAAW,GAAG,SAAS,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3D,WAAW,GAAG,OAAO,CAAC;QAEtB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAExC,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,oBAAoB,OAAO,CAAC,IAAI,4BAA4B,CAC7D,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAEtE,MAAM,MAAM,GAAI,GAAG,CAAC,IAAmC,CAAC,SAAS,EAAE,CAAC;QACpE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC3C,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,CAAC,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC;YACH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,MAAM,CACV,KAAK,CAAC,oBAAoB,OAAO,CAAC,IAAI,4BAA4B,CAAC,CACpE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { TunnelManager } from "./tunnel.js";
3
+ export declare function createTunnelHandler(manager: TunnelManager): (req: IncomingMessage, res: ServerResponse) => void;
@@ -0,0 +1,48 @@
1
+ export function createTunnelHandler(manager) {
2
+ return (req, res) => {
3
+ if (req.url === "/__skybridge/tunnel" && req.method === "POST") {
4
+ manager.start();
5
+ sendJson(res, 200, manager.getState());
6
+ return;
7
+ }
8
+ if (req.url === "/__skybridge/tunnel" && req.method === "DELETE") {
9
+ manager.stop();
10
+ sendJson(res, 200, manager.getState());
11
+ return;
12
+ }
13
+ if (req.url === "/__skybridge/tunnel/events" && req.method === "GET") {
14
+ writeSseHead(res);
15
+ writeSse(res, "state", manager.getState());
16
+ const onState = (s) => {
17
+ writeSse(res, "state", s);
18
+ };
19
+ const onActivity = (a) => {
20
+ writeSse(res, "activity", a);
21
+ };
22
+ manager.on("state", onState);
23
+ manager.on("activity", onActivity);
24
+ req.on("close", () => {
25
+ manager.off("state", onState);
26
+ manager.off("activity", onActivity);
27
+ });
28
+ return;
29
+ }
30
+ res.writeHead(404).end();
31
+ };
32
+ }
33
+ function sendJson(res, status, data) {
34
+ res.writeHead(status, { "Content-Type": "application/json" });
35
+ res.end(JSON.stringify(data));
36
+ }
37
+ function writeSseHead(res) {
38
+ res.writeHead(200, {
39
+ "Content-Type": "text/event-stream",
40
+ "Cache-Control": "no-cache, no-transform",
41
+ Connection: "keep-alive",
42
+ });
43
+ res.flushHeaders?.();
44
+ }
45
+ function writeSse(res, event, data) {
46
+ res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
47
+ }
48
+ //# sourceMappingURL=tunnel-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-handler.js","sourceRoot":"","sources":["../../src/cli/tunnel-handler.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,mBAAmB,CAAC,OAAsB;IACxD,OAAO,CAAC,GAAoB,EAAE,GAAmB,EAAQ,EAAE;QACzD,IAAI,GAAG,CAAC,GAAG,KAAK,qBAAqB,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC/D,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,GAAG,KAAK,qBAAqB,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACjE,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,GAAG,KAAK,4BAA4B,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACrE,YAAY,CAAC,GAAG,CAAC,CAAC;YAClB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,CAAC,CAAc,EAAE,EAAE;gBACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;YAC5B,CAAC,CAAC;YACF,MAAM,UAAU,GAAG,CAAC,CAAiB,EAAE,EAAE;gBACvC,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;YAC/B,CAAC,CAAC;YACF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7B,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACnC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IAC3B,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa;IAClE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,YAAY,CAAC,GAAmB;IACvC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;QACjB,cAAc,EAAE,mBAAmB;QACnC,eAAe,EAAE,wBAAwB;QACzC,UAAU,EAAE,YAAY;KACzB,CAAC,CAAC;IACH,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,QAAQ,CAAC,GAAmB,EAAE,KAAa,EAAE,IAAa;IACjE,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAClE,CAAC"}