ClaudeWrapper.DangerousClient (ClaudeWrapper v0.8.1)

Copy Markdown View Source

Opt-in wrapper for permission-bypass queries.

Running the claude CLI with --dangerously-skip-permissions turns off every confirmation prompt for tool use. It's legitimate for some automation -- but it's also the fastest way to turn a bug into a destructive action.

This module isolates that capability behind a type you have to explicitly reach for (ClaudeWrapper.DangerousClient) and a runtime env-var gate (CLAUDE_WRAPPER_ALLOW_DANGEROUS) you have to explicitly set.

Usage

# At process start (deliberately):
#   export CLAUDE_WRAPPER_ALLOW_DANGEROUS=1

config = ClaudeWrapper.Config.new(working_dir: "/path/to/project")

case ClaudeWrapper.DangerousClient.new(config) do
  {:ok, client} ->
    query = ClaudeWrapper.Query.new("clean up the build artifacts")
    {:ok, result} = ClaudeWrapper.DangerousClient.query_bypass(client, query)

  {:error, %ClaudeWrapper.Error{kind: :dangerous_not_allowed} = error} ->
    # The caller forgot to set the env var; refuse loudly rather
    # than silently running with (or without) bypass.
    {:error, error}
end

Why this shape

  • Separate type. new/1 is the only path to building a bypassed query through this module. If a reader of calling code sees DangerousClient, the danger is obvious at the call site.
  • Runtime env-var gate. The check happens at construction, so a caller who forgot to set the env var gets a tagged error rather than silently running with bypass off (which might surprise them) or silently running with bypass on (which might destroy things).
  • Checked on every construction. The gate is read on each call to new/1 rather than memoized, so a test that flips the env var mid-process sees the change.

The lower-level ClaudeWrapper.Query.dangerously_skip_permissions/1 setter is still available without this gate; this module is the guarded, intention-revealing path on top of it.

Equivalent to the Rust crate's dangerous::DangerousClient. The Rust type splits query_bypass into async (query_bypass) and blocking (query_bypass_sync) variants gated by cargo features; the Elixir port has a single synchronous query_bypass/2 since Query.execute/2 is synchronous and there are no feature gates.

Summary

Types

t()

A guarded client. Holds the shared ClaudeWrapper.Config.t/0 that bypass queries run against.

Functions

The name of the env var that must equal "1" for new/1 to succeed.

Return the underlying config for composition with other wrapper APIs.

Build a guarded client, refusing unless the opt-in env var is set.

Run query with --dangerously-skip-permissions set, against the client's config.

Types

t()

@type t() :: %ClaudeWrapper.DangerousClient{config: ClaudeWrapper.Config.t()}

A guarded client. Holds the shared ClaudeWrapper.Config.t/0 that bypass queries run against.

Functions

allow_env()

@spec allow_env() :: String.t()

The name of the env var that must equal "1" for new/1 to succeed.

Exposed so callers and tests can reference the gate without hardcoding the string.

Examples

iex> ClaudeWrapper.DangerousClient.allow_env()
"CLAUDE_WRAPPER_ALLOW_DANGEROUS"

config(dangerous_client)

@spec config(t()) :: ClaudeWrapper.Config.t()

Return the underlying config for composition with other wrapper APIs.

new(config)

@spec new(ClaudeWrapper.Config.t()) :: {:ok, t()} | {:error, ClaudeWrapper.Error.t()}

Build a guarded client, refusing unless the opt-in env var is set.

Succeeds only when System.get_env("CLAUDE_WRAPPER_ALLOW_DANGEROUS") == "1". Otherwise returns {:error, %ClaudeWrapper.Error{kind: :dangerous_not_allowed}}, whose :reason is the name of the env var to set.

The check is made on each call rather than memoized, so a process that sets the env var after start (or a test that flips it) is honored.

Examples

{:ok, client} = ClaudeWrapper.DangerousClient.new(ClaudeWrapper.Config.new())

{:error, %ClaudeWrapper.Error{kind: :dangerous_not_allowed, reason: "CLAUDE_WRAPPER_ALLOW_DANGEROUS"}} =
  ClaudeWrapper.DangerousClient.new(ClaudeWrapper.Config.new())

query_bypass(dangerous_client, query)

@spec query_bypass(t(), ClaudeWrapper.Query.t()) ::
  {:ok, ClaudeWrapper.Result.t()} | {:error, term()}

Run query with --dangerously-skip-permissions set, against the client's config.

Sets dangerously_skip_permissions on the query (replacing any prior value) and delegates to ClaudeWrapper.Query.execute/2. Returns whatever execute/2 returns: {:ok, %ClaudeWrapper.Result{}} or {:error, reason}.