@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
|
@@ -12,7 +12,8 @@ import { checkDiskSpace } from './disk.js';
|
|
|
12
12
|
import { checkConfigValidity } from './config.js';
|
|
13
13
|
import { checkCertificateExpiry } from './certs.js';
|
|
14
14
|
import { checkScalerProvisioning } from './scaler.js';
|
|
15
|
+
import { checkFirecrackerNetwork } from './firecracker-network.js';
|
|
15
16
|
/** All diagnostic checks in display order. */
|
|
16
17
|
export declare const defaultChecks: DiagnosticCheck[];
|
|
17
|
-
export { checkDbConnectivity, checkWsToPlatform, checkAgentConnectivity, checkDiskSpace, checkConfigValidity, checkCertificateExpiry, checkScalerProvisioning, };
|
|
18
|
+
export { checkDbConnectivity, checkWsToPlatform, checkAgentConnectivity, checkDiskSpace, checkConfigValidity, checkCertificateExpiry, checkScalerProvisioning, checkFirecrackerNetwork, };
|
|
18
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursive fleet subtree assembler.
|
|
3
|
+
*
|
|
4
|
+
* Every orchestrator runs this to produce a self-similar subtree ZIP:
|
|
5
|
+
* local/ + nested agents/<id>.zip + workers/<id>.zip + peers/<id>.zip, plus
|
|
6
|
+
* fleet-manifest.json recording per-node status. Dead branches are recorded,
|
|
7
|
+
* never fatal (Promise.allSettled at each level). The loop guard
|
|
8
|
+
* (includeCoordinatorMesh) is true only on the root call; every downstream peer
|
|
9
|
+
* request sets it false so the coordinator mesh never echoes.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
/** Per-node collection outcome recorded in the fleet manifest. */
|
|
13
|
+
export declare const FleetNodeStatus: z.ZodEnum<{
|
|
14
|
+
error: "error";
|
|
15
|
+
ok: "ok";
|
|
16
|
+
timeout: "timeout";
|
|
17
|
+
unreachable: "unreachable";
|
|
18
|
+
}>;
|
|
19
|
+
export type FleetNodeStatus = z.infer<typeof FleetNodeStatus>;
|
|
20
|
+
export interface FleetCollectorDeps {
|
|
21
|
+
/** This orchestrator's instanceId (the manifest owner). */
|
|
22
|
+
instanceId: string;
|
|
23
|
+
/** Build this node's own debug bundle as a Buffer (createDebugBundle output). */
|
|
24
|
+
buildLocalBundle: () => Promise<Buffer>;
|
|
25
|
+
/** List this node's directly-connected agents. */
|
|
26
|
+
listAgents: () => {
|
|
27
|
+
agentId: string;
|
|
28
|
+
}[];
|
|
29
|
+
/** Request a connected agent's mini-bundle over the agent WS channel. */
|
|
30
|
+
requestAgentBundle: (agentId: string) => Promise<Buffer>;
|
|
31
|
+
/** List this node's downstream peers (coordinator-mesh peers + its workers). */
|
|
32
|
+
listPeers: () => {
|
|
33
|
+
instanceId: string;
|
|
34
|
+
role: 'coordinator' | 'worker';
|
|
35
|
+
kind: 'peer' | 'worker';
|
|
36
|
+
}[];
|
|
37
|
+
/** Request a peer's subtree bundle over the peer WS channel (loop-guarded false). */
|
|
38
|
+
requestPeerSubtree: (instanceId: string, includeCoordinatorMesh: boolean) => Promise<Buffer>;
|
|
39
|
+
}
|
|
40
|
+
export interface FleetCollectOptions {
|
|
41
|
+
logWindowHours: number;
|
|
42
|
+
selection: {
|
|
43
|
+
all: boolean;
|
|
44
|
+
agentIds: string[];
|
|
45
|
+
workerInstanceIds: string[];
|
|
46
|
+
};
|
|
47
|
+
/** Loop guard. True only on the root call; downstream requests force false. */
|
|
48
|
+
includeCoordinatorMesh: boolean;
|
|
49
|
+
timeoutMs: number;
|
|
50
|
+
}
|
|
51
|
+
export declare function collectFleetSubtree(opts: FleetCollectOptions, deps: FleetCollectorDeps): Promise<Buffer>;
|
|
52
|
+
//# sourceMappingURL=fleet-collector.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for fleet log collection.
|
|
3
|
+
*/
|
|
4
|
+
/** Default per-node deadline for a fleet bundle request (overridable via --fleet-timeout). */
|
|
5
|
+
export declare const FLEET_NODE_TIMEOUT_MS = 60000;
|
|
6
|
+
/** Per-node cap on raw log bytes an agent includes in its mini-bundle (50 MiB). */
|
|
7
|
+
export declare const FLEET_MAX_LOG_BYTES: number;
|
|
8
|
+
//# sourceMappingURL=fleet-constants.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FleetSelection } from '@kici-dev/engine';
|
|
2
|
+
import type { FleetTopology } from './fleet-topology.js';
|
|
3
|
+
/** A per-orchestrator selection, keyed by orchestrator instanceId. Absent = prune. */
|
|
4
|
+
export type ResolvedSelectionMap = Map<string, FleetSelection>;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve `--pick` selectors into a per-orchestrator FleetSelection map.
|
|
7
|
+
*
|
|
8
|
+
* No selectors -> every orchestrator gets `{ all: true }`. With selectors, an
|
|
9
|
+
* orchestrator is included only if at least one of its agents/workers (or the
|
|
10
|
+
* orchestrator itself) matches; its FleetSelection then carries exactly the
|
|
11
|
+
* matched agent ids and worker instanceIds. Orchestrators with no matches are
|
|
12
|
+
* omitted from the map (their branch is pruned).
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveSelection(topology: FleetTopology, selectors: string[]): ResolvedSelectionMap;
|
|
15
|
+
//# sourceMappingURL=fleet-selection.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fleet topology enumeration.
|
|
3
|
+
*
|
|
4
|
+
* Builds the cluster tree for `debug-bundle --fleet --list` / `--pick` from the
|
|
5
|
+
* local agent registry plus the heartbeat-cached peer inventory — no fan-out.
|
|
6
|
+
* Each peer's cached agent list is already maintained by ~30s heartbeats, so
|
|
7
|
+
* enumeration is a cheap in-memory read.
|
|
8
|
+
*/
|
|
9
|
+
/** A node in the enumerated fleet topology. */
|
|
10
|
+
export interface FleetTopologyNode {
|
|
11
|
+
kind: 'orchestrator' | 'agent';
|
|
12
|
+
id: string;
|
|
13
|
+
/** Cluster role for orchestrator nodes (coordinator | worker); undefined for agents. */
|
|
14
|
+
role?: 'coordinator' | 'worker';
|
|
15
|
+
hostname?: string;
|
|
16
|
+
labels: Record<string, string>;
|
|
17
|
+
/** Parent orchestrator instanceId; null for the collector (root) node. */
|
|
18
|
+
parentId: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface FleetTopology {
|
|
21
|
+
nodes: FleetTopologyNode[];
|
|
22
|
+
}
|
|
23
|
+
export interface FleetTopologyDeps {
|
|
24
|
+
/** This collector's own instanceId (the root orchestrator). */
|
|
25
|
+
instanceId: string;
|
|
26
|
+
/** This collector's role. */
|
|
27
|
+
role: 'coordinator' | 'worker';
|
|
28
|
+
/** This collector's hostname, if known. */
|
|
29
|
+
hostname?: string;
|
|
30
|
+
/** This node's directly-connected agents. */
|
|
31
|
+
listLocalAgents: () => {
|
|
32
|
+
agentId: string;
|
|
33
|
+
labels: string[];
|
|
34
|
+
}[];
|
|
35
|
+
/** The heartbeat-cached peer inventory (each peer + its cached agents). */
|
|
36
|
+
listPeers: () => {
|
|
37
|
+
instanceId: string;
|
|
38
|
+
role: 'coordinator' | 'worker';
|
|
39
|
+
hostname?: string;
|
|
40
|
+
agents: {
|
|
41
|
+
agentId: string;
|
|
42
|
+
labels: string[];
|
|
43
|
+
}[];
|
|
44
|
+
}[];
|
|
45
|
+
}
|
|
46
|
+
export declare function buildFleetTopology(deps: FleetTopologyDeps): FleetTopology;
|
|
47
|
+
//# sourceMappingURL=fleet-topology.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { PeerLogsCollectRequest, PeerToPeerMessage, FleetSelection } from '@kici-dev/engine';
|
|
2
|
+
import type { AgentRegistry } from '../agent/registry.js';
|
|
3
|
+
import type { PeerRegistry } from '../cluster/peer-registry.js';
|
|
4
|
+
import type { PeerClient } from '../cluster/peer-client.js';
|
|
5
|
+
import type { DiagnosticDeps } from './types.js';
|
|
6
|
+
import { type FleetCollectorDeps } from './fleet-collector.js';
|
|
7
|
+
import { type FleetTopology, type FleetTopologyDeps } from './fleet-topology.js';
|
|
8
|
+
/** A peer-handler-like object exposing the dual-direction collect send. */
|
|
9
|
+
export interface FleetPeerHandlerLike {
|
|
10
|
+
sendLogsCollectAndWait: (targetInstanceId: string, msg: PeerLogsCollectRequest, timeoutMs: number) => Promise<Buffer>;
|
|
11
|
+
}
|
|
12
|
+
/** A collector that issues a fleet.logs.request to an agent and awaits its bundle. */
|
|
13
|
+
export interface FleetAgentRequester {
|
|
14
|
+
request: (requestId: string, agentId: string, send: () => void) => Promise<Buffer>;
|
|
15
|
+
}
|
|
16
|
+
export interface FleetRuntime {
|
|
17
|
+
instanceId: string;
|
|
18
|
+
role: 'coordinator' | 'worker';
|
|
19
|
+
/** Loop window for log files (hours). */
|
|
20
|
+
logWindowHours: number;
|
|
21
|
+
/** Per-node deadline (ms). */
|
|
22
|
+
timeoutMs: number;
|
|
23
|
+
/** Local agent log directory (KICI_LOG_DIR), if configured. */
|
|
24
|
+
logDir?: string;
|
|
25
|
+
agentRegistry: AgentRegistry;
|
|
26
|
+
peerRegistry: PeerRegistry;
|
|
27
|
+
fleetAgentCollector: FleetAgentRequester;
|
|
28
|
+
/** Outgoing peer clients, keyed by instanceId. */
|
|
29
|
+
peerClients: Map<string, PeerClient>;
|
|
30
|
+
/** Incoming peer-handler with the dual-direction collect send. */
|
|
31
|
+
peerHandler: FleetPeerHandlerLike;
|
|
32
|
+
/** Deps for the local createDebugBundle. */
|
|
33
|
+
diagnosticDeps: DiagnosticDeps;
|
|
34
|
+
/** Raw orchestrator config (redacted by createDebugBundle). */
|
|
35
|
+
config: Record<string, unknown>;
|
|
36
|
+
/** Cluster health endpoint for the local bundle's cluster/health.json. */
|
|
37
|
+
clusterHealthUrl?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build the FleetCollectorDeps for a given selection of this node's downstream
|
|
41
|
+
* agents/workers. The collector passes per-branch selection in when it knows
|
|
42
|
+
* which downstream subset each peer should gather.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildFleetCollectorDeps(runtime: FleetRuntime, perBranchSelection: (instanceId: string) => FleetSelection): FleetCollectorDeps;
|
|
45
|
+
/** Build the FleetTopologyDeps for `--list` / `--pick` enumeration. */
|
|
46
|
+
export declare function buildFleetTopologyDeps(runtime: FleetRuntime): FleetTopologyDeps;
|
|
47
|
+
/** Enumerate this node's fleet topology (no fan-out). */
|
|
48
|
+
export declare function getFleetTopology(runtime: FleetRuntime): FleetTopology;
|
|
49
|
+
/**
|
|
50
|
+
* Collect this node's full subtree (root call: includeCoordinatorMesh=true) with
|
|
51
|
+
* an optional per-orchestrator selection map. Absent map entry => collect all.
|
|
52
|
+
*/
|
|
53
|
+
export declare function collectFleet(runtime: FleetRuntime, selectionByOrch: Map<string, FleetSelection> | null): Promise<Buffer>;
|
|
54
|
+
/**
|
|
55
|
+
* Peer-side responder: on an inbound peer.logs.collect.request, assemble this
|
|
56
|
+
* node's subtree (with the request's loop guard + selection) and stream it back
|
|
57
|
+
* as peer.logs.collect.chunk frames, or a peer.logs.collect.error on failure.
|
|
58
|
+
*/
|
|
59
|
+
export declare function makeFleetCollectResponder(runtime: FleetRuntime): (msg: PeerLogsCollectRequest, send: (out: PeerToPeerMessage) => boolean) => Promise<void>;
|
|
60
|
+
//# sourceMappingURL=fleet-wiring.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
|
-
/**
|
|
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.
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Manages the lifecycle: pending -> approved/rejected/expired.
|
|
5
5
|
*/
|
|
6
6
|
import { type Kysely } from 'kysely';
|
|
7
|
-
import type
|
|
7
|
+
import { type ApprovalRequirement, type ApproverClause, ApprovalDecision, HoldScope, TriggerSource } from '@kici-dev/engine';
|
|
8
|
+
import type { Database, HeldRun, HeldRunApproval } from '../db/types.js';
|
|
8
9
|
/** Status values for held runs (held_runs table). */
|
|
9
10
|
export declare enum HeldRunStatus {
|
|
10
11
|
Pending = "pending",
|
|
@@ -24,6 +25,47 @@ export interface CreateHeldRunData {
|
|
|
24
25
|
/** Queue type: 'environment' (default) or 'security'. */
|
|
25
26
|
queueType?: 'environment' | 'security';
|
|
26
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Data required to create a generalized approval hold. Unlike the legacy
|
|
30
|
+
* environment-only `create()`, this carries the hold scope, trigger source,
|
|
31
|
+
* optional step index, and the normalized approval requirement.
|
|
32
|
+
*/
|
|
33
|
+
export interface CreateHoldData {
|
|
34
|
+
runId: string;
|
|
35
|
+
jobId: string;
|
|
36
|
+
/** Granularity of the held element. */
|
|
37
|
+
scope: HoldScope;
|
|
38
|
+
/** Step index within the job for step-scoped holds; omit otherwise. */
|
|
39
|
+
stepIndex?: number;
|
|
40
|
+
/** What triggered the hold (environment policy vs SDK requireApproval). */
|
|
41
|
+
triggerSource: TriggerSource;
|
|
42
|
+
/** The normalized requirement the hold must satisfy. */
|
|
43
|
+
requirement: ApprovalRequirement;
|
|
44
|
+
/** Environment id, when the hold originates from an environment policy. */
|
|
45
|
+
environmentId?: string | null;
|
|
46
|
+
/** Queue type: 'environment' (default) or 'security'. */
|
|
47
|
+
queueType?: 'environment' | 'security';
|
|
48
|
+
}
|
|
49
|
+
/** A single decision to record against a hold. */
|
|
50
|
+
export interface RecordDecisionData {
|
|
51
|
+
approverSub: string;
|
|
52
|
+
decision: ApprovalDecision;
|
|
53
|
+
/** Which requirement clauses this decision satisfied (for attribution). */
|
|
54
|
+
clausesSatisfied?: ApproverClause[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The outcome of `release()` — describes how the held element must be resumed.
|
|
58
|
+
* The store only writes the terminal DB state; the caller performs the actual
|
|
59
|
+
* re-dispatch (job/workflow) or agent notification (step) using this signal.
|
|
60
|
+
*/
|
|
61
|
+
export interface ReleaseSignal {
|
|
62
|
+
holdId: string;
|
|
63
|
+
runId: string;
|
|
64
|
+
jobId: string;
|
|
65
|
+
scope: HoldScope;
|
|
66
|
+
/** Set only for step-scoped holds. */
|
|
67
|
+
stepIndex: number | null;
|
|
68
|
+
}
|
|
27
69
|
/** Options for listing held runs. */
|
|
28
70
|
export interface ListHeldRunsOptions {
|
|
29
71
|
status?: string;
|
|
@@ -35,6 +77,26 @@ export declare class HeldRunStore {
|
|
|
35
77
|
constructor(db: Kysely<Database>);
|
|
36
78
|
/** Create a new held run with pending status. */
|
|
37
79
|
create(orgId: string, data: CreateHeldRunData): Promise<HeldRun>;
|
|
80
|
+
/**
|
|
81
|
+
* Create a generalized approval hold (workflow/job/step scope, explicit or
|
|
82
|
+
* environment trigger) carrying a normalized `ApprovalRequirement`. Returns
|
|
83
|
+
* the created row.
|
|
84
|
+
*/
|
|
85
|
+
createHold(orgId: string, data: CreateHoldData): Promise<HeldRun>;
|
|
86
|
+
/** Record one approve/reject decision against a hold. */
|
|
87
|
+
recordDecision(heldRunId: string, data: RecordDecisionData): Promise<HeldRunApproval>;
|
|
88
|
+
/** List the recorded decisions for a hold, oldest first. */
|
|
89
|
+
listDecisions(heldRunId: string): Promise<HeldRunApproval[]>;
|
|
90
|
+
/** Get a single held run by id (org-scoped). Returns null if absent. */
|
|
91
|
+
getById(orgId: string, heldRunId: string): Promise<HeldRun | null>;
|
|
92
|
+
/**
|
|
93
|
+
* Release a hold whose approval requirement is satisfied. Flips the row to
|
|
94
|
+
* 'approved' and returns a `ReleaseSignal` describing how the caller must
|
|
95
|
+
* resume the element (re-dispatch for job/workflow, agent notification for
|
|
96
|
+
* step). Throws if the hold is not found or not pending. Approver attribution
|
|
97
|
+
* lives in `held_run_approvals`, not on the row.
|
|
98
|
+
*/
|
|
99
|
+
release(orgId: string, heldRunId: string): Promise<ReleaseSignal>;
|
|
38
100
|
/** Approve a pending held run. Throws if not found or not pending. */
|
|
39
101
|
approve(orgId: string, heldRunId: string, approvedBy: string): Promise<HeldRun>;
|
|
40
102
|
/** Reject a pending held run. Throws if not found or not pending. */
|
|
@@ -53,6 +115,12 @@ export declare class HeldRunStore {
|
|
|
53
115
|
approveByQueueType(orgId: string, heldRunId: string, approvedBy: string, queueType: 'environment' | 'security'): Promise<HeldRun>;
|
|
54
116
|
/** Get a held run by run ID and job ID. Returns null if not found. */
|
|
55
117
|
getByRunAndJob(orgId: string, runId: string, jobId: string): Promise<HeldRun | null>;
|
|
118
|
+
/**
|
|
119
|
+
* List pending holds past their `expires_at`. Called by the stale detector
|
|
120
|
+
* BEFORE `expireOverdue()` so it can route each overdue hold by scope (step
|
|
121
|
+
* holds notify the waiting agent; job/workflow holds fail the run).
|
|
122
|
+
*/
|
|
123
|
+
listOverdue(): Promise<HeldRun[]>;
|
|
56
124
|
/**
|
|
57
125
|
* Expire overdue pending runs. Called by the stale detector.
|
|
58
126
|
* Sets status to 'expired' and resolved_at to now() for all
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firecracker host-network provisioning.
|
|
3
|
+
*
|
|
4
|
+
* Creates the per-coordinator bridge (kici-brN), assigns its gateway IP,
|
|
5
|
+
* marks kici-* interfaces unmanaged by NetworkManager, enables IP forwarding,
|
|
6
|
+
* and builds a disjoint, source-scoped nftables table for NAT + egress
|
|
7
|
+
* isolation. One pure command-builder drives live provisioning, the rendered
|
|
8
|
+
* boot script, and (read-only) verification.
|
|
9
|
+
*
|
|
10
|
+
* This is HOST setup, distinct from the runtime per-VM isolation in
|
|
11
|
+
* scaler/nftables.ts (added at spawn / removed at destroy). The two share the
|
|
12
|
+
* nft table name but have separate lifecycles; this module never touches the
|
|
13
|
+
* per-VM rules.
|
|
14
|
+
*/
|
|
15
|
+
/** A single subprocess invocation (no shell). */
|
|
16
|
+
export interface CommandSpec {
|
|
17
|
+
bin: string;
|
|
18
|
+
args: string[];
|
|
19
|
+
/** Optional stdin payload (used for `nft -f -`). */
|
|
20
|
+
stdin?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Host-bridge configuration for one Firecracker coordinator. */
|
|
23
|
+
export interface FirecrackerBridgeConfig {
|
|
24
|
+
/** Bridge interface name, e.g. 'kici-br0'. */
|
|
25
|
+
bridgeName: string;
|
|
26
|
+
/** Gateway IP + prefix, e.g. '10.0.0.1/24'. */
|
|
27
|
+
bridgeCidr: string;
|
|
28
|
+
/** nft table name, e.g. 'kici' or 'kici_b'. */
|
|
29
|
+
table: string;
|
|
30
|
+
/** NAT egress interface; auto-detected from the default route when omitted. */
|
|
31
|
+
hostIface?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Derive the network address (CIDR) from a gateway CIDR by masking host bits.
|
|
35
|
+
* '10.0.0.1/24' -> '10.0.0.0/24'.
|
|
36
|
+
*/
|
|
37
|
+
export declare function cidrToNetwork(cidr: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Build the ordered command list that provisions one Firecracker host bridge.
|
|
40
|
+
* Pure — performs no I/O. `provisionBridge` executes these; `renderBootScript`
|
|
41
|
+
* serializes them.
|
|
42
|
+
*
|
|
43
|
+
* The nft `delete table`/`add table` here only ever touches `cfg.table`, so a
|
|
44
|
+
* coord-B provision never wipes coord A's table (and vice versa). Every
|
|
45
|
+
* forward/postrouting/MSS rule is source-scoped to the bridge subnet so two
|
|
46
|
+
* tables on the shared hooks do not cross-drop each other's traffic.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildBridgeCommands(cfg: FirecrackerBridgeConfig): CommandSpec[];
|
|
49
|
+
export declare const NM_CONF_PATH = "/etc/NetworkManager/conf.d/90-kici-unmanaged.conf";
|
|
50
|
+
export declare const NM_CONF_CONTENT: string;
|
|
51
|
+
export type CommandRunner = (spec: CommandSpec) => Promise<{
|
|
52
|
+
stdout: string;
|
|
53
|
+
}>;
|
|
54
|
+
/** Writes the host-scoped NetworkManager conf. Injectable for tests. */
|
|
55
|
+
export type FileWriter = (path: string, content: string) => Promise<void>;
|
|
56
|
+
export interface ExecOptions {
|
|
57
|
+
/** Inject a runner for tests. */
|
|
58
|
+
runner?: CommandRunner;
|
|
59
|
+
/** Inject the NM-conf file writer for tests. */
|
|
60
|
+
writeNmConf?: FileWriter;
|
|
61
|
+
/** Wrap privileged bins with `sudo -n` (non-root orchestrator hosts). */
|
|
62
|
+
requireSudo?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** Resolve the default-route egress interface. */
|
|
65
|
+
export declare function resolveHostIface(opts?: ExecOptions): Promise<string>;
|
|
66
|
+
export interface BridgeHealth {
|
|
67
|
+
bridgeName: string;
|
|
68
|
+
bridgeExists: boolean;
|
|
69
|
+
bridgeUp: boolean;
|
|
70
|
+
addrPresent: boolean;
|
|
71
|
+
tablePresent: boolean;
|
|
72
|
+
healthy: boolean;
|
|
73
|
+
detail: string;
|
|
74
|
+
}
|
|
75
|
+
/** Provision (or heal) one Firecracker host bridge. Throws on any failure. */
|
|
76
|
+
export declare function provisionBridge(cfg: FirecrackerBridgeConfig, opts?: ExecOptions): Promise<void>;
|
|
77
|
+
/** Read-only health probe for one bridge. Never throws on a missing resource. */
|
|
78
|
+
export declare function verifyBridge(cfg: FirecrackerBridgeConfig, opts?: ExecOptions): Promise<BridgeHealth>;
|
|
79
|
+
/** Remove the bridge + its nft table. Leaves the host-scoped NM conf in place. */
|
|
80
|
+
export declare function teardownBridge(cfg: FirecrackerBridgeConfig, opts?: ExecOptions): Promise<void>;
|
|
81
|
+
/** Serialize the provisioning command list into a dependency-free boot script. */
|
|
82
|
+
export declare function renderBootScript(cfg: FirecrackerBridgeConfig): string;
|
|
83
|
+
//# sourceMappingURL=host-network.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type FirecrackerBridgeConfig } from './host-network.js';
|
|
2
|
+
export declare const BOOT_SCRIPT_DIR = "/usr/local/lib/kici-firecracker";
|
|
3
|
+
export declare const SYSTEMD_DIR = "/etc/systemd/system";
|
|
4
|
+
export declare function persistUnitName(bridgeName: string): string;
|
|
5
|
+
export declare function renderPersistUnit(bridgeName: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Install the boot script + unit and `systemctl enable` it (enable only — the
|
|
8
|
+
* deploy/CLI provisions the bridge live in the same run, so the unit fires on
|
|
9
|
+
* the NEXT boot rather than re-churning nft now).
|
|
10
|
+
*/
|
|
11
|
+
export declare function installPersist(cfg: FirecrackerBridgeConfig, opts?: {
|
|
12
|
+
requireSudo?: boolean;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=persist.d.ts.map
|
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
|