@kici-dev/orchestrator 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__test-helpers__/fake-scaler-state-store.d.ts +24 -0
- package/dist/__test-helpers__/mock-db-query.d.ts +130 -0
- package/dist/__test-helpers__/mock-db.d.ts +27 -0
- package/dist/agent/agent-version.d.ts +34 -0
- package/dist/agent/dispatcher.d.ts +151 -13
- package/dist/agent/host-roster.d.ts +37 -5
- package/dist/agent/registry.d.ts +2 -0
- package/dist/app.d.ts +56 -7
- package/dist/approvals/apply-decision.d.ts +67 -19
- package/dist/approvals/held-run-release-wiring.d.ts +45 -0
- package/dist/approvals/independent-wiring.d.ts +55 -0
- package/dist/approvals/resume-router.d.ts +9 -0
- package/dist/approvals/triggerer-subject.d.ts +37 -0
- package/dist/cache/dep-cache.d.ts +54 -10
- package/dist/cache/global-eval-round-cache.d.ts +95 -0
- package/dist/cache/index.d.ts +3 -0
- package/dist/cache/pending-global-evals.d.ts +42 -0
- package/dist/cache/pending-inits.d.ts +10 -0
- package/dist/cli/api-client.d.ts +18 -0
- package/dist/cli/commands/cluster-settings.d.ts +52 -3
- package/dist/cli/commands/diagnose.d.ts +6 -0
- package/dist/cli/commands/held-run.d.ts +58 -0
- package/dist/cli/commands/runs.d.ts +1 -0
- package/dist/cli/commands/trust-policy.d.ts +117 -12
- package/dist/cli/kici-admin.d.ts +10 -0
- package/dist/cli.js +2770 -938
- package/dist/cluster/cluster-identity.d.ts +3 -3
- package/dist/cluster/cluster-settings-reader.d.ts +59 -1
- package/dist/cluster/coordinator.d.ts +44 -0
- package/dist/cluster/index.d.ts +4 -0
- package/dist/cluster/peer-handler.d.ts +19 -0
- package/dist/cluster/peer-registry.d.ts +17 -0
- package/dist/cluster/plan-headroom-store.d.ts +26 -0
- package/dist/cluster/worker-eviction.d.ts +69 -0
- package/dist/cold-store/load-event-log-range.d.ts +10 -3
- package/dist/cold-store/load-secret-audit-log-range.d.ts +4 -3
- package/dist/cold-store/tables/secret-audit-log.d.ts +4 -3
- package/dist/config.d.ts +48 -21
- package/dist/content-requirements-cache.d.ts +55 -0
- package/dist/contexts/held-runs.d.ts +123 -16
- package/dist/contexts/protection/aggregate.d.ts +3 -2
- package/dist/contexts/protection/branch-gate.d.ts +15 -0
- package/dist/contexts/protection/concurrency-gate.d.ts +8 -2
- package/dist/contexts/protection/pipeline.d.ts +18 -0
- package/dist/contexts/release-queued-holds.d.ts +35 -0
- package/dist/dashboard/attestation-filters.d.ts +8 -0
- package/dist/dashboard/handler.d.ts +6 -24
- package/dist/db/migrations/109_cluster_settings_cache_knobs.d.ts +4 -0
- package/dist/db/migrations/110_cluster_settings_global_eval_knobs.d.ts +4 -0
- package/dist/db/migrations/111_cluster_settings_global_eval_wait.d.ts +4 -0
- package/dist/db/migrations/112_execution_runs_workflow_repo.d.ts +4 -0
- package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +30 -0
- package/dist/db/migrations/114_ingest_queue_claim.d.ts +4 -0
- package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +9 -0
- package/dist/db/migrations/116_org_plan_headroom.d.ts +4 -0
- package/dist/db/migrations/117_invoke_gate_proxy_jobs.d.ts +4 -0
- package/dist/db/migrations/118_invoke_gate_scheduling.d.ts +4 -0
- package/dist/db/migrations/119_scaler_ha_ownership.d.ts +4 -0
- package/dist/db/migrations/120_cluster_settings_scaler_reaper.d.ts +4 -0
- package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +19 -0
- package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +25 -0
- package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +24 -0
- package/dist/db/migrations/124_cluster_settings_provision_backoff.d.ts +4 -0
- package/dist/db/migrations/125_org_trust_directory.d.ts +30 -0
- package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +28 -0
- package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +26 -0
- package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +23 -0
- package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +24 -0
- package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +32 -0
- package/dist/db/types.d.ts +428 -12
- package/dist/entry-helpers.d.ts +28 -0
- package/dist/events/event-emitter.d.ts +22 -0
- package/dist/events/event-router.d.ts +87 -1
- package/dist/events/types.d.ts +2 -23
- package/dist/git/credential-broker.d.ts +94 -0
- package/dist/git/inherited-secret.d.ts +24 -0
- package/dist/git/installation-token.d.ts +38 -0
- package/dist/git/job-context.d.ts +13 -0
- package/dist/index.js +324 -84
- package/dist/metrics/agent-metrics-aggregator.d.ts +4 -4
- package/dist/metrics/prometheus.d.ts +170 -3
- package/dist/oidc/oidc-mint-registration.d.ts +7 -3
- package/dist/orchestrator-core.d.ts +91 -24
- package/dist/pipeline/content-filter.d.ts +71 -0
- package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +521 -11
- package/dist/pipeline/global-eval-round.d.ts +307 -0
- package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
- package/dist/pipeline/invoke-gate.d.ts +164 -0
- package/dist/pipeline/job-contexts.d.ts +16 -17
- package/dist/pipeline/needs-scheduler.d.ts +13 -0
- package/dist/pipeline/process-webhook.d.ts +126 -31
- package/dist/pipeline/processor.d.ts +282 -23
- package/dist/pipeline/rerun.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +40 -7
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +15 -0
- package/dist/pipeline/security-hold-check.d.ts +217 -0
- package/dist/pipeline/test-pipeline.d.ts +12 -0
- package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
- package/dist/pipeline/webhook-payload-store.d.ts +20 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
- package/dist/policy/dashboard-write-policy.d.ts +14 -0
- package/dist/provenance/backfill-run.d.ts +10 -1
- package/dist/provider-registry.d.ts +47 -4
- package/dist/providers/github/check-status-poster.d.ts +33 -3
- package/dist/providers/github/commit-message.d.ts +20 -0
- package/dist/providers/github/file-contents.d.ts +40 -0
- package/dist/providers/github/index.d.ts +2 -2
- package/dist/providers/github/normalizer.d.ts +3 -2
- package/dist/providers/local/normalizer.d.ts +3 -5
- package/dist/providers/universal-git/config.d.ts +2 -0
- package/dist/providers/universal-git/index.d.ts +11 -2
- package/dist/providers/universal-git/normalizer.d.ts +10 -0
- package/dist/queue/cleanup.d.ts +7 -1
- package/dist/queue/job-queue.d.ts +112 -7
- package/dist/queue/terminalize-unroutable.d.ts +13 -0
- package/dist/registration/extractor.d.ts +3 -3
- package/dist/registration/registration-index.d.ts +7 -0
- package/dist/registration/registration-run-match.d.ts +47 -0
- package/dist/registration/registration-store.d.ts +22 -0
- package/dist/reporting/check-run-reporter.d.ts +146 -2
- package/dist/reporting/check-run-summary.d.ts +25 -1
- package/dist/reporting/execution-tracker.d.ts +324 -9
- package/dist/reporting/log-chunk-sink.d.ts +8 -5
- package/dist/reporting/run-aggregator.d.ts +4 -14
- package/dist/reporting/step-display-order.d.ts +43 -0
- package/dist/routes/admin-held-runs.d.ts +142 -0
- package/dist/routes/admin-org-settings.d.ts +5 -0
- package/dist/routes/admin-trust-policy.d.ts +34 -6
- package/dist/routes/admin.d.ts +15 -0
- package/dist/scaler/backend-factory.d.ts +55 -0
- package/dist/scaler/bare-metal-backend.d.ts +43 -5
- package/dist/scaler/claim-store.d.ts +119 -0
- package/dist/scaler/config.d.ts +4 -0
- package/dist/scaler/container-backend.d.ts +18 -4
- package/dist/scaler/container-routing.d.ts +23 -0
- package/dist/scaler/container-spawn.d.ts +28 -0
- package/dist/scaler/event-backend.d.ts +124 -0
- package/dist/scaler/event-provision-reaper.d.ts +350 -0
- package/dist/scaler/failure-tracker.d.ts +1 -1
- package/dist/scaler/firecracker-backend.d.ts +33 -6
- package/dist/scaler/index.d.ts +11 -1
- package/dist/scaler/label-matcher.d.ts +4 -3
- package/dist/scaler/manager.d.ts +905 -76
- package/dist/scaler/resolve-container-auth.d.ts +45 -0
- package/dist/scaler/scaler-events.d.ts +11 -0
- package/dist/scaler/scaler-state-store.d.ts +294 -10
- package/dist/scaler/types.d.ts +131 -13
- package/dist/scaler/warm-pool.d.ts +132 -29
- package/dist/security/comment-handler.d.ts +50 -11
- package/dist/security/global-workflow-policy.d.ts +52 -12
- package/dist/security/identity-link.d.ts +60 -0
- package/dist/security/lock-source.d.ts +11 -13
- package/dist/security/reduced-privilege-note.d.ts +59 -0
- package/dist/security/trust-directory-store.d.ts +233 -0
- package/dist/security/trust-policy-gate.d.ts +94 -59
- package/dist/security/trust-policy-store.d.ts +3 -0
- package/dist/security/trust-resolver.d.ts +14 -98
- package/dist/server.d.ts +11 -1
- package/dist/server.js +56329 -42234
- package/dist/stale-detector/gate-deadline-detector.d.ts +52 -0
- package/dist/stale-detector/stale-run-detector.d.ts +61 -3
- package/dist/standalone.js +37619 -23570
- package/dist/webhook/ingest-accept.d.ts +70 -0
- package/dist/webhook/ingest-overflow-buffer.d.ts +35 -4
- package/dist/webhook/ingest-overflow-replayer.d.ts +50 -6
- package/dist/worker/in-memory-job-queue.d.ts +1 -1
- package/dist/ws/agent-handler.d.ts +31 -4
- package/dist/ws/dashboard-context-handler.d.ts +28 -4
- package/dist/ws/dashboard-global-workflows-handler.d.ts +36 -9
- package/dist/ws/execution-status-frame.d.ts +32 -0
- package/dist/ws/failure-messages.d.ts +2 -0
- package/dist/ws/git-credential-relay.d.ts +39 -0
- package/dist/ws/oidc-token-relay.d.ts +6 -11
- package/dist/ws/platform-client.d.ts +68 -9
- package/dist/ws/test-relay-handlers.d.ts +35 -10
- package/installer-image-digests.json +3 -3
- package/package.json +24 -19
- package/sbom.spdx.json +868 -821
- package/dist/approvals/team-membership-lookup.d.ts +0 -13
- package/dist/pipeline/inline-eval.d.ts +0 -44
- package/dist/providers/github/contributor-resolver.d.ts +0 -30
- package/dist/security/contributor-cache.d.ts +0 -83
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared approve/reject applier — the single code path behind both the
|
|
3
|
-
* dashboard WS handler and the `kici` CLI HTTP route, so the authorization +
|
|
4
|
-
* resume story is identical regardless of the surface.
|
|
5
|
-
*
|
|
6
|
-
* Flow for an approve:
|
|
7
|
-
* 1. Load the hold + its recorded decisions.
|
|
8
|
-
* 2. Build the team-membership lookup and run `canApprove` (eligibility +
|
|
9
|
-
* self-approval gate). Ineligible actors are rejected with a clear reason.
|
|
10
|
-
* 3. Record the decision in `held_run_approvals`.
|
|
11
|
-
* 4. Re-evaluate. If all clauses are satisfied, `release()` the hold and
|
|
12
|
-
* resume the element (re-dispatch for job/workflow; the agent bridge for
|
|
13
|
-
* step scope — wired by the caller via `onStepRelease`).
|
|
14
|
-
*
|
|
15
|
-
* A reject records the decision and `reject()`s the hold (failing the element).
|
|
16
|
-
*/
|
|
17
1
|
import { ApprovalDecision } from '@kici-dev/engine';
|
|
2
|
+
import type { HeldRun } from '../db/types.js';
|
|
3
|
+
import { HoldOutcome } from '../pipeline/security-hold-check.js';
|
|
18
4
|
import type { HeldRunStore, ReleaseSignal } from '../contexts/held-runs.js';
|
|
19
5
|
import { type TeamMembershipLookup } from './approval-resolver.js';
|
|
6
|
+
/** Whether a decision's post-commit consequence ran to completion. */
|
|
7
|
+
export interface DecisionConsequenceResult {
|
|
8
|
+
/** True when the whole consequence chain ran; false when a step of it threw. */
|
|
9
|
+
ok: boolean;
|
|
10
|
+
/** The failure message, when `ok` is false. */
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
20
13
|
/** Outcome of applying a decision. */
|
|
21
14
|
export interface ApplyDecisionResult {
|
|
22
15
|
/** Whether the decision was accepted (recorded). */
|
|
@@ -29,6 +22,29 @@ export interface ApplyDecisionResult {
|
|
|
29
22
|
reason?: string;
|
|
30
23
|
/** The release signal, when the element was released (for the caller to resume). */
|
|
31
24
|
release?: ReleaseSignal;
|
|
25
|
+
/**
|
|
26
|
+
* The decision's post-commit consequence — already running, deliberately not
|
|
27
|
+
* awaited by the applier. Present on `released` and `rejected`, absent on
|
|
28
|
+
* every outcome that changed nothing (`pending`, `ineligible`, `not-found`).
|
|
29
|
+
*
|
|
30
|
+
* It NEVER rejects: a throw anywhere in the chain is logged and surfaces as
|
|
31
|
+
* `{ ok: false, error }`, so a caller may drop it on the floor without
|
|
32
|
+
* risking an unhandled rejection. A caller that answers a waiting client
|
|
33
|
+
* answers without awaiting this, and attaches its audit record to it instead
|
|
34
|
+
* — the record then carries the consequence's real outcome rather than a
|
|
35
|
+
* guess. A caller that needs the consequence finished before it continues
|
|
36
|
+
* (a test, a sequential sweep) awaits it.
|
|
37
|
+
*
|
|
38
|
+
* The consequence is where an approve was spending nine-plus seconds behind a
|
|
39
|
+
* ten-second relay budget, answering 504 on an approval that had already
|
|
40
|
+
* landed. It is not a stronger answer for having been awaited: the resume it
|
|
41
|
+
* runs is the same dispatch the original webhook delivery ran without any
|
|
42
|
+
* client waiting on it, `dispatchMatchedWorkflow` returning says only that
|
|
43
|
+
* jobs were routed rather than that any of them ran, and `resumeWorkflow`
|
|
44
|
+
* already reports its own failures on the run row and on the provider's check
|
|
45
|
+
* runs rather than through this return value.
|
|
46
|
+
*/
|
|
47
|
+
consequence?: Promise<DecisionConsequenceResult>;
|
|
32
48
|
}
|
|
33
49
|
/** Dependencies injected into `applyDecision` (testable in isolation). */
|
|
34
50
|
export interface ApplyDecisionDeps {
|
|
@@ -55,10 +71,42 @@ export interface ApplyDecisionDeps {
|
|
|
55
71
|
*/
|
|
56
72
|
onStepReject?: (heldRunId: string, reason?: string) => Promise<void> | void;
|
|
57
73
|
/**
|
|
58
|
-
* Called when a workflow-scoped hold is rejected
|
|
59
|
-
*
|
|
74
|
+
* Called when a workflow-scoped hold is rejected — cancel the run and drop
|
|
75
|
+
* the pending workflow context. Carries the whole hold row and the rejecter's
|
|
76
|
+
* own reason, which becomes the run's cancellation reason: the callers serve
|
|
77
|
+
* the install gate, the org trust policy's PR-wide hold and the SDK's
|
|
78
|
+
* workflow-level `requireApproval` alike, so a reason invented at the wiring
|
|
79
|
+
* site would misattribute one of them.
|
|
80
|
+
*
|
|
81
|
+
* The row is what tells those apart — only some of them posted a pending
|
|
82
|
+
* `KiCI Security` check, and only those have one to complete. Resolves to
|
|
83
|
+
* whether a terminal security check was WRITTEN, so this applier suppresses
|
|
84
|
+
* its own post only when one actually landed.
|
|
85
|
+
*/
|
|
86
|
+
onWorkflowReject?: (hold: HeldRun, reason?: string) => Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Terminalize the `KiCI Security` check a just-ended hold posted, for every
|
|
89
|
+
* hold this applier ends that `onWorkflowReject` did not already report.
|
|
90
|
+
*
|
|
91
|
+
* Without it a job-scoped security hold answered from the dashboard strands
|
|
92
|
+
* its check forever: the row leaves `pending`, so the stale detector's
|
|
93
|
+
* approval-window sweep — which only ever sees pending rows — can no longer
|
|
94
|
+
* reach it. Optional so an orchestrator with no provider wiring degrades to
|
|
95
|
+
* flip-and-resume rather than failing the decision.
|
|
96
|
+
*
|
|
97
|
+
* `actorSub` is the deciding actor's opaque subject id, which is what this
|
|
98
|
+
* applier has and NOT something a contributor can read. Resolving it to a
|
|
99
|
+
* display name — or dropping the attribution when it resolves to none — is
|
|
100
|
+
* the wiring site's job, because only that site holds the org's identity
|
|
101
|
+
* directory. Omitted entirely where the summary must match one a delegate
|
|
102
|
+
* built without it; see the reject arm below.
|
|
60
103
|
*/
|
|
61
|
-
|
|
104
|
+
settleSecurityCheck?: (args: {
|
|
105
|
+
hold: HeldRun;
|
|
106
|
+
outcome: HoldOutcome;
|
|
107
|
+
actorSub?: string | undefined;
|
|
108
|
+
reason?: string | undefined;
|
|
109
|
+
}) => Promise<boolean>;
|
|
62
110
|
}
|
|
63
111
|
/** Apply a single approve/reject decision to a hold. */
|
|
64
112
|
export declare function applyDecision(deps: ApplyDecisionDeps, args: {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The release wiring the local (`kici-admin`) held-run decision surface runs on.
|
|
3
|
+
*
|
|
4
|
+
* `createApp` composes this for every mode. It lives here rather than inline in
|
|
5
|
+
* that composition root because the defect it guards against is a MISSING
|
|
6
|
+
* field, not a wrong algorithm: a release path that flips the `held_runs` row
|
|
7
|
+
* without dispatching looks like it works and does nothing. A factory can be
|
|
8
|
+
* constructed by a test and its fields asserted; an object literal buried in
|
|
9
|
+
* `createApp` cannot.
|
|
10
|
+
*
|
|
11
|
+
* It returns `undefined` when there is no job-release callback, so an app
|
|
12
|
+
* assembled without one mounts no decision route at all rather than one whose
|
|
13
|
+
* approves are silent no-ops.
|
|
14
|
+
*
|
|
15
|
+
* Every callback reads the LIVE processing-deps bag at call time rather than
|
|
16
|
+
* capturing one: the provider registry is swapped whenever sources reload, and
|
|
17
|
+
* a captured bag would resolve a check poster from a registry that no longer
|
|
18
|
+
* serves the routing key.
|
|
19
|
+
*/
|
|
20
|
+
import type { ReleaseSignal } from '../contexts/held-runs.js';
|
|
21
|
+
import type { ProcessingDeps } from '../pipeline/processor.js';
|
|
22
|
+
import type { HeldRunReleaseWiring } from '../routes/admin-held-runs.js';
|
|
23
|
+
/** The subset of `createApp`'s deps this factory reads. */
|
|
24
|
+
export interface HeldRunReleaseInputs {
|
|
25
|
+
/**
|
|
26
|
+
* Re-dispatch a released job-scoped hold. Supplied by both mode hooks through
|
|
27
|
+
* `appDepsExtras`; its absence is what makes the whole surface absent.
|
|
28
|
+
*/
|
|
29
|
+
onJobRelease?: ((signal: ReleaseSignal) => Promise<void>) | undefined;
|
|
30
|
+
/** Replay the stored dispatch context of a released workflow-scoped hold. */
|
|
31
|
+
onWorkflowRelease?: ((signal: ReleaseSignal) => Promise<void>) | undefined;
|
|
32
|
+
/** The live processing-deps bag, assembled per call. */
|
|
33
|
+
buildProcessingDeps: () => ProcessingDeps;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The reason a workflow-scoped hold rejected through this surface records on the
|
|
37
|
+
* run it cancels, when the operator supplied none.
|
|
38
|
+
*
|
|
39
|
+
* Scope-neutral on purpose: the install gate, the org trust policy's PR-wide
|
|
40
|
+
* hold and an SDK workflow-level `requireApproval` all arrive here, so a
|
|
41
|
+
* reason naming one of them would mislabel the other two.
|
|
42
|
+
*/
|
|
43
|
+
export declare const DEFAULT_LOCAL_REJECT_REASON = "Workflow hold rejected by the orchestrator operator";
|
|
44
|
+
export declare function buildHeldRunRelease(inputs: HeldRunReleaseInputs): HeldRunReleaseWiring | undefined;
|
|
45
|
+
//# sourceMappingURL=held-run-release-wiring.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The approval subsystem an INDEPENDENT orchestrator runs on.
|
|
3
|
+
*
|
|
4
|
+
* `server.ts` composes this inline for the three Platform-attached modes. An
|
|
5
|
+
* independent orchestrator has no Platform and therefore none of that hook, so
|
|
6
|
+
* every gate that raises a hold was inert there: a fork PR the org policy said
|
|
7
|
+
* to HOLD was dropped instead of held, an SDK `requireApproval` job ran
|
|
8
|
+
* UNGATED (the dispatch site logged an error and dispatched anyway), and the
|
|
9
|
+
* stale detector had no store through which to expire an overdue hold,
|
|
10
|
+
* terminalize its `KiCI Security` check, or drop the pending dispatch context
|
|
11
|
+
* it would have replayed.
|
|
12
|
+
*
|
|
13
|
+
* It lives here rather than inside `standalone.ts` because that module is an
|
|
14
|
+
* entry point: it runs `guardStartup` at import time, so nothing can construct
|
|
15
|
+
* its wiring in a test. A factory can be constructed, and the fields it
|
|
16
|
+
* produces can be asserted — which is the whole point, since every defect this
|
|
17
|
+
* closes was a missing field rather than a wrong algorithm.
|
|
18
|
+
*
|
|
19
|
+
* What it deliberately does NOT produce is a `stepApprovalBridge`. A
|
|
20
|
+
* step-scoped hold is opened by the agent WS `onStepApproval` seam and answered
|
|
21
|
+
* by the dashboard applier, which is Platform-relayed and has no
|
|
22
|
+
* independent-mode equivalent — so wiring the bridge would let an agent open a
|
|
23
|
+
* hold nothing could resolve short of expiry.
|
|
24
|
+
*/
|
|
25
|
+
import type { Kysely } from 'kysely';
|
|
26
|
+
import type { Database } from '../db/types.js';
|
|
27
|
+
import { HeldRunStore, type ReleaseSignal } from '../contexts/held-runs.js';
|
|
28
|
+
import { type ProcessingDeps, type ReadyDispatchContextRow } from '../pipeline/processor.js';
|
|
29
|
+
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
30
|
+
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
31
|
+
import type { RunCoordinator } from '../cluster/coordinator.js';
|
|
32
|
+
import type { InvokeGateDeps } from '../pipeline/invoke-gate.js';
|
|
33
|
+
/** The subsystem fields the composition reads. A subset of `OrchestratorSubsystems`. */
|
|
34
|
+
export interface IndependentApprovalSubsystems {
|
|
35
|
+
db: Kysely<Database>;
|
|
36
|
+
dispatcher: Dispatcher;
|
|
37
|
+
executionTracker?: ExecutionTracker | undefined;
|
|
38
|
+
coordinator?: RunCoordinator | undefined;
|
|
39
|
+
invokeGateDeps?: InvokeGateDeps | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The live direct-ingress deps bag. Read lazily inside the release callbacks:
|
|
42
|
+
* it is populated by `createApp`, which runs after the mode hook, and reading
|
|
43
|
+
* it at wiring time throws.
|
|
44
|
+
*/
|
|
45
|
+
buildProcessingDeps: () => ProcessingDeps;
|
|
46
|
+
}
|
|
47
|
+
/** What the independent mode hook merges into `appDepsExtras`. */
|
|
48
|
+
export interface IndependentApprovalExtras {
|
|
49
|
+
heldRunStore: HeldRunStore;
|
|
50
|
+
onWorkflowRelease: (signal: ReleaseSignal) => Promise<void>;
|
|
51
|
+
onJobRelease: (signal: ReleaseSignal) => Promise<void>;
|
|
52
|
+
matchContext: (orgId: string, name: string) => Promise<ReadyDispatchContextRow | null>;
|
|
53
|
+
}
|
|
54
|
+
export declare function createIndependentApprovalExtras(sub: IndependentApprovalSubsystems): IndependentApprovalExtras;
|
|
55
|
+
//# sourceMappingURL=independent-wiring.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReleaseSignal } from '../contexts/held-runs.js';
|
|
2
|
+
/** The three resume paths a released hold can take. */
|
|
3
|
+
export interface ResumeHandlers {
|
|
4
|
+
onStepRelease?: (signal: ReleaseSignal) => Promise<void>;
|
|
5
|
+
onWorkflowRelease?: (signal: ReleaseSignal) => Promise<void>;
|
|
6
|
+
onJobRelease: (signal: ReleaseSignal) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export declare function routeRelease(signal: ReleaseSignal, handlers: ResumeHandlers): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=resume-router.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `service:` namespace both approving surfaces use for a non-human
|
|
3
|
+
* principal: `kici-admin`'s token identity (its only subject), and the
|
|
4
|
+
* dashboard's rendering of a `service_account` actor.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SERVICE_SUBJECT_PREFIX = "service:";
|
|
7
|
+
/** Render an admin-token user id in the shared `service:` namespace. */
|
|
8
|
+
export declare function adminActorSub(tokenUserId: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the subject of whoever triggered a run, in the approver vocabulary.
|
|
11
|
+
*
|
|
12
|
+
* | `triggered_by` | Returns | Why |
|
|
13
|
+
* |---|---|---|
|
|
14
|
+
* | `user:<sub>` | `<sub>` | what a `{user}` clause and both surfaces' own subject use |
|
|
15
|
+
* | `platform_operator:<sub>` | `<sub>` | same Keycloak subject as a `user` |
|
|
16
|
+
* | `service_account:<id>` | `service:<id>` | the namespace {@link adminActorSub} and the dashboard both produce |
|
|
17
|
+
* | `system:<component>` | `system:<component>` | the dashboard renders a system actor with its prefix intact |
|
|
18
|
+
* | `api_key:<keyId>` | `<keyId>` | see the residual below |
|
|
19
|
+
* | anything else | unchanged | no other actor type reaches these surfaces |
|
|
20
|
+
*
|
|
21
|
+
* The agent suffix is stripped first. `stringifyActor` renders a `user` or
|
|
22
|
+
* `api_key` who acted through an agent as `user:<sub> via agent:<label>`, and a
|
|
23
|
+
* reader splitting on the first colon gets `<sub> via agent:<label>` — which
|
|
24
|
+
* matches no live subject, so the gate was inert for every agent-mediated
|
|
25
|
+
* trigger. That is the `user` case, i.e. the common one.
|
|
26
|
+
*
|
|
27
|
+
* **Residual: `api_key`.** A key's persisted identifier is its `keyId`, while
|
|
28
|
+
* both surfaces render a live `api_key` actor as its owner's Keycloak subject
|
|
29
|
+
* (`ownerSub`). Those are different identifiers for the same principal and no
|
|
30
|
+
* string transform bridges them — closing it needs a keyId → ownerSub lookup,
|
|
31
|
+
* which is a store this pure function does not have. So an owner approving a
|
|
32
|
+
* hold on a run their own key triggered is still admitted. Recorded rather than
|
|
33
|
+
* papered over: returning the bare keyId is at least honest about what was
|
|
34
|
+
* stored.
|
|
35
|
+
*/
|
|
36
|
+
export declare function triggererSubjectFor(triggeredBy: string | null | undefined): string | undefined;
|
|
37
|
+
//# sourceMappingURL=triggerer-subject.d.ts.map
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dependency-specific cache layer wrapping CacheStorage.
|
|
3
3
|
*
|
|
4
|
-
* Stores
|
|
5
|
-
*
|
|
6
|
-
* Refreshes TTL on reads
|
|
4
|
+
* Stores dependency tarballs under their own content hash, with a small pointer
|
|
5
|
+
* resolving lockfileHash + platform + arch to that hash. Shared CacheStorage
|
|
6
|
+
* backend with SourceCache (same S3 bucket). Refreshes TTL on reads
|
|
7
|
+
* (touch-on-read).
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
+
* Keys: `deps/{platform}-{arch}/{depsHash}.tar.gz` (immutable) and
|
|
10
|
+
* `deps/{platform}-{arch}/{lockfileHash}.hash` (the pointer).
|
|
9
11
|
*/
|
|
10
12
|
import type { CacheStorage } from '../storage/types.js';
|
|
11
13
|
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
14
|
+
/**
|
|
15
|
+
* Cache key for a dependency tarball, addressed by the tarball's OWN content
|
|
16
|
+
* hash: `deps/{platform}-{arch}/{depsHash}.tar.gz`.
|
|
17
|
+
*
|
|
18
|
+
* Content-addressing is what makes a mismatched pair unrepresentable. When the
|
|
19
|
+
* tarball lived at a lockfile-derived name, it and its companion `.hash` were
|
|
20
|
+
* two independently-written objects under keys that any two builds sharing a
|
|
21
|
+
* lockfile + platform + arch also share — so concurrent builders could leave the
|
|
22
|
+
* `.tar.gz` from one and the `.hash` from the other, and the reader failed
|
|
23
|
+
* verification durably. Naming the object by its own hash means every pair a
|
|
24
|
+
* reader can observe is self-consistent, and the bytes at a given key can never
|
|
25
|
+
* change after a URL is signed for it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function depTarballKey(depsHash: string, platform: string, arch: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Cache key for the pointer that resolves a lockfile to the content hash of the
|
|
30
|
+
* tarball built from it: `deps/{platform}-{arch}/{lockfileHash}.hash`.
|
|
31
|
+
*
|
|
32
|
+
* This is the one mutable object in the scheme. A concurrent write replaces a
|
|
33
|
+
* pointer wholesale — there is no window in which it is half-written — so the
|
|
34
|
+
* worst a racing pair of builders can do is leave whichever pointer landed last,
|
|
35
|
+
* and both tarballs remain valid and fetchable.
|
|
36
|
+
*/
|
|
37
|
+
export declare function depPointerKey(lockfileHash: string, platform: string, arch: string): string;
|
|
12
38
|
export declare class DepCache {
|
|
13
39
|
private readonly storage;
|
|
14
40
|
private readonly maxTarballBytes;
|
|
@@ -30,7 +56,17 @@ export declare class DepCache {
|
|
|
30
56
|
* `cache_ttl_days` change takes effect on the next lookup.
|
|
31
57
|
*/
|
|
32
58
|
private resolveTtlMsOverride;
|
|
33
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a lockfile to the content hash of the tarball built from it, or
|
|
61
|
+
* null when no pointer exists. The single place the indirection is read.
|
|
62
|
+
*/
|
|
63
|
+
private resolvePointer;
|
|
64
|
+
/**
|
|
65
|
+
* Check if a dep tarball exists in cache.
|
|
66
|
+
*
|
|
67
|
+
* Both halves must be present: a pointer whose tarball has aged out is a miss,
|
|
68
|
+
* not a hit, or the caller skips a rebuild and dispatches a URL that 404s.
|
|
69
|
+
*/
|
|
34
70
|
has(lockfileHash: string, platform: string, arch: string): Promise<boolean>;
|
|
35
71
|
/**
|
|
36
72
|
* Get a pre-signed download URL for the dep tarball (for agent delivery).
|
|
@@ -38,9 +74,10 @@ export declare class DepCache {
|
|
|
38
74
|
*/
|
|
39
75
|
getUrl(lockfileHash: string, platform: string, arch: string): Promise<string | null>;
|
|
40
76
|
/**
|
|
41
|
-
* Get a pre-signed download URL and the tarball content hash
|
|
42
|
-
*
|
|
43
|
-
*
|
|
77
|
+
* Get a pre-signed download URL and the tarball content hash.
|
|
78
|
+
*
|
|
79
|
+
* Returns null on cache miss — including an entry with no pointer, which is
|
|
80
|
+
* unverifiable and so is deliberately not served.
|
|
44
81
|
*/
|
|
45
82
|
getUrlAndHash(lockfileHash: string, platform: string, arch: string): Promise<{
|
|
46
83
|
url: string;
|
|
@@ -49,7 +86,14 @@ export declare class DepCache {
|
|
|
49
86
|
/**
|
|
50
87
|
* Get a pre-signed upload URL for direct agent-to-S3 upload.
|
|
51
88
|
*/
|
|
52
|
-
getUploadUrl(
|
|
89
|
+
getUploadUrl(depsHash: string, platform: string, arch: string): Promise<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Publish the pointer that makes an uploaded tarball discoverable by lockfile.
|
|
92
|
+
*
|
|
93
|
+
* Called only after the agent confirms its upload completed. Publishing before
|
|
94
|
+
* the bytes land would let a reader follow the pointer to a missing object.
|
|
95
|
+
*/
|
|
96
|
+
publishPointer(lockfileHash: string, platform: string, arch: string, depsHash: string): Promise<void>;
|
|
53
97
|
/**
|
|
54
98
|
* Store a dep tarball in cache.
|
|
55
99
|
* Throws if tarball exceeds max size (per user decision).
|
|
@@ -60,7 +104,7 @@ export declare class DepCache {
|
|
|
60
104
|
* Used by build agents to compute the hash for depsHash protocol field.
|
|
61
105
|
*/
|
|
62
106
|
static computeHash(data: Buffer): string;
|
|
63
|
-
/** Remove a dep tarball from cache. */
|
|
107
|
+
/** Remove a dep tarball and its pointer from cache. */
|
|
64
108
|
remove(lockfileHash: string, platform: string, arch: string): Promise<boolean>;
|
|
65
109
|
}
|
|
66
110
|
//# sourceMappingURL=dep-cache.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LRU for completed Tier-2 global eval round results.
|
|
3
|
+
*
|
|
4
|
+
* **This cache only helps webhook redelivery and same-input re-deliveries.** Its
|
|
5
|
+
* key pins the source commit and a digest of the whole round input, and every
|
|
6
|
+
* real push carries a new source SHA — so a developer pushing twice gets two
|
|
7
|
+
* rounds, by design. Read it as a duplicate-delivery guard, never as a
|
|
8
|
+
* steady-state optimization: sizing it up buys nothing, and a hit rate near zero
|
|
9
|
+
* on a healthy cluster is the expected shape, not a defect.
|
|
10
|
+
*
|
|
11
|
+
* There is deliberately no TTL. A round verdict is a pure function of its
|
|
12
|
+
* inputs, and {@link globalEvalRoundCacheKey} covers all of them, so an entry
|
|
13
|
+
* cannot go stale within its own key — the same content-addressable argument the
|
|
14
|
+
* lock-file and content caches make for their SHA-keyed entries, without their
|
|
15
|
+
* branch-name escape hatch.
|
|
16
|
+
*/
|
|
17
|
+
import type { GlobalEvalRoundResult } from '@kici-dev/engine';
|
|
18
|
+
/**
|
|
19
|
+
* Key one round over **every input its verdict depends on**.
|
|
20
|
+
*
|
|
21
|
+
* The three SHAs are not enough, and the gap is a wrong-answer bug rather than a
|
|
22
|
+
* missed-hit one. `createFilterContext` and `buildGeneratorContext` both receive
|
|
23
|
+
* the whole event, so a filter can branch on `event.type`, `targetBranch`,
|
|
24
|
+
* `changedFiles`, or anything in the raw payload. A push to `main` at commit X
|
|
25
|
+
* and a pull-request synchronize whose head is commit X share all three SHAs and
|
|
26
|
+
* genuinely deserve different verdicts: their events differ, and their
|
|
27
|
+
* changed-file sets are computed over different ranges. Two branches pointing at
|
|
28
|
+
* one commit collide the same way. `workflowRoutingKey` is in the key for the
|
|
29
|
+
* reason `groupCandidates` keeps it in the group key — it selects the provider
|
|
30
|
+
* bundle that mints the clone credentials.
|
|
31
|
+
*
|
|
32
|
+
* `sourceRepoIdentifier` is its own component, placed next to `sourceSha` so the
|
|
33
|
+
* source repo and its commit stay adjacent. Every event already carries its
|
|
34
|
+
* source repo, so the event digest covers it too — but keying it explicitly
|
|
35
|
+
* makes that coverage structural rather than incidental: an event shape that
|
|
36
|
+
* stopped carrying the source repo could otherwise collide two repos' rounds.
|
|
37
|
+
*
|
|
38
|
+
* The candidate list and the event are folded in as a SHA-256 digest of their
|
|
39
|
+
* JSON, so the key stays a bounded string no matter how large a payload is. Both
|
|
40
|
+
* are rebuilt by the same code from the same delivery, so a genuine redelivery
|
|
41
|
+
* reproduces the digest; anything else is a miss, which costs a round rather
|
|
42
|
+
* than a wrong verdict.
|
|
43
|
+
*
|
|
44
|
+
* Returns `null` when the inputs cannot be serialized. A round that cannot be
|
|
45
|
+
* keyed is simply not cached — never keyed on a partial input, which is how a
|
|
46
|
+
* cache starts answering questions it was not asked.
|
|
47
|
+
*/
|
|
48
|
+
export declare function globalEvalRoundCacheKey(args: {
|
|
49
|
+
workflowRepoIdentifier: string;
|
|
50
|
+
workflowSha: string;
|
|
51
|
+
workflowRoutingKey: string;
|
|
52
|
+
sourceRepoIdentifier: string;
|
|
53
|
+
sourceSha: string;
|
|
54
|
+
/** The exact per-candidate payload the round job carries. */
|
|
55
|
+
candidates: unknown;
|
|
56
|
+
/** The exact event the round job carries. */
|
|
57
|
+
event: unknown;
|
|
58
|
+
}): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* A round that left ANY candidate undecided is not a result worth replaying.
|
|
61
|
+
*
|
|
62
|
+
* An agent-side budget breach reports `success` with the affected candidates
|
|
63
|
+
* marked indeterminate — the whole round on a round-budget breach, one workflow
|
|
64
|
+
* on a candidate-budget breach. Caching either turns one slow round into a
|
|
65
|
+
* permanently replayed failure for the whole key: a webhook redelivery, which
|
|
66
|
+
* is exactly when an operator is retrying, would be served the stored failure
|
|
67
|
+
* instead of running again, and the round's own retry sits behind this cache
|
|
68
|
+
* read so a stored breach short-circuits that too.
|
|
69
|
+
*
|
|
70
|
+
* Requiring EVERY candidate to be decided costs almost no hit rate — an
|
|
71
|
+
* indeterminate verdict is the exceptional path — and buys a guarantee that is
|
|
72
|
+
* easy to state: nothing a redelivery replays was ever undecided.
|
|
73
|
+
*
|
|
74
|
+
* An empty candidate list is not cacheable either. For a non-empty group it
|
|
75
|
+
* means every candidate is about to be recorded as "no verdict", which is the
|
|
76
|
+
* same failure by another route.
|
|
77
|
+
*/
|
|
78
|
+
export declare function isCacheableRoundResult(result: GlobalEvalRoundResult): boolean;
|
|
79
|
+
export declare class GlobalEvalRoundCache {
|
|
80
|
+
private readonly cache;
|
|
81
|
+
private hits;
|
|
82
|
+
private misses;
|
|
83
|
+
constructor(options: {
|
|
84
|
+
max: number;
|
|
85
|
+
});
|
|
86
|
+
get(key: string): GlobalEvalRoundResult | undefined;
|
|
87
|
+
set(key: string, value: GlobalEvalRoundResult): void;
|
|
88
|
+
/** Hit/miss counters for metrics and debugging. */
|
|
89
|
+
stats(): {
|
|
90
|
+
hits: number;
|
|
91
|
+
misses: number;
|
|
92
|
+
size: number;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=global-eval-round-cache.d.ts.map
|
package/dist/cache/index.d.ts
CHANGED
|
@@ -15,4 +15,7 @@ export { PendingBuildTracker } from './pending-builds.js';
|
|
|
15
15
|
export { PendingInitTracker } from './pending-inits.js';
|
|
16
16
|
export type { InitResult } from './pending-inits.js';
|
|
17
17
|
export { PendingDynamicTracker } from './pending-dynamics.js';
|
|
18
|
+
export { PendingGlobalEvalTracker } from './pending-global-evals.js';
|
|
19
|
+
export { GlobalEvalRoundCache, globalEvalRoundCacheKey, isCacheableRoundResult, } from './global-eval-round-cache.js';
|
|
20
|
+
export { parseGlobalEvalResult } from './pending-global-evals.js';
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending tracker for the Tier-2 global eval round.
|
|
3
|
+
*
|
|
4
|
+
* The orchestrator dispatches one round job per (event × workflow repo) and
|
|
5
|
+
* waits for the agent to return a verdict per candidate workflow. The
|
|
6
|
+
* underlying tracker logic lives in `PendingTracker<GlobalEvalRoundResult>`;
|
|
7
|
+
* this subclass wires the round-specific logger prefix and disconnect error.
|
|
8
|
+
*
|
|
9
|
+
* Unlike the dynamic-eval and init trackers, this round runs BEFORE any run row
|
|
10
|
+
* exists: its whole purpose is to decide which global workflows produce a run at
|
|
11
|
+
* all, so creating one up-front would defeat it.
|
|
12
|
+
*/
|
|
13
|
+
import { type GlobalEvalRoundResult } from '@kici-dev/engine';
|
|
14
|
+
import { PendingTracker } from './pending-tracker.js';
|
|
15
|
+
/**
|
|
16
|
+
* Validate the round result carried on a `job.status` message before it reaches
|
|
17
|
+
* the tracker.
|
|
18
|
+
*
|
|
19
|
+
* **This is the one place the round's wire payload is validated.** The status
|
|
20
|
+
* message's `data` field is `z.record(z.string(), z.unknown())`, so nothing
|
|
21
|
+
* upstream checks `globalEvalResult` against its schema — a cast at the message
|
|
22
|
+
* handler would be a type assertion over an arbitrary agent-supplied value, and
|
|
23
|
+
* any agent that owns the round job can send one. Parsing here keeps the
|
|
24
|
+
* "typed but unvalidated" boundary at a single named function instead of spread
|
|
25
|
+
* across every consumer.
|
|
26
|
+
*
|
|
27
|
+
* The parsed value is widened back to {@link GlobalEvalRoundResult}: the schema
|
|
28
|
+
* is deliberately loose about `jobs` (the lock-job shape has no Zod mirror), and
|
|
29
|
+
* the dispatch path re-checks every generated job before it builds anything from
|
|
30
|
+
* one.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseGlobalEvalResult(raw: unknown): {
|
|
33
|
+
ok: true;
|
|
34
|
+
value: GlobalEvalRoundResult;
|
|
35
|
+
} | {
|
|
36
|
+
ok: false;
|
|
37
|
+
error: string;
|
|
38
|
+
};
|
|
39
|
+
export declare class PendingGlobalEvalTracker extends PendingTracker<GlobalEvalRoundResult> {
|
|
40
|
+
constructor();
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=pending-global-evals.d.ts.map
|
|
@@ -18,6 +18,16 @@ export interface InitResult {
|
|
|
18
18
|
* function. The dispatch path re-materializes these into N execution jobs.
|
|
19
19
|
*/
|
|
20
20
|
matrixValues?: Array<Record<string, string | undefined>>;
|
|
21
|
+
/**
|
|
22
|
+
* Verdict of the workflow-level `filter`, reported only when the init job was
|
|
23
|
+
* asked to evaluate one. `false` means the workflow does not apply to this
|
|
24
|
+
* event and its job must not be dispatched.
|
|
25
|
+
*
|
|
26
|
+
* Optional on purpose: an agent that predates the filter never sends it, so
|
|
27
|
+
* absence means "no verdict was reported" and dispatch proceeds — only an
|
|
28
|
+
* explicit `false` suppresses.
|
|
29
|
+
*/
|
|
30
|
+
filterPassed?: boolean;
|
|
21
31
|
}
|
|
22
32
|
export declare class PendingInitTracker extends PendingTracker<InitResult> {
|
|
23
33
|
constructor();
|
package/dist/cli/api-client.d.ts
CHANGED
|
@@ -84,6 +84,24 @@ export interface GenericSourceResponse {
|
|
|
84
84
|
* print an empty one.
|
|
85
85
|
*/
|
|
86
86
|
export declare function firstCauseMessage(err: unknown): string;
|
|
87
|
+
/**
|
|
88
|
+
* True when a rejected `fetch` failed at the transport layer, as opposed to
|
|
89
|
+
* failing while the request was still being built.
|
|
90
|
+
*
|
|
91
|
+
* `fetch` uses a `TypeError` for both, so they are easy to conflate — but only
|
|
92
|
+
* a transport failure means the orchestrator was actually dialled. Measured
|
|
93
|
+
* against Node 24's undici:
|
|
94
|
+
*
|
|
95
|
+
* closed port → TypeError('fetch failed') cause: Error(ECONNREFUSED)
|
|
96
|
+
* invalid header → TypeError('Headers.append: … invalid header …') cause: undefined
|
|
97
|
+
* invalid URL → TypeError('Failed to parse URL from …') cause: Error(ERR_INVALID_URL)
|
|
98
|
+
*
|
|
99
|
+
* The discriminator is therefore the message, NOT the presence of a `cause`:
|
|
100
|
+
* the invalid-URL case carries one. undici uses exactly `fetch failed` for a
|
|
101
|
+
* transport failure and a descriptive message for everything it rejects before
|
|
102
|
+
* opening a connection.
|
|
103
|
+
*/
|
|
104
|
+
export declare function isTransportFailure(err: unknown): boolean;
|
|
87
105
|
export declare function fetchAdminApi(url: string, init: RequestInit, baseUrl: string): Promise<Response>;
|
|
88
106
|
export declare class AdminApiClient {
|
|
89
107
|
private readonly baseUrl;
|
|
@@ -25,14 +25,63 @@ interface StringKnobSpec {
|
|
|
25
25
|
expects: string;
|
|
26
26
|
validate: (value: string) => boolean;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* One boolean knob. A third spec type for the same reason {@link StringKnobSpec}
|
|
30
|
+
* is separate from {@link KnobSpec}: the value parser, the PATCH body type, and
|
|
31
|
+
* the `show` formatter all differ. A boolean also has no bounds to validate —
|
|
32
|
+
* only two accepted spellings.
|
|
33
|
+
*/
|
|
34
|
+
interface BooleanKnobSpec {
|
|
35
|
+
field: string;
|
|
36
|
+
flag: string;
|
|
37
|
+
label: string;
|
|
38
|
+
}
|
|
28
39
|
/** camelCase → value (null clears the override). */
|
|
29
|
-
type PatchBody = Record<string, number | string | null>;
|
|
30
|
-
/**
|
|
31
|
-
|
|
40
|
+
type PatchBody = Record<string, number | string | boolean | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Parse an integer CLI flag against its knob's bounds, exiting on failure.
|
|
43
|
+
*
|
|
44
|
+
* `max` is optional because most knobs have no meaningful ceiling; the ones
|
|
45
|
+
* that do carry a hard boot-safety bound (see {@link KnobSpec.max}).
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseKnobValue(flag: string, value: string, min: number, max?: number): number;
|
|
32
48
|
/** Validate a text CLI flag against its knob's shape, exiting on failure. */
|
|
33
49
|
export declare function parseStringKnobValue(knob: StringKnobSpec, value: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Parse a boolean CLI flag, exiting on failure.
|
|
52
|
+
*
|
|
53
|
+
* Accepts only the exact strings `true` and `false`. A permissive parser (any
|
|
54
|
+
* non-empty string is true) would read `--global-workflows-enabled no` as an
|
|
55
|
+
* enable, which for this knob turns a typo into a security posture change.
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseBooleanKnobValue(knob: BooleanKnobSpec, value: string): boolean;
|
|
34
58
|
/** Assemble the PATCH body from provided `set` flags (exits if none given). */
|
|
35
59
|
export declare function buildClusterPatch(opts: Record<string, string | undefined>): PatchBody;
|
|
60
|
+
/**
|
|
61
|
+
* Warn when only one of the two ordered global-eval timeouts is being set.
|
|
62
|
+
*
|
|
63
|
+
* The orchestrator's wait ceiling must exceed the agent's round budget — the
|
|
64
|
+
* agent's budget starts only once the round job is RUNNING, so a lower ceiling
|
|
65
|
+
* fires on every round that merely waited for a free agent, failing every round
|
|
66
|
+
* silently. The server rejects the pair outright when BOTH effective values are
|
|
67
|
+
* stored, but it cannot compare against a column left NULL: NULL means "the
|
|
68
|
+
* orchestrator's configured default applies", and the route does not know that
|
|
69
|
+
* number. Setting one alone is exactly that blind spot, so warn here.
|
|
70
|
+
*
|
|
71
|
+
* Returns the lines rather than printing them so the check is unit-testable.
|
|
72
|
+
*/
|
|
73
|
+
export declare function unpairedEvalTimeoutWarnings(patch: PatchBody): string[];
|
|
74
|
+
/**
|
|
75
|
+
* Warn for every deprecated knob the patch sets.
|
|
76
|
+
*
|
|
77
|
+
* The set still goes through — the column and the route field are a released
|
|
78
|
+
* operator surface and keep accepting writes — so the warning says what the
|
|
79
|
+
* stored value now does, which is nothing.
|
|
80
|
+
*
|
|
81
|
+
* Returns the lines rather than printing them so the check is unit-testable,
|
|
82
|
+
* matching {@link unpairedEvalTimeoutWarnings}.
|
|
83
|
+
*/
|
|
84
|
+
export declare function deprecatedKnobWarnings(patch: PatchBody): string[];
|
|
36
85
|
/** Build the reset PATCH body: all knobs → null, or just the flagged ones. */
|
|
37
86
|
export declare function buildClusterReset(opts: Record<string, boolean | undefined>): PatchBody;
|
|
38
87
|
/**
|
|
@@ -11,5 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Command } from 'commander';
|
|
13
13
|
import type { AdminApiClient } from '../api-client.js';
|
|
14
|
+
/**
|
|
15
|
+
* Width of the table's Message column. A check message is cut at this many
|
|
16
|
+
* characters, so anything a check wants an operator to see in the default
|
|
17
|
+
* (non-`--json`) output has to fit inside it.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DIAGNOSE_TABLE_MESSAGE_WIDTH = 42;
|
|
14
20
|
export declare function registerDiagnoseCommand(program: Command, getClient: () => AdminApiClient): void;
|
|
15
21
|
//# sourceMappingURL=diagnose.d.ts.map
|