@kici-dev/orchestrator 0.1.22 → 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/host-roster.d.ts +39 -3
- package/dist/agent/token-store.d.ts +33 -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 +2284 -442
- 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/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/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 +78 -0
- package/dist/environments/binding-store.d.ts +12 -3
- package/dist/environments/held-runs.d.ts +24 -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/metrics/prometheus.d.ts +2 -0
- package/dist/orchestrator-core.d.ts +8 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +60 -3
- 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 +15 -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/log-writer.d.ts +19 -0
- 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/scaler/manager.d.ts +17 -0
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/secret-resolver.d.ts +19 -6
- package/dist/server.js +21535 -19131
- package/dist/standalone.js +26544 -24580
- package/dist/storage/loopback-guard.d.ts +49 -0
- package/dist/ws/agent-handler.d.ts +15 -1
- package/dist/ws/bringup-api.d.ts +76 -0
- package/dist/ws/dashboard-fleet-handler.d.ts +11 -1
- package/dist/ws/fleet-runs-on-all.d.ts +3 -0
- package/dist/ws/platform-client.d.ts +39 -1
- package/dist/ws/test-relay-handlers.d.ts +9 -0
- 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,18 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Add a per-host dimension to environment secret bindings:
|
|
4
|
+
*
|
|
5
|
+
* - `environment_bindings.host_pattern text NOT NULL DEFAULT '**'` — the host
|
|
6
|
+
* selector a binding applies to (exact / glob / regex, matched against a
|
|
7
|
+
* fan-out child's agentId / hostname / labels). `'**'` matches every host, so
|
|
8
|
+
* existing rows are backfilled to `'**'` and keep their fleet-wide behaviour.
|
|
9
|
+
* - The binding unique key widens from `(environment_id, scope_pattern)` to
|
|
10
|
+
* `(environment_id, scope_pattern, host_pattern)` so the same scope can carry
|
|
11
|
+
* distinct per-host selectors.
|
|
12
|
+
*
|
|
13
|
+
* Idempotent: re-running on a DB that already has the column / index is a no-op.
|
|
14
|
+
* Additive — staging data is preserved.
|
|
15
|
+
*/
|
|
16
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
17
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=051_binding_host_pattern.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Add pre-agent reach metadata to the host roster so a declared host (no agent
|
|
4
|
+
* yet) can be reached over SSH for bootstrap bring-up:
|
|
5
|
+
*
|
|
6
|
+
* - `host_roster.address text NULL` — IP / hostname to SSH to.
|
|
7
|
+
* - `host_roster.ssh_user text NULL` — SSH login user (defaults to `root` at use site).
|
|
8
|
+
* - `host_roster.ssh_port int NULL` — SSH port (defaults to 22 at use site).
|
|
9
|
+
* - `host_roster.ssh_key_secret text NULL` — scoped-secret ref (`scope/key`)
|
|
10
|
+
* holding the bring-up private key. The orchestrator resolves it server-side;
|
|
11
|
+
* the key never lives in the roster.
|
|
12
|
+
*
|
|
13
|
+
* All nullable: a host with no reach metadata simply cannot be bootstrapped and
|
|
14
|
+
* behaves exactly as before. Idempotent (`ADD COLUMN IF NOT EXISTS`); additive,
|
|
15
|
+
* so staging data is preserved.
|
|
16
|
+
*/
|
|
17
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
18
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=052_host_reach_metadata.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Add a single-use marker to agent tokens for the bootstrap (init-runner)
|
|
4
|
+
* bring-up flow:
|
|
5
|
+
*
|
|
6
|
+
* - `agent_tokens.consumed_at timestamptz NULL` — set the first time a
|
|
7
|
+
* single-use bootstrap token is consumed (at `agent.register`). A second
|
|
8
|
+
* register with the same token is rejected. NULL = never consumed (the
|
|
9
|
+
* default for every existing static / ephemeral token, which stay reusable
|
|
10
|
+
* until expiry).
|
|
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=053_agent_token_single_use.d.ts.map
|
|
@@ -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
|
/**
|
|
@@ -605,6 +636,12 @@ export interface EnvironmentBindingsTable {
|
|
|
605
636
|
environment_id: string;
|
|
606
637
|
/** Scope pattern for matching (e.g. workflow name glob, repo pattern) */
|
|
607
638
|
scope_pattern: string;
|
|
639
|
+
/**
|
|
640
|
+
* Host selector this binding applies to (exact / glob / regex over a fan-out
|
|
641
|
+
* child's agentId / hostname / labels). `'**'` (the default) matches every
|
|
642
|
+
* host.
|
|
643
|
+
*/
|
|
644
|
+
host_pattern: Generated<string>;
|
|
608
645
|
/** When this binding was created */
|
|
609
646
|
created_at: Generated<Date>;
|
|
610
647
|
}
|
|
@@ -798,6 +835,12 @@ export interface AccessLogTable {
|
|
|
798
835
|
source: string;
|
|
799
836
|
outcome: string;
|
|
800
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;
|
|
801
844
|
created_at: Generated<Date>;
|
|
802
845
|
/**
|
|
803
846
|
* Set inside the archive transaction before the row is DELETEd.
|
|
@@ -851,6 +894,14 @@ export interface AgentTokenTable {
|
|
|
851
894
|
token_prefix: string;
|
|
852
895
|
/** JSON-encoded string[] of agent labels (null = any) */
|
|
853
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;
|
|
854
905
|
/** Token type: 'ephemeral' (scaler-issued) or 'static' (CLI-created) */
|
|
855
906
|
agent_type: string;
|
|
856
907
|
/** When this token was created */
|
|
@@ -863,6 +914,13 @@ export interface AgentTokenTable {
|
|
|
863
914
|
revoked_at: Date | null;
|
|
864
915
|
/** When this token expires (null = never, static tokens) */
|
|
865
916
|
expires_at: Date | null;
|
|
917
|
+
/**
|
|
918
|
+
* Single-use marker for bootstrap (init-runner) tokens. Set the first time
|
|
919
|
+
* the token is consumed at `agent.register`; a second register is rejected.
|
|
920
|
+
* NULL = never consumed (every static / ephemeral token stays reusable until
|
|
921
|
+
* expiry).
|
|
922
|
+
*/
|
|
923
|
+
consumed_at: ColumnType<Date | null, Date | string | null | undefined, Date | string | null>;
|
|
866
924
|
}
|
|
867
925
|
export type AgentTokenRow = Selectable<AgentTokenTable>;
|
|
868
926
|
export type NewAgentTokenRow = Insertable<AgentTokenTable>;
|
|
@@ -1572,6 +1630,16 @@ export interface AttestationsTable {
|
|
|
1572
1630
|
media_type: string;
|
|
1573
1631
|
/** When this row was inserted. */
|
|
1574
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;
|
|
1575
1643
|
}
|
|
1576
1644
|
export type AttestationRow = Selectable<AttestationsTable>;
|
|
1577
1645
|
export type NewAttestationRow = Insertable<AttestationsTable>;
|
|
@@ -1639,6 +1707,16 @@ export interface HostRosterTable {
|
|
|
1639
1707
|
* reconnects (down-then-up). NULL = no reboot pending.
|
|
1640
1708
|
*/
|
|
1641
1709
|
reboot_pending_until: ColumnType<Date | null, Date | string | null, Date | string | null>;
|
|
1710
|
+
/**
|
|
1711
|
+
* Pre-agent reach metadata: how to SSH to a declared host before it has a
|
|
1712
|
+
* KiCI agent, for bootstrap bring-up. All nullable — a host with no reach
|
|
1713
|
+
* metadata cannot be bootstrapped and behaves exactly as before.
|
|
1714
|
+
*/
|
|
1715
|
+
address: ColumnType<string | null, string | null | undefined, string | null>;
|
|
1716
|
+
ssh_user: ColumnType<string | null, string | null | undefined, string | null>;
|
|
1717
|
+
ssh_port: ColumnType<number | null, number | null | undefined, number | null>;
|
|
1718
|
+
/** Scoped-secret ref (`scope/key`) holding the bring-up private key. */
|
|
1719
|
+
ssh_key_secret: ColumnType<string | null, string | null | undefined, string | null>;
|
|
1642
1720
|
created_at: Generated<Date>;
|
|
1643
1721
|
updated_at: ColumnType<Date, Date | string | undefined, Date | string>;
|
|
1644
1722
|
}
|
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { Kysely } from 'kysely';
|
|
8
8
|
import type { Database, EnvironmentBinding } from '../db/types.js';
|
|
9
|
+
/**
|
|
10
|
+
* A scope→environment 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
|
+
}
|
|
9
17
|
/**
|
|
10
18
|
* Data access layer for environment bindings.
|
|
11
19
|
*/
|
|
@@ -17,10 +25,11 @@ export declare class BindingStore {
|
|
|
17
25
|
/**
|
|
18
26
|
* Replace all bindings for an environment in a transaction.
|
|
19
27
|
*
|
|
20
|
-
* Deletes existing bindings and inserts the new set atomically.
|
|
21
|
-
*
|
|
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.
|
|
22
31
|
*/
|
|
23
|
-
set(orgId: string, environmentId: string,
|
|
32
|
+
set(orgId: string, environmentId: string, bindings: BindingInput[]): Promise<void>;
|
|
24
33
|
/** Find bindings for an environment (alias for list, used by secret resolver). */
|
|
25
34
|
findBindingsForEnvironment(orgId: string, environmentId: string): Promise<EnvironmentBinding[]>;
|
|
26
35
|
}
|
|
@@ -104,8 +104,32 @@ export declare class HeldRunStore {
|
|
|
104
104
|
* the created row.
|
|
105
105
|
*/
|
|
106
106
|
createHold(orgId: string, data: CreateHoldData): Promise<HeldRun>;
|
|
107
|
+
/** INSERT one decision row using the given executor (root or transaction). */
|
|
108
|
+
private insertDecisionRow;
|
|
109
|
+
/** Flip a pending hold to 'approved' using the given executor. Undefined if not pending. */
|
|
110
|
+
private flipToApproved;
|
|
111
|
+
/** Flip a pending hold to 'rejected' using the given executor. Undefined if not pending. */
|
|
112
|
+
private flipToRejected;
|
|
113
|
+
/** Map a released held_runs row to the resume ReleaseSignal. */
|
|
114
|
+
private toReleaseSignal;
|
|
107
115
|
/** Record one approve/reject decision against a hold. */
|
|
108
116
|
recordDecision(heldRunId: string, data: RecordDecisionData): Promise<HeldRunApproval>;
|
|
117
|
+
/**
|
|
118
|
+
* Atomically record an approve decision and release the (now-satisfied) hold.
|
|
119
|
+
* The INSERT into `held_run_approvals` and the `held_runs` → approved UPDATE
|
|
120
|
+
* run in a single transaction, so a crash between them cannot strand the hold
|
|
121
|
+
* `pending` with a recorded approve. Throws if the hold is not found or no
|
|
122
|
+
* longer pending (the whole transaction rolls back).
|
|
123
|
+
*/
|
|
124
|
+
recordAndRelease(orgId: string, heldRunId: string, data: RecordDecisionData): Promise<ReleaseSignal>;
|
|
125
|
+
/**
|
|
126
|
+
* Atomically record a reject decision and reject the hold. The INSERT and the
|
|
127
|
+
* `held_runs` → rejected UPDATE run in a single transaction, so a crash
|
|
128
|
+
* between them cannot strand the hold `pending` with a recorded reject (which
|
|
129
|
+
* would poison `evaluate()` forever). Throws if the hold is not found or no
|
|
130
|
+
* longer pending (the whole transaction rolls back).
|
|
131
|
+
*/
|
|
132
|
+
recordAndReject(orgId: string, heldRunId: string, data: RecordDecisionData, reason?: string): Promise<HeldRun>;
|
|
109
133
|
/** List the recorded decisions for a hold, oldest first. */
|
|
110
134
|
listDecisions(heldRunId: string): Promise<HeldRunApproval[]>;
|
|
111
135
|
/** Get a single held run by id (org-scoped). Returns null if absent. */
|
|
@@ -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
|
*/
|
|
@@ -8,6 +8,8 @@ export declare function setDeclaredHostsUnreachable(value: number): void;
|
|
|
8
8
|
export declare function setStaleRunsCurrent(value: number): void;
|
|
9
9
|
interface ScalerUsageRow {
|
|
10
10
|
scaler: string;
|
|
11
|
+
/** Backend type for this scaler (rollup dimension). `__global__` for the orchestrator-wide row. */
|
|
12
|
+
scalerType?: string;
|
|
11
13
|
machinePool?: string;
|
|
12
14
|
cpus: number;
|
|
13
15
|
memBytes: number;
|