feat(agent): log outbound env keys to AM; show direct URL + access token in 运行状态
- runtime: log the env KEY NAMES (never values) sent to AM on start, so we can confirm AGENT_ACCESS_TOKEN is actually transmitted without leaking secrets. - web/运行状态: detail panel now shows the full direct-connect URL (copyable) and the per-agent access token (masked + reveal + copy) with a hint that the client sends it as X-Agent-Access-Token. These are what's actually needed to hand the agent to a client / test it — previously only the bare hostname was shown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -183,6 +184,15 @@ func amStartTemplateAgent(ctx context.Context, args amStartArgs) (amStartResult,
|
||||
},
|
||||
"env": env,
|
||||
}
|
||||
// Operational log: which env KEYS we send to AM (names only, never values) —
|
||||
// lets us confirm e.g. AGENT_ACCESS_TOKEN is actually transmitted without
|
||||
// leaking any secret.
|
||||
envKeys := make([]string, 0, len(env))
|
||||
for k := range env {
|
||||
envKeys = append(envKeys, k)
|
||||
}
|
||||
sort.Strings(envKeys)
|
||||
common.SysLog("amStartTemplateAgent name=" + name + " env_keys=[" + strings.Join(envKeys, ",") + "]")
|
||||
data, err := amTemplateDo(ctx, http.MethodPost, agentTemplateStartPath(), payload, agentTemplateStartTimeout())
|
||||
if err != nil {
|
||||
return amStartResult{}, err
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { Activity, Copy, Database, GitBranch, RefreshCw, Server, X } from 'lucide-react'
|
||||
import { Activity, Copy, Database, Eye, EyeOff, GitBranch, RefreshCw, Server, X } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { api } from '@/lib/api'
|
||||
import { cn } from '@/lib/utils'
|
||||
@@ -12,6 +12,7 @@ type AgentItem = {
|
||||
agent_id: string
|
||||
template_id: string
|
||||
subdomain: string
|
||||
access_token: string
|
||||
binding_ids: number[]
|
||||
status: string
|
||||
runtime_id: string
|
||||
@@ -198,6 +199,9 @@ function DetailPanel({
|
||||
onClose: () => void
|
||||
onCopy: (t: string) => void
|
||||
}) {
|
||||
const [revealToken, setRevealToken] = useState(false)
|
||||
const directUrl = agent.subdomain ? `http://${agent.subdomain}` : ''
|
||||
|
||||
// Live status pull from AM for this agent.
|
||||
const liveQ = useQuery({
|
||||
queryKey: ['agent-status', 'live', agent.agent_id],
|
||||
@@ -225,14 +229,28 @@ function DetailPanel({
|
||||
<span className={cn('inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-semibold ring-1 ring-inset', st.cls)}>{st.text}</span>
|
||||
<span className='text-muted-foreground ml-2 text-[11px]'>{liveQ.isFetching ? '刷新中…' : '每 10 秒刷新'}</span>
|
||||
</Field>
|
||||
<Field label='访问地址(直连)'>
|
||||
<Field label='直连地址(客户端连这个)'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='min-w-0 flex-1 truncate font-mono text-xs'>{agent.subdomain || '地址生成中…'}</span>
|
||||
<Button variant='outline' size='sm' className='h-7 gap-1 text-xs' disabled={!agent.subdomain} onClick={() => onCopy(agent.subdomain)}>
|
||||
<span className='min-w-0 flex-1 truncate font-mono text-xs'>{directUrl || '地址生成中…'}</span>
|
||||
<Button variant='outline' size='sm' className='h-7 gap-1 text-xs' disabled={!directUrl} onClick={() => onCopy(directUrl)}>
|
||||
<Copy className='h-3 w-3' /> 复制
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
<Field label='访问令牌(直连鉴权 · X-Agent-Access-Token)'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='min-w-0 flex-1 truncate font-mono text-xs'>
|
||||
{agent.access_token ? (revealToken ? agent.access_token : '••••••••••••••••') : '—'}
|
||||
</span>
|
||||
<Button variant='ghost' size='icon' className='h-7 w-7' disabled={!agent.access_token} onClick={() => setRevealToken((v) => !v)} title={revealToken ? '隐藏' : '显示'}>
|
||||
{revealToken ? <EyeOff className='h-3.5 w-3.5' /> : <Eye className='h-3.5 w-3.5' />}
|
||||
</Button>
|
||||
<Button variant='outline' size='sm' className='h-7 gap-1 text-xs' disabled={!agent.access_token} onClick={() => onCopy(agent.access_token)}>
|
||||
<Copy className='h-3 w-3' /> 复制
|
||||
</Button>
|
||||
</div>
|
||||
<p className='text-muted-foreground mt-1 text-[10px]'>客户端直连时放进请求头 <span className='font-mono'>X-Agent-Access-Token</span>,证明只有你能调这个 Agent。</p>
|
||||
</Field>
|
||||
<Field label='挂载的资源'>
|
||||
{(agent.binding_ids?.length ?? 0) === 0 ? (
|
||||
<span className='text-muted-foreground text-xs'>未挂载资源</span>
|
||||
|
||||
Reference in New Issue
Block a user