@kici-dev/orchestrator 0.1.23 → 0.1.24
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/token-store.d.ts +4 -0
- package/dist/app.d.ts +3 -0
- package/dist/cache/pending-inits.d.ts +3 -2
- package/dist/cli/api-client.d.ts +8 -0
- package/dist/cli/commands/attestations-reverify.d.ts +18 -0
- package/dist/cli/commands/attestations.d.ts +3 -0
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +52 -0
- package/dist/cli.js +2023 -343
- package/dist/config.d.ts +2 -0
- package/dist/dashboard/attestation-filters.d.ts +70 -0
- package/dist/dashboard/handler.d.ts +33 -1
- package/dist/db/migrations/054_local_working_tree.d.ts +15 -0
- package/dist/db/migrations/055_agent_token_mandatory_labels.d.ts +18 -0
- package/dist/db/migrations/056_execution_jobs_environments.d.ts +17 -0
- package/dist/db/migrations/057_step_concurrency.d.ts +4 -0
- package/dist/db/migrations/058_access_log_agent_label.d.ts +4 -0
- package/dist/db/migrations/059_attestation_verdict.d.ts +4 -0
- package/dist/db/migrations/060_run_trigger_actor.d.ts +4 -0
- package/dist/db/types.d.ts +55 -0
- package/dist/environments/protection/aggregate.d.ts +43 -0
- package/dist/environments/protection/satisfiability.d.ts +64 -0
- package/dist/index.js +9 -0
- package/dist/orchestrator-core.d.ts +8 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +2 -0
- package/dist/pipeline/flatten-lock-steps.d.ts +11 -0
- package/dist/pipeline/inline-eval.d.ts +2 -1
- package/dist/pipeline/job-environments.d.ts +71 -0
- package/dist/pipeline/manual-schedule.d.ts +22 -0
- package/dist/pipeline/test-pipeline.d.ts +9 -0
- package/dist/provenance/trust-root.d.ts +24 -0
- package/dist/provenance/verify-at-ingest.d.ts +24 -0
- package/dist/reporting/agent-failure-category.d.ts +27 -0
- package/dist/reporting/agent-run-result-mapper.d.ts +13 -0
- package/dist/reporting/execution-tracker.d.ts +22 -2
- package/dist/reporting/run-aggregator.d.ts +161 -0
- package/dist/reporting/step-log-reader.d.ts +39 -0
- package/dist/routes/admin-registrations.d.ts +8 -0
- package/dist/routes/admin-runs.d.ts +7 -0
- package/dist/secrets/index.d.ts +1 -1
- package/dist/server.js +2798 -1148
- package/dist/standalone.js +2627 -1221
- package/dist/storage/loopback-guard.d.ts +49 -0
- package/dist/ws/agent-handler.d.ts +15 -1
- package/dist/ws/platform-client.d.ts +36 -1
- package/installer-image-digests.json +3 -3
- package/package.json +6 -8
- package/sbom.spdx.json +57 -57
- package/dist/secrets/crypto.d.ts +0 -49
package/dist/config.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
27
27
|
platformUrl: z.ZodOptional<z.ZodString>;
|
|
28
28
|
platformToken: z.ZodOptional<z.ZodString>;
|
|
29
29
|
dashboardUrl: z.ZodOptional<z.ZodString>;
|
|
30
|
+
provenanceIssuer: z.ZodOptional<z.ZodString>;
|
|
30
31
|
webhookPublicUrl: z.ZodOptional<z.ZodString>;
|
|
31
32
|
databaseUrl: z.ZodDefault<z.ZodString>;
|
|
32
33
|
lockfileCacheMax: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
@@ -248,6 +249,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
248
249
|
platformUrl?: string | undefined;
|
|
249
250
|
platformToken?: string | undefined;
|
|
250
251
|
dashboardUrl?: string | undefined;
|
|
252
|
+
provenanceIssuer?: string | undefined;
|
|
251
253
|
webhookPublicUrl?: string | undefined;
|
|
252
254
|
cacheStorageType?: "s3" | "filesystem" | undefined;
|
|
253
255
|
cacheStoragePath?: string | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
import type { AttestationListFilters } from '@kici-dev/engine';
|
|
3
|
+
import type { Database } from '../db/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* The org-wide attestations base query: joins `attestations` to `execution_jobs`
|
|
6
|
+
* (for the job name) and `execution_runs` (for repository / workflow context).
|
|
7
|
+
*
|
|
8
|
+
* `attestations.run_id` / `job_id` are TEXT (P1.5 schema) while the
|
|
9
|
+
* `execution_*` keys are `uuid`; Postgres won't compare `uuid = text` implicitly,
|
|
10
|
+
* so the join casts the uuid side to text — mirroring `resolveAttestationsForRun`.
|
|
11
|
+
* The `execution_runs` join is a LEFT join so a row with no matching run still
|
|
12
|
+
* lists (repository / workflow come back null).
|
|
13
|
+
*/
|
|
14
|
+
export declare function baseAttestationsQuery(db: Kysely<Database>): import("kysely").SelectQueryBuilder<{
|
|
15
|
+
environments: import("../db/types.js").EnvironmentsTable;
|
|
16
|
+
host_roster: import("../db/types.js").HostRosterTable;
|
|
17
|
+
dispatch_queue: import("../db/types.js").DispatchQueueTable;
|
|
18
|
+
dedup_cache: import("../db/types.js").DedupCacheTable;
|
|
19
|
+
ip_allocations: import("../db/types.js").IpAllocationTable;
|
|
20
|
+
execution_runs: import("kysely").Nullable<import("../db/types.js").ExecutionRunTable>;
|
|
21
|
+
execution_jobs: import("../db/types.js").ExecutionJobTable;
|
|
22
|
+
execution_steps: import("../db/types.js").ExecutionStepTable;
|
|
23
|
+
raft_state: import("../db/types.js").RaftStateTable;
|
|
24
|
+
secret_audit_log: import("../db/types.js").SecretAuditLogTable;
|
|
25
|
+
scoped_secrets: import("../db/types.js").ScopedSecretsTable;
|
|
26
|
+
environment_bindings: import("../db/types.js").EnvironmentBindingsTable;
|
|
27
|
+
environment_variables: import("../db/types.js").EnvironmentVariablesTable;
|
|
28
|
+
environment_source_overrides: import("../db/types.js").EnvironmentSourceOverridesTable;
|
|
29
|
+
held_runs: import("../db/types.js").HeldRunsTable;
|
|
30
|
+
held_run_approvals: import("../db/types.js").HeldRunApprovalsTable;
|
|
31
|
+
admin_tokens: import("../db/types.js").AdminTokenTable;
|
|
32
|
+
agent_tokens: import("../db/types.js").AgentTokenTable;
|
|
33
|
+
config_versions: import("../db/types.js").ConfigVersionTable;
|
|
34
|
+
kici_events: import("../db/types.js").KiciEventTable;
|
|
35
|
+
generic_webhook_sources: import("../db/types.js").GenericWebhookSourceTable;
|
|
36
|
+
cross_repo_trust: import("../db/types.js").CrossRepoTrustTable;
|
|
37
|
+
test_uploads: import("../db/types.js").TestUploadsTable;
|
|
38
|
+
workflow_registrations: import("../db/types.js").WorkflowRegistrationsTable;
|
|
39
|
+
registry_versions: import("../db/types.js").RegistryVersionsTable;
|
|
40
|
+
cron_last_fired: import("../db/types.js").CronLastFiredTable;
|
|
41
|
+
run_ephemeral_keys: import("../db/types.js").RunEphemeralKeysTable;
|
|
42
|
+
run_secret_outputs: import("../db/types.js").RunSecretOutputsTable;
|
|
43
|
+
concurrency_groups: import("../db/types.js").ConcurrencyGroupsTable;
|
|
44
|
+
sources: import("../db/types.js").SourcesTable;
|
|
45
|
+
cluster_meta: import("../db/types.js").ClusterMetaTable;
|
|
46
|
+
join_tokens: import("../db/types.js").JoinTokenTable;
|
|
47
|
+
org_settings: import("../db/types.js").OrgSettingsTable;
|
|
48
|
+
execution_job_needs: import("../db/types.js").ExecutionJobNeedsTable;
|
|
49
|
+
pending_job_contexts: import("../db/types.js").PendingJobContextsTable;
|
|
50
|
+
pending_workflow_contexts: import("../db/types.js").PendingWorkflowContextsTable;
|
|
51
|
+
event_log: import("../db/types.js").EventLogTable;
|
|
52
|
+
access_log: import("../db/types.js").AccessLogTable;
|
|
53
|
+
cold_store_chunk_counts: import("../db/types.js").ColdStoreChunkCountsTable;
|
|
54
|
+
cold_store_chunks: import("../db/types.js").ColdStoreChunksTable;
|
|
55
|
+
check_run_tracking: import("../db/types.js").CheckRunTrackingTable;
|
|
56
|
+
scaler_spawning_agents: import("../db/types.js").ScalerSpawningAgentsTable;
|
|
57
|
+
scaler_agent_jobs: import("../db/types.js").ScalerAgentJobsTable;
|
|
58
|
+
scaler_reservations: import("../db/types.js").ScalerReservationsTable;
|
|
59
|
+
attestations: import("../db/types.js").AttestationsTable;
|
|
60
|
+
remote_sources: import("../db/types.js").RemoteSourcesTable;
|
|
61
|
+
}, "execution_runs" | "execution_jobs" | "attestations", {}>;
|
|
62
|
+
export type AttestationsBaseQuery = ReturnType<typeof baseAttestationsQuery>;
|
|
63
|
+
/**
|
|
64
|
+
* Apply org-wide attestation filters to the base query. Digest is exact-match;
|
|
65
|
+
* name is an ILIKE substring; status / repository / workflow / job are equality;
|
|
66
|
+
* created_at gets `>=` / `<=` bounds for the date range. Absent filters are
|
|
67
|
+
* skipped.
|
|
68
|
+
*/
|
|
69
|
+
export declare function applyAttestationFilters(qb: AttestationsBaseQuery, filters: AttestationListFilters): AttestationsBaseQuery;
|
|
70
|
+
//# sourceMappingURL=attestation-filters.d.ts.map
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { type Kysely } from 'kysely';
|
|
15
15
|
import { type ColdStore } from '@kici-dev/shared';
|
|
16
|
-
import type { DashboardRunDetailRequest, DashboardRunsListRequest, DashboardRunsListResponse, DashboardRunsFiltersRequest, DashboardRunsFiltersResponse, DashboardSourcesListRequest, DashboardSourcesListResponse, DashboardStepLogsRequest, DashboardAttestationsListRequest, DashboardPayloadRequest, DashboardOrchLogsRequest, DashboardEventLogListRequest, DashboardEventLogDetailRequest, DashboardEventLogPayloadStreamRequest, DashboardAccessLogListRequest, DashboardEventDlqListRequest, DashboardEventDlqCountRequest, DashboardEventDlqRetryRequest, DashboardEventDlqDiscardRequest, RunRerunRequest, RunCancelRequest, ManualScheduleRequest } from '@kici-dev/engine';
|
|
16
|
+
import type { DashboardRunDetailRequest, DashboardRunsListRequest, DashboardRunsListResponse, DashboardRunsFiltersRequest, DashboardRunsFiltersResponse, DashboardSourcesListRequest, DashboardSourcesListResponse, DashboardStepLogsRequest, DashboardAttestationsListRequest, DashboardAttestationsListAllRequest, DashboardAttestationGetRequest, DashboardPayloadRequest, DashboardOrchLogsRequest, DashboardEventLogListRequest, DashboardEventLogDetailRequest, DashboardEventLogPayloadStreamRequest, DashboardAccessLogListRequest, DashboardEventDlqListRequest, DashboardEventDlqCountRequest, DashboardEventDlqRetryRequest, DashboardEventDlqDiscardRequest, RunRerunRequest, RunCancelRequest, ManualScheduleRequest, DashboardRunStructuredRequest } from '@kici-dev/engine';
|
|
17
17
|
import type { Database } from '../db/types.js';
|
|
18
18
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
19
19
|
import type { CacheStorage } from '../storage/types.js';
|
|
@@ -166,6 +166,14 @@ export declare class DashboardHandler {
|
|
|
166
166
|
* builds a nested job/step tree, and sends the response.
|
|
167
167
|
*/
|
|
168
168
|
handleRunDetail(msg: DashboardRunDetailRequest): Promise<void>;
|
|
169
|
+
/**
|
|
170
|
+
* Handle a dashboard.run.structured request — the user-plane equivalent of
|
|
171
|
+
* the orchestrator-admin `/runs/:id/structured` endpoint. Reuses the Phase-1
|
|
172
|
+
* aggregator + provenance mapper so the result is byte-identical to the admin
|
|
173
|
+
* surface; emits an `access_log` `run.structured.read` row (carrying the
|
|
174
|
+
* agent label when the actor came through an agent PAT).
|
|
175
|
+
*/
|
|
176
|
+
handleRunStructured(msg: DashboardRunStructuredRequest): Promise<void>;
|
|
169
177
|
/**
|
|
170
178
|
* Resolve every routing key owned by an org by unioning both source
|
|
171
179
|
* tables. The orchestrator is single-org but multi-routing-key: one org
|
|
@@ -275,6 +283,30 @@ export declare class DashboardHandler {
|
|
|
275
283
|
* skipped (best-effort) rather than failing the whole list.
|
|
276
284
|
*/
|
|
277
285
|
handleAttestationsList(msg: DashboardAttestationsListRequest): Promise<void>;
|
|
286
|
+
/**
|
|
287
|
+
* Fetch + parse one attestation bundle from object storage. Returns the full
|
|
288
|
+
* detail item, or null when the bundle is missing or not valid JSON (logged).
|
|
289
|
+
* Shared by `handleAttestationsList` (per-run) and `handleAttestationGet`.
|
|
290
|
+
*/
|
|
291
|
+
private inlineBundle;
|
|
292
|
+
/**
|
|
293
|
+
* Org-wide attestations query: one page of metadata-only summaries (no bundle
|
|
294
|
+
* fetch) plus the total matching count. Filters/pagination/sort applied via
|
|
295
|
+
* the shared filter builder.
|
|
296
|
+
*/
|
|
297
|
+
private resolveAttestationsListAll;
|
|
298
|
+
/**
|
|
299
|
+
* Handle a dashboard.attestations.list.all request: org-wide, paginated,
|
|
300
|
+
* filtered list of attestation summaries (metadata only). Access-logged
|
|
301
|
+
* against the `attestation` target.
|
|
302
|
+
*/
|
|
303
|
+
handleAttestationsListAll(msg: DashboardAttestationsListAllRequest): Promise<void>;
|
|
304
|
+
/**
|
|
305
|
+
* Handle a dashboard.attestation.get request: a single attestation by id with
|
|
306
|
+
* its bundle inlined for the detail page. Resolves the run-owning org for the
|
|
307
|
+
* access-log row, then logs against the `attestation` target.
|
|
308
|
+
*/
|
|
309
|
+
handleAttestationGet(msg: DashboardAttestationGetRequest): Promise<void>;
|
|
278
310
|
/**
|
|
279
311
|
* Handle a dashboard.payload request.
|
|
280
312
|
* Reads the webhook payload from log storage for the given runId.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Mark runs that executed an uploaded local working tree (`kici run remote`):
|
|
4
|
+
*
|
|
5
|
+
* - `execution_runs.local_working_tree boolean NOT NULL DEFAULT false` — true
|
|
6
|
+
* for runs that ran a developer's local working tree from the CLI (inline
|
|
7
|
+
* lock file). The dashboard renders a "Local machine" badge for these runs
|
|
8
|
+
* and avoids building an external repository link from the repo identifier.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive with a default, so staging
|
|
11
|
+
* data is preserved.
|
|
12
|
+
*/
|
|
13
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
14
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=054_local_working_tree.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Add a token-bound mandatory-label taint to agent tokens:
|
|
4
|
+
*
|
|
5
|
+
* - `agent_tokens.mandatory_labels text NULL` — a JSON-encoded `string[]` of
|
|
6
|
+
* Kubernetes-taint-style gate labels the token authorizes. When a static
|
|
7
|
+
* agent registers with this token, the set becomes the agent's registry-entry
|
|
8
|
+
* `mandatoryLabels`: the agent only accepts a job when every label here
|
|
9
|
+
* appears in the job's required labels. NULL = no taint (the default; the
|
|
10
|
+
* agent accepts any job its advertised labels match), so every existing token
|
|
11
|
+
* stays unconfined until re-minted.
|
|
12
|
+
*
|
|
13
|
+
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
14
|
+
* preserved.
|
|
15
|
+
*/
|
|
16
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
17
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=055_agent_token_mandatory_labels.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Add a per-job bound deployment-environment list to `execution_jobs`:
|
|
4
|
+
*
|
|
5
|
+
* - `execution_jobs.environments text NULL` — a JSON-encoded `string[]` of the
|
|
6
|
+
* ordered environment names a job binds (`environments: [...]`), in merge
|
|
7
|
+
* order. Written at dispatch with the statically-resolved names (impure
|
|
8
|
+
* dynamic elements as a `(dynamic)` placeholder), then overwritten with the
|
|
9
|
+
* fully-resolved list when a deferred-init agent eval resolves dynamic
|
|
10
|
+
* elements. NULL = the job binds no environment.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
13
|
+
* preserved.
|
|
14
|
+
*/
|
|
15
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
16
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=056_execution_jobs_environments.d.ts.map
|
package/dist/db/types.d.ts
CHANGED
|
@@ -271,6 +271,8 @@ export interface ExecutionRunTable {
|
|
|
271
271
|
provider_context: Generated<string>;
|
|
272
272
|
/** Whether this is a CLI-initiated test run */
|
|
273
273
|
is_test_run: Generated<boolean>;
|
|
274
|
+
/** True when the run executed an uploaded local working tree (`kici run remote`). */
|
|
275
|
+
local_working_tree: Generated<boolean>;
|
|
274
276
|
/** Fixture ID for test runs (null for real webhook runs) */
|
|
275
277
|
fixture_id: string | null;
|
|
276
278
|
/** Parent run ID for re-run lineage (null for original runs). */
|
|
@@ -289,6 +291,22 @@ export interface ExecutionRunTable {
|
|
|
289
291
|
lock_file_source: string | null;
|
|
290
292
|
/** Username of the contributor (null for non-PR events) */
|
|
291
293
|
contributor_username: string | null;
|
|
294
|
+
/**
|
|
295
|
+
* Origin provider of the triggering actor (`github` today). Provider-generic
|
|
296
|
+
* so GitLab/Bitbucket extend later. Null when no actor was captured.
|
|
297
|
+
*/
|
|
298
|
+
trigger_actor_provider: string | null;
|
|
299
|
+
/**
|
|
300
|
+
* Provider login of the person who triggered the run (pusher / PR author).
|
|
301
|
+
* Captured for ALL event types, unlike the PR-only `contributor_username`.
|
|
302
|
+
*/
|
|
303
|
+
trigger_actor_username: string | null;
|
|
304
|
+
/**
|
|
305
|
+
* Immutable provider user id of the triggering actor (mirrors
|
|
306
|
+
* `identity_links.provider_user_id`). Preferred over the mutable username
|
|
307
|
+
* when resolving the actor to a KiCI user.
|
|
308
|
+
*/
|
|
309
|
+
trigger_actor_user_id: string | null;
|
|
292
310
|
/** Human-readable reason why the run failed (null for non-failed runs). */
|
|
293
311
|
failure_reason: string | null;
|
|
294
312
|
/**
|
|
@@ -371,6 +389,12 @@ export interface ExecutionJobTable {
|
|
|
371
389
|
dispatched_contexts: Generated<string>;
|
|
372
390
|
/** Aggregated step outputs JSONB (step-keyed map of outputs). Populated on job success. */
|
|
373
391
|
outputs: string | null;
|
|
392
|
+
/**
|
|
393
|
+
* Ordered bound deployment-environment names for this job, JSON-encoded
|
|
394
|
+
* `string[]` (null when the job binds none). Written at dispatch and
|
|
395
|
+
* overwritten with the agent-resolved list for dynamic environments.
|
|
396
|
+
*/
|
|
397
|
+
environments: string | null;
|
|
374
398
|
/** Whether all upstream needs edges are satisfied (dispatch gate). */
|
|
375
399
|
needs_satisfied: Generated<boolean>;
|
|
376
400
|
/** Timestamp when needs_satisfied first flipped to true. */
|
|
@@ -453,6 +477,13 @@ export interface ExecutionStepTable {
|
|
|
453
477
|
drift_summary: string | null;
|
|
454
478
|
/** Structured drift value returned by `check()` (JSONB). NULL when no drift. */
|
|
455
479
|
drift: ColumnType<unknown | null, unknown, unknown>;
|
|
480
|
+
/**
|
|
481
|
+
* Parallel step-group concurrency role (`sequential` | `parallel-child` |
|
|
482
|
+
* `parallel-group`). NULL for an ordinary sequential step.
|
|
483
|
+
*/
|
|
484
|
+
concurrency_kind: string | null;
|
|
485
|
+
/** Parallel-group correlation id shared by a group's children. NULL for sequential steps. */
|
|
486
|
+
group_id: string | null;
|
|
456
487
|
/** When this record was created */
|
|
457
488
|
created_at: Generated<Date>;
|
|
458
489
|
/**
|
|
@@ -804,6 +835,12 @@ export interface AccessLogTable {
|
|
|
804
835
|
source: string;
|
|
805
836
|
outcome: string;
|
|
806
837
|
error_message: string | null;
|
|
838
|
+
/**
|
|
839
|
+
* Human-set agent name, when the actor authenticated with an agent-kind PAT.
|
|
840
|
+
* NULL for ordinary human / API-key / system actors. Queryable so the access
|
|
841
|
+
* log can be filtered by agent.
|
|
842
|
+
*/
|
|
843
|
+
agent_label: string | null;
|
|
807
844
|
created_at: Generated<Date>;
|
|
808
845
|
/**
|
|
809
846
|
* Set inside the archive transaction before the row is DELETEd.
|
|
@@ -857,6 +894,14 @@ export interface AgentTokenTable {
|
|
|
857
894
|
token_prefix: string;
|
|
858
895
|
/** JSON-encoded string[] of agent labels (null = any) */
|
|
859
896
|
labels: string | null;
|
|
897
|
+
/**
|
|
898
|
+
* JSON-encoded string[] of mandatory labels (a Kubernetes-taint-style gate):
|
|
899
|
+
* a static agent registering with this token only accepts a job when every
|
|
900
|
+
* label here appears in the job's required labels. null = no taint (the
|
|
901
|
+
* default; the agent accepts any job its advertised labels match).
|
|
902
|
+
* Authorized at mint time alongside `labels`.
|
|
903
|
+
*/
|
|
904
|
+
mandatory_labels: string | null;
|
|
860
905
|
/** Token type: 'ephemeral' (scaler-issued) or 'static' (CLI-created) */
|
|
861
906
|
agent_type: string;
|
|
862
907
|
/** When this token was created */
|
|
@@ -1585,6 +1630,16 @@ export interface AttestationsTable {
|
|
|
1585
1630
|
media_type: string;
|
|
1586
1631
|
/** When this row was inserted. */
|
|
1587
1632
|
created_at: Generated<Date>;
|
|
1633
|
+
/**
|
|
1634
|
+
* Server-side verification verdict computed at ingest. One of
|
|
1635
|
+
* `verified` / `failed` / `unverifiable` / `pending`
|
|
1636
|
+
* (`attestationVerifyStatusSchema.enum.*`). DB default is `pending`.
|
|
1637
|
+
*/
|
|
1638
|
+
verify_status: Generated<string>;
|
|
1639
|
+
/** First failure code from `verifyKiciBundle`, or NULL when verified/pending. */
|
|
1640
|
+
verify_reason: string | null;
|
|
1641
|
+
/** When the verdict was recorded (explicitly set, not DB-generated). */
|
|
1642
|
+
verified_at: Date | null;
|
|
1588
1643
|
}
|
|
1589
1644
|
export type AttestationRow = Selectable<AttestationsTable>;
|
|
1590
1645
|
export type NewAttestationRow = Insertable<AttestationsTable>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EnvGateRejectReason, type Environment } from '@kici-dev/engine';
|
|
2
|
+
import type { JobDispatchContext } from './pipeline.js';
|
|
3
|
+
/** A single environment's rejection under all-must-pass aggregation. */
|
|
4
|
+
export interface EnvGateRejection {
|
|
5
|
+
environment: string;
|
|
6
|
+
reason: EnvGateRejectReason;
|
|
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 environment's hard reject gates against the run context. A name
|
|
20
|
+
* with no `Environment` record yields an `env_not_found` rejection. Returns all
|
|
21
|
+
* rejections (empty = every environment passed the reject gates).
|
|
22
|
+
*/
|
|
23
|
+
export declare function evaluateMultiEnvGates(envs: ReadonlyArray<{
|
|
24
|
+
name: string;
|
|
25
|
+
env: Environment | undefined;
|
|
26
|
+
}>, ctx: JobDispatchContext): EnvGateRejection[];
|
|
27
|
+
/**
|
|
28
|
+
* Aggregate hold/wait/queue parameters across all bound environments, 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) environment.
|
|
32
|
+
*/
|
|
33
|
+
export declare function aggregateProtectionParams(envs: ReadonlyArray<Environment>): EffectiveProtection;
|
|
34
|
+
/**
|
|
35
|
+
* Build a synthetic `Environment` 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 `evaluateMultiEnvGates`, so they are neutralized here.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildEffectiveEnvironment(primary: Environment, eff: EffectiveProtection): Environment;
|
|
41
|
+
/** Format a human-readable rejection reason naming the env(s) and rule(s). */
|
|
42
|
+
export declare function formatMultiEnvRejection(rejections: ReadonlyArray<EnvGateRejection>): string;
|
|
43
|
+
//# sourceMappingURL=aggregate.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration-time satisfiability check for multi-environment job bindings.
|
|
3
|
+
*
|
|
4
|
+
* When a job binds several environments (`environments: [...]`), every
|
|
5
|
+
* environment's protection gates must pass for the job to ever dispatch
|
|
6
|
+
* (all-must-pass aggregation — see `aggregate.ts`). Some of those gates are
|
|
7
|
+
* decidable statically: if two bound environments restrict to disjoint fixed
|
|
8
|
+
* branch sets, no run can ever satisfy both, so the binding is provably
|
|
9
|
+
* unsatisfiable and should be rejected at registration rather than failing
|
|
10
|
+
* silently at every future dispatch.
|
|
11
|
+
*
|
|
12
|
+
* This module intersects the statically-decidable set rules (branch, trigger
|
|
13
|
+
* type, repository — only when every pattern is a literal, never a glob) plus
|
|
14
|
+
* existence and enabled across the bound environments, and reports the first
|
|
15
|
+
* provably-empty intersection. Any glob in a rule makes that rule undecidable,
|
|
16
|
+
* so it is skipped here and left to the dispatch-time catch-all
|
|
17
|
+
* (`evaluateMultiEnvGates`).
|
|
18
|
+
*/
|
|
19
|
+
import { z } from 'zod';
|
|
20
|
+
import type { Environment } from '@kici-dev/engine';
|
|
21
|
+
/** Which decidable rule made a binding unsatisfiable. */
|
|
22
|
+
export declare const UnsatisfiableRule: z.ZodEnum<{
|
|
23
|
+
enabled: "enabled";
|
|
24
|
+
repo: "repo";
|
|
25
|
+
branch: "branch";
|
|
26
|
+
existence: "existence";
|
|
27
|
+
trigger: "trigger";
|
|
28
|
+
}>;
|
|
29
|
+
export type UnsatisfiableRule = z.infer<typeof UnsatisfiableRule>;
|
|
30
|
+
/** A provably-unsatisfiable multi-environment binding, naming the rule + reason. */
|
|
31
|
+
export interface UnsatisfiableBinding {
|
|
32
|
+
jobName: string;
|
|
33
|
+
environments: string[];
|
|
34
|
+
rule: UnsatisfiableRule;
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns a precise problem when the bound environments can NEVER be jointly
|
|
39
|
+
* satisfied (a provably-empty intersection on a decidable rule, a missing
|
|
40
|
+
* environment, or a disabled one), else `null`. Glob / undecidable cases return
|
|
41
|
+
* `null` and are caught at dispatch by `evaluateMultiEnvGates`.
|
|
42
|
+
*
|
|
43
|
+
* `envs[i]` is the resolved `Environment` for `envNames[i]` (undefined when the
|
|
44
|
+
* name has no environment record). Only the statically-known (non-dynamic) bound
|
|
45
|
+
* names should be passed — dynamic elements are unknown at registration and the
|
|
46
|
+
* all-must-pass semantics make the static subset's exclusivity still sound.
|
|
47
|
+
*/
|
|
48
|
+
export declare function checkBindingSatisfiable(jobName: string, envs: ReadonlyArray<Environment | undefined>, envNames: readonly string[]): UnsatisfiableBinding | null;
|
|
49
|
+
/** Minimal lock-workflow shape needed to walk its jobs for satisfiability. */
|
|
50
|
+
interface SatisfiabilityLockWorkflow {
|
|
51
|
+
jobs?: readonly unknown[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Walk every workflow's static jobs and reject the registration when a bound
|
|
55
|
+
* environment list is provably unsatisfiable (missing/disabled environment, or
|
|
56
|
+
* mutually-exclusive fixed branch/trigger/repo restrictions). Dynamic elements
|
|
57
|
+
* are skipped (unresolvable at registration); the all-must-pass semantics keep
|
|
58
|
+
* the static subset's exclusivity sound. Throws the first
|
|
59
|
+
* `UnsatisfiableBinding.message` so the registration route / direct helper
|
|
60
|
+
* surfaces it to the caller.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assertWorkflowsSatisfiable(workflows: ReadonlyArray<SatisfiabilityLockWorkflow>, resolveEnv: (name: string) => Promise<Environment | null>): Promise<void>;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=satisfiability.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1779,6 +1779,15 @@ var AdminApiClient = class {
|
|
|
1779
1779
|
return this.request("GET", `/api/v1/admin/runs/${encodeURIComponent(runId)}/jobs${qs}`);
|
|
1780
1780
|
}
|
|
1781
1781
|
/**
|
|
1782
|
+
* Fetch the machine-first, provenance-tagged structured run result: typed
|
|
1783
|
+
* job DAG, per-step exit codes / durations / statuses, derived failure
|
|
1784
|
+
* category. Untrusted fields are envelope-tagged; secret values are never
|
|
1785
|
+
* returned (only secret-output key names).
|
|
1786
|
+
*/
|
|
1787
|
+
async getRunStructured(runId) {
|
|
1788
|
+
return this.request("GET", `/api/v1/admin/runs/${encodeURIComponent(runId)}/structured`);
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1782
1791
|
* Fetch the scrub status of the run's ephemeral key. Never returns the
|
|
1783
1792
|
* key material itself — only `{ exists, createdAt }`.
|
|
1784
1793
|
*/
|
|
@@ -27,6 +27,7 @@ import { ExecutionJobStatus, type LabelMatcher, type PeerHeartbeat, type PeerLog
|
|
|
27
27
|
import { ScalerManager } from './scaler/index.js';
|
|
28
28
|
import type { ScalerConfig } from './scaler/index.js';
|
|
29
29
|
import type { CacheStorage } from './storage/index.js';
|
|
30
|
+
import { type ProvenanceTrustRoot } from './provenance/trust-root.js';
|
|
30
31
|
import { SourceCache, BuildCoordinator, DepCache, UserCache, DispatchCacheRefTracker, PendingBuildTracker, PendingInitTracker, PendingDynamicTracker } from './cache/index.js';
|
|
31
32
|
import { CheckRunReporter } from './reporting/check-run-reporter.js';
|
|
32
33
|
import { StepLogBuffer } from './reporting/step-log-buffer.js';
|
|
@@ -70,6 +71,12 @@ export interface OrchestratorSubsystems {
|
|
|
70
71
|
scalerManager: ScalerManager | null;
|
|
71
72
|
scalerConfig: ScalerConfig | null;
|
|
72
73
|
cacheStorage: CacheStorage | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Provenance trust root used to verify build-provenance bundles at ingest.
|
|
76
|
+
* The mode-specific hook (server.ts) wires the live issuer onto it from the
|
|
77
|
+
* Platform `auth.success` message via `onProvenanceIssuer`.
|
|
78
|
+
*/
|
|
79
|
+
provenanceTrustRoot: ProvenanceTrustRoot;
|
|
73
80
|
sourceCache: SourceCache | undefined;
|
|
74
81
|
depCache: DepCache | undefined;
|
|
75
82
|
userCache: UserCache | undefined;
|
|
@@ -315,6 +322,7 @@ export declare function mergeUpstreamOutputs(db: Kysely<Database>, runId: string
|
|
|
315
322
|
upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined;
|
|
316
323
|
upstreamJobStatuses: Record<string, ExecutionJobStatus> | undefined;
|
|
317
324
|
}>;
|
|
325
|
+
export declare function buildInternalJobConfigForWorkflow(workflow: any, job: any): Record<string, unknown>;
|
|
318
326
|
export declare function bootstrapOrchestrator(config: AppConfig, hooks: OrchestratorHooks, options?: {
|
|
319
327
|
otelSdk?: {
|
|
320
328
|
shutdown(): Promise<void>;
|
|
@@ -71,6 +71,8 @@ export interface WorkflowDispatchContext {
|
|
|
71
71
|
runId: string;
|
|
72
72
|
trustResolution: TrustResolution | undefined;
|
|
73
73
|
lockFileSource: string | undefined;
|
|
74
|
+
/** True when this run executes an uploaded local working tree (CLI remote run). */
|
|
75
|
+
localWorkingTree: boolean;
|
|
74
76
|
/** True only when invoked from the cross-source dispatch shell. */
|
|
75
77
|
crossSource: boolean;
|
|
76
78
|
/** Composite dedup key `${info.deliveryId}:${reg.id}` (cross-source only). */
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type LockStep, type LockStepEntry } from '@kici-dev/engine';
|
|
2
|
+
/**
|
|
3
|
+
* Flatten a lock job's `steps` into the flat sequential list the orchestrator
|
|
4
|
+
* iterates by `stepIndex`. A `parallel` group's children are inlined in array
|
|
5
|
+
* order and the group wrapper is dropped (it consumes no flat index). This keeps
|
|
6
|
+
* the orchestrator's enumeration aligned with the agent's `extractAndNormalizeSteps`
|
|
7
|
+
* — the flat-stepIndex invariant: `flattenLockSteps(job.steps)[i]` is the step at
|
|
8
|
+
* agent `stepIndex i`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function flattenLockSteps(steps: readonly LockStepEntry[]): readonly LockStep[];
|
|
11
|
+
//# sourceMappingURL=flatten-lock-steps.d.ts.map
|
|
@@ -36,7 +36,8 @@ export declare function evaluateInlineRecord(expression: string, event: object):
|
|
|
36
36
|
* failures are immediate dispatch failures (no init-job fallback).
|
|
37
37
|
*/
|
|
38
38
|
export declare function evaluateInlineFields(lockJob: LockJob, event: object): {
|
|
39
|
-
|
|
39
|
+
/** Resolved name per `environments` element, aligned by index; undefined for static or impure-dynamic elements. */
|
|
40
|
+
inlineEnvironmentNames: Array<string | undefined>;
|
|
40
41
|
inlineEnv: Record<string, string> | undefined;
|
|
41
42
|
inlineConcurrencyGroup: string | undefined;
|
|
42
43
|
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-environment resolution helpers for the dispatch path.
|
|
3
|
+
*
|
|
4
|
+
* A job binds an ordered list of environments (`LockJob.environments`). This
|
|
5
|
+
* module resolves that list into concrete environment names (static values
|
|
6
|
+
* verbatim, pure-inline dynamic elements evaluated against the event) and folds
|
|
7
|
+
* the per-environment secrets/variables last-wins. It keeps the heavy fold logic
|
|
8
|
+
* out of `dispatchMatchedWorkflow`, which must stay under the function-length cap.
|
|
9
|
+
*/
|
|
10
|
+
import { type Environment, type HostFacts, type LockJob } from '@kici-dev/engine';
|
|
11
|
+
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
12
|
+
import type { VariableStore } from '../environments/variable-store.js';
|
|
13
|
+
/**
|
|
14
|
+
* Placeholder written into the persisted bound-env list for an impure dynamic
|
|
15
|
+
* element the orchestrator cannot resolve at dispatch. The agent's init eval
|
|
16
|
+
* later overwrites the list with the resolved name.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DYNAMIC_ENV_PLACEHOLDER = "(dynamic)";
|
|
19
|
+
/** Ordered resolved environment names plus whether any element still needs agent init. */
|
|
20
|
+
export interface ResolvedJobEnvironments {
|
|
21
|
+
/** Resolved static + pure-inline names, in order. */
|
|
22
|
+
names: string[];
|
|
23
|
+
/** True when an impure dynamic element must be resolved by an agent init job. */
|
|
24
|
+
needsInit: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the ordered bound-environment names from a lock job. Static elements
|
|
28
|
+
* use their value verbatim; pure-inline dynamic elements use the matching
|
|
29
|
+
* pre-evaluated inline name (aligned by index); an impure dynamic element cannot
|
|
30
|
+
* be resolved here and flags `needsInit`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveJobEnvironmentNames(lockJob: LockJob, inlineNames: ReadonlyArray<string | undefined>): ResolvedJobEnvironments;
|
|
33
|
+
/**
|
|
34
|
+
* Build the ordered bound-environment display list for persistence at dispatch.
|
|
35
|
+
* Unlike {@link resolveJobEnvironmentNames}, this never drops an unresolved
|
|
36
|
+
* element: a static element uses its value, a pure-inline element uses its
|
|
37
|
+
* resolved name when known, and any element the orchestrator cannot resolve at
|
|
38
|
+
* dispatch (impure dynamic, or an unresolved pure-inline) becomes the
|
|
39
|
+
* `(dynamic)` placeholder — so the persisted column reflects every declared
|
|
40
|
+
* slot in order. Returns an empty array when the job binds no environment.
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildJobEnvironmentDisplayNames(lockJob: LockJob, inlineNames: ReadonlyArray<string | undefined>): string[];
|
|
43
|
+
/** Merged secrets/variables across an ordered list of resolved environments. */
|
|
44
|
+
export interface MultiEnvMergedData {
|
|
45
|
+
environmentVars?: Record<string, string>;
|
|
46
|
+
jobSecrets?: Record<string, string>;
|
|
47
|
+
jobNamespacedSecrets?: Record<string, Record<string, string>>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve and fold variables + secrets across the ordered list of matched
|
|
51
|
+
* environments, last-wins. Each environment is resolved with the existing
|
|
52
|
+
* single-env logic (longest-scope-path-wins preserved within each environment),
|
|
53
|
+
* then folded in array order so a later environment overrides an earlier key.
|
|
54
|
+
* Secrets are also returned namespaced per environment so qualified
|
|
55
|
+
* `<env>:<secret>` references still resolve. `entries` carries the matched
|
|
56
|
+
* `Environment` for each name (in order); variables resolve by environment id.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveMultiEnvMergedData(args: {
|
|
59
|
+
deps: {
|
|
60
|
+
variableStore?: VariableStore;
|
|
61
|
+
secretResolver?: SecretResolverApi;
|
|
62
|
+
};
|
|
63
|
+
orgId: string;
|
|
64
|
+
entries: ReadonlyArray<{
|
|
65
|
+
name: string;
|
|
66
|
+
env: Environment;
|
|
67
|
+
}>;
|
|
68
|
+
hostCtx?: HostFacts;
|
|
69
|
+
routingKey?: string;
|
|
70
|
+
}): Promise<MultiEnvMergedData>;
|
|
71
|
+
//# sourceMappingURL=job-environments.d.ts.map
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* eventRouter fire-and-forget. The manual path needs request/response
|
|
10
10
|
* correlation to return the newRunId to the dashboard.
|
|
11
11
|
*/
|
|
12
|
+
import type { LockWorkflow, MaterializedJob } from '@kici-dev/engine';
|
|
12
13
|
import type { RerunDeps } from './rerun.js';
|
|
13
14
|
import type { RegistrationIndex } from '../registration/registration-index.js';
|
|
14
15
|
interface ManualScheduleDeps extends RerunDeps {
|
|
@@ -17,5 +18,26 @@ interface ManualScheduleDeps extends RerunDeps {
|
|
|
17
18
|
export declare function handleManualSchedule(registrationId: string, triggeredBy: string | null, deps: ManualScheduleDeps): Promise<{
|
|
18
19
|
newRunId: string;
|
|
19
20
|
}>;
|
|
21
|
+
export declare function buildManualJobConfig(workflow: LockWorkflow, mat: MaterializedJob): {
|
|
22
|
+
resolvedHashFiles?: string[] | undefined;
|
|
23
|
+
contentHash?: string | undefined;
|
|
24
|
+
dispatchInputs?: Record<string, unknown> | undefined;
|
|
25
|
+
steps: readonly import("@kici-dev/engine").LockStepEntry[];
|
|
26
|
+
needs: readonly (string | {
|
|
27
|
+
name: string;
|
|
28
|
+
runOn: ("success" | "pending" | "failed" | "recovering" | "running" | "queued" | "skipped" | "cancelled" | "cancelling" | "timed_out_stale" | "drift_dropped")[];
|
|
29
|
+
} | {
|
|
30
|
+
group: string;
|
|
31
|
+
runOn: ("success" | "pending" | "failed" | "recovering" | "running" | "queued" | "skipped" | "cancelled" | "cancelling" | "timed_out_stale" | "drift_dropped")[];
|
|
32
|
+
})[];
|
|
33
|
+
rules: readonly import("@kici-dev/engine").LockRule[] | undefined;
|
|
34
|
+
name: string;
|
|
35
|
+
baseJobName: string;
|
|
36
|
+
matrixValues?: import("@kici-dev/engine").MatrixValues;
|
|
37
|
+
fanoutIndex?: number;
|
|
38
|
+
fanoutTotal?: number;
|
|
39
|
+
source: import("@kici-dev/engine").LockSource | undefined;
|
|
40
|
+
workflowName: string;
|
|
41
|
+
};
|
|
20
42
|
export {};
|
|
21
43
|
//# sourceMappingURL=manual-schedule.d.ts.map
|
|
@@ -93,6 +93,15 @@ interface TestTriggerResult {
|
|
|
93
93
|
/** Dispatched job IDs. */
|
|
94
94
|
jobIds: string[];
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Repo identity for an inline-lock (local working tree) run. Derived from the
|
|
98
|
+
* event payload the CLI stamps -- NOT the relay routing key, which is the
|
|
99
|
+
* Platform-internal `remote:<orgId>` anchor and is meaningless as a repo.
|
|
100
|
+
*/
|
|
101
|
+
export declare function repoIdentityFromInlineInput(input: TestTriggerInput): {
|
|
102
|
+
repoIdentifier: string;
|
|
103
|
+
provider: string;
|
|
104
|
+
};
|
|
96
105
|
/**
|
|
97
106
|
* Process a test trigger through the shared dispatch core.
|
|
98
107
|
*
|