@idleware/ringi 0.4.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.
- package/README.md +1 -1
- package/package.json +5 -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/core/agents/heartbeat/index.d.ts +33 -0
- package/target/dts/core/agents/heartbeat/pulse.d.ts +32 -0
- 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} +58 -17
- 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 -33
- 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 +13 -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} +6 -73
- 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/core/agents/heartbeat/index.js +43 -0
- package/target/esm/core/agents/heartbeat/pulse.js +86 -0
- 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 +42 -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 -8
- 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/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 -79
- package/target/dts/store/git/remote.d.ts +0 -86
- 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 -163
- package/target/esm/app/session.js +0 -99
- package/target/esm/cli/context.js +0 -8
- package/target/esm/cli/operator.js +0 -157
- 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/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 -644
- package/target/esm/store/git/remote.js +0 -758
- 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,63 @@
|
|
|
1
|
+
// signals: a signal tag's name, object and target checked: its actor's own, on a message of the room's branch.
|
|
2
|
+
import * as C from '../../../lib/index.js';
|
|
3
|
+
import * as P from '../../../core/hub/protocol/index.js';
|
|
4
|
+
import { branch } from './branch.js';
|
|
5
|
+
import { git, isRemote, reason, rev } from './git.js';
|
|
6
|
+
import { heartbeatName } from './heartbeat.js';
|
|
7
|
+
import { checkSignature } from './signature.js';
|
|
8
|
+
import { SHA, TAGS } from './types.js';
|
|
9
|
+
export const SIGNAL_NAME = /^([a-z0-9]+(?:[_-][a-z0-9]+)*)\.([a-z][a-z0-9.-]*)\.([0-9a-f]{12})$/;
|
|
10
|
+
export const signalName = (ref) => {
|
|
11
|
+
if (heartbeatName(ref))
|
|
12
|
+
C.fail('heartbeat is not an immutable signal');
|
|
13
|
+
const m = ref.startsWith(TAGS) && SIGNAL_NAME.exec(ref.slice(TAGS.length));
|
|
14
|
+
if (!m)
|
|
15
|
+
return C.fail('malformed signal tag namespace');
|
|
16
|
+
return { actor: m[1], id: m[3], signal: m[2] };
|
|
17
|
+
};
|
|
18
|
+
// The signed header carries the full target, tag name and actor; there is no message body.
|
|
19
|
+
export const signalObject = (H, ref, sha) => {
|
|
20
|
+
const s = signalName(ref);
|
|
21
|
+
if (git(H, ['cat-file', '-t', sha]).trim() !== 'tag')
|
|
22
|
+
C.fail('signal must be an annotated tag');
|
|
23
|
+
const text = P.decodeText(git(H, ['cat-file', 'tag', sha], { raw: true }));
|
|
24
|
+
if (text.invalid)
|
|
25
|
+
C.fail(`signal tag is ${text.invalid}`);
|
|
26
|
+
const [header, ...rest] = text.text.split('\n\n');
|
|
27
|
+
const target = /^object ([0-9a-f]+)\ntype commit\ntag ([^\n]+)\ntagger ([^<\n]+) <[^\n]*> \d+ [+-]\d{4}\n?$/.exec(header) ?? C.fail('malformed signal tag headers');
|
|
28
|
+
if (!SHA.test(target[1]) || !target[1].startsWith(s.id) || target[2] !== ref.slice('refs/tags/'.length) || target[3].trim() !== s.actor)
|
|
29
|
+
C.fail('signal tag name, actor or target disagrees');
|
|
30
|
+
const body = rest.join('\n\n').split('-----BEGIN SSH SIGNATURE-----')[0];
|
|
31
|
+
if (body !== '')
|
|
32
|
+
C.fail('signal tag message must be empty');
|
|
33
|
+
return { ...s, message: target[1] };
|
|
34
|
+
};
|
|
35
|
+
// Derived signals belong to their message's author; read/taken marks may name another member's message.
|
|
36
|
+
// The room is determined by the full target commit on its verified branch, never by a short name or a tag body.
|
|
37
|
+
export const checkSignal = (H, R, ref, sha) => {
|
|
38
|
+
const s = signalObject(H, ref, sha);
|
|
39
|
+
const b = branch(H, R, rev(H, R.ref) ?? C.fail('signal room has no branch here'));
|
|
40
|
+
const signer = b.entries.find(e => e.principal === s.actor) ?? C.fail(`unadmitted signal principal ${s.actor}`);
|
|
41
|
+
checkSignature(H, sha, s.actor, signer.key, 'tag');
|
|
42
|
+
const mark = s.signal === 'read' || s.signal === 'taken';
|
|
43
|
+
if (s.message === R.genesis)
|
|
44
|
+
C.fail('signal points at no message of this room');
|
|
45
|
+
const author = b.authors.get(s.message);
|
|
46
|
+
if (!(mark ? author : author === s.actor))
|
|
47
|
+
C.fail(`signal points at no accepted message ${mark ? 'of this room' : 'of its actor'}`);
|
|
48
|
+
};
|
|
49
|
+
export const verifySignal = (H, R, ref, sha) => {
|
|
50
|
+
if (!isRemote(H))
|
|
51
|
+
return null;
|
|
52
|
+
const alarm = H.remoteAlarms?.[ref];
|
|
53
|
+
if (alarm)
|
|
54
|
+
return alarm;
|
|
55
|
+
try {
|
|
56
|
+
checkSignal(H, R, ref, sha);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return reason(e);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=signals.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// signature: commits and tags checked against the key of their principal; a branch checked to be linear.
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import * as C from '../../../lib/index.js';
|
|
7
|
+
import * as P from '../../../core/hub/protocol/index.js';
|
|
8
|
+
import { git, mkdir } from './git.js';
|
|
9
|
+
import { signerLine } from './keys.js';
|
|
10
|
+
export const verified = new Set();
|
|
11
|
+
const signerFile = (H, text) => {
|
|
12
|
+
const dir = path.join(H.dir, 'ringi', 'verify');
|
|
13
|
+
mkdir(dir);
|
|
14
|
+
const file = path.join(dir, crypto.createHash('sha256').update(text).digest('hex') + '.allowed_signers');
|
|
15
|
+
if (!fs.existsSync(file))
|
|
16
|
+
fs.writeFileSync(file, text, { flag: 'wx', mode: 0o600 });
|
|
17
|
+
else if (fs.readFileSync(file, 'utf8') !== text)
|
|
18
|
+
C.fail('local signature verification cache was changed');
|
|
19
|
+
return file;
|
|
20
|
+
};
|
|
21
|
+
export const checkSignature = (H, sha, who, key, kind = 'commit') => {
|
|
22
|
+
const cache = `${kind}/${sha}/${who}/${key.fp}`;
|
|
23
|
+
if (verified.has(cache))
|
|
24
|
+
return;
|
|
25
|
+
const file = signerFile(H, signerLine(who, key));
|
|
26
|
+
const r = git(H, ['-c', `gpg.ssh.allowedSignersFile=${file}`, '-c', 'gpg.ssh.revocationFile=' + os.devNull, `verify-${kind}`, sha], { ok: true });
|
|
27
|
+
if (r.status !== 0)
|
|
28
|
+
C.fail(`bad or missing signature at ${sha.slice(0, 12)}: not signed by the admitted key of ${who}`);
|
|
29
|
+
const raw = git(H, ['cat-file', kind, sha], { raw: true });
|
|
30
|
+
const split = raw.indexOf(Buffer.from('\n\n'));
|
|
31
|
+
if (split < 0)
|
|
32
|
+
C.fail(`malformed ${kind}`);
|
|
33
|
+
const hdr = raw.subarray(0, split).toString('utf8');
|
|
34
|
+
if (/^encoding (?!UTF-8$|utf-8$)/m.test(hdr))
|
|
35
|
+
C.fail('commit declares a non-UTF-8 message');
|
|
36
|
+
const d = P.decodeText(raw.subarray(split + 2));
|
|
37
|
+
if (d.invalid)
|
|
38
|
+
C.fail(`${kind} message is ${d.invalid}`);
|
|
39
|
+
verified.add(cache);
|
|
40
|
+
if (verified.size > 4096)
|
|
41
|
+
verified.delete(verified.values().next().value);
|
|
42
|
+
};
|
|
43
|
+
export const chain = (H, head, base) => git(H, ['rev-list', '--parents', '--reverse', '--topo-order', head, ...(base ? [`^${base}`] : [])]).trim().split('\n').filter(Boolean).map(line => {
|
|
44
|
+
const [sha, ...parents] = line.split(' ');
|
|
45
|
+
return { sha, parents };
|
|
46
|
+
});
|
|
47
|
+
export const linear = (commits, initial, check) => {
|
|
48
|
+
let expect = initial;
|
|
49
|
+
for (const { sha, parents: ps } of commits) {
|
|
50
|
+
if (ps.length > 1 || (ps[0] || null) !== expect)
|
|
51
|
+
C.fail(`history is not linear at ${sha.slice(0, 12)}`);
|
|
52
|
+
check(sha);
|
|
53
|
+
expect = sha;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// sync-heartbeat: a fetched heartbeat verified and installed as the remote's current object; its errors stay apart
|
|
2
|
+
// from signal alarms.
|
|
3
|
+
import * as C from '../../../lib/index.js';
|
|
4
|
+
import { git, reason } from './git.js';
|
|
5
|
+
import { checkHeartbeat, heartbeatObject } from './heartbeat.js';
|
|
6
|
+
import { TAGS, TAG_ACCEPT, TAG_TRACK } from './types.js';
|
|
7
|
+
export const syncHeartbeat = ({ H, tags, pinned, byGenesis }, name) => {
|
|
8
|
+
const beatRef = TAGS + name;
|
|
9
|
+
if (pinned.has(TAG_ACCEPT + name)) {
|
|
10
|
+
H.remoteAlarms[beatRef] = 'heartbeat namespace overlaps a previously accepted signal; operator review required';
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const sha = tags.get(TAG_TRACK + name) ?? C.fail('heartbeat is absent from the remote');
|
|
15
|
+
const b = heartbeatObject(H, beatRef, sha);
|
|
16
|
+
const R = byGenesis.get(b.genesis) ?? C.fail('heartbeat room is missing or quarantined');
|
|
17
|
+
checkHeartbeat(H, R, beatRef, sha);
|
|
18
|
+
git(H, ['update-ref', beatRef, sha]);
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
H.heartbeatErrors[beatRef] = reason(e);
|
|
22
|
+
git(H, ['update-ref', '-d', beatRef]);
|
|
23
|
+
C.note(`heartbeat ${name}: ${reason(e)}; withheld`);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=sync-heartbeat.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// sync-rooms: the rooms' branches as fetched, verified, and indexed by genesis and by commit.
|
|
2
|
+
import * as C from '../../../lib/index.js';
|
|
3
|
+
import * as P from '../../../core/hub/protocol/index.js';
|
|
4
|
+
import { branchData } from './branch.js';
|
|
5
|
+
import { promote, reason, refs, rev } from './git.js';
|
|
6
|
+
import { ACCEPT, TRACK } from './types.js';
|
|
7
|
+
export const syncRooms = (H, fetched) => {
|
|
8
|
+
// Every other branch is a room.
|
|
9
|
+
const names = roomNames(H, fetched);
|
|
10
|
+
const byGenesis = new Map(), byTarget = new Map();
|
|
11
|
+
for (const name of names) {
|
|
12
|
+
const ref = 'refs/heads/' + name;
|
|
13
|
+
try {
|
|
14
|
+
if (!P.ID_RE.test(name))
|
|
15
|
+
C.fail('invalid room name');
|
|
16
|
+
const sha = fetched.get(TRACK + name) ?? C.fail(`${name} was deleted on the remote`);
|
|
17
|
+
const data = branchData(H, name, sha);
|
|
18
|
+
if (byGenesis.has(data.genesis))
|
|
19
|
+
C.fail('room genesis is already bound to another room');
|
|
20
|
+
promote(H, ref, sha);
|
|
21
|
+
// Local commits not yet published stay on top.
|
|
22
|
+
const local = branchData(H, name, rev(H, ref), data.genesis);
|
|
23
|
+
const R = { name, ref, head: local.control.at(-1), genesis: data.genesis };
|
|
24
|
+
byGenesis.set(data.genesis, R);
|
|
25
|
+
for (const target of [...local.control, ...local.authors.keys(), ...local.skipped.keys()])
|
|
26
|
+
byTarget.set(target, R);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
H.remoteAlarms[ref] = reason(e);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { byGenesis, byTarget };
|
|
33
|
+
};
|
|
34
|
+
const roomNames = (H, fetched) => {
|
|
35
|
+
const names = new Set([...fetched.keys(), ...refs(H, ACCEPT).keys()].map(r => r.slice(r.startsWith(TRACK) ? TRACK.length : ACCEPT.length)).filter(r => r !== 'main' && !r.includes('/')));
|
|
36
|
+
return names;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=sync-rooms.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// sync-tags: fetched tags sorted into signals (immutable, pinned) and heartbeats (replaced).
|
|
2
|
+
import * as C from '../../../lib/index.js';
|
|
3
|
+
import { git, object, reason, refs, zero } from './git.js';
|
|
4
|
+
import { heartbeatName } from './heartbeat.js';
|
|
5
|
+
import { SIGNAL_NAME, checkSignal, signalObject } from './signals.js';
|
|
6
|
+
import { TAGS, TAG_ACCEPT, TAG_TRACK } from './types.js';
|
|
7
|
+
import { syncHeartbeat } from './sync-heartbeat.js';
|
|
8
|
+
const missingHeartbeats = (H, tags, pinned) => {
|
|
9
|
+
// Include local beats to remove disappeared remote telemetry; unlike signals, nothing is pinned.
|
|
10
|
+
for (const ref of refs(H, TAGS).keys())
|
|
11
|
+
if (heartbeatName(ref) && !tags.has(TAG_TRACK + ref.slice(TAGS.length))) {
|
|
12
|
+
if (pinned.has(TAG_ACCEPT + ref.slice(TAGS.length)))
|
|
13
|
+
continue; // never waive previously accepted history
|
|
14
|
+
git(H, ['update-ref', '-d', ref]);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const syncSignal = ({ H, tags, pinned, byTarget }, name) => {
|
|
18
|
+
const ref = TAGS + name;
|
|
19
|
+
try {
|
|
20
|
+
const sha = tags.get(TAG_TRACK + name) ?? C.fail('signal was deleted on the remote after acceptance');
|
|
21
|
+
const s = signalObject(H, ref, sha);
|
|
22
|
+
const R = byTarget.get(s.message) ?? C.fail('signal room is missing or quarantined');
|
|
23
|
+
checkSignal(H, R, ref, sha);
|
|
24
|
+
const acc = pinned.get(TAG_ACCEPT + name), cur = object(H, ref);
|
|
25
|
+
if (acc && acc !== sha)
|
|
26
|
+
C.fail('signal was replaced after transport acceptance');
|
|
27
|
+
if (cur && cur !== sha)
|
|
28
|
+
C.fail('signal diverges from the local tag object');
|
|
29
|
+
git(H, ['update-ref', ref, sha, cur ?? zero(H)]);
|
|
30
|
+
git(H, ['update-ref', TAG_ACCEPT + name, sha, acc ?? zero(H)]);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
H.remoteAlarms[ref] = reason(e);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export const syncTags = (H, rooms) => {
|
|
37
|
+
const tags = refs(H, TAG_TRACK), pinned = refs(H, TAG_ACCEPT);
|
|
38
|
+
missingHeartbeats(H, tags, pinned);
|
|
39
|
+
const state = { H, tags, pinned, ...rooms };
|
|
40
|
+
const names = new Set([...tags.keys()].map(r => r.slice(TAG_TRACK.length)).concat([...pinned.keys()].map(r => r.slice(TAG_ACCEPT.length))));
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
if (heartbeatName(TAGS + name))
|
|
43
|
+
syncHeartbeat(state, name);
|
|
44
|
+
else if (SIGNAL_NAME.test(name))
|
|
45
|
+
syncSignal(state, name);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=sync-tags.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// sync: one fetch of a signed hub under its transport lock: contract, rooms, signals and heartbeats verified.
|
|
2
|
+
import * as C from '../../../lib/index.js';
|
|
3
|
+
import { pushAlarms } from './alarms.js';
|
|
4
|
+
import { git, isRemote, locked, origin, promote, reason, refs, rev, suffix } from './git.js';
|
|
5
|
+
import { checkUrl } from './keys.js';
|
|
6
|
+
import { verifyHub, verifyMain } from './main.js';
|
|
7
|
+
import { syncRooms } from './sync-rooms.js';
|
|
8
|
+
import { syncTags } from './sync-tags.js';
|
|
9
|
+
import { TAGS, TAG_TRACK, TRACK } from './types.js';
|
|
10
|
+
const syncUnlocked = (H) => {
|
|
11
|
+
H.remoteAlarms = { ...pushAlarms(H) };
|
|
12
|
+
H.heartbeatErrors = {};
|
|
13
|
+
const url = origin(H);
|
|
14
|
+
if (!url) {
|
|
15
|
+
if (rev(H, 'refs/heads/main'))
|
|
16
|
+
verifyHub(H);
|
|
17
|
+
return H;
|
|
18
|
+
}
|
|
19
|
+
checkUrl(url);
|
|
20
|
+
git(H, ['-c', 'fetch.fsckObjects=true', 'fetch', '-q', '--no-tags', '--prune', url,
|
|
21
|
+
`+refs/heads/*:${TRACK}*`, `+${TAGS}*:${TAG_TRACK}*`]);
|
|
22
|
+
const fetched = refs(H, TRACK);
|
|
23
|
+
const main = fetched.get(TRACK + 'main') || C.fail('ALARM remote main was deleted or is missing; refusing authority from the hub');
|
|
24
|
+
try {
|
|
25
|
+
verifyMain(H, main);
|
|
26
|
+
promote(H, 'refs/heads/main', main);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
C.fail(`ALARM hub ${H.name}: ${reason(e)}; nothing is delivered`);
|
|
30
|
+
}
|
|
31
|
+
H.main = rev(H, 'refs/heads/main');
|
|
32
|
+
syncTags(H, syncRooms(H, fetched));
|
|
33
|
+
for (const [ref, why] of Object.entries(H.remoteAlarms))
|
|
34
|
+
C.note(`ALARM ${suffix(ref)}: ${why}; quarantined and withheld`);
|
|
35
|
+
return H;
|
|
36
|
+
};
|
|
37
|
+
export const sync = (H) => !isRemote(H) ? H : locked(H, () => syncUnlocked(H));
|
|
38
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// types: what the signed transport's modules share: the hub handle, a room, keys, signers, a verified branch.
|
|
2
|
+
export const CONTROL_FILES = ['ROOM.md', 'members.json', 'profile.json', 'allowed_signers'];
|
|
3
|
+
export const SHA = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
4
|
+
export const ROOT = 'operator';
|
|
5
|
+
export const TRACK = 'refs/remotes/origin/';
|
|
6
|
+
export const ACCEPT = 'refs/ringi/remote-accepted/';
|
|
7
|
+
export const TAGS = 'refs/tags/';
|
|
8
|
+
export const TAG_TRACK = 'refs/ringi/remote-tags/';
|
|
9
|
+
export const TAG_ACCEPT = 'refs/ringi/remote-accepted-tags/';
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { withVerification } from './remote/cache.js';
|
|
2
|
+
export { gitEnv, gitTimeout, checkGitTimeout } from './remote/process.js';
|
|
3
|
+
export { isRemote } from './remote/git.js';
|
|
4
|
+
export { parseKey, setupSigned, sign, rootRoster } from './remote/keys.js';
|
|
5
|
+
export { verifyHub } from './remote/main.js';
|
|
6
|
+
export { readCommits } from './remote/commits.js';
|
|
7
|
+
export { branch, verifyRoom, verify } from './remote/branch.js';
|
|
8
|
+
export { heartbeatName, heartbeatRef, verifyHeartbeat, makeHeartbeat } from './remote/heartbeat.js';
|
|
9
|
+
export { verifySignal } from './remote/signals.js';
|
|
10
|
+
export { sync } from './remote/sync.js';
|
|
11
|
+
export { hubAdd } from './remote/bootstrap.js';
|
|
12
|
+
export { join, roomAdmit } from './remote/admit.js';
|
|
13
|
+
export { push } from './remote/push.js';
|
|
14
|
+
export { pushHeartbeat } from './remote/heartbeat-push.js';
|
|
15
|
+
export { forget } from './remote/forget.js';
|
|
16
|
+
export { strictJson } from '../../lib/index.js';
|
|
17
|
+
//# sourceMappingURL=remote.js.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// repo: the store's repository and the contract on its main branch. Every commit is made here (signed on a signed hub),
|
|
2
|
+
// every ref moves here compare-and-swap, and the work tree follows the contract so Git clients open the store cleanly.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
import { fail } from '../../lib/index.js';
|
|
7
|
+
import { mkdirIn } from '../fs/index.js';
|
|
8
|
+
import * as Remote from './remote.js';
|
|
9
|
+
import { ZERO, ident, runGit, runGitOk } from './run.js';
|
|
10
|
+
import { signCommit } from './signing.js';
|
|
11
|
+
// The store is a repository whose work tree holds the contract (`main`) as it is now, so Git clients (SourceTree, IDEs)
|
|
12
|
+
// open `store/` without errors, on the contract, with every room's branch beside it; `git status` stays clean.
|
|
13
|
+
export const init = (dir, home) => runGit(['init', '-q', '-b', 'main', path.dirname(dir)], { where: home });
|
|
14
|
+
export class Repo {
|
|
15
|
+
trust;
|
|
16
|
+
dir;
|
|
17
|
+
d;
|
|
18
|
+
home;
|
|
19
|
+
// The handle remote.ts works on: the repository and what it learned on the last sync.
|
|
20
|
+
h;
|
|
21
|
+
constructor(hubDir, d, home, signing) {
|
|
22
|
+
this.d = d;
|
|
23
|
+
this.home = home;
|
|
24
|
+
this.trust = d.trust;
|
|
25
|
+
this.dir = path.join(hubDir, d.store.path, '.git');
|
|
26
|
+
this.h = { name: d.name, dir: this.dir, d, ...(d.trust === 'signed' && signing ? { signing } : {}) };
|
|
27
|
+
if (!fs.existsSync(this.dir))
|
|
28
|
+
fail(`hub ${d.name}: no git store at ${this.dir}`);
|
|
29
|
+
this.openable();
|
|
30
|
+
}
|
|
31
|
+
// A store made bare before, or a replica fetched bare, gets its work tree once (and loses the `_ringi` branch of an unreleased build).
|
|
32
|
+
openable() {
|
|
33
|
+
let changed = false;
|
|
34
|
+
if (/^\s*bare\s*=\s*true\s*$/m.test(fs.readFileSync(path.join(this.dir, 'config'), 'utf8'))) {
|
|
35
|
+
this.git(['config', 'core.bare', 'false']);
|
|
36
|
+
changed = true;
|
|
37
|
+
}
|
|
38
|
+
if (fs.readFileSync(path.join(this.dir, 'HEAD'), 'utf8') !== 'ref: refs/heads/main\n') {
|
|
39
|
+
this.git(['symbolic-ref', 'HEAD', 'refs/heads/main']);
|
|
40
|
+
changed = true;
|
|
41
|
+
}
|
|
42
|
+
if (fs.existsSync(path.join(this.dir, 'refs', 'heads', '_ringi'))) {
|
|
43
|
+
this.gitOk(['update-ref', '-d', 'refs/heads/_ringi']);
|
|
44
|
+
changed = true;
|
|
45
|
+
}
|
|
46
|
+
if (changed)
|
|
47
|
+
this.checkout();
|
|
48
|
+
}
|
|
49
|
+
// The work tree and the index follow the contract, so a Git client sees the store clean.
|
|
50
|
+
checkout() {
|
|
51
|
+
if (this.rev('refs/heads/main'))
|
|
52
|
+
this.git(['--work-tree', path.dirname(this.dir), 'read-tree', '-u', '--reset', 'refs/heads/main']);
|
|
53
|
+
}
|
|
54
|
+
args(a) { return ['--git-dir', this.dir, ...a]; }
|
|
55
|
+
git(a, o) { return runGit(this.args(a), { ...o, where: this.home }); }
|
|
56
|
+
gitOk(a, o) { return runGitOk(this.args(a), o); }
|
|
57
|
+
rev(ref) {
|
|
58
|
+
const r = this.gitOk(['rev-parse', '-q', '--verify', `${ref}^{commit}`]);
|
|
59
|
+
return r.status === 0 ? r.stdout.trim() : null;
|
|
60
|
+
}
|
|
61
|
+
isAncestor(a, b) { return this.gitOk(['merge-base', '--is-ancestor', a, b]).status === 0; }
|
|
62
|
+
isRef(s) { return /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/.test(s ?? ''); }
|
|
63
|
+
extends(older, newer) { return older === newer || this.isAncestor(older, newer); }
|
|
64
|
+
// A tree from a base tree without the `drop` paths, with entries put on top; nested paths are fine.
|
|
65
|
+
tree(base, entries, drop = []) {
|
|
66
|
+
const idx = path.join(this.home, 'tmp', `index.${process.pid}.${crypto.randomBytes(4).toString('hex')}`);
|
|
67
|
+
mkdirIn(this.home, path.dirname(idx));
|
|
68
|
+
const env = { GIT_INDEX_FILE: idx };
|
|
69
|
+
try {
|
|
70
|
+
this.git(['read-tree', ...(base ? [base] : ['--empty'])], { env });
|
|
71
|
+
if (drop.length)
|
|
72
|
+
this.git(['update-index', '--index-info'], { env, input: drop.map(p => `0 ${ZERO}\t${p}\n`).join('') });
|
|
73
|
+
for (const e of entries) {
|
|
74
|
+
const sha = e.blob ?? this.git(['hash-object', '-w', ...(e.file ? ['--', e.file] : ['--stdin'])], { input: e.content }).trim();
|
|
75
|
+
this.git(['update-index', '--add', '--cacheinfo', `${e.mode},${sha},${e.path}`], { env });
|
|
76
|
+
}
|
|
77
|
+
return this.git(['write-tree'], { env }).trim();
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
fs.rmSync(idx, { force: true });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Every commit is made here: on a signed hub, signed by its author's key on this machine.
|
|
84
|
+
commit(who, text, tree, parents) {
|
|
85
|
+
const sha = this.git(['commit-tree', ...(this.h.signing ? [] : Remote.sign(this.h, who, this.home)), tree, ...parents.flatMap(p => ['-p', p])], { input: text, env: ident(who) }).trim();
|
|
86
|
+
return signCommit(this.h, who, sha, this.home);
|
|
87
|
+
}
|
|
88
|
+
advance(ref, sha, old) {
|
|
89
|
+
if (this.gitOk(['update-ref', ref, sha, old ?? ZERO]).status !== 0)
|
|
90
|
+
return false;
|
|
91
|
+
// A signed hub publishes at once; a rejected push keeps the commit here and alarms (remote.ts).
|
|
92
|
+
return Remote.push(this.h, ref, { behind: !!old && ref !== 'refs/heads/main' });
|
|
93
|
+
}
|
|
94
|
+
sync() {
|
|
95
|
+
const main = this.rev('refs/heads/main');
|
|
96
|
+
Remote.sync(this.h);
|
|
97
|
+
if (this.rev('refs/heads/main') !== main)
|
|
98
|
+
this.checkout();
|
|
99
|
+
}
|
|
100
|
+
contractHead() { return this.rev('refs/heads/main') ?? fail(`hub ${this.d.name} has no contract (main)`); }
|
|
101
|
+
contractFile(rev, name) {
|
|
102
|
+
const r = this.gitOk(['show', `${rev}:${name}`]);
|
|
103
|
+
return r.status === 0 ? r.stdout : null;
|
|
104
|
+
}
|
|
105
|
+
contractFiles(rev) { return this.git(['ls-tree', '-r', '--name-only', rev]).split('\n').filter(Boolean); }
|
|
106
|
+
amendContract(who, text, files) {
|
|
107
|
+
const head = this.contractHead();
|
|
108
|
+
const sha = this.commit(who, text, this.tree(`${head}^{tree}`, Object.entries(files).map(([p, c]) => ({ path: p, content: c, mode: '100644' }))), [head]);
|
|
109
|
+
if (!this.advance('refs/heads/main', sha, head))
|
|
110
|
+
fail('the contract moved while amending; retry');
|
|
111
|
+
this.checkout();
|
|
112
|
+
return sha;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=repo.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// run: git as a child process, with the hub's timeout, its isolated environment and the sandbox's one message; and
|
|
2
|
+
// splitting what it prints.
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { fail } from '../../lib/index.js';
|
|
5
|
+
import { cannotWrite, SANDBOX } from '../fs/index.js';
|
|
6
|
+
import * as Remote from './remote.js';
|
|
7
|
+
export const ZERO = '0'.repeat(40);
|
|
8
|
+
export const ident = (id) => ({ GIT_AUTHOR_NAME: id, GIT_AUTHOR_EMAIL: `${id}@ringi`, GIT_COMMITTER_NAME: id, GIT_COMMITTER_EMAIL: `${id}@ringi` });
|
|
9
|
+
export const runGit = (args, o = {}) => {
|
|
10
|
+
const r = spawnSync('git', args, { input: o.input, encoding: 'utf8', env: Remote.gitEnv(o.env), maxBuffer: 1 << 30, timeout: Remote.gitTimeout() });
|
|
11
|
+
return check(args, r, o.where).stdout;
|
|
12
|
+
};
|
|
13
|
+
export const runGitOk = (args, o = {}) => {
|
|
14
|
+
const r = spawnSync('git', args, { input: o.input, encoding: 'utf8', env: Remote.gitEnv(o.env), maxBuffer: 1 << 30, timeout: Remote.gitTimeout() });
|
|
15
|
+
Remote.checkGitTimeout(r.error);
|
|
16
|
+
return r;
|
|
17
|
+
};
|
|
18
|
+
export const runGitRaw = (args, where, input) => {
|
|
19
|
+
const r = spawnSync('git', args, { input, encoding: 'buffer', env: Remote.gitEnv(), maxBuffer: 1 << 30, timeout: Remote.gitTimeout() });
|
|
20
|
+
return check(args, r, where).stdout;
|
|
21
|
+
};
|
|
22
|
+
function check(args, r, where = 'the hub store') {
|
|
23
|
+
Remote.checkGitTimeout(r.error);
|
|
24
|
+
if (r.error)
|
|
25
|
+
throw r.error;
|
|
26
|
+
if (r.status !== 0) {
|
|
27
|
+
const err = String(r.stderr ?? '').trim();
|
|
28
|
+
if (SANDBOX.test(err))
|
|
29
|
+
cannotWrite(where, err.split('\n').at(-1));
|
|
30
|
+
fail(`git ${args.find(a => !a.startsWith('-') && !a.includes('/')) ?? ''}: ${err.split('\n').at(-1)}`);
|
|
31
|
+
}
|
|
32
|
+
return r;
|
|
33
|
+
}
|
|
34
|
+
export const splitBuf = (b, sep) => {
|
|
35
|
+
const parts = [];
|
|
36
|
+
let at = 0;
|
|
37
|
+
for (let i = b.indexOf(sep); i >= 0; i = b.indexOf(sep, at)) {
|
|
38
|
+
parts.push(b.subarray(at, i));
|
|
39
|
+
at = i + 1;
|
|
40
|
+
}
|
|
41
|
+
parts.push(b.subarray(at));
|
|
42
|
+
return parts;
|
|
43
|
+
};
|
|
44
|
+
// Objects of `git cat-file --batch`, read whole by their size, so no byte inside one can split it.
|
|
45
|
+
export const frames = (raw, shas) => {
|
|
46
|
+
const out = new Map();
|
|
47
|
+
let at = 0;
|
|
48
|
+
for (const sha of shas) {
|
|
49
|
+
const nl = raw.indexOf(0x0a, at);
|
|
50
|
+
const [got, type, size] = raw.subarray(at, nl).toString('utf8').split(' ');
|
|
51
|
+
if (got === sha)
|
|
52
|
+
out.set(sha, { type, body: raw.subarray(nl + 1, nl + 1 + Number(size)) });
|
|
53
|
+
at = nl + 1 + Number(size) + 1;
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// GitSigningBridge: Signer signs the exact unsigned Git object. Tags append its SSH armor; commits fold it into
|
|
2
|
+
// gpgsig. Custody is injected; only legacy direct entrypoints retain their previous file-key signing default.
|
|
3
|
+
import { fail } from '../../lib/index.js';
|
|
4
|
+
import { git, id, ident, isRemote } from './remote/git.js';
|
|
5
|
+
import { identityKey, parseKey, rootKey, sign } from './remote/keys.js';
|
|
6
|
+
import { command } from './remote/process.js';
|
|
7
|
+
import { checkSignature } from './remote/signature.js';
|
|
8
|
+
export const signBytes = (H, who, payload, home) => {
|
|
9
|
+
if (!isRemote(H))
|
|
10
|
+
return '';
|
|
11
|
+
if (!H.signing) {
|
|
12
|
+
const file = sign(H, who, home)[0].slice(2);
|
|
13
|
+
return command('ssh-keygen', ['-Y', 'sign', '-n', 'git', '-f', file], { input: payload });
|
|
14
|
+
}
|
|
15
|
+
const context = H.signing, key = context.resolve(id(H.name), id(who));
|
|
16
|
+
const identity = context.signer.publicIdentity(key), publicKey = parseKey(identity.key);
|
|
17
|
+
if (who === 'operator' && publicKey.line !== rootKey(H).line)
|
|
18
|
+
fail('the local operator key does not match this hub\'s pinned root');
|
|
19
|
+
const signature = context.signer.sign({ key, payload: Buffer.isBuffer(payload) ? payload : Buffer.from(payload), namespace: 'git', interaction: 'forbidden', deadline: performance.now() + 30000 });
|
|
20
|
+
if (signature.format !== 'ssh' || signature.namespace !== 'git' || signature.fingerprint !== publicKey.fp)
|
|
21
|
+
fail('the signer returned an incompatible signature identity or format');
|
|
22
|
+
return Buffer.from(signature.bytes).toString('utf8');
|
|
23
|
+
};
|
|
24
|
+
// The legacy constructor path already asked Git to sign; only an injected provider uses the neutral byte bridge.
|
|
25
|
+
export const signCommit = (H, who, sha, home) => {
|
|
26
|
+
if (!H.signing || !isRemote(H))
|
|
27
|
+
return sha;
|
|
28
|
+
const raw = git(H, ['cat-file', 'commit', sha], { raw: true }), end = raw.indexOf('\n\n');
|
|
29
|
+
if (end < 0)
|
|
30
|
+
fail('malformed unsigned commit');
|
|
31
|
+
const signature = signBytes(H, who, raw, home).trimEnd().replace(/\n/g, '\n ');
|
|
32
|
+
const body = Buffer.concat([raw.subarray(0, end), Buffer.from(`\ngpgsig ${signature}\n\n`), raw.subarray(end + 2)]);
|
|
33
|
+
const signed = git(H, ['hash-object', '-t', 'commit', '-w', '--stdin'], { input: body }).trim();
|
|
34
|
+
checkSignature(H, signed, who, identityKey(H, who, home));
|
|
35
|
+
return signed;
|
|
36
|
+
};
|
|
37
|
+
export const signedTag = (H, who, tag, target, home) => {
|
|
38
|
+
const tagger = git(H, ['var', 'GIT_COMMITTER_IDENT'], { env: ident(who) }).trim();
|
|
39
|
+
const raw = `object ${target}\ntype commit\ntag ${tag}\ntagger ${tagger}\n\n`;
|
|
40
|
+
const signed = git(H, ['mktag'], { input: raw + signBytes(H, who, raw, home) }).trim();
|
|
41
|
+
checkSignature(H, signed, who, identityKey(H, who, home), 'tag');
|
|
42
|
+
return signed;
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=signing.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// tags: signal tags as git lists them, and an annotated tag object read whole.
|
|
2
|
+
const SIGNAL_REF = /^refs\/tags\/([a-z0-9]+(?:[_-][a-z0-9]+)*)\.([a-z][a-z0-9.-]*\.[0-9a-f]{12})$/;
|
|
3
|
+
// `for-each-ref` lines `<ref> <object> <type>` of tags named `<actor>.<signal>.<message12>`, but for heartbeats.
|
|
4
|
+
export const signalTags = (listing, heartbeat) => listing.split('\n').flatMap(line => {
|
|
5
|
+
const [ref, object, type] = line.split(' ');
|
|
6
|
+
const m = SIGNAL_REF.exec(ref ?? '');
|
|
7
|
+
return m && !heartbeat(ref) ? [{ ref, actor: m[1], name: m[2], object, type }] : [];
|
|
8
|
+
});
|
|
9
|
+
// A tag object's target commit, tagger and time, and its text without a signature.
|
|
10
|
+
export const tagObject = (body) => {
|
|
11
|
+
const split = body.indexOf('\n\n');
|
|
12
|
+
const hdr = body.subarray(0, split < 0 ? body.length : split).toString('utf8');
|
|
13
|
+
const tagger = /^tagger (.*?) <[^>\n]*> (\d+) [+-]\d{4}$/m.exec(hdr);
|
|
14
|
+
return {
|
|
15
|
+
target: /^object ([0-9a-f]+)\ntype commit$/m.exec(hdr)?.[1] ?? '',
|
|
16
|
+
tagger: tagger?.[1] ?? '',
|
|
17
|
+
time: Number(tagger?.[2] ?? NaN),
|
|
18
|
+
text: split < 0 ? '' : body.subarray(split + 2).toString('utf8').split('-----BEGIN SSH SIGNATURE-----')[0],
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=tags.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// transport: the git store as a store type: a hub's repository is `store/.git` in the hub's directory; a signed hub's
|
|
2
|
+
// root key is made here (remote/keys.ts) and a published hub is replicated by bootstrap (remote/bootstrap.ts).
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { GitStore } from './index.js';
|
|
5
|
+
import * as Remote from './remote.js';
|
|
6
|
+
const context = (d, opt) => d.trust === 'signed'
|
|
7
|
+
? typeof opt.signer === 'function' ? opt.signer() : opt.signer
|
|
8
|
+
: undefined;
|
|
9
|
+
// `home`: where this machine keeps its state; the git store keeps its keys and scratch indexes there.
|
|
10
|
+
export const gitTransport = (home, opt = {}) => ({
|
|
11
|
+
// One branch per room with its messages and control records, signals as tags on them (hub-1, Layout).
|
|
12
|
+
layout: 'git/2',
|
|
13
|
+
// Before contracts named it: ringi 0.1 kept a room as branches under `<room>/` (git/1).
|
|
14
|
+
layoutOf: store => store.roomBranches().some(b => b.includes('/')) ? 'git/1' : 'git/2',
|
|
15
|
+
open: (at, d) => new GitStore(at, d, home, context(d, opt)),
|
|
16
|
+
create: (at, d, genesis) => {
|
|
17
|
+
const signing = context(d, opt);
|
|
18
|
+
if (d.trust === 'signed')
|
|
19
|
+
d.root = GitStore.rootFor(at, d, home, signing);
|
|
20
|
+
const g = genesis(d);
|
|
21
|
+
const store = GitStore.create(at, d, { home, signing }, g.files, g.text);
|
|
22
|
+
if (d.trust === 'signed')
|
|
23
|
+
d.genesis = store.contractHead();
|
|
24
|
+
const custody = d.trust !== 'signed' ? null : signing ? signing.custody?.(d.name, 'operator') ?? null : path.join(home, 'keys', d.name, 'operator');
|
|
25
|
+
return { store, custody };
|
|
26
|
+
},
|
|
27
|
+
attach: (name, url, pins) => { Remote.hubAdd({ home, name, url, ...pins }); },
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { fail } from '../../lib/index.js';
|
|
2
|
+
const planned = (d) => fail(`hub ${d.name}: the kafka store is planned and not implemented in this ringi`);
|
|
3
|
+
export const kafka = { layout: 'kafka/1', open: (_at, d) => planned(d), create: (_at, d) => planned(d) };
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { fail } from '../../lib/index.js';
|
|
2
|
+
const planned = (d) => fail(`hub ${d.name}: the temporal store is planned and not implemented in this ringi`);
|
|
3
|
+
export const temporal = { layout: 'temporal/1', open: (_at, d) => planned(d), create: (_at, d) => planned(d) };
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
package/target/esm/cli.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// The `ringi` executable. The interface itself is cli/; this file only runs it
|
|
2
|
+
// The `ringi` executable. The interface itself is clients/cli/, put together by composition/; this file only runs it
|
|
3
|
+
// when started as a program.
|
|
3
4
|
import fs from 'node:fs';
|
|
4
5
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
6
|
+
import './composition/index.js';
|
|
7
|
+
import { failure, main } from './clients/cli/index.js';
|
|
7
8
|
export { main };
|
|
8
9
|
if (process.argv[1] && fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
9
10
|
main().catch(e => {
|
|
10
|
-
process.stderr.write(`ringi: ${e
|
|
11
|
+
process.stderr.write(`ringi: ${failure(e)}\n`);
|
|
11
12
|
process.exitCode = 1;
|
|
12
13
|
});
|
|
13
14
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
// agent: the commands only an agent runs, as the agent its session is bound to (`ringi agent inbox`, whoami, card,
|
|
2
|
+
// ping, wait, take); joining and leaving a room are session.ts.
|
|
3
|
+
import * as Agent from '../../../core/agents/sessions/agent.js';
|
|
4
|
+
import { asJson, render } from '../../../core/hub/usecases/view.js';
|
|
5
|
+
import { fail, short, when } from '../../../lib/index.js';
|
|
6
|
+
import { dump, flag, list, opt, out, textOf } from '../args.js';
|
|
7
|
+
import { scopeLine, waiting } from '../shared/index.js';
|
|
7
8
|
const options = (o) => ({ peek: flag(o, 'peek'), limit: opt(o, 'limit') ? Number(opt(o, 'limit')) : undefined });
|
|
8
9
|
export const showInbox = ({ R, me, s }, res, o) => {
|
|
9
10
|
if (flag(o, 'json'))
|
|
@@ -43,4 +44,4 @@ export const wait = async (a, o) => {
|
|
|
43
44
|
out(`wait: nothing for ${a.me} in ${Math.round(r.seconds)}s`);
|
|
44
45
|
process.exitCode = 3;
|
|
45
46
|
};
|
|
46
|
-
//# sourceMappingURL=
|
|
47
|
+
//# sourceMappingURL=index.js.map
|