@idleware/ringi 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/package.json +1 -1
  2. package/target/dts/adapters/fs/index.d.ts +2 -0
  3. package/target/dts/adapters/fs/lock.d.ts +1 -0
  4. package/target/dts/adapters/fs/write.d.ts +5 -0
  5. package/target/dts/adapters/host-posix/group-state.d.ts +7 -0
  6. package/target/dts/adapters/host-posix/groups.d.ts +1 -0
  7. package/target/dts/adapters/host-posix/index.d.ts +2 -0
  8. package/target/dts/adapters/host-posix/launch.d.ts +7 -0
  9. package/target/dts/adapters/host-posix/probes.d.ts +5 -0
  10. package/target/dts/adapters/host-posix/stats.d.ts +9 -0
  11. package/target/dts/adapters/keys-file/index.d.ts +2 -0
  12. package/target/dts/adapters/state-fs/index.d.ts +5 -0
  13. package/target/dts/adapters/tool-claude/index.d.ts +2 -0
  14. package/target/dts/adapters/tool-codex/index.d.ts +2 -0
  15. package/target/dts/adapters/tool-cursor/index.d.ts +2 -0
  16. package/target/dts/adapters/tool-traces/index.d.ts +3 -0
  17. package/target/dts/adapters/transport-git/branches.d.ts +43 -0
  18. package/target/dts/adapters/transport-git/files.d.ts +2 -0
  19. package/target/dts/adapters/transport-git/index.d.ts +32 -0
  20. package/target/dts/adapters/transport-git/marks.d.ts +20 -0
  21. package/target/dts/adapters/transport-git/remote/admit.d.ts +7 -0
  22. package/target/dts/adapters/transport-git/remote/alarms.d.ts +3 -0
  23. package/target/dts/adapters/transport-git/remote/bootstrap.d.ts +7 -0
  24. package/target/dts/adapters/transport-git/remote/branch-commit.d.ts +3 -0
  25. package/target/dts/adapters/transport-git/remote/branch-control.d.ts +15 -0
  26. package/target/dts/adapters/transport-git/remote/branch-scan.d.ts +2 -0
  27. package/target/dts/adapters/transport-git/remote/branch.d.ts +5 -0
  28. package/target/dts/adapters/transport-git/remote/cache.d.ts +3 -0
  29. package/target/dts/adapters/transport-git/remote/commits.d.ts +2 -0
  30. package/target/dts/adapters/transport-git/remote/forget.d.ts +2 -0
  31. package/target/dts/adapters/transport-git/remote/git.d.ts +26 -0
  32. package/target/dts/adapters/transport-git/remote/heartbeat-facts.d.ts +1 -0
  33. package/target/dts/adapters/transport-git/remote/heartbeat-name.d.ts +6 -0
  34. package/target/dts/adapters/transport-git/remote/heartbeat-object.d.ts +10 -0
  35. package/target/dts/adapters/transport-git/remote/heartbeat-push.d.ts +2 -0
  36. package/target/dts/adapters/transport-git/remote/heartbeat.d.ts +7 -0
  37. package/target/dts/adapters/transport-git/remote/keys.d.ts +13 -0
  38. package/target/dts/adapters/transport-git/remote/main.d.ts +11 -0
  39. package/target/dts/adapters/transport-git/remote/process.d.ts +20 -0
  40. package/target/dts/adapters/transport-git/remote/push-retry.d.ts +2 -0
  41. package/target/dts/adapters/transport-git/remote/push-validate.d.ts +2 -0
  42. package/target/dts/adapters/transport-git/remote/push.d.ts +16 -0
  43. package/target/dts/adapters/transport-git/remote/roster.d.ts +7 -0
  44. package/target/dts/adapters/transport-git/remote/signals.d.ts +15 -0
  45. package/target/dts/adapters/transport-git/remote/signature.d.ts +5 -0
  46. package/target/dts/adapters/transport-git/remote/sync-heartbeat.d.ts +2 -0
  47. package/target/dts/adapters/transport-git/remote/sync-rooms.d.ts +6 -0
  48. package/target/dts/adapters/transport-git/remote/sync-tags.d.ts +8 -0
  49. package/target/dts/adapters/transport-git/remote/sync.d.ts +2 -0
  50. package/target/dts/adapters/transport-git/remote/types.d.ts +64 -0
  51. package/target/dts/adapters/transport-git/remote.d.ts +17 -0
  52. package/target/dts/adapters/transport-git/repo.d.ts +31 -0
  53. package/target/dts/adapters/transport-git/run.d.ts +21 -0
  54. package/target/dts/adapters/transport-git/signing.d.ts +4 -0
  55. package/target/dts/adapters/transport-git/tags.d.ts +15 -0
  56. package/target/dts/adapters/transport-git/transport.d.ts +7 -0
  57. package/target/dts/adapters/transport-kafka/index.d.ts +2 -0
  58. package/target/dts/adapters/transport-temporal/index.d.ts +2 -0
  59. package/target/dts/cli.d.ts +2 -1
  60. package/target/dts/{cli/agent.d.ts → clients/cli/agent/index.d.ts} +3 -3
  61. package/target/dts/{cli → clients/cli/agent}/session.d.ts +2 -2
  62. package/target/dts/clients/cli/arg-parse.d.ts +2 -0
  63. package/target/dts/{cli → clients/cli}/args.d.ts +2 -2
  64. package/target/dts/clients/cli/commands.d.ts +2 -0
  65. package/target/dts/clients/cli/context.d.ts +4 -0
  66. package/target/dts/{cli/index.d.ts → clients/cli/help.d.ts} +1 -3
  67. package/target/dts/clients/cli/index.d.ts +4 -0
  68. package/target/dts/clients/cli/operator/heartbeat-format.d.ts +5 -0
  69. package/target/dts/clients/cli/operator/heartbeat.d.ts +3 -0
  70. package/target/dts/clients/cli/operator/hub-commands.d.ts +6 -0
  71. package/target/dts/clients/cli/operator/hub.d.ts +2 -0
  72. package/target/dts/clients/cli/operator/index.d.ts +5 -0
  73. package/target/dts/clients/cli/operator/room-lifecycle.d.ts +5 -0
  74. package/target/dts/clients/cli/operator/room-list.d.ts +5 -0
  75. package/target/dts/clients/cli/operator/room-members.d.ts +4 -0
  76. package/target/dts/clients/cli/operator/room.d.ts +2 -0
  77. package/target/dts/clients/cli/operator/sessions.d.ts +5 -0
  78. package/target/dts/clients/cli/operator/watch.d.ts +2 -0
  79. package/target/dts/clients/cli/operator/where-format.d.ts +7 -0
  80. package/target/dts/clients/cli/operator/where.d.ts +2 -0
  81. package/target/dts/clients/cli/shared/budget.d.ts +7 -0
  82. package/target/dts/clients/cli/shared/files.d.ts +4 -0
  83. package/target/dts/clients/cli/shared/index.d.ts +7 -0
  84. package/target/dts/clients/cli/shared/messages.d.ts +7 -0
  85. package/target/dts/clients/cli/shared/profile.d.ts +4 -0
  86. package/target/dts/clients/cli/shared/session.d.ts +6 -0
  87. package/target/dts/clients/cli/shared/signals.d.ts +3 -0
  88. package/target/dts/clients/cli/shared/work.d.ts +6 -0
  89. package/target/dts/clients/cli/skill/index.d.ts +3 -0
  90. package/target/dts/{cli/skill.d.ts → clients/cli/skill/targets.d.ts} +1 -2
  91. package/target/dts/composition/index.d.ts +2 -0
  92. package/target/dts/{heartbeat → core/agents/heartbeat}/index.d.ts +2 -4
  93. package/target/dts/{app/heartbeat.d.ts → core/agents/heartbeat/pulse.d.ts} +5 -6
  94. package/target/dts/core/agents/heartbeat/singleton.d.ts +3 -0
  95. package/target/dts/core/agents/heartbeat/tools.d.ts +6 -0
  96. package/target/dts/{app → core/agents}/overview.d.ts +12 -6
  97. package/target/dts/core/agents/ports/host.d.ts +47 -0
  98. package/target/dts/core/agents/ports/index.d.ts +9 -0
  99. package/target/dts/core/agents/ports/tools.d.ts +18 -0
  100. package/target/dts/core/agents/sessions/agent.d.ts +70 -0
  101. package/target/dts/core/agents/sessions/binding.d.ts +9 -0
  102. package/target/dts/core/agents/sessions/context.d.ts +8 -0
  103. package/target/dts/{runtime/session.d.ts → core/agents/sessions/index.d.ts} +2 -9
  104. package/target/dts/core/agents/sessions/join.d.ts +20 -0
  105. package/target/dts/core/agents/sessions/resume.d.ts +2 -0
  106. package/target/dts/core/agents/sessions/session.d.ts +46 -0
  107. package/target/dts/core/agents/sessions/wait.d.ts +38 -0
  108. package/target/dts/core/agents/turns/budgets.d.ts +5 -0
  109. package/target/dts/core/agents/turns/dispatch.d.ts +9 -0
  110. package/target/dts/core/agents/turns/exhaustion.d.ts +11 -0
  111. package/target/dts/core/agents/turns/grants.d.ts +4 -0
  112. package/target/dts/core/agents/turns/index.d.ts +4 -0
  113. package/target/dts/core/agents/turns/limits.d.ts +2 -0
  114. package/target/dts/core/agents/turns/messages.d.ts +10 -0
  115. package/target/dts/core/agents/turns/pings.d.ts +5 -0
  116. package/target/dts/core/agents/turns/plan.d.ts +3 -0
  117. package/target/dts/core/agents/turns/poll.d.ts +8 -0
  118. package/target/dts/core/agents/turns/results.d.ts +3 -0
  119. package/target/dts/core/agents/turns/seat.d.ts +2 -0
  120. package/target/dts/core/agents/turns/types.d.ts +59 -0
  121. package/target/dts/core/hub/decisions/budget.d.ts +15 -0
  122. package/target/dts/core/hub/decisions/check.d.ts +6 -0
  123. package/target/dts/core/hub/decisions/index.d.ts +5 -0
  124. package/target/dts/core/hub/decisions/proposal.d.ts +38 -0
  125. package/target/dts/core/hub/decisions/usage.d.ts +12 -0
  126. package/target/dts/{runtime/reader.d.ts → core/hub/history/index.d.ts} +12 -6
  127. package/target/dts/core/hub/ports/index.d.ts +16 -0
  128. package/target/dts/core/hub/ports/state.d.ts +12 -0
  129. package/target/dts/{store/index.d.ts → core/hub/ports/store.d.ts} +48 -18
  130. package/target/dts/core/hub/protocol/index.d.ts +5 -0
  131. package/target/dts/{protocol/index.d.ts → core/hub/protocol/model.d.ts} +0 -25
  132. package/target/dts/core/hub/protocol/order.d.ts +2 -0
  133. package/target/dts/core/hub/protocol/reading.d.ts +8 -0
  134. package/target/dts/core/hub/protocol/text.d.ts +11 -0
  135. package/target/dts/core/hub/protocol/threads.d.ts +5 -0
  136. package/target/dts/core/hub/rooms/compat.d.ts +5 -0
  137. package/target/dts/core/hub/rooms/hubs.d.ts +14 -0
  138. package/target/dts/core/hub/rooms/index.d.ts +53 -0
  139. package/target/dts/core/hub/rooms/messages.d.ts +8 -0
  140. package/target/dts/core/hub/rooms/rooms.d.ts +10 -0
  141. package/target/dts/core/hub/signals/check.d.ts +3 -0
  142. package/target/dts/core/hub/signals/effective.d.ts +3 -0
  143. package/target/dts/core/hub/signals/index.d.ts +5 -0
  144. package/target/dts/core/hub/signals/marks.d.ts +4 -0
  145. package/target/dts/core/hub/signals/model.d.ts +28 -0
  146. package/target/dts/core/hub/signals/pins.d.ts +5 -0
  147. package/target/dts/{hub/signals.d.ts → core/hub/signals/write.d.ts} +1 -1
  148. package/target/dts/core/hub/usecases/actor.d.ts +6 -0
  149. package/target/dts/{app → core/hub/usecases}/budget.d.ts +5 -18
  150. package/target/dts/core/hub/usecases/config.d.ts +6 -0
  151. package/target/dts/{app → core/hub/usecases}/context.d.ts +3 -8
  152. package/target/dts/{app → core/hub/usecases}/hubs.d.ts +1 -1
  153. package/target/dts/core/hub/usecases/propose.d.ts +17 -0
  154. package/target/dts/{app → core/hub/usecases}/room.d.ts +8 -34
  155. package/target/dts/{app → core/hub/usecases}/rooms.d.ts +2 -6
  156. package/target/dts/core/hub/usecases/roster.d.ts +5 -0
  157. package/target/dts/core/hub/usecases/send.d.ts +21 -0
  158. package/target/dts/core/hub/usecases/staging.d.ts +8 -0
  159. package/target/dts/{app → core/hub/usecases}/view.d.ts +4 -4
  160. package/target/dts/{domain/profiles.d.ts → core/hub/work/index.d.ts} +3 -7
  161. package/target/dts/core/hub/work/parse.d.ts +2 -0
  162. package/target/dts/core/hub/work/task.d.ts +6 -0
  163. package/target/dts/core/signing/index.d.ts +35 -0
  164. package/target/dts/index.d.ts +8 -8
  165. package/target/dts/lib/fail.d.ts +8 -0
  166. package/target/dts/lib/index.d.ts +3 -11
  167. package/target/dts/lib/json.d.ts +1 -0
  168. package/target/esm/adapters/fs/index.js +5 -0
  169. package/target/esm/adapters/fs/lock.js +83 -0
  170. package/target/esm/adapters/fs/write.js +25 -0
  171. package/target/esm/adapters/host-posix/group-state.js +24 -0
  172. package/target/esm/adapters/host-posix/groups.js +43 -0
  173. package/target/esm/adapters/host-posix/index.js +35 -0
  174. package/target/esm/adapters/host-posix/launch.js +80 -0
  175. package/target/esm/adapters/host-posix/probes.js +66 -0
  176. package/target/esm/adapters/host-posix/stats.js +27 -0
  177. package/target/esm/adapters/keys-file/index.js +68 -0
  178. package/target/esm/adapters/state-fs/index.js +79 -0
  179. package/target/esm/adapters/tool-claude/index.js +32 -0
  180. package/target/esm/adapters/tool-codex/index.js +38 -0
  181. package/target/esm/adapters/tool-cursor/index.js +8 -0
  182. package/target/esm/adapters/tool-traces/index.js +18 -0
  183. package/target/esm/adapters/transport-git/branches.js +155 -0
  184. package/target/esm/adapters/transport-git/files.js +48 -0
  185. package/target/esm/adapters/transport-git/index.js +128 -0
  186. package/target/esm/adapters/transport-git/marks.js +88 -0
  187. package/target/esm/adapters/transport-git/remote/admit.js +71 -0
  188. package/target/esm/adapters/transport-git/remote/alarms.js +30 -0
  189. package/target/esm/adapters/transport-git/remote/bootstrap.js +47 -0
  190. package/target/esm/adapters/transport-git/remote/branch-commit.js +36 -0
  191. package/target/esm/adapters/transport-git/remote/branch-control.js +37 -0
  192. package/target/esm/adapters/transport-git/remote/branch-scan.js +28 -0
  193. package/target/esm/adapters/transport-git/remote/branch.js +37 -0
  194. package/target/esm/adapters/transport-git/remote/cache.js +23 -0
  195. package/target/esm/adapters/transport-git/remote/commits.js +25 -0
  196. package/target/esm/adapters/transport-git/remote/forget.js +35 -0
  197. package/target/esm/adapters/transport-git/remote/git.js +58 -0
  198. package/target/esm/adapters/transport-git/remote/heartbeat-facts.js +35 -0
  199. package/target/esm/adapters/transport-git/remote/heartbeat-name.js +19 -0
  200. package/target/esm/adapters/transport-git/remote/heartbeat-object.js +40 -0
  201. package/target/esm/adapters/transport-git/remote/heartbeat-push.js +44 -0
  202. package/target/esm/adapters/transport-git/remote/heartbeat.js +44 -0
  203. package/target/esm/adapters/transport-git/remote/keys.js +75 -0
  204. package/target/esm/adapters/transport-git/remote/main.js +41 -0
  205. package/target/esm/adapters/transport-git/remote/process.js +37 -0
  206. package/target/esm/adapters/transport-git/remote/push-retry.js +46 -0
  207. package/target/esm/adapters/transport-git/remote/push-validate.js +39 -0
  208. package/target/esm/adapters/transport-git/remote/push.js +62 -0
  209. package/target/esm/adapters/transport-git/remote/roster.js +48 -0
  210. package/target/esm/adapters/transport-git/remote/signals.js +63 -0
  211. package/target/esm/adapters/transport-git/remote/signature.js +56 -0
  212. package/target/esm/adapters/transport-git/remote/sync-heartbeat.js +26 -0
  213. package/target/esm/adapters/transport-git/remote/sync-rooms.js +38 -0
  214. package/target/esm/adapters/transport-git/remote/sync-tags.js +48 -0
  215. package/target/esm/adapters/transport-git/remote/sync.js +38 -0
  216. package/target/esm/adapters/transport-git/remote/types.js +10 -0
  217. package/target/esm/adapters/transport-git/remote.js +17 -0
  218. package/target/esm/adapters/transport-git/repo.js +115 -0
  219. package/target/esm/adapters/transport-git/run.js +57 -0
  220. package/target/esm/adapters/transport-git/signing.js +44 -0
  221. package/target/esm/adapters/transport-git/tags.js +21 -0
  222. package/target/esm/adapters/transport-git/transport.js +29 -0
  223. package/target/esm/adapters/transport-kafka/index.js +4 -0
  224. package/target/esm/adapters/transport-temporal/index.js +4 -0
  225. package/target/esm/cli.js +5 -4
  226. package/target/esm/{cli/agent.js → clients/cli/agent/index.js} +8 -7
  227. package/target/esm/{cli → clients/cli/agent}/session.js +7 -7
  228. package/target/esm/clients/cli/arg-parse.js +41 -0
  229. package/target/esm/{cli → clients/cli}/args.js +3 -36
  230. package/target/esm/clients/cli/commands.js +45 -0
  231. package/target/esm/clients/cli/context.js +6 -0
  232. package/target/esm/{cli/index.js → clients/cli/help.js} +4 -77
  233. package/target/esm/clients/cli/index.js +27 -0
  234. package/target/esm/clients/cli/operator/heartbeat-format.js +8 -0
  235. package/target/esm/clients/cli/operator/heartbeat.js +35 -0
  236. package/target/esm/clients/cli/operator/hub-commands.js +37 -0
  237. package/target/esm/clients/cli/operator/hub.js +10 -0
  238. package/target/esm/clients/cli/operator/index.js +8 -0
  239. package/target/esm/clients/cli/operator/room-lifecycle.js +23 -0
  240. package/target/esm/clients/cli/operator/room-list.js +27 -0
  241. package/target/esm/clients/cli/operator/room-members.js +15 -0
  242. package/target/esm/clients/cli/operator/room.js +15 -0
  243. package/target/esm/clients/cli/operator/sessions.js +32 -0
  244. package/target/esm/clients/cli/operator/watch.js +15 -0
  245. package/target/esm/clients/cli/operator/where-format.js +24 -0
  246. package/target/esm/clients/cli/operator/where.js +32 -0
  247. package/target/esm/clients/cli/shared/budget.js +50 -0
  248. package/target/esm/clients/cli/shared/files.js +26 -0
  249. package/target/esm/clients/cli/shared/index.js +10 -0
  250. package/target/esm/clients/cli/shared/messages.js +41 -0
  251. package/target/esm/clients/cli/shared/profile.js +17 -0
  252. package/target/esm/clients/cli/shared/session.js +8 -0
  253. package/target/esm/clients/cli/shared/signals.js +21 -0
  254. package/target/esm/clients/cli/shared/work.js +26 -0
  255. package/target/esm/clients/cli/skill/index.js +40 -0
  256. package/target/esm/clients/cli/skill/targets.js +20 -0
  257. package/target/esm/composition/index.js +34 -0
  258. package/target/esm/{heartbeat → core/agents/heartbeat}/index.js +14 -12
  259. package/target/esm/{app/heartbeat.js → core/agents/heartbeat/pulse.js} +31 -55
  260. package/target/esm/core/agents/heartbeat/singleton.js +38 -0
  261. package/target/esm/core/agents/heartbeat/tools.js +24 -0
  262. package/target/esm/{app → core/agents}/overview.js +15 -9
  263. package/target/esm/core/agents/ports/host.js +4 -0
  264. package/target/esm/core/agents/ports/index.js +7 -0
  265. package/target/esm/core/agents/ports/tools.js +5 -0
  266. package/target/esm/{app → core/agents/sessions}/agent.js +11 -43
  267. package/target/esm/core/agents/sessions/binding.js +43 -0
  268. package/target/esm/core/agents/sessions/context.js +11 -0
  269. package/target/esm/core/agents/sessions/index.js +59 -0
  270. package/target/esm/core/agents/sessions/join.js +55 -0
  271. package/target/esm/core/agents/sessions/resume.js +54 -0
  272. package/target/esm/core/agents/sessions/session.js +28 -0
  273. package/target/esm/core/agents/sessions/wait.js +47 -0
  274. package/target/esm/core/agents/turns/budgets.js +48 -0
  275. package/target/esm/core/agents/turns/dispatch.js +40 -0
  276. package/target/esm/core/agents/turns/exhaustion.js +16 -0
  277. package/target/esm/core/agents/turns/grants.js +36 -0
  278. package/target/esm/core/agents/turns/index.js +33 -0
  279. package/target/esm/core/agents/turns/limits.js +11 -0
  280. package/target/esm/core/agents/turns/messages.js +32 -0
  281. package/target/esm/core/agents/turns/pings.js +30 -0
  282. package/target/esm/core/agents/turns/plan.js +29 -0
  283. package/target/esm/core/agents/turns/poll.js +48 -0
  284. package/target/esm/core/agents/turns/results.js +40 -0
  285. package/target/esm/core/agents/turns/seat.js +44 -0
  286. package/target/esm/core/agents/turns/types.js +3 -0
  287. package/target/esm/{domain/index.js → core/hub/capabilities.js} +3 -3
  288. package/target/esm/core/hub/decisions/budget.js +40 -0
  289. package/target/esm/core/hub/decisions/check.js +43 -0
  290. package/target/esm/core/hub/decisions/index.js +10 -0
  291. package/target/esm/core/hub/decisions/proposal.js +73 -0
  292. package/target/esm/core/hub/decisions/usage.js +39 -0
  293. package/target/esm/core/hub/history/index.js +90 -0
  294. package/target/esm/core/hub/ports/index.js +10 -0
  295. package/target/esm/core/hub/ports/state.js +5 -0
  296. package/target/esm/core/hub/ports/store.js +10 -0
  297. package/target/esm/{protocol → core/hub/protocol}/files.js +4 -2
  298. package/target/esm/core/hub/protocol/index.js +9 -0
  299. package/target/esm/core/hub/protocol/model.js +15 -0
  300. package/target/esm/core/hub/protocol/order.js +39 -0
  301. package/target/esm/core/hub/protocol/reading.js +29 -0
  302. package/target/esm/core/hub/protocol/text.js +95 -0
  303. package/target/esm/core/hub/protocol/threads.js +52 -0
  304. package/target/esm/core/hub/rooms/compat.js +51 -0
  305. package/target/esm/core/hub/rooms/hubs.js +92 -0
  306. package/target/esm/core/hub/rooms/index.js +23 -0
  307. package/target/esm/core/hub/rooms/messages.js +47 -0
  308. package/target/esm/core/hub/rooms/rooms.js +84 -0
  309. package/target/esm/core/hub/signals/check.js +33 -0
  310. package/target/esm/core/hub/signals/effective.js +31 -0
  311. package/target/esm/core/hub/signals/index.js +23 -0
  312. package/target/esm/core/hub/signals/marks.js +30 -0
  313. package/target/esm/core/hub/signals/model.js +5 -0
  314. package/target/esm/core/hub/signals/pins.js +21 -0
  315. package/target/esm/{hub/signals.js → core/hub/signals/write.js} +4 -4
  316. package/target/esm/core/hub/usecases/actor.js +3 -0
  317. package/target/esm/core/hub/usecases/budget.js +78 -0
  318. package/target/esm/core/hub/usecases/config.js +5 -0
  319. package/target/esm/core/hub/usecases/context.js +13 -0
  320. package/target/esm/core/hub/usecases/hubs.js +95 -0
  321. package/target/esm/core/hub/usecases/propose.js +39 -0
  322. package/target/esm/core/hub/usecases/room.js +82 -0
  323. package/target/esm/{app → core/hub/usecases}/rooms.js +11 -63
  324. package/target/esm/core/hub/usecases/roster.js +47 -0
  325. package/target/esm/core/hub/usecases/send.js +78 -0
  326. package/target/esm/core/hub/usecases/staging.js +60 -0
  327. package/target/esm/{app → core/hub/usecases}/view.js +33 -33
  328. package/target/esm/core/hub/work/index.js +13 -0
  329. package/target/esm/core/hub/work/parse.js +37 -0
  330. package/target/esm/core/hub/work/task.js +68 -0
  331. package/target/esm/core/signing/index.js +4 -0
  332. package/target/esm/index.js +7 -6
  333. package/target/esm/lib/fail.js +18 -0
  334. package/target/esm/lib/index.js +20 -195
  335. package/target/esm/lib/json.js +81 -0
  336. package/target/dts/app/agent.d.ts +0 -109
  337. package/target/dts/app/session.d.ts +0 -65
  338. package/target/dts/cli/context.d.ts +0 -6
  339. package/target/dts/cli/operator.d.ts +0 -10
  340. package/target/dts/cli/shared.d.ts +0 -27
  341. package/target/dts/domain/decisions.d.ts +0 -65
  342. package/target/dts/domain/signals.d.ts +0 -37
  343. package/target/dts/heartbeat/platform.d.ts +0 -15
  344. package/target/dts/heartbeat/tools.d.ts +0 -11
  345. package/target/dts/home/index.d.ts +0 -21
  346. package/target/dts/hub/index.d.ts +0 -72
  347. package/target/dts/runtime/watch.d.ts +0 -17
  348. package/target/dts/store/git/index.d.ts +0 -92
  349. package/target/dts/store/git/remote.d.ts +0 -95
  350. package/target/dts/store/kafka/index.d.ts +0 -2
  351. package/target/dts/store/temporal/index.d.ts +0 -2
  352. package/target/esm/app/budget.js +0 -95
  353. package/target/esm/app/context.js +0 -16
  354. package/target/esm/app/hubs.js +0 -76
  355. package/target/esm/app/room.js +0 -181
  356. package/target/esm/app/session.js +0 -99
  357. package/target/esm/cli/context.js +0 -8
  358. package/target/esm/cli/operator.js +0 -185
  359. package/target/esm/cli/shared.js +0 -159
  360. package/target/esm/cli/skill.js +0 -54
  361. package/target/esm/domain/decisions.js +0 -178
  362. package/target/esm/domain/profiles.js +0 -96
  363. package/target/esm/domain/signals.js +0 -134
  364. package/target/esm/heartbeat/platform.js +0 -83
  365. package/target/esm/heartbeat/tools.js +0 -68
  366. package/target/esm/home/index.js +0 -88
  367. package/target/esm/hub/index.js +0 -224
  368. package/target/esm/protocol/index.js +0 -219
  369. package/target/esm/runtime/reader.js +0 -65
  370. package/target/esm/runtime/session.js +0 -116
  371. package/target/esm/runtime/watch.js +0 -321
  372. package/target/esm/store/git/index.js +0 -719
  373. package/target/esm/store/git/remote.js +0 -928
  374. package/target/esm/store/index.js +0 -15
  375. package/target/esm/store/kafka/index.js +0 -3
  376. package/target/esm/store/temporal/index.js +0 -3
  377. /package/target/dts/{domain/index.d.ts → core/hub/capabilities.d.ts} +0 -0
  378. /package/target/dts/{protocol → core/hub/protocol}/files.d.ts +0 -0
