@kici-dev/orchestrator 0.1.21 → 0.1.23
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/agent/dispatcher.d.ts +44 -0
- package/dist/agent/host-roster-reaper.d.ts +2 -1
- package/dist/agent/host-roster.d.ts +57 -3
- package/dist/agent/token-store.d.ts +29 -0
- package/dist/approvals/step-approval-bridge.d.ts +5 -0
- package/dist/cli/commands/source-manifest.d.ts +8 -0
- package/dist/cli/service/compose.d.ts +13 -0
- package/dist/cli/service/deploy-env.d.ts +31 -0
- package/dist/cli.js +687 -219
- package/dist/config.d.ts +6 -0
- package/dist/dashboard/needs-edges.d.ts +4 -3
- package/dist/db/migrations/047_needs_run_on.d.ts +4 -0
- package/dist/db/migrations/048_host_reboot_pending.d.ts +19 -0
- package/dist/db/migrations/049_held_runs_payload.d.ts +14 -0
- package/dist/db/migrations/050_sources_slug.d.ts +17 -0
- package/dist/db/migrations/051_binding_host_pattern.d.ts +18 -0
- package/dist/db/migrations/052_host_reach_metadata.d.ts +19 -0
- package/dist/db/migrations/053_agent_token_single_use.d.ts +17 -0
- package/dist/db/types.d.ts +50 -4
- package/dist/deployment/deployment-identity.d.ts +9 -0
- package/dist/entry-helpers.d.ts +7 -0
- package/dist/environments/binding-store.d.ts +12 -3
- package/dist/environments/held-runs.d.ts +31 -1
- package/dist/github-app-name-refresher/github-app-name-refresher.d.ts +77 -0
- package/dist/index.js +21 -2
- package/dist/metrics/prometheus.d.ts +2 -0
- package/dist/orchestrator-core.d.ts +13 -1
- package/dist/pipeline/decorating-secret-resolver.d.ts +32 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +132 -5
- package/dist/pipeline/install-secrets-resolver.d.ts +2 -2
- package/dist/pipeline/needs-scheduler.d.ts +22 -13
- package/dist/pipeline/processor.d.ts +2 -2
- package/dist/pipeline/test-pipeline.d.ts +37 -59
- package/dist/providers/github/manifest.d.ts +25 -0
- package/dist/reporting/log-writer.d.ts +19 -0
- package/dist/routes/admin-sources.d.ts +7 -0
- package/dist/scaler/manager.d.ts +17 -0
- package/dist/secrets/secret-resolver.d.ts +33 -5
- package/dist/server.js +21664 -19907
- package/dist/sources/source-store.d.ts +4 -0
- package/dist/sources/source-validator.d.ts +2 -0
- package/dist/stale-detector/reboot-deadline-sweep.d.ts +29 -0
- package/dist/standalone.js +25263 -24075
- package/dist/ws/agent-handler.d.ts +11 -6
- package/dist/ws/bringup-api.d.ts +76 -0
- package/dist/ws/dashboard-fleet-handler.d.ts +43 -0
- package/dist/ws/dashboard-fleet-write-handler.d.ts +60 -0
- package/dist/ws/fleet-runs-on-all.d.ts +19 -0
- package/dist/ws/platform-client.d.ts +16 -1
- package/dist/ws/test-relay-handlers.d.ts +13 -2
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +50 -50
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
* exported function is a narrative orchestrator that threads the typed
|
|
14
14
|
* results through the pipeline.
|
|
15
15
|
*/
|
|
16
|
-
import { CacheRefScope } from '@kici-dev/engine';
|
|
17
|
-
import type { LabelMatcher, LockWorkflow, LockJob, SimulatedEvent, WorkflowDecision, MaterializedJob } from '@kici-dev/engine';
|
|
16
|
+
import { ExecutionJobStatus, InitFailureCategory, CacheRefScope } from '@kici-dev/engine';
|
|
17
|
+
import type { LabelMatcher, LockWorkflow, LockJob, HostTargetSelector, SimulatedEvent, WorkflowDecision, MaterializedJob, ResolvedHostAgent, HostFacts } from '@kici-dev/engine';
|
|
18
18
|
import { type HostRosterStore } from '../agent/host-roster.js';
|
|
19
19
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
20
20
|
import type { ProviderBundle } from '../provider-registry.js';
|
|
21
|
+
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
22
|
+
import type { QueuedJobInput } from '../queue/job-queue.js';
|
|
21
23
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
22
24
|
import { type ProcessingDeps } from './processor.js';
|
|
23
25
|
/**
|
|
@@ -41,7 +43,15 @@ export declare function deriveCacheRefScope(trust: TrustResolution | undefined):
|
|
|
41
43
|
export interface WorkflowDispatchContext {
|
|
42
44
|
info: WebhookInfo;
|
|
43
45
|
deps: ProcessingDeps;
|
|
44
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Provider bundle for the matched source. Undefined for local-repo test runs
|
|
48
|
+
* (`kici run` against an inline lock file with no remote provider): in that
|
|
49
|
+
* mode there is no clone-url builder / check-status poster / clone-token
|
|
50
|
+
* provider, and `repoUrl` falls back to `''` (the agent treats a missing url
|
|
51
|
+
* as a local/`fullRepo` clone). The webhook adapter always passes a defined
|
|
52
|
+
* bundle, so its dispatch behavior is unchanged.
|
|
53
|
+
*/
|
|
54
|
+
bundle?: ProviderBundle;
|
|
45
55
|
payload: unknown;
|
|
46
56
|
repoIdentifier: string;
|
|
47
57
|
credentials: Record<string, unknown>;
|
|
@@ -88,10 +98,42 @@ export interface WorkflowDispatchContext {
|
|
|
88
98
|
* correct clone + logging.
|
|
89
99
|
*/
|
|
90
100
|
extraJobConfig?: Record<string, unknown>;
|
|
101
|
+
/**
|
|
102
|
+
* Test-run provenance. Present only for `kici run` / test-trigger dispatches.
|
|
103
|
+
* When set, `recordRunStart` stamps `is_test_run = true` and
|
|
104
|
+
* `fixture_id = testRun.fixtureId` on the `execution_runs` row. Undefined for
|
|
105
|
+
* webhook runs (the stamp block is skipped).
|
|
106
|
+
*/
|
|
107
|
+
testRun?: {
|
|
108
|
+
fixtureId: string;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Run-wide flat secrets layered onto EVERY dispatched job's `jobConfig.secrets`
|
|
112
|
+
* (env-declaring or not). Used by the test path to deliver `kici run --secret`
|
|
113
|
+
* / `--env` CLI flat secrets, which must reach a job regardless of whether it
|
|
114
|
+
* declares an `environment:`. Merged UNDER the per-job env-resolved secrets so
|
|
115
|
+
* the CLI value wins on a key collision (matching the prior B1-env -> A-CLI
|
|
116
|
+
* precedence). Undefined for webhook runs.
|
|
117
|
+
*/
|
|
118
|
+
runWideFlatSecrets?: Record<string, string>;
|
|
119
|
+
/**
|
|
120
|
+
* Runtime host narrowing from `kici run --target` (Ansible `--limit`). Applied
|
|
121
|
+
* as a post-filter over each runsOnAll job's matched roster: effective hosts =
|
|
122
|
+
* runsOnAll ∩ target. Narrow-only. Undefined for webhook runs (no narrowing).
|
|
123
|
+
*/
|
|
124
|
+
target?: HostTargetSelector;
|
|
125
|
+
/**
|
|
126
|
+
* Resolved (coerced + defaulted + validated) workflow-dispatch inputs from
|
|
127
|
+
* `kici run --input`. Carried onto every dispatched job's request so the agent
|
|
128
|
+
* exposes them as `ctx.dispatchInputs`. Undefined for webhook runs.
|
|
129
|
+
*/
|
|
130
|
+
dispatchInputs?: Record<string, unknown>;
|
|
91
131
|
}
|
|
92
132
|
export interface DispatchMatchedWorkflowResult {
|
|
93
133
|
/** Number of jobs successfully dispatched (non-rejected). */
|
|
94
134
|
dispatchedJobCount: number;
|
|
135
|
+
/** Execution job ids of every dispatched/tracked job (root, gated, synthetic). */
|
|
136
|
+
dispatchedJobIds: string[];
|
|
95
137
|
/** True when the workflow install gate paused the dispatch (held run). */
|
|
96
138
|
held?: boolean;
|
|
97
139
|
}
|
|
@@ -110,6 +152,77 @@ export interface DispatchMatchedWorkflowOptions {
|
|
|
110
152
|
*/
|
|
111
153
|
reuseRunId?: string;
|
|
112
154
|
}
|
|
155
|
+
type DispatchFn = (input: QueuedJobInput) => ReturnType<Dispatcher['dispatch']>;
|
|
156
|
+
interface DispatchSetup {
|
|
157
|
+
/** Wrapped dispatcher that injects ctx.extraJobConfig into every dispatch. */
|
|
158
|
+
dispatcher: {
|
|
159
|
+
dispatch: DispatchFn;
|
|
160
|
+
};
|
|
161
|
+
/** WebhookInfo overlaid with effective routing key + provider. */
|
|
162
|
+
info: WebhookInfo;
|
|
163
|
+
/** Composite delivery id on cross-source, otherwise info.deliveryId. */
|
|
164
|
+
effectiveDeliveryId: string;
|
|
165
|
+
workflowConcurrency: {
|
|
166
|
+
cancelInProgress?: boolean;
|
|
167
|
+
max?: number;
|
|
168
|
+
} | undefined;
|
|
169
|
+
workflowTimeoutMs: number | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* Run mode for idempotent steps (`apply` | `check` | `check-fail-on-drift`),
|
|
172
|
+
* carried on `ctx.extraJobConfig` by the test-trigger / `kici run --check`
|
|
173
|
+
* path. Persisted onto the `execution_runs` row so `computeRunStatus` can
|
|
174
|
+
* fail a `check-fail-on-drift` run that detected drift. Undefined for the
|
|
175
|
+
* default apply-mode webhook path.
|
|
176
|
+
*/
|
|
177
|
+
checkMode: string | undefined;
|
|
178
|
+
}
|
|
179
|
+
interface RejectedJob {
|
|
180
|
+
jobId: string;
|
|
181
|
+
jobName: string;
|
|
182
|
+
reason: string;
|
|
183
|
+
/** Explicit init-failure category override; inferred from reason when absent. */
|
|
184
|
+
category?: InitFailureCategory;
|
|
185
|
+
/**
|
|
186
|
+
* Terminal status to record for this job. Defaults to `failed`. A zeroed
|
|
187
|
+
* `runsOnAll` that intentionally narrowed to no hosts is recorded as `skipped`
|
|
188
|
+
* (no init-failure) so its downstreams' `when` sets govern propagation.
|
|
189
|
+
*/
|
|
190
|
+
terminalStatus?: ExecutionJobStatus;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Build the QueuedJobInput for a synthetic `__bringup__` job: the orchestrator
|
|
194
|
+
* dispatches one per declared-but-un-agented `includeUninitialized` child to an
|
|
195
|
+
* agent holding `kici:capability:ssh-transport`, which runs the agent-side
|
|
196
|
+
* `ensureInitRunner(targetAgentId)` over SSH. The init-runner then connects
|
|
197
|
+
* under `targetAgentId`, and the child's pinned-hold (already queued with that
|
|
198
|
+
* pin) drains its bootstrap steps onto it. The bring-up job clones nothing and
|
|
199
|
+
* runs no sandbox (`bringupOnly`).
|
|
200
|
+
*/
|
|
201
|
+
export declare function buildBringupJobInput(args: {
|
|
202
|
+
ctx: WorkflowDispatchContext;
|
|
203
|
+
setup: DispatchSetup;
|
|
204
|
+
targetAgentId: string;
|
|
205
|
+
}): QueuedJobInput;
|
|
206
|
+
/**
|
|
207
|
+
* Phase B orchestrator: probe caches, dispatch the build job (if needed),
|
|
208
|
+
* and surface enough state for downstream phases to skip / continue
|
|
209
|
+
* appropriately.
|
|
210
|
+
*/
|
|
211
|
+
/**
|
|
212
|
+
* Materialize each static job's matrix into dispatchable children. A job whose
|
|
213
|
+
* matrix is invalid (zero combinations / over the cap) is dropped from the
|
|
214
|
+
* dispatch list and recorded as a `matrix_expansion` matrix failure so the run's
|
|
215
|
+
* other jobs still proceed. Dynamic-matrix jobs pass through with a
|
|
216
|
+
* `pendingDynamicMatrix` marker for the eval flow.
|
|
217
|
+
*/
|
|
218
|
+
/**
|
|
219
|
+
* Resolve a `runsOnAll` lock job against the declared host roster and partition
|
|
220
|
+
* the matched hosts into the target set per the `onUnreachable` policy (R2):
|
|
221
|
+
* `ready` hosts always run; unreachable durable (`static`) hosts hold / fail /
|
|
222
|
+
* skip; stale ephemeral hosts are always skipped. Throws {@link FanoutError}
|
|
223
|
+
* when the run can't proceed (fail policy with an absent host, or zero targets).
|
|
224
|
+
*/
|
|
225
|
+
export declare function resolveHostFanoutTargets(lockJob: LockJob, deps: ProcessingDeps, target?: HostTargetSelector): Promise<ResolvedHostAgent[]>;
|
|
113
226
|
/** Exact labels + regex patterns partitioned from a lock job's selectors. */
|
|
114
227
|
interface JobRoutingSelectors {
|
|
115
228
|
runsOnLabels: string[];
|
|
@@ -160,8 +273,9 @@ export interface WavePlan {
|
|
|
160
273
|
* Compute the rolling-wave plan for a materialized job set.
|
|
161
274
|
*
|
|
162
275
|
* For each base job declaring `maxParallel` whose fan-out produced more than one
|
|
163
|
-
* child, children are ordered deterministically by `
|
|
164
|
-
*
|
|
276
|
+
* child, children are ordered deterministically by `fanoutIndex` (the
|
|
277
|
+
* agentId / variant-label rank assigned at materialization; falling back to
|
|
278
|
+
* `expandedName` for children with no index) and every child at index `>=
|
|
165
279
|
* maxParallel` is held (`wave_gated=true`). The first `maxParallel` dispatch
|
|
166
280
|
* immediately; held children release one-per-terminal via the wave-scheduler.
|
|
167
281
|
* Every child of a bounded-wave base — held or not — gets a `policy` entry so
|
|
@@ -169,6 +283,19 @@ export interface WavePlan {
|
|
|
169
283
|
* job (single child) or one without `maxParallel` contributes nothing.
|
|
170
284
|
*/
|
|
171
285
|
export declare function computeWavePlan(materializedJobs: readonly MaterializedJob[]): WavePlan;
|
|
286
|
+
export declare function materializeStaticJobsSafe(staticJobs: readonly LockJob[], deps: ProcessingDeps, target?: HostTargetSelector): Promise<{
|
|
287
|
+
materializedJobs: MaterializedJob[];
|
|
288
|
+
expansionMap: Map<string, readonly string[]>;
|
|
289
|
+
matrixFailures: RejectedJob[];
|
|
290
|
+
}>;
|
|
291
|
+
/**
|
|
292
|
+
* Build the per-host secret-resolution context for a materialized child. A
|
|
293
|
+
* `runsOnAll` host child carries its identity on `mat.agent` (preferred) or
|
|
294
|
+
* `mat.pinnedAgentId`/`mat.host`; a non-host child (matrix/static) has none, so
|
|
295
|
+
* resolution stays fleet-wide (`'**'`-only). Returns `undefined` when there are
|
|
296
|
+
* no host facts to scope by.
|
|
297
|
+
*/
|
|
298
|
+
export declare function hostCtxFromMat(mat: MaterializedJob): HostFacts | undefined;
|
|
172
299
|
export interface GeneratedJobConfig {
|
|
173
300
|
/**
|
|
174
301
|
* The generated lock job with its `name` and `needs` rewritten to expanded
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
*/
|
|
32
32
|
import type { ApproverClause, LockRegistry } from '@kici-dev/engine';
|
|
33
33
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
34
|
-
import type {
|
|
34
|
+
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
35
35
|
import type { EnvironmentStore } from '../environments/environment-store.js';
|
|
36
36
|
import { type JobDispatchContext } from '../environments/protection/pipeline.js';
|
|
37
37
|
/** Registry spec carried on the dispatch message (token already resolved). */
|
|
@@ -48,7 +48,7 @@ export interface ResolveInstallSecretsArgs {
|
|
|
48
48
|
resolvedOrgId: string;
|
|
49
49
|
trustResolution: TrustResolution | undefined;
|
|
50
50
|
environmentStore: EnvironmentStore | undefined;
|
|
51
|
-
secretResolver:
|
|
51
|
+
secretResolver: SecretResolverApi | undefined;
|
|
52
52
|
protectionContext: JobDispatchContext;
|
|
53
53
|
/**
|
|
54
54
|
* Resume path: skip the protection-rule gate (already satisfied) and resolve
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DB-backed needs-aware dispatch scheduler
|
|
2
|
+
* DB-backed needs-aware dispatch scheduler.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* needs edges (static-to-static, static-to-dyn-group, dyn-to-static, dyn-to-dyn).
|
|
4
|
+
* Gates ALL needs edges (static-to-static, static-to-dyn-group, dyn-to-static,
|
|
5
|
+
* dyn-to-dyn) with event-driven scheduling instead of concurrent dispatch.
|
|
7
6
|
*
|
|
8
7
|
* The scheduler is pure DB — no in-memory state. Every scheduling decision is
|
|
9
8
|
* a fresh DB query against execution_jobs + execution_job_needs. This means
|
|
10
9
|
* zero recovery code on orchestrator restart.
|
|
11
10
|
*
|
|
11
|
+
* Each edge carries a `run_on` status-set (the upstream terminal statuses that
|
|
12
|
+
* satisfy the edge). A downstream edge is dispatch-satisfied when the upstream's
|
|
13
|
+
* terminal status is a member of the edge's run_on set; otherwise the downstream
|
|
14
|
+
* is skipped. A downstream dispatches only when every edge is satisfied.
|
|
15
|
+
*
|
|
12
16
|
* Entry points:
|
|
13
17
|
* - insertEdgesForRun: called at run start for static-to-static edges
|
|
14
18
|
* - resolveGroupEdges: called on dynamic-eval completion
|
|
15
19
|
* - evaluateDownstreams: called from onJobStatus(terminal)
|
|
16
20
|
* - recomputeNeedsSatisfied: batch recompute after group resolution
|
|
17
|
-
* - checkSchedulerInvariant:
|
|
21
|
+
* - checkSchedulerInvariant: defensive stuck-job check
|
|
18
22
|
* - getFailurePropagationTargets: cascade for transitive skip
|
|
19
23
|
*/
|
|
20
24
|
import type { Kysely } from 'kysely';
|
|
21
25
|
import type { Database } from '../db/types.js';
|
|
26
|
+
import { ExecutionJobStatus } from '@kici-dev/engine';
|
|
22
27
|
import type { MaterializedJob } from '@kici-dev/engine';
|
|
23
28
|
/** Result of evaluating downstream jobs after an upstream completes. */
|
|
24
29
|
export interface SchedulerResult {
|
|
@@ -48,21 +53,22 @@ export declare function insertEdgesForRun(db: Kysely<Database>, runId: string, j
|
|
|
48
53
|
* edge row. Empty groups (0 members) trigger immediate needs_satisfied=true
|
|
49
54
|
* for dependents.
|
|
50
55
|
*
|
|
51
|
-
* CRITICAL: dependentStaticJobs carries per-job
|
|
56
|
+
* CRITICAL: dependentStaticJobs carries the per-job run_on status-set from the
|
|
52
57
|
* NeedsGroupEntry in the lock file. Without this, all group edges would
|
|
53
|
-
* silently default to
|
|
58
|
+
* silently default to success-only.
|
|
54
59
|
*/
|
|
55
60
|
export declare function resolveGroupEdges(db: Kysely<Database>, runId: string, groupName: string, memberJobNames: string[], dependentStaticJobs: Array<{
|
|
56
61
|
jobName: string;
|
|
57
|
-
|
|
62
|
+
runOn: ExecutionJobStatus[];
|
|
58
63
|
}>): Promise<void>;
|
|
59
64
|
/**
|
|
60
65
|
* Evaluate downstream jobs after an upstream reaches terminal state.
|
|
61
66
|
*
|
|
62
67
|
* This is the core scheduler hook. For each downstream of the completed job:
|
|
63
|
-
* 1. If
|
|
64
|
-
*
|
|
65
|
-
*
|
|
68
|
+
* 1. If the completed status is not in this edge's run_on set, mark the
|
|
69
|
+
* downstream as 'skip' immediately.
|
|
70
|
+
* 2. Otherwise, check if ALL upstreams are terminal and satisfied.
|
|
71
|
+
* 3. If all satisfied, mark needs_satisfied=true and return for dispatch.
|
|
66
72
|
*/
|
|
67
73
|
export declare function evaluateDownstreams(db: Kysely<Database>, runId: string, completedJobName: string, completedStatus: string): Promise<SchedulerResult[]>;
|
|
68
74
|
/**
|
|
@@ -80,8 +86,11 @@ export declare function recomputeNeedsSatisfied(db: Kysely<Database>, runId: str
|
|
|
80
86
|
*/
|
|
81
87
|
export declare function checkSchedulerInvariant(db: Kysely<Database>, runId: string): Promise<string[]>;
|
|
82
88
|
/**
|
|
83
|
-
* cascade: find all transitive downstreams that should be
|
|
84
|
-
*
|
|
89
|
+
* Failure-propagation cascade: find all transitive downstreams that should be
|
|
90
|
+
* skipped because a terminal upstream's status is not in their edge's run_on
|
|
91
|
+
* set. At each hop, the propagating job's actual terminal status decides which
|
|
92
|
+
* downstream edges propagate (status not in run_on → the downstream skips and
|
|
93
|
+
* propagates further).
|
|
85
94
|
*/
|
|
86
95
|
export declare function getFailurePropagationTargets(db: Kysely<Database>, runId: string, failedJobName: string): Promise<string[]>;
|
|
87
96
|
//# sourceMappingURL=needs-scheduler.d.ts.map
|
|
@@ -33,7 +33,7 @@ import type { HostRosterStore } from '../agent/host-roster.js';
|
|
|
33
33
|
import type { RunCoordinator } from '../cluster/coordinator.js';
|
|
34
34
|
import type { TeamMembershipLookup } from '../approvals/team-membership-lookup.js';
|
|
35
35
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
36
|
-
import type {
|
|
36
|
+
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
37
37
|
import type { ContributorCache } from '../security/contributor-cache.js';
|
|
38
38
|
import type { AccessLogWriter } from '../audit/access-log.js';
|
|
39
39
|
import type { LockFile as FullLockFile, LockWorkflow, SimulatedEvent, WebhookNormalizer } from '@kici-dev/engine';
|
|
@@ -281,7 +281,7 @@ export interface ProcessingDeps {
|
|
|
281
281
|
/** Run coordinator for multi-orchestrator job routing. Optional -- if not set, all jobs dispatch locally (single-orchestrator mode). */
|
|
282
282
|
coordinator?: RunCoordinator;
|
|
283
283
|
/** Secret resolver for dispatch-time secret resolution. Optional -- if not set, secrets are not resolved. */
|
|
284
|
-
secretResolver?:
|
|
284
|
+
secretResolver?: SecretResolverApi;
|
|
285
285
|
/** Optional callback when source locations are extracted from a lock file workflow. */
|
|
286
286
|
onSourceLocationsExtracted?: (workflowName: string, jobName: string, sourceLocations: Array<{
|
|
287
287
|
file: string;
|
|
@@ -1,36 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Test
|
|
2
|
+
* Test-trigger adapter for CLI-initiated runs (`kici run`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Resolves the lock file, matched decisions, and fixture-specific concerns
|
|
5
|
+
* (inline-vs-provider lock, decision selection, fixture-payload storage, the
|
|
6
|
+
* `allow_local_execution` environment gate, CLI-secret overlay, in-memory
|
|
7
|
+
* test-run marking), then dispatches each matched workflow through the SAME
|
|
8
|
+
* shared core as webhooks (`dispatchMatchedWorkflow`). The test path is a thin
|
|
9
|
+
* adapter — needs-DAG scheduling, `expansionMap` fan-out edges, `runsOnAll`
|
|
10
|
+
* host fan-out, and deferred init/dynamic dispatch all come from the core.
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
13
|
-
* - deliveryId
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
12
|
+
* Differences from a webhook run:
|
|
13
|
+
* - the synthetic event is injected directly (no provider normalization/dedup);
|
|
14
|
+
* - the lock file may be inline (local repos have no remote provider, so
|
|
15
|
+
* `bundle` is undefined);
|
|
16
|
+
* - `deliveryId` carries a `test:` prefix;
|
|
17
|
+
* - the run is stamped `is_test_run = true` + `fixture_id` (via the core's
|
|
18
|
+
* `testRun` meta) and marked in-memory for live-log broadcast to the CLI;
|
|
19
|
+
* - CLI-uploaded local secrets win over orchestrator env secrets (the
|
|
20
|
+
* decorating secret resolver), and the fixture's `secrets` context mapping
|
|
21
|
+
* resolves into namespaced secrets;
|
|
22
|
+
* - direct workflow execution (bypass trigger matching) is supported.
|
|
16
23
|
*/
|
|
17
|
-
import type
|
|
18
|
-
import type {
|
|
19
|
-
import type { CheckRunReporter } from '../reporting/check-run-reporter.js';
|
|
20
|
-
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
21
|
-
import type { AgentRegistry } from '../agent/registry.js';
|
|
22
|
-
import type { ProviderRegistry } from '../provider-registry.js';
|
|
23
|
-
import type { SourceCache } from '../cache/index.js';
|
|
24
|
-
import type { BuildCoordinator } from '../cache/index.js';
|
|
25
|
-
import type { DepCache } from '../cache/index.js';
|
|
26
|
-
import type { PendingBuildTracker } from '../cache/index.js';
|
|
27
|
-
import type { SecretResolver } from '../secrets/secret-resolver.js';
|
|
28
|
-
import type { EnvironmentStore } from '../environments/environment-store.js';
|
|
29
|
-
import type { VariableStore } from '../environments/variable-store.js';
|
|
30
|
-
import type { LogStorage } from '../reporting/log-storage.js';
|
|
31
|
-
import type { Kysely } from 'kysely';
|
|
32
|
-
import type { Database } from '../db/types.js';
|
|
33
|
-
import type { CheckMode } from '@kici-dev/engine';
|
|
24
|
+
import { type ProcessingDeps } from './processor.js';
|
|
25
|
+
import type { CheckMode, HostTargetSelector } from '@kici-dev/engine';
|
|
34
26
|
/**
|
|
35
27
|
* Input for a test trigger request.
|
|
36
28
|
*/
|
|
@@ -76,6 +68,17 @@ export interface TestTriggerInput {
|
|
|
76
68
|
* Omitted means `apply`.
|
|
77
69
|
*/
|
|
78
70
|
checkMode?: CheckMode;
|
|
71
|
+
/**
|
|
72
|
+
* Runtime host narrowing from `kici run --target`. Threaded onto the dispatch
|
|
73
|
+
* context, where it post-filters each runsOnAll job's matched roster.
|
|
74
|
+
*/
|
|
75
|
+
target?: HostTargetSelector;
|
|
76
|
+
/**
|
|
77
|
+
* Raw operator-supplied `kici run --input KEY=VALUE` pairs (not coerced /
|
|
78
|
+
* defaulted). Validated + coerced + defaulted here against the matched
|
|
79
|
+
* workflow's lock dispatch descriptor before dispatch.
|
|
80
|
+
*/
|
|
81
|
+
dispatchInputs?: Record<string, string>;
|
|
79
82
|
}
|
|
80
83
|
/**
|
|
81
84
|
* Result of processing a test trigger.
|
|
@@ -91,37 +94,12 @@ interface TestTriggerResult {
|
|
|
91
94
|
jobIds: string[];
|
|
92
95
|
}
|
|
93
96
|
/**
|
|
94
|
-
*
|
|
95
|
-
* All injected for testability.
|
|
96
|
-
*/
|
|
97
|
-
export interface TestPipelineDeps {
|
|
98
|
-
lockFileCache: LockFileCache;
|
|
99
|
-
dispatcher: Dispatcher;
|
|
100
|
-
executionTracker?: ExecutionTracker;
|
|
101
|
-
checkRunReporter?: CheckRunReporter;
|
|
102
|
-
sourceCache?: SourceCache;
|
|
103
|
-
buildCoordinator?: BuildCoordinator;
|
|
104
|
-
depCache?: DepCache;
|
|
105
|
-
pendingBuilds?: PendingBuildTracker;
|
|
106
|
-
secretResolver?: SecretResolver;
|
|
107
|
-
agentRegistry: AgentRegistry;
|
|
108
|
-
providerRegistry: ProviderRegistry;
|
|
109
|
-
/** Log storage for persisting test fixture payloads. Optional -- if not set, payload storage is skipped. */
|
|
110
|
-
logStorage?: LogStorage;
|
|
111
|
-
/** Database connection for environment protection checks. Optional. */
|
|
112
|
-
db?: Kysely<Database>;
|
|
113
|
-
/** Environment store for resolving environment ids in test dispatch parity. Optional. */
|
|
114
|
-
environmentStore?: EnvironmentStore;
|
|
115
|
-
/** Variable store for resolving environment variables in test dispatch parity. Optional. */
|
|
116
|
-
variableStore?: VariableStore;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Process a test trigger through the existing pipeline.
|
|
97
|
+
* Process a test trigger through the shared dispatch core.
|
|
120
98
|
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
99
|
+
* Resolves the lock file + matched decisions + fixture concerns, then builds a
|
|
100
|
+
* `WorkflowDispatchContext` per matched workflow and calls
|
|
101
|
+
* `dispatchMatchedWorkflow` — the same core the webhook path uses.
|
|
124
102
|
*/
|
|
125
|
-
export declare function processTestTrigger(input: TestTriggerInput, deps:
|
|
103
|
+
export declare function processTestTrigger(input: TestTriggerInput, deps: ProcessingDeps): Promise<TestTriggerResult>;
|
|
126
104
|
export {};
|
|
127
105
|
//# sourceMappingURL=test-pipeline.d.ts.map
|
|
@@ -33,11 +33,21 @@ export interface GithubAppManifest {
|
|
|
33
33
|
default_permissions: Record<string, string>;
|
|
34
34
|
default_events: string[];
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate a self-hosted webhook URL supplied via `source add github
|
|
38
|
+
* --webhook-url`. Must be a well-formed absolute `https://` URL. Returns the URL
|
|
39
|
+
* verbatim on success; throws a clear error otherwise. The validated URL is
|
|
40
|
+
* baked into `manifest.hook_attributes.url` as-is — KiCI adds no ingress and
|
|
41
|
+
* does not receive events at it; the operator owns delivery.
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateWebhookUrl(value: string): string;
|
|
36
44
|
export declare function buildGithubAppManifest(input: GithubManifestInput): GithubAppManifest;
|
|
37
45
|
/** Credentials returned by GitHub's manifest-conversion endpoint. */
|
|
38
46
|
export interface GithubAppCredentials {
|
|
39
47
|
appId: string;
|
|
40
48
|
slug: string;
|
|
49
|
+
/** GitHub's display name for the App (the authoritative stored name). */
|
|
50
|
+
name: string;
|
|
41
51
|
privateKey: string;
|
|
42
52
|
webhookSecret: string;
|
|
43
53
|
clientId?: string;
|
|
@@ -52,6 +62,21 @@ export interface GithubAppCredentials {
|
|
|
52
62
|
export declare function convertManifestCode(code: string, deps?: {
|
|
53
63
|
octokit?: Pick<Octokit, 'request'>;
|
|
54
64
|
}): Promise<GithubAppCredentials>;
|
|
65
|
+
/**
|
|
66
|
+
* Ask GitHub who this App is (`GET /app`, authenticated as the App via its
|
|
67
|
+
* JWT) and return its authoritative display `name` + `slug`. This is the single
|
|
68
|
+
* "fetch the App's identity from GitHub" helper, reused at source creation, by
|
|
69
|
+
* the daily refresher, and by `kici-admin source refresh`.
|
|
70
|
+
*
|
|
71
|
+
* GitHub is the source of truth: a rename in the GitHub UI changes the value
|
|
72
|
+
* `GET /app` returns, which is what keeps the dashboard name fresh.
|
|
73
|
+
*/
|
|
74
|
+
export declare function fetchGithubAppIdentity(creds: Pick<GithubAppCredentials, 'appId' | 'privateKey'>, deps?: {
|
|
75
|
+
appOctokit?: Pick<Octokit, 'request'>;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
name: string;
|
|
78
|
+
slug: string;
|
|
79
|
+
}>;
|
|
55
80
|
/**
|
|
56
81
|
* Poll GitHub (as the App, via a JWT) until at least one installation exists,
|
|
57
82
|
* returning the first installation's id + account login. Throws on timeout.
|
|
@@ -21,7 +21,26 @@ export declare class LogWriter {
|
|
|
21
21
|
private readonly logStorage;
|
|
22
22
|
private readonly observerRegistry?;
|
|
23
23
|
private readonly isTestRun?;
|
|
24
|
+
/**
|
|
25
|
+
* In-flight `logStorage.append` promises, keyed by runId. `appendChunk` is
|
|
26
|
+
* called fire-and-forget from the agent WS handler (it does not await the
|
|
27
|
+
* storage write), so a run can flip to a terminal status while the final
|
|
28
|
+
* log chunk's append is still pending. `drain(runId)` lets a reader (the
|
|
29
|
+
* test-run logs cursor endpoint) wait for those pending writes before it
|
|
30
|
+
* reports the stream as fully drained — without this, the `done` flag can
|
|
31
|
+
* be returned `true` while the last user-visible log line is not yet on
|
|
32
|
+
* disk, dropping it from a blocking `kici run remote` follow.
|
|
33
|
+
*/
|
|
34
|
+
private readonly pendingAppends;
|
|
24
35
|
constructor(deps: LogWriterDeps);
|
|
36
|
+
/**
|
|
37
|
+
* Await every in-flight log append for `runId` that was registered before
|
|
38
|
+
* this call. Settles even if an append rejected (errors are already logged
|
|
39
|
+
* by `appendChunk`); the point is ordering, not error propagation. New
|
|
40
|
+
* appends started after this call are not waited on — a terminal run emits
|
|
41
|
+
* no further chunks, so the snapshot taken at call time is complete.
|
|
42
|
+
*/
|
|
43
|
+
drain(runId: string): Promise<void>;
|
|
25
44
|
/**
|
|
26
45
|
* Append log lines from an agent to storage in JSONL format.
|
|
27
46
|
*
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { Hono } from 'hono';
|
|
12
12
|
import type { SourceStore } from '../sources/source-store.js';
|
|
13
13
|
import type { Role } from '../secrets/rbac.js';
|
|
14
|
+
import { type FetchGithubAppIdentity } from '../github-app-name-refresher/github-app-name-refresher.js';
|
|
14
15
|
interface SourceRouteDeps {
|
|
15
16
|
sourceStore: SourceStore;
|
|
16
17
|
/**
|
|
@@ -39,6 +40,12 @@ interface SourceRouteDeps {
|
|
|
39
40
|
webhookUrl: string | null;
|
|
40
41
|
webhookNote?: string;
|
|
41
42
|
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Fetch a GitHub App's authoritative `{ name, slug }` from GitHub. Injectable
|
|
45
|
+
* for tests; defaults to the real `fetchGithubAppIdentity`. Used by the
|
|
46
|
+
* `source refresh` route.
|
|
47
|
+
*/
|
|
48
|
+
fetchAppIdentity?: FetchGithubAppIdentity;
|
|
42
49
|
}
|
|
43
50
|
type AdminSourcesEnv = {
|
|
44
51
|
Variables: {
|
package/dist/scaler/manager.d.ts
CHANGED
|
@@ -39,6 +39,23 @@ export interface ResolvedResources {
|
|
|
39
39
|
memBytes: number;
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the scaler-usage metric rows: one per active scaler (stamped with its
|
|
44
|
+
* backend type) plus a `__global__` rollup row. Pure so it is unit-testable
|
|
45
|
+
* without constructing a full ScalerManager.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildScalerUsageRows(perScalerUsage: ReadonlyMap<string, {
|
|
48
|
+
cpus: number;
|
|
49
|
+
memBytes: number;
|
|
50
|
+
}>, globalUsage: {
|
|
51
|
+
cpus: number;
|
|
52
|
+
memBytes: number;
|
|
53
|
+
}, scalerTypeOf: (name: string) => string | undefined): Array<{
|
|
54
|
+
scaler: string;
|
|
55
|
+
scalerType?: string;
|
|
56
|
+
cpus: number;
|
|
57
|
+
memBytes: number;
|
|
58
|
+
}>;
|
|
42
59
|
/**
|
|
43
60
|
* Status summary for metrics and health endpoints.
|
|
44
61
|
*/
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* longest-path-wins uses scope path after stripping backend prefix.
|
|
16
16
|
* audit log includes backend name.
|
|
17
17
|
*/
|
|
18
|
-
import { type EnvironmentBinding, type ScopedSecret } from '@kici-dev/engine';
|
|
18
|
+
import { type EnvironmentBinding, type HostFacts, type ScopedSecret } from '@kici-dev/engine';
|
|
19
19
|
import type { Logger } from '@kici-dev/shared';
|
|
20
20
|
import type { AuditLogger } from './audit-logger.js';
|
|
21
21
|
/**
|
|
@@ -62,6 +62,21 @@ export interface ResolvedSecretMeta {
|
|
|
62
62
|
backend: string;
|
|
63
63
|
scope: string;
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The public secret-resolution surface consumed by the dispatch path and the
|
|
67
|
+
* universal-git provider. `SecretResolver` implements this; the test adapter's
|
|
68
|
+
* `DecoratingSecretResolver` (CLI-secret overlay) implements it too, so either
|
|
69
|
+
* can flow through `ProcessingDeps.secretResolver`.
|
|
70
|
+
*/
|
|
71
|
+
export interface SecretResolverApi {
|
|
72
|
+
resolveForJob(orgId: string, environmentName: string, hostCtx?: HostFacts): Promise<Record<string, string>>;
|
|
73
|
+
resolveNamed(orgId: string, scope: string, key: string, opts?: {
|
|
74
|
+
store?: string;
|
|
75
|
+
runId?: string;
|
|
76
|
+
jobId?: string;
|
|
77
|
+
}): Promise<string | null>;
|
|
78
|
+
resolveForJobWithMeta(orgId: string, environmentName: string, hostCtx?: HostFacts): Promise<Record<string, ResolvedSecretMeta>>;
|
|
79
|
+
}
|
|
65
80
|
/**
|
|
66
81
|
* Resolves secrets for a job by matching environment bindings against scoped secrets
|
|
67
82
|
* from multiple backends.
|
|
@@ -71,7 +86,7 @@ export interface ResolvedSecretMeta {
|
|
|
71
86
|
* are matched against binding patterns, and longest-path-wins uses the path AFTER
|
|
72
87
|
* stripping the backend prefix.
|
|
73
88
|
*/
|
|
74
|
-
export declare class SecretResolver {
|
|
89
|
+
export declare class SecretResolver implements SecretResolverApi {
|
|
75
90
|
private readonly environmentStore;
|
|
76
91
|
private readonly bindingStore;
|
|
77
92
|
private readonly backendStores;
|
|
@@ -83,9 +98,13 @@ export declare class SecretResolver {
|
|
|
83
98
|
*
|
|
84
99
|
* @param orgId - Organization ID
|
|
85
100
|
* @param environmentName - Environment name to resolve secrets for
|
|
101
|
+
* @param hostCtx - Optional fan-out child identity for per-host resolution.
|
|
102
|
+
* When supplied, each binding is gated by its `host_pattern` and its
|
|
103
|
+
* `scope_pattern` is templated per-child; when omitted, only fleet-wide
|
|
104
|
+
* (`'**'`) non-templated bindings contribute.
|
|
86
105
|
* @returns Flat map of decrypted secret key-value pairs
|
|
87
106
|
*/
|
|
88
|
-
resolveForJob(orgId: string, environmentName: string): Promise<Record<string, string>>;
|
|
107
|
+
resolveForJob(orgId: string, environmentName: string, hostCtx?: HostFacts): Promise<Record<string, string>>;
|
|
89
108
|
/**
|
|
90
109
|
* Resolve a single named secret by (orgId, scope, key), optionally scoped to
|
|
91
110
|
* a specific backend. Bypasses environment bindings — this is a direct
|
|
@@ -111,7 +130,7 @@ export declare class SecretResolver {
|
|
|
111
130
|
*
|
|
112
131
|
* Returns the secret value along with which backend and scope provided it.
|
|
113
132
|
*/
|
|
114
|
-
resolveForJobWithMeta(orgId: string, environmentName: string): Promise<Record<string, ResolvedSecretMeta>>;
|
|
133
|
+
resolveForJobWithMeta(orgId: string, environmentName: string, hostCtx?: HostFacts): Promise<Record<string, ResolvedSecretMeta>>;
|
|
115
134
|
/**
|
|
116
135
|
* Collect all secrets from all backend stores, prefixing scopes.
|
|
117
136
|
* Per /: unreachable backends are tracked but not fatal here.
|
|
@@ -130,8 +149,17 @@ export declare class SecretResolver {
|
|
|
130
149
|
*/
|
|
131
150
|
private checkScopedFailure;
|
|
132
151
|
/**
|
|
133
|
-
* Find the winning secret for a given key
|
|
152
|
+
* Find the winning secret for a given key, mirroring the engine scope
|
|
153
|
+
* resolver's host-aware matching and `(host specificity, scope depth)`
|
|
154
|
+
* precedence so the enriched metadata reports the same secret the flat
|
|
155
|
+
* resolution selected.
|
|
134
156
|
*/
|
|
135
157
|
private findWinningSecret;
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the effective scope pattern a binding contributes for a host,
|
|
160
|
+
* applying the host gate and per-child scope templating. Mirrors the engine
|
|
161
|
+
* scope resolver's `bindingScopeForHost`. Returns `null` to skip the binding.
|
|
162
|
+
*/
|
|
163
|
+
private bindingScopeForHost;
|
|
136
164
|
}
|
|
137
165
|
//# sourceMappingURL=secret-resolver.d.ts.map
|