daemon: download.add / download.list / download.get behind the store

VeloxDispatcher now takes a store::Db& and three handlers are real:

- download.list -> store::Tasks::list (filter / sort / paging in SQL) ->
  to_summary per row. No more empty-table stub.
- download.add -> resolve saveDir (spec, else saveTo.defaultDir; ~ expanded)
  and the leaf (spec.filename, else the URL's last segment percent-decoded,
  else download.bin) -> fs::resolve_target against canonicalize_root'd
  saveTo.allowedRoots. Any path-destination failure is -32011 with the
  *original* saveDir in data.path. On success a TaskRow is inserted in
  state `queued` (or `new` for startMode "manual") and {taskId, state}
  returned. The scheduler that would then admit it is D4.
- download.get -> store::Tasks::get; a real -32010 + data.taskId for an
  unknown id, else a TaskDetail (segmentDetail empty until the engine
  segments the task, which the schema permits).

util/time.hpp: now_iso() factored out of ws_server.cpp.

main.cpp constructs the dispatcher with the opened db. The three
integration tests build an in-memory migrated db for it; velox.client
now drives the full slice through the CLI — add outside roots -> -32011
with data.path, add into an allowed root -> a task that download.list
shows and download.get details, unknown id -> -32010. Verified with the
real binaries: velox add persists, velox ls shows it, it survives a
daemon restart, /etc is refused.

ASan+UBSan and TSan clean; 34 daemon/cli tests green. deferrals.md:
D2 down to just download.probe; D3 down to categories/queues/rules/
settings/limiter/schedule.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Upd9WhG9oppieig5nRDLig
This commit is contained in:
2026-09-10 19:58:45 +04:00
co-authored by Claude Sonnet 5
parent 7514f5a4c4
commit 3db0d01f9d
10 changed files with 257 additions and 42 deletions
+12 -9
View File
@@ -5,22 +5,22 @@
// parse; a method here only ever sees a validated, typed params struct and returns a
// typed result.
//
// Scope of this drop (AGENT-DAEMON.md build order): the transport is real, the store is
// not. session.hello / session.pair / session.subscribe are handled in the server layer
// (they are connection- and transport-stateful) and never reach this class in the running
// daemon. download.list answers with an empty table so a client can connect and render.
// Every other method returns "not implemented in this build" — which the generated
// dispatch() surfaces as -32603 — until the store and scheduler land.
//
// The -32603 collapse for genuine in-handler errors (-32010 / -32011 / -32013) is a known
// codegen gap, filed as P1 in daemon/docs/proto-requests-m1.md. Not worked around here.
// Scope of this drop (AGENT-DAEMON.md build order): the transports are real and the store
// is behind download.add / download.list / download.get. session.hello / session.pair /
// session.subscribe are handled in the server layer (connection- and transport-stateful)
// and never reach this class in the running daemon. Everything else still returns
// "not implemented in this build" (-> -32603) until its handler and the scheduler land;
// see daemon/docs/deferrals.md.
#include "store/sqlite.hpp"
#include "velox_proto.hpp"
namespace velox::daemon::rpc {
class VeloxDispatcher final : public velox::proto::Dispatcher {
public:
explicit VeloxDispatcher(velox::daemon::store::Db& db) : db_(db) {}
velox::proto::HandlerResult<velox::proto::CaptureRules>
on_capture_getRules(const velox::proto::CaptureGetRulesParams&) override;
velox::proto::HandlerResult<velox::proto::CaptureOfferResult>
@@ -98,6 +98,9 @@ public:
on_settings_get(const velox::proto::SettingsGetParams&) override;
velox::proto::HandlerResult<velox::proto::SettingsSetResult>
on_settings_set(const velox::proto::SettingsSetParams&) override;
private:
velox::daemon::store::Db& db_;
};
} // namespace velox::daemon::rpc