akanjs 3.0.2 → 3.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) 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 +32 -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 +8 -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/injectInfo.ts +9 -1
  70. package/service/ipcTypes.ts +2 -1
  71. package/service/predefinedAdaptor/database.adaptor.ts +1 -146
  72. package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
  73. package/service/predefinedAdaptor/queueBackoff.ts +12 -0
  74. package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
  75. package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
  76. package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
  77. package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
  78. package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
  79. package/service/predefinedAdaptor/sql/types.ts +8 -6
  80. package/service/predefinedAdaptor/sql/values.ts +0 -23
  81. package/service/serve.ts +5 -1
  82. package/signal/agent.signal.ts +1 -1
  83. package/signal/base.signal.ts +2 -2
  84. package/signal/endpointIdempotency.ts +53 -0
  85. package/signal/internalArg.ts +14 -0
  86. package/signal/mcp/McpDocument.ts +38 -1
  87. package/signal/mcp/mcpProtocol.ts +18 -0
  88. package/signal/signalContext.ts +58 -30
  89. package/signal/signalRegistry.ts +14 -4
  90. package/signal/types.ts +18 -3
  91. package/store/agent/StoreSurfaceSource.ts +1 -1
  92. package/store/agentic/StToolBuilder.ts +3 -0
  93. package/test/conformance.ts +9 -33
  94. package/types/base/baseEnv.d.ts +0 -4
  95. package/types/base/primitiveRegistry.d.ts +1 -1
  96. package/types/client/csrLocation.d.ts +32 -0
  97. package/types/client/csrTypes.d.ts +40 -4
  98. package/types/client/frameConfig.d.ts +14 -2
  99. package/types/client/index.d.ts +3 -0
  100. package/types/client/native/external-view.d.ts +1 -0
  101. package/types/client/route/RootLayoutDefinition.d.ts +2 -1
  102. package/types/client/route/RouteHead.d.ts +13 -0
  103. package/types/client/route/index.d.ts +1 -0
  104. package/types/client/router.d.ts +6 -0
  105. package/types/client/rscNavigation.d.ts +4 -0
  106. package/types/client/viewportMeta.d.ts +18 -0
  107. package/types/client/webManifest.d.ts +4 -0
  108. package/types/common/PipelineNames.d.ts +6 -0
  109. package/types/common/PipelinePattern.d.ts +14 -0
  110. package/types/common/PipelinePlanSchema.d.ts +9 -0
  111. package/types/common/backoff.d.ts +10 -0
  112. package/types/common/index.d.ts +5 -0
  113. package/types/common/pipelinePlan.d.ts +224 -0
  114. package/types/common/types.d.ts +16 -0
  115. package/types/constant/constantRegistry.d.ts +5 -2
  116. package/types/dictionary/base.dictionary.d.ts +1 -1
  117. package/types/dictionary/dictionary.d.ts +8 -8
  118. package/types/document/into.d.ts +9 -0
  119. package/types/fetch/client/fetchClient.d.ts +31 -4
  120. package/types/fetch/client/httpClient.d.ts +8 -0
  121. package/types/fetch/client/wsClient.d.ts +26 -2
  122. package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
  123. package/types/index.d.ts +230 -9
  124. package/types/server/akanServer.d.ts +16 -1
  125. package/types/server/artifact/builderRpc.d.ts +7 -2
  126. package/types/server/artifact/ipcTypes.d.ts +5 -1
  127. package/types/server/di/utils.d.ts +1 -0
  128. package/types/server/ed25519Key.d.ts +13 -0
  129. package/types/server/index.d.ts +1 -0
  130. package/types/server/machine/index.d.ts +2 -0
  131. package/types/server/machine/machineAccount.d.ts +5 -0
  132. package/types/server/machine/machineToken.d.ts +29 -0
  133. package/types/server/machine.d.ts +1 -0
  134. package/types/server/mcp/McpApps.d.ts +50 -0
  135. package/types/server/mcp/McpDispatcher.d.ts +4 -1
  136. package/types/server/mcp/McpRouter.d.ts +4 -0
  137. package/types/server/resolver/signal.resolver.d.ts +2 -1
  138. package/types/server/routeElementComposer.d.ts +24 -4
  139. package/types/server/routeState.d.ts +4 -1
  140. package/types/server/routing/roomReplay.d.ts +24 -0
  141. package/types/server/rscWorkerHost.d.ts +5 -2
  142. package/types/server/sitemap.d.ts +2 -0
  143. package/types/server/ssrFromRscRenderer.d.ts +3 -3
  144. package/types/server/ssrTypes.d.ts +4 -1
  145. package/types/server/systemPageDocument.d.ts +2 -0
  146. package/types/server/types.d.ts +4 -1
  147. package/types/server/webRouter.d.ts +3 -1
  148. package/types/service/ipcTypes.d.ts +2 -0
  149. package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
  150. package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
  151. package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
  152. package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
  153. package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
  154. package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
  155. package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
  156. package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
  157. package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
  158. package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
  159. package/types/service/serve.d.ts +3 -0
  160. package/types/signal/endpointIdempotency.d.ts +7 -0
  161. package/types/signal/internalArg.d.ts +10 -0
  162. package/types/signal/mcp/McpDocument.d.ts +8 -0
  163. package/types/signal/mcp/mcpProtocol.d.ts +17 -0
  164. package/types/signal/signalContext.d.ts +5 -3
  165. package/types/signal/signalRegistry.d.ts +4 -4
  166. package/types/signal/types.d.ts +21 -1
  167. package/types/store/agentic/StToolBuilder.d.ts +2 -0
  168. package/types/test/conformance.d.ts +7 -10
  169. package/types/ui/Agent/Chat.d.ts +5 -1
  170. package/types/ui/Agent/EmbedApproval.d.ts +1 -0
  171. package/types/ui/Agent/index.d.ts +3 -0
  172. package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
  173. package/types/ui/Agent/useScreenActions.d.ts +18 -0
  174. package/types/ui/BottomSheet.d.ts +1 -0
  175. package/types/ui/Constant/Doc.d.ts +11 -17
  176. package/types/ui/Constant/index.d.ts +7 -2
  177. package/types/ui/Constant/schemaDoc.d.ts +11 -0
  178. package/types/ui/Dialog/Modal.d.ts +6 -1
  179. package/types/ui/Dialog/index.d.ts +1 -1
  180. package/types/ui/Dialog/modalSurface.d.ts +12 -0
  181. package/types/ui/Layout/BottomTab.d.ts +11 -2
  182. package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
  183. package/types/ui/Layout/Navbar.d.ts +4 -1
  184. package/types/ui/Layout/TopInset.d.ts +1 -1
  185. package/types/ui/Layout/index.d.ts +3 -3
  186. package/types/ui/Link/Action.d.ts +5 -1
  187. package/types/ui/Link/Anchor.d.ts +5 -3
  188. package/types/ui/Link/activePath.d.ts +13 -0
  189. package/types/ui/Link/index.d.ts +1 -1
  190. package/types/ui/Modal.d.ts +6 -1
  191. package/types/ui/Model/EditModal.d.ts +6 -1
  192. package/types/ui/Model/New.d.ts +6 -1
  193. package/types/ui/Model/ViewEditModal.d.ts +6 -1
  194. package/types/ui/Model/ViewModal.d.ts +6 -1
  195. package/types/ui/Model/newFormOrigin.d.ts +7 -0
  196. package/types/ui/Portal.d.ts +3 -1
  197. package/types/ui/Reference/origin.d.ts +12 -0
  198. package/types/ui/Signal/Doc.d.ts +23 -18
  199. package/types/ui/Signal/Listener.d.ts +2 -6
  200. package/types/ui/Signal/Object.d.ts +6 -10
  201. package/types/ui/Signal/Response.d.ts +3 -6
  202. package/types/ui/Signal/RestApi.d.ts +11 -15
  203. package/types/ui/Signal/WebSocket.d.ts +11 -21
  204. package/types/ui/Signal/endpointEntries.d.ts +8 -0
  205. package/types/ui/Signal/index.d.ts +35 -12
  206. package/types/ui/Signal/signalText.d.ts +178 -0
  207. package/types/ui/System/CSR.d.ts +2 -2
  208. package/types/ui/System/Client.d.ts +4 -3
  209. package/types/ui/System/Common.d.ts +27 -6
  210. package/types/ui/System/SSR.d.ts +6 -6
  211. package/types/ui/Tooltip.d.ts +4 -2
  212. package/types/ui/index.d.ts +2 -1
  213. package/types/ui/overlayLayer.d.ts +6 -0
  214. package/types/ui/overlayPosition.d.ts +17 -0
  215. package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
  216. package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
  217. package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
  218. package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
  219. package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
  220. package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
  221. package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
  222. package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
  223. package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
  224. package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
  225. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +7 -0
  226. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
  227. package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
  228. package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
  229. package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
  230. package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
  231. package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
  232. package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
  233. package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
  234. package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
  235. package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
  236. package/types/vendor/use-agentic/types.d.ts +2 -0
  237. package/types/webkit/CsrStack.d.ts +1 -1
  238. package/types/webkit/McpAppAgent.d.ts +14 -0
  239. package/types/webkit/McpAppApprovals.d.ts +8 -0
  240. package/types/webkit/McpAppBridge.d.ts +101 -0
  241. package/types/webkit/McpAppView.d.ts +21 -0
  242. package/types/webkit/index.d.ts +1 -0
  243. package/types/webkit/useLocation.d.ts +1 -1
  244. package/types/webkit/useVisualViewportFrame.d.ts +13 -0
  245. package/ui/Agent/Chat.tsx +29 -10
  246. package/ui/Agent/Composer.tsx +2 -1
  247. package/ui/Agent/EmbedApproval.tsx +17 -0
  248. package/ui/Agent/RichInput.tsx +1 -1
  249. package/ui/Agent/index.ts +2 -0
  250. package/ui/Agent/useKeyboardFrame.ts +10 -0
  251. package/ui/Agent/useScreenActions.ts +45 -0
  252. package/ui/BottomSheet.tsx +82 -53
  253. package/ui/Constant/Doc.tsx +113 -60
  254. package/ui/Constant/index.ts +2 -2
  255. package/ui/Constant/schemaDoc.ts +86 -26
  256. package/ui/Dialog/LegacyModal.tsx +53 -52
  257. package/ui/Dialog/Modal.tsx +93 -49
  258. package/ui/Dialog/modalSurface.ts +61 -0
  259. package/ui/DragAction.tsx +1 -1
  260. package/ui/DraggableList.tsx +1 -1
  261. package/ui/Dropdown.tsx +4 -6
  262. package/ui/KeyboardAvoiding.tsx +1 -1
  263. package/ui/Layout/BottomInset.tsx +20 -14
  264. package/ui/Layout/BottomTab.tsx +42 -16
  265. package/ui/Layout/FrameSlotFallback.tsx +59 -0
  266. package/ui/Layout/Navbar.tsx +37 -11
  267. package/ui/Layout/Sider.tsx +1 -1
  268. package/ui/Layout/TopInset.tsx +15 -14
  269. package/ui/Link/Action.tsx +15 -5
  270. package/ui/Link/Anchor.tsx +25 -13
  271. package/ui/Link/activePath.ts +25 -0
  272. package/ui/Link/index.tsx +1 -1
  273. package/ui/Load/Pagination.tsx +1 -1
  274. package/ui/Load/Units.tsx +1 -1
  275. package/ui/Loading/ProgressBar.tsx +1 -1
  276. package/ui/Modal.tsx +9 -0
  277. package/ui/Model/EditModal.tsx +16 -1
  278. package/ui/Model/New.tsx +25 -2
  279. package/ui/Model/NewWrapper_Client.tsx +2 -0
  280. package/ui/Model/ViewEditModal.tsx +9 -0
  281. package/ui/Model/ViewModal.tsx +16 -1
  282. package/ui/Model/newFormOrigin.ts +45 -0
  283. package/ui/Popconfirm.tsx +5 -7
  284. package/ui/Portal.tsx +4 -7
  285. package/ui/Reference/origin.ts +31 -0
  286. package/ui/ScreenNavigator.tsx +1 -1
  287. package/ui/Select.tsx +2 -7
  288. package/ui/Signal/Arg.tsx +2 -2
  289. package/ui/Signal/Doc.tsx +125 -63
  290. package/ui/Signal/Endpoint.tsx +11 -10
  291. package/ui/Signal/Listener.tsx +7 -10
  292. package/ui/Signal/Object.tsx +7 -14
  293. package/ui/Signal/Response.tsx +24 -24
  294. package/ui/Signal/RestApi.tsx +63 -52
  295. package/ui/Signal/WebSocket.tsx +65 -72
  296. package/ui/Signal/endpointEntries.ts +25 -0
  297. package/ui/Signal/index.ts +23 -6
  298. package/ui/Signal/signalText.ts +59 -0
  299. package/ui/System/CSR.tsx +34 -8
  300. package/ui/System/Client.tsx +36 -5
  301. package/ui/System/Common.tsx +76 -32
  302. package/ui/System/SSR.tsx +73 -31
  303. package/ui/Toast.tsx +1 -1
  304. package/ui/Tooltip.tsx +84 -30
  305. package/ui/index.ts +11 -1
  306. package/ui/overlayLayer.ts +25 -2
  307. package/ui/overlayPosition.ts +90 -0
  308. package/ui/recipe/inputRecipe.ts +8 -1
  309. package/ui/styles.css +152 -0
  310. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
  311. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
  312. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
  313. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
  314. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
  315. package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
  316. package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
  317. package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
  318. package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
  319. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
  320. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
  321. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
  322. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
  323. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
  324. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
  325. package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
  326. package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
  327. package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
  328. package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
  329. package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
  330. package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
  331. package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
  332. package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
  333. package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
  334. package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
  335. package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
  336. package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
  337. package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
  338. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
  339. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
  340. package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
  341. package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +25 -11
  342. package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
  343. package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
  344. package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
  345. package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
  346. package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
  347. package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
  348. package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
  349. package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
  350. package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
  351. package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
  352. package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
  353. package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
  354. package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
  355. package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
  356. package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
  357. package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
  358. package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
  359. package/vendor/use-agentic/ToolRunner.ts +8 -1
  360. package/vendor/use-agentic/types.ts +2 -0
  361. package/webkit/CsrRouteTable.ts +1 -0
  362. package/webkit/CsrStack.ts +9 -2
  363. package/webkit/McpAppAgent.ts +138 -0
  364. package/webkit/McpAppApprovals.ts +56 -0
  365. package/webkit/McpAppBridge.ts +291 -0
  366. package/webkit/McpAppView.ts +258 -0
  367. package/webkit/bootCsr.tsx +54 -5
  368. package/webkit/index.ts +1 -0
  369. package/webkit/useCsrValues.ts +16 -10
  370. package/webkit/useFrameRuntime.ts +10 -1
  371. package/webkit/useLocation.ts +4 -3
  372. package/webkit/useVisualViewportFrame.tsx +52 -0
  373. package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
  374. package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
  375. package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
  376. package/types/ui/ServerPortal.d.ts +0 -15
  377. package/ui/Agent/useKeyboardInset.ts +0 -19
  378. package/ui/ServerPortal.tsx +0 -49
