| 201 | snap_slot = -1; |
| 202 | size_t p = line.find(" snap="); |
| 203 | if (p != std::string::npos) { |
| 204 | int L = -1, S = -1; |
| 205 | if (std::sscanf(line.c_str() + p + 6, "%d:%d", &L, &S) == 2 && |
| 206 | L > 0 && S >= 0 && S < ModelBackend::kMaxSlots) { |
| 207 | snap_pos = L; |
| 208 | snap_slot = S; |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | // ── Main loop ─────────────────────────────────────────────────────────── |
| 214 | |
| 215 | int run_daemon(ModelBackend & backend, const DaemonLoopArgs & args) { |
| 216 | |
| 217 | DaemonIO io; |
| 218 | io.stream_fd = args.stream_fd; |
| 219 | |
| 220 | backend.print_ready_banner(); |
| 221 | std::fflush(stdout); |
| 222 | |
| 223 | std::string line; |
| 224 | while (std::getline(std::cin, line)) { |
| 225 | if (line == "quit" || line == "exit") break; |
| 226 | |
| 227 | // ── Lifecycle commands (no sampler tail) ───────────────────── |
| 228 | |
| 229 | if (starts_with(line, "park")) { |
| 230 | std::string what; |
| 231 | if (line.size() > 5) what = line.substr(5); |
| 232 | const auto target = parse_park_target(what); |
| 233 | if (!target) { |
| 234 | std::fprintf(stderr, "[daemon] invalid park target: %s\n", |
| 235 | what.c_str()); |
| 236 | } else { |
| 237 | backend.park(*target); |
| 238 | } |
| 239 | io.emit(-1); |
| 240 | continue; |
| 241 | } |
| 242 | if (starts_with(line, "unpark")) { |
| 243 | std::string what; |
| 244 | if (line.size() > 7) what = line.substr(7); |
| 245 | const auto target = parse_park_target(what); |
| 246 | if (!target) { |
| 247 | std::fprintf(stderr, "[daemon] invalid unpark target: %s\n", |
| 248 | what.c_str()); |
| 249 | } else { |
| 250 | backend.unpark(*target); |
| 251 | } |
| 252 | io.emit(-1); |
| 253 | continue; |
| 254 | } |
| 255 | if (line == "free drafter" || line == "drafter free") { |
| 256 | backend.free_drafter(); |
| 257 | io.emit(-1); |
| 258 | continue; |
| 259 | } |
| 260 | if (starts_with(line, "compress ")) { |
no test coverage detected