@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.
- package/README.md +13 -1
- package/dist/__test-helpers__/mock-db.d.ts +2 -0
- package/dist/agent/dispatcher.d.ts +110 -6
- package/dist/agent/registry.d.ts +14 -0
- package/dist/app.d.ts +36 -0
- package/dist/approvals/apply-decision.d.ts +60 -0
- package/dist/approvals/approval-resolver.d.ts +66 -0
- package/dist/approvals/step-approval-bridge.d.ts +72 -0
- package/dist/approvals/team-membership-lookup.d.ts +13 -0
- package/dist/cache/agent-job-failed-error.d.ts +13 -0
- package/dist/cache/dispatch-cache-ref-tracker.d.ts +45 -0
- package/dist/cache/index.d.ts +2 -0
- package/dist/cache/user-cache.d.ts +116 -0
- package/dist/cancel/cancel-run.d.ts +56 -0
- package/dist/cli/api-client.d.ts +24 -0
- package/dist/cli/commands/environment.d.ts +1 -0
- package/dist/cli/commands/firecracker/index.d.ts +11 -0
- package/dist/cli/commands/firecracker/provision.d.ts +13 -0
- package/dist/cli/commands/firecracker/teardown.d.ts +3 -0
- package/dist/cli/commands/firecracker/verify.d.ts +3 -0
- package/dist/cli/commands/scaler.d.ts +18 -0
- package/dist/cli/kici-admin.d.ts +10 -1
- package/dist/cli/service/image-digests.d.ts +21 -0
- package/dist/cli.js +4148 -455
- package/dist/cluster/peer-client.d.ts +23 -1
- package/dist/cluster/peer-handler.d.ts +9 -1
- package/dist/cluster/peer-registry.d.ts +6 -0
- package/dist/config/schema.d.ts +4 -0
- package/dist/config.d.ts +13 -0
- package/dist/dashboard/handler.d.ts +92 -1
- package/dist/db/migrations/026_event_log_lockfile_corrupt.d.ts +11 -0
- package/dist/db/migrations/027_workflow_timeout.d.ts +20 -0
- package/dist/db/migrations/028_org_settings_user_cache.d.ts +4 -0
- package/dist/db/migrations/029_dispatch_queue_attempts.d.ts +16 -0
- package/dist/db/migrations/030_held_runs_env_set_null.d.ts +13 -0
- package/dist/db/migrations/031_dispatch_queue_ack_deadline.d.ts +19 -0
- package/dist/db/migrations/032_org_settings_dispatch_ack_timeout.d.ts +14 -0
- package/dist/db/migrations/033_org_settings_approval.d.ts +19 -0
- package/dist/db/migrations/034_held_runs_generalize.d.ts +24 -0
- package/dist/db/types.d.ts +89 -3
- package/dist/diagnostics/bundle-writer.d.ts +1 -17
- package/dist/diagnostics/checks/firecracker-network.d.ts +13 -0
- package/dist/diagnostics/checks/index.d.ts +2 -1
- package/dist/diagnostics/fleet-collector.d.ts +52 -0
- package/dist/diagnostics/fleet-constants.d.ts +8 -0
- package/dist/diagnostics/fleet-selection.d.ts +15 -0
- package/dist/diagnostics/fleet-topology.d.ts +47 -0
- package/dist/diagnostics/fleet-wiring.d.ts +60 -0
- package/dist/environments/environment-store.d.ts +14 -1
- package/dist/environments/held-runs.d.ts +69 -1
- package/dist/firecracker/host-network.d.ts +83 -0
- package/dist/firecracker/persist.d.ts +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +380 -40
- package/dist/lockfile-cache.d.ts +1 -1
- package/dist/metrics/prometheus.d.ts +8 -0
- package/dist/orchestrator-core.d.ts +13 -2
- package/dist/pipeline/dispatch-matched-workflow.d.ts +9 -0
- package/dist/pipeline/inline-eval.d.ts +17 -2
- package/dist/pipeline/process-webhook.d.ts +19 -0
- package/dist/pipeline/processor.d.ts +28 -1
- package/dist/pipeline/test-pipeline.d.ts +10 -0
- package/dist/providers/github/lock-file.d.ts +1 -1
- package/dist/providers/internal/lock-file-fetcher.d.ts +3 -2
- package/dist/queue/job-queue.d.ts +53 -1
- package/dist/reporting/execution-tracker.d.ts +3 -1
- package/dist/routes/admin-environments.d.ts +1 -0
- package/dist/routes/fleet.d.ts +20 -0
- package/dist/scaler/bare-metal-backend.d.ts +1 -0
- package/dist/scaler/config.d.ts +4 -2
- package/dist/scaler/container-backend.d.ts +3 -2
- package/dist/scaler/disk-guard.d.ts +27 -0
- package/dist/scaler/firecracker-backend.d.ts +35 -0
- package/dist/scaler/manager.d.ts +8 -0
- package/dist/scaler/nftables.d.ts +29 -3
- package/dist/scaler/reap-orphans.d.ts +27 -0
- package/dist/scaler/types.d.ts +28 -1
- package/dist/server.js +6138 -1587
- package/dist/stale-detector/stale-run-detector.d.ts +18 -0
- package/dist/stale-detector/workflow-deadline-detector.d.ts +49 -0
- package/dist/standalone.js +18499 -14925
- package/dist/storage/filesystem.d.ts +14 -4
- package/dist/storage/s3.d.ts +19 -5
- package/dist/storage/types.d.ts +32 -5
- package/dist/worker/in-memory-job-queue.d.ts +40 -7
- package/dist/ws/agent-handler.d.ts +45 -0
- package/dist/ws/dashboard-env-handler.d.ts +36 -0
- package/dist/ws/fleet-agent-collector.d.ts +23 -0
- package/dist/ws/platform-client.d.ts +10 -1
- package/installer-image-digests.json +7 -0
- package/package.json +15 -11
- 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
|
}
|
package/dist/storage/s3.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/storage/types.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 —
|
|
47
|
+
/** No-op — the job is already tracked in `dispatched` from dequeue. */
|
|
44
48
|
markDispatched(_jobId: string, _agentId: string): Promise<void>;
|
|
45
|
-
/**
|
|
46
|
-
markFailed(
|
|
47
|
-
/**
|
|
48
|
-
markCompleted(
|
|
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
|
-
/**
|
|
56
|
-
getJobById(
|
|
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?;
|
package/package.json
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/orchestrator",
|
|
3
|
-
"version": "0.1.
|
|
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
|
-
"
|
|
9
|
-
"
|
|
7
|
+
"cicd",
|
|
8
|
+
"continuous-integration",
|
|
10
9
|
"typescript",
|
|
11
|
-
"
|
|
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.
|
|
86
|
-
"@kici-dev/shared": "0.1.
|
|
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.
|
|
104
|
+
"@kici-dev/agent": "0.1.16"
|
|
101
105
|
},
|
|
102
106
|
"scripts": {
|
|
103
107
|
"build": "node ../../scripts/build-service.mjs && tsc --emitDeclarationOnly",
|