@kici-dev/orchestrator 0.13.0 → 0.14.1

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 (58) hide show
  1. package/dist/agent/dispatcher.d.ts +17 -0
  2. package/dist/cancel/peer-job-cancel.d.ts +12 -0
  3. package/dist/cli/api-client.d.ts +73 -0
  4. package/dist/cli/commands/cluster-settings.d.ts +31 -1
  5. package/dist/cli/commands/peer.d.ts +27 -1
  6. package/dist/cli/commands/scaler.d.ts +40 -4
  7. package/dist/cli.js +2210 -933
  8. package/dist/cluster/cluster-settings-reader.d.ts +8 -2
  9. package/dist/cluster/coordinator.d.ts +34 -8
  10. package/dist/cluster/peer-client.d.ts +37 -4
  11. package/dist/cluster/peer-forget.d.ts +138 -0
  12. package/dist/cluster/peer-handler.d.ts +18 -1
  13. package/dist/cluster/scaler-orphans-peer.d.ts +40 -0
  14. package/dist/cluster/settings-propagation.d.ts +85 -0
  15. package/dist/cluster/worker-cluster-settings.d.ts +29 -0
  16. package/dist/config.d.ts +9 -0
  17. package/dist/db/migrations/154_cluster_settings_firecracker_api_socket_wait.d.ts +4 -0
  18. package/dist/db/migrations/155_org_settings_reroute_spawn_retry.d.ts +17 -0
  19. package/dist/db/migrations/156_kici_events_match_outcome.d.ts +17 -0
  20. package/dist/db/types.d.ts +16 -0
  21. package/dist/events/event-router.d.ts +7 -1
  22. package/dist/events/event-store.d.ts +20 -3
  23. package/dist/events/types.d.ts +45 -0
  24. package/dist/helpers/windows-batch.d.ts +59 -0
  25. package/dist/index.js +60 -0
  26. package/dist/metrics/prometheus.d.ts +2 -0
  27. package/dist/orchestrator-core.d.ts +7 -0
  28. package/dist/registration/registration-store.d.ts +10 -0
  29. package/dist/routes/admin-cluster-settings.d.ts +13 -1
  30. package/dist/routes/admin-event-inspect.d.ts +36 -0
  31. package/dist/routes/admin-peer-forget.d.ts +43 -0
  32. package/dist/routes/admin-scaler-orphans.d.ts +64 -0
  33. package/dist/routes/admin.d.ts +23 -0
  34. package/dist/scaler/backend-factory.d.ts +6 -1
  35. package/dist/scaler/bare-metal-backend.d.ts +17 -15
  36. package/dist/scaler/bare-metal-launch.d.ts +90 -0
  37. package/dist/scaler/firecracker-backend.d.ts +181 -7
  38. package/dist/scaler/live-vms.d.ts +57 -0
  39. package/dist/scaler/manager.d.ts +112 -3
  40. package/dist/scaler/orphan-requests.d.ts +23 -0
  41. package/dist/scaler/overlay-template.d.ts +38 -0
  42. package/dist/scaler/scaler-state-store.d.ts +7 -0
  43. package/dist/scaler/spawn-errors.d.ts +17 -0
  44. package/dist/scaler/startup-orphan-sweep.d.ts +15 -0
  45. package/dist/scaler/types.d.ts +21 -2
  46. package/dist/scaler/vm-job-bindings.d.ts +17 -0
  47. package/dist/secrets/rbac.d.ts +7 -1
  48. package/dist/server.js +5498 -2193
  49. package/dist/standalone.js +10754 -7451
  50. package/dist/worker/in-memory-execution-tracker.d.ts +5 -0
  51. package/dist/worker/in-memory-job-queue.d.ts +21 -1
  52. package/dist/worker/queued-reroute-release.d.ts +39 -0
  53. package/dist/worker/reroute-spawn-control.d.ts +58 -0
  54. package/dist/worker/spawn-retry-budget.d.ts +87 -0
  55. package/dist/worker-core.d.ts +11 -2
  56. package/installer-image-digests.json +3 -3
  57. package/package.json +4 -4
  58. package/sbom.spdx.json +53 -53
