{"version":3,"sources":["../src/core/service.ts","../src/sources/registry.ts","../src/sources/local/index.ts","../src/encryption/age.ts","../src/config/paths.ts","../src/utils/prompts.ts","../src/config/loader.ts","../src/config/types.ts","../src/core/validation.ts","../src/utils/banner.ts"],"sourcesContent":["/**\n * Credential Service\n *\n * Main service layer for SecretSage operations.\n * Orchestrates sources, encryption, and config.\n */\n\nimport * as fs from 'fs-extra';\nimport * as path from 'path';\nimport { parse as parseEnv } from 'dotenv';\nimport type { Credential, CredentialMetadata } from './types';\nimport { CredentialSourceRegistry } from '../sources/registry';\nimport { LocalSource } from '../sources/local';\nimport { loadConfig } from '../config/loader';\nimport {\n  getEnvPath,\n  getEnvBackupPath,\n  getGitignorePath,\n  getLocalDir,\n  getGlobalDir,\n  hasLocalVault,\n  hasGlobalVault,\n} from '../config/paths';\n\n/**\n * Main credential service\n */\nexport class CredentialService {\n  private registry: CredentialSourceRegistry;\n  private localSource: LocalSource | null = null;\n  private initialized = false;\n\n  constructor() {\n    this.registry = new CredentialSourceRegistry();\n  }\n\n  /**\n   * Initialize the service with configured sources\n   */\n  async init(options?: { local?: boolean }): Promise<void> {\n    if (this.initialized) return;\n\n    const config = await loadConfig();\n\n    // Create local source based on options or config\n    const useLocal = options?.local ?? config.vault.defaultLocation === 'local';\n\n    this.localSource = new LocalSource({\n      vaultPath: useLocal\n        ? path.join(getLocalDir(), 'vault.json')\n        : path.join(getGlobalDir(), 'vault.json'),\n      identityPath: useLocal\n        ? path.join(getLocalDir(), 'identity.txt')\n        : path.join(getGlobalDir(), 'identity.txt'),\n      recipientPath: useLocal\n        ? path.join(getLocalDir(), 'recipient.txt')\n        : path.join(getGlobalDir(), 'recipient.txt'),\n    });\n\n    this.registry.register(this.localSource);\n    this.initialized = true;\n  }\n\n  /**\n   * Initialize a new vault\n   */\n  async initializeVault(options?: {\n    local?: boolean;\n    customPath?: string;\n    passphrase?: string;\n  }): Promise<{ publicKey: string; vaultDir: string }> {\n    let vaultDir: string;\n\n    if (options?.customPath) {\n      // Use custom path (expand ~ if present)\n      vaultDir = path.resolve(\n        options.customPath.startsWith('~')\n          ? path.join(require('os').homedir(), options.customPath.slice(1))\n          : options.customPath\n      );\n    } else {\n      vaultDir = options?.local ? getLocalDir() : getGlobalDir();\n    }\n\n    this.localSource = new LocalSource({\n      vaultPath: path.join(vaultDir, 'vault.json'),\n      identityPath: path.join(vaultDir, 'identity.txt'),\n      recipientPath: path.join(vaultDir, 'recipient.txt'),\n    });\n\n    await this.localSource.initialize({ passphrase: options?.passphrase });\n    const publicKey = await this.localSource.getPublicKey();\n\n    return { publicKey, vaultDir };\n  }\n\n  /**\n   * Check if a vault exists\n   */\n  async hasVault(): Promise<{ local: boolean; global: boolean }> {\n    return {\n      local: await hasLocalVault(),\n      global: await hasGlobalVault(),\n    };\n  }\n\n  /**\n   * Get the active vault directory path\n   */\n  async getVaultPath(): Promise<string> {\n    // Priority: local vault > custom path > global vault\n    if (await hasLocalVault()) {\n      return getLocalDir();\n    }\n    const config = await loadConfig();\n    if (config.vault.defaultLocation === 'custom' && config.vault.customPath) {\n      return config.vault.customPath;\n    }\n    return getGlobalDir();\n  }\n\n  /**\n   * Add a credential to the vault\n   */\n  async add(name: string, value: string, metadata?: Partial<CredentialMetadata>): Promise<void> {\n    await this.ensureInitialized();\n    await this.registry.set(name, value, metadata);\n  }\n\n  /**\n   * Get a credential\n   */\n  async get(name: string): Promise<Credential | null> {\n    await this.ensureInitialized();\n    return this.registry.get(name);\n  }\n\n  /**\n   * List all credentials\n   */\n  async list(): Promise<CredentialMetadata[]> {\n    await this.ensureInitialized();\n    return this.registry.list();\n  }\n\n  /**\n   * Delete a credential\n   */\n  async delete(name: string): Promise<boolean> {\n    await this.ensureInitialized();\n    return this.registry.delete(name);\n  }\n\n  /**\n   * Grant credentials to .env file\n   */\n  async grant(\n    names: string[],\n    options?: { backup?: boolean; envPath?: string }\n  ): Promise<{ granted: string[]; envPath: string }> {\n    await this.ensureInitialized();\n\n    const envPath = options?.envPath || getEnvPath();\n\n    // Read existing .env\n    let existingEnv: Record<string, string> = {};\n    if (await fs.pathExists(envPath)) {\n      const content = await fs.readFile(envPath, 'utf8');\n      existingEnv = parseEnv(content);\n\n      // Backup if requested (with secure permissions)\n      if (options?.backup !== false) {\n        const backupPath = getEnvBackupPath();\n        const backupContent = await fs.readFile(envPath, 'utf8');\n        await fs.writeFile(backupPath, backupContent, { mode: 0o600 });\n      }\n    }\n\n    // Get requested credentials\n    const granted: string[] = [];\n    for (const name of names) {\n      const cred = await this.registry.get(name);\n      if (cred) {\n        existingEnv[name] = cred.value;\n        granted.push(name);\n      }\n    }\n\n    // Write .env with secure permissions\n    const envContent = this.stringifyEnv(existingEnv);\n    await fs.writeFile(envPath, envContent, { mode: 0o600 });\n\n    return { granted, envPath };\n  }\n\n  /**\n   * Revoke credentials from .env file\n   */\n  async revoke(names: string[]): Promise<{ revoked: string[]; envPath: string }> {\n    const envPath = getEnvPath();\n\n    if (!(await fs.pathExists(envPath))) {\n      return { revoked: [], envPath };\n    }\n\n    const content = await fs.readFile(envPath, 'utf8');\n    const existingEnv = parseEnv(content);\n\n    const revoked: string[] = [];\n    for (const name of names) {\n      if (name in existingEnv) {\n        delete existingEnv[name];\n        revoked.push(name);\n      }\n    }\n\n    // Write updated .env\n    const envContent = this.stringifyEnv(existingEnv);\n    await fs.writeFile(envPath, envContent);\n\n    return { revoked, envPath };\n  }\n\n  /**\n   * Get all credentials (decrypted)\n   */\n  async getAll(): Promise<Credential[]> {\n    await this.ensureInitialized();\n    if (this.localSource) {\n      return this.localSource.getAll();\n    }\n    return [];\n  }\n\n  /**\n   * Get access log for a credential\n   */\n  async getAccessLog(name: string): Promise<{ timestamp: Date; action: 'read' | 'grant' }[]> {\n    await this.ensureInitialized();\n    if (this.localSource) {\n      return this.localSource.getAccessLog(name);\n    }\n    return [];\n  }\n\n  /**\n   * Add .secretsage and .env to .gitignore\n   */\n  async updateGitignore(): Promise<boolean> {\n    const gitignorePath = getGitignorePath();\n    const entriesToAdd = ['.env', '.env.*', '.secretsage/'];\n\n    let content = '';\n    if (await fs.pathExists(gitignorePath)) {\n      content = await fs.readFile(gitignorePath, 'utf8');\n    }\n\n    const lines = content.split('\\n').map((l) => l.trim());\n    const additions: string[] = [];\n\n    for (const entry of entriesToAdd) {\n      if (!lines.includes(entry)) {\n        additions.push(entry);\n      }\n    }\n\n    if (additions.length === 0) {\n      return false; // Nothing to add\n    }\n\n    const newContent =\n      content.trimEnd() + '\\n\\n# SecretSage\\n' + additions.join('\\n') + '\\n';\n    await fs.writeFile(gitignorePath, newContent);\n\n    return true;\n  }\n\n  /**\n   * Ensure service is initialized\n   */\n  private async ensureInitialized(): Promise<void> {\n    if (!this.initialized) {\n      await this.init();\n    }\n  }\n\n  /**\n   * Convert env object to string format\n   */\n  private stringifyEnv(env: Record<string, string>): string {\n    const lines: string[] = [];\n\n    for (const [key, value] of Object.entries(env)) {\n      // Quote values that contain special characters\n      const needsQuotes = /[\\s#\"'\\\\]/.test(value) || value.includes('=');\n      const quotedValue = needsQuotes ? `\"${value.replace(/\"/g, '\\\\\"')}\"` : value;\n      lines.push(`${key}=${quotedValue}`);\n    }\n\n    return lines.join('\\n') + '\\n';\n  }\n}\n\n/**\n * Default singleton instance\n */\nexport const credentialService = new CredentialService();\n","/**\n * Credential Source Registry\n *\n * Manages multiple credential sources with priority-based resolution.\n * Enables plugin architecture for future sources (1Password, Bitwarden, etc.)\n */\n\nimport type { ICredentialSource } from './types';\nimport type { Credential, CredentialMetadata } from '../core/types';\n\n/**\n * Registry for credential sources\n *\n * Sources are resolved in priority order (lowest number = highest priority).\n * This allows fallback behavior when a credential isn't found in the\n * primary source.\n */\nexport class CredentialSourceRegistry {\n  private sources: Map<string, ICredentialSource> = new Map();\n\n  /**\n   * Register a credential source\n   *\n   * @param source - Credential source to register\n   */\n  register(source: ICredentialSource): void {\n    this.sources.set(source.id, source);\n  }\n\n  /**\n   * Unregister a credential source\n   *\n   * @param sourceId - ID of source to remove\n   */\n  unregister(sourceId: string): void {\n    this.sources.delete(sourceId);\n  }\n\n  /**\n   * Get a specific source by ID\n   *\n   * @param sourceId - Source ID\n   * @returns Source if found, undefined otherwise\n   */\n  getSource(sourceId: string): ICredentialSource | undefined {\n    return this.sources.get(sourceId);\n  }\n\n  /**\n   * Get all registered sources\n   */\n  getAllSources(): ICredentialSource[] {\n    return Array.from(this.sources.values());\n  }\n\n  /**\n   * Get available sources in priority order\n   *\n   * @returns Sources that are available and configured\n   */\n  async getAvailableSources(): Promise<ICredentialSource[]> {\n    const available: ICredentialSource[] = [];\n\n    for (const source of this.sources.values()) {\n      if (await source.isAvailable()) {\n        available.push(source);\n      }\n    }\n\n    // Sort by priority (lower number = higher priority)\n    return available.sort((a, b) => a.priority - b.priority);\n  }\n\n  /**\n   * Get a credential from the first available source that has it\n   *\n   * @param name - Credential name\n   * @returns Credential if found, null otherwise\n   */\n  async get(name: string): Promise<Credential | null> {\n    for (const source of await this.getAvailableSources()) {\n      const credential = await source.get(name);\n      if (credential) {\n        return credential;\n      }\n    }\n    return null;\n  }\n\n  /**\n   * List all credentials from all available sources\n   *\n   * @returns Combined list of credential metadata (deduplicated by name)\n   */\n  async list(): Promise<CredentialMetadata[]> {\n    const seen = new Set<string>();\n    const result: CredentialMetadata[] = [];\n\n    for (const source of await this.getAvailableSources()) {\n      const credentials = await source.list();\n      for (const cred of credentials) {\n        if (!seen.has(cred.name)) {\n          seen.add(cred.name);\n          result.push(cred);\n        }\n      }\n    }\n\n    return result;\n  }\n\n  /**\n   * Set a credential in the specified source (or first writable source)\n   *\n   * @param name - Credential name\n   * @param value - Credential value\n   * @param metadata - Optional metadata to store with credential\n   * @param sourceId - Optional specific source to use\n   */\n  async set(\n    name: string,\n    value: string,\n    metadata?: Partial<CredentialMetadata>,\n    sourceId?: string\n  ): Promise<void> {\n    if (sourceId) {\n      const source = this.sources.get(sourceId);\n      if (!source) {\n        throw new Error(`Source '${sourceId}' not found`);\n      }\n      if (!source.set) {\n        throw new Error(`Source '${sourceId}' is read-only`);\n      }\n      await source.set(name, value, metadata);\n      return;\n    }\n\n    // Use first available writable source\n    for (const source of await this.getAvailableSources()) {\n      if (source.set) {\n        await source.set(name, value, metadata);\n        return;\n      }\n    }\n\n    throw new Error('No writable credential source available');\n  }\n\n  /**\n   * Delete a credential from the specified source (or all sources)\n   *\n   * @param name - Credential name\n   * @param sourceId - Optional specific source\n   * @returns true if deleted from any source\n   */\n  async delete(name: string, sourceId?: string): Promise<boolean> {\n    if (sourceId) {\n      const source = this.sources.get(sourceId);\n      if (!source) {\n        throw new Error(`Source '${sourceId}' not found`);\n      }\n      if (!source.delete) {\n        throw new Error(`Source '${sourceId}' doesn't support deletion`);\n      }\n      return source.delete(name);\n    }\n\n    // Delete from all sources\n    let deleted = false;\n    for (const source of await this.getAvailableSources()) {\n      if (source.delete) {\n        const result = await source.delete(name);\n        deleted = deleted || result;\n      }\n    }\n    return deleted;\n  }\n}\n","/**\n * Local Credential Source\n *\n * Stores credentials in a local encrypted vault file.\n * Uses age encryption for secure storage.\n */\n\nimport * as fs from 'fs-extra';\nimport * as path from 'path';\nimport type { ICredentialSource } from '../types';\nimport type { Credential, CredentialMetadata, VaultEntry, RotationEvent, AccessLogEntry } from '../../core/types';\nimport {\n  AgeProvider,\n  encryptIdentityWithPassphrase,\n  decryptIdentityWithPassphrase,\n  isPassphraseProtectedIdentity,\n} from '../../encryption/age';\nimport { getVaultPath, getIdentityPath, getRecipientPath } from '../../config/paths';\nimport { promptPassphraseDecrypt } from '../../utils/prompts';\nimport { loadConfig } from '../../config/loader';\nimport { detectPromptLeak } from '../../core/validation';\n\n/**\n * Module-level cache for decrypted identity\n * Persists across LocalSource instances within same process\n */\ninterface IdentityCache {\n  key: string;\n  expiresAt: number;\n  identityPath: string;  // Track which identity file this was for\n}\n\nlet identityCache: IdentityCache | null = null;\n\n/**\n * Local credential source using age-encrypted vault\n */\nexport class LocalSource implements ICredentialSource {\n  readonly id = 'local';\n  readonly name = 'Local Vault';\n  readonly priority = 1;\n\n  private encryptionProvider: AgeProvider;\n  private vaultPath: string;\n  private identityPath: string;\n  private recipientPath: string;\n\n  constructor(options?: {\n    vaultPath?: string;\n    identityPath?: string;\n    recipientPath?: string;\n  }) {\n    this.encryptionProvider = new AgeProvider();\n    this.vaultPath = options?.vaultPath ?? getVaultPath();\n    this.identityPath = options?.identityPath ?? getIdentityPath();\n    this.recipientPath = options?.recipientPath ?? getRecipientPath();\n  }\n\n  /**\n   * Check if the local vault is available\n   */\n  async isAvailable(): Promise<boolean> {\n    try {\n      // Check if identity file exists\n      const identityExists = await fs.pathExists(this.identityPath);\n      return identityExists;\n    } catch {\n      return false;\n    }\n  }\n\n  /**\n   * Initialize the local vault\n   * Creates identity file and vault directory\n   */\n  async initialize(options?: { passphrase?: string }): Promise<void> {\n    // Generate new keypair\n    const keyPair = await this.encryptionProvider.generateKeyPair();\n\n    // Save identity (private key) - optionally encrypted with passphrase\n    if (options?.passphrase) {\n      const encryptedIdentity = await encryptIdentityWithPassphrase(\n        keyPair.privateKey,\n        options.passphrase\n      );\n      // Write as passphrase-protected format (marker + base64)\n      const content = `# SecretSage passphrase-protected identity\\n# Encrypted with scrypt\\n${encryptedIdentity}\\n`;\n      await fs.ensureDir(path.dirname(this.identityPath));\n      await fs.writeFile(this.identityPath, content, { mode: 0o600 });\n    } else {\n      await this.encryptionProvider.saveIdentity(keyPair.privateKey, this.identityPath);\n    }\n\n    // Save recipient (public key)\n    await this.encryptionProvider.saveRecipient(keyPair.publicKey, this.recipientPath);\n\n    // Create empty vault file\n    await this.writeVault([]);\n  }\n\n  /**\n   * Load identity, prompting for passphrase if protected\n   * Uses caching to avoid repeated passphrase prompts\n   */\n  private async loadIdentity(): Promise<string> {\n    // Check cache first\n    if (identityCache && identityCache.identityPath === this.identityPath) {\n      if (Date.now() < identityCache.expiresAt) {\n        return identityCache.key;\n      }\n      // Cache expired, clear it\n      identityCache = null;\n    }\n\n    const content = await fs.readFile(this.identityPath, 'utf8');\n\n    if (isPassphraseProtectedIdentity(content)) {\n      // Extract the base64 encrypted data (skip comment lines)\n      const lines = content.split('\\n').filter(l => !l.startsWith('#') && l.trim());\n      const encryptedData = lines.join('');\n\n      // Prompt for passphrase\n      const passphrase = await promptPassphraseDecrypt();\n\n      try {\n        const decryptedKey = await decryptIdentityWithPassphrase(encryptedData, passphrase);\n\n        // Cache the decrypted identity\n        const config = await loadConfig();\n        const ttlSeconds = config.encryption?.passphraseCacheTTL ?? 300;  // Default 5 minutes\n        if (ttlSeconds > 0) {\n          identityCache = {\n            key: decryptedKey,\n            expiresAt: Date.now() + (ttlSeconds * 1000),\n            identityPath: this.identityPath,\n          };\n        }\n\n        return decryptedKey;\n      } catch {\n        throw new Error('Invalid passphrase or corrupted identity file');\n      }\n    }\n\n    // Not passphrase protected, load normally\n    return this.encryptionProvider.loadIdentity(this.identityPath);\n  }\n\n  /**\n   * Get a credential by name\n   */\n  async get(name: string, options?: { action?: 'read' | 'grant' }): Promise<Credential | null> {\n    const entries = await this.readVault();\n    const entryIndex = entries.findIndex((e) => e.name === name);\n\n    if (entryIndex < 0) {\n      return null;\n    }\n\n    const entry = entries[entryIndex];\n\n    // Decrypt the value (will prompt for passphrase if needed)\n    const privateKey = await this.loadIdentity();\n    const value = await this.encryptionProvider.decrypt(entry.encryptedValue, privateKey);\n\n    // Log the access\n    await this.logAccess(entries, entryIndex, options?.action || 'read');\n\n    return {\n      name: entry.name,\n      value,\n      source: this.id,\n      metadata: entry.metadata,\n    };\n  }\n\n  /**\n   * Log an access event to the credential's access log\n   */\n  private async logAccess(\n    entries: VaultEntry[],\n    index: number,\n    action: 'read' | 'grant'\n  ): Promise<void> {\n    const entry = entries[index];\n    const accessLog = entry.accessLog || [];\n\n    // Add new entry\n    accessLog.push({\n      timestamp: new Date(),\n      action,\n    });\n\n    // Keep only last 100 entries\n    if (accessLog.length > 100) {\n      accessLog.shift();\n    }\n\n    entries[index].accessLog = accessLog;\n    await this.writeVault(entries);\n  }\n\n  /**\n   * List all credential metadata\n   */\n  async list(): Promise<CredentialMetadata[]> {\n    const entries = await this.readVault();\n    return entries.map((entry) => ({\n      name: entry.name,\n      ...entry.metadata,\n    }));\n  }\n\n  /**\n   * Store a credential\n   */\n  async set(\n    name: string,\n    value: string,\n    metadata?: Partial<CredentialMetadata>\n  ): Promise<void> {\n    // Defense-in-depth: refuse to encrypt a value that has the shape of the\n    // pre-v0.5.0 \"  KEY: value\" display-layer formatter output. Guards\n    // against any future code path that could re-introduce that symptom.\n    const leak = detectPromptLeak(name, value);\n    if (leak.leak) {\n      throw new Error(\n        `Refusing to store value: appears to be a \"  ${name}: ...\" formatted line, ` +\n        `not a raw credential. If this is intentional, strip the prefix at the call site.`\n      );\n    }\n\n    // Read current vault\n    const entries = await this.readVault();\n\n    // Get public key for encryption\n    const publicKey = await this.encryptionProvider.loadRecipient(this.recipientPath);\n\n    // Encrypt the value\n    const encryptedValue = await this.encryptionProvider.encrypt(value, publicKey);\n\n    // Find existing entry or create new\n    const existingIndex = entries.findIndex((e) => e.name === name);\n    const now = new Date();\n    const isRotation = existingIndex >= 0;\n\n    // Build rotation history\n    let rotationHistory: RotationEvent[] = [];\n    if (isRotation) {\n      // Preserve existing history\n      rotationHistory = entries[existingIndex].metadata?.rotationHistory || [];\n      // Add new rotation event\n      rotationHistory.push({\n        timestamp: now,\n        reason: metadata?.rotationHistory?.[0]?.reason,\n      });\n    }\n\n    const newEntry: VaultEntry = {\n      name,\n      encryptedValue,\n      metadata: {\n        name,\n        createdAt: isRotation ? entries[existingIndex].metadata?.createdAt : now,\n        updatedAt: now,\n        ...metadata,\n        rotationHistory: isRotation ? rotationHistory : undefined,\n      },\n    };\n\n    if (existingIndex >= 0) {\n      entries[existingIndex] = newEntry;\n    } else {\n      entries.push(newEntry);\n    }\n\n    // Write updated vault\n    await this.writeVault(entries);\n  }\n\n  /**\n   * Delete a credential\n   */\n  async delete(name: string): Promise<boolean> {\n    const entries = await this.readVault();\n    const index = entries.findIndex((e) => e.name === name);\n\n    if (index < 0) {\n      return false;\n    }\n\n    entries.splice(index, 1);\n    await this.writeVault(entries);\n    return true;\n  }\n\n  /**\n   * Search credentials by pattern\n   */\n  async search(pattern: string): Promise<CredentialMetadata[]> {\n    const entries = await this.readVault();\n    const regex = new RegExp(pattern, 'i');\n\n    return entries\n      .filter((e) => regex.test(e.name))\n      .map((e) => ({\n        name: e.name,\n        ...e.metadata,\n      }));\n  }\n\n  /**\n   * Get all credentials (decrypted)\n   */\n  async getAll(): Promise<Credential[]> {\n    const entries = await this.readVault();\n    const privateKey = await this.loadIdentity();\n\n    const credentials: Credential[] = [];\n\n    for (const entry of entries) {\n      const value = await this.encryptionProvider.decrypt(entry.encryptedValue, privateKey);\n      credentials.push({\n        name: entry.name,\n        value,\n        source: this.id,\n        metadata: entry.metadata,\n      });\n    }\n\n    return credentials;\n  }\n\n  /**\n   * Get the public key (recipient)\n   */\n  async getPublicKey(): Promise<string> {\n    return this.encryptionProvider.loadRecipient(this.recipientPath);\n  }\n\n  /**\n   * Get the access log for a credential\n   */\n  async getAccessLog(name: string): Promise<AccessLogEntry[]> {\n    const entries = await this.readVault();\n    const entry = entries.find((e) => e.name === name);\n    return entry?.accessLog || [];\n  }\n\n  /**\n   * Read vault entries from file\n   */\n  private async readVault(): Promise<VaultEntry[]> {\n    try {\n      const exists = await fs.pathExists(this.vaultPath);\n      if (!exists) {\n        return [];\n      }\n\n      const content = await fs.readFile(this.vaultPath, 'utf8');\n      if (!content.trim()) {\n        return [];\n      }\n\n      return JSON.parse(content) as VaultEntry[];\n    } catch (error) {\n      // If file doesn't exist or is invalid, return empty\n      return [];\n    }\n  }\n\n  /**\n   * Write vault entries to file\n   */\n  private async writeVault(entries: VaultEntry[]): Promise<void> {\n    await fs.ensureDir(path.dirname(this.vaultPath));\n\n    // Write with restrictive permissions\n    const content = JSON.stringify(entries, null, 2);\n    await fs.writeFile(this.vaultPath, content, { mode: 0o600 });\n  }\n}\n","/**\n * Age Encryption Provider\n *\n * Implementation of IEncryptionProvider using the age-encryption npm package.\n * Age is a modern, simple encryption tool: https://age-encryption.org\n *\n * Based on typage: https://github.com/FiloSottile/typage\n */\n\nimport * as age from 'age-encryption';\nimport * as fs from 'fs-extra';\nimport * as path from 'path';\nimport type { IEncryptionProvider, KeyPair } from './types';\n\n/**\n * Age encryption provider\n *\n * Uses age-encryption npm package for X25519 + ChaCha20-Poly1305.\n * Keys are age-native format (age1... for public, AGE-SECRET-KEY-... for private).\n */\nexport class AgeProvider implements IEncryptionProvider {\n  readonly id = 'age';\n  readonly name = 'age encryption';\n\n  /**\n   * Check if age encryption is available\n   * (always true since we use the npm package, not CLI)\n   */\n  async isAvailable(): Promise<boolean> {\n    return true;\n  }\n\n  /**\n   * Generate a new age key pair\n   *\n   * @returns KeyPair with age-format public and private keys\n   */\n  async generateKeyPair(): Promise<KeyPair> {\n    const identity = await age.generateIdentity();\n    const recipient = await age.identityToRecipient(identity);\n\n    return {\n      publicKey: recipient,   // age1...\n      privateKey: identity,   // AGE-SECRET-KEY-...\n    };\n  }\n\n  /**\n   * Encrypt plaintext with recipient's public key\n   *\n   * @param plaintext - Text to encrypt\n   * @param recipient - Public key (age1...)\n   * @returns Base64-encoded ciphertext\n   */\n  async encrypt(plaintext: string, recipient: string): Promise<string> {\n    const encrypter = new age.Encrypter();\n    encrypter.addRecipient(recipient);\n\n    const plaintextBytes = new TextEncoder().encode(plaintext);\n    const ciphertext = await encrypter.encrypt(plaintextBytes);\n\n    // Convert Uint8Array to base64 for storage\n    return this.uint8ArrayToBase64(ciphertext);\n  }\n\n  /**\n   * Decrypt ciphertext with private key\n   *\n   * @param ciphertext - Base64-encoded ciphertext\n   * @param privateKey - Private key (AGE-SECRET-KEY-...)\n   * @returns Decrypted plaintext\n   */\n  async decrypt(ciphertext: string, privateKey: string): Promise<string> {\n    const decrypter = new age.Decrypter();\n    decrypter.addIdentity(privateKey);\n\n    // Convert base64 back to Uint8Array\n    const ciphertextBytes = this.base64ToUint8Array(ciphertext);\n    const plaintext = await decrypter.decrypt(ciphertextBytes, 'text');\n\n    return plaintext as string;\n  }\n\n  /**\n   * Load private key from identity file\n   *\n   * @param identityPath - Path to identity file\n   * @returns Private key string\n   */\n  async loadIdentity(identityPath: string): Promise<string> {\n    const content = await fs.readFile(identityPath, 'utf8');\n\n    // Identity file may have comments, extract the key line\n    const lines = content.split('\\n');\n    for (const line of lines) {\n      const trimmed = line.trim();\n      if (trimmed.startsWith('AGE-SECRET-KEY-')) {\n        return trimmed;\n      }\n    }\n\n    throw new Error(`No valid age identity found in ${identityPath}`);\n  }\n\n  /**\n   * Save private key to identity file\n   *\n   * @param privateKey - Private key to save\n   * @param identityPath - Path to identity file\n   */\n  async saveIdentity(privateKey: string, identityPath: string): Promise<void> {\n    // Ensure directory exists\n    await fs.ensureDir(path.dirname(identityPath));\n\n    // Create identity file with comment header\n    const content = `# created: ${new Date().toISOString()}\n# SecretSage identity file\n# public key: (run 'secretsage config --show' to see)\n${privateKey}\n`;\n\n    // Write with restrictive permissions (owner read/write only)\n    await fs.writeFile(identityPath, content, { mode: 0o600 });\n  }\n\n  /**\n   * Save public key to recipient file\n   *\n   * @param publicKey - Public key to save\n   * @param recipientPath - Path to recipient file\n   */\n  async saveRecipient(publicKey: string, recipientPath: string): Promise<void> {\n    await fs.ensureDir(path.dirname(recipientPath));\n\n    const content = `# SecretSage recipient (public key)\n# Share this key to allow others to encrypt credentials for you\n${publicKey}\n`;\n\n    await fs.writeFile(recipientPath, content, { mode: 0o644 });\n  }\n\n  /**\n   * Load public key from recipient file\n   *\n   * @param recipientPath - Path to recipient file\n   * @returns Public key string\n   */\n  async loadRecipient(recipientPath: string): Promise<string> {\n    const content = await fs.readFile(recipientPath, 'utf8');\n\n    const lines = content.split('\\n');\n    for (const line of lines) {\n      const trimmed = line.trim();\n      if (trimmed.startsWith('age1')) {\n        return trimmed;\n      }\n    }\n\n    throw new Error(`No valid age recipient found in ${recipientPath}`);\n  }\n\n  /**\n   * Convert Uint8Array to base64 string\n   */\n  private uint8ArrayToBase64(bytes: Uint8Array): string {\n    // Use Buffer in Node.js for efficiency\n    return Buffer.from(bytes).toString('base64');\n  }\n\n  /**\n   * Convert base64 string to Uint8Array\n   */\n  private base64ToUint8Array(base64: string): Uint8Array {\n    return new Uint8Array(Buffer.from(base64, 'base64'));\n  }\n}\n\n/**\n * Encrypt the identity (private key) with a passphrase using scrypt\n *\n * @param privateKey - The age identity to protect\n * @param passphrase - User passphrase\n * @returns Base64-encoded encrypted identity\n */\nexport async function encryptIdentityWithPassphrase(\n  privateKey: string,\n  passphrase: string\n): Promise<string> {\n  const encrypter = new age.Encrypter();\n  encrypter.setPassphrase(passphrase);\n  // Use work factor 18 (default) for good security\n  encrypter.setScryptWorkFactor(18);\n\n  const plaintextBytes = new TextEncoder().encode(privateKey);\n  const ciphertext = await encrypter.encrypt(plaintextBytes);\n\n  return Buffer.from(ciphertext).toString('base64');\n}\n\n/**\n * Decrypt the identity (private key) with a passphrase\n *\n * @param encryptedIdentity - Base64-encoded encrypted identity\n * @param passphrase - User passphrase\n * @returns Decrypted age identity\n */\nexport async function decryptIdentityWithPassphrase(\n  encryptedIdentity: string,\n  passphrase: string\n): Promise<string> {\n  const decrypter = new age.Decrypter();\n  decrypter.addPassphrase(passphrase);\n\n  const ciphertextBytes = new Uint8Array(Buffer.from(encryptedIdentity, 'base64'));\n  const plaintext = await decrypter.decrypt(ciphertextBytes, 'text');\n\n  return plaintext as string;\n}\n\n/**\n * Check if an identity file contains a passphrase-protected identity\n *\n * @param content - Content of identity file\n * @returns true if passphrase-protected\n */\nexport function isPassphraseProtectedIdentity(content: string): boolean {\n  // Passphrase-protected identities are stored as base64 of age ciphertext\n  // They will NOT start with AGE-SECRET-KEY-\n  const trimmed = content.trim();\n  // If it doesn't have a raw AGE-SECRET-KEY line, it's probably encrypted\n  const hasRawKey = trimmed.split('\\n').some(line =>\n    line.trim().startsWith('AGE-SECRET-KEY-')\n  );\n  return !hasRawKey && trimmed.length > 0;\n}\n\n/**\n * Default singleton instance\n */\nexport const ageProvider = new AgeProvider();\n","/**\n * Platform-specific path utilities for SecretSage\n *\n * Handles cross-platform paths for:\n * - Global config: ~/.secretsage/\n * - Local config: .secretsage/\n * - Vault files\n * - Identity files\n */\n\nimport * as path from 'path';\nimport * as os from 'os';\nimport * as fs from 'fs-extra';\n\n/**\n * Get the user's home directory\n */\nexport function getHomeDir(): string {\n  return os.homedir();\n}\n\n/**\n * Get the global SecretSage directory\n * ~/.secretsage on Unix, %USERPROFILE%\\.secretsage on Windows\n */\nexport function getGlobalDir(): string {\n  return path.join(getHomeDir(), '.secretsage');\n}\n\n/**\n * Get the local SecretSage directory (in current project)\n */\nexport function getLocalDir(): string {\n  return path.join(process.cwd(), '.secretsage');\n}\n\n/**\n * Get a custom vault directory (user-specified path)\n */\nexport function getCustomDir(customPath: string): string {\n  return expandPath(customPath);\n}\n\n/**\n * Check if a local vault exists in the current directory\n */\nexport async function hasLocalVault(): Promise<boolean> {\n  return fs.pathExists(path.join(getLocalDir(), 'identity.txt'));\n}\n\n/**\n * Check if a global vault exists\n */\nexport async function hasGlobalVault(): Promise<boolean> {\n  return fs.pathExists(path.join(getGlobalDir(), 'identity.txt'));\n}\n\n/**\n * Check if a vault exists at a custom path\n */\nexport async function hasCustomVault(customPath: string): Promise<boolean> {\n  return fs.pathExists(path.join(getCustomDir(customPath), 'identity.txt'));\n}\n\n/**\n * Get the active vault directory (local if exists, otherwise global)\n */\nexport async function getActiveDir(): Promise<string> {\n  if (await hasLocalVault()) {\n    return getLocalDir();\n  }\n  return getGlobalDir();\n}\n\n/**\n * Get the vault file path\n */\nexport function getVaultPath(local?: boolean): string {\n  const dir = local ? getLocalDir() : getGlobalDir();\n  return path.join(dir, 'vault.json');\n}\n\n/**\n * Get the identity (private key) file path\n */\nexport function getIdentityPath(local?: boolean): string {\n  const dir = local ? getLocalDir() : getGlobalDir();\n  return path.join(dir, 'identity.txt');\n}\n\n/**\n * Get the recipient (public key) file path\n */\nexport function getRecipientPath(local?: boolean): string {\n  const dir = local ? getLocalDir() : getGlobalDir();\n  return path.join(dir, 'recipient.txt');\n}\n\n/**\n * Get the config file path\n */\nexport function getConfigPath(local?: boolean): string {\n  const dir = local ? getLocalDir() : getGlobalDir();\n  return path.join(dir, 'config.yaml');\n}\n\n/**\n * Get the .env file path in the current directory\n */\nexport function getEnvPath(): string {\n  return path.join(process.cwd(), '.env');\n}\n\n/**\n * Get the .env backup file path\n */\nexport function getEnvBackupPath(): string {\n  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n  return path.join(process.cwd(), `.env.backup.${timestamp}`);\n}\n\n/**\n * Get the .gitignore path in the current directory\n */\nexport function getGitignorePath(): string {\n  return path.join(process.cwd(), '.gitignore');\n}\n\n/**\n * Resolve a path with ~ expansion\n */\nexport function expandPath(p: string): string {\n  if (p.startsWith('~')) {\n    return path.join(getHomeDir(), p.slice(1));\n  }\n  return path.resolve(p);\n}\n","import inquirer from 'inquirer';\nimport chalk from 'chalk';\n\n/**\n * Inquirer prompt wrappers for consistent UX\n */\n\n/**\n * Prompt for a credential value with masked input\n */\nexport async function promptCredentialValue(name: string): Promise<string> {\n  const { value } = await inquirer.prompt([\n    {\n      type: 'password',\n      name: 'value',\n      message: `Enter value for ${chalk.cyan(name)}:`,\n      mask: '*',\n      validate: (input: string) => {\n        if (!input || input.trim().length === 0) {\n          return 'Value cannot be empty';\n        }\n        return true;\n      },\n    },\n  ]);\n  return value;\n}\n\n/**\n * Prompt for confirmation\n */\nexport async function confirm(message: string, defaultValue = false): Promise<boolean> {\n  const { confirmed } = await inquirer.prompt([\n    {\n      type: 'confirm',\n      name: 'confirmed',\n      message,\n      default: defaultValue,\n    },\n  ]);\n  return confirmed;\n}\n\n/**\n * Prompt for credential selection with checkboxes\n */\nexport async function selectCredentials(\n  credentials: string[],\n  message = 'Select credentials to grant:'\n): Promise<string[]> {\n  if (credentials.length === 0) {\n    return [];\n  }\n\n  const { selected } = await inquirer.prompt([\n    {\n      type: 'checkbox',\n      name: 'selected',\n      message,\n      choices: credentials.map((name) => ({\n        name,\n        value: name,\n        checked: false,\n      })),\n      validate: (answer: string[]) => {\n        if (answer.length === 0) {\n          return 'You must select at least one credential';\n        }\n        return true;\n      },\n    },\n  ]);\n  return selected;\n}\n\n/**\n * Prompt for vault location choice\n */\nexport async function promptVaultLocation(): Promise<'global' | 'local' | 'custom'> {\n  const { location } = await inquirer.prompt([\n    {\n      type: 'list',\n      name: 'location',\n      message: 'Where should the vault be created?',\n      choices: [\n        {\n          name: 'Global (~/.secretsage) - Shared across projects',\n          value: 'global',\n        },\n        {\n          name: 'Local (.secretsage) - Project-specific',\n          value: 'local',\n        },\n        {\n          name: 'Custom path - Specify a directory',\n          value: 'custom',\n        },\n      ],\n      default: 'global',\n    },\n  ]);\n  return location;\n}\n\n/**\n * Prompt for custom vault path\n */\nexport async function promptCustomPath(): Promise<string> {\n  const { customPath } = await inquirer.prompt([\n    {\n      type: 'input',\n      name: 'customPath',\n      message: 'Enter vault directory path:',\n      validate: (input: string) => {\n        if (!input || input.trim().length === 0) {\n          return 'Path cannot be empty';\n        }\n        return true;\n      },\n    },\n  ]);\n  return customPath.trim();\n}\n\n/**\n * Prompt for text input\n */\nexport async function promptText(\n  message: string,\n  defaultValue?: string\n): Promise<string> {\n  const { value } = await inquirer.prompt([\n    {\n      type: 'input',\n      name: 'value',\n      message,\n      default: defaultValue,\n    },\n  ]);\n  return value;\n}\n\n/**\n * Prompt for passphrase with confirmation\n */\nexport async function promptPassphrase(requireConfirm = true): Promise<string> {\n  const { passphrase } = await inquirer.prompt([\n    {\n      type: 'password',\n      name: 'passphrase',\n      message: 'Enter passphrase for identity file:',\n      mask: '*',\n      validate: (input: string) => {\n        if (!input || input.length < 8) {\n          return 'Passphrase must be at least 8 characters';\n        }\n        return true;\n      },\n    },\n  ]);\n\n  if (requireConfirm) {\n    const { confirm } = await inquirer.prompt([\n      {\n        type: 'password',\n        name: 'confirm',\n        message: 'Confirm passphrase:',\n        mask: '*',\n        validate: (input: string) => {\n          if (input !== passphrase) {\n            return 'Passphrases do not match';\n          }\n          return true;\n        },\n      },\n    ]);\n  }\n\n  return passphrase;\n}\n\n/**\n * Prompt for passphrase (no confirmation, for decryption)\n */\nexport async function promptPassphraseDecrypt(): Promise<string> {\n  const { passphrase } = await inquirer.prompt([\n    {\n      type: 'password',\n      name: 'passphrase',\n      message: 'Enter identity passphrase:',\n      mask: '*',\n    },\n  ]);\n  return passphrase;\n}\n","/**\n * Configuration loader for SecretSage\n *\n * Handles loading and merging global and local configs.\n */\n\nimport * as fs from 'fs-extra';\nimport * as yaml from 'yaml';\nimport { getConfigPath, getGlobalDir, getLocalDir, hasLocalVault } from './paths';\nimport type { SecretSageConfig } from './types';\nimport { getDefaultConfig } from './types';\n\n/**\n * Load configuration from file\n *\n * Priority:\n * 1. Local config (.secretsage/config.yaml)\n * 2. Global config (~/.secretsage/config.yaml)\n * 3. Default config\n */\nexport async function loadConfig(): Promise<SecretSageConfig> {\n  const defaultConfig = getDefaultConfig();\n\n  // Try to load global config first\n  let globalConfig: Partial<SecretSageConfig> = {};\n  const globalConfigPath = getConfigPath(false);\n\n  if (await fs.pathExists(globalConfigPath)) {\n    try {\n      const content = await fs.readFile(globalConfigPath, 'utf8');\n      globalConfig = yaml.parse(content) || {};\n    } catch {\n      // Ignore parse errors, use defaults\n    }\n  }\n\n  // Try to load local config\n  let localConfig: Partial<SecretSageConfig> = {};\n  const localConfigPath = getConfigPath(true);\n\n  if (await fs.pathExists(localConfigPath)) {\n    try {\n      const content = await fs.readFile(localConfigPath, 'utf8');\n      localConfig = yaml.parse(content) || {};\n    } catch {\n      // Ignore parse errors, use defaults\n    }\n  }\n\n  // Merge configs: default < global < local\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  return deepMerge(defaultConfig as any, globalConfig as any, localConfig as any) as unknown as SecretSageConfig;\n}\n\n/**\n * Save configuration to file\n */\nexport async function saveConfig(\n  config: Partial<SecretSageConfig>,\n  local = false\n): Promise<void> {\n  const configPath = getConfigPath(local);\n  const dir = local ? getLocalDir() : getGlobalDir();\n\n  await fs.ensureDir(dir);\n\n  const content = yaml.stringify(config, {\n    indent: 2,\n    lineWidth: 80,\n  });\n\n  // Add header comment\n  const header = `# SecretSage Configuration\n# https://cyclecore.ai\n#\n# Run 'secretsage config --show' to see all options\n`;\n\n  await fs.writeFile(configPath, header + content, { mode: 0o600 });\n}\n\n/**\n * Get the active config path (local if exists, otherwise global)\n */\nexport async function getActiveConfigPath(): Promise<string> {\n  if (await hasLocalVault()) {\n    return getConfigPath(true);\n  }\n  return getConfigPath(false);\n}\n\n/**\n * Check if local config exists\n */\nexport async function hasLocalConfig(): Promise<boolean> {\n  return fs.pathExists(getConfigPath(true));\n}\n\n/**\n * Check if global config exists\n */\nexport async function hasGlobalConfig(): Promise<boolean> {\n  return fs.pathExists(getConfigPath(false));\n}\n\n/**\n * Deep merge objects\n */\nfunction deepMerge(\n  target: Record<string, unknown>,\n  ...sources: Record<string, unknown>[]\n): Record<string, unknown> {\n  const result = { ...target };\n\n  for (const source of sources) {\n    if (!source) continue;\n\n    for (const key in source) {\n      const sourceValue = source[key];\n      const targetValue = result[key];\n\n      if (\n        sourceValue !== null &&\n        typeof sourceValue === 'object' &&\n        !Array.isArray(sourceValue) &&\n        targetValue !== null &&\n        typeof targetValue === 'object' &&\n        !Array.isArray(targetValue)\n      ) {\n        // Recursively merge objects\n        result[key] = deepMerge(\n          targetValue as Record<string, unknown>,\n          sourceValue as Record<string, unknown>\n        );\n      } else if (sourceValue !== undefined) {\n        // Override with source value\n        result[key] = sourceValue;\n      }\n    }\n  }\n\n  return result;\n}\n","/**\n * Configuration types for SecretSage\n */\n\n/**\n * Main configuration schema\n */\nexport interface SecretSageConfig {\n  /** Config file version */\n  version: string;\n\n  /** Vault settings */\n  vault: VaultConfig;\n\n  /** Encryption settings */\n  encryption: EncryptionConfig;\n\n  /** Credential source settings */\n  sources: SourceConfig[];\n\n  /** Agent/automation settings */\n  agent: AgentConfig;\n}\n\n/**\n * Vault configuration\n */\nexport interface VaultConfig {\n  /** Default vault location: 'global', 'local', or 'custom' */\n  defaultLocation: 'global' | 'local' | 'custom';\n\n  /** Custom global vault path (default: ~/.secretsage) */\n  globalPath?: string;\n\n  /** Custom local vault path (default: .secretsage) */\n  localPath?: string;\n\n  /** Custom vault path (user-specified arbitrary directory) */\n  customPath?: string;\n}\n\n/**\n * Encryption configuration\n */\nexport interface EncryptionConfig {\n  /** Encryption provider: 'age' (default) */\n  provider: 'age';\n\n  /** Path to identity file (overrides default) */\n  identityPath?: string;\n\n  /** Path to recipient file (overrides default) */\n  recipientPath?: string;\n\n  /** Enable passphrase protection on identity file (scrypt) */\n  passphrase?: boolean;\n\n  /** Passphrase cache TTL in seconds (default: 300, 0 to disable) */\n  passphraseCacheTTL?: number;\n}\n\n/**\n * Credential source configuration\n */\nexport interface SourceConfig {\n  /** Source type: 'local', '1password', 'bitwarden', etc. */\n  type: string;\n\n  /** Whether this source is enabled */\n  enabled: boolean;\n\n  /** Priority for resolution (lower = higher priority) */\n  priority: number;\n\n  /** Source-specific options */\n  options?: Record<string, unknown>;\n}\n\n/**\n * Agent/automation configuration\n */\nexport interface AgentConfig {\n  /** Automatically add .secretsage and .env to .gitignore */\n  autoGitignore: boolean;\n\n  /** Backup .env before granting credentials */\n  backupEnvOnGrant: boolean;\n\n  /** Require confirmation for grant operations */\n  requireConfirmation: boolean;\n}\n\n/**\n * Default configuration\n */\nexport function getDefaultConfig(): SecretSageConfig {\n  return {\n    version: '1',\n    vault: {\n      defaultLocation: 'global',\n    },\n    encryption: {\n      provider: 'age',\n    },\n    sources: [\n      {\n        type: 'local',\n        enabled: true,\n        priority: 1,\n      },\n    ],\n    agent: {\n      autoGitignore: true,\n      backupEnvOnGrant: true,\n      requireConfirmation: true,\n    },\n  };\n}\n","/**\n * Credential Validation Primitives\n *\n * v0.5.0 ships the storage-layer prompt-leak guard only. Full key-shape\n * validation (GitHub PAT family, Slack, GitLab, NPM, etc.) and the\n * --force-shape escape hatch are scoped for v0.5.1.\n *\n * The existing wizard-side regex table at src/wizard/validation.ts is\n * unchanged for v0.5.0. v0.5.1 will move it here and wire it through\n * all capture paths.\n */\n\n/**\n * Escape a string for use as a literal inside a RegExp.\n */\nfunction escapeRegExp(s: string): string {\n  return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Detect whether a value has the shape of the pre-v0.5.0 keyValue()\n * formatter output for the given key name: leading whitespace (0-4 spaces),\n * the key name, a colon, optional whitespace, then the actual value.\n *\n * Returns the stripped value alongside the leak flag so callers can choose\n * to reject (recommended; storage layer does this) or repair.\n *\n * Example match:\n *   \"  GH_PAT_TEST: ghp_AAAA...\" -> { leak: true, stripped: \"ghp_AAAA...\" }\n */\nexport function detectPromptLeak(\n  name: string,\n  value: string\n): { leak: boolean; stripped: string } {\n  const re = new RegExp(`^\\\\s{0,4}${escapeRegExp(name)}\\\\s*:\\\\s+`);\n  if (re.test(value)) {\n    return { leak: true, stripped: value.replace(re, '') };\n  }\n  return { leak: false, stripped: value };\n}\n","import chalk from 'chalk';\n\n/**\n * ASCII art banner for SecretSage\n * Displayed when running `secretsage` or `secretsage --help`\n */\nexport const BANNER = `\n${chalk.magenta('   ___                      _    ___')}\n${chalk.magenta('  / __|  ___  __  _ _  ___ | |_ / __|  __ _   __ _   ___')}\n${chalk.magenta('  \\\\__ \\\\ / -_)/ _|| \\'_|/ -_)|  _|\\\\__ \\\\ / _` | / _` | / -_)')}\n${chalk.magenta('  |___/ \\\\___|\\\\___|_|  \\\\___| \\\\__||___/ \\\\__,_| \\\\__, | \\\\___|')}\n${chalk.magenta('                                             |___/')}\n\n${chalk.gray('  @cyclecore/secretsage')} ${chalk.dim('v0.5.0')}\n${chalk.gray('  The missing OAuth for LLM agents')}\n`;\n\n/**\n * Compact banner for use in command output\n */\nexport const COMPACT_BANNER = `${chalk.magenta('SecretSage')} ${chalk.dim('v0.5.0')}`;\n\n/**\n * Print the full banner to console\n */\nexport function printBanner(): void {\n  console.log(BANNER);\n}\n"],"mappings":"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","names":["fs","path","fs","path","path","fs","fs","chalk"]}