@kici-dev/orchestrator 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/agent/dispatcher.d.ts +26 -0
  2. package/dist/agent/ownership-tracker.d.ts +45 -3
  3. package/dist/audit/access-log.d.ts +7 -0
  4. package/dist/cli/api-client.d.ts +14 -0
  5. package/dist/cli/commands/cluster-name.d.ts +18 -0
  6. package/dist/cli/commands/db.d.ts +9 -6
  7. package/dist/cli/commands/org-settings.d.ts +2 -1
  8. package/dist/cli/commands/shared/secret-input.d.ts +46 -0
  9. package/dist/cli/commands/variable.d.ts +18 -0
  10. package/dist/cli/service/index.d.ts +1 -0
  11. package/dist/cli/service/launchd.d.ts +19 -0
  12. package/dist/cli/service/privilege.d.ts +21 -0
  13. package/dist/cli/wizard/orchestrator-wizard.d.ts +4 -2
  14. package/dist/cli.js +1056 -236
  15. package/dist/cluster/join-token.d.ts +21 -13
  16. package/dist/cluster/peer-client.d.ts +9 -0
  17. package/dist/config/cluster-id.d.ts +23 -0
  18. package/dist/config/cluster-name.d.ts +39 -0
  19. package/dist/config.d.ts +6 -0
  20. package/dist/dashboard/handler.d.ts +54 -2
  21. package/dist/db/migrations/019_generic_sources_change_notify.d.ts +25 -0
  22. package/dist/db/migrations/020_org_settings_dashboard_write_policy.d.ts +17 -0
  23. package/dist/db/migrations/021_check_run_tracking.d.ts +26 -0
  24. package/dist/db/migrations/022_scaler_manager_state.d.ts +29 -0
  25. package/dist/db/migrations/023_dispatch_queue_recovery_deadline.d.ts +26 -0
  26. package/dist/db/types.d.ts +137 -0
  27. package/dist/events/event-store.d.ts +9 -2
  28. package/dist/events/trust-store.d.ts +7 -0
  29. package/dist/events/types.d.ts +33 -0
  30. package/dist/metrics/scheduled-jobs.d.ts +26 -5
  31. package/dist/orchestrator-core.d.ts +7 -0
  32. package/dist/policy/dashboard-write-policy.d.ts +115 -0
  33. package/dist/queue/job-queue.d.ts +54 -1
  34. package/dist/reporting/check-run-tracking-store.d.ts +133 -0
  35. package/dist/reporting/commit-status.d.ts +87 -9
  36. package/dist/routes/admin-access-log.d.ts +1 -0
  37. package/dist/routes/admin-backends.d.ts +9 -1
  38. package/dist/routes/admin-cluster-name.d.ts +50 -0
  39. package/dist/routes/admin-db.d.ts +9 -1
  40. package/dist/routes/admin-environments.d.ts +10 -6
  41. package/dist/routes/admin-event-dlq.d.ts +1 -0
  42. package/dist/routes/admin-event-log.d.ts +1 -0
  43. package/dist/routes/admin-events.d.ts +19 -0
  44. package/dist/routes/admin-maintenance.d.ts +9 -1
  45. package/dist/routes/admin-org-settings.d.ts +10 -1
  46. package/dist/routes/admin-queue-execution.d.ts +1 -0
  47. package/dist/routes/admin-registrations.d.ts +1 -0
  48. package/dist/routes/admin-runs.d.ts +1 -0
  49. package/dist/routes/admin-scheduled-jobs.d.ts +1 -0
  50. package/dist/routes/admin-sources.d.ts +9 -1
  51. package/dist/routes/admin.d.ts +8 -0
  52. package/dist/scaler/manager.d.ts +38 -0
  53. package/dist/scaler/scaler-state-store.d.ts +102 -0
  54. package/dist/secrets/routing-key-scope.d.ts +43 -0
  55. package/dist/server.js +9846 -6884
  56. package/dist/stale-detector/stale-run-detector.d.ts +14 -2
  57. package/dist/standalone.js +6120 -3838
  58. package/dist/webhook/generic-sources-listener.d.ts +74 -0
  59. package/dist/webhook/register-source-bundle.d.ts +57 -0
  60. package/dist/ws/dashboard-backends-handler.d.ts +14 -0
  61. package/dist/ws/dashboard-env-handler.d.ts +7 -0
  62. package/dist/ws/dashboard-global-workflows-handler.d.ts +6 -0
  63. package/dist/ws/dashboard-registrations-handler.d.ts +7 -0
  64. package/dist/ws/platform-client.d.ts +30 -1
  65. package/package.json +3 -3
  66. package/sbom.spdx.json +40 -35
