@kici-dev/orchestrator 0.1.3 → 0.1.6
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 +26 -0
- package/dist/agent/ownership-tracker.d.ts +45 -3
- package/dist/app.d.ts +9 -3
- package/dist/audit/access-log.d.ts +7 -0
- package/dist/cli/api-client.d.ts +14 -0
- package/dist/cli/commands/cluster-name.d.ts +18 -0
- package/dist/cli/commands/db.d.ts +9 -6
- package/dist/cli/commands/org-settings.d.ts +2 -1
- package/dist/cli/commands/shared/secret-input.d.ts +46 -0
- package/dist/cli/commands/variable.d.ts +18 -0
- package/dist/cli/service/index.d.ts +1 -0
- package/dist/cli/service/launchd.d.ts +19 -0
- package/dist/cli/service/privilege.d.ts +21 -0
- package/dist/cli/wizard/orchestrator-wizard.d.ts +4 -2
- package/dist/cli.js +1074 -225
- package/dist/cluster/coordinator.d.ts +3 -3
- package/dist/cluster/join-token.d.ts +21 -13
- package/dist/cluster/peer-client.d.ts +9 -0
- package/dist/config/cluster-id.d.ts +23 -0
- package/dist/config/cluster-name.d.ts +39 -0
- package/dist/config.d.ts +8 -0
- package/dist/dashboard/handler.d.ts +54 -2
- package/dist/db/migrations/019_generic_sources_change_notify.d.ts +25 -0
- package/dist/db/migrations/020_org_settings_dashboard_write_policy.d.ts +17 -0
- package/dist/db/migrations/021_check_run_tracking.d.ts +26 -0
- package/dist/db/migrations/022_scaler_manager_state.d.ts +29 -0
- package/dist/db/migrations/023_dispatch_queue_recovery_deadline.d.ts +26 -0
- package/dist/db/types.d.ts +137 -0
- package/dist/events/event-store.d.ts +9 -2
- package/dist/events/trust-store.d.ts +7 -0
- package/dist/events/types.d.ts +33 -0
- package/dist/metrics/scheduled-jobs.d.ts +26 -5
- package/dist/orchestrator-core.d.ts +16 -2
- package/dist/pipeline/processor.d.ts +2 -2
- package/dist/pipeline/rerun.d.ts +2 -2
- package/dist/pipeline/test-pipeline.d.ts +2 -2
- package/dist/policy/dashboard-write-policy.d.ts +115 -0
- package/dist/queue/job-queue.d.ts +54 -1
- package/dist/reporting/{commit-status.d.ts → check-run-reporter.d.ts} +94 -16
- package/dist/reporting/check-run-tracking-store.d.ts +133 -0
- package/dist/routes/admin-access-log.d.ts +1 -0
- package/dist/routes/admin-backends.d.ts +9 -1
- package/dist/routes/admin-cluster-name.d.ts +50 -0
- package/dist/routes/admin-db.d.ts +9 -1
- package/dist/routes/admin-environments.d.ts +10 -6
- package/dist/routes/admin-event-dlq.d.ts +1 -0
- package/dist/routes/admin-event-log.d.ts +1 -0
- package/dist/routes/admin-events.d.ts +19 -0
- package/dist/routes/admin-maintenance.d.ts +9 -1
- package/dist/routes/admin-org-settings.d.ts +10 -1
- package/dist/routes/admin-queue-execution.d.ts +1 -0
- package/dist/routes/admin-registrations.d.ts +1 -0
- package/dist/routes/admin-runs.d.ts +1 -0
- package/dist/routes/admin-scheduled-jobs.d.ts +1 -0
- package/dist/routes/admin-sources.d.ts +24 -1
- package/dist/routes/admin.d.ts +22 -0
- package/dist/scaler/manager.d.ts +38 -0
- package/dist/scaler/scaler-state-store.d.ts +102 -0
- package/dist/secrets/routing-key-scope.d.ts +43 -0
- package/dist/server.js +10098 -6961
- package/dist/sources/build-platform-sources.d.ts +26 -0
- package/dist/sources/source-manager.d.ts +14 -0
- package/dist/stale-detector/stale-run-detector.d.ts +17 -5
- package/dist/standalone.js +7525 -5172
- package/dist/webhook/generic-sources-listener.d.ts +89 -0
- package/dist/webhook/register-source-bundle.d.ts +57 -0
- package/dist/ws/dashboard-backends-handler.d.ts +14 -0
- package/dist/ws/dashboard-env-handler.d.ts +7 -0
- package/dist/ws/dashboard-global-workflows-handler.d.ts +6 -0
- package/dist/ws/dashboard-registrations-handler.d.ts +7 -0
- package/dist/ws/platform-client.d.ts +57 -2
- package/package.json +3 -3
- package/sbom.spdx.json +40 -35
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { Database } from '../db/types.js';
|
|
3
|
+
import type { StepProgressEntry } from './check-run-summary.js';
|
|
4
|
+
/**
|
|
5
|
+
* Composite key identifying a single check-run row.
|
|
6
|
+
*
|
|
7
|
+
* Matches the table primary key `(provider, owner, repo, sha, check_name)`.
|
|
8
|
+
* Used by the L1 in-memory cache and as the parameter shape for every
|
|
9
|
+
* store method.
|
|
10
|
+
*/
|
|
11
|
+
export interface CheckRunTrackingKey {
|
|
12
|
+
provider: string;
|
|
13
|
+
owner: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
sha: string;
|
|
16
|
+
checkName: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Snapshot of all per-key check-run state. Mirrors the columns of the
|
|
20
|
+
* `check_run_tracking` table with the in-memory shapes the consumer
|
|
21
|
+
* already uses.
|
|
22
|
+
*/
|
|
23
|
+
export interface CheckRunTrackingState {
|
|
24
|
+
/** GitHub Checks API check-run ID. Undefined when not yet created. */
|
|
25
|
+
checkRunId?: number;
|
|
26
|
+
/** Build check-run lifecycle marker. */
|
|
27
|
+
buildCreationState?: 'pending' | 'completed';
|
|
28
|
+
/** Live step-progress entries shown in the check run's `output.summary`. */
|
|
29
|
+
stepProgress: StepProgressEntry[];
|
|
30
|
+
/** Timestamp the first running-step transition was sent to GitHub. */
|
|
31
|
+
inProgressSentAt?: Date;
|
|
32
|
+
/** KiCI run this check-run belongs to. Used by `cleanupRun`. */
|
|
33
|
+
runId?: string;
|
|
34
|
+
/** Last persisted update time; powers debounce-after-failover recovery. */
|
|
35
|
+
updatedAt?: Date;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* DB persistence for `CheckRunReporter` check-run state.
|
|
39
|
+
*
|
|
40
|
+
* Backed by the `check_run_tracking` table — one row per
|
|
41
|
+
* `(provider, owner, repo, sha, check_name)`. Replaces six in-memory
|
|
42
|
+
* `Map`s previously held inside `CheckRunReporter`:
|
|
43
|
+
*
|
|
44
|
+
* - `checkRunIds` → `check_run_id` column
|
|
45
|
+
* - `pendingBuildCreations` → `build_creation_state` column
|
|
46
|
+
* - `stepProgress` → `step_progress_json` column
|
|
47
|
+
* - `inProgressSent` → `in_progress_sent_at` column
|
|
48
|
+
* - `runIdToKeys` → indexed `run_id` column + `listKeysByRunId`
|
|
49
|
+
*
|
|
50
|
+
* The `progressTimers` Map is intentionally NOT persisted — debounce
|
|
51
|
+
* timers are reconstructed on demand. After a coord failover the very
|
|
52
|
+
* next `updateStepProgress` either flushes immediately (debounce window
|
|
53
|
+
* elapsed) or starts a fresh timer.
|
|
54
|
+
*
|
|
55
|
+
* The consumer keeps an L1 in-memory cache in front of this store; the
|
|
56
|
+
* store itself is stateless beyond the connection it holds.
|
|
57
|
+
*/
|
|
58
|
+
export declare class CheckRunTrackingStore {
|
|
59
|
+
private readonly db;
|
|
60
|
+
constructor(db: Kysely<Database>);
|
|
61
|
+
/**
|
|
62
|
+
* Atomically set / overwrite the check-run ID for a key.
|
|
63
|
+
*
|
|
64
|
+
* Performed as an upsert so a re-issued setPending after a coord
|
|
65
|
+
* failover replaces the prior ID rather than silently leaving a row
|
|
66
|
+
* mismatched with the GitHub-side state.
|
|
67
|
+
*/
|
|
68
|
+
setCheckRunId(key: CheckRunTrackingKey, checkRunId: number): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Lookup the check-run ID for a key. Returns undefined if no row exists
|
|
71
|
+
* yet OR the row exists but the GitHub create has not finished
|
|
72
|
+
* persisting an ID (the build-creation in-flight window).
|
|
73
|
+
*/
|
|
74
|
+
getCheckRunId(key: CheckRunTrackingKey): Promise<number | undefined>;
|
|
75
|
+
/**
|
|
76
|
+
* Mark a build check-run as having an in-flight create. Returns true if
|
|
77
|
+
* this caller won the race (no prior row, or row had no in-flight state).
|
|
78
|
+
* Used to prevent a replacement coord from re-issuing a `checks.create()`
|
|
79
|
+
* against the same SHA when the original create is still pending.
|
|
80
|
+
*/
|
|
81
|
+
markBuildCreationPending(key: CheckRunTrackingKey, runId?: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Mark a build check-run create as complete. Idempotent.
|
|
84
|
+
*/
|
|
85
|
+
markBuildCreationComplete(key: CheckRunTrackingKey): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Replace the step-progress array for a key.
|
|
88
|
+
*/
|
|
89
|
+
setStepProgress(key: CheckRunTrackingKey, steps: StepProgressEntry[], runId?: string): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Mark the first in-progress transition as sent. Used to keep the
|
|
92
|
+
* single "did we already kick this check run into in_progress?" guard
|
|
93
|
+
* cluster-wide.
|
|
94
|
+
*/
|
|
95
|
+
markInProgressSent(key: CheckRunTrackingKey, runId?: string): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Get the full state snapshot for a key. Used by the L1 cache to
|
|
98
|
+
* hydrate on miss and by tests to verify the on-disk layout. Returns
|
|
99
|
+
* undefined when no row exists.
|
|
100
|
+
*/
|
|
101
|
+
getState(key: CheckRunTrackingKey): Promise<CheckRunTrackingState | undefined>;
|
|
102
|
+
/**
|
|
103
|
+
* Delete a single row. Returns true if the row existed.
|
|
104
|
+
*/
|
|
105
|
+
deleteRow(key: CheckRunTrackingKey): Promise<boolean>;
|
|
106
|
+
/**
|
|
107
|
+
* List every key currently tracked for a runId. Used by `cleanupRun`
|
|
108
|
+
* to reproduce the runId → keys reverse index that the in-memory map
|
|
109
|
+
* provided. Index `idx_check_run_tracking_run_id` keeps this O(matches).
|
|
110
|
+
*/
|
|
111
|
+
listKeysByRunId(runId: string): Promise<CheckRunTrackingKey[]>;
|
|
112
|
+
/**
|
|
113
|
+
* Delete every row for a runId. Mirrors the bulk-cleanup semantics of
|
|
114
|
+
* `cleanupRun` so a single call from execution-tracker prune releases
|
|
115
|
+
* all rows for the run.
|
|
116
|
+
*/
|
|
117
|
+
deleteByRunId(runId: string): Promise<number>;
|
|
118
|
+
private selectRow;
|
|
119
|
+
private upsertRow;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Convert a raw DB row to the in-memory `CheckRunTrackingState` shape.
|
|
123
|
+
* Exported for direct use from tests that bypass the store.
|
|
124
|
+
*/
|
|
125
|
+
export declare function rowToState(row: {
|
|
126
|
+
check_run_id: number | string | null;
|
|
127
|
+
build_creation_state: string | null;
|
|
128
|
+
step_progress_json: unknown;
|
|
129
|
+
in_progress_sent_at: Date | null;
|
|
130
|
+
run_id: string | null;
|
|
131
|
+
updated_at: Date;
|
|
132
|
+
}): CheckRunTrackingState;
|
|
133
|
+
//# sourceMappingURL=check-run-tracking-store.d.ts.map
|
|
@@ -12,6 +12,14 @@ import { Hono } from 'hono';
|
|
|
12
12
|
import type { BackendSyncManager } from '@kici-dev/engine';
|
|
13
13
|
import type { BackendRegistry } from '../secrets/backend-registry.js';
|
|
14
14
|
import type { BackendHealthChecker } from '../secrets/backend-health.js';
|
|
15
|
+
import type { Role } from '../secrets/rbac.js';
|
|
16
|
+
type AdminBackendsEnv = {
|
|
17
|
+
Variables: {
|
|
18
|
+
role: Role;
|
|
19
|
+
userId: string;
|
|
20
|
+
routingKey: string | null;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
15
23
|
interface BackendRouteDeps {
|
|
16
24
|
registry: BackendRegistry;
|
|
17
25
|
healthChecker: BackendHealthChecker;
|
|
@@ -23,6 +31,6 @@ interface BackendRouteDeps {
|
|
|
23
31
|
* @param deps - Backend route dependencies (registry, health checker, sync manager)
|
|
24
32
|
* @returns Hono app with backend routes
|
|
25
33
|
*/
|
|
26
|
-
export declare function createBackendRoutes(deps: BackendRouteDeps): Hono
|
|
34
|
+
export declare function createBackendRoutes(deps: BackendRouteDeps): Hono<AdminBackendsEnv>;
|
|
27
35
|
export {};
|
|
28
36
|
//# sourceMappingURL=admin-backends.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin API routes for managing this orchestrator's cluster name
|
|
3
|
+
* (`cluster_meta.cluster_name`).
|
|
4
|
+
*
|
|
5
|
+
* The cluster name is the human-friendly identifier that surfaces in
|
|
6
|
+
* Platform's connection registry and in the dashboard's per-orch URL
|
|
7
|
+
* segment (`/orgs/:cId/orchestrators/:clusterName/...`). Operators
|
|
8
|
+
* read and rename it via `kici-admin cluster-name {get,set}`.
|
|
9
|
+
*
|
|
10
|
+
* Routes:
|
|
11
|
+
*
|
|
12
|
+
* - `GET /api/v1/admin/cluster-name` →
|
|
13
|
+
* `{ clusterName: string, looksAutoGenerated: boolean }`
|
|
14
|
+
* - `PUT /api/v1/admin/cluster-name` → body `{ name: string }`,
|
|
15
|
+
* validates via the shared `clusterNameSchema` and persists.
|
|
16
|
+
* Returns `{ clusterName, prior }`. Renames are recorded in
|
|
17
|
+
* `access_log` so the audit trail captures who changed it and when.
|
|
18
|
+
*
|
|
19
|
+
* Mutation requires `secret.write` (same posture as the rest of
|
|
20
|
+
* admin-org-settings). The orchestrator publishes the new name on the
|
|
21
|
+
* next `source.register` — the route response includes a hint so the
|
|
22
|
+
* CLI can tell the operator to reconnect to make Platform aware.
|
|
23
|
+
*/
|
|
24
|
+
import { Hono } from 'hono';
|
|
25
|
+
import type { Kysely } from 'kysely';
|
|
26
|
+
import { CLUSTER_NAME_REGEX } from '@kici-dev/engine/protocol/cluster-name';
|
|
27
|
+
import type { Database } from '../db/types.js';
|
|
28
|
+
import type { RbacEnforcer, Role } from '../secrets/rbac.js';
|
|
29
|
+
import type { AccessLogWriter } from '../audit/access-log.js';
|
|
30
|
+
interface ClusterNameRouteDeps {
|
|
31
|
+
db: Kysely<Database>;
|
|
32
|
+
rbac: RbacEnforcer;
|
|
33
|
+
/**
|
|
34
|
+
* Optional — when wired, each rename emits one `access_log` row
|
|
35
|
+
* (`cluster_name.update`) with `orgId=null` carrying the prior + new
|
|
36
|
+
* value in `meta`. Cluster-name is orch-scoped, not org-scoped, so
|
|
37
|
+
* the row's `orgId` is null by design.
|
|
38
|
+
*/
|
|
39
|
+
accessLog?: AccessLogWriter;
|
|
40
|
+
}
|
|
41
|
+
type AdminEnv = {
|
|
42
|
+
Variables: {
|
|
43
|
+
role: Role;
|
|
44
|
+
userId: string;
|
|
45
|
+
routingKey: string | null;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export declare function createClusterNameRoutes(deps: ClusterNameRouteDeps): Hono<AdminEnv>;
|
|
49
|
+
export { CLUSTER_NAME_REGEX };
|
|
50
|
+
//# sourceMappingURL=admin-cluster-name.d.ts.map
|
|
@@ -11,10 +11,18 @@
|
|
|
11
11
|
import { Hono } from 'hono';
|
|
12
12
|
import type { Kysely } from 'kysely';
|
|
13
13
|
import type pg from 'pg';
|
|
14
|
+
import type { Role } from '../secrets/rbac.js';
|
|
14
15
|
interface DbRouteDeps {
|
|
15
16
|
db: Kysely<any>;
|
|
16
17
|
pool: pg.Pool;
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
+
type AdminDbEnv = {
|
|
20
|
+
Variables: {
|
|
21
|
+
role: Role;
|
|
22
|
+
userId: string;
|
|
23
|
+
routingKey: string | null;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export declare function createDbRoutes(deps: DbRouteDeps): Hono<AdminDbEnv>;
|
|
19
27
|
export {};
|
|
20
28
|
//# sourceMappingURL=admin-db.d.ts.map
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Admin API routes for environment management.
|
|
3
3
|
*
|
|
4
|
-
* POST /api/v1/admin/environments
|
|
5
|
-
* POST /api/v1/admin/environments/:name/bind
|
|
6
|
-
* PATCH /api/v1/admin/environments/:name/policy
|
|
7
|
-
* GET /api/v1/admin/environments?orgId=<id>
|
|
8
|
-
* GET /api/v1/admin/environments/:name?orgId=<id>
|
|
9
|
-
* POST /api/v1/admin/environments/templates
|
|
4
|
+
* POST /api/v1/admin/environments — create (upsert)
|
|
5
|
+
* POST /api/v1/admin/environments/:name/bind — bind a scope pattern
|
|
6
|
+
* PATCH /api/v1/admin/environments/:name/policy — update policy fields
|
|
7
|
+
* GET /api/v1/admin/environments?orgId=<id> — list environments
|
|
8
|
+
* GET /api/v1/admin/environments/:name?orgId=<id> — show env + vars + bindings
|
|
9
|
+
* POST /api/v1/admin/environments/templates — create/update a template
|
|
10
|
+
* GET /api/v1/admin/environments/:name/variables?orgId=<id> — list org-level variables
|
|
11
|
+
* PUT /api/v1/admin/environments/:name/variables/:key?orgId=<id> — upsert variable
|
|
12
|
+
* DELETE /api/v1/admin/environments/:name/variables/:key?orgId=<id> — delete variable
|
|
10
13
|
*
|
|
11
14
|
* Backs the `kici-admin environment` dual-mode CLI. Offline (direct-DB) mode
|
|
12
15
|
* bypasses this router entirely — the CLI calls `*Direct` helpers from
|
|
@@ -29,6 +32,7 @@ type AdminEnvEnv = {
|
|
|
29
32
|
Variables: {
|
|
30
33
|
role: Role;
|
|
31
34
|
userId: string;
|
|
35
|
+
routingKey: string | null;
|
|
32
36
|
};
|
|
33
37
|
};
|
|
34
38
|
/**
|
|
@@ -13,6 +13,9 @@ import type { GenericSourceManager } from '../webhook/generic-sources.js';
|
|
|
13
13
|
import type { TrustStore } from '../events/trust-store.js';
|
|
14
14
|
import type { TokenManager } from '../secrets/token-manager.js';
|
|
15
15
|
import type { RbacEnforcer, Role } from '../secrets/rbac.js';
|
|
16
|
+
import type { AppConfig } from '../config.js';
|
|
17
|
+
import type { ProviderRegistry } from '../provider-registry.js';
|
|
18
|
+
import type { SecretResolver } from '../secrets/secret-resolver.js';
|
|
16
19
|
/**
|
|
17
20
|
* Dependencies for admin event routes.
|
|
18
21
|
*/
|
|
@@ -21,6 +24,21 @@ interface AdminEventRouteDeps {
|
|
|
21
24
|
trustStore: TrustStore;
|
|
22
25
|
tokenManager: TokenManager;
|
|
23
26
|
rbac: RbacEnforcer;
|
|
27
|
+
/**
|
|
28
|
+
* The in-process bundle registry. The POST /generic-sources handler
|
|
29
|
+
* registers an internal / universal-git bundle into this registry
|
|
30
|
+
* immediately after the source row lands in the DB, so the next
|
|
31
|
+
* webhook against that source resolves the right normalizer without
|
|
32
|
+
* waiting for an orchestrator restart.
|
|
33
|
+
*/
|
|
34
|
+
providerRegistry: ProviderRegistry;
|
|
35
|
+
/** Needed by `registerProviderBundleForSource` to gate internal-bundle
|
|
36
|
+
* registration on `canServeGenericProviderType` and read the
|
|
37
|
+
* `internalProviderRepoPath` / `internalProviderCloneUrl` config. */
|
|
38
|
+
config: AppConfig;
|
|
39
|
+
/** Required for universal-git source registration — `null` is allowed;
|
|
40
|
+
* rows with `git_config` are skipped + metric-bumped in that case. */
|
|
41
|
+
secretResolver: SecretResolver | null;
|
|
24
42
|
/**
|
|
25
43
|
* Optional — when provided, the `POST /api/v1/admin/events/emit` route is
|
|
26
44
|
* mounted so operators can INSERT into `kici_events` + `pg_notify` via HTTP.
|
|
@@ -33,6 +51,7 @@ type AdminEventEnv = {
|
|
|
33
51
|
Variables: {
|
|
34
52
|
role: Role;
|
|
35
53
|
userId: string;
|
|
54
|
+
routingKey: string | null;
|
|
36
55
|
};
|
|
37
56
|
};
|
|
38
57
|
/**
|
|
@@ -14,9 +14,17 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { Hono } from 'hono';
|
|
16
16
|
import type { Kysely } from 'kysely';
|
|
17
|
+
import type { Role } from '../secrets/rbac.js';
|
|
17
18
|
interface MaintenanceRouteDeps {
|
|
18
19
|
db: Kysely<any>;
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
type AdminMaintenanceEnv = {
|
|
22
|
+
Variables: {
|
|
23
|
+
role: Role;
|
|
24
|
+
userId: string;
|
|
25
|
+
routingKey: string | null;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export declare function createMaintenanceRoutes(deps: MaintenanceRouteDeps): Hono<AdminMaintenanceEnv>;
|
|
21
29
|
export {};
|
|
22
30
|
//# sourceMappingURL=admin-maintenance.d.ts.map
|
|
@@ -11,11 +11,20 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { Hono } from 'hono';
|
|
13
13
|
import { type Kysely } from 'kysely';
|
|
14
|
+
import { DashboardWriteOperation } from '@kici-dev/engine/protocol/dashboard-write-operations';
|
|
14
15
|
import type { Database } from '../db/types.js';
|
|
15
16
|
import type { RbacEnforcer, Role } from '../secrets/rbac.js';
|
|
17
|
+
import type { AccessLogWriter } from '../audit/access-log.js';
|
|
16
18
|
interface OrgSettingsRouteDeps {
|
|
17
19
|
db: Kysely<Database>;
|
|
18
20
|
rbac: RbacEnforcer;
|
|
21
|
+
/**
|
|
22
|
+
* Optional — when wired, each `dashboard_write_policy` flip emits one
|
|
23
|
+
* `access_log` row (`org_settings.dashboard_write_policy.update`)
|
|
24
|
+
* carrying the operation name + prior/next state in `actor_meta`.
|
|
25
|
+
* Reset calls additionally stamp `reset: true`.
|
|
26
|
+
*/
|
|
27
|
+
accessLog?: AccessLogWriter;
|
|
19
28
|
}
|
|
20
29
|
type AdminEnv = {
|
|
21
30
|
Variables: {
|
|
@@ -25,5 +34,5 @@ type AdminEnv = {
|
|
|
25
34
|
};
|
|
26
35
|
};
|
|
27
36
|
export declare function createOrgSettingsRoutes(deps: OrgSettingsRouteDeps): Hono<AdminEnv>;
|
|
28
|
-
export {};
|
|
37
|
+
export { DashboardWriteOperation };
|
|
29
38
|
//# sourceMappingURL=admin-org-settings.d.ts.map
|
|
@@ -10,9 +10,32 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { Hono } from 'hono';
|
|
12
12
|
import type { SourceStore } from '../sources/source-store.js';
|
|
13
|
+
import type { Role } from '../secrets/rbac.js';
|
|
13
14
|
interface SourceRouteDeps {
|
|
14
15
|
sourceStore: SourceStore;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the public webhook URL for a freshly added source so the CLI can
|
|
18
|
+
* print it. Platform/hybrid mode registers the source with the Platform and
|
|
19
|
+
* reads the URL from the `source.register.ack`; independent mode returns a
|
|
20
|
+
* null URL with an explanatory `webhookNote`. Omitted in deployments with no
|
|
21
|
+
* resolver wired (the route then returns `webhookUrl: null`).
|
|
22
|
+
*/
|
|
23
|
+
resolveSourceWebhookUrl?: (params: {
|
|
24
|
+
routingKey: string;
|
|
25
|
+
provider: string;
|
|
26
|
+
sourceId: string;
|
|
27
|
+
}) => Promise<{
|
|
28
|
+
webhookUrl: string | null;
|
|
29
|
+
webhookNote?: string;
|
|
30
|
+
}>;
|
|
15
31
|
}
|
|
16
|
-
|
|
32
|
+
type AdminSourcesEnv = {
|
|
33
|
+
Variables: {
|
|
34
|
+
role: Role;
|
|
35
|
+
userId: string;
|
|
36
|
+
routingKey: string | null;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export declare function createSourceRoutes(deps: SourceRouteDeps): Hono<AdminSourcesEnv>;
|
|
17
40
|
export {};
|
|
18
41
|
//# sourceMappingURL=admin-sources.d.ts.map
|
package/dist/routes/admin.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type { BackendHealthChecker } from '../secrets/backend-health.js';
|
|
|
23
23
|
import type { BackendSyncManager } from '@kici-dev/engine';
|
|
24
24
|
import type { Kysely } from 'kysely';
|
|
25
25
|
import type pg from 'pg';
|
|
26
|
+
import type { AccessLogWriter } from '../audit/access-log.js';
|
|
26
27
|
/**
|
|
27
28
|
* Dependencies for admin API routes.
|
|
28
29
|
*/
|
|
@@ -62,6 +63,20 @@ export interface AdminRouteDeps {
|
|
|
62
63
|
broadcastAgentTokenRevoke?: (tokenId: string) => void;
|
|
63
64
|
/** Optional -- for source management endpoints. */
|
|
64
65
|
sourceStore?: SourceStore;
|
|
66
|
+
/**
|
|
67
|
+
* Optional -- resolves the public webhook URL for a newly added source so
|
|
68
|
+
* `kici-admin source add` can print it. Wired in platform/hybrid mode to
|
|
69
|
+
* register-and-await the Platform ack; independent mode returns a null URL
|
|
70
|
+
* with a note.
|
|
71
|
+
*/
|
|
72
|
+
resolveSourceWebhookUrl?: (params: {
|
|
73
|
+
routingKey: string;
|
|
74
|
+
provider: string;
|
|
75
|
+
sourceId: string;
|
|
76
|
+
}) => Promise<{
|
|
77
|
+
webhookUrl: string | null;
|
|
78
|
+
webhookNote?: string;
|
|
79
|
+
}>;
|
|
65
80
|
/** Optional -- for DB migration endpoints. */
|
|
66
81
|
db?: Kysely<any>;
|
|
67
82
|
/** Optional -- for DB migration endpoints. */
|
|
@@ -83,6 +98,13 @@ export interface AdminRouteDeps {
|
|
|
83
98
|
* response reports `reEncryptedConfigs: 0`.
|
|
84
99
|
*/
|
|
85
100
|
sharedStore?: SharedConfigStore;
|
|
101
|
+
/**
|
|
102
|
+
* Optional -- attribution writer for routes that emit an `access_log`
|
|
103
|
+
* row directly (today: org-settings dashboard-write policy flips). When
|
|
104
|
+
* unset, those routes execute the mutation without recording — the
|
|
105
|
+
* write is best-effort, never gating.
|
|
106
|
+
*/
|
|
107
|
+
accessLog?: AccessLogWriter;
|
|
86
108
|
}
|
|
87
109
|
/** Hono env type for admin routes with context variables. */
|
|
88
110
|
type AdminEnv = {
|
package/dist/scaler/manager.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { ResourceRequest } from '@kici-dev/engine';
|
|
10
10
|
import type { ScalerBackend, ScalerConfig, ScaleResult, ScalerEvent, ResourceCap, ValidationResult } from './types.js';
|
|
11
|
+
import type { ScalerStateStore, ScalerStateRecovery } from './scaler-state-store.js';
|
|
11
12
|
/**
|
|
12
13
|
* Resolved per-job resource amounts (cpus + bytes) for both `requests` and
|
|
13
14
|
* `limits`. The scaler manager produces this from the job's declared resources
|
|
@@ -125,6 +126,14 @@ export declare class ScalerManager {
|
|
|
125
126
|
*/
|
|
126
127
|
private readonly onScalerEvent?;
|
|
127
128
|
private readonly warmPool;
|
|
129
|
+
/**
|
|
130
|
+
* Optional DB-backed state store. When wired (production path), every
|
|
131
|
+
* mutation to `spawningAgents` / `agentJobCorrelation` / `reservations`
|
|
132
|
+
* is write-through-cached to Postgres so a coord crash mid-spawn no
|
|
133
|
+
* longer orphans agents, strands reservations, or loses correlation.
|
|
134
|
+
* Unit tests can omit the store and operate from in-memory Maps only.
|
|
135
|
+
*/
|
|
136
|
+
private readonly stateStore?;
|
|
128
137
|
constructor(deps: {
|
|
129
138
|
config: ScalerConfig;
|
|
130
139
|
backends: Array<{
|
|
@@ -133,6 +142,11 @@ export declare class ScalerManager {
|
|
|
133
142
|
}>;
|
|
134
143
|
/** Callback for relaying scaler events with runId/jobId context. */
|
|
135
144
|
onScalerEvent?: (runId: string, jobId: string, event: ScalerEvent) => void;
|
|
145
|
+
/**
|
|
146
|
+
* Optional DB-backed state store. Tests omit it; production wires it
|
|
147
|
+
* up via the orchestrator-core bootstrap.
|
|
148
|
+
*/
|
|
149
|
+
stateStore?: ScalerStateStore;
|
|
136
150
|
/**
|
|
137
151
|
* Optional machine-ledger options. When `machinePools` are configured,
|
|
138
152
|
* the manager initializes a `MachineLedger` keyed off this directory and
|
|
@@ -316,6 +330,30 @@ export declare class ScalerManager {
|
|
|
316
330
|
* entries would leak in spawningAgents forever.
|
|
317
331
|
*/
|
|
318
332
|
private pruneStaleSpawningEntries;
|
|
333
|
+
private persistSpawningAgent;
|
|
334
|
+
private deleteSpawningAgentFromStore;
|
|
335
|
+
private persistReservation;
|
|
336
|
+
private deleteReservationFromStore;
|
|
337
|
+
private persistAgentJob;
|
|
338
|
+
private deleteAgentJobFromStore;
|
|
339
|
+
/**
|
|
340
|
+
* Hydrate the in-memory Maps from the DB-backed state store after a
|
|
341
|
+
* coord boot or Raft leader switch. Reconstructs:
|
|
342
|
+
*
|
|
343
|
+
* - `spawningAgents` (with `boundJobId` preserved for eager-dispatch on register)
|
|
344
|
+
* - `agentJobCorrelation` (so scaler-lifecycle events route correctly)
|
|
345
|
+
* - `reservations` + `perScalerUsage` (so the cap-check critical
|
|
346
|
+
* section reflects the cluster-wide truth, not the local empty
|
|
347
|
+
* starting state)
|
|
348
|
+
*
|
|
349
|
+
* The `globalUsage` counter is recomputed from `perScalerUsage` to
|
|
350
|
+
* keep the cap math consistent. `eventBuffer` is NOT restored — events
|
|
351
|
+
* emitted by the previous coord before correlation are lost (see
|
|
352
|
+
* wishlist for the rationale).
|
|
353
|
+
*
|
|
354
|
+
* No-op when no store is wired (unit-test path).
|
|
355
|
+
*/
|
|
356
|
+
recoverState(): Promise<ScalerStateRecovery>;
|
|
319
357
|
private generateAgentId;
|
|
320
358
|
/**
|
|
321
359
|
* Start log forwarding for a scaler-managed agent if its backend supports LogCapture.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { Database } from '../db/types.js';
|
|
3
|
+
import type { ScalerEvent } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Snapshot of a spawning-agent record. Mirrors the row shape in
|
|
6
|
+
* `scaler_spawning_agents`.
|
|
7
|
+
*/
|
|
8
|
+
export interface SpawningAgentSnapshot {
|
|
9
|
+
agentId: string;
|
|
10
|
+
scalerName: string;
|
|
11
|
+
labelSet: string[];
|
|
12
|
+
runId?: string;
|
|
13
|
+
jobId?: string;
|
|
14
|
+
boundJobId?: string;
|
|
15
|
+
spawnedAt: Date;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Snapshot of an agent-job correlation. Mirrors the row shape in
|
|
19
|
+
* `scaler_agent_jobs`.
|
|
20
|
+
*/
|
|
21
|
+
export interface AgentJobCorrelationSnapshot {
|
|
22
|
+
agentId: string;
|
|
23
|
+
runId: string;
|
|
24
|
+
jobId: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Snapshot of a resource reservation. Mirrors the row shape in
|
|
28
|
+
* `scaler_reservations`.
|
|
29
|
+
*/
|
|
30
|
+
export interface ReservationSnapshot {
|
|
31
|
+
agentId: string;
|
|
32
|
+
scalerName: string;
|
|
33
|
+
cpus: number;
|
|
34
|
+
memBytes: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* DB persistence for `ScalerManager` HA-critical state.
|
|
38
|
+
*
|
|
39
|
+
* Backed by three tables — `scaler_spawning_agents`, `scaler_agent_jobs`,
|
|
40
|
+
* `scaler_reservations` — so a Raft leader switch / coord crash no
|
|
41
|
+
* longer:
|
|
42
|
+
*
|
|
43
|
+
* - orphans an agent that is mid-spawn (lost `boundJobId` → eager
|
|
44
|
+
* dispatch silently downgraded to a generic queue drain),
|
|
45
|
+
* - strands a reservation (resource counted as used until the agent's
|
|
46
|
+
* backend GC eventually disconnects, minutes later),
|
|
47
|
+
* - drops the agent → run/job correlation (execution-tracker loses
|
|
48
|
+
* scaler-lifecycle events emitted by the new coord).
|
|
49
|
+
*
|
|
50
|
+
* The consumer keeps the in-memory Maps as L1 caches. On boot /
|
|
51
|
+
* become-leader the caches are hydrated via `recoverState()`.
|
|
52
|
+
*
|
|
53
|
+
* `perScalerUsage` / `globalUsage` are NOT stored — they are derived
|
|
54
|
+
* state recomputed from `SUM(...) FROM scaler_reservations` on
|
|
55
|
+
* recovery, which means the on-disk reservation rows are the single
|
|
56
|
+
* source of truth for the cap-check critical section.
|
|
57
|
+
*
|
|
58
|
+
* The `eventBuffer` Map is also not persisted: events emitted before
|
|
59
|
+
* correlation are observability, not correctness. A coord crash before
|
|
60
|
+
* `correlateAgentToJob()` runs accepts losing those events (see the
|
|
61
|
+
* wishlist for the rationale).
|
|
62
|
+
*/
|
|
63
|
+
export declare class ScalerStateStore {
|
|
64
|
+
private readonly db;
|
|
65
|
+
constructor(db: Kysely<Database>);
|
|
66
|
+
upsertSpawningAgent(snapshot: SpawningAgentSnapshot): Promise<void>;
|
|
67
|
+
deleteSpawningAgent(agentId: string): Promise<void>;
|
|
68
|
+
listSpawningAgents(): Promise<SpawningAgentSnapshot[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Delete every spawning-agent row whose `spawned_at` is older than the
|
|
71
|
+
* given cutoff. Used by the leader-gated GC sweep so a coord that
|
|
72
|
+
* crashed mid-spawn doesn't leave the row blocking the spawn-timeout
|
|
73
|
+
* detection forever. Returns the row count GC'd.
|
|
74
|
+
*/
|
|
75
|
+
sweepStaleSpawningAgents(olderThan: Date): Promise<number>;
|
|
76
|
+
upsertAgentJob(snapshot: AgentJobCorrelationSnapshot): Promise<void>;
|
|
77
|
+
deleteAgentJob(agentId: string): Promise<void>;
|
|
78
|
+
listAgentJobs(): Promise<AgentJobCorrelationSnapshot[]>;
|
|
79
|
+
upsertReservation(snapshot: ReservationSnapshot): Promise<void>;
|
|
80
|
+
deleteReservation(agentId: string): Promise<void>;
|
|
81
|
+
listReservations(): Promise<ReservationSnapshot[]>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Aggregate event surface for "the scaler manager fully replayed its
|
|
85
|
+
* state from the DB after a leader switch". Kept here (vs in
|
|
86
|
+
* manager.ts) so `ScalerManager.recoverState()` can declare a clean
|
|
87
|
+
* return type. `bufferedEventsLost` always returns 0 today — the
|
|
88
|
+
* `eventBuffer` Map is intentionally not persisted — but the field
|
|
89
|
+
* exists so a future buffer-table addition is type-compatible.
|
|
90
|
+
*/
|
|
91
|
+
export interface ScalerStateRecovery {
|
|
92
|
+
spawningAgentsRehydrated: number;
|
|
93
|
+
agentJobsRehydrated: number;
|
|
94
|
+
reservationsRehydrated: number;
|
|
95
|
+
bufferedEventsLost: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Re-export for the buffered-events note above; sole reason
|
|
99
|
+
* `ScalerEvent` is imported is to keep that comment compile-checked.
|
|
100
|
+
*/
|
|
101
|
+
export type { ScalerEvent };
|
|
102
|
+
//# sourceMappingURL=scaler-state-store.d.ts.map
|