Current section

Files

Jump to
claude_code lib mix tasks claude_code.setup_token.ex
Raw

lib/mix/tasks/claude_code.setup_token.ex

defmodule Mix.Tasks.ClaudeCode.SetupToken do
@shortdoc "Runs `claude setup-token` to configure an OAuth token"
@moduledoc """
Runs `claude setup-token` using the resolved CLI binary.
This starts an interactive OAuth flow that opens your browser for authentication,
then prints the token to stdout.
## Usage
mix claude_code.setup_token
## Resolution
The CLI binary is resolved using the same logic as `mix claude_code.path`.
"""
use Mix.Task
alias ClaudeCode.Adapter.Port.Resolver
@impl Mix.Task
def run(_args) do
case Resolver.find_binary() do
{:ok, cli_path} ->
# The CLI uses Ink which requires a TTY with raw mode support.
# We use `script` to allocate a PTY and spawn via :erlang.open_port
# to avoid Elixir's IO system choking on raw terminal escape codes.
script = System.find_executable("script")
if !script do
Mix.shell().error("""
`script` command not found. Run the CLI directly instead:
#{cli_path} setup-token
""")
exit({:shutdown, 1})
end
# macOS and Linux `script` have different flag syntax
{wrapper, args} =
case :os.type() do
{:unix, :darwin} ->
{script, ["-q", "/dev/null", cli_path, "setup-token"]}
{:unix, _} ->
{script, ["-qc", "#{cli_path} setup-token", "/dev/null"]}
end
port =
Port.open({:spawn_executable, wrapper}, [
:binary,
:exit_status,
:nouse_stdio,
{:args, args}
])
wait_for_exit(port)
{:error, _reason} ->
Mix.shell().error("""
Claude CLI not found.
Install it with: mix claude_code.install
Or configure: config :claude_code, cli_path: :global
""")
exit({:shutdown, 1})
end
end
defp wait_for_exit(port) do
receive do
{^port, {:exit_status, 0}} -> :ok
{^port, {:exit_status, code}} -> exit({:shutdown, code})
{^port, _} -> wait_for_exit(port)
end
end
end