@idleware/ringi 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/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 +42 -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,58 @@
1
+ // git: the isolated git invocation the signed transport uses for every read, write and signature check.
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 * as P from '../../../core/hub/protocol/index.js';
7
+ import { withVerification } from './cache.js';
8
+ import { BASE, command } from './process.js';
9
+ import { ACCEPT, TRACK } from './types.js';
10
+ export const reason = (e) => e instanceof Error ? e.message : String(e);
11
+ export const mkdir = (d) => fs.mkdirSync(d, { recursive: true, mode: 0o700 });
12
+ export const id = (s) => P.ID_RE.test(s) ? s : C.fail(`bad agent or hub id ${s}`);
13
+ export const zero = (H) => '0'.repeat(git(H, ['rev-parse', '--show-object-format']).trim() === 'sha256' ? 64 : 40);
14
+ export const ident = (who) => ({ GIT_AUTHOR_NAME: who, GIT_AUTHOR_EMAIL: `${who}@ringi`, GIT_COMMITTER_NAME: who, GIT_COMMITTER_EMAIL: `${who}@ringi` });
15
+ export function git(H, args, opt) {
16
+ return command('git', ['--git-dir', H.dir, ...BASE, ...args], opt);
17
+ }
18
+ export const config = (H, key, value) => value === undefined
19
+ ? String(git(H, ['config', '--get', key], { ok: true }).stdout || '').trim()
20
+ : git(H, ['config', key, String(value)]);
21
+ export const rev = (H, ref) => {
22
+ const r = git(H, ['rev-parse', '-q', '--verify', `${ref}^{commit}`], { ok: true });
23
+ return r.status === 0 ? r.stdout.trim() : null;
24
+ };
25
+ export const object = (H, ref) => {
26
+ const r = git(H, ['rev-parse', '-q', '--verify', ref], { ok: true });
27
+ return r.status === 0 ? r.stdout.trim() : null;
28
+ };
29
+ export const ancestor = (H, a, b) => !!a && !!b && git(H, ['merge-base', '--is-ancestor', a, b], { ok: true }).status === 0;
30
+ export const isRemote = (H) => {
31
+ if (H.d.trust !== 'signed' && H.d.trust !== 'asserted')
32
+ C.fail(`unsupported hub trust ${String(H.d.trust)}`);
33
+ return H.d.trust === 'signed';
34
+ };
35
+ export const origin = (H) => H.d.store.remote;
36
+ export const suffix = (ref) => ref.replace(/^refs\/heads\//, '');
37
+ export const accepted = (ref) => ACCEPT + suffix(ref);
38
+ export const tracking = (ref) => TRACK + suffix(ref);
39
+ // Transport and home state use the same owner-aware lock.
40
+ export const locked = (H, fn) => {
41
+ const file = path.join(H.dir, 'ringi', 'transport.lock');
42
+ mkdir(path.dirname(file));
43
+ return FS.locked(file, () => withVerification(H, fn));
44
+ };
45
+ export const refs = (H, prefix) => new Map(git(H, ['for-each-ref', '--format=%(refname) %(objectname)', prefix]).trim().split('\n').filter(Boolean).map(s => s.split(' ')));
46
+ // The local accepted transport refs survive reader resets and retain evidence of remote rewrites/deletions.
47
+ export const promote = (H, ref, sha) => {
48
+ const acc = rev(H, accepted(ref));
49
+ const cur = rev(H, ref);
50
+ if (acc && !ancestor(H, acc, sha))
51
+ C.fail(`${suffix(ref)} was rewritten or rolled back from ${acc.slice(0, 12)} to ${sha.slice(0, 12)}`);
52
+ if (cur && cur !== sha && !ancestor(H, cur, sha) && !ancestor(H, sha, cur))
53
+ C.fail(`${suffix(ref)} diverges from the local accepted history`);
54
+ if (!cur || (cur !== sha && ancestor(H, cur, sha)))
55
+ git(H, ['update-ref', ref, sha, cur || zero(H)]);
56
+ git(H, ['update-ref', accepted(ref), sha, acc || zero(H)]);
57
+ };
58
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1,35 @@
1
+ // heartbeat-facts: the text of a heartbeat object, its fields checked.
2
+ import * as C from '../../../lib/index.js';
3
+ import * as P from '../../../core/hub/protocol/index.js';
4
+ import { SHA } from './types.js';
5
+ export const heartbeatBody = (text) => {
6
+ if (Buffer.byteLength(text) > 16384)
7
+ C.fail('heartbeat body exceeds 16 KiB');
8
+ const decoded = P.decodeText(Buffer.from(text));
9
+ if (decoded.invalid)
10
+ C.fail(`heartbeat body is ${decoded.invalid}`);
11
+ const lines = decoded.text.trimEnd().split('\n');
12
+ const fields = heartbeatFields(lines);
13
+ const genesis = fields.get('Room') ?? C.fail('heartbeat must name its full room genesis');
14
+ if (!SHA.test(genesis))
15
+ C.fail('heartbeat must name its full room genesis');
16
+ if (!/^(working|waiting|gone)$/.test(fields.get('State') ?? ''))
17
+ C.fail('invalid heartbeat State');
18
+ if (!/^(yes|no|unknown)$/.test(fields.get('Alive') ?? ''))
19
+ C.fail('invalid heartbeat Alive');
20
+ const next = Number(fields.get('Next'));
21
+ if (!Number.isFinite(next) || next <= 0)
22
+ C.fail('heartbeat Next must be positive seconds');
23
+ return genesis;
24
+ };
25
+ const heartbeatFields = (lines) => {
26
+ const fields = new Map();
27
+ for (const line of lines) {
28
+ const m = /^([A-Z][A-Za-z]*): (.+)$/.exec(line) ?? C.fail('heartbeat body must contain only facts as trailers');
29
+ if (fields.has(m[1]))
30
+ C.fail(`duplicate heartbeat field ${m[1]}`);
31
+ fields.set(m[1], m[2]);
32
+ }
33
+ return fields;
34
+ };
35
+ //# sourceMappingURL=heartbeat-facts.js.map
@@ -0,0 +1,19 @@
1
+ // heartbeat-name: the one mutable tag name, `<actor>.heartbeat.<genesis12>`; every other tag is a signal.
2
+ import * as C from '../../../lib/index.js';
3
+ import { SHA, TAGS } from './types.js';
4
+ // Heartbeats deliberately share the tag transport, but never the immutable signal acceptance.
5
+ // Only this exact namespace is mutable; e.g. `actor.opened.heartbeat.<id>` is still a signal.
6
+ export const HEARTBEAT_NAME = /^([a-z0-9]+(?:[_-][a-z0-9]+)*)\.heartbeat\.([0-9a-f]{12})$/;
7
+ export const heartbeatName = (ref) => {
8
+ const m = ref.startsWith(TAGS) && HEARTBEAT_NAME.exec(ref.slice(TAGS.length));
9
+ return m ? { actor: m[1], genesis12: m[2] } : null;
10
+ };
11
+ export const heartbeatRef = (who, genesis) => {
12
+ if (!SHA.test(genesis))
13
+ C.fail('heartbeat requires a full room genesis');
14
+ const ref = `${TAGS}${who}.heartbeat.${genesis.slice(0, 12)}`;
15
+ if (!heartbeatName(ref))
16
+ C.fail('malformed heartbeat actor');
17
+ return ref;
18
+ };
19
+ //# sourceMappingURL=heartbeat-name.js.map
@@ -0,0 +1,40 @@
1
+ // heartbeat-object: a heartbeat tag object read and checked: its target in the room, its tagger, its signature.
2
+ import * as C from '../../../lib/index.js';
3
+ import * as P from '../../../core/hub/protocol/index.js';
4
+ import { branch } from './branch.js';
5
+ import { git, rev } from './git.js';
6
+ import { heartbeatBody } from './heartbeat-facts.js';
7
+ import { heartbeatName } from './heartbeat-name.js';
8
+ import { checkSignature } from './signature.js';
9
+ import { SHA, TAGS } from './types.js';
10
+ export const heartbeatObject = (H, ref, sha) => {
11
+ const s = heartbeatName(ref) ?? C.fail('malformed heartbeat namespace');
12
+ if (!SHA.test(sha) || git(H, ['cat-file', '-t', sha]).trim() !== 'tag')
13
+ C.fail('heartbeat must be an annotated tag');
14
+ if (Number(git(H, ['cat-file', '-s', sha])) > 32768)
15
+ C.fail('heartbeat object exceeds 32 KiB');
16
+ const decoded = P.decodeText(git(H, ['cat-file', 'tag', sha], { raw: true }));
17
+ if (decoded.invalid)
18
+ C.fail(`heartbeat tag is ${decoded.invalid}`);
19
+ const [header, ...rest] = decoded.text.split('\n\n');
20
+ const target = /^object ([0-9a-f]+)\ntype commit\ntag ([^\n]+)\ntagger ([^<\n]+) <[^\n]*> (\d+) [+-]\d{4}\n?$/.exec(header) ?? C.fail('malformed heartbeat tag headers');
21
+ if (!SHA.test(target[1]) || target[2] !== ref.slice(TAGS.length) || target[3].trim() !== s.actor)
22
+ C.fail('heartbeat tag name or actor disagrees');
23
+ const text = rest.join('\n\n').split('-----BEGIN SSH SIGNATURE-----')[0];
24
+ const genesis = heartbeatBody(text);
25
+ if (!genesis.startsWith(s.genesis12))
26
+ C.fail('heartbeat tag name and room genesis disagree');
27
+ return { ...s, genesis, target: target[1], time: Number(target[4]), text };
28
+ };
29
+ export const checkHeartbeat = (H, R, ref, sha) => {
30
+ const s = heartbeatObject(H, ref, sha);
31
+ if (s.genesis !== R.genesis)
32
+ C.fail('heartbeat belongs to another room genesis');
33
+ const b = branch(H, R, rev(H, R.ref) ?? C.fail('heartbeat room has no branch here'));
34
+ // It is a replica's sync position, not a claim to author or execute the target message.
35
+ if (!b.control.includes(s.target) && !b.authors.has(s.target) && !b.skipped.has(s.target))
36
+ C.fail('heartbeat target is not on this room branch');
37
+ const signer = b.entries.find(e => e.principal === s.actor) ?? C.fail(`unadmitted heartbeat principal ${s.actor}`);
38
+ checkSignature(H, sha, s.actor, signer.key, 'tag');
39
+ };
40
+ //# sourceMappingURL=heartbeat-object.js.map
@@ -0,0 +1,44 @@
1
+ // heartbeat-push: a beat published with a lease on the object it replaces; a stale update loses.
2
+ import * as C from '../../../lib/index.js';
3
+ import { git, isRemote, locked, object, origin } from './git.js';
4
+ import { checkHeartbeat, heartbeatRef } from './heartbeat.js';
5
+ import { checkUrl } from './keys.js';
6
+ import { SHA, TAG_ACCEPT, TAG_TRACK, TAGS } from './types.js';
7
+ // Publish only the actor's exact heartbeat with an explicit old OID. A racing beat is ordinary contention, never a
8
+ // signal rewrite alarm. Neither success nor failure changes immutable acceptance pins or budget/history state.
9
+ export const pushHeartbeat = (H, R, who, sha, expected) => {
10
+ const ref = heartbeatRef(who, R.genesis);
11
+ if (!SHA.test(sha) || (expected !== null && !SHA.test(expected)))
12
+ C.fail('heartbeat publication requires full object ids');
13
+ if (!isRemote(H))
14
+ return true;
15
+ return locked(H, () => publishHeartbeat({ H, R, ref, sha, expected }));
16
+ };
17
+ const publishHeartbeat = ({ H, R, ref, sha, expected }) => {
18
+ if (object(H, ref) !== sha)
19
+ return false;
20
+ if (object(H, TAG_ACCEPT + ref.slice(TAGS.length)))
21
+ C.fail('heartbeat namespace overlaps previously accepted signal history');
22
+ checkHeartbeat(H, R, ref, sha);
23
+ const url = origin(H);
24
+ if (!url)
25
+ return true;
26
+ checkUrl(url);
27
+ const r = git(H, ['push', '-q', '--porcelain', `--force-with-lease=${ref}:${expected ?? ''}`, url, `${sha}:${ref}`], { ok: true });
28
+ if (r.status !== 0 && !reconcileHeartbeat(H, { url, ref, sha, stdout: r.stdout, stderr: r.stderr }))
29
+ return false;
30
+ git(H, ['update-ref', TAG_TRACK + ref.slice(TAGS.length), sha]);
31
+ if (H.heartbeatErrors)
32
+ delete H.heartbeatErrors[ref];
33
+ return true;
34
+ };
35
+ const reconcileHeartbeat = (H, { url, ref, sha, stdout, stderr }) => {
36
+ const observed = git(H, ['ls-remote', '--refs', url, ref], { ok: true });
37
+ const got = observed.status === 0 ? observed.stdout.trim().split(/\s+/) : [];
38
+ if (got[0] === sha && got[1] === ref)
39
+ return true;
40
+ if (/\[rejected\].*\(stale info\)/.test(stdout))
41
+ return false;
42
+ return C.fail(`heartbeat push failed for ${ref.slice(TAGS.length)}: ${String(stderr || stdout || 'transport error').trim().split('\n').at(-1)}`);
43
+ };
44
+ //# sourceMappingURL=heartbeat-push.js.map
@@ -0,0 +1,44 @@
1
+ // heartbeat: a signed heartbeat made and verified; it is published by heartbeat-push.ts.
2
+ import * as C from '../../../lib/index.js';
3
+ import { git, isRemote, reason } from './git.js';
4
+ import { heartbeatBody } from './heartbeat-facts.js';
5
+ import { heartbeatRef } from './heartbeat-name.js';
6
+ import { checkHeartbeat } from './heartbeat-object.js';
7
+ import { signBytes } from '../signing.js';
8
+ import { SHA, TAGS } from './types.js';
9
+ export { checkHeartbeat, heartbeatObject } from './heartbeat-object.js';
10
+ export { heartbeatBody } from './heartbeat-facts.js';
11
+ export { HEARTBEAT_NAME, heartbeatName, heartbeatRef } from './heartbeat-name.js';
12
+ export const verifyHeartbeat = (H, R, ref, sha) => {
13
+ if (!isRemote(H))
14
+ return null;
15
+ if (H.remoteAlarms?.[ref])
16
+ return H.remoteAlarms[ref];
17
+ if (H.heartbeatErrors?.[ref])
18
+ return H.heartbeatErrors[ref];
19
+ try {
20
+ checkHeartbeat(H, R, ref, sha);
21
+ return null;
22
+ }
23
+ catch (e) {
24
+ return reason(e);
25
+ }
26
+ };
27
+ // Keep the transport's existing call contract while object construction has one named input.
28
+ export const makeHeartbeat = (...[H, R, who, target, text, home]) => createHeartbeat({ H, R, who, target, text, home });
29
+ const createHeartbeat = ({ H, R, who, target, text, home }) => {
30
+ const ref = heartbeatRef(who, R.genesis);
31
+ if (!SHA.test(target))
32
+ C.fail('heartbeat target must be a full commit id');
33
+ if (/^Room:/m.test(text))
34
+ C.fail('heartbeat Room is transport metadata');
35
+ const body = `Room: ${R.genesis}\n${text.trimEnd()}\n`;
36
+ heartbeatBody(body);
37
+ const raw = `object ${target}\ntype commit\ntag ${ref.slice(TAGS.length)}\ntagger ${who} <${who}@ringi> ${Math.floor(Date.now() / 1000)} +0000\n\n${body}`;
38
+ const signature = signBytes(H, who, raw, home);
39
+ const sha = git(H, ['mktag'], { input: raw + signature }).trim();
40
+ if (isRemote(H))
41
+ checkHeartbeat(H, R, ref, sha);
42
+ return sha;
43
+ };
44
+ //# sourceMappingURL=heartbeat.js.map
@@ -0,0 +1,75 @@
1
+ // keys: public keys as given and pinned, a signed hub's root and its signers, and which key signs for a principal.
2
+ import crypto from 'node:crypto';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import * as C from '../../../lib/index.js';
6
+ import { config, id, isRemote, mkdir, rev } from './git.js';
7
+ import { command } from './process.js';
8
+ import { ROOT } from './types.js';
9
+ export const parseKey = (value) => {
10
+ const text = String(value || '').trim();
11
+ const m = /^(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp(?:256|384|521)) ([A-Za-z0-9+/]+={0,2})(?: [^\r\n]*)?$/.exec(text) ?? C.fail('expected one SSH public key (or a file containing it)');
12
+ if (m[2].length > 16384)
13
+ C.fail('expected one SSH public key (or a file containing it)');
14
+ const data = Buffer.from(m[2], 'base64');
15
+ if (data.length < 8 || data.readUInt32BE(0) > data.length - 4 || data.subarray(4, 4 + data.readUInt32BE(0)).toString() !== m[1])
16
+ C.fail('malformed SSH public key');
17
+ const line = `${m[1]} ${m[2]}`;
18
+ return { line, fp: 'SHA256:' + crypto.createHash('sha256').update(data).digest('base64').replace(/=+$/, '') };
19
+ };
20
+ export const keyInput = (value) => parseKey(fs.existsSync(value) && fs.statSync(value).isFile() ? fs.readFileSync(value, 'utf8') : value);
21
+ export const rootKey = (H) => parseKey(H.d.root || C.fail(`hub ${H.name} has no out-of-band root pin; use hub add --root <public-key|file>`));
22
+ export const signerLine = (who, key) => `${who} namespaces="git" ${key.line}\n`;
23
+ const keyPath = (H, who, home) => path.join(home, 'keys', id(H.name), id(who));
24
+ const localKey = (H, who, home, create = false) => {
25
+ const file = keyPath(H, who, home);
26
+ if (!fs.existsSync(file)) {
27
+ if (!create)
28
+ C.fail(`no private key for ${who} on this machine; ${who === ROOT ? 'root keys are never obtained from a hub' : 'join here to generate one, then ask the root to admit its public key'}`);
29
+ mkdir(path.dirname(file));
30
+ command('ssh-keygen', ['-q', '-t', 'ed25519', '-N', '', '-C', `${who}@ringi`, '-f', file]);
31
+ }
32
+ // Derive from the private key instead of trusting a mutable .pub sidecar.
33
+ const key = parseKey(command('ssh-keygen', ['-y', '-f', file]));
34
+ return { ...key, file };
35
+ };
36
+ export const identityKey = (H, who, home, create = false) => H.signing
37
+ ? parseKey(H.signing.signer.publicIdentity(H.signing.resolve(id(H.name), id(who), create)).key)
38
+ : localKey(H, who, home, create);
39
+ export const setupSigned = (H, home, url) => {
40
+ id(H.name);
41
+ if (rev(H, 'refs/heads/main'))
42
+ C.fail('setupSigned only initializes an empty hub; it cannot replace an existing trust anchor');
43
+ const key = identityKey(H, ROOT, home, true);
44
+ H.d.trust = 'signed';
45
+ H.d.root = key.line;
46
+ config(H, 'gpg.format', 'ssh');
47
+ config(H, 'gpg.ssh.program', 'ssh-keygen');
48
+ if (url) {
49
+ checkUrl(url);
50
+ H.d.store.remote = url;
51
+ }
52
+ return { root: key.line, allowedSigners: signerLine(ROOT, key) };
53
+ };
54
+ export const sign = (H, who, home) => {
55
+ if (!isRemote(H))
56
+ return [];
57
+ const key = localKey(H, who, home);
58
+ if (who === ROOT && key.line !== rootKey(H).line)
59
+ C.fail('the local operator key does not match this hub\'s pinned root');
60
+ config(H, 'gpg.format', 'ssh');
61
+ config(H, 'gpg.ssh.program', 'ssh-keygen');
62
+ return [`-S${key.file}`];
63
+ };
64
+ export const rootRoster = (H) => signerLine(ROOT, rootKey(H));
65
+ const supportedUrl = (url) => {
66
+ const malformed = /[\r\n\0]/.test(url) || url.startsWith('-');
67
+ const cleartext = /^http:\/\//i.test(url);
68
+ const transport = /^(?:file|https|ssh):\/\/|^(?:[^\s/@:]+@)?[^\s/:]+:[^\s]+$|^\//.test(url);
69
+ return !malformed && !cleartext && transport;
70
+ };
71
+ export const checkUrl = (url) => {
72
+ if (typeof url !== 'string' || !supportedUrl(url))
73
+ C.fail('use a file://, https:// or ssh Git URL; cleartext HTTP and custom transport helpers are refused');
74
+ };
75
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1,41 @@
1
+ // main: the hub's contract branch verified: every commit signed by the root since the pinned genesis.
2
+ import * as C from '../../../lib/index.js';
3
+ import { memo } from './cache.js';
4
+ import { git, isRemote, rev } from './git.js';
5
+ import { parseKey, rootKey } from './keys.js';
6
+ import { blobText } from './roster.js';
7
+ import { chain, checkSignature, linear } from './signature.js';
8
+ import { ROOT } from './types.js';
9
+ export const verifyMain = (H, head) => memo(H, `main/${head}`, () => {
10
+ const key = rootKey(H);
11
+ const commits = chain(H, head);
12
+ if (!commits.length)
13
+ C.fail('signed hub has no genesis');
14
+ const pin = H.d.genesis;
15
+ if (!pin && !H.bootstrap)
16
+ C.fail(`hub ${H.name} has no pinned genesis`);
17
+ if (pin && commits[0].sha !== pin)
18
+ C.fail('hub does not descend from the pinned genesis');
19
+ linear(commits, null, sha => checkContract(H, sha, key));
20
+ if (!pin)
21
+ H.d.genesis = commits[0].sha;
22
+ H.bootstrap = false;
23
+ return { head, genesis: commits[0].sha, commits: commits.map(c => c.sha) };
24
+ });
25
+ export const verifyHub = (H) => {
26
+ if (!isRemote(H))
27
+ return null;
28
+ const head = rev(H, 'refs/heads/main') || C.fail('signed hub has no main');
29
+ return verifyMain(H, head);
30
+ };
31
+ const checkContract = (H, sha, key) => {
32
+ checkSignature(H, sha, ROOT, key);
33
+ const name = 'contract.json';
34
+ const meta = C.strictJson(blobText(H, sha, name), name);
35
+ if (meta.schema !== 'ringi-hub' || meta.trust !== 'signed' || parseKey(meta.root).line !== key.line)
36
+ C.fail(`${name} must declare signed trust and the pinned root key`);
37
+ const authority = git(H, ['cat-file', '-e', `${sha}:authority.json`], { ok: true });
38
+ if (authority.status === 0)
39
+ C.strictJson(blobText(H, sha, 'authority.json'), 'authority.json');
40
+ };
41
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1,37 @@
1
+ // process: running git and ssh-keygen isolated from the user's configuration, with a timeout.
2
+ import { spawnSync } from 'node:child_process';
3
+ import os from 'node:os';
4
+ import * as C from '../../../lib/index.js';
5
+ export const ENV = { GIT_CONFIG_GLOBAL: os.devNull, GIT_CONFIG_NOSYSTEM: '1', GIT_TERMINAL_PROMPT: '0', GIT_NO_REPLACE_OBJECTS: '1', LC_ALL: 'C' };
6
+ export const BASE = ['-c', 'core.hooksPath=' + os.devNull, '-c', 'gpg.format=ssh', '-c', 'gpg.ssh.program=ssh-keygen',
7
+ '-c', 'color.ui=false', '-c', 'log.showSignature=false', '-c', 'core.quotePath=false',
8
+ '-c', 'protocol.allow=never', '-c', 'protocol.file.allow=always', '-c', 'protocol.ssh.allow=always',
9
+ '-c', 'protocol.https.allow=always', '-c', 'http.followRedirects=false'];
10
+ export const gitEnv = (extra = {}) => ({
11
+ ...Object.fromEntries(Object.entries(process.env).filter(([k]) => !/^(GIT_CONFIG_|GIT_DIR$|GIT_WORK_TREE$|GIT_INDEX_FILE$|GIT_OBJECT_DIRECTORY$|GIT_ALTERNATE_OBJECT_DIRECTORIES$|GIT_REPLACE_REF_BASE$)/.test(k))),
12
+ ...ENV, ...extra,
13
+ });
14
+ export const gitTimeout = () => {
15
+ const ms = Number(process.env.RINGI_GIT_TIMEOUT_MS ?? 30000);
16
+ if (!Number.isSafeInteger(ms) || ms < 1)
17
+ C.fail('RINGI_GIT_TIMEOUT_MS must be a positive integer (milliseconds)');
18
+ return ms;
19
+ };
20
+ export const checkGitTimeout = (error) => {
21
+ if (error?.code === 'ETIMEDOUT')
22
+ throw Object.assign(new C.Fail('git timed out; increase RINGI_GIT_TIMEOUT_MS (positive integer milliseconds)'), { code: 'ETIMEDOUT' });
23
+ };
24
+ export function command(cmd, args, opt = {}) {
25
+ const r = spawnSync(cmd, args, {
26
+ input: opt.input, encoding: opt.raw ? 'buffer' : 'utf8', env: gitEnv(opt.env),
27
+ maxBuffer: 64 * 1024 * 1024, timeout: cmd === 'git' ? gitTimeout() : 30000
28
+ });
29
+ if (cmd === 'git')
30
+ checkGitTimeout(r.error);
31
+ if (r.error && !opt.ok)
32
+ C.fail(`${cmd}: ${r.error.message}`);
33
+ if (r.status !== 0 && !opt.ok)
34
+ C.fail(`${cmd}: ${String(r.stderr || r.error?.message || r.signal || 'failed').trim().split('\n').at(-1)}`);
35
+ return opt.ok ? r : r.stdout;
36
+ }
37
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1,46 @@
1
+ // push-retry: a lost acknowledgement reconciled, or one unpublished append kept and its caller moved onto the
2
+ // verified remote tip to make it again.
3
+ import * as C from '../../../lib/index.js';
4
+ import { savePushAlarms } from './alarms.js';
5
+ import { branchData } from './branch.js';
6
+ import { ancestor, git, promote, rev, suffix } from './git.js';
7
+ const fetchIncoming = ({ H, ref, track, url, sha, acc }) => {
8
+ git(H, ['-c', 'fetch.fsckObjects=true', 'fetch', '-q', '--no-tags', url, `+${ref}:${track}`]);
9
+ const incoming = rev(H, track) ?? C.fail('remote room branch is missing');
10
+ const data = branchData(H, suffix(ref), sha);
11
+ branchData(H, suffix(ref), incoming, data.genesis);
12
+ if (acc && !ancestor(H, acc, incoming))
13
+ C.fail('remote room was rewritten or rolled back; retry refused');
14
+ return incoming;
15
+ };
16
+ const retryable = ({ H, sha, acc }, stdout) => {
17
+ const parents = git(H, ['show', '-s', '--format=%P', sha]).trim().split(' ');
18
+ const rejected = /\[rejected\].*\((?:fetch first|non-fast-forward)\)/.test(String(stdout));
19
+ return rejected && !!acc && parents.length === 1 && parents[0] === acc;
20
+ };
21
+ const retryRefs = ({ H, sha, ref, pin, acc, alarms }, incoming) => {
22
+ git(H, ['update-ref', `refs/ringi/unpublished/${sha}`, sha]);
23
+ git(H, ['update-ref', ref, incoming, sha]);
24
+ git(H, ['update-ref', pin, incoming, acc]);
25
+ delete alarms[ref];
26
+ savePushAlarms(H, alarms);
27
+ if (H.remoteAlarms)
28
+ delete H.remoteAlarms[ref];
29
+ };
30
+ export const recoverPublication = (p, stdout) => {
31
+ const incoming = fetchIncoming(p);
32
+ if (ancestor(p.H, p.sha, incoming)) {
33
+ // Acknowledgement lost after publication: the returned message id remains unchanged.
34
+ promote(p.H, p.ref, incoming);
35
+ delete p.alarms[p.ref];
36
+ savePushAlarms(p.H, p.alarms);
37
+ return true;
38
+ }
39
+ if (retryable(p, stdout)) {
40
+ // Only one unpublished message is re-made by its caller; retain its original object.
41
+ retryRefs(p, incoming);
42
+ return false;
43
+ }
44
+ return null;
45
+ };
46
+ //# sourceMappingURL=push-retry.js.map
@@ -0,0 +1,39 @@
1
+ // push-validate: a ref checked before it is published: main, a room's branch, or an actor's signal on a message of a
2
+ // verified room.
3
+ import * as C from '../../../lib/index.js';
4
+ import * as P from '../../../core/hub/protocol/index.js';
5
+ import { branchData } from './branch.js';
6
+ import { ancestor, refs } from './git.js';
7
+ import { verifyMain } from './main.js';
8
+ import { checkSignal, signalObject } from './signals.js';
9
+ import { TAGS } from './types.js';
10
+ export const validateRef = (H, ref, sha) => {
11
+ if (ref.startsWith(TAGS)) {
12
+ return validateSignalRef(H, ref, sha);
13
+ }
14
+ if (ref === 'refs/heads/main') {
15
+ verifyMain(H, sha);
16
+ return;
17
+ }
18
+ const room = /^refs\/heads\/([^/]+)$/.exec(ref) ?? C.fail('only explicit main, room or signal refs may be pushed');
19
+ if (!P.ID_RE.test(room[1]))
20
+ C.fail('only explicit main, room or signal refs may be pushed');
21
+ const why = branchData(H, room[1], sha).skipped.get(sha);
22
+ if (why)
23
+ C.fail(`${sha.slice(0, 12)} is no valid commit of room ${room[1]}: ${why}`);
24
+ };
25
+ const validateSignalRef = (H, ref, sha) => {
26
+ const s = signalObject(H, ref, sha);
27
+ for (const [branchRef, head] of refs(H, 'refs/heads/')) {
28
+ const name = branchRef.slice('refs/heads/'.length);
29
+ if (name === 'main' || name.includes('/') || !ancestor(H, s.message, head))
30
+ continue;
31
+ const b = branchData(H, name, head);
32
+ if (b.control.includes(s.message) || b.authors.has(s.message) || b.skipped.has(s.message)) {
33
+ checkSignal(H, { name, ref: branchRef, head: b.control.at(-1), genesis: b.genesis }, ref, sha);
34
+ return;
35
+ }
36
+ }
37
+ return C.fail('signal points at no verified room');
38
+ };
39
+ //# sourceMappingURL=push-validate.js.map
@@ -0,0 +1,62 @@
1
+ // Publish one immutable ref. Room append retries preserve already returned message ids.
2
+ import * as C from '../../../lib/index.js';
3
+ import { pushAlarms, savePushAlarms } from './alarms.js';
4
+ import { accepted, ancestor, git, isRemote, locked, object, origin, rev, suffix, tracking, zero } from './git.js';
5
+ import { heartbeatName } from './heartbeat.js';
6
+ import { checkUrl } from './keys.js';
7
+ import { recoverPublication } from './push-retry.js';
8
+ import { validateRef } from './push-validate.js';
9
+ import { TAGS, TAG_ACCEPT, TAG_TRACK } from './types.js';
10
+ export { validateRef } from './push-validate.js';
11
+ const preparePublication = (H, ref) => {
12
+ const alarms = pushAlarms(H), tag = ref.startsWith(TAGS);
13
+ const pin = tag ? TAG_ACCEPT + ref.slice(TAGS.length) : accepted(ref);
14
+ const track = tag ? TAG_TRACK + ref.slice(TAGS.length) : tracking(ref);
15
+ const sha = (tag ? object(H, ref) : rev(H, ref)) || C.fail(`no ${ref} to publish`);
16
+ validateRef(H, ref, sha);
17
+ return { H, ref, alarms, tag, pin, track, sha };
18
+ };
19
+ const rejectPublication = (p, behind, stdout) => {
20
+ let why = '';
21
+ if (behind && !p.tag && p.ref !== 'refs/heads/main') {
22
+ try {
23
+ const recovered = recoverPublication(p, stdout);
24
+ if (recovered !== null)
25
+ return recovered;
26
+ }
27
+ catch (e) {
28
+ why = `; ${e instanceof Error ? e.message : String(e)}`;
29
+ }
30
+ }
31
+ p.alarms[p.ref] = `push rejected for ${suffix(p.ref)}; local commit ${p.sha.slice(0, 12)} retained; resolve the remote conflict before continuing${why}`;
32
+ savePushAlarms(p.H, p.alarms);
33
+ return C.fail(`ALARM ${p.alarms[p.ref]}`);
34
+ };
35
+ const pushUnlocked = (H, ref, behind) => {
36
+ const prepared = preparePublication(H, ref);
37
+ const url = origin(H);
38
+ if (!url)
39
+ return true;
40
+ checkUrl(url);
41
+ const { tag, pin, sha, track, alarms } = prepared;
42
+ const acc = tag ? object(H, pin) : rev(H, pin);
43
+ if (acc && (tag ? acc !== sha : !ancestor(H, acc, sha)))
44
+ C.fail('ref to publish does not extend its accepted transport history');
45
+ const p = { ...prepared, acc, url };
46
+ const r = git(H, ['push', '-q', '--porcelain', url, `${sha}:${ref}`], { ok: true });
47
+ if (r.status !== 0)
48
+ return rejectPublication(p, behind, r.stdout);
49
+ delete alarms[ref];
50
+ savePushAlarms(H, alarms);
51
+ git(H, ['update-ref', pin, sha, acc || zero(H)]);
52
+ git(H, ['update-ref', track, sha]);
53
+ return true;
54
+ };
55
+ export const push = (H, ref, { behind = false } = {}) => {
56
+ if (heartbeatName(ref))
57
+ C.fail('heartbeat publication requires its actor and expected object id (pushHeartbeat)');
58
+ if (!isRemote(H))
59
+ return true;
60
+ return locked(H, () => pushUnlocked(H, ref, behind));
61
+ };
62
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1,48 @@
1
+ // roster: who may sign a room's logs at a control record: its signers file and members, checked against each other.
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
+ import { parseKey, rootKey } from './keys.js';
6
+ import { ROOT } from './types.js';
7
+ export const blobText = (H, sha, name) => {
8
+ const entry = git(H, ['ls-tree', sha, '--', name]).trim();
9
+ if (!/^100(?:644|755) blob [0-9a-f]+\t/.test(entry))
10
+ C.fail(`${name} at ${sha.slice(0, 12)} must be a regular blob`);
11
+ const raw = git(H, ['cat-file', 'blob', `${sha}:${name}`], { raw: true });
12
+ const d = P.decodeText(raw);
13
+ if (d.invalid)
14
+ C.fail(`${name} at ${sha.slice(0, 12)} is ${d.invalid}`);
15
+ return d.text;
16
+ };
17
+ export const rosterAt = (H, sha) => {
18
+ const entries = blobText(H, sha, 'allowed_signers').split('\n').map(s => s.trim()).filter(s => s && !s.startsWith('#')).map(line => {
19
+ const m = /^(\S+) namespaces="git" (.+)$/.exec(line) ?? C.fail('malformed allowed_signers; only literal principals and git namespace are supported');
20
+ if (!P.ID_RE.test(m[1]))
21
+ C.fail('malformed allowed_signers; only literal principals and git namespace are supported');
22
+ return { principal: m[1], key: parseKey(m[2]) };
23
+ });
24
+ const principals = uniqueRoster(entries);
25
+ const members = checkedMembers(H, sha, entries, principals);
26
+ return { entries, members };
27
+ };
28
+ const uniqueRoster = (entries) => {
29
+ const keys = new Set(), principals = new Set();
30
+ for (const e of entries) {
31
+ if (keys.has(e.key.fp) || principals.has(e.principal))
32
+ C.fail('duplicate key or principal in allowed_signers; rotation requires an explicit revocation protocol');
33
+ keys.add(e.key.fp);
34
+ principals.add(e.principal);
35
+ }
36
+ return principals;
37
+ };
38
+ const checkedMembers = (H, sha, entries, principals) => {
39
+ const members = C.strictJson(blobText(H, sha, 'members.json'), 'members.json');
40
+ if (!members || typeof members !== 'object' || Array.isArray(members))
41
+ C.fail('members.json must be an object');
42
+ if (entries.find(e => e.principal === ROOT)?.key.line !== rootKey(H).line || members[ROOT]?.role !== 'root')
43
+ C.fail('room roster must retain the pinned operator as root');
44
+ if (Object.keys(members).some(who => !P.ID_RE.test(who) || !principals.has(who) || (who !== ROOT && members[who]?.role === 'root')) || entries.some(e => !members[e.principal]))
45
+ C.fail('members.json and allowed_signers disagree');
46
+ return members;
47
+ };
48
+ //# sourceMappingURL=roster.js.map