cloving 1.2.0 → 1.3.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 +525 -185
- package/dist/src/agent-harness-shell.d.ts +12 -0
- package/dist/src/agent-harness-shell.js +90 -0
- package/dist/src/agent-harness-versions.d.ts +8 -0
- package/dist/src/agent-harness-versions.js +49 -0
- package/dist/src/agent-harnesses.d.ts +4 -0
- package/dist/src/agent-harnesses.js +13 -0
- package/dist/src/authority/commands.d.ts +1 -0
- package/dist/src/authority/commands.js +24 -4
- package/dist/src/authority/gate-reduction.d.ts +1 -0
- package/dist/src/authority/gate-reduction.js +18 -3
- package/dist/src/authority/gates.d.ts +2 -1
- package/dist/src/authority/gates.js +5 -1
- package/dist/src/authority/operations.js +1 -1
- package/dist/src/authority/reduction.d.ts +1 -1
- package/dist/src/authority/reduction.js +6 -1
- package/dist/src/authority/status-input.d.ts +1 -1
- package/dist/src/authority/status.d.ts +1 -0
- package/dist/src/authority/status.js +27 -3
- package/dist/src/authority/transitions.d.ts +5 -5
- package/dist/src/authority/transitions.js +6 -0
- package/dist/src/broker/actor/authority-delivery.js +1 -0
- package/dist/src/broker/actor/authority-queries.d.ts +7 -1
- package/dist/src/broker/actor/authority-queries.js +17 -2
- package/dist/src/broker/actor/authority-receipts.js +11 -1
- package/dist/src/broker/actor/authority-session.js +2 -2
- package/dist/src/broker/actor/authority-settings.js +4 -0
- package/dist/src/broker/actor/delivery-intents.d.ts +2 -1
- package/dist/src/broker/actor/delivery-intents.js +4 -4
- package/dist/src/broker/actor/effect-delivery.js +4 -1
- package/dist/src/broker/actor/effect-queries.d.ts +1 -0
- package/dist/src/broker/actor/effect-queries.js +7 -0
- package/dist/src/broker/actor/pin-check.js +1 -1
- package/dist/src/broker/actor/stop-plan.js +1 -1
- package/dist/src/broker/actor/turn-state.js +1 -1
- package/dist/src/broker/authority-admission.js +22 -9
- package/dist/src/broker/authority-restore.js +1 -0
- package/dist/src/broker/decision-restore.js +4 -4
- package/dist/src/broker/socket-address.d.ts +3 -1
- package/dist/src/broker/socket-address.js +2 -2
- package/dist/src/cli/arguments.d.ts +11 -13
- package/dist/src/cli/arguments.js +37 -107
- package/dist/src/cli/command-effects.d.ts +7 -0
- package/dist/src/cli/command-effects.js +39 -0
- package/dist/src/cli/commands.js +11 -4
- package/dist/src/cli/config-arguments.d.ts +11 -0
- package/dist/src/cli/config-arguments.js +59 -0
- package/dist/src/cli/config-command.d.ts +2 -0
- package/dist/src/cli/config-command.js +105 -0
- package/dist/src/cli/config-file.d.ts +3 -0
- package/dist/src/cli/config-file.js +50 -0
- package/dist/src/cli/config-lock.d.ts +7 -0
- package/dist/src/cli/config-lock.js +61 -0
- package/dist/src/cli/config-notice.d.ts +5 -0
- package/dist/src/cli/config-notice.js +46 -0
- package/dist/src/cli/doctor-github.d.ts +9 -0
- package/dist/src/cli/doctor-github.js +138 -0
- package/dist/src/cli/doctor-harness.d.ts +13 -0
- package/dist/src/cli/doctor-harness.js +242 -0
- package/dist/src/cli/doctor-local.d.ts +18 -0
- package/dist/src/cli/doctor-local.js +77 -0
- package/dist/src/cli/doctor-runtime.d.ts +15 -0
- package/dist/src/cli/doctor-runtime.js +176 -0
- package/dist/src/cli/doctor-service.d.ts +1 -0
- package/dist/src/cli/doctor-service.js +11 -0
- package/dist/src/cli/doctor-workflows.d.ts +3 -0
- package/dist/src/cli/doctor-workflows.js +45 -0
- package/dist/src/cli/doctor.d.ts +15 -0
- package/dist/src/cli/doctor.js +190 -0
- package/dist/src/cli/github-ci-progress.d.ts +8 -0
- package/dist/src/cli/github-ci-progress.js +83 -0
- package/dist/src/cli/github-retry.d.ts +6 -0
- package/dist/src/cli/github-retry.js +47 -0
- package/dist/src/cli/harness-probes.d.ts +22 -0
- package/dist/src/cli/harness-probes.js +183 -0
- package/dist/src/cli/main.d.ts +7 -1
- package/dist/src/cli/main.js +39 -61
- package/dist/src/cli/phone-arguments.d.ts +14 -0
- package/dist/src/cli/phone-arguments.js +37 -0
- package/dist/src/cli/phone-client.d.ts +2 -0
- package/dist/src/cli/phone-client.js +32 -0
- package/dist/src/cli/phone-command.d.ts +13 -0
- package/dist/src/cli/phone-command.js +101 -0
- package/dist/src/cli/probe-command.d.ts +36 -0
- package/dist/src/cli/probe-command.js +59 -0
- package/dist/src/cli/project-settings.d.ts +4 -0
- package/dist/src/cli/project-settings.js +33 -0
- package/dist/src/cli/render.d.ts +2 -1
- package/dist/src/cli/render.js +44 -28
- package/dist/src/cli/report-github.d.ts +18 -0
- package/dist/src/cli/report-github.js +200 -0
- package/dist/src/cli/report.d.ts +17 -0
- package/dist/src/cli/report.js +192 -0
- package/dist/src/cli/repository-arguments.d.ts +5 -0
- package/dist/src/cli/repository-arguments.js +34 -0
- package/dist/src/cli/runtime-arguments.d.ts +4 -0
- package/dist/src/cli/runtime-arguments.js +15 -0
- package/dist/src/cli/runtime-directory.d.ts +6 -2
- package/dist/src/cli/runtime-directory.js +72 -14
- package/dist/src/cli/toolchain.d.ts +2 -0
- package/dist/src/cli/toolchain.js +22 -2
- package/dist/src/cli/usage-render.d.ts +5 -0
- package/dist/src/cli/usage-render.js +40 -1
- package/dist/src/cli/usage.d.ts +3 -3
- package/dist/src/cli/usage.js +70 -44
- package/dist/src/cli/watch-actions.d.ts +2 -0
- package/dist/src/cli/watch-actions.js +4 -4
- package/dist/src/cli/watch-board-view.d.ts +1 -1
- package/dist/src/cli/watch-board-view.js +8 -2
- package/dist/src/cli/watch-board.js +23 -10
- package/dist/src/cli/watch-card-forms.js +2 -2
- package/dist/src/cli/watch-client.d.ts +1 -1
- package/dist/src/cli/watch-client.js +7 -2
- package/dist/src/cli/watch-header-view.d.ts +27 -0
- package/dist/src/cli/watch-header-view.js +116 -0
- package/dist/src/cli/watch-model.js +17 -4
- package/dist/src/cli/watch-page.js +48 -12
- package/dist/src/cli/watch-phone-client.d.ts +1 -0
- package/dist/src/cli/watch-phone-client.js +84 -0
- package/dist/src/cli/watch-phone-view.d.ts +56 -0
- package/dist/src/cli/watch-phone-view.js +113 -0
- package/dist/src/cli/watch-phone.d.ts +5 -0
- package/dist/src/cli/watch-phone.js +53 -0
- package/dist/src/cli/watch-pull-request.d.ts +3 -1
- package/dist/src/cli/watch-pull-request.js +4 -0
- package/dist/src/cli/watch-render.d.ts +9 -1
- package/dist/src/cli/watch-server.js +23 -5
- package/dist/src/cli/watch-turn.d.ts +4 -1
- package/dist/src/cli/watch-turn.js +37 -1
- package/dist/src/cli/watch-usage.js +5 -1
- package/dist/src/cli/watch-view.d.ts +6 -20
- package/dist/src/cli/watch-view.js +14 -106
- package/dist/src/cli.js +106 -23
- package/dist/src/command-settings.d.ts +16 -0
- package/dist/src/command-settings.js +42 -0
- package/dist/src/configuration-change.d.ts +11 -0
- package/dist/src/configuration-change.js +62 -0
- package/dist/src/configuration-display.d.ts +21 -0
- package/dist/src/configuration-display.js +69 -0
- package/dist/src/configuration-schema.d.ts +49 -0
- package/dist/src/configuration-schema.js +159 -0
- package/dist/src/daemon/claude-turn.d.ts +19 -5
- package/dist/src/daemon/claude-turn.js +116 -53
- package/dist/src/daemon/claude-worker.d.ts +3 -3
- package/dist/src/daemon/claude-worker.js +65 -76
- package/dist/src/daemon/command-journal.d.ts +5 -0
- package/dist/src/daemon/command-journal.js +50 -0
- package/dist/src/daemon/command-process.d.ts +28 -0
- package/dist/src/daemon/command-process.js +164 -0
- package/dist/src/daemon/command-worker.d.ts +19 -0
- package/dist/src/daemon/command-worker.js +182 -0
- package/dist/src/daemon/cursor-stream.d.ts +4 -0
- package/dist/src/daemon/cursor-stream.js +154 -0
- package/dist/src/daemon/daemon-adapters.d.ts +58 -0
- package/dist/src/daemon/daemon-adapters.js +102 -0
- package/dist/src/daemon/daemon-loops.d.ts +14 -0
- package/dist/src/daemon/daemon-loops.js +39 -0
- package/dist/src/daemon/daemon.d.ts +2 -33
- package/dist/src/daemon/daemon.js +126 -132
- package/dist/src/daemon/declared-input.d.ts +3 -0
- package/dist/src/daemon/declared-input.js +9 -0
- package/dist/src/daemon/delivery-router.js +1 -1
- package/dist/src/daemon/gemini-stream.d.ts +4 -0
- package/dist/src/daemon/gemini-stream.js +129 -0
- package/dist/src/daemon/github-ci-rerun.d.ts +26 -0
- package/dist/src/daemon/github-ci-rerun.js +92 -0
- package/dist/src/daemon/github-ci.js +89 -8
- package/dist/src/daemon/github-merge-call.d.ts +2 -0
- package/dist/src/daemon/github-merge.js +7 -3
- package/dist/src/daemon/github-methods.js +10 -0
- package/dist/src/daemon/github-operation.d.ts +1 -0
- package/dist/src/daemon/github-retry.js +8 -44
- package/dist/src/daemon/issue-queue.d.ts +1 -0
- package/dist/src/daemon/issue-queue.js +3 -26
- package/dist/src/daemon/notify.js +2 -1
- package/dist/src/daemon/opencode-stream.d.ts +5 -0
- package/dist/src/daemon/opencode-stream.js +87 -0
- package/dist/src/daemon/queue-status.d.ts +4 -0
- package/dist/src/daemon/queue-status.js +38 -0
- package/dist/src/daemon/retry-worktree.d.ts +18 -0
- package/dist/src/daemon/retry-worktree.js +139 -0
- package/dist/src/daemon/retrying-turn.d.ts +2 -0
- package/dist/src/daemon/retrying-turn.js +108 -30
- package/dist/src/daemon/schedule-journal.d.ts +19 -0
- package/dist/src/daemon/schedule-journal.js +57 -0
- package/dist/src/daemon/text-output.d.ts +18 -0
- package/dist/src/daemon/text-output.js +44 -0
- package/dist/src/daemon/ticket-registration.d.ts +5 -7
- package/dist/src/daemon/ticket-registration.js +85 -40
- package/dist/src/daemon/turn-command.d.ts +10 -0
- package/dist/src/daemon/turn-command.js +33 -0
- package/dist/src/daemon/turn-report.d.ts +18 -0
- package/dist/src/daemon/turn-report.js +79 -0
- package/dist/src/daemon/work-tree.d.ts +2 -1
- package/dist/src/daemon/work-tree.js +15 -12
- package/dist/src/daemon/workflow-generations.d.ts +32 -0
- package/dist/src/daemon/workflow-generations.js +57 -0
- package/dist/src/daemon/workflow-replay.d.ts +16 -0
- package/dist/src/daemon/workflow-replay.js +58 -0
- package/dist/src/daemon/workflow-schedules.d.ts +27 -0
- package/dist/src/daemon/workflow-schedules.js +185 -0
- package/dist/src/decision-wait.d.ts +1 -0
- package/dist/src/decision-wait.js +3 -0
- package/dist/src/delivery-backoff.d.ts +1 -0
- package/dist/src/delivery-backoff.js +3 -0
- package/dist/src/doctor-report.d.ts +24 -0
- package/dist/src/doctor-report.js +70 -0
- package/dist/src/execution-pin.d.ts +5 -0
- package/dist/src/execution-pin.js +67 -4
- package/dist/src/grok-result.d.ts +12 -0
- package/dist/src/grok-result.js +61 -0
- package/dist/src/harness-defaults.d.ts +35 -0
- package/dist/src/harness-defaults.js +121 -0
- package/dist/src/host/decision-resume.d.ts +35 -0
- package/dist/src/host/decision-resume.js +86 -0
- package/dist/src/host/gate-bridge.d.ts +48 -0
- package/dist/src/host/gate-bridge.js +124 -0
- package/dist/src/host/run-wait.d.ts +26 -0
- package/dist/src/host/run-wait.js +37 -0
- package/dist/src/host/start-delivery.d.ts +1 -1
- package/dist/src/host/start-delivery.js +10 -3
- package/dist/src/host/step-bridge-host.d.ts +6 -20
- package/dist/src/host/step-bridge-host.js +44 -58
- package/dist/src/merge-request.d.ts +19 -0
- package/dist/src/merge-request.js +32 -0
- package/dist/src/phone/canonical-json.d.ts +1 -0
- package/dist/src/phone/canonical-json.js +16 -0
- package/dist/src/phone/phone-link.d.ts +7 -0
- package/dist/src/phone/phone-link.js +21 -0
- package/dist/src/phone/qr-data.d.ts +8 -0
- package/dist/src/phone/qr-data.js +133 -0
- package/dist/src/phone/qr-matrix.d.ts +6 -0
- package/dist/src/phone/qr-matrix.js +175 -0
- package/dist/src/phone/qr-penalty.d.ts +3 -0
- package/dist/src/phone/qr-penalty.js +67 -0
- package/dist/src/phone/qr-picture.d.ts +5 -0
- package/dist/src/phone/qr-picture.js +24 -0
- package/dist/src/private-config.schema.json +263 -0
- package/dist/src/project-config.schema.json +65 -0
- package/dist/src/project-settings.d.ts +19 -0
- package/dist/src/project-settings.js +131 -0
- package/dist/src/relay/answer-check.d.ts +24 -0
- package/dist/src/relay/answer-check.js +145 -0
- package/dist/src/relay/answer-inbox.d.ts +15 -0
- package/dist/src/relay/answer-inbox.js +173 -0
- package/dist/src/relay/daemon-keys.d.ts +16 -0
- package/dist/src/relay/daemon-keys.js +75 -0
- package/dist/src/relay/decision-check.d.ts +3 -0
- package/dist/src/relay/decision-check.js +77 -0
- package/dist/src/relay/decision-close.d.ts +6 -0
- package/dist/src/relay/decision-close.js +72 -0
- package/dist/src/relay/decision-envelopes.d.ts +29 -0
- package/dist/src/relay/decision-envelopes.js +93 -0
- package/dist/src/relay/decision-watch.d.ts +14 -0
- package/dist/src/relay/decision-watch.js +68 -0
- package/dist/src/relay/envelope-post.d.ts +8 -0
- package/dist/src/relay/envelope-post.js +71 -0
- package/dist/src/relay/envelope.d.ts +28 -0
- package/dist/src/relay/envelope.js +53 -0
- package/dist/src/relay/hpke.d.ts +25 -0
- package/dist/src/relay/hpke.js +108 -0
- package/dist/src/relay/pairing-checks.d.ts +42 -0
- package/dist/src/relay/pairing-checks.js +172 -0
- package/dist/src/relay/pairing-follow.d.ts +8 -0
- package/dist/src/relay/pairing-follow.js +95 -0
- package/dist/src/relay/pairing-session.d.ts +29 -0
- package/dist/src/relay/pairing-session.js +87 -0
- package/dist/src/relay/phone-delivery.d.ts +51 -0
- package/dist/src/relay/phone-delivery.js +139 -0
- package/dist/src/relay/phone-devices.d.ts +36 -0
- package/dist/src/relay/phone-devices.js +46 -0
- package/dist/src/relay/phone-inbox.d.ts +18 -0
- package/dist/src/relay/phone-inbox.js +17 -0
- package/dist/src/relay/phone-operations.d.ts +18 -0
- package/dist/src/relay/phone-operations.js +121 -0
- package/dist/src/relay/phone-outbox.d.ts +21 -0
- package/dist/src/relay/phone-outbox.js +25 -0
- package/dist/src/relay/phone-socket.d.ts +6 -0
- package/dist/src/relay/phone-socket.js +63 -0
- package/dist/src/relay/relay-control.d.ts +7 -0
- package/dist/src/relay/relay-control.js +24 -0
- package/dist/src/relay/relay-files.d.ts +2 -0
- package/dist/src/relay/relay-files.js +23 -0
- package/dist/src/relay/relay-heartbeat.d.ts +17 -0
- package/dist/src/relay/relay-heartbeat.js +81 -0
- package/dist/src/relay/relay-http.d.ts +14 -0
- package/dist/src/relay/relay-http.js +63 -0
- package/dist/src/relay/relay-registration.d.ts +15 -0
- package/dist/src/relay/relay-registration.js +63 -0
- package/dist/src/relay/relay-service.d.ts +25 -0
- package/dist/src/relay/relay-service.js +216 -0
- package/dist/src/relay/relay-store.d.ts +48 -0
- package/dist/src/relay/relay-store.js +131 -0
- package/dist/src/relay/sealed-post.d.ts +25 -0
- package/dist/src/relay/sealed-post.js +52 -0
- package/dist/src/relay.d.ts +1 -0
- package/dist/src/relay.js +8 -0
- package/dist/src/report-statistics.d.ts +50 -0
- package/dist/src/report-statistics.js +70 -0
- package/dist/src/report.d.ts +171 -0
- package/dist/src/report.js +119 -0
- package/dist/src/repository-identity.d.ts +4 -0
- package/dist/src/repository-identity.js +4 -0
- package/dist/src/review-subject.d.ts +41 -0
- package/dist/src/review-subject.js +91 -0
- package/dist/src/run-wait.d.ts +23 -0
- package/dist/src/run-wait.js +20 -0
- package/dist/src/service/active-tickets.d.ts +4 -0
- package/dist/src/service/active-tickets.js +49 -0
- package/dist/src/service/command-effects.d.ts +4 -0
- package/dist/src/service/command-effects.js +16 -0
- package/dist/src/service/decision-handler.d.ts +4 -0
- package/dist/src/service/decision-handler.js +39 -4
- package/dist/src/service/gateway.js +18 -3
- package/dist/src/service/registry.d.ts +16 -3
- package/dist/src/service/registry.js +34 -3
- package/dist/src/service/replies.d.ts +9 -1
- package/dist/src/service/replies.js +12 -0
- package/dist/src/service/run-handlers.js +14 -55
- package/dist/src/service/run-metadata.d.ts +8 -0
- package/dist/src/service/run-metadata.js +53 -0
- package/dist/src/service/run-waits.d.ts +3 -0
- package/dist/src/service/run-waits.js +41 -0
- package/dist/src/service/service.d.ts +18 -2
- package/dist/src/service/service.js +23 -7
- package/dist/src/service/submission-handler.d.ts +0 -1
- package/dist/src/service/submission-handler.js +6 -34
- package/dist/src/service/worker-port.d.ts +1 -0
- package/dist/src/service/worker-totals.js +8 -23
- package/dist/src/step-bridge.d.ts +23 -0
- package/dist/src/steps/decision-gate.d.ts +2 -1
- package/dist/src/steps/decision-gate.js +7 -0
- package/dist/src/steps/merge-by-person.d.ts +2 -0
- package/dist/src/steps/merge-by-person.js +18 -0
- package/dist/src/steps/merge-when-green.d.ts +2 -0
- package/dist/src/steps/merge-when-green.js +62 -0
- package/dist/src/steps/review-by-agent.d.ts +5 -0
- package/dist/src/steps/review-by-agent.js +124 -0
- package/dist/src/steps/review-by-person.d.ts +4 -0
- package/dist/src/steps/review-by-person.js +16 -0
- package/dist/src/steps/review-subject.d.ts +2 -0
- package/dist/src/steps/review-subject.js +11 -0
- package/dist/src/steps/run-command.d.ts +2 -0
- package/dist/src/steps/run-command.js +15 -0
- package/dist/src/steps/sleep.d.ts +3 -0
- package/dist/src/steps/sleep.js +19 -0
- package/dist/src/steps/wait-for-decision.d.ts +2 -0
- package/dist/src/steps/wait-for-decision.js +71 -0
- package/dist/src/steps/wait-for-run.d.ts +5 -0
- package/dist/src/steps/wait-for-run.js +9 -0
- package/dist/src/storage/decision-snapshots.d.ts +1 -0
- package/dist/src/storage/decision-snapshots.js +7 -1
- package/dist/src/submission.d.ts +4 -0
- package/dist/src/submission.js +6 -2
- package/dist/src/testing.d.ts +29 -0
- package/dist/src/testing.js +264 -0
- package/dist/src/ticket-status-stream.d.ts +9 -0
- package/dist/src/ticket-status-stream.js +94 -0
- package/dist/src/token-prices.d.ts +12 -4
- package/dist/src/token-prices.js +15 -2
- package/dist/src/turn-profile.d.ts +1 -0
- package/dist/src/turn-profile.js +8 -1
- package/dist/src/worker-usage.d.ts +16 -0
- package/dist/src/worker-usage.js +57 -0
- package/dist/src/workflow-admission.d.ts +5 -0
- package/dist/src/workflow-candidate.d.ts +7 -2
- package/dist/src/workflow-candidate.js +47 -5
- package/dist/src/workflow-declaration.d.ts +21 -3
- package/dist/src/workflow-declaration.js +77 -9
- package/dist/src/workflow-discovery.d.ts +2 -1
- package/dist/src/workflow-discovery.js +4 -1
- package/dist/src/workflow-refusals.js +3 -0
- package/dist/src/workflow-schedule.d.ts +22 -0
- package/dist/src/workflow-schedule.js +53 -0
- package/dist/src/workflows.d.ts +11 -1
- package/dist/src/workflows.js +8 -0
- package/dist/tools/admit-workflow.d.ts +1 -0
- package/dist/tools/admit-workflow.js +18 -5
- package/dist/tools/doctor-adoption.d.ts +1 -0
- package/dist/tools/doctor-adoption.js +11 -0
- package/dist/tools/project-configuration.d.ts +4 -0
- package/dist/tools/project-configuration.js +47 -0
- package/dist/tools/self-host-processes.d.ts +1 -0
- package/dist/tools/self-host-processes.js +33 -22
- package/dist/tools/self-host-project.d.ts +4 -0
- package/dist/tools/self-host-project.js +61 -14
- package/dist/tools/self-host-runtime.d.ts +16 -0
- package/dist/tools/self-host-runtime.js +60 -0
- package/dist/tools/self-host-workflows.d.ts +1 -0
- package/dist/tools/self-host-workflows.js +36 -1
- package/dist/tools/self-host.js +86 -61
- package/dist/tools/workflow-candidate-counts.d.ts +6 -2
- package/dist/tools/workflow-candidate-counts.js +31 -16
- package/dist/tools/workflow-candidate-fields.d.ts +5 -0
- package/dist/tools/workflow-candidate-fields.js +24 -0
- package/dist/tools/workflow-candidate-helper-counts.d.ts +10 -0
- package/dist/tools/workflow-candidate-helper-counts.js +129 -0
- package/dist/tools/workflow-candidate-program.d.ts +2 -0
- package/dist/tools/workflow-candidate-program.js +36 -8
- package/dist/tools/workflow-candidate-source.js +77 -15
- package/dist/tools/workflow-candidate-syntax.d.ts +8 -2
- package/dist/tools/workflow-candidate-syntax.js +70 -26
- package/dist/tools/workflow-command.d.ts +1 -0
- package/dist/tools/workflow-command.js +143 -0
- package/dist/tools/workflow-folder-capture.d.ts +1 -0
- package/dist/tools/workflow-folder-capture.js +47 -2
- package/dist/tools/workflow-folders.d.ts +17 -1
- package/dist/tools/workflow-folders.js +59 -6
- package/dist/tools/workflow-test-hook.d.ts +7 -0
- package/dist/tools/workflow-test-hook.js +16 -0
- package/dist/tools/workflow-test-loader.d.ts +1 -0
- package/dist/tools/workflow-test-loader.js +10 -0
- package/dist/workflows/develop-ticket.js +1 -1
- package/dist/workflows/ticket-prompts.js +5 -1
- package/package.json +46 -4
- package/templates/develop/README.md +78 -0
- package/templates/develop/develop/accept.ts +11 -0
- package/templates/develop/develop/flow-tests.ts +357 -0
- package/templates/develop/develop/local.ts +40 -0
- package/templates/develop/develop/option-tests.ts +174 -0
- package/templates/develop/develop/partial-choice.ts +29 -0
- package/templates/develop/develop/partial.ts +28 -0
- package/templates/develop/develop/pull.ts +152 -0
- package/templates/develop/develop/queue-partial.ts +39 -0
- package/templates/develop/develop/refusal-tests.ts +174 -0
- package/templates/develop/develop/report.ts +156 -0
- package/templates/develop/develop/round.ts +40 -0
- package/templates/develop/develop/scenarios.ts +87 -0
- package/templates/develop/develop/turn.ts +67 -0
- package/templates/develop/develop.declaration.json +24 -0
- package/templates/develop/develop.test.ts +287 -0
- package/templates/develop/develop.ts +30 -0
- package/templates/develop/template.json +153 -0
- package/dist/src/chats/analysis-batches.d.ts +0 -42
- package/dist/src/chats/analysis-batches.js +0 -97
- package/dist/src/chats/analysis-checkpoint.d.ts +0 -29
- package/dist/src/chats/analysis-checkpoint.js +0 -31
- package/dist/src/chats/analysis-contract.d.ts +0 -80
- package/dist/src/chats/analysis-contract.js +0 -16
- package/dist/src/chats/analysis-data.d.ts +0 -3
- package/dist/src/chats/analysis-data.js +0 -85
- package/dist/src/chats/analysis-input.d.ts +0 -18
- package/dist/src/chats/analysis-input.js +0 -97
- package/dist/src/chats/analysis-links.d.ts +0 -2
- package/dist/src/chats/analysis-links.js +0 -25
- package/dist/src/chats/analysis-merge.d.ts +0 -16
- package/dist/src/chats/analysis-merge.js +0 -134
- package/dist/src/chats/analysis-prompt.d.ts +0 -18
- package/dist/src/chats/analysis-prompt.js +0 -42
- package/dist/src/chats/analysis-proposal.d.ts +0 -4
- package/dist/src/chats/analysis-proposal.js +0 -41
- package/dist/src/chats/analysis-provenance.d.ts +0 -4
- package/dist/src/chats/analysis-provenance.js +0 -99
- package/dist/src/chats/analysis-shape.d.ts +0 -4
- package/dist/src/chats/analysis-shape.js +0 -235
- package/dist/src/chats/analysis.d.ts +0 -3
- package/dist/src/chats/analysis.js +0 -42
- package/dist/src/chats/corpus-input.d.ts +0 -5
- package/dist/src/chats/corpus-input.js +0 -288
- package/dist/src/chats/destination.d.ts +0 -4
- package/dist/src/chats/destination.js +0 -96
- package/dist/src/chats/import.d.ts +0 -15
- package/dist/src/chats/import.js +0 -243
- package/dist/src/chats/input-file.d.ts +0 -3
- package/dist/src/chats/input-file.js +0 -32
- package/dist/src/chats/normalize.d.ts +0 -29
- package/dist/src/chats/normalize.js +0 -155
- package/dist/src/chats/previous-import.d.ts +0 -5
- package/dist/src/chats/previous-import.js +0 -34
- package/dist/src/chats/reader.d.ts +0 -21
- package/dist/src/chats/reader.js +0 -118
- package/dist/src/chats/redact-import.d.ts +0 -14
- package/dist/src/chats/redact-import.js +0 -141
- package/dist/src/chats/redaction.d.ts +0 -41
- package/dist/src/chats/redaction.js +0 -161
- package/dist/src/chats/reference-mapping.d.ts +0 -16
- package/dist/src/chats/reference-mapping.js +0 -153
- package/dist/src/chats/revision-input.d.ts +0 -3
- package/dist/src/chats/revision-input.js +0 -180
- package/dist/src/chats/revision.d.ts +0 -48
- package/dist/src/chats/revision.js +0 -94
- package/dist/src/chats/selection.d.ts +0 -25
- package/dist/src/chats/selection.js +0 -109
- package/dist/src/chats/source.d.ts +0 -23
- package/dist/src/chats/source.js +0 -135
- package/dist/src/chats/structure.d.ts +0 -57
- package/dist/src/chats/structure.js +0 -216
- package/dist/src/chats/workflow-generation-projection.d.ts +0 -8
- package/dist/src/chats/workflow-generation-projection.js +0 -75
- package/dist/src/chats/workflow-generation-source.d.ts +0 -25
- package/dist/src/chats/workflow-generation-source.js +0 -124
- package/dist/src/chats/workflow-generation-text.d.ts +0 -23
- package/dist/src/chats/workflow-generation-text.js +0 -74
- package/dist/src/chats/workflow-generation.d.ts +0 -69
- package/dist/src/chats/workflow-generation.js +0 -142
- package/dist/src/daemon/analysis-launch.d.ts +0 -5
- package/dist/src/daemon/analysis-launch.js +0 -81
- package/dist/src/daemon/analysis-token.d.ts +0 -6
- package/dist/src/daemon/analysis-token.js +0 -88
- package/dist/src/daemon/analysis-worker.d.ts +0 -20
- package/dist/src/daemon/analysis-worker.js +0 -271
- package/dist/src/daemon/chats-batch-binding.d.ts +0 -23
- package/dist/src/daemon/chats-batch-binding.js +0 -99
- package/dist/src/daemon/chats-capture.d.ts +0 -18
- package/dist/src/daemon/chats-capture.js +0 -194
- package/dist/src/daemon/chats-checkpoints.d.ts +0 -41
- package/dist/src/daemon/chats-checkpoints.js +0 -186
- package/dist/src/daemon/chats-files.d.ts +0 -42
- package/dist/src/daemon/chats-files.js +0 -252
- package/dist/src/daemon/chats-follow-up.d.ts +0 -38
- package/dist/src/daemon/chats-follow-up.js +0 -180
- package/dist/src/daemon/chats-question.d.ts +0 -15
- package/dist/src/daemon/chats-question.js +0 -93
- package/dist/src/daemon/chats-worker.d.ts +0 -13
- package/dist/src/daemon/chats-worker.js +0 -201
- package/dist/workflows/analyze-chats.d.ts +0 -15
- package/dist/workflows/analyze-chats.declaration.json +0 -22
- package/dist/workflows/analyze-chats.js +0 -250
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { pairingIdentity, pairingLink } from "./daemon-keys.js";
|
|
2
|
+
import { relayRequest } from "./relay-http.js";
|
|
3
|
+
export class RegistrationEnded extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super("registration-ended: cloving.ai does not know this computer.");
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function expiry(body) {
|
|
9
|
+
const value = typeof body === "object" && body !== null
|
|
10
|
+
? Reflect.get(body, "expires_at")
|
|
11
|
+
: undefined;
|
|
12
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
|
|
13
|
+
throw new Error("pairing-invalid: cloving.ai sent no expiry.");
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
async function created(registration, secret) {
|
|
17
|
+
const pairingId = pairingIdentity(secret);
|
|
18
|
+
const reply = await relayRequest(registration.link.origin, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
path: "/api/v1/relay/pairings",
|
|
21
|
+
token: registration.link.token,
|
|
22
|
+
body: { pairing_id: pairingId },
|
|
23
|
+
});
|
|
24
|
+
if (reply.status === 401)
|
|
25
|
+
throw new RegistrationEnded();
|
|
26
|
+
if (reply.status !== 201)
|
|
27
|
+
throw new Error(`pairing-failed: cloving.ai answered ${String(reply.status)}.`);
|
|
28
|
+
return {
|
|
29
|
+
pairingId,
|
|
30
|
+
secret,
|
|
31
|
+
expiresAt: expiry(reply.body),
|
|
32
|
+
registration,
|
|
33
|
+
stop: new AbortController(),
|
|
34
|
+
stage: "open",
|
|
35
|
+
request: undefined,
|
|
36
|
+
nonce: undefined,
|
|
37
|
+
link: pairingLink(registration.link.origin, {
|
|
38
|
+
secret,
|
|
39
|
+
keys: registration.keys,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function cancelled() {
|
|
44
|
+
return new Error("pairing-cancelled: The pairing was cancelled.");
|
|
45
|
+
}
|
|
46
|
+
export function pairingSessions(settings) {
|
|
47
|
+
let open;
|
|
48
|
+
let refusal = null;
|
|
49
|
+
let timer;
|
|
50
|
+
let generation = 0;
|
|
51
|
+
const end = (ended, code) => {
|
|
52
|
+
if (open !== ended)
|
|
53
|
+
return;
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
ended.stop.abort();
|
|
56
|
+
open = undefined;
|
|
57
|
+
refusal = code;
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
current: () => open,
|
|
61
|
+
refusal: () => refusal,
|
|
62
|
+
end,
|
|
63
|
+
cancel: () => {
|
|
64
|
+
generation += 1;
|
|
65
|
+
if (open !== undefined)
|
|
66
|
+
end(open, null);
|
|
67
|
+
},
|
|
68
|
+
generation: () => generation,
|
|
69
|
+
start: async (registration, startedIn) => {
|
|
70
|
+
if (startedIn !== generation)
|
|
71
|
+
throw cancelled();
|
|
72
|
+
const started = await created(registration, settings.secret());
|
|
73
|
+
if (startedIn !== generation)
|
|
74
|
+
throw cancelled();
|
|
75
|
+
open = started;
|
|
76
|
+
refusal = null;
|
|
77
|
+
timer = setTimeout(() => {
|
|
78
|
+
end(started, started.stage === "requested" ? "pairing-expired" : null);
|
|
79
|
+
}, Date.parse(started.expiresAt) - Date.now());
|
|
80
|
+
timer.unref();
|
|
81
|
+
settings.follow(started).catch(() => {
|
|
82
|
+
end(started, "pairing-failed");
|
|
83
|
+
});
|
|
84
|
+
return started;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Actor, type EffectSuccessPayload } from "solid-objects";
|
|
2
|
+
export type PostedEnvelope = {
|
|
3
|
+
readonly envelopeId: string;
|
|
4
|
+
readonly kind: string;
|
|
5
|
+
readonly ciphertext: string;
|
|
6
|
+
readonly expiresAt: string;
|
|
7
|
+
readonly push: string;
|
|
8
|
+
readonly collapseId: string | null;
|
|
9
|
+
};
|
|
10
|
+
export type DeliveryTopic = "decision" | "closed";
|
|
11
|
+
export type DeviceDelivery = {
|
|
12
|
+
readonly deviceId: string;
|
|
13
|
+
readonly topic: DeliveryTopic;
|
|
14
|
+
readonly state: "pending" | "sent" | "failed" | "cancelled";
|
|
15
|
+
readonly attempts: number;
|
|
16
|
+
readonly lastAttemptAt: number | null;
|
|
17
|
+
readonly lastFailure: string | null;
|
|
18
|
+
readonly envelopes: readonly PostedEnvelope[];
|
|
19
|
+
};
|
|
20
|
+
export type DeviceEnvelopes = {
|
|
21
|
+
readonly deviceId: string;
|
|
22
|
+
readonly envelopes: readonly PostedEnvelope[];
|
|
23
|
+
};
|
|
24
|
+
export type PostOutcome = {
|
|
25
|
+
readonly at: number;
|
|
26
|
+
readonly failure: string | null;
|
|
27
|
+
};
|
|
28
|
+
export declare const postEnvelopesEffect = "phone_post_envelopes";
|
|
29
|
+
export declare const deviceRemoved = "device-removed";
|
|
30
|
+
export declare class PhoneDelivery extends Actor {
|
|
31
|
+
static readonly actorType = "PhoneDelivery";
|
|
32
|
+
deliveries: DeviceDelivery[];
|
|
33
|
+
closedAt: number | null;
|
|
34
|
+
promptDigest: string | null;
|
|
35
|
+
offer(device: DeviceEnvelopes & {
|
|
36
|
+
readonly promptDigest: string;
|
|
37
|
+
}): boolean;
|
|
38
|
+
close({ devices, at, }: {
|
|
39
|
+
readonly devices: readonly DeviceEnvelopes[];
|
|
40
|
+
readonly at: number;
|
|
41
|
+
}): boolean;
|
|
42
|
+
posted({ arguments: request, result, }: EffectSuccessPayload<{
|
|
43
|
+
deviceId: string;
|
|
44
|
+
topic: DeliveryTopic;
|
|
45
|
+
}, PostOutcome>): void;
|
|
46
|
+
retry(key: {
|
|
47
|
+
readonly deviceId: string;
|
|
48
|
+
readonly topic: DeliveryTopic;
|
|
49
|
+
}): void;
|
|
50
|
+
retire(): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Actor } from "solid-objects";
|
|
2
|
+
import { deliveryBackoffMilliseconds } from "../delivery-backoff.js";
|
|
3
|
+
import { outboxIdentity, PhoneOutbox } from "./phone-outbox.js";
|
|
4
|
+
export const postEnvelopesEffect = "phone_post_envelopes";
|
|
5
|
+
export const deviceRemoved = "device-removed";
|
|
6
|
+
const attemptLimit = deliveryBackoffMilliseconds.length;
|
|
7
|
+
const retentionMilliseconds = 7 * 86_400_000;
|
|
8
|
+
function same(item, key) {
|
|
9
|
+
return item.deviceId === key.deviceId && item.topic === key.topic;
|
|
10
|
+
}
|
|
11
|
+
function replaced(deliveries, delivery) {
|
|
12
|
+
return deliveries.map((item) => (same(item, delivery) ? delivery : item));
|
|
13
|
+
}
|
|
14
|
+
function ended(delivery) {
|
|
15
|
+
if (delivery.state === "sent" || delivery.state === "cancelled")
|
|
16
|
+
return true;
|
|
17
|
+
return (delivery.state === "failed" &&
|
|
18
|
+
(delivery.attempts >= attemptLimit ||
|
|
19
|
+
delivery.lastFailure === deviceRemoved));
|
|
20
|
+
}
|
|
21
|
+
function started(topic, device) {
|
|
22
|
+
return {
|
|
23
|
+
...device,
|
|
24
|
+
topic,
|
|
25
|
+
state: "pending",
|
|
26
|
+
attempts: 1,
|
|
27
|
+
lastAttemptAt: null,
|
|
28
|
+
lastFailure: null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function settled(delivery, result) {
|
|
32
|
+
if (result.failure === null)
|
|
33
|
+
return {
|
|
34
|
+
...delivery,
|
|
35
|
+
state: "sent",
|
|
36
|
+
lastAttemptAt: result.at,
|
|
37
|
+
lastFailure: null,
|
|
38
|
+
envelopes: [],
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
...delivery,
|
|
42
|
+
state: "failed",
|
|
43
|
+
lastAttemptAt: result.at,
|
|
44
|
+
lastFailure: result.failure,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function afterClose(delivery, closedAt) {
|
|
48
|
+
const stopped = delivery.topic === "decision" &&
|
|
49
|
+
delivery.state === "failed" &&
|
|
50
|
+
closedAt !== null;
|
|
51
|
+
return stopped ? { ...delivery, state: "cancelled" } : delivery;
|
|
52
|
+
}
|
|
53
|
+
export class PhoneDelivery extends Actor {
|
|
54
|
+
static actorType = "PhoneDelivery";
|
|
55
|
+
deliveries = [];
|
|
56
|
+
closedAt = null;
|
|
57
|
+
promptDigest = null;
|
|
58
|
+
offer(device) {
|
|
59
|
+
const known = this.deliveries.some((item) => same(item, { deviceId: device.deviceId, topic: "decision" }));
|
|
60
|
+
if (known || this.closedAt !== null)
|
|
61
|
+
return false;
|
|
62
|
+
const delivery = started("decision", {
|
|
63
|
+
deviceId: device.deviceId,
|
|
64
|
+
envelopes: device.envelopes,
|
|
65
|
+
});
|
|
66
|
+
this.promptDigest ??= device.promptDigest;
|
|
67
|
+
this.deliveries = [...this.deliveries, delivery];
|
|
68
|
+
this.emit(postEnvelopesEffect, {
|
|
69
|
+
arguments: {
|
|
70
|
+
deviceId: delivery.deviceId,
|
|
71
|
+
envelopes: delivery.envelopes,
|
|
72
|
+
topic: delivery.topic,
|
|
73
|
+
},
|
|
74
|
+
onSuccess: "posted",
|
|
75
|
+
});
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
close({ devices, at, }) {
|
|
79
|
+
if (this.closedAt !== null)
|
|
80
|
+
return false;
|
|
81
|
+
this.closedAt = at;
|
|
82
|
+
const closing = devices.map((device) => started("closed", device));
|
|
83
|
+
this.deliveries = [
|
|
84
|
+
...this.deliveries.map((item) => item.state === "failed" ? { ...item, state: "cancelled" } : item),
|
|
85
|
+
...closing,
|
|
86
|
+
];
|
|
87
|
+
for (const device of devices)
|
|
88
|
+
this.emit(postEnvelopesEffect, {
|
|
89
|
+
arguments: { ...device, topic: "closed" },
|
|
90
|
+
onSuccess: "posted",
|
|
91
|
+
});
|
|
92
|
+
if (closing.length === 0)
|
|
93
|
+
this.schedule({ at: new Date(at + retentionMilliseconds) }).retire();
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
posted({ arguments: request, result, }) {
|
|
97
|
+
const delivery = this.deliveries.find((item) => same(item, request) && item.state === "pending");
|
|
98
|
+
if (delivery === undefined)
|
|
99
|
+
return;
|
|
100
|
+
const updated = afterClose(settled(delivery, result), this.closedAt);
|
|
101
|
+
this.deliveries = replaced(this.deliveries, updated);
|
|
102
|
+
if (!ended(updated)) {
|
|
103
|
+
const wait = deliveryBackoffMilliseconds[updated.attempts] ?? 0;
|
|
104
|
+
this.schedule({
|
|
105
|
+
at: new Date(result.at + wait),
|
|
106
|
+
key: `${updated.topic}:${updated.deviceId}`,
|
|
107
|
+
}).retry({ deviceId: updated.deviceId, topic: updated.topic });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const closing = this.deliveries.filter((item) => item.topic === "closed");
|
|
111
|
+
if (updated.topic === "closed" && closing.every(ended))
|
|
112
|
+
this.schedule({
|
|
113
|
+
at: new Date(result.at + retentionMilliseconds),
|
|
114
|
+
}).retire();
|
|
115
|
+
}
|
|
116
|
+
retry(key) {
|
|
117
|
+
const delivery = this.deliveries.find((item) => same(item, key) && item.state === "failed");
|
|
118
|
+
if (delivery === undefined || ended(delivery))
|
|
119
|
+
return;
|
|
120
|
+
this.deliveries = replaced(this.deliveries, {
|
|
121
|
+
...delivery,
|
|
122
|
+
state: "pending",
|
|
123
|
+
attempts: delivery.attempts + 1,
|
|
124
|
+
});
|
|
125
|
+
this.emit(postEnvelopesEffect, {
|
|
126
|
+
arguments: {
|
|
127
|
+
deviceId: delivery.deviceId,
|
|
128
|
+
topic: delivery.topic,
|
|
129
|
+
envelopes: delivery.envelopes,
|
|
130
|
+
},
|
|
131
|
+
onSuccess: "posted",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
retire() {
|
|
135
|
+
this.sendTo(PhoneOutbox.ref(outboxIdentity)).retire({
|
|
136
|
+
decisionId: this.actorId,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Actor } from "solid-objects";
|
|
2
|
+
export type PhoneDevice = {
|
|
3
|
+
readonly deviceId: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly platform: string;
|
|
6
|
+
readonly agreementPublicKey: string;
|
|
7
|
+
readonly answerPublicKey: string;
|
|
8
|
+
readonly approvalPublicKey: string;
|
|
9
|
+
readonly pairedAt: string;
|
|
10
|
+
readonly state: "checking" | "connected";
|
|
11
|
+
readonly removedAt: string | null;
|
|
12
|
+
};
|
|
13
|
+
export declare class PhoneDevices extends Actor {
|
|
14
|
+
static readonly actorType = "PhoneDevices";
|
|
15
|
+
devices: PhoneDevice[];
|
|
16
|
+
sequences: {
|
|
17
|
+
readonly deviceId: string;
|
|
18
|
+
readonly sequence: number;
|
|
19
|
+
}[];
|
|
20
|
+
add({ device }: {
|
|
21
|
+
readonly device: PhoneDevice;
|
|
22
|
+
}): PhoneDevice;
|
|
23
|
+
nextSequence({ deviceId }: {
|
|
24
|
+
readonly deviceId: string;
|
|
25
|
+
}): number;
|
|
26
|
+
connect({ deviceId }: {
|
|
27
|
+
readonly deviceId: string;
|
|
28
|
+
}): boolean;
|
|
29
|
+
remove({ deviceId, removedAt, }: {
|
|
30
|
+
readonly deviceId: string;
|
|
31
|
+
readonly removedAt: string;
|
|
32
|
+
}): boolean;
|
|
33
|
+
removeAll({ removedAt }: {
|
|
34
|
+
readonly removedAt: string;
|
|
35
|
+
}): number;
|
|
36
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Actor } from "solid-objects";
|
|
2
|
+
export class PhoneDevices extends Actor {
|
|
3
|
+
static actorType = "PhoneDevices";
|
|
4
|
+
devices = [];
|
|
5
|
+
sequences = [];
|
|
6
|
+
add({ device }) {
|
|
7
|
+
const removed = this.devices.some((item) => item.deviceId === device.deviceId && item.removedAt !== null);
|
|
8
|
+
if (removed)
|
|
9
|
+
this.reject("pairing_device_revoked", {
|
|
10
|
+
message: "This phone was removed. Connect it again as a new phone.",
|
|
11
|
+
});
|
|
12
|
+
this.devices = [
|
|
13
|
+
...this.devices.filter((item) => item.deviceId !== device.deviceId),
|
|
14
|
+
device,
|
|
15
|
+
];
|
|
16
|
+
return device;
|
|
17
|
+
}
|
|
18
|
+
nextSequence({ deviceId }) {
|
|
19
|
+
const sequence = (this.sequences.find((item) => item.deviceId === deviceId)?.sequence ??
|
|
20
|
+
0) + 1;
|
|
21
|
+
this.sequences = [
|
|
22
|
+
...this.sequences.filter((item) => item.deviceId !== deviceId),
|
|
23
|
+
{ deviceId, sequence },
|
|
24
|
+
];
|
|
25
|
+
return sequence;
|
|
26
|
+
}
|
|
27
|
+
connect({ deviceId }) {
|
|
28
|
+
const found = this.devices.some((item) => item.deviceId === deviceId && item.removedAt === null);
|
|
29
|
+
this.devices = this.devices.map((item) => item.deviceId === deviceId && item.removedAt === null
|
|
30
|
+
? { ...item, state: "connected" }
|
|
31
|
+
: item);
|
|
32
|
+
return found;
|
|
33
|
+
}
|
|
34
|
+
remove({ deviceId, removedAt, }) {
|
|
35
|
+
const found = this.devices.some((item) => item.deviceId === deviceId && item.removedAt === null);
|
|
36
|
+
this.devices = this.devices.map((item) => item.deviceId === deviceId && item.removedAt === null
|
|
37
|
+
? { ...item, removedAt }
|
|
38
|
+
: item);
|
|
39
|
+
return found;
|
|
40
|
+
}
|
|
41
|
+
removeAll({ removedAt }) {
|
|
42
|
+
const count = this.devices.filter((item) => item.removedAt === null).length;
|
|
43
|
+
this.devices = this.devices.map((item) => item.removedAt === null ? { ...item, removedAt } : item);
|
|
44
|
+
return count;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Actor } from "solid-objects";
|
|
2
|
+
import type { PostedEnvelope } from "./phone-delivery.ts";
|
|
3
|
+
export type InboxWork = {
|
|
4
|
+
readonly answerId: string;
|
|
5
|
+
readonly sequence: number;
|
|
6
|
+
readonly deviceId: string;
|
|
7
|
+
readonly envelope: PostedEnvelope | null;
|
|
8
|
+
};
|
|
9
|
+
export declare class PhoneInbox extends Actor {
|
|
10
|
+
static readonly actorType = "PhoneInbox";
|
|
11
|
+
cursor: number;
|
|
12
|
+
work: InboxWork[];
|
|
13
|
+
record(work: InboxWork): InboxWork;
|
|
14
|
+
complete({ answerId, sequence, }: {
|
|
15
|
+
readonly answerId: string;
|
|
16
|
+
readonly sequence: number;
|
|
17
|
+
}): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Actor } from "solid-objects";
|
|
2
|
+
export class PhoneInbox extends Actor {
|
|
3
|
+
static actorType = "PhoneInbox";
|
|
4
|
+
cursor = 0;
|
|
5
|
+
work = [];
|
|
6
|
+
record(work) {
|
|
7
|
+
const known = this.work.find((item) => item.answerId === work.answerId);
|
|
8
|
+
if (known !== undefined)
|
|
9
|
+
return known;
|
|
10
|
+
this.work = [...this.work, work];
|
|
11
|
+
return work;
|
|
12
|
+
}
|
|
13
|
+
complete({ answerId, sequence, }) {
|
|
14
|
+
this.work = this.work.filter((item) => item.answerId !== answerId);
|
|
15
|
+
this.cursor = Math.max(this.cursor, sequence);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Fields, Reply } from "../protocol.ts";
|
|
2
|
+
import type { PairingSessions } from "./pairing-session.ts";
|
|
3
|
+
import { type RegistrationContext } from "./relay-registration.ts";
|
|
4
|
+
import type { RelayStore } from "./relay-store.ts";
|
|
5
|
+
export interface OperationState {
|
|
6
|
+
readonly context: RegistrationContext;
|
|
7
|
+
readonly store: RelayStore;
|
|
8
|
+
readonly sessions: PairingSessions;
|
|
9
|
+
readonly registration: {
|
|
10
|
+
lost: boolean;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare function result(fields: Fields): Reply;
|
|
14
|
+
export declare function refused(code: string, detail: string): Reply;
|
|
15
|
+
export declare function pairAccept(relay: OperationState, parameters: Fields): Promise<Reply>;
|
|
16
|
+
export declare function pairDeny(relay: OperationState, parameters: Fields): Promise<Reply>;
|
|
17
|
+
export declare function phoneRemove(relay: OperationState, parameters: Fields): Promise<Reply>;
|
|
18
|
+
export declare function phoneReset(relay: OperationState): Promise<Reply>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { signedReceipt } from "./pairing-checks.js";
|
|
4
|
+
import { denied } from "./pairing-follow.js";
|
|
5
|
+
import { relayRequest } from "./relay-http.js";
|
|
6
|
+
import { readLink } from "./relay-registration.js";
|
|
7
|
+
export function result(fields) {
|
|
8
|
+
return { kind: "result", result: fields };
|
|
9
|
+
}
|
|
10
|
+
export function refused(code, detail) {
|
|
11
|
+
return { kind: "error", error: { code, detail } };
|
|
12
|
+
}
|
|
13
|
+
function requestedPairing(relay) {
|
|
14
|
+
const open = relay.sessions.current();
|
|
15
|
+
return open?.stage === "requested" && open.request !== undefined
|
|
16
|
+
? open
|
|
17
|
+
: undefined;
|
|
18
|
+
}
|
|
19
|
+
const noRequest = refused("pairing-not-requested", "No phone waits for an answer. Click Connect a phone and scan the code again.");
|
|
20
|
+
const changed = refused("pairing-changed", "The pairing changed. Check the name and the code on the watch page again.");
|
|
21
|
+
const acceptances = new WeakMap();
|
|
22
|
+
export function pairAccept(relay, parameters) {
|
|
23
|
+
const open = requestedPairing(relay);
|
|
24
|
+
if (open === undefined)
|
|
25
|
+
return Promise.resolve(noRequest);
|
|
26
|
+
if (parameters["pairingId"] !== open.pairingId)
|
|
27
|
+
return Promise.resolve(changed);
|
|
28
|
+
const running = acceptances.get(open);
|
|
29
|
+
if (running !== undefined)
|
|
30
|
+
return running;
|
|
31
|
+
const accepting = accepted(relay, open).finally(() => acceptances.delete(open));
|
|
32
|
+
acceptances.set(open, accepting);
|
|
33
|
+
return accepting;
|
|
34
|
+
}
|
|
35
|
+
async function accepted(relay, open) {
|
|
36
|
+
const { request } = open;
|
|
37
|
+
if (request === undefined)
|
|
38
|
+
return noRequest;
|
|
39
|
+
const pairedAt = new Date().toISOString();
|
|
40
|
+
const rejection = await relay.store.add({
|
|
41
|
+
deviceId: request.deviceId,
|
|
42
|
+
name: request.name,
|
|
43
|
+
platform: request.platform,
|
|
44
|
+
agreementPublicKey: request.agreementPublicKey,
|
|
45
|
+
answerPublicKey: request.answerPublicKey,
|
|
46
|
+
approvalPublicKey: request.approvalPublicKey,
|
|
47
|
+
pairedAt,
|
|
48
|
+
state: "checking",
|
|
49
|
+
removedAt: null,
|
|
50
|
+
});
|
|
51
|
+
if (rejection !== undefined) {
|
|
52
|
+
await denied(open);
|
|
53
|
+
relay.sessions.end(open, rejection);
|
|
54
|
+
return refused(rejection, "This phone was removed. Connect it as a new phone.");
|
|
55
|
+
}
|
|
56
|
+
const { receipt, nonce } = signedReceipt(open.registration.keys, {
|
|
57
|
+
daemonId: open.registration.link.daemonId,
|
|
58
|
+
deviceId: request.deviceId,
|
|
59
|
+
pairedAt,
|
|
60
|
+
});
|
|
61
|
+
const reply = await relayRequest(open.registration.link.origin, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
path: `/api/v1/relay/pairings/${open.pairingId}/receipt`,
|
|
64
|
+
token: open.registration.link.token,
|
|
65
|
+
body: { receipt },
|
|
66
|
+
});
|
|
67
|
+
if (reply.status !== 200)
|
|
68
|
+
return refused("receipt-failed", `cloving.ai answered ${String(reply.status)} to the receipt.`);
|
|
69
|
+
open.nonce = nonce;
|
|
70
|
+
open.stage = "checking";
|
|
71
|
+
return result({ deviceId: request.deviceId, state: "checking" });
|
|
72
|
+
}
|
|
73
|
+
export async function pairDeny(relay, parameters) {
|
|
74
|
+
const open = requestedPairing(relay);
|
|
75
|
+
if (open === undefined)
|
|
76
|
+
return noRequest;
|
|
77
|
+
if (parameters["pairingId"] !== open.pairingId)
|
|
78
|
+
return changed;
|
|
79
|
+
await denied(open);
|
|
80
|
+
relay.sessions.end(open, null);
|
|
81
|
+
return result({ pairing: null });
|
|
82
|
+
}
|
|
83
|
+
function warning(status, subject) {
|
|
84
|
+
if (status >= 200 && status < 300)
|
|
85
|
+
return null;
|
|
86
|
+
return `cloving.ai did not remove ${subject} (answer ${String(status)}). This computer keeps it removed.`;
|
|
87
|
+
}
|
|
88
|
+
async function serverCall(relay, call) {
|
|
89
|
+
const link = await readLink(relay.context.phoneDirectory);
|
|
90
|
+
if (link === undefined)
|
|
91
|
+
return null;
|
|
92
|
+
const reply = await relayRequest(link.origin, {
|
|
93
|
+
method: call.method,
|
|
94
|
+
path: call.path,
|
|
95
|
+
token: link.token,
|
|
96
|
+
}).catch(() => ({ status: 0, body: null }));
|
|
97
|
+
return warning(reply.status, call.subject);
|
|
98
|
+
}
|
|
99
|
+
export async function phoneRemove(relay, parameters) {
|
|
100
|
+
const deviceId = parameters["deviceId"];
|
|
101
|
+
if (typeof deviceId !== "string" || !(await relay.store.remove(deviceId)))
|
|
102
|
+
return refused("phone-unknown", "No connected phone has this device ID.");
|
|
103
|
+
const notice = await serverCall(relay, {
|
|
104
|
+
method: "DELETE",
|
|
105
|
+
path: `/api/v1/relay/devices/${encodeURIComponent(deviceId)}`,
|
|
106
|
+
subject: "the phone",
|
|
107
|
+
});
|
|
108
|
+
return result({ deviceId, removed: true, warning: notice });
|
|
109
|
+
}
|
|
110
|
+
export async function phoneReset(relay) {
|
|
111
|
+
relay.sessions.cancel();
|
|
112
|
+
const notice = await serverCall(relay, {
|
|
113
|
+
method: "DELETE",
|
|
114
|
+
path: "/api/v1/relay/daemon",
|
|
115
|
+
subject: "this computer",
|
|
116
|
+
});
|
|
117
|
+
const removed = await relay.store.removeAll();
|
|
118
|
+
for (const file of ["link.json", "daemon-keys.json"])
|
|
119
|
+
await rm(join(relay.context.phoneDirectory, file), { force: true });
|
|
120
|
+
return result({ removed, warning: notice });
|
|
121
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Actor } from "solid-objects";
|
|
2
|
+
export declare const outboxIdentity = "phone";
|
|
3
|
+
export type OpenDecision = {
|
|
4
|
+
readonly decisionId: string;
|
|
5
|
+
readonly runId: string;
|
|
6
|
+
};
|
|
7
|
+
export declare class PhoneOutbox extends Actor {
|
|
8
|
+
static readonly actorType = "PhoneOutbox";
|
|
9
|
+
decisions: OpenDecision[];
|
|
10
|
+
retired: string[];
|
|
11
|
+
track(decision: OpenDecision): boolean;
|
|
12
|
+
retire({ decisionId }: {
|
|
13
|
+
readonly decisionId: string;
|
|
14
|
+
}): void;
|
|
15
|
+
forget({ decisionId }: {
|
|
16
|
+
readonly decisionId: string;
|
|
17
|
+
}): void;
|
|
18
|
+
untrack({ decisionId }: {
|
|
19
|
+
readonly decisionId: string;
|
|
20
|
+
}): boolean;
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Actor } from "solid-objects";
|
|
2
|
+
export const outboxIdentity = "phone";
|
|
3
|
+
export class PhoneOutbox extends Actor {
|
|
4
|
+
static actorType = "PhoneOutbox";
|
|
5
|
+
decisions = [];
|
|
6
|
+
retired = [];
|
|
7
|
+
track(decision) {
|
|
8
|
+
if (this.decisions.some((item) => item.decisionId === decision.decisionId))
|
|
9
|
+
return false;
|
|
10
|
+
this.decisions = [...this.decisions, decision];
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
retire({ decisionId }) {
|
|
14
|
+
if (!this.retired.includes(decisionId))
|
|
15
|
+
this.retired = [...this.retired, decisionId];
|
|
16
|
+
}
|
|
17
|
+
forget({ decisionId }) {
|
|
18
|
+
this.retired = this.retired.filter((item) => item !== decisionId);
|
|
19
|
+
}
|
|
20
|
+
untrack({ decisionId }) {
|
|
21
|
+
const known = this.decisions.some((item) => item.decisionId === decisionId);
|
|
22
|
+
this.decisions = this.decisions.filter((item) => item.decisionId !== decisionId);
|
|
23
|
+
return known;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Reply, type ServiceRequest } from "../protocol.ts";
|
|
2
|
+
export type PhoneHandler = (request: ServiceRequest) => Promise<Reply>;
|
|
3
|
+
export interface PhoneSocket {
|
|
4
|
+
readonly close: () => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare function listenPhoneSocket(path: string, handle: PhoneHandler): Promise<PhoneSocket>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { chmod, rm } from "node:fs/promises";
|
|
2
|
+
import net from "node:net";
|
|
3
|
+
import { maximumFrameBytes, parseRequest, replyLine, } from "../protocol.js";
|
|
4
|
+
function failed(error) {
|
|
5
|
+
return {
|
|
6
|
+
kind: "error",
|
|
7
|
+
error: {
|
|
8
|
+
code: "phone-relay-failed",
|
|
9
|
+
detail: String(Reflect.get(Object(error), "message")),
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function served(socket, handle) {
|
|
14
|
+
let buffered = "";
|
|
15
|
+
socket.setEncoding("utf8");
|
|
16
|
+
socket.on("error", () => undefined);
|
|
17
|
+
socket.on("data", (data) => {
|
|
18
|
+
buffered += data;
|
|
19
|
+
const end = buffered.indexOf("\n");
|
|
20
|
+
if (end < 0 && Buffer.byteLength(buffered) <= maximumFrameBytes)
|
|
21
|
+
return;
|
|
22
|
+
socket.removeAllListeners("data");
|
|
23
|
+
const parsed = parseRequest(end < 0 ? "" : buffered.slice(0, end));
|
|
24
|
+
const answer = (reply) => {
|
|
25
|
+
socket.end(replyLine(reply));
|
|
26
|
+
};
|
|
27
|
+
const replied = parsed.kind === "error"
|
|
28
|
+
? Promise.resolve(parsed)
|
|
29
|
+
: handle(parsed.request);
|
|
30
|
+
replied.then(answer, (error) => {
|
|
31
|
+
answer(failed(error));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export async function listenPhoneSocket(path, handle) {
|
|
36
|
+
await rm(path, { force: true });
|
|
37
|
+
const sockets = new Set();
|
|
38
|
+
const server = net.createServer((socket) => {
|
|
39
|
+
sockets.add(socket);
|
|
40
|
+
socket.once("close", () => sockets.delete(socket));
|
|
41
|
+
served(socket, handle);
|
|
42
|
+
});
|
|
43
|
+
await new Promise((ready, refuse) => {
|
|
44
|
+
server.once("error", refuse);
|
|
45
|
+
server.listen(path, () => {
|
|
46
|
+
server.off("error", refuse);
|
|
47
|
+
ready();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
await chmod(path, 0o600);
|
|
51
|
+
return {
|
|
52
|
+
close: async () => {
|
|
53
|
+
const closed = new Promise((settle) => {
|
|
54
|
+
server.close(() => {
|
|
55
|
+
settle();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
for (const socket of sockets)
|
|
59
|
+
socket.destroy();
|
|
60
|
+
await closed;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Fields, Reply, ServiceRequest } from "../protocol.ts";
|
|
2
|
+
export declare function relayControl(runtimeDirectory: string): (request: ServiceRequest) => Promise<Reply>;
|
|
3
|
+
export declare function relayEvents(runtimeDirectory: string, stream: {
|
|
4
|
+
readonly after: number;
|
|
5
|
+
readonly onEvent: (event: Fields) => void;
|
|
6
|
+
readonly stopped: Promise<void>;
|
|
7
|
+
}): Promise<Reply>;
|