@madh-io/alfred-ai 0.15.9 → 0.16.0
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/bundle/index.js +1 -1
- package/package.json +1 -1
package/bundle/index.js
CHANGED
|
@@ -1399,7 +1399,7 @@ Alfred Chat \u2014 type your message and press Enter. Use /quit or /exit to leav
|
|
|
1399
1399
|
Goodbye!
|
|
1400
1400
|
`),this.emit("disconnected");return}this.messageCounter++;let s={id:`cli-${this.messageCounter}`,platform:"cli",chatId:"cli-chat",chatType:"dm",userId:"cli-user",userName:"cli-user",displayName:"You",text:t,timestamp:new Date};this.emit("message",s)}),this.rl.on("close",()=>{this.emit("disconnected")}),this.status="connected",this.emit("connected"),this.prompt()}async disconnect(){this.rl?.close(),this.rl=void 0,this.status="disconnected"}async sendMessage(e,t,s){let r=`cli-resp-${++this.messageCounter}`;return process.stdout.write(`
|
|
1401
1401
|
Alfred: ${t}
|
|
1402
|
-
`),this.prompt(),r}async editMessage(e,t,s,r){xc.clearLine(process.stdout,0),xc.cursorTo(process.stdout,0),process.stdout.write(`Alfred: ${s}`)}async deleteMessage(e,t){}prompt(){this.rl?.prompt()}}});import Ng from"node:http";import Kt from"node:fs";import sr from"node:path";import ti from"node:crypto";var Lg,Sm,si,vm=T(()=>{"use strict";ct();Lg={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"application/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".woff":"font/woff",".woff2":"font/woff2",".txt":"text/plain; charset=utf-8"},Sm=1048576,si=class extends ye{static{m(this,"HttpAdapter")}platform="api";server=null;streams=new Map;messageCounter=0;port;host;apiToken;corsOrigin;healthCheckFn;metricsFn;dashboardFn;webUiPath;webhooks=new Map;constructor(e,t,s){if(super(),this.port=e,this.host=t,this.apiToken=s?.apiToken,this.corsOrigin=s?.corsOrigin??"http://localhost:3420",this.healthCheckFn=s?.healthCheck,this.metricsFn=s?.metricsCallback,this.dashboardFn=s?.dashboardCallback,this.webUiPath=s?.webUiPath,s?.webhooks)for(let r of s.webhooks)this.webhooks.set(r.name,r)}addWebhook(e){this.webhooks.set(e.name,e)}async connect(){this.status="connecting",this.server=Ng.createServer((e,t)=>{this.handleRequest(e,t)}),await new Promise((e,t)=>{this.server.listen(this.port,this.host,()=>{e()}),this.server.once("error",t)}),this.status="connected",this.emit("connected")}async disconnect(){for(let[e,t]of this.streams)this.writeSseEvent(t,"done",{type:"done"}),t.end(),this.streams.delete(e);this.server&&(await new Promise(e=>{this.server.close(()=>e())}),this.server=null),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=`api-resp-${++this.messageCounter}`,n=this.streams.get(e);return n&&this.writeSseEvent(n,"response",{type:"response",text:t}),r}async editMessage(e,t,s,r){let n=this.streams.get(e);n&&this.writeSseEvent(n,"status",{type:"status",text:s})}async deleteMessage(e,t){}async sendPhoto(e,t,s){let r=this.streams.get(e);return r&&this.writeSseEvent(r,"attachment",{type:"attachment",attachmentType:"image",data:t.toString("base64"),caption:s}),`api-photo-${++this.messageCounter}`}async sendFile(e,t,s,r){let n=this.streams.get(e);return n&&this.writeSseEvent(n,"attachment",{type:"attachment",attachmentType:"file",data:t.toString("base64"),fileName:s,caption:r}),`api-file-${++this.messageCounter}`}async sendVoice(e,t,s){let r=this.streams.get(e);return r&&this.writeSseEvent(r,"attachment",{type:"attachment",attachmentType:"voice",data:t.toString("base64"),caption:s}),`api-voice-${++this.messageCounter}`}endStream(e){let t=this.streams.get(e);t&&(this.writeSseEvent(t,"done",{type:"done"}),t.end(),this.streams.delete(e))}handleRequest(e,t){if(t.setHeader("X-Content-Type-Options","nosniff"),t.setHeader("X-Frame-Options","DENY"),t.setHeader("Access-Control-Allow-Origin",this.corsOrigin),t.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),e.method==="OPTIONS"){t.writeHead(204),t.end();return}let s=new URL(e.url??"/",`http://${e.headers.host??"localhost"}`);if(s.pathname==="/api/health"&&e.method==="GET")this.handleHealth(t);else if(s.pathname==="/api/metrics"&&e.method==="GET")this.handleMetrics(t);else if(s.pathname==="/api/message"&&e.method==="POST")this.handleMessage(e,t);else if(s.pathname==="/api/dashboard"&&e.method==="GET")this.handleDashboard(e,t);else if(s.pathname.startsWith("/api/webhook/")&&e.method==="POST"){let r=s.pathname.slice(13);this.handleWebhook(e,t,r)}else this.webUiPath&&s.pathname.startsWith("/alfred/")&&e.method==="GET"?this.serveStaticFile(s.pathname,t):this.webUiPath&&s.pathname==="/alfred"&&e.method==="GET"?(t.writeHead(302,{Location:"/alfred/"}),t.end()):(t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not found"})))}checkAuth(e,t){if(!this.apiToken)return!0;let s=e.headers.authorization,r=`Bearer ${this.apiToken}`;return!s||s.length!==r.length||!ti.timingSafeEqual(Buffer.from(s),Buffer.from(r))?(t.writeHead(401,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Unauthorized"})),!1):!0}handleDashboard(e,t){if(this.checkAuth(e,t)){if(!this.dashboardFn){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Dashboard not configured"}));return}try{let s=this.dashboardFn();t.writeHead(200,{"Content-Type":"application/json"}),t.end(JSON.stringify(s))}catch{t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Dashboard data fetch failed"}))}}}serveStaticFile(e,t){if(!this.webUiPath){t.writeHead(404),t.end();return}let s=e.replace(/^\/alfred/,"");(!s||s==="/")&&(s="/index.html");let r=sr.resolve(this.webUiPath,"."+s);if(!r.startsWith(sr.resolve(this.webUiPath))){t.writeHead(403),t.end();return}let n=r;if(!Kt.existsSync(n))if(Kt.existsSync(n+".html"))n=n+".html";else if(Kt.existsSync(sr.join(n,"index.html")))n=sr.join(n,"index.html");else{t.writeHead(404,{"Content-Type":"text/html"}),t.end("Not found");return}try{if(Kt.statSync(n).isDirectory()){let u=sr.join(n,"index.html");if(Kt.existsSync(u))n=u;else{t.writeHead(404),t.end();return}}}catch{t.writeHead(404),t.end();return}let o=Kt.statSync(n),i=sr.extname(n).toLowerCase(),a=Lg[i]??"application/octet-stream",c=i===".html"?"no-cache":"public, max-age=31536000, immutable";t.writeHead(200,{"Content-Type":a,"Content-Length":o.size,"Cache-Control":c}),Kt.createReadStream(n).pipe(t)}handleHealth(e){let t=this.healthCheckFn?.()??{},s=t.db!==!1?"ok":"degraded",r=s==="ok"?200:503;e.writeHead(r,{"Content-Type":"application/json"}),e.end(JSON.stringify({status:s,...t,timestamp:new Date().toISOString()}))}handleMetrics(e){this.metricsFn?(e.writeHead(200,{"Content-Type":"text/plain; version=0.0.4; charset=utf-8"}),e.end(this.metricsFn())):this.handleHealth(e)}handleMessage(e,t){if(!this.checkAuth(e,t))return;let s="",r=0,n=!1;e.on("data",o=>{if(!n){if(r+=o.length,r>Sm){n=!0,t.writeHead(413,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Payload too large"})),e.destroy();return}s+=o.toString()}}),e.on("end",()=>{if(!n)try{let o=JSON.parse(s),i=o.text;if(!i||typeof i!="string"){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:'Missing or invalid "text" field'}));return}let a=o.chatId??`api-chat-${ti.randomUUID()}`,c=o.userId??"api-user",d=this.streams.get(a);d&&(this.writeSseEvent(d,"done",{type:"done"}),d.end()),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}),this.streams.set(a,t),e.on("close",()=>{this.streams.delete(a)}),this.messageCounter++;let u={id:`api-${this.messageCounter}`,platform:"api",chatId:a,chatType:"dm",userId:c,userName:c,displayName:"API User",text:i,timestamp:new Date};this.emit("message",u)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid JSON body"}))}})}handleWebhook(e,t,s){let r=this.webhooks.get(s);if(!r){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:`Webhook "${s}" not found`}));return}let n="",o=0,i=!1;e.on("data",a=>{if(!i){if(o+=a.length,o>Sm){i=!0,t.writeHead(413,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Payload too large"})),e.destroy();return}n+=a.toString()}}),e.on("end",async()=>{if(i)return;let a=e.headers["x-webhook-signature"];if(!a){t.writeHead(401,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Missing X-Webhook-Signature header"}));return}let c=ti.createHmac("sha256",r.secret).update(n).digest(),d=Buffer.from(a,"hex");if(d.length!==c.length||!ti.timingSafeEqual(d,c)){t.writeHead(403,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid signature"}));return}try{let u=JSON.parse(n);await r.callback(u),t.writeHead(200,{"Content-Type":"application/json"}),t.end(JSON.stringify({ok:!0}))}catch(u){t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:u instanceof Error?u.message:"Internal error"}))}})}writeSseEvent(e,t,s){e.writableEnded||e.write(`event: ${t}
|
|
1402
|
+
`),this.prompt(),r}async editMessage(e,t,s,r){xc.clearLine(process.stdout,0),xc.cursorTo(process.stdout,0),process.stdout.write(`Alfred: ${s}`)}async deleteMessage(e,t){}prompt(){this.rl?.prompt()}}});import Ng from"node:http";import Kt from"node:fs";import sr from"node:path";import ti from"node:crypto";var Lg,Sm,si,vm=T(()=>{"use strict";ct();Lg={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"application/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".woff":"font/woff",".woff2":"font/woff2",".txt":"text/plain; charset=utf-8"},Sm=1048576,si=class extends ye{static{m(this,"HttpAdapter")}platform="api";server=null;streams=new Map;messageCounter=0;port;host;apiToken;corsOrigin;healthCheckFn;metricsFn;dashboardFn;webUiPath;webhooks=new Map;constructor(e,t,s){if(super(),this.port=e,this.host=t,this.apiToken=s?.apiToken,this.corsOrigin=s?.corsOrigin??"http://localhost:3420",this.healthCheckFn=s?.healthCheck,this.metricsFn=s?.metricsCallback,this.dashboardFn=s?.dashboardCallback,this.webUiPath=s?.webUiPath,s?.webhooks)for(let r of s.webhooks)this.webhooks.set(r.name,r)}addWebhook(e){this.webhooks.set(e.name,e)}async connect(){this.status="connecting",this.server=Ng.createServer((e,t)=>{this.handleRequest(e,t)}),await new Promise((e,t)=>{this.server.listen(this.port,this.host,()=>{e()}),this.server.once("error",t)}),this.status="connected",this.emit("connected")}async disconnect(){for(let[e,t]of this.streams)this.writeSseEvent(t,"done",{type:"done"}),t.end(),this.streams.delete(e);this.server&&(await new Promise(e=>{this.server.close(()=>e())}),this.server=null),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=`api-resp-${++this.messageCounter}`,n=this.streams.get(e);return n&&this.writeSseEvent(n,"response",{type:"response",text:t}),r}async editMessage(e,t,s,r){let n=this.streams.get(e);n&&this.writeSseEvent(n,"status",{type:"status",text:s})}async deleteMessage(e,t){}async sendPhoto(e,t,s){let r=this.streams.get(e);return r&&this.writeSseEvent(r,"attachment",{type:"attachment",attachmentType:"image",data:t.toString("base64"),caption:s}),`api-photo-${++this.messageCounter}`}async sendFile(e,t,s,r){let n=this.streams.get(e);return n&&this.writeSseEvent(n,"attachment",{type:"attachment",attachmentType:"file",data:t.toString("base64"),fileName:s,caption:r}),`api-file-${++this.messageCounter}`}async sendVoice(e,t,s){let r=this.streams.get(e);return r&&this.writeSseEvent(r,"attachment",{type:"attachment",attachmentType:"voice",data:t.toString("base64"),caption:s}),`api-voice-${++this.messageCounter}`}endStream(e){let t=this.streams.get(e);t&&(this.writeSseEvent(t,"done",{type:"done"}),t.end(),this.streams.delete(e))}handleRequest(e,t){if(t.setHeader("X-Content-Type-Options","nosniff"),t.setHeader("X-Frame-Options","DENY"),t.setHeader("Access-Control-Allow-Origin",this.corsOrigin),t.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),e.method==="OPTIONS"){t.writeHead(204),t.end();return}let s=new URL(e.url??"/",`http://${e.headers.host??"localhost"}`);if(s.pathname==="/api/health"&&e.method==="GET")this.handleHealth(t);else if(s.pathname==="/api/metrics"&&e.method==="GET")this.handleMetrics(t);else if(s.pathname==="/api/message"&&e.method==="POST")this.handleMessage(e,t);else if(s.pathname==="/api/dashboard"&&e.method==="GET")this.handleDashboard(e,t);else if(s.pathname.startsWith("/api/webhook/")&&e.method==="POST"){let r=s.pathname.slice(13);this.handleWebhook(e,t,r)}else this.webUiPath&&s.pathname.startsWith("/alfred/")&&e.method==="GET"?this.serveStaticFile(s.pathname,t):this.webUiPath&&s.pathname==="/alfred"&&e.method==="GET"?(t.writeHead(302,{Location:"/alfred/"}),t.end()):(t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not found"})))}checkAuth(e,t){if(!this.apiToken)return!0;let s=e.headers.authorization,r=`Bearer ${this.apiToken}`;return!s||s.length!==r.length||!ti.timingSafeEqual(Buffer.from(s),Buffer.from(r))?(t.writeHead(401,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Unauthorized"})),!1):!0}handleDashboard(e,t){if(this.checkAuth(e,t)){if(!this.dashboardFn){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Dashboard not configured"}));return}try{let s=this.dashboardFn();t.writeHead(200,{"Content-Type":"application/json"}),t.end(JSON.stringify(s))}catch{t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Dashboard data fetch failed"}))}}}serveStaticFile(e,t){if(!this.webUiPath){t.writeHead(404),t.end();return}let s=e.replace(/^\/alfred/,"");(!s||s==="/")&&(s="/index.html");let r=sr.resolve(this.webUiPath,"."+s);if(!r.startsWith(sr.resolve(this.webUiPath))){t.writeHead(403),t.end();return}let n=r;if(!Kt.existsSync(n))if(Kt.existsSync(n+".html"))n=n+".html";else if(Kt.existsSync(sr.join(n,"index.html")))n=sr.join(n,"index.html");else{t.writeHead(404,{"Content-Type":"text/html"}),t.end("Not found");return}try{if(Kt.statSync(n).isDirectory()){let u=sr.join(n,"index.html");if(Kt.existsSync(u))n=u;else{t.writeHead(404),t.end();return}}}catch{t.writeHead(404),t.end();return}let o=Kt.statSync(n),i=sr.extname(n).toLowerCase(),a=Lg[i]??"application/octet-stream",c=i===".html"?"no-cache":"public, max-age=31536000, immutable";t.writeHead(200,{"Content-Type":a,"Content-Length":o.size,"Cache-Control":c}),Kt.createReadStream(n).pipe(t)}handleHealth(e){let t=this.healthCheckFn?.()??{},s=t.db!==!1?"ok":"degraded",r=s==="ok"?200:503;e.writeHead(r,{"Content-Type":"application/json"}),e.end(JSON.stringify({status:s,...t,timestamp:new Date().toISOString()}))}handleMetrics(e){this.metricsFn?(e.writeHead(200,{"Content-Type":"text/plain; version=0.0.4; charset=utf-8"}),e.end(this.metricsFn())):this.handleHealth(e)}handleMessage(e,t){if(!this.checkAuth(e,t))return;let s="",r=0,n=!1;e.on("data",o=>{if(!n){if(r+=o.length,r>Sm){n=!0,t.writeHead(413,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Payload too large"})),e.destroy();return}s+=o.toString()}}),e.on("end",()=>{if(!n)try{let o=JSON.parse(s),i=o.text;if(!i||typeof i!="string"){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:'Missing or invalid "text" field'}));return}let a=o.chatId??`api-chat-${ti.randomUUID()}`,c=o.userId??"api-user",d=this.streams.get(a);d&&(this.writeSseEvent(d,"done",{type:"done"}),d.end()),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":this.corsOrigin,"Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Authorization","X-Content-Type-Options":"nosniff"}),t.flushHeaders(),this.streams.set(a,t),e.on("close",()=>{this.streams.delete(a)}),this.messageCounter++;let u={id:`api-${this.messageCounter}`,platform:"api",chatId:a,chatType:"dm",userId:c,userName:c,displayName:"API User",text:i,timestamp:new Date};this.emit("message",u)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid JSON body"}))}})}handleWebhook(e,t,s){let r=this.webhooks.get(s);if(!r){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:`Webhook "${s}" not found`}));return}let n="",o=0,i=!1;e.on("data",a=>{if(!i){if(o+=a.length,o>Sm){i=!0,t.writeHead(413,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Payload too large"})),e.destroy();return}n+=a.toString()}}),e.on("end",async()=>{if(i)return;let a=e.headers["x-webhook-signature"];if(!a){t.writeHead(401,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Missing X-Webhook-Signature header"}));return}let c=ti.createHmac("sha256",r.secret).update(n).digest(),d=Buffer.from(a,"hex");if(d.length!==c.length||!ti.timingSafeEqual(d,c)){t.writeHead(403,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid signature"}));return}try{let u=JSON.parse(n);await r.callback(u),t.writeHead(200,{"Content-Type":"application/json"}),t.end(JSON.stringify({ok:!0}))}catch(u){t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:u instanceof Error?u.message:"Internal error"}))}})}writeSseEvent(e,t,s){e.writableEnded||e.write(`event: ${t}
|
|
1403
1403
|
data: ${JSON.stringify(s)}
|
|
1404
1404
|
|
|
1405
1405
|
`)}}});var _t={};pe(_t,{CLIAdapter:()=>ei,DiscordAdapter:()=>Yo,HttpAdapter:()=>si,MatrixAdapter:()=>Jo,MessagingAdapter:()=>ye,SignalAdapter:()=>Qo,TelegramAdapter:()=>Ko,WhatsAppAdapter:()=>Zo});var kt=T(()=>{"use strict";ct();wm();Tm();_m();km();bm();Em();vm()});import rr from"node:fs";import Oe from"node:path";import Dg from"js-yaml";var Vt,$m=T(()=>{"use strict";Gi();co();nt();aa();mo();re();Va();Ja();Za();Qa();ec();tc();sc();rc();nc();oc();ic();ac();cc();mm();dc();fc();gc();yc();wc();Tc();_c();kc();bc();vc();$c();Vt=class{static{m(this,"Alfred")}config;logger;database;pipeline;llmProvider;reminderScheduler;backgroundTaskRunner;proactiveScheduler;watchEngine;confirmationQueue;adapters=new Map;formatter=new Tn;userRepo;skillRegistry;mcpManager;calendarSkill;calendarWatcher;todoWatcher;reasoningEngine;usageRepo;auditRepo;summaryRepo;activityRepo;memoryRepo;watchRepo;scheduledActionRepo;skillHealthRepo;skillHealthTracker;healthCheckTimer;startedAt=new Date().toISOString();constructor(e){this.config=e,this.logger=Ar("alfred",e.logger.level)}async initialize(){this.logger.info("Initializing Alfred..."),this.database=new mt(this.config.storage.path);let e=this.database.getDb(),t=new es(e),s=new ts(e);this.userRepo=s;let r=new pt(e);this.auditRepo=r;let n=new ss(e);this.memoryRepo=n;let o=new rs(e),i=new ns(e),a=new os(e),c=new is(e),d=new as(e),u=new ls(e);this.scheduledActionRepo=u;let p=new ft(e);this.activityRepo=p;let h=new Dn(p,this.logger.child({component:"activity"})),f=new gs(e);this.skillHealthRepo=f;let g=new Mn(f,this.logger.child({component:"skill-health"}),h);this.skillHealthTracker=g,this.logger.info("Storage initialized");let y=new Ur,_=this.loadSecurityRules();y.loadRules(_);let S=new Fr(y,r,this.logger.child({component:"security"}));this.logger.info({ruleCount:_.length},"Security engine initialized");let E=oa(this.config.llm,this.logger.child({component:"llm"}));await E.initialize(),this.llmProvider=E;let v=new fs(e);this.usageRepo=v,E.setPersist((C,j,Z,te,Se,or)=>{v.record(C,j,Z,te,Se,or)});let I=new _n(E,a,this.logger.child({component:"embeddings"})),M=this.config.activeLearning?.enabled!==!1,q,G;M&&(q=new Rn({llm:E,memoryRepo:n,logger:this.logger.child({component:"active-learning"}),embeddingService:I,minMessageLength:this.config.activeLearning?.minMessageLength,minConfidence:this.config.activeLearning?.minConfidence,maxExtractionsPerMinute:this.config.activeLearning?.maxExtractionsPerMinute}),G=new xn(n,this.logger.child({component:"memory-retriever"}),I),this.logger.info("Active learning & memory retriever initialized"));let P=new hs(e);this.summaryRepo=P;let ee=new Cn(E,P,this.logger.child({component:"summarizer"}));this.logger.info("Conversation summarizer initialized");let ue=new ks(this.logger.child({component:"sandbox"})),L=this.skillRegistry=new _s;L.register(new bs),L.register(new Es),L.register(new Ss(this.config.search?{provider:this.config.search.provider,apiKey:this.config.search.apiKey,baseUrl:this.config.search.baseUrl}:void 0)),L.register(new vs(o)),L.register(new $s(i));let ne=new us(e);if(L.register(new zs(ne)),L.register(new As),L.register(new Is),L.register(new Rs(n,I)),L.register(new xs(E,L,ue,S)),this.config.email?.accounts?.length){let C=new Map;for(let Z of this.config.email.accounts)try{Z.provider==="microsoft"&&!Z.microsoft?.clientId&&this.config.calendar?.microsoft&&(Z.microsoft={...this.config.calendar.microsoft});let te=await jr(Z);C.set(Z.name,te),this.logger.info({account:Z.name,provider:Z.provider??"imap-smtp"},"Email account initialized")}catch(te){this.logger.warn({err:te,account:Z.name},"Email account initialization failed, skipping")}let j=C.size>0?new gt(C):new gt;j.setLLM(E),L.register(j)}else{let C=new gt;C.setLLM(E),L.register(C)}L.register(new Cs),L.register(new Ns);let ie=new Ks;ie.setReloadCallback(C=>this.reloadService(C)),L.register(ie),L.register(new Ds),L.register(new Ms),L.register(new Os),L.register(new Ps(s)),L.register(new Us(s,c,this.adapters,(C,j)=>t.findByPlatformAndUser(C,j)));let Q=new Fs(d);L.register(Q),L.register(new js(u));let B=new ds(e),he=new kn(B,I,this.logger.child({component:"documents"}));L.register(new Bs(B,he,I));let Ke,le;if(this.config.calendar)try{le=await Wr(this.config.calendar),Ke=new Ft(le),L.register(Ke),this.logger.info({provider:this.config.calendar.provider},"Calendar initialized")}catch(C){this.logger.warn({err:C},"Calendar initialization failed, continuing without calendar")}if(this.calendarSkill=Ke,le&&this.config.calendar?.vorlauf?.enabled){let C=new ht(e),j=this.config.security?.ownerUserId;j&&(this.calendarWatcher=new Nn(le,C,this.adapters,j,"telegram",this.config.calendar.vorlauf,this.logger.child({component:"calendar-watcher"}),h))}{let C=this.config.security?.ownerUserId;if(C){let j=new ht(e);this.todoWatcher=new Ln(ne,j,this.adapters,C,"telegram",{minutesBefore:30},this.logger.child({component:"todo-watcher"}),h)}}if(this.config.mcp?.servers?.length){let{MCPManager:C}=await Promise.resolve().then(()=>(re(),ce));this.mcpManager=new C(this.logger.child({component:"mcp"})),await this.mcpManager.initialize(this.config.mcp);for(let j of this.mcpManager.getSkills())L.register(j);this.logger.info({mcpSkills:this.mcpManager.getSkills().length},"MCP skills registered")}if(this.config.codeSandbox?.enabled){let{CodeExecutionSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C({allowedLanguages:this.config.codeSandbox.allowedLanguages,maxTimeoutMs:this.config.codeSandbox.maxTimeoutMs})),this.logger.info("Code sandbox enabled")}if(this.config.codeAgents?.enabled){let{CodeAgentSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C({agents:this.config.codeAgents.agents,forge:this.config.codeAgents.forge},E)),this.logger.info({agents:this.config.codeAgents.agents.map(j=>j.name)},"Code agent skill enabled")}if(this.config.projectAgents?.enabled&&this.config.codeAgents?.agents){let{ProjectAgentSkill:C}=await Promise.resolve().then(()=>(re(),ce)),{ProjectAgentSessionRepository:j}=await Promise.resolve().then(()=>(co(),sd)),Z=new j(e),te=new C({...this.config.projectAgents,agents:this.config.codeAgents.agents},E,Z),{ProjectAgentRunner:Se}=await Promise.resolve().then(()=>(Ac(),gm)),or=new Se(new Map(this.config.codeAgents.agents.map(ir=>[ir.name,ir])),E,Z,this.adapters,this.logger.child({component:"project-agent"}));te.setRunner(or),L.register(te),this.logger.info("Project agent skill enabled")}if(this.config.proxmox){let{ProxmoxSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.proxmox)),this.logger.info({baseUrl:this.config.proxmox.baseUrl},"Proxmox skill enabled")}if(this.config.unifi){let{UniFiSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.unifi)),this.logger.info({baseUrl:this.config.unifi.baseUrl},"UniFi skill enabled")}if(this.config.homeassistant){let{HomeAssistantSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.homeassistant)),this.logger.info({baseUrl:this.config.homeassistant.baseUrl},"Home Assistant skill enabled")}if(this.config.contacts)try{let{ContactsSkill:C,createContactsProvider:j}=await Promise.resolve().then(()=>(re(),ce)),Z=await j(this.config.contacts);L.register(new C(Z)),this.logger.info({provider:this.config.contacts.provider},"Contacts skill enabled")}catch(C){this.logger.warn({err:C},"Contacts initialization failed, continuing without contacts")}if(this.config.docker){let{DockerSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.docker)),this.logger.info("Docker skill enabled")}if(this.config.bmw){let{BMWSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.bmw)),this.logger.info("BMW CarData skill enabled")}if(this.config.routing){let{RoutingSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.routing)),this.logger.info("Routing skill enabled")}if(this.config.todo){let{MicrosoftTodoSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.todo)),this.logger.info("Microsoft To Do skill enabled")}if(this.config.proxmox||this.config.unifi||this.config.homeassistant||this.config.proxmoxBackup){let{MonitorSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C({proxmox:this.config.proxmox,unifi:this.config.unifi,homeassistant:this.config.homeassistant,proxmoxBackup:this.config.proxmoxBackup})),this.logger.info("Infrastructure monitor skill enabled")}{let{EnergyPriceSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.energy)),this.logger.info({grid:this.config.energy?.gridName},"Energy price skill registered")}{let{MarketplaceSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(this.config.marketplace)),this.logger.info("Marketplace skill registered")}{let{BriefingSkill:C}=await Promise.resolve().then(()=>(re(),ce));L.register(new C(L,this.config,n)),this.logger.info("Briefing skill registered")}L.register(new Js(n)),this.logger.info("Feed reader skill registered"),this.logger.info({skills:L.getAll().map(C=>C.metadata.name)},"Skills registered");let Ee;if(this.config.speech?.apiKey&&(Ee=new fn(this.config.speech,this.logger.child({component:"speech"})),this.logger.info({provider:this.config.speech.provider},"Speech-to-text initialized")),this.config.speech?.ttsEnabled){let C=new gn(this.config.speech,this.logger.child({component:"tts"}));L.register(new Hs(C)),this.logger.info("Text-to-speech skill registered")}let me=this.detectImageGenProvider();if(me){let C=new yn(me,this.logger.child({component:"image-gen"}));L.register(new Ws(C)),this.logger.info({provider:me.provider},"Image generation skill registered")}try{let C=new wn(this.logger.child({component:"transit"}));L.register(new Gs(C)),this.logger.info("Public transit skill registered")}catch(C){this.logger.warn({err:C},"Failed to register transit skill")}let Pe=new mn(t),Ze=Oe.resolve(Oe.dirname(this.config.storage.path),"inbox");this.pipeline=new pn({llm:E,conversationManager:Pe,users:s,logger:this.logger.child({component:"pipeline"}),skillRegistry:L,skillSandbox:ue,securityManager:S,memoryRepo:n,speechTranscriber:Ee,inboxPath:Ze,embeddingService:I,activeLearning:q,memoryRetriever:G,maxHistoryMessages:this.config.conversation?.maxHistoryMessages??100,documentProcessor:he,conversationSummarizer:ee}),this.reminderScheduler=new hn(o,async(C,j,Z)=>{let te=this.adapters.get(C);te?await te.sendMessage(j,Z):this.logger.warn({platform:C,chatId:j},"No adapter for reminder platform")},this.logger.child({component:"reminders"}),15e3,{getMasterUserId:m(C=>s.getMasterUserId(C),"getMasterUserId"),getLinkedUsers:m(C=>s.getLinkedUsers(C),"getLinkedUsers"),findConversation:m((C,j)=>t.findByPlatformAndUser(C,j),"findConversation")}),this.backgroundTaskRunner=new bn(L,ue,d,this.adapters,s,this.logger.child({component:"background-tasks"}),h,g);let Ve=new En(L,ue,d,this.adapters,s,this.logger.child({component:"persistent-agents"}),h);this.backgroundTaskRunner.setPersistentRunner(Ve),Q.setPersistentRunner(Ve),this.proactiveScheduler=new Sn(u,L,ue,E,this.adapters,s,this.logger.child({component:"proactive-scheduler"}),this.pipeline,this.formatter,Pe,h);let Qe=new ms(e);this.watchRepo=Qe,L.register(new Vs(Qe,L));let De=new ps(e);this.confirmationQueue=new An(De,L,ue,this.adapters,this.logger.child({component:"confirmation-queue"}),h);let Et=new ws(e),St=new tr(Et,n,this.logger.child({component:"feedback"}));this.confirmationQueue.setFeedbackService(St),q&&q.setFeedbackService(St),this.watchEngine=new qo(Qe,L,ue,this.adapters,s,this.logger.child({component:"watch-engine"}),this.confirmationQueue,h,g,E);let et=new ys(e),X=new Ys(et);L.register(X);let z=new On(et,L,ue,this.logger.child({component:"workflow-runner"}),h,g);X.setRunner(z);{let C=this.config.security?.ownerUserId;if(C&&this.config.reasoning?.enabled!==!1){let j=new ht(e);this.reasoningEngine=new Un(le,ne,Qe,n,p,f,j,L,ue,E,this.adapters,s,C,"telegram",this.config.reasoning,this.logger.child({component:"reasoning-engine"}),h,this.config.briefing?.location,Et,this.confirmationQueue)}}this.pipeline.setConfirmationQueue(this.confirmationQueue),this.pipeline.setActivityLogger(h),this.pipeline.setSkillHealthTracker(g),await this.initializeAdapters(),this.logger.info("Alfred initialized")}async initializeAdapters(){let{config:e}=this;if(e.telegram.enabled&&e.telegram.token){let{TelegramAdapter:t}=await Promise.resolve().then(()=>(kt(),_t));this.adapters.set("telegram",new t(e.telegram.token)),this.logger.info("Telegram adapter registered")}if(e.discord?.enabled&&e.discord.token){let{DiscordAdapter:t}=await Promise.resolve().then(()=>(kt(),_t));this.adapters.set("discord",new t(e.discord.token)),this.logger.info("Discord adapter registered")}if(e.whatsapp?.enabled){let{WhatsAppAdapter:t}=await Promise.resolve().then(()=>(kt(),_t));this.adapters.set("whatsapp",new t(e.whatsapp.dataPath)),this.logger.info("WhatsApp adapter registered")}if(e.matrix?.enabled&&e.matrix.accessToken){let{MatrixAdapter:t}=await Promise.resolve().then(()=>(kt(),_t));this.adapters.set("matrix",new t(e.matrix.homeserverUrl,e.matrix.accessToken,e.matrix.userId)),this.logger.info("Matrix adapter registered")}if(e.signal?.enabled&&e.signal.phoneNumber){let{SignalAdapter:t}=await Promise.resolve().then(()=>(kt(),_t));this.adapters.set("signal",new t(e.signal.apiUrl,e.signal.phoneNumber)),this.logger.info("Signal adapter registered")}if(e.api?.enabled!==!1){let{HttpAdapter:t}=await Promise.resolve().then(()=>(kt(),_t)),s=e.api?.port??3420,r=e.api?.host??"127.0.0.1";e.api?.token?this.logger.info("HTTP API authentication enabled"):this.logger.warn("HTTP API has no authentication token configured (api.token). API is open."),this.adapters.set("api",new t(s,r,{apiToken:e.api?.token,corsOrigin:e.api?.corsOrigin,healthCheck:m(()=>{let n;try{let o=this.config.storage.path,i=rr.statSync(o);n={path:o,sizeBytes:i.size}}catch{}return{db:!!this.database,uptime:Math.floor(process.uptime()),startedAt:this.startedAt,adapters:Object.fromEntries([...this.adapters].map(([o,i])=>[o,i.getStatus()])),metrics:this.pipeline.getMetrics(),costs:this.llmProvider.getCostSummary(),todayUsage:this.usageRepo?.getDaily(new Date().toISOString().slice(0,10)),watchesActive:this.watchRepo?.countEnabled()??0,schedulersActive:this.scheduledActionRepo?.countEnabled()??0,llmProviders:this.llmProvider.getProviderStatuses(),diskUsage:n}},"healthCheck"),metricsCallback:m(()=>this.buildPrometheusMetrics(),"metricsCallback"),dashboardCallback:m(()=>({watches:this.watchRepo?.getEnabled()??[],scheduled:this.scheduledActionRepo?.getAll()??[],skillHealth:this.skillHealthRepo?.getAll()??[]}),"dashboardCallback"),webUiPath:e.api?.webUi!==!1?this.resolveWebUiPath():void 0})),this.logger.info({port:s,host:r,webUi:e.api?.webUi!==!1},"HTTP API adapter registered")}}async start(){this.logger.info("Starting Alfred...");for(let[e,t]of this.adapters){this.setupAdapterHandlers(e,t);try{await t.connect(),this.logger.info({platform:e},"Adapter connected")}catch(s){this.logger.error({platform:e,err:s},"Adapter connection failed \u2014 skipping")}}if(this.reminderScheduler?.start(),this.backgroundTaskRunner?.start(),this.proactiveScheduler?.start(),this.watchEngine?.start(),this.confirmationQueue?.start(),this.calendarWatcher?.start(),this.todoWatcher?.start(),this.reasoningEngine?.start(),this.config.webhooks?.length&&this.watchEngine){let e=this.adapters.get("api");if(e&&"addWebhook"in e){let t=e;for(let s of this.config.webhooks)t.addWebhook({name:s.name,secret:s.secret,callback:m(async r=>{if(s.watchId&&this.watchEngine&&await this.watchEngine.triggerWatch(s.watchId),s.chatId&&s.platform){let n=this.adapters.get(s.platform);if(n){let o=`\u{1F514} Webhook "${s.name}" triggered`+(r.action?`: ${r.action}`:"");await n.sendMessage(s.chatId,o)}}},"callback")}),this.logger.info({name:s.name,watchId:s.watchId},"Webhook registered")}}try{let e={audit:this.auditRepo?.cleanup(90)??0,summaries:this.summaryRepo?.cleanup(180)??0,activity:this.activityRepo?.cleanup(90)??0,usage:this.usageRepo?.cleanup(365)??0,expiredMemories:this.memoryRepo?.cleanupExpired()??0};(e.audit||e.summaries||e.activity||e.usage)&&this.logger.info(e,"Startup DB cleanup completed")}catch(e){this.logger.warn({err:e},"Startup DB cleanup failed")}this.skillHealthTracker&&(this.healthCheckTimer=setInterval(()=>this.skillHealthTracker.checkReEnables(),5*6e4)),this.adapters.size===0&&this.logger.warn("No messaging adapters enabled. Configure at least one platform."),this.logger.info(`Alfred is running with ${this.adapters.size} adapter(s)`)}async startWithCLI(){this.adapters.clear();let{CLIAdapter:e}=await Promise.resolve().then(()=>(kt(),_t)),t=new e;this.adapters.set("cli",t),t.on("disconnected",()=>{this.stop().then(()=>process.exit(0))}),await this.start()}async stop(){this.logger.info("Stopping Alfred..."),this.reminderScheduler?.stop(),this.backgroundTaskRunner?.stop(),this.proactiveScheduler?.stop(),this.watchEngine?.stop(),this.confirmationQueue?.stop(),this.calendarWatcher?.stop(),this.todoWatcher?.stop(),this.reasoningEngine?.stop(),this.healthCheckTimer&&(clearInterval(this.healthCheckTimer),this.healthCheckTimer=void 0),this.mcpManager&&await this.mcpManager.shutdown();let e=5e3;for(let[t,s]of this.adapters)try{await Promise.race([s.disconnect(),new Promise(r=>setTimeout(r,e))]),this.logger.info({platform:t},"Adapter disconnected")}catch(r){this.logger.error({platform:t,err:r},"Failed to disconnect adapter")}try{this.database&&(this.database.getDb().pragma("wal_checkpoint(TRUNCATE)"),this.database.close())}catch{}this.logger.info("Alfred stopped")}async reloadService(e){try{zi();let t=new fe().loadConfig();if(this.skillRegistry.has(e)&&this.skillRegistry.unregister(e),e==="proxmox"&&t.proxmox){let{ProxmoxSkill:s}=await Promise.resolve().then(()=>(re(),ce));this.skillRegistry.register(new s(t.proxmox)),this.config.proxmox=t.proxmox,this.logger.info({baseUrl:t.proxmox.baseUrl},"Proxmox skill hot-reloaded")}if(e==="unifi"&&t.unifi){let{UniFiSkill:s}=await Promise.resolve().then(()=>(re(),ce));this.skillRegistry.register(new s(t.unifi)),this.config.unifi=t.unifi,this.logger.info({baseUrl:t.unifi.baseUrl},"UniFi skill hot-reloaded")}if(e==="homeassistant"&&t.homeassistant){let{HomeAssistantSkill:s}=await Promise.resolve().then(()=>(re(),ce));this.skillRegistry.register(new s(t.homeassistant)),this.config.homeassistant=t.homeassistant,this.logger.info({baseUrl:t.homeassistant.baseUrl},"Home Assistant skill hot-reloaded")}if(e==="contacts"&&t.contacts){let{ContactsSkill:s,createContactsProvider:r}=await Promise.resolve().then(()=>(re(),ce)),n=await r(t.contacts);this.skillRegistry.register(new s(n)),this.config.contacts=t.contacts,this.logger.info({provider:t.contacts.provider},"Contacts skill hot-reloaded")}if(e==="docker"&&t.docker){let{DockerSkill:s}=await Promise.resolve().then(()=>(re(),ce));this.skillRegistry.register(new s(t.docker)),this.config.docker=t.docker,this.logger.info("Docker skill hot-reloaded")}if(e==="bmw"&&t.bmw){let{BMWSkill:s}=await Promise.resolve().then(()=>(re(),ce));this.skillRegistry.register(new s(t.bmw)),this.config.bmw=t.bmw,this.logger.info("BMW CarData skill hot-reloaded")}if(e==="routing"&&t.routing){let{RoutingSkill:s}=await Promise.resolve().then(()=>(re(),ce));this.skillRegistry.register(new s(t.routing)),this.config.routing=t.routing,this.logger.info("Routing skill hot-reloaded")}if(e==="todo"&&t.todo){let{MicrosoftTodoSkill:s}=await Promise.resolve().then(()=>(re(),ce));this.skillRegistry.register(new s(t.todo)),this.config.todo=t.todo,this.logger.info("Microsoft To Do skill hot-reloaded")}return{success:!0}}catch(t){let s=t instanceof Error?t.message:String(t);return this.logger.error({err:t,service:e},"Failed to hot-reload service"),{success:!1,error:s}}}resolveWebUiPath(){let e;try{e=Oe.dirname(new URL(import.meta.url).pathname),process.platform==="win32"&&e.startsWith("/")&&(e=e.slice(1))}catch{e=process.cwd()}let t=[Oe.join(process.cwd(),"web-ui"),Oe.join(e,"..","web-ui"),Oe.join(e,"web-ui"),Oe.join(e,"..","..","web-ui"),Oe.join(e,"..","..","apps","web","out")];for(let s of t)try{let r=Oe.resolve(s);if(rr.existsSync(Oe.join(r,"index.html")))return this.logger.info({path:r},"Web UI found"),r}catch{}this.logger.debug("Web UI not found \u2014 serving API only")}buildPrometheusMetrics(){let e=[],t=Math.floor(process.uptime());e.push("# HELP alfred_uptime_seconds Process uptime in seconds"),e.push("# TYPE alfred_uptime_seconds gauge"),e.push(`alfred_uptime_seconds ${t}`);let s=this.pipeline.getMetrics();e.push("# HELP alfred_requests_total Total messages processed"),e.push("# TYPE alfred_requests_total counter"),e.push(`alfred_requests_total ${s.requestsTotal}`),e.push("# HELP alfred_requests_success_total Successful requests"),e.push("# TYPE alfred_requests_success_total counter"),e.push(`alfred_requests_success_total ${s.requestsSuccess}`),e.push("# HELP alfred_requests_failed_total Failed requests"),e.push("# TYPE alfred_requests_failed_total counter"),e.push(`alfred_requests_failed_total ${s.requestsFailed}`),e.push("# HELP alfred_request_duration_avg_ms Average request duration"),e.push("# TYPE alfred_request_duration_avg_ms gauge"),e.push(`alfred_request_duration_avg_ms ${s.avgDurationMs}`);let r=this.llmProvider.getCostSummary();e.push("# HELP alfred_llm_input_tokens_total Total LLM input tokens (session)"),e.push("# TYPE alfred_llm_input_tokens_total counter"),e.push(`alfred_llm_input_tokens_total ${r.totalInputTokens}`),e.push("# HELP alfred_llm_output_tokens_total Total LLM output tokens (session)"),e.push("# TYPE alfred_llm_output_tokens_total counter"),e.push(`alfred_llm_output_tokens_total ${r.totalOutputTokens}`),e.push("# HELP alfred_llm_cost_usd_total Total LLM cost in USD (session)"),e.push("# TYPE alfred_llm_cost_usd_total counter"),e.push(`alfred_llm_cost_usd_total ${r.totalCostUsd}`),e.push("# HELP alfred_llm_calls_total LLM calls by model"),e.push("# TYPE alfred_llm_calls_total counter");for(let[n,o]of Object.entries(r.byModel)){let i=`model="${n}"`;e.push(`alfred_llm_calls_total{${i}} ${o.calls}`)}e.push("# HELP alfred_llm_cost_usd LLM cost by model"),e.push("# TYPE alfred_llm_cost_usd counter");for(let[n,o]of Object.entries(r.byModel))e.push(`alfred_llm_cost_usd{model="${n}"} ${o.costUsd}`);e.push("# HELP alfred_llm_input_tokens LLM input tokens by model"),e.push("# TYPE alfred_llm_input_tokens counter");for(let[n,o]of Object.entries(r.byModel))e.push(`alfred_llm_input_tokens{model="${n}"} ${o.inputTokens}`);e.push("# HELP alfred_llm_output_tokens LLM output tokens by model"),e.push("# TYPE alfred_llm_output_tokens counter");for(let[n,o]of Object.entries(r.byModel))e.push(`alfred_llm_output_tokens{model="${n}"} ${o.outputTokens}`);if(this.watchRepo&&(e.push("# HELP alfred_watches_active Number of enabled watches"),e.push("# TYPE alfred_watches_active gauge"),e.push(`alfred_watches_active ${this.watchRepo.countEnabled()}`)),this.scheduledActionRepo&&(e.push("# HELP alfred_schedulers_active Number of enabled scheduled actions"),e.push("# TYPE alfred_schedulers_active gauge"),e.push(`alfred_schedulers_active ${this.scheduledActionRepo.countEnabled()}`)),this.usageRepo){let n=new Date().toISOString().slice(0,10),o=this.usageRepo.getDaily(n);e.push("# HELP alfred_llm_today_cost_usd Total LLM cost today (persisted)"),e.push("# TYPE alfred_llm_today_cost_usd gauge"),e.push(`alfred_llm_today_cost_usd ${o.totalCostUsd}`),e.push("# HELP alfred_llm_today_calls Total LLM calls today (persisted)"),e.push("# TYPE alfred_llm_today_calls gauge"),e.push(`alfred_llm_today_calls ${o.totalCalls}`)}return e.push(""),e.join(`
|