@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
131 lines • 6.27 kB
TypeScript
/**
* Unified web console orchestrator.
*
* Ties together leader election, console startup, follower wiring,
* and session lifecycle management. This is the main entry point
* called by the DI container during deferred setup.
*
* Flow:
* 1. Run leader election (read lock file, claim or follow)
* 2. If leader: start web server on fixed port, mount ingest routes, start heartbeat
* 3. If follower: register forwarding sinks with LogManager, start session heartbeat
*
* @since v2.1.0 — Issue #1700
*/
import type { UnifiedLogEntry } from '../../logging/types.js';
import type { MetricSnapshot } from '../../metrics/types.js';
import type { MemoryLogSink } from '../../logging/sinks/MemoryLogSink.js';
import type { MemoryMetricsSink } from '../../metrics/sinks/MemoryMetricsSink.js';
import { logger } from '../../utils/logger.js';
import { isLeaderWebConsoleReachable, forceClaimLeadership, detectLegacyLeader, readLeaderLock, deleteLeaderLock, type ElectionResult, type ConsoleLeaderInfo, type LeaderPreferenceDecision } from './LeaderElection.js';
import { findPidOnPort } from './StaleProcessRecovery.js';
/**
* Options for starting the unified console.
*/
export interface UnifiedConsoleOptions {
/** This process's unique session ID */
sessionId: string;
/** Stable Dollhouse session identity shown to humans and used for persistence. */
stableSessionId: string;
/** Portfolio base directory (for startWebServer) */
portfolioDir: string;
/** Log memory sink (for console history) */
memorySink: MemoryLogSink;
/** Metrics memory sink */
metricsSink?: MemoryMetricsSink;
/** MCP-AQL handler for permission routes (typed as any to avoid circular imports) */
mcpAqlHandler?: any;
/** Callback to register a log sink with the LogManager */
registerLogSink: (sink: {
write(entry: UnifiedLogEntry): void;
flush(): Promise<void>;
close(): Promise<void>;
}) => void;
/** Callback to wire SSE broadcasts after web server starts */
wireSSEBroadcasts: (webResult: {
logBroadcast?: (entry: UnifiedLogEntry) => void;
metricsOnSnapshot?: (snapshot: MetricSnapshot) => void;
}, metricsSink?: MemoryMetricsSink) => void;
/** Console port override from config file. Falls back to env var if not provided. */
port?: number;
}
/**
* Result of starting the unified console.
*/
export interface UnifiedConsoleResult {
role: 'leader' | 'follower';
election: ElectionResult;
/** Port the console is running on (leader only) */
port?: number;
/** Cleanup function to call on shutdown */
cleanup: () => Promise<void>;
}
/**
* Check for a running legacy (pre-authentication) DollhouseMCP console and
* log a WARN-level message if one is found (#1794).
*
* Extracted from `startUnifiedConsole` so the wiring can be integration-
* tested in isolation without spinning up a full web server and leader
* election. The implementation is fire-and-forget: detection failures
* are logged at DEBUG and never propagate, because a failure here must
* not block leader election of the authenticated console.
*
* @param currentPort - The port the authenticated console intends to
* bind to. Used in the warning message to help the
* user tell the two consoles apart.
* @param detect - Optional injection point for the detection
* function. Defaults to `detectLegacyLeader`. Tests
* pass a stub.
* @param log - Optional injection point for the logger. Defaults
* to the module logger. Tests pass a spy.
* @returns The legacy leader info from `detect()`, or null if detection
* threw. Exposed so tests can assert the full result shape.
*/
export declare function warnIfLegacyConsolePresent(currentPort: number, detect?: typeof detectLegacyLeader, log?: typeof logger): Promise<Awaited<ReturnType<typeof detectLegacyLeader>> | null>;
export interface PortLeaderDiscovery {
leaderInfo: ConsoleLeaderInfo | null;
ownerPid: number | null;
source: 'api' | 'lock' | 'synthetic' | 'none';
}
export interface BindFailureRecoveryResult extends PortLeaderDiscovery {
lockCleanupAttempted: boolean;
lockCleanupPerformed: boolean;
}
export interface PortOwnerReplacementDecision {
shouldEvict: boolean;
ownerPid: number | null;
preference: LeaderPreferenceDecision | null;
}
interface FollowerAuthorityResolution {
election: ElectionResult;
discovery: PortLeaderDiscovery | null;
replacement: PortOwnerReplacementDecision | null;
forcedClaim: boolean;
}
interface FollowerAuthorityDependencies {
isLeaderWebConsoleReachableImpl?: typeof isLeaderWebConsoleReachable;
discoverLeaderServingPortImpl?: typeof discoverLeaderServingPort;
forceClaimLeadershipImpl?: typeof forceClaimLeadership;
deleteLeaderLockImpl?: typeof deleteLeaderLock;
}
interface DiscoveryDependencies {
fetchImpl?: typeof fetch;
findPidOnPortImpl?: typeof findPidOnPort;
readLeaderLockImpl?: typeof readLeaderLock;
}
export declare function discoverLeaderServingPort(port: number, authToken: string | null, deps?: DiscoveryDependencies): Promise<PortLeaderDiscovery>;
interface BindFailureRecoveryDependencies extends DiscoveryDependencies {
deleteLeaderLockImpl?: typeof deleteLeaderLock;
}
export declare function recoverLeaderBindFailure(provisionalLeader: ConsoleLeaderInfo, port: number, authToken: string | null, deps?: BindFailureRecoveryDependencies): Promise<BindFailureRecoveryResult>;
export declare function evaluatePortOwnerReplacement(candidateLeader: ConsoleLeaderInfo, fallback: PortLeaderDiscovery): PortOwnerReplacementDecision;
export declare function resolveFollowerAuthority(sessionId: string, consolePort: number, election: ElectionResult, deps?: FollowerAuthorityDependencies): Promise<FollowerAuthorityResolution>;
/**
* Start the unified web console.
*
* Runs leader election, then either starts the full console (leader)
* or sets up event forwarding (follower).
*/
export declare function startUnifiedConsole(options: UnifiedConsoleOptions): Promise<UnifiedConsoleResult>;
export {};
//# sourceMappingURL=UnifiedConsole.d.ts.map