skybridge 0.0.0-dev.9ad4667 → 0.0.0-dev.9addcf5

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 (519) hide show
  1. package/README.md +126 -115
  2. package/dist/cli/build-helpers.d.ts +11 -0
  3. package/dist/cli/build-helpers.js +107 -0
  4. package/dist/cli/build-helpers.js.map +1 -0
  5. package/dist/cli/build-helpers.test.js +132 -0
  6. package/dist/cli/build-helpers.test.js.map +1 -0
  7. package/dist/cli/build-steps.d.ts +2 -0
  8. package/dist/cli/build-steps.js +73 -0
  9. package/dist/cli/build-steps.js.map +1 -0
  10. package/dist/cli/build-steps.test.js +52 -0
  11. package/dist/cli/build-steps.test.js.map +1 -0
  12. package/dist/cli/detect-port.d.ts +18 -0
  13. package/dist/cli/detect-port.js +49 -0
  14. package/dist/cli/detect-port.js.map +1 -0
  15. package/dist/cli/header.d.ts +1 -1
  16. package/dist/cli/header.js +1 -1
  17. package/dist/cli/header.js.map +1 -1
  18. package/dist/cli/resolve-skybridge-config.d.ts +5 -0
  19. package/dist/cli/resolve-skybridge-config.js +18 -0
  20. package/dist/cli/resolve-skybridge-config.js.map +1 -0
  21. package/dist/cli/run-command.js.map +1 -1
  22. package/dist/cli/run-plain.d.ts +18 -0
  23. package/dist/cli/run-plain.js +90 -0
  24. package/dist/cli/run-plain.js.map +1 -0
  25. package/dist/cli/telemetry.js +2 -0
  26. package/dist/cli/telemetry.js.map +1 -1
  27. package/dist/cli/tunnel-control-server.d.ts +9 -0
  28. package/dist/cli/tunnel-control-server.js +31 -0
  29. package/dist/cli/tunnel-control-server.js.map +1 -0
  30. package/dist/cli/tunnel-control-server.test.js +39 -0
  31. package/dist/cli/tunnel-control-server.test.js.map +1 -0
  32. package/dist/cli/tunnel-handler.d.ts +3 -0
  33. package/dist/cli/tunnel-handler.js +48 -0
  34. package/dist/cli/tunnel-handler.js.map +1 -0
  35. package/dist/cli/tunnel-handler.test.js +105 -0
  36. package/dist/cli/tunnel-handler.test.js.map +1 -0
  37. package/dist/cli/tunnel.d.ts +57 -0
  38. package/dist/cli/tunnel.js +154 -0
  39. package/dist/cli/tunnel.js.map +1 -0
  40. package/dist/cli/tunnel.test.js +190 -0
  41. package/dist/cli/tunnel.test.js.map +1 -0
  42. package/dist/cli/types.d.ts +5 -0
  43. package/dist/cli/types.js +2 -0
  44. package/dist/cli/types.js.map +1 -0
  45. package/dist/cli/use-execute-steps.d.ts +1 -1
  46. package/dist/cli/use-execute-steps.js.map +1 -1
  47. package/dist/cli/use-messages.d.ts +3 -0
  48. package/dist/cli/use-messages.js +11 -0
  49. package/dist/cli/use-messages.js.map +1 -0
  50. package/dist/cli/use-nodemon.d.ts +26 -6
  51. package/dist/cli/use-nodemon.js +99 -54
  52. package/dist/cli/use-nodemon.js.map +1 -1
  53. package/dist/cli/use-open-browser.d.ts +1 -0
  54. package/dist/cli/use-open-browser.js +44 -0
  55. package/dist/cli/use-open-browser.js.map +1 -0
  56. package/dist/cli/use-tunnel.d.ts +14 -0
  57. package/dist/cli/use-tunnel.js +131 -0
  58. package/dist/cli/use-tunnel.js.map +1 -0
  59. package/dist/cli/use-typescript-check.d.ts +9 -2
  60. package/dist/cli/use-typescript-check.js +89 -51
  61. package/dist/cli/use-typescript-check.js.map +1 -1
  62. package/dist/commands/build.d.ts +0 -3
  63. package/dist/commands/build.js +3 -18
  64. package/dist/commands/build.js.map +1 -1
  65. package/dist/commands/create.d.ts +9 -0
  66. package/dist/commands/create.js +30 -0
  67. package/dist/commands/create.js.map +1 -0
  68. package/dist/commands/dev.d.ts +5 -1
  69. package/dist/commands/dev.js +106 -9
  70. package/dist/commands/dev.js.map +1 -1
  71. package/dist/commands/start.d.ts +3 -1
  72. package/dist/commands/start.js +36 -9
  73. package/dist/commands/start.js.map +1 -1
  74. package/dist/commands/telemetry/disable.js.map +1 -1
  75. package/dist/commands/telemetry/enable.js.map +1 -1
  76. package/dist/commands/telemetry/status.js.map +1 -1
  77. package/dist/context-warnings.d.ts +5 -0
  78. package/dist/context-warnings.js +31 -0
  79. package/dist/context-warnings.js.map +1 -0
  80. package/dist/context-warnings.test.d.ts +1 -0
  81. package/dist/context-warnings.test.js +28 -0
  82. package/dist/context-warnings.test.js.map +1 -0
  83. package/dist/server/app.d.ts +194 -0
  84. package/dist/server/app.js +266 -0
  85. package/dist/server/app.js.map +1 -0
  86. package/dist/server/app.test.d.ts +1 -0
  87. package/dist/server/app.test.js +97 -0
  88. package/dist/server/app.test.js.map +1 -0
  89. package/dist/server/asset-base-url-transform-plugin.d.ts +6 -6
  90. package/dist/server/asset-base-url-transform-plugin.js +25 -11
  91. package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
  92. package/dist/server/asset-base-url-transform-plugin.test.js +92 -14
  93. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
  94. package/dist/server/auth/discovery.d.ts +33 -0
  95. package/dist/server/auth/discovery.js +56 -0
  96. package/dist/server/auth/discovery.js.map +1 -0
  97. package/dist/server/auth/discovery.test.d.ts +1 -0
  98. package/dist/server/auth/discovery.test.js +93 -0
  99. package/dist/server/auth/discovery.test.js.map +1 -0
  100. package/dist/server/auth/index.d.ts +48 -0
  101. package/dist/server/auth/index.js +2 -0
  102. package/dist/server/auth/index.js.map +1 -0
  103. package/dist/server/auth/providers/auth0.d.ts +40 -0
  104. package/dist/server/auth/providers/auth0.js +36 -0
  105. package/dist/server/auth/providers/auth0.js.map +1 -0
  106. package/dist/server/auth/providers/auth0.test.d.ts +1 -0
  107. package/dist/server/auth/providers/auth0.test.js +50 -0
  108. package/dist/server/auth/providers/auth0.test.js.map +1 -0
  109. package/dist/server/auth/providers/authplane.d.ts +49 -0
  110. package/dist/server/auth/providers/authplane.js +57 -0
  111. package/dist/server/auth/providers/authplane.js.map +1 -0
  112. package/dist/server/auth/providers/authplane.test.d.ts +1 -0
  113. package/dist/server/auth/providers/authplane.test.js +143 -0
  114. package/dist/server/auth/providers/authplane.test.js.map +1 -0
  115. package/dist/server/auth/providers/clerk.d.ts +51 -0
  116. package/dist/server/auth/providers/clerk.js +19 -0
  117. package/dist/server/auth/providers/clerk.js.map +1 -0
  118. package/dist/server/auth/providers/clerk.test.d.ts +1 -0
  119. package/dist/server/auth/providers/clerk.test.js +30 -0
  120. package/dist/server/auth/providers/clerk.test.js.map +1 -0
  121. package/dist/server/auth/providers/custom.d.ts +42 -0
  122. package/dist/server/auth/providers/custom.js +59 -0
  123. package/dist/server/auth/providers/custom.js.map +1 -0
  124. package/dist/server/auth/providers/custom.test.d.ts +1 -0
  125. package/dist/server/auth/providers/custom.test.js +161 -0
  126. package/dist/server/auth/providers/custom.test.js.map +1 -0
  127. package/dist/server/auth/providers/descope.d.ts +44 -0
  128. package/dist/server/auth/providers/descope.js +44 -0
  129. package/dist/server/auth/providers/descope.js.map +1 -0
  130. package/dist/server/auth/providers/descope.test.d.ts +1 -0
  131. package/dist/server/auth/providers/descope.test.js +62 -0
  132. package/dist/server/auth/providers/descope.test.js.map +1 -0
  133. package/dist/server/auth/providers/shared.d.ts +2 -0
  134. package/dist/server/auth/providers/shared.js +6 -0
  135. package/dist/server/auth/providers/shared.js.map +1 -0
  136. package/dist/server/auth/providers/shared.test.d.ts +1 -0
  137. package/dist/server/auth/providers/shared.test.js +10 -0
  138. package/dist/server/auth/providers/shared.test.js.map +1 -0
  139. package/dist/server/auth/providers/stytch.d.ts +25 -0
  140. package/dist/server/auth/providers/stytch.js +16 -0
  141. package/dist/server/auth/providers/stytch.js.map +1 -0
  142. package/dist/server/auth/providers/verify-spy.d.ts +12 -0
  143. package/dist/server/auth/providers/verify-spy.js +21 -0
  144. package/dist/server/auth/providers/verify-spy.js.map +1 -0
  145. package/dist/server/auth/providers/workos.d.ts +34 -0
  146. package/dist/server/auth/providers/workos.js +16 -0
  147. package/dist/server/auth/providers/workos.js.map +1 -0
  148. package/dist/server/auth/security-schemes.d.ts +23 -0
  149. package/dist/server/auth/security-schemes.js +64 -0
  150. package/dist/server/auth/security-schemes.js.map +1 -0
  151. package/dist/server/auth/security-schemes.test.d.ts +1 -0
  152. package/dist/server/auth/security-schemes.test.js +99 -0
  153. package/dist/server/auth/security-schemes.test.js.map +1 -0
  154. package/dist/server/auth/setup.d.ts +6 -0
  155. package/dist/server/auth/setup.js +91 -0
  156. package/dist/server/auth/setup.js.map +1 -0
  157. package/dist/server/auth/setup.test.d.ts +1 -0
  158. package/dist/server/auth/setup.test.js +514 -0
  159. package/dist/server/auth/setup.test.js.map +1 -0
  160. package/dist/server/auth/verify.d.ts +48 -0
  161. package/dist/server/auth/verify.js +62 -0
  162. package/dist/server/auth/verify.js.map +1 -0
  163. package/dist/server/auth/verify.test.d.ts +1 -0
  164. package/dist/server/auth/verify.test.js +165 -0
  165. package/dist/server/auth/verify.test.js.map +1 -0
  166. package/dist/server/auth-extra.test-d.d.ts +1 -0
  167. package/dist/server/auth-extra.test-d.js +130 -0
  168. package/dist/server/auth-extra.test-d.js.map +1 -0
  169. package/dist/server/auth.d.ts +57 -0
  170. package/dist/server/auth.js +27 -0
  171. package/dist/server/auth.js.map +1 -0
  172. package/dist/server/build-manifest.test.d.ts +1 -0
  173. package/dist/server/build-manifest.test.js +28 -0
  174. package/dist/server/build-manifest.test.js.map +1 -0
  175. package/dist/server/content-helpers.d.ts +67 -0
  176. package/dist/server/content-helpers.js +79 -0
  177. package/dist/server/content-helpers.js.map +1 -0
  178. package/dist/server/content-helpers.test.d.ts +1 -0
  179. package/dist/server/content-helpers.test.js +70 -0
  180. package/dist/server/content-helpers.test.js.map +1 -0
  181. package/dist/server/express.d.ts +31 -0
  182. package/dist/server/express.js +107 -0
  183. package/dist/server/express.js.map +1 -0
  184. package/dist/server/express.test.d.ts +1 -0
  185. package/dist/server/express.test.js +608 -0
  186. package/dist/server/express.test.js.map +1 -0
  187. package/dist/server/file-ref.d.ts +28 -0
  188. package/dist/server/file-ref.js +27 -0
  189. package/dist/server/file-ref.js.map +1 -0
  190. package/dist/server/host.d.ts +2 -0
  191. package/dist/server/host.js +11 -0
  192. package/dist/server/host.js.map +1 -0
  193. package/dist/server/host.test.d.ts +1 -0
  194. package/dist/server/host.test.js +13 -0
  195. package/dist/server/host.test.js.map +1 -0
  196. package/dist/server/index.d.ts +21 -3
  197. package/dist/server/index.js +17 -2
  198. package/dist/server/index.js.map +1 -1
  199. package/dist/server/inferUtilityTypes.d.ts +6 -6
  200. package/dist/server/inferUtilityTypes.js.map +1 -1
  201. package/dist/server/mcp-errors.test.d.ts +1 -0
  202. package/dist/server/mcp-errors.test.js +41 -0
  203. package/dist/server/mcp-errors.test.js.map +1 -0
  204. package/dist/server/metric.d.ts +14 -0
  205. package/dist/server/metric.js +62 -0
  206. package/dist/server/metric.js.map +1 -0
  207. package/dist/server/middleware.d.ts +162 -0
  208. package/dist/server/middleware.js +126 -0
  209. package/dist/server/middleware.js.map +1 -0
  210. package/dist/server/middleware.test-d.d.ts +1 -0
  211. package/dist/server/middleware.test-d.js +75 -0
  212. package/dist/server/middleware.test-d.js.map +1 -0
  213. package/dist/server/middleware.test.d.ts +1 -0
  214. package/dist/server/middleware.test.js +601 -0
  215. package/dist/server/middleware.test.js.map +1 -0
  216. package/dist/server/register-tool.test.d.ts +1 -0
  217. package/dist/server/register-tool.test.js +110 -0
  218. package/dist/server/register-tool.test.js.map +1 -0
  219. package/dist/server/requestOrigin.d.ts +7 -0
  220. package/dist/server/requestOrigin.js +41 -0
  221. package/dist/server/requestOrigin.js.map +1 -0
  222. package/dist/server/requestOrigin.test.d.ts +1 -0
  223. package/dist/server/requestOrigin.test.js +28 -0
  224. package/dist/server/requestOrigin.test.js.map +1 -0
  225. package/dist/server/server.d.ts +498 -64
  226. package/dist/server/server.js +591 -123
  227. package/dist/server/server.js.map +1 -1
  228. package/dist/server/skills-integration.test.d.ts +1 -0
  229. package/dist/server/skills-integration.test.js +86 -0
  230. package/dist/server/skills-integration.test.js.map +1 -0
  231. package/dist/server/skills.d.ts +27 -0
  232. package/dist/server/skills.js +188 -0
  233. package/dist/server/skills.js.map +1 -0
  234. package/dist/server/skills.test.d.ts +1 -0
  235. package/dist/server/skills.test.js +243 -0
  236. package/dist/server/skills.test.js.map +1 -0
  237. package/dist/server/templateHelper.d.ts +2 -8
  238. package/dist/server/templateHelper.js +3 -22
  239. package/dist/server/templateHelper.js.map +1 -1
  240. package/dist/server/templates.generated.d.ts +4 -0
  241. package/dist/server/templates.generated.js +43 -0
  242. package/dist/server/templates.generated.js.map +1 -0
  243. package/dist/server/tool-extra.test.d.ts +1 -0
  244. package/dist/server/tool-extra.test.js +88 -0
  245. package/dist/server/tool-extra.test.js.map +1 -0
  246. package/dist/server/tunnel-proxy-router.d.ts +7 -0
  247. package/dist/server/tunnel-proxy-router.js +110 -0
  248. package/dist/server/tunnel-proxy-router.js.map +1 -0
  249. package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
  250. package/dist/server/tunnel-proxy-router.test.js +229 -0
  251. package/dist/server/tunnel-proxy-router.test.js.map +1 -0
  252. package/dist/server/view-name.test-d.d.ts +1 -0
  253. package/dist/server/view-name.test-d.js +8 -0
  254. package/dist/server/view-name.test-d.js.map +1 -0
  255. package/dist/server/view-resource-resolution.test.d.ts +6 -0
  256. package/dist/server/view-resource-resolution.test.js +176 -0
  257. package/dist/server/view-resource-resolution.test.js.map +1 -0
  258. package/dist/server/viewsDevServer.d.ts +14 -0
  259. package/dist/server/viewsDevServer.js +48 -0
  260. package/dist/server/viewsDevServer.js.map +1 -0
  261. package/dist/test/utils.d.ts +21 -31
  262. package/dist/test/utils.js +222 -198
  263. package/dist/test/utils.js.map +1 -1
  264. package/dist/test/view.test.d.ts +1 -0
  265. package/dist/test/view.test.js +540 -0
  266. package/dist/test/view.test.js.map +1 -0
  267. package/dist/version.d.ts +1 -0
  268. package/dist/version.js +3 -0
  269. package/dist/version.js.map +1 -0
  270. package/dist/views/index.d.ts +10 -0
  271. package/dist/views/index.js +3 -0
  272. package/dist/views/index.js.map +1 -0
  273. package/dist/views/scan-views.d.ts +16 -0
  274. package/dist/views/scan-views.js +88 -0
  275. package/dist/views/scan-views.js.map +1 -0
  276. package/dist/views/scan-views.test.d.ts +1 -0
  277. package/dist/views/scan-views.test.js +99 -0
  278. package/dist/views/scan-views.test.js.map +1 -0
  279. package/dist/views/validate-view.d.ts +1 -0
  280. package/dist/views/validate-view.js +9 -0
  281. package/dist/views/validate-view.js.map +1 -0
  282. package/dist/views/validate-view.test.d.ts +1 -0
  283. package/dist/views/validate-view.test.js +24 -0
  284. package/dist/views/validate-view.test.js.map +1 -0
  285. package/dist/web/bridges/adaptor.d.ts +56 -0
  286. package/dist/web/bridges/adaptor.js +397 -0
  287. package/dist/web/bridges/adaptor.js.map +1 -0
  288. package/dist/web/bridges/adaptor.test.d.ts +1 -0
  289. package/dist/web/bridges/adaptor.test.js +264 -0
  290. package/dist/web/bridges/adaptor.test.js.map +1 -0
  291. package/dist/web/bridges/apps-sdk/bridge.d.ts +7 -2
  292. package/dist/web/bridges/apps-sdk/bridge.js +15 -3
  293. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  294. package/dist/web/bridges/apps-sdk/index.d.ts +1 -2
  295. package/dist/web/bridges/apps-sdk/index.js +0 -1
  296. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  297. package/dist/web/bridges/apps-sdk/types.d.ts +32 -14
  298. package/dist/web/bridges/apps-sdk/types.js.map +1 -1
  299. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
  300. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
  301. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
  302. package/dist/web/bridges/get-adaptor.d.ts +7 -0
  303. package/dist/web/bridges/get-adaptor.js +9 -7
  304. package/dist/web/bridges/get-adaptor.js.map +1 -1
  305. package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
  306. package/dist/web/bridges/get-adaptor.test.js +32 -0
  307. package/dist/web/bridges/get-adaptor.test.js.map +1 -0
  308. package/dist/web/bridges/index.js.map +1 -1
  309. package/dist/web/bridges/mcp-app/bridge.d.ts +27 -31
  310. package/dist/web/bridges/mcp-app/bridge.js +118 -198
  311. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  312. package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
  313. package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
  314. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
  315. package/dist/web/bridges/mcp-app/index.d.ts +0 -1
  316. package/dist/web/bridges/mcp-app/index.js +0 -1
  317. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  318. package/dist/web/bridges/mcp-app/types.d.ts +19 -2
  319. package/dist/web/bridges/mcp-app/types.js.map +1 -1
  320. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +17 -3
  321. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +14 -2
  322. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
  323. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +2 -42
  324. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
  325. package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
  326. package/dist/web/bridges/mcp-app/view-tools.test.js +143 -0
  327. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
  328. package/dist/web/bridges/types.d.ts +162 -15
  329. package/dist/web/bridges/types.js +14 -1
  330. package/dist/web/bridges/types.js.map +1 -1
  331. package/dist/web/bridges/types.test.d.ts +1 -0
  332. package/dist/web/bridges/types.test.js +19 -0
  333. package/dist/web/bridges/types.test.js.map +1 -0
  334. package/dist/web/bridges/use-host-context.d.ts +5 -0
  335. package/dist/web/bridges/use-host-context.js +5 -0
  336. package/dist/web/bridges/use-host-context.js.map +1 -1
  337. package/dist/web/components/modal-provider.d.ts +1 -1
  338. package/dist/web/components/modal-provider.js +5 -6
  339. package/dist/web/components/modal-provider.js.map +1 -1
  340. package/dist/web/create-store.d.ts +26 -0
  341. package/dist/web/create-store.js +43 -3
  342. package/dist/web/create-store.js.map +1 -1
  343. package/dist/web/create-store.test.js +33 -27
  344. package/dist/web/create-store.test.js.map +1 -1
  345. package/dist/web/data-llm.d.ts +35 -2
  346. package/dist/web/data-llm.js +34 -5
  347. package/dist/web/data-llm.js.map +1 -1
  348. package/dist/web/data-llm.test.js +59 -37
  349. package/dist/web/data-llm.test.js.map +1 -1
  350. package/dist/web/generate-helpers.d.ts +40 -32
  351. package/dist/web/generate-helpers.js +38 -30
  352. package/dist/web/generate-helpers.js.map +1 -1
  353. package/dist/web/generate-helpers.test-d.js +33 -31
  354. package/dist/web/generate-helpers.test-d.js.map +1 -1
  355. package/dist/web/generate-helpers.test.js +2 -2
  356. package/dist/web/generate-helpers.test.js.map +1 -1
  357. package/dist/web/helpers/state.d.ts +2 -2
  358. package/dist/web/helpers/state.js +11 -11
  359. package/dist/web/helpers/state.js.map +1 -1
  360. package/dist/web/helpers/state.test.js +9 -9
  361. package/dist/web/helpers/state.test.js.map +1 -1
  362. package/dist/web/hooks/index.d.ts +11 -3
  363. package/dist/web/hooks/index.js +10 -2
  364. package/dist/web/hooks/index.js.map +1 -1
  365. package/dist/web/hooks/openai-extensions.test.d.ts +1 -0
  366. package/dist/web/hooks/openai-extensions.test.js +107 -0
  367. package/dist/web/hooks/openai-extensions.test.js.map +1 -0
  368. package/dist/web/hooks/test/utils.d.ts +7 -2
  369. package/dist/web/hooks/test/utils.js +18 -3
  370. package/dist/web/hooks/test/utils.js.map +1 -1
  371. package/dist/web/hooks/use-call-tool.d.ts +47 -2
  372. package/dist/web/hooks/use-call-tool.js +43 -10
  373. package/dist/web/hooks/use-call-tool.js.map +1 -1
  374. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
  375. package/dist/web/hooks/use-call-tool.test.js +75 -29
  376. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  377. package/dist/web/hooks/use-deep-link.d.ts +22 -0
  378. package/dist/web/hooks/use-deep-link.js +34 -0
  379. package/dist/web/hooks/use-deep-link.js.map +1 -0
  380. package/dist/web/hooks/use-display-mode.d.ts +23 -3
  381. package/dist/web/hooks/use-display-mode.js +20 -0
  382. package/dist/web/hooks/use-display-mode.js.map +1 -1
  383. package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
  384. package/dist/web/hooks/use-display-mode.test-d.js +8 -0
  385. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
  386. package/dist/web/hooks/use-display-mode.test.js +57 -21
  387. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  388. package/dist/web/hooks/use-download.d.ts +3 -0
  389. package/dist/web/hooks/use-download.js +7 -0
  390. package/dist/web/hooks/use-download.js.map +1 -0
  391. package/dist/web/hooks/use-download.test.d.ts +1 -0
  392. package/dist/web/hooks/use-download.test.js +103 -0
  393. package/dist/web/hooks/use-download.test.js.map +1 -0
  394. package/dist/web/hooks/use-files.d.ts +34 -1
  395. package/dist/web/hooks/use-files.js +33 -0
  396. package/dist/web/hooks/use-files.js.map +1 -1
  397. package/dist/web/hooks/use-files.test.js +36 -4
  398. package/dist/web/hooks/use-files.test.js.map +1 -1
  399. package/dist/web/hooks/use-host.d.ts +44 -0
  400. package/dist/web/hooks/use-host.js +45 -0
  401. package/dist/web/hooks/use-host.js.map +1 -0
  402. package/dist/web/hooks/use-host.test.d.ts +1 -0
  403. package/dist/web/hooks/use-host.test.js +59 -0
  404. package/dist/web/hooks/use-host.test.js.map +1 -0
  405. package/dist/web/hooks/use-model-context.d.ts +44 -0
  406. package/dist/web/hooks/use-model-context.js +63 -0
  407. package/dist/web/hooks/use-model-context.js.map +1 -0
  408. package/dist/web/hooks/use-open-external.d.ts +20 -1
  409. package/dist/web/hooks/use-open-external.js +17 -1
  410. package/dist/web/hooks/use-open-external.js.map +1 -1
  411. package/dist/web/hooks/use-open-external.test.js +40 -15
  412. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  413. package/dist/web/hooks/use-open-file.d.ts +20 -0
  414. package/dist/web/hooks/use-open-file.js +25 -0
  415. package/dist/web/hooks/use-open-file.js.map +1 -0
  416. package/dist/web/hooks/use-register-view-tool.d.ts +37 -0
  417. package/dist/web/hooks/use-register-view-tool.js +50 -0
  418. package/dist/web/hooks/use-register-view-tool.js.map +1 -0
  419. package/dist/web/hooks/use-request-close.d.ts +16 -0
  420. package/dist/web/hooks/use-request-close.js +21 -0
  421. package/dist/web/hooks/use-request-close.js.map +1 -0
  422. package/dist/web/hooks/use-request-close.test.d.ts +1 -0
  423. package/dist/web/hooks/use-request-close.test.js +47 -0
  424. package/dist/web/hooks/use-request-close.test.js.map +1 -0
  425. package/dist/web/hooks/use-request-modal.d.ts +16 -1
  426. package/dist/web/hooks/use-request-modal.js +19 -4
  427. package/dist/web/hooks/use-request-modal.js.map +1 -1
  428. package/dist/web/hooks/use-request-modal.test.js +16 -2
  429. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  430. package/dist/web/hooks/use-request-size.d.ts +20 -0
  431. package/dist/web/hooks/use-request-size.js +24 -0
  432. package/dist/web/hooks/use-request-size.js.map +1 -0
  433. package/dist/web/hooks/use-request-size.test.d.ts +1 -0
  434. package/dist/web/hooks/use-request-size.test.js +47 -0
  435. package/dist/web/hooks/use-request-size.test.js.map +1 -0
  436. package/dist/web/hooks/use-send-follow-up-message.d.ts +25 -1
  437. package/dist/web/hooks/use-send-follow-up-message.js +25 -2
  438. package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
  439. package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
  440. package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
  441. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
  442. package/dist/web/hooks/use-set-open-in-app-url.test.js +29 -21
  443. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  444. package/dist/web/hooks/use-tool-info.d.ts +46 -14
  445. package/dist/web/hooks/use-tool-info.js +29 -7
  446. package/dist/web/hooks/use-tool-info.js.map +1 -1
  447. package/dist/web/hooks/use-tool-info.test-d.js +11 -34
  448. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
  449. package/dist/web/hooks/use-tool-info.test.js +43 -38
  450. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  451. package/dist/web/hooks/use-user.d.ts +5 -2
  452. package/dist/web/hooks/use-user.js +23 -4
  453. package/dist/web/hooks/use-user.js.map +1 -1
  454. package/dist/web/hooks/use-user.test.js +105 -29
  455. package/dist/web/hooks/use-user.test.js.map +1 -1
  456. package/dist/web/hooks/use-view-state.d.ts +25 -0
  457. package/dist/web/hooks/use-view-state.js +42 -0
  458. package/dist/web/hooks/use-view-state.js.map +1 -0
  459. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  460. package/dist/web/hooks/use-view-state.test.js +212 -0
  461. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  462. package/dist/web/hooks/use-viewport.d.ts +20 -0
  463. package/dist/web/hooks/use-viewport.js +21 -0
  464. package/dist/web/hooks/use-viewport.js.map +1 -0
  465. package/dist/web/hooks/use-viewport.test.d.ts +1 -0
  466. package/dist/web/hooks/use-viewport.test.js +109 -0
  467. package/dist/web/hooks/use-viewport.test.js.map +1 -0
  468. package/dist/web/index.d.ts +1 -2
  469. package/dist/web/index.js +1 -2
  470. package/dist/web/index.js.map +1 -1
  471. package/dist/web/mount-view.d.ts +21 -0
  472. package/dist/web/mount-view.js +43 -0
  473. package/dist/web/mount-view.js.map +1 -0
  474. package/dist/web/proxy.js.map +1 -1
  475. package/dist/web/types.d.ts +4 -0
  476. package/dist/web/types.js.map +1 -1
  477. package/package.json +53 -29
  478. package/tsconfig.base.json +33 -0
  479. package/dist/server/templates/development.hbs +0 -67
  480. package/dist/server/templates/production.hbs +0 -6
  481. package/dist/server/widgetsDevServer.d.ts +0 -12
  482. package/dist/server/widgetsDevServer.js +0 -57
  483. package/dist/server/widgetsDevServer.js.map +0 -1
  484. package/dist/test/widget.test.js +0 -255
  485. package/dist/test/widget.test.js.map +0 -1
  486. package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -22
  487. package/dist/web/bridges/apps-sdk/adaptor.js +0 -64
  488. package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
  489. package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -36
  490. package/dist/web/bridges/mcp-app/adaptor.js +0 -188
  491. package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
  492. package/dist/web/hooks/use-layout.d.ts +0 -22
  493. package/dist/web/hooks/use-layout.js +0 -23
  494. package/dist/web/hooks/use-layout.js.map +0 -1
  495. package/dist/web/hooks/use-layout.test.js +0 -96
  496. package/dist/web/hooks/use-layout.test.js.map +0 -1
  497. package/dist/web/hooks/use-widget-state.d.ts +0 -4
  498. package/dist/web/hooks/use-widget-state.js +0 -32
  499. package/dist/web/hooks/use-widget-state.js.map +0 -1
  500. package/dist/web/hooks/use-widget-state.test.js +0 -61
  501. package/dist/web/hooks/use-widget-state.test.js.map +0 -1
  502. package/dist/web/mount-widget.d.ts +0 -1
  503. package/dist/web/mount-widget.js +0 -27
  504. package/dist/web/mount-widget.js.map +0 -1
  505. package/dist/web/plugin/data-llm.test.js +0 -81
  506. package/dist/web/plugin/data-llm.test.js.map +0 -1
  507. package/dist/web/plugin/plugin.d.ts +0 -2
  508. package/dist/web/plugin/plugin.js +0 -46
  509. package/dist/web/plugin/plugin.js.map +0 -1
  510. package/dist/web/plugin/transform-data-llm.d.ts +0 -12
  511. package/dist/web/plugin/transform-data-llm.js +0 -96
  512. package/dist/web/plugin/transform-data-llm.js.map +0 -1
  513. package/dist/web/plugin/transform-data-llm.test.js +0 -81
  514. package/dist/web/plugin/transform-data-llm.test.js.map +0 -1
  515. /package/dist/{test/widget.test.d.ts → cli/build-helpers.test.d.ts} +0 -0
  516. /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-steps.test.d.ts} +0 -0
  517. /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
  518. /package/dist/{web/plugin/data-llm.test.d.ts → cli/tunnel-handler.test.d.ts} +0 -0
  519. /package/dist/{web/plugin/transform-data-llm.test.d.ts → cli/tunnel.test.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-deep-link.js","sourceRoot":"","sources":["../../../src/web/hooks/use-deep-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,GAAG,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,EAAE,GAAG,CAAC;IACrD,IACE,OAAO,GAAG,KAAK,QAAQ;QACvB,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;QACpB,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC;QACrB,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EACjB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["import { useMcpAppContext } from \"../bridges/index.js\";\n\n/**\n * App-relative URL (path and query, e.g. `\"/parts?tag=bolt\"`) that ChatGPT\n * opened the view on through a deep link. Updates when the user follows\n * another deep link while the view is open. `undefined` when the view was not\n * opened through a deep link, on hosts other than ChatGPT, or when the host\n * sends a URL that isn't app-relative (it must start with a single `/`) or\n * that contains a fragment.\n *\n * Deep links target global entrypoints: set `openai.entrypoints` to include\n * `{ type: \"global\" }` on the tool.\n *\n * @example\n * ```tsx\n * const deepLink = useDeepLink();\n * useEffect(() => {\n * if (deepLink) navigate(deepLink);\n * }, [deepLink, navigate]);\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-deep-link\n */\nexport function useDeepLink(): string | undefined {\n const url = useMcpAppContext(\"openai/deepLink\")?.url;\n if (\n typeof url !== \"string\" ||\n !url.startsWith(\"/\") ||\n url.startsWith(\"//\") ||\n url.startsWith(\"/\\\\\") ||\n url.includes(\"#\")\n ) {\n return undefined;\n }\n return url;\n}\n"]}
