akanjs 3.0.2 → 3.1.0-rc.1

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 (395) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/base/baseEnv.ts +0 -4
  3. package/base/primitiveRegistry.ts +2 -2
  4. package/client/cookie.ts +5 -1
  5. package/client/csrLocation.ts +122 -0
  6. package/client/csrTypes.ts +36 -4
  7. package/client/device.ts +2 -1
  8. package/client/frameConfig.ts +78 -7
  9. package/client/index.ts +3 -0
  10. package/client/makePageProto.tsx +3 -2
  11. package/client/native/external-view.ts +1 -0
  12. package/client/route/RootLayoutDefinition.ts +7 -1
  13. package/client/route/RouteHead.ts +108 -0
  14. package/client/route/index.ts +1 -0
  15. package/client/router.ts +36 -23
  16. package/client/rscNavigation.ts +8 -2
  17. package/client/viewportMeta.ts +60 -0
  18. package/client/webManifest.ts +18 -0
  19. package/common/Logger.ts +7 -2
  20. package/common/PipelineNames.ts +31 -0
  21. package/common/PipelinePattern.ts +70 -0
  22. package/common/PipelinePlanSchema.ts +490 -0
  23. package/common/backoff.ts +14 -0
  24. package/common/index.ts +39 -0
  25. package/common/pipelinePlan.ts +259 -0
  26. package/common/routeConvention.ts +1 -0
  27. package/common/types.ts +17 -0
  28. package/constant/constantRegistry.ts +26 -6
  29. package/dictionary/base.dictionary.ts +12 -0
  30. package/dictionary/trans.ts +9 -1
  31. package/document/into.ts +5 -0
  32. package/fetch/client/fetchClient.ts +135 -31
  33. package/fetch/client/httpClient.ts +45 -0
  34. package/fetch/client/wsClient.ts +125 -16
  35. package/fetch/fetchType/buildFetch.type.ts +1 -0
  36. package/index.ts +243 -10
  37. package/package.json +1 -5
  38. package/server/akanApp.ts +5 -2
  39. package/server/akanServer.ts +44 -8
  40. package/server/artifact/builderRpc.ts +8 -3
  41. package/server/artifact/ipcTypes.ts +5 -2
  42. package/server/di/diLifecycle.ts +39 -3
  43. package/server/di/utils.ts +28 -1
  44. package/server/ed25519Key.ts +48 -0
  45. package/server/index.ts +1 -0
  46. package/server/machine/index.ts +2 -0
  47. package/server/machine/machineAccount.ts +27 -0
  48. package/server/machine/machineToken.ts +151 -0
  49. package/server/mcp/McpApps.ts +256 -0
  50. package/server/mcp/McpDispatcher.ts +31 -7
  51. package/server/mcp/McpRouter.ts +209 -46
  52. package/server/ops/opsToken.ts +3 -10
  53. package/server/resolver/database.resolver.ts +3 -0
  54. package/server/resolver/signal.resolver.ts +40 -3
  55. package/server/routeElementComposer.tsx +78 -2
  56. package/server/routeState.ts +6 -0
  57. package/server/routeTreeBuilder.ts +26 -9
  58. package/server/routing/apiRouter.ts +6 -3
  59. package/server/routing/roomReplay.ts +88 -0
  60. package/server/rscClient.tsx +53 -8
  61. package/server/rscWorker.tsx +141 -27
  62. package/server/rscWorkerHost.ts +18 -5
  63. package/server/sitemap.ts +14 -0
  64. package/server/ssrFromRscRenderer.tsx +12 -6
  65. package/server/ssrTypes.ts +3 -1
  66. package/server/systemPageDocument.tsx +38 -30
  67. package/server/types.tsx +2 -0
  68. package/server/webRouter.ts +74 -5
  69. package/service/adapt.ts +1 -1
  70. package/service/injectInfo.ts +9 -1
  71. package/service/ipcTypes.ts +2 -1
  72. package/service/predefinedAdaptor/database.adaptor.ts +1 -146
  73. package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
  74. package/service/predefinedAdaptor/queueBackoff.ts +12 -0
  75. package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
  76. package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
  77. package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
  78. package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
  79. package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
  80. package/service/predefinedAdaptor/sql/types.ts +8 -6
  81. package/service/predefinedAdaptor/sql/values.ts +0 -23
  82. package/service/serve.ts +5 -1
  83. package/signal/agent.signal.ts +1 -1
  84. package/signal/base.signal.ts +2 -2
  85. package/signal/endpointIdempotency.ts +53 -0
  86. package/signal/internalArg.ts +14 -0
  87. package/signal/mcp/McpDocument.ts +38 -1
  88. package/signal/mcp/mcpProtocol.ts +18 -0
  89. package/signal/signalContext.ts +58 -30
  90. package/signal/signalRegistry.ts +14 -4
  91. package/signal/types.ts +18 -3
  92. package/store/action.ts +17 -19
  93. package/store/agent/StoreSurfaceSource.ts +1 -1
  94. package/store/agentic/StToolBuilder.ts +3 -0
  95. package/test/conformance.ts +9 -33
  96. package/types/base/baseEnv.d.ts +0 -4
  97. package/types/base/primitiveRegistry.d.ts +1 -1
  98. package/types/client/csrLocation.d.ts +32 -0
  99. package/types/client/csrTypes.d.ts +43 -4
  100. package/types/client/frameConfig.d.ts +14 -2
  101. package/types/client/index.d.ts +3 -0
  102. package/types/client/native/external-view.d.ts +1 -0
  103. package/types/client/route/RootLayoutDefinition.d.ts +2 -1
  104. package/types/client/route/RouteHead.d.ts +13 -0
  105. package/types/client/route/index.d.ts +1 -0
  106. package/types/client/router.d.ts +6 -0
  107. package/types/client/rscNavigation.d.ts +4 -0
  108. package/types/client/viewportMeta.d.ts +18 -0
  109. package/types/client/webManifest.d.ts +4 -0
  110. package/types/common/PipelineNames.d.ts +6 -0
  111. package/types/common/PipelinePattern.d.ts +14 -0
  112. package/types/common/PipelinePlanSchema.d.ts +9 -0
  113. package/types/common/backoff.d.ts +10 -0
  114. package/types/common/index.d.ts +5 -0
  115. package/types/common/pipelinePlan.d.ts +224 -0
  116. package/types/common/types.d.ts +16 -0
  117. package/types/constant/constantRegistry.d.ts +5 -2
  118. package/types/dictionary/base.dictionary.d.ts +1 -1
  119. package/types/dictionary/dictionary.d.ts +8 -8
  120. package/types/document/into.d.ts +9 -0
  121. package/types/fetch/client/fetchClient.d.ts +31 -4
  122. package/types/fetch/client/httpClient.d.ts +8 -0
  123. package/types/fetch/client/wsClient.d.ts +26 -2
  124. package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
  125. package/types/index.d.ts +230 -9
  126. package/types/server/akanServer.d.ts +16 -1
  127. package/types/server/artifact/builderRpc.d.ts +7 -2
  128. package/types/server/artifact/ipcTypes.d.ts +5 -1
  129. package/types/server/di/utils.d.ts +1 -0
  130. package/types/server/ed25519Key.d.ts +13 -0
  131. package/types/server/index.d.ts +1 -0
  132. package/types/server/machine/index.d.ts +2 -0
  133. package/types/server/machine/machineAccount.d.ts +5 -0
  134. package/types/server/machine/machineToken.d.ts +29 -0
  135. package/types/server/machine.d.ts +1 -0
  136. package/types/server/mcp/McpApps.d.ts +50 -0
  137. package/types/server/mcp/McpDispatcher.d.ts +4 -1
  138. package/types/server/mcp/McpRouter.d.ts +4 -0
  139. package/types/server/resolver/signal.resolver.d.ts +2 -1
  140. package/types/server/routeElementComposer.d.ts +24 -4
  141. package/types/server/routeState.d.ts +4 -1
  142. package/types/server/routing/roomReplay.d.ts +24 -0
  143. package/types/server/rscWorkerHost.d.ts +5 -2
  144. package/types/server/sitemap.d.ts +2 -0
  145. package/types/server/ssrFromRscRenderer.d.ts +3 -3
  146. package/types/server/ssrTypes.d.ts +4 -1
  147. package/types/server/systemPageDocument.d.ts +2 -0
  148. package/types/server/types.d.ts +4 -1
  149. package/types/server/webRouter.d.ts +3 -1
  150. package/types/service/adapt.d.ts +1 -1
  151. package/types/service/ipcTypes.d.ts +2 -0
  152. package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
  153. package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
  154. package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
  155. package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
  156. package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
  157. package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
  158. package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
  159. package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
  160. package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
  161. package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
  162. package/types/service/serve.d.ts +3 -0
  163. package/types/signal/endpointIdempotency.d.ts +7 -0
  164. package/types/signal/internalArg.d.ts +10 -0
  165. package/types/signal/mcp/McpDocument.d.ts +8 -0
  166. package/types/signal/mcp/mcpProtocol.d.ts +17 -0
  167. package/types/signal/signalContext.d.ts +5 -3
  168. package/types/signal/signalRegistry.d.ts +4 -4
  169. package/types/signal/types.d.ts +21 -1
  170. package/types/store/agentic/StToolBuilder.d.ts +2 -0
  171. package/types/test/conformance.d.ts +7 -10
  172. package/types/ui/Agent/Chat.d.ts +5 -1
  173. package/types/ui/Agent/EmbedApproval.d.ts +1 -0
  174. package/types/ui/Agent/index.d.ts +3 -0
  175. package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
  176. package/types/ui/Agent/useScreenActions.d.ts +18 -0
  177. package/types/ui/BottomSheet.d.ts +1 -0
  178. package/types/ui/Constant/Doc.d.ts +11 -17
  179. package/types/ui/Constant/index.d.ts +7 -2
  180. package/types/ui/Constant/schemaDoc.d.ts +11 -0
  181. package/types/ui/Dialog/Modal.d.ts +6 -1
  182. package/types/ui/Dialog/index.d.ts +1 -1
  183. package/types/ui/Dialog/modalSurface.d.ts +12 -0
  184. package/types/ui/Layout/BottomInset.d.ts +1 -1
  185. package/types/ui/Layout/BottomTab.d.ts +11 -2
  186. package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
  187. package/types/ui/Layout/Navbar.d.ts +4 -1
  188. package/types/ui/Layout/TopInset.d.ts +1 -1
  189. package/types/ui/Layout/index.d.ts +4 -4
  190. package/types/ui/Link/Action.d.ts +5 -1
  191. package/types/ui/Link/Anchor.d.ts +5 -3
  192. package/types/ui/Link/activePath.d.ts +13 -0
  193. package/types/ui/Link/index.d.ts +1 -1
  194. package/types/ui/Modal.d.ts +6 -1
  195. package/types/ui/Model/EditModal.d.ts +6 -1
  196. package/types/ui/Model/New.d.ts +6 -1
  197. package/types/ui/Model/ViewEditModal.d.ts +6 -1
  198. package/types/ui/Model/ViewModal.d.ts +6 -1
  199. package/types/ui/Model/newFormOrigin.d.ts +7 -0
  200. package/types/ui/Portal.d.ts +3 -1
  201. package/types/ui/Reference/origin.d.ts +12 -0
  202. package/types/ui/Signal/Doc.d.ts +23 -18
  203. package/types/ui/Signal/Listener.d.ts +2 -6
  204. package/types/ui/Signal/Object.d.ts +6 -10
  205. package/types/ui/Signal/Response.d.ts +3 -6
  206. package/types/ui/Signal/RestApi.d.ts +11 -15
  207. package/types/ui/Signal/WebSocket.d.ts +11 -21
  208. package/types/ui/Signal/endpointEntries.d.ts +8 -0
  209. package/types/ui/Signal/index.d.ts +35 -12
  210. package/types/ui/Signal/signalText.d.ts +178 -0
  211. package/types/ui/System/CSR.d.ts +2 -2
  212. package/types/ui/System/Client.d.ts +4 -3
  213. package/types/ui/System/Common.d.ts +27 -6
  214. package/types/ui/System/SSR.d.ts +6 -6
  215. package/types/ui/Tooltip.d.ts +4 -2
  216. package/types/ui/index.d.ts +2 -1
  217. package/types/ui/overlayLayer.d.ts +6 -0
  218. package/types/ui/overlayPosition.d.ts +17 -0
  219. package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
  220. package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
  221. package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
  222. package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
  223. package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
  224. package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
  225. package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
  226. package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
  227. package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
  228. package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
  229. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +12 -4
  230. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
  231. package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
  232. package/types/vendor/@akanjs/native/packages/desktop/src/dispatcher.d.ts +6 -0
  233. package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
  234. package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
  235. package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
  236. package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
  237. package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
  238. package/types/vendor/@akanjs/native/plugins/single-instance/src/socket.d.ts +1 -1
  239. package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
  240. package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
  241. package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
  242. package/types/vendor/use-agentic/AgentActivity.d.ts +3 -1
  243. package/types/vendor/use-agentic/AgenticSurface.d.ts +2 -2
  244. package/types/vendor/use-agentic/types.d.ts +2 -0
  245. package/types/webkit/CsrStack.d.ts +1 -1
  246. package/types/webkit/EmbeddedLayout.d.ts +10 -0
  247. package/types/webkit/McpAppAgent.d.ts +14 -0
  248. package/types/webkit/McpAppApprovals.d.ts +8 -0
  249. package/types/webkit/McpAppBridge.d.ts +101 -0
  250. package/types/webkit/McpAppView.d.ts +21 -0
  251. package/types/webkit/RenderLayer.d.ts +3 -1
  252. package/types/webkit/index.d.ts +1 -0
  253. package/types/webkit/useLocation.d.ts +1 -1
  254. package/types/webkit/useVisualViewportFrame.d.ts +13 -0
  255. package/ui/Agent/Chat.tsx +29 -10
  256. package/ui/Agent/Composer.tsx +2 -1
  257. package/ui/Agent/EmbedApproval.tsx +17 -0
  258. package/ui/Agent/RichInput.tsx +1 -1
  259. package/ui/Agent/index.ts +2 -0
  260. package/ui/Agent/useKeyboardFrame.ts +10 -0
  261. package/ui/Agent/useScreenActions.ts +45 -0
  262. package/ui/BottomSheet.tsx +82 -53
  263. package/ui/Constant/Doc.tsx +113 -60
  264. package/ui/Constant/index.ts +2 -2
  265. package/ui/Constant/schemaDoc.ts +86 -26
  266. package/ui/Dialog/LegacyModal.tsx +53 -52
  267. package/ui/Dialog/Modal.tsx +93 -49
  268. package/ui/Dialog/modalSurface.ts +61 -0
  269. package/ui/DragAction.tsx +1 -1
  270. package/ui/DraggableList.tsx +1 -1
  271. package/ui/Dropdown.tsx +4 -6
  272. package/ui/KeyboardAvoiding.tsx +1 -1
  273. package/ui/Layout/BottomInset.tsx +32 -16
  274. package/ui/Layout/BottomTab.tsx +42 -16
  275. package/ui/Layout/FrameSlotFallback.tsx +59 -0
  276. package/ui/Layout/Navbar.tsx +56 -15
  277. package/ui/Layout/Sider.tsx +1 -1
  278. package/ui/Layout/TopInset.tsx +22 -17
  279. package/ui/Link/Action.tsx +15 -5
  280. package/ui/Link/Anchor.tsx +25 -13
  281. package/ui/Link/activePath.ts +25 -0
  282. package/ui/Link/index.tsx +1 -1
  283. package/ui/Load/Pagination.tsx +1 -1
  284. package/ui/Load/Units.tsx +1 -1
  285. package/ui/Loading/ProgressBar.tsx +1 -1
  286. package/ui/Modal.tsx +9 -0
  287. package/ui/Model/EditModal.tsx +16 -1
  288. package/ui/Model/New.tsx +25 -2
  289. package/ui/Model/NewWrapper_Client.tsx +2 -0
  290. package/ui/Model/ViewEditModal.tsx +25 -10
  291. package/ui/Model/ViewModal.tsx +16 -1
  292. package/ui/Model/newFormOrigin.ts +45 -0
  293. package/ui/Popconfirm.tsx +5 -7
  294. package/ui/Portal.tsx +4 -7
  295. package/ui/Reference/origin.ts +31 -0
  296. package/ui/ScreenNavigator.tsx +1 -1
  297. package/ui/Select.tsx +14 -14
  298. package/ui/Signal/Arg.tsx +2 -2
  299. package/ui/Signal/Doc.tsx +125 -63
  300. package/ui/Signal/Endpoint.tsx +11 -10
  301. package/ui/Signal/Listener.tsx +7 -10
  302. package/ui/Signal/Object.tsx +7 -14
  303. package/ui/Signal/Response.tsx +24 -24
  304. package/ui/Signal/RestApi.tsx +63 -52
  305. package/ui/Signal/WebSocket.tsx +65 -72
  306. package/ui/Signal/endpointEntries.ts +25 -0
  307. package/ui/Signal/index.ts +23 -6
  308. package/ui/Signal/signalText.ts +59 -0
  309. package/ui/System/CSR.tsx +41 -9
  310. package/ui/System/Client.tsx +37 -5
  311. package/ui/System/Common.tsx +76 -32
  312. package/ui/System/SSR.tsx +73 -31
  313. package/ui/Toast.tsx +1 -1
  314. package/ui/Tooltip.tsx +84 -30
  315. package/ui/index.ts +11 -1
  316. package/ui/overlayLayer.ts +25 -2
  317. package/ui/overlayPosition.ts +90 -0
  318. package/ui/recipe/inputRecipe.ts +8 -1
  319. package/ui/styles.css +152 -0
  320. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
  321. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
  322. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
  323. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
  324. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
  325. package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
  326. package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
  327. package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
  328. package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
  329. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
  330. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
  331. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
  332. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
  333. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
  334. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
  335. package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
  336. package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
  337. package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
  338. package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
  339. package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
  340. package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
  341. package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
  342. package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
  343. package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
  344. package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
  345. package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
  346. package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
  347. package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
  348. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
  349. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
  350. package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
  351. package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +60 -21
  352. package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
  353. package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
  354. package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
  355. package/vendor/@akanjs/native/packages/desktop/src/dispatcher.ts +13 -5
  356. package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
  357. package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
  358. package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
  359. package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
  360. package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
  361. package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
  362. package/vendor/@akanjs/native/plugins/single-instance/src/desktop.ts +4 -0
  363. package/vendor/@akanjs/native/plugins/single-instance/src/socket.ts +12 -5
  364. package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
  365. package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
  366. package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
  367. package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
  368. package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
  369. package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
  370. package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
  371. package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
  372. package/vendor/use-agentic/AgentActivity.tsx +4 -2
  373. package/vendor/use-agentic/AgenticSurface.ts +3 -3
  374. package/vendor/use-agentic/ToolRunner.ts +8 -1
  375. package/vendor/use-agentic/types.ts +2 -0
  376. package/webkit/CsrRouteTable.ts +9 -1
  377. package/webkit/CsrStack.ts +9 -2
  378. package/webkit/EmbeddedLayout.tsx +77 -0
  379. package/webkit/McpAppAgent.ts +138 -0
  380. package/webkit/McpAppApprovals.ts +56 -0
  381. package/webkit/McpAppBridge.ts +291 -0
  382. package/webkit/McpAppView.ts +258 -0
  383. package/webkit/RenderLayer.tsx +20 -5
  384. package/webkit/bootCsr.tsx +54 -5
  385. package/webkit/index.ts +1 -0
  386. package/webkit/useCsrValues.ts +16 -10
  387. package/webkit/useFrameRuntime.ts +10 -1
  388. package/webkit/useLocation.ts +4 -3
  389. package/webkit/useVisualViewportFrame.tsx +52 -0
  390. package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
  391. package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
  392. package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
  393. package/types/ui/ServerPortal.d.ts +0 -15
  394. package/ui/Agent/useKeyboardInset.ts +0 -19
  395. package/ui/ServerPortal.tsx +0 -49
