@kici-dev/orchestrator 0.1.14 → 0.1.16

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 (92) 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 +36 -0
  6. package/dist/approvals/apply-decision.d.ts +60 -0
  7. package/dist/approvals/approval-resolver.d.ts +66 -0
  8. package/dist/approvals/step-approval-bridge.d.ts +72 -0
  9. package/dist/approvals/team-membership-lookup.d.ts +13 -0
  10. package/dist/cache/agent-job-failed-error.d.ts +13 -0
  11. package/dist/cache/dispatch-cache-ref-tracker.d.ts +45 -0
  12. package/dist/cache/index.d.ts +2 -0
  13. package/dist/cache/user-cache.d.ts +116 -0
  14. package/dist/cancel/cancel-run.d.ts +56 -0
  15. package/dist/cli/api-client.d.ts +24 -0
  16. package/dist/cli/commands/environment.d.ts +1 -0
  17. package/dist/cli/commands/firecracker/index.d.ts +11 -0
  18. package/dist/cli/commands/firecracker/provision.d.ts +13 -0
  19. package/dist/cli/commands/firecracker/teardown.d.ts +3 -0
  20. package/dist/cli/commands/firecracker/verify.d.ts +3 -0
  21. package/dist/cli/commands/scaler.d.ts +18 -0
  22. package/dist/cli/kici-admin.d.ts +10 -1
  23. package/dist/cli/service/image-digests.d.ts +21 -0
  24. package/dist/cli.js +4148 -455
  25. package/dist/cluster/peer-client.d.ts +23 -1
  26. package/dist/cluster/peer-handler.d.ts +9 -1
  27. package/dist/cluster/peer-registry.d.ts +6 -0
  28. package/dist/config/schema.d.ts +4 -0
  29. package/dist/config.d.ts +13 -0
  30. package/dist/dashboard/handler.d.ts +92 -1
  31. package/dist/db/migrations/026_event_log_lockfile_corrupt.d.ts +11 -0
  32. package/dist/db/migrations/027_workflow_timeout.d.ts +20 -0
  33. package/dist/db/migrations/028_org_settings_user_cache.d.ts +4 -0
  34. package/dist/db/migrations/029_dispatch_queue_attempts.d.ts +16 -0
  35. package/dist/db/migrations/030_held_runs_env_set_null.d.ts +13 -0
  36. package/dist/db/migrations/031_dispatch_queue_ack_deadline.d.ts +19 -0
  37. package/dist/db/migrations/032_org_settings_dispatch_ack_timeout.d.ts +14 -0
  38. package/dist/db/migrations/033_org_settings_approval.d.ts +19 -0
  39. package/dist/db/migrations/034_held_runs_generalize.d.ts +24 -0
  40. package/dist/db/types.d.ts +89 -3
  41. package/dist/diagnostics/bundle-writer.d.ts +1 -17
  42. package/dist/diagnostics/checks/firecracker-network.d.ts +13 -0
  43. package/dist/diagnostics/checks/index.d.ts +2 -1
  44. package/dist/diagnostics/fleet-collector.d.ts +52 -0
  45. package/dist/diagnostics/fleet-constants.d.ts +8 -0
  46. package/dist/diagnostics/fleet-selection.d.ts +15 -0
  47. package/dist/diagnostics/fleet-topology.d.ts +47 -0
  48. package/dist/diagnostics/fleet-wiring.d.ts +60 -0
  49. package/dist/environments/environment-store.d.ts +14 -1
  50. package/dist/environments/held-runs.d.ts +69 -1
  51. package/dist/firecracker/host-network.d.ts +83 -0
  52. package/dist/firecracker/persist.d.ts +14 -0
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.js +380 -40
  55. package/dist/lockfile-cache.d.ts +1 -1
  56. package/dist/metrics/prometheus.d.ts +8 -0
  57. package/dist/orchestrator-core.d.ts +13 -2
  58. package/dist/pipeline/dispatch-matched-workflow.d.ts +9 -0
  59. package/dist/pipeline/inline-eval.d.ts +17 -2
  60. package/dist/pipeline/process-webhook.d.ts +19 -0
  61. package/dist/pipeline/processor.d.ts +28 -1
  62. package/dist/pipeline/test-pipeline.d.ts +10 -0
  63. package/dist/providers/github/lock-file.d.ts +1 -1
  64. package/dist/providers/internal/lock-file-fetcher.d.ts +3 -2
  65. package/dist/queue/job-queue.d.ts +53 -1
  66. package/dist/reporting/execution-tracker.d.ts +3 -1
  67. package/dist/routes/admin-environments.d.ts +1 -0
  68. package/dist/routes/fleet.d.ts +20 -0
  69. package/dist/scaler/bare-metal-backend.d.ts +1 -0
  70. package/dist/scaler/config.d.ts +4 -2
  71. package/dist/scaler/container-backend.d.ts +3 -2
  72. package/dist/scaler/disk-guard.d.ts +27 -0
  73. package/dist/scaler/firecracker-backend.d.ts +35 -0
  74. package/dist/scaler/manager.d.ts +8 -0
  75. package/dist/scaler/nftables.d.ts +29 -3
  76. package/dist/scaler/reap-orphans.d.ts +27 -0
  77. package/dist/scaler/types.d.ts +28 -1
  78. package/dist/server.js +6138 -1587
  79. package/dist/stale-detector/stale-run-detector.d.ts +18 -0
  80. package/dist/stale-detector/workflow-deadline-detector.d.ts +49 -0
  81. package/dist/standalone.js +18499 -14925
  82. package/dist/storage/filesystem.d.ts +14 -4
  83. package/dist/storage/s3.d.ts +19 -5
  84. package/dist/storage/types.d.ts +32 -5
  85. package/dist/worker/in-memory-job-queue.d.ts +40 -7
  86. package/dist/ws/agent-handler.d.ts +45 -0
  87. package/dist/ws/dashboard-env-handler.d.ts +36 -0
  88. package/dist/ws/fleet-agent-collector.d.ts +23 -0
  89. package/dist/ws/platform-client.d.ts +10 -1
  90. package/installer-image-digests.json +7 -0
  91. package/package.json +15 -11
  92. package/sbom.spdx.json +62 -57