@@ -1,13 +1,13 @@
1
- // Web bundle updates (UP-2): which bundle the shell serves, and the trial / confirm / rollback
1
+ // Web bundle updates (UP-2, protocol v2): which bundle the shell serves, and the trial / confirm / rollback
2
2
  // state. @akanjs/native/plugins/updates downloads and verifies bundles and calls stage/apply/confirm; the
3
3
  // shell picks the bundle at launch (`launch`) and rolls back a trial that never confirms.
4
4
  //
5
- // Layout: Library/Application Support/akan-native-updates/{state.json, bundles/<bundle>/{…, manifest.json,
6
- // manifest.json.sig}} (the filesystem plugin's `data` is Application Support/files). A bundle is
7
- // served only while its signed manifest still verifies and its files are all there with their
8
- // sizes (checked at every launch).
9
- // state.json: { current?, pending?, trial?, failed: [bundle], rolledBack? } with entries
10
- // { bundle, sequence, nativeApi, attempts? }.
5
+ // Layout: Library/Application Support/akan-native-updates/{state.json, bundles/<bundle>/{…, manifest.signed.json}}
6
+ // (the filesystem plugin's `data` is Application Support/files). A bundle is served only while its envelope still
7
+ // verifies under a key of updates.publicKeys that is not revoked, and its files are all there with their sizes
8
+ // (checked at every launch).
9
+ // state.json: { current?, pending?, trial?, failed: [bundle], rolledBack?, revokedKeys: [keyId] } with entries
10
+ // { bundle, sequence, nativeApi, keyId, attempts? }. revokedKeys outlive reset(): a revocation is for good.
11
11
  // - A downloaded bundle is `pending`. At the next launch (or apply()) it becomes `trial` with one
