@treenity/core 1.0.48 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (696) hide show
  1. package/dist/chain.d.ts +20 -0
  2. package/dist/chain.d.ts.map +1 -0
  3. package/dist/chain.js +59 -0
  4. package/dist/chain.js.map +1 -0
  5. package/dist/client/handle.d.ts +13 -0
  6. package/dist/client/handle.d.ts.map +1 -0
  7. package/dist/client/handle.js +46 -0
  8. package/dist/client/handle.js.map +1 -0
  9. package/dist/client/index.d.ts +21 -0
  10. package/dist/client/index.d.ts.map +1 -0
  11. package/dist/client/index.js +5 -0
  12. package/dist/client/index.js.map +1 -0
  13. package/dist/client/trpc.d.ts +13 -0
  14. package/dist/client/trpc.d.ts.map +1 -0
  15. package/dist/client/trpc.js +78 -0
  16. package/dist/client/trpc.js.map +1 -0
  17. package/dist/client.d.ts +2 -0
  18. package/dist/client.d.ts.map +1 -0
  19. package/dist/client.js +2 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/comp/handle.d.ts +14 -0
  22. package/dist/comp/handle.d.ts.map +1 -0
  23. package/dist/comp/handle.js +21 -0
  24. package/dist/comp/handle.js.map +1 -0
  25. package/dist/comp/index.d.ts +42 -0
  26. package/dist/comp/index.d.ts.map +1 -0
  27. package/dist/comp/index.js +126 -0
  28. package/dist/comp/index.js.map +1 -0
  29. package/dist/comp/needs.d.ts +26 -0
  30. package/dist/comp/needs.d.ts.map +1 -0
  31. package/dist/comp/needs.js +101 -0
  32. package/dist/comp/needs.js.map +1 -0
  33. package/dist/comp/validate.d.ts +13 -0
  34. package/dist/comp/validate.d.ts.map +1 -0
  35. package/dist/comp/validate.js +117 -0
  36. package/dist/comp/validate.js.map +1 -0
  37. package/dist/comp.d.ts +2 -0
  38. package/dist/comp.d.ts.map +1 -0
  39. package/dist/comp.js +2 -0
  40. package/dist/comp.js.map +1 -0
  41. package/dist/contexts/schema/index.d.ts +7 -0
  42. package/dist/contexts/schema/index.d.ts.map +1 -0
  43. package/dist/contexts/schema/index.js +2 -0
  44. package/dist/contexts/schema/index.js.map +1 -0
  45. package/dist/contexts/schema.d.ts +2 -0
  46. package/dist/contexts/schema.d.ts.map +1 -0
  47. package/dist/contexts/schema.js +2 -0
  48. package/dist/contexts/schema.js.map +1 -0
  49. package/dist/contexts/service/index.d.ts +31 -0
  50. package/dist/contexts/service/index.d.ts.map +1 -0
  51. package/dist/contexts/service/index.js +16 -0
  52. package/dist/contexts/service/index.js.map +1 -0
  53. package/dist/contexts/service.d.ts +1 -14
  54. package/dist/contexts/service.d.ts.map +1 -1
  55. package/dist/contexts/service.js +2 -0
  56. package/dist/contexts/service.js.map +1 -0
  57. package/dist/contexts/telegram/index.d.ts +19 -0
  58. package/dist/contexts/telegram/index.d.ts.map +1 -0
  59. package/dist/contexts/telegram/index.js +89 -0
  60. package/dist/contexts/telegram/index.js.map +1 -0
  61. package/dist/contexts/text/index.d.ts +7 -0
  62. package/dist/contexts/text/index.d.ts.map +1 -0
  63. package/dist/contexts/text/index.js +9 -0
  64. package/dist/contexts/text/index.js.map +1 -0
  65. package/dist/contexts/text.d.ts +2 -0
  66. package/dist/contexts/text.d.ts.map +1 -0
  67. package/dist/contexts/text.js +2 -0
  68. package/dist/contexts/text.js.map +1 -0
  69. package/dist/core/component.d.ts +41 -0
  70. package/dist/core/component.d.ts.map +1 -0
  71. package/dist/core/component.js +105 -0
  72. package/dist/core/component.js.map +1 -0
  73. package/dist/core/context.d.ts +5 -0
  74. package/dist/core/context.d.ts.map +1 -0
  75. package/dist/core/context.js +3 -0
  76. package/dist/core/context.js.map +1 -0
  77. package/dist/core/index.d.ts +5 -0
  78. package/dist/core/index.d.ts.map +1 -0
  79. package/dist/core/index.js +8 -0
  80. package/dist/core/index.js.map +1 -0
  81. package/dist/core/path.d.ts +7 -0
  82. package/dist/core/path.d.ts.map +1 -0
  83. package/dist/core/path.js +37 -0
  84. package/dist/core/path.js.map +1 -0
  85. package/dist/core/registry.d.ts +16 -0
  86. package/dist/core/registry.d.ts.map +1 -0
  87. package/dist/core/registry.js +100 -0
  88. package/dist/core/registry.js.map +1 -0
  89. package/dist/core.d.ts +2 -0
  90. package/dist/core.d.ts.map +1 -0
  91. package/dist/core.js +2 -0
  92. package/dist/core.js.map +1 -0
  93. package/dist/log.d.ts +8 -0
  94. package/dist/log.d.ts.map +1 -0
  95. package/dist/log.js +31 -0
  96. package/dist/log.js.map +1 -0
  97. package/dist/mod/discover.d.ts +4 -0
  98. package/dist/mod/discover.d.ts.map +1 -0
  99. package/dist/mod/discover.js +103 -0
  100. package/dist/mod/discover.js.map +1 -0
  101. package/dist/mod/examples/ticker/seed.d.ts +3 -0
  102. package/dist/mod/examples/ticker/seed.d.ts.map +1 -0
  103. package/dist/mod/examples/ticker/seed.js +16 -0
  104. package/dist/mod/examples/ticker/seed.js.map +1 -0
  105. package/dist/mod/examples/ticker/service.d.ts +2 -0
  106. package/dist/mod/examples/ticker/service.d.ts.map +1 -0
  107. package/dist/mod/examples/ticker/service.js +18 -0
  108. package/dist/mod/examples/ticker/service.js.map +1 -0
  109. package/dist/mod/examples/ticker/types.d.ts +16 -0
  110. package/dist/mod/examples/ticker/types.d.ts.map +1 -0
  111. package/dist/mod/examples/ticker/types.js +20 -0
  112. package/dist/mod/examples/ticker/types.js.map +1 -0
  113. package/dist/mod/examples/ticker/view.d.ts +2 -0
  114. package/dist/mod/examples/ticker/view.d.ts.map +1 -0
  115. package/dist/mod/examples/ticker/view.js +10 -0
  116. package/dist/mod/examples/ticker/view.js.map +1 -0
  117. package/dist/mod/index.d.ts +11 -0
  118. package/dist/mod/index.d.ts.map +1 -0
  119. package/dist/mod/index.js +8 -0
  120. package/dist/mod/index.js.map +1 -0
  121. package/dist/mod/loader.d.ts +18 -0
  122. package/dist/mod/loader.d.ts.map +1 -0
  123. package/dist/mod/loader.js +154 -0
  124. package/dist/mod/loader.js.map +1 -0
  125. package/dist/mod/optimistic.d.ts +34 -0
  126. package/dist/mod/optimistic.d.ts.map +1 -0
  127. package/dist/mod/optimistic.js +176 -0
  128. package/dist/mod/optimistic.js.map +1 -0
  129. package/dist/mod/prefab.d.ts +18 -0
  130. package/dist/mod/prefab.d.ts.map +1 -0
  131. package/dist/mod/prefab.js +42 -0
  132. package/dist/mod/prefab.js.map +1 -0
  133. package/dist/mod/tracking.d.ts +8 -0
  134. package/dist/mod/tracking.d.ts.map +1 -0
  135. package/dist/mod/tracking.js +44 -0
  136. package/dist/mod/tracking.js.map +1 -0
  137. package/dist/mod/types.d.ts +31 -0
  138. package/dist/mod/types.d.ts.map +1 -0
  139. package/dist/mod/types.js +5 -0
  140. package/dist/mod/types.js.map +1 -0
  141. package/dist/mod.d.ts +2 -0
  142. package/dist/mod.d.ts.map +1 -0
  143. package/dist/mod.js +2 -0
  144. package/dist/mod.js.map +1 -0
  145. package/dist/mods/autostart/server.d.ts +2 -0
  146. package/dist/mods/autostart/server.d.ts.map +1 -0
  147. package/dist/mods/autostart/server.js +2 -0
  148. package/dist/mods/autostart/server.js.map +1 -0
  149. package/dist/mods/autostart/service.d.ts +14 -0
  150. package/dist/mods/autostart/service.d.ts.map +1 -0
  151. package/dist/mods/autostart/service.js +88 -0
  152. package/dist/mods/autostart/service.js.map +1 -0
  153. package/dist/mods/llm/index.d.ts +13 -0
  154. package/dist/mods/llm/index.d.ts.map +1 -0
  155. package/dist/mods/llm/index.js +43 -0
  156. package/dist/mods/llm/index.js.map +1 -0
  157. package/dist/mods/llm/server.d.ts +2 -0
  158. package/dist/mods/llm/server.d.ts.map +1 -0
  159. package/dist/mods/llm/server.js +2 -0
  160. package/dist/mods/llm/server.js.map +1 -0
  161. package/dist/mods/mcp/server.d.ts +3 -0
  162. package/dist/mods/mcp/server.d.ts.map +1 -0
  163. package/dist/mods/mcp/server.js +3 -0
  164. package/dist/mods/mcp/server.js.map +1 -0
  165. package/dist/mods/mcp/service.d.ts +2 -0
  166. package/dist/mods/mcp/service.d.ts.map +1 -0
  167. package/dist/mods/mcp/service.js +17 -0
  168. package/dist/mods/mcp/service.js.map +1 -0
  169. package/dist/mods/mcp/types.d.ts +5 -0
  170. package/dist/mods/mcp/types.d.ts.map +1 -0
  171. package/dist/mods/mcp/types.js +7 -0
  172. package/dist/mods/mcp/types.js.map +1 -0
  173. package/dist/mods/treenity/agent-port.d.ts +2 -0
  174. package/dist/mods/treenity/agent-port.d.ts.map +1 -0
  175. package/dist/mods/treenity/agent-port.js +76 -0
  176. package/dist/mods/treenity/agent-port.js.map +1 -0
  177. package/dist/mods/treenity/groups.d.ts +2 -0
  178. package/dist/mods/treenity/groups.d.ts.map +1 -0
  179. package/dist/mods/treenity/groups.js +18 -0
  180. package/dist/mods/treenity/groups.js.map +1 -0
  181. package/dist/mods/treenity/mod-type.d.ts +2 -0
  182. package/dist/mods/treenity/mod-type.d.ts.map +1 -0
  183. package/dist/mods/treenity/mod-type.js +9 -0
  184. package/dist/mods/treenity/mod-type.js.map +1 -0
  185. package/dist/mods/treenity/seed.d.ts +2 -0
  186. package/dist/mods/treenity/seed.d.ts.map +1 -0
  187. package/dist/mods/treenity/seed.js +53 -0
  188. package/dist/mods/treenity/seed.js.map +1 -0
  189. package/dist/mods/treenity/server.d.ts +5 -0
  190. package/dist/mods/treenity/server.d.ts.map +1 -0
  191. package/dist/mods/treenity/server.js +5 -0
  192. package/dist/mods/treenity/server.js.map +1 -0
  193. package/dist/mods/treenity/system.d.ts +36 -0
  194. package/dist/mods/treenity/system.d.ts.map +1 -0
  195. package/dist/mods/treenity/system.js +61 -0
  196. package/dist/mods/treenity/system.js.map +1 -0
  197. package/dist/mods/uix/client.d.ts +3 -0
  198. package/dist/mods/uix/client.d.ts.map +1 -0
  199. package/dist/mods/uix/client.js +96 -0
  200. package/dist/mods/uix/client.js.map +1 -0
  201. package/dist/mods/uix/compile.d.ts +7 -0
  202. package/dist/mods/uix/compile.d.ts.map +1 -0
  203. package/dist/mods/uix/compile.js +94 -0
  204. package/dist/mods/uix/compile.js.map +1 -0
  205. package/dist/mods/uix/jsx-parser.d.ts +2 -0
  206. package/dist/mods/uix/jsx-parser.d.ts.map +1 -0
  207. package/dist/mods/uix/jsx-parser.js +565 -0
  208. package/dist/mods/uix/jsx-parser.js.map +1 -0
  209. package/dist/mods/uix/verify.d.ts +7 -0
  210. package/dist/mods/uix/verify.d.ts.map +1 -0
  211. package/dist/mods/uix/verify.js +59 -0
  212. package/dist/mods/uix/verify.js.map +1 -0
  213. package/dist/schema/catalog.d.ts +23 -0
  214. package/dist/schema/catalog.d.ts.map +1 -0
  215. package/dist/schema/catalog.js +82 -0
  216. package/dist/schema/catalog.js.map +1 -0
  217. package/dist/schema/extract-schemas.d.ts +5 -0
  218. package/dist/schema/extract-schemas.d.ts.map +1 -0
  219. package/dist/schema/extract-schemas.js +444 -0
  220. package/dist/schema/extract-schemas.js.map +1 -0
  221. package/dist/schema/load.d.ts +2 -0
  222. package/dist/schema/load.d.ts.map +1 -0
  223. package/dist/schema/load.js +23 -0
  224. package/dist/schema/load.js.map +1 -0
  225. package/dist/schema/test-fixture.types.d.ts +2 -0
  226. package/dist/schema/test-fixture.types.d.ts.map +1 -0
  227. package/dist/schema/test-fixture.types.js +19 -0
  228. package/dist/schema/test-fixture.types.js.map +1 -0
  229. package/dist/schema/test-opaque.d.ts +3 -0
  230. package/dist/schema/test-opaque.d.ts.map +1 -0
  231. package/dist/schema/test-opaque.js +43 -0
  232. package/dist/schema/test-opaque.js.map +1 -0
  233. package/dist/schema/types.d.ts +37 -0
  234. package/dist/schema/types.d.ts.map +1 -0
  235. package/dist/schema/types.js +3 -0
  236. package/dist/schema/types.js.map +1 -0
  237. package/dist/server/actions.d.ts +35 -0
  238. package/dist/server/actions.d.ts.map +1 -0
  239. package/dist/server/actions.js +170 -0
  240. package/dist/server/actions.js.map +1 -0
  241. package/dist/server/agent.d.ts +6 -0
  242. package/dist/server/agent.d.ts.map +1 -0
  243. package/dist/server/agent.js +15 -0
  244. package/dist/server/agent.js.map +1 -0
  245. package/dist/server/auth.d.ts +39 -0
  246. package/dist/server/auth.d.ts.map +1 -0
  247. package/dist/server/auth.js +335 -0
  248. package/dist/server/auth.js.map +1 -0
  249. package/dist/server/client.d.ts +183 -0
  250. package/dist/server/client.d.ts.map +1 -0
  251. package/dist/server/client.js +22 -0
  252. package/dist/server/client.js.map +1 -0
  253. package/dist/server/cookies.d.ts +5 -0
  254. package/dist/server/cookies.d.ts.map +1 -0
  255. package/dist/server/cookies.js +24 -0
  256. package/dist/server/cookies.js.map +1 -0
  257. package/dist/server/doc-index.d.ts +38 -0
  258. package/dist/server/doc-index.d.ts.map +1 -0
  259. package/dist/server/doc-index.js +244 -0
  260. package/dist/server/doc-index.js.map +1 -0
  261. package/dist/server/errors.d.ts +7 -0
  262. package/dist/server/errors.d.ts.map +1 -0
  263. package/dist/server/errors.js +11 -0
  264. package/dist/server/errors.js.map +1 -0
  265. package/dist/server/factory.d.ts +23 -0
  266. package/dist/server/factory.d.ts.map +1 -0
  267. package/dist/server/factory.js +70 -0
  268. package/dist/server/factory.js.map +1 -0
  269. package/dist/server/main.d.ts +6 -0
  270. package/dist/server/main.d.ts.map +1 -0
  271. package/dist/server/main.js +45 -0
  272. package/dist/server/main.js.map +1 -0
  273. package/dist/server/mcp.d.ts +5 -0
  274. package/dist/server/mcp.d.ts.map +1 -0
  275. package/dist/server/mcp.js +280 -0
  276. package/dist/server/mcp.js.map +1 -0
  277. package/dist/server/migrate.d.ts +3 -0
  278. package/dist/server/migrate.d.ts.map +1 -0
  279. package/dist/server/migrate.js +56 -0
  280. package/dist/server/migrate.js.map +1 -0
  281. package/dist/server/mod-catalog.d.ts +14 -0
  282. package/dist/server/mod-catalog.d.ts.map +1 -0
  283. package/dist/server/mod-catalog.js +40 -0
  284. package/dist/server/mod-catalog.js.map +1 -0
  285. package/dist/server/mods-mount.d.ts +4 -0
  286. package/dist/server/mods-mount.d.ts.map +1 -0
  287. package/dist/server/mods-mount.js +169 -0
  288. package/dist/server/mods-mount.js.map +1 -0
  289. package/dist/server/mount-adapters.d.ts +9 -0
  290. package/dist/server/mount-adapters.d.ts.map +1 -0
  291. package/dist/server/mount-adapters.js +75 -0
  292. package/dist/server/mount-adapters.js.map +1 -0
  293. package/dist/server/mount.d.ts +3 -0
  294. package/dist/server/mount.d.ts.map +1 -0
  295. package/dist/server/mount.js +195 -0
  296. package/dist/server/mount.js.map +1 -0
  297. package/dist/server/prefab.d.ts +18 -0
  298. package/dist/server/prefab.d.ts.map +1 -0
  299. package/dist/server/prefab.js +70 -0
  300. package/dist/server/prefab.js.map +1 -0
  301. package/dist/server/seed/index.d.ts +6 -0
  302. package/dist/server/seed/index.d.ts.map +1 -0
  303. package/dist/server/seed/index.js +16 -0
  304. package/dist/server/seed/index.js.map +1 -0
  305. package/dist/server/server.d.ts +28 -0
  306. package/dist/server/server.d.ts.map +1 -0
  307. package/dist/server/server.js +117 -0
  308. package/dist/server/server.js.map +1 -0
  309. package/dist/server/sub.d.ts +34 -0
  310. package/dist/server/sub.d.ts.map +1 -0
  311. package/dist/server/sub.js +174 -0
  312. package/dist/server/sub.js.map +1 -0
  313. package/dist/server/trpc.d.ts +192 -0
  314. package/dist/server/trpc.d.ts.map +1 -0
  315. package/dist/server/trpc.js +319 -0
  316. package/dist/server/trpc.js.map +1 -0
  317. package/dist/server/types-mount.d.ts +3 -0
  318. package/dist/server/types-mount.d.ts.map +1 -0
  319. package/dist/server/types-mount.js +144 -0
  320. package/dist/server/types-mount.js.map +1 -0
  321. package/dist/server/validate.d.ts +3 -0
  322. package/dist/server/validate.d.ts.map +1 -0
  323. package/dist/server/validate.js +20 -0
  324. package/dist/server/validate.js.map +1 -0
  325. package/dist/server/volatile.d.ts +11 -0
  326. package/dist/server/volatile.d.ts.map +1 -0
  327. package/dist/server/volatile.js +26 -0
  328. package/dist/server/volatile.js.map +1 -0
  329. package/dist/server/watch.d.ts +23 -0
  330. package/dist/server/watch.d.ts.map +1 -0
  331. package/dist/server/watch.js +178 -0
  332. package/dist/server/watch.js.map +1 -0
  333. package/dist/tree/cache.d.ts +3 -0
  334. package/dist/tree/cache.d.ts.map +1 -0
  335. package/dist/tree/cache.js +48 -0
  336. package/dist/tree/cache.js.map +1 -0
  337. package/dist/tree/fs.d.ts +3 -0
  338. package/dist/tree/fs.d.ts.map +1 -0
  339. package/dist/tree/fs.js +274 -0
  340. package/dist/tree/fs.js.map +1 -0
  341. package/dist/tree/index.d.ts +38 -0
  342. package/dist/tree/index.d.ts.map +1 -0
  343. package/dist/tree/index.js +182 -0
  344. package/dist/tree/index.js.map +1 -0
  345. package/dist/tree/inflight.d.ts +2 -0
  346. package/dist/tree/inflight.d.ts.map +1 -0
  347. package/dist/tree/inflight.js +15 -0
  348. package/dist/tree/inflight.js.map +1 -0
  349. package/dist/tree/json-codec.d.ts +2 -0
  350. package/dist/tree/json-codec.d.ts.map +1 -0
  351. package/dist/tree/json-codec.js +13 -0
  352. package/dist/tree/json-codec.js.map +1 -0
  353. package/dist/tree/mimefs.d.ts +13 -0
  354. package/dist/tree/mimefs.d.ts.map +1 -0
  355. package/dist/tree/mimefs.js +124 -0
  356. package/dist/tree/mimefs.js.map +1 -0
  357. package/dist/tree/mongo.d.ts +5 -0
  358. package/dist/tree/mongo.d.ts.map +1 -0
  359. package/dist/tree/mongo.js +110 -0
  360. package/dist/tree/mongo.js.map +1 -0
  361. package/dist/tree/patch.d.ts +30 -0
  362. package/dist/tree/patch.d.ts.map +1 -0
  363. package/dist/tree/patch.js +112 -0
  364. package/dist/tree/patch.js.map +1 -0
  365. package/dist/tree/query.d.ts +12 -0
  366. package/dist/tree/query.d.ts.map +1 -0
  367. package/dist/tree/query.js +61 -0
  368. package/dist/tree/query.js.map +1 -0
  369. package/dist/tree/repath.d.ts +3 -0
  370. package/dist/tree/repath.d.ts.map +1 -0
  371. package/dist/tree/repath.js +38 -0
  372. package/dist/tree/repath.js.map +1 -0
  373. package/dist/tree-chain.d.ts +18 -0
  374. package/dist/tree-chain.d.ts.map +1 -0
  375. package/dist/tree-chain.js +109 -0
  376. package/dist/tree-chain.js.map +1 -0
  377. package/dist/tree.d.ts +2 -0
  378. package/dist/tree.d.ts.map +1 -0
  379. package/dist/tree.js +2 -0
  380. package/dist/tree.js.map +1 -0
  381. package/dist/uri.d.ts +11 -0
  382. package/dist/uri.d.ts.map +1 -0
  383. package/dist/uri.js +79 -0
  384. package/dist/uri.js.map +1 -0
  385. package/package.json +39 -40
  386. package/src/chain.test.ts +190 -0
  387. package/src/chain.ts +82 -0
  388. package/src/client/client.test.ts +192 -0
  389. package/src/client/handle.ts +53 -0
  390. package/src/client/index.ts +18 -0
  391. package/src/client/trpc.ts +91 -0
  392. package/src/client.ts +1 -0
  393. package/src/comp/CLAUDE.md +14 -0
  394. package/src/comp/handle.ts +36 -0
  395. package/src/comp/index.test.ts +129 -0
  396. package/src/comp/index.ts +175 -0
  397. package/src/comp/needs.test.ts +499 -0
  398. package/src/comp/needs.ts +113 -0
  399. package/src/comp/validate.test.ts +304 -0
  400. package/src/comp/validate.ts +125 -0
  401. package/src/comp.ts +1 -0
  402. package/src/contexts/schema/index.ts +7 -0
  403. package/src/contexts/schema.ts +1 -0
  404. package/src/contexts/service/index.test.ts +323 -0
  405. package/src/contexts/service/index.ts +43 -0
  406. package/src/contexts/service.ts +1 -0
  407. package/src/contexts/telegram/index.ts +115 -0
  408. package/src/contexts/text/index.test.ts +31 -0
  409. package/src/contexts/text/index.ts +18 -0
  410. package/src/contexts/text.ts +1 -0
  411. package/src/core/component.ts +151 -0
  412. package/src/core/context.ts +14 -0
  413. package/src/core/index.test.ts +203 -0
  414. package/src/core/index.ts +9 -0
  415. package/src/core/path.ts +35 -0
  416. package/src/core/registry.ts +115 -0
  417. package/src/core.ts +1 -0
  418. package/src/log.test.ts +70 -0
  419. package/src/log.ts +28 -0
  420. package/src/mod/discover.test.ts +133 -0
  421. package/src/mod/discover.ts +100 -0
  422. package/src/mod/docs/00-index.md +19 -0
  423. package/src/mod/docs/01-primitives.md +38 -0
  424. package/src/mod/docs/02-core-api.md +68 -0
  425. package/src/mod/docs/03-registry.md +30 -0
  426. package/src/mod/docs/04-store.md +62 -0
  427. package/src/mod/docs/05-comp.md +111 -0
  428. package/src/mod/docs/06-actions.md +193 -0
  429. package/src/mod/docs/07-realtime.md +100 -0
  430. package/src/mod/docs/08-services.md +33 -0
  431. package/src/mod/docs/09-mounts.md +43 -0
  432. package/src/mod/docs/10-acl.md +60 -0
  433. package/src/mod/docs/11-server.md +62 -0
  434. package/src/mod/docs/12-conventions.md +65 -0
  435. package/src/mod/docs/13-example.md +132 -0
  436. package/src/mod/docs/14-mod-format.md +304 -0
  437. package/src/mod/docs/15-documenting-types.md +156 -0
  438. package/src/mod/examples/ticker/seed.ts +19 -0
  439. package/src/mod/examples/ticker/service.ts +20 -0
  440. package/src/mod/examples/ticker/ticker.test.ts +18 -0
  441. package/src/mod/examples/ticker/types.ts +22 -0
  442. package/src/mod/examples/ticker/view.tsx +19 -0
  443. package/src/mod/index.ts +12 -0
  444. package/src/mod/loader.test.ts +168 -0
  445. package/src/mod/loader.ts +174 -0
  446. package/src/mod/optimistic.test.ts +446 -0
  447. package/src/mod/optimistic.ts +210 -0
  448. package/src/mod/prefab.ts +62 -0
  449. package/src/mod/tracking.test.ts +59 -0
  450. package/src/mod/tracking.ts +51 -0
  451. package/src/mod/types.ts +40 -0
  452. package/src/mod.ts +1 -0
  453. package/src/mods/autostart/CLAUDE.md +6 -0
  454. package/src/mods/autostart/autostart.test.ts +85 -0
  455. package/src/mods/autostart/server.ts +1 -0
  456. package/src/mods/autostart/service.ts +98 -0
  457. package/src/mods/llm/CLAUDE.md +6 -0
  458. package/src/mods/llm/index.ts +57 -0
  459. package/src/mods/llm/llm.test.ts +109 -0
  460. package/src/mods/llm/server.ts +1 -0
  461. package/src/mods/mcp/CLAUDE.md +6 -0
  462. package/src/mods/mcp/server.ts +2 -0
  463. package/src/mods/mcp/service.ts +19 -0
  464. package/src/mods/mcp/types.ts +7 -0
  465. package/src/mods/treenity/agent-port.ts +93 -0
  466. package/src/mods/treenity/groups.ts +19 -0
  467. package/src/mods/treenity/mod-type.ts +10 -0
  468. package/src/mods/treenity/seed.ts +56 -0
  469. package/src/mods/treenity/server.ts +4 -0
  470. package/src/mods/treenity/system.ts +70 -0
  471. package/src/mods/uix/CLAUDE.md +7 -0
  472. package/src/mods/uix/client.ts +117 -0
  473. package/src/mods/uix/compile.test.ts +228 -0
  474. package/src/mods/uix/compile.ts +110 -0
  475. package/src/mods/uix/jsx-parser.test.ts +554 -0
  476. package/src/mods/uix/jsx-parser.ts +489 -0
  477. package/src/mods/uix/lazy-load.test.ts +261 -0
  478. package/src/mods/uix/uix-repomix.md +3509 -0
  479. package/src/mods/uix/verify.ts +68 -0
  480. package/src/schema/CLAUDE.md +13 -0
  481. package/src/schema/catalog.ts +101 -0
  482. package/src/schema/extract-schemas.test.ts +84 -0
  483. package/src/schema/extract-schemas.ts +462 -0
  484. package/src/schema/generated/autostart.json +44 -0
  485. package/src/schema/generated/board.column.json +25 -0
  486. package/src/schema/generated/board.task.json +147 -0
  487. package/src/schema/generated/brahman.action.back.json +12 -0
  488. package/src/schema/generated/brahman.action.broadcast.json +31 -0
  489. package/src/schema/generated/brahman.action.call.json +57 -0
  490. package/src/schema/generated/brahman.action.emittext.json +23 -0
  491. package/src/schema/generated/brahman.action.eval.json +23 -0
  492. package/src/schema/generated/brahman.action.file.json +28 -0
  493. package/src/schema/generated/brahman.action.forward.json +29 -0
  494. package/src/schema/generated/brahman.action.getvalue.json +28 -0
  495. package/src/schema/generated/brahman.action.ifelse.json +42 -0
  496. package/src/schema/generated/brahman.action.keywordselect.json +46 -0
  497. package/src/schema/generated/brahman.action.message.json +127 -0
  498. package/src/schema/generated/brahman.action.onerror.json +29 -0
  499. package/src/schema/generated/brahman.action.page.json +22 -0
  500. package/src/schema/generated/brahman.action.params.json +36 -0
  501. package/src/schema/generated/brahman.action.question.json +43 -0
  502. package/src/schema/generated/brahman.action.remove.json +12 -0
  503. package/src/schema/generated/brahman.action.resethistory.json +12 -0
  504. package/src/schema/generated/brahman.action.resetsession.json +12 -0
  505. package/src/schema/generated/brahman.action.selectlang.json +20 -0
  506. package/src/schema/generated/brahman.action.setvalue.json +27 -0
  507. package/src/schema/generated/brahman.action.tag.json +27 -0
  508. package/src/schema/generated/brahman.bot.json +68 -0
  509. package/src/schema/generated/brahman.page.json +25 -0
  510. package/src/schema/generated/brahman.session.json +29 -0
  511. package/src/schema/generated/brahman.user.json +58 -0
  512. package/src/schema/generated/cafe.contact.json +56 -0
  513. package/src/schema/generated/cafe.mail.json +29 -0
  514. package/src/schema/generated/default.json +15 -0
  515. package/src/schema/generated/doc.page.json +23 -0
  516. package/src/schema/generated/examples.demo.generator.json +16 -0
  517. package/src/schema/generated/examples.demo.sensor.json +35 -0
  518. package/src/schema/generated/groups.json +20 -0
  519. package/src/schema/generated/launcher.json +91 -0
  520. package/src/schema/generated/mcp.server.json +15 -0
  521. package/src/schema/generated/mindmap.map.json +22 -0
  522. package/src/schema/generated/sim.agent.json +24 -0
  523. package/src/schema/generated/sim.ai.json +24 -0
  524. package/src/schema/generated/sim.config.json +38 -0
  525. package/src/schema/generated/sim.descriptive.json +26 -0
  526. package/src/schema/generated/sim.events.json +47 -0
  527. package/src/schema/generated/sim.item.json +20 -0
  528. package/src/schema/generated/sim.memory.json +17 -0
  529. package/src/schema/generated/sim.nearby.json +17 -0
  530. package/src/schema/generated/sim.position.json +25 -0
  531. package/src/schema/generated/sim.round.json +64 -0
  532. package/src/schema/generated/sim.world.json +32 -0
  533. package/src/schema/generated/t.agent.port.json +74 -0
  534. package/src/schema/generated/t.llm.json +20 -0
  535. package/src/schema/generated/t.mod.json +27 -0
  536. package/src/schema/generated/t3d.animator.json +46 -0
  537. package/src/schema/generated/t3d.audio.json +58 -0
  538. package/src/schema/generated/t3d.camera.json +50 -0
  539. package/src/schema/generated/t3d.collider.json +84 -0
  540. package/src/schema/generated/t3d.light.json +90 -0
  541. package/src/schema/generated/t3d.line.json +47 -0
  542. package/src/schema/generated/t3d.lod.json +28 -0
  543. package/src/schema/generated/t3d.material.json +131 -0
  544. package/src/schema/generated/t3d.mesh.json +65 -0
  545. package/src/schema/generated/t3d.object.json +64 -0
  546. package/src/schema/generated/t3d.particles.json +109 -0
  547. package/src/schema/generated/t3d.rigidbody.json +81 -0
  548. package/src/schema/generated/t3d.scene.json +7 -0
  549. package/src/schema/generated/t3d.script.json +23 -0
  550. package/src/schema/generated/t3d.text.json +86 -0
  551. package/src/schema/generated/t3d.trail.json +45 -0
  552. package/src/schema/generated/task.json +96 -0
  553. package/src/schema/generated/test.fixture.json +43 -0
  554. package/src/schema/generated/ticker.config.json +43 -0
  555. package/src/schema/generated/ticker.price.json +20 -0
  556. package/src/schema/generated/todo.item.json +25 -0
  557. package/src/schema/generated/todo.list.json +33 -0
  558. package/src/schema/generated/treenity.system.json +259 -0
  559. package/src/schema/generated/whisper.audio.json +25 -0
  560. package/src/schema/generated/whisper.checklist.json +17 -0
  561. package/src/schema/generated/whisper.config.json +30 -0
  562. package/src/schema/generated/whisper.inbox.json +24 -0
  563. package/src/schema/generated/whisper.meta.json +35 -0
  564. package/src/schema/generated/whisper.text.json +16 -0
  565. package/src/schema/load.ts +24 -0
  566. package/src/schema/schema.test.ts +86 -0
  567. package/src/schema/test-fixture.types.ts +21 -0
  568. package/src/schema/test-opaque.ts +42 -0
  569. package/src/schema/types.ts +33 -0
  570. package/src/server/CLAUDE.md +26 -0
  571. package/src/server/actions.test.ts +272 -0
  572. package/src/server/actions.ts +274 -0
  573. package/src/server/agent-sub.test.ts +90 -0
  574. package/src/server/agent.test.ts +305 -0
  575. package/src/server/agent.ts +17 -0
  576. package/src/server/api.test.ts +454 -0
  577. package/src/server/auth.test.ts +441 -0
  578. package/src/server/auth.ts +386 -0
  579. package/src/server/client.ts +24 -0
  580. package/src/server/conditions.test.ts +128 -0
  581. package/src/server/cookies.ts +25 -0
  582. package/src/server/coverage.test.ts +827 -0
  583. package/src/server/doc-index.ts +286 -0
  584. package/src/server/e2e.test.ts +966 -0
  585. package/src/server/errors.ts +11 -0
  586. package/src/server/factory.ts +99 -0
  587. package/src/server/main.ts +56 -0
  588. package/src/server/mcp.ts +326 -0
  589. package/src/server/migrate.test.ts +123 -0
  590. package/src/server/migrate.ts +62 -0
  591. package/src/server/mod-catalog.ts +59 -0
  592. package/src/server/mods-mount.ts +179 -0
  593. package/src/server/mount-adapters.ts +82 -0
  594. package/src/server/mount.parametrized.test.ts +52 -0
  595. package/src/server/mount.query.test.ts +127 -0
  596. package/src/server/mount.test.ts +497 -0
  597. package/src/server/mount.ts +208 -0
  598. package/src/server/prefab.test.ts +415 -0
  599. package/src/server/prefab.ts +104 -0
  600. package/src/server/seed/index.ts +24 -0
  601. package/src/server/server.ts +153 -0
  602. package/src/server/stress.test.ts +844 -0
  603. package/src/server/sub.test.ts +55 -0
  604. package/src/server/sub.ts +224 -0
  605. package/src/server/trpc.ts +369 -0
  606. package/src/server/types-mount.ts +142 -0
  607. package/src/server/validate.test.ts +91 -0
  608. package/src/server/validate.ts +22 -0
  609. package/src/server/volatile.test.ts +140 -0
  610. package/src/server/volatile.ts +32 -0
  611. package/src/server/watch.test.ts +594 -0
  612. package/src/server/watch.ts +202 -0
  613. package/src/server/workflow.test.ts +82 -0
  614. package/src/tree/CLAUDE.md +13 -0
  615. package/src/tree/cache.test.ts +213 -0
  616. package/src/tree/cache.ts +51 -0
  617. package/src/tree/fs.test.ts +247 -0
  618. package/src/tree/fs.ts +251 -0
  619. package/src/tree/index.test.ts +164 -0
  620. package/src/tree/index.ts +216 -0
  621. package/src/tree/inflight.ts +15 -0
  622. package/src/tree/json-codec.ts +16 -0
  623. package/src/tree/mimefs.test.ts +289 -0
  624. package/src/tree/mimefs.ts +142 -0
  625. package/src/tree/mongo.ts +125 -0
  626. package/src/tree/patch.test.ts +192 -0
  627. package/src/tree/patch.ts +133 -0
  628. package/src/tree/query.test.ts +110 -0
  629. package/src/tree/query.ts +70 -0
  630. package/src/tree/repath.test.ts +86 -0
  631. package/src/tree/repath.ts +53 -0
  632. package/src/tree-chain.test.ts +723 -0
  633. package/src/tree-chain.ts +144 -0
  634. package/src/tree.ts +1 -0
  635. package/src/uri.test.ts +86 -0
  636. package/src/uri.ts +82 -0
  637. package/CHANGELOG.md +0 -314
  638. package/README.md +0 -18
  639. package/dist/context.d.ts +0 -41
  640. package/dist/context.d.ts.map +0 -1
  641. package/dist/context.mjs +0 -81
  642. package/dist/context.mjs.map +0 -1
  643. package/dist/contexts/node-engine.d.ts +0 -12
  644. package/dist/contexts/node-engine.d.ts.map +0 -1
  645. package/dist/contexts/node-engine.mjs +0 -7
  646. package/dist/contexts/node-engine.mjs.map +0 -1
  647. package/dist/contexts/noflo/types.d.ts +0 -20
  648. package/dist/contexts/noflo/types.d.ts.map +0 -1
  649. package/dist/contexts/object.d.ts +0 -11
  650. package/dist/contexts/object.d.ts.map +0 -1
  651. package/dist/contexts/object.mjs +0 -15
  652. package/dist/contexts/object.mjs.map +0 -1
  653. package/dist/contexts/proto.d.ts +0 -11
  654. package/dist/contexts/proto.d.ts.map +0 -1
  655. package/dist/contexts/proto.mjs +0 -7
  656. package/dist/contexts/proto.mjs.map +0 -1
  657. package/dist/contexts/react-context.d.ts +0 -21
  658. package/dist/contexts/react-context.d.ts.map +0 -1
  659. package/dist/contexts/react-context.mjs +0 -24
  660. package/dist/contexts/react-context.mjs.map +0 -1
  661. package/dist/contexts/service.mjs +0 -7
  662. package/dist/contexts/service.mjs.map +0 -1
  663. package/dist/get-type-cache.d.ts +0 -2
  664. package/dist/get-type-cache.d.ts.map +0 -1
  665. package/dist/get-type-cache.mjs +0 -7
  666. package/dist/get-type-cache.mjs.map +0 -1
  667. package/dist/index.d.ts +0 -13
  668. package/dist/index.d.ts.map +0 -1
  669. package/dist/index.mjs +0 -10
  670. package/dist/index.mjs.map +0 -1
  671. package/dist/link/link.d.ts +0 -25
  672. package/dist/link/link.d.ts.map +0 -1
  673. package/dist/link/link.mjs +0 -72
  674. package/dist/link/link.mjs.map +0 -1
  675. package/dist/link/link.test.d.ts +0 -2
  676. package/dist/link/link.test.d.ts.map +0 -1
  677. package/dist/loading.d.ts +0 -9
  678. package/dist/loading.d.ts.map +0 -1
  679. package/dist/meta-type.d.ts +0 -58
  680. package/dist/meta-type.d.ts.map +0 -1
  681. package/dist/meta-type.mjs +0 -104
  682. package/dist/meta-type.mjs.map +0 -1
  683. package/dist/meta.d.ts +0 -20
  684. package/dist/meta.d.ts.map +0 -1
  685. package/dist/meta.mjs +0 -16
  686. package/dist/meta.mjs.map +0 -1
  687. package/dist/node/index.d.ts +0 -2
  688. package/dist/node/index.d.ts.map +0 -1
  689. package/dist/node/types.d.ts +0 -37
  690. package/dist/node/types.d.ts.map +0 -1
  691. package/dist/test/context.test.d.ts +0 -2
  692. package/dist/test/context.test.d.ts.map +0 -1
  693. package/dist/types.d.ts +0 -14
  694. package/dist/types.d.ts.map +0 -1
  695. package/dist/types.mjs +0 -16
  696. package/dist/types.mjs.map +0 -1