@@ -0,0 +1,116 @@
1
+ import type { CacheRefScope } from '@kici-dev/engine';
2
+ import type { CacheStorage } from '../storage/types.js';
3
+ /**
4
+ * Cluster-wide default quota: 5 GiB. Serves as the fallback when an org has no
5
+ * per-org override in `org_settings.user_cache_quota_bytes`. The cluster-wide
6
+ * value is itself operator-configurable via KICI_USER_CACHE_QUOTA_BYTES.
7
+ */
8
+ export declare const DEFAULT_USER_CACHE_QUOTA_BYTES: number;
9
+ /**
10
+ * Cluster-wide default entry TTL: 7 days. Fallback when an org has no per-org
11
+ * override in `org_settings.user_cache_ttl_ms`. The cluster-wide value is
12
+ * operator-configurable via KICI_USER_CACHE_TTL_MS.
13
+ */
14
+ export declare const DEFAULT_USER_CACHE_TTL_MS: number;
15
+ /**
16
+ * Per-org override of the cache quota + TTL, read from `org_settings` at
17
+ * operation time. A field left `undefined` means "no per-org override" — the
18
+ * cluster-wide default applies. Injected into `UserCache` so the cache stays
19
+ * decoupled from the DB layer (and unit-testable with a stub reader).
20
+ */
21
+ export interface UserCacheOrgLimits {
22
+ quotaBytes?: number;
23
+ ttlMs?: number;
24
+ }
25
+ /** Resolves the per-org cache limits for an org id (e.g. an `org_settings` read). */
26
+ export type UserCacheOrgLimitsReader = (orgId: string) => Promise<UserCacheOrgLimits>;
27
+ /** Identifies the org + repo + write scope a cache operation targets. */
28
+ export interface UserCacheRef {
29
+ org: string;
30
+ repo: string;
31
+ scope: CacheRefScope;
32
+ /** Required when scope === 'isolated' — the per-run isolation namespace. */
33
+ runId?: string;
34
+ }
35
+ /** Outcome of a restore: whether an entry matched and how to fetch it. */
36
+ export interface UserCacheRestoreResult {
37
+ hit: boolean;
38
+ matchedKey?: string;
39
+ downloadUrl?: string;
40
+ tarHash?: string;
41
+ }
42
+ /** Outcome of begin-save: a presigned PUT to a temp key, or skip when the key already exists. */
43
+ export interface UserCacheBeginSaveResult {
44
+ skip: boolean;
45
+ uploadUrl?: string;
46
+ tempKey?: string;
47
+ }
48
+ export declare class UserCache {
49
+ private readonly storage;
50
+ /** Cluster-wide default quota (the `KICI_USER_CACHE_QUOTA_BYTES` value). */
51
+ private readonly defaultQuotaBytes;
52
+ /** Cluster-wide default TTL (the `KICI_USER_CACHE_TTL_MS` value). */
53
+ private readonly defaultTtlMs;
54
+ /** Optional per-org override reader; absent = always use the cluster defaults. */
55
+ private readonly orgLimitsReader?;
56
+ constructor(opts: {
57
+ storage: CacheStorage;
58
+ /** Cluster-wide default quota (env-var default). */
59
+ quotaBytes?: number;
60
+ /** Cluster-wide default TTL (env-var default). */
61
+ ttlMs?: number;
62
+ /** Per-org override reader (org_settings). When unset, defaults apply. */
63
+ orgLimitsReader?: UserCacheOrgLimitsReader;
64
+ });
65
+ /**
66
+ * Resolve the effective quota + TTL for an org: the per-org override from
67
+ * `org_settings` when present, otherwise the cluster-wide default. A reader
68
+ * failure falls back to the defaults (logged) — the cache must never fail a
69
+ * restore/save because the settings lookup hiccupped.
70
+ */
71
+ private resolveLimits;
72
+ /**
73
+ * Sanitize a path segment so a key can never escape its org/repo/scope
74
+ * namespace. Beyond stripping disallowed characters, a segment consisting
75
+ * only of dots (`.`, `..`, …) is replaced wholesale: such a segment is a
76
+ * dot-segment that HTTP/S3 path canonicalization collapses (`a/./b` → `a/b`,
77
+ * `a/../b` → `b`), which both corrupts the namespace and breaks the SigV4
78
+ * signature on a pre-signed PUT/GET. Repo identifiers like `.` (the internal
79
+ * provider's repo id) hit exactly this case, so the all-dots guard keeps the
80
+ * object key canonical and the namespace boundary intact.
81
+ */
82
+ private seg;
83
+ /** Org-level prefix: the per-tenant isolation boundary and quota scope. */
84
+ private orgPrefix;
85
+ /** Org + repo prefix shared by every scope of a repo. */
86
+ private repoPrefix;
87
+ /** Namespace prefix for the WRITE scope of a ref (shared OR per-run isolated). */
88
+ private writePrefix;
89
+ /** Namespace prefixes the ref may READ, in priority order. Isolated reads its own run scope, then shared. */
90
+ private readPrefixes;
91
+ private finalKey;
92
+ /** Restore: try the exact key across read prefixes, then restoreKeys prefix scan (newest wins). */
93
+ restore(ref: UserCacheRef & {
94
+ key: string;
95
+ restoreKeys?: string[];
96
+ }): Promise<UserCacheRestoreResult>;
97
+ /** Try the exact key in read-prefix priority order. */
98
+ private restoreExact;
99
+ /** restoreKeys prefix fallback (ordered); within a prefix, list() returns newest-first. */
100
+ private restoreByPrefix;
101
+ /** Begin a save: presigned PUT to a temp key, or skip=true when the immutable key exists. */
102
+ beginSave(ref: UserCacheRef & {
103
+ key: string;
104
+ }): Promise<UserCacheBeginSaveResult>;
105
+ /** Commit a save: copy temp -> final, init metadata, store companion hash/size, delete temp, enforce quota. */
106
+ commitSave(ref: UserCacheRef & {
107
+ key: string;
108
+ tarHash: string;
109
+ sizeBytes: number;
110
+ tempKey?: string;
111
+ }): Promise<void>;
112
+ private readHash;
113
+ /** Evict least-recently-used entries for the org until total tarball size <= the per-org quota. */
114
+ private enforceQuota;
115
+ }
116
+ //# sourceMappingURL=user-cache.d.ts.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Shared run-cancellation path.
3
+ *
4
+ * `cancelRunWithReason` is the single canonical implementation of "cancel
5
+ * this whole run, recording a reason". It is invoked by:
6
+ *
7
+ * - the operator-facing `POST /api/v1/admin/runs/:runId/cancel` route
8
+ * (user-initiated `kici cancel`), and
9
+ * - the WorkflowDeadlineDetector, which cancels runs that exceeded their
10
+ * workflow-level wall-clock timeout (TimeoutReason.workflow_timeout).
11
+ *
12
+ * Keeping one implementation means the deadline enforcer and the user cancel
13
+ * follow identical mechanics: send `job.cancel` to the agents running the
14
+ * run's jobs, cancel queued dispatch rows, mark pending/queued jobs cancelled,
15
+ * stamp the failure reason, and drive the run terminal when no agent work is
16
+ * outstanding.
17
+ */
18
+ import type { Kysely } from 'kysely';
19
+ import type { Database } from '../db/types.js';
20
+ import type { JobQueue } from '../queue/job-queue.js';
21
+ import type { Dispatcher } from '../agent/dispatcher.js';
22
+ import type { AgentRegistry } from '../agent/registry.js';
23
+ import type { ExecutionTracker } from '../reporting/execution-tracker.js';
24
+ export interface CancelRunDeps {
25
+ db: Kysely<Database>;
26
+ jobQueue: JobQueue;
27
+ dispatcher: Dispatcher;
28
+ registry: AgentRegistry;
29
+ executionTracker: ExecutionTracker;
30
+ }
31
+ export interface CancelRunOptions {
32
+ /** Force immediate SIGKILL on the agent (skip graceful hooks). */
33
+ force?: boolean;
34
+ /** Attribution stamped into execution_runs.cancelled_by. */
35
+ cancelledBy?: string;
36
+ }
37
+ export interface CancelRunResult {
38
+ /** Number of `job.cancel` messages dispatched to agents (running jobs). */
39
+ agentsNotified: number;
40
+ /** Number of pending/queued execution_jobs rows marked cancelled. */
41
+ pendingCancelled: number;
42
+ }
43
+ /**
44
+ * Cancel a whole run, recording `reason` on its cancelled jobs and (if not
45
+ * already set) on the run's failure_reason. Sends `job.cancel` to every agent
46
+ * holding a dispatched job for the run; cancels queued dispatch rows; marks
47
+ * pending/queued jobs cancelled. When no agent had outstanding work, drives
48
+ * the run to its terminal status immediately so the run does not linger in
49
+ * `cancelling`.
50
+ *
51
+ * Idempotent against terminal runs: callers should pre-check terminal state
52
+ * (the cancel route returns 409); the underlying UPDATEs are status-guarded so
53
+ * a double-call is harmless.
54
+ */
55
+ export declare function cancelRunWithReason(deps: CancelRunDeps, runId: string, reason: string, options?: CancelRunOptions): Promise<CancelRunResult>;
56
+ //# sourceMappingURL=cancel-run.d.ts.map
@@ -5,6 +5,19 @@
5
5
  * All methods are thin wrappers around fetch() that handle JSON serialization,
