import { DockerEngineClient } from './docker-engine-client.js'; import { type ContainerInspect, type ContainerCreateBody, DockerEngineError } from './docker-engine-types.js'; import { type WorkerReplica, computeProjectName, enumerateWorkers } from './worker-enumeration.js'; export { computeProjectName, enumerateWorkers, type WorkerReplica }; export interface ScaleOptions { /** Target worker count. Must be >= 1 (validated upstream in lifecycle route). */ count: number; /** Override orchestrator URL for metadata-refresh callback. */ orchestratorUrl?: string; /** Override orchestrator internal API key. */ orchestratorApiKey?: string; /** Override Docker socket. Default: env DOCKER_HOST or unix:///var/run/docker-host.sock. */ dockerHost?: string; /** Override engine client (test seam — production wires the default). */ engineClient?: DockerEngineClient; } export interface ScaleResult { /** Worker count observed before scaling (from Engine API enumeration, not .env). */ previousCount: number; /** Target count from ScaleOptions.count. */ requestedCount: number; /** Actual achieved count after the operation. Equals requestedCount on success. */ actualCount: number; } export declare class PartialScaleError extends Error { readonly name = "PartialScaleError"; readonly requested: number; readonly actual: number; readonly previousCount: number; readonly cause: Error; constructor(requested: number, actual: number, previousCount: number, cause: Error); } export interface ScalePlan { toCreate: number[]; toRemove: string[]; } /** * Plan container-number assignment for a scale operation. * * Scale up: gap-fill ascending in [1..max(existing)], then append. * Scale down: sort exited (highest-numbered first), then running (highest-numbered first), * take the first (current - target) IDs. * No-op: returns `{ toCreate: [], toRemove: [] }`. * * Pure function — unit-tested independently. FR-006, SC-003, SC-011. */ export declare function assignContainerNumbers(existing: WorkerReplica[], target: number): ScalePlan; /** * Clone a source replica's inspect response into a create body for a new replica. * * Strips orchestrator-set fields (Id, Created, State, Status, Hostname, populated * NetworkSettings) and keeps Image, Cmd, Env, Entrypoint, WorkingDir, User, Labels, * Healthcheck, StopSignal, StopTimeout, ExposedPorts, and all of HostConfig. * * Overwrites the `com.docker.compose.container-number` label with the new number — * preserves all other labels (project, service, config-hash, etc.) per FR-007. * * Builds NetworkingConfig with **first network only** from source.NetworkSettings.Networks * in insertion order. Caller is responsible for `connectNetwork` calls for remaining * networks before `startContainer` (Q1=A multi-network sequencing). * * Throws `Error('SOURCE_REPLICA_HAS_NO_NETWORKS')` if source has zero networks. */ export declare function cloneInspectToCreate(inspect: ContainerInspect, newNumber: number, _newName: string): ContainerCreateBody; interface ScaleUpResult { created: number[]; failed?: { number: number; error: Error; }; } interface ScaleDownResult { removed: string[]; failed?: { id: string; error: Error; }; } /** * Create + connect-extra-networks + start, repeated per toCreate slot. * * Per-slot sequencing (Q1=A): * 1. Pre-check gap-fill name collision (FR-015, Q5=A) — if any container * already holds `-worker-`, force-remove it first. Edge * case after manual `docker rm` of a running worker; normal operation * doesn't trigger it because exited replicas are counted by FR-002. * 2. POST /containers/create with the first network in NetworkingConfig. * 3. POST /networks//connect per additional network from source. * 4. POST /containers//start. * * On the first error, stop the loop and return `{ created, failed }`. Do NOT * roll back already-created replicas (Q2=B: commit what succeeded). */ export declare function scaleUp(client: DockerEngineClient, project: string, source: ContainerInspect, toCreate: number[]): Promise; /** * Stop + remove per ID, in the caller-provided order (already sorted by * `assignContainerNumbers`: exited first, then running, highest-numbered first). * On the first error, stop and return `{ removed, failed }`. (FR-004) */ export declare function scaleDown(client: DockerEngineClient, toRemoveIds: string[]): Promise; /** * Scale worker replicas to the requested count via the Docker Engine API. * * Replaces the previous `docker compose --scale` shell-out: enumerates workers * by `com.docker.compose.*` labels, clones an existing replica's config, and * issues create/connect/start or stop/remove directly on the daemon. Removes * the host compose-file dependency entirely. * * Concurrent invocations are serialized by an in-process async mutex (FR-014). * * Stale clone drift case (FR-013): if a user edits the host compose file and * rebuilds without `docker compose up -d`, scale-up clones a stale source * replica's config — same behavior as compose itself when scaling from an * older replica. Documented here per the spec; not actionable in this code path. */ export declare function scaleWorkers(options: ScaleOptions): Promise; /** * Update the `workers` field in cluster.local.yaml atomically. * Creates the file if absent. Preserves any other top-level fields already * present. */ export declare function updateClusterLocalYaml(localYamlPath: string, count: number): Promise; /** * POST to orchestrator /internal/refresh-metadata to trigger immediate metadata push. */ export declare function triggerMetadataRefresh(orchestratorUrl: string, apiKey: string): Promise; export { DockerEngineError }; //# sourceMappingURL=worker-scaler.d.ts.map