kin: KIN-083 Healthcheck claude CLI auth: перед запуском pipeline проверять что claude залогинен (быстрый claude -p 'ok' --output-format json, проверить is_error и 'Not logged in'). Если не залогинен — не запускать pipeline, а показать ошибку 'Claude CLI requires login' в GUI с инструкцией.

This commit is contained in:
Gros Frumos 2026-03-16 15:48:09 +02:00
parent a80679ae72
commit bfc8f1c0bb
18 changed files with 1390 additions and 57 deletions

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { api, type ProjectDetail, type AuditResult, type Phase, type Task } from '../api'
import { api, ApiError, type ProjectDetail, type AuditResult, type Phase, type Task } from '../api'
import Badge from '../components/Badge.vue'
import Modal from '../components/Modal.vue'
@ -18,6 +18,7 @@ const activeTab = ref<'tasks' | 'phases' | 'decisions' | 'modules' | 'kanban'>('
const phases = ref<Phase[]>([])
const phasesLoading = ref(false)
const phaseError = ref('')
const claudeLoginError = ref(false)
const showReviseModal = ref(false)
const revisePhaseId = ref<number | null>(null)
const reviseComment = ref('')
@ -76,11 +77,16 @@ async function approvePhase(phaseId: number) {
async function startPhase() {
startPhaseSaving.value = true
phaseError.value = ''
claudeLoginError.value = false
try {
await api.startPhase(props.id)
await loadPhases()
} catch (e: any) {
phaseError.value = e.message
if (e instanceof ApiError && e.code === 'claude_auth_required') {
claudeLoginError.value = true
} else {
phaseError.value = e.message
}
} finally {
startPhaseSaving.value = false
}
@ -702,6 +708,17 @@ async function addDecision() {
<!-- Phases Tab -->
<div v-if="activeTab === 'phases'">
<div v-if="claudeLoginError" class="mb-3 px-4 py-3 border border-yellow-700 bg-yellow-950/30 rounded">
<div class="flex items-start justify-between gap-2">
<div>
<p class="text-sm font-semibold text-yellow-300">&#9888; Claude CLI requires login</p>
<p class="text-xs text-yellow-200/80 mt-1">Откройте терминал и выполните:</p>
<code class="text-xs text-yellow-400 font-mono bg-black/30 px-2 py-0.5 rounded mt-1 inline-block">claude login</code>
<p class="text-xs text-gray-500 mt-1">После входа повторите запуск pipeline.</p>
</div>
<button @click="claudeLoginError = false" class="text-gray-600 hover:text-gray-400 bg-transparent border-none cursor-pointer text-xs shrink-0"></button>
</div>
</div>
<p v-if="phasesLoading" class="text-gray-500 text-sm">Loading phases...</p>
<p v-else-if="phaseError" class="text-red-400 text-sm">{{ phaseError }}</p>
<div v-else-if="phases.length === 0" class="text-gray-600 text-sm">