@@ -1,4 +1,24 @@
1
- import type { DisplayMode } from "../bridges/types.js";
2
- export declare function useDisplayMode(): readonly [DisplayMode, (mode: DisplayMode) => Promise<{
3
- mode: DisplayMode;
1
+ import type { RequestDisplayMode } from "../bridges/types.js";
2
+ /**
3
+ * Read and change the view's display mode (`"inline"`, `"pip"`, `"fullscreen"`).
4
+ *
5
+ * Returns a tuple `[displayMode, setDisplayMode]`. `setDisplayMode` asks the
6
+ * host to switch modes — the host returns the mode it actually applied, which
7
+ * may differ from the request. The reported value also updates when the host
8
+ * changes the mode on its own (e.g. user expands the widget).
9
+ *
10
+ * `"modal"` is reachable via {@link useRequestModal}, not this hook. To react
11
+ * to layout changes that come with display-mode switches (e.g. `maxHeight`),
12
+ * pair with {@link useViewport}.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * const [mode, setMode] = useDisplayMode();
17
+ * <button onClick={() => setMode("fullscreen")}>Expand</button>
18
+ * ```
19
+ *
20
+ * @see https://docs.skybridge.tech/api-reference/use-display-mode
21
+ */
22
+ export declare function useDisplayMode(): readonly [import("../index.js").DisplayMode, (mode: RequestDisplayMode) => Promise<{
23
+ mode: RequestDisplayMode;
4
24
  }>];
@@ -1,5 +1,25 @@
1
1
  import { useCallback } from "react";
2
2
  import { getAdaptor, useHostContext } from "../bridges/index.js";
3
+ /**
4
+ * Read and change the view's display mode (`"inline"`, `"pip"`, `"fullscreen"`).
5
+ *
6
+ * Returns a tuple `[displayMode, setDisplayMode]`. `setDisplayMode` asks the
7
+ * host to switch modes — the host returns the mode it actually applied, which
8
+ * may differ from the request. The reported value also updates when the host
9
+ * changes the mode on its own (e.g. user expands the widget).
10
+ *
11
+ * `"modal"` is reachable via {@link useRequestModal}, not this hook. To react
12
+ * to layout changes that come with display-mode switches (e.g. `maxHeight`),
13
+ * pair with {@link useViewport}.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * const [mode, setMode] = useDisplayMode();
18
+ * <button onClick={() => setMode("fullscreen")}>Expand</button>
19
+ * ```
20
+ *
21
+ * @see https://docs.skybridge.tech/api-reference/use-display-mode
22
+ */
3
23
  export function useDisplayMode() {
4
24
  const displayMode = useHostContext("displayMode");
5
25
  const adaptor = getAdaptor();
@@ -1 +1 @@
1
- {"version":3,"file":"use-display-mode.js","sourceRoot":"","sources":["../../../src/web/hooks/use-display-mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGjE,MAAM,UAAU,cAAc;IAC5B,MAAM,WAAW,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,IAAiB,EAAE,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,EACvD,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,CAAC,WAAW,EAAE,cAAc,CAAU,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"use-display-mode.js","sourceRoot":"","sources":["../../../src/web/hooks/use-display-mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGjE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,WAAW,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,IAAwB,EAAE,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAC9D,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,CAAC,WAAW,EAAE,cAAc,CAAU,CAAC;AAChD,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor, useHostContext } from \"../bridges/index.js\";\nimport type { RequestDisplayMode } from \"../bridges/types.js\";\n\n/**\n * Read and change the view's display mode (`\"inline\"`, `\"pip\"`, `\"fullscreen\"`).\n *\n * Returns a tuple `[displayMode, setDisplayMode]`. `setDisplayMode` asks the\n * host to switch modes — the host returns the mode it actually applied, which\n * may differ from the request. The reported value also updates when the host\n * changes the mode on its own (e.g. user expands the widget).\n *\n * `\"modal\"` is reachable via {@link useRequestModal}, not this hook. To react\n * to layout changes that come with display-mode switches (e.g. `maxHeight`),\n * pair with {@link useViewport}.\n *\n * @example\n * ```tsx\n * const [mode, setMode] = useDisplayMode();\n * <button onClick={() => setMode(\"fullscreen\")}>Expand</button>\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-display-mode\n */\nexport function useDisplayMode() {\n const displayMode = useHostContext(\"displayMode\");\n const adaptor = getAdaptor();\n const setDisplayMode = useCallback(\n (mode: RequestDisplayMode) => adaptor.requestDisplayMode(mode),\n [adaptor],\n );\n\n return [displayMode, setDisplayMode] as const;\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { expectTypeOf, test } from "vitest";
2
+ test("setDisplayMode only accepts requestable display modes", () => {
3
+ expectTypeOf().toEqualTypeOf();
4
+ // @ts-expect-error "modal" is a host state, not a valid request mode
5
+ const _invalidMode = "modal";
6
+ void _invalidMode;
7
+ });
8
+ //# sourceMappingURL=use-display-mode.test-d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-display-mode.test-d.js","sourceRoot":"","sources":["../../../src/web/hooks/use-display-mode.test-d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAI5C,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;IAGjE,YAAY,EAET,CAAC,aAAa,EAAsB,CAAC;IAExC,qEAAqE;IACrE,MAAM,YAAY,GAAkC,OAAO,CAAC;IAC5D,KAAK,YAAY,CAAC;AACpB,CAAC,CAAC,CAAC","sourcesContent":["import { expectTypeOf, test } from \"vitest\";\nimport type { RequestDisplayMode } from \"../bridges/types.js\";\nimport type { useDisplayMode } from \"./use-display-mode.js\";\n\ntest(\"setDisplayMode only accepts requestable display modes\", () => {\n type SetDisplayMode = ReturnType<typeof useDisplayMode>[1];\n\n expectTypeOf<\n Parameters<SetDisplayMode>[0]\n >().toEqualTypeOf<RequestDisplayMode>();\n\n // @ts-expect-error \"modal\" is a host state, not a valid request mode\n const _invalidMode: Parameters<SetDisplayMode>[0] = \"modal\";\n void _invalidMode;\n});\n"]}
@@ -1,39 +1,75 @@
1
- import { act, renderHook } from "@testing-library/react";
2
- import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
1
+ import { act, fireEvent, renderHook, waitFor } from "@testing-library/react";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { HostAdaptor } from "../bridges/adaptor.js";
4
+ import { AppsSdkBridge } from "../bridges/apps-sdk/bridge.js";
5
+ import { getAdaptor } from "../bridges/get-adaptor.js";
6
+ import { McpAppBridge } from "../bridges/mcp-app/bridge.js";
7
+ import { getMcpAppHostPostMessageMock, MockResizeObserver, } from "./test/utils.js";
3
8
  import { useDisplayMode } from "./use-display-mode.js";
4
9
  describe("useDisplayMode", () => {
5
- let OpenaiMock;
6
10
  beforeEach(() => {
7
- OpenaiMock = {
8
- displayMode: "inline",
9
- requestDisplayMode: vi.fn().mockResolvedValue({ mode: "inline" }),
10
- };
11
- vi.stubGlobal("openai", OpenaiMock);
12
- vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
11
+ HostAdaptor.resetInstance();
12
+ McpAppBridge.resetInstance();
13
+ AppsSdkBridge.resetInstance();
14
+ vi.stubGlobal("skybridge", {});
15
+ vi.stubGlobal("openai", { view: { mode: "inline" } });
16
+ vi.stubGlobal("ResizeObserver", MockResizeObserver);
13
17
  });
14
18
  afterEach(() => {
15
19
  vi.unstubAllGlobals();
16
20
  vi.resetAllMocks();
21
+ HostAdaptor.resetInstance();
22
+ McpAppBridge.resetInstance();
23
+ AppsSdkBridge.resetInstance();
17
24
  });
18
- it("should return the current display mode from window.openai.displayMode", () => {
19
- OpenaiMock.displayMode = "inline";
25
+ it("should return the current display mode from MCP host context", async () => {
26
+ vi.stubGlobal("parent", {
27
+ postMessage: getMcpAppHostPostMessageMock({ displayMode: "inline" }),
28
+ });
20
29
  const { result } = renderHook(() => useDisplayMode());
21
- expect(result.current[0]).toBe("inline");
30
+ await waitFor(() => {
31
+ expect(result.current[0]).toBe("inline");
32
+ });
22
33
  });
23
- it("should return different display modes when window.openai.displayMode changes", () => {
24
- OpenaiMock.displayMode = "inline";
25
- const { result, rerender } = renderHook(() => useDisplayMode());
26
- expect(result.current[0]).toBe("inline");
27
- OpenaiMock.displayMode = "fullscreen";
28
- rerender();
29
- expect(result.current[0]).toBe("fullscreen");
34
+ it("should update display mode on host-context-changed notification", async () => {
35
+ vi.stubGlobal("parent", {
36
+ postMessage: getMcpAppHostPostMessageMock({ displayMode: "inline" }),
37
+ });
38
+ const { result } = renderHook(() => useDisplayMode());
39
+ await waitFor(() => {
40
+ expect(result.current[0]).toBe("inline");
41
+ });
42
+ act(() => {
43
+ fireEvent(window, new MessageEvent("message", {
44
+ source: window.parent,
45
+ data: {
46
+ jsonrpc: "2.0",
47
+ method: "ui/notifications/host-context-changed",
48
+ params: { displayMode: "fullscreen" },
49
+ },
50
+ }));
51
+ });
52
+ await waitFor(() => {
53
+ expect(result.current[0]).toBe("fullscreen");
54
+ });
30
55
  });
31
- it("should call window.openai.requestDisplayMode with correct mode when setDisplayMode is called", async () => {
56
+ it("should call requestDisplayMode on the adaptor when setDisplayMode is called", async () => {
57
+ vi.stubGlobal("parent", {
58
+ postMessage: getMcpAppHostPostMessageMock({ displayMode: "inline" }),
59
+ });
32
60
  const { result } = renderHook(() => useDisplayMode());
61
+ await waitFor(() => expect(result.current[0]).toBe("inline"));
62
+ // Patch mcp.getApp to avoid real protocol request
63
+ const adaptor = getAdaptor();
64
+ const fakeApp = {
65
+ requestDisplayMode: vi.fn().mockResolvedValue({ mode: "fullscreen" }),
66
+ };
67
+ // biome-ignore lint/suspicious/noExplicitAny: test seam
68
+ adaptor.mcp.getApp = vi.fn().mockResolvedValue(fakeApp);
33
69
  await act(async () => {
34
70
  await result.current[1]("fullscreen");
35
71
  });
36
- expect(OpenaiMock.requestDisplayMode).toHaveBeenCalledWith({
72
+ expect(fakeApp.requestDisplayMode).toHaveBeenCalledWith({
37
73
  mode: "fullscreen",
38
74
  });
39
75
  });
@@ -1 +1 @@
1
- {"version":3,"file":"use-display-mode.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-display-mode.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EACL,SAAS,EACT,UAAU,EACV,QAAQ,EACR,MAAM,EACN,EAAE,EAEF,EAAE,GACH,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,IAAI,UAGH,CAAC;IAEF,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG;YACX,WAAW,EAAE,QAAQ;YACrB,kBAAkB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;SAClE,CAAC;QACF,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACpC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,UAAU,CAAC,WAAW,GAAG,QAAQ,CAAC;QAClC,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;QAEtD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,UAAU,CAAC,WAAW,GAAG,QAAQ,CAAC;QAClC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;QAEhE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEzC,UAAU,CAAC,WAAW,GAAG,YAAY,CAAC;QACtC,QAAQ,EAAE,CAAC;QAEX,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;QAC5G,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;QAEtD,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE;YACnB,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,oBAAoB,CAAC;YACzD,IAAI,EAAE,YAAY;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"use-display-mode.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-display-mode.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,UAAU,CAAC,GAAG,EAAE;QACd,WAAW,CAAC,aAAa,EAAE,CAAC;QAC5B,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,aAAa,CAAC,aAAa,EAAE,CAAC;QAC9B,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACtD,EAAE,CAAC,UAAU,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,WAAW,CAAC,aAAa,EAAE,CAAC;QAC5B,YAAY,CAAC,aAAa,EAAE,CAAC;QAC7B,aAAa,CAAC,aAAa,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;YACtB,WAAW,EAAE,4BAA4B,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;SACrE,CAAC,CAAC;QAEH,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;QAEtD,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;YACtB,WAAW,EAAE,4BAA4B,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;SACrE,CAAC,CAAC;QAEH,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;QAEtD,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,GAAG,EAAE;YACP,SAAS,CACP,MAAM,EACN,IAAI,YAAY,CAAC,SAAS,EAAE;gBAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,IAAI,EAAE;oBACJ,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,uCAAuC;oBAC/C,MAAM,EAAE,EAAE,WAAW,EAAE,YAAY,EAAE;iBACtC;aACF,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;YACtB,WAAW,EAAE,4BAA4B,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;SACrE,CAAC,CAAC;QAEH,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;QAEtD,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAE9D,kDAAkD;QAClD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG;YACd,kBAAkB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;SACtE,CAAC;QACF,wDAAwD;QACvD,OAAe,CAAC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAEjE,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE;YACnB,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,oBAAoB,CAAC;YACtD,IAAI,EAAE,YAAY;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { act, fireEvent, renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"../bridges/adaptor.js\";\nimport { AppsSdkBridge } from \"../bridges/apps-sdk/bridge.js\";\nimport { getAdaptor } from \"../bridges/get-adaptor.js\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useDisplayMode } from \"./use-display-mode.js\";\n\ndescribe(\"useDisplayMode\", () => {\n beforeEach(() => {\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"openai\", { view: { mode: \"inline\" } });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"should return the current display mode from MCP host context\", async () => {\n vi.stubGlobal(\"parent\", {\n postMessage: getMcpAppHostPostMessageMock({ displayMode: \"inline\" }),\n });\n\n const { result } = renderHook(() => useDisplayMode());\n\n await waitFor(() => {\n expect(result.current[0]).toBe(\"inline\");\n });\n });\n\n it(\"should update display mode on host-context-changed notification\", async () => {\n vi.stubGlobal(\"parent\", {\n postMessage: getMcpAppHostPostMessageMock({ displayMode: \"inline\" }),\n });\n\n const { result } = renderHook(() => useDisplayMode());\n\n await waitFor(() => {\n expect(result.current[0]).toBe(\"inline\");\n });\n\n act(() => {\n fireEvent(\n window,\n new MessageEvent(\"message\", {\n source: window.parent,\n data: {\n jsonrpc: \"2.0\",\n method: \"ui/notifications/host-context-changed\",\n params: { displayMode: \"fullscreen\" },\n },\n }),\n );\n });\n\n await waitFor(() => {\n expect(result.current[0]).toBe(\"fullscreen\");\n });\n });\n\n it(\"should call requestDisplayMode on the adaptor when setDisplayMode is called\", async () => {\n vi.stubGlobal(\"parent\", {\n postMessage: getMcpAppHostPostMessageMock({ displayMode: \"inline\" }),\n });\n\n const { result } = renderHook(() => useDisplayMode());\n\n await waitFor(() => expect(result.current[0]).toBe(\"inline\"));\n\n // Patch mcp.getApp to avoid real protocol request\n const adaptor = getAdaptor();\n const fakeApp = {\n requestDisplayMode: vi.fn().mockResolvedValue({ mode: \"fullscreen\" }),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n\n await act(async () => {\n await result.current[1](\"fullscreen\");\n });\n\n expect(fakeApp.requestDisplayMode).toHaveBeenCalledWith({\n mode: \"fullscreen\",\n });\n });\n});\n"]}
@@ -0,0 +1,3 @@
1
+ import type { DownloadParams, DownloadResult } from "../bridges/types.js";
2
+ export type DownloadFn = (params: DownloadParams) => Promise<DownloadResult>;
3
+ export declare function useDownload(): DownloadFn;
@@ -0,0 +1,7 @@
1
+ import { useCallback } from "react";
2
+ import { getAdaptor } from "../bridges/index.js";
3
+ export function useDownload() {
4
+ const adaptor = getAdaptor();
5
+ return useCallback((params) => adaptor.download(params), [adaptor]);
6
+ }
7
+ //# sourceMappingURL=use-download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-download.js","sourceRoot":"","sources":["../../../src/web/hooks/use-download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKjD,MAAM,UAAU,WAAW;IACzB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,OAAO,WAAW,CAChB,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EACpC,CAAC,OAAO,CAAC,CACV,CAAC;AACJ,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\nimport type { DownloadParams, DownloadResult } from \"../bridges/types.js\";\n\nexport type DownloadFn = (params: DownloadParams) => Promise<DownloadResult>;\n\nexport function useDownload(): DownloadFn {\n const adaptor = getAdaptor();\n return useCallback<DownloadFn>(\n (params) => adaptor.download(params),\n [adaptor],\n );\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,103 @@
1
+ import { renderHook, waitFor } from "@testing-library/react";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { HostAdaptor } from "../bridges/adaptor.js";
4
+ import { McpAppBridge } from "../bridges/mcp-app/bridge.js";
5
+ import { getMcpAppHostPostMessageMock, MockResizeObserver, } from "./test/utils.js";
6
+ import { useDownload } from "./use-download.js";
7
+ const params = {
8
+ contents: [
9
+ {
10
+ type: "resource",
11
+ resource: {
12
+ uri: "file:///export.json",
13
+ mimeType: "application/json",
14
+ text: '{"hello":"world"}',
15
+ },
16
+ },
17
+ ],
18
+ };
19
+ describe("useDownload", () => {
20
+ describe("apps-sdk host", () => {
21
+ beforeEach(() => {
22
+ vi.stubGlobal("openai", {});
23
+ vi.stubGlobal("skybridge", {});
24
+ vi.stubGlobal("ResizeObserver", MockResizeObserver);
25
+ vi.stubGlobal("parent", { postMessage: getMcpAppHostPostMessageMock() });
26
+ });
27
+ afterEach(() => {
28
+ vi.unstubAllGlobals();
29
+ vi.resetAllMocks();
30
+ HostAdaptor.resetInstance();
31
+ McpAppBridge.resetInstance();
32
+ });
33
+ it("returns { isError: true } and logs an error", async () => {
34
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
35
+ const { result } = renderHook(() => useDownload());
36
+ const res = await result.current(params);
37
+ expect(res).toEqual({ isError: true });
38
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("does not support ui/download-file"));
39
+ });
40
+ });
41
+ describe("mcp-app host without downloadFile capability", () => {
42
+ beforeEach(() => {
43
+ vi.stubGlobal("openai", undefined);
44
+ vi.stubGlobal("skybridge", {});
45
+ vi.stubGlobal("ResizeObserver", MockResizeObserver);
46
+ vi.stubGlobal("parent", { postMessage: getMcpAppHostPostMessageMock() });
47
+ });
48
+ afterEach(async () => {
49
+ vi.unstubAllGlobals();
50
+ vi.resetAllMocks();
51
+ HostAdaptor.resetInstance();
52
+ McpAppBridge.resetInstance();
53
+ });
54
+ it("returns { isError: true } and logs an error", async () => {
55
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
56
+ const { result } = renderHook(() => useDownload());
57
+ const res = await result.current(params);
58
+ expect(res).toEqual({ isError: true });
59
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("does not support ui/download-file"));
60
+ });
61
+ });
62
+ describe("mcp-app host with downloadFile capability", () => {
63
+ let postMessageMock;
64
+ beforeEach(() => {
65
+ vi.stubGlobal("openai", undefined);
66
+ vi.stubGlobal("skybridge", {});
67
+ vi.stubGlobal("ResizeObserver", MockResizeObserver);
68
+ postMessageMock = getMcpAppHostPostMessageMock({}, { hostCapabilities: { downloadFile: {} } });
69
+ vi.stubGlobal("parent", { postMessage: postMessageMock });
70
+ });
71
+ afterEach(async () => {
72
+ vi.unstubAllGlobals();
73
+ vi.resetAllMocks();
74
+ HostAdaptor.resetInstance();
75
+ McpAppBridge.resetInstance();
76
+ });
77
+ it("sends ui/download-file with the provided contents", async () => {
78
+ const { result } = renderHook(() => useDownload());
79
+ const res = await result.current(params);
80
+ expect(res).toEqual({});
81
+ await waitFor(() => {
82
+ expect(postMessageMock).toHaveBeenCalledWith(expect.objectContaining({
83
+ jsonrpc: "2.0",
84
+ method: "ui/download-file",
85
+ params,
86
+ }), "*");
87
+ });
88
+ });
89
+ it("returns { isError: true } when the host denies the request", async () => {
90
+ HostAdaptor.resetInstance();
91
+ McpAppBridge.resetInstance();
92
+ postMessageMock = getMcpAppHostPostMessageMock({}, {
93
+ hostCapabilities: { downloadFile: {} },
94
+ downloadFileResult: { isError: true },
95
+ });
96
+ vi.stubGlobal("parent", { postMessage: postMessageMock });
97
+ const { result } = renderHook(() => useDownload());
98
+ const res = await result.current(params);
99
+ expect(res).toEqual({ isError: true });
100
+ });
101
+ });
102
+ });
103
+ //# sourceMappingURL=use-download.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-download.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-download.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"../bridges/adaptor.js\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport type { DownloadParams } from \"../bridges/types.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useDownload } from \"./use-download.js\";\n\nconst params: DownloadParams = {\n contents: [\n {\n type: \"resource\",\n resource: {\n uri: \"file:///export.json\",\n mimeType: \"application/json\",\n text: '{\"hello\":\"world\"}',\n },\n },\n ],\n};\n\ndescribe(\"useDownload\", () => {\n describe(\"apps-sdk host\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"openai\", {});\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.stubGlobal(\"parent\", { postMessage: getMcpAppHostPostMessageMock() });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n });\n\n it(\"returns { isError: true } and logs an error\", async () => {\n const errorSpy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current(params);\n\n expect(res).toEqual({ isError: true });\n expect(errorSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"does not support ui/download-file\"),\n );\n });\n });\n\n describe(\"mcp-app host without downloadFile capability\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"openai\", undefined);\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.stubGlobal(\"parent\", { postMessage: getMcpAppHostPostMessageMock() });\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n });\n\n it(\"returns { isError: true } and logs an error\", async () => {\n const errorSpy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current(params);\n\n expect(res).toEqual({ isError: true });\n expect(errorSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"does not support ui/download-file\"),\n );\n });\n });\n\n describe(\"mcp-app host with downloadFile capability\", () => {\n let postMessageMock: ReturnType<typeof getMcpAppHostPostMessageMock>;\n\n beforeEach(() => {\n vi.stubGlobal(\"openai\", undefined);\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n postMessageMock = getMcpAppHostPostMessageMock(\n {},\n { hostCapabilities: { downloadFile: {} } },\n );\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n });\n\n it(\"sends ui/download-file with the provided contents\", async () => {\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current(params);\n\n expect(res).toEqual({});\n await waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({\n jsonrpc: \"2.0\",\n method: \"ui/download-file\",\n params,\n }),\n \"*\",\n );\n });\n });\n\n it(\"returns { isError: true } when the host denies the request\", async () => {\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n postMessageMock = getMcpAppHostPostMessageMock(\n {},\n {\n hostCapabilities: { downloadFile: {} },\n downloadFileResult: { isError: true },\n },\n );\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current(params);\n\n expect(res).toEqual({ isError: true });\n });\n });\n});\n"]}
@@ -1,6 +1,39 @@
1
+ /**
2
+ * File operations bound to the current host: `upload` a `File`, resolve a
3
+ * `downloadUrl` for an uploaded file, and `selectFiles` to open the host's
4
+ * native file picker.
5
+ *
6
+ * Currently Apps-SDK-only — calling any of these from MCP Apps throws.
7
+ * `selectFiles` additionally requires a ChatGPT host version that exposes the
8
+ * picker; it throws if the capability is unavailable.
9
+ *
10
+ * `upload` returns `FileMetadata` (`fileId`, optional `fileName`, `mimeType`).
11
+ * To pass an uploaded file to a tool whose input uses {@link FileRef}, first
12
+ * call `getDownloadUrl` and then build the ref yourself — field names differ
13
+ * (camelCase on the client, snake_case in the schema) and `download_url` is
14
+ * required.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const { upload, getDownloadUrl } = useFiles();
19
+ * const meta = await upload(file);
20
+ * const { downloadUrl } = await getDownloadUrl(meta);
21
+ * callTool({
22
+ * document: {
23
+ * file_id: meta.fileId,
24
+ * download_url: downloadUrl,
25
+ * file_name: meta.fileName,
26
+ * mime_type: meta.mimeType,
27
+ * },
28
+ * });
29
+ * ```
30
+ *
31
+ * @see https://docs.skybridge.tech/api-reference/use-files
32
+ */
1
33
  export declare function useFiles(): {
2
- upload: (file: File) => Promise<import("../index.js").FileMetadata>;
34
+ upload: (file: File, options?: import("../index.js").UploadFileOptions) => Promise<import("../index.js").FileMetadata>;
3
35
  getDownloadUrl: (file: import("../index.js").FileMetadata) => Promise<{
4
36
  downloadUrl: string;
5
37
  }>;
38
+ selectFiles: () => Promise<import("../index.js").FileMetadata[]>;
6
39
  };
@@ -1,9 +1,42 @@
1
1
  import { getAdaptor } from "../bridges/index.js";
2
+ /**
3
+ * File operations bound to the current host: `upload` a `File`, resolve a
4
+ * `downloadUrl` for an uploaded file, and `selectFiles` to open the host's
5
+ * native file picker.
6
+ *
7
+ * Currently Apps-SDK-only — calling any of these from MCP Apps throws.
8
+ * `selectFiles` additionally requires a ChatGPT host version that exposes the
9
+ * picker; it throws if the capability is unavailable.
10
+ *
11
+ * `upload` returns `FileMetadata` (`fileId`, optional `fileName`, `mimeType`).
12
+ * To pass an uploaded file to a tool whose input uses {@link FileRef}, first
13
+ * call `getDownloadUrl` and then build the ref yourself — field names differ
14
+ * (camelCase on the client, snake_case in the schema) and `download_url` is
15
+ * required.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const { upload, getDownloadUrl } = useFiles();
20
+ * const meta = await upload(file);
21
+ * const { downloadUrl } = await getDownloadUrl(meta);
22
+ * callTool({
23
+ * document: {
24
+ * file_id: meta.fileId,
25
+ * download_url: downloadUrl,
26
+ * file_name: meta.fileName,
27
+ * mime_type: meta.mimeType,
28
+ * },
29
+ * });
30
+ * ```
31
+ *
32
+ * @see https://docs.skybridge.tech/api-reference/use-files
33
+ */
2
34
  export function useFiles() {
3
35
  const adaptor = getAdaptor();
4
36
  return {
5
37
  upload: adaptor.uploadFile,
6
38
  getDownloadUrl: adaptor.getFileDownloadUrl,
39
+ selectFiles: adaptor.selectFiles,
7
40
  };
8
41
  }
9
42
  //# sourceMappingURL=use-files.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-files.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,UAAU,QAAQ;IACtB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,UAAU;QAC1B,cAAc,EAAE,OAAO,CAAC,kBAAkB;KAC3C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"use-files.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,UAAU;QAC1B,cAAc,EAAE,OAAO,CAAC,kBAAkB;QAC1C,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { getAdaptor } from \"../bridges/index.js\";\n\n/**\n * File operations bound to the current host: `upload` a `File`, resolve a\n * `downloadUrl` for an uploaded file, and `selectFiles` to open the host's\n * native file picker.\n *\n * Currently Apps-SDK-only — calling any of these from MCP Apps throws.\n * `selectFiles` additionally requires a ChatGPT host version that exposes the\n * picker; it throws if the capability is unavailable.\n *\n * `upload` returns `FileMetadata` (`fileId`, optional `fileName`, `mimeType`).\n * To pass an uploaded file to a tool whose input uses {@link FileRef}, first\n * call `getDownloadUrl` and then build the ref yourself — field names differ\n * (camelCase on the client, snake_case in the schema) and `download_url` is\n * required.\n *\n * @example\n * ```tsx\n * const { upload, getDownloadUrl } = useFiles();\n * const meta = await upload(file);\n * const { downloadUrl } = await getDownloadUrl(meta);\n * callTool({\n * document: {\n * file_id: meta.fileId,\n * download_url: downloadUrl,\n * file_name: meta.fileName,\n * mime_type: meta.mimeType,\n * },\n * });\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-files\n */\nexport function useFiles() {\n const adaptor = getAdaptor();\n return {\n upload: adaptor.uploadFile,\n getDownloadUrl: adaptor.getFileDownloadUrl,\n selectFiles: adaptor.selectFiles,\n };\n}\n"]}
@@ -1,24 +1,56 @@
1
1
  import { renderHook } from "@testing-library/react";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { HostAdaptor } from "../bridges/adaptor.js";
4
+ import { AppsSdkBridge } from "../bridges/apps-sdk/bridge.js";
5
+ import { McpAppBridge } from "../bridges/mcp-app/bridge.js";
3
6
  import { useFiles } from "./use-files.js";
4
7
  describe("useFiles", () => {
5
8
  const OpenaiMock = {
6
- uploadFile: vi.fn(),
9
+ uploadFile: vi.fn().mockResolvedValue({
10
+ fileId: `sediment://file_abc123`,
11
+ }),
7
12
  getFileDownloadUrl: vi.fn(),
13
+ widgetState: null,
14
+ setWidgetState: vi.fn(),
8
15
  };
9
16
  beforeEach(() => {
10
- vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
17
+ HostAdaptor.resetInstance();
18
+ McpAppBridge.resetInstance();
19
+ AppsSdkBridge.resetInstance();
20
+ vi.stubGlobal("skybridge", {});
11
21
  vi.stubGlobal("openai", OpenaiMock);
22
+ vi.stubGlobal("parent", { postMessage: vi.fn() });
12
23
  });
13
24
  afterEach(() => {
14
25
  vi.unstubAllGlobals();
15
- vi.resetAllMocks();
26
+ vi.clearAllMocks();
27
+ HostAdaptor.resetInstance();
28
+ McpAppBridge.resetInstance();
29
+ AppsSdkBridge.resetInstance();
16
30
  });
17
31
  const dummyFile = new File([], "test.txt");
18
32
  it("should upload a file to ChatGPT", () => {
19
33
  const { result } = renderHook(() => useFiles());
20
34
  result.current.upload(dummyFile);
21
- expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile);
35
+ expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, undefined);
36
+ });
37
+ it("should upload a file with library option", () => {
38
+ const { result } = renderHook(() => useFiles());
39
+ result.current.upload(dummyFile, { library: true });
40
+ expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, {
41
+ library: true,
42
+ });
43
+ });
44
+ it("should select files from ChatGPT", async () => {
45
+ const selectedFiles = [
46
+ { fileId: "file_1", fileName: "doc.pdf", mimeType: "application/pdf" },
47
+ ];
48
+ OpenaiMock.selectFiles = vi.fn().mockResolvedValue(selectedFiles);
49
+ const { result } = renderHook(() => useFiles());
50
+ const files = await result.current.selectFiles();
51
+ expect(OpenaiMock.selectFiles).toHaveBeenCalled();
52
+ expect(files).toEqual(selectedFiles);
53
+ delete OpenaiMock.selectFiles;
22
54
  });
23
55
  it("should download a file from ChatGPT", () => {
24
56
  const fileId = "123";
@@ -1 +1 @@
1
- {"version":3,"file":"use-files.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,MAAM,UAAU,GAAG;QACjB,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;QACnB,kBAAkB,EAAE,EAAE,CAAC,EAAE,EAAE;KAC5B,CAAC;IAEF,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;QACrD,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAE3C,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEhD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,MAAM,GAAG,KAAK,CAAC;QACrB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEhD,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"use-files.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderHook } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"../bridges/adaptor.js\";\nimport { AppsSdkBridge } from \"../bridges/apps-sdk/bridge.js\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport { useFiles } from \"./use-files.js\";\n\ndescribe(\"useFiles\", () => {\n const OpenaiMock: Record<string, unknown> = {\n uploadFile: vi.fn().mockResolvedValue({\n fileId: `sediment://file_abc123`,\n }),\n getFileDownloadUrl: vi.fn(),\n widgetState: null,\n setWidgetState: vi.fn(),\n };\n\n beforeEach(() => {\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.clearAllMocks();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n const dummyFile = new File([], \"test.txt\");\n\n it(\"should upload a file to ChatGPT\", () => {\n const { result } = renderHook(() => useFiles());\n\n result.current.upload(dummyFile);\n expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, undefined);\n });\n\n it(\"should upload a file with library option\", () => {\n const { result } = renderHook(() => useFiles());\n\n result.current.upload(dummyFile, { library: true });\n expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, {\n library: true,\n });\n });\n\n it(\"should select files from ChatGPT\", async () => {\n const selectedFiles = [\n { fileId: \"file_1\", fileName: \"doc.pdf\", mimeType: \"application/pdf\" },\n ];\n OpenaiMock.selectFiles = vi.fn().mockResolvedValue(selectedFiles);\n\n const { result } = renderHook(() => useFiles());\n\n const files = await result.current.selectFiles();\n expect(OpenaiMock.selectFiles).toHaveBeenCalled();\n expect(files).toEqual(selectedFiles);\n\n delete OpenaiMock.selectFiles;\n });\n\n it(\"should download a file from ChatGPT\", () => {\n const fileId = \"123\";\n const { result } = renderHook(() => useFiles());\n\n result.current.getDownloadUrl({ fileId });\n expect(OpenaiMock.getFileDownloadUrl).toHaveBeenCalledWith({ fileId });\n });\n});\n"]}
@@ -0,0 +1,44 @@
1
+ import type { McpUiHostCapabilities } from "@modelcontextprotocol/ext-apps";
2
+ /**
3
+ * Known host applications, as normalized slugs. Unrecognized hosts surface
4
+ * their raw `hostInfo.name` string instead.
5
+ */
6
+ export type Host = "chatgpt" | "claude" | "cursor" | "goose" | "mistral-vibe" | "alpic";
7
+ /**
8
+ * OpenAI MCP extensions the host advertises. Each flag is `true` only when the
9
+ * host sends a truthy `hostCapabilities.experimental["openai/<name>"]`, so every flag is
10
+ * `false` before the handshake and on hosts other than ChatGPT.
11
+ */
12
+ export type OpenAIHostCapabilities = {
13
+ /** Read, subscribe to and write the file opened through a file entrypoint. */
14
+ resource: boolean;
15
+ /** Titles, thumbnails and background content in `ui/update-model-context`. */
16
+ modelContext: boolean;
17
+ /** `ui/message` targets and titled items. */
18
+ message: boolean;
19
+ /** Open a local file with `openai/files/open`. */
20
+ files: boolean;
21
+ };
22
+ export type HostInfo = {
23
+ name: Host | (string & {}) | undefined;
24
+ version: string | undefined;
25
+ /** The host's MCP Apps capabilities, `undefined` until the handshake. */
26
+ capabilities: McpUiHostCapabilities | undefined;
27
+ /** OpenAI MCP extensions the host supports. */
28
+ openaiCapabilities: OpenAIHostCapabilities;
29
+ };
30
+ /**
31
+ * Identity and capabilities of the host application rendering the view, from
32
+ * the MCP Apps `ui/initialize` handshake. `name` is normalized to a
33
+ * {@link Host} slug when recognized, otherwise the raw string; `name`,
34
+ * `version` and `capabilities` are `undefined` until the handshake resolves
35
+ * (the view renders first and re-renders once it lands).
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * const { name, openaiCapabilities } = useHost();
40
+ * if (name === "claude") return <ClaudeLayout />;
41
+ * if (openaiCapabilities.files) return <OpenFileButton />;
42
+ * ```
43
+ */
44
+ export declare function useHost(): HostInfo;
@@ -0,0 +1,45 @@
1
+ import { useMemo } from "react";
2
+ import { useMcpAppContext } from "../bridges/index.js";
3
+ const HOST_BY_REPORTED_NAME = {
4
+ chatgpt: "chatgpt",
5
+ Claude: "claude",
6
+ Cursor: "cursor",
7
+ "MCP-UI Host": "goose",
8
+ "Le Chat": "mistral-vibe",
9
+ "alpic-playground": "alpic",
10
+ };
11
+ /**
12
+ * Identity and capabilities of the host application rendering the view, from
13
+ * the MCP Apps `ui/initialize` handshake. `name` is normalized to a
14
+ * {@link Host} slug when recognized, otherwise the raw string; `name`,
15
+ * `version` and `capabilities` are `undefined` until the handshake resolves
16
+ * (the view renders first and re-renders once it lands).
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const { name, openaiCapabilities } = useHost();
21
+ * if (name === "claude") return <ClaudeLayout />;
22
+ * if (openaiCapabilities.files) return <OpenFileButton />;
23
+ * ```
24
+ */
25
+ export function useHost() {
26
+ const hostInfo = useMcpAppContext("hostInfo");
27
+ const capabilities = useMcpAppContext("hostCapabilities") ?? undefined;
28
+ const name = hostInfo?.name;
29
+ const openaiCapabilities = useMemo(() => {
30
+ const supports = (extension) => Boolean(capabilities?.experimental?.[`openai/${extension}`]);
31
+ return {
32
+ resource: supports("resource"),
33
+ modelContext: supports("modelContext"),
34
+ message: supports("message"),
35
+ files: supports("files"),
36
+ };
37
+ }, [capabilities]);
38
+ return {
39
+ name: name !== undefined ? (HOST_BY_REPORTED_NAME[name] ?? name) : undefined,
40
+ version: hostInfo?.version,
41
+ capabilities,
42
+ openaiCapabilities,
43
+ };
44
+ }
45
+ //# sourceMappingURL=use-host.js.map