skybridge 0.0.0-dev.fecd520 → 0.0.0-dev.fef24cf

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 (307) hide show
  1. package/README.md +123 -116
  2. package/dist/cli/detect-port.js.map +1 -1
  3. package/dist/cli/header.js +1 -1
  4. package/dist/cli/header.js.map +1 -1
  5. package/dist/cli/run-command.js.map +1 -1
  6. package/dist/cli/telemetry.js.map +1 -1
  7. package/dist/cli/tunnel-control-server.d.ts +9 -0
  8. package/dist/cli/tunnel-control-server.js +31 -0
  9. package/dist/cli/tunnel-control-server.js.map +1 -0
  10. package/dist/cli/tunnel-control-server.test.js +39 -0
  11. package/dist/cli/tunnel-control-server.test.js.map +1 -0
  12. package/dist/cli/tunnel-handler.d.ts +3 -0
  13. package/dist/cli/tunnel-handler.js +48 -0
  14. package/dist/cli/tunnel-handler.js.map +1 -0
  15. package/dist/cli/tunnel-handler.test.js +105 -0
  16. package/dist/cli/tunnel-handler.test.js.map +1 -0
  17. package/dist/cli/tunnel.d.ts +57 -0
  18. package/dist/cli/tunnel.js +154 -0
  19. package/dist/cli/tunnel.js.map +1 -0
  20. package/dist/cli/tunnel.test.js +190 -0
  21. package/dist/cli/tunnel.test.js.map +1 -0
  22. package/dist/cli/types.d.ts +5 -0
  23. package/dist/cli/types.js +2 -0
  24. package/dist/cli/types.js.map +1 -0
  25. package/dist/cli/use-execute-steps.js.map +1 -1
  26. package/dist/cli/use-messages.d.ts +3 -0
  27. package/dist/cli/use-messages.js +11 -0
  28. package/dist/cli/use-messages.js.map +1 -0
  29. package/dist/cli/use-nodemon.d.ts +2 -7
  30. package/dist/cli/use-nodemon.js +18 -21
  31. package/dist/cli/use-nodemon.js.map +1 -1
  32. package/dist/cli/use-open-browser.d.ts +1 -0
  33. package/dist/cli/use-open-browser.js +44 -0
  34. package/dist/cli/use-open-browser.js.map +1 -0
  35. package/dist/cli/use-tunnel.d.ts +14 -0
  36. package/dist/cli/use-tunnel.js +131 -0
  37. package/dist/cli/use-tunnel.js.map +1 -0
  38. package/dist/cli/use-typescript-check.d.ts +1 -0
  39. package/dist/cli/use-typescript-check.js +42 -7
  40. package/dist/cli/use-typescript-check.js.map +1 -1
  41. package/dist/commands/build.js +63 -7
  42. package/dist/commands/build.js.map +1 -1
  43. package/dist/commands/create.d.ts +9 -0
  44. package/dist/commands/create.js +30 -0
  45. package/dist/commands/create.js.map +1 -0
  46. package/dist/commands/dev.d.ts +3 -1
  47. package/dist/commands/dev.js +46 -8
  48. package/dist/commands/dev.js.map +1 -1
  49. package/dist/commands/start.js +7 -10
  50. package/dist/commands/start.js.map +1 -1
  51. package/dist/commands/telemetry/disable.js.map +1 -1
  52. package/dist/commands/telemetry/enable.js.map +1 -1
  53. package/dist/commands/telemetry/status.js.map +1 -1
  54. package/dist/server/asset-base-url-transform-plugin.d.ts +6 -6
  55. package/dist/server/asset-base-url-transform-plugin.js +25 -11
  56. package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
  57. package/dist/server/asset-base-url-transform-plugin.test.js +92 -14
  58. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
  59. package/dist/server/auth.d.ts +20 -0
  60. package/dist/server/auth.js +28 -0
  61. package/dist/server/auth.js.map +1 -0
  62. package/dist/server/content-helpers.d.ts +67 -0
  63. package/dist/server/content-helpers.js +79 -0
  64. package/dist/server/content-helpers.js.map +1 -0
  65. package/dist/server/content-helpers.test.d.ts +1 -0
  66. package/dist/server/content-helpers.test.js +70 -0
  67. package/dist/server/content-helpers.test.js.map +1 -0
  68. package/dist/server/express.d.ts +7 -5
  69. package/dist/server/express.js +52 -23
  70. package/dist/server/express.js.map +1 -1
  71. package/dist/server/express.test.js +381 -25
  72. package/dist/server/express.test.js.map +1 -1
  73. package/dist/server/file-ref.d.ts +28 -0
  74. package/dist/server/file-ref.js +27 -0
  75. package/dist/server/file-ref.js.map +1 -0
  76. package/dist/server/index.d.ts +7 -4
  77. package/dist/server/index.js +5 -2
  78. package/dist/server/index.js.map +1 -1
  79. package/dist/server/inferUtilityTypes.d.ts +6 -6
  80. package/dist/server/inferUtilityTypes.js.map +1 -1
  81. package/dist/server/metric.d.ts +14 -0
  82. package/dist/server/metric.js +62 -0
  83. package/dist/server/metric.js.map +1 -0
  84. package/dist/server/middleware.d.ts +47 -6
  85. package/dist/server/middleware.js.map +1 -1
  86. package/dist/server/middleware.test-d.js +41 -18
  87. package/dist/server/middleware.test-d.js.map +1 -1
  88. package/dist/server/middleware.test.js +115 -5
  89. package/dist/server/middleware.test.js.map +1 -1
  90. package/dist/server/server.d.ts +334 -75
  91. package/dist/server/server.js +419 -117
  92. package/dist/server/server.js.map +1 -1
  93. package/dist/server/templateHelper.d.ts +5 -8
  94. package/dist/server/templateHelper.js +3 -22
  95. package/dist/server/templateHelper.js.map +1 -1
  96. package/dist/server/templates.generated.d.ts +4 -0
  97. package/dist/server/templates.generated.js +47 -0
  98. package/dist/server/templates.generated.js.map +1 -0
  99. package/dist/server/tunnel-proxy-router.d.ts +7 -0
  100. package/dist/server/tunnel-proxy-router.js +110 -0
  101. package/dist/server/tunnel-proxy-router.js.map +1 -0
  102. package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
  103. package/dist/server/tunnel-proxy-router.test.js +229 -0
  104. package/dist/server/tunnel-proxy-router.test.js.map +1 -0
  105. package/dist/server/viewsDevServer.d.ts +14 -0
  106. package/dist/server/viewsDevServer.js +45 -0
  107. package/dist/server/viewsDevServer.js.map +1 -0
  108. package/dist/test/utils.d.ts +13 -21
  109. package/dist/test/utils.js +42 -37
  110. package/dist/test/utils.js.map +1 -1
  111. package/dist/test/view.test.d.ts +1 -0
  112. package/dist/test/view.test.js +568 -0
  113. package/dist/test/view.test.js.map +1 -0
  114. package/dist/version.d.ts +1 -0
  115. package/dist/version.js +3 -0
  116. package/dist/version.js.map +1 -0
  117. package/dist/web/bridges/apps-sdk/adaptor.d.ts +10 -4
  118. package/dist/web/bridges/apps-sdk/adaptor.js +55 -17
  119. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
  120. package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
  121. package/dist/web/bridges/apps-sdk/bridge.js +1 -0
  122. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  123. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  124. package/dist/web/bridges/apps-sdk/types.d.ts +18 -6
  125. package/dist/web/bridges/apps-sdk/types.js.map +1 -1
  126. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
  127. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
  128. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
  129. package/dist/web/bridges/get-adaptor.d.ts +7 -0
  130. package/dist/web/bridges/get-adaptor.js +7 -0
  131. package/dist/web/bridges/get-adaptor.js.map +1 -1
  132. package/dist/web/bridges/index.js.map +1 -1
  133. package/dist/web/bridges/mcp-app/adaptor.d.ts +24 -8
  134. package/dist/web/bridges/mcp-app/adaptor.js +152 -62
  135. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
  136. package/dist/web/bridges/mcp-app/bridge.d.ts +14 -30
  137. package/dist/web/bridges/mcp-app/bridge.js +44 -201
  138. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  139. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  140. package/dist/web/bridges/mcp-app/types.js.map +1 -1
  141. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +17 -3
  142. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +14 -2
  143. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
  144. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -41
  145. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
  146. package/dist/web/bridges/types.d.ts +80 -11
  147. package/dist/web/bridges/types.js.map +1 -1
  148. package/dist/web/bridges/use-host-context.d.ts +5 -0
  149. package/dist/web/bridges/use-host-context.js +5 -0
  150. package/dist/web/bridges/use-host-context.js.map +1 -1
  151. package/dist/web/components/modal-provider.js +3 -5
  152. package/dist/web/components/modal-provider.js.map +1 -1
  153. package/dist/web/create-store.d.ts +26 -0
  154. package/dist/web/create-store.js +43 -3
  155. package/dist/web/create-store.js.map +1 -1
  156. package/dist/web/create-store.test.js +17 -17
  157. package/dist/web/create-store.test.js.map +1 -1
  158. package/dist/web/data-llm.d.ts +34 -1
  159. package/dist/web/data-llm.js +31 -3
  160. package/dist/web/data-llm.js.map +1 -1
  161. package/dist/web/data-llm.test.js +23 -22
  162. package/dist/web/data-llm.test.js.map +1 -1
  163. package/dist/web/generate-helpers.d.ts +22 -18
  164. package/dist/web/generate-helpers.js +22 -18
  165. package/dist/web/generate-helpers.js.map +1 -1
  166. package/dist/web/generate-helpers.test-d.js +26 -26
  167. package/dist/web/generate-helpers.test-d.js.map +1 -1
  168. package/dist/web/generate-helpers.test.js.map +1 -1
  169. package/dist/web/helpers/state.d.ts +2 -2
  170. package/dist/web/helpers/state.js +11 -11
  171. package/dist/web/helpers/state.js.map +1 -1
  172. package/dist/web/helpers/state.test.js +9 -9
  173. package/dist/web/helpers/state.test.js.map +1 -1
  174. package/dist/web/hooks/index.d.ts +4 -1
  175. package/dist/web/hooks/index.js +4 -1
  176. package/dist/web/hooks/index.js.map +1 -1
  177. package/dist/web/hooks/test/utils.d.ts +6 -2
  178. package/dist/web/hooks/test/utils.js +17 -2
  179. package/dist/web/hooks/test/utils.js.map +1 -1
  180. package/dist/web/hooks/use-call-tool.d.ts +45 -0
  181. package/dist/web/hooks/use-call-tool.js +28 -0
  182. package/dist/web/hooks/use-call-tool.js.map +1 -1
  183. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
  184. package/dist/web/hooks/use-call-tool.test.js +27 -6
  185. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  186. package/dist/web/hooks/use-display-mode.d.ts +20 -0
  187. package/dist/web/hooks/use-display-mode.js +20 -0
  188. package/dist/web/hooks/use-display-mode.js.map +1 -1
  189. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -1
  190. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  191. package/dist/web/hooks/use-download.d.ts +5 -0
  192. package/dist/web/hooks/use-download.js +8 -0
  193. package/dist/web/hooks/use-download.js.map +1 -0
  194. package/dist/web/hooks/use-download.test.d.ts +1 -0
  195. package/dist/web/hooks/use-download.test.js +95 -0
  196. package/dist/web/hooks/use-download.test.js.map +1 -0
  197. package/dist/web/hooks/use-files.d.ts +34 -1
  198. package/dist/web/hooks/use-files.js +33 -0
  199. package/dist/web/hooks/use-files.js.map +1 -1
  200. package/dist/web/hooks/use-files.test.js +22 -2
  201. package/dist/web/hooks/use-files.test.js.map +1 -1
  202. package/dist/web/hooks/use-layout.d.ts +2 -0
  203. package/dist/web/hooks/use-layout.js +2 -0
  204. package/dist/web/hooks/use-layout.js.map +1 -1
  205. package/dist/web/hooks/use-layout.test.js +3 -3
  206. package/dist/web/hooks/use-layout.test.js.map +1 -1
  207. package/dist/web/hooks/use-open-external.d.ts +17 -0
  208. package/dist/web/hooks/use-open-external.js +16 -0
  209. package/dist/web/hooks/use-open-external.js.map +1 -1
  210. package/dist/web/hooks/use-open-external.test.js +15 -10
  211. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  212. package/dist/web/hooks/use-request-close.d.ts +16 -0
  213. package/dist/web/hooks/use-request-close.js +21 -0
  214. package/dist/web/hooks/use-request-close.js.map +1 -0
  215. package/dist/web/hooks/use-request-close.test.d.ts +1 -0
  216. package/dist/web/hooks/use-request-close.test.js +52 -0
  217. package/dist/web/hooks/use-request-close.test.js.map +1 -0
  218. package/dist/web/hooks/use-request-modal.d.ts +16 -1
  219. package/dist/web/hooks/use-request-modal.js +19 -4
  220. package/dist/web/hooks/use-request-modal.js.map +1 -1
  221. package/dist/web/hooks/use-request-modal.test.js +5 -1
  222. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  223. package/dist/web/hooks/use-request-size.d.ts +20 -0
  224. package/dist/web/hooks/use-request-size.js +24 -0
  225. package/dist/web/hooks/use-request-size.js.map +1 -0
  226. package/dist/web/hooks/use-request-size.test.d.ts +1 -0
  227. package/dist/web/hooks/use-request-size.test.js +65 -0
  228. package/dist/web/hooks/use-request-size.test.js.map +1 -0
  229. package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -1
  230. package/dist/web/hooks/use-send-follow-up-message.js +19 -2
  231. package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
  232. package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
  233. package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
  234. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
  235. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  236. package/dist/web/hooks/use-tool-info.d.ts +33 -0
  237. package/dist/web/hooks/use-tool-info.js +26 -0
  238. package/dist/web/hooks/use-tool-info.js.map +1 -1
  239. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
  240. package/dist/web/hooks/use-tool-info.test.js +1 -1
  241. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  242. package/dist/web/hooks/use-user.d.ts +2 -0
  243. package/dist/web/hooks/use-user.js +20 -2
  244. package/dist/web/hooks/use-user.js.map +1 -1
  245. package/dist/web/hooks/use-user.test.js +29 -1
  246. package/dist/web/hooks/use-user.test.js.map +1 -1
  247. package/dist/web/hooks/use-view-state.d.ts +25 -0
  248. package/dist/web/hooks/use-view-state.js +32 -0
  249. package/dist/web/hooks/use-view-state.js.map +1 -0
  250. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  251. package/dist/web/hooks/use-view-state.test.js +177 -0
  252. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  253. package/dist/web/index.d.ts +1 -2
  254. package/dist/web/index.js +1 -2
  255. package/dist/web/index.js.map +1 -1
  256. package/dist/web/mount-view.d.ts +20 -0
  257. package/dist/web/{mount-widget.js → mount-view.js} +21 -2
  258. package/dist/web/mount-view.js.map +1 -0
  259. package/dist/web/plugin/data-llm.test.js.map +1 -1
  260. package/dist/web/plugin/plugin.d.ts +32 -1
  261. package/dist/web/plugin/plugin.js +160 -25
  262. package/dist/web/plugin/plugin.js.map +1 -1
  263. package/dist/web/plugin/scan-views.d.ts +16 -0
  264. package/dist/web/plugin/scan-views.js +88 -0
  265. package/dist/web/plugin/scan-views.js.map +1 -0
  266. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  267. package/dist/web/plugin/scan-views.test.js +99 -0
  268. package/dist/web/plugin/scan-views.test.js.map +1 -0
  269. package/dist/web/plugin/transform-data-llm.js +1 -1
  270. package/dist/web/plugin/transform-data-llm.js.map +1 -1
  271. package/dist/web/plugin/transform-data-llm.test.js.map +1 -1
  272. package/dist/web/plugin/validate-view.d.ts +1 -0
  273. package/dist/web/plugin/validate-view.js +9 -0
  274. package/dist/web/plugin/validate-view.js.map +1 -0
  275. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  276. package/dist/web/plugin/validate-view.test.js +24 -0
  277. package/dist/web/plugin/validate-view.test.js.map +1 -0
  278. package/dist/web/proxy.js.map +1 -1
  279. package/dist/web/types.d.ts +4 -0
  280. package/dist/web/types.js.map +1 -1
  281. package/package.json +35 -21
  282. package/tsconfig.base.json +5 -0
  283. package/dist/server/const.d.ts +0 -1
  284. package/dist/server/const.js +0 -2
  285. package/dist/server/const.js.map +0 -1
  286. package/dist/server/templates/development.hbs +0 -67
  287. package/dist/server/templates/production.hbs +0 -6
  288. package/dist/server/widgetsDevServer.d.ts +0 -12
  289. package/dist/server/widgetsDevServer.js +0 -63
  290. package/dist/server/widgetsDevServer.js.map +0 -1
  291. package/dist/test/widget.test.js +0 -261
  292. package/dist/test/widget.test.js.map +0 -1
  293. package/dist/web/hooks/use-widget-state.d.ts +0 -4
  294. package/dist/web/hooks/use-widget-state.js +0 -32
  295. package/dist/web/hooks/use-widget-state.js.map +0 -1
  296. package/dist/web/hooks/use-widget-state.test.js +0 -64
  297. package/dist/web/hooks/use-widget-state.test.js.map +0 -1
  298. package/dist/web/mount-widget.d.ts +0 -1
  299. package/dist/web/mount-widget.js.map +0 -1
  300. package/dist/web/plugin/validate-widget.d.ts +0 -5
  301. package/dist/web/plugin/validate-widget.js +0 -27
  302. package/dist/web/plugin/validate-widget.js.map +0 -1
  303. package/dist/web/plugin/validate-widget.test.js +0 -42
  304. package/dist/web/plugin/validate-widget.test.js.map +0 -1
  305. /package/dist/{test/widget.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
  306. /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel-handler.test.d.ts} +0 -0
  307. /package/dist/{web/plugin/validate-widget.test.d.ts → cli/tunnel.test.d.ts} +0 -0
