@@ -34,18 +34,29 @@ def _now_utc() -> str:
3434
3535
3636def to_status_html (agent : AgentCard , start_time : float ) -> str :
37- """Render a stylish agent status page with logo, ping, favicon, and uptime."""
37+ def _fmt (value : str | None , default : str = "—" ) -> str :
38+ from html import escape
39+
40+ return escape (value ) if value else default
41+
42+ def _list (items : list [str ], empty : str = "None" ) -> str :
43+ if not items :
44+ return f"<li><em>{ empty } </em></li>"
45+ return "" .join (f"<li>{ escape (item )} </li>" for item in items )
46+
47+ import json
48+
3849 return f"""<!DOCTYPE html>
3950<html lang="en">
4051<head>
4152<meta charset="utf-8" />
4253<meta name="viewport" content="width=device-width, initial-scale=1" />
43- <title>{ escape (agent .name )} · Agent Status</title>
54+ <title>{ _fmt (agent .name )} · Agent Status</title>
4455<link rel="icon" href="https://raw.githubusercontent.com/nMaroulis/protolink/main/docs/assets/logo_sm.png" />
4556<style>
4657:root {{
4758 --bg: #070b1a;
48- --card: rgba(17, 22, 42, 0.85);
59+ --card-base : rgba(17, 22, 42, 0.85);
4960 --border: rgba(56, 189, 248, 0.25);
5061 --text: #e5e7eb;
5162 --muted: #9ca3af;
@@ -72,13 +83,21 @@ def to_status_html(agent: AgentCard, start_time: float) -> str:
7283.logo img:hover {{ opacity: 1; }}
7384.card {{
7485 width: min(520px, 92vw);
75- background: var(--card);
76- backdrop-filter: blur(10px );
86+ background: var(--card-base );
87+ backdrop-filter: blur(12px );
7788 border: 1px solid var(--border);
7889 border-radius: 18px;
7990 padding: 26px 28px;
8091 box-shadow: 0 20px 60px rgba(0,0,0,.6), inset 0 0 0 1px rgba(255,255,255,.02);
81- transition: transform .2s ease, box-shadow .2s ease;
92+ transition: transform .2s ease, box-shadow .2s ease, background-position 5s linear;
93+ background: linear-gradient(135deg, #11162a, #0b3a55, #11162a);
94+ background-size: 400% 400%;
95+ animation: gradientShift 30s ease infinite;
96+ }}
97+ @keyframes gradientShift {{
98+ 0% {{ background-position: 0% 50%; }}
99+ 50% {{ background-position: 100% 50%; }}
100+ 100% {{ background-position: 0% 50%; }}
82101}}
83102.card:hover {{ transform: translateY(-2px); box-shadow: 0 30px 80px rgba(0,0,0,.7); }}
84103header {{
@@ -100,16 +119,16 @@ def to_status_html(agent: AgentCard, start_time: float) -> str:
100119}}
101120.status::before {{
102121 content: "";
103- width: 8px ;
104- height: 8px ;
122+ width: 10px ;
123+ height: 10px ;
105124 border-radius: 50%;
106125 background: currentColor;
107- box-shadow: 0 0 0 0 rgba(34,197,94,.7) ;
108- animation: pulse 2s infinite;
126+ box-shadow: 0 0 6px currentColor ;
127+ animation: pulse 1.8s infinite;
109128}}
110129@keyframes pulse {{
111130 0% {{ box-shadow: 0 0 0 0 rgba(34,197,94,.7); }}
112- 70 % {{ box-shadow: 0 0 0 8px rgba(34,197,94,0); }}
131+ 50 % {{ box-shadow: 0 0 12px 6px rgba(34,197,94,0.2 ); }}
113132 100% {{ box-shadow: 0 0 0 0 rgba(34,197,94,0); }}
114133}}
115134.grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 14px 18px; margin-bottom: 20px; }}
@@ -119,7 +138,7 @@ def to_status_html(agent: AgentCard, start_time: float) -> str:
119138section h2 {{ font-size: .75rem; margin: 0 0 8px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }}
120139ul {{ margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 8px; }}
121140ul li {{ background: var(--accent-soft); border: 1px solid var(--border); padding: 4px 10px; border-radius: 999px; font-size: .75rem; }}
122- .actions {{ margin-top: 22px; display: flex; align-items: center; gap: 12px; }}
141+ .actions {{ margin-top: 22px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }}
123142button {{
124143 background: linear-gradient(135deg, transparent, rgba(56,189,248,.08));
125144 border: 1px solid var(--border);
@@ -134,13 +153,17 @@ def to_status_html(agent: AgentCard, start_time: float) -> str:
134153button:disabled {{ opacity: .6; cursor: not-allowed; }}
135154.ping-result {{ font-size: .75rem; color: var(--muted); }}
136155footer {{ margin-top: 22px; display: flex; justify-content: space-between; font-size: .7rem; color: var(--muted); border-top: 1px solid var(--border); padding-top: 10px; }}
137- .uptime {{ font-size: .75rem; color: var(--accent); }}
138-
156+ .uptime {{
157+ font-size: .75rem;
158+ color: var(--accent);
159+ text-shadow: 0 0 6px var(--accent);
160+ transition: text-shadow .3s ease;
161+ }}
162+ .uptime:hover {{ text-shadow: 0 0 12px var(--accent), 0 0 24px var(--accent-soft); }}
139163</style>
140164</head>
141165<body>
142166 <div class="card">
143-
144167 <header>
145168 <div class="logo">
146169 <img src="https://raw.githubusercontent.com/nMaroulis/protolink/main/docs/assets/logo_sm.png" alt="Protolink logo" />
@@ -149,33 +172,39 @@ def to_status_html(agent: AgentCard, start_time: float) -> str:
149172 <div id="status" class="status">RUNNING</div>
150173 </header>
151174
175+ <p style="color: var(--muted); font-size:.85rem; margin-bottom:16px;">{ _fmt (agent .description )} </p>
176+
152177 <div class="grid">
178+ <div><div class="label">Version</div><div class="value">{ _fmt (agent .version )} </div></div>
179+ <div><div class="label">Protocol</div><div class="value">{ _fmt (agent .protocol_version )} </div></div>
180+ <div><div class="label">Transport</div><div class="value">{ _fmt (agent .transport .upper ())} </div></div>
181+ <div><div class="label">Endpoint</div><div class="value">{ _fmt (agent .url )} </div></div>
182+ </div>
183+
184+ <section>
185+ <h2>Capabilities</h2>
186+ <ul>{ _list ([str (c ) for c in agent .capabilities .enabled () or []], empty = "None" )} </ul>
187+ </section>
188+
189+ <section style="display:grid; grid-template-columns: 1fr 1fr; gap:12px;">
153190 <div>
154- <div class="label">Version</div>
155- <div class="value">{ _fmt (agent .version )} </div>
156- </div>
157- <div>
158- <div class="label">Protocol</div>
159- <div class="value">{ _fmt (agent .protocol_version )} </div>
160- </div>
161- <div>
162- <div class="label">Transport</div>
163- <div class="value">{ _fmt (agent .transport .upper ())} </div>
191+ <h2>Input Formats</h2>
192+ <ul>{ _list (agent .input_formats , empty = "text/plain" )} </ul>
164193 </div>
165194 <div>
166- <div class="label">Endpoint</div >
167- <div class="value"> { _fmt (agent .url )} </div >
195+ <h2>Output Formats</h2 >
196+ <ul> { _list (agent .output_formats , empty = "text/plain" )} </ul >
168197 </div>
169- </div >
198+ </section >
170199
171200 <section>
172- <h2>Skills </h2>
173- <ul>{ _list ([s . name for s in agent .skills ], empty = "No skills declared " )} </ul>
201+ <h2>Security Schemes </h2>
202+ <ul>{ _list ([f" { k } : { json . dumps ( v ) } " for k , v in ( agent .security_schemes or {}). items () ], empty = "None " )} </ul>
174203 </section>
175204
176205 <section>
177- <h2>Formats </h2>
178- <ul>{ _list (agent . input_formats + agent .output_formats , empty = "text/plain " )} </ul>
206+ <h2>Skills </h2>
207+ <ul>{ _list ([ s . id for s in agent .skills ] , empty = "No skills declared " )} </ul>
179208 </section>
180209
181210 <div class="actions">
0 commit comments