{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/config/manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Configuration Manager - Handles Piagnet configuration loading, validation, and saving\n */\n\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport * as yaml from \"yaml\";\nimport {\n\ttype AgentDefinition,\n\ttype ChannelBinding,\n\ttype ChannelConfig,\n\tdefaultConfig,\n\ttype PiagentConfig,\n\ttype PiagnetConfig,\n\tpiagnetConfigSchema,\n} from \"./schema.js\";\n\n/** Configuration manager options */\nexport interface ConfigManagerOptions {\n\t/** Configuration directory */\n\tconfigDir?: string;\n\t/** Configuration file name */\n\tconfigFile?: string;\n}\n\n/** Configuration manager */\nexport class ConfigManager {\n\tprivate configDir: string;\n\tprivate configFile: string;\n\tprivate config: PiagnetConfig;\n\tprivate loaded = false;\n\n\tconstructor(options: ConfigManagerOptions = {}) {\n\t\tthis.configDir = options.configDir || this.getDefaultConfigDir();\n\t\tthis.configFile = options.configFile || \"piagnet.yaml\";\n\t\tthis.config = { ...defaultConfig };\n\t}\n\n\t/** Get default configuration directory */\n\tprivate getDefaultConfigDir(): string {\n\t\treturn path.join(os.homedir(), \".piagnet\");\n\t}\n\n\t/** Expand tilde in paths */\n\tprivate expandPath(filePath: string): string {\n\t\tif (filePath.startsWith(\"~/\")) {\n\t\t\treturn path.join(os.homedir(), filePath.slice(2));\n\t\t}\n\t\treturn filePath;\n\t}\n\n\t/** Get full configuration file path */\n\tgetConfigPath(): string {\n\t\treturn path.join(this.configDir, this.configFile);\n\t}\n\n\t/** Ensure configuration directory exists */\n\tasync ensureConfigDir(): Promise<void> {\n\t\tawait fs.mkdir(this.configDir, { recursive: true });\n\t}\n\n\t/** Load configuration from file */\n\tasync load(): Promise<PiagnetConfig> {\n\t\tconst configPath = this.getConfigPath();\n\n\t\ttry {\n\t\t\tconst content = await fs.readFile(configPath, \"utf-8\");\n\t\t\tconst parsed = yaml.parse(content);\n\n\t\t\t// Validate and merge with defaults\n\t\t\tconst result = piagnetConfigSchema.safeParse({\n\t\t\t\t...defaultConfig,\n\t\t\t\t...parsed,\n\t\t\t});\n\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(`Invalid configuration: ${result.error.message}`);\n\t\t\t}\n\n\t\t\tthis.config = result.data;\n\n\t\t\t// Expand paths\n\t\t\tthis.config.gateway.sessionDir = this.expandPath(this.config.gateway.sessionDir);\n\t\t\tthis.config.gateway.publicDir = this.expandPath(this.config.gateway.publicDir);\n\n\t\t\tthis.loaded = true;\n\t\t\treturn this.config;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\t// Config doesn't exist, use defaults through schema to get computed values\n\t\t\t\tconst result = piagnetConfigSchema.safeParse(defaultConfig);\n\t\t\t\tif (result.success) {\n\t\t\t\t\tthis.config = result.data;\n\t\t\t\t} else {\n\t\t\t\t\tthis.config = { ...defaultConfig };\n\t\t\t\t}\n\t\t\t\t// Expand paths\n\t\t\t\tthis.config.gateway.sessionDir = this.expandPath(this.config.gateway.sessionDir);\n\t\t\t\tthis.config.gateway.publicDir = this.expandPath(this.config.gateway.publicDir);\n\t\t\t\tthis.loaded = true;\n\t\t\t\treturn this.config;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/** Save configuration to file */\n\tasync save(): Promise<void> {\n\t\tawait this.ensureConfigDir();\n\t\tconst configPath = this.getConfigPath();\n\n\t\tconst configToSave = {\n\t\t\t...this.config,\n\t\t\tgateway: {\n\t\t\t\t...this.config.gateway,\n\t\t\t\t// Convert expanded paths back to relative\n\t\t\t\tsessionDir: this.config.gateway.sessionDir.replace(os.homedir(), \"~\"),\n\t\t\t\tpublicDir: this.config.gateway.publicDir.replace(os.homedir(), \"~\"),\n\t\t\t},\n\t\t};\n\n\t\tconst content = yaml.stringify(configToSave, {\n\t\t\tindent: 2,\n\t\t\tlineWidth: 100,\n\t\t});\n\n\t\tawait fs.writeFile(configPath, content, \"utf-8\");\n\t}\n\n\t/** Get current configuration */\n\tgetConfig(): PiagnetConfig {\n\t\tif (!this.loaded) {\n\t\t\tthrow new Error(\"Configuration not loaded. Call load() first.\");\n\t\t}\n\t\treturn this.config;\n\t}\n\n\t/** Update configuration */\n\tupdateConfig(updates: Partial<PiagnetConfig>): void {\n\t\tthis.config = { ...this.config, ...updates };\n\t}\n\n\t/** Get gateway configuration */\n\tgetGatewayConfig(): PiagnetConfig[\"gateway\"] {\n\t\treturn this.config.gateway;\n\t}\n\n\t/** Get agent by ID */\n\tgetAgent(id: string): AgentDefinition | undefined {\n\t\treturn this.config.agents.find((a) => a.id === id);\n\t}\n\n\t/** Get default agent */\n\tgetDefaultAgent(): AgentDefinition | undefined {\n\t\tif (this.config.defaults.agent) {\n\t\t\treturn this.getAgent(this.config.defaults.agent);\n\t\t}\n\t\treturn this.config.agents[0];\n\t}\n\n\t/** Add or update agent */\n\tupsertAgent(agent: AgentDefinition): void {\n\t\tconst index = this.config.agents.findIndex((a) => a.id === agent.id);\n\t\tif (index >= 0) {\n\t\t\tthis.config.agents[index] = agent;\n\t\t} else {\n\t\t\tthis.config.agents.push(agent);\n\t\t}\n\t}\n\n\t/** Remove agent */\n\tremoveAgent(id: string): boolean {\n\t\tconst index = this.config.agents.findIndex((a) => a.id === id);\n\t\tif (index >= 0) {\n\t\t\tthis.config.agents.splice(index, 1);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Get channel by type */\n\tgetChannel(type: string): ChannelConfig | undefined {\n\t\treturn this.config.channels.find((c) => c.type === type);\n\t}\n\n\t/** Get enabled channels */\n\tgetEnabledChannels(): ChannelConfig[] {\n\t\treturn this.config.channels.filter((c) => c.enabled);\n\t}\n\n\t/** Add or update channel */\n\tupsertChannel(channel: ChannelConfig): void {\n\t\tconst index = this.config.channels.findIndex((c) => c.type === channel.type);\n\t\tif (index >= 0) {\n\t\t\tthis.config.channels[index] = channel;\n\t\t} else {\n\t\t\tthis.config.channels.push(channel);\n\t\t}\n\t}\n\n\t/** Remove channel */\n\tremoveChannel(type: string): boolean {\n\t\tconst index = this.config.channels.findIndex((c) => c.type === type);\n\t\tif (index >= 0) {\n\t\t\tthis.config.channels.splice(index, 1);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Get bindings for agent */\n\tgetBindingsForAgent(agentId: string): ChannelBinding[] {\n\t\treturn this.config.bindings.filter((b) => b.agentId === agentId);\n\t}\n\n\t/** Get bindings for channel */\n\tgetBindingsForChannel(channel: string): ChannelBinding[] {\n\t\treturn this.config.bindings.filter((b) => b.channel === channel);\n\t}\n\n\t/** Add binding */\n\taddBinding(binding: ChannelBinding): void {\n\t\t// Remove existing binding for same agent/channel/bindType/bindValue\n\t\tthis.config.bindings = this.config.bindings.filter(\n\t\t\t(b) =>\n\t\t\t\t!(\n\t\t\t\t\tb.agentId === binding.agentId &&\n\t\t\t\t\tb.channel === binding.channel &&\n\t\t\t\t\tb.bindType === binding.bindType &&\n\t\t\t\t\tb.bindValue === binding.bindValue\n\t\t\t\t),\n\t\t);\n\t\tthis.config.bindings.push(binding);\n\t}\n\n\t/** Remove binding */\n\tremoveBinding(agentId: string, channel: string, bindType: string, bindValue: string): boolean {\n\t\tconst index = this.config.bindings.findIndex(\n\t\t\t(b) => b.agentId === agentId && b.channel === channel && b.bindType === bindType && b.bindValue === bindValue,\n\t\t);\n\t\tif (index >= 0) {\n\t\t\tthis.config.bindings.splice(index, 1);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Resolve route for incoming message */\n\tresolveRoute(channel: string, scope: string, identifier: string, guildId?: string): ChannelBinding | undefined {\n\t\tconst bindings = this.config.bindings.filter((b) => b.channel === channel);\n\n\t\t// Priority order: peer > guild > team > account > channel > default\n\t\tconst priority = [\"peer\", \"guild\", \"team\", \"account\", \"channel\", \"default\"];\n\n\t\tfor (const bindType of priority) {\n\t\t\tconst binding = bindings.find((b) => {\n\t\t\t\tif (b.bindType !== bindType) return false;\n\n\t\t\t\tswitch (bindType) {\n\t\t\t\t\tcase \"peer\":\n\t\t\t\t\t\treturn b.bindValue === identifier;\n\t\t\t\t\tcase \"guild\":\n\t\t\t\t\t\treturn b.bindValue === guildId;\n\t\t\t\t\tcase \"channel\":\n\t\t\t\t\t\treturn b.bindValue === \"*\";\n\t\t\t\t\tcase \"default\":\n\t\t\t\t\t\treturn b.bindValue === \"*\";\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn b.bindValue === identifier;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (binding) return binding;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/** Create initial configuration file if it doesn't exist */\n\tasync createInitialConfig(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fs.access(this.getConfigPath());\n\t\t\treturn false; // Already exists\n\t\t} catch {\n\t\t\t// Create with defaults\n\t\t\tawait this.save();\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/** Validate configuration */\n\tvalidate(): { valid: boolean; errors: string[] } {\n\t\tconst result = piagnetConfigSchema.safeParse(this.config);\n\t\tif (result.success) {\n\t\t\treturn { valid: true, errors: [] };\n\t\t}\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terrors: result.error.errors.map((e) => `${e.path.join(\".\")}: ${e.message}`),\n\t\t};\n\t}\n\n\t/** Create config manager instance */\n\tstatic async create(options?: ConfigManagerOptions): Promise<ConfigManager> {\n\t\tconst manager = new ConfigManager(options);\n\t\tawait manager.load();\n\t\treturn manager;\n\t}\n}\n\n/** Create config manager and load configuration */\nexport async function loadConfig(options?: ConfigManagerOptions): Promise<ConfigManager> {\n\tconst manager = new ConfigManager(options);\n\tawait manager.load();\n\treturn manager;\n}\n"]}