fix-node-pty.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. #!/usr/bin/env node
  2. /**
  3. * Fix node-pty spawn-helper permissions on macOS
  4. *
  5. * This script fixes a known issue with node-pty where the spawn-helper
  6. * binary is shipped without execute permissions, causing "posix_spawnp failed" errors.
  7. *
  8. * @see https://github.com/microsoft/node-pty/issues/850
  9. * @module scripts/fix-node-pty
  10. */
  11. import { promises as fs } from 'fs';
  12. import path from 'path';
  13. import { createRequire } from 'module';
  14. import { fileURLToPath } from 'url';
  15. const __filename = fileURLToPath(import.meta.url);
  16. const __dirname = path.dirname(__filename);
  17. const require = createRequire(import.meta.url);
  18. /**
  19. * Locate `node-pty`'s installation directory.
  20. *
  21. * With npm workspaces the package may be hoisted to the repo root
  22. * (`/PolitDeck/node_modules/node-pty`) instead of `ui/node_modules/node-pty`.
  23. * We resolve it via the Node module resolver so postinstall keeps working
  24. * in both layouts. Falls back to the legacy non-hoisted path.
  25. */
  26. function resolveNodePtyDir() {
  27. try {
  28. const pkgJsonPath = require.resolve('node-pty/package.json');
  29. return path.dirname(pkgJsonPath);
  30. } catch {
  31. return path.join(__dirname, '..', 'node_modules', 'node-pty');
  32. }
  33. }
  34. /**
  35. * Fixes the spawn-helper binary permissions for node-pty on macOS.
  36. *
  37. * The node-pty package ships the spawn-helper binary without execute permissions
  38. * (644 instead of 755), which causes "posix_spawnp failed" errors when trying
  39. * to spawn terminal processes.
  40. *
  41. * This function:
  42. * 1. Checks if running on macOS (darwin)
  43. * 2. Locates spawn-helper binaries for both arm64 and x64 architectures
  44. * 3. Sets execute permissions (755) on each binary found
  45. *
  46. * @async
  47. * @function fixSpawnHelper
  48. * @returns {Promise<void>} Resolves when permissions are fixed or skipped
  49. * @example
  50. * // Run as postinstall script
  51. * await fixSpawnHelper();
  52. */
  53. async function fixSpawnHelper() {
  54. const nodeModulesPath = path.join(resolveNodePtyDir(), 'prebuilds');
  55. // Only run on macOS
  56. if (process.platform !== 'darwin') {
  57. return;
  58. }
  59. const darwinDirs = ['darwin-arm64', 'darwin-x64'];
  60. for (const dir of darwinDirs) {
  61. const spawnHelperPath = path.join(nodeModulesPath, dir, 'spawn-helper');
  62. try {
  63. // Check if file exists
  64. await fs.access(spawnHelperPath);
  65. // Make it executable (755)
  66. await fs.chmod(spawnHelperPath, 0o755);
  67. console.log(`[postinstall] Fixed permissions for ${spawnHelperPath}`);
  68. } catch (err) {
  69. // File doesn't exist or other error - ignore
  70. if (err.code !== 'ENOENT') {
  71. console.warn(`[postinstall] Warning: Could not fix ${spawnHelperPath}: ${err.message}`);
  72. }
  73. }
  74. }
  75. }
  76. fixSpawnHelper().catch(console.error);