@kici-dev/orchestrator 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts +24 -0
- package/dist/audit/access-log.d.ts +4 -0
- package/dist/cancel/cancel-run.d.ts +2 -0
- package/dist/cli/api-client.d.ts +2 -0
- package/dist/cli/commands/attestations-list.d.ts +49 -0
- package/dist/cli/commands/attestations-retry.d.ts +28 -0
- package/dist/cli/commands/attestations.d.ts +2 -1
- package/dist/cli/commands/cluster.d.ts +3 -0
- package/dist/cli/local-github-ingress-url.d.ts +8 -0
- package/dist/cli.js +1021 -404
- package/dist/cluster/cluster-identity.d.ts +11 -0
- package/dist/cluster/reconcile-identity.d.ts +50 -0
- package/dist/cold-store/load-access-log-range.d.ts +4 -0
- package/dist/config.d.ts +2 -0
- package/dist/dashboard/attestation-filters.d.ts +62 -1
- package/dist/dashboard/handler.d.ts +24 -4
- package/dist/db/migrations/061_execution_runs_environment_id.d.ts +4 -0
- package/dist/db/migrations/062_execution_runs_agent_label.d.ts +4 -0
- package/dist/db/migrations/063_access_log_agent_label_index.d.ts +10 -0
- package/dist/db/migrations/064_execution_jobs_skipped_environments.d.ts +17 -0
- package/dist/db/migrations/065_pending_attestations.d.ts +22 -0
- package/dist/db/migrations/066_pending_attestations_rejected.d.ts +13 -0
- package/dist/db/types.d.ts +58 -0
- package/dist/environments/protection/satisfiability.d.ts +14 -9
- package/dist/events/circuit-breaker.d.ts +8 -5
- package/dist/events/event-router.d.ts +12 -4
- package/dist/events/event-store.d.ts +21 -3
- package/dist/events/trust-store.d.ts +6 -1
- package/dist/events/types.d.ts +6 -1
- package/dist/helpers/rate-limiter.d.ts +17 -2
- package/dist/index.js +2 -1
- package/dist/metrics/prometheus.d.ts +6 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +6 -0
- package/dist/pipeline/manual-schedule.d.ts +1 -1
- package/dist/pipeline/process-webhook.d.ts +14 -1
- package/dist/pipeline/rerun.d.ts +1 -1
- package/dist/pipeline/test-pipeline.d.ts +6 -0
- package/dist/provenance/attestation-retrier.d.ts +92 -0
- package/dist/provenance/backfill-run.d.ts +41 -0
- package/dist/provenance/pending-attestations-repo.d.ts +64 -0
- package/dist/provenance/verify-at-ingest.d.ts +10 -3
- package/dist/reporting/execution-tracker.d.ts +15 -3
- package/dist/reporting/run-aggregator.d.ts +5 -1
- package/dist/routes/admin-access-log.d.ts +2 -1
- package/dist/routes/admin.d.ts +14 -0
- package/dist/routes/github-webhook.d.ts +37 -0
- package/dist/routes/health.d.ts +9 -0
- package/dist/routes/webhooks.d.ts +2 -4
- package/dist/server.js +3462 -1886
- package/dist/sources/source-store.d.ts +6 -0
- package/dist/stale-detector/workflow-deadline-detector.d.ts +25 -0
- package/dist/standalone.js +19335 -18258
- package/dist/webhook/dedup.d.ts +14 -0
- package/dist/ws/agent-handler.d.ts +23 -0
- package/dist/ws/oidc-token-relay.d.ts +31 -21
- package/dist/ws/orch-rpc.d.ts +9 -0
- package/dist/ws/platform-client.d.ts +18 -1
- package/dist/ws/test-relay-handlers.d.ts +2 -0
- package/installer-image-digests.json +3 -3
- package/package.json +5 -5
- package/sbom.spdx.json +55 -50
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import path, { dirname, join, resolve } from "node:path";
|
|
|
6
6
|
import * as fs$1 from "node:fs";
|
|
7
7
|
import fs, { chmodSync, closeSync, createReadStream, createWriteStream, existsSync, mkdtempSync, openSync, promises, readFileSync, realpathSync, statSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
8
8
|
import { Command } from "commander";
|
|
9
|
-
import { BaseColdStore, ChunkLru, addLogsToArchive, chunkObjectKey, clearDispatchQueueDirect, computeChunkId, computeMigrationsHash, createDb, createDbRole, createEnvironmentTemplateDirect, createLogger, createPool, createReadOnlyDbUser, createS3Client, decodeChunk, deleteEnvironmentDirect, dropAndCreateDatabase, emitKiciEventDirect, encodeKeySegment, ensureDatabase, formatBytes, formatUptime, isSchemaCurrent, listEnvironmentsDirect, listExecutionRunsDirect, listQueueDirect, listRegistrationsDirect, maskDatabaseUrl, parseDatabaseUrl, parseManifest, prunePeerCredentialsDirect, purgeScopedSecretsDirect, purgeSecretBackendsDirect, purgeStaleExecutionDirect, purgeStaleSourcesDirect, registerWorkflowManualDirect, resetRaftStateDirect, seedEnvironmentBindingDirect, seedEnvironmentDirect, setEnvironmentPolicyDirect, setEnvironmentSecretDirect, sha256, showEnvironmentDirect, showExecutionRunDirect, showQueueEntryDirect, showRegistrationDirect, storeMigrationContentHash, tablePrefix, toErrorMessage } from "@kici-dev/shared";
|
|
9
|
+
import { BaseColdStore, ChunkLru, addLogsToArchive, chunkObjectKey, clearDispatchQueueDirect, computeChunkId, computeMigrationsHash, createDb, createDbRole, createEnvironmentTemplateDirect, createLogger, createPool, createReadOnlyDbUser, createS3Client, decodeChunk, deleteEnvironmentDirect, dropAndCreateDatabase, emitKiciEventDirect, encodeKeySegment, ensureDatabase, formatBytes, formatUptime, isSchemaCurrent, listEnvironmentsDirect, listExecutionRunsDirect, listQueueDirect, listRegistrationsDirect, maskDatabaseUrl, parseDatabaseUrl, parseManifest, prunePeerCredentialsDirect, purgeEnvironmentsDirect, purgeScopedSecretsDirect, purgeSecretBackendsDirect, purgeStaleExecutionDirect, purgeStaleSourcesDirect, registerWorkflowManualDirect, resetRaftStateDirect, seedEnvironmentBindingDirect, seedEnvironmentDirect, setEnvironmentPolicyDirect, setEnvironmentSecretDirect, sha256, showEnvironmentDirect, showExecutionRunDirect, showQueueEntryDirect, showRegistrationDirect, storeMigrationContentHash, tablePrefix, toErrorMessage } from "@kici-dev/shared";
|
|
10
10
|
import crypto, { createDecipheriv, createHash, createHmac, hkdfSync, randomBytes, randomUUID } from "node:crypto";
|
|
11
11
|
import { createInterface } from "node:readline";
|
|
12
12
|
import { KICI_AGENT_ENV_PREFIX, KNOWN_ROLES, PRIVILEGED_ROOT_LABEL, ScalerBackendType, ScalerEventType, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, WS_MAX_PAYLOAD_BYTES, accessLogWarmSqlCase, attestationVerifyStatusSchema, getAccessLogColdDays, getSecretAuditLogColdDays, matcherSatisfiedBy, minAccessLogWarmDays, minSecretAuditLogWarmDays, parseHostPropertyAssignments, scalerAgentLabels, secretAuditLogWarmSqlCase } from "@kici-dev/engine";
|
|
@@ -28,6 +28,7 @@ import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCom
|
|
|
28
28
|
import { Upload } from "@aws-sdk/lib-storage";
|
|
29
29
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
30
30
|
import { verifyKiciBundle } from "@kici-dev/engine/provenance/verify";
|
|
31
|
+
import { decodeProtectedHeader } from "jose";
|
|
31
32
|
import { ZipArchive } from "archiver";
|
|
32
33
|
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
33
34
|
import JSZip from "jszip";
|
|
@@ -35,10 +36,18 @@ import { pipeline } from "node:stream/promises";
|
|
|
35
36
|
import "pg";
|
|
36
37
|
import { DASHBOARD_WRITE_OPERATIONS, DASHBOARD_WRITE_OPERATIONS_BY_NAME, DashboardWriteCategory, DashboardWriteSensitivity } from "@kici-dev/engine/protocol/dashboard-write-operations";
|
|
37
38
|
import { CLUSTER_NAME_FORMAT_MESSAGE } from "@kici-dev/engine/protocol/cluster-name";
|
|
39
|
+
import { runIdempotentStep } from "@kici-dev/shared/idempotency";
|
|
38
40
|
import { PassThrough } from "node:stream";
|
|
39
41
|
import Docker from "dockerode";
|
|
40
42
|
var __defProp = Object.defineProperty;
|
|
41
|
-
var __esmMin = (fn, res) => () =>
|
|
43
|
+
var __esmMin = (fn, res, err) => () => {
|
|
44
|
+
if (err) throw err[0];
|
|
45
|
+
try {
|
|
46
|
+
return fn && (res = fn(fn = 0)), res;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
throw err = [e], e;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
42
51
|
var __exportAll = (all, no_symbols) => {
|
|
43
52
|
let target = {};
|
|
44
53
|
for (var name in all) __defProp(target, name, {
|
|
@@ -48,7 +57,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
48
57
|
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
49
58
|
return target;
|
|
50
59
|
};
|
|
51
|
-
var __require = /*
|
|
60
|
+
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
52
61
|
//#endregion
|
|
53
62
|
//#region src/cli/api-client.ts
|
|
54
63
|
/**
|
|
@@ -438,6 +447,8 @@ var AdminApiClient = class {
|
|
|
438
447
|
if (opts?.from) params.set("from", opts.from);
|
|
439
448
|
if (opts?.to) params.set("to", opts.to);
|
|
440
449
|
if (opts?.q) params.set("q", opts.q);
|
|
450
|
+
if (opts?.agentLabel) params.set("agentLabel", opts.agentLabel);
|
|
451
|
+
if (opts?.agentOnly) params.set("agentOnly", "true");
|
|
441
452
|
if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
|
|
442
453
|
if (opts?.cursor) params.set("cursor", opts.cursor);
|
|
443
454
|
const qs = params.toString();
|
|
@@ -817,7 +828,7 @@ function registerAgentCommands(program, getClient) {
|
|
|
817
828
|
if (opts.privilegedRoot) mandatorySet.add(PRIVILEGED_ROOT_LABEL);
|
|
818
829
|
const mandatoryLabels = mandatorySet.size > 0 ? [...mandatorySet] : void 0;
|
|
819
830
|
const baseLabels = opts.labels ? opts.labels.split(",").map((l) => l.trim()) : [];
|
|
820
|
-
const labelSet = new Set([...baseLabels, ...mandatoryLabels ?? []]);
|
|
831
|
+
const labelSet = /* @__PURE__ */ new Set([...baseLabels, ...mandatoryLabels ?? []]);
|
|
821
832
|
const labels = labelSet.size > 0 ? [...labelSet] : void 0;
|
|
822
833
|
const result = await getClient().createAgentToken({
|
|
823
834
|
labels,
|
|
@@ -1579,8 +1590,8 @@ var init_client = __esmMin((() => {
|
|
|
1579
1590
|
//#region src/db/migrations/001_initial.ts
|
|
1580
1591
|
init_client();
|
|
1581
1592
|
var _001_initial_exports = /* @__PURE__ */ __exportAll({
|
|
1582
|
-
down: () => down$
|
|
1583
|
-
up: () => up$
|
|
1593
|
+
down: () => down$65,
|
|
1594
|
+
up: () => up$65
|
|
1584
1595
|
});
|
|
1585
1596
|
/**
|
|
1586
1597
|
* Squashed initial migration -- creates the complete Orchestrator database schema.
|
|
@@ -2272,7 +2283,7 @@ const DDL_STATEMENTS = [
|
|
|
2272
2283
|
`ALTER TABLE ONLY public.held_runs
|
|
2273
2284
|
ADD CONSTRAINT held_runs_environment_id_fkey FOREIGN KEY (environment_id) REFERENCES public.environments(id);`
|
|
2274
2285
|
];
|
|
2275
|
-
async function up$
|
|
2286
|
+
async function up$65(db) {
|
|
2276
2287
|
for (const stmt of DDL_STATEMENTS) await sql.raw(stmt).execute(db);
|
|
2277
2288
|
await sql`
|
|
2278
2289
|
INSERT INTO cluster_meta (key, value)
|
|
@@ -2294,7 +2305,7 @@ async function up$59(db) {
|
|
|
2294
2305
|
* Rollback drops everything created above. Uses CASCADE on table drops to cut
|
|
2295
2306
|
* through the FK graph without relying on exact topological order.
|
|
2296
2307
|
*/
|
|
2297
|
-
async function down$
|
|
2308
|
+
async function down$65(db) {
|
|
2298
2309
|
for (const [trig, tbl] of [["source_secrets_change_trigger", "scoped_secrets"], ["sources_change_trigger", "sources"]]) await sql.raw(`DROP TRIGGER IF EXISTS ${trig} ON public.${tbl}`).execute(db);
|
|
2299
2310
|
for (const table of [
|
|
2300
2311
|
"workflow_registrations",
|
|
@@ -2341,8 +2352,8 @@ async function down$59(db) {
|
|
|
2341
2352
|
//#endregion
|
|
2342
2353
|
//#region src/db/migrations/002_config_versions_key_version.ts
|
|
2343
2354
|
var _002_config_versions_key_version_exports = /* @__PURE__ */ __exportAll({
|
|
2344
|
-
down: () => down$
|
|
2345
|
-
up: () => up$
|
|
2355
|
+
down: () => down$64,
|
|
2356
|
+
up: () => up$64
|
|
2346
2357
|
});
|
|
2347
2358
|
/**
|
|
2348
2359
|
* Add key_version column to config_versions so that sensitive-field encryption
|
|
@@ -2356,13 +2367,13 @@ var _002_config_versions_key_version_exports = /* @__PURE__ */ __exportAll({
|
|
|
2356
2367
|
* No index is needed: rotation does a full-table scan; reads are by
|
|
2357
2368
|
* `version` primary key and never filter on `key_version`.
|
|
2358
2369
|
*/
|
|
2359
|
-
async function up$
|
|
2370
|
+
async function up$64(db) {
|
|
2360
2371
|
await sql`
|
|
2361
2372
|
ALTER TABLE public.config_versions
|
|
2362
2373
|
ADD COLUMN key_version integer NOT NULL DEFAULT 1
|
|
2363
2374
|
`.execute(db);
|
|
2364
2375
|
}
|
|
2365
|
-
async function down$
|
|
2376
|
+
async function down$64(db) {
|
|
2366
2377
|
await sql`
|
|
2367
2378
|
ALTER TABLE public.config_versions
|
|
2368
2379
|
DROP COLUMN key_version
|
|
@@ -2371,8 +2382,8 @@ async function down$58(db) {
|
|
|
2371
2382
|
//#endregion
|
|
2372
2383
|
//#region src/db/migrations/003_access_log.ts
|
|
2373
2384
|
var _003_access_log_exports = /* @__PURE__ */ __exportAll({
|
|
2374
|
-
down: () => down$
|
|
2375
|
-
up: () => up$
|
|
2385
|
+
down: () => down$63,
|
|
2386
|
+
up: () => up$63
|
|
2376
2387
|
});
|
|
2377
2388
|
/**
|
|
2378
2389
|
* Access log: one row per read or orchestrator-admin mutation attributable
|
|
@@ -2396,7 +2407,7 @@ var _003_access_log_exports = /* @__PURE__ */ __exportAll({
|
|
|
2396
2407
|
* Retention is TTL-based via expires_at; packages/orchestrator/src/queue/
|
|
2397
2408
|
* cleanup.ts picks up the prune pass.
|
|
2398
2409
|
*/
|
|
2399
|
-
async function up$
|
|
2410
|
+
async function up$63(db) {
|
|
2400
2411
|
await sql`
|
|
2401
2412
|
CREATE TABLE public.access_log (
|
|
2402
2413
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
@@ -2434,28 +2445,28 @@ async function up$57(db) {
|
|
|
2434
2445
|
ON public.access_log (actor_type, actor_id, created_at DESC)
|
|
2435
2446
|
`.execute(db);
|
|
2436
2447
|
}
|
|
2437
|
-
async function down$
|
|
2448
|
+
async function down$63(db) {
|
|
2438
2449
|
await sql`DROP TABLE IF EXISTS public.access_log`.execute(db);
|
|
2439
2450
|
}
|
|
2440
2451
|
//#endregion
|
|
2441
2452
|
//#region src/db/migrations/004_rename_bundle_to_source.ts
|
|
2442
2453
|
var _004_rename_bundle_to_source_exports = /* @__PURE__ */ __exportAll({
|
|
2443
|
-
down: () => down$
|
|
2444
|
-
up: () => up$
|
|
2454
|
+
down: () => down$62,
|
|
2455
|
+
up: () => up$62
|
|
2445
2456
|
});
|
|
2446
|
-
async function up$
|
|
2457
|
+
async function up$62(db) {
|
|
2447
2458
|
await db.schema.alterTable("dispatch_queue").renameColumn("bundle_url", "source_tar_url").execute();
|
|
2448
2459
|
await db.schema.alterTable("dispatch_queue").renameColumn("bundle_hash", "source_tar_hash").execute();
|
|
2449
2460
|
}
|
|
2450
|
-
async function down$
|
|
2461
|
+
async function down$62(db) {
|
|
2451
2462
|
await db.schema.alterTable("dispatch_queue").renameColumn("source_tar_url", "bundle_url").execute();
|
|
2452
2463
|
await db.schema.alterTable("dispatch_queue").renameColumn("source_tar_hash", "bundle_hash").execute();
|
|
2453
2464
|
}
|
|
2454
2465
|
//#endregion
|
|
2455
2466
|
//#region src/db/migrations/005_cold_store_chunk_counter.ts
|
|
2456
2467
|
var _005_cold_store_chunk_counter_exports = /* @__PURE__ */ __exportAll({
|
|
2457
|
-
down: () => down$
|
|
2458
|
-
up: () => up$
|
|
2468
|
+
down: () => down$61,
|
|
2469
|
+
up: () => up$61
|
|
2459
2470
|
});
|
|
2460
2471
|
/**
|
|
2461
2472
|
* Cold-store chunk counter table.
|
|
@@ -2472,7 +2483,7 @@ var _005_cold_store_chunk_counter_exports = /* @__PURE__ */ __exportAll({
|
|
|
2472
2483
|
*
|
|
2473
2484
|
* sections 5 and 8.
|
|
2474
2485
|
*/
|
|
2475
|
-
async function up$
|
|
2486
|
+
async function up$61(db) {
|
|
2476
2487
|
await sql`
|
|
2477
2488
|
CREATE TABLE public.cold_store_chunk_counts (
|
|
2478
2489
|
db TEXT NOT NULL,
|
|
@@ -2490,14 +2501,14 @@ async function up$55(db) {
|
|
|
2490
2501
|
ON public.cold_store_chunk_counts (db, table_name)
|
|
2491
2502
|
`.execute(db);
|
|
2492
2503
|
}
|
|
2493
|
-
async function down$
|
|
2504
|
+
async function down$61(db) {
|
|
2494
2505
|
await sql`DROP TABLE IF EXISTS public.cold_store_chunk_counts`.execute(db);
|
|
2495
2506
|
}
|
|
2496
2507
|
//#endregion
|
|
2497
2508
|
//#region src/db/migrations/006_runs_jobs_steps_archived_at.ts
|
|
2498
2509
|
var _006_runs_jobs_steps_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
2499
|
-
down: () => down$
|
|
2500
|
-
up: () => up$
|
|
2510
|
+
down: () => down$60,
|
|
2511
|
+
up: () => up$60
|
|
2501
2512
|
});
|
|
2502
2513
|
/**
|
|
2503
2514
|
* `execution_runs` / `execution_jobs` / `execution_steps` cold-store
|
|
@@ -2533,7 +2544,7 @@ var _006_runs_jobs_steps_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
|
2533
2544
|
* - `idx_execution_jobs_routing_key_created (routing_key, created_at)`
|
|
2534
2545
|
* - `idx_execution_steps_routing_key_created (routing_key, created_at)`
|
|
2535
2546
|
*/
|
|
2536
|
-
async function up$
|
|
2547
|
+
async function up$60(db) {
|
|
2537
2548
|
await sql`
|
|
2538
2549
|
ALTER TABLE public.execution_runs
|
|
2539
2550
|
ADD COLUMN archived_at TIMESTAMPTZ NULL,
|
|
@@ -2578,7 +2589,7 @@ async function up$54(db) {
|
|
|
2578
2589
|
ON public.execution_steps (routing_key, created_at)
|
|
2579
2590
|
`.execute(db);
|
|
2580
2591
|
}
|
|
2581
|
-
async function down$
|
|
2592
|
+
async function down$60(db) {
|
|
2582
2593
|
await sql`DROP INDEX IF EXISTS public.idx_execution_steps_routing_key_created`.execute(db);
|
|
2583
2594
|
await sql`
|
|
2584
2595
|
ALTER TABLE public.execution_steps
|
|
@@ -2603,8 +2614,8 @@ async function down$54(db) {
|
|
|
2603
2614
|
//#endregion
|
|
2604
2615
|
//#region src/db/migrations/007_audit_logs_archived_at.ts
|
|
2605
2616
|
var _007_audit_logs_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
2606
|
-
down: () => down$
|
|
2607
|
-
up: () => up$
|
|
2617
|
+
down: () => down$59,
|
|
2618
|
+
up: () => up$59
|
|
2608
2619
|
});
|
|
2609
2620
|
/**
|
|
2610
2621
|
* `secret_audit_log` and `access_log` cold-store schema additions, plus
|
|
@@ -2644,7 +2655,7 @@ var _007_audit_logs_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
|
2644
2655
|
* `down()` would not have meaningful retention bounds. Acceptable for
|
|
2645
2656
|
* staging.
|
|
2646
2657
|
*/
|
|
2647
|
-
async function up$
|
|
2658
|
+
async function up$59(db) {
|
|
2648
2659
|
await sql`
|
|
2649
2660
|
ALTER TABLE public.secret_audit_log
|
|
2650
2661
|
ADD COLUMN archived_at TIMESTAMPTZ NULL,
|
|
@@ -2665,7 +2676,7 @@ async function up$53(db) {
|
|
|
2665
2676
|
DROP COLUMN IF EXISTS expires_at
|
|
2666
2677
|
`.execute(db);
|
|
2667
2678
|
}
|
|
2668
|
-
async function down$
|
|
2679
|
+
async function down$59(db) {
|
|
2669
2680
|
await sql`
|
|
2670
2681
|
ALTER TABLE public.access_log
|
|
2671
2682
|
ADD COLUMN expires_at TIMESTAMPTZ NOT NULL DEFAULT (now() + INTERVAL '90 days')
|
|
@@ -2693,8 +2704,8 @@ async function down$53(db) {
|
|
|
2693
2704
|
//#endregion
|
|
2694
2705
|
//#region src/db/migrations/008_event_log_archived_at.ts
|
|
2695
2706
|
var _008_event_log_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
2696
|
-
down: () => down$
|
|
2697
|
-
up: () => up$
|
|
2707
|
+
down: () => down$58,
|
|
2708
|
+
up: () => up$58
|
|
2698
2709
|
});
|
|
2699
2710
|
/**
|
|
2700
2711
|
* `event_log` cold-store schema additions plus removal of the
|
|
@@ -2732,7 +2743,7 @@ var _008_event_log_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
|
2732
2743
|
* — best effort; rows inserted between `up()` and a hypothetical
|
|
2733
2744
|
* `down()` would not have meaningful retention bounds.
|
|
2734
2745
|
*/
|
|
2735
|
-
async function up$
|
|
2746
|
+
async function up$58(db) {
|
|
2736
2747
|
await sql`
|
|
2737
2748
|
ALTER TABLE public.event_log
|
|
2738
2749
|
ADD COLUMN archived_at TIMESTAMPTZ NULL,
|
|
@@ -2748,7 +2759,7 @@ async function up$52(db) {
|
|
|
2748
2759
|
DROP COLUMN IF EXISTS expires_at
|
|
2749
2760
|
`.execute(db);
|
|
2750
2761
|
}
|
|
2751
|
-
async function down$
|
|
2762
|
+
async function down$58(db) {
|
|
2752
2763
|
await sql`
|
|
2753
2764
|
ALTER TABLE public.event_log
|
|
2754
2765
|
ADD COLUMN expires_at TIMESTAMPTZ NOT NULL DEFAULT (now() + INTERVAL '30 days')
|
|
@@ -2767,8 +2778,8 @@ async function down$52(db) {
|
|
|
2767
2778
|
//#endregion
|
|
2768
2779
|
//#region src/db/migrations/009_access_log_trigram.ts
|
|
2769
2780
|
var _009_access_log_trigram_exports = /* @__PURE__ */ __exportAll({
|
|
2770
|
-
down: () => down$
|
|
2771
|
-
up: () => up$
|
|
2781
|
+
down: () => down$57,
|
|
2782
|
+
up: () => up$57
|
|
2772
2783
|
});
|
|
2773
2784
|
/**
|
|
2774
2785
|
* Trigram (pg_trgm) index on access_log.error_message for the federated
|
|
@@ -2781,7 +2792,7 @@ var _009_access_log_trigram_exports = /* @__PURE__ */ __exportAll({
|
|
|
2781
2792
|
* EXISTS` are both safe to re-run. No CONCURRENTLY because Kysely runs
|
|
2782
2793
|
* migrations inside a transaction; the lock is brief on a sampled table.
|
|
2783
2794
|
*/
|
|
2784
|
-
async function up$
|
|
2795
|
+
async function up$57(db) {
|
|
2785
2796
|
await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`.execute(db);
|
|
2786
2797
|
await sql`
|
|
2787
2798
|
CREATE INDEX IF NOT EXISTS access_log_error_message_trgm_idx
|
|
@@ -2790,14 +2801,14 @@ async function up$51(db) {
|
|
|
2790
2801
|
WHERE error_message IS NOT NULL
|
|
2791
2802
|
`.execute(db);
|
|
2792
2803
|
}
|
|
2793
|
-
async function down$
|
|
2804
|
+
async function down$57(db) {
|
|
2794
2805
|
await sql`DROP INDEX IF EXISTS public.access_log_error_message_trgm_idx`.execute(db);
|
|
2795
2806
|
}
|
|
2796
2807
|
//#endregion
|
|
2797
2808
|
//#region src/db/migrations/010_cold_store_chunks.ts
|
|
2798
2809
|
var _010_cold_store_chunks_exports = /* @__PURE__ */ __exportAll({
|
|
2799
|
-
down: () => down$
|
|
2800
|
-
up: () => up$
|
|
2810
|
+
down: () => down$56,
|
|
2811
|
+
up: () => up$56
|
|
2801
2812
|
});
|
|
2802
2813
|
/**
|
|
2803
2814
|
* Cold-store chunk index — Phase 2 (cold-store purge).
|
|
@@ -2829,7 +2840,7 @@ var _010_cold_store_chunks_exports = /* @__PURE__ */ __exportAll({
|
|
|
2829
2840
|
* forever. Adapters that don't opt into per-bucket archival via
|
|
2830
2841
|
* `coldTtlDays` don't insert here either.
|
|
2831
2842
|
*/
|
|
2832
|
-
async function up$
|
|
2843
|
+
async function up$56(db) {
|
|
2833
2844
|
await sql`
|
|
2834
2845
|
CREATE TABLE public.cold_store_chunks (
|
|
2835
2846
|
db TEXT NOT NULL,
|
|
@@ -2856,14 +2867,14 @@ async function up$50(db) {
|
|
|
2856
2867
|
ON public.cold_store_chunks (db, table_name, tenant_id, archived_at DESC)
|
|
2857
2868
|
`.execute(db);
|
|
2858
2869
|
}
|
|
2859
|
-
async function down$
|
|
2870
|
+
async function down$56(db) {
|
|
2860
2871
|
await sql`DROP TABLE IF EXISTS public.cold_store_chunks`.execute(db);
|
|
2861
2872
|
}
|
|
2862
2873
|
//#endregion
|
|
2863
2874
|
//#region src/db/migrations/011_drop_source_secrets_notify.ts
|
|
2864
2875
|
var _011_drop_source_secrets_notify_exports = /* @__PURE__ */ __exportAll({
|
|
2865
|
-
down: () => down$
|
|
2866
|
-
up: () => up$
|
|
2876
|
+
down: () => down$55,
|
|
2877
|
+
up: () => up$55
|
|
2867
2878
|
});
|
|
2868
2879
|
/**
|
|
2869
2880
|
* Drop the `source_secrets_change_trigger` and the
|
|
@@ -2882,11 +2893,11 @@ var _011_drop_source_secrets_notify_exports = /* @__PURE__ */ __exportAll({
|
|
|
2882
2893
|
* in `001_initial.ts`. They wake up no consumer until the `WebhookSecretManager`
|
|
2883
2894
|
* is restored, so this migration is safe to roll back.
|
|
2884
2895
|
*/
|
|
2885
|
-
async function up$
|
|
2896
|
+
async function up$55(db) {
|
|
2886
2897
|
await sql`DROP TRIGGER IF EXISTS source_secrets_change_trigger ON public.scoped_secrets`.execute(db);
|
|
2887
2898
|
await sql`DROP FUNCTION IF EXISTS public.notify_source_secrets_change() CASCADE`.execute(db);
|
|
2888
2899
|
}
|
|
2889
|
-
async function down$
|
|
2900
|
+
async function down$55(db) {
|
|
2890
2901
|
await sql`
|
|
2891
2902
|
CREATE OR REPLACE FUNCTION public.notify_source_secrets_change() RETURNS trigger
|
|
2892
2903
|
LANGUAGE plpgsql
|
|
@@ -2925,8 +2936,8 @@ async function down$49(db) {
|
|
|
2925
2936
|
//#endregion
|
|
2926
2937
|
//#region src/db/migrations/012_peer_credentials_active_uniq.ts
|
|
2927
2938
|
var _012_peer_credentials_active_uniq_exports = /* @__PURE__ */ __exportAll({
|
|
2928
|
-
down: () => down$
|
|
2929
|
-
up: () => up$
|
|
2939
|
+
down: () => down$54,
|
|
2940
|
+
up: () => up$54
|
|
2930
2941
|
});
|
|
2931
2942
|
/**
|
|
2932
2943
|
* Add a partial unique index on `peer_credentials (instance_id) WHERE
|
|
@@ -2952,7 +2963,7 @@ var _012_peer_credentials_active_uniq_exports = /* @__PURE__ */ __exportAll({
|
|
|
2952
2963
|
* `down()` only drops the index; it does NOT undo the dedupe (there's no
|
|
2953
2964
|
* safe way to recreate revoked rows, and the dedupe is monotonic).
|
|
2954
2965
|
*/
|
|
2955
|
-
async function up$
|
|
2966
|
+
async function up$54(db) {
|
|
2956
2967
|
await sql`
|
|
2957
2968
|
UPDATE public.peer_credentials
|
|
2958
2969
|
SET revoked_at = NOW()
|
|
@@ -2970,14 +2981,14 @@ async function up$48(db) {
|
|
|
2970
2981
|
WHERE revoked_at IS NULL
|
|
2971
2982
|
`.execute(db);
|
|
2972
2983
|
}
|
|
2973
|
-
async function down$
|
|
2984
|
+
async function down$54(db) {
|
|
2974
2985
|
await sql`DROP INDEX IF EXISTS public.peer_credentials_active_uniq`.execute(db);
|
|
2975
2986
|
}
|
|
2976
2987
|
//#endregion
|
|
2977
2988
|
//#region src/db/migrations/013_execution_log_bytes.ts
|
|
2978
2989
|
var _013_execution_log_bytes_exports = /* @__PURE__ */ __exportAll({
|
|
2979
|
-
down: () => down$
|
|
2980
|
-
up: () => up$
|
|
2990
|
+
down: () => down$53,
|
|
2991
|
+
up: () => up$53
|
|
2981
2992
|
});
|
|
2982
2993
|
/**
|
|
2983
2994
|
* Add `log_bytes BIGINT NOT NULL DEFAULT 0` columns to `execution_runs` and
|
|
@@ -2996,7 +3007,7 @@ var _013_execution_log_bytes_exports = /* @__PURE__ */ __exportAll({
|
|
|
2996
3007
|
*
|
|
2997
3008
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`).
|
|
2998
3009
|
*/
|
|
2999
|
-
async function up$
|
|
3010
|
+
async function up$53(db) {
|
|
3000
3011
|
await sql`
|
|
3001
3012
|
ALTER TABLE public.execution_runs
|
|
3002
3013
|
ADD COLUMN IF NOT EXISTS log_bytes BIGINT NOT NULL DEFAULT 0
|
|
@@ -3006,15 +3017,15 @@ async function up$47(db) {
|
|
|
3006
3017
|
ADD COLUMN IF NOT EXISTS log_bytes BIGINT NOT NULL DEFAULT 0
|
|
3007
3018
|
`.execute(db);
|
|
3008
3019
|
}
|
|
3009
|
-
async function down$
|
|
3020
|
+
async function down$53(db) {
|
|
3010
3021
|
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS log_bytes`.execute(db);
|
|
3011
3022
|
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS log_bytes`.execute(db);
|
|
3012
3023
|
}
|
|
3013
3024
|
//#endregion
|
|
3014
3025
|
//#region src/db/migrations/014_kici_events_lease_retry.ts
|
|
3015
3026
|
var _014_kici_events_lease_retry_exports = /* @__PURE__ */ __exportAll({
|
|
3016
|
-
down: () => down$
|
|
3017
|
-
up: () => up$
|
|
3027
|
+
down: () => down$52,
|
|
3028
|
+
up: () => up$52
|
|
3018
3029
|
});
|
|
3019
3030
|
/**
|
|
3020
3031
|
* Add lease + retry + DLQ columns to `kici_events` so the EventRouter can
|
|
@@ -3048,7 +3059,7 @@ var _014_kici_events_lease_retry_exports = /* @__PURE__ */ __exportAll({
|
|
|
3048
3059
|
*
|
|
3049
3060
|
* Idempotent (`ADD COLUMN IF NOT EXISTS` + `CREATE INDEX IF NOT EXISTS`).
|
|
3050
3061
|
*/
|
|
3051
|
-
async function up$
|
|
3062
|
+
async function up$52(db) {
|
|
3052
3063
|
await sql`
|
|
3053
3064
|
ALTER TABLE public.kici_events
|
|
3054
3065
|
ADD COLUMN IF NOT EXISTS claimed_at TIMESTAMPTZ,
|
|
@@ -3079,7 +3090,7 @@ async function up$46(db) {
|
|
|
3079
3090
|
WHERE dlq_at IS NOT NULL
|
|
3080
3091
|
`.execute(db);
|
|
3081
3092
|
}
|
|
3082
|
-
async function down$
|
|
3093
|
+
async function down$52(db) {
|
|
3083
3094
|
await sql`DROP INDEX IF EXISTS public.idx_kici_events_dlq`.execute(db);
|
|
3084
3095
|
await sql`DROP INDEX IF EXISTS public.idx_kici_events_lease_expired`.execute(db);
|
|
3085
3096
|
await sql`DROP INDEX IF EXISTS public.idx_kici_events_retry_due`.execute(db);
|
|
@@ -3097,8 +3108,8 @@ async function down$46(db) {
|
|
|
3097
3108
|
//#endregion
|
|
3098
3109
|
//#region src/db/migrations/015_org_settings_customer_scoped.ts
|
|
3099
3110
|
var _015_org_settings_customer_scoped_exports = /* @__PURE__ */ __exportAll({
|
|
3100
|
-
down: () => down$
|
|
3101
|
-
up: () => up$
|
|
3111
|
+
down: () => down$51,
|
|
3112
|
+
up: () => up$51
|
|
3102
3113
|
});
|
|
3103
3114
|
/**
|
|
3104
3115
|
* Org-scope `org_settings` and qualify each glob entry by source.
|
|
@@ -3126,7 +3137,7 @@ var _015_org_settings_customer_scoped_exports = /* @__PURE__ */ __exportAll({
|
|
|
3126
3137
|
* Idempotent: a re-run on an already-migrated DB sees `customer_id` exists
|
|
3127
3138
|
* and the list columns are already jsonb, so it is a no-op.
|
|
3128
3139
|
*/
|
|
3129
|
-
async function up$
|
|
3140
|
+
async function up$51(db) {
|
|
3130
3141
|
if ((await sql`
|
|
3131
3142
|
SELECT EXISTS (
|
|
3132
3143
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3251,7 +3262,7 @@ async function up$45(db) {
|
|
|
3251
3262
|
await sql`DROP TABLE _org_settings_merged`.execute(db);
|
|
3252
3263
|
await sql`DROP TABLE _org_settings_stage`.execute(db);
|
|
3253
3264
|
}
|
|
3254
|
-
async function down$
|
|
3265
|
+
async function down$51(db) {
|
|
3255
3266
|
if (!(await sql`
|
|
3256
3267
|
SELECT EXISTS (
|
|
3257
3268
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3276,8 +3287,8 @@ async function down$45(db) {
|
|
|
3276
3287
|
//#endregion
|
|
3277
3288
|
//#region src/db/migrations/016_org_settings_allow_http_npm.ts
|
|
3278
3289
|
var _016_org_settings_allow_http_npm_exports = /* @__PURE__ */ __exportAll({
|
|
3279
|
-
down: () => down$
|
|
3280
|
-
up: () => up$
|
|
3290
|
+
down: () => down$50,
|
|
3291
|
+
up: () => up$50
|
|
3281
3292
|
});
|
|
3282
3293
|
/**
|
|
3283
3294
|
* Add `org_settings.allow_http_npm_registries boolean NOT NULL DEFAULT false`.
|
|
@@ -3290,7 +3301,7 @@ var _016_org_settings_allow_http_npm_exports = /* @__PURE__ */ __exportAll({
|
|
|
3290
3301
|
*
|
|
3291
3302
|
* Idempotent: a re-run on a DB that already has the column is a no-op.
|
|
3292
3303
|
*/
|
|
3293
|
-
async function up$
|
|
3304
|
+
async function up$50(db) {
|
|
3294
3305
|
if ((await sql`
|
|
3295
3306
|
SELECT EXISTS (
|
|
3296
3307
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3304,7 +3315,7 @@ async function up$44(db) {
|
|
|
3304
3315
|
ADD COLUMN allow_http_npm_registries boolean NOT NULL DEFAULT false
|
|
3305
3316
|
`.execute(db);
|
|
3306
3317
|
}
|
|
3307
|
-
async function down$
|
|
3318
|
+
async function down$50(db) {
|
|
3308
3319
|
await sql`
|
|
3309
3320
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS allow_http_npm_registries
|
|
3310
3321
|
`.execute(db);
|
|
@@ -3312,8 +3323,8 @@ async function down$44(db) {
|
|
|
3312
3323
|
//#endregion
|
|
3313
3324
|
//#region src/db/migrations/017_org_id_widen.ts
|
|
3314
3325
|
var _017_org_id_widen_exports = /* @__PURE__ */ __exportAll({
|
|
3315
|
-
down: () => down$
|
|
3316
|
-
up: () => up$
|
|
3326
|
+
down: () => down$49,
|
|
3327
|
+
up: () => up$49
|
|
3317
3328
|
});
|
|
3318
3329
|
/**
|
|
3319
3330
|
* Widen every orchestrator-side `org_id varchar(12)` column to
|
|
@@ -3353,17 +3364,17 @@ const ORG_ID_TABLES$1 = [
|
|
|
3353
3364
|
"held_runs",
|
|
3354
3365
|
"scoped_secrets"
|
|
3355
3366
|
];
|
|
3356
|
-
async function up$
|
|
3367
|
+
async function up$49(db) {
|
|
3357
3368
|
for (const table of ORG_ID_TABLES$1) await sql.raw(`ALTER TABLE public.${table} ALTER COLUMN org_id TYPE varchar(16)`).execute(db);
|
|
3358
3369
|
}
|
|
3359
|
-
async function down$
|
|
3370
|
+
async function down$49(db) {
|
|
3360
3371
|
for (const table of ORG_ID_TABLES$1) await sql.raw(`ALTER TABLE public.${table} ALTER COLUMN org_id TYPE varchar(12)`).execute(db);
|
|
3361
3372
|
}
|
|
3362
3373
|
//#endregion
|
|
3363
3374
|
//#region src/db/migrations/018_org_id_prefix_backfill.ts
|
|
3364
3375
|
var _018_org_id_prefix_backfill_exports = /* @__PURE__ */ __exportAll({
|
|
3365
|
-
down: () => down$
|
|
3366
|
-
up: () => up$
|
|
3376
|
+
down: () => down$48,
|
|
3377
|
+
up: () => up$48
|
|
3367
3378
|
});
|
|
3368
3379
|
/**
|
|
3369
3380
|
* Prefix every orchestrator-side tenant string with `org_` to align
|
|
@@ -3408,19 +3419,19 @@ const CUSTOMER_ID_TABLES = [
|
|
|
3408
3419
|
"workflow_registrations",
|
|
3409
3420
|
"org_settings"
|
|
3410
3421
|
];
|
|
3411
|
-
async function up$
|
|
3422
|
+
async function up$48(db) {
|
|
3412
3423
|
for (const table of ORG_ID_TABLES) await sql.raw(`UPDATE public.${table} SET org_id = 'org_' || org_id WHERE org_id <> 'kici-admin' AND org_id NOT LIKE 'org\\_%' ESCAPE '\\'`).execute(db);
|
|
3413
3424
|
for (const table of CUSTOMER_ID_TABLES) await sql.raw(`UPDATE public.${table} SET customer_id = 'org_' || customer_id WHERE customer_id <> 'kici-admin' AND customer_id NOT LIKE 'org\\_%' ESCAPE '\\'`).execute(db);
|
|
3414
3425
|
}
|
|
3415
|
-
async function down$
|
|
3426
|
+
async function down$48(db) {
|
|
3416
3427
|
for (const table of CUSTOMER_ID_TABLES) await sql.raw(`UPDATE public.${table} SET customer_id = substring(customer_id from 5) WHERE customer_id LIKE 'org\\_%' ESCAPE '\\'`).execute(db);
|
|
3417
3428
|
for (const table of ORG_ID_TABLES) await sql.raw(`UPDATE public.${table} SET org_id = substring(org_id from 5) WHERE org_id LIKE 'org\\_%' ESCAPE '\\'`).execute(db);
|
|
3418
3429
|
}
|
|
3419
3430
|
//#endregion
|
|
3420
3431
|
//#region src/db/migrations/019_generic_sources_change_notify.ts
|
|
3421
3432
|
var _019_generic_sources_change_notify_exports = /* @__PURE__ */ __exportAll({
|
|
3422
|
-
down: () => down$
|
|
3423
|
-
up: () => up$
|
|
3433
|
+
down: () => down$47,
|
|
3434
|
+
up: () => up$47
|
|
3424
3435
|
});
|
|
3425
3436
|
/**
|
|
3426
3437
|
* Add a Postgres trigger on `generic_webhook_sources` that emits
|
|
@@ -3443,7 +3454,7 @@ var _019_generic_sources_change_notify_exports = /* @__PURE__ */ __exportAll({
|
|
|
3443
3454
|
* (`notify_sources_change()` + `sources_change_trigger`, defined in
|
|
3444
3455
|
* `001_initial.ts`).
|
|
3445
3456
|
*/
|
|
3446
|
-
async function up$
|
|
3457
|
+
async function up$47(db) {
|
|
3447
3458
|
await sql`
|
|
3448
3459
|
CREATE FUNCTION public.notify_generic_sources_change() RETURNS trigger
|
|
3449
3460
|
LANGUAGE plpgsql
|
|
@@ -3464,15 +3475,15 @@ async function up$41(db) {
|
|
|
3464
3475
|
FOR EACH ROW EXECUTE FUNCTION public.notify_generic_sources_change()
|
|
3465
3476
|
`.execute(db);
|
|
3466
3477
|
}
|
|
3467
|
-
async function down$
|
|
3478
|
+
async function down$47(db) {
|
|
3468
3479
|
await sql`DROP TRIGGER IF EXISTS generic_sources_change_trigger ON public.generic_webhook_sources`.execute(db);
|
|
3469
3480
|
await sql`DROP FUNCTION IF EXISTS public.notify_generic_sources_change()`.execute(db);
|
|
3470
3481
|
}
|
|
3471
3482
|
//#endregion
|
|
3472
3483
|
//#region src/db/migrations/020_org_settings_dashboard_write_policy.ts
|
|
3473
3484
|
var _020_org_settings_dashboard_write_policy_exports = /* @__PURE__ */ __exportAll({
|
|
3474
|
-
down: () => down$
|
|
3475
|
-
up: () => up$
|
|
3485
|
+
down: () => down$46,
|
|
3486
|
+
up: () => up$46
|
|
3476
3487
|
});
|
|
3477
3488
|
/**
|
|
3478
3489
|
* Add `org_settings.dashboard_write_policy jsonb NOT NULL DEFAULT '{}'`.
|
|
@@ -3487,7 +3498,7 @@ var _020_org_settings_dashboard_write_policy_exports = /* @__PURE__ */ __exportA
|
|
|
3487
3498
|
*
|
|
3488
3499
|
* Idempotent: a re-run on a DB that already has the column is a no-op.
|
|
3489
3500
|
*/
|
|
3490
|
-
async function up$
|
|
3501
|
+
async function up$46(db) {
|
|
3491
3502
|
if ((await sql`
|
|
3492
3503
|
SELECT EXISTS (
|
|
3493
3504
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3501,7 +3512,7 @@ async function up$40(db) {
|
|
|
3501
3512
|
ADD COLUMN dashboard_write_policy jsonb NOT NULL DEFAULT '{}'::jsonb
|
|
3502
3513
|
`.execute(db);
|
|
3503
3514
|
}
|
|
3504
|
-
async function down$
|
|
3515
|
+
async function down$46(db) {
|
|
3505
3516
|
await sql`
|
|
3506
3517
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS dashboard_write_policy
|
|
3507
3518
|
`.execute(db);
|
|
@@ -3509,8 +3520,8 @@ async function down$40(db) {
|
|
|
3509
3520
|
//#endregion
|
|
3510
3521
|
//#region src/db/migrations/021_check_run_tracking.ts
|
|
3511
3522
|
var _021_check_run_tracking_exports = /* @__PURE__ */ __exportAll({
|
|
3512
|
-
down: () => down$
|
|
3513
|
-
up: () => up$
|
|
3523
|
+
down: () => down$45,
|
|
3524
|
+
up: () => up$45
|
|
3514
3525
|
});
|
|
3515
3526
|
/**
|
|
3516
3527
|
* Add `check_run_tracking` table for HA-safe check-run state persistence.
|
|
@@ -3534,7 +3545,7 @@ var _021_check_run_tracking_exports = /* @__PURE__ */ __exportAll({
|
|
|
3534
3545
|
*
|
|
3535
3546
|
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
3536
3547
|
*/
|
|
3537
|
-
async function up$
|
|
3548
|
+
async function up$45(db) {
|
|
3538
3549
|
if ((await sql`
|
|
3539
3550
|
SELECT EXISTS (
|
|
3540
3551
|
SELECT 1 FROM information_schema.tables
|
|
@@ -3565,14 +3576,14 @@ async function up$39(db) {
|
|
|
3565
3576
|
WHERE run_id IS NOT NULL
|
|
3566
3577
|
`.execute(db);
|
|
3567
3578
|
}
|
|
3568
|
-
async function down$
|
|
3579
|
+
async function down$45(db) {
|
|
3569
3580
|
await sql`DROP TABLE IF EXISTS public.check_run_tracking`.execute(db);
|
|
3570
3581
|
}
|
|
3571
3582
|
//#endregion
|
|
3572
3583
|
//#region src/db/migrations/022_scaler_manager_state.ts
|
|
3573
3584
|
var _022_scaler_manager_state_exports = /* @__PURE__ */ __exportAll({
|
|
3574
|
-
down: () => down$
|
|
3575
|
-
up: () => up$
|
|
3585
|
+
down: () => down$44,
|
|
3586
|
+
up: () => up$44
|
|
3576
3587
|
});
|
|
3577
3588
|
/**
|
|
3578
3589
|
* Add three tables persisting `ScalerManager` per-coord state:
|
|
@@ -3599,7 +3610,7 @@ var _022_scaler_manager_state_exports = /* @__PURE__ */ __exportAll({
|
|
|
3599
3610
|
* Idempotent: a re-run on a DB that already has any of these tables
|
|
3600
3611
|
* leaves the existing one alone.
|
|
3601
3612
|
*/
|
|
3602
|
-
async function up$
|
|
3613
|
+
async function up$44(db) {
|
|
3603
3614
|
const tableExists = async (name) => {
|
|
3604
3615
|
return (await sql`
|
|
3605
3616
|
SELECT EXISTS (
|
|
@@ -3649,7 +3660,7 @@ async function up$38(db) {
|
|
|
3649
3660
|
`.execute(db);
|
|
3650
3661
|
}
|
|
3651
3662
|
}
|
|
3652
|
-
async function down$
|
|
3663
|
+
async function down$44(db) {
|
|
3653
3664
|
await sql`DROP TABLE IF EXISTS public.scaler_reservations`.execute(db);
|
|
3654
3665
|
await sql`DROP TABLE IF EXISTS public.scaler_agent_jobs`.execute(db);
|
|
3655
3666
|
await sql`DROP TABLE IF EXISTS public.scaler_spawning_agents`.execute(db);
|
|
@@ -3657,8 +3668,8 @@ async function down$38(db) {
|
|
|
3657
3668
|
//#endregion
|
|
3658
3669
|
//#region src/db/migrations/023_dispatch_queue_recovery_deadline.ts
|
|
3659
3670
|
var _023_dispatch_queue_recovery_deadline_exports = /* @__PURE__ */ __exportAll({
|
|
3660
|
-
down: () => down$
|
|
3661
|
-
up: () => up$
|
|
3671
|
+
down: () => down$43,
|
|
3672
|
+
up: () => up$43
|
|
3662
3673
|
});
|
|
3663
3674
|
/**
|
|
3664
3675
|
* Add `dispatch_queue.recovery_deadline TIMESTAMPTZ` and
|
|
@@ -3682,7 +3693,7 @@ var _023_dispatch_queue_recovery_deadline_exports = /* @__PURE__ */ __exportAll(
|
|
|
3682
3693
|
* Idempotent: re-running on a DB that already has either column is a
|
|
3683
3694
|
* no-op.
|
|
3684
3695
|
*/
|
|
3685
|
-
async function up$
|
|
3696
|
+
async function up$43(db) {
|
|
3686
3697
|
const colExists = async (name) => {
|
|
3687
3698
|
return (await sql`
|
|
3688
3699
|
SELECT EXISTS (
|
|
@@ -3707,7 +3718,7 @@ async function up$37(db) {
|
|
|
3707
3718
|
WHERE recovery_deadline IS NOT NULL
|
|
3708
3719
|
`.execute(db);
|
|
3709
3720
|
}
|
|
3710
|
-
async function down$
|
|
3721
|
+
async function down$43(db) {
|
|
3711
3722
|
await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_recovery_deadline`.execute(db);
|
|
3712
3723
|
await sql`
|
|
3713
3724
|
ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS recovery_agent_id
|
|
@@ -3719,8 +3730,8 @@ async function down$37(db) {
|
|
|
3719
3730
|
//#endregion
|
|
3720
3731
|
//#region src/db/migrations/024_dispatch_queue_provisioning_error.ts
|
|
3721
3732
|
var _024_dispatch_queue_provisioning_error_exports = /* @__PURE__ */ __exportAll({
|
|
3722
|
-
down: () => down$
|
|
3723
|
-
up: () => up$
|
|
3733
|
+
down: () => down$42,
|
|
3734
|
+
up: () => up$42
|
|
3724
3735
|
});
|
|
3725
3736
|
/**
|
|
3726
3737
|
* Add `dispatch_queue.last_provisioning_error TEXT` recording the most
|
|
@@ -3736,7 +3747,7 @@ var _024_dispatch_queue_provisioning_error_exports = /* @__PURE__ */ __exportAll
|
|
|
3736
3747
|
*
|
|
3737
3748
|
* Idempotent: re-running on a DB that already has the column is a no-op.
|
|
3738
3749
|
*/
|
|
3739
|
-
async function up$
|
|
3750
|
+
async function up$42(db) {
|
|
3740
3751
|
const colExists = async (name) => {
|
|
3741
3752
|
return (await sql`
|
|
3742
3753
|
SELECT EXISTS (
|
|
@@ -3752,7 +3763,7 @@ async function up$36(db) {
|
|
|
3752
3763
|
ADD COLUMN last_provisioning_error TEXT
|
|
3753
3764
|
`.execute(db);
|
|
3754
3765
|
}
|
|
3755
|
-
async function down$
|
|
3766
|
+
async function down$42(db) {
|
|
3756
3767
|
await sql`
|
|
3757
3768
|
ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS last_provisioning_error
|
|
3758
3769
|
`.execute(db);
|
|
@@ -3760,8 +3771,8 @@ async function down$36(db) {
|
|
|
3760
3771
|
//#endregion
|
|
3761
3772
|
//#region src/db/migrations/025_init_failure.ts
|
|
3762
3773
|
var _025_init_failure_exports = /* @__PURE__ */ __exportAll({
|
|
3763
|
-
down: () => down$
|
|
3764
|
-
up: () => up$
|
|
3774
|
+
down: () => down$41,
|
|
3775
|
+
up: () => up$41
|
|
3765
3776
|
});
|
|
3766
3777
|
/**
|
|
3767
3778
|
* Add `init_failure jsonb` columns to `execution_runs` and `execution_jobs`.
|
|
@@ -3775,7 +3786,7 @@ var _025_init_failure_exports = /* @__PURE__ */ __exportAll({
|
|
|
3775
3786
|
* Idempotent: re-running on a DB that already has either column is a no-op
|
|
3776
3787
|
* for that column.
|
|
3777
3788
|
*/
|
|
3778
|
-
async function up$
|
|
3789
|
+
async function up$41(db) {
|
|
3779
3790
|
const colExists = async (table, name) => {
|
|
3780
3791
|
return (await sql`
|
|
3781
3792
|
SELECT EXISTS (
|
|
@@ -3795,7 +3806,7 @@ async function up$35(db) {
|
|
|
3795
3806
|
ADD COLUMN init_failure JSONB DEFAULT NULL
|
|
3796
3807
|
`.execute(db);
|
|
3797
3808
|
}
|
|
3798
|
-
async function down$
|
|
3809
|
+
async function down$41(db) {
|
|
3799
3810
|
await sql`
|
|
3800
3811
|
ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS init_failure
|
|
3801
3812
|
`.execute(db);
|
|
@@ -3806,8 +3817,8 @@ async function down$35(db) {
|
|
|
3806
3817
|
//#endregion
|
|
3807
3818
|
//#region src/db/migrations/026_event_log_lockfile_corrupt.ts
|
|
3808
3819
|
var _026_event_log_lockfile_corrupt_exports = /* @__PURE__ */ __exportAll({
|
|
3809
|
-
down: () => down$
|
|
3810
|
-
up: () => up$
|
|
3820
|
+
down: () => down$40,
|
|
3821
|
+
up: () => up$40
|
|
3811
3822
|
});
|
|
3812
3823
|
/**
|
|
3813
3824
|
* Extend the event_log.status CHECK constraint with 'lockfile_corrupt' so the
|
|
@@ -3816,7 +3827,7 @@ var _026_event_log_lockfile_corrupt_exports = /* @__PURE__ */ __exportAll({
|
|
|
3816
3827
|
*
|
|
3817
3828
|
* Idempotent: the DROP ... IF EXISTS / re-ADD pair re-runs cleanly.
|
|
3818
3829
|
*/
|
|
3819
|
-
async function up$
|
|
3830
|
+
async function up$40(db) {
|
|
3820
3831
|
await sql`ALTER TABLE event_log DROP CONSTRAINT IF EXISTS event_log_status_check`.execute(db);
|
|
3821
3832
|
await sql`
|
|
3822
3833
|
ALTER TABLE event_log ADD CONSTRAINT event_log_status_check
|
|
@@ -3826,7 +3837,7 @@ async function up$34(db) {
|
|
|
3826
3837
|
])))
|
|
3827
3838
|
`.execute(db);
|
|
3828
3839
|
}
|
|
3829
|
-
async function down$
|
|
3840
|
+
async function down$40(db) {
|
|
3830
3841
|
await sql`ALTER TABLE event_log DROP CONSTRAINT IF EXISTS event_log_status_check`.execute(db);
|
|
3831
3842
|
await sql`
|
|
3832
3843
|
ALTER TABLE event_log ADD CONSTRAINT event_log_status_check
|
|
@@ -3839,8 +3850,8 @@ async function down$34(db) {
|
|
|
3839
3850
|
//#endregion
|
|
3840
3851
|
//#region src/db/migrations/027_workflow_timeout.ts
|
|
3841
3852
|
var _027_workflow_timeout_exports = /* @__PURE__ */ __exportAll({
|
|
3842
|
-
down: () => down$
|
|
3843
|
-
up: () => up$
|
|
3853
|
+
down: () => down$39,
|
|
3854
|
+
up: () => up$39
|
|
3844
3855
|
});
|
|
3845
3856
|
/**
|
|
3846
3857
|
* Add `workflow_timeout_ms integer` to `execution_runs`.
|
|
@@ -3858,13 +3869,13 @@ var _027_workflow_timeout_exports = /* @__PURE__ */ __exportAll({
|
|
|
3858
3869
|
*
|
|
3859
3870
|
* Idempotent: re-running on a DB that already has the column is a no-op.
|
|
3860
3871
|
*/
|
|
3861
|
-
async function up$
|
|
3872
|
+
async function up$39(db) {
|
|
3862
3873
|
await sql`
|
|
3863
3874
|
ALTER TABLE public.execution_runs
|
|
3864
3875
|
ADD COLUMN IF NOT EXISTS workflow_timeout_ms INTEGER DEFAULT NULL
|
|
3865
3876
|
`.execute(db);
|
|
3866
3877
|
}
|
|
3867
|
-
async function down$
|
|
3878
|
+
async function down$39(db) {
|
|
3868
3879
|
await sql`
|
|
3869
3880
|
ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS workflow_timeout_ms
|
|
3870
3881
|
`.execute(db);
|
|
@@ -3872,8 +3883,8 @@ async function down$33(db) {
|
|
|
3872
3883
|
//#endregion
|
|
3873
3884
|
//#region src/db/migrations/028_org_settings_user_cache.ts
|
|
3874
3885
|
var _028_org_settings_user_cache_exports = /* @__PURE__ */ __exportAll({
|
|
3875
|
-
down: () => down$
|
|
3876
|
-
up: () => up$
|
|
3886
|
+
down: () => down$38,
|
|
3887
|
+
up: () => up$38
|
|
3877
3888
|
});
|
|
3878
3889
|
/**
|
|
3879
3890
|
* Add `org_settings.user_cache_quota_bytes bigint` and
|
|
@@ -3892,7 +3903,7 @@ var _028_org_settings_user_cache_exports = /* @__PURE__ */ __exportAll({
|
|
|
3892
3903
|
*
|
|
3893
3904
|
* Idempotent: a re-run on a DB that already has either column skips it.
|
|
3894
3905
|
*/
|
|
3895
|
-
async function columnExists(db, column) {
|
|
3906
|
+
async function columnExists$1(db, column) {
|
|
3896
3907
|
return (await sql`
|
|
3897
3908
|
SELECT EXISTS (
|
|
3898
3909
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3902,17 +3913,17 @@ async function columnExists(db, column) {
|
|
|
3902
3913
|
) AS exists
|
|
3903
3914
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
3904
3915
|
}
|
|
3905
|
-
async function up$
|
|
3906
|
-
if (!await columnExists(db, "user_cache_quota_bytes")) await sql`
|
|
3916
|
+
async function up$38(db) {
|
|
3917
|
+
if (!await columnExists$1(db, "user_cache_quota_bytes")) await sql`
|
|
3907
3918
|
ALTER TABLE public.org_settings
|
|
3908
3919
|
ADD COLUMN user_cache_quota_bytes bigint
|
|
3909
3920
|
`.execute(db);
|
|
3910
|
-
if (!await columnExists(db, "user_cache_ttl_ms")) await sql`
|
|
3921
|
+
if (!await columnExists$1(db, "user_cache_ttl_ms")) await sql`
|
|
3911
3922
|
ALTER TABLE public.org_settings
|
|
3912
3923
|
ADD COLUMN user_cache_ttl_ms bigint
|
|
3913
3924
|
`.execute(db);
|
|
3914
3925
|
}
|
|
3915
|
-
async function down$
|
|
3926
|
+
async function down$38(db) {
|
|
3916
3927
|
await sql`
|
|
3917
3928
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS user_cache_quota_bytes
|
|
3918
3929
|
`.execute(db);
|
|
@@ -3923,8 +3934,8 @@ async function down$32(db) {
|
|
|
3923
3934
|
//#endregion
|
|
3924
3935
|
//#region src/db/migrations/029_dispatch_queue_attempts.ts
|
|
3925
3936
|
var _029_dispatch_queue_attempts_exports = /* @__PURE__ */ __exportAll({
|
|
3926
|
-
down: () => down$
|
|
3927
|
-
up: () => up$
|
|
3937
|
+
down: () => down$37,
|
|
3938
|
+
up: () => up$37
|
|
3928
3939
|
});
|
|
3929
3940
|
/**
|
|
3930
3941
|
* Add `dispatch_queue.dispatch_attempts INT NOT NULL DEFAULT 0`.
|
|
@@ -3938,7 +3949,7 @@ var _029_dispatch_queue_attempts_exports = /* @__PURE__ */ __exportAll({
|
|
|
3938
3949
|
*
|
|
3939
3950
|
* Idempotent: re-running on a DB that already has the column is a no-op.
|
|
3940
3951
|
*/
|
|
3941
|
-
async function up$
|
|
3952
|
+
async function up$37(db) {
|
|
3942
3953
|
if (!((await sql`
|
|
3943
3954
|
SELECT EXISTS (
|
|
3944
3955
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3951,7 +3962,7 @@ async function up$31(db) {
|
|
|
3951
3962
|
ADD COLUMN dispatch_attempts INT NOT NULL DEFAULT 0
|
|
3952
3963
|
`.execute(db);
|
|
3953
3964
|
}
|
|
3954
|
-
async function down$
|
|
3965
|
+
async function down$37(db) {
|
|
3955
3966
|
await sql`
|
|
3956
3967
|
ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS dispatch_attempts
|
|
3957
3968
|
`.execute(db);
|
|
@@ -3959,8 +3970,8 @@ async function down$31(db) {
|
|
|
3959
3970
|
//#endregion
|
|
3960
3971
|
//#region src/db/migrations/030_held_runs_env_set_null.ts
|
|
3961
3972
|
var _030_held_runs_env_set_null_exports = /* @__PURE__ */ __exportAll({
|
|
3962
|
-
down: () => down$
|
|
3963
|
-
up: () => up$
|
|
3973
|
+
down: () => down$36,
|
|
3974
|
+
up: () => up$36
|
|
3964
3975
|
});
|
|
3965
3976
|
/**
|
|
3966
3977
|
* held_runs.environment_id becomes nullable with ON DELETE SET NULL so
|
|
@@ -3971,14 +3982,14 @@ var _030_held_runs_env_set_null_exports = /* @__PURE__ */ __exportAll({
|
|
|
3971
3982
|
* Idempotent: dropping the NOT NULL and the constraint are both no-ops on a
|
|
3972
3983
|
* re-run, and the constraint is re-created with the SET NULL action.
|
|
3973
3984
|
*/
|
|
3974
|
-
async function up$
|
|
3985
|
+
async function up$36(db) {
|
|
3975
3986
|
await sql`ALTER TABLE public.held_runs ALTER COLUMN environment_id DROP NOT NULL`.execute(db);
|
|
3976
3987
|
await sql`ALTER TABLE public.held_runs DROP CONSTRAINT IF EXISTS held_runs_environment_id_fkey`.execute(db);
|
|
3977
3988
|
await sql`ALTER TABLE public.held_runs
|
|
3978
3989
|
ADD CONSTRAINT held_runs_environment_id_fkey
|
|
3979
3990
|
FOREIGN KEY (environment_id) REFERENCES public.environments(id) ON DELETE SET NULL`.execute(db);
|
|
3980
3991
|
}
|
|
3981
|
-
async function down$
|
|
3992
|
+
async function down$36(db) {
|
|
3982
3993
|
await sql`ALTER TABLE public.held_runs DROP CONSTRAINT IF EXISTS held_runs_environment_id_fkey`.execute(db);
|
|
3983
3994
|
await sql`ALTER TABLE public.held_runs
|
|
3984
3995
|
ADD CONSTRAINT held_runs_environment_id_fkey
|
|
@@ -3988,8 +3999,8 @@ async function down$30(db) {
|
|
|
3988
3999
|
//#endregion
|
|
3989
4000
|
//#region src/db/migrations/031_dispatch_queue_ack_deadline.ts
|
|
3990
4001
|
var _031_dispatch_queue_ack_deadline_exports = /* @__PURE__ */ __exportAll({
|
|
3991
|
-
down: () => down$
|
|
3992
|
-
up: () => up$
|
|
4002
|
+
down: () => down$35,
|
|
4003
|
+
up: () => up$35
|
|
3993
4004
|
});
|
|
3994
4005
|
/**
|
|
3995
4006
|
* Add `dispatch_queue.ack_deadline TIMESTAMPTZ` and
|
|
@@ -4006,7 +4017,7 @@ var _031_dispatch_queue_ack_deadline_exports = /* @__PURE__ */ __exportAll({
|
|
|
4006
4017
|
*
|
|
4007
4018
|
* Idempotent: re-running on a DB that already has either column is a no-op.
|
|
4008
4019
|
*/
|
|
4009
|
-
async function up$
|
|
4020
|
+
async function up$35(db) {
|
|
4010
4021
|
const colExists = async (name) => {
|
|
4011
4022
|
return (await sql`
|
|
4012
4023
|
SELECT EXISTS (
|
|
@@ -4031,7 +4042,7 @@ async function up$29(db) {
|
|
|
4031
4042
|
WHERE ack_deadline IS NOT NULL
|
|
4032
4043
|
`.execute(db);
|
|
4033
4044
|
}
|
|
4034
|
-
async function down$
|
|
4045
|
+
async function down$35(db) {
|
|
4035
4046
|
await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_ack_deadline`.execute(db);
|
|
4036
4047
|
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS ack_agent_id`.execute(db);
|
|
4037
4048
|
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS ack_deadline`.execute(db);
|
|
@@ -4039,8 +4050,8 @@ async function down$29(db) {
|
|
|
4039
4050
|
//#endregion
|
|
4040
4051
|
//#region src/db/migrations/032_org_settings_dispatch_ack_timeout.ts
|
|
4041
4052
|
var _032_org_settings_dispatch_ack_timeout_exports = /* @__PURE__ */ __exportAll({
|
|
4042
|
-
down: () => down$
|
|
4043
|
-
up: () => up$
|
|
4053
|
+
down: () => down$34,
|
|
4054
|
+
up: () => up$34
|
|
4044
4055
|
});
|
|
4045
4056
|
/**
|
|
4046
4057
|
* Add `org_settings.dispatch_ack_timeout_ms BIGINT` (nullable).
|
|
@@ -4052,7 +4063,7 @@ var _032_org_settings_dispatch_ack_timeout_exports = /* @__PURE__ */ __exportAll
|
|
|
4052
4063
|
*
|
|
4053
4064
|
* Idempotent: a re-run on a DB that already has the column is a no-op.
|
|
4054
4065
|
*/
|
|
4055
|
-
async function up$
|
|
4066
|
+
async function up$34(db) {
|
|
4056
4067
|
if ((await sql`
|
|
4057
4068
|
SELECT EXISTS (
|
|
4058
4069
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4066,7 +4077,7 @@ async function up$28(db) {
|
|
|
4066
4077
|
ADD COLUMN dispatch_ack_timeout_ms BIGINT
|
|
4067
4078
|
`.execute(db);
|
|
4068
4079
|
}
|
|
4069
|
-
async function down$
|
|
4080
|
+
async function down$34(db) {
|
|
4070
4081
|
await sql`
|
|
4071
4082
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS dispatch_ack_timeout_ms
|
|
4072
4083
|
`.execute(db);
|
|
@@ -4074,8 +4085,8 @@ async function down$28(db) {
|
|
|
4074
4085
|
//#endregion
|
|
4075
4086
|
//#region src/db/migrations/033_org_settings_approval.ts
|
|
4076
4087
|
var _033_org_settings_approval_exports = /* @__PURE__ */ __exportAll({
|
|
4077
|
-
down: () => down$
|
|
4078
|
-
up: () => up$
|
|
4088
|
+
down: () => down$33,
|
|
4089
|
+
up: () => up$33
|
|
4079
4090
|
});
|
|
4080
4091
|
/**
|
|
4081
4092
|
* Add the two approval-policy columns to `org_settings`:
|
|
@@ -4092,7 +4103,7 @@ var _033_org_settings_approval_exports = /* @__PURE__ */ __exportAll({
|
|
|
4092
4103
|
* and the orchestrator admin route. Idempotent: a re-run on a DB that already
|
|
4093
4104
|
* has the columns is a no-op (each column is guarded independently).
|
|
4094
4105
|
*/
|
|
4095
|
-
async function up$
|
|
4106
|
+
async function up$33(db) {
|
|
4096
4107
|
const colExists = async (column) => {
|
|
4097
4108
|
return (await sql`
|
|
4098
4109
|
SELECT EXISTS (
|
|
@@ -4112,7 +4123,7 @@ async function up$27(db) {
|
|
|
4112
4123
|
ADD COLUMN allow_self_approval BOOLEAN NOT NULL DEFAULT true
|
|
4113
4124
|
`.execute(db);
|
|
4114
4125
|
}
|
|
4115
|
-
async function down$
|
|
4126
|
+
async function down$33(db) {
|
|
4116
4127
|
await sql`
|
|
4117
4128
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS approval_expiry_seconds
|
|
4118
4129
|
`.execute(db);
|
|
@@ -4123,8 +4134,8 @@ async function down$27(db) {
|
|
|
4123
4134
|
//#endregion
|
|
4124
4135
|
//#region src/db/migrations/034_held_runs_generalize.ts
|
|
4125
4136
|
var _034_held_runs_generalize_exports = /* @__PURE__ */ __exportAll({
|
|
4126
|
-
down: () => down$
|
|
4127
|
-
up: () => up$
|
|
4137
|
+
down: () => down$32,
|
|
4138
|
+
up: () => up$32
|
|
4128
4139
|
});
|
|
4129
4140
|
/**
|
|
4130
4141
|
* Generalize `held_runs` from an environment-only hold into the unified
|
|
@@ -4146,7 +4157,7 @@ var _034_held_runs_generalize_exports = /* @__PURE__ */ __exportAll({
|
|
|
4146
4157
|
* New `held_run_approvals` table: one row per approver decision, FK to
|
|
4147
4158
|
* `held_runs.id` (uuid) with ON DELETE CASCADE.
|
|
4148
4159
|
*/
|
|
4149
|
-
async function up$
|
|
4160
|
+
async function up$32(db) {
|
|
4150
4161
|
const colExists = async (column) => {
|
|
4151
4162
|
return (await sql`
|
|
4152
4163
|
SELECT EXISTS (
|
|
@@ -4179,7 +4190,7 @@ async function up$26(db) {
|
|
|
4179
4190
|
ON public.held_run_approvals USING btree (held_run_id)
|
|
4180
4191
|
`.execute(db);
|
|
4181
4192
|
}
|
|
4182
|
-
async function down$
|
|
4193
|
+
async function down$32(db) {
|
|
4183
4194
|
await sql`DROP TABLE IF EXISTS public.held_run_approvals`.execute(db);
|
|
4184
4195
|
await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS approval_requirement`.execute(db);
|
|
4185
4196
|
await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS trigger_source`.execute(db);
|
|
@@ -4189,8 +4200,8 @@ async function down$26(db) {
|
|
|
4189
4200
|
//#endregion
|
|
4190
4201
|
//#region src/db/migrations/035_pending_workflow_contexts.ts
|
|
4191
4202
|
var _035_pending_workflow_contexts_exports = /* @__PURE__ */ __exportAll({
|
|
4192
|
-
down: () => down$
|
|
4193
|
-
up: () => up$
|
|
4203
|
+
down: () => down$31,
|
|
4204
|
+
up: () => up$31
|
|
4194
4205
|
});
|
|
4195
4206
|
/**
|
|
4196
4207
|
* Pending workflow dispatch context — backs resume of a workflow whose install
|
|
@@ -4199,7 +4210,7 @@ var _035_pending_workflow_contexts_exports = /* @__PURE__ */ __exportAll({
|
|
|
4199
4210
|
* wait-timer expiry, concurrency slot free). The row is deleted once the resume
|
|
4200
4211
|
* dispatch has been kicked off.
|
|
4201
4212
|
*/
|
|
4202
|
-
async function up$
|
|
4213
|
+
async function up$31(db) {
|
|
4203
4214
|
await sql`
|
|
4204
4215
|
CREATE TABLE IF NOT EXISTS public.pending_workflow_contexts (
|
|
4205
4216
|
run_id text PRIMARY KEY,
|
|
@@ -4209,14 +4220,14 @@ async function up$25(db) {
|
|
|
4209
4220
|
)
|
|
4210
4221
|
`.execute(db);
|
|
4211
4222
|
}
|
|
4212
|
-
async function down$
|
|
4223
|
+
async function down$31(db) {
|
|
4213
4224
|
await sql`DROP TABLE IF EXISTS public.pending_workflow_contexts`.execute(db);
|
|
4214
4225
|
}
|
|
4215
4226
|
//#endregion
|
|
4216
4227
|
//#region src/db/migrations/036_attestations.ts
|
|
4217
4228
|
var _036_attestations_exports = /* @__PURE__ */ __exportAll({
|
|
4218
|
-
down: () => down$
|
|
4219
|
-
up: () => up$
|
|
4229
|
+
down: () => down$30,
|
|
4230
|
+
up: () => up$30
|
|
4220
4231
|
});
|
|
4221
4232
|
/**
|
|
4222
4233
|
* Add the `attestations` table for build-provenance bundles.
|
|
@@ -4229,7 +4240,7 @@ var _036_attestations_exports = /* @__PURE__ */ __exportAll({
|
|
|
4229
4240
|
*
|
|
4230
4241
|
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
4231
4242
|
*/
|
|
4232
|
-
async function up$
|
|
4243
|
+
async function up$30(db) {
|
|
4233
4244
|
if ((await sql`
|
|
4234
4245
|
SELECT EXISTS (
|
|
4235
4246
|
SELECT 1 FROM information_schema.tables
|
|
@@ -4255,14 +4266,14 @@ async function up$24(db) {
|
|
|
4255
4266
|
ON public.attestations (run_id, job_id)
|
|
4256
4267
|
`.execute(db);
|
|
4257
4268
|
}
|
|
4258
|
-
async function down$
|
|
4269
|
+
async function down$30(db) {
|
|
4259
4270
|
await sql`DROP TABLE IF EXISTS public.attestations`.execute(db);
|
|
4260
4271
|
}
|
|
4261
4272
|
//#endregion
|
|
4262
4273
|
//#region src/db/migrations/037_generic_sources_provider_type_local.ts
|
|
4263
4274
|
var _037_generic_sources_provider_type_local_exports = /* @__PURE__ */ __exportAll({
|
|
4264
|
-
down: () => down$
|
|
4265
|
-
up: () => up$
|
|
4275
|
+
down: () => down$29,
|
|
4276
|
+
up: () => up$29
|
|
4266
4277
|
});
|
|
4267
4278
|
/**
|
|
4268
4279
|
* Replace the `generic_webhook_sources.provider_type` CHECK constraint so it
|
|
@@ -4279,7 +4290,7 @@ var _037_generic_sources_provider_type_local_exports = /* @__PURE__ */ __exportA
|
|
|
4279
4290
|
* Idempotent: the constraint is dropped IF EXISTS and recreated; the data
|
|
4280
4291
|
* backfill is a plain UPDATE that is a no-op once no `'internal'` rows remain.
|
|
4281
4292
|
*/
|
|
4282
|
-
async function up$
|
|
4293
|
+
async function up$29(db) {
|
|
4283
4294
|
await sql`
|
|
4284
4295
|
ALTER TABLE public.generic_webhook_sources
|
|
4285
4296
|
DROP CONSTRAINT IF EXISTS generic_webhook_sources_provider_type_check
|
|
@@ -4295,7 +4306,7 @@ async function up$23(db) {
|
|
|
4295
4306
|
CHECK (provider_type = ANY (ARRAY['generic'::text, 'local'::text]))
|
|
4296
4307
|
`.execute(db);
|
|
4297
4308
|
}
|
|
4298
|
-
async function down$
|
|
4309
|
+
async function down$29(db) {
|
|
4299
4310
|
await sql`
|
|
4300
4311
|
ALTER TABLE public.generic_webhook_sources
|
|
4301
4312
|
DROP CONSTRAINT IF EXISTS generic_webhook_sources_provider_type_check
|
|
@@ -4314,8 +4325,8 @@ async function down$23(db) {
|
|
|
4314
4325
|
//#endregion
|
|
4315
4326
|
//#region src/db/migrations/038_remote_sources.ts
|
|
4316
4327
|
var _038_remote_sources_exports = /* @__PURE__ */ __exportAll({
|
|
4317
|
-
down: () => down$
|
|
4318
|
-
up: () => up$
|
|
4328
|
+
down: () => down$28,
|
|
4329
|
+
up: () => up$28
|
|
4319
4330
|
});
|
|
4320
4331
|
/**
|
|
4321
4332
|
* `remote_sources` anchors a Platform-relayed `kici run remote` to its real
|
|
@@ -4327,7 +4338,7 @@ var _038_remote_sources_exports = /* @__PURE__ */ __exportAll({
|
|
|
4327
4338
|
*
|
|
4328
4339
|
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
4329
4340
|
*/
|
|
4330
|
-
async function up$
|
|
4341
|
+
async function up$28(db) {
|
|
4331
4342
|
if ((await sql`
|
|
4332
4343
|
SELECT EXISTS (
|
|
4333
4344
|
SELECT 1 FROM information_schema.tables
|
|
@@ -4346,14 +4357,14 @@ async function up$22(db) {
|
|
|
4346
4357
|
)
|
|
4347
4358
|
`.execute(db);
|
|
4348
4359
|
}
|
|
4349
|
-
async function down$
|
|
4360
|
+
async function down$28(db) {
|
|
4350
4361
|
await sql`DROP TABLE IF EXISTS public.remote_sources`.execute(db);
|
|
4351
4362
|
}
|
|
4352
4363
|
//#endregion
|
|
4353
4364
|
//#region src/db/migrations/039_host_roster.ts
|
|
4354
4365
|
var _039_host_roster_exports = /* @__PURE__ */ __exportAll({
|
|
4355
|
-
down: () => down$
|
|
4356
|
-
up: () => up$
|
|
4366
|
+
down: () => down$27,
|
|
4367
|
+
up: () => up$27
|
|
4357
4368
|
});
|
|
4358
4369
|
/**
|
|
4359
4370
|
* `host_roster` is KiCI's declared inventory: one durable row per agent the
|
|
@@ -4370,7 +4381,7 @@ var _039_host_roster_exports = /* @__PURE__ */ __exportAll({
|
|
|
4370
4381
|
*
|
|
4371
4382
|
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
4372
4383
|
*/
|
|
4373
|
-
async function up$
|
|
4384
|
+
async function up$27(db) {
|
|
4374
4385
|
if ((await sql`
|
|
4375
4386
|
SELECT EXISTS (
|
|
4376
4387
|
SELECT 1 FROM information_schema.tables
|
|
@@ -4400,14 +4411,14 @@ async function up$21(db) {
|
|
|
4400
4411
|
await sql`CREATE INDEX idx_host_roster_reap
|
|
4401
4412
|
ON public.host_roster (lifecycle_class, last_seen)`.execute(db);
|
|
4402
4413
|
}
|
|
4403
|
-
async function down$
|
|
4414
|
+
async function down$27(db) {
|
|
4404
4415
|
await sql`DROP TABLE IF EXISTS public.host_roster`.execute(db);
|
|
4405
4416
|
}
|
|
4406
4417
|
//#endregion
|
|
4407
4418
|
//#region src/db/migrations/040_runsonall_pin.ts
|
|
4408
4419
|
var _040_runsonall_pin_exports = /* @__PURE__ */ __exportAll({
|
|
4409
|
-
down: () => down$
|
|
4410
|
-
up: () => up$
|
|
4420
|
+
down: () => down$26,
|
|
4421
|
+
up: () => up$26
|
|
4411
4422
|
});
|
|
4412
4423
|
/**
|
|
4413
4424
|
* Add the `runsOnAll` host fan-out columns:
|
|
@@ -4424,7 +4435,7 @@ var _040_runsonall_pin_exports = /* @__PURE__ */ __exportAll({
|
|
|
4424
4435
|
*
|
|
4425
4436
|
* Idempotent: re-running on a DB that already has any column is a no-op.
|
|
4426
4437
|
*/
|
|
4427
|
-
async function colExists$
|
|
4438
|
+
async function colExists$11(db, table, name) {
|
|
4428
4439
|
return (await sql`
|
|
4429
4440
|
SELECT EXISTS (
|
|
4430
4441
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4434,18 +4445,18 @@ async function colExists$9(db, table, name) {
|
|
|
4434
4445
|
) AS exists
|
|
4435
4446
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
4436
4447
|
}
|
|
4437
|
-
async function up$
|
|
4438
|
-
if (!await colExists$
|
|
4439
|
-
if (!await colExists$
|
|
4440
|
-
if (!await colExists$
|
|
4441
|
-
if (!await colExists$
|
|
4448
|
+
async function up$26(db) {
|
|
4449
|
+
if (!await colExists$11(db, "dispatch_queue", "pinned_agent_id")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN pinned_agent_id TEXT`.execute(db);
|
|
4450
|
+
if (!await colExists$11(db, "execution_jobs", "base_job_name")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN base_job_name TEXT`.execute(db);
|
|
4451
|
+
if (!await colExists$11(db, "execution_jobs", "variant_kind")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_kind TEXT`.execute(db);
|
|
4452
|
+
if (!await colExists$11(db, "execution_jobs", "variant_label")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_label TEXT`.execute(db);
|
|
4442
4453
|
await sql`
|
|
4443
4454
|
CREATE INDEX IF NOT EXISTS idx_dispatch_queue_pinned_agent
|
|
4444
4455
|
ON public.dispatch_queue (pinned_agent_id)
|
|
4445
4456
|
WHERE pinned_agent_id IS NOT NULL
|
|
4446
4457
|
`.execute(db);
|
|
4447
4458
|
}
|
|
4448
|
-
async function down$
|
|
4459
|
+
async function down$26(db) {
|
|
4449
4460
|
await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_pinned_agent`.execute(db);
|
|
4450
4461
|
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS pinned_agent_id`.execute(db);
|
|
4451
4462
|
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS variant_label`.execute(db);
|
|
@@ -4455,8 +4466,8 @@ async function down$20(db) {
|
|
|
4455
4466
|
//#endregion
|
|
4456
4467
|
//#region src/db/migrations/041_wave_gated.ts
|
|
4457
4468
|
var _041_wave_gated_exports = /* @__PURE__ */ __exportAll({
|
|
4458
|
-
down: () => down$
|
|
4459
|
-
up: () => up$
|
|
4469
|
+
down: () => down$25,
|
|
4470
|
+
up: () => up$25
|
|
4460
4471
|
});
|
|
4461
4472
|
/**
|
|
4462
4473
|
* Add the rolling fan-out wave-gate columns:
|
|
@@ -4476,7 +4487,7 @@ var _041_wave_gated_exports = /* @__PURE__ */ __exportAll({
|
|
|
4476
4487
|
*
|
|
4477
4488
|
* Idempotent: re-running on a DB that already has the columns is a no-op.
|
|
4478
4489
|
*/
|
|
4479
|
-
async function colExists$
|
|
4490
|
+
async function colExists$10(db, table, name) {
|
|
4480
4491
|
return (await sql`
|
|
4481
4492
|
SELECT EXISTS (
|
|
4482
4493
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4486,16 +4497,16 @@ async function colExists$8(db, table, name) {
|
|
|
4486
4497
|
) AS exists
|
|
4487
4498
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
4488
4499
|
}
|
|
4489
|
-
async function up$
|
|
4490
|
-
if (!await colExists$
|
|
4491
|
-
if (!await colExists$
|
|
4492
|
-
if (!await colExists$
|
|
4500
|
+
async function up$25(db) {
|
|
4501
|
+
if (!await colExists$10(db, "execution_jobs", "wave_gated")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_gated boolean NOT NULL DEFAULT false`.execute(db);
|
|
4502
|
+
if (!await colExists$10(db, "execution_jobs", "wave_max_parallel")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_max_parallel integer`.execute(db);
|
|
4503
|
+
if (!await colExists$10(db, "execution_jobs", "wave_fail_fast")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_fail_fast boolean`.execute(db);
|
|
4493
4504
|
await sql`
|
|
4494
4505
|
CREATE INDEX IF NOT EXISTS idx_execution_jobs_wave
|
|
4495
4506
|
ON public.execution_jobs (run_id, base_job_name, wave_gated)
|
|
4496
4507
|
`.execute(db);
|
|
4497
4508
|
}
|
|
4498
|
-
async function down$
|
|
4509
|
+
async function down$25(db) {
|
|
4499
4510
|
await sql`DROP INDEX IF EXISTS public.idx_execution_jobs_wave`.execute(db);
|
|
4500
4511
|
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS wave_fail_fast`.execute(db);
|
|
4501
4512
|
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS wave_max_parallel`.execute(db);
|
|
@@ -4504,8 +4515,8 @@ async function down$19(db) {
|
|
|
4504
4515
|
//#endregion
|
|
4505
4516
|
//#region src/db/migrations/042_dispatch_queue_patterns.ts
|
|
4506
4517
|
var _042_dispatch_queue_patterns_exports = /* @__PURE__ */ __exportAll({
|
|
4507
|
-
down: () => down$
|
|
4508
|
-
up: () => up$
|
|
4518
|
+
down: () => down$24,
|
|
4519
|
+
up: () => up$24
|
|
4509
4520
|
});
|
|
4510
4521
|
/**
|
|
4511
4522
|
* Add pattern columns to dispatch_queue. Exact labels stay in runs_on_labels /
|
|
@@ -4521,7 +4532,7 @@ var _042_dispatch_queue_patterns_exports = /* @__PURE__ */ __exportAll({
|
|
|
4521
4532
|
*
|
|
4522
4533
|
* Idempotent: re-running on a DB that already has the columns is a no-op.
|
|
4523
4534
|
*/
|
|
4524
|
-
async function colExists$
|
|
4535
|
+
async function colExists$9(db, table, name) {
|
|
4525
4536
|
return (await sql`
|
|
4526
4537
|
SELECT EXISTS (
|
|
4527
4538
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4531,19 +4542,19 @@ async function colExists$7(db, table, name) {
|
|
|
4531
4542
|
) AS exists
|
|
4532
4543
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
4533
4544
|
}
|
|
4534
|
-
async function up$
|
|
4535
|
-
if (!await colExists$
|
|
4536
|
-
if (!await colExists$
|
|
4545
|
+
async function up$24(db) {
|
|
4546
|
+
if (!await colExists$9(db, "dispatch_queue", "runs_on_patterns")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN runs_on_patterns jsonb NOT NULL DEFAULT '[]'::jsonb`.execute(db);
|
|
4547
|
+
if (!await colExists$9(db, "dispatch_queue", "exclude_patterns")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN exclude_patterns jsonb NOT NULL DEFAULT '[]'::jsonb`.execute(db);
|
|
4537
4548
|
}
|
|
4538
|
-
async function down$
|
|
4549
|
+
async function down$24(db) {
|
|
4539
4550
|
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS exclude_patterns`.execute(db);
|
|
4540
4551
|
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS runs_on_patterns`.execute(db);
|
|
4541
4552
|
}
|
|
4542
4553
|
//#endregion
|
|
4543
4554
|
//#region src/db/migrations/043_rerouted_to_peer.ts
|
|
4544
4555
|
var _043_rerouted_to_peer_exports = /* @__PURE__ */ __exportAll({
|
|
4545
|
-
down: () => down$
|
|
4546
|
-
up: () => up$
|
|
4556
|
+
down: () => down$23,
|
|
4557
|
+
up: () => up$23
|
|
4547
4558
|
});
|
|
4548
4559
|
/**
|
|
4549
4560
|
* Add a nullable `rerouted_to_peer text` marker to execution_jobs. Non-null
|
|
@@ -4554,7 +4565,7 @@ var _043_rerouted_to_peer_exports = /* @__PURE__ */ __exportAll({
|
|
|
4554
4565
|
*
|
|
4555
4566
|
* Idempotent: re-running on a DB that already has the column is a no-op.
|
|
4556
4567
|
*/
|
|
4557
|
-
async function colExists$
|
|
4568
|
+
async function colExists$8(db, table, name) {
|
|
4558
4569
|
return (await sql`
|
|
4559
4570
|
SELECT EXISTS (
|
|
4560
4571
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4564,17 +4575,17 @@ async function colExists$6(db, table, name) {
|
|
|
4564
4575
|
) AS exists
|
|
4565
4576
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
4566
4577
|
}
|
|
4567
|
-
async function up$
|
|
4568
|
-
if (!await colExists$
|
|
4578
|
+
async function up$23(db) {
|
|
4579
|
+
if (!await colExists$8(db, "execution_jobs", "rerouted_to_peer")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN rerouted_to_peer text`.execute(db);
|
|
4569
4580
|
}
|
|
4570
|
-
async function down$
|
|
4581
|
+
async function down$23(db) {
|
|
4571
4582
|
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS rerouted_to_peer`.execute(db);
|
|
4572
4583
|
}
|
|
4573
4584
|
//#endregion
|
|
4574
4585
|
//#region src/db/migrations/044_check_mode.ts
|
|
4575
4586
|
var _044_check_mode_exports = /* @__PURE__ */ __exportAll({
|
|
4576
|
-
down: () => down$
|
|
4577
|
-
up: () => up$
|
|
4587
|
+
down: () => down$22,
|
|
4588
|
+
up: () => up$22
|
|
4578
4589
|
});
|
|
4579
4590
|
/**
|
|
4580
4591
|
* Add idempotent check-mode columns:
|
|
@@ -4591,7 +4602,7 @@ var _044_check_mode_exports = /* @__PURE__ */ __exportAll({
|
|
|
4591
4602
|
* All nullable. Idempotent: re-running on a DB that already has the columns is
|
|
4592
4603
|
* a no-op.
|
|
4593
4604
|
*/
|
|
4594
|
-
async function colExists$
|
|
4605
|
+
async function colExists$7(db, table, name) {
|
|
4595
4606
|
return (await sql`
|
|
4596
4607
|
SELECT EXISTS (
|
|
4597
4608
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4601,13 +4612,13 @@ async function colExists$5(db, table, name) {
|
|
|
4601
4612
|
) AS exists
|
|
4602
4613
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
4603
4614
|
}
|
|
4604
|
-
async function up$
|
|
4605
|
-
if (!await colExists$
|
|
4606
|
-
if (!await colExists$
|
|
4607
|
-
if (!await colExists$
|
|
4608
|
-
if (!await colExists$
|
|
4615
|
+
async function up$22(db) {
|
|
4616
|
+
if (!await colExists$7(db, "execution_runs", "check_mode")) await sql`ALTER TABLE public.execution_runs ADD COLUMN check_mode text`.execute(db);
|
|
4617
|
+
if (!await colExists$7(db, "execution_steps", "check_outcome")) await sql`ALTER TABLE public.execution_steps ADD COLUMN check_outcome text`.execute(db);
|
|
4618
|
+
if (!await colExists$7(db, "execution_steps", "drift_summary")) await sql`ALTER TABLE public.execution_steps ADD COLUMN drift_summary text`.execute(db);
|
|
4619
|
+
if (!await colExists$7(db, "execution_steps", "drift")) await sql`ALTER TABLE public.execution_steps ADD COLUMN drift jsonb`.execute(db);
|
|
4609
4620
|
}
|
|
4610
|
-
async function down$
|
|
4621
|
+
async function down$22(db) {
|
|
4611
4622
|
await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS drift`.execute(db);
|
|
4612
4623
|
await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS drift_summary`.execute(db);
|
|
4613
4624
|
await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS check_outcome`.execute(db);
|
|
@@ -4616,8 +4627,8 @@ async function down$16(db) {
|
|
|
4616
4627
|
//#endregion
|
|
4617
4628
|
//#region src/db/migrations/045_host_properties.ts
|
|
4618
4629
|
var _045_host_properties_exports = /* @__PURE__ */ __exportAll({
|
|
4619
|
-
down: () => down$
|
|
4620
|
-
up: () => up$
|
|
4630
|
+
down: () => down$21,
|
|
4631
|
+
up: () => up$21
|
|
4621
4632
|
});
|
|
4622
4633
|
/**
|
|
4623
4634
|
* Add the typed host-vars dimension to the host roster:
|
|
@@ -4631,18 +4642,18 @@ var _045_host_properties_exports = /* @__PURE__ */ __exportAll({
|
|
|
4631
4642
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`): re-running on a DB that already has
|
|
4632
4643
|
* the column is a no-op. Staging data is preserved (additive column).
|
|
4633
4644
|
*/
|
|
4634
|
-
async function up$
|
|
4645
|
+
async function up$21(db) {
|
|
4635
4646
|
await sql`ALTER TABLE public.host_roster
|
|
4636
4647
|
ADD COLUMN IF NOT EXISTS host_properties jsonb NOT NULL DEFAULT '{}'::jsonb`.execute(db);
|
|
4637
4648
|
}
|
|
4638
|
-
async function down$
|
|
4649
|
+
async function down$21(db) {
|
|
4639
4650
|
await sql`ALTER TABLE public.host_roster DROP COLUMN IF EXISTS host_properties`.execute(db);
|
|
4640
4651
|
}
|
|
4641
4652
|
//#endregion
|
|
4642
4653
|
//#region src/db/migrations/046_join_token_consumed_by_instance.ts
|
|
4643
4654
|
var _046_join_token_consumed_by_instance_exports = /* @__PURE__ */ __exportAll({
|
|
4644
|
-
down: () => down$
|
|
4645
|
-
up: () => up$
|
|
4655
|
+
down: () => down$20,
|
|
4656
|
+
up: () => up$20
|
|
4646
4657
|
});
|
|
4647
4658
|
/**
|
|
4648
4659
|
* Record the joining peer's instanceId at first consumption of a join token:
|
|
@@ -4657,18 +4668,18 @@ var _046_join_token_consumed_by_instance_exports = /* @__PURE__ */ __exportAll({
|
|
|
4657
4668
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`): re-running on a DB that already has
|
|
4658
4669
|
* the column is a no-op. Staging data is preserved (additive nullable column).
|
|
4659
4670
|
*/
|
|
4660
|
-
async function up$
|
|
4671
|
+
async function up$20(db) {
|
|
4661
4672
|
await sql`ALTER TABLE public.join_tokens
|
|
4662
4673
|
ADD COLUMN IF NOT EXISTS consumed_by_instance text`.execute(db);
|
|
4663
4674
|
}
|
|
4664
|
-
async function down$
|
|
4675
|
+
async function down$20(db) {
|
|
4665
4676
|
await sql`ALTER TABLE public.join_tokens DROP COLUMN IF EXISTS consumed_by_instance`.execute(db);
|
|
4666
4677
|
}
|
|
4667
4678
|
//#endregion
|
|
4668
4679
|
//#region src/db/migrations/047_needs_run_on.ts
|
|
4669
4680
|
var _047_needs_run_on_exports = /* @__PURE__ */ __exportAll({
|
|
4670
|
-
down: () => down$
|
|
4671
|
-
up: () => up$
|
|
4681
|
+
down: () => down$19,
|
|
4682
|
+
up: () => up$19
|
|
4672
4683
|
});
|
|
4673
4684
|
/**
|
|
4674
4685
|
* Migrate `execution_job_needs` from the binary `if_failed` policy to a
|
|
@@ -4693,7 +4704,7 @@ const ALL_TERMINAL_JSON = JSON.stringify([
|
|
|
4693
4704
|
"drift_dropped"
|
|
4694
4705
|
]);
|
|
4695
4706
|
const SUCCESS_ONLY_JSON = JSON.stringify(["success"]);
|
|
4696
|
-
async function colExists$
|
|
4707
|
+
async function colExists$6(db, table, name) {
|
|
4697
4708
|
return (await sql`
|
|
4698
4709
|
SELECT EXISTS (
|
|
4699
4710
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4704,12 +4715,12 @@ async function colExists$4(db, table, name) {
|
|
|
4704
4715
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
4705
4716
|
}
|
|
4706
4717
|
const SUCCESS_ONLY_DEFAULT = `'${SUCCESS_ONLY_JSON}'`;
|
|
4707
|
-
async function up$
|
|
4708
|
-
if (!await colExists$
|
|
4718
|
+
async function up$19(db) {
|
|
4719
|
+
if (!await colExists$6(db, "execution_job_needs", "run_on")) await sql`
|
|
4709
4720
|
ALTER TABLE public.execution_job_needs
|
|
4710
4721
|
ADD COLUMN run_on text NOT NULL DEFAULT ${sql.raw(SUCCESS_ONLY_DEFAULT)}
|
|
4711
4722
|
`.execute(db);
|
|
4712
|
-
if (await colExists$
|
|
4723
|
+
if (await colExists$6(db, "execution_job_needs", "if_failed")) {
|
|
4713
4724
|
await sql`
|
|
4714
4725
|
UPDATE public.execution_job_needs
|
|
4715
4726
|
SET run_on = ${ALL_TERMINAL_JSON}
|
|
@@ -4723,12 +4734,12 @@ async function up$13(db) {
|
|
|
4723
4734
|
await sql`ALTER TABLE public.execution_job_needs DROP COLUMN if_failed`.execute(db);
|
|
4724
4735
|
}
|
|
4725
4736
|
}
|
|
4726
|
-
async function down$
|
|
4727
|
-
if (!await colExists$
|
|
4737
|
+
async function down$19(db) {
|
|
4738
|
+
if (!await colExists$6(db, "execution_job_needs", "if_failed")) await sql`
|
|
4728
4739
|
ALTER TABLE public.execution_job_needs
|
|
4729
4740
|
ADD COLUMN if_failed text NOT NULL DEFAULT 'skip'
|
|
4730
4741
|
`.execute(db);
|
|
4731
|
-
if (await colExists$
|
|
4742
|
+
if (await colExists$6(db, "execution_job_needs", "run_on")) {
|
|
4732
4743
|
await sql`
|
|
4733
4744
|
UPDATE public.execution_job_needs
|
|
4734
4745
|
SET if_failed = CASE WHEN run_on = ${SUCCESS_ONLY_JSON} THEN 'skip' ELSE 'run' END
|
|
@@ -4739,8 +4750,8 @@ async function down$13(db) {
|
|
|
4739
4750
|
//#endregion
|
|
4740
4751
|
//#region src/db/migrations/048_host_reboot_pending.ts
|
|
4741
4752
|
var _048_host_reboot_pending_exports = /* @__PURE__ */ __exportAll({
|
|
4742
|
-
down: () => down$
|
|
4743
|
-
up: () => up$
|
|
4753
|
+
down: () => down$18,
|
|
4754
|
+
up: () => up$18
|
|
4744
4755
|
});
|
|
4745
4756
|
/**
|
|
4746
4757
|
* Add `host_roster.reboot_pending_until timestamptz NULL` — the persisted
|
|
@@ -4757,18 +4768,18 @@ var _048_host_reboot_pending_exports = /* @__PURE__ */ __exportAll({
|
|
|
4757
4768
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
4758
4769
|
* preserved.
|
|
4759
4770
|
*/
|
|
4760
|
-
async function up$
|
|
4771
|
+
async function up$18(db) {
|
|
4761
4772
|
await sql`ALTER TABLE public.host_roster
|
|
4762
4773
|
ADD COLUMN IF NOT EXISTS reboot_pending_until timestamptz`.execute(db);
|
|
4763
4774
|
}
|
|
4764
|
-
async function down$
|
|
4775
|
+
async function down$18(db) {
|
|
4765
4776
|
await sql`ALTER TABLE public.host_roster DROP COLUMN IF EXISTS reboot_pending_until`.execute(db);
|
|
4766
4777
|
}
|
|
4767
4778
|
//#endregion
|
|
4768
4779
|
//#region src/db/migrations/049_held_runs_payload.ts
|
|
4769
4780
|
var _049_held_runs_payload_exports = /* @__PURE__ */ __exportAll({
|
|
4770
|
-
down: () => down$
|
|
4771
|
-
up: () => up$
|
|
4781
|
+
down: () => down$17,
|
|
4782
|
+
up: () => up$17
|
|
4772
4783
|
});
|
|
4773
4784
|
/**
|
|
4774
4785
|
* Add `held_runs.payload jsonb NULL` — the drift payload captured when a
|
|
@@ -4780,18 +4791,18 @@ var _049_held_runs_payload_exports = /* @__PURE__ */ __exportAll({
|
|
|
4780
4791
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
4781
4792
|
* preserved.
|
|
4782
4793
|
*/
|
|
4783
|
-
async function up$
|
|
4794
|
+
async function up$17(db) {
|
|
4784
4795
|
await sql`ALTER TABLE public.held_runs
|
|
4785
4796
|
ADD COLUMN IF NOT EXISTS payload jsonb`.execute(db);
|
|
4786
4797
|
}
|
|
4787
|
-
async function down$
|
|
4798
|
+
async function down$17(db) {
|
|
4788
4799
|
await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS payload`.execute(db);
|
|
4789
4800
|
}
|
|
4790
4801
|
//#endregion
|
|
4791
4802
|
//#region src/db/migrations/050_sources_slug.ts
|
|
4792
4803
|
var _050_sources_slug_exports = /* @__PURE__ */ __exportAll({
|
|
4793
|
-
down: () => down$
|
|
4794
|
-
up: () => up$
|
|
4804
|
+
down: () => down$16,
|
|
4805
|
+
up: () => up$16
|
|
4795
4806
|
});
|
|
4796
4807
|
/**
|
|
4797
4808
|
* Add `sources.slug TEXT NULL` — the GitHub App slug (the URL-safe identifier
|
|
@@ -4806,18 +4817,18 @@ var _050_sources_slug_exports = /* @__PURE__ */ __exportAll({
|
|
|
4806
4817
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
4807
4818
|
* preserved.
|
|
4808
4819
|
*/
|
|
4809
|
-
async function up$
|
|
4820
|
+
async function up$16(db) {
|
|
4810
4821
|
await sql`ALTER TABLE public.sources
|
|
4811
4822
|
ADD COLUMN IF NOT EXISTS slug text`.execute(db);
|
|
4812
4823
|
}
|
|
4813
|
-
async function down$
|
|
4824
|
+
async function down$16(db) {
|
|
4814
4825
|
await sql`ALTER TABLE public.sources DROP COLUMN IF EXISTS slug`.execute(db);
|
|
4815
4826
|
}
|
|
4816
4827
|
//#endregion
|
|
4817
4828
|
//#region src/db/migrations/051_binding_host_pattern.ts
|
|
4818
4829
|
var _051_binding_host_pattern_exports = /* @__PURE__ */ __exportAll({
|
|
4819
|
-
down: () => down$
|
|
4820
|
-
up: () => up$
|
|
4830
|
+
down: () => down$15,
|
|
4831
|
+
up: () => up$15
|
|
4821
4832
|
});
|
|
4822
4833
|
/**
|
|
4823
4834
|
* Add a per-host dimension to environment secret bindings:
|
|
@@ -4833,7 +4844,7 @@ var _051_binding_host_pattern_exports = /* @__PURE__ */ __exportAll({
|
|
|
4833
4844
|
* Idempotent: re-running on a DB that already has the column / index is a no-op.
|
|
4834
4845
|
* Additive — staging data is preserved.
|
|
4835
4846
|
*/
|
|
4836
|
-
async function up$
|
|
4847
|
+
async function up$15(db) {
|
|
4837
4848
|
await sql`ALTER TABLE public.environment_bindings
|
|
4838
4849
|
ADD COLUMN IF NOT EXISTS host_pattern text NOT NULL DEFAULT '**'`.execute(db);
|
|
4839
4850
|
await sql`ALTER TABLE public.environment_bindings
|
|
@@ -4841,7 +4852,7 @@ async function up$9(db) {
|
|
|
4841
4852
|
await sql`CREATE UNIQUE INDEX IF NOT EXISTS environment_bindings_env_scope_host_uniq
|
|
4842
4853
|
ON public.environment_bindings (environment_id, scope_pattern, host_pattern)`.execute(db);
|
|
4843
4854
|
}
|
|
4844
|
-
async function down$
|
|
4855
|
+
async function down$15(db) {
|
|
4845
4856
|
await sql`DROP INDEX IF EXISTS public.environment_bindings_env_scope_host_uniq`.execute(db);
|
|
4846
4857
|
await sql`ALTER TABLE public.environment_bindings
|
|
4847
4858
|
DROP COLUMN IF EXISTS host_pattern`.execute(db);
|
|
@@ -4851,8 +4862,8 @@ async function down$9(db) {
|
|
|
4851
4862
|
//#endregion
|
|
4852
4863
|
//#region src/db/migrations/052_host_reach_metadata.ts
|
|
4853
4864
|
var _052_host_reach_metadata_exports = /* @__PURE__ */ __exportAll({
|
|
4854
|
-
down: () => down$
|
|
4855
|
-
up: () => up$
|
|
4865
|
+
down: () => down$14,
|
|
4866
|
+
up: () => up$14
|
|
4856
4867
|
});
|
|
4857
4868
|
/**
|
|
4858
4869
|
* Add pre-agent reach metadata to the host roster so a declared host (no agent
|
|
@@ -4869,14 +4880,14 @@ var _052_host_reach_metadata_exports = /* @__PURE__ */ __exportAll({
|
|
|
4869
4880
|
* behaves exactly as before. Idempotent (`ADD COLUMN IF NOT EXISTS`); additive,
|
|
4870
4881
|
* so staging data is preserved.
|
|
4871
4882
|
*/
|
|
4872
|
-
async function up$
|
|
4883
|
+
async function up$14(db) {
|
|
4873
4884
|
await sql`ALTER TABLE public.host_roster
|
|
4874
4885
|
ADD COLUMN IF NOT EXISTS address text,
|
|
4875
4886
|
ADD COLUMN IF NOT EXISTS ssh_user text,
|
|
4876
4887
|
ADD COLUMN IF NOT EXISTS ssh_port integer,
|
|
4877
4888
|
ADD COLUMN IF NOT EXISTS ssh_key_secret text`.execute(db);
|
|
4878
4889
|
}
|
|
4879
|
-
async function down$
|
|
4890
|
+
async function down$14(db) {
|
|
4880
4891
|
await sql`ALTER TABLE public.host_roster
|
|
4881
4892
|
DROP COLUMN IF EXISTS address,
|
|
4882
4893
|
DROP COLUMN IF EXISTS ssh_user,
|
|
@@ -4886,8 +4897,8 @@ async function down$8(db) {
|
|
|
4886
4897
|
//#endregion
|
|
4887
4898
|
//#region src/db/migrations/053_agent_token_single_use.ts
|
|
4888
4899
|
var _053_agent_token_single_use_exports = /* @__PURE__ */ __exportAll({
|
|
4889
|
-
down: () => down$
|
|
4890
|
-
up: () => up$
|
|
4900
|
+
down: () => down$13,
|
|
4901
|
+
up: () => up$13
|
|
4891
4902
|
});
|
|
4892
4903
|
/**
|
|
4893
4904
|
* Add a single-use marker to agent tokens for the bootstrap (init-runner)
|
|
@@ -4902,19 +4913,19 @@ var _053_agent_token_single_use_exports = /* @__PURE__ */ __exportAll({
|
|
|
4902
4913
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
4903
4914
|
* preserved.
|
|
4904
4915
|
*/
|
|
4905
|
-
async function up$
|
|
4916
|
+
async function up$13(db) {
|
|
4906
4917
|
await sql`ALTER TABLE public.agent_tokens
|
|
4907
4918
|
ADD COLUMN IF NOT EXISTS consumed_at timestamptz`.execute(db);
|
|
4908
4919
|
}
|
|
4909
|
-
async function down$
|
|
4920
|
+
async function down$13(db) {
|
|
4910
4921
|
await sql`ALTER TABLE public.agent_tokens
|
|
4911
4922
|
DROP COLUMN IF EXISTS consumed_at`.execute(db);
|
|
4912
4923
|
}
|
|
4913
4924
|
//#endregion
|
|
4914
4925
|
//#region src/db/migrations/054_local_working_tree.ts
|
|
4915
4926
|
var _054_local_working_tree_exports = /* @__PURE__ */ __exportAll({
|
|
4916
|
-
down: () => down$
|
|
4917
|
-
up: () => up$
|
|
4927
|
+
down: () => down$12,
|
|
4928
|
+
up: () => up$12
|
|
4918
4929
|
});
|
|
4919
4930
|
/**
|
|
4920
4931
|
* Mark runs that executed an uploaded local working tree (`kici run remote`):
|
|
@@ -4927,19 +4938,19 @@ var _054_local_working_tree_exports = /* @__PURE__ */ __exportAll({
|
|
|
4927
4938
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive with a default, so staging
|
|
4928
4939
|
* data is preserved.
|
|
4929
4940
|
*/
|
|
4930
|
-
async function up$
|
|
4941
|
+
async function up$12(db) {
|
|
4931
4942
|
await sql`ALTER TABLE public.execution_runs
|
|
4932
4943
|
ADD COLUMN IF NOT EXISTS local_working_tree boolean NOT NULL DEFAULT false`.execute(db);
|
|
4933
4944
|
}
|
|
4934
|
-
async function down$
|
|
4945
|
+
async function down$12(db) {
|
|
4935
4946
|
await sql`ALTER TABLE public.execution_runs
|
|
4936
4947
|
DROP COLUMN IF EXISTS local_working_tree`.execute(db);
|
|
4937
4948
|
}
|
|
4938
4949
|
//#endregion
|
|
4939
4950
|
//#region src/db/migrations/055_agent_token_mandatory_labels.ts
|
|
4940
4951
|
var _055_agent_token_mandatory_labels_exports = /* @__PURE__ */ __exportAll({
|
|
4941
|
-
down: () => down$
|
|
4942
|
-
up: () => up$
|
|
4952
|
+
down: () => down$11,
|
|
4953
|
+
up: () => up$11
|
|
4943
4954
|
});
|
|
4944
4955
|
/**
|
|
4945
4956
|
* Add a token-bound mandatory-label taint to agent tokens:
|
|
@@ -4955,19 +4966,19 @@ var _055_agent_token_mandatory_labels_exports = /* @__PURE__ */ __exportAll({
|
|
|
4955
4966
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
4956
4967
|
* preserved.
|
|
4957
4968
|
*/
|
|
4958
|
-
async function up$
|
|
4969
|
+
async function up$11(db) {
|
|
4959
4970
|
await sql`ALTER TABLE public.agent_tokens
|
|
4960
4971
|
ADD COLUMN IF NOT EXISTS mandatory_labels text`.execute(db);
|
|
4961
4972
|
}
|
|
4962
|
-
async function down$
|
|
4973
|
+
async function down$11(db) {
|
|
4963
4974
|
await sql`ALTER TABLE public.agent_tokens
|
|
4964
4975
|
DROP COLUMN IF EXISTS mandatory_labels`.execute(db);
|
|
4965
4976
|
}
|
|
4966
4977
|
//#endregion
|
|
4967
4978
|
//#region src/db/migrations/056_execution_jobs_environments.ts
|
|
4968
4979
|
var _056_execution_jobs_environments_exports = /* @__PURE__ */ __exportAll({
|
|
4969
|
-
down: () => down$
|
|
4970
|
-
up: () => up$
|
|
4980
|
+
down: () => down$10,
|
|
4981
|
+
up: () => up$10
|
|
4971
4982
|
});
|
|
4972
4983
|
/**
|
|
4973
4984
|
* Add a per-job bound deployment-environment list to `execution_jobs`:
|
|
@@ -4982,19 +4993,19 @@ var _056_execution_jobs_environments_exports = /* @__PURE__ */ __exportAll({
|
|
|
4982
4993
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
4983
4994
|
* preserved.
|
|
4984
4995
|
*/
|
|
4985
|
-
async function up$
|
|
4996
|
+
async function up$10(db) {
|
|
4986
4997
|
await sql`ALTER TABLE public.execution_jobs
|
|
4987
4998
|
ADD COLUMN IF NOT EXISTS environments text`.execute(db);
|
|
4988
4999
|
}
|
|
4989
|
-
async function down$
|
|
5000
|
+
async function down$10(db) {
|
|
4990
5001
|
await sql`ALTER TABLE public.execution_jobs
|
|
4991
5002
|
DROP COLUMN IF EXISTS environments`.execute(db);
|
|
4992
5003
|
}
|
|
4993
5004
|
//#endregion
|
|
4994
5005
|
//#region src/db/migrations/057_step_concurrency.ts
|
|
4995
5006
|
var _057_step_concurrency_exports = /* @__PURE__ */ __exportAll({
|
|
4996
|
-
down: () => down$
|
|
4997
|
-
up: () => up$
|
|
5007
|
+
down: () => down$9,
|
|
5008
|
+
up: () => up$9
|
|
4998
5009
|
});
|
|
4999
5010
|
/**
|
|
5000
5011
|
* Add parallel step-group concurrency columns to `execution_steps`:
|
|
@@ -5007,6 +5018,75 @@ var _057_step_concurrency_exports = /* @__PURE__ */ __exportAll({
|
|
|
5007
5018
|
* Both nullable, no backfill. Idempotent: re-running on a DB that already has the
|
|
5008
5019
|
* columns is a no-op.
|
|
5009
5020
|
*/
|
|
5021
|
+
async function colExists$5(db, table, name) {
|
|
5022
|
+
return (await sql`
|
|
5023
|
+
SELECT EXISTS (
|
|
5024
|
+
SELECT 1 FROM information_schema.columns
|
|
5025
|
+
WHERE table_schema = 'public'
|
|
5026
|
+
AND table_name = ${table}
|
|
5027
|
+
AND column_name = ${name}
|
|
5028
|
+
) AS exists
|
|
5029
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
5030
|
+
}
|
|
5031
|
+
async function up$9(db) {
|
|
5032
|
+
if (!await colExists$5(db, "execution_steps", "concurrency_kind")) await sql`ALTER TABLE public.execution_steps ADD COLUMN concurrency_kind text`.execute(db);
|
|
5033
|
+
if (!await colExists$5(db, "execution_steps", "group_id")) await sql`ALTER TABLE public.execution_steps ADD COLUMN group_id text`.execute(db);
|
|
5034
|
+
}
|
|
5035
|
+
async function down$9(db) {
|
|
5036
|
+
await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS group_id`.execute(db);
|
|
5037
|
+
await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS concurrency_kind`.execute(db);
|
|
5038
|
+
}
|
|
5039
|
+
//#endregion
|
|
5040
|
+
//#region src/db/migrations/058_access_log_agent_label.ts
|
|
5041
|
+
var _058_access_log_agent_label_exports = /* @__PURE__ */ __exportAll({
|
|
5042
|
+
down: () => down$8,
|
|
5043
|
+
up: () => up$8
|
|
5044
|
+
});
|
|
5045
|
+
/**
|
|
5046
|
+
* Add `access_log.agent_label text` — the human-set name of the agent that
|
|
5047
|
+
* performed the action, when the actor authenticated with an agent-kind PAT.
|
|
5048
|
+
*
|
|
5049
|
+
* Queryable column (not just baked into `actor_meta`) so an operator can filter
|
|
5050
|
+
* the access log by agent. NULL for ordinary human / API-key / system actors.
|
|
5051
|
+
* Nullable, no backfill. Idempotent.
|
|
5052
|
+
*/
|
|
5053
|
+
async function colExists$4(db, table, name) {
|
|
5054
|
+
return (await sql`
|
|
5055
|
+
SELECT EXISTS (
|
|
5056
|
+
SELECT 1 FROM information_schema.columns
|
|
5057
|
+
WHERE table_schema = 'public'
|
|
5058
|
+
AND table_name = ${table}
|
|
5059
|
+
AND column_name = ${name}
|
|
5060
|
+
) AS exists
|
|
5061
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
5062
|
+
}
|
|
5063
|
+
async function up$8(db) {
|
|
5064
|
+
if (!await colExists$4(db, "access_log", "agent_label")) await sql`ALTER TABLE public.access_log ADD COLUMN agent_label text`.execute(db);
|
|
5065
|
+
}
|
|
5066
|
+
async function down$8(db) {
|
|
5067
|
+
await sql`ALTER TABLE public.access_log DROP COLUMN IF EXISTS agent_label`.execute(db);
|
|
5068
|
+
}
|
|
5069
|
+
//#endregion
|
|
5070
|
+
//#region src/db/migrations/059_attestation_verdict.ts
|
|
5071
|
+
var _059_attestation_verdict_exports = /* @__PURE__ */ __exportAll({
|
|
5072
|
+
down: () => down$7,
|
|
5073
|
+
up: () => up$7
|
|
5074
|
+
});
|
|
5075
|
+
/**
|
|
5076
|
+
* Add server-side verification verdict columns to `attestations`, plus the
|
|
5077
|
+
* lookup indexes the org-wide attestations browser queries against.
|
|
5078
|
+
*
|
|
5079
|
+
* The orchestrator verifies each provenance bundle at ingest and records the
|
|
5080
|
+
* verdict here so the dashboard list shows trustworthy badges with no per-row
|
|
5081
|
+
* bundle fetch:
|
|
5082
|
+
* - `verify_status` — one of `verified` / `failed` / `unverifiable` / `pending`
|
|
5083
|
+
* (mirrors `attestationVerifyStatusSchema.enum.*` in `@kici-dev/engine`).
|
|
5084
|
+
* `DEFAULT 'pending'` covers rows written before a verdict is computed.
|
|
5085
|
+
* - `verify_reason` — first failure code from `verifyKiciBundle` (nullable).
|
|
5086
|
+
* - `verified_at` — when the verdict was recorded (nullable).
|
|
5087
|
+
*
|
|
5088
|
+
* Idempotent: guarded on the `verify_status` column's existence.
|
|
5089
|
+
*/
|
|
5010
5090
|
async function colExists$3(db, table, name) {
|
|
5011
5091
|
return (await sql`
|
|
5012
5092
|
SELECT EXISTS (
|
|
@@ -5017,142 +5097,329 @@ async function colExists$3(db, table, name) {
|
|
|
5017
5097
|
) AS exists
|
|
5018
5098
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
5019
5099
|
}
|
|
5100
|
+
async function up$7(db) {
|
|
5101
|
+
if (await colExists$3(db, "attestations", "verify_status")) return;
|
|
5102
|
+
await sql`
|
|
5103
|
+
ALTER TABLE public.attestations
|
|
5104
|
+
ADD COLUMN verify_status TEXT NOT NULL DEFAULT 'pending',
|
|
5105
|
+
ADD COLUMN verify_reason TEXT,
|
|
5106
|
+
ADD COLUMN verified_at TIMESTAMPTZ
|
|
5107
|
+
`.execute(db);
|
|
5108
|
+
await sql`CREATE INDEX idx_attestations_subject_digest ON public.attestations (subject_digest)`.execute(db);
|
|
5109
|
+
await sql`CREATE INDEX idx_attestations_subject_name ON public.attestations (subject_name)`.execute(db);
|
|
5110
|
+
await sql`CREATE INDEX idx_attestations_verify_status ON public.attestations (verify_status)`.execute(db);
|
|
5111
|
+
await sql`CREATE INDEX idx_attestations_created_at ON public.attestations (created_at)`.execute(db);
|
|
5112
|
+
}
|
|
5113
|
+
async function down$7(db) {
|
|
5114
|
+
await sql`DROP INDEX IF EXISTS idx_attestations_created_at`.execute(db);
|
|
5115
|
+
await sql`DROP INDEX IF EXISTS idx_attestations_verify_status`.execute(db);
|
|
5116
|
+
await sql`DROP INDEX IF EXISTS idx_attestations_subject_name`.execute(db);
|
|
5117
|
+
await sql`DROP INDEX IF EXISTS idx_attestations_subject_digest`.execute(db);
|
|
5118
|
+
await sql`
|
|
5119
|
+
ALTER TABLE public.attestations
|
|
5120
|
+
DROP COLUMN IF EXISTS verify_status,
|
|
5121
|
+
DROP COLUMN IF EXISTS verify_reason,
|
|
5122
|
+
DROP COLUMN IF EXISTS verified_at
|
|
5123
|
+
`.execute(db);
|
|
5124
|
+
}
|
|
5125
|
+
//#endregion
|
|
5126
|
+
//#region src/db/migrations/060_run_trigger_actor.ts
|
|
5127
|
+
var _060_run_trigger_actor_exports = /* @__PURE__ */ __exportAll({
|
|
5128
|
+
down: () => down$6,
|
|
5129
|
+
up: () => up$6
|
|
5130
|
+
});
|
|
5131
|
+
/**
|
|
5132
|
+
* Add the triggering-actor columns to `execution_runs`:
|
|
5133
|
+
*
|
|
5134
|
+
* - `trigger_actor_provider` — the origin provider of the actor identity
|
|
5135
|
+
* (`github` today; provider-generic so GitLab/Bitbucket extend later).
|
|
5136
|
+
* - `trigger_actor_username` — the provider login of the person who triggered
|
|
5137
|
+
* the run (e.g. the GitHub pusher / PR author).
|
|
5138
|
+
* - `trigger_actor_user_id` — the immutable provider user id (mirrors
|
|
5139
|
+
* `identity_links.provider_user_id`), preferred over the mutable username
|
|
5140
|
+
* when resolving the actor to a KiCI user.
|
|
5141
|
+
*
|
|
5142
|
+
* Captured for ALL event types (push/tag/PR/comment), unlike the PR-only
|
|
5143
|
+
* `contributor_username`. Independently useful for a dashboard "triggered by
|
|
5144
|
+
* @x" and audit, beyond actor notifications. All nullable, no backfill,
|
|
5145
|
+
* idempotent.
|
|
5146
|
+
*/
|
|
5147
|
+
async function colExists$2(db, table, name) {
|
|
5148
|
+
return (await sql`
|
|
5149
|
+
SELECT EXISTS (
|
|
5150
|
+
SELECT 1 FROM information_schema.columns
|
|
5151
|
+
WHERE table_schema = 'public'
|
|
5152
|
+
AND table_name = ${table}
|
|
5153
|
+
AND column_name = ${name}
|
|
5154
|
+
) AS exists
|
|
5155
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
5156
|
+
}
|
|
5157
|
+
async function up$6(db) {
|
|
5158
|
+
if (!await colExists$2(db, "execution_runs", "trigger_actor_provider")) await sql`ALTER TABLE public.execution_runs ADD COLUMN trigger_actor_provider text`.execute(db);
|
|
5159
|
+
if (!await colExists$2(db, "execution_runs", "trigger_actor_username")) await sql`ALTER TABLE public.execution_runs ADD COLUMN trigger_actor_username text`.execute(db);
|
|
5160
|
+
if (!await colExists$2(db, "execution_runs", "trigger_actor_user_id")) await sql`ALTER TABLE public.execution_runs ADD COLUMN trigger_actor_user_id text`.execute(db);
|
|
5161
|
+
}
|
|
5162
|
+
async function down$6(db) {
|
|
5163
|
+
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS trigger_actor_provider`.execute(db);
|
|
5164
|
+
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS trigger_actor_username`.execute(db);
|
|
5165
|
+
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS trigger_actor_user_id`.execute(db);
|
|
5166
|
+
}
|
|
5167
|
+
//#endregion
|
|
5168
|
+
//#region src/db/migrations/061_execution_runs_environment_id.ts
|
|
5169
|
+
var _061_execution_runs_environment_id_exports = /* @__PURE__ */ __exportAll({
|
|
5170
|
+
down: () => down$5,
|
|
5171
|
+
up: () => up$5
|
|
5172
|
+
});
|
|
5173
|
+
/**
|
|
5174
|
+
* Add `environment_id` to `execution_runs` so environment run-history can be
|
|
5175
|
+
* queried by the matched environment's id.
|
|
5176
|
+
*
|
|
5177
|
+
* The `environment` column stores the job-declared environment name. For a
|
|
5178
|
+
* glob environment that is the concrete instance string (e.g. `review/PR-123`),
|
|
5179
|
+
* which never equals the environment row's label, so a name-keyed history query
|
|
5180
|
+
* always misses. Recording the matched environment id lets the History tab key
|
|
5181
|
+
* off a stable id instead. The `environment` name column is untouched (it
|
|
5182
|
+
* doubles as the concurrency-group key and the concrete-instance label).
|
|
5183
|
+
*
|
|
5184
|
+
* `ON DELETE SET NULL`: deleting an environment must not delete its run rows.
|
|
5185
|
+
*
|
|
5186
|
+
* Backfill: `execution_runs` has no `org_id`, so a name match is only safe when
|
|
5187
|
+
* exactly one fixed environment carries that name across the whole table. That
|
|
5188
|
+
* backfills 100% of fixed-env history on a single-tenant orchestrator and the
|
|
5189
|
+
* unambiguous majority on a shared one, never mis-attributing across tenants.
|
|
5190
|
+
* Glob-named rows stay null (they never appeared in history before — that is
|
|
5191
|
+
* the bug being fixed — so nothing regresses).
|
|
5192
|
+
*
|
|
5193
|
+
* Idempotent: guarded on the `environment_id` column's existence; the index and
|
|
5194
|
+
* backfill use `IF NOT EXISTS` / `WHERE environment_id IS NULL` so re-running is
|
|
5195
|
+
* a no-op.
|
|
5196
|
+
*/
|
|
5197
|
+
async function colExists$1(db, table, name) {
|
|
5198
|
+
return (await sql`
|
|
5199
|
+
SELECT EXISTS (
|
|
5200
|
+
SELECT 1 FROM information_schema.columns
|
|
5201
|
+
WHERE table_schema = 'public'
|
|
5202
|
+
AND table_name = ${table}
|
|
5203
|
+
AND column_name = ${name}
|
|
5204
|
+
) AS exists
|
|
5205
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
5206
|
+
}
|
|
5207
|
+
async function up$5(db) {
|
|
5208
|
+
if (!await colExists$1(db, "execution_runs", "environment_id")) await sql`
|
|
5209
|
+
ALTER TABLE public.execution_runs
|
|
5210
|
+
ADD COLUMN environment_id uuid
|
|
5211
|
+
REFERENCES public.environments(id) ON DELETE SET NULL
|
|
5212
|
+
`.execute(db);
|
|
5213
|
+
await sql`
|
|
5214
|
+
CREATE INDEX IF NOT EXISTS idx_execution_runs_environment_id
|
|
5215
|
+
ON public.execution_runs (environment_id)
|
|
5216
|
+
`.execute(db);
|
|
5217
|
+
await sql`
|
|
5218
|
+
UPDATE public.execution_runs er
|
|
5219
|
+
SET environment_id = e.id
|
|
5220
|
+
FROM public.environments e
|
|
5221
|
+
WHERE er.environment_id IS NULL
|
|
5222
|
+
AND er.environment IS NOT NULL
|
|
5223
|
+
AND e.type = 'fixed'
|
|
5224
|
+
AND e.name = er.environment
|
|
5225
|
+
AND (SELECT count(*) FROM public.environments e2
|
|
5226
|
+
WHERE e2.type = 'fixed' AND e2.name = er.environment) = 1
|
|
5227
|
+
`.execute(db);
|
|
5228
|
+
}
|
|
5229
|
+
async function down$5(db) {
|
|
5230
|
+
await sql`DROP INDEX IF EXISTS idx_execution_runs_environment_id`.execute(db);
|
|
5231
|
+
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS environment_id`.execute(db);
|
|
5232
|
+
}
|
|
5233
|
+
//#endregion
|
|
5234
|
+
//#region src/db/migrations/062_execution_runs_agent_label.ts
|
|
5235
|
+
var _062_execution_runs_agent_label_exports = /* @__PURE__ */ __exportAll({
|
|
5236
|
+
down: () => down$4,
|
|
5237
|
+
up: () => up$4
|
|
5238
|
+
});
|
|
5239
|
+
/**
|
|
5240
|
+
* Add the agent-provenance columns to `execution_runs`:
|
|
5241
|
+
*
|
|
5242
|
+
* - `triggered_by_agent_label` — the human-set agent name when the run was
|
|
5243
|
+
* triggered through an agent-kind credential (user agent PAT or org agent
|
|
5244
|
+
* API key) via the developer MCP server.
|
|
5245
|
+
* - `cancelled_by_agent_label` — likewise for a cancellation driven through an
|
|
5246
|
+
* agent credential.
|
|
5247
|
+
*
|
|
5248
|
+
* The label is provenance only (the credential still inherits its principal's
|
|
5249
|
+
* permissions). NULL for ordinary human / webhook / system-driven runs.
|
|
5250
|
+
* Nullable, no backfill, idempotent.
|
|
5251
|
+
*/
|
|
5252
|
+
async function colExists(db, table, name) {
|
|
5253
|
+
return (await sql`
|
|
5254
|
+
SELECT EXISTS (
|
|
5255
|
+
SELECT 1 FROM information_schema.columns
|
|
5256
|
+
WHERE table_schema = 'public'
|
|
5257
|
+
AND table_name = ${table}
|
|
5258
|
+
AND column_name = ${name}
|
|
5259
|
+
) AS exists
|
|
5260
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
5261
|
+
}
|
|
5262
|
+
async function up$4(db) {
|
|
5263
|
+
if (!await colExists(db, "execution_runs", "triggered_by_agent_label")) await sql`ALTER TABLE public.execution_runs ADD COLUMN triggered_by_agent_label text`.execute(db);
|
|
5264
|
+
if (!await colExists(db, "execution_runs", "cancelled_by_agent_label")) await sql`ALTER TABLE public.execution_runs ADD COLUMN cancelled_by_agent_label text`.execute(db);
|
|
5265
|
+
}
|
|
5266
|
+
async function down$4(db) {
|
|
5267
|
+
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS triggered_by_agent_label`.execute(db);
|
|
5268
|
+
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS cancelled_by_agent_label`.execute(db);
|
|
5269
|
+
}
|
|
5270
|
+
//#endregion
|
|
5271
|
+
//#region src/db/migrations/063_access_log_agent_label_index.ts
|
|
5272
|
+
var _063_access_log_agent_label_index_exports = /* @__PURE__ */ __exportAll({
|
|
5273
|
+
down: () => down$3,
|
|
5274
|
+
up: () => up$3
|
|
5275
|
+
});
|
|
5276
|
+
/**
|
|
5277
|
+
* Index `access_log.agent_label` (column added by migration 058) so the
|
|
5278
|
+
* operator-facing agent filter (`kici-admin access-log list --agent-label`,
|
|
5279
|
+
* the dashboard "agent name" filter) does an indexed exact-match lookup
|
|
5280
|
+
* instead of a scan. Idempotent.
|
|
5281
|
+
*/
|
|
5020
5282
|
async function up$3(db) {
|
|
5021
|
-
|
|
5022
|
-
if (!await colExists$3(db, "execution_steps", "group_id")) await sql`ALTER TABLE public.execution_steps ADD COLUMN group_id text`.execute(db);
|
|
5283
|
+
await sql`CREATE INDEX IF NOT EXISTS access_log_agent_label_idx ON public.access_log (agent_label)`.execute(db);
|
|
5023
5284
|
}
|
|
5024
5285
|
async function down$3(db) {
|
|
5025
|
-
await sql`
|
|
5026
|
-
await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS concurrency_kind`.execute(db);
|
|
5286
|
+
await sql`DROP INDEX IF EXISTS access_log_agent_label_idx`.execute(db);
|
|
5027
5287
|
}
|
|
5028
5288
|
//#endregion
|
|
5029
|
-
//#region src/db/migrations/
|
|
5030
|
-
var
|
|
5289
|
+
//#region src/db/migrations/064_execution_jobs_skipped_environments.ts
|
|
5290
|
+
var _064_execution_jobs_skipped_environments_exports = /* @__PURE__ */ __exportAll({
|
|
5031
5291
|
down: () => down$2,
|
|
5032
5292
|
up: () => up$2
|
|
5033
5293
|
});
|
|
5034
5294
|
/**
|
|
5035
|
-
* Add
|
|
5036
|
-
* performed the action, when the actor authenticated with an agent-kind PAT.
|
|
5295
|
+
* Add the test-run skipped-environment columns to `execution_jobs`:
|
|
5037
5296
|
*
|
|
5038
|
-
*
|
|
5039
|
-
* the
|
|
5040
|
-
*
|
|
5297
|
+
* - `execution_jobs.skipped_environments text NULL` — a JSON-encoded `string[]`
|
|
5298
|
+
* of the bound environment names dropped on a test/local run because they
|
|
5299
|
+
* disallow local execution (`allowLocalExecution=false`) or are unconfigured.
|
|
5300
|
+
* NULL = nothing skipped.
|
|
5301
|
+
* - `execution_jobs.env_warning text NULL` — the user-visible warning naming the
|
|
5302
|
+
* skipped environments, surfaced on the dashboard run view. NULL = no warning.
|
|
5303
|
+
*
|
|
5304
|
+
* Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
|
|
5305
|
+
* preserved.
|
|
5041
5306
|
*/
|
|
5042
|
-
async function colExists$2(db, table, name) {
|
|
5043
|
-
return (await sql`
|
|
5044
|
-
SELECT EXISTS (
|
|
5045
|
-
SELECT 1 FROM information_schema.columns
|
|
5046
|
-
WHERE table_schema = 'public'
|
|
5047
|
-
AND table_name = ${table}
|
|
5048
|
-
AND column_name = ${name}
|
|
5049
|
-
) AS exists
|
|
5050
|
-
`.execute(db)).rows[0]?.exists ?? false;
|
|
5051
|
-
}
|
|
5052
5307
|
async function up$2(db) {
|
|
5053
|
-
|
|
5308
|
+
await sql`ALTER TABLE public.execution_jobs
|
|
5309
|
+
ADD COLUMN IF NOT EXISTS skipped_environments text`.execute(db);
|
|
5310
|
+
await sql`ALTER TABLE public.execution_jobs
|
|
5311
|
+
ADD COLUMN IF NOT EXISTS env_warning text`.execute(db);
|
|
5054
5312
|
}
|
|
5055
5313
|
async function down$2(db) {
|
|
5056
|
-
await sql`ALTER TABLE public.
|
|
5314
|
+
await sql`ALTER TABLE public.execution_jobs
|
|
5315
|
+
DROP COLUMN IF EXISTS skipped_environments`.execute(db);
|
|
5316
|
+
await sql`ALTER TABLE public.execution_jobs
|
|
5317
|
+
DROP COLUMN IF EXISTS env_warning`.execute(db);
|
|
5057
5318
|
}
|
|
5058
5319
|
//#endregion
|
|
5059
|
-
//#region src/db/migrations/
|
|
5060
|
-
var
|
|
5320
|
+
//#region src/db/migrations/065_pending_attestations.ts
|
|
5321
|
+
var _065_pending_attestations_exports = /* @__PURE__ */ __exportAll({
|
|
5061
5322
|
down: () => down$1,
|
|
5062
5323
|
up: () => up$1
|
|
5063
5324
|
});
|
|
5064
5325
|
/**
|
|
5065
|
-
* Add
|
|
5066
|
-
*
|
|
5326
|
+
* Add the `pending_attestations` deferred-attestation outbox and an idempotency
|
|
5327
|
+
* unique index on `attestations`.
|
|
5067
5328
|
*
|
|
5068
|
-
*
|
|
5069
|
-
*
|
|
5070
|
-
*
|
|
5071
|
-
*
|
|
5072
|
-
*
|
|
5073
|
-
*
|
|
5074
|
-
*
|
|
5075
|
-
* - `verified_at` — when the verdict was recorded (nullable).
|
|
5329
|
+
* When a build's provenance mint fails transiently, the agent freezes and
|
|
5330
|
+
* DSSE-signs the statement at build time; the orchestrator records the frozen
|
|
5331
|
+
* envelope here instead of failing the job. A Raft-leader-only retrier later
|
|
5332
|
+
* mints the deferred token, attaches it, uploads the bundle, and records one
|
|
5333
|
+
* `attestations` row — deleting the pending row. The unique index on
|
|
5334
|
+
* `attestations (run_id, job_id, subject_digest)` makes that fulfilment
|
|
5335
|
+
* idempotent across a cluster (ON CONFLICT DO NOTHING).
|
|
5076
5336
|
*
|
|
5077
|
-
*
|
|
5337
|
+
* `origin_kind` holds the non-`live` `AttestationOrigin` values
|
|
5338
|
+
* (`deferred` / `offline-backfill`); the `created_at` default anchors the true
|
|
5339
|
+
* build time so temporal honesty survives a later mint.
|
|
5340
|
+
*
|
|
5341
|
+
* Idempotent: guarded on table existence.
|
|
5078
5342
|
*/
|
|
5079
|
-
async function
|
|
5080
|
-
|
|
5343
|
+
async function up$1(db) {
|
|
5344
|
+
if ((await sql`
|
|
5081
5345
|
SELECT EXISTS (
|
|
5082
|
-
SELECT 1 FROM information_schema.
|
|
5083
|
-
WHERE table_schema = 'public'
|
|
5084
|
-
AND table_name = ${table}
|
|
5085
|
-
AND column_name = ${name}
|
|
5346
|
+
SELECT 1 FROM information_schema.tables
|
|
5347
|
+
WHERE table_schema = 'public' AND table_name = 'pending_attestations'
|
|
5086
5348
|
) AS exists
|
|
5087
|
-
`.execute(db)).rows[0]?.exists
|
|
5088
|
-
}
|
|
5089
|
-
async function up$1(db) {
|
|
5090
|
-
if (await colExists$1(db, "attestations", "verify_status")) return;
|
|
5349
|
+
`.execute(db)).rows[0]?.exists) return;
|
|
5091
5350
|
await sql`
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5351
|
+
CREATE TABLE public.pending_attestations (
|
|
5352
|
+
id TEXT PRIMARY KEY,
|
|
5353
|
+
run_id TEXT NOT NULL,
|
|
5354
|
+
job_id TEXT NOT NULL,
|
|
5355
|
+
subject_name TEXT NOT NULL,
|
|
5356
|
+
subject_digest TEXT NOT NULL,
|
|
5357
|
+
audience TEXT NOT NULL,
|
|
5358
|
+
dsse_envelope JSONB NOT NULL,
|
|
5359
|
+
public_key JSONB NOT NULL,
|
|
5360
|
+
media_type TEXT NOT NULL,
|
|
5361
|
+
statement_hash TEXT NOT NULL,
|
|
5362
|
+
origin_kind TEXT NOT NULL,
|
|
5363
|
+
attempt_count INTEGER NOT NULL DEFAULT 0,
|
|
5364
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
5365
|
+
last_attempt_at TIMESTAMPTZ,
|
|
5366
|
+
last_error TEXT
|
|
5367
|
+
)
|
|
5096
5368
|
`.execute(db);
|
|
5097
|
-
await sql`CREATE INDEX idx_attestations_subject_digest ON public.attestations (subject_digest)`.execute(db);
|
|
5098
|
-
await sql`CREATE INDEX idx_attestations_subject_name ON public.attestations (subject_name)`.execute(db);
|
|
5099
|
-
await sql`CREATE INDEX idx_attestations_verify_status ON public.attestations (verify_status)`.execute(db);
|
|
5100
|
-
await sql`CREATE INDEX idx_attestations_created_at ON public.attestations (created_at)`.execute(db);
|
|
5101
|
-
}
|
|
5102
|
-
async function down$1(db) {
|
|
5103
|
-
await sql`DROP INDEX IF EXISTS idx_attestations_created_at`.execute(db);
|
|
5104
|
-
await sql`DROP INDEX IF EXISTS idx_attestations_verify_status`.execute(db);
|
|
5105
|
-
await sql`DROP INDEX IF EXISTS idx_attestations_subject_name`.execute(db);
|
|
5106
|
-
await sql`DROP INDEX IF EXISTS idx_attestations_subject_digest`.execute(db);
|
|
5107
5369
|
await sql`
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5370
|
+
CREATE UNIQUE INDEX uq_pending_attestations_run_job_subject
|
|
5371
|
+
ON public.pending_attestations (run_id, job_id, subject_digest)
|
|
5372
|
+
`.execute(db);
|
|
5373
|
+
await sql`
|
|
5374
|
+
CREATE INDEX idx_pending_attestations_created_at
|
|
5375
|
+
ON public.pending_attestations (created_at)
|
|
5376
|
+
`.execute(db);
|
|
5377
|
+
await sql`
|
|
5378
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_attestations_run_job_subject
|
|
5379
|
+
ON public.attestations (run_id, job_id, subject_digest)
|
|
5112
5380
|
`.execute(db);
|
|
5113
5381
|
}
|
|
5382
|
+
async function down$1(db) {
|
|
5383
|
+
await sql`DROP INDEX IF EXISTS uq_attestations_run_job_subject`.execute(db);
|
|
5384
|
+
await sql`DROP TABLE IF EXISTS public.pending_attestations`.execute(db);
|
|
5385
|
+
}
|
|
5114
5386
|
//#endregion
|
|
5115
|
-
//#region src/db/migrations/
|
|
5116
|
-
var
|
|
5387
|
+
//#region src/db/migrations/066_pending_attestations_rejected.ts
|
|
5388
|
+
var _066_pending_attestations_rejected_exports = /* @__PURE__ */ __exportAll({
|
|
5117
5389
|
down: () => down,
|
|
5118
5390
|
up: () => up
|
|
5119
5391
|
});
|
|
5120
5392
|
/**
|
|
5121
|
-
* Add the
|
|
5122
|
-
*
|
|
5123
|
-
*
|
|
5124
|
-
*
|
|
5125
|
-
*
|
|
5126
|
-
* the run (e.g. the GitHub pusher / PR author).
|
|
5127
|
-
* - `trigger_actor_user_id` — the immutable provider user id (mirrors
|
|
5128
|
-
* `identity_links.provider_user_id`), preferred over the mutable username
|
|
5129
|
-
* when resolving the actor to a KiCI user.
|
|
5393
|
+
* Add `rejected_at` to `pending_attestations`: the terminal-rejection marker for
|
|
5394
|
+
* a deferred attestation the Platform definitively cannot mint (run/job absent).
|
|
5395
|
+
* NULL while the row is still pending a later mint; set once, the row is skipped
|
|
5396
|
+
* by the retrier and drops out of the pending-attestations gauge. Re-armed via
|
|
5397
|
+
* `kici-admin attestations retry --include-rejected`.
|
|
5130
5398
|
*
|
|
5131
|
-
*
|
|
5132
|
-
* `contributor_username`. Independently useful for a dashboard "triggered by
|
|
5133
|
-
* @x" and audit, beyond actor notifications. All nullable, no backfill,
|
|
5134
|
-
* idempotent.
|
|
5399
|
+
* Idempotent: guarded on column existence.
|
|
5135
5400
|
*/
|
|
5136
|
-
async function
|
|
5401
|
+
async function up(db) {
|
|
5402
|
+
if (await columnExists(db, "pending_attestations", "rejected_at")) return;
|
|
5403
|
+
await sql`
|
|
5404
|
+
ALTER TABLE public.pending_attestations
|
|
5405
|
+
ADD COLUMN rejected_at TIMESTAMPTZ
|
|
5406
|
+
`.execute(db);
|
|
5407
|
+
}
|
|
5408
|
+
async function down(db) {
|
|
5409
|
+
if (!await columnExists(db, "pending_attestations", "rejected_at")) return;
|
|
5410
|
+
await sql`
|
|
5411
|
+
ALTER TABLE public.pending_attestations
|
|
5412
|
+
DROP COLUMN rejected_at
|
|
5413
|
+
`.execute(db);
|
|
5414
|
+
}
|
|
5415
|
+
async function columnExists(db, table, column) {
|
|
5137
5416
|
return (await sql`
|
|
5138
5417
|
SELECT EXISTS (
|
|
5139
5418
|
SELECT 1 FROM information_schema.columns
|
|
5140
|
-
WHERE table_schema = 'public'
|
|
5141
|
-
AND table_name = ${table}
|
|
5142
|
-
AND column_name = ${name}
|
|
5419
|
+
WHERE table_schema = 'public' AND table_name = ${table} AND column_name = ${column}
|
|
5143
5420
|
) AS exists
|
|
5144
5421
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
5145
5422
|
}
|
|
5146
|
-
async function up(db) {
|
|
5147
|
-
if (!await colExists(db, "execution_runs", "trigger_actor_provider")) await sql`ALTER TABLE public.execution_runs ADD COLUMN trigger_actor_provider text`.execute(db);
|
|
5148
|
-
if (!await colExists(db, "execution_runs", "trigger_actor_username")) await sql`ALTER TABLE public.execution_runs ADD COLUMN trigger_actor_username text`.execute(db);
|
|
5149
|
-
if (!await colExists(db, "execution_runs", "trigger_actor_user_id")) await sql`ALTER TABLE public.execution_runs ADD COLUMN trigger_actor_user_id text`.execute(db);
|
|
5150
|
-
}
|
|
5151
|
-
async function down(db) {
|
|
5152
|
-
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS trigger_actor_provider`.execute(db);
|
|
5153
|
-
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS trigger_actor_username`.execute(db);
|
|
5154
|
-
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS trigger_actor_user_id`.execute(db);
|
|
5155
|
-
}
|
|
5156
5423
|
//#endregion
|
|
5157
5424
|
//#region src/db/migration-provider.ts
|
|
5158
5425
|
function createMigrationProvider() {
|
|
@@ -5217,7 +5484,13 @@ function createMigrationProvider() {
|
|
|
5217
5484
|
"057_step_concurrency": _057_step_concurrency_exports,
|
|
5218
5485
|
"058_access_log_agent_label": _058_access_log_agent_label_exports,
|
|
5219
5486
|
"059_attestation_verdict": _059_attestation_verdict_exports,
|
|
5220
|
-
"060_run_trigger_actor": _060_run_trigger_actor_exports
|
|
5487
|
+
"060_run_trigger_actor": _060_run_trigger_actor_exports,
|
|
5488
|
+
"061_execution_runs_environment_id": _061_execution_runs_environment_id_exports,
|
|
5489
|
+
"062_execution_runs_agent_label": _062_execution_runs_agent_label_exports,
|
|
5490
|
+
"063_access_log_agent_label_index": _063_access_log_agent_label_index_exports,
|
|
5491
|
+
"064_execution_jobs_skipped_environments": _064_execution_jobs_skipped_environments_exports,
|
|
5492
|
+
"065_pending_attestations": _065_pending_attestations_exports,
|
|
5493
|
+
"066_pending_attestations_rejected": _066_pending_attestations_rejected_exports
|
|
5221
5494
|
};
|
|
5222
5495
|
} };
|
|
5223
5496
|
}
|
|
@@ -5229,7 +5502,7 @@ function createMigrationProvider() {
|
|
|
5229
5502
|
* Uses pg_advisory_lock to ensure only one orchestrator instance runs
|
|
5230
5503
|
* migrations at a time, preventing conflicts in multi-instance deployments.
|
|
5231
5504
|
*/
|
|
5232
|
-
const logger$
|
|
5505
|
+
const logger$13 = createLogger({ prefix: "migrate" });
|
|
5233
5506
|
const ADVISORY_LOCK_KEY = 543210001;
|
|
5234
5507
|
/**
|
|
5235
5508
|
* Run all pending migrations with an advisory lock for concurrency safety.
|
|
@@ -5241,21 +5514,21 @@ const ADVISORY_LOCK_KEY = 543210001;
|
|
|
5241
5514
|
async function runMigrations(opts) {
|
|
5242
5515
|
const client = await opts.pool.connect();
|
|
5243
5516
|
try {
|
|
5244
|
-
logger$
|
|
5517
|
+
logger$13.info("Acquiring migration lock...");
|
|
5245
5518
|
await client.query("SELECT pg_advisory_lock($1)", [ADVISORY_LOCK_KEY]);
|
|
5246
|
-
logger$
|
|
5519
|
+
logger$13.info("Migration lock acquired");
|
|
5247
5520
|
const migrator = new Migrator({
|
|
5248
5521
|
db: opts.db,
|
|
5249
5522
|
provider: createMigrationProvider()
|
|
5250
5523
|
});
|
|
5251
5524
|
const start = Date.now();
|
|
5252
5525
|
const { results, error } = await migrator.migrateToLatest();
|
|
5253
|
-
for (const result of results ?? []) if (result.status === "Success") logger$
|
|
5254
|
-
else if (result.status === "Error") logger$
|
|
5526
|
+
for (const result of results ?? []) if (result.status === "Success") logger$13.info(`${result.migrationName} ... OK (${Date.now() - start}ms)`);
|
|
5527
|
+
else if (result.status === "Error") logger$13.error(`${result.migrationName} ... FAILED`);
|
|
5255
5528
|
if (error) throw error;
|
|
5256
5529
|
const applied = (results ?? []).filter((r) => r.status === "Success");
|
|
5257
|
-
if (applied.length === 0) logger$
|
|
5258
|
-
else logger$
|
|
5530
|
+
if (applied.length === 0) logger$13.info("Database schema is up to date");
|
|
5531
|
+
else logger$13.info(`Applied ${applied.length} migration(s)`);
|
|
5259
5532
|
const hash = await computeMigrationsHash(createMigrationProvider());
|
|
5260
5533
|
await storeMigrationContentHash(opts.pool, hash);
|
|
5261
5534
|
return results ?? [];
|
|
@@ -5284,7 +5557,7 @@ async function runMigrations(opts) {
|
|
|
5284
5557
|
* as `kici-admin peer create-token` — open a pool from KICI_DATABASE_URL /
|
|
5285
5558
|
* DATABASE_URL / --database-url.
|
|
5286
5559
|
*/
|
|
5287
|
-
function resolveDatabaseUrl$
|
|
5560
|
+
function resolveDatabaseUrl$4(explicit) {
|
|
5288
5561
|
const url = explicit ?? process.env.KICI_DATABASE_URL;
|
|
5289
5562
|
if (!url) throw new Error("Database URL required. Pass --database-url or set KICI_DATABASE_URL.");
|
|
5290
5563
|
return url;
|
|
@@ -5331,7 +5604,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5331
5604
|
});
|
|
5332
5605
|
db.command("fresh").description("DROP + CREATE the orchestrator DB, run migrations, record content hash").option("--database-url <url>", "Target database URL (else KICI_DATABASE_URL / DATABASE_URL)").requiredOption("--confirm", "Explicit confirmation (destructive)").option("--yes", "Skip interactive confirmation (for scripted use)").action(async (opts) => {
|
|
5333
5606
|
try {
|
|
5334
|
-
const url = resolveDatabaseUrl$
|
|
5607
|
+
const url = resolveDatabaseUrl$4(opts.databaseUrl);
|
|
5335
5608
|
const { dbName } = parseDatabaseUrl(url);
|
|
5336
5609
|
if (!opts.yes) {
|
|
5337
5610
|
if (!await confirmInteractive$2(`About to DROP + RECREATE database "${dbName}". Type "${dbName}" to confirm: `, dbName)) {
|
|
@@ -5362,7 +5635,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5362
5635
|
});
|
|
5363
5636
|
db.command("ensure <name>").description("CREATE DATABASE IF NOT EXISTS (idempotent)").option("--database-url <url>", "Admin DB URL (else KICI_DATABASE_URL / DATABASE_URL)").option("--owner <role>", "DB owner role (default: URL user). Pass when the admin connection is privileged but the new DB should be owned by a separate non-privileged role.").option("--revoke-connect-public", "After ensure, REVOKE CONNECT ON DATABASE \"<name>\" FROM PUBLIC (recommended on shared clusters).").option("--grant-connect-role <role>", "After ensure (and any --revoke-connect-public), GRANT CONNECT ON DATABASE \"<name>\" TO \"<role>\". Repeatable.", (val, acc) => acc.concat([val]), []).action(async (name, opts) => {
|
|
5364
5637
|
try {
|
|
5365
|
-
const baseUrl = resolveDatabaseUrl$
|
|
5638
|
+
const baseUrl = resolveDatabaseUrl$4(opts.databaseUrl);
|
|
5366
5639
|
const url = new URL(baseUrl);
|
|
5367
5640
|
url.pathname = `/${name}`;
|
|
5368
5641
|
const targetUrl = url.toString();
|
|
@@ -5381,7 +5654,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5381
5654
|
});
|
|
5382
5655
|
db.command("create-role").description("CREATE / ALTER ROLE with LOGIN [+ CREATEDB] (idempotent)").option("--database-url <url>", "Admin DB URL (else KICI_DATABASE_URL / DATABASE_URL)").requiredOption("--user <name>", "Role name to create or update").requiredOption("--password <password>", "Role password (raw — quote as needed)").option("--createdb", "Grant CREATEDB to the new role", false).action(async (opts) => {
|
|
5383
5656
|
try {
|
|
5384
|
-
const { adminUrl } = parseDatabaseUrl(resolveDatabaseUrl$
|
|
5657
|
+
const { adminUrl } = parseDatabaseUrl(resolveDatabaseUrl$4(opts.databaseUrl));
|
|
5385
5658
|
logInvocation(`create-role ${opts.user}`, adminUrl);
|
|
5386
5659
|
const outcome = await createDbRole(adminUrl, {
|
|
5387
5660
|
username: opts.user,
|
|
@@ -5396,7 +5669,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5396
5669
|
});
|
|
5397
5670
|
db.command("create-readonly-user").description("Create a read-only role with SELECT on all tables + default privileges").option("--database-url <url>", "Target DB URL (must connect as owner)").requiredOption("--user <name>", "Read-only role name").requiredOption("--password <password>", "Role password").action(async (opts) => {
|
|
5398
5671
|
try {
|
|
5399
|
-
const url = resolveDatabaseUrl$
|
|
5672
|
+
const url = resolveDatabaseUrl$4(opts.databaseUrl);
|
|
5400
5673
|
const { dbName } = parseDatabaseUrl(url);
|
|
5401
5674
|
logInvocation(`create-readonly-user ${opts.user}`, url);
|
|
5402
5675
|
const outcome = await createReadOnlyDbUser(url, {
|
|
@@ -5411,7 +5684,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5411
5684
|
});
|
|
5412
5685
|
db.command("check-schema").description("Compare bundled migrations vs live schema. Exit 2 on drift.").option("--database-url <url>", "Target DB URL (else KICI_DATABASE_URL / DATABASE_URL)").option("--json", "Emit JSON instead of a human-readable line", false).action(async (opts) => {
|
|
5413
5686
|
try {
|
|
5414
|
-
const pool = createPool(resolveDatabaseUrl$
|
|
5687
|
+
const pool = createPool(resolveDatabaseUrl$4(opts.databaseUrl));
|
|
5415
5688
|
try {
|
|
5416
5689
|
const status = await isSchemaCurrent(pool, createMigrationProvider());
|
|
5417
5690
|
if (opts.json) process.stdout.write(JSON.stringify(status) + "\n");
|
|
@@ -5428,7 +5701,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5428
5701
|
});
|
|
5429
5702
|
db.command("collation-check").description("Compare pg_database.datcollversion against the running libc collation version. Exit 2 on drift.").option("--database-url <url>", "Target DB URL (else KICI_DATABASE_URL / DATABASE_URL)").option("--json", "Emit JSON instead of a human-readable line", false).action(async (opts) => {
|
|
5430
5703
|
try {
|
|
5431
|
-
const url = resolveDatabaseUrl$
|
|
5704
|
+
const url = resolveDatabaseUrl$4(opts.databaseUrl);
|
|
5432
5705
|
const { dbName } = parseDatabaseUrl(url);
|
|
5433
5706
|
const pool = createPool(url);
|
|
5434
5707
|
try {
|
|
@@ -5458,7 +5731,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5458
5731
|
});
|
|
5459
5732
|
db.command("reindex").description("REINDEX DATABASE CONCURRENTLY <db>. Rebuilds every index under the running libc collation rules. Non-blocking but takes minutes + ~2× temp disk.").option("--database-url <url>", "Target DB URL (else KICI_DATABASE_URL / DATABASE_URL)").requiredOption("--confirm", "Explicit confirmation (destructive — long-running)").requiredOption("--reason <text>", "Reason (recorded in stderr banner)").action(async (opts) => {
|
|
5460
5733
|
try {
|
|
5461
|
-
const url = resolveDatabaseUrl$
|
|
5734
|
+
const url = resolveDatabaseUrl$4(opts.databaseUrl);
|
|
5462
5735
|
const { dbName } = parseDatabaseUrl(url);
|
|
5463
5736
|
logInvocation(`reindex ${dbName} (${opts.reason})`, url);
|
|
5464
5737
|
const pool = createPool(url);
|
|
@@ -5475,7 +5748,7 @@ function registerDbCommands(program, getClient) {
|
|
|
5475
5748
|
});
|
|
5476
5749
|
db.command("refresh-collation-version").description("ALTER DATABASE <db> REFRESH COLLATION VERSION. Metadata-only bump; pair with db reindex after a libc-base image rebuild.").option("--database-url <url>", "Target DB URL (else KICI_DATABASE_URL / DATABASE_URL)").requiredOption("--reason <text>", "Reason (recorded in stderr banner)").action(async (opts) => {
|
|
5477
5750
|
try {
|
|
5478
|
-
const url = resolveDatabaseUrl$
|
|
5751
|
+
const url = resolveDatabaseUrl$4(opts.databaseUrl);
|
|
5479
5752
|
const { dbName } = parseDatabaseUrl(url);
|
|
5480
5753
|
logInvocation(`refresh-collation-version ${dbName} (${opts.reason})`, url);
|
|
5481
5754
|
const pool = createPool(url);
|
|
@@ -6302,7 +6575,7 @@ function safeParse(value) {
|
|
|
6302
6575
|
*/
|
|
6303
6576
|
function webhookNoteReason(note) {
|
|
6304
6577
|
switch (note) {
|
|
6305
|
-
case "
|
|
6578
|
+
case "no-public-url": return "KICI_WEBHOOK_PUBLIC_URL is not set on the orchestrator";
|
|
6306
6579
|
case "platform-no-public-url": return "the Platform has no public webhook URL configured";
|
|
6307
6580
|
case "platform-unavailable": return "could not reach the Platform";
|
|
6308
6581
|
default: return "webhook URL could not be determined";
|
|
@@ -6311,7 +6584,7 @@ function webhookNoteReason(note) {
|
|
|
6311
6584
|
/** Actionable next-step hint paired with {@link webhookNoteReason}. */
|
|
6312
6585
|
function webhookNoteHint(note) {
|
|
6313
6586
|
switch (note) {
|
|
6314
|
-
case "
|
|
6587
|
+
case "no-public-url": return "Set KICI_WEBHOOK_PUBLIC_URL to the orchestrator's public base, then re-run; paste the printed URL into your GitHub App or repo webhook.";
|
|
6315
6588
|
case "platform-unavailable": return "Retry once the orchestrator reconnects, or find the URL in the dashboard under Sources.";
|
|
6316
6589
|
default: return "Find the webhook URL in the dashboard under Sources.";
|
|
6317
6590
|
}
|
|
@@ -6913,7 +7186,7 @@ async function getRemoteSource(db, orgId) {
|
|
|
6913
7186
|
//#endregion
|
|
6914
7187
|
//#region src/cli/commands/remote-source.ts
|
|
6915
7188
|
init_client();
|
|
6916
|
-
function resolveDatabaseUrl$
|
|
7189
|
+
function resolveDatabaseUrl$3(explicit) {
|
|
6917
7190
|
const url = explicit ?? process.env.KICI_DATABASE_URL;
|
|
6918
7191
|
if (!url) throw new Error("Database URL required. Pass --database-url or set KICI_DATABASE_URL.");
|
|
6919
7192
|
return url;
|
|
@@ -6922,7 +7195,7 @@ function registerRemoteSourceCommands(program) {
|
|
|
6922
7195
|
program.command("remote-source").description("Inspect the auto-provisioned remote-source org anchor").command("show <orgId>").description("Print the remote_sources anchor row for an org (routing key remote:<orgId>).").option("--database-url <url>", "Orchestrator DB URL (else KICI_DATABASE_URL)").option("--format <format>", "Output format: json|table", "table").action(async (orgId, opts) => {
|
|
6923
7196
|
let url;
|
|
6924
7197
|
try {
|
|
6925
|
-
url = resolveDatabaseUrl$
|
|
7198
|
+
url = resolveDatabaseUrl$3(opts.databaseUrl);
|
|
6926
7199
|
} catch (err) {
|
|
6927
7200
|
console.error(`Error: ${toErrorMessage(err)}`);
|
|
6928
7201
|
process.exit(1);
|
|
@@ -7459,6 +7732,16 @@ function registerRunsCommands(program, getClient) {
|
|
|
7459
7732
|
}
|
|
7460
7733
|
});
|
|
7461
7734
|
}
|
|
7735
|
+
createLogger({ prefix: "cluster-identity" });
|
|
7736
|
+
/**
|
|
7737
|
+
* Build the S3 key for the cluster identity sentinel under an optional prefix.
|
|
7738
|
+
* Strips any trailing slash from the prefix so callers can pass either form.
|
|
7739
|
+
*/
|
|
7740
|
+
function clusterSentinelKey(prefix) {
|
|
7741
|
+
if (!prefix) return ".kici-cluster-id";
|
|
7742
|
+
const trimmed = prefix.replace(/\/+$/, "");
|
|
7743
|
+
return trimmed.length === 0 ? ".kici-cluster-id" : `${trimmed}/.kici-cluster-id`;
|
|
7744
|
+
}
|
|
7462
7745
|
//#endregion
|
|
7463
7746
|
//#region src/config.ts
|
|
7464
7747
|
/**
|
|
@@ -7593,6 +7876,15 @@ const baseSchema = z.object({
|
|
|
7593
7876
|
* `event.attempts <= N`. Ignored unless `KICI_TEST_MODE=1`.
|
|
7594
7877
|
*/
|
|
7595
7878
|
testEventFailFirstN: z.string().optional(),
|
|
7879
|
+
/**
|
|
7880
|
+
* **Test-only.** When set (and `KICI_TEST_MODE=1`), the orchestrator forces
|
|
7881
|
+
* the *initial* agent provenance mint to fail transiently (defer) for any job
|
|
7882
|
+
* whose requested OIDC `audience` equals this value, so an E2E can exercise
|
|
7883
|
+
* the deferred-attestation retry + per-run serve path with a REAL run. The
|
|
7884
|
+
* retrier's re-mint uses a different call site and is unaffected. Ignored
|
|
7885
|
+
* unless `KICI_TEST_MODE=1`. Production deployments leave this unset.
|
|
7886
|
+
*/
|
|
7887
|
+
testMintDeferAudience: z.string().optional(),
|
|
7596
7888
|
eventLogMaxPayloadBytes: z.coerce.number().default(5 * 1024 * 1024),
|
|
7597
7889
|
logLevel: z.enum([
|
|
7598
7890
|
"debug",
|
|
@@ -7782,6 +8074,7 @@ const envDef = defineEnv({
|
|
|
7782
8074
|
eventRouterRetryScanIntervalMs: "KICI_EVENT_ROUTER_RETRY_SCAN_INTERVAL_MS",
|
|
7783
8075
|
testMode: "KICI_TEST_MODE",
|
|
7784
8076
|
testEventFailFirstN: "KICI_TEST_EVENT_FAIL_FIRST_N",
|
|
8077
|
+
testMintDeferAudience: "KICI_TEST_MINT_DEFER_AUDIENCE",
|
|
7785
8078
|
eventLogMaxPayloadBytes: "KICI_EVENT_LOG_MAX_PAYLOAD_BYTES",
|
|
7786
8079
|
logLevel: "KICI_LOG_LEVEL",
|
|
7787
8080
|
nodeEnv: "NODE_ENV",
|
|
@@ -8508,11 +8801,18 @@ function createProvenanceTrustRoot(opts = {}) {
|
|
|
8508
8801
|
/**
|
|
8509
8802
|
* Compute the verification verdict for a stored provenance bundle at ingest.
|
|
8510
8803
|
*
|
|
8804
|
+
* Reads the bundle, selects the signing-key set by the identity token's own
|
|
8805
|
+
* `kid` (so a kid-miss triggers the trust root's single refetch — a token
|
|
8806
|
+
* minted with a freshly-rotated key still verifies against a briefly-stale
|
|
8807
|
+
* cached JWKS), then verifies offline.
|
|
8808
|
+
*
|
|
8511
8809
|
* Fail-closed: a missing trust root, unfetchable JWKS, missing storage, or an
|
|
8512
8810
|
* unreadable bundle all yield `unverifiable` (never silently `verified`). A
|
|
8513
|
-
*
|
|
8514
|
-
*
|
|
8515
|
-
*
|
|
8811
|
+
* signing key that is absent from the published JWKS even after the refetch
|
|
8812
|
+
* yields `unverifiable` (`signing_key_not_published`) — "couldn't verify", not
|
|
8813
|
+
* "proved bad". A bundle that verifies false yields `failed` with the first
|
|
8814
|
+
* failure code. Any thrown error is caught and recorded as `unverifiable` —
|
|
8815
|
+
* verification never fails the upload.
|
|
8516
8816
|
*/
|
|
8517
8817
|
async function computeAttestationVerdict(opts) {
|
|
8518
8818
|
const { trustRoot, storage, storageKey } = opts;
|
|
@@ -8529,20 +8829,33 @@ async function computeAttestationVerdict(opts) {
|
|
|
8529
8829
|
verifyReason: "no_storage",
|
|
8530
8830
|
verifiedAt: null
|
|
8531
8831
|
};
|
|
8532
|
-
const
|
|
8832
|
+
const raw = await storage.get(storageKey);
|
|
8833
|
+
if (!raw) return {
|
|
8834
|
+
verifyStatus: status.unverifiable,
|
|
8835
|
+
verifyReason: "bundle_unreadable",
|
|
8836
|
+
verifiedAt: null
|
|
8837
|
+
};
|
|
8838
|
+
const bundle = JSON.parse(raw.toString("utf8"));
|
|
8839
|
+
const idToken = bundle?.verificationMaterial?.identityToken;
|
|
8840
|
+
let kid;
|
|
8841
|
+
if (typeof idToken === "string") try {
|
|
8842
|
+
kid = decodeProtectedHeader(idToken).kid;
|
|
8843
|
+
} catch {
|
|
8844
|
+
kid = void 0;
|
|
8845
|
+
}
|
|
8846
|
+
const jwks = await trustRoot.getJwks(kid);
|
|
8533
8847
|
if (!jwks) return {
|
|
8534
8848
|
verifyStatus: status.unverifiable,
|
|
8535
8849
|
verifyReason: "jwks_fetch_failed",
|
|
8536
8850
|
verifiedAt: null
|
|
8537
8851
|
};
|
|
8538
|
-
|
|
8539
|
-
if (!raw) return {
|
|
8852
|
+
if (kid && !jwks.keys.some((k) => k.kid === kid)) return {
|
|
8540
8853
|
verifyStatus: status.unverifiable,
|
|
8541
|
-
verifyReason: "
|
|
8854
|
+
verifyReason: "signing_key_not_published",
|
|
8542
8855
|
verifiedAt: null
|
|
8543
8856
|
};
|
|
8544
8857
|
const result = await verifyKiciBundle({
|
|
8545
|
-
bundle
|
|
8858
|
+
bundle,
|
|
8546
8859
|
jwks,
|
|
8547
8860
|
expectedIssuer: issuer
|
|
8548
8861
|
});
|
|
@@ -8607,6 +8920,58 @@ async function reverifyAttestations(db, trustRoot, storage, opts) {
|
|
|
8607
8920
|
};
|
|
8608
8921
|
}
|
|
8609
8922
|
//#endregion
|
|
8923
|
+
//#region src/cli/commands/attestations-retry.ts
|
|
8924
|
+
/**
|
|
8925
|
+
* Post the retry request through the injected transport (the admin API POST)
|
|
8926
|
+
* and return the counts. Validates that the scope is unambiguous. When
|
|
8927
|
+
* `includeRejected` is set, previously terminally-rejected rows are re-armed
|
|
8928
|
+
* and re-attempted in the same drain.
|
|
8929
|
+
*/
|
|
8930
|
+
async function runAttestationRetry(post, opts) {
|
|
8931
|
+
const base = opts.includeRejected ? { includeRejected: true } : {};
|
|
8932
|
+
if (!opts.runId && !opts.allPending) return post(base);
|
|
8933
|
+
return post(opts.runId ? {
|
|
8934
|
+
...base,
|
|
8935
|
+
runId: opts.runId
|
|
8936
|
+
} : base);
|
|
8937
|
+
}
|
|
8938
|
+
//#endregion
|
|
8939
|
+
//#region src/cli/commands/attestations-list.ts
|
|
8940
|
+
/**
|
|
8941
|
+
* Map the injected DB read into the CLI envelope. `limit` / `runId` / `jobId`
|
|
8942
|
+
* are honored by the injected `read` fn (the real one is a Kysely query); this
|
|
8943
|
+
* runner owns only the row→envelope projection, so it stays pure and testable.
|
|
8944
|
+
*/
|
|
8945
|
+
async function runAttestationList(read, opts) {
|
|
8946
|
+
return { attestations: (await read(opts)).map((r) => ({
|
|
8947
|
+
id: r.id,
|
|
8948
|
+
runId: r.run_id,
|
|
8949
|
+
jobId: r.job_id,
|
|
8950
|
+
subjectName: r.subject_name,
|
|
8951
|
+
verifyStatus: r.verify_status,
|
|
8952
|
+
createdAt: r.created_at.toISOString()
|
|
8953
|
+
})) };
|
|
8954
|
+
}
|
|
8955
|
+
/**
|
|
8956
|
+
* Build the real DB read: a Kysely query over `attestations`, newest first,
|
|
8957
|
+
* with optional run/job filters and a row cap.
|
|
8958
|
+
*/
|
|
8959
|
+
function readAttestations(db) {
|
|
8960
|
+
return async ({ limit, runId, jobId }) => {
|
|
8961
|
+
let query = db.selectFrom("attestations").select([
|
|
8962
|
+
"id",
|
|
8963
|
+
"run_id",
|
|
8964
|
+
"job_id",
|
|
8965
|
+
"subject_name",
|
|
8966
|
+
"verify_status",
|
|
8967
|
+
"created_at"
|
|
8968
|
+
]).orderBy("created_at", "desc").limit(limit);
|
|
8969
|
+
if (runId) query = query.where("run_id", "=", runId);
|
|
8970
|
+
if (jobId) query = query.where("job_id", "=", jobId);
|
|
8971
|
+
return query.execute();
|
|
8972
|
+
};
|
|
8973
|
+
}
|
|
8974
|
+
//#endregion
|
|
8610
8975
|
//#region src/cli/commands/attestations.ts
|
|
8611
8976
|
/**
|
|
8612
8977
|
* `kici-admin attestations` — orchestrator-DB-direct provenance maintenance.
|
|
@@ -8620,7 +8985,7 @@ async function reverifyAttestations(db, trustRoot, storage, opts) {
|
|
|
8620
8985
|
*/
|
|
8621
8986
|
init_client();
|
|
8622
8987
|
const logger$8 = createLogger({ prefix: "kici-admin-attestations" });
|
|
8623
|
-
function resolveDatabaseUrl$
|
|
8988
|
+
function resolveDatabaseUrl$2(explicit) {
|
|
8624
8989
|
const url = explicit ?? process.env.KICI_DATABASE_URL;
|
|
8625
8990
|
if (!url) throw new Error("Database URL required. Pass --database-url or set KICI_DATABASE_URL.");
|
|
8626
8991
|
return url;
|
|
@@ -8658,11 +9023,34 @@ async function confirmInteractive$1(prompt) {
|
|
|
8658
9023
|
rl.close();
|
|
8659
9024
|
}
|
|
8660
9025
|
}
|
|
8661
|
-
|
|
8662
|
-
|
|
9026
|
+
/** Render attestation rows as a compact human table (stderr note when empty). */
|
|
9027
|
+
function printAttestationTable(rows) {
|
|
9028
|
+
if (rows.length === 0) {
|
|
9029
|
+
process.stderr.write("No attestations found.\n");
|
|
9030
|
+
return;
|
|
9031
|
+
}
|
|
9032
|
+
for (const r of rows) console.log(`${r.id} ${r.verifyStatus.padEnd(12)} run=${r.runId} job=${r.jobId} ${r.subjectName} ${r.createdAt}`);
|
|
9033
|
+
}
|
|
9034
|
+
function registerAttestationsCommands(program, getClient) {
|
|
9035
|
+
const attestations = program.command("attestations").description("Provenance-attestation maintenance (orchestrator DB)");
|
|
9036
|
+
attestations.command("retry").description("Mint deferred attestations now (drains the pending-attestations outbox)").option("--run-id <id>", "Scope to a single run (else drains every pending attestation)").option("--all-pending", "Drain every pending attestation (default when --run-id is absent)").option("--include-rejected", "Also re-attempt rows previously marked terminally rejected (re-arm)").action(async (opts) => {
|
|
9037
|
+
try {
|
|
9038
|
+
const result = await runAttestationRetry((body) => getClient().post("/api/v1/admin/attestations/retry", body), {
|
|
9039
|
+
runId: opts.runId,
|
|
9040
|
+
allPending: opts.allPending,
|
|
9041
|
+
includeRejected: opts.includeRejected
|
|
9042
|
+
});
|
|
9043
|
+
logger$8.info("attestations retry complete", result);
|
|
9044
|
+
process.stderr.write(`Minted ${result.minted} deferred attestation(s); ${result.stillPending} still pending; ${result.rejected} rejected.\n`);
|
|
9045
|
+
} catch (err) {
|
|
9046
|
+
console.error(`Error: ${toErrorMessage(err)}`);
|
|
9047
|
+
process.exit(1);
|
|
9048
|
+
}
|
|
9049
|
+
});
|
|
9050
|
+
attestations.command("reverify").description("Recompute stored attestation verdicts (verify-at-ingest backfill)").option("--all", "Re-evaluate every attestation (default: only pending/unverifiable)").option("--database-url <url>", "Orchestrator DB URL (else KICI_DATABASE_URL)").option("--yes", "Skip the --all confirmation prompt").action(async (opts) => {
|
|
8663
9051
|
try {
|
|
8664
9052
|
const config = loadConfig();
|
|
8665
|
-
const url = resolveDatabaseUrl$
|
|
9053
|
+
const url = resolveDatabaseUrl$2(opts.databaseUrl ?? (config.databaseUrl || void 0));
|
|
8666
9054
|
if (opts.all && !opts.yes) {
|
|
8667
9055
|
if (!await confirmInteractive$1("Re-evaluate the verdict of EVERY attestation (including already-verified)? [y/N] ")) {
|
|
8668
9056
|
console.error("Aborted.");
|
|
@@ -8689,6 +9077,29 @@ function registerAttestationsCommands(program) {
|
|
|
8689
9077
|
process.exit(1);
|
|
8690
9078
|
}
|
|
8691
9079
|
});
|
|
9080
|
+
attestations.command("list").description("List provenance attestations from the orchestrator DB (newest first)").option("--run-id <id>", "Filter to a single run").option("--job-id <id>", "Filter to a single job").option("--limit <n>", "Max results (default 20, max 100)", "20").option("--json", "Emit the raw JSON envelope instead of a table").option("--database-url <url>", "Orchestrator DB URL (else KICI_DATABASE_URL)").action(async (opts) => {
|
|
9081
|
+
try {
|
|
9082
|
+
const url = resolveDatabaseUrl$2(opts.databaseUrl);
|
|
9083
|
+
const limit = Math.min(Math.max(Number.parseInt(opts.limit, 10) || 20, 1), 100);
|
|
9084
|
+
const pool = createPool(url);
|
|
9085
|
+
const db = createDb$1(pool);
|
|
9086
|
+
try {
|
|
9087
|
+
const result = await runAttestationList(readAttestations(db), {
|
|
9088
|
+
limit,
|
|
9089
|
+
runId: opts.runId,
|
|
9090
|
+
jobId: opts.jobId
|
|
9091
|
+
});
|
|
9092
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
9093
|
+
else printAttestationTable(result.attestations);
|
|
9094
|
+
} finally {
|
|
9095
|
+
await db.destroy();
|
|
9096
|
+
await pool.end().catch(() => {});
|
|
9097
|
+
}
|
|
9098
|
+
} catch (err) {
|
|
9099
|
+
console.error(`Error: ${toErrorMessage(err)}`);
|
|
9100
|
+
process.exit(1);
|
|
9101
|
+
}
|
|
9102
|
+
});
|
|
8692
9103
|
}
|
|
8693
9104
|
//#endregion
|
|
8694
9105
|
//#region src/cli/commands/event-log.ts
|
|
@@ -8817,7 +9228,7 @@ function renderTable(headers, rows) {
|
|
|
8817
9228
|
}
|
|
8818
9229
|
function registerAccessLogCommands(program, getClient) {
|
|
8819
9230
|
const accessLog = program.command("access-log").description("Inspect the read / admin-mutation access log");
|
|
8820
|
-
accessLog.command("list").description("List access-log rows (dogfooded via /api/v1/admin/access-log)").option("--org-id <orgId>", "Filter by org/tenant ID").option("--actor-type <t>", "Filter by actor type (user|api_key|service_account|platform_operator|system)").option("--actor-id <id>", "Filter by actor id (zsub, keyId, service_account id, ...)").option("--action <action>", "Filter by dotted action (e.g. run.detail.read, run.cancel)").option("--source <s>", "Filter by source (platform_proxy|admin_http|admin_cli)").option("--outcome <o>", "Filter by outcome (allowed|denied|error)").option("--target-type <t>", "Filter by target type (run|step|event_log|secret_scope|...)").option("--target-id <id>", "Filter by target id").option("--from <ts>", "ISO timestamp lower bound (inclusive)").option("--to <ts>", "ISO timestamp upper bound (exclusive)").option("--q <text>", "Filter by substring of error_message (trigram-indexed full-text search)").option("--limit <n>", "Max results (default 50, max 200)", "50").option("--cursor <c>", "Opaque cursor from a previous nextCursor").option("--json", "Emit raw JSON instead of a table").action(async (opts) => {
|
|
9231
|
+
accessLog.command("list").description("List access-log rows (dogfooded via /api/v1/admin/access-log)").option("--org-id <orgId>", "Filter by org/tenant ID").option("--actor-type <t>", "Filter by actor type (user|api_key|service_account|platform_operator|system)").option("--actor-id <id>", "Filter by actor id (zsub, keyId, service_account id, ...)").option("--action <action>", "Filter by dotted action (e.g. run.detail.read, run.cancel)").option("--source <s>", "Filter by source (platform_proxy|admin_http|admin_cli)").option("--outcome <o>", "Filter by outcome (allowed|denied|error)").option("--target-type <t>", "Filter by target type (run|step|event_log|secret_scope|...)").option("--target-id <id>", "Filter by target id").option("--from <ts>", "ISO timestamp lower bound (inclusive)").option("--to <ts>", "ISO timestamp upper bound (exclusive)").option("--q <text>", "Filter by substring of error_message (trigram-indexed full-text search)").option("--agent-label <label>", "Filter by exact agent label").option("--agent-only", "Only agent-attributed rows").option("--limit <n>", "Max results (default 50, max 200)", "50").option("--cursor <c>", "Opaque cursor from a previous nextCursor").option("--json", "Emit raw JSON instead of a table").action(async (opts) => {
|
|
8821
9232
|
try {
|
|
8822
9233
|
const response = await getClient().listAccessLog({
|
|
8823
9234
|
orgId: opts.orgId,
|
|
@@ -8831,6 +9242,8 @@ function registerAccessLogCommands(program, getClient) {
|
|
|
8831
9242
|
from: opts.from,
|
|
8832
9243
|
to: opts.to,
|
|
8833
9244
|
q: opts.q,
|
|
9245
|
+
agentLabel: opts.agentLabel,
|
|
9246
|
+
agentOnly: opts.agentOnly,
|
|
8834
9247
|
limit: parseInt(opts.limit, 10),
|
|
8835
9248
|
cursor: opts.cursor
|
|
8836
9249
|
});
|
|
@@ -8845,6 +9258,7 @@ function registerAccessLogCommands(program, getClient) {
|
|
|
8845
9258
|
const headers = [
|
|
8846
9259
|
"created_at",
|
|
8847
9260
|
"actor",
|
|
9261
|
+
"agent",
|
|
8848
9262
|
"action",
|
|
8849
9263
|
"source",
|
|
8850
9264
|
"outcome",
|
|
@@ -8854,6 +9268,7 @@ function registerAccessLogCommands(program, getClient) {
|
|
|
8854
9268
|
const rows = response.items.map((r) => [
|
|
8855
9269
|
r.createdAt,
|
|
8856
9270
|
`${r.actorType}:${r.actorId}`,
|
|
9271
|
+
r.agentLabel ?? "",
|
|
8857
9272
|
r.action,
|
|
8858
9273
|
r.source,
|
|
8859
9274
|
r.outcome,
|
|
@@ -8880,6 +9295,7 @@ function registerAccessLogCommands(program, getClient) {
|
|
|
8880
9295
|
console.log(` Org: ${response.orgId ?? "(none)"}`);
|
|
8881
9296
|
if (response.routingKey) console.log(` Routing key: ${response.routingKey}`);
|
|
8882
9297
|
console.log(` Actor: ${response.actorType}:${response.actorId}`);
|
|
9298
|
+
console.log(` Agent label: ${response.agentLabel ?? "-"}`);
|
|
8883
9299
|
if (response.actorMeta) console.log(` Actor meta: ${JSON.stringify(response.actorMeta)}`);
|
|
8884
9300
|
console.log(` Action: ${response.action}`);
|
|
8885
9301
|
console.log(` Source: ${response.source}`);
|
|
@@ -10642,61 +11058,61 @@ var init_registry = __esmMin((() => {
|
|
|
10642
11058
|
rolldown: {
|
|
10643
11059
|
"linux-x64": {
|
|
10644
11060
|
name: "rolldown",
|
|
10645
|
-
version: "1.1.
|
|
11061
|
+
version: "1.1.3",
|
|
10646
11062
|
platform: "linux",
|
|
10647
11063
|
arch: "x64",
|
|
10648
|
-
url: "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.
|
|
10649
|
-
sha256: "
|
|
11064
|
+
url: "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz",
|
|
11065
|
+
sha256: "7759bc6da0140899e447345cb2467f5a4fb2c73d9cbac94a341b7a69e67f5cff",
|
|
10650
11066
|
extractPath: "package/",
|
|
10651
11067
|
archiveType: "tar.gz"
|
|
10652
11068
|
},
|
|
10653
11069
|
"linux-arm64": {
|
|
10654
11070
|
name: "rolldown",
|
|
10655
|
-
version: "1.1.
|
|
11071
|
+
version: "1.1.3",
|
|
10656
11072
|
platform: "linux",
|
|
10657
11073
|
arch: "arm64",
|
|
10658
|
-
url: "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.
|
|
10659
|
-
sha256: "
|
|
11074
|
+
url: "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz",
|
|
11075
|
+
sha256: "a1ab47becdde8e8d8f9176a0bdc938e6e76bf52c668ffb5287ea8b515048bb99",
|
|
10660
11076
|
extractPath: "package/",
|
|
10661
11077
|
archiveType: "tar.gz"
|
|
10662
11078
|
},
|
|
10663
11079
|
"darwin-x64": {
|
|
10664
11080
|
name: "rolldown",
|
|
10665
|
-
version: "1.1.
|
|
11081
|
+
version: "1.1.3",
|
|
10666
11082
|
platform: "darwin",
|
|
10667
11083
|
arch: "x64",
|
|
10668
|
-
url: "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.
|
|
10669
|
-
sha256: "
|
|
11084
|
+
url: "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz",
|
|
11085
|
+
sha256: "eabb0140217ba45fc0d1de1a7cd59ba475a9c0cd2c207c9364fb58328478b878",
|
|
10670
11086
|
extractPath: "package/",
|
|
10671
11087
|
archiveType: "tar.gz"
|
|
10672
11088
|
},
|
|
10673
11089
|
"darwin-arm64": {
|
|
10674
11090
|
name: "rolldown",
|
|
10675
|
-
version: "1.1.
|
|
11091
|
+
version: "1.1.3",
|
|
10676
11092
|
platform: "darwin",
|
|
10677
11093
|
arch: "arm64",
|
|
10678
|
-
url: "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.
|
|
10679
|
-
sha256: "
|
|
11094
|
+
url: "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz",
|
|
11095
|
+
sha256: "ddc152faf8328a44ee846c99cf945dbac45541ce2a6785f6b0707ad1c0ffe1f3",
|
|
10680
11096
|
extractPath: "package/",
|
|
10681
11097
|
archiveType: "tar.gz"
|
|
10682
11098
|
},
|
|
10683
11099
|
"win32-x64": {
|
|
10684
11100
|
name: "rolldown",
|
|
10685
|
-
version: "1.1.
|
|
11101
|
+
version: "1.1.3",
|
|
10686
11102
|
platform: "win32",
|
|
10687
11103
|
arch: "x64",
|
|
10688
|
-
url: "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.
|
|
10689
|
-
sha256: "
|
|
11104
|
+
url: "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz",
|
|
11105
|
+
sha256: "adc567f5a423c6e0db071272f7021b1a6d118368b7608eff22b8e61c4c7a6745",
|
|
10690
11106
|
extractPath: "package/",
|
|
10691
11107
|
archiveType: "tar.gz"
|
|
10692
11108
|
},
|
|
10693
11109
|
"win32-arm64": {
|
|
10694
11110
|
name: "rolldown",
|
|
10695
|
-
version: "1.1.
|
|
11111
|
+
version: "1.1.3",
|
|
10696
11112
|
platform: "win32",
|
|
10697
11113
|
arch: "arm64",
|
|
10698
|
-
url: "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.
|
|
10699
|
-
sha256: "
|
|
11114
|
+
url: "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz",
|
|
11115
|
+
sha256: "9d055f11eb04e60821013b30ce7d011491bcddf0169f59c7254499bb537b7da8",
|
|
10700
11116
|
extractPath: "package/",
|
|
10701
11117
|
archiveType: "tar.gz"
|
|
10702
11118
|
}
|
|
@@ -14472,6 +14888,191 @@ function registerClusterNameCommands(program, getClient) {
|
|
|
14472
14888
|
});
|
|
14473
14889
|
}
|
|
14474
14890
|
//#endregion
|
|
14891
|
+
//#region src/cluster/reconcile-identity.ts
|
|
14892
|
+
/**
|
|
14893
|
+
* Reconcile the orchestrator's cluster identity between the DB
|
|
14894
|
+
* (`cluster_meta.cluster_id`) and the durable S3 sentinel
|
|
14895
|
+
* (`<prefix>/.kici-cluster-id`).
|
|
14896
|
+
*
|
|
14897
|
+
* Used by `kici-admin cluster reconcile-identity` and the staging deploy's
|
|
14898
|
+
* pre-orchestrator-start self-heal step. Talks DB + S3 directly (never the
|
|
14899
|
+
* orchestrator HTTP admin API) so it works while the orchestrator process is
|
|
14900
|
+
* down — which is exactly when a "Cluster identity mismatch" boot failure needs
|
|
14901
|
+
* fixing. Default direction restores the DB FROM the durable sentinel (the
|
|
14902
|
+
* cross-restart / peer anchor); `sentinel-from-db` reverses it.
|
|
14903
|
+
*/
|
|
14904
|
+
async function makeS3Client(s3) {
|
|
14905
|
+
const { S3Client } = await import("@aws-sdk/client-s3");
|
|
14906
|
+
return new S3Client({
|
|
14907
|
+
region: s3.region ?? "us-east-1",
|
|
14908
|
+
credentials: {
|
|
14909
|
+
accessKeyId: s3.accessKeyId,
|
|
14910
|
+
secretAccessKey: s3.secretAccessKey
|
|
14911
|
+
},
|
|
14912
|
+
...s3.endpoint ? { endpoint: s3.endpoint } : {},
|
|
14913
|
+
...s3.forcePathStyle ? { forcePathStyle: true } : {}
|
|
14914
|
+
});
|
|
14915
|
+
}
|
|
14916
|
+
async function readClusterIdFromDb(databaseUrl) {
|
|
14917
|
+
const pool = createPool(databaseUrl);
|
|
14918
|
+
try {
|
|
14919
|
+
return (await pool.query(`SELECT value FROM cluster_meta WHERE key = 'cluster_id'`)).rows[0]?.value ?? null;
|
|
14920
|
+
} finally {
|
|
14921
|
+
await pool.end();
|
|
14922
|
+
}
|
|
14923
|
+
}
|
|
14924
|
+
async function writeClusterIdToDb(databaseUrl, clusterId) {
|
|
14925
|
+
const pool = createPool(databaseUrl);
|
|
14926
|
+
try {
|
|
14927
|
+
await pool.query(`UPDATE cluster_meta SET value = $1 WHERE key = 'cluster_id'`, [clusterId]);
|
|
14928
|
+
} finally {
|
|
14929
|
+
await pool.end();
|
|
14930
|
+
}
|
|
14931
|
+
}
|
|
14932
|
+
async function readSentinel(s3) {
|
|
14933
|
+
const { GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
14934
|
+
const client = await makeS3Client(s3);
|
|
14935
|
+
const key = clusterSentinelKey(s3.prefix);
|
|
14936
|
+
try {
|
|
14937
|
+
return (await (await client.send(new GetObjectCommand({
|
|
14938
|
+
Bucket: s3.bucket,
|
|
14939
|
+
Key: key
|
|
14940
|
+
}))).Body?.transformToString())?.trim() ?? null;
|
|
14941
|
+
} catch (err) {
|
|
14942
|
+
const e = err;
|
|
14943
|
+
if (e?.name === "NoSuchKey" || e?.$metadata?.httpStatusCode === 404) return null;
|
|
14944
|
+
throw err;
|
|
14945
|
+
}
|
|
14946
|
+
}
|
|
14947
|
+
async function writeSentinel(s3, clusterId) {
|
|
14948
|
+
const { PutObjectCommand } = await import("@aws-sdk/client-s3");
|
|
14949
|
+
await (await makeS3Client(s3)).send(new PutObjectCommand({
|
|
14950
|
+
Bucket: s3.bucket,
|
|
14951
|
+
Key: clusterSentinelKey(s3.prefix),
|
|
14952
|
+
Body: clusterId,
|
|
14953
|
+
ContentType: "text/plain"
|
|
14954
|
+
}));
|
|
14955
|
+
}
|
|
14956
|
+
/**
|
|
14957
|
+
* Indirection object for the DB + S3 reads/writes, so the unit test can stub
|
|
14958
|
+
* the I/O without an ESM partial-mock dance. `buildReconcileStep` calls every
|
|
14959
|
+
* reader/writer through this object; the test overrides its members directly.
|
|
14960
|
+
*/
|
|
14961
|
+
const reconcileIo = {
|
|
14962
|
+
readClusterIdFromDb,
|
|
14963
|
+
writeClusterIdToDb,
|
|
14964
|
+
readSentinel,
|
|
14965
|
+
writeSentinel
|
|
14966
|
+
};
|
|
14967
|
+
/**
|
|
14968
|
+
* Build the idempotent step that reconciles the cluster identity in the given
|
|
14969
|
+
* direction. `check()` returns drift when the two sides disagree (or null when
|
|
14970
|
+
* in sync); `apply()` performs the single write that brings them into
|
|
14971
|
+
* agreement. Run it through `runIdempotentStep` (`@kici-dev/core/idempotency`).
|
|
14972
|
+
*/
|
|
14973
|
+
function buildReconcileStep(deps) {
|
|
14974
|
+
const { databaseUrl, s3, direction } = deps;
|
|
14975
|
+
return {
|
|
14976
|
+
name: `cluster reconcile-identity (${direction})`,
|
|
14977
|
+
check: async () => {
|
|
14978
|
+
const dbClusterId = await reconcileIo.readClusterIdFromDb(databaseUrl);
|
|
14979
|
+
const sentinelClusterId = await reconcileIo.readSentinel(s3);
|
|
14980
|
+
if (dbClusterId === null) throw new Error("cluster_meta.cluster_id is missing — run orchestrator migrations first.");
|
|
14981
|
+
if (direction === "db-from-sentinel") {
|
|
14982
|
+
if (sentinelClusterId === null) throw new Error("No S3 sentinel to restore the DB from. Use --adopt-db to write the sentinel from the DB instead.");
|
|
14983
|
+
if (dbClusterId === sentinelClusterId) return null;
|
|
14984
|
+
} else if (sentinelClusterId === dbClusterId) return null;
|
|
14985
|
+
return {
|
|
14986
|
+
dbClusterId,
|
|
14987
|
+
sentinelClusterId,
|
|
14988
|
+
direction
|
|
14989
|
+
};
|
|
14990
|
+
},
|
|
14991
|
+
summarize: (drift) => drift.direction === "db-from-sentinel" ? `Rewrite DB cluster_id ${drift.dbClusterId} -> ${drift.sentinelClusterId} (from S3 sentinel s3://${s3.bucket}/${clusterSentinelKey(s3.prefix)}).` : `Rewrite S3 sentinel ${drift.sentinelClusterId ?? "(absent)"} -> ${drift.dbClusterId} (from DB cluster_meta.cluster_id).`,
|
|
14992
|
+
apply: async (drift) => {
|
|
14993
|
+
if (drift.direction === "db-from-sentinel") await reconcileIo.writeClusterIdToDb(databaseUrl, drift.sentinelClusterId);
|
|
14994
|
+
else await reconcileIo.writeSentinel(s3, drift.dbClusterId);
|
|
14995
|
+
}
|
|
14996
|
+
};
|
|
14997
|
+
}
|
|
14998
|
+
//#endregion
|
|
14999
|
+
//#region src/cli/commands/cluster.ts
|
|
15000
|
+
/**
|
|
15001
|
+
* Cluster recovery commands for kici-admin.
|
|
15002
|
+
*
|
|
15003
|
+
* Subcommand namespace: `kici-admin cluster reconcile-identity`.
|
|
15004
|
+
*
|
|
15005
|
+
* Reconciles the orchestrator's cluster identity between the DB
|
|
15006
|
+
* (`cluster_meta.cluster_id`) and the durable S3 sentinel
|
|
15007
|
+
* (`<prefix>/.kici-cluster-id`) when they diverge and the orchestrator refuses
|
|
15008
|
+
* to start with a "Cluster identity mismatch" error.
|
|
15009
|
+
*
|
|
15010
|
+
* DB + S3 direct (NOT the orchestrator HTTP admin API), so it works while the
|
|
15011
|
+
* orchestrator process is crash-looping — which is exactly when it is needed.
|
|
15012
|
+
* Default direction restores the DB FROM the durable sentinel (the
|
|
15013
|
+
* cross-restart / peer anchor); --adopt-db reverses it.
|
|
15014
|
+
*/
|
|
15015
|
+
function resolveDatabaseUrl$1(explicit) {
|
|
15016
|
+
const url = explicit ?? process.env.KICI_DATABASE_URL;
|
|
15017
|
+
if (!url) throw new Error("Database URL required. Pass --database-url or set KICI_DATABASE_URL.");
|
|
15018
|
+
return url;
|
|
15019
|
+
}
|
|
15020
|
+
function resolveS3(opts) {
|
|
15021
|
+
const bucket = opts.bucket ?? process.env.KICI_STORAGE_BUCKET;
|
|
15022
|
+
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
15023
|
+
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
15024
|
+
if (!bucket) throw new Error("S3 bucket required. Pass --bucket or set KICI_STORAGE_BUCKET.");
|
|
15025
|
+
if (!accessKeyId || !secretAccessKey) throw new Error("AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set for the S3 sentinel.");
|
|
15026
|
+
return {
|
|
15027
|
+
bucket,
|
|
15028
|
+
prefix: opts.prefix ?? process.env.KICI_STORAGE_PREFIX ?? "",
|
|
15029
|
+
region: opts.region ?? process.env.KICI_STORAGE_REGION,
|
|
15030
|
+
endpoint: opts.endpoint ?? process.env.KICI_STORAGE_ENDPOINT,
|
|
15031
|
+
forcePathStyle: opts.forcePathStyle ?? process.env.KICI_STORAGE_FORCE_PATH_STYLE === "true",
|
|
15032
|
+
accessKeyId,
|
|
15033
|
+
secretAccessKey
|
|
15034
|
+
};
|
|
15035
|
+
}
|
|
15036
|
+
async function cliConfirm(message) {
|
|
15037
|
+
if (!process.stdin.isTTY) return false;
|
|
15038
|
+
const rl = createInterface({
|
|
15039
|
+
input: process.stdin,
|
|
15040
|
+
output: process.stderr
|
|
15041
|
+
});
|
|
15042
|
+
try {
|
|
15043
|
+
const answer = await new Promise((resolve) => {
|
|
15044
|
+
rl.question(`${message}\nProceed? [y/N] `, resolve);
|
|
15045
|
+
});
|
|
15046
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
15047
|
+
} finally {
|
|
15048
|
+
rl.close();
|
|
15049
|
+
}
|
|
15050
|
+
}
|
|
15051
|
+
function registerClusterCommands(program) {
|
|
15052
|
+
program.command("cluster").description("Cluster identity recovery (DB <-> S3 sentinel reconcile).").command("reconcile-identity").description("Reconcile cluster_meta.cluster_id with the S3 sentinel. Default restores the DB from the sentinel.").option("--database-url <url>", "Orchestrator DB URL (else KICI_DATABASE_URL)").option("--bucket <bucket>", "S3 bucket (else KICI_STORAGE_BUCKET)").option("--prefix <prefix>", "Storage prefix (else KICI_STORAGE_PREFIX, default empty = bucket root)").option("--region <region>", "S3 region (else KICI_STORAGE_REGION)").option("--endpoint <url>", "S3 endpoint (else KICI_STORAGE_ENDPOINT)").option("--force-path-style", "Use S3 path-style addressing").option("--adopt-db", "Reverse direction: rewrite the sentinel from the DB cluster_id").option("--dry-run", "Report drift and exit without changing anything").option("--yes", "Skip confirmation and apply on drift").action(async (opts) => {
|
|
15053
|
+
try {
|
|
15054
|
+
const result = await runIdempotentStep(buildReconcileStep({
|
|
15055
|
+
databaseUrl: resolveDatabaseUrl$1(opts.databaseUrl),
|
|
15056
|
+
s3: resolveS3(opts),
|
|
15057
|
+
direction: opts.adoptDb ? "sentinel-from-db" : "db-from-sentinel"
|
|
15058
|
+
}), {
|
|
15059
|
+
confirm: cliConfirm,
|
|
15060
|
+
yes: opts.yes,
|
|
15061
|
+
dryRun: opts.dryRun,
|
|
15062
|
+
log: (line) => console.log(line)
|
|
15063
|
+
});
|
|
15064
|
+
if (result.outcome === "applied") console.log("Cluster identity reconciled. Restart the orchestrator to verify it boots clean.");
|
|
15065
|
+
else if (result.outcome === "declined") {
|
|
15066
|
+
console.log("No changes made (declined).");
|
|
15067
|
+
process.exit(1);
|
|
15068
|
+
}
|
|
15069
|
+
} catch (err) {
|
|
15070
|
+
console.error(`Error: ${toErrorMessage(err)}`);
|
|
15071
|
+
process.exit(1);
|
|
15072
|
+
}
|
|
15073
|
+
});
|
|
15074
|
+
}
|
|
15075
|
+
//#endregion
|
|
14475
15076
|
//#region src/cli/commands/maintenance.ts
|
|
14476
15077
|
/**
|
|
14477
15078
|
* Maintenance CLI commands for kici-admin.
|
|
@@ -14777,6 +15378,21 @@ function registerEnvironmentCommands(program, getClient) {
|
|
|
14777
15378
|
process.exit(1);
|
|
14778
15379
|
}
|
|
14779
15380
|
});
|
|
15381
|
+
env.command("purge").description("Delete all environments (and held runs) for an org — direct-DB break-glass / warm-start reset").option("--database-url <url>", "Use direct DB access (or KICI_DATABASE_URL)").option("--org <id>", "Restrict purge to a single org (omit to purge all orgs)").option("--json", "Emit JSON output").action(async (opts) => {
|
|
15382
|
+
try {
|
|
15383
|
+
const dbUrl = resolveDirectDbUrl$4(opts.databaseUrl);
|
|
15384
|
+
if (!dbUrl) {
|
|
15385
|
+
console.error("Error: --database-url or KICI_DATABASE_URL is required (direct-DB only)");
|
|
15386
|
+
process.exit(1);
|
|
15387
|
+
}
|
|
15388
|
+
const result = await purgeEnvironmentsDirect(dbUrl, opts.org);
|
|
15389
|
+
if (opts.json) console.log(JSON.stringify(result));
|
|
15390
|
+
else console.log(`environment purge: environmentsDeleted=${result.environmentsDeleted} heldRunsDeleted=${result.heldRunsDeleted}` + (opts.org ? ` (org ${opts.org})` : " (all orgs)"));
|
|
15391
|
+
} catch (err) {
|
|
15392
|
+
console.error(`Error: ${toErrorMessage(err)}`);
|
|
15393
|
+
process.exit(1);
|
|
15394
|
+
}
|
|
15395
|
+
});
|
|
14780
15396
|
env.command("create-template").description("Create or update an environment template + its seed variables").requiredOption("--org <id>", "Org ID").requiredOption("--template <name>", "Template name").option("--type <t>", "Environment type (defaults to \"template\")", "template").option("--branch-restrictions <json>", "JSON array of allowed branches").option("--required-reviewers <csv>", "CSV of required reviewer user IDs").option("--wait-timer <seconds>", "Wait timer (seconds)").option("--hold-expiry <seconds>", "Hold expiry TTL (seconds)").option("--minimum-trust <level>", "Minimum trust (known|trusted)").option("--variables <json>", "JSON object of env variables to seed (e.g. '{\"K\":\"V\"}')").option("--database-url <url>", "Use direct DB access instead of HTTP (offline mode)").option("--json", "Emit JSON output").action(async (opts) => {
|
|
14781
15397
|
try {
|
|
14782
15398
|
const branchRestrictions = parseJsonOption(opts.branchRestrictions, "--branch-restrictions");
|
|
@@ -17598,7 +18214,7 @@ function parseRuleHandles(nftOutput, identifier) {
|
|
|
17598
18214
|
const execFileP$1 = promisify(execFile);
|
|
17599
18215
|
const logger$4 = createLogger({ prefix: "fc-host-network" });
|
|
17600
18216
|
/** Privileged binaries that need `sudo -n` on a non-root orchestrator host. */
|
|
17601
|
-
const PRIVILEGED_BINS = new Set([
|
|
18217
|
+
const PRIVILEGED_BINS = /* @__PURE__ */ new Set([
|
|
17602
18218
|
"ip",
|
|
17603
18219
|
"nft",
|
|
17604
18220
|
"sysctl"
|
|
@@ -20663,7 +21279,7 @@ function buildProgram() {
|
|
|
20663
21279
|
registerRemoteSourceCommands(program);
|
|
20664
21280
|
registerWorkflowCommands(program, getClient);
|
|
20665
21281
|
registerRunsCommands(program, getClient);
|
|
20666
|
-
registerAttestationsCommands(program);
|
|
21282
|
+
registerAttestationsCommands(program, getClient);
|
|
20667
21283
|
registerEventLogCommands(program, getClient);
|
|
20668
21284
|
registerAccessLogCommands(program, getClient);
|
|
20669
21285
|
registerBackendCommands(program, getClient);
|
|
@@ -20678,6 +21294,7 @@ function buildProgram() {
|
|
|
20678
21294
|
registerHostCommands(program);
|
|
20679
21295
|
registerOrgSettingsCommands(program, getClient);
|
|
20680
21296
|
registerClusterNameCommands(program, getClient);
|
|
21297
|
+
registerClusterCommands(program);
|
|
20681
21298
|
registerMaintenanceCommands(program, getClient);
|
|
20682
21299
|
registerEnvironmentCommands(program, getClient);
|
|
20683
21300
|
registerVariableCommands(program, getClient);
|