6
6
  * error formatting, and URL construction.
7
7
  */
8
+ /** A node in the fleet topology returned by GET /admin/fleet-topology. */
9
+ export interface FleetTopologyNodeResponse {
10
+ kind: 'orchestrator' | 'agent';
11
+ id: string;
12
+ role?: 'coordinator' | 'worker';
13
+ hostname?: string;
14
+ labels: Record<string, string>;
15
+ parentId: string | null;
16
+ }
17
+ /** Response shape for GET /admin/fleet-topology. */
18
+ export interface FleetTopologyResponse {
19
+ nodes: FleetTopologyNodeResponse[];
20
+ }
8
21
  /**
9
22
  * Universal-git configuration shape carried in `git_config` on
10
23
  * generic_webhook_sources. Matches `UniversalGitConfigSchema` in
@@ -71,6 +84,17 @@ export declare class AdminApiClient {
71
84
  * Public GET request returning raw response text.
72
85
  */
73
86
  getText(path: string): Promise<string>;
87
+ /** Enumerate the cluster topology for `debug-bundle --fleet --list` / `--pick`. */
88
+ getFleetTopology(): Promise<FleetTopologyResponse>;
89
+ /**
90
+ * Drive the fleet fan-out and write the assembled ZIP to `outPath`. The
91
+ * response is an octet-stream, so it is read as bytes rather than parsed JSON.
92
+ */
93
+ downloadFleetBundle(body: {
94
+ selectors: string[];
95
+ logWindowHours?: number;
96
+ timeoutSeconds?: number;
97
+ }, outPath: string): Promise<void>;
74
98
  listScopes(orgId: string): Promise<{
75
99
  scopes: string[];
76
100
  }>;
