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});