-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathserver.cpp
More file actions
173 lines (154 loc) · 5.32 KB
/
Copy pathserver.cpp
File metadata and controls
173 lines (154 loc) · 5.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// server.cpp — WebServer orchestrator.
// All helper implementations are in server_helpers.cpp.
// All register_*() method bodies are in routes/routes_*.cpp.
// This file contains only: Impl state definition, register_routes(),
// WebServer public methods (ctor, dtor, run, stop).
#include "server_impl.hpp"
#include "remote_web_proxy.hpp"
namespace acecode::web {
using nlohmann::json;
WebServer::Impl::~Impl() {
if (global_session_search) global_session_search->stop();
if (!shutdown_requested.exchange(true)) {
std::lock_guard<std::mutex> stop_lock(listener_stop_mu);
app.stop();
}
// 先阻止 tracked-subagent producer 再停 flusher。若先停 flusher,
// 尚未解除的订阅仍可能标脏,却再也没有线程负责落盘。
if (subagent_tracker_state) {
std::lock_guard<std::mutex> lk(subagent_tracker_state->mu);
subagent_tracker_state->impl = nullptr;
}
std::vector<std::pair<std::string, SessionClient::SubscriptionId>> subs;
{
std::lock_guard<std::mutex> lk(tracked_subagents_mu);
subs.reserve(tracked_subagent_subscriptions.size());
for (const auto& [sid, sub] : tracked_subagent_subscriptions) {
subs.emplace_back(sid, sub);
}
tracked_subagent_subscriptions.clear();
}
if (deps.session_client) {
for (const auto& [sid, sub] : subs) {
deps.session_client->unsubscribe(sid, sub);
}
}
// 所有已知 attention producer 均已停用,最后一次 flush 不会再漏掉
// 在 shutdown / unsubscribe 期间到达的事件。
stop_attention_flusher();
}
// =====================================================================
// register_routes — dispatches to each domain's register_*()
// =====================================================================
void WebServer::Impl::register_routes() {
register_health();
register_usage();
register_workspaces();
register_pinned_sessions();
register_sessions();
register_models();
register_experts();
register_loops();
register_ui_preferences();
register_history();
register_files();
register_git();
register_lsp();
register_skills();
register_commands();
register_mcp();
register_hooks();
register_feedback();
register_pty();
register_websocket();
register_static();
}
// =====================================================================
// WebServer public methods
// =====================================================================
WebServer::WebServer(WebServerDeps deps)
: impl_(std::make_unique<Impl>(std::move(deps))) {
try {
std::string dir = impl_->deps.web_cfg ? impl_->deps.web_cfg->static_dir : std::string{};
impl_->assets = make_asset_source(dir);
} catch (const std::exception& e) {
LOG_ERROR(std::string("[web] failed to init asset source: ") + e.what());
}
impl_->register_routes();
}
WebServer::~WebServer() = default;
int WebServer::run() {
if (!impl_->deps.web_cfg) {
LOG_ERROR("[web] missing web_cfg");
return 1;
}
WebConfig cfg;
{
std::shared_lock<std::shared_mutex> config_lock(impl_->app_config_mu);
cfg = *impl_->deps.web_cfg;
}
cfg.port = impl_->runtime_port;
auto preflight = preflight_bind_check(
cfg.bind,
impl_->deps.token,
impl_->deps.dangerous);
if (!preflight.empty()) {
LOG_ERROR("[web] " + preflight);
return 2;
}
{
std::lock_guard<std::mutex> lock(impl_->listener_state_mu);
impl_->effective_bind = cfg.bind;
impl_->effective_port = cfg.port;
}
LOG_INFO(
"[web] listening on " + cfg.bind + ":" +
std::to_string(cfg.port));
try {
impl_->app
.bindaddr(cfg.bind)
.port(static_cast<std::uint16_t>(cfg.port))
.multithreaded()
.run();
} catch (const std::exception& e) {
LOG_ERROR(std::string("[web] server crashed: ") + e.what());
LOG_ERROR("[web] port " + std::to_string(cfg.port) +
" may be in use — change web.port in config.json or stop "
"the conflicting process; daemon will not retry");
return 3;
}
return 0;
}
void WebServer::stop() {
if (!impl_) return;
if (impl_->shutdown_requested.exchange(true)) return;
std::lock_guard<std::mutex> stop_lock(impl_->listener_stop_mu);
impl_->app.stop();
}
void WebServer::track_subagent(const std::string& child_session_id) {
if (impl_) impl_->track_subagent(child_session_id);
}
void WebServer::refresh_saved_models_from_disk() {
if (impl_) impl_->refresh_saved_models_from_disk();
}
void WebServer::with_app_config_lock(const std::function<void()>& fn) const {
if (!fn) return;
if (!impl_) {
fn();
return;
}
std::lock_guard<std::shared_mutex> lock(impl_->app_config_mu);
fn();
}
void WebServer::broadcast_remote_control_session_selected(
const std::string& session_id,
const std::string& workspace_hash,
const std::string& cwd,
bool no_workspace,
const std::string& title,
const std::string& updated_at) {
if (!impl_) return;
impl_->broadcast_remote_control_session_selected(
session_id, workspace_hash, cwd, no_workspace, title, updated_at);
}
} // namespace acecode::web