@kici-dev/orchestrator 0.1.16 → 0.1.17
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 +18 -1
- package/dist/approvals/apply-decision.d.ts +11 -1
- package/dist/cache/pending-inits.d.ts +5 -0
- package/dist/cli/api-client.d.ts +11 -0
- package/dist/cli/commands/local-hook.d.ts +15 -0
- package/dist/cli/commands/local-trigger.d.ts +24 -0
- package/dist/cli/commands/remote-source.d.ts +17 -0
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +14 -0
- package/dist/cli/kici-admin.d.ts +13 -0
- package/dist/cli.js +726 -177
- package/dist/config.d.ts +0 -4
- package/dist/dashboard/handler.d.ts +26 -2
- package/dist/dashboard/needs-edges.d.ts +13 -0
- package/dist/db/migrations/035_pending_workflow_contexts.d.ts +11 -0
- package/dist/db/migrations/036_attestations.d.ts +15 -0
- package/dist/db/migrations/037_generic_sources_provider_type_local.d.ts +19 -0
- package/dist/db/migrations/038_remote_sources.d.ts +14 -0
- package/dist/db/types.d.ts +83 -10
- package/dist/diagnostics/fleet-collector.d.ts +1 -1
- package/dist/entry-helpers.d.ts +7 -29
- package/dist/environments/held-runs.d.ts +24 -0
- package/dist/index.js +1 -0
- package/dist/metrics/prometheus.d.ts +4 -2
- package/dist/orchestrator-core.d.ts +48 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +18 -1
- package/dist/pipeline/install-secrets-resolver.d.ts +32 -5
- package/dist/pipeline/needs-scheduler.d.ts +12 -10
- package/dist/pipeline/pending-workflow-context.d.ts +44 -0
- package/dist/pipeline/processor.d.ts +6 -4
- package/dist/pipeline/remote-source-store.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +26 -0
- package/dist/providers/local/index.d.ts +33 -0
- package/dist/providers/local/local-source-config.d.ts +17 -0
- package/dist/providers/{internal → local}/lock-file-fetcher.d.ts +6 -6
- package/dist/providers/{internal → local}/normalizer.d.ts +28 -28
- package/dist/providers/{internal → local}/repo-url-builder.d.ts +6 -6
- package/dist/reporting/execution-tracker.d.ts +33 -0
- package/dist/routes/admin-events.d.ts +3 -4
- package/dist/routes/uploads.d.ts +36 -26
- package/dist/server.js +27910 -26483
- package/dist/sources/build-platform-sources.d.ts +4 -2
- package/dist/stale-detector/stale-run-detector.d.ts +14 -1
- package/dist/standalone.js +15708 -15619
- package/dist/webhook/generic-sources-listener.d.ts +4 -0
- package/dist/webhook/generic-sources.d.ts +37 -11
- package/dist/webhook/register-source-bundle.d.ts +14 -4
- package/dist/ws/agent-handler.d.ts +17 -0
- package/dist/ws/dashboard-dispatch-guard.d.ts +21 -0
- package/dist/ws/dashboard-env-handler.d.ts +13 -1
- package/dist/ws/oidc-token-relay.d.ts +59 -0
- package/dist/ws/platform-client.d.ts +30 -1
- package/dist/ws/test-relay-handlers.d.ts +112 -0
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +98 -48
- package/dist/providers/internal/index.d.ts +0 -32
- package/dist/routes/test-trigger.d.ts +0 -41
- package/dist/ws/observer-handler.d.ts +0 -42
package/dist/config.d.ts
CHANGED
|
@@ -105,8 +105,6 @@ declare const configSchema: z.ZodObject<{
|
|
|
105
105
|
production: "production";
|
|
106
106
|
test: "test";
|
|
107
107
|
}>>;
|
|
108
|
-
internalProviderRepoPath: z.ZodOptional<z.ZodString>;
|
|
109
|
-
internalProviderCloneUrl: z.ZodOptional<z.ZodString>;
|
|
110
108
|
autoMigrate: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
111
109
|
agentMaxReconnectDelayMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
112
110
|
skipS3SentinelValidation: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
@@ -262,8 +260,6 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
262
260
|
secretKeyFileOld?: string | undefined;
|
|
263
261
|
bootstrapAdminToken?: string | undefined;
|
|
264
262
|
testEventFailFirstN?: string | undefined;
|
|
265
|
-
internalProviderRepoPath?: string | undefined;
|
|
266
|
-
internalProviderCloneUrl?: string | undefined;
|
|
267
263
|
otelExporterOtlpEndpoint?: string | undefined;
|
|
268
264
|
clusterName?: string | undefined;
|
|
269
265
|
}>;
|
|
@@ -13,14 +13,24 @@
|
|
|
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, 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, DashboardPayloadRequest, DashboardOrchLogsRequest, DashboardEventLogListRequest, DashboardEventLogDetailRequest, DashboardEventLogPayloadStreamRequest, DashboardAccessLogListRequest, DashboardEventDlqListRequest, DashboardEventDlqCountRequest, DashboardEventDlqRetryRequest, DashboardEventDlqDiscardRequest, RunRerunRequest, RunCancelRequest, ManualScheduleRequest } from '@kici-dev/engine';
|
|
17
17
|
import type { Database } from '../db/types.js';
|
|
18
18
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
19
|
+
import type { CacheStorage } from '../storage/types.js';
|
|
19
20
|
import type { AccessLogWriter } from '../audit/access-log.js';
|
|
20
21
|
import type { EventStore } from '../events/event-store.js';
|
|
21
22
|
interface DashboardHandlerDeps {
|
|
22
23
|
db: Kysely<Database>;
|
|
23
24
|
logStorage: LogStorage;
|
|
25
|
+
/**
|
|
26
|
+
* Object-storage backend for provenance bundles. The attestations handler
|
|
27
|
+
* reads each stored bundle by `storage_key` and inlines it into the response
|
|
28
|
+
* so the dashboard verifies it client-side without a second fetch. Optional —
|
|
29
|
+
* when absent (e.g. orchestrators without provenance configured) the handler
|
|
30
|
+
* replies with an `error` and an empty list. Reuses the cache storage backend
|
|
31
|
+
* (the same one P1.5 writes bundles to).
|
|
32
|
+
*/
|
|
33
|
+
provenanceStorage?: CacheStorage | null;
|
|
24
34
|
/** Send a response message back to Platform over the WS connection. */
|
|
25
35
|
send: (msg: unknown) => void;
|
|
26
36
|
/** This orchestrator's instance ID, included in job detail responses. */
|
|
@@ -78,6 +88,7 @@ interface DashboardHandlerDeps {
|
|
|
78
88
|
export declare class DashboardHandler {
|
|
79
89
|
private readonly db;
|
|
80
90
|
private readonly logStorage;
|
|
91
|
+
private readonly provenanceStorage;
|
|
81
92
|
private readonly send;
|
|
82
93
|
private readonly orchestratorId;
|
|
83
94
|
private readonly accessLog;
|
|
@@ -222,7 +233,7 @@ export declare class DashboardHandler {
|
|
|
222
233
|
*
|
|
223
234
|
* `triggerTypes` is populated from `execution_runs.provider` — the
|
|
224
235
|
* orchestrator has no dedicated trigger-event column, so `provider`
|
|
225
|
-
* (`github` / `generic` / `
|
|
236
|
+
* (`github` / `generic` / `local`) is the closest available
|
|
226
237
|
* discriminator the customer filter can offer.
|
|
227
238
|
*
|
|
228
239
|
* Attribution uses the handler-bound `orgId` / `routingKey` directly — the
|
|
@@ -251,6 +262,19 @@ export declare class DashboardHandler {
|
|
|
251
262
|
* Looks up the step's log_path and reads content from LogStorage.
|
|
252
263
|
*/
|
|
253
264
|
handleStepLogs(msg: DashboardStepLogsRequest): Promise<void>;
|
|
265
|
+
/**
|
|
266
|
+
* Read the attestation rows for a run, joined to `execution_jobs` for the
|
|
267
|
+
* job name. Ordered oldest-first so the dashboard table is stable.
|
|
268
|
+
*/
|
|
269
|
+
private resolveAttestationsForRun;
|
|
270
|
+
/**
|
|
271
|
+
* Handle a dashboard.attestations.list request: list the run's provenance
|
|
272
|
+
* attestations, inlining each stored bundle from object storage so the
|
|
273
|
+
* dashboard verifies it client-side. Mirrors `handleStepLogs` (per-run org
|
|
274
|
+
* resolution + `recordAccess` + `send`). Rows whose bundle can't be read are
|
|
275
|
+
* skipped (best-effort) rather than failing the whole list.
|
|
276
|
+
*/
|
|
277
|
+
handleAttestationsList(msg: DashboardAttestationsListRequest): Promise<void>;
|
|
254
278
|
/**
|
|
255
279
|
* Handle a dashboard.payload request.
|
|
256
280
|
* Reads the webhook payload from log storage for the given runId.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IfFailedPolicy } from '@kici-dev/engine';
|
|
2
|
+
/** A single resolved upstream dependency edge for a job. */
|
|
3
|
+
export interface JobNeedEdge {
|
|
4
|
+
upstreamName: string;
|
|
5
|
+
ifFailed: IfFailedPolicy;
|
|
6
|
+
}
|
|
7
|
+
/** Group raw execution_job_needs rows by downstream job_name. */
|
|
8
|
+
export declare function groupNeedsByJobName(rows: ReadonlyArray<{
|
|
9
|
+
job_name: string;
|
|
10
|
+
upstream_name: string;
|
|
11
|
+
if_failed: string;
|
|
12
|
+
}>): Map<string, JobNeedEdge[]>;
|
|
13
|
+
//# sourceMappingURL=needs-edges.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Pending workflow dispatch context — backs resume of a workflow whose install
|
|
4
|
+
* gate held. One row per held run: the serializable dispatch inputs needed to
|
|
5
|
+
* rebuild the WorkflowDispatchContext when the hold releases (reviewer approve,
|
|
6
|
+
* wait-timer expiry, concurrency slot free). The row is deleted once the resume
|
|
7
|
+
* dispatch has been kicked off.
|
|
8
|
+
*/
|
|
9
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
10
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=035_pending_workflow_contexts.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Add the `attestations` table for build-provenance bundles.
|
|
4
|
+
*
|
|
5
|
+
* When a workflow step calls `ctx.attestProvenance`, the agent uploads the
|
|
6
|
+
* signed KiCI bundle to object storage under
|
|
7
|
+
* `provenance/{run_id}/{job_id}/{subject_digest}.kici.json` and the
|
|
8
|
+
* orchestrator records one row here so the dashboard can list and fetch
|
|
9
|
+
* attestations per run/job.
|
|
10
|
+
*
|
|
11
|
+
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
12
|
+
*/
|
|
13
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
14
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=036_attestations.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Replace the `generic_webhook_sources.provider_type` CHECK constraint so it
|
|
4
|
+
* permits `'local'` instead of `'internal'`, and migrate any carried-over
|
|
5
|
+
* `'internal'` rows to `'local'`.
|
|
6
|
+
*
|
|
7
|
+
* The local filesystem (`file://`) provider is `provider_type='local'`; the
|
|
8
|
+
* value used to be `'internal'`. The original CHECK constraint
|
|
9
|
+
* (`generic_webhook_sources_provider_type_check`) allowed only
|
|
10
|
+
* `{'generic','internal'}`, so writing a `'local'` row fails. Universal-git
|
|
11
|
+
* sources keep `provider_type='generic'` (discriminated by `git_config`), so
|
|
12
|
+
* the new allowed set is `{'generic','local'}`.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent: the constraint is dropped IF EXISTS and recreated; the data
|
|
15
|
+
* backfill is a plain UPDATE that is a no-op once no `'internal'` rows remain.
|
|
16
|
+
*/
|
|
17
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
18
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=037_generic_sources_provider_type_local.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* `remote_sources` anchors a Platform-relayed `kici run remote` to its real
|
|
4
|
+
* org. One row per org served by this orchestrator: routing key
|
|
5
|
+
* `remote:<orgId>` maps to the canonical org id, so `resolveOrgId` resolves
|
|
6
|
+
* the real tenant through the same local-source path a webhook takes — no
|
|
7
|
+
* GitHub App, no webhook secret, no manual setup. The row is auto-provisioned
|
|
8
|
+
* on Platform auth from the canonical org id carried on `auth.success`.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
11
|
+
*/
|
|
12
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
13
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=038_remote_sources.d.ts.map
|
package/dist/db/types.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export interface Database {
|
|
|
39
39
|
org_settings: OrgSettingsTable;
|
|
40
40
|
execution_job_needs: ExecutionJobNeedsTable;
|
|
41
41
|
pending_job_contexts: PendingJobContextsTable;
|
|
42
|
+
pending_workflow_contexts: PendingWorkflowContextsTable;
|
|
42
43
|
event_log: EventLogTable;
|
|
43
44
|
access_log: AccessLogTable;
|
|
44
45
|
cold_store_chunk_counts: ColdStoreChunkCountsTable;
|
|
@@ -47,6 +48,8 @@ export interface Database {
|
|
|
47
48
|
scaler_spawning_agents: ScalerSpawningAgentsTable;
|
|
48
49
|
scaler_agent_jobs: ScalerAgentJobsTable;
|
|
49
50
|
scaler_reservations: ScalerReservationsTable;
|
|
51
|
+
attestations: AttestationsTable;
|
|
52
|
+
remote_sources: RemoteSourcesTable;
|
|
50
53
|
}
|
|
51
54
|
/**
|
|
52
55
|
* Cluster metadata table
|
|
@@ -925,15 +928,17 @@ export interface GenericWebhookSourceTable {
|
|
|
925
928
|
deleted_at: Date | null;
|
|
926
929
|
/** Provider implementation to route this source through.
|
|
927
930
|
* 'generic' (default) → GenericWebhookNormalizer (Stripe-shaped payloads).
|
|
928
|
-
* '
|
|
929
|
-
*
|
|
931
|
+
* 'local' → LocalWebhookNormalizer (github-shaped push/PR payloads for a
|
|
932
|
+
* git repository present on the agent filesystem, cloned via file://). */
|
|
930
933
|
provider_type: Generated<string>;
|
|
931
|
-
/**
|
|
932
|
-
*
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
936
|
-
*
|
|
934
|
+
/** Git config (JSONB), dual-purpose discriminated by `provider_type`:
|
|
935
|
+
* - universal-git sources ('generic' with git_config) store a
|
|
936
|
+
* `UniversalGitConfig` (clone URLs, lock-file fetch, clone credentials
|
|
937
|
+
* against Forgejo / Gitea / Gogs / GitLab repo-webhook / GitHub repo).
|
|
938
|
+
* - local sources ('local') store a `LocalSourceConfig`
|
|
939
|
+
* (`{ repoBasePath, cloneUrlBase? }`).
|
|
940
|
+
* null for plain generic sources that don't drive git operations. Shape
|
|
941
|
+
* validated by the matching Zod schema at the application layer. */
|
|
937
942
|
git_config: ColumnType<string | Record<string, unknown> | null, string | null, string | null>;
|
|
938
943
|
}
|
|
939
944
|
export type GenericWebhookSource = Selectable<GenericWebhookSourceTable>;
|
|
@@ -1271,6 +1276,24 @@ export interface PendingJobContextsTable {
|
|
|
1271
1276
|
}
|
|
1272
1277
|
export type PendingJobContextRow = Selectable<PendingJobContextsTable>;
|
|
1273
1278
|
export type NewPendingJobContext = Insertable<PendingJobContextsTable>;
|
|
1279
|
+
/**
|
|
1280
|
+
* Pending workflow dispatch contexts table.
|
|
1281
|
+
* Persists the serializable WorkflowDispatchContext inputs needed to resume a
|
|
1282
|
+
* workflow whose install gate held. One row per held run (keyed by run_id).
|
|
1283
|
+
* Created at hold time, deleted once the resume dispatch is kicked off.
|
|
1284
|
+
*/
|
|
1285
|
+
export interface PendingWorkflowContextsTable {
|
|
1286
|
+
/** Run identifier (one pending dispatch per run) */
|
|
1287
|
+
run_id: string;
|
|
1288
|
+
/** Organization id */
|
|
1289
|
+
org_id: string;
|
|
1290
|
+
/** Serializable WorkflowDispatchContext inputs as JSONB */
|
|
1291
|
+
context: ColumnType<Record<string, unknown>, string, string>;
|
|
1292
|
+
/** When this context was stored */
|
|
1293
|
+
created_at: Generated<Date>;
|
|
1294
|
+
}
|
|
1295
|
+
export type PendingWorkflowContextRow = Selectable<PendingWorkflowContextsTable>;
|
|
1296
|
+
export type NewPendingWorkflowContext = Insertable<PendingWorkflowContextsTable>;
|
|
1274
1297
|
/**
|
|
1275
1298
|
* Inbound webhook delivery event log table.
|
|
1276
1299
|
* One row per inbound webhook delivery (relay or direct), regardless of
|
|
@@ -1289,7 +1312,7 @@ export interface EventLogTable {
|
|
|
1289
1312
|
org_id: string;
|
|
1290
1313
|
/** Provider delivery ID (unique within org) */
|
|
1291
1314
|
delivery_id: string;
|
|
1292
|
-
/** Routing key (e.g. "github:42", "generic:<org>:<src>"
|
|
1315
|
+
/** Routing key (e.g. "github:42", "generic:<org>:<src>") */
|
|
1293
1316
|
routing_key: string;
|
|
1294
1317
|
/** Provider event name (X-GitHub-Event / X-Event-Type / 'default') */
|
|
1295
1318
|
event: string;
|
|
@@ -1297,7 +1320,7 @@ export interface EventLogTable {
|
|
|
1297
1320
|
action: string | null;
|
|
1298
1321
|
/** Where the delivery arrived: 'relay' (Platform WS) or 'direct' (HTTP) */
|
|
1299
1322
|
source: string;
|
|
1300
|
-
/** Provider type ('github' / 'generic' / '
|
|
1323
|
+
/** Provider type ('github' / 'generic' / 'local') */
|
|
1301
1324
|
provider: string;
|
|
1302
1325
|
/** owner/repo when extractable */
|
|
1303
1326
|
repo_identifier: string | null;
|
|
@@ -1456,6 +1479,56 @@ export interface CheckRunTrackingTable {
|
|
|
1456
1479
|
export type CheckRunTrackingRow = Selectable<CheckRunTrackingTable>;
|
|
1457
1480
|
export type NewCheckRunTrackingRow = Insertable<CheckRunTrackingTable>;
|
|
1458
1481
|
export type CheckRunTrackingUpdate = Updateable<CheckRunTrackingTable>;
|
|
1482
|
+
/**
|
|
1483
|
+
* Build-provenance attestation table.
|
|
1484
|
+
*
|
|
1485
|
+
* One row per `ctx.attestProvenance` bundle uploaded to object storage. The
|
|
1486
|
+
* dashboard lists/fetches attestations by `(run_id, job_id)`.
|
|
1487
|
+
*/
|
|
1488
|
+
export interface AttestationsTable {
|
|
1489
|
+
/** Random id (primary key). */
|
|
1490
|
+
id: string;
|
|
1491
|
+
/** KiCI run this attestation belongs to. */
|
|
1492
|
+
run_id: string;
|
|
1493
|
+
/** KiCI job this attestation was produced by. */
|
|
1494
|
+
job_id: string;
|
|
1495
|
+
/** Caller-supplied artifact name (e.g. an npm package coordinate). */
|
|
1496
|
+
subject_name: string;
|
|
1497
|
+
/** Primary subject digest (lowercase hex) used in the storage key. */
|
|
1498
|
+
subject_digest: string;
|
|
1499
|
+
/** Object-storage key the bundle was written to. */
|
|
1500
|
+
storage_key: string;
|
|
1501
|
+
/** Signing mode ('kici' for Mode A). */
|
|
1502
|
+
mode: string;
|
|
1503
|
+
/** Bundle media type. */
|
|
1504
|
+
media_type: string;
|
|
1505
|
+
/** When this row was inserted. */
|
|
1506
|
+
created_at: Generated<Date>;
|
|
1507
|
+
}
|
|
1508
|
+
export type AttestationRow = Selectable<AttestationsTable>;
|
|
1509
|
+
export type NewAttestationRow = Insertable<AttestationsTable>;
|
|
1510
|
+
/**
|
|
1511
|
+
* Remote-source table (remote_sources).
|
|
1512
|
+
*
|
|
1513
|
+
* Anchors a Platform-relayed `kici run remote` to its real org: routing key
|
|
1514
|
+
* `remote:<orgId>` maps to the canonical org id so `resolveOrgId` resolves the
|
|
1515
|
+
* real tenant through the same local-source path a webhook takes. One row per
|
|
1516
|
+
* org served by this orchestrator, auto-provisioned on Platform auth.
|
|
1517
|
+
*/
|
|
1518
|
+
export interface RemoteSourcesTable {
|
|
1519
|
+
/** Canonical org id (`org_<…>`) this anchor resolves to. */
|
|
1520
|
+
customer_id: string;
|
|
1521
|
+
/** Deterministic routing key `remote:<orgId>`. */
|
|
1522
|
+
routing_key: string;
|
|
1523
|
+
/** Cluster id this orchestrator deployment reports, or null. */
|
|
1524
|
+
cluster_id: string | null;
|
|
1525
|
+
/** When this row was first provisioned. */
|
|
1526
|
+
created_at: Generated<Date>;
|
|
1527
|
+
/** When this row was last upserted. */
|
|
1528
|
+
updated_at: Generated<Date>;
|
|
1529
|
+
}
|
|
1530
|
+
export type RemoteSourceRow = Selectable<RemoteSourcesTable>;
|
|
1531
|
+
export type NewRemoteSourceRow = Insertable<RemoteSourcesTable>;
|
|
1459
1532
|
/**
|
|
1460
1533
|
* Scaler spawning-agents table (scaler_spawning_agents).
|
|
1461
1534
|
*
|
|
@@ -13,8 +13,8 @@ import { z } from 'zod';
|
|
|
13
13
|
export declare const FleetNodeStatus: z.ZodEnum<{
|
|
14
14
|
error: "error";
|
|
15
15
|
ok: "ok";
|
|
16
|
-
timeout: "timeout";
|
|
17
16
|
unreachable: "unreachable";
|
|
17
|
+
timeout: "timeout";
|
|
18
18
|
}>;
|
|
19
19
|
export type FleetNodeStatus = z.infer<typeof FleetNodeStatus>;
|
|
20
20
|
export interface FleetCollectorDeps {
|
package/dist/entry-helpers.d.ts
CHANGED
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
* Both entry points import these helpers instead of maintaining separate copies.
|
|
6
6
|
*/
|
|
7
7
|
import { type SourceProvider, SourceSubtype } from '@kici-dev/engine';
|
|
8
|
-
import { ProviderRegistry } from './provider-registry.js';
|
|
9
8
|
/**
|
|
10
9
|
* A provider source for Platform registration.
|
|
11
10
|
*
|
|
12
11
|
* `provider` is the coarse-grained Platform-routing family (`github` /
|
|
13
12
|
* `gitlab` / `bitbucket` / `generic`). `subtype` is the fine-grained
|
|
14
13
|
* source kind that survives all the way to the dashboard so universal-git,
|
|
15
|
-
* generic_webhook, and
|
|
14
|
+
* generic_webhook, and local sources stay distinguishable. `name` is
|
|
16
15
|
* the human-readable label shown in the runs view + sources tab.
|
|
17
16
|
*/
|
|
18
17
|
export interface ProviderSource {
|
|
@@ -50,24 +49,6 @@ export declare function diffProviderSources(oldSources: ProviderSource[], newSou
|
|
|
50
49
|
added: ProviderSource[];
|
|
51
50
|
removed: ProviderSource[];
|
|
52
51
|
};
|
|
53
|
-
/**
|
|
54
|
-
* Register the internal provider bundle for E2E testing when the
|
|
55
|
-
* `internalProviderRepoPath` config field (KICI_INTERNAL_PROVIDER_REPO_PATH)
|
|
56
|
-
* is set.
|
|
57
|
-
*
|
|
58
|
-
* When the field is present, this overwrites the previously-registered
|
|
59
|
-
* `generic:default` bundle with one that has a lockFileFetcher and
|
|
60
|
-
* repoUrlBuilder (needed for the webhook pipeline to match triggers and
|
|
61
|
-
* dispatch jobs).
|
|
62
|
-
*
|
|
63
|
-
* When the field is not set, does nothing (leaves `generic:default` as-is).
|
|
64
|
-
*
|
|
65
|
-
* @returns true if the internal provider was registered, false otherwise
|
|
66
|
-
*/
|
|
67
|
-
export declare function registerInternalProviderIfConfigured(registry: ProviderRegistry, config: {
|
|
68
|
-
internalProviderRepoPath?: string;
|
|
69
|
-
internalProviderCloneUrl?: string;
|
|
70
|
-
}): boolean;
|
|
71
52
|
/**
|
|
72
53
|
* Decide whether the local orchestrator can serve a `generic_webhook_sources`
|
|
73
54
|
* row with the given `provider_type`. Used at boot (and any future reload) to
|
|
@@ -79,15 +60,12 @@ export declare function registerInternalProviderIfConfigured(registry: ProviderR
|
|
|
79
60
|
* - `'generic'` / `'universal-git'` → always servable (the bundles register
|
|
80
61
|
* without filesystem dependencies; per-row failures during universal-git
|
|
81
62
|
* bundle registration are already isolated in orchestrator-core.ts).
|
|
82
|
-
* - `'
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* the same base dir).
|
|
63
|
+
* - `'local'` → only servable when the ROW's own `git_config.repoBasePath`
|
|
64
|
+
* exists as a directory on THIS peer. The check is per-source and per-peer:
|
|
65
|
+
* a local repo present on one HA peer may be absent on another, so only the
|
|
66
|
+
* peer that hosts the repo advertises the routing key. Mirrors the statSync
|
|
67
|
+
* gate in `registerProviderBundleForSource`.
|
|
88
68
|
* - Any unknown provider_type returns false to fail closed.
|
|
89
69
|
*/
|
|
90
|
-
export declare function canServeGenericProviderType(providerType: string,
|
|
91
|
-
internalProviderRepoPath?: string;
|
|
92
|
-
}): boolean;
|
|
70
|
+
export declare function canServeGenericProviderType(providerType: string, gitConfig?: string | Record<string, unknown> | null): boolean;
|
|
93
71
|
//# sourceMappingURL=entry-helpers.d.ts.map
|
|
@@ -45,6 +45,12 @@ export interface CreateHoldData {
|
|
|
45
45
|
environmentId?: string | null;
|
|
46
46
|
/** Queue type: 'environment' (default) or 'security'. */
|
|
47
47
|
queueType?: 'environment' | 'security';
|
|
48
|
+
/**
|
|
49
|
+
* Held-run `hold_type` discriminator. Defaults to `'approval'` (reviewer
|
|
50
|
+
* holds). The workflow install gate sets `'wait_timer'` / `'concurrency'` so
|
|
51
|
+
* the automated release sweeps can find their rows.
|
|
52
|
+
*/
|
|
53
|
+
holdType?: string;
|
|
48
54
|
}
|
|
49
55
|
/** A single decision to record against a hold. */
|
|
50
56
|
export interface RecordDecisionData {
|
|
@@ -121,6 +127,24 @@ export declare class HeldRunStore {
|
|
|
121
127
|
* holds notify the waiting agent; job/workflow holds fail the run).
|
|
122
128
|
*/
|
|
123
129
|
listOverdue(): Promise<HeldRun[]>;
|
|
130
|
+
/**
|
|
131
|
+
* Release overdue workflow `wait_timer` holds. The install-gate wait action
|
|
132
|
+
* pauses the workflow as a held run; on timer expiry it must RESUME (not
|
|
133
|
+
* fail like a reviewer-hold expiry). Flips each overdue pending
|
|
134
|
+
* `hold_type='wait_timer'`, `hold_scope='workflow'` row to `released` and
|
|
135
|
+
* returns a `ReleaseSignal` per row so the caller can resume the workflow.
|
|
136
|
+
* Runs BEFORE `expireOverdue()` so these rows leave the pending pool before
|
|
137
|
+
* the expire-and-fail sweep sees them.
|
|
138
|
+
*/
|
|
139
|
+
releaseDueWaitHolds(): Promise<ReleaseSignal[]>;
|
|
140
|
+
/**
|
|
141
|
+
* Release the oldest pending workflow `concurrency` hold for a group. Called
|
|
142
|
+
* when a concurrency slot frees on run completion. Flips the oldest matching
|
|
143
|
+
* pending row to `released` and returns its `ReleaseSignal`, or null when no
|
|
144
|
+
* concurrency hold is queued for the group. `group` matches the held row's
|
|
145
|
+
* `environment_id` (workflow-level install concurrency keys on the env id).
|
|
146
|
+
*/
|
|
147
|
+
releaseConcurrencyHold(orgId: string, group: string): Promise<ReleaseSignal | null>;
|
|
124
148
|
/**
|
|
125
149
|
* Expire overdue pending runs. Called by the stale detector.
|
|
126
150
|
* Sets status to 'expired' and resolved_at to now() for all
|
package/dist/index.js
CHANGED
|
@@ -264,6 +264,8 @@ export declare const InstallSecretsDecisionReason: {
|
|
|
264
264
|
readonly ProtectionRuleBlock: "protection_rule_block";
|
|
265
265
|
readonly MissingToken: "missing_token";
|
|
266
266
|
readonly MissingInstallEnv: "missing_install_env";
|
|
267
|
+
/** A protection gate held the workflow install (hold / wait / queue). */
|
|
268
|
+
readonly Held: "held";
|
|
267
269
|
};
|
|
268
270
|
export type InstallSecretsDecisionReason = (typeof InstallSecretsDecisionReason)[keyof typeof InstallSecretsDecisionReason];
|
|
269
271
|
/**
|
|
@@ -280,8 +282,8 @@ export type InstallSecretsChannel = (typeof InstallSecretsChannel)[keyof typeof
|
|
|
280
282
|
* Total install-secrets resolution decisions.
|
|
281
283
|
*
|
|
282
284
|
* Labels:
|
|
283
|
-
* - decision: pass | reject
|
|
284
|
-
* - reason: ok (only with `pass`), or one of the reject reasons enumerated in `InstallSecretsDecisionReason`
|
|
285
|
+
* - decision: pass | reject | hold
|
|
286
|
+
* - reason: ok (only with `pass`), held (only with `hold`), or one of the reject reasons enumerated in `InstallSecretsDecisionReason`
|
|
285
287
|
*
|
|
286
288
|
* Pass series are emitted exactly once per resolver call that does any work
|
|
287
289
|
* (workflows declaring neither `registries:` nor `installEnv:` are NOT
|
|
@@ -210,6 +210,54 @@ export interface OrchestratorHooks {
|
|
|
210
210
|
*/
|
|
211
211
|
startupLogMessage: (port: number) => string;
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Normalize a lock job's `needs` array (strings, NeedsEntry objects, or
|
|
215
|
+
* NeedsGroupEntry objects) to the set of upstream BASE job names. Group entries
|
|
216
|
+
* are skipped here — group fan-in is resolved separately by the scheduler.
|
|
217
|
+
*/
|
|
218
|
+
export declare function upstreamBaseNamesFromNeeds(needs: unknown): string[];
|
|
219
|
+
/**
|
|
220
|
+
* Build the downstream `upstreamJobOutputs` map keyed by BASE name. A base name
|
|
221
|
+
* that fanned into matrix children (rows with `matrix_values`) gets the
|
|
222
|
+
* `{ byMatrix, merged }` envelope; a single non-fanned row keeps the flat shape.
|
|
223
|
+
* Returns undefined when no upstream produced outputs.
|
|
224
|
+
*/
|
|
225
|
+
export declare function buildUpstreamOutputsByBase(baseNames: string[], rows: Array<{
|
|
226
|
+
job_name: string;
|
|
227
|
+
outputs: unknown;
|
|
228
|
+
matrix_values: unknown;
|
|
229
|
+
}>): Record<string, Record<string, unknown>> | undefined;
|
|
230
|
+
/**
|
|
231
|
+
* Group an upstream's child rows into the matrix outputs envelope
|
|
232
|
+
* `{ byMatrix: { '<suffix>': outputs }, merged: <last-write-wins> }`. The suffix
|
|
233
|
+
* is the text inside the `(...)` of each expanded child name; children are
|
|
234
|
+
* merged in name order (deterministic, matching dispatch order).
|
|
235
|
+
*/
|
|
236
|
+
export declare function buildMatrixOutputsEnvelope(baseName: string, children: Array<{
|
|
237
|
+
job_name: string;
|
|
238
|
+
parsed: Record<string, unknown>;
|
|
239
|
+
}>): {
|
|
240
|
+
byMatrix: Record<string, Record<string, unknown>>;
|
|
241
|
+
merged: Record<string, unknown>;
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Merge plain `outputs` and decrypted `secret outputs` from upstream jobs
|
|
245
|
+
* into the dispatch envelope's `secrets` + `upstreamJobOutputs` fields.
|
|
246
|
+
*
|
|
247
|
+
* For a fanned (matrix) upstream, the downstream sees a keyed envelope
|
|
248
|
+
* `{ byMatrix: { '<suffix>': outputs }, merged: <last-write-wins> }` under the
|
|
249
|
+
* BASE name; a non-fanned upstream keeps the flat outputs shape.
|
|
250
|
+
*
|
|
251
|
+
* The needs-aware scheduler guarantees upstreams are
|
|
252
|
+
* terminal before the downstream is dispatched, so the lookups below
|
|
253
|
+
* always find final values. Errors are swallowed (warn-logged) so a flaky
|
|
254
|
+
* upstream-outputs read doesn't block dispatch — the agent will still get
|
|
255
|
+
* its declared secrets, just without the merged upstream additions.
|
|
256
|
+
*/
|
|
257
|
+
export declare function mergeUpstreamOutputs(db: Kysely<Database>, runId: string, jobName: string, needs: unknown, dispatchSecrets: Record<string, string> | undefined, secretKey: string): Promise<{
|
|
258
|
+
mergedSecrets: Record<string, string> | undefined;
|
|
259
|
+
upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined;
|
|
260
|
+
}>;
|
|
213
261
|
export declare function bootstrapOrchestrator(config: AppConfig, hooks: OrchestratorHooks, options?: {
|
|
214
262
|
otelSdk?: {
|
|
215
263
|
shutdown(): Promise<void>;
|
|
@@ -91,6 +91,23 @@ export interface WorkflowDispatchContext {
|
|
|
91
91
|
export interface DispatchMatchedWorkflowResult {
|
|
92
92
|
/** Number of jobs successfully dispatched (non-rejected). */
|
|
93
93
|
dispatchedJobCount: number;
|
|
94
|
+
/** True when the workflow install gate paused the dispatch (held run). */
|
|
95
|
+
held?: boolean;
|
|
96
|
+
}
|
|
97
|
+
/** Options controlling a (re-)dispatch of a matched workflow. */
|
|
98
|
+
export interface DispatchMatchedWorkflowOptions {
|
|
99
|
+
/**
|
|
100
|
+
* Resume path: skip the workflow install protection gate (already satisfied)
|
|
101
|
+
* so secrets resolve directly and the dispatch flows into job dispatch.
|
|
102
|
+
*/
|
|
103
|
+
skipInstallProtectionGate?: boolean;
|
|
104
|
+
/** The released held-run id being resumed (for logging / correlation). */
|
|
105
|
+
reuseHeldRunId?: string;
|
|
106
|
+
/**
|
|
107
|
+
* The run id whose `held` execution_runs row should be reused (flipped to
|
|
108
|
+
* pending) instead of inserting a fresh row.
|
|
109
|
+
*/
|
|
110
|
+
reuseRunId?: string;
|
|
94
111
|
}
|
|
95
112
|
/**
|
|
96
113
|
* Dispatch a single matched workflow.
|
|
@@ -107,5 +124,5 @@ export interface DispatchMatchedWorkflowResult {
|
|
|
107
124
|
* I. deferred init dispatch (fire-and-forget per job)
|
|
108
125
|
* J. deferred dynamic dispatch (fire-and-forget per dynamic entry)
|
|
109
126
|
*/
|
|
110
|
-
export declare function dispatchMatchedWorkflow(ctx: WorkflowDispatchContext): Promise<DispatchMatchedWorkflowResult>;
|
|
127
|
+
export declare function dispatchMatchedWorkflow(ctx: WorkflowDispatchContext, opts?: DispatchMatchedWorkflowOptions): Promise<DispatchMatchedWorkflowResult>;
|
|
111
128
|
//# sourceMappingURL=dispatch-matched-workflow.d.ts.map
|
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* 1. Parses every qualified ref and groups by environment name.
|
|
10
10
|
* 2. Fires the per-environment protection-rule pipeline once per unique env
|
|
11
|
-
* (branch / trust / concurrency / reviewer / wait-timer).
|
|
12
|
-
* result
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* (branch / trust / concurrency / reviewer / wait-timer). A `reject`
|
|
12
|
+
* result fails the whole workflow dispatch with a clear reason; a
|
|
13
|
+
* `hold` / `wait` / `queue` result returns a structured `hold` decision
|
|
14
|
+
* so the caller can pause the workflow dispatch as a workflow-scoped held
|
|
15
|
+
* run and resume it when the gate clears. On the resume path the caller
|
|
16
|
+
* sets `skipProtectionGate` so the gate (already satisfied) is bypassed
|
|
17
|
+
* and secrets are resolved directly.
|
|
15
18
|
* 3. Resolves secrets per environment via `secretResolver.resolveForJob`
|
|
16
19
|
* (which writes its own audit log lines).
|
|
17
20
|
* 4. Validates each registry URL scheme: HTTPS always allowed; `http://`
|
|
@@ -26,7 +29,7 @@
|
|
|
26
29
|
* Pure helper: no imports of the dispatch giant. Tested in isolation in
|
|
27
30
|
* `install-secrets-resolver.test.ts`.
|
|
28
31
|
*/
|
|
29
|
-
import type { LockRegistry } from '@kici-dev/engine';
|
|
32
|
+
import type { ApproverClause, LockRegistry } from '@kici-dev/engine';
|
|
30
33
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
31
34
|
import type { SecretResolver } from '../secrets/secret-resolver.js';
|
|
32
35
|
import type { EnvironmentStore } from '../environments/environment-store.js';
|
|
@@ -47,6 +50,18 @@ export interface ResolveInstallSecretsArgs {
|
|
|
47
50
|
environmentStore: EnvironmentStore | undefined;
|
|
48
51
|
secretResolver: SecretResolver | undefined;
|
|
49
52
|
protectionContext: JobDispatchContext;
|
|
53
|
+
/**
|
|
54
|
+
* Resume path: skip the protection-rule gate (already satisfied) and resolve
|
|
55
|
+
* secrets directly. The untrusted-contributor strip still runs first.
|
|
56
|
+
*/
|
|
57
|
+
skipProtectionGate?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Normalized requirement carried on a `hold` decision. */
|
|
60
|
+
export interface InstallHoldRequirement {
|
|
61
|
+
clauses: ApproverClause[];
|
|
62
|
+
/** ISO timestamp after which the hold expires. */
|
|
63
|
+
expiresAt: string;
|
|
64
|
+
reason: string;
|
|
50
65
|
}
|
|
51
66
|
export type ResolveInstallSecretsResult = {
|
|
52
67
|
decision: 'pass';
|
|
@@ -56,6 +71,18 @@ export type ResolveInstallSecretsResult = {
|
|
|
56
71
|
} | {
|
|
57
72
|
decision: 'reject';
|
|
58
73
|
reason: string;
|
|
74
|
+
} | {
|
|
75
|
+
decision: 'hold';
|
|
76
|
+
/** The gate action that paused the dispatch. */
|
|
77
|
+
action: 'hold' | 'wait' | 'queue';
|
|
78
|
+
/** The environment whose install gate held. */
|
|
79
|
+
envName: string;
|
|
80
|
+
/** Resolved environment id (for the held row). */
|
|
81
|
+
environmentId: string;
|
|
82
|
+
/** Discriminates the release trigger: 'reviewer' | 'wait_timer' | 'concurrency' | 'security'. */
|
|
83
|
+
holdType: string;
|
|
84
|
+
queueType: 'environment' | 'security';
|
|
85
|
+
requirement: InstallHoldRequirement;
|
|
59
86
|
};
|
|
60
87
|
/** Parse `<environment>:<secret-name>`. Returns null on malformed input. */
|
|
61
88
|
export declare function parseQualifiedSecretRef(ref: string): {
|
|
@@ -19,25 +19,28 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import type { Kysely } from 'kysely';
|
|
21
21
|
import type { Database } from '../db/types.js';
|
|
22
|
-
import type {
|
|
22
|
+
import type { MaterializedJob } from '@kici-dev/engine';
|
|
23
23
|
/** Result of evaluating downstream jobs after an upstream completes. */
|
|
24
24
|
export interface SchedulerResult {
|
|
25
25
|
jobName: string;
|
|
26
26
|
action: 'dispatch' | 'skip';
|
|
27
27
|
reason?: string;
|
|
28
28
|
}
|
|
29
|
-
/** Minimal lock job shape needed by insertEdgesForRun. */
|
|
30
|
-
interface LockJobLike {
|
|
31
|
-
readonly name: string;
|
|
32
|
-
readonly needs: readonly (string | NeedsEntry | NeedsGroupEntry)[];
|
|
33
|
-
readonly dependsOnGroups?: readonly string[];
|
|
34
|
-
}
|
|
35
29
|
/**
|
|
36
30
|
* Insert dependency edges at run start for static-to-static needs.
|
|
31
|
+
*
|
|
32
|
+
* Operates on materialized jobs (matrix-expanded children) and an expansion map
|
|
33
|
+
* (base job name -> the expanded child names). An upstream reference to a base
|
|
34
|
+
* name `test` that fanned into `test (a)` / `test (b)` produces one edge per
|
|
35
|
+
* child, so a downstream `needs: ['test']` waits for ALL children to terminate.
|
|
36
|
+
* Each materialized child likewise inherits the full upstream edge set under its
|
|
37
|
+
* own expanded name.
|
|
38
|
+
*
|
|
37
39
|
* NeedsGroupEntry items are skipped (resolved later via resolveGroupEdges).
|
|
38
|
-
* Root jobs (no needs, no dependsOnGroups) are marked needs_satisfied=true
|
|
40
|
+
* Root jobs (no needs, no dependsOnGroups) are marked needs_satisfied=true,
|
|
41
|
+
* keyed by expanded name.
|
|
39
42
|
*/
|
|
40
|
-
export declare function insertEdgesForRun(db: Kysely<Database>, runId: string,
|
|
43
|
+
export declare function insertEdgesForRun(db: Kysely<Database>, runId: string, jobs: readonly MaterializedJob[], expansionMap: ReadonlyMap<string, readonly string[]>): Promise<void>;
|
|
41
44
|
/**
|
|
42
45
|
* Resolve dynamic group edges after eval completion.
|
|
43
46
|
*
|
|
@@ -81,5 +84,4 @@ export declare function checkSchedulerInvariant(db: Kysely<Database>, runId: str
|
|
|
81
84
|
* due to failure propagation. Only follows edges where if_failed='skip'.
|
|
82
85
|
*/
|
|
83
86
|
export declare function getFailurePropagationTargets(db: Kysely<Database>, runId: string, failedJobName: string): Promise<string[]>;
|
|
84
|
-
export {};
|
|
85
87
|
//# sourceMappingURL=needs-scheduler.d.ts.map
|