@@ -0,0 +1,274 @@
1
+ // Treenity Actions — Layer 4
2
+ // Server-specific: ActionCtx, SchemaHandler, client proxy
3
+ // Component registration lives in @/comp
4
+
5
+ import { chain, type Chain } from '#chain';
6
+ import { Class, type TypeProxy } from '#comp';
7
+ import { makeTypedProxy, type ExecuteFn, type StreamFn } from '#comp/handle';
8
+ import { collectDeps as _collectDeps, type ResolvedDeps } from '#comp/needs';
9
+ import { type ComponentData, getComponentField, isComponent, type NodeData, register, resolve } from '#core';
10
+ import { type Tree } from '#tree';
11
+ import { createDraft, enablePatches, finishDraft, type Patch } from 'immer';
12
+ import { OpError } from './errors';
13
+
14
+ export type NodeHandle = ReturnType<typeof serverNodeHandle>;
15
+
16
+ /** @opaque Runtime-injected, not part of public schema */
17
+ export type ActionCtx = {
18
+ node: NodeData;
19
+ store: Tree;
20
+ signal: AbortSignal;
21
+ /** Typed client for cross-node action calls: ctx.nc(path).get(Type).method(data) */
22
+ nc: NodeHandle;
23
+ comp?: ComponentData;
24
+ deps?: ResolvedDeps;
25
+ };
26
+
27
+ // ── Client proxy ──
28
+
29
+ export type { ExecuteInput } from '#comp/handle';
30
+
31
+ export function createNodeHandle(
32
+ execute: ExecuteFn,
33
+ stream: StreamFn,
34
+ getNode?: (path: string) => NodeData | undefined,
35
+ ) {
36
+ return (path: string) => ({
37
+ get<T extends object>(cls: Class<T>, key?: string): Chain<TypeProxy<T>> {
38
+ return chain(makeTypedProxy(getNode?.(path), cls, path, execute, stream, key)) as Chain<TypeProxy<T>>;
39
+ },
40
+ });
41
+ }
42
+ // Server-side typed node client: wraps executeAction/executeStream into createNodeHandle.
43
+ // Usage: const nc = serverNodeHandle(store); await nc(path).get(MyComp).myMethod();
44
+ export function serverNodeHandle(store: Tree) {
45
+ return createNodeHandle(
46
+ (input) => executeAction(store, input.path, input.type, input.key, input.action, input.data),
47
+ (input) => executeStream(store, input.path, input.type, input.key, input.action, input.data),
48
+ );
49
+ }
50
+
51
+ export { collectSiblings, collectDeps } from '#comp/needs';
52
+ export { registerActionNeeds, getActionNeeds } from '#comp/needs';
53
+
54
+ // ── Server-side operations ──
55
+ // Single entry point for tRPC, MCP, cook-bot, services — no boilerplate.
56
+ // All ops throw OpError for domain errors (NOT_FOUND, BAD_REQUEST, CONFLICT).
57
+ enablePatches();
58
+
59
+ // Action timeout: env-configurable, default 5 min (was 30s)
60
+ const ACTION_TIMEOUT = Number(process.env.ACTION_TIMEOUT) || 300_000;
61
+ const STREAM_TIMEOUT = Number(process.env.STREAM_TIMEOUT) || 600_000;
62
+
63
+ // ── Shared resolution: find handler + component for any action call ──
64
+ // Resolution order:
65
+ // key given → node[key], verify $type === type (if type given)
66
+ // type given, no key → scan components for first matching $type
67
+ // neither → node.$type, then scan components for matching action handler
68
+
69
+ type ResolvedAction = {
70
+ node: NodeData;
71
+ handler: (ctx: ActionCtx, data: unknown) => unknown;
72
+ type: string;
73
+ comp: ComponentData | undefined;
74
+ deps: ResolvedDeps;
75
+ fieldKey: string | undefined;
76
+ };
77
+
78
+ // Dynamic actions: load from /sys/types/{ns}/{name} node's `actions` component.
79
+ // Code is compiled with new Function, registered for future calls.
80
+ // Format: actions.{name} = "const node = ...; return result;" (async function body)
81
+ async function loadDynamicAction(
82
+ store: Tree, type: string, action: string,
83
+ ): Promise<((ctx: ActionCtx, data: unknown) => unknown) | null> {
84
+ if (!type.includes('.')) return null;
85
+
86
+ const typePath = `/sys/types/${type.replace(/\./g, '/')}`;
87
+ const typeNode = await store.get(typePath);
88
+ const actionCode = (typeNode as any)?.actions?.[action];
89
+ if (!actionCode || typeof actionCode !== 'string') return null;
90
+
91
+ try {
92
+ const fn = new Function('ctx', 'data', `return (async () => { ${actionCode} })()`) as
93
+ (ctx: ActionCtx, data: unknown) => unknown;
94
+ register(type, `action:${action}`, fn);
95
+ console.log(`[uix] loaded dynamic action "${action}" for "${type}"`);
96
+ return fn;
97
+ } catch (err: any) {
98
+ console.warn(`[uix] failed to compile action "${action}" for "${type}":`, err.message);
99
+ return null;
100
+ }
101
+ }
102
+
103
+ async function resolveActionHandler(
104
+ store: Tree,
105
+ path: string,
106
+ componentType: string | undefined,
107
+ componentKey: string | undefined,
108
+ action: string,
109
+ ): Promise<ResolvedAction> {
110
+ const node = await store.get(path);
111
+ if (!node) throw new OpError('NOT_FOUND', `Node not found: ${path}`);
112
+
113
+ const [comp, fieldKey] = getComponentField(node, componentType ?? 't.any', componentKey) ?? [];
114
+ if (!isComponent(comp)) throw new OpError('NOT_FOUND', `Component "${componentKey ?? componentType}" not found on ${path}`);
115
+
116
+ const type = comp.$type;
117
+
118
+ let deps: ResolvedDeps = await _collectDeps(node, fieldKey!, action, store);
119
+
120
+ let handler = resolve(comp, `action:${action}`);
121
+
122
+ // Fallback: try loading dynamic action from type definition node
123
+ if (!handler) handler = await loadDynamicAction(store, type, action);
124
+ if (!handler) throw new OpError('BAD_REQUEST', `No action "${action}" for type "${type}"`);
125
+
126
+ return { node, handler, type, comp, deps, fieldKey };
127
+ }
128
+
129
+ // ── executeAction: mutating action with Immer draft + patch collection ──
130
+ // Patches attached as $patches for subscription layer (CDC Matrix in sub.ts).
131
+ // Pure actions (no state changes) skip persist — patches.length === 0.
132
+
133
+ export async function executeAction(
134
+ store: Tree,
135
+ path: string,
136
+ componentType: string | undefined,
137
+ componentKey: string | undefined,
138
+ action: string,
139
+ data?: unknown,
140
+ ): Promise<unknown> {
141
+ const { node, handler, type, deps, fieldKey } = await resolveActionHandler(
142
+ store, path, componentType, componentKey, action,
143
+ );
144
+
145
+ // Pre/post condition checking (Design by Contract)
146
+ const schemaHandler = resolve(type, 'schema') as (() => any) | null;
147
+ const methodSchema = schemaHandler?.()?.methods?.[action];
148
+ const preFields: string[] = methodSchema?.pre ?? [];
149
+ const postFields: string[] = methodSchema?.post ?? [];
150
+ const target = fieldKey ? node[fieldKey] as Record<string, unknown> : node as Record<string, unknown>;
151
+
152
+ for (const f of preFields) {
153
+ const v = target[f];
154
+ if (v === undefined || v === null || v === '' || v === 0) {
155
+ console.warn(`[pre] ${type}.${action}: field "${f}" is empty`);
156
+ }
157
+ }
158
+
159
+ const postSnap = Object.fromEntries(postFields.map(f => [f, target[f]]));
160
+
161
+ const draft = createDraft(node);
162
+ // comp must be the draft version so Immer captures mutations via `this.*`
163
+ const dc = fieldKey ? draft[fieldKey] : undefined;
164
+ const draftComp = isComponent(dc) ? dc as ComponentData : undefined;
165
+ const nc = serverNodeHandle(store);
166
+ const actx: ActionCtx = { node: draft, comp: draftComp, deps, store, signal: AbortSignal.timeout(ACTION_TIMEOUT), nc };
167
+ const result = await handler(actx, data ?? {});
168
+
169
+ let patches: Patch[] = [];
170
+ const nextNode = finishDraft(draft, (p) => { patches = p });
171
+
172
+ const postTarget = fieldKey ? nextNode[fieldKey] as Record<string, unknown> : nextNode as Record<string, unknown>;
173
+ for (const f of postFields) {
174
+ if (postTarget[f] === postSnap[f]) {
175
+ console.warn(`[post] ${type}.${action}: field "${f}" unchanged`);
176
+ }
177
+ }
178
+
179
+ if (patches.length > 0) await store.set({ ...nextNode, $patches: patches } as NodeData);
180
+ return result;
181
+ }
182
+
183
+ // ── executeStream: generator action — yields multiple values, no Immer draft ──
184
+ // Generator actions persist via store.set() directly inside the generator body.
185
+
186
+ export async function* executeStream(
187
+ store: Tree,
188
+ path: string,
189
+ componentType: string | undefined,
190
+ componentKey: string | undefined,
191
+ action: string,
192
+ data?: unknown,
193
+ signal?: AbortSignal,
194
+ ): AsyncGenerator<unknown> {
195
+ const { node, handler, comp, deps } = await resolveActionHandler(
196
+ store, path, componentType, componentKey, action,
197
+ );
198
+ // comp is already node[fieldKey] from resolution — no Immer draft needed for generators
199
+ const nc = serverNodeHandle(store);
200
+ const actx: ActionCtx = { node, comp, deps, store, signal: signal ?? AbortSignal.timeout(STREAM_TIMEOUT), nc };
201
+ const result = handler(actx, data ?? {});
202
+ if (!result || typeof (result as any)[Symbol.asyncIterator] !== 'function')
203
+ throw new OpError('BAD_REQUEST', `Action "${action}" is not a generator`);
204
+ yield* result as AsyncIterable<unknown>;
205
+ }
206
+
207
+ // ── setComponent: single component update with OCC ──
208
+
209
+ export async function setComponent(
210
+ store: Tree,
211
+ path: string,
212
+ name: string,
213
+ data: Record<string, unknown>,
214
+ rev?: number,
215
+ ): Promise<void> {
216
+ const node = await store.get(path);
217
+ if (!node) throw new OpError('NOT_FOUND', `Node not found: ${path}`);
218
+
219
+ if (rev != null && node.$rev != null && rev !== node.$rev)
220
+ throw new OpError('CONFLICT', `Stale revision: expected ${rev}, got ${node.$rev}`);
221
+
222
+ node[name] = data;
223
+ await store.set(node);
224
+ }
225
+
226
+ // ── applyTemplate: copy template children to target path ──
227
+
228
+ export async function applyTemplate(
229
+ store: Tree,
230
+ templatePath: string,
231
+ targetPath: string,
232
+ ): Promise<{ applied: string; blocks: number }> {
233
+ const tmpl = await store.get(templatePath);
234
+ if (!tmpl) throw new OpError('NOT_FOUND', `Template not found: ${templatePath}`);
235
+
236
+ const { items: blocks } = await store.getChildren(templatePath);
237
+ const { items: existing } = await store.getChildren(targetPath);
238
+
239
+ for (const child of existing) await store.remove(child.$path);
240
+
241
+ for (const block of blocks) {
242
+ const bname = block.$path.slice(block.$path.lastIndexOf('/') + 1);
243
+ const bpath = targetPath === '/' ? `/${bname}` : `${targetPath}/${bname}`;
244
+ const { $rev, ...rest } = block;
245
+ await store.set({ ...rest, $path: bpath });
246
+ }
247
+
248
+ return { applied: tmpl.$path, blocks: blocks.length };
249
+ }
250
+
251
+ // ── Generic patch action — deep merge data into node (Immer draft) ──
252
+ // Registered on 'default' so every type inherits it via resolve fallback.
253
+ // Guards $ fields. Deep-merges objects, replaces arrays/primitives.
254
+
255
+ function deepAssign(target: any, source: Record<string, unknown>) {
256
+ for (const [k, v] of Object.entries(source)) {
257
+ if (k.startsWith('$')) continue;
258
+ if (v && typeof v === 'object' && !Array.isArray(v)
259
+ && target[k] && typeof target[k] === 'object' && !Array.isArray(target[k])) {
260
+ deepAssign(target[k], v as Record<string, unknown>);
261
+ } else {
262
+ target[k] = v;
263
+ }
264
+ }
265
+ }
266
+
267
+ export function registerBuiltinActions() {
268
+ register('default', 'action:patch', (ctx: ActionCtx, data: unknown) => {
269
+ if (!data || typeof data !== 'object') throw new OpError('BAD_REQUEST', 'patch: data must be an object');
270
+ deepAssign(ctx.node, data as Record<string, unknown>);
271
+ });
272
+ }
273
+
274
+ registerBuiltinActions();
@@ -0,0 +1,90 @@
1
+ // Focused reproduction test: new child created via action → watcher notifies
2
+ // Tests the exact agent scenario: action:task creates /agent/tasks/t-xxx,
3
+ // user watching /agent/tasks prefix should get the event.
4
+
5
+ import { createNode, register } from '#core';
6
+ import { clearRegistry } from '#core/index.test';
7
+ import { createMemoryTree } from '#tree';
8
+ import assert from 'node:assert/strict';
9
+ import { beforeEach, describe, it } from 'node:test';
10
+ import { type ActionCtx, executeAction } from './actions';
11
+ import { type NodeEvent, withSubscriptions } from './sub';
12
+ import { createWatchManager } from './watch';
13
+
14
+ describe('Agent task subscription', () => {
15
+ beforeEach(() => clearRegistry());
16
+
17
+ it('new child created via action triggers prefix watcher', async () => {
18
+ // Register the action (simulates agent's action:task)
19
+ register('agent.config', 'action:task', async (ctx: ActionCtx, data: { prompt: string }) => {
20
+ const id = `t-${Date.now()}`;
21
+ const taskPath = `${ctx.node.$path}/tasks/${id}`;
22
+ await ctx.store.set(createNode(taskPath, 'agent.task', {
23
+ prompt: data.prompt,
24
+ status: 'pending',
25
+ createdAt: Date.now(),
26
+ }));
27
+ return { taskPath };
28
+ });
29
+
30
+ // Build store pipeline: memory → subscriptions → watcher
31
+ const mem = createMemoryTree();
32
+ const watcher = createWatchManager();
33
+ const store = withSubscriptions(mem, (e) => watcher.notify(e));
34
+
35
+ // Seed the agent config node
36
+ await store.set(createNode('/agent', 'agent.config', { systemPrompt: 'test' }));
37
+ await store.set(createNode('/agent/tasks', 'dir'));
38
+
39
+ // Simulate user: connect SSE + register prefix watch
40
+ const events: NodeEvent[] = [];
41
+ watcher.connect('conn1', 'user1', (e) => events.push(e));
42
+ watcher.watch('user1', ['/agent/tasks'], { children: true, autoWatch: true });
43
+
44
+ // Execute the action (same as trpc.execute.mutate)
45
+ const result = await executeAction(store, '/agent', undefined, undefined, 'task', { prompt: 'test task' });
46
+ assert.ok(result && typeof (result as any).taskPath === 'string');
47
+
48
+ // Verify: the watcher should have received the new task event
49
+ assert.equal(events.length, 1, 'watcher should have received exactly 1 event');
50
+ assert.equal(events[0].type, 'set', 'event should be of type "set"');
51
+ assert.ok((events[0] as any).path.startsWith('/agent/tasks/t-'), 'event path should be the new task');
52
+ assert.equal((events[0] as any).node.$type, 'agent.task', 'node should have correct $type');
53
+ });
54
+
55
+ it('new child event includes correct node data for cache.put', async () => {
56
+ register('agent.config', 'action:task', async (ctx: ActionCtx, data: { prompt: string }) => {
57
+ const taskPath = `${ctx.node.$path}/tasks/t-1`;
58
+ await ctx.store.set(createNode(taskPath, 'agent.task', {
59
+ prompt: data.prompt,
60
+ status: 'pending',
61
+ createdAt: 12345,
62
+ }));
63
+ return { taskPath };
64
+ });
65
+
66
+ const mem = createMemoryTree();
67
+ const watcher = createWatchManager();
68
+ const store = withSubscriptions(mem, (e) => watcher.notify(e));
69
+
70
+ await store.set(createNode('/agent', 'agent.config'));
71
+ await store.set(createNode('/agent/tasks', 'dir'));
72
+
73
+ const events: NodeEvent[] = [];
74
+ watcher.connect('c1', 'u1', (e) => events.push(e));
75
+ watcher.watch('u1', ['/agent/tasks'], { children: true, autoWatch: true });
76
+
77
+ await executeAction(store, '/agent', undefined, undefined, 'task', { prompt: 'hello' });
78
+
79
+ // Simulate what the client does: reconstruct the node from the event
80
+ assert.equal(events.length, 1);
81
+ const evt = events[0] as { type: 'set'; path: string; node: any };
82
+ const reconstructed = { $path: evt.path, ...evt.node };
83
+
84
+ assert.equal(reconstructed.$path, '/agent/tasks/t-1');
85
+ assert.equal(reconstructed.$type, 'agent.task');
86
+ assert.equal(reconstructed.prompt, 'hello');
87
+ assert.equal(reconstructed.status, 'pending');
88
+ assert.equal(reconstructed.createdAt, 12345);
89
+ });
90
+ });
@@ -0,0 +1,305 @@
1
+ import { createNode, R, S, W } from '#core';
2
+ import { clearRegistry } from '#core/index.test';
3
+ import { createMemoryTree, type Tree } from '#tree';
4
+ import assert from 'node:assert/strict';
5
+ import { afterEach, beforeEach, describe, it } from 'node:test';
6
+ import { AGENT_SESSION_TTL, hashAgentKey, timingSafeCompare } from './agent';
7
+ import { buildClaims, createSession, resolveToken, withAcl } from './auth';
8
+
9
+ // Import agent-port type registration (side-effect: registers t.agent.port)
10
+ import '../mods/treenity/agent-port';
11
+
12
+ let store: Tree;
13
+
14
+ beforeEach(async () => {
15
+ clearRegistry();
16
+ // Re-import registers via dynamic import won't re-run, so import at top level
17
+ await import('../mods/treenity/agent-port');
18
+
19
+ store = createMemoryTree();
20
+ // Root with public read
21
+ await store.set({ ...createNode('/', 'root'), $acl: [{ g: 'public', p: R }] });
22
+ });
23
+
24
+ describe('hashAgentKey', () => {
25
+ it('produces consistent SHA-256 hex', () => {
26
+ const h1 = hashAgentKey('test-secret');
27
+ const h2 = hashAgentKey('test-secret');
28
+ assert.equal(h1, h2);
29
+ assert.equal(h1.length, 64); // SHA-256 = 32 bytes = 64 hex chars
30
+ });
31
+
32
+ it('different keys produce different hashes', () => {
33
+ assert.notEqual(hashAgentKey('key-a'), hashAgentKey('key-b'));
34
+ });
35
+ });
36
+
37
+ describe('timingSafeCompare', () => {
38
+ it('returns true for matching hashes', () => {
39
+ const h = hashAgentKey('secret');
40
+ assert.equal(timingSafeCompare(h, h), true);
41
+ });
42
+
43
+ it('returns false for different hashes', () => {
44
+ assert.equal(timingSafeCompare(hashAgentKey('a'), hashAgentKey('b')), false);
45
+ });
46
+
47
+ it('returns false for different lengths', () => {
48
+ assert.equal(timingSafeCompare('aa', 'bbbb'), false);
49
+ });
50
+ });
51
+
52
+ describe('agent session', () => {
53
+ it('createSession with custom TTL', async () => {
54
+ const token = await createSession(store, 'agent:/agents/bot', { ttlMs: AGENT_SESSION_TTL });
55
+ assert.ok(token);
56
+ const session = await resolveToken(store, token);
57
+ assert.ok(session);
58
+ assert.equal(session.userId, 'agent:/agents/bot');
59
+ });
60
+
61
+ it('createSession with custom claims stores and retrieves them', async () => {
62
+ const claims = ['u:agent:/agents/bot', 'agent'];
63
+ const token = await createSession(store, 'agent:/agents/bot', { claims });
64
+ const session = await resolveToken(store, token);
65
+ assert.ok(session);
66
+ assert.deepEqual(session.claims, claims);
67
+ });
68
+
69
+ it('regular session has no claims field', async () => {
70
+ const token = await createSession(store, 'alice');
71
+ const session = await resolveToken(store, token);
72
+ assert.ok(session);
73
+ assert.equal(session.claims, undefined);
74
+ });
75
+ });
76
+
77
+ describe('agent TOFU flow', () => {
78
+ const PORT_PATH = '/agents/test-bot';
79
+ const AGENT_KEY = 'super-secret-agent-key-12345';
80
+
81
+ beforeEach(async () => {
82
+ // Create agent port node (admin action)
83
+ await store.set({
84
+ ...createNode(PORT_PATH, 't.agent.port'),
85
+ label: 'Test Bot',
86
+ status: 'idle',
87
+ connected: false,
88
+ $acl: [{ g: 'admins', p: R | W | S }],
89
+ });
90
+ });
91
+
92
+ it('idle → pending on first connect', async () => {
93
+ const keyHash = hashAgentKey(AGENT_KEY);
94
+ const node = await store.get(PORT_PATH);
95
+ assert.ok(node);
96
+
97
+ // Simulate agentConnect: idle → pending
98
+ await store.set({ ...node, status: 'pending', pendingKey: keyHash });
99
+
100
+ const updated = await store.get(PORT_PATH);
101
+ assert.equal((updated as any).status, 'pending');
102
+ assert.equal((updated as any).pendingKey, keyHash);
103
+ });
104
+
105
+ it('pending + same key = still pending', async () => {
106
+ const keyHash = hashAgentKey(AGENT_KEY);
107
+ await store.set({
108
+ ...(await store.get(PORT_PATH))!,
109
+ status: 'pending',
110
+ pendingKey: keyHash,
111
+ });
112
+
113
+ // Same key: verify it matches
114
+ assert.ok(timingSafeCompare(hashAgentKey(AGENT_KEY), keyHash));
115
+ });
116
+
117
+ it('pending + different key = rejected', async () => {
118
+ const keyHash = hashAgentKey(AGENT_KEY);
119
+ await store.set({
120
+ ...(await store.get(PORT_PATH))!,
121
+ status: 'pending',
122
+ pendingKey: keyHash,
123
+ });
124
+
125
+ // Different key doesn't match
126
+ assert.equal(timingSafeCompare(hashAgentKey('wrong-key'), keyHash), false);
127
+ });
128
+
129
+ it('approve moves pending → approved, creates user, sets ACL', async () => {
130
+ const keyHash = hashAgentKey(AGENT_KEY);
131
+ const agentUserId = `agent:${PORT_PATH}`;
132
+
133
+ // Set to pending
134
+ await store.set({
135
+ ...(await store.get(PORT_PATH))!,
136
+ status: 'pending',
137
+ pendingKey: keyHash,
138
+ });
139
+
140
+ // Simulate approve action effects
141
+ const node = (await store.get(PORT_PATH))!;
142
+ const acl = [...(node.$acl ?? []), { g: `u:${agentUserId}`, p: R | W | S }];
143
+ await store.set({
144
+ ...node,
145
+ status: 'approved',
146
+ approvedKey: keyHash,
147
+ pendingKey: undefined,
148
+ $acl: acl,
149
+ });
150
+
151
+ // Create agent user
152
+ await store.set({
153
+ ...createNode(`/auth/users/${agentUserId}`, 'user'),
154
+ groups: { $type: 'groups', list: ['agent'] },
155
+ });
156
+
157
+ // Verify
158
+ const approved = await store.get(PORT_PATH);
159
+ assert.equal((approved as any).status, 'approved');
160
+ assert.equal((approved as any).approvedKey, keyHash);
161
+ assert.equal((approved as any).pendingKey, undefined);
162
+
163
+ const user = await store.get(`/auth/users/${agentUserId}`);
164
+ assert.ok(user);
165
+
166
+ // Agent ACL is set
167
+ assert.ok(approved!.$acl?.some(e => e.g === `u:${agentUserId}` && e.p === (R | W | S)));
168
+ });
169
+
170
+ it('approved + correct key = session with agent userId', async () => {
171
+ const keyHash = hashAgentKey(AGENT_KEY);
172
+ const agentUserId = `agent:${PORT_PATH}`;
173
+
174
+ // Setup approved state
175
+ await store.set({
176
+ ...(await store.get(PORT_PATH))!,
177
+ status: 'approved',
178
+ approvedKey: keyHash,
179
+ $acl: [
180
+ { g: 'admins', p: R | W | S },
181
+ { g: `u:${agentUserId}`, p: R | W | S },
182
+ ],
183
+ });
184
+ await store.set({
185
+ ...createNode(`/auth/users/${agentUserId}`, 'user'),
186
+ groups: { $type: 'groups', list: ['agent'] },
187
+ });
188
+
189
+ // Agent connects with correct key
190
+ assert.ok(timingSafeCompare(hashAgentKey(AGENT_KEY), keyHash));
191
+
192
+ // Create session
193
+ const token = await createSession(store, agentUserId, { ttlMs: AGENT_SESSION_TTL });
194
+ const session = await resolveToken(store, token);
195
+ assert.ok(session);
196
+ assert.equal(session.userId, agentUserId);
197
+
198
+ // buildClaims includes 'agent' group
199
+ const claims = await buildClaims(store, agentUserId);
200
+ assert.ok(claims.includes(`u:${agentUserId}`));
201
+ assert.ok(claims.includes('authenticated'));
202
+ assert.ok(claims.includes('agent'));
203
+ });
204
+
205
+ it('agent can write to its subtree via ACL', async () => {
206
+ const agentUserId = `agent:${PORT_PATH}`;
207
+ const claims = [`u:${agentUserId}`, 'authenticated', 'agent'];
208
+
209
+ // Setup: agent port with ACL granting agent access
210
+ await store.set({
211
+ ...(await store.get(PORT_PATH))!,
212
+ status: 'approved',
213
+ $acl: [
214
+ { g: 'admins', p: R | W | S },
215
+ { g: `u:${agentUserId}`, p: R | W | S },
216
+ ],
217
+ });
218
+
219
+ const aclStore = withAcl(store, agentUserId, claims);
220
+
221
+ // Agent can write to its subtree
222
+ await aclStore.set(createNode(`${PORT_PATH}/state`, 'agent.state'));
223
+ const child = await aclStore.get(`${PORT_PATH}/state`);
224
+ assert.ok(child);
225
+ assert.equal(child.$type, 'agent.state');
226
+ });
227
+
228
+ it('agent cannot write outside its subtree', async () => {
229
+ const agentUserId = `agent:${PORT_PATH}`;
230
+ const claims = [`u:${agentUserId}`, 'authenticated', 'agent'];
231
+
232
+ // Setup
233
+ await store.set({
234
+ ...(await store.get(PORT_PATH))!,
235
+ status: 'approved',
236
+ $acl: [
237
+ { g: 'admins', p: R | W | S },
238
+ { g: `u:${agentUserId}`, p: R | W | S },
239
+ ],
240
+ });
241
+
242
+ const aclStore = withAcl(store, agentUserId, claims);
243
+
244
+ // Agent cannot write to root-level paths (root ACL: public R only)
245
+ await assert.rejects(
246
+ () => aclStore.set(createNode('/something-else', 'dir')),
247
+ (err: Error) => err.message.includes('Access denied'),
248
+ );
249
+ });
250
+
251
+ it('revoke clears access', async () => {
252
+ const agentUserId = `agent:${PORT_PATH}`;
253
+
254
+ // Setup approved state with ACL
255
+ await store.set({
256
+ ...(await store.get(PORT_PATH))!,
257
+ status: 'approved',
258
+ approvedKey: hashAgentKey(AGENT_KEY),
259
+ $acl: [
260
+ { g: 'admins', p: R | W | S },
261
+ { g: `u:${agentUserId}`, p: R | W | S },
262
+ ],
263
+ });
264
+
265
+ // Simulate revoke: clear key, remove agent ACL
266
+ const node = (await store.get(PORT_PATH))!;
267
+ const acl = (node.$acl ?? []).filter(e => e.g !== `u:${agentUserId}`);
268
+ await store.set({
269
+ ...node,
270
+ status: 'revoked',
271
+ approvedKey: undefined,
272
+ connected: false,
273
+ $acl: acl,
274
+ });
275
+
276
+ const revoked = await store.get(PORT_PATH);
277
+ assert.equal((revoked as any).status, 'revoked');
278
+ assert.equal((revoked as any).approvedKey, undefined);
279
+ assert.ok(!revoked!.$acl?.some(e => e.g === `u:${agentUserId}`));
280
+ });
281
+
282
+ it('reset returns to idle', async () => {
283
+ const agentUserId = `agent:${PORT_PATH}`;
284
+
285
+ // Start from revoked
286
+ await store.set({
287
+ ...(await store.get(PORT_PATH))!,
288
+ status: 'revoked',
289
+ $acl: [{ g: 'admins', p: R | W | S }],
290
+ });
291
+
292
+ // Simulate reset
293
+ await store.set({
294
+ ...(await store.get(PORT_PATH))!,
295
+ status: 'idle',
296
+ pendingKey: undefined,
297
+ approvedKey: undefined,
298
+ connected: false,
299
+ connectedAt: undefined,
300
+ });
301
+
302
+ const reset = await store.get(PORT_PATH);
303
+ assert.equal((reset as any).status, 'idle');
304
+ });
305
+ });
@@ -0,0 +1,17 @@
1
+ // Agent Port — TOFU (Trust On First Use) connection system
2
+ // Utilities for agent key hashing and session management.
3
+
4
+ import { createHash, timingSafeEqual } from 'node:crypto';
5
+
6
+ export const AGENT_SESSION_TTL = 7 * 24 * 60 * 60 * 1000; // 7 days
7
+
8
+ /** SHA-256 hex hash — fast, sufficient for high-entropy agent keys */
9
+ export function hashAgentKey(key: string): string {
10
+ return createHash('sha256').update(key).digest('hex');
11
+ }
12
+
13
+ /** Constant-time comparison of two hex hashes */
14
+ export function timingSafeCompare(a: string, b: string): boolean {
15
+ if (a.length !== b.length) return false;
16
+ return timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'));
17
+ }