@aigne/aos 0.2.0-beta

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 (533) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE.md +26 -0
  3. package/dist/agent-dsl.cjs +116 -0
  4. package/dist/agent-dsl.d.cts +31 -0
  5. package/dist/agent-dsl.d.cts.map +1 -0
  6. package/dist/agent-dsl.d.mts +31 -0
  7. package/dist/agent-dsl.d.mts.map +1 -0
  8. package/dist/agent-dsl.mjs +115 -0
  9. package/dist/agent-dsl.mjs.map +1 -0
  10. package/dist/arc-compat.cjs +125 -0
  11. package/dist/arc-compat.d.cts +17 -0
  12. package/dist/arc-compat.d.cts.map +1 -0
  13. package/dist/arc-compat.d.mts +17 -0
  14. package/dist/arc-compat.d.mts.map +1 -0
  15. package/dist/arc-compat.mjs +123 -0
  16. package/dist/arc-compat.mjs.map +1 -0
  17. package/dist/auto-surface/compiler.cjs +36 -0
  18. package/dist/auto-surface/compiler.d.cts +7 -0
  19. package/dist/auto-surface/compiler.d.cts.map +1 -0
  20. package/dist/auto-surface/compiler.d.mts +7 -0
  21. package/dist/auto-surface/compiler.d.mts.map +1 -0
  22. package/dist/auto-surface/compiler.mjs +37 -0
  23. package/dist/auto-surface/compiler.mjs.map +1 -0
  24. package/dist/auto-surface/definition-dsl.cjs +333 -0
  25. package/dist/auto-surface/definition-dsl.d.cts +41 -0
  26. package/dist/auto-surface/definition-dsl.d.cts.map +1 -0
  27. package/dist/auto-surface/definition-dsl.d.mts +41 -0
  28. package/dist/auto-surface/definition-dsl.d.mts.map +1 -0
  29. package/dist/auto-surface/definition-dsl.mjs +332 -0
  30. package/dist/auto-surface/definition-dsl.mjs.map +1 -0
  31. package/dist/auto-surface/fragments.cjs +177 -0
  32. package/dist/auto-surface/fragments.d.cts +23 -0
  33. package/dist/auto-surface/fragments.d.cts.map +1 -0
  34. package/dist/auto-surface/fragments.d.mts +23 -0
  35. package/dist/auto-surface/fragments.d.mts.map +1 -0
  36. package/dist/auto-surface/fragments.mjs +175 -0
  37. package/dist/auto-surface/fragments.mjs.map +1 -0
  38. package/dist/auto-surface/index.d.mts +9 -0
  39. package/dist/auto-surface/layering.cjs +43 -0
  40. package/dist/auto-surface/layering.d.cts +7 -0
  41. package/dist/auto-surface/layering.d.cts.map +1 -0
  42. package/dist/auto-surface/layering.d.mts +7 -0
  43. package/dist/auto-surface/layering.d.mts.map +1 -0
  44. package/dist/auto-surface/layering.mjs +43 -0
  45. package/dist/auto-surface/layering.mjs.map +1 -0
  46. package/dist/auto-surface/projection.cjs +11 -0
  47. package/dist/auto-surface/projection.d.cts +11 -0
  48. package/dist/auto-surface/projection.d.cts.map +1 -0
  49. package/dist/auto-surface/projection.d.mts +11 -0
  50. package/dist/auto-surface/projection.d.mts.map +1 -0
  51. package/dist/auto-surface/projection.mjs +12 -0
  52. package/dist/auto-surface/projection.mjs.map +1 -0
  53. package/dist/auto-surface/projectors/settings-shell.cjs +114 -0
  54. package/dist/auto-surface/projectors/settings-shell.d.cts +10 -0
  55. package/dist/auto-surface/projectors/settings-shell.d.cts.map +1 -0
  56. package/dist/auto-surface/projectors/settings-shell.d.mts +10 -0
  57. package/dist/auto-surface/projectors/settings-shell.d.mts.map +1 -0
  58. package/dist/auto-surface/projectors/settings-shell.mjs +114 -0
  59. package/dist/auto-surface/projectors/settings-shell.mjs.map +1 -0
  60. package/dist/auto-surface/scenario.cjs +38 -0
  61. package/dist/auto-surface/scenario.d.cts +31 -0
  62. package/dist/auto-surface/scenario.d.cts.map +1 -0
  63. package/dist/auto-surface/scenario.d.mts +31 -0
  64. package/dist/auto-surface/scenario.d.mts.map +1 -0
  65. package/dist/auto-surface/scenario.mjs +37 -0
  66. package/dist/auto-surface/scenario.mjs.map +1 -0
  67. package/dist/auto-surface/types.cjs +9 -0
  68. package/dist/auto-surface/types.d.cts +153 -0
  69. package/dist/auto-surface/types.d.cts.map +1 -0
  70. package/dist/auto-surface/types.d.mts +153 -0
  71. package/dist/auto-surface/types.d.mts.map +1 -0
  72. package/dist/auto-surface/types.mjs +9 -0
  73. package/dist/auto-surface/types.mjs.map +1 -0
  74. package/dist/auto-surface/value-dsl.cjs +80 -0
  75. package/dist/auto-surface/value-dsl.d.cts +24 -0
  76. package/dist/auto-surface/value-dsl.d.cts.map +1 -0
  77. package/dist/auto-surface/value-dsl.d.mts +24 -0
  78. package/dist/auto-surface/value-dsl.d.mts.map +1 -0
  79. package/dist/auto-surface/value-dsl.mjs +79 -0
  80. package/dist/auto-surface/value-dsl.mjs.map +1 -0
  81. package/dist/blocklet/activation.cjs +94 -0
  82. package/dist/blocklet/activation.d.cts +30 -0
  83. package/dist/blocklet/activation.d.cts.map +1 -0
  84. package/dist/blocklet/activation.d.mts +30 -0
  85. package/dist/blocklet/activation.d.mts.map +1 -0
  86. package/dist/blocklet/activation.mjs +94 -0
  87. package/dist/blocklet/activation.mjs.map +1 -0
  88. package/dist/blocklet/did-space-registry.cjs +282 -0
  89. package/dist/blocklet/did-space-registry.d.cts +82 -0
  90. package/dist/blocklet/did-space-registry.d.cts.map +1 -0
  91. package/dist/blocklet/did-space-registry.d.mts +82 -0
  92. package/dist/blocklet/did-space-registry.d.mts.map +1 -0
  93. package/dist/blocklet/did-space-registry.mjs +281 -0
  94. package/dist/blocklet/did-space-registry.mjs.map +1 -0
  95. package/dist/blocklet/handlers.cjs +66 -0
  96. package/dist/blocklet/handlers.d.cts +16 -0
  97. package/dist/blocklet/handlers.d.cts.map +1 -0
  98. package/dist/blocklet/handlers.d.mts +16 -0
  99. package/dist/blocklet/handlers.d.mts.map +1 -0
  100. package/dist/blocklet/handlers.mjs +65 -0
  101. package/dist/blocklet/handlers.mjs.map +1 -0
  102. package/dist/blocklet/index.d.mts +7 -0
  103. package/dist/blocklet/manifest.d.cts +2 -0
  104. package/dist/blocklet/manifest.d.mts +2 -0
  105. package/dist/blocklet/manifest.mjs +3 -0
  106. package/dist/blocklet/registry.cjs +447 -0
  107. package/dist/blocklet/registry.d.cts +197 -0
  108. package/dist/blocklet/registry.d.cts.map +1 -0
  109. package/dist/blocklet/registry.d.mts +197 -0
  110. package/dist/blocklet/registry.d.mts.map +1 -0
  111. package/dist/blocklet/registry.mjs +441 -0
  112. package/dist/blocklet/registry.mjs.map +1 -0
  113. package/dist/blocklet/static-render.cjs +183 -0
  114. package/dist/blocklet/static-render.d.cts +55 -0
  115. package/dist/blocklet/static-render.d.cts.map +1 -0
  116. package/dist/blocklet/static-render.d.mts +55 -0
  117. package/dist/blocklet/static-render.d.mts.map +1 -0
  118. package/dist/blocklet/static-render.mjs +181 -0
  119. package/dist/blocklet/static-render.mjs.map +1 -0
  120. package/dist/blocklet/types.d.cts +70 -0
  121. package/dist/blocklet/types.d.cts.map +1 -0
  122. package/dist/blocklet/types.d.mts +70 -0
  123. package/dist/blocklet/types.d.mts.map +1 -0
  124. package/dist/devices/chain.cjs +127 -0
  125. package/dist/devices/chain.d.cts +48 -0
  126. package/dist/devices/chain.d.cts.map +1 -0
  127. package/dist/devices/chain.d.mts +48 -0
  128. package/dist/devices/chain.d.mts.map +1 -0
  129. package/dist/devices/chain.mjs +125 -0
  130. package/dist/devices/chain.mjs.map +1 -0
  131. package/dist/dsl-shared.cjs +290 -0
  132. package/dist/dsl-shared.d.cts +54 -0
  133. package/dist/dsl-shared.d.cts.map +1 -0
  134. package/dist/dsl-shared.d.mts +54 -0
  135. package/dist/dsl-shared.d.mts.map +1 -0
  136. package/dist/dsl-shared.mjs +287 -0
  137. package/dist/dsl-shared.mjs.map +1 -0
  138. package/dist/http/afs-mcp.cjs +85 -0
  139. package/dist/http/afs-mcp.d.cts +14 -0
  140. package/dist/http/afs-mcp.d.cts.map +1 -0
  141. package/dist/http/afs-mcp.d.mts +14 -0
  142. package/dist/http/afs-mcp.d.mts.map +1 -0
  143. package/dist/http/afs-mcp.mjs +86 -0
  144. package/dist/http/afs-mcp.mjs.map +1 -0
  145. package/dist/http/afs-raw.cjs +264 -0
  146. package/dist/http/afs-raw.d.cts +31 -0
  147. package/dist/http/afs-raw.d.cts.map +1 -0
  148. package/dist/http/afs-raw.d.mts +31 -0
  149. package/dist/http/afs-raw.d.mts.map +1 -0
  150. package/dist/http/afs-raw.mjs +264 -0
  151. package/dist/http/afs-raw.mjs.map +1 -0
  152. package/dist/http/afs-rest.cjs +30 -0
  153. package/dist/http/afs-rest.d.cts +21 -0
  154. package/dist/http/afs-rest.d.cts.map +1 -0
  155. package/dist/http/afs-rest.d.mts +21 -0
  156. package/dist/http/afs-rest.d.mts.map +1 -0
  157. package/dist/http/afs-rest.mjs +31 -0
  158. package/dist/http/afs-rest.mjs.map +1 -0
  159. package/dist/http/afs-rpc-wire.cjs +271 -0
  160. package/dist/http/afs-rpc-wire.d.cts +195 -0
  161. package/dist/http/afs-rpc-wire.d.cts.map +1 -0
  162. package/dist/http/afs-rpc-wire.d.mts +195 -0
  163. package/dist/http/afs-rpc-wire.d.mts.map +1 -0
  164. package/dist/http/afs-rpc-wire.mjs +262 -0
  165. package/dist/http/afs-rpc-wire.mjs.map +1 -0
  166. package/dist/http/afs-rpc.cjs +729 -0
  167. package/dist/http/afs-rpc.d.cts +103 -0
  168. package/dist/http/afs-rpc.d.cts.map +1 -0
  169. package/dist/http/afs-rpc.d.mts +103 -0
  170. package/dist/http/afs-rpc.d.mts.map +1 -0
  171. package/dist/http/afs-rpc.mjs +729 -0
  172. package/dist/http/afs-rpc.mjs.map +1 -0
  173. package/dist/http/afs-upload.cjs +116 -0
  174. package/dist/http/afs-upload.d.cts +40 -0
  175. package/dist/http/afs-upload.d.cts.map +1 -0
  176. package/dist/http/afs-upload.d.mts +40 -0
  177. package/dist/http/afs-upload.d.mts.map +1 -0
  178. package/dist/http/afs-upload.mjs +116 -0
  179. package/dist/http/afs-upload.mjs.map +1 -0
  180. package/dist/http/aup-client.cjs +60 -0
  181. package/dist/http/aup-client.d.cts +22 -0
  182. package/dist/http/aup-client.d.cts.map +1 -0
  183. package/dist/http/aup-client.d.mts +22 -0
  184. package/dist/http/aup-client.d.mts.map +1 -0
  185. package/dist/http/aup-client.mjs +59 -0
  186. package/dist/http/aup-client.mjs.map +1 -0
  187. package/dist/http/aup-event.cjs +100 -0
  188. package/dist/http/aup-event.d.cts +38 -0
  189. package/dist/http/aup-event.d.cts.map +1 -0
  190. package/dist/http/aup-event.d.mts +38 -0
  191. package/dist/http/aup-event.d.mts.map +1 -0
  192. package/dist/http/aup-event.mjs +101 -0
  193. package/dist/http/aup-event.mjs.map +1 -0
  194. package/dist/http/auth-context.cjs +66 -0
  195. package/dist/http/auth-context.d.cts +64 -0
  196. package/dist/http/auth-context.d.cts.map +1 -0
  197. package/dist/http/auth-context.d.mts +64 -0
  198. package/dist/http/auth-context.d.mts.map +1 -0
  199. package/dist/http/auth-context.mjs +66 -0
  200. package/dist/http/auth-context.mjs.map +1 -0
  201. package/dist/http/csrf.cjs +50 -0
  202. package/dist/http/csrf.d.cts +8 -0
  203. package/dist/http/csrf.d.cts.map +1 -0
  204. package/dist/http/csrf.d.mts +8 -0
  205. package/dist/http/csrf.d.mts.map +1 -0
  206. package/dist/http/csrf.mjs +49 -0
  207. package/dist/http/csrf.mjs.map +1 -0
  208. package/dist/http/handlers.cjs +106 -0
  209. package/dist/http/handlers.d.cts +39 -0
  210. package/dist/http/handlers.d.cts.map +1 -0
  211. package/dist/http/handlers.d.mts +39 -0
  212. package/dist/http/handlers.d.mts.map +1 -0
  213. package/dist/http/handlers.mjs +104 -0
  214. package/dist/http/handlers.mjs.map +1 -0
  215. package/dist/http/index.d.mts +1 -0
  216. package/dist/http/ingest-facade.cjs +315 -0
  217. package/dist/http/ingest-facade.mjs +314 -0
  218. package/dist/http/ingest-facade.mjs.map +1 -0
  219. package/dist/http/raw-url.cjs +136 -0
  220. package/dist/http/raw-url.d.cts +54 -0
  221. package/dist/http/raw-url.d.cts.map +1 -0
  222. package/dist/http/raw-url.d.mts +54 -0
  223. package/dist/http/raw-url.d.mts.map +1 -0
  224. package/dist/http/raw-url.mjs +136 -0
  225. package/dist/http/raw-url.mjs.map +1 -0
  226. package/dist/http/request-context.cjs +178 -0
  227. package/dist/http/request-context.d.cts +76 -0
  228. package/dist/http/request-context.d.cts.map +1 -0
  229. package/dist/http/request-context.d.mts +76 -0
  230. package/dist/http/request-context.d.mts.map +1 -0
  231. package/dist/http/request-context.mjs +179 -0
  232. package/dist/http/request-context.mjs.map +1 -0
  233. package/dist/index.cjs +197 -0
  234. package/dist/index.d.cts +63 -0
  235. package/dist/index.d.mts +68 -0
  236. package/dist/index.mjs +61 -0
  237. package/dist/mcp/index.cjs +9 -0
  238. package/dist/mcp/index.d.cts +5 -0
  239. package/dist/mcp/index.d.mts +5 -0
  240. package/dist/mcp/index.mjs +6 -0
  241. package/dist/mcp/prompts.cjs +17 -0
  242. package/dist/mcp/prompts.d.cts +8 -0
  243. package/dist/mcp/prompts.d.cts.map +1 -0
  244. package/dist/mcp/prompts.d.mts +8 -0
  245. package/dist/mcp/prompts.d.mts.map +1 -0
  246. package/dist/mcp/prompts.mjs +17 -0
  247. package/dist/mcp/prompts.mjs.map +1 -0
  248. package/dist/mcp/resources.cjs +22 -0
  249. package/dist/mcp/resources.d.cts +8 -0
  250. package/dist/mcp/resources.d.cts.map +1 -0
  251. package/dist/mcp/resources.d.mts +8 -0
  252. package/dist/mcp/resources.d.mts.map +1 -0
  253. package/dist/mcp/resources.mjs +22 -0
  254. package/dist/mcp/resources.mjs.map +1 -0
  255. package/dist/mcp/server.cjs +51 -0
  256. package/dist/mcp/server.d.cts +20 -0
  257. package/dist/mcp/server.d.cts.map +1 -0
  258. package/dist/mcp/server.d.mts +20 -0
  259. package/dist/mcp/server.d.mts.map +1 -0
  260. package/dist/mcp/server.mjs +52 -0
  261. package/dist/mcp/server.mjs.map +1 -0
  262. package/dist/mcp/tools.cjs +218 -0
  263. package/dist/mcp/tools.d.cts +8 -0
  264. package/dist/mcp/tools.d.cts.map +1 -0
  265. package/dist/mcp/tools.d.mts +8 -0
  266. package/dist/mcp/tools.d.mts.map +1 -0
  267. package/dist/mcp/tools.mjs +219 -0
  268. package/dist/mcp/tools.mjs.map +1 -0
  269. package/dist/mcp/utils.cjs +75 -0
  270. package/dist/mcp/utils.mjs +69 -0
  271. package/dist/mcp/utils.mjs.map +1 -0
  272. package/dist/mount/index.cjs +4 -0
  273. package/dist/mount/index.d.cts +3 -0
  274. package/dist/mount/index.d.mts +3 -0
  275. package/dist/mount/index.mjs +3 -0
  276. package/dist/mount/materializer.cjs +167 -0
  277. package/dist/mount/materializer.d.cts +97 -0
  278. package/dist/mount/materializer.d.cts.map +1 -0
  279. package/dist/mount/materializer.d.mts +97 -0
  280. package/dist/mount/materializer.d.mts.map +1 -0
  281. package/dist/mount/materializer.mjs +167 -0
  282. package/dist/mount/materializer.mjs.map +1 -0
  283. package/dist/mount/types.d.cts +90 -0
  284. package/dist/mount/types.d.cts.map +1 -0
  285. package/dist/mount/types.d.mts +90 -0
  286. package/dist/mount/types.d.mts.map +1 -0
  287. package/dist/mount-config.cjs +61 -0
  288. package/dist/mount-config.d.cts +19 -0
  289. package/dist/mount-config.d.cts.map +1 -0
  290. package/dist/mount-config.d.mts +19 -0
  291. package/dist/mount-config.d.mts.map +1 -0
  292. package/dist/mount-config.mjs +61 -0
  293. package/dist/mount-config.mjs.map +1 -0
  294. package/dist/projectors/aup-projector.cjs +190 -0
  295. package/dist/projectors/aup-projector.d.cts +59 -0
  296. package/dist/projectors/aup-projector.d.cts.map +1 -0
  297. package/dist/projectors/aup-projector.d.mts +59 -0
  298. package/dist/projectors/aup-projector.d.mts.map +1 -0
  299. package/dist/projectors/aup-projector.mjs +188 -0
  300. package/dist/projectors/aup-projector.mjs.map +1 -0
  301. package/dist/projectors/index.cjs +6 -0
  302. package/dist/projectors/index.d.cts +2 -0
  303. package/dist/projectors/index.d.mts +2 -0
  304. package/dist/projectors/index.mjs +3 -0
  305. package/dist/scaffold/afs.cjs +195 -0
  306. package/dist/scaffold/afs.d.cts +16 -0
  307. package/dist/scaffold/afs.d.cts.map +1 -0
  308. package/dist/scaffold/afs.d.mts +16 -0
  309. package/dist/scaffold/afs.d.mts.map +1 -0
  310. package/dist/scaffold/afs.mjs +195 -0
  311. package/dist/scaffold/afs.mjs.map +1 -0
  312. package/dist/scaffold/composition.cjs +92 -0
  313. package/dist/scaffold/composition.d.cts +10 -0
  314. package/dist/scaffold/composition.d.cts.map +1 -0
  315. package/dist/scaffold/composition.d.mts +10 -0
  316. package/dist/scaffold/composition.d.mts.map +1 -0
  317. package/dist/scaffold/composition.mjs +92 -0
  318. package/dist/scaffold/composition.mjs.map +1 -0
  319. package/dist/scaffold/dsl-error.cjs +21 -0
  320. package/dist/scaffold/dsl-error.d.cts +11 -0
  321. package/dist/scaffold/dsl-error.d.cts.map +1 -0
  322. package/dist/scaffold/dsl-error.d.mts +11 -0
  323. package/dist/scaffold/dsl-error.d.mts.map +1 -0
  324. package/dist/scaffold/dsl-error.mjs +20 -0
  325. package/dist/scaffold/dsl-error.mjs.map +1 -0
  326. package/dist/scaffold/index.d.mts +12 -0
  327. package/dist/scaffold/manifest-dsl.cjs +188 -0
  328. package/dist/scaffold/manifest-dsl.d.cts +8 -0
  329. package/dist/scaffold/manifest-dsl.d.cts.map +1 -0
  330. package/dist/scaffold/manifest-dsl.d.mts +8 -0
  331. package/dist/scaffold/manifest-dsl.d.mts.map +1 -0
  332. package/dist/scaffold/manifest-dsl.mjs +188 -0
  333. package/dist/scaffold/manifest-dsl.mjs.map +1 -0
  334. package/dist/scaffold/module-dsl.cjs +284 -0
  335. package/dist/scaffold/module-dsl.d.cts +8 -0
  336. package/dist/scaffold/module-dsl.d.cts.map +1 -0
  337. package/dist/scaffold/module-dsl.d.mts +8 -0
  338. package/dist/scaffold/module-dsl.d.mts.map +1 -0
  339. package/dist/scaffold/module-dsl.mjs +284 -0
  340. package/dist/scaffold/module-dsl.mjs.map +1 -0
  341. package/dist/scaffold/resolver.cjs +156 -0
  342. package/dist/scaffold/resolver.d.cts +7 -0
  343. package/dist/scaffold/resolver.d.cts.map +1 -0
  344. package/dist/scaffold/resolver.d.mts +7 -0
  345. package/dist/scaffold/resolver.d.mts.map +1 -0
  346. package/dist/scaffold/resolver.mjs +156 -0
  347. package/dist/scaffold/resolver.mjs.map +1 -0
  348. package/dist/scaffold/runtime.cjs +24 -0
  349. package/dist/scaffold/runtime.d.cts +19 -0
  350. package/dist/scaffold/runtime.d.cts.map +1 -0
  351. package/dist/scaffold/runtime.d.mts +19 -0
  352. package/dist/scaffold/runtime.d.mts.map +1 -0
  353. package/dist/scaffold/runtime.mjs +25 -0
  354. package/dist/scaffold/runtime.mjs.map +1 -0
  355. package/dist/scaffold/shell-dsl.cjs +279 -0
  356. package/dist/scaffold/shell-dsl.d.cts +8 -0
  357. package/dist/scaffold/shell-dsl.d.cts.map +1 -0
  358. package/dist/scaffold/shell-dsl.d.mts +8 -0
  359. package/dist/scaffold/shell-dsl.d.mts.map +1 -0
  360. package/dist/scaffold/shell-dsl.mjs +279 -0
  361. package/dist/scaffold/shell-dsl.mjs.map +1 -0
  362. package/dist/scaffold/surface-dsl.cjs +182 -0
  363. package/dist/scaffold/surface-dsl.d.cts +8 -0
  364. package/dist/scaffold/surface-dsl.d.cts.map +1 -0
  365. package/dist/scaffold/surface-dsl.d.mts +8 -0
  366. package/dist/scaffold/surface-dsl.d.mts.map +1 -0
  367. package/dist/scaffold/surface-dsl.mjs +182 -0
  368. package/dist/scaffold/surface-dsl.mjs.map +1 -0
  369. package/dist/scaffold/surfaces.cjs +84 -0
  370. package/dist/scaffold/surfaces.d.cts +23 -0
  371. package/dist/scaffold/surfaces.d.cts.map +1 -0
  372. package/dist/scaffold/surfaces.d.mts +24 -0
  373. package/dist/scaffold/surfaces.d.mts.map +1 -0
  374. package/dist/scaffold/surfaces.mjs +84 -0
  375. package/dist/scaffold/surfaces.mjs.map +1 -0
  376. package/dist/scaffold/types.d.cts +264 -0
  377. package/dist/scaffold/types.d.cts.map +1 -0
  378. package/dist/scaffold/types.d.mts +265 -0
  379. package/dist/scaffold/types.d.mts.map +1 -0
  380. package/dist/scaffold/workspace-dsl.cjs +339 -0
  381. package/dist/scaffold/workspace-dsl.d.cts +49 -0
  382. package/dist/scaffold/workspace-dsl.d.cts.map +1 -0
  383. package/dist/scaffold/workspace-dsl.d.mts +49 -0
  384. package/dist/scaffold/workspace-dsl.d.mts.map +1 -0
  385. package/dist/scaffold/workspace-dsl.mjs +337 -0
  386. package/dist/scaffold/workspace-dsl.mjs.map +1 -0
  387. package/dist/scope/index.d.mts +3 -0
  388. package/dist/scope/read-blocklet-scope.cjs +29 -0
  389. package/dist/scope/read-blocklet-scope.d.cts +8 -0
  390. package/dist/scope/read-blocklet-scope.d.cts.map +1 -0
  391. package/dist/scope/read-blocklet-scope.d.mts +8 -0
  392. package/dist/scope/read-blocklet-scope.d.mts.map +1 -0
  393. package/dist/scope/read-blocklet-scope.mjs +30 -0
  394. package/dist/scope/read-blocklet-scope.mjs.map +1 -0
  395. package/dist/scope/resolve-scoped-afs.cjs +87 -0
  396. package/dist/scope/resolve-scoped-afs.d.cts +7 -0
  397. package/dist/scope/resolve-scoped-afs.d.cts.map +1 -0
  398. package/dist/scope/resolve-scoped-afs.d.mts +7 -0
  399. package/dist/scope/resolve-scoped-afs.d.mts.map +1 -0
  400. package/dist/scope/resolve-scoped-afs.mjs +88 -0
  401. package/dist/scope/resolve-scoped-afs.mjs.map +1 -0
  402. package/dist/scope/types.d.cts +25 -0
  403. package/dist/scope/types.d.cts.map +1 -0
  404. package/dist/scope/types.d.mts +25 -0
  405. package/dist/scope/types.d.mts.map +1 -0
  406. package/dist/session/backends.cjs +90 -0
  407. package/dist/session/backends.d.cts +35 -0
  408. package/dist/session/backends.d.cts.map +1 -0
  409. package/dist/session/backends.d.mts +35 -0
  410. package/dist/session/backends.d.mts.map +1 -0
  411. package/dist/session/backends.mjs +89 -0
  412. package/dist/session/backends.mjs.map +1 -0
  413. package/dist/session/replication-resolver.cjs +323 -0
  414. package/dist/session/replication-resolver.d.cts +71 -0
  415. package/dist/session/replication-resolver.d.cts.map +1 -0
  416. package/dist/session/replication-resolver.d.mts +71 -0
  417. package/dist/session/replication-resolver.d.mts.map +1 -0
  418. package/dist/session/replication-resolver.mjs +323 -0
  419. package/dist/session/replication-resolver.mjs.map +1 -0
  420. package/dist/session/role-level.cjs +36 -0
  421. package/dist/session/role-level.d.cts +24 -0
  422. package/dist/session/role-level.d.cts.map +1 -0
  423. package/dist/session/role-level.d.mts +24 -0
  424. package/dist/session/role-level.d.mts.map +1 -0
  425. package/dist/session/role-level.mjs +35 -0
  426. package/dist/session/role-level.mjs.map +1 -0
  427. package/dist/session/session-dir-providers.cjs +254 -0
  428. package/dist/session/session-dir-providers.d.cts +32 -0
  429. package/dist/session/session-dir-providers.d.cts.map +1 -0
  430. package/dist/session/session-dir-providers.d.mts +32 -0
  431. package/dist/session/session-dir-providers.d.mts.map +1 -0
  432. package/dist/session/session-dir-providers.mjs +252 -0
  433. package/dist/session/session-dir-providers.mjs.map +1 -0
  434. package/dist/session/session-id.cjs +20 -0
  435. package/dist/session/session-id.d.cts +15 -0
  436. package/dist/session/session-id.d.cts.map +1 -0
  437. package/dist/session/session-id.d.mts +15 -0
  438. package/dist/session/session-id.d.mts.map +1 -0
  439. package/dist/session/session-id.mjs +20 -0
  440. package/dist/session/session-id.mjs.map +1 -0
  441. package/dist/session/session-user-afs.cjs +899 -0
  442. package/dist/session/session-user-afs.d.cts +296 -0
  443. package/dist/session/session-user-afs.d.cts.map +1 -0
  444. package/dist/session/session-user-afs.d.mts +296 -0
  445. package/dist/session/session-user-afs.d.mts.map +1 -0
  446. package/dist/session/session-user-afs.mjs +896 -0
  447. package/dist/session/session-user-afs.mjs.map +1 -0
  448. package/dist/session/settings-cascade.cjs +69 -0
  449. package/dist/session/settings-cascade.d.cts +81 -0
  450. package/dist/session/settings-cascade.d.cts.map +1 -0
  451. package/dist/session/settings-cascade.d.mts +81 -0
  452. package/dist/session/settings-cascade.d.mts.map +1 -0
  453. package/dist/session/settings-cascade.mjs +69 -0
  454. package/dist/session/settings-cascade.mjs.map +1 -0
  455. package/dist/session/settings-resolver.cjs +770 -0
  456. package/dist/session/settings-resolver.d.cts +177 -0
  457. package/dist/session/settings-resolver.d.cts.map +1 -0
  458. package/dist/session/settings-resolver.d.mts +177 -0
  459. package/dist/session/settings-resolver.d.mts.map +1 -0
  460. package/dist/session/settings-resolver.mjs +771 -0
  461. package/dist/session/settings-resolver.mjs.map +1 -0
  462. package/dist/session/settings-schema-store.cjs +0 -0
  463. package/dist/session/settings-schema-store.d.cts +72 -0
  464. package/dist/session/settings-schema-store.d.cts.map +1 -0
  465. package/dist/session/settings-schema-store.d.mts +72 -0
  466. package/dist/session/settings-schema-store.d.mts.map +1 -0
  467. package/dist/session/settings-schema-store.mjs +0 -0
  468. package/dist/session/settings-schema-store.mjs.map +1 -0
  469. package/dist/system-mounts/index.cjs +14 -0
  470. package/dist/system-mounts/index.d.cts +6 -0
  471. package/dist/system-mounts/index.d.mts +6 -0
  472. package/dist/system-mounts/index.mjs +6 -0
  473. package/dist/system-mounts/install.cjs +101 -0
  474. package/dist/system-mounts/install.d.cts +36 -0
  475. package/dist/system-mounts/install.d.cts.map +1 -0
  476. package/dist/system-mounts/install.d.mts +37 -0
  477. package/dist/system-mounts/install.d.mts.map +1 -0
  478. package/dist/system-mounts/install.mjs +102 -0
  479. package/dist/system-mounts/install.mjs.map +1 -0
  480. package/dist/system-mounts/installers.cjs +247 -0
  481. package/dist/system-mounts/installers.d.cts +13 -0
  482. package/dist/system-mounts/installers.d.cts.map +1 -0
  483. package/dist/system-mounts/installers.d.mts +14 -0
  484. package/dist/system-mounts/installers.d.mts.map +1 -0
  485. package/dist/system-mounts/installers.mjs +244 -0
  486. package/dist/system-mounts/installers.mjs.map +1 -0
  487. package/dist/system-mounts/lists.cjs +122 -0
  488. package/dist/system-mounts/lists.d.cts +34 -0
  489. package/dist/system-mounts/lists.d.cts.map +1 -0
  490. package/dist/system-mounts/lists.d.mts +34 -0
  491. package/dist/system-mounts/lists.d.mts.map +1 -0
  492. package/dist/system-mounts/lists.mjs +122 -0
  493. package/dist/system-mounts/lists.mjs.map +1 -0
  494. package/dist/system-mounts/types.d.cts +124 -0
  495. package/dist/system-mounts/types.d.cts.map +1 -0
  496. package/dist/system-mounts/types.d.mts +125 -0
  497. package/dist/system-mounts/types.d.mts.map +1 -0
  498. package/dist/system-mounts/validate.cjs +56 -0
  499. package/dist/system-mounts/validate.d.cts +34 -0
  500. package/dist/system-mounts/validate.d.cts.map +1 -0
  501. package/dist/system-mounts/validate.d.mts +34 -0
  502. package/dist/system-mounts/validate.d.mts.map +1 -0
  503. package/dist/system-mounts/validate.mjs +56 -0
  504. package/dist/system-mounts/validate.mjs.map +1 -0
  505. package/dist/terminal-llm.cjs +90 -0
  506. package/dist/terminal-llm.d.cts +50 -0
  507. package/dist/terminal-llm.d.cts.map +1 -0
  508. package/dist/terminal-llm.d.mts +50 -0
  509. package/dist/terminal-llm.d.mts.map +1 -0
  510. package/dist/terminal-llm.mjs +90 -0
  511. package/dist/terminal-llm.mjs.map +1 -0
  512. package/dist/terminal-repl.cjs +371 -0
  513. package/dist/terminal-repl.d.cts +69 -0
  514. package/dist/terminal-repl.d.cts.map +1 -0
  515. package/dist/terminal-repl.d.mts +69 -0
  516. package/dist/terminal-repl.d.mts.map +1 -0
  517. package/dist/terminal-repl.mjs +370 -0
  518. package/dist/terminal-repl.mjs.map +1 -0
  519. package/dist/transport/idle-timer.cjs +136 -0
  520. package/dist/transport/idle-timer.d.cts +92 -0
  521. package/dist/transport/idle-timer.d.cts.map +1 -0
  522. package/dist/transport/idle-timer.d.mts +92 -0
  523. package/dist/transport/idle-timer.d.mts.map +1 -0
  524. package/dist/transport/idle-timer.mjs +132 -0
  525. package/dist/transport/idle-timer.mjs.map +1 -0
  526. package/dist/web-component-dsl.cjs +153 -0
  527. package/dist/web-component-dsl.d.cts +27 -0
  528. package/dist/web-component-dsl.d.cts.map +1 -0
  529. package/dist/web-component-dsl.d.mts +27 -0
  530. package/dist/web-component-dsl.d.mts.map +1 -0
  531. package/dist/web-component-dsl.mjs +152 -0
  532. package/dist/web-component-dsl.mjs.map +1 -0
  533. package/package.json +81 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"afs-rpc.mjs","names":[],"sources":["../../src/http/afs-rpc.ts"],"sourcesContent":["/**\n * Blocklet-aware HTTP RPC handler for stateless AFS read operations.\n *\n * Why: AUP pages that just need `afs.list()` / `afs.read()` open a\n * WebSocket otherwise — pinning a Durable Object on Cloudflare, or holding a\n * WS handle on a Node daemon — even though the call is stateless. This\n * handler runs purely in the HTTP request path; no WebSocket required.\n *\n * Distinct from `createAFSRestHandler` (which exposes the global AFS\n * unscoped at `/afs/*`): this handler resolves the caller's blocklet from\n * the request and returns paths that look like the blocklet's root to the\n * client. It mirrors `AUPSessionDO.onSessionStart`'s `ProjectionProvider`\n * setup but for HTTP, not WS.\n *\n * Scope: `read`, `list`, `stat`, `search`, `exec` (open to any resolved\n * caller), plus `write`/`delete` (Phase 4) gated by an auth + allowlist layer —\n * a verified Bearer caller writing only their own `/user/**` data path.\n *\n * Blocklet resolution priority:\n * 1. options.resolveHostAlias(host) — caller-provided alias map\n * (e.g. CF worker's patternCache\n * from `.afs/aliases.json` —\n * resolves `explorer.arcblock.io`\n * → `chain-explorer` when the\n * first label is NOT the\n * blocklet id)\n * 2. extractBlockletFromHost(host) — e.g. `showcase.afsd.io`\n * → `\"showcase\"` when first\n * label IS the blocklet id\n * 3. body.blocklet / ?blocklet= — untrusted browser hint fallback\n * 4. options.fallbackBlocklet\n */\n\nimport {\n type AFSBatchWriteEntry,\n type AFSDeleteOptions,\n type AFSExecChunk,\n type AFSExecOptions,\n type AFSForbiddenError,\n type AFSPermissionError,\n type AFSReadOptions,\n type AFSRoot,\n type AFSSearchOptions,\n type AFSStatOptions,\n type AFSWriteEntryPayload,\n type AFSWriteOptions,\n type CallerInfo,\n logEnabled,\n makeNsLog,\n sanitizeAfsPath,\n UPLOAD_LIMIT_DEFAULTS,\n type UploadLimits,\n} from \"@aigne/afs\";\nimport { time, timeMeta } from \"@aigne/afs-timing\";\nimport type { BlockletScope } from \"../scope/types.js\";\nimport {\n BATCH_LIMITS_CF,\n type BatchLimits,\n type BatchReadWireEntry,\n type BatchValidation,\n type BatchWireEntry,\n type BatchWriteDecode,\n encodeContentBase64,\n readBoundedBody,\n validateAndDecodeWrite,\n validateBatchBody,\n WIRE_ERROR_CODES,\n} from \"./afs-rpc-wire.js\";\nimport type { ResolveCallerFn, ResolveTrustedAfsContext } from \"./auth-context.js\";\nimport { csrfRejection } from \"./csrf.js\";\nimport {\n execIngestFacade,\n type FacadeAFS,\n injectIngestFacadeList,\n isIngestFacadePath,\n} from \"./ingest-facade.js\";\nimport { createAfsRequestContext } from \"./request-context.js\";\n\nconst READ_ONLY_OPS = new Set([\"read\", \"list\", \"stat\", \"search\", \"exec\", \"batchRead\"]);\nconst log = makeNsLog(\"aos:rpc\");\n/**\n * Mutating ops (Phase 4 + afs-rpc-batch P1). Gated by an extra auth +\n * allowlist layer on top of the read pipeline: a verified BEARER caller\n * writing only their own `/user/**` data path. Batch ops run the SAME gate\n * per entry — any entry failing it rejects the whole envelope (fail-closed).\n */\nconst MUTATING_OPS = new Set([\"write\", \"delete\", \"batchWrite\", \"batchDelete\"]);\n/** Batch wire ops (afs-rpc-batch P1): entries carry paths; no top-level path. */\nconst BATCH_OPS = new Set([\"batchRead\", \"batchWrite\", \"batchDelete\"]);\n\nexport interface AfsRpcHandlerOptions {\n /** The runtime-global AFS (the projection's source). */\n globalAFS: AFSRoot;\n /** Roots passed to `extractBlockletFromHost` (e.g. `[\"afsd.io\"]`). */\n domainRoots?: string[];\n /**\n * Resolve a normalized Host hostname to a blocklet id via a runtime-specific alias map.\n * Called before browser-provided `body.blocklet` / `?blocklet=` hints and\n * before first-label extraction, so brand domains declared in `blocklet.yaml`\n * `sites.domains` win over a guessed first label like `staging.aside.sh`.\n *\n * Use case: the CF Worker has `aliases.json` mapping\n * `explorer.arcblock.io → chain-explorer`. Without this hook, the worker's\n * pre-existing wrapper had to inject `?blocklet=<id>` into the request URL\n * before calling the handler — see runtimes/cloudflare/src/index.ts.\n * Now the wrapper is gone; the worker passes its alias resolver directly.\n *\n * The Node daemon currently has no such alias map and leaves this unset,\n * which preserves prior behavior (first-label extraction only).\n */\n resolveHostAlias?: (host: string) => Promise<string | undefined> | string | undefined;\n /** Used when neither body, query, nor Host header resolve a blocklet. */\n fallbackBlocklet?: string;\n /**\n * Bound on how many distinct blocklet projections we keep in memory.\n * 32 is plenty for typical multi-site Workers / daemons.\n */\n projectionCacheLimit?: number;\n /**\n * Where the blocklet catalog lives in the global AFS. Defaults to\n * `/registry/blocklets`. Override only if your runtime mounts the catalog\n * at a non-standard path.\n */\n registryPath?: string;\n /** Test seam: override the projected-AFS factory. */\n createBlockletAFS?: (blocklet: string, globalAFS: AFSRoot) => Promise<AFSRoot>;\n /**\n * Resolve a verified caller from the inbound request — typically wired to\n * the auth service's cookie/JWT verifier. When unset, all calls run as\n * anonymous.\n *\n * The resolved CallerInfo is merged into every op's `context` (alongside\n * `userId` for legacy consumers), so domain actions, capability checks,\n * and audit hooks observe a single source of truth.\n */\n resolveCaller?: ResolveCallerFn;\n /**\n * Resolve the trusted, host-controlled AFSContext slice (observability\n * Phase 3a) — `instanceDid` (the DID Space column key) and `agentRunOrigin`.\n * Runs after `resolveCaller`; its result OVERRIDES any client-forged value.\n * When unset, the handler defaults to `{ instanceDid: <resolved blocklet>,\n * agentRunOrigin: \"interactive\" }` so even anonymous Node requests carry an\n * instanceDid. CF bound hosts wire this to return the real host-resolved\n * instanceDid instead of the blocklet compat key.\n */\n resolveTrustedAfsContext?: ResolveTrustedAfsContext;\n /**\n * Optional PER-REQUEST wrapper applied AFTER the (cached, per-blocklet) base\n * AFS is resolved and the caller is verified. Use this to overlay per-caller\n * (`/user`), `scope:user` whole-space (`/space`), and per-session (`/tmp`)\n * views onto the blocklet's Small World\n * — the same overlay the WS session binding applies — so HTTP reads see an\n * identical topology.\n *\n * CRITICAL: the result is NOT cached. It MUST be rebuilt per request from the\n * verified `caller` and the request's `sessionId`, so caller A's `/user`\n * can never leak to caller B and session X's `/tmp` can never leak to\n * session Y. Implementations should be best-effort (return `baseAFS`\n * unchanged on any failure) — an overlay error must not break reads.\n */\n overlayForRequest?: (\n baseAFS: AFSRoot,\n ctx: {\n blocklet: string;\n caller: CallerInfo | null;\n sessionId: string | null;\n scope: BlockletScope;\n /** Host-resolved instanceDid (present even for an anonymous caller). instance-resolve F-2. */\n instanceDid?: string;\n },\n ) => AFSRoot | Promise<AFSRoot>;\n /**\n * Batch-op limits (afs-rpc-batch §5.2) + the pre-parse encoded body cap.\n * Runtimes inject their tier (BATCH_LIMITS_CF / BATCH_LIMITS_NODE); unset\n * defaults to the conservative CF table.\n */\n batchLimits?: BatchLimits;\n /**\n * Server-enforced upload caps (DID-Space upload-limits). Resolved by the\n * runtime's single env-read point (CF `resolveSpaceBindings`, Node\n * `resolveUploadLimits(process.env)`) and threaded into the ingest-prepare\n * facade so it can early-reject over-size declared uploads (Phase 2). Unset\n * defaults to the safe baseline inside the facade.\n */\n uploadLimits?: UploadLimits;\n}\n\nexport type AfsRpcHandler = (request: Request) => Promise<Response>;\n\nconst RPC_BLOCKLET_HINT_PRIORITY = {\n explicitPriority: \"fallback\",\n queryPriority: \"fallback\",\n} as const;\n\ninterface RpcBody {\n type?: string;\n path?: string;\n options?: Record<string, unknown>;\n args?: Record<string, unknown>;\n blocklet?: string;\n optional?: boolean;\n /** write payload (base64). Decoded by `validateAndDecodeWrite` (Phase 4). */\n content?: string;\n /** write content encoding — only \"base64\" today (wire-contract §5). */\n encoding?: string;\n /** optional mime hint for write. */\n contentType?: string;\n /**\n * Application metadata (createdAt / updatedAt / …) on the write's top-level\n * `meta` — distinct from `options.meta` (sync provenance). Forwarded to\n * `AFSWriteEntryPayload.meta` so metadata-aware providers index it.\n */\n meta?: Record<string, unknown>;\n /**\n * Optional WS session id, forwarded by the AUP client so the server can\n * resolve the same per-session `/tmp` overlay the WS path uses. Absent for\n * pure-HTTP callers (no live session) → `/tmp` simply won't appear.\n */\n sessionId?: string;\n}\n\n/**\n * afs-rpc-probe scratch (diagnostic) — threaded from `handle` into\n * `handleInner` so the completion log can name the request's `type` and total\n * wall-time even though `type` is parsed deep inside the inner handler.\n * See the probe block in `handle` for the why (killed-isolate localization).\n */\ninterface RpcProbe {\n t0: number;\n type?: string;\n}\n\nconst JSON_HEADERS = { \"Content-Type\": \"application/json; charset=utf-8\" } as const;\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), { status, headers: { ...JSON_HEADERS } });\n}\n\nfunction fail(status: number, error: string): Response {\n return jsonResponse(status, { ok: false, error });\n}\n\n/**\n * Lift a wire write entry's top-level `meta` onto `AFSWriteEntryPayload.meta`\n * so metadata-aware providers (did-space) index application fields like\n * `createdAt` / `updatedAt`. Sync provenance stays in `options.meta` and is\n * untouched here. Mirrors `execRootWriteAction`'s `args.meta` forwarding\n * (afs-root-handlers) for the RPC write/batchWrite ingress, which previously\n * dropped `meta` entirely. Returns `undefined` for absent/non-object meta.\n */\nfunction wireMeta(meta: unknown): AFSWriteEntryPayload[\"meta\"] | undefined {\n return meta !== null && typeof meta === \"object\"\n ? (meta as AFSWriteEntryPayload[\"meta\"])\n : undefined;\n}\n\n/**\n * Map AFS-layer errors onto sensible HTTP status codes.\n * Unknown classes fall through to 500; the message still surfaces in\n * the response body for debugging.\n * Exported for the raw endpoint (afs-raw.ts) — one mapping, not two.\n */\n/**\n * Stable AFS error code → HTTP status (afs-rpc-batch P2). One map serves both\n * thrown errors ({@link statusForError}) and the structured `code` slot on\n * core batch entry results — the two derivations must never drift.\n */\nconst STATUS_BY_CODE: Record<string, number> = {\n // ifMatch optimistic-concurrency conflict (Phase 1/4) → 409.\n AFS_CONFLICT: 409,\n AFS_NOT_FOUND: 404,\n AFS_ACTION_NOT_FOUND: 404,\n ENOENT: 404,\n ENOTDIR: 404,\n AFS_VALIDATION_ERROR: 400,\n AFS_ACCESS_MODE: 405,\n // Readonly mounts surface as 405 (op not allowed on this mount), not a 500\n // — pinned by the P2 readonly-batch contract (design §6.2).\n AFS_READONLY: 405,\n AFS_ACCESS_DENIED: 403,\n AFS_FORBIDDEN: 403,\n // exec-auth-gate: anonymous mutating exec → 401 (unauthenticated), distinct\n // from AFSForbiddenError → 403 (authenticated but not allowed).\n AFS_AUTH_REQUIRED: 401,\n // x402 protocol: billable action requires payment → 402.\n AFS_PAYMENT_REQUIRED: 402,\n AFS_UNSUPPORTED: 501,\n};\n\n/** HTTP status for a structured per-entry error code (500 when unknown). */\nfunction statusForCode(code: string | undefined): number {\n return (code && STATUS_BY_CODE[code]) || 500;\n}\n\nexport function statusForError(err: unknown): number {\n // Code-first: every AFSError subclass sets a stable `code` in its\n // constructor, and codes survive pnpm-hoisted package copies where\n // instanceof does not. Name and message checks are the fallbacks.\n const code = (err as { code?: string } | undefined)?.code ?? \"\";\n const byCode = STATUS_BY_CODE[code];\n if (byCode) return byCode;\n // Forbidden / Permission don't have a stable instanceof contract across\n // package boundaries (different copies of the class can ship under pnpm\n // hoist), so we also check by `name`.\n const name = (err as { name?: string } | undefined)?.name ?? \"\";\n if (name === \"AFSAuthRequiredError\") return 401;\n if (name === \"AFSPaymentRequiredError\") return 402;\n if (name === \"AFSConflictError\") return 409;\n if (name === \"AFSForbiddenError\" || name === \"AFSPermissionError\") return 403;\n if (name === \"AFSNotFoundError\") return 404;\n if (name === \"AFSValidationError\") return 400;\n if (name === \"AFSAccessModeError\") return 405;\n if (name === \"AFSReadonlyError\") return 405;\n if (name === \"AFSUnsupportedError\") return 501;\n const message = errorMessage(err);\n if (/\\b(ENOENT|ENOTDIR):/.test(message)) return 404;\n return 500;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Shape the 409 conflict body fields (wire-contract §7): the opaque\n * current-version triple the client's conflict resolution (LWW) consumes.\n * Single source for the single-op catch branch AND batch per-entry results —\n * the triple must never drift between the two (afs-rpc-batch G3).\n */\nfunction enrichConflictBody(err: unknown): {\n error: string;\n code: string;\n currentCid: string | null;\n currentMtime: number | null;\n currentDeviceId: string | null;\n} {\n const c = err as { currentCid?: unknown; currentMtime?: unknown; currentDeviceId?: unknown };\n return {\n error: errorMessage(err),\n code: WIRE_ERROR_CODES.conflict,\n currentCid: typeof c.currentCid === \"string\" ? c.currentCid : null,\n currentMtime: typeof c.currentMtime === \"number\" ? c.currentMtime : null,\n currentDeviceId: typeof c.currentDeviceId === \"string\" ? c.currentDeviceId : null,\n };\n}\n\n/**\n * Map a per-entry failure onto the batch `results[i]` shape — identical to\n * the single-op error body plus the `status` field (design §5.4 isomorphism).\n * `optional` mirrors the single-op soft-miss: 404 → `{status:200, missing}`.\n */\nfunction batchEntryError(err: unknown, optional?: boolean): Record<string, unknown> {\n const status = statusForError(err);\n const message = errorMessage(err);\n if (optional === true && status === 404) {\n return { status: 200, ok: false, error: message, missing: true };\n }\n if (status === 409) {\n return { status: 409, ok: false, ...enrichConflictBody(err) };\n }\n const code = (err as { code?: string } | undefined)?.code;\n return { status, ok: false, error: message, ...(code ? { code } : {}) };\n}\n\n/**\n * Map a CORE batch entry result onto the wire `results[i]` shape (L2 switch,\n * P2). Field-for-field identical to what the P1 handler loop produced from\n * thrown errors: success → single-op body, conflict → 409 + triple, other\n * failures → status derived from the structured `code`.\n */\nfunction wireResultFromCore(r: {\n success: boolean;\n data?: unknown;\n error?: string;\n code?: string;\n conflict?: {\n currentCid: string | null;\n currentMtime: number | null;\n currentDeviceId: string | null;\n };\n}): Record<string, unknown> {\n if (r.success) {\n return { status: 200, ok: true, data: encodeReadData(r.data) };\n }\n if (r.conflict) {\n return {\n status: 409,\n ok: false,\n error: r.error ?? \"\",\n code: WIRE_ERROR_CODES.conflict,\n ...r.conflict,\n };\n }\n return {\n status: statusForCode(r.code),\n ok: false,\n error: r.error ?? \"\",\n ...(r.code ? { code: r.code } : {}),\n };\n}\n\n// Path-traversal guard: the shared sanitizer (core `sanitizeAfsPath`,\n// afs-preview T0.5) replaces the old private `isSafePath`. Unencoded\n// accept/reject semantics are identical; encoded traversal (%2e%2e /\n// double-encoded ..) is now ALSO rejected at this edge — an intentional\n// tightening (AFS core decodes downstream, so an encoded \"..\" that slipped\n// past the old literal-only check could re-materialize after rebase).\n// The ORIGINAL path is still what gets forwarded to AFS (zero change to\n// forwarding semantics — core's own decode/double-encode checks stay in\n// the loop); the sanitizer is used here as a gate only.\n\n/** Max write payload (decoded bytes) before a 413. Large-blob bypass (D-A) is\n * deferred to Phase 6; until then, oversize writes are rejected outright. */\nconst MAX_WRITE_BYTES = 16 * 1024 * 1024;\n\n/**\n * Phase 4 write-surface allowlist: ONLY the caller's own `/user/**` data paths\n * are mutable over afs-rpc. `/user` itself, `/instance/**`, `/space/**`, the\n * blocklet-global root paths, and the `.objects`/`.changelog`/`.shares` virtual\n * namespaces are all rejected (the provider's D4 guard is a second layer).\n */\nfunction isAllowedUserDataPath(path: string): boolean {\n if (!path.startsWith(\"/user/\")) return false;\n const segs = path.split(\"/\").filter(Boolean); // [\"user\", ...]\n if (segs.length < 2) return false;\n return !segs.some((s) => s === \".objects\" || s === \".changelog\" || s === \".shares\");\n}\n\n/**\n * Authorize a mutating op (Phase 4, P0-1 + P1-1). Returns an error `Response`\n * when the write/delete is NOT permitted, else `null`.\n *\n * Defense in depth (not \"trust the client to write /user\"):\n * - authenticated (caller.did present) → else 401\n * - BEARER auth-source (cookie alone can't drive a write) → else 403 (CSRF)\n * - non-root blocklet scope (root exposes global topology) → else 403\n * - path under the caller's own `/user/**` data namespace → else 403\n * The `/user` overlay is keyed by `caller.did`, so a caller can only ever reach\n * its own fragment (structural isolation); `body.blocklet` only selects the app.\n */\nfunction checkMutationAllowed(\n path: string,\n caller: CallerInfo | null,\n scope: BlockletScope,\n): Response | null {\n if (!caller || !caller.did) return fail(401, \"Authentication required for write/delete\");\n if (caller.authSource !== \"bearer\") {\n return fail(403, \"write/delete require Authorization: Bearer (cookie-only is rejected)\");\n }\n if (scope === \"root\") return fail(403, \"write/delete not allowed on a root-scope blocklet\");\n if (!isAllowedUserDataPath(path)) {\n return fail(403, `write/delete only allowed under /user/** data paths (got '${path}')`);\n }\n return null;\n}\n\n/**\n * Wire-contract §5: a `read` whose `content` is raw bytes (e.g. `.objects/{cid}`\n * or any binary file) is base64-encoded with an `encoding` marker so it\n * survives JSON transport. Text/JSON content passes through unchanged. The\n * client (mobile RemoteDIDSpaceAFS) base64-decodes + recomputes the CID.\n */\nfunction encodeReadData(data: unknown): unknown {\n if (!data || typeof data !== \"object\") return data;\n const c = (data as { content?: unknown }).content;\n if (c instanceof Uint8Array) {\n return { ...(data as object), content: encodeContentBase64(c), encoding: \"base64\" };\n }\n if (c instanceof ArrayBuffer) {\n return {\n ...(data as object),\n content: encodeContentBase64(new Uint8Array(c)),\n encoding: \"base64\",\n };\n }\n return data;\n}\n\n/**\n * Extract the `{error,code,details}` triple from a provider that resolved with\n * `success:false` (semantic failure, no throw). Buffered and streaming exec\n * MUST format these identically — the cross-phase \"流式与 buffered 语义对齐\"\n * invariant — so both spread this single source of truth.\n */\nfunction execFailureFields(\n error: { message?: string; code?: string; details?: unknown } | undefined,\n): { error: string; code?: string; details?: unknown } {\n return {\n error: error?.message || error?.code || \"Action failed\",\n code: error?.code,\n details: error?.details,\n };\n}\n\n/**\n * NDJSON streaming response for a streaming `exec` (README §3.2). Each output\n * line is one typed envelope:\n * {\"t\":\"chunk\",\"chunk\":{ ...full AFSExecChunk... }} — per onChunk callback\n * {\"t\":\"done\",\"data\":<final exec result data>} — exec resolved success\n * {\"t\":\"error\",\"error\":\"...\",\"code\":\"...\",\"details\":...} — failure / reject\n *\n * Wire contract (§3.2): the chunk envelope serializes the WHOLE `AFSExecChunk`\n * (incl. `thoughts` + provider-specific fields) so the transport layer never\n * drops fields — the client typed-surface narrowing happens later, not here.\n *\n * Errors are two-layered (§3.2.1): handler-level errors (CSRF, bad path) are\n * already returned with a real HTTP status BEFORE this is called. Once the\n * stream is open the status is committed to 200, so an `afs.exec()` failure —\n * including a provider that resolves with `success:false` (mirroring the\n * buffered branch) or an effect-gate reject — surfaces as a `{\"t\":\"error\"}`\n * line, never as `{\"t\":\"done\"}`.\n */\nfunction execStreamResponse(\n afs: AFSRoot,\n path: string,\n args: Record<string, unknown>,\n opts: Record<string, unknown>,\n context: Record<string, unknown>,\n): Response {\n const encoder = new TextEncoder();\n // `stream` is a WIRE-ONLY flag (§3.2). AFSExecOptions has no `stream` field;\n // strip it so nothing extraneous reaches the provider. The real streaming\n // signal to the provider is the `onChunk` callback we inject below.\n const { stream: _wireOnlyStream, ...restOpts } = opts as { stream?: unknown };\n // `closed` guards against enqueue-after-close (client disconnect / cancel):\n // once the consumer goes away `controller.enqueue` throws, and any in-flight\n // exec onChunk callbacks must become no-ops so the server never hangs.\n let closed = false;\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const enqueue = (line: unknown) => {\n if (closed) return;\n try {\n controller.enqueue(encoder.encode(`${JSON.stringify(line)}\\n`));\n } catch {\n // Consumer disconnected — stop emitting; exec may still be running\n // but its chunks are now dropped instead of throwing.\n closed = true;\n }\n };\n\n const execOptions: AFSExecOptions = {\n ...(restOpts as AFSExecOptions),\n context,\n onChunk: (chunk: AFSExecChunk) => enqueue({ t: \"chunk\", chunk }),\n };\n\n try {\n const r = (await time(\"op\", () => afs.exec!(path, args, execOptions))) as {\n success?: boolean;\n data?: unknown;\n error?: { message?: string; code?: string; details?: unknown };\n };\n // Mirror the buffered branch's success:false semantics — a provider\n // that resolves with success:false (e.g. bad input, no throw) is an\n // ERROR line, NOT a done line. Reuse the same safe-access extraction.\n if (r.success === false) {\n enqueue({ t: \"error\", ...execFailureFields(r.error) });\n } else {\n enqueue({ t: \"done\", data: r.data });\n }\n } catch (err) {\n // exec rejected mid-flight (effect gate, provider runtime error). The\n // status is already 200, so this is an error LINE, not an HTTP status.\n const code = (err as { code?: string } | undefined)?.code;\n enqueue({ t: \"error\", error: errorMessage(err), code });\n } finally {\n if (!closed) {\n try {\n controller.close();\n } catch {\n // already closed by the consumer — nothing to do.\n }\n }\n }\n },\n cancel() {\n // Consumer cancelled (disconnect). Mark closed so any in-flight exec\n // onChunk callbacks become no-ops and we release without hanging.\n closed = true;\n },\n });\n\n return new Response(stream, {\n status: 200,\n headers: { \"Content-Type\": \"application/x-ndjson; charset=utf-8\" },\n });\n}\n\nexport function createAfsRpcHandler(options: AfsRpcHandlerOptions): AfsRpcHandler {\n // Shared request pipeline (afs-preview T2.0): blocklet/host resolution,\n // per-blocklet projection cache, caller + trusted context, per-request\n // overlay. PER-HANDLER instance — the raw endpoint creates its own; the\n // two never share a projection cache (design §3 cache semantics).\n const reqCtx = createAfsRequestContext(options);\n\n return async function handle(request: Request): Promise<Response> {\n // Outer try/catch is defense-in-depth: workerd treats any unhandled\n // promise rejection from a fetch handler as a fatal isolate error\n // (ECONNRESET on the client side), and AFS providers can throw from\n // surprising places (e.g. registry alias lookup that touches an\n // unhealthy upstream). Wrap the whole pipeline so an unexpected\n // throw always becomes a 500 instead of killing the runtime.\n //\n // afs-rpc-probe (diagnostic, gated by ns `aos:rpc` at debug via ARC_LOG):\n // when the isolate is KILLED (exceededResources / duration guard) the\n // request never returns, so no response, Server-Timing, or access-log line\n // survives — the ONLY evidence is a line emitted BEFORE the blocking op.\n // `handleInner` logs `rpc:begin` (pre-backend) and `rpc:resolved`\n // (post-auth/scope); `rpc:done` fires here on completion. In a burst,\n // `wrangler tail` then shows the culprit: a killed request appears as\n // begin (±resolved) with NO done — naming the `type` and localizing the\n // kill to auth/scope (no `resolved`) vs the op itself (has `resolved`).\n const probe: RpcProbe = { t0: Date.now() };\n try {\n const resp = await handleInner(request, probe);\n if (logEnabled(\"aos:rpc\", \"debug\")) {\n log.debug(\n `rpc:done type=${probe.type ?? \"?\"} status=${resp.status} totalMs=${Date.now() - probe.t0}`,\n );\n }\n return resp;\n } catch (err) {\n log.warn(`afs-rpc unhandled failure error=${errorMessage(err)}`);\n return fail(500, errorMessage(err));\n }\n };\n\n async function handleInner(request: Request, probe: RpcProbe): Promise<Response> {\n if (request.method !== \"POST\") {\n return fail(405, \"Method Not Allowed (use POST)\");\n }\n\n // Pre-parse encoded body cap (afs-rpc-batch §5.2 hardening, whole\n // endpoint): rejects oversized bodies BEFORE JSON.parse, so an\n // unauthenticated request can't buy parse CPU/memory. Content-Length is\n // only a fast path — the bounded read catches lying declarations too.\n const limits = options.batchLimits ?? BATCH_LIMITS_CF;\n const bounded = await readBoundedBody(request, limits.maxEncodedRequestBytes);\n if (bounded.tooLarge) {\n return jsonResponse(413, {\n ok: false,\n error: `Request body too large (> ${limits.maxEncodedRequestBytes} bytes)`,\n code: WIRE_ERROR_CODES.payloadTooLarge,\n });\n }\n\n let body: RpcBody;\n try {\n body = JSON.parse(bounded.text) as RpcBody;\n if (typeof body !== \"object\" || body === null) throw new Error(\"body must be object\");\n } catch (err) {\n return fail(400, `Invalid JSON body: ${errorMessage(err)}`);\n }\n\n const type = body.type;\n if (typeof type !== \"string\" || !type) {\n return fail(400, \"Missing 'type' field\");\n }\n const isMutation = MUTATING_OPS.has(type);\n if (!READ_ONLY_OPS.has(type) && !isMutation) {\n return fail(\n 400,\n `Unsupported op '${type}' (supported: ${[...READ_ONLY_OPS, ...MUTATING_OPS].join(\", \")})`,\n );\n }\n\n // Batch ops carry per-entry paths; single ops a top-level path. Both are\n // fully validated (incl. traversal) before any blocklet/caller resolution.\n const isBatch = BATCH_OPS.has(type);\n let batch: (BatchValidation & { ok: true }) | null = null;\n let path = \"\";\n if (isBatch) {\n const validated = validateBatchBody(\n body as { type: string; entries?: unknown },\n limits,\n sanitizeAfsPath,\n );\n if (!validated.ok) {\n return jsonResponse(validated.status, {\n ok: false,\n error: validated.error,\n ...(validated.code ? { code: validated.code } : {}),\n });\n }\n batch = validated;\n } else {\n if (typeof body.path !== \"string\" || sanitizeAfsPath(body.path) === null) {\n return fail(400, \"Missing or invalid 'path'\");\n }\n path = body.path;\n }\n\n // afs-rpc-probe: emit BEFORE any backend call (blocklet/caller/scope\n // resolution + dispatch all make subrequests) so a killed isolate still\n // leaves a line naming this request's `type` + target + shape. `opt`\n // surfaces the list knobs a full-manifest suspect would set (maxDepth=-1),\n // `args.entries` the exec batch size (commit-upload N), so the tail line\n // alone distinguishes a heavy manifest list from a small write.\n probe.type = type;\n if (logEnabled(\"aos:rpc\", \"debug\")) {\n const o = (body.options ?? {}) as Record<string, unknown>;\n const optBits = [\n o.maxDepth !== undefined ? `maxDepth=${o.maxDepth}` : \"\",\n o.limit !== undefined ? `limit=${o.limit}` : \"\",\n o.offset !== undefined ? `offset=${o.offset}` : \"\",\n o.cursor !== undefined ? \"cursor=1\" : \"\",\n ]\n .filter(Boolean)\n .join(\";\");\n const argEntries = Array.isArray((body.args as { entries?: unknown } | undefined)?.entries)\n ? (body.args as { entries: unknown[] }).entries.length\n : undefined;\n const target = isBatch ? `batch:${batch?.entries.length ?? 0}` : path;\n log.debug(\n `rpc:begin type=${type} target=${target}${\n argEntries !== undefined ? ` args.entries=${argEntries}` : \"\"\n }${optBits ? ` opt=${optBits}` : \"\"} blockletHint=${body.blocklet ?? \"-\"}`,\n );\n }\n\n // afs-rpc-probe timing split: stamp before/after each resolve sub-phase so\n // `rpc:resolved` attributes the (kill-adjacent) resolve latency to blocklet\n // vs caller-auth (SERVICES hop) vs scope/session-view build.\n const tPre = Date.now();\n const blocklet = await time(\"resolve\", () =>\n reqCtx.resolveBlocklet(request, body.blocklet, RPC_BLOCKLET_HINT_PRIORITY),\n );\n const tBlocklet = Date.now();\n if (!blocklet) {\n return fail(\n 400,\n \"Could not resolve blocklet (set body.blocklet, ?blocklet=, or use a subdomain)\",\n );\n }\n\n const opts = (body.options as Record<string, unknown> | undefined) ?? {};\n const caller = await reqCtx.resolveCaller(request);\n const tCaller = Date.now();\n const sessionIdForCtx =\n typeof body.sessionId === \"string\" && body.sessionId ? body.sessionId : null;\n\n // Scope + cached base AFS + trusted context + per-request overlay —\n // the shared pipeline (request-context.ts). Per-caller/per-session\n // overlays are rebuilt every request, never cached.\n let scope: BlockletScope;\n let afs: AFSRoot;\n let context: Record<string, unknown>;\n try {\n const scoped = await reqCtx.resolveScoped(request, {\n blocklet,\n sessionId: sessionIdForCtx,\n caller,\n opOptions: opts,\n });\n scope = scoped.scope;\n afs = scoped.afs;\n context = scoped.context;\n } catch (err) {\n return fail(statusForError(err), errorMessage(err));\n }\n const tScoped = Date.now();\n\n // afs-rpc-probe: post-auth/scope checkpoint. A killed request with a\n // `rpc:begin` but NO `rpc:resolved` died in blocklet/caller/scope\n // resolution; one WITH `rpc:resolved` but no `rpc:done` died in the op\n // dispatch below. The split attributes the resolve latency (the slow tail\n // that reaches the kill line) to blocklet vs caller-auth (SERVICES hop) vs\n // scope/session-view build; `resolveMs - (blocklet+caller+scoped)` is the\n // pre-resolve body read.\n if (logEnabled(\"aos:rpc\", \"debug\")) {\n log.debug(\n `rpc:resolved type=${type} blocklet=${blocklet} scope=${scope} resolveMs=${tScoped - probe.t0} blockletMs=${tBlocklet - tPre} callerMs=${tCaller - tBlocklet} scopedMs=${tScoped - tCaller}`,\n );\n }\n\n // Phase 4: mutating ops pass an extra auth + allowlist gate BEFORE dispatch.\n // The `scope`/`caller`/overlaid `afs` above are exactly what the check needs.\n // Batch mutations run the SAME gate per entry; any entry failing rejects\n // the whole envelope (fail-closed — a half-executed batch would mask\n // configuration errors, design §5.3).\n if (isMutation) {\n const gatePaths = batch ? batch.entries.map((e) => e.path) : [path];\n for (const gatePath of gatePaths) {\n const denied = checkMutationAllowed(gatePath, caller, scope);\n if (denied) return denied;\n }\n }\n\n // exec-auth-gate §4.7: CSRF / same-origin guard for cookie-authed exec.\n // `exec` rides READ_ONLY_OPS on the wire but is the one mutating channel a\n // browser cookie session can reach (semantic actions), so the edge treats\n // every cookie-authed exec as potentially-mutating and validates Origin.\n // Bearer + anonymous + test-header callers are exempt (see csrf.ts). The\n // core effect gate still decides read-vs-write auth; this only blocks\n // cross-site cookie-driven intent.\n if (type === \"exec\") {\n const csrfReason = csrfRejection(request, caller, true);\n if (csrfReason) return fail(403, csrfReason);\n }\n\n try {\n switch (type) {\n case \"read\": {\n if (!afs.read) return fail(501, \"read not supported\");\n const r = await time(\"op\", () =>\n afs.read!(path, { ...(opts as AFSReadOptions), context }),\n );\n // Base64-encode raw-byte content for JSON transport (wire-contract §5).\n return jsonResponse(200, { ok: true, data: encodeReadData(r.data) });\n }\n case \"list\": {\n const r = await time(\"op\", () =>\n afs.list(path, {\n ...(opts as Parameters<AFSRoot[\"list\"]>[1]),\n context,\n }),\n );\n // Browser upload plane discovery (design §4.5): surface the facade\n // actions when the browser lists the ROOT `/.actions`, so it finds\n // `ingest-prepare` + protocolVersion without knowing any mount.\n const listData = path === \"/.actions\" ? injectIngestFacadeList(r.data) : r.data;\n return jsonResponse(200, {\n ok: true,\n data: listData,\n total: r.total,\n // Forward pagination meta (e.g. the `.changelog` cursor/hasMore the\n // mobile sync increment depends on) — Phase 4.\n meta: r.meta,\n // Forward the live-update watermark (Live Update Resilience 0a/0f):\n // the client writes it as its reconcile baseline for this collection.\n liveSeq: r.liveSeq,\n });\n }\n case \"stat\": {\n if (!afs.stat) return fail(501, \"stat not supported\");\n const r = await time(\"op\", () =>\n afs.stat!(path, { ...(opts as AFSStatOptions), context }),\n );\n return jsonResponse(200, { ok: true, data: r.data });\n }\n case \"search\": {\n if (!afs.search) return fail(501, \"search not supported\");\n const query = String(body.args?.query ?? body.options?.query ?? \"\");\n // Structured facet filters (Epic #932 S2/S3) — carried alongside the\n // free-text query; the /user/index forwarding applies them as a hard\n // filter. Providers without facet support ignore them.\n const filters = (body.args?.filters ?? body.options?.filters) as\n | AFSSearchOptions[\"filters\"]\n | undefined;\n // Recall mode (semantic-search-toggle): a client toggle opts one query\n // into \"semantic\" (vector recall). Omitted → the /user/index\n // `defaultSearchMode` (\"combined\"). Same args-or-options passthrough as\n // filters; `undefined` is falsy downstream so it's byte-identical to today.\n const mode = (body.args?.mode ?? body.options?.mode) as\n | AFSSearchOptions[\"mode\"]\n | undefined;\n const r = await time(\"op\", () =>\n afs.search!(path, query, { ...(opts as AFSSearchOptions), context, filters, mode }),\n );\n return jsonResponse(200, { ok: true, data: r.data, message: r.message, meta: r.meta });\n }\n case \"exec\": {\n if (!afs.exec) return fail(501, \"exec not supported\");\n // Browser upload plane (design §4.2, D1 candidate-b): intercept the\n // root-visible facade BEFORE the default dispatch — these paths have no\n // real provider action; the facade plans + delegates to the target\n // mount's prepare/commit/abort-upload using this composed `afs`.\n if (isIngestFacadePath(path)) {\n const fr = await time(\"op\", () =>\n execIngestFacade({\n afs: afs as unknown as FacadeAFS,\n path,\n args: (body.args as Record<string, unknown> | undefined) ?? {},\n context,\n caller,\n // upload-limits Phase 2: declared-size early reject in the facade.\n // Resolved at the runtime single env-read point (Phase 0); unset\n // (e.g. a runtime that didn't wire it) → safe defaults.\n limits: options.uploadLimits ?? UPLOAD_LIMIT_DEFAULTS,\n }),\n );\n if (\"status\" in fr) return fail(fr.status, fr.error);\n if (fr.ok) return jsonResponse(200, { ok: true, data: fr.data });\n return jsonResponse(200, {\n ok: false,\n error: fr.error,\n ...(fr.code ? { code: fr.code } : {}),\n });\n }\n const rawArgs = (body.args as Record<string, unknown> | undefined) ?? {};\n // Inject server-side `_scope_afs` so dispatched targets (notably\n // /dev/agent/.actions/run) write to THIS request's scoped AFS\n // instead of the daemon's global root. Without this, `window.afs\n // .exec('/dev/agent/.actions/run', …)` from a logged-in user\n // lands at globalAFS, and the agent's `afs_write` tool writes to\n // `~/.afs/<host>/user/...` rather than the user's own DID Space.\n // Client-supplied `_scope_afs` (object-capability mismatch) is\n // stripped first so the injection is the only source. Same logic\n // as the WS `afs_exec` handler — this closes the gap for the RPC\n // path used by `window.afs.exec()` through subdomain blocklets.\n const args = { ...rawArgs };\n delete args._scope_afs;\n args._scope_afs = afs;\n // Wire-only streaming flag (§3.2): when the client declares\n // `options.stream === true`, respond with an NDJSON stream that\n // forwards real onChunk callbacks. Otherwise stay buffered (zero\n // regression — DID Space sync never streams).\n if ((opts as { stream?: unknown }).stream === true) {\n return execStreamResponse(afs, path, args, opts, context);\n }\n const r = await time(\"op\", () =>\n afs.exec!(path, args, { ...(opts as AFSExecOptions), context }),\n );\n // AFSExecResult carries `success` + (`data` | `error`). The\n // legacy envelope here was `{ ok: true, data: r.data }` which\n // silently dropped `r.success === false` paths — every caller\n // saw success regardless of what the provider returned. That\n // broke iOS birth-profile updates (form dismissed, profile\n // unchanged) and the deploy \"always says correct\" bug — same\n // root cause. Surface failures as `ok: false` with\n // code/message/details so clients can distinguish\n // transport-success-but-semantic-failure from\n // transport-success-with-data.\n const exec = r as {\n success?: boolean;\n data?: unknown;\n error?: { message?: string; code?: string; details?: unknown };\n };\n if (exec.success === false) {\n return jsonResponse(200, { ok: false, ...execFailureFields(exec.error) });\n }\n return jsonResponse(200, { ok: true, data: r.data });\n }\n case \"write\": {\n if (!afs.write) return fail(501, \"write not supported\");\n const decoded = validateAndDecodeWrite({\n type: \"write\",\n path,\n content: body.content,\n encoding: body.encoding,\n options: opts,\n });\n if (!decoded.ok) return fail(decoded.status, decoded.error);\n if (decoded.bytes.length > MAX_WRITE_BYTES) {\n return jsonResponse(413, {\n ok: false,\n error: `Payload too large (> ${MAX_WRITE_BYTES} bytes)`,\n code: WIRE_ERROR_CODES.payloadTooLarge,\n });\n }\n const r = await time(\"op\", () =>\n afs.write!(\n path,\n { content: decoded.bytes, meta: wireMeta(body.meta) },\n { ...(opts as AFSWriteOptions), context },\n ),\n );\n // Encode any raw-byte echo in the write response (consistency with read).\n return jsonResponse(200, { ok: true, data: encodeReadData(r.data) });\n }\n case \"delete\": {\n if (!afs.delete) return fail(501, \"delete not supported\");\n const r = await time(\"op\", () =>\n afs.delete!(path, { ...(opts as AFSDeleteOptions), context }),\n );\n return jsonResponse(200, { ok: true, data: r });\n }\n // ── afs-rpc-batch L2 (P2): batch mutations go STRAIGHT to the core\n // batch methods. One auth/blocklet/overlay resolution (G4); the core\n // owns per-entry options passthrough, accessMode gates, module\n // pushdown routing, and per-entry change events. Wire behavior is\n // bit-identical to the P1 handler loop (the golden switch assertion).\n case \"batchWrite\": {\n if (!afs.batchWrite) return fail(501, \"write not supported\");\n const entries = batch!.entries;\n const decodes = batch!.writeDecodes as BatchWriteDecode[];\n timeMeta(\"batch\", `batchWrite n=${entries.length}`);\n const results: Record<string, unknown>[] = new Array(entries.length);\n // Decode failures stay wire-level results (single-op parity); only\n // cleanly decoded entries enter the core batch.\n const coreEntries: AFSBatchWriteEntry[] = [];\n const coreIndex: number[] = [];\n for (let i = 0; i < entries.length; i++) {\n const decoded = decodes[i]!;\n if (!decoded.ok) {\n results[i] = {\n status: decoded.status,\n ok: false,\n error: decoded.error,\n ...(decoded.code ? { code: decoded.code } : {}),\n };\n continue;\n }\n coreIndex.push(i);\n coreEntries.push({\n path: (entries[i] as BatchWireEntry).path,\n content: {\n content: decoded.bytes,\n meta: wireMeta((entries[i] as BatchWireEntry).meta),\n },\n options: (entries[i] as BatchWireEntry).options as AFSWriteOptions | undefined,\n });\n }\n if (coreEntries.length > 0) {\n const core = await time(\"op\", () => afs.batchWrite!(coreEntries, { context }));\n for (let j = 0; j < core.results.length; j++) {\n results[coreIndex[j]!] = wireResultFromCore(core.results[j]!);\n }\n }\n const succeeded = results.filter((r) => r?.ok === true).length;\n return jsonResponse(200, {\n ok: true,\n results,\n succeeded,\n failed: results.length - succeeded,\n });\n }\n case \"batchDelete\": {\n if (!afs.batchDelete) return fail(501, \"delete not supported\");\n const entries = batch!.entries;\n timeMeta(\"batch\", `batchDelete n=${entries.length}`);\n const core = await time(\"op\", () =>\n afs.batchDelete!(\n entries.map((entry) => ({\n path: entry.path,\n options: entry.options as AFSDeleteOptions | undefined,\n })),\n { context },\n ),\n );\n const results = core.results.map((r) =>\n r.success\n ? // Single-op delete echoes `{path, deleted}` — synthesize the same\n // shape (core batch delete results carry no provider data slot).\n { status: 200, ok: true, data: { path: r.path, deleted: true } }\n : wireResultFromCore(r),\n );\n return jsonResponse(200, {\n ok: true,\n results,\n succeeded: core.succeeded,\n failed: core.failed,\n });\n }\n case \"batchRead\": {\n // L2 note (P2, deliberate): batchRead KEEPS the wire-level per-entry\n // loop over `afs.read` instead of one `afs.batchRead` call. The tail\n // truncation budget counts ENCODED response bytes — a wire concept\n // that must short-circuit BEFORE each read; handing the whole array\n // to core batchRead would read every entry (read-then-discard,\n // forbidden by §5.4) or smuggle wire byte-accounting into core.\n // Core `afs.batchRead` exists for programmatic consumers; the\n // provider pushdown rework lands with §9-1 (size-aware pre-check).\n if (!afs.read) return fail(501, \"read not supported\");\n const entries = batch!.entries as BatchReadWireEntry[];\n timeMeta(\"batch\", `batchRead n=${entries.length}`);\n const results: Record<string, unknown>[] = [];\n let succeeded = 0;\n // Tail truncation accumulates ENCODED (base64 response) bytes and is\n // checked BEFORE each entry executes: once the threshold is reached,\n // every remaining entry is 503 BATCH_TAIL_SKIPPED without being read.\n // An entry that starts reading is always returned in full (never\n // read-then-discard — overshoot is bounded by one single-op read).\n let encodedBytes = 0;\n await time(\"op\", async () => {\n for (const entry of entries) {\n if (encodedBytes >= limits.readTailSkipThresholdBytes) {\n results.push({\n status: 503,\n ok: false,\n error: \"Batch response budget reached — retry this entry in the next batch\",\n code: WIRE_ERROR_CODES.batchTailSkipped,\n });\n continue;\n }\n try {\n // includeActions:\"none\" — P0-measured: default action\n // enrichment doubles the per-read backend cost (4 vs 2\n // subrequests on DID Space) and sync (the batch consumer)\n // never reads actions. Single-op read behavior is unchanged.\n const r = await afs.read!(entry.path, {\n ...((entry.options ?? {}) as AFSReadOptions),\n includeActions: \"none\",\n context,\n });\n const data = encodeReadData(r.data) as { content?: unknown } | undefined;\n const content = data?.content;\n if (typeof content === \"string\") encodedBytes += content.length;\n results.push({ status: 200, ok: true, data });\n succeeded++;\n } catch (err) {\n results.push(batchEntryError(err, entry.optional === true));\n }\n }\n });\n return jsonResponse(200, {\n ok: true,\n results,\n succeeded,\n failed: results.length - succeeded,\n });\n }\n default:\n return fail(400, `Unsupported op '${type}'`);\n }\n } catch (err) {\n const status = statusForError(err);\n const message = errorMessage(err);\n if (body.optional === true && status === 404) {\n return jsonResponse(200, { ok: false, error: message, missing: true });\n }\n // x402: payment required — surface PAYMENT-REQUIRED header per x402 spec v2 §3.1.\n if (status === 402) {\n const paymentRequired = (err as { paymentRequired?: unknown }).paymentRequired;\n const extraHeaders: Record<string, string> =\n paymentRequired !== undefined\n ? {\n \"PAYMENT-REQUIRED\":\n typeof paymentRequired === \"string\"\n ? paymentRequired\n : JSON.stringify(paymentRequired),\n }\n : {};\n return new Response(\n JSON.stringify({ ok: false, error: message, code: \"AFS_PAYMENT_REQUIRED\" }),\n { status: 402, headers: { ...JSON_HEADERS, ...extraHeaders } },\n );\n }\n // On a 409 conflict, surface the opaque current-version fields the client\n // needs for conflict resolution (wire-contract §7). Other coded errors\n // pass their `code` through so clients can branch without string-matching.\n if (status === 409) {\n return jsonResponse(409, { ok: false, ...enrichConflictBody(err) });\n }\n if (status >= 500) {\n log.warn(\n `afs-rpc op failed type=${type} path=${isBatch ? \"<batch>\" : path} blocklet=${blocklet} status=${status} error=${message}`,\n );\n }\n const code = (err as { code?: string } | undefined)?.code;\n if (code) return jsonResponse(status, { ok: false, error: message, code });\n return fail(status, message);\n }\n }\n}\n\nexport type { AFSForbiddenError, AFSPermissionError };\n"],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+
