fix: localize agnet console copy
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
1.4.10
|
||||
1.4.11
|
||||
|
||||
+8
-2
@@ -37,7 +37,13 @@ export function CommandMenu() {
|
||||
)
|
||||
|
||||
return (
|
||||
<CommandDialog modal open={open} onOpenChange={setOpen}>
|
||||
<CommandDialog
|
||||
modal
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
title={t('Command Palette')}
|
||||
description={t('Search for a command to run...')}
|
||||
>
|
||||
<CommandInput placeholder={t('Type a command or search...')} />
|
||||
<CommandList>
|
||||
<ScrollArea type='hover' className='h-72 pe-1'>
|
||||
@@ -79,7 +85,7 @@ export function CommandMenu() {
|
||||
</CommandGroup>
|
||||
))}
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading='Theme'>
|
||||
<CommandGroup heading={t('Theme')}>
|
||||
<CommandItem onSelect={() => runCommand(() => setTheme('light'))}>
|
||||
<Sun /> <span>{t('Light')}</span>
|
||||
</CommandItem>
|
||||
|
||||
+121
-33
@@ -90,6 +90,7 @@ const STATUS_TO_KEY: Record<string, StatusKey> = {
|
||||
running: 'running',
|
||||
active: 'running',
|
||||
in_progress: 'running',
|
||||
stopped: 'success',
|
||||
succeeded: 'success',
|
||||
success: 'success',
|
||||
completed: 'success',
|
||||
@@ -105,7 +106,44 @@ function classifyStatus(status: string): StatusKey {
|
||||
return STATUS_TO_KEY[(status || '').toLowerCase()] ?? 'pending'
|
||||
}
|
||||
|
||||
function formatStatusLabel(
|
||||
status: string | undefined,
|
||||
t: (key: string) => string
|
||||
): string {
|
||||
const value = (status || '').trim()
|
||||
if (!value) return t('Unknown')
|
||||
const normalized = value.toLowerCase()
|
||||
if (normalized.startsWith('handoff:')) {
|
||||
const tail = normalized.replace('handoff:', '')
|
||||
return `${t('Handoff')}: ${formatStatusLabel(tail, t)}`
|
||||
}
|
||||
const labels: Record<string, string> = {
|
||||
running: t('Running'),
|
||||
active: t('Running'),
|
||||
in_progress: t('In progress'),
|
||||
stopped: t('Stopped'),
|
||||
succeeded: t('Success'),
|
||||
success: t('Success'),
|
||||
completed: t('Completed'),
|
||||
failed: t('Failed'),
|
||||
error: t('Failed'),
|
||||
rejected: t('Rejected'),
|
||||
pending: t('Pending'),
|
||||
queued: t('Queued'),
|
||||
awaiting: t('Awaiting'),
|
||||
observed: t('Observed'),
|
||||
blocked: t('Blocked'),
|
||||
requested: t('Requested'),
|
||||
high: t('High'),
|
||||
medium: t('Medium'),
|
||||
mid: t('Medium'),
|
||||
low: t('Low'),
|
||||
}
|
||||
return labels[normalized] || value
|
||||
}
|
||||
|
||||
function StatusBadge({ phase }: { phase: string }) {
|
||||
const { t } = useTranslation()
|
||||
const k = classifyStatus(phase)
|
||||
const map: Record<StatusKey, { cls: string; icon: ReactNode }> = {
|
||||
running: {
|
||||
@@ -134,7 +172,7 @@ function StatusBadge({ phase }: { phase: string }) {
|
||||
)}
|
||||
>
|
||||
{m.icon}
|
||||
{phase || 'unknown'}
|
||||
{formatStatusLabel(phase, t)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -238,22 +276,37 @@ function describeRiskLevel(dep: AgnetDeployment): {
|
||||
return { label: 'low', tone: 'low' }
|
||||
}
|
||||
|
||||
function formatRiskLabel(label: string, t: (key: string) => string): string {
|
||||
const value = label.toLowerCase()
|
||||
if (value === 'high') return t('High')
|
||||
if (value === 'medium' || value === 'mid') return t('Medium')
|
||||
if (value === 'low') return t('Low')
|
||||
return label
|
||||
}
|
||||
|
||||
function describeSubMode(dep: AgnetDeployment): string {
|
||||
return dep.sub_mode || dep.orchestration_plan?.sub_mode || 'agile'
|
||||
}
|
||||
|
||||
function formatSubModeLabel(mode: string, t: (key: string) => string): string {
|
||||
const value = mode.toLowerCase()
|
||||
if (value === 'agile') return t('Agile')
|
||||
if (value === 'waterfall') return t('Waterfall')
|
||||
return mode
|
||||
}
|
||||
|
||||
function describeBudget(dep: AgnetDeployment): string {
|
||||
const budget = dep.orchestration_plan?.budget
|
||||
if (!budget) {
|
||||
const agents = dep.orchestration_plan?.agents?.length ?? 0
|
||||
return `${agents} agents`
|
||||
return `${agents} 个 Agent`
|
||||
}
|
||||
|
||||
const parts = []
|
||||
if (budget.max_cost_usd > 0) parts.push(`$${budget.max_cost_usd}`)
|
||||
if (budget.max_tokens > 0) parts.push(`${budget.max_tokens} tokens`)
|
||||
if (budget.max_tokens > 0) parts.push(`${budget.max_tokens} 个 token`)
|
||||
if (budget.max_duration_sec > 0)
|
||||
parts.push(`${Math.round(budget.max_duration_sec / 60)}m`)
|
||||
parts.push(`${Math.round(budget.max_duration_sec / 60)} 分钟`)
|
||||
return parts.length > 0 ? parts.join(' / ') : '—'
|
||||
}
|
||||
|
||||
@@ -277,9 +330,11 @@ function compactRuntimeRef(value: string | undefined): string {
|
||||
|
||||
function formatRecordSource(value: unknown): string {
|
||||
const source = String(value || '').trim()
|
||||
if (!source) return 'manager'
|
||||
if (source.includes('simulate')) return 'simulated'
|
||||
if (source.includes('runtime') || source.includes('swarm')) return 'runtime'
|
||||
const normalized = source.toLowerCase()
|
||||
if (!source || normalized.includes('manager')) return 'Manager'
|
||||
if (normalized.includes('simulate')) return '模拟'
|
||||
if (normalized.includes('runtime') || normalized.includes('swarm'))
|
||||
return 'Runtime'
|
||||
return source
|
||||
}
|
||||
|
||||
@@ -324,11 +379,11 @@ function taskFlowDetail(item: Record<string, unknown>): string {
|
||||
const attempt = String(recordPayloadValue(item, 'attempt') || '')
|
||||
const agentRole = String(recordPayloadValue(item, 'agent_role') || '')
|
||||
const parts = [
|
||||
taskId && `task ${taskId}`,
|
||||
agentRole && `role ${agentRole}`,
|
||||
taskId && `任务 ${taskId}`,
|
||||
agentRole && `角色 ${agentRole}`,
|
||||
fromRole && toRole && `${fromRole} -> ${toRole}`,
|
||||
reason && `reason ${reason}`,
|
||||
attempt && `attempt ${attempt}`,
|
||||
reason && `原因 ${reason}`,
|
||||
attempt && `第 ${attempt} 次`,
|
||||
].filter(Boolean)
|
||||
return parts.join(' / ') || '—'
|
||||
}
|
||||
@@ -415,13 +470,13 @@ function formatRelativeTime(value: string | undefined): string {
|
||||
if (Number.isNaN(date.getTime())) return value
|
||||
const diff = Date.now() - date.getTime()
|
||||
const sec = Math.round(diff / 1000)
|
||||
if (sec < 60) return `${sec}s ago`
|
||||
if (sec < 60) return `${sec} 秒前`
|
||||
const min = Math.round(sec / 60)
|
||||
if (min < 60) return `${min}m ago`
|
||||
if (min < 60) return `${min} 分钟前`
|
||||
const hr = Math.round(min / 60)
|
||||
if (hr < 24) return `${hr}h ago`
|
||||
if (hr < 24) return `${hr} 小时前`
|
||||
const day = Math.round(hr / 24)
|
||||
return `${day}d ago`
|
||||
return `${day} 天前`
|
||||
}
|
||||
|
||||
// maskSecretRef shows enough of a secret_ref to identify which vault
|
||||
@@ -461,6 +516,18 @@ function grantStatusToneClass(status: string | undefined): string {
|
||||
}
|
||||
}
|
||||
|
||||
function formatGrantStatus(
|
||||
status: string | undefined,
|
||||
t: (key: string) => string
|
||||
): string {
|
||||
const value = (status || 'active').toLowerCase()
|
||||
if (value === 'active') return t('Active')
|
||||
if (value === 'pending') return t('Pending')
|
||||
if (value === 'disabled') return t('Disabled')
|
||||
if (value === 'revoked') return t('Revoked')
|
||||
return status || t('Active')
|
||||
}
|
||||
|
||||
function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -527,13 +594,21 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
</div>
|
||||
|
||||
<div className='mt-4 grid gap-2 sm:grid-cols-2 xl:grid-cols-6'>
|
||||
<MetaPill icon={Activity} label={t('status')} value={phase || '—'} />
|
||||
<MetaPill
|
||||
icon={Activity}
|
||||
label={t('status')}
|
||||
value={formatStatusLabel(phase, t)}
|
||||
/>
|
||||
<MetaPill
|
||||
icon={Rocket}
|
||||
label={t('mode')}
|
||||
value={describeSubMode(dep)}
|
||||
value={formatSubModeLabel(describeSubMode(dep), t)}
|
||||
/>
|
||||
<MetaPill
|
||||
icon={Tag}
|
||||
label={t('risk')}
|
||||
value={formatRiskLabel(risk.label, t)}
|
||||
/>
|
||||
<MetaPill icon={Tag} label={t('risk')} value={risk.label} />
|
||||
<MetaPill
|
||||
icon={Coins}
|
||||
label={t('budget')}
|
||||
@@ -546,7 +621,7 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
/>
|
||||
<MetaPill
|
||||
icon={ShieldCheck}
|
||||
label='secret_ref'
|
||||
label={t('secret_ref')}
|
||||
value={describeSecretRefs(dep)}
|
||||
/>
|
||||
</div>
|
||||
@@ -555,16 +630,16 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
<MetaPill
|
||||
icon={Cpu}
|
||||
label={t('runtime')}
|
||||
value={dep.runtime_state || 'control-plane'}
|
||||
value={dep.runtime_state || t('Control plane')}
|
||||
/>
|
||||
<MetaPill
|
||||
icon={Rocket}
|
||||
label='runtime_id'
|
||||
label={t('runtime_id')}
|
||||
value={compactRuntimeRef(dep.runtime_deployment_id)}
|
||||
/>
|
||||
<MetaPill
|
||||
icon={GitCommit}
|
||||
label='swarm_id'
|
||||
label={t('swarm_id')}
|
||||
value={compactRuntimeRef(dep.runtime_swarm_id)}
|
||||
/>
|
||||
</div>
|
||||
@@ -694,7 +769,7 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
grantStatusToneClass(status)
|
||||
)}
|
||||
>
|
||||
{status}
|
||||
{formatGrantStatus(status, t)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -1112,7 +1187,7 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) {
|
||||
) : (
|
||||
<ol className='mt-2 max-h-48 space-y-2 overflow-y-auto pe-1'>
|
||||
{taskFlowRecords.slice(0, 8).map((item, idx) => {
|
||||
const event = String(item.event_type || item.event || 'event')
|
||||
const event = String(item.event_type || item.event || t('Event'))
|
||||
const source = formatRecordSource(item.source)
|
||||
const detail = taskFlowDetail(item)
|
||||
return (
|
||||
@@ -1222,7 +1297,7 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) {
|
||||
artifactTypeToneClass(item.artifact_type)
|
||||
)}
|
||||
>
|
||||
{String(item.artifact_type || 'artifact')}
|
||||
{String(item.artifact_type || t('Artifact'))}
|
||||
</span>
|
||||
</div>
|
||||
<p className='text-muted-foreground mt-1 line-clamp-2 text-[11px]'>
|
||||
@@ -1254,10 +1329,10 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) {
|
||||
className='bg-muted/25 rounded-lg p-2'
|
||||
>
|
||||
<p className='truncate font-mono text-[11px]'>
|
||||
{String(item.snapshot_id || 'snapshot')}
|
||||
{String(item.snapshot_id || t('Snapshot'))}
|
||||
</p>
|
||||
<p className='text-muted-foreground mt-1 truncate text-[11px]'>
|
||||
{String(item.source_type || 'source')} ·{' '}
|
||||
{String(item.source_type || t('Source'))} ·{' '}
|
||||
{String(item.source_ref || '—')}
|
||||
</p>
|
||||
</li>
|
||||
@@ -1278,7 +1353,7 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) {
|
||||
) : (
|
||||
<ol className='mt-2 max-h-52 space-y-2 overflow-y-auto pe-1'>
|
||||
{timeline.slice(0, 8).map((item, idx) => {
|
||||
const event = String(item.event_type || item.event || 'event')
|
||||
const event = String(item.event_type || item.event || t('Event'))
|
||||
const source = formatRecordSource(item.source)
|
||||
return (
|
||||
<li
|
||||
@@ -1314,6 +1389,17 @@ const EVENT_LEVELS = ['all', 'info', 'warn', 'error'] as const
|
||||
|
||||
type EventLevel = (typeof EVENT_LEVELS)[number]
|
||||
|
||||
function formatEventLevelLabel(
|
||||
level: EventLevel,
|
||||
t: (key: string) => string
|
||||
): string {
|
||||
if (level === 'all') return t('All levels')
|
||||
if (level === 'info') return t('Info')
|
||||
if (level === 'warn') return t('Warning')
|
||||
if (level === 'error') return t('Error')
|
||||
return level
|
||||
}
|
||||
|
||||
function classifyEventLevel(entry: Record<string, unknown>): EventLevel {
|
||||
const candidate = String(
|
||||
entry.level || entry.severity || entry.status || ''
|
||||
@@ -1391,7 +1477,7 @@ export function AgnetEventsPage() {
|
||||
<SelectContent>
|
||||
{EVENT_LEVELS.map((lv) => (
|
||||
<SelectItem key={lv} value={lv}>
|
||||
{lv}
|
||||
{formatEventLevelLabel(lv, t)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -1655,6 +1741,7 @@ function AgnetApprovalCard({
|
||||
onApprove: () => void
|
||||
onReject: () => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<article className='border-border/70 bg-background/45 rounded-xl border p-3'>
|
||||
<div className='flex flex-wrap items-start justify-between gap-3'>
|
||||
@@ -1675,7 +1762,7 @@ function AgnetApprovalCard({
|
||||
<RedactedField k='expires_at' v={formatUnixMs(approval.expires_at)} />
|
||||
<RedactedField
|
||||
k='credential'
|
||||
v={approval.requires_credential ? 'required' : 'not required'}
|
||||
v={approval.requires_credential ? t('Required') : t('Not required')}
|
||||
/>
|
||||
</dl>
|
||||
<div className='mt-3 flex flex-wrap justify-end gap-2'>
|
||||
@@ -1685,14 +1772,14 @@ function AgnetApprovalCard({
|
||||
disabled={rejectBusy || approveBusy}
|
||||
onClick={onReject}
|
||||
>
|
||||
Reject
|
||||
{t('Reject')}
|
||||
</Button>
|
||||
<Button
|
||||
size='sm'
|
||||
disabled={approveBusy || rejectBusy}
|
||||
onClick={onApprove}
|
||||
>
|
||||
Approve
|
||||
{t('Approve')}
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
@@ -1708,6 +1795,7 @@ function AgnetLeaseCard({
|
||||
busy: boolean
|
||||
onRevoke: () => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<article className='border-border/70 bg-background/45 rounded-xl border p-3'>
|
||||
<div className='flex flex-wrap items-start justify-between gap-3'>
|
||||
@@ -1727,7 +1815,7 @@ function AgnetLeaseCard({
|
||||
</dl>
|
||||
<div className='mt-3 flex justify-end'>
|
||||
<Button size='sm' variant='outline' disabled={busy} onClick={onRevoke}>
|
||||
Revoke
|
||||
{t('Revoke')}
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
+66
@@ -4081,9 +4081,75 @@
|
||||
"Resource binding": "资源绑定",
|
||||
"Show manifest": "展开权限清单",
|
||||
"Hide manifest": "收起权限清单",
|
||||
"Resource": "资源",
|
||||
"Allowed actions": "允许动作",
|
||||
"Constraints": "约束",
|
||||
"secret_ref": "密钥引用",
|
||||
"runtime_id": "运行 ID",
|
||||
"swarm_id": "蜂群 ID",
|
||||
"mode": "模式",
|
||||
"runtime": "运行时",
|
||||
"Control plane": "控制面",
|
||||
"Stopped": "已停止",
|
||||
"In progress": "进行中",
|
||||
"Awaiting": "等待中",
|
||||
"Observed": "已观测",
|
||||
"Blocked": "已阻塞",
|
||||
"Requested": "已请求",
|
||||
"Rejected": "已拒绝",
|
||||
"Active": "启用中",
|
||||
"Disabled": "已禁用",
|
||||
"Revoked": "已撤销",
|
||||
"High": "高",
|
||||
"Medium": "中",
|
||||
"Low": "低",
|
||||
"Agile": "敏捷",
|
||||
"Waterfall": "瀑布",
|
||||
"Handoff": "交接",
|
||||
"Event": "事件",
|
||||
"Snapshot": "快照",
|
||||
"Artifact": "产物",
|
||||
"Command Palette": "命令面板",
|
||||
"Search for a command to run...": "搜索可执行命令...",
|
||||
"Related records": "关联记录",
|
||||
"Callbacks, artifacts, SK snapshots and merged timeline": "回调、产物、SK 快照和合并时间线",
|
||||
"callbacks": "回调",
|
||||
"artifacts": "产物",
|
||||
"timeline": "时间线",
|
||||
"Sub task flow": "子任务流",
|
||||
"No task, retry or handoff callback records yet": "暂无任务、重试或交接回调记录",
|
||||
"Agent task map": "Agent 任务图",
|
||||
"Waiting for Runtime task graph callbacks. Manager will show task, Agent role, handoff and source here when callbacks arrive.": "等待 Runtime 回传任务图。收到回调后,这里会展示任务、Agent 角色、交接关系和来源。",
|
||||
"Agent role": "Agent 角色",
|
||||
"Artifacts": "产物",
|
||||
"No artifacts yet": "暂无产物",
|
||||
"SK snapshots": "SK 快照",
|
||||
"No SK snapshots yet": "暂无 SK 快照",
|
||||
"Merged timeline": "合并时间线",
|
||||
"No timeline records yet": "暂无时间线记录",
|
||||
"Simulate": "模拟事件",
|
||||
"Simulated events recorded": "模拟事件已记录",
|
||||
"Failed to simulate events": "模拟事件失败",
|
||||
"All levels": "全部级别",
|
||||
"Info": "信息",
|
||||
"Active credential leases": "有效凭证租约",
|
||||
"Approve or reject high-risk Agnet operations before credentials are leased.": "在下发凭证租约前,审批或拒绝高危 Agnet 操作。",
|
||||
"No pending approvals": "暂无待审批项",
|
||||
"High-risk operations will appear here.": "高危操作会显示在这里。",
|
||||
"No active credential leases": "暂无有效凭证租约",
|
||||
"Only short-lived lease references are shown. Revoke after the task ends.": "这里只展示短期租约引用。任务结束后请及时撤销。",
|
||||
"Approved credential leases will appear here.": "已审批的凭证租约会显示在这里。",
|
||||
"Approve": "批准",
|
||||
"Reject": "拒绝",
|
||||
"Revoke": "撤销",
|
||||
"Not required": "不需要",
|
||||
"Approval accepted": "审批已通过",
|
||||
"Approval rejected": "审批已拒绝",
|
||||
"Credential lease revoked": "凭证租约已撤销",
|
||||
"Approved from Manager audit page": "从 Manager 审计页批准",
|
||||
"Rejected from Manager audit page": "从 Manager 审计页拒绝",
|
||||
"Revoked from Manager audit page": "从 Manager 审计页撤销",
|
||||
"Connect code, docs and cloud resources for the current task, then confirm the recommendation before launching Agnet.": "为当前任务绑定代码、文档和云资源,然后确认推荐方案再启动 Agnet。",
|
||||
"Plaintext credentials are never shown. The secret_ref column is a vault pointer, not the secret itself.": "永远不展示明文密钥。表中的 secret_ref 只是密钥保管器里的引用指针,不是密钥本体。",
|
||||
"Confirm before launch": "确认后启动",
|
||||
"Heicode summarises what this run will do, what resources it can use, what stays off-limits, and the expected cost. Confirm to launch.": "Heicode 已汇总本次会做什么、能用哪些资源、哪些事情不会做、以及预计消耗。请确认后启动。",
|
||||
|
||||
Reference in New Issue
Block a user