feat: analytics parity — tool cost, date ranges, budgets, query explorer, replay, snapshots (v2.4.0)

Analytics parity phases 2–7 (docs/plans/analytics-parity/), built on
138e706 and ported onto v2.3.1:
- Cost by AI tool (?tool= on cost/analytics/stats/workflows, by_tool,
  Codex fast/priority and flex tiers, agenthub_cost_usd_total{tool,model})
- Date ranges (from/to) on cost/analytics/workflows; Analytics range select
  with Today / This week (Monday) / This month (1st) / 7d / 30d / 90d / All /
  Custom; workflow filters carried into workflow-stats.js (read worker)
- Budgets and cost alerts as alert rule types (alert_rules rebuilt without
  its rule_type CHECK), GET /api/budgets/status, rule preview, spend pace,
  status-bar budget chip; rules stay editable in place
- Query Explorer (/query, allowlisted sort, CSV/JSON export) and scheduled
  reports (report_definitions/report_runs, Settings → Reports)
- Session Replay tab and immutable snapshots (session_snapshots, /snapshot/:token)
- Remote Codex sync, push ingest with a hashed token, privacy redaction and
  ignored folders (Settings → Data), Cursor sessions (no cost)
- Read-only MCP server, statusline, Grafana/Prometheus files

Also: resizable sidebar, pet run/wave animations, the workspace home keeps a
half-typed prompt, and the pre-commit hook runs only the related tests for a
small commit.
This commit is contained in:
2026-10-06 17:12:28 +07:00
parent 837da6e5de
commit 4aae61297f
146 changed files with 13565 additions and 537 deletions
+33 -5
View File
@@ -7,20 +7,26 @@
"event_pattern": "Event pattern",
"inactivity": "Inactivity",
"status_duration": "Stuck agent",
"token_threshold": "Token threshold"
"token_threshold": "Token threshold",
"cost_threshold": "Cost threshold",
"budget": "Spend budget"
},
"ruleTypeHints": {
"event_pattern": "Fires when an ingested event matches the pattern. Set a count above 1 to require N matching events within a time window (e.g. more than 5 errors in 2 minutes).",
"inactivity": "Fires when an active session has received no events for the configured number of minutes.",
"status_duration": "Fires when an agent has remained in the selected status with no activity for the configured number of minutes.",
"token_threshold": "Fires when a session's total token usage (input + output + cache) exceeds the threshold."
"token_threshold": "Fires when a session's total token usage (input + output + cache) exceeds the threshold.",
"cost_threshold": "Fires when a session's cost reaches the amount, priced like the session's own cost.",
"budget": "Fires once per period at each threshold of a spend limit — per day, week or month in your local time, optionally for one AI tool."
},
"ruleDesc": {
"eventPattern": "Matches {{pattern}}",
"eventPatternCount": "≥{{count}} × {{pattern}} within {{window}} min",
"inactivity": "No session activity for {{minutes}} min",
"statusDuration": "Agent stuck in {{status}} for {{minutes}} min",
"tokenThreshold": "Session tokens exceed {{tokens}}"
"tokenThreshold": "Session tokens exceed {{tokens}}",
"costThreshold": "Session cost reaches {{usd}}",
"budget": "{{limit}} per {{period}} · alerts at {{thresholds}}"
},
"rules": {
"title": "Alert rules",
@@ -48,7 +54,13 @@
"agentStatus": "Agent status",
"minutes": "Minutes",
"totalTokens": "Total tokens",
"cooldown": "Cooldown (seconds)"
"cooldown": "Cooldown (seconds)",
"usd": "Cost (USD)",
"period": "Period",
"limitUsd": "Limit (USD)",
"thresholds": "Alert at (%)",
"budgetTool": "AI tool",
"allTools": "All AI tools"
},
"deleteTitle": "Delete alert rule?",
"help": {
@@ -63,8 +75,19 @@
"minutesInactivity": "Fire when an active session has had no events for this many minutes (a stalled or forgotten session).",
"minutesStatus": "Fire when an agent stays in the selected status with no activity for this many minutes (a stuck agent).",
"totalTokens": "Fire when a session's cumulative tokens (input + output + cache read + cache write) cross this number. e.g. 1000000 = 1M tokens.",
"cooldown": "Minimum seconds between repeat alerts for the same rule + session + agent. Prevents noisy duplicates. Default 300 (5 min); 0 = no cooldown."
"cooldown": "Minimum seconds between repeat alerts for the same rule + session + agent. Prevents noisy duplicates. Default 300 (5 min); 0 = no cooldown.",
"usd": "A session reaching this many US dollars fires the alert.",
"period": "Day starts at your local midnight, week on Monday, month on the 1st.",
"limitUsd": "The spend limit for one period, in US dollars.",
"thresholds": "Percents of the limit, comma-separated, e.g. 50, 80, 100. Each fires once per period.",
"budgetTool": "Count only this AI tool's sessions."
},
"preview": "Preview",
"previewing": "Checking…",
"previewHint": "What this rule would have fired over the last 7 days — nothing is saved or sent",
"previewResult": "Would have fired {{fires}} times in {{sessions}} sessions over the last {{days}} days.",
"previewBudget": "Spend per period (this one and the five before it):",
"previewUnsupported": "This rule type cannot be replayed from history.",
"edit": "Edit rule",
"save": "Save rule",
"typeFixed": "A rule's type cannot be changed — create a new rule instead.",
@@ -81,6 +104,11 @@
"emptyDescription": "Alerts fired by your rules will appear here in real time.",
"emptyUnacked": "Everything is acknowledged. New alerts will appear here as they fire."
},
"periods": {
"day": "day",
"week": "week",
"month": "month"
},
"errors": {
"loadRules": "Could not load alert rules: {{error}}",
"loadAlerts": "Could not load alerts: {{error}}",
+55
View File
@@ -51,6 +51,61 @@
"last7Days": "Last 7 days",
"less": "Less",
"more": "More",
"toolFilter": "AI tool",
"allTools": "All AI tools",
"costByTool": "Cost by AI tool",
"spendPace": "Spend pace",
"burnRate": "Burn rate (7-day avg / day)",
"burnRateHint": "Average cost per day over the last 7 days, today included",
"monthToDate": "Month to date",
"forecastMonth": "Forecast for this month",
"forecastHint": "Month to date plus the 7-day pace for each day left",
"budgets": "Budgets",
"noBudgets": "No spend budget yet.",
"addBudget": "Add one in Settings → Notifications",
"projected": "projected",
"projectedHint": "This period's spend at the current pace, carried to its end",
"range": "Date range",
"rangePreset": {
"today": "Today",
"week": "This week",
"month": "This month",
"7d": "Last 7 days",
"30d": "Last 30 days",
"90d": "Last 90 days",
"all": "All time",
"custom": "Custom"
},
"rangeFrom": "From",
"rangeTo": "To",
"query": {
"title": "Query Explorer",
"subtitle": "Filter sessions, events and agents, then export what you see.",
"entity": "Data",
"entities": {
"sessions": "Sessions",
"events": "Events",
"agents": "Agents"
},
"search": "Search",
"searchPlaceholder": "Search text…",
"status": "Status",
"allStatuses": "All statuses",
"cwd": "Folder",
"cwdPlaceholder": "/path/to/project",
"from": "From",
"to": "To",
"sortBy": "Sort by {{column}}",
"exportCsv": "Export CSV",
"exportJson": "Export JSON",
"exportNote": "An export holds the whole filter, up to {{cap}} rows.",
"exportTruncated": "{{total}} rows match; the export holds the first {{cap}}.",
"rows": "{{from}}–{{to}} of {{total}}",
"prev": "Previous page",
"next": "Next page",
"empty": "Nothing matches these filters.",
"reset": "Reset filters"
},
"tokensExCacheReads": "Tokens (excl. cache reads)",
"cacheReadsSub": "+{{reads}} cache reads · {{pct}}% hit rate",
"avgPerWeekday30d": "Average per weekday, last 30 days"
+8 -1
View File
@@ -67,6 +67,8 @@
"today": "Today {{cost}}",
"workspace": "Workspace {{cost}}",
"session": "Session {{cost}}",
"budget": "Budget {{pct}}%",
"budgetTitle": "{{name}}: {{spend}} of {{limit}} this period",
"tokens": "({{tokens}} Tokens)",
"quotaUsed": "{{pct}}% used",
"quotaTitle": "Usage",
@@ -80,10 +82,15 @@
"quotaProfileExpired": "Sign-in expired: use Check quota in Claude Code → Profiles to refresh it",
"quotaSignInExpires": "Sign-in expires in {{time}}",
"quotaSignInExpired": "Sign-in expired",
"quotaWindow": { "wk": "Week", "mo": "Month" }
"quotaWindow": {
"wk": "Week",
"mo": "Month"
}
},
"version": "Version",
"query": "Query Explorer",
"openMenu": "Open menu",
"resizeSidebar": "Resize sidebar (double-click to reset)",
"closeMenu": "Close menu",
"sidebar": "Sidebar",
"now": "now"
+49
View File
@@ -54,6 +54,55 @@
"copyId": "Copy session ID",
"copyFolder": "Copy folder path"
},
"replay": {
"tab": "Replay",
"loading": "Loading events…",
"label": "Session replay",
"truncated": "Replaying the newest {{shown}} of {{total}} events.",
"start": "Jump to start (Home)",
"back": "Step back (←)",
"play": "Play (Space)",
"pause": "Pause (Space)",
"forward": "Step forward (→)",
"end": "Jump to end (End)",
"speed": "Playback speed",
"position": "{{at}} / {{n}}",
"scrubber": "Event position",
"minimap": "Lifecycle markers",
"marker": {
"SessionStart": "Session start",
"UserPromptSubmit": "Prompt",
"Compaction": "Compaction",
"SubagentStop": "Subagent done",
"APIError": "API error",
"Stop": "Turn end"
},
"state": {
"status": "Status",
"tool": "Running tool",
"prompts": "Prompts",
"errors": "Errors",
"compactions": "Compactions",
"elapsed": "Elapsed",
"toolCounts": "Tool calls so far"
}
},
"snapshot": {
"create": "Snapshot",
"hint": "Freeze this session into a read-only copy with a share link",
"title": "Create a snapshot",
"body": "Freezes this session as it is now — its agents, events, stats and cost — into a read-only copy under a private link. Later activity does not change it, and it survives Clear Data.",
"redactPaths": "Redact file paths (working directory, transcript and file paths)",
"confirm": "Create and copy link",
"cancel": "Cancel",
"open": "Open snapshot",
"notFound": "Snapshot not found",
"banner": "Snapshot taken {{date}}",
"bannerBody": "A read-only copy of the session as it was then. Nothing here updates.",
"redacted": "Paths redacted",
"openLive": "Open live session",
"timeline": "Timeline ({{n}})"
},
"exportCsv": "CSV",
"exportJson": "JSON",
"exportCapped": "The file holds {{shown}} of {{total}} sessions (the export limit) — narrow the filters to export the rest.",
+86
View File
@@ -561,6 +561,11 @@
"deleteAndPurge": "Remove + delete data",
"removeKeep": "Remove (keep data)",
"remoteSourceBadgeTitle": "Collected from a remote machine",
"fieldSyncCodex": "Also pull Codex sessions",
"fieldRemoteCodexHome": "Remote Codex home (optional)",
"fieldRemoteCodexHomeHint": "Default ~/.codex (wsl:~/.codex when the Claude home is in WSL). Its sessions/ folder is copied, and the sessions show up as Codex sessions of this source, with their tokens and cost.",
"codexOn": "+ Codex",
"syncCodex": "{{n}} Codex",
"fieldLabelPlaceholder": "Dev box"
},
"cursorPathsNote": "Informational: Cursor sessions count here too — Cursor happens to use the same ~/.claude paths as Claude Code (locally and on synced remotes).",
@@ -802,6 +807,14 @@
"about": {
"title": "About",
"description": "Server version, uptime and runtime."
},
"snapshots": {
"title": "Snapshots",
"description": "Frozen, read-only copies of sessions, each behind its own link."
},
"reports": {
"title": "Reports",
"description": "Analytics summaries written on a schedule, as HTML or JSON."
}
},
"backPrevious": "Back",
@@ -839,6 +852,79 @@
"enable": "Enable",
"disable": "Disable"
},
"snapshots": {
"title": "Session snapshots",
"description": "Created from a session's page with Snapshot. They survive Clear Data; delete them here.",
"empty": "No snapshots yet.",
"events": "{{n}} events",
"redacted": "Paths redacted",
"open": "Open",
"copyLink": "Copy link",
"delete": "Delete",
"deleteTitle": "Delete this snapshot?",
"deleteBody": "Its link stops working. The session itself is not touched.",
"cancel": "Cancel"
},
"pushIngest": {
"title": "Push ingest",
"description": "For a machine this dashboard cannot reach over SSH: it pushes its own hooks to POST /api/hooks/ingest-batch with a token made here. Its sessions are tagged with the name it sends (push:<name>) and show live status and events; their token cost needs the transcripts, which only an SSH source brings.",
"on": "On — a token exists",
"off": "Off — no token yet",
"create": "Create token",
"rotate": "Rotate token",
"revoke": "Revoke",
"rotateConfirm": "Make a new token? The current one stops working at once.",
"revokeConfirm": "Revoke the token and turn push ingest off?",
"shownOnce": "Copy this token now: only its hash is stored, so it is never shown again.",
"copy": "Copy",
"envOwned": "AGENTHUB_PUSH_TOKEN_SHA256 is set by the environment the dashboard started in; change it there.",
"reachHint": "The other machine must reach this dashboard: start it with DASHBOARD_HOST (and DASHBOARD_ALLOWED_HOSTS for its name). Over plain http the token crosses the network readable — use it only on a network you trust, or behind TLS. The forwarder below keeps the token in a header file, never on a command line:",
"limits": "At most 500 events and 1 MB per request. A hook for a session another source owns is refused; transcript paths are dropped (they name files on the sender). Codex hooks are taken; Antigravity hooks are not."
},
"privacy": {
"title": "Privacy",
"description": "What AgentHub keeps from hooks and imported history. Both are off by default and apply to new data only.",
"redact": "Redact secrets before storing",
"redactHint": "API keys and tokens of known shapes, bearer headers, private keys and password=/TOKEN= values in event payloads and summaries become [redacted]. The transcripts on disk are the CLIs' own files and are not changed.",
"ignore": "Ignored folders",
"ignoreHint": "One absolute path per line. Hooks from these folders, and anything under them, are dropped whole; history import and the Codex reader skip their sessions. AGENTHUB_IGNORE_CWD sets the same list.",
"envOwned": "A value here is set by the environment the dashboard started in, and cannot be changed from this page."
},
"reports": {
"title": "Scheduled reports",
"description": "A summary of the last days — sessions, events, tokens, cost by model and by AI tool, top tools, budgets — written as HTML or JSON on a schedule. Reports and their history survive Clear Data.",
"add": "New report",
"defaultName": "Weekly summary",
"empty": "No scheduled report yet.",
"name": "Name",
"windowDays": "Days covered",
"frequency": "Frequency",
"daily": "Daily",
"weekly": "Weekly",
"weekday": "Day",
"hour": "Time",
"windowHint": "A report covers the full days before the day it runs, at this browser's time zone.",
"save": "Save",
"cancel": "Cancel",
"enabled": "Enabled",
"runNow": "Run now",
"history": "History",
"edit": "Edit",
"delete": "Delete",
"everyDay": "Every day at {{time}}",
"everyWeek": "Every {{day}} at {{time}}",
"window_one": "last {{count}} day",
"window_other": "last {{count}} days",
"lastRun": "Last run {{at}}",
"noRuns": "No runs yet.",
"trigger": {
"schedule": "Scheduled",
"manual": "Manual"
},
"download": "Download {{format}}",
"deleteTitle": "Delete this report?",
"deleteBody": "Its schedule, its run history and the files it wrote are deleted."
},
"prefsBackup": {
"title": "This browser's preferences",
"description": "Theme, language, notification settings, pet, sidebar and workspace layout are kept in this browser. Save them to a file to move them to another browser, or restore them from one.",
+33 -5
View File
@@ -7,20 +7,26 @@
"event_pattern": "Mẫu sự kiện",
"inactivity": "Không hoạt động",
"status_duration": "Agent bị kẹt",
"token_threshold": "Ngưỡng token"
"token_threshold": "Ngưỡng token",
"cost_threshold": "Ngưỡng chi phí",
"budget": "Ngân sách chi tiêu"
},
"ruleTypeHints": {
"event_pattern": "Kích hoạt khi một sự kiện khớp với mẫu. Đặt số lần lớn hơn 1 để yêu cầu N sự kiện khớp trong khung thời gian (ví dụ: hơn 5 lỗi trong 2 phút).",
"inactivity": "Kích hoạt khi một phiên đang hoạt động không nhận được sự kiện nào trong số phút đã cấu hình.",
"status_duration": "Kích hoạt khi một agent giữ nguyên trạng thái đã chọn mà không có hoạt động trong số phút đã cấu hình.",
"token_threshold": "Kích hoạt khi tổng mức dùng token của phiên (đầu vào + đầu ra + bộ nhớ đệm) vượt ngưỡng."
"token_threshold": "Kích hoạt khi tổng mức dùng token của phiên (đầu vào + đầu ra + bộ nhớ đệm) vượt ngưỡng.",
"cost_threshold": "Kích hoạt khi chi phí của một phiên chạm mức này, tính giá như chi phí riêng của phiên.",
"budget": "Kích hoạt một lần mỗi kỳ ở từng ngưỡng của hạn mức chi tiêu — theo ngày, tuần hoặc tháng theo giờ địa phương, có thể chỉ cho một công cụ AI."
},
"ruleDesc": {
"eventPattern": "Khớp {{pattern}}",
"eventPatternCount": "≥{{count}} × {{pattern}} trong {{window}} phút",
"inactivity": "Phiên không hoạt động trong {{minutes}} phút",
"statusDuration": "Agent kẹt ở trạng thái {{status}} trong {{minutes}} phút",
"tokenThreshold": "Token của phiên vượt {{tokens}}"
"tokenThreshold": "Token của phiên vượt {{tokens}}",
"costThreshold": "Chi phí phiên chạm {{usd}}",
"budget": "{{limit}} mỗi {{period}} · cảnh báo ở {{thresholds}}"
},
"rules": {
"title": "Quy tắc cảnh báo",
@@ -48,7 +54,13 @@
"agentStatus": "Trạng thái agent",
"minutes": "Phút",
"totalTokens": "Tổng token",
"cooldown": "Thời gian chờ (giây)"
"cooldown": "Thời gian chờ (giây)",
"usd": "Chi phí (USD)",
"period": "Kỳ",
"limitUsd": "Hạn mức (USD)",
"thresholds": "Cảnh báo ở (%)",
"budgetTool": "Công cụ AI",
"allTools": "Mọi công cụ AI"
},
"deleteTitle": "Xóa quy tắc cảnh báo?",
"help": {
@@ -63,8 +75,19 @@
"minutesInactivity": "Kích hoạt khi một phiên đang hoạt động không có sự kiện nào trong bấy nhiêu phút (phiên bị treo hoặc bị quên).",
"minutesStatus": "Kích hoạt khi agent ở trạng thái đã chọn mà không có hoạt động trong bấy nhiêu phút (agent bị kẹt).",
"totalTokens": "Kích hoạt khi tổng token của phiên (input + output + cache read + cache write) vượt số này. vd 1000000 = 1 triệu token.",
"cooldown": "Số giây tối thiểu giữa các cảnh báo lặp cho cùng quy tắc + phiên + agent. Tránh trùng lặp gây nhiễu. Mặc định 300 (5 phút); 0 = không có thời gian chờ."
"cooldown": "Số giây tối thiểu giữa các cảnh báo lặp cho cùng quy tắc + phiên + agent. Tránh trùng lặp gây nhiễu. Mặc định 300 (5 phút); 0 = không có thời gian chờ.",
"usd": "Một phiên chạm số đô la này sẽ kích hoạt cảnh báo.",
"period": "Ngày bắt đầu lúc nửa đêm giờ địa phương, tuần từ thứ Hai, tháng từ ngày 1.",
"limitUsd": "Hạn mức chi tiêu cho một kỳ, tính bằng đô la Mỹ.",
"thresholds": "Phần trăm hạn mức, cách nhau bằng dấu phẩy, vd 50, 80, 100. Mỗi ngưỡng kích hoạt một lần mỗi kỳ.",
"budgetTool": "Chỉ tính các phiên của công cụ AI này."
},
"preview": "Xem trước",
"previewing": "Đang kiểm tra…",
"previewHint": "Quy tắc này lẽ ra đã kích hoạt những gì trong 7 ngày qua — không lưu, không gửi gì",
"previewResult": "Lẽ ra đã kích hoạt {{fires}} lần ở {{sessions}} phiên trong {{days}} ngày qua.",
"previewBudget": "Chi tiêu mỗi kỳ (kỳ này và năm kỳ trước):",
"previewUnsupported": "Loại quy tắc này không phát lại được từ lịch sử.",
"edit": "Sửa quy tắc",
"save": "Lưu quy tắc",
"typeFixed": "Không đổi được loại của quy tắc — hãy tạo quy tắc mới.",
@@ -81,6 +104,11 @@
"emptyDescription": "Cảnh báo do các quy tắc của bạn kích hoạt sẽ hiển thị tại đây theo thời gian thực.",
"emptyUnacked": "Tất cả đã được xác nhận. Cảnh báo mới sẽ hiển thị tại đây khi được kích hoạt."
},
"periods": {
"day": "ngày",
"week": "tuần",
"month": "tháng"
},
"errors": {
"loadRules": "Không tải được quy tắc cảnh báo: {{error}}",
"loadAlerts": "Không tải được cảnh báo: {{error}}",
+55
View File
@@ -51,6 +51,61 @@
"last7Days": "7 ngày gần nhất",
"less": "Ít",
"more": "Nhiều",
"toolFilter": "Công cụ AI",
"allTools": "Mọi công cụ AI",
"costByTool": "Chi phí theo công cụ AI",
"spendPace": "Nhịp chi tiêu",
"burnRate": "Tốc độ đốt (TB 7 ngày / ngày)",
"burnRateHint": "Chi phí trung bình mỗi ngày trong 7 ngày qua, tính cả hôm nay",
"monthToDate": "Từ đầu tháng",
"forecastMonth": "Dự báo cả tháng",
"forecastHint": "Từ đầu tháng cộng nhịp 7 ngày cho mỗi ngày còn lại",
"budgets": "Ngân sách",
"noBudgets": "Chưa có ngân sách chi tiêu.",
"addBudget": "Thêm trong Cài đặt → Thông báo",
"projected": "dự kiến",
"projectedHint": "Chi tiêu kỳ này theo nhịp hiện tại, kéo đến cuối kỳ",
"range": "Khoảng thời gian",
"rangePreset": {
"today": "Hôm nay",
"week": "Tuần này",
"month": "Tháng này",
"7d": "7 ngày qua",
"30d": "30 ngày qua",
"90d": "90 ngày qua",
"all": "Toàn bộ",
"custom": "Tùy chọn"
},
"rangeFrom": "Từ ngày",
"rangeTo": "Đến ngày",
"query": {
"title": "Truy vấn dữ liệu",
"subtitle": "Lọc phiên, sự kiện và agent, rồi xuất đúng những gì đang thấy.",
"entity": "Dữ liệu",
"entities": {
"sessions": "Phiên",
"events": "Sự kiện",
"agents": "Agent"
},
"search": "Tìm kiếm",
"searchPlaceholder": "Tìm văn bản…",
"status": "Trạng thái",
"allStatuses": "Mọi trạng thái",
"cwd": "Thư mục",
"cwdPlaceholder": "/duong/dan/du-an",
"from": "Từ ngày",
"to": "Đến ngày",
"sortBy": "Sắp xếp theo {{column}}",
"exportCsv": "Xuất CSV",
"exportJson": "Xuất JSON",
"exportNote": "Tệp xuất chứa toàn bộ kết quả lọc, tối đa {{cap}} dòng.",
"exportTruncated": "{{total}} dòng khớp; tệp xuất chỉ chứa {{cap}} dòng đầu.",
"rows": "{{from}}–{{to}} / {{total}}",
"prev": "Trang trước",
"next": "Trang sau",
"empty": "Không có dữ liệu khớp bộ lọc.",
"reset": "Đặt lại bộ lọc"
},
"tokensExCacheReads": "Token (trừ đọc cache)",
"cacheReadsSub": "+{{reads}} token đọc cache · tỉ lệ trúng {{pct}}%",
"avgPerWeekday30d": "Trung bình theo thứ trong tuần, 30 ngày qua"
+8 -1
View File
@@ -67,6 +67,8 @@
"today": "Hôm nay {{cost}}",
"workspace": "Workspace {{cost}}",
"session": "Session {{cost}}",
"budget": "Ngân sách {{pct}}%",
"budgetTitle": "{{name}}: {{spend}} / {{limit}} kỳ này",
"tokens": "({{tokens}} Tokens)",
"quotaUsed": "đã dùng {{pct}}%",
"quotaTitle": "Mức dùng",
@@ -80,10 +82,15 @@
"quotaProfileExpired": "Đăng nhập đã hết hạn: bấm Kiểm tra quota trong Claude Code → Profiles để làm mới",
"quotaSignInExpires": "Đăng nhập hết hạn sau {{time}}",
"quotaSignInExpired": "Đăng nhập đã hết hạn",
"quotaWindow": { "wk": "Tuần", "mo": "Tháng" }
"quotaWindow": {
"wk": "Tuần",
"mo": "Tháng"
}
},
"version": "Phiên bản",
"query": "Truy vấn",
"openMenu": "Mở menu",
"resizeSidebar": "Kéo để đổi độ rộng sidebar (nhấp đúp để đặt lại)",
"closeMenu": "Đóng menu",
"sidebar": "Thanh bên",
"now": "bây giờ"
+49
View File
@@ -53,6 +53,55 @@
"copyId": "Sao chép ID session",
"copyFolder": "Sao chép đường dẫn thư mục"
},
"replay": {
"tab": "Phát lại",
"loading": "Đang tải sự kiện…",
"label": "Phát lại phiên",
"truncated": "Đang phát lại {{shown}} sự kiện mới nhất trong {{total}}.",
"start": "Về đầu (Home)",
"back": "Lùi một bước (←)",
"play": "Phát (Space)",
"pause": "Tạm dừng (Space)",
"forward": "Tiến một bước (→)",
"end": "Đến cuối (End)",
"speed": "Tốc độ phát",
"position": "{{at}} / {{n}}",
"scrubber": "Vị trí sự kiện",
"minimap": "Mốc vòng đời",
"marker": {
"SessionStart": "Bắt đầu phiên",
"UserPromptSubmit": "Prompt",
"Compaction": "Nén ngữ cảnh",
"SubagentStop": "Subagent xong",
"APIError": "Lỗi API",
"Stop": "Kết thúc lượt"
},
"state": {
"status": "Trạng thái",
"tool": "Công cụ đang chạy",
"prompts": "Prompt",
"errors": "Lỗi",
"compactions": "Lần nén",
"elapsed": "Đã trôi qua",
"toolCounts": "Số lần gọi công cụ đến lúc này"
}
},
"snapshot": {
"create": "Snapshot",
"hint": "Đóng băng phiên này thành một bản chỉ đọc kèm liên kết chia sẻ",
"title": "Tạo snapshot",
"body": "Đóng băng phiên như hiện tại — agent, sự kiện, thống kê và chi phí — thành một bản chỉ đọc dưới một liên kết riêng. Hoạt động sau đó không làm nó thay đổi, và nó vẫn còn sau khi Xóa dữ liệu.",
"redactPaths": "Che đường dẫn (thư mục làm việc, transcript và đường dẫn tệp)",
"confirm": "Tạo và sao chép liên kết",
"cancel": "Hủy",
"open": "Mở snapshot",
"notFound": "Không tìm thấy snapshot",
"banner": "Snapshot chụp lúc {{date}}",
"bannerBody": "Bản chỉ đọc của phiên tại thời điểm đó. Không có gì ở đây được cập nhật.",
"redacted": "Đã che đường dẫn",
"openLive": "Mở phiên đang có",
"timeline": "Dòng thời gian ({{n}})"
},
"exportCsv": "CSV",
"exportJson": "JSON",
"exportCapped": "Tệp chỉ gồm {{shown}} trên {{total}} phiên (giới hạn xuất) — hãy thu hẹp bộ lọc để xuất phần còn lại.",
+86
View File
@@ -561,6 +561,11 @@
"deleteAndPurge": "Xóa + xóa dữ liệu",
"removeKeep": "Xóa (giữ dữ liệu)",
"remoteSourceBadgeTitle": "Được thu thập từ một máy từ xa",
"fieldSyncCodex": "Kéo cả phiên Codex",
"fieldRemoteCodexHome": "Thư mục Codex trên máy từ xa (tùy chọn)",
"fieldRemoteCodexHomeHint": "Mặc định ~/.codex (wsl:~/.codex khi thư mục Claude nằm trong WSL). Thư mục sessions/ của nó được sao chép về, và các phiên hiện ra như phiên Codex của nguồn này, kèm token và chi phí.",
"codexOn": "+ Codex",
"syncCodex": "{{n}} Codex",
"fieldLabelPlaceholder": "Máy dev"
},
"cursorPathsNote": "Chỉ mang tính thông tin: phiên Cursor cũng được tính — Cursor tình cờ dùng cùng đường dẫn ~/.claude với Claude Code (cục bộ và trên máy từ xa đã đồng bộ).",
@@ -802,6 +807,14 @@
"about": {
"title": "Giới thiệu",
"description": "Phiên bản máy chủ, thời gian hoạt động và môi trường chạy."
},
"snapshots": {
"title": "Snapshot",
"description": "Các bản sao chỉ đọc, đã đóng băng của phiên, mỗi bản có liên kết riêng."
},
"reports": {
"title": "Báo cáo",
"description": "Bản tóm tắt phân tích ghi ra theo lịch, dạng HTML hoặc JSON."
}
},
"backPrevious": "Quay lại",
@@ -839,6 +852,79 @@
"enable": "Bật",
"disable": "Tắt"
},
"snapshots": {
"title": "Snapshot phiên",
"description": "Tạo từ trang của phiên bằng nút Snapshot. Chúng vẫn còn sau khi Xóa dữ liệu; xóa chúng ở đây.",
"empty": "Chưa có snapshot nào.",
"events": "{{n}} sự kiện",
"redacted": "Đã che đường dẫn",
"open": "Mở",
"copyLink": "Sao chép liên kết",
"delete": "Xóa",
"deleteTitle": "Xóa snapshot này?",
"deleteBody": "Liên kết của nó sẽ không dùng được nữa. Bản thân phiên không bị ảnh hưởng.",
"cancel": "Hủy"
},
"pushIngest": {
"title": "Đẩy dữ liệu về (push ingest)",
"description": "Cho máy mà dashboard không SSH tới được: máy đó tự đẩy hook của nó tới POST /api/hooks/ingest-batch bằng token tạo ở đây. Các phiên được gắn tên máy gửi lên (push:<tên>) và có trạng thái, sự kiện trực tiếp; chi phí token cần transcript, thứ chỉ nguồn SSH mang về.",
"on": "Đang bật — đã có token",
"off": "Đang tắt — chưa có token",
"create": "Tạo token",
"rotate": "Đổi token",
"revoke": "Thu hồi",
"rotateConfirm": "Tạo token mới? Token hiện tại ngừng hoạt động ngay.",
"revokeConfirm": "Thu hồi token và tắt push ingest?",
"shownOnce": "Hãy sao chép token này ngay: chỉ bản băm của nó được lưu, nên nó không bao giờ hiện lại.",
"copy": "Sao chép",
"envOwned": "AGENTHUB_PUSH_TOKEN_SHA256 được đặt bởi môi trường khởi động dashboard; hãy đổi nó ở đó.",
"reachHint": "Máy kia phải truy cập được dashboard này: khởi động với DASHBOARD_HOST (và DASHBOARD_ALLOWED_HOSTS cho tên của nó). Qua http thường, token đi trên mạng ở dạng đọc được — chỉ dùng trong mạng tin cậy, hoặc sau TLS. Script chuyển tiếp dưới đây giữ token trong một tệp header, không bao giờ trên dòng lệnh:",
"limits": "Tối đa 500 sự kiện và 1 MB mỗi yêu cầu. Hook cho phiên thuộc nguồn khác bị từ chối; đường dẫn transcript bị bỏ (chúng trỏ tới tệp trên máy gửi). Nhận hook Codex; không nhận hook Antigravity."
},
"privacy": {
"title": "Quyền riêng tư",
"description": "Những gì AgentHub giữ lại từ hook và lịch sử nhập vào. Cả hai mặc định tắt và chỉ áp dụng cho dữ liệu mới.",
"redact": "Che bí mật trước khi lưu",
"redactHint": "Khóa API và token có dạng đã biết, header bearer, khóa riêng và giá trị password=/TOKEN= trong payload và tóm tắt sự kiện được thay bằng [redacted]. Transcript trên đĩa là tệp của chính CLI và không bị thay đổi.",
"ignore": "Thư mục bỏ qua",
"ignoreHint": "Mỗi dòng một đường dẫn tuyệt đối. Hook từ các thư mục này, và mọi thứ bên trong, bị bỏ hoàn toàn; việc nhập lịch sử và bộ đọc Codex cũng bỏ qua phiên của chúng. AGENTHUB_IGNORE_CWD đặt cùng danh sách này.",
"envOwned": "Một giá trị ở đây do môi trường khởi động dashboard đặt, và không thể đổi từ trang này."
},
"reports": {
"title": "Báo cáo định kỳ",
"description": "Bản tóm tắt những ngày gần nhất — phiên, sự kiện, token, chi phí theo model và theo công cụ AI, công cụ dùng nhiều nhất, ngân sách — ghi ra HTML hoặc JSON theo lịch. Báo cáo và lịch sử chạy được giữ lại khi Xóa dữ liệu.",
"add": "Báo cáo mới",
"defaultName": "Tóm tắt hằng tuần",
"empty": "Chưa có báo cáo định kỳ nào.",
"name": "Tên",
"windowDays": "Số ngày",
"frequency": "Tần suất",
"daily": "Hằng ngày",
"weekly": "Hằng tuần",
"weekday": "Ngày",
"hour": "Giờ",
"windowHint": "Báo cáo gồm các ngày trọn vẹn trước ngày chạy, theo múi giờ của trình duyệt này.",
"save": "Lưu",
"cancel": "Hủy",
"enabled": "Bật",
"runNow": "Chạy ngay",
"history": "Lịch sử",
"edit": "Sửa",
"delete": "Xóa",
"everyDay": "Mỗi ngày lúc {{time}}",
"everyWeek": "Mỗi {{day}} lúc {{time}}",
"window_one": "{{count}} ngày gần nhất",
"window_other": "{{count}} ngày gần nhất",
"lastRun": "Lần chạy gần nhất {{at}}",
"noRuns": "Chưa chạy lần nào.",
"trigger": {
"schedule": "Theo lịch",
"manual": "Thủ công"
},
"download": "Tải {{format}}",
"deleteTitle": "Xóa báo cáo này?",
"deleteBody": "Lịch chạy, lịch sử chạy và các tệp nó đã ghi sẽ bị xóa."
},
"prefsBackup": {
"title": "Tùy chọn của trình duyệt này",
"description": "Giao diện, ngôn ngữ, cài đặt thông báo, thú cưng, thanh bên và bố cục không gian làm việc được lưu trong trình duyệt này. Lưu chúng ra tệp để chuyển sang trình duyệt khác, hoặc khôi phục từ tệp.",