@@ -1,7 +1,2 @@
1
- type Message = {
2
- id: string;
3
- text: string;
4
- type: "log" | "restart" | "error";
5
- };
6
- export declare function useNodemon(env: NodeJS.ProcessEnv): Array<Message>;
7
- export {};
1
+ import type { PushMessage } from "./use-messages.js";
2
+ export declare function useNodemon(env: NodeJS.ProcessEnv, pushMessage: PushMessage): void;
@@ -1,11 +1,10 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import { existsSync } from "node:fs";
3
2
  import { resolve } from "node:path";
4
3
  import nodemonOriginal from "nodemon";
5
- import { useEffect, useState } from "react";
4
+ import { useEffect } from "react";
6
5
  const nodemon = nodemonOriginal;
7
- export function useNodemon(env) {
8
- const [messages, setMessages] = useState([]);
6
+ const SOURCEMAP_WARNING = /^Sourcemap for ".*" points to missing source files$/;
7
+ export function useNodemon(env, pushMessage) {
9
8
  useEffect(() => {
10
9
  const configFile = resolve(process.cwd(), "nodemon.json");
11
10
  const config = existsSync(configFile)
@@ -13,27 +12,29 @@ export function useNodemon(env) {
13
12
  configFile,
14
13
  }
15
14
  : {
16
- watch: ["server/src"],
15
+ watch: ["src"],
17
16
  ext: "ts,json",
18
- exec: "tsx server/src/index.ts",
17
+ exec: "tsx src/server.ts",
19
18
  };
20
19
  nodemon({ ...config, env, stdout: false });
21
20
  const handleStdoutData = (chunk) => {
22
21
  const message = chunk.toString().trim();
23
22
  if (message) {
24
- setMessages((prev) => [
25
- ...prev,
26
- { id: randomUUID(), text: message, type: "log" },
27
- ]);
23
+ pushMessage(message, "log");
28
24
  }
29
25
  };
30
26
  const handleStderrData = (chunk) => {
31
27
  const message = chunk.toString().trim();
32
- if (message) {
33
- setMessages((prev) => [
34
- ...prev,
35
- { id: randomUUID(), text: message, type: "error" },
36
- ]);
28
+ if (!message) {
29
+ return;
30
+ }
31
+ // Node's source-map warnings for third-party deps (superjson, @mcp/sdk, …) not actionable.
32
+ const filtered = message
33
+ .split("\n")
34
+ .filter((line) => !SOURCEMAP_WARNING.test(line))
35
+ .join("\n");
36
+ if (filtered) {
37
+ pushMessage(filtered, "error");
37
38
  }
38
39
  };
39
40
  const setupStdoutListener = () => {
@@ -54,10 +55,7 @@ export function useNodemon(env) {
54
55
  });
55
56
  nodemon.on("restart", (files) => {
56
57
  const restartMessage = `Server restarted due to file changes: ${files.join(", ")}`;
57
- setMessages((prev) => [
58
- ...prev,
59
- { id: randomUUID(), text: restartMessage, type: "restart" },
60
- ]);
58
+ pushMessage(restartMessage, "restart");
61
59
  setupStdoutListener();
62
60
  setupStderrListener();
63
61
  });
@@ -70,7 +68,6 @@ export function useNodemon(env) {
70
68
  }
71
69
  nodemon.emit("quit");
72
70
  };
73
- }, [env]);
74
- return messages;
71
+ }, [env, pushMessage]);
75
72
  }
