@kici-dev/orchestrator 0.1.13 → 0.1.15

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 (65) hide show
  1. package/README.md +13 -1
  2. package/dist/__test-helpers__/mock-db.d.ts +2 -0
  3. package/dist/agent/dispatcher.d.ts +110 -6
  4. package/dist/agent/registry.d.ts +14 -0
  5. package/dist/app.d.ts +11 -0
  6. package/dist/cache/agent-job-failed-error.d.ts +13 -0
  7. package/dist/cache/dispatch-cache-ref-tracker.d.ts +45 -0
  8. package/dist/cache/index.d.ts +2 -0
  9. package/dist/cache/user-cache.d.ts +116 -0
  10. package/dist/cancel/cancel-run.d.ts +56 -0
  11. package/dist/cli/commands/environment.d.ts +1 -0
  12. package/dist/cli/service/index.d.ts +1 -1
  13. package/dist/cli/service/instance/manifest.d.ts +9 -0
  14. package/dist/cli.js +601 -113
  15. package/dist/cluster/peer-registry.d.ts +6 -0
  16. package/dist/config/schema.d.ts +4 -0
  17. package/dist/config.d.ts +13 -0
  18. package/dist/dashboard/handler.d.ts +92 -1
  19. package/dist/db/migrations/025_init_failure.d.ts +16 -0
  20. package/dist/db/migrations/026_event_log_lockfile_corrupt.d.ts +11 -0
  21. package/dist/db/migrations/027_workflow_timeout.d.ts +20 -0
  22. package/dist/db/migrations/028_org_settings_user_cache.d.ts +4 -0
  23. package/dist/db/migrations/029_dispatch_queue_attempts.d.ts +16 -0
  24. package/dist/db/migrations/030_held_runs_env_set_null.d.ts +13 -0
  25. package/dist/db/migrations/031_dispatch_queue_ack_deadline.d.ts +19 -0
  26. package/dist/db/migrations/032_org_settings_dispatch_ack_timeout.d.ts +14 -0
  27. package/dist/db/types.d.ts +52 -2
  28. package/dist/diagnostics/checks/index.d.ts +2 -1
  29. package/dist/diagnostics/checks/scaler.d.ts +13 -0
  30. package/dist/diagnostics/types.d.ts +5 -2
  31. package/dist/environments/environment-store.d.ts +14 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.js +362 -40
  34. package/dist/lockfile-cache.d.ts +1 -1
  35. package/dist/metrics/prometheus.d.ts +8 -0
  36. package/dist/orchestrator-core.d.ts +4 -1
  37. package/dist/pipeline/dispatch-matched-workflow.d.ts +9 -0
  38. package/dist/pipeline/inline-eval.d.ts +17 -2
  39. package/dist/pipeline/process-webhook.d.ts +19 -0
  40. package/dist/pipeline/processor.d.ts +6 -1
  41. package/dist/pipeline/test-pipeline.d.ts +10 -0
  42. package/dist/providers/github/lock-file.d.ts +1 -1
  43. package/dist/providers/internal/lock-file-fetcher.d.ts +3 -2
  44. package/dist/queue/job-queue.d.ts +53 -1
  45. package/dist/reporting/execution-tracker.d.ts +80 -7
  46. package/dist/routes/admin-environments.d.ts +1 -0
  47. package/dist/scaler/bare-metal-backend.d.ts +1 -0
  48. package/dist/scaler/container-backend.d.ts +3 -2
  49. package/dist/scaler/failure-tracker.d.ts +46 -0
  50. package/dist/scaler/firecracker-backend.d.ts +17 -0
  51. package/dist/scaler/manager.d.ts +10 -0
  52. package/dist/scaler/nftables.d.ts +25 -3
  53. package/dist/scaler/types.d.ts +26 -1
  54. package/dist/server.js +4421 -1459
  55. package/dist/stale-detector/workflow-deadline-detector.d.ts +49 -0
  56. package/dist/standalone.js +17003 -14561
  57. package/dist/storage/filesystem.d.ts +12 -3
  58. package/dist/storage/s3.d.ts +17 -4
  59. package/dist/storage/types.d.ts +25 -5
  60. package/dist/worker/in-memory-job-queue.d.ts +40 -7
  61. package/dist/ws/agent-handler.d.ts +13 -0
  62. package/dist/ws/dashboard-env-handler.d.ts +1 -0
  63. package/dist/ws/platform-client.d.ts +10 -1
  64. package/package.json +13 -10
  65. package/sbom.spdx.json +91 -36
