@kici-dev/orchestrator 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +1 -6
  3. package/dist/__test-helpers__/mock-db.d.ts +103 -0
  4. package/dist/__test-helpers__/mock-ws.d.ts +13 -0
  5. package/dist/agent/dispatcher.d.ts +231 -0
  6. package/dist/agent/ownership-tracker.d.ts +46 -0
  7. package/dist/agent/registry.d.ts +255 -0
  8. package/dist/agent/token-store.d.ts +96 -0
  9. package/dist/app.d.ts +234 -0
  10. package/dist/audit/access-log.d.ts +92 -0
  11. package/dist/audit/sampling-rate-limiter.d.ts +41 -0
  12. package/dist/cache/build-coordinator.d.ts +42 -0
  13. package/dist/cache/dep-cache.d.ts +51 -0
  14. package/dist/cache/index.d.ts +16 -0
  15. package/dist/cache/pending-builds.d.ts +14 -0
  16. package/dist/cache/pending-dynamics.d.ts +15 -0
  17. package/dist/cache/pending-inits.d.ts +19 -0
  18. package/dist/cache/pending-tracker.d.ts +65 -0
  19. package/dist/cache/source-cache.d.ts +30 -0
  20. package/dist/cli/api-client.d.ts +411 -0
  21. package/dist/cli/commands/access-log.d.ts +30 -0
  22. package/dist/cli/commands/agent-service/index.d.ts +15 -0
  23. package/dist/cli/commands/agent-service/install.d.ts +9 -0
  24. package/dist/cli/commands/agent-service/logs.d.ts +9 -0
  25. package/dist/cli/commands/agent-service/restart.d.ts +8 -0
  26. package/dist/cli/commands/agent-service/start.d.ts +8 -0
  27. package/dist/cli/commands/agent-service/status.d.ts +10 -0
  28. package/dist/cli/commands/agent-service/stop.d.ts +8 -0
  29. package/dist/cli/commands/agent-service/uninstall.d.ts +9 -0
  30. package/dist/cli/commands/agent-service/upgrade.d.ts +11 -0
  31. package/dist/cli/commands/agent.d.ts +10 -0
  32. package/dist/cli/commands/api-key.d.ts +12 -0
  33. package/dist/cli/commands/audit.d.ts +9 -0
  34. package/dist/cli/commands/backend.d.ts +17 -0
  35. package/dist/cli/commands/cold-store-impl.d.ts +101 -0
  36. package/dist/cli/commands/cold-store.d.ts +30 -0
  37. package/dist/cli/commands/config.d.ts +16 -0
  38. package/dist/cli/commands/db.d.ts +19 -0
  39. package/dist/cli/commands/debug-bundle.d.ts +16 -0
  40. package/dist/cli/commands/diagnose.d.ts +15 -0
  41. package/dist/cli/commands/environment.d.ts +24 -0
  42. package/dist/cli/commands/event-dlq.d.ts +19 -0
  43. package/dist/cli/commands/event-log.d.ts +27 -0
  44. package/dist/cli/commands/event.d.ts +4 -0
  45. package/dist/cli/commands/execution.d.ts +17 -0
  46. package/dist/cli/commands/inspect-bundle.d.ts +16 -0
  47. package/dist/cli/commands/maintenance.d.ts +4 -0
  48. package/dist/cli/commands/orchestrator-service/index.d.ts +12 -0
  49. package/dist/cli/commands/orchestrator-service/install.d.ts +10 -0
  50. package/dist/cli/commands/orchestrator-service/logs.d.ts +10 -0
  51. package/dist/cli/commands/orchestrator-service/restart.d.ts +8 -0
  52. package/dist/cli/commands/orchestrator-service/start.d.ts +8 -0
  53. package/dist/cli/commands/orchestrator-service/status.d.ts +10 -0
  54. package/dist/cli/commands/orchestrator-service/stop.d.ts +8 -0
  55. package/dist/cli/commands/orchestrator-service/uninstall.d.ts +9 -0
  56. package/dist/cli/commands/orchestrator-service/upgrade.d.ts +12 -0
  57. package/dist/cli/commands/org-settings.d.ts +17 -0
  58. package/dist/cli/commands/peer.d.ts +13 -0
  59. package/dist/cli/commands/queue.d.ts +16 -0
  60. package/dist/cli/commands/registration.d.ts +18 -0
  61. package/dist/cli/commands/rotate.d.ts +9 -0
  62. package/dist/cli/commands/runs.d.ts +34 -0
  63. package/dist/cli/commands/secret.d.ts +12 -0
  64. package/dist/cli/commands/shared/db.d.ts +16 -0
  65. package/dist/cli/commands/shared/versioned-upgrade.d.ts +38 -0
  66. package/dist/cli/commands/source.d.ts +24 -0
  67. package/dist/cli/commands/token.d.ts +10 -0
  68. package/dist/cli/commands/workflow.d.ts +31 -0
  69. package/dist/cli/join.d.ts +14 -0
  70. package/dist/cli/kici-admin.d.ts +13 -0
  71. package/dist/cli/lazy-deps/cache.d.ts +17 -0
  72. package/dist/cli/lazy-deps/downloader.d.ts +33 -0
  73. package/dist/cli/lazy-deps/registry.d.ts +43 -0
  74. package/dist/cli/service/compose.d.ts +25 -0
  75. package/dist/cli/service/index.d.ts +24 -0
  76. package/dist/cli/service/launchd.d.ts +34 -0
  77. package/dist/cli/service/platform-detect.d.ts +59 -0
  78. package/dist/cli/service/systemd.d.ts +41 -0
  79. package/dist/cli/service/types.d.ts +91 -0
  80. package/dist/cli/service/windows.d.ts +20 -0
  81. package/dist/cli/wizard/agent-wizard.d.ts +24 -0
  82. package/dist/cli/wizard/orchestrator-wizard.d.ts +38 -0
  83. package/dist/cli/wizard/prompts.d.ts +23 -0
  84. package/dist/cli.js +11435 -0
  85. package/dist/cluster/cluster-identity.d.ts +59 -0
  86. package/dist/cluster/coordinator.d.ts +204 -0
  87. package/dist/cluster/health-api.d.ts +30 -0
  88. package/dist/cluster/index.d.ts +24 -0
  89. package/dist/cluster/join-client.d.ts +75 -0
  90. package/dist/cluster/join-handler.d.ts +58 -0
  91. package/dist/cluster/join-token.d.ts +94 -0
  92. package/dist/cluster/orphan-recovery.d.ts +67 -0
  93. package/dist/cluster/peer-client.d.ts +190 -0
  94. package/dist/cluster/peer-credentials.d.ts +108 -0
  95. package/dist/cluster/peer-crypto.d.ts +57 -0
  96. package/dist/cluster/peer-handler.d.ts +97 -0
  97. package/dist/cluster/peer-registry.d.ts +186 -0
  98. package/dist/cluster/raft-state.d.ts +36 -0
  99. package/dist/cluster/raft.d.ts +140 -0
  100. package/dist/cold-store/load-access-log-range.d.ts +73 -0
  101. package/dist/cold-store/load-event-log-range.d.ts +62 -0
  102. package/dist/cold-store/load-secret-audit-log-range.d.ts +38 -0
  103. package/dist/cold-store/orchestrator-cold-store.d.ts +58 -0
  104. package/dist/cold-store/tables/access-log.d.ts +80 -0
  105. package/dist/cold-store/tables/event-log.d.ts +70 -0
  106. package/dist/cold-store/tables/execution-jobs.d.ts +63 -0
  107. package/dist/cold-store/tables/execution-runs.d.ts +81 -0
  108. package/dist/cold-store/tables/execution-steps.d.ts +65 -0
  109. package/dist/cold-store/tables/secret-audit-log.d.ts +77 -0
  110. package/dist/concurrency/dispatch-next-queued.d.ts +63 -0
  111. package/dist/concurrency/group-tracker.d.ts +51 -0
  112. package/dist/concurrency/index.d.ts +3 -0
  113. package/dist/concurrency/queue-manager.d.ts +68 -0
  114. package/dist/concurrency/waiters.d.ts +58 -0
  115. package/dist/config/encryption.d.ts +82 -0
  116. package/dist/config/env-overlay.d.ts +45 -0
  117. package/dist/config/index.d.ts +16 -0
  118. package/dist/config/loader.d.ts +26 -0
  119. package/dist/config/reload.d.ts +113 -0
  120. package/dist/config/resolver.d.ts +47 -0
  121. package/dist/config/schema.d.ts +210 -0
  122. package/dist/config/shared-store.d.ts +142 -0
  123. package/dist/config/types.d.ts +218 -0
  124. package/dist/config.d.ts +249 -0
  125. package/dist/cron/cron-scheduler.d.ts +78 -0
  126. package/dist/cron/cron-store.d.ts +38 -0
  127. package/dist/dashboard/handler.d.ts +243 -0
  128. package/dist/db/client.d.ts +13 -0
  129. package/dist/db/migration-provider.d.ts +9 -0
  130. package/dist/db/migrations/001_initial.d.ts +8 -0
  131. package/dist/db/migrations/002_config_versions_key_version.d.ts +16 -0
  132. package/dist/db/migrations/003_access_log.d.ts +26 -0
  133. package/dist/db/migrations/004_rename_bundle_to_source.d.ts +19 -0
  134. package/dist/db/migrations/005_cold_store_chunk_counter.d.ts +19 -0
  135. package/dist/db/migrations/006_runs_jobs_steps_archived_at.d.ts +38 -0
  136. package/dist/db/migrations/007_audit_logs_archived_at.d.ts +42 -0
  137. package/dist/db/migrations/008_event_log_archived_at.d.ts +40 -0
  138. package/dist/db/migrations/009_access_log_trigram.d.ts +15 -0
  139. package/dist/db/migrations/010_cold_store_chunks.d.ts +34 -0
  140. package/dist/db/migrations/011_drop_source_secrets_notify.d.ts +21 -0
  141. package/dist/db/migrations/012_peer_credentials_active_uniq.d.ts +28 -0
  142. package/dist/db/migrations/013_execution_log_bytes.d.ts +21 -0
  143. package/dist/db/migrations/014_kici_events_lease_retry.d.ts +36 -0
  144. package/dist/db/migrations/015_org_settings_customer_scoped.d.ts +30 -0
  145. package/dist/db/migrations/016_org_settings_allow_http_npm.d.ts +15 -0
  146. package/dist/db/migrations/017_org_id_widen.d.ts +4 -0
  147. package/dist/db/migrations/018_org_id_prefix_backfill.d.ts +4 -0
  148. package/dist/db/migrator.d.ts +32 -0
  149. package/dist/db/types.d.ts +1266 -0
  150. package/dist/diagnostics/bundle-reader.d.ts +48 -0
  151. package/dist/diagnostics/bundle-writer.d.ts +57 -0
  152. package/dist/diagnostics/checks/agents.d.ts +10 -0
  153. package/dist/diagnostics/checks/certs.d.ts +9 -0
  154. package/dist/diagnostics/checks/config.d.ts +10 -0
  155. package/dist/diagnostics/checks/db.d.ts +9 -0
  156. package/dist/diagnostics/checks/disk.d.ts +9 -0
  157. package/dist/diagnostics/checks/index.d.ts +17 -0
  158. package/dist/diagnostics/checks/ws.d.ts +9 -0
  159. package/dist/diagnostics/index.d.ts +13 -0
  160. package/dist/diagnostics/runner.d.ts +23 -0
  161. package/dist/diagnostics/types.d.ts +38 -0
  162. package/dist/entry-helpers.d.ts +93 -0
  163. package/dist/environments/binding-store.d.ts +27 -0
  164. package/dist/environments/environment-store.d.ts +74 -0
  165. package/dist/environments/held-runs.d.ts +64 -0
  166. package/dist/environments/index.d.ts +10 -0
  167. package/dist/environments/protection/branch-gate.d.ts +5 -0
  168. package/dist/environments/protection/concurrency-gate.d.ts +7 -0
  169. package/dist/environments/protection/pipeline.d.ts +18 -0
  170. package/dist/environments/protection/reviewer-gate.d.ts +7 -0
  171. package/dist/environments/protection/trust-gate.d.ts +7 -0
  172. package/dist/environments/protection/wait-timer-gate.d.ts +7 -0
  173. package/dist/environments/variable-store.d.ts +39 -0
  174. package/dist/events/circuit-breaker.d.ts +33 -0
  175. package/dist/events/event-emitter.d.ts +55 -0
  176. package/dist/events/event-retry-scanner.d.ts +52 -0
  177. package/dist/events/event-router.d.ts +183 -0
  178. package/dist/events/event-store.d.ts +157 -0
  179. package/dist/events/trust-store.d.ts +54 -0
  180. package/dist/events/types.d.ts +61 -0
  181. package/dist/helpers/ip-extraction.d.ts +29 -0
  182. package/dist/helpers/rate-limiter.d.ts +23 -0
  183. package/dist/index.d.ts +7 -0
  184. package/dist/index.js +772 -0
  185. package/dist/lockfile-cache.d.ts +39 -0
  186. package/dist/metrics/agent-metrics-aggregator.d.ts +116 -0
  187. package/dist/metrics/metrics-reporter.d.ts +40 -0
  188. package/dist/metrics/prometheus.d.ts +261 -0
  189. package/dist/metrics/scheduled-jobs.d.ts +20 -0
  190. package/dist/orchestrator-core.d.ts +193 -0
  191. package/dist/pipeline/dispatch-matched-workflow.d.ts +102 -0
  192. package/dist/pipeline/inline-eval.d.ts +28 -0
  193. package/dist/pipeline/install-secrets-resolver.d.ts +73 -0
  194. package/dist/pipeline/manual-schedule.d.ts +21 -0
  195. package/dist/pipeline/needs-scheduler.d.ts +85 -0
  196. package/dist/pipeline/process-webhook.d.ts +38 -0
  197. package/dist/pipeline/processor.d.ts +326 -0
  198. package/dist/pipeline/rerun.d.ts +79 -0
  199. package/dist/pipeline/test-pipeline.d.ts +110 -0
  200. package/dist/provider-registry.d.ts +122 -0
  201. package/dist/providers/generic/index.d.ts +23 -0
  202. package/dist/providers/generic/normalizer.d.ts +112 -0
  203. package/dist/providers/generic/verification.d.ts +47 -0
  204. package/dist/providers/github/auth.d.ts +60 -0
  205. package/dist/providers/github/changed-files.d.ts +51 -0
  206. package/dist/providers/github/check-status-poster.d.ts +35 -0
  207. package/dist/providers/github/contributor-resolver.d.ts +30 -0
  208. package/dist/providers/github/index.d.ts +20 -0
  209. package/dist/providers/github/lock-file.d.ts +32 -0
  210. package/dist/providers/github/normalizer.d.ts +103 -0
  211. package/dist/providers/github/repo-url.d.ts +35 -0
  212. package/dist/providers/internal/index.d.ts +32 -0
  213. package/dist/providers/internal/lock-file-fetcher.d.ts +34 -0
  214. package/dist/providers/internal/normalizer.d.ts +89 -0
  215. package/dist/providers/internal/repo-url-builder.d.ts +42 -0
  216. package/dist/providers/universal-git/changed-files.d.ts +42 -0
  217. package/dist/providers/universal-git/clone-token.d.ts +83 -0
  218. package/dist/providers/universal-git/config.d.ts +192 -0
  219. package/dist/providers/universal-git/index.d.ts +55 -0
  220. package/dist/providers/universal-git/lock-file.d.ts +71 -0
  221. package/dist/providers/universal-git/normalizer.d.ts +90 -0
  222. package/dist/providers/universal-git/repo-url.d.ts +50 -0
  223. package/dist/providers/universal-git/ssh-auth.d.ts +94 -0
  224. package/dist/queue/bootstrap.d.ts +50 -0
  225. package/dist/queue/cleanup.d.ts +41 -0
  226. package/dist/queue/depth-refresher.d.ts +65 -0
  227. package/dist/queue/job-queue.d.ts +343 -0
  228. package/dist/queue/scheduled-job.d.ts +91 -0
  229. package/dist/registration/extractor.d.ts +44 -0
  230. package/dist/registration/registration-index.d.ts +162 -0
  231. package/dist/registration/registration-store.d.ts +111 -0
  232. package/dist/reporting/check-run-summary.d.ts +102 -0
  233. package/dist/reporting/commit-status.d.ts +382 -0
  234. package/dist/reporting/execution-tracker.d.ts +538 -0
  235. package/dist/reporting/fs-log-storage.d.ts +41 -0
  236. package/dist/reporting/log-pull-handler.d.ts +35 -0
  237. package/dist/reporting/log-storage.d.ts +70 -0
  238. package/dist/reporting/log-writer.d.ts +45 -0
  239. package/dist/reporting/s3-log-storage.d.ts +42 -0
  240. package/dist/reporting/step-log-buffer.d.ts +57 -0
  241. package/dist/routes/admin-access-log.d.ts +34 -0
  242. package/dist/routes/admin-backends.d.ts +28 -0
  243. package/dist/routes/admin-config.d.ts +57 -0
  244. package/dist/routes/admin-db.d.ts +20 -0
  245. package/dist/routes/admin-environments.d.ts +42 -0
  246. package/dist/routes/admin-errors.d.ts +4 -0
  247. package/dist/routes/admin-event-dlq.d.ts +39 -0
  248. package/dist/routes/admin-event-log.d.ts +42 -0
  249. package/dist/routes/admin-events.d.ts +46 -0
  250. package/dist/routes/admin-maintenance.d.ts +22 -0
  251. package/dist/routes/admin-org-settings.d.ts +29 -0
  252. package/dist/routes/admin-queue-execution.d.ts +27 -0
  253. package/dist/routes/admin-registrations.d.ts +37 -0
  254. package/dist/routes/admin-runs.d.ts +57 -0
  255. package/dist/routes/admin-scheduled-jobs.d.ts +36 -0
  256. package/dist/routes/admin-sources.d.ts +18 -0
  257. package/dist/routes/admin.d.ts +103 -0
  258. package/dist/routes/capabilities.d.ts +24 -0
  259. package/dist/routes/diagnostics.d.ts +16 -0
  260. package/dist/routes/health.d.ts +20 -0
  261. package/dist/routes/test-trigger.d.ts +41 -0
  262. package/dist/routes/uploads.d.ts +38 -0
  263. package/dist/routes/webhooks.d.ts +36 -0
  264. package/dist/scaler/bare-metal-backend.d.ts +107 -0
  265. package/dist/scaler/config.d.ts +303 -0
  266. package/dist/scaler/container-backend.d.ts +125 -0
  267. package/dist/scaler/file-tail.d.ts +23 -0
  268. package/dist/scaler/firecracker-api.d.ts +100 -0
  269. package/dist/scaler/firecracker-backend.d.ts +263 -0
  270. package/dist/scaler/index.d.ts +24 -0
  271. package/dist/scaler/ip-allocator.d.ts +177 -0
  272. package/dist/scaler/label-matcher.d.ts +73 -0
  273. package/dist/scaler/log-forwarder.d.ts +54 -0
  274. package/dist/scaler/machine-ledger.d.ts +160 -0
  275. package/dist/scaler/manager.d.ts +338 -0
  276. package/dist/scaler/nftables.d.ts +85 -0
  277. package/dist/scaler/types.d.ts +388 -0
  278. package/dist/scaler/warm-pool.d.ts +81 -0
  279. package/dist/secrets/audit-logger.d.ts +65 -0
  280. package/dist/secrets/backend-health.d.ts +60 -0
  281. package/dist/secrets/backend-registry.d.ts +68 -0
  282. package/dist/secrets/backend-sync.d.ts +40 -0
  283. package/dist/secrets/cleanup.d.ts +40 -0
  284. package/dist/secrets/config.d.ts +51 -0
  285. package/dist/secrets/crypto.d.ts +49 -0
  286. package/dist/secrets/ephemeral-keys.d.ts +49 -0
  287. package/dist/secrets/index.d.ts +17 -0
  288. package/dist/secrets/pg-secret-store.d.ts +105 -0
  289. package/dist/secrets/rbac.d.ts +47 -0
  290. package/dist/secrets/secret-output-store.d.ts +46 -0
  291. package/dist/secrets/secret-resolver.d.ts +137 -0
  292. package/dist/secrets/source-credentials.d.ts +61 -0
  293. package/dist/secrets/token-manager.d.ts +63 -0
  294. package/dist/secrets/vault-secret-store.d.ts +85 -0
  295. package/dist/security/comment-handler.d.ts +67 -0
  296. package/dist/security/contributor-cache.d.ts +75 -0
  297. package/dist/security/global-workflow-policy.d.ts +72 -0
  298. package/dist/security/lock-source.d.ts +34 -0
  299. package/dist/security/trust-resolver.d.ts +108 -0
  300. package/dist/security/workflow-diff.d.ts +26 -0
  301. package/dist/server.d.ts +17 -0
  302. package/dist/server.js +48055 -0
  303. package/dist/sources/index.d.ts +10 -0
  304. package/dist/sources/source-manager.d.ts +63 -0
  305. package/dist/sources/source-store.d.ts +79 -0
  306. package/dist/sources/source-validator.d.ts +16 -0
  307. package/dist/stale-detector/stale-run-detector.d.ts +109 -0
  308. package/dist/standalone.d.ts +20 -0
  309. package/dist/standalone.js +41546 -0
  310. package/dist/storage/blob-routes.d.ts +20 -0
  311. package/dist/storage/filesystem.d.ts +60 -0
  312. package/dist/storage/index.d.ts +20 -0
  313. package/dist/storage/s3.d.ts +68 -0
  314. package/dist/storage/sign-url.d.ts +45 -0
  315. package/dist/storage/types.d.ts +98 -0
  316. package/dist/webhook/dedup.d.ts +44 -0
  317. package/dist/webhook/event-log.d.ts +83 -0
  318. package/dist/webhook/generic-sources.d.ts +169 -0
  319. package/dist/webhook/handler.d.ts +23 -0
  320. package/dist/webhook/relay-buffer.d.ts +96 -0
  321. package/dist/webhook/verify-inbound.d.ts +78 -0
  322. package/dist/worker/in-memory-execution-tracker.d.ts +94 -0
  323. package/dist/worker/in-memory-job-queue.d.ts +74 -0
  324. package/dist/worker/static-agent-token-store.d.ts +58 -0
  325. package/dist/worker/worker-status.d.ts +36 -0
  326. package/dist/worker-core.d.ts +74 -0
  327. package/dist/ws/agent-api-registry.d.ts +49 -0
  328. package/dist/ws/agent-handler.d.ts +176 -0
  329. package/dist/ws/agent-heartbeat.d.ts +45 -0
  330. package/dist/ws/dashboard-backends-handler.d.ts +51 -0
  331. package/dist/ws/dashboard-diagnostics-handler.d.ts +56 -0
  332. package/dist/ws/dashboard-env-handler.d.ts +107 -0
  333. package/dist/ws/dashboard-global-workflows-handler.d.ts +72 -0
  334. package/dist/ws/dashboard-registrations-handler.d.ts +84 -0
  335. package/dist/ws/event-buffer.d.ts +16 -0
  336. package/dist/ws/observer-handler.d.ts +42 -0
  337. package/dist/ws/observer-registry.d.ts +88 -0
  338. package/dist/ws/platform-client.d.ts +270 -0
  339. package/dist/ws/server-options.d.ts +15 -0
  340. package/package.json +87 -6
  341. package/sbom.spdx.json +14370 -0
  342. package/index.js +0 -3
