Skip to content

Commit b2051c6

Browse files
committed
revert: remove automatic opencode config refresh experiment
1 parent 682eca4 commit b2051c6

File tree

2 files changed

+38
-162
lines changed

2 files changed

+38
-162
lines changed

src-tauri/src/backend/app_server.rs

Lines changed: 14 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use std::path::{Path, PathBuf};
66
use std::sync::Arc;
77
use std::time::Duration;
88

9-
use chrono::{DateTime, Utc};
109
use futures_util::StreamExt;
1110
use reqwest_eventsource::{Event, EventSource};
1211
use tokio::process::{Child, Command};
@@ -54,7 +53,6 @@ const REST_PORT: u16 = 14096;
5453
struct ServerProcess {
5554
child: Child,
5655
base_url: String,
57-
started_at: DateTime<Utc>,
5856
}
5957

6058
fn rest_base_url() -> String {
@@ -80,7 +78,7 @@ fn pid_file_path() -> Option<PathBuf> {
8078
}
8179

8280
/// Write PID file after starting the server.
83-
async fn write_pid_file(pid: u32, port: u16, started_at: DateTime<Utc>) -> Result<(), String> {
81+
async fn write_pid_file(pid: u32, port: u16) -> Result<(), String> {
8482
let path = pid_file_path().ok_or("Could not determine PID file path")?;
8583
if let Some(parent) = path.parent() {
8684
tokio::fs::create_dir_all(parent)
@@ -90,7 +88,7 @@ async fn write_pid_file(pid: u32, port: u16, started_at: DateTime<Utc>) -> Resul
9088
let data = PidFileData {
9189
pid,
9290
port,
93-
started_at: started_at.to_rfc3339(),
91+
started_at: chrono::Utc::now().to_rfc3339(),
9492
};
9593
let json = serde_json::to_string_pretty(&data).map_err(|e| e.to_string())?;
9694
tokio::fs::write(&path, json)
@@ -119,105 +117,31 @@ fn is_process_running(pid: u32) -> bool {
119117
}
120118

121119
/// Try to reclaim an orphaned server (one we previously started but lost track of).
122-
/// Returns PID metadata when we successfully reclaimed, None otherwise.
123-
async fn try_reclaim_orphaned_server() -> Option<PidFileData> {
120+
/// Returns true if we successfully reclaimed, false otherwise.
121+
async fn try_reclaim_orphaned_server() -> bool {
124122
let pid_data = match read_pid_file().await {
125123
Some(data) => data,
126-
None => return None,
124+
None => return false,
127125
};
128126

129127
// Check if the process is still running
130128
if !is_process_running(pid_data.pid) {
131129
// Stale PID file, clean it up
132130
delete_pid_file().await;
133-
return None;
131+
return false;
134132
}
135133

136134
// Process is running - check if it's actually our server on the expected port
137135
let base_url = rest_base_url();
138136
if health_check(&base_url).await.is_err() {
139137
// Process exists but isn't responding as our server - stale PID file
140138
delete_pid_file().await;
141-
return None;
139+
return false;
142140
}
143141

144142
// Server is alive and healthy - this is an orphaned server we can reclaim
145143
// We can't actually adopt the Child handle, but we can track that we own it via PID
146-
Some(pid_data)
147-
}
148-
149-
fn parse_rfc3339_timestamp(value: &str) -> Option<DateTime<Utc>> {
150-
DateTime::parse_from_rfc3339(value)
151-
.ok()
152-
.map(|ts| ts.with_timezone(&Utc))
153-
}
154-
155-
async fn server_config_path(base_url: &str) -> Option<PathBuf> {
156-
let client = reqwest::Client::builder()
157-
.timeout(Duration::from_secs(3))
158-
.build()
159-
.ok()?;
160-
let response = client
161-
.get(format!("{base_url}/path"))
162-
.send()
163-
.await
164-
.ok()?;
165-
if !response.status().is_success() {
166-
return None;
167-
}
168-
let payload = response.json::<Value>().await.ok()?;
169-
let config = payload.get("config")?.as_str()?.trim();
170-
if config.is_empty() {
171-
return None;
172-
}
173-
Some(PathBuf::from(config))
174-
}
175-
176-
async fn latest_directory_change(root: PathBuf) -> Option<DateTime<Utc>> {
177-
let mut latest = None;
178-
let mut stack = vec![root];
179-
180-
while let Some(dir) = stack.pop() {
181-
let mut entries = match tokio::fs::read_dir(&dir).await {
182-
Ok(entries) => entries,
183-
Err(_) => continue,
184-
};
185-
186-
while let Ok(Some(entry)) = entries.next_entry().await {
187-
let metadata = match entry.metadata().await {
188-
Ok(metadata) => metadata,
189-
Err(_) => continue,
190-
};
191-
192-
if let Ok(modified) = metadata.modified() {
193-
let modified_at = DateTime::<Utc>::from(modified);
194-
latest = Some(match latest {
195-
Some(current) if current >= modified_at => current,
196-
_ => modified_at,
197-
});
198-
}
199-
200-
if metadata.is_dir() {
201-
stack.push(entry.path());
202-
}
203-
}
204-
}
205-
206-
latest
207-
}
208-
209-
async fn should_restart_for_config_change(server_started_at: DateTime<Utc>, base_url: &str) -> bool {
210-
let config_root = server_config_path(base_url)
211-
.await
212-
.or_else(crate::codex::home::resolve_default_codex_home);
213-
let Some(config_root) = config_root else {
214-
return false;
215-
};
216-
217-
latest_directory_change(config_root)
218-
.await
219-
.map(|latest_change| latest_change > server_started_at)
220-
.unwrap_or(false)
144+
true
221145
}
222146

223147
/// Kill process listening on the REST port (for takeover functionality).
@@ -291,7 +215,6 @@ async fn start_managed_server_process(
291215
codex_args: Option<&str>,
292216
) -> Result<ServerProcess, String> {
293217
let base_url = rest_base_url();
294-
let started_at = Utc::now();
295218
let mut command = build_codex_command_with_bin(
296219
codex_bin,
297220
codex_args,
@@ -316,7 +239,7 @@ async fn start_managed_server_process(
316239

317240
// Write PID file for ownership tracking
318241
if let Some(pid) = child.id() {
319-
if let Err(e) = write_pid_file(pid, REST_PORT, started_at.clone()).await {
242+
if let Err(e) = write_pid_file(pid, REST_PORT).await {
320243
eprintln!("Warning: failed to write PID file: {e}");
321244
}
322245
}
@@ -333,11 +256,7 @@ async fn start_managed_server_process(
333256
sleep(Duration::from_millis(200)).await;
334257
}
335258

336-
Ok(ServerProcess {
337-
child,
338-
base_url,
339-
started_at,
340-
})
259+
Ok(ServerProcess { child, base_url })
341260
}
342261

343262
async fn ensure_server_running(
@@ -346,29 +265,14 @@ async fn ensure_server_running(
346265
) -> Result<String, String> {
347266
let base_url = rest_base_url();
348267

349-
// Fast path: if already initialized and config has not changed, return the URL.
350-
if let Some(server_mutex) = SERVER_PROCESS.get() {
351-
let (started_at, server_base_url) = {
352-
let guard = server_mutex.lock().await;
353-
(guard.started_at, guard.base_url.clone())
354-
};
355-
if should_restart_for_config_change(started_at, &server_base_url).await {
356-
restart_opencode_server(codex_bin.clone(), codex_args).await?;
357-
}
268+
// Fast path: if already initialized, just return the URL.
269+
if SERVER_PROCESS.get().is_some() {
358270
return Ok(base_url);
359271
}
360272

361273
// Check if we have an orphaned server we can reclaim (via PID file).
362274
// This happens when the app crashed/exited but the server kept running.
363-
if let Some(pid_data) = try_reclaim_orphaned_server().await {
364-
if let Some(started_at) = parse_rfc3339_timestamp(&pid_data.started_at) {
365-
if should_restart_for_config_change(started_at, &base_url).await {
366-
restart_opencode_server(codex_bin, codex_args).await?;
367-
}
368-
} else {
369-
// Legacy/invalid PID timestamp - safest option is to restart.
370-
restart_opencode_server(codex_bin, codex_args).await?;
371-
}
275+
if try_reclaim_orphaned_server().await {
372276
return Ok(base_url);
373277
}
374278

@@ -584,10 +488,6 @@ pub(crate) async fn takeover_external_server(
584488

585489
pub(crate) struct WorkspaceSession {
586490
pub(crate) entry: WorkspaceEntry,
587-
/// Resolved OpenCode binary used to (re)start the managed server when needed.
588-
pub(crate) codex_bin: Option<String>,
589-
/// Resolved OpenCode args used to (re)start the managed server when needed.
590-
pub(crate) codex_args: Option<String>,
591491
/// HTTP client for REST calls to the OpenCode server.
592492
pub(crate) http_client: reqwest::Client,
593493
/// Base URL of the OpenCode server (e.g. "http://127.0.0.1:14096").
@@ -630,7 +530,6 @@ async fn route_translated_event_to_background_callback(
630530
impl WorkspaceSession {
631531
/// Send a GET request to the OpenCode REST API, scoped to this workspace.
632532
pub(crate) async fn rest_get(&self, path: &str) -> Result<Value, String> {
633-
ensure_server_running(self.codex_bin.clone(), self.codex_args.as_deref()).await?;
634533
let separator = if path.contains('?') { "&" } else { "?" };
635534
let url = format!(
636535
"{}{path}{separator}directory={}",
@@ -653,7 +552,6 @@ impl WorkspaceSession {
653552

654553
/// Send a POST request to the OpenCode REST API, scoped to this workspace.
655554
pub(crate) async fn rest_post(&self, path: &str, body: Value) -> Result<Value, String> {
656-
ensure_server_running(self.codex_bin.clone(), self.codex_args.as_deref()).await?;
657555
let separator = if path.contains('?') { "&" } else { "?" };
658556
let url = format!(
659557
"{}{path}{separator}directory={}",
@@ -686,7 +584,6 @@ impl WorkspaceSession {
686584

687585
/// Send a POST request that returns a boolean (e.g. abort, permissions).
688586
pub(crate) async fn rest_post_bool(&self, path: &str, body: Value) -> Result<bool, String> {
689-
ensure_server_running(self.codex_bin.clone(), self.codex_args.as_deref()).await?;
690587
let separator = if path.contains('?') { "&" } else { "?" };
691588
let url = format!(
692589
"{}{path}{separator}directory={}",
@@ -712,7 +609,6 @@ impl WorkspaceSession {
712609

713610
/// Send a PATCH request to the OpenCode REST API, scoped to this workspace.
714611
pub(crate) async fn rest_patch(&self, path: &str, body: Value) -> Result<Value, String> {
715-
ensure_server_running(self.codex_bin.clone(), self.codex_args.as_deref()).await?;
716612
let separator = if path.contains('?') { "&" } else { "?" };
717613
let url = format!(
718614
"{}{path}{separator}directory={}",
@@ -1087,12 +983,10 @@ pub(crate) async fn spawn_workspace_session<E: EventSink>(
1087983
.clone()
1088984
.filter(|value| !value.trim().is_empty())
1089985
.or(default_codex_bin);
1090-
let resolved_codex_args = codex_args;
1091986
let _ = check_codex_installation(codex_bin.clone()).await?;
1092987

1093988
// Ensure the shared `opencode serve` process is running.
1094-
let base_url =
1095-
ensure_server_running(codex_bin.clone(), resolved_codex_args.as_deref()).await?;
989+
let base_url = ensure_server_running(codex_bin, codex_args.as_deref()).await?;
1096990

1097991
let http_client = reqwest::Client::builder()
1098992
.timeout(Duration::from_secs(300))
@@ -1103,8 +997,6 @@ pub(crate) async fn spawn_workspace_session<E: EventSink>(
1103997

1104998
let session = Arc::new(WorkspaceSession {
1105999
entry: entry.clone(),
1106-
codex_bin,
1107-
codex_args: resolved_codex_args,
11081000
http_client,
11091001
base_url,
11101002
background_thread_callbacks: Mutex::new(HashMap::new()),

src/features/threads/hooks/useThreadMessaging.ts

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -191,53 +191,37 @@ export function useThreadMessaging({
191191
);
192192

193193
useEffect(() => {
194-
const workspaceId = activeWorkspace?.id;
195-
const connected = Boolean(activeWorkspace?.connected);
196-
if (!workspaceId || !connected) {
194+
if (!activeWorkspace?.id || !activeWorkspace.connected) {
197195
return;
198196
}
199-
200197
let cancelled = false;
201-
202-
const refreshSlashCommands = () => {
203-
void listSlashCommandsService(workspaceId)
204-
.then((response) => {
205-
if (cancelled) {
206-
return;
207-
}
208-
const normalized = normalizeSlashCommandsResponse(response);
209-
setSlashCommandsByWorkspace((prev) => ({
210-
...prev,
211-
[workspaceId]: normalized,
212-
}));
213-
})
214-
.catch((error) => {
215-
if (cancelled) {
216-
return;
217-
}
218-
onDebug?.({
219-
id: `${Date.now()}-client-slash-commands-list-error`,
220-
timestamp: Date.now(),
221-
source: "error",
222-
label: "slash_commands/list error",
223-
payload: error instanceof Error ? error.message : String(error),
224-
});
198+
void listSlashCommandsService(activeWorkspace.id)
199+
.then((response) => {
200+
if (cancelled) {
201+
return;
202+
}
203+
const normalized = normalizeSlashCommandsResponse(response);
204+
setSlashCommandsByWorkspace((prev) => ({
205+
...prev,
206+
[activeWorkspace.id]: normalized,
207+
}));
208+
})
209+
.catch((error) => {
210+
if (cancelled) {
211+
return;
212+
}
213+
onDebug?.({
214+
id: `${Date.now()}-client-slash-commands-list-error`,
215+
timestamp: Date.now(),
216+
source: "error",
217+
label: "slash_commands/list error",
218+
payload: error instanceof Error ? error.message : String(error),
225219
});
226-
};
227-
228-
refreshSlashCommands();
229-
const intervalId = window.setInterval(refreshSlashCommands, 15_000);
230-
const onFocus = () => {
231-
refreshSlashCommands();
232-
};
233-
window.addEventListener("focus", onFocus);
234-
220+
});
235221
return () => {
236222
cancelled = true;
237-
window.clearInterval(intervalId);
238-
window.removeEventListener("focus", onFocus);
239223
};
240-
}, [activeWorkspace?.connected, activeWorkspace?.id, onDebug]);
224+
}, [activeWorkspace, onDebug]);
241225

242226
const sendMessageToThread = useCallback(
243227
async (

0 commit comments

Comments
 (0)