2
+ //#region src/http/afs-upload.ts
3
+ const JSON_HEADERS = { "Content-Type": "application/json; charset=utf-8" };
4
+ const DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
5
+ function jsonResponse(status, body) {
6
+ return new Response(JSON.stringify(body), {
7
+ status,
8
+ headers: { ...JSON_HEADERS }
9
+ });
10
+ }
11
+ function fail(status, error) {
12
+ return jsonResponse(status, {
13
+ ok: false,
14
+ error
15
+ });
16
+ }
17
+ function errorMessage(err) {
18
+ return err instanceof Error ? err.message : String(err);
19
+ }
20
+ /** Same message-pattern mapping as aup-event.ts — error classes aren't exported across packages. */
21
+ function statusForError(err) {
22
+ const msg = errorMessage(err);
23
+ if (/unknown session/i.test(msg)) return 404;
24
+ if (/invalid session token/i.test(msg)) return 401;
25
+ return 500;
26
+ }
27
+ /** Blob/File duck-type — avoid relying on the `File` global across runtimes. */
28
+ function isBlobLike(v) {
29
+ return typeof v === "object" && v !== null && typeof v.arrayBuffer === "function";
30
+ }
31
+ function asString(v) {
32
+ return typeof v === "string" && v.length > 0 ? v : void 0;
33
+ }
34
+ function createAfsUploadHandler(options) {
35
+ const { provider, resolveCaller, maxBytes = DEFAULT_MAX_BYTES } = options;
36
+ return async function handle(request) {
37
+ if (request.method !== "POST") return fail(405, "Method Not Allowed (use POST)");
38
+ const declaredLen = Number(request.headers.get("content-length") ?? "");
39
+ if (Number.isFinite(declaredLen) && declaredLen > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);
40
+ const contentType = request.headers.get("content-type") ?? "";
41
+ let sessionId;
42
+ let sessionToken;
43
+ let targetDir;
44
+ let conflict;
45
+ const items = [];
46
+ try {
47
+ if (contentType.includes("multipart/form-data")) {
48
+ const form = await request.formData();
49
+ sessionId = asString(form.get("sessionId"));
50
+ sessionToken = asString(form.get("sessionToken"));
51
+ targetDir = asString(form.get("targetDir"));
52
+ conflict = asString(form.get("conflict"));
53
+ const files = form.getAll("file");
54
+ const relPaths = form.getAll("relPath");
55
+ let total = 0;
56
+ for (let i = 0; i < files.length; i++) {
57
+ const f = files[i];
58
+ if (!isBlobLike(f)) continue;
59
+ const bytes = new Uint8Array(await f.arrayBuffer());
60
+ total += bytes.length;
61
+ if (total > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);
62
+ const rel = asString(relPaths[i]);
63
+ const name = asString(f.name) ?? rel ?? `file-${i}`;
64
+ items.push({
65
+ kind: "content",
66
+ name,
67
+ relPath: rel,
68
+ content: bytes,
69
+ contentType: asString(f.type)
70
+ });
71
+ }
72
+ } else if (contentType.includes("application/octet-stream")) {
73
+ sessionId = asString(request.headers.get("x-afs-session"));
74
+ sessionToken = asString(request.headers.get("x-afs-session-token"));
75
+ targetDir = asString(request.headers.get("x-afs-target"));
76
+ conflict = asString(request.headers.get("x-afs-conflict"));
77
+ const name = asString(request.headers.get("x-afs-name")) ?? "upload.bin";
78
+ const relPath = asString(request.headers.get("x-afs-relpath"));
79
+ const bytes = new Uint8Array(await request.arrayBuffer());
80
+ if (bytes.length === 0) return fail(400, "empty body");
81
+ if (bytes.length > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);
82
+ items.push({
83
+ kind: "content",
84
+ name,
85
+ relPath,
86
+ content: bytes,
87
+ contentType: asString(request.headers.get("x-afs-content-type")) ?? "application/octet-stream"
88
+ });
89
+ } else return fail(415, "expected multipart/form-data or application/octet-stream");
90
+ } catch (err) {
91
+ return fail(400, `Invalid upload body: ${errorMessage(err)}`);
92
+ }
93
+ if (!sessionId) return fail(400, "Missing 'sessionId'");
94
+ if (!targetDir) return fail(400, "Missing 'targetDir'");
95
+ if (items.length === 0) return fail(400, "No files in upload");
96
+ const caller = resolveCaller ? await resolveCaller(request) : null;
97
+ try {
98
+ return jsonResponse(200, {
99
+ ok: true,
100
+ result: (await provider.dispatchHttpUpload({
101
+ sessionId,
102
+ sessionToken,
103
+ targetDir,
104
+ items,
105
+ conflict,
106
+ caller
107
+ })).result
108
+ });
109
+ } catch (err) {
110
+ return fail(statusForError(err), errorMessage(err));
111
+ }
112
+ };
113
+ }
114
+
115
+ //#endregion
116
+ exports.createAfsUploadHandler = createAfsUploadHandler;
@@ -0,0 +1,40 @@
1
+ import { ResolveCallerFn } from "./auth-context.cjs";
2
+ import { CallerInfo } from "@aigne/afs";
3
+
4
+ //#region src/http/afs-upload.d.ts
5
+ /** A single binary item parsed from the request, handed to the dispatcher. */
6
+ interface UploadItem {
7
+ kind: "content";
8
+ name: string;
9
+ relPath?: string;
10
+ content: Uint8Array;
11
+ contentType?: string;
12
+ }
13
+ interface AfsUploadDispatcher {
14
+ dispatchHttpUpload(args: {
15
+ sessionId: string;
16
+ sessionToken?: string;
17
+ targetDir: string;
18
+ items: UploadItem[];
19
+ conflict?: string;
20
+ caller?: CallerInfo | null;
21
+ }): Promise<{
22
+ result: unknown;
23
+ }>;
24
+ }
25
+ interface AfsUploadHandlerOptions {
26
+ /**
27
+ * The AUP UI provider that owns the session registry + ingest service.
28
+ * Typed loosely so this module doesn't import @aigne/afs-ui (cycle).
29
+ */
30
+ provider: AfsUploadDispatcher;
31
+ /** Resolve verified caller identity from the request (cookie/JWT). */
32
+ resolveCaller?: ResolveCallerFn;
33
+ /** Hard cap on total upload bytes per request. Default 100 MiB. */
34
+ maxBytes?: number;
35
+ }
36
+ type AfsUploadHandler = (request: Request) => Promise<Response>;
37
+ declare function createAfsUploadHandler(options: AfsUploadHandlerOptions): AfsUploadHandler;
38
+ //#endregion
39
+ export { AfsUploadHandler, AfsUploadHandlerOptions, UploadItem, createAfsUploadHandler };
40
+ //# sourceMappingURL=afs-upload.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"afs-upload.d.cts","names":[],"sources":["../../src/http/afs-upload.ts"],"mappings":";;;;;UAiCiB,UAAA;EACf,IAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA,EAAS,UAAA;EACT,WAAA;AAAA;AAAA,UAGQ,mBAAA;EACR,kBAAA,CAAmB,IAAA;IACjB,SAAA;IACA,YAAA;IACA,SAAA;IACA,KAAA,EAAO,UAAA;IACP,QAAA;IACA,MAAA,GAAS,UAAA;EAAA,IACP,OAAA;IAAU,MAAA;EAAA;AAAA;AAAA,UAGC,uBAAA;;;;;EAKf,QAAA,EAAU,mBAAA;EAOgD;EAL1D,aAAA,GAAgB,eAAA;EAKc;EAH9B,QAAA;AAAA;AAAA,KAGU,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;AAAA,iBAsC7C,sBAAA,CAAuB,OAAA,EAAS,uBAAA,GAA0B,gBAAA"}
@@ -0,0 +1,40 @@
1
+ import { ResolveCallerFn } from "./auth-context.mjs";
2
+ import { CallerInfo } from "@aigne/afs";
3
+
4
+ //#region src/http/afs-upload.d.ts
5
+ /** A single binary item parsed from the request, handed to the dispatcher. */
6
+ interface UploadItem {
7
+ kind: "content";
8
+ name: string;
9
+ relPath?: string;
10
+ content: Uint8Array;
11
+ contentType?: string;
12
+ }
13
+ interface AfsUploadDispatcher {
14
+ dispatchHttpUpload(args: {
15
+ sessionId: string;
16
+ sessionToken?: string;
17
+ targetDir: string;
18
+ items: UploadItem[];
19
+ conflict?: string;
20
+ caller?: CallerInfo | null;
21
+ }): Promise<{
22
+ result: unknown;
23
+ }>;
24
+ }
25
+ interface AfsUploadHandlerOptions {
26
+ /**
27
+ * The AUP UI provider that owns the session registry + ingest service.
28
+ * Typed loosely so this module doesn't import @aigne/afs-ui (cycle).
29
+ */
30
+ provider: AfsUploadDispatcher;
31
+ /** Resolve verified caller identity from the request (cookie/JWT). */
32
+ resolveCaller?: ResolveCallerFn;
33
+ /** Hard cap on total upload bytes per request. Default 100 MiB. */
34
+ maxBytes?: number;
35
+ }
36
+ type AfsUploadHandler = (request: Request) => Promise<Response>;
37
+ declare function createAfsUploadHandler(options: AfsUploadHandlerOptions): AfsUploadHandler;
38
+ //#endregion
39
+ export { AfsUploadHandler, AfsUploadHandlerOptions, UploadItem, createAfsUploadHandler };
40
+ //# sourceMappingURL=afs-upload.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"afs-upload.d.mts","names":[],"sources":["../../src/http/afs-upload.ts"],"mappings":";;;;;UAiCiB,UAAA;EACf,IAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA,EAAS,UAAA;EACT,WAAA;AAAA;AAAA,UAGQ,mBAAA;EACR,kBAAA,CAAmB,IAAA;IACjB,SAAA;IACA,YAAA;IACA,SAAA;IACA,KAAA,EAAO,UAAA;IACP,QAAA;IACA,MAAA,GAAS,UAAA;EAAA,IACP,OAAA;IAAU,MAAA;EAAA;AAAA;AAAA,UAGC,uBAAA;;;;;EAKf,QAAA,EAAU,mBAAA;EAOgD;EAL1D,aAAA,GAAgB,eAAA;EAKc;EAH9B,QAAA;AAAA;AAAA,KAGU,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;AAAA,iBAsC7C,sBAAA,CAAuB,OAAA,EAAS,uBAAA,GAA0B,gBAAA"}
@@ -0,0 +1,116 @@
1
+ //#region src/http/afs-upload.ts
2
+ const JSON_HEADERS = { "Content-Type": "application/json; charset=utf-8" };
3
+ const DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
4
+ function jsonResponse(status, body) {
5
+ return new Response(JSON.stringify(body), {
6
+ status,
7
+ headers: { ...JSON_HEADERS }
8
+ });
9
+ }
10
+ function fail(status, error) {
11
+ return jsonResponse(status, {
12
+ ok: false,
13
+ error
14
+ });
15
+ }
16
+ function errorMessage(err) {
17
+ return err instanceof Error ? err.message : String(err);
18
+ }
19
+ /** Same message-pattern mapping as aup-event.ts — error classes aren't exported across packages. */
20
+ function statusForError(err) {
21
+ const msg = errorMessage(err);
22
+ if (/unknown session/i.test(msg)) return 404;
23
+ if (/invalid session token/i.test(msg)) return 401;
24
+ return 500;
25
+ }
26
+ /** Blob/File duck-type — avoid relying on the `File` global across runtimes. */
27
+ function isBlobLike(v) {
28
+ return typeof v === "object" && v !== null && typeof v.arrayBuffer === "function";
29
+ }
30
+ function asString(v) {
31
+ return typeof v === "string" && v.length > 0 ? v : void 0;
32
+ }
33
+ function createAfsUploadHandler(options) {
34
+ const { provider, resolveCaller, maxBytes = DEFAULT_MAX_BYTES } = options;
35
+ return async function handle(request) {
36
+ if (request.method !== "POST") return fail(405, "Method Not Allowed (use POST)");
37
+ const declaredLen = Number(request.headers.get("content-length") ?? "");
38
+ if (Number.isFinite(declaredLen) && declaredLen > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);
39
+ const contentType = request.headers.get("content-type") ?? "";
40
+ let sessionId;
41
+ let sessionToken;
42
+ let targetDir;
43
+ let conflict;
44
+ const items = [];
45
+ try {
46
+ if (contentType.includes("multipart/form-data")) {
47
+ const form = await request.formData();
48
+ sessionId = asString(form.get("sessionId"));
49
+ sessionToken = asString(form.get("sessionToken"));
50
+ targetDir = asString(form.get("targetDir"));
51
+ conflict = asString(form.get("conflict"));
52
+ const files = form.getAll("file");
53
+ const relPaths = form.getAll("relPath");
54
+ let total = 0;
55
+ for (let i = 0; i < files.length; i++) {
56
+ const f = files[i];
57
+ if (!isBlobLike(f)) continue;
58
+ const bytes = new Uint8Array(await f.arrayBuffer());
59
+ total += bytes.length;
60
+ if (total > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);
61
+ const rel = asString(relPaths[i]);
62
+ const name = asString(f.name) ?? rel ?? `file-${i}`;
63
+ items.push({
64
+ kind: "content",
65
+ name,
66
+ relPath: rel,
67
+ content: bytes,
68
+ contentType: asString(f.type)
69
+ });
70
+ }
71
+ } else if (contentType.includes("application/octet-stream")) {
72
+ sessionId = asString(request.headers.get("x-afs-session"));
73
+ sessionToken = asString(request.headers.get("x-afs-session-token"));
74
+ targetDir = asString(request.headers.get("x-afs-target"));
75
+ conflict = asString(request.headers.get("x-afs-conflict"));
76
+ const name = asString(request.headers.get("x-afs-name")) ?? "upload.bin";
77
+ const relPath = asString(request.headers.get("x-afs-relpath"));
78
+ const bytes = new Uint8Array(await request.arrayBuffer());
79
+ if (bytes.length === 0) return fail(400, "empty body");
80
+ if (bytes.length > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);
81
+ items.push({
82
+ kind: "content",
83
+ name,
84
+ relPath,
85
+ content: bytes,
86
+ contentType: asString(request.headers.get("x-afs-content-type")) ?? "application/octet-stream"
87
+ });
88
+ } else return fail(415, "expected multipart/form-data or application/octet-stream");
89
+ } catch (err) {
90
+ return fail(400, `Invalid upload body: ${errorMessage(err)}`);
91
+ }
92
+ if (!sessionId) return fail(400, "Missing 'sessionId'");
93
+ if (!targetDir) return fail(400, "Missing 'targetDir'");
94
+ if (items.length === 0) return fail(400, "No files in upload");
95
+ const caller = resolveCaller ? await resolveCaller(request) : null;
96
+ try {
97
+ return jsonResponse(200, {
98
+ ok: true,
99
+ result: (await provider.dispatchHttpUpload({
100
+ sessionId,
101
+ sessionToken,
102
+ targetDir,
103
+ items,
104
+ conflict,
105
+ caller
106
+ })).result
107
+ });
108
+ } catch (err) {
109
+ return fail(statusForError(err), errorMessage(err));
110
+ }
111
+ };
112
+ }
113
+
114
+ //#endregion
115
+ export { createAfsUploadHandler };
116
+ //# sourceMappingURL=afs-upload.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"afs-upload.mjs","names":[],"sources":["../../src/http/afs-upload.ts"],"sourcesContent":["/**\n * Blocklet-aware HTTP handler for `POST /api/afs/upload`.\n *\n * Why a dedicated endpoint (vs. shipping bytes over the WS `afs_write`\n * base64 path): file/folder drops can be large and binary. Routing them\n * through the JSON AUP/WS channel would bloat every message ~33% and pin\n * the whole payload in memory as a string. A dedicated multipart endpoint\n * keeps bytes as bytes and lets us cap size cheaply.\n *\n * This module is pure HTTP plumbing — it parses the request, enforces the\n * size cap, resolves the verified caller, and forwards to the provider's\n * `dispatchHttpUpload`. ALL write semantics (conflict resolution, path\n * scope, fail-closed on read-only targets) live in the ingest service in\n * @aigne/afs-ui, which this package must not import (that would create a\n * cycle — afs-ui depends on aos). Same architecture as aup-event.ts.\n *\n * Auth: identical model to `/api/aup/event`. The session must have been\n * created via the WS handshake first; the provider validates `sessionToken`\n * against the session's `resumeToken`. When `resolveCaller` is provided the\n * handler resolves identity server-side (cookie/JWT) — request-supplied\n * caller fields are never trusted.\n *\n * Wire formats:\n * - `multipart/form-data`: fields `sessionId`, `sessionToken?`, `targetDir`,\n * `conflict?`, plus repeated `file` parts with index-aligned `relPath`\n * parts (re-creates folder structure from a folder drop).\n * - `application/octet-stream`: single file; metadata via `x-afs-*` headers.\n */\n\nimport type { CallerInfo } from \"@aigne/afs\";\nimport type { ResolveCallerFn } from \"./auth-context.js\";\n\n/** A single binary item parsed from the request, handed to the dispatcher. */\nexport interface UploadItem {\n kind: \"content\";\n name: string;\n relPath?: string;\n content: Uint8Array;\n contentType?: string;\n}\n\ninterface AfsUploadDispatcher {\n dispatchHttpUpload(args: {\n sessionId: string;\n sessionToken?: string;\n targetDir: string;\n items: UploadItem[];\n conflict?: string;\n caller?: CallerInfo | null;\n }): Promise<{ result: unknown }>;\n}\n\nexport interface AfsUploadHandlerOptions {\n /**\n * The AUP UI provider that owns the session registry + ingest service.\n * Typed loosely so this module doesn't import @aigne/afs-ui (cycle).\n */\n provider: AfsUploadDispatcher;\n /** Resolve verified caller identity from the request (cookie/JWT). */\n resolveCaller?: ResolveCallerFn;\n /** Hard cap on total upload bytes per request. Default 100 MiB. */\n maxBytes?: number;\n}\n\nexport type AfsUploadHandler = (request: Request) => Promise<Response>;\n\nconst JSON_HEADERS = { \"Content-Type\": \"application/json; charset=utf-8\" } as const;\nconst DEFAULT_MAX_BYTES = 100 * 1024 * 1024;\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), { status, headers: { ...JSON_HEADERS } });\n}\n\nfunction fail(status: number, error: string): Response {\n return jsonResponse(status, { ok: false, error });\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/** Same message-pattern mapping as aup-event.ts — error classes aren't exported across packages. */\nfunction statusForError(err: unknown): number {\n const msg = errorMessage(err);\n if (/unknown session/i.test(msg)) return 404;\n if (/invalid session token/i.test(msg)) return 401;\n return 500;\n}\n\n/** Blob/File duck-type — avoid relying on the `File` global across runtimes. */\nfunction isBlobLike(v: unknown): v is Blob {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as { arrayBuffer?: unknown }).arrayBuffer === \"function\"\n );\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\nexport function createAfsUploadHandler(options: AfsUploadHandlerOptions): AfsUploadHandler {\n const { provider, resolveCaller, maxBytes = DEFAULT_MAX_BYTES } = options;\n\n return async function handle(request: Request): Promise<Response> {\n if (request.method !== \"POST\") {\n return fail(405, \"Method Not Allowed (use POST)\");\n }\n\n // Cheap early rejection before buffering anything.\n const declaredLen = Number(request.headers.get(\"content-length\") ?? \"\");\n if (Number.isFinite(declaredLen) && declaredLen > maxBytes) {\n return fail(413, `upload exceeds ${maxBytes} bytes`);\n }\n\n const contentType = request.headers.get(\"content-type\") ?? \"\";\n\n let sessionId: string | undefined;\n let sessionToken: string | undefined;\n let targetDir: string | undefined;\n let conflict: string | undefined;\n const items: UploadItem[] = [];\n\n try {\n if (contentType.includes(\"multipart/form-data\")) {\n const form = await request.formData();\n sessionId = asString(form.get(\"sessionId\"));\n sessionToken = asString(form.get(\"sessionToken\"));\n targetDir = asString(form.get(\"targetDir\"));\n conflict = asString(form.get(\"conflict\"));\n\n const files = form.getAll(\"file\");\n const relPaths = form.getAll(\"relPath\");\n let total = 0;\n for (let i = 0; i < files.length; i++) {\n const f = files[i];\n if (!isBlobLike(f)) continue;\n const bytes = new Uint8Array(await f.arrayBuffer());\n total += bytes.length;\n if (total > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);\n const rel = asString(relPaths[i]);\n const name = asString((f as { name?: string }).name) ?? rel ?? `file-${i}`;\n items.push({\n kind: \"content\",\n name,\n relPath: rel,\n content: bytes,\n contentType: asString((f as { type?: string }).type),\n });\n }\n } else if (contentType.includes(\"application/octet-stream\")) {\n sessionId = asString(request.headers.get(\"x-afs-session\"));\n sessionToken = asString(request.headers.get(\"x-afs-session-token\"));\n targetDir = asString(request.headers.get(\"x-afs-target\"));\n conflict = asString(request.headers.get(\"x-afs-conflict\"));\n const name = asString(request.headers.get(\"x-afs-name\")) ?? \"upload.bin\";\n const relPath = asString(request.headers.get(\"x-afs-relpath\"));\n const bytes = new Uint8Array(await request.arrayBuffer());\n if (bytes.length === 0) return fail(400, \"empty body\");\n if (bytes.length > maxBytes) return fail(413, `upload exceeds ${maxBytes} bytes`);\n items.push({\n kind: \"content\",\n name,\n relPath,\n content: bytes,\n contentType:\n asString(request.headers.get(\"x-afs-content-type\")) ?? \"application/octet-stream\",\n });\n } else {\n return fail(415, \"expected multipart/form-data or application/octet-stream\");\n }\n } catch (err) {\n return fail(400, `Invalid upload body: ${errorMessage(err)}`);\n }\n\n if (!sessionId) return fail(400, \"Missing 'sessionId'\");\n if (!targetDir) return fail(400, \"Missing 'targetDir'\");\n if (items.length === 0) return fail(400, \"No files in upload\");\n\n const caller: CallerInfo | null = resolveCaller ? await resolveCaller(request) : null;\n\n try {\n const out = await provider.dispatchHttpUpload({\n sessionId,\n sessionToken,\n targetDir,\n items,\n conflict,\n caller,\n });\n return jsonResponse(200, { ok: true, result: out.result });\n } catch (err) {\n return fail(statusForError(err), errorMessage(err));\n }\n };\n}\n"],"mappings":";AAkEA,MAAM,eAAe,EAAE,gBAAgB,mCAAmC;AAC1E,MAAM,oBAAoB,MAAM,OAAO;AAEvC,SAAS,aAAa,QAAgB,MAAyB;AAC7D,QAAO,IAAI,SAAS,KAAK,UAAU,KAAK,EAAE;EAAE;EAAQ,SAAS,EAAE,GAAG,cAAc;EAAE,CAAC;;AAGrF,SAAS,KAAK,QAAgB,OAAyB;AACrD,QAAO,aAAa,QAAQ;EAAE,IAAI;EAAO;EAAO,CAAC;;AAGnD,SAAS,aAAa,KAAsB;AAC1C,QAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;;;AAIzD,SAAS,eAAe,KAAsB;CAC5C,MAAM,MAAM,aAAa,IAAI;AAC7B,KAAI,mBAAmB,KAAK,IAAI,CAAE,QAAO;AACzC,KAAI,yBAAyB,KAAK,IAAI,CAAE,QAAO;AAC/C,QAAO;;;AAIT,SAAS,WAAW,GAAuB;AACzC,QACE,OAAO,MAAM,YACb,MAAM,QACN,OAAQ,EAAgC,gBAAgB;;AAI5D,SAAS,SAAS,GAAgC;AAChD,QAAO,OAAO,MAAM,YAAY,EAAE,SAAS,IAAI,IAAI;;AAGrD,SAAgB,uBAAuB,SAAoD;CACzF,MAAM,EAAE,UAAU,eAAe,WAAW,sBAAsB;AAElE,QAAO,eAAe,OAAO,SAAqC;AAChE,MAAI,QAAQ,WAAW,OACrB,QAAO,KAAK,KAAK,gCAAgC;EAInD,MAAM,cAAc,OAAO,QAAQ,QAAQ,IAAI,iBAAiB,IAAI,GAAG;AACvE,MAAI,OAAO,SAAS,YAAY,IAAI,cAAc,SAChD,QAAO,KAAK,KAAK,kBAAkB,SAAS,QAAQ;EAGtD,MAAM,cAAc,QAAQ,QAAQ,IAAI,eAAe,IAAI;EAE3D,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,MAAM,QAAsB,EAAE;AAE9B,MAAI;AACF,OAAI,YAAY,SAAS,sBAAsB,EAAE;IAC/C,MAAM,OAAO,MAAM,QAAQ,UAAU;AACrC,gBAAY,SAAS,KAAK,IAAI,YAAY,CAAC;AAC3C,mBAAe,SAAS,KAAK,IAAI,eAAe,CAAC;AACjD,gBAAY,SAAS,KAAK,IAAI,YAAY,CAAC;AAC3C,eAAW,SAAS,KAAK,IAAI,WAAW,CAAC;IAEzC,MAAM,QAAQ,KAAK,OAAO,OAAO;IACjC,MAAM,WAAW,KAAK,OAAO,UAAU;IACvC,IAAI,QAAQ;AACZ,SAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;KACrC,MAAM,IAAI,MAAM;AAChB,SAAI,CAAC,WAAW,EAAE,CAAE;KACpB,MAAM,QAAQ,IAAI,WAAW,MAAM,EAAE,aAAa,CAAC;AACnD,cAAS,MAAM;AACf,SAAI,QAAQ,SAAU,QAAO,KAAK,KAAK,kBAAkB,SAAS,QAAQ;KAC1E,MAAM,MAAM,SAAS,SAAS,GAAG;KACjC,MAAM,OAAO,SAAU,EAAwB,KAAK,IAAI,OAAO,QAAQ;AACvE,WAAM,KAAK;MACT,MAAM;MACN;MACA,SAAS;MACT,SAAS;MACT,aAAa,SAAU,EAAwB,KAAK;MACrD,CAAC;;cAEK,YAAY,SAAS,2BAA2B,EAAE;AAC3D,gBAAY,SAAS,QAAQ,QAAQ,IAAI,gBAAgB,CAAC;AAC1D,mBAAe,SAAS,QAAQ,QAAQ,IAAI,sBAAsB,CAAC;AACnE,gBAAY,SAAS,QAAQ,QAAQ,IAAI,eAAe,CAAC;AACzD,eAAW,SAAS,QAAQ,QAAQ,IAAI,iBAAiB,CAAC;IAC1D,MAAM,OAAO,SAAS,QAAQ,QAAQ,IAAI,aAAa,CAAC,IAAI;IAC5D,MAAM,UAAU,SAAS,QAAQ,QAAQ,IAAI,gBAAgB,CAAC;IAC9D,MAAM,QAAQ,IAAI,WAAW,MAAM,QAAQ,aAAa,CAAC;AACzD,QAAI,MAAM,WAAW,EAAG,QAAO,KAAK,KAAK,aAAa;AACtD,QAAI,MAAM,SAAS,SAAU,QAAO,KAAK,KAAK,kBAAkB,SAAS,QAAQ;AACjF,UAAM,KAAK;KACT,MAAM;KACN;KACA;KACA,SAAS;KACT,aACE,SAAS,QAAQ,QAAQ,IAAI,qBAAqB,CAAC,IAAI;KAC1D,CAAC;SAEF,QAAO,KAAK,KAAK,2DAA2D;WAEvE,KAAK;AACZ,UAAO,KAAK,KAAK,wBAAwB,aAAa,IAAI,GAAG;;AAG/D,MAAI,CAAC,UAAW,QAAO,KAAK,KAAK,sBAAsB;AACvD,MAAI,CAAC,UAAW,QAAO,KAAK,KAAK,sBAAsB;AACvD,MAAI,MAAM,WAAW,EAAG,QAAO,KAAK,KAAK,qBAAqB;EAE9D,MAAM,SAA4B,gBAAgB,MAAM,cAAc,QAAQ,GAAG;AAEjF,MAAI;AASF,UAAO,aAAa,KAAK;IAAE,IAAI;IAAM,SARzB,MAAM,SAAS,mBAAmB;KAC5C;KACA;KACA;KACA;KACA;KACA;KACD,CAAC,EAC+C;IAAQ,CAAC;WACnD,KAAK;AACZ,UAAO,KAAK,eAAe,IAAI,EAAE,aAAa,IAAI,CAAC"}
@@ -0,0 +1,60 @@
1
+
2
+ //#region src/http/aup-client.ts
3
+ /**
4
+ * AUP client HTML response factory.
5
+ *
6
+ * Wraps AUP client HTML (from @aigne/afs-ui or equivalent) into HTTP responses.
7
+ * The actual HTML is injected by the runtime — AOS provides the response wrapper.
8
+ */
9
+ const HTML_HEADERS = {
10
+ "Content-Type": "text/html; charset=utf-8",
11
+ "Cache-Control": "no-cache",
12
+ "X-Content-Type-Options": "nosniff"
13
+ };
14
+ /**
15
+ * Create a function that returns an AUP client HTML Response.
16
+ *
17
+ * @param getHtml - Function returning the AUP client HTML string.
18
+ * Called lazily so the HTML can be built/loaded once and cached.
19
+ */
20
+ function createAupClientResponder(getHtml) {
21
+ return async (_blockletName) => {
22
+ const html = getHtml();
23
+ return new Response(html, {
24
+ status: 200,
25
+ headers: { ...HTML_HEADERS }
26
+ });
27
+ };
28
+ }
29
+ /**
30
+ * Minimal fallback AUP client HTML for environments without @aigne/afs-ui.
31
+ * Shows a basic page that connects via WebSocket to the AFS service.
32
+ */
33
+ const MINIMAL_AUP_HTML = `<!DOCTYPE html>
34
+ <html lang="en">
35
+ <head>
36
+ <meta charset="UTF-8">
37
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
38
+ <title>AFS</title>
39
+ </head>
40
+ <body>
41
+ <div id="root">Loading...</div>
42
+ <script>
43
+ (function() {
44
+ var ws = new WebSocket(location.origin.replace(/^http/, 'ws'));
45
+ ws.onmessage = function(e) {
46
+ try {
47
+ var msg = JSON.parse(e.data);
48
+ if (msg.type === 'aup_render') {
49
+ document.getElementById('root').innerHTML = '<pre>' + JSON.stringify(msg, null, 2) + '</pre>';
50
+ }
51
+ } catch(err) { console.error(err); }
52
+ };
53
+ })();
54
+ <\/script>
55
+ </body>
56
+ </html>`;
57
+
58
+ //#endregion
59
+ exports.MINIMAL_AUP_HTML = MINIMAL_AUP_HTML;
60
+ exports.createAupClientResponder = createAupClientResponder;
@@ -0,0 +1,22 @@
1
+ //#region src/http/aup-client.d.ts
2
+ /**
3
+ * AUP client HTML response factory.
4
+ *
5
+ * Wraps AUP client HTML (from @aigne/afs-ui or equivalent) into HTTP responses.
6
+ * The actual HTML is injected by the runtime — AOS provides the response wrapper.
7
+ */
8
+ /**
9
+ * Create a function that returns an AUP client HTML Response.
10
+ *
11
+ * @param getHtml - Function returning the AUP client HTML string.
12
+ * Called lazily so the HTML can be built/loaded once and cached.
13
+ */
14
+ declare function createAupClientResponder(getHtml: () => string): (blockletName?: string) => Promise<Response>;
15
+ /**
16
+ * Minimal fallback AUP client HTML for environments without @aigne/afs-ui.
17
+ * Shows a basic page that connects via WebSocket to the AFS service.
18
+ */
19
+ declare const MINIMAL_AUP_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>AFS</title>\n</head>\n<body>\n<div id=\"root\">Loading...</div>\n<script>\n(function() {\n var ws = new WebSocket(location.origin.replace(/^http/, 'ws'));\n ws.onmessage = function(e) {\n try {\n var msg = JSON.parse(e.data);\n if (msg.type === 'aup_render') {\n document.getElementById('root').innerHTML = '<pre>' + JSON.stringify(msg, null, 2) + '</pre>';\n }\n } catch(err) { console.error(err); }\n };\n})();\n</script>\n</body>\n</html>";
20
+ //#endregion
21
+ export { MINIMAL_AUP_HTML, createAupClientResponder };
22
+ //# sourceMappingURL=aup-client.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aup-client.d.cts","names":[],"sources":["../../src/http/aup-client.ts"],"mappings":";;AAmBA;;;;;;;;;;AAgBA;iBAhBgB,wBAAA,CACd,OAAA,kBACE,YAAA,cAA0B,OAAA,CAAQ,QAAA;;;;;cAczB,gBAAA"}
@@ -0,0 +1,22 @@
1
+ //#region src/http/aup-client.d.ts
2
+ /**
3
+ * AUP client HTML response factory.
4
+ *
5
+ * Wraps AUP client HTML (from @aigne/afs-ui or equivalent) into HTTP responses.
6
+ * The actual HTML is injected by the runtime — AOS provides the response wrapper.
7
+ */
8
+ /**
9
+ * Create a function that returns an AUP client HTML Response.
10
+ *
11
+ * @param getHtml - Function returning the AUP client HTML string.
12
+ * Called lazily so the HTML can be built/loaded once and cached.
13
+ */
14
+ declare function createAupClientResponder(getHtml: () => string): (blockletName?: string) => Promise<Response>;
15
+ /**
16
+ * Minimal fallback AUP client HTML for environments without @aigne/afs-ui.
17
+ * Shows a basic page that connects via WebSocket to the AFS service.
18
+ */
19
+ declare const MINIMAL_AUP_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>AFS</title>\n</head>\n<body>\n<div id=\"root\">Loading...</div>\n<script>\n(function() {\n var ws = new WebSocket(location.origin.replace(/^http/, 'ws'));\n ws.onmessage = function(e) {\n try {\n var msg = JSON.parse(e.data);\n if (msg.type === 'aup_render') {\n document.getElementById('root').innerHTML = '<pre>' + JSON.stringify(msg, null, 2) + '</pre>';\n }\n } catch(err) { console.error(err); }\n };\n})();\n</script>\n</body>\n</html>";
20
+ //#endregion
21
+ export { MINIMAL_AUP_HTML, createAupClientResponder };
22
+ //# sourceMappingURL=aup-client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aup-client.d.mts","names":[],"sources":["../../src/http/aup-client.ts"],"mappings":";;AAmBA;;;;;;;;;;AAgBA;iBAhBgB,wBAAA,CACd,OAAA,kBACE,YAAA,cAA0B,OAAA,CAAQ,QAAA;;;;;cAczB,gBAAA"}