76
73
  //# sourceMappingURL=use-nodemon.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-nodemon.js","sourceRoot":"","sources":["../../src/cli/use-nodemon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-nodemon.js","sourceRoot":"","sources":["../../src/cli/use-nodemon.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport nodemonOriginal from \"nodemon\";\nimport { useEffect } from \"react\";\nimport type { ExtendedNodemon } from \"./nodemon.d.ts\";\nimport type { PushMessage } from \"./use-messages.js\";\n\nconst nodemon = nodemonOriginal as ExtendedNodemon;\n\nconst SOURCEMAP_WARNING = /^Sourcemap for \".*\" points to missing source files$/;\n\nexport function useNodemon(\n env: NodeJS.ProcessEnv,\n pushMessage: PushMessage,\n): void {\n useEffect(() => {\n const configFile = resolve(process.cwd(), \"nodemon.json\");\n\n const config = existsSync(configFile)\n ? {\n configFile,\n }\n : {\n watch: [\"src\"],\n ext: \"ts,json\",\n exec: \"tsx src/server.ts\",\n };\n\n nodemon({ ...config, env, stdout: false });\n\n const handleStdoutData = (chunk: Buffer) => {\n const message = chunk.toString().trim();\n if (message) {\n pushMessage(message, \"log\");\n }\n };\n\n const handleStderrData = (chunk: Buffer) => {\n const message = chunk.toString().trim();\n if (!message) {\n return;\n }\n // Node's source-map warnings for third-party deps (superjson, @mcp/sdk, …) — not actionable.\n const filtered = message\n .split(\"\\n\")\n .filter((line) => !SOURCEMAP_WARNING.test(line))\n .join(\"\\n\");\n if (filtered) {\n pushMessage(filtered, \"error\");\n }\n };\n\n const setupStdoutListener = () => {\n if (nodemon.stdout) {\n nodemon.stdout.off(\"data\", handleStdoutData);\n nodemon.stdout.on(\"data\", handleStdoutData);\n }\n };\n\n const setupStderrListener = () => {\n if (nodemon.stderr) {\n nodemon.stderr.off(\"data\", handleStderrData);\n nodemon.stderr.on(\"data\", handleStderrData);\n }\n };\n\n nodemon.on(\"readable\", () => {\n setupStdoutListener();\n setupStderrListener();\n });\n\n nodemon.on(\"restart\", (files: string[]) => {\n const restartMessage = `Server restarted due to file changes: ${files.join(\", \")}`;\n pushMessage(restartMessage, \"restart\");\n setupStdoutListener();\n setupStderrListener();\n });\n\n return () => {\n if (nodemon.stdout) {\n nodemon.stdout.off(\"data\", handleStdoutData);\n }\n if (nodemon.stderr) {\n nodemon.stderr.off(\"data\", handleStderrData);\n }\n nodemon.emit(\"quit\");\n };\n }, [env, pushMessage]);\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function useOpenBrowser(port: number, enabled: boolean): void;
