@madh-io/alfred-ai 0.9.37 → 0.9.38

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.
Files changed (2) hide show
  1. package/bundle/index.js +1 -1
  2. package/package.json +1 -1
package/bundle/index.js CHANGED
@@ -542,7 +542,7 @@ Alfred: ${t}
542
542
  `),this.prompt(),r}async editMessage(e,t,s,r){Xr.clearLine(process.stdout,0),Xr.cursorTo(process.stdout,0),process.stdout.write(`Alfred: ${s}`)}async deleteMessage(e,t){}prompt(){this.rl?.prompt()}}});import Na from"node:http";import Oa from"node:crypto";var Cs,Uo=f(()=>{"use strict";ue();Cs=class extends q{static{d(this,"HttpAdapter")}port;host;platform="api";server=null;streams=new Map;messageCounter=0;constructor(e,t){super(),this.port=e,this.host=t}async connect(){this.status="connecting",this.server=Na.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("Access-Control-Allow-Origin","*"),t.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type"),e.method==="OPTIONS"){t.writeHead(204),t.end();return}let s=new URL(e.url??"/",`http://${e.headers.host??"localhost"}`);s.pathname==="/api/health"&&e.method==="GET"?this.handleHealth(t):s.pathname==="/api/message"&&e.method==="POST"?this.handleMessage(e,t):(t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not found"})))}handleHealth(e){e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({status:"ok"}))}handleMessage(e,t){let s="";e.on("data",r=>{s+=r.toString()}),e.on("end",()=>{try{let r=JSON.parse(s),n=r.text;if(!n||typeof n!="string"){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:'Missing or invalid "text" field'}));return}let o=r.chatId??`api-chat-${Oa.randomUUID()}`,i=r.userId??"api-user",a=this.streams.get(o);a&&(this.writeSseEvent(a,"done",{type:"done"}),a.end()),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}),this.streams.set(o,t),e.on("close",()=>{this.streams.delete(o)}),this.messageCounter++;let c={id:`api-${this.messageCounter}`,platform:"api",chatId:o,chatType:"dm",userId:i,userName:i,displayName:"API User",text:n,timestamp:new Date};this.emit("message",c)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid JSON body"}))}})}writeSseEvent(e,t,s){e.writableEnded||e.write(`event: ${t}
543
543
  data: ${JSON.stringify(s)}
544
544
 
