Skip to main content

netget/client/adc/
actions.rs

1use crate::llm::actions::{
2    client_trait::{Client, ClientActionResult},
3    protocol_trait::Protocol,
4    ActionDefinition,
5};
6use crate::protocol::{ConnectContext, EventType};
7use crate::server::p2p_support::{action, parameter};
8use crate::state::AppState;
9use anyhow::Result;
10use serde_json::{json, Value};
11use std::{future::Future, net::SocketAddr, pin::Pin, sync::LazyLock};
12
13#[derive(Default)]
14pub struct AdcClientProtocol;
15impl AdcClientProtocol {
16    pub fn new() -> Self {
17        Self
18    }
19}
20pub fn requests() -> Vec<ActionDefinition> {
21    vec![
22        action(
23            "adc_chat",
24            "Send public or private chat",
25            vec![
26                parameter(
27                    "message",
28                    "string",
29                    "UTF-8 chat message to send to the hub or selected peer",
30                    true,
31                ),
32                parameter("destination", "string", "Optional destination SID", false),
33            ],
34            json!({"type":"adc_chat","message":"Hello"}),
35        ),
36        action(
37            "adc_search",
38            "Broadcast a filename search through the connected ADC hub",
39            vec![parameter("query", "string", "Filename search term", true)],
40            json!({"type":"adc_search","query":"test"}),
41        ),
42        action(
43            "adc_connect",
44            "Request a direct peer transfer connection",
45            vec![
46                parameter(
47                    "destination",
48                    "string",
49                    "Four-character session ID of the destination ADC hub peer",
50                    true,
51                ),
52                parameter("port", "number", "Listening peer transfer port", true),
53                parameter("token", "string", "Connection token", true),
54                parameter(
55                    "secure",
56                    "boolean",
57                    "Request an implicit TLS peer connection using ADCS/1.0",
58                    false,
59                ),
60            ],
61            json!({"type":"adc_connect","destination":"AAAB","port":1512,"token":"netget"}),
62        ),
63        action(
64            "adc_reverse_connect",
65            "Ask a peer to initiate rendezvous",
66            vec![
67                parameter("destination", "string", "Destination SID", true),
68                parameter("token", "string", "Rendezvous token", true),
69                parameter(
70                    "secure",
71                    "boolean",
72                    "Request an implicit TLS peer connection rather than cleartext ADC",
73                    false,
74                ),
75            ],
76            json!({"type":"adc_reverse_connect","destination":"AAAA","token":"1"}),
77        ),
78        action(
79            "adc_search_result",
80            "Send a search result to a peer",
81            vec![
82                parameter("destination", "string", "Destination SID", true),
83                parameter(
84                    "identifier",
85                    "string",
86                    "Shared relative file name returned in the search result",
87                    true,
88                ),
89                parameter("size", "number", "Unsigned byte count", true),
90                parameter("tth", "string", "Tiger tree hash", true),
91                parameter(
92                    "token",
93                    "string",
94                    "Correlation token from the original ADC search request",
95                    true,
96                ),
97            ],
98            json!({"type":"adc_search_result","destination":"AAAA","identifier":"hello.txt","size":5,"tth":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","token":"1"}),
99        ),
100        action(
101            "disconnect",
102            "Close this peer",
103            vec![],
104            json!({"type":"disconnect"}),
105        ),
106    ]
107}
108impl Protocol for AdcClientProtocol {
109    fn protocol_name(&self) -> &'static str {
110        "ADC"
111    }
112    fn stack_name(&self) -> &'static str {
113        "ETH>IP>TCP>ADC"
114    }
115    fn keywords(&self) -> Vec<&'static str> {
116        vec!["adc", "adc"]
117    }
118    fn description(&self) -> &'static str {
119        "Selected ADC hub and peer operations connecting role"
120    }
121    fn example_prompt(&self) -> &'static str {
122        "Connect to ADC on 127.0.0.1:1511"
123    }
124    fn group_name(&self) -> &'static str {
125        "Peer-to-peer"
126    }
127    fn get_startup_parameters(&self) -> Vec<crate::llm::actions::ParameterDefinition> {
128        crate::server::p2p_support::tls_parameters(false)
129    }
130    fn get_startup_examples(&self) -> crate::llm::actions::StartupExamples {
131        let base = json!({"type":"open_client","protocol":"adc","remote_addr":"127.0.0.1:1511","instruction":"Use selected ADC operations"});
132        let mut fixed = base.clone();
133        fixed["event_handlers"] = json!([{"event_pattern":"adc_connected","handler":{"type":"static","actions":[requests()[0].example]}}]);
134        let mut script = base.clone();
135        script["event_handlers"] = json!([{"event_pattern":"adc_connected","handler":{"type":"script","language":"python","code":"import json\nprint(json.dumps({'actions':[{'type':'disconnect'}]}))"}}]);
136        crate::llm::actions::StartupExamples::new(base, script, fixed)
137    }
138    fn get_async_actions(&self, _: &AppState) -> Vec<ActionDefinition> {
139        requests()
140    }
141    fn get_sync_actions(&self) -> Vec<ActionDefinition> {
142        vec![]
143    }
144    fn get_event_types(&self) -> Vec<EventType> {
145        EVENTS.clone()
146    }
147    fn metadata(&self) -> crate::protocol::ProtocolMetadataV2 {
148        crate::protocol::ProtocolMetadataV2::builder().state(crate::protocol::metadata::DevelopmentState::Experimental).implementation("Native bounded ADC selected codec; TCP and optional implicit TLS").llm_control("Handler-approved identities, public/private chat, search and peer-connect routing").e2e_testing("Independent peer exchanges, malformed input, owner shutdown and standalone feature builds").notes("ADC 1.0 BASE/TIGR, anonymous identities with CID/PID validation and per-connection IDs. ADCS uses verified implicit TLS. Selected BINF/BMSG/DMSG/BSCH/DRES/DCTM/DRCM routing. No GPA/PAS password login, UDP search, NAT traversal or persistent hub account database. 256 connections, 30 s first frame, 600 s idle, 10 s handshake/exchange. No persistent protocol data; handlers supply content and decisions.").max_inbound_bytes(crate::server::adc::codec::MAX_COMMAND).well_known_port(1511).build()
149    }
150}
151impl Client for AdcClientProtocol {
152    fn connect(
153        &self,
154        ctx: ConnectContext,
155    ) -> Pin<Box<dyn Future<Output = Result<SocketAddr>> + Send>> {
156        Box::pin(super::connect(ctx))
157    }
158    fn execute_action(&self, v: Value) -> Result<ClientActionResult> {
159        if v["type"] == "disconnect" {
160            return Ok(ClientActionResult::Disconnect);
161        }
162        crate::server::adc::codec::validate(&v)?;
163        Ok(ClientActionResult::Custom {
164            name: v["type"].as_str().expect("validated type").into(),
165            data: v,
166        })
167    }
168}
169pub static EVENTS: LazyLock<Vec<EventType>> = LazyLock::new(|| {
170    vec![
171        EventType::new(
172            "adc_connected",
173            "Peer negotiation completed",
174            json!({"type":"disconnect"}),
175        )
176        .with_actions(requests()),
177        EventType::new(
178            "adc_response",
179            "Protocol response or incoming message",
180            json!({"type":"disconnect"}),
181        )
182        .with_actions(requests()),
183    ]
184});