@kici-dev/orchestrator 0.9.1 → 0.10.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__/test-db.d.ts +14 -1
- package/dist/agent/dispatcher.d.ts +137 -41
- package/dist/agent/registry.d.ts +79 -2
- package/dist/app.d.ts +2 -0
- package/dist/cancel/cancel-held-run.d.ts +68 -0
- package/dist/cancel/cancel-route-answer.d.ts +21 -0
- package/dist/cancel/cancel-run.d.ts +32 -0
- package/dist/cli/api-client.d.ts +3 -0
- package/dist/cli/commands/context.d.ts +3 -1
- package/dist/cli/commands/runs.d.ts +7 -0
- package/dist/cli/commands/shared/unbound-context-warning.d.ts +43 -0
- package/dist/cli/commands/signing-key.d.ts +22 -1
- package/dist/cli.js +1273 -715
- package/dist/cluster/cluster-settings-reader.d.ts +1 -1
- package/dist/cluster/coordinator.d.ts +2 -0
- package/dist/cluster/live-coordinator-peer.d.ts +13 -0
- package/dist/config.d.ts +9 -0
- package/dist/contexts/context-store.d.ts +3 -2
- package/dist/contexts/protection/satisfiability.d.ts +6 -3
- package/dist/db/migrations/145_execution_runs_workflow_provenance.d.ts +4 -0
- package/dist/db/migrations/146_pending_job_contexts_context_resolution.d.ts +4 -0
- package/dist/db/migrations/147_sealed_job_secrets.d.ts +19 -0
- package/dist/db/migrations/148_cluster_settings_sealed_secrets_retry_backoff_ms.d.ts +21 -0
- package/dist/db/migrations/149_workflow_registrations_dep_cache_key.d.ts +4 -0
- package/dist/db/migrations/150_execution_runs_status_epoch.d.ts +18 -0
- package/dist/db/migrations/151_workflow_registrations_dep_cache_key_sha.d.ts +4 -0
- package/dist/db/repos/signing-keys-repo.d.ts +48 -3
- package/dist/db/types.d.ts +81 -1
- package/dist/git/clone-token-auth.d.ts +7 -0
- package/dist/git/dispatch-git-auth.d.ts +63 -0
- package/dist/lockfile-validate.d.ts +4 -2
- package/dist/metrics/prometheus.d.ts +1 -1
- package/dist/oidc/id-token-claims.d.ts +10 -0
- package/dist/oidc/oidc-mint-registration.d.ts +2 -0
- package/dist/oidc/orchestrator-mint.d.ts +25 -4
- package/dist/oidc/reconcile-signing-key.d.ts +31 -6
- package/dist/oidc/resolve-signer.d.ts +12 -6
- package/dist/pipeline/decorating-secret-resolver.d.ts +5 -3
- package/dist/pipeline/dispatch-matched-workflow.d.ts +105 -13
- package/dist/pipeline/dispatch-matched-workflow.test-helpers.d.ts +121 -0
- package/dist/pipeline/global-dispatch-identity.d.ts +76 -0
- package/dist/pipeline/global-dispatch.d.ts +91 -0
- package/dist/pipeline/global-eval-round.d.ts +56 -4
- package/dist/pipeline/global-round-hold.d.ts +41 -0
- package/dist/pipeline/held-context-data.d.ts +143 -0
- package/dist/pipeline/held-round-registrations.d.ts +40 -0
- package/dist/pipeline/install-gate.d.ts +93 -0
- package/dist/pipeline/install-secrets-resolver.d.ts +28 -13
- package/dist/pipeline/job-contexts.d.ts +21 -6
- package/dist/pipeline/pending-workflow-context.d.ts +15 -3
- package/dist/pipeline/process-webhook.d.ts +38 -1
- package/dist/pipeline/processor.d.ts +20 -0
- package/dist/pipeline/rerun-global.d.ts +28 -0
- package/dist/pipeline/rerun-inputs.d.ts +88 -0
- package/dist/pipeline/rerun.d.ts +25 -2
- package/dist/pipeline/resume-workflow.d.ts +36 -2
- package/dist/pipeline/unbound-job-context.d.ts +34 -0
- package/dist/pipeline/workflow-lock-at-sha.d.ts +67 -0
- package/dist/provenance/backfill-run.d.ts +5 -0
- package/dist/providers/github/check-status-poster.d.ts +4 -16
- package/dist/queue/job-queue.d.ts +127 -10
- package/dist/queue/terminalize-unroutable.d.ts +37 -4
- package/dist/registration/registration-index.d.ts +13 -1
- package/dist/registration/registration-store.d.ts +10 -1
- package/dist/reporting/execution-tracker.d.ts +149 -8
- package/dist/reporting/log-chunk-sink.d.ts +1 -1
- package/dist/reporting/log-writer.d.ts +14 -0
- package/dist/reporting/step-log-path.d.ts +8 -0
- package/dist/reporting/step-log-reader.d.ts +21 -3
- package/dist/routes/admin-signing-keys.d.ts +33 -0
- package/dist/scaler/agent-fit.d.ts +159 -0
- package/dist/scaler/container-routing.d.ts +16 -16
- package/dist/scaler/manager.d.ts +23 -17
- package/dist/secrets/context-secret-resolver.d.ts +1 -1
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/job-secret-gate.d.ts +25 -3
- package/dist/secrets/job-secret-seal.d.ts +36 -0
- package/dist/secrets/master-key-rotation.d.ts +7 -3
- package/dist/secrets/secret-resolver.d.ts +42 -23
- package/dist/server.js +27690 -23447
- package/dist/standalone.js +58162 -53103
- package/dist/worker/in-memory-job-queue.d.ts +12 -3
- package/dist/worker/reroute-dispatch.d.ts +35 -0
- package/dist/ws/agent-handler.d.ts +23 -10
- package/dist/ws/git-credential-relay.d.ts +16 -3
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +53 -53
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* This module owns the partition, the grouping, and the dispatch-and-await. The
|
|
17
17
|
* caller decides what to do with the verdicts.
|
|
18
18
|
*/
|
|
19
|
-
import type { GlobalEvalCandidateResult, LockWorkflow, SimulatedEvent, WorkflowDecision } from '@kici-dev/engine';
|
|
19
|
+
import type { AgentCapabilities, GlobalEvalCandidateResult, LockJobOrFactory, LockWorkflow, SimulatedEvent, WorkflowDecision } from '@kici-dev/engine';
|
|
20
20
|
import { type GlobalEvalRoundCache } from '../cache/global-eval-round-cache.js';
|
|
21
21
|
import type { PendingGlobalEvalTracker } from '../cache/pending-global-evals.js';
|
|
22
22
|
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
@@ -69,14 +69,29 @@ export interface GlobalEvalCandidate {
|
|
|
69
69
|
* blank id still separates two workflows.
|
|
70
70
|
*/
|
|
71
71
|
export declare function candidateKey(candidate: GlobalEvalCandidate): string;
|
|
72
|
+
/**
|
|
73
|
+
* True for a `DynamicJobFn` entry the pre-run round runs: a generator with no
|
|
74
|
+
* upstream `needs`.
|
|
75
|
+
*
|
|
76
|
+
* A result-aware generator (`resultAware`, or any declared `needs`) reads its
|
|
77
|
+
* upstreams' outputs, which do not exist before the run starts. It runs on the
|
|
78
|
+
* run's deferred path instead, once those upstreams complete.
|
|
79
|
+
*/
|
|
80
|
+
export declare function isRoundGenerator(job: LockJobOrFactory): boolean;
|
|
81
|
+
/** True for a `DynamicJobFn` entry that runs on the deferred path, never in the round. */
|
|
82
|
+
export declare function isDeferredGenerator(job: LockJobOrFactory): boolean;
|
|
83
|
+
/** True when the workflow carries at least one generator the round must NOT run. */
|
|
84
|
+
export declare function hasDeferredGenerator(lockEntry: LockWorkflow): boolean;
|
|
72
85
|
/**
|
|
73
86
|
* Split matched global candidates into those that can dispatch straight away and
|
|
74
87
|
* those that must go through an eval round first.
|
|
75
88
|
*
|
|
76
89
|
* A candidate needs the round when it declares a `filter` (only the agent may
|
|
77
|
-
* run the predicate) or carries a `DynamicJobFn` (only the agent may
|
|
78
|
-
* generator
|
|
79
|
-
*
|
|
90
|
+
* run the predicate) or carries a needs-free `DynamicJobFn` (only the agent may
|
|
91
|
+
* run the generator, and it depends on nothing the run produces). Everything
|
|
92
|
+
* else — static jobs, and result-aware generators the run evaluates later — is
|
|
93
|
+
* handed to the dispatch pipeline as declared, so routing it through a round
|
|
94
|
+
* would add a job dispatch and an agent round trip for nothing.
|
|
80
95
|
*/
|
|
81
96
|
export declare function partitionCandidates(candidates: readonly GlobalEvalCandidate[]): {
|
|
82
97
|
immediate: GlobalEvalCandidate[];
|
|
@@ -119,6 +134,18 @@ export interface GlobalEvalAgentRegistry {
|
|
|
119
134
|
arch: string;
|
|
120
135
|
version?: string | null;
|
|
121
136
|
}>;
|
|
137
|
+
/**
|
|
138
|
+
* Every registered agent, busy or idle. Optional so a test double can omit
|
|
139
|
+
* it; without it the up-front result-aware refusal never fires.
|
|
140
|
+
*/
|
|
141
|
+
getAllEntries?(): Iterable<{
|
|
142
|
+
labels: ReadonlySet<string>;
|
|
143
|
+
platform: string;
|
|
144
|
+
arch: string;
|
|
145
|
+
version: string | null;
|
|
146
|
+
/** What the agent advertised on `agent.register`; `null` = supports nothing optional. */
|
|
147
|
+
capabilities: AgentCapabilities | null;
|
|
148
|
+
}>;
|
|
122
149
|
}
|
|
123
150
|
/**
|
|
124
151
|
* First agent release whose job runner understands a `globalEvalRound`
|
|
@@ -162,6 +189,12 @@ export interface GlobalEvalRoundArgs {
|
|
|
162
189
|
ref: string;
|
|
163
190
|
dispatchBundle: ProviderBundle;
|
|
164
191
|
dispatchCredentials: Record<string, unknown>;
|
|
192
|
+
/**
|
|
193
|
+
* The source `dispatchBundle` and `dispatchCredentials` belong to, when a
|
|
194
|
+
* cross-provider lock-file fallback made it a different source from
|
|
195
|
+
* `info.routingKey`. Absent means the inbound source.
|
|
196
|
+
*/
|
|
197
|
+
dispatchRoutingKey?: string;
|
|
165
198
|
/** Cluster defaults for the round budgets and the wait ceiling (`config.ts`). */
|
|
166
199
|
config: {
|
|
167
200
|
globalEvalRoundTimeoutMs: number;
|
|
@@ -192,6 +225,25 @@ export interface GlobalEvalRoundArgs {
|
|
|
192
225
|
* timeout message that says nothing about agent versions.
|
|
193
226
|
*/
|
|
194
227
|
export declare function unsupportedFleetReason(agentRegistry?: GlobalEvalAgentRegistry): string | null;
|
|
228
|
+
/**
|
|
229
|
+
* Why a candidate with a result-aware generator is refused before any round.
|
|
230
|
+
*
|
|
231
|
+
* An agent without the capability runs every generator in the round, a
|
|
232
|
+
* result-aware one included; that generator sees no upstream outputs and
|
|
233
|
+
* returns the wrong jobs.
|
|
234
|
+
*/
|
|
235
|
+
export declare const RESULT_AWARE_UNSUPPORTED_REASON: string;
|
|
236
|
+
/**
|
|
237
|
+
* Why no agent can run a round holding a result-aware generator, or `null`
|
|
238
|
+
* when one might.
|
|
239
|
+
*
|
|
240
|
+
* The same bar as {@link unsupportedFleetReason}: refused only on proof. That
|
|
241
|
+
* means init-runners ARE registered, every one reports a readable version, and
|
|
242
|
+
* none advertises the capability. An empty fleet, a busy capable agent, or an
|
|
243
|
+
* agent whose version cannot be read is not refused — the round queues on the
|
|
244
|
+
* agent-feature label, and a scaler or a registering agent can still serve it.
|
|
245
|
+
*/
|
|
246
|
+
export declare function resultAwareUnsupportedReason(agentRegistry?: GlobalEvalAgentRegistry): string | null;
|
|
195
247
|
/**
|
|
196
248
|
* Narrow one wire verdict into a value the dispatch path can trust.
|
|
197
249
|
*
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
2
|
+
import type { WebhookInfo } from '../webhook/handler.js';
|
|
3
|
+
import type { ProviderBundle } from '../provider-registry.js';
|
|
4
|
+
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
5
|
+
import type { TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
6
|
+
import { type GlobalEvalCandidate } from './global-eval-round.js';
|
|
7
|
+
import { type ProcessingDeps } from './processor.js';
|
|
8
|
+
/** Inputs of one pass's held evaluation rounds. */
|
|
9
|
+
export interface HoldGlobalEvalRoundsArgs {
|
|
10
|
+
info: WebhookInfo;
|
|
11
|
+
deps: ProcessingDeps;
|
|
12
|
+
event: SimulatedEvent;
|
|
13
|
+
/** The candidates that needed the round. */
|
|
14
|
+
candidates: readonly GlobalEvalCandidate[];
|
|
15
|
+
/** The source repository the event came from. */
|
|
16
|
+
repoIdentifier: string;
|
|
17
|
+
/** The event's commit SHA. */
|
|
18
|
+
ref: string;
|
|
19
|
+
resolvedOrgId: string;
|
|
20
|
+
/** Credentials the round would have run with, stored on the held row for its release. */
|
|
21
|
+
dispatchCredentials: Record<string, unknown>;
|
|
22
|
+
/** The source `dispatchCredentials` belongs to, when it is not the inbound one. */
|
|
23
|
+
dispatchRoutingKey?: string;
|
|
24
|
+
/** The inbound event's bundle and credentials: the security check lands on its repository. */
|
|
25
|
+
bundle?: ProviderBundle;
|
|
26
|
+
credentials?: Record<string, unknown>;
|
|
27
|
+
trustResolution: TrustResolution | undefined;
|
|
28
|
+
decision: Extract<TrustPolicyOutcome, {
|
|
29
|
+
action: 'hold';
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Hold the evaluation round of every workflow repository among `candidates`.
|
|
34
|
+
* Returns the id of each held run, in workflow-repository order.
|
|
35
|
+
*
|
|
36
|
+
* One run per workflow repository, not per registered SHA: the release
|
|
37
|
+
* re-evaluates a whole workflow repository, so two rows for one repository would
|
|
38
|
+
* dispatch its workflows twice.
|
|
39
|
+
*/
|
|
40
|
+
export declare function holdGlobalEvalRounds(args: HoldGlobalEvalRoundsArgs): Promise<string[]>;
|
|
41
|
+
//# sourceMappingURL=global-round-hold.d.ts.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a job's context data (variables, scoped secrets, container registry
|
|
3
|
+
* credentials) at dispatch, and again when a job stored as a pending dispatch
|
|
4
|
+
* context is dispatched later. A stored job keeps only what resolution needs
|
|
5
|
+
* ({@link DeferredContextResolution}), never a value, and both moments merge
|
|
6
|
+
* the result into the job config through {@link contextDataConfigFields}.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { type Context as EngineContext, type HostFacts, type LockJob, type TrustTier } from '@kici-dev/engine';
|
|
10
|
+
import type { ContextStore } from '../contexts/context-store.js';
|
|
11
|
+
import type { VariableStore } from '../contexts/variable-store.js';
|
|
12
|
+
import type { JobDispatchContext } from '../contexts/protection/pipeline.js';
|
|
13
|
+
import type { QueuedJobInput } from '../queue/job-queue.js';
|
|
14
|
+
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
15
|
+
type LockContainer = NonNullable<LockJob['container']>;
|
|
16
|
+
/** The context data a job config carries. */
|
|
17
|
+
export interface ContextJobData {
|
|
18
|
+
contextVars?: Record<string, string>;
|
|
19
|
+
jobSecrets?: Record<string, string>;
|
|
20
|
+
jobNamespacedSecrets?: Record<string, Record<string, string>>;
|
|
21
|
+
containerRegistryAuth?: {
|
|
22
|
+
username: string;
|
|
23
|
+
password: string;
|
|
24
|
+
serveraddress: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** The stores context-data resolution reads. */
|
|
28
|
+
export interface ContextDataDeps {
|
|
29
|
+
contextStore?: ContextStore;
|
|
30
|
+
variableStore?: VariableStore;
|
|
31
|
+
secretResolver?: SecretResolverApi;
|
|
32
|
+
}
|
|
33
|
+
/** What a job's container registry credentials resolve against. */
|
|
34
|
+
export interface RegistryAuthInputs {
|
|
35
|
+
container: LockContainer;
|
|
36
|
+
/** The dispatch facts the named context's protection rules evaluate. */
|
|
37
|
+
dispatchCtx: JobDispatchContext;
|
|
38
|
+
trustTier?: TrustTier;
|
|
39
|
+
/** Contexts already admitted to this job, whose rules are not re-run. */
|
|
40
|
+
admittedContextIds?: ReadonlySet<string>;
|
|
41
|
+
/**
|
|
42
|
+
* `omit`: a refusal (a context that does not admit the run, a missing
|
|
43
|
+
* secret, a malformed reference) leaves the job without registry
|
|
44
|
+
* credentials, as dispatch does. Only a store or resolver failure throws.
|
|
45
|
+
* `throw` (default): every failure throws, for a caller that catches.
|
|
46
|
+
*/
|
|
47
|
+
onRefusal?: 'throw' | 'omit';
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Stored with a job's pending dispatch context: everything resolution at its
|
|
51
|
+
* later dispatch needs, and no secret value.
|
|
52
|
+
*/
|
|
53
|
+
export declare const DeferredContextResolutionSchema: z.ZodObject<{
|
|
54
|
+
contexts: z.ZodArray<z.ZodObject<{
|
|
55
|
+
name: z.ZodString;
|
|
56
|
+
id: z.ZodString;
|
|
57
|
+
}, z.core.$strip>>;
|
|
58
|
+
orgId: z.ZodString;
|
|
59
|
+
resolvesSecrets: z.ZodOptional<z.ZodBoolean>;
|
|
60
|
+
routingKey: z.ZodOptional<z.ZodString>;
|
|
61
|
+
hostCtx: z.ZodOptional<z.ZodObject<{
|
|
62
|
+
agentId: z.ZodString;
|
|
63
|
+
host: z.ZodString;
|
|
64
|
+
labels: z.ZodArray<z.ZodString>;
|
|
65
|
+
}, z.core.$strip>>;
|
|
66
|
+
registryAuth: z.ZodOptional<z.ZodObject<{
|
|
67
|
+
container: z.ZodCustom<LockContainer, LockContainer>;
|
|
68
|
+
dispatchCtx: z.ZodObject<{
|
|
69
|
+
branch: z.ZodString;
|
|
70
|
+
triggerType: z.ZodString;
|
|
71
|
+
repository: z.ZodString;
|
|
72
|
+
runId: z.ZodString;
|
|
73
|
+
jobId: z.ZodString;
|
|
74
|
+
internallyTriggered: z.ZodOptional<z.ZodBoolean>;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
trustTier: z.ZodOptional<z.ZodEnum<{
|
|
77
|
+
trusted: "trusted";
|
|
78
|
+
unknown: "unknown";
|
|
79
|
+
}>>;
|
|
80
|
+
}, z.core.$strip>>;
|
|
81
|
+
}, z.core.$strip>;
|
|
82
|
+
export type DeferredContextResolution = z.infer<typeof DeferredContextResolutionSchema>;
|
|
83
|
+
/** Identity fields for log lines. */
|
|
84
|
+
interface LogFields {
|
|
85
|
+
runId: string;
|
|
86
|
+
workflow: string;
|
|
87
|
+
job: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the merged variables and secrets of `entries`, then the container
|
|
91
|
+
* registry credentials, writing each onto `into` as soon as it resolves. A
|
|
92
|
+
* context that has no scope binding is named in a warning log line.
|
|
93
|
+
*
|
|
94
|
+
* Writing progressively keeps what already resolved when a later step throws,
|
|
95
|
+
* which is what the dispatch path's catch relies on.
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveContextJobData(args: {
|
|
98
|
+
deps: ContextDataDeps;
|
|
99
|
+
orgId: string;
|
|
100
|
+
entries: ReadonlyArray<{
|
|
101
|
+
name: string;
|
|
102
|
+
env: EngineContext;
|
|
103
|
+
}>;
|
|
104
|
+
hostCtx?: HostFacts;
|
|
105
|
+
routingKey?: string;
|
|
106
|
+
registryAuth?: RegistryAuthInputs;
|
|
107
|
+
log: LogFields;
|
|
108
|
+
into: ContextJobData;
|
|
109
|
+
}): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* The job-config fields a job's context data maps to.
|
|
112
|
+
*
|
|
113
|
+
* `runWideFlatSecrets` are layered over the context secrets so a run-wide value
|
|
114
|
+
* wins a key collision, and so they reach a job with no context at all.
|
|
115
|
+
*/
|
|
116
|
+
export declare function contextDataConfigFields(data: ContextJobData | undefined, runWideFlatSecrets: Record<string, string> | undefined): Record<string, unknown>;
|
|
117
|
+
/**
|
|
118
|
+
* A job's dispatch input with its context data removed, for storing as a
|
|
119
|
+
* pending dispatch context. `runWideFlatSecrets` stay: they belong to the run,
|
|
120
|
+
* not to a context, and resolution at release layers them back on top.
|
|
121
|
+
*/
|
|
122
|
+
export declare function withoutContextData(jobInput: QueuedJobInput, runWideFlatSecrets: Record<string, string> | undefined): QueuedJobInput;
|
|
123
|
+
/**
|
|
124
|
+
* Resolve a released job's context data and return its dispatch input with
|
|
125
|
+
* that data merged into `jobConfig`.
|
|
126
|
+
*
|
|
127
|
+
* The job's own contexts are not re-gated: it passed their rules, or its hold
|
|
128
|
+
* was released. Throws when the record is malformed, the stores are
|
|
129
|
+
* unavailable, a bound context is gone or replaced, or a lookup fails, so the
|
|
130
|
+
* caller fails the job rather than dispatching it without the secrets it was
|
|
131
|
+
* bound to. A registry-auth refusal leaves the job without registry
|
|
132
|
+
* credentials, as dispatch does.
|
|
133
|
+
*/
|
|
134
|
+
export declare function resolveReleasedJobInput(args: {
|
|
135
|
+
jobInput: QueuedJobInput;
|
|
136
|
+
jobName: string;
|
|
137
|
+
resolution: unknown;
|
|
138
|
+
deps: ContextDataDeps | undefined;
|
|
139
|
+
}): Promise<QueuedJobInput>;
|
|
140
|
+
/** Log a release-time resolution failure; the caller fails the job. */
|
|
141
|
+
export declare function logReleaseResolutionFailure(jobInput: QueuedJobInput, err: unknown): string;
|
|
142
|
+
export {};
|
|
143
|
+
//# sourceMappingURL=held-context-data.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { RegisteredWorkflow } from '../registration/registration-index.js';
|
|
2
|
+
import { type ProcessingDeps } from './processor.js';
|
|
3
|
+
/** The held round's run row fields the release reads. */
|
|
4
|
+
export interface HeldRoundRow {
|
|
5
|
+
customer_id: string;
|
|
6
|
+
workflow_sha: string | null;
|
|
7
|
+
workflow_branch: string | null;
|
|
8
|
+
trigger_decision: string | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The workflows the hold recorded the round as covering, or `undefined` for a
|
|
12
|
+
* row held before the record existed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function heldRoundWorkflowsOf(triggerDecision: string | null): string[] | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The workflow repository's organization-wide registrations for the held
|
|
17
|
+
* event, as they were at the held commit.
|
|
18
|
+
*
|
|
19
|
+
* Throws, naming the workflow repository and the workflows, when the release
|
|
20
|
+
* cannot run what the approver approved: the repository registers nothing any
|
|
21
|
+
* more, its lock file at the held commit cannot be read, a workflow the round
|
|
22
|
+
* covered has no live, enabled registration that still subscribes to the event,
|
|
23
|
+
* or the lock file at the held commit does not define it as an
|
|
24
|
+
* organization-wide workflow for the event. The release then fails the held run
|
|
25
|
+
* with that reason and dispatches nothing — never a partial round.
|
|
26
|
+
*
|
|
27
|
+
* Every live registration of a covered workflow is returned, one per source
|
|
28
|
+
* that registers it, as the pass the round was held from evaluates them.
|
|
29
|
+
*
|
|
30
|
+
* The covered set is the one the hold recorded. A row held before that record
|
|
31
|
+
* existed falls back to the held commit's round-shaped workflows, which cannot
|
|
32
|
+
* see a workflow whose registration vanished at an unchanged commit.
|
|
33
|
+
*/
|
|
34
|
+
export declare function registrationsAtHeldCommit(args: {
|
|
35
|
+
row: HeldRoundRow;
|
|
36
|
+
deps: Pick<ProcessingDeps, 'registrationIndex' | 'providerRegistry'>;
|
|
37
|
+
workflowRepo: string;
|
|
38
|
+
eventName: string;
|
|
39
|
+
}): Promise<RegisteredWorkflow[]>;
|
|
40
|
+
//# sourceMappingURL=held-round-registrations.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workflow install gate: the protection rules of every context a
|
|
3
|
+
* workflow's `registries:` / `installEnv:` references name.
|
|
4
|
+
*
|
|
5
|
+
* Every context is evaluated before the gate decides, with the combination rule
|
|
6
|
+
* the job-level multi-context gate uses: a reject from any context rejects,
|
|
7
|
+
* whatever the others say. When none rejects and at least one holds, the gate
|
|
8
|
+
* raises ONE hold whose requirement aggregates every context's rules, most
|
|
9
|
+
* restrictive wins (`aggregateProtectionParams`): the reviewers of every held
|
|
10
|
+
* context must approve, the longest wait timer applies. That hold records which
|
|
11
|
+
* contexts held — its approval covers them — and which were admitted.
|
|
12
|
+
*
|
|
13
|
+
* The release gates again every context the approval did not cover, and
|
|
14
|
+
* refuses when any recorded context now matches a different row than the one
|
|
15
|
+
* the hold was raised against.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
import type { ProtectionGateResult } from '@kici-dev/engine';
|
|
19
|
+
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
20
|
+
import type { Context as ContextRow } from '../db/types.js';
|
|
21
|
+
import { type JobDispatchContext } from '../contexts/protection/pipeline.js';
|
|
22
|
+
import { InstallSecretsDecisionReason } from '../metrics/prometheus.js';
|
|
23
|
+
/** A context the install gate matched: the name the workflow used, and the row it matched. */
|
|
24
|
+
export declare const InstallGateContextSchema: z.ZodObject<{
|
|
25
|
+
name: z.ZodString;
|
|
26
|
+
id: z.ZodString;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
export type InstallGateContext = z.infer<typeof InstallGateContextSchema>;
|
|
29
|
+
/**
|
|
30
|
+
* What an install-gate hold records about its contexts. Stored with the held
|
|
31
|
+
* dispatch, so the release knows which contexts the approval covers.
|
|
32
|
+
*/
|
|
33
|
+
export declare const InstallGateRecordSchema: z.ZodObject<{
|
|
34
|
+
held: z.ZodArray<z.ZodObject<{
|
|
35
|
+
name: z.ZodString;
|
|
36
|
+
id: z.ZodString;
|
|
37
|
+
}, z.core.$strip>>;
|
|
38
|
+
admitted: z.ZodArray<z.ZodObject<{
|
|
39
|
+
name: z.ZodString;
|
|
40
|
+
id: z.ZodString;
|
|
41
|
+
}, z.core.$strip>>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
export type InstallGateRecord = z.infer<typeof InstallGateRecordSchema>;
|
|
44
|
+
/** The reject reasons the install gate reports, as its decision metric labels them. */
|
|
45
|
+
export type InstallGateRejectReason = typeof InstallSecretsDecisionReason.EnvNotFound | typeof InstallSecretsDecisionReason.ProtectionRuleBlock;
|
|
46
|
+
/** A gate verdict that holds the install. */
|
|
47
|
+
export type InstallGateHoldResult = ProtectionGateResult & {
|
|
48
|
+
action: 'hold' | 'wait' | 'queue';
|
|
49
|
+
};
|
|
50
|
+
export type InstallGateOutcome = {
|
|
51
|
+
kind: 'pass';
|
|
52
|
+
} | {
|
|
53
|
+
kind: 'reject';
|
|
54
|
+
reasonKind: InstallGateRejectReason;
|
|
55
|
+
reason: string;
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'hold';
|
|
58
|
+
/** The first held context in declaration order; it names the hold. */
|
|
59
|
+
primary: InstallGateContext;
|
|
60
|
+
result: InstallGateHoldResult;
|
|
61
|
+
record: InstallGateRecord;
|
|
62
|
+
};
|
|
63
|
+
interface GateInputs {
|
|
64
|
+
/** Every referenced context name, in declaration order, with the row it matched. */
|
|
65
|
+
matched: ReadonlyMap<string, ContextRow | null>;
|
|
66
|
+
trustResolution: TrustResolution | undefined;
|
|
67
|
+
protectionContext: JobDispatchContext;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Evaluate every referenced context's rules and decide the install gate.
|
|
71
|
+
*
|
|
72
|
+
* Reject wins over hold, as on the job-level gate. A hold aggregates the rules
|
|
73
|
+
* of every context, so its requirement is at least as strict as each held
|
|
74
|
+
* context's own, and its record lists which contexts held and which passed.
|
|
75
|
+
*/
|
|
76
|
+
export declare function gateInstall(inputs: GateInputs): Promise<InstallGateOutcome>;
|
|
77
|
+
/**
|
|
78
|
+
* Decide the install gate for a dispatch resumed from a released hold.
|
|
79
|
+
*
|
|
80
|
+
* The contexts the hold recorded as held are covered by its approval and are
|
|
81
|
+
* not gated again: their reviewer and wait-timer rules are stateless and would
|
|
82
|
+
* hold the approved run once more. Every other context is gated again, and any
|
|
83
|
+
* verdict but pass rejects — a release never raises a second hold. A context
|
|
84
|
+
* the hold recorded must still match the row it matched then.
|
|
85
|
+
*
|
|
86
|
+
* `releasedHold` comes from stored JSON, so it is parsed here: an absent or
|
|
87
|
+
* malformed record covers no context, and every context is gated again.
|
|
88
|
+
*/
|
|
89
|
+
export declare function gateReleasedInstall(inputs: GateInputs & {
|
|
90
|
+
releasedHold: unknown;
|
|
91
|
+
}): Promise<InstallGateOutcome>;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=install-gate.d.ts.map
|
|
@@ -7,16 +7,20 @@
|
|
|
7
7
|
* `<context>:<secret-name>` syntax. The resolver:
|
|
8
8
|
*
|
|
9
9
|
* 1. Parses every qualified ref and groups by context name.
|
|
10
|
-
* 2.
|
|
11
|
-
* (branch / trust / concurrency / reviewer / wait-timer)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
10
|
+
* 2. Runs the install gate (`install-gate.ts`): every referenced context's
|
|
11
|
+
* protection rules (branch / trust / concurrency / reviewer / wait-timer)
|
|
12
|
+
* are evaluated before it decides. Any `reject` fails the whole workflow
|
|
13
|
+
* dispatch with a clear reason; otherwise any hold returns ONE structured
|
|
14
|
+
* `hold` decision, aggregated across the contexts, so the caller can
|
|
15
|
+
* pause the workflow dispatch as a workflow-scoped held run and resume it
|
|
16
|
+
* when the gate clears. The hold records which contexts held and which
|
|
17
|
+
* passed. On the resume path the caller sets `skipProtectionGate` and
|
|
18
|
+
* hands back that record: the contexts that held are covered by the
|
|
19
|
+
* approval, every other context is gated again, and a context that now
|
|
20
|
+
* matches a different row is refused.
|
|
21
|
+
* 3. Resolves secrets per context via `secretResolver.resolveForContext`
|
|
22
|
+
* against the row step 2 gated (which writes its own audit log lines),
|
|
23
|
+
* so a context whose name is a glob pattern delivers its own bindings.
|
|
20
24
|
* 4. Validates each registry URL scheme: HTTPS always allowed; `http://`
|
|
21
25
|
* allowed only for loopback / `*.local` hosts OR when the org operator
|
|
22
26
|
* has flipped `org_settings.allow_http_npm_registries=true`.
|
|
@@ -33,7 +37,8 @@ import type { ApproverClause, LockRegistry } from '@kici-dev/engine';
|
|
|
33
37
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
34
38
|
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
35
39
|
import type { ContextStore } from '../contexts/context-store.js';
|
|
36
|
-
import {
|
|
40
|
+
import type { JobDispatchContext } from '../contexts/protection/pipeline.js';
|
|
41
|
+
import { type InstallGateRecord } from './install-gate.js';
|
|
37
42
|
/** Registry spec carried on the dispatch message (token already resolved). */
|
|
38
43
|
export interface NpmRegistrySpec {
|
|
39
44
|
url: string;
|
|
@@ -51,10 +56,18 @@ export interface ResolveInstallSecretsArgs {
|
|
|
51
56
|
secretResolver: SecretResolverApi | undefined;
|
|
52
57
|
protectionContext: JobDispatchContext;
|
|
53
58
|
/**
|
|
54
|
-
* Resume path:
|
|
55
|
-
*
|
|
59
|
+
* Resume path: the dispatch continues past a released install-gate hold. The
|
|
60
|
+
* gate runs only for the contexts `releasedHold` does not cover. The
|
|
61
|
+
* untrusted-contributor strip still runs first.
|
|
56
62
|
*/
|
|
57
63
|
skipProtectionGate?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Resume path: what the released hold recorded (`hold` decision's
|
|
66
|
+
* `record`). Its held contexts are covered by the approval; every recorded
|
|
67
|
+
* context must still match the same row. Read as stored JSON: absent or
|
|
68
|
+
* malformed, it covers no context.
|
|
69
|
+
*/
|
|
70
|
+
releasedHold?: InstallGateRecord;
|
|
58
71
|
}
|
|
59
72
|
/** Normalized requirement carried on a `hold` decision. */
|
|
60
73
|
export interface InstallHoldRequirement {
|
|
@@ -83,6 +96,8 @@ export type ResolveInstallSecretsResult = {
|
|
|
83
96
|
holdType: string;
|
|
84
97
|
queueType: 'context' | 'security';
|
|
85
98
|
requirement: InstallHoldRequirement;
|
|
99
|
+
/** Which contexts held (the approval covers them) and which passed. */
|
|
100
|
+
record: InstallGateRecord;
|
|
86
101
|
};
|
|
87
102
|
/** Parse `<context>:<secret-name>`. Returns null on malformed input. */
|
|
88
103
|
export declare function parseQualifiedSecretRef(ref: string): {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-context resolution helpers for the dispatch path.
|
|
3
3
|
*
|
|
4
|
-
* A job binds an ordered list of contexts
|
|
4
|
+
* A job binds an ordered list of contexts: the workflow-level names
|
|
5
|
+
* (`LockWorkflow.contexts`) first, then its own (`LockJob.contexts`). This
|
|
5
6
|
* module resolves that list into concrete context names (static values
|
|
6
7
|
* verbatim; every dynamic element is resolved by the agent's init job) and
|
|
7
8
|
* folds the per-context secrets/variables last-wins. It keeps the heavy fold logic
|
|
8
9
|
* out of `dispatchMatchedWorkflow`, which must stay under the function-length cap.
|
|
9
10
|
*/
|
|
10
|
-
import { type Context, type HostFacts, type LockJob } from '@kici-dev/engine';
|
|
11
|
+
import { type Context, type HostFacts, type LockJob, type LockWorkflow } from '@kici-dev/engine';
|
|
11
12
|
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
12
13
|
import type { VariableStore } from '../contexts/variable-store.js';
|
|
13
14
|
/**
|
|
@@ -23,12 +24,25 @@ export interface ResolvedJobContexts {
|
|
|
23
24
|
/** True when a dynamic element must be resolved by an agent init job. */
|
|
24
25
|
needsInit: boolean;
|
|
25
26
|
}
|
|
27
|
+
/** One bound-context element, as a lock job carries it. */
|
|
28
|
+
export type ContextRef = NonNullable<LockJob['contexts']>[number];
|
|
26
29
|
/**
|
|
27
|
-
*
|
|
30
|
+
* The ordered context elements a job binds: the workflow-level names first,
|
|
31
|
+
* then the job's own. Placing the workflow names first means a job-level
|
|
32
|
+
* context wins a key collision, since later contexts override earlier ones.
|
|
33
|
+
*
|
|
34
|
+
* A workflow-level name the job also binds statically is dropped from the
|
|
35
|
+
* workflow part, so each context is gated and resolved once, at the job's own
|
|
36
|
+
* position. Every gating site reads its names through this function, so a
|
|
37
|
+
* workflow-level context passes the same gates a job-level one does.
|
|
38
|
+
*/
|
|
39
|
+
export declare function effectiveContextRefs(workflow: Pick<LockWorkflow, 'contexts'>, job: Pick<LockJob, 'contexts'>): ContextRef[];
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the ordered bound-context names from a lock job and its workflow. Static elements
|
|
28
42
|
* use their value verbatim; a dynamic element is resolved by the agent's init
|
|
29
43
|
* job and flags `needsInit`.
|
|
30
44
|
*/
|
|
31
|
-
export declare function resolveJobContextNames(lockJob: LockJob): ResolvedJobContexts;
|
|
45
|
+
export declare function resolveJobContextNames(workflow: Pick<LockWorkflow, 'contexts'>, lockJob: Pick<LockJob, 'contexts'>): ResolvedJobContexts;
|
|
32
46
|
/**
|
|
33
47
|
* Build the ordered bound-context display list for persistence at dispatch.
|
|
34
48
|
* Unlike {@link resolveJobContextNames}, this never drops an unresolved
|
|
@@ -38,7 +52,7 @@ export declare function resolveJobContextNames(lockJob: LockJob): ResolvedJobCon
|
|
|
38
52
|
* The deferred-init flow-back overwrites the placeholder once the agent
|
|
39
53
|
* resolves the name. Returns an empty array when the job binds no context.
|
|
40
54
|
*/
|
|
41
|
-
export declare function buildJobContextDisplayNames(lockJob: LockJob): string[];
|
|
55
|
+
export declare function buildJobContextDisplayNames(workflow: Pick<LockWorkflow, 'contexts'>, lockJob: Pick<LockJob, 'contexts'>): string[];
|
|
42
56
|
/** Merged secrets/variables across an ordered list of resolved contexts. */
|
|
43
57
|
export interface MultiEnvMergedData {
|
|
44
58
|
contextVars?: Record<string, string>;
|
|
@@ -52,7 +66,8 @@ export interface MultiEnvMergedData {
|
|
|
52
66
|
* then folded in array order so a later context overrides an earlier key.
|
|
53
67
|
* Secrets are also returned namespaced per context so qualified
|
|
54
68
|
* `<env>:<secret>` references still resolve. `entries` carries the matched
|
|
55
|
-
* `Context` for each name (in order); variables resolve by
|
|
69
|
+
* `Context` for each name (in order); variables and secrets both resolve by the
|
|
70
|
+
* matched context's id, so a glob context contributes what its own row holds.
|
|
56
71
|
*/
|
|
57
72
|
export declare function resolveMultiEnvMergedData(args: {
|
|
58
73
|
deps: {
|
|
@@ -15,19 +15,31 @@
|
|
|
15
15
|
import { type Kysely } from 'kysely';
|
|
16
16
|
import type { Database } from '../db/types.js';
|
|
17
17
|
import type { WorkflowDispatchContext } from './dispatch-matched-workflow.js';
|
|
18
|
+
import { type SerializableGlobalDispatchIdentity } from './global-dispatch-identity.js';
|
|
18
19
|
/**
|
|
19
20
|
* The serializable subset of a `WorkflowDispatchContext` — everything except
|
|
20
21
|
* the live `deps` and `bundle`, which are rebuilt on resume. Every field here
|
|
21
22
|
* is JSON-safe (the event, payload, and lock file already ride the WS protocol
|
|
22
|
-
* as JSON).
|
|
23
|
+
* as JSON). A global run's identity is stored without its workflow bundle and
|
|
24
|
+
* credentials, which the resume re-derives.
|
|
23
25
|
*/
|
|
24
|
-
export type SerializableWorkflowDispatchInputs = Omit<WorkflowDispatchContext, 'deps' | 'bundle'
|
|
26
|
+
export type SerializableWorkflowDispatchInputs = Omit<WorkflowDispatchContext, 'deps' | 'bundle' | 'global'> & {
|
|
27
|
+
global?: SerializableGlobalDispatchIdentity;
|
|
28
|
+
};
|
|
29
|
+
/** Stored inputs as a read returns them. */
|
|
30
|
+
export type LoadedWorkflowDispatchInputs = SerializableWorkflowDispatchInputs & {
|
|
31
|
+
/**
|
|
32
|
+
* Set when the row's sealed secret fields could not be decrypted; the
|
|
33
|
+
* inputs then carry none of them, and the resume abandons the run.
|
|
34
|
+
*/
|
|
35
|
+
secretsUnavailable?: string;
|
|
36
|
+
};
|
|
25
37
|
/** Extract the serializable inputs from a live dispatch context. */
|
|
26
38
|
export declare function toSerializableInputs(ctx: WorkflowDispatchContext): SerializableWorkflowDispatchInputs;
|
|
27
39
|
/** Persist the pending workflow context to the in-memory Map and the DB. */
|
|
28
40
|
export declare function storePendingWorkflowContext(db: Kysely<Database> | undefined, inputs: SerializableWorkflowDispatchInputs): Promise<void>;
|
|
29
41
|
/** Load the pending workflow context by run id (memory first, then DB). */
|
|
30
|
-
export declare function loadPendingWorkflowContext(db: Kysely<Database> | undefined, runId: string): Promise<
|
|
42
|
+
export declare function loadPendingWorkflowContext(db: Kysely<Database> | undefined, runId: string): Promise<LoadedWorkflowDispatchInputs | null>;
|
|
31
43
|
/** Delete the pending workflow context from the in-memory Map and the DB. */
|
|
32
44
|
export declare function deletePendingWorkflowContext(db: Kysely<Database> | undefined, runId: string): Promise<void>;
|
|
33
45
|
/**
|
|
@@ -21,6 +21,7 @@ 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 { RegisteredWorkflow } from '../registration/registration-index.js';
|
|
24
25
|
import { type TrustResolution } from '../security/trust-resolver.js';
|
|
25
26
|
import { type ForkSwitchContext, type TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
26
27
|
import { type WorkflowModification } from '../security/workflow-diff.js';
|
|
@@ -200,9 +201,16 @@ export declare function dispatchGlobalWorkflowsForOtherRepos(args: {
|
|
|
200
201
|
ref: string;
|
|
201
202
|
dispatchBundle: ProviderBundle;
|
|
202
203
|
dispatchCredentials: Record<string, unknown>;
|
|
203
|
-
/** The inbound event's own bundle, used to post the
|
|
204
|
+
/** The inbound event's own bundle + credentials, used to post the failed-round check. */
|
|
204
205
|
bundle: ProviderBundle;
|
|
205
206
|
credentials: Record<string, unknown>;
|
|
207
|
+
/** The event's trust resolution, recorded on each global run. */
|
|
208
|
+
trustResolution: TrustResolution | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* The event's trust-policy verdict. It applies to each global run exactly as
|
|
211
|
+
* it applies to the per-repository ones: a `hold` parks the run in the
|
|
212
|
+
* security queue next to the pull request's own runs.
|
|
213
|
+
*/
|
|
206
214
|
securityDecision: TrustPolicyOutcome;
|
|
207
215
|
/**
|
|
208
216
|
* The source `dispatchCredentials` belongs to, when a cross-provider lock-file
|
|
@@ -216,6 +224,35 @@ export declare function dispatchGlobalWorkflowsForOtherRepos(args: {
|
|
|
216
224
|
* the re-run of a failed evaluation round, which re-decides that round alone.
|
|
217
225
|
*/
|
|
218
226
|
onlyWorkflowRepo?: string;
|
|
227
|
+
/**
|
|
228
|
+
* Dispatch only the candidates that need the evaluation round. Set by the
|
|
229
|
+
* re-evaluation of a failed or held round: the workflow repository's other
|
|
230
|
+
* candidates reached their outcome on the original delivery.
|
|
231
|
+
*/
|
|
232
|
+
onlyRoundCandidates?: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* Set with `onlyRoundCandidates` when the round being re-evaluated is a held
|
|
235
|
+
* round whose security hold was just approved. That hold also held every
|
|
236
|
+
* result-aware-only candidate of the repository, so the release dispatches
|
|
237
|
+
* them. A failed round's re-run leaves it unset: the delivery already
|
|
238
|
+
* dispatched those candidates.
|
|
239
|
+
*/
|
|
240
|
+
releasedHold?: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Asked once the round has decided, before anything it admitted dispatches.
|
|
243
|
+
* Resolving false withdraws those verdicts: nothing the round admitted
|
|
244
|
+
* dispatches, and its workflow repositories are not reported as decided. Set
|
|
245
|
+
* by the release of a held round, whose run may have been cancelled while
|
|
246
|
+
* the round ran.
|
|
247
|
+
*/
|
|
248
|
+
admitRoundVerdicts?: () => Promise<boolean>;
|
|
249
|
+
/**
|
|
250
|
+
* The global registrations to evaluate, in place of the ones the index holds
|
|
251
|
+
* for this event. Set by the release of a held round, which evaluates the
|
|
252
|
+
* workflow repository as it was at the commit the hold recorded: the round,
|
|
253
|
+
* its partition, and the dispatch after it all read these entries.
|
|
254
|
+
*/
|
|
255
|
+
registrations?: readonly RegisteredWorkflow[];
|
|
219
256
|
}): Promise<GlobalDispatchOutcome>;
|
|
220
257
|
/**
|
|
221
258
|
* Process a webhook through the complete pipeline, recording a `failed` event-log
|