Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit 2ec4079

Browse files
z23ccclaude
andcommitted
refactor: remove daemon, scheduler, and frontend
Remove flowctl-daemon (3130 LOC), flowctl-scheduler, and frontend/ (15587 files). These are unnecessary with JSON file storage — CLI is the sole interface. Stub daemon transport in approval.rs (returns None). Remove hyper/axum/tower-http/notify/bytes deps from CLI. Net: -11,275 lines. 243 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7a5d959 commit 2ec4079

71 files changed

Lines changed: 23 additions & 11275 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

flowctl/Cargo.lock

Lines changed: 11 additions & 562 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flowctl/Cargo.toml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ resolver = "2"
33
members = [
44
"crates/flowctl-core",
55
"crates/flowctl-db",
6-
"crates/flowctl-scheduler",
76
"crates/flowctl-service",
87
"crates/flowctl-cli",
9-
"crates/flowctl-daemon",
108
]
119

1210
# ── Shared package metadata (nushell convention) ──────────────────────
@@ -57,16 +55,10 @@ clap = { version = "4", features = ["derive"] }
5755
clap_complete = "4"
5856
miette = { version = "7", features = ["fancy"] }
5957

60-
# Async runtime (daemon + scheduler)
58+
# Async runtime (service layer)
6159
tokio = { version = "1", features = ["full"] }
6260
tokio-util = { version = "0.7", features = ["rt"] }
6361

64-
# HTTP (daemon)
65-
axum = { version = "0.8", features = ["ws"] }
66-
67-
# File watching (daemon)
68-
notify = "7"
69-
7062
# Unix process management
7163
nix = { version = "0.30", features = ["signal", "process"] }
7264

@@ -79,7 +71,6 @@ trycmd = "0.15"
7971
# Internal crate references
8072
flowctl-core = { path = "crates/flowctl-core" }
8173
flowctl-db = { path = "crates/flowctl-db" }
82-
flowctl-scheduler = { path = "crates/flowctl-scheduler" }
8374
flowctl-service = { path = "crates/flowctl-service" }
8475

8576
# ── Release profile (size-optimized) ─────────────────────────────────

flowctl/crates/flowctl-cli/Cargo.toml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,11 @@ license.workspace = true
1010
name = "flowctl"
1111
path = "src/main.rs"
1212

13-
[features]
14-
default = ["daemon"]
15-
daemon = ["dep:flowctl-daemon", "dep:axum", "dep:tower-http"]
16-
1713
[dependencies]
1814
flowctl-core = { workspace = true }
1915
flowctl-db = { workspace = true }
2016
flowctl-service = { workspace = true }
2117
libsql = { workspace = true }
22-
flowctl-scheduler = { workspace = true }
23-
flowctl-daemon = { path = "../flowctl-daemon", features = ["daemon"], optional = true }
24-
axum = { workspace = true, optional = true }
25-
tower-http = { version = "0.6", features = ["fs"], optional = true }
2618
serde = { workspace = true }
2719
serde_json = { workspace = true }
2820
anyhow = { workspace = true }
@@ -36,10 +28,6 @@ regex = { workspace = true }
3628
sha2 = { workspace = true }
3729
thiserror = { workspace = true }
3830
which = { workspace = true }
39-
hyper = { version = "1", features = ["client", "http1"] }
40-
hyper-util = { version = "0.1", features = ["client", "client-legacy", "tokio", "http1"] }
41-
http-body-util = "0.1"
42-
bytes = "1"
4331

4432
[dev-dependencies]
4533
trycmd = { workspace = true }

flowctl/crates/flowctl-cli/src/commands/approval.rs

Lines changed: 11 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -198,90 +198,23 @@ async fn daemon_request(
198198
TransportResult::Unreachable
199199
}
200200

201+
/// Daemon removed — transport stubs return None.
201202
async fn unix_socket_request(
202-
socket_path: &Path,
203-
method: &str,
204-
path: &str,
205-
body: Option<&Value>,
203+
_socket_path: &Path,
204+
_method: &str,
205+
_path: &str,
206+
_body: Option<&Value>,
206207
) -> Option<TransportResult> {
207-
use http_body_util::{BodyExt, Full};
208-
use hyper_util::rt::TokioIo;
209-
210-
let stream = tokio::net::UnixStream::connect(socket_path).await.ok()?;
211-
let io = TokioIo::new(stream);
212-
let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await.ok()?;
213-
tokio::spawn(async move {
214-
let _ = conn.await;
215-
});
216-
217-
let body_bytes = match body {
218-
Some(v) => serde_json::to_vec(v).ok()?,
219-
None => Vec::new(),
220-
};
221-
222-
let req = hyper::Request::builder()
223-
.method(method)
224-
.uri(path)
225-
.header("host", "localhost")
226-
.header("content-type", "application/json")
227-
.body(Full::new(bytes::Bytes::from(body_bytes)))
228-
.ok()?;
229-
230-
let resp = sender.send_request(req).await.ok()?;
231-
let status = resp.status().as_u16();
232-
let collected = resp.into_body().collect().await.ok()?.to_bytes();
233-
let json_body: Value = if collected.is_empty() {
234-
Value::Null
235-
} else {
236-
serde_json::from_slice(&collected).ok()?
237-
};
238-
Some(TransportResult::Response {
239-
status,
240-
body: json_body,
241-
})
208+
None // No daemon
242209
}
243210

244211
async fn tcp_request(
245-
port: u16,
246-
method: &str,
247-
path: &str,
248-
body: Option<&Value>,
212+
_port: u16,
213+
_method: &str,
214+
_path: &str,
215+
_body: Option<&Value>,
249216
) -> Option<TransportResult> {
250-
use http_body_util::{BodyExt, Full};
251-
use hyper_util::rt::TokioIo;
252-
253-
let stream = tokio::net::TcpStream::connect(("127.0.0.1", port)).await.ok()?;
254-
let io = TokioIo::new(stream);
255-
let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await.ok()?;
256-
tokio::spawn(async move {
257-
let _ = conn.await;
258-
});
259-
260-
let body_bytes = match body {
261-
Some(v) => serde_json::to_vec(v).ok()?,
262-
None => Vec::new(),
263-
};
264-
265-
let req = hyper::Request::builder()
266-
.method(method)
267-
.uri(path)
268-
.header("host", format!("127.0.0.1:{port}"))
269-
.header("content-type", "application/json")
270-
.body(Full::new(bytes::Bytes::from(body_bytes)))
271-
.ok()?;
272-
273-
let resp = sender.send_request(req).await.ok()?;
274-
let status = resp.status().as_u16();
275-
let collected = resp.into_body().collect().await.ok()?.to_bytes();
276-
let json_body: Value = if collected.is_empty() {
277-
Value::Null
278-
} else {
279-
serde_json::from_slice(&collected).ok()?
280-
};
281-
Some(TransportResult::Response {
282-
status,
283-
body: json_body,
284-
})
217+
None // No daemon
285218
}
286219

287220
// ── Direct-DB fallback ──────────────────────────────────────────────

flowctl/crates/flowctl-cli/src/main.rs

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -414,14 +414,6 @@ enum Commands {
414414
shell: Shell,
415415
},
416416

417-
// ── Daemon ───────────────────────────────────────────────────────
418-
/// Start the daemon process (HTTP API + scheduler + event bus).
419-
#[cfg(feature = "daemon")]
420-
Serve {
421-
/// Port override (default: Unix socket).
422-
#[arg(long)]
423-
port: Option<u16>,
424-
},
425417

426418
}
427419

@@ -575,114 +567,6 @@ fn main() {
575567
generate(shell, &mut cmd, "flowctl", &mut std::io::stdout());
576568
}
577569

578-
// Daemon
579-
#[cfg(feature = "daemon")]
580-
Commands::Serve { port } => {
581-
let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
582-
rt.block_on(async {
583-
let cwd = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
584-
let flow_dir = cwd.join(flowctl_core::types::FLOW_DIR);
585-
if !flow_dir.exists() {
586-
eprintln!("error: .flow/ does not exist. Run 'flowctl init' first.");
587-
std::process::exit(1);
588-
}
589-
590-
let paths = flowctl_daemon::lifecycle::DaemonPaths::new(&flow_dir);
591-
if let Err(e) = paths.ensure_state_dir() {
592-
eprintln!("error: failed to create state dir: {e}");
593-
std::process::exit(1);
594-
}
595-
596-
// Acquire PID lock.
597-
if let Err(e) = flowctl_daemon::lifecycle::acquire_pid_lock(&paths) {
598-
eprintln!("error: {e}");
599-
std::process::exit(1);
600-
}
601-
602-
// Clean up orphaned socket from previous run.
603-
let _ = flowctl_daemon::lifecycle::cleanup_orphaned_socket(&paths);
604-
605-
let runtime = flowctl_daemon::lifecycle::DaemonRuntime::new(paths.clone());
606-
let cancel = runtime.cancel.clone();
607-
608-
let (event_bus, _critical_rx) = flowctl_scheduler::EventBus::with_default_capacity();
609-
610-
// Start notification listener (sound + webhook).
611-
let notif_config = flowctl_daemon::notifications::load_config(&flow_dir);
612-
let notif_rx = event_bus.subscribe();
613-
flowctl_daemon::notifications::spawn_listener(
614-
&runtime.tracker,
615-
notif_rx,
616-
notif_config,
617-
);
618-
619-
// Handle Ctrl+C.
620-
let cancel_clone = cancel.clone();
621-
tokio::spawn(async move {
622-
tokio::signal::ctrl_c().await.ok();
623-
eprintln!("\nShutting down daemon...");
624-
cancel_clone.cancel();
625-
});
626-
627-
let result = if let Some(tcp_port) = port {
628-
println!("flowctl daemon starting on http://127.0.0.1:{tcp_port}");
629-
630-
// Build API router from daemon.
631-
let (state, cancel) = flowctl_daemon::server::create_state(runtime, event_bus)
632-
.await
633-
.expect("failed to create state");
634-
let api_router = flowctl_daemon::server::build_router(state);
635-
636-
// Serve static frontend from frontend/dist/ if it exists,
637-
// with SPA fallback to index.html for non-API routes.
638-
use axum::response::IntoResponse;
639-
let dist_dir = cwd.join("frontend").join("dist");
640-
let router = if dist_dir.exists() {
641-
let index_path = dist_dir.join("index.html");
642-
let spa_fallback = axum::routing::get(move || {
643-
let path = index_path.clone();
644-
async move {
645-
match tokio::fs::read_to_string(&path).await {
646-
Ok(html) => axum::response::Html(html).into_response(),
647-
Err(_) => axum::http::StatusCode::NOT_FOUND.into_response(),
648-
}
649-
}
650-
});
651-
api_router
652-
.fallback_service(
653-
tower_http::services::ServeDir::new(&dist_dir)
654-
.not_found_service(spa_fallback),
655-
)
656-
} else {
657-
api_router
658-
};
659-
660-
let addr = format!("127.0.0.1:{tcp_port}");
661-
let listener = tokio::net::TcpListener::bind(&addr).await
662-
.expect("failed to bind TCP");
663-
664-
axum::serve(listener, router)
665-
.with_graceful_shutdown(async move {
666-
cancel.cancelled().await;
667-
})
668-
.await
669-
.map_err(|e| anyhow::anyhow!("{e}"))
670-
} else {
671-
println!("flowctl daemon starting on {}", paths.socket_file.display());
672-
flowctl_daemon::server::serve(runtime, event_bus).await
673-
};
674-
675-
if let Err(e) = result {
676-
eprintln!("daemon error: {e}");
677-
std::process::exit(1);
678-
}
679-
680-
// Cleanup.
681-
let _ = std::fs::remove_file(&paths.socket_file);
682-
let _ = std::fs::remove_file(&paths.pid_file);
683-
println!("daemon stopped.");
684-
});
685-
}
686570

687571
}
688572
}

flowctl/crates/flowctl-daemon/Cargo.toml

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)