@@ -6,6 +6,7 @@
6
6
  * environment set-policy — update policy fields (branch, reviewers, timers, trust)
7
7
  * environment list — list environments for an org
8
8
  * environment show — show a single environment with variables + bindings
9
+ * environment delete — delete an environment (cascades bindings, variables, overrides; held-run history survives; pending held runs block with a clear error, resolved holds do not)
9
10
  * environment create-template — create/update an environment template + its seed variables
10
11
  *
11
12
  * Each command supports two modes (stage-4 pattern from `maintenance.ts`):
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `kici-admin firecracker` command group.
3
+ *
4
+ * Local host-networking ops for the orchestrator's Firecracker scaler:
5
+ * provision/verify/teardown the per-coordinator bridge + nft table, with
6
+ * --persist for reboot survival. These run directly on the host (privileged
7
+ * `ip`/`nft`/`systemctl`) and do NOT use the admin HTTP client.
8
+ */
9
+ import type { Command } from 'commander';
10
+ export declare function registerFirecrackerCommands(program: Command): void;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ import type { Command } from 'commander';
2
+ import { type FirecrackerBridgeConfig } from '../../../firecracker/host-network.js';
3
+ export interface ProvisionOptions {
4
+ bridge: string;
5
+ cidr?: string;
6
+ table?: string;
7
+ hostIface?: string;
8
+ persist?: boolean;
9
+ sudo?: boolean;
10
+ }
11
+ export declare function optionsToConfig(opts: ProvisionOptions): FirecrackerBridgeConfig;
12
+ export declare function registerProvision(group: Command): void;
13
+ //# sourceMappingURL=provision.d.ts.map
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerTeardown(group: Command): void;
3
+ //# sourceMappingURL=teardown.d.ts.map
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerVerify(group: Command): void;
3
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Standalone scaler maintenance commands for kici-admin.
3
+ *
4
+ * scaler reap-orphans Free leaked Firecracker/container resources WITHOUT a
5
+ * running orchestrator (recovery for a wedged node whose
6
+ * data disk is full).
7
+ *
8
+ * This command loads the orchestrator's LOCAL config (no HTTP admin API, no DB)
9
+ * and runs the liveness-driven reaper. It is the sanctioned operator path for
10
+ * the ENOSPC bootstrap deadlock: when the data disk is 100% full the
11
+ * orchestrator crash-loops before its in-process orphan sweep can run, so a
12
+ * tool that frees disk WITHOUT the orchestrator is required.
13
+ */
14
+ import type { Command } from 'commander';
15
+ /** Probe the local orchestrator /health endpoint. Healthy => it reaps itself. */
16
+ export declare function isOrchestratorHealthy(port: number, basePath: string): Promise<boolean>;
17
+ export declare function registerScalerCommands(program: Command): void;
18
+ //# sourceMappingURL=scaler.d.ts.map
@@ -9,5 +9,14 @@
9
9
  * --url / -u Orchestrator URL (env: KICI_ADMIN_URL, default: http://localhost:8080)
10
10
  * --token / -t Admin API token (env: KICI_ADMIN_TOKEN, required)
11
11
  */
12
- export {};
12
+ import { Command } from 'commander';
13
+ /**
14
+ * Build the kici-admin Commander program with every command group registered.
15
+ * Exported so the surface registry can walk the real command tree without
16
+ * parsing argv (no action runs during a tree walk, so `getClient` is
17
+ * constructed but never invoked).
18
+ */
19
+ export declare function buildProgram(): Command;
20
+ /** Build the program and parse argv — the bin-shim entry point. */
21
+ export declare function runCli(argv?: string[]): void;
13
22
  //# sourceMappingURL=kici-admin.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolves the customer installer's image reference from the digest record
3
+ * shipped with this package (installer-image-digests.json at the package root).
4
+ *
5
+ * Emits a manifest-list-digest-pinned ref so a `docker`/`podman pull` verifies
6
+ * the image hash — a registry that serves substituted bits fails the pull. When
7
+ * the record is missing (a dev tree that never ran a release) or lacks the
8
+ * image, falls back to the moving `:latest` tag and warns.
9
+ *
10
+ * The record resolves relative to this module's directory and works for both
11
+ * layouts: the source tree (`src/cli/service/` → the JSON is three dirs up at
12
+ * the package root) and the single-file bundle (`dist/cli.js` → one dir up).
13
+ */
14
+ /**
15
+ * Resolve `quay.io/kici-dev/<name>:<version>@sha256:<digest>` for an installer
16
+ * image. Falls back to `:latest` (with a warning) when no recorded digest exists.
17
+ */
18
+ export declare function resolveImageRef(name: string, opts?: {
19
+ filePath?: string;
20
+ }): string;
21
+ //# sourceMappingURL=image-digests.d.ts.map