545
- `)}}});var Te={};re(Te,{CLIAdapter:()=>Os,DiscordAdapter:()=>Rs,HttpAdapter:()=>Cs,MatrixAdapter:()=>Ls,MessagingAdapter:()=>q,SignalAdapter:()=>Ns,TelegramAdapter:()=>$s,WhatsAppAdapter:()=>Ms});var be=f(()=>{"use strict";ue();Lo();Mo();No();Oo();Co();Do();Uo()});import Ds from"node:fs";import Us from"node:path";import Ca from"js-yaml";var De,Po=f(()=>{"use strict";sr();or();gr();Ss();qt();$r();Ar();Rr();Lr();Mr();Nr();Or();Cr();Dr();Ur();Br();Wr();De=class{static{d(this,"Alfred")}config;logger;database;pipeline;reminderScheduler;backgroundTaskRunner;proactiveScheduler;adapters=new Map;formatter=new Qt;userRepo;mcpManager;calendarSkill;constructor(e){this.config=e,this.logger=bt("alfred",e.logger.level)}async initialize(){this.logger.info("Initializing Alfred..."),this.database=new xe(this.config.storage.path);let e=this.database.getDb(),t=new St(e),s=new kt(e);this.userRepo=s;let r=new Ie(e),n=new _t(e),o=new vt(e),i=new xt(e),a=new It(e),c=new $t(e),u=new At(e),p=new Rt(e);this.logger.info("Storage initialized");let h=new jt,m=this.loadSecurityRules();h.loadRules(m);let w=new Bt(h,r,this.logger.child({component:"security"}));this.logger.info({ruleCount:m.length},"Security engine initialized");let g=fr(this.config.llm);await g.initialize(),this.logger.info({provider:this.config.llm.default.provider,model:this.config.llm.default.model},"LLM provider initialized");let T=new es(g,a,this.logger.child({component:"embeddings"})),S=this.config.activeLearning?.enabled!==!1,$,v;S&&($=new os({llm:g,memoryRepo:n,logger:this.logger.child({component:"active-learning"}),embeddingService:T,minMessageLength:this.config.activeLearning?.minMessageLength,minConfidence:this.config.activeLearning?.minConfidence,maxExtractionsPerMinute:this.config.activeLearning?.maxExtractionsPerMinute}),v=new is(n,this.logger.child({component:"memory-retriever"}),T),this.logger.info("Active learning & memory retriever initialized"));let N=new ze(this.logger.child({component:"sandbox"})),A=new We;A.register(new He),A.register(new Xe),A.register(new Ke(this.config.search?{provider:this.config.search.provider,apiKey:this.config.search.apiKey,baseUrl:this.config.search.baseUrl}:void 0)),A.register(new qe(o)),A.register(new Ve(i)),A.register(new Ge),A.register(new Ye),A.register(new Je(n,T)),A.register(new Ze(g,A,N,w)),A.register(new Qe(this.config.email?{imap:this.config.email.imap,smtp:this.config.email.smtp,auth:this.config.email.auth}:void 0)),A.register(new et),A.register(new tt),A.register(new rt),A.register(new nt),A.register(new ot),A.register(new it(s)),A.register(new at(s,c,this.adapters,(R,j)=>t.findByPlatformAndUser(R,j))),A.register(new ct(u)),A.register(new lt(p));let O=new Lt(e),D=new ts(O,T,this.logger.child({component:"documents"}));A.register(new ut(O,D,T));let Z;if(this.config.calendar)try{let R=await Ht(this.config.calendar);Z=new Me(R),A.register(Z),this.logger.info({provider:this.config.calendar.provider},"Calendar initialized")}catch(R){this.logger.warn({err:R},"Calendar initialization failed, continuing without calendar")}if(this.calendarSkill=Z,this.config.mcp?.servers?.length){let{MCPManager:R}=await Promise.resolve().then(()=>(qt(),Is));this.mcpManager=new R(this.logger.child({component:"mcp"})),await this.mcpManager.initialize(this.config.mcp);for(let j of this.mcpManager.getSkills())A.register(j);this.logger.info({mcpSkills:this.mcpManager.getSkills().length},"MCP skills registered")}if(this.config.codeSandbox?.enabled){let{CodeExecutionSkill:R}=await Promise.resolve().then(()=>(qt(),Is));A.register(new R({allowedLanguages:this.config.codeSandbox.allowedLanguages,maxTimeoutMs:this.config.codeSandbox.maxTimeoutMs})),this.logger.info("Code sandbox enabled")}this.logger.info({skills:A.getAll().map(R=>R.metadata.name)},"Skills registered");let Pe;if(this.config.speech?.apiKey&&(Pe=new Jt(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 R=new Zt(this.config.speech,this.logger.child({component:"tts"}));A.register(new pt(R)),this.logger.info("Text-to-speech skill registered")}let ie=new Vt(t),X=Us.resolve(Us.dirname(this.config.storage.path),"inbox");this.pipeline=new Gt({llm:g,conversationManager:ie,users:s,logger:this.logger.child({component:"pipeline"}),skillRegistry:A,skillSandbox:N,securityManager:w,memoryRepo:n,speechTranscriber:Pe,inboxPath:X,embeddingService:T,activeLearning:$,memoryRetriever:v}),this.reminderScheduler=new Yt(o,async(R,j,he)=>{let C=this.adapters.get(R);C?await C.sendMessage(j,he):this.logger.warn({platform:R,chatId:j},"No adapter for reminder platform")},this.logger.child({component:"reminders"}),15e3,{getMasterUserId:d(R=>s.getMasterUserId(R),"getMasterUserId"),getLinkedUsers:d(R=>s.getLinkedUsers(R),"getLinkedUsers"),findConversation:d((R,j)=>t.findByPlatformAndUser(R,j),"findConversation")}),this.backgroundTaskRunner=new ss(A,N,u,this.adapters,this.logger.child({component:"background-tasks"})),this.proactiveScheduler=new rs(p,A,N,g,this.adapters,this.logger.child({component:"proactive-scheduler"})),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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te)),s=e.api?.port??3420,r=e.api?.host??"127.0.0.1";this.adapters.set("api",new t(s,r)),this.logger.info({port:s,host:r},"HTTP API adapter registered")}}async start(){this.logger.info("Starting Alfred...");for(let[e,t]of this.adapters)this.setupAdapterHandlers(e,t),await t.connect(),this.logger.info({platform:e},"Adapter connected");this.reminderScheduler?.start(),this.backgroundTaskRunner?.start(),this.proactiveScheduler?.start(),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(()=>(be(),Te)),t=new e;this.adapters.set("cli",t),this.setupAdapterHandlers("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.mcpManager&&await this.mcpManager.shutdown();for(let[e,t]of this.adapters)try{await t.disconnect(),this.logger.info({platform:e},"Adapter disconnected")}catch(s){this.logger.error({platform:e,err:s},"Failed to disconnect adapter")}this.database.close(),this.logger.info("Alfred stopped")}autoLinkApiUser(e){if(e.platform==="api")try{let t=this.userRepo.findOrCreate("api",e.userId,e.userName);if(this.userRepo.getMasterUserId(t.id)!==t.id)return;let r=this.userRepo.findFirstByPlatformNotIn(["api","cli"]);if(r){let n=this.userRepo.getMasterUserId(r.id);this.userRepo.setMasterUser(t.id,n),this.logger.info({apiUserId:t.id,masterUserId:n},"Auto-linked API user")}}catch(t){this.logger.debug({err:t},"Auto-link API user failed")}}setupAdapterHandlers(e,t){t.on("message",async s=>{try{this.autoLinkApiUser(s);let r,n="",o=d(async u=>{if(u!==n){n=u;try{r?await t.editMessage(s.chatId,r,u):r=await t.sendMessage(s.chatId,u)}catch(p){this.logger.debug({err:p,chatId:s.chatId},"Status message edit failed")}}},"onProgress"),i=await this.pipeline.process(s,o),a=this.formatter.format(i.text,s.platform),c=a.parseMode!=="text"?{parseMode:a.parseMode}:void 0;if(r)try{await t.editMessage(s.chatId,r,a.text,c)}catch(u){this.logger.debug({err:u,chatId:s.chatId},"Final response edit failed, sending as new message"),await t.sendMessage(s.chatId,a.text,c)}else await t.sendMessage(s.chatId,a.text,c);if(i.attachments)for(let u of i.attachments)try{let p=u.mimeType.startsWith("image/"),h=u.mimeType==="audio/ogg"||u.mimeType==="audio/opus";p?await t.sendPhoto(s.chatId,u.data,u.fileName):h?await t.sendVoice(s.chatId,u.data):await t.sendFile(s.chatId,u.data,u.fileName)}catch(p){this.logger.warn({err:p,fileName:u.fileName,chatId:s.chatId},"Failed to send attachment")}t.endStream(s.chatId)}catch(r){this.logger.error({platform:e,err:r,chatId:s.chatId},"Failed to handle message");try{await t.sendMessage(s.chatId,"Sorry, I encountered an error processing your message. Please try again.")}catch(n){this.logger.error({err:n},"Failed to send error message")}t.endStream(s.chatId)}}),t.on("error",s=>{this.logger.error({platform:e,err:s},"Adapter error")}),t.on("connected",()=>{this.logger.info({platform:e},"Adapter connected")}),t.on("disconnected",()=>{this.logger.warn({platform:e},"Adapter disconnected")})}loadSecurityRules(){let e=Us.resolve(this.config.security.rulesPath),t=[];if(!Ds.existsSync(e))return this.logger.warn({rulesPath:e},"Security rules directory not found, using default deny"),t;if(!Ds.statSync(e).isDirectory())return this.logger.warn({rulesPath:e},"Security rules path is not a directory"),t;let r=Ds.readdirSync(e).filter(n=>n.endsWith(".yml")||n.endsWith(".yaml"));for(let n of r)try{let o=Us.join(e,n),i=Ds.readFileSync(o,"utf-8"),a=Ca.load(i);a?.rules&&Array.isArray(a.rules)&&(t.push(...a.rules),this.logger.info({file:n,count:a.rules.length},"Loaded security rules"))}catch(o){this.logger.error({err:o,file:n},"Failed to load security rules file")}return t}}});var Fo=f(()=>{"use strict"});var Kr=f(()=>{"use strict";Po();Ar();$r();Rr();Lr();Mr();Nr();Or();Dr();Ur();Cr();Br();Wr();jr();Fo();Fr()});var jo={};re(jo,{startCommand:()=>Da});async function Da(){let l=new G,e;try{e=l.loadConfig()}catch(o){console.error("Failed to load configuration:",o.message),process.exit(1)}let t=bt("cli",e.logger.level);t.info({name:e.name},"Configuration loaded");let s=new De(e),r=!1,n=d(async o=>{if(!r){r=!0,t.info({signal:o},"Received shutdown signal");try{await s.stop(),t.info("Graceful shutdown complete"),process.exit(0)}catch(i){t.error({error:i},"Error during shutdown"),process.exit(1)}}},"shutdown");process.on("SIGINT",()=>n("SIGINT")),process.on("SIGTERM",()=>n("SIGTERM")),process.on("uncaughtException",o=>{t.fatal({error:o},"Uncaught exception"),n("uncaughtException")}),process.on("unhandledRejection",o=>{t.fatal({reason:o},"Unhandled rejection"),n("unhandledRejection")});try{await s.initialize(),await s.start(),t.info("Alfred is ready")}catch(o){t.fatal({error:o},"Failed to start Alfred"),process.exit(1)}}var Bo=f(()=>{"use strict";ve();sr();Kr();d(Da,"startCommand")});var zo={};re(zo,{chatCommand:()=>Fa});import Wo from"node:http";import as from"node:readline";function Ua(l,e){return new Promise(t=>{let s=Wo.get(`http://${l}:${e}/api/health`,{timeout:2e3},r=>{let n="";r.on("data",o=>{n+=o.toString()}),r.on("end",()=>{try{let o=JSON.parse(n);t(o.status==="ok")}catch{t(!1)}})});s.on("error",()=>t(!1)),s.on("timeout",()=>{s.destroy(),t(!1)})})}function Pa(l,e){let t=as.createInterface({input:process.stdin,output:process.stdout,prompt:"You: "});console.log(`
545
+ `)}}});var Te={};re(Te,{CLIAdapter:()=>Os,DiscordAdapter:()=>Rs,HttpAdapter:()=>Cs,MatrixAdapter:()=>Ls,MessagingAdapter:()=>q,SignalAdapter:()=>Ns,TelegramAdapter:()=>$s,WhatsAppAdapter:()=>Ms});var be=f(()=>{"use strict";ue();Lo();Mo();No();Oo();Co();Do();Uo()});import Ds from"node:fs";import Us from"node:path";import Ca from"js-yaml";var De,Po=f(()=>{"use strict";sr();or();gr();Ss();qt();$r();Ar();Rr();Lr();Mr();Nr();Or();Cr();Dr();Ur();Br();Wr();De=class{static{d(this,"Alfred")}config;logger;database;pipeline;reminderScheduler;backgroundTaskRunner;proactiveScheduler;adapters=new Map;formatter=new Qt;userRepo;mcpManager;calendarSkill;constructor(e){this.config=e,this.logger=bt("alfred",e.logger.level)}async initialize(){this.logger.info("Initializing Alfred..."),this.database=new xe(this.config.storage.path);let e=this.database.getDb(),t=new St(e),s=new kt(e);this.userRepo=s;let r=new Ie(e),n=new _t(e),o=new vt(e),i=new xt(e),a=new It(e),c=new $t(e),u=new At(e),p=new Rt(e);this.logger.info("Storage initialized");let h=new jt,m=this.loadSecurityRules();h.loadRules(m);let w=new Bt(h,r,this.logger.child({component:"security"}));this.logger.info({ruleCount:m.length},"Security engine initialized");let g=fr(this.config.llm);await g.initialize(),this.logger.info({provider:this.config.llm.default.provider,model:this.config.llm.default.model},"LLM provider initialized");let T=new es(g,a,this.logger.child({component:"embeddings"})),S=this.config.activeLearning?.enabled!==!1,$,v;S&&($=new os({llm:g,memoryRepo:n,logger:this.logger.child({component:"active-learning"}),embeddingService:T,minMessageLength:this.config.activeLearning?.minMessageLength,minConfidence:this.config.activeLearning?.minConfidence,maxExtractionsPerMinute:this.config.activeLearning?.maxExtractionsPerMinute}),v=new is(n,this.logger.child({component:"memory-retriever"}),T),this.logger.info("Active learning & memory retriever initialized"));let N=new ze(this.logger.child({component:"sandbox"})),A=new We;A.register(new He),A.register(new Xe),A.register(new Ke(this.config.search?{provider:this.config.search.provider,apiKey:this.config.search.apiKey,baseUrl:this.config.search.baseUrl}:void 0)),A.register(new qe(o)),A.register(new Ve(i)),A.register(new Ge),A.register(new Ye),A.register(new Je(n,T)),A.register(new Ze(g,A,N,w)),A.register(new Qe(this.config.email?{imap:this.config.email.imap,smtp:this.config.email.smtp,auth:this.config.email.auth}:void 0)),A.register(new et),A.register(new tt),A.register(new rt),A.register(new nt),A.register(new ot),A.register(new it(s)),A.register(new at(s,c,this.adapters,(R,j)=>t.findByPlatformAndUser(R,j))),A.register(new ct(u)),A.register(new lt(p));let O=new Lt(e),D=new ts(O,T,this.logger.child({component:"documents"}));A.register(new ut(O,D,T));let Z;if(this.config.calendar)try{let R=await Ht(this.config.calendar);Z=new Me(R),A.register(Z),this.logger.info({provider:this.config.calendar.provider},"Calendar initialized")}catch(R){this.logger.warn({err:R},"Calendar initialization failed, continuing without calendar")}if(this.calendarSkill=Z,this.config.mcp?.servers?.length){let{MCPManager:R}=await Promise.resolve().then(()=>(qt(),Is));this.mcpManager=new R(this.logger.child({component:"mcp"})),await this.mcpManager.initialize(this.config.mcp);for(let j of this.mcpManager.getSkills())A.register(j);this.logger.info({mcpSkills:this.mcpManager.getSkills().length},"MCP skills registered")}if(this.config.codeSandbox?.enabled){let{CodeExecutionSkill:R}=await Promise.resolve().then(()=>(qt(),Is));A.register(new R({allowedLanguages:this.config.codeSandbox.allowedLanguages,maxTimeoutMs:this.config.codeSandbox.maxTimeoutMs})),this.logger.info("Code sandbox enabled")}this.logger.info({skills:A.getAll().map(R=>R.metadata.name)},"Skills registered");let Pe;if(this.config.speech?.apiKey&&(Pe=new Jt(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 R=new Zt(this.config.speech,this.logger.child({component:"tts"}));A.register(new pt(R)),this.logger.info("Text-to-speech skill registered")}let ie=new Vt(t),X=Us.resolve(Us.dirname(this.config.storage.path),"inbox");this.pipeline=new Gt({llm:g,conversationManager:ie,users:s,logger:this.logger.child({component:"pipeline"}),skillRegistry:A,skillSandbox:N,securityManager:w,memoryRepo:n,speechTranscriber:Pe,inboxPath:X,embeddingService:T,activeLearning:$,memoryRetriever:v}),this.reminderScheduler=new Yt(o,async(R,j,he)=>{let C=this.adapters.get(R);C?await C.sendMessage(j,he):this.logger.warn({platform:R,chatId:j},"No adapter for reminder platform")},this.logger.child({component:"reminders"}),15e3,{getMasterUserId:d(R=>s.getMasterUserId(R),"getMasterUserId"),getLinkedUsers:d(R=>s.getLinkedUsers(R),"getLinkedUsers"),findConversation:d((R,j)=>t.findByPlatformAndUser(R,j),"findConversation")}),this.backgroundTaskRunner=new ss(A,N,u,this.adapters,this.logger.child({component:"background-tasks"})),this.proactiveScheduler=new rs(p,A,N,g,this.adapters,this.logger.child({component:"proactive-scheduler"})),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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te));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(()=>(be(),Te)),s=e.api?.port??3420,r=e.api?.host??"127.0.0.1";this.adapters.set("api",new t(s,r)),this.logger.info({port:s,host:r},"HTTP API adapter registered")}}async start(){this.logger.info("Starting Alfred...");for(let[e,t]of this.adapters)this.setupAdapterHandlers(e,t),await t.connect(),this.logger.info({platform:e},"Adapter connected");this.reminderScheduler?.start(),this.backgroundTaskRunner?.start(),this.proactiveScheduler?.start(),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(()=>(be(),Te)),t=new e;this.adapters.set("cli",t),this.setupAdapterHandlers("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.mcpManager&&await this.mcpManager.shutdown();for(let[e,t]of this.adapters)try{await t.disconnect(),this.logger.info({platform:e},"Adapter disconnected")}catch(s){this.logger.error({platform:e,err:s},"Failed to disconnect adapter")}this.database.close(),this.logger.info("Alfred stopped")}autoLinkApiUser(e){if(e.platform==="api")try{let t=this.userRepo.findOrCreate("api",e.userId,e.userName);if(this.userRepo.getMasterUserId(t.id)!==t.id)return;let r=this.userRepo.findFirstByPlatformNotIn(["api","cli"]);if(r){let n=this.userRepo.getMasterUserId(r.id);this.userRepo.setMasterUser(t.id,n),this.logger.info({apiUserId:t.id,masterUserId:n},"Auto-linked API user")}}catch(t){this.logger.debug({err:t},"Auto-link API user failed")}}setupAdapterHandlers(e,t){t.on("message",async s=>{try{this.autoLinkApiUser(s);let r,n="",o=d(async u=>{if(u!==n){n=u;try{r?await t.editMessage(s.chatId,r,u):r=await t.sendMessage(s.chatId,u)}catch(p){this.logger.debug({err:p,chatId:s.chatId},"Status message edit failed")}}},"onProgress"),i=await this.pipeline.process(s,o),a=this.formatter.format(i.text,s.platform),c=a.parseMode!=="text"?{parseMode:a.parseMode}:void 0;if(r&&e!=="api")try{await t.editMessage(s.chatId,r,a.text,c)}catch(u){this.logger.debug({err:u,chatId:s.chatId},"Final response edit failed, sending as new message"),await t.sendMessage(s.chatId,a.text,c)}else await t.sendMessage(s.chatId,a.text,c);if(i.attachments)for(let u of i.attachments)try{let p=u.mimeType.startsWith("image/"),h=u.mimeType==="audio/ogg"||u.mimeType==="audio/opus";p?await t.sendPhoto(s.chatId,u.data,u.fileName):h?await t.sendVoice(s.chatId,u.data):await t.sendFile(s.chatId,u.data,u.fileName)}catch(p){this.logger.warn({err:p,fileName:u.fileName,chatId:s.chatId},"Failed to send attachment")}t.endStream(s.chatId)}catch(r){this.logger.error({platform:e,err:r,chatId:s.chatId},"Failed to handle message");try{await t.sendMessage(s.chatId,"Sorry, I encountered an error processing your message. Please try again.")}catch(n){this.logger.error({err:n},"Failed to send error message")}t.endStream(s.chatId)}}),t.on("error",s=>{this.logger.error({platform:e,err:s},"Adapter error")}),t.on("connected",()=>{this.logger.info({platform:e},"Adapter connected")}),t.on("disconnected",()=>{this.logger.warn({platform:e},"Adapter disconnected")})}loadSecurityRules(){let e=Us.resolve(this.config.security.rulesPath),t=[];if(!Ds.existsSync(e))return this.logger.warn({rulesPath:e},"Security rules directory not found, using default deny"),t;if(!Ds.statSync(e).isDirectory())return this.logger.warn({rulesPath:e},"Security rules path is not a directory"),t;let r=Ds.readdirSync(e).filter(n=>n.endsWith(".yml")||n.endsWith(".yaml"));for(let n of r)try{let o=Us.join(e,n),i=Ds.readFileSync(o,"utf-8"),a=Ca.load(i);a?.rules&&Array.isArray(a.rules)&&(t.push(...a.rules),this.logger.info({file:n,count:a.rules.length},"Loaded security rules"))}catch(o){this.logger.error({err:o,file:n},"Failed to load security rules file")}return t}}});var Fo=f(()=>{"use strict"});var Kr=f(()=>{"use strict";Po();Ar();$r();Rr();Lr();Mr();Nr();Or();Dr();Ur();Cr();Br();Wr();jr();Fo();Fr()});var jo={};re(jo,{startCommand:()=>Da});async function Da(){let l=new G,e;try{e=l.loadConfig()}catch(o){console.error("Failed to load configuration:",o.message),process.exit(1)}let t=bt("cli",e.logger.level);t.info({name:e.name},"Configuration loaded");let s=new De(e),r=!1,n=d(async o=>{if(!r){r=!0,t.info({signal:o},"Received shutdown signal");try{await s.stop(),t.info("Graceful shutdown complete"),process.exit(0)}catch(i){t.error({error:i},"Error during shutdown"),process.exit(1)}}},"shutdown");process.on("SIGINT",()=>n("SIGINT")),process.on("SIGTERM",()=>n("SIGTERM")),process.on("uncaughtException",o=>{t.fatal({error:o},"Uncaught exception"),n("uncaughtException")}),process.on("unhandledRejection",o=>{t.fatal({reason:o},"Unhandled rejection"),n("unhandledRejection")});try{await s.initialize(),await s.start(),t.info("Alfred is ready")}catch(o){t.fatal({error:o},"Failed to start Alfred"),process.exit(1)}}var Bo=f(()=>{"use strict";ve();sr();Kr();d(Da,"startCommand")});var zo={};re(zo,{chatCommand:()=>Fa});import Wo from"node:http";import as from"node:readline";function Ua(l,e){return new Promise(t=>{let s=Wo.get(`http://${l}:${e}/api/health`,{timeout:2e3},r=>{let n="";r.on("data",o=>{n+=o.toString()}),r.on("end",()=>{try{let o=JSON.parse(n);t(o.status==="ok")}catch{t(!1)}})});s.on("error",()=>t(!1)),s.on("timeout",()=>{s.destroy(),t(!1)})})}function Pa(l,e){let t=as.createInterface({input:process.stdin,output:process.stdout,prompt:"You: "});console.log(`
546
546
  Alfred Chat (connected to server) \u2014 type your message and press Enter. Use /quit or /exit to leave.
547
547
  `),t.prompt(),t.on("line",s=>{let r=s.trim();if(!r){t.prompt();return}(r==="/quit"||r==="/exit")&&(console.log(`
548
548
  Goodbye!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madh-io/alfred-ai",
3
- "version": "0.9.37",
3
+ "version": "0.9.38",
4
4
  "description": "Alfred — Personal AI Assistant across Telegram, Discord, WhatsApp, Matrix & Signal",
5
5
  "type": "module",
6
6
  "bin": {