agent ui: terminal-themed live panel; pretty tool calls; collapsed results
- tool_use renders per-tool (Read /path, Bash $ cmd, send → operator: ...) - tool_result with >120 chars collapses into <details>; short ones inline - session_init / result / rate_limit dropped from the panel - thinking content shown inline if present, fallback indicator otherwise - TurnStart carries unread count → header badge "· 3 unread" - per-tool [status] line dropped from envelope; lives in wake prompt + UI - send form moved below the live panel - live panel themed as a terminal (crust bg, inset shadow, monospace)
This commit is contained in:
parent
d8807b8e8c
commit
ace13cd785
7 changed files with 191 additions and 87 deletions
|
|
@ -113,6 +113,20 @@ pre.diff {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
max-height: 30em;
|
max-height: 30em;
|
||||||
}
|
}
|
||||||
|
/* Terminal-ish look for the live panel. Crust as bg (almost-black),
|
||||||
|
slightly inset, mauve phosphor glow. */
|
||||||
|
.live.terminal {
|
||||||
|
background: #11111b;
|
||||||
|
border: 1px solid var(--purple-dim);
|
||||||
|
box-shadow: inset 0 0 24px rgba(0, 0, 0, 0.7);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.8em 1em;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 32em;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", "Source Code Pro", monospace;
|
||||||
|
font-size: 0.92em;
|
||||||
|
color: #cdd6f4;
|
||||||
|
}
|
||||||
.live {
|
.live {
|
||||||
background: rgba(255, 255, 255, 0.02);
|
background: rgba(255, 255, 255, 0.02);
|
||||||
border: 1px solid var(--purple-dim);
|
border: 1px solid var(--purple-dim);
|
||||||
|
|
@ -121,6 +135,43 @@ pre.diff {
|
||||||
max-height: 32em;
|
max-height: 32em;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
.live .unread-badge {
|
||||||
|
color: var(--amber);
|
||||||
|
font-weight: normal;
|
||||||
|
margin-left: 0.6em;
|
||||||
|
font-size: 0.85em;
|
||||||
|
text-shadow: 0 0 6px rgba(250, 179, 135, 0.55);
|
||||||
|
}
|
||||||
|
details.row {
|
||||||
|
white-space: normal;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
}
|
||||||
|
details.row > summary {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--muted);
|
||||||
|
list-style: none;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
details.row > summary::before {
|
||||||
|
content: '▸ ';
|
||||||
|
color: var(--muted);
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
details.row[open] > summary::before { content: '▾ '; }
|
||||||
|
details.row.tool-result-block > summary { color: var(--muted); }
|
||||||
|
details.row > pre.tool-body {
|
||||||
|
margin: 0.3em 0 0.4em 1.2em;
|
||||||
|
padding: 0.4em 0.6em;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border-left: 2px solid var(--purple-dim);
|
||||||
|
color: var(--fg);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
max-height: 22em;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
.live .row {
|
.live .row {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
|
|
||||||
|
|
@ -225,39 +225,88 @@
|
||||||
log.scrollTop = log.scrollHeight;
|
log.scrollTop = log.scrollHeight;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
function details(cls, summary, body) {
|
||||||
|
clearPlaceholder();
|
||||||
|
const d = document.createElement('details');
|
||||||
|
d.className = 'row ' + (cls || '');
|
||||||
|
const s = document.createElement('summary');
|
||||||
|
s.textContent = summary;
|
||||||
|
d.appendChild(s);
|
||||||
|
const pre = document.createElement('pre');
|
||||||
|
pre.className = 'tool-body';
|
||||||
|
pre.textContent = body;
|
||||||
|
d.appendChild(pre);
|
||||||
|
log.appendChild(d);
|
||||||
|
log.scrollTop = log.scrollHeight;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
function trim(s, n) { return s.length > n ? s.slice(0, n) + '…' : s; }
|
function trim(s, n) { return s.length > n ? s.slice(0, n) + '…' : s; }
|
||||||
|
// Pretty-print a tool call: per-known-tool format, fallback to JSON
|
||||||
|
// for unknown tools.
|
||||||
|
function fmtToolUse(c) {
|
||||||
|
const name = c.name || '';
|
||||||
|
const input = c.input || {};
|
||||||
|
const short = name.startsWith('mcp__hyperhive__')
|
||||||
|
? name.slice('mcp__hyperhive__'.length) + '*' : name;
|
||||||
|
switch (name) {
|
||||||
|
case 'Read': return short + ' ' + (input.file_path || '');
|
||||||
|
case 'Write': return short + ' ' + (input.file_path || '');
|
||||||
|
case 'Edit': return short + ' ' + (input.file_path || '');
|
||||||
|
case 'Glob': return short + ' ' + (input.pattern || '');
|
||||||
|
case 'Grep': return short + ' ' + (input.pattern || '');
|
||||||
|
case 'Bash': return short + ' $ ' + (input.command || '');
|
||||||
|
case 'TodoWrite': return short + ' (' + ((input.todos || []).length) + ' items)';
|
||||||
|
case 'mcp__hyperhive__send': return short + ' → ' + (input.to || '?') + ': '
|
||||||
|
+ JSON.stringify(input.body || '').slice(0, 80);
|
||||||
|
case 'mcp__hyperhive__recv': return short + '()';
|
||||||
|
case 'mcp__hyperhive__request_spawn': return short + ' ' + (input.name || '');
|
||||||
|
case 'mcp__hyperhive__kill': return short + ' ' + (input.name || '');
|
||||||
|
case 'mcp__hyperhive__request_apply_commit':
|
||||||
|
return short + ' ' + (input.agent || '') + ' @ ' + (input.commit_ref || '').slice(0, 12);
|
||||||
|
default: return name + ' ' + trim(JSON.stringify(input), 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function renderToolResult(c) {
|
||||||
|
const txt = Array.isArray(c.content)
|
||||||
|
? c.content.map(p => p.text || '').join('')
|
||||||
|
: (c.content || '');
|
||||||
|
const summary = '← ' + (() => {
|
||||||
|
const trimmed = txt.replace(/\s+/g, ' ').trim();
|
||||||
|
if (!trimmed) return '(empty)';
|
||||||
|
if (trimmed.length <= 120) return trimmed;
|
||||||
|
const lines = txt.split('\n').filter(l => l.length).length;
|
||||||
|
const headline = trimmed.slice(0, 90) + '…';
|
||||||
|
return `${lines}L · ${headline}`;
|
||||||
|
})();
|
||||||
|
// For empty / short results, render as a flat row (no expand).
|
||||||
|
if (!txt.trim() || txt.length <= 120) {
|
||||||
|
row('tool-result', summary);
|
||||||
|
} else {
|
||||||
|
details('tool-result-block', summary, txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
function renderStream(v) {
|
function renderStream(v) {
|
||||||
if (v.type === 'system' && v.subtype === 'init') {
|
// Drop session init, claude's result line, rate-limit — they're
|
||||||
row('sys', '· session init · tools=' + (v.tools||[]).length + ' model=' + (v.model || '?'));
|
// noise. TurnEnd communicates pass/fail; session init data isn't
|
||||||
return;
|
// actionable.
|
||||||
}
|
if (v.type === 'system' && v.subtype === 'init') return;
|
||||||
if (v.type === 'rate_limit_event') {
|
if (v.type === 'rate_limit_event') return;
|
||||||
const u = Math.round((v.rate_limit_info?.utilization || 0) * 100);
|
if (v.type === 'result') return;
|
||||||
const s = v.rate_limit_info?.status || '';
|
|
||||||
row('sys', '· rate-limit util=' + u + '% (' + s + ')');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (v.type === 'assistant' && v.message && v.message.content) {
|
if (v.type === 'assistant' && v.message && v.message.content) {
|
||||||
for (const c of v.message.content) {
|
for (const c of v.message.content) {
|
||||||
if (c.type === 'text' && c.text && c.text.trim()) row('text', c.text);
|
if (c.type === 'text' && c.text && c.text.trim()) row('text', c.text);
|
||||||
else if (c.type === 'thinking') row('thinking', '· thinking …');
|
else if (c.type === 'thinking') {
|
||||||
else if (c.type === 'tool_use') row('tool-use', '→ ' + c.name + ' ' + trim(JSON.stringify(c.input || {}), 240));
|
const txt = (c.thinking || c.text || '').trim();
|
||||||
|
row('thinking', txt ? '· ' + txt : '· thinking …');
|
||||||
|
}
|
||||||
|
else if (c.type === 'tool_use') row('tool-use', '→ ' + fmtToolUse(c));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (v.type === 'user' && v.message && v.message.content) {
|
if (v.type === 'user' && v.message && v.message.content) {
|
||||||
for (const c of v.message.content) {
|
for (const c of v.message.content) {
|
||||||
if (c.type === 'tool_result') {
|
if (c.type === 'tool_result') renderToolResult(c);
|
||||||
const txt = Array.isArray(c.content)
|
|
||||||
? c.content.map(p => p.text || '').join(' ')
|
|
||||||
: (c.content || '');
|
|
||||||
row('tool-result', '← ' + trim(txt, 300));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (v.type === 'result') {
|
|
||||||
row('result', '✓ done · ' + (v.subtype || '') + (v.is_error ? ' [error]' : ''));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
row('sys', '· ' + trim(JSON.stringify(v), 200));
|
row('sys', '· ' + trim(JSON.stringify(v), 200));
|
||||||
|
|
@ -265,6 +314,12 @@
|
||||||
function handle(ev) {
|
function handle(ev) {
|
||||||
if (ev.kind === 'turn_start') {
|
if (ev.kind === 'turn_start') {
|
||||||
const block = row('turn-start', '◆ TURN ← ' + ev.from);
|
const block = row('turn-start', '◆ TURN ← ' + ev.from);
|
||||||
|
if (ev.unread > 0) {
|
||||||
|
const badge = document.createElement('span');
|
||||||
|
badge.className = 'unread-badge';
|
||||||
|
badge.textContent = '· ' + ev.unread + ' unread';
|
||||||
|
block.appendChild(badge);
|
||||||
|
}
|
||||||
const body = document.createElement('div');
|
const body = document.createElement('div');
|
||||||
body.className = 'turn-body';
|
body.className = 'turn-body';
|
||||||
body.textContent = ev.body;
|
body.textContent = ev.body;
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@
|
||||||
<h2 id="title">◆ … ◆</h2>
|
<h2 id="title">◆ … ◆</h2>
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
|
||||||
|
<h3>live</h3>
|
||||||
|
<div id="live" class="live terminal"><div class="meta">connecting…</div></div>
|
||||||
|
|
||||||
<div id="status">
|
<div id="status">
|
||||||
<p class="meta">loading…</p>
|
<p class="meta">loading…</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>live</h3>
|
|
||||||
<div id="live" class="live"><div class="meta">connecting…</div></div>
|
|
||||||
|
|
||||||
<script src="/static/app.js" defer></script>
|
<script src="/static/app.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -135,11 +135,13 @@ async fn serve(
|
||||||
match recv {
|
match recv {
|
||||||
Ok(AgentResponse::Message { from, body }) => {
|
Ok(AgentResponse::Message { from, body }) => {
|
||||||
tracing::info!(%from, %body, "inbox");
|
tracing::info!(%from, %body, "inbox");
|
||||||
|
let unread = inbox_unread(socket).await;
|
||||||
bus.emit(LiveEvent::TurnStart {
|
bus.emit(LiveEvent::TurnStart {
|
||||||
from: from.clone(),
|
from: from.clone(),
|
||||||
body: body.clone(),
|
body: body.clone(),
|
||||||
|
unread,
|
||||||
});
|
});
|
||||||
let prompt = format_wake_prompt(&from, &body);
|
let prompt = format_wake_prompt(&from, &body, unread);
|
||||||
let outcome = turn::drive_turn(
|
let outcome = turn::drive_turn(
|
||||||
&prompt,
|
&prompt,
|
||||||
&mcp_config,
|
&mcp_config,
|
||||||
|
|
@ -168,9 +170,24 @@ async fn serve(
|
||||||
|
|
||||||
/// Per-turn user prompt. The role/tools/etc. is in the system prompt
|
/// Per-turn user prompt. The role/tools/etc. is in the system prompt
|
||||||
/// (`prompts/agent.md` → `claude --system-prompt-file`); this is just the
|
/// (`prompts/agent.md` → `claude --system-prompt-file`); this is just the
|
||||||
/// wake signal claude reacts to.
|
/// wake signal claude reacts to. `unread` is the count of *other*
|
||||||
fn format_wake_prompt(from: &str, body: &str) -> String {
|
/// messages in the inbox right after this one was popped.
|
||||||
format!("Incoming message from `{from}`:\n---\n{body}\n---")
|
fn format_wake_prompt(from: &str, body: &str, unread: u64) -> String {
|
||||||
|
let pending = if unread == 0 {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!("\n\n({unread} more message(s) pending in your inbox — drain via `mcp__hyperhive__recv` if relevant.)")
|
||||||
|
};
|
||||||
|
format!("Incoming message from `{from}`:\n---\n{body}\n---{pending}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Best-effort: ask our own per-agent socket how many messages are still
|
||||||
|
/// pending after the wake-up Recv. Returns 0 if anything goes wrong.
|
||||||
|
async fn inbox_unread(socket: &Path) -> u64 {
|
||||||
|
match client::request::<_, AgentResponse>(socket, &AgentRequest::Status).await {
|
||||||
|
Ok(AgentResponse::Status { unread }) => unread,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(resp: &AgentResponse) -> Result<()> {
|
fn render(resp: &AgentResponse) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -151,11 +151,13 @@ async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> {
|
||||||
// so the wake prompt can label it as such.
|
// so the wake prompt can label it as such.
|
||||||
}
|
}
|
||||||
tracing::info!(%from, %body, "manager inbox");
|
tracing::info!(%from, %body, "manager inbox");
|
||||||
|
let unread = inbox_unread(socket).await;
|
||||||
bus.emit(LiveEvent::TurnStart {
|
bus.emit(LiveEvent::TurnStart {
|
||||||
from: from.clone(),
|
from: from.clone(),
|
||||||
body: body.clone(),
|
body: body.clone(),
|
||||||
|
unread,
|
||||||
});
|
});
|
||||||
let prompt = format_wake_prompt(&from, &body);
|
let prompt = format_wake_prompt(&from, &body, unread);
|
||||||
let outcome = turn::drive_turn(
|
let outcome = turn::drive_turn(
|
||||||
&prompt,
|
&prompt,
|
||||||
&mcp_config,
|
&mcp_config,
|
||||||
|
|
@ -185,7 +187,20 @@ async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> {
|
||||||
|
|
||||||
/// Per-turn user prompt. The role/tools/etc. is in the system prompt
|
/// Per-turn user prompt. The role/tools/etc. is in the system prompt
|
||||||
/// (`prompts/manager.md` → `claude --system-prompt-file`); this is just
|
/// (`prompts/manager.md` → `claude --system-prompt-file`); this is just
|
||||||
/// the wake signal.
|
/// the wake signal. `unread` is the inbox depth after this message was
|
||||||
fn format_wake_prompt(from: &str, body: &str) -> String {
|
/// popped.
|
||||||
format!("Incoming message from `{from}`:\n---\n{body}\n---")
|
fn format_wake_prompt(from: &str, body: &str, unread: u64) -> String {
|
||||||
|
let pending = if unread == 0 {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!("\n\n({unread} more message(s) pending in your inbox — drain via `mcp__hyperhive__recv` if relevant.)")
|
||||||
|
};
|
||||||
|
format!("Incoming message from `{from}`:\n---\n{body}\n---{pending}")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inbox_unread(socket: &Path) -> u64 {
|
||||||
|
match client::request::<_, ManagerResponse>(socket, &ManagerRequest::Status).await {
|
||||||
|
Ok(ManagerResponse::Status { unread }) => unread,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,13 @@ const CHANNEL_CAPACITY: usize = 256;
|
||||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||||
pub enum LiveEvent {
|
pub enum LiveEvent {
|
||||||
/// Harness popped a wake-up message and is about to invoke claude.
|
/// Harness popped a wake-up message and is about to invoke claude.
|
||||||
TurnStart { from: String, body: String },
|
/// `unread` is the count of *other* messages still in the inbox at
|
||||||
|
/// that moment — surfaced as a badge in the live panel header.
|
||||||
|
TurnStart {
|
||||||
|
from: String,
|
||||||
|
body: String,
|
||||||
|
unread: u64,
|
||||||
|
},
|
||||||
/// One line of claude's `--output-format stream-json` stdout, parsed as
|
/// One line of claude's `--output-format stream-json` stdout, parsed as
|
||||||
/// a generic JSON value (so we don't have to track every claude-code
|
/// a generic JSON value (so we don't have to track every claude-code
|
||||||
/// event variant). The frontend pretty-prints by `type` field.
|
/// event variant). The frontend pretty-prints by `type` field.
|
||||||
|
|
|
||||||
|
|
@ -87,34 +87,18 @@ pub fn format_recv(resp: Result<SocketReply, anyhow::Error>) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format helper for the status peek used in the status line.
|
/// Common envelope around every MCP tool handler: pre-log → run →
|
||||||
pub fn format_status(resp: Result<SocketReply, anyhow::Error>) -> String {
|
/// post-log. The inbox-status hint used to be appended to every tool
|
||||||
match resp {
|
/// result; that lives in the wake prompt + UI header now, so tool
|
||||||
Ok(SocketReply::Status(unread)) => format!("{unread} unread message(s) in inbox"),
|
/// results stay clean.
|
||||||
Ok(other) => format!("status: unexpected response {other:?}"),
|
pub async fn run_tool_envelope<F>(tool: &'static str, args: String, body: F) -> String
|
||||||
Err(e) => format!("status: transport error: {e:#}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Common envelope around every MCP tool handler: pre-log → run → append
|
|
||||||
/// a status line → post-log. Free function so both `AgentServer` and
|
|
||||||
/// `ManagerServer` use the same shape; the per-server `status_line`
|
|
||||||
/// closure is what differs (different `Status` wire types).
|
|
||||||
pub async fn run_tool_envelope<F, S>(tool: &'static str, args: String, status: S, body: F) -> String
|
|
||||||
where
|
where
|
||||||
F: Future<Output = String>,
|
F: Future<Output = String>,
|
||||||
S: Future<Output = String>,
|
|
||||||
{
|
{
|
||||||
tracing::info!(tool, %args, "tool: request");
|
tracing::info!(tool, %args, "tool: request");
|
||||||
let result = body.await;
|
let result = body.await;
|
||||||
let status_text = status.await;
|
tracing::info!(tool, result = %result, "tool: result");
|
||||||
let full = if status_text.is_empty() {
|
|
||||||
result
|
result
|
||||||
} else {
|
|
||||||
format!("{result}\n\n[status] {status_text}")
|
|
||||||
};
|
|
||||||
tracing::info!(tool, result = %full, "tool: result");
|
|
||||||
full
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||||
|
|
@ -141,19 +125,6 @@ impl AgentServer {
|
||||||
pub fn new(socket: PathBuf) -> Self {
|
pub fn new(socket: PathBuf) -> Self {
|
||||||
Self { socket }
|
Self { socket }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Non-mutating peek used in the status line. Falls back to a vague
|
|
||||||
/// note rather than failing the whole tool call when the socket
|
|
||||||
/// hiccups.
|
|
||||||
async fn status_line(&self) -> String {
|
|
||||||
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
|
||||||
&self.socket,
|
|
||||||
&hive_sh4re::AgentRequest::Status,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map(SocketReply::from);
|
|
||||||
format_status(resp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool_router]
|
#[tool_router]
|
||||||
|
|
@ -165,7 +136,7 @@ impl AgentServer {
|
||||||
async fn send(&self, Parameters(args): Parameters<SendArgs>) -> String {
|
async fn send(&self, Parameters(args): Parameters<SendArgs>) -> String {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let to = args.to.clone();
|
let to = args.to.clone();
|
||||||
run_tool_envelope("send", log, self.status_line(), async move {
|
run_tool_envelope("send", log, async move {
|
||||||
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
||||||
&self.socket,
|
&self.socket,
|
||||||
&hive_sh4re::AgentRequest::Send {
|
&hive_sh4re::AgentRequest::Send {
|
||||||
|
|
@ -185,7 +156,7 @@ impl AgentServer {
|
||||||
or an empty marker if nothing is waiting."
|
or an empty marker if nothing is waiting."
|
||||||
)]
|
)]
|
||||||
async fn recv(&self, Parameters(_args): Parameters<RecvArgs>) -> String {
|
async fn recv(&self, Parameters(_args): Parameters<RecvArgs>) -> String {
|
||||||
run_tool_envelope("recv", String::new(), self.status_line(), async move {
|
run_tool_envelope("recv", String::new(), async move {
|
||||||
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
||||||
&self.socket,
|
&self.socket,
|
||||||
&hive_sh4re::AgentRequest::Recv,
|
&hive_sh4re::AgentRequest::Recv,
|
||||||
|
|
@ -258,16 +229,6 @@ impl ManagerServer {
|
||||||
Self { socket }
|
Self { socket }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn status_line(&self) -> String {
|
|
||||||
let resp = client::request::<_, hive_sh4re::ManagerResponse>(
|
|
||||||
&self.socket,
|
|
||||||
&hive_sh4re::ManagerRequest::Status,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map(SocketReply::from);
|
|
||||||
format_status(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper: issue any `ManagerRequest`, convert the reply through
|
/// Helper: issue any `ManagerRequest`, convert the reply through
|
||||||
/// `SocketReply`. Manager tools that just need an `Ok` ack share this.
|
/// `SocketReply`. Manager tools that just need an `Ok` ack share this.
|
||||||
async fn dispatch(
|
async fn dispatch(
|
||||||
|
|
@ -289,7 +250,7 @@ impl ManagerServer {
|
||||||
async fn send(&self, Parameters(args): Parameters<SendArgs>) -> String {
|
async fn send(&self, Parameters(args): Parameters<SendArgs>) -> String {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let to = args.to.clone();
|
let to = args.to.clone();
|
||||||
run_tool_envelope("send", log, self.status_line(), async move {
|
run_tool_envelope("send", log, async move {
|
||||||
let resp = self
|
let resp = self
|
||||||
.dispatch(hive_sh4re::ManagerRequest::Send {
|
.dispatch(hive_sh4re::ManagerRequest::Send {
|
||||||
to: args.to,
|
to: args.to,
|
||||||
|
|
@ -306,7 +267,7 @@ impl ManagerServer {
|
||||||
empty."
|
empty."
|
||||||
)]
|
)]
|
||||||
async fn recv(&self, Parameters(_args): Parameters<RecvArgs>) -> String {
|
async fn recv(&self, Parameters(_args): Parameters<RecvArgs>) -> String {
|
||||||
run_tool_envelope("recv", String::new(), self.status_line(), async move {
|
run_tool_envelope("recv", String::new(), async move {
|
||||||
let resp = self.dispatch(hive_sh4re::ManagerRequest::Recv).await;
|
let resp = self.dispatch(hive_sh4re::ManagerRequest::Recv).await;
|
||||||
format_recv(resp)
|
format_recv(resp)
|
||||||
})
|
})
|
||||||
|
|
@ -320,7 +281,7 @@ impl ManagerServer {
|
||||||
async fn request_spawn(&self, Parameters(args): Parameters<RequestSpawnArgs>) -> String {
|
async fn request_spawn(&self, Parameters(args): Parameters<RequestSpawnArgs>) -> String {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let name = args.name.clone();
|
let name = args.name.clone();
|
||||||
run_tool_envelope("request_spawn", log, self.status_line(), async move {
|
run_tool_envelope("request_spawn", log, async move {
|
||||||
let resp = self
|
let resp = self
|
||||||
.dispatch(hive_sh4re::ManagerRequest::RequestSpawn { name: args.name })
|
.dispatch(hive_sh4re::ManagerRequest::RequestSpawn { name: args.name })
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -340,7 +301,7 @@ impl ManagerServer {
|
||||||
async fn kill(&self, Parameters(args): Parameters<KillArgs>) -> String {
|
async fn kill(&self, Parameters(args): Parameters<KillArgs>) -> String {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let name = args.name.clone();
|
let name = args.name.clone();
|
||||||
run_tool_envelope("kill", log, self.status_line(), async move {
|
run_tool_envelope("kill", log, async move {
|
||||||
let resp = self
|
let resp = self
|
||||||
.dispatch(hive_sh4re::ManagerRequest::Kill { name: args.name })
|
.dispatch(hive_sh4re::ManagerRequest::Kill { name: args.name })
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -364,7 +325,6 @@ impl ManagerServer {
|
||||||
run_tool_envelope(
|
run_tool_envelope(
|
||||||
"request_apply_commit",
|
"request_apply_commit",
|
||||||
log,
|
log,
|
||||||
self.status_line(),
|
|
||||||
async move {
|
async move {
|
||||||
let resp = self
|
let resp = self
|
||||||
.dispatch(hive_sh4re::ManagerRequest::RequestApplyCommit {
|
.dispatch(hive_sh4re::ManagerRequest::RequestApplyCommit {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue