Skip to content

Commit d237ecf

Browse files
committed
community batch
1 parent 4a3d570 commit d237ecf

File tree

16 files changed

+466
-45
lines changed

16 files changed

+466
-45
lines changed

Cargo.lock

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/openfang-api/src/middleware.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ pub async fn security_headers(request: Request<Body>, next: Next) -> Response<Bo
196196
"cache-control",
197197
"no-store, no-cache, must-revalidate".parse().unwrap(),
198198
);
199+
headers.insert(
200+
"strict-transport-security",
201+
"max-age=63072000; includeSubDomains".parse().unwrap(),
202+
);
199203
response
200204
}
201205

crates/openfang-api/src/routes.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5030,6 +5030,90 @@ pub async fn update_agent(
50305030
)
50315031
}
50325032

5033+
/// PATCH /api/agents/{id} — Partial update of agent fields (name, description, model, system_prompt).
5034+
pub async fn patch_agent(
5035+
State(state): State<Arc<AppState>>,
5036+
Path(id): Path<String>,
5037+
Json(body): Json<serde_json::Value>,
5038+
) -> impl IntoResponse {
5039+
let agent_id: AgentId = match id.parse() {
5040+
Ok(id) => id,
5041+
Err(_) => {
5042+
return (
5043+
StatusCode::BAD_REQUEST,
5044+
Json(serde_json::json!({"error": "Invalid agent ID"})),
5045+
);
5046+
}
5047+
};
5048+
5049+
if state.kernel.registry.get(agent_id).is_none() {
5050+
return (
5051+
StatusCode::NOT_FOUND,
5052+
Json(serde_json::json!({"error": "Agent not found"})),
5053+
);
5054+
}
5055+
5056+
// Apply partial updates using dedicated registry methods
5057+
if let Some(name) = body.get("name").and_then(|v| v.as_str()) {
5058+
if let Err(e) = state
5059+
.kernel
5060+
.registry
5061+
.update_name(agent_id, name.to_string())
5062+
{
5063+
return (
5064+
StatusCode::BAD_REQUEST,
5065+
Json(serde_json::json!({"error": format!("{e}")})),
5066+
);
5067+
}
5068+
}
5069+
if let Some(desc) = body.get("description").and_then(|v| v.as_str()) {
5070+
if let Err(e) = state
5071+
.kernel
5072+
.registry
5073+
.update_description(agent_id, desc.to_string())
5074+
{
5075+
return (
5076+
StatusCode::BAD_REQUEST,
5077+
Json(serde_json::json!({"error": format!("{e}")})),
5078+
);
5079+
}
5080+
}
5081+
if let Some(model) = body.get("model").and_then(|v| v.as_str()) {
5082+
if let Err(e) = state.kernel.set_agent_model(agent_id, model) {
5083+
return (
5084+
StatusCode::BAD_REQUEST,
5085+
Json(serde_json::json!({"error": format!("{e}")})),
5086+
);
5087+
}
5088+
}
5089+
if let Some(system_prompt) = body.get("system_prompt").and_then(|v| v.as_str()) {
5090+
if let Err(e) = state
5091+
.kernel
5092+
.registry
5093+
.update_system_prompt(agent_id, system_prompt.to_string())
5094+
{
5095+
return (
5096+
StatusCode::BAD_REQUEST,
5097+
Json(serde_json::json!({"error": format!("{e}")})),
5098+
);
5099+
}
5100+
}
5101+
5102+
// Persist updated entry to SQLite
5103+
if let Some(entry) = state.kernel.registry.get(agent_id) {
5104+
let _ = state.kernel.memory.save_agent(&entry);
5105+
(
5106+
StatusCode::OK,
5107+
Json(serde_json::json!({"status": "ok", "agent_id": entry.id.to_string(), "name": entry.name})),
5108+
)
5109+
} else {
5110+
(
5111+
StatusCode::INTERNAL_SERVER_ERROR,
5112+
Json(serde_json::json!({"error": "Agent vanished during update"})),
5113+
)
5114+
}
5115+
}
5116+
50335117
// ---------------------------------------------------------------------------
50345118
// Migration endpoint
50355119
// ---------------------------------------------------------------------------
@@ -6113,6 +6197,12 @@ pub async fn clear_agent_history(
61136197
)
61146198
}
61156199
};
6200+
if state.kernel.registry.get(agent_id).is_none() {
6201+
return (
6202+
StatusCode::NOT_FOUND,
6203+
Json(serde_json::json!({"error": "Agent not found"})),
6204+
);
6205+
}
61166206
match state.kernel.clear_agent_history(agent_id) {
61176207
Ok(()) => (
61186208
StatusCode::OK,

crates/openfang-api/src/server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ pub async fn build_router(
126126
)
127127
.route(
128128
"/api/agents/{id}",
129-
axum::routing::get(routes::get_agent).delete(routes::kill_agent),
129+
axum::routing::get(routes::get_agent).delete(routes::kill_agent).patch(routes::patch_agent),
130130
)
131131
.route(
132132
"/api/agents/{id}/mode",

crates/openfang-api/static/css/components.css

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,32 @@
6969
gap: 16px;
7070
}
7171

72+
/* Card-based flex containers for agent chips and similar inline layouts */
73+
.card-flex {
74+
display: flex;
75+
flex-wrap: wrap;
76+
gap: 10px;
77+
}
78+
79+
/* Nested list indentation inside cards, detail panels, and modals */
80+
.card ul, .card ol,
81+
.detail-grid ul, .detail-grid ol,
82+
.modal ul, .modal ol,
83+
.info-card ul, .info-card ol {
84+
padding-left: 18px;
85+
margin: 4px 0;
86+
}
87+
.card ul ul, .card ol ol,
88+
.modal ul ul, .modal ol ol {
89+
padding-left: 16px;
90+
margin: 2px 0;
91+
}
92+
.card li, .modal li, .info-card li {
93+
margin-bottom: 2px;
94+
font-size: 12px;
95+
line-height: 1.5;
96+
}
97+
7298
/* Glow effect on card hover */
7399
.card-glow {
74100
overflow: hidden;
@@ -90,13 +116,17 @@
90116
display: inline-flex;
91117
align-items: center;
92118
gap: 4px;
93-
padding: 2px 8px;
119+
padding: 3px 8px;
94120
border-radius: 20px;
95121
font-size: 10px;
96122
font-weight: 600;
97123
letter-spacing: 0.5px;
98124
text-transform: uppercase;
125+
white-space: nowrap;
126+
line-height: 1.2;
127+
vertical-align: middle;
99128
}
129+
.badge + .badge { margin-left: 4px; }
100130

101131
.badge-running { background: rgba(74,222,128,0.12); color: var(--success); }
102132
.badge-suspended { background: rgba(245,158,11,0.12); color: var(--warning); }
@@ -110,7 +140,7 @@
110140
.badge-error { background: rgba(239,68,68,0.12); color: var(--error); }
111141
.badge-muted { background: rgba(148,163,184,0.12); color: var(--text-dim); }
112142
.badge-info { background: rgba(59,130,246,0.12); color: var(--info); }
113-
.badge-dim { background: rgba(148,163,184,0.08); color: var(--text-dim); font-size: 0.65rem; }
143+
.badge-dim { background: rgba(148,163,184,0.08); color: var(--text-dim); font-size: 0.65rem; padding: 2px 6px; }
114144
.text-danger { color: var(--error); }
115145

116146
/* Tables */
@@ -949,6 +979,14 @@ mark.search-highlight {
949979
padding: 8px 12px;
950980
border-bottom: 1px solid var(--border);
951981
}
982+
.model-switcher-search select {
983+
max-width: 100px;
984+
flex-shrink: 0;
985+
}
986+
.model-switcher-search select:focus {
987+
outline: none;
988+
border-color: var(--accent);
989+
}
952990
.model-switcher-search input {
953991
flex: 1;
954992
background: none;

crates/openfang-api/static/index_body.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,12 @@ <h3 style="margin:0 0 8px;font-size:16px;font-weight:600">Select an agent to sta
769769
<div class="model-switcher-search">
770770
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0;opacity:0.5"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
771771
<input id="model-switcher-search" type="text" x-model="modelSwitcherFilter" placeholder="Search models..." @keydown.escape.stop="showModelSwitcher = false" @keydown.arrow-down.prevent="modelSwitcherIdx = Math.min(modelSwitcherIdx + 1, filteredSwitcherModels.length - 1)" @keydown.arrow-up.prevent="modelSwitcherIdx = Math.max(modelSwitcherIdx - 1, 0)" @keydown.enter.prevent="filteredSwitcherModels[modelSwitcherIdx] && switchModel(filteredSwitcherModels[modelSwitcherIdx])">
772+
<select x-model="modelSwitcherProviderFilter" style="background:var(--surface2);border:1px solid var(--border);border-radius:6px;color:var(--text-dim);font-size:11px;padding:2px 6px;cursor:pointer;font-family:var(--font-mono);flex-shrink:0">
773+
<option value="">All</option>
774+
<template x-for="pn in switcherProviders" :key="pn">
775+
<option :value="pn" x-text="pn"></option>
776+
</template>
777+
</select>
772778
</div>
773779
<div x-show="modelSwitching" style="display:flex;align-items:center;justify-content:center;padding:12px;gap:8px">
774780
<div class="tool-card-spinner"></div>
@@ -841,6 +847,9 @@ <h2>Chat</h2>
841847
<div class="text-xs text-dim font-mono" style="font-size:11px" x-text="agent.model_name"></div>
842848
</div>
843849
<span class="badge" :class="'badge-' + agent.state.toLowerCase()" x-text="agent.state" style="font-size:10px"></span>
850+
<button class="agent-chip-config-btn" @click.stop="showDetail(agent)" title="Agent settings" style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;border:1px solid var(--border);background:transparent;cursor:pointer;color:var(--text-dim);transition:all 0.15s;flex-shrink:0" @mouseenter="$el.style.borderColor='var(--accent)';$el.style.color='var(--accent)';$el.style.background='var(--surface2)'" @mouseleave="$el.style.borderColor='var(--border)';$el.style.color='var(--text-dim)';$el.style.background='transparent'">
851+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
852+
</button>
844853
</div>
845854
</template>
846855
</div>

0 commit comments

Comments
 (0)