@@ -21,6 +21,12 @@ export interface PeerAgentInfo {
21
21
  * in the required label set.
22
22
  */
23
23
  mandatoryLabels: string[];
24
+ /**
25
+ * Name of the scaler backend that spawned this agent, or null for static
26
+ * (stateful) agents not bound to any scaler. Surfaced in diagnostics so the
27
+ * dashboard can group a peer's agents under the correct scaler row.
28
+ */
29
+ scalerName?: string | null;
24
30
  }
25
31
  export interface PeerInfo {
26
32
  instanceId: string;
@@ -107,6 +107,8 @@ export declare const sharedConfigSchema: z.ZodObject<{
107
107
  cacheTtlDays: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
108
108
  cacheBuildTimeoutMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
109
109
  cacheMaxTarballBytes: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
110
+ userCacheQuotaBytes: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
111
+ userCacheTtlMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
110
112
  eventRouter: z.ZodOptional<z.ZodObject<{
111
113
  maxChainDepth: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
112
114
  rateLimitPerWorkflowPerMinute: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
@@ -189,6 +191,8 @@ export declare const appConfigSchema: z.ZodObject<{
189
191
  cacheTtlDays: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
190
192
  cacheBuildTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
191
193
  cacheMaxTarballBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
194
+ userCacheQuotaBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
195
+ userCacheTtlMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
192
196
  eventRouterMaxChainDepth: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
193
197
  eventRouterRateLimitPerWorkflowPerMinute: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
194
198
  eventRouterEventTtlSeconds: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
package/dist/config.d.ts CHANGED
@@ -36,6 +36,7 @@ declare const configSchema: z.ZodObject<{
36
36
  queueBackpressureThreshold: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
37
37
  workerConcurrency: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
38
38
  concurrencyWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
39
+ dispatchAckTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
39
40
  cacheStorageType: z.ZodOptional<z.ZodEnum<{
40
41
  s3: "s3";
41
42
  filesystem: "filesystem";
@@ -46,6 +47,7 @@ declare const configSchema: z.ZodObject<{
46
47
  cacheStorageS3Region: z.ZodOptional<z.ZodString>;
47
48
  cacheStorageS3Endpoint: z.ZodOptional<z.ZodString>;
48
49
  cacheStorageS3ExternalEndpoint: z.ZodOptional<z.ZodString>;
50
+ cacheStorageS3UploadEndpoint: z.ZodOptional<z.ZodString>;
49
51
  cacheStorageS3ForcePathStyle: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
50
52
  true: "true";
51
53
  false: "false";
@@ -56,6 +58,8 @@ declare const configSchema: z.ZodObject<{
56
58
  cacheTtlDays: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
57
59
  cacheBuildTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
58
60
  cacheMaxTarballBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
61
+ userCacheQuotaBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
62
+ userCacheTtlMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
59
63
  webhookPayloadDir: z.ZodOptional<z.ZodString>;
60
64
  dataDir: z.ZodOptional<z.ZodString>;
61
65
  scalerConfigPath: z.ZodOptional<z.ZodString>;
@@ -149,12 +153,17 @@ export type AppConfig = z.infer<typeof configSchema> & {
149
153
  region?: string;
150
154
  endpoint?: string;
151
155
  externalEndpoint?: string;
156
+ uploadEndpoint?: string;
152
157
  forcePathStyle?: boolean;
153
158
  logBucket?: string;
154
159
  /** Filesystem backend: absolute base directory for cached blobs. */
155
160
  fsBasePath?: string;
156
161
  /** Filesystem backend: base URL the agent uses to reach this orchestrator. */
157
162
  fsBaseUrl?: string;
163
+ /** Per-org byte quota for the user-facing cache (UserCache). */
164
+ userCacheQuotaBytes?: number;
165
+ /** Per-entry TTL (ms) for the user-facing cache (UserCache). */
166
+ userCacheTtlMs?: number;
158
167
  };
159
168
  };
160
169
  /**
@@ -177,10 +186,13 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
177
186
  queueBackpressureThreshold: number;
178
187
  workerConcurrency: number;
179
188
  concurrencyWaitTimeoutMs: number;
189
+ dispatchAckTimeoutMs: number;
180
190
  cacheStorageS3Prefix: string;
181
191
  cacheTtlDays: number;
182
192
  cacheBuildTimeoutMs: number;
183
193
  cacheMaxTarballBytes: number;
194
+ userCacheQuotaBytes: number;
195
+ userCacheTtlMs: number;
184
196
  staleDetectorScanIntervalMs: number;
185
197
  staleDetectorThresholdMultiplier: number;
186
198
  jobHeartbeatIntervalMs: number;
@@ -234,6 +246,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
234
246
  cacheStorageS3Region?: string | undefined;
235
247
  cacheStorageS3Endpoint?: string | undefined;
236
248
  cacheStorageS3ExternalEndpoint?: string | undefined;
249
+ cacheStorageS3UploadEndpoint?: string | undefined;
237
250
  cacheStorageS3ForcePathStyle?: boolean | undefined;
238
251
  cacheStorageFsPath?: string | undefined;
239
252
  cacheStorageFsBaseUrl?: string | undefined;
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { type Kysely } from 'kysely';
15
15
  import { type ColdStore } from '@kici-dev/shared';
16
- import type { DashboardRunDetailRequest, DashboardStepLogsRequest, DashboardPayloadRequest, DashboardOrchLogsRequest, DashboardEventLogListRequest, DashboardEventLogDetailRequest, DashboardEventLogPayloadStreamRequest, DashboardAccessLogListRequest, DashboardEventDlqListRequest, DashboardEventDlqCountRequest, DashboardEventDlqRetryRequest, DashboardEventDlqDiscardRequest, RunRerunRequest, RunCancelRequest, ManualScheduleRequest } from '@kici-dev/engine';
16
+ import type { DashboardRunDetailRequest, DashboardRunsListRequest, DashboardRunsListResponse, DashboardRunsFiltersRequest, DashboardRunsFiltersResponse, DashboardSourcesListRequest, DashboardSourcesListResponse, DashboardStepLogsRequest, DashboardPayloadRequest, DashboardOrchLogsRequest, DashboardEventLogListRequest, DashboardEventLogDetailRequest, DashboardEventLogPayloadStreamRequest, DashboardAccessLogListRequest, DashboardEventDlqListRequest, DashboardEventDlqCountRequest, DashboardEventDlqRetryRequest, DashboardEventDlqDiscardRequest, RunRerunRequest, RunCancelRequest, ManualScheduleRequest } from '@kici-dev/engine';
17
17
  import type { Database } from '../db/types.js';
18
18
  import type { LogStorage } from '../reporting/log-storage.js';
19
19
  import type { AccessLogWriter } from '../audit/access-log.js';
@@ -155,6 +155,97 @@ export declare class DashboardHandler {
155
155
  * builds a nested job/step tree, and sends the response.
156
156
  */
157
157
  handleRunDetail(msg: DashboardRunDetailRequest): Promise<void>;
158
+ /**
159
+ * Resolve every routing key owned by an org by unioning both source
160
+ * tables. The orchestrator is single-org but multi-routing-key: one org
161
+ * owns one GitHub-app source plus N generic sources, each with its own
162
+ * `routing_key`. `execution_runs` has no `org_id` column — org ownership
163
+ * is expressed purely through `routing_key → sources.customer_id` /
164
+ * `generic_webhook_sources.customer_id`, so scoping a run query to an org
165
+ * means scoping it to the union of that org's routing keys.
166
+ *
167
+ * Predicates mirror the org-context resolver in `server.ts` and
168
+ * `resolveOrgForRoutingKey` above:
169
+ * - `sources`: `customer_id = orgId` excluding the `__default__` sentinel.
170
+ * - `generic_webhook_sources`: `customer_id = orgId` and not soft-deleted
171
+ * (`deleted_at is null`).
172
+ */
173
+ private resolveOrgRoutingKeys;
174
+ /**
175
+ * Resolve a per-routing-key source identity map for a set of routing keys,
176
+ * unioning both source tables. Uses the identical name/subtype/provider
177
+ * derivation as `handleSourcesList` so a run summary's `source` block
178
+ * matches what the sources page shows for the same routing key. Routing
179
+ * keys with no live source row are simply absent from the map (the caller
180
+ * falls back to the run's own provider).
181
+ */
182
+ private resolveSourceIdentities;
183
+ /**
184
+ * Look up the page's jobs in one query to derive per-run `jobCount` plus the
185
+ * compile-job markers (`hadCompileJob` / `compileJobId`). The compile job is
186
+ * the synthetic `__build__*` job KiCI inserts for the compile phase — the
187
+ * same definition the Platform run-list route uses
188
+ * (`job_name LIKE '__build__%'`). Returns a per-run aggregate keyed by
189
+ * `run_id`; runs with no job rows are absent (the caller omits the fields).
190
+ */
191
+ private resolveRunJobAggregates;
192
+ /**
193
+ * Handle a dashboard.runs.list request.
194
+ *
195
+ * Returns a page of run summaries from `execution_runs`, scoped to ALL
196
+ * routing keys owned by this orchestrator's bound org (`this.orgId`). The
197
+ * orchestrator is single-org but multi-routing-key, and the bound
198
+ * `this.routingKey` is just ONE of the org's keys (resolved
199
+ * non-deterministically at startup), so filtering on it alone would drop
200
+ * runs that arrived under a sibling routing key. We resolve the org's full
201
+ * routing-key set from `sources` + `generic_webhook_sources` and filter on
202
+ * the union. Attribution still uses the handler-bound `orgId` /
203
+ * `routingKey` directly — the same inline-ctx pattern as the org-scoped
204
+ * event-log / access-log handlers.
205
+ *
206
+ * Used by the operator console (`support-read` break-glass) and the
207
+ * dashboard's run-list view. The access_log row carries the wire actor
208
+ * (including the platform_operator reason) so the customer can audit any
209
+ * operator read.
210
+ */
211
+ handleRunsList(msg: DashboardRunsListRequest): Promise<DashboardRunsListResponse>;
212
+ /**
213
+ * Handle a dashboard.runs.filters request.
214
+ *
215
+ * Returns the distinct filter-option values the customer runs page renders
216
+ * in its filter controls — statuses / workflows / branches / repositories /
217
+ * triggerTypes / sources — scoped to ALL routing keys owned by this
218
+ * orchestrator's bound org (same scoping as `handleRunsList`). Distinct
219
+ * values are derived in-memory from a single column projection over
220
+ * `execution_runs`; `sources` reuses `resolveSourceIdentities` so the list
221
+ * matches what the sources page shows for the same routing key.
222
+ *
223
+ * `triggerTypes` is populated from `execution_runs.provider` — the
224
+ * orchestrator has no dedicated trigger-event column, so `provider`
225
+ * (`github` / `generic` / `internal`) is the closest available
226
+ * discriminator the customer filter can offer.
227
+ *
228
+ * Attribution uses the handler-bound `orgId` / `routingKey` directly — the
229
+ * same inline-ctx pattern as `handleRunsList`. The access_log row carries
230
+ * the wire actor (including the platform_operator reason) so the customer
231
+ * can audit any operator read.
232
+ */
233
+ handleRunsFilters(msg: DashboardRunsFiltersRequest): Promise<DashboardRunsFiltersResponse>;
234
+ /**
235
+ * Handle a dashboard.sources.list request.
236
+ *
237
+ * Returns the org's webhook source summaries by unioning both source
238
+ * tables (GitHub-app `sources` + live `generic_webhook_sources`), scoped
239
+ * to this orchestrator's bound org (`this.orgId`). Source counts are tiny
240
+ * so the page is returned unpaginated (`msg.limit` is ignored). Secret-
241
+ * bearing columns (`config`, `git_config`, `verification_config`) are
242
+ * never projected — `git_config` is read only to derive the subtype.
243
+ *
244
+ * Attribution uses the handler-bound `orgId` / `routingKey` directly —
245
+ * the same inline-ctx pattern as `handleRunsList`. The access_log row
246
+ * carries the wire actor so the customer can audit any operator read.
247
+ */
248
+ handleSourcesList(msg: DashboardSourcesListRequest): Promise<DashboardSourcesListResponse>;
158
249
  /**
159
250
  * Handle a dashboard.step.logs request.
160
251
  * Looks up the step's log_path and reads content from LogStorage.
@@ -0,0 +1,16 @@
1
+ import { type Kysely } from 'kysely';
2
+ /**
3
+ * Add `init_failure jsonb` columns to `execution_runs` and `execution_jobs`.
4
+ *
5
+ * Presence of this column on a row means the run/job never executed a step
6
+ * because of an init-phase failure; absence (NULL) means a normal run.
7
+ * Shape on the wire is `InitFailure` from `@kici-dev/engine`. The dashboard
8
+ * reads this column directly so it can render the right banner without
9
+ * round-tripping to the orchestrator (which may be offline).
10
+ *
11
+ * Idempotent: re-running on a DB that already has either column is a no-op
12
+ * for that column.
13
+ */
14
+ export declare function up(db: Kysely<unknown>): Promise<void>;
15
+ export declare function down(db: Kysely<unknown>): Promise<void>;
16
+ //# sourceMappingURL=025_init_failure.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { type Kysely } from 'kysely';
2
+ /**
3
+ * Extend the event_log.status CHECK constraint with 'lockfile_corrupt' so the
4
+ * orchestrator can log a delivery whose lock file was present but unparseable.
5
+ * Orchestrator-only: the Platform event_log uses a separate, narrower status set.
6
+ *
7
+ * Idempotent: the DROP ... IF EXISTS / re-ADD pair re-runs cleanly.
8
+ */
9
+ export declare function up(db: Kysely<unknown>): Promise<void>;
10
+ export declare function down(db: Kysely<unknown>): Promise<void>;
11
+ //# sourceMappingURL=026_event_log_lockfile_corrupt.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { type Kysely } from 'kysely';
2
+ /**
3
+ * Add `workflow_timeout_ms integer` to `execution_runs`.
4
+ *
5
+ * Persists the workflow-level wall-clock timeout (in milliseconds) read from
6
+ * the lock workflow at run creation. NULL means no workflow-level cap is
7
+ * configured. The WorkflowDeadlineDetector reads this column to find runs
8
+ * whose `started_at + workflow_timeout_ms` has passed and cancels them with
9
+ * the distinct workflow-timeout reason.
10
+ *
11
+ * Typed INTEGER to match the existing `*_ms` columns (`duration_ms`,
12
+ * `concurrency_timeout_ms`), which keeps the pg read representation a plain
13
+ * number rather than the BIGINT-as-string shape. INTEGER caps the timeout at
14
+ * ~24.8 days, far beyond any sane workflow wall-clock budget.
15
+ *
16
+ * Idempotent: re-running on a DB that already has the column is a no-op.
17
+ */
18
+ export declare function up(db: Kysely<unknown>): Promise<void>;
19
+ export declare function down(db: Kysely<unknown>): Promise<void>;
20
+ //# sourceMappingURL=027_workflow_timeout.d.ts.map
@@ -0,0 +1,4 @@
1
+ import { type Kysely } from 'kysely';
2
+ export declare function up(db: Kysely<unknown>): Promise<void>;
3
+ export declare function down(db: Kysely<unknown>): Promise<void>;
4
+ //# sourceMappingURL=028_org_settings_user_cache.d.ts.map
@@ -0,0 +1,16 @@
1
+ import { type Kysely } from 'kysely';
2
+ /**
3
+ * Add `dispatch_queue.dispatch_attempts INT NOT NULL DEFAULT 0`.
4
+ *
5
+ * Counts how many times a job has been returned to `pending` for
6
+ * re-dispatch after a failed delivery attempt (agent sent job.reject, or
7
+ * a scaler-managed agent disconnected before the job started). The
8
+ * dispatcher fails the job permanently once the counter reaches
9
+ * MAX_DISPATCH_ATTEMPTS, bounding requeue loops; `expires_at` remains the
10
+ * time-based backstop.
11
+ *
12
+ * Idempotent: re-running on a DB that already has the column is a no-op.
13
+ */
14
+ export declare function up(db: Kysely<unknown>): Promise<void>;
15
+ export declare function down(db: Kysely<unknown>): Promise<void>;
16
+ //# sourceMappingURL=029_dispatch_queue_attempts.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { type Kysely } from 'kysely';
2
+ /**
3
+ * held_runs.environment_id becomes nullable with ON DELETE SET NULL so
4
+ * terminal held-run history survives environment deletion (a null
5
+ * environment_id means the environment was since deleted). Pending held
6
+ * runs still block deletion — enforced in EnvironmentStore.delete.
7
+ *
8
+ * Idempotent: dropping the NOT NULL and the constraint are both no-ops on a
9
+ * re-run, and the constraint is re-created with the SET NULL action.
10
+ */
11
+ export declare function up(db: Kysely<unknown>): Promise<void>;
12
+ export declare function down(db: Kysely<unknown>): Promise<void>;
13
+ //# sourceMappingURL=030_held_runs_env_set_null.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { type Kysely } from 'kysely';
2
+ /**
3
+ * Add `dispatch_queue.ack_deadline TIMESTAMPTZ` and
4
+ * `dispatch_queue.ack_agent_id TEXT` persisting the per-dispatch
5
+ * acknowledgment deadline for HA-safe lost-dispatch detection.
6
+ *
7
+ * The dispatcher stamps both when a job.dispatch is sent and clears them
8
+ * when the agent answers (job.ack / job.reject / job.status running). A
9
+ * `dispatched` row past its deadline is requeued — by the owning coord's
10
+ * in-memory timer, or by the leader-gated sweep
11
+ * (`Dispatcher.sweepExpiredAckDeadlines`) when the owning coord crashed.
12
+ * On coord boot, `Dispatcher.recoverState()` re-arms timers from the
13
+ * persisted deadlines.
14
+ *
15
+ * Idempotent: re-running on a DB that already has either column is a no-op.
16
+ */
17
+ export declare function up(db: Kysely<unknown>): Promise<void>;
18
+ export declare function down(db: Kysely<unknown>): Promise<void>;
19
+ //# sourceMappingURL=031_dispatch_queue_ack_deadline.d.ts.map
@@ -0,0 +1,14 @@
1
+ import { type Kysely } from 'kysely';
2
+ /**
3
+ * Add `org_settings.dispatch_ack_timeout_ms BIGINT` (nullable).
4
+ *
5
+ * Per-org override of the dispatch-acknowledgment deadline. NULL falls
6
+ * back to the cluster-wide default (`KICI_DISPATCH_ACK_TIMEOUT_MS`,
7
+ * default 10s). Operators raise it on high-latency networks via
8
+ * `kici-admin org-settings`.
9
+ *
10
+ * Idempotent: a re-run on a DB that already has the column is a no-op.
11
+ */
12
+ export declare function up(db: Kysely<unknown>): Promise<void>;
13
+ export declare function down(db: Kysely<unknown>): Promise<void>;
14
+ //# sourceMappingURL=032_org_settings_dispatch_ack_timeout.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import type { ColumnType, Generated, Insertable, Selectable, Updateable } from 'kysely';
2
+ import type { InitFailure } from '@kici-dev/engine';
2
3
  /**
3
4
  * PostgreSQL-only database types.
4
5
  * Column names use snake_case matching the actual database column names.
@@ -157,6 +158,17 @@ export interface DispatchQueueTable {
157
158
  * real provisioning cause; cleared on dispatch. NULL when none recorded.
158
159
  */
159
160
  last_provisioning_error: ColumnType<string | null, string | null | undefined, string | null>;
161
+ /** Times this job was returned to pending for re-dispatch (job.reject / pre-start agent loss). */
162
+ dispatch_attempts: Generated<number>;
163
+ /**
164
+ * Deadline by which the dispatched job's agent must answer the
165
+ * job.dispatch (job.ack / job.reject / job.status running). Stamped when
166
+ * the dispatch is sent, cleared on any answer; `dispatched` rows past the
167
+ * deadline are requeued by the owning coord's timer or the leader sweep.
168
+ */
169
+ ack_deadline: ColumnType<Date | null, Date | null | undefined, Date | null>;
170
+ /** Agent the dispatch was sent to (for ack-timeout disconnect + logging). */
171
+ ack_agent_id: ColumnType<string | null, string | null | undefined, string | null>;
160
172
  }
161
173
  /**
162
174
  * Deduplication cache table
@@ -258,6 +270,19 @@ export interface ExecutionRunTable {
258
270
  contributor_username: string | null;
259
271
  /** Human-readable reason why the run failed (null for non-failed runs). */
260
272
  failure_reason: string | null;
273
+ /**
274
+ * Structured init-phase failure detail (shape: `InitFailure` from
275
+ * `@kici-dev/engine`). Non-null means the run never executed a step
276
+ * because something failed during the init phase (lock-file fetch,
277
+ * provider context, agent spawn). NULL for normal runs.
278
+ */
279
+ init_failure: ColumnType<InitFailure | null, unknown, unknown>;
280
+ /**
281
+ * Whole-run wall-clock timeout in ms from the workflow lock; null when
282
+ * unset. Read by the WorkflowDeadlineDetector. INTEGER in Postgres, matching
283
+ * the other `*_ms` columns, so pg returns a plain number.
284
+ */
285
+ workflow_timeout_ms: ColumnType<number | null, number | null | undefined, number | null>;
261
286
  /** When this record was created */
262
287
  created_at: Generated<Date>;
263
288
  /**
@@ -304,6 +329,13 @@ export interface ExecutionJobTable {
304
329
  log_bytes: Generated<number>;
305
330
  /** Error info if failed */
306
331
  error_message: string | null;
332
+ /**
333
+ * Structured init-phase failure detail (shape: `InitFailure` from
334
+ * `@kici-dev/engine`). Non-null means the job never executed a step
335
+ * because something failed during init (lock-file fetch, provider
336
+ * context, agent spawn). NULL for normal runs.
337
+ */
338
+ init_failure: ColumnType<InitFailure | null, unknown, unknown>;
307
339
  /** Labels used for agent routing (e.g. ["kici:os:linux", "kici:arch:x64"]). JSONB. */
308
340
  runs_on_labels: string | null;
309
341
  /** Last heartbeat received from agent (for stale run detection) */
@@ -580,8 +612,8 @@ export interface HeldRunsTable {
580
612
  run_id: string;
581
613
  /** Job ID within the run */
582
614
  job_id: string;
583
- /** Environment ID (FK to environments.id) */
584
- environment_id: string;
615
+ /** Environment ID (FK to environments.id); null once the environment is deleted */
616
+ environment_id: string | null;
585
617
  /** Hold type: 'approval' | 'wait_timer' | 'concurrency' */
586
618
  hold_type: string;
587
619
  /** Hold status: 'pending' | 'approved' | 'rejected' | 'expired' | 'released' */
@@ -1142,6 +1174,24 @@ export interface OrgSettingsTable {
1142
1174
  * treats missing keys as `true`.
1143
1175
  */
1144
1176
  dashboard_write_policy: ColumnType<Record<string, boolean>, Record<string, boolean> | string | undefined, Record<string, boolean> | string>;
1177
+ /**
1178
+ * Per-org byte quota for the user-facing cache (UserCache). NULL = use the
1179
+ * cluster-wide default (`KICI_USER_CACHE_QUOTA_BYTES`, 5 GiB). Postgres
1180
+ * BIGINT — pg returns a string on select; accept a number on insert/update.
1181
+ */
1182
+ user_cache_quota_bytes: ColumnType<string | null, number | null | undefined, number | null>;
1183
+ /**
1184
+ * Per-entry TTL (ms) for the user-facing cache (UserCache). NULL = use the
1185
+ * cluster-wide default (`KICI_USER_CACHE_TTL_MS`, 7 days). Postgres BIGINT —
1186
+ * pg returns a string on select; accept a number on insert/update.
1187
+ */
1188
+ user_cache_ttl_ms: ColumnType<string | null, number | null | undefined, number | null>;
1189
+ /**
1190
+ * Per-org dispatch-acknowledgment deadline (ms); null = cluster default
1191
+ * (config.dispatchAckTimeoutMs / KICI_DISPATCH_ACK_TIMEOUT_MS). Postgres
1192
+ * BIGINT — pg returns a string on select; accept a number on insert/update.
1193
+ */
1194
+ dispatch_ack_timeout_ms: ColumnType<string | null, number | null | undefined, number | null>;
1145
1195
  /** When this setting was created */
1146
1196
  created_at: Generated<Date>;
1147
1197
  /** When this setting was last updated */
@@ -11,7 +11,8 @@ import { checkAgentConnectivity } from './agents.js';
11
11
  import { checkDiskSpace } from './disk.js';
12
12
  import { checkConfigValidity } from './config.js';
13
13
  import { checkCertificateExpiry } from './certs.js';
14
+ import { checkScalerProvisioning } from './scaler.js';
14
15
  /** All diagnostic checks in display order. */
15
16
  export declare const defaultChecks: DiagnosticCheck[];
16
- export { checkDbConnectivity, checkWsToPlatform, checkAgentConnectivity, checkDiskSpace, checkConfigValidity, checkCertificateExpiry, };
17
+ export { checkDbConnectivity, checkWsToPlatform, checkAgentConnectivity, checkDiskSpace, checkConfigValidity, checkCertificateExpiry, checkScalerProvisioning, };
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Scaler provisioning diagnostic check.
3
+ *
4
+ * Reads recent scaler spawn failures (in-process, over a short rolling window)
5
+ * and emits one row per configured scaler backend instance. Severity is
6
+ * bound-aware: a warm-pool/unbound failure is a warning (no run impact yet); a
7
+ * job-bound failure is a failure (a queued run could not get an agent).
8
+ */
9
+ import type { DiagnosticDeps, DiagnosticResult } from '../types.js';
10
+ /** Rolling window for "recent" spawn failures. */
11
+ export declare const SCALER_FAILURE_WINDOW_MS: number;
12
+ export declare function checkScalerProvisioning(deps: DiagnosticDeps): Promise<DiagnosticResult[]>;
13
+ //# sourceMappingURL=scaler.d.ts.map
@@ -7,6 +7,7 @@
7
7
  import type { Kysely } from 'kysely';
8
8
  import type { Database } from '../db/types.js';
9
9
  import type { AgentRegistry } from '../agent/registry.js';
10
+ import type { ScalerManager } from '../scaler/manager.js';
10
11
  /** Result of a single diagnostic check. */
11
12
  export interface DiagnosticResult {
12
13
  /** Human-readable check name (e.g., "Database connectivity"). */
@@ -32,7 +33,9 @@ export interface DiagnosticDeps {
32
33
  config: Record<string, unknown>;
33
34
  /** TLS cert path (for expiry check). */
34
35
  tlsCertPath?: string;
36
+ /** Scaler manager for recent spawn-failure health (optional -- no scaler configured). */
37
+ scalerManager?: ScalerManager;
35
38
  }
36
- /** A diagnostic check function. */
37
- export type DiagnosticCheck = (deps: DiagnosticDeps) => Promise<DiagnosticResult>;
39
+ /** A diagnostic check function. May return one result or several (e.g. one per scaler backend). */
40
+ export type DiagnosticCheck = (deps: DiagnosticDeps) => Promise<DiagnosticResult | DiagnosticResult[]>;
38
41
  //# sourceMappingURL=types.d.ts.map
@@ -1,6 +1,11 @@
1
1
  import { type Kysely } from 'kysely';
2
2
  import type { Environment as EngineEnvironment } from '@kici-dev/engine';
3
3
  import type { Database, Environment } from '../db/types.js';
4
+ /** Thrown by `delete` when pending held runs reference the environment. */
5
+ export declare class EnvironmentDeleteBlockedError extends Error {
6
+ readonly pendingCount: number;
7
+ constructor(pendingCount: number);
8
+ }
4
9
  /**
5
10
  * Map a DB environment row (snake_case) to the engine Environment type (camelCase).
6
11
  *
@@ -60,7 +65,15 @@ export declare class EnvironmentStore {
60
65
  create(orgId: string, data: EnvironmentCreateInput): Promise<Environment>;
61
66
  /** Update an environment. Returns the updated row, or null if not found. */
62
67
  update(orgId: string, id: string, updates: EnvironmentUpdateInput): Promise<Environment | null>;
63
- /** Delete an environment. Cascade deletes bindings, variables, and overrides. */
68
+ /**
69
+ * Delete an environment.
70
+ *
71
+ * Bindings, variables, and source overrides cascade away via their FK.
72
+ * Terminal held-run history survives with a null `environment_id` (the FK
73
+ * uses ON DELETE SET NULL). Pending held runs still reference the
74
+ * environment, so deletion is blocked with `EnvironmentDeleteBlockedError`
75
+ * until they are approved or rejected.
76
+ */
64
77
  delete(orgId: string, id: string): Promise<boolean>;
65
78
  /**
66
79
  * Match an environment name against org environments.
package/dist/index.d.ts CHANGED
@@ -3,5 +3,6 @@ export { S3CacheStorage, type S3CacheStorageOptions } from './storage/s3.js';
3
3
  export { createCacheStorage } from './storage/index.js';
4
4
  export { SourceCache } from './cache/source-cache.js';
5
5
  export { DepCache } from './cache/dep-cache.js';
6
+ export { UserCache, DEFAULT_USER_CACHE_QUOTA_BYTES, DEFAULT_USER_CACHE_TTL_MS, type UserCacheRef, type UserCacheRestoreResult, type UserCacheBeginSaveResult, } from './cache/user-cache.js';
6
7
  export { PeerCredentialStore, createPeerCredentialStoreFromUrl, type PeerCredential, } from './cluster/peer-credentials.js';
7
8
  //# sourceMappingURL=index.d.ts.map