Skip to main content

claude_code/client/setup_token/
start.rs

1use std::sync::Arc;
2
3use tokio::{
4    process::Command,
5    sync::{oneshot, Mutex},
6};
7
8use super::super::ClaudeClient;
9use super::{
10    capture::spawn_capture_task, process::SetupTokenProcess, session::ClaudeSetupTokenSession,
11    url::UrlCapture,
12};
13use crate::{
14    commands::setup_token::ClaudeSetupTokenRequest,
15    process::{self as cli_process, ConsoleTarget},
16    ClaudeCodeError,
17};
18
19#[cfg(unix)]
20use super::pty::spawn_setup_token_pty;
21
22impl ClaudeClient {
23    pub async fn setup_token_start(&self) -> Result<ClaudeSetupTokenSession, ClaudeCodeError> {
24        self.setup_token_start_with(ClaudeSetupTokenRequest::new())
25            .await
26    }
27
28    pub async fn setup_token_start_with(
29        &self,
30        request: ClaudeSetupTokenRequest,
31    ) -> Result<ClaudeSetupTokenSession, ClaudeCodeError> {
32        self.ensure_home_prepared()?;
33        let requested_timeout = request.timeout;
34        let binary = self.resolve_binary();
35        let argv = request.into_command().argv();
36
37        let stdout_buf = Arc::new(Mutex::new(Vec::<u8>::new()));
38        let stderr_buf = Arc::new(Mutex::new(Vec::<u8>::new()));
39
40        let (url_tx, url_rx) = oneshot::channel::<String>();
41        let url_tx = Arc::new(Mutex::new(Some(url_tx)));
42        let url_state = Arc::new(Mutex::new(UrlCapture::default()));
43
44        #[cfg(unix)]
45        if let Ok((process, stdout_task)) = spawn_setup_token_pty(
46            &binary,
47            &argv,
48            self.working_dir.as_deref(),
49            &self.env,
50            self.mirror_stdout,
51            self.mirror_stderr,
52            stdout_buf.clone(),
53            url_state.clone(),
54            url_tx.clone(),
55        ) {
56            return Ok(ClaudeSetupTokenSession::new(
57                process,
58                stdout_buf,
59                stderr_buf,
60                stdout_task,
61                None,
62                url_rx,
63                requested_timeout,
64            ));
65        }
66
67        let mut cmd = Command::new(&binary);
68        cmd.args(&argv);
69
70        if let Some(dir) = self.working_dir.as_ref() {
71            cmd.current_dir(dir);
72        }
73
74        cli_process::apply_env(&mut cmd, &self.env);
75
76        cmd.stdin(std::process::Stdio::piped());
77        cmd.stdout(std::process::Stdio::piped());
78        cmd.stderr(std::process::Stdio::piped());
79        cmd.kill_on_drop(true);
80
81        let mut child = cli_process::spawn_with_retry(&mut cmd, &binary)?;
82        let stdin = child.stdin.take();
83        let stdout = child.stdout.take().ok_or(ClaudeCodeError::MissingStdout)?;
84        let stderr = child.stderr.take().ok_or(ClaudeCodeError::MissingStderr)?;
85
86        let stdout_task = spawn_capture_task(
87            stdout,
88            ConsoleTarget::Stdout,
89            self.mirror_stdout,
90            stdout_buf.clone(),
91            url_state.clone(),
92            url_tx.clone(),
93        );
94        let stderr_task = spawn_capture_task(
95            stderr,
96            ConsoleTarget::Stderr,
97            self.mirror_stderr,
98            stderr_buf.clone(),
99            url_state.clone(),
100            url_tx.clone(),
101        );
102
103        Ok(ClaudeSetupTokenSession::new(
104            SetupTokenProcess::Pipes { child, stdin },
105            stdout_buf,
106            stderr_buf,
107            stdout_task,
108            Some(stderr_task),
109            url_rx,
110            // `setup-token` is inherently interactive and can require human/browser steps.
111            // Do not apply the client's default timeout unless the caller explicitly requests one.
112            requested_timeout,
113        ))
114    }
115}