argus-code 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- argus/__init__.py +3 -0
- argus/adapters/__init__.py +7 -0
- argus/adapters/base.py +108 -0
- argus/adapters/claude_code/__init__.py +5 -0
- argus/adapters/claude_code/adapter.py +63 -0
- argus/adapters/claude_code/discover.py +72 -0
- argus/adapters/claude_code/extract_tool_calls.py +86 -0
- argus/adapters/claude_code/extract_transcript.py +111 -0
- argus/adapters/claude_code/extract_turns.py +69 -0
- argus/adapters/claude_code/history_jsonl.py +138 -0
- argus/adapters/claude_code/ingest_file.py +137 -0
- argus/adapters/claude_code/model.py +11 -0
- argus/adapters/claude_code/schemas.py +77 -0
- argus/adapters/registry.py +30 -0
- argus/cli.py +384 -0
- argus/collector/__init__.py +0 -0
- argus/collector/aggregate.py +102 -0
- argus/collector/first_run.py +189 -0
- argus/collector/pipeline.py +140 -0
- argus/collector/rollup_subagents.py +27 -0
- argus/collector/scheduler.py +89 -0
- argus/collector/search_backfill.py +109 -0
- argus/collector/watcher.py +178 -0
- argus/dashboard-dist/_astro/charts.BIevw6Es.js +1 -0
- argus/dashboard-dist/_astro/format.DxC1NGYT.js +1 -0
- argus/dashboard-dist/_astro/index.astro_astro_type_script_index_0_lang.CgwSARdD.js +24 -0
- argus/dashboard-dist/_astro/index.astro_astro_type_script_index_0_lang.W18SJsr7.js +11 -0
- argus/dashboard-dist/_astro/installCanvasRenderer.D_tC6TXz.js +18 -0
- argus/dashboard-dist/_astro/models.astro_astro_type_script_index_0_lang.BHTHXYHC.js +13 -0
- argus/dashboard-dist/_astro/prompts.astro_astro_type_script_index_0_lang.DfNgiDv9.js +17 -0
- argus/dashboard-dist/_astro/session.astro_astro_type_script_index_0_lang.Dj_bfrIa.js +86 -0
- argus/dashboard-dist/_astro/settings.astro_astro_type_script_index_0_lang.d_a-uvdi.js +24 -0
- argus/dashboard-dist/_astro/tools.astro_astro_type_script_index_0_lang.Dzzau3Yt.js +12 -0
- argus/dashboard-dist/_astro/trends.astro_astro_type_script_index_0_lang.BLLeGRNa.js +5 -0
- argus/dashboard-dist/index.html +2 -0
- argus/dashboard-dist/models/index.html +1 -0
- argus/dashboard-dist/prompts/index.html +18 -0
- argus/dashboard-dist/session/index.html +2 -0
- argus/dashboard-dist/sessions/index.html +1 -0
- argus/dashboard-dist/settings/index.html +8 -0
- argus/dashboard-dist/styles/global.css +307 -0
- argus/dashboard-dist/tools/index.html +1 -0
- argus/dashboard-dist/trends/index.html +1 -0
- argus/detectors/__init__.py +6 -0
- argus/detectors/base.py +34 -0
- argus/detectors/registry.py +20 -0
- argus/detectors/tool_error_rate_spike.py +138 -0
- argus/pricing/2026-05-02.json +24 -0
- argus/pricing/__init__.py +0 -0
- argus/pricing/compute.py +46 -0
- argus/pricing/load.py +45 -0
- argus/pricing/refresh.py +91 -0
- argus/pricing/types.py +21 -0
- argus/scaffold/__init__.py +0 -0
- argus/scaffold/scaffolder.py +45 -0
- argus/scaffold/snapshot.py +73 -0
- argus/scaffold/storage.py +60 -0
- argus/schema/__init__.py +0 -0
- argus/schema/types.py +157 -0
- argus/server/__init__.py +0 -0
- argus/server/api.py +661 -0
- argus/server/app.py +97 -0
- argus/store/__init__.py +0 -0
- argus/store/db.py +103 -0
- argus/store/migrations/__init__.py +0 -0
- argus/store/migrations/inline.py +180 -0
- argus/store/repository.py +778 -0
- argus/templates/default/.claude/agents/code-reviewer.md +27 -0
- argus/templates/default/.claude/agents/security-auditor.md +28 -0
- argus/templates/default/.claude/commands/commit.md +38 -0
- argus/templates/default/.claude/commands/deploy.md +13 -0
- argus/templates/default/.claude/commands/fix-issue.md +15 -0
- argus/templates/default/.claude/commands/pr.md +38 -0
- argus/templates/default/.claude/commands/review.md +14 -0
- argus/templates/default/.claude/rules/api-conventions.md +27 -0
- argus/templates/default/.claude/rules/code-style.md +25 -0
- argus/templates/default/.claude/rules/testing.md +19 -0
- argus/templates/default/.claude/settings.json +28 -0
- argus/templates/default/.claude/skills/example/SKILL.md +11 -0
- argus/templates/default/CLAUDE.md +57 -0
- argus_code-0.2.0.dist-info/METADATA +247 -0
- argus_code-0.2.0.dist-info/RECORD +86 -0
- argus_code-0.2.0.dist-info/WHEEL +4 -0
- argus_code-0.2.0.dist-info/entry_points.txt +2 -0
- argus_code-0.2.0.dist-info/licenses/LICENSE +21 -0
- argus_code-0.2.0.dist-info/licenses/NOTICE +22 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{a as f,e as y,n as w,t as a,u as d}from"./format.DxC1NGYT.js";import{u as b,i as h,a as _,b as k,c as S,d as x,e as $}from"./installCanvasRenderer.D_tC6TXz.js";b([h,_,k,S,x]);const r=c=>document.querySelector(c),l=r("#w");let n;async function u(){const c=(await f.sessions("?limit=100000")).sessions,m=l.value==="7d"?Date.now()-7*864e5:l.value==="30d"?Date.now()-30*864e5:0,p=c.filter(t=>new Date(t.started_at).getTime()>=m),i=new Map;for(const t of p){const e=t.primary_model,o=i.get(e)??{sessions:0,fresh:0,output:0,cr:0,cw:0,cost:0};o.sessions+=1,o.fresh+=t.total_fresh_input_tokens,o.output+=t.total_output_tokens,o.cr+=t.total_cache_read_tokens,o.cw+=t.total_cache_write_tokens,o.cost+=t.total_cost_usd,i.set(e,o)}const s=[...i.entries()].sort((t,e)=>e[1].cost-t[1].cost);if(s.length===0){r("#bar").innerHTML='<p class="empty">No sessions in this window.</p>',r("#t tbody").innerHTML='<tr><td colspan="9" class="empty">—</td></tr>';return}n?.dispose(),n=$(r("#bar")),n.setOption({textStyle:{color:"#9ba6b3"},tooltip:{trigger:"axis",backgroundColor:"#1c222d",borderColor:"#262d3a",textStyle:{color:"#e6edf3",fontSize:12},valueFormatter:t=>t.toLocaleString()+" tokens"},legend:{top:0,textStyle:{color:"#9ba6b3",fontSize:11}},grid:{left:140,right:30,top:36,bottom:28},xAxis:{type:"value",axisLabel:{color:"#6b7585",fontSize:11,formatter:t=>t>=1e6?(t/1e6).toFixed(1)+"M":t>=1e3?(t/1e3).toFixed(0)+"k":String(t)},splitLine:{lineStyle:{color:"#1c222d"}},axisLine:{show:!1}},yAxis:{type:"category",data:s.map(([t])=>t).reverse(),axisLabel:{color:"#6b7585",fontSize:11},axisLine:{show:!1},axisTick:{show:!1}},series:[{name:"Fresh input",type:"bar",stack:"t",data:s.map(([t,e])=>e.fresh).reverse(),itemStyle:{color:"#f0883e"}},{name:"Output",type:"bar",stack:"t",data:s.map(([t,e])=>e.output).reverse(),itemStyle:{color:"#bc8cff"}},{name:"Cache read",type:"bar",stack:"t",data:s.map(([t,e])=>e.cr).reverse(),itemStyle:{color:"#58a6ff"}},{name:"Cache write",type:"bar",stack:"t",data:s.map(([t,e])=>e.cw).reverse(),itemStyle:{color:"#7ee787"}}]}),r("#t tbody").innerHTML=s.map(([t,e])=>`
|
|
2
|
+
<tr>
|
|
3
|
+
<td><code>${y(t)}</code></td>
|
|
4
|
+
<td class="num">${w(e.sessions)}</td>
|
|
5
|
+
<td class="num">${a(e.fresh)}</td>
|
|
6
|
+
<td class="num">${a(e.output)}</td>
|
|
7
|
+
<td class="num">${a(e.cr)}</td>
|
|
8
|
+
<td class="num">${a(e.cw)}</td>
|
|
9
|
+
<td class="num tok">${a(e.fresh+e.output+e.cr+e.cw)}</td>
|
|
10
|
+
<td class="num cost">~${d(e.cost)}</td>
|
|
11
|
+
<td class="num">~${d(e.sessions?e.cost/e.sessions:0)}</td>
|
|
12
|
+
</tr>
|
|
13
|
+
`).join("")}l.addEventListener("change",u);u();window.addEventListener("resize",()=>n?.resize());
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import{a as l,e as _,c as k}from"./format.DxC1NGYT.js";const r=e=>document.querySelector(e),f=e=>Array.from(document.querySelectorAll(e)),m=r("#q"),p=r("#proj"),g=r("#slash");function y(e){if(!e)return"—";const t=e.split("/");return t[t.length-1]||e}function S(e){return e<1024?e+" B":e<1024*1024?(e/1024).toFixed(1)+" KB":(e/1024/1024).toFixed(1)+" MB"}function j(e){const t="\0MK_OPEN\0",s="\0MK_CLOSE\0",a=e.replace(/<mark>/g,t).replace(/<\/mark>/g,s);return _(a).replace(new RegExp(t,"g"),"<mark>").replace(new RegExp(s,"g"),"</mark>")}function E(e){switch(e){case"prompt":return"you typed";case"user":return"you said";case"assistant":return"claude said";case"thinking":return"thinking";case"tool_result":return"tool output";default:return e}}function L(){return f(".role-toggle input").filter(e=>e.checked).map(e=>e.dataset.role)}function u(e){const t=new Map;for(const s of e){const a=s.session_id??`__unlinked__:${s.project_path??"_"}`;let n=t.get(a);n||(n={session_id:s.session_id,project_path:s.project_path,latest_ms:s.timestamp_ms,earliest_ms:s.timestamp_ms,hits:[]},t.set(a,n)),n.hits.push(s),s.timestamp_ms>n.latest_ms&&(n.latest_ms=s.timestamp_ms),s.timestamp_ms<n.earliest_ms&&(n.earliest_ms=s.timestamp_ms)}for(const s of t.values())s.hits.sort((a,n)=>a.timestamp_ms-n.timestamp_ms);return[...t.values()].sort((s,a)=>a.latest_ms-s.latest_ms)}function w(e,t){const s=_(y(e.project_path)),a=e.latest_ms?k(new Date(e.latest_ms).toISOString()):"",n=!e.session_id,c=e.session_id?`/session?id=${encodeURIComponent(e.session_id)}${t?"&q="+encodeURIComponent(t):""}`:null,d=`
|
|
2
|
+
<div class="s-session-head">
|
|
3
|
+
<div>
|
|
4
|
+
<span class="proj">${s}</span>
|
|
5
|
+
<span class="count">${e.hits.length} match${e.hits.length===1?"":"es"}</span>
|
|
6
|
+
<span class="meta" style="display:block;margin-top:0.15rem;">${a}${n?" · no linked session":""}</span>
|
|
7
|
+
</div>
|
|
8
|
+
${c?`<a class="open" href="${c}">Open session →</a>`:""}
|
|
9
|
+
</div>`,$=e.hits.map(o=>{const b=o.timestamp_ms?new Date(o.timestamp_ms).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):"";return`
|
|
10
|
+
<div class="s-seg">
|
|
11
|
+
<div class="s-seg-role-col">
|
|
12
|
+
<span class="s-pill role-${o.role}">${E(o.role)}</span>
|
|
13
|
+
<span class="s-seg-time">${b}</span>
|
|
14
|
+
${o.pasted_chars>0?`<span class="s-pasted-tag">+${S(o.pasted_chars)} pasted</span>`:""}
|
|
15
|
+
</div>
|
|
16
|
+
<div class="s-seg-snippet">${j(o.snippet)}</div>
|
|
17
|
+
</div>`}).join("");return`<div class="s-session-card${n?" is-unlinked":""}">${d}<div class="s-seg-list">${$}</div></div>`}function x(e){const t=r("#totals"),s=m.value.trim();if(s){t.style.display="";const c=u(e.results).filter(d=>d.session_id).length;t.innerHTML=`${e.total.toLocaleString()} matches across ${c} session${c===1?"":"s"} · <span style="color:var(--accent);">${e.prompt_total.toLocaleString()}</span> in your prompts, <span style="color:var(--codex);">${e.transcript_total.toLocaleString()}</span> in transcripts`}else t.style.display="none";if(e.results.length===0){r("#results").innerHTML=s?'<div class="empty">No matches.</div>':'<div class="empty">Type something to search across every prompt and every transcript.</div>';return}const a=u(e.results);r("#results").innerHTML=a.map(n=>w(n,s)).join("")}async function v(){try{const{projects:e}=await l.promptProjects();for(const t of e){const s=document.createElement("option");s.value=t,s.textContent=y(t),p.appendChild(s)}}catch{}}async function B(){try{const e=await l.searchIndexStatus(),t=document.getElementById("search-disabled-banner");t.style.display=e.enabled?"none":"flex";const s=document.getElementById("banner-enable");s&&!e.enabled&&(s.onclick=async()=>{s.setAttribute("disabled","true"),s.textContent="Enabling…",await l.searchIndexEnable(),t.style.display="none",await i(),p.querySelectorAll('option:not([value=""])').forEach(a=>a.remove()),await v()})}catch{}}let h;async function i(){const e=m.value.trim(),t=p.value||void 0,s=g.checked,a=L();try{const n=await l.search({q:e,limit:200,project:t,includeSlash:s,roles:a});x(n)}catch{r("#results").innerHTML='<div class="empty">Search failed.</div>'}}function I(){clearTimeout(h),h=setTimeout(i,150)}m.addEventListener("input",I);p.addEventListener("change",i);g.addEventListener("change",i);f(".role-toggle input").forEach(e=>e.addEventListener("change",i));v();B();i();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import{a as k,u as m,t as C,n as a,d as E,e as n}from"./format.DxC1NGYT.js";const $=Intl.DateTimeFormat().resolvedOptions().timeZone,f=(()=>{const t=new Date().toLocaleTimeString("en-US",{timeZoneName:"short"}).match(/[A-Z]{2,5}$/);return t?t[0]:""})(),u=s=>s?new Date(s).toLocaleString([],{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}):"—",b=s=>s?new Date(s).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}):"—";function M(s){const t="\0MK_OPEN\0",r="\0MK_CLOSE\0",i=s.replace(/<mark>/g,t).replace(/<\/mark>/g,r);return n(i).replace(new RegExp(t,"g"),"<mark>").replace(new RegExp(r,"g"),"</mark>")}function D(s){switch(s){case"user":return"you said";case"assistant":return"claude said";case"thinking":return"claude thinking";case"tool_result":return"tool output";default:return s}}const T=new URLSearchParams(location.search),h=T.get("id"),x=T.get("q")??"",g=document.getElementById("content");h?k.session(h).then(s=>{if(!s){g.innerHTML='<p class="empty">Session not found.</p>';return}const{session:t,turns:r}=s,i=t.total_fresh_input_tokens+t.total_output_tokens+t.total_cache_read_tokens+t.total_cache_write_tokens,c=t.metadata?.sub_agent_session_ids,w=t.agent_reported_cost_usd!=null?`<span style="color:var(--text-2);font-size:0.8rem;"> (agent reported: ${m(t.agent_reported_cost_usd)})</span>`:"";g.innerHTML=`
|
|
2
|
+
<div class="grid-cards" style="margin-bottom:1.2rem;">
|
|
3
|
+
<div class="card kpi"><span class="kpi-label">Tokens</span><span class="kpi-value tokens">${C(i)}</span><span class="kpi-sub">${a(i)} total</span></div>
|
|
4
|
+
<div class="card kpi"><span class="kpi-label">Cost <span style="color:var(--text-2);font-weight:400;">(est.)</span></span><span class="kpi-value cost">~${m(t.total_cost_usd)}</span><span class="kpi-sub">${w}</span></div>
|
|
5
|
+
<div class="card kpi"><span class="kpi-label">Turns</span><span class="kpi-value">${a(t.turn_count)}</span><span class="kpi-sub">${r.length} loaded</span></div>
|
|
6
|
+
<div class="card kpi"><span class="kpi-label">Duration</span><span class="kpi-value">${E(t.duration_sec)}</span><span class="kpi-sub">started ${u(t.started_at)}</span></div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="card" style="margin-bottom:1rem;">
|
|
10
|
+
<h3>Session</h3>
|
|
11
|
+
<table style="border:none;">
|
|
12
|
+
<tbody>
|
|
13
|
+
<tr><td style="width:25%;color:var(--text-2);">Claude Code</td><td><code>${n(t.agent_version??"—")}</code></td></tr>
|
|
14
|
+
<tr><td style="color:var(--text-2);">Project</td><td class="mono">${n(t.project_path||"—")}</td></tr>
|
|
15
|
+
<tr><td style="color:var(--text-2);">Primary model</td><td><code>${n(t.primary_model)}</code></td></tr>
|
|
16
|
+
<tr><td style="color:var(--text-2);">Started</td><td>${u(t.started_at)} <span style="color:var(--text-2);font-size:0.8rem;">${n(f)} (${n($)})</span></td></tr>
|
|
17
|
+
<tr><td style="color:var(--text-2);">Ended</td><td>${t.ended_at?u(t.ended_at):'<span style="color:var(--text-2);">— still active</span>'}</td></tr>
|
|
18
|
+
<tr><td style="color:var(--text-2);">Fresh input</td><td>${a(t.total_fresh_input_tokens)} tokens</td></tr>
|
|
19
|
+
<tr><td style="color:var(--text-2);">Output</td><td>${a(t.total_output_tokens)} tokens</td></tr>
|
|
20
|
+
<tr><td style="color:var(--text-2);">Cache writes</td><td>${a(t.total_cache_write_tokens)} tokens</td></tr>
|
|
21
|
+
<tr><td style="color:var(--text-2);">Cache reads</td><td>${a(t.total_cache_read_tokens)} tokens</td></tr>
|
|
22
|
+
<tr><td style="color:var(--text-2);">Pricing version</td><td><code>${n(t.pricing_table_version)}</code></td></tr>
|
|
23
|
+
<tr><td style="color:var(--text-2);">Session id</td><td class="mono" style="word-break:break-all;">${n(t.id)}</td></tr>
|
|
24
|
+
</tbody>
|
|
25
|
+
</table>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
${c?.length?`
|
|
29
|
+
<div class="card" style="margin-bottom:1rem;">
|
|
30
|
+
<h3>Sub-agents (${c.length})</h3>
|
|
31
|
+
<ul style="margin:0;padding-left:1.2rem;color:var(--text-1);font-size:0.88rem;">
|
|
32
|
+
${c.map(e=>`<li><a href="/session?id=${encodeURIComponent(e)}" class="mono">${n(e)}</a></li>`).join("")}
|
|
33
|
+
</ul>
|
|
34
|
+
</div>`:""}
|
|
35
|
+
|
|
36
|
+
<div class="card" style="margin-bottom:1rem;">
|
|
37
|
+
<div style="display:flex;gap:0.6rem;align-items:center;flex-wrap:wrap;">
|
|
38
|
+
<h3 style="margin:0;flex-shrink:0;">Search this session</h3>
|
|
39
|
+
<input id="seg-q" type="text" placeholder="Find text in this session…"
|
|
40
|
+
style="flex:1;min-width:200px;background:var(--bg-2);color:var(--text-0);
|
|
41
|
+
border:1px solid var(--border);border-radius:6px;padding:0.4rem 0.7rem;
|
|
42
|
+
font-size:0.9rem;font-family:inherit;outline:none;" />
|
|
43
|
+
<span id="seg-count" style="color:var(--text-2);font-size:0.8rem;"></span>
|
|
44
|
+
</div>
|
|
45
|
+
<div id="seg-results" style="margin-top:0.8rem;"></div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="card">
|
|
49
|
+
<div style="display:flex;justify-content:space-between;align-items:baseline;">
|
|
50
|
+
<h3 style="margin:0;">Turns timeline</h3>
|
|
51
|
+
<span style="color:var(--text-2);font-size:0.75rem;">times in ${f} (${$})</span>
|
|
52
|
+
</div>
|
|
53
|
+
${r.length?`
|
|
54
|
+
<table style="margin-top:0.8rem;">
|
|
55
|
+
<thead>
|
|
56
|
+
<tr>
|
|
57
|
+
<th class="num">#</th><th>Time</th><th>Model</th>
|
|
58
|
+
<th class="num">Fresh in</th><th class="num">Cache read</th><th class="num">Cache write</th>
|
|
59
|
+
<th class="num">Output</th><th class="num">Tools</th><th class="num">Cost <span class="est">(est.)</span></th>
|
|
60
|
+
</tr>
|
|
61
|
+
</thead>
|
|
62
|
+
<tbody>
|
|
63
|
+
${r.map(e=>`
|
|
64
|
+
<tr>
|
|
65
|
+
<td class="num">${e.sequence}</td>
|
|
66
|
+
<td>${b(e.timestamp)}</td>
|
|
67
|
+
<td><code>${n(e.model)}</code></td>
|
|
68
|
+
<td class="num tok">${a(e.fresh_input_tokens)}</td>
|
|
69
|
+
<td class="num tok">${a(e.cache_read_tokens)}</td>
|
|
70
|
+
<td class="num tok">${a(e.cache_write_tokens)}</td>
|
|
71
|
+
<td class="num tok">${a(e.output_tokens)}</td>
|
|
72
|
+
<td class="num">${e.tool_calls_count}</td>
|
|
73
|
+
<td class="num cost">~${m(e.cost_usd)}</td>
|
|
74
|
+
</tr>`).join("")}
|
|
75
|
+
</tbody>
|
|
76
|
+
</table>`:'<p class="empty">No turns recorded for this session.</p>'}
|
|
77
|
+
</div>
|
|
78
|
+
`;const p=document.getElementById("seg-q"),d=document.getElementById("seg-results"),v=document.getElementById("seg-count");let y;async function _(){const e=p.value.trim();if(!e){d.innerHTML="",v.textContent="";return}try{const l=await k.sessionTranscriptSearch(h,e,200);if(v.textContent=`${l.total} match${l.total===1?"":"es"}`,l.segments.length===0){d.innerHTML='<p class="empty" style="margin:0.5rem 0;">No matches in this session.</p>';return}const S=l.segments.slice().sort((o,L)=>Date.parse(o.timestamp)-Date.parse(L.timestamp));d.innerHTML=S.map(o=>`
|
|
79
|
+
<div class="sd-seg-card">
|
|
80
|
+
<div class="sd-seg-meta">
|
|
81
|
+
<span class="sd-role-pill role-${o.role}">${D(o.role)}</span>
|
|
82
|
+
<span style="margin-left:0.5rem;">${b(o.timestamp)}</span>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="sd-seg-snippet">${M(o.snippet)}</div>
|
|
85
|
+
</div>
|
|
86
|
+
`).join("")}catch{d.innerHTML='<p class="empty">Search failed.</p>'}}p.addEventListener("input",()=>{clearTimeout(y),y=setTimeout(_,150)}),x&&(p.value=x,_())}):g.innerHTML='<p class="empty">No session id.</p>';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import{a as i,e as a}from"./format.DxC1NGYT.js";const s=e=>document.querySelector(e);function x(e){return e.replace(/([A-Z])/g," $1").replace(/_/g," ").toLowerCase().trim()}function h(e){return typeof e=="boolean"?`<span style="color:${e?"var(--good)":"var(--text-2)"};">${e?"yes":"no"}</span>`:typeof e=="number"?`<span style="font-variant-numeric:tabular-nums;color:var(--text-0);">${e.toLocaleString()}</span>`:e==null?'<span style="color:var(--text-2);">—</span>':typeof e=="object"?`<code style="font-size:0.78rem;">${a(JSON.stringify(e))}</code>`:a(String(e))}function $(e){return Object.entries(e).map(([n,r])=>`
|
|
2
|
+
<div style="display:flex;justify-content:space-between;gap:1rem;padding:0.4rem 0;border-top:1px solid var(--border);">
|
|
3
|
+
<span style="color:var(--text-2);text-transform:uppercase;letter-spacing:0.05em;font-size:0.72rem;">${a(x(n))}</span>
|
|
4
|
+
<span>${h(r)}</span>
|
|
5
|
+
</div>
|
|
6
|
+
`).join("")}function v(e){const t=e.split(/[\\/]/);return t[t.length-1]||e}function w(e,t=70){if(e.length<=t)return e;const n=e.split(/[\\/]/);return n.length<=3?e:".../"+n.slice(-3).join("/")}function k(e){try{const t=JSON.parse(e);if(Array.isArray(t)&&t.length>0)return t.slice(0,3).map(n=>{const r=Array.isArray(n.path)&&n.path.length?` at ${n.path.join(".")}`:"";return n.code==="invalid_type"?`expected ${n.expected}, received ${n.received}${r}`:`${n.message||n.code}${r}`}).join("; ")+(t.length>3?` (+${t.length-3} more)`:"")}catch{}return e.replace(/\s+/g," ").slice(0,240)}function _(e){const t=e.file??"unknown",n=v(t),r=w(t),u=k(e.reason??""),d=e.raw_line_truncated??"",o=typeof e.byte_offset=="number"&&e.byte_offset>=0?`offset ${e.byte_offset.toLocaleString()}`:"";return`
|
|
7
|
+
<div style="padding:0.7rem 0;border-bottom:1px solid var(--border);word-break:break-word;overflow-wrap:anywhere;">
|
|
8
|
+
<div style="display:flex;justify-content:space-between;gap:0.6rem;align-items:baseline;flex-wrap:wrap;">
|
|
9
|
+
<code style="color:var(--accent);background:none;padding:0;font-size:0.84rem;">${a(n)}</code>
|
|
10
|
+
<span style="color:var(--text-2);font-size:0.72rem;font-variant-numeric:tabular-nums;">${a(o)}</span>
|
|
11
|
+
</div>
|
|
12
|
+
<div style="color:var(--text-2);font-size:0.7rem;margin-top:0.1rem;" title="${a(t)}">${a(r)}</div>
|
|
13
|
+
<div style="color:var(--text-0);font-size:0.82rem;margin-top:0.4rem;">${a(u)}</div>
|
|
14
|
+
${d?`
|
|
15
|
+
<details style="margin-top:0.35rem;">
|
|
16
|
+
<summary style="cursor:pointer;color:var(--text-2);font-size:0.76rem;list-style:none;">▸ raw line</summary>
|
|
17
|
+
<pre style="margin:0.3rem 0 0;background:var(--bg-2);padding:0.5rem 0.7rem;border-radius:4px;font-size:0.72rem;white-space:pre-wrap;word-break:break-all;max-height:160px;overflow-y:auto;">${a(d)}</pre>
|
|
18
|
+
</details>`:""}
|
|
19
|
+
</div>
|
|
20
|
+
`}async function L(){const[e,t,n]=await Promise.all([i.pricing(),i.parseErrors(),fetch("/api/ingest/status").then(r=>r.json())]);s("#pv").textContent=e.version,s("#status").innerHTML=$(n),s("#err-count").textContent=t.errors.length===0?"No parse errors recorded. Healthy.":`${t.errors.length} recent parse error${t.errors.length===1?"":"s"}.`,s("#errs").innerHTML=t.errors.length===0?"":t.errors.map(_).join("")}function m(e,t){return`<span style="display:inline-flex;align-items:center;gap:0.35em;
|
|
21
|
+
padding:0.15em 0.6em;border-radius:999px;background:${t}22;color:${t};
|
|
22
|
+
font-size:0.78rem;font-weight:500;">
|
|
23
|
+
<span style="width:6px;height:6px;border-radius:50%;background:${t};"></span>
|
|
24
|
+
${e}</span>`}async function c(){let e;try{e=await i.searchIndexStatus()}catch{return}const t=s("#search-status-line"),n=s("#search-actions"),r=s("#search-progress"),u=e.enabled?m("Enabled","#7ee787"):m("Disabled","#6b7585"),d=e.segment_count>0?`${e.segment_count.toLocaleString()} segments across ${e.indexed_sessions} session${e.indexed_sessions===1?"":"s"}`:"no data indexed yet";t.innerHTML=`${u}<span style="color:var(--text-2);font-size:0.85rem;">${d}</span>`,e.backfill.in_progress?(r.style.display="",r.textContent=`Indexing… ${e.backfill.processed}/${e.backfill.total} sessions`):r.style.display="none";const o=[];e.enabled?o.push('<button id="btn-disable">Disable indexing</button>'):o.push('<button id="btn-enable" style="border-color:var(--accent);color:var(--accent);">Enable indexing</button>'),e.segment_count>0&&o.push('<button id="btn-clear" style="border-color:var(--bad);color:var(--bad);">Clear indexed data</button>'),n.innerHTML=o.join("");const p=document.getElementById("btn-enable");p&&p.addEventListener("click",async()=>{p.setAttribute("disabled","true"),p.textContent="Enabling…",await i.searchIndexEnable(),await c()});const f=document.getElementById("btn-disable");f&&f.addEventListener("click",async()=>{confirm("Stop indexing new sessions? Existing indexed data stays on disk and can be searched again if you re-enable.")&&(await i.searchIndexDisable(),await c())});const b=document.getElementById("btn-clear");b&&b.addEventListener("click",async()=>{if(!confirm(`Delete all ${e.segment_count.toLocaleString()} indexed segments and disable indexing? This cannot be undone — re-enabling will require re-indexing from your sessions.`))return;const g=await i.searchIndexClear();if(await c(),typeof g?.freed_bytes=="number"){const y=l=>l>=1048576?(l/1024/1024).toFixed(1)+" MB":l>=1024?(l/1024).toFixed(1)+" KB":l+" B";s("#search-progress").style.display="",s("#search-progress").innerHTML=`Cleared. Freed <strong style="color:var(--text-0);">${y(g.freed_bytes)}</strong> on disk.`,setTimeout(()=>{s("#search-progress").style.display="none"},6e3)}})}L();c();setInterval(c,2e3);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import{a as C,n,e as y}from"./format.DxC1NGYT.js";import{u as S,i as L,a as k,b as M,c as T,d as z,e as E}from"./installCanvasRenderer.D_tC6TXz.js";S([L,k,M,T,z]);const a=e=>document.querySelector(e),u=a("#w");let $,g,w;function h(e,t,o,l,c){const i=E(e);return i.setOption({textStyle:{color:"#9ba6b3"},tooltip:{trigger:"axis",axisPointer:{type:"shadow"},backgroundColor:"#1c222d",borderColor:"#262d3a",textStyle:{color:"#e6edf3",fontSize:12},formatter:d=>{const p=d[0].dataIndex,b=o[p],r=c?.[p]??0;return`${t[p]}<br>Calls: ${b.toLocaleString()}`+(r?`<br>Errors: ${r.toLocaleString()} (${(r/b*100).toFixed(1)}%)`:"")}},grid:{left:160,right:30,top:8,bottom:28},xAxis:{type:"value",axisLabel:{color:"#6b7585",fontSize:11},splitLine:{lineStyle:{color:"#1c222d"}},axisLine:{show:!1}},yAxis:{type:"category",data:t.slice().reverse(),axisLabel:{color:"#6b7585",fontSize:11},axisLine:{show:!1},axisTick:{show:!1}},series:[{type:"bar",data:o.slice().reverse(),itemStyle:{color:l,borderRadius:[0,4,4,0]},barWidth:"60%"}]}),i}function v(e,t,o,l="scalar"){const c=l==="name"?"font-size:1.05rem;font-family:'SF Mono',Consolas,monospace;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;display:block;color:var(--text-0);":"",i=l==="name"?`title="${t.replace(/"/g,""")}"`:"";return`<div class="card"><div class="kpi">
|
|
2
|
+
<div class="kpi-label">${e}</div>
|
|
3
|
+
<div class="kpi-value" style="${c}" ${i}>${t}</div>
|
|
4
|
+
${o?`<div class="kpi-sub">${o}</div>`:""}
|
|
5
|
+
</div></div>`}async function x(){const e=await C.toolsOverview(u.value),t=e.total_calls,o=e.total_errors,l=t>0?(o/t*100).toFixed(1)+"%":"—",c=e.tool_leaderboard[0]?.name??"—",i=e.mcp_servers[0]?.server??"—";if(a("#kpis").innerHTML=[v("Tool calls",n(t),u.value==="all"?"all time":u.value),v("Error rate",l,o>0?`${n(o)} errors`:"no errors"),v("Top tool",y(c),e.tool_leaderboard[0]?`${n(e.tool_leaderboard[0].calls)} calls`:"","name"),v("Top MCP",y(i),e.mcp_servers[0]?`${n(e.mcp_servers[0].calls)} calls`:"no MCP usage","name")].join(""),$?.dispose(),e.tool_leaderboard.length===0)a("#leaderboard").innerHTML='<p class="empty">No tool activity in this window.</p>';else{const s=e.tool_leaderboard.slice(0,12);$=h(a("#leaderboard"),s.map(m=>m.name),s.map(m=>m.calls),"#f0883e",s.map(m=>m.errors))}const d=a("#splits"),p=d.children[0],b=d.children[1],r=e.mcp_servers.length>0,f=e.subagents.length>0;g?.dispose(),w?.dispose(),d.style.display=r||f?"":"none",p.style.display=r?"":"none",b.style.display=f?"":"none",r&&(g=h(a("#mcp"),e.mcp_servers.map(s=>s.server),e.mcp_servers.map(s=>s.calls),"#58a6ff",e.mcp_servers.map(s=>s.errors))),f&&(w=h(a("#subagents"),e.subagents.map(s=>s.type),e.subagents.map(s=>s.calls),"#bc8cff",e.subagents.map(s=>s.errors)));const _=a("#tools-table tbody");_.innerHTML=e.tool_leaderboard.length===0?'<tr><td colspan="4" class="empty">—</td></tr>':e.tool_leaderboard.map(s=>`
|
|
6
|
+
<tr>
|
|
7
|
+
<td><code>${y(s.name)}</code></td>
|
|
8
|
+
<td class="num">${n(s.calls)}</td>
|
|
9
|
+
<td class="num">${n(s.errors)}</td>
|
|
10
|
+
<td class="num">${(s.error_rate*100).toFixed(1)}%</td>
|
|
11
|
+
</tr>
|
|
12
|
+
`).join("")}u.addEventListener("change",x);x();window.addEventListener("resize",()=>[$,g,w].forEach(e=>e?.resize()));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{a as h,e as d,n as i,u as g}from"./format.DxC1NGYT.js";import{t as k}from"./charts.BIevw6Es.js";const s=n=>document.querySelector(n);let c;async function l(){const n=s("#g").value,u=s("#b").value,{points:a}=await h.trends(n,u);c?.dispose(),a.length?c=k(s("#line"),a):s("#line").innerHTML='<p class="empty">No data.</p>';const r=[...new Set(a.flatMap(t=>Object.keys(t.groups)))];s("#breakdown").innerHTML=a.length?`
|
|
2
|
+
<table>
|
|
3
|
+
<thead><tr><th>${n.charAt(0).toUpperCase()+n.slice(1)}</th>${r.map(t=>`<th class="num">${d(t)} tokens</th>`).join("")}<th class="num">Total tokens</th><th class="num">Total <span class="est">(est. cost)</span></th></tr></thead>
|
|
4
|
+
<tbody>${a.map(t=>{const p=r.reduce((o,e)=>o+(t.groups[e]?.cost??0),0),m=r.reduce((o,e)=>o+(t.groups[e]?.tokens??0),0);return`<tr><td>${d(t.bucket)}</td>${r.map(o=>{const e=t.groups[o]?.tokens??0;return`<td class="num tok">${e>0?i(e):"—"}</td>`}).join("")}<td class="num tok"><strong>${i(m)}</strong></td><td class="num cost">~${g(p)}</td></tr>`}).join("")}</tbody>
|
|
5
|
+
</table>`:'<p class="empty">—</p>'}s("#g").addEventListener("change",l);s("#b").addEventListener("change",l);l();window.addEventListener("resize",()=>c?.resize());
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Overview</title><link rel="stylesheet" href="/styles/global.css"></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class="active">Overview</a> <a href="/sessions" class>Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <section class="hero"> <div class="hero-controls"> <select id="window"> <option value="today">Today</option> <option value="7d" selected>Last 7 days</option> <option value="30d">Last 30 days</option> <option value="all">All time</option> </select> </div> <div class="hero-box"> <div class="hero-tokens"><span class="skel" style="width:5em;height:1em;"></span></div> <div class="hero-tokens-label">tokens</div> </div> <p class="hero-sub"> <span class="est-note">~<span id="hero-cost">—</span> estimated</span> <span style="margin: 0 0.6em; color: var(--text-2);">·</span> <strong id="hero-sessions">—</strong> sessions
|
|
2
|
+
<span id="hero-avg" style="margin-left:0.6em; color:var(--text-2);"></span> </p> </section> <div class="card" id="alerts-card" style="display:none; margin-bottom:1.2rem;"> <h3>What needs attention</h3> <div id="alerts-body"></div> </div> <div class="card" style="margin-bottom:1.2rem;"> <h3>Cost over time</h3> <div id="line" class="chart"></div> </div> <div class="grid-2"> <div class="card"> <h3>Daily heatmap (last 90 days)</h3> <div id="heatmap" class="chart"></div> </div> <div class="card"> <h3>Top models by cost (window)</h3> <div id="models" class="chart"></div> </div> </div> <div class="card" style="margin-top:1.2rem;"> <h3>Top sessions in window</h3> <div id="recent"></div> </div> <script type="module" src="/_astro/index.astro_astro_type_script_index_0_lang.CgwSARdD.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Models</title><link rel="stylesheet" href="/styles/global.css"></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class>Overview</a> <a href="/sessions" class>Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <h2>Spend by model</h2> <div class="card" style="margin-bottom:1rem;display:flex;gap:0.6rem;align-items:center;"> <label style="color:var(--text-2);font-size:0.8rem;">Window</label> <select id="w"> <option value="7d">Last 7 days</option> <option value="30d" selected>Last 30 days</option> <option value="all">All time</option> </select> </div> <div class="card"> <h3>Cost & token mix per model</h3> <div id="bar" class="chart-tall"></div> </div> <div class="card" style="margin-top:1rem;"> <h3>Per-model details</h3> <table id="t"> <thead> <tr> <th>Model</th> <th class="num">Sessions</th> <th class="num">Fresh in</th> <th class="num">Output</th> <th class="num">Cache read</th> <th class="num">Cache write</th> <th class="num">Total tokens</th> <th class="num">Cost <span class="est">(est.)</span></th> <th class="num">Avg $/session</th> </tr> </thead> <tbody></tbody> </table> </div> <script type="module" src="/_astro/models.astro_astro_type_script_index_0_lang.BHTHXYHC.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Search</title><link rel="stylesheet" href="/styles/global.css"><style>.role-toggle{display:inline-flex;align-items:center;gap:.3rem;cursor:pointer;color:var(--text-1)}.s-session-card{background:var(--bg-1);border:1px solid var(--border);border-radius:10px;padding:0;margin-bottom:.85rem;overflow:hidden}.s-session-card.is-unlinked{border-style:dashed}.s-session-head{display:flex;justify-content:space-between;align-items:center;gap:1rem;padding:.7rem 1rem;background:var(--bg-2);border-bottom:1px solid var(--border);font-size:.82rem;flex-wrap:wrap}.s-session-head .proj{font-family:SF Mono,Consolas,monospace;color:var(--text-0);font-weight:500}.s-session-head .meta{color:var(--text-2);font-size:.75rem}.s-session-head .open{color:var(--accent);font-size:.82rem;flex-shrink:0}.s-session-head .count{display:inline-block;background:var(--accent-dim);color:var(--accent);padding:.05em .55em;border-radius:4px;font-size:.72rem;font-weight:500;margin-left:.5rem}.s-seg-list{padding:.4rem .4rem .6rem}.s-seg{display:grid;grid-template-columns:max-content 1fr;gap:.7rem;padding:.55rem .7rem;border-radius:6px;align-items:start}.s-seg+.s-seg{border-top:1px solid var(--border)}.s-seg:hover{background:#ffffff04}.s-seg-role-col{display:flex;flex-direction:column;gap:.2rem;align-items:flex-start;padding-top:.1rem;min-width:92px}.s-seg-snippet{font-size:.88rem;color:var(--text-0);white-space:pre-wrap;word-break:break-word;line-height:1.45;overflow-wrap:anywhere}.s-seg-snippet mark{background:#f0883e47;color:var(--text-0);padding:0 1px;border-radius:2px}.s-seg-time{color:var(--text-2);font-size:.7rem}.s-pill{display:inline-block;padding:.05em .55em;border-radius:4px;font-size:.7rem;font-weight:500;letter-spacing:.02em;white-space:nowrap}.s-pill.role-prompt{background:#f0883e2e;color:var(--accent)}.s-pill.role-user{background:#7ee78729;color:var(--good)}.s-pill.role-assistant{background:#58a6ff29;color:var(--codex)}.s-pill.role-thinking{background:#bc8cff29;color:#bc8cff}.s-pill.role-tool_result{background:var(--bg-2);color:var(--text-2)}.s-pasted-tag{display:inline-block;background:var(--bg-2);color:var(--text-2);padding:.05em .5em;border-radius:4px;font-size:.68rem;margin-top:.2rem}
|
|
2
|
+
</style></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class>Overview</a> <a href="/sessions" class>Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <h2>Search</h2> <p style="color:var(--text-2);font-size:0.85rem;margin:-0.4rem 0 1rem;">
|
|
3
|
+
Searches every prompt you've typed <em>and</em> every transcript Claude wrote
|
|
4
|
+
(assistant text, your replies, tool output). 100% local — SQLite FTS5, no
|
|
5
|
+
embeddings, no API calls.
|
|
6
|
+
</p> <div id="search-disabled-banner" style="display:none;
|
|
7
|
+
background:var(--bg-1);border:1px solid var(--warn);border-left-width:4px;
|
|
8
|
+
border-radius:6px;padding:0.85rem 1.05rem;margin-bottom:1rem;
|
|
9
|
+
color:var(--text-1);font-size:0.88rem;
|
|
10
|
+
align-items:center;gap:0.8rem;flex-wrap:wrap;"> <div style="flex:1;min-width:240px;"> <strong style="color:var(--warn);">Search is off.</strong>
|
|
11
|
+
Argus isn't surfacing any of your prompt or transcript history right now.
|
|
12
|
+
Enable to index your sessions for full-text search — 100% local, no API calls.
|
|
13
|
+
</div> <button id="banner-enable" style="border-color:var(--accent);color:var(--accent);flex-shrink:0;">
|
|
14
|
+
Enable search
|
|
15
|
+
</button> </div> <div class="card" style="margin-bottom:1rem;display:flex;gap:0.6rem;align-items:center;flex-wrap:wrap;"> <input id="q" type="text" placeholder="Search…" style="flex:1;min-width:240px;background:var(--bg-2);color:var(--text-0);
|
|
16
|
+
border:1px solid var(--border);border-radius:6px;padding:0.5rem 0.8rem;
|
|
17
|
+
font-size:0.95rem;font-family:inherit;outline:none;"> <select id="proj"> <option value="">All projects</option> </select> </div> <div class="card" style="margin-bottom:1rem;display:flex;gap:0.6rem;align-items:center;flex-wrap:wrap;font-size:0.82rem;"> <span style="color:var(--text-2);">Show:</span> <label class="role-toggle"><input type="checkbox" data-role="prompt" checked> Your prompts</label> <label class="role-toggle"><input type="checkbox" data-role="user" checked> Your replies</label> <label class="role-toggle"><input type="checkbox" data-role="assistant" checked> Claude</label> <label class="role-toggle"><input type="checkbox" data-role="thinking"> Thinking</label> <label class="role-toggle"><input type="checkbox" data-role="tool_result"> Tool output</label> <span style="margin-left:auto;color:var(--text-2);"> <label style="cursor:pointer;"><input id="slash" type="checkbox"> Include slash commands</label> </span> </div> <div id="totals" class="card" style="margin-bottom:1rem;display:none;
|
|
18
|
+
color:var(--text-2);font-size:0.82rem;text-align:center;padding:0.6rem 1rem;"></div> <div id="results"></div> <script type="module" src="/_astro/prompts.astro_astro_type_script_index_0_lang.DfNgiDv9.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Session</title><link rel="stylesheet" href="/styles/global.css"><style>.sd-seg-card{background:var(--bg-1);border:1px solid var(--border);border-radius:8px;padding:.75rem 1rem;margin-bottom:.55rem}.sd-seg-meta{font-size:.74rem;color:var(--text-2);margin-bottom:.3rem}.sd-seg-snippet{font-size:.88rem;color:var(--text-0);white-space:pre-wrap;word-break:break-word;line-height:1.4;overflow-wrap:anywhere}.sd-seg-snippet mark{background:#f0883e47;color:var(--text-0);padding:0 1px;border-radius:2px}.sd-role-pill{display:inline-block;padding:.05em .55em;border-radius:4px;font-size:.7rem;font-weight:500}.sd-role-pill.role-user{background:#7ee78729;color:var(--good)}.sd-role-pill.role-assistant{background:#58a6ff29;color:var(--codex)}.sd-role-pill.role-thinking{background:#bc8cff29;color:#bc8cff}.sd-role-pill.role-tool_result{background:var(--bg-2);color:var(--text-2)}
|
|
2
|
+
</style></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class>Overview</a> <a href="/sessions" class>Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <p style="margin:0 0 1rem;"><a href="/sessions">← All sessions</a></p> <div id="content"> <div class="card"><div class="skel" style="width:60%;height:1.5em;"></div></div> </div> <script type="module" src="/_astro/session.astro_astro_type_script_index_0_lang.Dj_bfrIa.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Sessions</title><link rel="stylesheet" href="/styles/global.css"></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class>Overview</a> <a href="/sessions" class="active">Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <h2>Sessions</h2> <div class="card" style="margin-bottom:1rem;"> <div style="display:flex;gap:0.6rem;flex-wrap:wrap;align-items:center;"> <input type="text" id="search" placeholder="Search project, model, or id…" style="flex:1;min-width:200px;"> <span id="count" style="color:var(--text-2);font-size:0.85rem;"></span> </div> </div> <div class="card" style="padding:0;overflow:auto;max-height:75vh;"> <table id="t"> <thead> <tr> <th data-sort="started_at" class="sorted">Started</th> <th data-sort="project_path">Project</th> <th data-sort="primary_model">Model</th> <th data-sort="duration_sec" class="num">Duration</th> <th data-sort="turn_count" class="num">Turns</th> <th data-sort="tokens" class="num">Tokens</th> <th data-sort="total_cost_usd" class="num">Cost <span class="est">(est.)</span></th> </tr> </thead> <tbody></tbody> </table> </div> <script type="module" src="/_astro/index.astro_astro_type_script_index_0_lang.W18SJsr7.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Settings</title><link rel="stylesheet" href="/styles/global.css"></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class>Overview</a> <a href="/sessions" class>Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <h2>Settings</h2> <div class="grid-2"> <div> <div class="card" id="search-card"> <h3>Search indexing</h3> <p style="color:var(--text-1);font-size:0.85rem;margin-top:0;">
|
|
2
|
+
Index every assistant response, your replies, and tool output for full-text
|
|
3
|
+
search. 100% local — SQLite FTS5, no embeddings, no API calls. Costs disk space
|
|
4
|
+
(~0.5 MB per session on average), so it's opt-in.
|
|
5
|
+
</p> <div id="search-status-line" style="display:flex;align-items:center;gap:0.6rem;
|
|
6
|
+
padding:0.7rem 0;border-top:1px solid var(--border);margin-top:0.6rem;"> <span class="skel" style="width:8em;height:1em;"></span> </div> <div id="search-actions" style="display:flex;gap:0.5rem;flex-wrap:wrap;margin-top:0.4rem;"></div> <div id="search-progress" style="display:none;margin-top:0.6rem;color:var(--text-2);font-size:0.82rem;"></div> </div> <div class="card"> <h3>Pricing</h3> <p>Pricing table version: <code id="pv">…</code></p> <p style="color:var(--text-1);font-size:0.85rem;">To refresh from LiteLLM, run in terminal:</p> <pre>argus pricing refresh</pre> </div> <div class="card"> <h3>Export data</h3> <p style="color:var(--text-1);font-size:0.85rem;">All session metadata. No prompts, responses, or code are stored.</p> <p> <a href="/api/export.json" download class="button-link"><button>Download JSON</button></a>
|
|
7
|
+
|
|
8
|
+
<a href="/api/export.csv" download class="button-link"><button>Download CSV</button></a> </p> </div> <div class="card"> <h3>Wipe local data</h3> <p style="color:var(--text-1);font-size:0.85rem;">Delete <code>~/.argus/</code> from disk. Cannot be undone — must run from terminal:</p> <pre>argus wipe</pre> </div> </div> <div> <div class="card"> <h3>Ingest status</h3> <div id="status" style="font-size:0.85rem;"><span class="skel" style="width:8em;"></span></div> </div> <div class="card"> <h3>Parse errors</h3> <p id="err-count" style="color:var(--text-2);font-size:0.85rem;margin-top:0;">…</p> <div id="errs" style="max-height:420px;overflow-y:auto;overflow-x:hidden;"></div> </div> </div> </div> <script type="module" src="/_astro/settings.astro_astro_type_script_index_0_lang.d_a-uvdi.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg-0: #0a0d12;
|
|
3
|
+
--bg-1: #131820;
|
|
4
|
+
--bg-2: #1c222d;
|
|
5
|
+
--border: #262d3a;
|
|
6
|
+
--text-0: #e6edf3;
|
|
7
|
+
--text-1: #9ba6b3;
|
|
8
|
+
--text-2: #6b7585;
|
|
9
|
+
--accent: #f0883e;
|
|
10
|
+
--accent-dim: rgba(240, 136, 62, 0.15);
|
|
11
|
+
--claude: #f0883e;
|
|
12
|
+
--codex: #58a6ff;
|
|
13
|
+
--good: #7ee787;
|
|
14
|
+
--warn: #d29922;
|
|
15
|
+
--bad: #f85149;
|
|
16
|
+
--shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
* { box-sizing: border-box; }
|
|
20
|
+
|
|
21
|
+
html, body {
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 0;
|
|
24
|
+
background: var(--bg-0);
|
|
25
|
+
color: var(--text-0);
|
|
26
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
line-height: 1.5;
|
|
29
|
+
-webkit-font-smoothing: antialiased;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
a { color: #58a6ff; text-decoration: none; }
|
|
33
|
+
a:hover { text-decoration: underline; }
|
|
34
|
+
|
|
35
|
+
code {
|
|
36
|
+
font-family: 'SF Mono', Consolas, 'Cascadia Code', monospace;
|
|
37
|
+
background: var(--bg-2);
|
|
38
|
+
padding: 0.1em 0.4em;
|
|
39
|
+
border-radius: 3px;
|
|
40
|
+
font-size: 0.85em;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pre {
|
|
44
|
+
background: var(--bg-1);
|
|
45
|
+
border: 1px solid var(--border);
|
|
46
|
+
padding: 1rem;
|
|
47
|
+
border-radius: 6px;
|
|
48
|
+
overflow-x: auto;
|
|
49
|
+
font-size: 0.82rem;
|
|
50
|
+
margin: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
header.app-header {
|
|
54
|
+
position: sticky;
|
|
55
|
+
top: 0;
|
|
56
|
+
z-index: 10;
|
|
57
|
+
background: rgba(10, 13, 18, 0.85);
|
|
58
|
+
backdrop-filter: blur(8px);
|
|
59
|
+
border-bottom: 1px solid var(--border);
|
|
60
|
+
padding: 0.9rem 2rem;
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: space-between;
|
|
64
|
+
}
|
|
65
|
+
.brand {
|
|
66
|
+
font-weight: 700;
|
|
67
|
+
font-size: 0.9rem;
|
|
68
|
+
letter-spacing: 0.15em;
|
|
69
|
+
color: var(--accent);
|
|
70
|
+
}
|
|
71
|
+
.brand .dot { color: var(--good); margin-left: 0.35em; font-size: 0.6em; }
|
|
72
|
+
nav.app-nav { display: flex; gap: 1.6rem; }
|
|
73
|
+
nav.app-nav a {
|
|
74
|
+
color: var(--text-1);
|
|
75
|
+
font-weight: 500;
|
|
76
|
+
font-size: 0.92rem;
|
|
77
|
+
letter-spacing: 0.01em;
|
|
78
|
+
padding: 0.3rem 0;
|
|
79
|
+
border-bottom: 2px solid transparent;
|
|
80
|
+
transition: color 0.15s, border-color 0.15s;
|
|
81
|
+
text-decoration: none;
|
|
82
|
+
}
|
|
83
|
+
nav.app-nav a:hover, nav.app-nav a.active { color: var(--text-0); border-bottom-color: var(--accent); }
|
|
84
|
+
|
|
85
|
+
main { padding: 1.5rem 2rem 4rem; max-width: 1200px; margin: 0 auto; }
|
|
86
|
+
|
|
87
|
+
footer.app-footer {
|
|
88
|
+
border-top: 1px solid var(--border);
|
|
89
|
+
padding: 1rem 2rem;
|
|
90
|
+
color: var(--text-2);
|
|
91
|
+
font-size: 0.78rem;
|
|
92
|
+
text-align: center;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
h1, h2, h3 { font-weight: 600; }
|
|
96
|
+
h2 { margin: 0 0 1rem; font-size: 1.4rem; }
|
|
97
|
+
h3 { margin: 0 0 0.6rem; font-size: 0.78rem; color: var(--text-2); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; }
|
|
98
|
+
|
|
99
|
+
.card {
|
|
100
|
+
background: var(--bg-1);
|
|
101
|
+
border: 1px solid var(--border);
|
|
102
|
+
border-radius: 10px;
|
|
103
|
+
padding: 1.25rem 1.4rem;
|
|
104
|
+
box-shadow: var(--shadow);
|
|
105
|
+
}
|
|
106
|
+
.card + .card { margin-top: 1rem; }
|
|
107
|
+
|
|
108
|
+
.grid-2 { display: grid; grid-template-columns: 2fr 1fr; gap: 1.2rem; }
|
|
109
|
+
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.2rem; }
|
|
110
|
+
.grid-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
|
|
111
|
+
|
|
112
|
+
@media (max-width: 800px) {
|
|
113
|
+
.grid-2, .grid-3 { grid-template-columns: 1fr; }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
select, input[type="text"], button {
|
|
117
|
+
background: var(--bg-2);
|
|
118
|
+
color: var(--text-0);
|
|
119
|
+
border: 1px solid var(--border);
|
|
120
|
+
border-radius: 6px;
|
|
121
|
+
padding: 0.45rem 0.8rem;
|
|
122
|
+
font-size: 0.88rem;
|
|
123
|
+
font-family: inherit;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
}
|
|
126
|
+
select:hover, button:hover { border-color: var(--accent); }
|
|
127
|
+
select:focus, input:focus, button:focus { outline: 2px solid var(--accent-dim); outline-offset: 1px; }
|
|
128
|
+
|
|
129
|
+
table { width: 100%; border-collapse: collapse; }
|
|
130
|
+
th, td {
|
|
131
|
+
text-align: left;
|
|
132
|
+
padding: 0.7rem 0.8rem;
|
|
133
|
+
border-bottom: 1px solid var(--border);
|
|
134
|
+
font-size: 0.88rem;
|
|
135
|
+
}
|
|
136
|
+
th {
|
|
137
|
+
color: var(--text-2);
|
|
138
|
+
font-weight: 500;
|
|
139
|
+
text-transform: uppercase;
|
|
140
|
+
font-size: 0.72rem;
|
|
141
|
+
letter-spacing: 0.06em;
|
|
142
|
+
background: var(--bg-1);
|
|
143
|
+
position: sticky;
|
|
144
|
+
top: 0;
|
|
145
|
+
cursor: pointer;
|
|
146
|
+
user-select: none;
|
|
147
|
+
}
|
|
148
|
+
th:hover { color: var(--text-0); }
|
|
149
|
+
th.sorted::after { content: " ↓"; color: var(--accent); }
|
|
150
|
+
th.sorted-asc::after { content: " ↑"; color: var(--accent); }
|
|
151
|
+
tbody tr:hover { background: var(--bg-2); cursor: pointer; }
|
|
152
|
+
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
153
|
+
td.mono { font-family: 'SF Mono', Consolas, monospace; font-size: 0.82rem; color: var(--text-1); }
|
|
154
|
+
td.tok { color: var(--accent); font-weight: 600; }
|
|
155
|
+
td.cost { color: var(--text-1); font-weight: 400; }
|
|
156
|
+
th .est { color: var(--text-2); font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 0.7rem; }
|
|
157
|
+
|
|
158
|
+
.kpi {
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
gap: 0.3rem;
|
|
162
|
+
}
|
|
163
|
+
.kpi-label { font-size: 0.72rem; color: var(--text-2); text-transform: uppercase; letter-spacing: 0.06em; }
|
|
164
|
+
.kpi-value { font-size: 2rem; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
165
|
+
.kpi-sub { font-size: 0.82rem; color: var(--text-1); }
|
|
166
|
+
.kpi-value.tokens { color: var(--accent); }
|
|
167
|
+
.kpi-value.cost { color: var(--text-1); font-weight: 500; }
|
|
168
|
+
|
|
169
|
+
.hero {
|
|
170
|
+
text-align: center;
|
|
171
|
+
padding: 2rem 1rem 1.5rem;
|
|
172
|
+
}
|
|
173
|
+
.hero-controls { margin-bottom: 1.5rem; display: flex; justify-content: center; gap: 0.6rem; align-items: center; }
|
|
174
|
+
.hero-box {
|
|
175
|
+
display: inline-block;
|
|
176
|
+
padding: 1.5rem 2.4rem 1.1rem;
|
|
177
|
+
background: var(--bg-1);
|
|
178
|
+
border: 1px solid var(--border);
|
|
179
|
+
border-radius: 12px;
|
|
180
|
+
box-shadow: var(--shadow);
|
|
181
|
+
margin: 0.4rem 0 0.5rem;
|
|
182
|
+
position: relative;
|
|
183
|
+
}
|
|
184
|
+
.hero-box::before {
|
|
185
|
+
content: "";
|
|
186
|
+
position: absolute;
|
|
187
|
+
inset: 0;
|
|
188
|
+
border-radius: 12px;
|
|
189
|
+
background: radial-gradient(ellipse at top, rgba(240, 136, 62, 0.07), transparent 70%);
|
|
190
|
+
pointer-events: none;
|
|
191
|
+
}
|
|
192
|
+
.hero-tokens { font-size: 3.5rem; font-weight: 700; line-height: 1; color: var(--accent); margin: 0; font-variant-numeric: tabular-nums; position: relative; }
|
|
193
|
+
.hero-tokens-label { color: var(--text-2); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.16em; margin-top: 0.45rem; position: relative; }
|
|
194
|
+
.hero-sub { color: var(--text-1); font-size: 1rem; margin-top: 1.2rem; }
|
|
195
|
+
.hero-sub strong { color: var(--text-0); font-weight: 600; }
|
|
196
|
+
.hero-sub .est-note { color: var(--text-2); font-size: 0.85rem; }
|
|
197
|
+
|
|
198
|
+
.chart { width: 100%; min-height: 260px; }
|
|
199
|
+
.chart-sm { width: 100%; min-height: 180px; }
|
|
200
|
+
.chart-tall { width: 100%; min-height: 360px; }
|
|
201
|
+
|
|
202
|
+
.empty {
|
|
203
|
+
color: var(--text-2);
|
|
204
|
+
text-align: center;
|
|
205
|
+
padding: 2.5rem 1rem;
|
|
206
|
+
font-style: italic;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.tag {
|
|
210
|
+
display: inline-block;
|
|
211
|
+
padding: 0.15em 0.5em;
|
|
212
|
+
border-radius: 4px;
|
|
213
|
+
font-size: 0.75rem;
|
|
214
|
+
font-weight: 500;
|
|
215
|
+
letter-spacing: 0.02em;
|
|
216
|
+
}
|
|
217
|
+
.tag-claude { background: rgba(240, 136, 62, 0.18); color: var(--claude); }
|
|
218
|
+
.tag-codex { background: rgba(88, 166, 255, 0.18); color: var(--codex); }
|
|
219
|
+
|
|
220
|
+
.disclaimer {
|
|
221
|
+
font-size: 0.82rem;
|
|
222
|
+
color: var(--text-2);
|
|
223
|
+
background: var(--bg-1);
|
|
224
|
+
border-left: 3px solid var(--warn);
|
|
225
|
+
padding: 0.8rem 1rem;
|
|
226
|
+
border-radius: 0 6px 6px 0;
|
|
227
|
+
margin-top: 1.5rem;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.status-pill {
|
|
231
|
+
display: inline-flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
gap: 0.4rem;
|
|
234
|
+
padding: 0.2em 0.6em;
|
|
235
|
+
border-radius: 999px;
|
|
236
|
+
font-size: 0.75rem;
|
|
237
|
+
background: var(--bg-2);
|
|
238
|
+
color: var(--text-1);
|
|
239
|
+
}
|
|
240
|
+
.status-pill::before {
|
|
241
|
+
content: "";
|
|
242
|
+
display: block;
|
|
243
|
+
width: 6px;
|
|
244
|
+
height: 6px;
|
|
245
|
+
border-radius: 50%;
|
|
246
|
+
background: var(--good);
|
|
247
|
+
}
|
|
248
|
+
.status-pill.busy::before { background: var(--warn); animation: pulse 1.5s ease-in-out infinite; }
|
|
249
|
+
|
|
250
|
+
@keyframes pulse {
|
|
251
|
+
0%, 100% { opacity: 1; }
|
|
252
|
+
50% { opacity: 0.4; }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Skeleton loading */
|
|
256
|
+
.skel {
|
|
257
|
+
background: linear-gradient(90deg, var(--bg-1) 25%, var(--bg-2) 50%, var(--bg-1) 75%);
|
|
258
|
+
background-size: 200% 100%;
|
|
259
|
+
animation: skel 1.4s ease-in-out infinite;
|
|
260
|
+
border-radius: 4px;
|
|
261
|
+
display: inline-block;
|
|
262
|
+
height: 1em;
|
|
263
|
+
width: 4em;
|
|
264
|
+
}
|
|
265
|
+
@keyframes skel {
|
|
266
|
+
0% { background-position: 200% 0; }
|
|
267
|
+
100% { background-position: -200% 0; }
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* Alerts card */
|
|
271
|
+
.alerts {
|
|
272
|
+
list-style: none;
|
|
273
|
+
padding: 0;
|
|
274
|
+
margin: 0;
|
|
275
|
+
display: grid;
|
|
276
|
+
gap: 0.6rem;
|
|
277
|
+
}
|
|
278
|
+
.alert {
|
|
279
|
+
padding: 0.6rem 0.8rem;
|
|
280
|
+
border-left: 3px solid var(--text-2);
|
|
281
|
+
background: var(--bg-2);
|
|
282
|
+
border-radius: 4px;
|
|
283
|
+
}
|
|
284
|
+
.alert-head {
|
|
285
|
+
display: flex;
|
|
286
|
+
justify-content: space-between;
|
|
287
|
+
align-items: baseline;
|
|
288
|
+
gap: 0.5rem;
|
|
289
|
+
}
|
|
290
|
+
.alert p {
|
|
291
|
+
margin: 0.25rem 0;
|
|
292
|
+
color: var(--text-1);
|
|
293
|
+
font-size: 0.9rem;
|
|
294
|
+
}
|
|
295
|
+
.alert time {
|
|
296
|
+
color: var(--text-2);
|
|
297
|
+
font-size: 0.75rem;
|
|
298
|
+
}
|
|
299
|
+
.alert-severity {
|
|
300
|
+
font-size: 0.72rem;
|
|
301
|
+
text-transform: uppercase;
|
|
302
|
+
letter-spacing: 0.06em;
|
|
303
|
+
color: var(--text-2);
|
|
304
|
+
}
|
|
305
|
+
.alert-info { border-left-color: var(--codex); }
|
|
306
|
+
.alert-warning { border-left-color: var(--warn); }
|
|
307
|
+
.alert-critical { border-left-color: var(--bad); }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Tools</title><link rel="stylesheet" href="/styles/global.css"></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class>Overview</a> <a href="/sessions" class>Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <h2>Tool usage</h2> <div class="card" style="margin-bottom:1rem;display:flex;gap:0.6rem;align-items:center;"> <label style="color:var(--text-2);font-size:0.8rem;">Window</label> <select id="w"> <option value="today">Today</option> <option value="7d" selected>Last 7 days</option> <option value="30d">Last 30 days</option> <option value="all">All time</option> </select> </div> <div class="grid-cards" id="kpis"></div> <div class="card" style="margin-top:1.2rem;"> <h3>Tool leaderboard</h3> <div id="leaderboard" class="chart-tall"></div> </div> <div class="grid-2" style="margin-top:1.2rem;" id="splits"> <div class="card"> <h3>MCP servers</h3> <div id="mcp" class="chart"></div> </div> <div class="card"> <h3>Sub-agent invocations</h3> <div id="subagents" class="chart"></div> </div> </div> <div class="card" style="margin-top:1.2rem;"> <h3>Per-tool details</h3> <table id="tools-table"> <thead> <tr> <th>Tool</th> <th class="num">Calls</th> <th class="num">Errors</th> <th class="num">Error rate</th> </tr> </thead> <tbody></tbody> </table> </div> <script type="module" src="/_astro/tools.astro_astro_type_script_index_0_lang.Dzzau3Yt.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Argus — Trends</title><link rel="stylesheet" href="/styles/global.css"></head> <body> <header class="app-header"> <a href="/" class="brand">ARGUS<span class="dot">●</span></a> <nav class="app-nav"> <a href="/" class>Overview</a> <a href="/sessions" class>Sessions</a> <a href="/tools" class>Tools</a> <a href="/prompts" class>Prompts</a> <a href="/trends" class>Trends</a> <a href="/models" class>Models</a> <a href="/settings" class>Settings</a> </nav> </header> <main> <h2>Trends</h2> <div class="card" style="margin-bottom:1rem;display:flex;gap:0.8rem;flex-wrap:wrap;align-items:center;"> <label style="color:var(--text-2);font-size:0.8rem;">Granularity</label> <select id="g"><option>day</option><option>week</option><option selected>month</option></select> <label style="color:var(--text-2);font-size:0.8rem;">Group by</label> <select id="b"><option selected>model</option></select> </div> <div class="card"> <div id="line" class="chart-tall"></div> </div> <div class="card" style="margin-top:1rem;overflow:auto;max-height:60vh;"> <h3>Breakdown</h3> <div id="breakdown"></div> </div> <script type="module" src="/_astro/trends.astro_astro_type_script_index_0_lang.BLLeGRNa.js"></script> </main> <footer class="app-footer" id="footer"> <span class="status-pill" id="ingest-status">Loading…</span> <span style="margin: 0 0.8rem;">·</span> <span id="pricing-info">Pricing —</span> </footer> <script type="module">async function i(){try{const[n,e]=await Promise.all([fetch("/api/pricing").then(s=>s.json()),fetch("/api/ingest/status").then(s=>s.json())]),o=document.getElementById("pricing-info");o&&(o.innerHTML=`Costs estimated from pricing table v<code>${n.version}</code> — tokens are exact; <code>argus pricing refresh</code> to update`);const t=document.getElementById("ingest-status");if(t)if(e.foregroundComplete&&e.pending===0){const s=typeof e.sessionCount=="number"?e.sessionCount:e.processed;t.textContent=`Tracking ${s} sessions`,t.classList.remove("busy")}else e.foregroundComplete?(t.textContent=`Backfilling history... ${e.processed}/${e.total}`,t.classList.add("busy")):(t.textContent=`Ingesting recent... ${e.processed}/${e.total}`,t.classList.add("busy"))}catch{}}i();setInterval(i,4e3);</script> </body> </html>
|