@kici-dev/orchestrator 0.1.21 → 0.1.22

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.
Files changed (44) hide show
  1. package/dist/agent/dispatcher.d.ts +44 -0
  2. package/dist/agent/host-roster-reaper.d.ts +2 -1
  3. package/dist/agent/host-roster.d.ts +18 -0
  4. package/dist/approvals/step-approval-bridge.d.ts +5 -0
  5. package/dist/cli/commands/source-manifest.d.ts +8 -0
  6. package/dist/cli/service/compose.d.ts +13 -0
  7. package/dist/cli/service/deploy-env.d.ts +31 -0
  8. package/dist/cli.js +515 -209
  9. package/dist/config.d.ts +6 -0
  10. package/dist/dashboard/needs-edges.d.ts +4 -3
  11. package/dist/db/migrations/047_needs_run_on.d.ts +4 -0
  12. package/dist/db/migrations/048_host_reboot_pending.d.ts +19 -0
  13. package/dist/db/migrations/049_held_runs_payload.d.ts +14 -0
  14. package/dist/db/migrations/050_sources_slug.d.ts +17 -0
  15. package/dist/db/types.d.ts +27 -4
  16. package/dist/deployment/deployment-identity.d.ts +9 -0
  17. package/dist/entry-helpers.d.ts +7 -0
  18. package/dist/environments/held-runs.d.ts +7 -1
  19. package/dist/github-app-name-refresher/github-app-name-refresher.d.ts +77 -0
  20. package/dist/index.js +21 -2
  21. package/dist/orchestrator-core.d.ts +13 -1
  22. package/dist/pipeline/decorating-secret-resolver.d.ts +32 -0
  23. package/dist/pipeline/dispatch-matched-workflow.d.ts +75 -3
  24. package/dist/pipeline/install-secrets-resolver.d.ts +2 -2
  25. package/dist/pipeline/needs-scheduler.d.ts +22 -13
  26. package/dist/pipeline/processor.d.ts +2 -2
  27. package/dist/pipeline/test-pipeline.d.ts +31 -59
  28. package/dist/providers/github/manifest.d.ts +25 -0
  29. package/dist/routes/admin-sources.d.ts +7 -0
  30. package/dist/secrets/secret-resolver.d.ts +16 -1
  31. package/dist/server.js +2728 -1725
  32. package/dist/sources/source-store.d.ts +4 -0
  33. package/dist/sources/source-validator.d.ts +2 -0
  34. package/dist/stale-detector/reboot-deadline-sweep.d.ts +29 -0
  35. package/dist/standalone.js +1307 -677
  36. package/dist/ws/agent-handler.d.ts +11 -6
  37. package/dist/ws/dashboard-fleet-handler.d.ts +33 -0
  38. package/dist/ws/dashboard-fleet-write-handler.d.ts +60 -0
  39. package/dist/ws/fleet-runs-on-all.d.ts +16 -0
  40. package/dist/ws/platform-client.d.ts +13 -1
  41. package/dist/ws/test-relay-handlers.d.ts +4 -2
  42. package/installer-image-digests.json +3 -3
  43. package/package.json +4 -4
  44. package/sbom.spdx.json +50 -50
