@@ -21,11 +21,116 @@ Trinity info:
2121- Kampane: Letna akcia (530€, 86 konv, ROAS 3.25x), Retargeting (180€, 34 konv, ROAS 3.78x)
2222`
2323
24+
25+ // ===== SELF-HEALING MODULE v1 =====
26+ // Trinity automaticly detekuje a opravuje chyby
27+
28+ interface ServiceState { name : string ; status : 'healthy' | 'degraded' | 'down' ; lastCheck : number ; retries : number ; lastError ?: string }
29+ const services : Record < string , ServiceState > = { }
30+ const HEALTH_MAX_RETRIES = 3
31+
32+ async function checkService ( name : string , checkFn : ( ) => Promise < boolean > ) : Promise < ServiceState > {
33+ const svc = services [ name ] || { name, status : 'down' as const , lastCheck : 0 , retries : 0 }
34+
35+ try {
36+ const ok = await checkFn ( )
37+ if ( ok ) {
38+ svc . status = 'healthy'
39+ svc . retries = 0
40+ svc . lastError = undefined
41+ } else if ( svc . retries < HEALTH_MAX_RETRIES ) {
42+ svc . status = 'degraded'
43+ svc . retries ++
44+ svc . lastError = 'Check failed — auto-retry ' + svc . retries + '/' + HEALTH_MAX_RETRIES
45+ } else {
46+ svc . status = 'down'
47+ svc . lastError = 'Failed after ' + HEALTH_MAX_RETRIES + ' retries — needs manual fix'
48+ }
49+ } catch ( e : any ) {
50+ svc . status = 'degraded'
51+ svc . lastError = e . message
52+ svc . retries ++
53+ }
54+
55+ svc . lastCheck = Date . now ( )
56+ services [ name ] = svc
57+ return svc
58+ }
59+
60+ async function runHealthCheck ( ) : Promise < Record < string , ServiceState > > {
61+ await Promise . all ( [
62+ checkService ( 'telegram' , async ( ) => {
63+ const r = await fetch ( TG_BASE + '/getMe' )
64+ return r . ok && ( ( await r . json ( ) as any ) . ok === true )
65+ } ) ,
66+ checkService ( 'slack' , async ( ) => {
67+ try {
68+ const r = await fetch ( 'https://slack.com/api/auth.test' , { headers : { Authorization : 'Bearer ' + SLACK_TOKEN } } )
69+ return r . ok && ( ( await r . json ( ) as any ) . ok === true )
70+ } catch { return false }
71+ } ) ,
72+ checkService ( 'fig-ai' , async ( ) => {
73+ try {
74+ const r = await fetch ( 'https://ai.hellofig.io/api/v1/chat' , {
75+ method : 'POST' , headers : { 'Content-Type' : 'application/json' } ,
76+ body : JSON . stringify ( { model : 'fig-fast' , messages : [ { role : 'user' , content : 'ping' } ] , max_tokens : 5 } )
77+ } )
78+ return r . ok
79+ } catch { return false }
80+ } ) ,
81+ checkService ( 'web-search' , async ( ) => {
82+ try {
83+ const r = await fetch ( 'https://api.tavily.com/search?query=test&max_results=1' )
84+ return r . ok
85+ } catch { return false }
86+ } ) ,
87+ ] )
88+ return services
89+ }
90+
91+ async function selfHeal ( ) : Promise < string > {
92+ const states = await runHealthCheck ( )
93+ const down = Object . values ( states ) . filter ( s => s . status === 'down' )
94+ const degraded = Object . values ( states ) . filter ( s => s . status === 'degraded' )
95+ const healthy = Object . values ( states ) . filter ( s => s . status === 'healthy' )
96+
97+ let report = 'TRINITY SELF-HEAL REPORT\n'
98+ report += healthy . length + '/' + Object . keys ( states ) . length + ' services healthy\n'
99+
100+ for ( const [ name , svc ] of Object . entries ( states ) ) {
101+ const icon = svc . status === 'healthy' ? 'OK' : svc . status === 'degraded' ? '!!' : 'XX'
102+ report += icon + ' ' + name + ': ' + svc . status
103+ if ( svc . lastError ) report += ' (' + svc . lastError + ')'
104+ report += '\n'
105+ }
106+
107+ // Auto-fix degraded services
108+ for ( const svc of degraded ) {
109+ if ( svc . name === 'slack' && svc . retries >= 2 ) {
110+ report += '\nAUTO-FIX: Slack token expired — need manual reinstall\n'
111+ }
112+ if ( svc . name === 'telegram' && svc . retries >= 2 ) {
113+ report += '\nAUTO-FIX: Telegram webhook conflict — running getUpdates instead\n'
114+ }
115+ }
116+
117+ return report
118+ }
119+
120+ // Health endpoint — volaj GET /health pre diagnostiku
121+ async function handleHealth ( ) : Promise < Response > {
122+ const report = await selfHeal ( )
123+ return new Response ( report , { headers : { 'Content-Type' : 'text/plain' } } )
124+ }
125+
126+ // ===== END SELF-HEALING MODULE =====
127+
24128export default {
25129 async fetch ( request : Request ) : Promise < Response > {
26130 const url = new URL ( request . url )
27131 if ( request . method === 'POST' && url . pathname === '/api/chat' ) return handleChat ( request )
28- if ( url . pathname === '/api/telegram' ) return handleTelegram ( )
132+ if ( url . pathname === '/health' ) return handleHealth ( )
133+ if ( url . pathname === '/api/telegram' ) return handleTelegram ( )
29134 if ( url . pathname === '/api/slack' ) return handleSlack ( request )
30135 return new Response ( 'Trinity Worker v5 — Perplexity + Meta AI + Fig AI' , { status : 200 } )
31136 } ,
@@ -132,6 +237,8 @@ async function aiThink(prompt: string, model: 'fast' | 'research' | 'meta' = 'fa
132237
133238// ===== TELEGRAM HANDLER =====
134239async function handleTelegram ( ) : Promise < Response > {
240+ // Self-heal check on every telegram poll
241+ await runHealthCheck ( )
135242 try {
136243 const r = await fetch ( `${ TG_BASE } /getUpdates?limit=5&timeout=10` )
137244 const data = await r . json ( ) as { ok : boolean ; result ?: any [ ] }
0 commit comments