@@ -0,0 +1,190 @@
1
+ /**
2
+ * WebSocket client for outgoing peer-to-peer connections between orchestrators.
3
+ *
4
+ * Follows PlatformClient patterns closely:
5
+ * - State machine: disconnected -> connecting -> handshaking -> authenticating -> connected
6
+ * - Exponential backoff with jitter for reconnection
7
+ * - Periodic heartbeat (30s default)
8
+ * - Intentional disconnect flag to prevent reconnection
9
+ *
10
+ * Authentication uses ECDH key exchange followed by join token (first connect)
11
+ * or HMAC credential proof (reconnection).
12
+ */
13
+ import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
14
+ import type { PeerRegistry } from './peer-registry.js';
15
+ type PeerConnectionState = 'disconnected' | 'connecting' | 'handshaking' | 'authenticating' | 'connected';
16
+ export interface PeerClientOptions {
17
+ /** WebSocket URL of the remote peer orchestrator. */
18
+ url: string;
19
+ /** Join token for first-time cluster join (optional if credential file exists). */
20
+ joinToken?: string;
21
+ /** Path to the credential file for reconnection. */
22
+ credentialFile: string;
23
+ /** This orchestrator's instance ID. */
24
+ instanceId: string;
25
+ /** Peer registry to update on heartbeats from remote peer. */
26
+ peerRegistry: PeerRegistry;
27
+ /** Callback to get this orchestrator's local agent inventory for heartbeats. */
28
+ getLocalInventory: () => Omit<PeerHeartbeat, 'type'>;
29
+ /** Heartbeat interval in ms. Default: 30000 (30s). */
30
+ heartbeatIntervalMs?: number;
31
+ /** Maximum reconnect delay in ms. Default: 60000 (60s). */
32
+ maxReconnectDelayMs?: number;
33
+ /** Callback when a job reroute request is received from peer. */
34
+ onJobReroute: (msg: JobReroute) => Promise<void>;
35
+ /** Callback when a job progress update is received from peer. */
36
+ onJobProgress: (msg: JobProgress) => void;
37
+ /** This orchestrator's role. Default: 'coordinator'. */
38
+ role?: 'coordinator' | 'worker';
39
+ /** Callback when a job cancel request is received from peer. */
40
+ onJobCancel: (msg: PeerJobCancel) => void;
41
+ /** Callback when a log chunk is received from a peer (coordinator side). */
42
+ onPeerLogChunk?: (chunk: PeerLogChunk) => void;
43
+ /** Callback when a cache upload request is received from a peer (coordinator side). */
44
+ onPeerCacheUploadRequest?: (req: PeerCacheUploadRequest) => Promise<PeerCacheUploadResponse>;
45
+ /** Callback for Raft vote requests. */
46
+ onRaftVoteRequest?: (msg: RaftVoteRequest) => RaftVoteResponse;
47
+ /** Callback for Raft vote responses (forwarded to Raft module). */
48
+ onRaftVoteResponse?: (msg: RaftVoteResponse) => void;
49
+ /** Callback for Raft append entries (leader heartbeat). */
50
+ onRaftAppendEntries?: (msg: RaftAppendEntries) => void;
51
+ /** Callback when a peer.leaving announcement is received. */
52
+ onPeerLeaving?: (msg: PeerLeaving) => void;
53
+ /**
54
+ * Callback when a peer.agent-token.revoke announcement is received.
55
+ * The local handler should call agentRegistry.disconnectByTokenId(tokenId)
56
+ * to close every in-flight WS authenticated by the now-revoked token.
57
+ */
58
+ onAgentTokenRevoke?: (msg: PeerAgentTokenRevoke) => void;
59
+ /**
60
+ * Callback when a config reload request is received from the peer.
61
+ * Should execute a local config reload and return the result fields,
62
+ * which are sent back via peer.config.reload.response.
63
+ *
64
+ * If undefined, incoming reload requests are answered with success=false.
65
+ */
66
+ onPeerConfigReload?: (msg: PeerConfigReload) => Promise<{
67
+ success: boolean;
68
+ version?: number;
69
+ errors?: string[];
70
+ restartRequired?: string[];
71
+ fieldsChanged?: string[];
72
+ }>;
73
+ }
74
+ export declare class PeerClient {
75
+ private ws;
76
+ private _state;
77
+ private heartbeatTimer;
78
+ private reconnectTimer;
79
+ private reconnectAttempts;
80
+ private intentionalDisconnect;
81
+ private _targetInstanceId;
82
+ private sessionKey;
83
+ private readonly ackWaiters;
84
+ private readonly cacheWaiters;
85
+ private readonly configReloadWaiters;
86
+ private readonly url;
87
+ private readonly joinToken?;
88
+ private readonly credentialFile;
89
+ private readonly instanceId;
90
+ private readonly role;
91
+ private readonly peerRegistry;
92
+ private readonly getLocalInventory;
93
+ private readonly heartbeatIntervalMs;
94
+ private readonly maxReconnectDelayMs;
95
+ private readonly onJobReroute;
96
+ private readonly onJobProgress;
97
+ private readonly onJobCancel;
98
+ private readonly onPeerLogChunk?;
99
+ private readonly onPeerCacheUploadRequest?;
100
+ private readonly onRaftVoteRequest?;
101
+ private readonly onRaftVoteResponse?;
102
+ private readonly onRaftAppendEntries?;
103
+ private readonly onPeerLeaving?;
104
+ private readonly onAgentTokenRevoke?;
105
+ private readonly onPeerConfigReload?;
106
+ constructor(options: PeerClientOptions);
107
+ /** Current connection state. */
108
+ get state(): PeerConnectionState;
109
+ /** The remote peer's instanceId (set after auth handshake). */
110
+ get targetInstanceId(): string | null;
111
+ /**
112
+ * Initiate connection to the peer orchestrator.
113
+ */
114
+ connect(): void;
115
+ /**
116
+ * Gracefully disconnect. Does not trigger reconnection.
117
+ */
118
+ disconnect(): void;
119
+ /**
120
+ * Send a peer protocol message. Returns false if not connected.
121
+ * Messages are encrypted with the session key.
122
+ */
123
+ send(msg: PeerToPeerMessage): boolean;
124
+ /**
125
+ * Send a message and wait for an ACK response.
126
+ * Used for job.reroute which expects job.reroute.ack.
127
+ *
128
+ * @returns true if accepted, false if rejected or timeout
129
+ */
130
+ sendAndWaitAck(msg: JobReroute, timeoutMs?: number): Promise<boolean>;
131
+ /**
132
+ * Send a log chunk to the coordinator (worker -> coordinator relay).
133
+ * Fire-and-forget: no ACK expected.
134
+ */
135
+ sendLogChunk(chunk: PeerLogChunk): boolean;
136
+ /**
137
+ * Send a peer.config.reload request to the connected peer and wait for the
138
+ * matching peer.config.reload.response.
139
+ *
140
+ * @returns The response, or null if not connected. Resolves with success=false
141
+ * if the peer doesn't reply within the timeout.
142
+ */
143
+ sendConfigReloadAndWait(msg: PeerConfigReload, timeoutMs?: number): Promise<PeerConfigReloadResponse | null>;
144
+ /**
145
+ * Send a cache upload request to the coordinator and wait for a response
146
+ * with a pre-signed URL.
147
+ *
148
+ * @returns The response with the uploadUrl, or rejects on timeout/disconnect.
149
+ */
150
+ sendCacheUploadRequest(req: PeerCacheUploadRequest, timeoutMs?: number): Promise<PeerCacheUploadResponse>;
151
+ /**
152
+ * Calculate the reconnection delay with exponential backoff and jitter.
153
+ */
154
+ getReconnectDelay(): number;
155
+ private doConnect;
156
+ private handleMessage;
157
+ /**
158
+ * Determine auth method and send encrypted auth request.
159
+ *
160
+ * Credentials are **identity-scoped**, not URL-scoped. A single orchestrator
161
+ * runs N peer-clients (one per remote peer) and they all share the same
162
+ * on-disk credential file. The credential represents "this orchestrator's
163
+ * cluster membership credential", and the server (peer-handler) verifies it
164
+ * by `instanceId` alone — not by the requester URL. So any peer-client
165
+ * connecting to any peer can use the same credential, as long as the
166
+ * `instanceId` on disk matches ours.
167
+ *
168
+ * (4-coordinator mesh) bug history: the previous gate was
169
+ * `cred.coordinatorUrl === this.coordinatorUrl`, which broke the moment a
170
+ * second peer-client on the same orchestrator tried to authenticate to a
171
+ * different peer URL — it would see the first peer-client's credential,
172
+ * reject it (URL mismatch), fall through to the join token, and get
173
+ * permanently rejected because the token had already been consumed by the
174
+ * first peer-client. The Option B fix (commit 79f93da4) keyed the match on
175
+ * `instanceId`; this follow-up (Plan 04 Task 1 cleanup) drops the
176
+ * `coordinatorUrl` field from the credential file schema entirely because
177
+ * it was dead weight once the match switched to identity scope.
178
+ */
179
+ private sendAuthRequest;
180
+ private routeMessage;
181
+ private startHeartbeat;
182
+ private stopHeartbeat;
183
+ private scheduleReconnect;
184
+ private cancelReconnect;
185
+ private clearAckWaiters;
186
+ private clearCacheWaiters;
187
+ private clearConfigReloadWaiters;
188
+ }
189
+ export {};
190
+ //# sourceMappingURL=peer-client.d.ts.map
@@ -0,0 +1,108 @@
1
+ import { Kysely } from 'kysely';
2
+ /**
3
+ * A peer credential record stored in the coordinator's database.
4
+ */
5
+ export interface PeerCredential {
6
+ id: string;
7
+ instanceId: string;
8
+ credentialHash: string;
9
+ role: string;
10
+ routingKeys: string[];
11
+ sourceTokenHash: string | null;
12
+ createdAt: Date;
13
+ lastSeenAt: Date | null;
14
+ lastValidatedBy: string | null;
15
+ expiresAt: Date;
16
+ revokedAt: Date | null;
17
+ }
18
+ /**
19
+ * Data persisted in this orchestrator's local credential file
20
+ * (~/.kici/credential.json). The credential is identity-scoped: one file per
21
+ * orchestrator, shared across every peer-client this orchestrator runs. See
22
+ * the `sendAuthRequest` doc in peer-client.ts for the history.
23
+ */
24
+ export interface CredentialFileData {
25
+ instanceId: string;
26
+ credential: string;
27
+ role: string;
28
+ /** ISO 8601 timestamp of when this credential was issued. */
29
+ issuedAt: string;
30
+ }
31
+ /**
32
+ * Database CRUD operations for peer credentials.
33
+ * Used by the coordinator to manage credentials for all cluster members.
34
+ */
35
+ export declare class PeerCredentialStore {
36
+ private readonly db;
37
+ constructor(db: Kysely<any>);
38
+ /**
39
+ * Save a new peer credential.
40
+ */
41
+ save(opts: {
42
+ instanceId: string;
43
+ credentialHash: string;
44
+ role: string;
45
+ routingKeys: string[];
46
+ sourceTokenHash?: string;
47
+ expiryDays?: number;
48
+ }): Promise<void>;
49
+ /**
50
+ * Find a non-revoked, non-expired credential by its hash.
51
+ */
52
+ findByCredentialHash(hash: string): Promise<PeerCredential | null>;
53
+ /**
54
+ * Find a non-revoked, non-expired credential by instance ID.
55
+ */
56
+ findByInstanceId(instanceId: string): Promise<PeerCredential | null>;
57
+ /**
58
+ * Update the last_seen_at timestamp for a credential.
59
+ * Optionally records which coordinator validated it.
60
+ */
61
+ updateLastSeen(credentialHash: string, validatedBy?: string): Promise<void>;
62
+ /**
63
+ * Revoke a peer's credential by instance ID.
64
+ */
65
+ revoke(instanceId: string): Promise<void>;
66
+ /**
67
+ * Revoke all active credentials.
68
+ * @returns The number of credentials revoked.
69
+ */
70
+ revokeAll(): Promise<number>;
71
+ /**
72
+ * List all active (non-revoked, non-expired) credentials.
73
+ */
74
+ listActive(): Promise<PeerCredential[]>;
75
+ }
76
+ /**
77
+ * Write credential data to a JSON file with restrictive permissions.
78
+ *
79
+ * Creates the parent directory (e.g., ~/.kici/) with 0700 if it doesn't exist.
80
+ * The credential file is written with 0600 permissions (owner read/write only).
81
+ *
82
+ * @param filePath - Absolute path to the credential file
83
+ * @param data - Credential data to persist
84
+ */
85
+ export declare function writeCredentialFile(filePath: string, data: CredentialFileData): Promise<void>;
86
+ /**
87
+ * Read credential data from a JSON file.
88
+ *
89
+ * @param filePath - Absolute path to the credential file
90
+ * @returns Parsed credential data, or null if the file doesn't exist
91
+ */
92
+ export declare function readCredentialFile(filePath: string): Promise<CredentialFileData | null>;
93
+ /**
94
+ * Construct a PeerCredentialStore bound to a Postgres URL, returning the
95
+ * store plus a disposer that closes the underlying pool. Lets tests
96
+ * exercise the real Kysely-backed store without importing pg/kysely.
97
+ *
98
+ * Intended for e2e coverage of the store's own CRUD methods — production
99
+ * code constructs the store with an already-allocated Kysely instance
100
+ * shared with the orchestrator.
101
+ */
102
+ export declare function createPeerCredentialStoreFromUrl(databaseUrl: string, opts?: {
103
+ maxConnections?: number;
104
+ }): Promise<{
105
+ store: PeerCredentialStore;
106
+ dispose: () => Promise<void>;
107
+ }>;
108
+ //# sourceMappingURL=peer-credentials.d.ts.map
@@ -0,0 +1,57 @@
1
+ /**
2
+ * ECDH key exchange and session encryption for peer-to-peer channels.
3
+ *
4
+ * Uses X25519 for key agreement and AES-256-GCM for message encryption.
5
+ * Session keys are derived via HKDF-SHA256 from the ECDH shared secret
6
+ * and a caller-provided nonce (to ensure per-session uniqueness).
7
+ *
8
+ * Wire format for encrypted messages (base64 encoded):
9
+ * IV (12 bytes) || AuthTag (16 bytes) || Ciphertext
10
+ */
11
+ import { type KeyObject } from 'node:crypto';
12
+ /**
13
+ * An X25519 key pair for ECDH key exchange.
14
+ * The public key is DER SPKI-encoded for wire transport.
15
+ */
16
+ export interface EcdhKeyPair {
17
+ /** DER SPKI-encoded X25519 public key. */
18
+ publicKey: Buffer;
19
+ /** X25519 private key object (not exported to wire). */
20
+ privateKey: KeyObject;
21
+ }
22
+ /**
23
+ * Generate a new X25519 key pair for ECDH key exchange.
24
+ *
25
+ * @returns Key pair with DER SPKI public key and KeyObject private key
26
+ */
27
+ export declare function generateEcdhKeyPair(): EcdhKeyPair;
28
+ /**
29
+ * Derive a 32-byte AES-256 session key from an ECDH key exchange.
30
+ *
31
+ * Performs X25519 Diffie-Hellman, then derives the session key via
32
+ * HKDF-SHA256 with the provided nonce as salt.
33
+ *
34
+ * @param localPrivateKey - This node's X25519 private key
35
+ * @param remotePubKeyDer - Remote node's DER SPKI-encoded X25519 public key
36
+ * @param nonce - Random nonce (used as HKDF salt for per-session uniqueness)
37
+ * @returns 32-byte session key suitable for AES-256-GCM
38
+ */
39
+ export declare function deriveSessionKey(localPrivateKey: KeyObject, remotePubKeyDer: Buffer, nonce: Buffer): Buffer;
40
+ /**
41
+ * Encrypt a plaintext message with a session key using AES-256-GCM.
42
+ *
43
+ * @param plaintext - The message to encrypt
44
+ * @param sessionKey - 32-byte session key from deriveSessionKey
45
+ * @returns Base64-encoded string: IV (12) || AuthTag (16) || Ciphertext
46
+ */
47
+ export declare function encryptMessage(plaintext: string, sessionKey: Buffer): string;
48
+ /**
49
+ * Decrypt a message encrypted with encryptMessage.
50
+ *
51
+ * @param encrypted - Base64-encoded string: IV (12) || AuthTag (16) || Ciphertext
52
+ * @param sessionKey - 32-byte session key (must match the key used for encryption)
53
+ * @returns Decrypted plaintext string
54
+ * @throws If the session key is wrong or the ciphertext has been tampered with
55
+ */
56
+ export declare function decryptMessage(encrypted: string, sessionKey: Buffer): string;
57
+ //# sourceMappingURL=peer-crypto.d.ts.map
@@ -0,0 +1,97 @@
1
+ /**
2
+ * WebSocket handler for incoming peer connections from other orchestrators.
3
+ *
4
+ * Accepts WS upgrade, performs ECDH key exchange for encrypted channel,
5
+ * validates authentication via join token (first connect) or HMAC credential
6
+ * proof (reconnection), registers the peer in PeerRegistry, and routes
7
+ * messages bidirectionally. Sends periodic heartbeats to the connecting peer.
8
+ */
9
+ import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
10
+ import type { PeerRegistry } from './peer-registry.js';
11
+ import type { PeerCredentialStore } from './peer-credentials.js';
12
+ import { type JoinTokenManager } from './join-token.js';
13
+ /**
14
+ * Minimal WebSocket interface for peer connections.
15
+ * Same pattern as agent/registry.ts WsLike, allowing mock testing.
16
+ */
17
+ export interface PeerWsLike {
18
+ send(data: string): void;
19
+ close(code?: number, reason?: string): void;
20
+ on(event: string, listener: (...args: unknown[]) => void): void;
21
+ readyState: number;
22
+ }
23
+ export interface PeerHandlerDeps {
24
+ /** Join token manager for validating first-time peer tokens. */
25
+ tokenManager: JoinTokenManager;
26
+ /** Credential store for session credential CRUD. */
27
+ credentialStore: PeerCredentialStore;
28
+ /** Accepted roles for connecting peers. Default: ['coordinator', 'worker'] (accept any). */
29
+ acceptedRoles?: Array<'coordinator' | 'worker'>;
30
+ /** This orchestrator's instance ID. */
31
+ instanceId: string;
32
+ /** Peer registry to track incoming peers. */
33
+ peerRegistry: PeerRegistry;
34
+ /** Callback to get this orchestrator's local agent inventory for heartbeats. */
35
+ getLocalInventory: () => Omit<PeerHeartbeat, 'type'>;
36
+ /** Heartbeat interval in ms. Default: 30000 (30s). */
37
+ heartbeatIntervalMs?: number;
38
+ /** Auth timeout in ms. Default: 15000 (15s). */
39
+ authTimeoutMs?: number;
40
+ /** Callback when a job reroute request is received from peer. */
41
+ onJobReroute: (msg: JobReroute) => Promise<void>;
42
+ /** Callback when a job progress update is received from peer. */
43
+ onJobProgress: (msg: JobProgress) => void;
44
+ /** Callback when a job cancel request is received from peer. */
45
+ onJobCancel: (msg: PeerJobCancel) => void;
46
+ /** Callback when a log chunk is received from a worker peer. */
47
+ onPeerLogChunk?: (chunk: PeerLogChunk, peerId: string) => void;
48
+ /** Callback when a cache upload request is received from a worker peer. */
49
+ onPeerCacheUploadRequest?: (req: PeerCacheUploadRequest, peerId: string) => Promise<PeerCacheUploadResponse>;
50
+ /** Callback for Raft vote requests. */
51
+ onRaftVoteRequest?: (msg: RaftVoteRequest) => RaftVoteResponse;
52
+ /** Callback for Raft vote responses (forwarded to Raft module). */
53
+ onRaftVoteResponse?: (msg: RaftVoteResponse) => void;
54
+ /** Callback for Raft append entries (leader heartbeat). */
55
+ onRaftAppendEntries?: (msg: RaftAppendEntries) => void;
56
+ /** Callback when a peer.leaving announcement is received. */
57
+ onPeerLeaving?: (msg: PeerLeaving) => void;
58
+ /**
59
+ * Callback when a peer.agent-token.revoke announcement is received.
60
+ * The local handler should call agentRegistry.disconnectByTokenId(tokenId)
61
+ * to close every in-flight WS authenticated by the now-revoked token.
62
+ */
63
+ onAgentTokenRevoke?: (msg: PeerAgentTokenRevoke) => void;
64
+ /**
65
+ * Callback when a config reload request is received from a peer.
66
+ * Should execute a local config reload and return the result fields,
67
+ * which are sent back via peer.config.reload.response.
68
+ *
69
+ * If undefined, incoming reload requests are answered with success=false
70
+ * and an error explaining that reload is unavailable on this peer.
71
+ */
72
+ onPeerConfigReload?: (msg: PeerConfigReload) => Promise<{
73
+ success: boolean;
74
+ version?: number;
75
+ errors?: string[];
76
+ restartRequired?: string[];
77
+ fieldsChanged?: string[];
78
+ }>;
79
+ }
80
+ /**
81
+ * Create a handler function for incoming peer WebSocket connections.
82
+ *
83
+ * Returns `handleConnection(ws, remoteIp?)` which should be called when a new
84
+ * WebSocket connection is upgraded on the peer endpoint.
85
+ */
86
+ export declare function createPeerHandler(deps: PeerHandlerDeps): {
87
+ handleConnection: (ws: PeerWsLike, remoteIp?: string) => void;
88
+ sendToPeer: (targetInstanceId: string, msg: PeerToPeerMessage) => boolean;
89
+ sendAndWaitAck: (targetInstanceId: string, msg: JobReroute, timeoutMs?: number) => Promise<boolean>;
90
+ sendConfigReloadAndWait: (targetInstanceId: string, msg: PeerConfigReload, timeoutMs?: number) => Promise<PeerConfigReloadResponse | null>;
91
+ getConnectionCount: () => number;
92
+ broadcastHeartbeat: (inventory: Omit<PeerHeartbeat, "type">) => void;
93
+ broadcastAgentTokenRevoke: (msg: PeerAgentTokenRevoke) => void;
94
+ cleanup: () => void;
95
+ closeAllInbound: () => void;
96
+ };
97
+ //# sourceMappingURL=peer-handler.d.ts.map
@@ -0,0 +1,186 @@
1
+ /**
2
+ * In-memory peer registry for tracking orchestrator peers in a cluster.
3
+ *
4
+ * Maintains peer state including agent inventories from heartbeats,
5
+ * enabling routing decisions without real-time queries. Follows the
6
+ * same label-matching semantics as agent/registry.ts for consistency.
7
+ */
8
+ import type { PeerHeartbeat, PeerCapabilities, ScalerCapacitySummary } from '@kici-dev/engine';
9
+ export interface PeerAgentInfo {
10
+ agentId: string;
11
+ labels: string[];
12
+ activeJobs: number;
13
+ maxConcurrency: number;
14
+ platform: string;
15
+ arch: string;
16
+ /**
17
+ * Kubernetes-taint-style mandatory labels inherited from the spawning
18
+ * scaler. Empty for static agents and warm-pool replenishment spawns.
19
+ * Cross-peer routing applies the same gate the local label matcher does:
20
+ * a connected-agent entry only matches when every mandatory label appears
21
+ * in the required label set.
22
+ */
23
+ mandatoryLabels: string[];
24
+ }
25
+ export interface PeerInfo {
26
+ instanceId: string;
27
+ connectionId: string;
28
+ address: string | null;
29
+ routingKeys: string[];
30
+ connected: boolean;
31
+ lastHeartbeatAt: number;
32
+ agents: PeerAgentInfo[];
33
+ draining: boolean;
34
+ capabilities: PeerCapabilities;
35
+ term: number;
36
+ leaderId: string | null;
37
+ /** Scaler-advertised capacity for on-demand backends (from heartbeat). */
38
+ scalerCapacity?: ScalerCapacitySummary[];
39
+ /** Shared config version reported by this peer (0 = unknown/legacy). */
40
+ configVersion: number;
41
+ /** Registry version reported by this peer (0 = unknown/legacy). */
42
+ registryVersion: number;
43
+ /** Cluster role of the peer. */
44
+ role: 'coordinator' | 'worker';
45
+ hostname?: string;
46
+ osRelease?: string;
47
+ totalMemoryMb?: number;
48
+ memoryUsedMb?: number;
49
+ memoryAvailableMb?: number;
50
+ cpuCount?: number;
51
+ uptimeSeconds?: number;
52
+ nodeVersion?: string;
53
+ runningAsUser?: string | null;
54
+ runningAsUid?: number | null;
55
+ version?: string | null;
56
+ }
57
+ export interface PeerRegistryOptions {
58
+ /** Called when a peer's configVersion is higher than the local version. */
59
+ onConfigVersionBehind?: (peerVersion: number) => void;
60
+ /** Called when a peer's registryVersion is higher than the local version. */
61
+ onRegistryVersionBehind?: (peerVersion: number) => void;
62
+ /** Called when a peer transitions to disconnected state. */
63
+ onPeerDisconnected?: (instanceId: string) => void;
64
+ }
65
+ export declare class PeerRegistry {
66
+ private readonly peers;
67
+ private localConfigVersion;
68
+ private localRegistryVersion;
69
+ private readonly onConfigVersionBehind?;
70
+ private readonly onRegistryVersionBehind?;
71
+ private readonly onPeerDisconnected?;
72
+ constructor(options?: PeerRegistryOptions);
73
+ /**
74
+ * Update the local config version for comparison with peer heartbeats.
75
+ */
76
+ setLocalConfigVersion(version: number): void;
77
+ /**
78
+ * Update the local registry version for comparison with peer heartbeats.
79
+ */
80
+ setLocalRegistryVersion(version: number): void;
81
+ /**
82
+ * Add or update a peer entry. Sets initial connected state to true.
83
+ * If the peer already exists and is connected, preserves capabilities
84
+ * (avoids the incoming handler resetting data set by the outgoing client).
85
+ * On fresh add or disconnected peer, starts with empty capabilities —
86
+ * auth response or first heartbeat will populate fresh data.
87
+ */
88
+ addPeer(info: {
89
+ instanceId: string;
90
+ connectionId: string;
91
+ address: string | null;
92
+ routingKeys: string[];
93
+ role?: 'coordinator' | 'worker';
94
+ }): void;
95
+ /**
96
+ * Remove a peer entirely from the registry.
97
+ */
98
+ removePeer(instanceId: string): void;
99
+ /**
100
+ * Update peer state from a heartbeat message.
101
+ * Updates agent inventory, draining status, capabilities, term, leaderId,
102
+ * and lastHeartbeatAt timestamp.
103
+ */
104
+ updateHeartbeat(instanceId: string, heartbeat: PeerHeartbeat): void;
105
+ /**
106
+ * Get a single peer by instanceId.
107
+ */
108
+ getPeer(instanceId: string): PeerInfo | undefined;
109
+ /**
110
+ * Get all tracked peers.
111
+ */
112
+ getAllPeers(): PeerInfo[];
113
+ /**
114
+ * Get only connected peers.
115
+ */
116
+ getConnectedPeers(): PeerInfo[];
117
+ /**
118
+ * Find peers whose agents or scaler backends match any of the given label
119
+ * sets AND have available capacity.
120
+ *
121
+ * A peer matches if EITHER:
122
+ * 1. Connected agents: at least one agent's labels are a superset of a
123
+ * required label set with activeJobs < maxConcurrency.
124
+ * 2. Scaler capacity: at least one scaler backend has activeCount < maxAgents
125
+ * AND its label sets overlap with the required label sets (same
126
+ * intersection semantics as agent matching).
127
+ *
128
+ * Excludes draining peers and disconnected peers.
129
+ *
130
+ * @param labelSets - Array of label sets. A peer matches if ANY of its
131
+ * agents or scaler backends match ANY of the label sets with capacity.
132
+ */
133
+ findPeersWithCapacity(labelSets: string[][]): PeerInfo[];
134
+ /**
135
+ * Find peers whose agents or scaler backends match any of the given label sets,
136
+ * regardless of current capacity. Used to differentiate "no peer handles this label"
137
+ * from "peers exist but are at capacity".
138
+ *
139
+ * Excludes disconnected and draining peers.
140
+ */
141
+ findPeersWithLabels(labelSets: string[][]): PeerInfo[];
142
+ /**
143
+ * Mark a peer as disconnected and immediately evict its capabilities.
144
+ * No grace period — disconnected peers have no usable agents or scaler capacity.
145
+ */
146
+ markDisconnected(instanceId: string): void;
147
+ /**
148
+ * Mark a peer as connected.
149
+ */
150
+ markConnected(instanceId: string): void;
151
+ /**
152
+ * Check if a peer's last heartbeat is older than the given threshold.
153
+ */
154
+ isStale(instanceId: string, thresholdMs: number): boolean;
155
+ /**
156
+ * Total number of tracked peers (connected + disconnected).
157
+ */
158
+ getPeerCount(): number;
159
+ /**
160
+ * Number of currently connected peers.
161
+ */
162
+ getConnectedPeerCount(): number;
163
+ /**
164
+ * Get count of connected coordinator (non-worker) peers.
165
+ * Used for Raft quorum calculations — workers don't participate in elections.
166
+ */
167
+ getConnectedCoordinatorPeerCount(): number;
168
+ /**
169
+ * Evict stale peers whose last heartbeat exceeds the timeout.
170
+ * Marks them as disconnected and clears their capabilities.
171
+ * Returns the list of evicted peer instanceIds.
172
+ *
173
+ * called on a timer by the coordinator
174
+ * (2 missed heartbeats at 30s interval = 60s default timeout).
175
+ */
176
+ evictStalePeers(staleTimeoutMs: number): string[];
177
+ /**
178
+ * Get all peers with role=worker.
179
+ */
180
+ getWorkerPeers(): PeerInfo[];
181
+ /**
182
+ * Get all peers with role=coordinator.
183
+ */
184
+ getCoordinatorPeers(): PeerInfo[];
185
+ }
186
+ //# sourceMappingURL=peer-registry.d.ts.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Postgres-backed Raft state persistence.
3
+ *
4
+ * Stores the minimal Raft persistent state (currentTerm, votedFor, leaderId)
5
+ * in the raft_state table. One row per cluster (default cluster_id = 'default').
6
+ * State is loaded on node startup and saved on term/vote/leader transitions.
7
+ */
8
+ import type { Kysely } from 'kysely';
9
+ import type { Database } from '../db/types.js';
10
+ export interface RaftPersistentState {
11
+ currentTerm: number;
12
+ votedFor: string | null;
13
+ leaderId: string | null;
14
+ }
15
+ export declare class RaftStateStore {
16
+ private readonly db;
17
+ private readonly clusterId;
18
+ constructor(deps: {
19
+ db: Kysely<Database>;
20
+ clusterId?: string;
21
+ });
22
+ /**
23
+ * Load persisted Raft state from the DB.
24
+ * Returns defaults (term=0, votedFor=null, leaderId=null) if no row exists.
25
+ */
26
+ load(): Promise<RaftPersistentState>;
27
+ /**
28
+ * Upsert full Raft state into the DB.
29
+ */
30
+ save(state: RaftPersistentState): Promise<void>;
31
+ /**
32
+ * Update just the leader and term fields (lightweight update for heartbeat acceptance).
33
+ */
34
+ updateLeader(leaderId: string, term: number): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=raft-state.d.ts.map