package/dist/cli.js CHANGED
@@ -1556,8 +1556,8 @@ var init_client = __esmMin((() => {
1556
1556
  //#region src/db/migrations/001_initial.ts
1557
1557
  init_client();
1558
1558
  var _001_initial_exports = /* @__PURE__ */ __exportAll({
1559
- down: () => down$45,
1560
- up: () => up$45
1559
+ down: () => down$49,
1560
+ up: () => up$49
1561
1561
  });
1562
1562
  /**
1563
1563
  * Squashed initial migration -- creates the complete Orchestrator database schema.
@@ -2249,7 +2249,7 @@ const DDL_STATEMENTS = [
2249
2249
  `ALTER TABLE ONLY public.held_runs
2250
2250
  ADD CONSTRAINT held_runs_environment_id_fkey FOREIGN KEY (environment_id) REFERENCES public.environments(id);`
2251
2251
  ];
2252
- async function up$45(db) {
2252
+ async function up$49(db) {
2253
2253
  for (const stmt of DDL_STATEMENTS) await sql.raw(stmt).execute(db);
2254
2254
  await sql`
2255
2255
  INSERT INTO cluster_meta (key, value)
@@ -2271,7 +2271,7 @@ async function up$45(db) {
2271
2271
  * Rollback drops everything created above. Uses CASCADE on table drops to cut
2272
2272
  * through the FK graph without relying on exact topological order.
2273
2273
  */
2274
- async function down$45(db) {
2274
+ async function down$49(db) {
2275
2275
  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);
2276
2276
  for (const table of [
2277
2277
  "workflow_registrations",
@@ -2318,8 +2318,8 @@ async function down$45(db) {
2318
2318
  //#endregion
2319
2319
  //#region src/db/migrations/002_config_versions_key_version.ts
2320
2320
  var _002_config_versions_key_version_exports = /* @__PURE__ */ __exportAll({
2321
- down: () => down$44,
2322
- up: () => up$44
2321
+ down: () => down$48,
2322
+ up: () => up$48
2323
2323
  });
2324
2324
  /**
2325
2325
  * Add key_version column to config_versions so that sensitive-field encryption
@@ -2333,13 +2333,13 @@ var _002_config_versions_key_version_exports = /* @__PURE__ */ __exportAll({
2333
2333
  * No index is needed: rotation does a full-table scan; reads are by
2334
2334
  * `version` primary key and never filter on `key_version`.
2335
2335
  */
2336
- async function up$44(db) {
2336
+ async function up$48(db) {
2337
2337
  await sql`
2338
2338
  ALTER TABLE public.config_versions
2339
2339
  ADD COLUMN key_version integer NOT NULL DEFAULT 1
2340
2340
  `.execute(db);
2341
2341
  }
2342
- async function down$44(db) {
2342
+ async function down$48(db) {
2343
2343
  await sql`
2344
2344
  ALTER TABLE public.config_versions
2345
2345
  DROP COLUMN key_version
@@ -2348,8 +2348,8 @@ async function down$44(db) {
2348
2348
  //#endregion
2349
2349
  //#region src/db/migrations/003_access_log.ts
2350
2350
  var _003_access_log_exports = /* @__PURE__ */ __exportAll({
2351
- down: () => down$43,
2352
- up: () => up$43
2351
+ down: () => down$47,
2352
+ up: () => up$47
2353
2353
  });
2354
2354
  /**
2355
2355
  * Access log: one row per read or orchestrator-admin mutation attributable
@@ -2373,7 +2373,7 @@ var _003_access_log_exports = /* @__PURE__ */ __exportAll({
2373
2373
  * Retention is TTL-based via expires_at; packages/orchestrator/src/queue/
2374
2374
  * cleanup.ts picks up the prune pass.
2375
2375
  */
2376
- async function up$43(db) {
2376
+ async function up$47(db) {
2377
2377
  await sql`
2378
2378
  CREATE TABLE public.access_log (
2379
2379
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -2411,28 +2411,28 @@ async function up$43(db) {
2411
2411
  ON public.access_log (actor_type, actor_id, created_at DESC)
2412
2412
  `.execute(db);
2413
2413
  }
2414
- async function down$43(db) {
2414
+ async function down$47(db) {
2415
2415
  await sql`DROP TABLE IF EXISTS public.access_log`.execute(db);
2416
2416
  }
2417
2417
  //#endregion
2418
2418
  //#region src/db/migrations/004_rename_bundle_to_source.ts
2419
2419
  var _004_rename_bundle_to_source_exports = /* @__PURE__ */ __exportAll({
2420
- down: () => down$42,
2421
- up: () => up$42
2420
+ down: () => down$46,
2421
+ up: () => up$46
2422
2422
  });
2423
- async function up$42(db) {
2423
+ async function up$46(db) {
2424
2424
  await db.schema.alterTable("dispatch_queue").renameColumn("bundle_url", "source_tar_url").execute();
2425
2425
  await db.schema.alterTable("dispatch_queue").renameColumn("bundle_hash", "source_tar_hash").execute();
2426
2426
  }
2427
- async function down$42(db) {
2427
+ async function down$46(db) {
2428
2428
  await db.schema.alterTable("dispatch_queue").renameColumn("source_tar_url", "bundle_url").execute();
2429
2429
  await db.schema.alterTable("dispatch_queue").renameColumn("source_tar_hash", "bundle_hash").execute();
2430
2430
  }
2431
2431
  //#endregion
2432
2432
  //#region src/db/migrations/005_cold_store_chunk_counter.ts
2433
2433
  var _005_cold_store_chunk_counter_exports = /* @__PURE__ */ __exportAll({
2434
- down: () => down$41,
2435
- up: () => up$41
2434
+ down: () => down$45,
2435
+ up: () => up$45
2436
2436
  });
2437
2437
  /**
2438
2438
  * Cold-store chunk counter table.
@@ -2449,7 +2449,7 @@ var _005_cold_store_chunk_counter_exports = /* @__PURE__ */ __exportAll({
2449
2449
  *
2450
2450
  * sections 5 and 8.
2451
2451
  */
2452
- async function up$41(db) {
2452
+ async function up$45(db) {
2453
2453
  await sql`
2454
2454
  CREATE TABLE public.cold_store_chunk_counts (
2455
2455
  db TEXT NOT NULL,
@@ -2467,14 +2467,14 @@ async function up$41(db) {
2467
2467
  ON public.cold_store_chunk_counts (db, table_name)
2468
2468
  `.execute(db);
2469
2469
  }
2470
- async function down$41(db) {
2470
+ async function down$45(db) {
2471
2471
  await sql`DROP TABLE IF EXISTS public.cold_store_chunk_counts`.execute(db);
2472
2472
  }
2473
2473
  //#endregion
2474
2474
  //#region src/db/migrations/006_runs_jobs_steps_archived_at.ts
2475
2475
  var _006_runs_jobs_steps_archived_at_exports = /* @__PURE__ */ __exportAll({
2476
- down: () => down$40,
2477
- up: () => up$40
2476
+ down: () => down$44,
2477
+ up: () => up$44
2478
2478
  });
2479
2479
  /**
2480
2480
  * `execution_runs` / `execution_jobs` / `execution_steps` cold-store
@@ -2510,7 +2510,7 @@ var _006_runs_jobs_steps_archived_at_exports = /* @__PURE__ */ __exportAll({
2510
2510
  * - `idx_execution_jobs_routing_key_created (routing_key, created_at)`
2511
2511
  * - `idx_execution_steps_routing_key_created (routing_key, created_at)`
2512
2512
  */
2513
- async function up$40(db) {
2513
+ async function up$44(db) {
2514
2514
  await sql`
2515
2515
  ALTER TABLE public.execution_runs
2516
2516
  ADD COLUMN archived_at TIMESTAMPTZ NULL,
@@ -2555,7 +2555,7 @@ async function up$40(db) {
2555
2555
  ON public.execution_steps (routing_key, created_at)
2556
2556
  `.execute(db);
2557
2557
  }
2558
- async function down$40(db) {
2558
+ async function down$44(db) {
2559
2559
  await sql`DROP INDEX IF EXISTS public.idx_execution_steps_routing_key_created`.execute(db);
2560
2560
  await sql`
2561
2561
  ALTER TABLE public.execution_steps
@@ -2580,8 +2580,8 @@ async function down$40(db) {
2580
2580
  //#endregion
2581
2581
  //#region src/db/migrations/007_audit_logs_archived_at.ts
2582
2582
  var _007_audit_logs_archived_at_exports = /* @__PURE__ */ __exportAll({
2583
- down: () => down$39,
2584
- up: () => up$39
2583
+ down: () => down$43,
2584
+ up: () => up$43
2585
2585
  });
2586
2586
  /**
2587
2587
  * `secret_audit_log` and `access_log` cold-store schema additions, plus
@@ -2621,7 +2621,7 @@ var _007_audit_logs_archived_at_exports = /* @__PURE__ */ __exportAll({
2621
2621
  * `down()` would not have meaningful retention bounds. Acceptable for
2622
2622
  * staging.
2623
2623
  */
2624
- async function up$39(db) {
2624
+ async function up$43(db) {
2625
2625
  await sql`
2626
2626
  ALTER TABLE public.secret_audit_log
2627
2627
  ADD COLUMN archived_at TIMESTAMPTZ NULL,
@@ -2642,7 +2642,7 @@ async function up$39(db) {
2642
2642
  DROP COLUMN IF EXISTS expires_at
2643
2643
  `.execute(db);
2644
2644
  }
2645
- async function down$39(db) {
2645
+ async function down$43(db) {
2646
2646
  await sql`
2647
2647
  ALTER TABLE public.access_log
2648
2648
  ADD COLUMN expires_at TIMESTAMPTZ NOT NULL DEFAULT (now() + INTERVAL '90 days')
@@ -2670,8 +2670,8 @@ async function down$39(db) {
2670
2670
  //#endregion
2671
2671
  //#region src/db/migrations/008_event_log_archived_at.ts
2672
2672
  var _008_event_log_archived_at_exports = /* @__PURE__ */ __exportAll({
2673
- down: () => down$38,
2674
- up: () => up$38
2673
+ down: () => down$42,
2674
+ up: () => up$42
2675
2675
  });
2676
2676
  /**
2677
2677
  * `event_log` cold-store schema additions plus removal of the
@@ -2709,7 +2709,7 @@ var _008_event_log_archived_at_exports = /* @__PURE__ */ __exportAll({
2709
2709
  * — best effort; rows inserted between `up()` and a hypothetical
2710
2710
  * `down()` would not have meaningful retention bounds.
2711
2711
  */
2712
- async function up$38(db) {
2712
+ async function up$42(db) {
2713
2713
  await sql`
2714
2714
  ALTER TABLE public.event_log
2715
2715
  ADD COLUMN archived_at TIMESTAMPTZ NULL,
@@ -2725,7 +2725,7 @@ async function up$38(db) {
2725
2725
  DROP COLUMN IF EXISTS expires_at
2726
2726
  `.execute(db);
2727
2727
  }
2728
- async function down$38(db) {
2728
+ async function down$42(db) {
2729
2729
  await sql`
2730
2730
  ALTER TABLE public.event_log
2731
2731
  ADD COLUMN expires_at TIMESTAMPTZ NOT NULL DEFAULT (now() + INTERVAL '30 days')
@@ -2744,8 +2744,8 @@ async function down$38(db) {
2744
2744
  //#endregion
2745
2745
  //#region src/db/migrations/009_access_log_trigram.ts
2746
2746
  var _009_access_log_trigram_exports = /* @__PURE__ */ __exportAll({
2747
- down: () => down$37,
2748
- up: () => up$37
2747
+ down: () => down$41,
2748
+ up: () => up$41
2749
2749
  });
2750
2750
  /**
2751
2751
  * Trigram (pg_trgm) index on access_log.error_message for the federated
@@ -2758,7 +2758,7 @@ var _009_access_log_trigram_exports = /* @__PURE__ */ __exportAll({
2758
2758
  * EXISTS` are both safe to re-run. No CONCURRENTLY because Kysely runs
2759
2759
  * migrations inside a transaction; the lock is brief on a sampled table.
2760
2760
  */
2761
- async function up$37(db) {
2761
+ async function up$41(db) {
2762
2762
  await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`.execute(db);
2763
2763
  await sql`
2764
2764
  CREATE INDEX IF NOT EXISTS access_log_error_message_trgm_idx
@@ -2767,14 +2767,14 @@ async function up$37(db) {
2767
2767
  WHERE error_message IS NOT NULL
2768
2768
  `.execute(db);
2769
2769
  }
2770
- async function down$37(db) {
2770
+ async function down$41(db) {
2771
2771
  await sql`DROP INDEX IF EXISTS public.access_log_error_message_trgm_idx`.execute(db);
2772
2772
  }
2773
2773
  //#endregion
2774
2774
  //#region src/db/migrations/010_cold_store_chunks.ts
2775
2775
  var _010_cold_store_chunks_exports = /* @__PURE__ */ __exportAll({
2776
- down: () => down$36,
2777
- up: () => up$36
2776
+ down: () => down$40,
2777
+ up: () => up$40
2778
2778
  });
2779
2779
  /**
2780
2780
  * Cold-store chunk index — Phase 2 (cold-store purge).
@@ -2806,7 +2806,7 @@ var _010_cold_store_chunks_exports = /* @__PURE__ */ __exportAll({
2806
2806
  * forever. Adapters that don't opt into per-bucket archival via
2807
2807
  * `coldTtlDays` don't insert here either.
2808
2808
  */
2809
- async function up$36(db) {
2809
+ async function up$40(db) {
2810
2810
  await sql`
2811
2811
  CREATE TABLE public.cold_store_chunks (
2812
2812
  db TEXT NOT NULL,
@@ -2833,14 +2833,14 @@ async function up$36(db) {
2833
2833
  ON public.cold_store_chunks (db, table_name, tenant_id, archived_at DESC)
2834
2834
  `.execute(db);
2835
2835
  }
2836
- async function down$36(db) {
2836
+ async function down$40(db) {
2837
2837
  await sql`DROP TABLE IF EXISTS public.cold_store_chunks`.execute(db);
2838
2838
  }
2839
2839
  //#endregion
2840
2840
  //#region src/db/migrations/011_drop_source_secrets_notify.ts
2841
2841
  var _011_drop_source_secrets_notify_exports = /* @__PURE__ */ __exportAll({
2842
- down: () => down$35,
2843
- up: () => up$35
2842
+ down: () => down$39,
2843
+ up: () => up$39
2844
2844
  });
2845
2845
  /**
2846
2846
  * Drop the `source_secrets_change_trigger` and the
@@ -2859,11 +2859,11 @@ var _011_drop_source_secrets_notify_exports = /* @__PURE__ */ __exportAll({
2859
2859
  * in `001_initial.ts`. They wake up no consumer until the `WebhookSecretManager`
2860
2860
  * is restored, so this migration is safe to roll back.
2861
2861
  */
2862
- async function up$35(db) {
2862
+ async function up$39(db) {
2863
2863
  await sql`DROP TRIGGER IF EXISTS source_secrets_change_trigger ON public.scoped_secrets`.execute(db);
2864
2864
  await sql`DROP FUNCTION IF EXISTS public.notify_source_secrets_change() CASCADE`.execute(db);
2865
2865
  }
2866
- async function down$35(db) {
2866
+ async function down$39(db) {
2867
2867
  await sql`
2868
2868
  CREATE OR REPLACE FUNCTION public.notify_source_secrets_change() RETURNS trigger
2869
2869
  LANGUAGE plpgsql
@@ -2902,8 +2902,8 @@ async function down$35(db) {
2902
2902
  //#endregion
2903
2903
  //#region src/db/migrations/012_peer_credentials_active_uniq.ts
2904
2904
  var _012_peer_credentials_active_uniq_exports = /* @__PURE__ */ __exportAll({
2905
- down: () => down$34,
2906
- up: () => up$34
2905
+ down: () => down$38,
2906
+ up: () => up$38
2907
2907
  });
2908
2908
  /**
2909
2909
  * Add a partial unique index on `peer_credentials (instance_id) WHERE
@@ -2929,7 +2929,7 @@ var _012_peer_credentials_active_uniq_exports = /* @__PURE__ */ __exportAll({
2929
2929
  * `down()` only drops the index; it does NOT undo the dedupe (there's no
2930
2930
  * safe way to recreate revoked rows, and the dedupe is monotonic).
2931
2931
  */
2932
- async function up$34(db) {
2932
+ async function up$38(db) {
2933
2933
  await sql`
2934
2934
  UPDATE public.peer_credentials
2935
2935
  SET revoked_at = NOW()
@@ -2947,14 +2947,14 @@ async function up$34(db) {
2947
2947
  WHERE revoked_at IS NULL
2948
2948
  `.execute(db);
2949
2949
  }
2950
- async function down$34(db) {
2950
+ async function down$38(db) {
2951
2951
  await sql`DROP INDEX IF EXISTS public.peer_credentials_active_uniq`.execute(db);
2952
2952
  }
2953
2953
  //#endregion
2954
2954
  //#region src/db/migrations/013_execution_log_bytes.ts
2955
2955
  var _013_execution_log_bytes_exports = /* @__PURE__ */ __exportAll({
2956
- down: () => down$33,
2957
- up: () => up$33
2956
+ down: () => down$37,
2957
+ up: () => up$37
2958
2958
  });
2959
2959
  /**
2960
2960
  * Add `log_bytes BIGINT NOT NULL DEFAULT 0` columns to `execution_runs` and
@@ -2973,7 +2973,7 @@ var _013_execution_log_bytes_exports = /* @__PURE__ */ __exportAll({
2973
2973
  *
2974
2974
  * Idempotent (`ADD COLUMN IF NOT EXISTS`).
2975
2975
  */
2976
- async function up$33(db) {
2976
+ async function up$37(db) {
2977
2977
  await sql`
2978
2978
  ALTER TABLE public.execution_runs
2979
2979
  ADD COLUMN IF NOT EXISTS log_bytes BIGINT NOT NULL DEFAULT 0
@@ -2983,15 +2983,15 @@ async function up$33(db) {
2983
2983
  ADD COLUMN IF NOT EXISTS log_bytes BIGINT NOT NULL DEFAULT 0
2984
2984
  `.execute(db);
2985
2985
  }
2986
- async function down$33(db) {
2986
+ async function down$37(db) {
2987
2987
  await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS log_bytes`.execute(db);
2988
2988
  await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS log_bytes`.execute(db);
2989
2989
  }
2990
2990
  //#endregion
2991
2991
  //#region src/db/migrations/014_kici_events_lease_retry.ts
2992
2992
  var _014_kici_events_lease_retry_exports = /* @__PURE__ */ __exportAll({
2993
- down: () => down$32,
2994
- up: () => up$32
2993
+ down: () => down$36,
2994
+ up: () => up$36
2995
2995
  });
2996
2996
  /**
2997
2997
  * Add lease + retry + DLQ columns to `kici_events` so the EventRouter can
@@ -3025,7 +3025,7 @@ var _014_kici_events_lease_retry_exports = /* @__PURE__ */ __exportAll({
3025
3025
  *
3026
3026
  * Idempotent (`ADD COLUMN IF NOT EXISTS` + `CREATE INDEX IF NOT EXISTS`).
3027
3027
  */
3028
- async function up$32(db) {
3028
+ async function up$36(db) {
3029
3029
  await sql`
3030
3030
  ALTER TABLE public.kici_events
3031
3031
  ADD COLUMN IF NOT EXISTS claimed_at TIMESTAMPTZ,
@@ -3056,7 +3056,7 @@ async function up$32(db) {
3056
3056
  WHERE dlq_at IS NOT NULL
3057
3057
  `.execute(db);
3058
3058
  }
3059
- async function down$32(db) {
3059
+ async function down$36(db) {
3060
3060
  await sql`DROP INDEX IF EXISTS public.idx_kici_events_dlq`.execute(db);
3061
3061
  await sql`DROP INDEX IF EXISTS public.idx_kici_events_lease_expired`.execute(db);
3062
3062
  await sql`DROP INDEX IF EXISTS public.idx_kici_events_retry_due`.execute(db);
@@ -3074,8 +3074,8 @@ async function down$32(db) {
3074
3074
  //#endregion
3075
3075
  //#region src/db/migrations/015_org_settings_customer_scoped.ts
3076
3076
  var _015_org_settings_customer_scoped_exports = /* @__PURE__ */ __exportAll({
3077
- down: () => down$31,
3078
- up: () => up$31
3077
+ down: () => down$35,
3078
+ up: () => up$35
3079
3079
  });
3080
3080
  /**
3081
3081
  * Org-scope `org_settings` and qualify each glob entry by source.
@@ -3103,7 +3103,7 @@ var _015_org_settings_customer_scoped_exports = /* @__PURE__ */ __exportAll({
3103
3103
  * Idempotent: a re-run on an already-migrated DB sees `customer_id` exists
3104
3104
  * and the list columns are already jsonb, so it is a no-op.
3105
3105
  */
3106
- async function up$31(db) {
3106
+ async function up$35(db) {
3107
3107
  if ((await sql`
3108
3108
  SELECT EXISTS (
3109
3109
  SELECT 1 FROM information_schema.columns
@@ -3228,7 +3228,7 @@ async function up$31(db) {
3228
3228
  await sql`DROP TABLE _org_settings_merged`.execute(db);
3229
3229
  await sql`DROP TABLE _org_settings_stage`.execute(db);
3230
3230
  }
3231
- async function down$31(db) {
3231
+ async function down$35(db) {
3232
3232
  if (!(await sql`
3233
3233
  SELECT EXISTS (
3234
3234
  SELECT 1 FROM information_schema.columns
@@ -3253,8 +3253,8 @@ async function down$31(db) {
3253
3253
  //#endregion
3254
3254
  //#region src/db/migrations/016_org_settings_allow_http_npm.ts
3255
3255
  var _016_org_settings_allow_http_npm_exports = /* @__PURE__ */ __exportAll({
3256
- down: () => down$30,
3257
- up: () => up$30
3256
+ down: () => down$34,
3257
+ up: () => up$34
3258
3258
  });
3259
3259
  /**
3260
3260
  * Add `org_settings.allow_http_npm_registries boolean NOT NULL DEFAULT false`.
@@ -3267,7 +3267,7 @@ var _016_org_settings_allow_http_npm_exports = /* @__PURE__ */ __exportAll({
3267
3267
  *
3268
3268
  * Idempotent: a re-run on a DB that already has the column is a no-op.
3269
3269
  */
3270
- async function up$30(db) {
3270
+ async function up$34(db) {
3271
3271
  if ((await sql`
3272
3272
  SELECT EXISTS (
3273
3273
  SELECT 1 FROM information_schema.columns
@@ -3281,7 +3281,7 @@ async function up$30(db) {
3281
3281
  ADD COLUMN allow_http_npm_registries boolean NOT NULL DEFAULT false
3282
3282
  `.execute(db);
3283
3283
  }
3284
- async function down$30(db) {
3284
+ async function down$34(db) {
3285
3285
  await sql`
3286
3286
  ALTER TABLE public.org_settings DROP COLUMN IF EXISTS allow_http_npm_registries
3287
3287
  `.execute(db);
@@ -3289,8 +3289,8 @@ async function down$30(db) {
3289
3289
  //#endregion
3290
3290
  //#region src/db/migrations/017_org_id_widen.ts
3291
3291
  var _017_org_id_widen_exports = /* @__PURE__ */ __exportAll({
3292
- down: () => down$29,
3293
- up: () => up$29
3292
+ down: () => down$33,
3293
+ up: () => up$33
3294
3294
  });
3295
3295
  /**
3296
3296
  * Widen every orchestrator-side `org_id varchar(12)` column to
@@ -3330,17 +3330,17 @@ const ORG_ID_TABLES$1 = [
3330
3330
  "held_runs",
3331
3331
  "scoped_secrets"
3332
3332
  ];
3333
- async function up$29(db) {
3333
+ async function up$33(db) {
3334
3334
  for (const table of ORG_ID_TABLES$1) await sql.raw(`ALTER TABLE public.${table} ALTER COLUMN org_id TYPE varchar(16)`).execute(db);
3335
3335
  }
3336
- async function down$29(db) {
3336
+ async function down$33(db) {
3337
3337
  for (const table of ORG_ID_TABLES$1) await sql.raw(`ALTER TABLE public.${table} ALTER COLUMN org_id TYPE varchar(12)`).execute(db);
3338
3338
  }
3339
3339
  //#endregion
3340
3340
  //#region src/db/migrations/018_org_id_prefix_backfill.ts
3341
3341
  var _018_org_id_prefix_backfill_exports = /* @__PURE__ */ __exportAll({
3342
- down: () => down$28,
3343
- up: () => up$28
3342
+ down: () => down$32,
3343
+ up: () => up$32
3344
3344
  });
3345
3345
  /**
3346
3346
  * Prefix every orchestrator-side tenant string with `org_` to align
@@ -3385,19 +3385,19 @@ const CUSTOMER_ID_TABLES = [
3385
3385
  "workflow_registrations",
3386
3386
  "org_settings"
3387
3387
  ];
3388
- async function up$28(db) {
3388
+ async function up$32(db) {
3389
3389
  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);
3390
3390
  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);
3391
3391
  }
3392
- async function down$28(db) {
3392
+ async function down$32(db) {
3393
3393
  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);
3394
3394
  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);
3395
3395
  }
3396
3396
  //#endregion
3397
3397
  //#region src/db/migrations/019_generic_sources_change_notify.ts
3398
3398
  var _019_generic_sources_change_notify_exports = /* @__PURE__ */ __exportAll({
3399
- down: () => down$27,
3400
- up: () => up$27
3399
+ down: () => down$31,
3400
+ up: () => up$31
3401
3401
  });
3402
3402
  /**
3403
3403
  * Add a Postgres trigger on `generic_webhook_sources` that emits
@@ -3420,7 +3420,7 @@ var _019_generic_sources_change_notify_exports = /* @__PURE__ */ __exportAll({
3420
3420
  * (`notify_sources_change()` + `sources_change_trigger`, defined in
3421
3421
  * `001_initial.ts`).
3422
3422
  */
3423
- async function up$27(db) {
3423
+ async function up$31(db) {
3424
3424
  await sql`
3425
3425
  CREATE FUNCTION public.notify_generic_sources_change() RETURNS trigger
3426
3426
  LANGUAGE plpgsql
@@ -3441,15 +3441,15 @@ async function up$27(db) {
3441
3441
  FOR EACH ROW EXECUTE FUNCTION public.notify_generic_sources_change()
3442
3442
  `.execute(db);
3443
3443
  }
3444
- async function down$27(db) {
3444
+ async function down$31(db) {
3445
3445
  await sql`DROP TRIGGER IF EXISTS generic_sources_change_trigger ON public.generic_webhook_sources`.execute(db);
3446
3446
  await sql`DROP FUNCTION IF EXISTS public.notify_generic_sources_change()`.execute(db);
3447
3447
  }
3448
3448
  //#endregion
3449
3449
  //#region src/db/migrations/020_org_settings_dashboard_write_policy.ts
3450
3450
  var _020_org_settings_dashboard_write_policy_exports = /* @__PURE__ */ __exportAll({
3451
- down: () => down$26,
3452
- up: () => up$26
3451
+ down: () => down$30,
3452
+ up: () => up$30
3453
3453
  });
3454
3454
  /**
3455
3455
  * Add `org_settings.dashboard_write_policy jsonb NOT NULL DEFAULT '{}'`.
@@ -3464,7 +3464,7 @@ var _020_org_settings_dashboard_write_policy_exports = /* @__PURE__ */ __exportA
3464
3464
  *
3465
3465
  * Idempotent: a re-run on a DB that already has the column is a no-op.
3466
3466
  */
3467
- async function up$26(db) {
3467
+ async function up$30(db) {
3468
3468
  if ((await sql`
3469
3469
  SELECT EXISTS (
3470
3470
  SELECT 1 FROM information_schema.columns
@@ -3478,7 +3478,7 @@ async function up$26(db) {
3478
3478
  ADD COLUMN dashboard_write_policy jsonb NOT NULL DEFAULT '{}'::jsonb
3479
3479
  `.execute(db);
3480
3480
  }
3481
- async function down$26(db) {
3481
+ async function down$30(db) {
3482
3482
  await sql`
3483
3483
  ALTER TABLE public.org_settings DROP COLUMN IF EXISTS dashboard_write_policy
3484
3484
  `.execute(db);
@@ -3486,8 +3486,8 @@ async function down$26(db) {
3486
3486
  //#endregion
3487
3487
  //#region src/db/migrations/021_check_run_tracking.ts
3488
3488
  var _021_check_run_tracking_exports = /* @__PURE__ */ __exportAll({
3489
- down: () => down$25,
3490
- up: () => up$25
3489
+ down: () => down$29,
3490
+ up: () => up$29
3491
3491
  });
3492
3492
  /**
3493
3493
  * Add `check_run_tracking` table for HA-safe check-run state persistence.
@@ -3511,7 +3511,7 @@ var _021_check_run_tracking_exports = /* @__PURE__ */ __exportAll({
3511
3511
  *
3512
3512
  * Idempotent: a re-run on a DB that already has the table is a no-op.
3513
3513
  */
3514
- async function up$25(db) {
3514
+ async function up$29(db) {
3515
3515
  if ((await sql`
3516
3516
  SELECT EXISTS (
3517
3517
  SELECT 1 FROM information_schema.tables
@@ -3542,14 +3542,14 @@ async function up$25(db) {
3542
3542
  WHERE run_id IS NOT NULL
3543
3543
  `.execute(db);
3544
3544
  }
3545
- async function down$25(db) {
3545
+ async function down$29(db) {
3546
3546
  await sql`DROP TABLE IF EXISTS public.check_run_tracking`.execute(db);
3547
3547
  }
3548
3548
  //#endregion
3549
3549
  //#region src/db/migrations/022_scaler_manager_state.ts
3550
3550
  var _022_scaler_manager_state_exports = /* @__PURE__ */ __exportAll({
3551
- down: () => down$24,
3552
- up: () => up$24
3551
+ down: () => down$28,
3552
+ up: () => up$28
3553
3553
  });
3554
3554
  /**
3555
3555
  * Add three tables persisting `ScalerManager` per-coord state:
@@ -3576,7 +3576,7 @@ var _022_scaler_manager_state_exports = /* @__PURE__ */ __exportAll({
3576
3576
  * Idempotent: a re-run on a DB that already has any of these tables
3577
3577
  * leaves the existing one alone.
3578
3578
  */
3579
- async function up$24(db) {
3579
+ async function up$28(db) {
3580
3580
  const tableExists = async (name) => {
3581
3581
  return (await sql`
3582
3582
  SELECT EXISTS (
@@ -3626,7 +3626,7 @@ async function up$24(db) {
3626
3626
  `.execute(db);
3627
3627
  }
3628
3628
  }
3629
- async function down$24(db) {
3629
+ async function down$28(db) {
3630
3630
  await sql`DROP TABLE IF EXISTS public.scaler_reservations`.execute(db);
3631
3631
  await sql`DROP TABLE IF EXISTS public.scaler_agent_jobs`.execute(db);
3632
3632
  await sql`DROP TABLE IF EXISTS public.scaler_spawning_agents`.execute(db);
@@ -3634,8 +3634,8 @@ async function down$24(db) {
3634
3634
  //#endregion
3635
3635
  //#region src/db/migrations/023_dispatch_queue_recovery_deadline.ts
3636
3636
  var _023_dispatch_queue_recovery_deadline_exports = /* @__PURE__ */ __exportAll({
3637
- down: () => down$23,
3638
- up: () => up$23
3637
+ down: () => down$27,
3638
+ up: () => up$27
3639
3639
  });
3640
3640
  /**
3641
3641
  * Add `dispatch_queue.recovery_deadline TIMESTAMPTZ` and
@@ -3659,7 +3659,7 @@ var _023_dispatch_queue_recovery_deadline_exports = /* @__PURE__ */ __exportAll(
3659
3659
  * Idempotent: re-running on a DB that already has either column is a
3660
3660
  * no-op.
3661
3661
  */
3662
- async function up$23(db) {
3662
+ async function up$27(db) {
3663
3663
  const colExists = async (name) => {
3664
3664
  return (await sql`
3665
3665
  SELECT EXISTS (
@@ -3684,7 +3684,7 @@ async function up$23(db) {
3684
3684
  WHERE recovery_deadline IS NOT NULL
3685
3685
  `.execute(db);
3686
3686
  }
3687
- async function down$23(db) {
3687
+ async function down$27(db) {
3688
3688
  await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_recovery_deadline`.execute(db);
3689
3689
  await sql`
3690
3690
  ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS recovery_agent_id
@@ -3696,8 +3696,8 @@ async function down$23(db) {
3696
3696
  //#endregion
3697
3697
  //#region src/db/migrations/024_dispatch_queue_provisioning_error.ts
3698
3698
  var _024_dispatch_queue_provisioning_error_exports = /* @__PURE__ */ __exportAll({
3699
- down: () => down$22,
3700
- up: () => up$22
3699
+ down: () => down$26,
3700
+ up: () => up$26
3701
3701
  });
3702
3702
  /**
3703
3703
  * Add `dispatch_queue.last_provisioning_error TEXT` recording the most
@@ -3713,7 +3713,7 @@ var _024_dispatch_queue_provisioning_error_exports = /* @__PURE__ */ __exportAll
3713
3713
  *
3714
3714
  * Idempotent: re-running on a DB that already has the column is a no-op.
3715
3715
  */
3716
- async function up$22(db) {
3716
+ async function up$26(db) {
3717
3717
  const colExists = async (name) => {
3718
3718
  return (await sql`
3719
3719
  SELECT EXISTS (
@@ -3729,7 +3729,7 @@ async function up$22(db) {
3729
3729
  ADD COLUMN last_provisioning_error TEXT
3730
3730
  `.execute(db);
3731
3731
  }
3732
- async function down$22(db) {
3732
+ async function down$26(db) {
3733
3733
  await sql`
3734
3734
  ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS last_provisioning_error
3735
3735
  `.execute(db);
@@ -3737,8 +3737,8 @@ async function down$22(db) {
3737
3737
  //#endregion
3738
3738
  //#region src/db/migrations/025_init_failure.ts
3739
3739
  var _025_init_failure_exports = /* @__PURE__ */ __exportAll({
3740
- down: () => down$21,
3741
- up: () => up$21
3740
+ down: () => down$25,
3741
+ up: () => up$25
3742
3742
  });
3743
3743
  /**
3744
3744
  * Add `init_failure jsonb` columns to `execution_runs` and `execution_jobs`.
@@ -3752,7 +3752,7 @@ var _025_init_failure_exports = /* @__PURE__ */ __exportAll({
3752
3752
  * Idempotent: re-running on a DB that already has either column is a no-op
3753
3753
  * for that column.
3754
3754
  */
3755
- async function up$21(db) {
3755
+ async function up$25(db) {
3756
3756
  const colExists = async (table, name) => {
3757
3757
  return (await sql`
3758
3758
  SELECT EXISTS (
@@ -3772,7 +3772,7 @@ async function up$21(db) {
3772
3772
  ADD COLUMN init_failure JSONB DEFAULT NULL
3773
3773
  `.execute(db);
3774
3774
  }
3775
- async function down$21(db) {
3775
+ async function down$25(db) {
3776
3776
  await sql`
3777
3777
  ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS init_failure
3778
3778
  `.execute(db);
@@ -3783,8 +3783,8 @@ async function down$21(db) {
3783
3783
  //#endregion
3784
3784
  //#region src/db/migrations/026_event_log_lockfile_corrupt.ts
3785
3785
  var _026_event_log_lockfile_corrupt_exports = /* @__PURE__ */ __exportAll({
3786
- down: () => down$20,
3787
- up: () => up$20
3786
+ down: () => down$24,
3787
+ up: () => up$24
3788
3788
  });
3789
3789
  /**
3790
3790
  * Extend the event_log.status CHECK constraint with 'lockfile_corrupt' so the
@@ -3793,7 +3793,7 @@ var _026_event_log_lockfile_corrupt_exports = /* @__PURE__ */ __exportAll({
3793
3793
  *
3794
3794
  * Idempotent: the DROP ... IF EXISTS / re-ADD pair re-runs cleanly.
3795
3795
  */
3796
- async function up$20(db) {
3796
+ async function up$24(db) {
3797
3797
  await sql`ALTER TABLE event_log DROP CONSTRAINT IF EXISTS event_log_status_check`.execute(db);
3798
3798
  await sql`
3799
3799
  ALTER TABLE event_log ADD CONSTRAINT event_log_status_check
@@ -3803,7 +3803,7 @@ async function up$20(db) {
3803
3803
  ])))
3804
3804
  `.execute(db);
3805
3805
  }
3806
- async function down$20(db) {
3806
+ async function down$24(db) {
3807
3807
  await sql`ALTER TABLE event_log DROP CONSTRAINT IF EXISTS event_log_status_check`.execute(db);
3808
3808
  await sql`
3809
3809
  ALTER TABLE event_log ADD CONSTRAINT event_log_status_check
@@ -3816,8 +3816,8 @@ async function down$20(db) {
3816
3816
  //#endregion
3817
3817
  //#region src/db/migrations/027_workflow_timeout.ts
3818
3818
  var _027_workflow_timeout_exports = /* @__PURE__ */ __exportAll({
3819
- down: () => down$19,
3820
- up: () => up$19
3819
+ down: () => down$23,
3820
+ up: () => up$23
3821
3821
  });
3822
3822
  /**
3823
3823
  * Add `workflow_timeout_ms integer` to `execution_runs`.
@@ -3835,13 +3835,13 @@ var _027_workflow_timeout_exports = /* @__PURE__ */ __exportAll({
3835
3835
  *
3836
3836
  * Idempotent: re-running on a DB that already has the column is a no-op.
3837
3837
  */
3838
- async function up$19(db) {
3838
+ async function up$23(db) {
3839
3839
  await sql`
3840
3840
  ALTER TABLE public.execution_runs
3841
3841
  ADD COLUMN IF NOT EXISTS workflow_timeout_ms INTEGER DEFAULT NULL
3842
3842
  `.execute(db);
3843
3843
  }
3844
- async function down$19(db) {
3844
+ async function down$23(db) {
3845
3845
  await sql`
3846
3846
  ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS workflow_timeout_ms
3847
3847
  `.execute(db);
@@ -3849,8 +3849,8 @@ async function down$19(db) {
3849
3849
  //#endregion
3850
3850
  //#region src/db/migrations/028_org_settings_user_cache.ts
3851
3851
  var _028_org_settings_user_cache_exports = /* @__PURE__ */ __exportAll({
3852
- down: () => down$18,
3853
- up: () => up$18
3852
+ down: () => down$22,
3853
+ up: () => up$22
3854
3854
  });
3855
3855
  /**
3856
3856
  * Add `org_settings.user_cache_quota_bytes bigint` and
@@ -3879,7 +3879,7 @@ async function columnExists(db, column) {
3879
3879
  ) AS exists
3880
3880
  `.execute(db)).rows[0]?.exists ?? false;
3881
3881
  }
3882
- async function up$18(db) {
3882
+ async function up$22(db) {
3883
3883
  if (!await columnExists(db, "user_cache_quota_bytes")) await sql`
3884
3884
  ALTER TABLE public.org_settings
3885
3885
  ADD COLUMN user_cache_quota_bytes bigint
@@ -3889,7 +3889,7 @@ async function up$18(db) {
3889
3889
  ADD COLUMN user_cache_ttl_ms bigint
3890
3890
  `.execute(db);
3891
3891
  }
3892
- async function down$18(db) {
3892
+ async function down$22(db) {
3893
3893
  await sql`
3894
3894
  ALTER TABLE public.org_settings DROP COLUMN IF EXISTS user_cache_quota_bytes
3895
3895
  `.execute(db);
@@ -3900,8 +3900,8 @@ async function down$18(db) {
3900
3900
  //#endregion
3901
3901
  //#region src/db/migrations/029_dispatch_queue_attempts.ts
3902
3902
  var _029_dispatch_queue_attempts_exports = /* @__PURE__ */ __exportAll({
3903
- down: () => down$17,
3904
- up: () => up$17
3903
+ down: () => down$21,
3904
+ up: () => up$21
3905
3905
  });
3906
3906
  /**
3907
3907
  * Add `dispatch_queue.dispatch_attempts INT NOT NULL DEFAULT 0`.
@@ -3915,7 +3915,7 @@ var _029_dispatch_queue_attempts_exports = /* @__PURE__ */ __exportAll({
3915
3915
  *
3916
3916
  * Idempotent: re-running on a DB that already has the column is a no-op.
3917
3917
  */
3918
- async function up$17(db) {
3918
+ async function up$21(db) {
3919
3919
  if (!((await sql`
3920
3920
  SELECT EXISTS (
3921
3921
  SELECT 1 FROM information_schema.columns
@@ -3928,7 +3928,7 @@ async function up$17(db) {
3928
3928
  ADD COLUMN dispatch_attempts INT NOT NULL DEFAULT 0
3929
3929
  `.execute(db);
3930
3930
  }
3931
- async function down$17(db) {
3931
+ async function down$21(db) {
3932
3932
  await sql`
3933
3933
  ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS dispatch_attempts
3934
3934
  `.execute(db);
@@ -3936,8 +3936,8 @@ async function down$17(db) {
3936
3936
  //#endregion
3937
3937
  //#region src/db/migrations/030_held_runs_env_set_null.ts
3938
3938
  var _030_held_runs_env_set_null_exports = /* @__PURE__ */ __exportAll({
3939
- down: () => down$16,
3940
- up: () => up$16
3939
+ down: () => down$20,
3940
+ up: () => up$20
3941
3941
  });
3942
3942
  /**
3943
3943
  * held_runs.environment_id becomes nullable with ON DELETE SET NULL so
@@ -3948,14 +3948,14 @@ var _030_held_runs_env_set_null_exports = /* @__PURE__ */ __exportAll({
3948
3948
  * Idempotent: dropping the NOT NULL and the constraint are both no-ops on a
3949
3949
  * re-run, and the constraint is re-created with the SET NULL action.
3950
3950
  */
3951
- async function up$16(db) {
3951
+ async function up$20(db) {
3952
3952
  await sql`ALTER TABLE public.held_runs ALTER COLUMN environment_id DROP NOT NULL`.execute(db);
3953
3953
  await sql`ALTER TABLE public.held_runs DROP CONSTRAINT IF EXISTS held_runs_environment_id_fkey`.execute(db);
3954
3954
  await sql`ALTER TABLE public.held_runs
3955
3955
  ADD CONSTRAINT held_runs_environment_id_fkey
3956
3956
  FOREIGN KEY (environment_id) REFERENCES public.environments(id) ON DELETE SET NULL`.execute(db);
3957
3957
  }
3958
- async function down$16(db) {
3958
+ async function down$20(db) {
3959
3959
  await sql`ALTER TABLE public.held_runs DROP CONSTRAINT IF EXISTS held_runs_environment_id_fkey`.execute(db);
3960
3960
  await sql`ALTER TABLE public.held_runs
3961
3961
  ADD CONSTRAINT held_runs_environment_id_fkey
@@ -3965,8 +3965,8 @@ async function down$16(db) {
3965
3965
  //#endregion
3966
3966
  //#region src/db/migrations/031_dispatch_queue_ack_deadline.ts
3967
3967
  var _031_dispatch_queue_ack_deadline_exports = /* @__PURE__ */ __exportAll({
3968
- down: () => down$15,
3969
- up: () => up$15
3968
+ down: () => down$19,
3969
+ up: () => up$19
3970
3970
  });
3971
3971
  /**
3972
3972
  * Add `dispatch_queue.ack_deadline TIMESTAMPTZ` and
@@ -3983,7 +3983,7 @@ var _031_dispatch_queue_ack_deadline_exports = /* @__PURE__ */ __exportAll({
3983
3983
  *
3984
3984
  * Idempotent: re-running on a DB that already has either column is a no-op.
3985
3985
  */
3986
- async function up$15(db) {
3986
+ async function up$19(db) {
3987
3987
  const colExists = async (name) => {
3988
3988
  return (await sql`
3989
3989
  SELECT EXISTS (
@@ -4008,7 +4008,7 @@ async function up$15(db) {
4008
4008
  WHERE ack_deadline IS NOT NULL
4009
4009
  `.execute(db);
4010
4010
  }
4011
- async function down$15(db) {
4011
+ async function down$19(db) {
4012
4012
  await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_ack_deadline`.execute(db);
4013
4013
  await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS ack_agent_id`.execute(db);
4014
4014
  await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS ack_deadline`.execute(db);
@@ -4016,8 +4016,8 @@ async function down$15(db) {
4016
4016
  //#endregion
4017
4017
  //#region src/db/migrations/032_org_settings_dispatch_ack_timeout.ts
4018
4018
  var _032_org_settings_dispatch_ack_timeout_exports = /* @__PURE__ */ __exportAll({
4019
- down: () => down$14,
4020
- up: () => up$14
4019
+ down: () => down$18,
4020
+ up: () => up$18
4021
4021
  });
4022
4022
  /**
4023
4023
  * Add `org_settings.dispatch_ack_timeout_ms BIGINT` (nullable).
@@ -4029,7 +4029,7 @@ var _032_org_settings_dispatch_ack_timeout_exports = /* @__PURE__ */ __exportAll
4029
4029
  *
4030
4030
  * Idempotent: a re-run on a DB that already has the column is a no-op.
4031
4031
  */
4032
- async function up$14(db) {
4032
+ async function up$18(db) {
4033
4033
  if ((await sql`
4034
4034
  SELECT EXISTS (
4035
4035
  SELECT 1 FROM information_schema.columns
@@ -4043,7 +4043,7 @@ async function up$14(db) {
4043
4043
  ADD COLUMN dispatch_ack_timeout_ms BIGINT
4044
4044
  `.execute(db);
4045
4045
  }
4046
- async function down$14(db) {
4046
+ async function down$18(db) {
4047
4047
  await sql`
4048
4048
  ALTER TABLE public.org_settings DROP COLUMN IF EXISTS dispatch_ack_timeout_ms
4049
4049
  `.execute(db);
@@ -4051,8 +4051,8 @@ async function down$14(db) {
4051
4051
  //#endregion
4052
4052
  //#region src/db/migrations/033_org_settings_approval.ts
4053
4053
  var _033_org_settings_approval_exports = /* @__PURE__ */ __exportAll({
4054
- down: () => down$13,
4055
- up: () => up$13
4054
+ down: () => down$17,
4055
+ up: () => up$17
4056
4056
  });
4057
4057
  /**
4058
4058
  * Add the two approval-policy columns to `org_settings`:
@@ -4069,7 +4069,7 @@ var _033_org_settings_approval_exports = /* @__PURE__ */ __exportAll({
4069
4069
  * and the orchestrator admin route. Idempotent: a re-run on a DB that already
4070
4070
  * has the columns is a no-op (each column is guarded independently).
4071
4071
  */
4072
- async function up$13(db) {
4072
+ async function up$17(db) {
4073
4073
  const colExists = async (column) => {
4074
4074
  return (await sql`
4075
4075
  SELECT EXISTS (
@@ -4089,7 +4089,7 @@ async function up$13(db) {
4089
4089
  ADD COLUMN allow_self_approval BOOLEAN NOT NULL DEFAULT true
4090
4090
  `.execute(db);
4091
4091
  }
4092
- async function down$13(db) {
4092
+ async function down$17(db) {
4093
4093
  await sql`
4094
4094
  ALTER TABLE public.org_settings DROP COLUMN IF EXISTS approval_expiry_seconds
4095
4095
  `.execute(db);
@@ -4100,8 +4100,8 @@ async function down$13(db) {
4100
4100
  //#endregion
4101
4101
  //#region src/db/migrations/034_held_runs_generalize.ts
4102
4102
  var _034_held_runs_generalize_exports = /* @__PURE__ */ __exportAll({
4103
- down: () => down$12,
4104
- up: () => up$12
4103
+ down: () => down$16,
4104
+ up: () => up$16
4105
4105
  });
4106
4106
  /**
4107
4107
  * Generalize `held_runs` from an environment-only hold into the unified
@@ -4123,7 +4123,7 @@ var _034_held_runs_generalize_exports = /* @__PURE__ */ __exportAll({
4123
4123
  * New `held_run_approvals` table: one row per approver decision, FK to
4124
4124
  * `held_runs.id` (uuid) with ON DELETE CASCADE.
4125
4125
  */
4126
- async function up$12(db) {
4126
+ async function up$16(db) {
4127
4127
  const colExists = async (column) => {
4128
4128
  return (await sql`
4129
4129
  SELECT EXISTS (
@@ -4156,7 +4156,7 @@ async function up$12(db) {
4156
4156
  ON public.held_run_approvals USING btree (held_run_id)
4157
4157
  `.execute(db);
4158
4158
  }
4159
- async function down$12(db) {
4159
+ async function down$16(db) {
4160
4160
  await sql`DROP TABLE IF EXISTS public.held_run_approvals`.execute(db);
4161
4161
  await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS approval_requirement`.execute(db);
4162
4162
  await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS trigger_source`.execute(db);
@@ -4166,8 +4166,8 @@ async function down$12(db) {
4166
4166
  //#endregion
4167
4167
  //#region src/db/migrations/035_pending_workflow_contexts.ts
4168
4168
  var _035_pending_workflow_contexts_exports = /* @__PURE__ */ __exportAll({
4169
- down: () => down$11,
4170
- up: () => up$11
4169
+ down: () => down$15,
4170
+ up: () => up$15
4171
4171
  });
4172
4172
  /**
4173
4173
  * Pending workflow dispatch context — backs resume of a workflow whose install
@@ -4176,7 +4176,7 @@ var _035_pending_workflow_contexts_exports = /* @__PURE__ */ __exportAll({
4176
4176
  * wait-timer expiry, concurrency slot free). The row is deleted once the resume
4177
4177
  * dispatch has been kicked off.
4178
4178
  */
4179
- async function up$11(db) {
4179
+ async function up$15(db) {
4180
4180
  await sql`
4181
4181
  CREATE TABLE IF NOT EXISTS public.pending_workflow_contexts (
4182
4182
  run_id text PRIMARY KEY,
@@ -4186,14 +4186,14 @@ async function up$11(db) {
4186
4186
  )
4187
4187
  `.execute(db);
4188
4188
  }
4189
- async function down$11(db) {
4189
+ async function down$15(db) {
4190
4190
  await sql`DROP TABLE IF EXISTS public.pending_workflow_contexts`.execute(db);
4191
4191
  }
4192
4192
  //#endregion
4193
4193
  //#region src/db/migrations/036_attestations.ts
4194
4194
  var _036_attestations_exports = /* @__PURE__ */ __exportAll({
4195
- down: () => down$10,
4196
- up: () => up$10
4195
+ down: () => down$14,
4196
+ up: () => up$14
4197
4197
  });
4198
4198
  /**
4199
4199
  * Add the `attestations` table for build-provenance bundles.
@@ -4206,7 +4206,7 @@ var _036_attestations_exports = /* @__PURE__ */ __exportAll({
4206
4206
  *
4207
4207
  * Idempotent: a re-run on a DB that already has the table is a no-op.
4208
4208
  */
4209
- async function up$10(db) {
4209
+ async function up$14(db) {
4210
4210
  if ((await sql`
4211
4211
  SELECT EXISTS (
4212
4212
  SELECT 1 FROM information_schema.tables
@@ -4232,14 +4232,14 @@ async function up$10(db) {
4232
4232
  ON public.attestations (run_id, job_id)
4233
4233
  `.execute(db);
4234
4234
  }
4235
- async function down$10(db) {
4235
+ async function down$14(db) {
4236
4236
  await sql`DROP TABLE IF EXISTS public.attestations`.execute(db);
4237
4237
  }
4238
4238
  //#endregion
4239
4239
  //#region src/db/migrations/037_generic_sources_provider_type_local.ts
4240
4240
  var _037_generic_sources_provider_type_local_exports = /* @__PURE__ */ __exportAll({
4241
- down: () => down$9,
4242
- up: () => up$9
4241
+ down: () => down$13,
4242
+ up: () => up$13
4243
4243
  });
4244
4244
  /**
4245
4245
  * Replace the `generic_webhook_sources.provider_type` CHECK constraint so it
@@ -4256,7 +4256,7 @@ var _037_generic_sources_provider_type_local_exports = /* @__PURE__ */ __exportA
4256
4256
  * Idempotent: the constraint is dropped IF EXISTS and recreated; the data
4257
4257
  * backfill is a plain UPDATE that is a no-op once no `'internal'` rows remain.
4258
4258
  */
4259
- async function up$9(db) {
4259
+ async function up$13(db) {
4260
4260
  await sql`
4261
4261
  ALTER TABLE public.generic_webhook_sources
4262
4262
  DROP CONSTRAINT IF EXISTS generic_webhook_sources_provider_type_check
@@ -4272,7 +4272,7 @@ async function up$9(db) {
4272
4272
  CHECK (provider_type = ANY (ARRAY['generic'::text, 'local'::text]))
4273
4273
  `.execute(db);
4274
4274
  }
4275
- async function down$9(db) {
4275
+ async function down$13(db) {
4276
4276
  await sql`
4277
4277
  ALTER TABLE public.generic_webhook_sources
4278
4278
  DROP CONSTRAINT IF EXISTS generic_webhook_sources_provider_type_check
@@ -4291,8 +4291,8 @@ async function down$9(db) {
4291
4291
  //#endregion
4292
4292
  //#region src/db/migrations/038_remote_sources.ts
4293
4293
  var _038_remote_sources_exports = /* @__PURE__ */ __exportAll({
4294
- down: () => down$8,
4295
- up: () => up$8
4294
+ down: () => down$12,
4295
+ up: () => up$12
4296
4296
  });
4297
4297
  /**
4298
4298
  * `remote_sources` anchors a Platform-relayed `kici run remote` to its real
@@ -4304,7 +4304,7 @@ var _038_remote_sources_exports = /* @__PURE__ */ __exportAll({
4304
4304
  *
4305
4305
  * Idempotent: a re-run on a DB that already has the table is a no-op.
4306
4306
  */
4307
- async function up$8(db) {
4307
+ async function up$12(db) {
4308
4308
  if ((await sql`
4309
4309
  SELECT EXISTS (
4310
4310
  SELECT 1 FROM information_schema.tables
@@ -4323,14 +4323,14 @@ async function up$8(db) {
4323
4323
  )
4324
4324
  `.execute(db);
4325
4325
  }
4326
- async function down$8(db) {
4326
+ async function down$12(db) {
4327
4327
  await sql`DROP TABLE IF EXISTS public.remote_sources`.execute(db);
4328
4328
  }
4329
4329
  //#endregion
4330
4330
  //#region src/db/migrations/039_host_roster.ts
4331
4331
  var _039_host_roster_exports = /* @__PURE__ */ __exportAll({
4332
- down: () => down$7,
4333
- up: () => up$7
4332
+ down: () => down$11,
4333
+ up: () => up$11
4334
4334
  });
4335
4335
  /**
4336
4336
  * `host_roster` is KiCI's declared inventory: one durable row per agent the
@@ -4347,7 +4347,7 @@ var _039_host_roster_exports = /* @__PURE__ */ __exportAll({
4347
4347
  *
4348
4348
  * Idempotent: a re-run on a DB that already has the table is a no-op.
4349
4349
  */
4350
- async function up$7(db) {
4350
+ async function up$11(db) {
4351
4351
  if ((await sql`
4352
4352
  SELECT EXISTS (
4353
4353
  SELECT 1 FROM information_schema.tables
@@ -4377,14 +4377,14 @@ async function up$7(db) {
4377
4377
  await sql`CREATE INDEX idx_host_roster_reap
4378
4378
  ON public.host_roster (lifecycle_class, last_seen)`.execute(db);
4379
4379
  }
4380
- async function down$7(db) {
4380
+ async function down$11(db) {
4381
4381
  await sql`DROP TABLE IF EXISTS public.host_roster`.execute(db);
4382
4382
  }
4383
4383
  //#endregion
4384
4384
  //#region src/db/migrations/040_runsonall_pin.ts
4385
4385
  var _040_runsonall_pin_exports = /* @__PURE__ */ __exportAll({
4386
- down: () => down$6,
4387
- up: () => up$6
4386
+ down: () => down$10,
4387
+ up: () => up$10
4388
4388
  });
4389
4389
  /**
4390
4390
  * Add the `runsOnAll` host fan-out columns:
@@ -4401,7 +4401,7 @@ var _040_runsonall_pin_exports = /* @__PURE__ */ __exportAll({
4401
4401
  *
4402
4402
  * Idempotent: re-running on a DB that already has any column is a no-op.
4403
4403
  */
4404
- async function colExists$4(db, table, name) {
4404
+ async function colExists$5(db, table, name) {
4405
4405
  return (await sql`
4406
4406
  SELECT EXISTS (
4407
4407
  SELECT 1 FROM information_schema.columns
@@ -4411,18 +4411,18 @@ async function colExists$4(db, table, name) {
4411
4411
  ) AS exists
4412
4412
  `.execute(db)).rows[0]?.exists ?? false;
4413
4413
  }
4414
- async function up$6(db) {
4415
- if (!await colExists$4(db, "dispatch_queue", "pinned_agent_id")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN pinned_agent_id TEXT`.execute(db);
4416
- if (!await colExists$4(db, "execution_jobs", "base_job_name")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN base_job_name TEXT`.execute(db);
4417
- if (!await colExists$4(db, "execution_jobs", "variant_kind")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_kind TEXT`.execute(db);
4418
- if (!await colExists$4(db, "execution_jobs", "variant_label")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_label TEXT`.execute(db);
4414
+ async function up$10(db) {
4415
+ if (!await colExists$5(db, "dispatch_queue", "pinned_agent_id")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN pinned_agent_id TEXT`.execute(db);
4416
+ if (!await colExists$5(db, "execution_jobs", "base_job_name")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN base_job_name TEXT`.execute(db);
4417
+ if (!await colExists$5(db, "execution_jobs", "variant_kind")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_kind TEXT`.execute(db);
4418
+ if (!await colExists$5(db, "execution_jobs", "variant_label")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_label TEXT`.execute(db);
4419
4419
  await sql`
4420
4420
  CREATE INDEX IF NOT EXISTS idx_dispatch_queue_pinned_agent
4421
4421
  ON public.dispatch_queue (pinned_agent_id)
4422
4422
  WHERE pinned_agent_id IS NOT NULL
4423
4423
  `.execute(db);
4424
4424
  }
4425
- async function down$6(db) {
4425
+ async function down$10(db) {
4426
4426
  await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_pinned_agent`.execute(db);
4427
4427
  await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS pinned_agent_id`.execute(db);
4428
4428
  await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS variant_label`.execute(db);
@@ -4432,8 +4432,8 @@ async function down$6(db) {
4432
4432
  //#endregion
4433
4433
  //#region src/db/migrations/041_wave_gated.ts
4434
4434
  var _041_wave_gated_exports = /* @__PURE__ */ __exportAll({
4435
- down: () => down$5,
4436
- up: () => up$5
4435
+ down: () => down$9,
4436
+ up: () => up$9
4437
4437
  });
4438
4438
  /**
4439
4439
  * Add the rolling fan-out wave-gate columns:
@@ -4453,7 +4453,7 @@ var _041_wave_gated_exports = /* @__PURE__ */ __exportAll({
4453
4453
  *
4454
4454
  * Idempotent: re-running on a DB that already has the columns is a no-op.
4455
4455
  */
4456
- async function colExists$3(db, table, name) {
4456
+ async function colExists$4(db, table, name) {
4457
4457
  return (await sql`
4458
4458
  SELECT EXISTS (
4459
4459
  SELECT 1 FROM information_schema.columns
@@ -4463,16 +4463,16 @@ async function colExists$3(db, table, name) {
4463
4463
  ) AS exists
4464
4464
  `.execute(db)).rows[0]?.exists ?? false;
4465
4465
  }
4466
- async function up$5(db) {
4467
- if (!await colExists$3(db, "execution_jobs", "wave_gated")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_gated boolean NOT NULL DEFAULT false`.execute(db);
4468
- if (!await colExists$3(db, "execution_jobs", "wave_max_parallel")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_max_parallel integer`.execute(db);
4469
- if (!await colExists$3(db, "execution_jobs", "wave_fail_fast")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_fail_fast boolean`.execute(db);
4466
+ async function up$9(db) {
4467
+ if (!await colExists$4(db, "execution_jobs", "wave_gated")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_gated boolean NOT NULL DEFAULT false`.execute(db);
4468
+ if (!await colExists$4(db, "execution_jobs", "wave_max_parallel")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_max_parallel integer`.execute(db);
4469
+ if (!await colExists$4(db, "execution_jobs", "wave_fail_fast")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_fail_fast boolean`.execute(db);
4470
4470
  await sql`
4471
4471
  CREATE INDEX IF NOT EXISTS idx_execution_jobs_wave
4472
4472
  ON public.execution_jobs (run_id, base_job_name, wave_gated)
4473
4473
  `.execute(db);
4474
4474
  }
4475
- async function down$5(db) {
4475
+ async function down$9(db) {
4476
4476
  await sql`DROP INDEX IF EXISTS public.idx_execution_jobs_wave`.execute(db);
4477
4477
  await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS wave_fail_fast`.execute(db);
4478
4478
  await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS wave_max_parallel`.execute(db);
@@ -4481,8 +4481,8 @@ async function down$5(db) {
4481
4481
  //#endregion
4482
4482
  //#region src/db/migrations/042_dispatch_queue_patterns.ts
4483
4483
  var _042_dispatch_queue_patterns_exports = /* @__PURE__ */ __exportAll({
4484
- down: () => down$4,
4485
- up: () => up$4
4484
+ down: () => down$8,
4485
+ up: () => up$8
4486
4486
  });
4487
4487
  /**
4488
4488
  * Add pattern columns to dispatch_queue. Exact labels stay in runs_on_labels /
@@ -4498,7 +4498,7 @@ var _042_dispatch_queue_patterns_exports = /* @__PURE__ */ __exportAll({
4498
4498
  *
4499
4499
  * Idempotent: re-running on a DB that already has the columns is a no-op.
4500
4500
  */
4501
- async function colExists$2(db, table, name) {
4501
+ async function colExists$3(db, table, name) {
4502
4502
  return (await sql`
4503
4503
  SELECT EXISTS (
4504
4504
  SELECT 1 FROM information_schema.columns
@@ -4508,19 +4508,19 @@ async function colExists$2(db, table, name) {
4508
4508
  ) AS exists
4509
4509
  `.execute(db)).rows[0]?.exists ?? false;
4510
4510
  }
4511
- async function up$4(db) {
4512
- if (!await colExists$2(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);
4513
- if (!await colExists$2(db, "dispatch_queue", "exclude_patterns")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN exclude_patterns jsonb NOT NULL DEFAULT '[]'::jsonb`.execute(db);
4511
+ async function up$8(db) {
4512
+ if (!await colExists$3(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);
4513
+ if (!await colExists$3(db, "dispatch_queue", "exclude_patterns")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN exclude_patterns jsonb NOT NULL DEFAULT '[]'::jsonb`.execute(db);
4514
4514
  }
4515
- async function down$4(db) {
4515
+ async function down$8(db) {
4516
4516
  await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS exclude_patterns`.execute(db);
4517
4517
  await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS runs_on_patterns`.execute(db);
4518
4518
  }
4519
4519
  //#endregion
4520
4520
  //#region src/db/migrations/043_rerouted_to_peer.ts
4521
4521
  var _043_rerouted_to_peer_exports = /* @__PURE__ */ __exportAll({
4522
- down: () => down$3,
4523
- up: () => up$3
4522
+ down: () => down$7,
4523
+ up: () => up$7
4524
4524
  });
4525
4525
  /**
4526
4526
  * Add a nullable `rerouted_to_peer text` marker to execution_jobs. Non-null
@@ -4531,7 +4531,7 @@ var _043_rerouted_to_peer_exports = /* @__PURE__ */ __exportAll({
4531
4531
  *
4532
4532
  * Idempotent: re-running on a DB that already has the column is a no-op.
4533
4533
  */
4534
- async function colExists$1(db, table, name) {
4534
+ async function colExists$2(db, table, name) {
4535
4535
  return (await sql`
4536
4536
  SELECT EXISTS (
4537
4537
  SELECT 1 FROM information_schema.columns
@@ -4541,17 +4541,17 @@ async function colExists$1(db, table, name) {
4541
4541
  ) AS exists
4542
4542
  `.execute(db)).rows[0]?.exists ?? false;
4543
4543
  }
4544
- async function up$3(db) {
4545
- if (!await colExists$1(db, "execution_jobs", "rerouted_to_peer")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN rerouted_to_peer text`.execute(db);
4544
+ async function up$7(db) {
4545
+ if (!await colExists$2(db, "execution_jobs", "rerouted_to_peer")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN rerouted_to_peer text`.execute(db);
4546
4546
  }
4547
- async function down$3(db) {
4547
+ async function down$7(db) {
4548
4548
  await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS rerouted_to_peer`.execute(db);
4549
4549
  }
4550
4550
  //#endregion
4551
4551
  //#region src/db/migrations/044_check_mode.ts
4552
4552
  var _044_check_mode_exports = /* @__PURE__ */ __exportAll({
4553
- down: () => down$2,
4554
- up: () => up$2
4553
+ down: () => down$6,
4554
+ up: () => up$6
4555
4555
  });
4556
4556
  /**
4557
4557
  * Add idempotent check-mode columns:
@@ -4568,7 +4568,7 @@ var _044_check_mode_exports = /* @__PURE__ */ __exportAll({
4568
4568
  * All nullable. Idempotent: re-running on a DB that already has the columns is
4569
4569
  * a no-op.
4570
4570
  */
4571
- async function colExists(db, table, name) {
4571
+ async function colExists$1(db, table, name) {
4572
4572
  return (await sql`
4573
4573
  SELECT EXISTS (
4574
4574
  SELECT 1 FROM information_schema.columns
@@ -4578,13 +4578,13 @@ async function colExists(db, table, name) {
4578
4578
  ) AS exists
4579
4579
  `.execute(db)).rows[0]?.exists ?? false;
4580
4580
  }
4581
- async function up$2(db) {
4582
- if (!await colExists(db, "execution_runs", "check_mode")) await sql`ALTER TABLE public.execution_runs ADD COLUMN check_mode text`.execute(db);
4583
- if (!await colExists(db, "execution_steps", "check_outcome")) await sql`ALTER TABLE public.execution_steps ADD COLUMN check_outcome text`.execute(db);
4584
- if (!await colExists(db, "execution_steps", "drift_summary")) await sql`ALTER TABLE public.execution_steps ADD COLUMN drift_summary text`.execute(db);
4585
- if (!await colExists(db, "execution_steps", "drift")) await sql`ALTER TABLE public.execution_steps ADD COLUMN drift jsonb`.execute(db);
4581
+ async function up$6(db) {
4582
+ if (!await colExists$1(db, "execution_runs", "check_mode")) await sql`ALTER TABLE public.execution_runs ADD COLUMN check_mode text`.execute(db);
4583
+ if (!await colExists$1(db, "execution_steps", "check_outcome")) await sql`ALTER TABLE public.execution_steps ADD COLUMN check_outcome text`.execute(db);
4584
+ if (!await colExists$1(db, "execution_steps", "drift_summary")) await sql`ALTER TABLE public.execution_steps ADD COLUMN drift_summary text`.execute(db);
4585
+ if (!await colExists$1(db, "execution_steps", "drift")) await sql`ALTER TABLE public.execution_steps ADD COLUMN drift jsonb`.execute(db);
4586
4586
  }
4587
- async function down$2(db) {
4587
+ async function down$6(db) {
4588
4588
  await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS drift`.execute(db);
4589
4589
  await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS drift_summary`.execute(db);
4590
4590
  await sql`ALTER TABLE public.execution_steps DROP COLUMN IF EXISTS check_outcome`.execute(db);
@@ -4593,8 +4593,8 @@ async function down$2(db) {
4593
4593
  //#endregion
4594
4594
  //#region src/db/migrations/045_host_properties.ts
4595
4595
  var _045_host_properties_exports = /* @__PURE__ */ __exportAll({
4596
- down: () => down$1,
4597
- up: () => up$1
4596
+ down: () => down$5,
4597
+ up: () => up$5
4598
4598
  });
4599
4599
  /**
4600
4600
  * Add the typed host-vars dimension to the host roster:
@@ -4608,18 +4608,18 @@ var _045_host_properties_exports = /* @__PURE__ */ __exportAll({
4608
4608
  * Idempotent (`ADD COLUMN IF NOT EXISTS`): re-running on a DB that already has
4609
4609
  * the column is a no-op. Staging data is preserved (additive column).
4610
4610
  */
4611
- async function up$1(db) {
4611
+ async function up$5(db) {
4612
4612
  await sql`ALTER TABLE public.host_roster
4613
4613
  ADD COLUMN IF NOT EXISTS host_properties jsonb NOT NULL DEFAULT '{}'::jsonb`.execute(db);
4614
4614
  }
4615
- async function down$1(db) {
4615
+ async function down$5(db) {
4616
4616
  await sql`ALTER TABLE public.host_roster DROP COLUMN IF EXISTS host_properties`.execute(db);
4617
4617
  }
4618
4618
  //#endregion
4619
4619
  //#region src/db/migrations/046_join_token_consumed_by_instance.ts
4620
4620
  var _046_join_token_consumed_by_instance_exports = /* @__PURE__ */ __exportAll({
4621
- down: () => down,
4622
- up: () => up
4621
+ down: () => down$4,
4622
+ up: () => up$4
4623
4623
  });
4624
4624
  /**
4625
4625
  * Record the joining peer's instanceId at first consumption of a join token:
@@ -4634,14 +4634,163 @@ var _046_join_token_consumed_by_instance_exports = /* @__PURE__ */ __exportAll({
4634
4634
  * Idempotent (`ADD COLUMN IF NOT EXISTS`): re-running on a DB that already has
4635
4635
  * the column is a no-op. Staging data is preserved (additive nullable column).
4636
4636
  */
4637
- async function up(db) {
4637
+ async function up$4(db) {
4638
4638
  await sql`ALTER TABLE public.join_tokens
4639
4639
  ADD COLUMN IF NOT EXISTS consumed_by_instance text`.execute(db);
4640
4640
  }
4641
- async function down(db) {
4641
+ async function down$4(db) {
4642
4642
  await sql`ALTER TABLE public.join_tokens DROP COLUMN IF EXISTS consumed_by_instance`.execute(db);
4643
4643
  }
4644
4644
  //#endregion
4645
+ //#region src/db/migrations/047_needs_run_on.ts
4646
+ var _047_needs_run_on_exports = /* @__PURE__ */ __exportAll({
4647
+ down: () => down$3,
4648
+ up: () => up$3
4649
+ });
4650
+ /**
4651
+ * Migrate `execution_job_needs` from the binary `if_failed` policy to a
4652
+ * programmable `run_on` status-set:
4653
+ *
4654
+ * - Add `run_on text` (JSON-encoded array of upstream terminal statuses) with a
4655
+ * default of `'["success"]'`.
4656
+ * - Backfill from the existing `if_failed`: `skip` → `["success"]`,
4657
+ * `run` → every terminal job status.
4658
+ * - Drop `if_failed`.
4659
+ *
4660
+ * Idempotent: column-exists guarded so re-running on a DB that already has
4661
+ * `run_on` (and no `if_failed`) is a no-op. Existing rows are preserved
4662
+ * (staging data is not dropped).
4663
+ */
4664
+ const ALL_TERMINAL_JSON = JSON.stringify([
4665
+ "success",
4666
+ "failed",
4667
+ "cancelled",
4668
+ "skipped",
4669
+ "timed_out_stale",
4670
+ "drift_dropped"
4671
+ ]);
4672
+ const SUCCESS_ONLY_JSON = JSON.stringify(["success"]);
4673
+ async function colExists(db, table, name) {
4674
+ return (await sql`
4675
+ SELECT EXISTS (
4676
+ SELECT 1 FROM information_schema.columns
4677
+ WHERE table_schema = 'public'
4678
+ AND table_name = ${table}
4679
+ AND column_name = ${name}
4680
+ ) AS exists
4681
+ `.execute(db)).rows[0]?.exists ?? false;
4682
+ }
4683
+ const SUCCESS_ONLY_DEFAULT = `'${SUCCESS_ONLY_JSON}'`;
4684
+ async function up$3(db) {
4685
+ if (!await colExists(db, "execution_job_needs", "run_on")) await sql`
4686
+ ALTER TABLE public.execution_job_needs
4687
+ ADD COLUMN run_on text NOT NULL DEFAULT ${sql.raw(SUCCESS_ONLY_DEFAULT)}
4688
+ `.execute(db);
4689
+ if (await colExists(db, "execution_job_needs", "if_failed")) {
4690
+ await sql`
4691
+ UPDATE public.execution_job_needs
4692
+ SET run_on = ${ALL_TERMINAL_JSON}
4693
+ WHERE if_failed = 'run'
4694
+ `.execute(db);
4695
+ await sql`
4696
+ UPDATE public.execution_job_needs
4697
+ SET run_on = ${SUCCESS_ONLY_JSON}
4698
+ WHERE if_failed = 'skip'
4699
+ `.execute(db);
4700
+ await sql`ALTER TABLE public.execution_job_needs DROP COLUMN if_failed`.execute(db);
4701
+ }
4702
+ }
4703
+ async function down$3(db) {
4704
+ if (!await colExists(db, "execution_job_needs", "if_failed")) await sql`
4705
+ ALTER TABLE public.execution_job_needs
4706
+ ADD COLUMN if_failed text NOT NULL DEFAULT 'skip'
4707
+ `.execute(db);
4708
+ if (await colExists(db, "execution_job_needs", "run_on")) {
4709
+ await sql`
4710
+ UPDATE public.execution_job_needs
4711
+ SET if_failed = CASE WHEN run_on = ${SUCCESS_ONLY_JSON} THEN 'skip' ELSE 'run' END
4712
+ `.execute(db);
4713
+ await sql`ALTER TABLE public.execution_job_needs DROP COLUMN run_on`.execute(db);
4714
+ }
4715
+ }
4716
+ //#endregion
4717
+ //#region src/db/migrations/048_host_reboot_pending.ts
4718
+ var _048_host_reboot_pending_exports = /* @__PURE__ */ __exportAll({
4719
+ down: () => down$2,
4720
+ up: () => up$2
4721
+ });
4722
+ /**
4723
+ * Add `host_roster.reboot_pending_until timestamptz NULL` — the persisted
4724
+ * reboot-pending flag for workflow-level host restart.
4725
+ *
4726
+ * Set when an agent's `restartHost()` step calls `host.requestReboot` (so it
4727
+ * survives an orchestrator restart during the host's reboot window). While the
4728
+ * value is in the future it (1) makes the agent's imminent disconnect an
4729
+ * expected reboot rather than a recovery-fail, (2) gates the pinned-drain off
4730
+ * so the post-restart job is not dispatched into the about-to-reboot box, and
4731
+ * (3) clears on the next reconnect (down-then-up), releasing the held job. NULL
4732
+ * = no reboot pending.
4733
+ *
4734
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
4735
+ * preserved.
4736
+ */
4737
+ async function up$2(db) {
4738
+ await sql`ALTER TABLE public.host_roster
4739
+ ADD COLUMN IF NOT EXISTS reboot_pending_until timestamptz`.execute(db);
4740
+ }
4741
+ async function down$2(db) {
4742
+ await sql`ALTER TABLE public.host_roster DROP COLUMN IF EXISTS reboot_pending_until`.execute(db);
4743
+ }
4744
+ //#endregion
4745
+ //#region src/db/migrations/049_held_runs_payload.ts
4746
+ var _049_held_runs_payload_exports = /* @__PURE__ */ __exportAll({
4747
+ down: () => down$1,
4748
+ up: () => up$1
4749
+ });
4750
+ /**
4751
+ * Add `held_runs.payload jsonb NULL` — the drift payload captured when a
4752
+ * `when: 'drift'` step-approval gate fires. Holds `{ summaryMarkdown, drift }`:
4753
+ * the author's `summarize(drift)` rendering plus the structured drift blob, so
4754
+ * the dashboard approval queue and the CLI render the computed diff the
4755
+ * operator approves. NULL for every non-drift hold.
4756
+ *
4757
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
4758
+ * preserved.
4759
+ */
4760
+ async function up$1(db) {
4761
+ await sql`ALTER TABLE public.held_runs
4762
+ ADD COLUMN IF NOT EXISTS payload jsonb`.execute(db);
4763
+ }
4764
+ async function down$1(db) {
4765
+ await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS payload`.execute(db);
4766
+ }
4767
+ //#endregion
4768
+ //#region src/db/migrations/050_sources_slug.ts
4769
+ var _050_sources_slug_exports = /* @__PURE__ */ __exportAll({
4770
+ down: () => down,
4771
+ up: () => up
4772
+ });
4773
+ /**
4774
+ * Add `sources.slug TEXT NULL` — the GitHub App slug (the URL-safe identifier
4775
+ * GitHub assigns, e.g. `my-kici-app`).
4776
+ *
4777
+ * For GitHub-App sources GitHub is the source of truth for both the display
4778
+ * `name` and the `slug`: both are captured at creation and kept fresh by the
4779
+ * daily refresher + `kici-admin source refresh`. NULL when the identity fetch
4780
+ * hasn't populated it yet (manual `--app-id` flow whose initial fetch failed,
4781
+ * or a row created before the rollout).
4782
+ *
4783
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
4784
+ * preserved.
4785
+ */
4786
+ async function up(db) {
4787
+ await sql`ALTER TABLE public.sources
4788
+ ADD COLUMN IF NOT EXISTS slug text`.execute(db);
4789
+ }
4790
+ async function down(db) {
4791
+ await sql`ALTER TABLE public.sources DROP COLUMN IF EXISTS slug`.execute(db);
4792
+ }
4793
+ //#endregion
4645
4794
  //#region src/db/migration-provider.ts
4646
4795
  function createMigrationProvider() {
4647
4796
  return { async getMigrations() {
@@ -4691,7 +4840,11 @@ function createMigrationProvider() {
4691
4840
  "043_rerouted_to_peer": _043_rerouted_to_peer_exports,
4692
4841
  "044_check_mode": _044_check_mode_exports,
4693
4842
  "045_host_properties": _045_host_properties_exports,
4694
- "046_join_token_consumed_by_instance": _046_join_token_consumed_by_instance_exports
4843
+ "046_join_token_consumed_by_instance": _046_join_token_consumed_by_instance_exports,
4844
+ "047_needs_run_on": _047_needs_run_on_exports,
4845
+ "048_host_reboot_pending": _048_host_reboot_pending_exports,
4846
+ "049_held_runs_payload": _049_held_runs_payload_exports,
4847
+ "050_sources_slug": _050_sources_slug_exports
4695
4848
  };
4696
4849
  } };
4697
4850
  }
@@ -5361,6 +5514,23 @@ function createInstallationOctokit(config, installationId) {
5361
5514
  * registration and returns it on the conversion response, so both GitHub and
5362
5515
  * the Platform end up sharing the same secret with zero operator effort.
5363
5516
  */
5517
+ /**
5518
+ * Validate a self-hosted webhook URL supplied via `source add github
5519
+ * --webhook-url`. Must be a well-formed absolute `https://` URL. Returns the URL
5520
+ * verbatim on success; throws a clear error otherwise. The validated URL is
5521
+ * baked into `manifest.hook_attributes.url` as-is — KiCI adds no ingress and
5522
+ * does not receive events at it; the operator owns delivery.
5523
+ */
5524
+ function validateWebhookUrl(value) {
5525
+ let url;
5526
+ try {
5527
+ url = new URL(value);
5528
+ } catch {
5529
+ throw new Error(`--webhook-url must be a valid absolute URL: ${value}`);
5530
+ }
5531
+ if (url.protocol !== "https:") throw new Error(`--webhook-url must be an https:// URL (got ${url.protocol}//…)`);
5532
+ return value;
5533
+ }
5364
5534
  function buildGithubAppManifest(input) {
5365
5535
  return {
5366
5536
  name: input.name,
@@ -5399,6 +5569,7 @@ async function convertManifestCode(code, deps = {}) {
5399
5569
  return {
5400
5570
  appId: String(d.id),
5401
5571
  slug: d.slug,
5572
+ name: d.name,
5402
5573
  privateKey: d.pem,
5403
5574
  webhookSecret: d.webhook_secret,
5404
5575
  clientId: d.client_id,
@@ -5647,7 +5818,8 @@ async function storeAndRegister(opts, client, deps, creds) {
5647
5818
  try {
5648
5819
  return await client.post("/api/v1/admin/sources", {
5649
5820
  provider: "github",
5650
- name: opts.name,
5821
+ name: creds.name,
5822
+ slug: creds.slug,
5651
5823
  appId: creds.appId,
5652
5824
  privateKey: creds.privateKey,
5653
5825
  webhookSecret: creds.webhookSecret
@@ -5664,7 +5836,7 @@ async function storeAndRegister(opts, client, deps, creds) {
5664
5836
  }
5665
5837
  }
5666
5838
  async function runGithubManifestSetup(opts, client, deps = realManifestSetupDeps) {
5667
- const webhookUrl = await resolveWebhookUrl(client);
5839
+ const webhookUrl = opts.webhookUrl ? validateWebhookUrl(opts.webhookUrl) : await resolveWebhookUrl(client);
5668
5840
  const state = randomBytes(16).toString("hex");
5669
5841
  const createUrl = manifestCreateUrl(opts.githubOrg);
5670
5842
  const loopback = await deps.startLoopback({
@@ -5793,6 +5965,16 @@ async function readFromStdin() {
5793
5965
  for await (const line of rl) chunks.push(line);
5794
5966
  return chunks.join("\n");
5795
5967
  }
5968
+ /** Print a `source refresh` result, showing old → new for name + slug. */
5969
+ function printRefreshResult(r) {
5970
+ if (!r.changed) {
5971
+ console.log(`${r.routingKey}: up to date (name "${r.newName}", slug "${r.newSlug}")`);
5972
+ return;
5973
+ }
5974
+ console.log(`${r.routingKey}: updated`);
5975
+ if (r.oldName !== r.newName) console.log(` name: ${r.oldName} → ${r.newName}`);
5976
+ if (r.oldSlug !== r.newSlug) console.log(` slug: ${r.oldSlug ?? "(none)"} → ${r.newSlug}`);
5977
+ }
5796
5978
  /**
5797
5979
  * Format a generic source for display.
5798
5980
  */
@@ -5900,13 +6082,14 @@ async function buildUniversalGitConfigFromOpts(opts) {
5900
6082
  function registerSourceCommands(program, getClient) {
5901
6083
  const src = program.command("source").description("Manage webhook sources");
5902
6084
  const add = src.command("add").description("Add a new webhook source");
5903
- add.command("github").description("Add a new GitHub App source").requiredOption("--name <name>", "Human-readable source name").option("--app-id <id>", "GitHub App ID (omit when using --manifest)").option("--private-key <pathOrValue>", "Private key (prefix with @ for file path)").option("--webhook-secret <secret>", "Webhook secret").option("--from-env <varName>", "Read private key from environment variable").option("--stdin", "Read private key from stdin").option("--manifest", "One-click setup: create and configure a new GitHub App via the App Manifest flow").option("--no-browser", "Headless manifest setup: print a URL and paste the setup code back").option("--github-org <slug>", "Create the App under a GitHub org instead of your personal account").option("--json", "Emit raw JSON (the API response) instead of formatted text").action(async (opts) => {
6085
+ add.command("github").description("Add a new GitHub App source").requiredOption("--name <name>", "Human-readable source name").option("--app-id <id>", "GitHub App ID (omit when using --manifest)").option("--private-key <pathOrValue>", "Private key (prefix with @ for file path)").option("--webhook-secret <secret>", "Webhook secret").option("--from-env <varName>", "Read private key from environment variable").option("--stdin", "Read private key from stdin").option("--manifest", "One-click setup: create and configure a new GitHub App via the App Manifest flow").option("--no-browser", "Headless manifest setup: print a URL and paste the setup code back").option("--github-org <slug>", "Create the App under a GitHub org instead of your personal account").option("--webhook-url <url>", "Advanced/self-hosted: bake this https:// URL into the App webhook verbatim and skip platform-mode URL resolution. KiCI adds no ingress at this URL — your own infra owns delivery.").option("--json", "Emit raw JSON (the API response) instead of formatted text").action(async (opts) => {
5904
6086
  if (opts.manifest) {
5905
6087
  try {
5906
6088
  await runGithubManifestSetup({
5907
6089
  name: opts.name,
5908
6090
  noBrowser: !opts.browser,
5909
- githubOrg: opts.githubOrg
6091
+ githubOrg: opts.githubOrg,
6092
+ webhookUrl: opts.webhookUrl
5910
6093
  }, getClient());
5911
6094
  } catch (err) {
5912
6095
  console.error(`Error: ${toErrorMessage(err)}`);
@@ -6147,6 +6330,34 @@ function registerSourceCommands(program, getClient) {
6147
6330
  process.exit(1);
6148
6331
  }
6149
6332
  });
6333
+ src.command("refresh [routingKey]").description("Re-sync a GitHub source's name and slug from GitHub (use --all for every source)").option("--all", "Refresh every GitHub source").option("--json", "Emit raw JSON instead of formatted text").action(async (routingKey, opts) => {
6334
+ try {
6335
+ if (opts.all) {
6336
+ const res = await getClient().post("/api/v1/admin/sources/refresh-all", {});
6337
+ if (opts.json) {
6338
+ console.log(JSON.stringify(res, null, 2));
6339
+ return;
6340
+ }
6341
+ for (const r of res.results) printRefreshResult(r);
6342
+ for (const e of res.errors) console.error(` ${e.routingKey}: ${e.error}`);
6343
+ if (res.results.length === 0 && res.errors.length === 0) console.log("No GitHub sources to refresh.");
6344
+ return;
6345
+ }
6346
+ if (!routingKey) {
6347
+ console.error("Error: provide a <routingKey> or use --all");
6348
+ process.exit(1);
6349
+ }
6350
+ const result = await getClient().post(`/api/v1/admin/sources/${encodeURIComponent(routingKey)}/refresh`, {});
6351
+ if (opts.json) {
6352
+ console.log(JSON.stringify(result, null, 2));
6353
+ return;
6354
+ }
6355
+ printRefreshResult(result);
6356
+ } catch (err) {
6357
+ console.error(`Error: ${toErrorMessage(err)}`);
6358
+ process.exit(1);
6359
+ }
6360
+ });
6150
6361
  src.command("update-generic <id>").description("Update a generic webhook source").option("--name <name>", "New name").option("--verification <method>", "Verification method: hmac_sha256, bearer_token, ip_allowlist, none").option("--secret <value>", "New verification secret (prefix with @ for file)").option("--from-env <varName>", "Read verification secret from environment variable").option("--stdin", "Read verification secret from stdin").option("--event-type-header <header>", "Header name for event type extraction").option("--event-type-path <jsonpath>", "JSONPath for event type extraction from body").option("--idempotency-key-header <header>", "Header name for idempotency key").option("--idempotency-key-path <jsonpath>", "JSONPath for idempotency key from body").option("--dedup-window <seconds>", "Dedup window in seconds", parseInt).option("--max-payload <bytes>", "Maximum payload size in bytes", parseInt).option("--allowed-events <events>", "Comma-separated list of allowed event types").option("--strip-headers <headers>", "Comma-separated list of headers to strip").option("--rate-limit <rpm>", "Rate limit in requests per minute", parseInt).option("--preset <name>", "Universal-git preset").option("--git-url-template <url>", "Clone URL template").option("--credential-ref <key>", "Secret key name").option("--credential-store <backend>", "Secret backend name").option("--credential-type <type>", "Credential type: pat, basic, ssh").option("--credential-user <user>", "Username for PAT/basic auth").option("--ssh-host-key-policy <policy>", "SSH host-key policy: accept-new, pinned").option("--ssh-known-hosts-pem <pathOrValue>", "Pinned SSH known_hosts (prefix with @ for file)").option("--clear-git-config", "Revert this source back to a payload-only generic webhook").option("--provider-type <type>", "Provider implementation: generic (default) or local (a git repo on the agent filesystem cloned via file://)").option("--json", "Emit raw JSON (the full source row) instead of formatted text").action(async (id, opts) => {
6151
6362
  try {
6152
6363
  const secret = await resolveSecret({
@@ -9139,11 +9350,17 @@ var init_image_digests = __esmMin((() => {
9139
9350
  * a container-based service management backend that works on any
9140
9351
  * platform with Docker or Podman installed.
9141
9352
  */
9142
- var compose_exports = /* @__PURE__ */ __exportAll({ ComposeServiceManager: () => ComposeServiceManager });
9353
+ var compose_exports = /* @__PURE__ */ __exportAll({
9354
+ ComposeServiceManager: () => ComposeServiceManager,
9355
+ detectRuntime: () => detectRuntime$1
9356
+ });
9143
9357
  /**
9144
9358
  * Detect which container runtime is available.
9145
9359
  * Tries podman compose first, then docker compose.
9146
9360
  *
9361
+ * Exported so the orchestrator installer can resolve the runtime once when
9362
+ * injecting the `KICI_DEPLOY_CONTAINER_RUNTIME` env var for a compose deploy.
9363
+ *
9147
9364
  * @throws When neither runtime is found.
9148
9365
  */
9149
9366
  function detectRuntime$1() {
@@ -9393,7 +9610,36 @@ function resolveServiceExecutable(opts) {
9393
9610
  };
9394
9611
  }
9395
9612
  //#endregion
9613
+ //#region src/cli/service/deploy-env.ts
9614
+ /** Prefix every line uses; also the strip marker for idempotent re-install. */
9615
+ const DEPLOY_ENV_PREFIX = "KICI_DEPLOY_";
9616
+ /**
9617
+ * Build the `KICI_DEPLOY_*` env lines for a given deployment shape. The mode
9618
+ * line is always emitted; container name + runtime are emitted only for the
9619
+ * `compose` platform (the only shape with a container to `exec` into).
9620
+ */
9621
+ function buildDeployEnvLines(input) {
9622
+ const lines = [`KICI_DEPLOY_MODE=${input.platform}`];
9623
+ if (input.platform === "compose") {
9624
+ lines.push(`KICI_DEPLOY_CONTAINER=${input.serviceName}`);
9625
+ if (input.containerRuntime) lines.push(`KICI_DEPLOY_CONTAINER_RUNTIME=${input.containerRuntime}`);
9626
+ }
9627
+ return lines;
9628
+ }
9629
+ /**
9630
+ * Append the freshly-computed deploy lines to existing env-file content,
9631
+ * idempotently: any pre-existing `KICI_DEPLOY_*` line is stripped first so a
9632
+ * re-install / upgrade heals the shape rather than duplicating it.
9633
+ */
9634
+ function upsertDeployEnvLines(existingContent, lines) {
9635
+ const kept = existingContent.split("\n").filter((line) => !line.trimStart().startsWith(DEPLOY_ENV_PREFIX));
9636
+ while (kept.length > 0 && kept[kept.length - 1].trim() === "") kept.pop();
9637
+ const body = kept.join("\n");
9638
+ return `${body.length > 0 ? `${body}\n` : ""}${lines.join("\n")}\n`;
9639
+ }
9640
+ //#endregion
9396
9641
  //#region src/cli/commands/shared/versioned-upgrade.ts
9642
+ init_compose();
9397
9643
  /**
9398
9644
  * Shared versioned directory upgrade logic for kici-admin upgrade commands.
9399
9645
  *
@@ -10205,6 +10451,18 @@ function registerOrchestratorInstall(orchestrator) {
10205
10451
  fs.appendFileSync(envFilePath, `\nKICI_DATABASE_URL=${devDbUrl}\n`);
10206
10452
  console.log(`Appended KICI_DATABASE_URL to ${envFilePath}`);
10207
10453
  }
10454
+ let composeRuntime;
10455
+ if (platform === "compose") try {
10456
+ composeRuntime = detectRuntime$1();
10457
+ } catch {
10458
+ composeRuntime = void 0;
10459
+ }
10460
+ const deployLines = buildDeployEnvLines({
10461
+ platform,
10462
+ serviceName,
10463
+ containerRuntime: composeRuntime
10464
+ });
10465
+ fs.writeFileSync(envFilePath, upsertDeployEnvLines(fs.readFileSync(envFilePath, "utf-8"), deployLines), "utf-8");
10208
10466
  const entryScript = opts.binary ? void 0 : fileURLToPath(import.meta.resolve(`@kici-dev/orchestrator/${selectServerEntry(fs.readFileSync(envFilePath, "utf-8"))}`));
10209
10467
  const { executablePath, args } = resolveServiceExecutable({
10210
10468
  binary: opts.binary ? path.resolve(opts.binary) : void 0,
@@ -11754,11 +12012,47 @@ var HostRosterStore = class {
11754
12012
  const now = Date.now();
11755
12013
  return rows.filter((r) => deriveHostStatus(r, now, graceMs) === "unreachable").length;
11756
12014
  }
12015
+ /** Remove a host from the roster by agent id. Returns rows deleted. */
12016
+ async removeStatic(agentId) {
12017
+ const res = await this.db.deleteFrom("host_roster").where("agent_id", "=", agentId).executeTakeFirst();
12018
+ return Number(res.numDeletedRows ?? 0n);
12019
+ }
11757
12020
  /** Delete ephemeral rows whose last_seen is older than ttl. Returns count. */
11758
12021
  async reapEphemeralPastTtl(ttlMs) {
11759
12022
  const res = await this.db.deleteFrom("host_roster").where("lifecycle_class", "=", "ephemeral").where("last_seen", "<", sql`now() - (${ttlMs}::text || ' milliseconds')::interval`).executeTakeFirst();
11760
12023
  return Number(res.numDeletedRows ?? 0n);
11761
12024
  }
12025
+ /**
12026
+ * Mark a host as about-to-reboot until `until`. Set by the `host.requestReboot`
12027
+ * API handler when an agent's `restartHost()` step runs. While the value is in
12028
+ * the future, the host's disconnect is the expected reboot and its pinned
12029
+ * post-restart job is held.
12030
+ */
12031
+ async setRebootPending(agentId, until) {
12032
+ await this.db.updateTable("host_roster").set({
12033
+ reboot_pending_until: until,
12034
+ updated_at: sql`now()`
12035
+ }).where("agent_id", "=", agentId).execute();
12036
+ }
12037
+ /** Clear the reboot-pending flag (on reconnect, deadline expiry, or cancel). */
12038
+ async clearRebootPending(agentId) {
12039
+ await this.db.updateTable("host_roster").set({
12040
+ reboot_pending_until: null,
12041
+ updated_at: sql`now()`
12042
+ }).where("agent_id", "=", agentId).execute();
12043
+ }
12044
+ /** True when the host has a reboot-pending deadline still in the future at `nowMs`. */
12045
+ async isRebootPending(agentId, nowMs) {
12046
+ const row = await this.db.selectFrom("host_roster").select("reboot_pending_until").where("agent_id", "=", agentId).executeTakeFirst();
12047
+ return (row?.reboot_pending_until ? new Date(row.reboot_pending_until).getTime() : 0) > nowMs;
12048
+ }
12049
+ /**
12050
+ * Agent ids whose reboot-pending deadline has passed at `nowMs`. The deadline
12051
+ * sweep clears these; the held post-restart job then hits the queue timeout.
12052
+ */
12053
+ async listExpiredRebootPending(nowMs) {
12054
+ return (await this.db.selectFrom("host_roster").select("agent_id").where("reboot_pending_until", "is not", null).where("reboot_pending_until", "<=", new Date(nowMs)).execute()).map((r) => r.agent_id);
12055
+ }
11762
12056
  };
11763
12057
  //#endregion
11764
12058
  //#region src/cli/commands/host.ts
@@ -11835,6 +12129,18 @@ function registerHostCommands(program) {
11835
12129
  process.exit(1);
11836
12130
  }
11837
12131
  });
12132
+ host.command("remove").description("Remove a host from the roster").requiredOption("--agent-id <id>", "Agent id to remove").action(async (opts) => {
12133
+ try {
12134
+ if (await withDb((db) => new HostRosterStore(db).removeStatic(opts.agentId)) === 0) {
12135
+ console.error(`No host found: ${opts.agentId}`);
12136
+ process.exit(1);
12137
+ }
12138
+ console.log(`Removed host: ${opts.agentId}`);
12139
+ } catch (err) {
12140
+ console.error(`Error: ${toErrorMessage(err)}`);
12141
+ process.exit(1);
12142
+ }
12143
+ });
11838
12144
  }
11839
12145
  //#endregion
11840
12146
  //#region src/cli/commands/org-settings.ts