@@ -0,0 +1,44 @@
1
+ import net from "node:net";
2
+ import open from "open";
3
+ import { useEffect, useRef } from "react";
4
+ const POLL_INTERVAL_MS = 200;
5
+ const POLL_TIMEOUT_MS = 5_000;
6
+ const isPortListening = (port) => new Promise((resolve) => {
7
+ const socket = net.createConnection({ port, host: "127.0.0.1" });
8
+ socket.once("connect", () => {
9
+ socket.end();
10
+ resolve(true);
11
+ });
12
+ socket.once("error", () => {
13
+ socket.destroy();
14
+ resolve(false);
15
+ });
16
+ });
17
+ export function useOpenBrowser(port, enabled) {
18
+ const opened = useRef(false);
19
+ useEffect(() => {
20
+ if (!enabled || opened.current) {
21
+ return;
22
+ }
23
+ let cancelled = false;
24
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
25
+ const tick = async () => {
26
+ while (!cancelled && Date.now() < deadline) {
27
+ if (await isPortListening(port)) {
28
+ if (cancelled || opened.current) {
29
+ return;
30
+ }
31
+ opened.current = true;
32
+ await open(`http://localhost:${port}/`).catch(() => { });
33
+ return;
34
+ }
35
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
36
+ }
37
+ };
38
+ void tick();
39
+ return () => {
40
+ cancelled = true;
41
+ };
42
+ }, [port, enabled]);
43
+ }
44
+ //# sourceMappingURL=use-open-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-open-browser.js","sourceRoot":"","sources":["../../src/cli/use-open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B,MAAM,eAAe,GAAG,CAAC,IAAY,EAAoB,EAAE,CACzD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;IACtB,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IACjE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;QACxB,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,OAAgB;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QAED,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;QAE9C,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACtB,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;gBAC3C,IAAI,MAAM,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;oBAChC,IAAI,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;wBAChC,OAAO;oBACT,CAAC;oBACD,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;oBACtB,MAAM,IAAI,CAAC,oBAAoB,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACxD,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,IAAI,EAAE,CAAC;QAEZ,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACtB,CAAC","sourcesContent":["import net from \"node:net\";\nimport open from \"open\";\nimport { useEffect, useRef } from \"react\";\n\nconst POLL_INTERVAL_MS = 200;\nconst POLL_TIMEOUT_MS = 5_000;\n\nconst isPortListening = (port: number): Promise<boolean> =>\n new Promise((resolve) => {\n const socket = net.createConnection({ port, host: \"127.0.0.1\" });\n socket.once(\"connect\", () => {\n socket.end();\n resolve(true);\n });\n socket.once(\"error\", () => {\n socket.destroy();\n resolve(false);\n });\n });\n\nexport function useOpenBrowser(port: number, enabled: boolean): void {\n const opened = useRef(false);\n\n useEffect(() => {\n if (!enabled || opened.current) {\n return;\n }\n\n let cancelled = false;\n const deadline = Date.now() + POLL_TIMEOUT_MS;\n\n const tick = async () => {\n while (!cancelled && Date.now() < deadline) {\n if (await isPortListening(port)) {\n if (cancelled || opened.current) {\n return;\n }\n opened.current = true;\n await open(`http://localhost:${port}/`).catch(() => {});\n return;\n }\n await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));\n }\n };\n\n void tick();\n\n return () => {\n cancelled = true;\n };\n }, [port, enabled]);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { PushMessage } from "./use-messages.js";
2
+ export type TunnelState = {
3
+ status: "idle";
4
+ } | {
5
+ status: "starting";
6
+ message: string;
7
+ } | {
8
+ status: "connected";
9
+ url: string;
10
+ } | {
11
+ status: "error";
12
+ message: string;
13
+ };
14
+ export declare function useTunnel(port: number | null, pushMessage: PushMessage, verbose: boolean, autoStart: boolean): TunnelState;
@@ -0,0 +1,131 @@
1
+ import { useEffect, useState } from "react";
2
+ const POST_RETRY_DELAY_MS = 250;
3
+ export function useTunnel(port, pushMessage, verbose, autoStart) {
4
+ const [state, setState] = useState(port !== null && autoStart
5
+ ? { status: "starting", message: "Starting tunnel…" }
6
+ : { status: "idle" });
7
+ useEffect(() => {
8
+ if (port === null) {
9
+ return;
10
+ }
11
+ const baseUrl = `http://localhost:${port}`;
12
+ const controller = new AbortController();
13
+ let cancelled = false;
14
+ const pushLog = (text, type) => {
15
+ const time = new Date().toLocaleTimeString("en-US", {
16
+ hour: "numeric",
17
+ minute: "2-digit",
18
+ second: "2-digit",
19
+ hour12: true,
20
+ });
21
+ pushMessage(`${time} [tunnel] ${text}`, type);
22
+ };
23
+ const handleEvent = (event, data) => {
24
+ if (event === "state") {
25
+ const next = JSON.parse(data);
26
+ setState(next);
27
+ return;
28
+ }
29
+ if (event === "activity") {
30
+ if (!verbose) {
31
+ return;
32
+ }
33
+ const activity = JSON.parse(data);
34
+ pushLog(activity.text, activity.level);
35
+ }
36
+ };
37
+ const consumeSse = async () => {
38
+ const res = await fetch(`${baseUrl}/__skybridge/tunnel/events`, {
39
+ signal: controller.signal,
40
+ headers: { Accept: "text/event-stream" },
41
+ });
42
+ if (!res.ok || !res.body) {
43
+ throw new Error(`SSE connection failed (${res.status})`);
44
+ }
45
+ const reader = res.body.getReader();
46
+ const decoder = new TextDecoder();
47
+ let buffer = "";
48
+ while (!cancelled) {
49
+ const { value, done } = await reader.read();
50
+ if (done) {
51
+ return;
52
+ }
53
+ buffer += decoder.decode(value, { stream: true });
54
+ // SSE frames are separated by a blank line ("\n\n").
55
+ let sep = buffer.indexOf("\n\n");
56
+ while (sep !== -1) {
57
+ const frame = buffer.slice(0, sep);
58
+ buffer = buffer.slice(sep + 2);
59
+ let eventName = "message";
60
+ const dataLines = [];
61
+ for (const rawLine of frame.split("\n")) {
62
+ const line = rawLine.replace(/\r$/, "");
63
+ if (line.startsWith("event:")) {
64
+ eventName = line.slice(6).trim();
65
+ }
66
+ else if (line.startsWith("data:")) {
67
+ dataLines.push(line.slice(5).trimStart());
68
+ }
69
+ }
70
+ if (dataLines.length > 0) {
71
+ handleEvent(eventName, dataLines.join("\n"));
72
+ }
73
+ sep = buffer.indexOf("\n\n");
74
+ }
75
+ }
76
+ };
77
+ const postUntilStarted = async () => {
78
+ // Retry indefinitely until POST lands once. Bounded by the effect
79
+ // lifetime via controller.abort() on unmount. Once the manager has
80
+ // been started, this returns and never POSTs again — a user-driven
81
+ // DELETE /tunnel won't be auto-undone.
82
+ while (!cancelled) {
83
+ try {
84
+ const res = await fetch(`${baseUrl}/__skybridge/tunnel`, {
85
+ method: "POST",
86
+ signal: controller.signal,
87
+ });
88
+ if (res.ok) {
89
+ return;
90
+ }
91
+ }
92
+ catch {
93
+ // dev server not up yet (or restarting under nodemon) — wait, retry
94
+ }
95
+ await new Promise((r) => setTimeout(r, POST_RETRY_DELAY_MS));
96
+ }
97
+ };
98
+ // Always observe the tunnel state so external triggers (curl, future
99
+ // devtools UI) update the cli UI. `autoStart` only decides whether we
100
+ // also POST /tunnel on mount. Reconnects indefinitely so we survive
101
+ // dev-server boot delay and nodemon restarts; the cli owns the actual
102
+ // subprocess so a temporarily-unreachable dev server is fine.
103
+ const observe = async () => {
104
+ while (!cancelled) {
105
+ try {
106
+ await consumeSse();
107
+ }
108
+ catch {
109
+ // network error or stream ended abnormally — fall through to retry
110
+ }
111
+ if (cancelled) {
112
+ return;
113
+ }
114
+ await new Promise((r) => setTimeout(r, POST_RETRY_DELAY_MS));
115
+ }
116
+ };
117
+ // observe() always runs — it owns the cli's view of tunnel state. POST is
118
+ // a fire-and-forget side-effect that nudges the manager into starting and
119
+ // retries until it lands at least once.
120
+ if (autoStart) {
121
+ void postUntilStarted();
122
+ }
123
+ void observe();
124
+ return () => {
125
+ cancelled = true;
126
+ controller.abort();
127
+ };
128
+ }, [port, pushMessage, verbose, autoStart]);
129
+ return state;
130
+ }
131
+ //# sourceMappingURL=use-tunnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tunnel.js","sourceRoot":"","sources":["../../src/cli/use-tunnel.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { PushMessage } from \"./use-messages.js\";\n\nexport type TunnelState =\n | { status: \"idle\" }\n | { status: \"starting\"; message: string }\n | { status: \"connected\"; url: string }\n | { status: \"error\"; message: string };\n\ntype TunnelActivity = {\n time: string;\n text: string;\n level: \"log\" | \"error\";\n};\n\nconst POST_RETRY_DELAY_MS = 250;\n\nexport function useTunnel(\n port: number | null,\n pushMessage: PushMessage,\n verbose: boolean,\n autoStart: boolean,\n): TunnelState {\n const [state, setState] = useState<TunnelState>(\n port !== null && autoStart\n ? { status: \"starting\", message: \"Starting tunnel…\" }\n : { status: \"idle\" },\n );\n\n useEffect(() => {\n if (port === null) {\n return;\n }\n\n const baseUrl = `http://localhost:${port}`;\n const controller = new AbortController();\n let cancelled = false;\n\n const pushLog = (text: string, type: \"log\" | \"error\") => {\n const time = new Date().toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: true,\n });\n pushMessage(`${time} [tunnel] ${text}`, type);\n };\n\n const handleEvent = (event: string, data: string) => {\n if (event === \"state\") {\n const next = JSON.parse(data) as TunnelState;\n setState(next);\n return;\n }\n if (event === \"activity\") {\n if (!verbose) {\n return;\n }\n const activity = JSON.parse(data) as TunnelActivity;\n pushLog(activity.text, activity.level);\n }\n };\n\n const consumeSse = async () => {\n const res = await fetch(`${baseUrl}/__skybridge/tunnel/events`, {\n signal: controller.signal,\n headers: { Accept: \"text/event-stream\" },\n });\n if (!res.ok || !res.body) {\n throw new Error(`SSE connection failed (${res.status})`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (!cancelled) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n buffer += decoder.decode(value, { stream: true });\n\n // SSE frames are separated by a blank line (\"\\n\\n\").\n let sep = buffer.indexOf(\"\\n\\n\");\n while (sep !== -1) {\n const frame = buffer.slice(0, sep);\n buffer = buffer.slice(sep + 2);\n let eventName = \"message\";\n const dataLines: string[] = [];\n for (const rawLine of frame.split(\"\\n\")) {\n const line = rawLine.replace(/\\r$/, \"\");\n if (line.startsWith(\"event:\")) {\n eventName = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n dataLines.push(line.slice(5).trimStart());\n }\n }\n if (dataLines.length > 0) {\n handleEvent(eventName, dataLines.join(\"\\n\"));\n }\n sep = buffer.indexOf(\"\\n\\n\");\n }\n }\n };\n\n const postUntilStarted = async () => {\n // Retry indefinitely until POST lands once. Bounded by the effect\n // lifetime via controller.abort() on unmount. Once the manager has\n // been started, this returns and never POSTs again — a user-driven\n // DELETE /tunnel won't be auto-undone.\n while (!cancelled) {\n try {\n const res = await fetch(`${baseUrl}/__skybridge/tunnel`, {\n method: \"POST\",\n signal: controller.signal,\n });\n if (res.ok) {\n return;\n }\n } catch {\n // dev server not up yet (or restarting under nodemon) — wait, retry\n }\n await new Promise((r) => setTimeout(r, POST_RETRY_DELAY_MS));\n }\n };\n\n // Always observe the tunnel state so external triggers (curl, future\n // devtools UI) update the cli UI. `autoStart` only decides whether we\n // also POST /tunnel on mount. Reconnects indefinitely so we survive\n // dev-server boot delay and nodemon restarts; the cli owns the actual\n // subprocess so a temporarily-unreachable dev server is fine.\n const observe = async () => {\n while (!cancelled) {\n try {\n await consumeSse();\n } catch {\n // network error or stream ended abnormally — fall through to retry\n }\n if (cancelled) {\n return;\n }\n await new Promise((r) => setTimeout(r, POST_RETRY_DELAY_MS));\n }\n };\n\n // observe() always runs — it owns the cli's view of tunnel state. POST is\n // a fire-and-forget side-effect that nudges the manager into starting and\n // retries until it lands at least once.\n if (autoStart) {\n void postUntilStarted();\n }\n void observe();\n\n return () => {\n cancelled = true;\n controller.abort();\n };\n }, [port, pushMessage, verbose, autoStart]);\n\n return state;\n}\n"]}
@@ -2,6 +2,7 @@ type TsError = {
2
2
  file: string;
3
3
  line: number;
4
4
  col: number;
5
+ code: string;
5
6
  message: string;
6
7
  };