@@ -279,3 +279,52 @@ export function decideNavigation(
279
279
  if (!topLevel) return FRAME_SCHEMES.includes(scheme) ? "load" : "drop";
280
280
  return externalSchemes(extra).includes(scheme) ? "open" : "drop";
281
281
  }
282
+
283
+ export interface UpdateDecisionInput {
284
+ /** The binary's keyIds, in `updates.publicKeys` order. */
285
+ ring: readonly string[];
286
+ /** keyIds state.json has revoked. */
287
+ revoked: readonly string[];
288
+ /** The envelope's keyId, whose signature over the manifest verified. */
289
+ signer: string;
290
+ revokeKeys: readonly string[];
291
+ resetSequence: boolean;
292
+ sequence: number;
293
+ /** The binary's own bundle (updates.json `embeddedSequence`). */
294
+ embedded: number;
295
+ /** The bundle running now, null for the binary's own. */
296
+ running: { sequence: number; keyId: string } | null;
297
+ }
298
+
299
+ export type UpdateDecision =
300
+ | { kind: "untrusted" | "revoked" | "self-revoke" | "revoke-later" }
301
+ | { kind: "take" | "stale"; baseline: number; revoke: string[] };
302
+
303
+ /**
304
+ * Whether a web bundle manifest is taken (updates protocol v2, vectors/updates.json). A key may revoke only keys listed
305
+ * before it, so a leaked online key — listed first — cannot revoke the offline spares that recover from it, and
306
+ * `resetSequence` counts only on a manifest that newly revokes a key, so replaying it cannot roll anyone back. A bundle a
307
+ * revoked key signed sets no bar, and revocations apply even when the release itself is not newer.
308
+ */
309
+ export function updateDecision(input: UpdateDecisionInput): UpdateDecision {
310
+ const { ring, revoked, signer, revokeKeys } = input;
311
+ const index = ring.indexOf(signer);
312
+ if (index < 0) return { kind: "untrusted" };
313
+ if (revoked.includes(signer)) return { kind: "revoked" };
314
+ if (revokeKeys.includes(signer)) return { kind: "self-revoke" };
315
+ if (revokeKeys.some((id) => ring.indexOf(id) > index)) return { kind: "revoke-later" };
316
+ const revoke = ring.filter((id) => revokeKeys.includes(id) && !revoked.includes(id));
317
+ const running = input.running && ![...revoked, ...revoke].includes(input.running.keyId) ? input.running.sequence : 0;
318
+ const baseline = input.resetSequence && revoke.length ? input.embedded : Math.max(input.embedded, running);
319
+ return { kind: input.sequence > baseline ? "take" : "stale", baseline, revoke };
320
+ }
321
+
322
+ /** One answer of updateDecision as the vectors write it. */
323
+ export const updateDecisionWire = (decision: UpdateDecision) =>
324
+ decision.kind === "take" || decision.kind === "stale"
325
+ ? `${decision.kind}:${decision.baseline}:${decision.revoke.join(",")}`
326
+ : decision.kind;
327
+
328
+ /** The v2 envelope of a channel for one native API: one file per binary generation, so each takes its own fixes. */
329
+ export const updateManifestUrl = (url: string, platform: string, channel: string, nativeApi: string) =>
330
+ `${url}/${platform}/${channel}/${nativeApi}.json`;
@@ -0,0 +1,31 @@
1
+ {
2
+ "about": "Web bundle updates, protocol v2 (architecture.md \"업데이트\"). keyIds: [raw Ed25519 public key in base64, its keyId: the first 8 bytes of its sha256 in hex]. decisions: [the binary's keyIds in updates.publicKeys order, keyIds state.json has revoked, the envelope's keyId, the manifest's revokeKeys, its resetSequence, its sequence, the binary's embeddedSequence, the running bundle as [sequence, keyId] or null for the app's own, the answer, why] for a manifest whose signature verified; the answer is untrusted | revoked | self-revoke | revoke-later, or take|stale:<the sequence it had to beat>:<keyIds it newly revokes, comma-separated>, revocations applying even to a stale manifest. urls: [updates.url, platform, channel, nativeApi, the envelope's URL].",
3
+ "keyIds": [
4
+ ["BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwc=", "4bb06f8e4e3a7715"],
5
+ ["11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=", "21fe31dfa154a261"],
6
+ ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "66687aadf862bd77"]
7
+ ],
8
+ "decisions": [
9
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "a1a1a1a1a1a1a1a1", [], false, 200, 100, null, "take:100:", "signed by the first key, nothing running but the app's own bundle"],
10
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "d4d4d4d4d4d4d4d4", [], false, 200, 100, null, "untrusted", "a key the binary does not carry"],
11
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], ["a1a1a1a1a1a1a1a1"], "a1a1a1a1a1a1a1a1", [], false, 200, 100, null, "revoked", "a key revoked earlier signs nothing"],
12
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "b2b2b2b2b2b2b2b2", ["b2b2b2b2b2b2b2b2"], false, 200, 100, null, "self-revoke", "a key never revokes itself"],
13
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "a1a1a1a1a1a1a1a1", ["b2b2b2b2b2b2b2b2"], false, 200, 100, null, "revoke-later", "a key revokes only keys listed before it"],
14
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "b2b2b2b2b2b2b2b2", ["a1a1a1a1a1a1a1a1"], false, 300, 100, [500, "a1a1a1a1a1a1a1a1"], "take:100:a1a1a1a1a1a1a1a1", "the bundle a revoked key signed no longer sets the bar"],
15
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "b2b2b2b2b2b2b2b2", ["a1a1a1a1a1a1a1a1"], true, 300, 100, [500, "b2b2b2b2b2b2b2b2"], "take:100:a1a1a1a1a1a1a1a1", "resetSequence with a new revocation compares with the app's own bundle"],
16
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], ["a1a1a1a1a1a1a1a1"], "b2b2b2b2b2b2b2b2", ["a1a1a1a1a1a1a1a1"], true, 300, 100, [500, "b2b2b2b2b2b2b2b2"], "stale:500:", "replayed: nothing new revoked, so the reset does not apply"],
17
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "b2b2b2b2b2b2b2b2", ["a1a1a1a1a1a1a1a1", "d4d4d4d4d4d4d4d4"], false, 200, 100, null, "take:100:a1a1a1a1a1a1a1a1", "an id this binary does not carry is ignored"],
18
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "a1a1a1a1a1a1a1a1", [], false, 100, 100, null, "stale:100:", "no newer than the app's own bundle"],
19
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "a1a1a1a1a1a1a1a1", [], false, 150, 100, [150, "a1a1a1a1a1a1a1a1"], "stale:150:", "the bundle already running"],
20
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "c3c3c3c3c3c3c3c3", ["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2"], false, 120, 100, null, "take:100:a1a1a1a1a1a1a1a1,b2b2b2b2b2b2b2b2", "the last key revokes both before it"],
21
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "b2b2b2b2b2b2b2b2", ["a1a1a1a1a1a1a1a1", "a1a1a1a1a1a1a1a1"], false, 200, 100, null, "take:100:a1a1a1a1a1a1a1a1", "listed twice, revoked once"],
22
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "a1a1a1a1a1a1a1a1", [], false, 160, 100, [150, "b2b2b2b2b2b2b2b2"], "take:150:", "another trusted key's bundle sets the bar"],
23
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], ["a1a1a1a1a1a1a1a1"], "c3c3c3c3c3c3c3c3", ["b2b2b2b2b2b2b2b2"], false, 200, 100, [900, "a1a1a1a1a1a1a1a1"], "take:100:b2b2b2b2b2b2b2b2", "a bundle of a key revoked before still runs this session but sets no bar"],
24
+ [["a1a1a1a1a1a1a1a1", "b2b2b2b2b2b2b2b2", "c3c3c3c3c3c3c3c3"], [], "a1a1a1a1a1a1a1a1", [], true, 300, 100, [500, "a1a1a1a1a1a1a1a1"], "stale:500:", "resetSequence without a revocation does nothing"],
25
+ [["a1a1a1a1a1a1a1a1"], [], "a1a1a1a1a1a1a1a1", [], false, 200, 100, null, "take:100:", "a ring of one"]
26
+ ],
27
+ "urls": [
28
+ ["https://asset.akamir.com/ota/inst1/pathA", "ios", "main", "0123456789abcdef", "https://asset.akamir.com/ota/inst1/pathA/ios/main/0123456789abcdef.json"],
29
+ ["http://10.0.2.2:8790", "android", "pilot", "fedcba9876543210", "http://10.0.2.2:8790/android/pilot/fedcba9876543210.json"]
30
+ ]
31
+ }
@@ -33,6 +33,12 @@ import type {
33
33
  /** How long the window waits for one plugin's setup. */
34
34
  export const SETUP_TIMEOUT_MS = 3000;
35
35
 
36
+ /**
37
+ * How long the launch waits for a gate plugin's setup. A gate decides whether this launch runs at all, so passing it
38
+ * by on time starts what the losing launch must not: a second app's server on the same data (single-instance).
39
+ */
40
+ export const GATE_TIMEOUT_MS = 30_000;
41
+
36
42
  /**
37
43
  * How long a page's call to a plugin whose setup is still running (it outlasted SETUP_TIMEOUT_MS)
38
44
  * waits for it before it is answered INTERNAL with `retryable`.
@@ -189,6 +195,7 @@ export const COALESCE_MS = 100;
189
195
 
190
196
  export interface DispatcherOptions {
191
197
  setupTimeoutMs?: number;
198
+ gateTimeoutMs?: number;
192
199
  setupWaitMs?: number;
193
200
  /** The app's resolved capabilities from boot.json (PL-11). Absent: everything is allowed. */
194
201
  acl?: ResolvedAcl | null;
@@ -301,18 +308,19 @@ export function createDispatcher(
301
308
  };
302
309
 
303
310
  const timeout = options.setupTimeoutMs ?? SETUP_TIMEOUT_MS;
311
+ const gateTimeout = options.gateTimeoutMs ?? GATE_TIMEOUT_MS;
304
312
  /** Setups still running (plugin lifecycle rule: calls and $listen wait for them). */
305
313
  const settingUp = new Map<string, Promise<unknown>>();
306
- const setUp = async (plugin: DesktopPlugin) => {
314
+ const setUp = async (plugin: DesktopPlugin, limit = timeout) => {
307
315
  let timer: ReturnType<typeof setTimeout> | undefined;
308
316
  try {
309
317
  const done = Promise.resolve(plugin.setup?.(context(plugin)));
310
318
  settingUp.set(plugin.id, done);
311
319
  const forget = () => void settingUp.delete(plugin.id);
312
320
  done.then(forget, forget);
313
- const late = new Promise<"late">((resolve) => (timer = setTimeout(() => resolve("late"), timeout)));
321
+ const late = new Promise<"late">((resolve) => (timer = setTimeout(() => resolve("late"), limit)));
314
322
  if ((await Promise.race([done, late])) === "late")
315
- console.warn(`[akan-native] ${plugin.id} setup took over ${timeout} ms; creating the window anyway`);
323
+ console.warn(`[akan-native] ${plugin.id} setup took over ${limit} ms; creating the window anyway`);
316
324
  } catch (error) {
317
325
  console.error(`[akan-native] ${plugin.id} setup failed`, error);
318
326
  } finally {
@@ -322,10 +330,10 @@ export function createDispatcher(
322
330
 
323
331
  const setups = (async () => {
324
332
  for (const plugin of plugins.filter((p) => p.launchPhase === "gate")) {
325
- await setUp(plugin);
333
+ await setUp(plugin, gateTimeout);
326
334
  if (launch.exit !== undefined) return;
327
335
  }
328
- await Promise.all(plugins.filter((p) => p.launchPhase !== "gate").map(setUp));
336
+ await Promise.all(plugins.filter((p) => p.launchPhase !== "gate").map((plugin) => setUp(plugin)));
329
337
  })();
330
338
  const launched = setups.then(() => {
331
339
  launching = false;
@@ -49,6 +49,7 @@ interface ShellConfig {
49
49
  /** desktop.window: the main window from its first frame, unless the launch phase says otherwise. */
50
50
  fullscreen?: boolean;
51
51
  skipTaskbar?: boolean;
52
+ frameless?: boolean;
52
53
  }
53
54
 
54
55
  const finite = (v: unknown): v is number => typeof v === "number" && Number.isFinite(v);
@@ -62,6 +63,7 @@ export function launchBounds(window: LaunchWindow): LaunchWindow {
62
63
  if (window.maximized === true) out.maximized = true;
63
64
  if (typeof window.fullscreen === "boolean") out.fullscreen = window.fullscreen;
64
65
  if (typeof window.skipTaskbar === "boolean") out.skipTaskbar = window.skipTaskbar;
66
+ if (typeof window.frameless === "boolean") out.frameless = window.frameless;
65
67
  return out;
66
68
  }
67
69
 
@@ -154,6 +156,7 @@ export async function startMain(workerUrl: string): Promise<never> {
154
156
  height: shell.height ?? 720,
155
157
  fullscreen: shell.fullscreen === true,
156
158
  skipTaskbar: shell.skipTaskbar === true,
159
+ frameless: shell.frameless === true,
157
160
  ...bounds,
158
161
  appDir: paths.appDir,
159
162
  initJs,
@@ -170,6 +170,8 @@ export interface LaunchWindow {
170
170
  fullscreen?: boolean;
171
171
  /** No taskbar button (Windows, Linux; macOS has none). */
172
172
  skipTaskbar?: boolean;
173
+ /** No title bar or border. */
174
+ frameless?: boolean;
173
175
  }
174
176
 
175
177
  /** What the launch phase decided; sent to the main thread with "ready". */
@@ -20,8 +20,15 @@ export interface ServerManifest {
20
20
  /** The file in resources/server that starts the server. */
21
21
  entry: string;
22
22
  env: Record<string, string>;
23
+ /** Crashes in a row before the launcher gives up, MAX_FAILURES unless named; "forever" never gives up. */
24
+ restart?: number | "forever";
25
+ /** The health route asked every `intervalMs` once the server is ready; `failures` missed in a row restart it. */
26
+ watchdog?: { intervalMs: number; failures: number };
23
27
  }
24
28
 
29
+ /** Answered by the server's own process, solo or behind its gateway, without a database round trip. */
30
+ export const HEALTH_PATH = "/_akan/app/health";
31
+
25
32
  /** Set by the launcher at every start, so desktop.server.env may not name them (cli lib/desktop-server.ts). */
26
33
  export const LAUNCHER_ENV_KEYS = [
27
34
  "BUN_BE_BUN",
@@ -138,6 +145,8 @@ export interface DesktopServerOptions {
138
145
  stopGrace?: number;
139
146
  restartDelay?(failures: number, everReady: boolean): number;
140
147
  now?(): number;
148
+ /** Whether the server at `url` answers its health route; the watchdog's question. */
149
+ probe?(url: string, timeoutMs: number): Promise<boolean>;
141
150
  }
142
151
 
143
152
  export interface DesktopServer {
@@ -186,14 +195,28 @@ export function createServerStatus() {
186
195
  export function readServerManifest(resources: string): ServerManifest | null {
187
196
  const file = join(resources, "server.json");
188
197
  if (!existsSync(file)) return null;
189
- const raw = JSON.parse(readFileSync(file, "utf8")) as { entry?: unknown; env?: unknown };
198
+ const raw = JSON.parse(readFileSync(file, "utf8")) as {
199
+ entry?: unknown;
200
+ env?: unknown;
201
+ restart?: unknown;
202
+ watchdog?: { intervalMs?: unknown; failures?: unknown } | null;
203
+ };
190
204
  if (typeof raw.entry !== "string" || !raw.entry) throw new Error(`${file}: entry must name a file`);
191
205
  const env = Object.fromEntries(
192
206
  Object.entries((raw.env ?? {}) as Record<string, unknown>).filter(
193
207
  (entry): entry is [string, string] => typeof entry[1] === "string",
194
208
  ),
195
209
  );
196
- return { entry: raw.entry, env };
210
+ const restart =
211
+ raw.restart === "forever" || (Number.isInteger(raw.restart) && (raw.restart as number) > 0)
212
+ ? (raw.restart as number | "forever")
213
+ : undefined;
214
+ const { intervalMs, failures } = raw.watchdog ?? {};
215
+ const watchdog =
216
+ Number.isInteger(intervalMs) && (intervalMs as number) > 0 && Number.isInteger(failures) && (failures as number) > 0
217
+ ? { intervalMs: intervalMs as number, failures: failures as number }
218
+ : undefined;
219
+ return { entry: raw.entry, env, ...(restart ? { restart } : {}), ...(watchdog ? { watchdog } : {}) };
197
220
  }
198
221
 
199
222
  export function serverArgv(execPath: string, resources: string, entry: string): string[] {
@@ -299,6 +322,16 @@ export function jwtSecret(dataDir: string): string {
299
322
  return secret;
300
323
  }
301
324
 
325
+ async function probeHealth(url: string, timeoutMs: number): Promise<boolean> {
326
+ try {
327
+ const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
328
+ await response.body?.cancel().catch(() => {});
329
+ return response.ok;
330
+ } catch {
331
+ return false;
332
+ }
333
+ }
334
+
302
335
  export function restartDelay(failures: number, everReady = true): number {
303
336
  return everReady ? Math.min(1000 * 2 ** (failures - 1), 30_000) : BOOT_RESTART_DELAY;
304
337
  }
@@ -404,6 +437,8 @@ export function createDesktopServer(options: DesktopServerOptions): DesktopServe
404
437
  const pickPort = options.freePort ?? loopbackPort;
405
438
  const argv = serverArgv(options.execPath ?? process.execPath, options.resources, options.manifest.entry);
406
439
  const grace = options.stopGrace ?? STOP_GRACE;
440
+ const maxFailures = options.manifest.restart === "forever" ? Infinity : (options.manifest.restart ?? MAX_FAILURES);
441
+ const probe = options.probe ?? probeHealth;
407
442
  const logs = join(dataDir, "runtime", "logs");
408
443
  const outputLog = join(logs, "server-output.log");
409
444
  let outputSize = -1;
@@ -474,19 +509,46 @@ export function createDesktopServer(options: DesktopServerOptions): DesktopServe
474
509
 
475
510
  const failed = (why: string) => {
476
511
  failures++;
477
- const line = `[akan-native] the server ${why} (${failures} of ${MAX_FAILURES} in a row)`;
512
+ const count = Number.isFinite(maxFailures) ? `${failures} of ${maxFailures}` : `${failures}`;
513
+ const line = `[akan-native] the server ${why} (${count} in a row)`;
478
514
  console.error(line);
479
515
  record(line);
480
516
  setState(everReady ? "restarting" : "starting");
481
- if (failures >= MAX_FAILURES)
517
+ if (failures >= maxFailures)
482
518
  return giveUp(
483
- `The app's server stopped ${MAX_FAILURES} times in a row and is not restarted again. Its logs are in ${logs}.`,
519
+ `The app's server stopped ${maxFailures} times in a row and is not restarted again. Its logs are in ${logs}.`,
484
520
  );
485
- restartTimer = setTimeout(() => void launch(), (options.restartDelay ?? restartDelay)(failures, everReady));
521
+
522
+ const paced = everReady || failures >= MAX_FAILURES;
523
+ restartTimer = setTimeout(() => void launch(), (options.restartDelay ?? restartDelay)(failures, paced));
524
+ };
525
+
526
+ const watch = (child: ServerProcess) => {
527
+ const { watchdog } = options.manifest;
528
+ if (!watchdog) return () => {};
529
+ let missed = 0;
530
+ let asking = false;
531
+ const timer = setInterval(async () => {
532
+ if (asking || proc !== child || stopping) return;
533
+ asking = true;
534
+ const healthy = await probe(`http://127.0.0.1:${port}${HEALTH_PATH}`, Math.min(watchdog.intervalMs, 5000));
535
+ asking = false;
536
+ if (proc !== child || stopping) return;
537
+ missed = healthy ? 0 : missed + 1;
538
+ if (missed < watchdog.failures) return;
539
+ const line = `[akan-native] the server missed ${missed} health checks in a row; killing it to restart`;
540
+ console.error(line);
541
+ record(line);
542
+ clearInterval(timer);
543
+ child.kill("SIGKILL");
544
+ }, watchdog.intervalMs);
545
+ timer.unref?.();
546
+ return () => clearInterval(timer);
486
547
  };
487
548
 
488
549
  const run = () => {
489
550
  let readyAt: number | null = null;
551
+ let unwatch = () => {};
490
552
  const child = spawn(argv, {
491
553
  cwd: dataDir,
492
554
  env,
@@ -496,6 +558,7 @@ export function createDesktopServer(options: DesktopServerOptions): DesktopServe
496
558
  if (type !== "ready" || readyAt !== null) return;
497
559
  readyAt = now();
498
560
  setState("up");
561
+ unwatch = watch(child);
499
562
  if (!everReady) {
500
563
  everReady = true;
501
564
  settleReady(true);
@@ -513,6 +576,7 @@ export function createDesktopServer(options: DesktopServerOptions): DesktopServe
513
576
  proc = child;
514
577
  child.exited.then(
515
578
  (code) => {
579
+ unwatch();
516
580
  if (proc === child) proc = null;
517
581
  child.killGroup?.(stopping ? "SIGKILL" : "SIGTERM");
518
582
  if (stopping) return;
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "external-view",
3
+ "apiVersion": 1,
4
+ "methods": [
5
+ "open",
6
+ "navigate",
7
+ "reload",
8
+ "close",
9
+ "list",
10
+ "setVisible",
11
+ "setBounds",
12
+ "evaluate",
13
+ "sendKey",
14
+ "setMuted",
15
+ "clearData",
16
+ "capture"
17
+ ],
18
+ "events": ["load", "loadFailed", "gone", "consoleMessage"],
19
+ "web": null,
20
+ "desktop": "./src/desktop.ts",
21
+ "ios": null,
22
+ "android": null
23
+ }
@@ -0,0 +1,321 @@
1
+
2
+
3
+ import { mkdirSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { AkanNativeError } from "../../../packages/core/src/index.ts";
7
+ import { type DesktopContext, defineDesktopPlugin } from "../../../packages/desktop/src/plugin.ts";
8
+ import type {
9
+ ExternalViewApi,
10
+ ExternalViewBounds,
11
+ ExternalViewConsoleLevel,
12
+ ExternalViewEvents,
13
+ ExternalViewState,
14
+ } from "./index.ts";
15
+
16
+ const ID = /^[A-Za-z0-9_-]{1,64}$/;
17
+ const LEVELS: ExternalViewConsoleLevel[] = ["debug", "info", "warn", "error"];
18
+ /** consoleMessage per view: this many in CONSOLE_WINDOW_MS, the rest counted and reported with the next one. */
19
+ export const CONSOLE_BURST = 20;
20
+ export const CONSOLE_WINDOW_MS = 10_000;
21
+
22
+ function id(args: unknown): string {
23
+ const value = (args as { id?: unknown } | undefined)?.id;
24
+ if (typeof value !== "string" || !ID.test(value))
25
+ throw new AkanNativeError("INVALID_ARGS", "id must be 1-64 letters, digits, - or _");
26
+ return value;
27
+ }
28
+
29
+ function text(args: unknown, name: string): string {
30
+ const value = (args as Record<string, unknown> | undefined)?.[name];
31
+ if (typeof value !== "string" || !value) throw new AkanNativeError("INVALID_ARGS", `${name} must be a string`);
32
+ return value;
33
+ }
34
+
35
+ function bool(args: unknown, name: string): boolean {
36
+ const value = (args as Record<string, unknown> | undefined)?.[name];
37
+ if (typeof value !== "boolean") throw new AkanNativeError("INVALID_ARGS", `${name} must be a boolean`);
38
+ return value;
39
+ }
40
+
41
+ function bounds(value: unknown): ExternalViewBounds | undefined {
42
+ if (value === undefined || value === "fill") return value;
43
+ const b = value as Record<string, unknown> | null;
44
+ const ok = !!b && ["x", "y", "width", "height"].every((k) => typeof b[k] === "number" && Number.isFinite(b[k]));
45
+ if (!ok || (b?.width as number) <= 0 || (b?.height as number) <= 0)
46
+ throw new AkanNativeError("INVALID_ARGS", 'bounds must be "fill" or { x, y, width, height } with a positive size');
47
+ return value as ExternalViewBounds;
48
+ }
49
+
50
+ export function consoleLevel(value: unknown): ExternalViewConsoleLevel | null {
51
+ if (value === undefined || value === false) return null;
52
+ if (value === true) return "warn";
53
+ if (typeof value === "string" && (LEVELS as string[]).includes(value)) return value as ExternalViewConsoleLevel;
54
+ throw new AkanNativeError("INVALID_ARGS", `console must be a boolean or one of ${LEVELS.join(", ")}`);
55
+ }
56
+
57
+ const shell = (ctx: DesktopContext, op: string, args: Record<string, unknown>) => ctx.shell(`externalView.${op}`, args);
58
+
59
+ /** The engine's own text, which the shell sends on as a string: JSON, or not, as the engine had it. */
60
+ function parsed(value: unknown): unknown {
61
+ if (typeof value !== "string") return value;
62
+ try {
63
+ return JSON.parse(value) as unknown;
64
+ } catch {
65
+ return value;
66
+ }
67
+ }
68
+
69
+ function state(raw: unknown): ExternalViewState {
70
+ const { view, ...rest } = raw as { view: string } & Omit<ExternalViewState, "id">;
71
+ return { id: view, ...rest };
72
+ }
73
+
74
+ /** Chrome's virtual key code for a KeyboardEvent.code, which a trusted key event needs beside `key`. */
75
+ export function virtualKey(code: string): number {
76
+ const letter = /^Key([A-Z])$/.exec(code);
77
+ if (letter?.[1]) return letter[1].charCodeAt(0);
78
+ const digit = /^(?:Digit|Numpad)([0-9])$/.exec(code);
79
+ if (digit?.[1]) return (code.startsWith("Numpad") ? 96 : 48) + Number(digit[1]);
80
+ const fn = /^F([0-9]{1,2})$/.exec(code);
81
+ if (fn?.[1] && Number(fn[1]) >= 1 && Number(fn[1]) <= 24) return 111 + Number(fn[1]);
82
+ const named: Record<string, number> = {
83
+ Space: 32,
84
+ Enter: 13,
85
+ NumpadEnter: 13,
86
+ Escape: 27,
87
+ Tab: 9,
88
+ Backspace: 8,
89
+ Delete: 46,
90
+ ArrowLeft: 37,
91
+ ArrowUp: 38,
92
+ ArrowRight: 39,
93
+ ArrowDown: 40,
94
+ Home: 36,
95
+ End: 35,
96
+ PageUp: 33,
97
+ PageDown: 34,
98
+ Slash: 191,
99
+ Minus: 189,
100
+ Equal: 187,
101
+ Period: 190,
102
+ Comma: 188,
103
+ Semicolon: 186,
104
+ Quote: 222,
105
+ BracketLeft: 219,
106
+ BracketRight: 221,
107
+ Backslash: 220,
108
+ Backquote: 192,
109
+ };
110
+ const known = named[code];
111
+ if (known === undefined) throw new AkanNativeError("INVALID_ARGS", `code ${code} is not a key sendKey knows`);
112
+ return known;
113
+ }
114
+
115
+ interface CdpConsoleCall {
116
+ type?: string;
117
+ args?: { value?: unknown; description?: string; unserializableValue?: string; type?: string }[];
118
+ stackTrace?: { callFrames?: { url?: string; lineNumber?: number }[] };
119
+ }
120
+ interface CdpException {
121
+ exceptionDetails?: {
122
+ text?: string;
123
+ url?: string;
124
+ lineNumber?: number;
125
+ exception?: { description?: string };
126
+ stackTrace?: CdpConsoleCall["stackTrace"];
127
+ };
128
+ }
129
+
130
+ /** One console call or uncaught exception as a message, from the protocol event the shell passed on. */
131
+ export function consoleMessageOf(
132
+ method: string,
133
+ params: unknown,
134
+ ): Omit<ExternalViewEvents["consoleMessage"], "id"> | null {
135
+ if (method === "Runtime.exceptionThrown") {
136
+ const details = (params as CdpException).exceptionDetails ?? {};
137
+ const frame = details.stackTrace?.callFrames?.[0];
138
+ return {
139
+ level: "error",
140
+ text: details.exception?.description ?? details.text ?? "Uncaught exception",
141
+ ...(details.url || frame?.url ? { url: details.url || frame?.url } : {}),
142
+ ...(typeof details.lineNumber === "number" ? { line: details.lineNumber + 1 } : {}),
143
+ };
144
+ }
145
+ if (method !== "Runtime.consoleAPICalled") return null;
146
+ const call = params as CdpConsoleCall;
147
+ const level: ExternalViewConsoleLevel =
148
+ call.type === "error" || call.type === "assert"
149
+ ? "error"
150
+ : call.type === "warning"
151
+ ? "warn"
152
+ : call.type === "debug"
153
+ ? "debug"
154
+ : "info";
155
+ const parts = (call.args ?? []).map((arg) =>
156
+ typeof arg.value === "string"
157
+ ? arg.value
158
+ : String(arg.value ?? arg.unserializableValue ?? arg.description ?? arg.type),
159
+ );
160
+ const frame = call.stackTrace?.callFrames?.[0];
161
+ return {
162
+ level,
163
+ text: parts.join(" "),
164
+ ...(frame?.url ? { url: frame.url } : {}),
165
+ ...(typeof frame?.lineNumber === "number" ? { line: frame.lineNumber + 1 } : {}),
166
+ };
167
+ }
168
+
169
+ /** Lets CONSOLE_BURST messages through per window of time, and counts what it held back for the next one. */
170
+ export function createConsoleCap(now: () => number = Date.now) {
171
+ const views = new Map<string, { since: number; sent: number; dropped: number }>();
172
+ return (view: string): { pass: boolean; dropped: number } => {
173
+ const t = now();
174
+ const entry = views.get(view) ?? { since: t, sent: 0, dropped: 0 };
175
+ if (t - entry.since >= CONSOLE_WINDOW_MS) Object.assign(entry, { since: t, sent: 0 });
176
+ views.set(view, entry);
177
+ if (entry.sent >= CONSOLE_BURST) {
178
+ entry.dropped++;
179
+ return { pass: false, dropped: 0 };
180
+ }
181
+ entry.sent++;
182
+ const dropped = entry.dropped;
183
+ entry.dropped = 0;
184
+ return { pass: true, dropped };
185
+ };
186
+ }
187
+
188
+ export function createDesktopExternalView() {
189
+ /** The console level each view was opened with: the shell sends every message, and this filters them. */
190
+ const levels = new Map<string, ExternalViewConsoleLevel>();
191
+
192
+ return defineDesktopPlugin<ExternalViewApi, ExternalViewEvents>({
193
+ id: "external-view",
194
+ methods: {
195
+ async open(args, ctx) {
196
+ const view = id(args);
197
+ const url = text(args, "url");
198
+ const allowOrigins = (args as { allowOrigins?: unknown }).allowOrigins;
199
+ if (!Array.isArray(allowOrigins) || !allowOrigins.length || allowOrigins.some((o) => typeof o !== "string"))
200
+ throw new AkanNativeError("INVALID_ARGS", "allowOrigins must be a list of origins");
201
+ const level = consoleLevel(args.console);
202
+ const opened = state(
203
+ await shell(ctx, "open", {
204
+ view,
205
+ url,
206
+ allowOrigins,
207
+ ...(args.bounds !== undefined ? { bounds: bounds(args.bounds) } : {}),
208
+ ...(typeof args.initScript === "string" ? { initScript: args.initScript } : {}),
209
+ muted: args.muted === true,
210
+ autoplay: args.autoplay !== false,
211
+ console: level !== null,
212
+ ...(args.window !== undefined ? { window: args.window } : {}),
213
+ }),
214
+ );
215
+ if (level) levels.set(view, level);
216
+ else levels.delete(view);
217
+ return opened;
218
+ },
219
+ async navigate(args, ctx) {
220
+ await shell(ctx, "navigate", { view: id(args), url: text(args, "url") });
221
+ },
222
+ async reload(args, ctx) {
223
+ await shell(ctx, "reload", { view: id(args) });
224
+ },
225
+ async close(args, ctx) {
226
+ const view = id(args);
227
+ await shell(ctx, "close", { view });
228
+ levels.delete(view);
229
+ },
230
+ async list(_args, ctx) {
231
+ return ((await shell(ctx, "list", {})) as unknown[]).map(state);
232
+ },
233
+ async setVisible(args, ctx) {
234
+ await shell(ctx, "setVisible", { view: id(args), visible: bool(args, "visible") });
235
+ },
236
+ async setBounds(args, ctx) {
237
+ await shell(ctx, "setBounds", { view: id(args), bounds: bounds(args.bounds) ?? "fill" });
238
+ },
239
+ async evaluate(args, ctx) {
240
+ return { value: parsed(await shell(ctx, "evaluate", { view: id(args), script: text(args, "script") })) };
241
+ },
242
+ async sendKey(args, ctx) {
243
+ const view = id(args);
244
+ const key = text(args, "key");
245
+ const code = text(args, "code");
246
+ const vk = virtualKey(code);
247
+ const base = { key, code, windowsVirtualKeyCode: vk, nativeVirtualKeyCode: vk };
248
+
249
+ const typed = key.length === 1 ? { text: key, unmodifiedText: key } : {};
250
+ await shell(ctx, "cdp", {
251
+ view,
252
+ method: "Input.dispatchKeyEvent",
253
+ params: { type: "keyDown", ...base, ...typed },
254
+ });
255
+ await shell(ctx, "cdp", { view, method: "Input.dispatchKeyEvent", params: { type: "keyUp", ...base } });
256
+ },
257
+ async setMuted(args, ctx) {
258
+ await shell(ctx, "setMuted", { view: id(args), muted: bool(args, "muted") });
259
+ },
260
+ async clearData(args, ctx) {
261
+ await shell(ctx, "clearData", { view: id(args) });
262
+ },
263
+ async capture(args, ctx) {
264
+ const view = id(args);
265
+ const shot = parsed(
266
+ await shell(ctx, "cdp", { view, method: "Page.captureScreenshot", params: { format: "png" } }),
267
+ ) as { data?: string } | null;
268
+ if (!shot?.data) throw new AkanNativeError("INTERNAL", "the view answered no screenshot");
269
+ const dir = join(tmpdir(), "akan-native-external-view");
270
+ mkdirSync(dir, { recursive: true });
271
+ const file = join(dir, `${view}-${Date.now()}.png`);
272
+ const bytes = Buffer.from(shot.data, "base64");
273
+ await Bun.write(file, bytes);
274
+
275
+ const size = bytes.length >= 24 ? { width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20) } : {};
276
+ return { ...ctx.registerFile(file, "image/png"), ...size };
277
+ },
278
+ },
279
+ events: {
280
+ load: (emit, ctx) =>
281
+ ctx.onNativeEvent("externalView", (e) => {
282
+ if (e.event !== "load") return;
283
+ emit(
284
+ { id: e.view as string, state: e.state as "started" | "finished", url: e.url as string },
285
+ { window: e.window as number },
286
+ );
287
+ }),
288
+ loadFailed: (emit, ctx) =>
289
+ ctx.onNativeEvent("externalView", (e) => {
290
+ if (e.event !== "loadFailed") return;
291
+ emit(
292
+ { id: e.view as string, url: e.url as string, errorCode: e.errorCode as string },
293
+ { window: e.window as number },
294
+ );
295
+ }),
296
+ gone: (emit, ctx) =>
297
+ ctx.onNativeEvent("externalView", (e) => {
298
+ if (e.event !== "gone") return;
299
+ emit(
300
+ { id: e.view as string, reason: e.reason as string, restarts: e.restarts as number },
301
+ { window: e.window as number },
302
+ );
303
+ }),
304
+ consoleMessage: (emit, ctx) => {
305
+ const cap = createConsoleCap();
306
+ return ctx.onNativeEvent("externalView", (e) => {
307
+ if (e.event !== "cdp") return;
308
+ const view = e.view as string;
309
+ const min = levels.get(view);
310
+ const message = min ? consoleMessageOf(e.method as string, e.params) : null;
311
+ if (!min || !message || LEVELS.indexOf(message.level) < LEVELS.indexOf(min)) return;
312
+ const { pass, dropped } = cap(view);
313
+ if (!pass) return;
314
+ emit({ id: view, ...message, ...(dropped ? { dropped } : {}) }, { window: e.window as number });
315
+ });
316
+ },
317
+ },
318
+ });
319
+ }
320
+
321
+ export default createDesktopExternalView();