import { type ControllerStackView, type MaintenanceLock, type ServiceAdapterStopAbandonment, stackJournalPath } from "@cotal-ai/workspace"; import { type ServiceDefinitionSnapshot } from "./service-definitions.js"; import { type ServiceInstanceSnapshot } from "./service-instances.js"; export declare const STACK_ROLES: readonly ["broker", "auth", "delivery", "manager"]; export type StackRole = (typeof STACK_ROLES)[number]; /** The fixed, root-bound definition names a controller stack owns. A custom definition with * another name is never swept into the stack, whatever its adapter. */ export declare const STACK_DEFINITION: Readonly>; export declare const STACK_ADAPTER: Readonly>; export declare const STACK_MODES: readonly ["auth", "open", "user"]; export type StackMode = (typeof STACK_MODES)[number]; /** Which roles a mode can ever plan. An open stack has no delivery; only a user stack has an auth * service. A role outside its mode stays `not-attempted` with no instance, and validation refuses * any other shape. */ export declare function stackRoleApplies(mode: StackMode, role: StackRole): boolean; export type StackMemberState = "planned" | "not-attempted" | "started" | "reused" | "stopped" | "already-stopped" | "refused" | "failed" | "unknown"; /** A redefinition in flight: the instance an `up` launches for a member after it retires the * member's recorded instance with the old definition. Its IDs are journaled before that retirement * and the new definition's revision before the launch, so a crash in between is resolved from the * replacement's own record (see {@link withSettledMembers}). */ export interface StackReplacement { operation: string; instanceId: string; launchRequestId: string; definitionRevision?: string; } export interface StackMember { role: StackRole; definition: string; instanceId: string | null; state: StackMemberState; code?: string; replacement?: StackReplacement; } export interface StackJournal { schemaVersion: 1; kind: "cotal-stack-controller"; root: string; space: string; server: string; /** Pinned at the first whole-stack up. An open stack has no delivery member and no credential; * only a user stack has an auth-service member. */ mode: StackMode; members: StackMember[]; /** The broker inputs this stack's broker was last prepared from. A later preparation of the same * stopped broker reuses them rather than a default store, and an explicit request that differs * refuses. Absent on journals written before they were recorded. */ broker?: StackBrokerInputs; operation: { id: string; kind: "up" | "down" | "preserve" | "resume"; phase: "planned" | "applying" | "completed" | "partial" | "unknown" | "refused" | "failed"; recordedAt: string; withAgents: boolean; /** The maintenance attempt a preserve or resume serves. Required for those kinds, absent otherwise; * the maintenance journal stays the authority for the attempt's state and evidence. */ maintenanceAttemptId?: string; }; updatedAt: string; } export { stackJournalPath }; export interface StackBrokerInputs { storeDir: string; maxFileStore?: number; host?: string; } export interface StackJournalSnapshot { journal: StackJournal; sha256: string; } export declare function readStackJournal(root: string): StackJournalSnapshot | undefined; /** A controller-owned stack cannot be preserved in this release, so its preserve, and the backup and * restore that need a preserved cut, refuse while validating, before the seed or any effect. A * root with no stack journal keeps its legacy route. */ export declare function refuseControllerCut(root: string, action: "down --preserve-state" | "backup create" | "backup restore" | "up --restore"): void; /** Compare-and-replace under a private lock beside the journal. A changed journal refuses. */ export declare function writeStackJournal(root: string, expected: StackJournalSnapshot | undefined, next: StackJournal): StackJournalSnapshot; /** Whether the root's maintenance journal fences this stack. A settled active controller restore of * this same stack does not; its attempt is returned so the caller names it * in its result. Any other journal fences. The journal is only read. */ export declare function stackMaintenanceFence(root: string, journal: StackJournal): { fenced: boolean; admittedRestore?: string; }; /** The settled active controller restore that no longer fences this root's own stack, if any. */ export declare function admittedStackRestore(root: string): string | undefined; /** The stack facts `maintenanceAdmitsStackWork` compares while a root holds a maintenance journal. * The broker adapter and a service restart read them here; a manager-service launch carries them, * because that adapter reads no stack journal. Undefined without a maintenance journal or when the * root's stack journal names another space. */ export declare function maintenanceStackView(root: string, space: string): ControllerStackView | undefined; /** Take the root's maintenance lock for one whole-stack operation. Every whole-stack operation * holds it for its whole run, and a dead holder is reclaimed only when its PID is proven dead. A * live holder is a known refusal that names its PID; nothing was read or changed. */ export declare function acquireStackOperationLock(root: string): MaintenanceLock; /** An earlier operation that had no recorded outcome, and the member evidence it was settled from. */ export interface StackSettlement { operation: StackJournal["operation"]; members: { role: StackRole; instanceId: string; process: "running" | "gone"; }[]; } /** Whether the journal's last operation has no recorded outcome. */ export declare function stackOperationUnsettled(journal: StackJournal): boolean; /** Settle each member's retained native stop from its adapter's own receipt, under that member's * definition lock, before an `up` or `down` classifies the members. The caller holds the root's * maintenance lock. A member whose adapter confirms less keeps its stop, and the classification * refuses it as before. A preserve or resume with no outcome is left to its maintenance attempt. */ export declare function settleRetainedStackStops(journal: StackJournal): Promise; /** The remedy a refusal over a retained, published member stop names. */ export declare const abandonRemedy: (requestId: string) => string; /** Abandon the one member stop that retains `requestId`, on an operator's explicit request, under that * member's definition lock, before a `down` classifies the members. The caller holds the root's * maintenance lock. A native stop is abandoned through its adapter, and a broker's or another * member's signal stop once its process is gone. No member retaining it refuses `TARGET_NOT_FOUND`, * and nothing is written. */ export declare function abandonRetainedStackStop(journal: StackJournal, requestId: string): Promise<{ role: StackRole; row: ServiceInstanceSnapshot; abandonment: ServiceAdapterStopAbandonment; }>; /** Settle an earlier `up` or `down` that has no recorded outcome, from evidence only. The caller * holds the root's maintenance lock, which every whole-stack operation holds for its whole run, so * none of them is in flight. Each member is then classified under its definition's writer lock, so * no launch or stop of it is in flight either: its exact process runs, or it is proven gone (PID and * creation token) and recorded so. The operation is recorded as failed. A member it planned is * recorded as unknown when an instance exists and not attempted when none does. A member that is * neither live nor proven gone refuses; instances settled before that refusal stay settled, and * the operation stays unsettled. A settled journal, and a preserve or resume (decided by its * maintenance attempt), are returned unchanged. */ export declare function settleStackOperation(lock: MaintenanceLock, snapshot: StackJournalSnapshot): { snapshot: StackJournalSnapshot; settled?: StackSettlement; }; /** The journal with each journaled replacement resolved, and each member row that has no recorded * instance given the one the settlement classified under that member's stack definition. An `up` * cut off during a member's readiness leaves that member's instance unrecorded; it is the stack's * own member, never another dependant. */ export declare function withSettledMembers(journal: StackJournal, settlement: StackSettlement | undefined): StackJournal; /** The read-only classification `settleStackOperation` would record, for a preview. Writes nothing. */ export declare function previewStackSettlement(journal: StackJournal): StackSettlement | undefined; /** The one-line notice a command prints after it settled an earlier operation. */ export declare function stackSettlementNotice(settled: StackSettlement): string; export declare function newStackOperation(kind: "up" | "down", withAgents?: boolean): StackJournal["operation"]; /** Whether this root records a controller stack for `space` with no live manager instance. False * when it records none, or an unreadable one, so a remote or legacy mesh keeps its own answer. */ export declare function stackManagerAbsent(root: string, space: string): boolean; /** The controller stack definition a legacy component name stands for in `space`, when that * definition is saved there. A controller stack names its members stack-. */ export declare function stackMemberFor(space: string, name: string): string | undefined; /** The controller stack definitions saved for `space` that start a stack at `root`. */ export declare function stackDefinitionsAt(space: string, root: string): ServiceDefinitionSnapshot[]; /** Remove the controller stack definitions saved for `space` at `root`, with their stopped instance * records, as `service remove` does, so that another folder can start a stack under the same name. * A definition saved for another root is left alone. Nothing is removed while the root holds * preserved state, which resumes from them, or while any of their instances is not removable; * `kept` names them, why, and the route that removes them. */ export declare function releaseStackDefinitions(root: string, space: string): { released: string[]; kept?: { names: string[]; why: string; }; }; /** Legacy pidfile records under the root that may front a process, excluding the settled records * the stack's own controller children publish for themselves: a record counts as owned only when * its PID is the live creation-pinned process of a stack member instance. Nothing is adopted. */ export declare function foreignLocalProcesses(root: string, space: string): string[]; /** A record under a root that may still front a process: whose descriptor names it, its path * relative to the root, the pid it holds when one parses, and why it counts. */ export interface RecordedProcess { readonly name: string; readonly path: string; readonly pid?: number; readonly note: string; } /** How a refusal lists one record: ` at ()`. */ export declare const recordedProcessLine: (row: RecordedProcess) => string; /** Every local-process record under the root that may still front a process. Each descriptor's * candidates are read for `spaces`, and a `{space}` template's records for any other space are * found by listing what it names under `.cotal`. Nothing is read outside `/.cotal`: a link on * a record's path is listed at the link, unread. A live or unanswered pid, an unattributable, * unreadable or oversized record, one that is not a regular file or changed while it was read, a * record whose space segment decodes to no space and a directory that cannot be listed all count; * only an empty or ESRCH-proven dead record of a known space does not. Each path is read once and * each unread one listed once, where reading stopped, whichever descriptor reached it first. Read * only: nothing is stopped, adopted or removed. */ export declare function recordedProcessesUnder(root: string, spaces: readonly string[]): RecordedProcess[]; /** No member of this stack ever launched: no member row names an instance or a replacement, and no * stack definition in its space has an instance record. No process of it runs and no broker of it * wrote a store, so its journal pins nothing for the next up and a down has nothing to stop. */ export declare function stackNeverStarted(journal: StackJournal): boolean; /** An observed process state that counts as stopped for a whole-stack stop. */ export declare const processGone: (state: string) => state is "gone" | "previous-boot" | "foreign-pid"; /** What stopping the stack's broker would cut off outside its recorded members refuses before the * first signal: another live or unresolved service instance in the space, a legacy pidfile process * under the root, or another registration that names the same broker address. Bare `down` and * `down --preserve-state` both run it; `next` is the command to run once the named dependants are * stopped. */ export declare function stackScopeRefusals(journal: StackJournal, next: string): void; //# sourceMappingURL=stack-journal.d.ts.map