@kici-dev/orchestrator 0.16.0 → 0.18.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/dist/agent/agent-version.d.ts +12 -21
- package/dist/agent/dispatcher.d.ts +283 -437
- package/dist/agent/host-roster-reaper.d.ts +8 -10
- package/dist/agent/host-roster.d.ts +77 -100
- package/dist/agent/ownership-refusal.d.ts +3 -5
- package/dist/agent/ownership-tracker.d.ts +42 -76
- package/dist/agent/registry.d.ts +121 -233
- package/dist/agent/token-store.d.ts +34 -77
- package/dist/agent-packaging/availability.d.ts +6 -9
- package/dist/agent-packaging/download.d.ts +6 -9
- package/dist/agent-packaging/install-closure.d.ts +9 -12
- package/dist/agent-packaging/node-binary.d.ts +6 -10
- package/dist/agent-packaging/node-runtime-members.d.ts +3 -5
- package/dist/agent-packaging/refresh.d.ts +12 -16
- package/dist/agent-packaging/store.d.ts +5 -6
- package/dist/agent-packaging/upload.d.ts +3 -4
- package/dist/app.d.ts +0 -3
- package/dist/approvals/apply-decision.d.ts +39 -45
- package/dist/approvals/approval-resolver.d.ts +11 -23
- package/dist/approvals/held-run-release-wiring.d.ts +14 -21
- package/dist/approvals/independent-wiring.d.ts +13 -20
- package/dist/approvals/resume-router.d.ts +1 -1
- package/dist/approvals/step-approval-bridge.d.ts +11 -21
- package/dist/approvals/triggerer-subject.d.ts +8 -10
- package/dist/artifacts/artifact-store.d.ts +55 -95
- package/dist/artifacts/failure-messages.d.ts +15 -20
- package/dist/audit/access-log.d.ts +27 -45
- package/dist/audit/sampling-rate-limiter.d.ts +11 -23
- package/dist/cache/agent-job-failed-error.d.ts +2 -3
- package/dist/cache/build-coordinator.d.ts +5 -28
- package/dist/cache/cache-upload-settle.d.ts +11 -11
- package/dist/cache/dep-cache.d.ts +32 -60
- package/dist/cache/dispatch-cache-ref-tracker.d.ts +15 -23
- package/dist/cache/global-eval-round-cache.d.ts +33 -41
- package/dist/cache/pending-builds.d.ts +2 -6
- package/dist/cache/pending-dynamics.d.ts +2 -7
- package/dist/cache/pending-global-evals.d.ts +9 -12
- package/dist/cache/pending-inits.d.ts +11 -22
- package/dist/cache/pending-precursor-db-watcher.d.ts +24 -32
- package/dist/cache/pending-tracker.d.ts +17 -45
- package/dist/cache/precursor-result.d.ts +17 -30
- package/dist/cache/source-cache.d.ts +36 -65
- package/dist/cache/user-cache.d.ts +21 -25
- package/dist/cancel/cancel-held-run.d.ts +13 -16
- package/dist/cancel/cancel-route-answer.d.ts +1 -2
- package/dist/cancel/cancel-run.d.ts +26 -50
- package/dist/cancel/dashboard-cancel-handler.d.ts +2 -2
- package/dist/cancel/peer-job-cancel.d.ts +11 -14
- package/dist/cancel/sweep-stuck-cancelling.d.ts +9 -13
- package/dist/cli/api-client.d.ts +33 -76
- package/dist/cli/commands/access-log.d.ts +4 -24
- package/dist/cli/commands/agent-package.d.ts +7 -15
- package/dist/cli/commands/agent-service/agent-env.d.ts +3 -6
- package/dist/cli/commands/agent-service/index.d.ts +4 -10
- package/dist/cli/commands/agent-service/install.d.ts +4 -11
- package/dist/cli/commands/agent-service/status.d.ts +4 -9
- package/dist/cli/commands/agent-service/upgrade.d.ts +4 -16
- package/dist/cli/commands/agent.d.ts +12 -25
- package/dist/cli/commands/api-key.d.ts +7 -7
- package/dist/cli/commands/attestations-list.d.ts +7 -17
- package/dist/cli/commands/attestations-retry.d.ts +5 -11
- package/dist/cli/commands/attestations-reverify.d.ts +4 -5
- package/dist/cli/commands/attestations.d.ts +3 -8
- package/dist/cli/commands/audit.d.ts +1 -5
- package/dist/cli/commands/backend.d.ts +2 -11
- package/dist/cli/commands/check-run.d.ts +7 -18
- package/dist/cli/commands/cluster-name.d.ts +5 -12
- package/dist/cli/commands/cluster-settings.d.ts +28 -53
- package/dist/cli/commands/cold-store.d.ts +7 -15
- package/dist/cli/commands/config.d.ts +9 -20
- package/dist/cli/commands/context.d.ts +8 -20
- package/dist/cli/commands/dashboard-encryption-key.d.ts +9 -14
- package/dist/cli/commands/db-backup.d.ts +17 -32
- package/dist/cli/commands/db.d.ts +3 -4
- package/dist/cli/commands/debug-bundle.d.ts +3 -10
- package/dist/cli/commands/diagnose.d.ts +3 -14
- package/dist/cli/commands/event-dlq.d.ts +4 -12
- package/dist/cli/commands/event-log.d.ts +2 -20
- package/dist/cli/commands/execution.d.ts +3 -9
- package/dist/cli/commands/firecracker/index.d.ts +3 -6
- package/dist/cli/commands/held-run.d.ts +23 -37
- package/dist/cli/commands/host.d.ts +4 -11
- package/dist/cli/commands/inspect-bundle.d.ts +3 -11
- package/dist/cli/commands/local-hook.d.ts +2 -10
- package/dist/cli/commands/local-trigger.d.ts +2 -2
- package/dist/cli/commands/orchestrator-service/drain.d.ts +3 -6
- package/dist/cli/commands/orchestrator-service/index.d.ts +6 -14
- package/dist/cli/commands/orchestrator-service/install-env.d.ts +25 -49
- package/dist/cli/commands/orchestrator-service/install.d.ts +2 -5
- package/dist/cli/commands/orchestrator-service/status.d.ts +10 -22
- package/dist/cli/commands/orchestrator-service/upgrade.d.ts +6 -15
- package/dist/cli/commands/org-settings.d.ts +5 -11
- package/dist/cli/commands/peer.d.ts +5 -10
- package/dist/cli/commands/queue.d.ts +3 -9
- package/dist/cli/commands/registration.d.ts +4 -8
- package/dist/cli/commands/remote-source.d.ts +6 -12
- package/dist/cli/commands/rotate.d.ts +1 -5
- package/dist/cli/commands/runs.d.ts +1 -30
- package/dist/cli/commands/scaler.d.ts +5 -12
- package/dist/cli/commands/secret.d.ts +1 -9
- package/dist/cli/commands/service-health.d.ts +5 -10
- package/dist/cli/commands/shared/admin-cli-access-log.d.ts +10 -19
- package/dist/cli/commands/shared/bare-metal-binary-check.d.ts +86 -0
- package/dist/cli/commands/shared/cli-action.d.ts +1 -4
- package/dist/cli/commands/shared/confirm.d.ts +5 -10
- package/dist/cli/commands/shared/db.d.ts +1 -12
- package/dist/cli/commands/shared/env-file-mode.d.ts +6 -9
- package/dist/cli/commands/shared/secret-input.d.ts +7 -21
- package/dist/cli/commands/shared/service-verbs.d.ts +6 -12
- package/dist/cli/commands/shared/unbound-context-warning.d.ts +2 -4
- package/dist/cli/commands/shared/upgrade-hooks.d.ts +7 -16
- package/dist/cli/commands/shared/upgrade-safety.d.ts +20 -31
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +83 -161
- package/dist/cli/commands/signing-key.d.ts +4 -9
- package/dist/cli/commands/source-manifest.d.ts +11 -14
- package/dist/cli/commands/source.d.ts +7 -32
- package/dist/cli/commands/token.d.ts +4 -15
- package/dist/cli/commands/trust-policy.d.ts +35 -88
- package/dist/cli/commands/variable.d.ts +3 -5
- package/dist/cli/commands/workflow.d.ts +3 -24
- package/dist/cli/join.d.ts +3 -6
- package/dist/cli/kici-admin.d.ts +11 -29
- package/dist/cli/lazy-deps/cache.d.ts +1 -14
- package/dist/cli/lazy-deps/downloader.d.ts +4 -27
- package/dist/cli/lazy-deps/registry.d.ts +5 -25
- package/dist/cli/local-github-ingress-url.d.ts +5 -7
- package/dist/cli/loopback-callback.d.ts +6 -7
- package/dist/cli/open-browser.d.ts +4 -8
- package/dist/cli/root-option-routing.d.ts +17 -29
- package/dist/cli/service/backup-timer.d.ts +16 -39
- package/dist/cli/service/compose-path.d.ts +3 -7
- package/dist/cli/service/compose.d.ts +12 -30
- package/dist/cli/service/deploy-env.d.ts +6 -18
- package/dist/cli/service/entrypoint.d.ts +14 -25
- package/dist/cli/service/env-assignment.d.ts +6 -17
- package/dist/cli/service/image-digests.d.ts +6 -15
- package/dist/cli/service/index.d.ts +2 -18
- package/dist/cli/service/instance/index-file.d.ts +9 -16
- package/dist/cli/service/instance/manifest.d.ts +33 -59
- package/dist/cli/service/instance/resolve.d.ts +32 -68
- package/dist/cli/service/instance/types.d.ts +12 -29
- package/dist/cli/service/launchd.d.ts +29 -60
- package/dist/cli/service/platform-detect.d.ts +13 -68
- package/dist/cli/service/privilege.d.ts +12 -25
- package/dist/cli/service/shutdown-grace.d.ts +11 -20
- package/dist/cli/service/systemd.d.ts +7 -20
- package/dist/cli/service/types.d.ts +63 -114
- package/dist/cli/service/windows-acl.d.ts +14 -21
- package/dist/cli/service/windows-env-file.d.ts +10 -28
- package/dist/cli/service/windows.d.ts +15 -31
- package/dist/cli/webhook-url-notes.d.ts +4 -6
- package/dist/cli/wizard/agent-wizard.d.ts +3 -14
- package/dist/cli/wizard/orchestrator-wizard.d.ts +12 -37
- package/dist/cli/wizard/prompts.d.ts +6 -16
- package/dist/cli.js +4553 -6556
- package/dist/cluster/cluster-identity.d.ts +20 -32
- package/dist/cluster/cluster-settings-reader.d.ts +50 -77
- package/dist/cluster/coordinator-credential.d.ts +4 -5
- package/dist/cluster/coordinator.d.ts +76 -148
- package/dist/cluster/health-api.d.ts +2 -10
- package/dist/cluster/index.d.ts +1 -6
- package/dist/cluster/instance-heartbeat.d.ts +26 -42
- package/dist/cluster/join-client.d.ts +4 -7
- package/dist/cluster/join-handler.d.ts +3 -7
- package/dist/cluster/join-token.d.ts +13 -27
- package/dist/cluster/leader-gated-scheduler.d.ts +8 -17
- package/dist/cluster/orphan-recovery.d.ts +13 -35
- package/dist/cluster/peer-auth-coordinator.d.ts +2 -8
- package/dist/cluster/peer-client.d.ts +46 -76
- package/dist/cluster/peer-credentials.d.ts +24 -70
- package/dist/cluster/peer-crypto.d.ts +5 -26
- package/dist/cluster/peer-forget.d.ts +3 -3
- package/dist/cluster/peer-handler.d.ts +47 -71
- package/dist/cluster/peer-registry.d.ts +37 -106
- package/dist/cluster/plan-headroom-store.d.ts +4 -6
- package/dist/cluster/raft-state.d.ts +4 -13
- package/dist/cluster/raft.d.ts +18 -53
- package/dist/cluster/reconcile-identity.d.ts +8 -11
- package/dist/cluster/rerouted-job-guard.d.ts +7 -11
- package/dist/cluster/verified-issuer.d.ts +2 -3
- package/dist/cluster/worker-cluster-settings.d.ts +7 -10
- package/dist/cluster/worker-eviction.d.ts +16 -26
- package/dist/cold-store/load-access-log-range.d.ts +15 -31
- package/dist/cold-store/load-event-log-range.d.ts +13 -27
- package/dist/cold-store/load-secret-audit-log-range.d.ts +8 -12
- package/dist/cold-store/orchestrator-cold-store.d.ts +8 -28
- package/dist/cold-store/tables/access-log.d.ts +12 -14
- package/dist/cold-store/tables/event-log.d.ts +4 -8
- package/dist/cold-store/tables/execution-runs.d.ts +6 -11
- package/dist/cold-store/tables/secret-audit-log.d.ts +5 -12
- package/dist/concurrency/dispatch-next-queued.d.ts +13 -25
- package/dist/concurrency/queue-manager.d.ts +22 -54
- package/dist/concurrency/waiters.d.ts +15 -31
- package/dist/config/encryption.d.ts +0 -2
- package/dist/config/loader.d.ts +0 -1
- package/dist/config/reload.d.ts +0 -6
- package/dist/config/resolver.d.ts +0 -4
- package/dist/config/shared-store.d.ts +0 -2
- package/dist/config/types.d.ts +0 -3
- package/dist/content-requirements-cache.d.ts +0 -1
- package/dist/contexts/context-store.d.ts +0 -2
- package/dist/contexts/held-runs.d.ts +0 -2
- package/dist/contexts/variable-store.d.ts +0 -2
- package/dist/dashboard/attestation-filters.d.ts +13 -18
- package/dist/dashboard/handler.d.ts +143 -307
- package/dist/db/client.d.ts +2 -6
- package/dist/db/deferred-index-build.d.ts +8 -10
- package/dist/db/deferred-indexes.d.ts +15 -22
- package/dist/db/migration-provider.d.ts +2 -4
- package/dist/db/migration-test-harness.d.ts +26 -44
- package/dist/db/migrations/001_initial.d.ts +1 -4
- package/dist/db/migrations/002_config_versions_key_version.d.ts +5 -8
- package/dist/db/migrations/003_access_log.d.ts +10 -15
- package/dist/db/migrations/004_rename_bundle_to_source.d.ts +7 -9
- package/dist/db/migrations/005_cold_store_chunk_counter.d.ts +4 -10
- package/dist/db/migrations/006_runs_jobs_steps_archived_at.d.ts +9 -29
- package/dist/db/migrations/007_audit_logs_archived_at.d.ts +11 -30
- package/dist/db/migrations/008_event_log_archived_at.d.ts +14 -29
- package/dist/db/migrations/009_access_log_trigram.d.ts +4 -8
- package/dist/db/migrations/010_cold_store_chunks.d.ts +16 -26
- package/dist/db/migrations/011_drop_source_secrets_notify.d.ts +6 -14
- package/dist/db/migrations/012_peer_credentials_active_uniq.d.ts +9 -14
- package/dist/db/migrations/013_execution_log_bytes.d.ts +5 -13
- package/dist/db/migrations/014_kici_events_lease_retry.d.ts +14 -27
- package/dist/db/migrations/015_org_settings_customer_scoped.d.ts +12 -17
- package/dist/db/migrations/016_org_settings_allow_http_npm.d.ts +4 -6
- package/dist/db/migrations/019_generic_sources_change_notify.d.ts +13 -17
- package/dist/db/migrations/020_org_settings_dashboard_write_policy.d.ts +5 -8
- package/dist/db/migrations/021_check_run_tracking.d.ts +5 -17
- package/dist/db/migrations/022_scaler_manager_state.d.ts +10 -20
- package/dist/db/migrations/023_dispatch_queue_recovery_deadline.d.ts +7 -18
- package/dist/db/migrations/024_dispatch_queue_provisioning_error.d.ts +5 -11
- package/dist/db/migrations/025_init_failure.d.ts +5 -9
- package/dist/db/migrations/027_workflow_timeout.d.ts +5 -13
- package/dist/db/migrations/029_dispatch_queue_attempts.d.ts +4 -10
- package/dist/db/migrations/030_held_runs_env_set_null.d.ts +5 -6
- package/dist/db/migrations/031_dispatch_queue_ack_deadline.d.ts +6 -12
- package/dist/db/migrations/033_org_settings_approval.d.ts +7 -10
- package/dist/db/migrations/034_held_runs_generalize.d.ts +10 -18
- package/dist/db/migrations/035_pending_workflow_contexts.d.ts +4 -5
- package/dist/db/migrations/037_generic_sources_provider_type_local.d.ts +5 -6
- package/dist/db/migrations/038_remote_sources.d.ts +5 -7
- package/dist/db/migrations/045_host_properties.d.ts +5 -9
- package/dist/db/migrations/046_join_token_consumed_by_instance.d.ts +5 -10
- package/dist/db/migrations/048_host_reboot_pending.d.ts +6 -12
- package/dist/db/migrations/049_held_runs_payload.d.ts +3 -8
- package/dist/db/migrations/050_sources_slug.d.ts +4 -5
- package/dist/db/migrations/051_binding_host_pattern.d.ts +7 -10
- package/dist/db/migrations/052_host_reach_metadata.d.ts +8 -11
- package/dist/db/migrations/053_agent_token_single_use.d.ts +6 -10
- package/dist/db/migrations/054_local_working_tree.d.ts +4 -6
- package/dist/db/migrations/055_agent_token_mandatory_labels.d.ts +6 -11
- package/dist/db/migrations/056_execution_jobs_environments.d.ts +4 -11
- package/dist/db/migrations/064_execution_jobs_skipped_environments.d.ts +6 -9
- package/dist/db/migrations/065_pending_attestations.d.ts +9 -12
- package/dist/db/migrations/066_pending_attestations_rejected.d.ts +4 -7
- package/dist/db/migrations/068_request_idempotency.d.ts +7 -9
- package/dist/db/migrations/069_reroute_tunables.d.ts +6 -14
- package/dist/db/migrations/071_batch_accumulation.d.ts +8 -16
- package/dist/db/migrations/072_dispatch_queue_run_id_index.d.ts +3 -9
- package/dist/db/migrations/074_normalize_zero_concurrency_limit.d.ts +4 -7
- package/dist/db/migrations/076_artifacts.d.ts +7 -8
- package/dist/db/migrations/078_org_settings_backup_staleness.d.ts +3 -6
- package/dist/db/migrations/079_org_settings_scaler_spawn_timeout.d.ts +4 -8
- package/dist/db/migrations/080_cluster_settings.d.ts +6 -10
- package/dist/db/migrations/081_org_settings_queue_timeout.d.ts +7 -11
- package/dist/db/migrations/082_host_s3_reachable.d.ts +5 -10
- package/dist/db/migrations/083_org_settings_artifact_caps.d.ts +6 -8
- package/dist/db/migrations/084_orchestrator_signing_keys.d.ts +8 -13
- package/dist/db/migrations/085_cluster_settings_reroute_flap_grace_ms.d.ts +4 -5
- package/dist/db/migrations/086_cluster_settings_max_fanout_hosts.d.ts +3 -3
- package/dist/db/migrations/087_cluster_settings_event_router_rate_limit.d.ts +5 -3
- package/dist/db/migrations/088_cluster_settings_cache_max_tarball_bytes.d.ts +3 -3
- package/dist/db/migrations/089_cluster_settings_cache_ttl_days.d.ts +3 -5
- package/dist/db/migrations/090_cluster_settings_concurrency_wait_timeout_ms.d.ts +3 -6
- package/dist/db/migrations/091_cluster_settings_agent_token_ttl_ms.d.ts +3 -6
- package/dist/db/migrations/092_cluster_settings_version.d.ts +5 -9
- package/dist/db/migrations/094_dashboard_encryption_keys.d.ts +9 -14
- package/dist/db/migrations/095_dashboard_write_policy_tristate.d.ts +2 -6
- package/dist/db/migrations/099_cluster_settings_dashboard_verified_issuer.d.ts +8 -11
- package/dist/db/migrations/101_contexts_hold_expiry_drop_default.d.ts +9 -15
- package/dist/db/migrations/102_dispatch_queue_agent_id.d.ts +6 -11
- package/dist/db/migrations/103_cluster_settings_ownership_db_check_timeout_ms.d.ts +6 -12
- package/dist/db/migrations/104_check_run_terminal_sent.d.ts +8 -14
- package/dist/db/migrations/105_org_trust_policy.d.ts +5 -7
- package/dist/db/migrations/106_cluster_settings_check_run_tracking_ttl_days.d.ts +4 -12
- package/dist/db/migrations/107_check_run_tracking_updated_at_index.d.ts +8 -14
- package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +10 -19
- package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +2 -3
- package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +7 -8
- package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +9 -16
- package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +8 -16
- package/dist/db/migrations/125_org_trust_directory.d.ts +13 -21
- package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +10 -16
- package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +8 -16
- package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +6 -11
- package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +8 -11
- package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +13 -20
- package/dist/db/migrations/131_execution_jobs_git_credentials.d.ts +12 -22
- package/dist/db/migrations/132_execution_runs_trigger_event.d.ts +6 -16
- package/dist/db/migrations/133_dispatch_queue_source_tar_digest.d.ts +6 -9
- package/dist/db/migrations/140_dispatch_queue_canonical_labels.d.ts +22 -40
- package/dist/db/migrations/143_execution_jobs_precursor_result.d.ts +9 -18
- package/dist/db/migrations/144_execution_runs_registration_window.d.ts +12 -20
- package/dist/db/migrations/147_sealed_job_secrets.d.ts +5 -9
- package/dist/db/migrations/148_cluster_settings_sealed_secrets_retry_backoff_ms.d.ts +7 -13
- package/dist/db/migrations/150_execution_runs_status_epoch.d.ts +6 -11
- package/dist/db/migrations/152_org_settings_cache_upload_settle_timeout.d.ts +5 -8
- package/dist/db/migrations/157_host_roster_identity_source.d.ts +9 -13
- package/dist/db/migrations/159_org_id_widen_to_source_width.d.ts +5 -0
- package/dist/db/migrator.d.ts +17 -25
- package/dist/db/notify-listener.d.ts +8 -11
- package/dist/db/repos/dashboard-encryption-keys-repo.d.ts +9 -10
- package/dist/db/synthetic-job-ids.d.ts +6 -8
- package/dist/db/types.d.ts +531 -1058
- package/dist/deployment/deployment-identity.d.ts +42 -60
- package/dist/diagnostics/bundle-reader.d.ts +3 -15
- package/dist/diagnostics/bundle-writer.d.ts +5 -12
- package/dist/diagnostics/checks/agents.d.ts +1 -7
- package/dist/diagnostics/checks/backup.d.ts +7 -10
- package/dist/diagnostics/checks/certs.d.ts +1 -6
- package/dist/diagnostics/checks/config.d.ts +2 -5
- package/dist/diagnostics/checks/db.d.ts +1 -1
- package/dist/diagnostics/checks/disk.d.ts +1 -6
- package/dist/diagnostics/checks/firecracker-network.d.ts +3 -3
- package/dist/diagnostics/checks/index.d.ts +0 -6
- package/dist/diagnostics/checks/retention.d.ts +3 -6
- package/dist/diagnostics/checks/scaler.d.ts +3 -7
- package/dist/diagnostics/checks/ws.d.ts +1 -6
- package/dist/diagnostics/fleet-collector.d.ts +11 -14
- package/dist/diagnostics/fleet-constants.d.ts +1 -3
- package/dist/diagnostics/fleet-selection.d.ts +3 -7
- package/dist/diagnostics/fleet-topology.d.ts +2 -6
- package/dist/diagnostics/fleet-wiring.d.ts +6 -12
- package/dist/diagnostics/index.d.ts +0 -5
- package/dist/diagnostics/runner.d.ts +4 -8
- package/dist/diagnostics/types.d.ts +10 -21
- package/dist/drain/drain-controller.d.ts +16 -22
- package/dist/entry-helpers.d.ts +0 -2
- package/dist/events/circuit-breaker.d.ts +0 -3
- package/dist/events/event-router.d.ts +1 -2
- package/dist/events/event-store.d.ts +0 -9
- package/dist/events/trust-store.d.ts +0 -3
- package/dist/events/types.d.ts +0 -9
- package/dist/firecracker/host-network.d.ts +24 -30
- package/dist/firecracker/net-interfaces.d.ts +4 -7
- package/dist/helpers/rate-limiter.d.ts +0 -1
- package/dist/index.js +251 -486
- package/dist/metrics/agent-metrics-aggregator.d.ts +24 -67
- package/dist/metrics/metrics-reporter.d.ts +4 -13
- package/dist/metrics/prometheus.d.ts +47 -81
- package/dist/orchestrator-core.d.ts +3 -7
- package/dist/pipeline/content-filter.d.ts +20 -30
- package/dist/pipeline/cross-source-tally.d.ts +7 -8
- package/dist/pipeline/decorating-secret-resolver.d.ts +4 -8
- package/dist/pipeline/degraded-reason.d.ts +3 -4
- package/dist/pipeline/direct-ingress-deps.d.ts +15 -24
- package/dist/pipeline/dispatch-matched-workflow.d.ts +289 -525
- package/dist/pipeline/flatten-lock-steps.d.ts +4 -6
- package/dist/pipeline/global-dispatch-identity.d.ts +5 -10
- package/dist/pipeline/global-eval-round.d.ts +65 -151
- package/dist/pipeline/global-round-hold.d.ts +5 -7
- package/dist/pipeline/held-context-data.d.ts +5 -8
- package/dist/pipeline/held-round-registrations.d.ts +8 -11
- package/dist/pipeline/install-gate.d.ts +15 -25
- package/dist/pipeline/install-secrets-resolver.d.ts +24 -34
- package/dist/pipeline/internal-event-pipeline.d.ts +45 -62
- package/dist/pipeline/invoke-gate.d.ts +27 -40
- package/dist/pipeline/job-contexts.d.ts +19 -36
- package/dist/pipeline/manual-schedule.d.ts +7 -13
- package/dist/pipeline/needs-scheduler.d.ts +29 -46
- package/dist/pipeline/pending-workflow-context.d.ts +14 -28
- package/dist/pipeline/process-webhook.d.ts +88 -123
- package/dist/pipeline/processor.d.ts +209 -355
- package/dist/pipeline/remote-source-store.d.ts +5 -8
- package/dist/pipeline/request-idempotency.d.ts +10 -13
- package/dist/pipeline/rerun-global.d.ts +6 -7
- package/dist/pipeline/rerun-inputs.d.ts +23 -37
- package/dist/pipeline/rerun.d.ts +34 -57
- package/dist/pipeline/resolve-sandbox-grant.d.ts +5 -10
- package/dist/pipeline/resume-workflow.d.ts +28 -34
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +19 -30
- package/dist/pipeline/run-event-context.d.ts +16 -21
- package/dist/pipeline/sandbox-allowlist-reader.d.ts +7 -11
- package/dist/pipeline/security-hold-check.d.ts +56 -80
- package/dist/pipeline/test-pipeline.d.ts +21 -46
- package/dist/pipeline/unbound-job-context.d.ts +6 -9
- package/dist/pipeline/undispatched-hold-checks.d.ts +4 -6
- package/dist/pipeline/wave-scheduler.d.ts +15 -22
- package/dist/pipeline/webhook-payload-store.d.ts +3 -10
- package/dist/pipeline/workflow-lock-at-sha.d.ts +14 -20
- package/dist/policy/dashboard-write-policy.d.ts +1 -1
- package/dist/provider-registry.d.ts +0 -19
- package/dist/providers/generic/index.d.ts +0 -2
- package/dist/providers/generic/normalizer.d.ts +0 -1
- package/dist/providers/generic/verification.d.ts +0 -8
- package/dist/providers/github/auth.d.ts +0 -1
- package/dist/providers/github/changed-files.d.ts +2 -1
- package/dist/providers/github/file-contents.d.ts +0 -4
- package/dist/providers/github/lock-file.d.ts +0 -4
- package/dist/providers/github/normalizer.d.ts +1 -9
- package/dist/providers/local/index.d.ts +1 -2
- package/dist/providers/local/lock-file-fetcher.d.ts +0 -4
- package/dist/providers/local/normalizer.d.ts +0 -10
- package/dist/providers/local/repo-url-builder.d.ts +0 -2
- package/dist/providers/universal-git/clone-token.d.ts +0 -2
- package/dist/providers/universal-git/lock-file.d.ts +0 -2
- package/dist/providers/universal-git/normalizer.d.ts +0 -2
- package/dist/queue/bootstrap.d.ts +3 -15
- package/dist/queue/cleanup.d.ts +29 -50
- package/dist/queue/depth-refresher.d.ts +21 -39
- package/dist/queue/job-queue.d.ts +112 -163
- package/dist/queue/retention.d.ts +23 -36
- package/dist/queue/scheduled-job.d.ts +17 -39
- package/dist/queue/terminalize-unroutable.d.ts +21 -18
- package/dist/queue/unroutable-probe.d.ts +15 -22
- package/dist/registration/extractor.d.ts +0 -6
- package/dist/registration/registration-index.d.ts +0 -6
- package/dist/registration/registration-store.d.ts +0 -9
- package/dist/reporting/agent-failure-category.d.ts +3 -5
- package/dist/reporting/check-run-reporter.d.ts +142 -316
- package/dist/reporting/check-run-summary.d.ts +16 -35
- package/dist/reporting/check-run-tracking-store.d.ts +54 -107
- package/dist/reporting/execution-tracker.d.ts +356 -526
- package/dist/reporting/fs-log-storage.d.ts +31 -33
- package/dist/reporting/job-check-run-completion.d.ts +14 -22
- package/dist/reporting/log-chunk-sink.d.ts +11 -16
- package/dist/reporting/log-pull-handler.d.ts +4 -13
- package/dist/reporting/log-retention.d.ts +3 -4
- package/dist/reporting/log-storage.d.ts +24 -52
- package/dist/reporting/log-writer.d.ts +28 -56
- package/dist/reporting/peer-log-normalize.d.ts +4 -8
- package/dist/reporting/run-aggregator.d.ts +6 -11
- package/dist/reporting/run-summary-fields.d.ts +5 -8
- package/dist/reporting/s3-log-storage.d.ts +31 -50
- package/dist/reporting/step-display-order.d.ts +11 -21
- package/dist/reporting/step-log-buffer.d.ts +7 -28
- package/dist/reporting/step-log-reader.d.ts +11 -19
- package/dist/routes/admin-access-log.d.ts +4 -6
- package/dist/routes/admin-auth.d.ts +19 -38
- package/dist/routes/admin-backends.d.ts +3 -14
- package/dist/routes/admin-cluster-name.d.ts +13 -21
- package/dist/routes/admin-cluster-settings.d.ts +6 -9
- package/dist/routes/admin-config.d.ts +7 -19
- package/dist/routes/admin-contexts.d.ts +6 -13
- package/dist/routes/admin-db.d.ts +5 -10
- package/dist/routes/admin-event-dlq.d.ts +3 -5
- package/dist/routes/admin-event-inspect.d.ts +3 -4
- package/dist/routes/admin-event-log.d.ts +3 -7
- package/dist/routes/admin-events.d.ts +16 -30
- package/dist/routes/admin-held-runs.d.ts +52 -76
- package/dist/routes/admin-maintenance.d.ts +3 -6
- package/dist/routes/admin-orchestrator-drain.d.ts +0 -2
- package/dist/routes/admin-org-settings.d.ts +8 -14
- package/dist/routes/admin-peer-forget.d.ts +0 -1
- package/dist/routes/admin-queue-execution.d.ts +3 -3
- package/dist/routes/admin-registrations.d.ts +3 -12
- package/dist/routes/admin-runs.d.ts +16 -29
- package/dist/routes/admin-scaler-orphans.d.ts +4 -4
- package/dist/routes/admin-scaler-reload.d.ts +5 -8
- package/dist/routes/admin-scheduled-jobs.d.ts +3 -4
- package/dist/routes/admin-sources.d.ts +20 -29
- package/dist/routes/admin-trust-policy.d.ts +31 -48
- package/dist/routes/admin.d.ts +67 -94
- package/dist/routes/caller-identity.d.ts +45 -0
- package/dist/routes/capabilities.d.ts +8 -13
- package/dist/routes/diagnostics.d.ts +2 -10
- package/dist/routes/fleet.d.ts +1 -2
- package/dist/routes/github-webhook.d.ts +2 -5
- package/dist/routes/health.d.ts +6 -19
- package/dist/routes/provenance-oidc.d.ts +3 -3
- package/dist/routes/test-direct.d.ts +46 -0
- package/dist/routes/uploads.d.ts +14 -19
- package/dist/routes/webhooks.d.ts +2 -24
- package/dist/scaler/agent-binary-location.d.ts +77 -0
- package/dist/scaler/agent-fit.d.ts +2 -2
- package/dist/scaler/backend-factory.d.ts +10 -18
- package/dist/scaler/bare-metal-backend.d.ts +28 -45
- package/dist/scaler/claim-store.d.ts +20 -43
- package/dist/scaler/config.d.ts +10 -37
- package/dist/scaler/container-backend.d.ts +22 -35
- package/dist/scaler/container-hostconfig.d.ts +10 -16
- package/dist/scaler/container-routing.d.ts +10 -13
- package/dist/scaler/disk-guard.d.ts +2 -3
- package/dist/scaler/event-backend.d.ts +33 -58
- package/dist/scaler/event-provision-reaper.d.ts +61 -84
- package/dist/scaler/failure-tracker.d.ts +6 -13
- package/dist/scaler/file-tail.d.ts +6 -19
- package/dist/scaler/firecracker-api.d.ts +19 -63
- package/dist/scaler/firecracker-backend.d.ts +142 -258
- package/dist/scaler/host-access.d.ts +7 -10
- package/dist/scaler/index.d.ts +3 -6
- package/dist/scaler/ip-allocator.d.ts +30 -82
- package/dist/scaler/label-matcher.d.ts +14 -35
- package/dist/scaler/log-forwarder.d.ts +6 -25
- package/dist/scaler/machine-ledger.d.ts +39 -86
- package/dist/scaler/manager.d.ts +517 -787
- package/dist/scaler/pending-scale-sweeper.d.ts +8 -11
- package/dist/scaler/reap-orphans.d.ts +8 -12
- package/dist/scaler/resolve-container-auth.d.ts +12 -21
- package/dist/scaler/scaler-events.d.ts +2 -5
- package/dist/scaler/scaler-state-store.d.ts +127 -189
- package/dist/scaler/spawn-errors.d.ts +1 -4
- package/dist/scaler/startup-orphan-sweep.d.ts +3 -4
- package/dist/scaler/startup-tools-check.d.ts +21 -0
- package/dist/scaler/types.d.ts +142 -255
- package/dist/scaler/vm-job-bindings.d.ts +5 -9
- package/dist/scaler/warm-pool.d.ts +46 -93
- package/dist/secrets/audit-logger.d.ts +0 -3
- package/dist/secrets/backend-health.d.ts +0 -9
- package/dist/secrets/backend-registry.d.ts +0 -9
- package/dist/secrets/cleanup.d.ts +0 -1
- package/dist/secrets/config.d.ts +0 -3
- package/dist/secrets/ephemeral-keys.d.ts +0 -3
- package/dist/secrets/pg-secret-store.d.ts +0 -3
- package/dist/secrets/rbac.d.ts +5 -7
- package/dist/secrets/routing-key-scope.d.ts +1 -0
- package/dist/secrets/secret-output-store.d.ts +0 -6
- package/dist/secrets/secret-resolver.d.ts +2 -6
- package/dist/secrets/token-manager.d.ts +1 -4
- package/dist/secrets/vault-secret-store.d.ts +0 -13
- package/dist/security/comment-handler.d.ts +0 -2
- package/dist/security/workflow-diff.d.ts +1 -1
- package/dist/server.js +49004 -53782
- package/dist/sources/source-manager.d.ts +0 -3
- package/dist/sources/source-store.d.ts +0 -9
- package/dist/sources/source-validator.d.ts +0 -3
- package/dist/stale-detector/all-terminal-runs.d.ts +8 -11
- package/dist/stale-detector/gate-deadline-detector.d.ts +14 -19
- package/dist/stale-detector/reboot-deadline-sweep.d.ts +7 -11
- package/dist/stale-detector/stale-dispatch-candidates.d.ts +13 -20
- package/dist/stale-detector/stale-run-detector.d.ts +74 -121
- package/dist/stale-detector/workflow-deadline-detector.d.ts +15 -43
- package/dist/standalone.js +9342 -13077
- package/dist/storage/blob-routes.d.ts +10 -19
- package/dist/storage/filesystem.d.ts +19 -29
- package/dist/storage/index.d.ts +2 -10
- package/dist/storage/key-discriminator.d.ts +8 -10
- package/dist/storage/loopback-guard.d.ts +27 -45
- package/dist/storage/s3.d.ts +26 -53
- package/dist/storage/sign-url.d.ts +7 -22
- package/dist/storage/types.d.ts +46 -72
- package/dist/webhook/event-log.d.ts +0 -2
- package/dist/webhook/generic-sources.d.ts +0 -21
- package/dist/webhook/ingest-admission.d.ts +0 -1
- package/dist/webhook/relay-buffer.d.ts +2 -2
- package/dist/webhook/verify-inbound.d.ts +0 -1
- package/dist/worker/in-memory-execution-tracker.d.ts +9 -31
- package/dist/worker/in-memory-job-queue.d.ts +32 -72
- package/dist/worker/peer-outbox.d.ts +5 -8
- package/dist/worker/reroute-dispatch.d.ts +4 -6
- package/dist/worker/static-agent-token-store.d.ts +6 -27
- package/dist/worker/worker-outbox-relay.d.ts +2 -2
- package/dist/worker/worker-status.d.ts +3 -20
- package/dist/ws/agent-api-registry.d.ts +8 -27
- package/dist/ws/agent-handler.d.ts +63 -110
- package/dist/ws/agent-heartbeat.d.ts +5 -12
- package/dist/ws/bringup-api.d.ts +31 -38
- package/dist/ws/dashboard-backends-handler.d.ts +12 -22
- package/dist/ws/dashboard-context-handler.d.ts +37 -80
- package/dist/ws/dashboard-diagnostics-handler.d.ts +8 -25
- package/dist/ws/dashboard-dispatch-guard.d.ts +3 -4
- package/dist/ws/dashboard-fleet-handler.d.ts +9 -13
- package/dist/ws/dashboard-fleet-write-handler.d.ts +7 -21
- package/dist/ws/dashboard-global-workflows-handler.d.ts +22 -44
- package/dist/ws/dashboard-registrations-handler.d.ts +10 -27
- package/dist/ws/dashboard-run-state-handler.d.ts +10 -13
- package/dist/ws/event-buffer.d.ts +3 -6
- package/dist/ws/execution-status-frame.d.ts +7 -10
- package/dist/ws/failure-messages.d.ts +9 -14
- package/dist/ws/fleet-agent-collector.d.ts +6 -12
- package/dist/ws/fleet-runs-on-all.d.ts +1 -6
- package/dist/ws/git-credential-relay.d.ts +12 -20
- package/dist/ws/inventory-api.d.ts +4 -5
- package/dist/ws/observer-registry.d.ts +14 -59
- package/dist/ws/ownership-gate.d.ts +1 -1
- package/dist/ws/platform-client.d.ts +21 -30
- package/dist/ws/replay-chunker.d.ts +7 -10
- package/dist/ws/server-options.d.ts +5 -9
- package/dist/ws/test-relay-handlers.d.ts +46 -63
- package/installer-image-digests.json +3 -3
- package/package.json +6 -5
- package/sbom.spdx.json +53 -53
- package/dist/__test-helpers__/fake-scaler-state-store.d.ts +0 -24
- package/dist/__test-helpers__/mock-db-query.d.ts +0 -129
- package/dist/__test-helpers__/mock-db.d.ts +0 -134
- package/dist/__test-helpers__/mock-ws.d.ts +0 -13
- package/dist/__test-helpers__/peer-mutual-auth.d.ts +0 -42
- package/dist/__test-helpers__/scratch-db.d.ts +0 -16
- package/dist/__test-helpers__/test-db.d.ts +0 -34
- package/dist/pipeline/dispatch-matched-workflow.test-helpers.d.ts +0 -121
- package/dist/scaler/container-spawn.d.ts +0 -28
package/dist/index.js
CHANGED
|
@@ -22,17 +22,13 @@ import { exec } from "node:child_process";
|
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/storage/s3.ts
|
|
24
24
|
/**
|
|
25
|
-
* S3-backed cache storage.
|
|
25
|
+
* S3-backed cache storage; AWS SDK v3 modules load only when S3 storage is configured.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
* Uses AWS SDK v3 modular imports -- only loaded when S3 storage is configured.
|
|
29
|
-
*
|
|
30
|
-
* Metadata storage:
|
|
27
|
+
* TTL metadata headers:
|
|
31
28
|
* x-amz-meta-created-at -- ISO timestamp of creation
|
|
32
29
|
* x-amz-meta-last-accessed-at -- ISO timestamp of last access
|
|
33
30
|
*
|
|
34
31
|
* TTL is enforced lazily: expired items are deleted on access.
|
|
35
|
-
* Pre-signed URLs are generated for getUrl() with 15-minute expiry.
|
|
36
32
|
*/
|
|
37
33
|
const logger$5 = createLogger({ prefix: "s3-cache" });
|
|
38
34
|
/** Pre-signed download URL expiry in seconds (15 minutes) */
|
|
@@ -41,17 +37,14 @@ const PRESIGNED_URL_EXPIRY_SECONDS = 900;
|
|
|
41
37
|
const UPLOAD_URL_EXPIRY_SECONDS = 1800;
|
|
42
38
|
var S3CacheStorage = class {
|
|
43
39
|
client;
|
|
44
|
-
/**
|
|
40
|
+
/** Pre-signing client: externalEndpoint when configured. */
|
|
45
41
|
presignClient;
|
|
46
|
-
/**
|
|
42
|
+
/** Client for the developer upload URL: uploadEndpoint, else the presign client. */
|
|
47
43
|
uploadPresignClient;
|
|
48
44
|
bucket;
|
|
49
45
|
prefix;
|
|
50
46
|
ttlMs;
|
|
51
|
-
/**
|
|
52
|
-
* False once the backend answered a conditional self-copy with
|
|
53
|
-
* `501 NotImplemented`. From then on `updateMeta` copies unconditionally.
|
|
54
|
-
*/
|
|
47
|
+
/** False once the backend answered a conditional self-copy with `501 NotImplemented`. */
|
|
55
48
|
conditionalCopySupported = true;
|
|
56
49
|
constructor(options) {
|
|
57
50
|
this.bucket = options.bucket;
|
|
@@ -63,13 +56,11 @@ var S3CacheStorage = class {
|
|
|
63
56
|
endpoint: options.externalEndpoint
|
|
64
57
|
});
|
|
65
58
|
else this.presignClient = this.client;
|
|
66
|
-
|
|
59
|
+
this.uploadPresignClient = options.uploadEndpoint ? createS3Client({
|
|
67
60
|
...options,
|
|
68
61
|
endpoint: options.uploadEndpoint
|
|
69
|
-
});
|
|
70
|
-
else this.uploadPresignClient = this.client;
|
|
62
|
+
}) : this.presignClient;
|
|
71
63
|
}
|
|
72
|
-
/** Build the full S3 object key from a cache key. */
|
|
73
64
|
objectKey(key) {
|
|
74
65
|
return `${this.prefix}${key}`;
|
|
75
66
|
}
|
|
@@ -79,17 +70,11 @@ var S3CacheStorage = class {
|
|
|
79
70
|
const lastAccessed = new Date(meta.lastAccessedAt).getTime();
|
|
80
71
|
return Date.now() - lastAccessed > ttl;
|
|
81
72
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Read metadata from an S3 object's custom headers.
|
|
84
|
-
* Returns null if the object doesn't exist (NoSuchKey/NotFound).
|
|
85
|
-
*/
|
|
73
|
+
/** Null when the object does not exist (NoSuchKey/NotFound). */
|
|
86
74
|
async readMeta(key) {
|
|
87
75
|
return (await this.readHead(key))?.meta ?? null;
|
|
88
76
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Read an object's TTL metadata together with the ETag of the version it
|
|
91
|
-
* belongs to. Returns null if the object doesn't exist (NoSuchKey/NotFound).
|
|
92
|
-
*/
|
|
77
|
+
/** TTL metadata plus the ETag of its version; null when the object does not exist. */
|
|
93
78
|
async readHead(key) {
|
|
94
79
|
try {
|
|
95
80
|
const head = await this.client.send(new HeadObjectCommand({
|
|
@@ -112,25 +97,16 @@ var S3CacheStorage = class {
|
|
|
112
97
|
}
|
|
113
98
|
}
|
|
114
99
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* read. A self-copy is a whole-object write: without the condition, one that
|
|
120
|
-
* races a `put()` of the same key can land after it and write the older body
|
|
121
|
-
* back. Cache pointers are rewritten in place while builds publish them, so a
|
|
122
|
-
* touch-on-read racing a publish reverted the pointer to a tarball that no
|
|
123
|
-
* longer exists. `CopySourceIfMatch` pins the bytes copied to that version and
|
|
124
|
-
* `IfMatch` refuses the write unless the key still holds it.
|
|
100
|
+
* Updates metadata by self-copy, conditional on `etag` (the version the caller read). A
|
|
101
|
+
* self-copy is a whole-object write: unconditioned, one racing a `put()` of the key can write
|
|
102
|
+
* the older body back, reverting a cache pointer to a deleted tarball. `CopySourceIfMatch`
|
|
103
|
+
* pins the copied bytes; `IfMatch` refuses the write unless the key still holds that version.
|
|
125
104
|
*
|
|
126
|
-
* Returns false when the object was replaced since the read (`412`)
|
|
127
|
-
*
|
|
128
|
-
* write stamps metadata of its own, so there is nothing left to refresh.
|
|
105
|
+
* Returns false when the object was replaced since the read (`412`) or is being replaced
|
|
106
|
+
* (`409 ConditionalRequestConflict`): the newer write stamps its own metadata.
|
|
129
107
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* condition, and later copies skip it, as on a backend that ignores the
|
|
133
|
-
* headers.
|
|
108
|
+
* A backend without conditional copies answers `501 NotImplemented`; the copy reruns
|
|
109
|
+
* unconditionally, and later copies skip the condition.
|
|
134
110
|
*/
|
|
135
111
|
async updateMeta(key, meta, etag) {
|
|
136
112
|
const condition = this.conditionalCopySupported ? etag : void 0;
|
|
@@ -165,15 +141,15 @@ var S3CacheStorage = class {
|
|
|
165
141
|
}
|
|
166
142
|
}));
|
|
167
143
|
}
|
|
168
|
-
/**
|
|
144
|
+
/** A failed conditional request (PreconditionFailed, 412). */
|
|
169
145
|
isPreconditionFailedError(err) {
|
|
170
146
|
return this.hasErrorShape(err, "PreconditionFailed", 412);
|
|
171
147
|
}
|
|
172
|
-
/**
|
|
148
|
+
/** A conditional write that raced another write (ConditionalRequestConflict, 409). */
|
|
173
149
|
isConditionalConflictError(err) {
|
|
174
150
|
return this.hasErrorShape(err, "ConditionalRequestConflict", 409);
|
|
175
151
|
}
|
|
176
|
-
/**
|
|
152
|
+
/** A request the backend does not implement (NotImplemented, 501). */
|
|
177
153
|
isNotImplementedError(err) {
|
|
178
154
|
return this.hasErrorShape(err, "NotImplemented", 501);
|
|
179
155
|
}
|
|
@@ -182,7 +158,7 @@ var S3CacheStorage = class {
|
|
|
182
158
|
if (err.name === name) return true;
|
|
183
159
|
return err.$metadata?.httpStatusCode === status;
|
|
184
160
|
}
|
|
185
|
-
/**
|
|
161
|
+
/** NoSuchKey, NotFound or 404. */
|
|
186
162
|
isNotFoundError(err) {
|
|
187
163
|
if (typeof err !== "object" || err === null) return false;
|
|
188
164
|
const name = err.name;
|
|
@@ -360,30 +336,21 @@ var S3CacheStorage = class {
|
|
|
360
336
|
//#endregion
|
|
361
337
|
//#region src/storage/sign-url.ts
|
|
362
338
|
/**
|
|
363
|
-
* HMAC-
|
|
364
|
-
*
|
|
365
|
-
* Pre-signed S3 URLs are not available for filesystem storage, so the
|
|
366
|
-
* filesystem backend mints HMAC-SHA256 tokens that the orchestrator's HTTP
|
|
367
|
-
* blob route verifies before serving / writing a file. Token shape:
|
|
339
|
+
* HMAC-SHA256 URL tokens for the filesystem cache backend (which has no pre-signed S3 URLs);
|
|
340
|
+
* the blob route verifies them before serving / writing a file. Token shape:
|
|
368
341
|
*
|
|
369
342
|
* `${expiresUnixMs}.${hexSig}`
|
|
370
343
|
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* for the lifetime of the process). Tokens become invalid on orchestrator
|
|
374
|
-
* restart — fine for the filesystem backend's single-host / test-sandbox use
|
|
375
|
-
* case.
|
|
344
|
+
* Signs `${method}:${key}:${expiresUnixMs}` with a per-process secret (random 32 bytes at
|
|
345
|
+
* boot), so tokens die on orchestrator restart — fine for single-host / test-sandbox use.
|
|
376
346
|
*/
|
|
377
|
-
/** Default URL lifetime: 1 hour
|
|
347
|
+
/** Default URL lifetime: 1 hour. */
|
|
378
348
|
const DEFAULT_SIGN_URL_TTL_MS = 36e5;
|
|
379
349
|
function computeSig(secret, method, key, expiresMs) {
|
|
380
350
|
const hmac = createHmac("sha256", secret);
|
|
381
351
|
hmac.update(`${method}:${key}:${expiresMs}`);
|
|
382
352
|
return hmac.digest("hex");
|
|
383
353
|
}
|
|
384
|
-
/**
|
|
385
|
-
* Mint a signed token for the given (method, key, expiry) triple.
|
|
386
|
-
*/
|
|
387
354
|
function signToken(secret, method, key, ttlMs = DEFAULT_SIGN_URL_TTL_MS) {
|
|
388
355
|
const expiresMs = Date.now() + ttlMs;
|
|
389
356
|
return {
|
|
@@ -394,19 +361,15 @@ function signToken(secret, method, key, ttlMs = DEFAULT_SIGN_URL_TTL_MS) {
|
|
|
394
361
|
//#endregion
|
|
395
362
|
//#region src/storage/filesystem.ts
|
|
396
363
|
/**
|
|
397
|
-
* Filesystem-backed
|
|
398
|
-
*
|
|
399
|
-
* Mirrors the `CacheStorage` interface that `S3CacheStorage` provides, but
|
|
400
|
-
* stores blobs as files under a configured base directory. Pre-signed URLs
|
|
401
|
-
* are replaced by HMAC-signed `http://<orchUrl>/api/v1/cache/blob/...` URLs
|
|
402
|
-
* — see `sign-url.ts` for the token mechanics and `app.ts` for the route.
|
|
364
|
+
* Filesystem-backed `CacheStorage`, the counterpart of `S3CacheStorage`, for
|
|
365
|
+
* single-host deployments and test sandboxes. Production should still use S3.
|
|
403
366
|
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
367
|
+
* Pre-signed URLs become HMAC-signed `http://<orchUrl>/api/v1/cache/blob/...`
|
|
368
|
+
* URLs (token mechanics: `sign-url.ts`; route: `app.ts`).
|
|
406
369
|
*
|
|
407
|
-
* Metadata
|
|
408
|
-
*
|
|
409
|
-
*
|
|
370
|
+
* Metadata sits in a sibling `<key>.meta.json`, so the data file is
|
|
371
|
+
* byte-identical to what the agent receives over HTTP. TTL is enforced lazily
|
|
372
|
+
* on access, as in the S3 backend.
|
|
410
373
|
*/
|
|
411
374
|
const DEFAULT_ROUTE_PREFIX = "/api/v1/cache/blob/";
|
|
412
375
|
var FilesystemCacheStorage = class {
|
|
@@ -423,9 +386,9 @@ var FilesystemCacheStorage = class {
|
|
|
423
386
|
this.routePrefix = (options.routePrefix ?? DEFAULT_ROUTE_PREFIX).replace(/\/?$/, "/");
|
|
424
387
|
}
|
|
425
388
|
/**
|
|
426
|
-
* Map a cache key to its on-disk path
|
|
427
|
-
*
|
|
428
|
-
*
|
|
389
|
+
* Map a cache key to its on-disk path (`/` becomes a directory separator).
|
|
390
|
+
* Rejects `..` segments and NUL bytes against path traversal, as the route
|
|
391
|
+
* layer also does.
|
|
429
392
|
*/
|
|
430
393
|
resolvePath(key) {
|
|
431
394
|
if (key.includes("\0") || key.split("/").some((seg) => seg === ".." || seg === "")) throw new Error(`Invalid cache key: ${JSON.stringify(key)}`);
|
|
@@ -535,19 +498,14 @@ var FilesystemCacheStorage = class {
|
|
|
535
498
|
});
|
|
536
499
|
}
|
|
537
500
|
/**
|
|
538
|
-
* List cache keys under a prefix
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
* `restoreKeys` fallback lists on `${prefix}${seg(restoreKey)}`, a partial
|
|
542
|
-
* object name. Two shapes, and both must work the way the S3 backend does:
|
|
501
|
+
* List cache keys under a **key prefix**, which need not be a directory:
|
|
502
|
+
* `UserCache`'s `restoreKeys` fallback lists on `${prefix}${seg(restoreKey)}`,
|
|
503
|
+
* a partial object name. Both shapes must behave as on S3:
|
|
543
504
|
*
|
|
544
505
|
* - trailing `/` — a directory. Walk it directly; this is the hot path.
|
|
545
|
-
* - no trailing `/` — a partial name. Walk the parent
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
* Reading the prefix as a directory in the second case is what made the
|
|
549
|
-
* `restoreKeys` fallback silently never hit here (`ENOENT` → `[]`) or throw
|
|
550
|
-
* (`ENOTDIR`, when a real object carried that exact name).
|
|
506
|
+
* - no trailing `/` — a partial name. Walk the parent and filter by the
|
|
507
|
+
* prefix, which may name a file or nothing at all. Reading it as a
|
|
508
|
+
* directory made `restoreKeys` never hit (`ENOENT`) or throw (`ENOTDIR`).
|
|
551
509
|
*/
|
|
552
510
|
async list(subPrefix) {
|
|
553
511
|
const isDirPrefix = subPrefix.endsWith("/");
|
|
@@ -588,10 +546,9 @@ var FilesystemCacheStorage = class {
|
|
|
588
546
|
});
|
|
589
547
|
}
|
|
590
548
|
/**
|
|
591
|
-
* Recursively collect data files under `dir
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
* `list()` only surfaces real cache objects.
|
|
549
|
+
* Recursively collect data files under `dir` as `basePath`-relative keys with
|
|
550
|
+
* their mtime, skipping `*.meta.json` sidecars and `*.tmp-*` atomic-write
|
|
551
|
+
* temp files so `list()` surfaces only real cache objects.
|
|
595
552
|
*/
|
|
596
553
|
async walkDataFiles(dir, keyPrefix) {
|
|
597
554
|
const out = [];
|
|
@@ -628,9 +585,8 @@ function isNotFoundFsError(err) {
|
|
|
628
585
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
629
586
|
}
|
|
630
587
|
/**
|
|
631
|
-
* Write
|
|
632
|
-
* partial
|
|
633
|
-
* randomised so concurrent writers don't collide on the same temp name.
|
|
588
|
+
* Write atomically (temp sibling, fsync, rename) so a crash mid-write leaves no
|
|
589
|
+
* partial file. The random temp suffix keeps concurrent writers apart.
|
|
634
590
|
*/
|
|
635
591
|
async function writeFileAtomic(path, data) {
|
|
636
592
|
const tmp = `${path}.tmp-${randomBytes(6).toString("hex")}`;
|
|
@@ -645,9 +601,6 @@ async function writeFileAtomic(path, data) {
|
|
|
645
601
|
}
|
|
646
602
|
//#endregion
|
|
647
603
|
//#region src/storage/index.ts
|
|
648
|
-
/**
|
|
649
|
-
* Create a cache storage backend from configuration.
|
|
650
|
-
*/
|
|
651
604
|
function createCacheStorage(config) {
|
|
652
605
|
if (config.type === "s3") return new S3CacheStorage({
|
|
653
606
|
bucket: config.bucket,
|
|
@@ -670,61 +623,44 @@ function createCacheStorage(config) {
|
|
|
670
623
|
//#endregion
|
|
671
624
|
//#region src/cache/source-cache.ts
|
|
672
625
|
/**
|
|
673
|
-
* Source-tarball cache
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
* workflow `contentHash` to that hash. Tarballs are platform-independent: raw
|
|
679
|
-
* TypeScript source is identical across architectures. Refreshes TTL on reads
|
|
680
|
-
* (touch-on-read) so actively used sources stay in cache longer.
|
|
626
|
+
* Source-tarball cache over CacheStorage: the `.kici/` directory minus
|
|
627
|
+
* `node_modules/` (packed by the agent's build job via `source-packer.ts`),
|
|
628
|
+
* stored under the tarball's OWN content hash, with a pointer resolving an
|
|
629
|
+
* org's workflow `contentHash` to it. Tarballs are platform-independent (raw
|
|
630
|
+
* TypeScript source). Reads touch the TTL so active sources stay cached.
|
|
681
631
|
*
|
|
682
632
|
* Keys: `source/v2/{orgId}/{sourceTarDigest}.tar.gz` (immutable) and
|
|
683
|
-
* `source/v2/{orgId}/{contentHash}.hash` (the pointer) — the
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
* **Content addressing** makes a mismatched pair unrepresentable. At
|
|
687
|
-
* `source/{contentHash}.tar.gz` the object's key was derived from something
|
|
688
|
-
* other than its bytes, so nothing a reader fetched could be verified against
|
|
689
|
-
* anything, and the agent restored whatever the signed URL returned.
|
|
633
|
+
* `source/v2/{orgId}/{contentHash}.hash` (the pointer) — the `dep-cache.ts`
|
|
634
|
+
* shape, for the same reasons:
|
|
690
635
|
*
|
|
691
|
-
* **
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
* by;
|
|
698
|
-
* so a monorepo or
|
|
636
|
+
* - **Content addressing** makes a mismatched pair unrepresentable: a key not
|
|
637
|
+
* derived from the bytes lets nothing a reader fetches be verified.
|
|
638
|
+
* - **Org scoping** makes the key a tenant's key. `contentHash` alone is a
|
|
639
|
+
* property of a `.kici/` tree, so two repositories from the same `kici init`
|
|
640
|
+
* template, or a fork and its upstream, would share one object — a
|
|
641
|
+
* cross-tenant read on a multi-org orchestrator. The org is the tenant
|
|
642
|
+
* boundary the user cache scopes by too; within one org a shared key means
|
|
643
|
+
* byte-identical trees, so a monorepo or same-org fork keeps its warm cache.
|
|
699
644
|
*
|
|
700
|
-
*
|
|
701
|
-
* `compileSchemaVersion: 5` lock
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
* Old objects are orphaned. The cache TTL is enforced lazily on access, so
|
|
706
|
-
* nothing here removes them.
|
|
645
|
+
* Retired `source/{contentHash}.tar.gz` objects are **not read**: only a
|
|
646
|
+
* `compileSchemaVersion: 5` lock names one, and its `contentHash` covers the
|
|
647
|
+
* entry file alone rather than the packed tree, so serving it would serve the
|
|
648
|
+
* bug. A v6 lock cannot reach it (the bump moves every hash). The lazy TTL never
|
|
649
|
+
* removes them (docs/operator/orchestrator/storage-layout.md § "Retired layouts").
|
|
707
650
|
*/
|
|
708
651
|
const logger$4 = createLogger({ prefix: "source-cache" });
|
|
709
652
|
/**
|
|
710
|
-
*
|
|
711
|
-
*
|
|
712
|
-
*
|
|
713
|
-
* Naming the object by its own hash means every pair a reader can observe is
|
|
714
|
-
* self-consistent, and the bytes at a given key can never change after a URL is
|
|
715
|
-
* signed for it.
|
|
653
|
+
* Named by its own hash, so every pair a reader observes is self-consistent and
|
|
654
|
+
* the bytes at a key never change after a URL is signed for it.
|
|
716
655
|
*/
|
|
717
656
|
function sourceTarballKey(orgId, sourceTarDigest) {
|
|
718
657
|
return `source/v2/${orgId}/${sourceTarDigest}.tar.gz`;
|
|
719
658
|
}
|
|
720
659
|
/**
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
*
|
|
725
|
-
* pointer wholesale — there is no half-written window — so the worst a racing
|
|
726
|
-
* pair of builders can do is leave whichever pointer landed last, and both
|
|
727
|
-
* tarballs stay valid and fetchable.
|
|
660
|
+
* The pointer from a workflow `contentHash` to its tarball's hash: the one
|
|
661
|
+
* mutable object. A write replaces it wholesale (no half-written window), so
|
|
662
|
+
* racing builders at worst leave the last-landed pointer, and both tarballs
|
|
663
|
+
* stay valid and fetchable.
|
|
728
664
|
*/
|
|
729
665
|
function sourcePointerKey(orgId, contentHash) {
|
|
730
666
|
return `source/v2/${orgId}/${contentHash}.hash`;
|
|
@@ -734,16 +670,13 @@ var SourceCache = class SourceCache {
|
|
|
734
670
|
constructor(options) {
|
|
735
671
|
this.storage = options.storage;
|
|
736
672
|
}
|
|
737
|
-
/**
|
|
673
|
+
/** Resolves the pointer indirection (`getUrlAndDigest` reads it inline to touch it). */
|
|
738
674
|
async resolvePointer(orgId, contentHash) {
|
|
739
675
|
return (await this.storage.get(sourcePointerKey(orgId, contentHash)))?.toString("utf-8").trim() || null;
|
|
740
676
|
}
|
|
741
677
|
/**
|
|
742
|
-
*
|
|
743
|
-
*
|
|
744
|
-
* Both halves must be present: a pointer whose tarball has aged out is a
|
|
745
|
-
* miss, not a hit, or the caller skips a rebuild and dispatches a URL that
|
|
746
|
-
* 404s on the agent.
|
|
678
|
+
* Both halves must exist: a pointer whose tarball aged out is a miss, or the
|
|
679
|
+
* caller skips a rebuild and dispatches a URL that 404s on the agent.
|
|
747
680
|
*/
|
|
748
681
|
async has(orgId, contentHash) {
|
|
749
682
|
const digest = await this.resolvePointer(orgId, contentHash);
|
|
@@ -770,13 +703,9 @@ var SourceCache = class SourceCache {
|
|
|
770
703
|
return data;
|
|
771
704
|
}
|
|
772
705
|
/**
|
|
773
|
-
* A pre-signed download URL and the tarball's own digest.
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
* tarball's key, so the url and the digest returned here are read from a
|
|
777
|
-
* single source of truth and cannot disagree. Returns null on a miss —
|
|
778
|
-
* including a pointer with no tarball behind it, which is unverifiable and so
|
|
779
|
-
* is deliberately not served.
|
|
706
|
+
* A pre-signed download URL and the tarball's own digest. The pointer is
|
|
707
|
+
* resolved FIRST and names both, so url and digest cannot disagree. Null on a
|
|
708
|
+
* miss, including a pointer with no tarball (unverifiable, so not served).
|
|
780
709
|
*/
|
|
781
710
|
async getUrlAndDigest(orgId, contentHash) {
|
|
782
711
|
const pointerKey = sourcePointerKey(orgId, contentHash);
|
|
@@ -803,19 +732,15 @@ var SourceCache = class SourceCache {
|
|
|
803
732
|
return (await this.getUrlAndDigest(orgId, contentHash))?.url ?? null;
|
|
804
733
|
}
|
|
805
734
|
/**
|
|
806
|
-
* A pre-signed upload URL
|
|
807
|
-
*
|
|
808
|
-
* Takes the tarball's OWN digest, not the workflow `contentHash`: the agent
|
|
809
|
-
* computes it before asking for a URL, and the object is named by it.
|
|
735
|
+
* A pre-signed agent-to-storage upload URL, keyed by the tarball's OWN digest
|
|
736
|
+
* (which the agent computes first), not the workflow `contentHash`.
|
|
810
737
|
*/
|
|
811
738
|
async getUploadUrl(orgId, sourceTarDigest) {
|
|
812
739
|
return this.storage.getUploadUrl(sourceTarballKey(orgId, sourceTarDigest));
|
|
813
740
|
}
|
|
814
741
|
/**
|
|
815
|
-
* Publish the pointer
|
|
816
|
-
*
|
|
817
|
-
* publishing before the bytes land would let a reader follow the pointer to a
|
|
818
|
-
* missing object.
|
|
742
|
+
* Publish the pointer to an uploaded tarball. Called only after the agent
|
|
743
|
+
* confirms the upload, or a reader could follow it to a missing object.
|
|
819
744
|
*/
|
|
820
745
|
async publishPointer(orgId, contentHash, sourceTarDigest) {
|
|
821
746
|
await this.storage.put(sourcePointerKey(orgId, contentHash), sourceTarDigest);
|
|
@@ -836,7 +761,6 @@ var SourceCache = class SourceCache {
|
|
|
836
761
|
static computeDigest(data) {
|
|
837
762
|
return sha256(data);
|
|
838
763
|
}
|
|
839
|
-
/** Remove a source tarball and its pointer. */
|
|
840
764
|
async remove(orgId, contentHash) {
|
|
841
765
|
const digest = await this.resolvePointer(orgId, contentHash);
|
|
842
766
|
await this.storage.delete(sourcePointerKey(orgId, contentHash));
|
|
@@ -848,12 +772,9 @@ var SourceCache = class SourceCache {
|
|
|
848
772
|
//#endregion
|
|
849
773
|
//#region src/cache/dep-cache.ts
|
|
850
774
|
/**
|
|
851
|
-
* Dependency
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
* resolving lockfileHash + platform + arch to that hash. Shared CacheStorage
|
|
855
|
-
* backend with SourceCache (same S3 bucket). Refreshes TTL on reads
|
|
856
|
-
* (touch-on-read).
|
|
775
|
+
* Dependency cache over CacheStorage (the same S3 bucket as SourceCache).
|
|
776
|
+
* Tarballs live under their own content hash, with a small pointer resolving
|
|
777
|
+
* lockfileHash + platform + arch to it. Reads refresh TTL (touch-on-read).
|
|
857
778
|
*
|
|
858
779
|
* Keys: `deps/{platform}-{arch}/{depsHash}.tar.gz` (immutable) and
|
|
859
780
|
* `deps/{platform}-{arch}/{lockfileHash}.hash` (the pointer).
|
|
@@ -865,17 +786,14 @@ const DEFAULT_MAX_TARBALL_BYTES = 524288e3;
|
|
|
865
786
|
const DEFAULT_CACHE_TTL_DAYS = 30;
|
|
866
787
|
const MS_PER_DAY = 864e5;
|
|
867
788
|
/**
|
|
868
|
-
* Cache key for a dependency tarball, addressed by
|
|
869
|
-
*
|
|
789
|
+
* Cache key for a dependency tarball, addressed by its OWN content hash:
|
|
790
|
+
* `deps/{platform}-{arch}/{depsHash}.tar.gz`.
|
|
870
791
|
*
|
|
871
|
-
* Content-addressing
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
* verification durably. Naming the object by its own hash means every pair a
|
|
877
|
-
* reader can observe is self-consistent, and the bytes at a given key can never
|
|
878
|
-
* change after a URL is signed for it.
|
|
792
|
+
* Content-addressing makes a mismatched pair unrepresentable. Under a
|
|
793
|
+
* lockfile-derived name, concurrent builders sharing lockfile + platform + arch
|
|
794
|
+
* could leave one's `.tar.gz` and the other's `.hash`, and the reader failed
|
|
795
|
+
* verification durably. Named by its own hash, every observable pair is
|
|
796
|
+
* self-consistent and a key's bytes never change after a URL is signed for it.
|
|
879
797
|
*/
|
|
880
798
|
function depTarballKey(depsHash, platform, arch) {
|
|
881
799
|
return `deps/${platform}-${arch}/${depsHash}.tar.gz`;
|
|
@@ -885,17 +803,14 @@ function depTarballKey(depsHash, platform, arch) {
|
|
|
885
803
|
* tarball built from it: `deps/{platform}-{arch}/{lockfileHash}.hash`, or
|
|
886
804
|
* `…/{lockfileHash}-{siblingsDigest}.hash` when the lock carries one.
|
|
887
805
|
*
|
|
888
|
-
* `siblingsDigest` is
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
892
|
-
* lock with no in-repo sibling carries no digest and keys exactly as before, so
|
|
893
|
-
* existing pointers stay live.
|
|
806
|
+
* `siblingsDigest` is keyed because the tarball carries in-repo `workspace:`
|
|
807
|
+
* siblings WITH their built output, and editing a sibling moves no lock file —
|
|
808
|
+
* a `lockfileHash`-only key restored a stale sibling build over the fresh clone.
|
|
809
|
+
* A lock with no in-repo sibling carries no digest, so existing pointers stay live.
|
|
894
810
|
*
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
* and both tarballs remain valid and fetchable.
|
|
811
|
+
* The one mutable object in the scheme. A write replaces the pointer wholesale
|
|
812
|
+
* (never half-written), so racing builders at worst leave whichever landed
|
|
813
|
+
* last, and both tarballs stay valid and fetchable.
|
|
899
814
|
*/
|
|
900
815
|
function depPointerKey(lockfileHash, platform, arch, siblingsDigest) {
|
|
901
816
|
return `deps/${platform}-${arch}/${siblingsDigest ? `${lockfileHash}-${siblingsDigest}` : lockfileHash}.hash`;
|
|
@@ -912,28 +827,21 @@ var DepCache = class DepCache {
|
|
|
912
827
|
this.clusterSettings = options.clusterSettings;
|
|
913
828
|
}
|
|
914
829
|
/**
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
* its own configured TTL. Passed to read/expiry operations so an operator's
|
|
919
|
-
* `cache_ttl_days` change takes effect on the next lookup.
|
|
830
|
+
* Per-operation TTL override (ms) from `cluster_settings.cache_ttl_days`, so
|
|
831
|
+
* an operator's change takes effect on the next lookup. `undefined` when no
|
|
832
|
+
* reader is wired, so the backend uses its own configured TTL.
|
|
920
833
|
*/
|
|
921
834
|
async resolveTtlMsOverride() {
|
|
922
835
|
if (!this.clusterSettings) return void 0;
|
|
923
836
|
return await this.clusterSettings.getNumber("cache_ttl_days", this.cacheTtlDaysFallback) * MS_PER_DAY;
|
|
924
837
|
}
|
|
925
|
-
/**
|
|
926
|
-
* Resolve a lockfile to the content hash of the tarball built from it, or
|
|
927
|
-
* null when no pointer exists. The single place the indirection is read.
|
|
928
|
-
*/
|
|
838
|
+
/** Lockfile → tarball content hash, or null when no pointer exists. */
|
|
929
839
|
async resolvePointer(lockfileHash, platform, arch, ttlMsOverride, siblingsDigest) {
|
|
930
840
|
return (await this.storage.get(depPointerKey(lockfileHash, platform, arch, siblingsDigest), ttlMsOverride))?.toString("utf-8").trim() || null;
|
|
931
841
|
}
|
|
932
842
|
/**
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
* Both halves must be present: a pointer whose tarball has aged out is a miss,
|
|
936
|
-
* not a hit, or the caller skips a rebuild and dispatches a URL that 404s.
|
|
843
|
+
* Both halves must be present: a pointer whose tarball aged out is a miss, or
|
|
844
|
+
* the caller skips a rebuild and dispatches a URL that 404s.
|
|
937
845
|
*/
|
|
938
846
|
async has(lockfileHash, platform, arch, siblingsDigest) {
|
|
939
847
|
const ttlMsOverride = await this.resolveTtlMsOverride();
|
|
@@ -952,18 +860,13 @@ var DepCache = class DepCache {
|
|
|
952
860
|
});
|
|
953
861
|
return exists;
|
|
954
862
|
}
|
|
955
|
-
/**
|
|
956
|
-
* Get a pre-signed download URL for the dep tarball (for agent delivery).
|
|
957
|
-
* Refreshes TTL on hit.
|
|
958
|
-
*/
|
|
863
|
+
/** Pre-signed download URL for agent delivery. Refreshes TTL on hit. */
|
|
959
864
|
async getUrl(lockfileHash, platform, arch, siblingsDigest) {
|
|
960
865
|
return (await this.getUrlAndHash(lockfileHash, platform, arch, siblingsDigest))?.url ?? null;
|
|
961
866
|
}
|
|
962
867
|
/**
|
|
963
|
-
*
|
|
964
|
-
*
|
|
965
|
-
* Returns null on cache miss — including an entry with no pointer, which is
|
|
966
|
-
* unverifiable and so is deliberately not served.
|
|
868
|
+
* Pre-signed download URL plus tarball content hash; null on a miss,
|
|
869
|
+
* including an entry with no pointer (unverifiable, so never served).
|
|
967
870
|
*/
|
|
968
871
|
async getUrlAndHash(lockfileHash, platform, arch, siblingsDigest) {
|
|
969
872
|
const ttlMsOverride = await this.resolveTtlMsOverride();
|
|
@@ -997,17 +900,13 @@ var DepCache = class DepCache {
|
|
|
997
900
|
hash
|
|
998
901
|
};
|
|
999
902
|
}
|
|
1000
|
-
/**
|
|
1001
|
-
* Get a pre-signed upload URL for direct agent-to-S3 upload.
|
|
1002
|
-
*/
|
|
903
|
+
/** Pre-signed URL for direct agent-to-S3 upload. */
|
|
1003
904
|
async getUploadUrl(depsHash, platform, arch) {
|
|
1004
905
|
return this.storage.getUploadUrl(depTarballKey(depsHash, platform, arch));
|
|
1005
906
|
}
|
|
1006
907
|
/**
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
* Called only after the agent confirms its upload completed. Publishing before
|
|
1010
|
-
* the bytes land would let a reader follow the pointer to a missing object.
|
|
908
|
+
* Make an uploaded tarball discoverable by lockfile. Called only after the
|
|
909
|
+
* agent confirms its upload, or a reader could follow the pointer to nothing.
|
|
1011
910
|
*/
|
|
1012
911
|
async publishPointer(lockfileHash, platform, arch, depsHash, siblingsDigest) {
|
|
1013
912
|
await this.storage.put(depPointerKey(lockfileHash, platform, arch, siblingsDigest), depsHash);
|
|
@@ -1016,10 +915,7 @@ var DepCache = class DepCache {
|
|
|
1016
915
|
arch
|
|
1017
916
|
});
|
|
1018
917
|
}
|
|
1019
|
-
/**
|
|
1020
|
-
* Store a dep tarball in cache.
|
|
1021
|
-
* Throws if tarball exceeds max size (per user decision).
|
|
1022
|
-
*/
|
|
918
|
+
/** Throws when the tarball exceeds the max size. */
|
|
1023
919
|
async store(lockfileHash, platform, arch, tarballData) {
|
|
1024
920
|
const maxTarballBytes = this.clusterSettings ? await this.clusterSettings.getNumber("cache_max_tarball_bytes", this.maxTarballBytes) : this.maxTarballBytes;
|
|
1025
921
|
if (tarballData.length > maxTarballBytes) throw new Error(`Dep tarball exceeds max size: ${tarballData.length} bytes > ${maxTarballBytes} bytes limit`);
|
|
@@ -1034,8 +930,8 @@ var DepCache = class DepCache {
|
|
|
1034
930
|
});
|
|
1035
931
|
}
|
|
1036
932
|
/**
|
|
1037
|
-
* Compute SHA-256 hash of a tarball buffer
|
|
1038
|
-
*
|
|
933
|
+
* Compute SHA-256 hash of a tarball buffer: the `depsHash` that `store()`
|
|
934
|
+
* keys and logs the tarball under.
|
|
1039
935
|
*/
|
|
1040
936
|
static computeHash(data) {
|
|
1041
937
|
return sha256(data);
|
|
@@ -1055,10 +951,9 @@ var DepCache = class DepCache {
|
|
|
1055
951
|
/**
|
|
1056
952
|
* A collision-resistant discriminator for a user-supplied object-key segment.
|
|
1057
953
|
*
|
|
1058
|
-
* Hashes the **exact** name
|
|
1059
|
-
*
|
|
1060
|
-
*
|
|
1061
|
-
* the pre-sanitization name is what keeps each of those pairs two objects.
|
|
954
|
+
* Hashes the **exact** pre-sanitization name: `seg()` maps both `/` and `_` to
|
|
955
|
+
* `_` (`a/b` and `a_b` collapse), and a case-insensitive backend folds `build`
|
|
956
|
+
* onto `Build`, so only the hash keeps each pair two objects.
|
|
1062
957
|
*/
|
|
1063
958
|
function keyDiscriminator(exact) {
|
|
1064
959
|
return createHash("sha256").update(exact, "utf8").digest("hex").slice(0, 32);
|
|
@@ -1069,47 +964,40 @@ function keyDiscriminator(exact) {
|
|
|
1069
964
|
* User-facing cache layer wrapping CacheStorage.
|
|
1070
965
|
*
|
|
1071
966
|
* Namespacing: `cache/<orgId>/<repoId>/<refScope>/<key>-<discriminator>.tar.gz`.
|
|
1072
|
-
* The discriminator is a hash of the exact cache key, so
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1078
|
-
* strictly per org so no tenant can read another tenant's cache.
|
|
967
|
+
* The discriminator is a hash of the exact cache key, so keys differing only by
|
|
968
|
+
* case stay two objects on a case-insensitive store. `refScope` is `shared` for
|
|
969
|
+
* trusted refs (org-shared, default-branch cache) or `iso/<runId>` for
|
|
970
|
+
* untrusted refs. An untrusted ref reads the shared scope as a fallback but can
|
|
971
|
+
* NEVER write it — the GitHub Actions cache-isolation model. Keyed strictly per
|
|
972
|
+
* org so no tenant can read another tenant's cache.
|
|
1079
973
|
*
|
|
1080
|
-
* Saves are immutable (first save under an exact key wins) and atomic
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
974
|
+
* Saves are immutable (first save under an exact key wins) and atomic (upload
|
|
975
|
+
* to `.tmp-<uuid>`, server-side copy to the final key, delete the temp), so a
|
|
976
|
+
* crashed save never leaves a corrupt final entry.
|
|
1083
977
|
*
|
|
1084
|
-
* Eviction: per-org byte quota plus the TTL the backing CacheStorage
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
* hash and size accounting outside the tarball's own (presigned, metadata-less)
|
|
1090
|
-
* upload.
|
|
978
|
+
* Eviction: per-org byte quota plus the TTL the backing CacheStorage enforces
|
|
979
|
+
* lazily on access. A save that pushes the org over quota evicts LRU entries
|
|
980
|
+
* (oldest `lastAccessedAt` from backend metadata) until under quota, logging
|
|
981
|
+
* each. The `.hash` / `.size` sidecars carry the integrity hash and size
|
|
982
|
+
* accounting outside the presigned, metadata-less tarball upload.
|
|
1091
983
|
*/
|
|
1092
984
|
const logger$2 = createLogger({ prefix: "user-cache" });
|
|
1093
985
|
/**
|
|
1094
|
-
* Cluster-wide default quota: 5 GiB
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
986
|
+
* Cluster-wide default quota: 5 GiB, used when an org has no
|
|
987
|
+
* `org_settings.user_cache_quota_bytes` override. Operator-configurable via
|
|
988
|
+
* KICI_USER_CACHE_QUOTA_BYTES.
|
|
1097
989
|
*/
|
|
1098
990
|
const DEFAULT_USER_CACHE_QUOTA_BYTES = 5368709120;
|
|
1099
991
|
/**
|
|
1100
|
-
* Cluster-wide default entry TTL: 7 days
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
992
|
+
* Cluster-wide default entry TTL: 7 days, used when an org has no
|
|
993
|
+
* `org_settings.user_cache_ttl_ms` override. Operator-configurable via
|
|
994
|
+
* KICI_USER_CACHE_TTL_MS.
|
|
1103
995
|
*/
|
|
1104
996
|
const DEFAULT_USER_CACHE_TTL_MS = 6048e5;
|
|
1105
997
|
/** Tarball suffix for committed cache entries. */
|
|
1106
998
|
const TAR_SUFFIX = ".tar.gz";
|
|
1107
|
-
/**
|
|
1108
|
-
* Stem prefix of an in-flight upload, `.tmp-<uuid>.tar.gz`: the presigned PUT
|
|
1109
|
-
* target a save lands on before commit copies it to its final key.
|
|
1110
|
-
*/
|
|
999
|
+
/** Stem of an in-flight upload, `.tmp-<uuid>.tar.gz`, before commit copies it to its final key. */
|
|
1111
1000
|
const TEMP_UPLOAD_STEM = ".tmp-";
|
|
1112
|
-
/** Whether a listed object is an in-flight upload rather than a cache entry. */
|
|
1113
1001
|
function isTempUpload(key) {
|
|
1114
1002
|
return key.slice(key.lastIndexOf("/") + 1).startsWith(TEMP_UPLOAD_STEM);
|
|
1115
1003
|
}
|
|
@@ -1117,36 +1005,28 @@ function isTempUpload(key) {
|
|
|
1117
1005
|
const KEY_DISCRIMINATOR_TAIL = new RegExp(`^(.*)-([0-9a-f]{32})$`);
|
|
1118
1006
|
/**
|
|
1119
1007
|
* Whether a listed object is a committed entry, `<stem>-<discriminator>.tar.gz`.
|
|
1120
|
-
*
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
* `.tmp-<uuid>.tar.gz` upload, which is a partial tarball until commit copies
|
|
1124
|
-
* it to its final key, and an object under the retired un-discriminated layout,
|
|
1125
|
-
* which nothing writes.
|
|
1008
|
+
* The `restoreKeys` prefix scan filters on it, which keeps out an in-flight
|
|
1009
|
+
* `.tmp-` upload (a partial tarball until commit) and an object under the
|
|
1010
|
+
* retired un-discriminated layout, which nothing writes.
|
|
1126
1011
|
*/
|
|
1127
1012
|
function isCommittedEntry(key) {
|
|
1128
1013
|
return key.endsWith(TAR_SUFFIX) && KEY_DISCRIMINATOR_TAIL.test(key.slice(0, -7));
|
|
1129
1014
|
}
|
|
1130
1015
|
/**
|
|
1131
|
-
* Recover the reported cache key from a committed entry's
|
|
1132
|
-
*
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1135
|
-
* ends in a discriminator-shaped tail loses it — that affects only the string
|
|
1136
|
-
* reported back as `matchedKey`, never which object is served.
|
|
1016
|
+
* Recover the reported cache key from a committed entry's (already sanitized,
|
|
1017
|
+
* lossy) stem by undoing the appended discriminator. A stem that itself ends in
|
|
1018
|
+
* a discriminator-shaped tail loses it — that affects only the reported
|
|
1019
|
+
* `matchedKey`, never which object is served.
|
|
1137
1020
|
*/
|
|
1138
1021
|
function stripKeyDiscriminator(stem) {
|
|
1139
1022
|
return KEY_DISCRIMINATOR_TAIL.exec(stem)?.[1] ?? stem;
|
|
1140
1023
|
}
|
|
1141
1024
|
/**
|
|
1142
1025
|
* Sanitize a path segment so a key can never escape its org/repo/scope
|
|
1143
|
-
* namespace.
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1146
|
-
*
|
|
1147
|
-
* signature on a pre-signed PUT/GET. Repo identifiers like `.` (the internal
|
|
1148
|
-
* provider's repo id) hit exactly this case, so the all-dots guard keeps the
|
|
1149
|
-
* object key canonical and the namespace boundary intact.
|
|
1026
|
+
* namespace. An all-dots segment (`.`, `..`, …) is also rewritten: HTTP/S3
|
|
1027
|
+
* path canonicalization collapses it (`a/./b` → `a/b`, `a/../b` → `b`), which
|
|
1028
|
+
* corrupts the namespace and breaks the SigV4 signature on a pre-signed
|
|
1029
|
+
* PUT/GET. The internal provider's repo id `.` hits exactly this case.
|
|
1150
1030
|
*/
|
|
1151
1031
|
function sanitizeSegment(s) {
|
|
1152
1032
|
const cleaned = s.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -1179,36 +1059,27 @@ function readPrefixes(ref) {
|
|
|
1179
1059
|
return [`${base}/shared/`];
|
|
1180
1060
|
}
|
|
1181
1061
|
/**
|
|
1182
|
-
* Object key for a committed cache entry.
|
|
1183
|
-
*
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1188
|
-
* tarball.
|
|
1189
|
-
*
|
|
1190
|
-
* It is deliberately a SUFFIX: `restoreByPrefix` matches `restoreKeys` by
|
|
1191
|
-
* string prefix, so appending leaves those semantics untouched. The only
|
|
1192
|
-
* parse that has to know about it is the `matchedKey` slice.
|
|
1062
|
+
* Object key for a committed cache entry. The trailing discriminator, a hash of
|
|
1063
|
+
* the EXACT cache key, keeps `build` and `Build` apart on a case-insensitive
|
|
1064
|
+
* namespace (the filesystem backend on macOS or Windows), where a restore could
|
|
1065
|
+
* otherwise return the other key's tarball. It is deliberately a SUFFIX:
|
|
1066
|
+
* `restoreByPrefix` matches `restoreKeys` by string prefix, so only the
|
|
1067
|
+
* `matchedKey` slice has to know about it.
|
|
1193
1068
|
*/
|
|
1194
1069
|
function finalKey(prefix, key) {
|
|
1195
1070
|
return `${prefix}${sanitizeSegment(key)}-${keyDiscriminator(key)}${TAR_SUFFIX}`;
|
|
1196
1071
|
}
|
|
1197
|
-
/**
|
|
1198
|
-
* The object key a committed save under `key` lands on for this ref. This is
|
|
1199
|
-
* the writer's own path: `beginSave` and `commitSave` address the entry
|
|
1200
|
-
* through it.
|
|
1201
|
-
*/
|
|
1072
|
+
/** The writer's object key for `key` under this ref, used by `beginSave` and `commitSave`. */
|
|
1202
1073
|
function userCacheEntryKey(ref, key) {
|
|
1203
1074
|
return finalKey(writePrefix(ref), key);
|
|
1204
1075
|
}
|
|
1205
1076
|
var UserCache = class {
|
|
1206
1077
|
storage;
|
|
1207
|
-
/**
|
|
1078
|
+
/** `KICI_USER_CACHE_QUOTA_BYTES`. */
|
|
1208
1079
|
defaultQuotaBytes;
|
|
1209
|
-
/**
|
|
1080
|
+
/** `KICI_USER_CACHE_TTL_MS`. */
|
|
1210
1081
|
defaultTtlMs;
|
|
1211
|
-
/**
|
|
1082
|
+
/** Absent = always use the cluster defaults. */
|
|
1212
1083
|
orgLimitsReader;
|
|
1213
1084
|
constructor(opts) {
|
|
1214
1085
|
this.storage = opts.storage;
|
|
@@ -1217,10 +1088,9 @@ var UserCache = class {
|
|
|
1217
1088
|
this.orgLimitsReader = opts.orgLimitsReader;
|
|
1218
1089
|
}
|
|
1219
1090
|
/**
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1222
|
-
*
|
|
1223
|
-
* restore/save because the settings lookup hiccupped.
|
|
1091
|
+
* Per-org override when present, else the cluster default. A reader failure
|
|
1092
|
+
* falls back to the defaults (logged): a settings-lookup hiccup must never
|
|
1093
|
+
* fail a restore/save.
|
|
1224
1094
|
*/
|
|
1225
1095
|
async resolveLimits(org) {
|
|
1226
1096
|
if (!this.orgLimitsReader) return {
|
|
@@ -1319,13 +1189,11 @@ var UserCache = class {
|
|
|
1319
1189
|
return (await this.storage.get(`${key}.hash`))?.toString("utf-8") || void 0;
|
|
1320
1190
|
}
|
|
1321
1191
|
/**
|
|
1322
|
-
* Evict
|
|
1323
|
-
*
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
* fail on its copy to the final key. Retired un-discriminated entries stay
|
|
1328
|
-
* in the set: they hold accounted bytes and are evictable like any other.
|
|
1192
|
+
* Evict the org's LRU entries until total tarball size <= the per-org quota.
|
|
1193
|
+
* An in-flight `.tmp-` upload is never a candidate: with no metadata it would
|
|
1194
|
+
* rank oldest and be reclaimed first, freeing nothing (no `.size`) and failing
|
|
1195
|
+
* the concurrent commit's copy. Retired un-discriminated entries stay
|
|
1196
|
+
* candidates: they hold accounted bytes.
|
|
1329
1197
|
*/
|
|
1330
1198
|
async enforceQuota(ref) {
|
|
1331
1199
|
const { quotaBytes } = await this.resolveLimits(ref.org);
|
|
@@ -1368,34 +1236,25 @@ var UserCache = class {
|
|
|
1368
1236
|
}
|
|
1369
1237
|
};
|
|
1370
1238
|
/**
|
|
1371
|
-
* `metadata` key set when a coordinator retires
|
|
1372
|
-
*
|
|
1373
|
-
* retirement is told apart from a revoke.
|
|
1239
|
+
* `metadata` key set when a coordinator retires its previous run's self-issued
|
|
1240
|
+
* credential. An operator revoke never sets it, which tells the two apart.
|
|
1374
1241
|
*/
|
|
1375
1242
|
const RETIRED_BY_INSTANCE_KEY = "retiredByInstance";
|
|
1376
1243
|
/** Default credential expiry: 90 days. */
|
|
1377
1244
|
const DEFAULT_EXPIRY_DAYS = 90;
|
|
1378
|
-
/**
|
|
1379
|
-
* Database CRUD operations for peer credentials.
|
|
1380
|
-
* Used by the coordinator to manage credentials for all cluster members.
|
|
1381
|
-
*/
|
|
1245
|
+
/** The coordinator's DB store of every cluster member's credential. */
|
|
1382
1246
|
var PeerCredentialStore = class {
|
|
1383
1247
|
db;
|
|
1384
1248
|
constructor(db) {
|
|
1385
1249
|
this.db = db;
|
|
1386
1250
|
}
|
|
1387
1251
|
/**
|
|
1388
|
-
* Save a
|
|
1389
|
-
*
|
|
1390
|
-
* permits exactly one active credential per instanceId).
|
|
1252
|
+
* Save a credential, revoking the instanceId's prior active one
|
|
1253
|
+
* (`peer_credentials_active_uniq` permits one active credential per instanceId).
|
|
1391
1254
|
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1395
|
-
* peer-client of `instanceId` that was authenticating with the old
|
|
1396
|
-
* credential is now invalidated and will be rejected on its next proof. The
|
|
1397
|
-
* caller logs this so a revoke that cascades sibling rejections is visible in
|
|
1398
|
-
* the orchestrator logs.
|
|
1255
|
+
* `revokedCount > 0` means every sibling peer-client of `instanceId` still on the
|
|
1256
|
+
* old shared credential is rejected on its next proof; the caller logs it so a
|
|
1257
|
+
* cascade of sibling rejections is visible.
|
|
1399
1258
|
*/
|
|
1400
1259
|
async save(opts) {
|
|
1401
1260
|
const expiryDays = opts.expiryDays ?? DEFAULT_EXPIRY_DAYS;
|
|
@@ -1422,16 +1281,12 @@ var PeerCredentialStore = class {
|
|
|
1422
1281
|
throw err;
|
|
1423
1282
|
}
|
|
1424
1283
|
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Find a non-revoked, non-expired credential by its hash.
|
|
1427
|
-
*/
|
|
1284
|
+
/** Non-revoked, non-expired credential by hash. */
|
|
1428
1285
|
async findByCredentialHash(hash) {
|
|
1429
1286
|
const row = await this.db.selectFrom("peer_credentials").selectAll().where("credential_hash", "=", hash).where("revoked_at", "is", null).where("expires_at", ">", /* @__PURE__ */ new Date()).executeTakeFirst();
|
|
1430
1287
|
return row ? mapRow(row) : null;
|
|
1431
1288
|
}
|
|
1432
|
-
/**
|
|
1433
|
-
* Find a non-revoked, non-expired credential by instance ID.
|
|
1434
|
-
*/
|
|
1289
|
+
/** Newest non-revoked, non-expired credential for the instance. */
|
|
1435
1290
|
async findByInstanceId(instanceId) {
|
|
1436
1291
|
const row = await this.db.selectFrom("peer_credentials").selectAll().where("instance_id", "=", instanceId).where("revoked_at", "is", null).where("expires_at", ">", /* @__PURE__ */ new Date()).orderBy("created_at", "desc").executeTakeFirst();
|
|
1437
1292
|
return row ? mapRow(row) : null;
|
|
@@ -1450,10 +1305,9 @@ var PeerCredentialStore = class {
|
|
|
1450
1305
|
return row ? mapRow(row) : null;
|
|
1451
1306
|
}
|
|
1452
1307
|
/**
|
|
1453
|
-
* Revoke `instanceId`'s unrevoked
|
|
1454
|
-
*
|
|
1455
|
-
*
|
|
1456
|
-
* Returns whether a row was retired.
|
|
1308
|
+
* Revoke `instanceId`'s unrevoked self-issued row with `credentialHash`,
|
|
1309
|
+
* recording who retired it. Every condition is in the WHERE clause, so a row
|
|
1310
|
+
* that changed meanwhile is left alone. Returns whether a row was retired.
|
|
1457
1311
|
*/
|
|
1458
1312
|
async retireSelfIssued(opts) {
|
|
1459
1313
|
const marker = JSON.stringify({ [RETIRED_BY_INSTANCE_KEY]: opts.retiredByInstance });
|
|
@@ -1463,39 +1317,25 @@ var PeerCredentialStore = class {
|
|
|
1463
1317
|
}).where("instance_id", "=", opts.instanceId).where("credential_hash", "=", opts.credentialHash).where("revoked_at", "is", null).where(sql`metadata->>'issuance'`, "=", "self").executeTakeFirst();
|
|
1464
1318
|
return Number(result?.numUpdatedRows ?? 0n) > 0;
|
|
1465
1319
|
}
|
|
1466
|
-
/**
|
|
1467
|
-
* Update the last_seen_at timestamp for a credential.
|
|
1468
|
-
* Optionally records which coordinator validated it.
|
|
1469
|
-
*/
|
|
1320
|
+
/** Bump last_seen_at; optionally record the validating coordinator. */
|
|
1470
1321
|
async updateLastSeen(credentialHash, validatedBy) {
|
|
1471
1322
|
const updates = { last_seen_at: /* @__PURE__ */ new Date() };
|
|
1472
1323
|
if (validatedBy) updates.last_validated_by = validatedBy;
|
|
1473
1324
|
await this.db.updateTable("peer_credentials").set(updates).where("credential_hash", "=", credentialHash).execute();
|
|
1474
1325
|
}
|
|
1475
|
-
/**
|
|
1476
|
-
* Revoke a peer's credential by instance ID.
|
|
1477
|
-
*/
|
|
1478
1326
|
async revoke(instanceId) {
|
|
1479
1327
|
await this.db.updateTable("peer_credentials").set({ revoked_at: /* @__PURE__ */ new Date() }).where("instance_id", "=", instanceId).where("revoked_at", "is", null).execute();
|
|
1480
1328
|
}
|
|
1481
|
-
/**
|
|
1482
|
-
* Revoke all active credentials.
|
|
1483
|
-
* @returns The number of credentials revoked.
|
|
1484
|
-
*/
|
|
1329
|
+
/** @returns The number of credentials revoked. */
|
|
1485
1330
|
async revokeAll() {
|
|
1486
1331
|
const result = await this.db.updateTable("peer_credentials").set({ revoked_at: /* @__PURE__ */ new Date() }).where("revoked_at", "is", null).executeTakeFirst();
|
|
1487
1332
|
return Number(result?.numUpdatedRows ?? 0);
|
|
1488
1333
|
}
|
|
1489
|
-
/**
|
|
1490
|
-
* List all active (non-revoked, non-expired) credentials.
|
|
1491
|
-
*/
|
|
1334
|
+
/** Non-revoked, non-expired credentials, oldest first. */
|
|
1492
1335
|
async listActive() {
|
|
1493
1336
|
return (await this.db.selectFrom("peer_credentials").selectAll().where("revoked_at", "is", null).where("expires_at", ">", /* @__PURE__ */ new Date()).orderBy("created_at", "asc").execute()).map(mapRow);
|
|
1494
1337
|
}
|
|
1495
1338
|
};
|
|
1496
|
-
/**
|
|
1497
|
-
* Map a database row to a PeerCredential interface.
|
|
1498
|
-
*/
|
|
1499
1339
|
function mapRow(row) {
|
|
1500
1340
|
return {
|
|
1501
1341
|
id: row.id,
|
|
@@ -1512,15 +1352,7 @@ function mapRow(row) {
|
|
|
1512
1352
|
metadata: row.metadata ?? {}
|
|
1513
1353
|
};
|
|
1514
1354
|
}
|
|
1515
|
-
/**
|
|
1516
|
-
* Write credential data to a JSON file with restrictive permissions.
|
|
1517
|
-
*
|
|
1518
|
-
* Creates the parent directory (e.g., ~/.kici/) with 0700 if it doesn't exist.
|
|
1519
|
-
* The credential file is written with 0600 permissions (owner read/write only).
|
|
1520
|
-
*
|
|
1521
|
-
* @param filePath - Absolute path to the credential file
|
|
1522
|
-
* @param data - Credential data to persist
|
|
1523
|
-
*/
|
|
1355
|
+
/** Write the credential file 0600, creating its parent directory 0700 if missing. */
|
|
1524
1356
|
async function writeCredentialFile(filePath, data) {
|
|
1525
1357
|
const dir = dirname(filePath);
|
|
1526
1358
|
await mkdir(dir, {
|
|
@@ -1531,12 +1363,7 @@ async function writeCredentialFile(filePath, data) {
|
|
|
1531
1363
|
await writeFile(filePath, json, { mode: 384 });
|
|
1532
1364
|
await chmod(filePath, 384);
|
|
1533
1365
|
}
|
|
1534
|
-
/**
|
|
1535
|
-
* Read credential data from a JSON file.
|
|
1536
|
-
*
|
|
1537
|
-
* @param filePath - Absolute path to the credential file
|
|
1538
|
-
* @returns Parsed credential data, or null if the file doesn't exist
|
|
1539
|
-
*/
|
|
1366
|
+
/** @returns The parsed credential file, or null if it does not exist. */
|
|
1540
1367
|
async function readCredentialFile(filePath) {
|
|
1541
1368
|
try {
|
|
1542
1369
|
const content = await readFile(filePath, "utf-8");
|
|
@@ -1547,13 +1374,9 @@ async function readCredentialFile(filePath) {
|
|
|
1547
1374
|
}
|
|
1548
1375
|
}
|
|
1549
1376
|
/**
|
|
1550
|
-
*
|
|
1551
|
-
* store
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
* Intended for tests of the store's own CRUD methods — production
|
|
1555
|
-
* code constructs the store with an already-allocated Kysely instance
|
|
1556
|
-
* shared with the orchestrator.
|
|
1377
|
+
* A store bound to a Postgres URL plus a pool disposer, so tests exercise the real
|
|
1378
|
+
* store without importing pg/kysely. Test-only: production passes the
|
|
1379
|
+
* orchestrator's shared Kysely instance.
|
|
1557
1380
|
*/
|
|
1558
1381
|
async function createPeerCredentialStoreFromUrl(databaseUrl, opts) {
|
|
1559
1382
|
const pool = new pg.Pool({
|
|
@@ -1610,22 +1433,19 @@ createLogger({ prefix: "coordinator-credential" });
|
|
|
1610
1433
|
/**
|
|
1611
1434
|
* Per-orchestrator coordinator that owns the shared peer credential file.
|
|
1612
1435
|
*
|
|
1613
|
-
*
|
|
1614
|
-
*
|
|
1615
|
-
*
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1618
|
-
*
|
|
1619
|
-
* per storm, and it never deletes a credential file a sibling has refreshed.
|
|
1436
|
+
* An orchestrator's peer-clients (one per peer) share one identity-scoped
|
|
1437
|
+
* credential file. Uncoordinated, a reconnect storm makes each sibling token-join
|
|
1438
|
+
* (each join revokes the prior credential, invalidating the others) and delete the
|
|
1439
|
+
* shared file on any rejection — a revocation cascade. One in-process mutex
|
|
1440
|
+
* serializes all file access, so one peer-client token-joins per storm, and a file
|
|
1441
|
+
* a sibling has refreshed is never deleted.
|
|
1620
1442
|
*
|
|
1621
|
-
* A coordinator without a join token
|
|
1622
|
-
*
|
|
1623
|
-
* coordinator its own credential, once per storm, under the same mutex.
|
|
1443
|
+
* A coordinator without a join token wires `selfIssue`: with no credential and no
|
|
1444
|
+
* token, it issues its own credential, once per storm, under the same mutex.
|
|
1624
1445
|
*
|
|
1625
|
-
* A coordinator also wires `corroborateRejection`. Any dialled endpoint can
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
* cannot read, keeps the file.
|
|
1446
|
+
* A coordinator also wires `corroborateRejection`. Any dialled endpoint can send a
|
|
1447
|
+
* rejection, so before deleting the file it asks its own cluster database: a
|
|
1448
|
+
* credential the database holds as valid, or an unreadable database, keeps the file.
|
|
1629
1449
|
*/
|
|
1630
1450
|
const logger = createLogger({ prefix: "peer-auth-coordinator" });
|
|
1631
1451
|
/** How long a waiting peer-client awaits an in-flight sibling token-join. */
|
|
@@ -1861,10 +1681,7 @@ var JoinTokenManager = class {
|
|
|
1861
1681
|
constructor(deps) {
|
|
1862
1682
|
this.deps = deps;
|
|
1863
1683
|
}
|
|
1864
|
-
/**
|
|
1865
|
-
* Create a new join token.
|
|
1866
|
-
* Returns the full token string (only available at creation time).
|
|
1867
|
-
*/
|
|
1684
|
+
/** Returns the full token string, available only at creation time. */
|
|
1868
1685
|
async createToken(opts) {
|
|
1869
1686
|
const expiryMs = opts.expiryMs ?? DEFAULT_EXPIRY_MS;
|
|
1870
1687
|
const expiry = Date.now() + Math.floor(expiryMs);
|
|
@@ -1897,12 +1714,11 @@ var JoinTokenManager = class {
|
|
|
1897
1714
|
* Peer token mode: claim the row the presented token's secret hashes to. Role and
|
|
1898
1715
|
* routing come from the row (`claimByHash`), never from the token's routing part.
|
|
1899
1716
|
*
|
|
1900
|
-
* Self-healing reuse:
|
|
1901
|
-
*
|
|
1902
|
-
*
|
|
1903
|
-
*
|
|
1904
|
-
*
|
|
1905
|
-
* usefulness beyond the instance that first consumed it.
|
|
1717
|
+
* Self-healing reuse: the same joining peer (`peerInstanceId`) can re-consume
|
|
1718
|
+
* the token until `expires_at`, so a peer that lost its credential re-presents
|
|
1719
|
+
* the token in its env and gets a fresh credential without an operator action.
|
|
1720
|
+
* Bounding reuse by `expires_at` and the consuming instanceId keeps a leaked
|
|
1721
|
+
* token useless beyond the instance that first consumed it.
|
|
1906
1722
|
*/
|
|
1907
1723
|
async validateAndConsumeToken(token, consumedBy, peerInstanceId) {
|
|
1908
1724
|
return this.claimByHash(tokenHashOf(parseToken(token).secretHex), consumedBy, peerInstanceId);
|
|
@@ -1983,10 +1799,8 @@ var JoinTokenManager = class {
|
|
|
1983
1799
|
}
|
|
1984
1800
|
};
|
|
1985
1801
|
/**
|
|
1986
|
-
*
|
|
1987
|
-
*
|
|
1988
|
-
* used by tests that need to exercise token validation/reuse against a real
|
|
1989
|
-
* cluster DB.
|
|
1802
|
+
* A JoinTokenManager with its own connection pool, for tests against a real
|
|
1803
|
+
* cluster DB. Mirrors `createPeerCredentialStoreFromUrl`.
|
|
1990
1804
|
*/
|
|
1991
1805
|
function createJoinTokenManagerFromUrl(databaseUrl, opts) {
|
|
1992
1806
|
const pool = new pg.Pool({
|
|
@@ -2001,9 +1815,6 @@ function createJoinTokenManagerFromUrl(databaseUrl, opts) {
|
|
|
2001
1815
|
}
|
|
2002
1816
|
};
|
|
2003
1817
|
}
|
|
2004
|
-
/**
|
|
2005
|
-
* Parse a join token string into routing info and secret hex.
|
|
2006
|
-
*/
|
|
2007
1818
|
function parseToken(token) {
|
|
2008
1819
|
const parts = token.split(".");
|
|
2009
1820
|
if (parts.length !== 3 || parts[0] !== TOKEN_PREFIX) throw new Error(`Invalid join token format. Expected: ${TOKEN_PREFIX}.<routing>.<secret>`);
|
|
@@ -2530,32 +2341,18 @@ function createTrustPolicyStoreFromUrl(databaseUrl, opts) {
|
|
|
2530
2341
|
}
|
|
2531
2342
|
//#endregion
|
|
2532
2343
|
//#region src/cli/api-client.ts
|
|
2344
|
+
/** kici-admin's HTTP client for the orchestrator admin API (Bearer token auth). */
|
|
2533
2345
|
/**
|
|
2534
|
-
*
|
|
2535
|
-
*
|
|
2536
|
-
*
|
|
2537
|
-
*
|
|
2538
|
-
*
|
|
2346
|
+
* A bare `TypeError: fetch failed` names neither the address nor the knob that
|
|
2347
|
+
* sets it, so it reads as a broken subcommand rather than a misaddressed client.
|
|
2348
|
+
* The base URL comes from `KICI_ADMIN_URL`, not `KICI_ORCHESTRATOR_URL`, and
|
|
2349
|
+
* subcommands with `--database-url` fall back to direct DB access, so with
|
|
2350
|
+
* `KICI_DATABASE_URL` set only the HTTP-only ones fail. The error therefore names
|
|
2351
|
+
* the address, the variable and the near-miss.
|
|
2539
2352
|
*/
|
|
2540
2353
|
/**
|
|
2541
|
-
*
|
|
2542
|
-
*
|
|
2543
|
-
* through the CLI's `Error: ${message}` handler that becomes `Error: fetch
|
|
2544
|
-
* failed`, which reads like a fault in the subcommand rather than a misaddressed
|
|
2545
|
-
* client, and sends the operator debugging the wrong thing.
|
|
2546
|
-
*
|
|
2547
|
-
* The trap is sharpened by two details of this CLI. The base URL comes from
|
|
2548
|
-
* `KICI_ADMIN_URL`, not the `KICI_ORCHESTRATOR_URL` an operator is likelier to
|
|
2549
|
-
* have exported; and subcommands accepting `--database-url` fall back to direct
|
|
2550
|
-
* DB access, so on a host with `KICI_DATABASE_URL` set they keep working while
|
|
2551
|
-
* the HTTP-only ones fail — making it look like specific subcommands are broken.
|
|
2552
|
-
*
|
|
2553
|
-
* So: name the address, name the variable, and name the near-miss.
|
|
2554
|
-
*/
|
|
2555
|
-
/**
|
|
2556
|
-
* Best-effort one-line detail from a rejected `fetch`. Returns '' when nothing
|
|
2557
|
-
* useful is available, so the caller can omit the parenthetical rather than
|
|
2558
|
-
* print an empty one.
|
|
2354
|
+
* Best-effort one-line detail from a rejected `fetch`; '' when none, so the
|
|
2355
|
+
* caller omits the parenthetical.
|
|
2559
2356
|
*/
|
|
2560
2357
|
function firstCauseMessage(err) {
|
|
2561
2358
|
if (!(err instanceof Error)) return "";
|
|
@@ -2569,21 +2366,16 @@ function firstCauseMessage(err) {
|
|
|
2569
2366
|
return "";
|
|
2570
2367
|
}
|
|
2571
2368
|
/**
|
|
2572
|
-
* True when a rejected `fetch` failed at the transport layer,
|
|
2573
|
-
*
|
|
2574
|
-
*
|
|
2575
|
-
* `fetch` uses a `TypeError` for both, so they are easy to conflate — but only
|
|
2576
|
-
* a transport failure means the orchestrator was actually dialled. Measured
|
|
2577
|
-
* against Node 24's undici:
|
|
2369
|
+
* True when a rejected `fetch` failed at the transport layer, not while the
|
|
2370
|
+
* request was being built. Both are a `TypeError`, but only a transport failure
|
|
2371
|
+
* means the orchestrator was dialled. Measured against Node 24's undici:
|
|
2578
2372
|
*
|
|
2579
2373
|
* closed port → TypeError('fetch failed') cause: Error(ECONNREFUSED)
|
|
2580
2374
|
* invalid header → TypeError('Headers.append: … invalid header …') cause: undefined
|
|
2581
2375
|
* invalid URL → TypeError('Failed to parse URL from …') cause: Error(ERR_INVALID_URL)
|
|
2582
2376
|
*
|
|
2583
|
-
*
|
|
2584
|
-
*
|
|
2585
|
-
* transport failure and a descriptive message for everything it rejects before
|
|
2586
|
-
* opening a connection.
|
|
2377
|
+
* So the discriminator is the message, NOT a `cause` (the invalid-URL case has
|
|
2378
|
+
* one): undici says exactly `fetch failed` only for a transport failure.
|
|
2587
2379
|
*/
|
|
2588
2380
|
function isTransportFailure(err) {
|
|
2589
2381
|
return err instanceof TypeError && err.message === "fetch failed";
|
|
@@ -2621,9 +2413,6 @@ var AdminApiClient = class {
|
|
|
2621
2413
|
this.baseUrl = baseUrl;
|
|
2622
2414
|
this.token = token;
|
|
2623
2415
|
}
|
|
2624
|
-
/**
|
|
2625
|
-
* Make an authenticated HTTP request to the admin API.
|
|
2626
|
-
*/
|
|
2627
2416
|
async request(method, path, body) {
|
|
2628
2417
|
const res = await fetchAdminApi(`${this.baseUrl}${path}`, {
|
|
2629
2418
|
method,
|
|
@@ -2646,39 +2435,21 @@ var AdminApiClient = class {
|
|
|
2646
2435
|
if (res.status === 204) return;
|
|
2647
2436
|
return await res.json();
|
|
2648
2437
|
}
|
|
2649
|
-
/**
|
|
2650
|
-
* Public GET request returning parsed JSON.
|
|
2651
|
-
*/
|
|
2652
2438
|
async get(path) {
|
|
2653
2439
|
return this.request("GET", path);
|
|
2654
2440
|
}
|
|
2655
|
-
/**
|
|
2656
|
-
* Public POST request returning parsed JSON.
|
|
2657
|
-
*/
|
|
2658
2441
|
async post(path, body) {
|
|
2659
2442
|
return this.request("POST", path, body);
|
|
2660
2443
|
}
|
|
2661
|
-
/**
|
|
2662
|
-
* Public PATCH request returning parsed JSON.
|
|
2663
|
-
*/
|
|
2664
2444
|
async patch(path, body) {
|
|
2665
2445
|
return this.request("PATCH", path, body);
|
|
2666
2446
|
}
|
|
2667
|
-
/**
|
|
2668
|
-
* Public PUT request returning parsed JSON.
|
|
2669
|
-
*/
|
|
2670
2447
|
async put(path, body) {
|
|
2671
2448
|
return this.request("PUT", path, body);
|
|
2672
2449
|
}
|
|
2673
|
-
/**
|
|
2674
|
-
* Public DELETE request returning parsed JSON.
|
|
2675
|
-
*/
|
|
2676
2450
|
async delete(path) {
|
|
2677
2451
|
return this.request("DELETE", path);
|
|
2678
2452
|
}
|
|
2679
|
-
/**
|
|
2680
|
-
* Public GET request returning raw response text.
|
|
2681
|
-
*/
|
|
2682
2453
|
async getText(path) {
|
|
2683
2454
|
const res = await fetchAdminApi(`${this.baseUrl}${path}`, {
|
|
2684
2455
|
method: "GET",
|
|
@@ -2842,13 +2613,9 @@ var AdminApiClient = class {
|
|
|
2842
2613
|
return this.request("GET", `/api/v1/agent-tokens${qs}`);
|
|
2843
2614
|
}
|
|
2844
2615
|
/**
|
|
2845
|
-
* Revoke an agent token.
|
|
2846
|
-
*
|
|
2847
|
-
*
|
|
2848
|
-
* synchronously closes every in-flight agent WS authenticated by
|
|
2849
|
-
* this token. The returned `kicked` count is the number
|
|
2850
|
-
* of WS connections that were closed on the wire — surface it to
|
|
2851
|
-
* the operator so they know the revocation actually propagated.
|
|
2616
|
+
* Revoke an agent token: the orchestrator sets `agent_tokens.revoked_at` AND
|
|
2617
|
+
* synchronously closes every agent WS authenticated by it. `kicked` counts
|
|
2618
|
+
* the closed connections; show it so the operator sees the revocation propagated.
|
|
2852
2619
|
*/
|
|
2853
2620
|
async revokeAgentToken(id) {
|
|
2854
2621
|
return this.request("DELETE", `/api/v1/agent-tokens/${encodeURIComponent(id)}`);
|
|
@@ -2906,7 +2673,6 @@ var AdminApiClient = class {
|
|
|
2906
2673
|
async drainStatus() {
|
|
2907
2674
|
return this.request("GET", "/api/v1/admin/orchestrator/drain");
|
|
2908
2675
|
}
|
|
2909
|
-
/** Forget a departed peer on this coordinator and every connected sibling. */
|
|
2910
2676
|
/**
|
|
2911
2677
|
* Forget a departed peer on this coordinator and every connected sibling. A
|
|
2912
2678
|
* refusal that only needs the backstop acknowledgement comes back as a body
|
|
@@ -2978,9 +2744,17 @@ var AdminApiClient = class {
|
|
|
2978
2744
|
async stopScalerOrphans(body) {
|
|
2979
2745
|
return this.request("POST", "/api/v1/admin/scaler/orphans/stop", body);
|
|
2980
2746
|
}
|
|
2747
|
+
/**
|
|
2748
|
+
* @deprecated No KiCI server serves `/api/v1/api-keys`; use `createToken`
|
|
2749
|
+
* (`kici-admin token create`). Removed in v1.0.0.
|
|
2750
|
+
*/
|
|
2981
2751
|
async createApiKey(opts) {
|
|
2982
2752
|
return this.request("POST", "/api/v1/api-keys", opts);
|
|
2983
2753
|
}
|
|
2754
|
+
/**
|
|
2755
|
+
* @deprecated No KiCI server serves `/api/v1/api-keys`; use `createToken`
|
|
2756
|
+
* with `routingKey` (`kici-admin token create --routing-key`). Removed in v1.0.0.
|
|
2757
|
+
*/
|
|
2984
2758
|
async addRoutingKeyPermission(keyId, pattern) {
|
|
2985
2759
|
return this.request("POST", `/api/v1/api-keys/${encodeURIComponent(keyId)}/routing-permissions`, { pattern });
|
|
2986
2760
|
}
|
|
@@ -3009,10 +2783,9 @@ var AdminApiClient = class {
|
|
|
3009
2783
|
return this.request("POST", "/api/v1/admin/backends/sync");
|
|
3010
2784
|
}
|
|
3011
2785
|
/**
|
|
3012
|
-
* List
|
|
3013
|
-
*
|
|
3014
|
-
*
|
|
3015
|
-
* are returned.
|
|
2786
|
+
* List runs. `status` takes one status or a comma-separated list (e.g.
|
|
2787
|
+
* `success,failed`). `since` is an ISO-8601 timestamp; only runs with
|
|
2788
|
+
* `created_at` strictly later are returned.
|
|
3016
2789
|
*/
|
|
3017
2790
|
async listRuns(opts) {
|
|
3018
2791
|
const qs = toQuery({
|
|
@@ -3418,14 +3191,10 @@ function startManifestLoopback(opts) {
|
|
|
3418
3191
|
//#endregion
|
|
3419
3192
|
//#region src/cli/open-browser.ts
|
|
3420
3193
|
/**
|
|
3421
|
-
*
|
|
3422
|
-
*
|
|
3423
|
-
*
|
|
3424
|
-
*
|
|
3425
|
-
*
|
|
3426
|
-
* `KICI_BROWSER_CMD` overrides the launcher: `none` suppresses it entirely
|
|
3427
|
-
* (automated tests / headless capture), any other value is run with `{url}` substituted —
|
|
3428
|
-
* mirroring the `kici login` convention.
|
|
3194
|
+
* Best-effort: the caller always prints the URL too, so a headless host falls
|
|
3195
|
+
* back to copy-paste. `KICI_BROWSER_CMD` overrides the launcher as in
|
|
3196
|
+
* `kici login`: `none` suppresses it (tests, headless capture), any other
|
|
3197
|
+
* value runs with `{url}` substituted.
|
|
3429
3198
|
*/
|
|
3430
3199
|
function openBrowserBestEffort(url) {
|
|
3431
3200
|
const override = process.env.KICI_BROWSER_CMD;
|
|
@@ -3459,15 +3228,13 @@ const WebhookUrlNote = z.enum([
|
|
|
3459
3228
|
//#endregion
|
|
3460
3229
|
//#region src/cli/webhook-url-notes.ts
|
|
3461
3230
|
/**
|
|
3462
|
-
*
|
|
3463
|
-
*
|
|
3464
|
-
* `WebhookUrlNote` reads the same in both.
|
|
3231
|
+
* Text for a missing webhook URL, shared by `kici-admin source add` and the
|
|
3232
|
+
* manifest setup flow.
|
|
3465
3233
|
*/
|
|
3466
3234
|
const DASHBOARD_HINT = "Find the webhook URL in the dashboard under Sources.";
|
|
3467
3235
|
/**
|
|
3468
|
-
*
|
|
3469
|
-
*
|
|
3470
|
-
* version can send a note this CLI does not know.
|
|
3236
|
+
* The reason in `(unavailable — <reason>)`. Takes a plain string: another
|
|
3237
|
+
* orchestrator version can send a note this CLI does not know.
|
|
3471
3238
|
*/
|
|
3472
3239
|
function webhookNoteReason(note) {
|
|
3473
3240
|
switch (note) {
|
|
@@ -3499,16 +3266,14 @@ function webhookNoteHint(note) {
|
|
|
3499
3266
|
//#endregion
|
|
3500
3267
|
//#region src/cli/commands/source-manifest.ts
|
|
3501
3268
|
/**
|
|
3502
|
-
* `kici-admin source add github --manifest
|
|
3503
|
-
*
|
|
3504
|
-
*
|
|
3505
|
-
*
|
|
3506
|
-
*
|
|
3507
|
-
* App's id + private key + webhook secret, then reuses the existing source
|
|
3508
|
-
* storage + Platform-registration path.
|
|
3269
|
+
* `kici-admin source add github --manifest`: GitHub's App Manifest flow end to
|
|
3270
|
+
* end. The pre-configured manifest goes to GitHub through an auto-submitting
|
|
3271
|
+
* form; the short-lived setup code comes back over a localhost loopback (or is
|
|
3272
|
+
* pasted with --no-browser) and is exchanged for the App's id, private key and
|
|
3273
|
+
* webhook secret, which take the normal source storage and registration path.
|
|
3509
3274
|
*
|
|
3510
|
-
* The private-key-bearing conversion
|
|
3511
|
-
*
|
|
3275
|
+
* The private-key-bearing conversion runs only on the orchestrator host and
|
|
3276
|
+
* never transits the Platform (sovereignty invariant).
|
|
3512
3277
|
*/
|
|
3513
3278
|
/** Static marketing-site page the CLI points at in headless paste-code mode. */
|
|
3514
3279
|
const STATIC_CALLBACK_URL = "https://kici.dev/gh-manifest-callback";
|