opencastle 0.27.0 → 0.27.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +6 -0
- package/dist/cli/agents.d.ts +3 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +161 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/baselines.d.ts +3 -0
- package/dist/cli/baselines.d.ts.map +1 -0
- package/dist/cli/baselines.js +128 -0
- package/dist/cli/baselines.js.map +1 -0
- package/dist/cli/convoy/dashboard-types.d.ts +146 -0
- package/dist/cli/convoy/dashboard-types.d.ts.map +1 -0
- package/dist/cli/convoy/dashboard-types.js +2 -0
- package/dist/cli/convoy/dashboard-types.js.map +1 -0
- package/dist/cli/convoy/engine.d.ts +67 -2
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +2036 -28
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/engine.test.js +1659 -70
- package/dist/cli/convoy/engine.test.js.map +1 -1
- package/dist/cli/convoy/event-schemas.d.ts +9 -0
- package/dist/cli/convoy/event-schemas.d.ts.map +1 -0
- package/dist/cli/convoy/event-schemas.js +185 -0
- package/dist/cli/convoy/event-schemas.js.map +1 -0
- package/dist/cli/convoy/events.d.ts +12 -1
- package/dist/cli/convoy/events.d.ts.map +1 -1
- package/dist/cli/convoy/events.js +186 -13
- package/dist/cli/convoy/events.js.map +1 -1
- package/dist/cli/convoy/events.test.js +325 -28
- package/dist/cli/convoy/events.test.js.map +1 -1
- package/dist/cli/convoy/expertise.d.ts +16 -0
- package/dist/cli/convoy/expertise.d.ts.map +1 -0
- package/dist/cli/convoy/expertise.js +121 -0
- package/dist/cli/convoy/expertise.js.map +1 -0
- package/dist/cli/convoy/expertise.test.d.ts +2 -0
- package/dist/cli/convoy/expertise.test.d.ts.map +1 -0
- package/dist/cli/convoy/expertise.test.js +96 -0
- package/dist/cli/convoy/expertise.test.js.map +1 -0
- package/dist/cli/convoy/export.test.js +1 -0
- package/dist/cli/convoy/export.test.js.map +1 -1
- package/dist/cli/convoy/formula.d.ts +19 -0
- package/dist/cli/convoy/formula.d.ts.map +1 -0
- package/dist/cli/convoy/formula.js +142 -0
- package/dist/cli/convoy/formula.js.map +1 -0
- package/dist/cli/convoy/formula.test.d.ts +2 -0
- package/dist/cli/convoy/formula.test.d.ts.map +1 -0
- package/dist/cli/convoy/formula.test.js +342 -0
- package/dist/cli/convoy/formula.test.js.map +1 -0
- package/dist/cli/convoy/gates.d.ts +128 -0
- package/dist/cli/convoy/gates.d.ts.map +1 -0
- package/dist/cli/convoy/gates.js +606 -0
- package/dist/cli/convoy/gates.js.map +1 -0
- package/dist/cli/convoy/gates.test.d.ts +2 -0
- package/dist/cli/convoy/gates.test.d.ts.map +1 -0
- package/dist/cli/convoy/gates.test.js +976 -0
- package/dist/cli/convoy/gates.test.js.map +1 -0
- package/dist/cli/convoy/health.d.ts +11 -0
- package/dist/cli/convoy/health.d.ts.map +1 -1
- package/dist/cli/convoy/health.js +54 -0
- package/dist/cli/convoy/health.js.map +1 -1
- package/dist/cli/convoy/health.test.js +56 -1
- package/dist/cli/convoy/health.test.js.map +1 -1
- package/dist/cli/convoy/issues.d.ts +8 -0
- package/dist/cli/convoy/issues.d.ts.map +1 -0
- package/dist/cli/convoy/issues.js +98 -0
- package/dist/cli/convoy/issues.js.map +1 -0
- package/dist/cli/convoy/issues.test.d.ts +2 -0
- package/dist/cli/convoy/issues.test.d.ts.map +1 -0
- package/dist/cli/convoy/issues.test.js +107 -0
- package/dist/cli/convoy/issues.test.js.map +1 -0
- package/dist/cli/convoy/knowledge.d.ts +5 -0
- package/dist/cli/convoy/knowledge.d.ts.map +1 -0
- package/dist/cli/convoy/knowledge.js +116 -0
- package/dist/cli/convoy/knowledge.js.map +1 -0
- package/dist/cli/convoy/knowledge.test.d.ts +2 -0
- package/dist/cli/convoy/knowledge.test.d.ts.map +1 -0
- package/dist/cli/convoy/knowledge.test.js +87 -0
- package/dist/cli/convoy/knowledge.test.js.map +1 -0
- package/dist/cli/convoy/lessons.d.ts +17 -0
- package/dist/cli/convoy/lessons.d.ts.map +1 -0
- package/dist/cli/convoy/lessons.js +149 -0
- package/dist/cli/convoy/lessons.js.map +1 -0
- package/dist/cli/convoy/lessons.test.d.ts +2 -0
- package/dist/cli/convoy/lessons.test.d.ts.map +1 -0
- package/dist/cli/convoy/lessons.test.js +135 -0
- package/dist/cli/convoy/lessons.test.js.map +1 -0
- package/dist/cli/convoy/lock.d.ts +13 -0
- package/dist/cli/convoy/lock.d.ts.map +1 -0
- package/dist/cli/convoy/lock.js +88 -0
- package/dist/cli/convoy/lock.js.map +1 -0
- package/dist/cli/convoy/lock.test.d.ts +2 -0
- package/dist/cli/convoy/lock.test.d.ts.map +1 -0
- package/dist/cli/convoy/lock.test.js +136 -0
- package/dist/cli/convoy/lock.test.js.map +1 -0
- package/dist/cli/convoy/log-merge.test.d.ts +2 -0
- package/dist/cli/convoy/log-merge.test.d.ts.map +1 -0
- package/dist/cli/convoy/log-merge.test.js +147 -0
- package/dist/cli/convoy/log-merge.test.js.map +1 -0
- package/dist/cli/convoy/merge.d.ts +4 -0
- package/dist/cli/convoy/merge.d.ts.map +1 -1
- package/dist/cli/convoy/merge.js +18 -1
- package/dist/cli/convoy/merge.js.map +1 -1
- package/dist/cli/convoy/merge.test.js +6 -7
- package/dist/cli/convoy/merge.test.js.map +1 -1
- package/dist/cli/convoy/partition.d.ts +51 -0
- package/dist/cli/convoy/partition.d.ts.map +1 -0
- package/dist/cli/convoy/partition.js +186 -0
- package/dist/cli/convoy/partition.js.map +1 -0
- package/dist/cli/convoy/partition.test.d.ts +2 -0
- package/dist/cli/convoy/partition.test.d.ts.map +1 -0
- package/dist/cli/convoy/partition.test.js +315 -0
- package/dist/cli/convoy/partition.test.js.map +1 -0
- package/dist/cli/convoy/pipeline.test.js +6 -0
- package/dist/cli/convoy/pipeline.test.js.map +1 -1
- package/dist/cli/convoy/store.d.ts +99 -7
- package/dist/cli/convoy/store.d.ts.map +1 -1
- package/dist/cli/convoy/store.js +764 -31
- package/dist/cli/convoy/store.js.map +1 -1
- package/dist/cli/convoy/store.test.js +1810 -18
- package/dist/cli/convoy/store.test.js.map +1 -1
- package/dist/cli/convoy/types.d.ts +427 -5
- package/dist/cli/convoy/types.d.ts.map +1 -1
- package/dist/cli/convoy/types.js +42 -1
- package/dist/cli/convoy/types.js.map +1 -1
- package/dist/cli/log.d.ts +11 -0
- package/dist/cli/log.d.ts.map +1 -1
- package/dist/cli/log.js +114 -2
- package/dist/cli/log.js.map +1 -1
- package/dist/cli/run/adapters/claude.d.ts +2 -0
- package/dist/cli/run/adapters/claude.d.ts.map +1 -1
- package/dist/cli/run/adapters/claude.js +89 -49
- package/dist/cli/run/adapters/claude.js.map +1 -1
- package/dist/cli/run/adapters/claude.test.d.ts +2 -0
- package/dist/cli/run/adapters/claude.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/claude.test.js +205 -0
- package/dist/cli/run/adapters/claude.test.js.map +1 -0
- package/dist/cli/run/adapters/copilot.d.ts +1 -0
- package/dist/cli/run/adapters/copilot.d.ts.map +1 -1
- package/dist/cli/run/adapters/copilot.js +84 -46
- package/dist/cli/run/adapters/copilot.js.map +1 -1
- package/dist/cli/run/adapters/copilot.test.d.ts +2 -0
- package/dist/cli/run/adapters/copilot.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/copilot.test.js +195 -0
- package/dist/cli/run/adapters/copilot.test.js.map +1 -0
- package/dist/cli/run/adapters/cursor.d.ts +1 -0
- package/dist/cli/run/adapters/cursor.d.ts.map +1 -1
- package/dist/cli/run/adapters/cursor.js +83 -47
- package/dist/cli/run/adapters/cursor.js.map +1 -1
- package/dist/cli/run/adapters/cursor.test.d.ts +2 -0
- package/dist/cli/run/adapters/cursor.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/cursor.test.js +129 -0
- package/dist/cli/run/adapters/cursor.test.js.map +1 -0
- package/dist/cli/run/adapters/opencode.d.ts +1 -0
- package/dist/cli/run/adapters/opencode.d.ts.map +1 -1
- package/dist/cli/run/adapters/opencode.js +81 -47
- package/dist/cli/run/adapters/opencode.js.map +1 -1
- package/dist/cli/run/adapters/opencode.test.d.ts +2 -0
- package/dist/cli/run/adapters/opencode.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/opencode.test.js +119 -0
- package/dist/cli/run/adapters/opencode.test.js.map +1 -0
- package/dist/cli/run/executor.js +1 -1
- package/dist/cli/run/executor.js.map +1 -1
- package/dist/cli/run/schema.d.ts.map +1 -1
- package/dist/cli/run/schema.js +245 -4
- package/dist/cli/run/schema.js.map +1 -1
- package/dist/cli/run/schema.test.js +669 -0
- package/dist/cli/run/schema.test.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +362 -22
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/types.d.ts +85 -2
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/cli/watch.d.ts +15 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +279 -0
- package/dist/cli/watch.js.map +1 -0
- package/package.json +5 -1
- package/src/cli/agents.ts +177 -0
- package/src/cli/baselines.ts +143 -0
- package/src/cli/convoy/TELEMETRY.md +203 -0
- package/src/cli/convoy/dashboard-types.ts +141 -0
- package/src/cli/convoy/engine.test.ts +1937 -70
- package/src/cli/convoy/engine.ts +2350 -40
- package/src/cli/convoy/event-schemas.ts +195 -0
- package/src/cli/convoy/events.test.ts +384 -39
- package/src/cli/convoy/events.ts +202 -16
- package/src/cli/convoy/expertise.test.ts +128 -0
- package/src/cli/convoy/expertise.ts +163 -0
- package/src/cli/convoy/export.test.ts +1 -0
- package/src/cli/convoy/formula.test.ts +405 -0
- package/src/cli/convoy/formula.ts +174 -0
- package/src/cli/convoy/gates.test.ts +1169 -0
- package/src/cli/convoy/gates.ts +774 -0
- package/src/cli/convoy/health.test.ts +64 -2
- package/src/cli/convoy/health.ts +80 -2
- package/src/cli/convoy/issues.test.ts +143 -0
- package/src/cli/convoy/issues.ts +136 -0
- package/src/cli/convoy/knowledge.test.ts +101 -0
- package/src/cli/convoy/knowledge.ts +132 -0
- package/src/cli/convoy/lessons.test.ts +188 -0
- package/src/cli/convoy/lessons.ts +164 -0
- package/src/cli/convoy/lock.test.ts +181 -0
- package/src/cli/convoy/lock.ts +103 -0
- package/src/cli/convoy/log-merge.test.ts +179 -0
- package/src/cli/convoy/merge.test.ts +6 -7
- package/src/cli/convoy/merge.ts +19 -1
- package/src/cli/convoy/partition.test.ts +423 -0
- package/src/cli/convoy/partition.ts +232 -0
- package/src/cli/convoy/pipeline.test.ts +6 -0
- package/src/cli/convoy/store.test.ts +2041 -20
- package/src/cli/convoy/store.ts +945 -46
- package/src/cli/convoy/types.ts +278 -4
- package/src/cli/log.ts +120 -2
- package/src/cli/run/adapters/claude.test.ts +234 -0
- package/src/cli/run/adapters/claude.ts +45 -5
- package/src/cli/run/adapters/copilot.test.ts +224 -0
- package/src/cli/run/adapters/copilot.ts +34 -4
- package/src/cli/run/adapters/cursor.test.ts +144 -0
- package/src/cli/run/adapters/cursor.ts +33 -2
- package/src/cli/run/adapters/opencode.test.ts +135 -0
- package/src/cli/run/adapters/opencode.ts +30 -2
- package/src/cli/run/executor.ts +1 -1
- package/src/cli/run/schema.test.ts +758 -0
- package/src/cli/run/schema.ts +300 -25
- package/src/cli/run.ts +341 -21
- package/src/cli/types.ts +86 -1
- package/src/cli/watch.ts +298 -0
- package/src/dashboard/dist/_astro/{index.DtnyD8a5.css → index.6L3_HsPT.css} +1 -1
- package/src/dashboard/dist/data/.gitkeep +0 -0
- package/src/dashboard/dist/data/convoy-list.json +1 -0
- package/src/dashboard/dist/data/overall-stats.json +24 -0
- package/src/dashboard/dist/index.html +701 -3
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/public/data/.gitkeep +0 -0
- package/src/dashboard/public/data/convoy-list.json +1 -0
- package/src/dashboard/public/data/overall-stats.json +24 -0
- package/src/dashboard/scripts/etl.test.ts +210 -0
- package/src/dashboard/scripts/etl.ts +108 -0
- package/src/dashboard/scripts/integration-test.ts +504 -0
- package/src/dashboard/src/pages/index.astro +854 -15
- package/src/dashboard/src/styles/dashboard.css +557 -1
- package/src/orchestrator/prompts/generate-convoy.prompt.md +212 -13
package/src/cli/watch.ts
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
3
|
+
import type { TaskSpec, AgentAdapter } from './types.js'
|
|
4
|
+
import type { ConvoyResult } from './convoy/engine.js'
|
|
5
|
+
import type { WatchConfig } from './convoy/types.js'
|
|
6
|
+
import { parseTaskSpecText, parseYaml } from './run/schema.js'
|
|
7
|
+
import { c } from './prompt.js'
|
|
8
|
+
|
|
9
|
+
export interface WatchLoopOptions {
|
|
10
|
+
spec: TaskSpec
|
|
11
|
+
specText: string
|
|
12
|
+
specPath: string
|
|
13
|
+
adapter: AgentAdapter
|
|
14
|
+
verbose: boolean
|
|
15
|
+
pidPath: string
|
|
16
|
+
clearScratchpad: boolean
|
|
17
|
+
watchConfigPath: string | null
|
|
18
|
+
printResult: (result: ConvoyResult) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseCronField(field: string, min: number, max: number): number[] {
|
|
22
|
+
if (field === '*') {
|
|
23
|
+
const all: number[] = []
|
|
24
|
+
for (let i = min; i <= max; i++) all.push(i)
|
|
25
|
+
return all
|
|
26
|
+
}
|
|
27
|
+
const values: number[] = []
|
|
28
|
+
for (const part of field.split(',')) {
|
|
29
|
+
if (part.includes('/')) {
|
|
30
|
+
const [rangeStr, stepStr] = part.split('/')
|
|
31
|
+
const step = parseInt(stepStr, 10)
|
|
32
|
+
if (!Number.isFinite(step) || step < 1) continue
|
|
33
|
+
let start = min
|
|
34
|
+
let end = max
|
|
35
|
+
if (rangeStr !== '*') {
|
|
36
|
+
const [s, e] = rangeStr.split('-').map(Number)
|
|
37
|
+
start = s
|
|
38
|
+
end = e ?? s
|
|
39
|
+
}
|
|
40
|
+
for (let i = start; i <= end; i += step) values.push(i)
|
|
41
|
+
} else if (part.includes('-')) {
|
|
42
|
+
const [s, e] = part.split('-').map(Number)
|
|
43
|
+
for (let i = s; i <= e; i++) values.push(i)
|
|
44
|
+
} else {
|
|
45
|
+
const n = parseInt(part, 10)
|
|
46
|
+
if (Number.isFinite(n)) values.push(n)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return values
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function cronMatches(schedule: string, date: Date): boolean {
|
|
53
|
+
const parts = schedule.trim().split(/\s+/)
|
|
54
|
+
if (parts.length !== 5) return false
|
|
55
|
+
const [minF, hourF, domF, monF, dowF] = parts
|
|
56
|
+
const minute = parseCronField(minF, 0, 59)
|
|
57
|
+
const hour = parseCronField(hourF, 0, 23)
|
|
58
|
+
const dom = parseCronField(domF, 1, 31)
|
|
59
|
+
const mon = parseCronField(monF, 1, 12)
|
|
60
|
+
const dow = parseCronField(dowF, 0, 6)
|
|
61
|
+
return (
|
|
62
|
+
minute.includes(date.getMinutes()) &&
|
|
63
|
+
hour.includes(date.getHours()) &&
|
|
64
|
+
dom.includes(date.getDate()) &&
|
|
65
|
+
mon.includes(date.getMonth() + 1) &&
|
|
66
|
+
dow.includes(date.getDay())
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function sleep(ms: number): Promise<void> {
|
|
71
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function watchLoop(options: WatchLoopOptions): Promise<void> {
|
|
75
|
+
const {
|
|
76
|
+
spec,
|
|
77
|
+
specText,
|
|
78
|
+
specPath,
|
|
79
|
+
adapter,
|
|
80
|
+
verbose,
|
|
81
|
+
pidPath,
|
|
82
|
+
clearScratchpad,
|
|
83
|
+
watchConfigPath,
|
|
84
|
+
printResult,
|
|
85
|
+
} = options
|
|
86
|
+
|
|
87
|
+
// Resolve watch config — from --watch-config file, spec.watch, or default file-change
|
|
88
|
+
let watchConfig: WatchConfig
|
|
89
|
+
if (watchConfigPath) {
|
|
90
|
+
const raw = readFileSync(watchConfigPath, 'utf8')
|
|
91
|
+
const parsed = parseYaml(raw) as Record<string, unknown>
|
|
92
|
+
watchConfig = parsed as unknown as WatchConfig
|
|
93
|
+
} else if (spec.watch) {
|
|
94
|
+
watchConfig = spec.watch
|
|
95
|
+
} else {
|
|
96
|
+
// Default: file-change trigger on the spec file itself
|
|
97
|
+
watchConfig = {
|
|
98
|
+
triggers: [{ type: 'file-change', glob: specPath, debounce_ms: 500 }],
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Write PID file
|
|
103
|
+
mkdirSync(dirname(pidPath), { recursive: true })
|
|
104
|
+
writeFileSync(pidPath, String(process.pid), 'utf8')
|
|
105
|
+
|
|
106
|
+
let shuttingDown = false
|
|
107
|
+
let currentRun: Promise<ConvoyResult> | null = null
|
|
108
|
+
let cycleNumber = 0
|
|
109
|
+
|
|
110
|
+
// Graceful shutdown handlers
|
|
111
|
+
function onShutdownSignal(): void {
|
|
112
|
+
if (shuttingDown) return
|
|
113
|
+
shuttingDown = true
|
|
114
|
+
console.log(`\n ${c.yellow('\u26a0')} Watch mode shutting down...`)
|
|
115
|
+
// If a run is in progress, we let it finish
|
|
116
|
+
if (!currentRun) {
|
|
117
|
+
cleanup()
|
|
118
|
+
process.exit(0)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
process.on('SIGTERM', onShutdownSignal)
|
|
123
|
+
process.on('SIGINT', onShutdownSignal)
|
|
124
|
+
|
|
125
|
+
function cleanup(): void {
|
|
126
|
+
try { unlinkSync(pidPath) } catch { /* ignore */ }
|
|
127
|
+
process.removeListener('SIGTERM', onShutdownSignal)
|
|
128
|
+
process.removeListener('SIGINT', onShutdownSignal)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Emit watch_started
|
|
132
|
+
const { createConvoyStore } = await import('./convoy/store.js')
|
|
133
|
+
const { createEventEmitter } = await import('./convoy/events.js')
|
|
134
|
+
const dbPath = resolve(process.cwd(), '.opencastle', 'convoy.db')
|
|
135
|
+
mkdirSync(dirname(dbPath), { recursive: true })
|
|
136
|
+
const evtStore = createConvoyStore(dbPath)
|
|
137
|
+
const ndjsonPath = resolve(process.cwd(), '.opencastle', 'logs', 'convoy-events.ndjson')
|
|
138
|
+
mkdirSync(dirname(ndjsonPath), { recursive: true })
|
|
139
|
+
const watchEvents = createEventEmitter(evtStore, { ndjsonPath })
|
|
140
|
+
|
|
141
|
+
const triggerTypes = watchConfig.triggers.map(t => t.type).join(',')
|
|
142
|
+
watchEvents.emit('watch_started', { trigger_type: triggerTypes, pid: process.pid })
|
|
143
|
+
|
|
144
|
+
// Clear scratchpad if requested
|
|
145
|
+
if (clearScratchpad || watchConfig.clear_scratchpad) {
|
|
146
|
+
evtStore.clearScratchpad()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log(`\n ${c.cyan('\ud83d\udc41')} Watch mode active (PID: ${process.pid})`)
|
|
150
|
+
console.log(` Triggers: ${watchConfig.triggers.map(t => t.type).join(', ')}`)
|
|
151
|
+
|
|
152
|
+
// Set up trigger watchers
|
|
153
|
+
let triggerFired = false
|
|
154
|
+
let triggerSource = ''
|
|
155
|
+
const triggerCleanups: Array<() => void> = []
|
|
156
|
+
|
|
157
|
+
for (const trigger of watchConfig.triggers) {
|
|
158
|
+
if (trigger.type === 'file-change') {
|
|
159
|
+
const { watch } = await import('node:fs')
|
|
160
|
+
const debounceMs = trigger.debounce_ms ?? 500
|
|
161
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined
|
|
162
|
+
const globPattern = trigger.glob ?? '**/*'
|
|
163
|
+
const watcher = watch(resolve(process.cwd()), { recursive: true }, (_event, filename) => {
|
|
164
|
+
if (!filename) return
|
|
165
|
+
const globStr = globPattern
|
|
166
|
+
let matches = false
|
|
167
|
+
if (globStr.includes('*')) {
|
|
168
|
+
// Convert simple glob to regex
|
|
169
|
+
const re = new RegExp('^' + globStr.replace(/\./g, '\\.').replace(/\*\*/g, '\u29bf').replace(/\*/g, '[^/]*').replace(/\u29bf/g, '.*') + '$')
|
|
170
|
+
matches = re.test(filename)
|
|
171
|
+
} else {
|
|
172
|
+
matches = filename === globStr || filename.endsWith('/' + globStr)
|
|
173
|
+
}
|
|
174
|
+
if (matches) {
|
|
175
|
+
if (debounceTimer) clearTimeout(debounceTimer)
|
|
176
|
+
debounceTimer = setTimeout(() => {
|
|
177
|
+
triggerFired = true
|
|
178
|
+
triggerSource = `file-change:${filename}`
|
|
179
|
+
}, debounceMs)
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
triggerCleanups.push(() => { watcher.close(); if (debounceTimer) clearTimeout(debounceTimer) })
|
|
183
|
+
} else if (trigger.type === 'cron') {
|
|
184
|
+
// Poll every 30s to check if cron matches
|
|
185
|
+
let lastFired = -1
|
|
186
|
+
const interval = setInterval(() => {
|
|
187
|
+
const now = new Date()
|
|
188
|
+
const minuteKey = now.getFullYear() * 525600 + now.getMonth() * 43800 + now.getDate() * 1440 + now.getHours() * 60 + now.getMinutes()
|
|
189
|
+
if (minuteKey !== lastFired && cronMatches(trigger.schedule!, now)) {
|
|
190
|
+
lastFired = minuteKey
|
|
191
|
+
triggerFired = true
|
|
192
|
+
triggerSource = `cron:${trigger.schedule}`
|
|
193
|
+
}
|
|
194
|
+
}, 30_000)
|
|
195
|
+
triggerCleanups.push(() => clearInterval(interval))
|
|
196
|
+
} else if (trigger.type === 'git-push') {
|
|
197
|
+
// Poll git remote every 60s
|
|
198
|
+
let lastRef = ''
|
|
199
|
+
const { execFile: execFileCb } = await import('node:child_process')
|
|
200
|
+
const { promisify } = await import('node:util')
|
|
201
|
+
const execFileAsync = promisify(execFileCb)
|
|
202
|
+
const interval = setInterval(async () => {
|
|
203
|
+
try {
|
|
204
|
+
await execFileAsync('git', ['fetch', '--quiet'], { cwd: process.cwd() })
|
|
205
|
+
const { stdout } = await execFileAsync('git', ['rev-parse', 'FETCH_HEAD'], { cwd: process.cwd() })
|
|
206
|
+
const ref = stdout.trim()
|
|
207
|
+
if (lastRef && ref !== lastRef) {
|
|
208
|
+
triggerFired = true
|
|
209
|
+
triggerSource = `git-push:${ref.slice(0, 8)}`
|
|
210
|
+
}
|
|
211
|
+
lastRef = ref
|
|
212
|
+
} catch { /* ignore fetch errors */ }
|
|
213
|
+
}, 60_000)
|
|
214
|
+
triggerCleanups.push(() => clearInterval(interval))
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Main watch loop
|
|
219
|
+
try {
|
|
220
|
+
while (!shuttingDown) {
|
|
221
|
+
// Wait for a trigger
|
|
222
|
+
while (!triggerFired && !shuttingDown) {
|
|
223
|
+
await sleep(500)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (shuttingDown) break
|
|
227
|
+
triggerFired = false
|
|
228
|
+
|
|
229
|
+
cycleNumber++
|
|
230
|
+
console.log(`\n ${c.cyan('\u27f3')} Watch cycle ${cycleNumber} triggered by: ${triggerSource}`)
|
|
231
|
+
|
|
232
|
+
// Retention cleanup of agent identities
|
|
233
|
+
try {
|
|
234
|
+
evtStore.deleteAgentIdentitiesOlderThan(90)
|
|
235
|
+
} catch { /* non-critical */ }
|
|
236
|
+
|
|
237
|
+
// Scratchpad retention cleanup
|
|
238
|
+
if (watchConfig.scratchpad_retention_days) {
|
|
239
|
+
try {
|
|
240
|
+
evtStore.clearScratchpadOlderThan(watchConfig.scratchpad_retention_days)
|
|
241
|
+
} catch { /* non-critical */ }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
watchEvents.emit('watch_cycle_start', { cycle_number: cycleNumber, triggered_by: triggerSource })
|
|
245
|
+
|
|
246
|
+
// Re-read spec from disk (may have changed since last cycle)
|
|
247
|
+
let cycleSpec: TaskSpec
|
|
248
|
+
let cycleSpecText: string
|
|
249
|
+
try {
|
|
250
|
+
cycleSpecText = readFileSync(specPath, 'utf8')
|
|
251
|
+
cycleSpec = parseTaskSpecText(cycleSpecText)
|
|
252
|
+
// Apply overrides from original spec
|
|
253
|
+
if (spec.concurrency !== 1) cycleSpec.concurrency = spec.concurrency
|
|
254
|
+
if (spec.adapter) cycleSpec.adapter = spec.adapter
|
|
255
|
+
if (verbose) cycleSpec._verbose = true
|
|
256
|
+
} catch (err) {
|
|
257
|
+
console.error(` ${c.red('\u2717')} Failed to re-read spec: ${(err as Error).message}`)
|
|
258
|
+
watchEvents.emit('watch_cycle_end', { cycle_number: cycleNumber, status: 'error' })
|
|
259
|
+
continue
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Run convoy
|
|
263
|
+
const { createConvoyEngine } = await import('./convoy/engine.js')
|
|
264
|
+
const cycleEngine = createConvoyEngine({
|
|
265
|
+
spec: cycleSpec,
|
|
266
|
+
specYaml: cycleSpecText,
|
|
267
|
+
adapter,
|
|
268
|
+
verbose,
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
let cycleResult: ConvoyResult
|
|
272
|
+
try {
|
|
273
|
+
currentRun = cycleEngine.run()
|
|
274
|
+
cycleResult = await currentRun
|
|
275
|
+
currentRun = null
|
|
276
|
+
} catch (err) {
|
|
277
|
+
currentRun = null
|
|
278
|
+
console.error(` ${c.red('\u2717')} Cycle ${cycleNumber} failed: ${(err as Error).message}`)
|
|
279
|
+
watchEvents.emit('watch_cycle_end', { cycle_number: cycleNumber, status: 'error' })
|
|
280
|
+
continue
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
printResult(cycleResult)
|
|
284
|
+
watchEvents.emit('watch_cycle_end', { cycle_number: cycleNumber, status: cycleResult.status })
|
|
285
|
+
|
|
286
|
+
if (shuttingDown) break
|
|
287
|
+
}
|
|
288
|
+
} finally {
|
|
289
|
+
// Clean up
|
|
290
|
+
watchEvents.emit('watch_stopped', { reason: shuttingDown ? 'signal' : 'exit' })
|
|
291
|
+
watchEvents.close()
|
|
292
|
+
evtStore.close()
|
|
293
|
+
for (const fn of triggerCleanups) {
|
|
294
|
+
try { fn() } catch { /* ignore */ }
|
|
295
|
+
}
|
|
296
|
+
cleanup()
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--bg-primary: #0a0a0f;--bg-secondary: #111118;--bg-tertiary: #1a1a24;--bg-card: rgba(255, 255, 255, .03);--bg-card-hover: rgba(255, 255, 255, .06);--text-primary: #f0f0f5;--text-secondary: #8a8a9a;--text-tertiary: #5a5a6e;--text-accent: #a78bfa;--gradient-accent: linear-gradient(135deg, #a78bfa 0%, #6366f1 50%, #3b82f6 100%);--gradient-glow: radial-gradient(ellipse 800px 400px at 50% 0%, rgba(99, 102, 241, .12) 0%, transparent 70%);--border-color: rgba(255, 255, 255, .06);--border-accent: rgba(167, 139, 250, .3);--color-success: #22c55e;--color-partial: #f59e0b;--color-failed: #ef4444;--color-redirected: #64748b;--color-premium: #f59e0b;--color-standard: #a78bfa;--color-utility: #3b82f6;--color-economy: #64748b;--accent-blue: #3b82f6;--accent-purple: #a78bfa;--accent-indigo: #6366f1;--max-width: 1280px;--transition-fast: .15s cubic-bezier(.4, 0, .2, 1);--transition-base: .3s cubic-bezier(.4, 0, .2, 1)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Inter,Roboto,Helvetica,Arial,sans-serif;background-color:var(--bg-primary);color:var(--text-primary);line-height:1.6;overflow-x:hidden;min-height:100vh}.dash-header{position:sticky;top:0;z-index:50;background:#0a0a0fd9;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border-color)}.dash-header__inner{max-width:var(--max-width);margin:0 auto;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between}.dash-header__brand{display:flex;align-items:center;gap:10px}.dash-header__icon{width:32px;height:32px;border-radius:8px;object-fit:contain}.dash-header__title{font-size:1rem;font-weight:600;color:var(--text-primary)}.dash-layout{display:flex;max-width:var(--max-width);margin:0 auto;position:relative}.dash-sidebar{position:sticky;top:56px;height:calc(100vh - 56px);width:180px;flex-shrink:0;padding:24px 0 24px 24px;overflow-y:auto;display:none}@media(min-width:1024px){.dash-sidebar{display:block}}.dash-sidebar__list{list-style:none;display:flex;flex-direction:column;gap:2px}.dash-sidebar__link{display:block;padding:8px 16px;font-size:.8125rem;font-weight:500;color:var(--text-tertiary);text-decoration:none;border-radius:8px;transition:color var(--transition-fast),background var(--transition-fast)}.dash-sidebar__link:hover{color:var(--text-secondary);background:#ffffff0a}.dash-sidebar__link--active{color:var(--text-accent);background:#a78bfa14;font-weight:600}.dash-main{flex:1;min-width:0;max-width:var(--max-width);margin:0 auto;padding:24px;display:flex;flex-direction:column;gap:20px;position:relative}.dash-main:before{content:"";position:fixed;top:0;left:50%;transform:translate(-50%);width:100%;height:600px;background:var(--gradient-glow);pointer-events:none;z-index:0}.dash-main>*{position:relative;z-index:1}[data-nav-section]{scroll-margin-top:72px}.kpi-row{display:grid;grid-template-columns:1fr;gap:12px}@media(min-width:480px){.kpi-row{grid-template-columns:repeat(2,1fr)}}@media(min-width:960px){.kpi-row{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}}.kpi-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;padding:20px 24px;display:flex;flex-direction:column;gap:4px;transition:border-color var(--transition-fast)}.kpi-card:hover{border-color:#ffffff1a}.kpi-card__label{font-size:.75rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.kpi-card__value{font-size:2rem;font-weight:700;color:var(--text-primary);line-height:1.2;letter-spacing:-.02em}.kpi-card__sub{font-size:.75rem;color:var(--text-secondary);display:flex;align-items:center;gap:4px}.kpi-trend{font-weight:600}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-failed)}.kpi-trend--neutral{color:var(--text-tertiary)}.chart-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;overflow:hidden;transition:border-color var(--transition-fast)}.chart-card:hover{border-color:#ffffff1a}.chart-card__header{padding:20px 24px 8px}.chart-card__title{font-size:.9375rem;font-weight:600;color:var(--text-primary)}.chart-card__desc{font-size:.75rem;color:var(--text-tertiary);margin-top:2px}.chart-card__body{padding:16px 24px 24px;min-height:120px}.chart-card__body--table{padding:0}.charts-row{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width:768px){.charts-row{grid-template-columns:repeat(2,1fr)}}.bar-row{display:flex;align-items:center;gap:12px;padding:6px 0}.bar-row+.bar-row{border-top:1px solid rgba(255,255,255,.03)}.bar-label{font-size:.8125rem;color:var(--text-secondary);width:130px;flex-shrink:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-track{flex:1;height:24px;background:var(--bg-tertiary);border-radius:6px;display:flex;overflow:hidden}.bar-segment{height:100%;transition:width .8s cubic-bezier(.4,0,.2,1);min-width:0}.bar--success{background:var(--color-success)}.bar--partial{background:var(--color-partial)}.bar--failed{background:var(--color-failed)}.bar--premium{background:var(--color-premium)}.bar--standard{background:var(--color-standard)}.bar--utility{background:var(--color-utility)}.bar--economy{background:var(--color-economy)}.bar--accent{background:var(--accent-blue)}.bar-value{font-size:.8125rem;font-weight:600;color:var(--text-primary);width:36px;text-align:right;flex-shrink:0;font-variant-numeric:tabular-nums}.donut-container{display:flex;align-items:center;justify-content:center;gap:32px;flex-wrap:wrap}.donut-wrap{position:relative;width:180px;height:180px;flex-shrink:0}.donut-svg{width:100%;height:100%}.donut-svg circle{transition:stroke-dasharray .8s cubic-bezier(.4,0,.2,1),stroke-dashoffset .8s cubic-bezier(.4,0,.2,1)}.donut-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center}.donut-total{display:block;font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.donut-total-label{display:block;font-size:.6875rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.08em;margin-top:2px}.donut-legend{display:flex;flex-direction:column;gap:10px}.legend-item{display:flex;align-items:center;gap:8px;font-size:.8125rem}.legend-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0}.legend-name{color:var(--text-secondary);text-transform:capitalize}.legend-count{color:var(--text-tertiary);font-variant-numeric:tabular-nums;margin-left:auto}.timeline-svg{width:100%;height:auto;display:block}.timeline-svg text{font-family:inherit}.timeline-legend{display:flex;gap:16px;justify-content:center;margin-top:12px}.timeline-legend__item{display:flex;align-items:center;gap:6px;font-size:.75rem;color:var(--text-tertiary)}.timeline-legend__dot{width:8px;height:8px;border-radius:2px}.pipeline{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:8px 0}.pipeline-stage{flex:1;min-width:140px;display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 12px;position:relative}.pipeline-stage:not(:last-child):after{content:"";position:absolute;right:-1px;top:50%;transform:translateY(-50%);width:2px;height:40%;background:var(--border-color)}.pipeline-stage__icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:1rem}.pipeline-stage__icon--pending{background:#64748b26;color:#94a3b8;border:1px solid rgba(100,116,139,.2)}.pipeline-stage__icon--active{background:#3b82f626;color:#60a5fa;border:1px solid rgba(59,130,246,.3);animation:pulse-glow 2s ease-in-out infinite}.pipeline-stage__icon--review{background:#f59e0b26;color:#fbbf24;border:1px solid rgba(245,158,11,.3)}.pipeline-stage__icon--done{background:#22c55e26;color:#4ade80;border:1px solid rgba(34,197,94,.3)}@keyframes pulse-glow{0%,to{box-shadow:0 0 #3b82f633}50%{box-shadow:0 0 12px 4px #3b82f626}}.pipeline-stage__count{font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.pipeline-stage__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.04em;font-weight:500}.pipeline-arrow{display:flex;align-items:center;color:var(--text-tertiary);font-size:1.25rem;padding:0 4px;flex-shrink:0}.exec-log{display:flex;flex-direction:column}.exec-step{display:flex;gap:16px;padding:14px 0;position:relative}.exec-step+.exec-step{border-top:1px solid rgba(255,255,255,.03)}.exec-step__indicator{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:32px}.exec-step__dot{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.6875rem;font-weight:700;flex-shrink:0}.exec-step__dot--success{background:#22c55e26;color:var(--color-success);border:1.5px solid rgba(34,197,94,.3)}.exec-step__dot--partial{background:#f59e0b26;color:var(--color-partial);border:1.5px solid rgba(245,158,11,.3)}.exec-step__dot--failed{background:#ef444426;color:var(--color-failed);border:1.5px solid rgba(239,68,68,.3)}.exec-step__line{flex:1;width:1.5px;background:var(--border-color);margin-top:4px}.exec-step__content{flex:1;min-width:0}.exec-step__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.exec-step__agent{font-size:.875rem;font-weight:600;color:var(--text-primary)}.exec-step__badge{display:inline-flex;align-items:center;padding:2px 8px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.exec-step__badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.exec-step__badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.exec-step__badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.exec-step__task{font-size:.8125rem;color:var(--text-secondary);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.exec-step__meta{display:flex;gap:16px;margin-top:6px;font-size:.6875rem;color:var(--text-tertiary)}.exec-step__meta-item{display:flex;align-items:center;gap:4px}.panel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px}.panel-item{background:var(--bg-tertiary);border-radius:8px;padding:16px;display:flex;flex-direction:column;gap:8px;border:1px solid transparent;transition:border-color var(--transition-fast)}.panel-item:hover{border-color:var(--border-color)}.panel-item__header{display:flex;align-items:center;justify-content:space-between}.panel-item__key{font-size:.8125rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.panel-item__verdict{font-size:.6875rem;font-weight:700;padding:2px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.04em}.panel-item__verdict--pass{background:#22c55e26;color:var(--color-success)}.panel-item__verdict--block{background:#ef444426;color:var(--color-failed)}.panel-item__votes{display:flex;gap:4px}.panel-item__vote{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.625rem;font-weight:700}.panel-item__vote--pass{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.panel-item__vote--block{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.panel-item__fixes{font-size:.6875rem;color:var(--text-tertiary)}.panel-item__meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;padding-top:8px;border-top:1px solid var(--border-color)}.panel-item__meta-item{font-size:.625rem;color:var(--text-tertiary);white-space:nowrap}.sessions-table{width:100%;border-collapse:collapse;font-size:.8125rem}.sessions-table thead{position:sticky;top:0}.sessions-table th{padding:12px 16px;font-size:.6875rem;font-weight:600;color:var(--text-tertiary);text-align:left;text-transform:uppercase;letter-spacing:.06em;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color)}.sessions-table th:last-child,.sessions-table td:last-child{text-align:right}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5){text-align:right}.sessions-table td{padding:10px 16px;color:var(--text-secondary);border-bottom:1px solid rgba(255,255,255,.03);white-space:nowrap}.sessions-table tr:hover td{background:#ffffff05}.sessions-table .td-agent{font-weight:500;color:var(--text-primary)}.sessions-table .td-task{max-width:260px;overflow:hidden;text-overflow:ellipsis}.outcome-badge{display:inline-flex;align-items:center;padding:3px 10px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.outcome-badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.outcome-badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.outcome-badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.td-num{font-variant-numeric:tabular-nums;text-align:right}.td-issue{font-size:.75rem;color:var(--text-accent);font-weight:500;font-variant-numeric:tabular-nums}.loading-skeleton{display:flex;align-items:center;justify-content:center;min-height:200px;color:var(--text-tertiary);font-size:.8125rem}.loading-skeleton:after{content:"Loading data…";animation:fade-pulse 1.5s ease-in-out infinite}@keyframes fade-pulse{0%,to{opacity:.4}50%{opacity:1}}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;text-align:center;gap:12px}.empty-state__icon{font-size:2rem;opacity:.4}.empty-state__text{font-size:.875rem;color:var(--text-tertiary);max-width:320px}.empty-state--enhanced{padding:56px 32px;gap:16px;border:1px dashed rgba(167,139,250,.15);border-radius:12px;background:radial-gradient(ellipse 300px 200px at 50% 30%,rgba(99,102,241,.04) 0%,transparent 70%),var(--bg-tertiary);position:relative;overflow:hidden}.empty-state--enhanced:before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 23px,rgba(255,255,255,.015) 23px,rgba(255,255,255,.015) 24px);pointer-events:none}.empty-state__icon-wrap{width:64px;height:64px;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#a78bfa0f;border:1px solid rgba(167,139,250,.12);color:var(--text-accent);animation:empty-breathe 4s ease-in-out infinite}@keyframes empty-breathe{0%,to{box-shadow:0 0 #a78bfa14;transform:scale(1)}50%{box-shadow:0 0 20px 4px #a78bfa0f;transform:scale(1.03)}}.empty-state__title{font-size:.9375rem;font-weight:600;color:var(--text-secondary);letter-spacing:-.01em}.empty-state__desc{font-size:.8125rem;color:var(--text-tertiary);max-width:380px;line-height:1.55}.kpi-card__hint{color:var(--text-tertiary);font-style:italic;font-size:.6875rem}.kpi-row--empty .kpi-card{border-style:dashed;border-color:#ffffff0a}.kpi-row--empty .kpi-card__value{color:var(--text-tertiary);opacity:.5}.welcome-banner{position:relative;background:var(--bg-secondary);border:1px solid transparent;border-radius:16px;padding:48px 40px;overflow:hidden;z-index:1}.welcome-banner:before{content:"";position:absolute;inset:-1px;border-radius:16px;padding:1px;background:linear-gradient(135deg,#a78bfa4d,#6366f126,#3b82f61a 60%,#a78bfa33);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none;z-index:0}.welcome-banner__glow{position:absolute;top:-60px;left:50%;transform:translate(-50%);width:500px;height:300px;background:radial-gradient(ellipse at center,rgba(167,139,250,.08) 0%,rgba(99,102,241,.04) 40%,transparent 70%);pointer-events:none;z-index:0}.welcome-banner__content{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:20px}.welcome-banner__icon{width:72px;height:72px;display:flex;align-items:center;justify-content:center;border-radius:20px;background:#a78bfa14;border:1px solid rgba(167,139,250,.15);color:var(--text-accent);animation:welcome-float 6s ease-in-out infinite}@keyframes welcome-float{0%,to{transform:translateY(0);box-shadow:0 8px 32px #a78bfa14}50%{transform:translateY(-6px);box-shadow:0 16px 48px #a78bfa1f}}.welcome-banner__title{font-size:1.375rem;font-weight:700;color:var(--text-primary);letter-spacing:-.02em;line-height:1.3}.welcome-banner__subtitle{font-size:.9375rem;color:var(--text-secondary);max-width:480px;line-height:1.6}.welcome-banner__steps{display:flex;gap:20px;margin-top:12px;flex-wrap:wrap;justify-content:center}.welcome-step{display:flex;align-items:flex-start;gap:12px;text-align:left;padding:16px 20px;background:#ffffff05;border:1px solid rgba(255,255,255,.05);border-radius:12px;min-width:200px;max-width:220px;transition:border-color var(--transition-fast),background var(--transition-fast)}.welcome-step:hover{border-color:#a78bfa26;background:#ffffff08}.welcome-step__num{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;color:var(--text-accent);background:#a78bfa1a;border:1px solid rgba(167,139,250,.2);flex-shrink:0}.welcome-step__text{display:flex;flex-direction:column;gap:3px}.welcome-step__text strong{font-size:.8125rem;font-weight:600;color:var(--text-primary)}.welcome-step__text span{font-size:.75rem;color:var(--text-tertiary);line-height:1.4}@media(max-width:640px){.welcome-banner{padding:32px 24px}.welcome-banner__steps{flex-direction:column;align-items:center}.welcome-step{max-width:100%;width:100%}}@keyframes slide-up{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.dash-main>*{animation:slide-up .5s ease-out backwards}.dash-main>*:nth-child(1){animation-delay:0ms}.dash-main>*:nth-child(2){animation-delay:60ms}.dash-main>*:nth-child(3){animation-delay:.12s}.dash-main>*:nth-child(4){animation-delay:.18s}.dash-main>*:nth-child(5){animation-delay:.24s}.dash-main>*:nth-child(6){animation-delay:.3s}.dash-main>*:nth-child(7){animation-delay:.36s}.dash-main>*:nth-child(8){animation-delay:.42s}.dash-main>*:nth-child(9){animation-delay:.48s}.dash-main>*:nth-child(10){animation-delay:.54s}.dash-main>*:nth-child(11){animation-delay:.6s}@media(max-width:640px){.bar-label{width:90px;font-size:.75rem}.donut-container{flex-direction:column;align-items:center}.donut-wrap{width:150px;height:150px}.pipeline{gap:0}.pipeline-stage{min-width:100px;padding:12px 8px}.panel-grid{grid-template-columns:1fr}.sessions-table th:nth-child(3),.sessions-table td:nth-child(3){display:none}}.filter-bar{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;padding:16px 20px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px}.filter-group{display:flex;flex-direction:column;gap:4px;min-width:0}.filter-label{font-size:.6875rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.filter-input,.filter-select{height:34px;padding:0 10px;font-size:.8125rem;color:var(--text-primary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;outline:none;transition:border-color var(--transition-fast);font-family:inherit}.filter-input:focus,.filter-select:focus{border-color:var(--border-accent)}.filter-input{width:140px;color-scheme:dark}.filter-select{min-width:140px;cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235a5a6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}.filter-reset{height:34px;font-size:.75rem}.dash-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;font-size:.8125rem;font-weight:500;font-family:inherit;border:none;border-radius:8px;cursor:pointer;transition:background var(--transition-fast),color var(--transition-fast)}.dash-btn--ghost{color:var(--text-secondary);background:#ffffff0f}.dash-btn--ghost:hover{color:var(--text-primary);background:#ffffff1a}.dash-header__actions{display:flex;align-items:center;gap:8px}@media(max-width:480px){.dash-header__inner{padding:0 12px}.dash-main{padding:12px;gap:12px}.kpi-card,.chart-card__header{padding:14px 16px}.chart-card__body{padding:12px 16px 16px}.filter-bar{padding:12px;gap:8px}.filter-input,.filter-select{width:100%;min-width:unset}.filter-group{flex:1 1 calc(50% - 4px)}.filter-reset{width:100%}.dash-header__title{font-size:.875rem}.exec-step__meta{flex-direction:column;gap:2px}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5),.sessions-table th:nth-child(6),.sessions-table td:nth-child(6),.sessions-table th:nth-child(7),.sessions-table td:nth-child(7),.sessions-table th:nth-child(8),.sessions-table td:nth-child(8){display:none}}@media(max-width:768px){.charts-row{grid-template-columns:1fr}.pipeline{flex-wrap:wrap;gap:8px}.pipeline-arrow{display:none}.pipeline-stage{flex:1 1 calc(50% - 4px);min-width:100px}.tier-chart .donut-container,.donut-container{flex-direction:column;align-items:center}.sessions-table{font-size:.75rem}.sessions-table th,.sessions-table td{padding:8px 6px}}.convoy-overview{display:flex;flex-wrap:wrap;gap:24px;margin-bottom:20px}.convoy-stat{display:flex;flex-direction:column;gap:4px}.convoy-stat__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.convoy-stat__value{font-size:.95rem;color:var(--text-primary)}.convoy-stat__value--error{color:var(--color-failed)}.convoy-progress{display:flex;align-items:center;gap:12px;margin-bottom:20px}.convoy-progress__bar{flex:1;height:8px;background:var(--bg-tertiary);border-radius:4px;overflow:hidden}.convoy-progress__fill{height:100%;background:var(--gradient-accent);border-radius:4px;transition:width var(--transition-base)}.convoy-progress__label{font-size:.8rem;color:var(--text-secondary);white-space:nowrap}.convoy-tasks{margin-top:8px}.convoy-chain{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:1rem 0 1.5rem;scrollbar-width:thin;scrollbar-color:var(--border-color) transparent}.convoy-chain::-webkit-scrollbar{height:4px}.convoy-chain::-webkit-scrollbar-track{background:transparent}.convoy-chain::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:2px}.convoy-chain__connector{display:flex;align-items:center;padding:0 .5rem;color:var(--text-tertiary);font-size:1.1rem;flex-shrink:0}.convoy-chain__node{display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 16px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:10px;min-width:140px;cursor:pointer;transition:background var(--transition-fast),border-color var(--transition-fast),transform var(--transition-fast),box-shadow var(--transition-fast);flex-shrink:0}.convoy-chain__node:hover{background:var(--bg-card-hover);transform:translateY(-2px);box-shadow:0 4px 12px #0000004d}.convoy-chain__node-name{font-size:.8rem;font-weight:600;color:var(--text-primary);text-align:center;word-break:break-word;max-width:120px}.convoy-chain__node-meta{font-size:.72rem;color:var(--text-tertiary);text-align:center}.convoy-chain__node--active{border-color:var(--accent-purple);box-shadow:0 0 0 1px var(--accent-purple),0 0 12px #a78bfa33;animation:convoy-pulse 2s ease-in-out infinite}.convoy-chain__node--done{border-color:#22c55e4d}.convoy-chain__node--failed{border-color:#ef44444d}.convoy-chain__node--pending{opacity:.6}@keyframes convoy-pulse{0%,to{box-shadow:0 0 0 1px var(--accent-purple),0 0 8px #a78bfa26}50%{box-shadow:0 0 0 1px var(--accent-purple),0 0 18px #a78bfa59}}@media(max-width:768px){.convoy-chain{flex-wrap:wrap;gap:8px}.convoy-chain__connector{display:none}.convoy-chain__node{flex:1 1 calc(50% - 4px);min-width:120px}}
|
|
1
|
+
:root{--bg-primary: #0a0a0f;--bg-secondary: #111118;--bg-tertiary: #1a1a24;--bg-card: rgba(255, 255, 255, .03);--bg-card-hover: rgba(255, 255, 255, .06);--text-primary: #f0f0f5;--text-secondary: #8a8a9a;--text-tertiary: #7a7a8e;--text-accent: #a78bfa;--gradient-accent: linear-gradient(135deg, #a78bfa 0%, #6366f1 50%, #3b82f6 100%);--gradient-glow: radial-gradient(ellipse 800px 400px at 50% 0%, rgba(99, 102, 241, .12) 0%, transparent 70%);--border-color: rgba(255, 255, 255, .06);--border-accent: rgba(167, 139, 250, .3);--color-success: #22c55e;--color-partial: #f59e0b;--color-failed: #ef4444;--color-redirected: #64748b;--color-premium: #f59e0b;--color-standard: #a78bfa;--color-utility: #3b82f6;--color-economy: #64748b;--accent-blue: #3b82f6;--accent-purple: #a78bfa;--accent-indigo: #6366f1;--max-width: 1280px;--transition-fast: .15s cubic-bezier(.4, 0, .2, 1);--transition-base: .3s cubic-bezier(.4, 0, .2, 1)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Inter,Roboto,Helvetica,Arial,sans-serif;background-color:var(--bg-primary);color:var(--text-primary);line-height:1.6;overflow-x:hidden;min-height:100vh}.dash-header{position:sticky;top:0;z-index:50;background:#0a0a0fd9;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border-color)}.dash-header__inner{max-width:var(--max-width);margin:0 auto;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between}.dash-header__brand{display:flex;align-items:center;gap:10px}.dash-header__icon{width:32px;height:32px;border-radius:8px;object-fit:contain}.dash-header__title{font-size:1rem;font-weight:600;color:var(--text-primary)}.dash-layout{display:flex;max-width:var(--max-width);margin:0 auto;position:relative}.dash-sidebar{position:sticky;top:56px;height:calc(100vh - 56px);width:180px;flex-shrink:0;padding:24px 0 24px 24px;overflow-y:auto;display:none}@media(min-width:1024px){.dash-sidebar{display:block}}.dash-sidebar__list{list-style:none;display:flex;flex-direction:column;gap:2px}.dash-sidebar__link{display:block;padding:8px 16px;font-size:.8125rem;font-weight:500;color:var(--text-tertiary);text-decoration:none;border-radius:8px;transition:color var(--transition-fast),background var(--transition-fast)}.dash-sidebar__link:hover{color:var(--text-secondary);background:#ffffff0a}.dash-sidebar__link--active{color:var(--text-accent);background:#a78bfa14;font-weight:600}.dash-main{flex:1;min-width:0;max-width:var(--max-width);margin:0 auto;padding:24px;display:flex;flex-direction:column;gap:20px;position:relative}.dash-main:before{content:"";position:fixed;top:0;left:50%;transform:translate(-50%);width:100%;height:600px;background:var(--gradient-glow);pointer-events:none;z-index:0}.dash-main>*{position:relative;z-index:1}[data-nav-section]{scroll-margin-top:72px}.kpi-row{display:grid;grid-template-columns:1fr;gap:12px}@media(min-width:480px){.kpi-row{grid-template-columns:repeat(2,1fr)}}@media(min-width:960px){.kpi-row{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}}.kpi-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;padding:20px 24px;display:flex;flex-direction:column;gap:4px;transition:border-color var(--transition-fast)}.kpi-card:hover{border-color:#ffffff1a}.kpi-card__label{font-size:.75rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.kpi-card__value{font-size:2rem;font-weight:700;color:var(--text-primary);line-height:1.2;letter-spacing:-.02em}.kpi-card__sub{font-size:.75rem;color:var(--text-secondary);display:flex;align-items:center;gap:4px}.kpi-trend{font-weight:600}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-failed)}.kpi-trend--neutral{color:var(--text-tertiary)}.chart-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;overflow:hidden;transition:border-color var(--transition-fast)}.chart-card:hover{border-color:#ffffff1a}.chart-card__header{padding:20px 24px 8px}.chart-card__title{font-size:.9375rem;font-weight:600;color:var(--text-primary)}.chart-card__desc{font-size:.75rem;color:var(--text-tertiary);margin-top:2px}.chart-card__body{padding:16px 24px 24px;min-height:120px}.chart-card__body--table{padding:0}.charts-row{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width:768px){.charts-row{grid-template-columns:repeat(2,1fr)}}.bar-row{display:flex;align-items:center;gap:12px;padding:6px 0}.bar-row+.bar-row{border-top:1px solid rgba(255,255,255,.03)}.bar-label{font-size:.8125rem;color:var(--text-secondary);width:130px;flex-shrink:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-track{flex:1;height:24px;background:var(--bg-tertiary);border-radius:6px;display:flex;overflow:hidden}.bar-segment{height:100%;transition:width .8s cubic-bezier(.4,0,.2,1);min-width:0}.bar--success{background:var(--color-success)}.bar--partial{background:var(--color-partial)}.bar--failed{background:var(--color-failed)}.bar--premium{background:var(--color-premium)}.bar--standard{background:var(--color-standard)}.bar--utility{background:var(--color-utility)}.bar--economy{background:var(--color-economy)}.bar--accent{background:var(--accent-blue)}.bar-value{font-size:.8125rem;font-weight:600;color:var(--text-primary);width:36px;text-align:right;flex-shrink:0;font-variant-numeric:tabular-nums}.donut-container{display:flex;align-items:center;justify-content:center;gap:32px;flex-wrap:wrap}.donut-wrap{position:relative;width:180px;height:180px;flex-shrink:0}.donut-svg{width:100%;height:100%}.donut-svg circle{transition:stroke-dasharray .8s cubic-bezier(.4,0,.2,1),stroke-dashoffset .8s cubic-bezier(.4,0,.2,1)}.donut-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center}.donut-total{display:block;font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.donut-total-label{display:block;font-size:.6875rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.08em;margin-top:2px}.donut-legend{display:flex;flex-direction:column;gap:10px}.legend-item{display:flex;align-items:center;gap:8px;font-size:.8125rem}.legend-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0}.legend-name{color:var(--text-secondary);text-transform:capitalize}.legend-count{color:var(--text-tertiary);font-variant-numeric:tabular-nums;margin-left:auto}.timeline-svg{width:100%;height:auto;display:block}.timeline-svg text{font-family:inherit}.timeline-legend{display:flex;gap:16px;justify-content:center;margin-top:12px}.timeline-legend__item{display:flex;align-items:center;gap:6px;font-size:.75rem;color:var(--text-tertiary)}.timeline-legend__dot{width:8px;height:8px;border-radius:2px}.pipeline{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:8px 0}.pipeline-stage{flex:1;min-width:140px;display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 12px;position:relative}.pipeline-stage:not(:last-child):after{content:"";position:absolute;right:-1px;top:50%;transform:translateY(-50%);width:2px;height:40%;background:var(--border-color)}.pipeline-stage__icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:1rem}.pipeline-stage__icon--pending{background:#64748b26;color:#94a3b8;border:1px solid rgba(100,116,139,.2)}.pipeline-stage__icon--active{background:#3b82f626;color:#60a5fa;border:1px solid rgba(59,130,246,.3);animation:pulse-glow 2s ease-in-out infinite}.pipeline-stage__icon--review{background:#f59e0b26;color:#fbbf24;border:1px solid rgba(245,158,11,.3)}.pipeline-stage__icon--done{background:#22c55e26;color:#4ade80;border:1px solid rgba(34,197,94,.3)}@keyframes pulse-glow{0%,to{box-shadow:0 0 #3b82f633}50%{box-shadow:0 0 12px 4px #3b82f626}}.pipeline-stage__count{font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.pipeline-stage__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.04em;font-weight:500}.pipeline-arrow{display:flex;align-items:center;color:var(--text-tertiary);font-size:1.25rem;padding:0 4px;flex-shrink:0}.exec-log{display:flex;flex-direction:column}.exec-step{display:flex;gap:16px;padding:14px 0;position:relative}.exec-step+.exec-step{border-top:1px solid rgba(255,255,255,.03)}.exec-step__indicator{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:32px}.exec-step__dot{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.6875rem;font-weight:700;flex-shrink:0}.exec-step__dot--success{background:#22c55e26;color:var(--color-success);border:1.5px solid rgba(34,197,94,.3)}.exec-step__dot--partial{background:#f59e0b26;color:var(--color-partial);border:1.5px solid rgba(245,158,11,.3)}.exec-step__dot--failed{background:#ef444426;color:var(--color-failed);border:1.5px solid rgba(239,68,68,.3)}.exec-step__line{flex:1;width:1.5px;background:var(--border-color);margin-top:4px}.exec-step__content{flex:1;min-width:0}.exec-step__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.exec-step__agent{font-size:.875rem;font-weight:600;color:var(--text-primary)}.exec-step__badge{display:inline-flex;align-items:center;padding:2px 8px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.exec-step__badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.exec-step__badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.exec-step__badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.exec-step__task{font-size:.8125rem;color:var(--text-secondary);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.exec-step__meta{display:flex;gap:16px;margin-top:6px;font-size:.6875rem;color:var(--text-tertiary)}.exec-step__meta-item{display:flex;align-items:center;gap:4px}.panel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px}.panel-item{background:var(--bg-tertiary);border-radius:8px;padding:16px;display:flex;flex-direction:column;gap:8px;border:1px solid transparent;transition:border-color var(--transition-fast)}.panel-item:hover{border-color:var(--border-color)}.panel-item__header{display:flex;align-items:center;justify-content:space-between}.panel-item__key{font-size:.8125rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.panel-item__verdict{font-size:.6875rem;font-weight:700;padding:2px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.04em}.panel-item__verdict--pass{background:#22c55e26;color:var(--color-success)}.panel-item__verdict--block{background:#ef444426;color:var(--color-failed)}.panel-item__votes{display:flex;gap:4px}.panel-item__vote{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.625rem;font-weight:700}.panel-item__vote--pass{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.panel-item__vote--block{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.panel-item__fixes{font-size:.6875rem;color:var(--text-tertiary)}.panel-item__meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;padding-top:8px;border-top:1px solid var(--border-color)}.panel-item__meta-item{font-size:.625rem;color:var(--text-tertiary);white-space:nowrap}.sessions-table{width:100%;border-collapse:collapse;font-size:.8125rem}.sessions-table thead{position:sticky;top:0}.sessions-table th{padding:12px 16px;font-size:.6875rem;font-weight:600;color:var(--text-tertiary);text-align:left;text-transform:uppercase;letter-spacing:.06em;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color)}.sessions-table th:last-child,.sessions-table td:last-child{text-align:right}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5){text-align:right}.sessions-table td{padding:10px 16px;color:var(--text-secondary);border-bottom:1px solid rgba(255,255,255,.03);white-space:nowrap}.sessions-table tr:hover td{background:#ffffff05}.sessions-table .td-agent{font-weight:500;color:var(--text-primary)}.sessions-table .td-task{max-width:260px;overflow:hidden;text-overflow:ellipsis}.outcome-badge{display:inline-flex;align-items:center;padding:3px 10px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.outcome-badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.outcome-badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.outcome-badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.td-num{font-variant-numeric:tabular-nums;text-align:right}.td-issue{font-size:.75rem;color:var(--text-accent);font-weight:500;font-variant-numeric:tabular-nums}.loading-skeleton{display:flex;align-items:center;justify-content:center;min-height:200px;color:var(--text-tertiary);font-size:.8125rem}.loading-skeleton:after{content:"Loading data…";animation:fade-pulse 1.5s ease-in-out infinite}@keyframes fade-pulse{0%,to{opacity:.4}50%{opacity:1}}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;text-align:center;gap:12px}.empty-state__icon{font-size:2rem;opacity:.4}.empty-state__text{font-size:.875rem;color:var(--text-tertiary);max-width:320px}.empty-state--enhanced{padding:56px 32px;gap:16px;border:1px dashed rgba(167,139,250,.15);border-radius:12px;background:radial-gradient(ellipse 300px 200px at 50% 30%,rgba(99,102,241,.04) 0%,transparent 70%),var(--bg-tertiary);position:relative;overflow:hidden}.empty-state--enhanced:before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 23px,rgba(255,255,255,.015) 23px,rgba(255,255,255,.015) 24px);pointer-events:none}.empty-state__icon-wrap{width:64px;height:64px;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#a78bfa0f;border:1px solid rgba(167,139,250,.12);color:var(--text-accent);animation:empty-breathe 4s ease-in-out infinite}@keyframes empty-breathe{0%,to{box-shadow:0 0 #a78bfa14;transform:scale(1)}50%{box-shadow:0 0 20px 4px #a78bfa0f;transform:scale(1.03)}}.empty-state__title{font-size:.9375rem;font-weight:600;color:var(--text-secondary);letter-spacing:-.01em}.empty-state__desc{font-size:.8125rem;color:var(--text-tertiary);max-width:380px;line-height:1.55}.kpi-card__hint{color:var(--text-tertiary);font-style:italic;font-size:.6875rem}.kpi-row--empty .kpi-card{border-style:dashed;border-color:#ffffff0a}.kpi-row--empty .kpi-card__value{color:var(--text-tertiary);opacity:.5}.welcome-banner{position:relative;background:var(--bg-secondary);border:1px solid transparent;border-radius:16px;padding:48px 40px;overflow:hidden;z-index:1}.welcome-banner:before{content:"";position:absolute;inset:-1px;border-radius:16px;padding:1px;background:linear-gradient(135deg,#a78bfa4d,#6366f126,#3b82f61a 60%,#a78bfa33);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none;z-index:0}.welcome-banner__glow{position:absolute;top:-60px;left:50%;transform:translate(-50%);width:500px;height:300px;background:radial-gradient(ellipse at center,rgba(167,139,250,.08) 0%,rgba(99,102,241,.04) 40%,transparent 70%);pointer-events:none;z-index:0}.welcome-banner__content{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:20px}.welcome-banner__icon{width:72px;height:72px;display:flex;align-items:center;justify-content:center;border-radius:20px;background:#a78bfa14;border:1px solid rgba(167,139,250,.15);color:var(--text-accent);animation:welcome-float 6s ease-in-out infinite}@keyframes welcome-float{0%,to{transform:translateY(0);box-shadow:0 8px 32px #a78bfa14}50%{transform:translateY(-6px);box-shadow:0 16px 48px #a78bfa1f}}.welcome-banner__title{font-size:1.375rem;font-weight:700;color:var(--text-primary);letter-spacing:-.02em;line-height:1.3}.welcome-banner__subtitle{font-size:.9375rem;color:var(--text-secondary);max-width:480px;line-height:1.6}.welcome-banner__steps{display:flex;gap:20px;margin-top:12px;flex-wrap:wrap;justify-content:center}.welcome-step{display:flex;align-items:flex-start;gap:12px;text-align:left;padding:16px 20px;background:#ffffff05;border:1px solid rgba(255,255,255,.05);border-radius:12px;min-width:200px;max-width:220px;transition:border-color var(--transition-fast),background var(--transition-fast)}.welcome-step:hover{border-color:#a78bfa26;background:#ffffff08}.welcome-step__num{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;color:var(--text-accent);background:#a78bfa1a;border:1px solid rgba(167,139,250,.2);flex-shrink:0}.welcome-step__text{display:flex;flex-direction:column;gap:3px}.welcome-step__text strong{font-size:.8125rem;font-weight:600;color:var(--text-primary)}.welcome-step__text span{font-size:.75rem;color:var(--text-tertiary);line-height:1.4}@media(max-width:640px){.welcome-banner{padding:32px 24px}.welcome-banner__steps{flex-direction:column;align-items:center}.welcome-step{max-width:100%;width:100%}}@keyframes slide-up{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.dash-main>*{animation:slide-up .5s ease-out backwards}.dash-main>*:nth-child(1){animation-delay:0ms}.dash-main>*:nth-child(2){animation-delay:60ms}.dash-main>*:nth-child(3){animation-delay:.12s}.dash-main>*:nth-child(4){animation-delay:.18s}.dash-main>*:nth-child(5){animation-delay:.24s}.dash-main>*:nth-child(6){animation-delay:.3s}.dash-main>*:nth-child(7){animation-delay:.36s}.dash-main>*:nth-child(8){animation-delay:.42s}.dash-main>*:nth-child(9){animation-delay:.48s}.dash-main>*:nth-child(10){animation-delay:.54s}.dash-main>*:nth-child(11){animation-delay:.6s}@media(max-width:640px){.bar-label{width:90px;font-size:.75rem}.donut-container{flex-direction:column;align-items:center}.donut-wrap{width:150px;height:150px}.pipeline{gap:0}.pipeline-stage{min-width:100px;padding:12px 8px}.panel-grid{grid-template-columns:1fr}.sessions-table th:nth-child(3),.sessions-table td:nth-child(3){display:none}}.filter-bar{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;padding:16px 20px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px}.filter-group{display:flex;flex-direction:column;gap:4px;min-width:0}.filter-label{font-size:.6875rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.filter-input,.filter-select{height:34px;padding:0 10px;font-size:.8125rem;color:var(--text-primary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;outline:none;transition:border-color var(--transition-fast);font-family:inherit}.filter-input:focus,.filter-select:focus{border-color:var(--border-accent)}.filter-input{width:140px;color-scheme:dark}.filter-select{min-width:140px;cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235a5a6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}.filter-reset{height:34px;font-size:.75rem}.dash-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;font-size:.8125rem;font-weight:500;font-family:inherit;border:none;border-radius:8px;cursor:pointer;transition:background var(--transition-fast),color var(--transition-fast)}.dash-btn--ghost{color:var(--text-secondary);background:#ffffff0f}.dash-btn--ghost:hover{color:var(--text-primary);background:#ffffff1a}.dash-header__actions{display:flex;align-items:center;gap:8px}@media(max-width:480px){.dash-header__inner{padding:0 12px}.dash-main{padding:12px;gap:12px}.kpi-card,.chart-card__header{padding:14px 16px}.chart-card__body{padding:12px 16px 16px}.filter-bar{padding:12px;gap:8px}.filter-input,.filter-select{width:100%;min-width:unset}.filter-group{flex:1 1 calc(50% - 4px)}.filter-reset{width:100%}.dash-header__title{font-size:.875rem}.exec-step__meta{flex-direction:column;gap:2px}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5),.sessions-table th:nth-child(6),.sessions-table td:nth-child(6),.sessions-table th:nth-child(7),.sessions-table td:nth-child(7),.sessions-table th:nth-child(8),.sessions-table td:nth-child(8){display:none}}@media(max-width:768px){.charts-row{grid-template-columns:1fr}.pipeline{flex-wrap:wrap;gap:8px}.pipeline-arrow{display:none}.pipeline-stage{flex:1 1 calc(50% - 4px);min-width:100px}.tier-chart .donut-container,.donut-container{flex-direction:column;align-items:center}.sessions-table{font-size:.75rem}.sessions-table th,.sessions-table td{padding:8px 6px}}.convoy-overview{display:flex;flex-wrap:wrap;gap:24px;margin-bottom:20px}.convoy-stat{display:flex;flex-direction:column;gap:4px}.convoy-stat__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.convoy-stat__value{font-size:.95rem;color:var(--text-primary)}.convoy-stat__value--error{color:var(--color-failed)}.convoy-progress{display:flex;align-items:center;gap:12px;margin-bottom:20px}.convoy-progress__bar{flex:1;height:8px;background:var(--bg-tertiary);border-radius:4px;overflow:hidden}.convoy-progress__fill{height:100%;background:var(--gradient-accent);border-radius:4px;transition:width var(--transition-base)}.convoy-progress__label{font-size:.8rem;color:var(--text-secondary);white-space:nowrap}.convoy-tasks{margin-top:8px}.convoy-chain{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:1rem 0 1.5rem;scrollbar-width:thin;scrollbar-color:var(--border-color) transparent}.convoy-chain::-webkit-scrollbar{height:4px}.convoy-chain::-webkit-scrollbar-track{background:transparent}.convoy-chain::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:2px}.convoy-chain__connector{display:flex;align-items:center;padding:0 .5rem;color:var(--text-tertiary);font-size:1.1rem;flex-shrink:0}.convoy-chain__node{display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 16px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:10px;min-width:140px;cursor:pointer;transition:background var(--transition-fast),border-color var(--transition-fast),transform var(--transition-fast),box-shadow var(--transition-fast);flex-shrink:0}.convoy-chain__node:hover{background:var(--bg-card-hover);transform:translateY(-2px);box-shadow:0 4px 12px #0000004d}.convoy-chain__node-name{font-size:.8rem;font-weight:600;color:var(--text-primary);text-align:center;word-break:break-word;max-width:120px}.convoy-chain__node-meta{font-size:.72rem;color:var(--text-tertiary);text-align:center}.convoy-chain__node--active{border-color:var(--accent-purple);box-shadow:0 0 0 1px var(--accent-purple),0 0 12px #a78bfa33;animation:convoy-pulse 2s ease-in-out infinite}.convoy-chain__node--done{border-color:#22c55e4d}.convoy-chain__node--failed{border-color:#ef44444d}.convoy-chain__node--pending{opacity:.6}@keyframes convoy-pulse{0%,to{box-shadow:0 0 0 1px var(--accent-purple),0 0 8px #a78bfa26}50%{box-shadow:0 0 0 1px var(--accent-purple),0 0 18px #a78bfa59}}@media(max-width:768px){.convoy-chain{flex-wrap:wrap;gap:8px}.convoy-chain__connector{display:none}.convoy-chain__node{flex:1 1 calc(50% - 4px);min-width:120px}}.convoy-selector{display:flex;align-items:center;gap:.5rem}.convoy-selector__label{font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary)}.convoy-selector__select{appearance:none;background:var(--bg-tertiary);border:1px solid rgba(255,255,255,.08);border-radius:6px;color:var(--text-primary);font-size:.8125rem;padding:.375rem 2rem .375rem .75rem;cursor:pointer;max-width:320px;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%238a8a9a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right .75rem center;transition:border-color .15s}.convoy-selector__select:hover{border-color:#ffffff26}.convoy-selector__select:focus{outline:2px solid var(--accent-purple);outline-offset:2px}.overall-stats{margin-bottom:0;padding:1.25rem;background:var(--bg-secondary);border-radius:12px;border:1px solid rgba(255,255,255,.06)}.overall-stats__header{display:flex;align-items:center;gap:.5rem;margin-bottom:1rem}.overall-stats__title{font-size:1rem;font-weight:600;color:var(--text-primary);margin:0}.overall-stats__grid{display:grid;grid-template-columns:repeat(6,1fr);gap:.75rem}.overall-kpi{display:flex;flex-direction:column;gap:.25rem;padding:.75rem;background:var(--bg-tertiary);border-radius:8px;border:1px solid rgba(255,255,255,.04);transition:border-color .15s}.overall-kpi:hover{border-color:#ffffff1a}.overall-kpi__label{font-size:.6875rem;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);display:flex;align-items:center;gap:.25rem}.overall-kpi__value{font-size:1.375rem;font-weight:700;color:var(--text-primary);font-variant-numeric:tabular-nums}.convoy-detail-header{padding:1.25rem;background:var(--bg-secondary);border-radius:12px;border:1px solid rgba(255,255,255,.06)}.convoy-detail-header__top{display:flex;align-items:center;gap:.75rem;margin-bottom:.75rem}.convoy-detail-header__name{font-size:1.25rem;font-weight:700;color:var(--text-primary);margin:0}.convoy-detail-header__meta{display:flex;flex-wrap:wrap;gap:1rem}.convoy-meta__item{font-size:.8125rem;color:var(--text-secondary)}.status-badge{display:inline-flex;align-items:center;padding:.125rem .625rem;border-radius:999px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.status-badge--done{background:#22c55e26;color:var(--color-success)}.status-badge--running{background:#3b82f626;color:var(--accent-blue)}.status-badge--failed{background:#ef444426;color:var(--color-failed)}.status-badge--gate-failed,.status-badge--gate_failed{background:#f59e0b26;color:var(--color-partial)}.tooltip-trigger{position:relative;cursor:help;font-size:.75rem;opacity:.5;transition:opacity .15s}.tooltip-trigger:hover{opacity:1}.tooltip-trigger:hover:after{content:attr(data-tooltip);position:absolute;bottom:100%;left:50%;transform:translate(-50%);padding:.375rem .625rem;background:var(--bg-primary);border:1px solid rgba(255,255,255,.12);border-radius:6px;font-size:.75rem;color:var(--text-primary);max-width:280px;white-space:normal;text-align:left;z-index:100;pointer-events:none;box-shadow:0 4px 12px #0006}.tooltip-trigger:focus{opacity:1;outline:2px solid var(--accent-blue);outline-offset:2px;border-radius:2px}.tooltip-trigger:focus:after,.tooltip-trigger:focus-visible:after{content:attr(data-tooltip);position:absolute;bottom:100%;left:50%;transform:translate(-50%);padding:.375rem .625rem;background:var(--bg-primary);border:1px solid rgba(255,255,255,.12);border-radius:6px;font-size:.75rem;color:var(--text-primary);max-width:280px;white-space:normal;text-align:left;z-index:100;pointer-events:none;box-shadow:0 4px 12px #0006}.status-badge--pending{background:#64748b26;color:#94a3b8}.status-badge--assigned{background:#3b82f61a;color:#60a5fa}.status-badge--timed-out{background:#ef44441f;color:#f87171}.status-badge--review-blocked{background:#f59e0b1f;color:#fbbf24}.status-badge--skipped{background:#64748b1a;color:#64748b}.status-badge--hook-failed{background:#ef44441a;color:#f87171}.status-badge--disputed{background:#a78bfa26;color:var(--accent-purple)}.status-badge--wait-for-input{background:#f59e0b1a;color:var(--color-partial)}.task-summary-cards{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:20px}.task-summary-card{flex:1 1 140px;display:flex;flex-direction:column;gap:8px;padding:14px 16px;background:var(--bg-card);border:1px solid var(--border-color);border-radius:10px;transition:border-color .15s}.task-summary-card:hover{border-color:#ffffff1f}.task-summary-card__label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--text-tertiary)}.task-summary-card__value{font-size:1.75rem;font-weight:700;line-height:1;color:var(--text-primary)}.task-summary-card--done{border-left:3px solid var(--color-success)}.task-summary-card--running{border-left:3px solid var(--accent-blue)}.task-summary-card--errors{border-left:3px solid var(--color-failed)}.task-summary-card--waiting{border-left:3px solid #94a3b8}.task-summary-card--input{border-left:3px solid var(--color-partial)}.task-table-wrap{overflow-x:auto}.task-table .td-num{text-align:right}.sortable-th{cursor:pointer;user-select:none}.sortable-th:hover{color:var(--text-secondary)}.sortable-th--active{color:var(--text-primary)}.sort-indicator{margin-left:4px;font-size:.5625rem;opacity:.5}.sortable-th--active .sort-indicator{opacity:1;color:var(--accent-blue)}.phase-breakdown{display:flex;flex-direction:column;gap:8px;margin-bottom:20px}.phase-breakdown__row{display:flex;align-items:center;gap:12px}.phase-breakdown__label{font-size:.75rem;font-weight:600;color:var(--text-tertiary);min-width:60px}.phase-breakdown__bar{flex:1;height:10px;background:#ffffff0a;border-radius:5px;overflow:hidden;display:flex}.phase-breakdown__seg{height:100%;transition:width .3s ease}.phase-breakdown__seg--done{background:var(--color-success)}.phase-breakdown__seg--running{background:var(--accent-blue)}.phase-breakdown__seg--waiting{background:#475569}.phase-breakdown__seg--failed{background:var(--color-failed)}.phase-breakdown__count{font-size:.6875rem;color:var(--text-tertiary);min-width:52px;text-align:right}@media(max-width:960px){.overall-stats__grid{grid-template-columns:repeat(3,1fr)}}@media(max-width:640px){.overall-stats__grid{grid-template-columns:repeat(2,1fr)}.convoy-detail-header__name{font-size:1rem}.convoy-selector__select{max-width:200px}}@media(max-width:480px){.overall-stats__grid{grid-template-columns:1fr}}.reliability-empty{font-size:.875rem;color:var(--text-tertiary);padding:12px 0;margin:0}.secret-leak-banner{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;background:#f59e0b1a;border:1px solid rgba(245,158,11,.3);border-radius:8px;margin-top:16px}.secret-leak-banner__icon{font-size:1.25rem;flex-shrink:0;line-height:1.4}.secret-leak-banner__text{display:flex;flex-direction:column;gap:4px}.secret-leak-banner__text strong{font-size:.875rem;font-weight:600;color:var(--color-partial)}.secret-leak-banner__text span{font-size:.8125rem;color:var(--text-secondary)}.artifact-type-badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:.75rem;font-weight:600;color:#fff;text-transform:uppercase;letter-spacing:.03em}.timeline-filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}.timeline-filter-chip{display:inline-flex;align-items:center;padding:6px 14px;border-radius:20px;border:1px solid var(--border);background:transparent;color:var(--text-secondary);font-size:.8125rem;font-weight:500;cursor:pointer;transition:all .15s ease}.timeline-filter-chip:hover{border-color:var(--accent);color:var(--text-primary)}.timeline-filter-chip--active{background:var(--accent);border-color:var(--accent);color:#fff}.event-timeline-row{padding:12px 16px;border-bottom:1px solid var(--border);cursor:pointer;transition:background .15s ease}.event-timeline-row:hover{background:#a78bfa0d}.event-timeline-row--expanded{background:#a78bfa14}.event-timeline-row__main{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.event-timeline-ts{font-size:.8125rem;color:var(--text-secondary);min-width:140px;font-variant-numeric:tabular-nums}.event-type-badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:.6875rem;font-weight:600;color:#fff;text-transform:uppercase;letter-spacing:.03em}.event-timeline-context{font-size:.8125rem;color:var(--text-tertiary, #6b7280);font-family:var(--font-mono, "SF Mono", "Fira Code", monospace)}.event-timeline-detail{margin-top:8px;padding:12px;background:var(--bg-card, #111118);border-radius:6px;border:1px solid var(--border)}.event-timeline-json{font-size:.75rem;color:var(--text-secondary);white-space:pre-wrap;word-break:break-all;margin:0;font-family:var(--font-mono, "SF Mono", "Fira Code", monospace);max-height:300px;overflow-y:auto}@media(max-width:640px){.event-timeline-ts{min-width:auto;font-size:.75rem}.event-timeline-row__main{gap:8px}.timeline-filter-chip{padding:4px 10px;font-size:.75rem}}.dash-btn:focus-visible,.convoy-selector__select:focus-visible,.filter-select:focus-visible,.filter-input:focus-visible,.dash-sidebar__link:focus-visible,.timeline-filter-chip:focus-visible{outline:2px solid var(--accent-blue);outline-offset:2px}.convoy-status-explanation{font-size:.8125rem;color:var(--text-secondary);margin-top:.25rem;margin-bottom:.5rem;font-style:italic}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"convoyCounts": {
|
|
3
|
+
"total": 0,
|
|
4
|
+
"running": 0,
|
|
5
|
+
"done": 0,
|
|
6
|
+
"failed": 0,
|
|
7
|
+
"gate_failed": 0
|
|
8
|
+
},
|
|
9
|
+
"durationStats": {
|
|
10
|
+
"avg_sec": null,
|
|
11
|
+
"p95_sec": null,
|
|
12
|
+
"max_sec": null
|
|
13
|
+
},
|
|
14
|
+
"tokenCostTotals": {
|
|
15
|
+
"total_tokens": 0,
|
|
16
|
+
"total_cost_usd": 0
|
|
17
|
+
},
|
|
18
|
+
"topAgents": [],
|
|
19
|
+
"topModels": [],
|
|
20
|
+
"dlqSummary": {
|
|
21
|
+
"count": 0,
|
|
22
|
+
"top_failure_types": []
|
|
23
|
+
}
|
|
24
|
+
}
|