@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,80 @@
1
+ // host-posix launch: an isolated shell turn settles its group before its result, as the watcher did before this
2
+ // adapter. Detached argv launches leave the caller's event loop. Only this module knows POSIX group signals.
3
+ import { spawn } from 'node:child_process';
4
+ import { sleep } from '../../lib/index.js';
5
+ import { groupAlive } from './groups.js';
6
+ const GRACE = 5000;
7
+ const signalGroup = (pid, sig) => {
8
+ if (pid)
9
+ try {
10
+ process.kill(-pid, sig);
11
+ }
12
+ catch { }
13
+ };
14
+ const detachedSettlement = async (pid) => {
15
+ while (pid && groupAlive(pid))
16
+ await new Promise(resolve => { setTimeout(resolve, 50).unref(); });
17
+ };
18
+ const settle = async (pid) => {
19
+ if (!pid)
20
+ return;
21
+ signalGroup(pid, 'SIGTERM');
22
+ const until = Date.now() + GRACE;
23
+ while (groupAlive(pid) && Date.now() < until)
24
+ await sleep(50);
25
+ if (groupAlive(pid))
26
+ signalGroup(pid, 'SIGKILL');
27
+ while (groupAlive(pid))
28
+ await sleep(50);
29
+ };
30
+ const detached = (r, clock) => {
31
+ const at = clock.wall(), child = spawn(r.file, [...r.args], { stdio: 'ignore', env: r.env, detached: true });
32
+ let finished = false;
33
+ const done = new Promise((resolve, reject) => {
34
+ child.once('error', e => { finished = true; reject(e); });
35
+ child.once('exit', (code, signal) => {
36
+ void detachedSettlement(child.pid).then(() => {
37
+ finished = true;
38
+ resolve({ code, signal, timedOut: false, seconds: Math.round((clock.wall() - at) / 1000) });
39
+ }, reject);
40
+ });
41
+ });
42
+ child.unref();
43
+ // Fire-and-forget launches still handle spawn failures without an unhandled rejection.
44
+ void done.catch(() => { });
45
+ return { done, stop: () => { if (!finished)
46
+ signalGroup(child.pid, 'SIGTERM'); } };
47
+ };
48
+ const shell = (r, clock) => {
49
+ const at = clock.wall(), child = spawn('sh', ['-c', r.shell], { stdio: 'inherit', env: r.env, detached: true });
50
+ let timedOut = false, finished = false;
51
+ const stop = () => { if (!finished)
52
+ signalGroup(child.pid, 'SIGTERM'); };
53
+ const done = new Promise((resolve, reject) => {
54
+ let hard;
55
+ const timer = r.timeout ? setTimeout(() => {
56
+ timedOut = true;
57
+ signalGroup(child.pid, 'SIGTERM');
58
+ hard = setTimeout(() => signalGroup(child.pid, 'SIGKILL'), GRACE);
59
+ }, r.timeout * 1000) : undefined;
60
+ const cleanup = () => {
61
+ clearTimeout(timer);
62
+ clearTimeout(hard);
63
+ process.removeListener('SIGINT', stop);
64
+ process.removeListener('SIGTERM', stop);
65
+ };
66
+ process.once('SIGINT', stop);
67
+ process.once('SIGTERM', stop);
68
+ child.once('error', e => { finished = true; cleanup(); reject(e); });
69
+ child.once('exit', (code, signal) => {
70
+ cleanup();
71
+ void settle(child.pid).then(() => {
72
+ finished = true;
73
+ resolve({ code, signal, timedOut, seconds: Math.round((clock.wall() - at) / 1000) });
74
+ }, reject);
75
+ });
76
+ });
77
+ return { done, stop };
78
+ };
79
+ export const launch = (request, clock) => 'shell' in request ? shell(request, clock) : detached(request, clock);
80
+ //# sourceMappingURL=launch.js.map
@@ -0,0 +1,66 @@
1
+ // host-posix probes: process existence, ancestry and CPU samples on macOS/Linux. Unavailable CPU probes stay null.
2
+ import { spawnSync } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { procStat, seconds } from './stats.js';
6
+ export const supported = process.platform === 'darwin' || process.platform === 'linux';
7
+ export const alive = (pid) => {
8
+ try {
9
+ process.kill(pid, 0);
10
+ return true;
11
+ }
12
+ catch (e) {
13
+ return e.code === 'EPERM';
14
+ }
15
+ };
16
+ const proc = () => {
17
+ let pids;
18
+ try {
19
+ pids = fs.readdirSync('/proc').filter(d => /^\d+$/.test(d));
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ const rows = new Map();
25
+ for (const pid of pids) {
26
+ try {
27
+ const row = procStat(fs.readFileSync(`/proc/${pid}/stat`, 'utf8'));
28
+ if (row)
29
+ rows.set(Number(pid), row);
30
+ }
31
+ catch { /* the process ended meanwhile */ }
32
+ }
33
+ return rows.size ? rows : null;
34
+ };
35
+ export const snapshot = () => {
36
+ if (!supported)
37
+ return null;
38
+ if (process.platform === 'linux') {
39
+ const rows = proc();
40
+ if (rows)
41
+ return rows;
42
+ }
43
+ const r = spawnSync('ps', ['-A', '-o', 'pid=,ppid=,time='], { encoding: 'utf8', timeout: 10000 });
44
+ if (r.status !== 0)
45
+ return null;
46
+ const rows = new Map();
47
+ for (const line of r.stdout.split('\n')) {
48
+ const [pid, ppid, time] = line.trim().split(/\s+/);
49
+ if (pid && time)
50
+ rows.set(Number(pid), { ppid: Number(ppid), cpu: seconds(time) });
51
+ }
52
+ return rows;
53
+ };
54
+ export const ancestors = (first) => {
55
+ const result = [];
56
+ for (let pid = first, i = 0; i < 12 && pid > 1; i++) {
57
+ const r = spawnSync('ps', ['-o', 'ppid=,comm=', '-p', String(pid)], { encoding: 'utf8' });
58
+ const m = r.status === 0 ? /^\s*(\d+)\s+(.*)$/.exec(String(r.stdout ?? '').trim()) : null;
59
+ if (!m)
60
+ break;
61
+ result.push({ pid, name: path.basename(m[2].trim()).toLowerCase() });
62
+ pid = Number(m[1]);
63
+ }
64
+ return result;
65
+ };
66
+ //# sourceMappingURL=probes.js.map
@@ -0,0 +1,27 @@
1
+ // host-posix stats: parsing and folding of process CPU samples; no probes, files or platform selection.
2
+ // Cumulative CPU time: [DD-][HH:]MM:SS[.ss].
3
+ export const seconds = (time) => {
4
+ const [days, rest] = time.includes('-') ? [Number(time.split('-')[0]), time.split('-')[1]] : [0, time];
5
+ return days * 86400 + rest.split(':').reduce((acc, part) => acc * 60 + Number(part), 0);
6
+ };
7
+ // A parenthesized command may itself contain spaces or parentheses; fields follow its last closing parenthesis.
8
+ export const procStat = (line) => {
9
+ const f = line.slice(line.lastIndexOf(')') + 2).split(' ');
10
+ const ppid = Number(f[1]), cpu = (Number(f[11]) + Number(f[12])) / 100;
11
+ return Number.isFinite(ppid) && Number.isFinite(cpu) ? { ppid, cpu } : null;
12
+ };
13
+ export const treeCpu = (pid, rows) => {
14
+ if (!rows?.has(pid))
15
+ return null;
16
+ const children = new Map();
17
+ for (const [p, { ppid }] of rows)
18
+ children.set(ppid, [...children.get(ppid) ?? [], p]);
19
+ let sum = 0;
20
+ for (const stack = [pid]; stack.length;) {
21
+ const p = stack.pop();
22
+ sum += rows.get(p)?.cpu ?? 0;
23
+ stack.push(...children.get(p) ?? []);
24
+ }
25
+ return sum;
26
+ };
27
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1,68 @@
1
+ // keys-file: the existing headless SSH key custody behind Signer. Construction does no I/O; only an explicit
2
+ // resolve(create) makes a key. Public identity is derived from the private key, never from a mutable .pub sidecar.
3
+ import crypto from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { spawnSync } from 'node:child_process';
7
+ import { Fail } from '../../lib/index.js';
8
+ import { guardWrite, mkdirIn } from '../fs/index.js';
9
+ const refusal = (reason, message) => Object.assign(new Fail(message), { reason });
10
+ const name = (value) => {
11
+ if (!value || value === '.' || value === '..' || /[/\\\0]/.test(value))
12
+ throw refusal('denied', 'invalid key owner');
13
+ return value;
14
+ };
15
+ const command = (args, input = '', timeout = 30000) => {
16
+ const r = spawnSync('ssh-keygen', args, { encoding: 'utf8', input, timeout, maxBuffer: 64 * 1024 * 1024,
17
+ env: { ...process.env, SSH_ASKPASS_REQUIRE: 'never' } });
18
+ if (r.error?.code === 'ETIMEDOUT')
19
+ throw refusal('timeout', 'key signing timed out');
20
+ if (r.error || r.status !== 0) {
21
+ const detail = String(r.stderr || r.error?.message || r.signal || 'failed').trim().split('\n').at(-1);
22
+ throw refusal(/passphrase/i.test(detail || '') ? 'interaction-required' : 'unavailable', `ssh-keygen: ${detail}`);
23
+ }
24
+ return r.stdout;
25
+ };
26
+ const identity = (file, timeout = 30000) => {
27
+ const [algorithm, bytes] = command(['-y', '-P', '', '-f', file], '', timeout).trim().split(/\s+/);
28
+ return { key: `${algorithm} ${bytes}`, fingerprint: 'SHA256:' + crypto.createHash('sha256').update(Buffer.from(bytes, 'base64')).digest('base64').replace(/=+$/, '') };
29
+ };
30
+ const remaining = (r) => {
31
+ const ms = r.deadline - performance.now();
32
+ if (!Number.isFinite(ms) || ms <= 0)
33
+ throw refusal('timeout', 'key signing deadline expired');
34
+ if (!r.namespace || /[\r\n\0]/.test(r.namespace))
35
+ throw refusal('denied', 'invalid signature namespace');
36
+ return Math.max(1, Math.min(30000, Math.floor(ms)));
37
+ };
38
+ export const keysFile = (home) => {
39
+ const files = new Map();
40
+ const refs = new Map();
41
+ const keyFile = (key) => files.get(key) ?? (() => { throw refusal('denied', 'key reference belongs to another signer'); })();
42
+ const custody = (hub, principal) => path.join(home, 'keys', name(hub), name(principal));
43
+ return {
44
+ custody,
45
+ resolve: (hub, principal, create = false) => {
46
+ const file = custody(hub, principal);
47
+ if (!fs.existsSync(file)) {
48
+ if (!create)
49
+ throw refusal('unavailable', `no private key for ${principal} on this machine; ${principal === 'operator' ? 'root keys are never obtained from a hub' : 'join here to generate one, then ask the root to admit its public key'}`);
50
+ mkdirIn(home, path.dirname(file));
51
+ guardWrite(home, () => command(['-q', '-t', 'ed25519', '-N', '', '-C', `${principal}@ringi`, '-f', file]));
52
+ }
53
+ const ref = refs.get(file) ?? crypto.randomUUID();
54
+ files.set(ref, file);
55
+ refs.set(file, ref);
56
+ return ref;
57
+ },
58
+ signer: {
59
+ publicIdentity: ref => identity(keyFile(ref)),
60
+ sign: r => {
61
+ const file = keyFile(r.key), publicKey = identity(file, remaining(r));
62
+ const bytes = command(['-Y', 'sign', '-n', r.namespace, '-f', file], Buffer.from(r.payload), remaining(r));
63
+ return { format: 'ssh', bytes: Buffer.from(bytes), fingerprint: publicKey.fingerprint, namespace: r.namespace };
64
+ },
65
+ },
66
+ };
67
+ };
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,79 @@
1
+ // state-fs: the machine's state as files under one directory ($RINGI_HOME, default ~/.ringi). A key path is a path
2
+ // below it and a record is `<key>.json`, written atomically; a lock is a file under `locks/`. The directory is made on
3
+ // first use; one of another version, or anything else there, is refused. A write the sandbox denies fails with one fixed instruction (adapters/fs/write.ts).
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { errno, fail, strictJson } from '../../lib/index.js';
7
+ import { guardWrite, locked as lockFile, mkdirIn, writeJsonAtomic } from '../fs/index.js';
8
+ const HOME_VERSION = 3;
9
+ // State fails closed: unreadable JSON is an error, never a silent fresh start.
10
+ const readJson = (f, dflt) => {
11
+ let raw;
12
+ try {
13
+ raw = fs.readFileSync(f, 'utf8');
14
+ }
15
+ catch (e) {
16
+ if (errno(e) === 'ENOENT')
17
+ return dflt;
18
+ throw e;
19
+ }
20
+ try {
21
+ JSON.parse(raw);
22
+ }
23
+ catch (e) {
24
+ return fail(`${f} is not valid JSON (${e.message}); refusing to continue with lost state`);
25
+ }
26
+ // A field written twice hides a value (a second root pin, say): refused, not read as the last one.
27
+ return strictJson(raw, f);
28
+ };
29
+ // A key's segment as a name in its directory: nothing that leaves it or names it twice.
30
+ const segment = (s) => !s || s === '.' || s === '..' || /[/\\\0]/.test(s) ? fail(`bad state key segment ${JSON.stringify(s)}`) : s;
31
+ export const stateFs = ({ root, build }) => {
32
+ const guard = (fn) => guardWrite(root, fn);
33
+ const dir = (key) => path.join(root, ...key.map(segment));
34
+ const file = (key) => `${dir(key)}.json`;
35
+ const marker = path.join(root, 'home.json');
36
+ const state = {
37
+ root,
38
+ present: () => fs.existsSync(root),
39
+ ensure: () => {
40
+ if (fs.existsSync(marker)) {
41
+ const v = readJson(marker, {}).version;
42
+ if (v !== HOME_VERSION)
43
+ fail(`${root} is a ringi home of version ${v}; this ringi ${build.version} needs version ${HOME_VERSION}: set RINGI_HOME to a new directory for a fresh home; ringi does not migrate`);
44
+ return;
45
+ }
46
+ if (fs.existsSync(root) && fs.readdirSync(root).length)
47
+ fail(`${root} exists and is not a ringi home (no home.json); move it away or set RINGI_HOME`);
48
+ mkdirIn(root, root);
49
+ state.write(['home'], { version: HOME_VERSION });
50
+ },
51
+ locked: (name, fn) => {
52
+ state.ensure();
53
+ return guard(() => lockFile(path.join(state.place(['locks']), `${name}.lock`), fn));
54
+ },
55
+ read: (key, dflt) => readJson(file(key), dflt),
56
+ write: (key, value) => guard(() => {
57
+ mkdirIn(root, path.dirname(file(key)));
58
+ writeJsonAtomic(file(key), value);
59
+ }),
60
+ remove: key => guard(() => {
61
+ fs.rmSync(file(key), { force: true });
62
+ fs.rmSync(dir(key), { recursive: true, force: true });
63
+ }),
64
+ list: key => {
65
+ let names = [];
66
+ try {
67
+ names = fs.readdirSync(dir(key));
68
+ }
69
+ catch { }
70
+ return [...new Set(names.map(n => n.replace(/\.json$/, '')))].sort();
71
+ },
72
+ place: key => {
73
+ mkdirIn(root, dir(key));
74
+ return dir(key);
75
+ },
76
+ };
77
+ return state;
78
+ };
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,32 @@
1
+ // tool-claude: Claude Code. A session says so in its environment (CLAUDECODE, CLAUDE_CODE_SESSION_ID) and is known by
2
+ // its session id, `claude:<id>`. While it works it writes its transcript
3
+ // $CLAUDE_CONFIG_DIR (or ~/.claude)/projects/<dir>/<session id>.jsonl; it reads skills from .claude/skills.
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { UUID, mtime, newestFirst } from '../tool-traces/index.js';
8
+ const transcript = (id) => {
9
+ const root = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), '.claude'), 'projects');
10
+ const dir = newestFirst(root).find(d => fs.existsSync(path.join(root, d, `${id}.jsonl`)));
11
+ return dir ? path.join(root, dir, `${id}.jsonl`) : null;
12
+ };
13
+ // A transcript once found keeps its path; only its time is read again.
14
+ const found = new Map();
15
+ export const claude = {
16
+ id: 'claude',
17
+ detect: env => env.CLAUDECODE === '1' || !!env.CLAUDE_CODE_SESSION_ID,
18
+ processes: ['claude'],
19
+ session: env => env.CLAUDE_CODE_SESSION_ID ? { handle: `claude:${env.CLAUDE_CODE_SESSION_ID}`, pid: Number(env.CLAUDE_PID) || null } : null,
20
+ traceId: s => s.handle.startsWith('claude:') || s.product === 'claude' ? UUID.exec(s.handle)?.[1] ?? null : null,
21
+ traceTime: id => {
22
+ if (!found.has(id)) {
23
+ const f = transcript(id);
24
+ if (!f)
25
+ return null;
26
+ found.set(id, f);
27
+ }
28
+ return mtime(found.get(id));
29
+ },
30
+ skillDirs: base => [path.join(base, '.claude', 'skills', 'ringi')],
31
+ };
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,38 @@
1
+ // tool-codex: Codex. A session says so in its environment (CODEX_*); a chat names itself on every ringi command with
2
+ // RINGI_SESSION=codex-<thread id>. While it works it writes its rollout
3
+ // $CODEX_HOME (or ~/.codex)/sessions/<yyyy>/<mm>/<dd>/rollout-<time>-<thread id>.jsonl; it reads skills from
4
+ // .agents/skills, and its sandbox writes where its writable roots allow.
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { UUID, mtime, newestFirst } from '../tool-traces/index.js';
8
+ const rollout = (id) => {
9
+ const root = path.join(process.env.CODEX_HOME ?? path.join(os.homedir(), '.codex'), 'sessions');
10
+ for (const y of newestFirst(root))
11
+ for (const m of newestFirst(path.join(root, y)))
12
+ for (const d of newestFirst(path.join(root, y, m))) {
13
+ const f = newestFirst(path.join(root, y, m, d)).find(n => n.startsWith('rollout-') && n.endsWith(`-${id}.jsonl`));
14
+ if (f)
15
+ return path.join(root, y, m, d, f);
16
+ }
17
+ return null;
18
+ };
19
+ // A rollout once found keeps its path; only its time is read again.
20
+ const found = new Map();
21
+ export const codex = {
22
+ id: 'codex',
23
+ detect: env => Object.keys(env).some(k => k.startsWith('CODEX_')),
24
+ processes: ['codex'],
25
+ traceId: s => s.product === 'codex' || s.handle.startsWith('env:codex-') ? UUID.exec(s.handle)?.[1] ?? null : null,
26
+ traceTime: id => {
27
+ if (!found.has(id)) {
28
+ const f = rollout(id);
29
+ if (!f)
30
+ return null;
31
+ found.set(id, f);
32
+ }
33
+ return mtime(found.get(id));
34
+ },
35
+ skillDirs: base => [path.join(base, '.agents', 'skills', 'ringi')],
36
+ sandboxHint: where => `Codex: the narrowest grant is [sandbox_workspace_write] writable_roots = ["${where}"] in ~/.codex/config.toml.`,
37
+ };
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,8 @@
1
+ // tool-cursor: Cursor, recognised only: a session says so in its environment (CURSOR_*) or by its process. It names no
2
+ // session, writes no trace ringi reads, and gets no skill.
3
+ export const cursor = {
4
+ id: 'cursor',
5
+ detect: env => Object.keys(env).some(k => k.startsWith('CURSOR_')),
6
+ processes: ['cursor', 'cursor-agent'],
7
+ };
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ // tool-traces: what the tool adapters share to find a session's trace: a session id at the end of a name, directories
2
+ // newest first, and when a file was last written.
3
+ import fs from 'node:fs';
4
+ export const UUID = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/;
5
+ export const newestFirst = (d) => { try {
6
+ return fs.readdirSync(d).sort().reverse();
7
+ }
8
+ catch {
9
+ return [];
10
+ } };
11
+ // Unix seconds, or null when the file is gone.
12
+ export const mtime = (f) => { try {
13
+ return fs.statSync(f).mtimeMs / 1000;
14
+ }
15
+ catch {
16
+ return null;
17
+ } };
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,155 @@
1
+ // branches: a room is one linear branch from its genesis. What its commits are (messages or control records), the
2
+ // room's control files, and the room's life: create, archive, delete.
3
+ import { fail } from '../../lib/index.js';
4
+ import * as Remote from './remote.js';
5
+ import { ZERO } from './run.js';
6
+ import { Repo } from './repo.js';
7
+ import { ID_RE, parse } from '../../core/hub/protocol/index.js';
8
+ // The root's principal, and the files only its control records may change.
9
+ const ROOT = 'operator';
10
+ export const CONTROL_FILES = new Set(['ROOM.md', 'members.json', 'profile.json', 'allowed_signers']);
11
+ export class Branches extends Repo {
12
+ ref(name) { return name.includes('@') ? `refs/archive/${name}` : `refs/heads/${name}`; }
13
+ tip(name) { return this.rev(this.ref(name)); }
14
+ // Every branch but the contract's, by name.
15
+ roomBranches() {
16
+ return this.git(['for-each-ref', '--format=%(refname:lstrip=2)', 'refs/heads/']).split('\n').filter(b => b && b !== 'main');
17
+ }
18
+ // Every branch but the contract's is a room; an archive is `<room>@<genesis8>`.
19
+ roomNames(archived) {
20
+ const prefix = archived ? 'refs/archive/' : 'refs/heads/';
21
+ const names = this.git(['for-each-ref', '--format=%(refname)', prefix]).split('\n').filter(Boolean).map(r => r.slice(prefix.length))
22
+ .filter(n => ID_RE.test(n.split('@')[0]) && n !== 'main' && n.includes('@') === archived);
23
+ return names.sort();
24
+ }
25
+ // A control record: the room's genesis, or a root commit of Kind: room.
26
+ isControl(room, sha, author, text) {
27
+ return sha === room.genesis || (author === ROOT && parse(text, x => this.isRef(x)).kind === 'room');
28
+ }
29
+ // The room's branch, oldest first, from its genesis. The last scan of each room is kept and grown by what the tip
30
+ // added on top of it.
31
+ scanned = new Map();
32
+ entries(room, tip) {
33
+ const last = this.scanned.get(room.genesis);
34
+ if (last?.tip === tip)
35
+ return last.entries;
36
+ const added = last ? this.scan(room, tip, last.tip) : [];
37
+ const entries = last && added.length && added[0].parent === last.tip ? [...last.entries, ...added] : this.scan(room, tip);
38
+ const judged = this.judged(room, tip, entries);
39
+ this.scanned.set(room.genesis, { tip, entries: judged });
40
+ return judged;
41
+ }
42
+ // On a signed hub the branch's verification says which commits are control records, and why the others that are no
43
+ // valid message are skipped; a commit off the branch's first parents is never a message (remote.ts).
44
+ judged(room, tip, entries) {
45
+ if (this.trust !== 'signed' || room.archived)
46
+ return entries;
47
+ const b = Remote.branch(this.h, { name: room.name, ref: this.ref(room.name), genesis: room.genesis }, tip);
48
+ const control = new Set(b.control);
49
+ return entries.map(e => ({ ...e, control: control.has(e.sha),
50
+ skipped: control.has(e.sha) || b.authors.has(e.sha) ? undefined : b.skipped.get(e.sha) ?? 'not on the room\'s branch' }));
51
+ }
52
+ scan(room, tip, after) {
53
+ const shas = this.git(['rev-list', '--topo-order', '--reverse', tip, '--not', after ?? `${room.genesis}^@`]).trim().split('\n').filter(Boolean);
54
+ return Remote.readCommits(this.h, shas).map(c => {
55
+ const text = c.body.toString('utf8');
56
+ return { sha: c.sha, parent: c.parents[0], author: c.author, text, control: this.isControl(room, c.sha, c.author, text) };
57
+ });
58
+ }
59
+ room(name) {
60
+ const ref = this.ref(name);
61
+ const tip = this.tip(name);
62
+ if (!tip)
63
+ return null;
64
+ const genesis = this.git(['rev-list', '--first-parent', '--reverse', tip, '^refs/heads/main']).split('\n')[0]
65
+ || this.git(['rev-list', '--first-parent', '--reverse', tip]).split('\n').filter(Boolean).at(-1);
66
+ const archived = name.includes('@');
67
+ if (this.trust === 'signed' && !archived && this.h.remoteAlarms?.[ref])
68
+ fail(`ALARM ${this.h.remoteAlarms[ref]}; room ${name} is quarantined`);
69
+ const control = this.entries({ name, genesis, archived }, tip).filter(e => e.control).map(e => ({ rev: e.sha, text: e.text }));
70
+ return { name, archived, head: control.at(-1).rev, genesis, tip, base: this.rev(`${genesis}^`) ?? ZERO, control };
71
+ }
72
+ // Control files are read at a control record, never at the branch's tip.
73
+ controlFile(room, name, rev) {
74
+ const r = this.gitOk(['show', `${rev ?? room.head}:${name}`]);
75
+ return r.status === 0 ? r.stdout : null;
76
+ }
77
+ createRoom(name, who, text, files) {
78
+ const ref = `refs/heads/${name}`;
79
+ if (this.rev(ref))
80
+ fail(`room ${name} exists in hub ${this.d.name}`);
81
+ const sha = this.commit(who, text, this.tree(null, Object.entries(files).map(([p, c]) => ({ path: p, content: c, mode: '100644' }))), [this.contractHead()]);
82
+ if (!this.advance(ref, sha, null))
83
+ fail(`room ${name} was created meanwhile`);
84
+ return sha;
85
+ }
86
+ // A control record on top of the branch: the control files come from the last control record, never from the tip,
87
+ // and the messages' files are kept.
88
+ updateControl(room, who, text, files) {
89
+ const kept = [...CONTROL_FILES].flatMap(n => { const c = this.controlFile(room, n); return c === null ? [] : [[n, c]]; });
90
+ const all = { ...Object.fromEntries(kept), ...files };
91
+ return this.onTop(room, who, text, Object.entries(all).map(([p, c]) => ({ path: p, content: c, mode: '100644' })), [...CONTROL_FILES]);
92
+ }
93
+ refsUnder(prefix) {
94
+ return this.git(['for-each-ref', '--format=%(refname) %(objectname)', prefix]).split('\n').filter(Boolean).map(l => l.split(' '));
95
+ }
96
+ // The branch moves under the archive; its signals stay where they are, on its commits.
97
+ archiveRoom(room, as) {
98
+ const tip = this.rev(this.ref(room.name)) ?? fail(`room ${room.name} is gone`);
99
+ this.git(['update-ref', `refs/archive/${as}`, tip, ZERO]);
100
+ this.git(['update-ref', '-d', this.ref(room.name), tip]);
101
+ }
102
+ // Every ref of the room in one transaction: its branch (or archive), every tag on its commits (signals, heartbeats,
103
+ // anyone's), what the store keeps under its genesis (pins, evidence) and its unpublished commits. Objects are left to
104
+ // the repository.
105
+ deleteRoom(room) {
106
+ const commits = this.commitsOf(room);
107
+ const tags = this.git(['for-each-ref', '--format=%(refname) %(objectname) %(*objectname)', 'refs/tags/']).split('\n').filter(Boolean)
108
+ .map(l => l.split(' ')).filter(([, object, target]) => commits.has(target || object)).map(([ref]) => ref);
109
+ const kept = this.refsUnder('refs/ringi/').map(([ref]) => ref).filter(ref => {
110
+ const [, , kind, key] = ref.split('/');
111
+ return key === room.genesis || (kind === 'unpublished' && commits.has(key));
112
+ });
113
+ const refs = [this.ref(room.name), ...tags, ...kept];
114
+ this.git(['update-ref', '--stdin'], { input: ['start', ...refs.map(r => `delete ${r}`), 'prepare', 'commit', ''].join('\n') });
115
+ }
116
+ remote(room) {
117
+ return { name: room.name, ref: this.ref(room.name), head: room.head, genesis: room.genesis, archived: room.archived };
118
+ }
119
+ roster(room) {
120
+ if (this.trust !== 'signed' || room.archived)
121
+ return null;
122
+ const v = Remote.verifyRoom(this.h, this.remote(room));
123
+ return { members: v.members, signers: v.entries };
124
+ }
125
+ // Logs are read at the branch's live tip, as a reader sees the room now.
126
+ live(room) {
127
+ const tip = this.tip(room.name);
128
+ return tip ? this.entries(room, tip).filter(e => !e.control && !e.skipped) : [];
129
+ }
130
+ // What a signed hub skips in the room's branch, as alarms; the room reads on without it.
131
+ alarms(room) {
132
+ const tip = this.tip(room.name);
133
+ return tip ? this.entries(room, tip).filter(e => e.skipped).map(e => `commit ${e.sha.slice(0, 12)} by ${e.author || '(none)'}: ${e.skipped}`) : [];
134
+ }
135
+ // A commit on the branch's tip, compare-and-swap: when another writer got there first, it is made again on the new tip.
136
+ onTop(room, who, text, entries, drop = []) {
137
+ const ref = this.ref(room.name);
138
+ for (let i = 0; i < 8; i++) {
139
+ const tip = this.rev(ref) ?? fail(`room ${room.name} is gone`);
140
+ if (drop.length && this.room(room.name)?.head !== room.head)
141
+ fail('room control moved while writing; retry the operation');
142
+ const tree = entries.length || drop.length ? this.tree(`${tip}^{tree}`, entries, drop) : this.git(['rev-parse', `${tip}^{tree}`]).trim();
143
+ const sha = this.commit(who, text, tree, [tip]);
144
+ if (this.advance(ref, sha, tip))
145
+ return sha;
146
+ }
147
+ return fail(`room ${room.name} kept moving while writing; retry`);
148
+ }
149
+ // Every commit of the room's branch: a signal belongs to the room whose branch holds its message.
150
+ commitsOf(room) {
151
+ const tip = this.tip(room.name);
152
+ return new Set(tip ? this.entries(room, tip).map(e => e.sha) : []);
153
+ }
154
+ }
155
+ //# sourceMappingURL=branches.js.map
@@ -0,0 +1,48 @@
1
+ // files: which files a message carries — the explicit Ringi-Files list, or for records without one the paths its
2
+ // commit adds or changes — and which it refuses.
3
+ import { checkCollisions, checkPath } from '../../core/hub/protocol/files.js';
4
+ import { parse } from '../../core/hub/protocol/index.js';
5
+ import { splitBuf } from './run.js';
6
+ const REGULAR = new Set(['100644', '100755']);
7
+ const RAW = /^:\d{6} (\d{6}) \S+ \S+ ([A-Z])\d*\t(.*)$/;
8
+ const TREE = /^(\d+) \w+ [0-9a-f]+\t(.*)$/s;
9
+ const INVALID = { path: 'Ringi-Files', mode: 'invalid', why: 'expected one JSON array of safe, distinct file paths' };
10
+ // The paths a commit adds or changes over its parent (`git log --raw`).
11
+ const changed = (raw) => raw.split('\n').map(l => RAW.exec(l)).filter((x) => !!x)
12
+ .filter(([, , status]) => status !== 'D').map(([, mode, , p]) => ({ path: p, mode }));
13
+ // The Ringi-Files list's paths, or null when it is not one JSON array of safe, distinct paths.
14
+ const declaredPaths = (declared) => {
15
+ try {
16
+ const paths = JSON.parse(declared[0]);
17
+ if (declared.length !== 1 || !Array.isArray(paths) || !paths.every(p => typeof p === 'string'))
18
+ return null;
19
+ const names = paths.map(checkPath);
20
+ checkCollisions(names);
21
+ return new Set(names).size === names.length ? names : null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ };
27
+ // The declared paths as the commit's tree holds them; one the tree lacks is `missing`.
28
+ const inTree = (names, tree) => {
29
+ const wanted = new Set(names);
30
+ const found = splitBuf(tree, 0).map(b => TREE.exec(b.toString('utf8'))).filter((x) => !!x)
31
+ .map(([, mode, p]) => ({ path: p, mode })).filter(f => wanted.has(f.path));
32
+ for (const f of found)
33
+ wanted.delete(f.path);
34
+ return [...found, ...[...wanted].map(p => ({ path: p, mode: 'missing' }))];
35
+ };
36
+ const listed = (declared, sha, lsTree) => {
37
+ const names = declaredPaths(declared);
38
+ if (!names)
39
+ return { all: [], refused: [INVALID] };
40
+ return { all: names.length ? inTree(names, lsTree(sha)) : [], refused: [] };
41
+ };
42
+ // A message's regular files, and what it refuses: an invalid list, a link, a missing file.
43
+ export const messageFiles = (body, rest, sha, isRef, lsTree) => {
44
+ const declared = parse(body.toString('utf8'), isRef).trailers['ringi-files'];
45
+ const { all, refused } = declared ? listed(declared, sha, lsTree) : { all: changed(rest.toString('utf8')), refused: [] };
46
+ return { files: all.filter(f => REGULAR.has(f.mode)), refused: [...refused, ...all.filter(f => !REGULAR.has(f.mode))] };
47
+ };
48
+ //# sourceMappingURL=files.js.map