diff --git a/ui/src/i18n/locales/en-US.ts b/ui/src/i18n/locales/en-US.ts
index 2ab24b4e4..33c076fe4 100644
--- a/ui/src/i18n/locales/en-US.ts
+++ b/ui/src/i18n/locales/en-US.ts
@@ -304,8 +304,8 @@ export const enUS = {
cron: {
title: "Cron Jobs",
desc: "Scheduled agent wakeups and recurring tasks.",
- noJobs: "No cron jobs configured.",
- addJob: "Add Job",
+ noJobs: "No jobs yet.",
+ addJob: "Add job",
runNow: "Run",
remove: "Remove",
enable: "Enable",
@@ -314,6 +314,57 @@ export const enUS = {
lastRun: "Last run",
nextRun: "Next run",
+ // Scheduler card
+ scheduler: "Scheduler",
+ schedulerDesc: "Gateway-owned cron scheduler status.",
+ jobs: "Jobs",
+
+ // New job form
+ newJob: "New Job",
+ newJobDesc: "Create a scheduled wakeup or agent run.",
+ name: "Name",
+ description: "Description",
+ agentId: "Agent ID",
+ agentIdPlaceholder: "default",
+ scheduleKind: "Schedule",
+ everyLabel: "Every",
+ atLabel: "At",
+ cronLabel: "Cron",
+ runAt: "Run at",
+ every: "Every",
+ unit: "Unit",
+ minutes: "Minutes",
+ hours: "Hours",
+ days: "Days",
+ expression: "Expression",
+ timezone: "Timezone (optional)",
+ session: "Session",
+ main: "Main",
+ isolated: "Isolated",
+ wakeMode: "Wake mode",
+ nextHeartbeat: "Next heartbeat",
+ now: "Now",
+ payload: "Payload",
+ systemEvent: "System event",
+ agentTurn: "Agent turn",
+ systemText: "System text",
+ agentMessage: "Agent message",
+ deliver: "Deliver",
+ to: "To",
+ toPlaceholder: "+1555… or chat id",
+ timeoutSeconds: "Timeout (seconds)",
+ postToMainPrefix: "Post to main prefix",
+
+ // Jobs list
+ jobsList: "Jobs",
+ jobsListDesc: "All scheduled jobs stored in the gateway.",
+
+ // Run history
+ runHistory: "Run history",
+ runHistoryDesc: "Latest runs for",
+ selectJob: "Select a job to inspect run history.",
+ noRuns: "No runs yet.",
+
form: {
schedule: "Schedule (cron)",
message: "Message",
@@ -336,8 +387,9 @@ export const enUS = {
desc: "Manage bundled and installed skills.",
noSkills: "No skills found.",
filter: "Filter skills",
- apiKey: "API Key",
- saveKey: "Save Key",
+ shown: "shown",
+ apiKey: "API key",
+ saveKey: "Save key",
install: "Install",
installing: "Installing…",
enabled: "Enabled",
@@ -345,6 +397,11 @@ export const enUS = {
toggle: "Toggle",
keySaved: "API key saved",
keyError: "Failed to save API key",
+ eligible: "eligible",
+ blocked: "blocked",
+ missing: "Missing",
+ reason: "Reason",
+ blockedByAllowlist: "blocked by allowlist",
},
// Nodes page
@@ -427,14 +484,29 @@ export const enUS = {
status: "Status",
health: "Health",
models: "Models",
+ modelsDesc: "Catalog from models.list.",
heartbeat: "Heartbeat",
+ lastHeartbeat: "Last heartbeat",
events: "Events",
+ eventLog: "Event Log",
+ eventLogDesc: "Latest gateway events.",
+ noEvents: "No events yet.",
rpcCall: "RPC Call",
+ manualRpc: "Manual RPC",
+ manualRpcDesc: "Send a raw gateway method with JSON params.",
method: "Method",
params: "Params",
+ paramsJson: "Params (JSON)",
call: "Call",
result: "Result",
noResult: "No result yet.",
+ snapshots: "Snapshots",
+ snapshotsDesc: "Status, health, and heartbeat data.",
+ securityAudit: "Security audit",
+ critical: "critical",
+ warnings: "warnings",
+ noCritical: "No critical issues",
+ runAuditCmd: "Run for details.",
},
// Logs page
diff --git a/ui/src/i18n/locales/zh-TW.ts b/ui/src/i18n/locales/zh-TW.ts
index f7800f1fb..5516263db 100644
--- a/ui/src/i18n/locales/zh-TW.ts
+++ b/ui/src/i18n/locales/zh-TW.ts
@@ -311,7 +311,7 @@ export const zhTW = {
cron: {
title: "排程任務",
desc: "排程代理喚醒與週期性任務。",
- noJobs: "尚未設定排程任務。",
+ noJobs: "尚無任務。",
addJob: "新增任務",
runNow: "立即執行",
remove: "移除",
@@ -321,6 +321,57 @@ export const zhTW = {
lastRun: "上次執行",
nextRun: "下次執行",
+ // 排程器卡片
+ scheduler: "排程器",
+ schedulerDesc: "閘道器所管理的排程器狀態。",
+ jobs: "任務數",
+
+ // 新任務表單
+ newJob: "新增任務",
+ newJobDesc: "建立排程喚醒或代理執行。",
+ name: "名稱",
+ description: "描述",
+ agentId: "代理 ID",
+ agentIdPlaceholder: "default",
+ scheduleKind: "排程類型",
+ everyLabel: "間隔",
+ atLabel: "指定時間",
+ cronLabel: "Cron",
+ runAt: "執行時間",
+ every: "每隔",
+ unit: "單位",
+ minutes: "分鐘",
+ hours: "小時",
+ days: "天",
+ expression: "運算式",
+ timezone: "時區(選填)",
+ session: "工作階段",
+ main: "主要",
+ isolated: "獨立",
+ wakeMode: "喚醒模式",
+ nextHeartbeat: "下次心跳",
+ now: "立即",
+ payload: "內容類型",
+ systemEvent: "系統事件",
+ agentTurn: "代理回合",
+ systemText: "系統文字",
+ agentMessage: "代理訊息",
+ deliver: "傳送",
+ to: "收件者",
+ toPlaceholder: "+1555… 或聊天 ID",
+ timeoutSeconds: "逾時(秒)",
+ postToMainPrefix: "發送至主工作階段前綴",
+
+ // 任務列表
+ jobsList: "任務",
+ jobsListDesc: "所有儲存在閘道器的排程任務。",
+
+ // 執行歷史
+ runHistory: "執行歷史",
+ runHistoryDesc: "最近的執行記錄:",
+ selectJob: "選擇任務以檢視執行歷史。",
+ noRuns: "尚無執行記錄。",
+
form: {
schedule: "排程(cron 格式)",
message: "訊息",
@@ -343,6 +394,7 @@ export const zhTW = {
desc: "管理內建與已安裝的技能。",
noSkills: "找不到技能。",
filter: "篩選技能",
+ shown: "個顯示中",
apiKey: "API 金鑰",
saveKey: "儲存金鑰",
install: "安裝",
@@ -352,6 +404,11 @@ export const zhTW = {
toggle: "切換",
keySaved: "API 金鑰已儲存",
keyError: "儲存 API 金鑰失敗",
+ eligible: "可用",
+ blocked: "已封鎖",
+ missing: "缺少",
+ reason: "原因",
+ blockedByAllowlist: "被許可清單封鎖",
},
// 節點頁面
@@ -434,14 +491,29 @@ export const zhTW = {
status: "狀態",
health: "健康狀態",
models: "模型",
+ modelsDesc: "來自 models.list 的目錄。",
heartbeat: "心跳",
+ lastHeartbeat: "上次心跳",
events: "事件",
+ eventLog: "事件日誌",
+ eventLogDesc: "最新的閘道器事件。",
+ noEvents: "尚無事件。",
rpcCall: "RPC 呼叫",
+ manualRpc: "手動 RPC",
+ manualRpcDesc: "使用 JSON 參數發送原始閘道器方法。",
method: "方法",
params: "參數",
+ paramsJson: "參數(JSON)",
call: "呼叫",
result: "結果",
noResult: "尚無結果。",
+ snapshots: "快照",
+ snapshotsDesc: "狀態、健康狀態與心跳資料。",
+ securityAudit: "安全稽核",
+ critical: "嚴重",
+ warnings: "警告",
+ noCritical: "無嚴重問題",
+ runAuditCmd: "執行以檢視詳情。",
},
// 日誌頁面
diff --git a/ui/src/ui/views/config.ts b/ui/src/ui/views/config.ts
index ff6f57f32..8b5c02409 100644
--- a/ui/src/ui/views/config.ts
+++ b/ui/src/ui/views/config.ts
@@ -1,4 +1,5 @@
import { html, nothing } from "lit";
+import { t } from "../../i18n";
import type { ConfigUiHints } from "../types";
import { analyzeConfigSchema, renderConfigForm, SECTION_META } from "./config-form";
import {
@@ -73,21 +74,15 @@ const sidebarIcons = {
default: html``,
};
-// Section definitions
-const SECTIONS: Array<{ key: string; label: string }> = [
- { key: "env", label: "Environment" },
- { key: "update", label: "Updates" },
- { key: "agents", label: "Agents" },
- { key: "auth", label: "Authentication" },
- { key: "channels", label: "Channels" },
- { key: "messages", label: "Messages" },
- { key: "commands", label: "Commands" },
- { key: "hooks", label: "Hooks" },
- { key: "skills", label: "Skills" },
- { key: "tools", label: "Tools" },
- { key: "gateway", label: "Gateway" },
- { key: "wizard", label: "Setup Wizard" },
-];
+// Section definitions - labels are resolved via t() at render time
+const SECTION_KEYS = [
+ "env", "update", "agents", "auth", "channels", "messages",
+ "commands", "hooks", "skills", "tools", "gateway", "wizard",
+] as const;
+
+function getSectionLabel(key: string): string {
+ return t(`config.sections.${key}`) || humanize(key);
+}
type SubsectionEntry = {
key: string;
@@ -191,13 +186,15 @@ export function renderConfig(props: ConfigProps) {
// Get available sections from schema
const schemaProps = analysis.schema?.properties ?? {};
- const availableSections = SECTIONS.filter(s => s.key in schemaProps);
+ const knownKeys = new Set(SECTION_KEYS);
+ const availableSections = SECTION_KEYS
+ .filter(k => k in schemaProps)
+ .map(k => ({ key: k, label: getSectionLabel(k) }));
// Add any sections in schema but not in our list
- const knownKeys = new Set(SECTIONS.map(s => s.key));
const extraSections = Object.keys(schemaProps)
.filter(k => !knownKeys.has(k))
- .map(k => ({ key: k, label: k.charAt(0).toUpperCase() + k.slice(1) }));
+ .map(k => ({ key: k, label: getSectionLabel(k) }));
const allSections = [...availableSections, ...extraSections];
@@ -255,8 +252,8 @@ export function renderConfig(props: ConfigProps) {