1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Exfiltrate gives your running program a command line.
//!
//! You write the commands in Rust, inside the program, against its live state. Then you —
//! or a coding agent — run them from a terminal while the program is running: on your
//! desktop, on a phone, in a browser tab, or on a box you can only reach over SSH.
//!
//! 
//!
//! ```rust,no_run
//! # // no_run because: the example starts a live server and requires an external CLI.
//! # #[cfg(target_arch = "wasm32")]
//! wasm_lite::set_panic_hook();
//! use exfiltrate::command::{Command, Response};
//!
//! # fn connected_players() -> usize { 3 }
//! struct Players;
//! impl Command for Players {
//! fn name(&self) -> &'static str { "players" }
//! fn short_description(&self) -> &'static str { "Count connected players" }
//! fn full_description(&self) -> &'static str { "Prints the number of connected players." }
//! fn execute(&self, _args: Vec<String>) -> Result<Response, Response> {
//! Ok(format!("{} players online", connected_players()).into())
//! }
//! }
//!
//! exfiltrate::begin(); // once, at startup
//! exfiltrate::add_command(Players);
//! // ... the rest of your program
//! ```
//!
//! ```text
//! $ exfiltrate help players
//! Prints the number of connected players.
//! $ exfiltrate players
//! 3 players online
//! ```
//!
//! # Why not a debugger?
//!
//! Because most of what you want from a running program isn't a breakpoint. It's *what's
//! in the cache right now*, *flip this flag*, *kick that job*, *dump the scene graph*,
//! *save me a screenshot of the framebuffer*. A debugger can get you there, but you'll be
//! typing expressions at an lldb prompt, and the moment you want the output formatted your
//! way you're writing formatters in a scripting dialect nobody else on the team knows.
//!
//! With exfiltrate you write that tooling once, in Rust, next to the code it inspects. It
//! costs one dependency and one call at startup, and it works the same everywhere your
//! program runs — including places a debugger can't attach. It's as useful for a library
//! as for an application: register a command that dumps whatever your library knows and
//! you have a live inspector for it.
//!
//! # Why not just read the source?
//!
//! Because agents, like people, reason better about a program they can poke than one they
//! can only read. Giving an LLM a purpose-built interface for running and observing the
//! target — instead of retrieval over source — took SWE-bench issue resolution from 3.8%
//! to 12.5%: more than 3× from the harness alone, with no change to the model.[^swe-agent]
//! Expose your program's nouns and verbs, and an agent can list the nouns and do the verbs
//! instead of guessing from source.
//!
//! [^swe-agent]: Yang et al., *SWE-agent: Agent–Computer Interfaces Enable Automated
//! Software Engineering*, NeurIPS 2024. <https://arxiv.org/abs/2405.15793>
//!
//! # Features
//!
//! - **One call to start.** Add the dependency, call [`begin()`]. No daemon, no config file.
//! - **No async runtime.** Plain threads, no `tokio`.
//! - **Text, files, images.** Commands return strings, binary files, or RGBA images; the CLI
//! prints or saves them.
//! - **Desktop, mobile, WebAssembly.** Browser targets go through a small proxy (see below).
//! - **Log capture, separately.** Logs come from the `logwise_agent_exfiltrate` package
//! rather than a feature of this one.
//!
//! # Quick start (Rust)
//!
//! 1. Add `exfiltrate` as a dependency. Call [`begin()`] as early as possible — the top of
//! `main` — and register your commands with [`add_command()`], as in the example above.
//!
//! 2. With the program running, drive it from the `exfiltrate` CLI. The native server
//! listens on `127.0.0.1:1337`; the CLI connects there automatically.
//!
//! In a sandbox that forbids binding a port, point both ends somewhere else with
//! `EXFILTRATE_ADDR` (or `with_addr` and `--addr`):
//! `unix:/path/to/socket` uses a Unix socket, access-controlled by its `0700` parent
//! directory rather than by "anyone on loopback"; `unix:@name` uses the Linux abstract
//! namespace; and `fd:3` adopts a connected `socketpair(2)` a supervising process already
//! handed the program, which needs no new socket at all.
//!
//! An address that is reachable from outside this machine needs a token, and gets one
//! without you arranging anything: the server invents one for the run and prints it, and
//! you type it into `exfiltrate --token`. Set `$EXFILTRATE_TOKEN` on both ends instead if
//! you would rather pin one, in which case nothing is printed. A token authenticates but
//! does not encrypt — debug output crossing a hostile network still wants a tunnel.
//! `exfiltrate connecting` explains the whole picture.
//!
//! ```text
//! exfiltrate list # every command available right now
//! exfiltrate help <command> # details for one command
//! exfiltrate <command> [args] # run it against the live program
//! ```
//!
//! `list` shows the CLI's own built-ins plus whatever the running program registered (a
//! local name wins on collision); if the program is down, it says so. The CLI is
//! self-documenting — `list`, then `help`, and there's nothing to memorize — which is also
//! what makes it easy to hand to an agent:
//!
//! ```text
//! claude "Run `exfiltrate list`, then integrate the exfiltrate library into my program."
//! ```
//!
//! Not on Rust, but on C or C++? That's a use case exfiltrate doesn't cover yet —
//! [file a feature request](https://github.com/drewcrawford/exfiltrate/issues/new) with
//! details about your setup.
//!
//! # Custom commands
//!
//! Implement [`command::Command`] and register it with [`add_command()`]. `name` is what
//! you type; `short_description` is what `list` shows; `full_description` is what `help`
//! prints — so write them for someone reading cold, human or agent.
//!
//! ## Response types
//!
//! A command's [`Response`](command::Response) can carry:
//!
//! - **Text** — the common case; anything that's `Into<Response>` from a `String`.
//! - **Files** — binary payloads via [`FileInfo`](command::FileInfo).
//! - **Images** — RGBA images via [`ImageInfo`](command::ImageInfo), built from
//! [`rgb::RGBA8`] pixels (the [`rgb`] crate is re-exported).
//!
//! The CLI prints text and writes files and images to disk. For worked examples of every
//! response type, run `exfiltrate help custom_commands`.
//!
//! # Architecture
//!
//! ## Why threads
//!
//! Most Rust networking crates pull in `tokio` or another async runtime. That's the right
//! call for a high-concurrency server and the wrong one for a debug shim you just want to
//! embed. Exfiltrate has no `tokio` dependency; it uses threads. Threads for everyone.
//!
//! ## WebAssembly
//!
//! A browser WASM app can't open raw TCP sockets, so exfiltrate bridges through a proxy:
//!
//! 1. The WASM app connects out to `exfiltrate proxy` over WebSockets.
//! 2. Another `exfiltrate` invocation connects to that proxy over TCP.
//! 3. The proxy relays between them, so the CLI drives the WASM app as if it were local.
//!
//! Start an installed copy with `exfiltrate proxy --help`, or run it from this
//! workspace with `cargo run -p exfiltrate_cli -- proxy --help`.
//!
//! # Log capture
//!
//! This crate has no feature flags. Log capture used to live here behind a
//! `logwise` feature; it is now the separate `logwise_agent_exfiltrate`
//! package, so a program that wants live state does not pull a logging
//! integration in with it, and one that wants logs asks for it by name.
//!
//! # Further reading
//!
//! Run `exfiltrate help integration` for embedding guidance, or `exfiltrate help
//! custom_commands` for the full response-type reference.
/// Re-export of the [`rgb`](https://docs.rs/rgb) crate for image pixel types.
///
/// Use [`rgb::RGBA8`] when constructing [`ImageInfo`](command::ImageInfo) responses.
pub use rgb;
use crateregister_commands;
use Command;
use RwLock;
pub use crateRegisterError;
pub use crate;
/// The configuration `begin` was called with.
///
/// Held so that anything asked about this process later — the handshake, the
/// `build_info` command — answers from the same settings the server started
/// from, rather than each re-deriving them.
static CONFIG: = new;
/// The configuration in force, or the defaults if `begin` has not run yet.
///
/// Commands can be executed from a test without a server, so this must answer
/// something sensible rather than panicking.
pub
/// Writes a diagnostic from the library itself.
///
/// Routed through one function so that everything exfiltrate says about its own
/// operation is easy to find and to filter out of a program's output — and so
/// that the browser, where `eprintln!` goes nowhere useful by default, has a
/// single place to be fixed.
pub
/// Initializes the exfiltrate debugging server.
///
/// This function should be called as early as possible in your application's lifecycle
/// (e.g., at the start of `main`). It starts the background server thread (or WASM worker)
/// that listens for connections from the CLI.
///
/// Calling this twice is harmless — the second call is a no-op.
///
/// # Example
///
/// ```rust
/// # #[cfg(target_arch = "wasm32")]
/// wasm_lite::set_panic_hook();
/// exfiltrate::begin();
///
/// // ... rest of your application
/// ```
/// Initializes the exfiltrate debugging server with explicit settings.
///
/// See [`Config`] for what can be set and what the defaults are. The two that
/// most often need changing:
///
/// * [`Config::addr`] — where to listen. `"127.0.0.1:0"` takes an ephemeral
/// port, which is how several debugged programs coexist on one machine;
/// the chosen port is printed and written to the instance registry.
/// * [`Config::app`] — the host program's name and version, which this crate
/// cannot discover on its own. Use [`app_info!`].
///
/// Calling this twice is harmless — the second call is a no-op — so a library
/// that wants a debug server does not have to coordinate with `main`.
///
/// # Example
///
/// ```rust
/// # #[cfg(target_arch = "wasm32")]
/// wasm_lite::set_panic_hook();
/// exfiltrate::begin_with(
/// exfiltrate::Config::default()
/// .with_addr("127.0.0.1:0")
/// .with_app(exfiltrate::app_info!()),
/// );
/// ```
/// Registers a custom command with the exfiltrate server.
///
/// Custom commands allow you to expose application-specific state or actions
/// to the CLI.
///
/// # Duplicate names
///
/// Names are unique. If the name is already taken, the existing command keeps
/// it, this registration is refused, and a diagnostic naming both is printed —
/// the collision is never silent, because a silently shadowed command means the
/// description a user read and the code that ran belong to different commands.
/// Use [`try_add_command`] when you would rather handle that yourself, and a
/// dotted prefix (`mycrate.stats`) when a name is likely to be contested.
///
/// # Example
///
/// ```rust
/// # #[cfg(target_arch = "wasm32")]
/// wasm_lite::set_panic_hook();
/// use exfiltrate::command::{Command, Response};
///
/// struct MyCommand;
/// impl Command for MyCommand {
/// fn name(&self) -> &'static str { "my_command" }
/// fn short_description(&self) -> &'static str { "Does something cool" }
/// fn full_description(&self) -> &'static str { "Does something cool..." }
/// fn execute(&self, _args: Vec<String>) -> Result<Response, Response> {
/// Ok("Cool!".into())
/// }
/// }
///
/// exfiltrate::add_command(MyCommand);
/// ```
/// Registers a custom command, reporting a name collision instead of printing it.
///
/// ```rust
/// # #[cfg(target_arch = "wasm32")]
/// wasm_lite::set_panic_hook();
/// use exfiltrate::command::{Command, Response};
///
/// struct Twice;
/// impl Command for Twice {
/// fn name(&self) -> &'static str { "doc_example_twice" }
/// fn short_description(&self) -> &'static str { "registered twice on purpose" }
/// fn full_description(&self) -> &'static str { "registered twice on purpose" }
/// fn execute(&self, _args: Vec<String>) -> Result<Response, Response> { Ok("".into()) }
/// }
///
/// assert!(exfiltrate::try_add_command(Twice).is_ok());
/// assert!(exfiltrate::try_add_command(Twice).is_err());
/// ```
/// Emits an event to every client subscribed to `topic`.
///
/// This is how an application says "the thing you were waiting for happened",
/// so a client can block on [`exfiltrate watch`] instead of polling. It is
/// designed to sit on a hot path: with nobody subscribed it is one relaxed
/// atomic load and a branch, and the payload closure is never called.
///
/// Topics are dotted names and a subscriber may glob a subtree (`render.*`);
/// see [`exfiltrate_internal::topic`] for the exact grammar.
///
/// Returns how many subscribers the event was queued for, which is zero in the
/// overwhelmingly common case that nobody is watching.
///
/// # Example
///
/// ```rust
/// # #[cfg(target_arch = "wasm32")]
/// wasm_lite::set_panic_hook();
/// // Cheap enough for a per-frame call site: the closure only runs if
/// // somebody is subscribed to a matching topic.
/// exfiltrate::emit("render.frame", || format!("frame {} presented", 12).into());
/// ```
///
/// [`exfiltrate watch`]: https://docs.rs/exfiltrate
/// Adds the calling thread to the list `exfiltrate threads` reports.
///
/// No Rust API enumerates a process's threads after the fact, so on any target
/// without `/proc` this is the only source there is. The entry is removed
/// automatically when the thread exits.
///
/// `note` is free-form: what the thread is for, what it is currently working on,
/// anything that would help someone looking at a wedged program.
///
/// ```rust
/// # #[cfg(not(target_arch = "wasm32"))]
/// # {
/// std::thread::Builder::new()
/// .name("render".to_string())
/// .spawn(|| {
/// exfiltrate::register_thread(Some("draws frames".to_string()));
/// })
/// .unwrap()
/// .join()
/// .unwrap();
/// # }
/// ```
/// Re-exports of types needed to implement custom commands.