@kici-dev/orchestrator 0.5.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 +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 +17671 -7350
- 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,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke-gate executor.
|
|
3
|
+
*
|
|
4
|
+
* An invoke gate is a standard job whose only novel attribute is `invoke`: it
|
|
5
|
+
* never dispatches steps to an agent. When the gate becomes ready the
|
|
6
|
+
* orchestrator emits the gate's kici event at the source repo, matches the
|
|
7
|
+
* repo's opt-in subscribers, dispatches each as a normal in-repo run, and
|
|
8
|
+
* tracks each spawned run as a **proxy job** in the gate's own run.
|
|
9
|
+
*
|
|
10
|
+
* Correlation is a synchronous in-process match-and-dispatch — one orchestrator
|
|
11
|
+
* serves one org, so the executor matches + dispatches through an injected
|
|
12
|
+
* `summon` callback and gets the spawned run ids back directly. It ALSO persists
|
|
13
|
+
* `summoned_by_run_id` / `summoned_by_proxy_job` on each spawned run so proxy
|
|
14
|
+
* completion still resolves if the run finalizes on another HA instance.
|
|
15
|
+
*
|
|
16
|
+
* The gate itself is the aggregating graph node: downstream `needs` edges point
|
|
17
|
+
* at the gate, and its proxies are its fan-out children (`base_job_name` = the
|
|
18
|
+
* gate name). A zero-subscriber emit fails the gate by default; `optional: true`
|
|
19
|
+
* makes it a green skip.
|
|
20
|
+
*/
|
|
21
|
+
import { ExecutionJobStatus } from '@kici-dev/engine';
|
|
22
|
+
import type { LockInvoke, LockJob } from '@kici-dev/engine';
|
|
23
|
+
import type { Kysely } from 'kysely';
|
|
24
|
+
import type { Database } from '../db/types.js';
|
|
25
|
+
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
26
|
+
/**
|
|
27
|
+
* The invoke parameters carried from a gate's lock job to its (possibly deferred)
|
|
28
|
+
* execution. Persisted on the pending job context so a gate released later — or
|
|
29
|
+
* after a crash-recovery restore — still summons instead of reaching an agent.
|
|
30
|
+
*/
|
|
31
|
+
export interface InvokeGateParams {
|
|
32
|
+
event: string;
|
|
33
|
+
payload?: Record<string, unknown>;
|
|
34
|
+
/** Require-by-default: false fails the gate on zero subscribers. */
|
|
35
|
+
optional: boolean;
|
|
36
|
+
/** Fan-out concurrency width applied to the gate's proxy children. */
|
|
37
|
+
maxParallel?: number;
|
|
38
|
+
/** Fan-out fail-fast policy applied to the gate's proxy children. */
|
|
39
|
+
failFast?: boolean;
|
|
40
|
+
/** The gate's own wall-clock timeout in ms (orchestrator-swept). */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Extract the invoke parameters from a lock job, or undefined when it is not a gate. */
|
|
44
|
+
export declare function invokeParamsFromLockJob(lockJob: Pick<LockJob, 'invoke' | 'maxParallel' | 'failFast' | 'timeout'> & {
|
|
45
|
+
invoke?: LockInvoke;
|
|
46
|
+
}): InvokeGateParams | undefined;
|
|
47
|
+
/** True when a lock job is an invoke gate (carries an `invoke` action). */
|
|
48
|
+
export declare function isInvokeGate(lockJob: Pick<LockJob, 'invoke'>): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A summon that was REFUSED, as opposed to one that matched nothing.
|
|
51
|
+
*
|
|
52
|
+
* The two are not the same verdict and must never render the same. A gate
|
|
53
|
+
* carrying `optional: true` treats zero summoned runs as a green skip, so a
|
|
54
|
+
* refusal returned as `[]` would report success for work that was declined.
|
|
55
|
+
* Throwing this makes the confusion unrepresentable: {@link runInvokeGate}
|
|
56
|
+
* fails the gate job with the reason, whatever order its own checks run in.
|
|
57
|
+
*/
|
|
58
|
+
export declare class SummonRefusedError extends Error {
|
|
59
|
+
constructor(reason: string);
|
|
60
|
+
}
|
|
61
|
+
/** One source-repo run summoned by an invoke gate. */
|
|
62
|
+
export interface SummonedRun {
|
|
63
|
+
/** The spawned run's id. */
|
|
64
|
+
runId: string;
|
|
65
|
+
/** The source repo the run belongs to (`owner/repo`). */
|
|
66
|
+
repo: string;
|
|
67
|
+
/** The subscribing workflow's name. */
|
|
68
|
+
workflow: string;
|
|
69
|
+
}
|
|
70
|
+
/** Arguments to the injected `summon` callback. */
|
|
71
|
+
export interface SummonArgs {
|
|
72
|
+
event: string;
|
|
73
|
+
payload?: Record<string, unknown>;
|
|
74
|
+
sourceRepo: string;
|
|
75
|
+
/** The chain depth to stamp on the summoned runs (already incremented). */
|
|
76
|
+
chainDepth: number;
|
|
77
|
+
/** The summoning gate's run id — tag each spawned run so its proxy resolves. */
|
|
78
|
+
summonedByRunId: string;
|
|
79
|
+
}
|
|
80
|
+
/** Dependencies for {@link runInvokeGate}. */
|
|
81
|
+
export interface InvokeGateDeps {
|
|
82
|
+
db: Kysely<Database>;
|
|
83
|
+
/** Insert the proxy rows + mark the gate terminal on a zero-match emit. */
|
|
84
|
+
executionTracker: Pick<ExecutionTracker, 'addJobsToRun' | 'onJobStatus' | 'reconcileSummonedRunIfTerminal'>;
|
|
85
|
+
/**
|
|
86
|
+
* Match the source repo's subscribers to `event`, dispatch each as a normal
|
|
87
|
+
* in-repo run, and return the spawned run ids. Implemented at the composition
|
|
88
|
+
* root, where it reuses the orchestrator's event match + trust-policy path.
|
|
89
|
+
*
|
|
90
|
+
* An empty array means the event matched no subscriber — a real outcome a
|
|
91
|
+
* gate may declare `optional`. A REFUSAL raises
|
|
92
|
+
* {@link SummonRefusedError} instead, so the two can never be read as the
|
|
93
|
+
* same thing.
|
|
94
|
+
*/
|
|
95
|
+
summon: (args: SummonArgs) => Promise<SummonedRun[]>;
|
|
96
|
+
/** Max chain depth allowed for a summon (the circuit breaker's bound). */
|
|
97
|
+
maxChainDepth: number;
|
|
98
|
+
}
|
|
99
|
+
/** Arguments identifying the gate to run. */
|
|
100
|
+
export interface RunInvokeGateArgs {
|
|
101
|
+
runId: string;
|
|
102
|
+
gateJobId: string;
|
|
103
|
+
gateJobName: string;
|
|
104
|
+
event: string;
|
|
105
|
+
payload?: Record<string, unknown>;
|
|
106
|
+
/** When true, a zero-subscriber emit succeeds; when false it fails the gate. */
|
|
107
|
+
optional: boolean;
|
|
108
|
+
sourceRepo: string;
|
|
109
|
+
chainDepth: number;
|
|
110
|
+
/** Fan-out concurrency width stamped on the proxy children (`maxParallel`). */
|
|
111
|
+
maxParallel?: number;
|
|
112
|
+
/** Fan-out fail-fast policy stamped on the proxy children (`failFast`). */
|
|
113
|
+
failFast?: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Aggregate an invoke gate's status from its proxies' terminal statuses. The
|
|
117
|
+
* gate stays open (`allTerminal: false`) until every proxy is terminal; then it
|
|
118
|
+
* is `failed` if any proxy is a failure, else `success`. Pure — the tracker's
|
|
119
|
+
* aggregation hook drives the DB reads and the gate transition.
|
|
120
|
+
*/
|
|
121
|
+
export declare function aggregateGateStatus(proxyStatuses: readonly string[]): {
|
|
122
|
+
allTerminal: boolean;
|
|
123
|
+
status?: ExecutionJobStatus;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* The message a gate naming a reserved event fails with.
|
|
127
|
+
*
|
|
128
|
+
* Exported so the gate executor and its test state the same string, and so the
|
|
129
|
+
* SDK-side error can be compared against it.
|
|
130
|
+
*/
|
|
131
|
+
export declare function reservedEventMessage(event: string, prefix: string): string;
|
|
132
|
+
/** The message a require-by-default gate fails with when nothing subscribes. */
|
|
133
|
+
export declare function zeroSubscriberMessage(sourceRepo: string, event: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* The proxy job name for a summoned run — the gate name, a `repo:workflow`
|
|
136
|
+
* variant, and the summoned run id. The run id makes the name **unique per run**:
|
|
137
|
+
* two summoned runs with the same `repo`+`workflow` (duplicate registrations)
|
|
138
|
+
* would otherwise share one `job_name`, so the mirror's `job_name` lookup would
|
|
139
|
+
* resolve one proxy and leave the other hanging. The human-readable label for the
|
|
140
|
+
* graph is carried separately in `variant_label` (`repo:workflow`).
|
|
141
|
+
*/
|
|
142
|
+
export declare function proxyJobName(gateJobName: string, run: SummonedRun): string;
|
|
143
|
+
/**
|
|
144
|
+
* Run an invoke gate: summon the source repo's subscribers and create one proxy
|
|
145
|
+
* child per spawned run. A zero-subscriber emit terminalizes the gate — failed
|
|
146
|
+
* by default, success under `optional`. When the chain-depth bound is reached
|
|
147
|
+
* the gate fails without summoning, so an invoke chain cannot loop.
|
|
148
|
+
*/
|
|
149
|
+
export declare function runInvokeGate(deps: InvokeGateDeps, args: RunInvokeGateArgs): Promise<void>;
|
|
150
|
+
/** Dependencies for {@link releaseInvokeGate}. */
|
|
151
|
+
export interface ReleaseInvokeGateDeps {
|
|
152
|
+
db: Kysely<Database>;
|
|
153
|
+
executionTracker: ExecutionTracker;
|
|
154
|
+
invokeGateDeps: InvokeGateDeps;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Release a ready invoke gate: swap its synthetic needs-pending row for a real
|
|
158
|
+
* `gate` job row, then run the gate executor. Reads the summoning run's source
|
|
159
|
+
* repo and chain depth from `execution_runs` (the chain depth bounds recursion).
|
|
160
|
+
* Shared by the needs-release path (`dispatchReadyJob`) and the root-gate
|
|
161
|
+
* post-registration nudge, so both go through one code path.
|
|
162
|
+
*/
|
|
163
|
+
export declare function releaseInvokeGate(deps: ReleaseInvokeGateDeps, runId: string, gateJobName: string, params: InvokeGateParams): Promise<void>;
|
|
164
|
+
//# sourceMappingURL=invoke-gate.d.ts.map
|
|
@@ -31,6 +31,19 @@ export interface SchedulerResult {
|
|
|
31
31
|
action: 'dispatch' | 'skip';
|
|
32
32
|
reason?: string;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if ALL upstreams of a job are satisfied.
|
|
36
|
+
* Returns { satisfied: true, action: 'dispatch' } when every upstream is
|
|
37
|
+
* terminal and its status is a member of the edge's run_on set. Returns
|
|
38
|
+
* { satisfied: true, action: 'skip', reason } when an upstream is terminal but
|
|
39
|
+
* its status is not in the edge's run_on set. Returns { satisfied: false } when
|
|
40
|
+
* any upstream is not yet terminal.
|
|
41
|
+
*/
|
|
42
|
+
export declare function checkAllUpstreamsSatisfied(db: Kysely<Database>, runId: string, jobName: string): Promise<{
|
|
43
|
+
satisfied: boolean;
|
|
44
|
+
action?: 'dispatch' | 'skip';
|
|
45
|
+
reason?: string;
|
|
46
|
+
}>;
|
|
34
47
|
/**
|
|
35
48
|
* Insert dependency edges at run start for static-to-static needs.
|
|
36
49
|
*
|
|
@@ -21,9 +21,10 @@ import type { LockFile as FullLockFile, SimulatedEvent } from '@kici-dev/engine'
|
|
|
21
21
|
import type { OrchestratorMode } from '@kici-dev/engine';
|
|
22
22
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
23
23
|
import type { ProviderBundle } from '../provider-registry.js';
|
|
24
|
-
import type
|
|
24
|
+
import { type TrustResolution } from '../security/trust-resolver.js';
|
|
25
25
|
import { type TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
26
26
|
import { type WorkflowModification } from '../security/workflow-diff.js';
|
|
27
|
+
import type { IdentityLink, PermissionLevel } from '../security/identity-link.js';
|
|
27
28
|
import { type ProcessingDeps } from './processor.js';
|
|
28
29
|
/**
|
|
29
30
|
* Outcome of a single inbound-webhook ingestion. `duplicate` lets a direct-
|
|
@@ -45,27 +46,76 @@ export declare const WebhookIngestOutcome: z.ZodEnum<{
|
|
|
45
46
|
skipped: "skipped";
|
|
46
47
|
}>;
|
|
47
48
|
export type WebhookIngestOutcome = z.infer<typeof WebhookIngestOutcome>;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the directory a `/kici approve` comment is authorized against.
|
|
51
|
+
*
|
|
52
|
+
* Two sources, and which one applies is decided by the caller that assembled
|
|
53
|
+
* these deps rather than by the orchestrator's mode:
|
|
54
|
+
*
|
|
55
|
+
* - `server.ts` holds the Platform-pushed directory in memory and refreshes it
|
|
56
|
+
* on every `trust_policy.update`, so it always supplies both fields. An
|
|
57
|
+
* in-memory directory therefore wins outright — reading the row instead
|
|
58
|
+
* would answer from a cache the push has already superseded.
|
|
59
|
+
* - Every other assembly supplies neither. That includes the direct-ingress
|
|
60
|
+
* pipeline in `app.ts`, which is the only path an independent orchestrator
|
|
61
|
+
* has, and where the operator's own `kici-admin trust-policy directory-set`
|
|
62
|
+
* registrations live only in the row. Reading it per command is also what
|
|
63
|
+
* makes a registration take effect immediately rather than at the next
|
|
64
|
+
* restart.
|
|
65
|
+
*
|
|
66
|
+
* A failed read degrades to an empty directory: the commenter is not resolved
|
|
67
|
+
* and the command is refused, which is the same fail-closed answer an absent
|
|
68
|
+
* directory already gives.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveApprovalDirectory(deps: ProcessingDeps, orgId: string): Promise<{
|
|
71
|
+
identityLinks: IdentityLink[];
|
|
72
|
+
orgMemberPermissions: Map<string, PermissionLevel>;
|
|
73
|
+
}>;
|
|
48
74
|
interface TrustOutcome {
|
|
49
75
|
trustResolution: TrustResolution | undefined;
|
|
50
76
|
/** Default 'base' for PR events; trust resolution may override to 'head'. */
|
|
51
77
|
lockFileSource: 'head' | 'base';
|
|
52
78
|
}
|
|
79
|
+
/** Whether an inbound event name is one of the pull-request events. */
|
|
80
|
+
export declare function isPullRequestEvent(eventName: string): boolean;
|
|
53
81
|
export declare function resolveTrustForPR(args: {
|
|
54
82
|
info: WebhookInfo;
|
|
55
|
-
deps: ProcessingDeps;
|
|
56
83
|
bundle: ProviderBundle;
|
|
57
84
|
event: SimulatedEvent;
|
|
85
|
+
/** Raw webhook payload — read only to decide whether a push targets the default branch. */
|
|
58
86
|
payload: Record<string, unknown>;
|
|
59
|
-
resolvedOrgId: string;
|
|
60
|
-
repoIdentifier: string;
|
|
61
|
-
credentials: Record<string, unknown>;
|
|
62
87
|
}): Promise<TrustOutcome>;
|
|
88
|
+
/** What one organization-wide dispatch pass produced. */
|
|
89
|
+
interface GlobalDispatchOutcome {
|
|
90
|
+
matchedCount: number;
|
|
91
|
+
matchedRunIds: string[];
|
|
92
|
+
/** One summary per global decision this pass reached, dispatched or excluded. */
|
|
93
|
+
decisionSummaries: Record<string, unknown>[];
|
|
94
|
+
/**
|
|
95
|
+
* The workflow repository of every evaluation round this pass could not
|
|
96
|
+
* decide. Empty means every round the pass ran reached a verdict.
|
|
97
|
+
*/
|
|
98
|
+
roundFailureWorkflowRepos: string[];
|
|
99
|
+
/**
|
|
100
|
+
* Every workflow repository this pass actually evaluated: it examined that
|
|
101
|
+
* repository's registrations and reached a real outcome for each of them —
|
|
102
|
+
* dispatched, filtered out, or excluded by a verdict.
|
|
103
|
+
*
|
|
104
|
+
* The pass's POSITIVE signal, and the one a re-run gates its success check
|
|
105
|
+
* on. An empty `roundFailureWorkflowRepos` cannot carry that meaning: a pass
|
|
106
|
+
* with no registration index, no pending-eval tracker, or an event the trust
|
|
107
|
+
* policy held evaluates nothing and reports no failure either, so reading its
|
|
108
|
+
* silence as a clean verdict is the false assurance the round exists to
|
|
109
|
+
* remove.
|
|
110
|
+
*/
|
|
111
|
+
decidedWorkflowRepos: string[];
|
|
112
|
+
}
|
|
63
113
|
interface SecurityState {
|
|
64
114
|
workflowModifications: WorkflowModification[];
|
|
65
115
|
/**
|
|
66
|
-
* True when this PR changes `.kici/` workflow definitions.
|
|
67
|
-
*
|
|
68
|
-
*
|
|
116
|
+
* True when this PR changes `.kici/` workflow definitions. Reported, not
|
|
117
|
+
* enforced: the org trust policy is the fork switch, and it reads the fork
|
|
118
|
+
* signal and the tier — never this one.
|
|
69
119
|
*/
|
|
70
120
|
hasWorkflowModifications: boolean;
|
|
71
121
|
}
|
|
@@ -74,10 +124,11 @@ interface SecurityState {
|
|
|
74
124
|
* modifications by diffing base vs. head and post a neutral informational check
|
|
75
125
|
* (on its own dedicated check name).
|
|
76
126
|
*
|
|
77
|
-
* Detection
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
127
|
+
* Detection and legibility only: the check tells a reviewer that this PR edits
|
|
128
|
+
* the workflows it would run under. It feeds no policy decision — a PR that
|
|
129
|
+
* edits `.kici/` from the base repo is written by a contributor who could
|
|
130
|
+
* already push to it, and one from a fork is governed by the fork switch
|
|
131
|
+
* whether or not it touches `.kici/`.
|
|
81
132
|
*/
|
|
82
133
|
export declare function applyWorkflowModificationsAndSecurityHold(args: {
|
|
83
134
|
info: WebhookInfo;
|
|
@@ -95,11 +146,17 @@ export declare function applyWorkflowModificationsAndSecurityHold(args: {
|
|
|
95
146
|
/**
|
|
96
147
|
* Evaluate the org trust policy for a PR event.
|
|
97
148
|
*
|
|
98
|
-
* Scoped to providers with a
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
149
|
+
* Scoped to providers with a fork model (`bundle.hasForkModel`): a source
|
|
150
|
+
* without one passes regardless of policy. That is not a trust claim about such
|
|
151
|
+
* a source — a PR from a fork-less provider resolves no tier at all. It is that
|
|
152
|
+
* the policy's one condition cannot be established for it: universal-git does
|
|
153
|
+
* compute an `isForkPR`, but from payload keys many forges omit, and it reads
|
|
154
|
+
* `false` when they are absent. Gating on a signal that fails toward trust
|
|
155
|
+
* would be worse than not gating.
|
|
156
|
+
*
|
|
157
|
+
* The policy read has two distinct failure shapes, and they get distinct
|
|
158
|
+
* answers: no stored row is the unconfigured default (`FAIL_CLOSED_POLICY`),
|
|
159
|
+
* while a read that THREW gets `READ_FAILURE_POLICY` — see there for why.
|
|
103
160
|
*/
|
|
104
161
|
export declare function evaluateSecurityPolicy(args: {
|
|
105
162
|
deps: ProcessingDeps;
|
|
@@ -109,23 +166,54 @@ export declare function evaluateSecurityPolicy(args: {
|
|
|
109
166
|
mode: OrchestratorMode;
|
|
110
167
|
trustResolution: TrustResolution | undefined;
|
|
111
168
|
isForkPR: boolean;
|
|
112
|
-
hasWorkflowModifications: boolean;
|
|
113
169
|
}): Promise<TrustPolicyOutcome>;
|
|
114
170
|
/**
|
|
115
|
-
*
|
|
171
|
+
* Phase J — After per-repo workflows are dispatched, query the global index
|
|
172
|
+
* for workflows authored in OTHER repos that target this event type via
|
|
173
|
+
* cross-repo `repos` patterns. Same-org scope picks up both same-source and
|
|
174
|
+
* cross-source globals (cross-provider global workflows).
|
|
175
|
+
*/
|
|
176
|
+
export declare function dispatchGlobalWorkflowsForOtherRepos(args: {
|
|
177
|
+
info: WebhookInfo;
|
|
178
|
+
deps: ProcessingDeps;
|
|
179
|
+
eventWithFiles: SimulatedEvent;
|
|
180
|
+
resolvedOrgId: string;
|
|
181
|
+
repoIdentifier: string;
|
|
182
|
+
ref: string;
|
|
183
|
+
dispatchBundle: ProviderBundle;
|
|
184
|
+
dispatchCredentials: Record<string, unknown>;
|
|
185
|
+
/** The inbound event's own bundle, used to post the globals-skipped check. */
|
|
186
|
+
bundle: ProviderBundle;
|
|
187
|
+
credentials: Record<string, unknown>;
|
|
188
|
+
securityDecision: TrustPolicyOutcome;
|
|
189
|
+
/**
|
|
190
|
+
* The source `dispatchCredentials` belongs to, when a cross-provider lock-file
|
|
191
|
+
* fallback made it a different source from `info.routingKey`. Persisted on a
|
|
192
|
+
* failed round's run row so its re-run can rebuild the same dispatch pair
|
|
193
|
+
* instead of handing this source's credentials to the inbound source's client.
|
|
194
|
+
*/
|
|
195
|
+
dispatchRoutingKey?: string;
|
|
196
|
+
/**
|
|
197
|
+
* Restrict the pass to global workflows authored in this repository. Set by
|
|
198
|
+
* the re-run of a failed evaluation round, which re-decides that round alone.
|
|
199
|
+
*/
|
|
200
|
+
onlyWorkflowRepo?: string;
|
|
201
|
+
}): Promise<GlobalDispatchOutcome>;
|
|
202
|
+
/**
|
|
203
|
+
* Process a webhook through the complete pipeline, recording a `failed` event-log
|
|
204
|
+
* row for a delivery the pipeline could not finish.
|
|
205
|
+
*
|
|
206
|
+
* The row is the only per-delivery record of such a failure. Nothing the pipeline
|
|
207
|
+
* decides after acknowledgement can reach the sender — `acceptWebhookDelivery`
|
|
208
|
+
* answers 202 and runs this detached — and the dashboard's webhook-activity view
|
|
209
|
+
* counts `event_log.status = 'failed'` for exactly this. Without the write that
|
|
210
|
+
* count is structurally always zero, and a post-acknowledgement failure appears
|
|
211
|
+
* in the orchestrator's own logs and nowhere a customer can see.
|
|
116
212
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* 4. Trust resolution for PR events
|
|
122
|
-
* 5. Lock file fetch (with multi-provider fallback)
|
|
123
|
-
* - No lock file: dispatch global workflows for this event (same org) and return
|
|
124
|
-
* 6. Workflow modification detection + security hold check status
|
|
125
|
-
* 7. Default-branch registration
|
|
126
|
-
* 8. Match triggers + dispatch matched same-source workflows
|
|
127
|
-
* 9. Match + dispatch global workflows for OTHER repos
|
|
128
|
-
* 10. Forward Platform trace + record event log
|
|
213
|
+
* Best-effort and non-masking: the write is wrapped, and the original error is
|
|
214
|
+
* rethrown either way. The delivery still reverts to `buffered` for the drain
|
|
215
|
+
* pass to retry, so a redelivery that succeeds writes its own `processed` row
|
|
216
|
+
* beside this one — the two are distinct rows, not a mutation.
|
|
129
217
|
*/
|
|
130
218
|
export declare function processWebhook(info: WebhookInfo, deps: ProcessingDeps): Promise<WebhookIngestOutcome>;
|
|
131
219
|
export {};
|