@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
@@ -13,7 +13,7 @@
13
13
  * data file is byte-identical to what the agent receives over HTTP. TTL is
14
14
  * enforced lazily on access — same model as the S3 backend.
15
15
  */
16
- import type { CacheStorage } from './types.js';
16
+ import type { CacheMetadata, CacheStorage } from './types.js';
17
17
  export interface FilesystemCacheStorageOptions {
18
18
  /** Absolute path to the cache root directory. Created on first write if missing. */
19
19
  basePath: string;
@@ -46,14 +46,24 @@ export declare class FilesystemCacheStorage implements CacheStorage {
46
46
  private readMeta;
47
47
  private writeMeta;
48
48
  put(key: string, data: Buffer | string): Promise<void>;
49
- get(key: string): Promise<Buffer | null>;
50
- has(key: string): Promise<boolean>;
49
+ get(key: string, ttlMsOverride?: number): Promise<Buffer | null>;
50
+ has(key: string, ttlMsOverride?: number): Promise<boolean>;
51
51
  delete(key: string): Promise<boolean>;
52
52
  touch(key: string): Promise<void>;
53
- getUrl(key: string): Promise<string | null>;
53
+ getUrl(key: string, ttlMsOverride?: number): Promise<string | null>;
54
54
  getUploadUrl(key: string): Promise<string>;
55
55
  getInternalUploadUrl(key: string): Promise<string>;
56
56
  initMeta(key: string): Promise<void>;
57
+ list(subPrefix: string): Promise<string[]>;
58
+ getMetadata(key: string): Promise<CacheMetadata | null>;
59
+ copy(srcKey: string, destKey: string): Promise<void>;
60
+ /**
61
+ * Recursively collect data files under `dir`, returning each as a cache key
62
+ * relative to `basePath` paired with its mtime. Metadata sidecars
63
+ * (`*.meta.json`) and the atomic-write temp files (`*.tmp-*`) are skipped so
64
+ * `list()` only surfaces real cache objects.
65
+ */
66
+ private walkDataFiles;
57
67
  private signedUrl;
58
68
  private deleteFiles;
59
69
  }
@@ -11,7 +11,7 @@
11
11
  * TTL is enforced lazily: expired items are deleted on access.
12
12
  * Pre-signed URLs are generated for getUrl() with 15-minute expiry.
13
13
  */
14
- import type { CacheStorage } from './types.js';
14
+ import type { CacheMetadata, CacheStorage } from './types.js';
15
15
  export interface S3CacheStorageOptions {
16
16
  bucket: string;
17
17
  prefix: string;
@@ -26,6 +26,15 @@ export interface S3CacheStorageOptions {
26
26
  * need a container-routable address (e.g., container DNS name).
27
27
  */
28
28
  externalEndpoint?: string;
29
+ /**
30
+ * Separate endpoint for the host-facing (CLI) pre-signed upload URL.
31
+ * The CLI running `kici run remote` is on the developer's host, which may
32
+ * reach the bucket at a different address than the orchestrator's own
33
+ * `endpoint` (e.g. an orchestrator in a container uses the compose DNS name
34
+ * while the host CLI uses localhost). When unset, `getInternalUploadUrl`
35
+ * falls back to the internal `endpoint` client (current behavior).
36
+ */
37
+ uploadEndpoint?: string;
29
38
  /** Use path-style access instead of virtual-hosted-style (required for most S3-compatible services) */
30
39
  forcePathStyle?: boolean;
31
40
  }
@@ -33,13 +42,15 @@ export declare class S3CacheStorage implements CacheStorage {
33
42
  private readonly client;
34
43
  /** Separate client for pre-signed URL generation (uses externalEndpoint if configured). */
35
44
  private readonly presignClient;
45
+ /** Separate client for the host CLI's pre-signed upload URL (uses uploadEndpoint if configured). */
46
+ private readonly uploadPresignClient;
36
47
  private readonly bucket;
37
48
  private readonly prefix;
38
49
  private readonly ttlMs;
39
50
  constructor(options: S3CacheStorageOptions);
40
51
  /** Build the full S3 object key from a cache key. */
41
52
  private objectKey;
42
- /** Check if metadata indicates an expired item. */
53
+ /** Check if metadata indicates an expired item, honoring a per-op TTL override. */
43
54
  private isExpired;
44
55
  /**
45
56
  * Read metadata from an S3 object's custom headers.
@@ -54,14 +65,17 @@ export declare class S3CacheStorage implements CacheStorage {
54
65
  /** Check if an error is a "not found" error (NoSuchKey, NotFound, 404). */
55
66
  private isNotFoundError;
56
67
  put(key: string, data: Buffer | string): Promise<void>;
57
- get(key: string): Promise<Buffer | null>;
58
- has(key: string): Promise<boolean>;
68
+ get(key: string, ttlMsOverride?: number): Promise<Buffer | null>;
69
+ has(key: string, ttlMsOverride?: number): Promise<boolean>;
59
70
  delete(key: string): Promise<boolean>;
60
71
  touch(key: string): Promise<void>;
61
- getUrl(key: string): Promise<string | null>;
72
+ getUrl(key: string, ttlMsOverride?: number): Promise<string | null>;
62
73
  getUploadUrl(key: string): Promise<string>;
63
74
  getInternalUploadUrl(key: string): Promise<string>;
64
75
  initMeta(key: string): Promise<void>;
76
+ list(subPrefix: string): Promise<string[]>;
77
+ getMetadata(key: string): Promise<CacheMetadata | null>;
78
+ copy(srcKey: string, destKey: string): Promise<void>;
65
79
  /** Delete an S3 object. Idempotent (doesn't error if missing). */
66
80
  private deleteObject;
67
81
  }
@@ -29,10 +29,18 @@ export interface CacheMetadata {
29
29
  export interface CacheStorage {
30
30
  /** Store a value under the given key. Overwrites if key already exists. */
31
31
  put(key: string, data: Buffer | string): Promise<void>;
32
- /** Retrieve a value by key. Returns null if not found or expired. */
33
- get(key: string): Promise<Buffer | null>;
34
- /** Check if a key exists and is not expired, without retrieving data. */
35
- has(key: string): Promise<boolean>;
32
+ /**
33
+ * Retrieve a value by key. Returns null if not found or expired.
34
+ * `ttlMsOverride` applies a per-operation TTL instead of the backend's
35
+ * constructor-time default (lets per-org cache TTLs override the
36
+ * cluster-wide default without re-constructing the backend).
37
+ */
38
+ get(key: string, ttlMsOverride?: number): Promise<Buffer | null>;
39
+ /**
40
+ * Check if a key exists and is not expired, without retrieving data.
41
+ * `ttlMsOverride` applies a per-operation TTL (see `get`).
42
+ */
43
+ has(key: string, ttlMsOverride?: number): Promise<boolean>;
36
44
  /** Delete a key. Returns true if the key existed (even if expired). */
37
45
  delete(key: string): Promise<boolean>;
38
46
  /** Update lastAccessedAt to now, refreshing the TTL. No-op if key doesn't exist. */
@@ -40,8 +48,9 @@ export interface CacheStorage {
40
48
  /**
41
49
  * Get a pre-signed download URL for the cached item.
42
50
  * Returns null if not found or expired.
51
+ * `ttlMsOverride` applies a per-operation TTL (see `get`).
43
52
  */
44
- getUrl(key: string): Promise<string | null>;
53
+ getUrl(key: string, ttlMsOverride?: number): Promise<string | null>;
45
54
  /**
46
55
  * Generate a pre-signed PUT URL for direct agent upload.
47
56
  * Uses the external endpoint (container-routable) when configured.
@@ -61,6 +70,22 @@ export interface CacheStorage {
61
70
  * Sets created-at and last-accessed-at to now.
62
71
  */
63
72
  initMeta(key: string): Promise<void>;
73
+ /**
74
+ * List object keys (relative to the configured prefix) under a sub-prefix,
75
+ * newest first (by createdAt/last-modified metadata; falls back to
76
+ * lexicographic when metadata is unavailable). Returns full cache keys
77
+ * (without the storage prefix). Returns an empty array when nothing matches.
78
+ */
79
+ list(subPrefix: string): Promise<string[]>;
80
+ /** Server-side copy from one cache key to another (used for atomic temp -> final commit). */
81
+ copy(srcKey: string, destKey: string): Promise<void>;
82
+ /**
83
+ * Read the stored metadata (createdAt + lastAccessedAt) for a key WITHOUT
84
+ * refreshing lastAccessedAt. Returns null when the key is missing or carries
85
+ * no metadata. A pure read (no TTL-expiry side effect) — used by quota
86
+ * eviction to order candidates by access recency.
87
+ */
88
+ getMetadata(key: string): Promise<CacheMetadata | null>;
64
89
  }
65
90
  import type { SharedS3Config } from '@kici-dev/shared';
66
91
  /**
@@ -70,6 +95,8 @@ import type { SharedS3Config } from '@kici-dev/shared';
70
95
  export type S3CacheStorageConfig = SharedS3Config & {
71
96
  type: 's3';
72
97
  ttlMs: number;
98
+ /** Host-facing pre-signed upload endpoint (see S3CacheStorageOptions.uploadEndpoint). */
99
+ uploadEndpoint?: string;
73
100
  };
74
101
  /**
75
102
  * Filesystem backend config. Stores blobs as files under `basePath` and
@@ -12,6 +12,10 @@
12
12
  import { DispatchQueueStatus, type QueuedJobInput, type QueuedJob } from '../queue/job-queue.js';
13
13
  export declare class InMemoryJobQueue {
14
14
  private readonly jobs;
15
+ /** Jobs handed to an agent, retained so requeue / lookups can restore them. */
16
+ private readonly dispatched;
17
+ /** Per-job re-dispatch counter (parity with dispatch_queue.dispatch_attempts). */
18
+ private readonly attempts;
15
19
  /** Enqueue a job in memory and return its ID. */
16
20
  enqueue(input: QueuedJobInput): Promise<string>;
17
21
  /**
@@ -40,20 +44,49 @@ export declare class InMemoryJobQueue {
40
44
  dequeueById(jobId: string, agentLabels: string[], agentMandatoryLabels?: string[]): Promise<QueuedJob | null>;
41
45
  /** Return count of pending jobs. */
42
46
  getDepth(): Promise<number>;
43
- /** No-op — workers don't track dispatch status in a queue. */
47
+ /** No-op — the job is already tracked in `dispatched` from dequeue. */
44
48
  markDispatched(_jobId: string, _agentId: string): Promise<void>;
45
- /** No-op. */
46
- markFailed(_jobId: string, _reason: string): Promise<void>;
47
- /** No-op. */
48
- markCompleted(_jobId: string): Promise<void>;
49
+ /** Drop the dispatched entry — the job reached a terminal state. */
50
+ markFailed(jobId: string, _reason: string): Promise<void>;
51
+ /** Drop the dispatched entry — the job completed. */
52
+ markCompleted(jobId: string): Promise<void>;
49
53
  /** No-op. */
50
54
  markRecovering(_jobId: string): Promise<void>;
55
+ /**
56
+ * No-op. The worker's dispatch-ack deadline lives entirely in the
57
+ * in-memory timer; HA-safe persistence belongs to the coord's DB-backed
58
+ * queue, not the worker's in-memory queue.
59
+ */
60
+ setAckDeadline(_jobId: string, _deadline: Date, _agentId: string): Promise<void>;
61
+ /** No-op (see setAckDeadline). */
62
+ clearAckDeadline(_jobId: string): Promise<void>;
63
+ /** Always empty — the worker never rehydrates ack timers from a DB. */
64
+ getDispatchedAwaitingAck(): Promise<Array<{
65
+ id: string;
66
+ runId: string;
67
+ agentId: string | null;
68
+ deadline: Date;
69
+ }>>;
70
+ /** Always empty — the worker is never the leader running the ack sweep. */
71
+ listExpiredAckDeadlines(_now: Date): Promise<Array<{
72
+ id: string;
73
+ runId: string;
74
+ agentId: string | null;
75
+ }>>;
76
+ /**
77
+ * Return a dispatched job to pending for re-dispatch, bumping its attempt
78
+ * counter. Mirrors the DB-backed `JobQueue.requeue`. Returns the new
79
+ * attempt count, or null when the job is not currently dispatched.
80
+ */
81
+ requeue(jobId: string): Promise<number | null>;
82
+ /** Full job lookup across pending and dispatched jobs. */
83
+ getFullJobById(jobId: string): Promise<QueuedJob | null>;
51
84
  /** Return all pending jobs. */
52
85
  getPendingJobs(): Promise<QueuedJob[]>;
53
86
  /** Always returns empty array. */
54
87
  getDispatchedJobIdsByRunId(_runId: string): Promise<string[]>;
55
- /** Always returns null. */
56
- getJobById(_jobId: string): Promise<{
88
+ /** Lookup across pending and dispatched jobs (id / runId / status). */
89
+ getJobById(jobId: string): Promise<{
57
90
  id: string;
58
91
  runId: string;
59
92
  status: DispatchQueueStatus;
@@ -23,12 +23,15 @@ import type { AgentTokenStore } from '../agent/token-store.js';
23
23
  import type { OwnershipTracker } from '../agent/ownership-tracker.js';
24
24
  import type { SourceCache } from '../cache/source-cache.js';
25
25
  import type { DepCache } from '../cache/dep-cache.js';
26
+ import type { UserCache } from '../cache/user-cache.js';
27
+ import type { DispatchCacheRefTracker } from '../cache/dispatch-cache-ref-tracker.js';
26
28
  import type { PendingBuildTracker } from '../cache/pending-builds.js';
27
29
  import type { PendingInitTracker } from '../cache/pending-inits.js';
28
30
  import type { PendingDynamicTracker } from '../cache/pending-dynamics.js';
29
31
  import type { CacheStorage } from '../storage/types.js';
30
32
  import type { AgentMetricsAggregator } from '../metrics/agent-metrics-aggregator.js';
31
33
  import type { AgentApiRegistry } from './agent-api-registry.js';
34
+ import type { FleetAgentCollector } from './fleet-agent-collector.js';
32
35
  /**
33
36
  * Run the three token-bound authorization gates against a wire-supplied
34
37
  * `agent.register` payload. The gates are identical at first register
@@ -166,6 +169,17 @@ export interface AgentWsHandlerDeps {
166
169
  sourceCache?: SourceCache;
167
170
  /** Dep cache for generating pre-signed upload URLs. */
168
171
  depCache?: DepCache;
172
+ /** User-facing cache for serving restore/save requests from the sandbox. */
173
+ userCache?: UserCache;
174
+ /**
175
+ * Server-side jobId -> user-cache-namespace store. The orchestrator records
176
+ * `{orgId, repoId, cacheRefScope, runId}` per job at dispatch time; the
177
+ * `cache.user.*` handlers resolve the ref from HERE (keyed by the wire jobId),
178
+ * NEVER from the wire message body — a `cache.user.*` message carries only a
179
+ * `jobId` + `key`, so an agent can name a job it owns but can never influence
180
+ * which org/repo/scope that job resolves to.
181
+ */
182
+ dispatchCacheRefs?: DispatchCacheRefTracker;
169
183
  /** Cache storage for setting metadata after upload completion. */
170
184
  cacheStorage?: CacheStorage;
171
185
  /** Optional rate limiter configuration. */
@@ -195,6 +209,30 @@ export interface AgentWsHandlerDeps {
195
209
  action: 'proceed' | 'wait' | 'cancel';
196
210
  reason?: string;
197
211
  }>;
212
+ /**
213
+ * Optional callback when an agent blocks a `requireApproval` step. The server
214
+ * creates a step-scoped hold from the carried clauses and returns a promise
215
+ * that resolves when the hold is approved, rejected, or expired. The handler
216
+ * relays the resolution back to the originating agent as a
217
+ * `step.approval-resolved` message. The `agentId` lets the server drop the
218
+ * pending resolver when the agent disconnects.
219
+ */
220
+ onStepApproval?: (agentId: string, msg: {
221
+ runId: string;
222
+ jobId: string;
223
+ stepIndex: number;
224
+ stepName: string;
225
+ clauses: Array<{
226
+ team: string;
227
+ } | {
228
+ user: string;
229
+ }>;
230
+ reason: string;
231
+ timeoutSeconds?: number;
232
+ }) => Promise<{
233
+ outcome: 'approved' | 'rejected' | 'expired';
234
+ reason?: string;
235
+ }>;
198
236
  /**
199
237
  * Optional callback fired when an agent's WS connection closes, after the
200
238
  * dispatcher's own cleanup has run. Used by the long-poll concurrency
@@ -204,6 +242,13 @@ export interface AgentWsHandlerDeps {
204
242
  onConcurrencyAgentDisconnect?: (agentId: string) => void | Promise<void>;
205
243
  /** Agent private API registry for handling agent.api.request messages. */
206
244
  agentApiRegistry?: AgentApiRegistry;
245
+ /**
246
+ * Orchestrator-scoped collector that correlates fleet.logs.request with the
247
+ * agent's chunked fleet.bundle.chunk / fleet.bundle.error response. Routed to
248
+ * from the incoming-message switch; pending requests for a given agent are
249
+ * rejected when that agent's WS closes.
250
+ */
251
+ fleetAgentCollector?: FleetAgentCollector;
207
252
  }
208
253
  export declare function createAgentWsHandler(deps: AgentWsHandlerDeps): WSEvents;
209
254
  //# sourceMappingURL=agent-handler.d.ts.map
@@ -11,6 +11,8 @@
11
11
  * 3. Returns a response message with requestId and data (or error)
12
12
  */
13
13
  import { type Kysely } from 'kysely';
14
+ import type { HeldRunStore, ReleaseSignal } from '../environments/held-runs.js';
15
+ import type { TeamMembershipLookup } from '../approvals/approval-resolver.js';
14
16
  import type { DashboardPlatformToOrchMessage } from '@kici-dev/engine';
15
17
  import type { EnvironmentStore } from '../environments/environment-store.js';
16
18
  import type { VariableStore } from '../environments/variable-store.js';
@@ -44,6 +46,26 @@ export interface DashboardEnvHandlerDeps {
44
46
  accessLog?: AccessLogWriter;
45
47
  /** Routing key for access_log rows (null when not run-scoped). */
46
48
  routingKey?: string | null;
49
+ /**
50
+ * Held-run store + resume hook for the approve/reject flow. When present, the
51
+ * approve/reject handlers route through the shared `applyDecision` applier
52
+ * (eligibility check, real attribution, multi-clause accumulation, and the
53
+ * resume-after-approval re-dispatch). When absent (legacy / tests), the
54
+ * handlers fall back to the direct status flip.
55
+ */
56
+ approvals?: ApprovalHandlerDeps;
57
+ }
58
+ /** Dependencies enabling the resolver-backed approve/reject + resume path. */
59
+ export interface ApprovalHandlerDeps {
60
+ store: HeldRunStore;
61
+ /** Team name → member user ids (Plan-1 trust-policy cache). */
62
+ teamMembershipLookup: TeamMembershipLookup;
63
+ /** Re-dispatch a released job/workflow hold (consumes its pending context). */
64
+ resumeJob: (signal: ReleaseSignal) => Promise<void>;
65
+ /** Notify a waiting agent that a step hold released (step scope). */
66
+ resumeStep?: (signal: ReleaseSignal) => Promise<void>;
67
+ /** Notify a waiting agent that a step hold was rejected (step scope). */
68
+ rejectStep?: (heldRunId: string, reason?: string) => Promise<void> | void;
47
69
  }
48
70
  /**
49
71
  * Handler for all dashboard environment and held run WS messages.
@@ -81,6 +103,7 @@ export declare class DashboardEnvHandler {
81
103
  private handleEnvGet;
82
104
  private handleEnvCreate;
83
105
  private handleEnvUpdate;
106
+ private handleTestAccessSet;
84
107
  private handleEnvDelete;
85
108
  private handleVarsList;
86
109
  private handleVarSet;
@@ -106,6 +129,19 @@ export declare class DashboardEnvHandler {
106
129
  private handleScopeDelete;
107
130
  private handleEnvHistory;
108
131
  private handleHeldRunsList;
132
+ /**
133
+ * Resolve the actor's Keycloak sub for approval attribution. Only `user` and
134
+ * `platform_operator` actors carry a `sub`; others fall back to a stable id.
135
+ */
136
+ private actorSub;
137
+ /** Read org_settings.allow_self_approval (default true). */
138
+ private readAllowSelfApproval;
139
+ /**
140
+ * Route an approve/reject through the shared `applyDecision` applier:
141
+ * eligibility check, real attribution, multi-clause accumulation, and the
142
+ * resume-after-approval re-dispatch. Sends the matching response message.
143
+ */
144
+ private applyApprovalDecision;
109
145
  private handleHeldRunApprove;
110
146
  private handleHeldRunReject;
111
147
  private sendError;
@@ -0,0 +1,23 @@
1
+ export interface FleetAgentCollectorOptions {
2
+ timeoutMs: number;
3
+ }
4
+ export declare class FleetAgentCollector {
5
+ private readonly opts;
6
+ private readonly waiter;
7
+ /** requestId -> owning agentId, so a disconnect rejects only that agent's requests. */
8
+ private readonly requestAgent;
9
+ constructor(opts: FleetAgentCollectorOptions);
10
+ /**
11
+ * Register the pending request `requestId` (owned by `agentId`), invoke
12
+ * `send` to dispatch the fleet.logs.request envelope, and await the
13
+ * reassembled bundle Buffer.
14
+ */
15
+ request(requestId: string, agentId: string, send: () => void): Promise<Buffer>;
16
+ onChunk(requestId: string, seq: number, dataB64: string, isLast: boolean): void;
17
+ onError(requestId: string, message: string): void;
18
+ /** Reject every pending request owned by `agentId` (called on its disconnect). */
19
+ rejectAgent(agentId: string, reason: string): void;
20
+ /** Reject every pending request regardless of owner (orchestrator shutdown). */
21
+ rejectAll(reason: string): void;
22
+ }
23
+ //# sourceMappingURL=fleet-agent-collector.d.ts.map
@@ -1,4 +1,4 @@
1
- import { type OrchestratorToPlatformMessage, type WebhookRelay, type WebhookRelayResult, type TrustPolicyUpdate, type StaleCheckrunCleanup, type DashboardRunDetailRequest, type DashboardStepLogsRequest, type DashboardOrchLogsRequest, type RunRerunRequest, type ManualScheduleRequest, type RunCancelRequest, type DashboardPayloadRequest, type DashboardPlatformToOrchMessage, type DashboardDiagnosticsRequest, type DashboardScalerCapacityRequest, type DashboardScalerAgentsRequest, type JoinRequest, type JoinResponse, type OrchCapabilities, type OrchRole } from '@kici-dev/engine';
1
+ import { type OrchestratorToPlatformMessage, type WebhookRelay, type WebhookRelayResult, type TrustPolicyUpdate, type StaleCheckrunCleanup, type DashboardRunDetailRequest, type DashboardRunsListRequest, type DashboardRunsFiltersRequest, type DashboardSourcesListRequest, type DashboardStepLogsRequest, type DashboardOrchLogsRequest, type RunRerunRequest, type ManualScheduleRequest, type RunCancelRequest, type DashboardPayloadRequest, type DashboardPlatformToOrchMessage, type DashboardDiagnosticsRequest, type DashboardScalerCapacityRequest, type DashboardScalerAgentsRequest, type JoinRequest, type JoinResponse, type OrchCapabilities, type OrchRole } from '@kici-dev/engine';
2
2
  import { RelayBufferRegistry, type RelayStartMeta } from '../webhook/relay-buffer.js';
3
3
  /**
4
4
  * Verification + processing outcome returned by the chunked relay path's
@@ -89,6 +89,12 @@ export interface PlatformClientOptions {
89
89
  onAuthenticated?: () => void;
90
90
  /** Optional callback for dashboard run detail requests from Platform. */
91
91
  onDashboardRunDetail?: (msg: DashboardRunDetailRequest) => void;
92
+ /** Optional callback for dashboard runs.list (operator console) requests from Platform. */
93
+ onDashboardRunsList?: (msg: DashboardRunsListRequest) => void;
94
+ /** Optional callback for dashboard runs.filters (operator console) requests from Platform. */
95
+ onDashboardRunsFilters?: (msg: DashboardRunsFiltersRequest) => void;
96
+ /** Optional callback for dashboard sources.list (operator console) requests from Platform. */
97
+ onDashboardSourcesList?: (msg: DashboardSourcesListRequest) => void;
92
98
  /** Optional callback for dashboard step logs requests from Platform. */
93
99
  onDashboardStepLogs?: (msg: DashboardStepLogsRequest) => void;
94
100
  /** Optional callback for run re-run requests from Platform (dashboard action). */
@@ -175,6 +181,9 @@ export declare class PlatformClient {
175
181
  private readonly onPeerDiscover?;
176
182
  private readonly onAuthenticated?;
177
183
  private readonly onDashboardRunDetail?;
184
+ private readonly onDashboardRunsList?;
185
+ private readonly onDashboardRunsFilters?;
186
+ private readonly onDashboardSourcesList?;
178
187
  private readonly onDashboardStepLogs?;
179
188
  private readonly onRunRerun?;
180
189
  private readonly onManualSchedule?;
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": "0.1.16",
3
+ "images": {
4
+ "kici-agent": "sha256:817dfadfe941eb9e9096724c58d7af6d53665d813de0bc952c31090347b959da",
5
+ "kici-orchestrator": "sha256:85dd71ae39df06dad94defc19cc9152621640a5324e2008a83813cac001d979e"
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "@kici-dev/orchestrator",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Customer-deployable orchestrator for the KiCI CI/CD stack. Receives webhook events (direct or via Platform relay), matches triggers against the lock file, and dispatches jobs to connected agents.",
5
5
  "keywords": [
6
- "kici",
7
6
  "ci",
8
- "cd",
9
- "ci-cd",
7
+ "cicd",
8
+ "continuous-integration",
10
9
  "typescript",
11
- "workflows",
12
- "devops",
10
+ "workflow",
13
11
  "orchestrator",
14
- "webhook",
15
12
  "self-hosted"
16
13
  ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/kici-dev/kici-public.git",
17
+ "directory": "packages/orchestrator"
18
+ },
19
+ "bugs": "https://github.com/kici-dev/kici-public/issues",
17
20
  "homepage": "https://kici.dev",
18
21
  "author": {
19
22
  "name": "KiCI",
@@ -27,7 +30,8 @@
27
30
  "files": [
28
31
  "dist",
29
32
  "!dist/**/*.map",
30
- "sbom.spdx.json"
33
+ "sbom.spdx.json",
34
+ "installer-image-digests.json"
31
35
  ],
32
36
  "publishConfig": {
33
37
  "registry": "https://registry.npmjs.org/",
@@ -82,8 +86,8 @@
82
86
  "ws": "^8.20.0",
83
87
  "yaml": "^2.8.3",
84
88
  "zod": "^4.3.6",
85
- "@kici-dev/engine": "0.1.14",
86
- "@kici-dev/shared": "0.1.14"
89
+ "@kici-dev/engine": "0.1.16",
90
+ "@kici-dev/shared": "0.1.16"
87
91
  },
88
92
  "kici": {
89
93
  "metrics": {
@@ -97,7 +101,7 @@
97
101
  "@types/archiver": "^7.0.0",
98
102
  "@types/dockerode": "^4.0.1",
99
103
  "kysely-ctl": "^0.21.0",
100
- "@kici-dev/agent": "0.1.14"
104
+ "@kici-dev/agent": "0.1.16"
101
105
  },
102
106
  "scripts": {
103
107
  "build": "node ../../scripts/build-service.mjs && tsc --emitDeclarationOnly",