install-failure.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. /**
  2. * What a failed pnpm run was, read off how it ended and what it printed:
  3. * pnpm names its failures with stable `ERR_PNPM_*` codes and Node's errno
  4. * names, which the run's captured tail carries whatever the locale.
  5. * @module @deepseek-ai/dsh-plugin-manager/install-failure
  6. */
  7. import type { PluginInstallFailureKind } from './types.ts'
  8. /** How a run ended, beyond its exit code. */
  9. export interface InstallFailureFacts {
  10. /** The output tail the failure reports. */
  11. readonly log: string
  12. /** The spawn error, when the child never ran. */
  13. readonly cause?: unknown
  14. /** Whether the run outlived its bound. */
  15. readonly timedOut?: boolean
  16. }
  17. /** Patterns in the order they decide: a specific code before the generic network family. */
  18. const LOG_KINDS: readonly [PluginInstallFailureKind, RegExp][] = [
  19. ['build-blocked', /ERR_PNPM_IGNORED_BUILDS|Ignored build scripts/],
  20. ['not-found', /ERR_PNPM_FETCH_404|\bE404\b|404 Not Found|Not Found - GET/],
  21. ['no-matching-version', /ERR_PNPM_NO_MATCHING_VERSION|\bETARGET\b|No matching version/],
  22. ['disk-full', /\bENOSPC\b|no space left on device/i],
  23. ['permission', /\bEACCES\b|\bEPERM\b|permission denied/i],
  24. ['integrity', /ERR_PNPM_TARBALL_INTEGRITY|ERR_PNPM_BAD_TARBALL_SIZE|\bEINTEGRITY\b/],
  25. ['network', /\bENOTFOUND\b|\bECONNRESET\b|\bETIMEDOUT\b|\bECONNREFUSED\b|\bEAI_AGAIN\b|ERR_PNPM_META_FETCH_FAIL|ERR_PNPM_FETCH_5\d\d|ERR_PNPM_FETCH_TIMEOUT|socket hang up|Could not resolve host|unable to access/],
  26. ]
  27. /**
  28. * Classify a failed run.
  29. * @param facts - how the run ended and what it printed.
  30. * @returns the kind, `unknown` when nothing in the facts names one.
  31. */
  32. export function classifyInstallFailure(facts: InstallFailureFacts): PluginInstallFailureKind {
  33. if (facts.timedOut === true) return 'timeout'
  34. if ((facts.cause as NodeJS.ErrnoException | undefined)?.code === 'ENOENT') return 'pnpm-missing'
  35. for (const [kind, pattern] of LOG_KINDS) {
  36. if (pattern.test(facts.log)) return kind
  37. }
  38. return 'unknown'
  39. }