| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- <script setup lang="ts">
- import { onMounted, ref } from 'vue'
- import type { ProviderStatusDto } from '@agent-server/shared'
- import ClaudeLoginDialog from '../components/ClaudeLoginDialog.vue'
- import CodexLoginDialog from '../components/CodexLoginDialog.vue'
- import PageHeader from '../components/PageHeader.vue'
- import { admin, errorMessage } from '../lib/api'
- import { toast } from '../lib/ui'
- const providers = ref<ProviderStatusDto[]>([])
- const runs = ref({ running: 0, queued: 0, active: 0 })
- const loading = ref(false)
- const showLogin = ref(false)
- const showCodexLogin = ref(false)
- const loggingOut = ref(false)
- async function logout(provider: 'claude' | 'codex' = 'claude') {
- const name = provider === 'claude' ? 'Claude' : 'Codex'
- if (!confirm(`退出 ${name} 登录?退出后 AgentServer 将无法运行 ${name},直到重新登录。`)) return
- loggingOut.value = true
- try {
- await (provider === 'claude' ? admin.claudeLogin.logout() : admin.codexLogin.logout())
- toast('已退出登录', 'success')
- await load()
- } catch (err) {
- toast(errorMessage(err), 'error')
- } finally {
- loggingOut.value = false
- }
- }
- function onLoginClosed(loggedIn: boolean, name = 'Claude') {
- showLogin.value = false
- showCodexLogin.value = false
- if (loggedIn) toast(`${name} 登录成功`, 'success')
- void load()
- }
- async function load() {
- loading.value = true
- try {
- const o = await admin.overview()
- providers.value = o.providers
- runs.value = o.runs
- } catch (err) {
- toast(errorMessage(err), 'error')
- } finally {
- loading.value = false
- }
- }
- onMounted(load)
- </script>
- <template>
- <div class="h-full flex flex-col">
- <PageHeader title="概览" subtitle="引擎状态与运行情况">
- <button type="button" class="btn-secondary" :disabled="loading" @click="load"><span class="codicon codicon-refresh" />刷新</button>
- </PageHeader>
- <div class="flex-1 overflow-y-auto p-6 flex flex-col gap-6 max-w-240">
- <section class="grid grid-cols-3 gap-3">
- <div v-for="(v, k) in { running: '运行中', queued: '排队中', active: '活动 Run' }" :key="k" class="card p-4">
- <div class="hint">{{ v }}</div>
- <div class="text-[24px] font-600 mt-1">{{ runs[k] }}</div>
- </div>
- </section>
- <section class="flex flex-col gap-3">
- <h2 class="section-title m-0">引擎</h2>
- <div v-for="p in providers" :key="p.id" class="card p-4 flex gap-4 items-start">
- <span class="codicon codicon-hubot text-[28px]" :class="p.available ? 'text-accent' : 'text-fg-muted'" />
- <div class="flex-1 min-w-0 grid grid-cols-[96px_1fr] gap-y-1 text-[12.5px]">
- <div class="col-span-2 flex items-center gap-2 mb-1">
- <span class="text-[14px] font-600">{{ p.id === 'claude' ? 'Claude Code' : 'Codex' }}</span>
- <span class="badge" :class="p.available ? 'bg-success text-white' : p.enabled ? 'bg-danger text-white' : 'bg-[var(--app-input-border)] text-fg-muted'">
- {{ p.available ? '可用' : p.enabled ? '不可用' : '未启用' }}
- </span>
- <span class="flex-1" />
- <template v-if="p.loggedIn !== null">
- <button v-if="!p.loggedIn" type="button" class="btn-primary" @click="p.id === 'claude' ? (showLogin = true) : (showCodexLogin = true)"><span class="codicon codicon-account" />登录</button>
- <template v-else>
- <button type="button" class="btn-ghost" @click="p.id === 'claude' ? (showLogin = true) : (showCodexLogin = true)">重新登录</button>
- <button type="button" class="btn-ghost" :disabled="loggingOut" @click="logout(p.id)">退出登录</button>
- </template>
- </template>
- </div>
- <span class="text-fg-muted">账户</span><span>{{ p.account ?? '—' }}</span>
- <span class="text-fg-muted">CLI</span><span class="font-mono break-all">{{ p.cliPath }} <span class="text-fg-muted">{{ p.version }}</span></span>
- <span class="text-fg-muted">默认模型</span><span>{{ p.defaultModel || '—' }}</span>
- <span class="text-fg-muted">可选模型</span><span>{{ p.models.join('、') || '—' }}</span>
- <template v-if="p.message"><span class="text-fg-muted">说明</span><span class="text-warning">{{ p.message }}</span></template>
- </div>
- </div>
- <div class="hint">服务以专用账号 agentsvc 运行,登录的是你自己的账号:Claude 粘贴授权码完成,Codex 用设备码完成(在 OpenAI 页面输入短码)。也可以在服务器终端执行 <code class="font-mono">agent-claude auth login</code> / <code class="font-mono">agent-codex login --device-auth</code>。无需重启服务。</div>
- </section>
- </div>
- <ClaudeLoginDialog v-if="showLogin" @close="onLoginClosed" />
- <CodexLoginDialog v-if="showCodexLogin" @close="ok => onLoginClosed(ok, 'Codex')" />
- </div>
- </template>
|