fix: repo sync error

This commit is contained in:
engigu
2026-03-19 16:08:02 +08:00
parent 9c5da93a27
commit 05104415ce
12 changed files with 675 additions and 229 deletions
+43 -4
View File
@@ -12,7 +12,7 @@ import DirTreeSelect from '@/components/DirTreeSelect.vue'
import { Plus, ChevronDown, X, Search, Check, ChevronsUpDown, Loader2, AlertCircle, Terminal, Clock, Zap } from 'lucide-vue-next'
import { cn } from '@/lib/utils'
import { api, type Task, type EnvVar, type Agent, type MiseLanguage } from '@/api'
import { TRIGGER_TYPE } from '@/constants'
import { PATHS, TRIGGER_TYPE } from '@/constants'
import { toast } from 'vue-sonner'
import { getCronDescription } from '@/utils/cron'
@@ -54,6 +54,8 @@ const workDirCache = ref<Record<string, string>>({})
const concurrency = ref(0)
const concurrencyEnabled = ref(false)
const allEnvsEnabled = ref(false)
const SCRIPTS_DIR_PLACEHOLDER = '$SCRIPTS_DIR$'
const scriptsDir = ref(PATHS.SCRIPTS_DIR)
const cronDescription = computed(() => {
if (!form.value.schedule) return ''
@@ -307,6 +309,11 @@ watch(() => props.open, async (val) => {
envSearchQuery.value = ''
// 加载数据
await loadData()
workDirCache.value = {
[agentId]: agentId === 'local'
? normalizeLocalWorkDirForDisplay(props.task?.work_dir)
: (props.task?.work_dir || '')
}
if (selectedAgentId.value === 'local') {
await fetchInstalledLangs()
// 更新所有语言的可用版本
@@ -319,12 +326,14 @@ watch(() => props.open, async (val) => {
async function loadData() {
try {
const [envs, agents] = await Promise.all([
const [envs, agents, paths] = await Promise.all([
api.env.all(),
api.agents.list()
api.agents.list(),
api.settings.getPaths().catch(() => ({ scripts_dir: PATHS.SCRIPTS_DIR }))
])
allEnvVars.value = envs
allAgents.value = agents
scriptsDir.value = paths?.scripts_dir || PATHS.SCRIPTS_DIR
} catch { /* ignore */ }
}
@@ -338,6 +347,34 @@ function removeEnv(id: string) {
selectedEnvIds.value = selectedEnvIds.value.filter(envId => envId !== id)
}
function normalizeLocalWorkDirForDisplay(workDir?: string | null): string {
if (!workDir) return ''
if (workDir === SCRIPTS_DIR_PLACEHOLDER) return ''
if (workDir.startsWith(`${SCRIPTS_DIR_PLACEHOLDER}/`)) {
return workDir.slice(SCRIPTS_DIR_PLACEHOLDER.length + 1)
}
const base = scriptsDir.value || PATHS.SCRIPTS_DIR
if (workDir === base) return ''
if (workDir.startsWith(`${base}/`)) {
return workDir.slice(base.length + 1)
}
return workDir
}
function encodeLocalWorkDir(workDir?: string | null): string {
const value = workDir?.trim() || ''
if (!value) return SCRIPTS_DIR_PLACEHOLDER
if (value === SCRIPTS_DIR_PLACEHOLDER || value.startsWith(`${SCRIPTS_DIR_PLACEHOLDER}/`)) {
return value
}
const base = scriptsDir.value || PATHS.SCRIPTS_DIR
if (value === base) return SCRIPTS_DIR_PLACEHOLDER
if (value.startsWith(`${base}/`)) {
return `${SCRIPTS_DIR_PLACEHOLDER}/${value.slice(base.length + 1)}`
}
return `${SCRIPTS_DIR_PLACEHOLDER}/${value.replace(/^\/+/, '')}`
}
async function save() {
try {
form.value.clean_config = cleanConfig.value
@@ -376,7 +413,9 @@ async function save() {
form.value.config = JSON.stringify(config)
// 保存当前选择的执行位置对应的工作目录
form.value.work_dir = currentWorkDir.value
form.value.work_dir = selectedAgentId.value === 'local'
? encodeLocalWorkDir(currentWorkDir.value)
: currentWorkDir.value
if (props.isEdit && form.value.id) {
await api.tasks.update(form.value.id, form.value)
+45 -3
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted, computed, watch } from 'vue'
import { ref, onMounted, computed, watch, onUnmounted } from 'vue'
import { Button } from '@/components/ui/button'
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog'
import { Input } from '@/components/ui/input'
@@ -218,6 +218,30 @@ const showLogViewer = ref(false)
const selectedLogId = ref<string | undefined>()
const latestLogStatus = ref('')
const latestLogTitle = ref('')
const logContent = ref('')
let logSocket: WebSocket | null = null
function cleanupLogSocket() {
if (logSocket) {
logSocket.onopen = null
logSocket.onmessage = null
logSocket.onerror = null
logSocket.onclose = null
logSocket.close()
logSocket = null
}
}
watch(showLogViewer, (val) => {
if (!val) {
cleanupLogSocket()
logContent.value = ''
}
})
onUnmounted(() => {
cleanupLogSocket()
})
async function viewLogs(taskId: string) {
try {
@@ -228,7 +252,25 @@ async function viewLogs(taskId: string) {
latestLogTitle.value = latestLog.task_name || ''
latestLogStatus.value = latestLog.status || ''
selectedLogId.value = latestLog.id
logContent.value = ''
showLogViewer.value = true
// Connect WebSocket to load log content
cleanupLogSocket()
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const host = window.location.host
const baseUrl = (window as any).__BASE_URL__ || ''
const apiVersion = (window as any).__API_VERSION__ || '/api/v1'
const wsUrl = `${protocol}//${host}${baseUrl}${apiVersion}/logs/ws?log_id=${latestLog.id}`
logSocket = new WebSocket(wsUrl)
logSocket.onmessage = (event) => {
if (latestLog.status !== 'running') {
logContent.value = event.data
} else {
logContent.value += event.data
}
}
} else {
toast.info('该任务暂无执行日志')
}
@@ -460,8 +502,8 @@ watch(() => route.query.agent_id, (newVal) => {
<RepoDialog v-model:open="showRepoDialog" :task="editingTask" :is-edit="isEdit" @saved="loadTasks" />
<!-- 最新日志全屏查看 -->
<LogViewer v-model:open="showLogViewer" :task-name="latestLogTitle"
:log-id="selectedLogId" :initial-status="latestLogStatus" />
<LogViewer v-model:open="showLogViewer" :title="`最新日志 - ${latestLogTitle}`"
:content="logContent || '无输出'" :status="latestLogStatus" />
<!-- 删除确认 (批量) -->
<AlertDialog v-model:open="showBatchDeleteDialog">