@kici-dev/orchestrator 0.1.17 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/dispatcher.d.ts +9 -0
- package/dist/agent/host-roster-reaper.d.ts +42 -0
- package/dist/agent/host-roster.d.ts +110 -0
- package/dist/agent/registry.d.ts +65 -2
- package/dist/app.d.ts +3 -0
- package/dist/cli/commands/host.d.ts +13 -0
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +23 -0
- package/dist/cli/service/compose.d.ts +2 -1
- package/dist/cli/service/index.d.ts +2 -2
- package/dist/cli/service/instance/manifest.d.ts +12 -0
- package/dist/cli/service/instance/resolve.d.ts +12 -2
- package/dist/cli/service/launchd.d.ts +4 -1
- package/dist/cli/service/platform-detect.d.ts +5 -0
- package/dist/cli/service/systemd.d.ts +2 -1
- package/dist/cli/service/types.d.ts +37 -0
- package/dist/cli/service/windows.d.ts +2 -1
- package/dist/cli.js +797 -202
- package/dist/cluster/coordinator.d.ts +5 -1
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/types.d.ts +9 -0
- package/dist/config.d.ts +6 -0
- package/dist/db/migrations/039_host_roster.d.ts +19 -0
- package/dist/db/migrations/040_runsonall_pin.d.ts +4 -0
- package/dist/db/migrations/041_wave_gated.d.ts +4 -0
- package/dist/db/migrations/042_dispatch_queue_patterns.d.ts +4 -0
- package/dist/db/types.d.ts +61 -0
- package/dist/diagnostics/fleet-collector.d.ts +1 -1
- package/dist/environments/held-runs.d.ts +9 -0
- package/dist/lockfile-redos-guard.d.ts +19 -0
- package/dist/metrics/prometheus.d.ts +13 -0
- package/dist/metrics/scheduled-jobs.d.ts +2 -2
- package/dist/orchestrator-core.d.ts +46 -1
- package/dist/pipeline/dispatch-matched-workflow.d.ts +42 -1
- package/dist/pipeline/processor.d.ts +25 -0
- package/dist/pipeline/wave-scheduler.d.ts +60 -0
- package/dist/queue/job-queue.d.ts +61 -2
- package/dist/reporting/execution-tracker.d.ts +28 -0
- package/dist/server.js +52760 -51263
- package/dist/standalone.js +1953 -500
- package/installer-image-digests.json +3 -3
- package/package.json +22 -22
- package/sbom.spdx.json +3227 -6682
package/dist/cli.js
CHANGED
|
@@ -19,14 +19,14 @@ import { Migrator } from "kysely/migration";
|
|
|
19
19
|
import "jsonpath-plus";
|
|
20
20
|
import { execFile, execFileSync, execSync, spawn } from "node:child_process";
|
|
21
21
|
import { promisify } from "node:util";
|
|
22
|
-
import
|
|
22
|
+
import { ZipArchive } from "archiver";
|
|
23
23
|
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
24
24
|
import JSZip from "jszip";
|
|
25
25
|
import { pipeline } from "node:stream/promises";
|
|
26
26
|
import "pg";
|
|
27
|
+
import { KICI_AGENT_ENV_PREFIX, KNOWN_ROLES, ScalerBackendType, ScalerEventType, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, WS_MAX_PAYLOAD_BYTES, accessLogWarmSqlCase, getAccessLogColdDays, getSecretAuditLogColdDays, matcherSatisfiedBy, minAccessLogWarmDays, minSecretAuditLogWarmDays, scalerAgentLabels, secretAuditLogWarmSqlCase } from "@kici-dev/engine";
|
|
27
28
|
import { DASHBOARD_WRITE_OPERATIONS, DASHBOARD_WRITE_OPERATIONS_BY_NAME, DashboardWriteCategory, DashboardWriteSensitivity } from "@kici-dev/engine/protocol/dashboard-write-operations";
|
|
28
29
|
import { CLUSTER_NAME_FORMAT_MESSAGE } from "@kici-dev/engine/protocol/cluster-name";
|
|
29
|
-
import { KICI_AGENT_ENV_PREFIX, KNOWN_ROLES, ScalerBackendType, ScalerEventType, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, WS_MAX_PAYLOAD_BYTES, accessLogWarmSqlCase, getAccessLogColdDays, getSecretAuditLogColdDays, minAccessLogWarmDays, minSecretAuditLogWarmDays, scalerAgentLabels, secretAuditLogWarmSqlCase } from "@kici-dev/engine";
|
|
30
30
|
import http from "node:http";
|
|
31
31
|
import { PassThrough } from "node:stream";
|
|
32
32
|
import Docker from "dockerode";
|
|
@@ -979,6 +979,8 @@ var init_schema = __esmMin((() => {
|
|
|
979
979
|
}).optional(),
|
|
980
980
|
agentAuth: z.enum(["token", "none"]).optional(),
|
|
981
981
|
agentTokenTtlMs: z.coerce.number().optional(),
|
|
982
|
+
rosterGraceMs: z.coerce.number().optional(),
|
|
983
|
+
rosterTtlMs: z.coerce.number().optional(),
|
|
982
984
|
queue: z.object({
|
|
983
985
|
maxDepth: z.coerce.number().optional(),
|
|
984
986
|
timeoutMs: z.coerce.number().optional(),
|
|
@@ -1055,6 +1057,8 @@ var init_schema = __esmMin((() => {
|
|
|
1055
1057
|
}).optional(),
|
|
1056
1058
|
agentAuth: z.enum(["token", "none"]).default("token"),
|
|
1057
1059
|
agentTokenTtlMs: z.coerce.number().default(36e5),
|
|
1060
|
+
rosterGraceMs: z.coerce.number().default(3e5),
|
|
1061
|
+
rosterTtlMs: z.coerce.number().default(18e5),
|
|
1058
1062
|
queueMaxDepth: z.coerce.number().default(1e3),
|
|
1059
1063
|
queueTimeoutMs: z.coerce.number().default(36e5),
|
|
1060
1064
|
/**
|
|
@@ -1550,8 +1554,8 @@ var init_client = __esmMin((() => {
|
|
|
1550
1554
|
//#region src/db/migrations/001_initial.ts
|
|
1551
1555
|
init_client();
|
|
1552
1556
|
var _001_initial_exports = /* @__PURE__ */ __exportAll({
|
|
1553
|
-
down: () => down$
|
|
1554
|
-
up: () => up$
|
|
1557
|
+
down: () => down$41,
|
|
1558
|
+
up: () => up$41
|
|
1555
1559
|
});
|
|
1556
1560
|
/**
|
|
1557
1561
|
* Squashed initial migration -- creates the complete Orchestrator database schema.
|
|
@@ -2243,7 +2247,7 @@ const DDL_STATEMENTS = [
|
|
|
2243
2247
|
`ALTER TABLE ONLY public.held_runs
|
|
2244
2248
|
ADD CONSTRAINT held_runs_environment_id_fkey FOREIGN KEY (environment_id) REFERENCES public.environments(id);`
|
|
2245
2249
|
];
|
|
2246
|
-
async function up$
|
|
2250
|
+
async function up$41(db) {
|
|
2247
2251
|
for (const stmt of DDL_STATEMENTS) await sql.raw(stmt).execute(db);
|
|
2248
2252
|
await sql`
|
|
2249
2253
|
INSERT INTO cluster_meta (key, value)
|
|
@@ -2265,7 +2269,7 @@ async function up$37(db) {
|
|
|
2265
2269
|
* Rollback drops everything created above. Uses CASCADE on table drops to cut
|
|
2266
2270
|
* through the FK graph without relying on exact topological order.
|
|
2267
2271
|
*/
|
|
2268
|
-
async function down$
|
|
2272
|
+
async function down$41(db) {
|
|
2269
2273
|
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);
|
|
2270
2274
|
for (const table of [
|
|
2271
2275
|
"workflow_registrations",
|
|
@@ -2312,8 +2316,8 @@ async function down$37(db) {
|
|
|
2312
2316
|
//#endregion
|
|
2313
2317
|
//#region src/db/migrations/002_config_versions_key_version.ts
|
|
2314
2318
|
var _002_config_versions_key_version_exports = /* @__PURE__ */ __exportAll({
|
|
2315
|
-
down: () => down$
|
|
2316
|
-
up: () => up$
|
|
2319
|
+
down: () => down$40,
|
|
2320
|
+
up: () => up$40
|
|
2317
2321
|
});
|
|
2318
2322
|
/**
|
|
2319
2323
|
* Add key_version column to config_versions so that sensitive-field encryption
|
|
@@ -2327,13 +2331,13 @@ var _002_config_versions_key_version_exports = /* @__PURE__ */ __exportAll({
|
|
|
2327
2331
|
* No index is needed: rotation does a full-table scan; reads are by
|
|
2328
2332
|
* `version` primary key and never filter on `key_version`.
|
|
2329
2333
|
*/
|
|
2330
|
-
async function up$
|
|
2334
|
+
async function up$40(db) {
|
|
2331
2335
|
await sql`
|
|
2332
2336
|
ALTER TABLE public.config_versions
|
|
2333
2337
|
ADD COLUMN key_version integer NOT NULL DEFAULT 1
|
|
2334
2338
|
`.execute(db);
|
|
2335
2339
|
}
|
|
2336
|
-
async function down$
|
|
2340
|
+
async function down$40(db) {
|
|
2337
2341
|
await sql`
|
|
2338
2342
|
ALTER TABLE public.config_versions
|
|
2339
2343
|
DROP COLUMN key_version
|
|
@@ -2342,8 +2346,8 @@ async function down$36(db) {
|
|
|
2342
2346
|
//#endregion
|
|
2343
2347
|
//#region src/db/migrations/003_access_log.ts
|
|
2344
2348
|
var _003_access_log_exports = /* @__PURE__ */ __exportAll({
|
|
2345
|
-
down: () => down$
|
|
2346
|
-
up: () => up$
|
|
2349
|
+
down: () => down$39,
|
|
2350
|
+
up: () => up$39
|
|
2347
2351
|
});
|
|
2348
2352
|
/**
|
|
2349
2353
|
* Access log: one row per read or orchestrator-admin mutation attributable
|
|
@@ -2367,7 +2371,7 @@ var _003_access_log_exports = /* @__PURE__ */ __exportAll({
|
|
|
2367
2371
|
* Retention is TTL-based via expires_at; packages/orchestrator/src/queue/
|
|
2368
2372
|
* cleanup.ts picks up the prune pass.
|
|
2369
2373
|
*/
|
|
2370
|
-
async function up$
|
|
2374
|
+
async function up$39(db) {
|
|
2371
2375
|
await sql`
|
|
2372
2376
|
CREATE TABLE public.access_log (
|
|
2373
2377
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
@@ -2405,28 +2409,28 @@ async function up$35(db) {
|
|
|
2405
2409
|
ON public.access_log (actor_type, actor_id, created_at DESC)
|
|
2406
2410
|
`.execute(db);
|
|
2407
2411
|
}
|
|
2408
|
-
async function down$
|
|
2412
|
+
async function down$39(db) {
|
|
2409
2413
|
await sql`DROP TABLE IF EXISTS public.access_log`.execute(db);
|
|
2410
2414
|
}
|
|
2411
2415
|
//#endregion
|
|
2412
2416
|
//#region src/db/migrations/004_rename_bundle_to_source.ts
|
|
2413
2417
|
var _004_rename_bundle_to_source_exports = /* @__PURE__ */ __exportAll({
|
|
2414
|
-
down: () => down$
|
|
2415
|
-
up: () => up$
|
|
2418
|
+
down: () => down$38,
|
|
2419
|
+
up: () => up$38
|
|
2416
2420
|
});
|
|
2417
|
-
async function up$
|
|
2421
|
+
async function up$38(db) {
|
|
2418
2422
|
await db.schema.alterTable("dispatch_queue").renameColumn("bundle_url", "source_tar_url").execute();
|
|
2419
2423
|
await db.schema.alterTable("dispatch_queue").renameColumn("bundle_hash", "source_tar_hash").execute();
|
|
2420
2424
|
}
|
|
2421
|
-
async function down$
|
|
2425
|
+
async function down$38(db) {
|
|
2422
2426
|
await db.schema.alterTable("dispatch_queue").renameColumn("source_tar_url", "bundle_url").execute();
|
|
2423
2427
|
await db.schema.alterTable("dispatch_queue").renameColumn("source_tar_hash", "bundle_hash").execute();
|
|
2424
2428
|
}
|
|
2425
2429
|
//#endregion
|
|
2426
2430
|
//#region src/db/migrations/005_cold_store_chunk_counter.ts
|
|
2427
2431
|
var _005_cold_store_chunk_counter_exports = /* @__PURE__ */ __exportAll({
|
|
2428
|
-
down: () => down$
|
|
2429
|
-
up: () => up$
|
|
2432
|
+
down: () => down$37,
|
|
2433
|
+
up: () => up$37
|
|
2430
2434
|
});
|
|
2431
2435
|
/**
|
|
2432
2436
|
* Cold-store chunk counter table.
|
|
@@ -2443,7 +2447,7 @@ var _005_cold_store_chunk_counter_exports = /* @__PURE__ */ __exportAll({
|
|
|
2443
2447
|
*
|
|
2444
2448
|
* sections 5 and 8.
|
|
2445
2449
|
*/
|
|
2446
|
-
async function up$
|
|
2450
|
+
async function up$37(db) {
|
|
2447
2451
|
await sql`
|
|
2448
2452
|
CREATE TABLE public.cold_store_chunk_counts (
|
|
2449
2453
|
db TEXT NOT NULL,
|
|
@@ -2461,14 +2465,14 @@ async function up$33(db) {
|
|
|
2461
2465
|
ON public.cold_store_chunk_counts (db, table_name)
|
|
2462
2466
|
`.execute(db);
|
|
2463
2467
|
}
|
|
2464
|
-
async function down$
|
|
2468
|
+
async function down$37(db) {
|
|
2465
2469
|
await sql`DROP TABLE IF EXISTS public.cold_store_chunk_counts`.execute(db);
|
|
2466
2470
|
}
|
|
2467
2471
|
//#endregion
|
|
2468
2472
|
//#region src/db/migrations/006_runs_jobs_steps_archived_at.ts
|
|
2469
2473
|
var _006_runs_jobs_steps_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
2470
|
-
down: () => down$
|
|
2471
|
-
up: () => up$
|
|
2474
|
+
down: () => down$36,
|
|
2475
|
+
up: () => up$36
|
|
2472
2476
|
});
|
|
2473
2477
|
/**
|
|
2474
2478
|
* `execution_runs` / `execution_jobs` / `execution_steps` cold-store
|
|
@@ -2504,7 +2508,7 @@ var _006_runs_jobs_steps_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
|
2504
2508
|
* - `idx_execution_jobs_routing_key_created (routing_key, created_at)`
|
|
2505
2509
|
* - `idx_execution_steps_routing_key_created (routing_key, created_at)`
|
|
2506
2510
|
*/
|
|
2507
|
-
async function up$
|
|
2511
|
+
async function up$36(db) {
|
|
2508
2512
|
await sql`
|
|
2509
2513
|
ALTER TABLE public.execution_runs
|
|
2510
2514
|
ADD COLUMN archived_at TIMESTAMPTZ NULL,
|
|
@@ -2549,7 +2553,7 @@ async function up$32(db) {
|
|
|
2549
2553
|
ON public.execution_steps (routing_key, created_at)
|
|
2550
2554
|
`.execute(db);
|
|
2551
2555
|
}
|
|
2552
|
-
async function down$
|
|
2556
|
+
async function down$36(db) {
|
|
2553
2557
|
await sql`DROP INDEX IF EXISTS public.idx_execution_steps_routing_key_created`.execute(db);
|
|
2554
2558
|
await sql`
|
|
2555
2559
|
ALTER TABLE public.execution_steps
|
|
@@ -2574,8 +2578,8 @@ async function down$32(db) {
|
|
|
2574
2578
|
//#endregion
|
|
2575
2579
|
//#region src/db/migrations/007_audit_logs_archived_at.ts
|
|
2576
2580
|
var _007_audit_logs_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
2577
|
-
down: () => down$
|
|
2578
|
-
up: () => up$
|
|
2581
|
+
down: () => down$35,
|
|
2582
|
+
up: () => up$35
|
|
2579
2583
|
});
|
|
2580
2584
|
/**
|
|
2581
2585
|
* `secret_audit_log` and `access_log` cold-store schema additions, plus
|
|
@@ -2615,7 +2619,7 @@ var _007_audit_logs_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
|
2615
2619
|
* `down()` would not have meaningful retention bounds. Acceptable for
|
|
2616
2620
|
* staging.
|
|
2617
2621
|
*/
|
|
2618
|
-
async function up$
|
|
2622
|
+
async function up$35(db) {
|
|
2619
2623
|
await sql`
|
|
2620
2624
|
ALTER TABLE public.secret_audit_log
|
|
2621
2625
|
ADD COLUMN archived_at TIMESTAMPTZ NULL,
|
|
@@ -2636,7 +2640,7 @@ async function up$31(db) {
|
|
|
2636
2640
|
DROP COLUMN IF EXISTS expires_at
|
|
2637
2641
|
`.execute(db);
|
|
2638
2642
|
}
|
|
2639
|
-
async function down$
|
|
2643
|
+
async function down$35(db) {
|
|
2640
2644
|
await sql`
|
|
2641
2645
|
ALTER TABLE public.access_log
|
|
2642
2646
|
ADD COLUMN expires_at TIMESTAMPTZ NOT NULL DEFAULT (now() + INTERVAL '90 days')
|
|
@@ -2664,8 +2668,8 @@ async function down$31(db) {
|
|
|
2664
2668
|
//#endregion
|
|
2665
2669
|
//#region src/db/migrations/008_event_log_archived_at.ts
|
|
2666
2670
|
var _008_event_log_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
2667
|
-
down: () => down$
|
|
2668
|
-
up: () => up$
|
|
2671
|
+
down: () => down$34,
|
|
2672
|
+
up: () => up$34
|
|
2669
2673
|
});
|
|
2670
2674
|
/**
|
|
2671
2675
|
* `event_log` cold-store schema additions plus removal of the
|
|
@@ -2703,7 +2707,7 @@ var _008_event_log_archived_at_exports = /* @__PURE__ */ __exportAll({
|
|
|
2703
2707
|
* — best effort; rows inserted between `up()` and a hypothetical
|
|
2704
2708
|
* `down()` would not have meaningful retention bounds.
|
|
2705
2709
|
*/
|
|
2706
|
-
async function up$
|
|
2710
|
+
async function up$34(db) {
|
|
2707
2711
|
await sql`
|
|
2708
2712
|
ALTER TABLE public.event_log
|
|
2709
2713
|
ADD COLUMN archived_at TIMESTAMPTZ NULL,
|
|
@@ -2719,7 +2723,7 @@ async function up$30(db) {
|
|
|
2719
2723
|
DROP COLUMN IF EXISTS expires_at
|
|
2720
2724
|
`.execute(db);
|
|
2721
2725
|
}
|
|
2722
|
-
async function down$
|
|
2726
|
+
async function down$34(db) {
|
|
2723
2727
|
await sql`
|
|
2724
2728
|
ALTER TABLE public.event_log
|
|
2725
2729
|
ADD COLUMN expires_at TIMESTAMPTZ NOT NULL DEFAULT (now() + INTERVAL '30 days')
|
|
@@ -2738,8 +2742,8 @@ async function down$30(db) {
|
|
|
2738
2742
|
//#endregion
|
|
2739
2743
|
//#region src/db/migrations/009_access_log_trigram.ts
|
|
2740
2744
|
var _009_access_log_trigram_exports = /* @__PURE__ */ __exportAll({
|
|
2741
|
-
down: () => down$
|
|
2742
|
-
up: () => up$
|
|
2745
|
+
down: () => down$33,
|
|
2746
|
+
up: () => up$33
|
|
2743
2747
|
});
|
|
2744
2748
|
/**
|
|
2745
2749
|
* Trigram (pg_trgm) index on access_log.error_message for the federated
|
|
@@ -2752,7 +2756,7 @@ var _009_access_log_trigram_exports = /* @__PURE__ */ __exportAll({
|
|
|
2752
2756
|
* EXISTS` are both safe to re-run. No CONCURRENTLY because Kysely runs
|
|
2753
2757
|
* migrations inside a transaction; the lock is brief on a sampled table.
|
|
2754
2758
|
*/
|
|
2755
|
-
async function up$
|
|
2759
|
+
async function up$33(db) {
|
|
2756
2760
|
await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`.execute(db);
|
|
2757
2761
|
await sql`
|
|
2758
2762
|
CREATE INDEX IF NOT EXISTS access_log_error_message_trgm_idx
|
|
@@ -2761,14 +2765,14 @@ async function up$29(db) {
|
|
|
2761
2765
|
WHERE error_message IS NOT NULL
|
|
2762
2766
|
`.execute(db);
|
|
2763
2767
|
}
|
|
2764
|
-
async function down$
|
|
2768
|
+
async function down$33(db) {
|
|
2765
2769
|
await sql`DROP INDEX IF EXISTS public.access_log_error_message_trgm_idx`.execute(db);
|
|
2766
2770
|
}
|
|
2767
2771
|
//#endregion
|
|
2768
2772
|
//#region src/db/migrations/010_cold_store_chunks.ts
|
|
2769
2773
|
var _010_cold_store_chunks_exports = /* @__PURE__ */ __exportAll({
|
|
2770
|
-
down: () => down$
|
|
2771
|
-
up: () => up$
|
|
2774
|
+
down: () => down$32,
|
|
2775
|
+
up: () => up$32
|
|
2772
2776
|
});
|
|
2773
2777
|
/**
|
|
2774
2778
|
* Cold-store chunk index — Phase 2 (cold-store purge).
|
|
@@ -2800,7 +2804,7 @@ var _010_cold_store_chunks_exports = /* @__PURE__ */ __exportAll({
|
|
|
2800
2804
|
* forever. Adapters that don't opt into per-bucket archival via
|
|
2801
2805
|
* `coldTtlDays` don't insert here either.
|
|
2802
2806
|
*/
|
|
2803
|
-
async function up$
|
|
2807
|
+
async function up$32(db) {
|
|
2804
2808
|
await sql`
|
|
2805
2809
|
CREATE TABLE public.cold_store_chunks (
|
|
2806
2810
|
db TEXT NOT NULL,
|
|
@@ -2827,14 +2831,14 @@ async function up$28(db) {
|
|
|
2827
2831
|
ON public.cold_store_chunks (db, table_name, tenant_id, archived_at DESC)
|
|
2828
2832
|
`.execute(db);
|
|
2829
2833
|
}
|
|
2830
|
-
async function down$
|
|
2834
|
+
async function down$32(db) {
|
|
2831
2835
|
await sql`DROP TABLE IF EXISTS public.cold_store_chunks`.execute(db);
|
|
2832
2836
|
}
|
|
2833
2837
|
//#endregion
|
|
2834
2838
|
//#region src/db/migrations/011_drop_source_secrets_notify.ts
|
|
2835
2839
|
var _011_drop_source_secrets_notify_exports = /* @__PURE__ */ __exportAll({
|
|
2836
|
-
down: () => down$
|
|
2837
|
-
up: () => up$
|
|
2840
|
+
down: () => down$31,
|
|
2841
|
+
up: () => up$31
|
|
2838
2842
|
});
|
|
2839
2843
|
/**
|
|
2840
2844
|
* Drop the `source_secrets_change_trigger` and the
|
|
@@ -2853,11 +2857,11 @@ var _011_drop_source_secrets_notify_exports = /* @__PURE__ */ __exportAll({
|
|
|
2853
2857
|
* in `001_initial.ts`. They wake up no consumer until the `WebhookSecretManager`
|
|
2854
2858
|
* is restored, so this migration is safe to roll back.
|
|
2855
2859
|
*/
|
|
2856
|
-
async function up$
|
|
2860
|
+
async function up$31(db) {
|
|
2857
2861
|
await sql`DROP TRIGGER IF EXISTS source_secrets_change_trigger ON public.scoped_secrets`.execute(db);
|
|
2858
2862
|
await sql`DROP FUNCTION IF EXISTS public.notify_source_secrets_change() CASCADE`.execute(db);
|
|
2859
2863
|
}
|
|
2860
|
-
async function down$
|
|
2864
|
+
async function down$31(db) {
|
|
2861
2865
|
await sql`
|
|
2862
2866
|
CREATE OR REPLACE FUNCTION public.notify_source_secrets_change() RETURNS trigger
|
|
2863
2867
|
LANGUAGE plpgsql
|
|
@@ -2896,8 +2900,8 @@ async function down$27(db) {
|
|
|
2896
2900
|
//#endregion
|
|
2897
2901
|
//#region src/db/migrations/012_peer_credentials_active_uniq.ts
|
|
2898
2902
|
var _012_peer_credentials_active_uniq_exports = /* @__PURE__ */ __exportAll({
|
|
2899
|
-
down: () => down$
|
|
2900
|
-
up: () => up$
|
|
2903
|
+
down: () => down$30,
|
|
2904
|
+
up: () => up$30
|
|
2901
2905
|
});
|
|
2902
2906
|
/**
|
|
2903
2907
|
* Add a partial unique index on `peer_credentials (instance_id) WHERE
|
|
@@ -2923,7 +2927,7 @@ var _012_peer_credentials_active_uniq_exports = /* @__PURE__ */ __exportAll({
|
|
|
2923
2927
|
* `down()` only drops the index; it does NOT undo the dedupe (there's no
|
|
2924
2928
|
* safe way to recreate revoked rows, and the dedupe is monotonic).
|
|
2925
2929
|
*/
|
|
2926
|
-
async function up$
|
|
2930
|
+
async function up$30(db) {
|
|
2927
2931
|
await sql`
|
|
2928
2932
|
UPDATE public.peer_credentials
|
|
2929
2933
|
SET revoked_at = NOW()
|
|
@@ -2941,14 +2945,14 @@ async function up$26(db) {
|
|
|
2941
2945
|
WHERE revoked_at IS NULL
|
|
2942
2946
|
`.execute(db);
|
|
2943
2947
|
}
|
|
2944
|
-
async function down$
|
|
2948
|
+
async function down$30(db) {
|
|
2945
2949
|
await sql`DROP INDEX IF EXISTS public.peer_credentials_active_uniq`.execute(db);
|
|
2946
2950
|
}
|
|
2947
2951
|
//#endregion
|
|
2948
2952
|
//#region src/db/migrations/013_execution_log_bytes.ts
|
|
2949
2953
|
var _013_execution_log_bytes_exports = /* @__PURE__ */ __exportAll({
|
|
2950
|
-
down: () => down$
|
|
2951
|
-
up: () => up$
|
|
2954
|
+
down: () => down$29,
|
|
2955
|
+
up: () => up$29
|
|
2952
2956
|
});
|
|
2953
2957
|
/**
|
|
2954
2958
|
* Add `log_bytes BIGINT NOT NULL DEFAULT 0` columns to `execution_runs` and
|
|
@@ -2967,7 +2971,7 @@ var _013_execution_log_bytes_exports = /* @__PURE__ */ __exportAll({
|
|
|
2967
2971
|
*
|
|
2968
2972
|
* Idempotent (`ADD COLUMN IF NOT EXISTS`).
|
|
2969
2973
|
*/
|
|
2970
|
-
async function up$
|
|
2974
|
+
async function up$29(db) {
|
|
2971
2975
|
await sql`
|
|
2972
2976
|
ALTER TABLE public.execution_runs
|
|
2973
2977
|
ADD COLUMN IF NOT EXISTS log_bytes BIGINT NOT NULL DEFAULT 0
|
|
@@ -2977,15 +2981,15 @@ async function up$25(db) {
|
|
|
2977
2981
|
ADD COLUMN IF NOT EXISTS log_bytes BIGINT NOT NULL DEFAULT 0
|
|
2978
2982
|
`.execute(db);
|
|
2979
2983
|
}
|
|
2980
|
-
async function down$
|
|
2984
|
+
async function down$29(db) {
|
|
2981
2985
|
await sql`ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS log_bytes`.execute(db);
|
|
2982
2986
|
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS log_bytes`.execute(db);
|
|
2983
2987
|
}
|
|
2984
2988
|
//#endregion
|
|
2985
2989
|
//#region src/db/migrations/014_kici_events_lease_retry.ts
|
|
2986
2990
|
var _014_kici_events_lease_retry_exports = /* @__PURE__ */ __exportAll({
|
|
2987
|
-
down: () => down$
|
|
2988
|
-
up: () => up$
|
|
2991
|
+
down: () => down$28,
|
|
2992
|
+
up: () => up$28
|
|
2989
2993
|
});
|
|
2990
2994
|
/**
|
|
2991
2995
|
* Add lease + retry + DLQ columns to `kici_events` so the EventRouter can
|
|
@@ -3019,7 +3023,7 @@ var _014_kici_events_lease_retry_exports = /* @__PURE__ */ __exportAll({
|
|
|
3019
3023
|
*
|
|
3020
3024
|
* Idempotent (`ADD COLUMN IF NOT EXISTS` + `CREATE INDEX IF NOT EXISTS`).
|
|
3021
3025
|
*/
|
|
3022
|
-
async function up$
|
|
3026
|
+
async function up$28(db) {
|
|
3023
3027
|
await sql`
|
|
3024
3028
|
ALTER TABLE public.kici_events
|
|
3025
3029
|
ADD COLUMN IF NOT EXISTS claimed_at TIMESTAMPTZ,
|
|
@@ -3050,7 +3054,7 @@ async function up$24(db) {
|
|
|
3050
3054
|
WHERE dlq_at IS NOT NULL
|
|
3051
3055
|
`.execute(db);
|
|
3052
3056
|
}
|
|
3053
|
-
async function down$
|
|
3057
|
+
async function down$28(db) {
|
|
3054
3058
|
await sql`DROP INDEX IF EXISTS public.idx_kici_events_dlq`.execute(db);
|
|
3055
3059
|
await sql`DROP INDEX IF EXISTS public.idx_kici_events_lease_expired`.execute(db);
|
|
3056
3060
|
await sql`DROP INDEX IF EXISTS public.idx_kici_events_retry_due`.execute(db);
|
|
@@ -3068,8 +3072,8 @@ async function down$24(db) {
|
|
|
3068
3072
|
//#endregion
|
|
3069
3073
|
//#region src/db/migrations/015_org_settings_customer_scoped.ts
|
|
3070
3074
|
var _015_org_settings_customer_scoped_exports = /* @__PURE__ */ __exportAll({
|
|
3071
|
-
down: () => down$
|
|
3072
|
-
up: () => up$
|
|
3075
|
+
down: () => down$27,
|
|
3076
|
+
up: () => up$27
|
|
3073
3077
|
});
|
|
3074
3078
|
/**
|
|
3075
3079
|
* Org-scope `org_settings` and qualify each glob entry by source.
|
|
@@ -3097,7 +3101,7 @@ var _015_org_settings_customer_scoped_exports = /* @__PURE__ */ __exportAll({
|
|
|
3097
3101
|
* Idempotent: a re-run on an already-migrated DB sees `customer_id` exists
|
|
3098
3102
|
* and the list columns are already jsonb, so it is a no-op.
|
|
3099
3103
|
*/
|
|
3100
|
-
async function up$
|
|
3104
|
+
async function up$27(db) {
|
|
3101
3105
|
if ((await sql`
|
|
3102
3106
|
SELECT EXISTS (
|
|
3103
3107
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3222,7 +3226,7 @@ async function up$23(db) {
|
|
|
3222
3226
|
await sql`DROP TABLE _org_settings_merged`.execute(db);
|
|
3223
3227
|
await sql`DROP TABLE _org_settings_stage`.execute(db);
|
|
3224
3228
|
}
|
|
3225
|
-
async function down$
|
|
3229
|
+
async function down$27(db) {
|
|
3226
3230
|
if (!(await sql`
|
|
3227
3231
|
SELECT EXISTS (
|
|
3228
3232
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3247,8 +3251,8 @@ async function down$23(db) {
|
|
|
3247
3251
|
//#endregion
|
|
3248
3252
|
//#region src/db/migrations/016_org_settings_allow_http_npm.ts
|
|
3249
3253
|
var _016_org_settings_allow_http_npm_exports = /* @__PURE__ */ __exportAll({
|
|
3250
|
-
down: () => down$
|
|
3251
|
-
up: () => up$
|
|
3254
|
+
down: () => down$26,
|
|
3255
|
+
up: () => up$26
|
|
3252
3256
|
});
|
|
3253
3257
|
/**
|
|
3254
3258
|
* Add `org_settings.allow_http_npm_registries boolean NOT NULL DEFAULT false`.
|
|
@@ -3261,7 +3265,7 @@ var _016_org_settings_allow_http_npm_exports = /* @__PURE__ */ __exportAll({
|
|
|
3261
3265
|
*
|
|
3262
3266
|
* Idempotent: a re-run on a DB that already has the column is a no-op.
|
|
3263
3267
|
*/
|
|
3264
|
-
async function up$
|
|
3268
|
+
async function up$26(db) {
|
|
3265
3269
|
if ((await sql`
|
|
3266
3270
|
SELECT EXISTS (
|
|
3267
3271
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3275,7 +3279,7 @@ async function up$22(db) {
|
|
|
3275
3279
|
ADD COLUMN allow_http_npm_registries boolean NOT NULL DEFAULT false
|
|
3276
3280
|
`.execute(db);
|
|
3277
3281
|
}
|
|
3278
|
-
async function down$
|
|
3282
|
+
async function down$26(db) {
|
|
3279
3283
|
await sql`
|
|
3280
3284
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS allow_http_npm_registries
|
|
3281
3285
|
`.execute(db);
|
|
@@ -3283,8 +3287,8 @@ async function down$22(db) {
|
|
|
3283
3287
|
//#endregion
|
|
3284
3288
|
//#region src/db/migrations/017_org_id_widen.ts
|
|
3285
3289
|
var _017_org_id_widen_exports = /* @__PURE__ */ __exportAll({
|
|
3286
|
-
down: () => down$
|
|
3287
|
-
up: () => up$
|
|
3290
|
+
down: () => down$25,
|
|
3291
|
+
up: () => up$25
|
|
3288
3292
|
});
|
|
3289
3293
|
/**
|
|
3290
3294
|
* Widen every orchestrator-side `org_id varchar(12)` column to
|
|
@@ -3324,17 +3328,17 @@ const ORG_ID_TABLES$1 = [
|
|
|
3324
3328
|
"held_runs",
|
|
3325
3329
|
"scoped_secrets"
|
|
3326
3330
|
];
|
|
3327
|
-
async function up$
|
|
3331
|
+
async function up$25(db) {
|
|
3328
3332
|
for (const table of ORG_ID_TABLES$1) await sql.raw(`ALTER TABLE public.${table} ALTER COLUMN org_id TYPE varchar(16)`).execute(db);
|
|
3329
3333
|
}
|
|
3330
|
-
async function down$
|
|
3334
|
+
async function down$25(db) {
|
|
3331
3335
|
for (const table of ORG_ID_TABLES$1) await sql.raw(`ALTER TABLE public.${table} ALTER COLUMN org_id TYPE varchar(12)`).execute(db);
|
|
3332
3336
|
}
|
|
3333
3337
|
//#endregion
|
|
3334
3338
|
//#region src/db/migrations/018_org_id_prefix_backfill.ts
|
|
3335
3339
|
var _018_org_id_prefix_backfill_exports = /* @__PURE__ */ __exportAll({
|
|
3336
|
-
down: () => down$
|
|
3337
|
-
up: () => up$
|
|
3340
|
+
down: () => down$24,
|
|
3341
|
+
up: () => up$24
|
|
3338
3342
|
});
|
|
3339
3343
|
/**
|
|
3340
3344
|
* Prefix every orchestrator-side tenant string with `org_` to align
|
|
@@ -3379,19 +3383,19 @@ const CUSTOMER_ID_TABLES = [
|
|
|
3379
3383
|
"workflow_registrations",
|
|
3380
3384
|
"org_settings"
|
|
3381
3385
|
];
|
|
3382
|
-
async function up$
|
|
3386
|
+
async function up$24(db) {
|
|
3383
3387
|
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);
|
|
3384
3388
|
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);
|
|
3385
3389
|
}
|
|
3386
|
-
async function down$
|
|
3390
|
+
async function down$24(db) {
|
|
3387
3391
|
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);
|
|
3388
3392
|
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);
|
|
3389
3393
|
}
|
|
3390
3394
|
//#endregion
|
|
3391
3395
|
//#region src/db/migrations/019_generic_sources_change_notify.ts
|
|
3392
3396
|
var _019_generic_sources_change_notify_exports = /* @__PURE__ */ __exportAll({
|
|
3393
|
-
down: () => down$
|
|
3394
|
-
up: () => up$
|
|
3397
|
+
down: () => down$23,
|
|
3398
|
+
up: () => up$23
|
|
3395
3399
|
});
|
|
3396
3400
|
/**
|
|
3397
3401
|
* Add a Postgres trigger on `generic_webhook_sources` that emits
|
|
@@ -3414,7 +3418,7 @@ var _019_generic_sources_change_notify_exports = /* @__PURE__ */ __exportAll({
|
|
|
3414
3418
|
* (`notify_sources_change()` + `sources_change_trigger`, defined in
|
|
3415
3419
|
* `001_initial.ts`).
|
|
3416
3420
|
*/
|
|
3417
|
-
async function up$
|
|
3421
|
+
async function up$23(db) {
|
|
3418
3422
|
await sql`
|
|
3419
3423
|
CREATE FUNCTION public.notify_generic_sources_change() RETURNS trigger
|
|
3420
3424
|
LANGUAGE plpgsql
|
|
@@ -3435,15 +3439,15 @@ async function up$19(db) {
|
|
|
3435
3439
|
FOR EACH ROW EXECUTE FUNCTION public.notify_generic_sources_change()
|
|
3436
3440
|
`.execute(db);
|
|
3437
3441
|
}
|
|
3438
|
-
async function down$
|
|
3442
|
+
async function down$23(db) {
|
|
3439
3443
|
await sql`DROP TRIGGER IF EXISTS generic_sources_change_trigger ON public.generic_webhook_sources`.execute(db);
|
|
3440
3444
|
await sql`DROP FUNCTION IF EXISTS public.notify_generic_sources_change()`.execute(db);
|
|
3441
3445
|
}
|
|
3442
3446
|
//#endregion
|
|
3443
3447
|
//#region src/db/migrations/020_org_settings_dashboard_write_policy.ts
|
|
3444
3448
|
var _020_org_settings_dashboard_write_policy_exports = /* @__PURE__ */ __exportAll({
|
|
3445
|
-
down: () => down$
|
|
3446
|
-
up: () => up$
|
|
3449
|
+
down: () => down$22,
|
|
3450
|
+
up: () => up$22
|
|
3447
3451
|
});
|
|
3448
3452
|
/**
|
|
3449
3453
|
* Add `org_settings.dashboard_write_policy jsonb NOT NULL DEFAULT '{}'`.
|
|
@@ -3458,7 +3462,7 @@ var _020_org_settings_dashboard_write_policy_exports = /* @__PURE__ */ __exportA
|
|
|
3458
3462
|
*
|
|
3459
3463
|
* Idempotent: a re-run on a DB that already has the column is a no-op.
|
|
3460
3464
|
*/
|
|
3461
|
-
async function up$
|
|
3465
|
+
async function up$22(db) {
|
|
3462
3466
|
if ((await sql`
|
|
3463
3467
|
SELECT EXISTS (
|
|
3464
3468
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3472,7 +3476,7 @@ async function up$18(db) {
|
|
|
3472
3476
|
ADD COLUMN dashboard_write_policy jsonb NOT NULL DEFAULT '{}'::jsonb
|
|
3473
3477
|
`.execute(db);
|
|
3474
3478
|
}
|
|
3475
|
-
async function down$
|
|
3479
|
+
async function down$22(db) {
|
|
3476
3480
|
await sql`
|
|
3477
3481
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS dashboard_write_policy
|
|
3478
3482
|
`.execute(db);
|
|
@@ -3480,8 +3484,8 @@ async function down$18(db) {
|
|
|
3480
3484
|
//#endregion
|
|
3481
3485
|
//#region src/db/migrations/021_check_run_tracking.ts
|
|
3482
3486
|
var _021_check_run_tracking_exports = /* @__PURE__ */ __exportAll({
|
|
3483
|
-
down: () => down$
|
|
3484
|
-
up: () => up$
|
|
3487
|
+
down: () => down$21,
|
|
3488
|
+
up: () => up$21
|
|
3485
3489
|
});
|
|
3486
3490
|
/**
|
|
3487
3491
|
* Add `check_run_tracking` table for HA-safe check-run state persistence.
|
|
@@ -3505,7 +3509,7 @@ var _021_check_run_tracking_exports = /* @__PURE__ */ __exportAll({
|
|
|
3505
3509
|
*
|
|
3506
3510
|
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
3507
3511
|
*/
|
|
3508
|
-
async function up$
|
|
3512
|
+
async function up$21(db) {
|
|
3509
3513
|
if ((await sql`
|
|
3510
3514
|
SELECT EXISTS (
|
|
3511
3515
|
SELECT 1 FROM information_schema.tables
|
|
@@ -3536,14 +3540,14 @@ async function up$17(db) {
|
|
|
3536
3540
|
WHERE run_id IS NOT NULL
|
|
3537
3541
|
`.execute(db);
|
|
3538
3542
|
}
|
|
3539
|
-
async function down$
|
|
3543
|
+
async function down$21(db) {
|
|
3540
3544
|
await sql`DROP TABLE IF EXISTS public.check_run_tracking`.execute(db);
|
|
3541
3545
|
}
|
|
3542
3546
|
//#endregion
|
|
3543
3547
|
//#region src/db/migrations/022_scaler_manager_state.ts
|
|
3544
3548
|
var _022_scaler_manager_state_exports = /* @__PURE__ */ __exportAll({
|
|
3545
|
-
down: () => down$
|
|
3546
|
-
up: () => up$
|
|
3549
|
+
down: () => down$20,
|
|
3550
|
+
up: () => up$20
|
|
3547
3551
|
});
|
|
3548
3552
|
/**
|
|
3549
3553
|
* Add three tables persisting `ScalerManager` per-coord state:
|
|
@@ -3570,7 +3574,7 @@ var _022_scaler_manager_state_exports = /* @__PURE__ */ __exportAll({
|
|
|
3570
3574
|
* Idempotent: a re-run on a DB that already has any of these tables
|
|
3571
3575
|
* leaves the existing one alone.
|
|
3572
3576
|
*/
|
|
3573
|
-
async function up$
|
|
3577
|
+
async function up$20(db) {
|
|
3574
3578
|
const tableExists = async (name) => {
|
|
3575
3579
|
return (await sql`
|
|
3576
3580
|
SELECT EXISTS (
|
|
@@ -3620,7 +3624,7 @@ async function up$16(db) {
|
|
|
3620
3624
|
`.execute(db);
|
|
3621
3625
|
}
|
|
3622
3626
|
}
|
|
3623
|
-
async function down$
|
|
3627
|
+
async function down$20(db) {
|
|
3624
3628
|
await sql`DROP TABLE IF EXISTS public.scaler_reservations`.execute(db);
|
|
3625
3629
|
await sql`DROP TABLE IF EXISTS public.scaler_agent_jobs`.execute(db);
|
|
3626
3630
|
await sql`DROP TABLE IF EXISTS public.scaler_spawning_agents`.execute(db);
|
|
@@ -3628,8 +3632,8 @@ async function down$16(db) {
|
|
|
3628
3632
|
//#endregion
|
|
3629
3633
|
//#region src/db/migrations/023_dispatch_queue_recovery_deadline.ts
|
|
3630
3634
|
var _023_dispatch_queue_recovery_deadline_exports = /* @__PURE__ */ __exportAll({
|
|
3631
|
-
down: () => down$
|
|
3632
|
-
up: () => up$
|
|
3635
|
+
down: () => down$19,
|
|
3636
|
+
up: () => up$19
|
|
3633
3637
|
});
|
|
3634
3638
|
/**
|
|
3635
3639
|
* Add `dispatch_queue.recovery_deadline TIMESTAMPTZ` and
|
|
@@ -3653,7 +3657,7 @@ var _023_dispatch_queue_recovery_deadline_exports = /* @__PURE__ */ __exportAll(
|
|
|
3653
3657
|
* Idempotent: re-running on a DB that already has either column is a
|
|
3654
3658
|
* no-op.
|
|
3655
3659
|
*/
|
|
3656
|
-
async function up$
|
|
3660
|
+
async function up$19(db) {
|
|
3657
3661
|
const colExists = async (name) => {
|
|
3658
3662
|
return (await sql`
|
|
3659
3663
|
SELECT EXISTS (
|
|
@@ -3678,7 +3682,7 @@ async function up$15(db) {
|
|
|
3678
3682
|
WHERE recovery_deadline IS NOT NULL
|
|
3679
3683
|
`.execute(db);
|
|
3680
3684
|
}
|
|
3681
|
-
async function down$
|
|
3685
|
+
async function down$19(db) {
|
|
3682
3686
|
await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_recovery_deadline`.execute(db);
|
|
3683
3687
|
await sql`
|
|
3684
3688
|
ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS recovery_agent_id
|
|
@@ -3690,8 +3694,8 @@ async function down$15(db) {
|
|
|
3690
3694
|
//#endregion
|
|
3691
3695
|
//#region src/db/migrations/024_dispatch_queue_provisioning_error.ts
|
|
3692
3696
|
var _024_dispatch_queue_provisioning_error_exports = /* @__PURE__ */ __exportAll({
|
|
3693
|
-
down: () => down$
|
|
3694
|
-
up: () => up$
|
|
3697
|
+
down: () => down$18,
|
|
3698
|
+
up: () => up$18
|
|
3695
3699
|
});
|
|
3696
3700
|
/**
|
|
3697
3701
|
* Add `dispatch_queue.last_provisioning_error TEXT` recording the most
|
|
@@ -3707,7 +3711,7 @@ var _024_dispatch_queue_provisioning_error_exports = /* @__PURE__ */ __exportAll
|
|
|
3707
3711
|
*
|
|
3708
3712
|
* Idempotent: re-running on a DB that already has the column is a no-op.
|
|
3709
3713
|
*/
|
|
3710
|
-
async function up$
|
|
3714
|
+
async function up$18(db) {
|
|
3711
3715
|
const colExists = async (name) => {
|
|
3712
3716
|
return (await sql`
|
|
3713
3717
|
SELECT EXISTS (
|
|
@@ -3723,7 +3727,7 @@ async function up$14(db) {
|
|
|
3723
3727
|
ADD COLUMN last_provisioning_error TEXT
|
|
3724
3728
|
`.execute(db);
|
|
3725
3729
|
}
|
|
3726
|
-
async function down$
|
|
3730
|
+
async function down$18(db) {
|
|
3727
3731
|
await sql`
|
|
3728
3732
|
ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS last_provisioning_error
|
|
3729
3733
|
`.execute(db);
|
|
@@ -3731,8 +3735,8 @@ async function down$14(db) {
|
|
|
3731
3735
|
//#endregion
|
|
3732
3736
|
//#region src/db/migrations/025_init_failure.ts
|
|
3733
3737
|
var _025_init_failure_exports = /* @__PURE__ */ __exportAll({
|
|
3734
|
-
down: () => down$
|
|
3735
|
-
up: () => up$
|
|
3738
|
+
down: () => down$17,
|
|
3739
|
+
up: () => up$17
|
|
3736
3740
|
});
|
|
3737
3741
|
/**
|
|
3738
3742
|
* Add `init_failure jsonb` columns to `execution_runs` and `execution_jobs`.
|
|
@@ -3746,7 +3750,7 @@ var _025_init_failure_exports = /* @__PURE__ */ __exportAll({
|
|
|
3746
3750
|
* Idempotent: re-running on a DB that already has either column is a no-op
|
|
3747
3751
|
* for that column.
|
|
3748
3752
|
*/
|
|
3749
|
-
async function up$
|
|
3753
|
+
async function up$17(db) {
|
|
3750
3754
|
const colExists = async (table, name) => {
|
|
3751
3755
|
return (await sql`
|
|
3752
3756
|
SELECT EXISTS (
|
|
@@ -3766,7 +3770,7 @@ async function up$13(db) {
|
|
|
3766
3770
|
ADD COLUMN init_failure JSONB DEFAULT NULL
|
|
3767
3771
|
`.execute(db);
|
|
3768
3772
|
}
|
|
3769
|
-
async function down$
|
|
3773
|
+
async function down$17(db) {
|
|
3770
3774
|
await sql`
|
|
3771
3775
|
ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS init_failure
|
|
3772
3776
|
`.execute(db);
|
|
@@ -3777,8 +3781,8 @@ async function down$13(db) {
|
|
|
3777
3781
|
//#endregion
|
|
3778
3782
|
//#region src/db/migrations/026_event_log_lockfile_corrupt.ts
|
|
3779
3783
|
var _026_event_log_lockfile_corrupt_exports = /* @__PURE__ */ __exportAll({
|
|
3780
|
-
down: () => down$
|
|
3781
|
-
up: () => up$
|
|
3784
|
+
down: () => down$16,
|
|
3785
|
+
up: () => up$16
|
|
3782
3786
|
});
|
|
3783
3787
|
/**
|
|
3784
3788
|
* Extend the event_log.status CHECK constraint with 'lockfile_corrupt' so the
|
|
@@ -3787,7 +3791,7 @@ var _026_event_log_lockfile_corrupt_exports = /* @__PURE__ */ __exportAll({
|
|
|
3787
3791
|
*
|
|
3788
3792
|
* Idempotent: the DROP ... IF EXISTS / re-ADD pair re-runs cleanly.
|
|
3789
3793
|
*/
|
|
3790
|
-
async function up$
|
|
3794
|
+
async function up$16(db) {
|
|
3791
3795
|
await sql`ALTER TABLE event_log DROP CONSTRAINT IF EXISTS event_log_status_check`.execute(db);
|
|
3792
3796
|
await sql`
|
|
3793
3797
|
ALTER TABLE event_log ADD CONSTRAINT event_log_status_check
|
|
@@ -3797,7 +3801,7 @@ async function up$12(db) {
|
|
|
3797
3801
|
])))
|
|
3798
3802
|
`.execute(db);
|
|
3799
3803
|
}
|
|
3800
|
-
async function down$
|
|
3804
|
+
async function down$16(db) {
|
|
3801
3805
|
await sql`ALTER TABLE event_log DROP CONSTRAINT IF EXISTS event_log_status_check`.execute(db);
|
|
3802
3806
|
await sql`
|
|
3803
3807
|
ALTER TABLE event_log ADD CONSTRAINT event_log_status_check
|
|
@@ -3810,8 +3814,8 @@ async function down$12(db) {
|
|
|
3810
3814
|
//#endregion
|
|
3811
3815
|
//#region src/db/migrations/027_workflow_timeout.ts
|
|
3812
3816
|
var _027_workflow_timeout_exports = /* @__PURE__ */ __exportAll({
|
|
3813
|
-
down: () => down$
|
|
3814
|
-
up: () => up$
|
|
3817
|
+
down: () => down$15,
|
|
3818
|
+
up: () => up$15
|
|
3815
3819
|
});
|
|
3816
3820
|
/**
|
|
3817
3821
|
* Add `workflow_timeout_ms integer` to `execution_runs`.
|
|
@@ -3829,13 +3833,13 @@ var _027_workflow_timeout_exports = /* @__PURE__ */ __exportAll({
|
|
|
3829
3833
|
*
|
|
3830
3834
|
* Idempotent: re-running on a DB that already has the column is a no-op.
|
|
3831
3835
|
*/
|
|
3832
|
-
async function up$
|
|
3836
|
+
async function up$15(db) {
|
|
3833
3837
|
await sql`
|
|
3834
3838
|
ALTER TABLE public.execution_runs
|
|
3835
3839
|
ADD COLUMN IF NOT EXISTS workflow_timeout_ms INTEGER DEFAULT NULL
|
|
3836
3840
|
`.execute(db);
|
|
3837
3841
|
}
|
|
3838
|
-
async function down$
|
|
3842
|
+
async function down$15(db) {
|
|
3839
3843
|
await sql`
|
|
3840
3844
|
ALTER TABLE public.execution_runs DROP COLUMN IF EXISTS workflow_timeout_ms
|
|
3841
3845
|
`.execute(db);
|
|
@@ -3843,8 +3847,8 @@ async function down$11(db) {
|
|
|
3843
3847
|
//#endregion
|
|
3844
3848
|
//#region src/db/migrations/028_org_settings_user_cache.ts
|
|
3845
3849
|
var _028_org_settings_user_cache_exports = /* @__PURE__ */ __exportAll({
|
|
3846
|
-
down: () => down$
|
|
3847
|
-
up: () => up$
|
|
3850
|
+
down: () => down$14,
|
|
3851
|
+
up: () => up$14
|
|
3848
3852
|
});
|
|
3849
3853
|
/**
|
|
3850
3854
|
* Add `org_settings.user_cache_quota_bytes bigint` and
|
|
@@ -3873,7 +3877,7 @@ async function columnExists(db, column) {
|
|
|
3873
3877
|
) AS exists
|
|
3874
3878
|
`.execute(db)).rows[0]?.exists ?? false;
|
|
3875
3879
|
}
|
|
3876
|
-
async function up$
|
|
3880
|
+
async function up$14(db) {
|
|
3877
3881
|
if (!await columnExists(db, "user_cache_quota_bytes")) await sql`
|
|
3878
3882
|
ALTER TABLE public.org_settings
|
|
3879
3883
|
ADD COLUMN user_cache_quota_bytes bigint
|
|
@@ -3883,7 +3887,7 @@ async function up$10(db) {
|
|
|
3883
3887
|
ADD COLUMN user_cache_ttl_ms bigint
|
|
3884
3888
|
`.execute(db);
|
|
3885
3889
|
}
|
|
3886
|
-
async function down$
|
|
3890
|
+
async function down$14(db) {
|
|
3887
3891
|
await sql`
|
|
3888
3892
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS user_cache_quota_bytes
|
|
3889
3893
|
`.execute(db);
|
|
@@ -3894,8 +3898,8 @@ async function down$10(db) {
|
|
|
3894
3898
|
//#endregion
|
|
3895
3899
|
//#region src/db/migrations/029_dispatch_queue_attempts.ts
|
|
3896
3900
|
var _029_dispatch_queue_attempts_exports = /* @__PURE__ */ __exportAll({
|
|
3897
|
-
down: () => down$
|
|
3898
|
-
up: () => up$
|
|
3901
|
+
down: () => down$13,
|
|
3902
|
+
up: () => up$13
|
|
3899
3903
|
});
|
|
3900
3904
|
/**
|
|
3901
3905
|
* Add `dispatch_queue.dispatch_attempts INT NOT NULL DEFAULT 0`.
|
|
@@ -3909,7 +3913,7 @@ var _029_dispatch_queue_attempts_exports = /* @__PURE__ */ __exportAll({
|
|
|
3909
3913
|
*
|
|
3910
3914
|
* Idempotent: re-running on a DB that already has the column is a no-op.
|
|
3911
3915
|
*/
|
|
3912
|
-
async function up$
|
|
3916
|
+
async function up$13(db) {
|
|
3913
3917
|
if (!((await sql`
|
|
3914
3918
|
SELECT EXISTS (
|
|
3915
3919
|
SELECT 1 FROM information_schema.columns
|
|
@@ -3922,7 +3926,7 @@ async function up$9(db) {
|
|
|
3922
3926
|
ADD COLUMN dispatch_attempts INT NOT NULL DEFAULT 0
|
|
3923
3927
|
`.execute(db);
|
|
3924
3928
|
}
|
|
3925
|
-
async function down$
|
|
3929
|
+
async function down$13(db) {
|
|
3926
3930
|
await sql`
|
|
3927
3931
|
ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS dispatch_attempts
|
|
3928
3932
|
`.execute(db);
|
|
@@ -3930,8 +3934,8 @@ async function down$9(db) {
|
|
|
3930
3934
|
//#endregion
|
|
3931
3935
|
//#region src/db/migrations/030_held_runs_env_set_null.ts
|
|
3932
3936
|
var _030_held_runs_env_set_null_exports = /* @__PURE__ */ __exportAll({
|
|
3933
|
-
down: () => down$
|
|
3934
|
-
up: () => up$
|
|
3937
|
+
down: () => down$12,
|
|
3938
|
+
up: () => up$12
|
|
3935
3939
|
});
|
|
3936
3940
|
/**
|
|
3937
3941
|
* held_runs.environment_id becomes nullable with ON DELETE SET NULL so
|
|
@@ -3942,14 +3946,14 @@ var _030_held_runs_env_set_null_exports = /* @__PURE__ */ __exportAll({
|
|
|
3942
3946
|
* Idempotent: dropping the NOT NULL and the constraint are both no-ops on a
|
|
3943
3947
|
* re-run, and the constraint is re-created with the SET NULL action.
|
|
3944
3948
|
*/
|
|
3945
|
-
async function up$
|
|
3949
|
+
async function up$12(db) {
|
|
3946
3950
|
await sql`ALTER TABLE public.held_runs ALTER COLUMN environment_id DROP NOT NULL`.execute(db);
|
|
3947
3951
|
await sql`ALTER TABLE public.held_runs DROP CONSTRAINT IF EXISTS held_runs_environment_id_fkey`.execute(db);
|
|
3948
3952
|
await sql`ALTER TABLE public.held_runs
|
|
3949
3953
|
ADD CONSTRAINT held_runs_environment_id_fkey
|
|
3950
3954
|
FOREIGN KEY (environment_id) REFERENCES public.environments(id) ON DELETE SET NULL`.execute(db);
|
|
3951
3955
|
}
|
|
3952
|
-
async function down$
|
|
3956
|
+
async function down$12(db) {
|
|
3953
3957
|
await sql`ALTER TABLE public.held_runs DROP CONSTRAINT IF EXISTS held_runs_environment_id_fkey`.execute(db);
|
|
3954
3958
|
await sql`ALTER TABLE public.held_runs
|
|
3955
3959
|
ADD CONSTRAINT held_runs_environment_id_fkey
|
|
@@ -3959,8 +3963,8 @@ async function down$8(db) {
|
|
|
3959
3963
|
//#endregion
|
|
3960
3964
|
//#region src/db/migrations/031_dispatch_queue_ack_deadline.ts
|
|
3961
3965
|
var _031_dispatch_queue_ack_deadline_exports = /* @__PURE__ */ __exportAll({
|
|
3962
|
-
down: () => down$
|
|
3963
|
-
up: () => up$
|
|
3966
|
+
down: () => down$11,
|
|
3967
|
+
up: () => up$11
|
|
3964
3968
|
});
|
|
3965
3969
|
/**
|
|
3966
3970
|
* Add `dispatch_queue.ack_deadline TIMESTAMPTZ` and
|
|
@@ -3977,7 +3981,7 @@ var _031_dispatch_queue_ack_deadline_exports = /* @__PURE__ */ __exportAll({
|
|
|
3977
3981
|
*
|
|
3978
3982
|
* Idempotent: re-running on a DB that already has either column is a no-op.
|
|
3979
3983
|
*/
|
|
3980
|
-
async function up$
|
|
3984
|
+
async function up$11(db) {
|
|
3981
3985
|
const colExists = async (name) => {
|
|
3982
3986
|
return (await sql`
|
|
3983
3987
|
SELECT EXISTS (
|
|
@@ -4002,7 +4006,7 @@ async function up$7(db) {
|
|
|
4002
4006
|
WHERE ack_deadline IS NOT NULL
|
|
4003
4007
|
`.execute(db);
|
|
4004
4008
|
}
|
|
4005
|
-
async function down$
|
|
4009
|
+
async function down$11(db) {
|
|
4006
4010
|
await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_ack_deadline`.execute(db);
|
|
4007
4011
|
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS ack_agent_id`.execute(db);
|
|
4008
4012
|
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS ack_deadline`.execute(db);
|
|
@@ -4010,8 +4014,8 @@ async function down$7(db) {
|
|
|
4010
4014
|
//#endregion
|
|
4011
4015
|
//#region src/db/migrations/032_org_settings_dispatch_ack_timeout.ts
|
|
4012
4016
|
var _032_org_settings_dispatch_ack_timeout_exports = /* @__PURE__ */ __exportAll({
|
|
4013
|
-
down: () => down$
|
|
4014
|
-
up: () => up$
|
|
4017
|
+
down: () => down$10,
|
|
4018
|
+
up: () => up$10
|
|
4015
4019
|
});
|
|
4016
4020
|
/**
|
|
4017
4021
|
* Add `org_settings.dispatch_ack_timeout_ms BIGINT` (nullable).
|
|
@@ -4023,7 +4027,7 @@ var _032_org_settings_dispatch_ack_timeout_exports = /* @__PURE__ */ __exportAll
|
|
|
4023
4027
|
*
|
|
4024
4028
|
* Idempotent: a re-run on a DB that already has the column is a no-op.
|
|
4025
4029
|
*/
|
|
4026
|
-
async function up$
|
|
4030
|
+
async function up$10(db) {
|
|
4027
4031
|
if ((await sql`
|
|
4028
4032
|
SELECT EXISTS (
|
|
4029
4033
|
SELECT 1 FROM information_schema.columns
|
|
@@ -4037,7 +4041,7 @@ async function up$6(db) {
|
|
|
4037
4041
|
ADD COLUMN dispatch_ack_timeout_ms BIGINT
|
|
4038
4042
|
`.execute(db);
|
|
4039
4043
|
}
|
|
4040
|
-
async function down$
|
|
4044
|
+
async function down$10(db) {
|
|
4041
4045
|
await sql`
|
|
4042
4046
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS dispatch_ack_timeout_ms
|
|
4043
4047
|
`.execute(db);
|
|
@@ -4045,8 +4049,8 @@ async function down$6(db) {
|
|
|
4045
4049
|
//#endregion
|
|
4046
4050
|
//#region src/db/migrations/033_org_settings_approval.ts
|
|
4047
4051
|
var _033_org_settings_approval_exports = /* @__PURE__ */ __exportAll({
|
|
4048
|
-
down: () => down$
|
|
4049
|
-
up: () => up$
|
|
4052
|
+
down: () => down$9,
|
|
4053
|
+
up: () => up$9
|
|
4050
4054
|
});
|
|
4051
4055
|
/**
|
|
4052
4056
|
* Add the two approval-policy columns to `org_settings`:
|
|
@@ -4063,7 +4067,7 @@ var _033_org_settings_approval_exports = /* @__PURE__ */ __exportAll({
|
|
|
4063
4067
|
* and the orchestrator admin route. Idempotent: a re-run on a DB that already
|
|
4064
4068
|
* has the columns is a no-op (each column is guarded independently).
|
|
4065
4069
|
*/
|
|
4066
|
-
async function up$
|
|
4070
|
+
async function up$9(db) {
|
|
4067
4071
|
const colExists = async (column) => {
|
|
4068
4072
|
return (await sql`
|
|
4069
4073
|
SELECT EXISTS (
|
|
@@ -4083,7 +4087,7 @@ async function up$5(db) {
|
|
|
4083
4087
|
ADD COLUMN allow_self_approval BOOLEAN NOT NULL DEFAULT true
|
|
4084
4088
|
`.execute(db);
|
|
4085
4089
|
}
|
|
4086
|
-
async function down$
|
|
4090
|
+
async function down$9(db) {
|
|
4087
4091
|
await sql`
|
|
4088
4092
|
ALTER TABLE public.org_settings DROP COLUMN IF EXISTS approval_expiry_seconds
|
|
4089
4093
|
`.execute(db);
|
|
@@ -4094,8 +4098,8 @@ async function down$5(db) {
|
|
|
4094
4098
|
//#endregion
|
|
4095
4099
|
//#region src/db/migrations/034_held_runs_generalize.ts
|
|
4096
4100
|
var _034_held_runs_generalize_exports = /* @__PURE__ */ __exportAll({
|
|
4097
|
-
down: () => down$
|
|
4098
|
-
up: () => up$
|
|
4101
|
+
down: () => down$8,
|
|
4102
|
+
up: () => up$8
|
|
4099
4103
|
});
|
|
4100
4104
|
/**
|
|
4101
4105
|
* Generalize `held_runs` from an environment-only hold into the unified
|
|
@@ -4117,7 +4121,7 @@ var _034_held_runs_generalize_exports = /* @__PURE__ */ __exportAll({
|
|
|
4117
4121
|
* New `held_run_approvals` table: one row per approver decision, FK to
|
|
4118
4122
|
* `held_runs.id` (uuid) with ON DELETE CASCADE.
|
|
4119
4123
|
*/
|
|
4120
|
-
async function up$
|
|
4124
|
+
async function up$8(db) {
|
|
4121
4125
|
const colExists = async (column) => {
|
|
4122
4126
|
return (await sql`
|
|
4123
4127
|
SELECT EXISTS (
|
|
@@ -4150,7 +4154,7 @@ async function up$4(db) {
|
|
|
4150
4154
|
ON public.held_run_approvals USING btree (held_run_id)
|
|
4151
4155
|
`.execute(db);
|
|
4152
4156
|
}
|
|
4153
|
-
async function down$
|
|
4157
|
+
async function down$8(db) {
|
|
4154
4158
|
await sql`DROP TABLE IF EXISTS public.held_run_approvals`.execute(db);
|
|
4155
4159
|
await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS approval_requirement`.execute(db);
|
|
4156
4160
|
await sql`ALTER TABLE public.held_runs DROP COLUMN IF EXISTS trigger_source`.execute(db);
|
|
@@ -4160,8 +4164,8 @@ async function down$4(db) {
|
|
|
4160
4164
|
//#endregion
|
|
4161
4165
|
//#region src/db/migrations/035_pending_workflow_contexts.ts
|
|
4162
4166
|
var _035_pending_workflow_contexts_exports = /* @__PURE__ */ __exportAll({
|
|
4163
|
-
down: () => down$
|
|
4164
|
-
up: () => up$
|
|
4167
|
+
down: () => down$7,
|
|
4168
|
+
up: () => up$7
|
|
4165
4169
|
});
|
|
4166
4170
|
/**
|
|
4167
4171
|
* Pending workflow dispatch context — backs resume of a workflow whose install
|
|
@@ -4170,7 +4174,7 @@ var _035_pending_workflow_contexts_exports = /* @__PURE__ */ __exportAll({
|
|
|
4170
4174
|
* wait-timer expiry, concurrency slot free). The row is deleted once the resume
|
|
4171
4175
|
* dispatch has been kicked off.
|
|
4172
4176
|
*/
|
|
4173
|
-
async function up$
|
|
4177
|
+
async function up$7(db) {
|
|
4174
4178
|
await sql`
|
|
4175
4179
|
CREATE TABLE IF NOT EXISTS public.pending_workflow_contexts (
|
|
4176
4180
|
run_id text PRIMARY KEY,
|
|
@@ -4180,14 +4184,14 @@ async function up$3(db) {
|
|
|
4180
4184
|
)
|
|
4181
4185
|
`.execute(db);
|
|
4182
4186
|
}
|
|
4183
|
-
async function down$
|
|
4187
|
+
async function down$7(db) {
|
|
4184
4188
|
await sql`DROP TABLE IF EXISTS public.pending_workflow_contexts`.execute(db);
|
|
4185
4189
|
}
|
|
4186
4190
|
//#endregion
|
|
4187
4191
|
//#region src/db/migrations/036_attestations.ts
|
|
4188
4192
|
var _036_attestations_exports = /* @__PURE__ */ __exportAll({
|
|
4189
|
-
down: () => down$
|
|
4190
|
-
up: () => up$
|
|
4193
|
+
down: () => down$6,
|
|
4194
|
+
up: () => up$6
|
|
4191
4195
|
});
|
|
4192
4196
|
/**
|
|
4193
4197
|
* Add the `attestations` table for build-provenance bundles.
|
|
@@ -4200,7 +4204,7 @@ var _036_attestations_exports = /* @__PURE__ */ __exportAll({
|
|
|
4200
4204
|
*
|
|
4201
4205
|
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
4202
4206
|
*/
|
|
4203
|
-
async function up$
|
|
4207
|
+
async function up$6(db) {
|
|
4204
4208
|
if ((await sql`
|
|
4205
4209
|
SELECT EXISTS (
|
|
4206
4210
|
SELECT 1 FROM information_schema.tables
|
|
@@ -4226,14 +4230,14 @@ async function up$2(db) {
|
|
|
4226
4230
|
ON public.attestations (run_id, job_id)
|
|
4227
4231
|
`.execute(db);
|
|
4228
4232
|
}
|
|
4229
|
-
async function down$
|
|
4233
|
+
async function down$6(db) {
|
|
4230
4234
|
await sql`DROP TABLE IF EXISTS public.attestations`.execute(db);
|
|
4231
4235
|
}
|
|
4232
4236
|
//#endregion
|
|
4233
4237
|
//#region src/db/migrations/037_generic_sources_provider_type_local.ts
|
|
4234
4238
|
var _037_generic_sources_provider_type_local_exports = /* @__PURE__ */ __exportAll({
|
|
4235
|
-
down: () => down$
|
|
4236
|
-
up: () => up$
|
|
4239
|
+
down: () => down$5,
|
|
4240
|
+
up: () => up$5
|
|
4237
4241
|
});
|
|
4238
4242
|
/**
|
|
4239
4243
|
* Replace the `generic_webhook_sources.provider_type` CHECK constraint so it
|
|
@@ -4250,7 +4254,7 @@ var _037_generic_sources_provider_type_local_exports = /* @__PURE__ */ __exportA
|
|
|
4250
4254
|
* Idempotent: the constraint is dropped IF EXISTS and recreated; the data
|
|
4251
4255
|
* backfill is a plain UPDATE that is a no-op once no `'internal'` rows remain.
|
|
4252
4256
|
*/
|
|
4253
|
-
async function up$
|
|
4257
|
+
async function up$5(db) {
|
|
4254
4258
|
await sql`
|
|
4255
4259
|
ALTER TABLE public.generic_webhook_sources
|
|
4256
4260
|
DROP CONSTRAINT IF EXISTS generic_webhook_sources_provider_type_check
|
|
@@ -4266,7 +4270,7 @@ async function up$1(db) {
|
|
|
4266
4270
|
CHECK (provider_type = ANY (ARRAY['generic'::text, 'local'::text]))
|
|
4267
4271
|
`.execute(db);
|
|
4268
4272
|
}
|
|
4269
|
-
async function down$
|
|
4273
|
+
async function down$5(db) {
|
|
4270
4274
|
await sql`
|
|
4271
4275
|
ALTER TABLE public.generic_webhook_sources
|
|
4272
4276
|
DROP CONSTRAINT IF EXISTS generic_webhook_sources_provider_type_check
|
|
@@ -4285,8 +4289,8 @@ async function down$1(db) {
|
|
|
4285
4289
|
//#endregion
|
|
4286
4290
|
//#region src/db/migrations/038_remote_sources.ts
|
|
4287
4291
|
var _038_remote_sources_exports = /* @__PURE__ */ __exportAll({
|
|
4288
|
-
down: () => down,
|
|
4289
|
-
up: () => up
|
|
4292
|
+
down: () => down$4,
|
|
4293
|
+
up: () => up$4
|
|
4290
4294
|
});
|
|
4291
4295
|
/**
|
|
4292
4296
|
* `remote_sources` anchors a Platform-relayed `kici run remote` to its real
|
|
@@ -4298,7 +4302,7 @@ var _038_remote_sources_exports = /* @__PURE__ */ __exportAll({
|
|
|
4298
4302
|
*
|
|
4299
4303
|
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
4300
4304
|
*/
|
|
4301
|
-
async function up(db) {
|
|
4305
|
+
async function up$4(db) {
|
|
4302
4306
|
if ((await sql`
|
|
4303
4307
|
SELECT EXISTS (
|
|
4304
4308
|
SELECT 1 FROM information_schema.tables
|
|
@@ -4317,10 +4321,200 @@ async function up(db) {
|
|
|
4317
4321
|
)
|
|
4318
4322
|
`.execute(db);
|
|
4319
4323
|
}
|
|
4320
|
-
async function down(db) {
|
|
4324
|
+
async function down$4(db) {
|
|
4321
4325
|
await sql`DROP TABLE IF EXISTS public.remote_sources`.execute(db);
|
|
4322
4326
|
}
|
|
4323
4327
|
//#endregion
|
|
4328
|
+
//#region src/db/migrations/039_host_roster.ts
|
|
4329
|
+
var _039_host_roster_exports = /* @__PURE__ */ __exportAll({
|
|
4330
|
+
down: () => down$3,
|
|
4331
|
+
up: () => up$3
|
|
4332
|
+
});
|
|
4333
|
+
/**
|
|
4334
|
+
* `host_roster` is KiCI's declared inventory: one durable row per agent the
|
|
4335
|
+
* cluster has ever enrolled, reconciled from the in-memory AgentRegistry on
|
|
4336
|
+
* every register/unregister. `lifecycle_class` (snapshot of the auth token's
|
|
4337
|
+
* agent_type) drives reaping — `ephemeral` rows are GC'd past their TTL,
|
|
4338
|
+
* `static` rows persist and read as `unreachable` when their heartbeat goes
|
|
4339
|
+
* stale. `connected_instance_id` records which orchestrator holds the live WS
|
|
4340
|
+
* (cluster liveness + the host-fanout reroute target); NULL = disconnected.
|
|
4341
|
+
*
|
|
4342
|
+
* The roster lives in the shared cluster DB (one table, all instances). Status
|
|
4343
|
+
* is derived at read from the shared `last_seen` + `connected_instance_id`, so
|
|
4344
|
+
* every instance agrees regardless of which one holds the agent's live WS.
|
|
4345
|
+
*
|
|
4346
|
+
* Idempotent: a re-run on a DB that already has the table is a no-op.
|
|
4347
|
+
*/
|
|
4348
|
+
async function up$3(db) {
|
|
4349
|
+
if ((await sql`
|
|
4350
|
+
SELECT EXISTS (
|
|
4351
|
+
SELECT 1 FROM information_schema.tables
|
|
4352
|
+
WHERE table_schema = 'public' AND table_name = 'host_roster'
|
|
4353
|
+
) AS exists
|
|
4354
|
+
`.execute(db)).rows[0]?.exists) return;
|
|
4355
|
+
await sql`
|
|
4356
|
+
CREATE TABLE public.host_roster (
|
|
4357
|
+
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
|
4358
|
+
agent_id text NOT NULL,
|
|
4359
|
+
token_id uuid,
|
|
4360
|
+
lifecycle_class text NOT NULL,
|
|
4361
|
+
labels text NOT NULL DEFAULT '[]',
|
|
4362
|
+
hostname text,
|
|
4363
|
+
platform text,
|
|
4364
|
+
arch text,
|
|
4365
|
+
connected_instance_id text,
|
|
4366
|
+
last_seen timestamptz NOT NULL DEFAULT now(),
|
|
4367
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
4368
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
4369
|
+
CONSTRAINT host_roster_pkey PRIMARY KEY (id),
|
|
4370
|
+
CONSTRAINT host_roster_agent_id_key UNIQUE (agent_id),
|
|
4371
|
+
CONSTRAINT host_roster_lifecycle_class_check
|
|
4372
|
+
CHECK (lifecycle_class = ANY (ARRAY['static'::text, 'ephemeral'::text]))
|
|
4373
|
+
)
|
|
4374
|
+
`.execute(db);
|
|
4375
|
+
await sql`CREATE INDEX idx_host_roster_reap
|
|
4376
|
+
ON public.host_roster (lifecycle_class, last_seen)`.execute(db);
|
|
4377
|
+
}
|
|
4378
|
+
async function down$3(db) {
|
|
4379
|
+
await sql`DROP TABLE IF EXISTS public.host_roster`.execute(db);
|
|
4380
|
+
}
|
|
4381
|
+
//#endregion
|
|
4382
|
+
//#region src/db/migrations/040_runsonall_pin.ts
|
|
4383
|
+
var _040_runsonall_pin_exports = /* @__PURE__ */ __exportAll({
|
|
4384
|
+
down: () => down$2,
|
|
4385
|
+
up: () => up$2
|
|
4386
|
+
});
|
|
4387
|
+
/**
|
|
4388
|
+
* Add the `runsOnAll` host fan-out columns:
|
|
4389
|
+
*
|
|
4390
|
+
* - `dispatch_queue.pinned_agent_id TEXT` — when set, the queued/dispatched job
|
|
4391
|
+
* targets exactly that agent (a host-fanout child). The dispatcher routes it
|
|
4392
|
+
* only to that agent; the queue drain never hands it to a different one.
|
|
4393
|
+
* - `execution_jobs.base_job_name` / `variant_kind` / `variant_label` — generic
|
|
4394
|
+
* fan-out columns (matrix + host uniform). `variant_kind` is `'matrix'` or
|
|
4395
|
+
* `'host'`; `variant_label` is the matrix suffix or the hostname. They make the
|
|
4396
|
+
* logical fan-out job first-class server-side so the dashboard groups on real
|
|
4397
|
+
* fields instead of string-parsing the job name. (`matrix_values` / `group_name`
|
|
4398
|
+
* already exist; the matrix path now also backfills `variant_kind='matrix'`.)
|
|
4399
|
+
*
|
|
4400
|
+
* Idempotent: re-running on a DB that already has any column is a no-op.
|
|
4401
|
+
*/
|
|
4402
|
+
async function colExists$2(db, table, name) {
|
|
4403
|
+
return (await sql`
|
|
4404
|
+
SELECT EXISTS (
|
|
4405
|
+
SELECT 1 FROM information_schema.columns
|
|
4406
|
+
WHERE table_schema = 'public'
|
|
4407
|
+
AND table_name = ${table}
|
|
4408
|
+
AND column_name = ${name}
|
|
4409
|
+
) AS exists
|
|
4410
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
4411
|
+
}
|
|
4412
|
+
async function up$2(db) {
|
|
4413
|
+
if (!await colExists$2(db, "dispatch_queue", "pinned_agent_id")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN pinned_agent_id TEXT`.execute(db);
|
|
4414
|
+
if (!await colExists$2(db, "execution_jobs", "base_job_name")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN base_job_name TEXT`.execute(db);
|
|
4415
|
+
if (!await colExists$2(db, "execution_jobs", "variant_kind")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_kind TEXT`.execute(db);
|
|
4416
|
+
if (!await colExists$2(db, "execution_jobs", "variant_label")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN variant_label TEXT`.execute(db);
|
|
4417
|
+
await sql`
|
|
4418
|
+
CREATE INDEX IF NOT EXISTS idx_dispatch_queue_pinned_agent
|
|
4419
|
+
ON public.dispatch_queue (pinned_agent_id)
|
|
4420
|
+
WHERE pinned_agent_id IS NOT NULL
|
|
4421
|
+
`.execute(db);
|
|
4422
|
+
}
|
|
4423
|
+
async function down$2(db) {
|
|
4424
|
+
await sql`DROP INDEX IF EXISTS public.idx_dispatch_queue_pinned_agent`.execute(db);
|
|
4425
|
+
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS pinned_agent_id`.execute(db);
|
|
4426
|
+
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS variant_label`.execute(db);
|
|
4427
|
+
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS variant_kind`.execute(db);
|
|
4428
|
+
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS base_job_name`.execute(db);
|
|
4429
|
+
}
|
|
4430
|
+
//#endregion
|
|
4431
|
+
//#region src/db/migrations/041_wave_gated.ts
|
|
4432
|
+
var _041_wave_gated_exports = /* @__PURE__ */ __exportAll({
|
|
4433
|
+
down: () => down$1,
|
|
4434
|
+
up: () => up$1
|
|
4435
|
+
});
|
|
4436
|
+
/**
|
|
4437
|
+
* Add the rolling fan-out wave-gate columns:
|
|
4438
|
+
*
|
|
4439
|
+
* - `execution_jobs.wave_gated boolean NOT NULL DEFAULT false` — when a fan-out
|
|
4440
|
+
* job declares `maxParallel`, children beyond the sliding window are persisted
|
|
4441
|
+
* `wave_gated=true` (held, not enqueued). The dispatch loop skips them; the
|
|
4442
|
+
* wave-scheduler clears the flag one-per-terminal as siblings complete (or, on
|
|
4443
|
+
* `failFast`, skips the held remainder).
|
|
4444
|
+
* - `execution_jobs.wave_max_parallel int` / `wave_fail_fast boolean` — the
|
|
4445
|
+
* base's wave policy, stamped on every fan-out child so the wave-scheduler can
|
|
4446
|
+
* read it at terminal time without re-fetching the lock file (the tracker has
|
|
4447
|
+
* no lock access). NULL for any job not part of a bounded wave.
|
|
4448
|
+
*
|
|
4449
|
+
* A composite index on (run_id, base_job_name, wave_gated) supports the
|
|
4450
|
+
* wave-scheduler's "next held sibling of this base" lookups.
|
|
4451
|
+
*
|
|
4452
|
+
* Idempotent: re-running on a DB that already has the columns is a no-op.
|
|
4453
|
+
*/
|
|
4454
|
+
async function colExists$1(db, table, name) {
|
|
4455
|
+
return (await sql`
|
|
4456
|
+
SELECT EXISTS (
|
|
4457
|
+
SELECT 1 FROM information_schema.columns
|
|
4458
|
+
WHERE table_schema = 'public'
|
|
4459
|
+
AND table_name = ${table}
|
|
4460
|
+
AND column_name = ${name}
|
|
4461
|
+
) AS exists
|
|
4462
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
4463
|
+
}
|
|
4464
|
+
async function up$1(db) {
|
|
4465
|
+
if (!await colExists$1(db, "execution_jobs", "wave_gated")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_gated boolean NOT NULL DEFAULT false`.execute(db);
|
|
4466
|
+
if (!await colExists$1(db, "execution_jobs", "wave_max_parallel")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_max_parallel integer`.execute(db);
|
|
4467
|
+
if (!await colExists$1(db, "execution_jobs", "wave_fail_fast")) await sql`ALTER TABLE public.execution_jobs ADD COLUMN wave_fail_fast boolean`.execute(db);
|
|
4468
|
+
await sql`
|
|
4469
|
+
CREATE INDEX IF NOT EXISTS idx_execution_jobs_wave
|
|
4470
|
+
ON public.execution_jobs (run_id, base_job_name, wave_gated)
|
|
4471
|
+
`.execute(db);
|
|
4472
|
+
}
|
|
4473
|
+
async function down$1(db) {
|
|
4474
|
+
await sql`DROP INDEX IF EXISTS public.idx_execution_jobs_wave`.execute(db);
|
|
4475
|
+
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS wave_fail_fast`.execute(db);
|
|
4476
|
+
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS wave_max_parallel`.execute(db);
|
|
4477
|
+
await sql`ALTER TABLE public.execution_jobs DROP COLUMN IF EXISTS wave_gated`.execute(db);
|
|
4478
|
+
}
|
|
4479
|
+
//#endregion
|
|
4480
|
+
//#region src/db/migrations/042_dispatch_queue_patterns.ts
|
|
4481
|
+
var _042_dispatch_queue_patterns_exports = /* @__PURE__ */ __exportAll({
|
|
4482
|
+
down: () => down,
|
|
4483
|
+
up: () => up
|
|
4484
|
+
});
|
|
4485
|
+
/**
|
|
4486
|
+
* Add pattern columns to dispatch_queue. Exact labels stay in runs_on_labels /
|
|
4487
|
+
* exclude_labels (the SQL @> prefilter); regex matchers go here and are applied
|
|
4488
|
+
* as a JS post-filter at drain time, since Postgres `~` regex semantics differ
|
|
4489
|
+
* from JavaScript `RegExp` and the engine's `matcherSatisfiedBy` is the single
|
|
4490
|
+
* matching authority.
|
|
4491
|
+
*
|
|
4492
|
+
* - `runs_on_patterns jsonb NOT NULL DEFAULT '[]'` — regex matchers the agent's
|
|
4493
|
+
* labels must satisfy.
|
|
4494
|
+
* - `exclude_patterns jsonb NOT NULL DEFAULT '[]'` — regex matchers that
|
|
4495
|
+
* disqualify an agent.
|
|
4496
|
+
*
|
|
4497
|
+
* Idempotent: re-running on a DB that already has the columns is a no-op.
|
|
4498
|
+
*/
|
|
4499
|
+
async function colExists(db, table, name) {
|
|
4500
|
+
return (await sql`
|
|
4501
|
+
SELECT EXISTS (
|
|
4502
|
+
SELECT 1 FROM information_schema.columns
|
|
4503
|
+
WHERE table_schema = 'public'
|
|
4504
|
+
AND table_name = ${table}
|
|
4505
|
+
AND column_name = ${name}
|
|
4506
|
+
) AS exists
|
|
4507
|
+
`.execute(db)).rows[0]?.exists ?? false;
|
|
4508
|
+
}
|
|
4509
|
+
async function up(db) {
|
|
4510
|
+
if (!await colExists(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);
|
|
4511
|
+
if (!await colExists(db, "dispatch_queue", "exclude_patterns")) await sql`ALTER TABLE public.dispatch_queue ADD COLUMN exclude_patterns jsonb NOT NULL DEFAULT '[]'::jsonb`.execute(db);
|
|
4512
|
+
}
|
|
4513
|
+
async function down(db) {
|
|
4514
|
+
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS exclude_patterns`.execute(db);
|
|
4515
|
+
await sql`ALTER TABLE public.dispatch_queue DROP COLUMN IF EXISTS runs_on_patterns`.execute(db);
|
|
4516
|
+
}
|
|
4517
|
+
//#endregion
|
|
4324
4518
|
//#region src/db/migration-provider.ts
|
|
4325
4519
|
function createMigrationProvider() {
|
|
4326
4520
|
return { async getMigrations() {
|
|
@@ -4362,7 +4556,11 @@ function createMigrationProvider() {
|
|
|
4362
4556
|
"035_pending_workflow_contexts": _035_pending_workflow_contexts_exports,
|
|
4363
4557
|
"036_attestations": _036_attestations_exports,
|
|
4364
4558
|
"037_generic_sources_provider_type_local": _037_generic_sources_provider_type_local_exports,
|
|
4365
|
-
"038_remote_sources": _038_remote_sources_exports
|
|
4559
|
+
"038_remote_sources": _038_remote_sources_exports,
|
|
4560
|
+
"039_host_roster": _039_host_roster_exports,
|
|
4561
|
+
"040_runsonall_pin": _040_runsonall_pin_exports,
|
|
4562
|
+
"041_wave_gated": _041_wave_gated_exports,
|
|
4563
|
+
"042_dispatch_queue_patterns": _042_dispatch_queue_patterns_exports
|
|
4366
4564
|
};
|
|
4367
4565
|
} };
|
|
4368
4566
|
}
|
|
@@ -6631,7 +6829,7 @@ function registerDebugBundleCommand(program, getClient) {
|
|
|
6631
6829
|
safeRequest(() => client.getText("/metrics"))
|
|
6632
6830
|
]);
|
|
6633
6831
|
const output = fs$1.createWriteStream(outputPath);
|
|
6634
|
-
const archive =
|
|
6832
|
+
const archive = new ZipArchive({ zlib: { level: 6 } });
|
|
6635
6833
|
const finalized = new Promise((resolve, reject) => {
|
|
6636
6834
|
output.on("close", resolve);
|
|
6637
6835
|
archive.on("error", reject);
|
|
@@ -6958,8 +7156,18 @@ function isRoot() {
|
|
|
6958
7156
|
*
|
|
6959
7157
|
* Used by the instance index (`<kiciRoot>/instances.json`) which lives
|
|
6960
7158
|
* outside any per-instance subdir, and as the base for {@link getConfigDir}.
|
|
7159
|
+
*
|
|
7160
|
+
* `KICI_CONFIG_ROOT` overrides the platform default for both privilege scopes.
|
|
7161
|
+
* It exists so a test harness (or a sandboxed run) can point the instance index
|
|
7162
|
+
* at an isolated directory instead of sharing the host's `~/.config/kici/` —
|
|
7163
|
+
* which otherwise lets concurrent installs clobber each other's index.
|
|
6961
7164
|
*/
|
|
6962
7165
|
function kiciConfigRoot(isUserLevel) {
|
|
7166
|
+
const override = process.env.KICI_CONFIG_ROOT;
|
|
7167
|
+
if (override) {
|
|
7168
|
+
const sep = os.platform() === "win32" ? "\\" : "/";
|
|
7169
|
+
return override.endsWith(sep) ? override : override + sep;
|
|
7170
|
+
}
|
|
6963
7171
|
const plat = os.platform();
|
|
6964
7172
|
if (plat === "win32") {
|
|
6965
7173
|
if (isUserLevel) return (process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local")) + "\\kici\\";
|
|
@@ -7107,6 +7315,37 @@ function readKiciVersion() {
|
|
|
7107
7315
|
return "unknown";
|
|
7108
7316
|
}
|
|
7109
7317
|
}
|
|
7318
|
+
/**
|
|
7319
|
+
* Resolve the `@kici-dev/<component>` package version that a {@link LaunchSpec}
|
|
7320
|
+
* will actually execute, by locating the component's entry script among the
|
|
7321
|
+
* spec's tokens and reading the version from the package.json that owns it.
|
|
7322
|
+
*
|
|
7323
|
+
* Returns null when no resolvable entry script is present (e.g. an opaque
|
|
7324
|
+
* `--binary` install) or the owning package.json is missing / name-mismatched.
|
|
7325
|
+
* This is what lets the npm-source upgrade refuse to report a version it can't
|
|
7326
|
+
* stand behind.
|
|
7327
|
+
*/
|
|
7328
|
+
function resolveVersionFromLaunchSpec(spec, component) {
|
|
7329
|
+
const pkgName = `@kici-dev/${component}`;
|
|
7330
|
+
const entryRe = new RegExp(`[/\\\\]@kici-dev[/\\\\]${component}[/\\\\]dist[/\\\\](?:server|standalone)\\.js$`);
|
|
7331
|
+
const entry = [spec.execPath, ...spec.args].find((a) => entryRe.test(a));
|
|
7332
|
+
if (!entry) return null;
|
|
7333
|
+
let dir = path.dirname(entry);
|
|
7334
|
+
for (let i = 0; i < 6; i++) {
|
|
7335
|
+
const candidate = path.join(dir, "package.json");
|
|
7336
|
+
if (fs.existsSync(candidate)) try {
|
|
7337
|
+
const pkg = JSON.parse(fs.readFileSync(candidate, "utf-8"));
|
|
7338
|
+
if (pkg.name === pkgName && typeof pkg.version === "string") return pkg.version;
|
|
7339
|
+
return null;
|
|
7340
|
+
} catch {
|
|
7341
|
+
return null;
|
|
7342
|
+
}
|
|
7343
|
+
const parent = path.dirname(dir);
|
|
7344
|
+
if (parent === dir) break;
|
|
7345
|
+
dir = parent;
|
|
7346
|
+
}
|
|
7347
|
+
return null;
|
|
7348
|
+
}
|
|
7110
7349
|
//#endregion
|
|
7111
7350
|
//#region src/cli/service/instance/index-file.ts
|
|
7112
7351
|
/**
|
|
@@ -7184,27 +7423,54 @@ function removeIndexEntry(kiciRoot, key) {
|
|
|
7184
7423
|
*/
|
|
7185
7424
|
/**
|
|
7186
7425
|
* Reconcile <kiciRoot>/instances.json with the driver's native scan, then
|
|
7187
|
-
* rewrite the index
|
|
7188
|
-
*
|
|
7426
|
+
* rewrite the index so it mirrors the scanned instances that have a known
|
|
7427
|
+
* instanceDir. Two self-heal directions happen here:
|
|
7428
|
+
*
|
|
7429
|
+
* - backward: drop index entries whose unit no longer exists.
|
|
7430
|
+
* - forward: adopt an instanceDir the driver recovered from the unit marker
|
|
7431
|
+
* when the index entry is missing or carries no dir — so a lost or emptied
|
|
7432
|
+
* index rebuilds itself from the init system on the next read.
|
|
7433
|
+
*
|
|
7434
|
+
* The init system is therefore the source of truth for the name→folder mapping
|
|
7435
|
+
* (via the X-KiCI-InstanceDir / KiCIInstanceDir / dev.kici.instance-dir / [KiCI-DIR]
|
|
7436
|
+
* markers), and the index is a pure cache. Returns the merged list filtered to
|
|
7437
|
+
* the requested component + isUserLevel.
|
|
7189
7438
|
*/
|
|
7190
7439
|
async function listInstances(args) {
|
|
7191
7440
|
const { component, isUserLevel, kiciRoot, manager } = args;
|
|
7192
7441
|
const scanForComponent = (await manager.list(isUserLevel)).filter((s) => s.component === component);
|
|
7193
|
-
const scanNames = new Set(scanForComponent.map((s) => s.name));
|
|
7194
7442
|
const index = readIndex(kiciRoot);
|
|
7195
7443
|
const relevantIndex = index.filter((e) => e.component === component && e.isUserLevel === isUserLevel);
|
|
7196
|
-
const
|
|
7197
|
-
|
|
7198
|
-
const indexByName = new Map(survivors.map((e) => [e.name, e]));
|
|
7199
|
-
return scanForComponent.map((s) => {
|
|
7444
|
+
const indexByName = new Map(relevantIndex.map((e) => [e.name, e]));
|
|
7445
|
+
const listed = scanForComponent.map((s) => {
|
|
7200
7446
|
const idx = indexByName.get(s.name);
|
|
7201
7447
|
return {
|
|
7202
7448
|
...s,
|
|
7203
7449
|
component,
|
|
7204
|
-
instanceDir: idx?.instanceDir,
|
|
7450
|
+
instanceDir: idx?.instanceDir ?? s.instanceDir,
|
|
7205
7451
|
source: idx ? "index+scan" : "scan"
|
|
7206
7452
|
};
|
|
7207
7453
|
});
|
|
7454
|
+
const reconciled = [];
|
|
7455
|
+
for (const l of listed) {
|
|
7456
|
+
if (!l.instanceDir) continue;
|
|
7457
|
+
reconciled.push({
|
|
7458
|
+
component,
|
|
7459
|
+
name: l.name,
|
|
7460
|
+
platform: l.platform,
|
|
7461
|
+
isUserLevel,
|
|
7462
|
+
instanceDir: l.instanceDir
|
|
7463
|
+
});
|
|
7464
|
+
}
|
|
7465
|
+
if (!sameIndexSet(relevantIndex, reconciled)) writeIndex(kiciRoot, [...index.filter((e) => !(e.component === component && e.isUserLevel === isUserLevel)), ...reconciled]);
|
|
7466
|
+
return listed;
|
|
7467
|
+
}
|
|
7468
|
+
/** Order-insensitive equality of two index-entry sets (by their full identity). */
|
|
7469
|
+
function sameIndexSet(a, b) {
|
|
7470
|
+
if (a.length !== b.length) return false;
|
|
7471
|
+
const key = (e) => `${e.component}\0${e.name}\0${e.platform}\0${e.isUserLevel}\0${e.instanceDir}`;
|
|
7472
|
+
const setA = new Set(a.map(key));
|
|
7473
|
+
return b.every((e) => setA.has(key(e)));
|
|
7208
7474
|
}
|
|
7209
7475
|
/**
|
|
7210
7476
|
* Resolve the target instance for the current lifecycle invocation.
|
|
@@ -7326,6 +7592,7 @@ var init_systemd = __esmMin((() => {
|
|
|
7326
7592
|
lines.push("[Unit]");
|
|
7327
7593
|
lines.push(`Description=${config.description}`);
|
|
7328
7594
|
if (config.component) lines.push(`X-KiCI-Component=${config.component}`);
|
|
7595
|
+
if (config.instanceDir) lines.push(`X-KiCI-InstanceDir=${config.instanceDir}`);
|
|
7329
7596
|
lines.push("After=network.target postgresql.service");
|
|
7330
7597
|
lines.push("");
|
|
7331
7598
|
lines.push("[Service]");
|
|
@@ -7462,6 +7729,22 @@ var init_systemd = __esmMin((() => {
|
|
|
7462
7729
|
async isInstalled(config) {
|
|
7463
7730
|
return fs.existsSync(this.unitFilePath(config));
|
|
7464
7731
|
}
|
|
7732
|
+
async readLaunchSpec(config) {
|
|
7733
|
+
let content;
|
|
7734
|
+
try {
|
|
7735
|
+
content = fs.readFileSync(this.unitFilePath(config), "utf-8");
|
|
7736
|
+
} catch {
|
|
7737
|
+
return null;
|
|
7738
|
+
}
|
|
7739
|
+
const m = content.match(/^ExecStart=(.+?)\s*$/m);
|
|
7740
|
+
if (!m) return null;
|
|
7741
|
+
const tokens = m[1].trim().split(/\s+/).filter(Boolean);
|
|
7742
|
+
if (tokens.length === 0) return null;
|
|
7743
|
+
return {
|
|
7744
|
+
execPath: tokens[0],
|
|
7745
|
+
args: tokens.slice(1)
|
|
7746
|
+
};
|
|
7747
|
+
}
|
|
7465
7748
|
async list(isUserLevel) {
|
|
7466
7749
|
const unitDir = isUserLevel ? path.join(os.homedir(), ".config", "systemd", "user") : "/etc/systemd/system";
|
|
7467
7750
|
if (!fs.existsSync(unitDir)) return [];
|
|
@@ -7469,13 +7752,16 @@ var init_systemd = __esmMin((() => {
|
|
|
7469
7752
|
for (const entry of fs.readdirSync(unitDir)) {
|
|
7470
7753
|
if (typeof entry !== "string") continue;
|
|
7471
7754
|
if (!entry.startsWith("kici-") || !entry.endsWith(".service")) continue;
|
|
7472
|
-
const
|
|
7755
|
+
const content = fs.readFileSync(path.join(unitDir, entry), "utf-8");
|
|
7756
|
+
const m = content.match(/^X-KiCI-Component=(orchestrator|agent)\s*$/m);
|
|
7473
7757
|
if (!m) continue;
|
|
7758
|
+
const dirMatch = content.match(/^X-KiCI-InstanceDir=(.+?)\s*$/m);
|
|
7474
7759
|
out.push({
|
|
7475
7760
|
name: entry.replace(/\.service$/, ""),
|
|
7476
7761
|
platform: "systemd",
|
|
7477
7762
|
isUserLevel,
|
|
7478
|
-
component: m[1]
|
|
7763
|
+
component: m[1],
|
|
7764
|
+
instanceDir: dirMatch?.[1]
|
|
7479
7765
|
});
|
|
7480
7766
|
}
|
|
7481
7767
|
return out;
|
|
@@ -7534,6 +7820,10 @@ var init_launchd = __esmMin((() => {
|
|
|
7534
7820
|
escapeXml(s) {
|
|
7535
7821
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
7536
7822
|
}
|
|
7823
|
+
/** Inverse of {@link escapeXml} — decode the entities back to literals. */
|
|
7824
|
+
unescapeXml(s) {
|
|
7825
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/&/g, "&");
|
|
7826
|
+
}
|
|
7537
7827
|
/**
|
|
7538
7828
|
* Generate a launchd plist XML string from a service config.
|
|
7539
7829
|
* Visible for testing.
|
|
@@ -7553,6 +7843,10 @@ var init_launchd = __esmMin((() => {
|
|
|
7553
7843
|
lines.push(" <key>KiCIComponent</key>");
|
|
7554
7844
|
lines.push(` <string>${config.component}</string>`);
|
|
7555
7845
|
}
|
|
7846
|
+
if (config.instanceDir) {
|
|
7847
|
+
lines.push(" <key>KiCIInstanceDir</key>");
|
|
7848
|
+
lines.push(` <string>${this.escapeXml(config.instanceDir)}</string>`);
|
|
7849
|
+
}
|
|
7556
7850
|
lines.push(" <key>ProgramArguments</key>");
|
|
7557
7851
|
lines.push(" <array>");
|
|
7558
7852
|
lines.push(` <string>${this.escapeXml(config.executablePath)}</string>`);
|
|
@@ -7772,15 +8066,33 @@ var init_launchd = __esmMin((() => {
|
|
|
7772
8066
|
}
|
|
7773
8067
|
const match = content.match(/<key>KiCIComponent<\/key>\s*<string>(orchestrator|agent)<\/string>/);
|
|
7774
8068
|
if (!match) continue;
|
|
8069
|
+
const dirMatch = content.match(/<key>KiCIInstanceDir<\/key>\s*<string>([^<]+)<\/string>/);
|
|
7775
8070
|
out.push({
|
|
7776
8071
|
name: entry.replace(/\.plist$/, ""),
|
|
7777
8072
|
platform: "launchd",
|
|
7778
8073
|
isUserLevel,
|
|
7779
|
-
component: match[1]
|
|
8074
|
+
component: match[1],
|
|
8075
|
+
instanceDir: dirMatch ? this.unescapeXml(dirMatch[1]) : void 0
|
|
7780
8076
|
});
|
|
7781
8077
|
}
|
|
7782
8078
|
return out;
|
|
7783
8079
|
}
|
|
8080
|
+
async readLaunchSpec(config) {
|
|
8081
|
+
let content;
|
|
8082
|
+
try {
|
|
8083
|
+
content = fs.readFileSync(this.plistPath(config), "utf-8");
|
|
8084
|
+
} catch {
|
|
8085
|
+
return null;
|
|
8086
|
+
}
|
|
8087
|
+
const arr = content.match(/<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/);
|
|
8088
|
+
if (!arr) return null;
|
|
8089
|
+
const strings = [...arr[1].matchAll(/<string>([\s\S]*?)<\/string>/g)].map((s) => this.unescapeXml(s[1]));
|
|
8090
|
+
if (strings.length === 0) return null;
|
|
8091
|
+
return {
|
|
8092
|
+
execPath: strings[0],
|
|
8093
|
+
args: strings.slice(1)
|
|
8094
|
+
};
|
|
8095
|
+
}
|
|
7784
8096
|
};
|
|
7785
8097
|
}));
|
|
7786
8098
|
//#endregion
|
|
@@ -7915,61 +8227,61 @@ var init_registry = __esmMin((() => {
|
|
|
7915
8227
|
rolldown: {
|
|
7916
8228
|
"linux-x64": {
|
|
7917
8229
|
name: "rolldown",
|
|
7918
|
-
version: "1.1.
|
|
8230
|
+
version: "1.1.1",
|
|
7919
8231
|
platform: "linux",
|
|
7920
8232
|
arch: "x64",
|
|
7921
|
-
url: "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.
|
|
7922
|
-
sha256: "
|
|
8233
|
+
url: "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.1.tgz",
|
|
8234
|
+
sha256: "55bf455e4d6c5df19636c40b434718891a574214bbf09a50e2e7b21b9f640d56",
|
|
7923
8235
|
extractPath: "package/",
|
|
7924
8236
|
archiveType: "tar.gz"
|
|
7925
8237
|
},
|
|
7926
8238
|
"linux-arm64": {
|
|
7927
8239
|
name: "rolldown",
|
|
7928
|
-
version: "1.1.
|
|
8240
|
+
version: "1.1.1",
|
|
7929
8241
|
platform: "linux",
|
|
7930
8242
|
arch: "arm64",
|
|
7931
|
-
url: "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.
|
|
7932
|
-
sha256: "
|
|
8243
|
+
url: "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.1.tgz",
|
|
8244
|
+
sha256: "af458cfd2ce237a5161bdfdcef960e710c394b966537e1d1374840fb7fc78308",
|
|
7933
8245
|
extractPath: "package/",
|
|
7934
8246
|
archiveType: "tar.gz"
|
|
7935
8247
|
},
|
|
7936
8248
|
"darwin-x64": {
|
|
7937
8249
|
name: "rolldown",
|
|
7938
|
-
version: "1.1.
|
|
8250
|
+
version: "1.1.1",
|
|
7939
8251
|
platform: "darwin",
|
|
7940
8252
|
arch: "x64",
|
|
7941
|
-
url: "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.
|
|
7942
|
-
sha256: "
|
|
8253
|
+
url: "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.1.tgz",
|
|
8254
|
+
sha256: "31bd12457aa41646dcee674d7fa73ec2c4625ef5d34d0ef6c6823cad811de3e2",
|
|
7943
8255
|
extractPath: "package/",
|
|
7944
8256
|
archiveType: "tar.gz"
|
|
7945
8257
|
},
|
|
7946
8258
|
"darwin-arm64": {
|
|
7947
8259
|
name: "rolldown",
|
|
7948
|
-
version: "1.1.
|
|
8260
|
+
version: "1.1.1",
|
|
7949
8261
|
platform: "darwin",
|
|
7950
8262
|
arch: "arm64",
|
|
7951
|
-
url: "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.
|
|
7952
|
-
sha256: "
|
|
8263
|
+
url: "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.1.tgz",
|
|
8264
|
+
sha256: "8399e7a87640ffe485ba16289f6c3a7ab69b25d09b43e5896441372bcf6a02f9",
|
|
7953
8265
|
extractPath: "package/",
|
|
7954
8266
|
archiveType: "tar.gz"
|
|
7955
8267
|
},
|
|
7956
8268
|
"win32-x64": {
|
|
7957
8269
|
name: "rolldown",
|
|
7958
|
-
version: "1.1.
|
|
8270
|
+
version: "1.1.1",
|
|
7959
8271
|
platform: "win32",
|
|
7960
8272
|
arch: "x64",
|
|
7961
|
-
url: "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.
|
|
7962
|
-
sha256: "
|
|
8273
|
+
url: "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.1.tgz",
|
|
8274
|
+
sha256: "2b2c34d530db0e2cf15cdbe328822d5d385b19ee3b5aba55771587027b9afa17",
|
|
7963
8275
|
extractPath: "package/",
|
|
7964
8276
|
archiveType: "tar.gz"
|
|
7965
8277
|
},
|
|
7966
8278
|
"win32-arm64": {
|
|
7967
8279
|
name: "rolldown",
|
|
7968
|
-
version: "1.1.
|
|
8280
|
+
version: "1.1.1",
|
|
7969
8281
|
platform: "win32",
|
|
7970
8282
|
arch: "arm64",
|
|
7971
|
-
url: "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.
|
|
7972
|
-
sha256: "
|
|
8283
|
+
url: "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.1.tgz",
|
|
8284
|
+
sha256: "d7431cb75d0bed42dd8794b89c3d73ac7129a36465ac020519726f2a9dca1d7c",
|
|
7973
8285
|
extractPath: "package/",
|
|
7974
8286
|
archiveType: "tar.gz"
|
|
7975
8287
|
}
|
|
@@ -8078,7 +8390,8 @@ var init_windows = __esmMin((() => {
|
|
|
8078
8390
|
cmdParts.push("--", `"${config.executablePath}"`);
|
|
8079
8391
|
for (const arg of config.args ?? []) cmdParts.push(`"${arg}"`);
|
|
8080
8392
|
execSync(cmdParts.join(" "), { stdio: "pipe" });
|
|
8081
|
-
const
|
|
8393
|
+
const dirSuffix = config.component && config.instanceDir ? ` [KiCI-DIR:${config.instanceDir}]` : "";
|
|
8394
|
+
const descText = config.component ? `[KiCI:${config.component}] ${config.description}${dirSuffix}` : config.description;
|
|
8082
8395
|
execSync(`sc.exe description ${config.name} "${descText.replace(/"/g, "\\\"")}"`, { stdio: "pipe" });
|
|
8083
8396
|
execSync(`sc.exe config ${config.name} start= auto`, { stdio: "pipe" });
|
|
8084
8397
|
const actions = config.restartPolicy.delays.map((d) => `restart/${d * 1e3}`).join("/");
|
|
@@ -8189,18 +8502,39 @@ var init_windows = __esmMin((() => {
|
|
|
8189
8502
|
for (const row of rows) {
|
|
8190
8503
|
if (!row || typeof row !== "object") continue;
|
|
8191
8504
|
const r = row;
|
|
8192
|
-
const
|
|
8505
|
+
const desc = typeof r.Description === "string" ? r.Description : "";
|
|
8506
|
+
const match = desc.match(/^\[KiCI:(orchestrator|agent)\]/);
|
|
8193
8507
|
if (!match) continue;
|
|
8194
8508
|
if (typeof r.Name !== "string") continue;
|
|
8509
|
+
const dirMatch = desc.match(/\[KiCI-DIR:([^\]]+)\]/);
|
|
8195
8510
|
out.push({
|
|
8196
8511
|
name: r.Name,
|
|
8197
8512
|
platform: "windows",
|
|
8198
8513
|
isUserLevel,
|
|
8199
|
-
component: match[1]
|
|
8514
|
+
component: match[1],
|
|
8515
|
+
instanceDir: dirMatch?.[1]
|
|
8200
8516
|
});
|
|
8201
8517
|
}
|
|
8202
8518
|
return out;
|
|
8203
8519
|
}
|
|
8520
|
+
async readLaunchSpec(config) {
|
|
8521
|
+
let raw;
|
|
8522
|
+
try {
|
|
8523
|
+
raw = execSync(`sc.exe qc ${config.name}`, { stdio: "pipe" }).toString();
|
|
8524
|
+
} catch {
|
|
8525
|
+
return null;
|
|
8526
|
+
}
|
|
8527
|
+
const m = raw.match(/BINARY_PATH_NAME\s*:\s*(.+)/);
|
|
8528
|
+
if (!m) return null;
|
|
8529
|
+
const sep = m[1].indexOf(" -- ");
|
|
8530
|
+
if (sep === -1) return null;
|
|
8531
|
+
const tokens = [...m[1].slice(sep + 4).matchAll(/"([^"]*)"|(\S+)/g)].map((t) => t[1] ?? t[2]);
|
|
8532
|
+
if (tokens.length === 0 || !tokens[0]) return null;
|
|
8533
|
+
return {
|
|
8534
|
+
execPath: tokens[0],
|
|
8535
|
+
args: tokens.slice(1)
|
|
8536
|
+
};
|
|
8537
|
+
}
|
|
8204
8538
|
};
|
|
8205
8539
|
}));
|
|
8206
8540
|
//#endregion
|
|
@@ -8341,6 +8675,7 @@ function generateComposeYaml(config) {
|
|
|
8341
8675
|
if (config.component) {
|
|
8342
8676
|
lines.push(" labels:");
|
|
8343
8677
|
lines.push(` dev.kici.component: ${config.component}`);
|
|
8678
|
+
if (config.instanceDir) lines.push(` dev.kici.instance-dir: "${config.instanceDir}"`);
|
|
8344
8679
|
}
|
|
8345
8680
|
return lines.join("\n") + "\n";
|
|
8346
8681
|
}
|
|
@@ -8433,19 +8768,25 @@ var init_compose = __esmMin((() => {
|
|
|
8433
8768
|
}
|
|
8434
8769
|
if (!row || typeof row !== "object") continue;
|
|
8435
8770
|
const r = row;
|
|
8436
|
-
const
|
|
8771
|
+
const labels = typeof r.Labels === "string" ? r.Labels : "";
|
|
8772
|
+
const m = labels.match(/dev\.kici\.component=(orchestrator|agent)/);
|
|
8437
8773
|
if (!m) continue;
|
|
8438
8774
|
const name = typeof r.Names === "string" ? r.Names : String(r.Names ?? "");
|
|
8439
8775
|
if (!name) continue;
|
|
8776
|
+
const dirMatch = labels.match(/dev\.kici\.instance-dir=([^,]+)/);
|
|
8440
8777
|
out.push({
|
|
8441
8778
|
name,
|
|
8442
8779
|
platform: "compose",
|
|
8443
8780
|
isUserLevel,
|
|
8444
|
-
component: m[1]
|
|
8781
|
+
component: m[1],
|
|
8782
|
+
instanceDir: dirMatch?.[1]
|
|
8445
8783
|
});
|
|
8446
8784
|
}
|
|
8447
8785
|
return out;
|
|
8448
8786
|
}
|
|
8787
|
+
async readLaunchSpec(_config) {
|
|
8788
|
+
return null;
|
|
8789
|
+
}
|
|
8449
8790
|
};
|
|
8450
8791
|
}));
|
|
8451
8792
|
//#endregion
|
|
@@ -8567,7 +8908,8 @@ async function resolveUpgradeTarget(args) {
|
|
|
8567
8908
|
maxRetries: 5,
|
|
8568
8909
|
windowSeconds: 300
|
|
8569
8910
|
},
|
|
8570
|
-
component
|
|
8911
|
+
component,
|
|
8912
|
+
instanceDir: resolved.instanceDir
|
|
8571
8913
|
},
|
|
8572
8914
|
installBase: resolved.manifest.installBase,
|
|
8573
8915
|
resolvedInstance: resolved
|
|
@@ -8721,6 +9063,36 @@ function resolveNpmSourceVersion(opts) {
|
|
|
8721
9063
|
if (opts.requested && opts.requested !== opts.running) throw new Error(`npm-source upgrade: requested --version ${opts.requested} does not match the installed package version ${opts.running}. Run \`npm install -g @kici-dev/<pkg>@${opts.requested}\` first, then re-run the upgrade.`);
|
|
8722
9064
|
return opts.running;
|
|
8723
9065
|
}
|
|
9066
|
+
/**
|
|
9067
|
+
* Decide whether an npm-source upgrade may proceed, given the version the
|
|
9068
|
+
* invoking CLI is (`invoked`) and the version the installed unit will actually
|
|
9069
|
+
* launch (`launched`, or null when unresolvable — e.g. an opaque --binary
|
|
9070
|
+
* install). On success `manifestVersion` is the value to persist (null = leave
|
|
9071
|
+
* the manifest's kiciVersion unchanged because we couldn't verify it).
|
|
9072
|
+
*/
|
|
9073
|
+
function verifyNpmSourceLaunch(opts) {
|
|
9074
|
+
const { component, invoked, launched, launchedPath, force } = opts;
|
|
9075
|
+
if (launched === null) {
|
|
9076
|
+
if (force) return {
|
|
9077
|
+
ok: true,
|
|
9078
|
+
version: invoked,
|
|
9079
|
+
manifestVersion: null
|
|
9080
|
+
};
|
|
9081
|
+
return {
|
|
9082
|
+
ok: false,
|
|
9083
|
+
reason: "npm-source upgrade aborted: could not determine the version the installed unit will launch (custom --binary install or unparseable launch target). Pass --force to restart without version verification (the manifest version is left unchanged)."
|
|
9084
|
+
};
|
|
9085
|
+
}
|
|
9086
|
+
if (launched !== invoked) return {
|
|
9087
|
+
ok: false,
|
|
9088
|
+
reason: `npm-source upgrade aborted: this kici-admin is version ${invoked}, but the installed unit will launch version ${launched}${launchedPath ? ` (via ${launchedPath})` : ""}. Your \`npm install -g\` updated a different install than the service is pinned to. Install ${invoked} under the unit's runtime (the node its ExecStart points at), or re-run \`kici-admin ${component} install\` to repoint the unit, then retry.`
|
|
9089
|
+
};
|
|
9090
|
+
return {
|
|
9091
|
+
ok: true,
|
|
9092
|
+
version: launched,
|
|
9093
|
+
manifestVersion: launched
|
|
9094
|
+
};
|
|
9095
|
+
}
|
|
8724
9096
|
async function performVersionedUpgrade(component, opts) {
|
|
8725
9097
|
try {
|
|
8726
9098
|
const platform = detectPlatform(opts.platform);
|
|
@@ -8750,7 +9122,7 @@ async function performVersionedUpgrade(component, opts) {
|
|
|
8750
9122
|
return;
|
|
8751
9123
|
}
|
|
8752
9124
|
if (!opts.from && !opts.url) {
|
|
8753
|
-
await performNpmSourceUpgrade(config, resolvedInstance, manager, opts);
|
|
9125
|
+
await performNpmSourceUpgrade(component, config, resolvedInstance, manager, opts);
|
|
8754
9126
|
return;
|
|
8755
9127
|
}
|
|
8756
9128
|
if (!opts.version) {
|
|
@@ -8859,18 +9231,30 @@ async function performVersionedUpgrade(component, opts) {
|
|
|
8859
9231
|
* package (see install.ts → import.meta.resolve), so a stop+start re-executes
|
|
8860
9232
|
* the freshly-installed code. No archive, no versioned dir, no symlink.
|
|
8861
9233
|
*/
|
|
8862
|
-
async function performNpmSourceUpgrade(config, resolvedInstance, manager, opts) {
|
|
8863
|
-
const
|
|
8864
|
-
|
|
9234
|
+
async function performNpmSourceUpgrade(component, config, resolvedInstance, manager, opts) {
|
|
9235
|
+
const invoked = readKiciVersion();
|
|
9236
|
+
resolveNpmSourceVersion({
|
|
8865
9237
|
requested: opts.version,
|
|
8866
|
-
running
|
|
9238
|
+
running: invoked
|
|
8867
9239
|
});
|
|
8868
9240
|
if (!await manager.isInstalled(config)) {
|
|
8869
9241
|
console.error(`Error: service "${config.name}" is not installed`);
|
|
8870
9242
|
process.exit(1);
|
|
8871
9243
|
}
|
|
9244
|
+
const spec = await manager.readLaunchSpec(config);
|
|
9245
|
+
const verdict = verifyNpmSourceLaunch({
|
|
9246
|
+
component,
|
|
9247
|
+
invoked,
|
|
9248
|
+
launched: spec ? resolveVersionFromLaunchSpec(spec, component) : null,
|
|
9249
|
+
launchedPath: spec?.execPath ?? null,
|
|
9250
|
+
force: opts.force ?? false
|
|
9251
|
+
});
|
|
9252
|
+
if (!verdict.ok) {
|
|
9253
|
+
console.error(`Error: ${verdict.reason}`);
|
|
9254
|
+
process.exit(1);
|
|
9255
|
+
}
|
|
8872
9256
|
if (!opts.yes) {
|
|
8873
|
-
console.log(`This will restart "${config.name}" onto the npm-installed version ${version}.`);
|
|
9257
|
+
console.log(`This will restart "${config.name}" onto the npm-installed version ${verdict.version}.`);
|
|
8874
9258
|
console.log(" The service will be stopped briefly during the restart.");
|
|
8875
9259
|
console.log("");
|
|
8876
9260
|
if (!await confirm$2("Proceed with upgrade?")) {
|
|
@@ -8881,11 +9265,12 @@ async function performNpmSourceUpgrade(config, resolvedInstance, manager, opts)
|
|
|
8881
9265
|
console.log("Restarting service onto the npm-installed package...");
|
|
8882
9266
|
if ((await manager.status(config)).state === "running") await manager.stop(config);
|
|
8883
9267
|
await manager.start(config);
|
|
8884
|
-
writeManifest(resolvedInstance.instanceDir, {
|
|
9268
|
+
if (verdict.manifestVersion !== null) writeManifest(resolvedInstance.instanceDir, {
|
|
8885
9269
|
...resolvedInstance.manifest,
|
|
8886
|
-
kiciVersion:
|
|
9270
|
+
kiciVersion: verdict.manifestVersion
|
|
8887
9271
|
});
|
|
8888
|
-
console.log(
|
|
9272
|
+
else console.log("Note: manifest version left unchanged (verification was skipped via --force).");
|
|
9273
|
+
console.log(`Upgrade complete: service "${config.name}" is now running version ${verdict.version}.`);
|
|
8889
9274
|
}
|
|
8890
9275
|
/**
|
|
8891
9276
|
* Handle --rollback: switch symlink to the previous version and restart.
|
|
@@ -9235,7 +9620,7 @@ function registerOrchestratorInstall(orchestrator) {
|
|
|
9235
9620
|
})).find((c) => c.name === serviceName);
|
|
9236
9621
|
if (existing && existing.instanceDir !== instanceDir && !opts.force) {
|
|
9237
9622
|
const at = existing.instanceDir ?? "(no manifest)";
|
|
9238
|
-
console.error(`Error: an orchestrator instance "${serviceName}" is already installed at ${at}.
|
|
9623
|
+
console.error(`Error: an orchestrator instance "${serviceName}" is already installed at ${at}.\n\nUpgrading this service? Don't re-run install — installing again is for first-time\nsetup, not upgrades. For an npm-global install run \`npm install -g kici-admin@latest\`\nthen \`kici-admin orchestrator restart\`; for a versioned-directory install run\n\`kici-admin orchestrator upgrade\`.\n\nInstalling a second, separate instance? Pass a different --name or --instance-dir,\nor --force to take over this one.`);
|
|
9239
9624
|
process.exit(1);
|
|
9240
9625
|
}
|
|
9241
9626
|
const configDir = getConfigDir(serviceName, userLevel);
|
|
@@ -9304,6 +9689,7 @@ function registerOrchestratorInstall(orchestrator) {
|
|
|
9304
9689
|
isUserLevel: userLevel,
|
|
9305
9690
|
user: opts.user,
|
|
9306
9691
|
component: "orchestrator",
|
|
9692
|
+
instanceDir,
|
|
9307
9693
|
restartPolicy: DEFAULT_RESTART_POLICY
|
|
9308
9694
|
};
|
|
9309
9695
|
await manager.install(config);
|
|
@@ -9810,6 +10196,7 @@ function registerAgentInstall(agent) {
|
|
|
9810
10196
|
workingDirectory: configDir,
|
|
9811
10197
|
isUserLevel: userLevel,
|
|
9812
10198
|
component: "agent",
|
|
10199
|
+
instanceDir,
|
|
9813
10200
|
restartPolicy: DEFAULT_RESTART_POLICY
|
|
9814
10201
|
};
|
|
9815
10202
|
await manager.install(config);
|
|
@@ -10602,6 +10989,211 @@ function registerPeerCommands(program, _getClient) {
|
|
|
10602
10989
|
}
|
|
10603
10990
|
});
|
|
10604
10991
|
}
|
|
10992
|
+
/**
|
|
10993
|
+
* The ONE status-derivation function — used by the store and the kici-admin
|
|
10994
|
+
* host CLI. `ready` requires the host to be genuinely live (connected to some
|
|
10995
|
+
* instance AND heartbeat fresh — the freshness check catches a crashed
|
|
10996
|
+
* instance that never cleared `connected_instance_id`). It never returns
|
|
10997
|
+
* `ready` for a not-currently-live host: a not-live `static` reads
|
|
10998
|
+
* `unreachable` (the declared-but-absent alarm), a not-live `ephemeral` reads
|
|
10999
|
+
* `stale` (scaled down, awaiting reap).
|
|
11000
|
+
*/
|
|
11001
|
+
function deriveHostStatus(row, nowMs, graceMs) {
|
|
11002
|
+
const ageMs = nowMs - new Date(row.last_seen).getTime();
|
|
11003
|
+
if (row.connected_instance_id !== null && ageMs <= graceMs) return "ready";
|
|
11004
|
+
if (row.lifecycle_class === "ephemeral") return "stale";
|
|
11005
|
+
return "unreachable";
|
|
11006
|
+
}
|
|
11007
|
+
/**
|
|
11008
|
+
* Durable, cluster-shared roster of every agent the cluster has ever enrolled.
|
|
11009
|
+
*
|
|
11010
|
+
* The in-memory `AgentRegistry` reconciles into this table on register
|
|
11011
|
+
* (`upsert`) / unregister (`markDisconnected`), with a coarse heartbeat
|
|
11012
|
+
* (`stampLastSeen`). All liveness writes are owner-guarded by `instanceId` so a
|
|
11013
|
+
* stale disconnect from an old instance can never clobber a row a different
|
|
11014
|
+
* instance now owns. The leader-only reaper deletes `ephemeral` rows past their
|
|
11015
|
+
* ttl via `reapEphemeralPastTtl`; `static` rows persist and read `unreachable`.
|
|
11016
|
+
*/
|
|
11017
|
+
var HostRosterStore = class {
|
|
11018
|
+
db;
|
|
11019
|
+
constructor(db) {
|
|
11020
|
+
this.db = db;
|
|
11021
|
+
}
|
|
11022
|
+
/** Idempotent upsert on agent_id; stamps connected_instance_id + last_seen. */
|
|
11023
|
+
async upsert(input) {
|
|
11024
|
+
const labelsJson = JSON.stringify(input.labels);
|
|
11025
|
+
await this.db.insertInto("host_roster").values({
|
|
11026
|
+
agent_id: input.agentId,
|
|
11027
|
+
token_id: input.tokenId,
|
|
11028
|
+
lifecycle_class: input.lifecycleClass,
|
|
11029
|
+
labels: labelsJson,
|
|
11030
|
+
hostname: input.hostname,
|
|
11031
|
+
platform: input.platform,
|
|
11032
|
+
arch: input.arch,
|
|
11033
|
+
connected_instance_id: input.instanceId,
|
|
11034
|
+
last_seen: sql`now()`,
|
|
11035
|
+
updated_at: sql`now()`
|
|
11036
|
+
}).onConflict((oc) => oc.column("agent_id").doUpdateSet({
|
|
11037
|
+
token_id: input.tokenId,
|
|
11038
|
+
lifecycle_class: input.lifecycleClass,
|
|
11039
|
+
labels: labelsJson,
|
|
11040
|
+
hostname: input.hostname,
|
|
11041
|
+
platform: input.platform,
|
|
11042
|
+
arch: input.arch,
|
|
11043
|
+
connected_instance_id: input.instanceId,
|
|
11044
|
+
last_seen: sql`now()`,
|
|
11045
|
+
updated_at: sql`now()`
|
|
11046
|
+
})).execute();
|
|
11047
|
+
}
|
|
11048
|
+
/** Clear liveness on disconnect — but only if THIS instance still owns it. */
|
|
11049
|
+
async markDisconnected(agentId, instanceId) {
|
|
11050
|
+
await this.db.updateTable("host_roster").set({
|
|
11051
|
+
connected_instance_id: null,
|
|
11052
|
+
updated_at: sql`now()`
|
|
11053
|
+
}).where("agent_id", "=", agentId).where("connected_instance_id", "=", instanceId).execute();
|
|
11054
|
+
}
|
|
11055
|
+
/** Coarse heartbeat stamp — same owner-guard as markDisconnected. */
|
|
11056
|
+
async stampLastSeen(agentId, instanceId) {
|
|
11057
|
+
await this.db.updateTable("host_roster").set({ last_seen: sql`now()` }).where("agent_id", "=", agentId).where("connected_instance_id", "=", instanceId).execute();
|
|
11058
|
+
}
|
|
11059
|
+
/** Operator pre-declare of a static host before its agent dials in. */
|
|
11060
|
+
async declareStatic(input) {
|
|
11061
|
+
await this.db.insertInto("host_roster").values({
|
|
11062
|
+
agent_id: input.agentId,
|
|
11063
|
+
token_id: null,
|
|
11064
|
+
lifecycle_class: "static",
|
|
11065
|
+
labels: JSON.stringify(input.labels),
|
|
11066
|
+
hostname: input.hostname ?? null,
|
|
11067
|
+
connected_instance_id: null,
|
|
11068
|
+
last_seen: sql`now()`,
|
|
11069
|
+
updated_at: sql`now()`
|
|
11070
|
+
}).onConflict((oc) => oc.column("agent_id").doNothing()).execute();
|
|
11071
|
+
}
|
|
11072
|
+
async get(agentId) {
|
|
11073
|
+
return await this.db.selectFrom("host_roster").selectAll().where("agent_id", "=", agentId).executeTakeFirst() ?? null;
|
|
11074
|
+
}
|
|
11075
|
+
async listAll() {
|
|
11076
|
+
return this.db.selectFrom("host_roster").selectAll().orderBy("agent_id", "asc").execute();
|
|
11077
|
+
}
|
|
11078
|
+
/**
|
|
11079
|
+
* Resolve every roster host matching a `runsOnAll` predicate (OR-of-AND
|
|
11080
|
+
* include groups, minus exclude labels), tagged with its derived status. This
|
|
11081
|
+
* is the host-fanout resolver: it returns declared-but-absent static hosts
|
|
11082
|
+
* (status `unreachable`) so the caller can apply `onUnreachable` — the live
|
|
11083
|
+
* registry alone cannot name an expected-but-absent host.
|
|
11084
|
+
*/
|
|
11085
|
+
async findMatching(include, exclude, graceMs) {
|
|
11086
|
+
const rows = await this.db.selectFrom("host_roster").selectAll().execute();
|
|
11087
|
+
const now = Date.now();
|
|
11088
|
+
const out = [];
|
|
11089
|
+
for (const row of rows) {
|
|
11090
|
+
const labels = JSON.parse(row.labels);
|
|
11091
|
+
const set = new Set(labels);
|
|
11092
|
+
if (exclude.some((e) => matcherSatisfiedBy(e, set))) continue;
|
|
11093
|
+
if (include.length && !include.some((grp) => grp.every((m) => matcherSatisfiedBy(m, set)))) continue;
|
|
11094
|
+
out.push({
|
|
11095
|
+
agentId: row.agent_id,
|
|
11096
|
+
host: row.hostname ?? row.agent_id,
|
|
11097
|
+
labels,
|
|
11098
|
+
lifecycleClass: row.lifecycle_class,
|
|
11099
|
+
connectedInstanceId: row.connected_instance_id,
|
|
11100
|
+
status: deriveHostStatus(row, now, graceMs),
|
|
11101
|
+
platform: row.platform,
|
|
11102
|
+
arch: row.arch
|
|
11103
|
+
});
|
|
11104
|
+
}
|
|
11105
|
+
out.sort((a, b) => a.agentId.localeCompare(b.agentId));
|
|
11106
|
+
return out;
|
|
11107
|
+
}
|
|
11108
|
+
/**
|
|
11109
|
+
* Count `static` (declared) hosts whose derived status is `unreachable` —
|
|
11110
|
+
* the "declared-but-absent" alarm population. Reuses the single-source
|
|
11111
|
+
* {@link deriveHostStatus} so the count never diverges from what
|
|
11112
|
+
* `kici-admin host list` shows. A not-currently-connected static host reads
|
|
11113
|
+
* `unreachable` regardless of grace (only the connected-but-stale case
|
|
11114
|
+
* depends on `graceMs`).
|
|
11115
|
+
*/
|
|
11116
|
+
async countStaticUnreachable(graceMs) {
|
|
11117
|
+
const rows = await this.db.selectFrom("host_roster").selectAll().where("lifecycle_class", "=", "static").execute();
|
|
11118
|
+
const now = Date.now();
|
|
11119
|
+
return rows.filter((r) => deriveHostStatus(r, now, graceMs) === "unreachable").length;
|
|
11120
|
+
}
|
|
11121
|
+
/** Delete ephemeral rows whose last_seen is older than ttl. Returns count. */
|
|
11122
|
+
async reapEphemeralPastTtl(ttlMs) {
|
|
11123
|
+
const res = await this.db.deleteFrom("host_roster").where("lifecycle_class", "=", "ephemeral").where("last_seen", "<", sql`now() - (${ttlMs}::text || ' milliseconds')::interval`).executeTakeFirst();
|
|
11124
|
+
return Number(res.numDeletedRows ?? 0n);
|
|
11125
|
+
}
|
|
11126
|
+
};
|
|
11127
|
+
//#endregion
|
|
11128
|
+
//#region src/cli/commands/host.ts
|
|
11129
|
+
/**
|
|
11130
|
+
* The CLI reads the roster DB directly (no full orchestrator config load), so
|
|
11131
|
+
* it sources the grace window from the same env var the orchestrator config
|
|
11132
|
+
* defaults from. Default mirrors `config.ts` rosterGraceMs (300_000).
|
|
11133
|
+
*/
|
|
11134
|
+
function rosterGraceMs() {
|
|
11135
|
+
const v = Number(process.env.KICI_ROSTER_GRACE_MS);
|
|
11136
|
+
return Number.isFinite(v) && v > 0 ? v : 3e5;
|
|
11137
|
+
}
|
|
11138
|
+
function formatHostTable(rows) {
|
|
11139
|
+
if (rows.length === 0) return "No hosts in the roster.";
|
|
11140
|
+
const header = "Agent ID | Class | Status | Instance | Last Seen | Labels";
|
|
11141
|
+
const sep = "-".repeat(57);
|
|
11142
|
+
const now = Date.now();
|
|
11143
|
+
const grace = rosterGraceMs();
|
|
11144
|
+
return [
|
|
11145
|
+
header,
|
|
11146
|
+
sep,
|
|
11147
|
+
...rows.map((r) => {
|
|
11148
|
+
const status = deriveHostStatus(r, now, grace);
|
|
11149
|
+
const labels = JSON.parse(r.labels).join(",");
|
|
11150
|
+
return `${r.agent_id} | ${r.lifecycle_class} | ${status} | ${r.connected_instance_id ?? "-"} | ${new Date(r.last_seen).toISOString()} | ${labels}`;
|
|
11151
|
+
})
|
|
11152
|
+
].join("\n");
|
|
11153
|
+
}
|
|
11154
|
+
function registerHostCommands(program) {
|
|
11155
|
+
const host = program.command("host").description("Inspect and declare the host roster");
|
|
11156
|
+
host.command("list").description("List all roster hosts").option("--json", "Output JSON").action(async (opts) => {
|
|
11157
|
+
try {
|
|
11158
|
+
const rows = await withDb((db) => new HostRosterStore(db).listAll());
|
|
11159
|
+
if (opts.json) {
|
|
11160
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
11161
|
+
return;
|
|
11162
|
+
}
|
|
11163
|
+
console.log(formatHostTable(rows));
|
|
11164
|
+
} catch (err) {
|
|
11165
|
+
console.error(`Error: ${toErrorMessage(err)}`);
|
|
11166
|
+
process.exit(1);
|
|
11167
|
+
}
|
|
11168
|
+
});
|
|
11169
|
+
host.command("get").description("Show one roster host").requiredOption("--agent-id <id>", "Agent id").option("--json", "Output JSON").action(async (opts) => {
|
|
11170
|
+
try {
|
|
11171
|
+
const row = await withDb((db) => new HostRosterStore(db).get(opts.agentId));
|
|
11172
|
+
if (!row) {
|
|
11173
|
+
console.error(`No host found: ${opts.agentId}`);
|
|
11174
|
+
process.exit(1);
|
|
11175
|
+
}
|
|
11176
|
+
console.log(opts.json ? JSON.stringify(row, null, 2) : formatHostTable([row]));
|
|
11177
|
+
} catch (err) {
|
|
11178
|
+
console.error(`Error: ${toErrorMessage(err)}`);
|
|
11179
|
+
process.exit(1);
|
|
11180
|
+
}
|
|
11181
|
+
});
|
|
11182
|
+
host.command("declare").description("Pre-declare a static host before it connects").requiredOption("--agent-id <id>", "Agent id the host will register as").option("--labels <labels>", "Comma-separated labels", "").option("--hostname <name>", "Hostname").action(async (opts) => {
|
|
11183
|
+
try {
|
|
11184
|
+
const labels = opts.labels ? String(opts.labels).split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
11185
|
+
await withDb((db) => new HostRosterStore(db).declareStatic({
|
|
11186
|
+
agentId: opts.agentId,
|
|
11187
|
+
labels,
|
|
11188
|
+
hostname: opts.hostname
|
|
11189
|
+
}));
|
|
11190
|
+
console.log(`Declared static host: ${opts.agentId}`);
|
|
11191
|
+
} catch (err) {
|
|
11192
|
+
console.error(`Error: ${toErrorMessage(err)}`);
|
|
11193
|
+
process.exit(1);
|
|
11194
|
+
}
|
|
11195
|
+
});
|
|
11196
|
+
}
|
|
10605
11197
|
//#endregion
|
|
10606
11198
|
//#region src/cli/commands/org-settings.ts
|
|
10607
11199
|
function formatSettings(s, format) {
|
|
@@ -14504,7 +15096,9 @@ function renderBootScript(cfg) {
|
|
|
14504
15096
|
"#!/usr/bin/env bash",
|
|
14505
15097
|
`# Generated by kici-admin firecracker --persist for bridge ${cfg.bridgeName}. Do not hand-edit.`,
|
|
14506
15098
|
"set -euo pipefail",
|
|
14507
|
-
`
|
|
15099
|
+
`cat > ${NM_CONF_PATH} <<'KICI_NM_EOF' || true`,
|
|
15100
|
+
NM_CONF_CONTENT.replace(/\n$/, ""),
|
|
15101
|
+
"KICI_NM_EOF",
|
|
14508
15102
|
"HOST_IFACE=$(ip -j route show default | sed -n 's/.*\"dev\":\"\\([^\"]*\\)\".*/\\1/p' | head -1)",
|
|
14509
15103
|
"if [ -z \"$HOST_IFACE\" ]; then echo \"no default-route iface\" >&2; exit 1; fi",
|
|
14510
15104
|
...lines,
|
|
@@ -17291,6 +17885,7 @@ function buildProgram() {
|
|
|
17291
17885
|
registerScalerCommands(program);
|
|
17292
17886
|
registerAgentServiceCommands(program);
|
|
17293
17887
|
registerPeerCommands(program, getClient);
|
|
17888
|
+
registerHostCommands(program);
|
|
17294
17889
|
registerOrgSettingsCommands(program, getClient);
|
|
17295
17890
|
registerClusterNameCommands(program, getClient);
|
|
17296
17891
|
registerMaintenanceCommands(program, getClient);
|