Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
7.7 kB · 238 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239import { APIUserAbortError } from '@anthropic-ai/sdk'
export class ClaudeError extends Error { constructor(message: string) { super(message) this.name = this.constructor.name }}
export class MalformedCommandError extends Error {}
export class AbortError extends Error { constructor(message?: string) { super(message) this.name = 'AbortError' }}
/** * True iff `e` is any of the abort-shaped errors the codebase encounters: * our AbortError class, a DOMException from AbortController.abort() * (.name === 'AbortError'), or the SDK's APIUserAbortError. The SDK class * is checked via instanceof because minified builds mangle class names — * constructor.name becomes something like 'nJT' and the SDK never sets * this.name, so string matching silently fails in production. */export function isAbortError(e: unknown): boolean { return ( e instanceof AbortError || e instanceof APIUserAbortError || (e instanceof Error && e.name === 'AbortError') )}
/** * Custom error class for configuration file parsing errors * Includes the file path and the default configuration that should be used */export class ConfigParseError extends Error { filePath: string defaultConfig: unknown
constructor(message: string, filePath: string, defaultConfig: unknown) { super(message) this.name = 'ConfigParseError' this.filePath = filePath this.defaultConfig = defaultConfig }}
export class ShellError extends Error { constructor( public readonly stdout: string, public readonly stderr: string, public readonly code: number, public readonly interrupted: boolean, ) { super('Shell command failed') this.name = 'ShellError' }}
export class TeleportOperationError extends Error { constructor( message: string, public readonly formattedMessage: string, ) { super(message) this.name = 'TeleportOperationError' }}
/** * Error with a message that is safe to log to telemetry. * Use the long name to confirm you've verified the message contains no * sensitive data (file paths, URLs, code snippets). * * Single-arg: same message for user and telemetry * Two-arg: different messages (e.g., full message has file path, telemetry doesn't) * * @example * // Same message for both * throw new TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS( * 'MCP server "slack" connection timed out' * ) * * // Different messages * throw new TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS( * `MCP tool timed out after ${ms}ms`, // Full message for logs/user * 'MCP tool timed out' // Telemetry message * ) */export class TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS extends Error { readonly telemetryMessage: string
constructor(message: string, telemetryMessage?: string) { super(message) this.name = 'TelemetrySafeError' this.telemetryMessage = telemetryMessage ?? message }}
export function hasExactErrorMessage(error: unknown, message: string): boolean { return error instanceof Error && error.message === message}
/** * Normalize an unknown value into an Error. * Use at catch-site boundaries when you need an Error instance. */export function toError(e: unknown): Error { return e instanceof Error ? e : new Error(String(e))}
/** * Extract a string message from an unknown error-like value. * Use when you only need the message (e.g., for logging or display). */export function errorMessage(e: unknown): string { return e instanceof Error ? e.message : String(e)}
/** * Extract the errno code (e.g., 'ENOENT', 'EACCES') from a caught error. * Returns undefined if the error has no code or is not an ErrnoException. * Replaces the `(e as NodeJS.ErrnoException).code` cast pattern. */export function getErrnoCode(e: unknown): string | undefined { if (e && typeof e === 'object' && 'code' in e && typeof e.code === 'string') { return e.code } return undefined}
/** * True if the error is ENOENT (file or directory does not exist). * Replaces `(e as NodeJS.ErrnoException).code === 'ENOENT'`. */export function isENOENT(e: unknown): boolean { return getErrnoCode(e) === 'ENOENT'}
/** * Extract the errno path (the filesystem path that triggered the error) * from a caught error. Returns undefined if the error has no path. * Replaces the `(e as NodeJS.ErrnoException).path` cast pattern. */export function getErrnoPath(e: unknown): string | undefined { if (e && typeof e === 'object' && 'path' in e && typeof e.path === 'string') { return e.path } return undefined}
/** * Extract error message + top N stack frames from an unknown error. * Use when the error flows to the model as a tool_result — full stack * traces are ~500-2000 chars of mostly-irrelevant internal frames and * waste context tokens. Keep the full stack in debug logs instead. */export function shortErrorStack(e: unknown, maxFrames = 5): string { if (!(e instanceof Error)) return String(e) if (!e.stack) return e.message // V8/Bun stack format: "Name: message\n at frame1\n at frame2..." // First line is the message; subsequent " at " lines are frames. const lines = e.stack.split('\n') const header = lines[0] ?? e.message const frames = lines.slice(1).filter(l => l.trim().startsWith('at ')) if (frames.length <= maxFrames) return e.stack return [header, ...frames.slice(0, maxFrames)].join('\n')}
/** * True if the error means the path is missing, inaccessible, or * structurally unreachable — use in catch blocks after fs operations to * distinguish expected "nothing there / no access" from unexpected errors. * * Covers: * ENOENT — path does not exist * EACCES — permission denied * EPERM — operation not permitted * ENOTDIR — a path component is not a directory (e.g. a file named * `.claude` exists where a directory is expected) * ELOOP — too many symlink levels (circular symlinks) */export function isFsInaccessible(e: unknown): e is NodeJS.ErrnoException { const code = getErrnoCode(e) return ( code === 'ENOENT' || code === 'EACCES' || code === 'EPERM' || code === 'ENOTDIR' || code === 'ELOOP' )}
export type AxiosErrorKind = | 'auth' // 401/403 — caller typically sets skipRetry | 'timeout' // ECONNABORTED | 'network' // ECONNREFUSED/ENOTFOUND | 'http' // other axios error (may have status) | 'other' // not an axios error
/** * Classify a caught error from an axios request into one of a few buckets. * Replaces the ~20-line isAxiosError → 401/403 → ECONNABORTED → ECONNREFUSED * chain duplicated across sync-style services (settingsSync, policyLimits, * remoteManagedSettings, teamMemorySync). * * Checks the `.isAxiosError` marker property directly (same as * axios.isAxiosError()) to keep this module dependency-free. */export function classifyAxiosError(e: unknown): { kind: AxiosErrorKind status?: number message: string} { const message = errorMessage(e) if ( !e || typeof e !== 'object' || !('isAxiosError' in e) || !e.isAxiosError ) { return { kind: 'other', message } } const err = e as { response?: { status?: number } code?: string } const status = err.response?.status if (status === 401 || status === 403) return { kind: 'auth', status, message } if (err.code === 'ECONNABORTED') return { kind: 'timeout', status, message } if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') { return { kind: 'network', status, message } } return { kind: 'http', status, message }}