kin: KIN-021 Аудит-лог для --dangerously-skip-permissions в auto mode
This commit is contained in:
parent
67071c757d
commit
a0b0976d8d
16 changed files with 1477 additions and 14 deletions
|
|
@ -151,6 +151,13 @@ const filteredTasks = computed(() => {
|
|||
return tasks
|
||||
})
|
||||
|
||||
const manualEscalationTasks = computed(() => {
|
||||
if (!project.value) return []
|
||||
return project.value.tasks.filter(
|
||||
t => t.brief?.task_type === 'manual_escalation' && t.status !== 'done' && t.status !== 'cancelled'
|
||||
)
|
||||
})
|
||||
|
||||
const filteredDecisions = computed(() => {
|
||||
if (!project.value) return []
|
||||
let decs = project.value.decisions
|
||||
|
|
@ -220,24 +227,30 @@ async function runTask(taskId: string, event: Event) {
|
|||
}
|
||||
}
|
||||
|
||||
async function patchTaskField(taskId: string, data: { priority?: number; route_type?: string }) {
|
||||
try {
|
||||
const updated = await api.patchTask(taskId, data)
|
||||
if (project.value) {
|
||||
const idx = project.value.tasks.findIndex(t => t.id === taskId)
|
||||
if (idx >= 0) project.value.tasks[idx] = updated
|
||||
}
|
||||
} catch (e: any) {
|
||||
error.value = e.message
|
||||
}
|
||||
}
|
||||
|
||||
async function addDecision() {
|
||||
decFormError.value = ''
|
||||
try {
|
||||
const tags = decForm.value.tags ? decForm.value.tags.split(',').map(s => s.trim()).filter(Boolean) : undefined
|
||||
const body = {
|
||||
await api.createDecision({
|
||||
project_id: props.id,
|
||||
type: decForm.value.type,
|
||||
title: decForm.value.title,
|
||||
description: decForm.value.description,
|
||||
category: decForm.value.category || undefined,
|
||||
tags,
|
||||
}
|
||||
const res = await fetch('/api/decisions', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
if (!res.ok) throw new Error('Failed')
|
||||
showAddDecision.value = false
|
||||
decForm.value = { type: 'decision', title: '', description: '', category: '', tags: '' }
|
||||
await load()
|
||||
|
|
@ -328,6 +341,30 @@ async function addDecision() {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Manual escalation tasks -->
|
||||
<div v-if="manualEscalationTasks.length" class="mb-4">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="text-xs font-semibold text-orange-400 uppercase tracking-wide">⚠ Требуют ручного решения</span>
|
||||
<span class="text-xs text-orange-600">({{ manualEscalationTasks.length }})</span>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<router-link v-for="t in manualEscalationTasks" :key="t.id"
|
||||
:to="{ path: `/task/${t.id}`, query: selectedStatuses.length ? { back_status: selectedStatuses.join(',') } : undefined }"
|
||||
class="flex items-center justify-between px-3 py-2 border border-orange-800/60 bg-orange-950/20 rounded text-sm hover:border-orange-600 no-underline block transition-colors">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<span class="text-gray-500 shrink-0 w-24">{{ t.id }}</span>
|
||||
<Badge :text="t.status" :color="taskStatusColor(t.status)" />
|
||||
<span class="text-orange-300 truncate">{{ t.title }}</span>
|
||||
<span v-if="t.parent_task_id" class="text-[10px] text-gray-600 shrink-0">escalated from {{ t.parent_task_id }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs text-gray-600 shrink-0">
|
||||
<span v-if="t.brief?.description" class="text-orange-600 truncate max-w-[200px]">{{ t.brief.description }}</span>
|
||||
<span>pri {{ t.priority }}</span>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredTasks.length === 0" class="text-gray-600 text-sm">No tasks.</div>
|
||||
<div v-else class="space-y-1">
|
||||
<router-link v-for="t in filteredTasks" :key="t.id"
|
||||
|
|
@ -344,7 +381,26 @@ async function addDecision() {
|
|||
</div>
|
||||
<div class="flex items-center gap-2 text-xs text-gray-600 shrink-0">
|
||||
<span v-if="t.assigned_role">{{ t.assigned_role }}</span>
|
||||
<span>pri {{ t.priority }}</span>
|
||||
<select
|
||||
@click.stop
|
||||
@change.stop="patchTaskField(t.id, { route_type: ($event.target as HTMLSelectElement).value })"
|
||||
:value="(t.brief as Record<string, string> | null)?.route_type || ''"
|
||||
class="bg-gray-900 border border-gray-700 rounded px-1 py-0.5 text-[10px] text-gray-500 cursor-pointer hover:border-gray-500 hover:text-gray-300 transition-colors"
|
||||
title="Task type">
|
||||
<option value="">—</option>
|
||||
<option value="debug">debug</option>
|
||||
<option value="feature">feature</option>
|
||||
<option value="refactor">refactor</option>
|
||||
<option value="hotfix">hotfix</option>
|
||||
</select>
|
||||
<select
|
||||
@click.stop
|
||||
@change.stop="patchTaskField(t.id, { priority: Number(($event.target as HTMLSelectElement).value) })"
|
||||
:value="t.priority"
|
||||
class="bg-gray-900 border border-gray-700 rounded px-1 py-0.5 text-[10px] text-gray-500 cursor-pointer hover:border-gray-500 hover:text-gray-300 transition-colors"
|
||||
title="Priority">
|
||||
<option v-for="n in 10" :key="n" :value="n">p{{ n }}</option>
|
||||
</select>
|
||||
<button v-if="t.status === 'pending'"
|
||||
@click="runTask(t.id, $event)"
|
||||
class="px-2 py-0.5 bg-blue-900/40 text-blue-400 border border-blue-800 rounded hover:bg-blue-900 text-[10px]"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue