feat(daemon): RestartSurface honors resume — swap to resume_args when mapped

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 16:05:53 +07:00
parent eecea9c38c
commit 31c08b5387
+40 -1
View File
@@ -291,6 +291,23 @@ fn spawn_env(sid: &SurfaceId, spec: &spacesh_proto::workspace::SurfaceSpec) -> (
(env, active) (env, active)
} }
/// Build the spawn spec for a (re)start. When `resume` and the command has a
/// resume mapping, its args are replaced with the resume args; otherwise the
/// original spec args are kept.
fn resume_spec(
spec: &spacesh_proto::workspace::SurfaceSpec,
resume: bool,
cfg: &crate::config::Config,
) -> spacesh_proto::workspace::SurfaceSpec {
let mut out = spec.clone();
if resume {
if let Some(args) = cfg.resume_args(&spec.command) {
out.args = args;
}
}
out
}
/// Emit a `layout_changed` event for a workspace's current tree. /// Emit a `layout_changed` event for a workspace's current tree.
fn emit_layout(reg: &Registry, ws_id: &WorkspaceId, clients: &HashMap<ClientId, ClientTx>) { fn emit_layout(reg: &Registry, ws_id: &WorkspaceId, clients: &HashMap<ClientId, ClientTx>) {
if let Some(w) = reg.workspace(ws_id) { if let Some(w) = reg.workspace(ws_id) {
@@ -475,13 +492,14 @@ async fn handle_request(
let _ = out.send(ok(id, serde_json::json!({ "surface_ids": new_ids.iter().map(|s| s.0.clone()).collect::<Vec<_>>() }))).await; let _ = out.send(ok(id, serde_json::json!({ "surface_ids": new_ids.iter().map(|s| s.0.clone()).collect::<Vec<_>>() }))).await;
} }
Cmd::RestartSurface { surface_id } => { Cmd::RestartSurface { surface_id, resume } => {
if reg.is_running(&surface_id) { if reg.is_running(&surface_id) {
let _ = out.send(ok(id, serde_json::Value::Null)).await; return; // already running let _ = out.send(ok(id, serde_json::Value::Null)).await; return; // already running
} }
let Some(spec) = reg.surface_spec(&surface_id) else { let Some(spec) = reg.surface_spec(&surface_id) else {
let _ = out.send(err(id, "NOT_FOUND", "surface")).await; return; let _ = out.send(err(id, "NOT_FOUND", "surface")).await; return;
}; };
let spec = resume_spec(&spec, resume, config);
let ws_id = reg.workspace_of(&surface_id).unwrap(); let ws_id = reg.workspace_of(&surface_id).unwrap();
let (env, hooks_active) = spawn_env(&surface_id, &spec); let (env, hooks_active) = spawn_env(&surface_id, &spec);
match crate::surface::spawn_from_spec(surface_id.clone(), ws_id.clone(), &spec, env, hooks_active, state_tx.clone(), exit_tx.clone(), snapshot_tx.clone()) { match crate::surface::spawn_from_spec(surface_id.clone(), ws_id.clone(), &spec, env, hooks_active, state_tx.clone(), exit_tx.clone(), snapshot_tx.clone()) {
@@ -1607,4 +1625,25 @@ mod tests {
let snap = data["snapshot"].as_str().unwrap_or(""); let snap = data["snapshot"].as_str().unwrap_or("");
assert!(snap.contains("SNAPDISK"), "on-disk snapshot should contain SNAPDISK, got: {snap:?}"); assert!(snap.contains("SNAPDISK"), "on-disk snapshot should contain SNAPDISK, got: {snap:?}");
} }
#[test]
fn resume_spec_swaps_args_when_mapped() {
use spacesh_proto::workspace::SurfaceSpec;
let spec = SurfaceSpec {
command: "claude".into(), args: vec!["--foo".into()], cwd: "/tmp".into(),
agent_label: Some("claude".into()), cols: 80, rows: 24, autostart: false,
};
let cfg = crate::config::Config::default();
// resume=false → original args
let plain = resume_spec(&spec, false, &cfg);
assert_eq!(plain.args, vec!["--foo".to_string()]);
// resume=true with a default mapping → resume args
let resumed = resume_spec(&spec, true, &cfg);
assert_eq!(resumed.args, vec!["--continue".to_string()]);
// resume=true for an unmapped command → original args (graceful fallback)
let mut shell = spec.clone();
shell.command = "bash".into();
let resumed_shell = resume_spec(&shell, true, &cfg);
assert_eq!(resumed_shell.args, shell.args);
}
} }