@kici-dev/orchestrator 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +23 -0
- package/dist/agent/dispatcher.d.ts +81 -13
- package/dist/agent/host-roster.d.ts +37 -5
- package/dist/agent/registry.d.ts +2 -0
- package/dist/app.d.ts +33 -6
- 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 +7 -0
- package/dist/cli/api-client.d.ts +18 -0
- package/dist/cli/commands/cluster-settings.d.ts +11 -0
- package/dist/cli/commands/diagnose.d.ts +6 -0
- package/dist/cli/commands/held-run.d.ts +58 -0
- package/dist/cli/commands/trust-policy.d.ts +117 -12
- package/dist/cli/kici-admin.d.ts +10 -0
- package/dist/cli.js +2272 -1025
- package/dist/cluster/cluster-identity.d.ts +3 -3
- package/dist/cluster/cluster-settings-reader.d.ts +7 -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 +21 -21
- 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/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 +368 -10
- 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 +2 -2
- package/dist/metrics/prometheus.d.ts +111 -3
- package/dist/oidc/oidc-mint-registration.d.ts +7 -3
- package/dist/orchestrator-core.d.ts +79 -23
- package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +302 -31
- package/dist/pipeline/global-eval-round.d.ts +14 -0
- package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
- package/dist/pipeline/invoke-gate.d.ts +164 -0
- package/dist/pipeline/needs-scheduler.d.ts +13 -0
- package/dist/pipeline/process-webhook.d.ts +119 -31
- package/dist/pipeline/processor.d.ts +226 -21
- 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 +9 -0
- package/dist/pipeline/security-hold-check.d.ts +217 -0
- package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
- package/dist/policy/dashboard-write-policy.d.ts +14 -0
- package/dist/provider-registry.d.ts +10 -2
- package/dist/providers/github/check-status-poster.d.ts +11 -0
- package/dist/providers/github/index.d.ts +0 -2
- package/dist/providers/github/normalizer.d.ts +3 -2
- package/dist/providers/local/normalizer.d.ts +3 -5
- package/dist/providers/universal-git/index.d.ts +11 -2
- package/dist/queue/job-queue.d.ts +43 -1
- package/dist/registration/extractor.d.ts +3 -3
- package/dist/registration/registration-index.d.ts +7 -0
- package/dist/registration/registration-store.d.ts +22 -0
- package/dist/reporting/check-run-reporter.d.ts +94 -1
- package/dist/reporting/check-run-summary.d.ts +25 -1
- package/dist/reporting/execution-tracker.d.ts +207 -2
- 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-trust-policy.d.ts +34 -6
- package/dist/routes/admin.d.ts +9 -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 +899 -72
- 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/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 +17684 -7351
- 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 +24238 -13822
- package/dist/worker/in-memory-job-queue.d.ts +1 -1
- package/dist/ws/agent-handler.d.ts +28 -4
- package/dist/ws/dashboard-context-handler.d.ts +28 -4
- package/dist/ws/dashboard-global-workflows-handler.d.ts +6 -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 +54 -9
- package/installer-image-digests.json +3 -3
- package/package.json +24 -19
- package/sbom.spdx.json +864 -822
- package/dist/approvals/team-membership-lookup.d.ts +0 -13
- package/dist/providers/github/contributor-resolver.d.ts +0 -30
- package/dist/security/contributor-cache.d.ts +0 -83
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { type CheckStatus, type CheckStatusPoster } from '@kici-dev/engine';
|
|
2
|
+
import type { Kysely } from 'kysely';
|
|
3
|
+
import type { Database } from '../db/types.js';
|
|
4
|
+
/** Resolve the check poster of the provider bundle a routing key is served by. */
|
|
5
|
+
export type ResolveCheckStatusPoster = (routingKey: string) => CheckStatusPoster | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* The `held_runs` columns this module reads. A full `HeldRun` row satisfies it,
|
|
8
|
+
* so every caller passes the row it already loaded rather than a projection.
|
|
9
|
+
*/
|
|
10
|
+
export interface SecurityCheckHold {
|
|
11
|
+
id: string;
|
|
12
|
+
org_id: string;
|
|
13
|
+
run_id: string;
|
|
14
|
+
job_id: string;
|
|
15
|
+
hold_scope: string;
|
|
16
|
+
hold_type: string;
|
|
17
|
+
approval_requirement: unknown;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the pending post reached the provider. Required rather than
|
|
20
|
+
* optional so a query that projects columns by name cannot omit it and fall
|
|
21
|
+
* back to the shape derivation without the compiler saying so.
|
|
22
|
+
*/
|
|
23
|
+
posted_pending_check: boolean | null;
|
|
24
|
+
}
|
|
25
|
+
/** How a hold ended, which decides the conclusion, title and summary. */
|
|
26
|
+
export declare enum HoldOutcome {
|
|
27
|
+
/** Released to run — an approver satisfied it. */
|
|
28
|
+
Approved = "approved",
|
|
29
|
+
/** Refused by an approver. */
|
|
30
|
+
Rejected = "rejected",
|
|
31
|
+
/** Nobody answered inside the approval window. */
|
|
32
|
+
Expired = "expired"
|
|
33
|
+
}
|
|
34
|
+
/** Why {@link settleSecurityHoldCheck} did or did not write the check. */
|
|
35
|
+
export declare enum SecurityCheckSettlement {
|
|
36
|
+
/** A terminal status was written. */
|
|
37
|
+
Posted = "posted",
|
|
38
|
+
/** This hold posted no pending check, so it has none to terminalize. */
|
|
39
|
+
NotOwned = "not-owned",
|
|
40
|
+
/** The caller already wrote this commit's check earlier in the same pass. */
|
|
41
|
+
AlreadySettled = "already-settled",
|
|
42
|
+
/** Another pending hold on the same commit still owns the check. */
|
|
43
|
+
Contended = "contended",
|
|
44
|
+
/** The hold's run row is gone, so the commit cannot be named. */
|
|
45
|
+
NoCommit = "no-commit",
|
|
46
|
+
/** No provider bundle serves the run's routing key any more. */
|
|
47
|
+
NoPoster = "no-poster",
|
|
48
|
+
/**
|
|
49
|
+
* The settlement could not complete — the provider refused the write, or the
|
|
50
|
+
* hold row did not carry the columns the ownership predicate reads.
|
|
51
|
+
*/
|
|
52
|
+
Failed = "failed"
|
|
53
|
+
}
|
|
54
|
+
export interface SettleSecurityCheckResult {
|
|
55
|
+
outcome: SecurityCheckSettlement;
|
|
56
|
+
/** Whether a terminal status actually reached the provider. */
|
|
57
|
+
posted: boolean;
|
|
58
|
+
/** `owner/repo@sha`, once the hold's commit resolved. */
|
|
59
|
+
commit?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Whether this hold put a pending `KiCI Security` check on its commit, and so
|
|
63
|
+
* has one to terminalize when it ends.
|
|
64
|
+
*
|
|
65
|
+
* `posted_pending_check` is the answer whenever the row carries one: it is
|
|
66
|
+
* written `true` only after the provider accepted the post, so it records what
|
|
67
|
+
* HAPPENED. Everything below records what the code INTENDED, which is a
|
|
68
|
+
* different question and answers the wrong way twice — a post the provider
|
|
69
|
+
* refused, and a hold reached with no check poster in `ctx.bundle`, both leave
|
|
70
|
+
* a shape that says "posted" and a commit that has nothing. Terminalizing then
|
|
71
|
+
* CREATES the check run, so the mirror of the stuck-check leak is a fabricated
|
|
72
|
+
* failing check on a pull request.
|
|
73
|
+
*
|
|
74
|
+
* The shape derivation stays for `null` — a row written before the column
|
|
75
|
+
* existed, for which no fact was recorded and an inference is all there is.
|
|
76
|
+
*
|
|
77
|
+
* Three sites post that pending status, and the derivation is read off them
|
|
78
|
+
* rather than off the queue type — which cannot make the distinction, because a
|
|
79
|
+
* workflow install gate whose context protection rule is security-typed carries
|
|
80
|
+
* `queue_type = 'security'` and posts nothing at all.
|
|
81
|
+
*
|
|
82
|
+
* | Hold | Written by | Posts pending? | Recognised here by |
|
|
83
|
+
* |---|---|---|---|
|
|
84
|
+
* | org trust policy's PR-wide hold | `holdRunForSecurityPolicy` | yes | `job_id` is a `SECURITY_HOLD_JOB_IDS` sentinel |
|
|
85
|
+
* | workflow install gate | `holdWorkflowForInstallGate` | no | `job_id` is `installGateJobId(name)` |
|
|
86
|
+
* | SDK workflow `requireApproval` | `holdJobForApproval` | yes | carries an `approval_requirement` |
|
|
87
|
+
* | context reviewer hold | `holdJobForApproval` | yes | carries an `approval_requirement` |
|
|
88
|
+
* | SDK job `requireApproval` | `holdJobForApproval` | yes | carries an `approval_requirement` |
|
|
89
|
+
* | context security hold | the per-env gate | yes | job scope + `hold_type = security` |
|
|
90
|
+
* | wait-timer / concurrency hold | the per-env gate | no | none of the above matches |
|
|
91
|
+
* | step approval | `StepApprovalBridge.request` | no | step scope |
|
|
92
|
+
*
|
|
93
|
+
* `approval_requirement` is what `createHold` writes and `create` does not, so
|
|
94
|
+
* it identifies exactly the rows `holdJobForApproval` wrote through the
|
|
95
|
+
* approval path — the site whose post is guarded by `if (hold && …)`. The two
|
|
96
|
+
* other `createHold` callers are excluded above it: the install gate by its job
|
|
97
|
+
* id, the step bridge by its scope.
|
|
98
|
+
*/
|
|
99
|
+
export declare function postedPendingSecurityCheck(hold: SecurityCheckHold): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* The terminal summary for a hold that ended, in the one vocabulary both check
|
|
102
|
+
* families use: `cancelled` for a rejection, `timed_out` for an elapsed window,
|
|
103
|
+
* `success` for an approval.
|
|
104
|
+
*
|
|
105
|
+
* The subject follows the hold's scope on every outcome, because the same event
|
|
106
|
+
* means different things at each: a workflow-scoped hold owns the whole run,
|
|
107
|
+
* while a job-scoped one owns one job and says nothing about the run's other
|
|
108
|
+
* jobs — including a step-approval hold that is still pending, which a run-wide
|
|
109
|
+
* "no longer waiting for approval" would contradict.
|
|
110
|
+
*
|
|
111
|
+
* `actor` names whoever ended the hold, on an approve and on a reject alike.
|
|
112
|
+
* Only an expiry has none: nobody answered, which is what expiry means. A
|
|
113
|
+
* rejecter whose surface already writes its own attribution into `reason` (the
|
|
114
|
+
* `/kici reject` handler builds "Rejected by alice via /kici reject") passes no
|
|
115
|
+
* `actor`, so the sentence lands once rather than twice.
|
|
116
|
+
*
|
|
117
|
+
* **`actor` is a display name, and it is published verbatim on a public commit
|
|
118
|
+
* check.** It must never be an opaque identity-provider subject id: a surface
|
|
119
|
+
* that holds one — the dashboard / CLI / MCP applier, which decides by KiCI
|
|
120
|
+
* user id — resolves it against the org's identity directory first and passes
|
|
121
|
+
* nothing when that resolves to no single name. Omitting the attribution reads
|
|
122
|
+
* as "an approver"; leaking the subject reads as `Approved by
|
|
123
|
+
* 8f3a-…-uuid`, on a pull request anyone can open.
|
|
124
|
+
*
|
|
125
|
+
* `reason` is normalized to end in terminal punctuation — a rejecter's own
|
|
126
|
+
* sentence carries none, and without one it runs straight into what follows.
|
|
127
|
+
*/
|
|
128
|
+
export declare function buildHoldEndedSummary(args: {
|
|
129
|
+
outcome: HoldOutcome;
|
|
130
|
+
scope: string;
|
|
131
|
+
reason?: string | undefined;
|
|
132
|
+
actor?: string | undefined;
|
|
133
|
+
via?: string | undefined;
|
|
134
|
+
}): string;
|
|
135
|
+
/**
|
|
136
|
+
* Terminalize the `KiCI Security` check of a hold that just ended, choosing the
|
|
137
|
+
* conclusion, title and summary from {@link HoldOutcome}.
|
|
138
|
+
*
|
|
139
|
+
* The single entry point for every surface that ends a hold — the `/kici`
|
|
140
|
+
* comment handler, the dashboard / CLI / MCP applier, and the stale detector's
|
|
141
|
+
* approval-window sweep — so those surfaces cannot render one event three ways.
|
|
142
|
+
*/
|
|
143
|
+
export declare function settleSecurityCheckForOutcome(args: {
|
|
144
|
+
db: Kysely<Database> | undefined;
|
|
145
|
+
resolvePoster: ResolveCheckStatusPoster | undefined;
|
|
146
|
+
hold: SecurityCheckHold;
|
|
147
|
+
outcome: HoldOutcome;
|
|
148
|
+
reason?: string | undefined;
|
|
149
|
+
actor?: string | undefined;
|
|
150
|
+
via?: string | undefined;
|
|
151
|
+
excludeHoldIds?: readonly string[] | undefined;
|
|
152
|
+
skipCommits?: ReadonlySet<string> | undefined;
|
|
153
|
+
}): Promise<SettleSecurityCheckResult>;
|
|
154
|
+
/**
|
|
155
|
+
* The dashboard / CLI / MCP applier's entry point into
|
|
156
|
+
* {@link settleSecurityCheckForOutcome}.
|
|
157
|
+
*
|
|
158
|
+
* That applier decides by KiCI user id, so the only actor it holds is an opaque
|
|
159
|
+
* identity-provider subject — and this summary is published verbatim on a public
|
|
160
|
+
* commit check. `resolveDisplayName` maps the subject onto the account name the
|
|
161
|
+
* org's identity directory links it to; when it names none, the attribution is
|
|
162
|
+
* DROPPED rather than falling back to the subject. So an approve reads
|
|
163
|
+
* "Approved by an approver" at worst, never "Approved by 8f3a-…-uuid".
|
|
164
|
+
*
|
|
165
|
+
* Separate from its caller so the resolve-or-drop step is a unit under test
|
|
166
|
+
* rather than a line in the composition root.
|
|
167
|
+
*/
|
|
168
|
+
export declare function settleSecurityCheckForDecision(args: {
|
|
169
|
+
db: Kysely<Database> | undefined;
|
|
170
|
+
resolvePoster: ResolveCheckStatusPoster | undefined;
|
|
171
|
+
/** Subject id → the linked account name, or undefined for no single name. */
|
|
172
|
+
resolveDisplayName: (actorSub: string) => string | undefined;
|
|
173
|
+
hold: SecurityCheckHold;
|
|
174
|
+
outcome: HoldOutcome;
|
|
175
|
+
/** Omitted where the summary must match one another writer built without it. */
|
|
176
|
+
actorSub?: string | undefined;
|
|
177
|
+
reason?: string | undefined;
|
|
178
|
+
}): Promise<SettleSecurityCheckResult>;
|
|
179
|
+
/**
|
|
180
|
+
* Terminalize the `KiCI Security` check of a hold that just ended, under a
|
|
181
|
+
* caller-supplied status and copy.
|
|
182
|
+
*
|
|
183
|
+
* Used directly where one summary has to reach BOTH check families — a rejected
|
|
184
|
+
* workflow-scoped hold reports the same sentence on `KiCI Security` and on the
|
|
185
|
+
* `kici/…` runs of the same event, and that sameness is asserted rather than
|
|
186
|
+
* assumed. Everything else goes through {@link settleSecurityCheckForOutcome}.
|
|
187
|
+
*
|
|
188
|
+
* Call it AFTER the hold's own row has left `pending` (every caller does: the
|
|
189
|
+
* applier flips inside `recordAndReject` / `recordAndRelease`, the comment
|
|
190
|
+
* handler inside `reject` / `approveByQueueType`) — and, on an approve, BEFORE
|
|
191
|
+
* the resume, so a replayed dispatch that holds again overwrites this terminal
|
|
192
|
+
* status with its own pending one rather than the other way round. The ending
|
|
193
|
+
* hold's own id is excluded from the contention query regardless, so a sweep
|
|
194
|
+
* that has not yet flipped its batch can name it in `excludeHoldIds`.
|
|
195
|
+
*
|
|
196
|
+
* Never throws: terminalizing a check run is a reporting courtesy on a path
|
|
197
|
+
* whose real job is to terminalize the hold, and a provider error must not stop
|
|
198
|
+
* that. The returned `posted` is what a caller binds its own suppression to —
|
|
199
|
+
* it says a check was written, not merely that a delegate resolved.
|
|
200
|
+
*/
|
|
201
|
+
export declare function settleSecurityHoldCheck(args: {
|
|
202
|
+
db: Kysely<Database> | undefined;
|
|
203
|
+
resolvePoster: ResolveCheckStatusPoster | undefined;
|
|
204
|
+
hold: SecurityCheckHold;
|
|
205
|
+
status: CheckStatus;
|
|
206
|
+
title: string;
|
|
207
|
+
summary: string;
|
|
208
|
+
excludeHoldIds?: readonly string[] | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Commits this caller already wrote in the same pass. A sweep that ends
|
|
211
|
+
* several holds on one commit shares one check run between them, and every
|
|
212
|
+
* hold in it carries the same conclusion and summary — so the second write
|
|
213
|
+
* would be a byte-identical provider round-trip.
|
|
214
|
+
*/
|
|
215
|
+
skipCommits?: ReadonlySet<string> | undefined;
|
|
216
|
+
}): Promise<SettleSecurityCheckResult>;
|
|
217
|
+
//# sourceMappingURL=security-hold-check.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type CheckRunConclusion } from '@kici-dev/engine';
|
|
2
|
+
import type { Kysely } from 'kysely';
|
|
3
|
+
import type { Database } from '../db/types.js';
|
|
4
|
+
import type { CheckRunReporter } from '../reporting/check-run-reporter.js';
|
|
5
|
+
/**
|
|
6
|
+
* Complete the check runs of `runId`'s stored workflow dispatch, if it has one.
|
|
7
|
+
*
|
|
8
|
+
* A run with no pending workflow context never held at workflow scope (or its
|
|
9
|
+
* context was already consumed by a resume), so there is nothing to close and
|
|
10
|
+
* this is a no-op. Call it BEFORE `deletePendingWorkflowContext` — the context
|
|
11
|
+
* is the only place the check-run names can still be derived from.
|
|
12
|
+
*
|
|
13
|
+
* Never throws: closing a check run is a reporting courtesy on a path whose real
|
|
14
|
+
* job is to terminalize the run, and a provider error must not stop that.
|
|
15
|
+
*/
|
|
16
|
+
export declare function completeUndispatchedHoldChecks(args: {
|
|
17
|
+
db: Kysely<Database> | undefined;
|
|
18
|
+
checkRunReporter: CheckRunReporter | undefined;
|
|
19
|
+
runId: string;
|
|
20
|
+
conclusion: CheckRunConclusion;
|
|
21
|
+
summary: string;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=undispatched-hold-checks.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-coordinator propagation of dashboard-write policy changes.
|
|
3
|
+
*
|
|
4
|
+
* Sister to `GenericSourcesChangeListener`
|
|
5
|
+
* (`packages/orchestrator/src/webhook/generic-sources-listener.ts`), which
|
|
6
|
+
* owns the `generic_sources_change` channel. This listener owns
|
|
7
|
+
* `dashboard_write_policy_change` — the channel `setDashboardWritePolicy`
|
|
8
|
+
* emits from inside its own write transaction.
|
|
9
|
+
*
|
|
10
|
+
* The problem it solves: `dashboardWritePolicyEvents` is a per-process
|
|
11
|
+
* `EventEmitter`, so only the coordinator that served the `kici-admin`
|
|
12
|
+
* write learned about a flip. Its siblings kept serving a stale cached map
|
|
13
|
+
* from their own policy gate for up to the cache TTL, and — worse — kept
|
|
14
|
+
* advertising the stale map to the control plane indefinitely, because
|
|
15
|
+
* capabilities are pushed on change and on reconnect, never polled. The
|
|
16
|
+
* control plane caches capabilities per orchestrator connection and resolves
|
|
17
|
+
* a cluster name to whichever of its connections it finds first, so a policy
|
|
18
|
+
* flip could be invisible to the dashboard for as long as the cluster stayed
|
|
19
|
+
* up.
|
|
20
|
+
*
|
|
21
|
+
* On NOTIFY:
|
|
22
|
+
* 1. Queue the affected `customer_id` (the channel payload).
|
|
23
|
+
* 2. Debounce 200 ms so rapid flips coalesce into one drain pass.
|
|
24
|
+
* 3. For each queued customer: drop the cached map, re-read the
|
|
25
|
+
* authoritative one from the database, and emit `'changed'` on
|
|
26
|
+
* `dashboardWritePolicyEvents`. The platform-mode boot already
|
|
27
|
+
* subscribes to that event and re-broadcasts `orch.capabilities`.
|
|
28
|
+
*
|
|
29
|
+
* The payload is the customer id alone: every coordinator shares one
|
|
30
|
+
* orchestrator database, so re-reading is both cheap and authoritative,
|
|
31
|
+
* and no wire copy of the map can go stale in flight.
|
|
32
|
+
*
|
|
33
|
+
* The writing coordinator receives its own NOTIFY too. That is deliberate
|
|
34
|
+
* and harmless — it already emitted the event in-process, and the second
|
|
35
|
+
* emit produces an identical capability broadcast.
|
|
36
|
+
*/
|
|
37
|
+
import type pg from 'pg';
|
|
38
|
+
import type { Kysely } from 'kysely';
|
|
39
|
+
import type { Database } from '../db/types.js';
|
|
40
|
+
export interface DashboardWritePolicyChangeListenerOptions {
|
|
41
|
+
/** Raw pg pool — a dedicated PoolClient is checked out for LISTEN. */
|
|
42
|
+
pool: pg.Pool;
|
|
43
|
+
/** Used to re-read the authoritative policy map on each NOTIFY. */
|
|
44
|
+
db: Kysely<Database>;
|
|
45
|
+
/** Coalesce window for rapid NOTIFYs against the same customer. Default 200 ms. */
|
|
46
|
+
debounceMs?: number;
|
|
47
|
+
}
|
|
48
|
+
export declare class DashboardWritePolicyChangeListener {
|
|
49
|
+
private readonly opts;
|
|
50
|
+
private client;
|
|
51
|
+
private readonly pending;
|
|
52
|
+
private debounceTimer;
|
|
53
|
+
private readonly debounceMs;
|
|
54
|
+
constructor(opts: DashboardWritePolicyChangeListenerOptions);
|
|
55
|
+
/** Open the dedicated client and subscribe. */
|
|
56
|
+
start(): Promise<void>;
|
|
57
|
+
stop(): Promise<void>;
|
|
58
|
+
private scheduleDrain;
|
|
59
|
+
/** Visible for tests — drain the queued customer ids synchronously. */
|
|
60
|
+
drain(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=dashboard-write-policy-listener.d.ts.map
|
|
@@ -23,10 +23,24 @@ import { type Kysely } from 'kysely';
|
|
|
23
23
|
import { DashboardWriteOperation, type DashboardWritePolicyMap, type DashboardWritePolicyState } from '@kici-dev/engine/protocol/dashboard-write-operations';
|
|
24
24
|
import type { Database } from '../db/types.js';
|
|
25
25
|
import type { ActorPrincipal } from '@kici-dev/engine';
|
|
26
|
+
/**
|
|
27
|
+
* Postgres `LISTEN/NOTIFY` channel carrying policy changes across the
|
|
28
|
+
* cluster. The payload is the `customer_id` alone — every coordinator
|
|
29
|
+
* shares one orchestrator database, so each listener re-reads the
|
|
30
|
+
* authoritative map for itself rather than trusting a wire copy.
|
|
31
|
+
*
|
|
32
|
+
* `DashboardWritePolicyChangeListener` (`./dashboard-write-policy-listener.ts`)
|
|
33
|
+
* is the consumer.
|
|
34
|
+
*/
|
|
35
|
+
export declare const DASHBOARD_WRITE_POLICY_CHANNEL = "dashboard_write_policy_change";
|
|
26
36
|
/**
|
|
27
37
|
* Event bus for policy-change notifications. The WS publisher subscribes
|
|
28
38
|
* to `'changed'` so it can broadcast a fresh `orch.capabilities` to
|
|
29
39
|
* Platform whenever the operator flips a switch.
|
|
40
|
+
*
|
|
41
|
+
* The emitter is per-process, so it carries a change only to the peer that
|
|
42
|
+
* served the admin write. The NOTIFY channel above is what reaches the other
|
|
43
|
+
* coordinators, whose listeners then emit this same event locally.
|
|
30
44
|
*/
|
|
31
45
|
export declare const dashboardWritePolicyEvents: EventEmitter<[never]>;
|
|
32
46
|
/**
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* webhook normalization, lock file fetching, changed files retrieval,
|
|
13
13
|
* clone token creation, and URL building.
|
|
14
14
|
*/
|
|
15
|
-
import type { WebhookNormalizer, LockFileFetcher, FileContentsFetcher, ChangedFilesFetcher, CloneTokenProvider, RepoUrlBuilder,
|
|
15
|
+
import type { WebhookNormalizer, LockFileFetcher, FileContentsFetcher, ChangedFilesFetcher, CloneTokenProvider, RepoUrlBuilder, CheckStatusPoster, ProviderType } from '@kici-dev/engine';
|
|
16
16
|
/**
|
|
17
17
|
* Complete set of provider capabilities.
|
|
18
18
|
*
|
|
@@ -44,7 +44,15 @@ export interface ProviderBundle {
|
|
|
44
44
|
changedFilesFetcher?: ChangedFilesFetcher;
|
|
45
45
|
cloneTokenProvider?: CloneTokenProvider;
|
|
46
46
|
repoUrlBuilder?: RepoUrlBuilder;
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* This provider has a fork model: a head ref can live outside the base repo,
|
|
49
|
+
* so the fork trust policy applies to its pull-request events. True for
|
|
50
|
+
* GitHub. Absent for providers whose trust boundary is something else — a
|
|
51
|
+
* generic source's verification secret, a local source's on-disk ownership —
|
|
52
|
+
* and for universal-git, which reports an `isForkPR` signal that must not
|
|
53
|
+
* gate it.
|
|
54
|
+
*/
|
|
55
|
+
hasForkModel?: boolean;
|
|
48
56
|
checkStatusPoster?: CheckStatusPoster;
|
|
49
57
|
/**
|
|
50
58
|
* Local `file://` in-place profile: this bundle's `repoBasePath` is the
|
|
@@ -66,5 +66,16 @@ export declare class GitHubCheckStatusPoster implements CheckStatusPoster {
|
|
|
66
66
|
* only requires check names it was configured with.
|
|
67
67
|
*/
|
|
68
68
|
postGlobalEvalFailedCheck(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Post the success conclusion on the organization-workflow-evaluation check
|
|
71
|
+
* after a re-run of a failed round completed cleanly.
|
|
72
|
+
*
|
|
73
|
+
* Deliberately the SAME check name the failure posts: GitHub keys a check run
|
|
74
|
+
* by its name on a commit, so writing the success under that name is what
|
|
75
|
+
* turns the red check green on the original commit. A second, differently
|
|
76
|
+
* named green check would leave the red one standing and keep a branch
|
|
77
|
+
* protection rule blocked with no way to clear it short of a new commit.
|
|
78
|
+
*/
|
|
79
|
+
postGlobalEvalSucceededCheck(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
69
80
|
}
|
|
70
81
|
//# sourceMappingURL=check-status-poster.d.ts.map
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* - GitHubChangedFilesFetcher -> ChangedFilesFetcher
|
|
9
9
|
* - GitHubCloneTokenProvider -> CloneTokenProvider
|
|
10
10
|
* - GitHubRepoUrlBuilder -> RepoUrlBuilder
|
|
11
|
-
* - GitHubContributorResolver -> ContributorResolver
|
|
12
11
|
*/
|
|
13
12
|
export { GitHubWebhookNormalizer } from './normalizer.js';
|
|
14
13
|
export { GitHubLockFileFetcher } from './lock-file.js';
|
|
@@ -17,6 +16,5 @@ export { GitHubChangedFilesFetcher } from './changed-files.js';
|
|
|
17
16
|
export { GitHubCloneTokenProvider, createInstallationOctokit } from './auth.js';
|
|
18
17
|
export type { GitHubAppConfig, GitHubCredentials } from './auth.js';
|
|
19
18
|
export { GitHubRepoUrlBuilder } from './repo-url.js';
|
|
20
|
-
export { GitHubContributorResolver } from './contributor-resolver.js';
|
|
21
19
|
export { GitHubCheckStatusPoster } from './check-status-poster.js';
|
|
22
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -74,8 +74,9 @@ export declare class GitHubWebhookNormalizer implements WebhookNormalizer {
|
|
|
74
74
|
*/
|
|
75
75
|
extractCredentials(payload: unknown): Record<string, unknown>;
|
|
76
76
|
/**
|
|
77
|
-
* Map GitHub membership-related webhook events to
|
|
78
|
-
* invalidations. See WebhookNormalizer.getAccessCacheInvalidations
|
|
77
|
+
* Map GitHub membership-related webhook events to permission-cache
|
|
78
|
+
* invalidations. See WebhookNormalizer.getAccessCacheInvalidations, which is
|
|
79
|
+
* deprecated and has no caller.
|
|
79
80
|
*
|
|
80
81
|
* Covered event types:
|
|
81
82
|
*
|
|
@@ -70,8 +70,9 @@ export declare class LocalWebhookNormalizer implements WebhookNormalizer {
|
|
|
70
70
|
*/
|
|
71
71
|
normalizeEvent(eventType: string, _action: string | null, payload: unknown): SimulatedEvent | null;
|
|
72
72
|
/**
|
|
73
|
-
* Map membership-related local-source webhook events to
|
|
74
|
-
* invalidations.
|
|
73
|
+
* Map membership-related local-source webhook events to permission-cache
|
|
74
|
+
* invalidations. See WebhookNormalizer.getAccessCacheInvalidations, which is
|
|
75
|
+
* deprecated and has no caller.
|
|
75
76
|
*
|
|
76
77
|
* Local-source payloads are GitHub-shaped by design, so the mapping
|
|
77
78
|
* mirrors the GitHub normalizer exactly:
|
|
@@ -80,9 +81,6 @@ export declare class LocalWebhookNormalizer implements WebhookNormalizer {
|
|
|
80
81
|
* - `organization`: user-in-org
|
|
81
82
|
* - `membership`: user-in-org
|
|
82
83
|
* - `team` (repo-scoped actions only): repo
|
|
83
|
-
*
|
|
84
|
-
* This lets membership-invalidation paths be exercised through
|
|
85
|
-
* `sendLocalWebhook()` without requiring a real GitHub App.
|
|
86
84
|
*/
|
|
87
85
|
getAccessCacheInvalidations(eventType: string, _action: string | null, payload: unknown): AccessCacheInvalidation[];
|
|
88
86
|
}
|
|
@@ -18,8 +18,17 @@
|
|
|
18
18
|
* - `UniversalGitRepoUrlBuilder` — substitutes `{owner}`/`{name}`/`{repo}`
|
|
19
19
|
* in the source's `gitUrlTemplate`.
|
|
20
20
|
*
|
|
21
|
-
* No `
|
|
22
|
-
*
|
|
21
|
+
* No `CheckStatusPoster` is wired for v1 — forge API support for it is uneven
|
|
22
|
+
* and adding it is a separate phase.
|
|
23
|
+
*
|
|
24
|
+
* The bundle also leaves `hasForkModel` unset. The normalizer does report an
|
|
25
|
+
* `isForkPR`, but it compares two repo names that `extractRepoFullName` reads
|
|
26
|
+
* from fixed keys (`repo.full_name`, then `full_name`) on the payload's `head`
|
|
27
|
+
* and `base` — keys many forges' PR payloads do not carry, and which no
|
|
28
|
+
* `payloadPaths` entry can redirect. It yields `false` whenever either is
|
|
29
|
+
* absent, so it fails toward trust. A pull-request event here therefore
|
|
30
|
+
* resolves no trust tier: it reads the base branch's lock file and writes an
|
|
31
|
+
* isolated cache scope, and the org fork policy never gates it.
|
|
23
32
|
*/
|
|
24
33
|
import type { ProviderBundle } from '../../provider-registry.js';
|
|
25
34
|
import type { GenericWebhookSource } from '../../db/types.js';
|
|
@@ -180,6 +180,23 @@ export interface QueuedJob {
|
|
|
180
180
|
/** For a runsOnAll host-fanout child: the agent this job is pinned to. */
|
|
181
181
|
pinnedAgentId?: string;
|
|
182
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* The resource shape a job declares, read from the one location that always
|
|
185
|
+
* carries it.
|
|
186
|
+
*
|
|
187
|
+
* `jobConfig.resources` is where the declaration is stored and transported: it
|
|
188
|
+
* is what `job_config` holds across the DB round trip and what a worker
|
|
189
|
+
* receives over the wire. The typed `resources` field on {@link QueuedJobInput}
|
|
190
|
+
* and {@link QueuedJob} is a convenience mirror, and it is optional — several
|
|
191
|
+
* dispatch paths build their input without it (the worker's reroute handler and
|
|
192
|
+
* the webhook dispatch path among them). So a consumer that reads only the
|
|
193
|
+
* mirror sees a job that declares nothing, and a shape-sensitive decision made
|
|
194
|
+
* on that reading is wrong rather than merely unaware.
|
|
195
|
+
*
|
|
196
|
+
* Callers that hold a mirror still prefer it — it is the same value — and fall
|
|
197
|
+
* back here.
|
|
198
|
+
*/
|
|
199
|
+
export declare function resourcesFromJobConfig(jobConfig: Record<string, unknown> | undefined): ResourceRequest | undefined;
|
|
183
200
|
/**
|
|
184
201
|
* DB-backed FIFO job dispatch queue using Kysely (PostgreSQL only).
|
|
185
202
|
* Uses SQL-based JSONB containment queries (@> operator) for label matching.
|
|
@@ -236,9 +253,16 @@ export declare class JobQueue {
|
|
|
236
253
|
* @param agentLabels Labels the agent provides.
|
|
237
254
|
* @param agentMandatoryLabels Mandatory labels the spawning scaler declared
|
|
238
255
|
* (empty for static / non-scaler agents).
|
|
256
|
+
* @param canServe Optional extra predicate applied to every candidate row
|
|
257
|
+
* before it is claimed. Labels cannot express whether a pre-spawned agent's
|
|
258
|
+
* fixed cpu / memory / image fit a job, so a caller that has to answer that
|
|
259
|
+
* supplies it here — the row must never be claimed and put back, because a
|
|
260
|
+
* claim-then-release both strands it as Dispatched for a window and burns a
|
|
261
|
+
* dispatch attempt. Supplying it opts out of the single-statement fast
|
|
262
|
+
* path, so pass it only for an agent that actually needs the check.
|
|
239
263
|
* @returns The matching job, or null if none found.
|
|
240
264
|
*/
|
|
241
|
-
dequeueForLabels(agentLabels: string[], agentMandatoryLabels?: string[], agentId?: string): Promise<QueuedJob | null>;
|
|
265
|
+
dequeueForLabels(agentLabels: string[], agentMandatoryLabels?: string[], agentId?: string, canServe?: (job: QueuedJob) => boolean): Promise<QueuedJob | null>;
|
|
242
266
|
/**
|
|
243
267
|
* The column writes that constitute a claim. Identical to what
|
|
244
268
|
* {@link markDispatched} sets, so a claim and the caller's follow-up
|
|
@@ -309,6 +333,24 @@ export declare class JobQueue {
|
|
|
309
333
|
* null, so a lost race costs this agent a candidate and not a whole drain.
|
|
310
334
|
*/
|
|
311
335
|
private claimWithPatterns;
|
|
336
|
+
/**
|
|
337
|
+
* Both drain passes at once, with an extra JS predicate between the SELECT
|
|
338
|
+
* and the claim.
|
|
339
|
+
*
|
|
340
|
+
* Same shape as {@link claimWithPatterns} and for the same reason: the filter
|
|
341
|
+
* has to run BEFORE the row transitions, so the claim is a conditional
|
|
342
|
+
* `status = Pending` UPDATE by id rather than the embedded sub-select the
|
|
343
|
+
* fast path uses. Claiming first and releasing on a rejection is not an
|
|
344
|
+
* option — it strands the row as Dispatched for the width of the round trip
|
|
345
|
+
* and spends one of the job's bounded dispatch attempts every time an
|
|
346
|
+
* unsuitable agent polls.
|
|
347
|
+
*
|
|
348
|
+
* It covers pattern-free rows too, which the fast path would otherwise take
|
|
349
|
+
* first, so the predicate is applied to every candidate rather than to the
|
|
350
|
+
* pattern-bearing minority. Losing a claim continues to the next candidate,
|
|
351
|
+
* exactly as the pattern pass does.
|
|
352
|
+
*/
|
|
353
|
+
private claimWithPostFilter;
|
|
312
354
|
/**
|
|
313
355
|
* Atomically claim the oldest pending job pinned to a specific agent. Used by
|
|
314
356
|
* the eager pin drain when the pinned agent (re)registers or frees a slot —
|
|
@@ -29,9 +29,9 @@ export declare function extractGlobalWorkflows(lockFile: LockFile): LockWorkflow
|
|
|
29
29
|
* Extract workflows that have at least one registerable trigger, OR that
|
|
30
30
|
* have repo patterns (global workflows).
|
|
31
31
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* Git-provider triggers (push, pr, tag, …) are ALSO registerable, so
|
|
33
|
+
* cross-source dispatch can resolve them via the registration index when a
|
|
34
|
+
* generic webhook targets an externally-hosted repo. The
|
|
35
35
|
* per-event lock file pipeline remains the primary matching path for
|
|
36
36
|
* same-source git events; registration is an additive index.
|
|
37
37
|
*
|
|
@@ -21,6 +21,13 @@ export interface RegisteredWorkflow {
|
|
|
21
21
|
*/
|
|
22
22
|
customerId: string;
|
|
23
23
|
commitSha: string | null;
|
|
24
|
+
/**
|
|
25
|
+
* The repository's default branch at last registration. A `__schedule_fire`
|
|
26
|
+
* run executes this branch's lock file, so it is the branch such a run
|
|
27
|
+
* presents to a context's branch restrictions. `null` until the repo's next
|
|
28
|
+
* default-branch push populates it.
|
|
29
|
+
*/
|
|
30
|
+
defaultBranch: string | null;
|
|
24
31
|
sourceFile: string | null;
|
|
25
32
|
}
|
|
26
33
|
/**
|
|
@@ -24,6 +24,16 @@ export interface RegistrationRow {
|
|
|
24
24
|
*/
|
|
25
25
|
customerId: string;
|
|
26
26
|
commitSha: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* The repository's default branch, captured from the push that last updated
|
|
29
|
+
* this registration. A `__schedule_fire` run executes this branch's lock
|
|
30
|
+
* file, so this is the branch such a run presents to a context's branch
|
|
31
|
+
* restrictions.
|
|
32
|
+
*
|
|
33
|
+
* `null` when the registration predates the column or the payload named no
|
|
34
|
+
* default branch — the run then presents no branch and the gate rejects.
|
|
35
|
+
*/
|
|
36
|
+
defaultBranch: string | null;
|
|
27
37
|
sourceFile: string | null;
|
|
28
38
|
created_at: Date;
|
|
29
39
|
updated_at: Date;
|
|
@@ -40,6 +50,18 @@ interface ReplaceAllOptions {
|
|
|
40
50
|
*/
|
|
41
51
|
customerId: string;
|
|
42
52
|
commitSha?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The repository's default branch, read off the registering push's payload.
|
|
55
|
+
* Persisted so a `__schedule_fire` run can present it as its own branch.
|
|
56
|
+
*
|
|
57
|
+
* Three states, and the difference between the last two is load-bearing on an
|
|
58
|
+
* UPDATE: a string is written; `null` means the caller LOOKED and the payload
|
|
59
|
+
* named none, so the stored value is cleared; **omitting the field** means the
|
|
60
|
+
* caller does not know, and an already-captured branch is preserved. Only a
|
|
61
|
+
* default-branch push knows a repository's default branch, so a caller that
|
|
62
|
+
* is not one (the manual registration route) must not erase what one proved.
|
|
63
|
+
*/
|
|
64
|
+
defaultBranch?: string | null;
|
|
43
65
|
sourceFile?: string;
|
|
44
66
|
/** Set of workflow names that should be marked as global */
|
|
45
67
|
globalWorkflowNames?: Set<string>;
|