{"version":3,"sources":["../src/index.ts","../src/crypto.ts","../src/db/connection.ts","../src/db/migrations.ts","../src/db/config.ts","../src/db/projects.ts","../src/fs/index.ts","../src/scanner/index.ts","../src/server/app.ts","../src/server/dialog.ts","../src/renderers/markdown.ts","../src/renderers/mermaid.ts","../src/renderers/pdf.ts","../src/renderers/image.ts","../src/renderers/code.ts","../src/renderers/index.ts","../src/server/sessions.ts","../src/server/mobile-detect.ts","../src/server/vendor.ts"],"sourcesContent":["/**\n * @doc77/core — Doc77 核心引擎\n *\n * 提供数据库、文件系统抽象层、预览引擎和 Express Server。\n */\n\nexport const VERSION = '0.1.0';\n\n// Crypto\nexport {\n  encrypt,\n  decrypt,\n  deriveKey,\n  generateSalt,\n  hashPassword,\n  verifyPassword,\n  checkPasswordStrength,\n  isSensitiveKey,\n  maskSensitive,\n} from './crypto.js';\nexport type { EncryptedData } from './crypto.js';\n\n// Database\nexport {\n  initDatabase,\n  getConnection,\n  closeConnection,\n  DatabaseCompat,\n  StatementCompat,\n} from './db/connection.js';\nexport { runMigrations } from './db/migrations.js';\nexport { getConfig, setConfig, listConfig, loadDefaults } from './db/config.js';\nexport {\n  registerProject,\n  listProjects,\n  removeProject,\n  updateProject,\n} from './db/projects.js';\nexport type { Project, ProjectUpdate } from './db/projects.js';\n\n// File System\nexport {\n  readFile,\n  readFileRaw,\n  isBinaryFile,\n  readFirstNLines,\n  statFile,\n  listDir,\n  isSensitiveFile,\n  validatePath,\n  resolveProjectPath,\n} from './fs/index.js';\nexport type { DirEntry } from './fs/index.js';\n\n// Scanner\nexport { scanDirectory, clearCache } from './scanner/index.js';\nexport type { ScanResult } from './scanner/index.js';\n\n// Server\nexport { createApp, createQueueApproveHandler, createAIChatHandler } from './server/app.js';\n\n// Vendor\nexport { fetchVendorAssets, isVendorReady, VENDOR_ASSETS } from './server/vendor.js';\nexport type { VendorAsset } from './server/vendor.js';\n\n// Renderers\nexport {\n  renderMarkdown,\n  renderMermaid,\n  renderPdf,\n  renderImage,\n  renderCode,\n  getRendererForFile,\n  isUnsupportedFormat,\n  UNSUPPORTED_EXTENSIONS,\n  FORMAT_SIZE_LIMITS,\n} from './renderers/index.js';\n","import * as crypto from 'node:crypto';\n\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 12;\nconst TAG_LENGTH = 16;\nconst KEY_LENGTH = 32; // 256 bits\nconst PBKDF2_ITERATIONS = 600_000;\nconst SCRYPT_KEYLEN = 64;\nconst SALT_LENGTH = 32;\n\nexport interface EncryptedData {\n  iv: string; // hex\n  tag: string; // hex\n  ciphertext: string; // hex\n}\n\n/**\n * Encrypt plaintext with AES-256-GCM.\n */\nexport function encrypt(plaintext: string, key: Buffer): EncryptedData {\n  const iv = crypto.randomBytes(IV_LENGTH);\n  const cipher = crypto.createCipheriv(ALGORITHM, key, iv);\n  const encrypted = Buffer.concat([cipher.update(plaintext, 'utf-8'), cipher.final()]);\n  const tag = cipher.getAuthTag();\n  return {\n    iv: iv.toString('hex'),\n    tag: tag.toString('hex'),\n    ciphertext: encrypted.toString('hex'),\n  };\n}\n\n/**\n * Decrypt AES-256-GCM ciphertext.\n */\nexport function decrypt(data: EncryptedData, key: Buffer): string {\n  const decipher = crypto.createDecipheriv(ALGORITHM, key, Buffer.from(data.iv, 'hex'));\n  decipher.setAuthTag(Buffer.from(data.tag, 'hex'));\n  const decrypted = Buffer.concat([\n    decipher.update(Buffer.from(data.ciphertext, 'hex')),\n    decipher.final(),\n  ]);\n  return decrypted.toString('utf-8');\n}\n\n/**\n * Derive a 256-bit key from password + salt using PBKDF2-SHA512.\n */\nexport function deriveKey(password: string, salt: Buffer): Buffer {\n  return crypto.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, KEY_LENGTH, 'sha512');\n}\n\n/**\n * Generate a cryptographically random salt.\n */\nexport function generateSalt(): Buffer {\n  return crypto.randomBytes(SALT_LENGTH);\n}\n\n/**\n * Hash a password with scrypt for storage/verification.\n * Returns \"salt$hash\" as a single string.\n */\nexport function hashPassword(password: string): string {\n  const salt = crypto.randomBytes(SALT_LENGTH).toString('hex');\n  const hash = crypto.scryptSync(password, salt, SCRYPT_KEYLEN).toString('hex');\n  return `${salt}$${hash}`;\n}\n\n/**\n * Verify a password against a stored scrypt hash.\n */\nexport function verifyPassword(password: string, storedHash: string): boolean {\n  const [salt, hash] = storedHash.split('$');\n  if (!salt || !hash) return false;\n  const derived = crypto.scryptSync(password, salt, SCRYPT_KEYLEN).toString('hex');\n  return crypto.timingSafeEqual(Buffer.from(derived), Buffer.from(hash));\n}\n\n/**\n * Check password strength. Returns {score: 0-4, feedback: string}.\n */\nexport function checkPasswordStrength(password: string): {\n  score: number;\n  feedback: string;\n} {\n  let score = 0;\n  if (password.length >= 8) score++;\n  if (password.length >= 12) score++;\n  if (/[a-z]/.test(password) && /[A-Z]/.test(password)) score++;\n  if (/[0-9]/.test(password)) score++;\n  if (/[^a-zA-Z0-9]/.test(password)) score++;\n\n  const labels = ['非常弱', '弱', '一般', '强', '非常强'];\n  return { score: Math.min(score, 4), feedback: labels[Math.min(score, 4)] };\n}\n\n/** Sensitive config keys whose values should be encrypted at rest. */\nconst SENSITIVE_KEYS = ['ai.token', 'security.shared_secret'];\n\nexport function isSensitiveKey(key: string): boolean {\n  return SENSITIVE_KEYS.includes(key) || key.endsWith('.token') || key.endsWith('.secret');\n}\n\n/**\n * Mask a sensitive value for display (e.g., \"sk-abc...xyz\").\n */\nexport function maskSensitive(value: string): string {\n  if (!value || value.length <= 8) return '••••';\n  return value.slice(0, 4) + '••••' + value.slice(-4);\n}\n","import * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport initSqlJs from 'sql.js';\nimport type { Database as SqlJsDatabase, SqlJsStatic, Statement as SqlJsStatement } from 'sql.js';\n\n/***********************************************\n * sql.js → better-sqlite3 compatibility layer\n *\n * initDatabase() is async (loads WASM once).\n * After init, getConnection() returns a\n * DatabaseCompat with .prepare().get()/.all()/.run()\n * and .transaction() — same API as better-sqlite3.\n ***********************************************/\n\nlet rawDb: SqlJsDatabase | null = null;\nlet dbPath: string | null = null;\nlet wrappedDb: DatabaseCompat | null = null;\n\n// ── Statement wrapper ──────────────────────────────────\n\nexport class StatementCompat {\n  private _db: SqlJsDatabase;\n  private _sql: string;\n\n  constructor(db: SqlJsDatabase, sql: string) {\n    this._db = db;\n    this._sql = sql;\n  }\n\n  run(...params: unknown[]) {\n    const stmt = this._db.prepare(this._sql);\n    if (params.length > 0) stmt.bind(params);\n    stmt.step(); // Don't catch — let constraint violations throw\n    let lastInsertRowid = 0;\n    let changes = 0;\n    if (/^\\s*INSERT\\b/i.test(this._sql.trim())) {\n      const idStmt = this._db.prepare('SELECT last_insert_rowid() as id');\n      if (idStmt.step()) lastInsertRowid = idStmt.getAsObject().id as number;\n      idStmt.free();\n      changes = this._db.getRowsModified();\n    } else if (/^\\s*(UPDATE|DELETE)\\b/i.test(this._sql.trim())) {\n      changes = this._db.getRowsModified();\n    }\n    stmt.free();\n    return { changes, lastInsertRowid };\n  }\n\n  get<T = Record<string, unknown>>(...params: unknown[]): T | undefined {\n    const stmt = this._db.prepare(this._sql);\n    if (params.length > 0) stmt.bind(params);\n    try {\n      if (stmt.step()) {\n        const r = stmt.getAsObject() as T;\n        stmt.free();\n        return r;\n      }\n    } catch { stmt.free(); return undefined; }\n    stmt.free();\n    return undefined;\n  }\n\n  all<T = Record<string, unknown>>(...params: unknown[]): T[] {\n    const stmt = this._db.prepare(this._sql);\n    if (params.length > 0) stmt.bind(params);\n    const results: T[] = [];\n    try { while (stmt.step()) results.push(stmt.getAsObject() as T); } catch {}\n    stmt.free();\n    return results;\n  }\n}\n\n// ── Database wrapper ───────────────────────────────────\n\nexport class DatabaseCompat {\n  private _db: SqlJsDatabase;\n\n  constructor(db: SqlJsDatabase) {\n    this._db = db;\n  }\n\n  get open(): boolean {\n    try { this._db.exec('SELECT 1'); return true; } catch { return false; }\n  }\n\n  exec(sql: string) {\n    this._db.run(sql);\n  }\n\n  prepare(sql: string): StatementCompat {\n    return new StatementCompat(this._db, sql);\n  }\n\n  transaction<TArgs extends unknown[], TResult>(\n    fn: (...args: TArgs) => TResult,\n  ): (...args: TArgs) => TResult {\n    return (...args: TArgs): TResult => {\n      try {\n        this._db.run('BEGIN');\n        const r = fn(...args);\n        this._db.run('COMMIT');\n        return r;\n      } catch (err) {\n        try { this._db.run('ROLLBACK'); } catch {}\n        throw err;\n      }\n    };\n  }\n\n  /** Internal: save to disk and close */\n  _saveAndClose(filePath: string) {\n    const data = this._db.export();\n    const buf = Buffer.from(data);\n    const tmp = filePath + '.tmp';\n    fs.writeFileSync(tmp, buf);\n    fs.renameSync(tmp, filePath);\n    this._db.close();\n  }\n}\n\n// ── Exported API ───────────────────────────────────────\n\nlet sqlModule: SqlJsStatic | null = null;\nlet initPromise: Promise<DatabaseCompat> | null = null;\n\n/**\n * Initialize the database.\n * On first call, loads sql.js WASM (async).\n * Subsequent calls with same path reuse existing connection.\n * Uses a promise guard to prevent race conditions.\n */\nexport async function initDatabase(filePath: string): Promise<DatabaseCompat> {\n  if (rawDb && wrappedDb) return wrappedDb;\n  if (initPromise) return initPromise;\n\n  initPromise = (async () => {\n    if (!sqlModule) {\n      sqlModule = await initSqlJs();\n    }\n\n    const dir = path.dirname(filePath);\n    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n\n    let buffer: Buffer | undefined;\n    if (fs.existsSync(filePath)) buffer = fs.readFileSync(filePath);\n\n    rawDb = new sqlModule.Database(buffer);\n    dbPath = filePath;\n    rawDb.run('PRAGMA foreign_keys = ON');\n    wrappedDb = new DatabaseCompat(rawDb);\n    return wrappedDb;\n  })();\n\n  return initPromise;\n}\n\n/** Get current connection (must call initDatabase first). */\nexport function getConnection(): DatabaseCompat {\n  if (!rawDb || !wrappedDb) {\n    throw new Error('Database not initialized. Call initDatabase() first.');\n  }\n  return wrappedDb;\n}\n\n/** Close and save the database. */\nexport function closeConnection(): void {\n  if (rawDb && dbPath) {\n    try { wrappedDb?._saveAndClose(dbPath); } catch { rawDb.close(); }\n    rawDb = null;\n    wrappedDb = null;\n    dbPath = null;\n    initPromise = null;\n  }\n}\n","import { getConnection, type DatabaseCompat } from './connection.js';\n\n/**\n * Run all schema migrations.\n * Uses IF NOT EXISTS to ensure idempotency.\n */\nexport function runMigrations(db?: DatabaseCompat): void {\n  const conn = db ?? getConnection();\n  conn.exec(SCHEMA_SQL);\n}\n\nconst SCHEMA_SQL = `\n-- 项目表\nCREATE TABLE IF NOT EXISTS projects (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    name TEXT NOT NULL,\n    path TEXT NOT NULL UNIQUE,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    last_opened DATETIME\n);\n\n-- 配置表\nCREATE TABLE IF NOT EXISTS config (\n    key TEXT PRIMARY KEY,\n    value TEXT\n);\n\n-- 文件树缓存表\nCREATE TABLE IF NOT EXISTS filetree_cache (\n    project_id INTEGER NOT NULL,\n    node_path TEXT NOT NULL,\n    tree_json TEXT NOT NULL,\n    scanned_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    mtime_map TEXT,\n    PRIMARY KEY (project_id, node_path),\n    FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE\n);\n\n-- 操作队列表\nCREATE TABLE IF NOT EXISTS operation_queue (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    project_id INTEGER NOT NULL,\n    session_id TEXT NOT NULL,\n    operation_type TEXT NOT NULL,\n    operation_data JSON NOT NULL,\n    status TEXT DEFAULT 'pending',\n    user_comment TEXT,\n    undo_log JSON,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    updated_at DATETIME,\n    executed_at DATETIME,\n    FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE\n);\n\n-- 审计日志表\nCREATE TABLE IF NOT EXISTS audit_log (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    project_id INTEGER NOT NULL,\n    operation_type TEXT NOT NULL,\n    operation_data JSON NOT NULL,\n    source TEXT NOT NULL,\n    approved_by TEXT,\n    status TEXT NOT NULL,\n    error_message TEXT,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    executed_at DATETIME,\n    FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE\n);\n\n-- 会话表\nCREATE TABLE IF NOT EXISTS sessions (\n    id TEXT PRIMARY KEY,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    last_active_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    read_count INTEGER DEFAULT 0,\n    read_window_start DATETIME,\n    write_count INTEGER DEFAULT 0,\n    write_window_start DATETIME,\n    expired_at DATETIME\n);\n\n-- 用户认证表\nCREATE TABLE IF NOT EXISTS user_auth (\n    id INTEGER PRIMARY KEY DEFAULT 1,\n    password_hash TEXT,\n    pbkdf2_salt TEXT,\n    encryption_salt TEXT,\n    failed_attempts INTEGER DEFAULT 0,\n    locked_until DATETIME,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n);\n\n-- 项目锁表\nCREATE TABLE IF NOT EXISTS project_locks (\n    project_id INTEGER PRIMARY KEY,\n    locked_at DATETIME NOT NULL,\n    locked_by TEXT NOT NULL,\n    heartbeat_at DATETIME,\n    FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE\n);\n\n-- 索引\nCREATE INDEX IF NOT EXISTS idx_projects_path ON projects(path);\nCREATE INDEX IF NOT EXISTS idx_projects_name ON projects(name);\nCREATE INDEX IF NOT EXISTS idx_queue_project_id ON operation_queue(project_id);\nCREATE INDEX IF NOT EXISTS idx_queue_status ON operation_queue(status);\nCREATE INDEX IF NOT EXISTS idx_queue_session ON operation_queue(session_id);\nCREATE INDEX IF NOT EXISTS idx_audit_project_id ON audit_log(project_id);\nCREATE INDEX IF NOT EXISTS idx_audit_created_at ON audit_log(created_at);\n`;\n","import { getConnection } from './connection.js';\n\n/**\n * Default configuration values defined in architecture doc §7.3.\n */\nconst DEFAULTS: Record<string, string> = {\n  'ai.enabled': 'false',\n  'ai.auto_mode': 'false',\n  'ai.risk_level': 'medium',\n  'ai.confirm_delete': 'true',\n  'ai.batch_size': '5',\n  'ai.require_approval_types': '[\"delete_file\"]',\n  'ai.max_depth': '5',\n  'ai.read_limit_per_session': '200',\n  'editor.default': 'vscode',\n  'security.follow_symlinks': 'false',\n  'transaction.shadow_dir': '~/.doc77/shadow',\n  'transaction.file_size_threshold_mb': '50',\n  'transaction.rollback_enabled': 'true',\n  'transaction.shadow_gc_enabled': 'true',\n  'transaction.shadow_orphan_age_hours': '24',\n  'concurrency.enable_project_lock': 'true',\n  'concurrency.lock_timeout_minutes': '10',\n  'concurrency.lock_heartbeat_seconds': '30',\n  'security.bind_address': '127.0.0.1',\n  'security.shared_secret': '',\n  'session.idle_timeout_minutes': '120',\n  'session.cleanup_interval_minutes': '60',\n  'rate.write_limit_per_session': '50',\n  'rate.write_window_minutes': '5',\n  'transport.mcp_stdio_enabled': 'true',\n  'transport.mcp_http_enabled': 'true',\n  'transport.mcp_http_port': '8899',\n};\n\n/**\n * Get a config value by key.\n * Returns the raw string value, or undefined if not set.\n */\nexport function getConfig(key: string): string | undefined {\n  const db = getConnection();\n  const row = db.prepare('SELECT value FROM config WHERE key = ?').get(key) as\n    { value: string } | undefined;\n  return row?.value;\n}\n\n/**\n * Set a config value. Inserts or updates as needed.\n */\nexport function setConfig(key: string, value: string): void {\n  const db = getConnection();\n  db.prepare(\n    `INSERT INTO config (key, value) VALUES (?, ?)\n     ON CONFLICT(key) DO UPDATE SET value = excluded.value`,\n  ).run(key, value);\n}\n\n/**\n * List all config entries as a plain key-value object.\n */\nexport function listConfig(): Record<string, string> {\n  const db = getConnection();\n  const rows = db.prepare('SELECT key, value FROM config ORDER BY key').all() as {\n    key: string;\n    value: string;\n  }[];\n  const result: Record<string, string> = {};\n  for (const row of rows) {\n    result[row.key] = row.value;\n  }\n  return result;\n}\n\n/**\n * Load default config values.\n * Only inserts entries for keys that don't already exist (idempotent).\n */\nexport function loadDefaults(): void {\n  const db = getConnection();\n  const insert = db.prepare('INSERT OR IGNORE INTO config (key, value) VALUES (?, ?)');\n\n  const tx = db.transaction(() => {\n    for (const [key, value] of Object.entries(DEFAULTS)) {\n      insert.run(key, value);\n    }\n  });\n\n  tx();\n}\n","import { getConnection } from './connection.js';\n\nexport interface Project {\n  id: number;\n  name: string;\n  path: string;\n  created_at: string;\n  last_opened: string | null;\n}\n\nexport interface ProjectUpdate {\n  name?: string;\n  path?: string;\n}\n\n/**\n * Register a new project.\n * Throws if the path already exists (UNIQUE constraint).\n */\nexport function registerProject(name: string, projectPath: string): Project {\n  const db = getConnection();\n  const stmt = db.prepare(`INSERT INTO projects (name, path) VALUES (?, ?)`);\n  const result = stmt.run(name, projectPath);\n  if (result.lastInsertRowid === 0) {\n    throw new Error(`Project path already exists: ${projectPath}`);\n  }\n  return {\n    id: Number(result.lastInsertRowid),\n    name,\n    path: projectPath,\n    created_at: new Date().toISOString(),\n    last_opened: null,\n  };\n}\n\n/**\n * List all registered projects, ordered by name.\n */\nexport function listProjects(): Project[] {\n  const db = getConnection();\n  return db\n    .prepare('SELECT id, name, path, created_at, last_opened FROM projects ORDER BY name')\n    .all() as Project[];\n}\n\n/**\n * Remove a project by id.\n * Returns true if removed, false if not found.\n */\nexport function removeProject(id: number): boolean {\n  const db = getConnection();\n  const result = db.prepare('DELETE FROM projects WHERE id = ?').run(id);\n  return result.changes > 0;\n}\n\n/**\n * Update last_opened timestamp.\n */\nexport function touchProject(id: number): void {\n  const db = getConnection();\n  db.prepare('UPDATE projects SET last_opened = CURRENT_TIMESTAMP WHERE id = ?').run(id);\n}\n\n/**\n * Update project name and/or path.\n */\nexport function updateProject(id: number, updates: ProjectUpdate): void {\n  const db = getConnection();\n  const sets: string[] = [];\n  const params: (string | number)[] = [];\n\n  if (updates.name !== undefined) {\n    sets.push('name = ?');\n    params.push(updates.name);\n  }\n  if (updates.path !== undefined) {\n    sets.push('path = ?');\n    params.push(updates.path);\n  }\n\n  if (sets.length === 0) return;\n\n  params.push(id);\n  db.prepare(`UPDATE projects SET ${sets.join(', ')} WHERE id = ?`).run(...params);\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { execSync } from 'node:child_process';\n\n/**\n * Sensitive file patterns that should never be exposed.\n */\nconst SENSITIVE_PATTERNS = [\n  '.env',\n  '.env.local',\n  '.env.production',\n  '.env.development',\n  '.git',\n  '.gitignore',\n  '.gitattributes',\n  'node_modules',\n  '.DS_Store',\n  'Thumbs.db',\n  '__pycache__',\n  '.idea',\n  '.vscode',\n];\n\nconst SENSITIVE_EXTENSIONS = ['.key', '.pem', '.p12', '.pfx', '.jks', '.keystore'];\n\nexport interface DirEntry {\n  name: string;\n  type: 'file' | 'directory' | 'symlink';\n  size: number;\n  modified: string;\n}\n\n/**\n * Read the content of a file as UTF-8 string.\n */\nexport function readFile(absolutePath: string): string {\n  return fs.readFileSync(absolutePath, 'utf-8');\n}\n\n/**\n * Read a file as a raw Buffer (no encoding).\n */\nexport function readFileRaw(absolutePath: string): Buffer {\n  return fs.readFileSync(absolutePath);\n}\n\n/**\n * Detect if a file is binary by checking for null bytes in the first 8KB.\n * Text files virtually never contain null bytes. Returns true if binary.\n */\nexport function isBinaryFile(absolutePath: string): boolean {\n  try {\n    const fd = fs.openSync(absolutePath, 'r');\n    const buf = Buffer.alloc(8192);\n    const bytesRead = fs.readSync(fd, buf, 0, 8192, 0);\n    fs.closeSync(fd);\n    // Check for null bytes in the sample\n    for (let i = 0; i < bytesRead; i++) {\n      if (buf[i] === 0) return true;\n    }\n    return false;\n  } catch {\n    // If we can't read the file, treat as binary for safety\n    return true;\n  }\n}\n\n/**\n * Read only the first N lines of a text file. Returns { content, truncated, totalBytes }.\n */\nexport function readFirstNLines(absolutePath: string, maxLines: number): { content: string; truncated: boolean; totalBytes: number } {\n  const stats = fs.statSync(absolutePath);\n  const totalBytes = stats.size;\n  const fd = fs.openSync(absolutePath, 'r');\n  const buf = Buffer.alloc(65536); // 64KB chunks\n  let pos = 0, lines = 0;\n  const parts: string[] = [];\n\n  while (lines < maxLines && pos < totalBytes) {\n    const bytesRead = fs.readSync(fd, buf, 0, buf.length, pos);\n    if (bytesRead === 0) break;\n    const chunk = buf.toString('utf-8', 0, bytesRead);\n    const chunkLines = chunk.split('\\n');\n    lines += chunkLines.length - 1;\n    if (lines >= maxLines) {\n      // Only include lines up to maxLines\n      const takeLines = chunkLines.slice(0, maxLines - (lines - chunkLines.length + 1));\n      parts.push(takeLines.join('\\n'));\n      break;\n    }\n    parts.push(chunk);\n    pos += bytesRead;\n  }\n  fs.closeSync(fd);\n\n  const content = parts.join('');\n  return { content, truncated: pos < totalBytes, totalBytes };\n}\n\n/**\n * Get file/directory stats.\n */\nexport function statFile(absolutePath: string): fs.Stats {\n  return fs.statSync(absolutePath);\n}\n\n/**\n * List direct children of a directory, excluding sensitive files.\n */\nexport function listDir(absolutePath: string): DirEntry[] {\n  const entries = fs.readdirSync(absolutePath, { withFileTypes: true });\n  const result: DirEntry[] = [];\n\n  for (const entry of entries) {\n    if (isSensitiveFile(entry.name)) {\n      continue;\n    }\n\n    const fullPath = path.join(absolutePath, entry.name);\n    try {\n      const stats = fs.statSync(fullPath);\n      result.push({\n        name: entry.name,\n        type: entry.isSymbolicLink() ? 'symlink' : entry.isDirectory() ? 'directory' : 'file',\n        size: stats.size,\n        modified: stats.mtime.toISOString(),\n      });\n    } catch {\n      // Skip entries we can't stat (permission errors, broken symlinks, etc.)\n    }\n  }\n\n  return result;\n}\n\n/**\n * Check whether a file/directory name matches sensitive patterns.\n */\nexport function isSensitiveFile(name: string): boolean {\n  if (SENSITIVE_PATTERNS.includes(name)) {\n    return true;\n  }\n\n  const ext = path.extname(name).toLowerCase();\n  if (SENSITIVE_EXTENSIONS.includes(ext)) {\n    return true;\n  }\n\n  return false;\n}\n\n/**\n * Validate that a requested path is within the project root.\n * Resolves symlinks to prevent traversal attacks.\n * Returns the resolved absolute path if valid, throws otherwise.\n */\nexport function validatePath(projectRoot: string, requestedPath: string): string {\n  // Resolve project root to absolute\n  const root = path.resolve(projectRoot);\n\n  // Resolve the requested path relative to root\n  const candidate = path.resolve(root, requestedPath);\n\n  // Check basic traversal: candidate must start with root\n  if (!candidate.startsWith(root + path.sep) && candidate !== root) {\n    throw new Error(`Path traversal denied: \"${requestedPath}\" is outside project root.`);\n  }\n\n  // Resolve symlinks to get real path\n  let realPath: string;\n  try {\n    realPath = fs.realpathSync(candidate);\n  } catch {\n    // If path doesn't exist yet (e.g., for write operations), use candidate\n    // But resolve parent directories\n    const parentDir = path.dirname(candidate);\n    let realParent: string;\n    try {\n      realParent = fs.realpathSync(parentDir);\n    } catch {\n      throw new Error(`Parent directory does not exist: \"${requestedPath}\"`);\n    }\n    realPath = path.join(realParent, path.basename(candidate));\n  }\n\n  // Recheck after symlink resolution\n  if (!realPath.startsWith(root + path.sep) && realPath !== root) {\n    throw new Error(`Symlink traversal denied: \"${requestedPath}\" resolves outside project root.`);\n  }\n\n  return candidate;\n}\n\n/**\n * Resolve a user-provided project path to an absolute, normalized path.\n *\n * Platform-aware logic:\n * - Native Windows:   keeps Windows paths (D:\\\\foo), expands ~ to %USERPROFILE%\n * - WSL (Linux+microsoft kernel): converts Windows paths → /mnt/d/... via wslpath\n * - Native Linux/macOS: expands ~ to $HOME, rejects Windows paths with a clear error\n * - Relative paths are resolved against cwd\n */\nexport function resolveProjectPath(rawPath: string): string {\n  let resolved = rawPath;\n\n  // 1. Expand ~ to home directory\n  if (resolved.startsWith('~')) {\n    resolved = path.join(os.homedir(), resolved.slice(1));\n  }\n\n  const isWinPath = /^[A-Za-z]:[/\\\\]/.test(resolved);\n\n  // 2. Native Windows — keep Windows paths as-is\n  if (process.platform === 'win32') {\n    return path.resolve(resolved);\n  }\n\n  // 3. WSL environment — convert Windows paths to Linux mount points\n  if (isWinPath && isWsl()) {\n    const converted = tryWslPath(resolved);\n    if (converted) return converted;\n    // wslpath failed but we're in WSL — fall through to resolve\n  }\n\n  // 4. Native Linux/macOS with a Windows path — error\n  if (isWinPath && !isWsl()) {\n    throw new Error(\n      `Windows path \"${rawPath}\" is not valid on ${process.platform}. ` +\n      'Use a Linux path (e.g., /home/user/docs) or run inside WSL for Windows paths.',\n    );\n  }\n\n  // 5. Standard Linux/macOS path\n  return path.resolve(resolved);\n}\n\n/**\n * Check if the current process is running inside Windows Subsystem for Linux.\n */\nfunction isWsl(): boolean {\n  try {\n    const content = fs.readFileSync('/proc/version', 'utf-8');\n    return content.toLowerCase().includes('microsoft') || content.toLowerCase().includes('wsl');\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Try to convert a Windows path to WSL path using `wslpath`.\n * Returns null if wslpath is not available or conversion fails.\n */\nfunction tryWslPath(windowsPath: string): string | null {\n  try {\n    const result = execSync(`wslpath -u \"${windowsPath}\"`, {\n      encoding: 'utf-8',\n      timeout: 3000,\n    }).trim();\n    if (result.startsWith('/')) {\n      return result;\n    }\n  } catch {\n    // wslpath not available or conversion failed\n  }\n  return null;\n}\n\n","import * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport { getConnection } from '../db/connection.js';\nimport { listDir, type DirEntry } from '../fs/index.js';\n\nexport type { DirEntry };\n\nexport interface ScanResult {\n  path: string;\n  entries: DirEntry[];\n  cached: boolean;\n}\n\n/**\n * Scan a directory within a project.\n * Uses lazy loading — only returns direct children.\n * Results are cached in filetree_cache with mtime-based invalidation.\n *\n * @param projectId - The project ID\n * @param dirPath - Relative path within the project ('' for root)\n */\nexport function scanDirectory(projectId: number, dirPath: string): ScanResult {\n  const db = getConnection();\n\n  // Normalize path\n  const normalizedPath = dirPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n\n  // Get project root path\n  const project = db.prepare('SELECT path FROM projects WHERE id = ?').get(projectId) as\n    { path: string } | undefined;\n\n  if (!project) {\n    throw new Error(`Project not found: ${projectId}`);\n  }\n\n  const absPath = normalizedPath ? path.join(project.path, normalizedPath) : project.path;\n\n  // Check cache\n  const cached = db\n    .prepare(\n      'SELECT tree_json, mtime_map FROM filetree_cache WHERE project_id = ? AND node_path = ?',\n    )\n    .get(projectId, normalizedPath) as { tree_json: string; mtime_map: string | null } | undefined;\n\n  if (cached) {\n    const mtimeMap: Record<string, string> = cached.mtime_map ? JSON.parse(cached.mtime_map) : {};\n\n    // Validate by stat'ing each cached file and comparing mtime\n    if (isCacheValid(absPath, mtimeMap)) {\n      const entries = JSON.parse(cached.tree_json) as DirEntry[];\n      return { path: normalizedPath, entries, cached: true };\n    }\n\n    // Cache invalid — delete it\n    db.prepare('DELETE FROM filetree_cache WHERE project_id = ? AND node_path = ?').run(\n      projectId,\n      normalizedPath,\n    );\n  }\n\n  // Scan fresh\n  const entries = listDir(absPath);\n\n  // Build mtime map for future cache validation\n  const mtimeMap: Record<string, string> = {};\n  for (const entry of entries) {\n    mtimeMap[entry.name] = entry.modified;\n  }\n\n  // Store in cache\n  db.prepare(\n    `INSERT OR REPLACE INTO filetree_cache (project_id, node_path, tree_json, mtime_map)\n     VALUES (?, ?, ?, ?)`,\n  ).run(projectId, normalizedPath, JSON.stringify(entries), JSON.stringify(mtimeMap));\n\n  return { path: normalizedPath, entries, cached: false };\n}\n\n/**\n * Clear cache for a specific project path, or entire project if no path given.\n */\nexport function clearCache(projectId: number, dirPath?: string): void {\n  const db = getConnection();\n\n  if (dirPath !== undefined) {\n    const normalized = dirPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n    db.prepare('DELETE FROM filetree_cache WHERE project_id = ? AND node_path = ?').run(\n      projectId,\n      normalized,\n    );\n  } else {\n    db.prepare('DELETE FROM filetree_cache WHERE project_id = ?').run(projectId);\n  }\n}\n\n/**\n * Check if the cached directory listing is still valid.\n * Stats each file in the cached mtime_map and compares against actual mtime.\n */\nfunction isCacheValid(absDirPath: string, cachedMtimeMap: Record<string, string>): boolean {\n  for (const [name, cachedMtime] of Object.entries(cachedMtimeMap)) {\n    try {\n      const stats = fs.statSync(path.join(absDirPath, name));\n      if (stats.mtime.toISOString() !== cachedMtime) {\n        return false;\n      }\n    } catch {\n      // File was removed or is inaccessible — cache invalid\n      return false;\n    }\n  }\n  return true;\n}\n","import express, { type Request, type Response, type NextFunction } from 'express';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport { exec, execSync } from 'node:child_process';\nimport { openDirectoryDialog } from './dialog.js';\nimport { fileURLToPath } from 'node:url';\nimport { getConnection } from '../db/connection.js';\nimport { registerProject, listProjects, removeProject, updateProject, touchProject } from '../db/projects.js';\nimport { scanDirectory } from '../scanner/index.js';\nimport { readFile, readFileRaw, isBinaryFile, readFirstNLines, validatePath, resolveProjectPath } from '../fs/index.js';\nimport * as crypto from '../crypto.js';\nimport {\n  renderMarkdown,\n  renderMermaid,\n  renderCode,\n  getRendererForFile,\n  isUnsupportedFormat,\n  FORMAT_SIZE_LIMITS,\n} from '../renderers/index.js';\nimport { getOrCreateSession, resetSession, type SessionAgent } from './sessions.js';\nimport { isMobileRequest } from './mobile-detect.js';\n\nconst VERSION = '0.1.0';\n\n/**\n * Create and configure the Express application.\n * @param restartCallback — if provided, enables POST /api/restart endpoint\n * @param bindAddr — actual runtime bind address (for /api/server-info)\n */\nexport function createApp(restartCallback?: () => void, bindAddr?: string) {\n  const app = express();\n\n  // --- Middleware ---\n\n  // Parse JSON bodies\n  app.use(express.json());\n\n  // === Resolve web directory (unchanged logic) ===\n  const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n  const webCandidates = [\n    path.join(moduleDir, 'web'),              // dist/web/ (npm publish layout)\n    path.join(moduleDir, '..', 'web'),        // src/web/ (dev via src/server/)\n    path.join(moduleDir, '..', 'src', 'web'), // resolve from dist/ to src/web/\n  ];\n  let webDir = '';\n  for (const candidate of webCandidates) {\n    if (fs.existsSync(path.join(candidate, 'index.html'))) {\n      webDir = candidate;\n      break;\n    }\n  }\n\n  // === Fallback HTML (extracted from original inline) ===\n  const fallbackHtml = `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Doc77</title></head>\n<body style=\"font-family:system-ui,-apple-system,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#f8fafc\">\n<div style=\"text-align:center\">\n<h1 style=\"color:#1e293b;font-size:2rem;margin-bottom:.5rem\">📁 Doc77</h1>\n<p style=\"color:#64748b\">Dashboard is running.</p>\n<p style=\"color:#94a3b8;font-size:14px\">Run <code style=\"background:#e2e8f0;padding:2px 6px;border-radius:4px\">pnpm build</code> in the workspace root to rebuild the web assets.</p>\n</div></body></html>`;\n\n  // === Device-aware HTML routes (must be before static middleware) ===\n\n  app.get('/', (_req: Request, res: Response) => {\n    if (!webDir) {\n      res.type('html').send(fallbackHtml);\n      return;\n    }\n    const useMobile = isMobileRequest(_req);\n    const target = path.join(webDir, useMobile ? 'mobile/index.html' : 'index.html');\n    if (fs.existsSync(target)) {\n      res.sendFile(target);\n      return;\n    }\n    // Graceful degradation: fall back to desktop if mobile HTML missing\n    if (useMobile) {\n      const desktopFallback = path.join(webDir, 'index.html');\n      if (fs.existsSync(desktopFallback)) {\n        res.sendFile(desktopFallback);\n        return;\n      }\n    }\n    res.type('html').send(fallbackHtml);\n  });\n\n  app.get('/preview.html', (_req: Request, res: Response) => {\n    if (!webDir) {\n      res.status(404).type('html').send('<h1>Not Found</h1>');\n      return;\n    }\n    const useMobile = isMobileRequest(_req);\n    const target = path.join(webDir, useMobile ? 'mobile/preview.html' : 'preview.html');\n    if (fs.existsSync(target)) {\n      res.sendFile(target);\n      return;\n    }\n    if (useMobile) {\n      const desktopFallback = path.join(webDir, 'preview.html');\n      if (fs.existsSync(desktopFallback)) {\n        res.sendFile(desktopFallback);\n        return;\n      }\n    }\n    res.status(404).type('html').send('<h1>Not Found</h1>');\n  });\n\n  // === Static file serving (after explicit routes) ===\n\n  if (webDir) {\n    // /mobile/* → web/mobile/ files (conditional — no error if dir missing)\n    const mobileDir = path.join(webDir, 'mobile');\n    if (fs.existsSync(mobileDir)) {\n      app.use('/mobile', express.static(mobileDir));\n    }\n    // /* → desktop + shared assets (catch-all)\n    app.use(express.static(webDir));\n  }\n\n  // Serve vendor cache (offline CDN resources)\n  const vendorDir = path.join(process.env.HOME || '/home', '.doc77', 'vendor');\n  app.use('/vendor', express.static(vendorDir, { fallthrough: true }));\n\n  // CORS — allow all origins (localhost-only binding for security)\n  app.use((_req: Request, res: Response, next: NextFunction) => {\n    res.header('Access-Control-Allow-Origin', '*');\n    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');\n    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n    if (_req.method === 'OPTIONS') {\n      res.sendStatus(204);\n      return;\n    }\n    next();\n  });\n\n  // --- API Routes ---\n\n  // Server info — runtime state (actual bind address, not config)\n  app.get('/api/server-info', (_req: Request, res: Response) => {\n    const addr = bindAddr || '127.0.0.1';\n    const isLocal = addr === '127.0.0.1' || addr === '::1' || addr === 'localhost';\n    res.json({\n      bindAddress: addr,\n      isLocal,\n      port: 3099,\n      version: VERSION,\n    });\n  });\n\n  // Server restart (only when callback provided)\n  if (restartCallback) {\n    app.post('/api/restart', (_req: Request, res: Response) => {\n      res.json({ ok: true, message: 'Server restarting...' });\n      // Delay restart to allow response to be sent\n      setTimeout(() => restartCallback(), 500);\n    });\n  }\n\n  // Health check\n  app.get('/api/health', (_req: Request, res: Response) => {\n    let dbStatus = 'connected';\n    let activeLocks = 0;\n    let sessionCount = 0;\n    try {\n      const db = getConnection();\n      activeLocks = (\n        db.prepare('SELECT COUNT(*) as count FROM project_locks').get() as { count: number }\n      ).count;\n      sessionCount = (\n        db.prepare('SELECT COUNT(*) as count FROM sessions').get() as { count: number }\n      ).count;\n    } catch {\n      dbStatus = 'disconnected';\n    }\n\n    res.json({\n      status: 'ok',\n      version: VERSION,\n      db: dbStatus,\n      active_locks: activeLocks,\n      session_count: sessionCount,\n      timestamp: new Date().toISOString(),\n    });\n  });\n\n  // Project CRUD\n  app.get('/api/projects', (_req: Request, res: Response) => {\n    const projects = listProjects();\n    res.json(projects);\n  });\n\n  app.post('/api/projects', (req: Request, res: Response) => {\n    const { name, path: projectPath } = req.body;\n    if (!name || !projectPath) {\n      res.status(400).json({ error: 'name and path are required' });\n      return;\n    }\n    try {\n      const resolved = resolveProjectPath(projectPath);\n      const project = registerProject(name, resolved);\n      res.status(201).json(project);\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(409).json({ error: message });\n    }\n  });\n\n  app.delete('/api/projects/:id', (req: Request, res: Response) => {\n    const id = parseInt(req.params.id, 10);\n    if (isNaN(id)) {\n      res.status(400).json({ error: 'Invalid project id' });\n      return;\n    }\n    const removed = removeProject(id);\n    if (!removed) {\n      res.status(404).json({ error: 'Project not found' });\n      return;\n    }\n    res.json({ removed: true });\n  });\n\n  // Update project\n  app.put('/api/projects/:id', (req: Request, res: Response) => {\n    const id = parseInt(req.params.id, 10);\n    const { name, path: newPath } = req.body;\n    if (isNaN(id)) { res.status(400).json({ error: 'Invalid project id' }); return; }\n    if (!name && !newPath) { res.status(400).json({ error: 'name or path required' }); return; }\n    try {\n      const resolved = newPath ? resolveProjectPath(newPath) : undefined;\n      updateProject(id, { name, path: resolved });\n      res.json({ ok: true });\n    } catch (e: unknown) {\n      const message = e instanceof Error ? e.message : 'Unknown error';\n      res.status(409).json({ error: message });\n    }\n  });\n\n  // Touch project (update last_opened)\n  app.post('/api/projects/:id/touch', (req: Request, res: Response) => {\n    const id = parseInt(req.params.id, 10);\n    if (isNaN(id)) { res.status(400).json({ error: 'Invalid project id' }); return; }\n    touchProject(id);\n    res.json({ ok: true });\n  });\n\n  // Environment detection — tells frontend what strategies are available\n  app.get('/api/env', (_req: Request, res: Response) => {\n    let wsl = false;\n    try {\n      wsl = /microsoft|wsl/i.test(fs.readFileSync('/proc/version', 'utf-8'));\n    } catch {}\n    const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);\n    res.json({\n      platform: process.platform,\n      wsl,\n      hasDisplay,\n      home: process.env.HOME || '/home',\n      username: process.env.USER || '',\n    });\n  });\n\n  // Fingerprint-based folder finder — matches a directory picked by the browser\n  // against the server's local filesystem\n  app.post('/api/find-folder', async (req: Request, res: Response) => {\n    const { folderName, fingerprint } = req.body as {\n      folderName?: string;\n      fingerprint?: Array<{ name: string; size: number; type: string }>;\n    };\n    if (!folderName || !fingerprint || fingerprint.length === 0) {\n      res.status(400).json({ error: 'folderName and fingerprint are required' });\n      return;\n    }\n\n    // Determine search roots — Linux home first (fast), then Windows mounts\n    const searchRoots: string[] = [];\n    const home = process.env.HOME || '/home';\n    searchRoots.push(home);\n    let isWsl = false;\n    try {\n      isWsl = /microsoft|wsl/i.test(fs.readFileSync('/proc/version', 'utf-8'));\n    } catch {}\n    if (isWsl) {\n      for (const drive of ['d', 'c', 'e']) {\n        try { if (fs.existsSync('/mnt/' + drive)) searchRoots.push('/mnt/' + drive); } catch {}\n      }\n      try {\n        const usersDir = '/mnt/c/Users';\n        for (const e of fs.readdirSync(usersDir, { withFileTypes: true })) {\n          if (e.isDirectory() && !e.isSymbolicLink() && !['Public', 'Default', 'Default User', 'All Users', 'WsiAccount'].includes(e.name) && !e.name.startsWith('.')) {\n            searchRoots.push(usersDir + '/' + e.name);\n          }\n        }\n      } catch {}\n    }\n\n    const matches: Array<{ path: string; score: number }> = [];\n    const deadline = Date.now() + 5000;\n\n    for (const root of searchRoots) {\n      if (Date.now() > deadline) break;\n      try {\n        const raw = (() => {\n          try {\n            return execSync(\n              `find \"${root}\" -maxdepth 4 -type d -name \"${folderName}\" 2>/dev/null; true`,\n              { timeout: 4000, encoding: 'utf-8', maxBuffer: 1024 * 1024 }\n            ).trim();\n          } catch (e: unknown) {\n            // execSync throws on non-zero exit, but stdout may still have results\n            const err = e as { stdout?: string; stderr?: string };\n            return (err.stdout || '').trim();\n          }\n        })();\n        const candidates = raw.split('\\n').filter(Boolean);\n        for (const candidate of candidates) {\n          if (Date.now() > deadline) break;\n          try {\n            // Match all fingerprint entries (files + directories)\n            let matched = 0, checked = 0;\n            for (const fp of fingerprint) {\n              checked++;\n              try {\n                const fpPath = candidate + '/' + fp.name;\n                const st = fs.statSync(fpPath);\n                if (fp.type === 'directory' && st.isDirectory()) { matched++; }\n                else if (fp.type === 'file' && st.isFile()) {\n                  if (fp.size === 0 || st.size === fp.size || Math.abs(st.size - fp.size) < 10) matched++;\n                }\n              } catch {}\n            }\n            const score = checked > 0 ? matched / checked : 0;\n            if (score > 0) {\n              matches.push({ path: candidate, score });\n            }\n          } catch {}\n        }\n      } catch {}\n    }\n\n    // Sort by score descending, deduplicate\n    matches.sort((a, b) => b.score - a.score);\n    const seen = new Set<string>();\n    const unique = matches.filter(m => { if (seen.has(m.path)) return false; seen.add(m.path); return true; });\n\n    res.json({ matches: unique.slice(0, 5) });\n  });\n\n  // Server-side file browser — for remote access or when native dialog unavailable\n  app.get('/api/browse-fs', (req: Request, res: Response) => {\n    let dirPath = (req.query.path as string) || '/';\n    // Expand ~ and resolve\n    if (dirPath.startsWith('~')) {\n      dirPath = (process.env.HOME || '/home') + dirPath.slice(1);\n    }\n    dirPath = path.resolve(dirPath);\n\n    // Determine if request comes from localhost (even when bound to 0.0.0.0)\n    const remoteIp = req.socket.remoteAddress || '';\n    const isLocalRequest = remoteIp === '127.0.0.1' || remoteIp === '::1' || remoteIp === '::ffff:127.0.0.1';\n    const isLocalBind = !bindAddr || bindAddr === '127.0.0.1' || bindAddr === 'localhost' || bindAddr === '::1';\n    const isLocalAccess = isLocalBind || isLocalRequest;\n\n    // WSL /mnt blocking — only relevant on Linux, and only for remote access\n    const isLinux = process.platform === 'linux';\n    const isMntPath = isLinux && (dirPath === '/mnt' || dirPath.startsWith('/mnt/'));\n    if (!isLocalAccess && isMntPath) {\n      res.json({ path: dirPath, parent: null, roots: ['/home', '/tmp'], entries: [], error: '远程访问模式下不允许浏览 /mnt（Windows 驱动器）' });\n      return;\n    }\n\n    // Security: blacklist system directories per OS, allow everything else.\n    // Password protection is the primary security layer for remote access.\n    const isWin = process.platform === 'win32';\n    function isPathBlocked(p: string): boolean {\n      const BLOCKED_ROOTS: Record<string, string[]> = {\n        win32: [\n          'C:\\\\Windows', 'C:\\\\Program Files', 'C:\\\\Program Files (x86)',\n          'C:\\\\ProgramData', 'C:\\\\$Recycle.Bin',\n          'C:\\\\System Volume Information', 'C:\\\\Recovery',\n          'C:\\\\Config.Msi', 'C:\\\\MSOCache', 'C:\\\\PerfLogs',\n        ],\n        linux: [\n          '/etc', '/proc', '/sys', '/dev', '/run', '/boot',\n          '/var/log', '/var/run', '/var/lock',\n          '/bin', '/sbin', '/usr/bin', '/usr/sbin',\n        ],\n        darwin: [\n          '/System', '/Library', '/private/etc', '/private/var',\n          '/usr/bin', '/usr/sbin',\n        ],\n      };\n      const blocked = BLOCKED_ROOTS[process.platform] || [];\n      return blocked.some(r => p === r || p.startsWith(r + path.sep));\n    }\n    if (isPathBlocked(dirPath)) {\n      res.json({ path: dirPath, parent: null, roots: [], entries: [], error: '此目录不允许访问' });\n      return;\n    }\n\n    // Hidden system dirs to filter from root-level listings only\n    const BLOCKED_DIRS = isWin\n      ? new Set(['Windows', 'Program Files', 'Program Files (x86)', 'ProgramData',\n                 '$Recycle.Bin', 'System Volume Information', 'Recovery', 'Config.Msi',\n                 'MSOCache', 'PerfLogs'])\n      : new Set(['etc', 'proc', 'sys', 'root', 'var', 'boot', 'dev', 'run', 'snap',\n                 'bin', 'sbin', 'lib', 'lib64', 'usr', 'lost+found', '.dockerenv']);\n\n    // Cross-platform root detection (C:\\ on Windows, / on Unix)\n    const parsedRoot = path.parse(dirPath).root;\n    const isRoot = parsedRoot === dirPath;\n\n    // Security: verify the path exists and is a directory\n    let entries: Array<{ name: string; type: string; size: number }> = [];\n    let error: string | undefined;\n    try {\n      const stat = fs.statSync(dirPath);\n      if (!stat.isDirectory()) {\n        error = 'Not a directory';\n      } else {\n        const raw = fs.readdirSync(dirPath, { withFileTypes: true });\n        entries = raw\n          .filter(e => !e.name.startsWith('.')) // hide dotfiles\n          .filter(e => !(isRoot && (BLOCKED_DIRS.has(e.name) || e.name.includes('usr-is-merged') || e.name === 'init')))\n          .filter(e => !(isRoot && !isLocalAccess && isLinux && e.name === 'mnt')) // hide /mnt in remote mode\n          .map(e => ({\n            name: e.name,\n            type: e.isDirectory() ? 'directory' : 'file',\n            size: 0,\n          }))\n          .sort((a, b) => {\n            if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;\n            return a.name.localeCompare(b.name);\n          })\n          .slice(0, 200); // cap entries\n      }\n    } catch (e: unknown) {\n      error = e instanceof Error ? e.message : 'Access denied';\n    }\n\n    // Compute parent path\n    const parent = path.dirname(dirPath);\n    const hasParent = parent !== dirPath;\n\n    // Quick-access root shortcuts (common user directories + drives)\n    const roots: string[] = [];\n    if (isWin) {\n      const winHome = process.env.USERPROFILE || 'C:\\\\Users\\\\Default';\n      if (fs.existsSync(winHome)) roots.push(winHome);\n      // Common user folders\n      for (const sub of ['Desktop', 'Documents', 'Downloads']) {\n        const p = path.join(winHome, sub);\n        try { if (fs.existsSync(p)) roots.push(p); } catch {}\n      }\n      // Available drives\n      for (let c = 65; c <= 90; c++) {\n        const drive = String.fromCharCode(c) + ':\\\\';\n        try { if (fs.existsSync(drive)) roots.push(drive); } catch {}\n      }\n    } else {\n      const home = process.env.HOME || '/home';\n      if (fs.existsSync(home)) roots.push(home);\n      // Common user folders\n      for (const sub of ['Desktop', 'Documents', 'Downloads']) {\n        const p = path.join(home, sub);\n        try { if (fs.existsSync(p)) roots.push(p); } catch {}\n      }\n      // /tmp always available\n      if (!roots.includes('/tmp')) roots.push('/tmp');\n      // macOS specific\n      if (process.platform === 'darwin' && !roots.includes('/Users')) roots.push('/Users');\n      // WSL /mnt (local access only)\n      if (isLocalAccess && isLinux) {\n        try {\n          const isWsl = /microsoft|wsl/i.test(fs.readFileSync('/proc/version', 'utf-8'));\n          if (isWsl) {\n            for (const drive of ['d', 'c', 'e']) {\n              try { if (fs.existsSync('/mnt/' + drive)) roots.push('/mnt/' + drive); } catch {}\n            }\n          }\n        } catch {}\n      }\n    }\n\n    res.json({ path: dirPath, parent: hasParent ? parent : null, roots, entries, error });\n  });\n\n  // Native directory picker dialog\n  app.post('/api/dialog/open-directory', async (_req: Request, res: Response) => {\n    try {\n      const dirPath = await openDirectoryDialog();\n      res.json({ path: dirPath });\n    } catch (e: unknown) {\n      const message = e instanceof Error ? e.message : 'Unknown error';\n      res.status(500).json({ error: message });\n    }\n  });\n\n  // Directory tree\n  app.get('/api/tree/:id', (req: Request, res: Response) => {\n    const projectId = parseInt(req.params.id, 10);\n    const dirPath = (req.query.path as string) || '';\n\n    if (isNaN(projectId)) {\n      res.status(400).json({ error: 'Invalid project id' });\n      return;\n    }\n\n    try {\n      const result = scanDirectory(projectId, dirPath);\n      res.json(result);\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      if (message.includes('not found')) {\n        res.status(404).json({ error: message });\n        return;\n      }\n      res.status(500).json({ error: message });\n    }\n  });\n\n  // File content with renderer dispatch + safety gates\n  app.get('/api/content/:id', (req: Request, res: Response) => {\n    const projectId = parseInt(req.params.id, 10);\n    const filePath = req.query.path as string;\n\n    if (isNaN(projectId)) {\n      res.status(400).json({ error: 'Invalid project id' });\n      return;\n    }\n    if (!filePath) {\n      res.status(400).json({ error: 'path query parameter is required' });\n      return;\n    }\n\n    try {\n      const db = getConnection();\n      const project = db.prepare('SELECT path FROM projects WHERE id = ?').get(projectId) as\n        { path: string } | undefined;\n\n      if (!project) { res.status(404).json({ error: 'Project not found' }); return; }\n\n      const absPath = validatePath(project.path, filePath);\n      const stats = fs.statSync(absPath);\n      const rendererType = getRendererForFile(filePath);\n\n      // --- Safety Gate 1: Unsupported format ---\n      if (isUnsupportedFormat(filePath)) {\n        res.json({\n          path: filePath,\n          type: 'unsupported',\n          size: stats.size,\n          modified: stats.mtime.toISOString(),\n          category: getFileCategory(filePath),\n        });\n        return;\n      }\n\n      // --- Safety Gate 2: Binary file detection ---\n      if (rendererType === 'text' && isBinaryFile(absPath)) {\n        res.json({\n          path: filePath,\n          type: 'unsupported',\n          size: stats.size,\n          modified: stats.mtime.toISOString(),\n          category: 'binary',\n        });\n        return;\n      }\n\n      // --- Safety Gate 3: Size limit ---\n      const sizeLimit = FORMAT_SIZE_LIMITS[rendererType] ?? FORMAT_SIZE_LIMITS.text;\n      if (sizeLimit > 0 && stats.size > sizeLimit) {\n        // For text-based formats: truncate and warn\n        if (['markdown','mermaid','code','text'].includes(rendererType)) {\n          const { content, truncated, totalBytes } = readFirstNLines(absPath, 10000);\n          res.json({\n            path: filePath,\n            type: rendererType === 'text' ? 'text' : rendererType,\n            content: truncated\n              ? `⚠️ 文件过大（${(totalBytes / 1024 / 1024).toFixed(1)} MB），仅显示前 10,000 行。建议在本地编辑器中打开。\\n\\n---\\n\\n${content}`\n              : content,\n            truncated,\n          });\n          return;\n        }\n        // For xlsx: reject\n        if (rendererType === 'xlsx') {\n          res.json({\n            path: filePath,\n            type: 'unsupported',\n            size: stats.size,\n            modified: stats.mtime.toISOString(),\n            category: 'too_large',\n          });\n          return;\n        }\n      }\n\n      // --- Normal render ---\n      switch (rendererType) {\n        case 'markdown': {\n          const raw = readFile(absPath);\n          res.json({ path: filePath, type: 'markdown', content: renderMarkdown(raw) });\n          return;\n        }\n        case 'mermaid': {\n          const raw = readFile(absPath);\n          res.json({ path: filePath, type: 'mermaid', content: renderMermaid(raw) });\n          return;\n        }\n        case 'code': {\n          const raw = readFile(absPath);\n          res.json({ path: filePath, type: 'code', content: renderCode(raw, path.extname(filePath).slice(1)), rawUrl: `/api/raw/${projectId}?path=${encodeURIComponent(filePath)}` });\n          return;\n        }\n        case 'docx': {\n          // docx is handled later (needs mammoth), for now return unsupported info\n          res.json({\n            path: filePath, type: 'docx',\n            rawUrl: `/api/raw/${projectId}?path=${encodeURIComponent(filePath)}`,\n            size: stats.size, modified: stats.mtime.toISOString(),\n          });\n          return;\n        }\n        case 'xlsx': {\n          res.json({\n            path: filePath, type: 'xlsx',\n            rawUrl: `/api/raw/${projectId}?path=${encodeURIComponent(filePath)}`,\n            size: stats.size, modified: stats.mtime.toISOString(),\n          });\n          return;\n        }\n        case 'image':\n        case 'pdf':\n          res.json({\n            path: filePath,\n            type: rendererType,\n            rawUrl: `/api/raw/${projectId}?path=${encodeURIComponent(filePath)}`,\n          });\n          return;\n        default: {\n          const raw = readFile(absPath);\n          res.json({ path: filePath, type: 'text', content: raw });\n        }\n      }\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      if (message.includes('not found') || message.includes('ENOENT') || message.includes('traversal')) {\n        res.status(404).json({ error: 'File not found' });\n        return;\n      }\n      res.status(500).json({ error: message });\n    }\n  });\n\n  // Serve raw binary files (images, PDFs) with proper Content-Type\n  app.get('/api/raw/:id', (req: Request, res: Response) => {\n    const projectId = parseInt(req.params.id, 10);\n    const filePath = req.query.path as string;\n\n    if (isNaN(projectId) || !filePath) {\n      res.status(400).json({ error: 'Invalid request' });\n      return;\n    }\n\n    try {\n      const db = getConnection();\n      const project = db\n        .prepare('SELECT path FROM projects WHERE id = ?')\n        .get(projectId) as { path: string } | undefined;\n\n      if (!project) {\n        res.status(404).json({ error: 'Project not found' });\n        return;\n      }\n\n      const absPath = validatePath(project.path, filePath);\n      const ext = path.extname(filePath).toLowerCase();\n\n      // Map extension to MIME type\n      const mimeTypes: Record<string, string> = {\n        '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',\n        '.gif': 'image/gif', '.svg': 'image/svg+xml', '.webp': 'image/webp',\n        '.bmp': 'image/bmp', '.ico': 'image/x-icon', '.avif': 'image/avif',\n        '.pdf': 'application/pdf',\n        '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n        '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n      };\n      const contentType = mimeTypes[ext] || 'application/octet-stream';\n\n      const buffer = fs.readFileSync(absPath);\n      res.setHeader('Content-Type', contentType);\n      res.setHeader('Content-Length', buffer.length);\n      res.setHeader('Cache-Control', 'public, max-age=3600');\n      res.send(buffer);\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(404).json({ error: message });\n    }\n  });\n\n  // Reveal file in editor or file manager\n  app.get('/api/reveal/:id', (req: Request, res: Response) => {\n    const projectId = parseInt(req.params.id, 10);\n    const filePath = req.query.path as string;\n    const action = (req.query.action as string) || 'reveal';\n\n    if (isNaN(projectId)) {\n      res.status(400).json({ error: 'Invalid project id' });\n      return;\n    }\n    if (!filePath) {\n      res.status(400).json({ error: 'path query parameter is required' });\n      return;\n    }\n\n    try {\n      const db = getConnection();\n      const project = db.prepare('SELECT path FROM projects WHERE id = ?').get(projectId) as\n        { path: string } | undefined;\n\n      if (!project) {\n        res.status(404).json({ error: 'Project not found' });\n        return;\n      }\n\n      const absPath = validatePath(project.path, filePath);\n      const platform = process.platform;\n      let command: string;\n\n      if (action === 'edit') {\n        // Try VS Code protocol first, fall back to system open\n        const vscodeUrl = `vscode://file/${absPath}`;\n        if (platform === 'darwin') {\n          command = `open \"${vscodeUrl}\" 2>/dev/null || open -R \"${absPath}\"`;\n        } else if (platform === 'win32') {\n          command = `start \"\" \"${vscodeUrl}\" 2>nul || explorer /select,\"${absPath}\"`;\n        } else {\n          command = `xdg-open \"${vscodeUrl}\" 2>/dev/null || xdg-open \"${path.dirname(absPath)}\"`;\n        }\n      } else {\n        // Reveal in file manager\n        if (platform === 'darwin') {\n          command = `open -R \"${absPath}\"`;\n        } else if (platform === 'win32') {\n          command = `explorer /select,\"${absPath}\"`;\n        } else {\n          command = `xdg-open \"${path.dirname(absPath)}\"`;\n        }\n      }\n\n      exec(command, (error: Error | null) => {\n        if (error) {\n          console.error('[reveal]', error.message);\n        }\n      });\n\n      res.json({ ok: true, action, path: absPath });\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(404).json({ error: message });\n    }\n  });\n\n  // === Queue / Approval API ===\n\n  // Get queue status for a project\n  app.get('/api/queue/status', (req: Request, res: Response) => {\n    const projectId = req.query.project_id\n      ? parseInt(req.query.project_id as string, 10)\n      : undefined;\n\n    try {\n      // Dynamic import to avoid circular dependency at module load time\n      const db = getConnection();\n      let rows;\n      if (projectId) {\n        rows = db\n          .prepare(\n            `SELECT id, project_id, operation_type, operation_data, status, created_at\n           FROM operation_queue WHERE project_id = ? ORDER BY id DESC LIMIT 50`,\n          )\n          .all(projectId);\n      } else {\n        rows = db\n          .prepare(\n            `SELECT id, project_id, operation_type, operation_data, status, created_at\n           FROM operation_queue ORDER BY id DESC LIMIT 50`,\n          )\n          .all();\n      }\n      res.json(rows);\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(500).json({ error: message });\n    }\n  });\n\n  // Approve a task (handled by CLI layer via createQueueApproveHandler to avoid\n  // a cross-package circular import between @doc77/core and @doc77/mcp)\n\n  // Reject a task\n  app.post('/api/queue/reject', (req: Request, res: Response) => {\n    const { task_id } = req.body;\n    if (!task_id) {\n      res.status(400).json({ error: 'task_id is required' });\n      return;\n    }\n\n    try {\n      const db = getConnection();\n      const task = db\n        .prepare('SELECT * FROM operation_queue WHERE id = ? AND status = ?')\n        .get(task_id, 'pending') as unknown;\n\n      if (!task) {\n        res.status(404).json({ error: 'Task not found or not pending' });\n        return;\n      }\n\n      db.prepare(\n        \"UPDATE operation_queue SET status = 'rejected', updated_at = datetime('now') WHERE id = ?\",\n      ).run(task_id);\n\n      res.json({ task_id, status: 'rejected' });\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(500).json({ error: message });\n    }\n  });\n\n  // === AI Test Connection ===\n\n  // (helper for reading & decrypting AI config)\n  const { getDecryptedAiConfig } = (() => {\n    const fn = (): { token: string; baseUrl: string; model: string } | null => {\n      const db = getConnection();\n      const tokenRow = db.prepare(\"SELECT value FROM config WHERE key = 'ai.token'\").get() as { value: string } | undefined;\n      const baseRow = db.prepare(\"SELECT value FROM config WHERE key = 'ai.base_url'\").get() as { value: string } | undefined;\n      const modelRow = db.prepare(\"SELECT value FROM config WHERE key = 'ai.model'\").get() as { value: string } | undefined;\n\n      if (!tokenRow?.value) return null;\n\n      const baseUrl = baseRow?.value || 'https://api.openai.com/v1';\n      const model = modelRow?.value || 'gpt-4o';\n\n      let token = tokenRow.value;\n      if (token.startsWith('{')) {\n        try {\n          const encData = JSON.parse(token);\n          if (encData.iv && encData.tag && encData.ciphertext) {\n            const authRow = db.prepare('SELECT pbkdf2_salt FROM user_auth WHERE id = 1').get() as { pbkdf2_salt: string } | undefined;\n            if (authRow?.pbkdf2_salt) {\n              const encKey = crypto.deriveKey('doc77-config-key', Buffer.from(authRow.pbkdf2_salt, 'hex'));\n              token = crypto.decrypt(encData, encKey);\n            }\n          }\n        } catch { /* not encrypted */ }\n      }\n\n      return { token, baseUrl, model };\n    };\n    return { getDecryptedAiConfig: fn };\n  })();\n\n  app.post('/api/ai/test', async (req: Request, res: Response) => {\n    try {\n      const cfg = getDecryptedAiConfig();\n      if (!cfg) {\n        res.json({ ok: false, error: '请先配置 Base URL 和 API Token' });\n        return;\n      }\n      const url = cfg.baseUrl.replace(/\\/+$/, '') + '/chat/completions';\n      const resp = await fetch(url, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${cfg.token}` },\n        body: JSON.stringify({ model: cfg.model, messages: [{ role: 'user', content: 'Hi' }], max_tokens: 5 }),\n        signal: AbortSignal.timeout(15000),\n      });\n      if (resp.ok) {\n        res.json({ ok: true, status: resp.status });\n      } else {\n        const errText = await resp.text().catch(() => '');\n        let errMsg = `HTTP ${resp.status}`;\n        try { const errJson = JSON.parse(errText); errMsg = errJson.error?.message || errMsg; } catch {}\n        res.json({ ok: false, error: errMsg });\n      }\n    } catch (e: unknown) {\n      const message = e instanceof Error ? e.message : 'Unknown error';\n      res.json({ ok: false, error: `网络错误: ${message}` });\n    }\n  });\n\n  // === AI Session Reset ===\n\n  app.post('/api/ai/reset', (req: Request, res: Response) => {\n    const { session_id } = req.body;\n    if (!session_id) {\n      res.status(400).json({ error: 'session_id is required' });\n      return;\n    }\n    const ok = resetSession(session_id);\n    res.json({ ok });\n  });\n\n  // === AI Quick Capabilities ===\n\n  app.post('/api/ai/summarize', async (req: Request, res: Response) => {\n    const { project_id, file_path } = req.body;\n    if (!project_id || !file_path) {\n      res.status(400).json({ error: 'project_id and file_path are required' });\n      return;\n    }\n    try {\n      const db = getConnection();\n      const project = db.prepare('SELECT path FROM projects WHERE id = ?').get(project_id) as\n        { path: string } | undefined;\n      if (!project) { res.status(404).json({ error: 'Project not found' }); return; }\n      const absPath = validatePath(project.path, file_path);\n      const content = readFile(absPath);\n      const summary = `文档摘要（${file_path}）：\\n该文档包含 ${content.split('\\n').length} 行内容，共 ${content.length} 个字符。主要涉及技术规范和设计文档。`;\n      res.json({ summary });\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(500).json({ error: message });\n    }\n  });\n\n  // === AI Chat API (handled by CLI layer via createAIChatHandler to avoid\n  //     a circular dependency between @doc77/core and @doc77/ai) ===\n\n  // === Search API ===\n\n  /**\n   * Recursively search text files under `dirPath` for `keyword`.\n   * Pure Node.js — no external dependencies (grep, ripgrep, etc.).\n   * Cross-platform: works on Windows, macOS, and Linux.\n   */\n  function searchInFiles(\n    dirPath: string,\n    keyword: string,\n    maxResults = 50,\n  ): Array<{ file: string; line: number; content: string }> {\n    const results: Array<{ file: string; line: number; content: string }> = [];\n    const lowerKey = keyword.toLowerCase();\n    const SKIP_DIRS = new Set([\n      'node_modules', '.git', '.svn', '__pycache__', '.venv', 'venv',\n      'dist', '.cache', '.next', '.nuxt', 'build', 'target',\n    ]);\n    const SKIP_EXT = new Set([\n      '.exe', '.dll', '.so', '.dylib', '.bin', '.class', '.jar', '.war', '.o', '.wasm',\n      '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.ico', '.bmp', '.avif',\n      '.mp4', '.mp3', '.wav', '.ogg', '.flac', '.aac', '.mov', '.avi', '.mkv',\n      '.zip', '.tar', '.gz', '.7z', '.rar', '.bz2', '.xz', '.zst',\n      '.pdf', '.docx', '.xlsx', '.pptx', '.epub', '.mobi',\n      '.ttf', '.woff', '.woff2', '.otf', '.eot',\n      '.db', '.sqlite', '.sqlite3',\n    ]);\n\n    function walk(dir: string) {\n      if (results.length >= maxResults) return;\n      let entries: fs.Dirent[];\n      try { entries = fs.readdirSync(dir, { withFileTypes: true }); }\n      catch { return; }\n\n      for (const entry of entries) {\n        if (results.length >= maxResults) return;\n        if (entry.name.startsWith('.')) continue; // skip dotfiles/dotdirs\n\n        const fullPath = path.join(dir, entry.name);\n        if (entry.isDirectory()) {\n          if (!SKIP_DIRS.has(entry.name)) walk(fullPath);\n        } else if (entry.isFile()) {\n          const ext = path.extname(entry.name).toLowerCase();\n          if (SKIP_EXT.has(ext)) continue;\n          try {\n            const content = fs.readFileSync(fullPath, 'utf-8');\n            const lines = content.split('\\n');\n            for (let i = 0; i < lines.length && results.length < maxResults; i++) {\n              if (lines[i].toLowerCase().includes(lowerKey)) {\n                results.push({\n                  file: fullPath.slice(dirPath.length + 1), // relative path\n                  line: i + 1,\n                  content: lines[i].substring(0, 200),\n                });\n              }\n            }\n          } catch { /* skip unreadable / binary files */ }\n        }\n      }\n    }\n\n    walk(dirPath);\n    return results;\n  }\n\n  // Full-text search (cross-platform, pure Node.js — no grep dependency)\n  app.get('/api/search', (req: Request, res: Response) => {\n    const keyword = (req.query.q as string) || '';\n    const projectId = parseInt(req.query.project_id as string, 10);\n\n    if (!keyword || keyword.length < 2) {\n      res.status(400).json({ error: 'keyword must be at least 2 characters' });\n      return;\n    }\n    if (isNaN(projectId)) {\n      res.status(400).json({ error: 'project_id is required' });\n      return;\n    }\n\n    try {\n      const db = getConnection();\n      const project = db\n        .prepare('SELECT path FROM projects WHERE id = ?')\n        .get(projectId) as { path: string } | undefined;\n\n      if (!project) {\n        res.status(404).json({ error: 'Project not found' });\n        return;\n      }\n\n      const matches = searchInFiles(project.path, keyword, 50);\n      res.json({ keyword, matches });\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(500).json({ error: message });\n    }\n  });\n\n  // === Auth API ===\n\n  // Check auth status\n  app.get('/api/auth/status', (_req: Request, res: Response) => {\n    try {\n      const db = getConnection();\n      const row = db.prepare('SELECT password_hash FROM user_auth WHERE id = 1').get() as { password_hash: string } | undefined;\n      res.json({ hasPassword: !!row?.password_hash });\n    } catch { res.json({ hasPassword: false }); }\n  });\n\n  // Setup password (first time)\n  app.post('/api/auth/setup', (req: Request, res: Response) => {\n    const { password } = req.body;\n    if (!password || password.length < 6) {\n      res.status(400).json({ error: '密码至少6位' });\n      return;\n    }\n    try {\n      const db = getConnection();\n      const existing = db.prepare('SELECT password_hash FROM user_auth WHERE id = 1').get() as { password_hash: string } | undefined;\n      if (existing?.password_hash) {\n        res.status(409).json({ error: '密码已设置，请使用修改密码功能' });\n        return;\n      }\n      const hash = crypto.hashPassword(password);\n      const encSalt = crypto.generateSalt().toString('hex');\n      const pbkdf2Salt = crypto.generateSalt().toString('hex');\n      db.prepare('INSERT OR REPLACE INTO user_auth (id, password_hash, encryption_salt, pbkdf2_salt) VALUES (1, ?, ?, ?)').run(hash, encSalt, pbkdf2Salt);\n      res.json({ ok: true });\n    } catch (e: unknown) { res.status(500).json({ error: (e as Error).message }); }\n  });\n\n  // Login\n  app.post('/api/auth/login', (req: Request, res: Response) => {\n    const { password } = req.body;\n    if (!password) { res.status(400).json({ error: '密码不能为空' }); return; }\n    try {\n      const db = getConnection();\n      const row = db.prepare('SELECT * FROM user_auth WHERE id = 1').get() as Record<string, unknown> | undefined;\n      if (!row?.password_hash) { res.status(404).json({ error: '未设置密码' }); return; }\n      if (row.locked_until && new Date(row.locked_until as string) > new Date()) {\n        res.status(423).json({ error: '账户已锁定，请稍后再试' }); return;\n      }\n      if (!crypto.verifyPassword(password, row.password_hash as string)) {\n        const fails = ((row.failed_attempts as number) || 0) + 1;\n        if (fails >= 5) {\n          db.prepare(\"UPDATE user_auth SET failed_attempts=0, locked_until=datetime('now','+15 minutes') WHERE id=1\").run();\n          res.status(423).json({ error: '密码错误次数过多，已锁定15分钟' });\n        } else {\n          db.prepare('UPDATE user_auth SET failed_attempts=? WHERE id=1').run(fails);\n          res.status(401).json({ error: `密码错误（${fails}/5）` });\n        }\n        return;\n      }\n      db.prepare('UPDATE user_auth SET failed_attempts=0, locked_until=NULL WHERE id=1').run();\n      res.json({ ok: true, token: 'session-' + Date.now() });\n    } catch (e: unknown) { res.status(500).json({ error: (e as Error).message }); }\n  });\n\n  // === Config API ===\n\n  app.get('/api/config', (_req: Request, res: Response) => {\n    try {\n      const db = getConnection();\n      const rows = db.prepare('SELECT key, value FROM config ORDER BY key').all() as { key: string; value: string }[];\n      const result: Record<string, string> = {};\n      for (const r of rows) {\n        result[r.key] = crypto.isSensitiveKey(r.key) ? crypto.maskSensitive(r.value) : r.value;\n      }\n      res.json(result);\n    } catch (e: unknown) { res.status(500).json({ error: (e as Error).message }); }\n  });\n\n  app.put('/api/config', (req: Request, res: Response) => {\n    const { key, value } = req.body;\n    if (!key || value === undefined) { res.status(400).json({ error: 'key and value required' }); return; }\n    try {\n      const db = getConnection();\n      let storeValue = value;\n      // Encrypt sensitive fields\n      if (crypto.isSensitiveKey(key)) {\n        const authRow = db.prepare('SELECT pbkdf2_salt FROM user_auth WHERE id = 1').get() as { pbkdf2_salt: string } | undefined;\n        if (authRow?.pbkdf2_salt) {\n          // Use a fixed passphrase for config encryption (derived from user password if available)\n          // For now, store encrypted with a local key\n          const encKey = crypto.deriveKey('doc77-config-key', Buffer.from(authRow.pbkdf2_salt, 'hex'));\n          const enc = crypto.encrypt(value, encKey);\n          storeValue = JSON.stringify(enc);\n        }\n      }\n      db.prepare('INSERT INTO config (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, storeValue);\n      res.json({ ok: true, key });\n    } catch (e: unknown) { res.status(500).json({ error: (e as Error).message }); }\n  });\n\n  // --- Error handler ---\n  app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {\n    console.error('[server error]', err.message);\n    res.status(500).json({\n      error: process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message,\n    });\n  });\n\n  return app;\n}\n\n/**\n * Map a file extension to a human-readable category label for the unsupported-format UI.\n */\nfunction getFileCategory(filePath: string): string {\n  const ext = path.extname(filePath).toLowerCase();\n  const map: Record<string, string> = {\n    '.mp4':'video','.avi':'video','.mov':'video','.mkv':'video','.webm':'video','.wmv':'video','.flv':'video','.m4v':'video',\n    '.mp3':'audio','.wav':'audio','.ogg':'audio','.flac':'audio','.aac':'audio','.wma':'audio','.m4a':'audio','.opus':'audio',\n    '.zip':'archive','.tar':'archive','.gz':'archive','.7z':'archive','.rar':'archive','.bz2':'archive','.xz':'archive','.zst':'archive',\n    '.ttf':'font','.woff':'font','.woff2':'font','.otf':'font','.eot':'font',\n    '.db':'database','.sqlite':'database','.sqlite3':'database','.mdb':'database','.accdb':'database',\n    '.psd':'design','.ai':'design','.sketch':'design','.fig':'design','.xd':'design',\n    '.exe':'binary','.dll':'binary','.so':'binary','.dylib':'binary','.bin':'binary','.class':'binary','.jar':'binary','.war':'binary','.o':'binary','.wasm':'binary',\n    '.shp':'gis','.shx':'gis','.dbf':'gis','.obj':'3d','.stl':'3d','.glb':'3d','.gltf':'3d',\n    '.epub':'ebook','.mobi':'ebook','.pages':'document','.numbers':'spreadsheet','.key':'presentation','.ppt':'presentation','.pptx':'presentation',\n  };\n  return map[ext] || 'unknown';\n}\n\n/**\n * Create a POST /api/queue/approve handler.\n *\n * Accepts the executor function as a dependency to avoid a circular\n * cross-package import between @doc77/core and @doc77/mcp.\n *\n * Usage (in CLI layer):\n *   app.post('/api/queue/approve', createQueueApproveHandler(executeApprovedTasks));\n */\nexport function createQueueApproveHandler(\n  executeApprovedTasks: (\n    projectId: number,\n    taskIds: string[],\n  ) => Promise<{ success: boolean; errors: string[] }>,\n) {\n  return async (req: Request, res: Response) => {\n    const { task_id } = req.body;\n    if (!task_id) {\n      res.status(400).json({ error: 'task_id is required' });\n      return;\n    }\n\n    try {\n      const db = getConnection();\n      const task = db\n        .prepare('SELECT * FROM operation_queue WHERE id = ? AND status = ?')\n        .get(task_id, 'pending') as\n        | { id: number; project_id: number; operation_type: string; status: string }\n        | undefined;\n\n      if (!task) {\n        res.status(404).json({ error: 'Task not found or not pending' });\n        return;\n      }\n\n      db.prepare(\n        \"UPDATE operation_queue SET status = 'approved', updated_at = datetime('now') WHERE id = ?\",\n      ).run(task_id);\n\n      // Fire-and-forget execution\n      executeApprovedTasks(task.project_id, [String(task.id)]).catch((e: Error) =>\n        console.error('[executor]', e.message),\n      );\n\n      res.json({ task_id, status: 'approved' });\n    } catch (err: unknown) {\n      const message = err instanceof Error ? err.message : 'Unknown error';\n      res.status(500).json({ error: message });\n    }\n  };\n}\n\n/**\n * Create POST /api/ai/chat handler with SSE streaming + tool-use.\n *\n * Accepts AI dependencies as parameters to avoid a circular dependency\n * between @doc77/core and @doc77/ai. Register from the CLI layer.\n *\n * Usage (in CLI start command):\n *   const { AiProvider, DocAgent, READ_TOOLS } = await import('@doc77/ai');\n *   app.post('/api/ai/chat', createAIChatHandler({ AiProvider, DocAgent, READ_TOOLS }));\n */\nexport function createAIChatHandler(deps: {\n  AiProvider: new (config: { apiKey: string; baseUrl: string; model: string }) => SessionAgent;\n  DocAgent: new (config: {\n    provider: SessionAgent;\n    model: string;\n    tools: unknown[];\n    executeTool: (name: string, args: Record<string, unknown>) => Promise<string>;\n    maxSteps: number;\n  }) => SessionAgent & {\n    hasContext: boolean;\n    addContext(ctx: string): void;\n    chatStream(message: string): AsyncIterable<\n      { type: 'token'; content: string } | { type: 'tool_call'; name: string; arguments: string; status: string } | { type: 'done' } | { type: 'error'; message: string }\n    >;\n  };\n  READ_TOOLS: unknown[];\n}) {\n  const { AiProvider, DocAgent, READ_TOOLS } = deps;\n\n  return async (req: Request, res: Response) => {\n    const { message, project_id, session_id } = req.body;\n    if (!message) {\n      res.status(400).json({ error: 'message is required' });\n      return;\n    }\n\n    const { getDecryptedAiConfig } = (() => {\n      type AiConfig = { token: string; baseUrl: string; model: string } | null;\n      const fn = (): AiConfig => {\n        const db = getConnection();\n        const tokenRow = db.prepare(\"SELECT value FROM config WHERE key = 'ai.token'\").get() as { value: string } | undefined;\n        const baseRow = db.prepare(\"SELECT value FROM config WHERE key = 'ai.base_url'\").get() as { value: string } | undefined;\n        const modelRow = db.prepare(\"SELECT value FROM config WHERE key = 'ai.model'\").get() as { value: string } | undefined;\n        if (!tokenRow?.value) return null;\n        const baseUrl = baseRow?.value || 'https://api.openai.com/v1';\n        const model = modelRow?.value || 'gpt-4o';\n        let token = tokenRow.value;\n        if (token.startsWith('{')) {\n          try {\n            const encData = JSON.parse(token);\n            if (encData.iv && encData.tag && encData.ciphertext) {\n              const authRow = db.prepare('SELECT pbkdf2_salt FROM user_auth WHERE id = 1').get() as { pbkdf2_salt: string } | undefined;\n              if (authRow?.pbkdf2_salt) {\n                const encKey = crypto.deriveKey('doc77-config-key', Buffer.from(authRow.pbkdf2_salt, 'hex'));\n                token = crypto.decrypt(encData, encKey);\n              }\n            }\n          } catch { /* not encrypted */ }\n        }\n        return { token, baseUrl, model };\n      };\n      return { getDecryptedAiConfig: fn };\n    })();\n\n    const cfg = getDecryptedAiConfig();\n    if (!cfg) {\n      res.status(400).json({ error: 'AI_NOT_CONFIGURED', message: '请先在设置中配置 AI 模型和 API Token' });\n      return;\n    }\n\n    res.writeHead(200, {\n      'Content-Type': 'text/event-stream',\n      'Cache-Control': 'no-cache',\n      Connection: 'keep-alive',\n      'X-Accel-Buffering': 'no',\n    });\n\n    const send = (event: string, data: unknown) => {\n      res.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n    };\n\n    try {\n      const executeTool = async (name: string, args: Record<string, unknown>): Promise<string> => {\n        const pid = (args.project_id as number) || project_id;\n        if (!pid) return 'Error: project_id is required';\n        switch (name) {\n          case 'list_files': {\n            const dirPath = (args.dir_path as string) || '';\n            const result = scanDirectory(pid, dirPath);\n            const entries = result.entries.slice(0, 50);\n            if (entries.length === 0) return `目录 \"${dirPath || '/'}\" 为空或不存在`;\n            return entries.map(e => `${e.type === 'directory' ? '📁' : '📄'} ${e.name} (${e.type}, ${e.size ?? 'N/A'} bytes)`).join('\\n');\n          }\n          case 'read_file': {\n            const filePath = args.file_path as string;\n            if (!filePath) return 'Error: file_path is required';\n            const fileName = filePath.split('/').pop() || filePath;\n            if (isSensitiveFile(fileName)) return `Error: Access denied — \"${fileName}\" is a sensitive file`;\n            try {\n              const db = getConnection();\n              const project = db.prepare('SELECT path FROM projects WHERE id = ?').get(pid) as { path: string } | undefined;\n              if (!project) return 'Error: Project not found';\n              const absPath = validatePath(project.path, filePath);\n              const content = readFile(absPath);\n              return content.length > 4000\n                ? content.slice(0, 4000) + `\\n\\n[... truncated, total ${content.length} chars]`\n                : content;\n            } catch (e: unknown) { return `Error: ${e instanceof Error ? e.message : 'Unknown'}`; }\n          }\n          case 'get_file_info': {\n            const filePath = args.file_path as string;\n            if (!filePath) return 'Error: file_path is required';\n            try {\n              const db = getConnection();\n              const project = db.prepare('SELECT path FROM projects WHERE id = ?').get(pid) as { path: string } | undefined;\n              if (!project) return 'Error: Project not found';\n              const absPath = validatePath(project.path, filePath);\n              const stats = fs.statSync(absPath);\n              return `File: ${filePath}\\nType: ${stats.isDirectory() ? 'directory' : 'file'}\\nSize: ${stats.size} bytes\\nModified: ${stats.mtime.toISOString()}`;\n            } catch (e: unknown) { return `Error: ${e instanceof Error ? e.message : 'Unknown'}`; }\n          }\n          default: return `Error: Unknown tool \"${name}\"`;\n        }\n      };\n\n      const provider = new AiProvider({ apiKey: cfg.token, baseUrl: cfg.baseUrl, model: cfg.model });\n      const { sessionId: sid, agent } = getOrCreateSession(\n        session_id,\n        () => new DocAgent({ provider, model: cfg.model, tools: READ_TOOLS as any[], executeTool, maxSteps: 5 }) as any,\n        project_id,\n      );\n\n      if (project_id && !(agent as any).hasContext) {\n        try {\n          const root = scanDirectory(project_id, '');\n          const fileList = root.entries.slice(0, 30).map(e => `${e.type === 'directory' ? '📁' : '📄'} ${e.name}`).join('\\n');\n          const proj = (() => {\n            const db = getConnection();\n            return db.prepare('SELECT name, path FROM projects WHERE id = ?').get(project_id) as { name: string; path: string } | undefined;\n          })();\n          (agent as any).addContext(`当前项目: ${proj?.name || 'Unknown'} (路径: ${proj?.path || 'N/A'})\\n根目录内容:\\n${fileList || '(空目录)'}`);\n        } catch { /* non-fatal */ }\n      }\n\n      send('session', { session_id: sid });\n\n      for await (const chunk of (agent as any).chatStream(message)) {\n        switch (chunk.type) {\n          case 'token': send('token', { text: chunk.content }); break;\n          case 'tool_call': send('tool_call', { name: chunk.name, arguments: chunk.arguments, status: 'executing' }); break;\n          case 'done': send('done', {}); break;\n          case 'error': send('error', { message: chunk.message }); break;\n        }\n      }\n    } catch (e: unknown) {\n      const msg = e instanceof Error ? e.message : 'Unknown error';\n      send('error', { message: `AI 服务异常: ${msg}` });\n    }\n\n    res.end();\n  };\n}\n","import { execFile } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\n\n/**\n * Detect WSL by checking /proc/version.\n */\nfunction isWSL(): boolean {\n  try {\n    return /microsoft|wsl/i.test(readFileSync('/proc/version', 'utf-8'));\n  } catch { return false; }\n}\n\n/**\n * Detect if we have a graphical display (X11 or Wayland).\n */\nfunction hasDisplay(): boolean {\n  return !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);\n}\n\n/**\n * Open a native OS directory picker dialog.\n * Returns the selected directory path, or null if cancelled / unavailable.\n */\nexport function openDirectoryDialog(): Promise<string | null> {\n  if (isWSL()) {\n    // WSL: try Windows interop first, then X11 fallbacks\n    return wslDialog();\n  }\n\n  const platform = process.platform;\n  if (platform === 'linux') return linuxDialog();\n  if (platform === 'darwin') return macDialog();\n  if (platform === 'win32') return winDialog();\n\n  return Promise.resolve(null);\n}\n\n// ══════════ WSL ══════════\n\nasync function wslDialog(): Promise<string | null> {\n  // Strategy A: Try Windows interop (works when WSL binfmt is configured)\n  const winPath = await tryWindowsInterop();\n  if (winPath) {\n    return winToWsl(winPath);\n  }\n\n  // Strategy B: Try Linux GUI tools (works when X11 forwarding is set up)\n  if (hasDisplay()) {\n    return linuxDialog();\n  }\n\n  // Strategy C: Neither Windows interop nor X11 — return null\n  // Frontend should fall back to browser's webkitdirectory picker\n  return null;\n}\n\nasync function tryWindowsInterop(): Promise<string | null> {\n  // Try different methods to invoke PowerShell from WSL\n  const methods: Array<() => Promise<string | null>> = [\n    () => runPowershell('powershell.exe'),\n    () => runViaCmd(),\n  ];\n\n  for (const method of methods) {\n    const result = await method();\n    if (result) return result;\n  }\n  return null;\n}\n\nfunction runPowershell(exePath: string): Promise<string | null> {\n  const psScript = `Add-Type -AssemblyName System.Windows.Forms;$d=New-Object System.Windows.Forms.FolderBrowserDialog;$d.Description='Select project directory';$d.ShowNewFolderButton=1;if($d.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){Write-Output $d.SelectedPath}`;\n  return new Promise((resolve) => {\n    execFile(exePath, ['-NoProfile', '-Command', psScript], { timeout: 120000, windowsHide: true }, (err, stdout) => {\n      if (err || !stdout) { resolve(null); return; }\n      const s = stdout.trim();\n      resolve(s || null);\n    });\n  });\n}\n\nfunction runViaCmd(): Promise<string | null> {\n  return new Promise((resolve) => {\n    const psScript = `Add-Type -AssemblyName System.Windows.Forms;$d=New-Object System.Windows.Forms.FolderBrowserDialog;$d.Description='Select project directory';if($d.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){Write-Output $d.SelectedPath}`;\n    execFile('cmd.exe', ['/c', 'powershell.exe', '-NoProfile', '-Command', psScript], { timeout: 120000, windowsHide: true }, (err, stdout) => {\n      if (err || !stdout) { resolve(null); return; }\n      const s = stdout.trim();\n      resolve(s || null);\n    });\n  });\n}\n\nfunction winToWsl(winPath: string): string | null {\n  return new Promise((resolve) => {\n    execFile('wslpath', ['-u', winPath], { timeout: 5000 }, (err, stdout) => {\n      if (err || !stdout) {\n        // Manual fallback: C:\\foo\\bar → /mnt/c/foo/bar\n        const match = winPath.match(/^([A-Z]):\\\\(.*)$/i);\n        if (match) {\n          resolve(`/mnt/${match[1].toLowerCase()}/${match[2].replace(/\\\\/g, '/')}`);\n        } else {\n          resolve(null);\n        }\n        return;\n      }\n      resolve(stdout.trim());\n    });\n  });\n}\n\n// ══════════ Linux ══════════\n\nfunction linuxDialog(): Promise<string | null> {\n  return new Promise((resolve) => {\n    // Try zenity first\n    execFile('zenity', ['--file-selection', '--directory', '--title=选择项目目录'], { timeout: 120000 }, (err, stdout) => {\n      if (!err && stdout) { resolve(stdout.trim()); return; }\n      // Fallback to kdialog\n      execFile('kdialog', ['--getexistingdirectory', process.env.HOME || '/'], { timeout: 120000 }, (err2, stdout2) => {\n        if (!err2 && stdout2) { resolve(stdout2.trim()); return; }\n        // Fallback to yad\n        execFile('yad', ['--file-selection', '--directory', '--title=选择项目目录'], { timeout: 120000 }, (err3, stdout3) => {\n          if (!err3 && stdout3) { resolve(stdout3.trim()); return; }\n          // Fallback to python3 tkinter\n          tryTkDialog().then(resolve);\n        });\n      });\n    });\n  });\n}\n\nfunction tryTkDialog(): Promise<string | null> {\n  return new Promise((resolve) => {\n    const pyScript = 'import tkinter.filedialog as fd, tkinter as tk; root=tk.Tk(); root.withdraw(); print(fd.askdirectory(title=\"选择项目目录\") or \"\")';\n    execFile('python3', ['-c', pyScript], { timeout: 120000 }, (err, stdout) => {\n      if (err || !stdout) { resolve(null); return; }\n      const s = stdout.trim();\n      resolve(s || null);\n    });\n  });\n}\n\n// ══════════ macOS ══════════\n\nfunction macDialog(): Promise<string | null> {\n  return new Promise((resolve) => {\n    execFile('osascript', ['-e', 'POSIX path of (choose folder with prompt \"选择项目目录\")'], { timeout: 120000 }, (err, stdout) => {\n      if (err || !stdout) { resolve(null); return; }\n      resolve(stdout.trim());\n    });\n  });\n}\n\n// ══════════ Windows ══════════\n\nfunction winDialog(): Promise<string | null> {\n  return new Promise((resolve) => {\n    const psScript = `Add-Type -AssemblyName System.Windows.Forms;$d=New-Object System.Windows.Forms.FolderBrowserDialog;$d.Description='选择项目目录';$d.ShowNewFolderButton=1;if($d.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){Write-Output $d.SelectedPath}`;\n    execFile('powershell', ['-NoProfile', '-Command', psScript], { timeout: 30000, windowsHide: true }, (err, stdout) => {\n      if (err || !stdout) { resolve(null); return; }\n      const s = stdout.trim();\n      resolve(s || null);\n    });\n  });\n}\n","import { marked } from 'marked';\n\n/**\n * Render Markdown content to HTML with theme-aware code blocks.\n * Uses marked with GFM (GitHub Flavored Markdown) enabled.\n * Code blocks are wrapped in .doc77-code-block for theme CSS variable support.\n */\nexport function renderMarkdown(content: string): string {\n  if (!content) return '';\n\n  // Custom renderer to wrap code blocks with theme class\n  const renderer = new marked.Renderer();\n  renderer.code = ({ text, lang }: { text: string; lang?: string }) => {\n    const langClass = lang ? ` class=\"language-${lang}\"` : '';\n    return `<div class=\"doc77-code-block\"><pre><code${langClass}>${text}</code></pre></div>`;\n  };\n\n  return marked.parse(content, {\n    gfm: true,\n    breaks: false,\n    renderer,\n  }) as string;\n}\n","/**\n * Generate an HTML wrapper for client-side Mermaid.js rendering.\n * The browser will use mermaid.js to render the diagram on load.\n */\nexport function renderMermaid(content: string): string {\n  return `<pre class=\"mermaid\">\\n${content}\\n</pre>`;\n}\n","/**\n * Generate an HTML wrapper for client-side PDF.js rendering.\n * Provides a canvas-based viewer with page navigation.\n *\n * @param fileUrl - The URL to fetch the PDF file\n */\nexport function renderPdf(fileUrl: string): string {\n  const escapedUrl = escapeHtml(fileUrl);\n  return `\n<div class=\"pdf-viewer\" data-pdf-url=\"${escapedUrl}\">\n  <div class=\"pdf-toolbar\">\n    <button class=\"pdf-prev\" onclick=\"this.closest('.pdf-viewer').dispatchEvent(new CustomEvent('pdf-prev'))\">◀ 上一页</button>\n    <span class=\"pdf-page-info\">第 <span class=\"pdf-current-page\">1</span> / <span class=\"pdf-total-pages\">?</span> 页</span>\n    <button class=\"pdf-next\" onclick=\"this.closest('.pdf-viewer').dispatchEvent(new CustomEvent('pdf-next'))\">下一页 ▶</button>\n  </div>\n  <div class=\"pdf-canvas-wrapper\">\n    <canvas class=\"pdf-canvas\"></canvas>\n  </div>\n  <div class=\"pdf-loading\">加载 PDF 中...</div>\n</div>`;\n}\n\nfunction escapeHtml(str: string): string {\n  return str\n    .replace(/&/g, '&amp;')\n    .replace(/\"/g, '&quot;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;');\n}\n","/**\n * Generate an HTML img tag for image preview.\n *\n * @param url - The URL to fetch the image\n * @param alt - Alt text (typically the filename)\n */\nexport function renderImage(url: string, alt: string): string {\n  const escapedUrl = escapeHtml(url);\n  const escapedAlt = escapeHtml(alt);\n  return `<img src=\"${escapedUrl}\" alt=\"${escapedAlt}\" class=\"preview-image\" loading=\"lazy\" />`;\n}\n\nfunction escapeHtml(str: string): string {\n  return str\n    .replace(/&/g, '&amp;')\n    .replace(/\"/g, '&quot;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;');\n}\n","/**\n * Generate an HTML wrapper for syntax-highlighted code display.\n * Uses CSS classes for theme-aware styling (--bg-code / --text-code).\n *\n * @param content - The raw code content\n * @param language - Programming language identifier\n */\nexport function renderCode(content: string, language: string): string {\n  const escapedContent = escapeHtml(content);\n  return `<div class=\"doc77-code-block\"><pre><code class=\"language-${escapeHtml(language)}\">${escapedContent}</code></pre></div>`;\n}\n\nfunction escapeHtml(str: string): string {\n  return str\n    .replace(/&/g, '&amp;')\n    .replace(/\"/g, '&quot;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;');\n}\n","export { renderMarkdown } from './markdown.js';\nexport { renderMermaid } from './mermaid.js';\nexport { renderPdf } from './pdf.js';\nexport { renderImage } from './image.js';\nexport { renderCode } from './code.js';\n\n/**\n * File extension to renderer type mapping.\n */\nconst EXTENSION_MAP: Record<string, string> = {\n  // Markdown\n  '.md': 'markdown',\n  '.mdx': 'markdown',\n  '.markdown': 'markdown',\n\n  // Mermaid\n  '.mermaid': 'mermaid',\n  '.mmd': 'mermaid',\n\n  // PDF\n  '.pdf': 'pdf',\n\n  // Images\n  '.png': 'image',\n  '.jpg': 'image',\n  '.jpeg': 'image',\n  '.gif': 'image',\n  '.svg': 'image',\n  '.webp': 'image',\n  '.bmp': 'image',\n  '.ico': 'image',\n  '.avif': 'image',\n\n  // Office documents\n  '.docx': 'docx',\n  '.xlsx': 'xlsx',\n\n  // Code (common source file extensions)\n  '.ts': 'code',\n  '.tsx': 'code',\n  '.js': 'code',\n  '.jsx': 'code',\n  '.py': 'code',\n  '.rb': 'code',\n  '.go': 'code',\n  '.rs': 'code',\n  '.java': 'code',\n  '.c': 'code',\n  '.cpp': 'code',\n  '.h': 'code',\n  '.hpp': 'code',\n  '.cs': 'code',\n  '.swift': 'code',\n  '.kt': 'code',\n  '.scala': 'code',\n  '.php': 'code',\n  '.sh': 'code',\n  '.bash': 'code',\n  '.zsh': 'code',\n  '.fish': 'code',\n  '.ps1': 'code',\n  '.bat': 'code',\n  '.sql': 'code',\n  '.html': 'code',\n  '.css': 'code',\n  '.scss': 'code',\n  '.less': 'code',\n  '.json': 'code',\n  '.xml': 'code',\n  '.yaml': 'code',\n  '.yml': 'code',\n  '.toml': 'code',\n  '.ini': 'code',\n  '.cfg': 'code',\n  '.conf': 'code',\n  '.env': 'code',\n  '.gitignore': 'code',\n  '.dockerignore': 'code',\n  '.editorconfig': 'code',\n  Dockerfile: 'code',\n  Makefile: 'code',\n};\n\n/**\n * Unsupportable formats — known binary/proprietary extensions that should\n * show a file-info card rather than attempting any content render.\n */\nexport const UNSUPPORTED_EXTENSIONS = new Set([\n  // Video\n  '.mp4','.avi','.mov','.mkv','.webm','.wmv','.flv','.m4v',\n  // Audio\n  '.mp3','.wav','.ogg','.flac','.aac','.wma','.m4a','.opus',\n  // Archives\n  '.zip','.tar','.gz','.7z','.rar','.bz2','.xz','.zst',\n  // GIS / 3D\n  '.shp','.shx','.dbf','.geojson','.geotiff','.obj','.stl','.glb','.gltf',\n  // Fonts\n  '.ttf','.woff','.woff2','.otf','.eot',\n  // Binaries\n  '.exe','.dll','.so','.dylib','.bin','.dat','.class','.jar','.war',\n  '.o','.a','.lib','.pdb','.obj','.wasm',\n  // Databases\n  '.db','.sqlite','.sqlite3','.mdb','.accdb',\n  // Design\n  '.psd','.ai','.sketch','.fig','.xd',\n  // Other proprietary\n  '.epub','.mobi','.pages','.numbers','.key',\n  '.ppt','.pptx',\n]);\n\n/**\n * File size limits by format (bytes). Exceeding these triggers truncation or rejection.\n * 0 means no limit.\n */\nexport const FORMAT_SIZE_LIMITS: Record<string, number> = {\n  markdown: 5 * 1024 * 1024,\n  mermaid: 5 * 1024 * 1024,\n  code: 5 * 1024 * 1024,\n  text: 5 * 1024 * 1024,\n  docx: 50 * 1024 * 1024,\n  xlsx: 10 * 1024 * 1024,\n  pdf: 0,       // unlimited — served via raw/stream\n  image: 0,     // unlimited — served via raw\n};\n\n/**\n * Determine the appropriate renderer type for a given filename.\n */\nexport function getRendererForFile(filename: string): string {\n  // Check for exact match first (e.g., Dockerfile, Makefile)\n  const basename = filename.split('/').pop() || filename;\n  if (EXTENSION_MAP[basename]) {\n    return EXTENSION_MAP[basename];\n  }\n\n  // Check by extension\n  const dotIndex = basename.lastIndexOf('.');\n  if (dotIndex === -1) return 'text';\n\n  const ext = basename.slice(dotIndex).toLowerCase();\n  return EXTENSION_MAP[ext] || 'text';\n}\n\n/**\n * Check if a filename's extension is in the unsupported list.\n */\nexport function isUnsupportedFormat(filename: string): boolean {\n  const basename = filename.split('/').pop() || filename;\n  const dotIndex = basename.lastIndexOf('.');\n  if (dotIndex === -1) return false;\n  return UNSUPPORTED_EXTENSIONS.has(basename.slice(dotIndex).toLowerCase());\n}\n","/**\n * Generic session agent interface — avoids direct dependency on @doc77/ai.\n */\nexport interface SessionAgent {\n  hasContext?: boolean;\n  runWithTools(message: string): Promise<string>;\n  reset(): void;\n  getHistory(): { role: string; content: string }[];\n}\n\ninterface SessionEntry {\n  agent: SessionAgent;\n  projectId: number | null;\n  lastAccess: number;\n}\n\nconst sessions = new Map<string, SessionEntry>();\n\nconst SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes\n\n/**\n * Get or create a session with the given agent factory.\n */\nexport function getOrCreateSession(\n  sessionId: string | undefined,\n  agentFactory: () => SessionAgent,\n  projectId?: number,\n): { sessionId: string; agent: SessionAgent } {\n  // Cleanup expired sessions\n  const now = Date.now();\n  for (const [id, entry] of sessions) {\n    if (now - entry.lastAccess > SESSION_TTL_MS) {\n      sessions.delete(id);\n    }\n  }\n\n  // Reuse existing session\n  if (sessionId && sessions.has(sessionId)) {\n    const entry = sessions.get(sessionId)!;\n    entry.lastAccess = now;\n    return { sessionId, agent: entry.agent };\n  }\n\n  // Create new session\n  const newId = sessionId || `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n  const agent = agentFactory();\n  sessions.set(newId, { agent, projectId: projectId ?? null, lastAccess: now });\n  return { sessionId: newId, agent };\n}\n\n/** Reset (clear history of) a specific session. */\nexport function resetSession(sessionId: string): boolean {\n  const entry = sessions.get(sessionId);\n  if (!entry) return false;\n  entry.agent.reset();\n  return true;\n}\n\n/** Delete a session. */\nexport function deleteSession(sessionId: string): boolean {\n  return sessions.delete(sessionId);\n}\n\n/** Get conversation history for a session. */\nexport function getSessionHistory(sessionId: string): { role: string; content: string }[] | null {\n  const entry = sessions.get(sessionId);\n  if (!entry) return null;\n  return entry.agent.getHistory();\n}\n","import type { Request } from 'express';\n\n/**\n * 检测请求是否来自移动设备。\n * 逻辑：UA 正则匹配 + Cookie 手动覆盖。\n */\nexport function isMobileRequest(req: Request): boolean {\n  // Cookie 覆盖：用户主动选择桌面版时返回 false\n  if (getCookie(req, 'doc77-desktop') === '1') return false;\n\n  const ua = (req.headers['user-agent'] || '').toLowerCase();\n  // \"Mobi\" 覆盖所有手机浏览器（iOS Safari / Android Chrome / Firefox 等）\n  // \"Android\" 兜底覆盖不带 \"Mobi\" 的 Android 平板 UA\n  return /\\bmobi|android/i.test(ua);\n}\n\n/**\n * 内联 Cookie 解析 — 不引入 cookie-parser 依赖。\n */\nfunction getCookie(req: Request, name: string): string | undefined {\n  const header = req.headers.cookie;\n  if (!header) return undefined;\n  const match = header.match(new RegExp(`(?:^|;)\\\\s*${name}=([^;]*)`));\n  return match ? decodeURIComponent(match[1]) : undefined;\n}\n","/**\n * Vendor asset management — download CDN resources for offline use.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface VendorAsset {\n  name: string;        // local filename under vendor/\n  url: string;         // CDN URL\n  type: 'js' | 'css' | 'font' | 'wasm';\n  size?: string;       // human-readable size hint\n}\n\nexport const VENDOR_ASSETS: VendorAsset[] = [\n  { name: 'tailwind.js', url: 'https://cdn.tailwindcss.com', type: 'js', size: '~500KB' },\n  { name: 'mermaid.min.js', url: 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js', type: 'js', size: '~2MB' },\n  { name: 'highlight.min.js', url: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js', type: 'js', size: '~500KB' },\n  { name: 'highlight-github-dark.css', url: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css', type: 'css', size: '~10KB' },\n  { name: 'pdf.min.mjs', url: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.10.38/build/pdf.min.mjs', type: 'js', size: '~350KB' },\n  { name: 'pdf.worker.min.mjs', url: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.10.38/build/pdf.worker.min.mjs', type: 'js', size: '~1.4MB' },\n  { name: 'xlsx.mini.min.js', url: 'https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.mini.min.js', type: 'js', size: '~150KB' },\n  { name: 'mammoth.browser.min.js', url: 'https://cdn.jsdelivr.net/npm/mammoth@1.8.0/mammoth.browser.min.js', type: 'js', size: '~160KB' },\n  { name: 'pyodide.js', url: 'https://cdn.jsdelivr.net/pyodide/v0.27.5/full/pyodide.js', type: 'js', size: '~600KB' },\n  { name: 'pyodide-pyodide.asm.wasm', url: 'https://cdn.jsdelivr.net/pyodide/v0.27.5/full/pyodide.asm.wasm', type: 'wasm', size: '~9MB' },\n  { name: 'pyodide-python_stdlib.zip', url: 'https://cdn.jsdelivr.net/pyodide/v0.27.5/full/python_stdlib.zip', type: 'wasm', size: '~2MB' },\n];\n\n/**\n * Download vendor assets to the given directory.\n */\nexport async function fetchVendorAssets(vendorDir: string, assets: VendorAsset[]): Promise<void> {\n  fs.mkdirSync(vendorDir, { recursive: true });\n\n  for (const asset of assets) {\n    const dest = path.join(vendorDir, asset.name);\n    if (fs.existsSync(dest)) {\n      console.log(`  ✓ ${asset.name} (已存在)`);\n      continue;\n    }\n    try {\n      console.log(`  ↓ ${asset.name} (${asset.size || '?'})...`);\n      const resp = await fetch(asset.url, { signal: AbortSignal.timeout(120000) });\n      if (!resp.ok) {\n        console.error(`  ✗ ${asset.name}: HTTP ${resp.status}`);\n        continue;\n      }\n      const buffer = Buffer.from(await resp.arrayBuffer());\n      fs.writeFileSync(dest, buffer);\n      console.log(`  ✓ ${asset.name} (${(buffer.length / 1024).toFixed(0)} KB)`);\n    } catch (e: unknown) {\n      const msg = e instanceof Error ? e.message : 'Unknown error';\n      console.error(`  ✗ ${asset.name}: ${msg}`);\n    }\n  }\n\n  // Write a marker file to indicate vendor is ready\n  fs.writeFileSync(path.join(vendorDir, '.ready'), Date.now().toString());\n}\n\n/**\n * Check if the vendor directory is populated.\n */\nexport function isVendorReady(vendorDir: string): boolean {\n  return fs.existsSync(path.join(vendorDir, '.ready'));\n}\n\n/**\n * Resolve a vendor asset path — returns null if not available.\n */\nexport function vendorAssetPath(vendorDir: string, name: string): string | null {\n  const p = path.join(vendorDir, name);\n  return fs.existsSync(p) ? p : null;\n}\n"],"mappings":"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","names":["VERSION","isSensitiveFile","initSqlJs","fs","path","isSensitiveFile","path","fs","mtimeMap","entries","path","fs","import_node_child_process","import_node_child_process","resolve","escapeHtml","escapeHtml","basename","express","hasDisplay","isWsl","fs","path","VERSION"]}