@idleware/ringi 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/target/dts/adapters/fs/index.d.ts +2 -0
- package/target/dts/adapters/fs/lock.d.ts +1 -0
- package/target/dts/adapters/fs/write.d.ts +5 -0
- package/target/dts/adapters/host-posix/group-state.d.ts +7 -0
- package/target/dts/adapters/host-posix/groups.d.ts +1 -0
- package/target/dts/adapters/host-posix/index.d.ts +2 -0
- package/target/dts/adapters/host-posix/launch.d.ts +7 -0
- package/target/dts/adapters/host-posix/probes.d.ts +5 -0
- package/target/dts/adapters/host-posix/stats.d.ts +9 -0
- package/target/dts/adapters/keys-file/index.d.ts +2 -0
- package/target/dts/adapters/state-fs/index.d.ts +5 -0
- package/target/dts/adapters/tool-claude/index.d.ts +2 -0
- package/target/dts/adapters/tool-codex/index.d.ts +2 -0
- package/target/dts/adapters/tool-cursor/index.d.ts +2 -0
- package/target/dts/adapters/tool-traces/index.d.ts +3 -0
- package/target/dts/adapters/transport-git/branches.d.ts +43 -0
- package/target/dts/adapters/transport-git/files.d.ts +2 -0
- package/target/dts/adapters/transport-git/index.d.ts +32 -0
- package/target/dts/adapters/transport-git/marks.d.ts +20 -0
- package/target/dts/adapters/transport-git/remote/admit.d.ts +7 -0
- package/target/dts/adapters/transport-git/remote/alarms.d.ts +3 -0
- package/target/dts/adapters/transport-git/remote/bootstrap.d.ts +7 -0
- package/target/dts/adapters/transport-git/remote/branch-commit.d.ts +3 -0
- package/target/dts/adapters/transport-git/remote/branch-control.d.ts +15 -0
- package/target/dts/adapters/transport-git/remote/branch-scan.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/branch.d.ts +5 -0
- package/target/dts/adapters/transport-git/remote/cache.d.ts +3 -0
- package/target/dts/adapters/transport-git/remote/commits.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/forget.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/git.d.ts +26 -0
- package/target/dts/adapters/transport-git/remote/heartbeat-facts.d.ts +1 -0
- package/target/dts/adapters/transport-git/remote/heartbeat-name.d.ts +6 -0
- package/target/dts/adapters/transport-git/remote/heartbeat-object.d.ts +10 -0
- package/target/dts/adapters/transport-git/remote/heartbeat-push.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/heartbeat.d.ts +7 -0
- package/target/dts/adapters/transport-git/remote/keys.d.ts +13 -0
- package/target/dts/adapters/transport-git/remote/main.d.ts +11 -0
- package/target/dts/adapters/transport-git/remote/process.d.ts +20 -0
- package/target/dts/adapters/transport-git/remote/push-retry.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/push-validate.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/push.d.ts +16 -0
- package/target/dts/adapters/transport-git/remote/roster.d.ts +7 -0
- package/target/dts/adapters/transport-git/remote/signals.d.ts +15 -0
- package/target/dts/adapters/transport-git/remote/signature.d.ts +5 -0
- package/target/dts/adapters/transport-git/remote/sync-heartbeat.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/sync-rooms.d.ts +6 -0
- package/target/dts/adapters/transport-git/remote/sync-tags.d.ts +8 -0
- package/target/dts/adapters/transport-git/remote/sync.d.ts +2 -0
- package/target/dts/adapters/transport-git/remote/types.d.ts +64 -0
- package/target/dts/adapters/transport-git/remote.d.ts +17 -0
- package/target/dts/adapters/transport-git/repo.d.ts +31 -0
- package/target/dts/adapters/transport-git/run.d.ts +21 -0
- package/target/dts/adapters/transport-git/signing.d.ts +4 -0
- package/target/dts/adapters/transport-git/tags.d.ts +15 -0
- package/target/dts/adapters/transport-git/transport.d.ts +7 -0
- package/target/dts/adapters/transport-kafka/index.d.ts +2 -0
- package/target/dts/adapters/transport-temporal/index.d.ts +2 -0
- package/target/dts/cli.d.ts +2 -1
- package/target/dts/{cli/agent.d.ts → clients/cli/agent/index.d.ts} +3 -3
- package/target/dts/{cli → clients/cli/agent}/session.d.ts +2 -2
- package/target/dts/clients/cli/arg-parse.d.ts +2 -0
- package/target/dts/{cli → clients/cli}/args.d.ts +2 -2
- package/target/dts/clients/cli/commands.d.ts +2 -0
- package/target/dts/clients/cli/context.d.ts +4 -0
- package/target/dts/{cli/index.d.ts → clients/cli/help.d.ts} +1 -3
- package/target/dts/clients/cli/index.d.ts +4 -0
- package/target/dts/clients/cli/operator/heartbeat-format.d.ts +5 -0
- package/target/dts/clients/cli/operator/heartbeat.d.ts +3 -0
- package/target/dts/clients/cli/operator/hub-commands.d.ts +6 -0
- package/target/dts/clients/cli/operator/hub.d.ts +2 -0
- package/target/dts/clients/cli/operator/index.d.ts +5 -0
- package/target/dts/clients/cli/operator/room-lifecycle.d.ts +5 -0
- package/target/dts/clients/cli/operator/room-list.d.ts +5 -0
- package/target/dts/clients/cli/operator/room-members.d.ts +4 -0
- package/target/dts/clients/cli/operator/room.d.ts +2 -0
- package/target/dts/clients/cli/operator/sessions.d.ts +5 -0
- package/target/dts/clients/cli/operator/watch.d.ts +2 -0
- package/target/dts/clients/cli/operator/where-format.d.ts +7 -0
- package/target/dts/clients/cli/operator/where.d.ts +2 -0
- package/target/dts/clients/cli/shared/budget.d.ts +7 -0
- package/target/dts/clients/cli/shared/files.d.ts +4 -0
- package/target/dts/clients/cli/shared/index.d.ts +7 -0
- package/target/dts/clients/cli/shared/messages.d.ts +7 -0
- package/target/dts/clients/cli/shared/profile.d.ts +4 -0
- package/target/dts/clients/cli/shared/session.d.ts +6 -0
- package/target/dts/clients/cli/shared/signals.d.ts +3 -0
- package/target/dts/clients/cli/shared/work.d.ts +6 -0
- package/target/dts/clients/cli/skill/index.d.ts +3 -0
- package/target/dts/{cli/skill.d.ts → clients/cli/skill/targets.d.ts} +1 -2
- package/target/dts/composition/index.d.ts +2 -0
- package/target/dts/{heartbeat → core/agents/heartbeat}/index.d.ts +2 -4
- package/target/dts/{app/heartbeat.d.ts → core/agents/heartbeat/pulse.d.ts} +5 -6
- package/target/dts/core/agents/heartbeat/singleton.d.ts +3 -0
- package/target/dts/core/agents/heartbeat/tools.d.ts +6 -0
- package/target/dts/{app → core/agents}/overview.d.ts +12 -6
- package/target/dts/core/agents/ports/host.d.ts +47 -0
- package/target/dts/core/agents/ports/index.d.ts +9 -0
- package/target/dts/core/agents/ports/tools.d.ts +18 -0
- package/target/dts/core/agents/sessions/agent.d.ts +70 -0
- package/target/dts/core/agents/sessions/binding.d.ts +9 -0
- package/target/dts/core/agents/sessions/context.d.ts +8 -0
- package/target/dts/{runtime/session.d.ts → core/agents/sessions/index.d.ts} +2 -9
- package/target/dts/core/agents/sessions/join.d.ts +20 -0
- package/target/dts/core/agents/sessions/resume.d.ts +2 -0
- package/target/dts/core/agents/sessions/session.d.ts +46 -0
- package/target/dts/core/agents/sessions/wait.d.ts +38 -0
- package/target/dts/core/agents/turns/budgets.d.ts +5 -0
- package/target/dts/core/agents/turns/dispatch.d.ts +9 -0
- package/target/dts/core/agents/turns/exhaustion.d.ts +11 -0
- package/target/dts/core/agents/turns/grants.d.ts +4 -0
- package/target/dts/core/agents/turns/index.d.ts +4 -0
- package/target/dts/core/agents/turns/limits.d.ts +2 -0
- package/target/dts/core/agents/turns/messages.d.ts +10 -0
- package/target/dts/core/agents/turns/pings.d.ts +5 -0
- package/target/dts/core/agents/turns/plan.d.ts +3 -0
- package/target/dts/core/agents/turns/poll.d.ts +8 -0
- package/target/dts/core/agents/turns/results.d.ts +3 -0
- package/target/dts/core/agents/turns/seat.d.ts +2 -0
- package/target/dts/core/agents/turns/types.d.ts +59 -0
- package/target/dts/core/hub/decisions/budget.d.ts +15 -0
- package/target/dts/core/hub/decisions/check.d.ts +6 -0
- package/target/dts/core/hub/decisions/index.d.ts +5 -0
- package/target/dts/core/hub/decisions/proposal.d.ts +38 -0
- package/target/dts/core/hub/decisions/usage.d.ts +12 -0
- package/target/dts/{runtime/reader.d.ts → core/hub/history/index.d.ts} +12 -6
- package/target/dts/core/hub/ports/index.d.ts +16 -0
- package/target/dts/core/hub/ports/state.d.ts +12 -0
- package/target/dts/{store/index.d.ts → core/hub/ports/store.d.ts} +48 -18
- package/target/dts/core/hub/protocol/index.d.ts +5 -0
- package/target/dts/{protocol/index.d.ts → core/hub/protocol/model.d.ts} +0 -25
- package/target/dts/core/hub/protocol/order.d.ts +2 -0
- package/target/dts/core/hub/protocol/reading.d.ts +8 -0
- package/target/dts/core/hub/protocol/text.d.ts +11 -0
- package/target/dts/core/hub/protocol/threads.d.ts +5 -0
- package/target/dts/core/hub/rooms/compat.d.ts +5 -0
- package/target/dts/core/hub/rooms/hubs.d.ts +14 -0
- package/target/dts/core/hub/rooms/index.d.ts +53 -0
- package/target/dts/core/hub/rooms/messages.d.ts +8 -0
- package/target/dts/core/hub/rooms/rooms.d.ts +10 -0
- package/target/dts/core/hub/signals/check.d.ts +3 -0
- package/target/dts/core/hub/signals/effective.d.ts +3 -0
- package/target/dts/core/hub/signals/index.d.ts +5 -0
- package/target/dts/core/hub/signals/marks.d.ts +4 -0
- package/target/dts/core/hub/signals/model.d.ts +28 -0
- package/target/dts/core/hub/signals/pins.d.ts +5 -0
- package/target/dts/{hub/signals.d.ts → core/hub/signals/write.d.ts} +1 -1
- package/target/dts/core/hub/usecases/actor.d.ts +6 -0
- package/target/dts/{app → core/hub/usecases}/budget.d.ts +5 -18
- package/target/dts/core/hub/usecases/config.d.ts +6 -0
- package/target/dts/{app → core/hub/usecases}/context.d.ts +3 -8
- package/target/dts/{app → core/hub/usecases}/hubs.d.ts +1 -1
- package/target/dts/core/hub/usecases/propose.d.ts +17 -0
- package/target/dts/{app → core/hub/usecases}/room.d.ts +8 -34
- package/target/dts/{app → core/hub/usecases}/rooms.d.ts +2 -6
- package/target/dts/core/hub/usecases/roster.d.ts +5 -0
- package/target/dts/core/hub/usecases/send.d.ts +21 -0
- package/target/dts/core/hub/usecases/staging.d.ts +8 -0
- package/target/dts/{app → core/hub/usecases}/view.d.ts +4 -4
- package/target/dts/{domain/profiles.d.ts → core/hub/work/index.d.ts} +3 -7
- package/target/dts/core/hub/work/parse.d.ts +2 -0
- package/target/dts/core/hub/work/task.d.ts +6 -0
- package/target/dts/core/signing/index.d.ts +35 -0
- package/target/dts/index.d.ts +8 -8
- package/target/dts/lib/fail.d.ts +8 -0
- package/target/dts/lib/index.d.ts +3 -11
- package/target/dts/lib/json.d.ts +1 -0
- package/target/esm/adapters/fs/index.js +5 -0
- package/target/esm/adapters/fs/lock.js +83 -0
- package/target/esm/adapters/fs/write.js +25 -0
- package/target/esm/adapters/host-posix/group-state.js +24 -0
- package/target/esm/adapters/host-posix/groups.js +43 -0
- package/target/esm/adapters/host-posix/index.js +35 -0
- package/target/esm/adapters/host-posix/launch.js +80 -0
- package/target/esm/adapters/host-posix/probes.js +66 -0
- package/target/esm/adapters/host-posix/stats.js +27 -0
- package/target/esm/adapters/keys-file/index.js +68 -0
- package/target/esm/adapters/state-fs/index.js +79 -0
- package/target/esm/adapters/tool-claude/index.js +32 -0
- package/target/esm/adapters/tool-codex/index.js +38 -0
- package/target/esm/adapters/tool-cursor/index.js +8 -0
- package/target/esm/adapters/tool-traces/index.js +18 -0
- package/target/esm/adapters/transport-git/branches.js +155 -0
- package/target/esm/adapters/transport-git/files.js +48 -0
- package/target/esm/adapters/transport-git/index.js +128 -0
- package/target/esm/adapters/transport-git/marks.js +88 -0
- package/target/esm/adapters/transport-git/remote/admit.js +71 -0
- package/target/esm/adapters/transport-git/remote/alarms.js +30 -0
- package/target/esm/adapters/transport-git/remote/bootstrap.js +47 -0
- package/target/esm/adapters/transport-git/remote/branch-commit.js +36 -0
- package/target/esm/adapters/transport-git/remote/branch-control.js +37 -0
- package/target/esm/adapters/transport-git/remote/branch-scan.js +28 -0
- package/target/esm/adapters/transport-git/remote/branch.js +37 -0
- package/target/esm/adapters/transport-git/remote/cache.js +23 -0
- package/target/esm/adapters/transport-git/remote/commits.js +25 -0
- package/target/esm/adapters/transport-git/remote/forget.js +35 -0
- package/target/esm/adapters/transport-git/remote/git.js +58 -0
- package/target/esm/adapters/transport-git/remote/heartbeat-facts.js +35 -0
- package/target/esm/adapters/transport-git/remote/heartbeat-name.js +19 -0
- package/target/esm/adapters/transport-git/remote/heartbeat-object.js +40 -0
- package/target/esm/adapters/transport-git/remote/heartbeat-push.js +44 -0
- package/target/esm/adapters/transport-git/remote/heartbeat.js +44 -0
- package/target/esm/adapters/transport-git/remote/keys.js +75 -0
- package/target/esm/adapters/transport-git/remote/main.js +41 -0
- package/target/esm/adapters/transport-git/remote/process.js +37 -0
- package/target/esm/adapters/transport-git/remote/push-retry.js +46 -0
- package/target/esm/adapters/transport-git/remote/push-validate.js +39 -0
- package/target/esm/adapters/transport-git/remote/push.js +62 -0
- package/target/esm/adapters/transport-git/remote/roster.js +48 -0
- package/target/esm/adapters/transport-git/remote/signals.js +63 -0
- package/target/esm/adapters/transport-git/remote/signature.js +56 -0
- package/target/esm/adapters/transport-git/remote/sync-heartbeat.js +26 -0
- package/target/esm/adapters/transport-git/remote/sync-rooms.js +38 -0
- package/target/esm/adapters/transport-git/remote/sync-tags.js +48 -0
- package/target/esm/adapters/transport-git/remote/sync.js +38 -0
- package/target/esm/adapters/transport-git/remote/types.js +10 -0
- package/target/esm/adapters/transport-git/remote.js +17 -0
- package/target/esm/adapters/transport-git/repo.js +115 -0
- package/target/esm/adapters/transport-git/run.js +57 -0
- package/target/esm/adapters/transport-git/signing.js +44 -0
- package/target/esm/adapters/transport-git/tags.js +21 -0
- package/target/esm/adapters/transport-git/transport.js +29 -0
- package/target/esm/adapters/transport-kafka/index.js +4 -0
- package/target/esm/adapters/transport-temporal/index.js +4 -0
- package/target/esm/cli.js +5 -4
- package/target/esm/{cli/agent.js → clients/cli/agent/index.js} +8 -7
- package/target/esm/{cli → clients/cli/agent}/session.js +7 -7
- package/target/esm/clients/cli/arg-parse.js +41 -0
- package/target/esm/{cli → clients/cli}/args.js +3 -36
- package/target/esm/clients/cli/commands.js +45 -0
- package/target/esm/clients/cli/context.js +6 -0
- package/target/esm/{cli/index.js → clients/cli/help.js} +4 -77
- package/target/esm/clients/cli/index.js +27 -0
- package/target/esm/clients/cli/operator/heartbeat-format.js +8 -0
- package/target/esm/clients/cli/operator/heartbeat.js +35 -0
- package/target/esm/clients/cli/operator/hub-commands.js +37 -0
- package/target/esm/clients/cli/operator/hub.js +10 -0
- package/target/esm/clients/cli/operator/index.js +8 -0
- package/target/esm/clients/cli/operator/room-lifecycle.js +23 -0
- package/target/esm/clients/cli/operator/room-list.js +27 -0
- package/target/esm/clients/cli/operator/room-members.js +15 -0
- package/target/esm/clients/cli/operator/room.js +15 -0
- package/target/esm/clients/cli/operator/sessions.js +32 -0
- package/target/esm/clients/cli/operator/watch.js +15 -0
- package/target/esm/clients/cli/operator/where-format.js +24 -0
- package/target/esm/clients/cli/operator/where.js +32 -0
- package/target/esm/clients/cli/shared/budget.js +50 -0
- package/target/esm/clients/cli/shared/files.js +26 -0
- package/target/esm/clients/cli/shared/index.js +10 -0
- package/target/esm/clients/cli/shared/messages.js +41 -0
- package/target/esm/clients/cli/shared/profile.js +17 -0
- package/target/esm/clients/cli/shared/session.js +8 -0
- package/target/esm/clients/cli/shared/signals.js +21 -0
- package/target/esm/clients/cli/shared/work.js +26 -0
- package/target/esm/clients/cli/skill/index.js +40 -0
- package/target/esm/clients/cli/skill/targets.js +20 -0
- package/target/esm/composition/index.js +34 -0
- package/target/esm/{heartbeat → core/agents/heartbeat}/index.js +14 -12
- package/target/esm/{app/heartbeat.js → core/agents/heartbeat/pulse.js} +31 -55
- package/target/esm/core/agents/heartbeat/singleton.js +38 -0
- package/target/esm/core/agents/heartbeat/tools.js +24 -0
- package/target/esm/{app → core/agents}/overview.js +15 -9
- package/target/esm/core/agents/ports/host.js +4 -0
- package/target/esm/core/agents/ports/index.js +7 -0
- package/target/esm/core/agents/ports/tools.js +5 -0
- package/target/esm/{app → core/agents/sessions}/agent.js +11 -43
- package/target/esm/core/agents/sessions/binding.js +43 -0
- package/target/esm/core/agents/sessions/context.js +11 -0
- package/target/esm/core/agents/sessions/index.js +59 -0
- package/target/esm/core/agents/sessions/join.js +55 -0
- package/target/esm/core/agents/sessions/resume.js +54 -0
- package/target/esm/core/agents/sessions/session.js +28 -0
- package/target/esm/core/agents/sessions/wait.js +47 -0
- package/target/esm/core/agents/turns/budgets.js +48 -0
- package/target/esm/core/agents/turns/dispatch.js +40 -0
- package/target/esm/core/agents/turns/exhaustion.js +16 -0
- package/target/esm/core/agents/turns/grants.js +36 -0
- package/target/esm/core/agents/turns/index.js +33 -0
- package/target/esm/core/agents/turns/limits.js +11 -0
- package/target/esm/core/agents/turns/messages.js +32 -0
- package/target/esm/core/agents/turns/pings.js +30 -0
- package/target/esm/core/agents/turns/plan.js +29 -0
- package/target/esm/core/agents/turns/poll.js +48 -0
- package/target/esm/core/agents/turns/results.js +40 -0
- package/target/esm/core/agents/turns/seat.js +44 -0
- package/target/esm/core/agents/turns/types.js +3 -0
- package/target/esm/{domain/index.js → core/hub/capabilities.js} +3 -3
- package/target/esm/core/hub/decisions/budget.js +40 -0
- package/target/esm/core/hub/decisions/check.js +43 -0
- package/target/esm/core/hub/decisions/index.js +10 -0
- package/target/esm/core/hub/decisions/proposal.js +73 -0
- package/target/esm/core/hub/decisions/usage.js +39 -0
- package/target/esm/core/hub/history/index.js +90 -0
- package/target/esm/core/hub/ports/index.js +10 -0
- package/target/esm/core/hub/ports/state.js +5 -0
- package/target/esm/core/hub/ports/store.js +10 -0
- package/target/esm/{protocol → core/hub/protocol}/files.js +4 -2
- package/target/esm/core/hub/protocol/index.js +9 -0
- package/target/esm/core/hub/protocol/model.js +15 -0
- package/target/esm/core/hub/protocol/order.js +39 -0
- package/target/esm/core/hub/protocol/reading.js +29 -0
- package/target/esm/core/hub/protocol/text.js +95 -0
- package/target/esm/core/hub/protocol/threads.js +52 -0
- package/target/esm/core/hub/rooms/compat.js +51 -0
- package/target/esm/core/hub/rooms/hubs.js +92 -0
- package/target/esm/core/hub/rooms/index.js +23 -0
- package/target/esm/core/hub/rooms/messages.js +47 -0
- package/target/esm/core/hub/rooms/rooms.js +84 -0
- package/target/esm/core/hub/signals/check.js +33 -0
- package/target/esm/core/hub/signals/effective.js +31 -0
- package/target/esm/core/hub/signals/index.js +23 -0
- package/target/esm/core/hub/signals/marks.js +30 -0
- package/target/esm/core/hub/signals/model.js +5 -0
- package/target/esm/core/hub/signals/pins.js +21 -0
- package/target/esm/{hub/signals.js → core/hub/signals/write.js} +4 -4
- package/target/esm/core/hub/usecases/actor.js +3 -0
- package/target/esm/core/hub/usecases/budget.js +78 -0
- package/target/esm/core/hub/usecases/config.js +5 -0
- package/target/esm/core/hub/usecases/context.js +13 -0
- package/target/esm/core/hub/usecases/hubs.js +95 -0
- package/target/esm/core/hub/usecases/propose.js +39 -0
- package/target/esm/core/hub/usecases/room.js +82 -0
- package/target/esm/{app → core/hub/usecases}/rooms.js +11 -63
- package/target/esm/core/hub/usecases/roster.js +47 -0
- package/target/esm/core/hub/usecases/send.js +78 -0
- package/target/esm/core/hub/usecases/staging.js +60 -0
- package/target/esm/{app → core/hub/usecases}/view.js +33 -33
- package/target/esm/core/hub/work/index.js +13 -0
- package/target/esm/core/hub/work/parse.js +37 -0
- package/target/esm/core/hub/work/task.js +68 -0
- package/target/esm/core/signing/index.js +4 -0
- package/target/esm/index.js +7 -6
- package/target/esm/lib/fail.js +18 -0
- package/target/esm/lib/index.js +20 -195
- package/target/esm/lib/json.js +81 -0
- package/target/dts/app/agent.d.ts +0 -109
- package/target/dts/app/session.d.ts +0 -65
- package/target/dts/cli/context.d.ts +0 -6
- package/target/dts/cli/operator.d.ts +0 -10
- package/target/dts/cli/shared.d.ts +0 -27
- package/target/dts/domain/decisions.d.ts +0 -65
- package/target/dts/domain/signals.d.ts +0 -37
- package/target/dts/heartbeat/platform.d.ts +0 -15
- package/target/dts/heartbeat/tools.d.ts +0 -11
- package/target/dts/home/index.d.ts +0 -21
- package/target/dts/hub/index.d.ts +0 -72
- package/target/dts/runtime/watch.d.ts +0 -17
- package/target/dts/store/git/index.d.ts +0 -92
- package/target/dts/store/git/remote.d.ts +0 -95
- package/target/dts/store/kafka/index.d.ts +0 -2
- package/target/dts/store/temporal/index.d.ts +0 -2
- package/target/esm/app/budget.js +0 -95
- package/target/esm/app/context.js +0 -16
- package/target/esm/app/hubs.js +0 -76
- package/target/esm/app/room.js +0 -181
- package/target/esm/app/session.js +0 -99
- package/target/esm/cli/context.js +0 -8
- package/target/esm/cli/operator.js +0 -185
- package/target/esm/cli/shared.js +0 -159
- package/target/esm/cli/skill.js +0 -54
- package/target/esm/domain/decisions.js +0 -178
- package/target/esm/domain/profiles.js +0 -96
- package/target/esm/domain/signals.js +0 -134
- package/target/esm/heartbeat/platform.js +0 -83
- package/target/esm/heartbeat/tools.js +0 -68
- package/target/esm/home/index.js +0 -88
- package/target/esm/hub/index.js +0 -224
- package/target/esm/protocol/index.js +0 -219
- package/target/esm/runtime/reader.js +0 -65
- package/target/esm/runtime/session.js +0 -116
- package/target/esm/runtime/watch.js +0 -321
- package/target/esm/store/git/index.js +0 -719
- package/target/esm/store/git/remote.js +0 -928
- package/target/esm/store/index.js +0 -15
- package/target/esm/store/kafka/index.js +0 -3
- package/target/esm/store/temporal/index.js +0 -3
- /package/target/dts/{domain/index.d.ts → core/hub/capabilities.d.ts} +0 -0
- /package/target/dts/{protocol → core/hub/protocol}/files.d.ts +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// text: a message's text (core-1 §3) — decoding, the trailer block, and why a message is malformed.
|
|
2
|
+
import { fail } from '../../../lib/index.js';
|
|
3
|
+
import { CC_RE } from './files.js';
|
|
4
|
+
import { HOP_RE, ID_RE, KNOWN, TEXT_MAX, TOKEN_RE, TRAILER_RE } from './model.js';
|
|
5
|
+
const utf8 = new TextDecoder('utf-8', { fatal: true });
|
|
6
|
+
// Invalid text is dangerous bytes: readers treat the message as absent.
|
|
7
|
+
export const decodeText = (buf) => {
|
|
8
|
+
let s;
|
|
9
|
+
try {
|
|
10
|
+
s = utf8.decode(buf);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return { invalid: 'not valid UTF-8' };
|
|
14
|
+
}
|
|
15
|
+
s = s.replace(/\r\n/g, '\n');
|
|
16
|
+
if (/\r/.test(s))
|
|
17
|
+
return { invalid: 'a CR not followed by LF' };
|
|
18
|
+
if (CC_RE.test(s))
|
|
19
|
+
return { invalid: 'control characters in the text' };
|
|
20
|
+
return { text: s };
|
|
21
|
+
};
|
|
22
|
+
// The trailer block: the last paragraph, when every line of it is a trailer.
|
|
23
|
+
const trailerBlock = (text) => {
|
|
24
|
+
const cut = text.lastIndexOf('\n\n');
|
|
25
|
+
const tail = cut < 0 ? [] : text.slice(cut + 2).split('\n');
|
|
26
|
+
const has = tail.length > 0 && tail.every(l => TRAILER_RE.test(l));
|
|
27
|
+
const trailers = {};
|
|
28
|
+
for (const l of has ? tail : []) {
|
|
29
|
+
const [, k, v] = TRAILER_RE.exec(l);
|
|
30
|
+
const key = k.toLowerCase();
|
|
31
|
+
trailers[key] = [...(trailers[key] ?? []), v.trim()];
|
|
32
|
+
}
|
|
33
|
+
return { head: has ? text.slice(0, cut) : text, trailers, has };
|
|
34
|
+
};
|
|
35
|
+
const ids = (v) => v === null ? null : [...new Set(v.split(',').map(x => x.trim()))];
|
|
36
|
+
// Roles: `<id>=<label>, …`; the label `none` clears one.
|
|
37
|
+
const rolePairs = (v) => v === null ? null : v.split(',').map(x => x.trim().split('=').map(y => y.trim()));
|
|
38
|
+
const isRole = (p) => p.length === 2 && ID_RE.test(p[0]) && TOKEN_RE.test(p[1]);
|
|
39
|
+
const allIds = (xs) => !xs || xs.every(x => ID_RE.test(x));
|
|
40
|
+
// A trailer present but outside its grammar.
|
|
41
|
+
const malformed = (v, ok) => v !== null && !ok(v);
|
|
42
|
+
// Why the address makes a message route nowhere, if it does.
|
|
43
|
+
const addressProblem = (has, to) => {
|
|
44
|
+
if (!has)
|
|
45
|
+
return 'no trailer block';
|
|
46
|
+
if (!to)
|
|
47
|
+
return 'no To';
|
|
48
|
+
return to[0] === '*' || allIds(to) ? null : 'malformed To';
|
|
49
|
+
};
|
|
50
|
+
// Why the message is malformed, each reason once, in a fixed order.
|
|
51
|
+
const problems = ({ raw, subject, has, trailers }, f, isRef) => {
|
|
52
|
+
const address = addressProblem(has, f.to);
|
|
53
|
+
const found = [
|
|
54
|
+
[!!f.pairs && !f.pairs.every(isRole), 'malformed Roles'],
|
|
55
|
+
[!subject, 'empty subject'],
|
|
56
|
+
[Buffer.byteLength(raw) > TEXT_MAX, 'text over 64 KiB'],
|
|
57
|
+
[address !== null, address ?? ''],
|
|
58
|
+
[!allIds(f.assign), 'malformed Assign'],
|
|
59
|
+
[malformed(f.get('re'), isRef), 'malformed Re'],
|
|
60
|
+
[malformed(f.get('hop'), v => HOP_RE.test(v)), 'malformed Hop'],
|
|
61
|
+
// A state is a token; whether the room's profile knows it is the store's check.
|
|
62
|
+
[malformed(f.get('state'), v => TOKEN_RE.test(v)), 'malformed State'],
|
|
63
|
+
[malformed(f.get('kind'), v => TOKEN_RE.test(v)), 'malformed Kind'],
|
|
64
|
+
];
|
|
65
|
+
const duplicates = [...KNOWN].filter(k => (trailers[k]?.length ?? 0) > 1).map(k => `duplicate ${k}`);
|
|
66
|
+
return [...duplicates, ...found.filter(([b]) => b).map(([, why]) => why)];
|
|
67
|
+
};
|
|
68
|
+
// A malformed message is read as if it had no trailers.
|
|
69
|
+
const UNROUTED = { to: [], assign: null, roles: null, re: null, kind: null, state: null, hop: 0 };
|
|
70
|
+
const routed = ({ get, to, assign, pairs }) => ({ to: to, assign, roles: pairs ? Object.fromEntries(pairs) : null,
|
|
71
|
+
re: get('re'), kind: get('kind'), state: get('state'), hop: get('hop') === null ? 0 : Number(get('hop')) });
|
|
72
|
+
// Parses well-formed and malformed text alike; `bad` names why a message routes nowhere. `isRef` is the store's
|
|
73
|
+
// grammar for message ids in `Re`.
|
|
74
|
+
export const parse = (raw, isRef) => {
|
|
75
|
+
const { head, trailers, has } = trailerBlock(raw.replace(/\r\n/g, '\n').trimEnd());
|
|
76
|
+
const nl = head.indexOf('\n');
|
|
77
|
+
const subject = (nl < 0 ? head : head.slice(0, nl)).trim();
|
|
78
|
+
const get = (k) => trailers[k]?.[0] ?? null;
|
|
79
|
+
const f = { get, to: get('to') === '*' ? ['*'] : ids(get('to')), assign: ids(get('assign')), pairs: rolePairs(get('roles')) };
|
|
80
|
+
const bad = problems({ raw, subject, has, trailers }, f, isRef);
|
|
81
|
+
const body = nl < 0 ? '' : head.slice(nl + 1).replace(/^\n+/, '').trimEnd();
|
|
82
|
+
return { subject, body, trailers, bad: bad.join(', ') || null, ...(bad.length ? UNROUTED : routed(f)) };
|
|
83
|
+
};
|
|
84
|
+
export const compose = (text, trailers) => `${text}\n\n${trailers.filter(([, v]) => v).map(([k, v]) => `${k}: ${v}`).join('\n')}\n`;
|
|
85
|
+
// A client refuses to write what readers would drop or ignore (§3).
|
|
86
|
+
export const checkOutgoing = (text, isRef) => {
|
|
87
|
+
const d = decodeText(Buffer.from(text));
|
|
88
|
+
if (d.invalid !== undefined)
|
|
89
|
+
fail(`refusing to send: ${d.invalid}`);
|
|
90
|
+
const m = parse(d.text, isRef);
|
|
91
|
+
if (m.bad)
|
|
92
|
+
fail(`refusing to send a malformed message: ${m.bad}`);
|
|
93
|
+
return m;
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// threads: Re links and effective hops, and reading a thread (core-1 §5); the order of messages is order.ts.
|
|
2
|
+
import { fail } from '../../../lib/index.js';
|
|
3
|
+
import { ordered } from './order.js';
|
|
4
|
+
// The hop a message counts from its parent: one more than an answered message, one for an unresolved Re.
|
|
5
|
+
const hopAfter = (m, eff) => m.reId ? eff.get(m.reId) + 1 : m.re ? 1 : 0;
|
|
6
|
+
// Effective hops along a message's Re chain, from the nearest one already known.
|
|
7
|
+
const settleHops = (all, eff, m) => {
|
|
8
|
+
const chain = [];
|
|
9
|
+
for (let x = m; x && !eff.has(x.id) && chain.length < 100000; x = x.reId ? all.get(x.reId) : undefined)
|
|
10
|
+
chain.push(x);
|
|
11
|
+
for (const c of chain.reverse())
|
|
12
|
+
eff.set(c.id, Math.max(c.hop, hopAfter(c, eff)));
|
|
13
|
+
};
|
|
14
|
+
// Resolves Re and computes effective hops. Invalid messages are absent: a Re naming one does not resolve.
|
|
15
|
+
export const link = (logs) => {
|
|
16
|
+
const all = new Map();
|
|
17
|
+
for (const msgs of logs.values())
|
|
18
|
+
for (const m of msgs)
|
|
19
|
+
if (!m.invalid)
|
|
20
|
+
all.set(m.id, m);
|
|
21
|
+
for (const m of all.values())
|
|
22
|
+
m.reId = m.re && all.has(m.re) ? m.re : null;
|
|
23
|
+
const eff = new Map();
|
|
24
|
+
for (const m of all.values()) {
|
|
25
|
+
settleHops(all, eff, m);
|
|
26
|
+
m.hopEff = eff.get(m.id);
|
|
27
|
+
}
|
|
28
|
+
return all;
|
|
29
|
+
};
|
|
30
|
+
const chainHas = (all, m, top) => {
|
|
31
|
+
for (let x = m, i = 0; x && i < 10000; x = x.reId ? all.get(x.reId) : undefined, i++)
|
|
32
|
+
if (x === top)
|
|
33
|
+
return true;
|
|
34
|
+
return false;
|
|
35
|
+
};
|
|
36
|
+
export const rootOf = (all, m) => {
|
|
37
|
+
let x = m;
|
|
38
|
+
for (let i = 0; x.reId && all.has(x.reId) && i < 10000; i++)
|
|
39
|
+
x = all.get(x.reId);
|
|
40
|
+
return x;
|
|
41
|
+
};
|
|
42
|
+
export const under = (all, top) => ordered([...all.values()].filter(m => chainHas(all, m, top)));
|
|
43
|
+
export const lookup = (all, ref) => {
|
|
44
|
+
const key = String(ref ?? '').toLowerCase();
|
|
45
|
+
if (all.has(key))
|
|
46
|
+
return all.get(key);
|
|
47
|
+
const hits = key.length >= 4 ? [...all.keys()].filter(k => k.startsWith(key)) : [];
|
|
48
|
+
if (hits.length === 1)
|
|
49
|
+
return all.get(hits[0]);
|
|
50
|
+
return fail(hits.length ? `ambiguous message id ${ref}` : `unknown message ${ref}`);
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=threads.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// compat: the gates a hub passes before ringi reads it, all in one place: this machine's descriptor of it, its
|
|
2
|
+
// contract's schema and major version, the protocol major, the capabilities its rooms need, and how its store lays out
|
|
3
|
+
// what it keeps. The home has its own gate (adapters/state-fs). Each gate fails closed and says what to do. ringi migrates nothing: a hub it cannot read stays as it is, and the operator makes a new hub
|
|
4
|
+
// beside it, or a new root branch on a forge (docs/ARCHITECTURE.md, Evolution).
|
|
5
|
+
import { unenforceable } from '../capabilities.js';
|
|
6
|
+
import { hubPorts } from '../ports/index.js';
|
|
7
|
+
import { fail, short, strictJson } from '../../../lib/index.js';
|
|
8
|
+
import { HUB_MAJOR, PROTOCOL_MAJOR } from './index.js';
|
|
9
|
+
const version = () => hubPorts().build.version;
|
|
10
|
+
// What to do with a hub this ringi cannot read.
|
|
11
|
+
const beside = 'make a new hub beside it (ringi hub create <name>), or a new root branch on a forge; ringi does not migrate';
|
|
12
|
+
const newer = (major, mine) => major > mine ? 'upgrade ringi' : beside;
|
|
13
|
+
// Why this ringi cannot read the hub's descriptor (hub.json), or nothing.
|
|
14
|
+
const knownSchema = (d, name) => d.schema === 'ringi-hub/2' && d.name === name;
|
|
15
|
+
const knownStore = (d) => ['signed', 'asserted'].includes(d.trust) && !!d.store?.type && !!d.store.path;
|
|
16
|
+
export const descriptorRefusal = (d, name) => knownSchema(d, name) && knownStore(d) ? null : `hub ${name}: its descriptor is not one this ringi ${version()} reads; ${beside}`;
|
|
17
|
+
// Why this ringi cannot read the contract, or nothing.
|
|
18
|
+
const refusal = (c, name) => {
|
|
19
|
+
if (c.schema !== 'ringi-hub')
|
|
20
|
+
return `hub ${name} declares schema ${String(c.schema)}, not ringi-hub`;
|
|
21
|
+
const major = Number(String(c.version).split('.')[0]);
|
|
22
|
+
if (major !== HUB_MAJOR)
|
|
23
|
+
return `hub ${name} follows contract version ${c.version}; this ringi ${version()} supports ${HUB_MAJOR}.x: ${newer(major, HUB_MAJOR)}`;
|
|
24
|
+
if (c.protocol !== PROTOCOL_MAJOR)
|
|
25
|
+
return `hub ${name} speaks protocol ${String(c.protocol)}; this ringi ${version()} speaks ${PROTOCOL_MAJOR}: ${newer(Number(c.protocol), PROTOCOL_MAJOR)}`;
|
|
26
|
+
const why = unenforceable(c.capabilities ?? []);
|
|
27
|
+
return why && `hub ${name} ${why}, which this ringi ${version()} cannot enforce. Upgrade ringi`;
|
|
28
|
+
};
|
|
29
|
+
// Old contract snapshots stay immutable after an explicit transition. Only ancestors of a readable canonical
|
|
30
|
+
// main may use the old filename; an old current head or a room's attachment is never accepted this way.
|
|
31
|
+
const historicalContract = (store, name, rev) => {
|
|
32
|
+
const head = store.contractHead();
|
|
33
|
+
if (rev === head || !store.extends(rev, head))
|
|
34
|
+
return null;
|
|
35
|
+
contractAt(store, name, head);
|
|
36
|
+
return store.contractFile(rev, 'hub.json');
|
|
37
|
+
};
|
|
38
|
+
// The contract at `rev`, once it passes every gate.
|
|
39
|
+
export const contractAt = (store, name, rev) => {
|
|
40
|
+
const text = store.contractFile(rev, 'contract.json') ?? historicalContract(store, name, rev) ?? fail(`hub ${name} has no contract at ${short(rev)}`);
|
|
41
|
+
const c = strictJson(text, `hub ${name}: contract`);
|
|
42
|
+
const why = refusal(c, name);
|
|
43
|
+
if (why)
|
|
44
|
+
fail(why);
|
|
45
|
+
const a = store.contractFile(rev, 'authority.json');
|
|
46
|
+
return { ...c, authority: a === null ? {} : strictJson(a, `hub ${name}: authority.json at ${short(rev)}`) };
|
|
47
|
+
};
|
|
48
|
+
// Why this ringi cannot read the hub's store as it is laid out (`shown`: what its contract names, or what its store
|
|
49
|
+
// type finds there when the contract names nothing), or nothing; `reads` is what the store type reads.
|
|
50
|
+
export const layoutRefusal = (name, shown, reads) => shown === reads ? null : `hub ${name} keeps its store as ${shown}; this ringi ${version()} reads ${reads}: upgrade ringi if the hub is newer, or ${beside}`;
|
|
51
|
+
//# sourceMappingURL=compat.js.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// hubs: a hub's descriptor beside its store, the contract it carries, and opening it.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import * as D from '../decisions/index.js';
|
|
5
|
+
import { DEFAULT_PROFILE, PROFILES_CAPABILITY } from '../work/index.js';
|
|
6
|
+
import { hubPorts } from '../ports/index.js';
|
|
7
|
+
import { fail } from '../../../lib/index.js';
|
|
8
|
+
import { compose } from '../protocol/index.js';
|
|
9
|
+
import { contractAt, descriptorRefusal, layoutRefusal } from './compat.js';
|
|
10
|
+
import { PROTOCOL_MAJOR } from './index.js';
|
|
11
|
+
// A hub on this machine is a directory under `hub` with its descriptor (`hub`) in it.
|
|
12
|
+
const has = (name) => hubPorts().state.list(['hub', name]).includes('hub');
|
|
13
|
+
export const hubNames = () => hubPorts().state.list(['hub']).filter(has);
|
|
14
|
+
const VERSION = () => hubPorts().build.version;
|
|
15
|
+
const HUB_MD = `# ringi hub
|
|
16
|
+
|
|
17
|
+
The shared contract of a ringi hub (docs/spec/hub-1.md): contract.json (schema, version, protocol, trust, capabilities, the
|
|
18
|
+
default profile), authority.json (who may decide what, up to which budget; the root, \`operator\`, always may) and
|
|
19
|
+
profiles/ (what a piece of work is in a room: its states, and who may move it). Use \`ringi\`; never edit by hand.
|
|
20
|
+
`;
|
|
21
|
+
export const readProfileResource = (name) => fs.readFileSync(path.join(hubPorts().build.resources, 'profiles', `${name}.json`), 'utf8');
|
|
22
|
+
// A new hub: local (asserted senders), or signed, with the operator's root key generated on this machine and pinned.
|
|
23
|
+
// The files of a new hub's first contract.
|
|
24
|
+
const genesisFiles = (d, layout) => {
|
|
25
|
+
const contract = { schema: 'ringi-hub', version: '1.2', protocol: PROTOCOL_MAJOR, trust: d.trust, store: layout, capabilities: [D.CAPABILITY, PROFILES_CAPABILITY], profile: DEFAULT_PROFILE, ...(d.root ? { root: d.root } : {}) };
|
|
26
|
+
return {
|
|
27
|
+
'contract.json': JSON.stringify(contract, null, 2) + '\n',
|
|
28
|
+
'authority.json': JSON.stringify({ decide: {} }, null, 2) + '\n',
|
|
29
|
+
'HUB.md': HUB_MD,
|
|
30
|
+
[`profiles/${DEFAULT_PROFILE}.json`]: readProfileResource(DEFAULT_PROFILE),
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const descriptor = (name, signed, url) => ({ schema: 'ringi-hub/2', name, trust: signed ? 'signed' : 'asserted', store: { type: hubPorts().store, path: 'store', ...(url ? { remote: url } : {}) } });
|
|
34
|
+
// The store type a descriptor names, as this build registers it.
|
|
35
|
+
export const transportOf = (name, type) => {
|
|
36
|
+
const t = hubPorts().transports;
|
|
37
|
+
return t[type] ?? fail(`hub ${name} uses a ${type} store; this ringi ${VERSION()} knows ${Object.keys(t).join(', ')}`);
|
|
38
|
+
};
|
|
39
|
+
// A new hub: its store made, then its descriptor written; a failure in between leaves no half-made hub. `custody`: where
|
|
40
|
+
// a signed hub's new root key is kept.
|
|
41
|
+
export const createHub = (name, { signed = false, url } = {}) => {
|
|
42
|
+
const st = hubPorts().state;
|
|
43
|
+
st.ensure();
|
|
44
|
+
if (st.list(['hub']).includes(name))
|
|
45
|
+
fail(`hub ${name} exists`);
|
|
46
|
+
if (url && !signed)
|
|
47
|
+
fail('--url publishes a hub; only a --signed hub may be published');
|
|
48
|
+
const d = descriptor(name, signed, url);
|
|
49
|
+
try {
|
|
50
|
+
const t = transportOf(name, d.store.type);
|
|
51
|
+
const made = t.create(st.place(['hub', name]), d, x => ({ files: genesisFiles(x, t.layout), text: compose(`hub ${name}`, [['To', '*'], ['Kind', 'hub']]) }));
|
|
52
|
+
st.write(['hub', name, 'hub'], d);
|
|
53
|
+
return { custody: made.custody };
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
if (!has(name))
|
|
57
|
+
st.remove(['hub', name]);
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const readDescriptor = (name) => {
|
|
62
|
+
const d = hubPorts().state.read(['hub', name, 'hub'], null) ?? fail(`no hub ${name}; hubs: ${hubNames().join(', ') || 'none'}`);
|
|
63
|
+
const why = descriptorRefusal(d, name);
|
|
64
|
+
if (why)
|
|
65
|
+
fail(why);
|
|
66
|
+
return d;
|
|
67
|
+
};
|
|
68
|
+
// A hub is opened once per process; its store syncs then (a signed store fetches and verifies), and again on refresh.
|
|
69
|
+
const opened = new Map();
|
|
70
|
+
export const openHub = (name, { refresh = false } = {}) => {
|
|
71
|
+
hubPorts().state.ensure();
|
|
72
|
+
if (!/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(name))
|
|
73
|
+
fail(`bad hub name ${name}`);
|
|
74
|
+
if (opened.has(name) && !refresh)
|
|
75
|
+
return opened.get(name);
|
|
76
|
+
if (name === 'local' && !has(name))
|
|
77
|
+
createHub('local');
|
|
78
|
+
const d = readDescriptor(name);
|
|
79
|
+
const dir = hubPorts().state.place(['hub', name]);
|
|
80
|
+
const t = transportOf(name, d.store.type);
|
|
81
|
+
const store = t.open(dir, d);
|
|
82
|
+
store.sync();
|
|
83
|
+
const main = store.contractHead();
|
|
84
|
+
const contract = contractAt(store, name, main);
|
|
85
|
+
const why = layoutRefusal(name, contract.store ?? t.layoutOf?.(store) ?? t.layout, t.layout);
|
|
86
|
+
if (why)
|
|
87
|
+
fail(why);
|
|
88
|
+
const H = { name, dir, d, store, main, contract, signed: d.trust === 'signed' };
|
|
89
|
+
opened.set(name, H);
|
|
90
|
+
return H;
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=hubs.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// hub: hubs and rooms on top of any store. A hub is a directory with a descriptor (hub.json: which store, where,
|
|
2
|
+
// what this machine pins) beside its store. Its shared contract lives in the store (contract.json, authority.json,
|
|
3
|
+
// profiles/*.json). hubs.ts reads descriptors and contracts, rooms.ts opens rooms with their profile and authority,
|
|
4
|
+
// messages.ts turns stored records into messages and writes them (docs/spec/hub-1.md).
|
|
5
|
+
import { CAPABILITIES } from '../capabilities.js';
|
|
6
|
+
// The root principal of every hub and room (hub-1): the operator of the machine that made it.
|
|
7
|
+
export const OPERATOR = 'operator';
|
|
8
|
+
export const HUB_MAJOR = 1;
|
|
9
|
+
export const PROTOCOL_MAJOR = 1;
|
|
10
|
+
export const CAPS = new Set(Object.keys(CAPABILITIES));
|
|
11
|
+
export const RESERVED_ROOMS = new Set(['main', 'archive']);
|
|
12
|
+
// Whether the room's work has started: it has the quorum of agents its genesis asked for (`Quorum`), or asked for none.
|
|
13
|
+
export const start = (R) => {
|
|
14
|
+
const agents = Object.values(R.members).filter(m => m.role !== 'root').length;
|
|
15
|
+
return { quorum: R.quorum, agents, started: R.quorum === null || agents >= R.quorum };
|
|
16
|
+
};
|
|
17
|
+
export * from './hubs.js';
|
|
18
|
+
export * from './compat.js';
|
|
19
|
+
export * from './rooms.js';
|
|
20
|
+
export * from './messages.js';
|
|
21
|
+
export * from '../usecases/config.js';
|
|
22
|
+
export { HIDDEN } from '../protocol/index.js';
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// messages: stored records read as messages, and a message written with the signals it made.
|
|
2
|
+
import * as D from '../decisions/index.js';
|
|
3
|
+
import { compose, decodeText, link, parse } from '../protocol/index.js';
|
|
4
|
+
import { emit } from '../signals/write.js';
|
|
5
|
+
export const toMessage = (R, from, x, seq) => {
|
|
6
|
+
const trust = R.hub.signed ? 'verified' : 'asserted';
|
|
7
|
+
const files = x.files.map(f => ({ path: f.path, size: null }));
|
|
8
|
+
const base = { id: x.id, from, trust, time: x.time, seq, files, refused: x.refused, reId: null, hopEff: 0 };
|
|
9
|
+
const d = decodeText(x.text);
|
|
10
|
+
if (d.invalid !== undefined) {
|
|
11
|
+
return { ...base, invalid: d.invalid, subject: '', body: '', to: [], re: null, kind: null, state: null, assign: null, roles: null, hop: 0, bad: d.invalid, trailers: {} };
|
|
12
|
+
}
|
|
13
|
+
const p = parse(d.text, s => R.hub.store.isRef(s));
|
|
14
|
+
// An extension's trailers and the room profile's states are part of a message's validity.
|
|
15
|
+
const ext = D.check(p, s => R.hub.store.isRef(s));
|
|
16
|
+
const state = p.state && !p.bad && !R.profile.states.includes(p.state) ? `State ${p.state} is not in profile ${R.profile.name}` : null;
|
|
17
|
+
return { ...base, invalid: null, ...p, bad: p.bad ?? ext ?? state };
|
|
18
|
+
};
|
|
19
|
+
// The room as stored, read without a reader's lock or state: for an author working out the signals of its own message
|
|
20
|
+
// (append runs inside reader locks too). Readers check signals themselves.
|
|
21
|
+
const asStored = (R) => {
|
|
22
|
+
const s = R.hub.store.room(R.name) ?? R.s;
|
|
23
|
+
return link(new Map(R.hub.store.logIds(s).map(id => [id, R.hub.store.messages(s, id, R.hub.store.logHead(s, id)).map((x, seq) => toMessage(R, id, x, seq))])));
|
|
24
|
+
};
|
|
25
|
+
// Whether a message can make signals: it opens, assigns or moves work, or it is a record of the decisions extension.
|
|
26
|
+
const DECISION_KINDS = ['proposal', 'stamp', 'decision', 'usage'];
|
|
27
|
+
const movesWork = (p, R) => p.kind === R.profile.opens || !!p.assign || !!p.state;
|
|
28
|
+
const CARRIES = (p, R) => !p.bad && (movesWork(p, R) || DECISION_KINDS.includes(p.kind ?? ''));
|
|
29
|
+
// A message on the author's log, and the signals it made (core/hub/signals/write.ts).
|
|
30
|
+
export const append = (R, who, text, entries = []) => {
|
|
31
|
+
const id = R.hub.store.append(R.s, who, text, entries);
|
|
32
|
+
if (CARRIES(parse(text, s => R.hub.store.isRef(s)), R)) {
|
|
33
|
+
const all = asStored(R);
|
|
34
|
+
const m = all.get(id);
|
|
35
|
+
if (m)
|
|
36
|
+
emit(R, who, all, m);
|
|
37
|
+
}
|
|
38
|
+
return id;
|
|
39
|
+
};
|
|
40
|
+
export const withSizes = (R, m) => {
|
|
41
|
+
if (!m.files.length)
|
|
42
|
+
return [];
|
|
43
|
+
const sizes = R.hub.store.sizes(m.id, m.files.map(f => f.path));
|
|
44
|
+
return m.files.map(f => ({ ...f, size: sizes.get(f.path) ?? null }));
|
|
45
|
+
};
|
|
46
|
+
export const pongText = (me, m, what) => compose(`pong: ${me} ${what}`, [['To', m.from], ['Re', m.id], ['Kind', 'pong'], ['Hop', String(m.hopEff + 1)]]);
|
|
47
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// rooms: a room opened with its contract, authority, profile, members and quorum; and the root's control changes.
|
|
2
|
+
import { DEFAULT_PROFILE, parseProfile } from '../work/index.js';
|
|
3
|
+
import { locked } from '../ports/index.js';
|
|
4
|
+
import { fail, strictJson } from '../../../lib/index.js';
|
|
5
|
+
import { compose } from '../protocol/index.js';
|
|
6
|
+
import { readProfileResource } from './hubs.js';
|
|
7
|
+
import { contractAt } from './compat.js';
|
|
8
|
+
export const roomNames = (H, archived = false) => H.store.roomNames(archived);
|
|
9
|
+
// No room by that name: quiet, or why, naming an archive it may have become.
|
|
10
|
+
const missing = (H, name, quiet) => {
|
|
11
|
+
if (quiet)
|
|
12
|
+
return null;
|
|
13
|
+
const gone = roomNames(H, true).find(a => a.startsWith(`${name}@`));
|
|
14
|
+
return fail(gone ? `room ${name} is archived as ${gone}; read it with --room ${gone}` : `no room ${name} in hub ${H.name}; rooms: ${roomNames(H).join(', ') || 'none'}`);
|
|
15
|
+
};
|
|
16
|
+
// The genesis names its room, in the root's hand: a history offered under another name is not that room.
|
|
17
|
+
const checkNamed = (s, name) => {
|
|
18
|
+
const named = s.archived ? name.slice(0, name.lastIndexOf('@')) : name;
|
|
19
|
+
const subject = s.control[0]?.rev === s.genesis ? s.control[0].text.split('\n')[0] : '(none)';
|
|
20
|
+
const archiveName = !s.archived || name === `${named}@${s.genesis.slice(0, 8)}`;
|
|
21
|
+
if (subject !== `room ${named}` || !archiveName)
|
|
22
|
+
fail(`room ${name}: its genesis is "${subject}"; refusing a room's history under another name`);
|
|
23
|
+
};
|
|
24
|
+
// The contract in effect at each control entry: the hub contract the room was created on, then each `Contract:` the
|
|
25
|
+
// operator moved it to. A decision names the control entry it was made under (core/hub/decisions/proposal.ts).
|
|
26
|
+
const contracts = (s) => {
|
|
27
|
+
const atControl = new Map();
|
|
28
|
+
let current = s.base;
|
|
29
|
+
for (const rec of s.control) {
|
|
30
|
+
current = /^Contract: ([0-9a-f]{40,64})$/m.exec(rec.text)?.[1] ?? current;
|
|
31
|
+
atControl.set(rec.rev, current);
|
|
32
|
+
}
|
|
33
|
+
return { atControl, current };
|
|
34
|
+
};
|
|
35
|
+
const authorityOf = (H, atControl) => {
|
|
36
|
+
const tables = new Map();
|
|
37
|
+
return control => {
|
|
38
|
+
const rev = control ? atControl.get(control) : undefined;
|
|
39
|
+
if (!rev)
|
|
40
|
+
return null;
|
|
41
|
+
if (!tables.has(rev))
|
|
42
|
+
tables.set(rev, contractAt(H.store, H.name, rev).authority);
|
|
43
|
+
return tables.get(rev);
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
// The room's profile is fixed when the room is made: its content is copied into the genesis control entry, so no
|
|
47
|
+
// later change of the hub's profiles or contract reinterprets the room's history. Rooms made before this carry none
|
|
48
|
+
// and use the default profile of the contract they were created on.
|
|
49
|
+
const profileOf = (H, s, name) => {
|
|
50
|
+
const base = contractAt(H.store, H.name, s.base).profile ?? DEFAULT_PROFILE;
|
|
51
|
+
const fallback = () => base === DEFAULT_PROFILE ? readProfileResource(DEFAULT_PROFILE) : fail(`room ${name}: unknown profile ${base}`);
|
|
52
|
+
return H.store.controlFile(s, 'profile.json', s.genesis) ?? H.store.contractFile(s.base, `profiles/${base}.json`) ?? fallback();
|
|
53
|
+
};
|
|
54
|
+
const membersOf = (H, s, name) => H.store.roster(s)?.members ?? strictJson(H.store.controlFile(s, 'members.json') ?? '{}', `room ${name}: members.json`);
|
|
55
|
+
// The root's control records set the quorum; the latest one counts.
|
|
56
|
+
const quorumOf = (s) => s.control.reduce((q, rec) => Number(/^Quorum: ([1-9][0-9]?)$/m.exec(rec.text)?.[1] ?? NaN) || q, null);
|
|
57
|
+
export const openRoom = (H, name, { quiet = false } = {}) => {
|
|
58
|
+
if (!name)
|
|
59
|
+
return fail('no room: the operator creates one with ringi room create <id>, or picks one with ringi room switch <id>');
|
|
60
|
+
const s = H.store.room(name);
|
|
61
|
+
if (!s)
|
|
62
|
+
return missing(H, name, quiet);
|
|
63
|
+
checkNamed(s, name);
|
|
64
|
+
const { atControl, current } = contracts(s);
|
|
65
|
+
const contract = current === H.main ? H.contract : contractAt(H.store, H.name, current);
|
|
66
|
+
return { hub: H, s, name, archived: s.archived, head: s.head, genesis: s.genesis, contractRev: current, contract,
|
|
67
|
+
authorityAt: authorityOf(H, atControl), profile: parseProfile(profileOf(H, s, name), `room ${name}: profile`),
|
|
68
|
+
members: membersOf(H, s, name), quorum: quorumOf(s) };
|
|
69
|
+
};
|
|
70
|
+
export const room = (H, name) => openRoom(H, name);
|
|
71
|
+
export const ROOM_MD = (name) => `# room ${name}\n\nA ringi room: members.json lists who takes part; profile.json is what a piece of work is here, fixed when the room was made.\n`;
|
|
72
|
+
export const profileText = (H, name) => H.store.contractFile(H.main, `profiles/${name}.json`) ?? (name === DEFAULT_PROFILE ? readProfileResource(DEFAULT_PROFILE) : null);
|
|
73
|
+
// A control change under the hub lock. In a signed room membership changes only through `room admit`.
|
|
74
|
+
export const controlChange = (R, who, subject, mutate, extra = []) => locked(`hub-${R.hub.name}`, () => {
|
|
75
|
+
const s = R.hub.store.room(R.name) ?? fail(`room ${R.name} is gone`);
|
|
76
|
+
const members = strictJson(R.hub.store.controlFile(s, 'members.json') ?? '{}', `room ${R.name}: members.json`);
|
|
77
|
+
const before = JSON.stringify(members);
|
|
78
|
+
mutate(members);
|
|
79
|
+
if (R.hub.signed && JSON.stringify(members) !== before)
|
|
80
|
+
fail('in a signed room, membership changes only by ringi room admit');
|
|
81
|
+
R.head = R.hub.store.updateControl(s, who, compose(subject, [['To', '*'], ['Kind', 'room'], ...extra]), { 'members.json': JSON.stringify(members, null, 2) + '\n' });
|
|
82
|
+
R.members = members;
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=rooms.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// check: a signal holds when its author made it, it is named after and points at a message of its author in this room,
|
|
2
|
+
// and that message made exactly this signal; a mark holds when it is possible.
|
|
3
|
+
import { effective } from './effective.js';
|
|
4
|
+
import { markWhy } from './marks.js';
|
|
5
|
+
import { MARKS } from './model.js';
|
|
6
|
+
const namedAfter = (id, target) => id.length === 12 && target.startsWith(id);
|
|
7
|
+
const signalWhy = (R, all, actor, m, signal) => {
|
|
8
|
+
if (m.from !== actor)
|
|
9
|
+
return `points at a message of ${m.from}`;
|
|
10
|
+
return effective(R, all, m).includes(signal) ? undefined : `its message did not make ${signal}`;
|
|
11
|
+
};
|
|
12
|
+
const whyNot = (R, all, x, id, signal) => {
|
|
13
|
+
const m = all.get(x.target);
|
|
14
|
+
const shape = [
|
|
15
|
+
[!x.target, 'is not an annotated tag on a message'],
|
|
16
|
+
[x.tagger !== x.actor, `was made by ${x.tagger}, not ${x.actor}`],
|
|
17
|
+
[!namedAfter(id, x.target), 'is named after another message'],
|
|
18
|
+
[!m, 'points at no accepted message of this room'],
|
|
19
|
+
];
|
|
20
|
+
const wrong = shape.find(([bad]) => bad);
|
|
21
|
+
if (wrong)
|
|
22
|
+
return wrong[1];
|
|
23
|
+
const meaning = MARKS.includes(signal) ? markWhy(R, x.actor, m, signal) : signalWhy(R, all, x.actor, m, signal);
|
|
24
|
+
// A signal is its name and its target; any text besides a signature is not one.
|
|
25
|
+
return meaning ?? (x.text === '' ? undefined : 'carries text; a signal has none');
|
|
26
|
+
};
|
|
27
|
+
export const check = (R, all, records) => records.map(x => {
|
|
28
|
+
const dot = x.name.lastIndexOf('.');
|
|
29
|
+
const signal = x.name.slice(0, dot);
|
|
30
|
+
const why = whyNot(R, all, x, x.name.slice(dot + 1), signal);
|
|
31
|
+
return { ref: x.ref, actor: x.actor, signal, target: x.target, ok: !why, why };
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// effective: the signals a message made, judged on the room as it is now (thread order is fixed, so this does not
|
|
2
|
+
// change).
|
|
3
|
+
import * as D from '../decisions/index.js';
|
|
4
|
+
import { taskFor, taskOf } from '../work/index.js';
|
|
5
|
+
import { one } from '../protocol/index.js';
|
|
6
|
+
const isWorkSignal = (fx) => fx === 'opened' || fx === 'assigned' || fx.startsWith('state.');
|
|
7
|
+
// What the message did to its piece of work: opened, assigned, a state.
|
|
8
|
+
const workSignals = (R, all, m) => {
|
|
9
|
+
const work = taskFor(all, m, R.profile);
|
|
10
|
+
return work ? (taskOf(all, work, R.profile).effects.get(m.id) ?? []).filter(isWorkSignal) : [];
|
|
11
|
+
};
|
|
12
|
+
// A proposal, and a usage receipt its grant's ledger counts.
|
|
13
|
+
const ownSignals = (R, all, m) => [
|
|
14
|
+
...m.kind === 'proposal' ? ['proposed'] : [],
|
|
15
|
+
...m.kind === 'usage' && D.counted(all, R.authorityAt, m) ? [`usage.${one(m, 'usage')}`] : [],
|
|
16
|
+
];
|
|
17
|
+
// An answer to a proposal: a counted stamp, the decision, its revocation.
|
|
18
|
+
const answerSignals = (R, all, m) => {
|
|
19
|
+
const p = m.reId ? all.get(m.reId) : undefined;
|
|
20
|
+
if (p?.kind !== 'proposal' || p.bad)
|
|
21
|
+
return [];
|
|
22
|
+
const info = D.proposalOf(all, p, R.authorityAt);
|
|
23
|
+
const decided = m.kind === 'decision';
|
|
24
|
+
return [
|
|
25
|
+
...m.kind === 'stamp' && info.reviewers.includes(m.from) ? [`stamp.${one(m, 'stamp')}`] : [],
|
|
26
|
+
...decided && info.decision?.id === m.id ? [`decision.${info.decision.decision}`] : [],
|
|
27
|
+
...decided && info.revoked?.id === m.id ? ['decision.revoked'] : [],
|
|
28
|
+
];
|
|
29
|
+
};
|
|
30
|
+
export const effective = (R, all, m) => m.bad || m.invalid ? [] : [...workSignals(R, all, m), ...ownSignals(R, all, m), ...answerSignals(R, all, m)];
|
|
31
|
+
//# sourceMappingURL=effective.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// signals: explicit markers of what happens in a room, beside the messages that carry it. Every effective transition
|
|
2
|
+
// (work opened, assigned, a state reached, a proposal, a counted stamp, the decision, a counted usage receipt) is also
|
|
3
|
+
// a signal of its author on the message that made it: in a git store an annotated tag `<author>.<signal>.<message12>`
|
|
4
|
+
// on that message's commit, with no text of its own, signed on a signed hub. The message's id keeps the name unique;
|
|
5
|
+
// the name, target, author and time are the tag itself. Signals are visible and checkable with the store's own tools
|
|
6
|
+
// (`git tag -v`, a forge's tag list and tag-push webhooks) and cheap to poll.
|
|
7
|
+
//
|
|
8
|
+
// The messages stay the truth. A signal is made only for a transition the fold actually counted (a State the author
|
|
9
|
+
// had no right to makes none). A reader checks every signal against its message and pins the stored object it
|
|
10
|
+
// accepted; a signal that disagrees, or an accepted one that changes or disappears, is an alarm. A signal missing
|
|
11
|
+
// after a crash between message and tag changes nothing and is restored with `signals --repair`.
|
|
12
|
+
//
|
|
13
|
+
// Marks are the second layer, in the same namespace: where attention is, not what was decided. `read` says a message
|
|
14
|
+
// reached its reader (made on delivery); `taken` says a member took a piece of work. A mark changes no state,
|
|
15
|
+
// assignment or budget. It is checked only for being possible: its target is an accepted message of the room, its
|
|
16
|
+
// actor a member; `read` only for a message addressed to the actor or to all, `taken` only for a message that opens
|
|
17
|
+
// work.
|
|
18
|
+
export * from './model.js';
|
|
19
|
+
export * from './marks.js';
|
|
20
|
+
export * from './effective.js';
|
|
21
|
+
export * from './check.js';
|
|
22
|
+
export * from './pins.js';
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// marks: where attention is — `read` on delivery, `taken` on demand — possible or not, and who put which on what.
|
|
2
|
+
import { HIDDEN, visible } from '../protocol/index.js';
|
|
3
|
+
import { MARKS } from './model.js';
|
|
4
|
+
// Why each mark cannot be put on this message, or nothing when it can.
|
|
5
|
+
const RULES = {
|
|
6
|
+
read: (_R, actor, m) => {
|
|
7
|
+
if (m.from === actor)
|
|
8
|
+
return 'one does not read one\'s own message';
|
|
9
|
+
return visible(m, actor, HIDDEN) ? undefined : `the message is not delivered to ${actor}`;
|
|
10
|
+
},
|
|
11
|
+
taken: (R, _actor, m) => m.kind !== R.profile.opens || m.bad ? `the message does not open work (${R.profile.opens})` : undefined,
|
|
12
|
+
};
|
|
13
|
+
// Why the actor cannot put this mark on this message, or nothing when it can.
|
|
14
|
+
export const markWhy = (R, actor, m, mark) => {
|
|
15
|
+
if (!R.members[actor])
|
|
16
|
+
return `${actor} is not a member of ${R.name}`;
|
|
17
|
+
const rule = RULES[mark];
|
|
18
|
+
return rule ? rule(R, actor, m) : `unknown mark ${mark}`;
|
|
19
|
+
};
|
|
20
|
+
// The marks on each message that passed the check: target → mark → actors.
|
|
21
|
+
export const marksOn = (checked) => {
|
|
22
|
+
const by = new Map();
|
|
23
|
+
for (const c of checked.filter(c => c.ok && MARKS.includes(c.signal))) {
|
|
24
|
+
const e = by.get(c.target) ?? {};
|
|
25
|
+
(e[c.signal] ??= []).push(c.actor);
|
|
26
|
+
by.set(c.target, e);
|
|
27
|
+
}
|
|
28
|
+
return by;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=marks.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// model: what the folds need of a room, and a signal as a store returns it.
|
|
2
|
+
export const MARKS = ['read', 'taken'];
|
|
3
|
+
// A signal's name after its actor: the signal and the message's short id.
|
|
4
|
+
export const signalName = (id, signal) => `${signal}.${id.slice(0, 12)}`;
|
|
5
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// pins: a reader's pins, the stored object of each signal it accepted. A changed or vanished one is an alarm.
|
|
2
|
+
// A signal's identity in any store and after an archive: its tag's name `<actor>.<signal>.<message12>`.
|
|
3
|
+
const keyOf = (ref) => ref.split('/').at(-1);
|
|
4
|
+
export const pins = (records, checked, accepted = {}) => {
|
|
5
|
+
const now = new Map(records.map(x => [`${x.actor}.${x.name}`, x]));
|
|
6
|
+
const alarms = [];
|
|
7
|
+
const next = {};
|
|
8
|
+
for (const [k, object] of Object.entries(accepted)) {
|
|
9
|
+
const key = keyOf(k), x = now.get(key);
|
|
10
|
+
next[key] = object;
|
|
11
|
+
if (!x)
|
|
12
|
+
alarms.push(`signal ${key} was removed after it was accepted`);
|
|
13
|
+
else if (x.object !== object)
|
|
14
|
+
alarms.push(`signal ${x.ref} was replaced after it was accepted`);
|
|
15
|
+
}
|
|
16
|
+
for (const c of checked)
|
|
17
|
+
if (c.ok && !next[keyOf(c.ref)])
|
|
18
|
+
next[keyOf(c.ref)] = now.get(keyOf(c.ref)).object;
|
|
19
|
+
return { accepted: next, alarms };
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=pins.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// signals (write side): an author stores the signals its message made and the marks it puts; readers check them
|
|
2
|
-
// (
|
|
3
|
-
import { effective, markWhy, signalName } from '
|
|
4
|
-
import { fail } from '
|
|
2
|
+
// (index.ts).
|
|
3
|
+
import { effective, markWhy, signalName } from './index.js';
|
|
4
|
+
import { fail } from '../../../lib/index.js';
|
|
5
5
|
// Puts one of the actor's marks on a message; false if it was already there.
|
|
6
6
|
export const mark = (R, who, m, name) => {
|
|
7
7
|
const why = markWhy(R, who, m, name);
|
|
@@ -24,4 +24,4 @@ export const emit = (R, who, all, m) => {
|
|
|
24
24
|
}
|
|
25
25
|
return n;
|
|
26
26
|
};
|
|
27
|
-
//# sourceMappingURL=
|
|
27
|
+
//# sourceMappingURL=write.js.map
|