{"version":3,"file":"paths-Wz-_334T.mjs","names":[],"sources":["../src/transport/paths.ts"],"sourcesContent":["import { chmod, mkdir } from 'node:fs/promises'\nimport { homedir, platform } from 'node:os'\nimport { dirname, join } from 'node:path'\n\n/**\n * Platform-specific paths for the MCP server's Unix domain socket\n * and the discovery JSON file.\n *\n * Socket directory layout (overridable via OPENPENCIL_MCP_SOCKET):\n *   macOS:   ~/Library/Application Support/OpenPencil/\n *   Linux:   $XDG_RUNTIME_DIR/openpencil/  (fallback: ~/.openpencil/)\n *   Windows: %LOCALAPPDATA%\\OpenPencil\\  (fallback: ~\\AppData\\Local\\OpenPencil\\)\n *\n * On Windows, Unix domain sockets are unavailable — the server uses TCP only.\n *\n * Discovery file: at the platform-default path above, UNLESS\n * OPENPENCIL_MCP_DISCOVERY_PATH is set (see getDiscoveryPath()). The socket\n * override (OPENPENCIL_MCP_SOCKET) never moves the discovery file — it is\n * recorded in the discovery file's `socketPath` field so clients read it\n * from the well-known location.\n * Socket file:     <socketDir>/mcp.sock  (or the override path)\n *\n * IMPORTANT: getSocketDir() returns the directory that contains the socket\n * file. It does NOT always contain the discovery file — when\n * OPENPENCIL_MCP_SOCKET is set, the discovery file stays at getPlatformDir().\n */\n\nconst DIR_NAME_UNIX = 'openpencil'\nconst DIR_NAME_MACOS = 'OpenPencil'\nconst SOCKET_FILENAME = 'mcp.sock'\nconst DISCOVERY_FILENAME = 'mcp.json'\n\nconst isMacOS = platform() === 'darwin'\nconst isWindows = platform() === 'win32'\n\n/**\n * Returns the platform-specific default directory for MCP runtime files,\n * ignoring OPENPENCIL_MCP_SOCKET. The discovery file always lives here so\n * clients can find it at a well-known location regardless of socket overrides.\n * Creates the directory (with restrictive permissions) if it does not exist.\n *\n * The 0o700 mode restricts permissions on Unix. On Windows, mkdir ignores\n * the mode and uses default ACLs — directory access is controlled by the\n * filesystem, not the permission bits.\n */\nasync function getPlatformDir(): Promise<string> {\n  let dir: string\n\n  if (isMacOS) {\n    dir = join(homedir(), 'Library', 'Application Support', DIR_NAME_MACOS)\n  } else if (isWindows) {\n    const localAppData = process.env.LOCALAPPDATA?.trim()\n    if (localAppData) {\n      dir = join(localAppData, DIR_NAME_MACOS)\n    } else {\n      dir = join(homedir(), 'AppData', 'Local', DIR_NAME_MACOS)\n    }\n  } else {\n    // Linux / other Unix\n    const xdgRuntime = process.env.XDG_RUNTIME_DIR?.trim()\n    if (xdgRuntime) {\n      dir = join(xdgRuntime, DIR_NAME_UNIX)\n    } else {\n      dir = join(homedir(), `.${DIR_NAME_UNIX}`)\n    }\n  }\n\n  await mkdir(dir, { recursive: true, mode: 0o700 })\n  if (!isWindows) await chmod(dir, 0o700)\n  return dir\n}\n\n/**\n * Returns the directory for the MCP socket file.\n *\n * When OPENPENCIL_MCP_SOCKET is set, its dirname is used as the socket\n * directory. When unset, the platform default from getPlatformDir() is used.\n * Creates the directory (with restrictive permissions) if it does not exist.\n *\n * NOTE: The discovery file always lives at getPlatformDir(), regardless of\n * OPENPENCIL_MCP_SOCKET. This function should NOT be used to locate it.\n */\nexport async function getSocketDir(): Promise<string> {\n  const socketOverride = process.env.OPENPENCIL_MCP_SOCKET?.trim()\n\n  if (socketOverride) {\n    const dir = dirname(socketOverride)\n    // Create missing parents with restrictive permissions. Do NOT chmod\n    // existing directories — the override path is user-controlled and\n    // may be in a shared directory (e.g. /tmp for tests).\n    await mkdir(dir, { recursive: true, mode: 0o700 })\n    return dir\n  }\n\n  return getPlatformDir()\n}\n\n/**\n * Returns the full path to the MCP Unix domain socket.\n *\n * On macOS/Linux: <socketDir>/mcp.sock\n *\n * When OPENPENCIL_MCP_SOCKET is set, its value is returned directly\n * (no directory resolution needed).\n */\nexport async function getSocketPath(): Promise<string> {\n  const socketOverride = process.env.OPENPENCIL_MCP_SOCKET?.trim()\n  if (socketOverride) {\n    // Ensure the override directory exists. getSocketDir() creates the\n    // directory for the custom socket path (dirname of the override).\n    await getSocketDir()\n    return socketOverride\n  }\n\n  const dir = await getSocketDir()\n  return join(dir, SOCKET_FILENAME)\n}\n\n/**\n * Returns the full path to the MCP discovery JSON file.\n *\n * The discovery file lives at the platform-default location so clients can\n * find it without knowing whether OPENPENCIL_MCP_SOCKET is set. It contains\n * the actual socket path (which may be overridden) in its `socketPath` field,\n * so clients read the discovery file to learn where to connect — not the other\n * way around.\n *\n * Set OPENPENCIL_MCP_DISCOVERY_PATH to relocate the discovery file (e.g. to a\n * temp directory for test isolation). The desktop app reads the platform\n * default via its own path computation (src/app/automation/mcp/spawn.ts) and\n * does not honor this override, so it is primarily useful for tests and\n * standalone server invocations. The parent directory is created (0o700) so\n * writeDiscoveryFile's atomic temp-then-rename succeeds.\n */\nexport async function getDiscoveryPath(): Promise<string> {\n  const override = process.env.OPENPENCIL_MCP_DISCOVERY_PATH?.trim()\n  if (override) {\n    const dir = dirname(override)\n    await mkdir(dir, { recursive: true, mode: 0o700 })\n    return override\n  }\n  const dir = await getPlatformDir()\n  return join(dir, DISCOVERY_FILENAME)\n}\n\n/**\n * Returns true if the current platform supports Unix domain sockets.\n * Unix domain sockets are available on macOS, Linux, and other POSIX\n * platforms but not on native Windows. WSL is detected as Linux.\n */\nexport function platformHasUnixSockets(): boolean {\n  return !isWindows\n}\n\n/**\n * Returns the platform name for display purposes.\n */\nexport function platformName(): 'macos' | 'linux' | 'windows' | 'other' {\n  if (isMacOS) return 'macos'\n  if (isWindows) return 'windows'\n  if (platform() === 'linux') return 'linux'\n  return 'other'\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AACxB,MAAM,qBAAqB;AAE3B,MAAM,UAAU,SAAS,MAAM;AAC/B,MAAM,YAAY,SAAS,MAAM;;;;;;;;;;;AAYjC,eAAe,iBAAkC;CAC/C,IAAI;CAEJ,IAAI,SACF,MAAM,KAAK,QAAQ,GAAG,WAAW,uBAAuB,cAAc;MACjE,IAAI,WAAW;EACpB,MAAM,eAAe,QAAQ,IAAI,cAAc,KAAK;EACpD,IAAI,cACF,MAAM,KAAK,cAAc,cAAc;OAEvC,MAAM,KAAK,QAAQ,GAAG,WAAW,SAAS,cAAc;CAE5D,OAAO;EAEL,MAAM,aAAa,QAAQ,IAAI,iBAAiB,KAAK;EACrD,IAAI,YACF,MAAM,KAAK,YAAY,aAAa;OAEpC,MAAM,KAAK,QAAQ,GAAG,IAAI,eAAe;CAE7C;CAEA,MAAM,MAAM,KAAK;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACjD,IAAI,CAAC,WAAW,MAAM,MAAM,KAAK,GAAK;CACtC,OAAO;AACT;;;;;;;;;;;AAYA,eAAsB,eAAgC;CACpD,MAAM,iBAAiB,QAAQ,IAAI,uBAAuB,KAAK;CAE/D,IAAI,gBAAgB;EAClB,MAAM,MAAM,QAAQ,cAAc;EAIlC,MAAM,MAAM,KAAK;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EACjD,OAAO;CACT;CAEA,OAAO,eAAe;AACxB;;;;;;;;;AAUA,eAAsB,gBAAiC;CACrD,MAAM,iBAAiB,QAAQ,IAAI,uBAAuB,KAAK;CAC/D,IAAI,gBAAgB;EAGlB,MAAM,aAAa;EACnB,OAAO;CACT;CAEA,MAAM,MAAM,MAAM,aAAa;CAC/B,OAAO,KAAK,KAAK,eAAe;AAClC;;;;;;;;;;;;;;;;;AAkBA,eAAsB,mBAAoC;CACxD,MAAM,WAAW,QAAQ,IAAI,+BAA+B,KAAK;CACjE,IAAI,UAAU;EACZ,MAAM,MAAM,QAAQ,QAAQ;EAC5B,MAAM,MAAM,KAAK;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EACjD,OAAO;CACT;CACA,MAAM,MAAM,MAAM,eAAe;CACjC,OAAO,KAAK,KAAK,kBAAkB;AACrC;;;;;;AAOA,SAAgB,yBAAkC;CAChD,OAAO,CAAC;AACV;;;;AAKA,SAAgB,eAAwD;CACtE,IAAI,SAAS,OAAO;CACpB,IAAI,WAAW,OAAO;CACtB,IAAI,SAAS,MAAM,SAAS,OAAO;CACnC,OAAO;AACT"}