12
12
  // attempt. notifyReady() makes the trial `current`. A trial that is launched again without
13
13
  // having confirmed (it crashed or hung) is marked failed and the previous bundle serves.
@@ -27,22 +27,26 @@ struct AkanNativeUpdateEntry: Equatable, Sendable {
27
27
  var bundle: String
28
28
  var sequence: Double
29
29
  var nativeApi: String
30
+ /// The key whose envelope the bundle came in.
31
+ var keyId: String
30
32
  var attempts: Int = 0
31
33
 
32
- init(bundle: String, sequence: Double, nativeApi: String, attempts: Int = 0) {
34
+ init(bundle: String, sequence: Double, nativeApi: String, keyId: String, attempts: Int = 0) {
33
35
  self.bundle = bundle
34
36
  self.sequence = sequence
35
37
  self.nativeApi = nativeApi
38
+ self.keyId = keyId
36
39
  self.attempts = attempts
37
40
  }
38
41
 
39
42
  init?(_ json: Any?) {
40
43
  guard let o = json as? [String: Any], let bundle = o["bundle"] as? String, AkanNativeUpdates.isBundleId(bundle),
41
- let sequence = (o["sequence"] as? NSNumber)?.doubleValue, let nativeApi = o["nativeApi"] as? String else { return nil }
42
- self.init(bundle: bundle, sequence: sequence, nativeApi: nativeApi, attempts: (o["attempts"] as? NSNumber)?.intValue ?? 0)
44
+ let sequence = (o["sequence"] as? NSNumber)?.doubleValue, let nativeApi = o["nativeApi"] as? String,
45
+ let keyId = o["keyId"] as? String, AkanNativeKernel.isUpdateKeyId(keyId) else { return nil }
46
+ self.init(bundle: bundle, sequence: sequence, nativeApi: nativeApi, keyId: keyId, attempts: (o["attempts"] as? NSNumber)?.intValue ?? 0)
43
47
  }
44
48
 
45
- var json: [String: Any] { ["bundle": bundle, "sequence": sequence, "nativeApi": nativeApi, "attempts": attempts] }
49
+ var json: [String: Any] { ["bundle": bundle, "sequence": sequence, "nativeApi": nativeApi, "keyId": keyId, "attempts": attempts] }
46
50
  }
47
51
 
48
52
  struct AkanNativeUpdateState: Sendable {
@@ -51,8 +55,11 @@ struct AkanNativeUpdateState: Sendable {
51
55
  var trial: AkanNativeUpdateEntry?
52
56
  var failed: [String] = []
53
57
  var rolledBack: String?
58
+ var revokedKeys: [String] = []
54
59
 
55
- init() {}
60
+ init(revokedKeys: [String] = []) {
61
+ self.revokedKeys = revokedKeys
62
+ }
56
63
 
57
64
  init(_ o: [String: Any]) {
58
65
  current = AkanNativeUpdateEntry(o["current"])
@@ -60,10 +67,11 @@ struct AkanNativeUpdateState: Sendable {
60
67
  trial = AkanNativeUpdateEntry(o["trial"])
61
68
  failed = (o["failed"] as? [String] ?? []).filter(AkanNativeUpdates.isBundleId)
62
69
  rolledBack = o["rolledBack"] as? String
70
+ revokedKeys = (o["revokedKeys"] as? [String] ?? []).filter(AkanNativeKernel.isUpdateKeyId)
63
71
  }
64
72
 
65
73
  var json: [String: Any] {
66
- var o: [String: Any] = ["failed": Array(failed.suffix(20))]
74
+ var o: [String: Any] = ["failed": Array(failed.suffix(20)), "revokedKeys": revokedKeys]
67
75
  if let current { o["current"] = current.json }
68
76
  if let pending { o["pending"] = pending.json }
69
77
  if let trial { o["trial"] = trial.json }
@@ -75,33 +83,62 @@ struct AkanNativeUpdateState: Sendable {
75
83
  /// updates.json from the build (akan-native.config.ts `updates`), nil when the app has no updates.
76
84
  struct AkanNativeUpdateConfig: Sendable {
77
85
  let app: String
86
+ let version: String
78
87
  let platform: String
79
88
  let nativeApi: String
80
89
  let embeddedSequence: Double
81
90
  let url: String
82
- let publicKey: Data
91
+ /// updates.publicKeys in order: the order decides which key may revoke which (AkanNativeKernel.updateDecision).
92
+ let keys: [(keyId: String, key: Data)]
83
93
  let channel: String
84
94
  let readyTimeout: TimeInterval
85
95
  let dev: Bool
96
+ /// updates.report.url: where AkanNativeUpdateReports posts what became of each release; nil sends nothing.
97
+ let reportUrl: String?
98
+
99
+ var ring: [String] { keys.map(\.keyId) }
100
+
101
+ func key(_ keyId: String) -> Data? { keys.first { $0.keyId == keyId }?.key }
86
102
 
87
103
  static func load() -> AkanNativeUpdateConfig? {
88
104
  let o = AkanNativeResources.json("updates.json")
89
- guard let app = o["app"] as? String, let url = o["url"] as? String, let key = (o["publicKey"] as? String).flatMap({ Data(base64Encoded: $0) }), key.count == 32,
105
+ let encoded = o["publicKeys"] as? [String] ?? (o["publicKey"] as? String).map { [$0] } ?? []
106
+ let raw = encoded.compactMap { Data(base64Encoded: $0) }.filter { $0.count == 32 }
107
+ guard let app = o["app"] as? String, let url = o["url"] as? String, !raw.isEmpty, raw.count == encoded.count,
90
108
  let nativeApi = o["nativeApi"] as? String else { return nil }
91
109
  return AkanNativeUpdateConfig(
92
110
  app: app,
111
+ version: o["version"] as? String ?? "",
93
112
  platform: o["platform"] as? String ?? "ios",
94
113
  nativeApi: nativeApi,
95
114
  embeddedSequence: (o["embeddedSequence"] as? NSNumber)?.doubleValue ?? 0,
96
115
  url: url,
97
- publicKey: key,
116
+ keys: raw.map { (keyId: AkanNativeKernel.updateKeyId($0), key: $0) },
98
117
  channel: o["channel"] as? String ?? "production",
99
118
  readyTimeout: ((o["readyTimeout"] as? NSNumber)?.doubleValue ?? 10_000) / 1000,
100
- dev: o["dev"] as? Bool ?? false
119
+ dev: o["dev"] as? Bool ?? false,
120
+ reportUrl: o["reportUrl"] as? String
101
121
  )
102
122
  }
103
123
  }
104
124
 
125
+ /// A `<channel>/<nativeApi>.json` envelope, checked as far as its signature: what the manifest says is read after.
126
+ struct AkanNativeUpdateEnvelope {
127
+ let manifest: Data
128
+ let keyId: String
129
+
130
+ /// nil: not an envelope, or signed by a key this binary does not trust or has revoked.
131
+ static func open(_ data: Data, _ config: AkanNativeUpdateConfig, revoked: [String]) -> AkanNativeUpdateEnvelope? {
132
+ guard let o = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
133
+ let keyId = o["keyId"] as? String, AkanNativeKernel.isUpdateKeyId(keyId), !revoked.contains(keyId),
134
+ let raw = config.key(keyId), let key = try? Curve25519.Signing.PublicKey(rawRepresentation: raw),
135
+ let manifest = (o["manifest"] as? String).flatMap({ Data(base64Encoded: $0) }), manifest.count <= 1 << 20,
136
+ let signature = (o["signature"] as? String).flatMap({ Data(base64Encoded: $0) }),
137
+ key.isValidSignature(signature, for: manifest) else { return nil }
138
+ return AkanNativeUpdateEnvelope(manifest: manifest, keyId: keyId)
139
+ }
140
+ }
141
+
105
142
  @MainActor
106
143
  enum AkanNativeUpdates {
107
144
  static let config = AkanNativeUpdateConfig.load()
@@ -147,24 +184,40 @@ enum AkanNativeUpdates {
147
184
  }
148
185
  }
149
186
 
150
- /// Whether a bundle may run in this binary.
151
- static func usable(_ entry: AkanNativeUpdateEntry?, _ config: AkanNativeUpdateConfig) -> Bool {
187
+ /// Whether a bundle may run in this binary: one it still takes, whose files are intact.
188
+ static func usable(_ entry: AkanNativeUpdateEntry?, _ config: AkanNativeUpdateConfig, revoked: [String]) -> Bool {
152
189
  guard let entry else { return false }
153
- return entry.nativeApi == config.nativeApi && entry.sequence > config.embeddedSequence
154
- && FileManager.default.fileExists(atPath: bundleDirectory(entry.bundle).appendingPathComponent("index.html").path)
155
- && verified(entry, config)
190
+ return takes(entry, config, revoked: revoked) && intact(entry, config, revoked: revoked)
156
191
  }
157
192
 
158
- /// The bundle's signed manifest (kept next to it by the plugin) verifies, describes this entry,
193
+ /// Built for its native API, newer than its own bundle, and signed by a key it trusts and has not revoked: a bundle
194
+ /// a new binary or a revocation left behind is dropped as expected, not reported as a failure.
195
+ private static func takes(_ entry: AkanNativeUpdateEntry, _ config: AkanNativeUpdateConfig, revoked: [String]) -> Bool {
196
+ entry.nativeApi == config.nativeApi && entry.sequence > config.embeddedSequence
197
+ && config.ring.contains(entry.keyId) && !revoked.contains(entry.keyId)
198
+ }
199
+
200
+ private static func intact(_ entry: AkanNativeUpdateEntry, _ config: AkanNativeUpdateConfig, revoked: [String]) -> Bool {
201
+ FileManager.default.fileExists(atPath: bundleDirectory(entry.bundle).appendingPathComponent("index.html").path)
202
+ && verified(entry, config, revoked: revoked)
203
+ }
204
+
205
+ /// Drops an entry this launch cannot serve, reporting `event` (reason verify) when its files were the reason.
206
+ private static func drop(_ entry: AkanNativeUpdateEntry?, _ config: AkanNativeUpdateConfig, revoked: [String], _ event: String) -> AkanNativeUpdateEntry? {
207
+ guard let entry else { return nil }
208
+ guard takes(entry, config, revoked: revoked) else { return nil }
209
+ if intact(entry, config, revoked: revoked) { return entry }
210
+ AkanNativeUpdateReports.record(event, entry, reason: "verify")
211
+ return nil
212
+ }
213
+
214
+ /// The bundle's envelope (kept next to it by the plugin) verifies, describes this entry,
159
215
  /// and every file it lists is there with its size. The hashes were checked at the download.
160
- private static func verified(_ entry: AkanNativeUpdateEntry, _ config: AkanNativeUpdateConfig) -> Bool {
216
+ private static func verified(_ entry: AkanNativeUpdateEntry, _ config: AkanNativeUpdateConfig, revoked: [String]) -> Bool {
161
217
  let dir = bundleDirectory(entry.bundle)
162
- guard let manifest = try? Data(contentsOf: dir.appendingPathComponent("manifest.json")),
163
- let sigText = try? String(contentsOf: dir.appendingPathComponent("manifest.json.sig"), encoding: .utf8),
164
- let signature = Data(base64Encoded: sigText.trimmingCharacters(in: .whitespacesAndNewlines)),
165
- let key = try? Curve25519.Signing.PublicKey(rawRepresentation: config.publicKey),
166
- key.isValidSignature(signature, for: manifest),
167
- let o = try? JSONSerialization.jsonObject(with: manifest) as? [String: Any],
218
+ guard let data = try? Data(contentsOf: dir.appendingPathComponent("manifest.signed.json")),
219
+ let envelope = AkanNativeUpdateEnvelope.open(data, config, revoked: revoked), envelope.keyId == entry.keyId,
220
+ let o = try? JSONSerialization.jsonObject(with: envelope.manifest) as? [String: Any],
168
221
  o["bundle"] as? String == entry.bundle, (o["sequence"] as? NSNumber)?.doubleValue == entry.sequence,
169
222
  o["nativeApi"] as? String == entry.nativeApi, o["app"] as? String == config.app, o["platform"] as? String == config.platform,
170
223
  let files = o["files"] as? [[String: Any]] else { return false }
@@ -180,15 +233,16 @@ enum AkanNativeUpdates {
180
233
  static func launch() -> URL? {
181
234
  guard let config else { return nil }
182
235
  var state = readState()
183
- if !usable(state.current, config) { state.current = nil }
184
- if !usable(state.pending, config) { state.pending = nil }
185
- if !usable(state.trial, config) { state.trial = nil }
236
+ state.current = drop(state.current, config, revoked: state.revokedKeys, "rolledBack")
237
+ state.pending = drop(state.pending, config, revoked: state.revokedKeys, "failed")
238
+ state.trial = drop(state.trial, config, revoked: state.revokedKeys, "rolledBack")
186
239
  if let trial = state.trial {
187
240
  // Launched again without notifyReady(): the trial crashed or hung.
188
241
  state.failed.append(trial.bundle)
189
242
  state.rolledBack = trial.bundle
190
243
  state.trial = nil
191
244
  NSLog("[akan-native] updates: bundle %@ did not confirm; rolled back", trial.bundle)
245
+ AkanNativeUpdateReports.record("rolledBack", trial, reason: "relaunch")
192
246
  }
193
247
  if var pending = state.pending, !state.failed.contains(pending.bundle) {
194
248
  pending.attempts = 1
@@ -198,8 +252,10 @@ enum AkanNativeUpdates {
198
252
  if !writeState(state), state.trial != nil {
199
253
  state.trial = nil // not on record: the pending bundle waits for a launch that can record it
200
254
  }
255
+ if let trial = state.trial { AkanNativeUpdateReports.record("applied", trial) }
201
256
  active = state.trial ?? state.current
202
257
  onTrial = state.trial != nil
258
+ AkanNativeUpdateReports.start()
203
259
  return active.map { bundleDirectory($0.bundle) }
204
260
  }
205
261
 
@@ -208,20 +264,21 @@ enum AkanNativeUpdates {
208
264
  var state = readState()
209
265
  state.pending = entry
210
266
  state.failed.removeAll { $0 == entry.bundle }
211
- writeState(state)
267
+ if writeState(state) { AkanNativeUpdateReports.record("downloaded", entry) }
212
268
  }
213
269
 
214
270
  /// apply(): makes the pending bundle the trial now; returns its folder for the reload.
215
271
  static func applyPending() -> URL? {
216
272
  guard let config else { return nil }
217
273
  var state = readState()
218
- guard var pending = state.pending, usable(pending, config) else { return nil }
274
+ guard var pending = state.pending, usable(pending, config, revoked: state.revokedKeys) else { return nil }
219
275
  pending.attempts = 1
220
276
  state.trial = pending
221
277
  state.pending = nil
222
278
  guard writeState(state) else { return nil }
223
279
  active = pending
224
280
  onTrial = true
281
+ AkanNativeUpdateReports.record("applied", pending)
225
282
  return bundleDirectory(pending.bundle)
226
283
  }
227
284
 
@@ -230,34 +287,46 @@ enum AkanNativeUpdates {
230
287
  guard onTrial, let trial = active else { return }
231
288
  var state = readState()
232
289
  guard state.trial?.bundle == trial.bundle else { return }
233
- state.current = AkanNativeUpdateEntry(bundle: trial.bundle, sequence: trial.sequence, nativeApi: trial.nativeApi)
290
+ state.current = AkanNativeUpdateEntry(bundle: trial.bundle, sequence: trial.sequence, nativeApi: trial.nativeApi, keyId: trial.keyId)
234
291
  state.trial = nil
235
292
  state.rolledBack = nil
236
293
  writeState(state)
237
294
  onTrial = false
295
+ AkanNativeUpdateReports.record("confirmed", trial)
238
296
  prune(keep: [state.current?.bundle, state.pending?.bundle].compactMap { $0 })
239
297
  }
240
298
 
241
- /// The trial did not confirm in time: back to the previous bundle. Returns its folder (nil: the app's own).
242
- static func rollback() -> URL? {
243
- guard onTrial, let trial = active else { return active.map { bundleDirectory($0.bundle) } }
299
+ /// The trial did not confirm in time: back to the previous bundle. Returns its folder (nil: the app's own) and the
300
+ /// trial it ended, whose report waits for the renderer probe to say whether the page timed out or hung.
301
+ static func rollback() -> (root: URL?, trial: AkanNativeUpdateEntry?) {
302
+ guard onTrial, let trial = active else { return (active.map { bundleDirectory($0.bundle) }, nil) }
244
303
  var state = readState()
245
304
  state.trial = nil
246
305
  state.failed.append(trial.bundle)
247
306
  state.rolledBack = trial.bundle
248
307
  writeState(state)
249
308
  onTrial = false
250
- active = config.flatMap { usable(state.current, $0) ? state.current : nil }
309
+ active = config.flatMap { usable(state.current, $0, revoked: state.revokedKeys) ? state.current : nil }
251
310
  NSLog("[akan-native] updates: bundle %@ did not call notifyReady() in time; rolled back", trial.bundle)
252
- return active.map { bundleDirectory($0.bundle) }
311
+ return (active.map { bundleDirectory($0.bundle) }, trial)
253
312
  }
254
313
 
255
- /// reset(): the app's own bundle from the next launch on.
314
+ /// reset(): the app's own bundle from the next launch on. Revocations stay.
256
315
  static func reset() {
257
- writeState(AkanNativeUpdateState())
316
+ writeState(AkanNativeUpdateState(revokedKeys: readState().revokedKeys))
258
317
  prune(keep: active.map { [$0.bundle] } ?? [])
259
318
  }
260
319
 
320
+ /// Keys a verified manifest revoked: no bundle they signed is served again, from the next launch on.
321
+ static func revoke(_ keyIds: [String]) {
322
+ var state = readState()
323
+ let added = keyIds.filter { !state.revokedKeys.contains($0) }
324
+ guard !added.isEmpty else { return }
325
+ state.revokedKeys += added
326
+ writeState(state)
327
+ NSLog("[akan-native] updates: revoked keys %@", added.joined(separator: ", "))
328
+ }
329
+
261
330
  /// A download in progress (bundles/<bundle>.partial) is the plugin's, which removes it.
262
331
  private static func prune(keep: [String]) {
263
332
  let folder = directory.appendingPathComponent("bundles", isDirectory: true)
@@ -141,6 +141,24 @@ enum AkanNativeVectors {
141
141
  check(valid == c[3] as? Bool, "ed25519 \(c[4])")
142
142
  }
143
143
 
144
+ // Updates protocol v2 (AkanNativeUpdates, AkanNativeKernel.updateDecision).
145
+ let updates = load("updates")
146
+ for c in rows(updates, "keyIds") {
147
+ check(AkanNativeKernel.updateKeyId(Data(base64Encoded: c[0] as! String) ?? Data()) == c[1] as? String, "update keyId \(c[0])")
148
+ }
149
+ for c in rows(updates, "decisions") {
150
+ let running = (c[7] as? [Any]).map { (sequence: ($0[0] as! NSNumber).doubleValue, keyId: $0[1] as! String) }
151
+ let got = AkanNativeKernel.updateDecision(
152
+ ring: c[0] as! [String], revoked: c[1] as! [String], signer: c[2] as! String, revokeKeys: c[3] as! [String],
153
+ resetSequence: c[4] as! Bool, sequence: (c[5] as! NSNumber).doubleValue, embedded: (c[6] as! NSNumber).doubleValue, running: running
154
+ )
155
+ check(got.wire == c[8] as? String, "update decision \(c[9]): \(got.wire)")
156
+ }
157
+ for c in rows(updates, "urls") {
158
+ let got = AkanNativeKernel.updateManifestURL(c[0] as! String, platform: c[1] as! String, channel: c[2] as! String, nativeApi: c[3] as! String)
159
+ check(got == c[4] as? String, "update url \(got)")
160
+ }
161
+
144
162
  // A missing or truncated file must not pass as fewer checks.
145
163
  for (name, count) in AkanNativeVectorData.counts where ran[name, default: 0] != count {
146
164
  failures.append("\(name): \(ran[name, default: 0]) of \(count) checks ran")
@@ -185,6 +185,7 @@ final class AkanNativeViewController: UIViewController, WKNavigationDelegate, WK
185
185
  // MARK: web bundle updates (UP-2)
186
186
 
187
187
  private var readyTimer: Timer?
188
+ private var rendererAnswered = false
188
189
 
189
190
  /// A bundle on trial must call notifyReady() within updates.readyTimeout of loading.
190
191
  private func startReadyTimer() {
@@ -194,12 +195,32 @@ final class AkanNativeViewController: UIViewController, WKNavigationDelegate, WK
194
195
  readyTimer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { [weak self] _ in
195
196
  MainActor.assumeIsolated {
196
197
  guard let self, AkanNativeUpdates.onTrial else { return }
197
- self.serveBundle(AkanNativeUpdates.rollback())
198
+ self.rollBackTrial()
198
199
  }
199
200
  }
200
201
  #endif
201
202
  }
202
203
 
204
+ #if AKAN_NATIVE_UPDATES
205
+ /// A web content process stuck in a script never runs the next load: one that does not answer is left behind with
206
+ /// its window, and a new window serves the bundle the rollback made active.
207
+ private func rollBackTrial() {
208
+ let (root, trial) = AkanNativeUpdates.rollback()
209
+ rendererAnswered = false
210
+ webView.evaluateJavaScript("0") { [weak self] _, _ in self?.rendererAnswered = true }
211
+ DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
212
+ guard let self else { return }
213
+ if let trial { AkanNativeUpdateReports.record("rolledBack", trial, reason: self.rendererAnswered ? "timeout" : "hang") }
214
+ if !self.rendererAnswered, let scene = self.view.window?.windowScene?.delegate as? AkanNativeSceneDelegate {
215
+ AkanNativeLog.info("updates: the page on trial does not answer; making the window again")
216
+ scene.recreate()
217
+ } else {
218
+ self.serveBundle(root)
219
+ }
220
+ }
221
+ }
222
+ #endif
223
+
203
224
  /// Serves another web bundle (nil: the app's own) and loads it from the start.
204
225
  func serveBundle(_ root: URL?) {
205
226
  readyTimer?.invalidate()
@@ -296,6 +317,8 @@ final class AkanNativeViewController: UIViewController, WKNavigationDelegate, WK
296
317
  /// fail or turn into a download and leave the page in place.
297
318
  func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
298
319
  bridge.endDocument()
320
+ // Armed at commit, not at didFinish: a bundle stuck in a script never finishes loading.
321
+ startReadyTimer()
299
322
  }
300
323
 
301
324
  /// The window is going away (the scene disconnected, or $host.recreate): its page's document ends.
@@ -306,7 +329,6 @@ final class AkanNativeViewController: UIViewController, WKNavigationDelegate, WK
306
329
 
307
330
  func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
308
331
  if config.splashAutoHide { hideSplash(fadeOut: 0.2) }
309
- startReadyTimer()
310
332
  }
311
333
 
312
334
  func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
@@ -6,6 +6,7 @@ import { type DoctorCheck, doctorChecks, hostPlatforms, runFixes } from "./comma
6
6
  import type { AkanNativeConfig } from "./config.ts";
7
7
  import { makeNativeBoot } from "./lib/boot.ts";
8
8
  import { BUNDLE_FILE, compatProblems, readBundleInfo } from "./lib/compat.ts";
9
+ import { requestedDistribution } from "./lib/distribution.ts";
9
10
  import { CancelledError, withSignal } from "./lib/exec.ts";
10
11
  import { type HmrContext, lanAddress, resolveDevEntry, startHmrServer } from "./lib/hmr.ts";
11
12
  import type { Device, DeviceSelector, Launched } from "./lib/launch.ts";
@@ -47,6 +48,12 @@ import { androidDevices, launchAndroid } from "./platforms/android.ts";
47
48
  import { PLATFORM_TARGETS, type TargetPlatform } from "./platforms/index.ts";
48
49
  import { iosDevices, physicalIosDevice } from "./platforms/ios.ts";
49
50
 
51
+ export { type UpdateDecision, type UpdateDecisionInput, updateDecision } from "../../core/src/kernel.ts";
52
+ /**
53
+ * A phone's update protocol as the apps run it, for a signer or a server that checks what it publishes or registers:
54
+ * the envelope's verification, the keyId of a public key, and what a verified manifest means to a binary.
55
+ */
56
+ export { keyIdOf, openEnvelope } from "./lib/updates.ts";
50
57
  export type {
51
58
  AkanNativeConfig,
52
59
  AndroidSigning,
@@ -71,10 +78,10 @@ export type {
71
78
  * environment (the API itself reads no variable): pass the result as `macos`.
72
79
  */
73
80
  /** The Authenticode settings the AKAN_NATIVE_WINDOWS_* variables name: pass the result as `windows.signing`. */
74
- export { macosDistributionFromEnv, windowsSigningFromEnv };
81
+ export { macosDistributionFromEnv, requestedDistribution, windowsSigningFromEnv };
75
82
 
76
83
  /** semver of this API. A caller checks the major before it relies on anything here. */
77
- export const API_VERSION = "0.10.0";
84
+ export const API_VERSION = "0.11.0";
78
85
 
79
86
  export type AkanNativeErrorCode =
80
87
  | "CONFIG_INVALID"
@@ -293,6 +300,8 @@ export interface PublishCheckOptions extends Pick<TaskOptions, "appDir" | "confi
293
300
  out?: string;
294
301
  /** Whether the release will carry a server; the config's `desktop.server` by default. */
295
302
  server?: boolean;
303
+ /** A desktop release's CPU, which names its feed folder: this computer's by default. */
304
+ arch?: DesktopArch;
296
305
  }
297
306
 
298
307
  /**
@@ -302,14 +311,18 @@ export interface PublishCheckOptions extends Pick<TaskOptions, "appDir" | "confi
302
311
  */
303
312
  export function checkPublishUpdate(options: PublishCheckOptions): { channel: string; out: string } {
304
313
  if (!options.config.updates)
305
- throw new AkanNativeError("CONFIG_INVALID", "the config has no updates: { url, publicKey } to publish for");
314
+ throw new AkanNativeError("CONFIG_INVALID", "the config has no updates: { url, publicKeys } to publish for");
306
315
  const channel = options.channel ?? options.config.updates.channel ?? "production";
307
316
  const out = resolve(options.out ?? resolve(options.appDir, ".akan", "native", "updates"));
308
317
  try {
309
318
  if (options.channel !== undefined) assertChannel(options.channel);
310
- assertSigningKey(options.config);
319
+ const { publicKey, publicKeys } = options.config.updates;
320
+ assertSigningKey(
321
+ { app: options.config.app, updates: { publicKeys: publicKeys ?? (publicKey ? [publicKey] : []) } },
322
+ options.platform,
323
+ );
311
324
  const server = options.server ?? !!options.config.desktop?.server;
312
- assertServerOfChannel({ desktop: server ? { server } : {} }, options.platform, out, channel);
325
+ assertServerOfChannel({ desktop: server ? { server } : {} }, options.platform, out, channel, options.arch);
313
326
  } catch (error) {
314
327
  throw toAkanNativeError(error, "CONFIG_INVALID");
315
328
  }
@@ -329,6 +342,8 @@ export function publishUpdate(
329
342
  macos?: Pick<MacosBuild, "signing" | "notarize">;
330
343
  /** Authenticode for a Windows release's files. No setup program is made. */
331
344
  windows?: Pick<WindowsBuild, "signing">;
345
+ /** A desktop release's CPU, of the same OS: an x64 release from an arm64 machine. Default this computer's. */
346
+ arch?: DesktopArch;
332
347
  },
333
348
  ): Promise<PublishResult> {
334
349
  return task(options, async (warnings) => {
@@ -336,6 +351,7 @@ export function publishUpdate(
336
351
  const { ctx, artifact, result } = await buildIn(options, "release", options.mode ?? "production", warnings, {
337
352
  ...(options.macos ? { macos: { ...options.macos, dmg: false } } : {}),
338
353
  ...(options.windows?.signing ? { windows: { signing: options.windows.signing } } : {}),
354
+ ...(options.arch ? { arch: options.arch } : {}),
339
355
  });
340
356
  const { dir, manifest } = await publishRelease(ctx, options.platform, artifact, out, channel).catch((error) => {
341
357
  throw toAkanNativeError(error, "CONFIG_INVALID");
@@ -366,7 +382,7 @@ export function compareBundles(shipped: string, bundle: string): { compatible: b
366
382
 
367
383
  /**
368
384
  * The app's update signing key: made once (AKAN_NATIVE_UPDATE_KEY, else ~/.akan/native/keys/<app id>.update.key),
369
- * then read. `publicKey` goes into the config's `updates.publicKey`; the key file never leaves the machine.
385
+ * then read. `publicKey` goes into the config's `updates.publicKeys`; the key file never leaves the machine.
370
386
  */
371
387
  export function updateKeygen(options: { config: AkanNativeConfig }): {
372
388
  publicKey: string;
@@ -2,16 +2,25 @@
2
2
 
3
3
  import { relative } from "node:path";
4
4
  import { checkFlags, type ParsedArgs, parseArgs, stringFlag } from "../lib/args.ts";
5
+ import { requestedDistribution } from "../lib/distribution.ts";
5
6
  import { bold, CliError, dim, log } from "../lib/log.ts";
6
- import { macosDistributionFromEnv } from "../lib/macossigning.ts";
7
7
  import { type BuildContext, type BuildProfile, prepare } from "../lib/prepare.ts";
8
8
  import { findAppDir, loadProject } from "../lib/project.ts";
9
9
  import type { DesktopArch } from "../lib/updates.ts";
10
- import { windowsSigningFromEnv } from "../lib/windowssigning.ts";
11
10
  import { PLATFORM_TARGETS, TARGETS, type TargetPlatform } from "../platforms/index.ts";
12
11
  import { physicalIosDevice } from "../platforms/ios.ts";
13
12
 
14
- export const BOOLEAN_FLAGS = ["skip-web-build", "open", "release", "debug", "headless", "aab", "installer"];
13
+ export const BOOLEAN_FLAGS = [
14
+ "skip-web-build",
15
+ "open",
16
+ "release",
17
+ "debug",
18
+ "headless",
19
+ "aab",
20
+ "installer",
21
+ "sign",
22
+ "notarize",
23
+ ];
15
24
 
16
25
  /** Flags of every build: the app, the env mode (.env.<mode>), the profile, the web build. */
17
26
  export const BUILD_FLAGS = [
@@ -25,11 +34,13 @@ export const BUILD_FLAGS = [
25
34
  "device",
26
35
  "build",
27
36
  "arch",
37
+ "sign",
38
+ "notarize",
28
39
  ];
29
40
  /** Flags of launching a build (web server, simulators, emulators). */
30
41
  export const LAUNCH_FLAGS = ["port", "host", "open", "headless", "device", "avd"];
31
42
 
32
- export const BUILD_USAGE = `akan-native build <${TARGETS.join("|")}> [--app <dir>] [--mode <mode>] [--debug] [--skip-web-build] [--aab] [--installer] [--device] [--build <n>] [--arch <arm64|x64>]`;
43
+ export const BUILD_USAGE = `akan-native build <${TARGETS.join("|")}> [--app <dir>] [--mode <mode>] [--debug] [--skip-web-build] [--aab] [--installer] [--device] [--build <n>] [--arch <arm64|x64>] [--sign] [--notarize]`;
33
44
  export const RUN_USAGE = `akan-native run <${TARGETS.join("|")}> [--app <dir>] [--mode <mode>] [--release] [--skip-web-build] [--port <n>] [--open] [--headless] [--device <name>] [--avd <name>] [--start <path>]`;
34
45
 
35
46
  export interface BuildDefaults {
@@ -77,6 +88,13 @@ export async function buildFromArgs(
77
88
  const device = args.flags.device;
78
89
  const iphone = platform === "ios" && typeof device === "string" ? await physicalIosDevice(device) : null;
79
90
  const iosDevice = platform === "ios" && (device === true || iphone !== null);
91
+ const distribution = requestedDistribution(platform, profile, {
92
+ sign: args.flags.sign === true,
93
+ notarize: args.flags.notarize === true,
94
+ });
95
+ if (distribution.notice) log.warn(distribution.notice);
96
+ else if (distribution.unused.length && (platform === "macos" || platform === "windows"))
97
+ log.info(dim(`${distribution.unused.join(", ")} set: this build is not signed without --sign`));
80
98
  const ctx = await prepare(project, platform, {
81
99
  mode: stringFlag(args, "mode") ?? defaults.mode,
82
100
  profile,
@@ -89,16 +107,14 @@ export async function buildFromArgs(
89
107
  ? {
90
108
  windows: {
91
109
  ...(args.flags.installer === true ? { installer: true } : {}),
92
-
93
- ...(profile === "release" ? { signing: windowsSigningFromEnv() } : {}),
110
+ ...distribution.windows,
94
111
  },
95
112
  }
96
113
  : {}),
97
-
98
114
  ...(platform === "macos"
99
115
  ? {
100
116
  macos: {
101
- ...(profile === "release" ? macosDistributionFromEnv() : {}),
117
+ ...distribution.macos,
102
118
  ...(args.flags.installer === true ? { dmg: true } : {}),
103
119
  },
104
120
  }
@@ -4,13 +4,14 @@ import { existsSync, statSync } from "node:fs";
4
4
  import { join, relative, resolve, sep } from "node:path";
5
5
  import { checkFlags, type ParsedArgs, parseArgs, stringFlag } from "../lib/args.ts";
6
6
  import { bold, CliError, dim, log } from "../lib/log.ts";
7
+ import { desktopArch } from "../lib/prepare.ts";
7
8
  import { findAppDir, loadProject } from "../lib/project.ts";
8
9
  import { assertServerOfChannel, assertSigningKey, publishRelease } from "../lib/publish.ts";
9
- import { assertChannel, generateUpdateKey, updateKeyPath } from "../lib/updates.ts";
10
+ import { assertChannel, type DesktopArch, generateUpdateKey, updateKeyPath } from "../lib/updates.ts";
10
11
  import { BOOLEAN_FLAGS, buildFromArgs } from "./build.ts";
11
12
 
12
13
  export const UPDATE_USAGE =
13
- "akan-native update keygen [--app <dir>] | akan-native update publish <ios|android|macos|windows|linux> [--app <dir>] [--mode <mode>] [--debug] [--channel <name>] [--out <dir>] | akan-native update serve [--app <dir>] [--out <dir>] [--port <n>]";
14
+ "akan-native update keygen [--app <dir>] | akan-native update publish <ios|android|macos|windows|linux> [--app <dir>] [--mode <mode>] [--debug] [--channel <name>] [--out <dir>] [--arch <arm64|x64>] [--sign] [--notarize] | akan-native update serve [--app <dir>] [--out <dir>] [--port <n>]";
14
15
 
15
16
  const outDir = (args: ParsedArgs, appDir: string) =>
16
17
  resolve(stringFlag(args, "out") ?? join(appDir, ".akan", "native", "updates"));
@@ -25,10 +26,10 @@ async function keygen(args: ParsedArgs): Promise<number> {
25
26
  );
26
27
  else log.info(`using the existing key ${path}`);
27
28
  log.info(
28
- `Add to akan-native.config.ts:\n\n updates: { url: "https://…/updates", publicKey: ${JSON.stringify(publicKey)} },\n`,
29
+ `Add to akan-native.config.ts:\n\n updates: { url: "https://…/updates", publicKeys: [${JSON.stringify(publicKey)}] },\n`,
29
30
  );
30
- if (project.config.updates && project.config.updates.publicKey !== publicKey)
31
- log.warn("akan-native.config.ts has a different updates.publicKey");
31
+ if (project.config.updates && !project.config.updates.publicKeys.includes(publicKey))
32
+ log.warn("akan-native.config.ts has updates.publicKeys without this key");
32
33
  return 0;
33
34
  }
34
35
 
@@ -40,12 +41,13 @@ async function publish(args: ParsedArgs): Promise<number> {
40
41
  const requested = stringFlag(args, "channel");
41
42
  if (requested !== undefined) assertChannel(requested);
42
43
  const project = await loadProject(findAppDir(stringFlag(args, "app") ?? process.cwd()));
43
- assertSigningKey(project.config);
44
+ assertSigningKey(project.config, platform);
44
45
  assertServerOfChannel(
45
46
  project.config,
46
47
  platform,
47
48
  outDir(args, project.appDir),
48
49
  requested ?? project.config.updates?.channel ?? "production",
50
+ desktopArch(platform, stringFlag(args, "arch") as DesktopArch | undefined),
49
51
  );
50
52
  const { ctx, artifact } = await buildFromArgs(
51
53
  { positional: [platform], flags: args.flags },
@@ -54,10 +56,10 @@ async function publish(args: ParsedArgs): Promise<number> {
54
56
  );
55
57
  const { config, appDir } = ctx.project;
56
58
  const channel = requested ?? config.updates?.channel ?? "production";
57
- const { dir, manifest } = await publishRelease(ctx, platform, artifact, outDir(args, appDir), channel);
59
+ const { dir, manifest, file } = await publishRelease(ctx, platform, artifact, outDir(args, appDir), channel);
58
60
  const size = manifest.files.reduce((n, f) => n + f.size, 0);
59
61
  log.ok(
60
- `published ${bold(manifest.bundle)} to ${relative(process.cwd(), dir) || dir}/${channel}.json ${dim(`(${manifest.files.length} files, ${(size / 1024).toFixed(0)} KiB${manifest.nativeApi ? `, native API ${manifest.nativeApi}` : ""})`)}`,
62
+ `published ${bold(manifest.bundle)} to ${relative(process.cwd(), file) || file} ${dim(`(${manifest.files.length} files, ${(size / 1024).toFixed(0)} KiB${manifest.nativeApi ? `, native API ${manifest.nativeApi}` : ""})`)}`,
61
63
  );
62
64
  log.info(
63
65
  dim(
@@ -97,7 +99,7 @@ export async function update(argv: string[]): Promise<number> {
97
99
 
98
100
  const known = {
99
101
  keygen: ["app"],
100
- publish: ["app", "mode", "debug", "channel", "out", "skip-web-build"],
102
+ publish: ["app", "mode", "debug", "channel", "out", "skip-web-build", "arch", "sign", "notarize"],
101
103
  serve: ["app", "out", "port"],
102
104
  }[args.positional[0] as string];
103
105
  if (known) checkFlags(args, known, UPDATE_USAGE);