7
8
  export declare function useTypeScriptCheck(): Array<TsError>;
@@ -1,6 +1,24 @@
1
- import { spawn } from "node:child_process";
2
1
  import { isAbsolute, relative } from "node:path";
2
+ import spawn from "cross-spawn";
3
3
  import { useEffect, useRef, useState } from "react";
4
+ // TypeScript nests from general to specific — the deepest line is the root cause.
5
+ function extractBestMessage(message, continuationLines) {
6
+ if (!continuationLines.length) {
7
+ return message;
8
+ }
9
+ let maxIndent = 0;
10
+ for (const line of continuationLines) {
11
+ const indent = line.length - line.trimStart().length;
12
+ if (indent > maxIndent) {
13
+ maxIndent = indent;
14
+ }
15
+ }
16
+ const deepest = continuationLines
17
+ .filter((l) => l.length - l.trimStart().length === maxIndent)
18
+ .map((l) => l.trim())
19
+ .filter(Boolean)[0];
20
+ return deepest ?? message;
21
+ }
4
22
  export function useTypeScriptCheck() {
5
23
  const tsProcessRef = useRef(null);
6
24
  const [tsErrors, setTsErrors] = useState([]);
@@ -12,31 +30,48 @@ export function useTypeScriptCheck() {
12
30
  tsProcessRef.current = tsProcess;
13
31
  let outputBuffer = "";
14
32
  let currentErrors = [];
33
+ let pendingError = null;
34
+ let continuationLines = [];
35
+ const flushPending = () => {
36
+ if (!pendingError) {
37
+ return;
38
+ }
39
+ pendingError.message = extractBestMessage(pendingError.message, continuationLines);
40
+ currentErrors.push(pendingError);
41
+ pendingError = null;
42
+ continuationLines = [];
43
+ };
15
44
  const processOutput = (data) => {
16
45
  outputBuffer += data.toString();
17
46
  const lines = outputBuffer.split("\n");
18
- outputBuffer = lines.pop() || ""; // Keep incomplete line in buffer
47
+ outputBuffer = lines.pop() || "";
19
48
  for (const line of lines) {
20
49
  const trimmed = line.trim();
21
- // Parse TypeScript error format: file.ts(10,5): error TS2322: message
22
- // Match pattern: filename(line,col): error code: message
23
50
  const errorMatch = trimmed.match(/^(.+?)\((\d+),(\d+)\):\s+error\s+(TS\d+)?\s*:?\s*(.+)$/);
24
51
  if (errorMatch) {
25
- const [, file, lineStr, colStr, , message] = errorMatch;
52
+ flushPending();
53
+ const [, file, lineStr, colStr, code, message] = errorMatch;
26
54
  if (file && lineStr && colStr && message) {
27
55
  let cleanFile = file.trim();
28
56
  if (isAbsolute(cleanFile)) {
29
57
  cleanFile = relative(process.cwd(), cleanFile);
30
58
  }
31
- currentErrors.push({
59
+ pendingError = {
32
60
  file: cleanFile,
33
61
  line: Number.parseInt(lineStr, 10),
34
62
  col: Number.parseInt(colStr, 10),
63
+ code: code ?? "",
35
64
  message: message.trim(),
36
- });
65
+ };
37
66
  }
67
+ continue;
68
+ }
69
+ if (pendingError && line.startsWith(" ")) {
70
+ continuationLines.push(line);
71
+ continue;
38
72
  }
39
73
  if (trimmed.includes("Found") && trimmed.includes("error")) {
74
+ flushPending();
40
75
  setTsErrors(trimmed.match(/Found 0 error/) ? [] : [...currentErrors]);
41
76
  currentErrors = [];
42
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-typescript-check.js","sourceRoot":"","sources":["../../src/cli/use-typescript-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-typescript-check.js","sourceRoot":"","sources":["../../src/cli/use-typescript-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative } from \"node:path\";\nimport spawn from \"cross-spawn\";\nimport { useEffect, useRef, useState } from \"react\";\n\ntype TsError = {\n file: string;\n line: number;\n col: number;\n code: string;\n message: string;\n};\n\n// TypeScript nests from general to specific — the deepest line is the root cause.\nfunction extractBestMessage(\n message: string,\n continuationLines: Array<string>,\n): string {\n if (!continuationLines.length) {\n return message;\n }\n let maxIndent = 0;\n for (const line of continuationLines) {\n const indent = line.length - line.trimStart().length;\n if (indent > maxIndent) {\n maxIndent = indent;\n }\n }\n const deepest = continuationLines\n .filter((l) => l.length - l.trimStart().length === maxIndent)\n .map((l) => l.trim())\n .filter(Boolean)[0];\n return deepest ?? message;\n}\n\nexport function useTypeScriptCheck(): Array<TsError> {\n const tsProcessRef = useRef<ReturnType<typeof spawn> | null>(null);\n const [tsErrors, setTsErrors] = useState<Array<TsError>>([]);\n\n useEffect(() => {\n const tsProcess = spawn(\n \"npx\",\n [\"tsc\", \"--noEmit\", \"--watch\", \"--pretty\", \"false\"],\n {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: true,\n },\n );\n\n tsProcessRef.current = tsProcess;\n\n let outputBuffer = \"\";\n let currentErrors: Array<TsError> = [];\n let pendingError: TsError | null = null;\n let continuationLines: Array<string> = [];\n\n const flushPending = () => {\n if (!pendingError) {\n return;\n }\n pendingError.message = extractBestMessage(\n pendingError.message,\n continuationLines,\n );\n currentErrors.push(pendingError);\n pendingError = null;\n continuationLines = [];\n };\n\n const processOutput = (data: Buffer) => {\n outputBuffer += data.toString();\n const lines = outputBuffer.split(\"\\n\");\n outputBuffer = lines.pop() || \"\";\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n const errorMatch = trimmed.match(\n /^(.+?)\\((\\d+),(\\d+)\\):\\s+error\\s+(TS\\d+)?\\s*:?\\s*(.+)$/,\n );\n if (errorMatch) {\n flushPending();\n const [, file, lineStr, colStr, code, message] = errorMatch;\n if (file && lineStr && colStr && message) {\n let cleanFile = file.trim();\n if (isAbsolute(cleanFile)) {\n cleanFile = relative(process.cwd(), cleanFile);\n }\n pendingError = {\n file: cleanFile,\n line: Number.parseInt(lineStr, 10),\n col: Number.parseInt(colStr, 10),\n code: code ?? \"\",\n message: message.trim(),\n };\n }\n continue;\n }\n\n if (pendingError && line.startsWith(\" \")) {\n continuationLines.push(line);\n continue;\n }\n\n if (trimmed.includes(\"Found\") && trimmed.includes(\"error\")) {\n flushPending();\n setTsErrors(trimmed.match(/Found 0 error/) ? [] : [...currentErrors]);\n currentErrors = [];\n }\n }\n };\n\n if (tsProcess.stdout) {\n tsProcess.stdout.on(\"data\", processOutput);\n }\n if (tsProcess.stderr) {\n tsProcess.stderr.on(\"data\", processOutput);\n }\n\n return () => {\n if (tsProcessRef.current) {\n tsProcessRef.current.kill();\n }\n };\n }, []);\n\n return tsErrors;\n}\n"]}
@@ -1,27 +1,83 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cpSync, rmSync } from "node:fs";
2
+ import { readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import path from "node:path";
3
4
  import { Command } from "@oclif/core";
4
5
  import { Box, render, Text } from "ink";
5
6
  import { useEffect } from "react";
6
7
  import { Header } from "../cli/header.js";
7
8
  import { useExecuteSteps } from "../cli/use-execute-steps.js";
9
+ import { scanAndWriteViewsDts } from "../web/plugin/scan-views.js";
10
+ async function resolveViewsDir(root) {
11
+ const { loadConfigFromFile } = await import("vite");
12
+ const loaded = await loadConfigFromFile({ command: "build", mode: "production" }, undefined, root);
13
+ const isPluginCandidate = (value) => typeof value === "object" && value !== null;
14
+ const plugins = [];
15
+ const walk = (value) => {
16
+ if (Array.isArray(value)) {
17
+ value.forEach(walk);
18
+ }
19
+ else if (isPluginCandidate(value)) {
20
+ plugins.push(value);
21
+ }
22
+ };
23
+ walk(loaded?.config.plugins ?? []);
24
+ return plugins.find((p) => p.name === "skybridge")?.api?.viewsDir;
25
+ }
8
26
  export const commandSteps = [
9
27
  {
10
- label: "Building widgets",
11
- command: "vite build -c web/vite.config.ts",
28
+ label: "Scanning views",
29
+ run: async () => {
30
+ const root = process.cwd();
31
+ const viewsDir = await resolveViewsDir(root);
32
+ scanAndWriteViewsDts(root, viewsDir);
33
+ },
12
34
  },
13
35
  {
14
36
  label: "Compiling server",
15
37
  run: () => rmSync("dist", { recursive: true, force: true }),
16
- command: "tsc -b",
38
+ command: "tsc -b --force",
39
+ },
40
+ {
41
+ label: "Building views",
42
+ command: "vite build",
43
+ },
44
+ {
45
+ label: "Emitting manifest module",
46
+ // Inline the Vite manifest as a JS module so the server can `import` it
47
+ // instead of `readFileSync(process.cwd() + ...)` at runtime — required for
48
+ // workerd, where neither cwd nor the assets directory is readable.
49
+ // The path mirrors `skybridge start`'s entry convention (dist/server.js)
50
+ // so the import in the user's entry resolves to a sibling file.
51
+ run: () => {
52
+ const root = process.cwd();
53
+ const manifest = readFileSync(path.join(root, "dist", "assets", ".vite", "manifest.json"), "utf-8");
54
+ writeFileSync(path.join(root, "dist", "vite-manifest.js"), `export default ${manifest};\n`);
55
+ },
56
+ },
57
+ {
58
+ label: "Emitting Cloudflare redirects",
59
+ // Cloudflare's `assets.directory` maps URL → file literally — no
60
+ // mount-strip like `app.use("/assets", express.static(...))`. Rewrite
61
+ // `/assets/assets/*` to `/assets/*` before lookup; status 200 =
62
+ // server-side rewrite, not HTTP redirect.
63
+ run: () => {
64
+ const root = process.cwd();
65
+ writeFileSync(path.join(root, "dist", "assets", "_redirects"), "/assets/assets/* /assets/:splat 200\n");
66
+ },
17
67
  },
18
68
  {
19
- label: "Copying static assets",
20
- run: () => cpSync("web/dist", "dist/assets", { recursive: true }),
69
+ label: "Emitting Cloudflare headers",
70
+ // Cloudflare's static asset handler bypasses the worker entirely, so
71
+ // `app.use("/assets", cors())` never fires for asset requests. Attach
72
+ // CORS at the edge so cross-origin view iframes can load JS/CSS.
73
+ run: () => {
74
+ const root = process.cwd();
75
+ writeFileSync(path.join(root, "dist", "assets", "_headers"), "/assets/*\n Access-Control-Allow-Origin: *\n");
76
+ },
21
77
  },
22
78
  ];
23
79
  export default class Build extends Command {
24
- static description = "Build the widgets and MCP server";
80
+ static description = "Build the views and MCP server";
25
81
  static examples = ["skybridge build"];
26
82
  static flags = {};
27
83
  async run() {
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAoB,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEhF,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC;QACE,KAAK,EAAE,kBAAkB;QACzB,OAAO,EAAE,kCAAkC;KAC5C;IACD;QACE,KAAK,EAAE,kBAAkB;QACzB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC3D,OAAO,EAAE,QAAQ;KAClB;IACD;QACE,KAAK,EAAE,uBAAuB;QAC9B,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;KAClE;CACF,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,OAAO;IACxC,MAAM,CAAU,WAAW,GAAG,kCAAkC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/C,MAAM,CAAU,KAAK,GAAG,EAAE,CAAC;IAEpB,KAAK,CAAC,GAAG;QACd,MAAM,GAAG,GAAG,GAAG,EAAE;YACf,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAC3C,eAAe,CAAC,YAAY,CAAC,CAAC;YAEhC,SAAS,CAAC,GAAG,EAAE;gBACb,OAAO,EAAE,CAAC;YACZ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YAEd,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC,aACpC,KAAC,MAAM,IAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,YAClC,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,sDAAmC,GAC/C,EAER,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;wBAChC,MAAM,SAAS,GAAG,KAAK,KAAK,WAAW,IAAI,MAAM,KAAK,SAAS,CAAC;wBAChE,MAAM,WAAW,GAAG,KAAK,GAAG,WAAW,IAAI,MAAM,KAAK,SAAS,CAAC;wBAChE,MAAM,OAAO,GAAG,MAAM,KAAK,OAAO,IAAI,KAAK,KAAK,WAAW,CAAC;wBAE5D,OAAO,CACL,KAAC,GAAG,IAAkB,YAAY,EAAE,CAAC,YACnC,MAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,aAC1D,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,EAC9D,IAAI,CAAC,KAAK,IACN,IAJC,IAAI,CAAC,KAAK,CAKd,CACP,CAAC;oBACJ,CAAC,CAAC,EAED,MAAM,KAAK,SAAS,IAAI,CACvB,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,2DAEjB,GACH,CACP,EAEA,MAAM,KAAK,OAAO,IAAI,KAAK,IAAI,CAC9B,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACvC,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,EAAC,IAAI,0CAEf,EACP,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,YACtC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAC/B,KAAC,IAAI,IAAY,KAAK,EAAC,KAAK,YACzB,IAAI,IADI,IAAI,CAER,CACR,CAAC,GACE,IACF,CACP,IACG,CACP,CAAC;QACJ,CAAC,CAAC;QAEF,MAAM,CAAC,KAAC,GAAG,KAAG,EAAE;YACd,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;IACL,CAAC"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.tsx"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { Command } from \"@oclif/core\";\nimport { Box, render, Text } from \"ink\";\nimport { useEffect } from \"react\";\nimport { Header } from \"../cli/header.js\";\nimport { type CommandStep, useExecuteSteps } from \"../cli/use-execute-steps.js\";\nimport { scanAndWriteViewsDts } from \"../web/plugin/scan-views.js\";\n\nasync function resolveViewsDir(root: string): Promise<string | undefined> {\n const { loadConfigFromFile } = await import(\"vite\");\n const loaded = await loadConfigFromFile(\n { command: \"build\", mode: \"production\" },\n undefined,\n root,\n );\n\n const isPluginCandidate = (\n value: unknown,\n ): value is { name?: string; api?: { viewsDir?: string } } =>\n typeof value === \"object\" && value !== null;\n\n const plugins: Array<{ name?: string; api?: { viewsDir?: string } }> = [];\n const walk = (value: unknown) => {\n if (Array.isArray(value)) {\n value.forEach(walk);\n } else if (isPluginCandidate(value)) {\n plugins.push(value);\n }\n };\n walk(loaded?.config.plugins ?? []);\n return plugins.find((p) => p.name === \"skybridge\")?.api?.viewsDir;\n}\n\nexport const commandSteps: CommandStep[] = [\n {\n label: \"Scanning views\",\n run: async () => {\n const root = process.cwd();\n const viewsDir = await resolveViewsDir(root);\n scanAndWriteViewsDts(root, viewsDir);\n },\n },\n {\n label: \"Compiling server\",\n run: () => rmSync(\"dist\", { recursive: true, force: true }),\n command: \"tsc -b --force\",\n },\n {\n label: \"Building views\",\n command: \"vite build\",\n },\n {\n label: \"Emitting manifest module\",\n // Inline the Vite manifest as a JS module so the server can `import` it\n // instead of `readFileSync(process.cwd() + ...)` at runtime — required for\n // workerd, where neither cwd nor the assets directory is readable.\n // The path mirrors `skybridge start`'s entry convention (dist/server.js)\n // so the import in the user's entry resolves to a sibling file.\n run: () => {\n const root = process.cwd();\n const manifest = readFileSync(\n path.join(root, \"dist\", \"assets\", \".vite\", \"manifest.json\"),\n \"utf-8\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"vite-manifest.js\"),\n `export default ${manifest};\\n`,\n );\n },\n },\n\n {\n label: \"Emitting Cloudflare redirects\",\n // Cloudflare's `assets.directory` maps URL → file literally — no\n // mount-strip like `app.use(\"/assets\", express.static(...))`. Rewrite\n // `/assets/assets/*` to `/assets/*` before lookup; status 200 =\n // server-side rewrite, not HTTP redirect.\n run: () => {\n const root = process.cwd();\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"_redirects\"),\n \"/assets/assets/* /assets/:splat 200\\n\",\n );\n },\n },\n {\n label: \"Emitting Cloudflare headers\",\n // Cloudflare's static asset handler bypasses the worker entirely, so\n // `app.use(\"/assets\", cors())` never fires for asset requests. Attach\n // CORS at the edge so cross-origin view iframes can load JS/CSS.\n run: () => {\n const root = process.cwd();\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"_headers\"),\n \"/assets/*\\n Access-Control-Allow-Origin: *\\n\",\n );\n },\n },\n];\n\nexport default class Build extends Command {\n static override description = \"Build the views and MCP server\";\n static override examples = [\"skybridge build\"];\n static override flags = {};\n\n public async run(): Promise<void> {\n const App = () => {\n const { currentStep, status, error, execute } =\n useExecuteSteps(commandSteps);\n\n useEffect(() => {\n execute();\n }, [execute]);\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Header version={this.config.version}>\n <Text color=\"green\"> → building for production…</Text>\n </Header>\n\n {commandSteps.map((step, index) => {\n const isCurrent = index === currentStep && status === \"running\";\n const isCompleted = index < currentStep || status === \"success\";\n const isError = status === \"error\" && index === currentStep;\n\n return (\n <Box key={step.label} marginBottom={0}>\n <Text color={isError ? \"red\" : isCompleted ? \"green\" : \"grey\"}>\n {isError ? \"✗\" : isCompleted ? \"✓\" : isCurrent ? \"⟳\" : \"○\"}{\" \"}\n {step.label}\n </Text>\n </Box>\n );\n })}\n\n {status === \"success\" && (\n <Box marginTop={1}>\n <Text color=\"green\" bold>\n ✓ Build completed successfully!\n </Text>\n </Box>\n )}\n\n {status === \"error\" && error && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"red\" bold>\n ✗ Build failed\n </Text>\n <Box marginTop={1} flexDirection=\"column\">\n {error.split(\"\\n\").map((line) => (\n <Text key={line} color=\"red\">\n {line}\n </Text>\n ))}\n </Box>\n </Box>\n )}\n </Box>\n );\n };\n\n render(<App />, {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n }\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { Command, type Command as CommandLoadable, Help } from "@oclif/core";
2
+ export default class Create extends Command {
3
+ static description: string;
4
+ static strict: boolean;
5
+ run(): Promise<void>;
6
+ }
7
+ export declare class SkybridgeHelp extends Help {
8
+ showCommandHelp(command: CommandLoadable.Loadable): Promise<void>;
9
+ }
@@ -0,0 +1,30 @@
1
+ import { Command, Help } from "@oclif/core";
2
+ import spawn from "cross-spawn";
3
+ function passthrough(args) {
4
+ const { status, error } = spawn.sync("npx", ["--yes", "create-skybridge@latest", ...args], { stdio: "inherit" });
5
+ if (error) {
6
+ console.error(error);
7
+ process.exit(1);
8
+ }
9
+ process.exit(status ?? 1);
10
+ }
11
+ export default class Create extends Command {
12
+ static description = "Scaffold a new Skybridge project";
13
+ static strict = false;
14
+ async run() {
15
+ passthrough(this.argv);
16
+ }
17
+ }
18
+ // Registered as `oclif.helpClass` so that `skybridge create --help` forwards
19
+ // to `create-skybridge --help` (single source of truth for the help text)
20
+ // instead of rendering oclif's auto-generated help. All other commands fall
21
+ // through to the default `Help` behaviour.
22
+ export class SkybridgeHelp extends Help {
23
+ async showCommandHelp(command) {
24
+ if (command.id === "create") {
25
+ passthrough(["--help"]);
26
+ }
27
+ return super.showCommandHelp(command);
28
+ }
29
+ }
30
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAmC,IAAI,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,MAAM,aAAa,CAAC;AAEhC,SAAS,WAAW,CAAC,IAAc;IACjC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,IAAI,CAClC,KAAK,EACL,CAAC,OAAO,EAAE,yBAAyB,EAAE,GAAG,IAAI,CAAC,EAC7C,EAAE,KAAK,EAAE,SAAS,EAAE,CACrB,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,OAAO;IACzC,MAAM,CAAU,WAAW,GAAG,kCAAkC,CAAC;IACjE,MAAM,CAAU,MAAM,GAAG,KAAK,CAAC;IAExB,KAAK,CAAC,GAAG;QACd,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;;AAGH,6EAA6E;AAC7E,0EAA0E;AAC1E,4EAA4E;AAC5E,2CAA2C;AAC3C,MAAM,OAAO,aAAc,SAAQ,IAAI;IAC5B,KAAK,CAAC,eAAe,CAC5B,OAAiC;QAEjC,IAAI,OAAO,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC5B,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;CACF","sourcesContent":["import { Command, type Command as CommandLoadable, Help } from \"@oclif/core\";\nimport spawn from \"cross-spawn\";\n\nfunction passthrough(args: string[]): never {\n const { status, error } = spawn.sync(\n \"npx\",\n [\"--yes\", \"create-skybridge@latest\", ...args],\n { stdio: \"inherit\" },\n );\n if (error) {\n console.error(error);\n process.exit(1);\n }\n process.exit(status ?? 1);\n}\n\nexport default class Create extends Command {\n static override description = \"Scaffold a new Skybridge project\";\n static override strict = false;\n\n public async run(): Promise<void> {\n passthrough(this.argv);\n }\n}\n\n// Registered as `oclif.helpClass` so that `skybridge create --help` forwards\n// to `create-skybridge --help` (single source of truth for the help text)\n// instead of rendering oclif's auto-generated help. All other commands fall\n// through to the default `Help` behaviour.\nexport class SkybridgeHelp extends Help {\n override async showCommandHelp(\n command: CommandLoadable.Loadable,\n ): Promise<void> {\n if (command.id === \"create\") {\n passthrough([\"--help\"]);\n }\n return super.showCommandHelp(command);\n }\n}\n"]}