@@ -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
- export declare function createMaintenanceRoutes(deps: MaintenanceRouteDeps): Hono;
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
@@ -20,6 +20,7 @@ type AdminQEEnv = {
20
20
  Variables: {
21
21
  role: Role;
22
22
  userId: string;
23
+ routingKey: string | null;
23
24
  };
24
25
  };
25
26
  export declare function createAdminQueueExecutionRoutes(deps: AdminQueueExecutionRoutesDeps): Hono<AdminQEEnv>;
@@ -24,6 +24,7 @@ type AdminRegEnv = {
24
24
  Variables: {
25
25
  role: Role;
26
26
  userId: string;
27
+ routingKey: string | null;
27
28
  };
28
29
  };
29
30
  /**
@@ -44,6 +44,7 @@ type AdminRunEnv = {
44
44
  Variables: {
45
45
  role: Role;
46
46
  userId: string;
47
+ routingKey: string | null;
47
48
  };
48
49
  };
49
50
  /**
@@ -29,6 +29,7 @@ type AdminEnv = {
29
29
  Variables: {
30
30
  role: Role;
31
31
  userId: string;
32
+ routingKey: string | null;
32
33
  };
33
34
  };
34
35
  export declare function createAdminScheduledJobsRoutes(deps: AdminScheduledJobsRoutesDeps): Hono<AdminEnv>;
@@ -10,9 +10,17 @@
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;
15
16
  }
16
- export declare function createSourceRoutes(deps: SourceRouteDeps): Hono;
17
+ type AdminSourcesEnv = {
18
+ Variables: {
19
+ role: Role;
20
+ userId: string;
21
+ routingKey: string | null;
22
+ };
23
+ };
24
+ export declare function createSourceRoutes(deps: SourceRouteDeps): Hono<AdminSourcesEnv>;
17
25
  export {};
18
26
  //# sourceMappingURL=admin-sources.d.ts.map
@@ -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
  */
@@ -83,6 +84,13 @@ export interface AdminRouteDeps {
83
84
  * response reports `reEncryptedConfigs: 0`.
84
85
  */
85
86
  sharedStore?: SharedConfigStore;
87
+ /**
88
+ * Optional -- attribution writer for routes that emit an `access_log`
89
+ * row directly (today: org-settings dashboard-write policy flips). When
90
+ * unset, those routes execute the mutation without recording — the
91
+ * write is best-effort, never gating.
92
+ */
93
+ accessLog?: AccessLogWriter;
86
94
  }
87
95
  /** Hono env type for admin routes with context variables. */
88
96
  type AdminEnv = {
@@ -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
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Helpers for enforcing the `routing_key` scope on admin tokens.
3
+ *
4
+ * `admin_tokens.routing_key` is an optional column. A token created with
5
+ * `kici-admin token create --routing-key <key>` is restricted to operations
6
+ * that target that exact routing key:
7
+ *
8
+ * - Routes that take an explicit routing key (URL param, query param, or
9
+ * request body) MUST gate access with `enforceRoutingKeyScope`. Routes
10
+ * that target an entity by ID look up the row's `routing_key` column
11
+ * first and pass that to `enforceRoutingKeyScope`.
12
+ * - Routes that target an org or that operate orchestrator-wide (no
13
+ * routing-key concept at all) MUST refuse routing-key tokens via
14
+ * `requireUnscopedToken`.
15
+ *
16
+ * Both helpers return a Hono `Response` to short-circuit the handler when
17
+ * access is denied, or `null` to let the handler continue. The middleware
18
+ * mounted in each admin route file is responsible for calling
19
+ * `c.set('routingKey', tokenInfo.routingKey)` so these helpers can read it.
20
+ */
21
+ import type { Context } from 'hono';
22
+ /**
23
+ * Reject the request with 403 when the calling token has a routing-key
24
+ * scope and the request targets a different routing key.
25
+ *
26
+ * Pass `requested = null | undefined` for routes that explicitly do not
27
+ * carry a routing key — in that case the call is also rejected for
28
+ * scoped tokens. Use {@link requireUnscopedToken} when the route is
29
+ * orchestrator-wide or org-scoped (no per-routing-key variant exists at
30
+ * all) to make the intent explicit.
31
+ *
32
+ * Returns a `Response` to short-circuit the handler when access is
33
+ * denied, or `null` to let the handler continue.
34
+ */
35
+ export declare function enforceRoutingKeyScope(c: Context, requested: string | null | undefined): Response | null;
36
+ /**
37
+ * Reject the request with 403 when the calling token has any routing-key
38
+ * scope. Use this on routes that operate on orchestrator-wide state,
39
+ * org-level state, or any other surface where the routing-key concept
40
+ * does not apply.
41
+ */
42
+ export declare function requireUnscopedToken(c: Context): Response | null;
43
+ //# sourceMappingURL=routing-key-scope.d.ts.map