Skip to main content

dk_agent_sdk/
client.rs

1use dk_protocol::agent_service_client::AgentServiceClient;
2use tonic::transport::{Channel, ClientTlsConfig};
3
4use crate::error::{Result, SdkError};
5use crate::session::Session;
6use crate::types::ConnectResult;
7
8/// Top-level client for the dkod Agent Protocol.
9///
10/// Use [`AgentClient::connect`] to establish a gRPC channel to the server, then
11/// call [`AgentClient::init`] to create a stateful session for a specific
12/// codebase and intent.
13pub struct AgentClient {
14    inner: AgentServiceClient<Channel>,
15    auth_token: String,
16}
17
18impl AgentClient {
19    /// Connect to a dkod Agent Protocol server at the given address.
20    ///
21    /// `addr` should be a full URI such as `"http://localhost:50051"` or
22    /// `"https://agent.dkod.io:443"`. TLS is enabled automatically for
23    /// `https://` addresses.
24    pub async fn connect(addr: &str, auth_token: &str) -> Result<Self> {
25        let mut endpoint = Channel::from_shared(addr.to_string())
26            .map_err(|e| SdkError::Connection(e.to_string()))?;
27
28        if addr.starts_with("https://") {
29            endpoint = endpoint
30                .tls_config(ClientTlsConfig::new().with_webpki_roots())
31                .map_err(|e| SdkError::Connection(format!("TLS config error: {e}")))?;
32        }
33
34        let channel = endpoint.connect().await?;
35
36        Ok(Self {
37            inner: AgentServiceClient::new(channel),
38            auth_token: auth_token.to_string(),
39        })
40    }
41
42    /// Perform the CONNECT handshake: authenticate, specify the target codebase
43    /// and intent, and receive a [`Session`] bound to the resulting changeset.
44    pub async fn init(&mut self, repo: &str, intent: &str) -> Result<Session> {
45        let resp = self
46            .inner
47            .connect(dk_protocol::ConnectRequest {
48                agent_id: format!("sdk-{}", uuid::Uuid::new_v4()),
49                auth_token: self.auth_token.clone(),
50                codebase: repo.to_string(),
51                intent: intent.to_string(),
52                workspace_config: None,
53                agent_name: String::new(),
54            })
55            .await?
56            .into_inner();
57
58        let connect_result = ConnectResult {
59            session_id: resp.session_id.clone(),
60            changeset_id: resp.changeset_id.clone(),
61            codebase_version: resp.codebase_version.clone(),
62            summary: resp.summary,
63        };
64
65        Ok(Session::new(self.inner.clone(), connect_result))
66    }
67}