@noosphere/agent-core 0.1.0-alpha.11 → 0.1.0-alpha.12
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/dist/index.cjs +51 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +51 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/EventMonitor.ts","../src/ContainerManager.ts","../src/NoosphereAgent.ts","../src/SchedulerService.ts","../src/utils/CommitmentUtils.ts","../src/utils/ConfigLoader.ts","../src/types/index.ts","../src/utils/RequestIdUtils.ts","../src/index.ts"],"sourcesContent":["import { EventEmitter } from 'events';\nimport { ethers } from 'ethers';\nimport type { AgentConfig, RequestStartedEvent } from './types';\n\nexport interface CheckpointData {\n blockNumber: number;\n blockHash?: string;\n blockTimestamp?: number;\n}\n\nexport interface EventMonitorOptions {\n loadCheckpoint?: () => CheckpointData | undefined;\n saveCheckpoint?: (checkpoint: CheckpointData) => void;\n}\n\nexport class EventMonitor extends EventEmitter {\n private provider!: ethers.WebSocketProvider | ethers.JsonRpcProvider;\n private router!: ethers.Contract;\n private coordinator!: ethers.Contract;\n private lastProcessedBlock: number;\n private useWebSocket: boolean;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 10;\n private isReconnecting = false;\n private heartbeatInterval: NodeJS.Timeout | null = null;\n private lastEventTime = Date.now();\n private checkpointCallbacks?: EventMonitorOptions;\n\n constructor(\n private config: AgentConfig,\n private routerAbi: any[],\n private coordinatorAbi: any[],\n options?: EventMonitorOptions\n ) {\n super();\n this.lastProcessedBlock = config.deploymentBlock || 0;\n this.useWebSocket = false;\n this.checkpointCallbacks = options;\n }\n\n async connect(): Promise<void> {\n try {\n // Try WebSocket first for push-based events\n if (this.config.wsRpcUrl) {\n this.provider = new ethers.WebSocketProvider(this.config.wsRpcUrl);\n // Note: Do NOT call _start() - it causes race condition with auto-initialization\n // The provider will initialize automatically on first request\n this.useWebSocket = true;\n console.log('✓ Connected via WebSocket (push-based events)');\n } else {\n throw new Error('WebSocket URL not provided');\n }\n } catch (error) {\n // Fallback to HTTP with polling\n console.warn('WebSocket unavailable, falling back to HTTP polling');\n this.provider = new ethers.JsonRpcProvider(this.config.rpcUrl);\n this.useWebSocket = false;\n }\n\n // Initialize contract instances\n this.router = new ethers.Contract(this.config.routerAddress, this.routerAbi, this.provider);\n\n this.coordinator = new ethers.Contract(\n this.config.coordinatorAddress,\n this.coordinatorAbi,\n this.provider\n );\n }\n\n async start(): Promise<void> {\n // Load checkpoint from callback or use deployment block\n if (this.checkpointCallbacks?.loadCheckpoint) {\n const checkpoint = this.checkpointCallbacks.loadCheckpoint();\n if (checkpoint) {\n this.lastProcessedBlock = checkpoint.blockNumber;\n }\n }\n\n console.log(`Starting from block ${this.lastProcessedBlock}`);\n\n // Replay missed events\n await this.replayEvents(this.lastProcessedBlock, 'latest');\n\n // Start real-time listening\n if (this.useWebSocket) {\n await this.startWebSocketListening();\n } else {\n await this.startPolling();\n }\n }\n\n private async replayEvents(fromBlock: number, toBlock: string | number): Promise<void> {\n console.log(`Replaying events from block ${fromBlock} to ${toBlock}`);\n\n const currentBlock = await this.provider.getBlockNumber();\n const toBlockNumber = toBlock === 'latest' ? currentBlock : Number(toBlock);\n\n // Query historical events in chunks to avoid RPC limits\n const chunkSize = 10000;\n for (let start = fromBlock; start <= toBlockNumber; start += chunkSize) {\n const end = Math.min(start + chunkSize - 1, toBlockNumber);\n\n const events = await this.coordinator.queryFilter(\n this.coordinator.filters.RequestStarted(),\n start,\n end\n );\n\n for (const event of events) {\n await this.processEvent(event);\n }\n\n if (events.length > 0) {\n this.saveCheckpoint(end);\n }\n }\n\n console.log(`Replayed events up to block ${toBlockNumber}`);\n }\n\n private async startWebSocketListening(): Promise<void> {\n console.log('Starting WebSocket event listening...');\n\n // Listen for RequestStarted events\n this.coordinator.on('RequestStarted', async (...args) => {\n const event = args[args.length - 1]; // Last argument is the event object\n this.lastEventTime = Date.now();\n await this.processEvent(event);\n\n const blockNumber = event.blockNumber;\n if (blockNumber - this.lastProcessedBlock >= 10) {\n this.saveCheckpoint(blockNumber);\n }\n });\n\n // Setup WebSocket error/close handlers for reconnection\n if (this.provider instanceof ethers.WebSocketProvider) {\n const wsProvider = this.provider as ethers.WebSocketProvider;\n\n // Access the underlying WebSocket to detect connection issues\n // ethers v6 exposes websocket through _websocket property (internal)\n const ws = (wsProvider as any)._websocket;\n if (ws) {\n ws.on('close', () => {\n console.warn('⚠️ WebSocket connection closed');\n this.handleDisconnect();\n });\n ws.on('error', (error: Error) => {\n console.error('⚠️ WebSocket error:', error.message);\n this.handleDisconnect();\n });\n }\n }\n\n // Start heartbeat to detect silent disconnections\n this.startHeartbeat();\n\n console.log('✓ WebSocket event listener started');\n }\n\n private startHeartbeat(): void {\n // Check connection health every 2 minutes (reduced from 30s to avoid rate limits)\n this.heartbeatInterval = setInterval(async () => {\n try {\n if (this.provider instanceof ethers.WebSocketProvider) {\n // Try to get block number as heartbeat\n const blockNumber = await this.provider.getBlockNumber();\n\n // If we haven't received events for 3+ minutes but blocks are advancing,\n // we might have a stale subscription - replay missed events\n const timeSinceLastEvent = Date.now() - this.lastEventTime;\n if (timeSinceLastEvent > 180000 && blockNumber > this.lastProcessedBlock + 5) {\n console.log(`⚠️ No events for ${Math.round(timeSinceLastEvent / 1000)}s, checking for missed events...`);\n await this.replayMissedEvents();\n }\n }\n } catch (error) {\n console.error('⚠️ Heartbeat failed, WebSocket may be disconnected');\n this.handleDisconnect();\n }\n }, 120000); // 2 minutes\n }\n\n private async replayMissedEvents(): Promise<void> {\n try {\n const currentBlock = await this.provider.getBlockNumber();\n if (currentBlock > this.lastProcessedBlock) {\n console.log(`📥 Replaying events from block ${this.lastProcessedBlock + 1} to ${currentBlock}`);\n await this.replayEvents(this.lastProcessedBlock + 1, currentBlock);\n this.lastEventTime = Date.now();\n }\n } catch (error) {\n console.error('Failed to replay missed events:', error);\n }\n }\n\n private handleDisconnect(): void {\n if (this.isReconnecting) return;\n this.isReconnecting = true;\n\n // Clear heartbeat during reconnection\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n\n // Attempt reconnection\n this.reconnect().finally(() => {\n this.isReconnecting = false;\n });\n }\n\n private async startPolling(): Promise<void> {\n console.log('Starting HTTP polling (fallback mode)...');\n\n const pollingInterval = this.config.pollingInterval || 12000; // 12 seconds default\n let lastBlock = await this.provider.getBlockNumber();\n\n setInterval(async () => {\n try {\n const currentBlock = await this.provider.getBlockNumber();\n if (currentBlock > lastBlock) {\n const events = await this.coordinator.queryFilter(\n this.coordinator.filters.RequestStarted(),\n lastBlock + 1,\n currentBlock\n );\n\n for (const event of events) {\n await this.processEvent(event);\n }\n\n if (events.length > 0) {\n this.saveCheckpoint(currentBlock);\n }\n\n lastBlock = currentBlock;\n }\n } catch (error) {\n console.error('Polling error:', error);\n }\n }, pollingInterval);\n }\n\n private async processEvent(event: any): Promise<void> {\n // Parse event data\n // The RequestStarted event has: (requestId, subscriptionId, containerId, commitment)\n // Most fields are in the commitment struct\n const commitment = event.args.commitment;\n\n const requestStartedEvent: RequestStartedEvent = {\n requestId: event.args.requestId,\n subscriptionId: event.args.subscriptionId,\n containerId: event.args.containerId,\n interval: commitment.interval,\n redundancy: commitment.redundancy,\n useDeliveryInbox: commitment.useDeliveryInbox,\n feeAmount: commitment.feeAmount,\n feeToken: commitment.feeToken,\n verifier: commitment.verifier,\n coordinator: commitment.coordinator,\n walletAddress: commitment.walletAddress,\n blockNumber: event.blockNumber,\n };\n\n // Emit event for NoosphereAgent to handle\n this.emit('RequestStarted', requestStartedEvent);\n }\n\n private async reconnect(): Promise<void> {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error('Max reconnection attempts reached. Falling back to HTTP polling.');\n this.useWebSocket = false;\n await this.connect();\n await this.startPolling();\n return;\n }\n\n const backoff = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 60000);\n console.log(\n `🔄 Reconnecting in ${backoff}ms (attempt ${this.reconnectAttempts + 1}/${this.maxReconnectAttempts})`\n );\n\n await new Promise((resolve) => setTimeout(resolve, backoff));\n\n try {\n // Clean up old listeners before reconnecting\n if (this.coordinator) {\n this.coordinator.removeAllListeners();\n }\n if (this.provider instanceof ethers.WebSocketProvider) {\n try {\n await this.provider.destroy();\n } catch {\n // Ignore destroy errors\n }\n }\n\n await this.connect();\n\n // Replay any missed events since last checkpoint\n await this.replayMissedEvents();\n\n await this.startWebSocketListening();\n console.log('✓ Reconnected successfully');\n this.reconnectAttempts = 0;\n this.lastEventTime = Date.now();\n } catch (error) {\n console.error('Reconnection failed:', error);\n this.reconnectAttempts++;\n await this.reconnect();\n }\n }\n\n private saveCheckpoint(blockNumber: number): void {\n this.lastProcessedBlock = blockNumber;\n\n // Use callback if provided\n if (this.checkpointCallbacks?.saveCheckpoint) {\n this.checkpointCallbacks.saveCheckpoint({\n blockNumber,\n blockTimestamp: Date.now(),\n });\n }\n }\n\n async stop(): Promise<void> {\n // Clear heartbeat interval\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n\n if (this.router) {\n this.router.removeAllListeners();\n }\n if (this.coordinator) {\n this.coordinator.removeAllListeners();\n }\n if (this.provider instanceof ethers.WebSocketProvider) {\n await this.provider.destroy();\n }\n }\n}\n","import Docker from 'dockerode';\nimport fs from 'fs/promises';\nimport path from 'path';\nimport axios from 'axios';\nimport type { ContainerMetadata } from './types';\n\nexport interface ContainerExecutionResult {\n output: string;\n exitCode: number;\n executionTime: number;\n}\n\nexport class ContainerManager {\n private docker: Docker;\n private runningContainers: Set<Docker.Container> = new Set();\n private persistentContainers: Map<string, Docker.Container> = new Map();\n private containerPorts: Map<string, number> = new Map();\n\n constructor() {\n this.docker = new Docker();\n }\n\n async runContainer(\n container: ContainerMetadata,\n input: string,\n timeout: number = 300000, // 5 minutes default\n connectionRetries: number = 5, // Retry up to 5 times for connection issues\n connectionRetryDelayMs: number = 3000 // Wait 3 seconds between retries\n ): Promise<ContainerExecutionResult> {\n const startTime = Date.now();\n\n // Get the port for this container\n const port = container.port ? parseInt(container.port) : 8081; // Default to 8081\n\n // Make HTTP POST request to the persistent container\n // Use container name as host when DOCKER_NETWORK is set (DinD), otherwise localhost\n const containerHost = process.env.DOCKER_NETWORK\n ? `noosphere-${container.name}`\n : 'localhost';\n const url = `http://${containerHost}:${port}/computation`;\n\n // Prepare request body\n let requestBody: any;\n try {\n const parsedInput = JSON.parse(input);\n requestBody = { input: input, ...parsedInput };\n } catch {\n requestBody = { input: input };\n }\n\n let lastError: any;\n\n for (let attempt = 1; attempt <= connectionRetries; attempt++) {\n try {\n const response = await axios.post(url, requestBody, {\n timeout,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n const executionTime = Date.now() - startTime;\n\n // Extract output from response\n let output: string;\n if (typeof response.data === 'string') {\n output = response.data;\n } else if (response.data.output !== undefined) {\n output =\n typeof response.data.output === 'string'\n ? response.data.output\n : JSON.stringify(response.data.output);\n } else {\n output = JSON.stringify(response.data);\n }\n\n return {\n output,\n exitCode: 0,\n executionTime,\n };\n } catch (error: any) {\n lastError = error;\n\n // Only retry on connection refused (container not ready yet)\n if (error.code === 'ECONNREFUSED') {\n if (attempt < connectionRetries) {\n console.log(` ⏳ Container not ready (attempt ${attempt}/${connectionRetries}), retrying in ${connectionRetryDelayMs / 1000}s...`);\n await this.sleep(connectionRetryDelayMs);\n continue;\n }\n }\n\n // Don't retry on other errors, break immediately\n break;\n }\n }\n\n // All retries exhausted or non-retryable error\n const executionTime = Date.now() - startTime;\n\n if (lastError.response) {\n throw new Error(\n `Container HTTP error ${lastError.response.status}: ${JSON.stringify(lastError.response.data)}`\n );\n } else if (lastError.code === 'ECONNREFUSED') {\n throw new Error(\n `Cannot connect to container (port ${container.port || 8081}) after ${connectionRetries} attempts. Is it running?`\n );\n } else if (lastError.code === 'ETIMEDOUT' || lastError.code === 'ECONNABORTED') {\n throw new Error(`Container execution timeout after ${timeout}ms`);\n }\n\n throw lastError;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private async collectContainerResult(\n dockerContainer: Docker.Container,\n workDir: string,\n startTime: number\n ): Promise<ContainerExecutionResult> {\n try {\n // Get exit status\n const inspectData = await dockerContainer.inspect();\n const exitCode = inspectData.State.ExitCode || 0;\n\n // Read output\n const outputPath = path.join(workDir, 'output.json');\n let output = '';\n try {\n output = await fs.readFile(outputPath, 'utf-8');\n } catch (error) {\n // If no output file, try to get container logs\n const logs = await dockerContainer.logs({\n stdout: true,\n stderr: true,\n });\n output = logs.toString();\n }\n\n const executionTime = Date.now() - startTime;\n\n // Remove the container now that we've collected results\n try {\n await dockerContainer.remove({ force: true });\n } catch (error) {\n // Container might already be removed, ignore\n }\n\n // Cleanup work directory\n await fs.rm(workDir, { recursive: true, force: true });\n\n return {\n output,\n exitCode,\n executionTime,\n };\n } catch (error) {\n // Cleanup container and work directory on error\n try {\n await dockerContainer.remove({ force: true });\n } catch {\n // Ignore removal errors\n }\n await fs.rm(workDir, { recursive: true, force: true }).catch(() => {});\n throw error;\n }\n }\n\n private async pullImage(image: string, tag: string): Promise<void> {\n const imageTag = `${image}:${tag}`;\n\n try {\n // Check if image exists\n await this.docker.getImage(imageTag).inspect();\n console.log(`Image ${imageTag} already exists`);\n } catch {\n // Image doesn't exist, pull it\n console.log(`Pulling image ${imageTag}...`);\n\n return new Promise((resolve, reject) => {\n this.docker.pull(imageTag, (err: any, stream: NodeJS.ReadableStream) => {\n if (err) return reject(err);\n\n this.docker.modem.followProgress(\n stream,\n (err: any) => {\n if (err) return reject(err);\n console.log(`✓ Pulled image ${imageTag}`);\n resolve();\n },\n (event: any) => {\n // Progress update\n if (event.status) {\n console.log(`${event.status} ${event.progress || ''}`);\n }\n }\n );\n });\n });\n }\n }\n\n private async waitForContainer(container: Docker.Container): Promise<any> {\n return new Promise((resolve, reject) => {\n container.wait((err, data) => {\n if (err) return reject(err);\n resolve(data);\n });\n });\n }\n\n private timeout(ms: number): Promise<null> {\n return new Promise((resolve) => setTimeout(() => resolve(null), ms));\n }\n\n private parseMemory(memory: string): number {\n const units: { [key: string]: number } = {\n b: 1,\n kb: 1024,\n mb: 1024 * 1024,\n gb: 1024 * 1024 * 1024,\n };\n\n const match = memory.toLowerCase().match(/^(\\d+)\\s*(b|kb|mb|gb)$/);\n if (!match) {\n throw new Error(`Invalid memory format: ${memory}`);\n }\n\n const [, value, unit] = match;\n return parseInt(value, 10) * units[unit];\n }\n\n async checkDockerAvailable(): Promise<boolean> {\n try {\n await this.docker.ping();\n return true;\n } catch {\n return false;\n }\n }\n\n async getDockerInfo(): Promise<any> {\n return this.docker.info();\n }\n\n /**\n * Cleanup all running containers\n * Called when agent is shutting down\n */\n async cleanup(): Promise<void> {\n if (this.runningContainers.size === 0) {\n return;\n }\n\n console.log(`🧹 Cleaning up ${this.runningContainers.size} running containers...`);\n\n const cleanupPromises = Array.from(this.runningContainers).map(async (container) => {\n try {\n const inspect = await container.inspect();\n if (inspect.State.Running) {\n console.log(` Stopping container ${inspect.Id.slice(0, 12)}...`);\n await container.stop({ t: 10 }); // 10 second grace period\n await container.remove({ force: true });\n }\n } catch (error) {\n // Container might already be stopped/removed\n console.warn(` Warning: Failed to cleanup container:`, (error as Error).message);\n }\n });\n\n await Promise.all(cleanupPromises);\n this.runningContainers.clear();\n console.log('✓ Container cleanup completed');\n }\n\n /**\n * Get number of running containers\n */\n getRunningContainerCount(): number {\n return this.runningContainers.size;\n }\n\n /**\n * Pre-pull container images and start persistent containers on startup\n * This speeds up request handling by having containers ready\n */\n async prepareContainers(containers: Map<string, ContainerMetadata>): Promise<void> {\n if (containers.size === 0) {\n console.log('No containers to prepare');\n return;\n }\n\n console.log(`\\n🚀 Preparing ${containers.size} containers...`);\n\n const pullAndStartPromises = Array.from(containers.entries()).map(async ([id, container]) => {\n const imageTag = `${container.image}:${container.tag || 'latest'}`;\n\n try {\n console.log(` Pulling ${imageTag}...`);\n await this.pullImage(container.image, container.tag || 'latest');\n console.log(` ✓ ${imageTag} ready`);\n } catch (error) {\n console.error(` ❌ Failed to pull ${imageTag}:`, (error as Error).message);\n // Skip container start if pull failed\n return;\n }\n\n try {\n // Start persistent container\n await this.startPersistentContainer(id, container);\n } catch (error) {\n console.error(` ❌ Failed to start ${container.name || container.image}:`, (error as Error).message);\n }\n });\n\n await Promise.all(pullAndStartPromises);\n console.log('✓ All containers prepared\\n');\n }\n\n /**\n * Start a persistent container that stays running\n */\n private async startPersistentContainer(\n containerId: string,\n metadata: ContainerMetadata\n ): Promise<void> {\n // Use metadata.name for Docker container name (human-readable)\n // This matches the hostname used in runContainer()\n const containerName = `noosphere-${metadata.name}`;\n const imageTag = `${metadata.image}:${metadata.tag || 'latest'}`;\n\n // Check if container already exists\n const existingContainer = this.docker.getContainer(containerName);\n try {\n const inspect = await existingContainer.inspect();\n if (inspect.State.Running) {\n console.log(` ✓ Container ${containerName} already running`);\n this.persistentContainers.set(containerId, existingContainer);\n return;\n } else {\n // Container exists but stopped - try to start it\n try {\n await existingContainer.start();\n console.log(` ✓ Started existing container ${containerName}`);\n this.persistentContainers.set(containerId, existingContainer);\n return;\n } catch (startErr) {\n // Failed to start, remove and recreate\n console.log(` Removing stopped container ${containerName} to recreate...`);\n await existingContainer.remove({ force: true });\n }\n }\n } catch (err) {\n // Container doesn't exist, will create new one\n }\n\n // Create new persistent container\n const dockerNetwork = process.env.DOCKER_NETWORK;\n const createOptions: Docker.ContainerCreateOptions = {\n name: containerName,\n Image: imageTag,\n Tty: false,\n AttachStdout: false,\n AttachStderr: false,\n ExposedPorts: metadata.port ? { [`${metadata.port}/tcp`]: {} } : undefined,\n HostConfig: {\n AutoRemove: false, // Keep container for reuse\n // Only bind ports to host when not using Docker network (local dev)\n PortBindings:\n metadata.port && !dockerNetwork\n ? {\n [`${metadata.port}/tcp`]: [{ HostPort: metadata.port }],\n }\n : undefined,\n // Join the specified Docker network for DinD communication\n NetworkMode: dockerNetwork || undefined,\n },\n Env: metadata.env ? Object.entries(metadata.env).map(([k, v]) => `${k}=${v}`) : undefined,\n };\n\n // Add resource limits\n if (metadata.requirements) {\n const resources: any = {};\n\n if (metadata.requirements.memory) {\n resources.Memory = this.parseMemory(metadata.requirements.memory);\n }\n\n if (metadata.requirements.cpu) {\n resources.NanoCpus = metadata.requirements.cpu * 1e9;\n }\n\n if (metadata.requirements.gpu) {\n createOptions.HostConfig!.DeviceRequests = [\n {\n Driver: 'nvidia',\n Count: -1,\n Capabilities: [['gpu']],\n },\n ];\n }\n\n if (Object.keys(resources).length > 0) {\n createOptions.HostConfig = {\n ...createOptions.HostConfig,\n ...resources,\n };\n }\n }\n\n const dockerContainer = await this.docker.createContainer(createOptions);\n await dockerContainer.start();\n\n this.persistentContainers.set(containerId, dockerContainer);\n if (metadata.port) {\n this.containerPorts.set(containerId, parseInt(metadata.port));\n }\n\n console.log(` ✓ Started persistent container ${containerName}`);\n }\n\n /**\n * Stop and remove all persistent containers\n */\n async stopPersistentContainers(): Promise<void> {\n if (this.persistentContainers.size === 0) {\n return;\n }\n\n console.log(`\\n🛑 Stopping ${this.persistentContainers.size} persistent containers...`);\n\n const stopPromises = Array.from(this.persistentContainers.entries()).map(\n async ([id, container]) => {\n try {\n const inspect = await container.inspect();\n if (inspect.State.Running) {\n console.log(` Stopping ${inspect.Name}...`);\n await container.stop({ t: 10 });\n }\n await container.remove({ force: true });\n console.log(` ✓ Stopped ${inspect.Name}`);\n } catch (error) {\n console.warn(` Warning: Failed to stop container ${id}:`, (error as Error).message);\n }\n }\n );\n\n await Promise.all(stopPromises);\n this.persistentContainers.clear();\n this.containerPorts.clear();\n console.log('✓ All persistent containers stopped\\n');\n }\n}\n","import { ethers } from 'ethers';\nimport { EventMonitor } from './EventMonitor';\nimport { ContainerManager } from './ContainerManager';\nimport { SchedulerService, SchedulerConfig } from './SchedulerService';\nimport { WalletManager, KeystoreManager } from '@noosphere/crypto';\nimport { RegistryManager } from '@noosphere/registry';\nimport { ABIs } from '@noosphere/contracts';\nimport { CommitmentUtils } from './utils/CommitmentUtils';\nimport { ConfigLoader } from './utils/ConfigLoader';\nimport type {\n AgentConfig,\n RequestStartedEvent,\n ContainerMetadata,\n Commitment,\n NoosphereAgentConfig,\n} from './types';\n\nexport interface ComputeDeliveredEvent {\n requestId: string;\n subscriptionId: number;\n interval: number;\n containerId: string;\n redundancy: number;\n feeAmount: string;\n feeToken: string;\n input: string;\n output: string;\n txHash: string;\n blockNumber: number;\n gasUsed: bigint;\n gasPrice: bigint;\n}\n\nexport interface RequestStartedCallbackEvent {\n requestId: string;\n subscriptionId: number;\n interval: number;\n containerId: string;\n redundancy: number;\n feeAmount: string;\n feeToken: string;\n verifier: string;\n walletAddress: string;\n blockNumber: number;\n}\n\n/**\n * Event data for commitment success (scheduler prepareNextInterval)\n */\nexport interface CommitmentSuccessCallbackEvent {\n subscriptionId: bigint;\n interval: bigint;\n txHash: string;\n blockNumber: number;\n gasUsed: string;\n gasPrice: string;\n gasCost: string;\n}\n\nexport interface CheckpointData {\n blockNumber: number;\n blockHash?: string;\n blockTimestamp?: number;\n}\n\nexport interface RetryableEvent {\n requestId: string;\n subscriptionId: number;\n interval: number;\n containerId: string;\n retryCount: number;\n}\n\nexport interface NoosphereAgentOptions {\n config: AgentConfig;\n routerAbi?: any[]; // Optional - defaults to ABIs.Router from @noosphere/contracts\n coordinatorAbi?: any[]; // Optional - defaults to ABIs.Coordinator from @noosphere/contracts\n getContainer?: (containerId: string) => ContainerMetadata | undefined;\n containers?: Map<string, ContainerMetadata>; // Container map from config\n walletManager?: WalletManager; // Optional - provide pre-initialized WalletManager\n paymentWallet?: string; // Optional - WalletFactory wallet address for the agent\n schedulerConfig?: Partial<SchedulerConfig>; // Optional - scheduler configuration from config.json\n onRequestStarted?: (event: RequestStartedCallbackEvent) => void; // Callback when request is received\n onRequestProcessing?: (requestId: string) => void; // Callback when request processing starts\n onRequestSkipped?: (requestId: string, reason: string) => void; // Callback when request is skipped\n onRequestFailed?: (requestId: string, error: string, txHash?: string) => void; // Callback when request fails (txHash included if tx was sent)\n onComputeDelivered?: (event: ComputeDeliveredEvent) => void; // Callback when compute is delivered\n onCommitmentSuccess?: (event: CommitmentSuccessCallbackEvent) => void; // Callback when scheduler prepares interval\n isRequestProcessed?: (requestId: string) => boolean; // Check if request is already processed (completed/failed)\n loadCheckpoint?: () => CheckpointData | undefined; // Load checkpoint from storage\n saveCheckpoint?: (checkpoint: CheckpointData) => void; // Save checkpoint to storage\n // Retry configuration\n maxRetries?: number; // Maximum retry attempts for failed requests (default: 3)\n retryIntervalMs?: number; // Interval to check for retryable events (default: 30000ms)\n getRetryableEvents?: (maxRetries: number) => RetryableEvent[]; // Get events that can be retried\n resetEventForRetry?: (requestId: string) => void; // Reset event status to pending for retry\n}\n\nexport class NoosphereAgent {\n private eventMonitor: EventMonitor;\n private containerManager: ContainerManager;\n private scheduler: SchedulerService;\n private walletManager: WalletManager;\n private registryManager: RegistryManager;\n private router: ethers.Contract;\n private coordinator: ethers.Contract;\n private provider: ethers.JsonRpcProvider;\n private config: AgentConfig;\n private getContainer?: (containerId: string) => ContainerMetadata | undefined;\n private containers?: Map<string, ContainerMetadata>;\n private paymentWallet?: string;\n private isRunning = false;\n private processingRequests = new Set<string>(); // Deduplication: track requests being processed\n private retryTimer?: NodeJS.Timeout; // Timer for retry mechanism\n private maxRetries: number;\n private retryIntervalMs: number;\n\n constructor(private options: NoosphereAgentOptions) {\n this.config = options.config;\n this.provider = new ethers.JsonRpcProvider(options.config.rpcUrl);\n const provider = this.provider;\n\n // Use default ABIs from @noosphere/contracts if not provided\n const routerAbi = options.routerAbi || ABIs.Router;\n const coordinatorAbi = options.coordinatorAbi || ABIs.Coordinator;\n\n // Use provided WalletManager or create from private key\n if (options.walletManager) {\n this.walletManager = options.walletManager;\n } else if (options.config.privateKey) {\n this.walletManager = new WalletManager(options.config.privateKey, provider);\n } else {\n throw new Error(\n 'Either walletManager or config.privateKey must be provided. ' +\n 'Recommended: Use NoosphereAgent.fromKeystore() for production.'\n );\n }\n\n this.containerManager = new ContainerManager();\n this.registryManager = new RegistryManager({\n autoSync: true, // Enable automatic sync with remote registry\n cacheTTL: 3600000, // 1 hour cache\n });\n this.eventMonitor = new EventMonitor(options.config, routerAbi, coordinatorAbi, {\n loadCheckpoint: options.loadCheckpoint,\n saveCheckpoint: options.saveCheckpoint,\n });\n\n // Initialize router contract\n this.router = new ethers.Contract(\n options.config.routerAddress,\n routerAbi,\n this.provider\n );\n\n this.coordinator = new ethers.Contract(\n options.config.coordinatorAddress,\n coordinatorAbi,\n this.walletManager.getWallet()\n );\n\n // Store container sources\n this.getContainer = options.getContainer;\n this.containers = options.containers;\n\n // Initialize scheduler service (with container filter)\n this.scheduler = new SchedulerService(\n provider,\n this.router,\n this.coordinator,\n this.walletManager.getAddress(),\n undefined, // batchReaderAddress (set later)\n undefined, // config (set later)\n this.getContainer // Pass container filter\n );\n\n // Store payment wallet (WalletFactory wallet for the agent)\n this.paymentWallet = options.paymentWallet;\n\n // Validate we have at least one container source\n if (!this.getContainer && (!this.containers || this.containers.size === 0)) {\n console.warn('⚠️ No container source provided. Agent will not be able to execute requests.');\n }\n\n // Initialize retry configuration\n this.maxRetries = options.maxRetries ?? 3;\n this.retryIntervalMs = options.retryIntervalMs ?? 30000; // 30 seconds\n }\n\n /**\n * Initialize NoosphereAgent from config.json (RECOMMENDED)\n * This loads all configuration including containers from a config file\n *\n * @param configPath - Path to config.json file\n * @param routerAbi - Router contract ABI (optional - defaults to ABIs.Router)\n * @param coordinatorAbi - Coordinator contract ABI (optional - defaults to ABIs.Coordinator)\n * @returns Initialized NoosphereAgent\n */\n static async fromConfig(\n configPath: string,\n routerAbi?: any[],\n coordinatorAbi?: any[]\n ): Promise<NoosphereAgent> {\n // Load config from file\n const fullConfig = ConfigLoader.loadFromFile(configPath);\n\n // Extract keystore path and password\n const keystorePath = fullConfig.chain.wallet.keystore?.path;\n const password = fullConfig.chain.wallet.keystore?.password;\n\n if (!keystorePath || !password) {\n throw new Error('Keystore path and password are required in config.chain.wallet.keystore');\n }\n\n // Load containers from config\n const containers = ConfigLoader.getContainersFromConfig(fullConfig);\n\n console.log(`📦 Loaded ${containers.size} containers from config:`);\n for (const [id, container] of containers.entries()) {\n console.log(` - ${id}: ${container.image}:${container.tag || 'latest'}`);\n }\n\n // Create provider\n const provider = new ethers.JsonRpcProvider(fullConfig.chain.rpcUrl);\n\n // Load keystore\n const keystoreManager = new KeystoreManager(keystorePath, password);\n await keystoreManager.load();\n\n // Initialize WalletManager from keystore\n const walletManager = await WalletManager.fromKeystoreManager(keystoreManager, provider);\n\n // Create AgentConfig from NoosphereAgentConfig\n const agentConfig: AgentConfig = {\n rpcUrl: fullConfig.chain.rpcUrl,\n wsRpcUrl: fullConfig.chain.wsRpcUrl,\n routerAddress: fullConfig.chain.routerAddress,\n coordinatorAddress: fullConfig.chain.coordinatorAddress || fullConfig.chain.routerAddress,\n deploymentBlock: fullConfig.chain.deploymentBlock,\n pollingInterval: fullConfig.chain.processingInterval,\n };\n\n // Extract payment wallet (WalletFactory wallet for the agent)\n const paymentWallet = fullConfig.chain.wallet.paymentAddress;\n\n // Create agent with pre-initialized WalletManager and containers\n return new NoosphereAgent({\n config: agentConfig,\n routerAbi,\n coordinatorAbi,\n walletManager,\n containers,\n paymentWallet,\n });\n }\n\n /**\n * Initialize NoosphereAgent from keystore (RECOMMENDED)\n * This is the secure way to initialize an agent in production\n *\n * @param keystorePath - Path to keystore file\n * @param password - Keystore password\n * @param options - Agent configuration options\n * @returns Initialized NoosphereAgent\n */\n static async fromKeystore(\n keystorePath: string,\n password: string,\n options: Omit<NoosphereAgentOptions, 'walletManager'>\n ): Promise<NoosphereAgent> {\n const provider = new ethers.JsonRpcProvider(options.config.rpcUrl);\n\n // Load keystore\n const keystoreManager = new KeystoreManager(keystorePath, password);\n await keystoreManager.load();\n\n // Initialize WalletManager from keystore\n const walletManager = await WalletManager.fromKeystoreManager(keystoreManager, provider);\n\n // Create agent with pre-initialized WalletManager\n return new NoosphereAgent({\n ...options,\n walletManager,\n });\n }\n\n async start(): Promise<void> {\n console.log('Starting Noosphere Agent...');\n\n // Load registry (local + remote sync)\n console.log('📋 Loading container registry...');\n await this.registryManager.load();\n const stats = this.registryManager.getStats();\n console.log(\n `✓ Registry loaded: ${stats.totalContainers} containers, ${stats.totalVerifiers} verifiers`\n );\n\n // Check Docker availability\n const dockerAvailable = await this.containerManager.checkDockerAvailable();\n if (!dockerAvailable) {\n throw new Error('Docker is not available. Please ensure Docker daemon is running.');\n }\n\n // Get wallet info\n const address = this.walletManager.getAddress();\n const balance = await this.walletManager.getBalance();\n console.log(`Agent wallet: ${address}`);\n console.log(`Balance: ${ethers.formatEther(balance)} ETH`);\n\n if (balance === 0n) {\n console.warn('⚠️ Warning: Wallet has zero balance. Agent needs ETH for gas fees.');\n }\n\n // Pre-pull container images if containers are provided\n if (this.containers && this.containers.size > 0) {\n console.log(`\\n🚀 Preparing ${this.containers.size} containers...`);\n await this.containerManager.prepareContainers(this.containers);\n }\n\n // Connect event monitor\n await this.eventMonitor.connect();\n\n // Set up event handler\n this.eventMonitor.on('RequestStarted', async (event: RequestStartedEvent) => {\n await this.handleRequest(event);\n });\n\n // Start listening\n await this.eventMonitor.start();\n\n // Try to get SubscriptionBatchReader address from coordinator\n try {\n const batchReaderAddress = await this.coordinator.getSubscriptionBatchReader();\n if (\n batchReaderAddress &&\n batchReaderAddress !== '0x0000000000000000000000000000000000000000'\n ) {\n console.log(`✓ SubscriptionBatchReader found: ${batchReaderAddress}`);\n\n // Reinitialize scheduler with BatchReader and config from options\n this.scheduler.stop();\n this.scheduler = new SchedulerService(\n this.provider,\n this.router,\n this.coordinator,\n this.walletManager.getAddress(),\n batchReaderAddress,\n this.options.schedulerConfig || {\n cronIntervalMs: 60000, // 1 minute (default)\n syncPeriodMs: 3000, // 3 seconds (default)\n maxRetryAttempts: 3, // 3 retries (default)\n },\n this.getContainer // Pass container filter\n );\n } else {\n console.warn('⚠️ SubscriptionBatchReader not available - subscription sync disabled');\n }\n } catch (error) {\n console.warn('⚠️ Failed to get SubscriptionBatchReader address:', (error as Error).message);\n }\n\n // Start scheduler service\n this.scheduler.start();\n\n // Listen for commitment:success events to handle cases where WebSocket misses events\n this.scheduler.on('commitment:success', async (data: {\n subscriptionId: bigint;\n interval: bigint;\n txHash: string;\n blockNumber: number;\n gasUsed?: string;\n gasPrice?: string;\n gasCost?: string;\n requestStartedEvent?: RequestStartedEvent;\n }) => {\n // Call callback if provided (for DB persistence)\n if (this.options.onCommitmentSuccess) {\n this.options.onCommitmentSuccess({\n subscriptionId: data.subscriptionId,\n interval: data.interval,\n txHash: data.txHash,\n blockNumber: data.blockNumber,\n gasUsed: data.gasUsed || '0',\n gasPrice: data.gasPrice || '0',\n gasCost: data.gasCost || '0',\n });\n }\n\n if (data.requestStartedEvent) {\n console.log(` 📥 Processing RequestStarted from prepare receipt (fallback for missed WebSocket)`);\n await this.handleRequest(data.requestStartedEvent);\n }\n });\n\n // Start retry timer if retry callbacks are provided\n if (this.options.getRetryableEvents && this.options.resetEventForRetry) {\n this.startRetryTimer();\n }\n\n this.isRunning = true;\n console.log('✓ Noosphere Agent is running');\n console.log('Listening for requests...');\n }\n\n /**\n * Start the retry timer for failed requests\n */\n private startRetryTimer(): void {\n if (this.retryTimer) {\n clearInterval(this.retryTimer);\n }\n\n console.log(`🔄 Retry mechanism enabled: max ${this.maxRetries} retries, check every ${this.retryIntervalMs / 1000}s`);\n\n this.retryTimer = setInterval(async () => {\n await this.processRetries();\n }, this.retryIntervalMs);\n }\n\n /**\n * Process retryable failed events (with throttling to avoid rate limits)\n */\n private async processRetries(): Promise<void> {\n if (!this.options.getRetryableEvents || !this.options.resetEventForRetry) {\n return;\n }\n\n const retryableEvents = this.options.getRetryableEvents(this.maxRetries);\n if (retryableEvents.length === 0) {\n return;\n }\n\n // Only retry one event per cycle to avoid rate limiting\n const event = retryableEvents[0];\n\n // Skip if already being processed\n if (this.processingRequests.has(event.requestId)) {\n return;\n }\n\n console.log(`🔄 Retrying request ${event.requestId.slice(0, 10)}... (attempt ${event.retryCount + 1}/${this.maxRetries}, ${retryableEvents.length} remaining)`);\n\n // Reset event to pending\n this.options.resetEventForRetry(event.requestId);\n\n // Re-process the request\n const container = this.getContainerMetadata(event.containerId);\n if (!container) {\n console.log(` ⚠️ Container ${event.containerId.slice(0, 10)}... no longer supported, skipping retry`);\n return;\n }\n\n // Create a synthetic RequestStartedEvent for retry\n const retryEvent: RequestStartedEvent = {\n requestId: event.requestId,\n subscriptionId: BigInt(event.subscriptionId),\n interval: event.interval,\n containerId: event.containerId,\n redundancy: 1,\n useDeliveryInbox: false,\n feeAmount: BigInt(0),\n feeToken: '0x0000000000000000000000000000000000000000',\n walletAddress: '0x0000000000000000000000000000000000000000',\n verifier: '0x0000000000000000000000000000000000000000',\n coordinator: this.config.coordinatorAddress,\n blockNumber: 0,\n };\n\n // Handle the request\n try {\n await this.handleRequest(retryEvent);\n } catch (error) {\n console.log(` ❌ Retry failed for ${event.requestId.slice(0, 10)}...: ${(error as Error).message}`);\n }\n }\n\n /**\n * Convert registry ContainerMetadata to agent-core ContainerMetadata\n */\n private convertRegistryContainer(registryContainer: any): ContainerMetadata {\n // Parse image name and tag from imageName (format: \"image:tag\" or just \"image\")\n const [image, tag] = registryContainer.imageName.split(':');\n\n return {\n id: registryContainer.id,\n name: registryContainer.name,\n image: image,\n tag: tag || 'latest',\n port: registryContainer.port?.toString(),\n env: registryContainer.env,\n requirements: registryContainer.requirements,\n payments: registryContainer.payments\n ? {\n basePrice: registryContainer.payments.basePrice,\n unit: registryContainer.payments.token,\n per: registryContainer.payments.per,\n }\n : undefined,\n verified: registryContainer.verified,\n };\n }\n\n /**\n * Get container metadata from available sources\n * Returns undefined if container is not supported by this agent\n *\n * NOTE: Only checks config-defined sources (callback and containers map).\n * Registry is NOT used here - we only process containers explicitly configured.\n */\n private getContainerMetadata(containerId: string): ContainerMetadata | undefined {\n // 1. Try callback function first (allows config-based filtering)\n if (this.getContainer) {\n const container = this.getContainer(containerId);\n if (container) return container;\n }\n\n // 2. Fallback to containers map from config\n // NOTE: We do NOT use registry here - only explicitly configured containers\n if (this.containers) {\n const container = this.containers.get(containerId);\n if (container) return container;\n }\n\n return undefined;\n }\n\n private async handleRequest(event: RequestStartedEvent): Promise<void> {\n const requestIdShort = event.requestId.slice(0, 10);\n\n // Container filter: Only process events for containers we support\n // This must be checked FIRST, before any DB save or RPC calls\n const container = this.getContainerMetadata(event.containerId);\n if (!container) {\n // Silently skip - we don't support this container\n return;\n }\n\n // Deduplication: Check if this request is already being processed\n if (this.processingRequests.has(event.requestId)) {\n console.log(` ⏭️ Request ${requestIdShort}... already being processed, skipping duplicate`);\n return;\n }\n\n // Check if request has already been processed (completed/failed)\n if (this.options.isRequestProcessed && this.options.isRequestProcessed(event.requestId)) {\n console.log(` ⏭️ Request ${requestIdShort}... already processed, skipping`);\n return;\n }\n\n this.processingRequests.add(event.requestId);\n\n console.log(`\\n[${new Date().toISOString()}] RequestStarted: ${requestIdShort}...`);\n console.log(` SubscriptionId: ${event.subscriptionId}`);\n console.log(` Interval: ${event.interval}`);\n console.log(` ContainerId: ${event.containerId.slice(0, 10)}...`);\n console.log(` 📦 Container: ${container.name} (${container.image}:${container.tag || 'latest'})`);\n\n // Call onRequestStarted callback if provided (saves to DB)\n // This is called AFTER container check, so only supported containers are saved\n if (this.options.onRequestStarted) {\n this.options.onRequestStarted({\n requestId: event.requestId,\n subscriptionId: Number(event.subscriptionId),\n interval: Number(event.interval),\n containerId: event.containerId,\n redundancy: event.redundancy,\n feeAmount: event.feeAmount.toString(),\n feeToken: event.feeToken,\n verifier: event.verifier,\n walletAddress: event.walletAddress,\n blockNumber: event.blockNumber,\n });\n }\n\n // Check if this interval is still current (skip old replayed events)\n // Note: One-time executions (intervalSeconds=0) return type(uint32).max, should not be skipped\n try {\n const currentInterval = await this.router.getComputeSubscriptionInterval(event.subscriptionId);\n const eventInterval = Number(event.interval);\n\n // Skip check only for scheduled subscriptions (not one-time executions)\n // type(uint32).max = 4294967295 indicates one-time execution\n const isOneTimeExecution = currentInterval === 4294967295n;\n\n if (!isOneTimeExecution && currentInterval > eventInterval + 2) {\n console.log(` ⏭️ Skipping old interval ${eventInterval} (current: ${currentInterval})`);\n if (this.options.onRequestSkipped) {\n this.options.onRequestSkipped(event.requestId, `Old interval ${eventInterval} (current: ${currentInterval})`);\n }\n this.processingRequests.delete(event.requestId);\n return;\n }\n } catch (error) {\n console.warn(` Could not verify interval currency:`, (error as Error).message);\n // Continue processing if we can't verify\n }\n\n // Mark this interval as committed (subscription will be tracked by batch reader)\n this.scheduler.markIntervalCommitted(BigInt(event.subscriptionId), BigInt(event.interval));\n\n // Track sent transaction hash for error reporting\n let sentTxHash: string | undefined;\n\n try {\n // Self-coordination: Wait based on position-based priority\n await this.waitForPriority(event);\n\n // Call onRequestProcessing callback if provided\n if (this.options.onRequestProcessing) {\n this.options.onRequestProcessing(event.requestId);\n }\n\n // Check if already fulfilled (redundancy check)\n const currentCount = await this.coordinator.redundancyCount(event.requestId);\n if (currentCount >= event.redundancy) {\n console.log(` ⏭️ Already fulfilled (${currentCount}/${event.redundancy}), skipping`);\n if (this.options.onRequestSkipped) {\n this.options.onRequestSkipped(event.requestId, `Already fulfilled (${currentCount}/${event.redundancy})`);\n }\n this.processingRequests.delete(event.requestId);\n return;\n }\n\n // Container already verified at the start of handleRequest()\n // Using the container variable from there\n\n // Fetch subscription to get client address\n const subscription = await this.router.getComputeSubscription(event.subscriptionId);\n const clientAddress = subscription.client;\n\n if (!clientAddress || clientAddress === '0x0000000000000000000000000000000000000000') {\n console.error(` ❌ Invalid client address for subscription ${event.subscriptionId}`);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, `Invalid client address for subscription ${event.subscriptionId}`);\n }\n return;\n }\n\n console.log(` 📞 Fetching inputs from client: ${clientAddress.slice(0, 10)}...`);\n\n // Call client's getComputeInputs to get the input data\n const clientAbi = [\n 'function getComputeInputs(uint64 subscriptionId, uint32 interval, uint32 timestamp, address caller) external view returns (bytes memory)',\n ];\n const client = new ethers.Contract(clientAddress, clientAbi, this.provider);\n const timestamp = Math.floor(Date.now() / 1000);\n\n let inputBytes: string;\n try {\n inputBytes = await client.getComputeInputs(\n event.subscriptionId,\n event.interval,\n timestamp,\n this.walletManager.getAddress()\n );\n } catch (error) {\n const errorMessage = (error as Error).message || String(error);\n console.error(` ❌ Failed to get inputs from client:`, error);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, `Failed to get inputs: ${errorMessage}`);\n }\n return;\n }\n\n // Convert bytes to string\n const inputData = ethers.toUtf8String(inputBytes);\n console.log(\n ` 📥 Inputs received: ${inputData.substring(0, 100)}${inputData.length > 100 ? '...' : ''}`\n );\n\n // Execute container\n console.log(` ⚙️ Executing...`);\n const result = await this.containerManager.runContainer(\n container,\n inputData,\n 300000 // 5 min timeout\n );\n\n if (result.exitCode !== 0) {\n console.error(` ❌ Container execution failed with exit code ${result.exitCode}`);\n console.error(` 📄 Container output:`, result.output);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, `Container execution failed with exit code ${result.exitCode}`);\n }\n return;\n }\n\n console.log(` ✓ Execution completed in ${result.executionTime}ms`);\n\n // Submit result to coordinator\n console.log(` 📤 Submitting result...`);\n\n // Use the original input data from client (already a string)\n const input = inputData;\n const output = result.output;\n const proof = event.verifier ? result.output : ''; // Use output as proof if verifier exists\n\n // Use the wallet address from the event (subscription's payment wallet)\n const subscriptionWallet = event.walletAddress;\n\n // Create commitment using SDK utility\n const commitment: Commitment = CommitmentUtils.fromEvent(event, subscriptionWallet);\n\n // Encode commitment data using SDK utility\n const commitmentData = CommitmentUtils.encode(commitment);\n\n // Use payment wallet (WalletFactory wallet) if set, otherwise fallback to EOA\n const nodeWallet = this.paymentWallet || this.walletManager.getAddress();\n\n // Send transaction\n const tx = await this.coordinator.reportComputeResult(\n event.interval,\n ethers.toUtf8Bytes(input),\n ethers.toUtf8Bytes(output),\n ethers.toUtf8Bytes(proof),\n commitmentData,\n nodeWallet\n );\n\n // Capture tx hash immediately for error reporting\n sentTxHash = tx.hash;\n console.log(` 📤 Transaction sent: ${tx.hash}`);\n\n // Wait for confirmation\n const receipt = await tx.wait();\n\n if (receipt.status === 1) {\n console.log(` ✓ Result delivered successfully (block ${receipt.blockNumber})`);\n console.log(` 💰 Fee earned: ${ethers.formatEther(event.feeAmount)} ETH`);\n\n // Call onComputeDelivered callback if provided\n if (this.options.onComputeDelivered) {\n this.options.onComputeDelivered({\n requestId: event.requestId,\n subscriptionId: Number(event.subscriptionId),\n interval: Number(event.interval),\n containerId: event.containerId,\n redundancy: event.redundancy,\n feeAmount: event.feeAmount.toString(),\n feeToken: event.feeToken,\n input: input,\n output: output,\n txHash: tx.hash,\n blockNumber: receipt.blockNumber,\n gasUsed: receipt.gasUsed,\n gasPrice: receipt.gasPrice || tx.gasPrice || 0n,\n });\n }\n } else {\n throw new Error(`Delivery transaction failed with status ${receipt.status}`);\n }\n } catch (error) {\n const errorMessage = (error as Error).message || String(error);\n const errorCode = (error as any).code;\n\n // Handle nonce expired error - this usually means another handler already processed this request\n if (errorCode === 'NONCE_EXPIRED' || errorMessage.includes('nonce has already been used') || errorMessage.includes('nonce too low')) {\n console.log(` ⚠️ Nonce expired (likely already processed by another handler)`);\n // Don't mark as failed - it was probably successful via another path\n return;\n }\n\n console.error(` ❌ Error processing request:`, error);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, errorMessage, sentTxHash);\n }\n } finally {\n // Cleanup: Remove from processing set\n this.processingRequests.delete(event.requestId);\n }\n }\n\n /**\n * Self-coordination: Calculate priority and wait\n */\n private async waitForPriority(event: RequestStartedEvent): Promise<void> {\n const priority = this.calculatePriority(event.requestId);\n const maxDelay = event.redundancy === 1 ? 1000 : 200; // 1s for single redundancy, 200ms otherwise\n const delay = Math.floor((priority / 0xffffffff) * maxDelay);\n\n if (delay > 0) {\n console.log(\n ` ⏱️ Priority wait: ${delay}ms (priority: 0x${priority.toString(16).slice(0, 8)})`\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n /**\n * Calculate deterministic priority for this agent and request\n */\n private calculatePriority(requestId: string): number {\n const hash = ethers.keccak256(ethers.concat([requestId, this.walletManager.getAddress()]));\n\n // Use first 8 hex chars as priority (0x00000000 - 0xffffffff)\n return parseInt(hash.slice(2, 10), 16);\n }\n\n async stop(): Promise<void> {\n console.log('Stopping Noosphere Agent...');\n\n // Stop retry timer\n if (this.retryTimer) {\n clearInterval(this.retryTimer);\n this.retryTimer = undefined;\n }\n\n // Stop event monitoring\n await this.eventMonitor.stop();\n\n // Stop scheduler\n this.scheduler.stop();\n\n // Cleanup running containers\n await this.containerManager.cleanup();\n\n // Stop persistent containers\n await this.containerManager.stopPersistentContainers();\n\n this.isRunning = false;\n console.log('✓ Agent stopped');\n }\n\n getStatus(): {\n running: boolean;\n address: string;\n scheduler: {\n totalSubscriptions: number;\n activeSubscriptions: number;\n committedIntervals: number;\n pendingTransactions: number;\n };\n containers: {\n runningCount: number;\n };\n } {\n return {\n running: this.isRunning,\n address: this.walletManager.getAddress(),\n scheduler: this.scheduler.getStats(),\n containers: {\n runningCount: this.containerManager.getRunningContainerCount(),\n },\n };\n }\n\n /**\n * Get scheduler service (for advanced usage)\n */\n getScheduler(): SchedulerService {\n return this.scheduler;\n }\n}\n","/**\n * SchedulerService\n *\n * Manages periodic commitment generation for active subscriptions.\n * Equivalent to Java's CommitmentGenerationService with cron scheduling.\n */\n\nimport { EventEmitter } from 'events';\nimport { Contract, Provider, TransactionReceipt } from 'ethers';\nimport { SubscriptionBatchReaderContract, type ComputeSubscription } from '@noosphere/contracts';\nimport type { RequestStartedEvent } from './types';\n\nexport interface SubscriptionState {\n subscriptionId: bigint;\n routeId: string;\n containerId: string;\n client: string;\n wallet: string;\n activeAt: bigint;\n intervalSeconds: bigint;\n maxExecutions: bigint;\n redundancy: number;\n verifier?: string;\n currentInterval: bigint;\n lastProcessedAt: number;\n pendingTx?: string;\n txAttempts: number;\n}\n\nexport interface SchedulerConfig {\n cronIntervalMs: number; // Default: 60000 (1 minute)\n maxRetryAttempts: number; // Default: 3\n syncPeriodMs: number; // Default: 3000 (3 seconds)\n // Persistence callbacks for committed intervals\n loadCommittedIntervals?: () => string[]; // Load from DB on startup\n saveCommittedInterval?: (key: string) => void; // Save to DB when committed\n}\n\nexport class SchedulerService extends EventEmitter {\n private subscriptions = new Map<string, SubscriptionState>();\n private committedIntervals = new Set<string>(); // subscriptionId:interval\n private pendingTxs = new Map<string, string>(); // key -> txHash\n private intervalTimer?: NodeJS.Timeout;\n private syncTimer?: NodeJS.Timeout;\n private config: SchedulerConfig;\n private batchReader?: SubscriptionBatchReaderContract;\n private lastSyncedId: bigint = 0n;\n private maxSubscriptionId?: bigint;\n private getContainer?: (containerId: string) => any;\n\n constructor(\n private provider: Provider,\n private router: Contract,\n private coordinator: Contract,\n private agentWallet: string,\n batchReaderAddress?: string,\n config?: Partial<SchedulerConfig>,\n getContainer?: (containerId: string) => any\n ) {\n super();\n this.config = {\n cronIntervalMs: config?.cronIntervalMs ?? 60000, // 1 minute\n maxRetryAttempts: config?.maxRetryAttempts ?? 3,\n syncPeriodMs: config?.syncPeriodMs ?? 3000, // 3 seconds\n loadCommittedIntervals: config?.loadCommittedIntervals,\n saveCommittedInterval: config?.saveCommittedInterval,\n };\n this.getContainer = getContainer;\n\n // Initialize SubscriptionBatchReader if address provided\n if (batchReaderAddress) {\n this.batchReader = new SubscriptionBatchReaderContract(batchReaderAddress, provider);\n console.log(`✓ SubscriptionBatchReader configured: ${batchReaderAddress}`);\n } else {\n console.warn('⚠️ SubscriptionBatchReader not configured - subscription sync disabled');\n }\n }\n\n /**\n * Start the scheduler service\n */\n start(): void {\n console.log('🕐 Starting Scheduler Service...');\n console.log(` Commitment generation interval: ${this.config.cronIntervalMs}ms`);\n console.log(` Sync period: ${this.config.syncPeriodMs}ms`);\n\n // Load committed intervals from persistent storage\n if (this.config.loadCommittedIntervals) {\n const loaded = this.config.loadCommittedIntervals();\n for (const key of loaded) {\n this.committedIntervals.add(key);\n }\n if (loaded.length > 0) {\n console.log(` Loaded ${loaded.length} committed intervals from storage`);\n }\n }\n\n // Start commitment generation timer (like Spring @Scheduled cron)\n this.intervalTimer = setInterval(() => this.generateCommitments(), this.config.cronIntervalMs);\n\n // Start periodic sync timer\n this.syncTimer = setInterval(() => this.syncSubscriptions(), this.config.syncPeriodMs);\n\n console.log('✓ Scheduler Service started');\n }\n\n /**\n * Stop the scheduler service\n */\n stop(): void {\n if (this.intervalTimer) {\n clearInterval(this.intervalTimer);\n this.intervalTimer = undefined;\n }\n if (this.syncTimer) {\n clearInterval(this.syncTimer);\n this.syncTimer = undefined;\n }\n console.log('✓ Scheduler Service stopped');\n }\n\n /**\n * Track a new subscription\n */\n trackSubscription(\n subscription: Omit<SubscriptionState, 'currentInterval' | 'lastProcessedAt' | 'txAttempts'>\n ): void {\n const key = subscription.subscriptionId.toString();\n\n if (this.subscriptions.has(key)) {\n console.log(` Subscription ${key} already tracked, updating...`);\n }\n\n // Calculate current interval based on elapsed time\n // Note: Contract uses 1-based indexing: ((timestamp - activeAt) / intervalSeconds) + 1\n const now = Math.floor(Date.now() / 1000);\n const elapsed = now - Number(subscription.activeAt);\n const currentInterval = subscription.intervalSeconds > 0n\n ? BigInt(Math.max(1, Math.floor(elapsed / Number(subscription.intervalSeconds)) + 1))\n : 1n;\n\n this.subscriptions.set(key, {\n ...subscription,\n currentInterval,\n lastProcessedAt: Date.now(),\n txAttempts: 0,\n });\n\n console.log(`✓ Tracking subscription ${key}`);\n this.emit('subscription:tracked', subscription.subscriptionId);\n }\n\n /**\n * Remove a subscription from tracking\n */\n untrackSubscription(subscriptionId: bigint): void {\n const key = subscriptionId.toString();\n if (this.subscriptions.delete(key)) {\n // Clean up committedIntervals for this subscription to prevent memory leak\n const prefix = `${key}:`;\n let cleanedCount = 0;\n for (const commitmentKey of this.committedIntervals) {\n if (commitmentKey.startsWith(prefix)) {\n this.committedIntervals.delete(commitmentKey);\n cleanedCount++;\n }\n }\n if (cleanedCount > 0) {\n console.log(` 🧹 Cleaned up ${cleanedCount} committed intervals for subscription ${key}`);\n }\n console.log(`✓ Stopped tracking subscription ${key}`);\n this.emit('subscription:untracked', subscriptionId);\n }\n }\n\n /**\n * Mark an interval as committed (for RequestStarted events)\n * Also persists to storage if callback is configured\n */\n markIntervalCommitted(subscriptionId: bigint, interval: bigint): void {\n const commitmentKey = `${subscriptionId}:${interval}`;\n this.addCommittedInterval(commitmentKey);\n console.log(` ✓ Marked interval ${interval} as committed for subscription ${subscriptionId}`);\n }\n\n /**\n * Add to committed intervals set and persist to storage\n */\n private addCommittedInterval(key: string): void {\n if (!this.committedIntervals.has(key)) {\n this.committedIntervals.add(key);\n if (this.config.saveCommittedInterval) {\n this.config.saveCommittedInterval(key);\n }\n }\n }\n\n /**\n * Main commitment generation loop (runs every cronIntervalMs)\n * Equivalent to Java's CommitmentGenerationService.generateCommitment()\n */\n private async generateCommitments(): Promise<void> {\n try {\n const timestamp = new Date().toISOString();\n console.log(`\\n🔄 [${timestamp}] Starting commitment generation task...`);\n\n // 1. Prune failed transactions\n this.pruneFailedTxs();\n\n // 2. Process active subscriptions\n await this.processActiveSubscriptions();\n\n const endTimestamp = new Date().toISOString();\n console.log(`✓ [${endTimestamp}] Finished commitment generation task.\\n`);\n } catch (error) {\n console.error('❌ Error in commitment generation:', error);\n this.emit('error', error);\n }\n }\n\n /**\n * Process all active subscriptions\n */\n private async processActiveSubscriptions(): Promise<void> {\n // Get blockchain timestamp instead of local system time\n const latestBlock = await this.provider.getBlock('latest');\n if (!latestBlock) {\n console.warn(' Could not fetch latest block, skipping this cycle');\n return;\n }\n const currentBlockTime = latestBlock.timestamp;\n\n if (this.subscriptions.size === 0) {\n console.log(' No subscriptions to process');\n return;\n }\n\n console.log(` Processing ${this.subscriptions.size} subscription(s)...`);\n\n for (const [subId, sub] of this.subscriptions.entries()) {\n let currentInterval: bigint = 0n;\n try {\n // Validate intervalSeconds to prevent division by zero\n if (sub.intervalSeconds <= 0n) {\n console.warn(` Skipping subscription ${subId}: invalid intervalSeconds (${sub.intervalSeconds})`);\n this.untrackSubscription(sub.subscriptionId);\n continue;\n }\n\n // Get current interval from blockchain (Router contract)\n // This ensures we prepare intervals in the correct sequence\n try {\n currentInterval = BigInt(await this.router.getComputeSubscriptionInterval(sub.subscriptionId));\n } catch (error) {\n console.warn(` Could not get interval from router for subscription ${subId}:`, (error as Error).message);\n // Fall back to local calculation as last resort\n const intervalsSinceActive = BigInt(currentBlockTime) - sub.activeAt;\n currentInterval = (intervalsSinceActive / sub.intervalSeconds) + 1n;\n }\n\n console.log(` Subscription ${subId}: currentInterval=${currentInterval}, maxExecutions=${sub.maxExecutions}, activeAt=${sub.activeAt}`);\n\n // Check if subscription should process\n if (!this.shouldProcess(sub, currentBlockTime)) {\n continue;\n }\n\n // Check if we've already committed this interval\n const commitmentKey = `${subId}:${currentInterval}`;\n if (this.committedIntervals.has(commitmentKey)) {\n continue;\n }\n\n // Check if there's already a commitment on-chain\n const hasCommitment = await this.hasRequestCommitments(sub.subscriptionId, currentInterval);\n\n if (hasCommitment) {\n this.addCommittedInterval(commitmentKey);\n console.log(` Subscription ${subId} interval ${currentInterval} already committed`);\n continue;\n }\n\n // Generate commitment (prepare next interval)\n await this.prepareNextInterval(sub, currentInterval);\n } catch (error) {\n const errorMessage = (error as Error).message;\n console.error(` Error processing subscription ${subId}:`, error);\n\n // Check if error is in exception chain (like Java's containsErrorInChain)\n const containsError = (ex: Error, text: string): boolean => {\n let current: Error | undefined = ex;\n while (current) {\n if (current.message?.includes(text)) return true;\n current = (current as any).cause;\n }\n return false;\n };\n\n // If overflow/underflow error, interval likely already executed\n if (containsError(error as Error, 'Panic due to OVERFLOW') ||\n containsError(error as Error, 'arithmetic underflow or overflow')) {\n console.log(` Interval ${currentInterval} for subscription ${subId} appears to be already executed (overflow), marking as committed`);\n const commitmentKey = `${subId}:${currentInterval}`;\n this.addCommittedInterval(commitmentKey);\n sub.currentInterval = currentInterval + 1n;\n }\n // NoNextInterval error (0x3cdc51d3) - client hasn't triggered interval 1 yet\n // For scheduled subscriptions, interval 1 is triggered by the client, not the scheduler\n // Keep the subscription tracked and wait for the client to trigger interval 1\n else if (containsError(error as Error, '0x3cdc51d3') ||\n containsError(error as Error, 'NoNextInterval')) {\n console.log(` Subscription ${subId}: waiting for client to trigger interval 1 (NoNextInterval)`);\n // Don't untrack - just wait for the client to trigger interval 1\n // Once interval 1 is executed, we can prepare interval 2\n }\n // If execution reverted or simulation failed, likely subscription was cancelled\n else if (containsError(error as Error, 'execution reverted') ||\n containsError(error as Error, 'Transaction simulation failed')) {\n console.log(` Subscription ${subId} appears to be cancelled or invalid, untracking...`);\n this.untrackSubscription(sub.subscriptionId);\n }\n }\n }\n }\n\n /**\n * Check if subscription should be processed\n */\n private shouldProcess(sub: SubscriptionState, currentBlockTime: number): boolean {\n const subId = sub.subscriptionId.toString();\n\n // Not active yet\n if (BigInt(currentBlockTime) < sub.activeAt) {\n console.log(` Skip: not active yet (currentTime=${currentBlockTime}, activeAt=${sub.activeAt})`);\n return false;\n }\n\n // Calculate current interval\n const intervalsSinceActive = BigInt(currentBlockTime) - sub.activeAt;\n const currentInterval = (intervalsSinceActive / sub.intervalSeconds) + 1n;\n\n // Note: We don't skip interval 1 unconditionally anymore.\n // If triggerFirstExecution was called, hasRequestCommitments will catch it.\n // If agent crashed before triggerFirstExecution completed, we need to prepare it.\n\n // Untrack if subscription is completed (past last interval)\n if (sub.maxExecutions > 0n && currentInterval > sub.maxExecutions) {\n console.log(` Subscription ${subId} completed (interval ${currentInterval} > maxExecutions ${sub.maxExecutions}), untracking...`);\n this.untrackSubscription(sub.subscriptionId);\n return false;\n }\n\n // Has pending transaction\n const runKey = `${sub.subscriptionId}:${currentInterval}`;\n if (this.pendingTxs.has(runKey)) {\n console.log(` Skip: pending transaction for interval ${currentInterval}`);\n return false;\n }\n\n // Exceeded max retry attempts\n if (sub.txAttempts >= this.config.maxRetryAttempts) {\n console.log(` Skip: max retry attempts reached (${sub.txAttempts}/${this.config.maxRetryAttempts})`);\n return false;\n }\n\n return true;\n }\n\n /**\n * Check if interval already has commitments on-chain\n */\n private async hasRequestCommitments(subscriptionId: bigint, interval: bigint): Promise<boolean> {\n try {\n const redundancy = await this.coordinator.redundancyCount(\n this.getRequestId(subscriptionId, interval)\n );\n return redundancy > 0;\n } catch (error) {\n console.error('Error checking commitments:', error);\n return false;\n }\n }\n\n /**\n * Prepare next interval by calling coordinator contract\n * Equivalent to Java's CoordinatorService.prepareNextInterval()\n */\n private async prepareNextInterval(sub: SubscriptionState, interval: bigint): Promise<void> {\n const runKey = `${sub.subscriptionId}:${interval}`;\n\n try {\n console.log(` Preparing interval ${interval} for subscription ${sub.subscriptionId}...`);\n\n // Re-verify interval is still current before sending transaction\n // This prevents NotReadyForNextInterval errors due to timing race conditions\n const currentIntervalNow = BigInt(await this.router.getComputeSubscriptionInterval(sub.subscriptionId));\n\n if (currentIntervalNow !== interval && currentIntervalNow !== interval - 1n) {\n console.log(` ⚠️ Interval changed: expected ${interval}, blockchain is at ${currentIntervalNow}. Skipping.`);\n return;\n }\n\n // Send actual transaction to coordinator contract\n const tx = await this.coordinator.prepareNextInterval(\n sub.subscriptionId,\n interval,\n this.agentWallet\n );\n\n // Track pending transaction\n this.pendingTxs.set(runKey, tx.hash);\n sub.pendingTx = tx.hash;\n\n console.log(` 📤 Transaction sent: ${tx.hash}`);\n\n // Wait for transaction confirmation\n const receipt = await tx.wait();\n\n if (receipt.status === 1) {\n console.log(\n ` ✓ Interval ${interval} prepared successfully (block ${receipt.blockNumber})`\n );\n\n // Mark as committed\n const commitmentKey = `${sub.subscriptionId}:${interval}`;\n this.addCommittedInterval(commitmentKey);\n\n // Update subscription state\n sub.currentInterval = interval;\n sub.lastProcessedAt = Date.now();\n sub.txAttempts = 0;\n sub.pendingTx = undefined;\n this.pendingTxs.delete(runKey);\n\n // Parse RequestStarted event from receipt logs\n // This ensures compute is triggered even if WebSocket misses the event\n const requestStartedEvent = this.parseRequestStartedFromReceipt(receipt, sub);\n\n // Calculate gas cost\n const gasUsed = receipt.gasUsed;\n const gasPrice = receipt.gasPrice ?? tx.gasPrice ?? 0n;\n const gasCost = gasUsed * gasPrice;\n\n this.emit('commitment:success', {\n subscriptionId: sub.subscriptionId,\n interval,\n txHash: tx.hash,\n blockNumber: receipt.blockNumber,\n gasUsed: gasUsed.toString(),\n gasPrice: gasPrice.toString(),\n gasCost: gasCost.toString(),\n requestStartedEvent, // Include parsed event for immediate processing\n });\n } else {\n throw new Error(`Transaction failed with status ${receipt.status}`);\n }\n } catch (error) {\n console.error(` Failed to prepare interval for ${runKey}:`, error);\n\n // Clean up pending state\n sub.pendingTx = undefined;\n this.pendingTxs.delete(runKey);\n\n // Check if this is a NoNextInterval error (client hasn't triggered interval 1 yet)\n const errorMessage = (error as Error).message || '';\n const isNoNextIntervalError = errorMessage.includes('0x3cdc51d3') ||\n errorMessage.includes('NoNextInterval');\n\n if (isNoNextIntervalError) {\n // Don't increment retry attempts for NoNextInterval\n // This is expected for scheduled subscriptions where interval 1 is client-triggered\n console.log(` Subscription ${sub.subscriptionId}: NoNextInterval - waiting for client to trigger interval 1`);\n sub.txAttempts = 0; // Reset retry counter\n return;\n }\n\n // Increment retry attempts for other errors\n sub.txAttempts++;\n\n if (sub.txAttempts >= this.config.maxRetryAttempts) {\n console.log(` Max retry attempts reached for ${runKey}`);\n this.emit('commitment:failed', {\n subscriptionId: sub.subscriptionId,\n interval,\n error,\n });\n }\n }\n }\n\n /**\n * Prune transactions that have failed\n */\n private pruneFailedTxs(): void {\n // Remove old pending transactions (older than 5 minutes)\n const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;\n\n for (const [subId, sub] of this.subscriptions.entries()) {\n if (sub.lastProcessedAt < fiveMinutesAgo && sub.pendingTx) {\n console.log(` Pruning stale transaction for subscription ${subId}`);\n sub.pendingTx = undefined;\n sub.txAttempts = 0;\n }\n }\n }\n\n /**\n * Sync subscriptions (placeholder for blockchain event listening)\n */\n /**\n * Sync subscriptions from blockchain\n * Reads subscriptions in batches and tracks active ones\n */\n private async syncSubscriptions(): Promise<void> {\n if (!this.batchReader) {\n // BatchReader not configured, skip sync\n this.emit('sync:tick');\n return;\n }\n\n try {\n // Query max subscription ID from router\n if (this.maxSubscriptionId === undefined) {\n this.maxSubscriptionId = await this.router.getLastSubscriptionId();\n console.log(`📊 Total subscriptions in registry: ${this.maxSubscriptionId}`);\n }\n\n // Stop syncing if we've reached the end, but re-check for new subscriptions\n if (this.lastSyncedId >= this.maxSubscriptionId!) {\n // Re-check maxSubscriptionId to detect new subscriptions\n const latestMaxId = await this.router.getLastSubscriptionId();\n if (latestMaxId > this.maxSubscriptionId!) {\n console.log(`📊 Found new subscriptions: ${latestMaxId} (was ${this.maxSubscriptionId})`);\n this.maxSubscriptionId = latestMaxId;\n // Continue syncing with updated maxSubscriptionId\n } else {\n this.emit('sync:tick');\n return;\n }\n }\n\n const maxSubId = this.maxSubscriptionId!; // Use latest maxSubscriptionId\n\n const currentBlock = await this.provider.getBlockNumber();\n const block = await this.provider.getBlock(currentBlock);\n const blockTime = block?.timestamp || Math.floor(Date.now() / 1000);\n\n // Read subscriptions in batches\n const BATCH_SIZE = 100n;\n const startId = this.lastSyncedId + 1n;\n const endId = startId + BATCH_SIZE - 1n > maxSubId\n ? maxSubId\n : startId + BATCH_SIZE - 1n;\n\n const subscriptions = await this.batchReader.getSubscriptions(startId, endId, currentBlock);\n\n if (subscriptions.length === 0) {\n // No new subscriptions\n this.emit('sync:tick');\n return;\n }\n\n // Track active subscriptions (only containers this agent can run)\n let newSubscriptions = 0;\n let skippedContainers = 0;\n let skippedInactive = 0;\n let skippedEmpty = 0;\n let skippedOnDemand = 0;\n\n console.log(` Syncing ${subscriptions.length} subscriptions (blockTime: ${blockTime})`);\n\n for (let i = 0; i < subscriptions.length; i++) {\n const sub = subscriptions[i];\n const subscriptionId = startId + BigInt(i);\n\n // Skip cancelled/deleted subscriptions (containerId = 0x0000...0)\n if (sub.containerId === '0x0000000000000000000000000000000000000000000000000000000000000000') {\n skippedEmpty++;\n continue;\n }\n\n // Skip if not active\n if (!this.isSubscriptionActive(sub, blockTime)) {\n // Debug log for non-empty subscriptions that are inactive\n if (sub.intervalSeconds > 0) {\n console.log(` Sub ${subscriptionId}: inactive (activeAt=${sub.activeAt}, now=${blockTime})`);\n }\n skippedInactive++;\n continue;\n }\n\n // Skip if agent cannot run this container (silently filter)\n if (this.getContainer && !this.getContainer(sub.containerId)) {\n skippedContainers++;\n continue;\n }\n\n // Track subscription (returns true if actually tracked)\n if (this.trackSubscriptionFromConfig(sub, subscriptionId)) {\n newSubscriptions++;\n } else {\n // Track on-demand subscriptions that were skipped\n if (sub.intervalSeconds <= 0) {\n skippedOnDemand++;\n }\n }\n }\n\n console.log(` Sync stats: ${newSubscriptions} tracked, ${skippedEmpty} empty, ${skippedInactive} inactive, ${skippedContainers} unsupported containers, ${skippedOnDemand} on-demand`);\n\n // Update last synced ID\n this.lastSyncedId = endId;\n\n // Log results only if there are new subscriptions\n if (newSubscriptions > 0) {\n console.log(`✓ Synced ${newSubscriptions} active subscriptions (ID ${startId} - ${endId})`);\n }\n\n // Log completion if we've reached the end\n if (this.lastSyncedId >= maxSubId) {\n console.log(`✓ Sync completed - processed all ${maxSubId} subscriptions`);\n }\n\n this.emit('sync:completed', {\n subscriptions: this.subscriptions.size,\n newSubscriptions,\n });\n } catch (error) {\n console.error('Error syncing subscriptions:', error);\n this.emit('sync:error', error);\n }\n }\n\n /**\n * Check if subscription is currently active\n */\n private isSubscriptionActive(sub: ComputeSubscription, currentBlockTime: number): boolean {\n // Not started yet\n if (currentBlockTime < sub.activeAt) {\n return false;\n }\n\n // Check if max executions reached\n // Note: currentInterval is 0-based here ((elapsed / intervalSeconds) gives 0 for first interval)\n // But maxExecutions is count, so we need > not >= (if currentInterval is 4 and max is 5, still active)\n if (sub.maxExecutions > 0 && sub.intervalSeconds > 0) {\n const elapsed = currentBlockTime - sub.activeAt;\n const currentInterval = Math.floor(elapsed / sub.intervalSeconds);\n // If currentInterval >= maxExecutions, all intervals have passed\n // e.g., maxExecutions=5, intervalSeconds=180, after 900s currentInterval=5 -> all done\n if (currentInterval >= sub.maxExecutions) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Track subscription from ComputeSubscription config\n * Returns true if subscription was tracked, false if skipped\n */\n private trackSubscriptionFromConfig(sub: ComputeSubscription, subscriptionId: bigint): boolean {\n // Skip empty/deleted subscriptions (all fields are 0)\n if (sub.containerId === '0x0000000000000000000000000000000000000000000000000000000000000000') {\n // This is an empty slot (never created or deleted)\n return false;\n }\n\n // Skip if client address is zero (another indicator of empty subscription)\n if (sub.client === '0x0000000000000000000000000000000000000000') {\n return false;\n }\n\n const key = subscriptionId.toString();\n\n if (this.subscriptions.has(key)) {\n // Already tracked\n return false;\n }\n\n // Validate subscription data - intervalSeconds=0 means on-demand (not scheduled)\n if (sub.intervalSeconds <= 0) {\n // Silently skip on-demand subscriptions (not an error)\n return false;\n }\n\n // Log subscription details\n try {\n const { ethers } = require('ethers');\n const containerIdStr = ethers.decodeBytes32String(sub.containerId);\n console.log(` ✓ Tracking subscription: ${containerIdStr}`);\n console.log(` Client: ${sub.client}`);\n console.log(` Interval: ${sub.intervalSeconds}s`);\n console.log(` Max Executions: ${sub.maxExecutions}`);\n } catch (e) {\n console.log(` ✓ Tracking subscription: ${sub.containerId}`);\n }\n\n // Calculate current interval\n // Note: Contract uses 1-based indexing: ((timestamp - activeAt) / intervalSeconds) + 1\n const now = Math.floor(Date.now() / 1000);\n const elapsed = now - sub.activeAt;\n const currentInterval =\n sub.intervalSeconds > 0 ? Math.max(1, Math.floor(elapsed / sub.intervalSeconds) + 1) : 1;\n\n this.subscriptions.set(key, {\n subscriptionId: subscriptionId,\n routeId: sub.routeId,\n containerId: sub.containerId,\n client: sub.client,\n wallet: sub.wallet,\n activeAt: BigInt(sub.activeAt),\n intervalSeconds: BigInt(sub.intervalSeconds),\n maxExecutions: Number.isFinite(sub.maxExecutions) ? BigInt(sub.maxExecutions) : 0n,\n redundancy: sub.redundancy,\n verifier: sub.verifier || undefined,\n currentInterval: BigInt(currentInterval),\n lastProcessedAt: Date.now(),\n txAttempts: 0,\n });\n\n return true;\n }\n\n /**\n * Get request ID (hash of subscription ID and interval)\n */\n private getRequestId(subscriptionId: bigint, interval: bigint): string {\n // This should match the on-chain calculation\n const ethers = require('ethers');\n const abiCoder = ethers.AbiCoder.defaultAbiCoder();\n return ethers.keccak256(abiCoder.encode(['uint256', 'uint256'], [subscriptionId, interval]));\n }\n\n /**\n * Get scheduler statistics\n */\n getStats(): {\n totalSubscriptions: number;\n activeSubscriptions: number;\n committedIntervals: number;\n pendingTransactions: number;\n } {\n const now = Math.floor(Date.now() / 1000);\n const activeCount = Array.from(this.subscriptions.values()).filter((sub) => {\n // Must have started\n if (BigInt(now) < sub.activeAt) {\n return false;\n }\n\n // Check if completed (all maxExecutions done)\n if (sub.maxExecutions > 0n) {\n const elapsed = BigInt(now) - sub.activeAt;\n const currentInterval = elapsed / sub.intervalSeconds;\n\n // Subscription is completed if we've passed all intervals\n if (currentInterval >= sub.maxExecutions) {\n return false;\n }\n }\n\n return true;\n }).length;\n\n return {\n totalSubscriptions: this.subscriptions.size,\n activeSubscriptions: activeCount,\n committedIntervals: this.committedIntervals.size,\n pendingTransactions: this.pendingTxs.size,\n };\n }\n\n /**\n * Get all tracked subscriptions\n */\n getSubscriptions(): SubscriptionState[] {\n return Array.from(this.subscriptions.values());\n }\n\n /**\n * Parse RequestStarted event from transaction receipt\n * This allows the agent to process the event immediately without waiting for WebSocket\n */\n private parseRequestStartedFromReceipt(\n receipt: TransactionReceipt,\n sub: SubscriptionState\n ): RequestStartedEvent | null {\n try {\n // Find the RequestStarted log in the receipt\n for (const log of receipt.logs) {\n try {\n const parsed = this.coordinator.interface.parseLog({\n topics: log.topics as string[],\n data: log.data,\n });\n\n if (parsed && parsed.name === 'RequestStarted') {\n const commitment = parsed.args.commitment;\n return {\n requestId: parsed.args.requestId,\n subscriptionId: parsed.args.subscriptionId,\n containerId: parsed.args.containerId,\n interval: Number(commitment.interval),\n redundancy: Number(commitment.redundancy),\n useDeliveryInbox: commitment.useDeliveryInbox,\n feeAmount: commitment.feeAmount,\n feeToken: commitment.feeToken,\n verifier: commitment.verifier,\n coordinator: commitment.coordinator,\n walletAddress: commitment.walletAddress,\n blockNumber: receipt.blockNumber,\n };\n }\n } catch {\n // Not a RequestStarted log, continue\n }\n }\n } catch (error) {\n console.warn(' ⚠️ Could not parse RequestStarted event from receipt:', error);\n }\n return null;\n }\n}\n","import { ethers } from 'ethers';\nimport type { Commitment } from '../types';\n\nexport class CommitmentUtils {\n /**\n * Calculate commitment hash\n * Matches the keccak256(abi.encode(commitment)) in Solidity\n */\n static hash(commitment: Commitment): string {\n const encoded = ethers.AbiCoder.defaultAbiCoder().encode(\n [\n 'bytes32', // requestId\n 'uint64', // subscriptionId\n 'bytes32', // containerId\n 'uint32', // interval\n 'bool', // useDeliveryInbox\n 'uint16', // redundancy\n 'address', // walletAddress\n 'uint256', // feeAmount\n 'address', // feeToken\n 'address', // verifier\n 'address', // coordinator\n ],\n [\n commitment.requestId,\n commitment.subscriptionId,\n commitment.containerId,\n commitment.interval,\n commitment.useDeliveryInbox,\n commitment.redundancy,\n commitment.walletAddress,\n commitment.feeAmount,\n commitment.feeToken,\n commitment.verifier,\n commitment.coordinator,\n ]\n );\n\n return ethers.keccak256(encoded);\n }\n\n /**\n * Verify commitment hash matches expected value\n */\n static verify(commitment: Commitment, expectedHash: string): boolean {\n const actualHash = this.hash(commitment);\n return actualHash === expectedHash;\n }\n\n /**\n * Encode commitment data for reportComputeResult\n * Returns ABI-encoded commitment struct\n */\n static encode(commitment: Commitment): string {\n return ethers.AbiCoder.defaultAbiCoder().encode(\n [\n 'bytes32', // requestId\n 'uint64', // subscriptionId\n 'bytes32', // containerId\n 'uint32', // interval\n 'bool', // useDeliveryInbox\n 'uint16', // redundancy\n 'address', // walletAddress\n 'uint256', // feeAmount\n 'address', // feeToken\n 'address', // verifier\n 'address', // coordinator\n ],\n [\n commitment.requestId,\n commitment.subscriptionId,\n commitment.containerId,\n commitment.interval,\n commitment.useDeliveryInbox,\n commitment.redundancy,\n commitment.walletAddress,\n commitment.feeAmount,\n commitment.feeToken,\n commitment.verifier,\n commitment.coordinator,\n ]\n );\n }\n\n /**\n * Create Commitment from RequestStartedEvent\n */\n static fromEvent(event: any, walletAddress: string): Commitment {\n return {\n requestId: event.requestId,\n subscriptionId: event.subscriptionId,\n containerId: event.containerId,\n interval: event.interval,\n redundancy: event.redundancy,\n useDeliveryInbox: event.useDeliveryInbox || false,\n feeToken: event.feeToken,\n feeAmount: event.feeAmount,\n walletAddress: walletAddress, // Client wallet from subscription\n verifier: event.verifier || ethers.ZeroAddress,\n coordinator: event.coordinator,\n };\n }\n}\n","import { readFileSync } from 'fs';\nimport { ethers } from 'ethers';\nimport type { NoosphereAgentConfig, ContainerMetadata } from '../types';\n\nexport class ConfigLoader {\n /**\n * Load agent configuration from JSON file\n */\n static loadFromFile(configPath: string): NoosphereAgentConfig {\n try {\n const configData = readFileSync(configPath, 'utf-8');\n const config = JSON.parse(configData) as NoosphereAgentConfig;\n\n // Validate required fields\n if (!config.chain || !config.chain.enabled) {\n throw new Error('Chain configuration is required and must be enabled');\n }\n\n if (!config.chain.rpcUrl) {\n throw new Error('Chain RPC URL is required');\n }\n\n if (!config.chain.routerAddress) {\n throw new Error('Router address is required');\n }\n\n if (!config.containers || config.containers.length === 0) {\n console.warn('⚠️ No containers configured in config file');\n }\n\n return config;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}`);\n }\n throw error;\n }\n }\n\n /**\n * Convert ContainerConfig to ContainerMetadata format\n */\n static containerConfigToMetadata(\n containerConfig: NoosphereAgentConfig['containers'][0]\n ): ContainerMetadata {\n // Parse image name and tag\n const [imageName, tag] = containerConfig.image.includes(':')\n ? containerConfig.image.split(':')\n : [containerConfig.image, 'latest'];\n\n // Extract container name from image (last part after /)\n const name = imageName.split('/').pop() || containerConfig.id;\n\n // Convert acceptedPayments to payments format\n let payments: ContainerMetadata['payments'] | undefined;\n if (containerConfig.acceptedPayments) {\n const basePrice = Object.values(containerConfig.acceptedPayments)[0]?.toString() || '0';\n payments = {\n basePrice,\n unit: 'wei',\n per: 'execution',\n };\n }\n\n return {\n id: containerConfig.id,\n name,\n image: imageName,\n tag,\n port: containerConfig.port,\n env: containerConfig.env,\n verified: !!containerConfig.verifierAddress,\n payments,\n };\n }\n\n /**\n * Get all containers from config as ContainerMetadata array\n */\n static getContainersFromConfig(config: NoosphereAgentConfig): Map<string, ContainerMetadata> {\n const containersMap = new Map<string, ContainerMetadata>();\n\n for (const containerConfig of config.containers) {\n const metadata = this.containerConfigToMetadata(containerConfig);\n // Hash the container ID the same way as the smart contract does:\n // keccak256(abi.encode(containerId))\n const containerIdHash = ethers.keccak256(\n ethers.AbiCoder.defaultAbiCoder().encode(['string'], [containerConfig.id])\n );\n containersMap.set(containerIdHash, metadata);\n }\n\n return containersMap;\n }\n\n /**\n * Get container config by ID\n */\n static getContainerConfig(\n config: NoosphereAgentConfig,\n containerId: string\n ): NoosphereAgentConfig['containers'][0] | undefined {\n return config.containers.find((c) => c.id === containerId);\n }\n}\n","export interface Commitment {\n requestId: string;\n subscriptionId: bigint;\n containerId: string;\n interval: number;\n redundancy: number;\n useDeliveryInbox: boolean;\n feeToken: string;\n feeAmount: bigint;\n walletAddress: string;\n verifier: string;\n coordinator: string;\n}\n\nexport interface Payment {\n recipient: string;\n feeToken: string;\n feeAmount: bigint;\n}\n\nexport interface ProofVerificationRequest {\n subscriptionId: bigint;\n interval: number;\n submitterAddress: string;\n escrowedAmount: bigint;\n}\n\nexport interface RequestStartedEvent {\n requestId: string;\n subscriptionId: bigint;\n containerId: string;\n interval: number;\n redundancy: number;\n useDeliveryInbox: boolean;\n feeAmount: bigint;\n feeToken: string;\n verifier: string;\n coordinator: string;\n walletAddress: string;\n blockNumber: number;\n}\n\nexport interface ComputeSubscription {\n owner: string;\n wallet: string;\n containerId: string;\n intervalSeconds: number;\n maxExecutions: number;\n redundancy: number;\n feeToken: string;\n feeAmount: bigint;\n verifier: string;\n routeId: string;\n activeAt: number;\n useDeliveryInbox: boolean;\n}\n\nexport interface AgentConfig {\n rpcUrl: string;\n wsRpcUrl?: string;\n privateKey?: string; // Optional - use keystore initialization instead\n routerAddress: string;\n coordinatorAddress: string;\n deploymentBlock?: number;\n pollingInterval?: number;\n}\n\n// Extended config for full agent configuration (matches Java agent config.json)\nexport interface NoosphereAgentConfig {\n forwardStats?: boolean;\n manageContainers?: boolean;\n startupWait?: number;\n agent?: {\n name: string;\n apiKey?: string;\n email?: string;\n };\n server?: {\n port: number;\n rateLimit?: {\n numRequests: number;\n period: number;\n };\n };\n hub?: {\n register: boolean;\n url: string;\n keepAlive?: {\n enabled: boolean;\n intervalMs: number;\n batchSize?: number;\n };\n };\n chain: {\n enabled: boolean;\n rpcUrl: string;\n wsRpcUrl?: string;\n trailHeadBlocks?: number;\n routerAddress: string;\n coordinatorAddress?: string;\n deploymentBlock?: number;\n processingInterval?: number;\n wallet: {\n maxGasLimit?: number;\n paymentAddress?: string;\n allowedSimErrors?: string[];\n keystore?: {\n path: string;\n password: string;\n keys?: {\n eth?: string;\n };\n };\n };\n snapshotSync?: {\n sleep?: number;\n batchSize?: number;\n startingSubId?: number;\n syncPeriod?: number;\n };\n connection?: {\n timeout?: number;\n readTimeout?: number;\n writeTimeout?: number;\n };\n gasConfig?: {\n priceMultiplier?: number;\n limitMultiplier?: number;\n };\n };\n docker?: {\n username?: string;\n password?: string;\n };\n containers: ContainerConfig[];\n}\n\nexport interface ContainerConfig {\n id: string;\n image: string;\n port?: string;\n env?: Record<string, string>;\n volumes?: string[];\n verifierAddress?: string;\n acceptedPayments?: Record<string, number>;\n}\n\nexport interface ContainerMetadata {\n id: string;\n name: string;\n image: string;\n tag?: string;\n port?: string;\n env?: Record<string, string>;\n requirements?: {\n memory?: string;\n cpu?: number;\n gpu?: boolean;\n };\n payments?: {\n basePrice: string;\n unit: string;\n per: string;\n };\n verified?: boolean;\n}\n\nexport interface VerifierMetadata {\n address: string;\n name: string;\n image: string;\n tag?: string;\n}\n\nexport enum FulfillResult {\n FULFILLED = 0,\n INVALID_REQUEST_ID = 1,\n INVALID_COMMITMENT = 2,\n SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION = 3,\n INSUFFICIENT_SUBSCRIPTION_BALANCE = 4,\n COST_EXCEEDS_COMMITMENT = 5,\n}\n\n/**\n * Event emitted when scheduler successfully prepares next interval\n */\nexport interface CommitmentSuccessEvent {\n subscriptionId: bigint;\n interval: bigint;\n txHash: string;\n blockNumber: number;\n gasUsed: string;\n gasPrice: string;\n gasCost: string;\n requestStartedEvent?: RequestStartedEvent;\n}\n","import { ethers } from 'ethers';\n\nexport class RequestIdUtils {\n /**\n * Pack subscriptionId and interval into requestId\n * Matches Solidity: keccak256(abi.encodePacked(subscriptionId, interval))\n */\n static pack(subscriptionId: bigint, interval: number): string {\n // encodePacked for uint64 and uint32\n const subscriptionIdBytes = ethers.zeroPadValue(ethers.toBeHex(subscriptionId), 8);\n const intervalBytes = ethers.zeroPadValue(ethers.toBeHex(interval), 4);\n\n const packed = ethers.concat([subscriptionIdBytes, intervalBytes]);\n return ethers.keccak256(packed);\n }\n\n /**\n * Unpack requestId into subscriptionId and interval (if stored separately)\n * Note: This is not possible from the hash alone - only for informational purposes\n * In practice, subscriptionId and interval are stored in events\n */\n static format(requestId: string, subscriptionId: bigint, interval: number): string {\n return `Request(id=${requestId.slice(0, 10)}..., sub=${subscriptionId}, interval=${interval})`;\n }\n}\n","export { EventMonitor } from './EventMonitor';\nexport type { CheckpointData } from './EventMonitor';\nexport { ContainerManager } from './ContainerManager';\nexport { NoosphereAgent } from './NoosphereAgent';\nexport type { ComputeDeliveredEvent, RequestStartedCallbackEvent, CommitmentSuccessCallbackEvent, RetryableEvent } from './NoosphereAgent';\nexport { SchedulerService } from './SchedulerService';\n\nexport * from './types';\nexport * from './utils';\n\n// Re-export crypto utilities for convenience\nexport { WalletManager, KeystoreManager } from '@noosphere/crypto';\nexport type { NoosphereKeystore, PaymentWalletInfo, KeystoreInfo } from '@noosphere/crypto';\n\n// Re-export registry utilities for convenience\nexport { RegistryManager } from '@noosphere/registry';\nexport type {\n ContainerMetadata as RegistryContainerMetadata,\n VerifierMetadata as RegistryVerifierMetadata,\n RegistryConfig,\n} from '@noosphere/registry';\n\n// Export config types\nexport type { NoosphereAgentConfig, ContainerConfig } from './types';\n"],"mappings":"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","names":["executionTime","err","ethers","EventEmitter","ethers","ethers","ethers","ethers","FulfillResult","ethers","WalletManager","KeystoreManager","RegistryManager"]}
|
|
1
|
+
{"version":3,"sources":["../src/EventMonitor.ts","../src/ContainerManager.ts","../src/NoosphereAgent.ts","../src/SchedulerService.ts","../src/utils/CommitmentUtils.ts","../src/utils/ConfigLoader.ts","../src/types/index.ts","../src/utils/RequestIdUtils.ts","../src/index.ts"],"sourcesContent":["import { EventEmitter } from 'events';\nimport { ethers } from 'ethers';\nimport type { AgentConfig, RequestStartedEvent } from './types';\n\nexport interface CheckpointData {\n blockNumber: number;\n blockHash?: string;\n blockTimestamp?: number;\n}\n\nexport interface EventMonitorOptions {\n loadCheckpoint?: () => CheckpointData | undefined;\n saveCheckpoint?: (checkpoint: CheckpointData) => void;\n}\n\nexport class EventMonitor extends EventEmitter {\n private provider!: ethers.WebSocketProvider | ethers.JsonRpcProvider;\n private router!: ethers.Contract;\n private coordinator!: ethers.Contract;\n private lastProcessedBlock: number;\n private useWebSocket: boolean;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 10;\n private isReconnecting = false;\n private heartbeatInterval: NodeJS.Timeout | null = null;\n private lastEventTime = Date.now();\n private checkpointCallbacks?: EventMonitorOptions;\n\n constructor(\n private config: AgentConfig,\n private routerAbi: any[],\n private coordinatorAbi: any[],\n options?: EventMonitorOptions\n ) {\n super();\n this.lastProcessedBlock = config.deploymentBlock || 0;\n this.useWebSocket = false;\n this.checkpointCallbacks = options;\n }\n\n async connect(): Promise<void> {\n try {\n // Try WebSocket first for push-based events\n if (this.config.wsRpcUrl) {\n this.provider = new ethers.WebSocketProvider(this.config.wsRpcUrl);\n // Note: Do NOT call _start() - it causes race condition with auto-initialization\n // The provider will initialize automatically on first request\n this.useWebSocket = true;\n console.log('✓ Connected via WebSocket (push-based events)');\n } else {\n throw new Error('WebSocket URL not provided');\n }\n } catch (error) {\n // Fallback to HTTP with polling\n console.warn('WebSocket unavailable, falling back to HTTP polling');\n this.provider = new ethers.JsonRpcProvider(this.config.rpcUrl);\n this.useWebSocket = false;\n }\n\n // Initialize contract instances\n this.router = new ethers.Contract(this.config.routerAddress, this.routerAbi, this.provider);\n\n this.coordinator = new ethers.Contract(\n this.config.coordinatorAddress,\n this.coordinatorAbi,\n this.provider\n );\n }\n\n async start(): Promise<void> {\n // Load checkpoint from callback or use deployment block\n if (this.checkpointCallbacks?.loadCheckpoint) {\n const checkpoint = this.checkpointCallbacks.loadCheckpoint();\n if (checkpoint) {\n this.lastProcessedBlock = checkpoint.blockNumber;\n }\n }\n\n console.log(`Starting from block ${this.lastProcessedBlock}`);\n\n // Replay missed events\n await this.replayEvents(this.lastProcessedBlock, 'latest');\n\n // Start real-time listening\n if (this.useWebSocket) {\n await this.startWebSocketListening();\n } else {\n await this.startPolling();\n }\n }\n\n private async replayEvents(fromBlock: number, toBlock: string | number): Promise<void> {\n console.log(`Replaying events from block ${fromBlock} to ${toBlock}`);\n\n const currentBlock = await this.provider.getBlockNumber();\n const toBlockNumber = toBlock === 'latest' ? currentBlock : Number(toBlock);\n\n // Query historical events in chunks to avoid RPC limits\n const chunkSize = 10000;\n for (let start = fromBlock; start <= toBlockNumber; start += chunkSize) {\n const end = Math.min(start + chunkSize - 1, toBlockNumber);\n\n const events = await this.coordinator.queryFilter(\n this.coordinator.filters.RequestStarted(),\n start,\n end\n );\n\n for (const event of events) {\n await this.processEvent(event);\n }\n\n if (events.length > 0) {\n this.saveCheckpoint(end);\n }\n }\n\n console.log(`Replayed events up to block ${toBlockNumber}`);\n }\n\n private async startWebSocketListening(): Promise<void> {\n console.log('Starting WebSocket event listening...');\n\n // Listen for RequestStarted events\n this.coordinator.on('RequestStarted', async (...args) => {\n const event = args[args.length - 1]; // Last argument is the event object\n this.lastEventTime = Date.now();\n await this.processEvent(event);\n\n const blockNumber = event.blockNumber;\n if (blockNumber - this.lastProcessedBlock >= 10) {\n this.saveCheckpoint(blockNumber);\n }\n });\n\n // Setup WebSocket error/close handlers for reconnection\n if (this.provider instanceof ethers.WebSocketProvider) {\n const wsProvider = this.provider as ethers.WebSocketProvider;\n\n // Access the underlying WebSocket to detect connection issues\n // ethers v6 exposes websocket through _websocket property (internal)\n const ws = (wsProvider as any)._websocket;\n if (ws) {\n ws.on('close', () => {\n console.warn('⚠️ WebSocket connection closed');\n this.handleDisconnect();\n });\n ws.on('error', (error: Error) => {\n console.error('⚠️ WebSocket error:', error.message);\n this.handleDisconnect();\n });\n }\n }\n\n // Start heartbeat to detect silent disconnections\n this.startHeartbeat();\n\n console.log('✓ WebSocket event listener started');\n }\n\n private startHeartbeat(): void {\n // Check connection health every 2 minutes (reduced from 30s to avoid rate limits)\n this.heartbeatInterval = setInterval(async () => {\n try {\n if (this.provider instanceof ethers.WebSocketProvider) {\n // Try to get block number as heartbeat\n const blockNumber = await this.provider.getBlockNumber();\n\n // If we haven't received events for 3+ minutes but blocks are advancing,\n // we might have a stale subscription - replay missed events\n const timeSinceLastEvent = Date.now() - this.lastEventTime;\n if (timeSinceLastEvent > 180000 && blockNumber > this.lastProcessedBlock + 5) {\n console.log(`⚠️ No events for ${Math.round(timeSinceLastEvent / 1000)}s, checking for missed events...`);\n await this.replayMissedEvents();\n }\n }\n } catch (error) {\n console.error('⚠️ Heartbeat failed, WebSocket may be disconnected');\n this.handleDisconnect();\n }\n }, 120000); // 2 minutes\n }\n\n private async replayMissedEvents(): Promise<void> {\n try {\n const currentBlock = await this.provider.getBlockNumber();\n if (currentBlock > this.lastProcessedBlock) {\n console.log(`📥 Replaying events from block ${this.lastProcessedBlock + 1} to ${currentBlock}`);\n await this.replayEvents(this.lastProcessedBlock + 1, currentBlock);\n this.lastEventTime = Date.now();\n }\n } catch (error) {\n console.error('Failed to replay missed events:', error);\n }\n }\n\n private handleDisconnect(): void {\n if (this.isReconnecting) return;\n this.isReconnecting = true;\n\n // Clear heartbeat during reconnection\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n\n // Attempt reconnection\n this.reconnect().finally(() => {\n this.isReconnecting = false;\n });\n }\n\n private async startPolling(): Promise<void> {\n console.log('Starting HTTP polling (fallback mode)...');\n\n const pollingInterval = this.config.pollingInterval || 12000; // 12 seconds default\n let lastBlock = await this.provider.getBlockNumber();\n\n setInterval(async () => {\n try {\n const currentBlock = await this.provider.getBlockNumber();\n if (currentBlock > lastBlock) {\n const events = await this.coordinator.queryFilter(\n this.coordinator.filters.RequestStarted(),\n lastBlock + 1,\n currentBlock\n );\n\n for (const event of events) {\n await this.processEvent(event);\n }\n\n if (events.length > 0) {\n this.saveCheckpoint(currentBlock);\n }\n\n lastBlock = currentBlock;\n }\n } catch (error) {\n console.error('Polling error:', error);\n }\n }, pollingInterval);\n }\n\n private async processEvent(event: any): Promise<void> {\n // Parse event data\n // The RequestStarted event has: (requestId, subscriptionId, containerId, commitment)\n // Most fields are in the commitment struct\n const commitment = event.args.commitment;\n\n const requestStartedEvent: RequestStartedEvent = {\n requestId: event.args.requestId,\n subscriptionId: event.args.subscriptionId,\n containerId: event.args.containerId,\n interval: commitment.interval,\n redundancy: commitment.redundancy,\n useDeliveryInbox: commitment.useDeliveryInbox,\n feeAmount: commitment.feeAmount,\n feeToken: commitment.feeToken,\n verifier: commitment.verifier,\n coordinator: commitment.coordinator,\n walletAddress: commitment.walletAddress,\n blockNumber: event.blockNumber,\n };\n\n // Emit event for NoosphereAgent to handle\n this.emit('RequestStarted', requestStartedEvent);\n }\n\n private async reconnect(): Promise<void> {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error('Max reconnection attempts reached. Falling back to HTTP polling.');\n this.useWebSocket = false;\n await this.connect();\n await this.startPolling();\n return;\n }\n\n const backoff = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 60000);\n console.log(\n `🔄 Reconnecting in ${backoff}ms (attempt ${this.reconnectAttempts + 1}/${this.maxReconnectAttempts})`\n );\n\n await new Promise((resolve) => setTimeout(resolve, backoff));\n\n try {\n // Clean up old listeners before reconnecting\n if (this.coordinator) {\n this.coordinator.removeAllListeners();\n }\n if (this.provider instanceof ethers.WebSocketProvider) {\n try {\n await this.provider.destroy();\n } catch {\n // Ignore destroy errors\n }\n }\n\n await this.connect();\n\n // Replay any missed events since last checkpoint\n await this.replayMissedEvents();\n\n await this.startWebSocketListening();\n console.log('✓ Reconnected successfully');\n this.reconnectAttempts = 0;\n this.lastEventTime = Date.now();\n } catch (error) {\n console.error('Reconnection failed:', error);\n this.reconnectAttempts++;\n await this.reconnect();\n }\n }\n\n private saveCheckpoint(blockNumber: number): void {\n this.lastProcessedBlock = blockNumber;\n\n // Use callback if provided\n if (this.checkpointCallbacks?.saveCheckpoint) {\n this.checkpointCallbacks.saveCheckpoint({\n blockNumber,\n blockTimestamp: Date.now(),\n });\n }\n }\n\n async stop(): Promise<void> {\n // Clear heartbeat interval\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n\n if (this.router) {\n this.router.removeAllListeners();\n }\n if (this.coordinator) {\n this.coordinator.removeAllListeners();\n }\n if (this.provider instanceof ethers.WebSocketProvider) {\n await this.provider.destroy();\n }\n }\n}\n","import Docker from 'dockerode';\nimport fs from 'fs/promises';\nimport path from 'path';\nimport axios from 'axios';\nimport type { ContainerMetadata } from './types';\n\nexport interface ContainerExecutionResult {\n output: string;\n exitCode: number;\n executionTime: number;\n}\n\nexport class ContainerManager {\n private docker: Docker;\n private runningContainers: Set<Docker.Container> = new Set();\n private persistentContainers: Map<string, Docker.Container> = new Map();\n private containerPorts: Map<string, number> = new Map();\n\n constructor() {\n this.docker = new Docker();\n }\n\n async runContainer(\n container: ContainerMetadata,\n input: string,\n timeout: number = 300000, // 5 minutes default\n connectionRetries: number = 5, // Retry up to 5 times for connection issues\n connectionRetryDelayMs: number = 3000 // Wait 3 seconds between retries\n ): Promise<ContainerExecutionResult> {\n const startTime = Date.now();\n\n // Get the port for this container\n const port = container.port ? parseInt(container.port) : 8081; // Default to 8081\n\n // Make HTTP POST request to the persistent container\n // Use container name as host when DOCKER_NETWORK is set (DinD), otherwise localhost\n const containerHost = process.env.DOCKER_NETWORK\n ? `noosphere-${container.name}`\n : 'localhost';\n const url = `http://${containerHost}:${port}/computation`;\n\n // Prepare request body\n let requestBody: any;\n try {\n const parsedInput = JSON.parse(input);\n requestBody = { input: input, ...parsedInput };\n } catch {\n requestBody = { input: input };\n }\n\n let lastError: any;\n\n for (let attempt = 1; attempt <= connectionRetries; attempt++) {\n try {\n const response = await axios.post(url, requestBody, {\n timeout,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n const executionTime = Date.now() - startTime;\n\n // Extract output from response\n let output: string;\n if (typeof response.data === 'string') {\n output = response.data;\n } else if (response.data.output !== undefined) {\n output =\n typeof response.data.output === 'string'\n ? response.data.output\n : JSON.stringify(response.data.output);\n } else {\n output = JSON.stringify(response.data);\n }\n\n return {\n output,\n exitCode: 0,\n executionTime,\n };\n } catch (error: any) {\n lastError = error;\n\n // Only retry on connection refused (container not ready yet)\n if (error.code === 'ECONNREFUSED') {\n if (attempt < connectionRetries) {\n console.log(` ⏳ Container not ready (attempt ${attempt}/${connectionRetries}), retrying in ${connectionRetryDelayMs / 1000}s...`);\n await this.sleep(connectionRetryDelayMs);\n continue;\n }\n }\n\n // Don't retry on other errors, break immediately\n break;\n }\n }\n\n // All retries exhausted or non-retryable error\n const executionTime = Date.now() - startTime;\n\n if (lastError.response) {\n throw new Error(\n `Container HTTP error ${lastError.response.status}: ${JSON.stringify(lastError.response.data)}`\n );\n } else if (lastError.code === 'ECONNREFUSED') {\n throw new Error(\n `Cannot connect to container (port ${container.port || 8081}) after ${connectionRetries} attempts. Is it running?`\n );\n } else if (lastError.code === 'ETIMEDOUT' || lastError.code === 'ECONNABORTED') {\n throw new Error(`Container execution timeout after ${timeout}ms`);\n }\n\n throw lastError;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private async collectContainerResult(\n dockerContainer: Docker.Container,\n workDir: string,\n startTime: number\n ): Promise<ContainerExecutionResult> {\n try {\n // Get exit status\n const inspectData = await dockerContainer.inspect();\n const exitCode = inspectData.State.ExitCode || 0;\n\n // Read output\n const outputPath = path.join(workDir, 'output.json');\n let output = '';\n try {\n output = await fs.readFile(outputPath, 'utf-8');\n } catch (error) {\n // If no output file, try to get container logs\n const logs = await dockerContainer.logs({\n stdout: true,\n stderr: true,\n });\n output = logs.toString();\n }\n\n const executionTime = Date.now() - startTime;\n\n // Remove the container now that we've collected results\n try {\n await dockerContainer.remove({ force: true });\n } catch (error) {\n // Container might already be removed, ignore\n }\n\n // Cleanup work directory\n await fs.rm(workDir, { recursive: true, force: true });\n\n return {\n output,\n exitCode,\n executionTime,\n };\n } catch (error) {\n // Cleanup container and work directory on error\n try {\n await dockerContainer.remove({ force: true });\n } catch {\n // Ignore removal errors\n }\n await fs.rm(workDir, { recursive: true, force: true }).catch(() => {});\n throw error;\n }\n }\n\n private async pullImage(image: string, tag: string): Promise<void> {\n const imageTag = `${image}:${tag}`;\n\n try {\n // Check if image exists\n await this.docker.getImage(imageTag).inspect();\n console.log(`Image ${imageTag} already exists`);\n } catch {\n // Image doesn't exist, pull it\n console.log(`Pulling image ${imageTag}...`);\n\n return new Promise((resolve, reject) => {\n this.docker.pull(imageTag, (err: any, stream: NodeJS.ReadableStream) => {\n if (err) return reject(err);\n\n this.docker.modem.followProgress(\n stream,\n (err: any) => {\n if (err) return reject(err);\n console.log(`✓ Pulled image ${imageTag}`);\n resolve();\n },\n (event: any) => {\n // Progress update\n if (event.status) {\n console.log(`${event.status} ${event.progress || ''}`);\n }\n }\n );\n });\n });\n }\n }\n\n private async waitForContainer(container: Docker.Container): Promise<any> {\n return new Promise((resolve, reject) => {\n container.wait((err, data) => {\n if (err) return reject(err);\n resolve(data);\n });\n });\n }\n\n private timeout(ms: number): Promise<null> {\n return new Promise((resolve) => setTimeout(() => resolve(null), ms));\n }\n\n private parseMemory(memory: string): number {\n const units: { [key: string]: number } = {\n b: 1,\n kb: 1024,\n mb: 1024 * 1024,\n gb: 1024 * 1024 * 1024,\n };\n\n const match = memory.toLowerCase().match(/^(\\d+)\\s*(b|kb|mb|gb)$/);\n if (!match) {\n throw new Error(`Invalid memory format: ${memory}`);\n }\n\n const [, value, unit] = match;\n return parseInt(value, 10) * units[unit];\n }\n\n async checkDockerAvailable(): Promise<boolean> {\n try {\n await this.docker.ping();\n return true;\n } catch {\n return false;\n }\n }\n\n async getDockerInfo(): Promise<any> {\n return this.docker.info();\n }\n\n /**\n * Cleanup all running containers\n * Called when agent is shutting down\n */\n async cleanup(): Promise<void> {\n if (this.runningContainers.size === 0) {\n return;\n }\n\n console.log(`🧹 Cleaning up ${this.runningContainers.size} running containers...`);\n\n const cleanupPromises = Array.from(this.runningContainers).map(async (container) => {\n try {\n const inspect = await container.inspect();\n if (inspect.State.Running) {\n console.log(` Stopping container ${inspect.Id.slice(0, 12)}...`);\n await container.stop({ t: 10 }); // 10 second grace period\n await container.remove({ force: true });\n }\n } catch (error) {\n // Container might already be stopped/removed\n console.warn(` Warning: Failed to cleanup container:`, (error as Error).message);\n }\n });\n\n await Promise.all(cleanupPromises);\n this.runningContainers.clear();\n console.log('✓ Container cleanup completed');\n }\n\n /**\n * Get number of running containers\n */\n getRunningContainerCount(): number {\n return this.runningContainers.size;\n }\n\n /**\n * Pre-pull container images and start persistent containers on startup\n * This speeds up request handling by having containers ready\n */\n async prepareContainers(containers: Map<string, ContainerMetadata>): Promise<void> {\n if (containers.size === 0) {\n console.log('No containers to prepare');\n return;\n }\n\n console.log(`\\n🚀 Preparing ${containers.size} containers...`);\n\n const pullAndStartPromises = Array.from(containers.entries()).map(async ([id, container]) => {\n const imageTag = `${container.image}:${container.tag || 'latest'}`;\n\n try {\n console.log(` Pulling ${imageTag}...`);\n await this.pullImage(container.image, container.tag || 'latest');\n console.log(` ✓ ${imageTag} ready`);\n } catch (error) {\n console.error(` ❌ Failed to pull ${imageTag}:`, (error as Error).message);\n // Skip container start if pull failed\n return;\n }\n\n try {\n // Start persistent container\n await this.startPersistentContainer(id, container);\n } catch (error) {\n console.error(` ❌ Failed to start ${container.name || container.image}:`, (error as Error).message);\n }\n });\n\n await Promise.all(pullAndStartPromises);\n console.log('✓ All containers prepared\\n');\n }\n\n /**\n * Start a persistent container that stays running\n */\n private async startPersistentContainer(\n containerId: string,\n metadata: ContainerMetadata\n ): Promise<void> {\n // Use metadata.name for Docker container name (human-readable)\n // This matches the hostname used in runContainer()\n const containerName = `noosphere-${metadata.name}`;\n const imageTag = `${metadata.image}:${metadata.tag || 'latest'}`;\n\n // Check if container already exists\n const existingContainer = this.docker.getContainer(containerName);\n try {\n const inspect = await existingContainer.inspect();\n if (inspect.State.Running) {\n console.log(` ✓ Container ${containerName} already running`);\n this.persistentContainers.set(containerId, existingContainer);\n return;\n } else {\n // Container exists but stopped - try to start it\n try {\n await existingContainer.start();\n console.log(` ✓ Started existing container ${containerName}`);\n this.persistentContainers.set(containerId, existingContainer);\n return;\n } catch (startErr) {\n // Failed to start, remove and recreate\n console.log(` Removing stopped container ${containerName} to recreate...`);\n await existingContainer.remove({ force: true });\n }\n }\n } catch (err) {\n // Container doesn't exist, will create new one\n }\n\n // Create new persistent container\n const dockerNetwork = process.env.DOCKER_NETWORK;\n const createOptions: Docker.ContainerCreateOptions = {\n name: containerName,\n Image: imageTag,\n Tty: false,\n AttachStdout: false,\n AttachStderr: false,\n ExposedPorts: metadata.port ? { [`${metadata.port}/tcp`]: {} } : undefined,\n HostConfig: {\n AutoRemove: false, // Keep container for reuse\n // Only bind ports to host when not using Docker network (local dev)\n PortBindings:\n metadata.port && !dockerNetwork\n ? {\n [`${metadata.port}/tcp`]: [{ HostPort: metadata.port }],\n }\n : undefined,\n // Join the specified Docker network for DinD communication\n NetworkMode: dockerNetwork || undefined,\n },\n Env: metadata.env ? Object.entries(metadata.env).map(([k, v]) => `${k}=${v}`) : undefined,\n };\n\n // Add resource limits\n if (metadata.requirements) {\n const resources: any = {};\n\n if (metadata.requirements.memory) {\n resources.Memory = this.parseMemory(metadata.requirements.memory);\n }\n\n if (metadata.requirements.cpu) {\n resources.NanoCpus = metadata.requirements.cpu * 1e9;\n }\n\n if (metadata.requirements.gpu) {\n createOptions.HostConfig!.DeviceRequests = [\n {\n Driver: 'nvidia',\n Count: -1,\n Capabilities: [['gpu']],\n },\n ];\n }\n\n if (Object.keys(resources).length > 0) {\n createOptions.HostConfig = {\n ...createOptions.HostConfig,\n ...resources,\n };\n }\n }\n\n const dockerContainer = await this.docker.createContainer(createOptions);\n await dockerContainer.start();\n\n this.persistentContainers.set(containerId, dockerContainer);\n if (metadata.port) {\n this.containerPorts.set(containerId, parseInt(metadata.port));\n }\n\n console.log(` ✓ Started persistent container ${containerName}`);\n }\n\n /**\n * Stop and remove all persistent containers\n */\n async stopPersistentContainers(): Promise<void> {\n if (this.persistentContainers.size === 0) {\n return;\n }\n\n console.log(`\\n🛑 Stopping ${this.persistentContainers.size} persistent containers...`);\n\n const stopPromises = Array.from(this.persistentContainers.entries()).map(\n async ([id, container]) => {\n try {\n const inspect = await container.inspect();\n if (inspect.State.Running) {\n console.log(` Stopping ${inspect.Name}...`);\n await container.stop({ t: 10 });\n }\n await container.remove({ force: true });\n console.log(` ✓ Stopped ${inspect.Name}`);\n } catch (error) {\n console.warn(` Warning: Failed to stop container ${id}:`, (error as Error).message);\n }\n }\n );\n\n await Promise.all(stopPromises);\n this.persistentContainers.clear();\n this.containerPorts.clear();\n console.log('✓ All persistent containers stopped\\n');\n }\n}\n","import { ethers } from 'ethers';\nimport { EventMonitor } from './EventMonitor';\nimport { ContainerManager } from './ContainerManager';\nimport { SchedulerService, SchedulerConfig } from './SchedulerService';\nimport { WalletManager, KeystoreManager } from '@noosphere/crypto';\nimport { RegistryManager } from '@noosphere/registry';\nimport { ABIs } from '@noosphere/contracts';\nimport { CommitmentUtils } from './utils/CommitmentUtils';\nimport { ConfigLoader } from './utils/ConfigLoader';\nimport type {\n AgentConfig,\n RequestStartedEvent,\n ContainerMetadata,\n Commitment,\n NoosphereAgentConfig,\n} from './types';\n\nexport interface ComputeDeliveredEvent {\n requestId: string;\n subscriptionId: number;\n interval: number;\n containerId: string;\n redundancy: number;\n feeAmount: string;\n feeToken: string;\n input: string;\n output: string;\n txHash: string;\n blockNumber: number;\n gasUsed: bigint;\n gasPrice: bigint;\n}\n\nexport interface RequestStartedCallbackEvent {\n requestId: string;\n subscriptionId: number;\n interval: number;\n containerId: string;\n redundancy: number;\n feeAmount: string;\n feeToken: string;\n verifier: string;\n walletAddress: string;\n blockNumber: number;\n}\n\n/**\n * Event data for commitment success (scheduler prepareNextInterval)\n */\nexport interface CommitmentSuccessCallbackEvent {\n subscriptionId: bigint;\n interval: bigint;\n txHash: string;\n blockNumber: number;\n gasUsed: string;\n gasPrice: string;\n gasCost: string;\n}\n\nexport interface CheckpointData {\n blockNumber: number;\n blockHash?: string;\n blockTimestamp?: number;\n}\n\nexport interface RetryableEvent {\n requestId: string;\n subscriptionId: number;\n interval: number;\n containerId: string;\n retryCount: number;\n}\n\nexport interface ContainerExecutionConfig {\n timeout?: number; // Container execution timeout in ms (default: 300000 = 5 min)\n connectionRetries?: number; // Number of connection retry attempts (default: 5)\n connectionRetryDelayMs?: number; // Delay between retries in ms (default: 3000)\n}\n\nexport interface NoosphereAgentOptions {\n config: AgentConfig;\n routerAbi?: any[]; // Optional - defaults to ABIs.Router from @noosphere/contracts\n coordinatorAbi?: any[]; // Optional - defaults to ABIs.Coordinator from @noosphere/contracts\n getContainer?: (containerId: string) => ContainerMetadata | undefined;\n containers?: Map<string, ContainerMetadata>; // Container map from config\n walletManager?: WalletManager; // Optional - provide pre-initialized WalletManager\n paymentWallet?: string; // Optional - WalletFactory wallet address for the agent\n schedulerConfig?: Partial<SchedulerConfig>; // Optional - scheduler configuration from config.json\n containerConfig?: ContainerExecutionConfig; // Optional - container execution configuration\n onRequestStarted?: (event: RequestStartedCallbackEvent) => void; // Callback when request is received\n onRequestProcessing?: (requestId: string) => void; // Callback when request processing starts\n onRequestSkipped?: (requestId: string, reason: string) => void; // Callback when request is skipped\n onRequestFailed?: (requestId: string, error: string, txHash?: string) => void; // Callback when request fails (txHash included if tx was sent)\n onComputeDelivered?: (event: ComputeDeliveredEvent) => void; // Callback when compute is delivered\n onCommitmentSuccess?: (event: CommitmentSuccessCallbackEvent) => void; // Callback when scheduler prepares interval\n isRequestProcessed?: (requestId: string) => boolean; // Check if request is already processed (completed/failed)\n loadCheckpoint?: () => CheckpointData | undefined; // Load checkpoint from storage\n saveCheckpoint?: (checkpoint: CheckpointData) => void; // Save checkpoint to storage\n // Retry configuration\n maxRetries?: number; // Maximum retry attempts for failed requests (default: 3)\n retryIntervalMs?: number; // Interval to check for retryable events (default: 30000ms)\n getRetryableEvents?: (maxRetries: number) => RetryableEvent[]; // Get events that can be retried\n resetEventForRetry?: (requestId: string) => void; // Reset event status to pending for retry\n // Health check configuration\n healthCheckIntervalMs?: number; // Interval to check registry health (default: 300000ms = 5 min)\n}\n\nexport class NoosphereAgent {\n private eventMonitor: EventMonitor;\n private containerManager: ContainerManager;\n private scheduler: SchedulerService;\n private walletManager: WalletManager;\n private registryManager: RegistryManager;\n private router: ethers.Contract;\n private coordinator: ethers.Contract;\n private provider: ethers.JsonRpcProvider;\n private config: AgentConfig;\n private getContainer?: (containerId: string) => ContainerMetadata | undefined;\n private containers?: Map<string, ContainerMetadata>;\n private paymentWallet?: string;\n private isRunning = false;\n private processingRequests = new Set<string>(); // Deduplication: track requests being processed\n private retryTimer?: NodeJS.Timeout; // Timer for retry mechanism\n private healthCheckTimer?: NodeJS.Timeout; // Timer for registry health check\n private maxRetries: number;\n private retryIntervalMs: number;\n private healthCheckIntervalMs: number;\n // Container execution config\n private containerTimeout: number;\n private containerConnectionRetries: number;\n private containerConnectionRetryDelayMs: number;\n\n constructor(private options: NoosphereAgentOptions) {\n this.config = options.config;\n this.provider = new ethers.JsonRpcProvider(options.config.rpcUrl);\n const provider = this.provider;\n\n // Use default ABIs from @noosphere/contracts if not provided\n const routerAbi = options.routerAbi || ABIs.Router;\n const coordinatorAbi = options.coordinatorAbi || ABIs.Coordinator;\n\n // Use provided WalletManager or create from private key\n if (options.walletManager) {\n this.walletManager = options.walletManager;\n } else if (options.config.privateKey) {\n this.walletManager = new WalletManager(options.config.privateKey, provider);\n } else {\n throw new Error(\n 'Either walletManager or config.privateKey must be provided. ' +\n 'Recommended: Use NoosphereAgent.fromKeystore() for production.'\n );\n }\n\n this.containerManager = new ContainerManager();\n this.registryManager = new RegistryManager({\n autoSync: true, // Enable automatic sync with remote registry\n cacheTTL: 3600000, // 1 hour cache\n });\n this.eventMonitor = new EventMonitor(options.config, routerAbi, coordinatorAbi, {\n loadCheckpoint: options.loadCheckpoint,\n saveCheckpoint: options.saveCheckpoint,\n });\n\n // Initialize router contract\n this.router = new ethers.Contract(\n options.config.routerAddress,\n routerAbi,\n this.provider\n );\n\n this.coordinator = new ethers.Contract(\n options.config.coordinatorAddress,\n coordinatorAbi,\n this.walletManager.getWallet()\n );\n\n // Store container sources\n this.getContainer = options.getContainer;\n this.containers = options.containers;\n\n // Initialize scheduler service (with container filter)\n this.scheduler = new SchedulerService(\n provider,\n this.router,\n this.coordinator,\n this.walletManager.getAddress(),\n undefined, // batchReaderAddress (set later)\n undefined, // config (set later)\n this.getContainer // Pass container filter\n );\n\n // Store payment wallet (WalletFactory wallet for the agent)\n this.paymentWallet = options.paymentWallet;\n\n // Validate we have at least one container source\n if (!this.getContainer && (!this.containers || this.containers.size === 0)) {\n console.warn('⚠️ No container source provided. Agent will not be able to execute requests.');\n }\n\n // Initialize retry configuration\n this.maxRetries = options.maxRetries ?? 3;\n this.retryIntervalMs = options.retryIntervalMs ?? 30000; // 30 seconds\n\n // Initialize container execution configuration\n this.containerTimeout = options.containerConfig?.timeout ?? 300000; // 5 minutes default\n this.containerConnectionRetries = options.containerConfig?.connectionRetries ?? 5;\n this.containerConnectionRetryDelayMs = options.containerConfig?.connectionRetryDelayMs ?? 3000; // 3 seconds default\n\n // Initialize health check configuration\n this.healthCheckIntervalMs = options.healthCheckIntervalMs ?? 300000; // 5 minutes default\n }\n\n /**\n * Initialize NoosphereAgent from config.json (RECOMMENDED)\n * This loads all configuration including containers from a config file\n *\n * @param configPath - Path to config.json file\n * @param routerAbi - Router contract ABI (optional - defaults to ABIs.Router)\n * @param coordinatorAbi - Coordinator contract ABI (optional - defaults to ABIs.Coordinator)\n * @returns Initialized NoosphereAgent\n */\n static async fromConfig(\n configPath: string,\n routerAbi?: any[],\n coordinatorAbi?: any[]\n ): Promise<NoosphereAgent> {\n // Load config from file\n const fullConfig = ConfigLoader.loadFromFile(configPath);\n\n // Extract keystore path and password\n const keystorePath = fullConfig.chain.wallet.keystore?.path;\n const password = fullConfig.chain.wallet.keystore?.password;\n\n if (!keystorePath || !password) {\n throw new Error('Keystore path and password are required in config.chain.wallet.keystore');\n }\n\n // Load containers from config\n const containers = ConfigLoader.getContainersFromConfig(fullConfig);\n\n console.log(`📦 Loaded ${containers.size} containers from config:`);\n for (const [id, container] of containers.entries()) {\n console.log(` - ${id}: ${container.image}:${container.tag || 'latest'}`);\n }\n\n // Create provider\n const provider = new ethers.JsonRpcProvider(fullConfig.chain.rpcUrl);\n\n // Load keystore\n const keystoreManager = new KeystoreManager(keystorePath, password);\n await keystoreManager.load();\n\n // Initialize WalletManager from keystore\n const walletManager = await WalletManager.fromKeystoreManager(keystoreManager, provider);\n\n // Create AgentConfig from NoosphereAgentConfig\n const agentConfig: AgentConfig = {\n rpcUrl: fullConfig.chain.rpcUrl,\n wsRpcUrl: fullConfig.chain.wsRpcUrl,\n routerAddress: fullConfig.chain.routerAddress,\n coordinatorAddress: fullConfig.chain.coordinatorAddress || fullConfig.chain.routerAddress,\n deploymentBlock: fullConfig.chain.deploymentBlock,\n pollingInterval: fullConfig.chain.processingInterval,\n };\n\n // Extract payment wallet (WalletFactory wallet for the agent)\n const paymentWallet = fullConfig.chain.wallet.paymentAddress;\n\n // Create agent with pre-initialized WalletManager and containers\n return new NoosphereAgent({\n config: agentConfig,\n routerAbi,\n coordinatorAbi,\n walletManager,\n containers,\n paymentWallet,\n });\n }\n\n /**\n * Initialize NoosphereAgent from keystore (RECOMMENDED)\n * This is the secure way to initialize an agent in production\n *\n * @param keystorePath - Path to keystore file\n * @param password - Keystore password\n * @param options - Agent configuration options\n * @returns Initialized NoosphereAgent\n */\n static async fromKeystore(\n keystorePath: string,\n password: string,\n options: Omit<NoosphereAgentOptions, 'walletManager'>\n ): Promise<NoosphereAgent> {\n const provider = new ethers.JsonRpcProvider(options.config.rpcUrl);\n\n // Load keystore\n const keystoreManager = new KeystoreManager(keystorePath, password);\n await keystoreManager.load();\n\n // Initialize WalletManager from keystore\n const walletManager = await WalletManager.fromKeystoreManager(keystoreManager, provider);\n\n // Create agent with pre-initialized WalletManager\n return new NoosphereAgent({\n ...options,\n walletManager,\n });\n }\n\n async start(): Promise<void> {\n console.log('Starting Noosphere Agent...');\n\n // Load registry (local + remote sync)\n console.log('📋 Loading container registry...');\n await this.registryManager.load();\n const stats = this.registryManager.getStats();\n console.log(\n `✓ Registry loaded: ${stats.totalContainers} containers, ${stats.totalVerifiers} verifiers`\n );\n\n // Check Docker availability\n const dockerAvailable = await this.containerManager.checkDockerAvailable();\n if (!dockerAvailable) {\n throw new Error('Docker is not available. Please ensure Docker daemon is running.');\n }\n\n // Get wallet info\n const address = this.walletManager.getAddress();\n const balance = await this.walletManager.getBalance();\n console.log(`Agent wallet: ${address}`);\n console.log(`Balance: ${ethers.formatEther(balance)} ETH`);\n\n if (balance === 0n) {\n console.warn('⚠️ Warning: Wallet has zero balance. Agent needs ETH for gas fees.');\n }\n\n // Pre-pull container images if containers are provided\n if (this.containers && this.containers.size > 0) {\n console.log(`\\n🚀 Preparing ${this.containers.size} containers...`);\n await this.containerManager.prepareContainers(this.containers);\n }\n\n // Connect event monitor\n await this.eventMonitor.connect();\n\n // Set up event handler\n this.eventMonitor.on('RequestStarted', async (event: RequestStartedEvent) => {\n await this.handleRequest(event);\n });\n\n // Start listening\n await this.eventMonitor.start();\n\n // Try to get SubscriptionBatchReader address from coordinator\n try {\n const batchReaderAddress = await this.coordinator.getSubscriptionBatchReader();\n if (\n batchReaderAddress &&\n batchReaderAddress !== '0x0000000000000000000000000000000000000000'\n ) {\n console.log(`✓ SubscriptionBatchReader found: ${batchReaderAddress}`);\n\n // Reinitialize scheduler with BatchReader and config from options\n this.scheduler.stop();\n this.scheduler = new SchedulerService(\n this.provider,\n this.router,\n this.coordinator,\n this.walletManager.getAddress(),\n batchReaderAddress,\n this.options.schedulerConfig || {\n cronIntervalMs: 60000, // 1 minute (default)\n syncPeriodMs: 3000, // 3 seconds (default)\n maxRetryAttempts: 3, // 3 retries (default)\n },\n this.getContainer // Pass container filter\n );\n } else {\n console.warn('⚠️ SubscriptionBatchReader not available - subscription sync disabled');\n }\n } catch (error) {\n console.warn('⚠️ Failed to get SubscriptionBatchReader address:', (error as Error).message);\n }\n\n // Start scheduler service\n this.scheduler.start();\n\n // Listen for commitment:success events to handle cases where WebSocket misses events\n this.scheduler.on('commitment:success', async (data: {\n subscriptionId: bigint;\n interval: bigint;\n txHash: string;\n blockNumber: number;\n gasUsed?: string;\n gasPrice?: string;\n gasCost?: string;\n requestStartedEvent?: RequestStartedEvent;\n }) => {\n // Call callback if provided (for DB persistence)\n if (this.options.onCommitmentSuccess) {\n this.options.onCommitmentSuccess({\n subscriptionId: data.subscriptionId,\n interval: data.interval,\n txHash: data.txHash,\n blockNumber: data.blockNumber,\n gasUsed: data.gasUsed || '0',\n gasPrice: data.gasPrice || '0',\n gasCost: data.gasCost || '0',\n });\n }\n\n if (data.requestStartedEvent) {\n console.log(` 📥 Processing RequestStarted from prepare receipt (fallback for missed WebSocket)`);\n await this.handleRequest(data.requestStartedEvent);\n }\n });\n\n // Start retry timer if retry callbacks are provided\n if (this.options.getRetryableEvents && this.options.resetEventForRetry) {\n this.startRetryTimer();\n }\n\n // Start health check timer\n this.startHealthCheck();\n\n this.isRunning = true;\n console.log('✓ Noosphere Agent is running');\n console.log('Listening for requests...');\n }\n\n /**\n * Start the retry timer for failed requests\n */\n private startRetryTimer(): void {\n if (this.retryTimer) {\n clearInterval(this.retryTimer);\n }\n\n console.log(`🔄 Retry mechanism enabled: max ${this.maxRetries} retries, check every ${this.retryIntervalMs / 1000}s`);\n\n this.retryTimer = setInterval(async () => {\n await this.processRetries();\n }, this.retryIntervalMs);\n }\n\n /**\n * Start the health check timer for registry validation\n */\n private startHealthCheck(): void {\n if (this.healthCheckTimer) {\n clearInterval(this.healthCheckTimer);\n }\n\n console.log(`🏥 Health check enabled: check every ${this.healthCheckIntervalMs / 1000}s`);\n\n this.healthCheckTimer = setInterval(async () => {\n await this.performHealthCheck();\n }, this.healthCheckIntervalMs);\n }\n\n /**\n * Perform health check - verify registry has containers and reload if necessary\n */\n private async performHealthCheck(): Promise<void> {\n const stats = this.registryManager.getStats();\n\n if (stats.totalContainers === 0) {\n console.warn('⚠️ Health check: 0 containers detected, attempting registry reload...');\n\n try {\n await this.registryManager.reload();\n const newStats = this.registryManager.getStats();\n\n if (newStats.totalContainers > 0) {\n console.log(`✓ Health check: Registry recovered - ${newStats.totalContainers} containers loaded`);\n } else {\n console.error('❌ Health check: Registry reload failed - still 0 containers');\n }\n } catch (error) {\n console.error('❌ Health check: Registry reload error:', (error as Error).message);\n }\n }\n }\n\n /**\n * Process retryable failed events (with throttling to avoid rate limits)\n */\n private async processRetries(): Promise<void> {\n if (!this.options.getRetryableEvents || !this.options.resetEventForRetry) {\n return;\n }\n\n const retryableEvents = this.options.getRetryableEvents(this.maxRetries);\n if (retryableEvents.length === 0) {\n return;\n }\n\n // Only retry one event per cycle to avoid rate limiting\n const event = retryableEvents[0];\n\n // Skip if already being processed\n if (this.processingRequests.has(event.requestId)) {\n return;\n }\n\n console.log(`🔄 Retrying request ${event.requestId.slice(0, 10)}... (attempt ${event.retryCount + 1}/${this.maxRetries}, ${retryableEvents.length} remaining)`);\n\n // Reset event to pending\n this.options.resetEventForRetry(event.requestId);\n\n // Re-process the request\n const container = this.getContainerMetadata(event.containerId);\n if (!container) {\n console.log(` ⚠️ Container ${event.containerId.slice(0, 10)}... no longer supported, skipping retry`);\n return;\n }\n\n // Create a synthetic RequestStartedEvent for retry\n const retryEvent: RequestStartedEvent = {\n requestId: event.requestId,\n subscriptionId: BigInt(event.subscriptionId),\n interval: event.interval,\n containerId: event.containerId,\n redundancy: 1,\n useDeliveryInbox: false,\n feeAmount: BigInt(0),\n feeToken: '0x0000000000000000000000000000000000000000',\n walletAddress: '0x0000000000000000000000000000000000000000',\n verifier: '0x0000000000000000000000000000000000000000',\n coordinator: this.config.coordinatorAddress,\n blockNumber: 0,\n };\n\n // Handle the request\n try {\n await this.handleRequest(retryEvent);\n } catch (error) {\n console.log(` ❌ Retry failed for ${event.requestId.slice(0, 10)}...: ${(error as Error).message}`);\n }\n }\n\n /**\n * Convert registry ContainerMetadata to agent-core ContainerMetadata\n */\n private convertRegistryContainer(registryContainer: any): ContainerMetadata {\n // Parse image name and tag from imageName (format: \"image:tag\" or just \"image\")\n const [image, tag] = registryContainer.imageName.split(':');\n\n return {\n id: registryContainer.id,\n name: registryContainer.name,\n image: image,\n tag: tag || 'latest',\n port: registryContainer.port?.toString(),\n env: registryContainer.env,\n requirements: registryContainer.requirements,\n payments: registryContainer.payments\n ? {\n basePrice: registryContainer.payments.basePrice,\n unit: registryContainer.payments.token,\n per: registryContainer.payments.per,\n }\n : undefined,\n verified: registryContainer.verified,\n };\n }\n\n /**\n * Get container metadata from available sources\n * Returns undefined if container is not supported by this agent\n *\n * NOTE: Only checks config-defined sources (callback and containers map).\n * Registry is NOT used here - we only process containers explicitly configured.\n */\n private getContainerMetadata(containerId: string): ContainerMetadata | undefined {\n // 1. Try callback function first (allows config-based filtering)\n if (this.getContainer) {\n const container = this.getContainer(containerId);\n if (container) return container;\n }\n\n // 2. Fallback to containers map from config\n // NOTE: We do NOT use registry here - only explicitly configured containers\n if (this.containers) {\n const container = this.containers.get(containerId);\n if (container) return container;\n }\n\n return undefined;\n }\n\n private async handleRequest(event: RequestStartedEvent): Promise<void> {\n const requestIdShort = event.requestId.slice(0, 10);\n\n // Container filter: Only process events for containers we support\n // This must be checked FIRST, before any DB save or RPC calls\n const container = this.getContainerMetadata(event.containerId);\n if (!container) {\n // Silently skip - we don't support this container\n return;\n }\n\n // Deduplication: Check if this request is already being processed\n if (this.processingRequests.has(event.requestId)) {\n console.log(` ⏭️ Request ${requestIdShort}... already being processed, skipping duplicate`);\n return;\n }\n\n // Check if request has already been processed (completed/failed)\n if (this.options.isRequestProcessed && this.options.isRequestProcessed(event.requestId)) {\n console.log(` ⏭️ Request ${requestIdShort}... already processed, skipping`);\n return;\n }\n\n this.processingRequests.add(event.requestId);\n\n console.log(`\\n[${new Date().toISOString()}] RequestStarted: ${requestIdShort}...`);\n console.log(` SubscriptionId: ${event.subscriptionId}`);\n console.log(` Interval: ${event.interval}`);\n console.log(` ContainerId: ${event.containerId.slice(0, 10)}...`);\n console.log(` 📦 Container: ${container.name} (${container.image}:${container.tag || 'latest'})`);\n\n // Call onRequestStarted callback if provided (saves to DB)\n // This is called AFTER container check, so only supported containers are saved\n if (this.options.onRequestStarted) {\n this.options.onRequestStarted({\n requestId: event.requestId,\n subscriptionId: Number(event.subscriptionId),\n interval: Number(event.interval),\n containerId: event.containerId,\n redundancy: event.redundancy,\n feeAmount: event.feeAmount.toString(),\n feeToken: event.feeToken,\n verifier: event.verifier,\n walletAddress: event.walletAddress,\n blockNumber: event.blockNumber,\n });\n }\n\n // Check if this interval is still current (skip old replayed events)\n // Note: One-time executions (intervalSeconds=0) return type(uint32).max, should not be skipped\n try {\n const currentInterval = await this.router.getComputeSubscriptionInterval(event.subscriptionId);\n const eventInterval = Number(event.interval);\n\n // Skip check only for scheduled subscriptions (not one-time executions)\n // type(uint32).max = 4294967295 indicates one-time execution\n const isOneTimeExecution = currentInterval === 4294967295n;\n\n if (!isOneTimeExecution && currentInterval > eventInterval + 2) {\n console.log(` ⏭️ Skipping old interval ${eventInterval} (current: ${currentInterval})`);\n if (this.options.onRequestSkipped) {\n this.options.onRequestSkipped(event.requestId, `Old interval ${eventInterval} (current: ${currentInterval})`);\n }\n this.processingRequests.delete(event.requestId);\n return;\n }\n } catch (error) {\n console.warn(` Could not verify interval currency:`, (error as Error).message);\n // Continue processing if we can't verify\n }\n\n // Mark this interval as committed (subscription will be tracked by batch reader)\n this.scheduler.markIntervalCommitted(BigInt(event.subscriptionId), BigInt(event.interval));\n\n // Track sent transaction hash for error reporting\n let sentTxHash: string | undefined;\n\n try {\n // Self-coordination: Wait based on position-based priority\n await this.waitForPriority(event);\n\n // Call onRequestProcessing callback if provided\n if (this.options.onRequestProcessing) {\n this.options.onRequestProcessing(event.requestId);\n }\n\n // Check if already fulfilled (redundancy check)\n const currentCount = await this.coordinator.redundancyCount(event.requestId);\n if (currentCount >= event.redundancy) {\n console.log(` ⏭️ Already fulfilled (${currentCount}/${event.redundancy}), skipping`);\n if (this.options.onRequestSkipped) {\n this.options.onRequestSkipped(event.requestId, `Already fulfilled (${currentCount}/${event.redundancy})`);\n }\n this.processingRequests.delete(event.requestId);\n return;\n }\n\n // Container already verified at the start of handleRequest()\n // Using the container variable from there\n\n // Fetch subscription to get client address\n const subscription = await this.router.getComputeSubscription(event.subscriptionId);\n const clientAddress = subscription.client;\n\n if (!clientAddress || clientAddress === '0x0000000000000000000000000000000000000000') {\n console.error(` ❌ Invalid client address for subscription ${event.subscriptionId}`);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, `Invalid client address for subscription ${event.subscriptionId}`);\n }\n return;\n }\n\n console.log(` 📞 Fetching inputs from client: ${clientAddress.slice(0, 10)}...`);\n\n // Call client's getComputeInputs to get the input data\n const clientAbi = [\n 'function getComputeInputs(uint64 subscriptionId, uint32 interval, uint32 timestamp, address caller) external view returns (bytes memory)',\n ];\n const client = new ethers.Contract(clientAddress, clientAbi, this.provider);\n const timestamp = Math.floor(Date.now() / 1000);\n\n let inputBytes: string;\n try {\n inputBytes = await client.getComputeInputs(\n event.subscriptionId,\n event.interval,\n timestamp,\n this.walletManager.getAddress()\n );\n } catch (error) {\n const errorMessage = (error as Error).message || String(error);\n console.error(` ❌ Failed to get inputs from client:`, error);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, `Failed to get inputs: ${errorMessage}`);\n }\n return;\n }\n\n // Convert bytes to string\n const inputData = ethers.toUtf8String(inputBytes);\n console.log(\n ` 📥 Inputs received: ${inputData.substring(0, 100)}${inputData.length > 100 ? '...' : ''}`\n );\n\n // Execute container\n console.log(` ⚙️ Executing...`);\n const result = await this.containerManager.runContainer(\n container,\n inputData,\n this.containerTimeout,\n this.containerConnectionRetries,\n this.containerConnectionRetryDelayMs\n );\n\n if (result.exitCode !== 0) {\n console.error(` ❌ Container execution failed with exit code ${result.exitCode}`);\n console.error(` 📄 Container output:`, result.output);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, `Container execution failed with exit code ${result.exitCode}`);\n }\n return;\n }\n\n console.log(` ✓ Execution completed in ${result.executionTime}ms`);\n\n // Submit result to coordinator\n console.log(` 📤 Submitting result...`);\n\n // Use the original input data from client (already a string)\n const input = inputData;\n const output = result.output;\n const proof = event.verifier ? result.output : ''; // Use output as proof if verifier exists\n\n // Use the wallet address from the event (subscription's payment wallet)\n const subscriptionWallet = event.walletAddress;\n\n // Create commitment using SDK utility\n const commitment: Commitment = CommitmentUtils.fromEvent(event, subscriptionWallet);\n\n // Encode commitment data using SDK utility\n const commitmentData = CommitmentUtils.encode(commitment);\n\n // Use payment wallet (WalletFactory wallet) if set, otherwise fallback to EOA\n const nodeWallet = this.paymentWallet || this.walletManager.getAddress();\n\n // Send transaction\n const tx = await this.coordinator.reportComputeResult(\n event.interval,\n ethers.toUtf8Bytes(input),\n ethers.toUtf8Bytes(output),\n ethers.toUtf8Bytes(proof),\n commitmentData,\n nodeWallet\n );\n\n // Capture tx hash immediately for error reporting\n sentTxHash = tx.hash;\n console.log(` 📤 Transaction sent: ${tx.hash}`);\n\n // Wait for confirmation\n const receipt = await tx.wait();\n\n if (receipt.status === 1) {\n console.log(` ✓ Result delivered successfully (block ${receipt.blockNumber})`);\n console.log(` 💰 Fee earned: ${ethers.formatEther(event.feeAmount)} ETH`);\n\n // Call onComputeDelivered callback if provided\n if (this.options.onComputeDelivered) {\n this.options.onComputeDelivered({\n requestId: event.requestId,\n subscriptionId: Number(event.subscriptionId),\n interval: Number(event.interval),\n containerId: event.containerId,\n redundancy: event.redundancy,\n feeAmount: event.feeAmount.toString(),\n feeToken: event.feeToken,\n input: input,\n output: output,\n txHash: tx.hash,\n blockNumber: receipt.blockNumber,\n gasUsed: receipt.gasUsed,\n gasPrice: receipt.gasPrice || tx.gasPrice || 0n,\n });\n }\n } else {\n throw new Error(`Delivery transaction failed with status ${receipt.status}`);\n }\n } catch (error) {\n const errorMessage = (error as Error).message || String(error);\n const errorCode = (error as any).code;\n\n // Handle nonce expired error - this usually means another handler already processed this request\n if (errorCode === 'NONCE_EXPIRED' || errorMessage.includes('nonce has already been used') || errorMessage.includes('nonce too low')) {\n console.log(` ⚠️ Nonce expired (likely already processed by another handler)`);\n // Don't mark as failed - it was probably successful via another path\n return;\n }\n\n console.error(` ❌ Error processing request:`, error);\n if (this.options.onRequestFailed) {\n this.options.onRequestFailed(event.requestId, errorMessage, sentTxHash);\n }\n } finally {\n // Cleanup: Remove from processing set\n this.processingRequests.delete(event.requestId);\n }\n }\n\n /**\n * Self-coordination: Calculate priority and wait\n */\n private async waitForPriority(event: RequestStartedEvent): Promise<void> {\n const priority = this.calculatePriority(event.requestId);\n const maxDelay = event.redundancy === 1 ? 1000 : 200; // 1s for single redundancy, 200ms otherwise\n const delay = Math.floor((priority / 0xffffffff) * maxDelay);\n\n if (delay > 0) {\n console.log(\n ` ⏱️ Priority wait: ${delay}ms (priority: 0x${priority.toString(16).slice(0, 8)})`\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n /**\n * Calculate deterministic priority for this agent and request\n */\n private calculatePriority(requestId: string): number {\n const hash = ethers.keccak256(ethers.concat([requestId, this.walletManager.getAddress()]));\n\n // Use first 8 hex chars as priority (0x00000000 - 0xffffffff)\n return parseInt(hash.slice(2, 10), 16);\n }\n\n async stop(): Promise<void> {\n console.log('Stopping Noosphere Agent...');\n\n // Stop retry timer\n if (this.retryTimer) {\n clearInterval(this.retryTimer);\n this.retryTimer = undefined;\n }\n\n // Stop health check timer\n if (this.healthCheckTimer) {\n clearInterval(this.healthCheckTimer);\n this.healthCheckTimer = undefined;\n }\n\n // Stop event monitoring\n await this.eventMonitor.stop();\n\n // Stop scheduler\n this.scheduler.stop();\n\n // Cleanup running containers\n await this.containerManager.cleanup();\n\n // Stop persistent containers\n await this.containerManager.stopPersistentContainers();\n\n this.isRunning = false;\n console.log('✓ Agent stopped');\n }\n\n getStatus(): {\n running: boolean;\n address: string;\n scheduler: {\n totalSubscriptions: number;\n activeSubscriptions: number;\n committedIntervals: number;\n pendingTransactions: number;\n };\n containers: {\n runningCount: number;\n };\n } {\n return {\n running: this.isRunning,\n address: this.walletManager.getAddress(),\n scheduler: this.scheduler.getStats(),\n containers: {\n runningCount: this.containerManager.getRunningContainerCount(),\n },\n };\n }\n\n /**\n * Get scheduler service (for advanced usage)\n */\n getScheduler(): SchedulerService {\n return this.scheduler;\n }\n}\n","/**\n * SchedulerService\n *\n * Manages periodic commitment generation for active subscriptions.\n * Equivalent to Java's CommitmentGenerationService with cron scheduling.\n */\n\nimport { EventEmitter } from 'events';\nimport { Contract, Provider, TransactionReceipt } from 'ethers';\nimport { SubscriptionBatchReaderContract, type ComputeSubscription } from '@noosphere/contracts';\nimport type { RequestStartedEvent } from './types';\n\nexport interface SubscriptionState {\n subscriptionId: bigint;\n routeId: string;\n containerId: string;\n client: string;\n wallet: string;\n activeAt: bigint;\n intervalSeconds: bigint;\n maxExecutions: bigint;\n redundancy: number;\n verifier?: string;\n currentInterval: bigint;\n lastProcessedAt: number;\n pendingTx?: string;\n txAttempts: number;\n}\n\nexport interface SchedulerConfig {\n cronIntervalMs: number; // Default: 60000 (1 minute)\n maxRetryAttempts: number; // Default: 3\n syncPeriodMs: number; // Default: 3000 (3 seconds)\n // Persistence callbacks for committed intervals\n loadCommittedIntervals?: () => string[]; // Load from DB on startup\n saveCommittedInterval?: (key: string) => void; // Save to DB when committed\n}\n\nexport class SchedulerService extends EventEmitter {\n private subscriptions = new Map<string, SubscriptionState>();\n private committedIntervals = new Set<string>(); // subscriptionId:interval\n private pendingTxs = new Map<string, string>(); // key -> txHash\n private intervalTimer?: NodeJS.Timeout;\n private syncTimer?: NodeJS.Timeout;\n private config: SchedulerConfig;\n private batchReader?: SubscriptionBatchReaderContract;\n private lastSyncedId: bigint = 0n;\n private maxSubscriptionId?: bigint;\n private getContainer?: (containerId: string) => any;\n\n constructor(\n private provider: Provider,\n private router: Contract,\n private coordinator: Contract,\n private agentWallet: string,\n batchReaderAddress?: string,\n config?: Partial<SchedulerConfig>,\n getContainer?: (containerId: string) => any\n ) {\n super();\n this.config = {\n cronIntervalMs: config?.cronIntervalMs ?? 60000, // 1 minute\n maxRetryAttempts: config?.maxRetryAttempts ?? 3,\n syncPeriodMs: config?.syncPeriodMs ?? 3000, // 3 seconds\n loadCommittedIntervals: config?.loadCommittedIntervals,\n saveCommittedInterval: config?.saveCommittedInterval,\n };\n this.getContainer = getContainer;\n\n // Initialize SubscriptionBatchReader if address provided\n if (batchReaderAddress) {\n this.batchReader = new SubscriptionBatchReaderContract(batchReaderAddress, provider);\n console.log(`✓ SubscriptionBatchReader configured: ${batchReaderAddress}`);\n } else {\n console.warn('⚠️ SubscriptionBatchReader not configured - subscription sync disabled');\n }\n }\n\n /**\n * Start the scheduler service\n */\n start(): void {\n console.log('🕐 Starting Scheduler Service...');\n console.log(` Commitment generation interval: ${this.config.cronIntervalMs}ms`);\n console.log(` Sync period: ${this.config.syncPeriodMs}ms`);\n\n // Load committed intervals from persistent storage\n if (this.config.loadCommittedIntervals) {\n const loaded = this.config.loadCommittedIntervals();\n for (const key of loaded) {\n this.committedIntervals.add(key);\n }\n if (loaded.length > 0) {\n console.log(` Loaded ${loaded.length} committed intervals from storage`);\n }\n }\n\n // Start commitment generation timer (like Spring @Scheduled cron)\n this.intervalTimer = setInterval(() => this.generateCommitments(), this.config.cronIntervalMs);\n\n // Start periodic sync timer\n this.syncTimer = setInterval(() => this.syncSubscriptions(), this.config.syncPeriodMs);\n\n console.log('✓ Scheduler Service started');\n }\n\n /**\n * Stop the scheduler service\n */\n stop(): void {\n if (this.intervalTimer) {\n clearInterval(this.intervalTimer);\n this.intervalTimer = undefined;\n }\n if (this.syncTimer) {\n clearInterval(this.syncTimer);\n this.syncTimer = undefined;\n }\n console.log('✓ Scheduler Service stopped');\n }\n\n /**\n * Track a new subscription\n */\n trackSubscription(\n subscription: Omit<SubscriptionState, 'currentInterval' | 'lastProcessedAt' | 'txAttempts'>\n ): void {\n const key = subscription.subscriptionId.toString();\n\n if (this.subscriptions.has(key)) {\n console.log(` Subscription ${key} already tracked, updating...`);\n }\n\n // Calculate current interval based on elapsed time\n // Note: Contract uses 1-based indexing: ((timestamp - activeAt) / intervalSeconds) + 1\n const now = Math.floor(Date.now() / 1000);\n const elapsed = now - Number(subscription.activeAt);\n const currentInterval = subscription.intervalSeconds > 0n\n ? BigInt(Math.max(1, Math.floor(elapsed / Number(subscription.intervalSeconds)) + 1))\n : 1n;\n\n this.subscriptions.set(key, {\n ...subscription,\n currentInterval,\n lastProcessedAt: Date.now(),\n txAttempts: 0,\n });\n\n console.log(`✓ Tracking subscription ${key}`);\n this.emit('subscription:tracked', subscription.subscriptionId);\n }\n\n /**\n * Remove a subscription from tracking\n */\n untrackSubscription(subscriptionId: bigint): void {\n const key = subscriptionId.toString();\n if (this.subscriptions.delete(key)) {\n // Clean up committedIntervals for this subscription to prevent memory leak\n const prefix = `${key}:`;\n let cleanedCount = 0;\n for (const commitmentKey of this.committedIntervals) {\n if (commitmentKey.startsWith(prefix)) {\n this.committedIntervals.delete(commitmentKey);\n cleanedCount++;\n }\n }\n if (cleanedCount > 0) {\n console.log(` 🧹 Cleaned up ${cleanedCount} committed intervals for subscription ${key}`);\n }\n console.log(`✓ Stopped tracking subscription ${key}`);\n this.emit('subscription:untracked', subscriptionId);\n }\n }\n\n /**\n * Mark an interval as committed (for RequestStarted events)\n * Also persists to storage if callback is configured\n */\n markIntervalCommitted(subscriptionId: bigint, interval: bigint): void {\n const commitmentKey = `${subscriptionId}:${interval}`;\n this.addCommittedInterval(commitmentKey);\n console.log(` ✓ Marked interval ${interval} as committed for subscription ${subscriptionId}`);\n }\n\n /**\n * Add to committed intervals set and persist to storage\n */\n private addCommittedInterval(key: string): void {\n if (!this.committedIntervals.has(key)) {\n this.committedIntervals.add(key);\n if (this.config.saveCommittedInterval) {\n this.config.saveCommittedInterval(key);\n }\n }\n }\n\n /**\n * Main commitment generation loop (runs every cronIntervalMs)\n * Equivalent to Java's CommitmentGenerationService.generateCommitment()\n */\n private async generateCommitments(): Promise<void> {\n try {\n const timestamp = new Date().toISOString();\n console.log(`\\n🔄 [${timestamp}] Starting commitment generation task...`);\n\n // 1. Prune failed transactions\n this.pruneFailedTxs();\n\n // 2. Process active subscriptions\n await this.processActiveSubscriptions();\n\n const endTimestamp = new Date().toISOString();\n console.log(`✓ [${endTimestamp}] Finished commitment generation task.\\n`);\n } catch (error) {\n console.error('❌ Error in commitment generation:', error);\n this.emit('error', error);\n }\n }\n\n /**\n * Process all active subscriptions\n */\n private async processActiveSubscriptions(): Promise<void> {\n // Get blockchain timestamp instead of local system time\n const latestBlock = await this.provider.getBlock('latest');\n if (!latestBlock) {\n console.warn(' Could not fetch latest block, skipping this cycle');\n return;\n }\n const currentBlockTime = latestBlock.timestamp;\n\n if (this.subscriptions.size === 0) {\n console.log(' No subscriptions to process');\n return;\n }\n\n console.log(` Processing ${this.subscriptions.size} subscription(s)...`);\n\n for (const [subId, sub] of this.subscriptions.entries()) {\n let currentInterval: bigint = 0n;\n try {\n // Validate intervalSeconds to prevent division by zero\n if (sub.intervalSeconds <= 0n) {\n console.warn(` Skipping subscription ${subId}: invalid intervalSeconds (${sub.intervalSeconds})`);\n this.untrackSubscription(sub.subscriptionId);\n continue;\n }\n\n // Get current interval from blockchain (Router contract)\n // This ensures we prepare intervals in the correct sequence\n try {\n currentInterval = BigInt(await this.router.getComputeSubscriptionInterval(sub.subscriptionId));\n } catch (error) {\n const errorMessage = (error as Error).message || '';\n console.warn(` Could not get interval from router for subscription ${subId}:`, errorMessage);\n\n // If subscription not found, it was cancelled - untrack and skip\n if (errorMessage.includes('SubscriptionNotFound')) {\n console.log(` Subscription ${subId} not found (cancelled), untracking...`);\n this.untrackSubscription(sub.subscriptionId);\n continue;\n }\n\n // Fall back to local calculation only for transient errors\n const intervalsSinceActive = BigInt(currentBlockTime) - sub.activeAt;\n currentInterval = (intervalsSinceActive / sub.intervalSeconds) + 1n;\n }\n\n console.log(` Subscription ${subId}: currentInterval=${currentInterval}, maxExecutions=${sub.maxExecutions}, activeAt=${sub.activeAt}`);\n\n // Check if subscription should process\n if (!this.shouldProcess(sub, currentBlockTime)) {\n continue;\n }\n\n // Check if we've already committed this interval\n const commitmentKey = `${subId}:${currentInterval}`;\n if (this.committedIntervals.has(commitmentKey)) {\n continue;\n }\n\n // Check if there's already a commitment on-chain\n const hasCommitment = await this.hasRequestCommitments(sub.subscriptionId, currentInterval);\n\n if (hasCommitment) {\n this.addCommittedInterval(commitmentKey);\n console.log(` Subscription ${subId} interval ${currentInterval} already committed`);\n continue;\n }\n\n // Generate commitment (prepare next interval)\n await this.prepareNextInterval(sub, currentInterval);\n } catch (error) {\n const errorMessage = (error as Error).message;\n console.error(` Error processing subscription ${subId}:`, error);\n\n // Check if error is in exception chain (like Java's containsErrorInChain)\n const containsError = (ex: Error, text: string): boolean => {\n let current: Error | undefined = ex;\n while (current) {\n if (current.message?.includes(text)) return true;\n current = (current as any).cause;\n }\n return false;\n };\n\n // If overflow/underflow error, interval likely already executed\n if (containsError(error as Error, 'Panic due to OVERFLOW') ||\n containsError(error as Error, 'arithmetic underflow or overflow')) {\n console.log(` Interval ${currentInterval} for subscription ${subId} appears to be already executed (overflow), marking as committed`);\n const commitmentKey = `${subId}:${currentInterval}`;\n this.addCommittedInterval(commitmentKey);\n sub.currentInterval = currentInterval + 1n;\n }\n // NoNextInterval error (0x3cdc51d3) - client hasn't triggered interval 1 yet\n // For scheduled subscriptions, interval 1 is triggered by the client, not the scheduler\n // Keep the subscription tracked and wait for the client to trigger interval 1\n else if (containsError(error as Error, '0x3cdc51d3') ||\n containsError(error as Error, 'NoNextInterval')) {\n console.log(` Subscription ${subId}: waiting for client to trigger interval 1 (NoNextInterval)`);\n // Don't untrack - just wait for the client to trigger interval 1\n // Once interval 1 is executed, we can prepare interval 2\n }\n // If execution reverted or simulation failed, likely subscription was cancelled\n else if (containsError(error as Error, 'execution reverted') ||\n containsError(error as Error, 'Transaction simulation failed')) {\n console.log(` Subscription ${subId} appears to be cancelled or invalid, untracking...`);\n this.untrackSubscription(sub.subscriptionId);\n }\n }\n }\n }\n\n /**\n * Check if subscription should be processed\n */\n private shouldProcess(sub: SubscriptionState, currentBlockTime: number): boolean {\n const subId = sub.subscriptionId.toString();\n\n // Not active yet\n if (BigInt(currentBlockTime) < sub.activeAt) {\n console.log(` Skip: not active yet (currentTime=${currentBlockTime}, activeAt=${sub.activeAt})`);\n return false;\n }\n\n // Calculate current interval\n const intervalsSinceActive = BigInt(currentBlockTime) - sub.activeAt;\n const currentInterval = (intervalsSinceActive / sub.intervalSeconds) + 1n;\n\n // Note: We don't skip interval 1 unconditionally anymore.\n // If triggerFirstExecution was called, hasRequestCommitments will catch it.\n // If agent crashed before triggerFirstExecution completed, we need to prepare it.\n\n // Untrack if subscription is completed (past last interval)\n if (sub.maxExecutions > 0n && currentInterval > sub.maxExecutions) {\n console.log(` Subscription ${subId} completed (interval ${currentInterval} > maxExecutions ${sub.maxExecutions}), untracking...`);\n this.untrackSubscription(sub.subscriptionId);\n return false;\n }\n\n // Has pending transaction\n const runKey = `${sub.subscriptionId}:${currentInterval}`;\n if (this.pendingTxs.has(runKey)) {\n console.log(` Skip: pending transaction for interval ${currentInterval}`);\n return false;\n }\n\n // Exceeded max retry attempts\n if (sub.txAttempts >= this.config.maxRetryAttempts) {\n console.log(` Skip: max retry attempts reached (${sub.txAttempts}/${this.config.maxRetryAttempts})`);\n return false;\n }\n\n return true;\n }\n\n /**\n * Check if interval already has commitments on-chain\n */\n private async hasRequestCommitments(subscriptionId: bigint, interval: bigint): Promise<boolean> {\n try {\n const redundancy = await this.coordinator.redundancyCount(\n this.getRequestId(subscriptionId, interval)\n );\n return redundancy > 0;\n } catch (error) {\n console.error('Error checking commitments:', error);\n return false;\n }\n }\n\n /**\n * Prepare next interval by calling coordinator contract\n * Equivalent to Java's CoordinatorService.prepareNextInterval()\n */\n private async prepareNextInterval(sub: SubscriptionState, interval: bigint): Promise<void> {\n const runKey = `${sub.subscriptionId}:${interval}`;\n\n try {\n console.log(` Preparing interval ${interval} for subscription ${sub.subscriptionId}...`);\n\n // Re-verify interval is still current before sending transaction\n // This prevents NotReadyForNextInterval errors due to timing race conditions\n const currentIntervalNow = BigInt(await this.router.getComputeSubscriptionInterval(sub.subscriptionId));\n\n if (currentIntervalNow !== interval && currentIntervalNow !== interval - 1n) {\n console.log(` ⚠️ Interval changed: expected ${interval}, blockchain is at ${currentIntervalNow}. Skipping.`);\n return;\n }\n\n // Send actual transaction to coordinator contract\n const tx = await this.coordinator.prepareNextInterval(\n sub.subscriptionId,\n interval,\n this.agentWallet\n );\n\n // Track pending transaction\n this.pendingTxs.set(runKey, tx.hash);\n sub.pendingTx = tx.hash;\n\n console.log(` 📤 Transaction sent: ${tx.hash}`);\n\n // Wait for transaction confirmation\n const receipt = await tx.wait();\n\n if (receipt.status === 1) {\n console.log(\n ` ✓ Interval ${interval} prepared successfully (block ${receipt.blockNumber})`\n );\n\n // Mark as committed\n const commitmentKey = `${sub.subscriptionId}:${interval}`;\n this.addCommittedInterval(commitmentKey);\n\n // Update subscription state\n sub.currentInterval = interval;\n sub.lastProcessedAt = Date.now();\n sub.txAttempts = 0;\n sub.pendingTx = undefined;\n this.pendingTxs.delete(runKey);\n\n // Parse RequestStarted event from receipt logs\n // This ensures compute is triggered even if WebSocket misses the event\n const requestStartedEvent = this.parseRequestStartedFromReceipt(receipt, sub);\n\n // Calculate gas cost\n const gasUsed = receipt.gasUsed;\n const gasPrice = receipt.gasPrice ?? tx.gasPrice ?? 0n;\n const gasCost = gasUsed * gasPrice;\n\n this.emit('commitment:success', {\n subscriptionId: sub.subscriptionId,\n interval,\n txHash: tx.hash,\n blockNumber: receipt.blockNumber,\n gasUsed: gasUsed.toString(),\n gasPrice: gasPrice.toString(),\n gasCost: gasCost.toString(),\n requestStartedEvent, // Include parsed event for immediate processing\n });\n } else {\n throw new Error(`Transaction failed with status ${receipt.status}`);\n }\n } catch (error) {\n console.error(` Failed to prepare interval for ${runKey}:`, error);\n\n // Clean up pending state\n sub.pendingTx = undefined;\n this.pendingTxs.delete(runKey);\n\n // Check if this is a NoNextInterval error (client hasn't triggered interval 1 yet)\n const errorMessage = (error as Error).message || '';\n const isNoNextIntervalError = errorMessage.includes('0x3cdc51d3') ||\n errorMessage.includes('NoNextInterval');\n\n if (isNoNextIntervalError) {\n // Don't increment retry attempts for NoNextInterval\n // This is expected for scheduled subscriptions where interval 1 is client-triggered\n console.log(` Subscription ${sub.subscriptionId}: NoNextInterval - waiting for client to trigger interval 1`);\n sub.txAttempts = 0; // Reset retry counter\n return;\n }\n\n // Increment retry attempts for other errors\n sub.txAttempts++;\n\n if (sub.txAttempts >= this.config.maxRetryAttempts) {\n console.log(` Max retry attempts reached for ${runKey}`);\n this.emit('commitment:failed', {\n subscriptionId: sub.subscriptionId,\n interval,\n error,\n });\n }\n }\n }\n\n /**\n * Prune transactions that have failed\n */\n private pruneFailedTxs(): void {\n // Remove old pending transactions (older than 5 minutes)\n const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;\n\n for (const [subId, sub] of this.subscriptions.entries()) {\n if (sub.lastProcessedAt < fiveMinutesAgo && sub.pendingTx) {\n console.log(` Pruning stale transaction for subscription ${subId}`);\n sub.pendingTx = undefined;\n sub.txAttempts = 0;\n }\n }\n }\n\n /**\n * Sync subscriptions (placeholder for blockchain event listening)\n */\n /**\n * Sync subscriptions from blockchain\n * Reads subscriptions in batches and tracks active ones\n */\n private async syncSubscriptions(): Promise<void> {\n if (!this.batchReader) {\n // BatchReader not configured, skip sync\n this.emit('sync:tick');\n return;\n }\n\n try {\n // Query max subscription ID from router\n if (this.maxSubscriptionId === undefined) {\n this.maxSubscriptionId = await this.router.getLastSubscriptionId();\n console.log(`📊 Total subscriptions in registry: ${this.maxSubscriptionId}`);\n }\n\n // Stop syncing if we've reached the end, but re-check for new subscriptions\n if (this.lastSyncedId >= this.maxSubscriptionId!) {\n // Re-check maxSubscriptionId to detect new subscriptions\n const latestMaxId = await this.router.getLastSubscriptionId();\n if (latestMaxId > this.maxSubscriptionId!) {\n console.log(`📊 Found new subscriptions: ${latestMaxId} (was ${this.maxSubscriptionId})`);\n this.maxSubscriptionId = latestMaxId;\n // Continue syncing with updated maxSubscriptionId\n } else {\n this.emit('sync:tick');\n return;\n }\n }\n\n const maxSubId = this.maxSubscriptionId!; // Use latest maxSubscriptionId\n\n const currentBlock = await this.provider.getBlockNumber();\n const block = await this.provider.getBlock(currentBlock);\n const blockTime = block?.timestamp || Math.floor(Date.now() / 1000);\n\n // Read subscriptions in batches\n const BATCH_SIZE = 100n;\n const startId = this.lastSyncedId + 1n;\n const endId = startId + BATCH_SIZE - 1n > maxSubId\n ? maxSubId\n : startId + BATCH_SIZE - 1n;\n\n const subscriptions = await this.batchReader.getSubscriptions(startId, endId, currentBlock);\n\n if (subscriptions.length === 0) {\n // No new subscriptions\n this.emit('sync:tick');\n return;\n }\n\n // Track active subscriptions (only containers this agent can run)\n let newSubscriptions = 0;\n let skippedContainers = 0;\n let skippedInactive = 0;\n let skippedEmpty = 0;\n let skippedOnDemand = 0;\n\n console.log(` Syncing ${subscriptions.length} subscriptions (blockTime: ${blockTime})`);\n\n for (let i = 0; i < subscriptions.length; i++) {\n const sub = subscriptions[i];\n const subscriptionId = startId + BigInt(i);\n\n // Skip cancelled/deleted subscriptions (containerId = 0x0000...0)\n if (sub.containerId === '0x0000000000000000000000000000000000000000000000000000000000000000') {\n skippedEmpty++;\n continue;\n }\n\n // Skip if not active\n if (!this.isSubscriptionActive(sub, blockTime)) {\n // Debug log for non-empty subscriptions that are inactive\n if (sub.intervalSeconds > 0) {\n console.log(` Sub ${subscriptionId}: inactive (activeAt=${sub.activeAt}, now=${blockTime})`);\n }\n skippedInactive++;\n continue;\n }\n\n // Skip if agent cannot run this container (silently filter)\n if (this.getContainer && !this.getContainer(sub.containerId)) {\n skippedContainers++;\n continue;\n }\n\n // Track subscription (returns true if actually tracked)\n if (this.trackSubscriptionFromConfig(sub, subscriptionId)) {\n newSubscriptions++;\n } else {\n // Track on-demand subscriptions that were skipped\n if (sub.intervalSeconds <= 0) {\n skippedOnDemand++;\n }\n }\n }\n\n console.log(` Sync stats: ${newSubscriptions} tracked, ${skippedEmpty} empty, ${skippedInactive} inactive, ${skippedContainers} unsupported containers, ${skippedOnDemand} on-demand`);\n\n // Update last synced ID\n this.lastSyncedId = endId;\n\n // Log results only if there are new subscriptions\n if (newSubscriptions > 0) {\n console.log(`✓ Synced ${newSubscriptions} active subscriptions (ID ${startId} - ${endId})`);\n }\n\n // Log completion if we've reached the end\n if (this.lastSyncedId >= maxSubId) {\n console.log(`✓ Sync completed - processed all ${maxSubId} subscriptions`);\n }\n\n this.emit('sync:completed', {\n subscriptions: this.subscriptions.size,\n newSubscriptions,\n });\n } catch (error) {\n console.error('Error syncing subscriptions:', error);\n this.emit('sync:error', error);\n }\n }\n\n /**\n * Check if subscription is currently active\n */\n private isSubscriptionActive(sub: ComputeSubscription, currentBlockTime: number): boolean {\n // Not started yet\n if (currentBlockTime < sub.activeAt) {\n return false;\n }\n\n // Check if max executions reached\n // Note: currentInterval is 0-based here ((elapsed / intervalSeconds) gives 0 for first interval)\n // But maxExecutions is count, so we need > not >= (if currentInterval is 4 and max is 5, still active)\n if (sub.maxExecutions > 0 && sub.intervalSeconds > 0) {\n const elapsed = currentBlockTime - sub.activeAt;\n const currentInterval = Math.floor(elapsed / sub.intervalSeconds);\n // If currentInterval >= maxExecutions, all intervals have passed\n // e.g., maxExecutions=5, intervalSeconds=180, after 900s currentInterval=5 -> all done\n if (currentInterval >= sub.maxExecutions) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Track subscription from ComputeSubscription config\n * Returns true if subscription was tracked, false if skipped\n */\n private trackSubscriptionFromConfig(sub: ComputeSubscription, subscriptionId: bigint): boolean {\n // Skip empty/deleted subscriptions (all fields are 0)\n if (sub.containerId === '0x0000000000000000000000000000000000000000000000000000000000000000') {\n // This is an empty slot (never created or deleted)\n return false;\n }\n\n // Skip if client address is zero (another indicator of empty subscription)\n if (sub.client === '0x0000000000000000000000000000000000000000') {\n return false;\n }\n\n const key = subscriptionId.toString();\n\n if (this.subscriptions.has(key)) {\n // Already tracked\n return false;\n }\n\n // Validate subscription data - intervalSeconds=0 means on-demand (not scheduled)\n if (sub.intervalSeconds <= 0) {\n // Silently skip on-demand subscriptions (not an error)\n return false;\n }\n\n // Log subscription details\n try {\n const { ethers } = require('ethers');\n const containerIdStr = ethers.decodeBytes32String(sub.containerId);\n console.log(` ✓ Tracking subscription: ${containerIdStr}`);\n console.log(` Client: ${sub.client}`);\n console.log(` Interval: ${sub.intervalSeconds}s`);\n console.log(` Max Executions: ${sub.maxExecutions}`);\n } catch (e) {\n console.log(` ✓ Tracking subscription: ${sub.containerId}`);\n }\n\n // Calculate current interval\n // Note: Contract uses 1-based indexing: ((timestamp - activeAt) / intervalSeconds) + 1\n const now = Math.floor(Date.now() / 1000);\n const elapsed = now - sub.activeAt;\n const currentInterval =\n sub.intervalSeconds > 0 ? Math.max(1, Math.floor(elapsed / sub.intervalSeconds) + 1) : 1;\n\n this.subscriptions.set(key, {\n subscriptionId: subscriptionId,\n routeId: sub.routeId,\n containerId: sub.containerId,\n client: sub.client,\n wallet: sub.wallet,\n activeAt: BigInt(sub.activeAt),\n intervalSeconds: BigInt(sub.intervalSeconds),\n maxExecutions: Number.isFinite(sub.maxExecutions) ? BigInt(sub.maxExecutions) : 0n,\n redundancy: sub.redundancy,\n verifier: sub.verifier || undefined,\n currentInterval: BigInt(currentInterval),\n lastProcessedAt: Date.now(),\n txAttempts: 0,\n });\n\n return true;\n }\n\n /**\n * Get request ID (hash of subscription ID and interval)\n */\n private getRequestId(subscriptionId: bigint, interval: bigint): string {\n // This should match the on-chain calculation\n const ethers = require('ethers');\n const abiCoder = ethers.AbiCoder.defaultAbiCoder();\n return ethers.keccak256(abiCoder.encode(['uint256', 'uint256'], [subscriptionId, interval]));\n }\n\n /**\n * Get scheduler statistics\n */\n getStats(): {\n totalSubscriptions: number;\n activeSubscriptions: number;\n committedIntervals: number;\n pendingTransactions: number;\n } {\n const now = Math.floor(Date.now() / 1000);\n const activeCount = Array.from(this.subscriptions.values()).filter((sub) => {\n // Must have started\n if (BigInt(now) < sub.activeAt) {\n return false;\n }\n\n // Check if completed (all maxExecutions done)\n if (sub.maxExecutions > 0n) {\n const elapsed = BigInt(now) - sub.activeAt;\n const currentInterval = elapsed / sub.intervalSeconds;\n\n // Subscription is completed if we've passed all intervals\n if (currentInterval >= sub.maxExecutions) {\n return false;\n }\n }\n\n return true;\n }).length;\n\n return {\n totalSubscriptions: this.subscriptions.size,\n activeSubscriptions: activeCount,\n committedIntervals: this.committedIntervals.size,\n pendingTransactions: this.pendingTxs.size,\n };\n }\n\n /**\n * Get all tracked subscriptions\n */\n getSubscriptions(): SubscriptionState[] {\n return Array.from(this.subscriptions.values());\n }\n\n /**\n * Parse RequestStarted event from transaction receipt\n * This allows the agent to process the event immediately without waiting for WebSocket\n */\n private parseRequestStartedFromReceipt(\n receipt: TransactionReceipt,\n sub: SubscriptionState\n ): RequestStartedEvent | null {\n try {\n // Find the RequestStarted log in the receipt\n for (const log of receipt.logs) {\n try {\n const parsed = this.coordinator.interface.parseLog({\n topics: log.topics as string[],\n data: log.data,\n });\n\n if (parsed && parsed.name === 'RequestStarted') {\n const commitment = parsed.args.commitment;\n return {\n requestId: parsed.args.requestId,\n subscriptionId: parsed.args.subscriptionId,\n containerId: parsed.args.containerId,\n interval: Number(commitment.interval),\n redundancy: Number(commitment.redundancy),\n useDeliveryInbox: commitment.useDeliveryInbox,\n feeAmount: commitment.feeAmount,\n feeToken: commitment.feeToken,\n verifier: commitment.verifier,\n coordinator: commitment.coordinator,\n walletAddress: commitment.walletAddress,\n blockNumber: receipt.blockNumber,\n };\n }\n } catch {\n // Not a RequestStarted log, continue\n }\n }\n } catch (error) {\n console.warn(' ⚠️ Could not parse RequestStarted event from receipt:', error);\n }\n return null;\n }\n}\n","import { ethers } from 'ethers';\nimport type { Commitment } from '../types';\n\nexport class CommitmentUtils {\n /**\n * Calculate commitment hash\n * Matches the keccak256(abi.encode(commitment)) in Solidity\n */\n static hash(commitment: Commitment): string {\n const encoded = ethers.AbiCoder.defaultAbiCoder().encode(\n [\n 'bytes32', // requestId\n 'uint64', // subscriptionId\n 'bytes32', // containerId\n 'uint32', // interval\n 'bool', // useDeliveryInbox\n 'uint16', // redundancy\n 'address', // walletAddress\n 'uint256', // feeAmount\n 'address', // feeToken\n 'address', // verifier\n 'address', // coordinator\n ],\n [\n commitment.requestId,\n commitment.subscriptionId,\n commitment.containerId,\n commitment.interval,\n commitment.useDeliveryInbox,\n commitment.redundancy,\n commitment.walletAddress,\n commitment.feeAmount,\n commitment.feeToken,\n commitment.verifier,\n commitment.coordinator,\n ]\n );\n\n return ethers.keccak256(encoded);\n }\n\n /**\n * Verify commitment hash matches expected value\n */\n static verify(commitment: Commitment, expectedHash: string): boolean {\n const actualHash = this.hash(commitment);\n return actualHash === expectedHash;\n }\n\n /**\n * Encode commitment data for reportComputeResult\n * Returns ABI-encoded commitment struct\n */\n static encode(commitment: Commitment): string {\n return ethers.AbiCoder.defaultAbiCoder().encode(\n [\n 'bytes32', // requestId\n 'uint64', // subscriptionId\n 'bytes32', // containerId\n 'uint32', // interval\n 'bool', // useDeliveryInbox\n 'uint16', // redundancy\n 'address', // walletAddress\n 'uint256', // feeAmount\n 'address', // feeToken\n 'address', // verifier\n 'address', // coordinator\n ],\n [\n commitment.requestId,\n commitment.subscriptionId,\n commitment.containerId,\n commitment.interval,\n commitment.useDeliveryInbox,\n commitment.redundancy,\n commitment.walletAddress,\n commitment.feeAmount,\n commitment.feeToken,\n commitment.verifier,\n commitment.coordinator,\n ]\n );\n }\n\n /**\n * Create Commitment from RequestStartedEvent\n */\n static fromEvent(event: any, walletAddress: string): Commitment {\n return {\n requestId: event.requestId,\n subscriptionId: event.subscriptionId,\n containerId: event.containerId,\n interval: event.interval,\n redundancy: event.redundancy,\n useDeliveryInbox: event.useDeliveryInbox || false,\n feeToken: event.feeToken,\n feeAmount: event.feeAmount,\n walletAddress: walletAddress, // Client wallet from subscription\n verifier: event.verifier || ethers.ZeroAddress,\n coordinator: event.coordinator,\n };\n }\n}\n","import { readFileSync } from 'fs';\nimport { ethers } from 'ethers';\nimport type { NoosphereAgentConfig, ContainerMetadata } from '../types';\n\nexport class ConfigLoader {\n /**\n * Load agent configuration from JSON file\n */\n static loadFromFile(configPath: string): NoosphereAgentConfig {\n try {\n const configData = readFileSync(configPath, 'utf-8');\n const config = JSON.parse(configData) as NoosphereAgentConfig;\n\n // Validate required fields\n if (!config.chain || !config.chain.enabled) {\n throw new Error('Chain configuration is required and must be enabled');\n }\n\n if (!config.chain.rpcUrl) {\n throw new Error('Chain RPC URL is required');\n }\n\n if (!config.chain.routerAddress) {\n throw new Error('Router address is required');\n }\n\n if (!config.containers || config.containers.length === 0) {\n console.warn('⚠️ No containers configured in config file');\n }\n\n return config;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}`);\n }\n throw error;\n }\n }\n\n /**\n * Convert ContainerConfig to ContainerMetadata format\n */\n static containerConfigToMetadata(\n containerConfig: NoosphereAgentConfig['containers'][0]\n ): ContainerMetadata {\n // Parse image name and tag\n const [imageName, tag] = containerConfig.image.includes(':')\n ? containerConfig.image.split(':')\n : [containerConfig.image, 'latest'];\n\n // Extract container name from image (last part after /)\n const name = imageName.split('/').pop() || containerConfig.id;\n\n // Convert acceptedPayments to payments format\n let payments: ContainerMetadata['payments'] | undefined;\n if (containerConfig.acceptedPayments) {\n const basePrice = Object.values(containerConfig.acceptedPayments)[0]?.toString() || '0';\n payments = {\n basePrice,\n unit: 'wei',\n per: 'execution',\n };\n }\n\n return {\n id: containerConfig.id,\n name,\n image: imageName,\n tag,\n port: containerConfig.port,\n env: containerConfig.env,\n verified: !!containerConfig.verifierAddress,\n payments,\n };\n }\n\n /**\n * Get all containers from config as ContainerMetadata array\n */\n static getContainersFromConfig(config: NoosphereAgentConfig): Map<string, ContainerMetadata> {\n const containersMap = new Map<string, ContainerMetadata>();\n\n for (const containerConfig of config.containers) {\n const metadata = this.containerConfigToMetadata(containerConfig);\n // Hash the container ID the same way as the smart contract does:\n // keccak256(abi.encode(containerId))\n const containerIdHash = ethers.keccak256(\n ethers.AbiCoder.defaultAbiCoder().encode(['string'], [containerConfig.id])\n );\n containersMap.set(containerIdHash, metadata);\n }\n\n return containersMap;\n }\n\n /**\n * Get container config by ID\n */\n static getContainerConfig(\n config: NoosphereAgentConfig,\n containerId: string\n ): NoosphereAgentConfig['containers'][0] | undefined {\n return config.containers.find((c) => c.id === containerId);\n }\n}\n","export interface Commitment {\n requestId: string;\n subscriptionId: bigint;\n containerId: string;\n interval: number;\n redundancy: number;\n useDeliveryInbox: boolean;\n feeToken: string;\n feeAmount: bigint;\n walletAddress: string;\n verifier: string;\n coordinator: string;\n}\n\nexport interface Payment {\n recipient: string;\n feeToken: string;\n feeAmount: bigint;\n}\n\nexport interface ProofVerificationRequest {\n subscriptionId: bigint;\n interval: number;\n submitterAddress: string;\n escrowedAmount: bigint;\n}\n\nexport interface RequestStartedEvent {\n requestId: string;\n subscriptionId: bigint;\n containerId: string;\n interval: number;\n redundancy: number;\n useDeliveryInbox: boolean;\n feeAmount: bigint;\n feeToken: string;\n verifier: string;\n coordinator: string;\n walletAddress: string;\n blockNumber: number;\n}\n\nexport interface ComputeSubscription {\n owner: string;\n wallet: string;\n containerId: string;\n intervalSeconds: number;\n maxExecutions: number;\n redundancy: number;\n feeToken: string;\n feeAmount: bigint;\n verifier: string;\n routeId: string;\n activeAt: number;\n useDeliveryInbox: boolean;\n}\n\nexport interface AgentConfig {\n rpcUrl: string;\n wsRpcUrl?: string;\n privateKey?: string; // Optional - use keystore initialization instead\n routerAddress: string;\n coordinatorAddress: string;\n deploymentBlock?: number;\n pollingInterval?: number;\n}\n\n// Extended config for full agent configuration (matches Java agent config.json)\nexport interface NoosphereAgentConfig {\n forwardStats?: boolean;\n manageContainers?: boolean;\n startupWait?: number;\n agent?: {\n name: string;\n apiKey?: string;\n email?: string;\n };\n server?: {\n port: number;\n rateLimit?: {\n numRequests: number;\n period: number;\n };\n };\n hub?: {\n register: boolean;\n url: string;\n keepAlive?: {\n enabled: boolean;\n intervalMs: number;\n batchSize?: number;\n };\n };\n chain: {\n enabled: boolean;\n rpcUrl: string;\n wsRpcUrl?: string;\n trailHeadBlocks?: number;\n routerAddress: string;\n coordinatorAddress?: string;\n deploymentBlock?: number;\n processingInterval?: number;\n wallet: {\n maxGasLimit?: number;\n paymentAddress?: string;\n allowedSimErrors?: string[];\n keystore?: {\n path: string;\n password: string;\n keys?: {\n eth?: string;\n };\n };\n };\n snapshotSync?: {\n sleep?: number;\n batchSize?: number;\n startingSubId?: number;\n syncPeriod?: number;\n };\n connection?: {\n timeout?: number;\n readTimeout?: number;\n writeTimeout?: number;\n };\n gasConfig?: {\n priceMultiplier?: number;\n limitMultiplier?: number;\n };\n };\n docker?: {\n username?: string;\n password?: string;\n };\n containers: ContainerConfig[];\n}\n\nexport interface ContainerConfig {\n id: string;\n image: string;\n port?: string;\n env?: Record<string, string>;\n volumes?: string[];\n verifierAddress?: string;\n acceptedPayments?: Record<string, number>;\n}\n\nexport interface ContainerMetadata {\n id: string;\n name: string;\n image: string;\n tag?: string;\n port?: string;\n env?: Record<string, string>;\n requirements?: {\n memory?: string;\n cpu?: number;\n gpu?: boolean;\n };\n payments?: {\n basePrice: string;\n unit: string;\n per: string;\n };\n verified?: boolean;\n}\n\nexport interface VerifierMetadata {\n address: string;\n name: string;\n image: string;\n tag?: string;\n}\n\nexport enum FulfillResult {\n FULFILLED = 0,\n INVALID_REQUEST_ID = 1,\n INVALID_COMMITMENT = 2,\n SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION = 3,\n INSUFFICIENT_SUBSCRIPTION_BALANCE = 4,\n COST_EXCEEDS_COMMITMENT = 5,\n}\n\n/**\n * Event emitted when scheduler successfully prepares next interval\n */\nexport interface CommitmentSuccessEvent {\n subscriptionId: bigint;\n interval: bigint;\n txHash: string;\n blockNumber: number;\n gasUsed: string;\n gasPrice: string;\n gasCost: string;\n requestStartedEvent?: RequestStartedEvent;\n}\n","import { ethers } from 'ethers';\n\nexport class RequestIdUtils {\n /**\n * Pack subscriptionId and interval into requestId\n * Matches Solidity: keccak256(abi.encodePacked(subscriptionId, interval))\n */\n static pack(subscriptionId: bigint, interval: number): string {\n // encodePacked for uint64 and uint32\n const subscriptionIdBytes = ethers.zeroPadValue(ethers.toBeHex(subscriptionId), 8);\n const intervalBytes = ethers.zeroPadValue(ethers.toBeHex(interval), 4);\n\n const packed = ethers.concat([subscriptionIdBytes, intervalBytes]);\n return ethers.keccak256(packed);\n }\n\n /**\n * Unpack requestId into subscriptionId and interval (if stored separately)\n * Note: This is not possible from the hash alone - only for informational purposes\n * In practice, subscriptionId and interval are stored in events\n */\n static format(requestId: string, subscriptionId: bigint, interval: number): string {\n return `Request(id=${requestId.slice(0, 10)}..., sub=${subscriptionId}, interval=${interval})`;\n }\n}\n","export { EventMonitor } from './EventMonitor';\nexport type { CheckpointData } from './EventMonitor';\nexport { ContainerManager } from './ContainerManager';\nexport { NoosphereAgent } from './NoosphereAgent';\nexport type { ComputeDeliveredEvent, RequestStartedCallbackEvent, CommitmentSuccessCallbackEvent, RetryableEvent, ContainerExecutionConfig } from './NoosphereAgent';\nexport { SchedulerService } from './SchedulerService';\n\nexport * from './types';\nexport * from './utils';\n\n// Re-export crypto utilities for convenience\nexport { WalletManager, KeystoreManager } from '@noosphere/crypto';\nexport type { NoosphereKeystore, PaymentWalletInfo, KeystoreInfo } from '@noosphere/crypto';\n\n// Re-export registry utilities for convenience\nexport { RegistryManager } from '@noosphere/registry';\nexport type {\n ContainerMetadata as RegistryContainerMetadata,\n VerifierMetadata as RegistryVerifierMetadata,\n RegistryConfig,\n} from '@noosphere/registry';\n\n// Export config types\nexport type { NoosphereAgentConfig, ContainerConfig } from './types';\n"],"mappings":";;;;;;;;AAAA,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AAchB,IAAM,eAAN,cAA2B,aAAa;AAAA,EAa7C,YACU,QACA,WACA,gBACR,SACA;AACA,UAAM;AALE;AACA;AACA;AAVV,SAAQ,oBAAoB;AAC5B,SAAQ,uBAAuB;AAC/B,SAAQ,iBAAiB;AACzB,SAAQ,oBAA2C;AACnD,SAAQ,gBAAgB,KAAK,IAAI;AAU/B,SAAK,qBAAqB,OAAO,mBAAmB;AACpD,SAAK,eAAe;AACpB,SAAK,sBAAsB;AAAA,EAC7B;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI;AAEF,UAAI,KAAK,OAAO,UAAU;AACxB,aAAK,WAAW,IAAI,OAAO,kBAAkB,KAAK,OAAO,QAAQ;AAGjE,aAAK,eAAe;AACpB,gBAAQ,IAAI,oDAA+C;AAAA,MAC7D,OAAO;AACL,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAC9C;AAAA,IACF,SAAS,OAAO;AAEd,cAAQ,KAAK,qDAAqD;AAClE,WAAK,WAAW,IAAI,OAAO,gBAAgB,KAAK,OAAO,MAAM;AAC7D,WAAK,eAAe;AAAA,IACtB;AAGA,SAAK,SAAS,IAAI,OAAO,SAAS,KAAK,OAAO,eAAe,KAAK,WAAW,KAAK,QAAQ;AAE1F,SAAK,cAAc,IAAI,OAAO;AAAA,MAC5B,KAAK,OAAO;AAAA,MACZ,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAE3B,QAAI,KAAK,qBAAqB,gBAAgB;AAC5C,YAAM,aAAa,KAAK,oBAAoB,eAAe;AAC3D,UAAI,YAAY;AACd,aAAK,qBAAqB,WAAW;AAAA,MACvC;AAAA,IACF;AAEA,YAAQ,IAAI,uBAAuB,KAAK,kBAAkB,EAAE;AAG5D,UAAM,KAAK,aAAa,KAAK,oBAAoB,QAAQ;AAGzD,QAAI,KAAK,cAAc;AACrB,YAAM,KAAK,wBAAwB;AAAA,IACrC,OAAO;AACL,YAAM,KAAK,aAAa;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAc,aAAa,WAAmB,SAAyC;AACrF,YAAQ,IAAI,+BAA+B,SAAS,OAAO,OAAO,EAAE;AAEpE,UAAM,eAAe,MAAM,KAAK,SAAS,eAAe;AACxD,UAAM,gBAAgB,YAAY,WAAW,eAAe,OAAO,OAAO;AAG1E,UAAM,YAAY;AAClB,aAAS,QAAQ,WAAW,SAAS,eAAe,SAAS,WAAW;AACtE,YAAM,MAAM,KAAK,IAAI,QAAQ,YAAY,GAAG,aAAa;AAEzD,YAAM,SAAS,MAAM,KAAK,YAAY;AAAA,QACpC,KAAK,YAAY,QAAQ,eAAe;AAAA,QACxC;AAAA,QACA;AAAA,MACF;AAEA,iBAAW,SAAS,QAAQ;AAC1B,cAAM,KAAK,aAAa,KAAK;AAAA,MAC/B;AAEA,UAAI,OAAO,SAAS,GAAG;AACrB,aAAK,eAAe,GAAG;AAAA,MACzB;AAAA,IACF;AAEA,YAAQ,IAAI,+BAA+B,aAAa,EAAE;AAAA,EAC5D;AAAA,EAEA,MAAc,0BAAyC;AACrD,YAAQ,IAAI,uCAAuC;AAGnD,SAAK,YAAY,GAAG,kBAAkB,UAAU,SAAS;AACvD,YAAM,QAAQ,KAAK,KAAK,SAAS,CAAC;AAClC,WAAK,gBAAgB,KAAK,IAAI;AAC9B,YAAM,KAAK,aAAa,KAAK;AAE7B,YAAM,cAAc,MAAM;AAC1B,UAAI,cAAc,KAAK,sBAAsB,IAAI;AAC/C,aAAK,eAAe,WAAW;AAAA,MACjC;AAAA,IACF,CAAC;AAGD,QAAI,KAAK,oBAAoB,OAAO,mBAAmB;AACrD,YAAM,aAAa,KAAK;AAIxB,YAAM,KAAM,WAAmB;AAC/B,UAAI,IAAI;AACN,WAAG,GAAG,SAAS,MAAM;AACnB,kBAAQ,KAAK,0CAAgC;AAC7C,eAAK,iBAAiB;AAAA,QACxB,CAAC;AACD,WAAG,GAAG,SAAS,CAAC,UAAiB;AAC/B,kBAAQ,MAAM,iCAAuB,MAAM,OAAO;AAClD,eAAK,iBAAiB;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAGA,SAAK,eAAe;AAEpB,YAAQ,IAAI,yCAAoC;AAAA,EAClD;AAAA,EAEQ,iBAAuB;AAE7B,SAAK,oBAAoB,YAAY,YAAY;AAC/C,UAAI;AACF,YAAI,KAAK,oBAAoB,OAAO,mBAAmB;AAErD,gBAAM,cAAc,MAAM,KAAK,SAAS,eAAe;AAIvD,gBAAM,qBAAqB,KAAK,IAAI,IAAI,KAAK;AAC7C,cAAI,qBAAqB,QAAU,cAAc,KAAK,qBAAqB,GAAG;AAC5E,oBAAQ,IAAI,8BAAoB,KAAK,MAAM,qBAAqB,GAAI,CAAC,kCAAkC;AACvG,kBAAM,KAAK,mBAAmB;AAAA,UAChC;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,8DAAoD;AAClE,aAAK,iBAAiB;AAAA,MACxB;AAAA,IACF,GAAG,IAAM;AAAA,EACX;AAAA,EAEA,MAAc,qBAAoC;AAChD,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,SAAS,eAAe;AACxD,UAAI,eAAe,KAAK,oBAAoB;AAC1C,gBAAQ,IAAI,yCAAkC,KAAK,qBAAqB,CAAC,OAAO,YAAY,EAAE;AAC9F,cAAM,KAAK,aAAa,KAAK,qBAAqB,GAAG,YAAY;AACjE,aAAK,gBAAgB,KAAK,IAAI;AAAA,MAChC;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,mCAAmC,KAAK;AAAA,IACxD;AAAA,EACF;AAAA,EAEQ,mBAAyB;AAC/B,QAAI,KAAK,eAAgB;AACzB,SAAK,iBAAiB;AAGtB,QAAI,KAAK,mBAAmB;AAC1B,oBAAc,KAAK,iBAAiB;AACpC,WAAK,oBAAoB;AAAA,IAC3B;AAGA,SAAK,UAAU,EAAE,QAAQ,MAAM;AAC7B,WAAK,iBAAiB;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,eAA8B;AAC1C,YAAQ,IAAI,0CAA0C;AAEtD,UAAM,kBAAkB,KAAK,OAAO,mBAAmB;AACvD,QAAI,YAAY,MAAM,KAAK,SAAS,eAAe;AAEnD,gBAAY,YAAY;AACtB,UAAI;AACF,cAAM,eAAe,MAAM,KAAK,SAAS,eAAe;AACxD,YAAI,eAAe,WAAW;AAC5B,gBAAM,SAAS,MAAM,KAAK,YAAY;AAAA,YACpC,KAAK,YAAY,QAAQ,eAAe;AAAA,YACxC,YAAY;AAAA,YACZ;AAAA,UACF;AAEA,qBAAW,SAAS,QAAQ;AAC1B,kBAAM,KAAK,aAAa,KAAK;AAAA,UAC/B;AAEA,cAAI,OAAO,SAAS,GAAG;AACrB,iBAAK,eAAe,YAAY;AAAA,UAClC;AAEA,sBAAY;AAAA,QACd;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,kBAAkB,KAAK;AAAA,MACvC;AAAA,IACF,GAAG,eAAe;AAAA,EACpB;AAAA,EAEA,MAAc,aAAa,OAA2B;AAIpD,UAAM,aAAa,MAAM,KAAK;AAE9B,UAAM,sBAA2C;AAAA,MAC/C,WAAW,MAAM,KAAK;AAAA,MACtB,gBAAgB,MAAM,KAAK;AAAA,MAC3B,aAAa,MAAM,KAAK;AAAA,MACxB,UAAU,WAAW;AAAA,MACrB,YAAY,WAAW;AAAA,MACvB,kBAAkB,WAAW;AAAA,MAC7B,WAAW,WAAW;AAAA,MACtB,UAAU,WAAW;AAAA,MACrB,UAAU,WAAW;AAAA,MACrB,aAAa,WAAW;AAAA,MACxB,eAAe,WAAW;AAAA,MAC1B,aAAa,MAAM;AAAA,IACrB;AAGA,SAAK,KAAK,kBAAkB,mBAAmB;AAAA,EACjD;AAAA,EAEA,MAAc,YAA2B;AACvC,QAAI,KAAK,qBAAqB,KAAK,sBAAsB;AACvD,cAAQ,MAAM,kEAAkE;AAChF,WAAK,eAAe;AACpB,YAAM,KAAK,QAAQ;AACnB,YAAM,KAAK,aAAa;AACxB;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,KAAK,iBAAiB,GAAG,GAAK;AAC1E,YAAQ;AAAA,MACN,6BAAsB,OAAO,eAAe,KAAK,oBAAoB,CAAC,IAAI,KAAK,oBAAoB;AAAA,IACrG;AAEA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,OAAO,CAAC;AAE3D,QAAI;AAEF,UAAI,KAAK,aAAa;AACpB,aAAK,YAAY,mBAAmB;AAAA,MACtC;AACA,UAAI,KAAK,oBAAoB,OAAO,mBAAmB;AACrD,YAAI;AACF,gBAAM,KAAK,SAAS,QAAQ;AAAA,QAC9B,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,KAAK,QAAQ;AAGnB,YAAM,KAAK,mBAAmB;AAE9B,YAAM,KAAK,wBAAwB;AACnC,cAAQ,IAAI,iCAA4B;AACxC,WAAK,oBAAoB;AACzB,WAAK,gBAAgB,KAAK,IAAI;AAAA,IAChC,SAAS,OAAO;AACd,cAAQ,MAAM,wBAAwB,KAAK;AAC3C,WAAK;AACL,YAAM,KAAK,UAAU;AAAA,IACvB;AAAA,EACF;AAAA,EAEQ,eAAe,aAA2B;AAChD,SAAK,qBAAqB;AAG1B,QAAI,KAAK,qBAAqB,gBAAgB;AAC5C,WAAK,oBAAoB,eAAe;AAAA,QACtC;AAAA,QACA,gBAAgB,KAAK,IAAI;AAAA,MAC3B,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,OAAsB;AAE1B,QAAI,KAAK,mBAAmB;AAC1B,oBAAc,KAAK,iBAAiB;AACpC,WAAK,oBAAoB;AAAA,IAC3B;AAEA,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,mBAAmB;AAAA,IACjC;AACA,QAAI,KAAK,aAAa;AACpB,WAAK,YAAY,mBAAmB;AAAA,IACtC;AACA,QAAI,KAAK,oBAAoB,OAAO,mBAAmB;AACrD,YAAM,KAAK,SAAS,QAAQ;AAAA,IAC9B;AAAA,EACF;AACF;;;ACvVA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,WAAW;AASX,IAAM,mBAAN,MAAuB;AAAA,EAM5B,cAAc;AAJd,SAAQ,oBAA2C,oBAAI,IAAI;AAC3D,SAAQ,uBAAsD,oBAAI,IAAI;AACtE,SAAQ,iBAAsC,oBAAI,IAAI;AAGpD,SAAK,SAAS,IAAI,OAAO;AAAA,EAC3B;AAAA,EAEA,MAAM,aACJ,WACA,OACA,UAAkB,KAClB,oBAA4B,GAC5B,yBAAiC,KACE;AACnC,UAAM,YAAY,KAAK,IAAI;AAG3B,UAAM,OAAO,UAAU,OAAO,SAAS,UAAU,IAAI,IAAI;AAIzD,UAAM,gBAAgB,QAAQ,IAAI,iBAC9B,aAAa,UAAU,IAAI,KAC3B;AACJ,UAAM,MAAM,UAAU,aAAa,IAAI,IAAI;AAG3C,QAAI;AACJ,QAAI;AACF,YAAM,cAAc,KAAK,MAAM,KAAK;AACpC,oBAAc,EAAE,OAAc,GAAG,YAAY;AAAA,IAC/C,QAAQ;AACN,oBAAc,EAAE,MAAa;AAAA,IAC/B;AAEA,QAAI;AAEJ,aAAS,UAAU,GAAG,WAAW,mBAAmB,WAAW;AAC7D,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,KAAK,KAAK,aAAa;AAAA,UAClD;AAAA,UACA,SAAS;AAAA,YACP,gBAAgB;AAAA,UAClB;AAAA,QACF,CAAC;AAED,cAAMA,iBAAgB,KAAK,IAAI,IAAI;AAGnC,YAAI;AACJ,YAAI,OAAO,SAAS,SAAS,UAAU;AACrC,mBAAS,SAAS;AAAA,QACpB,WAAW,SAAS,KAAK,WAAW,QAAW;AAC7C,mBACE,OAAO,SAAS,KAAK,WAAW,WAC5B,SAAS,KAAK,SACd,KAAK,UAAU,SAAS,KAAK,MAAM;AAAA,QAC3C,OAAO;AACL,mBAAS,KAAK,UAAU,SAAS,IAAI;AAAA,QACvC;AAEA,eAAO;AAAA,UACL;AAAA,UACA,UAAU;AAAA,UACV,eAAAA;AAAA,QACF;AAAA,MACF,SAAS,OAAY;AACnB,oBAAY;AAGZ,YAAI,MAAM,SAAS,gBAAgB;AACjC,cAAI,UAAU,mBAAmB;AAC/B,oBAAQ,IAAI,yCAAoC,OAAO,IAAI,iBAAiB,kBAAkB,yBAAyB,GAAI,MAAM;AACjI,kBAAM,KAAK,MAAM,sBAAsB;AACvC;AAAA,UACF;AAAA,QACF;AAGA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,gBAAgB,KAAK,IAAI,IAAI;AAEnC,QAAI,UAAU,UAAU;AACtB,YAAM,IAAI;AAAA,QACR,wBAAwB,UAAU,SAAS,MAAM,KAAK,KAAK,UAAU,UAAU,SAAS,IAAI,CAAC;AAAA,MAC/F;AAAA,IACF,WAAW,UAAU,SAAS,gBAAgB;AAC5C,YAAM,IAAI;AAAA,QACR,qCAAqC,UAAU,QAAQ,IAAI,WAAW,iBAAiB;AAAA,MACzF;AAAA,IACF,WAAW,UAAU,SAAS,eAAe,UAAU,SAAS,gBAAgB;AAC9E,YAAM,IAAI,MAAM,qCAAqC,OAAO,IAAI;AAAA,IAClE;AAEA,UAAM;AAAA,EACR;AAAA,EAEQ,MAAM,IAA2B;AACvC,WAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AAAA,EACvD;AAAA,EAEA,MAAc,uBACZ,iBACA,SACA,WACmC;AACnC,QAAI;AAEF,YAAM,cAAc,MAAM,gBAAgB,QAAQ;AAClD,YAAM,WAAW,YAAY,MAAM,YAAY;AAG/C,YAAM,aAAa,KAAK,KAAK,SAAS,aAAa;AACnD,UAAI,SAAS;AACb,UAAI;AACF,iBAAS,MAAM,GAAG,SAAS,YAAY,OAAO;AAAA,MAChD,SAAS,OAAO;AAEd,cAAM,OAAO,MAAM,gBAAgB,KAAK;AAAA,UACtC,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV,CAAC;AACD,iBAAS,KAAK,SAAS;AAAA,MACzB;AAEA,YAAM,gBAAgB,KAAK,IAAI,IAAI;AAGnC,UAAI;AACF,cAAM,gBAAgB,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,MAC9C,SAAS,OAAO;AAAA,MAEhB;AAGA,YAAM,GAAG,GAAG,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAErD,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAEd,UAAI;AACF,cAAM,gBAAgB,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,MAC9C,QAAQ;AAAA,MAER;AACA,YAAM,GAAG,GAAG,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACrE,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,OAAe,KAA4B;AACjE,UAAM,WAAW,GAAG,KAAK,IAAI,GAAG;AAEhC,QAAI;AAEF,YAAM,KAAK,OAAO,SAAS,QAAQ,EAAE,QAAQ;AAC7C,cAAQ,IAAI,SAAS,QAAQ,iBAAiB;AAAA,IAChD,QAAQ;AAEN,cAAQ,IAAI,iBAAiB,QAAQ,KAAK;AAE1C,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,aAAK,OAAO,KAAK,UAAU,CAAC,KAAU,WAAkC;AACtE,cAAI,IAAK,QAAO,OAAO,GAAG;AAE1B,eAAK,OAAO,MAAM;AAAA,YAChB;AAAA,YACA,CAACC,SAAa;AACZ,kBAAIA,KAAK,QAAO,OAAOA,IAAG;AAC1B,sBAAQ,IAAI,uBAAkB,QAAQ,EAAE;AACxC,sBAAQ;AAAA,YACV;AAAA,YACA,CAAC,UAAe;AAEd,kBAAI,MAAM,QAAQ;AAChB,wBAAQ,IAAI,GAAG,MAAM,MAAM,IAAI,MAAM,YAAY,EAAE,EAAE;AAAA,cACvD;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,WAA2C;AACxE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAU,KAAK,CAAC,KAAK,SAAS;AAC5B,YAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,gBAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,QAAQ,IAA2B;AACzC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,MAAM,QAAQ,IAAI,GAAG,EAAE,CAAC;AAAA,EACrE;AAAA,EAEQ,YAAY,QAAwB;AAC1C,UAAM,QAAmC;AAAA,MACvC,GAAG;AAAA,MACH,IAAI;AAAA,MACJ,IAAI,OAAO;AAAA,MACX,IAAI,OAAO,OAAO;AAAA,IACpB;AAEA,UAAM,QAAQ,OAAO,YAAY,EAAE,MAAM,wBAAwB;AACjE,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,0BAA0B,MAAM,EAAE;AAAA,IACpD;AAEA,UAAM,CAAC,EAAE,OAAO,IAAI,IAAI;AACxB,WAAO,SAAS,OAAO,EAAE,IAAI,MAAM,IAAI;AAAA,EACzC;AAAA,EAEA,MAAM,uBAAyC;AAC7C,QAAI;AACF,YAAM,KAAK,OAAO,KAAK;AACvB,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,gBAA8B;AAClC,WAAO,KAAK,OAAO,KAAK;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAyB;AAC7B,QAAI,KAAK,kBAAkB,SAAS,GAAG;AACrC;AAAA,IACF;AAEA,YAAQ,IAAI,yBAAkB,KAAK,kBAAkB,IAAI,wBAAwB;AAEjF,UAAM,kBAAkB,MAAM,KAAK,KAAK,iBAAiB,EAAE,IAAI,OAAO,cAAc;AAClF,UAAI;AACF,cAAM,UAAU,MAAM,UAAU,QAAQ;AACxC,YAAI,QAAQ,MAAM,SAAS;AACzB,kBAAQ,IAAI,wBAAwB,QAAQ,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK;AAChE,gBAAM,UAAU,KAAK,EAAE,GAAG,GAAG,CAAC;AAC9B,gBAAM,UAAU,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,QACxC;AAAA,MACF,SAAS,OAAO;AAEd,gBAAQ,KAAK,2CAA4C,MAAgB,OAAO;AAAA,MAClF;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,IAAI,eAAe;AACjC,SAAK,kBAAkB,MAAM;AAC7B,YAAQ,IAAI,oCAA+B;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,2BAAmC;AACjC,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,YAA2D;AACjF,QAAI,WAAW,SAAS,GAAG;AACzB,cAAQ,IAAI,0BAA0B;AACtC;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,sBAAkB,WAAW,IAAI,gBAAgB;AAE7D,UAAM,uBAAuB,MAAM,KAAK,WAAW,QAAQ,CAAC,EAAE,IAAI,OAAO,CAAC,IAAI,SAAS,MAAM;AAC3F,YAAM,WAAW,GAAG,UAAU,KAAK,IAAI,UAAU,OAAO,QAAQ;AAEhE,UAAI;AACF,gBAAQ,IAAI,aAAa,QAAQ,KAAK;AACtC,cAAM,KAAK,UAAU,UAAU,OAAO,UAAU,OAAO,QAAQ;AAC/D,gBAAQ,IAAI,YAAO,QAAQ,QAAQ;AAAA,MACrC,SAAS,OAAO;AACd,gBAAQ,MAAM,2BAAsB,QAAQ,KAAM,MAAgB,OAAO;AAEzE;AAAA,MACF;AAEA,UAAI;AAEF,cAAM,KAAK,yBAAyB,IAAI,SAAS;AAAA,MACnD,SAAS,OAAO;AACd,gBAAQ,MAAM,4BAAuB,UAAU,QAAQ,UAAU,KAAK,KAAM,MAAgB,OAAO;AAAA,MACrG;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,IAAI,oBAAoB;AACtC,YAAQ,IAAI,kCAA6B;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,yBACZ,aACA,UACe;AAGf,UAAM,gBAAgB,aAAa,SAAS,IAAI;AAChD,UAAM,WAAW,GAAG,SAAS,KAAK,IAAI,SAAS,OAAO,QAAQ;AAG9D,UAAM,oBAAoB,KAAK,OAAO,aAAa,aAAa;AAChE,QAAI;AACF,YAAM,UAAU,MAAM,kBAAkB,QAAQ;AAChD,UAAI,QAAQ,MAAM,SAAS;AACzB,gBAAQ,IAAI,sBAAiB,aAAa,kBAAkB;AAC5D,aAAK,qBAAqB,IAAI,aAAa,iBAAiB;AAC5D;AAAA,MACF,OAAO;AAEL,YAAI;AACF,gBAAM,kBAAkB,MAAM;AAC9B,kBAAQ,IAAI,uCAAkC,aAAa,EAAE;AAC7D,eAAK,qBAAqB,IAAI,aAAa,iBAAiB;AAC5D;AAAA,QACF,SAAS,UAAU;AAEjB,kBAAQ,IAAI,gCAAgC,aAAa,iBAAiB;AAC1E,gBAAM,kBAAkB,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AAAA,IAEd;AAGA,UAAM,gBAAgB,QAAQ,IAAI;AAClC,UAAM,gBAA+C;AAAA,MACnD,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,MACL,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc,SAAS,OAAO,EAAE,CAAC,GAAG,SAAS,IAAI,MAAM,GAAG,CAAC,EAAE,IAAI;AAAA,MACjE,YAAY;AAAA,QACV,YAAY;AAAA;AAAA;AAAA,QAEZ,cACE,SAAS,QAAQ,CAAC,gBACd;AAAA,UACE,CAAC,GAAG,SAAS,IAAI,MAAM,GAAG,CAAC,EAAE,UAAU,SAAS,KAAK,CAAC;AAAA,QACxD,IACA;AAAA;AAAA,QAEN,aAAa,iBAAiB;AAAA,MAChC;AAAA,MACA,KAAK,SAAS,MAAM,OAAO,QAAQ,SAAS,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI;AAAA,IAClF;AAGA,QAAI,SAAS,cAAc;AACzB,YAAM,YAAiB,CAAC;AAExB,UAAI,SAAS,aAAa,QAAQ;AAChC,kBAAU,SAAS,KAAK,YAAY,SAAS,aAAa,MAAM;AAAA,MAClE;AAEA,UAAI,SAAS,aAAa,KAAK;AAC7B,kBAAU,WAAW,SAAS,aAAa,MAAM;AAAA,MACnD;AAEA,UAAI,SAAS,aAAa,KAAK;AAC7B,sBAAc,WAAY,iBAAiB;AAAA,UACzC;AAAA,YACE,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,cAAc,CAAC,CAAC,KAAK,CAAC;AAAA,UACxB;AAAA,QACF;AAAA,MACF;AAEA,UAAI,OAAO,KAAK,SAAS,EAAE,SAAS,GAAG;AACrC,sBAAc,aAAa;AAAA,UACzB,GAAG,cAAc;AAAA,UACjB,GAAG;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAEA,UAAM,kBAAkB,MAAM,KAAK,OAAO,gBAAgB,aAAa;AACvE,UAAM,gBAAgB,MAAM;AAE5B,SAAK,qBAAqB,IAAI,aAAa,eAAe;AAC1D,QAAI,SAAS,MAAM;AACjB,WAAK,eAAe,IAAI,aAAa,SAAS,SAAS,IAAI,CAAC;AAAA,IAC9D;AAEA,YAAQ,IAAI,yCAAoC,aAAa,EAAE;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,2BAA0C;AAC9C,QAAI,KAAK,qBAAqB,SAAS,GAAG;AACxC;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,qBAAiB,KAAK,qBAAqB,IAAI,2BAA2B;AAEtF,UAAM,eAAe,MAAM,KAAK,KAAK,qBAAqB,QAAQ,CAAC,EAAE;AAAA,MACnE,OAAO,CAAC,IAAI,SAAS,MAAM;AACzB,YAAI;AACF,gBAAM,UAAU,MAAM,UAAU,QAAQ;AACxC,cAAI,QAAQ,MAAM,SAAS;AACzB,oBAAQ,IAAI,cAAc,QAAQ,IAAI,KAAK;AAC3C,kBAAM,UAAU,KAAK,EAAE,GAAG,GAAG,CAAC;AAAA,UAChC;AACA,gBAAM,UAAU,OAAO,EAAE,OAAO,KAAK,CAAC;AACtC,kBAAQ,IAAI,oBAAe,QAAQ,IAAI,EAAE;AAAA,QAC3C,SAAS,OAAO;AACd,kBAAQ,KAAK,uCAAuC,EAAE,KAAM,MAAgB,OAAO;AAAA,QACrF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,YAAY;AAC9B,SAAK,qBAAqB,MAAM;AAChC,SAAK,eAAe,MAAM;AAC1B,YAAQ,IAAI,4CAAuC;AAAA,EACrD;AACF;;;ACzcA,SAAS,UAAAC,eAAc;;;ACOvB,SAAS,gBAAAC,qBAAoB;AAE7B,SAAS,uCAAiE;AA6BnE,IAAM,mBAAN,cAA+BA,cAAa;AAAA,EAYjD,YACU,UACA,QACA,aACA,aACR,oBACA,QACA,cACA;AACA,UAAM;AARE;AACA;AACA;AACA;AAfV,SAAQ,gBAAgB,oBAAI,IAA+B;AAC3D,SAAQ,qBAAqB,oBAAI,IAAY;AAC7C;AAAA,SAAQ,aAAa,oBAAI,IAAoB;AAK7C,SAAQ,eAAuB;AAc7B,SAAK,SAAS;AAAA,MACZ,gBAAgB,QAAQ,kBAAkB;AAAA;AAAA,MAC1C,kBAAkB,QAAQ,oBAAoB;AAAA,MAC9C,cAAc,QAAQ,gBAAgB;AAAA;AAAA,MACtC,wBAAwB,QAAQ;AAAA,MAChC,uBAAuB,QAAQ;AAAA,IACjC;AACA,SAAK,eAAe;AAGpB,QAAI,oBAAoB;AACtB,WAAK,cAAc,IAAI,gCAAgC,oBAAoB,QAAQ;AACnF,cAAQ,IAAI,8CAAyC,kBAAkB,EAAE;AAAA,IAC3E,OAAO;AACL,cAAQ,KAAK,mFAAyE;AAAA,IACxF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,YAAQ,IAAI,yCAAkC;AAC9C,YAAQ,IAAI,qCAAqC,KAAK,OAAO,cAAc,IAAI;AAC/E,YAAQ,IAAI,kBAAkB,KAAK,OAAO,YAAY,IAAI;AAG1D,QAAI,KAAK,OAAO,wBAAwB;AACtC,YAAM,SAAS,KAAK,OAAO,uBAAuB;AAClD,iBAAW,OAAO,QAAQ;AACxB,aAAK,mBAAmB,IAAI,GAAG;AAAA,MACjC;AACA,UAAI,OAAO,SAAS,GAAG;AACrB,gBAAQ,IAAI,YAAY,OAAO,MAAM,mCAAmC;AAAA,MAC1E;AAAA,IACF;AAGA,SAAK,gBAAgB,YAAY,MAAM,KAAK,oBAAoB,GAAG,KAAK,OAAO,cAAc;AAG7F,SAAK,YAAY,YAAY,MAAM,KAAK,kBAAkB,GAAG,KAAK,OAAO,YAAY;AAErF,YAAQ,IAAI,kCAA6B;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,QAAI,KAAK,eAAe;AACtB,oBAAc,KAAK,aAAa;AAChC,WAAK,gBAAgB;AAAA,IACvB;AACA,QAAI,KAAK,WAAW;AAClB,oBAAc,KAAK,SAAS;AAC5B,WAAK,YAAY;AAAA,IACnB;AACA,YAAQ,IAAI,kCAA6B;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,kBACE,cACM;AACN,UAAM,MAAM,aAAa,eAAe,SAAS;AAEjD,QAAI,KAAK,cAAc,IAAI,GAAG,GAAG;AAC/B,cAAQ,IAAI,kBAAkB,GAAG,+BAA+B;AAAA,IAClE;AAIA,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,UAAM,UAAU,MAAM,OAAO,aAAa,QAAQ;AAClD,UAAM,kBAAkB,aAAa,kBAAkB,KACnD,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,OAAO,aAAa,eAAe,CAAC,IAAI,CAAC,CAAC,IAClF;AAEJ,SAAK,cAAc,IAAI,KAAK;AAAA,MAC1B,GAAG;AAAA,MACH;AAAA,MACA,iBAAiB,KAAK,IAAI;AAAA,MAC1B,YAAY;AAAA,IACd,CAAC;AAED,YAAQ,IAAI,gCAA2B,GAAG,EAAE;AAC5C,SAAK,KAAK,wBAAwB,aAAa,cAAc;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,gBAA8B;AAChD,UAAM,MAAM,eAAe,SAAS;AACpC,QAAI,KAAK,cAAc,OAAO,GAAG,GAAG;AAElC,YAAM,SAAS,GAAG,GAAG;AACrB,UAAI,eAAe;AACnB,iBAAW,iBAAiB,KAAK,oBAAoB;AACnD,YAAI,cAAc,WAAW,MAAM,GAAG;AACpC,eAAK,mBAAmB,OAAO,aAAa;AAC5C;AAAA,QACF;AAAA,MACF;AACA,UAAI,eAAe,GAAG;AACpB,gBAAQ,IAAI,0BAAmB,YAAY,yCAAyC,GAAG,EAAE;AAAA,MAC3F;AACA,cAAQ,IAAI,wCAAmC,GAAG,EAAE;AACpD,WAAK,KAAK,0BAA0B,cAAc;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,gBAAwB,UAAwB;AACpE,UAAM,gBAAgB,GAAG,cAAc,IAAI,QAAQ;AACnD,SAAK,qBAAqB,aAAa;AACvC,YAAQ,IAAI,4BAAuB,QAAQ,kCAAkC,cAAc,EAAE;AAAA,EAC/F;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAqB,KAAmB;AAC9C,QAAI,CAAC,KAAK,mBAAmB,IAAI,GAAG,GAAG;AACrC,WAAK,mBAAmB,IAAI,GAAG;AAC/B,UAAI,KAAK,OAAO,uBAAuB;AACrC,aAAK,OAAO,sBAAsB,GAAG;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,sBAAqC;AACjD,QAAI;AACF,YAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,cAAQ,IAAI;AAAA,aAAS,SAAS,0CAA0C;AAGxE,WAAK,eAAe;AAGpB,YAAM,KAAK,2BAA2B;AAEtC,YAAM,gBAAe,oBAAI,KAAK,GAAE,YAAY;AAC5C,cAAQ,IAAI,WAAM,YAAY;AAAA,CAA0C;AAAA,IAC1E,SAAS,OAAO;AACd,cAAQ,MAAM,0CAAqC,KAAK;AACxD,WAAK,KAAK,SAAS,KAAK;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,6BAA4C;AAExD,UAAM,cAAc,MAAM,KAAK,SAAS,SAAS,QAAQ;AACzD,QAAI,CAAC,aAAa;AAChB,cAAQ,KAAK,qDAAqD;AAClE;AAAA,IACF;AACA,UAAM,mBAAmB,YAAY;AAErC,QAAI,KAAK,cAAc,SAAS,GAAG;AACjC,cAAQ,IAAI,+BAA+B;AAC3C;AAAA,IACF;AAEA,YAAQ,IAAI,gBAAgB,KAAK,cAAc,IAAI,qBAAqB;AAExE,eAAW,CAAC,OAAO,GAAG,KAAK,KAAK,cAAc,QAAQ,GAAG;AACvD,UAAI,kBAA0B;AAC9B,UAAI;AAEF,YAAI,IAAI,mBAAmB,IAAI;AAC7B,kBAAQ,KAAK,2BAA2B,KAAK,8BAA8B,IAAI,eAAe,GAAG;AACjG,eAAK,oBAAoB,IAAI,cAAc;AAC3C;AAAA,QACF;AAIA,YAAI;AACF,4BAAkB,OAAO,MAAM,KAAK,OAAO,+BAA+B,IAAI,cAAc,CAAC;AAAA,QAC/F,SAAS,OAAO;AACd,gBAAM,eAAgB,MAAgB,WAAW;AACjD,kBAAQ,KAAK,yDAAyD,KAAK,KAAK,YAAY;AAG5F,cAAI,aAAa,SAAS,sBAAsB,GAAG;AACjD,oBAAQ,IAAI,kBAAkB,KAAK,uCAAuC;AAC1E,iBAAK,oBAAoB,IAAI,cAAc;AAC3C;AAAA,UACF;AAGA,gBAAM,uBAAuB,OAAO,gBAAgB,IAAI,IAAI;AAC5D,4BAAmB,uBAAuB,IAAI,kBAAmB;AAAA,QACnE;AAEA,gBAAQ,IAAI,kBAAkB,KAAK,qBAAqB,eAAe,mBAAmB,IAAI,aAAa,cAAc,IAAI,QAAQ,EAAE;AAGvI,YAAI,CAAC,KAAK,cAAc,KAAK,gBAAgB,GAAG;AAC9C;AAAA,QACF;AAGA,cAAM,gBAAgB,GAAG,KAAK,IAAI,eAAe;AACjD,YAAI,KAAK,mBAAmB,IAAI,aAAa,GAAG;AAC9C;AAAA,QACF;AAGA,cAAM,gBAAgB,MAAM,KAAK,sBAAsB,IAAI,gBAAgB,eAAe;AAE1F,YAAI,eAAe;AACjB,eAAK,qBAAqB,aAAa;AACvC,kBAAQ,IAAI,kBAAkB,KAAK,aAAa,eAAe,oBAAoB;AACnF;AAAA,QACF;AAGA,cAAM,KAAK,oBAAoB,KAAK,eAAe;AAAA,MACrD,SAAS,OAAO;AACd,cAAM,eAAgB,MAAgB;AACtC,gBAAQ,MAAM,mCAAmC,KAAK,KAAK,KAAK;AAGhE,cAAM,gBAAgB,CAAC,IAAW,SAA0B;AAC1D,cAAI,UAA6B;AACjC,iBAAO,SAAS;AACd,gBAAI,QAAQ,SAAS,SAAS,IAAI,EAAG,QAAO;AAC5C,sBAAW,QAAgB;AAAA,UAC7B;AACA,iBAAO;AAAA,QACT;AAGA,YAAI,cAAc,OAAgB,uBAAuB,KACrD,cAAc,OAAgB,kCAAkC,GAAG;AACrE,kBAAQ,IAAI,cAAc,eAAe,qBAAqB,KAAK,kEAAkE;AACrI,gBAAM,gBAAgB,GAAG,KAAK,IAAI,eAAe;AACjD,eAAK,qBAAqB,aAAa;AACvC,cAAI,kBAAkB,kBAAkB;AAAA,QAC1C,WAIS,cAAc,OAAgB,YAAY,KAC1C,cAAc,OAAgB,gBAAgB,GAAG;AACxD,kBAAQ,IAAI,kBAAkB,KAAK,6DAA6D;AAAA,QAGlG,WAES,cAAc,OAAgB,oBAAoB,KAClD,cAAc,OAAgB,+BAA+B,GAAG;AACvE,kBAAQ,IAAI,kBAAkB,KAAK,oDAAoD;AACvF,eAAK,oBAAoB,IAAI,cAAc;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,KAAwB,kBAAmC;AAC/E,UAAM,QAAQ,IAAI,eAAe,SAAS;AAG1C,QAAI,OAAO,gBAAgB,IAAI,IAAI,UAAU;AAC3C,cAAQ,IAAI,yCAAyC,gBAAgB,cAAc,IAAI,QAAQ,GAAG;AAClG,aAAO;AAAA,IACT;AAGA,UAAM,uBAAuB,OAAO,gBAAgB,IAAI,IAAI;AAC5D,UAAM,kBAAmB,uBAAuB,IAAI,kBAAmB;AAOvE,QAAI,IAAI,gBAAgB,MAAM,kBAAkB,IAAI,eAAe;AACjE,cAAQ,IAAI,oBAAoB,KAAK,wBAAwB,eAAe,oBAAoB,IAAI,aAAa,kBAAkB;AACnI,WAAK,oBAAoB,IAAI,cAAc;AAC3C,aAAO;AAAA,IACT;AAGA,UAAM,SAAS,GAAG,IAAI,cAAc,IAAI,eAAe;AACvD,QAAI,KAAK,WAAW,IAAI,MAAM,GAAG;AAC/B,cAAQ,IAAI,8CAA8C,eAAe,EAAE;AAC3E,aAAO;AAAA,IACT;AAGA,QAAI,IAAI,cAAc,KAAK,OAAO,kBAAkB;AAClD,cAAQ,IAAI,yCAAyC,IAAI,UAAU,IAAI,KAAK,OAAO,gBAAgB,GAAG;AACtG,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,sBAAsB,gBAAwB,UAAoC;AAC9F,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,YAAY;AAAA,QACxC,KAAK,aAAa,gBAAgB,QAAQ;AAAA,MAC5C;AACA,aAAO,aAAa;AAAA,IACtB,SAAS,OAAO;AACd,cAAQ,MAAM,+BAA+B,KAAK;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,oBAAoB,KAAwB,UAAiC;AACzF,UAAM,SAAS,GAAG,IAAI,cAAc,IAAI,QAAQ;AAEhD,QAAI;AACF,cAAQ,IAAI,wBAAwB,QAAQ,qBAAqB,IAAI,cAAc,KAAK;AAIxF,YAAM,qBAAqB,OAAO,MAAM,KAAK,OAAO,+BAA+B,IAAI,cAAc,CAAC;AAEtG,UAAI,uBAAuB,YAAY,uBAAuB,WAAW,IAAI;AAC3E,gBAAQ,IAAI,8CAAoC,QAAQ,sBAAsB,kBAAkB,aAAa;AAC7G;AAAA,MACF;AAGA,YAAM,KAAK,MAAM,KAAK,YAAY;AAAA,QAChC,IAAI;AAAA,QACJ;AAAA,QACA,KAAK;AAAA,MACP;AAGA,WAAK,WAAW,IAAI,QAAQ,GAAG,IAAI;AACnC,UAAI,YAAY,GAAG;AAEnB,cAAQ,IAAI,iCAA0B,GAAG,IAAI,EAAE;AAG/C,YAAM,UAAU,MAAM,GAAG,KAAK;AAE9B,UAAI,QAAQ,WAAW,GAAG;AACxB,gBAAQ;AAAA,UACN,qBAAgB,QAAQ,iCAAiC,QAAQ,WAAW;AAAA,QAC9E;AAGA,cAAM,gBAAgB,GAAG,IAAI,cAAc,IAAI,QAAQ;AACvD,aAAK,qBAAqB,aAAa;AAGvC,YAAI,kBAAkB;AACtB,YAAI,kBAAkB,KAAK,IAAI;AAC/B,YAAI,aAAa;AACjB,YAAI,YAAY;AAChB,aAAK,WAAW,OAAO,MAAM;AAI7B,cAAM,sBAAsB,KAAK,+BAA+B,SAAS,GAAG;AAG5E,cAAM,UAAU,QAAQ;AACxB,cAAM,WAAW,QAAQ,YAAY,GAAG,YAAY;AACpD,cAAM,UAAU,UAAU;AAE1B,aAAK,KAAK,sBAAsB;AAAA,UAC9B,gBAAgB,IAAI;AAAA,UACpB;AAAA,UACA,QAAQ,GAAG;AAAA,UACX,aAAa,QAAQ;AAAA,UACrB,SAAS,QAAQ,SAAS;AAAA,UAC1B,UAAU,SAAS,SAAS;AAAA,UAC5B,SAAS,QAAQ,SAAS;AAAA,UAC1B;AAAA;AAAA,QACF,CAAC;AAAA,MACH,OAAO;AACL,cAAM,IAAI,MAAM,kCAAkC,QAAQ,MAAM,EAAE;AAAA,MACpE;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,oCAAoC,MAAM,KAAK,KAAK;AAGlE,UAAI,YAAY;AAChB,WAAK,WAAW,OAAO,MAAM;AAG7B,YAAM,eAAgB,MAAgB,WAAW;AACjD,YAAM,wBAAwB,aAAa,SAAS,YAAY,KACjC,aAAa,SAAS,gBAAgB;AAErE,UAAI,uBAAuB;AAGzB,gBAAQ,IAAI,kBAAkB,IAAI,cAAc,6DAA6D;AAC7G,YAAI,aAAa;AACjB;AAAA,MACF;AAGA,UAAI;AAEJ,UAAI,IAAI,cAAc,KAAK,OAAO,kBAAkB;AAClD,gBAAQ,IAAI,oCAAoC,MAAM,EAAE;AACxD,aAAK,KAAK,qBAAqB;AAAA,UAC7B,gBAAgB,IAAI;AAAA,UACpB;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAuB;AAE7B,UAAM,iBAAiB,KAAK,IAAI,IAAI,IAAI,KAAK;AAE7C,eAAW,CAAC,OAAO,GAAG,KAAK,KAAK,cAAc,QAAQ,GAAG;AACvD,UAAI,IAAI,kBAAkB,kBAAkB,IAAI,WAAW;AACzD,gBAAQ,IAAI,gDAAgD,KAAK,EAAE;AACnE,YAAI,YAAY;AAChB,YAAI,aAAa;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,oBAAmC;AAC/C,QAAI,CAAC,KAAK,aAAa;AAErB,WAAK,KAAK,WAAW;AACrB;AAAA,IACF;AAEA,QAAI;AAEF,UAAI,KAAK,sBAAsB,QAAW;AACxC,aAAK,oBAAoB,MAAM,KAAK,OAAO,sBAAsB;AACjE,gBAAQ,IAAI,8CAAuC,KAAK,iBAAiB,EAAE;AAAA,MAC7E;AAGA,UAAI,KAAK,gBAAgB,KAAK,mBAAoB;AAEhD,cAAM,cAAc,MAAM,KAAK,OAAO,sBAAsB;AAC5D,YAAI,cAAc,KAAK,mBAAoB;AACzC,kBAAQ,IAAI,sCAA+B,WAAW,SAAS,KAAK,iBAAiB,GAAG;AACxF,eAAK,oBAAoB;AAAA,QAE3B,OAAO;AACL,eAAK,KAAK,WAAW;AACrB;AAAA,QACF;AAAA,MACF;AAEA,YAAM,WAAW,KAAK;AAEtB,YAAM,eAAe,MAAM,KAAK,SAAS,eAAe;AACxD,YAAM,QAAQ,MAAM,KAAK,SAAS,SAAS,YAAY;AACvD,YAAM,YAAY,OAAO,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAGlE,YAAM,aAAa;AACnB,YAAM,UAAU,KAAK,eAAe;AACpC,YAAM,QAAQ,UAAU,aAAa,KAAK,WACtC,WACA,UAAU,aAAa;AAE3B,YAAM,gBAAgB,MAAM,KAAK,YAAY,iBAAiB,SAAS,OAAO,YAAY;AAE1F,UAAI,cAAc,WAAW,GAAG;AAE9B,aAAK,KAAK,WAAW;AACrB;AAAA,MACF;AAGA,UAAI,mBAAmB;AACvB,UAAI,oBAAoB;AACxB,UAAI,kBAAkB;AACtB,UAAI,eAAe;AACnB,UAAI,kBAAkB;AAEtB,cAAQ,IAAI,aAAa,cAAc,MAAM,8BAA8B,SAAS,GAAG;AAEvF,eAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,cAAM,MAAM,cAAc,CAAC;AAC3B,cAAM,iBAAiB,UAAU,OAAO,CAAC;AAGzC,YAAI,IAAI,gBAAgB,sEAAsE;AAC5F;AACA;AAAA,QACF;AAGA,YAAI,CAAC,KAAK,qBAAqB,KAAK,SAAS,GAAG;AAE9C,cAAI,IAAI,kBAAkB,GAAG;AAC3B,oBAAQ,IAAI,SAAS,cAAc,wBAAwB,IAAI,QAAQ,SAAS,SAAS,GAAG;AAAA,UAC9F;AACA;AACA;AAAA,QACF;AAGA,YAAI,KAAK,gBAAgB,CAAC,KAAK,aAAa,IAAI,WAAW,GAAG;AAC5D;AACA;AAAA,QACF;AAGA,YAAI,KAAK,4BAA4B,KAAK,cAAc,GAAG;AACzD;AAAA,QACF,OAAO;AAEL,cAAI,IAAI,mBAAmB,GAAG;AAC5B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,IAAI,iBAAiB,gBAAgB,aAAa,YAAY,WAAW,eAAe,cAAc,iBAAiB,4BAA4B,eAAe,YAAY;AAGtL,WAAK,eAAe;AAGpB,UAAI,mBAAmB,GAAG;AACxB,gBAAQ,IAAI,iBAAY,gBAAgB,6BAA6B,OAAO,MAAM,KAAK,GAAG;AAAA,MAC5F;AAGA,UAAI,KAAK,gBAAgB,UAAU;AACjC,gBAAQ,IAAI,yCAAoC,QAAQ,gBAAgB;AAAA,MAC1E;AAEA,WAAK,KAAK,kBAAkB;AAAA,QAC1B,eAAe,KAAK,cAAc;AAAA,QAClC;AAAA,MACF,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,gCAAgC,KAAK;AACnD,WAAK,KAAK,cAAc,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAqB,KAA0B,kBAAmC;AAExF,QAAI,mBAAmB,IAAI,UAAU;AACnC,aAAO;AAAA,IACT;AAKA,QAAI,IAAI,gBAAgB,KAAK,IAAI,kBAAkB,GAAG;AACpD,YAAM,UAAU,mBAAmB,IAAI;AACvC,YAAM,kBAAkB,KAAK,MAAM,UAAU,IAAI,eAAe;AAGhE,UAAI,mBAAmB,IAAI,eAAe;AACxC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,4BAA4B,KAA0B,gBAAiC;AAE7F,QAAI,IAAI,gBAAgB,sEAAsE;AAE5F,aAAO;AAAA,IACT;AAGA,QAAI,IAAI,WAAW,8CAA8C;AAC/D,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,eAAe,SAAS;AAEpC,QAAI,KAAK,cAAc,IAAI,GAAG,GAAG;AAE/B,aAAO;AAAA,IACT;AAGA,QAAI,IAAI,mBAAmB,GAAG;AAE5B,aAAO;AAAA,IACT;AAGA,QAAI;AACF,YAAM,EAAE,QAAAC,QAAO,IAAI,UAAQ,QAAQ;AACnC,YAAM,iBAAiBA,QAAO,oBAAoB,IAAI,WAAW;AACjE,cAAQ,IAAI,mCAA8B,cAAc,EAAE;AAC1D,cAAQ,IAAI,eAAe,IAAI,MAAM,EAAE;AACvC,cAAQ,IAAI,iBAAiB,IAAI,eAAe,GAAG;AACnD,cAAQ,IAAI,uBAAuB,IAAI,aAAa,EAAE;AAAA,IACxD,SAAS,GAAG;AACV,cAAQ,IAAI,mCAA8B,IAAI,WAAW,EAAE;AAAA,IAC7D;AAIA,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,UAAM,UAAU,MAAM,IAAI;AAC1B,UAAM,kBACJ,IAAI,kBAAkB,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,IAAI,eAAe,IAAI,CAAC,IAAI;AAEzF,SAAK,cAAc,IAAI,KAAK;AAAA,MAC1B;AAAA,MACA,SAAS,IAAI;AAAA,MACb,aAAa,IAAI;AAAA,MACjB,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,UAAU,OAAO,IAAI,QAAQ;AAAA,MAC7B,iBAAiB,OAAO,IAAI,eAAe;AAAA,MAC3C,eAAe,OAAO,SAAS,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI;AAAA,MAChF,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI,YAAY;AAAA,MAC1B,iBAAiB,OAAO,eAAe;AAAA,MACvC,iBAAiB,KAAK,IAAI;AAAA,MAC1B,YAAY;AAAA,IACd,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,gBAAwB,UAA0B;AAErE,UAAMA,UAAS,UAAQ,QAAQ;AAC/B,UAAM,WAAWA,QAAO,SAAS,gBAAgB;AACjD,WAAOA,QAAO,UAAU,SAAS,OAAO,CAAC,WAAW,SAAS,GAAG,CAAC,gBAAgB,QAAQ,CAAC,CAAC;AAAA,EAC7F;AAAA;AAAA;AAAA;AAAA,EAKA,WAKE;AACA,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,UAAM,cAAc,MAAM,KAAK,KAAK,cAAc,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ;AAE1E,UAAI,OAAO,GAAG,IAAI,IAAI,UAAU;AAC9B,eAAO;AAAA,MACT;AAGA,UAAI,IAAI,gBAAgB,IAAI;AAC1B,cAAM,UAAU,OAAO,GAAG,IAAI,IAAI;AAClC,cAAM,kBAAkB,UAAU,IAAI;AAGtC,YAAI,mBAAmB,IAAI,eAAe;AACxC,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,aAAO;AAAA,IACT,CAAC,EAAE;AAEH,WAAO;AAAA,MACL,oBAAoB,KAAK,cAAc;AAAA,MACvC,qBAAqB;AAAA,MACrB,oBAAoB,KAAK,mBAAmB;AAAA,MAC5C,qBAAqB,KAAK,WAAW;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAwC;AACtC,WAAO,MAAM,KAAK,KAAK,cAAc,OAAO,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,+BACN,SACA,KAC4B;AAC5B,QAAI;AAEF,iBAAW,OAAO,QAAQ,MAAM;AAC9B,YAAI;AACF,gBAAM,SAAS,KAAK,YAAY,UAAU,SAAS;AAAA,YACjD,QAAQ,IAAI;AAAA,YACZ,MAAM,IAAI;AAAA,UACZ,CAAC;AAED,cAAI,UAAU,OAAO,SAAS,kBAAkB;AAC9C,kBAAM,aAAa,OAAO,KAAK;AAC/B,mBAAO;AAAA,cACL,WAAW,OAAO,KAAK;AAAA,cACvB,gBAAgB,OAAO,KAAK;AAAA,cAC5B,aAAa,OAAO,KAAK;AAAA,cACzB,UAAU,OAAO,WAAW,QAAQ;AAAA,cACpC,YAAY,OAAO,WAAW,UAAU;AAAA,cACxC,kBAAkB,WAAW;AAAA,cAC7B,WAAW,WAAW;AAAA,cACtB,UAAU,WAAW;AAAA,cACrB,UAAU,WAAW;AAAA,cACrB,aAAa,WAAW;AAAA,cACxB,eAAe,WAAW;AAAA,cAC1B,aAAa,QAAQ;AAAA,YACvB;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,sEAA4D,KAAK;AAAA,IAChF;AACA,WAAO;AAAA,EACT;AACF;;;AD5zBA,SAAS,eAAe,uBAAuB;AAC/C,SAAS,uBAAuB;AAChC,SAAS,YAAY;;;AENrB,SAAS,UAAAC,eAAc;AAGhB,IAAM,kBAAN,MAAsB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3B,OAAO,KAAK,YAAgC;AAC1C,UAAM,UAAUA,QAAO,SAAS,gBAAgB,EAAE;AAAA,MAChD;AAAA,QACE;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,MACb;AAAA,IACF;AAEA,WAAOA,QAAO,UAAU,OAAO;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,OAAO,YAAwB,cAA+B;AACnE,UAAM,aAAa,KAAK,KAAK,UAAU;AACvC,WAAO,eAAe;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,OAAO,YAAgC;AAC5C,WAAOA,QAAO,SAAS,gBAAgB,EAAE;AAAA,MACvC;AAAA,QACE;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,QACX,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,UAAU,OAAY,eAAmC;AAC9D,WAAO;AAAA,MACL,WAAW,MAAM;AAAA,MACjB,gBAAgB,MAAM;AAAA,MACtB,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,YAAY,MAAM;AAAA,MAClB,kBAAkB,MAAM,oBAAoB;AAAA,MAC5C,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MACjB;AAAA;AAAA,MACA,UAAU,MAAM,YAAYA,QAAO;AAAA,MACnC,aAAa,MAAM;AAAA,IACrB;AAAA,EACF;AACF;;;ACtGA,SAAS,oBAAoB;AAC7B,SAAS,UAAAC,eAAc;AAGhB,IAAM,eAAN,MAAmB;AAAA;AAAA;AAAA;AAAA,EAIxB,OAAO,aAAa,YAA0C;AAC5D,QAAI;AACF,YAAM,aAAa,aAAa,YAAY,OAAO;AACnD,YAAM,SAAS,KAAK,MAAM,UAAU;AAGpC,UAAI,CAAC,OAAO,SAAS,CAAC,OAAO,MAAM,SAAS;AAC1C,cAAM,IAAI,MAAM,qDAAqD;AAAA,MACvE;AAEA,UAAI,CAAC,OAAO,MAAM,QAAQ;AACxB,cAAM,IAAI,MAAM,2BAA2B;AAAA,MAC7C;AAEA,UAAI,CAAC,OAAO,MAAM,eAAe;AAC/B,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAC9C;AAEA,UAAI,CAAC,OAAO,cAAc,OAAO,WAAW,WAAW,GAAG;AACxD,gBAAQ,KAAK,uDAA6C;AAAA,MAC5D;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAK,MAAgC,SAAS,UAAU;AACtD,cAAM,IAAI,MAAM,0BAA0B,UAAU,EAAE;AAAA,MACxD;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,0BACL,iBACmB;AAEnB,UAAM,CAAC,WAAW,GAAG,IAAI,gBAAgB,MAAM,SAAS,GAAG,IACvD,gBAAgB,MAAM,MAAM,GAAG,IAC/B,CAAC,gBAAgB,OAAO,QAAQ;AAGpC,UAAM,OAAO,UAAU,MAAM,GAAG,EAAE,IAAI,KAAK,gBAAgB;AAG3D,QAAI;AACJ,QAAI,gBAAgB,kBAAkB;AACpC,YAAM,YAAY,OAAO,OAAO,gBAAgB,gBAAgB,EAAE,CAAC,GAAG,SAAS,KAAK;AACpF,iBAAW;AAAA,QACT;AAAA,QACA,MAAM;AAAA,QACN,KAAK;AAAA,MACP;AAAA,IACF;AAEA,WAAO;AAAA,MACL,IAAI,gBAAgB;AAAA,MACpB;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA,MAAM,gBAAgB;AAAA,MACtB,KAAK,gBAAgB;AAAA,MACrB,UAAU,CAAC,CAAC,gBAAgB;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,wBAAwB,QAA8D;AAC3F,UAAM,gBAAgB,oBAAI,IAA+B;AAEzD,eAAW,mBAAmB,OAAO,YAAY;AAC/C,YAAM,WAAW,KAAK,0BAA0B,eAAe;AAG/D,YAAM,kBAAkBA,QAAO;AAAA,QAC7BA,QAAO,SAAS,gBAAgB,EAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,gBAAgB,EAAE,CAAC;AAAA,MAC3E;AACA,oBAAc,IAAI,iBAAiB,QAAQ;AAAA,IAC7C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,mBACL,QACA,aACmD;AACnD,WAAO,OAAO,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,WAAW;AAAA,EAC3D;AACF;;;AHGO,IAAM,iBAAN,MAAM,gBAAe;AAAA,EAyB1B,YAAoB,SAAgC;AAAhC;AAZpB,SAAQ,YAAY;AACpB,SAAQ,qBAAqB,oBAAI,IAAY;AAY3C,SAAK,SAAS,QAAQ;AACtB,SAAK,WAAW,IAAIC,QAAO,gBAAgB,QAAQ,OAAO,MAAM;AAChE,UAAM,WAAW,KAAK;AAGtB,UAAM,YAAY,QAAQ,aAAa,KAAK;AAC5C,UAAM,iBAAiB,QAAQ,kBAAkB,KAAK;AAGtD,QAAI,QAAQ,eAAe;AACzB,WAAK,gBAAgB,QAAQ;AAAA,IAC/B,WAAW,QAAQ,OAAO,YAAY;AACpC,WAAK,gBAAgB,IAAI,cAAc,QAAQ,OAAO,YAAY,QAAQ;AAAA,IAC5E,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAEA,SAAK,mBAAmB,IAAI,iBAAiB;AAC7C,SAAK,kBAAkB,IAAI,gBAAgB;AAAA,MACzC,UAAU;AAAA;AAAA,MACV,UAAU;AAAA;AAAA,IACZ,CAAC;AACD,SAAK,eAAe,IAAI,aAAa,QAAQ,QAAQ,WAAW,gBAAgB;AAAA,MAC9E,gBAAgB,QAAQ;AAAA,MACxB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAGD,SAAK,SAAS,IAAIA,QAAO;AAAA,MACvB,QAAQ,OAAO;AAAA,MACf;AAAA,MACA,KAAK;AAAA,IACP;AAEA,SAAK,cAAc,IAAIA,QAAO;AAAA,MAC5B,QAAQ,OAAO;AAAA,MACf;AAAA,MACA,KAAK,cAAc,UAAU;AAAA,IAC/B;AAGA,SAAK,eAAe,QAAQ;AAC5B,SAAK,aAAa,QAAQ;AAG1B,SAAK,YAAY,IAAI;AAAA,MACnB;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,cAAc,WAAW;AAAA,MAC9B;AAAA;AAAA,MACA;AAAA;AAAA,MACA,KAAK;AAAA;AAAA,IACP;AAGA,SAAK,gBAAgB,QAAQ;AAG7B,QAAI,CAAC,KAAK,iBAAiB,CAAC,KAAK,cAAc,KAAK,WAAW,SAAS,IAAI;AAC1E,cAAQ,KAAK,yFAA+E;AAAA,IAC9F;AAGA,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,kBAAkB,QAAQ,mBAAmB;AAGlD,SAAK,mBAAmB,QAAQ,iBAAiB,WAAW;AAC5D,SAAK,6BAA6B,QAAQ,iBAAiB,qBAAqB;AAChF,SAAK,kCAAkC,QAAQ,iBAAiB,0BAA0B;AAG1F,SAAK,wBAAwB,QAAQ,yBAAyB;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,aAAa,WACX,YACA,WACA,gBACyB;AAEzB,UAAM,aAAa,aAAa,aAAa,UAAU;AAGvD,UAAM,eAAe,WAAW,MAAM,OAAO,UAAU;AACvD,UAAM,WAAW,WAAW,MAAM,OAAO,UAAU;AAEnD,QAAI,CAAC,gBAAgB,CAAC,UAAU;AAC9B,YAAM,IAAI,MAAM,yEAAyE;AAAA,IAC3F;AAGA,UAAM,aAAa,aAAa,wBAAwB,UAAU;AAElE,YAAQ,IAAI,oBAAa,WAAW,IAAI,0BAA0B;AAClE,eAAW,CAAC,IAAI,SAAS,KAAK,WAAW,QAAQ,GAAG;AAClD,cAAQ,IAAI,OAAO,EAAE,KAAK,UAAU,KAAK,IAAI,UAAU,OAAO,QAAQ,EAAE;AAAA,IAC1E;AAGA,UAAM,WAAW,IAAIA,QAAO,gBAAgB,WAAW,MAAM,MAAM;AAGnE,UAAM,kBAAkB,IAAI,gBAAgB,cAAc,QAAQ;AAClE,UAAM,gBAAgB,KAAK;AAG3B,UAAM,gBAAgB,MAAM,cAAc,oBAAoB,iBAAiB,QAAQ;AAGvF,UAAM,cAA2B;AAAA,MAC/B,QAAQ,WAAW,MAAM;AAAA,MACzB,UAAU,WAAW,MAAM;AAAA,MAC3B,eAAe,WAAW,MAAM;AAAA,MAChC,oBAAoB,WAAW,MAAM,sBAAsB,WAAW,MAAM;AAAA,MAC5E,iBAAiB,WAAW,MAAM;AAAA,MAClC,iBAAiB,WAAW,MAAM;AAAA,IACpC;AAGA,UAAM,gBAAgB,WAAW,MAAM,OAAO;AAG9C,WAAO,IAAI,gBAAe;AAAA,MACxB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,aAAa,aACX,cACA,UACA,SACyB;AACzB,UAAM,WAAW,IAAIA,QAAO,gBAAgB,QAAQ,OAAO,MAAM;AAGjE,UAAM,kBAAkB,IAAI,gBAAgB,cAAc,QAAQ;AAClE,UAAM,gBAAgB,KAAK;AAG3B,UAAM,gBAAgB,MAAM,cAAc,oBAAoB,iBAAiB,QAAQ;AAGvF,WAAO,IAAI,gBAAe;AAAA,MACxB,GAAG;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAuB;AAC3B,YAAQ,IAAI,6BAA6B;AAGzC,YAAQ,IAAI,yCAAkC;AAC9C,UAAM,KAAK,gBAAgB,KAAK;AAChC,UAAM,QAAQ,KAAK,gBAAgB,SAAS;AAC5C,YAAQ;AAAA,MACN,2BAAsB,MAAM,eAAe,gBAAgB,MAAM,cAAc;AAAA,IACjF;AAGA,UAAM,kBAAkB,MAAM,KAAK,iBAAiB,qBAAqB;AACzE,QAAI,CAAC,iBAAiB;AACpB,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AAGA,UAAM,UAAU,KAAK,cAAc,WAAW;AAC9C,UAAM,UAAU,MAAM,KAAK,cAAc,WAAW;AACpD,YAAQ,IAAI,iBAAiB,OAAO,EAAE;AACtC,YAAQ,IAAI,YAAYA,QAAO,YAAY,OAAO,CAAC,MAAM;AAEzD,QAAI,YAAY,IAAI;AAClB,cAAQ,KAAK,+EAAqE;AAAA,IACpF;AAGA,QAAI,KAAK,cAAc,KAAK,WAAW,OAAO,GAAG;AAC/C,cAAQ,IAAI;AAAA,sBAAkB,KAAK,WAAW,IAAI,gBAAgB;AAClE,YAAM,KAAK,iBAAiB,kBAAkB,KAAK,UAAU;AAAA,IAC/D;AAGA,UAAM,KAAK,aAAa,QAAQ;AAGhC,SAAK,aAAa,GAAG,kBAAkB,OAAO,UAA+B;AAC3E,YAAM,KAAK,cAAc,KAAK;AAAA,IAChC,CAAC;AAGD,UAAM,KAAK,aAAa,MAAM;AAG9B,QAAI;AACF,YAAM,qBAAqB,MAAM,KAAK,YAAY,2BAA2B;AAC7E,UACE,sBACA,uBAAuB,8CACvB;AACA,gBAAQ,IAAI,yCAAoC,kBAAkB,EAAE;AAGpE,aAAK,UAAU,KAAK;AACpB,aAAK,YAAY,IAAI;AAAA,UACnB,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK,cAAc,WAAW;AAAA,UAC9B;AAAA,UACA,KAAK,QAAQ,mBAAmB;AAAA,YAC9B,gBAAgB;AAAA;AAAA,YAChB,cAAc;AAAA;AAAA,YACd,kBAAkB;AAAA;AAAA,UACpB;AAAA,UACA,KAAK;AAAA;AAAA,QACP;AAAA,MACF,OAAO;AACL,gBAAQ,KAAK,kFAAwE;AAAA,MACvF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,gEAAuD,MAAgB,OAAO;AAAA,IAC7F;AAGA,SAAK,UAAU,MAAM;AAGrB,SAAK,UAAU,GAAG,sBAAsB,OAAO,SASzC;AAEJ,UAAI,KAAK,QAAQ,qBAAqB;AACpC,aAAK,QAAQ,oBAAoB;AAAA,UAC/B,gBAAgB,KAAK;AAAA,UACrB,UAAU,KAAK;AAAA,UACf,QAAQ,KAAK;AAAA,UACb,aAAa,KAAK;AAAA,UAClB,SAAS,KAAK,WAAW;AAAA,UACzB,UAAU,KAAK,YAAY;AAAA,UAC3B,SAAS,KAAK,WAAW;AAAA,QAC3B,CAAC;AAAA,MACH;AAEA,UAAI,KAAK,qBAAqB;AAC5B,gBAAQ,IAAI,4FAAqF;AACjG,cAAM,KAAK,cAAc,KAAK,mBAAmB;AAAA,MACnD;AAAA,IACF,CAAC;AAGD,QAAI,KAAK,QAAQ,sBAAsB,KAAK,QAAQ,oBAAoB;AACtE,WAAK,gBAAgB;AAAA,IACvB;AAGA,SAAK,iBAAiB;AAEtB,SAAK,YAAY;AACjB,YAAQ,IAAI,mCAA8B;AAC1C,YAAQ,IAAI,2BAA2B;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAwB;AAC9B,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAAA,IAC/B;AAEA,YAAQ,IAAI,0CAAmC,KAAK,UAAU,yBAAyB,KAAK,kBAAkB,GAAI,GAAG;AAErH,SAAK,aAAa,YAAY,YAAY;AACxC,YAAM,KAAK,eAAe;AAAA,IAC5B,GAAG,KAAK,eAAe;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,QAAI,KAAK,kBAAkB;AACzB,oBAAc,KAAK,gBAAgB;AAAA,IACrC;AAEA,YAAQ,IAAI,+CAAwC,KAAK,wBAAwB,GAAI,GAAG;AAExF,SAAK,mBAAmB,YAAY,YAAY;AAC9C,YAAM,KAAK,mBAAmB;AAAA,IAChC,GAAG,KAAK,qBAAqB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAAoC;AAChD,UAAM,QAAQ,KAAK,gBAAgB,SAAS;AAE5C,QAAI,MAAM,oBAAoB,GAAG;AAC/B,cAAQ,KAAK,kFAAwE;AAErF,UAAI;AACF,cAAM,KAAK,gBAAgB,OAAO;AAClC,cAAM,WAAW,KAAK,gBAAgB,SAAS;AAE/C,YAAI,SAAS,kBAAkB,GAAG;AAChC,kBAAQ,IAAI,6CAAwC,SAAS,eAAe,oBAAoB;AAAA,QAClG,OAAO;AACL,kBAAQ,MAAM,kEAA6D;AAAA,QAC7E;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,+CAA2C,MAAgB,OAAO;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAgC;AAC5C,QAAI,CAAC,KAAK,QAAQ,sBAAsB,CAAC,KAAK,QAAQ,oBAAoB;AACxE;AAAA,IACF;AAEA,UAAM,kBAAkB,KAAK,QAAQ,mBAAmB,KAAK,UAAU;AACvE,QAAI,gBAAgB,WAAW,GAAG;AAChC;AAAA,IACF;AAGA,UAAM,QAAQ,gBAAgB,CAAC;AAG/B,QAAI,KAAK,mBAAmB,IAAI,MAAM,SAAS,GAAG;AAChD;AAAA,IACF;AAEA,YAAQ,IAAI,8BAAuB,MAAM,UAAU,MAAM,GAAG,EAAE,CAAC,gBAAgB,MAAM,aAAa,CAAC,IAAI,KAAK,UAAU,KAAK,gBAAgB,MAAM,aAAa;AAG9J,SAAK,QAAQ,mBAAmB,MAAM,SAAS;AAG/C,UAAM,YAAY,KAAK,qBAAqB,MAAM,WAAW;AAC7D,QAAI,CAAC,WAAW;AACd,cAAQ,IAAI,4BAAkB,MAAM,YAAY,MAAM,GAAG,EAAE,CAAC,yCAAyC;AACrG;AAAA,IACF;AAGA,UAAM,aAAkC;AAAA,MACtC,WAAW,MAAM;AAAA,MACjB,gBAAgB,OAAO,MAAM,cAAc;AAAA,MAC3C,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAClB,WAAW,OAAO,CAAC;AAAA,MACnB,UAAU;AAAA,MACV,eAAe;AAAA,MACf,UAAU;AAAA,MACV,aAAa,KAAK,OAAO;AAAA,MACzB,aAAa;AAAA,IACf;AAGA,QAAI;AACF,YAAM,KAAK,cAAc,UAAU;AAAA,IACrC,SAAS,OAAO;AACd,cAAQ,IAAI,6BAAwB,MAAM,UAAU,MAAM,GAAG,EAAE,CAAC,QAAS,MAAgB,OAAO,EAAE;AAAA,IACpG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,yBAAyB,mBAA2C;AAE1E,UAAM,CAAC,OAAO,GAAG,IAAI,kBAAkB,UAAU,MAAM,GAAG;AAE1D,WAAO;AAAA,MACL,IAAI,kBAAkB;AAAA,MACtB,MAAM,kBAAkB;AAAA,MACxB;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,MAAM,kBAAkB,MAAM,SAAS;AAAA,MACvC,KAAK,kBAAkB;AAAA,MACvB,cAAc,kBAAkB;AAAA,MAChC,UAAU,kBAAkB,WACxB;AAAA,QACE,WAAW,kBAAkB,SAAS;AAAA,QACtC,MAAM,kBAAkB,SAAS;AAAA,QACjC,KAAK,kBAAkB,SAAS;AAAA,MAClC,IACA;AAAA,MACJ,UAAU,kBAAkB;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,qBAAqB,aAAoD;AAE/E,QAAI,KAAK,cAAc;AACrB,YAAM,YAAY,KAAK,aAAa,WAAW;AAC/C,UAAI,UAAW,QAAO;AAAA,IACxB;AAIA,QAAI,KAAK,YAAY;AACnB,YAAM,YAAY,KAAK,WAAW,IAAI,WAAW;AACjD,UAAI,UAAW,QAAO;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,cAAc,OAA2C;AACrE,UAAM,iBAAiB,MAAM,UAAU,MAAM,GAAG,EAAE;AAIlD,UAAM,YAAY,KAAK,qBAAqB,MAAM,WAAW;AAC7D,QAAI,CAAC,WAAW;AAEd;AAAA,IACF;AAGA,QAAI,KAAK,mBAAmB,IAAI,MAAM,SAAS,GAAG;AAChD,cAAQ,IAAI,2BAAiB,cAAc,iDAAiD;AAC5F;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,sBAAsB,KAAK,QAAQ,mBAAmB,MAAM,SAAS,GAAG;AACvF,cAAQ,IAAI,2BAAiB,cAAc,iCAAiC;AAC5E;AAAA,IACF;AAEA,SAAK,mBAAmB,IAAI,MAAM,SAAS;AAE3C,YAAQ,IAAI;AAAA,IAAM,oBAAI,KAAK,GAAE,YAAY,CAAC,qBAAqB,cAAc,KAAK;AAClF,YAAQ,IAAI,qBAAqB,MAAM,cAAc,EAAE;AACvD,YAAQ,IAAI,eAAe,MAAM,QAAQ,EAAE;AAC3C,YAAQ,IAAI,kBAAkB,MAAM,YAAY,MAAM,GAAG,EAAE,CAAC,KAAK;AACjE,YAAQ,IAAI,0BAAmB,UAAU,IAAI,KAAK,UAAU,KAAK,IAAI,UAAU,OAAO,QAAQ,GAAG;AAIjG,QAAI,KAAK,QAAQ,kBAAkB;AACjC,WAAK,QAAQ,iBAAiB;AAAA,QAC5B,WAAW,MAAM;AAAA,QACjB,gBAAgB,OAAO,MAAM,cAAc;AAAA,QAC3C,UAAU,OAAO,MAAM,QAAQ;AAAA,QAC/B,aAAa,MAAM;AAAA,QACnB,YAAY,MAAM;AAAA,QAClB,WAAW,MAAM,UAAU,SAAS;AAAA,QACpC,UAAU,MAAM;AAAA,QAChB,UAAU,MAAM;AAAA,QAChB,eAAe,MAAM;AAAA,QACrB,aAAa,MAAM;AAAA,MACrB,CAAC;AAAA,IACH;AAIA,QAAI;AACF,YAAM,kBAAkB,MAAM,KAAK,OAAO,+BAA+B,MAAM,cAAc;AAC7F,YAAM,gBAAgB,OAAO,MAAM,QAAQ;AAI3C,YAAM,qBAAqB,oBAAoB;AAE/C,UAAI,CAAC,sBAAsB,kBAAkB,gBAAgB,GAAG;AAC9D,gBAAQ,IAAI,yCAA+B,aAAa,cAAc,eAAe,GAAG;AACxF,YAAI,KAAK,QAAQ,kBAAkB;AACjC,eAAK,QAAQ,iBAAiB,MAAM,WAAW,gBAAgB,aAAa,cAAc,eAAe,GAAG;AAAA,QAC9G;AACA,aAAK,mBAAmB,OAAO,MAAM,SAAS;AAC9C;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,yCAA0C,MAAgB,OAAO;AAAA,IAEhF;AAGA,SAAK,UAAU,sBAAsB,OAAO,MAAM,cAAc,GAAG,OAAO,MAAM,QAAQ,CAAC;AAGzF,QAAI;AAEJ,QAAI;AAEF,YAAM,KAAK,gBAAgB,KAAK;AAGhC,UAAI,KAAK,QAAQ,qBAAqB;AACpC,aAAK,QAAQ,oBAAoB,MAAM,SAAS;AAAA,MAClD;AAGA,YAAM,eAAe,MAAM,KAAK,YAAY,gBAAgB,MAAM,SAAS;AAC3E,UAAI,gBAAgB,MAAM,YAAY;AACpC,gBAAQ,IAAI,sCAA4B,YAAY,IAAI,MAAM,UAAU,aAAa;AACrF,YAAI,KAAK,QAAQ,kBAAkB;AACjC,eAAK,QAAQ,iBAAiB,MAAM,WAAW,sBAAsB,YAAY,IAAI,MAAM,UAAU,GAAG;AAAA,QAC1G;AACA,aAAK,mBAAmB,OAAO,MAAM,SAAS;AAC9C;AAAA,MACF;AAMA,YAAM,eAAe,MAAM,KAAK,OAAO,uBAAuB,MAAM,cAAc;AAClF,YAAM,gBAAgB,aAAa;AAEnC,UAAI,CAAC,iBAAiB,kBAAkB,8CAA8C;AACpF,gBAAQ,MAAM,oDAA+C,MAAM,cAAc,EAAE;AACnF,YAAI,KAAK,QAAQ,iBAAiB;AAChC,eAAK,QAAQ,gBAAgB,MAAM,WAAW,2CAA2C,MAAM,cAAc,EAAE;AAAA,QACjH;AACA;AAAA,MACF;AAEA,cAAQ,IAAI,4CAAqC,cAAc,MAAM,GAAG,EAAE,CAAC,KAAK;AAGhF,YAAM,YAAY;AAAA,QAChB;AAAA,MACF;AACA,YAAM,SAAS,IAAIA,QAAO,SAAS,eAAe,WAAW,KAAK,QAAQ;AAC1E,YAAM,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAE9C,UAAI;AACJ,UAAI;AACF,qBAAa,MAAM,OAAO;AAAA,UACxB,MAAM;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA,KAAK,cAAc,WAAW;AAAA,QAChC;AAAA,MACF,SAAS,OAAO;AACd,cAAM,eAAgB,MAAgB,WAAW,OAAO,KAAK;AAC7D,gBAAQ,MAAM,8CAAyC,KAAK;AAC5D,YAAI,KAAK,QAAQ,iBAAiB;AAChC,eAAK,QAAQ,gBAAgB,MAAM,WAAW,yBAAyB,YAAY,EAAE;AAAA,QACvF;AACA;AAAA,MACF;AAGA,YAAM,YAAYA,QAAO,aAAa,UAAU;AAChD,cAAQ;AAAA,QACN,gCAAyB,UAAU,UAAU,GAAG,GAAG,CAAC,GAAG,UAAU,SAAS,MAAM,QAAQ,EAAE;AAAA,MAC5F;AAGA,cAAQ,IAAI,8BAAoB;AAChC,YAAM,SAAS,MAAM,KAAK,iBAAiB;AAAA,QACzC;AAAA,QACA;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AAEA,UAAI,OAAO,aAAa,GAAG;AACzB,gBAAQ,MAAM,sDAAiD,OAAO,QAAQ,EAAE;AAChF,gBAAQ,MAAM,iCAA0B,OAAO,MAAM;AACrD,YAAI,KAAK,QAAQ,iBAAiB;AAChC,eAAK,QAAQ,gBAAgB,MAAM,WAAW,6CAA6C,OAAO,QAAQ,EAAE;AAAA,QAC9G;AACA;AAAA,MACF;AAEA,cAAQ,IAAI,mCAA8B,OAAO,aAAa,IAAI;AAGlE,cAAQ,IAAI,kCAA2B;AAGvC,YAAM,QAAQ;AACd,YAAM,SAAS,OAAO;AACtB,YAAM,QAAQ,MAAM,WAAW,OAAO,SAAS;AAG/C,YAAM,qBAAqB,MAAM;AAGjC,YAAM,aAAyB,gBAAgB,UAAU,OAAO,kBAAkB;AAGlF,YAAM,iBAAiB,gBAAgB,OAAO,UAAU;AAGxD,YAAM,aAAa,KAAK,iBAAiB,KAAK,cAAc,WAAW;AAGvE,YAAM,KAAK,MAAM,KAAK,YAAY;AAAA,QAChC,MAAM;AAAA,QACNA,QAAO,YAAY,KAAK;AAAA,QACxBA,QAAO,YAAY,MAAM;AAAA,QACzBA,QAAO,YAAY,KAAK;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAGA,mBAAa,GAAG;AAChB,cAAQ,IAAI,iCAA0B,GAAG,IAAI,EAAE;AAG/C,YAAM,UAAU,MAAM,GAAG,KAAK;AAE9B,UAAI,QAAQ,WAAW,GAAG;AACxB,gBAAQ,IAAI,iDAA4C,QAAQ,WAAW,GAAG;AAC9E,gBAAQ,IAAI,2BAAoBA,QAAO,YAAY,MAAM,SAAS,CAAC,MAAM;AAGzE,YAAI,KAAK,QAAQ,oBAAoB;AACnC,eAAK,QAAQ,mBAAmB;AAAA,YAC9B,WAAW,MAAM;AAAA,YACjB,gBAAgB,OAAO,MAAM,cAAc;AAAA,YAC3C,UAAU,OAAO,MAAM,QAAQ;AAAA,YAC/B,aAAa,MAAM;AAAA,YACnB,YAAY,MAAM;AAAA,YAClB,WAAW,MAAM,UAAU,SAAS;AAAA,YACpC,UAAU,MAAM;AAAA,YAChB;AAAA,YACA;AAAA,YACA,QAAQ,GAAG;AAAA,YACX,aAAa,QAAQ;AAAA,YACrB,SAAS,QAAQ;AAAA,YACjB,UAAU,QAAQ,YAAY,GAAG,YAAY;AAAA,UAC/C,CAAC;AAAA,QACH;AAAA,MACF,OAAO;AACL,cAAM,IAAI,MAAM,2CAA2C,QAAQ,MAAM,EAAE;AAAA,MAC7E;AAAA,IACF,SAAS,OAAO;AACd,YAAM,eAAgB,MAAgB,WAAW,OAAO,KAAK;AAC7D,YAAM,YAAa,MAAc;AAGjC,UAAI,cAAc,mBAAmB,aAAa,SAAS,6BAA6B,KAAK,aAAa,SAAS,eAAe,GAAG;AACnI,gBAAQ,IAAI,6EAAmE;AAE/E;AAAA,MACF;AAEA,cAAQ,MAAM,sCAAiC,KAAK;AACpD,UAAI,KAAK,QAAQ,iBAAiB;AAChC,aAAK,QAAQ,gBAAgB,MAAM,WAAW,cAAc,UAAU;AAAA,MACxE;AAAA,IACF,UAAE;AAEA,WAAK,mBAAmB,OAAO,MAAM,SAAS;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBAAgB,OAA2C;AACvE,UAAM,WAAW,KAAK,kBAAkB,MAAM,SAAS;AACvD,UAAM,WAAW,MAAM,eAAe,IAAI,MAAO;AACjD,UAAM,QAAQ,KAAK,MAAO,WAAW,aAAc,QAAQ;AAE3D,QAAI,QAAQ,GAAG;AACb,cAAQ;AAAA,QACN,kCAAwB,KAAK,mBAAmB,SAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,MACnF;AACA,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,WAA2B;AACnD,UAAM,OAAOA,QAAO,UAAUA,QAAO,OAAO,CAAC,WAAW,KAAK,cAAc,WAAW,CAAC,CAAC,CAAC;AAGzF,WAAO,SAAS,KAAK,MAAM,GAAG,EAAE,GAAG,EAAE;AAAA,EACvC;AAAA,EAEA,MAAM,OAAsB;AAC1B,YAAQ,IAAI,6BAA6B;AAGzC,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAC7B,WAAK,aAAa;AAAA,IACpB;AAGA,QAAI,KAAK,kBAAkB;AACzB,oBAAc,KAAK,gBAAgB;AACnC,WAAK,mBAAmB;AAAA,IAC1B;AAGA,UAAM,KAAK,aAAa,KAAK;AAG7B,SAAK,UAAU,KAAK;AAGpB,UAAM,KAAK,iBAAiB,QAAQ;AAGpC,UAAM,KAAK,iBAAiB,yBAAyB;AAErD,SAAK,YAAY;AACjB,YAAQ,IAAI,sBAAiB;AAAA,EAC/B;AAAA,EAEA,YAYE;AACA,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,SAAS,KAAK,cAAc,WAAW;AAAA,MACvC,WAAW,KAAK,UAAU,SAAS;AAAA,MACnC,YAAY;AAAA,QACV,cAAc,KAAK,iBAAiB,yBAAyB;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAiC;AAC/B,WAAO,KAAK;AAAA,EACd;AACF;;;AI/uBO,IAAK,gBAAL,kBAAKC,mBAAL;AACL,EAAAA,8BAAA,eAAY,KAAZ;AACA,EAAAA,8BAAA,wBAAqB,KAArB;AACA,EAAAA,8BAAA,wBAAqB,KAArB;AACA,EAAAA,8BAAA,8CAA2C,KAA3C;AACA,EAAAA,8BAAA,uCAAoC,KAApC;AACA,EAAAA,8BAAA,6BAA0B,KAA1B;AANU,SAAAA;AAAA,GAAA;;;AC9KZ,SAAS,UAAAC,eAAc;AAEhB,IAAM,iBAAN,MAAqB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1B,OAAO,KAAK,gBAAwB,UAA0B;AAE5D,UAAM,sBAAsBA,QAAO,aAAaA,QAAO,QAAQ,cAAc,GAAG,CAAC;AACjF,UAAM,gBAAgBA,QAAO,aAAaA,QAAO,QAAQ,QAAQ,GAAG,CAAC;AAErE,UAAM,SAASA,QAAO,OAAO,CAAC,qBAAqB,aAAa,CAAC;AACjE,WAAOA,QAAO,UAAU,MAAM;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,OAAO,WAAmB,gBAAwB,UAA0B;AACjF,WAAO,cAAc,UAAU,MAAM,GAAG,EAAE,CAAC,YAAY,cAAc,cAAc,QAAQ;AAAA,EAC7F;AACF;;;ACbA,SAAS,iBAAAC,gBAAe,mBAAAC,wBAAuB;AAI/C,SAAS,mBAAAC,wBAAuB;","names":["executionTime","err","ethers","EventEmitter","ethers","ethers","ethers","ethers","FulfillResult","ethers","WalletManager","KeystoreManager","RegistryManager"]}
|