@@ -0,0 +1,128 @@
1
+ // git-store: the Store port on git. The store directory holds one repository (`store/.git`):
2
+ // refs/heads/main the shared contract, one commit per amendment
3
+ // refs/heads/<room> the room: one linear branch, like a chat. Its first commit is the room's genesis
4
+ // (the root's, on top of main); then every message is a commit by its author, and
5
+ // every control record (Kind: room) a commit by the root
6
+ // refs/tags/<agent>.<signal>.<message12> signals: annotated tags without text on the message's commit
7
+ // refs/archive/<room>@<genesis8> an archived room, read-only
8
+ // refs/ringi/accepted/<genesis>/<reader>/<author> readers' pins of each author's accepted last message
9
+ // A member's log is its own commits in the room's branch, in branch order. The control files at the root of a room's
10
+ // tree (members, profile, signers) change only in the root's control records.
11
+ // A signed hub signs every commit and tag with its author's key and verifies what it fetches (remote.ts).
12
+ // The class is layered by concern: repo.ts (repository, contract), branches.ts (rooms), marks.ts (signals, heartbeats)
13
+ // and here the logs: messages, their files, staging and pins.
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+ import { fail } from '../../lib/index.js';
17
+ import { mkdirIn } from '../fs/index.js';
18
+ import * as Remote from './remote.js';
19
+ import { runGitRaw, splitBuf } from './run.js';
20
+ import { messageFiles } from './files.js';
21
+ import { init } from './repo.js';
22
+ import { CONTROL_FILES } from './branches.js';
23
+ import { Marks } from './marks.js';
24
+ export { runGit } from './run.js';
25
+ import { TEXT_MAX, checkOutgoing } from '../../core/hub/protocol/index.js';
26
+ export class GitStore extends Marks {
27
+ type = 'git';
28
+ static create(hubDir, d, connection, contract, text) {
29
+ const { home, signing } = typeof connection === 'string' ? { home: connection, signing: undefined } : connection;
30
+ const dir = path.join(hubDir, d.store.path, '.git');
31
+ // A signed hub's repository already exists: rootFor made it with the root key.
32
+ if (!fs.existsSync(dir)) {
33
+ mkdirIn(home, path.dirname(dir));
34
+ init(dir, home);
35
+ }
36
+ const s = new GitStore(hubDir, d, home, signing);
37
+ const sha = s.commit('operator', text, s.tree(null, Object.entries(contract).map(([p, c]) => ({ path: p, content: c, mode: '100644' }))), []);
38
+ if (d.trust === 'signed')
39
+ d.genesis = sha;
40
+ if (!s.advance('refs/heads/main', sha, null))
41
+ fail(`hub ${d.name}: main already exists`);
42
+ s.checkout();
43
+ return s;
44
+ }
45
+ // A new signed hub: the root key is generated on this machine and pinned in the descriptor.
46
+ static rootFor(hubDir, d, home, signing) {
47
+ const dir = path.join(hubDir, d.store.path, '.git');
48
+ mkdirIn(home, path.dirname(dir));
49
+ init(dir, home);
50
+ return Remote.setupSigned({ name: d.name, dir, d, signing }, home, d.store.remote).root;
51
+ }
52
+ logIds(room) { return [...new Set(this.live(room).map(e => e.author))]; }
53
+ logHead(room, id) { return this.live(room).filter(e => e.author === id).at(-1)?.sha ?? null; }
54
+ // One author's messages after the genesis up to `head`, oldest first, read as bytes: the text is the commit message,
55
+ // the files are the explicit Ringi-Files list, or the paths the commit adds or changes for records without it.
56
+ messages(room, id, head) {
57
+ const tip = this.trust === 'signed' ? this.tip(room.name) : null;
58
+ const skipped = new Set(tip ? this.entries(room, tip).filter(e => e.skipped).map(e => e.sha) : []);
59
+ const raw = runGitRaw(this.args(['log', '--topo-order', '--reverse', '--no-renames', '--raw', '--no-abbrev', '--format=%x1e%H%x1f%P%x1f%ct%x1f', head, `^${room.genesis}`]), this.home);
60
+ const records = splitBuf(raw, 0x1e).filter(b => b.length).map(rec => splitBuf(rec, 0x1f));
61
+ const commits = new Map(Remote.readCommits(this.h, records.map(([sha]) => sha.toString())).map(c => [c.sha, c]));
62
+ const lsTree = (sha) => runGitRaw(this.args(['ls-tree', '-r', '-z', sha]), this.home);
63
+ // The author's own messages: no control record, nothing a signed hub skips.
64
+ const own = (sha, author, body) => author === id && !skipped.has(sha) && !this.isControl(room, sha, id, body.toString('utf8'));
65
+ return records.flatMap(([sha, parent, ct, rest = Buffer.alloc(0)]) => {
66
+ const { author, body } = commits.get(sha.toString());
67
+ if (!own(sha.toString(), author, body))
68
+ return [];
69
+ return [{ id: sha.toString(), parent: parent.toString() || null, time: Number(ct.toString()), text: body,
70
+ ...messageFiles(body, rest, sha.toString(), x => this.isRef(x), lsTree) }];
71
+ });
72
+ }
73
+ blob(file) {
74
+ const id = this.git(['hash-object', '-w', '--', file]).trim();
75
+ return { id, size: Number(this.git(['cat-file', '-s', id]).trim()) };
76
+ }
77
+ // A tree of the staged files under refs/ringi/staged/<genesis>/<who>: git keeps what a ref reaches.
78
+ retain(room, who, entries) {
79
+ const ref = `refs/ringi/staged/${room.genesis}/${who}`;
80
+ if (!entries.length) {
81
+ this.gitOk(['update-ref', '-d', ref]);
82
+ return;
83
+ }
84
+ this.git(['update-ref', ref, this.tree(null, entries)]);
85
+ }
86
+ // One message on top of the room's branch.
87
+ append(room, who, text, entries) {
88
+ if (room.archived)
89
+ fail(`room ${room.name} is archived: read-only`);
90
+ if (checkOutgoing(text, s => this.isRef(s)).trailers['ringi-files'])
91
+ fail('Ringi-Files is store metadata; supply attachments instead');
92
+ const own = entries.find(e => CONTROL_FILES.has(e.path));
93
+ if (own)
94
+ fail(`${own.path} is the room's own file; attach it under another path`);
95
+ if (entries.length)
96
+ text = `${text.trimEnd()}\nRingi-Files: ${JSON.stringify([...new Set(entries.map(e => e.path))])}\n`;
97
+ if (Buffer.byteLength(text) > TEXT_MAX)
98
+ fail('attachment list makes the message exceed 64 KiB; use fewer or shorter paths');
99
+ checkOutgoing(text, s => this.isRef(s));
100
+ return this.onTop(room, who, text, entries);
101
+ }
102
+ verifyLog(room, id, head) {
103
+ return this.trust === 'signed' ? Remote.verify(this.h, this.remote(room), id, head) : null;
104
+ }
105
+ pin(room, reader, id, rev) {
106
+ if (!room.archived)
107
+ this.git(['update-ref', `refs/ringi/accepted/${room.genesis}/${reader}/${id}`, rev]);
108
+ }
109
+ unpin(room, reader, id) {
110
+ this.gitOk(['update-ref', '-d', `refs/ringi/accepted/${room.genesis}/${reader}/${id}`]);
111
+ }
112
+ file(rev, p) { return runGitRaw(this.args(['show', `${rev}:${p}`]), this.home); }
113
+ sizes(rev, paths) {
114
+ if (!paths.length)
115
+ return new Map();
116
+ const wanted = new Set(paths);
117
+ return new Map(splitBuf(runGitRaw(this.args(['ls-tree', '-r', '-l', '-z', rev]), this.home), 0)
118
+ .map(b => /^\d+ blob [0-9a-f]+ +(\d+)\t(.*)$/s.exec(b.toString('utf8'))).filter((x) => !!x)
119
+ .filter(([, , p]) => wanted.has(p)).map(([, n, p]) => [p, Number(n)]));
120
+ }
121
+ admit(room, who, key) { return Remote.roomAdmit(this.h, this.remote(room), who, key, this.home); }
122
+ joinKey(room, who) { return Remote.join(this.h, this.remote(room), who, this.home); }
123
+ // A room's transport alarm is the room's, whichever author the operator forgets.
124
+ forgetLog(room, _id) { Remote.forget(this.h, this.remote(room)); }
125
+ rootKey() { return this.d.root ?? fail(`hub ${this.d.name} has no pinned root`); }
126
+ rootRoster() { return Remote.rootRoster(this.h); }
127
+ }
128
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,88 @@
1
+ // marks: tags on a room's commits. Signals are immutable annotated tags `<agent>.<signal>.<message12>`; a heartbeat is
2
+ // one mutable tag `<agent>.heartbeat.<genesis12>` per agent and room, replaced on every beat.
3
+ import * as Remote from './remote.js';
4
+ import { signedTag } from './signing.js';
5
+ import { ZERO, frames, ident, runGitRaw } from './run.js';
6
+ import { signalTags, tagObject } from './tags.js';
7
+ import { Branches } from './branches.js';
8
+ import { fail, note } from '../../lib/index.js';
9
+ export class Marks extends Branches {
10
+ // The heartbeat of `who` in the room: a tag `<who>.heartbeat.<genesis12>` on the room's last commit here, its text the
11
+ // facts of the beat (core/agents/heartbeat). Each beat replaces the object under the same name, compare-and-swap on the one it
12
+ // read, so two writers do not overwrite each other blindly. No reader pins it; its replacement is no alarm.
13
+ // On a signed hub the object is signed by `who` and published with the OID it replaced; a newer beat there wins, and
14
+ // this one is taken back here (remote.ts).
15
+ heartbeat(room, who, target, text) {
16
+ const ref = Remote.heartbeatRef(who, room.genesis), name = ref.slice('refs/tags/'.length);
17
+ const old = this.rev(`${ref}^{tag}`) ? this.git(['rev-parse', ref]).trim() : null;
18
+ const obj = this.trust === 'signed' ? Remote.makeHeartbeat(this.h, this.remote(room), who, target, text, this.home)
19
+ : this.git(['mktag'], { input: `object ${target}\ntype commit\ntag ${name}\ntagger ${who} <${who}@ringi> ${Math.floor(Date.now() / 1000)} +0000\n\n${text}` }).trim();
20
+ if (this.gitOk(['update-ref', ref, obj, old ?? ZERO]).status !== 0)
21
+ fail(`${name} moved meanwhile; the next beat writes it`);
22
+ if (this.trust === 'signed' && !Remote.pushHeartbeat(this.h, this.remote(room), who, obj, old))
23
+ this.gitOk(['update-ref', ...(old ? [ref, old, obj] : ['-d', ref, obj])]);
24
+ return obj;
25
+ }
26
+ // The room's heartbeats as they stand: whose, on which commit, when, and what they say.
27
+ heartbeats(room) {
28
+ const g12 = room.genesis.slice(0, 12);
29
+ const tags = this.git(['for-each-ref', '--format=%(refname) %(objectname) %(objecttype)', 'refs/tags/']).split('\n')
30
+ .map(l => l.split(' ')).filter(([ref, , type]) => type === 'tag' && Remote.heartbeatName(ref ?? '')?.genesis12 === g12);
31
+ const objects = this.tagObjects(tags.map(([, object]) => object));
32
+ // On a signed hub only a verified beat shows.
33
+ const ok = (ref, object) => this.trust !== 'signed' || !Remote.verifyHeartbeat(this.h, this.remote(room), ref, object);
34
+ return tags.flatMap(([ref, object]) => {
35
+ const o = objects.get(object);
36
+ return o && ok(ref, object) ? [{ agent: Remote.heartbeatName(ref).actor, target: o.target, time: o.time, text: o.text }] : [];
37
+ });
38
+ }
39
+ // An annotated tag without text in its author's namespace, on the message's commit, signed on a signed hub. The
40
+ // message's id in the name keeps it unique, so writing the same signal twice is a no-op.
41
+ signal(room, who, target, name) {
42
+ const tag = `${who}.${name}`;
43
+ const ref = `refs/tags/${tag}`;
44
+ if (this.gitOk(['rev-parse', '-q', '--verify', ref]).status === 0) {
45
+ Remote.push(this.h, ref);
46
+ return;
47
+ }
48
+ if (this.h.signing)
49
+ this.git(['update-ref', ref, signedTag(this.h, who, tag, target, this.home), ZERO]);
50
+ else {
51
+ const sign = Remote.sign(this.h, who, this.home);
52
+ const key = sign[0]?.slice(2);
53
+ this.git([...(key ? ['-c', `user.signingkey=${key}`] : []), 'tag', key ? '-s' : '-a', '-F', '-', tag, target], { input: '', env: ident(who) });
54
+ }
55
+ Remote.push(this.h, ref);
56
+ }
57
+ // The tags named `<actor>.<signal>.<message12>`, with the objects they name; a heartbeat is no signal (heartbeats()).
58
+ tags() {
59
+ return signalTags(this.git(['for-each-ref', '--format=%(refname) %(objectname) %(objecttype)', 'refs/tags/']), ref => !!Remote.heartbeatName(ref));
60
+ }
61
+ // Annotated tag objects read whole (run.ts frames): their target, tagger and text are never split on bytes they hold.
62
+ tagObjects(shas) {
63
+ if (!shas.length)
64
+ return new Map();
65
+ const raw = runGitRaw(this.args(['cat-file', '--batch']), this.home, Buffer.from(shas.join('\n') + '\n'));
66
+ return new Map([...frames(raw, shas)].filter(([, f]) => f.type === 'tag').map(([sha, f]) => [sha, tagObject(f.body)]));
67
+ }
68
+ signals(room) {
69
+ const tags = this.tags(), objects = this.tagObjects(tags.filter(t => t.type === 'tag').map(t => t.object));
70
+ const mine = this.commitsOf(room);
71
+ const records = tags.flatMap(t => {
72
+ const o = objects.get(t.object);
73
+ // Only an annotated tag on a commit is a signal; anything else points nowhere.
74
+ if (!mine.has(o?.target || t.object))
75
+ return [];
76
+ return [{ ref: t.ref, object: t.object, name: t.name, actor: t.actor, target: o?.target ?? '', tagger: o?.tagger ?? '', text: o?.text ?? '' }];
77
+ });
78
+ if (this.trust !== 'signed' || room.archived)
79
+ return records;
80
+ return Remote.withVerification(this.h, () => records.filter(s => {
81
+ const why = Remote.verifySignal(this.h, this.remote(room), s.ref, s.object);
82
+ if (why)
83
+ note(`ALARM signal ${s.ref}: ${why}; quarantined and withheld`);
84
+ return !why;
85
+ }));
86
+ }
87
+ }
88
+ //# sourceMappingURL=marks.js.map
@@ -0,0 +1,71 @@
1
+ // admit: a member's key on a signed hub: made on its machine (join) and admitted by the root in a control record.
2
+ import * as C from '../../../lib/index.js';
3
+ import * as P from '../../../core/hub/protocol/index.js';
4
+ import { branch, verifyRoom } from './branch.js';
5
+ import { git, id, ident, isRemote, locked, rev } from './git.js';
6
+ import { keyInput, identityKey, sign, signerLine } from './keys.js';
7
+ import { signCommit } from '../signing.js';
8
+ import { push } from './push.js';
9
+ import { CONTROL_FILES, ROOT } from './types.js';
10
+ export const join = (H, R, who, home) => {
11
+ const room = verifyRoom(H, R);
12
+ const key = locked(H, () => identityKey(H, who, home, true));
13
+ const admitted = !!room.entries.find(e => e.principal === who && e.key.line === key.line);
14
+ return { publicKey: key.line, fingerprint: key.fp, admitted };
15
+ };
16
+ // A tree from `base` with the control files as `control` has them, then `patches` on top.
17
+ const treeWith = (H, base, control, patches) => {
18
+ const entries = (sha) => new Map(git(H, ['ls-tree', sha]).trim().split('\n').filter(Boolean).map(line => {
19
+ const m = /^(\d+) (\w+) ([0-9a-f]+)\t(.+)$/.exec(line) ?? C.fail('malformed room tree');
20
+ return [m[4], line];
21
+ }));
22
+ const files = entries(base), kept = entries(control);
23
+ for (const name of CONTROL_FILES) {
24
+ files.delete(name);
25
+ if (kept.has(name))
26
+ files.set(name, kept.get(name));
27
+ }
28
+ for (const [name, content] of Object.entries(patches))
29
+ files.set(name, `100644 blob ${git(H, ['hash-object', '-w', '--stdin'], { input: content }).trim()}\t${name}`);
30
+ return git(H, ['mktree'], { input: [...files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, line]) => line).join('\n') + '\n' }).trim();
31
+ };
32
+ // The root's control record admitting a member, on top of the room's branch: the key and the member go in together.
33
+ export const roomAdmit = (H, R, who, publicKey, home) => {
34
+ const key = admissionKey(H, R, who, publicKey);
35
+ for (let attempt = 0; attempt < 8; attempt++) {
36
+ const tip = rev(H, R.ref) ?? C.fail('room branch is missing');
37
+ const room = branch(H, R, tip), head = room.control.at(-1);
38
+ if (room.entries.some(e => e.principal === who || e.key.fp === key.fp))
39
+ C.fail('principal or key is already admitted; key rotation is not implemented');
40
+ const { sha, members } = admissionCommit({ H, R, who, home, tip, head, room, key });
41
+ if (git(H, ['update-ref', R.ref, sha, tip], { ok: true }).status !== 0)
42
+ continue;
43
+ if (!push(H, R.ref, { behind: true }))
44
+ continue;
45
+ R.head = sha;
46
+ R.members = members;
47
+ return sha;
48
+ }
49
+ return C.fail('room kept moving while admitting; retry');
50
+ };
51
+ const admissionCommit = ({ H, who, home, tip, head, room, key }) => {
52
+ const members = { ...room.members, [who]: { role: 'member', since: new Date().toISOString() } };
53
+ const roster = [...room.entries.map(e => signerLine(e.principal, e.key)), signerLine(who, key)].join('');
54
+ const tree = treeWith(H, tip, head, { 'members.json': JSON.stringify(members, null, 2) + '\n', allowed_signers: roster });
55
+ const sha = git(H, ['commit-tree', ...(H.signing ? [] : sign(H, ROOT, home)), tree, '-p', tip], {
56
+ input: P.compose(`admit ${who}`, [['To', '*'], ['Kind', 'room'], ['Key', key.fp]]), env: ident(ROOT),
57
+ }).trim();
58
+ return { sha: signCommit(H, ROOT, sha, home), members };
59
+ };
60
+ const admissionKey = (H, R, who, publicKey) => {
61
+ id(who);
62
+ if (!isRemote(H))
63
+ C.fail('room admit is for signed hubs; local membership is added by join');
64
+ if (who === ROOT)
65
+ C.fail('the root is already pinned; root replacement requires a separate trust transition');
66
+ if (R.archived)
67
+ C.fail('cannot admit into an archived room');
68
+ const key = keyInput(publicKey);
69
+ return key;
70
+ };
71
+ //# sourceMappingURL=admit.js.map
@@ -0,0 +1,30 @@
1
+ // alarms: what a rejected push left to say, kept per hub until the next sync settles it.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import * as C from '../../../lib/index.js';
5
+ import * as FS from '../../fs/index.js';
6
+ import { mkdir } from './git.js';
7
+ const pushAlarmsFile = (H) => path.join(H.dir, 'ringi', 'push-alarms.json');
8
+ const alarmState = (text, label) => {
9
+ const value = C.strictJson(text, label);
10
+ if (!value || typeof value !== 'object' || Array.isArray(value) || !Object.values(value).every(v => typeof v === 'string'))
11
+ C.fail(`${label}: expected an object of alarm strings`);
12
+ return value;
13
+ };
14
+ export const savePushAlarms = (H, alarms) => {
15
+ const file = pushAlarmsFile(H);
16
+ mkdir(path.dirname(file));
17
+ FS.writeJsonAtomic(file, alarms);
18
+ };
19
+ export const pushAlarms = (H) => {
20
+ const file = pushAlarmsFile(H);
21
+ try {
22
+ return alarmState(fs.readFileSync(file, 'utf8'), file);
23
+ }
24
+ catch (e) {
25
+ if (C.errno(e) !== 'ENOENT')
26
+ throw e;
27
+ return {};
28
+ }
29
+ };
30
+ //# sourceMappingURL=alarms.js.map
@@ -0,0 +1,47 @@
1
+ // bootstrap: a replica of a published signed hub, verified against the root and genesis the operator gives out of
2
+ // band before its descriptor is written.
3
+ import crypto from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import * as C from '../../../lib/index.js';
7
+ import { id, mkdir } from './git.js';
8
+ import { checkUrl, keyInput } from './keys.js';
9
+ import { command } from './process.js';
10
+ import { sync } from './sync.js';
11
+ import { SHA } from './types.js';
12
+ export const hubAdd = ({ home, name, url, root, genesis }) => {
13
+ id(name);
14
+ checkUrl(url);
15
+ if (!root)
16
+ C.fail('hub add requires --root <public-key|file> from the operator; a key advertised by the hub is not a trust anchor');
17
+ const key = keyInput(root ?? C.fail('hub add requires --root <public-key|file>'));
18
+ if (genesis && !SHA.test(genesis))
19
+ C.fail('--genesis must be a full commit id');
20
+ // A hub is a descriptor (hub.json) beside its store; for a git store the repository is store/.git.
21
+ const hubDir = path.join(home, 'hub', name);
22
+ const dir = path.join(hubDir, 'store', '.git');
23
+ if (fs.existsSync(hubDir))
24
+ C.fail(`hub ${name} exists`);
25
+ mkdir(path.dirname(dir));
26
+ const stage = path.join(home, 'hub', `.${name}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.git`);
27
+ try {
28
+ command('git', ['init', '--bare', '-q', '-b', 'main', stage]);
29
+ const descriptor = {
30
+ schema: 'ringi-hub/2', name, trust: 'signed', root: key.line, genesis,
31
+ store: { type: 'git', path: 'store', remote: url }
32
+ };
33
+ const H = { name, dir: stage, d: descriptor, bootstrap: !genesis };
34
+ sync(H);
35
+ fs.renameSync(stage, dir);
36
+ fs.writeFileSync(path.join(hubDir, 'hub.json'), JSON.stringify(descriptor, null, 2) + '\n', { mode: 0o600 });
37
+ return dir;
38
+ }
39
+ finally {
40
+ if (fs.existsSync(stage))
41
+ fs.rmSync(stage, { recursive: true, force: true });
42
+ // A failed add leaves no half-made hub behind (the directory did not exist before).
43
+ if (!fs.existsSync(dir))
44
+ fs.rmSync(hubDir, { recursive: true, force: true });
45
+ }
46
+ };
47
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1,36 @@
1
+ // branch-commit: one commit of a room's branch, judged: a control record, a valid message of an admitted author, or
2
+ // skipped with the reason.
3
+ import * as C from '../../../lib/index.js';
4
+ import { acceptControl, broken, isControl } from './branch-control.js';
5
+ import { reason } from './git.js';
6
+ import { checkSignature } from './signature.js';
7
+ const acceptMessage = (s, c) => {
8
+ const signer = s.roster.entries.find(e => e.principal === c.author) ?? C.fail(`${c.author || 'its author'} is unadmitted in this room`);
9
+ if (s.touching.has(c.sha))
10
+ C.fail('it changes the room\'s control files, which only the root changes');
11
+ checkSignature(s.H, c.sha, c.author, signer.key);
12
+ s.authors.set(c.sha, c.author);
13
+ };
14
+ const fatal = (s, c, e) => {
15
+ if (!(e instanceof C.Fail))
16
+ return true;
17
+ const brokenRoom = c === s.genesis || e.broken;
18
+ return !!brokenRoom || C.errno(e) === 'ETIMEDOUT';
19
+ };
20
+ export const visitCommit = (s, c) => {
21
+ if (c.parents.length !== 1 || c.parents[0] !== s.parent)
22
+ throw broken('a merge or non-linear history; a room is one linear branch');
23
+ s.parent = c.sha;
24
+ try {
25
+ if (isControl(s, c))
26
+ acceptControl(s, c);
27
+ else
28
+ acceptMessage(s, c);
29
+ }
30
+ catch (e) {
31
+ if (fatal(s, c, e))
32
+ throw e;
33
+ s.skipped.set(c.sha, reason(e));
34
+ }
35
+ };
36
+ //# sourceMappingURL=branch-commit.js.map
@@ -0,0 +1,37 @@
1
+ // branch-control: root-signed control records and the room's roster, as a branch scan meets them.
2
+ import * as C from '../../../lib/index.js';
3
+ import * as P from '../../../core/hub/protocol/index.js';
4
+ import { reason } from './git.js';
5
+ import { rootKey } from './keys.js';
6
+ import { rosterAt } from './roster.js';
7
+ import { checkSignature } from './signature.js';
8
+ import { ROOT, SHA } from './types.js';
9
+ export const broken = (why) => Object.assign(new C.Fail(why), { broken: true });
10
+ export const isControl = (s, c) => c === s.genesis || (c.author === ROOT && P.parse(c.text, x => SHA.test(x)).kind === 'room');
11
+ const rootSignature = (s, c) => {
12
+ try {
13
+ checkSignature(s.H, c.sha, ROOT, rootKey(s.H));
14
+ }
15
+ catch (e) {
16
+ if (c === s.genesis || C.errno(e) === 'ETIMEDOUT')
17
+ throw e;
18
+ C.fail(`${reason(e)}; only the root writes control records`);
19
+ }
20
+ };
21
+ export const acceptControl = (s, c) => {
22
+ rootSignature(s, c);
23
+ let next;
24
+ try {
25
+ next = rosterAt(s.H, c.sha);
26
+ }
27
+ catch (e) {
28
+ throw broken(reason(e));
29
+ }
30
+ for (const e of s.roster.entries) {
31
+ if (!next.entries.some(n => n.principal === e.principal && n.key.line === e.key.line))
32
+ throw broken('room roster drops or changes an admitted key; revocation/freeze is not implemented');
33
+ }
34
+ s.roster = next;
35
+ s.control.push(c.sha);
36
+ };
37
+ //# sourceMappingURL=branch-control.js.map
@@ -0,0 +1,28 @@
1
+ // branch-scan: a room's branch scanned from its genesis to a tip.
2
+ import * as C from '../../../lib/index.js';
3
+ import { visitCommit } from './branch-commit.js';
4
+ import { readCommits } from './commits.js';
5
+ import { git } from './git.js';
6
+ import { verifyHub } from './main.js';
7
+ import { CONTROL_FILES } from './types.js';
8
+ export const scanBranch = (H, name, tip) => {
9
+ const main = verifyHub(H) ?? C.fail('room verification requires a signed hub');
10
+ const shas = git(H, ['rev-list', '--first-parent', '--reverse', tip, `^${main.head}`]).trim().split('\n').filter(Boolean);
11
+ if (!shas.length)
12
+ C.fail(`room ${name} has no genesis`);
13
+ const list = readCommits(H, shas), genesis = list[0];
14
+ if (genesis.parents.length !== 1 || !main.commits.includes(genesis.parents[0]))
15
+ C.fail('room does not descend from the verified hub contract');
16
+ const touching = new Set(git(H, ['log', '--first-parent', '--format=%H', tip, `^${main.head}`, '--', ...CONTROL_FILES]).trim().split('\n').filter(Boolean));
17
+ const scan = {
18
+ H, genesis, touching, parent: genesis.parents[0], roster: { entries: [], members: {} },
19
+ control: [], authors: new Map(), skipped: new Map()
20
+ };
21
+ for (const c of list)
22
+ visitCommit(scan, c);
23
+ return {
24
+ genesis: genesis.sha, subject: genesis.text.split('\n')[0], ...scan.roster,
25
+ control: scan.control, authors: scan.authors, skipped: scan.skipped
26
+ };
27
+ };
28
+ //# sourceMappingURL=branch-scan.js.map
@@ -0,0 +1,37 @@
1
+ // branch: a room's branch as verified up to a tip, kept per tip; its roster, and why a log's head is not valid.
2
+ import * as C from '../../../lib/index.js';
3
+ import { scanBranch } from './branch-scan.js';
4
+ import { memo } from './cache.js';
5
+ import { isRemote, reason } from './git.js';
6
+ // Memoize by immutable tip; ref names and expected genesis are checked outside the cache.
7
+ export const branchData = (H, name, tip, expectedGenesis) => {
8
+ const data = memo(H, `branch/${tip}`, () => scanBranch(H, name, tip));
9
+ if (data.subject !== `room ${name}`)
10
+ C.fail(`room genesis subject must be "room ${name}"`);
11
+ if (expectedGenesis && data.genesis !== expectedGenesis)
12
+ C.fail('room changed its genesis');
13
+ return data;
14
+ };
15
+ // The room's branch verified up to `tip`; a quarantined room is not read.
16
+ export const branch = (H, R, tip) => {
17
+ if (!isRemote(H))
18
+ C.fail('room verification requires a signed hub');
19
+ if (H.remoteAlarms?.[R.ref])
20
+ C.fail(`ALARM ${H.remoteAlarms[R.ref]}; room is quarantined`);
21
+ return branchData(H, R.name, tip, R.genesis);
22
+ };
23
+ // The roster in effect at the room's latest control record.
24
+ export const verifyRoom = (H, R) => branch(H, R, R.head);
25
+ // Why `head` is not a valid message of `who`, or null.
26
+ export const verify = (H, R, who, head) => {
27
+ if (!isRemote(H))
28
+ return null;
29
+ try {
30
+ const b = branch(H, R, head);
31
+ return b.authors.get(head) === who ? null : b.skipped.get(head) ?? `${head.slice(0, 12)} is not a message of ${who}`;
32
+ }
33
+ catch (e) {
34
+ return reason(e);
35
+ }
36
+ };
37
+ //# sourceMappingURL=branch.js.map
@@ -0,0 +1,23 @@
1
+ // cache: checks of immutable heads reused within one operation; nothing survives into the next one.
2
+ // Reuse immutable-head checks within one operation; no ref or alarm survives into the next one.
3
+ const checking = new WeakMap();
4
+ export const withVerification = (H, fn) => {
5
+ if (checking.has(H))
6
+ return fn();
7
+ checking.set(H, new Map());
8
+ try {
9
+ return fn();
10
+ }
11
+ finally {
12
+ checking.delete(H);
13
+ }
14
+ };
15
+ export const memo = (H, key, fn) => {
16
+ const cache = checking.get(H);
17
+ if (cache?.has(key))
18
+ return cache.get(key);
19
+ const value = fn();
20
+ cache?.set(key, value);
21
+ return value;
22
+ };
23
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1,25 @@
1
+ // commits: commit objects read whole: author, parents and message as bytes.
2
+ import * as C from '../../../lib/index.js';
3
+ import * as P from '../../../core/hub/protocol/index.js';
4
+ import { git } from './git.js';
5
+ export const readCommits = (H, shas) => {
6
+ if (!shas.length)
7
+ return [];
8
+ const out = git(H, ['cat-file', '--batch'], { input: Buffer.from(shas.join('\n') + '\n'), raw: true });
9
+ let at = 0;
10
+ return shas.map(sha => {
11
+ const nl = out.indexOf(0x0a, at);
12
+ const [got, type, size] = out.subarray(at, nl).toString('utf8').split(' ');
13
+ if (got !== sha || type !== 'commit')
14
+ C.fail(`malformed commit ${sha.slice(0, 12)}`);
15
+ const body = out.subarray(nl + 1, nl + 1 + Number(size));
16
+ at = nl + 1 + Number(size) + 1;
17
+ const split = body.indexOf(Buffer.from('\n\n'));
18
+ const hdr = body.subarray(0, split < 0 ? body.length : split).toString('utf8');
19
+ const parents = [...hdr.matchAll(/^parent ([0-9a-f]+)$/gm)].map(m => m[1]);
20
+ const author = /^author (.*?) <[^>\n]*> \d+ [+-]\d{4}$/m.exec(hdr)?.[1] ?? '';
21
+ const message = split < 0 ? Buffer.alloc(0) : body.subarray(split + 2);
22
+ return { sha, parents, author, body: message, text: P.decodeText(message).text ?? '' };
23
+ });
24
+ };
25
+ //# sourceMappingURL=commits.js.map
@@ -0,0 +1,35 @@
1
+ // forget: after the operator reviews a room's transport alarm, the remote branch becomes the accepted baseline.
2
+ import { pushAlarms, savePushAlarms } from './alarms.js';
3
+ import { branchData } from './branch.js';
4
+ import { accepted, git, isRemote, locked, rev, tracking, zero } from './git.js';
5
+ // Only the operator command calls this after reviewing a room's transport alarm: the room's branch as the remote has
6
+ // it becomes the accepted baseline, and the displaced local branch is kept as evidence. Nothing is waived: the branch
7
+ // must verify, and its invalid commits stay skipped.
8
+ export const forget = (H, R) => {
9
+ if (!isRemote(H))
10
+ return;
11
+ return locked(H, () => {
12
+ const alarms = pushAlarms(H);
13
+ if (!alarms[R.ref] && !H.remoteAlarms?.[R.ref])
14
+ return;
15
+ replaceBaseline(H, R);
16
+ delete alarms[R.ref];
17
+ savePushAlarms(H, alarms);
18
+ if (H.remoteAlarms)
19
+ delete H.remoteAlarms[R.ref];
20
+ });
21
+ };
22
+ const replaceBaseline = (H, R) => {
23
+ const sha = rev(H, tracking(R.ref));
24
+ if (sha)
25
+ branchData(H, R.name, sha, R.genesis);
26
+ const old = rev(H, R.ref);
27
+ if (old)
28
+ git(H, ['update-ref', `refs/ringi/quarantine/${R.genesis}/${old}`, old]);
29
+ git(H, ['update-ref', '-d', accepted(R.ref)]);
30
+ if (sha)
31
+ git(H, ['update-ref', R.ref, sha, old || zero(H)]);
32
+ else if (old)
33
+ git(H, ['update-ref', '-d', R.ref, old]);
34
+ };
35
+ //# sourceMappingURL=forget.js.map