@kici-dev/orchestrator 0.1.26 → 0.1.27
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/app.d.ts +13 -4
- package/dist/approvals/apply-decision.d.ts +1 -1
- package/dist/approvals/step-approval-bridge.d.ts +1 -1
- package/dist/cache/pending-inits.d.ts +4 -4
- package/dist/cli/api-client.d.ts +29 -4
- package/dist/cli/commands/context.d.ts +25 -0
- package/dist/cli/commands/debug-bundle.d.ts +1 -1
- package/dist/cli/commands/variable.d.ts +6 -6
- package/dist/cli/service/windows.d.ts +6 -0
- package/dist/cli.js +526 -340
- package/dist/config.d.ts +16 -0
- package/dist/contexts/binding-store.d.ts +36 -0
- package/dist/contexts/context-store.d.ts +87 -0
- package/dist/{environments → contexts}/held-runs.d.ts +15 -15
- package/dist/{environments → contexts}/index.d.ts +1 -1
- package/dist/contexts/protection/aggregate.d.ts +43 -0
- package/dist/contexts/protection/branch-gate.d.ts +5 -0
- package/dist/contexts/protection/concurrency-gate.d.ts +7 -0
- package/dist/{environments → contexts}/protection/pipeline.d.ts +3 -3
- package/dist/contexts/protection/reviewer-gate.d.ts +7 -0
- package/dist/{environments → contexts}/protection/satisfiability.d.ts +21 -21
- package/dist/contexts/protection/trust-gate.d.ts +7 -0
- package/dist/contexts/protection/wait-timer-gate.d.ts +7 -0
- package/dist/contexts/variable-store.d.ts +39 -0
- package/dist/dashboard/attestation-filters.d.ts +10 -8
- package/dist/dashboard/handler.d.ts +28 -2
- package/dist/db/migrations/067_environments_to_contexts.d.ts +36 -0
- package/dist/db/migrations/068_request_idempotency.d.ts +17 -0
- package/dist/db/types.d.ts +78 -59
- package/dist/github-app-name-refresher/github-app-name-refresher.d.ts +1 -1
- package/dist/index.js +21 -6
- package/dist/metrics/prometheus.d.ts +2 -0
- package/dist/oidc/id-token-claims.d.ts +63 -0
- package/dist/oidc/jwt.d.ts +15 -0
- package/dist/oidc/local-dev-signer.d.ts +31 -0
- package/dist/oidc/local-mint.d.ts +77 -0
- package/dist/oidc/oidc-mint-registration.d.ts +49 -0
- package/dist/pipeline/decorating-secret-resolver.d.ts +4 -4
- package/dist/pipeline/dispatch-matched-workflow.d.ts +3 -3
- package/dist/pipeline/inline-eval.d.ts +2 -2
- package/dist/pipeline/install-secrets-resolver.d.ts +13 -13
- package/dist/pipeline/{job-environments.d.ts → job-contexts.d.ts} +23 -23
- package/dist/pipeline/manual-schedule.d.ts +8 -1
- package/dist/pipeline/processor.d.ts +6 -6
- package/dist/pipeline/request-idempotency.d.ts +28 -0
- package/dist/pipeline/rerun.d.ts +8 -0
- package/dist/pipeline/resume-workflow.d.ts +1 -1
- package/dist/pipeline/test-pipeline.d.ts +2 -2
- package/dist/provenance/trust-root.d.ts +7 -0
- package/dist/provider-registry.d.ts +8 -0
- package/dist/providers/local/index.d.ts +1 -0
- package/dist/providers/local/local-source-config.d.ts +1 -0
- package/dist/reporting/execution-tracker.d.ts +17 -7
- package/dist/reporting/run-aggregator.d.ts +5 -5
- package/dist/routes/admin-contexts.d.ts +47 -0
- package/dist/routes/admin-registrations.d.ts +2 -2
- package/dist/routes/github-webhook.d.ts +2 -2
- package/dist/secrets/context-secret-resolver.d.ts +30 -0
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/pg-secret-store.d.ts +3 -3
- package/dist/secrets/rbac.d.ts +1 -1
- package/dist/secrets/secret-resolver.d.ts +19 -19
- package/dist/security/comment-handler.d.ts +1 -1
- package/dist/server.js +1770 -1196
- package/dist/stale-detector/stale-run-detector.d.ts +1 -1
- package/dist/standalone.js +1649 -747
- package/dist/storage/blob-routes.d.ts +16 -0
- package/dist/ws/{dashboard-env-handler.d.ts → dashboard-context-handler.d.ts} +15 -15
- package/dist/ws/oidc-token-relay.d.ts +6 -0
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +50 -50
- package/dist/cli/commands/environment.d.ts +0 -25
- package/dist/environments/binding-store.d.ts +0 -36
- package/dist/environments/environment-store.d.ts +0 -87
- package/dist/environments/protection/aggregate.d.ts +0 -43
- package/dist/environments/protection/branch-gate.d.ts +0 -5
- package/dist/environments/protection/concurrency-gate.d.ts +0 -7
- package/dist/environments/protection/reviewer-gate.d.ts +0 -7
- package/dist/environments/protection/trust-gate.d.ts +0 -7
- package/dist/environments/protection/wait-timer-gate.d.ts +0 -7
- package/dist/environments/variable-store.d.ts +0 -39
- package/dist/routes/admin-environments.d.ts +0 -47
package/dist/config.d.ts
CHANGED
|
@@ -75,6 +75,15 @@ declare const configSchema: z.ZodObject<{
|
|
|
75
75
|
secretKeyOld: z.ZodOptional<z.ZodString>;
|
|
76
76
|
secretKeyFileOld: z.ZodOptional<z.ZodString>;
|
|
77
77
|
bootstrapAdminToken: z.ZodOptional<z.ZodString>;
|
|
78
|
+
independentSecrets: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
79
|
+
true: "true";
|
|
80
|
+
false: "false";
|
|
81
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
82
|
+
independentIdentity: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
83
|
+
true: "true";
|
|
84
|
+
false: "false";
|
|
85
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
86
|
+
devIdentityKeyFile: z.ZodOptional<z.ZodString>;
|
|
78
87
|
pgCustomerSecrets: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
79
88
|
true: "true";
|
|
80
89
|
false: "false";
|
|
@@ -101,6 +110,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
101
110
|
testMode: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
102
111
|
testEventFailFirstN: z.ZodOptional<z.ZodString>;
|
|
103
112
|
testMintDeferAudience: z.ZodOptional<z.ZodString>;
|
|
113
|
+
testMintRejectAudience: z.ZodOptional<z.ZodString>;
|
|
114
|
+
testRerunDelayMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
104
115
|
eventLogMaxPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
105
116
|
logLevel: z.ZodDefault<z.ZodEnum<{
|
|
106
117
|
error: "error";
|
|
@@ -203,6 +214,8 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
203
214
|
staleDetectorThresholdMultiplier: number;
|
|
204
215
|
jobHeartbeatIntervalMs: number;
|
|
205
216
|
githubAppNameRefreshIntervalMs: number;
|
|
217
|
+
independentSecrets: boolean;
|
|
218
|
+
independentIdentity: boolean;
|
|
206
219
|
pgCustomerSecrets: boolean;
|
|
207
220
|
agentAuth: "token" | "none";
|
|
208
221
|
agentTokenTtlMs: number;
|
|
@@ -273,9 +286,12 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
273
286
|
secretKeyOld?: string | undefined;
|
|
274
287
|
secretKeyFileOld?: string | undefined;
|
|
275
288
|
bootstrapAdminToken?: string | undefined;
|
|
289
|
+
devIdentityKeyFile?: string | undefined;
|
|
276
290
|
orchestratorHostAgentId?: string | undefined;
|
|
277
291
|
testEventFailFirstN?: string | undefined;
|
|
278
292
|
testMintDeferAudience?: string | undefined;
|
|
293
|
+
testMintRejectAudience?: string | undefined;
|
|
294
|
+
testRerunDelayMs?: number | undefined;
|
|
279
295
|
otelExporterOtlpEndpoint?: string | undefined;
|
|
280
296
|
clusterName?: string | undefined;
|
|
281
297
|
}>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binding store -- scope-to-context binding CRUD.
|
|
3
|
+
*
|
|
4
|
+
* Bindings map scope patterns (e.g. 'aws/prod/**') to contexts,
|
|
5
|
+
* controlling which scoped secrets are available in each context.
|
|
6
|
+
*/
|
|
7
|
+
import type { Kysely } from 'kysely';
|
|
8
|
+
import type { Database, ContextBinding } from '../db/types.js';
|
|
9
|
+
/**
|
|
10
|
+
* A scope→context binding with its host selector. `hostPattern` defaults to
|
|
11
|
+
* `'**'` (all hosts) when omitted.
|
|
12
|
+
*/
|
|
13
|
+
export interface BindingInput {
|
|
14
|
+
scopePattern: string;
|
|
15
|
+
hostPattern?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Data access layer for context bindings.
|
|
19
|
+
*/
|
|
20
|
+
export declare class BindingStore {
|
|
21
|
+
private readonly db;
|
|
22
|
+
constructor(db: Kysely<Database>);
|
|
23
|
+
/** List all bindings for an context. */
|
|
24
|
+
list(orgId: string, contextId: string): Promise<ContextBinding[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Replace all bindings for an context in a transaction.
|
|
27
|
+
*
|
|
28
|
+
* Deletes existing bindings and inserts the new set atomically. Each binding
|
|
29
|
+
* carries a `scopePattern` and an optional `hostPattern` (defaulting to
|
|
30
|
+
* `'**'`). Pass an empty array to clear all bindings.
|
|
31
|
+
*/
|
|
32
|
+
set(orgId: string, contextId: string, bindings: BindingInput[]): Promise<void>;
|
|
33
|
+
/** Find bindings for an context (alias for list, used by secret resolver). */
|
|
34
|
+
findBindingsForContext(orgId: string, contextId: string): Promise<ContextBinding[]>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=binding-store.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
import type { Context as EngineContext } from '@kici-dev/engine';
|
|
3
|
+
import type { Database, Context } from '../db/types.js';
|
|
4
|
+
/** Thrown by `delete` when pending held runs reference the context. */
|
|
5
|
+
export declare class ContextDeleteBlockedError extends Error {
|
|
6
|
+
readonly pendingCount: number;
|
|
7
|
+
constructor(pendingCount: number);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Map a DB context row (snake_case) to the engine Context type (camelCase).
|
|
11
|
+
*
|
|
12
|
+
* Kysely returns JSONB columns as strings; this function parses them into arrays.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toContext(row: Context): EngineContext;
|
|
15
|
+
/** Fields accepted when creating a context. */
|
|
16
|
+
export interface ContextCreateInput {
|
|
17
|
+
name: string;
|
|
18
|
+
type?: 'fixed' | 'glob';
|
|
19
|
+
globPattern?: string | null;
|
|
20
|
+
branchRestrictions?: string[];
|
|
21
|
+
triggerTypeFilters?: string[];
|
|
22
|
+
repoPatterns?: string[];
|
|
23
|
+
concurrencyLimit?: number | null;
|
|
24
|
+
concurrencyStrategy?: 'queue' | 'cancel-pending';
|
|
25
|
+
concurrencyTimeoutMs?: number;
|
|
26
|
+
requiredReviewers?: string[] | null;
|
|
27
|
+
waitTimerSeconds?: number | null;
|
|
28
|
+
holdExpirySeconds?: number;
|
|
29
|
+
minimumTrust?: 'known' | 'trusted' | null;
|
|
30
|
+
allowLocalExecution?: boolean;
|
|
31
|
+
enabled?: boolean;
|
|
32
|
+
createdBy?: string | null;
|
|
33
|
+
}
|
|
34
|
+
/** Fields accepted when updating a context. */
|
|
35
|
+
export interface ContextUpdateInput {
|
|
36
|
+
name?: string;
|
|
37
|
+
type?: 'fixed' | 'glob';
|
|
38
|
+
globPattern?: string | null;
|
|
39
|
+
branchRestrictions?: string[];
|
|
40
|
+
triggerTypeFilters?: string[];
|
|
41
|
+
repoPatterns?: string[];
|
|
42
|
+
concurrencyLimit?: number | null;
|
|
43
|
+
concurrencyStrategy?: 'queue' | 'cancel-pending';
|
|
44
|
+
concurrencyTimeoutMs?: number;
|
|
45
|
+
requiredReviewers?: string[] | null;
|
|
46
|
+
waitTimerSeconds?: number | null;
|
|
47
|
+
holdExpirySeconds?: number;
|
|
48
|
+
minimumTrust?: 'known' | 'trusted' | null;
|
|
49
|
+
allowLocalExecution?: boolean;
|
|
50
|
+
enabled?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Data access layer for contexts.
|
|
54
|
+
*/
|
|
55
|
+
export declare class ContextStore {
|
|
56
|
+
private readonly db;
|
|
57
|
+
constructor(db: Kysely<Database>);
|
|
58
|
+
/** List all contexts for an org, ordered by name. */
|
|
59
|
+
list(orgId: string): Promise<Context[]>;
|
|
60
|
+
/** Get a single context by org + id. Returns null if not found. */
|
|
61
|
+
get(orgId: string, id: string): Promise<Context | null>;
|
|
62
|
+
/** Get a single context by org + name. Returns null if not found. */
|
|
63
|
+
getByName(orgId: string, name: string): Promise<Context | null>;
|
|
64
|
+
/** Create a new context. Returns the created row. */
|
|
65
|
+
create(orgId: string, data: ContextCreateInput): Promise<Context>;
|
|
66
|
+
/** Update a context. Returns the updated row, or null if not found. */
|
|
67
|
+
update(orgId: string, id: string, updates: ContextUpdateInput): Promise<Context | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Delete a context.
|
|
70
|
+
*
|
|
71
|
+
* Bindings, variables, and source overrides cascade away via their FK.
|
|
72
|
+
* Terminal held-run history survives with a null `context_id` (the FK
|
|
73
|
+
* uses ON DELETE SET NULL). Pending held runs still reference the
|
|
74
|
+
* context, so deletion is blocked with `ContextDeleteBlockedError`
|
|
75
|
+
* until they are approved or rejected.
|
|
76
|
+
*/
|
|
77
|
+
delete(orgId: string, id: string): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* Match a context name against org contexts.
|
|
80
|
+
*
|
|
81
|
+
* First tries exact name match (for fixed contexts).
|
|
82
|
+
* If no exact match, scans glob-type contexts and uses picomatch.
|
|
83
|
+
* Returns the first matching context or null.
|
|
84
|
+
*/
|
|
85
|
+
matchContext(orgId: string, name: string): Promise<Context | null>;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=context-store.d.ts.map
|
|
@@ -18,16 +18,16 @@ export declare enum HeldRunStatus {
|
|
|
18
18
|
export interface CreateHeldRunData {
|
|
19
19
|
runId: string;
|
|
20
20
|
jobId: string;
|
|
21
|
-
|
|
21
|
+
contextId: string;
|
|
22
22
|
holdType: string;
|
|
23
23
|
reason: string;
|
|
24
24
|
expiresAt: Date;
|
|
25
|
-
/** Queue type: '
|
|
26
|
-
queueType?: '
|
|
25
|
+
/** Queue type: 'context' (default) or 'security'. */
|
|
26
|
+
queueType?: 'context' | 'security';
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Data required to create a generalized approval hold. Unlike the legacy
|
|
30
|
-
*
|
|
30
|
+
* context-only `create()`, this carries the hold scope, trigger source,
|
|
31
31
|
* optional step index, and the normalized approval requirement.
|
|
32
32
|
*/
|
|
33
33
|
export interface CreateHoldData {
|
|
@@ -37,14 +37,14 @@ export interface CreateHoldData {
|
|
|
37
37
|
scope: HoldScope;
|
|
38
38
|
/** Step index within the job for step-scoped holds; omit otherwise. */
|
|
39
39
|
stepIndex?: number;
|
|
40
|
-
/** What triggered the hold (
|
|
40
|
+
/** What triggered the hold (context policy vs SDK requireApproval). */
|
|
41
41
|
triggerSource: TriggerSource;
|
|
42
42
|
/** The normalized requirement the hold must satisfy. */
|
|
43
43
|
requirement: ApprovalRequirement;
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
/** Queue type: '
|
|
47
|
-
queueType?: '
|
|
44
|
+
/** Context id, when the hold originates from a context policy. */
|
|
45
|
+
contextId?: string | null;
|
|
46
|
+
/** Queue type: 'context' (default) or 'security'. */
|
|
47
|
+
queueType?: 'context' | 'security';
|
|
48
48
|
/**
|
|
49
49
|
* Held-run `hold_type` discriminator. Defaults to `'approval'` (reviewer
|
|
50
50
|
* holds). The workflow install gate sets `'wait_timer'` / `'concurrency'` so
|
|
@@ -79,7 +79,7 @@ export interface ReleaseSignal {
|
|
|
79
79
|
stepIndex: number | null;
|
|
80
80
|
/**
|
|
81
81
|
* What kind of gate created the hold. `explicit` (SDK `requireApproval`) holds
|
|
82
|
-
* a real root job and resumes by re-dispatching it; `
|
|
82
|
+
* a real root job and resumes by re-dispatching it; `context` covers the
|
|
83
83
|
* workflow install-gate (wait-timer / concurrency / env approval) which resumes
|
|
84
84
|
* by rebuilding the workflow dispatch context. The resume router keys off this
|
|
85
85
|
* so a workflow-scoped explicit hold goes through the job re-dispatch path
|
|
@@ -100,7 +100,7 @@ export declare class HeldRunStore {
|
|
|
100
100
|
create(orgId: string, data: CreateHeldRunData): Promise<HeldRun>;
|
|
101
101
|
/**
|
|
102
102
|
* Create a generalized approval hold (workflow/job/step scope, explicit or
|
|
103
|
-
*
|
|
103
|
+
* context trigger) carrying a normalized `ApprovalRequirement`. Returns
|
|
104
104
|
* the created row.
|
|
105
105
|
*/
|
|
106
106
|
createHold(orgId: string, data: CreateHoldData): Promise<HeldRun>;
|
|
@@ -151,13 +151,13 @@ export declare class HeldRunStore {
|
|
|
151
151
|
/** List held runs for an org with optional filters. */
|
|
152
152
|
listAll(orgId: string, options?: ListHeldRunsOptions): Promise<HeldRun[]>;
|
|
153
153
|
/** List held runs for an org filtered by queue type with optional filters. */
|
|
154
|
-
listByQueueType(orgId: string, queueType: '
|
|
154
|
+
listByQueueType(orgId: string, queueType: 'context' | 'security', options?: ListHeldRunsOptions): Promise<HeldRun[]>;
|
|
155
155
|
/**
|
|
156
156
|
* Approve a pending held run, enforcing queue_type boundary.
|
|
157
|
-
* Prevents
|
|
157
|
+
* Prevents context approvals from approving security holds and vice versa.
|
|
158
158
|
* Throws if not found, not pending, or queue_type mismatch.
|
|
159
159
|
*/
|
|
160
|
-
approveByQueueType(orgId: string, heldRunId: string, approvedBy: string, queueType: '
|
|
160
|
+
approveByQueueType(orgId: string, heldRunId: string, approvedBy: string, queueType: 'context' | 'security'): Promise<HeldRun>;
|
|
161
161
|
/** Get a held run by run ID and job ID. Returns null if not found. */
|
|
162
162
|
getByRunAndJob(orgId: string, runId: string, jobId: string): Promise<HeldRun | null>;
|
|
163
163
|
/**
|
|
@@ -181,7 +181,7 @@ export declare class HeldRunStore {
|
|
|
181
181
|
* when a concurrency slot frees on run completion. Flips the oldest matching
|
|
182
182
|
* pending row to `released` and returns its `ReleaseSignal`, or null when no
|
|
183
183
|
* concurrency hold is queued for the group. `group` matches the held row's
|
|
184
|
-
* `
|
|
184
|
+
* `context_id` (workflow-level install concurrency keys on the env id).
|
|
185
185
|
*/
|
|
186
186
|
releaseConcurrencyHold(orgId: string, group: string): Promise<ReleaseSignal | null>;
|
|
187
187
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { ContextStore, type ContextCreateInput, type ContextUpdateInput } from './context-store.js';
|
|
2
2
|
export { BindingStore } from './binding-store.js';
|
|
3
3
|
export { VariableStore } from './variable-store.js';
|
|
4
4
|
export { HeldRunStore, type CreateHeldRunData, type ListHeldRunsOptions } from './held-runs.js';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ContextGateRejectReason, type Context } from '@kici-dev/engine';
|
|
2
|
+
import type { JobDispatchContext } from './pipeline.js';
|
|
3
|
+
/** A single context's rejection under all-must-pass aggregation. */
|
|
4
|
+
export interface ContextGateRejection {
|
|
5
|
+
context: string;
|
|
6
|
+
reason: ContextGateRejectReason;
|
|
7
|
+
detail: string;
|
|
8
|
+
}
|
|
9
|
+
/** Effective protection parameters after most-restrictive aggregation. */
|
|
10
|
+
export interface EffectiveProtection {
|
|
11
|
+
minimumTrust?: 'known' | 'trusted';
|
|
12
|
+
requiredReviewers: string[];
|
|
13
|
+
waitTimerSeconds: number | null;
|
|
14
|
+
holdExpirySeconds: number;
|
|
15
|
+
concurrencyLimit: number | null;
|
|
16
|
+
concurrencyStrategy: 'queue' | 'cancel-pending';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Evaluate each context's hard reject gates against the run context. A name
|
|
20
|
+
* with no `Context` record yields a `context_not_found` rejection. Returns all
|
|
21
|
+
* rejections (empty = every context passed the reject gates).
|
|
22
|
+
*/
|
|
23
|
+
export declare function evaluateMultiContextGates(envs: ReadonlyArray<{
|
|
24
|
+
name: string;
|
|
25
|
+
env: Context | undefined;
|
|
26
|
+
}>, ctx: JobDispatchContext): ContextGateRejection[];
|
|
27
|
+
/**
|
|
28
|
+
* Aggregate hold/wait/queue parameters across all bound contexts, most
|
|
29
|
+
* restrictive wins: trust = max tier, reviewers = sorted dedup union, wait timer
|
|
30
|
+
* = max, hold expiry = min, concurrency limit = min (tightest). The concurrency
|
|
31
|
+
* strategy follows the primary (first) context.
|
|
32
|
+
*/
|
|
33
|
+
export declare function aggregateProtectionParams(envs: ReadonlyArray<Context>): EffectiveProtection;
|
|
34
|
+
/**
|
|
35
|
+
* Build a synthetic `Context` carrying the aggregated protection parameters,
|
|
36
|
+
* so the existing per-rule gate functions (trust/concurrency/reviewer/wait) can
|
|
37
|
+
* evaluate the all-must-pass holds in one pass. Branch/trigger/repo/enabled are
|
|
38
|
+
* already handled by `evaluateMultiContextGates`, so they are neutralized here.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildEffectiveContext(primary: Context, eff: EffectiveProtection): Context;
|
|
41
|
+
/** Format a human-readable rejection reason naming the env(s) and rule(s). */
|
|
42
|
+
export declare function formatMultiContextRejection(rejections: ReadonlyArray<ContextGateRejection>): string;
|
|
43
|
+
//# sourceMappingURL=aggregate.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Context, ProtectionGateResult } from '@kici-dev/engine';
|
|
2
|
+
import type { JobDispatchContext } from './pipeline.js';
|
|
3
|
+
/** Evaluate branch restrictions, trigger type filters, and repo patterns. */
|
|
4
|
+
export declare function evaluateBranchGate(env: Context, ctx: JobDispatchContext): ProtectionGateResult;
|
|
5
|
+
//# sourceMappingURL=branch-gate.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency gate -- checks concurrency limits.
|
|
3
|
+
*/
|
|
4
|
+
import type { Context, ProtectionGateResult } from '@kici-dev/engine';
|
|
5
|
+
/** Evaluate concurrency limits for the context. */
|
|
6
|
+
export declare function evaluateConcurrencyGate(env: Context, currentRunningCount: number, _concurrencyGroup: string): ProtectionGateResult;
|
|
7
|
+
//# sourceMappingURL=concurrency-gate.d.ts.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Gates evaluated in order: branch -> trust -> concurrency -> reviewer -> timer.
|
|
5
5
|
* First non-pass result stops evaluation.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { Context, ProtectionGateResult, TrustTier } from '@kici-dev/engine';
|
|
8
8
|
/** Context needed for protection gate evaluation. */
|
|
9
9
|
export interface JobDispatchContext {
|
|
10
10
|
branch: string;
|
|
@@ -13,6 +13,6 @@ export interface JobDispatchContext {
|
|
|
13
13
|
runId: string;
|
|
14
14
|
jobId: string;
|
|
15
15
|
}
|
|
16
|
-
/** Evaluate all protection rules for
|
|
17
|
-
export declare function evaluateProtectionRules(env:
|
|
16
|
+
/** Evaluate all protection rules for a context. */
|
|
17
|
+
export declare function evaluateProtectionRules(env: Context, ctx: JobDispatchContext, currentRunningCount: number, concurrencyGroup: string, trustTier?: TrustTier): Promise<ProtectionGateResult>;
|
|
18
18
|
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reviewer gate -- checks required reviewers.
|
|
3
|
+
*/
|
|
4
|
+
import type { Context, ProtectionGateResult } from '@kici-dev/engine';
|
|
5
|
+
/** Evaluate reviewer requirements for the context. */
|
|
6
|
+
export declare function evaluateReviewerGate(env: Context): ProtectionGateResult;
|
|
7
|
+
//# sourceMappingURL=reviewer-gate.d.ts.map
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registration-time satisfiability check for multi-
|
|
2
|
+
* Registration-time satisfiability check for multi-context job bindings.
|
|
3
3
|
*
|
|
4
|
-
* When a job binds several
|
|
5
|
-
*
|
|
4
|
+
* When a job binds several contexts (`contexts: [...]`), every
|
|
5
|
+
* context's protection gates must pass for the job to ever dispatch
|
|
6
6
|
* (all-must-pass aggregation — see `aggregate.ts`). Some of those gates are
|
|
7
|
-
* decidable statically: if two bound
|
|
7
|
+
* decidable statically: if two bound contexts restrict to disjoint fixed
|
|
8
8
|
* branch sets, no run can ever satisfy both, so the binding is provably
|
|
9
9
|
* unsatisfiable and should be rejected at registration rather than failing
|
|
10
10
|
* silently at every future dispatch.
|
|
11
11
|
*
|
|
12
12
|
* This module intersects the statically-decidable set rules (branch, trigger
|
|
13
13
|
* type, repository — only when every pattern is a literal, never a glob) plus
|
|
14
|
-
* the `enabled` gate across the **resolved** bound
|
|
15
|
-
* first provably-empty intersection. Bound names that resolve to no
|
|
14
|
+
* the `enabled` gate across the **resolved** bound contexts, and reports the
|
|
15
|
+
* first provably-empty intersection. Bound names that resolve to no context
|
|
16
16
|
* record are lenient — skipped, never rejected — matching the dispatch-time
|
|
17
17
|
* behavior in `dispatch-matched-workflow.ts`. Any glob in a rule makes that rule
|
|
18
18
|
* undecidable, so it is skipped here and left to the dispatch-time catch-all
|
|
19
|
-
* (`
|
|
19
|
+
* (`evaluateMultiContextGates`).
|
|
20
20
|
*/
|
|
21
21
|
import { z } from 'zod';
|
|
22
|
-
import type {
|
|
22
|
+
import type { Context } from '@kici-dev/engine';
|
|
23
23
|
/** Which decidable rule made a binding unsatisfiable. */
|
|
24
24
|
export declare const UnsatisfiableRule: z.ZodEnum<{
|
|
25
25
|
enabled: "enabled";
|
|
@@ -28,42 +28,42 @@ export declare const UnsatisfiableRule: z.ZodEnum<{
|
|
|
28
28
|
trigger: "trigger";
|
|
29
29
|
}>;
|
|
30
30
|
export type UnsatisfiableRule = z.infer<typeof UnsatisfiableRule>;
|
|
31
|
-
/** A provably-unsatisfiable multi-
|
|
31
|
+
/** A provably-unsatisfiable multi-context binding, naming the rule + reason. */
|
|
32
32
|
export interface UnsatisfiableBinding {
|
|
33
33
|
jobName: string;
|
|
34
|
-
|
|
34
|
+
contexts: string[];
|
|
35
35
|
rule: UnsatisfiableRule;
|
|
36
36
|
message: string;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* Returns a precise problem when the bound
|
|
40
|
-
* satisfied (a disabled
|
|
41
|
-
* among the resolved
|
|
42
|
-
* skipped — a bound name with no
|
|
39
|
+
* Returns a precise problem when the bound contexts can NEVER be jointly
|
|
40
|
+
* satisfied (a disabled context, or mutually-exclusive fixed restrictions
|
|
41
|
+
* among the resolved contexts), else `null`. Missing (unresolved) names are
|
|
42
|
+
* skipped — a bound name with no context record contributes no protection
|
|
43
43
|
* rules and is lenient at dispatch, so it is not rejected here. Glob /
|
|
44
44
|
* undecidable cases also return `null` and are caught at dispatch by
|
|
45
|
-
* `
|
|
45
|
+
* `evaluateMultiContextGates`.
|
|
46
46
|
*
|
|
47
|
-
* `envs[i]` is the resolved `
|
|
48
|
-
* name has no
|
|
47
|
+
* `envs[i]` is the resolved `Context` for `envNames[i]` (undefined when the
|
|
48
|
+
* name has no context record). Only the statically-known (non-dynamic) bound
|
|
49
49
|
* names should be passed — dynamic elements are unknown at registration and the
|
|
50
50
|
* all-must-pass semantics make the static subset's exclusivity still sound.
|
|
51
51
|
*/
|
|
52
|
-
export declare function checkBindingSatisfiable(jobName: string, envs: ReadonlyArray<
|
|
52
|
+
export declare function checkBindingSatisfiable(jobName: string, envs: ReadonlyArray<Context | undefined>, envNames: readonly string[]): UnsatisfiableBinding | null;
|
|
53
53
|
/** Minimal lock-workflow shape needed to walk its jobs for satisfiability. */
|
|
54
54
|
interface SatisfiabilityLockWorkflow {
|
|
55
55
|
jobs?: readonly unknown[];
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* Walk every workflow's static jobs and reject the registration when a bound
|
|
59
|
-
*
|
|
59
|
+
* context list is provably unsatisfiable (a disabled context, or
|
|
60
60
|
* mutually-exclusive fixed branch/trigger/repo restrictions among the resolved
|
|
61
|
-
*
|
|
61
|
+
* contexts — missing names are lenient, never rejected). Dynamic elements
|
|
62
62
|
* are skipped (unresolvable at registration); the all-must-pass semantics keep
|
|
63
63
|
* the static subset's exclusivity sound. Throws the first
|
|
64
64
|
* `UnsatisfiableBinding.message` so the registration route / direct helper
|
|
65
65
|
* surfaces it to the caller.
|
|
66
66
|
*/
|
|
67
|
-
export declare function assertWorkflowsSatisfiable(workflows: ReadonlyArray<SatisfiabilityLockWorkflow>, resolveEnv: (name: string) => Promise<
|
|
67
|
+
export declare function assertWorkflowsSatisfiable(workflows: ReadonlyArray<SatisfiabilityLockWorkflow>, resolveEnv: (name: string) => Promise<Context | null>): Promise<void>;
|
|
68
68
|
export {};
|
|
69
69
|
//# sourceMappingURL=satisfiability.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust gate -- checks contributor trust tier against context minimumTrust.
|
|
3
|
+
*/
|
|
4
|
+
import type { Context, ProtectionGateResult, TrustTier } from '@kici-dev/engine';
|
|
5
|
+
/** Evaluate minimumTrust requirements for the context. */
|
|
6
|
+
export declare function evaluateTrustGate(env: Context, trustTier: TrustTier | undefined): ProtectionGateResult;
|
|
7
|
+
//# sourceMappingURL=trust-gate.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wait timer gate -- checks wait timer configuration.
|
|
3
|
+
*/
|
|
4
|
+
import type { Context, ProtectionGateResult } from '@kici-dev/engine';
|
|
5
|
+
/** Evaluate wait timer for the context. */
|
|
6
|
+
export declare function evaluateWaitTimerGate(env: Context): ProtectionGateResult;
|
|
7
|
+
//# sourceMappingURL=wait-timer-gate.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable store -- context variable CRUD with lock enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Manages org-level context variables and per-source overrides.
|
|
5
|
+
* Lock enforcement: locked org vars cannot be overridden by source overrides.
|
|
6
|
+
*/
|
|
7
|
+
import { type Kysely } from 'kysely';
|
|
8
|
+
import type { Database, ContextVariable, ContextSourceOverride } from '../db/types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Data access layer for context variables and source overrides.
|
|
11
|
+
*/
|
|
12
|
+
export declare class VariableStore {
|
|
13
|
+
private readonly db;
|
|
14
|
+
constructor(db: Kysely<Database>);
|
|
15
|
+
/** List all org-level variables for a context. */
|
|
16
|
+
listVars(orgId: string, contextId: string): Promise<ContextVariable[]>;
|
|
17
|
+
/** Upsert an org-level variable. */
|
|
18
|
+
setVar(orgId: string, contextId: string, key: string, value: string, locked?: boolean): Promise<void>;
|
|
19
|
+
/** Delete an org-level variable. */
|
|
20
|
+
deleteVar(orgId: string, contextId: string, key: string): Promise<void>;
|
|
21
|
+
/** List source overrides for a specific source (routing key). */
|
|
22
|
+
listSourceOverrides(orgId: string, contextId: string, routingKey: string): Promise<ContextSourceOverride[]>;
|
|
23
|
+
/** Upsert a source override. */
|
|
24
|
+
setSourceOverride(orgId: string, contextId: string, routingKey: string, key: string, value: string): Promise<void>;
|
|
25
|
+
/** Delete a source override. */
|
|
26
|
+
deleteSourceOverride(orgId: string, contextId: string, routingKey: string, key: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get resolved variables for a context, optionally merged with source overrides.
|
|
29
|
+
*
|
|
30
|
+
* Merge rules:
|
|
31
|
+
* - Org-level vars form the base
|
|
32
|
+
* - Source overrides can add new keys and override unlocked org vars
|
|
33
|
+
* - Locked org vars are NOT overridden by source overrides
|
|
34
|
+
*
|
|
35
|
+
* @param routingKey - If provided, source overrides are merged in
|
|
36
|
+
*/
|
|
37
|
+
getResolvedVars(orgId: string, contextId: string, routingKey?: string): Promise<Record<string, string>>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=variable-store.d.ts.map
|
|
@@ -13,7 +13,7 @@ import type { Database } from '../db/types.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function baseAttestationsQuery(db: Kysely<Database>): import("kysely").SelectQueryBuilder<{
|
|
15
15
|
cluster_meta: import("../db/types.js").ClusterMetaTable;
|
|
16
|
-
|
|
16
|
+
contexts: import("../db/types.js").ContextsTable;
|
|
17
17
|
host_roster: import("../db/types.js").HostRosterTable;
|
|
18
18
|
dispatch_queue: import("../db/types.js").DispatchQueueTable;
|
|
19
19
|
dedup_cache: import("../db/types.js").DedupCacheTable;
|
|
@@ -24,9 +24,9 @@ export declare function baseAttestationsQuery(db: Kysely<Database>): import("kys
|
|
|
24
24
|
raft_state: import("../db/types.js").RaftStateTable;
|
|
25
25
|
secret_audit_log: import("../db/types.js").SecretAuditLogTable;
|
|
26
26
|
scoped_secrets: import("../db/types.js").ScopedSecretsTable;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
context_bindings: import("../db/types.js").ContextBindingsTable;
|
|
28
|
+
context_variables: import("../db/types.js").ContextVariablesTable;
|
|
29
|
+
context_source_overrides: import("../db/types.js").ContextSourceOverridesTable;
|
|
30
30
|
held_runs: import("../db/types.js").HeldRunsTable;
|
|
31
31
|
held_run_approvals: import("../db/types.js").HeldRunApprovalsTable;
|
|
32
32
|
admin_tokens: import("../db/types.js").AdminTokenTable;
|
|
@@ -59,6 +59,7 @@ export declare function baseAttestationsQuery(db: Kysely<Database>): import("kys
|
|
|
59
59
|
attestations: import("../db/types.js").AttestationsTable;
|
|
60
60
|
pending_attestations: import("../db/types.js").PendingAttestationsTable;
|
|
61
61
|
remote_sources: import("../db/types.js").RemoteSourcesTable;
|
|
62
|
+
request_idempotency: import("../db/types.js").RequestIdempotencyTable;
|
|
62
63
|
}, "execution_runs" | "execution_jobs" | "attestations", {}>;
|
|
63
64
|
export type AttestationsBaseQuery = ReturnType<typeof baseAttestationsQuery>;
|
|
64
65
|
/**
|
|
@@ -74,7 +75,7 @@ export type AttestationsBaseQuery = ReturnType<typeof baseAttestationsQuery>;
|
|
|
74
75
|
*/
|
|
75
76
|
export declare function basePendingAttestationsQuery(db: Kysely<Database>): import("kysely").SelectQueryBuilder<{
|
|
76
77
|
cluster_meta: import("../db/types.js").ClusterMetaTable;
|
|
77
|
-
|
|
78
|
+
contexts: import("../db/types.js").ContextsTable;
|
|
78
79
|
host_roster: import("../db/types.js").HostRosterTable;
|
|
79
80
|
dispatch_queue: import("../db/types.js").DispatchQueueTable;
|
|
80
81
|
dedup_cache: import("../db/types.js").DedupCacheTable;
|
|
@@ -85,9 +86,9 @@ export declare function basePendingAttestationsQuery(db: Kysely<Database>): impo
|
|
|
85
86
|
raft_state: import("../db/types.js").RaftStateTable;
|
|
86
87
|
secret_audit_log: import("../db/types.js").SecretAuditLogTable;
|
|
87
88
|
scoped_secrets: import("../db/types.js").ScopedSecretsTable;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
context_bindings: import("../db/types.js").ContextBindingsTable;
|
|
90
|
+
context_variables: import("../db/types.js").ContextVariablesTable;
|
|
91
|
+
context_source_overrides: import("../db/types.js").ContextSourceOverridesTable;
|
|
91
92
|
held_runs: import("../db/types.js").HeldRunsTable;
|
|
92
93
|
held_run_approvals: import("../db/types.js").HeldRunApprovalsTable;
|
|
93
94
|
admin_tokens: import("../db/types.js").AdminTokenTable;
|
|
@@ -120,6 +121,7 @@ export declare function basePendingAttestationsQuery(db: Kysely<Database>): impo
|
|
|
120
121
|
attestations: import("../db/types.js").AttestationsTable;
|
|
121
122
|
pending_attestations: import("../db/types.js").PendingAttestationsTable;
|
|
122
123
|
remote_sources: import("../db/types.js").RemoteSourcesTable;
|
|
124
|
+
request_idempotency: import("../db/types.js").RequestIdempotencyTable;
|
|
123
125
|
}, "execution_runs" | "execution_jobs" | "pending_attestations", {}>;
|
|
124
126
|
/**
|
|
125
127
|
* Apply org-wide attestation filters to the base query. Digest is exact-match;
|
|
@@ -70,7 +70,14 @@ interface DashboardHandlerDeps {
|
|
|
70
70
|
* compat with mixed deploys; absent means the orchestrator falls
|
|
71
71
|
* back to the legacy "no PG row → throw" path.
|
|
72
72
|
*/
|
|
73
|
-
onRerun: (runId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null,
|
|
73
|
+
onRerun: (runId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null,
|
|
74
|
+
/**
|
|
75
|
+
* Platform-minted `requestId` for this rerun. Stable across an HA relay
|
|
76
|
+
* failover re-send, so the orchestrator uses it as an idempotency key: a
|
|
77
|
+
* re-sent request returns the first coordinator's run instead of minting a
|
|
78
|
+
* second one.
|
|
79
|
+
*/
|
|
80
|
+
requestId: string, routingKey?: string) => Promise<{
|
|
74
81
|
newRunId: string;
|
|
75
82
|
}>;
|
|
76
83
|
/**
|
|
@@ -85,7 +92,13 @@ interface DashboardHandlerDeps {
|
|
|
85
92
|
* Callback for handling manual schedule trigger requests.
|
|
86
93
|
* Returns { newRunId } on success or throws on failure.
|
|
87
94
|
*/
|
|
88
|
-
onManualSchedule: (registrationId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null
|
|
95
|
+
onManualSchedule: (registrationId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null,
|
|
96
|
+
/**
|
|
97
|
+
* Platform-minted `requestId` for this manual schedule. Used as an
|
|
98
|
+
* idempotency key so an HA relay failover re-send returns the first
|
|
99
|
+
* coordinator's run instead of minting a second one.
|
|
100
|
+
*/
|
|
101
|
+
requestId: string) => Promise<{
|
|
89
102
|
newRunId: string;
|
|
90
103
|
}>;
|
|
91
104
|
/**
|
|
@@ -95,6 +108,17 @@ interface DashboardHandlerDeps {
|
|
|
95
108
|
* deployments the store is wired by `bootstrapOrchestrator`.
|
|
96
109
|
*/
|
|
97
110
|
eventStore?: EventStore | null;
|
|
111
|
+
/**
|
|
112
|
+
* **Test-only.** Master switch for fault-injection knobs. When false the
|
|
113
|
+
* handler ignores every test-only knob below even if it is set.
|
|
114
|
+
*/
|
|
115
|
+
testMode?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* **Test-only.** When set (and `testMode` is true), `handleRerunRequest`
|
|
118
|
+
* sleeps this many ms before invoking `onRerun`, so an HA E2E can force a
|
|
119
|
+
* slow first coordinator and trigger a relay failover. Ignored in production.
|
|
120
|
+
*/
|
|
121
|
+
testRerunDelayMs?: number;
|
|
98
122
|
}
|
|
99
123
|
export declare class DashboardHandler {
|
|
100
124
|
private readonly db;
|
|
@@ -111,6 +135,8 @@ export declare class DashboardHandler {
|
|
|
111
135
|
private readonly coldStore;
|
|
112
136
|
private readonly eventStore;
|
|
113
137
|
private readonly retryAttestations;
|
|
138
|
+
private readonly testMode;
|
|
139
|
+
private readonly testRerunDelayMs;
|
|
114
140
|
constructor(deps: DashboardHandlerDeps);
|
|
115
141
|
/**
|
|
116
142
|
* Update the bound orgId + routingKey. Called from server.ts after resolving
|