Files
clide/lib/src/ipc/errno_mapping.dart
T
jpmschweitzerandClaude Opus 4.7 d4f8f89016
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m7s
code-quality batch (T-112)
Seven small consultant findings, one commit:

1. TreeSitterLib stores last dlopen error + path in static fields
   instead of swallowing them. Callers that observe a null instance
   can now read the diagnostic.

2. Drop the Cmsghdr alias in libc.dart — back-compat shim with no
   callers; CLAUDE.md forbids those in a solo repo.

3. Drop EditorController._events field + the unused_field
   suppression. The constructor still subscribes via `events.on<...>`
   for _eventSub; the field itself was speculative retention.

4. Replace inline hex / errno literals in native_pty.dart with
   PosixErrno.{eintr,ebadf,epipe} and new libc.{pollin, pollAnyErr,
   sighup, sigkill, sigwinch}. PosixErrno gains eintr.

5. ExtensionManager records activate/deactivate exceptions in a
   `_failed` map exposed as `failedExtensions` + `didFail(id)`.
   Listeners are notified on entry/exit; cleared on a clean
   activate. UI surfaces the degraded state instead of pretending
   everything is fine.

6. file_tree_view imports FileEntry via the clide.dart barrel
   instead of `package:clide/src/files/listing.dart` directly — the
   leak the consultant flagged (barrel already re-exports it).

7. test_app branch in main.dart wrapped in `if (kDebugMode)` so
   release tree-shaker elides the test harness from shipping
   binaries. Source import stays; tree-shake handles the rest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 22:11:23 +02:00

105 lines
3.3 KiB
Dart

/// Map POSIX errno values to IPC error envelopes with actionable
/// messages. Used by command handlers that wrap syscall-backed work
/// (PTY spawn, file open) so the client can distinguish "binary not
/// found" from "permission denied" from "system limit hit" instead
/// of seeing the same generic `tool_error: foo failed`.
library;
import 'envelope.dart';
import 'schema_v1.dart';
/// Selected POSIX errno values we map specially. Others fall through
/// to a generic toolError. Values match Linux glibc and macOS Darwin
/// (the two platforms that share the same numbers for these entries).
abstract class PosixErrno {
static const int eperm = 1;
static const int enoent = 2;
static const int esrch = 3;
static const int eintr = 4;
static const int eio = 5;
static const int ebadf = 9;
static const int eagain = 11;
static const int enomem = 12;
static const int eacces = 13;
static const int eexist = 17;
static const int enotdir = 20;
static const int eisdir = 21;
static const int emfile = 24;
static const int enfile = 23;
static const int epipe = 32;
}
/// Build an [IpcError] from a POSIX [errno] for an operation [op]
/// (e.g. `pane.spawn`, `editor.open`) on optional [target] (a path,
/// command name, etc.). The returned error uses `notFound`,
/// `userError`, or `toolError` based on what's actionable.
IpcError errnoToIpcError({
required int errno,
required String op,
String? target,
String? raw,
}) {
final what = target != null ? ' ($target)' : '';
switch (errno) {
case PosixErrno.enoent:
return IpcError(
code: IpcExitCode.notFound,
kind: IpcErrorKind.notFound,
message: '$op: not found$what',
);
case PosixErrno.eacces:
case PosixErrno.eperm:
return IpcError(
code: IpcExitCode.userError,
kind: IpcErrorKind.userError,
message: '$op: permission denied$what',
hint: 'check file permissions or run with appropriate access',
);
case PosixErrno.eisdir:
return IpcError(
code: IpcExitCode.userError,
kind: IpcErrorKind.userError,
message: '$op: is a directory$what',
);
case PosixErrno.enotdir:
return IpcError(
code: IpcExitCode.userError,
kind: IpcErrorKind.userError,
message: '$op: not a directory$what',
);
case PosixErrno.eexist:
return IpcError(
code: IpcExitCode.conflict,
kind: IpcErrorKind.conflict,
message: '$op: already exists$what',
);
case PosixErrno.emfile:
case PosixErrno.enfile:
return IpcError(
code: IpcExitCode.toolError,
kind: IpcErrorKind.toolError,
message: '$op: too many open files',
hint: 'system or per-process file descriptor limit reached',
);
case PosixErrno.enomem:
return IpcError(
code: IpcExitCode.toolError,
kind: IpcErrorKind.toolError,
message: '$op: out of memory',
);
case PosixErrno.eagain:
return IpcError(
code: IpcExitCode.toolError,
kind: IpcErrorKind.toolError,
message: '$op: resource temporarily unavailable',
hint: 'retry may succeed',
);
default:
return IpcError(
code: IpcExitCode.toolError,
kind: IpcErrorKind.toolError,
message: '$op failed${raw != null ? ': $raw' : ' (errno=$errno)'}',
);
}
}