@@ -300,6 +300,23 @@ export declare class Dispatcher {
300
300
  * @returns the number of jobs that entered `spawning`.
301
301
  */
302
302
  retryPendingScaleRequests(maxJobs?: number, trigger?: ScalerRedispatchTrigger): Promise<number>;
303
+ /**
304
+ * Re-offer ONE pending job to the scaler: a worker's spawn-retry backoff ending.
305
+ *
306
+ * Outside the batch's single-flight on purpose: a wake-up that lands during a
307
+ * batch pass would otherwise be dropped, and a worker runs no pending-scale
308
+ * sweep to pick it up. The worker's per-job attempt gate is what keeps this
309
+ * from starting a second spawn for the same job.
310
+ *
311
+ * @returns true when a spawn started.
312
+ */
313
+ redrivePendingJob(jobId: string): Promise<boolean>;
314
+ /**
315
+ * Offer one pending job to the scaler through the same `onNoMatchingAgent` →
316
+ * `requestScale` path a dispatch uses, or settle it when its sealed secrets
317
+ * cannot be opened here.
318
+ */
319
+ private offerPendingToScaler;
303
320
  /**
304
321
  * Release the slot claimed for a direct dispatch the queue refused because
305
322
  * the job's run has stopped. Nothing is sent to the agent. The queue already
@@ -8,6 +8,10 @@
8
8
  * only to `dispatch_queue.agent_id`. So the agent is resolved from the
9
9
  * dispatcher first and from the dispatch record second, and a cancel that
10
10
  * cannot be delivered is logged with the reason.
11
+ *
12
+ * On a worker the receiver also removes the jobs of the cancel that are still
13
+ * queued (waiting for an agent spawn), and stop-marks the dispatched ones so a
14
+ * requeue cannot bring them back. A removed job is reported `dequeued`.
11
15
  */
12
16
  import type { Kysely } from 'kysely';
13
17
  import { z } from 'zod';
@@ -18,6 +22,7 @@ import type { Dispatcher } from '../agent/dispatcher.js';
18
22
  export declare const PeerCancelOutcome: z.ZodEnum<{
19
23
  "agent-not-connected": "agent-not-connected";
20
24
  delivered: "delivered";
25
+ dequeued: "dequeued";
21
26
  "lookup-failed": "lookup-failed";
22
27
  "not-tracked": "not-tracked";
23
28
  }>;
@@ -35,6 +40,13 @@ export interface PeerJobCancelDeps {
35
40
  registry: Pick<AgentRegistry, 'get'>;
36
41
  /** The durable dispatch record. Absent on a worker, which has no database. */
37
42
  lookupDispatched?: (runId: string, jobId: string | undefined) => Promise<DispatchedAgent[]>;
43
+ /**
44
+ * Remove this process's queued (not yet dispatched) jobs of the cancel, and
45
+ * stop-mark its dispatched ones so a requeue cannot bring them back. Wired only
46
+ * on a worker, whose queue lives in memory; a coordinator's queue rows are
47
+ * settled by its own run-cancel path.
48
+ */
49
+ releaseQueued?: (runId: string, jobId: string | undefined) => Promise<string[]>;
38
50
  logger: {
39
51
  info(message: string, meta?: object): unknown;
40
52
  warn(message: string, meta?: object): unknown;
@@ -5,6 +5,42 @@
5
5
  * All methods are thin wrappers around fetch() that handle JSON serialization,
6
6
  * error formatting, and URL construction.
7
7
  */
8
+ import type { PeerForgetOutcome, ScalerLiveVm, ScalerVmStopResult } from '@kici-dev/engine';
9
+ /** `POST /api/v1/admin/peers/forget`: one result per coordinator, this one first. */
10
+ export interface PeerForgetResponseBody {
11
+ instanceId: string;
12
+ results: Array<{
13
+ coordinator: string;
14
+ outcome: PeerForgetOutcome;
15
+ detail: string;
16
+ }>;
17
+ /** Set when the coordinator kept the peer only for want of the backstop acknowledgement. */
18
+ acknowledgementRequired?: boolean;
19
+ error?: string;
20
+ }
21
+ /** The node a scaler orphans request answered for. */
22
+ export interface ScalerOrphansNode {
23
+ instanceId: string;
24
+ /** The node's cluster role; `unknown` for a peer the coordinator has no role for. */
25
+ role: 'coordinator' | 'worker' | 'unknown';
26
+ /** The node's Firecracker scaler names; empty when it runs none. */
27
+ firecrackerScalers: string[];
28
+ /**
29
+ * Coordinator peers the cluster expects but the answering coordinator has no
30
+ * live link to. While any is listed, no VM is `orphaned`.
31
+ */
32
+ disconnectedCoordinators?: string[];
33
+ }
34
+ /** `GET /api/v1/admin/scaler/orphans`: every live Firecracker VM on the node. */
35
+ export interface ScalerOrphansListResponse {
36
+ node: ScalerOrphansNode;
37
+ vms: ScalerLiveVm[];
38
+ }
39
+ /** `POST /api/v1/admin/scaler/orphans/stop`: one result per requested VM. */
40
+ export interface ScalerOrphansStopResponse {
41
+ node: ScalerOrphansNode;
42
+ results: ScalerVmStopResult[];
43
+ }
8
44
  /** A node in the fleet topology returned by GET /admin/fleet-topology. */
9
45
  export interface FleetTopologyNodeResponse {
10
46
  kind: 'orchestrator' | 'agent';
@@ -384,6 +420,29 @@ export declare class AdminApiClient {
384
420
  draining: boolean;
385
421
  jobsRunning: number;
386
422
  }>;
423
+ /** Forget a departed peer on this coordinator and every connected sibling. */
424
+ /**
425
+ * Forget a departed peer on this coordinator and every connected sibling. A
426
+ * refusal that only needs the backstop acknowledgement comes back as a body
427
+ * with `acknowledgementRequired`, so the caller can ask and retry; every
428
+ * other failure throws.
429
+ */
430
+ forgetPeer(body: {
431
+ instanceId: string;
432
+ timeoutMs?: number;
433
+ acknowledgeBackstop?: boolean;
434
+ }): Promise<PeerForgetResponseBody>;
435
+ /** List the live Firecracker VMs on this coordinator, or on the `target` peer. */
436
+ listScalerOrphans(opts?: {
437
+ target?: string;
438
+ timeoutMs?: number;
439
+ }): Promise<ScalerOrphansListResponse>;
440
+ /** Stop the named VMs on this coordinator, or on the `target` peer. */
441
+ stopScalerOrphans(body: {
442
+ target?: string;
443
+ vmIds: string[];
444
+ timeoutMs?: number;
445
+ }): Promise<ScalerOrphansStopResponse>;
387
446
  createApiKey(opts: {
388
447
  label?: string;
389
448
  routingKeys?: string[];
@@ -547,6 +606,20 @@ export declare class AdminApiClient {
547
606
  includePayload?: boolean;
548
607
  routingKey?: string;
549
608
  }): Promise<Record<string, unknown>>;
609
+ /** List internal events, newest first (`GET /api/v1/admin/events`). */
610
+ listEvents(opts?: {
611
+ name?: string;
612
+ outcome?: string;
613
+ since?: string;
614
+ before?: string;
615
+ limit?: number;
616
+ }): Promise<{
617
+ events: Array<Record<string, unknown>>;
618
+ limit: number;
619
+ nextCursor: string | null;
620
+ }>;
621
+ /** One internal event with its redacted payload and dispatched runs. */
622
+ getEvent(id: string): Promise<Record<string, unknown>>;
550
623
  listAccessLog(opts?: {
551
624
  orgId?: string;
552
625
  actorType?: string;
@@ -5,13 +5,15 @@
5
5
  *
6
6
  * Talks to the orchestrator admin API directly (not the Platform dashboard
7
7
  * proxy), so the CLI stays operable even when Platform is unavailable. Backed
8
- * by `packages/orchestrator/src/routes/admin-cluster-settings.ts`.
8
+ * by `packages/orchestrator/src/routes/admin-cluster-settings.ts`. `show` also
9
+ * lists which settings version each coordinator and worker applied.
9
10
  *
10
11
  * These knobs are fleet-wide (a single `cluster_settings` row), not per-tenant.
11
12
  * For per-tenant knobs use `kici-admin org-settings`.
12
13
  */
13
14
  import type { Command } from 'commander';
14
15
  import type { AdminApiClient } from '../api-client.js';
16
+ import { type SettingsPropagationReport } from '../../cluster/settings-propagation.js';
15
17
  /**
16
18
  * One text knob. Kept separate from {@link KnobSpec} because the value parser,
17
19
  * the PATCH body type, and the `show` formatter all differ — a string knob has
@@ -36,8 +38,36 @@ interface BooleanKnobSpec {
36
38
  flag: string;
37
39
  label: string;
38
40
  }
41
+ type ClusterSettings = Record<string, number | string | boolean | null>;
39
42
  /** camelCase → value (null clears the override). */
40
43
  type PatchBody = Record<string, number | string | boolean | null>;
44
+ /** The outcome of the propagation request `show` makes after the settings read. */
45
+ export type PropagationFetch = {
46
+ kind: 'ok';
47
+ report: SettingsPropagationReport;
48
+ } | {
49
+ kind: 'unsupported';
50
+ } | {
51
+ kind: 'error';
52
+ reason: string;
53
+ };
54
+ export declare const PROPAGATION_UNSUPPORTED_NOTE: string;
55
+ /**
56
+ * Fetch the propagation report. A 404 means the orchestrator predates the
57
+ * route; `show` then prints the settings alone, because this CLI may be newer
58
+ * than the orchestrator it talks to.
59
+ */
60
+ export declare function fetchPropagation(client: AdminApiClient): Promise<PropagationFetch>;
61
+ /** The table section: one row per orchestrator, lagging ones in upper case. */
62
+ export declare function formatPropagation(report: SettingsPropagationReport): string;
63
+ /**
64
+ * What `show` prints. The JSON keeps the flat settings map exactly as before
65
+ * and adds one `propagation` key, so scripts that read knob keys keep working.
66
+ */
67
+ export declare function renderShow(settings: ClusterSettings, propagation: PropagationFetch, format: string): {
68
+ stdout: string;
69
+ stderr: string | null;
70
+ };
41
71
  /**
42
72
  * Parse an integer CLI flag against its knob's bounds, exiting on failure.
43
73
  *
@@ -5,9 +5,35 @@
5
5
  * peer create-token Create a join token for a new peer
6
6
  * peer list List active peer credentials
7
7
  * peer revoke Revoke a specific peer's credential
8
+ * peer forget Drop a departed peer from every coordinator's live
9
+ * peer registry (admin HTTP API; credential untouched)
8
10
  * peer revoke-all Revoke all peer credentials
9
11
  */
10
12
  import type { Command } from 'commander';
11
13
  import type { AdminApiClient } from '../api-client.js';
12
- export declare function registerPeerCommands(program: Command, _getClient: () => AdminApiClient): void;
14
+ /** Where `peer forget` writes its results. */
15
+ export interface PeerForgetIo {
16
+ out: (line: string) => void;
17
+ /** Asks the operator; resolves true on yes. */
18
+ confirm: (prompt: string) => Promise<boolean>;
19
+ /** Whether an operator can answer a prompt (stdin is a terminal). */
20
+ interactive: boolean;
21
+ }
22
+ /**
23
+ * `kici-admin peer forget`: drop a departed peer from this coordinator's live
24
+ * peer registry and every connected sibling's. Exits 1 when any coordinator
25
+ * kept it (connected, heard from recently, or no answer).
26
+ *
27
+ * When the forget would switch a coordinator's event-provision backstop back
28
+ * on, the operator confirms that consequence first; `--yes` acknowledges it up
29
+ * front, and without a terminal the command refuses instead of asking.
30
+ *
31
+ * @returns the process exit code
32
+ */
33
+ export declare function runPeerForget(client: Pick<AdminApiClient, 'forgetPeer'>, instanceId: string, opts: {
34
+ json: boolean;
35
+ timeout: string;
36
+ yes: boolean;
37
+ }, io: PeerForgetIo): Promise<number>;
38
+ export declare function registerPeerCommands(program: Command, getClient: () => AdminApiClient): void;
13
39
  //# sourceMappingURL=peer.d.ts.map
@@ -1,18 +1,54 @@
1
1
  /**
2
- * Standalone scaler maintenance commands for kici-admin.
2
+ * Scaler maintenance commands for kici-admin.
3
3
  *
4
+ * scaler orphans List the live Firecracker VMs a node's running
5
+ * orchestrator does not track and, with --stop, stop
6
+ * them. Goes through the admin HTTP API; `--target`
7
+ * reaches a worker through the coordinator.
4
8
  * scaler reap-orphans Free leaked Firecracker/container resources WITHOUT a
5
9
  * running orchestrator (recovery for a wedged node whose
6
10
  * data disk is full).
7
11
  *
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
12
+ * `reap-orphans` loads the orchestrator's LOCAL config (no HTTP admin API, no
13
+ * DB) and runs the liveness-driven reaper, which spares every live VM. It is the sanctioned operator path for
10
14
  * the ENOSPC bootstrap deadlock: when the data disk is 100% full the
11
15
  * orchestrator crash-loops before its in-process orphan sweep can run, so a
12
16
  * tool that frees disk WITHOUT the orchestrator is required.
13
17
  */
14
18
  import type { Command } from 'commander';
19
+ import type { AdminApiClient } from '../api-client.js';
15
20
  /** Probe the local orchestrator /health endpoint. Healthy => it reaps itself. */
16
21
  export declare function isOrchestratorHealthy(port: number, basePath: string): Promise<boolean>;
17
- export declare function registerScalerCommands(program: Command): void;
22
+ /** The `scaler orphans` options as Commander parses them. */
23
+ export interface ScalerOrphansOptions {
24
+ target?: string;
25
+ all: boolean;
26
+ stop: boolean;
27
+ vm: string[];
28
+ yes: boolean;
29
+ dryRun: boolean;
30
+ /** Seconds to wait for a `--target` node. */
31
+ timeout: string;
32
+ json: boolean;
33
+ }
34
+ /** Where `scaler orphans` reads its confirmation and writes its output. */
35
+ export interface ScalerOrphansIo {
36
+ confirm: (prompt: string) => Promise<boolean>;
37
+ /** The listing, the results, or the JSON body. */
38
+ out: (line: string) => void;
39
+ /** Progress and the confirmation context; stderr, so `--json` output stays parseable. */
40
+ err: (line: string) => void;
41
+ }
42
+ type OrphansClient = Pick<AdminApiClient, 'listScalerOrphans' | 'stopScalerOrphans'>;
43
+ /** `45s`, `12m3s`, `1h2m`, `3d4h`. */
44
+ export declare function formatAge(seconds: number): string;
45
+ /**
46
+ * `kici-admin scaler orphans`: list the live Firecracker VMs a node's
47
+ * orchestrator does not track, and with `--stop` stop the orphaned ones.
48
+ *
49
+ * @returns the process exit code
50
+ */
51
+ export declare function runScalerOrphans(client: OrphansClient, opts: ScalerOrphansOptions, io: ScalerOrphansIo): Promise<number>;
52
+ export declare function registerScalerCommands(program: Command, getClient: () => AdminApiClient): void;
53
+ export {};
18
54
  //# sourceMappingURL=scaler.d.ts.map