@madh-io/alfred-ai 0.10.66 → 0.10.67

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 +291 -242
  2. package/package.json +1 -1
package/bundle/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- var ka=Object.defineProperty;var m=(l,e)=>ka(l,"name",{value:e,configurable:!0}),va=(l=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(l,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):l)(function(l){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+l+'" is not supported')});var _=(l,e)=>()=>(l&&(e=l(l=0)),e);var ue=(l,e)=>{for(var t in e)ka(l,t,{get:e[t],enumerable:!0})};import{z as w}from"zod";var Aa,Ia,xa,Ra,Ca,Da,La,Na,gt,Eu,$u,ku,Sa,vu,Su,Au,Iu,xu,Ru,Cu,Du,Lu,Nu,Mu,Ou,Pu,Uu,Fu,ju,Bu,Hu,qu,zu,Wu,Gu,Vu,Ku,Xu,Yu,Ju,Zu,Qu,So,Ao=_(()=>{"use strict";Aa=w.object({token:w.string().optional(),enabled:w.boolean()}),Ia=w.object({token:w.string().optional(),enabled:w.boolean()}),xa=w.object({enabled:w.boolean(),dataPath:w.string()}),Ra=w.object({homeserverUrl:w.string(),accessToken:w.string().optional(),userId:w.string().optional(),enabled:w.boolean()}),Ca=w.object({apiUrl:w.string(),phoneNumber:w.string().optional(),enabled:w.boolean()}),Da=w.object({path:w.string()}),La=w.object({level:w.enum(["trace","debug","info","warn","error","fatal"]),pretty:w.boolean(),auditLogPath:w.string().optional()}),Na=w.object({rulesPath:w.string(),defaultEffect:w.enum(["allow","deny"]),ownerUserId:w.string().optional()}),gt=w.object({provider:w.enum(["anthropic","openai","openrouter","ollama","openwebui","google","mistral"]),apiKey:w.string().optional(),baseUrl:w.string().optional(),model:w.string(),temperature:w.number().optional(),maxTokens:w.number().optional()}),Eu=w.object({default:gt,strong:gt.optional(),fast:gt.optional(),embeddings:gt.optional(),local:gt.optional()}).passthrough(),$u=w.union([gt,Eu]),ku=w.object({provider:w.enum(["brave","searxng","tavily","duckduckgo"]),apiKey:w.string().optional(),baseUrl:w.string().optional()}),Sa=w.object({name:w.string().optional(),provider:w.enum(["imap-smtp","microsoft"]).optional(),imap:w.object({host:w.string(),port:w.number(),secure:w.boolean()}).optional(),smtp:w.object({host:w.string(),port:w.number(),secure:w.boolean()}).optional(),auth:w.object({user:w.string(),pass:w.string()}).optional(),microsoft:w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}).optional()}),vu=w.union([w.object({accounts:w.array(Sa)}),Sa]),Su=w.object({provider:w.enum(["openai","groq","google"]),apiKey:w.string(),baseUrl:w.string().optional(),ttsEnabled:w.boolean().optional(),ttsModel:w.string().optional(),ttsVoice:w.string().optional()}),Au=w.object({serverUrl:w.string(),username:w.string(),password:w.string()}),Iu=w.object({clientId:w.string(),clientSecret:w.string(),refreshToken:w.string()}),xu=w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}),Ru=w.object({provider:w.enum(["caldav","google","microsoft"]),caldav:Au.optional(),google:Iu.optional(),microsoft:xu.optional()}),Cu=w.object({name:w.string(),command:w.string().optional(),args:w.array(w.string()).optional(),env:w.record(w.string()).optional(),url:w.string().optional()}),Du=w.object({servers:w.array(Cu)}),Lu=w.object({enabled:w.boolean(),allowedLanguages:w.array(w.enum(["javascript","python"])).optional(),maxTimeoutMs:w.number().optional(),allowNetwork:w.boolean().optional()}),Nu=w.object({enabled:w.boolean().optional(),minMessageLength:w.number().optional(),minConfidence:w.number().min(0).max(1).optional(),maxExtractionsPerMinute:w.number().optional()}),Mu=w.object({enabled:w.boolean(),port:w.coerce.number().int().min(1).max(65535),host:w.string()}),Ou=w.object({name:w.string(),command:w.string(),argsTemplate:w.array(w.string()),promptVia:w.enum(["arg","stdin"]).default("arg"),env:w.record(w.string()).optional(),cwd:w.string().optional(),timeoutMs:w.number().max(9e5).optional()}),Pu=w.object({token:w.string(),baseUrl:w.string().optional()}),Uu=w.object({token:w.string(),baseUrl:w.string().optional()}),Fu=w.object({provider:w.enum(["github","gitlab"]),baseBranch:w.string().optional(),github:Pu.optional(),gitlab:Uu.optional()}),ju=w.object({enabled:w.boolean(),agents:w.array(Ou),forge:Fu.optional()}),Bu=w.object({baseUrl:w.string(),tokenId:w.string(),tokenSecret:w.string(),verifyTls:w.boolean().optional(),defaultNode:w.string().optional()}),Hu=w.object({baseUrl:w.string(),apiKey:w.string().optional(),username:w.string().optional(),password:w.string().optional(),site:w.string().optional(),verifyTls:w.boolean().optional()}),qu=w.object({baseUrl:w.string(),accessToken:w.string(),verifyTls:w.boolean().optional()}),zu=w.object({serverUrl:w.string(),username:w.string(),password:w.string(),addressBookPath:w.string().optional()}),Wu=w.object({clientId:w.string(),clientSecret:w.string(),refreshToken:w.string()}),Gu=w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}),Vu=w.object({provider:w.enum(["carddav","google","microsoft"]),carddav:zu.optional(),google:Wu.optional(),microsoft:Gu.optional()}),Ku=w.object({socketPath:w.string().optional(),host:w.string().optional(),verifyTls:w.boolean().optional()}),Xu=w.object({clientId:w.string()}),Yu=w.object({apiKey:w.string()}),Ju=w.object({gridName:w.string().optional(),gridUsageCt:w.coerce.number().optional(),gridLossCt:w.coerce.number().optional(),gridCapacityFee:w.coerce.number().optional(),gridMeterFee:w.coerce.number().optional()}),Zu=w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}),Qu=w.object({maxHistoryMessages:w.number().min(10).max(500).optional()}).optional(),So=w.object({name:w.string(),telegram:Aa,discord:Ia.optional(),whatsapp:xa.optional(),matrix:Ra.optional(),signal:Ca.optional(),llm:$u,storage:Da,logger:La,security:Na,search:ku.optional(),email:vu.optional(),speech:Su.optional(),calendar:Ru.optional(),mcp:Du.optional(),codeSandbox:Lu.optional(),activeLearning:Nu.optional(),api:Mu.optional(),codeAgents:ju.optional(),proxmox:Bu.optional(),unifi:Hu.optional(),homeassistant:qu.optional(),contacts:Vu.optional(),docker:Ku.optional(),bmw:Xu.optional(),routing:Yu.optional(),todo:Zu.optional(),energy:Ju.optional(),conversation:Qu})});var Io,xo=_(()=>{"use strict";Io={name:"Alfred",telegram:{token:"",enabled:!1},discord:{token:"",enabled:!1},whatsapp:{enabled:!1,dataPath:"./data/whatsapp"},matrix:{homeserverUrl:"https://matrix.org",accessToken:"",userId:"",enabled:!1},signal:{apiUrl:"http://localhost:8080",phoneNumber:"",enabled:!1},llm:{provider:"anthropic",model:"claude-sonnet-4-20250514",temperature:.7,maxTokens:4096},storage:{path:"./data/alfred.db"},logger:{level:"info",pretty:!0},security:{rulesPath:"./config/rules",defaultEffect:"deny"},api:{enabled:!0,port:3420,host:"127.0.0.1"},conversation:{maxHistoryMessages:100}}});import Ma from"node:fs";import em from"node:path";import{config as Oa}from"dotenv";import tm from"js-yaml";function Pa(l,e){let t={...l};for(let s of Object.keys(e)){let r=e[s],n=t[s];r!=null&&typeof r=="object"&&!Array.isArray(r)&&n!==null&&n!==void 0&&typeof n=="object"&&!Array.isArray(n)?t[s]=Pa(n,r):t[s]=r}return t}function rm(l){let e={...l};for(let[t,s]of Object.entries(sm)){let r=process.env[t];if(r===void 0)continue;let n=e;for(let o=0;o<s.length-1;o++){let i=s[o];(n[i]===void 0||n[i]===null||typeof n[i]!="object")&&(n[i]={}),n[i]={...n[i]},n=n[i]}n[s[s.length-1]]=r}return e}function Ro(){Oa({override:!0})}var sm,ce,Ua=_(()=>{"use strict";Ao();xo();m(Pa,"deepMerge");sm={ALFRED_TELEGRAM_TOKEN:["telegram","token"],ALFRED_DISCORD_TOKEN:["discord","token"],ALFRED_MATRIX_HOMESERVER_URL:["matrix","homeserverUrl"],ALFRED_MATRIX_ACCESS_TOKEN:["matrix","accessToken"],ALFRED_MATRIX_USER_ID:["matrix","userId"],ALFRED_SIGNAL_API_URL:["signal","apiUrl"],ALFRED_SIGNAL_PHONE_NUMBER:["signal","phoneNumber"],ALFRED_ANTHROPIC_API_KEY:["llm","apiKey"],ALFRED_OPENAI_API_KEY:["llm","apiKey"],ALFRED_GOOGLE_API_KEY:["llm","apiKey"],ALFRED_OPENROUTER_API_KEY:["llm","apiKey"],ALFRED_OPENWEBUI_API_KEY:["llm","apiKey"],ALFRED_LLM_PROVIDER:["llm","provider"],ALFRED_LLM_MODEL:["llm","model"],ALFRED_LLM_BASE_URL:["llm","baseUrl"],ALFRED_LLM_STRONG_PROVIDER:["llm","strong","provider"],ALFRED_LLM_STRONG_MODEL:["llm","strong","model"],ALFRED_LLM_STRONG_API_KEY:["llm","strong","apiKey"],ALFRED_LLM_FAST_PROVIDER:["llm","fast","provider"],ALFRED_LLM_FAST_MODEL:["llm","fast","model"],ALFRED_LLM_FAST_API_KEY:["llm","fast","apiKey"],ALFRED_LLM_EMBEDDINGS_PROVIDER:["llm","embeddings","provider"],ALFRED_LLM_EMBEDDINGS_MODEL:["llm","embeddings","model"],ALFRED_LLM_EMBEDDINGS_API_KEY:["llm","embeddings","apiKey"],ALFRED_LLM_LOCAL_PROVIDER:["llm","local","provider"],ALFRED_LLM_LOCAL_MODEL:["llm","local","model"],ALFRED_LLM_LOCAL_BASE_URL:["llm","local","baseUrl"],ALFRED_STORAGE_PATH:["storage","path"],ALFRED_LOG_LEVEL:["logger","level"],ALFRED_OWNER_USER_ID:["security","ownerUserId"],ALFRED_SEARCH_PROVIDER:["search","provider"],ALFRED_SEARCH_API_KEY:["search","apiKey"],ALFRED_SEARCH_BASE_URL:["search","baseUrl"],ALFRED_EMAIL_PROVIDER:["email","provider"],ALFRED_EMAIL_USER:["email","auth","user"],ALFRED_EMAIL_PASS:["email","auth","pass"],ALFRED_MICROSOFT_EMAIL_CLIENT_ID:["email","microsoft","clientId"],ALFRED_MICROSOFT_EMAIL_CLIENT_SECRET:["email","microsoft","clientSecret"],ALFRED_MICROSOFT_EMAIL_TENANT_ID:["email","microsoft","tenantId"],ALFRED_MICROSOFT_EMAIL_REFRESH_TOKEN:["email","microsoft","refreshToken"],ALFRED_SPEECH_PROVIDER:["speech","provider"],ALFRED_SPEECH_API_KEY:["speech","apiKey"],ALFRED_SPEECH_BASE_URL:["speech","baseUrl"],ALFRED_CALENDAR_PROVIDER:["calendar","provider"],ALFRED_CALDAV_SERVER_URL:["calendar","caldav","serverUrl"],ALFRED_CALDAV_USERNAME:["calendar","caldav","username"],ALFRED_CALDAV_PASSWORD:["calendar","caldav","password"],ALFRED_GOOGLE_CALENDAR_CLIENT_ID:["calendar","google","clientId"],ALFRED_GOOGLE_CALENDAR_CLIENT_SECRET:["calendar","google","clientSecret"],ALFRED_GOOGLE_CALENDAR_REFRESH_TOKEN:["calendar","google","refreshToken"],ALFRED_MICROSOFT_CALENDAR_CLIENT_ID:["calendar","microsoft","clientId"],ALFRED_MICROSOFT_CALENDAR_CLIENT_SECRET:["calendar","microsoft","clientSecret"],ALFRED_MICROSOFT_CALENDAR_TENANT_ID:["calendar","microsoft","tenantId"],ALFRED_MICROSOFT_CALENDAR_REFRESH_TOKEN:["calendar","microsoft","refreshToken"],ALFRED_FORGE_PROVIDER:["codeAgents","forge","provider"],ALFRED_FORGE_BASE_BRANCH:["codeAgents","forge","baseBranch"],ALFRED_GITHUB_TOKEN:["codeAgents","forge","github","token"],ALFRED_GITHUB_BASE_URL:["codeAgents","forge","github","baseUrl"],ALFRED_GITLAB_TOKEN:["codeAgents","forge","gitlab","token"],ALFRED_GITLAB_BASE_URL:["codeAgents","forge","gitlab","baseUrl"],ALFRED_PROXMOX_BASE_URL:["proxmox","baseUrl"],ALFRED_PROXMOX_TOKEN_ID:["proxmox","tokenId"],ALFRED_PROXMOX_TOKEN_SECRET:["proxmox","tokenSecret"],ALFRED_UNIFI_BASE_URL:["unifi","baseUrl"],ALFRED_UNIFI_API_KEY:["unifi","apiKey"],ALFRED_UNIFI_USERNAME:["unifi","username"],ALFRED_UNIFI_PASSWORD:["unifi","password"],ALFRED_UNIFI_SITE:["unifi","site"],ALFRED_HOMEASSISTANT_URL:["homeassistant","baseUrl"],ALFRED_HOMEASSISTANT_TOKEN:["homeassistant","accessToken"],ALFRED_CONTACTS_PROVIDER:["contacts","provider"],ALFRED_CARDDAV_CONTACTS_SERVER_URL:["contacts","carddav","serverUrl"],ALFRED_CARDDAV_CONTACTS_USERNAME:["contacts","carddav","username"],ALFRED_CARDDAV_CONTACTS_PASSWORD:["contacts","carddav","password"],ALFRED_GOOGLE_CONTACTS_CLIENT_ID:["contacts","google","clientId"],ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET:["contacts","google","clientSecret"],ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN:["contacts","google","refreshToken"],ALFRED_MICROSOFT_CONTACTS_CLIENT_ID:["contacts","microsoft","clientId"],ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET:["contacts","microsoft","clientSecret"],ALFRED_MICROSOFT_CONTACTS_TENANT_ID:["contacts","microsoft","tenantId"],ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN:["contacts","microsoft","refreshToken"],ALFRED_DOCKER_SOCKET_PATH:["docker","socketPath"],ALFRED_DOCKER_HOST:["docker","host"],ALFRED_BMW_CLIENT_ID:["bmw","clientId"],ALFRED_ROUTING_API_KEY:["routing","apiKey"],ALFRED_MICROSOFT_TODO_CLIENT_ID:["todo","clientId"],ALFRED_MICROSOFT_TODO_CLIENT_SECRET:["todo","clientSecret"],ALFRED_MICROSOFT_TODO_TENANT_ID:["todo","tenantId"],ALFRED_MICROSOFT_TODO_REFRESH_TOKEN:["todo","refreshToken"],ALFRED_ENERGY_GRID_NAME:["energy","gridName"],ALFRED_ENERGY_GRID_USAGE_CT:["energy","gridUsageCt"],ALFRED_ENERGY_GRID_LOSS_CT:["energy","gridLossCt"],ALFRED_ENERGY_GRID_CAPACITY_FEE:["energy","gridCapacityFee"],ALFRED_ENERGY_GRID_METER_FEE:["energy","gridMeterFee"],ALFRED_EBAY_APP_ID:["marketplace","ebay","appId"],ALFRED_EBAY_CERT_ID:["marketplace","ebay","certId"],ALFRED_BRIEFING_LOCATION:["briefing","location"],ALFRED_BRIEFING_HOME_ADDRESS:["briefing","homeAddress"],ALFRED_BRIEFING_OFFICE_ADDRESS:["briefing","officeAddress"]};m(rm,"applyEnvOverrides");m(Ro,"reloadDotenv");ce=class{static{m(this,"ConfigLoader")}loadConfig(e){Oa();let t=e??process.env.ALFRED_CONFIG_PATH??"./config/default.yml",s={},r=em.resolve(t);if(Ma.existsSync(r)){let g=Ma.readFileSync(r,"utf-8"),f=tm.load(g);f&&typeof f=="object"&&(s=f)}let n=Pa(Io,s),o=rm(n),i=["strong","fast","embeddings","local"],a=o.llm;if(a&&"provider"in a&&i.some(f=>a[f]&&typeof a[f]=="object")){let f={};for(let[y,E]of Object.entries(a))!i.includes(y)&&y!=="default"&&(f[y]=E);let h={default:f};for(let y of i)a[y]&&(h[y]=a[y]);o.llm=h}let c=So.parse(o),d=c.llm;d&&"provider"in d&&(c.llm={default:d});let u=c.llm;if(u&&typeof u=="object"){let g=u.apiKey??u.default?.apiKey;if(g)for(let f of["default","strong","fast","embeddings","local"]){let h=u[f];h&&!h.apiKey&&(h.apiKey=g)}}let p=c.email;if(p&&!("accounts"in p))c.email={accounts:[{name:"default",...p}]};else if(p&&"accounts"in p&&"microsoft"in p){let g=p.microsoft;if(g){let h=p.accounts.find(y=>y.provider==="microsoft");if(h){let y=h.microsoft??{};h.microsoft={...y,...g}}delete p.microsoft}}return c}}});var Ve=_(()=>{"use strict";Ao();xo();Ua()});import Co from"pino";function Os(l,e){let t=e??process.env.LOG_LEVEL??"info";if(t==="debug"||t==="trace"||process.env.NODE_ENV!=="production"){let r=Co.transport({target:"pino-pretty",options:{colorize:!0}});return Co({name:l,level:t},r)}return Co({name:l,level:t})}var Fa=_(()=>{"use strict";m(Os,"createLogger")});import yf from"pino";var ja=_(()=>{"use strict"});var Do=_(()=>{"use strict";Fa();ja()});var Mt,nn=_(()=>{"use strict";Mt=class{static{m(this,"Migrator")}db;constructor(e){this.db=e,this.ensureMigrationsTable()}ensureMigrationsTable(){this.db.exec(`
2
+ var Aa=Object.defineProperty;var m=(l,e)=>Aa(l,"name",{value:e,configurable:!0}),Ia=(l=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(l,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):l)(function(l){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+l+'" is not supported')});var _=(l,e)=>()=>(l&&(e=l(l=0)),e);var le=(l,e)=>{for(var t in e)Aa(l,t,{get:e[t],enumerable:!0})};import{z as w}from"zod";var Ra,Ca,Da,La,Na,Ma,Oa,Pa,yt,Su,Au,Iu,xa,xu,Ru,Cu,Du,Lu,Nu,Mu,Ou,Pu,Uu,Fu,ju,Bu,Hu,zu,qu,Wu,Gu,Vu,Ku,Xu,Yu,Ju,Zu,Qu,em,tm,sm,rm,Io,xo=_(()=>{"use strict";Ra=w.object({token:w.string().optional(),enabled:w.boolean()}),Ca=w.object({token:w.string().optional(),enabled:w.boolean()}),Da=w.object({enabled:w.boolean(),dataPath:w.string()}),La=w.object({homeserverUrl:w.string(),accessToken:w.string().optional(),userId:w.string().optional(),enabled:w.boolean()}),Na=w.object({apiUrl:w.string(),phoneNumber:w.string().optional(),enabled:w.boolean()}),Ma=w.object({path:w.string()}),Oa=w.object({level:w.enum(["trace","debug","info","warn","error","fatal"]),pretty:w.boolean(),auditLogPath:w.string().optional()}),Pa=w.object({rulesPath:w.string(),defaultEffect:w.enum(["allow","deny"]),ownerUserId:w.string().optional()}),yt=w.object({provider:w.enum(["anthropic","openai","openrouter","ollama","openwebui","google","mistral"]),apiKey:w.string().optional(),baseUrl:w.string().optional(),model:w.string(),temperature:w.number().optional(),maxTokens:w.number().optional()}),Su=w.object({default:yt,strong:yt.optional(),fast:yt.optional(),embeddings:yt.optional(),local:yt.optional()}).passthrough(),Au=w.union([yt,Su]),Iu=w.object({provider:w.enum(["brave","searxng","tavily","duckduckgo"]),apiKey:w.string().optional(),baseUrl:w.string().optional()}),xa=w.object({name:w.string().optional(),provider:w.enum(["imap-smtp","microsoft"]).optional(),imap:w.object({host:w.string(),port:w.number(),secure:w.boolean()}).optional(),smtp:w.object({host:w.string(),port:w.number(),secure:w.boolean()}).optional(),auth:w.object({user:w.string(),pass:w.string()}).optional(),microsoft:w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}).optional()}),xu=w.union([w.object({accounts:w.array(xa)}),xa]),Ru=w.object({provider:w.enum(["openai","groq","google"]),apiKey:w.string(),baseUrl:w.string().optional(),ttsEnabled:w.boolean().optional(),ttsModel:w.string().optional(),ttsVoice:w.string().optional()}),Cu=w.object({serverUrl:w.string(),username:w.string(),password:w.string()}),Du=w.object({clientId:w.string(),clientSecret:w.string(),refreshToken:w.string()}),Lu=w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}),Nu=w.object({provider:w.enum(["caldav","google","microsoft"]),caldav:Cu.optional(),google:Du.optional(),microsoft:Lu.optional()}),Mu=w.object({name:w.string(),command:w.string().optional(),args:w.array(w.string()).optional(),env:w.record(w.string()).optional(),url:w.string().optional()}),Ou=w.object({servers:w.array(Mu)}),Pu=w.object({enabled:w.boolean(),allowedLanguages:w.array(w.enum(["javascript","python"])).optional(),maxTimeoutMs:w.number().optional(),allowNetwork:w.boolean().optional()}),Uu=w.object({enabled:w.boolean().optional(),minMessageLength:w.number().optional(),minConfidence:w.number().min(0).max(1).optional(),maxExtractionsPerMinute:w.number().optional()}),Fu=w.object({enabled:w.boolean(),port:w.coerce.number().int().min(1).max(65535),host:w.string()}),ju=w.object({name:w.string(),command:w.string(),argsTemplate:w.array(w.string()),promptVia:w.enum(["arg","stdin"]).default("arg"),env:w.record(w.string()).optional(),cwd:w.string().optional(),timeoutMs:w.number().max(9e5).optional()}),Bu=w.object({token:w.string(),baseUrl:w.string().optional()}),Hu=w.object({token:w.string(),baseUrl:w.string().optional()}),zu=w.object({provider:w.enum(["github","gitlab"]),baseBranch:w.string().optional(),github:Bu.optional(),gitlab:Hu.optional()}),qu=w.object({enabled:w.boolean(),agents:w.array(ju),forge:zu.optional()}),Wu=w.object({baseUrl:w.string(),tokenId:w.string(),tokenSecret:w.string(),verifyTls:w.boolean().optional(),defaultNode:w.string().optional()}),Gu=w.object({baseUrl:w.string(),apiKey:w.string().optional(),username:w.string().optional(),password:w.string().optional(),site:w.string().optional(),verifyTls:w.boolean().optional()}),Vu=w.object({baseUrl:w.string(),accessToken:w.string(),verifyTls:w.boolean().optional()}),Ku=w.object({serverUrl:w.string(),username:w.string(),password:w.string(),addressBookPath:w.string().optional()}),Xu=w.object({clientId:w.string(),clientSecret:w.string(),refreshToken:w.string()}),Yu=w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}),Ju=w.object({provider:w.enum(["carddav","google","microsoft"]),carddav:Ku.optional(),google:Xu.optional(),microsoft:Yu.optional()}),Zu=w.object({socketPath:w.string().optional(),host:w.string().optional(),verifyTls:w.boolean().optional()}),Qu=w.object({clientId:w.string()}),em=w.object({apiKey:w.string()}),tm=w.object({gridName:w.string().optional(),gridUsageCt:w.coerce.number().optional(),gridLossCt:w.coerce.number().optional(),gridCapacityFee:w.coerce.number().optional(),gridMeterFee:w.coerce.number().optional()}),sm=w.object({clientId:w.string(),clientSecret:w.string(),tenantId:w.string(),refreshToken:w.string()}),rm=w.object({maxHistoryMessages:w.number().min(10).max(500).optional()}).optional(),Io=w.object({name:w.string(),telegram:Ra,discord:Ca.optional(),whatsapp:Da.optional(),matrix:La.optional(),signal:Na.optional(),llm:Au,storage:Ma,logger:Oa,security:Pa,search:Iu.optional(),email:xu.optional(),speech:Ru.optional(),calendar:Nu.optional(),mcp:Ou.optional(),codeSandbox:Pu.optional(),activeLearning:Uu.optional(),api:Fu.optional(),codeAgents:qu.optional(),proxmox:Wu.optional(),unifi:Gu.optional(),homeassistant:Vu.optional(),contacts:Ju.optional(),docker:Zu.optional(),bmw:Qu.optional(),routing:em.optional(),todo:sm.optional(),energy:tm.optional(),conversation:rm})});var Ro,Co=_(()=>{"use strict";Ro={name:"Alfred",telegram:{token:"",enabled:!1},discord:{token:"",enabled:!1},whatsapp:{enabled:!1,dataPath:"./data/whatsapp"},matrix:{homeserverUrl:"https://matrix.org",accessToken:"",userId:"",enabled:!1},signal:{apiUrl:"http://localhost:8080",phoneNumber:"",enabled:!1},llm:{provider:"anthropic",model:"claude-sonnet-4-20250514",temperature:.7,maxTokens:4096},storage:{path:"./data/alfred.db"},logger:{level:"info",pretty:!0},security:{rulesPath:"./config/rules",defaultEffect:"deny"},api:{enabled:!0,port:3420,host:"127.0.0.1"},conversation:{maxHistoryMessages:100}}});import Ua from"node:fs";import nm from"node:path";import{config as Fa}from"dotenv";import om from"js-yaml";function ja(l,e){let t={...l};for(let s of Object.keys(e)){let r=e[s],n=t[s];r!=null&&typeof r=="object"&&!Array.isArray(r)&&n!==null&&n!==void 0&&typeof n=="object"&&!Array.isArray(n)?t[s]=ja(n,r):t[s]=r}return t}function am(l){let e={...l};for(let[t,s]of Object.entries(im)){let r=process.env[t];if(r===void 0)continue;let n=e;for(let o=0;o<s.length-1;o++){let i=s[o];(n[i]===void 0||n[i]===null||typeof n[i]!="object")&&(n[i]={}),n[i]={...n[i]},n=n[i]}n[s[s.length-1]]=r}return e}function Do(){Fa({override:!0})}var im,ce,Ba=_(()=>{"use strict";xo();Co();m(ja,"deepMerge");im={ALFRED_TELEGRAM_TOKEN:["telegram","token"],ALFRED_DISCORD_TOKEN:["discord","token"],ALFRED_MATRIX_HOMESERVER_URL:["matrix","homeserverUrl"],ALFRED_MATRIX_ACCESS_TOKEN:["matrix","accessToken"],ALFRED_MATRIX_USER_ID:["matrix","userId"],ALFRED_SIGNAL_API_URL:["signal","apiUrl"],ALFRED_SIGNAL_PHONE_NUMBER:["signal","phoneNumber"],ALFRED_ANTHROPIC_API_KEY:["llm","apiKey"],ALFRED_OPENAI_API_KEY:["llm","apiKey"],ALFRED_GOOGLE_API_KEY:["llm","apiKey"],ALFRED_OPENROUTER_API_KEY:["llm","apiKey"],ALFRED_OPENWEBUI_API_KEY:["llm","apiKey"],ALFRED_LLM_PROVIDER:["llm","provider"],ALFRED_LLM_MODEL:["llm","model"],ALFRED_LLM_BASE_URL:["llm","baseUrl"],ALFRED_LLM_STRONG_PROVIDER:["llm","strong","provider"],ALFRED_LLM_STRONG_MODEL:["llm","strong","model"],ALFRED_LLM_STRONG_API_KEY:["llm","strong","apiKey"],ALFRED_LLM_FAST_PROVIDER:["llm","fast","provider"],ALFRED_LLM_FAST_MODEL:["llm","fast","model"],ALFRED_LLM_FAST_API_KEY:["llm","fast","apiKey"],ALFRED_LLM_EMBEDDINGS_PROVIDER:["llm","embeddings","provider"],ALFRED_LLM_EMBEDDINGS_MODEL:["llm","embeddings","model"],ALFRED_LLM_EMBEDDINGS_API_KEY:["llm","embeddings","apiKey"],ALFRED_LLM_LOCAL_PROVIDER:["llm","local","provider"],ALFRED_LLM_LOCAL_MODEL:["llm","local","model"],ALFRED_LLM_LOCAL_BASE_URL:["llm","local","baseUrl"],ALFRED_STORAGE_PATH:["storage","path"],ALFRED_LOG_LEVEL:["logger","level"],ALFRED_OWNER_USER_ID:["security","ownerUserId"],ALFRED_SEARCH_PROVIDER:["search","provider"],ALFRED_SEARCH_API_KEY:["search","apiKey"],ALFRED_SEARCH_BASE_URL:["search","baseUrl"],ALFRED_EMAIL_PROVIDER:["email","provider"],ALFRED_EMAIL_USER:["email","auth","user"],ALFRED_EMAIL_PASS:["email","auth","pass"],ALFRED_MICROSOFT_EMAIL_CLIENT_ID:["email","microsoft","clientId"],ALFRED_MICROSOFT_EMAIL_CLIENT_SECRET:["email","microsoft","clientSecret"],ALFRED_MICROSOFT_EMAIL_TENANT_ID:["email","microsoft","tenantId"],ALFRED_MICROSOFT_EMAIL_REFRESH_TOKEN:["email","microsoft","refreshToken"],ALFRED_SPEECH_PROVIDER:["speech","provider"],ALFRED_SPEECH_API_KEY:["speech","apiKey"],ALFRED_SPEECH_BASE_URL:["speech","baseUrl"],ALFRED_CALENDAR_PROVIDER:["calendar","provider"],ALFRED_CALDAV_SERVER_URL:["calendar","caldav","serverUrl"],ALFRED_CALDAV_USERNAME:["calendar","caldav","username"],ALFRED_CALDAV_PASSWORD:["calendar","caldav","password"],ALFRED_GOOGLE_CALENDAR_CLIENT_ID:["calendar","google","clientId"],ALFRED_GOOGLE_CALENDAR_CLIENT_SECRET:["calendar","google","clientSecret"],ALFRED_GOOGLE_CALENDAR_REFRESH_TOKEN:["calendar","google","refreshToken"],ALFRED_MICROSOFT_CALENDAR_CLIENT_ID:["calendar","microsoft","clientId"],ALFRED_MICROSOFT_CALENDAR_CLIENT_SECRET:["calendar","microsoft","clientSecret"],ALFRED_MICROSOFT_CALENDAR_TENANT_ID:["calendar","microsoft","tenantId"],ALFRED_MICROSOFT_CALENDAR_REFRESH_TOKEN:["calendar","microsoft","refreshToken"],ALFRED_FORGE_PROVIDER:["codeAgents","forge","provider"],ALFRED_FORGE_BASE_BRANCH:["codeAgents","forge","baseBranch"],ALFRED_GITHUB_TOKEN:["codeAgents","forge","github","token"],ALFRED_GITHUB_BASE_URL:["codeAgents","forge","github","baseUrl"],ALFRED_GITLAB_TOKEN:["codeAgents","forge","gitlab","token"],ALFRED_GITLAB_BASE_URL:["codeAgents","forge","gitlab","baseUrl"],ALFRED_PROXMOX_BASE_URL:["proxmox","baseUrl"],ALFRED_PROXMOX_TOKEN_ID:["proxmox","tokenId"],ALFRED_PROXMOX_TOKEN_SECRET:["proxmox","tokenSecret"],ALFRED_UNIFI_BASE_URL:["unifi","baseUrl"],ALFRED_UNIFI_API_KEY:["unifi","apiKey"],ALFRED_UNIFI_USERNAME:["unifi","username"],ALFRED_UNIFI_PASSWORD:["unifi","password"],ALFRED_UNIFI_SITE:["unifi","site"],ALFRED_HOMEASSISTANT_URL:["homeassistant","baseUrl"],ALFRED_HOMEASSISTANT_TOKEN:["homeassistant","accessToken"],ALFRED_CONTACTS_PROVIDER:["contacts","provider"],ALFRED_CARDDAV_CONTACTS_SERVER_URL:["contacts","carddav","serverUrl"],ALFRED_CARDDAV_CONTACTS_USERNAME:["contacts","carddav","username"],ALFRED_CARDDAV_CONTACTS_PASSWORD:["contacts","carddav","password"],ALFRED_GOOGLE_CONTACTS_CLIENT_ID:["contacts","google","clientId"],ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET:["contacts","google","clientSecret"],ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN:["contacts","google","refreshToken"],ALFRED_MICROSOFT_CONTACTS_CLIENT_ID:["contacts","microsoft","clientId"],ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET:["contacts","microsoft","clientSecret"],ALFRED_MICROSOFT_CONTACTS_TENANT_ID:["contacts","microsoft","tenantId"],ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN:["contacts","microsoft","refreshToken"],ALFRED_DOCKER_SOCKET_PATH:["docker","socketPath"],ALFRED_DOCKER_HOST:["docker","host"],ALFRED_BMW_CLIENT_ID:["bmw","clientId"],ALFRED_ROUTING_API_KEY:["routing","apiKey"],ALFRED_MICROSOFT_TODO_CLIENT_ID:["todo","clientId"],ALFRED_MICROSOFT_TODO_CLIENT_SECRET:["todo","clientSecret"],ALFRED_MICROSOFT_TODO_TENANT_ID:["todo","tenantId"],ALFRED_MICROSOFT_TODO_REFRESH_TOKEN:["todo","refreshToken"],ALFRED_ENERGY_GRID_NAME:["energy","gridName"],ALFRED_ENERGY_GRID_USAGE_CT:["energy","gridUsageCt"],ALFRED_ENERGY_GRID_LOSS_CT:["energy","gridLossCt"],ALFRED_ENERGY_GRID_CAPACITY_FEE:["energy","gridCapacityFee"],ALFRED_ENERGY_GRID_METER_FEE:["energy","gridMeterFee"],ALFRED_EBAY_APP_ID:["marketplace","ebay","appId"],ALFRED_EBAY_CERT_ID:["marketplace","ebay","certId"],ALFRED_BRIEFING_LOCATION:["briefing","location"],ALFRED_BRIEFING_HOME_ADDRESS:["briefing","homeAddress"],ALFRED_BRIEFING_OFFICE_ADDRESS:["briefing","officeAddress"]};m(am,"applyEnvOverrides");m(Do,"reloadDotenv");ce=class{static{m(this,"ConfigLoader")}loadConfig(e){Fa();let t=e??process.env.ALFRED_CONFIG_PATH??"./config/default.yml",s={},r=nm.resolve(t);if(Ua.existsSync(r)){let f=Ua.readFileSync(r,"utf-8"),g=om.load(f);g&&typeof g=="object"&&(s=g)}let n=ja(Ro,s),o=am(n),i=["strong","fast","embeddings","local"],a=o.llm;if(a&&"provider"in a&&i.some(g=>a[g]&&typeof a[g]=="object")){let g={};for(let[y,E]of Object.entries(a))!i.includes(y)&&y!=="default"&&(g[y]=E);let h={default:g};for(let y of i)a[y]&&(h[y]=a[y]);o.llm=h}let c=Io.parse(o),d=c.llm;d&&"provider"in d&&(c.llm={default:d});let u=c.llm;if(u&&typeof u=="object"){let f=u.apiKey??u.default?.apiKey;if(f)for(let g of["default","strong","fast","embeddings","local"]){let h=u[g];h&&!h.apiKey&&(h.apiKey=f)}}let p=c.email;if(p&&!("accounts"in p))c.email={accounts:[{name:"default",...p}]};else if(p&&"accounts"in p&&"microsoft"in p){let f=p.microsoft;if(f){let h=p.accounts.find(y=>y.provider==="microsoft");if(h){let y=h.microsoft??{};h.microsoft={...y,...f}}delete p.microsoft}}return c}}});var Ke=_(()=>{"use strict";xo();Co();Ba()});import Lo from"pino";function Fs(l,e){let t=e??process.env.LOG_LEVEL??"info";if(t==="debug"||t==="trace"||process.env.NODE_ENV!=="production"){let r=Lo.transport({target:"pino-pretty",options:{colorize:!0}});return Lo({name:l,level:t},r)}return Lo({name:l,level:t})}var Ha=_(()=>{"use strict";m(Fs,"createLogger")});import Ef from"pino";var za=_(()=>{"use strict"});var No=_(()=>{"use strict";Ha();za()});var Pt,an=_(()=>{"use strict";Pt=class{static{m(this,"Migrator")}db;constructor(e){this.db=e,this.ensureMigrationsTable()}ensureMigrationsTable(){this.db.exec(`
3
3
  CREATE TABLE IF NOT EXISTS _migrations (
4
4
  version INTEGER PRIMARY KEY,
5
5
  description TEXT,
6
6
  applied_at TEXT NOT NULL
7
7
  )
8
- `)}getCurrentVersion(){return this.db.prepare("SELECT MAX(version) as version FROM _migrations").get()?.version??0}migrate(e){let t=[...e].sort((r,n)=>r.version-n.version),s=this.getCurrentVersion();for(let r of t){if(r.version<=s)continue;this.db.transaction(()=>{r.up(this.db),this.db.prepare("INSERT INTO _migrations (version, description, applied_at) VALUES (?, ?, ?)").run(r.version,r.description,new Date().toISOString())})()}}getAppliedMigrations(){return this.db.prepare("SELECT version, applied_at FROM _migrations ORDER BY version ASC").all().map(t=>({version:t.version,appliedAt:t.applied_at}))}}});var Lo,No=_(()=>{"use strict";nn();Lo=[{version:1,description:"Initial schema \u2014 conversations, messages, users, audit_log",up(l){}},{version:2,description:"Add plugin_skills table for tracking loaded external plugins",up(l){l.exec(`
8
+ `)}getCurrentVersion(){return this.db.prepare("SELECT MAX(version) as version FROM _migrations").get()?.version??0}migrate(e){let t=[...e].sort((r,n)=>r.version-n.version),s=this.getCurrentVersion();for(let r of t){if(r.version<=s)continue;this.db.transaction(()=>{r.up(this.db),this.db.prepare("INSERT INTO _migrations (version, description, applied_at) VALUES (?, ?, ?)").run(r.version,r.description,new Date().toISOString())})()}}getAppliedMigrations(){return this.db.prepare("SELECT version, applied_at FROM _migrations ORDER BY version ASC").all().map(t=>({version:t.version,appliedAt:t.applied_at}))}}});var Mo,Oo=_(()=>{"use strict";an();Mo=[{version:1,description:"Initial schema \u2014 conversations, messages, users, audit_log",up(l){}},{version:2,description:"Add plugin_skills table for tracking loaded external plugins",up(l){l.exec(`
9
9
  CREATE TABLE IF NOT EXISTS plugin_skills (
10
10
  name TEXT PRIMARY KEY,
11
11
  file_path TEXT NOT NULL,
@@ -237,7 +237,16 @@ var ka=Object.defineProperty;var m=(l,e)=>ka(l,"name",{value:e,configurable:!0})
237
237
  );
238
238
  CREATE INDEX idx_watches_chat ON watches(chat_id, platform);
239
239
  CREATE INDEX idx_watches_enabled ON watches(enabled);
240
- `)}}]});import nm from"better-sqlite3";import om from"node:fs";import im from"node:path";var yt,Ba=_(()=>{"use strict";nn();No();yt=class{static{m(this,"Database")}db;constructor(e){let t=im.dirname(e);om.mkdirSync(t,{recursive:!0}),this.db=new nm(e),this.db.pragma("journal_mode = WAL"),this.db.pragma("foreign_keys = ON"),this.db.pragma("busy_timeout = 5000"),this.initTables(),this.runMigrations()}initTables(){this.db.exec(`
240
+ `)}},{version:16,description:"Running conversation summaries for long conversations",up(l){l.exec(`
241
+ CREATE TABLE IF NOT EXISTS conversation_summaries (
242
+ conversation_id TEXT PRIMARY KEY REFERENCES conversations(id) ON DELETE CASCADE,
243
+ summary TEXT NOT NULL,
244
+ message_count INTEGER NOT NULL DEFAULT 0,
245
+ last_user_message TEXT,
246
+ last_assistant_message TEXT,
247
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
248
+ );
249
+ `)}}]});import cm from"better-sqlite3";import lm from"node:fs";import dm from"node:path";var wt,qa=_(()=>{"use strict";an();Oo();wt=class{static{m(this,"Database")}db;constructor(e){let t=dm.dirname(e);lm.mkdirSync(t,{recursive:!0}),this.db=new cm(e),this.db.pragma("journal_mode = WAL"),this.db.pragma("foreign_keys = ON"),this.db.pragma("busy_timeout = 5000"),this.initTables(),this.runMigrations()}initTables(){this.db.exec(`
241
250
  CREATE TABLE IF NOT EXISTS conversations (
242
251
  id TEXT PRIMARY KEY,
243
252
  platform TEXT NOT NULL,
@@ -287,23 +296,23 @@ var ka=Object.defineProperty;var m=(l,e)=>ka(l,"name",{value:e,configurable:!0})
287
296
 
288
297
  CREATE UNIQUE INDEX IF NOT EXISTS idx_users_platform
289
298
  ON users(platform, platform_user_id);
290
- `)}runMigrations(){new Mt(this.db).migrate(Lo)}getDb(){return this.db}close(){this.db.close()}}});import Ha from"node:crypto";var Ps,qa=_(()=>{"use strict";Ps=class{static{m(this,"ConversationRepository")}db;constructor(e){this.db=e}create(e,t,s){let r=new Date().toISOString(),n={id:Ha.randomUUID(),platform:e,chatId:t,userId:s,createdAt:r,updatedAt:r};return this.db.prepare(`
299
+ `)}runMigrations(){new Pt(this.db).migrate(Mo)}getDb(){return this.db}close(){this.db.close()}}});import Wa from"node:crypto";var js,Ga=_(()=>{"use strict";js=class{static{m(this,"ConversationRepository")}db;constructor(e){this.db=e}create(e,t,s){let r=new Date().toISOString(),n={id:Wa.randomUUID(),platform:e,chatId:t,userId:s,createdAt:r,updatedAt:r};return this.db.prepare(`
291
300
  INSERT INTO conversations (id, platform, chat_id, user_id, created_at, updated_at)
292
301
  VALUES (?, ?, ?, ?, ?, ?)
293
- `).run(n.id,n.platform,n.chatId,n.userId,n.createdAt,n.updatedAt),n}findById(e){let t=this.db.prepare("SELECT * FROM conversations WHERE id = ?").get(e);if(t)return this.mapRow(t)}findByPlatformChat(e,t){let s=this.db.prepare("SELECT * FROM conversations WHERE platform = ? AND chat_id = ?").get(e,t);if(s)return this.mapRow(s)}findByPlatformAndUser(e,t){let s=this.db.prepare("SELECT * FROM conversations WHERE platform = ? AND user_id = ? ORDER BY updated_at DESC LIMIT 1").get(e,t);if(s)return this.mapRow(s)}addMessage(e,t,s,r){let n={id:Ha.randomUUID(),conversationId:e,role:t,content:s,toolCalls:r,createdAt:new Date().toISOString()};return this.db.prepare(`
302
+ `).run(n.id,n.platform,n.chatId,n.userId,n.createdAt,n.updatedAt),n}findById(e){let t=this.db.prepare("SELECT * FROM conversations WHERE id = ?").get(e);if(t)return this.mapRow(t)}findByPlatformChat(e,t){let s=this.db.prepare("SELECT * FROM conversations WHERE platform = ? AND chat_id = ?").get(e,t);if(s)return this.mapRow(s)}findByPlatformAndUser(e,t){let s=this.db.prepare("SELECT * FROM conversations WHERE platform = ? AND user_id = ? ORDER BY updated_at DESC LIMIT 1").get(e,t);if(s)return this.mapRow(s)}addMessage(e,t,s,r){let n={id:Wa.randomUUID(),conversationId:e,role:t,content:s,toolCalls:r,createdAt:new Date().toISOString()};return this.db.prepare(`
294
303
  INSERT INTO messages (id, conversation_id, role, content, tool_calls, created_at)
295
304
  VALUES (?, ?, ?, ?, ?, ?)
296
305
  `).run(n.id,n.conversationId,n.role,n.content,n.toolCalls??null,n.createdAt),n}getMessages(e,t=50){return this.db.prepare("SELECT * FROM (SELECT *, rowid AS _rn FROM messages WHERE conversation_id = ? ORDER BY created_at DESC, _rn DESC LIMIT ?) ORDER BY created_at ASC, _rn ASC").all(e,t).map(r=>({id:r.id,conversationId:r.conversation_id,role:r.role,content:r.content,toolCalls:r.tool_calls??void 0,createdAt:r.created_at}))}updateTimestamp(e){this.db.prepare("UPDATE conversations SET updated_at = ? WHERE id = ?").run(new Date().toISOString(),e)}pruneMessages(e,t){return this.db.prepare(`
297
306
  DELETE FROM messages WHERE conversation_id = ? AND id NOT IN (
298
307
  SELECT id FROM messages WHERE conversation_id = ? ORDER BY created_at DESC LIMIT ?
299
308
  )
300
- `).run(e,e,t).changes}mapRow(e){return{id:e.id,platform:e.platform,chatId:e.chat_id,userId:e.user_id,createdAt:e.created_at,updatedAt:e.updated_at}}}});import am from"node:crypto";var Us,za=_(()=>{"use strict";Us=class{static{m(this,"UserRepository")}db;constructor(e){this.db=e}findOrCreate(e,t,s,r){let n=this.db.prepare("SELECT * FROM users WHERE platform = ? AND platform_user_id = ?").get(e,t);if(n)return this.mapRow(n);let o=new Date().toISOString(),i={id:am.randomUUID(),platform:e,platformUserId:t,username:s,displayName:r,createdAt:o,updatedAt:o};return this.db.prepare(`
309
+ `).run(e,e,t).changes}mapRow(e){return{id:e.id,platform:e.platform,chatId:e.chat_id,userId:e.user_id,createdAt:e.created_at,updatedAt:e.updated_at}}}});import um from"node:crypto";var Bs,Va=_(()=>{"use strict";Bs=class{static{m(this,"UserRepository")}db;constructor(e){this.db=e}findOrCreate(e,t,s,r){let n=this.db.prepare("SELECT * FROM users WHERE platform = ? AND platform_user_id = ?").get(e,t);if(n)return this.mapRow(n);let o=new Date().toISOString(),i={id:um.randomUUID(),platform:e,platformUserId:t,username:s,displayName:r,createdAt:o,updatedAt:o};return this.db.prepare(`
301
310
  INSERT INTO users (id, platform, platform_user_id, username, display_name, created_at, updated_at)
302
311
  VALUES (?, ?, ?, ?, ?, ?, ?)
303
- `).run(i.id,i.platform,i.platformUserId,i.username??null,i.displayName??null,i.createdAt,i.updatedAt),i}findById(e){let t=this.db.prepare("SELECT * FROM users WHERE id = ?").get(e);if(t)return this.mapRow(t)}update(e,t){let s=[],r=[];t.username!==void 0&&(s.push("username = ?"),r.push(t.username??null)),t.displayName!==void 0&&(s.push("display_name = ?"),r.push(t.displayName??null)),s.length!==0&&(s.push("updated_at = ?"),r.push(new Date().toISOString()),r.push(e),this.db.prepare(`UPDATE users SET ${s.join(", ")} WHERE id = ?`).run(...r))}updateProfile(e,t){let s=[],r=[];t.timezone!==void 0&&(s.push("timezone = ?"),r.push(t.timezone??null)),t.language!==void 0&&(s.push("language = ?"),r.push(t.language??null)),t.bio!==void 0&&(s.push("bio = ?"),r.push(t.bio??null)),t.preferences!==void 0&&(s.push("preferences = ?"),r.push(t.preferences?JSON.stringify(t.preferences):null)),s.length!==0&&(s.push("updated_at = ?"),r.push(new Date().toISOString()),r.push(e),this.db.prepare(`UPDATE users SET ${s.join(", ")} WHERE id = ?`).run(...r))}getProfile(e){let t=this.db.prepare("SELECT display_name, timezone, language, bio, preferences FROM users WHERE id = ?").get(e);if(t)return{displayName:t.display_name??void 0,timezone:t.timezone??void 0,language:t.language??void 0,bio:t.bio??void 0,preferences:t.preferences?JSON.parse(t.preferences):void 0}}setMasterUser(e,t){this.db.prepare("UPDATE users SET master_user_id = ?, updated_at = ? WHERE id = ?").run(t,new Date().toISOString(),e)}getLinkedUsers(e){return this.db.prepare("SELECT DISTINCT * FROM users WHERE master_user_id = ? OR id = ?").all(e,e).map(s=>this.mapRow(s))}findFirstByPlatformNotIn(e){let t=e.map(()=>"?").join(", "),s=this.db.prepare(`SELECT * FROM users WHERE platform NOT IN (${t}) LIMIT 1`).get(...e);if(s)return this.mapRow(s)}getMasterUserId(e){return this.db.prepare("SELECT master_user_id FROM users WHERE id = ?").get(e)?.master_user_id??e}mapRow(e){return{id:e.id,platform:e.platform,platformUserId:e.platform_user_id,username:e.username??void 0,displayName:e.display_name??void 0,timezone:e.timezone??void 0,language:e.language??void 0,bio:e.bio??void 0,preferences:e.preferences?JSON.parse(e.preferences):void 0,masterUserId:e.master_user_id??void 0,createdAt:e.created_at,updatedAt:e.updated_at}}}});var wt,Wa=_(()=>{"use strict";wt=class{static{m(this,"AuditRepository")}db;constructor(e){this.db=e}log(e){this.db.prepare(`
312
+ `).run(i.id,i.platform,i.platformUserId,i.username??null,i.displayName??null,i.createdAt,i.updatedAt),i}findById(e){let t=this.db.prepare("SELECT * FROM users WHERE id = ?").get(e);if(t)return this.mapRow(t)}update(e,t){let s=[],r=[];t.username!==void 0&&(s.push("username = ?"),r.push(t.username??null)),t.displayName!==void 0&&(s.push("display_name = ?"),r.push(t.displayName??null)),s.length!==0&&(s.push("updated_at = ?"),r.push(new Date().toISOString()),r.push(e),this.db.prepare(`UPDATE users SET ${s.join(", ")} WHERE id = ?`).run(...r))}updateProfile(e,t){let s=[],r=[];t.timezone!==void 0&&(s.push("timezone = ?"),r.push(t.timezone??null)),t.language!==void 0&&(s.push("language = ?"),r.push(t.language??null)),t.bio!==void 0&&(s.push("bio = ?"),r.push(t.bio??null)),t.preferences!==void 0&&(s.push("preferences = ?"),r.push(t.preferences?JSON.stringify(t.preferences):null)),s.length!==0&&(s.push("updated_at = ?"),r.push(new Date().toISOString()),r.push(e),this.db.prepare(`UPDATE users SET ${s.join(", ")} WHERE id = ?`).run(...r))}getProfile(e){let t=this.db.prepare("SELECT display_name, timezone, language, bio, preferences FROM users WHERE id = ?").get(e);if(t)return{displayName:t.display_name??void 0,timezone:t.timezone??void 0,language:t.language??void 0,bio:t.bio??void 0,preferences:t.preferences?JSON.parse(t.preferences):void 0}}setMasterUser(e,t){this.db.prepare("UPDATE users SET master_user_id = ?, updated_at = ? WHERE id = ?").run(t,new Date().toISOString(),e)}getLinkedUsers(e){return this.db.prepare("SELECT DISTINCT * FROM users WHERE master_user_id = ? OR id = ?").all(e,e).map(s=>this.mapRow(s))}findFirstByPlatformNotIn(e){let t=e.map(()=>"?").join(", "),s=this.db.prepare(`SELECT * FROM users WHERE platform NOT IN (${t}) LIMIT 1`).get(...e);if(s)return this.mapRow(s)}getMasterUserId(e){return this.db.prepare("SELECT master_user_id FROM users WHERE id = ?").get(e)?.master_user_id??e}mapRow(e){return{id:e.id,platform:e.platform,platformUserId:e.platform_user_id,username:e.username??void 0,displayName:e.display_name??void 0,timezone:e.timezone??void 0,language:e.language??void 0,bio:e.bio??void 0,preferences:e.preferences?JSON.parse(e.preferences):void 0,masterUserId:e.master_user_id??void 0,createdAt:e.created_at,updatedAt:e.updated_at}}}});var Tt,Ka=_(()=>{"use strict";Tt=class{static{m(this,"AuditRepository")}db;constructor(e){this.db=e}log(e){this.db.prepare(`
304
313
  INSERT INTO audit_log (id, timestamp, user_id, action, risk_level, rule_id, effect, platform, chat_id, context)
305
314
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
306
- `).run(e.id,e.timestamp.toISOString(),e.userId,e.action,e.riskLevel,e.ruleId??null,e.effect,e.platform,e.chatId??null,e.context?JSON.stringify(e.context):null)}query(e){let t=[],s=[];e.userId&&(t.push("user_id = ?"),s.push(e.userId)),e.action&&(t.push("action = ?"),s.push(e.action)),e.effect&&(t.push("effect = ?"),s.push(e.effect));let r=t.length>0?`WHERE ${t.join(" AND ")}`:"",n=e.limit??100;return s.push(n),this.db.prepare(`SELECT * FROM audit_log ${r} ORDER BY timestamp DESC LIMIT ?`).all(...s).map(i=>this.mapRow(i))}count(e){let t=[],s=[];e.userId&&(t.push("user_id = ?"),s.push(e.userId)),e.effect&&(t.push("effect = ?"),s.push(e.effect));let r=t.length>0?`WHERE ${t.join(" AND ")}`:"";return this.db.prepare(`SELECT COUNT(*) as count FROM audit_log ${r}`).get(...s).count}mapRow(e){return{id:e.id,timestamp:new Date(e.timestamp),userId:e.user_id,action:e.action,riskLevel:e.risk_level,ruleId:e.rule_id??void 0,effect:e.effect,platform:e.platform,chatId:e.chat_id??void 0,context:e.context?JSON.parse(e.context):void 0}}}});import{randomUUID as cm}from"node:crypto";var Fs,Ga=_(()=>{"use strict";Fs=class{static{m(this,"MemoryRepository")}db;constructor(e){this.db=e}save(e,t,s,r="general"){return this.saveWithMetadata(e,t,s,r,"general",1,"manual")}saveWithMetadata(e,t,s,r,n,o,i){let a=new Date().toISOString(),c=cm();this.db.prepare(`INSERT INTO memories (id, user_id, key, value, category, type, confidence, source, created_at, updated_at)
315
+ `).run(e.id,e.timestamp.toISOString(),e.userId,e.action,e.riskLevel,e.ruleId??null,e.effect,e.platform,e.chatId??null,e.context?JSON.stringify(e.context):null)}query(e){let t=[],s=[];e.userId&&(t.push("user_id = ?"),s.push(e.userId)),e.action&&(t.push("action = ?"),s.push(e.action)),e.effect&&(t.push("effect = ?"),s.push(e.effect));let r=t.length>0?`WHERE ${t.join(" AND ")}`:"",n=e.limit??100;return s.push(n),this.db.prepare(`SELECT * FROM audit_log ${r} ORDER BY timestamp DESC LIMIT ?`).all(...s).map(i=>this.mapRow(i))}count(e){let t=[],s=[];e.userId&&(t.push("user_id = ?"),s.push(e.userId)),e.effect&&(t.push("effect = ?"),s.push(e.effect));let r=t.length>0?`WHERE ${t.join(" AND ")}`:"";return this.db.prepare(`SELECT COUNT(*) as count FROM audit_log ${r}`).get(...s).count}mapRow(e){return{id:e.id,timestamp:new Date(e.timestamp),userId:e.user_id,action:e.action,riskLevel:e.risk_level,ruleId:e.rule_id??void 0,effect:e.effect,platform:e.platform,chatId:e.chat_id??void 0,context:e.context?JSON.parse(e.context):void 0}}}});import{randomUUID as mm}from"node:crypto";var Hs,Xa=_(()=>{"use strict";Hs=class{static{m(this,"MemoryRepository")}db;constructor(e){this.db=e}save(e,t,s,r="general"){return this.saveWithMetadata(e,t,s,r,"general",1,"manual")}saveWithMetadata(e,t,s,r,n,o,i){let a=new Date().toISOString(),c=mm();this.db.prepare(`INSERT INTO memories (id, user_id, key, value, category, type, confidence, source, created_at, updated_at)
307
316
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
308
317
  ON CONFLICT(user_id, key) DO UPDATE SET
309
318
  value = excluded.value,
@@ -311,13 +320,13 @@ var ka=Object.defineProperty;var m=(l,e)=>ka(l,"name",{value:e,configurable:!0})
311
320
  type = excluded.type,
312
321
  confidence = excluded.confidence,
313
322
  source = excluded.source,
314
- updated_at = excluded.updated_at`).run(c,e,t,s,r,n,o,i,a,a);let d=this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND key = ?").get(e,t);return this.mapRow(d)}recall(e,t){let s=this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND key = ?").get(e,t);if(s)return this.mapRow(s)}search(e,t){let s=`%${t}%`;return this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND (key LIKE ? OR value LIKE ?) ORDER BY updated_at DESC").all(e,s,s).map(n=>this.mapRow(n))}keywordSearch(e,t,s=20){let r=t.toLowerCase().split(/\s+/).filter(c=>c.length>=2);if(r.length===0)return[];let n=r.map(()=>"(LOWER(key) LIKE ? OR LOWER(value) LIKE ?)").join(" OR "),o=[e];for(let c of r)o.push(`%${c}%`,`%${c}%`);let a=this.db.prepare(`SELECT * FROM memories WHERE user_id = ? AND (${n}) ORDER BY updated_at DESC`).all(...o).map(c=>{let d=this.mapRow(c),u=`${d.key} ${d.value}`.toLowerCase(),p=0;for(let g of r)u.includes(g)&&(p+=1);return{entry:d,score:p/r.length}});return a.sort((c,d)=>d.score-c.score),a.slice(0,s).map(c=>c.entry)}recordAccess(e){let t=new Date().toISOString();this.db.prepare("UPDATE memories SET last_accessed_at = ?, access_count = access_count + 1 WHERE id = ?").run(t,e)}findStale(e,t,s){let r=new Date(Date.now()-t*24*60*60*1e3).toISOString();return this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND updated_at < ? AND confidence <= ? ORDER BY confidence ASC").all(e,r,s).map(o=>this.mapRow(o))}deleteByIds(e){if(e.length===0)return 0;let t=e.map(()=>"?").join(",");return this.db.prepare(`DELETE FROM memories WHERE id IN (${t})`).run(...e).changes}listByCategory(e,t){return this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND category = ? ORDER BY updated_at DESC").all(e,t).map(r=>this.mapRow(r))}listAll(e){return this.db.prepare("SELECT * FROM memories WHERE user_id = ? ORDER BY updated_at DESC").all(e).map(s=>this.mapRow(s))}delete(e,t){return this.db.prepare("DELETE FROM memories WHERE user_id = ? AND key = ?").run(e,t).changes>0}getRecentForPrompt(e,t=20){return this.db.prepare("SELECT * FROM memories WHERE user_id = ? ORDER BY updated_at DESC LIMIT ?").all(e,t).map(r=>this.mapRow(r))}mapRow(e){return{id:e.id,userId:e.user_id,key:e.key,value:e.value,category:e.category||"general",type:e.type||"general",confidence:e.confidence??1,source:e.source||"manual",lastAccessedAt:e.last_accessed_at||null,accessCount:e.access_count??0,createdAt:e.created_at,updatedAt:e.updated_at}}}});import{randomUUID as lm}from"node:crypto";var js,Va=_(()=>{"use strict";js=class{static{m(this,"ReminderRepository")}db;constructor(e){this.db=e}create(e,t,s,r,n){let o={id:lm(),userId:e,platform:t,chatId:s,message:r,triggerAt:n.toISOString(),createdAt:new Date().toISOString(),fired:!1};return this.db.prepare(`
323
+ updated_at = excluded.updated_at`).run(c,e,t,s,r,n,o,i,a,a);let d=this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND key = ?").get(e,t);return this.mapRow(d)}recall(e,t){let s=this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND key = ?").get(e,t);if(s)return this.mapRow(s)}search(e,t){let s=`%${t}%`;return this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND (key LIKE ? OR value LIKE ?) ORDER BY updated_at DESC").all(e,s,s).map(n=>this.mapRow(n))}keywordSearch(e,t,s=20){let r=t.toLowerCase().split(/\s+/).filter(c=>c.length>=2);if(r.length===0)return[];let n=r.map(()=>"(LOWER(key) LIKE ? OR LOWER(value) LIKE ?)").join(" OR "),o=[e];for(let c of r)o.push(`%${c}%`,`%${c}%`);let a=this.db.prepare(`SELECT * FROM memories WHERE user_id = ? AND (${n}) ORDER BY updated_at DESC`).all(...o).map(c=>{let d=this.mapRow(c),u=`${d.key} ${d.value}`.toLowerCase(),p=0;for(let f of r)u.includes(f)&&(p+=1);return{entry:d,score:p/r.length}});return a.sort((c,d)=>d.score-c.score),a.slice(0,s).map(c=>c.entry)}recordAccess(e){let t=new Date().toISOString();this.db.prepare("UPDATE memories SET last_accessed_at = ?, access_count = access_count + 1 WHERE id = ?").run(t,e)}findStale(e,t,s){let r=new Date(Date.now()-t*24*60*60*1e3).toISOString();return this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND updated_at < ? AND confidence <= ? ORDER BY confidence ASC").all(e,r,s).map(o=>this.mapRow(o))}deleteByIds(e){if(e.length===0)return 0;let t=e.map(()=>"?").join(",");return this.db.prepare(`DELETE FROM memories WHERE id IN (${t})`).run(...e).changes}listByCategory(e,t){return this.db.prepare("SELECT * FROM memories WHERE user_id = ? AND category = ? ORDER BY updated_at DESC").all(e,t).map(r=>this.mapRow(r))}listAll(e){return this.db.prepare("SELECT * FROM memories WHERE user_id = ? ORDER BY updated_at DESC").all(e).map(s=>this.mapRow(s))}delete(e,t){return this.db.prepare("DELETE FROM memories WHERE user_id = ? AND key = ?").run(e,t).changes>0}getRecentForPrompt(e,t=20){return this.db.prepare("SELECT * FROM memories WHERE user_id = ? ORDER BY updated_at DESC LIMIT ?").all(e,t).map(r=>this.mapRow(r))}mapRow(e){return{id:e.id,userId:e.user_id,key:e.key,value:e.value,category:e.category||"general",type:e.type||"general",confidence:e.confidence??1,source:e.source||"manual",lastAccessedAt:e.last_accessed_at||null,accessCount:e.access_count??0,createdAt:e.created_at,updatedAt:e.updated_at}}}});import{randomUUID as pm}from"node:crypto";var zs,Ya=_(()=>{"use strict";zs=class{static{m(this,"ReminderRepository")}db;constructor(e){this.db=e}create(e,t,s,r,n){let o={id:pm(),userId:e,platform:t,chatId:s,message:r,triggerAt:n.toISOString(),createdAt:new Date().toISOString(),fired:!1};return this.db.prepare(`
315
324
  INSERT INTO reminders (id, user_id, platform, chat_id, message, trigger_at, created_at, fired)
316
325
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
317
- `).run(o.id,o.userId,o.platform,o.chatId,o.message,o.triggerAt,o.createdAt,0),o}getDue(){let e=new Date().toISOString();return this.db.prepare("SELECT * FROM reminders WHERE fired = 0 AND trigger_at <= ? ORDER BY trigger_at ASC").all(e).map(s=>this.mapRow(s))}getByUser(e){return this.db.prepare("SELECT * FROM reminders WHERE fired = 0 AND user_id = ? ORDER BY trigger_at ASC").all(e).map(s=>this.mapRow(s))}markFired(e){this.db.prepare("UPDATE reminders SET fired = 1 WHERE id = ?").run(e)}cancel(e){return this.db.prepare("DELETE FROM reminders WHERE id = ?").run(e).changes>0}mapRow(e){return{id:e.id,userId:e.user_id,platform:e.platform,chatId:e.chat_id,message:e.message,triggerAt:e.trigger_at,createdAt:e.created_at,fired:e.fired===1}}}});import{randomUUID as dm}from"node:crypto";var Bs,Ka=_(()=>{"use strict";Bs=class{static{m(this,"NoteRepository")}db;constructor(e){this.db=e}save(e,t,s){let r=new Date().toISOString(),n=dm();return this.db.prepare("INSERT INTO notes (id, user_id, title, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(n,e,t,s,r,r),{id:n,userId:e,title:t,content:s,createdAt:r,updatedAt:r}}getById(e){let t=this.db.prepare("SELECT * FROM notes WHERE id = ?").get(e);return t?this.mapRow(t):void 0}list(e,t=50){return this.db.prepare("SELECT * FROM notes WHERE user_id = ? ORDER BY updated_at DESC LIMIT ?").all(e,t).map(r=>this.mapRow(r))}search(e,t){let s=`%${t}%`;return this.db.prepare("SELECT * FROM notes WHERE user_id = ? AND (title LIKE ? OR content LIKE ?) ORDER BY updated_at DESC").all(e,s,s).map(n=>this.mapRow(n))}update(e,t,s){let r=this.getById(e);if(!r)return;let n=new Date().toISOString(),o=t??r.title,i=s??r.content;return this.db.prepare("UPDATE notes SET title = ?, content = ?, updated_at = ? WHERE id = ?").run(o,i,n,e),{...r,title:o,content:i,updatedAt:n}}delete(e){return this.db.prepare("DELETE FROM notes WHERE id = ?").run(e).changes>0}mapRow(e){return{id:e.id,userId:e.user_id,title:e.title,content:e.content,createdAt:e.created_at,updatedAt:e.updated_at}}}});import{randomUUID as um}from"node:crypto";var Hs,Xa=_(()=>{"use strict";Hs=class{static{m(this,"EmbeddingRepository")}db;constructor(e){this.db=e}store(e){let t=um(),s=new Date().toISOString(),r=Buffer.from(new Float32Array(e.embedding).buffer);return this.db.transaction(()=>{this.db.prepare("DELETE FROM embeddings WHERE user_id = ? AND source_type = ? AND source_id = ?").run(e.userId,e.sourceType,e.sourceId),this.db.prepare("INSERT INTO embeddings (id, user_id, source_type, source_id, content, embedding, model, dimensions, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)").run(t,e.userId,e.sourceType,e.sourceId,e.content,r,e.model,e.dimensions,s)})(),{id:t,userId:e.userId,sourceType:e.sourceType,sourceId:e.sourceId,content:e.content,embedding:e.embedding,model:e.model,dimensions:e.dimensions,createdAt:s}}findByUser(e){return this.db.prepare("SELECT * FROM embeddings WHERE user_id = ? ORDER BY created_at DESC").all(e).map(s=>this.mapRow(s))}findBySource(e,t){let s=this.db.prepare("SELECT * FROM embeddings WHERE source_type = ? AND source_id = ?").get(e,t);if(s)return this.mapRow(s)}delete(e,t){return this.db.prepare("DELETE FROM embeddings WHERE source_type = ? AND source_id = ?").run(e,t).changes>0}mapRow(e){let t=e.embedding,s=new Float32Array(t.buffer,t.byteOffset,t.byteLength/4),r=Array.from(s);return{id:e.id,userId:e.user_id,sourceType:e.source_type,sourceId:e.source_id,content:e.content,embedding:r,model:e.model,dimensions:e.dimensions,createdAt:e.created_at}}}});import mm from"node:crypto";var qs,Ya=_(()=>{"use strict";qs=class{static{m(this,"LinkTokenRepository")}db;constructor(e){this.db=e}create(e,t){for(let s=0;s<5;s++){let r={id:mm.randomUUID(),code:String(Math.floor(1e5+Math.random()*9e5)),userId:e,platform:t,createdAt:new Date().toISOString(),expiresAt:new Date(Date.now()+6e5).toISOString()};try{return this.db.prepare(`
326
+ `).run(o.id,o.userId,o.platform,o.chatId,o.message,o.triggerAt,o.createdAt,0),o}getDue(){let e=new Date().toISOString();return this.db.prepare("SELECT * FROM reminders WHERE fired = 0 AND trigger_at <= ? ORDER BY trigger_at ASC").all(e).map(s=>this.mapRow(s))}getByUser(e){return this.db.prepare("SELECT * FROM reminders WHERE fired = 0 AND user_id = ? ORDER BY trigger_at ASC").all(e).map(s=>this.mapRow(s))}markFired(e){this.db.prepare("UPDATE reminders SET fired = 1 WHERE id = ?").run(e)}cancel(e){return this.db.prepare("DELETE FROM reminders WHERE id = ?").run(e).changes>0}mapRow(e){return{id:e.id,userId:e.user_id,platform:e.platform,chatId:e.chat_id,message:e.message,triggerAt:e.trigger_at,createdAt:e.created_at,fired:e.fired===1}}}});import{randomUUID as hm}from"node:crypto";var qs,Ja=_(()=>{"use strict";qs=class{static{m(this,"NoteRepository")}db;constructor(e){this.db=e}save(e,t,s){let r=new Date().toISOString(),n=hm();return this.db.prepare("INSERT INTO notes (id, user_id, title, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(n,e,t,s,r,r),{id:n,userId:e,title:t,content:s,createdAt:r,updatedAt:r}}getById(e){let t=this.db.prepare("SELECT * FROM notes WHERE id = ?").get(e);return t?this.mapRow(t):void 0}list(e,t=50){return this.db.prepare("SELECT * FROM notes WHERE user_id = ? ORDER BY updated_at DESC LIMIT ?").all(e,t).map(r=>this.mapRow(r))}search(e,t){let s=`%${t}%`;return this.db.prepare("SELECT * FROM notes WHERE user_id = ? AND (title LIKE ? OR content LIKE ?) ORDER BY updated_at DESC").all(e,s,s).map(n=>this.mapRow(n))}update(e,t,s){let r=this.getById(e);if(!r)return;let n=new Date().toISOString(),o=t??r.title,i=s??r.content;return this.db.prepare("UPDATE notes SET title = ?, content = ?, updated_at = ? WHERE id = ?").run(o,i,n,e),{...r,title:o,content:i,updatedAt:n}}delete(e){return this.db.prepare("DELETE FROM notes WHERE id = ?").run(e).changes>0}mapRow(e){return{id:e.id,userId:e.user_id,title:e.title,content:e.content,createdAt:e.created_at,updatedAt:e.updated_at}}}});import{randomUUID as fm}from"node:crypto";var Ws,Za=_(()=>{"use strict";Ws=class{static{m(this,"EmbeddingRepository")}db;constructor(e){this.db=e}store(e){let t=fm(),s=new Date().toISOString(),r=Buffer.from(new Float32Array(e.embedding).buffer);return this.db.transaction(()=>{this.db.prepare("DELETE FROM embeddings WHERE user_id = ? AND source_type = ? AND source_id = ?").run(e.userId,e.sourceType,e.sourceId),this.db.prepare("INSERT INTO embeddings (id, user_id, source_type, source_id, content, embedding, model, dimensions, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)").run(t,e.userId,e.sourceType,e.sourceId,e.content,r,e.model,e.dimensions,s)})(),{id:t,userId:e.userId,sourceType:e.sourceType,sourceId:e.sourceId,content:e.content,embedding:e.embedding,model:e.model,dimensions:e.dimensions,createdAt:s}}findByUser(e){return this.db.prepare("SELECT * FROM embeddings WHERE user_id = ? ORDER BY created_at DESC").all(e).map(s=>this.mapRow(s))}findBySource(e,t){let s=this.db.prepare("SELECT * FROM embeddings WHERE source_type = ? AND source_id = ?").get(e,t);if(s)return this.mapRow(s)}delete(e,t){return this.db.prepare("DELETE FROM embeddings WHERE source_type = ? AND source_id = ?").run(e,t).changes>0}mapRow(e){let t=e.embedding,s=new Float32Array(t.buffer,t.byteOffset,t.byteLength/4),r=Array.from(s);return{id:e.id,userId:e.user_id,sourceType:e.source_type,sourceId:e.source_id,content:e.content,embedding:r,model:e.model,dimensions:e.dimensions,createdAt:e.created_at}}}});import gm from"node:crypto";var Gs,Qa=_(()=>{"use strict";Gs=class{static{m(this,"LinkTokenRepository")}db;constructor(e){this.db=e}create(e,t){for(let s=0;s<5;s++){let r={id:gm.randomUUID(),code:String(Math.floor(1e5+Math.random()*9e5)),userId:e,platform:t,createdAt:new Date().toISOString(),expiresAt:new Date(Date.now()+6e5).toISOString()};try{return this.db.prepare(`
318
327
  INSERT INTO link_tokens (id, code, user_id, platform, created_at, expires_at)
319
328
  VALUES (?, ?, ?, ?, ?, ?)
320
- `).run(r.id,r.code,r.userId,r.platform,r.createdAt,r.expiresAt),r}catch(n){if(!(n instanceof Error?n.message:"").includes("UNIQUE")||s===4)throw n}}throw new Error("Failed to generate unique link code after retries")}findByCode(e){let t=this.db.prepare("SELECT * FROM link_tokens WHERE code = ? AND expires_at > ?").get(e,new Date().toISOString());if(t)return{id:t.id,code:t.code,userId:t.user_id,platform:t.platform,createdAt:t.created_at,expiresAt:t.expires_at}}consume(e){this.db.prepare("DELETE FROM link_tokens WHERE id = ?").run(e)}countRecentByUser(e,t=10){let s=new Date(Date.now()-t*6e4).toISOString();return this.db.prepare("SELECT COUNT(*) as cnt FROM link_tokens WHERE user_id = ? AND created_at > ?").get(e,s).cnt}cleanup(){this.db.prepare("DELETE FROM link_tokens WHERE expires_at <= ?").run(new Date().toISOString())}}});import{randomUUID as pm}from"node:crypto";var zs,Ja=_(()=>{"use strict";zs=class{static{m(this,"BackgroundTaskRepository")}db;constructor(e){this.db=e}create(e,t,s,r,n,o){let i={id:pm(),userId:e,platform:t,chatId:s,description:r,skillName:n,skillInput:o,status:"pending",createdAt:new Date().toISOString()};return this.db.prepare(`
329
+ `).run(r.id,r.code,r.userId,r.platform,r.createdAt,r.expiresAt),r}catch(n){if(!(n instanceof Error?n.message:"").includes("UNIQUE")||s===4)throw n}}throw new Error("Failed to generate unique link code after retries")}findByCode(e){let t=this.db.prepare("SELECT * FROM link_tokens WHERE code = ? AND expires_at > ?").get(e,new Date().toISOString());if(t)return{id:t.id,code:t.code,userId:t.user_id,platform:t.platform,createdAt:t.created_at,expiresAt:t.expires_at}}consume(e){this.db.prepare("DELETE FROM link_tokens WHERE id = ?").run(e)}countRecentByUser(e,t=10){let s=new Date(Date.now()-t*6e4).toISOString();return this.db.prepare("SELECT COUNT(*) as cnt FROM link_tokens WHERE user_id = ? AND created_at > ?").get(e,s).cnt}cleanup(){this.db.prepare("DELETE FROM link_tokens WHERE expires_at <= ?").run(new Date().toISOString())}}});import{randomUUID as ym}from"node:crypto";var Vs,ec=_(()=>{"use strict";Vs=class{static{m(this,"BackgroundTaskRepository")}db;constructor(e){this.db=e}create(e,t,s,r,n,o){let i={id:ym(),userId:e,platform:t,chatId:s,description:r,skillName:n,skillInput:o,status:"pending",createdAt:new Date().toISOString()};return this.db.prepare(`
321
330
  INSERT INTO background_tasks (id, user_id, platform, chat_id, description, skill_name, skill_input, status, created_at)
322
331
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
323
332
  `).run(i.id,i.userId,i.platform,i.chatId,i.description,i.skillName,i.skillInput,i.status,i.createdAt),i}updateStatus(e,t,s,r){let n=new Date().toISOString(),o=null,i=null;t==="running"&&(o=n),(t==="completed"||t==="failed")&&(i=n),this.db.prepare(`
@@ -333,7 +342,7 @@ var ka=Object.defineProperty;var m=(l,e)=>ka(l,"name",{value:e,configurable:!0})
333
342
  AND (status IN ('pending', 'running') OR completed_at > datetime('now', '-1 day'))
334
343
  ORDER BY created_at DESC`).all(e).map(s=>this.mapRow(s))}cancel(e){return this.db.prepare("DELETE FROM background_tasks WHERE id = ? AND status IN ('pending', 'running')").run(e).changes>0}cleanup(e=7){return this.db.prepare(`DELETE FROM background_tasks
335
344
  WHERE status IN ('completed', 'failed')
336
- AND completed_at < datetime('now', '-' || ? || ' days')`).run(e).changes}mapRow(e){return{id:e.id,userId:e.user_id,platform:e.platform,chatId:e.chat_id,description:e.description,skillName:e.skill_name,skillInput:e.skill_input,status:e.status,result:e.result,error:e.error,createdAt:e.created_at,startedAt:e.started_at,completedAt:e.completed_at}}}});import{randomUUID as hm}from"node:crypto";var Ws,Za=_(()=>{"use strict";Ws=class{static{m(this,"ScheduledActionRepository")}db;constructor(e){this.db=e}create(e){let t=new Date().toISOString(),s=hm(),r=this.calculateInitialNextRun(e.scheduleType,e.scheduleValue);return this.db.prepare(`
345
+ AND completed_at < datetime('now', '-' || ? || ' days')`).run(e).changes}mapRow(e){return{id:e.id,userId:e.user_id,platform:e.platform,chatId:e.chat_id,description:e.description,skillName:e.skill_name,skillInput:e.skill_input,status:e.status,result:e.result,error:e.error,createdAt:e.created_at,startedAt:e.started_at,completedAt:e.completed_at}}}});import{randomUUID as wm}from"node:crypto";var Ks,tc=_(()=>{"use strict";Ks=class{static{m(this,"ScheduledActionRepository")}db;constructor(e){this.db=e}create(e){let t=new Date().toISOString(),s=wm(),r=this.calculateInitialNextRun(e.scheduleType,e.scheduleValue);return this.db.prepare(`
337
346
  INSERT INTO scheduled_actions
338
347
  (id, user_id, platform, chat_id, name, description, schedule_type, schedule_value,
339
348
  skill_name, skill_input, prompt_template, enabled, next_run_at, created_at)
@@ -344,11 +353,11 @@ var ka=Object.defineProperty;var m=(l,e)=>ka(l,"name",{value:e,configurable:!0})
344
353
  UPDATE scheduled_actions
345
354
  SET last_run_at = ?, next_run_at = ?
346
355
  WHERE id = ?
347
- `).run(t,s,e)}setEnabled(e,t){return this.db.prepare("UPDATE scheduled_actions SET enabled = ? WHERE id = ?").run(t?1:0,e).changes>0}delete(e){return this.db.prepare("DELETE FROM scheduled_actions WHERE id = ?").run(e).changes>0}calculateInitialNextRun(e,t){let s=new Date;switch(e){case"interval":{let r=parseInt(t,10);return isNaN(r)||r<=0?null:new Date(s.getTime()+r*6e4).toISOString()}case"once":return new Date(t).toISOString();case"cron":return this.getNextCronDate(t,s)?.toISOString()??null;default:return null}}getNextCronDate(e,t){let s=e.trim().split(/\s+/);if(s.length!==5)return null;let r=new Date(t.getTime()+6e4);r.setSeconds(0,0);for(let n=0;n<1440;n++){if(this.matchesCron(s,r))return r;r.setTime(r.getTime()+6e4)}return null}matchesCron(e,t){let s=t.getMinutes(),r=t.getHours(),n=t.getDate(),o=t.getMonth()+1,i=t.getDay();return this.matchCronField(e[0],s)&&this.matchCronField(e[1],r)&&this.matchCronField(e[2],n)&&this.matchCronField(e[3],o)&&this.matchCronField(e[4],i)}matchCronField(e,t){if(e==="*")return!0;let s=/^\*\/(\d+)$/.exec(e);if(s){let n=parseInt(s[1],10);return t%n===0}let r=parseInt(e,10);return isNaN(r)?!1:t===r}mapRow(e){return{id:e.id,userId:e.user_id,platform:e.platform,chatId:e.chat_id,name:e.name,description:e.description,scheduleType:e.schedule_type,scheduleValue:e.schedule_value,skillName:e.skill_name,skillInput:e.skill_input,promptTemplate:e.prompt_template,enabled:e.enabled===1,lastRunAt:e.last_run_at,nextRunAt:e.next_run_at,createdAt:e.created_at}}}});import{randomUUID as Qa}from"node:crypto";var Gs,ec=_(()=>{"use strict";Gs=class{static{m(this,"DocumentRepository")}db;constructor(e){this.db=e}createDocument(e,t,s,r,n){let o=Qa(),i=new Date().toISOString();return this.db.prepare("INSERT INTO documents (id, user_id, filename, mime_type, size_bytes, chunk_count, content_hash, created_at) VALUES (?, ?, ?, ?, ?, 0, ?, ?)").run(o,e,t,s,r,n??null,i),{id:o,userId:e,filename:t,mimeType:s,sizeBytes:r,chunkCount:0,contentHash:n,createdAt:i}}findByContentHash(e,t){let s=this.db.prepare("SELECT * FROM documents WHERE user_id = ? AND content_hash = ? ORDER BY chunk_count DESC LIMIT 1").get(e,t);return s?this.mapDocumentRow(s):void 0}updateChunkCount(e,t){this.db.prepare("UPDATE documents SET chunk_count = ? WHERE id = ?").run(t,e)}addChunk(e,t,s,r){let n=Qa(),o=new Date().toISOString();return this.db.prepare("INSERT INTO document_chunks (id, document_id, chunk_index, content, embedding_id, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(n,e,t,s,r??null,o),{id:n,documentId:e,chunkIndex:t,content:s,embeddingId:r,createdAt:o}}getDocument(e){let t=this.db.prepare("SELECT * FROM documents WHERE id = ?").get(e);return t?this.mapDocumentRow(t):void 0}getChunks(e){return this.db.prepare("SELECT * FROM document_chunks WHERE document_id = ? ORDER BY chunk_index ASC").all(e).map(s=>this.mapChunkRow(s))}listByUser(e){return this.db.prepare("SELECT * FROM documents WHERE user_id = ? ORDER BY created_at DESC").all(e).map(s=>this.mapDocumentRow(s))}deleteDocument(e){this.db.transaction(()=>{let s=this.db.prepare("SELECT embedding_id FROM document_chunks WHERE document_id = ? AND embedding_id IS NOT NULL").all(e);if(s.length>0){let r=s.map(o=>o.embedding_id),n=r.map(()=>"?").join(", ");this.db.prepare(`DELETE FROM embeddings WHERE id IN (${n})`).run(...r)}this.db.prepare("DELETE FROM document_chunks WHERE document_id = ?").run(e),this.db.prepare("DELETE FROM documents WHERE id = ?").run(e)})()}getChunksByEmbeddingIds(e){if(e.length===0)return[];let t=e.map(()=>"?").join(", ");return this.db.prepare(`SELECT * FROM document_chunks WHERE embedding_id IN (${t}) ORDER BY chunk_index ASC`).all(...e).map(r=>this.mapChunkRow(r))}mapDocumentRow(e){return{id:e.id,userId:e.user_id,filename:e.filename,mimeType:e.mime_type,sizeBytes:e.size_bytes,chunkCount:e.chunk_count,contentHash:e.content_hash||void 0,createdAt:e.created_at}}mapChunkRow(e){return{id:e.id,documentId:e.document_id,chunkIndex:e.chunk_index,content:e.content,embeddingId:e.embedding_id||void 0,createdAt:e.created_at}}}});import{randomUUID as fm}from"node:crypto";var Vs,tc=_(()=>{"use strict";Vs=class{static{m(this,"TodoRepository")}db;constructor(e){this.db=e}add(e,t,s){let r=new Date().toISOString(),n=fm(),o=s?.list??"default",i=s?.priority??"normal";return this.db.prepare("INSERT INTO todos (id, user_id, list, title, description, priority, due_date, completed, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?)").run(n,e,o,t,s?.description??null,i,s?.dueDate??null,r,r),{id:n,userId:e,list:o,title:t,description:s?.description,priority:i,dueDate:s?.dueDate,completed:!1,createdAt:r,updatedAt:r}}list(e,t,s=!1){let r="SELECT * FROM todos WHERE user_id = ?",n=[e];return t&&(r+=" AND list = ?",n.push(t)),s||(r+=" AND completed = 0"),r+=" ORDER BY CASE priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'normal' THEN 2 WHEN 'low' THEN 3 END, created_at DESC",this.db.prepare(r).all(...n).map(i=>this.mapRow(i))}getById(e){let t=this.db.prepare("SELECT * FROM todos WHERE id = ?").get(e);return t?this.mapRow(t):void 0}complete(e){let t=new Date().toISOString();return this.db.prepare("UPDATE todos SET completed = 1, updated_at = ? WHERE id = ? AND completed = 0").run(t,e).changes>0}uncomplete(e){let t=new Date().toISOString();return this.db.prepare("UPDATE todos SET completed = 0, updated_at = ? WHERE id = ? AND completed = 1").run(t,e).changes>0}delete(e){return this.db.prepare("DELETE FROM todos WHERE id = ?").run(e).changes>0}clearCompleted(e,t){let s="DELETE FROM todos WHERE user_id = ? AND completed = 1",r=[e];return t&&(s+=" AND list = ?",r.push(t)),this.db.prepare(s).run(...r).changes}getLists(e){return this.db.prepare(`SELECT list,
356
+ `).run(t,s,e)}setEnabled(e,t){return this.db.prepare("UPDATE scheduled_actions SET enabled = ? WHERE id = ?").run(t?1:0,e).changes>0}delete(e){return this.db.prepare("DELETE FROM scheduled_actions WHERE id = ?").run(e).changes>0}calculateInitialNextRun(e,t){let s=new Date;switch(e){case"interval":{let r=parseInt(t,10);return isNaN(r)||r<=0?null:new Date(s.getTime()+r*6e4).toISOString()}case"once":return new Date(t).toISOString();case"cron":return this.getNextCronDate(t,s)?.toISOString()??null;default:return null}}getNextCronDate(e,t){let s=e.trim().split(/\s+/);if(s.length!==5)return null;let r=new Date(t.getTime()+6e4);r.setSeconds(0,0);for(let n=0;n<1440;n++){if(this.matchesCron(s,r))return r;r.setTime(r.getTime()+6e4)}return null}matchesCron(e,t){let s=t.getMinutes(),r=t.getHours(),n=t.getDate(),o=t.getMonth()+1,i=t.getDay();return this.matchCronField(e[0],s)&&this.matchCronField(e[1],r)&&this.matchCronField(e[2],n)&&this.matchCronField(e[3],o)&&this.matchCronField(e[4],i)}matchCronField(e,t){if(e==="*")return!0;let s=/^\*\/(\d+)$/.exec(e);if(s){let n=parseInt(s[1],10);return t%n===0}let r=parseInt(e,10);return isNaN(r)?!1:t===r}mapRow(e){return{id:e.id,userId:e.user_id,platform:e.platform,chatId:e.chat_id,name:e.name,description:e.description,scheduleType:e.schedule_type,scheduleValue:e.schedule_value,skillName:e.skill_name,skillInput:e.skill_input,promptTemplate:e.prompt_template,enabled:e.enabled===1,lastRunAt:e.last_run_at,nextRunAt:e.next_run_at,createdAt:e.created_at}}}});import{randomUUID as sc}from"node:crypto";var Xs,rc=_(()=>{"use strict";Xs=class{static{m(this,"DocumentRepository")}db;constructor(e){this.db=e}createDocument(e,t,s,r,n){let o=sc(),i=new Date().toISOString();return this.db.prepare("INSERT INTO documents (id, user_id, filename, mime_type, size_bytes, chunk_count, content_hash, created_at) VALUES (?, ?, ?, ?, ?, 0, ?, ?)").run(o,e,t,s,r,n??null,i),{id:o,userId:e,filename:t,mimeType:s,sizeBytes:r,chunkCount:0,contentHash:n,createdAt:i}}findByContentHash(e,t){let s=this.db.prepare("SELECT * FROM documents WHERE user_id = ? AND content_hash = ? ORDER BY chunk_count DESC LIMIT 1").get(e,t);return s?this.mapDocumentRow(s):void 0}updateChunkCount(e,t){this.db.prepare("UPDATE documents SET chunk_count = ? WHERE id = ?").run(t,e)}addChunk(e,t,s,r){let n=sc(),o=new Date().toISOString();return this.db.prepare("INSERT INTO document_chunks (id, document_id, chunk_index, content, embedding_id, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(n,e,t,s,r??null,o),{id:n,documentId:e,chunkIndex:t,content:s,embeddingId:r,createdAt:o}}getDocument(e){let t=this.db.prepare("SELECT * FROM documents WHERE id = ?").get(e);return t?this.mapDocumentRow(t):void 0}getChunks(e){return this.db.prepare("SELECT * FROM document_chunks WHERE document_id = ? ORDER BY chunk_index ASC").all(e).map(s=>this.mapChunkRow(s))}listByUser(e){return this.db.prepare("SELECT * FROM documents WHERE user_id = ? ORDER BY created_at DESC").all(e).map(s=>this.mapDocumentRow(s))}deleteDocument(e){this.db.transaction(()=>{let s=this.db.prepare("SELECT embedding_id FROM document_chunks WHERE document_id = ? AND embedding_id IS NOT NULL").all(e);if(s.length>0){let r=s.map(o=>o.embedding_id),n=r.map(()=>"?").join(", ");this.db.prepare(`DELETE FROM embeddings WHERE id IN (${n})`).run(...r)}this.db.prepare("DELETE FROM document_chunks WHERE document_id = ?").run(e),this.db.prepare("DELETE FROM documents WHERE id = ?").run(e)})()}getChunksByEmbeddingIds(e){if(e.length===0)return[];let t=e.map(()=>"?").join(", ");return this.db.prepare(`SELECT * FROM document_chunks WHERE embedding_id IN (${t}) ORDER BY chunk_index ASC`).all(...e).map(r=>this.mapChunkRow(r))}mapDocumentRow(e){return{id:e.id,userId:e.user_id,filename:e.filename,mimeType:e.mime_type,sizeBytes:e.size_bytes,chunkCount:e.chunk_count,contentHash:e.content_hash||void 0,createdAt:e.created_at}}mapChunkRow(e){return{id:e.id,documentId:e.document_id,chunkIndex:e.chunk_index,content:e.content,embeddingId:e.embedding_id||void 0,createdAt:e.created_at}}}});import{randomUUID as Tm}from"node:crypto";var Ys,nc=_(()=>{"use strict";Ys=class{static{m(this,"TodoRepository")}db;constructor(e){this.db=e}add(e,t,s){let r=new Date().toISOString(),n=Tm(),o=s?.list??"default",i=s?.priority??"normal";return this.db.prepare("INSERT INTO todos (id, user_id, list, title, description, priority, due_date, completed, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?)").run(n,e,o,t,s?.description??null,i,s?.dueDate??null,r,r),{id:n,userId:e,list:o,title:t,description:s?.description,priority:i,dueDate:s?.dueDate,completed:!1,createdAt:r,updatedAt:r}}list(e,t,s=!1){let r="SELECT * FROM todos WHERE user_id = ?",n=[e];return t&&(r+=" AND list = ?",n.push(t)),s||(r+=" AND completed = 0"),r+=" ORDER BY CASE priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'normal' THEN 2 WHEN 'low' THEN 3 END, created_at DESC",this.db.prepare(r).all(...n).map(i=>this.mapRow(i))}getById(e){let t=this.db.prepare("SELECT * FROM todos WHERE id = ?").get(e);return t?this.mapRow(t):void 0}complete(e){let t=new Date().toISOString();return this.db.prepare("UPDATE todos SET completed = 1, updated_at = ? WHERE id = ? AND completed = 0").run(t,e).changes>0}uncomplete(e){let t=new Date().toISOString();return this.db.prepare("UPDATE todos SET completed = 0, updated_at = ? WHERE id = ? AND completed = 1").run(t,e).changes>0}delete(e){return this.db.prepare("DELETE FROM todos WHERE id = ?").run(e).changes>0}clearCompleted(e,t){let s="DELETE FROM todos WHERE user_id = ? AND completed = 1",r=[e];return t&&(s+=" AND list = ?",r.push(t)),this.db.prepare(s).run(...r).changes}getLists(e){return this.db.prepare(`SELECT list,
348
357
  SUM(CASE WHEN completed = 0 THEN 1 ELSE 0 END) as open,
349
358
  SUM(CASE WHEN completed = 1 THEN 1 ELSE 0 END) as completed,
350
359
  COUNT(*) as total
351
- FROM todos WHERE user_id = ? GROUP BY list ORDER BY list`).all(e).map(s=>({list:s.list,open:Number(s.open),completed:Number(s.completed),total:Number(s.total)}))}mapRow(e){return{id:e.id,userId:e.user_id,list:e.list,title:e.title,description:e.description,priority:e.priority,dueDate:e.due_date,completed:e.completed===1,createdAt:e.created_at,updatedAt:e.updated_at}}}});import{randomUUID as gm}from"node:crypto";var Ks,sc=_(()=>{"use strict";Ks=class{static{m(this,"WatchRepository")}db;constructor(e){this.db=e}create(e){let t=gm(),s=new Date().toISOString();return this.db.prepare(`
360
+ FROM todos WHERE user_id = ? GROUP BY list ORDER BY list`).all(e).map(s=>({list:s.list,open:Number(s.open),completed:Number(s.completed),total:Number(s.total)}))}mapRow(e){return{id:e.id,userId:e.user_id,list:e.list,title:e.title,description:e.description,priority:e.priority,dueDate:e.due_date,completed:e.completed===1,createdAt:e.created_at,updatedAt:e.updated_at}}}});import{randomUUID as _m}from"node:crypto";var Js,oc=_(()=>{"use strict";Js=class{static{m(this,"WatchRepository")}db;constructor(e){this.db=e}create(e){let t=_m(),s=new Date().toISOString();return this.db.prepare(`
352
361
  INSERT INTO watches
353
362
  (id, chat_id, platform, name, skill_name, skill_params,
354
363
  condition_field, condition_operator, condition_value,
@@ -368,10 +377,19 @@ var ka=Object.defineProperty;var m=(l,e)=>ka(l,"name",{value:e,configurable:!0})
368
377
  UPDATE watches
369
378
  SET last_checked_at = ?, last_value = ?
370
379
  WHERE id = ?
371
- `).run(t.lastCheckedAt,t.lastValue,e)}findByChatId(e,t){return this.db.prepare("SELECT * FROM watches WHERE chat_id = ? AND platform = ? ORDER BY created_at DESC").all(e,t).map(r=>this.mapRow(r))}toggle(e,t){return this.db.prepare("UPDATE watches SET enabled = ? WHERE id = ?").run(t?1:0,e).changes>0}delete(e){return this.db.prepare("DELETE FROM watches WHERE id = ?").run(e).changes>0}getById(e){let t=this.db.prepare("SELECT * FROM watches WHERE id = ?").get(e);return t?this.mapRow(t):void 0}mapRow(e){let t={};try{t=JSON.parse(e.skill_params)}catch{}let s={field:e.condition_field,operator:e.condition_operator};if(e.condition_value!=null){let r=Number(e.condition_value);s.value=isNaN(r)?e.condition_value:r}return{id:e.id,chatId:e.chat_id,platform:e.platform,name:e.name,skillName:e.skill_name,skillParams:t,condition:s,intervalMinutes:e.interval_minutes,cooldownMinutes:e.cooldown_minutes,enabled:e.enabled===1,lastCheckedAt:e.last_checked_at??null,lastTriggeredAt:e.last_triggered_at??null,lastValue:e.last_value??null,createdAt:e.created_at,messageTemplate:e.message_template}}}});var Mo=_(()=>{"use strict";Ba();qa();za();Wa();Ga();nn();No();Va();Ka();Xa();Ya();Ja();Za();ec();tc();sc()});function Ke(l){if(Oo[l])return Oo[l];let e=Object.entries(Oo).sort((t,s)=>s[0].length-t[0].length);for(let[t,s]of e)if(l.startsWith(t))return s}var Oo,ym,Ie,Tt=_(()=>{"use strict";Oo={"claude-opus-4-20250514":{maxInputTokens:2e5,maxOutputTokens:32e3},"claude-opus-4-5-20251101":{maxInputTokens:2e5,maxOutputTokens:32e3},"claude-sonnet-4-20250514":{maxInputTokens:2e5,maxOutputTokens:16e3},"claude-sonnet-4-5-20250929":{maxInputTokens:2e5,maxOutputTokens:16e3},"claude-3-5-sonnet-20241022":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-3-5-sonnet-20240620":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-3-5-haiku-20241022":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-haiku-3-5-20241022":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-haiku-4-5-20251001":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-3-opus-20240229":{maxInputTokens:2e5,maxOutputTokens:4096},"claude-3-sonnet-20240229":{maxInputTokens:2e5,maxOutputTokens:4096},"claude-3-haiku-20240307":{maxInputTokens:2e5,maxOutputTokens:4096},"claude-":{maxInputTokens:2e5,maxOutputTokens:16e3},"gpt-4.1":{maxInputTokens:1047576,maxOutputTokens:32768},"gpt-4.1-mini":{maxInputTokens:1047576,maxOutputTokens:32768},"gpt-4.1-nano":{maxInputTokens:1047576,maxOutputTokens:32768},"gpt-4o":{maxInputTokens:128e3,maxOutputTokens:16384},"gpt-4o-mini":{maxInputTokens:128e3,maxOutputTokens:16384},"gpt-4-turbo":{maxInputTokens:128e3,maxOutputTokens:4096},"gpt-4":{maxInputTokens:8192,maxOutputTokens:4096},"gpt-3.5-turbo":{maxInputTokens:16384,maxOutputTokens:4096},o3:{maxInputTokens:2e5,maxOutputTokens:1e5},"o3-mini":{maxInputTokens:2e5,maxOutputTokens:1e5},"o4-mini":{maxInputTokens:2e5,maxOutputTokens:1e5},o1:{maxInputTokens:2e5,maxOutputTokens:1e5},"o1-mini":{maxInputTokens:128e3,maxOutputTokens:65536},"gpt-5.4":{maxInputTokens:105e4,maxOutputTokens:128e3},"gpt-5":{maxInputTokens:4e5,maxOutputTokens:128e3},"gemini-3.1-pro":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-3.1-flash":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-3-pro":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-3-flash":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-2.5-pro":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-2.5-flash":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-2.0-flash":{maxInputTokens:1048576,maxOutputTokens:8192},"gemini-2.0-pro":{maxInputTokens:1048576,maxOutputTokens:8192},"gemini-1.5-pro":{maxInputTokens:2097152,maxOutputTokens:8192},"gemini-1.5-flash":{maxInputTokens:1048576,maxOutputTokens:8192},"mistral-large":{maxInputTokens:262144,maxOutputTokens:262144},"mistral-medium":{maxInputTokens:128e3,maxOutputTokens:128e3},"mistral-small":{maxInputTokens:128e3,maxOutputTokens:128e3},codestral:{maxInputTokens:262144,maxOutputTokens:262144},"magistral-medium":{maxInputTokens:128e3,maxOutputTokens:131072},"magistral-small":{maxInputTokens:128e3,maxOutputTokens:131072},ministral:{maxInputTokens:128e3,maxOutputTokens:128e3},llama4:{maxInputTokens:128e3,maxOutputTokens:4096},"llama3.2":{maxInputTokens:128e3,maxOutputTokens:4096},"llama3.1":{maxInputTokens:128e3,maxOutputTokens:4096},llama3:{maxInputTokens:8192,maxOutputTokens:4096},gemma3:{maxInputTokens:128e3,maxOutputTokens:8192},gemma2:{maxInputTokens:8192,maxOutputTokens:4096},qwen3:{maxInputTokens:128e3,maxOutputTokens:8192},"qwen2.5":{maxInputTokens:128e3,maxOutputTokens:4096},mixtral:{maxInputTokens:32e3,maxOutputTokens:4096},phi3:{maxInputTokens:128e3,maxOutputTokens:4096},phi4:{maxInputTokens:128e3,maxOutputTokens:4096},"deepseek-r1":{maxInputTokens:128e3,maxOutputTokens:8192},"deepseek-v3":{maxInputTokens:128e3,maxOutputTokens:8192},"deepseek-chat":{maxInputTokens:128e3,maxOutputTokens:8192},"command-r":{maxInputTokens:128e3,maxOutputTokens:4096},"command-r-plus":{maxInputTokens:128e3,maxOutputTokens:4096}},ym={maxInputTokens:128e3,maxOutputTokens:8192};m(Ke,"lookupContextWindow");Ie=class{static{m(this,"LLMProvider")}config;contextWindow=ym;constructor(e){this.config=e}getContextWindow(){return this.contextWindow}async embed(e){}supportsEmbeddings(){return!1}}});import wm from"@anthropic-ai/sdk";var Xs,Po=_(()=>{"use strict";Tt();Xs=class extends Ie{static{m(this,"AnthropicProvider")}client;constructor(e){super(e)}async initialize(){this.client=new wm({apiKey:this.config.apiKey,maxRetries:5});let e=Ke(this.config.model);e&&(this.contextWindow=e)}async complete(e){let t=this.mapMessages(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,max_tokens:e.maxTokens??this.config.maxTokens??4096,temperature:e.temperature??this.config.temperature,system:e.system?[{type:"text",text:e.system,cache_control:{type:"ephemeral"}}]:void 0,messages:t,tools:s},n=await this.client.messages.create(r);return this.mapResponse(n)}async*stream(e){let t=this.mapMessages(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r=this.client.messages.stream({model:this.config.model,max_tokens:e.maxTokens??this.config.maxTokens??4096,temperature:e.temperature??this.config.temperature,system:e.system?[{type:"text",text:e.system,cache_control:{type:"ephemeral"}}]:void 0,messages:t,tools:s});for await(let n of r)if(n.type==="content_block_delta")n.delta.type==="text_delta"?yield{type:"text_delta",text:n.delta.text}:n.delta.type==="input_json_delta"&&(yield{type:"tool_use_delta",toolCall:{input:n.delta.partial_json}});else if(n.type==="content_block_start")n.content_block.type==="tool_use"&&(yield{type:"tool_use_start",toolCall:{id:n.content_block.id,name:n.content_block.name}});else if(n.type==="message_stop"){let o=await r.finalMessage();yield{type:"message_complete",response:this.mapResponse(o)}}}isAvailable(){return!!this.config.apiKey}mapMessages(e){return e.map(t=>{if(typeof t.content=="string")return{role:t.role,content:t.content};let s=t.content.map(r=>{switch(r.type){case"text":return{type:"text",text:r.text};case"image":return{type:"image",source:{type:"base64",media_type:r.source.media_type,data:r.source.data}};case"tool_use":return{type:"tool_use",id:r.id,name:r.name,input:r.input};case"tool_result":return{type:"tool_result",tool_use_id:r.tool_use_id,content:r.content,is_error:r.is_error};default:return{type:"text",text:"[Unsupported block type]"}}}).filter(r=>r!==void 0);return{role:t.role,content:s}})}mapTools(e){let t=e.map(s=>({name:s.name,description:s.description,input_schema:s.inputSchema}));return t.length>0&&(t[t.length-1].cache_control={type:"ephemeral"}),t}mapResponse(e){let t="",s=[];for(let r of e.content)r.type==="text"?t+=r.text:r.type==="tool_use"&&s.push({id:r.id,name:r.name,input:r.input});return{content:t,toolCalls:s.length>0?s:void 0,usage:{inputTokens:e.usage.input_tokens,outputTokens:e.usage.output_tokens,cacheCreationTokens:e.usage.cache_creation_input_tokens??0,cacheReadTokens:e.usage.cache_read_input_tokens??0},stopReason:e.stop_reason}}}});import Tm from"openai";var Le,Ot=_(()=>{"use strict";Tt();Le=class extends Ie{static{m(this,"OpenAIProvider")}client;constructor(e){super(e)}async initialize(){this.client=new Tm({apiKey:this.config.apiKey,baseURL:this.config.baseUrl,maxRetries:5});let e=Ke(this.config.model);e&&(this.contextWindow=e)}async complete(e){let t=this.mapMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,...this.tokenLimitParam(e.maxTokens),temperature:this.safeTemperature(e.temperature),messages:t,...s?{tools:s}:{}},n=await this.client.chat.completions.create(r);return this.mapResponse(n)}async*stream(e){let t=this.mapMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r=await this.client.chat.completions.create({model:this.config.model,...this.tokenLimitParam(e.maxTokens),temperature:this.safeTemperature(e.temperature),messages:t,...s?{tools:s}:{},stream:!0}),n,o,i="",a="",c=[],d=null,u=0,p=0;for await(let g of r){let f=g.choices[0];if(!f)continue;let h=f.delta;if(h?.content&&(a+=h.content,yield{type:"text_delta",text:h.content}),h?.tool_calls)for(let y of h.tool_calls)if(y.id){if(n){let E;try{E=JSON.parse(i||"{}")}catch{E={}}c.push({id:n,name:o,input:E})}n=y.id,o=y.function?.name,i=y.function?.arguments??"",yield{type:"tool_use_start",toolCall:{id:n,name:o}}}else y.function?.arguments&&(i+=y.function.arguments,yield{type:"tool_use_delta",toolCall:{input:y.function.arguments}});f.finish_reason&&(d=f.finish_reason),g.usage&&(u=g.usage.prompt_tokens,p=g.usage.completion_tokens)}if(n){let g;try{g=JSON.parse(i||"{}")}catch{g={}}c.push({id:n,name:o,input:g})}yield{type:"message_complete",response:{content:a,toolCalls:c.length>0?c:void 0,usage:{inputTokens:u,outputTokens:p},stopReason:this.mapStopReason(d)}}}isAvailable(){return!!this.config.apiKey}async embed(e){try{let s=(await this.client.embeddings.create({model:"text-embedding-3-small",input:e})).data[0];return{embedding:s.embedding,model:"text-embedding-3-small",dimensions:s.embedding.length}}catch{return}}supportsEmbeddings(){return!0}isReasoningModel(){return/^(o[1-9]|gpt-5($|[.-][01]))/.test(this.config.model)}tokenLimitParam(e){let t=e??this.config.maxTokens??4096;return/^(gpt-5|o[1-9])/.test(this.config.model)?{max_completion_tokens:t}:{max_tokens:t}}safeTemperature(e){if(!this.isReasoningModel())return e??this.config.temperature}mapMessages(e,t){let s=[];t&&s.push({role:"system",content:t});for(let r of e){if(typeof r.content=="string"){s.push({role:r.role,content:r.content});continue}let n=[],o=[],i=[];for(let a of r.content)switch(a.type){case"text":n.push({type:"text",text:a.text});break;case"image":n.push({type:"image_url",image_url:{url:`data:${a.source.media_type};base64,${a.source.data}`}});break;case"tool_use":o.push({id:a.id,type:"function",function:{name:a.name,arguments:JSON.stringify(a.input)}});break;case"tool_result":i.push({tool_call_id:a.tool_use_id,content:a.content});break}if(r.role==="assistant"&&o.length>0){let a=n.map(c=>c.text).join("");s.push({role:"assistant",content:a||null,tool_calls:o})}else if(i.length>0)for(let a of i)s.push({role:"tool",tool_call_id:a.tool_call_id,content:a.content});else n.length>0&&(r.role==="user"?s.push({role:"user",content:n}):s.push({role:r.role,content:n.map(a=>a.text).join("")}))}return s}mapTools(e){return e.map(t=>({type:"function",function:{name:t.name,description:t.description,parameters:t.inputSchema}}))}mapResponse(e){let t=e.choices[0],s=t?.message,r=s?.content??"",n=s?.tool_calls?.map(o=>({id:o.id,name:o.function.name,input:(()=>{try{return JSON.parse(o.function.arguments)}catch{return{}}})()}));return{content:r,toolCalls:n&&n.length>0?n:void 0,usage:{inputTokens:e.usage?.prompt_tokens??0,outputTokens:e.usage?.completion_tokens??0},stopReason:this.mapStopReason(t?.finish_reason??null)}}mapStopReason(e){switch(e){case"stop":return"end_turn";case"tool_calls":return"tool_use";case"length":return"max_tokens";default:return"end_turn"}}}});var Ys,Uo=_(()=>{"use strict";Ot();Ys=class extends Le{static{m(this,"OpenRouterProvider")}constructor(e){super({...e,baseUrl:e.baseUrl??"https://openrouter.ai/api/v1"})}isAvailable(){return!!this.config.apiKey}supportsEmbeddings(){return!1}}});var Js,Fo=_(()=>{"use strict";Tt();Js=class extends Ie{static{m(this,"OllamaProvider")}baseUrl="";constructor(e){super(e)}apiKey="";async initialize(){let e=this.config.baseUrl??"http://localhost:11434";this.baseUrl=e.replace(/\/v1\/?$/,"").replace(/\/+$/,""),this.apiKey=this.config.apiKey??"";let t=Ke(this.config.model);t?this.contextWindow=t:await this.fetchModelContextWindow()}async fetchModelContextWindow(){try{let e=await fetch(`${this.baseUrl}/api/show`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:this.config.model})});if(!e.ok)return;let s=(await e.json()).model_info??{},r=Object.keys(s).find(o=>o.includes("context_length")||o==="num_ctx"),n=r?Number(s[r]):0;n>0&&(this.contextWindow={maxInputTokens:n,maxOutputTokens:Math.min(n,4096)})}catch{}}getHeaders(){let e={"Content-Type":"application/json"};return this.apiKey&&(e.Authorization=`Bearer ${this.apiKey}`),e}async complete(e){let t=this.buildMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,messages:t,stream:!1,options:this.buildOptions(e)};s&&s.length>0&&(r.tools=s);let n=await fetch(`${this.baseUrl}/api/chat`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(r)});if(!n.ok){let i=await n.text();throw new Error(`Ollama API error (${n.status}): ${i}`)}let o=await n.json();return this.mapResponse(o)}async*stream(e){let t=this.buildMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,messages:t,stream:!0,options:this.buildOptions(e)};s&&s.length>0&&(r.tools=s);let n=await fetch(`${this.baseUrl}/api/chat`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(r)});if(!n.ok){let g=await n.text();throw new Error(`Ollama API error (${n.status}): ${g}`)}if(!n.body)throw new Error("Ollama streaming response has no body");let o=n.body.getReader(),i=new TextDecoder,a="",c="",d=0,u=0,p=[];try{for(;;){let{done:g,value:f}=await o.read();if(g)break;a+=i.decode(f,{stream:!0});let h=a.split(`
372
- `);a=h.pop()??"";for(let y of h){let E=y.trim();if(!E)continue;let $;try{$=JSON.parse(E)}catch{continue}if($.message?.content&&(c+=$.message.content,yield{type:"text_delta",text:$.message.content}),$.message?.tool_calls)for(let v of $.message.tool_calls){let P={id:`ollama_tool_${p.length}`,name:v.function.name,input:v.function.arguments};p.push(P),yield{type:"tool_use_start",toolCall:{id:P.id,name:P.name}},yield{type:"tool_use_delta",toolCall:{input:P.input}}}$.done&&(d=$.prompt_eval_count??0,u=$.eval_count??0,yield{type:"message_complete",response:{content:c,toolCalls:p.length>0?p:void 0,usage:{inputTokens:d,outputTokens:u},stopReason:p.length>0?"tool_use":"end_turn"}})}}if(a.trim()){let g;try{g=JSON.parse(a.trim())}catch{return}if(g.message?.content&&(c+=g.message.content,yield{type:"text_delta",text:g.message.content}),g.message?.tool_calls)for(let f of g.message.tool_calls){let h={id:`ollama_tool_${p.length}`,name:f.function.name,input:f.function.arguments};p.push(h),yield{type:"tool_use_start",toolCall:{id:h.id,name:h.name}},yield{type:"tool_use_delta",toolCall:{input:h.input}}}g.done&&(d=g.prompt_eval_count??0,u=g.eval_count??0,yield{type:"message_complete",response:{content:c,toolCalls:p.length>0?p:void 0,usage:{inputTokens:d,outputTokens:u},stopReason:p.length>0?"tool_use":"end_turn"}})}}finally{o.releaseLock()}}isAvailable(){try{return this.baseUrl.length>0}catch{return!1}}async embed(e){try{let t=await fetch(`${this.baseUrl}/api/embed`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({model:"nomic-embed-text",input:e})});if(!t.ok)return;let s=await t.json();if(!s.embeddings||s.embeddings.length===0)return;let r=s.embeddings[0];return{embedding:r,model:"nomic-embed-text",dimensions:r.length}}catch{return}}supportsEmbeddings(){return!0}buildOptions(e){let t={},s=e.temperature??this.config.temperature;s!==void 0&&(t.temperature=s);let r=e.maxTokens??this.config.maxTokens;return r!==void 0&&(t.num_predict=r),t}buildMessages(e,t){let s=[];t&&s.push({role:"system",content:t});for(let r of e)typeof r.content=="string"?s.push({role:r.role,content:r.content}):s.push(this.mapContentBlocks(r.role,r.content));return s}mapContentBlocks(e,t){let s=[],r=[];for(let o of t)switch(o.type){case"text":s.push(o.text);break;case"image":r.push(o.source.data);break;case"tool_use":s.push(`[Tool call: ${o.name}(${JSON.stringify(o.input)})]`);break;case"tool_result":s.push(`[Tool result for ${o.tool_use_id}]: ${o.content}`);break}let n={role:e,content:s.join(`
373
- `)};return r.length>0&&(n.images=r),n}mapTools(e){return e.map(t=>({type:"function",function:{name:t.name,description:t.description,parameters:t.inputSchema}}))}mapResponse(e){let t=[];if(e.message.tool_calls)for(let s of e.message.tool_calls)t.push({id:`ollama_tool_${t.length}`,name:s.function.name,input:s.function.arguments});return{content:e.message.content,toolCalls:t.length>0?t:void 0,usage:{inputTokens:e.prompt_eval_count??0,outputTokens:e.eval_count??0},stopReason:t.length>0?"tool_use":"end_turn"}}}});var Zs,jo=_(()=>{"use strict";Ot();Zs=class extends Le{static{m(this,"OpenWebUIProvider")}constructor(e){super({...e,apiKey:e.apiKey||"openwebui",baseUrl:e.baseUrl??"http://localhost:3000/api/v1"})}isAvailable(){return!0}supportsEmbeddings(){return!1}}});import{GoogleGenAI as _m}from"@google/genai";var Qs,Bo=_(()=>{"use strict";Tt();Qs=class extends Ie{static{m(this,"GoogleProvider")}client;rawContentCache=new Map;constructor(e){super(e)}async initialize(){this.client=new _m({apiKey:this.config.apiKey});let e=Ke(this.config.model);e&&(this.contextWindow=e)}async complete(e){let t=this.mapContents(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r=await this.client.models.generateContent({model:this.config.model,contents:t,config:{systemInstruction:e.system,...s?{tools:[{functionDeclarations:s}]}:{},temperature:e.temperature??this.config.temperature,maxOutputTokens:e.maxTokens??this.config.maxTokens??4096}});return this.cacheRawContent(r),this.mapResponse(r)}async*stream(e){let t=this.mapContents(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r=await this.client.models.generateContentStream({model:this.config.model,contents:t,config:{systemInstruction:e.system,...s?{tools:[{functionDeclarations:s}]}:{},temperature:e.temperature??this.config.temperature,maxOutputTokens:e.maxTokens??this.config.maxTokens??4096}}),n="",o=[],i=0,a=0,c;for await(let d of r){c=d;let u=d.text;if(u&&(n+=u,yield{type:"text_delta",text:u}),d.functionCalls)for(let p of d.functionCalls){let g={id:p.id??`google_tool_${o.length}`,name:p.name,input:p.args??{}};o.push(g),yield{type:"tool_use_start",toolCall:{id:g.id,name:g.name}}}d.usageMetadata&&(i=d.usageMetadata.promptTokenCount??0,a=d.usageMetadata.candidatesTokenCount??0)}c&&this.cacheRawContent(c),yield{type:"message_complete",response:{content:n,toolCalls:o.length>0?o:void 0,usage:{inputTokens:i,outputTokens:a},stopReason:o.length>0?"tool_use":"end_turn"}}}isAvailable(){return!!this.config.apiKey}supportsEmbeddings(){return!1}cacheRawContent(e){let t=e.candidates?.[0]?.content;if(!t?.parts)return;let s=t.parts.filter(n=>n.functionCall);if(s.length===0)return;let r=this.buildCacheKey(s);if(this.rawContentCache.set(r,t),this.rawContentCache.size>20){let n=this.rawContentCache.keys().next().value;n&&this.rawContentCache.delete(n)}}buildCacheKey(e){return e.map(t=>`${t.functionCall.id??""}:${t.functionCall.name}`).sort().join("|")}buildCacheKeyFromBlocks(e){return e.map(t=>`${t.id}:${t.name}`).sort().join("|")}mapContents(e){let t=new Map;for(let r of e)if(!(r.role!=="assistant"||typeof r.content=="string"))for(let n of r.content)n.type==="tool_use"&&t.set(n.id,n.name);let s=[];for(let r of e){let n=r.role==="assistant"?"model":"user";if(typeof r.content=="string"){s.push({role:n,parts:[{text:r.content}]});continue}let o=[],i=[],a=[];for(let c of r.content)switch(c.type){case"text":i.push({text:c.text});break;case"image":i.push({inlineData:{mimeType:c.source.media_type,data:c.source.data}});break;case"tool_use":o.push({id:c.id,name:c.name,input:c.input});break;case"tool_result":{let d=t.get(c.tool_use_id)??c.tool_use_id,u;try{let p=JSON.parse(c.content);u=typeof p=="object"&&p!==null?p:{result:p}}catch{u={result:c.content}}a.push({functionResponse:{id:c.tool_use_id,name:d,response:u}});break}}if(r.role==="assistant"&&o.length>0){let c=this.buildCacheKeyFromBlocks(o),d=this.rawContentCache.get(c);if(d)s.push(d);else{let u=[...i];for(let p=0;p<o.length;p++){let g=o[p];u.push({functionCall:{id:g.id,name:g.name,args:g.input},...p===0?{thoughtSignature:"skip_thought_signature_validator"}:{}})}s.push({role:"model",parts:u})}}else i.length>0&&s.push({role:n,parts:i});a.length>0&&s.push({role:"user",parts:a})}return this.sanitizeContents(s)}sanitizeContents(e){let t=new Set,s=[];for(let i of e)if(i.role==="model"){for(let a of i.parts??[])if(a.functionCall){let c=a.functionCall.id??a.functionCall.name??"";c&&t.add(c)}s.push(i)}else{let a=(i.parts??[]).filter(c=>{if(c.functionResponse){let d=c.functionResponse.id??c.functionResponse.name??"";return d?t.has(d):!1}return!0});a.length>0&&s.push({role:i.role,parts:a})}let r=new Set;for(let i of s)if(i.role!=="model"){for(let a of i.parts??[])if(a.functionResponse){let c=a.functionResponse.id??a.functionResponse.name??"";c&&r.add(c)}}let n=[];for(let i of s)if(i.role==="model"){let a=(i.parts??[]).filter(c=>{if(c.functionCall){let d=c.functionCall.id??c.functionCall.name??"";return d?r.has(d):!1}return!0});a.length>0&&n.push({role:i.role,parts:a})}else n.push(i);if(n.length<=1)return n;let o=[n[0]];for(let i=1;i<n.length;i++){let a=o[o.length-1],c=n[i];a.role===c.role?a.parts=[...a.parts??[],...c.parts??[]]:o.push(c)}return o}mapTools(e){return e.map(t=>({name:t.name,description:t.description,parameters:t.inputSchema}))}mapResponse(e){let t=e.text??"",r=e.functionCalls?.map((n,o)=>({id:n.id??`google_tool_${o}`,name:n.name,input:n.args??{}}));return{content:t,toolCalls:r&&r.length>0?r:void 0,usage:{inputTokens:e.usageMetadata?.promptTokenCount??0,outputTokens:e.usageMetadata?.candidatesTokenCount??0},stopReason:r&&r.length>0?"tool_use":"end_turn"}}}});var er,Ho=_(()=>{"use strict";Ot();er=class extends Le{static{m(this,"MistralProvider")}constructor(e){super({...e,baseUrl:e.baseUrl??"https://api.mistral.ai/v1/"})}isAvailable(){return!!this.config.apiKey}supportsEmbeddings(){return!1}}});function qo(l){switch(l.provider){case"anthropic":return new Xs(l);case"openai":return new Le(l);case"openrouter":return new Ys(l);case"ollama":return new Js(l);case"openwebui":return new Zs(l);case"google":return new Qs(l);case"mistral":return new er(l);default:throw new Error(`Unknown LLM provider: ${l.provider}`)}}var zo=_(()=>{"use strict";Po();Ot();Uo();Fo();jo();Bo();Ho();m(qo,"createLLMProvider")});function Wo(l,e){return new on(l,e)}var bm,on,rc=_(()=>{"use strict";Tt();zo();bm=["default","strong","fast","embeddings","local"],on=class extends Ie{static{m(this,"ModelRouter")}providers=new Map;multiConfig;logger;constructor(e,t){super(e.default),this.multiConfig=e,this.logger=t}async initialize(){for(let e of bm){let t=this.multiConfig[e];if(t){let s=qo(t);await s.initialize(),this.providers.set(e,s),this.logger?.info({tier:e,provider:t.provider,model:t.model},"LLM tier initialized")}}}resolve(e){return e&&this.providers.has(e)?{provider:this.providers.get(e),resolvedTier:e}:{provider:this.providers.get("default"),resolvedTier:"default"}}async complete(e){let{provider:t,resolvedTier:s}=this.resolve(e.tier),r=this.multiConfig[s];this.logger?.debug({requestedTier:e.tier??"default",resolvedTier:s,model:r?.model},"LLM routing request");let n=await t.complete(e);return this.logger?.info({tier:s,model:r?.model,inputTokens:n.usage?.inputTokens,outputTokens:n.usage?.outputTokens},"LLM call completed"),n}async*stream(e){let{provider:t}=this.resolve(e.tier);yield*t.stream(e)}async embed(e){return(this.providers.get("embeddings")??this.resolve().provider).embed(e)}supportsEmbeddings(){return(this.providers.get("embeddings")??this.resolve().provider).supportsEmbeddings()}isAvailable(){return this.resolve().provider.isAvailable()}getContextWindow(){return this.resolve().provider.getContextWindow()}};m(Wo,"createModelRouter")});function Ne(l){return Math.ceil(l.length/3.5)}function _t(l){if(typeof l.content=="string")return Ne(l.content)+4;let e=4;for(let t of l.content)switch(t.type){case"text":e+=Ne(t.text);break;case"image":e+=1e3;break;case"tool_use":e+=Ne(t.name)+Ne(JSON.stringify(t.input));break;case"tool_result":e+=Ne(t.content);break}return e}function Go(l,e){let t=e?.keepRecentPairs??3,s=e?.maxTrimmedLength??120,r=e?.minContentLength??300,n=new Map;for(let a of l)if(a.role==="assistant"&&Array.isArray(a.content))for(let c of a.content)c.type==="tool_use"&&n.set(c.id,c.name);let o=[];for(let a=0;a<l.length;a++){let c=l[a];c.role==="user"&&Array.isArray(c.content)&&c.content.some(d=>d.type==="tool_result")&&o.push(a)}let i=new Set(o.slice(-t));return l.map((a,c)=>{if(i.has(c)||a.role!=="user"||!Array.isArray(a.content))return a;let d=!1,u=a.content.map(p=>{if(p.type!=="tool_result")return p;let g=p.content;if(typeof g!="string"||g.length<r)return p;d=!0;let f=n.get(p.tool_use_id)||"unknown",h=g.split(`
374
- `)[0].slice(0,s),y=p.is_error?"Fehler":"Ergebnis";return{...p,content:`[${y}: ${f} \u2014 ${h}]`}});return d?{...a,content:u}:a})}var tr,nc=_(()=>{"use strict";m(Ne,"estimateTokens");m(_t,"estimateMessageTokens");m(Go,"trimOldToolResults");tr=class{static{m(this,"PromptBuilder")}buildSystemPrompt(e={}){let{memories:t,skills:s,userProfile:r,todayEvents:n}=e,o=process.platform==="darwin"?"macOS":process.platform==="win32"?"Windows":"Linux",i=process.env.HOME||process.env.USERPROFILE||"~",a=`You are Alfred, a personal AI assistant. You run on ${o} (home: ${i}).
380
+ `).run(t.lastCheckedAt,t.lastValue,e)}findByChatId(e,t){return this.db.prepare("SELECT * FROM watches WHERE chat_id = ? AND platform = ? ORDER BY created_at DESC").all(e,t).map(r=>this.mapRow(r))}toggle(e,t){return this.db.prepare("UPDATE watches SET enabled = ? WHERE id = ?").run(t?1:0,e).changes>0}delete(e){return this.db.prepare("DELETE FROM watches WHERE id = ?").run(e).changes>0}getById(e){let t=this.db.prepare("SELECT * FROM watches WHERE id = ?").get(e);return t?this.mapRow(t):void 0}mapRow(e){let t={};try{t=JSON.parse(e.skill_params)}catch{}let s={field:e.condition_field,operator:e.condition_operator};if(e.condition_value!=null){let r=Number(e.condition_value);s.value=isNaN(r)?e.condition_value:r}return{id:e.id,chatId:e.chat_id,platform:e.platform,name:e.name,skillName:e.skill_name,skillParams:t,condition:s,intervalMinutes:e.interval_minutes,cooldownMinutes:e.cooldown_minutes,enabled:e.enabled===1,lastCheckedAt:e.last_checked_at??null,lastTriggeredAt:e.last_triggered_at??null,lastValue:e.last_value??null,createdAt:e.created_at,messageTemplate:e.message_template}}}});var Zs,ic=_(()=>{"use strict";Zs=class{static{m(this,"SummaryRepository")}db;constructor(e){this.db=e}get(e){let t=this.db.prepare("SELECT conversation_id, summary, message_count, last_user_message, last_assistant_message, updated_at FROM conversation_summaries WHERE conversation_id = ?").get(e);if(t)return{conversationId:t.conversation_id,summary:t.summary,messageCount:t.message_count,lastUserMessage:t.last_user_message,lastAssistantMessage:t.last_assistant_message,updatedAt:t.updated_at}}upsert(e){this.db.prepare(`
381
+ INSERT INTO conversation_summaries (conversation_id, summary, message_count, last_user_message, last_assistant_message, updated_at)
382
+ VALUES (?, ?, ?, ?, ?, ?)
383
+ ON CONFLICT(conversation_id) DO UPDATE SET
384
+ summary = excluded.summary,
385
+ message_count = excluded.message_count,
386
+ last_user_message = excluded.last_user_message,
387
+ last_assistant_message = excluded.last_assistant_message,
388
+ updated_at = excluded.updated_at
389
+ `).run(e.conversationId,e.summary,e.messageCount,e.lastUserMessage??null,e.lastAssistantMessage??null,e.updatedAt)}delete(e){this.db.prepare("DELETE FROM conversation_summaries WHERE conversation_id = ?").run(e)}}});var Po=_(()=>{"use strict";qa();Ga();Va();Ka();Xa();an();Oo();Ya();Ja();Za();Qa();ec();tc();rc();nc();oc();ic()});function Xe(l){if(Uo[l])return Uo[l];let e=Object.entries(Uo).sort((t,s)=>s[0].length-t[0].length);for(let[t,s]of e)if(l.startsWith(t))return s}var Uo,bm,Ae,_t=_(()=>{"use strict";Uo={"claude-opus-4-20250514":{maxInputTokens:2e5,maxOutputTokens:32e3},"claude-opus-4-5-20251101":{maxInputTokens:2e5,maxOutputTokens:32e3},"claude-sonnet-4-20250514":{maxInputTokens:2e5,maxOutputTokens:16e3},"claude-sonnet-4-5-20250929":{maxInputTokens:2e5,maxOutputTokens:16e3},"claude-3-5-sonnet-20241022":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-3-5-sonnet-20240620":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-3-5-haiku-20241022":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-haiku-3-5-20241022":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-haiku-4-5-20251001":{maxInputTokens:2e5,maxOutputTokens:8192},"claude-3-opus-20240229":{maxInputTokens:2e5,maxOutputTokens:4096},"claude-3-sonnet-20240229":{maxInputTokens:2e5,maxOutputTokens:4096},"claude-3-haiku-20240307":{maxInputTokens:2e5,maxOutputTokens:4096},"claude-":{maxInputTokens:2e5,maxOutputTokens:16e3},"gpt-4.1":{maxInputTokens:1047576,maxOutputTokens:32768},"gpt-4.1-mini":{maxInputTokens:1047576,maxOutputTokens:32768},"gpt-4.1-nano":{maxInputTokens:1047576,maxOutputTokens:32768},"gpt-4o":{maxInputTokens:128e3,maxOutputTokens:16384},"gpt-4o-mini":{maxInputTokens:128e3,maxOutputTokens:16384},"gpt-4-turbo":{maxInputTokens:128e3,maxOutputTokens:4096},"gpt-4":{maxInputTokens:8192,maxOutputTokens:4096},"gpt-3.5-turbo":{maxInputTokens:16384,maxOutputTokens:4096},o3:{maxInputTokens:2e5,maxOutputTokens:1e5},"o3-mini":{maxInputTokens:2e5,maxOutputTokens:1e5},"o4-mini":{maxInputTokens:2e5,maxOutputTokens:1e5},o1:{maxInputTokens:2e5,maxOutputTokens:1e5},"o1-mini":{maxInputTokens:128e3,maxOutputTokens:65536},"gpt-5.4":{maxInputTokens:105e4,maxOutputTokens:128e3},"gpt-5":{maxInputTokens:4e5,maxOutputTokens:128e3},"gemini-3.1-pro":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-3.1-flash":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-3-pro":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-3-flash":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-2.5-pro":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-2.5-flash":{maxInputTokens:1048576,maxOutputTokens:65536},"gemini-2.0-flash":{maxInputTokens:1048576,maxOutputTokens:8192},"gemini-2.0-pro":{maxInputTokens:1048576,maxOutputTokens:8192},"gemini-1.5-pro":{maxInputTokens:2097152,maxOutputTokens:8192},"gemini-1.5-flash":{maxInputTokens:1048576,maxOutputTokens:8192},"mistral-large":{maxInputTokens:262144,maxOutputTokens:262144},"mistral-medium":{maxInputTokens:128e3,maxOutputTokens:128e3},"mistral-small":{maxInputTokens:128e3,maxOutputTokens:128e3},codestral:{maxInputTokens:262144,maxOutputTokens:262144},"magistral-medium":{maxInputTokens:128e3,maxOutputTokens:131072},"magistral-small":{maxInputTokens:128e3,maxOutputTokens:131072},ministral:{maxInputTokens:128e3,maxOutputTokens:128e3},llama4:{maxInputTokens:128e3,maxOutputTokens:4096},"llama3.2":{maxInputTokens:128e3,maxOutputTokens:4096},"llama3.1":{maxInputTokens:128e3,maxOutputTokens:4096},llama3:{maxInputTokens:8192,maxOutputTokens:4096},gemma3:{maxInputTokens:128e3,maxOutputTokens:8192},gemma2:{maxInputTokens:8192,maxOutputTokens:4096},qwen3:{maxInputTokens:128e3,maxOutputTokens:8192},"qwen2.5":{maxInputTokens:128e3,maxOutputTokens:4096},mixtral:{maxInputTokens:32e3,maxOutputTokens:4096},phi3:{maxInputTokens:128e3,maxOutputTokens:4096},phi4:{maxInputTokens:128e3,maxOutputTokens:4096},"deepseek-r1":{maxInputTokens:128e3,maxOutputTokens:8192},"deepseek-v3":{maxInputTokens:128e3,maxOutputTokens:8192},"deepseek-chat":{maxInputTokens:128e3,maxOutputTokens:8192},"command-r":{maxInputTokens:128e3,maxOutputTokens:4096},"command-r-plus":{maxInputTokens:128e3,maxOutputTokens:4096}},bm={maxInputTokens:128e3,maxOutputTokens:8192};m(Xe,"lookupContextWindow");Ae=class{static{m(this,"LLMProvider")}config;contextWindow=bm;constructor(e){this.config=e}getContextWindow(){return this.contextWindow}async embed(e){}supportsEmbeddings(){return!1}}});import Em from"@anthropic-ai/sdk";var Qs,Fo=_(()=>{"use strict";_t();Qs=class extends Ae{static{m(this,"AnthropicProvider")}client;constructor(e){super(e)}async initialize(){this.client=new Em({apiKey:this.config.apiKey,maxRetries:5});let e=Xe(this.config.model);e&&(this.contextWindow=e)}async complete(e){let t=this.mapMessages(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,max_tokens:e.maxTokens??this.config.maxTokens??4096,temperature:e.temperature??this.config.temperature,system:e.system?[{type:"text",text:e.system,cache_control:{type:"ephemeral"}}]:void 0,messages:t,tools:s},n=await this.client.messages.create(r);return this.mapResponse(n)}async*stream(e){let t=this.mapMessages(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r=this.client.messages.stream({model:this.config.model,max_tokens:e.maxTokens??this.config.maxTokens??4096,temperature:e.temperature??this.config.temperature,system:e.system?[{type:"text",text:e.system,cache_control:{type:"ephemeral"}}]:void 0,messages:t,tools:s});for await(let n of r)if(n.type==="content_block_delta")n.delta.type==="text_delta"?yield{type:"text_delta",text:n.delta.text}:n.delta.type==="input_json_delta"&&(yield{type:"tool_use_delta",toolCall:{input:n.delta.partial_json}});else if(n.type==="content_block_start")n.content_block.type==="tool_use"&&(yield{type:"tool_use_start",toolCall:{id:n.content_block.id,name:n.content_block.name}});else if(n.type==="message_stop"){let o=await r.finalMessage();yield{type:"message_complete",response:this.mapResponse(o)}}}isAvailable(){return!!this.config.apiKey}mapMessages(e){return e.map(t=>{if(typeof t.content=="string")return{role:t.role,content:t.content};let s=t.content.map(r=>{switch(r.type){case"text":return{type:"text",text:r.text};case"image":return{type:"image",source:{type:"base64",media_type:r.source.media_type,data:r.source.data}};case"tool_use":return{type:"tool_use",id:r.id,name:r.name,input:r.input};case"tool_result":return{type:"tool_result",tool_use_id:r.tool_use_id,content:r.content,is_error:r.is_error};default:return{type:"text",text:"[Unsupported block type]"}}}).filter(r=>r!==void 0);return{role:t.role,content:s}})}mapTools(e){let t=e.map(s=>({name:s.name,description:s.description,input_schema:s.inputSchema}));return t.length>0&&(t[t.length-1].cache_control={type:"ephemeral"}),t}mapResponse(e){let t="",s=[];for(let r of e.content)r.type==="text"?t+=r.text:r.type==="tool_use"&&s.push({id:r.id,name:r.name,input:r.input});return{content:t,toolCalls:s.length>0?s:void 0,usage:{inputTokens:e.usage.input_tokens,outputTokens:e.usage.output_tokens,cacheCreationTokens:e.usage.cache_creation_input_tokens??0,cacheReadTokens:e.usage.cache_read_input_tokens??0},stopReason:e.stop_reason}}}});import $m from"openai";var Me,Ut=_(()=>{"use strict";_t();Me=class extends Ae{static{m(this,"OpenAIProvider")}client;constructor(e){super(e)}async initialize(){this.client=new $m({apiKey:this.config.apiKey,baseURL:this.config.baseUrl,maxRetries:5});let e=Xe(this.config.model);e&&(this.contextWindow=e)}async complete(e){let t=this.mapMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,...this.tokenLimitParam(e.maxTokens),temperature:this.safeTemperature(e.temperature),messages:t,...s?{tools:s}:{}},n=await this.client.chat.completions.create(r);return this.mapResponse(n)}async*stream(e){let t=this.mapMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r=await this.client.chat.completions.create({model:this.config.model,...this.tokenLimitParam(e.maxTokens),temperature:this.safeTemperature(e.temperature),messages:t,...s?{tools:s}:{},stream:!0}),n,o,i="",a="",c=[],d=null,u=0,p=0;for await(let f of r){let g=f.choices[0];if(!g)continue;let h=g.delta;if(h?.content&&(a+=h.content,yield{type:"text_delta",text:h.content}),h?.tool_calls)for(let y of h.tool_calls)if(y.id){if(n){let E;try{E=JSON.parse(i||"{}")}catch{E={}}c.push({id:n,name:o,input:E})}n=y.id,o=y.function?.name,i=y.function?.arguments??"",yield{type:"tool_use_start",toolCall:{id:n,name:o}}}else y.function?.arguments&&(i+=y.function.arguments,yield{type:"tool_use_delta",toolCall:{input:y.function.arguments}});g.finish_reason&&(d=g.finish_reason),f.usage&&(u=f.usage.prompt_tokens,p=f.usage.completion_tokens)}if(n){let f;try{f=JSON.parse(i||"{}")}catch{f={}}c.push({id:n,name:o,input:f})}yield{type:"message_complete",response:{content:a,toolCalls:c.length>0?c:void 0,usage:{inputTokens:u,outputTokens:p},stopReason:this.mapStopReason(d)}}}isAvailable(){return!!this.config.apiKey}async embed(e){try{let s=(await this.client.embeddings.create({model:"text-embedding-3-small",input:e})).data[0];return{embedding:s.embedding,model:"text-embedding-3-small",dimensions:s.embedding.length}}catch{return}}supportsEmbeddings(){return!0}isReasoningModel(){return/^(o[1-9]|gpt-5($|[.-][01]))/.test(this.config.model)}tokenLimitParam(e){let t=e??this.config.maxTokens??4096;return/^(gpt-5|o[1-9])/.test(this.config.model)?{max_completion_tokens:t}:{max_tokens:t}}safeTemperature(e){if(!this.isReasoningModel())return e??this.config.temperature}mapMessages(e,t){let s=[];t&&s.push({role:"system",content:t});for(let r of e){if(typeof r.content=="string"){s.push({role:r.role,content:r.content});continue}let n=[],o=[],i=[];for(let a of r.content)switch(a.type){case"text":n.push({type:"text",text:a.text});break;case"image":n.push({type:"image_url",image_url:{url:`data:${a.source.media_type};base64,${a.source.data}`}});break;case"tool_use":o.push({id:a.id,type:"function",function:{name:a.name,arguments:JSON.stringify(a.input)}});break;case"tool_result":i.push({tool_call_id:a.tool_use_id,content:a.content});break}if(r.role==="assistant"&&o.length>0){let a=n.map(c=>c.text).join("");s.push({role:"assistant",content:a||null,tool_calls:o})}else if(i.length>0)for(let a of i)s.push({role:"tool",tool_call_id:a.tool_call_id,content:a.content});else n.length>0&&(r.role==="user"?s.push({role:"user",content:n}):s.push({role:r.role,content:n.map(a=>a.text).join("")}))}return s}mapTools(e){return e.map(t=>({type:"function",function:{name:t.name,description:t.description,parameters:t.inputSchema}}))}mapResponse(e){let t=e.choices[0],s=t?.message,r=s?.content??"",n=s?.tool_calls?.map(o=>({id:o.id,name:o.function.name,input:(()=>{try{return JSON.parse(o.function.arguments)}catch{return{}}})()}));return{content:r,toolCalls:n&&n.length>0?n:void 0,usage:{inputTokens:e.usage?.prompt_tokens??0,outputTokens:e.usage?.completion_tokens??0},stopReason:this.mapStopReason(t?.finish_reason??null)}}mapStopReason(e){switch(e){case"stop":return"end_turn";case"tool_calls":return"tool_use";case"length":return"max_tokens";default:return"end_turn"}}}});var er,jo=_(()=>{"use strict";Ut();er=class extends Me{static{m(this,"OpenRouterProvider")}constructor(e){super({...e,baseUrl:e.baseUrl??"https://openrouter.ai/api/v1"})}isAvailable(){return!!this.config.apiKey}supportsEmbeddings(){return!1}}});var tr,Bo=_(()=>{"use strict";_t();tr=class extends Ae{static{m(this,"OllamaProvider")}baseUrl="";constructor(e){super(e)}apiKey="";async initialize(){let e=this.config.baseUrl??"http://localhost:11434";this.baseUrl=e.replace(/\/v1\/?$/,"").replace(/\/+$/,""),this.apiKey=this.config.apiKey??"";let t=Xe(this.config.model);t?this.contextWindow=t:await this.fetchModelContextWindow()}async fetchModelContextWindow(){try{let e=await fetch(`${this.baseUrl}/api/show`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:this.config.model})});if(!e.ok)return;let s=(await e.json()).model_info??{},r=Object.keys(s).find(o=>o.includes("context_length")||o==="num_ctx"),n=r?Number(s[r]):0;n>0&&(this.contextWindow={maxInputTokens:n,maxOutputTokens:Math.min(n,4096)})}catch{}}getHeaders(){let e={"Content-Type":"application/json"};return this.apiKey&&(e.Authorization=`Bearer ${this.apiKey}`),e}async complete(e){let t=this.buildMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,messages:t,stream:!1,options:this.buildOptions(e)};s&&s.length>0&&(r.tools=s);let n=await fetch(`${this.baseUrl}/api/chat`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(r)});if(!n.ok){let i=await n.text();throw new Error(`Ollama API error (${n.status}): ${i}`)}let o=await n.json();return this.mapResponse(o)}async*stream(e){let t=this.buildMessages(e.messages,e.system),s=e.tools?this.mapTools(e.tools):void 0,r={model:this.config.model,messages:t,stream:!0,options:this.buildOptions(e)};s&&s.length>0&&(r.tools=s);let n=await fetch(`${this.baseUrl}/api/chat`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(r)});if(!n.ok){let f=await n.text();throw new Error(`Ollama API error (${n.status}): ${f}`)}if(!n.body)throw new Error("Ollama streaming response has no body");let o=n.body.getReader(),i=new TextDecoder,a="",c="",d=0,u=0,p=[];try{for(;;){let{done:f,value:g}=await o.read();if(f)break;a+=i.decode(g,{stream:!0});let h=a.split(`
390
+ `);a=h.pop()??"";for(let y of h){let E=y.trim();if(!E)continue;let $;try{$=JSON.parse(E)}catch{continue}if($.message?.content&&(c+=$.message.content,yield{type:"text_delta",text:$.message.content}),$.message?.tool_calls)for(let v of $.message.tool_calls){let j={id:`ollama_tool_${p.length}`,name:v.function.name,input:v.function.arguments};p.push(j),yield{type:"tool_use_start",toolCall:{id:j.id,name:j.name}},yield{type:"tool_use_delta",toolCall:{input:j.input}}}$.done&&(d=$.prompt_eval_count??0,u=$.eval_count??0,yield{type:"message_complete",response:{content:c,toolCalls:p.length>0?p:void 0,usage:{inputTokens:d,outputTokens:u},stopReason:p.length>0?"tool_use":"end_turn"}})}}if(a.trim()){let f;try{f=JSON.parse(a.trim())}catch{return}if(f.message?.content&&(c+=f.message.content,yield{type:"text_delta",text:f.message.content}),f.message?.tool_calls)for(let g of f.message.tool_calls){let h={id:`ollama_tool_${p.length}`,name:g.function.name,input:g.function.arguments};p.push(h),yield{type:"tool_use_start",toolCall:{id:h.id,name:h.name}},yield{type:"tool_use_delta",toolCall:{input:h.input}}}f.done&&(d=f.prompt_eval_count??0,u=f.eval_count??0,yield{type:"message_complete",response:{content:c,toolCalls:p.length>0?p:void 0,usage:{inputTokens:d,outputTokens:u},stopReason:p.length>0?"tool_use":"end_turn"}})}}finally{o.releaseLock()}}isAvailable(){try{return this.baseUrl.length>0}catch{return!1}}async embed(e){try{let t=await fetch(`${this.baseUrl}/api/embed`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({model:"nomic-embed-text",input:e})});if(!t.ok)return;let s=await t.json();if(!s.embeddings||s.embeddings.length===0)return;let r=s.embeddings[0];return{embedding:r,model:"nomic-embed-text",dimensions:r.length}}catch{return}}supportsEmbeddings(){return!0}buildOptions(e){let t={},s=e.temperature??this.config.temperature;s!==void 0&&(t.temperature=s);let r=e.maxTokens??this.config.maxTokens;return r!==void 0&&(t.num_predict=r),t}buildMessages(e,t){let s=[];t&&s.push({role:"system",content:t});for(let r of e)typeof r.content=="string"?s.push({role:r.role,content:r.content}):s.push(this.mapContentBlocks(r.role,r.content));return s}mapContentBlocks(e,t){let s=[],r=[];for(let o of t)switch(o.type){case"text":s.push(o.text);break;case"image":r.push(o.source.data);break;case"tool_use":s.push(`[Tool call: ${o.name}(${JSON.stringify(o.input)})]`);break;case"tool_result":s.push(`[Tool result for ${o.tool_use_id}]: ${o.content}`);break}let n={role:e,content:s.join(`
391
+ `)};return r.length>0&&(n.images=r),n}mapTools(e){return e.map(t=>({type:"function",function:{name:t.name,description:t.description,parameters:t.inputSchema}}))}mapResponse(e){let t=[];if(e.message.tool_calls)for(let s of e.message.tool_calls)t.push({id:`ollama_tool_${t.length}`,name:s.function.name,input:s.function.arguments});return{content:e.message.content,toolCalls:t.length>0?t:void 0,usage:{inputTokens:e.prompt_eval_count??0,outputTokens:e.eval_count??0},stopReason:t.length>0?"tool_use":"end_turn"}}}});var sr,Ho=_(()=>{"use strict";Ut();sr=class extends Me{static{m(this,"OpenWebUIProvider")}constructor(e){super({...e,apiKey:e.apiKey||"openwebui",baseUrl:e.baseUrl??"http://localhost:3000/api/v1"})}isAvailable(){return!0}supportsEmbeddings(){return!1}}});import{GoogleGenAI as km}from"@google/genai";var rr,zo=_(()=>{"use strict";_t();rr=class extends Ae{static{m(this,"GoogleProvider")}client;rawContentCache=new Map;constructor(e){super(e)}async initialize(){this.client=new km({apiKey:this.config.apiKey});let e=Xe(this.config.model);e&&(this.contextWindow=e)}async complete(e){let t=this.mapContents(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r=await this.client.models.generateContent({model:this.config.model,contents:t,config:{systemInstruction:e.system,...s?{tools:[{functionDeclarations:s}]}:{},temperature:e.temperature??this.config.temperature,maxOutputTokens:e.maxTokens??this.config.maxTokens??4096}});return this.cacheRawContent(r),this.mapResponse(r)}async*stream(e){let t=this.mapContents(e.messages),s=e.tools?this.mapTools(e.tools):void 0,r=await this.client.models.generateContentStream({model:this.config.model,contents:t,config:{systemInstruction:e.system,...s?{tools:[{functionDeclarations:s}]}:{},temperature:e.temperature??this.config.temperature,maxOutputTokens:e.maxTokens??this.config.maxTokens??4096}}),n="",o=[],i=0,a=0,c;for await(let d of r){c=d;let u=d.text;if(u&&(n+=u,yield{type:"text_delta",text:u}),d.functionCalls)for(let p of d.functionCalls){let f={id:p.id??`google_tool_${o.length}`,name:p.name,input:p.args??{}};o.push(f),yield{type:"tool_use_start",toolCall:{id:f.id,name:f.name}}}d.usageMetadata&&(i=d.usageMetadata.promptTokenCount??0,a=d.usageMetadata.candidatesTokenCount??0)}c&&this.cacheRawContent(c),yield{type:"message_complete",response:{content:n,toolCalls:o.length>0?o:void 0,usage:{inputTokens:i,outputTokens:a},stopReason:o.length>0?"tool_use":"end_turn"}}}isAvailable(){return!!this.config.apiKey}supportsEmbeddings(){return!1}cacheRawContent(e){let t=e.candidates?.[0]?.content;if(!t?.parts)return;let s=t.parts.filter(n=>n.functionCall);if(s.length===0)return;let r=this.buildCacheKey(s);if(this.rawContentCache.set(r,t),this.rawContentCache.size>20){let n=this.rawContentCache.keys().next().value;n&&this.rawContentCache.delete(n)}}buildCacheKey(e){return e.map(t=>`${t.functionCall.id??""}:${t.functionCall.name}`).sort().join("|")}buildCacheKeyFromBlocks(e){return e.map(t=>`${t.id}:${t.name}`).sort().join("|")}mapContents(e){let t=new Map;for(let r of e)if(!(r.role!=="assistant"||typeof r.content=="string"))for(let n of r.content)n.type==="tool_use"&&t.set(n.id,n.name);let s=[];for(let r of e){let n=r.role==="assistant"?"model":"user";if(typeof r.content=="string"){s.push({role:n,parts:[{text:r.content}]});continue}let o=[],i=[],a=[];for(let c of r.content)switch(c.type){case"text":i.push({text:c.text});break;case"image":i.push({inlineData:{mimeType:c.source.media_type,data:c.source.data}});break;case"tool_use":o.push({id:c.id,name:c.name,input:c.input});break;case"tool_result":{let d=t.get(c.tool_use_id)??c.tool_use_id,u;try{let p=JSON.parse(c.content);u=typeof p=="object"&&p!==null?p:{result:p}}catch{u={result:c.content}}a.push({functionResponse:{id:c.tool_use_id,name:d,response:u}});break}}if(r.role==="assistant"&&o.length>0){let c=this.buildCacheKeyFromBlocks(o),d=this.rawContentCache.get(c);if(d)s.push(d);else{let u=[...i];for(let p=0;p<o.length;p++){let f=o[p];u.push({functionCall:{id:f.id,name:f.name,args:f.input},...p===0?{thoughtSignature:"skip_thought_signature_validator"}:{}})}s.push({role:"model",parts:u})}}else i.length>0&&s.push({role:n,parts:i});a.length>0&&s.push({role:"user",parts:a})}return this.sanitizeContents(s)}sanitizeContents(e){let t=new Set,s=[];for(let i of e)if(i.role==="model"){for(let a of i.parts??[])if(a.functionCall){let c=a.functionCall.id??a.functionCall.name??"";c&&t.add(c)}s.push(i)}else{let a=(i.parts??[]).filter(c=>{if(c.functionResponse){let d=c.functionResponse.id??c.functionResponse.name??"";return d?t.has(d):!1}return!0});a.length>0&&s.push({role:i.role,parts:a})}let r=new Set;for(let i of s)if(i.role!=="model"){for(let a of i.parts??[])if(a.functionResponse){let c=a.functionResponse.id??a.functionResponse.name??"";c&&r.add(c)}}let n=[];for(let i of s)if(i.role==="model"){let a=(i.parts??[]).filter(c=>{if(c.functionCall){let d=c.functionCall.id??c.functionCall.name??"";return d?r.has(d):!1}return!0});a.length>0&&n.push({role:i.role,parts:a})}else n.push(i);if(n.length<=1)return n;let o=[n[0]];for(let i=1;i<n.length;i++){let a=o[o.length-1],c=n[i];a.role===c.role?a.parts=[...a.parts??[],...c.parts??[]]:o.push(c)}return o}mapTools(e){return e.map(t=>({name:t.name,description:t.description,parameters:t.inputSchema}))}mapResponse(e){let t=e.text??"",r=e.functionCalls?.map((n,o)=>({id:n.id??`google_tool_${o}`,name:n.name,input:n.args??{}}));return{content:t,toolCalls:r&&r.length>0?r:void 0,usage:{inputTokens:e.usageMetadata?.promptTokenCount??0,outputTokens:e.usageMetadata?.candidatesTokenCount??0},stopReason:r&&r.length>0?"tool_use":"end_turn"}}}});var nr,qo=_(()=>{"use strict";Ut();nr=class extends Me{static{m(this,"MistralProvider")}constructor(e){super({...e,baseUrl:e.baseUrl??"https://api.mistral.ai/v1/"})}isAvailable(){return!!this.config.apiKey}supportsEmbeddings(){return!1}}});function Wo(l){switch(l.provider){case"anthropic":return new Qs(l);case"openai":return new Me(l);case"openrouter":return new er(l);case"ollama":return new tr(l);case"openwebui":return new sr(l);case"google":return new rr(l);case"mistral":return new nr(l);default:throw new Error(`Unknown LLM provider: ${l.provider}`)}}var Go=_(()=>{"use strict";Fo();Ut();jo();Bo();Ho();zo();qo();m(Wo,"createLLMProvider")});function Vo(l,e){return new cn(l,e)}var vm,cn,ac=_(()=>{"use strict";_t();Go();vm=["default","strong","fast","embeddings","local"],cn=class extends Ae{static{m(this,"ModelRouter")}providers=new Map;multiConfig;logger;constructor(e,t){super(e.default),this.multiConfig=e,this.logger=t}async initialize(){for(let e of vm){let t=this.multiConfig[e];if(t){let s=Wo(t);await s.initialize(),this.providers.set(e,s),this.logger?.info({tier:e,provider:t.provider,model:t.model},"LLM tier initialized")}}}resolve(e){return e&&this.providers.has(e)?{provider:this.providers.get(e),resolvedTier:e}:{provider:this.providers.get("default"),resolvedTier:"default"}}async complete(e){let{provider:t,resolvedTier:s}=this.resolve(e.tier),r=this.multiConfig[s];this.logger?.debug({requestedTier:e.tier??"default",resolvedTier:s,model:r?.model},"LLM routing request");let n=await t.complete(e);return this.logger?.info({tier:s,model:r?.model,inputTokens:n.usage?.inputTokens,outputTokens:n.usage?.outputTokens},"LLM call completed"),n}async*stream(e){let{provider:t}=this.resolve(e.tier);yield*t.stream(e)}async embed(e){return(this.providers.get("embeddings")??this.resolve().provider).embed(e)}supportsEmbeddings(){return(this.providers.get("embeddings")??this.resolve().provider).supportsEmbeddings()}isAvailable(){return this.resolve().provider.isAvailable()}getContextWindow(){return this.resolve().provider.getContextWindow()}};m(Vo,"createModelRouter")});function Oe(l){return Math.ceil(l.length/3.5)}function bt(l){if(typeof l.content=="string")return Oe(l.content)+4;let e=4;for(let t of l.content)switch(t.type){case"text":e+=Oe(t.text);break;case"image":e+=1e3;break;case"tool_use":e+=Oe(t.name)+Oe(JSON.stringify(t.input));break;case"tool_result":e+=Oe(t.content);break}return e}function Ko(l,e){let t=e?.keepRecentPairs??3,s=e?.maxTrimmedLength??120,r=e?.minContentLength??300,n=new Map;for(let a of l)if(a.role==="assistant"&&Array.isArray(a.content))for(let c of a.content)c.type==="tool_use"&&n.set(c.id,c.name);let o=[];for(let a=0;a<l.length;a++){let c=l[a];c.role==="user"&&Array.isArray(c.content)&&c.content.some(d=>d.type==="tool_result")&&o.push(a)}let i=new Set(o.slice(-t));return l.map((a,c)=>{if(i.has(c)||a.role!=="user"||!Array.isArray(a.content))return a;let d=!1,u=a.content.map(p=>{if(p.type!=="tool_result")return p;let f=p.content;if(typeof f!="string"||f.length<r)return p;d=!0;let g=n.get(p.tool_use_id)||"unknown",h=f.split(`
392
+ `)[0].slice(0,s),y=p.is_error?"Fehler":"Ergebnis";return{...p,content:`[${y}: ${g} \u2014 ${h}]`}});return d?{...a,content:u}:a})}var or,cc=_(()=>{"use strict";m(Oe,"estimateTokens");m(bt,"estimateMessageTokens");m(Ko,"trimOldToolResults");or=class{static{m(this,"PromptBuilder")}buildSystemPrompt(e={}){let{memories:t,skills:s,userProfile:r,todayEvents:n,conversationSummary:o}=e,i=process.platform==="darwin"?"macOS":process.platform==="win32"?"Windows":"Linux",a=process.env.HOME||process.env.USERPROFILE||"~",c=`You are Alfred, a personal AI assistant. You run on ${i} (home: ${a}).
375
393
 
376
394
  ## Core principles
377
395
  - **When the user's intent is clear**, ACT immediately using your tools. Don't explain what you'll do \u2014 just do it.
@@ -397,78 +415,83 @@ For complex tasks, work through multiple steps:
397
415
  5. **Summarize** the final result clearly.
398
416
 
399
417
  ## Environment
400
- - OS: ${o}
401
- - Home: ${i}
402
- - Documents: ${i}/Documents
403
- - Desktop: ${i}/Desktop
404
- - Downloads: ${i}/Downloads`;if(s&&s.length>0){a+=`
418
+ - OS: ${i}
419
+ - Home: ${a}
420
+ - Documents: ${a}/Documents
421
+ - Desktop: ${a}/Desktop
422
+ - Downloads: ${a}/Downloads`;if(s&&s.length>0){c+=`
405
423
 
406
424
  ## Available tools
407
- `;for(let h of s)a+=`- **${h.name}** (${h.riskLevel}): ${h.description}
408
- `;s.some(h=>h.name==="code_sandbox")&&(a+="\n## File generation (PDF, HTML, images, etc.)\nTo generate and send a file to the user:\n1. Use `code_sandbox` to run code that creates the file (e.g. pdfkit for PDF, HTML generation, etc.)\n2. The sandbox **automatically collects all files** written to the working directory and sends them as attachments to the user.\n3. Do NOT use `file send` afterwards \u2014 the files are already delivered. Using `file send` on sandbox-generated files will fail because the sandbox runs in an isolated temp directory.\n"),s.some(h=>h.name==="code_agent")&&(a+='\n## Code agent delegation\nWhen the user asks you to **write code, edit files, fix bugs, refactor, implement features, or perform any coding task in a repository**, delegate to the `code_agent` tool. You are an orchestrator, not a coder.\n\n- For **single, focused tasks**: use `code_agent` with `action: "run"` and pick the best agent.\n- For **complex, multi-step tasks**: use `code_agent` with `action: "orchestrate"` \u2014 the system will decompose the task, run agents in parallel, and validate results.\n- Add `git: true` when the user wants the changes committed, pushed, and a PR/MR created.\n- Use `action: "list_agents"` if you\'re unsure which agents are available.\n\n**Do NOT delegate to code_agent** when the task requires your own data or tools (documents, memories, emails, todos, calendar, etc.). For these tasks, use your tools directly \u2014 the code agent has no access to your skills or data.\n\n## Data-to-file workflow\nWhen the user asks to **collect data and produce a file** (e.g. "list all invoices in an Excel"):\n1. **Gather data** using your own tools first (document search/summarize, file list, email, etc.)\n2. **Generate the file** using `code_sandbox` \u2014 pass the collected data as variables in the code, then write the output file (Excel via exceljs, PDF via pdfkit, CSV, etc.)\n3. Do NOT try to do both steps inside code_sandbox \u2014 it cannot access your documents or skills.'),s.some(h=>h.name==="watch")&&s.some(h=>h.name==="scheduled_task")&&(a+='\n## Automation: watch vs. scheduled_task vs. reminder\n- **"Alert me when X happens"** \u2192 use `watch` (polls a skill, evaluates condition, no LLM cost per check)\n- **"Do X every day at 9 AM"** / **"Check X and report"** / **"Do X in 5 minutes"** \u2192 use `scheduled_task` (time-based, can use LLM via prompt_template, can execute any skill)\n- **"Remind me to X"** \u2192 use `reminder` ONLY for simple text reminders that just send a notification message\n- **IMPORTANT:** When the user asks to **execute a task** at a future time (e.g. "f\xFChre ein Briefing in 2 Minuten aus", "run a backup at 9 PM"), ALWAYS use `scheduled_task` with `prompt_template`, NOT `reminder`. Reminders cannot execute skills or call the LLM.\n- For infrastructure monitoring on a schedule, use `scheduled_task` with `prompt_template` that instructs you to run the `monitor` tool and report only problems.\n- **IMPORTANT:** When creating a scheduled briefing task, use `skill_name: "briefing"` with `skill_input: {"action":"run"}` instead of `prompt_template`. This executes the briefing directly without LLM overhead.'),s.some(h=>h.name==="background_task")&&s.some(h=>h.name==="delegate")&&(a+='\n## background_task vs. delegate\n- **`background_task`**: Runs a **single skill call** asynchronously (e.g. schedule one email send, one file download). It does NOT support multi-step workflows.\n- **`delegate`**: Runs a **multi-step sub-agent** with full tool access. Use for any task that requires multiple tool calls (search \u2192 read \u2192 process \u2192 generate).\n- **NEVER use `background_task` for complex tasks** like "search emails and create a report" \u2014 use `delegate` instead.')}r&&(a+=`
425
+ `;for(let y of s)c+=`- **${y.name}** (${y.riskLevel}): ${y.description}
426
+ `;s.some(y=>y.name==="code_sandbox")&&(c+="\n## File generation (PDF, HTML, images, etc.)\nTo generate and send a file to the user:\n1. Use `code_sandbox` to run code that creates the file (e.g. pdfkit for PDF, HTML generation, etc.)\n2. The sandbox **automatically collects all files** written to the working directory and sends them as attachments to the user.\n3. Do NOT use `file send` afterwards \u2014 the files are already delivered. Using `file send` on sandbox-generated files will fail because the sandbox runs in an isolated temp directory.\n"),s.some(y=>y.name==="code_agent")&&(c+='\n## Code agent delegation\nWhen the user asks you to **write code, edit files, fix bugs, refactor, implement features, or perform any coding task in a repository**, delegate to the `code_agent` tool. You are an orchestrator, not a coder.\n\n- For **single, focused tasks**: use `code_agent` with `action: "run"` and pick the best agent.\n- For **complex, multi-step tasks**: use `code_agent` with `action: "orchestrate"` \u2014 the system will decompose the task, run agents in parallel, and validate results.\n- Add `git: true` when the user wants the changes committed, pushed, and a PR/MR created.\n- Use `action: "list_agents"` if you\'re unsure which agents are available.\n\n**Do NOT delegate to code_agent** when the task requires your own data or tools (documents, memories, emails, todos, calendar, etc.). For these tasks, use your tools directly \u2014 the code agent has no access to your skills or data.\n\n## Data-to-file workflow\nWhen the user asks to **collect data and produce a file** (e.g. "list all invoices in an Excel"):\n1. **Gather data** using your own tools first (document search/summarize, file list, email, etc.)\n2. **Generate the file** using `code_sandbox` \u2014 pass the collected data as variables in the code, then write the output file (Excel via exceljs, PDF via pdfkit, CSV, etc.)\n3. Do NOT try to do both steps inside code_sandbox \u2014 it cannot access your documents or skills.'),s.some(y=>y.name==="watch")&&s.some(y=>y.name==="scheduled_task")&&(c+='\n## Automation: watch vs. scheduled_task vs. reminder\n- **"Alert me when X happens"** \u2192 use `watch` (polls a skill, evaluates condition, no LLM cost per check)\n- **"Do X every day at 9 AM"** / **"Check X and report"** / **"Do X in 5 minutes"** \u2192 use `scheduled_task` (time-based, can use LLM via prompt_template, can execute any skill)\n- **"Remind me to X"** \u2192 use `reminder` ONLY for simple text reminders that just send a notification message\n- **IMPORTANT:** When the user asks to **execute a task** at a future time (e.g. "f\xFChre ein Briefing in 2 Minuten aus", "run a backup at 9 PM"), ALWAYS use `scheduled_task` with `prompt_template`, NOT `reminder`. Reminders cannot execute skills or call the LLM.\n- For infrastructure monitoring on a schedule, use `scheduled_task` with `prompt_template` that instructs you to run the `monitor` tool and report only problems.\n- **IMPORTANT:** When creating a scheduled briefing task, use `skill_name: "briefing"` with `skill_input: {"action":"run"}` instead of `prompt_template`. This executes the briefing directly without LLM overhead.'),s.some(y=>y.name==="background_task")&&s.some(y=>y.name==="delegate")&&(c+='\n## background_task vs. delegate\n- **`background_task`**: Runs a **single skill call** asynchronously (e.g. schedule one email send, one file download). It does NOT support multi-step workflows.\n- **`delegate`**: Runs a **multi-step sub-agent** with full tool access. Use for any task that requires multiple tool calls (search \u2192 read \u2192 process \u2192 generate).\n- **NEVER use `background_task` for complex tasks** like "search emails and create a report" \u2014 use `delegate` instead.')}r&&(c+=`
427
+
428
+ ## User profile`,r.displayName&&(c+=`
429
+ - Name: ${r.displayName}`),r.timezone&&(c+=`
430
+ - Timezone: ${r.timezone}`),r.language&&(c+=`
431
+ - Language: ${r.language}`),r.bio&&(c+=`
432
+ - Bio: ${r.bio}`));let d=Intl.DateTimeFormat().resolvedOptions().timeZone,u=r?.timezone||d,p=new Date,f=p.toLocaleTimeString("en-GB",{timeZone:u,hour:"2-digit",minute:"2-digit"}),g=p.toLocaleDateString("en-CA",{timeZone:u}),h=p.toLocaleDateString("en-US",{timeZone:u,weekday:"long"});if(c+=`
409
433
 
410
- ## User profile`,r.displayName&&(a+=`
411
- - Name: ${r.displayName}`),r.timezone&&(a+=`
412
- - Timezone: ${r.timezone}`),r.language&&(a+=`
413
- - Language: ${r.language}`),r.bio&&(a+=`
414
- - Bio: ${r.bio}`));let c=Intl.DateTimeFormat().resolvedOptions().timeZone,d=r?.timezone||c,u=new Date,p=u.toLocaleTimeString("en-GB",{timeZone:d,hour:"2-digit",minute:"2-digit"}),g=u.toLocaleDateString("en-CA",{timeZone:d}),f=u.toLocaleDateString("en-US",{timeZone:d,weekday:"long"});if(a+=`
434
+ ## Current date & time`,c+=`
435
+ - Timezone: ${u}`,c+=`
436
+ - Date: ${g} (${h})`,c+=`
437
+ - Time: ${f}`,r?.timezone&&r.timezone!==d&&(c+=`
438
+ - Server timezone: ${d}`),n&&n.length>0){c+=`
415
439
 
416
- ## Current date & time`,a+=`
417
- - Timezone: ${d}`,a+=`
418
- - Date: ${g} (${f})`,a+=`
419
- - Time: ${p}`,r?.timezone&&r.timezone!==c&&(a+=`
420
- - Server timezone: ${c}`),n&&n.length>0){a+=`
440
+ ## Today's events`;for(let y of n){let E=y.allDay?"All day":y.start.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",...r?.timezone?{timeZone:r.timezone}:{}}),$=y.allDay?"":`-${y.end.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",...r?.timezone?{timeZone:r.timezone}:{}})}`,v=y.location?` @ ${y.location}`:"";c+=`
441
+ - ${E}${$}: ${y.title}${v}`}}if(o&&(c+=`
421
442
 
422
- ## Today's events`;for(let h of n){let y=h.allDay?"All day":h.start.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",...r?.timezone?{timeZone:r.timezone}:{}}),E=h.allDay?"":`-${h.end.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",...r?.timezone?{timeZone:r.timezone}:{}})}`,$=h.location?` @ ${h.location}`:"";a+=`
423
- - ${y}${E}: ${h.title}${$}`}}if(t&&t.length>0){if(a+=`
443
+ ## Conversation context
444
+ `,c+=o,c+=`
445
+
446
+ This summarizes the earlier conversation. Use it to maintain continuity. The most recent messages follow below.`),t&&t.length>0){if(c+=`
424
447
 
425
448
  ## Memories about this user
426
- `,t.some(y=>y.type&&y.type!=="general")){let y=new Map;for(let $ of t){let v=$.type||"general",P=y.get(v);P||(P=[],y.set(v,P)),P.push($)}let E={fact:"Facts",preference:"Preferences",correction:"Corrections",entity:"Entities",decision:"Decisions",relationship:"Relationships",principle:"Principles",commitment:"Commitments",moment:"Moments",skill:"Skills",general:"General"};for(let[$,v]of y){a+=`
427
- ### ${E[$]||$}
428
- `;for(let P of v)a+=`- ${P.key}: ${P.value}
429
- `}}else for(let y of t)a+=`- [${y.category}] ${y.key}: ${y.value}
430
- `;a+=`
431
- Use these memories to personalize your responses. When the user tells you new facts or preferences, use the memory tool to save them.`}else a+=`
432
-
433
- When the user tells you facts about themselves or preferences, use the memory tool to save them for future reference.`;return a}buildMessages(e){let t=e.filter(s=>s.role==="user"||s.role==="assistant").map(s=>{if(s.toolCalls){let r;try{r=JSON.parse(s.toolCalls)}catch{r=[]}if(s.role==="assistant"){let i=r,a=[];s.content&&a.push({type:"text",text:s.content});for(let c of i)a.push({type:"tool_use",id:c.id,name:c.name,input:c.input});return a.length===0&&a.push({type:"text",text:""}),{role:"assistant",content:a}}let n=r,o=[];for(let i of n)i.type==="tool_result"&&o.push(i);return o.length>0?{role:"user",content:o}:{role:"user",content:s.content||""}}return{role:s.role,content:s.content}});return this.sanitizeToolMessages(t)}sanitizeToolMessages(e){let t=new Set,s=new Set;for(let a of e)if(a.role==="assistant"&&Array.isArray(a.content))for(let c of a.content)c.type==="tool_use"&&t.add(c.id);else if(a.role==="user"&&Array.isArray(a.content))for(let c of a.content)c.type==="tool_result"&&t.has(c.tool_use_id)&&s.add(c.tool_use_id);let r=new Set,n=new Set,o=[];for(let a of e){if(!Array.isArray(a.content)){o.push(a);continue}let c=a.content.filter(d=>d.type==="tool_use"?!s.has(d.id)||r.has(d.id)?!1:(r.add(d.id),!0):d.type==="tool_result"?!s.has(d.tool_use_id)||n.has(d.tool_use_id)?!1:(n.add(d.tool_use_id),!0):!0);c.length!==0&&o.push({...a,content:c})}let i=[];for(let a of o){let c=i[i.length-1];if(c&&c.role===a.role){let d=typeof c.content=="string"?[{type:"text",text:c.content}]:c.content,u=typeof a.content=="string"?[{type:"text",text:a.content}]:a.content;i[i.length-1]={...c,content:[...d,...u]}}else i.push(a)}return i}buildTools(e){return e.map(t=>({name:t.name,description:t.description,inputSchema:t.inputSchema}))}}});var Vo=_(()=>{"use strict";Tt();Po();Ot();Uo();Fo();jo();Bo();Ho();zo();rc();nc()});var sr,Ko=_(()=>{"use strict";sr=class{static{m(this,"RateLimiter")}buckets=new Map;checkCount=0;check(e,t){this.checkCount++,this.checkCount%100===0&&this.cleanup();let s=Date.now(),r=t.windowSeconds*1e3,n=this.buckets.get(e);if(!n)return{allowed:!0,remaining:t.maxInvocations,resetsAt:s+r};if(s>n.windowStart+r)return{allowed:!0,remaining:t.maxInvocations,resetsAt:s+r};let o=Math.max(0,t.maxInvocations-n.count);return{allowed:n.count<t.maxInvocations,remaining:o,resetsAt:n.windowStart+r}}increment(e,t){let s=Date.now(),r=t.windowSeconds*1e3,n=this.buckets.get(e);!n||s>n.windowStart+r?this.buckets.set(e,{count:1,windowStart:s}):n.count+=1}cleanup(){let e=Date.now();for(let[t,s]of this.buckets)e>s.windowStart+36e5&&this.buckets.delete(t)}reset(){this.buckets.clear()}}});var rr,oc=_(()=>{"use strict";Ko();rr=class{static{m(this,"RuleEngine")}rules=[];rateLimiter=new sr;loadRules(e){this.rules=[...e].sort((t,s)=>t.priority-s.priority)}getRules(){return this.rules}evaluate(e){for(let t of this.rules)if(this.ruleMatches(t,e))return t.rateLimit&&t.effect==="allow"&&!this.checkRateLimit(t,e)?{allowed:!1,matchedRule:t,reason:`Rate limit exceeded for rule: ${t.id}`,timestamp:new Date}:{allowed:t.effect==="allow",matchedRule:t,reason:`Matched rule: ${t.id}`,timestamp:new Date};return{allowed:!1,matchedRule:void 0,reason:"No matching rule found \u2014 default deny",timestamp:new Date}}checkRateLimit(e,t){if(!e.rateLimit)return!0;let s=this.getScopeKey(e.scope,t),r=`${e.id}:${s}`;return this.rateLimiter.check(r,e.rateLimit).allowed?(this.rateLimiter.increment(r,e.rateLimit),!0):!1}resetRateLimits(){this.rateLimiter.reset()}getScopeKey(e,t){switch(e){case"global":return"global";case"user":return t.userId;case"conversation":return t.chatId??"unknown";case"platform":return t.platform}}ruleMatches(e,t){return!(!e.actions.includes("*")&&!e.actions.includes(t.action)||!e.riskLevels.includes(t.riskLevel)||e.conditions&&(e.conditions.users&&e.conditions.users.length>0&&!e.conditions.users.includes(t.userId)||e.conditions.platforms&&e.conditions.platforms.length>0&&!e.conditions.platforms.includes(t.platform)||e.conditions.chatType&&t.chatType&&e.conditions.chatType!==t.chatType||e.conditions.timeWindow&&!this.matchesTimeWindow(e.conditions.timeWindow)))}matchesTimeWindow(e){if(!e)return!0;let t=new Date;if(e.daysOfWeek&&e.daysOfWeek.length>0&&!e.daysOfWeek.includes(t.getDay()))return!1;let s=t.getHours();if(e.startHour!==void 0&&e.endHour!==void 0){if(e.startHour<=e.endHour){if(s<e.startHour||s>=e.endHour)return!1}else if(s>=e.endHour&&s<e.startHour)return!1}else if(e.startHour!==void 0){if(s<e.startHour)return!1}else if(e.endHour!==void 0&&s>=e.endHour)return!1;return!0}}});var ic,ac,cc,bt,lc=_(()=>{"use strict";ic=["allow","deny"],ac=["global","user","conversation","platform"],cc=["read","write","destructive","admin"],bt=class{static{m(this,"RuleLoader")}loadFromObject(e){if(!e||!Array.isArray(e.rules))throw new Error('Invalid data: expected an object with a "rules" array');return e.rules.map((t,s)=>this.validateRule(t,s))}validateRule(e,t){if(typeof e!="object"||e===null)throw new Error(`Rule at index ${t} is not an object`);let s=e;if(typeof s.id!="string"||s.id.length===0)throw new Error(`Rule at index ${t} is missing a valid "id" string`);if(typeof s.effect!="string"||!ic.includes(s.effect))throw new Error(`Rule "${s.id}" has invalid "effect": expected one of ${ic.join(", ")}`);if(typeof s.priority!="number"||!Number.isFinite(s.priority))throw new Error(`Rule "${s.id}" is missing a valid "priority" number`);if(typeof s.scope!="string"||!ac.includes(s.scope))throw new Error(`Rule "${s.id}" has invalid "scope": expected one of ${ac.join(", ")}`);if(!Array.isArray(s.actions)||s.actions.length===0)throw new Error(`Rule "${s.id}" is missing a valid "actions" array`);for(let n of s.actions)if(typeof n!="string")throw new Error(`Rule "${s.id}" has a non-string entry in "actions"`);if(!Array.isArray(s.riskLevels)||s.riskLevels.length===0)throw new Error(`Rule "${s.id}" is missing a valid "riskLevels" array`);for(let n of s.riskLevels)if(!cc.includes(n))throw new Error(`Rule "${s.id}" has invalid risk level "${n}": expected one of ${cc.join(", ")}`);let r={id:s.id,effect:s.effect,priority:s.priority,scope:s.scope,actions:s.actions,riskLevels:s.riskLevels};if(s.conditions!==void 0){if(typeof s.conditions!="object"||s.conditions===null)throw new Error(`Rule "${s.id}" has invalid "conditions": expected an object`);r.conditions=s.conditions}if(s.rateLimit!==void 0){if(typeof s.rateLimit!="object"||s.rateLimit===null)throw new Error(`Rule "${s.id}" has invalid "rateLimit": expected an object`);let n=s.rateLimit;if(typeof n.maxInvocations!="number"||typeof n.windowSeconds!="number")throw new Error(`Rule "${s.id}" has invalid "rateLimit": expected maxInvocations and windowSeconds numbers`);r.rateLimit=s.rateLimit}return r}}});import Em from"node:crypto";var nr,dc=_(()=>{"use strict";nr=class{static{m(this,"SecurityManager")}ruleEngine;auditRepository;logger;constructor(e,t,s){this.ruleEngine=e,this.auditRepository=t,this.logger=s}evaluate(e){let t=this.ruleEngine.evaluate(e),s={id:Em.randomUUID(),timestamp:t.timestamp,userId:e.userId,action:e.action,riskLevel:e.riskLevel,ruleId:t.matchedRule?.id,effect:t.allowed?"allow":"deny",platform:e.platform,chatId:e.chatId,context:{chatType:e.chatType,reason:t.reason}};try{this.auditRepository.log(s)}catch(r){this.logger.error({err:r,auditEntry:s},"Failed to write audit log entry")}return this.logger.debug({userId:e.userId,action:e.action,allowed:t.allowed,ruleId:t.matchedRule?.id,reason:t.reason},"Security evaluation completed"),t}}});var an=_(()=>{"use strict";oc();Ko();lc();dc()});var R,U=_(()=>{"use strict";R=class{static{m(this,"Skill")}}});function ne(l){return l.masterUserId??l.userId}function W(l){let e=new Set;if(e.add(ne(l)),e.add(l.userId),l.linkedPlatformUserIds)for(let t of l.linkedPlatformUserIds)e.add(t);return[...e]}var Me=_(()=>{"use strict";m(ne,"effectiveUserId");m(W,"allUserIds")});var Pt,uc=_(()=>{"use strict";Pt=class{static{m(this,"SkillRegistry")}skills=new Map;register(e){let{name:t}=e.metadata;if(this.skills.has(t))throw new Error(`Skill "${t}" is already registered`);this.skills.set(t,e)}get(e){return this.skills.get(e)}getAll(){return[...this.skills.values()]}has(e){return this.skills.has(e)}unregister(e){return this.skills.delete(e)}toToolDefinitions(){return this.getAll().map(e=>({name:e.metadata.name,description:e.metadata.description,inputSchema:e.metadata.inputSchema}))}}});var Ut,mc=_(()=>{"use strict";Ut=class{static{m(this,"SkillSandbox")}logger;constructor(e){this.logger=e}async execute(e,t,s,r,n){r=r??e.metadata.timeoutMs??3e4;let{name:o}=e.metadata;return this.logger.info({skill:o,input:t},"Skill execution started"),n?this.executeWithTracker(e,t,s,o,r,n):this.executeWithHardTimeout(e,t,s,o,r)}async executeWithTracker(e,t,s,r,n,o){return new Promise(i=>{let a=!1,c,d,u,p=m(()=>{c&&clearInterval(c),d&&clearTimeout(d),u&&clearTimeout(u)},"cleanup"),g=m(f=>{a||(a=!0,p(),i(f))},"finish");e.execute(t,s).then(f=>{this.logger.info({skill:r,success:f.success,...f.success?{}:{error:f.error}},"Skill execution completed"),g(f)},f=>{let h=f instanceof Error?f.message:String(f);this.logger.error({skill:r,error:h},"Skill execution failed"),g({success:!1,error:h})}),u=setTimeout(()=>{if(a)return;let f=o.getIdleMs();if(f>=12e4){let y=o.getSnapshot();this.logger.warn({skill:r,idleMs:f,state:y.state,iteration:y.iteration},"Agent inactive after initial timeout \u2014 aborting"),g({success:!1,error:`Skill "${r}" timed out \u2014 inactive for ${Math.round(f/1e3)}s (last state: ${y.state})`});return}let h=o.getSnapshot();this.logger.info({skill:r,idleMs:f,state:h.state,iteration:h.iteration,totalMs:h.totalElapsedMs},"Initial timeout reached but agent is active \u2014 extending"),c=setInterval(()=>{if(a){p();return}let y=o.getIdleMs(),E=o.getSnapshot();y>=12e4?(this.logger.warn({skill:r,idleMs:y,state:E.state,iteration:E.iteration,totalMs:E.totalElapsedMs},"Agent went inactive \u2014 aborting"),g({success:!1,error:`Skill "${r}" killed \u2014 inactive for ${Math.round(y/1e3)}s (last state: ${E.state})`})):this.logger.debug({skill:r,idleMs:y,state:E.state,iteration:E.iteration},"Agent still active, continuing...")},1e4)},n),d=setTimeout(()=>{if(a)return;let f=o.getSnapshot();this.logger.error({skill:r,totalMs:f.totalElapsedMs,state:f.state,iteration:f.iteration},"Absolute time limit reached \u2014 force killing agent"),g({success:!1,error:`Skill "${r}" force-killed after ${Math.round(12e5/6e4)} minutes (safety limit)`})},12e5)})}async executeWithHardTimeout(e,t,s,r,n){try{let o=await Promise.race([e.execute(t,s),new Promise((i,a)=>{setTimeout(()=>a(new Error(`Skill "${r}" timed out after ${n}ms`)),n)})]);return this.logger.info({skill:r,success:o.success,...o.success?{}:{error:o.error}},"Skill execution completed"),o}catch(o){let i=o instanceof Error?o.message:String(o);return this.logger.error({skill:r,error:i},"Skill execution failed"),{success:!1,error:i}}}}});var Et,Xo=_(()=>{"use strict";Et=class{static{m(this,"ActivityTracker")}state="starting";iteration=0;maxIterations=0;currentTool;lastPingAt;startedAt;history=[];onProgress;constructor(e){this.startedAt=Date.now(),this.lastPingAt=Date.now(),this.onProgress=e}ping(e,t){this.state=e,this.lastPingAt=Date.now(),t?.iteration!==void 0&&(this.iteration=t.iteration),t?.maxIterations!==void 0&&(this.maxIterations=t.maxIterations),this.currentTool=t?.tool,this.history.push({state:e,tool:t?.tool,iteration:this.iteration,timestamp:this.lastPingAt}),this.onProgress&&this.onProgress(this.formatStatus())}getIdleMs(){return Date.now()-this.lastPingAt}getTotalElapsedMs(){return Date.now()-this.startedAt}formatStatus(){let e=this.maxIterations>0?` (${this.iteration}/${this.maxIterations})`:"";switch(this.state){case"starting":return"Sub-agent starting...";case"llm_call":return`Sub-agent thinking...${e}`;case"tool_call":return this.currentTool?`Sub-agent using ${this.currentTool}${e}`:`Sub-agent using tool...${e}`;case"processing":return`Sub-agent processing...${e}`;case"done":return`Sub-agent done${e}`;default:return`Sub-agent working...${e}`}}getSnapshot(){return{state:this.state,iteration:this.iteration,maxIterations:this.maxIterations,lastPingAt:this.lastPingAt,idleMs:this.getIdleMs(),currentTool:this.currentTool,totalElapsedMs:this.getTotalElapsedMs(),history:[...this.history]}}}});import $m from"node:fs";import Yo from"node:path";var cn,pc=_(()=>{"use strict";U();cn=class{static{m(this,"PluginLoader")}async loadFromDirectory(e){let t=Yo.resolve(e),s;try{s=await $m.promises.readdir(t)}catch(o){let i=o instanceof Error?o.message:String(o);return console.warn(`PluginLoader: failed to read directory "${t}": ${i}`),[]}let r=s.filter(o=>o.endsWith(".js")),n=[];for(let o of r){let i=Yo.join(t,o);try{let a=await this.loadFromFile(i);n.push(a)}catch(a){let c=a instanceof Error?a.message:String(a);console.warn(`PluginLoader: skipping "${i}": ${c}`)}}return n}async loadFromFile(e){let t=Yo.resolve(e),n=(await import(`file:///${t.replace(/\\/g,"/")}`)).default;if(typeof n!="function")throw new Error(`Module "${t}" does not have a default export that is a class`);let o=new n;if(!(o instanceof R))throw new Error(`Default export of "${t}" does not extend Skill`);return this.validateMetadata(o,t),o}validateMetadata(e,t){let{metadata:s}=e;if(!s)throw new Error(`Plugin "${t}" is missing metadata`);if(!s.name||typeof s.name!="string")throw new Error(`Plugin "${t}" has invalid or missing metadata.name`);if(!s.description||typeof s.description!="string")throw new Error(`Plugin "${t}" has invalid or missing metadata.description`);if(!["read","write","destructive","admin"].includes(s.riskLevel))throw new Error(`Plugin "${t}" has invalid metadata.riskLevel: "${String(s.riskLevel)}"`);if(!s.version||typeof s.version!="string")throw new Error(`Plugin "${t}" has invalid or missing metadata.version`)}}});var km,vm,Ft,hc=_(()=>{"use strict";U();km=/Math\.(sin|cos|tan|sqrt|pow|abs|floor|ceil|round|log|log2|log10|PI|E)/g,vm=/^[\d+\-*/().,\s%]*(Math\.(sin|cos|tan|sqrt|pow|abs|floor|ceil|round|log|log2|log10|PI|E)[\d+\-*/().,\s(%)]*)*$/,Ft=class extends R{static{m(this,"CalculatorSkill")}metadata={name:"calculator",category:"information",description:"Evaluate mathematical expressions. Use for any calculation, unit conversion, or math question the user asks.",riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{expression:{type:"string",description:"The mathematical expression to evaluate"}},required:["expression"]}};async execute(e,t){let s=e.expression;if(!s||typeof s!="string")return{success:!1,error:"Invalid expression: input must be a non-empty string"};let r=s.trim();if(!vm.test(r))return{success:!1,error:`Invalid expression: "${r}" contains disallowed constructs`};let n=r.replace(km,"");if(/[a-zA-Z]/.test(n))return{success:!1,error:`Invalid expression: "${r}" contains disallowed identifiers`};try{let i=new Function("Math",`"use strict"; return (${r});`)(Math);return typeof i!="number"||!isFinite(i)?{success:!1,error:`Invalid expression: "${r}" did not produce a finite number`}:{success:!0,data:i,display:`${r} = ${i}`}}catch{return{success:!1,error:`Invalid expression: "${r}"`}}}}});var jt,fc=_(()=>{"use strict";U();jt=class extends R{static{m(this,"SystemInfoSkill")}metadata={name:"system_info",category:"information",description:'Get system information: current date/time (datetime), system stats (general), memory usage (memory), or uptime (uptime). Use "datetime" when the user asks what day/time it is.',riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{category:{type:"string",enum:["general","memory","uptime","datetime"],description:"Category of system info (use datetime for current date/time)"}},required:["category"]}};async execute(e,t){let s=e.category;switch(s){case"general":return this.getGeneralInfo();case"memory":return this.getMemoryInfo();case"uptime":return this.getUptimeInfo();case"datetime":return this.getDateTimeInfo();default:return{success:!1,error:`Unknown category: "${String(s)}". Valid categories: general, memory, uptime`}}}getGeneralInfo(){let e={nodeVersion:process.version,platform:process.platform,arch:process.arch};return{success:!0,data:e,display:`Node.js ${e.nodeVersion} on ${e.platform} (${e.arch})`}}getMemoryInfo(){let e=process.memoryUsage(),t=m(r=>(r/1024/1024).toFixed(2),"toMB"),s={rss:`${t(e.rss)} MB`,heapTotal:`${t(e.heapTotal)} MB`,heapUsed:`${t(e.heapUsed)} MB`,external:`${t(e.external)} MB`};return{success:!0,data:s,display:`Memory \u2014 RSS: ${s.rss}, Heap: ${s.heapUsed} / ${s.heapTotal}, External: ${s.external}`}}getUptimeInfo(){let e=process.uptime(),t=Math.floor(e/3600),s=Math.floor(e%3600/60),r=Math.floor(e%60),n={uptimeSeconds:e,formatted:`${t}h ${s}m ${r}s`};return{success:!0,data:n,display:`Uptime: ${n.formatted}`}}getDateTimeInfo(){let e=new Date,t={iso:e.toISOString(),date:e.toLocaleDateString("de-DE",{weekday:"long",year:"numeric",month:"long",day:"numeric"}),time:e.toLocaleTimeString("de-DE"),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timestamp:e.getTime()};return{success:!0,data:t,display:`${t.date}, ${t.time} (${t.timezone})`}}}});var Bt,gc=_(()=>{"use strict";U();Bt=class extends R{static{m(this,"WebSearchSkill")}config;metadata={name:"web_search",category:"information",description:"Search the internet for current information, news, facts, or anything the user asks about that you don't know. Use this whenever you need up-to-date information.",riskLevel:"read",version:"1.1.0",inputSchema:{type:"object",properties:{query:{type:"string",description:"The search query"},count:{type:"number",description:"Number of results to return (default: 5, max: 10)"}},required:["query"]}};constructor(e){super(),this.config=e}async execute(e,t){let s=e.query,r=Math.min(Math.max(1,e.count||5),10);if(!s||typeof s!="string")return{success:!1,error:'Invalid input: "query" must be a non-empty string'};if(!this.config)return{success:!1,error:"Web search is not configured. Run `alfred setup` to configure a search provider."};if((this.config.provider==="brave"||this.config.provider==="tavily")&&!this.config.apiKey)return{success:!1,error:`Web search requires an API key for ${this.config.provider}. Run \`alfred setup\` to configure it.`};try{let o;switch(this.config.provider){case"brave":o=await this.searchBrave(s,r);break;case"searxng":o=await this.searchSearXNG(s,r);break;case"tavily":o=await this.searchTavily(s,r);break;case"duckduckgo":o=await this.searchDuckDuckGo(s,r);break;default:return{success:!1,error:`Unknown search provider: ${this.config.provider}`}}if(o.length===0)return{success:!0,data:{results:[]},display:`No results found for "${s}".`};let i=o.map((a,c)=>`${c+1}. **${a.title}**
449
+ `,t.some(E=>E.type&&E.type!=="general")){let E=new Map;for(let v of t){let j=v.type||"general",N=E.get(j);N||(N=[],E.set(j,N)),N.push(v)}let $={fact:"Facts",preference:"Preferences",correction:"Corrections",entity:"Entities",decision:"Decisions",relationship:"Relationships",principle:"Principles",commitment:"Commitments",moment:"Moments",skill:"Skills",general:"General"};for(let[v,j]of E){c+=`
450
+ ### ${$[v]||v}
451
+ `;for(let N of j)c+=`- ${N.key}: ${N.value}
452
+ `}}else for(let E of t)c+=`- [${E.category}] ${E.key}: ${E.value}
453
+ `;c+=`
454
+ Use these memories to personalize your responses. When the user tells you new facts or preferences, use the memory tool to save them.`}else c+=`
455
+
456
+ When the user tells you facts about themselves or preferences, use the memory tool to save them for future reference.`;return c}buildMessages(e){let t=e.filter(s=>s.role==="user"||s.role==="assistant").map(s=>{if(s.toolCalls){let r;try{r=JSON.parse(s.toolCalls)}catch{r=[]}if(s.role==="assistant"){let i=r,a=[];s.content&&a.push({type:"text",text:s.content});for(let c of i)a.push({type:"tool_use",id:c.id,name:c.name,input:c.input});return a.length===0&&a.push({type:"text",text:""}),{role:"assistant",content:a}}let n=r,o=[];for(let i of n)i.type==="tool_result"&&o.push(i);return o.length>0?{role:"user",content:o}:{role:"user",content:s.content||""}}return{role:s.role,content:s.content}});return this.sanitizeToolMessages(t)}sanitizeToolMessages(e){let t=new Set,s=new Set;for(let a of e)if(a.role==="assistant"&&Array.isArray(a.content))for(let c of a.content)c.type==="tool_use"&&t.add(c.id);else if(a.role==="user"&&Array.isArray(a.content))for(let c of a.content)c.type==="tool_result"&&t.has(c.tool_use_id)&&s.add(c.tool_use_id);let r=new Set,n=new Set,o=[];for(let a of e){if(!Array.isArray(a.content)){o.push(a);continue}let c=a.content.filter(d=>d.type==="tool_use"?!s.has(d.id)||r.has(d.id)?!1:(r.add(d.id),!0):d.type==="tool_result"?!s.has(d.tool_use_id)||n.has(d.tool_use_id)?!1:(n.add(d.tool_use_id),!0):!0);c.length!==0&&o.push({...a,content:c})}let i=[];for(let a of o){let c=i[i.length-1];if(c&&c.role===a.role){let d=typeof c.content=="string"?[{type:"text",text:c.content}]:c.content,u=typeof a.content=="string"?[{type:"text",text:a.content}]:a.content;i[i.length-1]={...c,content:[...d,...u]}}else i.push(a)}return i}buildTools(e){return e.map(t=>({name:t.name,description:t.description,inputSchema:t.inputSchema}))}}});var Xo=_(()=>{"use strict";_t();Fo();Ut();jo();Bo();Ho();zo();qo();Go();ac();cc()});var ir,Yo=_(()=>{"use strict";ir=class{static{m(this,"RateLimiter")}buckets=new Map;checkCount=0;check(e,t){this.checkCount++,this.checkCount%100===0&&this.cleanup();let s=Date.now(),r=t.windowSeconds*1e3,n=this.buckets.get(e);if(!n)return{allowed:!0,remaining:t.maxInvocations,resetsAt:s+r};if(s>n.windowStart+r)return{allowed:!0,remaining:t.maxInvocations,resetsAt:s+r};let o=Math.max(0,t.maxInvocations-n.count);return{allowed:n.count<t.maxInvocations,remaining:o,resetsAt:n.windowStart+r}}increment(e,t){let s=Date.now(),r=t.windowSeconds*1e3,n=this.buckets.get(e);!n||s>n.windowStart+r?this.buckets.set(e,{count:1,windowStart:s}):n.count+=1}cleanup(){let e=Date.now();for(let[t,s]of this.buckets)e>s.windowStart+36e5&&this.buckets.delete(t)}reset(){this.buckets.clear()}}});var ar,lc=_(()=>{"use strict";Yo();ar=class{static{m(this,"RuleEngine")}rules=[];rateLimiter=new ir;loadRules(e){this.rules=[...e].sort((t,s)=>t.priority-s.priority)}getRules(){return this.rules}evaluate(e){for(let t of this.rules)if(this.ruleMatches(t,e))return t.rateLimit&&t.effect==="allow"&&!this.checkRateLimit(t,e)?{allowed:!1,matchedRule:t,reason:`Rate limit exceeded for rule: ${t.id}`,timestamp:new Date}:{allowed:t.effect==="allow",matchedRule:t,reason:`Matched rule: ${t.id}`,timestamp:new Date};return{allowed:!1,matchedRule:void 0,reason:"No matching rule found \u2014 default deny",timestamp:new Date}}checkRateLimit(e,t){if(!e.rateLimit)return!0;let s=this.getScopeKey(e.scope,t),r=`${e.id}:${s}`;return this.rateLimiter.check(r,e.rateLimit).allowed?(this.rateLimiter.increment(r,e.rateLimit),!0):!1}resetRateLimits(){this.rateLimiter.reset()}getScopeKey(e,t){switch(e){case"global":return"global";case"user":return t.userId;case"conversation":return t.chatId??"unknown";case"platform":return t.platform}}ruleMatches(e,t){return!(!e.actions.includes("*")&&!e.actions.includes(t.action)||!e.riskLevels.includes(t.riskLevel)||e.conditions&&(e.conditions.users&&e.conditions.users.length>0&&!e.conditions.users.includes(t.userId)||e.conditions.platforms&&e.conditions.platforms.length>0&&!e.conditions.platforms.includes(t.platform)||e.conditions.chatType&&t.chatType&&e.conditions.chatType!==t.chatType||e.conditions.timeWindow&&!this.matchesTimeWindow(e.conditions.timeWindow)))}matchesTimeWindow(e){if(!e)return!0;let t=new Date;if(e.daysOfWeek&&e.daysOfWeek.length>0&&!e.daysOfWeek.includes(t.getDay()))return!1;let s=t.getHours();if(e.startHour!==void 0&&e.endHour!==void 0){if(e.startHour<=e.endHour){if(s<e.startHour||s>=e.endHour)return!1}else if(s>=e.endHour&&s<e.startHour)return!1}else if(e.startHour!==void 0){if(s<e.startHour)return!1}else if(e.endHour!==void 0&&s>=e.endHour)return!1;return!0}}});var dc,uc,mc,Et,pc=_(()=>{"use strict";dc=["allow","deny"],uc=["global","user","conversation","platform"],mc=["read","write","destructive","admin"],Et=class{static{m(this,"RuleLoader")}loadFromObject(e){if(!e||!Array.isArray(e.rules))throw new Error('Invalid data: expected an object with a "rules" array');return e.rules.map((t,s)=>this.validateRule(t,s))}validateRule(e,t){if(typeof e!="object"||e===null)throw new Error(`Rule at index ${t} is not an object`);let s=e;if(typeof s.id!="string"||s.id.length===0)throw new Error(`Rule at index ${t} is missing a valid "id" string`);if(typeof s.effect!="string"||!dc.includes(s.effect))throw new Error(`Rule "${s.id}" has invalid "effect": expected one of ${dc.join(", ")}`);if(typeof s.priority!="number"||!Number.isFinite(s.priority))throw new Error(`Rule "${s.id}" is missing a valid "priority" number`);if(typeof s.scope!="string"||!uc.includes(s.scope))throw new Error(`Rule "${s.id}" has invalid "scope": expected one of ${uc.join(", ")}`);if(!Array.isArray(s.actions)||s.actions.length===0)throw new Error(`Rule "${s.id}" is missing a valid "actions" array`);for(let n of s.actions)if(typeof n!="string")throw new Error(`Rule "${s.id}" has a non-string entry in "actions"`);if(!Array.isArray(s.riskLevels)||s.riskLevels.length===0)throw new Error(`Rule "${s.id}" is missing a valid "riskLevels" array`);for(let n of s.riskLevels)if(!mc.includes(n))throw new Error(`Rule "${s.id}" has invalid risk level "${n}": expected one of ${mc.join(", ")}`);let r={id:s.id,effect:s.effect,priority:s.priority,scope:s.scope,actions:s.actions,riskLevels:s.riskLevels};if(s.conditions!==void 0){if(typeof s.conditions!="object"||s.conditions===null)throw new Error(`Rule "${s.id}" has invalid "conditions": expected an object`);r.conditions=s.conditions}if(s.rateLimit!==void 0){if(typeof s.rateLimit!="object"||s.rateLimit===null)throw new Error(`Rule "${s.id}" has invalid "rateLimit": expected an object`);let n=s.rateLimit;if(typeof n.maxInvocations!="number"||typeof n.windowSeconds!="number")throw new Error(`Rule "${s.id}" has invalid "rateLimit": expected maxInvocations and windowSeconds numbers`);r.rateLimit=s.rateLimit}return r}}});import Sm from"node:crypto";var cr,hc=_(()=>{"use strict";cr=class{static{m(this,"SecurityManager")}ruleEngine;auditRepository;logger;constructor(e,t,s){this.ruleEngine=e,this.auditRepository=t,this.logger=s}evaluate(e){let t=this.ruleEngine.evaluate(e),s={id:Sm.randomUUID(),timestamp:t.timestamp,userId:e.userId,action:e.action,riskLevel:e.riskLevel,ruleId:t.matchedRule?.id,effect:t.allowed?"allow":"deny",platform:e.platform,chatId:e.chatId,context:{chatType:e.chatType,reason:t.reason}};try{this.auditRepository.log(s)}catch(r){this.logger.error({err:r,auditEntry:s},"Failed to write audit log entry")}return this.logger.debug({userId:e.userId,action:e.action,allowed:t.allowed,ruleId:t.matchedRule?.id,reason:t.reason},"Security evaluation completed"),t}}});var ln=_(()=>{"use strict";lc();Yo();pc();hc()});var x,U=_(()=>{"use strict";x=class{static{m(this,"Skill")}}});function ne(l){return l.masterUserId??l.userId}function G(l){let e=new Set;if(e.add(ne(l)),e.add(l.userId),l.linkedPlatformUserIds)for(let t of l.linkedPlatformUserIds)e.add(t);return[...e]}var Pe=_(()=>{"use strict";m(ne,"effectiveUserId");m(G,"allUserIds")});var Ft,fc=_(()=>{"use strict";Ft=class{static{m(this,"SkillRegistry")}skills=new Map;register(e){let{name:t}=e.metadata;if(this.skills.has(t))throw new Error(`Skill "${t}" is already registered`);this.skills.set(t,e)}get(e){return this.skills.get(e)}getAll(){return[...this.skills.values()]}has(e){return this.skills.has(e)}unregister(e){return this.skills.delete(e)}toToolDefinitions(){return this.getAll().map(e=>({name:e.metadata.name,description:e.metadata.description,inputSchema:e.metadata.inputSchema}))}}});var jt,gc=_(()=>{"use strict";jt=class{static{m(this,"SkillSandbox")}logger;constructor(e){this.logger=e}async execute(e,t,s,r,n){r=r??e.metadata.timeoutMs??3e4;let{name:o}=e.metadata;return this.logger.info({skill:o,input:t},"Skill execution started"),n?this.executeWithTracker(e,t,s,o,r,n):this.executeWithHardTimeout(e,t,s,o,r)}async executeWithTracker(e,t,s,r,n,o){return new Promise(i=>{let a=!1,c,d,u,p=m(()=>{c&&clearInterval(c),d&&clearTimeout(d),u&&clearTimeout(u)},"cleanup"),f=m(g=>{a||(a=!0,p(),i(g))},"finish");e.execute(t,s).then(g=>{this.logger.info({skill:r,success:g.success,...g.success?{}:{error:g.error}},"Skill execution completed"),f(g)},g=>{let h=g instanceof Error?g.message:String(g);this.logger.error({skill:r,error:h},"Skill execution failed"),f({success:!1,error:h})}),u=setTimeout(()=>{if(a)return;let g=o.getIdleMs();if(g>=12e4){let y=o.getSnapshot();this.logger.warn({skill:r,idleMs:g,state:y.state,iteration:y.iteration},"Agent inactive after initial timeout \u2014 aborting"),f({success:!1,error:`Skill "${r}" timed out \u2014 inactive for ${Math.round(g/1e3)}s (last state: ${y.state})`});return}let h=o.getSnapshot();this.logger.info({skill:r,idleMs:g,state:h.state,iteration:h.iteration,totalMs:h.totalElapsedMs},"Initial timeout reached but agent is active \u2014 extending"),c=setInterval(()=>{if(a){p();return}let y=o.getIdleMs(),E=o.getSnapshot();y>=12e4?(this.logger.warn({skill:r,idleMs:y,state:E.state,iteration:E.iteration,totalMs:E.totalElapsedMs},"Agent went inactive \u2014 aborting"),f({success:!1,error:`Skill "${r}" killed \u2014 inactive for ${Math.round(y/1e3)}s (last state: ${E.state})`})):this.logger.debug({skill:r,idleMs:y,state:E.state,iteration:E.iteration},"Agent still active, continuing...")},1e4)},n),d=setTimeout(()=>{if(a)return;let g=o.getSnapshot();this.logger.error({skill:r,totalMs:g.totalElapsedMs,state:g.state,iteration:g.iteration},"Absolute time limit reached \u2014 force killing agent"),f({success:!1,error:`Skill "${r}" force-killed after ${Math.round(12e5/6e4)} minutes (safety limit)`})},12e5)})}async executeWithHardTimeout(e,t,s,r,n){try{let o=await Promise.race([e.execute(t,s),new Promise((i,a)=>{setTimeout(()=>a(new Error(`Skill "${r}" timed out after ${n}ms`)),n)})]);return this.logger.info({skill:r,success:o.success,...o.success?{}:{error:o.error}},"Skill execution completed"),o}catch(o){let i=o instanceof Error?o.message:String(o);return this.logger.error({skill:r,error:i},"Skill execution failed"),{success:!1,error:i}}}}});var $t,Jo=_(()=>{"use strict";$t=class{static{m(this,"ActivityTracker")}state="starting";iteration=0;maxIterations=0;currentTool;lastPingAt;startedAt;history=[];onProgress;constructor(e){this.startedAt=Date.now(),this.lastPingAt=Date.now(),this.onProgress=e}ping(e,t){this.state=e,this.lastPingAt=Date.now(),t?.iteration!==void 0&&(this.iteration=t.iteration),t?.maxIterations!==void 0&&(this.maxIterations=t.maxIterations),this.currentTool=t?.tool,this.history.push({state:e,tool:t?.tool,iteration:this.iteration,timestamp:this.lastPingAt}),this.onProgress&&this.onProgress(this.formatStatus())}getIdleMs(){return Date.now()-this.lastPingAt}getTotalElapsedMs(){return Date.now()-this.startedAt}formatStatus(){let e=this.maxIterations>0?` (${this.iteration}/${this.maxIterations})`:"";switch(this.state){case"starting":return"Sub-agent starting...";case"llm_call":return`Sub-agent thinking...${e}`;case"tool_call":return this.currentTool?`Sub-agent using ${this.currentTool}${e}`:`Sub-agent using tool...${e}`;case"processing":return`Sub-agent processing...${e}`;case"done":return`Sub-agent done${e}`;default:return`Sub-agent working...${e}`}}getSnapshot(){return{state:this.state,iteration:this.iteration,maxIterations:this.maxIterations,lastPingAt:this.lastPingAt,idleMs:this.getIdleMs(),currentTool:this.currentTool,totalElapsedMs:this.getTotalElapsedMs(),history:[...this.history]}}}});import Am from"node:fs";import Zo from"node:path";var dn,yc=_(()=>{"use strict";U();dn=class{static{m(this,"PluginLoader")}async loadFromDirectory(e){let t=Zo.resolve(e),s;try{s=await Am.promises.readdir(t)}catch(o){let i=o instanceof Error?o.message:String(o);return console.warn(`PluginLoader: failed to read directory "${t}": ${i}`),[]}let r=s.filter(o=>o.endsWith(".js")),n=[];for(let o of r){let i=Zo.join(t,o);try{let a=await this.loadFromFile(i);n.push(a)}catch(a){let c=a instanceof Error?a.message:String(a);console.warn(`PluginLoader: skipping "${i}": ${c}`)}}return n}async loadFromFile(e){let t=Zo.resolve(e),n=(await import(`file:///${t.replace(/\\/g,"/")}`)).default;if(typeof n!="function")throw new Error(`Module "${t}" does not have a default export that is a class`);let o=new n;if(!(o instanceof x))throw new Error(`Default export of "${t}" does not extend Skill`);return this.validateMetadata(o,t),o}validateMetadata(e,t){let{metadata:s}=e;if(!s)throw new Error(`Plugin "${t}" is missing metadata`);if(!s.name||typeof s.name!="string")throw new Error(`Plugin "${t}" has invalid or missing metadata.name`);if(!s.description||typeof s.description!="string")throw new Error(`Plugin "${t}" has invalid or missing metadata.description`);if(!["read","write","destructive","admin"].includes(s.riskLevel))throw new Error(`Plugin "${t}" has invalid metadata.riskLevel: "${String(s.riskLevel)}"`);if(!s.version||typeof s.version!="string")throw new Error(`Plugin "${t}" has invalid or missing metadata.version`)}}});var Im,xm,Bt,wc=_(()=>{"use strict";U();Im=/Math\.(sin|cos|tan|sqrt|pow|abs|floor|ceil|round|log|log2|log10|PI|E)/g,xm=/^[\d+\-*/().,\s%]*(Math\.(sin|cos|tan|sqrt|pow|abs|floor|ceil|round|log|log2|log10|PI|E)[\d+\-*/().,\s(%)]*)*$/,Bt=class extends x{static{m(this,"CalculatorSkill")}metadata={name:"calculator",category:"information",description:"Evaluate mathematical expressions. Use for any calculation, unit conversion, or math question the user asks.",riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{expression:{type:"string",description:"The mathematical expression to evaluate"}},required:["expression"]}};async execute(e,t){let s=e.expression;if(!s||typeof s!="string")return{success:!1,error:"Invalid expression: input must be a non-empty string"};let r=s.trim();if(!xm.test(r))return{success:!1,error:`Invalid expression: "${r}" contains disallowed constructs`};let n=r.replace(Im,"");if(/[a-zA-Z]/.test(n))return{success:!1,error:`Invalid expression: "${r}" contains disallowed identifiers`};try{let i=new Function("Math",`"use strict"; return (${r});`)(Math);return typeof i!="number"||!isFinite(i)?{success:!1,error:`Invalid expression: "${r}" did not produce a finite number`}:{success:!0,data:i,display:`${r} = ${i}`}}catch{return{success:!1,error:`Invalid expression: "${r}"`}}}}});var Ht,Tc=_(()=>{"use strict";U();Ht=class extends x{static{m(this,"SystemInfoSkill")}metadata={name:"system_info",category:"information",description:'Get system information: current date/time (datetime), system stats (general), memory usage (memory), or uptime (uptime). Use "datetime" when the user asks what day/time it is.',riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{category:{type:"string",enum:["general","memory","uptime","datetime"],description:"Category of system info (use datetime for current date/time)"}},required:["category"]}};async execute(e,t){let s=e.category;switch(s){case"general":return this.getGeneralInfo();case"memory":return this.getMemoryInfo();case"uptime":return this.getUptimeInfo();case"datetime":return this.getDateTimeInfo();default:return{success:!1,error:`Unknown category: "${String(s)}". Valid categories: general, memory, uptime`}}}getGeneralInfo(){let e={nodeVersion:process.version,platform:process.platform,arch:process.arch};return{success:!0,data:e,display:`Node.js ${e.nodeVersion} on ${e.platform} (${e.arch})`}}getMemoryInfo(){let e=process.memoryUsage(),t=m(r=>(r/1024/1024).toFixed(2),"toMB"),s={rss:`${t(e.rss)} MB`,heapTotal:`${t(e.heapTotal)} MB`,heapUsed:`${t(e.heapUsed)} MB`,external:`${t(e.external)} MB`};return{success:!0,data:s,display:`Memory \u2014 RSS: ${s.rss}, Heap: ${s.heapUsed} / ${s.heapTotal}, External: ${s.external}`}}getUptimeInfo(){let e=process.uptime(),t=Math.floor(e/3600),s=Math.floor(e%3600/60),r=Math.floor(e%60),n={uptimeSeconds:e,formatted:`${t}h ${s}m ${r}s`};return{success:!0,data:n,display:`Uptime: ${n.formatted}`}}getDateTimeInfo(){let e=new Date,t={iso:e.toISOString(),date:e.toLocaleDateString("de-DE",{weekday:"long",year:"numeric",month:"long",day:"numeric"}),time:e.toLocaleTimeString("de-DE"),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timestamp:e.getTime()};return{success:!0,data:t,display:`${t.date}, ${t.time} (${t.timezone})`}}}});var zt,_c=_(()=>{"use strict";U();zt=class extends x{static{m(this,"WebSearchSkill")}config;metadata={name:"web_search",category:"information",description:"Search the internet for current information, news, facts, or anything the user asks about that you don't know. Use this whenever you need up-to-date information.",riskLevel:"read",version:"1.1.0",inputSchema:{type:"object",properties:{query:{type:"string",description:"The search query"},count:{type:"number",description:"Number of results to return (default: 5, max: 10)"}},required:["query"]}};constructor(e){super(),this.config=e}async execute(e,t){let s=e.query,r=Math.min(Math.max(1,e.count||5),10);if(!s||typeof s!="string")return{success:!1,error:'Invalid input: "query" must be a non-empty string'};if(!this.config)return{success:!1,error:"Web search is not configured. Run `alfred setup` to configure a search provider."};if((this.config.provider==="brave"||this.config.provider==="tavily")&&!this.config.apiKey)return{success:!1,error:`Web search requires an API key for ${this.config.provider}. Run \`alfred setup\` to configure it.`};try{let o;switch(this.config.provider){case"brave":o=await this.searchBrave(s,r);break;case"searxng":o=await this.searchSearXNG(s,r);break;case"tavily":o=await this.searchTavily(s,r);break;case"duckduckgo":o=await this.searchDuckDuckGo(s,r);break;default:return{success:!1,error:`Unknown search provider: ${this.config.provider}`}}if(o.length===0)return{success:!0,data:{results:[]},display:`No results found for "${s}".`};let i=o.map((a,c)=>`${c+1}. **${a.title}**
434
457
  ${a.url}
435
458
  ${a.snippet}`).join(`
436
459
 
437
460
  `);return{success:!0,data:{query:s,results:o},display:`Search results for "${s}":
438
461
 
439
- ${i}`}}catch(o){return{success:!1,error:`Search failed: ${o instanceof Error?o.message:String(o)}`}}}async searchBrave(e,t){let s=new URL("https://api.search.brave.com/res/v1/web/search");s.searchParams.set("q",e),s.searchParams.set("count",String(t));let r=await fetch(s.toString(),{headers:{Accept:"application/json","Accept-Encoding":"gzip","X-Subscription-Token":this.config.apiKey}});if(!r.ok)throw new Error(`Brave Search API returned ${r.status}: ${r.statusText}`);return((await r.json()).web?.results??[]).slice(0,t).map(o=>({title:o.title,url:o.url,snippet:o.description}))}async searchSearXNG(e,t){let s=(this.config.baseUrl??"http://localhost:8080").replace(/\/+$/,""),r=new URL(`${s}/search`);r.searchParams.set("q",e),r.searchParams.set("format","json"),r.searchParams.set("pageno","1");let n=await fetch(r.toString(),{headers:{Accept:"application/json"}});if(!n.ok)throw new Error(`SearXNG returned ${n.status}: ${n.statusText}`);return((await n.json()).results??[]).slice(0,t).map(i=>({title:i.title,url:i.url,snippet:i.content}))}async searchTavily(e,t){let s=await fetch("https://api.tavily.com/search",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({api_key:this.config.apiKey,query:e,max_results:t,include_answer:!1})});if(!s.ok)throw new Error(`Tavily API returned ${s.status}: ${s.statusText}`);return((await s.json()).results??[]).slice(0,t).map(n=>({title:n.title,url:n.url,snippet:n.content}))}async searchDuckDuckGo(e,t){let s=new URL("https://html.duckduckgo.com/html/");s.searchParams.set("q",e);let r=await fetch(s.toString(),{headers:{"User-Agent":"Mozilla/5.0 (compatible; Alfred/1.0)"}});if(!r.ok)throw new Error(`DuckDuckGo returned ${r.status}: ${r.statusText}`);let n=await r.text();return this.parseDuckDuckGoHtml(n,t)}parseDuckDuckGoHtml(e,t){let s=[],r=/<a[^>]+class="result__a"[^>]+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/g,n=/<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g,o=[],i;for(;(i=r.exec(e))!==null;){let c=i[1],d=this.stripHtml(i[2]).trim(),u=this.extractDdgUrl(c);d&&u&&o.push({url:u,title:d})}let a=[];for(;(i=n.exec(e))!==null;)a.push(this.stripHtml(i[1]).trim());for(let c=0;c<Math.min(o.length,t);c++)s.push({title:o[c].title,url:o[c].url,snippet:a[c]??""});return s}extractDdgUrl(e){try{if(e.includes("uddg=")){let s=new URL(e,"https://duckduckgo.com").searchParams.get("uddg");if(s)return decodeURIComponent(s)}}catch{}return e.startsWith("http")?e:""}stripHtml(e){return e.replace(/<[^>]*>/g,"").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#x27;/g,"'").replace(/&nbsp;/g," ").replace(/\s+/g," ")}}});var Ht,yc=_(()=>{"use strict";U();Me();Ht=class extends R{static{m(this,"ReminderSkill")}reminderRepo;metadata={name:"reminder",category:"productivity",description:'Set timed reminders that notify the user later. Use when the user says "remind me", "erinnere mich", or asks to be notified about something at a specific time. Prefer triggerAt (absolute time like "14:30" or "2026-02-28 09:00") over delayMinutes \u2014 it is more precise and avoids calculation errors.',riskLevel:"write",version:"3.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["set","list","cancel"],description:"The reminder action to perform"},message:{type:"string",description:"The reminder message (required for set)"},triggerAt:{type:"string",description:'Absolute time for the reminder. Accepts "HH:MM" for today or "YYYY-MM-DD HH:MM" for a specific date. Preferred over delayMinutes for time-specific reminders.'},delayMinutes:{type:"number",description:"Minutes until the reminder triggers. Use triggerAt instead when the user specifies a clock time."},reminderId:{type:"string",description:"The ID of the reminder to cancel (required for cancel)"}},required:["action"]}};constructor(e){super(),this.reminderRepo=e}getAllReminders(e){let t=new Set,s=[];for(let r of W(e))for(let n of this.reminderRepo.getByUser(r))t.has(n.id)||(t.add(n.id),s.push(n));return s}async execute(e,t){let s=e.action;switch(s){case"set":return this.setReminder(e,t);case"list":return this.listReminders(t);case"cancel":return this.cancelReminder(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: set, list, cancel`}}}setReminder(e,t){let s=e.message,r=e.triggerAt,n=e.delayMinutes;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "message" for set action'};let o;if(r&&typeof r=="string"){let u=this.parseTriggerAt(r,t.timezone);if(!u)return{success:!1,error:`Could not parse triggerAt "${r}". Use "HH:MM" for today or "YYYY-MM-DD HH:MM" for a specific date.`};if(u.getTime()<=Date.now())return{success:!1,error:`The time "${r}" is in the past. Please specify a future time.`};o=u}else if(n!==void 0&&typeof n=="number"&&n>0)o=new Date(Date.now()+n*60*1e3);else return{success:!1,error:'Provide either "triggerAt" (e.g. "14:30") or "delayMinutes" (positive number) for set action.'};let i=this.reminderRepo.create(ne(t),t.platform,t.chatId,s,o),a=o.getTime()-Date.now(),c=Math.round(a/6e4),d=o.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",...t.timezone?{timeZone:t.timezone}:{}});return{success:!0,data:{reminderId:i.id,message:s,triggerAt:i.triggerAt},display:`Reminder set (${i.id}): "${s}" at ${d} (in ${c} min)`}}parseTriggerAt(e,t){let s=e.trim(),r=/^(\d{1,2}):(\d{2})$/.exec(s);if(r){let o=parseInt(r[1],10),i=parseInt(r[2],10);return o>23||i>59?void 0:this.buildDateInTimezone(o,i,void 0,t)}let n=/^(\d{4})-(\d{2})-(\d{2})\s+(\d{1,2}):(\d{2})$/.exec(s);if(n){let o=parseInt(n[1],10),i=parseInt(n[2],10)-1,a=parseInt(n[3],10),c=parseInt(n[4],10),d=parseInt(n[5],10);return c>23||d>59||i>11||a>31?void 0:this.buildDateInTimezone(c,d,{year:o,month:i,day:a},t)}}buildDateInTimezone(e,t,s,r){if(!r){let g=s?new Date(s.year,s.month,s.day,e,t,0,0):new Date;return s||g.setHours(e,t,0,0),g}let n=new Date,o=s?new Date(Date.UTC(s.year,s.month,s.day,e,t,0)):new Date(Date.UTC(n.getFullYear(),n.getMonth(),n.getDate(),e,t,0)),i=new Intl.DateTimeFormat("en-CA",{timeZone:r,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1});if(!s){let g=i.formatToParts(n),f=parseInt(g.find(q=>q.type==="year").value,10),h=parseInt(g.find(q=>q.type==="month").value,10)-1,y=parseInt(g.find(q=>q.type==="day").value,10),E=new Date(Date.UTC(f,h,y,e,t,0)),$=i.formatToParts(E),v=parseInt($.find(q=>q.type==="hour").value,10),P=parseInt($.find(q=>q.type==="minute").value,10),x=(e-v)*60+(t-P);return E=new Date(E.getTime()+x*6e4),E}let a=o,c=i.formatToParts(a),d=parseInt(c.find(g=>g.type==="hour").value,10),u=parseInt(c.find(g=>g.type==="minute").value,10),p=(e-d)*60+(t-u);return a=new Date(a.getTime()+p*6e4),a}listReminders(e){let s=this.getAllReminders(e).map(r=>({reminderId:r.id,message:r.message,triggerAt:r.triggerAt}));return{success:!0,data:s,display:s.length===0?"No active reminders.":`Active reminders:
462
+ ${i}`}}catch(o){return{success:!1,error:`Search failed: ${o instanceof Error?o.message:String(o)}`}}}async searchBrave(e,t){let s=new URL("https://api.search.brave.com/res/v1/web/search");s.searchParams.set("q",e),s.searchParams.set("count",String(t));let r=await fetch(s.toString(),{headers:{Accept:"application/json","Accept-Encoding":"gzip","X-Subscription-Token":this.config.apiKey}});if(!r.ok)throw new Error(`Brave Search API returned ${r.status}: ${r.statusText}`);return((await r.json()).web?.results??[]).slice(0,t).map(o=>({title:o.title,url:o.url,snippet:o.description}))}async searchSearXNG(e,t){let s=(this.config.baseUrl??"http://localhost:8080").replace(/\/+$/,""),r=new URL(`${s}/search`);r.searchParams.set("q",e),r.searchParams.set("format","json"),r.searchParams.set("pageno","1");let n=await fetch(r.toString(),{headers:{Accept:"application/json"}});if(!n.ok)throw new Error(`SearXNG returned ${n.status}: ${n.statusText}`);return((await n.json()).results??[]).slice(0,t).map(i=>({title:i.title,url:i.url,snippet:i.content}))}async searchTavily(e,t){let s=await fetch("https://api.tavily.com/search",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({api_key:this.config.apiKey,query:e,max_results:t,include_answer:!1})});if(!s.ok)throw new Error(`Tavily API returned ${s.status}: ${s.statusText}`);return((await s.json()).results??[]).slice(0,t).map(n=>({title:n.title,url:n.url,snippet:n.content}))}async searchDuckDuckGo(e,t){let s=new URL("https://html.duckduckgo.com/html/");s.searchParams.set("q",e);let r=await fetch(s.toString(),{headers:{"User-Agent":"Mozilla/5.0 (compatible; Alfred/1.0)"}});if(!r.ok)throw new Error(`DuckDuckGo returned ${r.status}: ${r.statusText}`);let n=await r.text();return this.parseDuckDuckGoHtml(n,t)}parseDuckDuckGoHtml(e,t){let s=[],r=/<a[^>]+class="result__a"[^>]+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/g,n=/<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g,o=[],i;for(;(i=r.exec(e))!==null;){let c=i[1],d=this.stripHtml(i[2]).trim(),u=this.extractDdgUrl(c);d&&u&&o.push({url:u,title:d})}let a=[];for(;(i=n.exec(e))!==null;)a.push(this.stripHtml(i[1]).trim());for(let c=0;c<Math.min(o.length,t);c++)s.push({title:o[c].title,url:o[c].url,snippet:a[c]??""});return s}extractDdgUrl(e){try{if(e.includes("uddg=")){let s=new URL(e,"https://duckduckgo.com").searchParams.get("uddg");if(s)return decodeURIComponent(s)}}catch{}return e.startsWith("http")?e:""}stripHtml(e){return e.replace(/<[^>]*>/g,"").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#x27;/g,"'").replace(/&nbsp;/g," ").replace(/\s+/g," ")}}});var qt,bc=_(()=>{"use strict";U();Pe();qt=class extends x{static{m(this,"ReminderSkill")}reminderRepo;metadata={name:"reminder",category:"productivity",description:'Set timed reminders that notify the user later. Use when the user says "remind me", "erinnere mich", or asks to be notified about something at a specific time. Prefer triggerAt (absolute time like "14:30" or "2026-02-28 09:00") over delayMinutes \u2014 it is more precise and avoids calculation errors.',riskLevel:"write",version:"3.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["set","list","cancel"],description:"The reminder action to perform"},message:{type:"string",description:"The reminder message (required for set)"},triggerAt:{type:"string",description:'Absolute time for the reminder. Accepts "HH:MM" for today or "YYYY-MM-DD HH:MM" for a specific date. Preferred over delayMinutes for time-specific reminders.'},delayMinutes:{type:"number",description:"Minutes until the reminder triggers. Use triggerAt instead when the user specifies a clock time."},reminderId:{type:"string",description:"The ID of the reminder to cancel (required for cancel)"}},required:["action"]}};constructor(e){super(),this.reminderRepo=e}getAllReminders(e){let t=new Set,s=[];for(let r of G(e))for(let n of this.reminderRepo.getByUser(r))t.has(n.id)||(t.add(n.id),s.push(n));return s}async execute(e,t){let s=e.action;switch(s){case"set":return this.setReminder(e,t);case"list":return this.listReminders(t);case"cancel":return this.cancelReminder(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: set, list, cancel`}}}setReminder(e,t){let s=e.message,r=e.triggerAt,n=e.delayMinutes;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "message" for set action'};let o;if(r&&typeof r=="string"){let u=this.parseTriggerAt(r,t.timezone);if(!u)return{success:!1,error:`Could not parse triggerAt "${r}". Use "HH:MM" for today or "YYYY-MM-DD HH:MM" for a specific date.`};if(u.getTime()<=Date.now())return{success:!1,error:`The time "${r}" is in the past. Please specify a future time.`};o=u}else if(n!==void 0&&typeof n=="number"&&n>0)o=new Date(Date.now()+n*60*1e3);else return{success:!1,error:'Provide either "triggerAt" (e.g. "14:30") or "delayMinutes" (positive number) for set action.'};let i=this.reminderRepo.create(ne(t),t.platform,t.chatId,s,o),a=o.getTime()-Date.now(),c=Math.round(a/6e4),d=o.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",...t.timezone?{timeZone:t.timezone}:{}});return{success:!0,data:{reminderId:i.id,message:s,triggerAt:i.triggerAt},display:`Reminder set (${i.id}): "${s}" at ${d} (in ${c} min)`}}parseTriggerAt(e,t){let s=e.trim(),r=/^(\d{1,2}):(\d{2})$/.exec(s);if(r){let o=parseInt(r[1],10),i=parseInt(r[2],10);return o>23||i>59?void 0:this.buildDateInTimezone(o,i,void 0,t)}let n=/^(\d{4})-(\d{2})-(\d{2})\s+(\d{1,2}):(\d{2})$/.exec(s);if(n){let o=parseInt(n[1],10),i=parseInt(n[2],10)-1,a=parseInt(n[3],10),c=parseInt(n[4],10),d=parseInt(n[5],10);return c>23||d>59||i>11||a>31?void 0:this.buildDateInTimezone(c,d,{year:o,month:i,day:a},t)}}buildDateInTimezone(e,t,s,r){if(!r){let f=s?new Date(s.year,s.month,s.day,e,t,0,0):new Date;return s||f.setHours(e,t,0,0),f}let n=new Date,o=s?new Date(Date.UTC(s.year,s.month,s.day,e,t,0)):new Date(Date.UTC(n.getFullYear(),n.getMonth(),n.getDate(),e,t,0)),i=new Intl.DateTimeFormat("en-CA",{timeZone:r,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1});if(!s){let f=i.formatToParts(n),g=parseInt(f.find(H=>H.type==="year").value,10),h=parseInt(f.find(H=>H.type==="month").value,10)-1,y=parseInt(f.find(H=>H.type==="day").value,10),E=new Date(Date.UTC(g,h,y,e,t,0)),$=i.formatToParts(E),v=parseInt($.find(H=>H.type==="hour").value,10),j=parseInt($.find(H=>H.type==="minute").value,10),N=(e-v)*60+(t-j);return E=new Date(E.getTime()+N*6e4),E}let a=o,c=i.formatToParts(a),d=parseInt(c.find(f=>f.type==="hour").value,10),u=parseInt(c.find(f=>f.type==="minute").value,10),p=(e-d)*60+(t-u);return a=new Date(a.getTime()+p*6e4),a}listReminders(e){let s=this.getAllReminders(e).map(r=>({reminderId:r.id,message:r.message,triggerAt:r.triggerAt}));return{success:!0,data:s,display:s.length===0?"No active reminders.":`Active reminders:
440
463
  ${s.map(r=>`- ${r.reminderId}: "${r.message}" (triggers at ${r.triggerAt})`).join(`
441
- `)}`}}cancelReminder(e,t){let s=e.reminderId;return!s||typeof s!="string"?{success:!1,error:'Missing required field "reminderId" for cancel action'}:this.getAllReminders(t).some(i=>i.id===s)?this.reminderRepo.cancel(s)?{success:!0,data:{reminderId:s},display:`Reminder "${s}" cancelled.`}:{success:!1,error:`Reminder "${s}" not found`}:{success:!1,error:`Reminder "${s}" not found`}}}});var qt,wc=_(()=>{"use strict";U();Me();qt=class extends R{static{m(this,"NoteSkill")}noteRepo;metadata={name:"note",category:"productivity",description:"Save, list, search, or delete persistent notes (stored in SQLite). Use when the user wants to write down or retrieve text notes, lists, or ideas.",riskLevel:"write",version:"2.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["save","list","search","delete"],description:"The note action to perform"},title:{type:"string",description:"The note title (required for save)"},content:{type:"string",description:"The note content (required for save)"},noteId:{type:"string",description:"The ID of the note to delete (required for delete)"},query:{type:"string",description:"Search query to filter notes (required for search)"}},required:["action"]}};constructor(e){super(),this.noteRepo=e}async execute(e,t){let s=e.action;switch(s){case"save":return this.saveNote(e,t);case"list":return this.listNotes(t);case"search":return this.searchNotes(e,t);case"delete":return this.deleteNote(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: save, list, search, delete`}}}saveNote(e,t){let s=e.title,r=e.content;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "title" for save action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "content" for save action'};let n=this.noteRepo.save(ne(t),s,r);return{success:!0,data:{noteId:n.id,title:n.title},display:`Note saved: "${s}"`}}listNotes(e){let t=new Set,s=[];for(let n of W(e))for(let o of this.noteRepo.list(n))t.has(o.id)||(t.add(o.id),s.push(o));if(s.length===0)return{success:!0,data:[],display:"No notes found."};let r=s.map(n=>`- **${n.title}** (${n.id.slice(0,8)}\u2026)
464
+ `)}`}}cancelReminder(e,t){let s=e.reminderId;return!s||typeof s!="string"?{success:!1,error:'Missing required field "reminderId" for cancel action'}:this.getAllReminders(t).some(i=>i.id===s)?this.reminderRepo.cancel(s)?{success:!0,data:{reminderId:s},display:`Reminder "${s}" cancelled.`}:{success:!1,error:`Reminder "${s}" not found`}:{success:!1,error:`Reminder "${s}" not found`}}}});var Wt,Ec=_(()=>{"use strict";U();Pe();Wt=class extends x{static{m(this,"NoteSkill")}noteRepo;metadata={name:"note",category:"productivity",description:"Save, list, search, or delete persistent notes (stored in SQLite). Use when the user wants to write down or retrieve text notes, lists, or ideas.",riskLevel:"write",version:"2.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["save","list","search","delete"],description:"The note action to perform"},title:{type:"string",description:"The note title (required for save)"},content:{type:"string",description:"The note content (required for save)"},noteId:{type:"string",description:"The ID of the note to delete (required for delete)"},query:{type:"string",description:"Search query to filter notes (required for search)"}},required:["action"]}};constructor(e){super(),this.noteRepo=e}async execute(e,t){let s=e.action;switch(s){case"save":return this.saveNote(e,t);case"list":return this.listNotes(t);case"search":return this.searchNotes(e,t);case"delete":return this.deleteNote(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: save, list, search, delete`}}}saveNote(e,t){let s=e.title,r=e.content;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "title" for save action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "content" for save action'};let n=this.noteRepo.save(ne(t),s,r);return{success:!0,data:{noteId:n.id,title:n.title},display:`Note saved: "${s}"`}}listNotes(e){let t=new Set,s=[];for(let n of G(e))for(let o of this.noteRepo.list(n))t.has(o.id)||(t.add(o.id),s.push(o));if(s.length===0)return{success:!0,data:[],display:"No notes found."};let r=s.map(n=>`- **${n.title}** (${n.id.slice(0,8)}\u2026)
442
465
  ${n.content.slice(0,100)}${n.content.length>100?"\u2026":""}`).join(`
443
466
  `);return{success:!0,data:s,display:`${s.length} note(s):
444
- ${r}`}}searchNotes(e,t){let s=e.query;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for search action'};let r=new Set,n=[];for(let i of W(t))for(let a of this.noteRepo.search(i,s))r.has(a.id)||(r.add(a.id),n.push(a));if(n.length===0)return{success:!0,data:[],display:`No notes matching "${s}".`};let o=n.map(i=>`- **${i.title}** (${i.id.slice(0,8)}\u2026)
467
+ ${r}`}}searchNotes(e,t){let s=e.query;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for search action'};let r=new Set,n=[];for(let i of G(t))for(let a of this.noteRepo.search(i,s))r.has(a.id)||(r.add(a.id),n.push(a));if(n.length===0)return{success:!0,data:[],display:`No notes matching "${s}".`};let o=n.map(i=>`- **${i.title}** (${i.id.slice(0,8)}\u2026)
445
468
  ${i.content.slice(0,100)}${i.content.length>100?"\u2026":""}`).join(`
446
469
  `);return{success:!0,data:n,display:`Found ${n.length} note(s):
447
- ${o}`}}deleteNote(e,t){let s=e.noteId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "noteId" for delete action'};let r=this.noteRepo.getById(s);return r?W(t).includes(r.userId)?this.noteRepo.delete(s)?{success:!0,data:{noteId:s},display:"Note deleted."}:{success:!1,error:`Note "${s}" not found`}:{success:!1,error:`Note "${s}" not found`}:{success:!1,error:`Note "${s}" not found`}}}});var Sm,zt,Tc=_(()=>{"use strict";U();Sm={0:"Clear sky",1:"Mainly clear",2:"Partly cloudy",3:"Overcast",45:"Foggy",48:"Depositing rime fog",51:"Light drizzle",53:"Moderate drizzle",55:"Dense drizzle",61:"Slight rain",63:"Moderate rain",65:"Heavy rain",71:"Slight snow",73:"Moderate snow",75:"Heavy snow",77:"Snow grains",80:"Slight rain showers",81:"Moderate rain showers",82:"Violent rain showers",85:"Slight snow showers",86:"Heavy snow showers",95:"Thunderstorm",96:"Thunderstorm with slight hail",99:"Thunderstorm with heavy hail"},zt=class extends R{static{m(this,"WeatherSkill")}metadata={name:"weather",category:"information",description:"Get current weather for any location. Uses Open-Meteo (free, no API key). Use when the user asks about weather, temperature, or conditions somewhere.",riskLevel:"read",version:"2.0.0",inputSchema:{type:"object",properties:{location:{type:"string",description:'City or place name (e.g. "Vienna", "New York", "Tokyo")'}},required:["location"]}};async execute(e,t){let s=e.location;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "location"'};try{let r=await this.geocode(s);if(!r)return{success:!1,error:`Location "${s}" not found`};let n=await this.fetchWeather(r.latitude,r.longitude),o=Sm[n.weathercode]??`Code ${n.weathercode}`,i=r.admin1?`${r.name}, ${r.admin1}, ${r.country}`:`${r.name}, ${r.country}`,a={location:i,temperature:n.temperature,unit:"\xB0C",condition:o,windSpeed:n.windspeed,windDirection:n.winddirection,isDay:n.is_day===1},c=`${i}: ${n.temperature}\xB0C, ${o}
448
- Wind: ${n.windspeed} km/h`;return{success:!0,data:a,display:c}}catch(r){return{success:!1,error:`Weather fetch failed: ${r instanceof Error?r.message:String(r)}`}}}async geocode(e){let t=`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(e)}&count=1&language=en&format=json`,s=await fetch(t);if(!s.ok)throw new Error(`Geocoding API returned ${s.status}`);return(await s.json()).results?.[0]}async fetchWeather(e,t){let s=`https://api.open-meteo.com/v1/forecast?latitude=${e}&longitude=${t}&current_weather=true&timezone=auto`,r=await fetch(s);if(!r.ok)throw new Error(`Weather API returned ${r.status}`);return(await r.json()).current_weather}}});import{exec as Am}from"node:child_process";function bc(l){return l.length>_c?l.slice(0,_c)+`
449
- [output truncated]`:l}var Im,_c,Wt,Ec=_(()=>{"use strict";U();Im=3e4,_c=1e4;m(bc,"truncate");Wt=class extends R{static{m(this,"ShellSkill")}metadata={name:"shell",category:"automation",description:"Execute shell commands on the host system. Use this for ANY task involving files, folders, system operations, or running programs: ls, cat, find, file, du, mkdir, cp, mv, grep, etc. When the user asks about their documents, files, or anything on disk \u2014 use this tool.",riskLevel:"admin",version:"1.0.0",inputSchema:{type:"object",properties:{command:{type:"string",description:"The shell command to execute"},timeout:{type:"number",description:"Timeout in milliseconds (default: 30000)"},cwd:{type:"string",description:"Working directory for the command"}},required:["command"]}};async execute(e,t){let s=e.command;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "command"'};let r=[/\brm\s+-rf\s+\/(?:\s|$)/,/\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+-[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*\s+-[a-zA-Z]*r)[a-zA-Z]*\s+\/(?:\s|$)/,/\brm\s+-rf\s+\/\*/,/:(){ :|:& };:/,/:\(\)\s*\{.*\|.*&\s*\}\s*;/,/>\s*\/dev\/sd[a-z]/,/\bmkfs\b/,/\bdd\s+.*\bif=/,/\bchmod\s+777\b/,/\bcurl\b.*\|\s*\bbash\b/,/\bwget\b.*\|\s*\bsh\b/,/\bpython[23]?\s+-c\b/,/\bnode\s+-e\b/,/\b(bash|sh)\s+-i\b.*\/dev\/tcp/,/\bnc\s+.*-e\b/];for(let i of r)if(i.test(s))return{success:!1,error:"Command blocked: potentially destructive system operation"};let n=typeof e.timeout=="number"&&e.timeout>0?e.timeout:Im,o=typeof e.cwd=="string"&&e.cwd.length>0?e.cwd:void 0;try{let{stdout:i,stderr:a,exitCode:c}=await this.run(s,n,o),d=[];return i&&d.push(`stdout:
450
- ${bc(i)}`),a&&d.push(`stderr:
451
- ${bc(a)}`),d.length===0&&d.push("(no output)"),d.push(`exit code: ${c}`),{success:c===0,data:{stdout:i,stderr:a,exitCode:c},display:d.join(`
470
+ ${o}`}}deleteNote(e,t){let s=e.noteId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "noteId" for delete action'};let r=this.noteRepo.getById(s);return r?G(t).includes(r.userId)?this.noteRepo.delete(s)?{success:!0,data:{noteId:s},display:"Note deleted."}:{success:!1,error:`Note "${s}" not found`}:{success:!1,error:`Note "${s}" not found`}:{success:!1,error:`Note "${s}" not found`}}}});var Rm,Gt,$c=_(()=>{"use strict";U();Rm={0:"Clear sky",1:"Mainly clear",2:"Partly cloudy",3:"Overcast",45:"Foggy",48:"Depositing rime fog",51:"Light drizzle",53:"Moderate drizzle",55:"Dense drizzle",61:"Slight rain",63:"Moderate rain",65:"Heavy rain",71:"Slight snow",73:"Moderate snow",75:"Heavy snow",77:"Snow grains",80:"Slight rain showers",81:"Moderate rain showers",82:"Violent rain showers",85:"Slight snow showers",86:"Heavy snow showers",95:"Thunderstorm",96:"Thunderstorm with slight hail",99:"Thunderstorm with heavy hail"},Gt=class extends x{static{m(this,"WeatherSkill")}metadata={name:"weather",category:"information",description:"Get current weather for any location. Uses Open-Meteo (free, no API key). Use when the user asks about weather, temperature, or conditions somewhere.",riskLevel:"read",version:"2.0.0",inputSchema:{type:"object",properties:{location:{type:"string",description:'City or place name (e.g. "Vienna", "New York", "Tokyo")'}},required:["location"]}};async execute(e,t){let s=e.location;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "location"'};try{let r=await this.geocode(s);if(!r)return{success:!1,error:`Location "${s}" not found`};let n=await this.fetchWeather(r.latitude,r.longitude),o=Rm[n.weathercode]??`Code ${n.weathercode}`,i=r.admin1?`${r.name}, ${r.admin1}, ${r.country}`:`${r.name}, ${r.country}`,a={location:i,temperature:n.temperature,unit:"\xB0C",condition:o,windSpeed:n.windspeed,windDirection:n.winddirection,isDay:n.is_day===1},c=`${i}: ${n.temperature}\xB0C, ${o}
471
+ Wind: ${n.windspeed} km/h`;return{success:!0,data:a,display:c}}catch(r){return{success:!1,error:`Weather fetch failed: ${r instanceof Error?r.message:String(r)}`}}}async geocode(e){let t=`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(e)}&count=1&language=en&format=json`,s=await fetch(t);if(!s.ok)throw new Error(`Geocoding API returned ${s.status}`);return(await s.json()).results?.[0]}async fetchWeather(e,t){let s=`https://api.open-meteo.com/v1/forecast?latitude=${e}&longitude=${t}&current_weather=true&timezone=auto`,r=await fetch(s);if(!r.ok)throw new Error(`Weather API returned ${r.status}`);return(await r.json()).current_weather}}});import{exec as Cm}from"node:child_process";function vc(l){return l.length>kc?l.slice(0,kc)+`
472
+ [output truncated]`:l}var Dm,kc,Vt,Sc=_(()=>{"use strict";U();Dm=3e4,kc=1e4;m(vc,"truncate");Vt=class extends x{static{m(this,"ShellSkill")}metadata={name:"shell",category:"automation",description:"Execute shell commands on the host system. Use this for ANY task involving files, folders, system operations, or running programs: ls, cat, find, file, du, mkdir, cp, mv, grep, etc. When the user asks about their documents, files, or anything on disk \u2014 use this tool.",riskLevel:"admin",version:"1.0.0",inputSchema:{type:"object",properties:{command:{type:"string",description:"The shell command to execute"},timeout:{type:"number",description:"Timeout in milliseconds (default: 30000)"},cwd:{type:"string",description:"Working directory for the command"}},required:["command"]}};async execute(e,t){let s=e.command;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "command"'};let r=[/\brm\s+-rf\s+\/(?:\s|$)/,/\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+-[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*\s+-[a-zA-Z]*r)[a-zA-Z]*\s+\/(?:\s|$)/,/\brm\s+-rf\s+\/\*/,/:(){ :|:& };:/,/:\(\)\s*\{.*\|.*&\s*\}\s*;/,/>\s*\/dev\/sd[a-z]/,/\bmkfs\b/,/\bdd\s+.*\bif=/,/\bchmod\s+777\b/,/\bcurl\b.*\|\s*\bbash\b/,/\bwget\b.*\|\s*\bsh\b/,/\bpython[23]?\s+-c\b/,/\bnode\s+-e\b/,/\b(bash|sh)\s+-i\b.*\/dev\/tcp/,/\bnc\s+.*-e\b/];for(let i of r)if(i.test(s))return{success:!1,error:"Command blocked: potentially destructive system operation"};let n=typeof e.timeout=="number"&&e.timeout>0?e.timeout:Dm,o=typeof e.cwd=="string"&&e.cwd.length>0?e.cwd:void 0;try{let{stdout:i,stderr:a,exitCode:c}=await this.run(s,n,o),d=[];return i&&d.push(`stdout:
473
+ ${vc(i)}`),a&&d.push(`stderr:
474
+ ${vc(a)}`),d.length===0&&d.push("(no output)"),d.push(`exit code: ${c}`),{success:c===0,data:{stdout:i,stderr:a,exitCode:c},display:d.join(`
452
475
 
453
- `),...c!==0&&{error:`Command exited with code ${c}`}}}catch(i){return{success:!1,error:`Shell execution failed: ${i instanceof Error?i.message:String(i)}`}}}run(e,t,s){return new Promise(r=>{Am(e,{timeout:t,cwd:s},(n,o,i)=>{let a=n&&"code"in n&&typeof n.code=="number"?n.code:n?1:0;r({stdout:typeof o=="string"?o:"",stderr:typeof i=="string"?i:"",exitCode:a})})})}}});var Gt,$c=_(()=>{"use strict";U();Me();Gt=class extends R{static{m(this,"MemorySkill")}memoryRepo;embeddingService;metadata={name:"memory",category:"core",description:"Store and retrieve persistent memories. Use this to remember user preferences, facts, and important information across conversations.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["save","recall","search","list","delete","semantic_search"],description:"The memory action to perform"},key:{type:"string",description:"The memory key/label"},value:{type:"string",description:"The value to remember (for save)"},category:{type:"string",description:"Optional category (for save/list)"},query:{type:"string",description:"Search query (for search)"}},required:["action"]}};constructor(e,t){super(),this.memoryRepo=e,this.embeddingService=t}async execute(e,t){let s=e.action;switch(s){case"save":return this.saveMemory(e,t);case"recall":return this.recallMemory(e,t);case"search":return this.searchMemories(e,t);case"list":return this.listMemories(e,t);case"delete":return this.deleteMemory(e,t);case"semantic_search":return this.semanticSearchMemories(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: save, recall, search, list, delete, semantic_search`}}}saveMemory(e,t){let s=e.key,r=e.value,n=e.category;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "key" for save action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "value" for save action'};let o=this.memoryRepo.save(ne(t),s,r,n??"general");return this.embeddingService&&this.embeddingService.embedAndStore(ne(t),`${s}: ${r}`,"memory",s).catch(()=>{}),{success:!0,data:o,display:`Remembered "${s}" = "${r}" (category: ${o.category})`}}recallMemory(e,t){let s=e.key;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "key" for recall action'};let r;for(let n of W(t))if(r=this.memoryRepo.recall(n,s),r)break;return r?{success:!0,data:r,display:`${s} = "${r.value}" (category: ${r.category}, updated: ${r.updatedAt})`}:{success:!0,data:null,display:`No memory found for key "${s}".`}}searchMemories(e,t){let s=e.query;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for search action'};let r=new Set,n=[];for(let o of W(t))for(let i of this.memoryRepo.search(o,s))r.has(i.id)||(r.add(i.id),n.push(i));return{success:!0,data:n,display:n.length===0?`No memories matching "${s}".`:`Found ${n.length} memory(ies):
476
+ `),...c!==0&&{error:`Command exited with code ${c}`}}}catch(i){return{success:!1,error:`Shell execution failed: ${i instanceof Error?i.message:String(i)}`}}}run(e,t,s){return new Promise(r=>{Cm(e,{timeout:t,cwd:s},(n,o,i)=>{let a=n&&"code"in n&&typeof n.code=="number"?n.code:n?1:0;r({stdout:typeof o=="string"?o:"",stderr:typeof i=="string"?i:"",exitCode:a})})})}}});var Kt,Ac=_(()=>{"use strict";U();Pe();Kt=class extends x{static{m(this,"MemorySkill")}memoryRepo;embeddingService;metadata={name:"memory",category:"core",description:"Store and retrieve persistent memories. Use this to remember user preferences, facts, and important information across conversations.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["save","recall","search","list","delete","semantic_search"],description:"The memory action to perform"},key:{type:"string",description:"The memory key/label"},value:{type:"string",description:"The value to remember (for save)"},category:{type:"string",description:"Optional category (for save/list)"},query:{type:"string",description:"Search query (for search)"}},required:["action"]}};constructor(e,t){super(),this.memoryRepo=e,this.embeddingService=t}async execute(e,t){let s=e.action;switch(s){case"save":return this.saveMemory(e,t);case"recall":return this.recallMemory(e,t);case"search":return this.searchMemories(e,t);case"list":return this.listMemories(e,t);case"delete":return this.deleteMemory(e,t);case"semantic_search":return this.semanticSearchMemories(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: save, recall, search, list, delete, semantic_search`}}}saveMemory(e,t){let s=e.key,r=e.value,n=e.category;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "key" for save action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "value" for save action'};let o=this.memoryRepo.save(ne(t),s,r,n??"general");return this.embeddingService&&this.embeddingService.embedAndStore(ne(t),`${s}: ${r}`,"memory",s).catch(()=>{}),{success:!0,data:o,display:`Remembered "${s}" = "${r}" (category: ${o.category})`}}recallMemory(e,t){let s=e.key;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "key" for recall action'};let r;for(let n of G(t))if(r=this.memoryRepo.recall(n,s),r)break;return r?{success:!0,data:r,display:`${s} = "${r.value}" (category: ${r.category}, updated: ${r.updatedAt})`}:{success:!0,data:null,display:`No memory found for key "${s}".`}}searchMemories(e,t){let s=e.query;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for search action'};let r=new Set,n=[];for(let o of G(t))for(let i of this.memoryRepo.search(o,s))r.has(i.id)||(r.add(i.id),n.push(i));return{success:!0,data:n,display:n.length===0?`No memories matching "${s}".`:`Found ${n.length} memory(ies):
454
477
  ${n.map(o=>`- ${o.key}: "${o.value}"`).join(`
455
- `)}`}}listMemories(e,t){let s=e.category,r=new Set,n=[];for(let i of W(t)){let a=s&&typeof s=="string"?this.memoryRepo.listByCategory(i,s):this.memoryRepo.listAll(i);for(let c of a)r.has(c.id)||(r.add(c.id),n.push(c))}let o=s?`in category "${s}"`:"total";return{success:!0,data:n,display:n.length===0?`No memories found${s?` in category "${s}"`:""}.`:`${n.length} memory(ies) ${o}:
478
+ `)}`}}listMemories(e,t){let s=e.category,r=new Set,n=[];for(let i of G(t)){let a=s&&typeof s=="string"?this.memoryRepo.listByCategory(i,s):this.memoryRepo.listAll(i);for(let c of a)r.has(c.id)||(r.add(c.id),n.push(c))}let o=s?`in category "${s}"`:"total";return{success:!0,data:n,display:n.length===0?`No memories found${s?` in category "${s}"`:""}.`:`${n.length} memory(ies) ${o}:
456
479
  ${n.map(i=>`- [${i.category}] ${i.key}: "${i.value}"`).join(`
457
- `)}`}}deleteMemory(e,t){let s=e.key;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "key" for delete action'};let r=!1;for(let n of W(t))if(this.memoryRepo.delete(n,s)){r=!0;break}return{success:!0,data:{key:s,deleted:r},display:r?`Memory "${s}" deleted.`:`No memory found for key "${s}".`}}async semanticSearchMemories(e,t){let s=e.query;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for semantic_search action'};if(!this.embeddingService)return this.searchMemories(e,t);let r=new Set,n=[];for(let o of W(t))for(let i of await this.embeddingService.semanticSearch(o,s,10))r.has(i.key)||(r.add(i.key),n.push(i));return n.length===0?this.searchMemories(e,t):{success:!0,data:n,display:`Found ${n.length} semantically related memory(ies):
480
+ `)}`}}deleteMemory(e,t){let s=e.key;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "key" for delete action'};let r=!1;for(let n of G(t))if(this.memoryRepo.delete(n,s)){r=!0;break}return{success:!0,data:{key:s,deleted:r},display:r?`Memory "${s}" deleted.`:`No memory found for key "${s}".`}}async semanticSearchMemories(e,t){let s=e.query;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for semantic_search action'};if(!this.embeddingService)return this.searchMemories(e,t);let r=new Set,n=[];for(let o of G(t))for(let i of await this.embeddingService.semanticSearch(o,s,10))r.has(i.key)||(r.add(i.key),n.push(i));return n.length===0?this.searchMemories(e,t):{success:!0,data:n,display:`Found ${n.length} semantically related memory(ies):
458
481
  ${n.map(o=>`- ${o.key}: "${o.value}" (score: ${o.score.toFixed(2)})`).join(`
459
- `)}`}}}});var xm,Rm,Cm,Vt,kc=_(()=>{"use strict";U();Xo();xm=15,Rm=25,Cm=12e4,Vt=class extends R{static{m(this,"DelegateSkill")}llm;skillRegistry;skillSandbox;securityManager;metadata={name:"delegate",category:"core",description:'Delegate a complex sub-task to an autonomous sub-agent that has full tool access. The sub-agent can use shell, web search, calculator, memory, email, and all other tools. Use when a task is independent enough to run in parallel or when it requires a focused, multi-step workflow (e.g. "research X and summarize", "find all TODO files and list them", "check the weather and draft a packing list"). Control depth with max_iterations (default 15, max 25).',riskLevel:"write",version:"3.0.0",timeoutMs:Cm,inputSchema:{type:"object",properties:{task:{type:"string",description:"The task to delegate to the sub-agent. Be specific about what you want."},context:{type:"string",description:"Additional context the sub-agent needs (optional)"},max_iterations:{type:"number",description:"Max tool iterations (1-25). Default: 15."}},required:["task"]}};onProgress;constructor(e,t,s,r){super(),this.llm=e,this.skillRegistry=t,this.skillSandbox=s,this.securityManager=r}setProgressCallback(e){this.onProgress=e}createTracker(){return new Et(this.onProgress)}async execute(e,t){let s=e.task,r=e.context;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "task"'};let n=e.max_iterations,o=n?Math.max(1,Math.min(Rm,Math.round(n))):xm,i=t.onProgress??this.onProgress,a=t.tracker?t.tracker:new Et(i);a.ping("starting",{maxIterations:o});let c=this.buildSubAgentTools(),d=new Map,u=0,p=`You are a sub-agent of Alfred, a personal AI assistant. Complete the assigned task using the tools available to you. Work step by step: use tools to gather information, then synthesize a clear result. Be concise and return only the final answer when done.
482
+ `)}`}}}});var Lm,Nm,Mm,Xt,Ic=_(()=>{"use strict";U();Jo();Lm=15,Nm=25,Mm=12e4,Xt=class extends x{static{m(this,"DelegateSkill")}llm;skillRegistry;skillSandbox;securityManager;metadata={name:"delegate",category:"core",description:'Delegate a complex sub-task to an autonomous sub-agent that has full tool access. The sub-agent can use shell, web search, calculator, memory, email, and all other tools. Use when a task is independent enough to run in parallel or when it requires a focused, multi-step workflow (e.g. "research X and summarize", "find all TODO files and list them", "check the weather and draft a packing list"). Control depth with max_iterations (default 15, max 25).',riskLevel:"write",version:"3.0.0",timeoutMs:Mm,inputSchema:{type:"object",properties:{task:{type:"string",description:"The task to delegate to the sub-agent. Be specific about what you want."},context:{type:"string",description:"Additional context the sub-agent needs (optional)"},max_iterations:{type:"number",description:"Max tool iterations (1-25). Default: 15."}},required:["task"]}};onProgress;constructor(e,t,s,r){super(),this.llm=e,this.skillRegistry=t,this.skillSandbox=s,this.securityManager=r}setProgressCallback(e){this.onProgress=e}createTracker(){return new $t(this.onProgress)}async execute(e,t){let s=e.task,r=e.context;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "task"'};let n=e.max_iterations,o=n?Math.max(1,Math.min(Nm,Math.round(n))):Lm,i=t.onProgress??this.onProgress,a=t.tracker?t.tracker:new $t(i);a.ping("starting",{maxIterations:o});let c=this.buildSubAgentTools(),d=new Map,u=0,p=`You are a sub-agent of Alfred, a personal AI assistant. Complete the assigned task using the tools available to you. Work step by step: use tools to gather information, then synthesize a clear result. Be concise and return only the final answer when done.
460
483
 
461
484
  When tool results contain "[Data stored as result_N]", use code_sandbox with action "run_with_data" and data="result_N" to process the data. The data will be injected as INPUT_DATA (parsed array/object). Never hardcode data in code.
462
- Available JS libraries in code_sandbox (no install needed): exceljs, pdfkit, pdf-parse.`,g=s;r&&typeof r=="string"&&(g=`${s}
485
+ Available JS libraries in code_sandbox (no install needed): exceljs, pdfkit, pdf-parse.`,f=s;r&&typeof r=="string"&&(f=`${s}
463
486
 
464
- Additional context: ${r}`);let f=[{role:"user",content:g}];try{let h=0,y=0,E=0;for(;;){a.ping("llm_call",{iteration:h,maxIterations:o});let $=await this.llm.complete({messages:f,system:p,tools:c.length>0?c:void 0,maxTokens:8192,tier:"strong"});if(y+=$.usage.inputTokens,E+=$.usage.outputTokens,a.ping("processing",{iteration:h,maxIterations:o}),!$.toolCalls||$.toolCalls.length===0||h>=o)return a.ping("done",{iteration:h,maxIterations:o}),{success:!0,data:{response:$.content,iterations:h,usage:{inputTokens:y,outputTokens:E}},display:$.content};h++;let v=[];$.content&&v.push({type:"text",text:$.content});for(let x of $.toolCalls)v.push({type:"tool_use",id:x.id,name:x.name,input:x.input});f.push({role:"assistant",content:v});let P=[];for(let x of $.toolCalls){a.ping("tool_call",{iteration:h,maxIterations:o,tool:x.name});let q=x.input;if(x.name==="code_sandbox"&&x.input.data){let O=String(x.input.data);d.has(O)&&(q={...x.input,data:d.get(O)},q.action==="run"&&(q.action="run_with_data"))}let J=await this.executeSubAgentTool({...x,input:q},t),oe=J.content;if(!J.isError&&oe.length>500){let O=`result_${++u}`,se=J.rawData!=null?JSON.stringify(J.rawData):oe;d.set(O,se),oe+=`
487
+ Additional context: ${r}`);let g=[{role:"user",content:f}];try{let h=0,y=0,E=0;for(;;){a.ping("llm_call",{iteration:h,maxIterations:o});let $=await this.llm.complete({messages:g,system:p,tools:c.length>0?c:void 0,maxTokens:8192,tier:"strong"});if(y+=$.usage.inputTokens,E+=$.usage.outputTokens,a.ping("processing",{iteration:h,maxIterations:o}),!$.toolCalls||$.toolCalls.length===0||h>=o)return a.ping("done",{iteration:h,maxIterations:o}),{success:!0,data:{response:$.content,iterations:h,usage:{inputTokens:y,outputTokens:E}},display:$.content};h++;let v=[];$.content&&v.push({type:"text",text:$.content});for(let N of $.toolCalls)v.push({type:"tool_use",id:N.id,name:N.name,input:N.input});g.push({role:"assistant",content:v});let j=[];for(let N of $.toolCalls){a.ping("tool_call",{iteration:h,maxIterations:o,tool:N.name});let H=N.input;if(N.name==="code_sandbox"&&N.input.data){let M=String(N.input.data);d.has(M)&&(H={...N.input,data:d.get(M)},H.action==="run"&&(H.action="run_with_data"))}let D=await this.executeSubAgentTool({...N,input:H},t),oe=D.content;if(!D.isError&&oe.length>500){let M=`result_${++u}`,re=D.rawData!=null?JSON.stringify(D.rawData):oe;d.set(M,re),oe+=`
465
488
 
466
- [Data stored as "${O}" \u2014 use code_sandbox action "run_with_data" with data="${O}" to process this data. Do NOT copy data into code.]`}P.push({type:"tool_result",tool_use_id:x.id,content:oe,is_error:J.isError})}f.push({role:"user",content:P})}}catch(h){return{success:!1,error:`Sub-agent failed: ${h instanceof Error?h.message:String(h)}`}}}buildSubAgentTools(){return this.skillRegistry?this.skillRegistry.getAll().filter(e=>e.metadata.name!=="delegate").map(e=>({name:e.metadata.name,description:e.metadata.description,inputSchema:e.metadata.inputSchema})):[]}async executeSubAgentTool(e,t){let s=this.skillRegistry?.get(e.name);if(!s)return{content:`Error: Unknown tool "${e.name}"`,isError:!0};if(this.securityManager){let r=this.securityManager.evaluate({userId:t.userId,action:e.name,riskLevel:s.metadata.riskLevel,platform:t.platform,chatId:t.chatId,chatType:t.chatType});if(!r.allowed)return{content:`Access denied: ${r.reason}`,isError:!0}}if(this.skillSandbox){let r=await this.skillSandbox.execute(s,e.input,t);return{content:r.display??(r.success?JSON.stringify(r.data):r.error??"Unknown error"),isError:!r.success,rawData:r.data}}try{let r=await s.execute(e.input,t);return{content:r.display??(r.success?JSON.stringify(r.data):r.error??"Unknown error"),isError:!r.success,rawData:r.data}}catch(r){return{content:`Skill execution failed: ${r instanceof Error?r.message:String(r)}`,isError:!0}}}}});var Xe,ln=_(()=>{"use strict";Xe=class{static{m(this,"EmailProvider")}async extractFromSearch(e,t,s,r,n){throw new Error("Extract is not supported by this email provider.")}async createDraft(e){throw new Error("Draft creation is not supported by this email provider.")}async forwardMessage(e,t,s){throw new Error("Email forwarding is not supported by this email provider.")}}});var vc={};ue(vc,{MicrosoftGraphEmailProvider:()=>dn});var dn,Jo=_(()=>{"use strict";ln();dn=class extends Xe{static{m(this,"MicrosoftGraphEmailProvider")}config;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){if(!this.config.refreshToken)throw new Error("Microsoft email: refreshToken is missing from config");let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Mail.ReadWrite https://graph.microsoft.com/Mail.Send offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok){let n=await s.text().catch(()=>"");throw new Error(`Microsoft token refresh failed: ${s.status} \u2014 ${n.slice(0,300)}`)}let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=m(n=>fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json",...t.headers}}),"doFetch"),r=await s(this.accessToken);if(r.status===401){await this.refreshAccessToken();let n=await s(this.accessToken);if(!n.ok)throw new Error(`Graph API error: ${n.status}`);return this.parseJsonOrUndefined(n)}if(!r.ok)throw new Error(`Graph API error: ${r.status}`);return this.parseJsonOrUndefined(r)}async parseJsonOrUndefined(e){if(e.status===204||e.status===202)return;let t=await e.text();if(!(!t||t.length===0))return JSON.parse(t)}async graphRequestRaw(e){let t=m(n=>fetch(`https://graph.microsoft.com/v1.0${e}`,{headers:{Authorization:`Bearer ${n}`}}),"doFetch"),s=await t(this.accessToken);if(s.status===401&&(await this.refreshAccessToken(),s=await t(this.accessToken)),!s.ok)throw new Error(`Graph API error: ${s.status}`);let r=await s.arrayBuffer();return Buffer.from(r)}async fetchInbox(e){let t=new URLSearchParams({$top:String(Math.min(Math.max(1,e),50)),$orderby:"receivedDateTime desc",$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"});return((await this.graphRequest(`/me/mailFolders/inbox/messages?${t}`)).value??[]).map(r=>this.mapMessage(r))}async readMessage(e){let t=new URLSearchParams({$select:"id,from,toRecipients,ccRecipients,bccRecipients,subject,body,receivedDateTime,isRead,hasAttachments"}),s=await this.graphRequest(`/me/messages/${e}?${t}`),r=[];return s.hasAttachments&&(r=((await this.graphRequest(`/me/messages/${e}/attachments?$select=id,name,contentType,size`)).value??[]).map(o=>({id:o.id,name:o.name,contentType:o.contentType,size:o.size??0}))),{id:s.id,from:this.formatGraphAddress(s.from),to:(s.toRecipients??[]).map(n=>this.formatGraphAddress(n)),subject:s.subject??"(no subject)",date:new Date(s.receivedDateTime),read:s.isRead??!1,body:s.body?.contentType==="html"?this.stripHtml(s.body.content??""):s.body?.content??"",bodyHtml:s.body?.contentType==="html"?s.body.content:void 0,cc:s.ccRecipients?.map(n=>this.formatGraphAddress(n)),bcc:s.bccRecipients?.map(n=>this.formatGraphAddress(n)),hasAttachments:s.hasAttachments,attachments:r}}async searchMessages(e,t){let s=Math.min(Math.max(1,t),50),r=new URLSearchParams({$search:`"${e}"`,$top:String(s),$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"}),n=await this.graphRequest(`/me/messages?${r}`),o=(n.value??[]).map(i=>this.mapMessage(i));if(t>50){let i=n["@odata.nextLink"];for(;i&&o.length<t;){let a=await this.graphRequest(i.replace("https://graph.microsoft.com/v1.0","")),c=(a.value??[]).map(d=>this.mapMessage(d));o.push(...c),i=a["@odata.nextLink"]}}return o.slice(0,t)}async extractFromSearch(e,t,s,r,n){let o=await this.searchWithDateFilter(e,t,r,n),i=s.includes("amount"),a=[];if(i)for(let d=0;d<o.length;d+=5){let u=o.slice(d,d+5),p=await Promise.allSettled(u.map(async g=>{let f={id:g.id,from:g.from,subject:g.subject,date:g.date.toISOString().split("T")[0],preview:(g.preview??"").slice(0,200)};try{let h=await this.readMessage(g.id),y=this.extractAmount(h.body);y&&(f.amount=y.amount,f.currency=y.currency)}catch{}return f}));for(let g of p)g.status==="fulfilled"&&a.push(g.value)}else for(let c of o)a.push({id:c.id,from:c.from,subject:c.subject,date:c.date.toISOString().split("T")[0],preview:(c.preview??"").slice(0,200)});return a}async searchWithDateFilter(e,t,s,r){let n=Math.min(Math.max(1,t),50),o=[];if(s||r){let p=s?this.toKqlDate(s):"01/01/2000",g=r?this.toKqlDate(r):"12/31/2099";o.push(`received:${p}..${g}`)}e&&o.push(e.includes(" ")?`(${e})`:e);let i=o.join(" AND "),a=new URLSearchParams({$top:String(n),$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"});i?a.set("$search",`"${i}"`):a.set("$orderby","receivedDateTime desc");let c=await this.graphRequest(`/me/messages?${a}`),d=(c.value??[]).map(p=>this.mapMessage(p)),u=c["@odata.nextLink"];for(;u&&d.length<t;){let p=await this.graphRequest(u.replace("https://graph.microsoft.com/v1.0","")),g=(p.value??[]).map(f=>this.mapMessage(f));d.push(...g),u=p["@odata.nextLink"]}return d.slice(0,t)}toKqlDate(e){let[t,s,r]=e.split("-");return`${s}/${r}/${t}`}extractAmount(e){let t=[{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*€\s*([\d.,]+)/i,currency:"EUR"},{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*([\d.,]+)\s*€/i,currency:"EUR"},{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*EUR\s*([\d.,]+)/i,currency:"EUR"},{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*([\d.,]+)\s*EUR/i,currency:"EUR"},{regex:/(?:total|amount|charged|price|subtotal)[:\s]*\$\s*([\d.,]+)/i,currency:"USD"},{regex:/(?:total|amount|charged|price|subtotal)[:\s]*USD\s*([\d.,]+)/i,currency:"USD"},{regex:/(?:total|amount|charged|price|subtotal)[:\s]*([\d.,]+)\s*USD/i,currency:"USD"},{regex:/€\s*([\d]+[.,]\d{2})\b/,currency:"EUR"},{regex:/\b([\d]+[.,]\d{2})\s*€/,currency:"EUR"},{regex:/EUR\s*([\d]+[.,]\d{2})\b/,currency:"EUR"},{regex:/\$([\d]+[.,]\d{2})\b/,currency:"USD"},{regex:/USD\s*([\d]+[.,]\d{2})\b/,currency:"USD"}];for(let{regex:s,currency:r}of t){let n=e.match(s);if(n?.[1])return{amount:n[1].trim(),currency:r}}return null}async sendMessage(e){if(e.replyTo)return await this.graphRequest(`/me/messages/${e.replyTo}/reply`,{method:"POST",body:JSON.stringify({comment:e.body})}),{messageId:e.replyTo};let t={subject:e.subject,body:{contentType:e.isHtml?"html":"text",content:e.body},toRecipients:e.to.split(",").map(s=>({emailAddress:{address:s.trim()}}))};return e.cc&&(t.ccRecipients=e.cc.split(",").map(s=>({emailAddress:{address:s.trim()}}))),await this.graphRequest("/me/sendMail",{method:"POST",body:JSON.stringify({message:t})}),{messageId:`sent-${Date.now()}`}}async createDraft(e){if(e.replyTo)return{messageId:(await this.graphRequest(`/me/messages/${e.replyTo}/createReply`,{method:"POST",body:JSON.stringify({comment:e.body})}))?.id??e.replyTo};let t={subject:e.subject,body:{contentType:e.isHtml?"html":"text",content:e.body},toRecipients:e.to.split(",").map(r=>({emailAddress:{address:r.trim()}}))};return e.cc&&(t.ccRecipients=e.cc.split(",").map(r=>({emailAddress:{address:r.trim()}}))),{messageId:(await this.graphRequest("/me/messages",{method:"POST",body:JSON.stringify(t)}))?.id??`draft-${Date.now()}`}}async listFolders(){return((await this.graphRequest("/me/mailFolders?$select=displayName&$top=100")).value??[]).map(t=>t.displayName)}async fetchFolder(e,t){let r=((await this.graphRequest("/me/mailFolders?$select=id,displayName&$top=100")).value??[]).find(i=>i.displayName.toLowerCase()===e.toLowerCase());if(!r)throw new Error(`Folder "${e}" not found. Use the 'folders' action to list available folders.`);let n=new URLSearchParams({$top:String(Math.min(Math.max(1,t),50)),$orderby:"receivedDateTime desc",$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"});return((await this.graphRequest(`/me/mailFolders/${r.id}/messages?${n}`)).value??[]).map(i=>this.mapMessage(i))}async downloadAttachment(e,t){return this.graphRequestRaw(`/me/messages/${e}/attachments/${t}/$value`)}async forwardMessage(e,t,s){return await this.graphRequest(`/me/messages/${e}/forward`,{method:"POST",body:JSON.stringify({comment:s??"",toRecipients:t.split(",").map(r=>({emailAddress:{address:r.trim()}}))})}),{messageId:`fwd-${Date.now()}`}}mapMessage(e){return{id:e.id,from:this.formatGraphAddress(e.from),to:(e.toRecipients??[]).map(t=>this.formatGraphAddress(t)),subject:e.subject??"(no subject)",date:new Date(e.receivedDateTime),read:e.isRead??!1,preview:e.bodyPreview??void 0,hasAttachments:e.hasAttachments??!1}}formatGraphAddress(e){if(!e)return"unknown";let t=e.emailAddress??e;return t?.address?t.name?`${t.name} <${t.address}>`:t.address:"unknown"}stripHtml(e){return e.replace(/<br\s*\/?>/gi,`
489
+ [Data stored as "${M}" \u2014 use code_sandbox action "run_with_data" with data="${M}" to process this data. Do NOT copy data into code.]`}j.push({type:"tool_result",tool_use_id:N.id,content:oe,is_error:D.isError})}g.push({role:"user",content:j})}}catch(h){return{success:!1,error:`Sub-agent failed: ${h instanceof Error?h.message:String(h)}`}}}buildSubAgentTools(){return this.skillRegistry?this.skillRegistry.getAll().filter(e=>e.metadata.name!=="delegate").map(e=>({name:e.metadata.name,description:e.metadata.description,inputSchema:e.metadata.inputSchema})):[]}async executeSubAgentTool(e,t){let s=this.skillRegistry?.get(e.name);if(!s)return{content:`Error: Unknown tool "${e.name}"`,isError:!0};if(this.securityManager){let r=this.securityManager.evaluate({userId:t.userId,action:e.name,riskLevel:s.metadata.riskLevel,platform:t.platform,chatId:t.chatId,chatType:t.chatType});if(!r.allowed)return{content:`Access denied: ${r.reason}`,isError:!0}}if(this.skillSandbox){let r=await this.skillSandbox.execute(s,e.input,t);return{content:r.display??(r.success?JSON.stringify(r.data):r.error??"Unknown error"),isError:!r.success,rawData:r.data}}try{let r=await s.execute(e.input,t);return{content:r.display??(r.success?JSON.stringify(r.data):r.error??"Unknown error"),isError:!r.success,rawData:r.data}}catch(r){return{content:`Skill execution failed: ${r instanceof Error?r.message:String(r)}`,isError:!0}}}}});var Ye,un=_(()=>{"use strict";Ye=class{static{m(this,"EmailProvider")}async extractFromSearch(e,t,s,r,n){throw new Error("Extract is not supported by this email provider.")}async createDraft(e){throw new Error("Draft creation is not supported by this email provider.")}async forwardMessage(e,t,s){throw new Error("Email forwarding is not supported by this email provider.")}}});var xc={};le(xc,{MicrosoftGraphEmailProvider:()=>mn});var mn,Qo=_(()=>{"use strict";un();mn=class extends Ye{static{m(this,"MicrosoftGraphEmailProvider")}config;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){if(!this.config.refreshToken)throw new Error("Microsoft email: refreshToken is missing from config");let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Mail.ReadWrite https://graph.microsoft.com/Mail.Send offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok){let n=await s.text().catch(()=>"");throw new Error(`Microsoft token refresh failed: ${s.status} \u2014 ${n.slice(0,300)}`)}let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=m(n=>fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json",...t.headers}}),"doFetch"),r=await s(this.accessToken);if(r.status===401){await this.refreshAccessToken();let n=await s(this.accessToken);if(!n.ok)throw new Error(`Graph API error: ${n.status}`);return this.parseJsonOrUndefined(n)}if(!r.ok)throw new Error(`Graph API error: ${r.status}`);return this.parseJsonOrUndefined(r)}async parseJsonOrUndefined(e){if(e.status===204||e.status===202)return;let t=await e.text();if(!(!t||t.length===0))return JSON.parse(t)}async graphRequestRaw(e){let t=m(n=>fetch(`https://graph.microsoft.com/v1.0${e}`,{headers:{Authorization:`Bearer ${n}`}}),"doFetch"),s=await t(this.accessToken);if(s.status===401&&(await this.refreshAccessToken(),s=await t(this.accessToken)),!s.ok)throw new Error(`Graph API error: ${s.status}`);let r=await s.arrayBuffer();return Buffer.from(r)}async fetchInbox(e){let t=new URLSearchParams({$top:String(Math.min(Math.max(1,e),50)),$orderby:"receivedDateTime desc",$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"});return((await this.graphRequest(`/me/mailFolders/inbox/messages?${t}`)).value??[]).map(r=>this.mapMessage(r))}async readMessage(e){let t=new URLSearchParams({$select:"id,from,toRecipients,ccRecipients,bccRecipients,subject,body,receivedDateTime,isRead,hasAttachments"}),s=await this.graphRequest(`/me/messages/${e}?${t}`),r=[];return s.hasAttachments&&(r=((await this.graphRequest(`/me/messages/${e}/attachments?$select=id,name,contentType,size`)).value??[]).map(o=>({id:o.id,name:o.name,contentType:o.contentType,size:o.size??0}))),{id:s.id,from:this.formatGraphAddress(s.from),to:(s.toRecipients??[]).map(n=>this.formatGraphAddress(n)),subject:s.subject??"(no subject)",date:new Date(s.receivedDateTime),read:s.isRead??!1,body:s.body?.contentType==="html"?this.stripHtml(s.body.content??""):s.body?.content??"",bodyHtml:s.body?.contentType==="html"?s.body.content:void 0,cc:s.ccRecipients?.map(n=>this.formatGraphAddress(n)),bcc:s.bccRecipients?.map(n=>this.formatGraphAddress(n)),hasAttachments:s.hasAttachments,attachments:r}}async searchMessages(e,t){let s=Math.min(Math.max(1,t),50),r=new URLSearchParams({$search:`"${e}"`,$top:String(s),$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"}),n=await this.graphRequest(`/me/messages?${r}`),o=(n.value??[]).map(i=>this.mapMessage(i));if(t>50){let i=n["@odata.nextLink"];for(;i&&o.length<t;){let a=await this.graphRequest(i.replace("https://graph.microsoft.com/v1.0","")),c=(a.value??[]).map(d=>this.mapMessage(d));o.push(...c),i=a["@odata.nextLink"]}}return o.slice(0,t)}async extractFromSearch(e,t,s,r,n){let o=await this.searchWithDateFilter(e,t,r,n),i=s.includes("amount"),a=[];if(i)for(let d=0;d<o.length;d+=5){let u=o.slice(d,d+5),p=await Promise.allSettled(u.map(async f=>{let g={id:f.id,from:f.from,subject:f.subject,date:f.date.toISOString().split("T")[0],preview:(f.preview??"").slice(0,200)};try{let h=await this.readMessage(f.id),y=this.extractAmount(h.body);y&&(g.amount=y.amount,g.currency=y.currency)}catch{}return g}));for(let f of p)f.status==="fulfilled"&&a.push(f.value)}else for(let c of o)a.push({id:c.id,from:c.from,subject:c.subject,date:c.date.toISOString().split("T")[0],preview:(c.preview??"").slice(0,200)});return a}async searchWithDateFilter(e,t,s,r){let n=Math.min(Math.max(1,t),50),o=[];if(s||r){let p=s?this.toKqlDate(s):"01/01/2000",f=r?this.toKqlDate(r):"12/31/2099";o.push(`received:${p}..${f}`)}e&&o.push(e.includes(" ")?`(${e})`:e);let i=o.join(" AND "),a=new URLSearchParams({$top:String(n),$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"});i?a.set("$search",`"${i}"`):a.set("$orderby","receivedDateTime desc");let c=await this.graphRequest(`/me/messages?${a}`),d=(c.value??[]).map(p=>this.mapMessage(p)),u=c["@odata.nextLink"];for(;u&&d.length<t;){let p=await this.graphRequest(u.replace("https://graph.microsoft.com/v1.0","")),f=(p.value??[]).map(g=>this.mapMessage(g));d.push(...f),u=p["@odata.nextLink"]}return d.slice(0,t)}toKqlDate(e){let[t,s,r]=e.split("-");return`${s}/${r}/${t}`}extractAmount(e){let t=[{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*€\s*([\d.,]+)/i,currency:"EUR"},{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*([\d.,]+)\s*€/i,currency:"EUR"},{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*EUR\s*([\d.,]+)/i,currency:"EUR"},{regex:/(?:gesamt|total|summe|betrag|amount|charged|bezahlt|preis|price)[:\s]*([\d.,]+)\s*EUR/i,currency:"EUR"},{regex:/(?:total|amount|charged|price|subtotal)[:\s]*\$\s*([\d.,]+)/i,currency:"USD"},{regex:/(?:total|amount|charged|price|subtotal)[:\s]*USD\s*([\d.,]+)/i,currency:"USD"},{regex:/(?:total|amount|charged|price|subtotal)[:\s]*([\d.,]+)\s*USD/i,currency:"USD"},{regex:/€\s*([\d]+[.,]\d{2})\b/,currency:"EUR"},{regex:/\b([\d]+[.,]\d{2})\s*€/,currency:"EUR"},{regex:/EUR\s*([\d]+[.,]\d{2})\b/,currency:"EUR"},{regex:/\$([\d]+[.,]\d{2})\b/,currency:"USD"},{regex:/USD\s*([\d]+[.,]\d{2})\b/,currency:"USD"}];for(let{regex:s,currency:r}of t){let n=e.match(s);if(n?.[1])return{amount:n[1].trim(),currency:r}}return null}async sendMessage(e){if(e.replyTo)return await this.graphRequest(`/me/messages/${e.replyTo}/reply`,{method:"POST",body:JSON.stringify({comment:e.body})}),{messageId:e.replyTo};let t={subject:e.subject,body:{contentType:e.isHtml?"html":"text",content:e.body},toRecipients:e.to.split(",").map(s=>({emailAddress:{address:s.trim()}}))};return e.cc&&(t.ccRecipients=e.cc.split(",").map(s=>({emailAddress:{address:s.trim()}}))),await this.graphRequest("/me/sendMail",{method:"POST",body:JSON.stringify({message:t})}),{messageId:`sent-${Date.now()}`}}async createDraft(e){if(e.replyTo)return{messageId:(await this.graphRequest(`/me/messages/${e.replyTo}/createReply`,{method:"POST",body:JSON.stringify({comment:e.body})}))?.id??e.replyTo};let t={subject:e.subject,body:{contentType:e.isHtml?"html":"text",content:e.body},toRecipients:e.to.split(",").map(r=>({emailAddress:{address:r.trim()}}))};return e.cc&&(t.ccRecipients=e.cc.split(",").map(r=>({emailAddress:{address:r.trim()}}))),{messageId:(await this.graphRequest("/me/messages",{method:"POST",body:JSON.stringify(t)}))?.id??`draft-${Date.now()}`}}async listFolders(){return((await this.graphRequest("/me/mailFolders?$select=displayName&$top=100")).value??[]).map(t=>t.displayName)}async fetchFolder(e,t){let r=((await this.graphRequest("/me/mailFolders?$select=id,displayName&$top=100")).value??[]).find(i=>i.displayName.toLowerCase()===e.toLowerCase());if(!r)throw new Error(`Folder "${e}" not found. Use the 'folders' action to list available folders.`);let n=new URLSearchParams({$top:String(Math.min(Math.max(1,t),50)),$orderby:"receivedDateTime desc",$select:"id,from,toRecipients,subject,receivedDateTime,isRead,bodyPreview,hasAttachments"});return((await this.graphRequest(`/me/mailFolders/${r.id}/messages?${n}`)).value??[]).map(i=>this.mapMessage(i))}async downloadAttachment(e,t){return this.graphRequestRaw(`/me/messages/${e}/attachments/${t}/$value`)}async forwardMessage(e,t,s){return await this.graphRequest(`/me/messages/${e}/forward`,{method:"POST",body:JSON.stringify({comment:s??"",toRecipients:t.split(",").map(r=>({emailAddress:{address:r.trim()}}))})}),{messageId:`fwd-${Date.now()}`}}mapMessage(e){return{id:e.id,from:this.formatGraphAddress(e.from),to:(e.toRecipients??[]).map(t=>this.formatGraphAddress(t)),subject:e.subject??"(no subject)",date:new Date(e.receivedDateTime),read:e.isRead??!1,preview:e.bodyPreview??void 0,hasAttachments:e.hasAttachments??!1}}formatGraphAddress(e){if(!e)return"unknown";let t=e.emailAddress??e;return t?.address?t.name?`${t.name} <${t.address}>`:t.address:"unknown"}stripHtml(e){return e.replace(/<br\s*\/?>/gi,`
467
490
  `).replace(/<\/p>/gi,`
468
491
 
469
492
  `).replace(/<[^>]+>/g,"").replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/\n{3,}/g,`
470
493
 
471
- `).trim()}}});var Sc={};ue(Sc,{StandardEmailProvider:()=>un});var un,Zo=_(()=>{"use strict";ln();un=class extends Xe{static{m(this,"StandardEmailProvider")}config;constructor(e){super(),this.config=e}async initialize(){}createImapClient(){return import("imapflow").then(({ImapFlow:e})=>new e({host:this.config.imap.host,port:this.config.imap.port,secure:this.config.imap.secure,auth:this.config.auth,logger:!1}))}formatAddress(e){return e?e.name?`${e.name} <${e.address}>`:e.address??"unknown":"unknown"}async fetchInbox(e){return this.fetchFolder("INBOX",e)}async readMessage(e){let t=parseInt(e,10);if(isNaN(t)||t<1)throw new Error("messageId must be a positive number (sequence number).");let s=await this.createImapClient();try{await s.connect();let r=await s.getMailboxLock("INBOX");try{let n=await s.fetchOne(String(t),{envelope:!0,source:!0,bodyStructure:!0});if(!n)throw new Error(`Message #${t} not found.`);let o=this.formatAddress(n.envelope?.from?.[0]),i=n.envelope?.to?.map(p=>p.name?`${p.name} <${p.address}>`:p.address??"")??[],a=n.envelope?.cc?.map(p=>p.name?`${p.name} <${p.address}>`:p.address??""),c=n.source?.toString()??"",d=this.extractTextBody(c),u=this.extractAttachmentInfo(n.bodyStructure);return{id:String(t),from:o,to:i,subject:n.envelope?.subject??"(no subject)",date:n.envelope?.date??new Date,read:n.flags?.has("\\Seen")??!1,body:d,cc:a,attachments:u}}finally{r.release()}}finally{await s.logout()}}async searchMessages(e,t){let s=Math.min(Math.max(1,t),50),r=await this.createImapClient();try{await r.connect();let n=await r.getMailboxLock("INBOX");try{let o=await r.search({or:[{subject:e},{from:e},{body:e}]}),i=Array.isArray(o)?o:[];if(i.length===0)return[];let a=i.slice(-s),c=[];for await(let d of r.fetch(a,{envelope:!0,flags:!0}))c.push({id:String(d.seq),from:this.formatAddress(d.envelope?.from?.[0]),to:d.envelope?.to?.map(u=>u.name?`${u.name} <${u.address}>`:u.address??"")??[],subject:d.envelope?.subject??"(no subject)",date:d.envelope?.date??new Date,read:d.flags?.has("\\Seen")??!1});return c.reverse(),c}finally{n.release()}}finally{await r.logout()}}async sendMessage(e){let s=(await import("nodemailer")).createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure,auth:this.config.auth}),r={from:this.config.auth.user,to:e.to,subject:e.subject};return e.cc&&(r.cc=e.cc),e.isHtml?r.html=e.body:r.text=e.body,e.replyTo&&(r.inReplyTo=e.replyTo,r.references=e.replyTo),{messageId:(await s.sendMail(r)).messageId}}async listFolders(){let e=await this.createImapClient();try{await e.connect();let t=await e.list(),s=[];for(let r of t)s.push(r.path);return s}finally{await e.logout()}}async fetchFolder(e,t){let s=Math.min(Math.max(1,t),50),r=await this.createImapClient();try{await r.connect();let n=await r.getMailboxLock(e);try{let o=[],i=r.mailbox,a=i&&typeof i=="object"?i.exists??0:0;if(a===0)return[];let d=`${Math.max(1,a-s+1)}:*`;for await(let u of r.fetch(d,{envelope:!0,flags:!0})){let p=this.formatAddress(u.envelope?.from?.[0]);o.push({id:String(u.seq),from:p,to:u.envelope?.to?.map(g=>g.name?`${g.name} <${g.address}>`:g.address??"")??[],subject:u.envelope?.subject??"(no subject)",date:u.envelope?.date??new Date,read:u.flags?.has("\\Seen")??!1})}return o.reverse(),o}finally{n.release()}}finally{await r.logout()}}async downloadAttachment(e,t){let s=parseInt(e,10);if(isNaN(s)||s<1)throw new Error("messageId must be a positive number.");let r=await this.createImapClient();try{await r.connect();let n=await r.getMailboxLock("INBOX");try{let i=(await r.fetchOne(String(s),{bodyParts:[t]}))?.bodyParts?.get(t);if(!i)throw new Error(`Attachment "${t}" not found in message #${s}.`);return Buffer.from(i)}finally{n.release()}}finally{await r.logout()}}extractAttachmentInfo(e){let t=[];if(!e)return t;let s=m((r,n)=>{if(r.childNodes){for(let i=0;i<r.childNodes.length;i++)s(r.childNodes[i],n?`${n}.${i+1}`:String(i+1));return}let o=r.disposition?.toLowerCase();if(o==="attachment"||r.type&&!r.type.startsWith("text/")&&o!=="inline"){let i=r.dispositionParameters?.filename??r.parameters?.name??`part-${n}`;t.push({id:r.part??n,name:i,contentType:r.type??"application/octet-stream",size:r.size??0})}},"walk");return s(e,""),t}extractTextBody(e){let t=e.split(/\r?\n\r?\n/);if(t.length<2)return e;let s=t[0].toLowerCase();if(!s.includes("multipart"))return this.decodeBody(t.slice(1).join(`
494
+ `).trim()}}});var Rc={};le(Rc,{StandardEmailProvider:()=>pn});var pn,ei=_(()=>{"use strict";un();pn=class extends Ye{static{m(this,"StandardEmailProvider")}config;constructor(e){super(),this.config=e}async initialize(){}createImapClient(){return import("imapflow").then(({ImapFlow:e})=>new e({host:this.config.imap.host,port:this.config.imap.port,secure:this.config.imap.secure,auth:this.config.auth,logger:!1}))}formatAddress(e){return e?e.name?`${e.name} <${e.address}>`:e.address??"unknown":"unknown"}async fetchInbox(e){return this.fetchFolder("INBOX",e)}async readMessage(e){let t=parseInt(e,10);if(isNaN(t)||t<1)throw new Error("messageId must be a positive number (sequence number).");let s=await this.createImapClient();try{await s.connect();let r=await s.getMailboxLock("INBOX");try{let n=await s.fetchOne(String(t),{envelope:!0,source:!0,bodyStructure:!0});if(!n)throw new Error(`Message #${t} not found.`);let o=this.formatAddress(n.envelope?.from?.[0]),i=n.envelope?.to?.map(p=>p.name?`${p.name} <${p.address}>`:p.address??"")??[],a=n.envelope?.cc?.map(p=>p.name?`${p.name} <${p.address}>`:p.address??""),c=n.source?.toString()??"",d=this.extractTextBody(c),u=this.extractAttachmentInfo(n.bodyStructure);return{id:String(t),from:o,to:i,subject:n.envelope?.subject??"(no subject)",date:n.envelope?.date??new Date,read:n.flags?.has("\\Seen")??!1,body:d,cc:a,attachments:u}}finally{r.release()}}finally{await s.logout()}}async searchMessages(e,t){let s=Math.min(Math.max(1,t),50),r=await this.createImapClient();try{await r.connect();let n=await r.getMailboxLock("INBOX");try{let o=await r.search({or:[{subject:e},{from:e},{body:e}]}),i=Array.isArray(o)?o:[];if(i.length===0)return[];let a=i.slice(-s),c=[];for await(let d of r.fetch(a,{envelope:!0,flags:!0}))c.push({id:String(d.seq),from:this.formatAddress(d.envelope?.from?.[0]),to:d.envelope?.to?.map(u=>u.name?`${u.name} <${u.address}>`:u.address??"")??[],subject:d.envelope?.subject??"(no subject)",date:d.envelope?.date??new Date,read:d.flags?.has("\\Seen")??!1});return c.reverse(),c}finally{n.release()}}finally{await r.logout()}}async sendMessage(e){let s=(await import("nodemailer")).createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure,auth:this.config.auth}),r={from:this.config.auth.user,to:e.to,subject:e.subject};return e.cc&&(r.cc=e.cc),e.isHtml?r.html=e.body:r.text=e.body,e.replyTo&&(r.inReplyTo=e.replyTo,r.references=e.replyTo),{messageId:(await s.sendMail(r)).messageId}}async listFolders(){let e=await this.createImapClient();try{await e.connect();let t=await e.list(),s=[];for(let r of t)s.push(r.path);return s}finally{await e.logout()}}async fetchFolder(e,t){let s=Math.min(Math.max(1,t),50),r=await this.createImapClient();try{await r.connect();let n=await r.getMailboxLock(e);try{let o=[],i=r.mailbox,a=i&&typeof i=="object"?i.exists??0:0;if(a===0)return[];let d=`${Math.max(1,a-s+1)}:*`;for await(let u of r.fetch(d,{envelope:!0,flags:!0})){let p=this.formatAddress(u.envelope?.from?.[0]);o.push({id:String(u.seq),from:p,to:u.envelope?.to?.map(f=>f.name?`${f.name} <${f.address}>`:f.address??"")??[],subject:u.envelope?.subject??"(no subject)",date:u.envelope?.date??new Date,read:u.flags?.has("\\Seen")??!1})}return o.reverse(),o}finally{n.release()}}finally{await r.logout()}}async downloadAttachment(e,t){let s=parseInt(e,10);if(isNaN(s)||s<1)throw new Error("messageId must be a positive number.");let r=await this.createImapClient();try{await r.connect();let n=await r.getMailboxLock("INBOX");try{let i=(await r.fetchOne(String(s),{bodyParts:[t]}))?.bodyParts?.get(t);if(!i)throw new Error(`Attachment "${t}" not found in message #${s}.`);return Buffer.from(i)}finally{n.release()}}finally{await r.logout()}}extractAttachmentInfo(e){let t=[];if(!e)return t;let s=m((r,n)=>{if(r.childNodes){for(let i=0;i<r.childNodes.length;i++)s(r.childNodes[i],n?`${n}.${i+1}`:String(i+1));return}let o=r.disposition?.toLowerCase();if(o==="attachment"||r.type&&!r.type.startsWith("text/")&&o!=="inline"){let i=r.dispositionParameters?.filename??r.parameters?.name??`part-${n}`;t.push({id:r.part??n,name:i,contentType:r.type??"application/octet-stream",size:r.size??0})}},"walk");return s(e,""),t}extractTextBody(e){let t=e.split(/\r?\n\r?\n/);if(t.length<2)return e;let s=t[0].toLowerCase();if(!s.includes("multipart"))return this.decodeBody(t.slice(1).join(`
472
495
 
473
496
  `));let r=s.match(/boundary="?([^"\s;]+)"?/i)??e.match(/boundary="?([^"\s;]+)"?/i);if(!r)return t.slice(1).join(`
474
497
 
@@ -478,7 +501,7 @@ Additional context: ${r}`);let f=[{role:"user",content:g}];try{let h=0,y=0,E=0;f
478
501
  \r
479
502
  `);if(d>=0)return this.decodeBody(i.slice(d+4))}}return this.decodeBody(t.slice(1).join(`
480
503
 
481
- `).slice(0,5e3))}decodeBody(e){return e.replace(/=\r?\n/g,"").replace(/=([0-9A-Fa-f]{2})/g,(t,s)=>String.fromCharCode(parseInt(s,16))).trim()}}});async function or(l){if(l.provider==="microsoft"){if(!l.microsoft)throw new Error("Microsoft email config missing");let{MicrosoftGraphEmailProvider:s}=await Promise.resolve().then(()=>(Jo(),vc)),r=new s(l.microsoft);return await r.initialize(),r}if(!l.imap||!l.smtp||!l.auth)throw new Error("IMAP/SMTP email config missing (imap, smtp, auth required)");let{StandardEmailProvider:e}=await Promise.resolve().then(()=>(Zo(),Sc)),t=new e(l);return await t.initialize(),t}var Ac=_(()=>{"use strict";m(or,"createEmailProvider")});var tt,Ic=_(()=>{"use strict";ln();Ac();Zo();Jo();U();tt=class extends R{static{m(this,"EmailSkill")}metadata;providers;accountNames;defaultAccount;multiAccount;constructor(e){super(),e instanceof Map?this.providers=e:e?this.providers=new Map([["default",e]]):this.providers=new Map,this.accountNames=[...this.providers.keys()],this.defaultAccount=this.accountNames[0]??"default",this.multiAccount=this.providers.size>1;let t=this.multiAccount?{account:{type:"string",enum:this.accountNames,description:`Email account to use (available: ${this.accountNames.join(", ")})`}}:{},s=this.multiAccount?`Access the user's email accounts (${this.accountNames.join(", ")}): check inbox, read messages, search emails, send new emails, create drafts, list folders, read from specific folders, reply to messages, forward messages, or download attachments.`:`Access the user's email: check inbox, read messages, search emails, send new emails, create drafts, list folders, read from specific folders, reply to messages, forward messages, or download attachments. Use "draft" instead of "send" when the user asks to prepare/draft an email without sending it.`;this.metadata={name:"email",category:"productivity",description:s,riskLevel:"write",version:"3.1.0",timeoutMs:3e5,inputSchema:{type:"object",properties:{action:{type:"string",enum:["inbox","read","search","send","draft","folders","folder","reply","forward","attachment","extract"],description:'The email action to perform. Use "extract" for bulk invoice/receipt extraction from large mailboxes \u2014 it searches with pagination, reads bodies server-side, and returns structured data with amounts.'},...t,count:{type:"number",description:"Number of emails to fetch (for inbox/search/folder, default: 10)"},messageId:{type:"string",description:"Message ID to read or reply to"},query:{type:"string",description:"Search query (for search action)"},to:{type:"string",description:"Recipient email address (for send action)"},subject:{type:"string",description:"Email subject (for send action)"},body:{type:"string",description:"Email body text (for send/reply action)"},cc:{type:"string",description:"CC recipients, comma-separated (for send action)"},folder:{type:"string",description:"Folder name (for folder action)"},attachmentId:{type:"string",description:"Attachment ID or filename (for attachment action)"},save:{type:"string",description:"Directory path to save the attachment to disk instead of reading its content (for attachment action)"},isHtml:{type:"boolean",description:"Whether the body is HTML (for send action)"},maxResults:{type:"number",description:"Maximum number of results for extract action (default: 200, max: 1000)"},fields:{type:"array",items:{type:"string"},description:'Fields to extract (for extract action). Available: "from", "subject", "date", "amount". Include "amount" to read email bodies and extract monetary amounts.'},dateFrom:{type:"string",description:'Start date filter for extract action (YYYY-MM-DD format, e.g. "2026-01-01")'},dateTo:{type:"string",description:'End date filter for extract action (YYYY-MM-DD format, e.g. "2026-12-31")'}},required:["action"]}}}async execute(e,t){if(this.providers.size===0)return{success:!1,error:"Email is not configured. Run `alfred setup` to configure email access."};let s=e.action;try{switch(s){case"inbox":return await this.handleInbox(e);case"read":return await this.handleRead(e);case"search":return await this.handleSearch(e);case"send":return await this.handleSend(e);case"draft":return await this.handleDraft(e);case"folders":return await this.handleFolders(e);case"folder":return await this.handleFolder(e);case"reply":return await this.handleReply(e);case"forward":return await this.handleForward(e);case"attachment":return await this.handleAttachment(e);case"extract":return await this.handleExtract(e);default:return{success:!1,error:`Unknown action: ${s}. Use: inbox, read, search, send, draft, folders, folder, reply, forward, attachment`}}}catch(r){return{success:!1,error:`Email error: ${r instanceof Error?r.message:String(r)}`}}}resolveProvider(e){let t=e.account??this.defaultAccount,s=this.providers.get(t);return s?{provider:s,account:t}:{success:!1,error:`Unknown email account "${t}". Available: ${this.accountNames.join(", ")}`}}encodeId(e,t){return this.multiAccount?`${e}::${t}`:t}decodeId(e){if(this.multiAccount){let t=e.indexOf("::");if(t>=0)return{account:e.slice(0,t),rawId:e.slice(t+2)}}return{account:this.defaultAccount,rawId:e}}accountLabel(e,t){return this.multiAccount?`[${e}] ${t}`:t}async handleInbox(e){let t=this.resolveProvider(e);if("success"in t)return t;let{provider:s,account:r}=t,n=Math.min(Math.max(1,e.count??10),50),o=await s.fetchInbox(n);if(o.length===0)return{success:!0,data:{messages:[]},display:this.accountLabel(r,"Inbox is empty.")};let i=o.filter(c=>!c.read).length,a=o.map((c,d)=>{let u=c.read?"":" [UNREAD]",p=c.hasAttachments?" [ATT]":"";return`${d+1}. [${this.encodeId(r,c.id)}]${u}${p} ${c.subject}
504
+ `).slice(0,5e3))}decodeBody(e){return e.replace(/=\r?\n/g,"").replace(/=([0-9A-Fa-f]{2})/g,(t,s)=>String.fromCharCode(parseInt(s,16))).trim()}}});async function lr(l){if(l.provider==="microsoft"){if(!l.microsoft)throw new Error("Microsoft email config missing");let{MicrosoftGraphEmailProvider:s}=await Promise.resolve().then(()=>(Qo(),xc)),r=new s(l.microsoft);return await r.initialize(),r}if(!l.imap||!l.smtp||!l.auth)throw new Error("IMAP/SMTP email config missing (imap, smtp, auth required)");let{StandardEmailProvider:e}=await Promise.resolve().then(()=>(ei(),Rc)),t=new e(l);return await t.initialize(),t}var Cc=_(()=>{"use strict";m(lr,"createEmailProvider")});var rt,Dc=_(()=>{"use strict";un();Cc();ei();Qo();U();rt=class extends x{static{m(this,"EmailSkill")}metadata;providers;accountNames;defaultAccount;multiAccount;constructor(e){super(),e instanceof Map?this.providers=e:e?this.providers=new Map([["default",e]]):this.providers=new Map,this.accountNames=[...this.providers.keys()],this.defaultAccount=this.accountNames[0]??"default",this.multiAccount=this.providers.size>1;let t=this.multiAccount?{account:{type:"string",enum:this.accountNames,description:`Email account to use (available: ${this.accountNames.join(", ")})`}}:{},s=this.multiAccount?`Access the user's email accounts (${this.accountNames.join(", ")}): check inbox, read messages, search emails, send new emails, create drafts, list folders, read from specific folders, reply to messages, forward messages, or download attachments.`:`Access the user's email: check inbox, read messages, search emails, send new emails, create drafts, list folders, read from specific folders, reply to messages, forward messages, or download attachments. Use "draft" instead of "send" when the user asks to prepare/draft an email without sending it.`;this.metadata={name:"email",category:"productivity",description:s,riskLevel:"write",version:"3.1.0",timeoutMs:3e5,inputSchema:{type:"object",properties:{action:{type:"string",enum:["inbox","read","search","send","draft","folders","folder","reply","forward","attachment","extract"],description:'The email action to perform. Use "extract" for bulk invoice/receipt extraction from large mailboxes \u2014 it searches with pagination, reads bodies server-side, and returns structured data with amounts.'},...t,count:{type:"number",description:"Number of emails to fetch (for inbox/search/folder, default: 10)"},messageId:{type:"string",description:"Message ID to read or reply to"},query:{type:"string",description:"Search query (for search action)"},to:{type:"string",description:"Recipient email address (for send action)"},subject:{type:"string",description:"Email subject (for send action)"},body:{type:"string",description:"Email body text (for send/reply action)"},cc:{type:"string",description:"CC recipients, comma-separated (for send action)"},folder:{type:"string",description:"Folder name (for folder action)"},attachmentId:{type:"string",description:"Attachment ID or filename (for attachment action)"},save:{type:"string",description:"Directory path to save the attachment to disk instead of reading its content (for attachment action)"},isHtml:{type:"boolean",description:"Whether the body is HTML (for send action)"},maxResults:{type:"number",description:"Maximum number of results for extract action (default: 200, max: 1000)"},fields:{type:"array",items:{type:"string"},description:'Fields to extract (for extract action). Available: "from", "subject", "date", "amount". Include "amount" to read email bodies and extract monetary amounts.'},dateFrom:{type:"string",description:'Start date filter for extract action (YYYY-MM-DD format, e.g. "2026-01-01")'},dateTo:{type:"string",description:'End date filter for extract action (YYYY-MM-DD format, e.g. "2026-12-31")'}},required:["action"]}}}async execute(e,t){if(this.providers.size===0)return{success:!1,error:"Email is not configured. Run `alfred setup` to configure email access."};let s=e.action;try{switch(s){case"inbox":return await this.handleInbox(e);case"read":return await this.handleRead(e);case"search":return await this.handleSearch(e);case"send":return await this.handleSend(e);case"draft":return await this.handleDraft(e);case"folders":return await this.handleFolders(e);case"folder":return await this.handleFolder(e);case"reply":return await this.handleReply(e);case"forward":return await this.handleForward(e);case"attachment":return await this.handleAttachment(e);case"extract":return await this.handleExtract(e);default:return{success:!1,error:`Unknown action: ${s}. Use: inbox, read, search, send, draft, folders, folder, reply, forward, attachment`}}}catch(r){return{success:!1,error:`Email error: ${r instanceof Error?r.message:String(r)}`}}}resolveProvider(e){let t=e.account??this.defaultAccount,s=this.providers.get(t);return s?{provider:s,account:t}:{success:!1,error:`Unknown email account "${t}". Available: ${this.accountNames.join(", ")}`}}encodeId(e,t){return this.multiAccount?`${e}::${t}`:t}decodeId(e){if(this.multiAccount){let t=e.indexOf("::");if(t>=0)return{account:e.slice(0,t),rawId:e.slice(t+2)}}return{account:this.defaultAccount,rawId:e}}accountLabel(e,t){return this.multiAccount?`[${e}] ${t}`:t}async handleInbox(e){let t=this.resolveProvider(e);if("success"in t)return t;let{provider:s,account:r}=t,n=Math.min(Math.max(1,e.count??10),50),o=await s.fetchInbox(n);if(o.length===0)return{success:!0,data:{messages:[]},display:this.accountLabel(r,"Inbox is empty.")};let i=o.filter(c=>!c.read).length,a=o.map((c,d)=>{let u=c.read?"":" [UNREAD]",p=c.hasAttachments?" [ATT]":"";return`${d+1}. [${this.encodeId(r,c.id)}]${u}${p} ${c.subject}
482
505
  From: ${c.from}
483
506
  Date: ${c.date.toISOString()}`}).join(`
484
507
 
@@ -498,8 +521,8 @@ ${o.attachments.map(a=>` - [attachmentId: ${a.id}] ${a.name} (${a.contentType},
498
521
 
499
522
  ${a}`)}}async handleSend(e){let t=e.to,s=e.subject,r=e.body;if(!t)return{success:!1,error:'"to" (recipient email) is required.'};if(!s)return{success:!1,error:'"subject" is required.'};if(!r)return{success:!1,error:'"body" is required.'};let n=this.resolveProvider(e);if("success"in n)return n;let{provider:o,account:i}=n,a=await o.sendMessage({to:t,subject:s,body:r,cc:e.cc,isHtml:e.isHtml});return{success:!0,data:{messageId:a.messageId,to:t,subject:s},display:this.accountLabel(i,`Email sent to ${t}
500
523
  Subject: ${s}
501
- Message ID: ${a.messageId}`)}}async handleDraft(e){let t=e.messageId,s=e.body;if(t){if(!s)return{success:!1,error:'"body" is required for reply draft.'};let{account:d,rawId:u}=this.decodeId(t),p=this.providers.get(d);if(!p)return{success:!1,error:`Unknown email account "${d}".`};let g=await p.createDraft({to:"",subject:"",body:s,replyTo:u});return{success:!0,data:{messageId:g.messageId},display:this.accountLabel(d,`Reply draft created for message ${u}.
502
- Message ID: ${g.messageId}
524
+ Message ID: ${a.messageId}`)}}async handleDraft(e){let t=e.messageId,s=e.body;if(t){if(!s)return{success:!1,error:'"body" is required for reply draft.'};let{account:d,rawId:u}=this.decodeId(t),p=this.providers.get(d);if(!p)return{success:!1,error:`Unknown email account "${d}".`};let f=await p.createDraft({to:"",subject:"",body:s,replyTo:u});return{success:!0,data:{messageId:f.messageId},display:this.accountLabel(d,`Reply draft created for message ${u}.
525
+ Message ID: ${f.messageId}
503
526
 
504
527
  The reply is saved as a draft and has NOT been sent.`)}}let r=e.to,n=e.subject;if(!r)return{success:!1,error:'"to" (recipient email) is required.'};if(!n)return{success:!1,error:'"subject" is required.'};if(!s)return{success:!1,error:'"body" is required.'};let o=this.resolveProvider(e);if("success"in o)return o;let{provider:i,account:a}=o,c=await i.createDraft({to:r,subject:n,body:s,cc:e.cc,isHtml:e.isHtml});return{success:!0,data:{messageId:c.messageId,to:r,subject:n},display:this.accountLabel(a,`Draft created for ${r}
505
528
  Subject: ${n}
@@ -513,43 +536,43 @@ ${n.map((o,i)=>` ${i+1}. ${o}`).join(`
513
536
 
514
537
  `);return{success:!0,data:{folder:t,messages:i.map(c=>({...c,id:this.encodeId(n,c.id)}))},display:this.accountLabel(n,`Folder "${t}" (${i.length} messages):
515
538
 
516
- ${a}`)}}async handleReply(e){let t=e.messageId,s=e.body;if(!t)return{success:!1,error:'"messageId" is required for reply.'};if(!s)return{success:!1,error:'"body" is required for reply.'};let{account:r,rawId:n}=this.decodeId(t),o=this.providers.get(r);return o?{success:!0,data:{messageId:(await o.sendMessage({to:"",subject:"",body:s,replyTo:n})).messageId},display:this.accountLabel(r,`Reply sent to message ${n}.`)}:{success:!1,error:`Unknown email account "${r}".`}}async handleForward(e){let t=e.messageId,s=e.to;if(!t)return{success:!1,error:'"messageId" is required for forward.'};if(!s)return{success:!1,error:'"to" (recipient email) is required for forward.'};let{account:r,rawId:n}=this.decodeId(t),o=this.providers.get(r);if(!o)return{success:!1,error:`Unknown email account "${r}".`};let i=e.body??void 0;return{success:!0,data:{messageId:(await o.forwardMessage(n,s,i)).messageId,to:s},display:this.accountLabel(r,`Email forwarded to ${s}.`)}}async handleAttachment(e){let t=e.messageId,s=e.attachmentId,r=e.save;if(!t)return{success:!1,error:'"messageId" is required.'};if(!s)return{success:!1,error:'"attachmentId" is required.'};let{account:n,rawId:o}=this.decodeId(t),i=this.providers.get(n);if(!i)return{success:!1,error:`Unknown email account "${n}".`};let a=await i.readMessage(o),c=a.attachments?.find(f=>f.id===s);if(c||(c=a.attachments?.find(f=>f.name.toLowerCase()===s.toLowerCase())),!c){let f=a.attachments?.map(h=>`[${h.id}] ${h.name}`).join(", ")??"none";return{success:!1,error:`Attachment "${s}" not found. Available: ${f}`}}let d=await i.downloadAttachment(o,c.id),u=c.name,p=c.contentType;if(r){let f=await import("node:fs"),h=await import("node:path"),y=h.resolve(r);f.existsSync(y)||f.mkdirSync(y,{recursive:!0});let E=h.join(y,u);return f.writeFileSync(E,d),{success:!0,data:{messageId:t,attachmentId:c.id,fileName:u,size:d.length,savedTo:E},display:this.accountLabel(n,`Saved attachment: ${u} (${this.formatSize(d.length)}) \u2192 ${E}`)}}let g=await this.extractText(d,p,u);if(g!==null){let f=g.length>6e3?g.slice(0,6e3)+`
539
+ ${a}`)}}async handleReply(e){let t=e.messageId,s=e.body;if(!t)return{success:!1,error:'"messageId" is required for reply.'};if(!s)return{success:!1,error:'"body" is required for reply.'};let{account:r,rawId:n}=this.decodeId(t),o=this.providers.get(r);return o?{success:!0,data:{messageId:(await o.sendMessage({to:"",subject:"",body:s,replyTo:n})).messageId},display:this.accountLabel(r,`Reply sent to message ${n}.`)}:{success:!1,error:`Unknown email account "${r}".`}}async handleForward(e){let t=e.messageId,s=e.to;if(!t)return{success:!1,error:'"messageId" is required for forward.'};if(!s)return{success:!1,error:'"to" (recipient email) is required for forward.'};let{account:r,rawId:n}=this.decodeId(t),o=this.providers.get(r);if(!o)return{success:!1,error:`Unknown email account "${r}".`};let i=e.body??void 0;return{success:!0,data:{messageId:(await o.forwardMessage(n,s,i)).messageId,to:s},display:this.accountLabel(r,`Email forwarded to ${s}.`)}}async handleAttachment(e){let t=e.messageId,s=e.attachmentId,r=e.save;if(!t)return{success:!1,error:'"messageId" is required.'};if(!s)return{success:!1,error:'"attachmentId" is required.'};let{account:n,rawId:o}=this.decodeId(t),i=this.providers.get(n);if(!i)return{success:!1,error:`Unknown email account "${n}".`};let a=await i.readMessage(o),c=a.attachments?.find(g=>g.id===s);if(c||(c=a.attachments?.find(g=>g.name.toLowerCase()===s.toLowerCase())),!c){let g=a.attachments?.map(h=>`[${h.id}] ${h.name}`).join(", ")??"none";return{success:!1,error:`Attachment "${s}" not found. Available: ${g}`}}let d=await i.downloadAttachment(o,c.id),u=c.name,p=c.contentType;if(r){let g=await import("node:fs"),h=await import("node:path"),y=h.resolve(r);g.existsSync(y)||g.mkdirSync(y,{recursive:!0});let E=h.join(y,u);return g.writeFileSync(E,d),{success:!0,data:{messageId:t,attachmentId:c.id,fileName:u,size:d.length,savedTo:E},display:this.accountLabel(n,`Saved attachment: ${u} (${this.formatSize(d.length)}) \u2192 ${E}`)}}let f=await this.extractText(d,p,u);if(f!==null){let g=f.length>6e3?f.slice(0,6e3)+`
517
540
 
518
- ... (truncated)`:g;return{success:!0,data:{messageId:t,attachmentId:c.id,fileName:u,size:d.length,hasContent:!0},display:this.accountLabel(n,`Attachment: ${u} (${this.formatSize(d.length)})
541
+ ... (truncated)`:f;return{success:!0,data:{messageId:t,attachmentId:c.id,fileName:u,size:d.length,hasContent:!0},display:this.accountLabel(n,`Attachment: ${u} (${this.formatSize(d.length)})
519
542
 
520
543
  Content:
521
- ${f}`),attachments:[{fileName:u,data:d,mimeType:p}]}}return{success:!0,data:{messageId:t,attachmentId:c.id,fileName:u,size:d.length},display:this.accountLabel(n,`Downloaded attachment: ${u} (${this.formatSize(d.length)})`),attachments:[{fileName:u,data:d,mimeType:p}]}}async handleExtract(e){let t=e.query;if(!t)return{success:!1,error:'query is required for extract. Example: "rechnung OR invoice OR receipt 2026"'};let s=this.resolveProvider(e);if("success"in s)return s;let{provider:r,account:n}=s,o=Math.min(Math.max(1,e.maxResults??200),1e3),i=e.fields??["from","subject","date","amount"],a=e.dateFrom,c=e.dateTo;try{let d=await r.extractFromSearch(t,o,i,a,c);if(d.length===0)return{success:!0,data:{results:[]},display:this.accountLabel(n,`No emails found for "${t}".`)};let u=d.map((f,h)=>{let y=f.amount?` | ${f.amount} ${f.currency??""}`:"";return`${h+1}. ${f.date} | ${f.from} | ${f.subject}${y}`}),p=d.filter(f=>f.amount),g=`Found ${d.length} emails (${p.length} with detected amounts)`;return{success:!0,data:{results:d,totalFound:d.length,withAmounts:p.length},display:this.accountLabel(n,`${g}:
544
+ ${g}`),attachments:[{fileName:u,data:d,mimeType:p}]}}return{success:!0,data:{messageId:t,attachmentId:c.id,fileName:u,size:d.length},display:this.accountLabel(n,`Downloaded attachment: ${u} (${this.formatSize(d.length)})`),attachments:[{fileName:u,data:d,mimeType:p}]}}async handleExtract(e){let t=e.query;if(!t)return{success:!1,error:'query is required for extract. Example: "rechnung OR invoice OR receipt 2026"'};let s=this.resolveProvider(e);if("success"in s)return s;let{provider:r,account:n}=s,o=Math.min(Math.max(1,e.maxResults??200),1e3),i=e.fields??["from","subject","date","amount"],a=e.dateFrom,c=e.dateTo;try{let d=await r.extractFromSearch(t,o,i,a,c);if(d.length===0)return{success:!0,data:{results:[]},display:this.accountLabel(n,`No emails found for "${t}".`)};let u=d.map((g,h)=>{let y=g.amount?` | ${g.amount} ${g.currency??""}`:"";return`${h+1}. ${g.date} | ${g.from} | ${g.subject}${y}`}),p=d.filter(g=>g.amount),f=`Found ${d.length} emails (${p.length} with detected amounts)`;return{success:!0,data:{results:d,totalFound:d.length,withAmounts:p.length},display:this.accountLabel(n,`${f}:
522
545
 
523
546
  ${u.join(`
524
- `)}`)}}catch(d){if((d instanceof Error?d.message:String(d)).includes("Extract is not supported"))return{success:!1,error:"The extract action is only supported with Microsoft Graph email. Use search + read for other providers."};throw d}}async extractText(e,t,s){try{if(t==="application/pdf"){let r=(await import("pdf-parse")).default;return(await r(e)).text?.trim()||null}if(t==="application/vnd.openxmlformats-officedocument.wordprocessingml.document"||t==="application/msword")return(await(await import("mammoth")).extractRawText({buffer:e})).value?.trim()||null;if(t.startsWith("text/")||s.endsWith(".csv")||s.endsWith(".json")||s.endsWith(".md"))return e.toString("utf-8")}catch{}return null}formatSize(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}}});var xc,Kt,Rc=_(()=>{"use strict";U();xc=1e5,Kt=class extends R{static{m(this,"HttpSkill")}metadata={name:"http",category:"files",description:"Make HTTP requests to fetch web pages or call REST APIs. Use when you need to read a URL, call an API endpoint, or fetch data from the web. Supports GET, POST, PUT, PATCH, DELETE methods.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{url:{type:"string",description:"The URL to request"},method:{type:"string",enum:["GET","POST","PUT","PATCH","DELETE"],description:"HTTP method (default: GET)"},headers:{type:"object",description:"Request headers as key-value pairs (optional)"},body:{type:"string",description:"Request body for POST/PUT/PATCH (optional)"}},required:["url"]}};async execute(e,t){let s=e.url,r=(e.method??"GET").toUpperCase(),n=e.headers,o=e.body;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "url"'};let i;try{i=new URL(s)}catch{return{success:!1,error:`Invalid URL: "${s}"`}}if(i.protocol!=="http:"&&i.protocol!=="https:")return{success:!1,error:`Unsupported URL scheme "${i.protocol}". Only http: and https: are allowed.`};if(this.isPrivateHost(i.hostname))return{success:!1,error:`Access to private/internal network address "${i.hostname}" is blocked.`};try{let a={method:r,headers:{"User-Agent":"Alfred/1.0",...n??{}},signal:AbortSignal.timeout(15e3)};o&&["POST","PUT","PATCH"].includes(r)&&(a.body=o,!n?.["Content-Type"]&&!n?.["content-type"]&&(a.headers["Content-Type"]="application/json"));let c=await fetch(s,a),d=c.headers.get("content-type")??"",u=await c.text(),p=u.length>xc,g=p?u.slice(0,xc)+`
547
+ `)}`)}}catch(d){if((d instanceof Error?d.message:String(d)).includes("Extract is not supported"))return{success:!1,error:"The extract action is only supported with Microsoft Graph email. Use search + read for other providers."};throw d}}async extractText(e,t,s){try{if(t==="application/pdf"){let r=(await import("pdf-parse")).default;return(await r(e)).text?.trim()||null}if(t==="application/vnd.openxmlformats-officedocument.wordprocessingml.document"||t==="application/msword")return(await(await import("mammoth")).extractRawText({buffer:e})).value?.trim()||null;if(t.startsWith("text/")||s.endsWith(".csv")||s.endsWith(".json")||s.endsWith(".md"))return e.toString("utf-8")}catch{}return null}formatSize(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}}});var Lc,Yt,Nc=_(()=>{"use strict";U();Lc=1e5,Yt=class extends x{static{m(this,"HttpSkill")}metadata={name:"http",category:"files",description:"Make HTTP requests to fetch web pages or call REST APIs. Use when you need to read a URL, call an API endpoint, or fetch data from the web. Supports GET, POST, PUT, PATCH, DELETE methods.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{url:{type:"string",description:"The URL to request"},method:{type:"string",enum:["GET","POST","PUT","PATCH","DELETE"],description:"HTTP method (default: GET)"},headers:{type:"object",description:"Request headers as key-value pairs (optional)"},body:{type:"string",description:"Request body for POST/PUT/PATCH (optional)"}},required:["url"]}};async execute(e,t){let s=e.url,r=(e.method??"GET").toUpperCase(),n=e.headers,o=e.body;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "url"'};let i;try{i=new URL(s)}catch{return{success:!1,error:`Invalid URL: "${s}"`}}if(i.protocol!=="http:"&&i.protocol!=="https:")return{success:!1,error:`Unsupported URL scheme "${i.protocol}". Only http: and https: are allowed.`};if(this.isPrivateHost(i.hostname))return{success:!1,error:`Access to private/internal network address "${i.hostname}" is blocked.`};try{let a={method:r,headers:{"User-Agent":"Alfred/1.0",...n??{}},signal:AbortSignal.timeout(15e3)};o&&["POST","PUT","PATCH"].includes(r)&&(a.body=o,!n?.["Content-Type"]&&!n?.["content-type"]&&(a.headers["Content-Type"]="application/json"));let c=await fetch(s,a),d=c.headers.get("content-type")??"",u=await c.text(),p=u.length>Lc,f=p?u.slice(0,Lc)+`
525
548
 
526
- [... truncated]`:u,f=g;d.includes("text/html")&&(f=this.stripHtml(g).slice(0,1e4));let h={status:c.status,statusText:c.statusText,contentType:d,bodyLength:u.length,truncated:p,body:g};return c.ok?{success:!0,data:h,display:`HTTP ${c.status} OK (${u.length} bytes)
549
+ [... truncated]`:u,g=f;d.includes("text/html")&&(g=this.stripHtml(f).slice(0,1e4));let h={status:c.status,statusText:c.statusText,contentType:d,bodyLength:u.length,truncated:p,body:f};return c.ok?{success:!0,data:h,display:`HTTP ${c.status} OK (${u.length} bytes)
527
550
 
528
- ${f.slice(0,5e3)}`}:{success:!0,data:h,display:`HTTP ${c.status} ${c.statusText}
551
+ ${g.slice(0,5e3)}`}:{success:!0,data:h,display:`HTTP ${c.status} ${c.statusText}
529
552
 
530
- ${f.slice(0,2e3)}`}}catch(a){return{success:!1,error:`HTTP request failed: ${a instanceof Error?a.message:String(a)}`}}}isPrivateHost(e){if(e==="localhost"||e==="127.0.0.1"||e==="::1")return!0;let t=e.replace(/[\[\]]/g,"").toLowerCase();if(t.startsWith("fc")||t.startsWith("fd")||t==="::1")return!0;let s=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(e);if(s){let[,r,n]=s.map(Number);if(r===10||r===172&&n>=16&&n<=31||r===192&&n===168||r===127||r===169&&n===254||r===0)return!0}return!1}stripHtml(e){return e.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,"").replace(/<style[^>]*>[\s\S]*?<\/style>/gi,"").replace(/<[^>]+>/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#x27;/g,"'").replace(/&nbsp;/g," ").replace(/\s+/g," ").trim()}}});import Z from"node:fs";import st from"node:path";var Qo,Cc,Dm,Xt,Dc=_(()=>{"use strict";U();Qo=5e5,Cc=5e7,Dm={".pdf":"application/pdf",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".odt":"application/vnd.oasis.opendocument.text",".ods":"application/vnd.oasis.opendocument.spreadsheet",".odp":"application/vnd.oasis.opendocument.presentation",".rtf":"application/rtf",".epub":"application/epub+zip",".txt":"text/plain",".md":"text/markdown",".csv":"text/csv",".tsv":"text/tab-separated-values",".html":"text/html",".htm":"text/html",".xml":"application/xml",".yaml":"application/yaml",".yml":"application/yaml",".toml":"application/toml",".ini":"text/plain",".cfg":"text/plain",".log":"text/plain",".json":"application/json",".js":"application/javascript",".ts":"application/typescript",".py":"text/x-python",".sh":"application/x-sh",".sql":"application/sql",".css":"text/css",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".bmp":"image/bmp",".ico":"image/x-icon",".tiff":"image/tiff",".tif":"image/tiff",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".flac":"audio/flac",".aac":"audio/aac",".m4a":"audio/mp4",".mp4":"video/mp4",".webm":"video/webm",".mkv":"video/x-matroska",".avi":"video/x-msvideo",".mov":"video/quicktime",".zip":"application/zip",".tar":"application/x-tar",".gz":"application/gzip",".7z":"application/x-7z-compressed",".rar":"application/vnd.rar",".ttf":"font/ttf",".otf":"font/otf",".woff":"font/woff",".woff2":"font/woff2"},Xt=class extends R{static{m(this,"FileSkill")}metadata={name:"file",category:"files",description:'Read, write, move, copy, or send files. Use for reading file contents, writing text to files, saving binary data, listing directory contents, moving/copying files, or getting file info. Use "send" to deliver a file to the user in the chat (PDF, images, etc.). Prefer this over shell for file operations. When a user sends a file attachment, it is saved to the inbox \u2014 use "move" to relocate it. IMPORTANT: For large content (HTML pages, long text), use code_sandbox instead to generate the file programmatically.',riskLevel:"write",version:"2.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["read","write","write_binary","append","list","info","exists","move","copy","delete","send"],description:"The file operation to perform"},path:{type:"string",description:"Absolute or relative file/directory path (~ expands to home)"},destination:{type:"string",description:"Destination path for move/copy actions (~ expands to home)"},content:{type:"string",description:"Content to write (required for write/append; base64-encoded for write_binary)"}},required:["action","path"]}};async execute(e,t){let s=e.action,r=e.path,n=e.content,o=e.destination;if(!s||!r)return{success:!1,error:'Missing required fields "action" and "path"'};if((s==="write"||s==="write_binary"||s==="append")&&!n)return{success:!1,error:`Missing "content" field for "${s}" action. The content is likely too large to include in a tool call. Use the code_sandbox skill instead \u2014 write COMPACT data-driven code: define your data as arrays/objects, then build HTML/text programmatically with .map()/.join(). Example: const data = [{h:8,p:5.2},{h:9,p:4.1}]; const rows = data.map(r => \`<tr><td>\${r.h}</td><td>\${r.p}</td></tr>\`).join(''); fs.writeFileSync('output.html', \`<table>\${rows}</table>\`); \u2014 the sandbox collects output files automatically. Do NOT embed large string literals.`};let i=this.resolvePath(r),a=this.checkBlocked(i);if(a)return a;try{if(Z.existsSync(i)&&Z.lstatSync(i).isSymbolicLink()){let c=Z.realpathSync(i);if(this.checkBlocked(c))return{success:!1,error:"Access denied: symlink target is a blocked path"}}}catch{}switch(s){case"read":return this.readFile(i);case"write":return this.writeFile(i,n);case"write_binary":return this.writeBinaryFile(i,n);case"append":return this.appendFile(i,n);case"list":return this.listDir(i);case"info":return this.fileInfo(i);case"exists":return this.fileExists(i);case"move":return this.moveFile(i,o);case"copy":return this.copyFile(i,o);case"delete":return this.deleteFile(i);case"send":return this.sendFile(i);default:return{success:!1,error:`Unknown action "${s}". Valid: read, write, write_binary, append, list, info, exists, move, copy, delete, send`}}}resolvePath(e){let t=process.env.HOME||process.env.USERPROFILE||"",s=e.startsWith("~")?e.replace("~",t):e;return st.resolve(s)}checkBlocked(e){let t=e.toLowerCase().replace(/\\/g,"/"),s=(process.env.HOME||process.env.USERPROFILE||"").toLowerCase().replace(/\\/g,"/"),r=["/etc/shadow","/etc/passwd","/etc/sudoers","/proc/","/sys/","/dev/","c:/windows/system32","c:/windows/syswow64"],n=["/.ssh","/.aws","/.gnupg"],o=[".env"];if(r.some(a=>t.startsWith(a)||t===a.replace(/\/$/,"")))return{success:!1,error:"Access to system directories/files is blocked for security"};if(s&&n.some(a=>t.startsWith(s+a)))return{success:!1,error:"Access to sensitive user directories is blocked for security"};let i=st.basename(e);return o.includes(i.toLowerCase())?{success:!1,error:"Access to sensitive files is blocked for security"}:null}readFile(e){try{let t=Z.statSync(e);if(t.isDirectory())return{success:!1,error:`"${e}" is a directory, not a file. Use action "list" instead.`};if(t.size>Qo){let r=Z.readFileSync(e,"utf-8").slice(0,Qo);return{success:!0,data:{path:e,size:t.size,truncated:!0},display:`${e} (${t.size} bytes, truncated to ${Qo}):
553
+ ${g.slice(0,2e3)}`}}catch(a){return{success:!1,error:`HTTP request failed: ${a instanceof Error?a.message:String(a)}`}}}isPrivateHost(e){if(e==="localhost"||e==="127.0.0.1"||e==="::1")return!0;let t=e.replace(/[\[\]]/g,"").toLowerCase();if(t.startsWith("fc")||t.startsWith("fd")||t==="::1")return!0;let s=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(e);if(s){let[,r,n]=s.map(Number);if(r===10||r===172&&n>=16&&n<=31||r===192&&n===168||r===127||r===169&&n===254||r===0)return!0}return!1}stripHtml(e){return e.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,"").replace(/<style[^>]*>[\s\S]*?<\/style>/gi,"").replace(/<[^>]+>/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#x27;/g,"'").replace(/&nbsp;/g," ").replace(/\s+/g," ").trim()}}});import Q from"node:fs";import nt from"node:path";var ti,Mc,Om,Jt,Oc=_(()=>{"use strict";U();ti=5e5,Mc=5e7,Om={".pdf":"application/pdf",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".odt":"application/vnd.oasis.opendocument.text",".ods":"application/vnd.oasis.opendocument.spreadsheet",".odp":"application/vnd.oasis.opendocument.presentation",".rtf":"application/rtf",".epub":"application/epub+zip",".txt":"text/plain",".md":"text/markdown",".csv":"text/csv",".tsv":"text/tab-separated-values",".html":"text/html",".htm":"text/html",".xml":"application/xml",".yaml":"application/yaml",".yml":"application/yaml",".toml":"application/toml",".ini":"text/plain",".cfg":"text/plain",".log":"text/plain",".json":"application/json",".js":"application/javascript",".ts":"application/typescript",".py":"text/x-python",".sh":"application/x-sh",".sql":"application/sql",".css":"text/css",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".bmp":"image/bmp",".ico":"image/x-icon",".tiff":"image/tiff",".tif":"image/tiff",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".flac":"audio/flac",".aac":"audio/aac",".m4a":"audio/mp4",".mp4":"video/mp4",".webm":"video/webm",".mkv":"video/x-matroska",".avi":"video/x-msvideo",".mov":"video/quicktime",".zip":"application/zip",".tar":"application/x-tar",".gz":"application/gzip",".7z":"application/x-7z-compressed",".rar":"application/vnd.rar",".ttf":"font/ttf",".otf":"font/otf",".woff":"font/woff",".woff2":"font/woff2"},Jt=class extends x{static{m(this,"FileSkill")}metadata={name:"file",category:"files",description:'Read, write, move, copy, or send files. Use for reading file contents, writing text to files, saving binary data, listing directory contents, moving/copying files, or getting file info. Use "send" to deliver a file to the user in the chat (PDF, images, etc.). Prefer this over shell for file operations. When a user sends a file attachment, it is saved to the inbox \u2014 use "move" to relocate it. IMPORTANT: For large content (HTML pages, long text), use code_sandbox instead to generate the file programmatically.',riskLevel:"write",version:"2.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["read","write","write_binary","append","list","info","exists","move","copy","delete","send"],description:"The file operation to perform"},path:{type:"string",description:"Absolute or relative file/directory path (~ expands to home)"},destination:{type:"string",description:"Destination path for move/copy actions (~ expands to home)"},content:{type:"string",description:"Content to write (required for write/append; base64-encoded for write_binary)"}},required:["action","path"]}};async execute(e,t){let s=e.action,r=e.path,n=e.content,o=e.destination;if(!s||!r)return{success:!1,error:'Missing required fields "action" and "path"'};if((s==="write"||s==="write_binary"||s==="append")&&!n)return{success:!1,error:`Missing "content" field for "${s}" action. The content is likely too large to include in a tool call. Use the code_sandbox skill instead \u2014 write COMPACT data-driven code: define your data as arrays/objects, then build HTML/text programmatically with .map()/.join(). Example: const data = [{h:8,p:5.2},{h:9,p:4.1}]; const rows = data.map(r => \`<tr><td>\${r.h}</td><td>\${r.p}</td></tr>\`).join(''); fs.writeFileSync('output.html', \`<table>\${rows}</table>\`); \u2014 the sandbox collects output files automatically. Do NOT embed large string literals.`};let i=this.resolvePath(r),a=this.checkBlocked(i);if(a)return a;try{if(Q.existsSync(i)&&Q.lstatSync(i).isSymbolicLink()){let c=Q.realpathSync(i);if(this.checkBlocked(c))return{success:!1,error:"Access denied: symlink target is a blocked path"}}}catch{}switch(s){case"read":return this.readFile(i);case"write":return this.writeFile(i,n);case"write_binary":return this.writeBinaryFile(i,n);case"append":return this.appendFile(i,n);case"list":return this.listDir(i);case"info":return this.fileInfo(i);case"exists":return this.fileExists(i);case"move":return this.moveFile(i,o);case"copy":return this.copyFile(i,o);case"delete":return this.deleteFile(i);case"send":return this.sendFile(i);default:return{success:!1,error:`Unknown action "${s}". Valid: read, write, write_binary, append, list, info, exists, move, copy, delete, send`}}}resolvePath(e){let t=process.env.HOME||process.env.USERPROFILE||"",s=e.startsWith("~")?e.replace("~",t):e;return nt.resolve(s)}checkBlocked(e){let t=e.toLowerCase().replace(/\\/g,"/"),s=(process.env.HOME||process.env.USERPROFILE||"").toLowerCase().replace(/\\/g,"/"),r=["/etc/shadow","/etc/passwd","/etc/sudoers","/proc/","/sys/","/dev/","c:/windows/system32","c:/windows/syswow64"],n=["/.ssh","/.aws","/.gnupg"],o=[".env"];if(r.some(a=>t.startsWith(a)||t===a.replace(/\/$/,"")))return{success:!1,error:"Access to system directories/files is blocked for security"};if(s&&n.some(a=>t.startsWith(s+a)))return{success:!1,error:"Access to sensitive user directories is blocked for security"};let i=nt.basename(e);return o.includes(i.toLowerCase())?{success:!1,error:"Access to sensitive files is blocked for security"}:null}readFile(e){try{let t=Q.statSync(e);if(t.isDirectory())return{success:!1,error:`"${e}" is a directory, not a file. Use action "list" instead.`};if(t.size>ti){let r=Q.readFileSync(e,"utf-8").slice(0,ti);return{success:!0,data:{path:e,size:t.size,truncated:!0},display:`${e} (${t.size} bytes, truncated to ${ti}):
531
554
 
532
- ${r}`}}let s=Z.readFileSync(e,"utf-8");return{success:!0,data:{path:e,size:t.size,content:s},display:s}}catch(t){return{success:!1,error:`Cannot read "${e}": ${t.message}`}}}writeFile(e,t){if(t==null)return{success:!1,error:'Missing "content" for write action'};try{let s=st.dirname(e);return Z.mkdirSync(s,{recursive:!0}),Z.writeFileSync(e,t,"utf-8"),{success:!0,data:{path:e,bytes:Buffer.byteLength(t)},display:`Written ${Buffer.byteLength(t)} bytes to ${e}`}}catch(s){return{success:!1,error:`Cannot write "${e}": ${s.message}`}}}appendFile(e,t){if(t==null)return{success:!1,error:'Missing "content" for append action'};try{return Z.appendFileSync(e,t,"utf-8"),{success:!0,data:{path:e,appendedBytes:Buffer.byteLength(t)},display:`Appended ${Buffer.byteLength(t)} bytes to ${e}`}}catch(s){return{success:!1,error:`Cannot append to "${e}": ${s.message}`}}}listDir(e){try{let s=Z.readdirSync(e,{withFileTypes:!0}).map(n=>({name:n.name,type:n.isDirectory()?"dir":n.isSymbolicLink()?"symlink":"file"})),r=s.length===0?`${e}: (empty)`:s.map(n=>`${n.type==="dir"?"\u{1F4C1}":"\u{1F4C4}"} ${n.name}`).join(`
533
- `);return{success:!0,data:{path:e,entries:s},display:r}}catch(t){return{success:!1,error:`Cannot list "${e}": ${t.message}`}}}fileInfo(e){try{let t=Z.statSync(e),s={path:e,type:t.isDirectory()?"directory":t.isFile()?"file":"other",size:t.size,created:t.birthtime.toISOString(),modified:t.mtime.toISOString(),permissions:t.mode.toString(8)};return{success:!0,data:s,display:`${s.type}: ${e}
555
+ ${r}`}}let s=Q.readFileSync(e,"utf-8");return{success:!0,data:{path:e,size:t.size,content:s},display:s}}catch(t){return{success:!1,error:`Cannot read "${e}": ${t.message}`}}}writeFile(e,t){if(t==null)return{success:!1,error:'Missing "content" for write action'};try{let s=nt.dirname(e);return Q.mkdirSync(s,{recursive:!0}),Q.writeFileSync(e,t,"utf-8"),{success:!0,data:{path:e,bytes:Buffer.byteLength(t)},display:`Written ${Buffer.byteLength(t)} bytes to ${e}`}}catch(s){return{success:!1,error:`Cannot write "${e}": ${s.message}`}}}appendFile(e,t){if(t==null)return{success:!1,error:'Missing "content" for append action'};try{return Q.appendFileSync(e,t,"utf-8"),{success:!0,data:{path:e,appendedBytes:Buffer.byteLength(t)},display:`Appended ${Buffer.byteLength(t)} bytes to ${e}`}}catch(s){return{success:!1,error:`Cannot append to "${e}": ${s.message}`}}}listDir(e){try{let s=Q.readdirSync(e,{withFileTypes:!0}).map(n=>({name:n.name,type:n.isDirectory()?"dir":n.isSymbolicLink()?"symlink":"file"})),r=s.length===0?`${e}: (empty)`:s.map(n=>`${n.type==="dir"?"\u{1F4C1}":"\u{1F4C4}"} ${n.name}`).join(`
556
+ `);return{success:!0,data:{path:e,entries:s},display:r}}catch(t){return{success:!1,error:`Cannot list "${e}": ${t.message}`}}}fileInfo(e){try{let t=Q.statSync(e),s={path:e,type:t.isDirectory()?"directory":t.isFile()?"file":"other",size:t.size,created:t.birthtime.toISOString(),modified:t.mtime.toISOString(),permissions:t.mode.toString(8)};return{success:!0,data:s,display:`${s.type}: ${e}
534
557
  Size: ${t.size} bytes
535
- Modified: ${s.modified}`}}catch(t){return{success:!1,error:`Cannot stat "${e}": ${t.message}`}}}fileExists(e){let t=Z.existsSync(e);return{success:!0,data:{path:e,exists:t},display:t?`Yes, "${e}" exists`:`No, "${e}" does not exist`}}writeBinaryFile(e,t){if(!t)return{success:!1,error:'Missing "content" (base64-encoded) for write_binary action'};try{let s=st.dirname(e);Z.mkdirSync(s,{recursive:!0});let r=Buffer.from(t,"base64");return Z.writeFileSync(e,r),{success:!0,data:{path:e,bytes:r.length},display:`Written ${r.length} bytes (binary) to ${e}`}}catch(s){return{success:!1,error:`Cannot write "${e}": ${s.message}`}}}moveFile(e,t){if(!t)return{success:!1,error:'Missing "destination" for move action'};let s=this.resolvePath(t),r=this.checkBlocked(s);if(r)return r;try{let n=st.dirname(s);return Z.mkdirSync(n,{recursive:!0}),Z.renameSync(e,s),{success:!0,data:{from:e,to:s},display:`Moved ${e} \u2192 ${s}`}}catch{try{return Z.copyFileSync(e,s),Z.unlinkSync(e),{success:!0,data:{from:e,to:s},display:`Moved ${e} \u2192 ${s}`}}catch(o){return{success:!1,error:`Cannot move "${e}" to "${s}": ${o.message}`}}}}copyFile(e,t){if(!t)return{success:!1,error:'Missing "destination" for copy action'};let s=this.resolvePath(t),r=this.checkBlocked(s);if(r)return r;try{let n=st.dirname(s);return Z.mkdirSync(n,{recursive:!0}),Z.copyFileSync(e,s),{success:!0,data:{from:e,to:s},display:`Copied ${e} \u2192 ${s}`}}catch(n){return{success:!1,error:`Cannot copy "${e}" to "${s}": ${n.message}`}}}sendFile(e){try{if(!Z.existsSync(e))return{success:!1,error:`"${e}" does not exist`};let t=Z.statSync(e);if(t.isDirectory())return{success:!1,error:`"${e}" is a directory, not a file`};if(t.size===0)return{success:!1,error:`"${e}" is empty (0 bytes) \u2014 cannot send an empty file`};if(t.size>Cc)return{success:!1,error:`File too large to send (${t.size} bytes, max ${Cc})`};let s=Z.readFileSync(e),r=st.basename(e),n=st.extname(e).toLowerCase(),o=Dm[n]||"application/octet-stream",i={fileName:r,data:s,mimeType:o};return{success:!0,data:{path:e,size:t.size,fileName:r,mimeType:o},display:`Sending ${r} (${t.size} bytes)`,attachments:[i]}}catch(t){return{success:!1,error:`Cannot send "${e}": ${t.message}`}}}deleteFile(e){try{return Z.existsSync(e)?Z.statSync(e).isDirectory()?{success:!1,error:`"${e}" is a directory. Use shell for directory deletion.`}:(Z.unlinkSync(e),{success:!0,data:{path:e},display:`Deleted ${e}`}):{success:!1,error:`"${e}" does not exist`}}catch(t){return{success:!1,error:`Cannot delete "${e}": ${t.message}`}}}}});import{execSync as Yt}from"node:child_process";var Jt,Lc=_(()=>{"use strict";U();Jt=class extends R{static{m(this,"ClipboardSkill")}metadata={name:"clipboard",category:"media",description:"Read or write the system clipboard. Use when the user asks to copy something, paste from clipboard, or check what is in their clipboard.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["read","write"],description:'"read" to get clipboard contents, "write" to set clipboard contents'},text:{type:"string",description:"Text to copy to clipboard (required for write)"}},required:["action"]}};async execute(e,t){let s=e.action;switch(s){case"read":return this.readClipboard();case"write":return this.writeClipboard(e.text);default:return{success:!1,error:`Unknown action "${s}". Valid: read, write`}}}readClipboard(){try{let e;switch(process.platform){case"darwin":e=Yt("pbpaste",{encoding:"utf-8",timeout:5e3});break;case"win32":e=Yt("powershell -NoProfile -Command Get-Clipboard",{encoding:"utf-8",timeout:5e3}).replace(/\r\n$/,"");break;default:e=Yt("xclip -selection clipboard -o 2>/dev/null || xsel --clipboard --output",{encoding:"utf-8",timeout:5e3});break}return!e||e.trim().length===0?{success:!0,data:{content:""},display:"Clipboard is empty."}:{success:!0,data:{content:e},display:e.length>2e3?e.slice(0,2e3)+`
558
+ Modified: ${s.modified}`}}catch(t){return{success:!1,error:`Cannot stat "${e}": ${t.message}`}}}fileExists(e){let t=Q.existsSync(e);return{success:!0,data:{path:e,exists:t},display:t?`Yes, "${e}" exists`:`No, "${e}" does not exist`}}writeBinaryFile(e,t){if(!t)return{success:!1,error:'Missing "content" (base64-encoded) for write_binary action'};try{let s=nt.dirname(e);Q.mkdirSync(s,{recursive:!0});let r=Buffer.from(t,"base64");return Q.writeFileSync(e,r),{success:!0,data:{path:e,bytes:r.length},display:`Written ${r.length} bytes (binary) to ${e}`}}catch(s){return{success:!1,error:`Cannot write "${e}": ${s.message}`}}}moveFile(e,t){if(!t)return{success:!1,error:'Missing "destination" for move action'};let s=this.resolvePath(t),r=this.checkBlocked(s);if(r)return r;try{let n=nt.dirname(s);return Q.mkdirSync(n,{recursive:!0}),Q.renameSync(e,s),{success:!0,data:{from:e,to:s},display:`Moved ${e} \u2192 ${s}`}}catch{try{return Q.copyFileSync(e,s),Q.unlinkSync(e),{success:!0,data:{from:e,to:s},display:`Moved ${e} \u2192 ${s}`}}catch(o){return{success:!1,error:`Cannot move "${e}" to "${s}": ${o.message}`}}}}copyFile(e,t){if(!t)return{success:!1,error:'Missing "destination" for copy action'};let s=this.resolvePath(t),r=this.checkBlocked(s);if(r)return r;try{let n=nt.dirname(s);return Q.mkdirSync(n,{recursive:!0}),Q.copyFileSync(e,s),{success:!0,data:{from:e,to:s},display:`Copied ${e} \u2192 ${s}`}}catch(n){return{success:!1,error:`Cannot copy "${e}" to "${s}": ${n.message}`}}}sendFile(e){try{if(!Q.existsSync(e))return{success:!1,error:`"${e}" does not exist`};let t=Q.statSync(e);if(t.isDirectory())return{success:!1,error:`"${e}" is a directory, not a file`};if(t.size===0)return{success:!1,error:`"${e}" is empty (0 bytes) \u2014 cannot send an empty file`};if(t.size>Mc)return{success:!1,error:`File too large to send (${t.size} bytes, max ${Mc})`};let s=Q.readFileSync(e),r=nt.basename(e),n=nt.extname(e).toLowerCase(),o=Om[n]||"application/octet-stream",i={fileName:r,data:s,mimeType:o};return{success:!0,data:{path:e,size:t.size,fileName:r,mimeType:o},display:`Sending ${r} (${t.size} bytes)`,attachments:[i]}}catch(t){return{success:!1,error:`Cannot send "${e}": ${t.message}`}}}deleteFile(e){try{return Q.existsSync(e)?Q.statSync(e).isDirectory()?{success:!1,error:`"${e}" is a directory. Use shell for directory deletion.`}:(Q.unlinkSync(e),{success:!0,data:{path:e},display:`Deleted ${e}`}):{success:!1,error:`"${e}" does not exist`}}catch(t){return{success:!1,error:`Cannot delete "${e}": ${t.message}`}}}}});import{execSync as Zt}from"node:child_process";var Qt,Pc=_(()=>{"use strict";U();Qt=class extends x{static{m(this,"ClipboardSkill")}metadata={name:"clipboard",category:"media",description:"Read or write the system clipboard. Use when the user asks to copy something, paste from clipboard, or check what is in their clipboard.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["read","write"],description:'"read" to get clipboard contents, "write" to set clipboard contents'},text:{type:"string",description:"Text to copy to clipboard (required for write)"}},required:["action"]}};async execute(e,t){let s=e.action;switch(s){case"read":return this.readClipboard();case"write":return this.writeClipboard(e.text);default:return{success:!1,error:`Unknown action "${s}". Valid: read, write`}}}readClipboard(){try{let e;switch(process.platform){case"darwin":e=Zt("pbpaste",{encoding:"utf-8",timeout:5e3});break;case"win32":e=Zt("powershell -NoProfile -Command Get-Clipboard",{encoding:"utf-8",timeout:5e3}).replace(/\r\n$/,"");break;default:e=Zt("xclip -selection clipboard -o 2>/dev/null || xsel --clipboard --output",{encoding:"utf-8",timeout:5e3});break}return!e||e.trim().length===0?{success:!0,data:{content:""},display:"Clipboard is empty."}:{success:!0,data:{content:e},display:e.length>2e3?e.slice(0,2e3)+`
536
559
 
537
- [... truncated]`:e}}catch(e){return{success:!1,error:`Failed to read clipboard: ${e.message}`}}}writeClipboard(e){if(!e||typeof e!="string")return{success:!1,error:'Missing "text" for write action'};try{switch(process.platform){case"darwin":Yt("pbcopy",{input:e,timeout:5e3});break;case"win32":Yt('powershell -NoProfile -Command "$input | Set-Clipboard"',{input:e,timeout:5e3});break;default:Yt("xclip -selection clipboard 2>/dev/null || xsel --clipboard --input",{input:e,timeout:5e3});break}return{success:!0,data:{copiedLength:e.length},display:`Copied ${e.length} characters to clipboard.`}}catch(t){return{success:!1,error:`Failed to write clipboard: ${t.message}`}}}}});import{execSync as ir}from"node:child_process";import Nc from"node:path";import Lm from"node:os";var Zt,Mc=_(()=>{"use strict";U();Zt=class extends R{static{m(this,"ScreenshotSkill")}metadata={name:"screenshot",category:"media",description:"Take a screenshot of the current screen and save it to a file. Use when the user asks to capture their screen or take a screenshot.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{path:{type:"string",description:"Output file path (optional, defaults to ~/Desktop/screenshot-<timestamp>.png)"}}}};async execute(e,t){let s=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),r=Nc.join(Lm.homedir(),"Desktop"),n=e.path||Nc.join(r,`screenshot-${s}.png`);try{switch(process.platform){case"darwin":ir(`screencapture -x "${n}"`,{timeout:1e4});break;case"win32":ir(`powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; $screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds; $bitmap = New-Object System.Drawing.Bitmap($screen.Width, $screen.Height); $graphics = [System.Drawing.Graphics]::FromImage($bitmap); $graphics.CopyFromScreen($screen.Location, [System.Drawing.Point]::Empty, $screen.Size); $bitmap.Save('${n.replace(/'/g,"''")}'); $graphics.Dispose(); $bitmap.Dispose()"`,{timeout:1e4});break;default:try{ir(`scrot "${n}"`,{timeout:1e4})}catch{try{ir(`import -window root "${n}"`,{timeout:1e4})}catch{ir(`gnome-screenshot -f "${n}"`,{timeout:1e4})}}break}return{success:!0,data:{path:n},display:`Screenshot saved to ${n}`}}catch(o){return{success:!1,error:`Screenshot failed: ${o.message}`}}}}});import Nm from"node:path";import Mm from"node:os";var Oc,Qt,Pc=_(()=>{"use strict";U();Oc=5e4,Qt=class extends R{static{m(this,"BrowserSkill")}browser=null;page=null;metadata={name:"browser",category:"media",description:"Open web pages in a real browser (Puppeteer/Chromium). Renders JavaScript, so it works with SPAs and dynamic sites. Can also interact with pages: click buttons, fill forms, take screenshots. Use when http skill returns empty/broken content, or when you need to interact with a web page.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["open","screenshot","click","type","evaluate","close"],description:"open = navigate to URL and return page text. screenshot = save screenshot of current page. click = click element by CSS selector. type = type text into input by CSS selector. evaluate = run JavaScript on the page. close = close the browser."},url:{type:"string",description:'URL to open (required for "open", optional for "screenshot")'},selector:{type:"string",description:'CSS selector for the element (required for "click" and "type")'},text:{type:"string",description:'Text to type (required for "type")'},script:{type:"string",description:'JavaScript code to evaluate (required for "evaluate")'},path:{type:"string",description:"File path to save screenshot (optional, defaults to Desktop)"}},required:["action"]}};async execute(e,t){let s=e.action;if(s==="close")return this.closeBrowser();let r=await this.loadPuppeteer();if(!r)return{success:!1,error:`Puppeteer is not installed. Run: npm install -g puppeteer
560
+ [... truncated]`:e}}catch(e){return{success:!1,error:`Failed to read clipboard: ${e.message}`}}}writeClipboard(e){if(!e||typeof e!="string")return{success:!1,error:'Missing "text" for write action'};try{switch(process.platform){case"darwin":Zt("pbcopy",{input:e,timeout:5e3});break;case"win32":Zt('powershell -NoProfile -Command "$input | Set-Clipboard"',{input:e,timeout:5e3});break;default:Zt("xclip -selection clipboard 2>/dev/null || xsel --clipboard --input",{input:e,timeout:5e3});break}return{success:!0,data:{copiedLength:e.length},display:`Copied ${e.length} characters to clipboard.`}}catch(t){return{success:!1,error:`Failed to write clipboard: ${t.message}`}}}}});import{execSync as dr}from"node:child_process";import Uc from"node:path";import Pm from"node:os";var es,Fc=_(()=>{"use strict";U();es=class extends x{static{m(this,"ScreenshotSkill")}metadata={name:"screenshot",category:"media",description:"Take a screenshot of the current screen and save it to a file. Use when the user asks to capture their screen or take a screenshot.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{path:{type:"string",description:"Output file path (optional, defaults to ~/Desktop/screenshot-<timestamp>.png)"}}}};async execute(e,t){let s=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),r=Uc.join(Pm.homedir(),"Desktop"),n=e.path||Uc.join(r,`screenshot-${s}.png`);try{switch(process.platform){case"darwin":dr(`screencapture -x "${n}"`,{timeout:1e4});break;case"win32":dr(`powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; $screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds; $bitmap = New-Object System.Drawing.Bitmap($screen.Width, $screen.Height); $graphics = [System.Drawing.Graphics]::FromImage($bitmap); $graphics.CopyFromScreen($screen.Location, [System.Drawing.Point]::Empty, $screen.Size); $bitmap.Save('${n.replace(/'/g,"''")}'); $graphics.Dispose(); $bitmap.Dispose()"`,{timeout:1e4});break;default:try{dr(`scrot "${n}"`,{timeout:1e4})}catch{try{dr(`import -window root "${n}"`,{timeout:1e4})}catch{dr(`gnome-screenshot -f "${n}"`,{timeout:1e4})}}break}return{success:!0,data:{path:n},display:`Screenshot saved to ${n}`}}catch(o){return{success:!1,error:`Screenshot failed: ${o.message}`}}}}});import Um from"node:path";import Fm from"node:os";var jc,ts,Bc=_(()=>{"use strict";U();jc=5e4,ts=class extends x{static{m(this,"BrowserSkill")}browser=null;page=null;metadata={name:"browser",category:"media",description:"Open web pages in a real browser (Puppeteer/Chromium). Renders JavaScript, so it works with SPAs and dynamic sites. Can also interact with pages: click buttons, fill forms, take screenshots. Use when http skill returns empty/broken content, or when you need to interact with a web page.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["open","screenshot","click","type","evaluate","close"],description:"open = navigate to URL and return page text. screenshot = save screenshot of current page. click = click element by CSS selector. type = type text into input by CSS selector. evaluate = run JavaScript on the page. close = close the browser."},url:{type:"string",description:'URL to open (required for "open", optional for "screenshot")'},selector:{type:"string",description:'CSS selector for the element (required for "click" and "type")'},text:{type:"string",description:'Text to type (required for "type")'},script:{type:"string",description:'JavaScript code to evaluate (required for "evaluate")'},path:{type:"string",description:"File path to save screenshot (optional, defaults to Desktop)"}},required:["action"]}};async execute(e,t){let s=e.action;if(s==="close")return this.closeBrowser();let r=await this.loadPuppeteer();if(!r)return{success:!1,error:`Puppeteer is not installed. Run: npm install -g puppeteer
538
561
  Or add it to Alfred: npm install puppeteer`};switch(s){case"open":return this.openPage(r,e);case"screenshot":return this.screenshotPage(r,e);case"click":return this.clickElement(e);case"type":return this.typeText(e);case"evaluate":return this.evaluateScript(e);default:return{success:!1,error:`Unknown action "${s}". Valid: open, screenshot, click, type, evaluate, close`}}}async loadPuppeteer(){try{let e=await Function('return import("puppeteer")')();return this.resolvePuppeteerModule(e)}catch{try{let e=await Function('return import("puppeteer-core")')();return this.resolvePuppeteerModule(e)}catch{return null}}}resolvePuppeteerModule(e){let t=e;return typeof t.launch=="function"?t:t.default}async ensureBrowser(e){return this.browser&&this.browser.connected?this.browser:(this.browser=await e.launch({headless:!0,args:["--no-sandbox","--disable-setuid-sandbox","--disable-dev-shm-usage"]}),this.browser)}async ensurePage(e){let t=await this.ensureBrowser(e);return this.page||(this.page=await t.newPage(),await this.page.setViewport({width:1280,height:900})),this.page}async openPage(e,t){let s=t.url;if(!s)return{success:!1,error:'Missing "url" for open action'};let r=this.validateUrl(s);if(r)return{success:!1,error:r};try{let n=await this.ensurePage(e);await n.goto(s,{waitUntil:"networkidle2",timeout:3e4});let o=await n.title(),i=await n.evaluate(`
539
562
  (() => {
540
563
  document.querySelectorAll('script, style, noscript').forEach(el => el.remove());
541
564
  return document.body?.innerText ?? '';
542
565
  })()
543
- `),c=(i.length>Oc?i.slice(0,Oc)+`
566
+ `),c=(i.length>jc?i.slice(0,jc)+`
544
567
 
545
568
  [... truncated]`:i).replace(/\n{3,}/g,`
546
569
 
547
570
  `).trim();return{success:!0,data:{url:n.url(),title:o,length:i.length},display:`**${o}** (${n.url()})
548
571
 
549
- ${c}`}}catch(n){return{success:!1,error:`Failed to open "${s}": ${n.message}`}}}async screenshotPage(e,t){try{let s=await this.ensurePage(e),r=t.url;r&&await s.goto(r,{waitUntil:"networkidle2",timeout:3e4});let n=s.url();if(n==="about:blank")return{success:!1,error:'No page is open. Use action "open" with a URL first, or provide a URL.'};let o=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),i=t.path||Nm.join(Mm.homedir(),"Desktop",`browser-${o}.png`);return await s.screenshot({path:i,fullPage:!1}),{success:!0,data:{path:i,url:n},display:`Screenshot saved to ${i}`}}catch(s){return{success:!1,error:`Screenshot failed: ${s.message}`}}}async clickElement(e){let t=e.selector;if(!t)return{success:!1,error:'Missing "selector" for click action'};if(!this.page)return{success:!1,error:'No page is open. Use action "open" first.'};try{await this.page.waitForSelector(t,{timeout:5e3}),await this.page.click(t);try{await this.page.waitForNavigation({timeout:3e3})}catch{}let s=await this.page.title();return{success:!0,data:{selector:t,url:this.page.url(),title:s},display:`Clicked "${t}" \u2014 now on: ${s} (${this.page.url()})`}}catch(s){return{success:!1,error:`Click failed on "${t}": ${s.message}`}}}async typeText(e){let t=e.selector,s=e.text;if(!t)return{success:!1,error:'Missing "selector" for type action'};if(!s)return{success:!1,error:'Missing "text" for type action'};if(!this.page)return{success:!1,error:'No page is open. Use action "open" first.'};try{return await this.page.waitForSelector(t,{timeout:5e3}),await this.page.click(t),await this.page.type(t,s,{delay:50}),{success:!0,data:{selector:t,textLength:s.length},display:`Typed ${s.length} characters into "${t}"`}}catch(r){return{success:!1,error:`Type failed on "${t}": ${r.message}`}}}async evaluateScript(e){let t=e.script;if(!t)return{success:!1,error:'Missing "script" for evaluate action'};if(!this.page)return{success:!1,error:'No page is open. Use action "open" first.'};try{let s=await this.page.evaluate(t),r=typeof s=="string"?s:JSON.stringify(s,null,2);return{success:!0,data:{result:s},display:r?.slice(0,1e4)??"(no output)"}}catch(s){return{success:!1,error:`Evaluate failed: ${s.message}`}}}validateUrl(e){let t;try{t=new URL(e)}catch{return`Invalid URL: "${e}"`}if(["file:","chrome:","about:","data:","javascript:"].includes(t.protocol))return`Blocked URL protocol "${t.protocol}". Only http: and https: are allowed.`;if(t.protocol!=="http:"&&t.protocol!=="https:")return`Unsupported URL protocol "${t.protocol}". Only http: and https: are allowed.`;let r=t.hostname;return this.isPrivateHost(r)?`Access to private/internal network address "${r}" is blocked.`:null}isPrivateHost(e){if(e==="localhost"||e==="127.0.0.1"||e==="::1")return!0;if(e.startsWith("[")||e.toLowerCase().startsWith("fc")||e.toLowerCase().startsWith("fd")){let s=e.replace(/[\[\]]/g,"").toLowerCase();if(s.startsWith("fc")||s.startsWith("fd")||s==="::1")return!0}let t=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(e);if(t){let[,s,r]=t.map(Number);if(s===10||s===172&&r>=16&&r<=31||s===192&&r===168||s===127||s===169&&r===254||s===0)return!0}return!1}async closeBrowser(){try{return this.page=null,this.browser&&(await this.browser.close(),this.browser=null),{success:!0,display:"Browser closed."}}catch(e){return this.browser=null,this.page=null,{success:!1,error:`Close failed: ${e.message}`}}}}});var es,Uc=_(()=>{"use strict";U();Me();es=class extends R{static{m(this,"ProfileSkill")}userRepo;metadata={name:"profile",category:"core",description:"Manage user profile settings including timezone, language, and bio. Use this to personalize Alfred for each user.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["get","set_timezone","set_language","set_bio","set_preference"],description:"The profile action to perform"},value:{type:"string",description:"The value to set (for set_* actions)"},preference_key:{type:"string",description:"The preference key (for set_preference)"},preference_value:{type:"string",description:"The preference value (for set_preference)"}},required:["action"]}};constructor(e){super(),this.userRepo=e}async execute(e,t){let s=e.action,r=ne(t);switch(s){case"get":return this.getProfile(r);case"set_timezone":return this.setField(r,"timezone",e.value);case"set_language":return this.setField(r,"language",e.value);case"set_bio":return this.setField(r,"bio",e.value);case"set_preference":return this.setPreference(r,e.preference_key,e.preference_value);default:return{success:!1,error:`Unknown action: "${String(s)}"`}}}getProfile(e){let t=this.userRepo.getProfile(e);if(!t)return{success:!0,data:null,display:"No profile found. Set your timezone, language, or bio to create one."};let s=[];if(t.displayName&&s.push(`Name: ${t.displayName}`),t.timezone&&s.push(`Timezone: ${t.timezone}`),t.language&&s.push(`Language: ${t.language}`),t.bio&&s.push(`Bio: ${t.bio}`),t.preferences)for(let[r,n]of Object.entries(t.preferences))s.push(`${r}: ${String(n)}`);return{success:!0,data:t,display:s.length>0?`Profile:
572
+ ${c}`}}catch(n){return{success:!1,error:`Failed to open "${s}": ${n.message}`}}}async screenshotPage(e,t){try{let s=await this.ensurePage(e),r=t.url;r&&await s.goto(r,{waitUntil:"networkidle2",timeout:3e4});let n=s.url();if(n==="about:blank")return{success:!1,error:'No page is open. Use action "open" with a URL first, or provide a URL.'};let o=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),i=t.path||Um.join(Fm.homedir(),"Desktop",`browser-${o}.png`);return await s.screenshot({path:i,fullPage:!1}),{success:!0,data:{path:i,url:n},display:`Screenshot saved to ${i}`}}catch(s){return{success:!1,error:`Screenshot failed: ${s.message}`}}}async clickElement(e){let t=e.selector;if(!t)return{success:!1,error:'Missing "selector" for click action'};if(!this.page)return{success:!1,error:'No page is open. Use action "open" first.'};try{await this.page.waitForSelector(t,{timeout:5e3}),await this.page.click(t);try{await this.page.waitForNavigation({timeout:3e3})}catch{}let s=await this.page.title();return{success:!0,data:{selector:t,url:this.page.url(),title:s},display:`Clicked "${t}" \u2014 now on: ${s} (${this.page.url()})`}}catch(s){return{success:!1,error:`Click failed on "${t}": ${s.message}`}}}async typeText(e){let t=e.selector,s=e.text;if(!t)return{success:!1,error:'Missing "selector" for type action'};if(!s)return{success:!1,error:'Missing "text" for type action'};if(!this.page)return{success:!1,error:'No page is open. Use action "open" first.'};try{return await this.page.waitForSelector(t,{timeout:5e3}),await this.page.click(t),await this.page.type(t,s,{delay:50}),{success:!0,data:{selector:t,textLength:s.length},display:`Typed ${s.length} characters into "${t}"`}}catch(r){return{success:!1,error:`Type failed on "${t}": ${r.message}`}}}async evaluateScript(e){let t=e.script;if(!t)return{success:!1,error:'Missing "script" for evaluate action'};if(!this.page)return{success:!1,error:'No page is open. Use action "open" first.'};try{let s=await this.page.evaluate(t),r=typeof s=="string"?s:JSON.stringify(s,null,2);return{success:!0,data:{result:s},display:r?.slice(0,1e4)??"(no output)"}}catch(s){return{success:!1,error:`Evaluate failed: ${s.message}`}}}validateUrl(e){let t;try{t=new URL(e)}catch{return`Invalid URL: "${e}"`}if(["file:","chrome:","about:","data:","javascript:"].includes(t.protocol))return`Blocked URL protocol "${t.protocol}". Only http: and https: are allowed.`;if(t.protocol!=="http:"&&t.protocol!=="https:")return`Unsupported URL protocol "${t.protocol}". Only http: and https: are allowed.`;let r=t.hostname;return this.isPrivateHost(r)?`Access to private/internal network address "${r}" is blocked.`:null}isPrivateHost(e){if(e==="localhost"||e==="127.0.0.1"||e==="::1")return!0;if(e.startsWith("[")||e.toLowerCase().startsWith("fc")||e.toLowerCase().startsWith("fd")){let s=e.replace(/[\[\]]/g,"").toLowerCase();if(s.startsWith("fc")||s.startsWith("fd")||s==="::1")return!0}let t=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(e);if(t){let[,s,r]=t.map(Number);if(s===10||s===172&&r>=16&&r<=31||s===192&&r===168||s===127||s===169&&r===254||s===0)return!0}return!1}async closeBrowser(){try{return this.page=null,this.browser&&(await this.browser.close(),this.browser=null),{success:!0,display:"Browser closed."}}catch(e){return this.browser=null,this.page=null,{success:!1,error:`Close failed: ${e.message}`}}}}});var ss,Hc=_(()=>{"use strict";U();Pe();ss=class extends x{static{m(this,"ProfileSkill")}userRepo;metadata={name:"profile",category:"core",description:"Manage user profile settings including timezone, language, and bio. Use this to personalize Alfred for each user.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["get","set_timezone","set_language","set_bio","set_preference"],description:"The profile action to perform"},value:{type:"string",description:"The value to set (for set_* actions)"},preference_key:{type:"string",description:"The preference key (for set_preference)"},preference_value:{type:"string",description:"The preference value (for set_preference)"}},required:["action"]}};constructor(e){super(),this.userRepo=e}async execute(e,t){let s=e.action,r=ne(t);switch(s){case"get":return this.getProfile(r);case"set_timezone":return this.setField(r,"timezone",e.value);case"set_language":return this.setField(r,"language",e.value);case"set_bio":return this.setField(r,"bio",e.value);case"set_preference":return this.setPreference(r,e.preference_key,e.preference_value);default:return{success:!1,error:`Unknown action: "${String(s)}"`}}}getProfile(e){let t=this.userRepo.getProfile(e);if(!t)return{success:!0,data:null,display:"No profile found. Set your timezone, language, or bio to create one."};let s=[];if(t.displayName&&s.push(`Name: ${t.displayName}`),t.timezone&&s.push(`Timezone: ${t.timezone}`),t.language&&s.push(`Language: ${t.language}`),t.bio&&s.push(`Bio: ${t.bio}`),t.preferences)for(let[r,n]of Object.entries(t.preferences))s.push(`${r}: ${String(n)}`);return{success:!0,data:t,display:s.length>0?`Profile:
550
573
  ${s.map(r=>`- ${r}`).join(`
551
- `)}`:"Profile is empty."}}setField(e,t,s){return!s||typeof s!="string"?{success:!1,error:`Missing required "value" for ${t}`}:(this.userRepo.updateProfile(e,{[t]:s}),{success:!0,data:{[t]:s},display:`${t} set to "${s}"`})}setPreference(e,t,s){if(!t||typeof t!="string")return{success:!1,error:'Missing required "preference_key"'};let n=this.userRepo.getProfile(e)?.preferences??{};return n[t]=s,this.userRepo.updateProfile(e,{preferences:n}),{success:!0,data:{key:t,value:s},display:`Preference "${t}" set to "${s}"`}}}});var Oe,ar=_(()=>{"use strict";Oe=class{static{m(this,"CalendarProvider")}timezone=Intl.DateTimeFormat().resolvedOptions().timeZone}});var Fc={};ue(Fc,{CalDAVProvider:()=>mn});var mn,ei=_(()=>{"use strict";ar();mn=class extends Oe{static{m(this,"CalDAVProvider")}config;client;constructor(e){super(),this.config=e}async initialize(){try{let e=await import("tsdav"),{createDAVClient:t}=e;this.client=await t({serverUrl:this.config.serverUrl,credentials:{username:this.config.username,password:this.config.password},authMethod:"Basic",defaultAccountType:"caldav"})}catch(e){throw new Error(`CalDAV initialization failed: ${e instanceof Error?e.message:String(e)}`)}}async listEvents(e,t){let s=await this.client.fetchCalendars();if(!s||s.length===0)return[];let r=[];for(let n of s){let o=await this.client.fetchCalendarObjects({calendar:n,timeRange:{start:e.toISOString(),end:t.toISOString()}});for(let i of o){let a=this.parseICalEvent(i.data,i.url);a&&r.push(a)}}return r.sort((n,o)=>n.start.getTime()-o.start.getTime())}async createEvent(e){let t=await this.client.fetchCalendars();if(!t||t.length===0)throw new Error("No calendars found");let s=`alfred-${Date.now()}@alfred`,r=this.buildICalEvent(s,e);return await this.client.createCalendarObject({calendar:t[0],filename:`${s}.ics`,iCalString:r}),{id:s,title:e.title,start:e.start,end:e.end,location:e.location,description:e.description,allDay:e.allDay}}async updateEvent(e,t){let s=await this.client.fetchCalendars();for(let r of s){let n=await this.client.fetchCalendarObjects({calendar:r});for(let o of n)if(o.url?.includes(e)||o.data?.includes(e)){let i=this.parseICalEvent(o.data,o.url);if(!i)continue;let a={title:t.title??i.title,start:t.start??i.start,end:t.end??i.end,location:t.location??i.location,description:t.description??i.description,allDay:t.allDay??i.allDay},c=this.buildICalEvent(e,a);return await this.client.updateCalendarObject({calendarObject:{...o,data:c}}),{id:e,...a}}}throw new Error(`Event ${e} not found`)}async deleteEvent(e){let t=await this.client.fetchCalendars();for(let s of t){let r=await this.client.fetchCalendarObjects({calendar:s});for(let n of r)if(n.url?.includes(e)||n.data?.includes(e)){await this.client.deleteCalendarObject({calendarObject:n});return}}throw new Error(`Event ${e} not found`)}async checkAvailability(e,t){let r=(await this.listEvents(e,t)).filter(n=>!n.allDay&&n.start<t&&n.end>e);return{available:r.length===0,conflicts:r}}parseICalEvent(e,t){try{let s=e.split(`
552
- `).map(p=>p.trim()),r=m(p=>s.find(g=>g.startsWith(p+":"))?.slice(p.length+1),"get"),n=r("SUMMARY"),o=r("DTSTART")??r("DTSTART;VALUE=DATE"),i=r("DTEND")??r("DTEND;VALUE=DATE"),a=r("LOCATION"),c=r("DESCRIPTION"),d=r("UID")??t;if(!n||!o)return;let u=o.length===8;return{id:d,title:n,start:this.parseICalDate(o),end:i?this.parseICalDate(i):this.parseICalDate(o),location:a||void 0,description:c||void 0,allDay:u}}catch(s){console.error("[caldav] Failed to parse iCal event",s);return}}parseICalDate(e){if(e.length===8)return new Date(`${e.slice(0,4)}-${e.slice(4,6)}-${e.slice(6,8)}`);let t=e.replace(/[^0-9TZ]/g,"");return t.length>=15?new Date(`${t.slice(0,4)}-${t.slice(4,6)}-${t.slice(6,8)}T${t.slice(9,11)}:${t.slice(11,13)}:${t.slice(13,15)}Z`):new Date(e)}buildICalEvent(e,t){let s=m((n,o)=>o?n.toISOString().slice(0,10).replace(/-/g,""):n.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}/,""),"formatDate"),r=`BEGIN:VCALENDAR\r
574
+ `)}`:"Profile is empty."}}setField(e,t,s){return!s||typeof s!="string"?{success:!1,error:`Missing required "value" for ${t}`}:(this.userRepo.updateProfile(e,{[t]:s}),{success:!0,data:{[t]:s},display:`${t} set to "${s}"`})}setPreference(e,t,s){if(!t||typeof t!="string")return{success:!1,error:'Missing required "preference_key"'};let n=this.userRepo.getProfile(e)?.preferences??{};return n[t]=s,this.userRepo.updateProfile(e,{preferences:n}),{success:!0,data:{key:t,value:s},display:`Preference "${t}" set to "${s}"`}}}});var Ue,ur=_(()=>{"use strict";Ue=class{static{m(this,"CalendarProvider")}timezone=Intl.DateTimeFormat().resolvedOptions().timeZone}});var zc={};le(zc,{CalDAVProvider:()=>hn});var hn,si=_(()=>{"use strict";ur();hn=class extends Ue{static{m(this,"CalDAVProvider")}config;client;constructor(e){super(),this.config=e}async initialize(){try{let e=await import("tsdav"),{createDAVClient:t}=e;this.client=await t({serverUrl:this.config.serverUrl,credentials:{username:this.config.username,password:this.config.password},authMethod:"Basic",defaultAccountType:"caldav"})}catch(e){throw new Error(`CalDAV initialization failed: ${e instanceof Error?e.message:String(e)}`)}}async listEvents(e,t){let s=await this.client.fetchCalendars();if(!s||s.length===0)return[];let r=[];for(let n of s){let o=await this.client.fetchCalendarObjects({calendar:n,timeRange:{start:e.toISOString(),end:t.toISOString()}});for(let i of o){let a=this.parseICalEvent(i.data,i.url);a&&r.push(a)}}return r.sort((n,o)=>n.start.getTime()-o.start.getTime())}async createEvent(e){let t=await this.client.fetchCalendars();if(!t||t.length===0)throw new Error("No calendars found");let s=`alfred-${Date.now()}@alfred`,r=this.buildICalEvent(s,e);return await this.client.createCalendarObject({calendar:t[0],filename:`${s}.ics`,iCalString:r}),{id:s,title:e.title,start:e.start,end:e.end,location:e.location,description:e.description,allDay:e.allDay}}async updateEvent(e,t){let s=await this.client.fetchCalendars();for(let r of s){let n=await this.client.fetchCalendarObjects({calendar:r});for(let o of n)if(o.url?.includes(e)||o.data?.includes(e)){let i=this.parseICalEvent(o.data,o.url);if(!i)continue;let a={title:t.title??i.title,start:t.start??i.start,end:t.end??i.end,location:t.location??i.location,description:t.description??i.description,allDay:t.allDay??i.allDay},c=this.buildICalEvent(e,a);return await this.client.updateCalendarObject({calendarObject:{...o,data:c}}),{id:e,...a}}}throw new Error(`Event ${e} not found`)}async deleteEvent(e){let t=await this.client.fetchCalendars();for(let s of t){let r=await this.client.fetchCalendarObjects({calendar:s});for(let n of r)if(n.url?.includes(e)||n.data?.includes(e)){await this.client.deleteCalendarObject({calendarObject:n});return}}throw new Error(`Event ${e} not found`)}async checkAvailability(e,t){let r=(await this.listEvents(e,t)).filter(n=>!n.allDay&&n.start<t&&n.end>e);return{available:r.length===0,conflicts:r}}parseICalEvent(e,t){try{let s=e.split(`
575
+ `).map(p=>p.trim()),r=m(p=>s.find(f=>f.startsWith(p+":"))?.slice(p.length+1),"get"),n=r("SUMMARY"),o=r("DTSTART")??r("DTSTART;VALUE=DATE"),i=r("DTEND")??r("DTEND;VALUE=DATE"),a=r("LOCATION"),c=r("DESCRIPTION"),d=r("UID")??t;if(!n||!o)return;let u=o.length===8;return{id:d,title:n,start:this.parseICalDate(o),end:i?this.parseICalDate(i):this.parseICalDate(o),location:a||void 0,description:c||void 0,allDay:u}}catch(s){console.error("[caldav] Failed to parse iCal event",s);return}}parseICalDate(e){if(e.length===8)return new Date(`${e.slice(0,4)}-${e.slice(4,6)}-${e.slice(6,8)}`);let t=e.replace(/[^0-9TZ]/g,"");return t.length>=15?new Date(`${t.slice(0,4)}-${t.slice(4,6)}-${t.slice(6,8)}T${t.slice(9,11)}:${t.slice(11,13)}:${t.slice(13,15)}Z`):new Date(e)}buildICalEvent(e,t){let s=m((n,o)=>o?n.toISOString().slice(0,10).replace(/-/g,""):n.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}/,""),"formatDate"),r=`BEGIN:VCALENDAR\r
553
576
  VERSION:2.0\r
554
577
  PRODID:-//Alfred//EN\r
555
578
  BEGIN:VEVENT\r
@@ -564,24 +587,24 @@ BEGIN:VEVENT\r
564
587
  `),r+=`DTSTAMP:${s(new Date)}\r
565
588
  `,r+=`END:VEVENT\r
566
589
  END:VCALENDAR\r
567
- `,r}}});var jc={};ue(jc,{GoogleCalendarProvider:()=>pn});var pn,ti=_(()=>{"use strict";ar();pn=class extends Oe{static{m(this,"GoogleCalendarProvider")}config;calendar;constructor(e){super(),this.config=e}async initialize(){try{let{google:e}=await import("googleapis"),t=new e.auth.OAuth2(this.config.clientId,this.config.clientSecret);t.setCredentials({refresh_token:this.config.refreshToken}),this.calendar=e.calendar({version:"v3",auth:t})}catch(e){throw new Error(`Google Calendar initialization failed: ${e instanceof Error?e.message:String(e)}`)}}async listEvents(e,t){return((await this.calendar.events.list({calendarId:"primary",timeMin:e.toISOString(),timeMax:t.toISOString(),singleEvents:!0,orderBy:"startTime"})).data.items??[]).map(r=>this.mapEvent(r))}async createEvent(e){let t={summary:e.title,location:e.location,description:e.description};e.allDay?(t.start={date:e.start.toISOString().slice(0,10)},t.end={date:e.end.toISOString().slice(0,10)}):(t.start={dateTime:e.start.toISOString()},t.end={dateTime:e.end.toISOString()});let s=await this.calendar.events.insert({calendarId:"primary",requestBody:t});return this.mapEvent(s.data)}async updateEvent(e,t){let s={};t.title&&(s.summary=t.title),t.location&&(s.location=t.location),t.description&&(s.description=t.description),t.start&&(s.start=t.allDay?{date:t.start.toISOString().slice(0,10)}:{dateTime:t.start.toISOString()}),t.end&&(s.end=t.allDay?{date:t.end.toISOString().slice(0,10)}:{dateTime:t.end.toISOString()});let r=await this.calendar.events.patch({calendarId:"primary",eventId:e,requestBody:s});return this.mapEvent(r.data)}async deleteEvent(e){await this.calendar.events.delete({calendarId:"primary",eventId:e})}async checkAvailability(e,t){let r=(await this.listEvents(e,t)).filter(n=>!n.allDay&&n.start<t&&n.end>e);return{available:r.length===0,conflicts:r}}mapEvent(e){let t=!!e.start?.date;return{id:e.id,title:e.summary??"(No title)",start:new Date(e.start?.dateTime??e.start?.date),end:new Date(e.end?.dateTime??e.end?.date),location:e.location??void 0,description:e.description??void 0,allDay:t}}}});var Bc={};ue(Bc,{MicrosoftCalendarProvider:()=>hn});var hn,si=_(()=>{"use strict";ar();hn=class extends Oe{static{m(this,"MicrosoftCalendarProvider")}config;client;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Calendars.ReadWrite offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok)throw new Error(`Microsoft token refresh failed: ${s.status}`);let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(s.status===401){await this.refreshAccessToken();let r=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(!r.ok)throw new Error(`Graph API error: ${r.status}`);return r.json()}if(!s.ok)throw new Error(`Graph API error: ${s.status}`);if(s.status!==204)return s.json()}async listEvents(e,t){let s=new URLSearchParams({startDateTime:e.toISOString(),endDateTime:t.toISOString(),$orderby:"start/dateTime",$top:"50"});return((await this.graphRequest(`/me/calendarView?${s}`,{headers:{Prefer:'outlook.timezone="UTC"'}})).value??[]).map(n=>this.mapEvent(n))}async createEvent(e){let t=this.timezone,s={subject:e.title,body:e.description?{contentType:"text",content:e.description}:void 0,location:e.location?{displayName:e.location}:void 0,isAllDay:e.allDay??!1};e.allDay?(s.start={dateTime:this.formatDateInTz(e.start,t).slice(0,10)+"T00:00:00",timeZone:t},s.end={dateTime:this.formatDateInTz(e.end,t).slice(0,10)+"T00:00:00",timeZone:t}):(s.start={dateTime:this.formatDateInTz(e.start,t),timeZone:t},s.end={dateTime:this.formatDateInTz(e.end,t),timeZone:t});let r=await this.graphRequest("/me/events",{method:"POST",body:JSON.stringify(s)});return this.mapEvent(r)}async updateEvent(e,t){let s={};t.title&&(s.subject=t.title),t.description&&(s.body={contentType:"text",content:t.description}),t.location&&(s.location={displayName:t.location}),t.start&&(s.start={dateTime:this.formatDateInTz(t.start,this.timezone),timeZone:this.timezone}),t.end&&(s.end={dateTime:this.formatDateInTz(t.end,this.timezone),timeZone:this.timezone});let r=await this.graphRequest(`/me/events/${e}`,{method:"PATCH",body:JSON.stringify(s)});return this.mapEvent(r)}async deleteEvent(e){await this.graphRequest(`/me/events/${e}`,{method:"DELETE"})}async checkAvailability(e,t){let r=(await this.listEvents(e,t)).filter(n=>!n.allDay&&n.start<t&&n.end>e);return{available:r.length===0,conflicts:r}}formatDateInTz(e,t){let s=new Intl.DateTimeFormat("en-CA",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}).formatToParts(e),r=m(n=>s.find(o=>o.type===n)?.value??"00","g");return`${r("year")}-${r("month")}-${r("day")}T${r("hour")}:${r("minute")}:${r("second")}`}mapEvent(e){return{id:e.id,title:e.subject??"(No title)",start:this.parseGraphDateTime(e.start),end:this.parseGraphDateTime(e.end),location:e.location?.displayName??void 0,description:e.body?.content??void 0,allDay:e.isAllDay??!1}}parseGraphDateTime(e){if(!e?.dateTime)return new Date;let t=e.dateTime.replace(/(\.\d{3})\d*$/,"$1");return new Date(t+"Z")}}});async function cr(l){switch(l.provider){case"caldav":{if(!l.caldav)throw new Error("CalDAV config missing");let{CalDAVProvider:e}=await Promise.resolve().then(()=>(ei(),Fc)),t=new e(l.caldav);return await t.initialize(),t}case"google":{if(!l.google)throw new Error("Google Calendar config missing");let{GoogleCalendarProvider:e}=await Promise.resolve().then(()=>(ti(),jc)),t=new e(l.google);return await t.initialize(),t}case"microsoft":{if(!l.microsoft)throw new Error("Microsoft Calendar config missing");let{MicrosoftCalendarProvider:e}=await Promise.resolve().then(()=>(si(),Bc)),t=new e(l.microsoft);return await t.initialize(),t}default:throw new Error(`Unknown calendar provider: ${l.provider}`)}}var Hc=_(()=>{"use strict";m(cr,"createCalendarProvider")});var $t,qc=_(()=>{"use strict";U();$t=class extends R{static{m(this,"CalendarSkill")}calendarProvider;timezone;metadata={name:"calendar",category:"productivity",description:"Manage calendar events. List upcoming events, create new events, update or delete existing ones, and check availability.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_events","create_event","update_event","delete_event","check_availability"],description:"The calendar action to perform"},start:{type:"string",description:"Start date/time in ISO 8601 format"},end:{type:"string",description:"End date/time in ISO 8601 format"},title:{type:"string",description:"Event title (for create/update)"},location:{type:"string",description:"Event location (for create/update)"},description:{type:"string",description:"Event description (for create/update)"},event_id:{type:"string",description:"Event ID (for update/delete)"},all_day:{type:"boolean",description:"Whether this is an all-day event"}},required:["action"]}};constructor(e,t){super(),this.calendarProvider=e,this.timezone=t}async execute(e,t){t.timezone&&(this.calendarProvider.timezone=t.timezone);let s=e.action;switch(s){case"list_events":return this.listEvents(e);case"create_event":return this.createEvent(e);case"update_event":return this.updateEvent(e);case"delete_event":return this.deleteEvent(e);case"check_availability":return this.checkAvailability(e);default:return{success:!1,error:`Unknown action: "${String(s)}"`}}}async getTodayEvents(){let e=new Date,t=new Date(e.getFullYear(),e.getMonth(),e.getDate()),s=new Date(e.getFullYear(),e.getMonth(),e.getDate()+1);try{return await this.calendarProvider.listEvents(t,s)}catch(r){return console.error("[calendar] Failed to fetch today events",r),[]}}async listEvents(e){let t=e.start?new Date(e.start):new Date,s=e.end?new Date(e.end):new Date(t.getTime()+7*24*60*60*1e3);try{let r=await this.calendarProvider.listEvents(t,s);if(r.length===0)return{success:!0,data:[],display:"No events found in this time range."};let n=r.map(o=>this.formatEvent(o)).join(`
590
+ `,r}}});var qc={};le(qc,{GoogleCalendarProvider:()=>fn});var fn,ri=_(()=>{"use strict";ur();fn=class extends Ue{static{m(this,"GoogleCalendarProvider")}config;calendar;constructor(e){super(),this.config=e}async initialize(){try{let{google:e}=await import("googleapis"),t=new e.auth.OAuth2(this.config.clientId,this.config.clientSecret);t.setCredentials({refresh_token:this.config.refreshToken}),this.calendar=e.calendar({version:"v3",auth:t})}catch(e){throw new Error(`Google Calendar initialization failed: ${e instanceof Error?e.message:String(e)}`)}}async listEvents(e,t){return((await this.calendar.events.list({calendarId:"primary",timeMin:e.toISOString(),timeMax:t.toISOString(),singleEvents:!0,orderBy:"startTime"})).data.items??[]).map(r=>this.mapEvent(r))}async createEvent(e){let t={summary:e.title,location:e.location,description:e.description};e.allDay?(t.start={date:e.start.toISOString().slice(0,10)},t.end={date:e.end.toISOString().slice(0,10)}):(t.start={dateTime:e.start.toISOString()},t.end={dateTime:e.end.toISOString()});let s=await this.calendar.events.insert({calendarId:"primary",requestBody:t});return this.mapEvent(s.data)}async updateEvent(e,t){let s={};t.title&&(s.summary=t.title),t.location&&(s.location=t.location),t.description&&(s.description=t.description),t.start&&(s.start=t.allDay?{date:t.start.toISOString().slice(0,10)}:{dateTime:t.start.toISOString()}),t.end&&(s.end=t.allDay?{date:t.end.toISOString().slice(0,10)}:{dateTime:t.end.toISOString()});let r=await this.calendar.events.patch({calendarId:"primary",eventId:e,requestBody:s});return this.mapEvent(r.data)}async deleteEvent(e){await this.calendar.events.delete({calendarId:"primary",eventId:e})}async checkAvailability(e,t){let r=(await this.listEvents(e,t)).filter(n=>!n.allDay&&n.start<t&&n.end>e);return{available:r.length===0,conflicts:r}}mapEvent(e){let t=!!e.start?.date;return{id:e.id,title:e.summary??"(No title)",start:new Date(e.start?.dateTime??e.start?.date),end:new Date(e.end?.dateTime??e.end?.date),location:e.location??void 0,description:e.description??void 0,allDay:t}}}});var Wc={};le(Wc,{MicrosoftCalendarProvider:()=>gn});var gn,ni=_(()=>{"use strict";ur();gn=class extends Ue{static{m(this,"MicrosoftCalendarProvider")}config;client;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Calendars.ReadWrite offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok)throw new Error(`Microsoft token refresh failed: ${s.status}`);let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(s.status===401){await this.refreshAccessToken();let r=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(!r.ok)throw new Error(`Graph API error: ${r.status}`);return r.json()}if(!s.ok)throw new Error(`Graph API error: ${s.status}`);if(s.status!==204)return s.json()}async listEvents(e,t){let s=new URLSearchParams({startDateTime:e.toISOString(),endDateTime:t.toISOString(),$orderby:"start/dateTime",$top:"50"});return((await this.graphRequest(`/me/calendarView?${s}`,{headers:{Prefer:'outlook.timezone="UTC"'}})).value??[]).map(n=>this.mapEvent(n))}async createEvent(e){let t=this.timezone,s={subject:e.title,body:e.description?{contentType:"text",content:e.description}:void 0,location:e.location?{displayName:e.location}:void 0,isAllDay:e.allDay??!1};e.allDay?(s.start={dateTime:this.formatDateInTz(e.start,t).slice(0,10)+"T00:00:00",timeZone:t},s.end={dateTime:this.formatDateInTz(e.end,t).slice(0,10)+"T00:00:00",timeZone:t}):(s.start={dateTime:this.formatDateInTz(e.start,t),timeZone:t},s.end={dateTime:this.formatDateInTz(e.end,t),timeZone:t});let r=await this.graphRequest("/me/events",{method:"POST",body:JSON.stringify(s)});return this.mapEvent(r)}async updateEvent(e,t){let s={};t.title&&(s.subject=t.title),t.description&&(s.body={contentType:"text",content:t.description}),t.location&&(s.location={displayName:t.location}),t.start&&(s.start={dateTime:this.formatDateInTz(t.start,this.timezone),timeZone:this.timezone}),t.end&&(s.end={dateTime:this.formatDateInTz(t.end,this.timezone),timeZone:this.timezone});let r=await this.graphRequest(`/me/events/${e}`,{method:"PATCH",body:JSON.stringify(s)});return this.mapEvent(r)}async deleteEvent(e){await this.graphRequest(`/me/events/${e}`,{method:"DELETE"})}async checkAvailability(e,t){let r=(await this.listEvents(e,t)).filter(n=>!n.allDay&&n.start<t&&n.end>e);return{available:r.length===0,conflicts:r}}formatDateInTz(e,t){let s=new Intl.DateTimeFormat("en-CA",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}).formatToParts(e),r=m(n=>s.find(o=>o.type===n)?.value??"00","g");return`${r("year")}-${r("month")}-${r("day")}T${r("hour")}:${r("minute")}:${r("second")}`}mapEvent(e){return{id:e.id,title:e.subject??"(No title)",start:this.parseGraphDateTime(e.start),end:this.parseGraphDateTime(e.end),location:e.location?.displayName??void 0,description:e.body?.content??void 0,allDay:e.isAllDay??!1}}parseGraphDateTime(e){if(!e?.dateTime)return new Date;let t=e.dateTime.replace(/(\.\d{3})\d*$/,"$1");return new Date(t+"Z")}}});async function mr(l){switch(l.provider){case"caldav":{if(!l.caldav)throw new Error("CalDAV config missing");let{CalDAVProvider:e}=await Promise.resolve().then(()=>(si(),zc)),t=new e(l.caldav);return await t.initialize(),t}case"google":{if(!l.google)throw new Error("Google Calendar config missing");let{GoogleCalendarProvider:e}=await Promise.resolve().then(()=>(ri(),qc)),t=new e(l.google);return await t.initialize(),t}case"microsoft":{if(!l.microsoft)throw new Error("Microsoft Calendar config missing");let{MicrosoftCalendarProvider:e}=await Promise.resolve().then(()=>(ni(),Wc)),t=new e(l.microsoft);return await t.initialize(),t}default:throw new Error(`Unknown calendar provider: ${l.provider}`)}}var Gc=_(()=>{"use strict";m(mr,"createCalendarProvider")});var kt,Vc=_(()=>{"use strict";U();kt=class extends x{static{m(this,"CalendarSkill")}calendarProvider;timezone;metadata={name:"calendar",category:"productivity",description:"Manage calendar events. List upcoming events, create new events, update or delete existing ones, and check availability.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_events","create_event","update_event","delete_event","check_availability"],description:"The calendar action to perform"},start:{type:"string",description:"Start date/time in ISO 8601 format"},end:{type:"string",description:"End date/time in ISO 8601 format"},title:{type:"string",description:"Event title (for create/update)"},location:{type:"string",description:"Event location (for create/update)"},description:{type:"string",description:"Event description (for create/update)"},event_id:{type:"string",description:"Event ID (for update/delete)"},all_day:{type:"boolean",description:"Whether this is an all-day event"}},required:["action"]}};constructor(e,t){super(),this.calendarProvider=e,this.timezone=t}async execute(e,t){t.timezone&&(this.calendarProvider.timezone=t.timezone);let s=e.action;switch(s){case"list_events":return this.listEvents(e);case"create_event":return this.createEvent(e);case"update_event":return this.updateEvent(e);case"delete_event":return this.deleteEvent(e);case"check_availability":return this.checkAvailability(e);default:return{success:!1,error:`Unknown action: "${String(s)}"`}}}async getTodayEvents(){let e=new Date,t=new Date(e.getFullYear(),e.getMonth(),e.getDate()),s=new Date(e.getFullYear(),e.getMonth(),e.getDate()+1);try{return await this.calendarProvider.listEvents(t,s)}catch(r){return console.error("[calendar] Failed to fetch today events",r),[]}}async listEvents(e){let t=e.start?new Date(e.start):new Date,s=e.end?new Date(e.end):new Date(t.getTime()+7*24*60*60*1e3);try{let r=await this.calendarProvider.listEvents(t,s);if(r.length===0)return{success:!0,data:[],display:"No events found in this time range."};let n=r.map(o=>this.formatEvent(o)).join(`
568
591
  `);return{success:!0,data:r,display:`${r.length} event(s):
569
592
  ${n}`}}catch(r){return{success:!1,error:`Failed to list events: ${r instanceof Error?r.message:String(r)}`}}}async createEvent(e){let t=e.title,s=e.start,r=e.end;if(!t)return{success:!1,error:'Missing required field "title"'};if(!s)return{success:!1,error:'Missing required field "start"'};if(!r)return{success:!1,error:'Missing required field "end"'};try{let n=await this.calendarProvider.createEvent({title:t,start:new Date(s),end:new Date(r),location:e.location,description:e.description,allDay:e.all_day});return{success:!0,data:n,display:`Event created: ${this.formatEvent(n)}`}}catch(n){return{success:!1,error:`Failed to create event: ${n instanceof Error?n.message:String(n)}`}}}async updateEvent(e){let t=e.event_id;if(!t)return{success:!1,error:'Missing required field "event_id"'};try{let s=await this.calendarProvider.updateEvent(t,{title:e.title,start:e.start?new Date(e.start):void 0,end:e.end?new Date(e.end):void 0,location:e.location,description:e.description,allDay:e.all_day});return{success:!0,data:s,display:`Event updated: ${this.formatEvent(s)}`}}catch(s){return{success:!1,error:`Failed to update event: ${s instanceof Error?s.message:String(s)}`}}}async deleteEvent(e){let t=e.event_id;if(!t)return{success:!1,error:'Missing required field "event_id"'};try{return await this.calendarProvider.deleteEvent(t),{success:!0,data:{deleted:t},display:`Event "${t}" deleted.`}}catch(s){return{success:!1,error:`Failed to delete event: ${s instanceof Error?s.message:String(s)}`}}}async checkAvailability(e){let t=e.start,s=e.end;if(!t||!s)return{success:!1,error:'Missing required fields "start" and "end"'};try{let r=await this.calendarProvider.checkAvailability(new Date(t),new Date(s)),n=r.available?"Time slot is available.":`Time slot has ${r.conflicts.length} conflict(s):
570
593
  ${r.conflicts.map(o=>this.formatEvent(o)).join(`
571
- `)}`;return{success:!0,data:r,display:n}}catch(r){return{success:!1,error:`Failed to check availability: ${r instanceof Error?r.message:String(r)}`}}}formatEvent(e){let t={hour:"2-digit",minute:"2-digit",...this.timezone?{timeZone:this.timezone}:{}},s=e.location?` @ ${e.location}`:"",r=e.id?` [id:${e.id}]`:"";if(e.allDay)return`- All day: ${e.title}${s}${r}`;let n=e.start.toLocaleTimeString("en-GB",t),o=e.end.toLocaleTimeString("en-GB",t);return`- ${n}-${o}: ${e.title}${s}${r}`}}});var zc=_(()=>{"use strict";ar();ei();ti();si();Hc();qc()});var ts,Wc=_(()=>{"use strict";U();ts=class extends R{static{m(this,"CrossPlatformSkill")}users;linkTokens;adapters;findConversation;metadata={name:"cross_platform",category:"identity",description:"Manage cross-platform identity linking and messaging. Actions: link_start (generate a linking code on current platform), link_confirm (enter a code from another platform to link accounts), send_message (send a message to a linked platform), list_identities (show all linked platforms), unlink (remove a platform link).",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["link_start","link_confirm","send_message","list_identities","unlink"],description:"The action to perform"},code:{type:"string",description:"The 6-digit linking code (for link_confirm)"},platform:{type:"string",description:"Target platform (for send_message or unlink)"},chat_id:{type:"string",description:"Target chat ID (for send_message)"},message:{type:"string",description:"Message text to send (for send_message)"}},required:["action"]}};constructor(e,t,s,r){super(),this.users=e,this.linkTokens=t,this.adapters=s,this.findConversation=r}resolveInternalId(e){return this.users.findOrCreate(e.platform,e.userId).id}async execute(e,t){let s=e.action;switch(s){case"link_start":return this.linkStart(t);case"link_confirm":return this.linkConfirm(e,t);case"send_message":return this.sendMessage(e,t);case"list_identities":return this.listIdentities(t);case"unlink":return this.unlink(e,t);default:return{success:!1,error:`Unknown action: ${s}`}}}failedConfirmAttempts=new Map;checkConfirmRateLimit(e){let t=Date.now(),s=this.failedConfirmAttempts.get(e);return s&&t<s.resetAt&&s.count>=5?`Too many failed attempts. Please wait ${Math.ceil((s.resetAt-t)/1e3)}s before trying again.`:null}recordFailedConfirm(e){let t=Date.now(),s=this.failedConfirmAttempts.get(e);s&&t<s.resetAt?s.count++:this.failedConfirmAttempts.set(e,{count:1,resetAt:t+5*6e4})}async linkStart(e){this.linkTokens.cleanup();let t=this.resolveInternalId(e);if(this.linkTokens.countRecentByUser(t,10)>=5)return{success:!1,error:"Too many linking codes generated recently. Please wait a few minutes."};let r=this.linkTokens.create(t,e.platform);return{success:!0,data:{code:r.code,expiresAt:r.expiresAt},display:`Your linking code is: **${r.code}**
594
+ `)}`;return{success:!0,data:r,display:n}}catch(r){return{success:!1,error:`Failed to check availability: ${r instanceof Error?r.message:String(r)}`}}}formatEvent(e){let t={hour:"2-digit",minute:"2-digit",...this.timezone?{timeZone:this.timezone}:{}},s=e.location?` @ ${e.location}`:"",r=e.id?` [id:${e.id}]`:"";if(e.allDay)return`- All day: ${e.title}${s}${r}`;let n=e.start.toLocaleTimeString("en-GB",t),o=e.end.toLocaleTimeString("en-GB",t);return`- ${n}-${o}: ${e.title}${s}${r}`}}});var Kc=_(()=>{"use strict";ur();si();ri();ni();Gc();Vc()});var rs,Xc=_(()=>{"use strict";U();rs=class extends x{static{m(this,"CrossPlatformSkill")}users;linkTokens;adapters;findConversation;metadata={name:"cross_platform",category:"identity",description:"Manage cross-platform identity linking and messaging. Actions: link_start (generate a linking code on current platform), link_confirm (enter a code from another platform to link accounts), send_message (send a message to a linked platform), list_identities (show all linked platforms), unlink (remove a platform link).",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["link_start","link_confirm","send_message","list_identities","unlink"],description:"The action to perform"},code:{type:"string",description:"The 6-digit linking code (for link_confirm)"},platform:{type:"string",description:"Target platform (for send_message or unlink)"},chat_id:{type:"string",description:"Target chat ID (for send_message)"},message:{type:"string",description:"Message text to send (for send_message)"}},required:["action"]}};constructor(e,t,s,r){super(),this.users=e,this.linkTokens=t,this.adapters=s,this.findConversation=r}resolveInternalId(e){return this.users.findOrCreate(e.platform,e.userId).id}async execute(e,t){let s=e.action;switch(s){case"link_start":return this.linkStart(t);case"link_confirm":return this.linkConfirm(e,t);case"send_message":return this.sendMessage(e,t);case"list_identities":return this.listIdentities(t);case"unlink":return this.unlink(e,t);default:return{success:!1,error:`Unknown action: ${s}`}}}failedConfirmAttempts=new Map;checkConfirmRateLimit(e){let t=Date.now(),s=this.failedConfirmAttempts.get(e);return s&&t<s.resetAt&&s.count>=5?`Too many failed attempts. Please wait ${Math.ceil((s.resetAt-t)/1e3)}s before trying again.`:null}recordFailedConfirm(e){let t=Date.now(),s=this.failedConfirmAttempts.get(e);s&&t<s.resetAt?s.count++:this.failedConfirmAttempts.set(e,{count:1,resetAt:t+5*6e4})}async linkStart(e){this.linkTokens.cleanup();let t=this.resolveInternalId(e);if(this.linkTokens.countRecentByUser(t,10)>=5)return{success:!1,error:"Too many linking codes generated recently. Please wait a few minutes."};let r=this.linkTokens.create(t,e.platform);return{success:!0,data:{code:r.code,expiresAt:r.expiresAt},display:`Your linking code is: **${r.code}**
572
595
 
573
596
  Enter this code on your other platform within 10 minutes using:
574
- "Link my account with code ${r.code}"`}}async linkConfirm(e,t){let s=e.code;if(!s)return{success:!1,error:'Missing required field "code"'};let r=this.resolveInternalId(t),n=this.checkConfirmRateLimit(r);if(n)return{success:!1,error:n};let o=this.linkTokens.findByCode(s.trim());if(!o)return this.recordFailedConfirm(r),{success:!1,error:"Invalid or expired linking code. Please generate a new one."};let i=o.userId;if(i===r)return{success:!1,error:"Cannot link an account to itself. Use the code on a different platform."};let a=this.users.getMasterUserId(i),c=this.users.getMasterUserId(r),d;if(a!==i?d=a:c!==r?d=c:d=i,a!==i&&c!==r&&a!==c){let g=this.users.getLinkedUsers(c);for(let f of g)this.users.setMasterUser(f.id,d)}i!==d&&this.users.setMasterUser(i,d),r!==d&&this.users.setMasterUser(r,d),this.linkTokens.consume(o.id);let u=this.users.findById(i),p=o.platform;return{success:!0,data:{masterUserId:d,linkedPlatform:p},display:`Account linked successfully! Your ${p} account (${u?.displayName??u?.username??"unknown"}) is now linked to this ${t.platform} account.
597
+ "Link my account with code ${r.code}"`}}async linkConfirm(e,t){let s=e.code;if(!s)return{success:!1,error:'Missing required field "code"'};let r=this.resolveInternalId(t),n=this.checkConfirmRateLimit(r);if(n)return{success:!1,error:n};let o=this.linkTokens.findByCode(s.trim());if(!o)return this.recordFailedConfirm(r),{success:!1,error:"Invalid or expired linking code. Please generate a new one."};let i=o.userId;if(i===r)return{success:!1,error:"Cannot link an account to itself. Use the code on a different platform."};let a=this.users.getMasterUserId(i),c=this.users.getMasterUserId(r),d;if(a!==i?d=a:c!==r?d=c:d=i,a!==i&&c!==r&&a!==c){let f=this.users.getLinkedUsers(c);for(let g of f)this.users.setMasterUser(g.id,d)}i!==d&&this.users.setMasterUser(i,d),r!==d&&this.users.setMasterUser(r,d),this.linkTokens.consume(o.id);let u=this.users.findById(i),p=o.platform;return{success:!0,data:{masterUserId:d,linkedPlatform:p},display:`Account linked successfully! Your ${p} account (${u?.displayName??u?.username??"unknown"}) is now linked to this ${t.platform} account.
575
598
 
576
599
  Your memories, preferences, and context are now shared across platforms.`}}async sendMessage(e,t){let s=e.platform,r=e.chat_id,n=e.message;if(!s)return{success:!1,error:'Missing required field "platform"'};if(!n)return{success:!1,error:'Missing required field "message"'};let o=this.adapters.get(s);if(!o)return{success:!1,error:`Platform "${s}" is not connected. Available: ${[...this.adapters.keys()].join(", ")}`};if(!r||!/^[!0-9]/.test(r)){let i=this.resolveInternalId(t),a=this.users.getMasterUserId(i),d=this.users.getLinkedUsers(a).find(u=>u.platform===s);if(d&&this.findConversation){let u=this.findConversation(s,d.id);u&&(r=u.chatId)}!r&&d&&(r=d.platformUserId)}if(!r)return{success:!1,error:"Could not resolve chat_id for target platform. No linked account or conversation found."};try{return{success:!0,data:{messageId:await o.sendMessage(r,n),platform:s,chatId:r},display:`Message sent to ${s}.`}}catch(i){return{success:!1,error:`Failed to send message: ${i instanceof Error?i.message:String(i)}`}}}async listIdentities(e){let t=this.resolveInternalId(e),s=this.users.getMasterUserId(t),r=this.users.getLinkedUsers(s);if(r.length<=1)return{success:!0,data:{identities:r},display:`No linked accounts found. To link another platform, use:
577
600
  "Start linking my account" on the platform you want to link from, then enter the code on the other platform.`};let n=r.map(o=>{let i=o.id===t?" (current)":"",a=o.displayName??o.username??o.platformUserId;return`- **${o.platform}**: ${a}${i}`});return{success:!0,data:{identities:r.map(o=>({platform:o.platform,username:o.username,displayName:o.displayName}))},display:`Linked accounts:
578
601
  ${n.join(`
579
- `)}`}}async unlink(e,t){let s=e.platform;if(!s)return{success:!1,error:'Missing required field "platform"'};let r=this.resolveInternalId(t),n=this.users.getMasterUserId(r),i=this.users.getLinkedUsers(n).find(a=>a.platform===s&&a.id!==r);return i?(this.users.setMasterUser(i.id,i.id),{success:!0,data:{unlinkedPlatform:s,unlinkedUserId:i.id},display:`Unlinked ${s} account (${i.displayName??i.username??i.platformUserId}).`}):{success:!1,error:`No linked account found on platform "${s}".`}}}});var ss,Gc=_(()=>{"use strict";U();Me();ss=class extends R{static{m(this,"BackgroundTaskSkill")}taskRepo;metadata={name:"background_task",category:"automation",description:'Schedule, list, or cancel background tasks that run a SINGLE skill call asynchronously. Use "schedule" to queue ONE skill execution in the background (user will be notified when done). NOT for multi-step tasks \u2014 use "delegate" instead when a task needs multiple tool calls (e.g. search + read + process + generate). Use "list" to see active/recent tasks. Use "cancel" to stop a pending or running task.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["schedule","list","cancel"],description:"The background task action to perform"},description:{type:"string",description:"Human-readable description of what the task does (for schedule)"},skill_name:{type:"string",description:"The skill to run in the background (for schedule)"},skill_input:{type:"object",description:"Input to pass to the skill (for schedule)"},task_id:{type:"string",description:"Task ID (for cancel)"}},required:["action"]}};constructor(e){super(),this.taskRepo=e}getAllTasks(e){let t=new Set,s=[];for(let r of W(e))for(let n of this.taskRepo.getByUser(r))t.has(n.id)||(t.add(n.id),s.push(n));return s}async execute(e,t){let s=e.action;switch(s){case"schedule":return this.scheduleTask(e,t);case"list":return this.listTasks(t);case"cancel":return this.cancelTask(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: schedule, list, cancel`}}}scheduleTask(e,t){let s=e.description,r=e.skill_name,n=e.skill_input;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "description" for schedule action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "skill_name" for schedule action'};let o=this.taskRepo.create(ne(t),t.platform,t.chatId,s,r,JSON.stringify(n??{}));return{success:!0,data:{taskId:o.id,description:s,skillName:r,status:o.status},display:`Background task scheduled (${o.id}): "${s}" using skill "${r}". You'll be notified when it completes.`}}listTasks(e){let t=this.getAllTasks(e);if(t.length===0)return{success:!0,data:[],display:"No active or recent background tasks."};let s={pending:"\u23F3",running:"\u25B6\uFE0F",completed:"\u2705",failed:"\u274C"},r=t.map(n=>`- ${s[n.status]??"?"} ${n.id}: "${n.description}" [${n.status}] (${n.skillName})`);return{success:!0,data:t.map(n=>({taskId:n.id,description:n.description,status:n.status,skillName:n.skillName,createdAt:n.createdAt,completedAt:n.completedAt})),display:`Background tasks:
602
+ `)}`}}async unlink(e,t){let s=e.platform;if(!s)return{success:!1,error:'Missing required field "platform"'};let r=this.resolveInternalId(t),n=this.users.getMasterUserId(r),i=this.users.getLinkedUsers(n).find(a=>a.platform===s&&a.id!==r);return i?(this.users.setMasterUser(i.id,i.id),{success:!0,data:{unlinkedPlatform:s,unlinkedUserId:i.id},display:`Unlinked ${s} account (${i.displayName??i.username??i.platformUserId}).`}):{success:!1,error:`No linked account found on platform "${s}".`}}}});var ns,Yc=_(()=>{"use strict";U();Pe();ns=class extends x{static{m(this,"BackgroundTaskSkill")}taskRepo;metadata={name:"background_task",category:"automation",description:'Schedule, list, or cancel background tasks that run a SINGLE skill call asynchronously. Use "schedule" to queue ONE skill execution in the background (user will be notified when done). NOT for multi-step tasks \u2014 use "delegate" instead when a task needs multiple tool calls (e.g. search + read + process + generate). Use "list" to see active/recent tasks. Use "cancel" to stop a pending or running task.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["schedule","list","cancel"],description:"The background task action to perform"},description:{type:"string",description:"Human-readable description of what the task does (for schedule)"},skill_name:{type:"string",description:"The skill to run in the background (for schedule)"},skill_input:{type:"object",description:"Input to pass to the skill (for schedule)"},task_id:{type:"string",description:"Task ID (for cancel)"}},required:["action"]}};constructor(e){super(),this.taskRepo=e}getAllTasks(e){let t=new Set,s=[];for(let r of G(e))for(let n of this.taskRepo.getByUser(r))t.has(n.id)||(t.add(n.id),s.push(n));return s}async execute(e,t){let s=e.action;switch(s){case"schedule":return this.scheduleTask(e,t);case"list":return this.listTasks(t);case"cancel":return this.cancelTask(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: schedule, list, cancel`}}}scheduleTask(e,t){let s=e.description,r=e.skill_name,n=e.skill_input;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "description" for schedule action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "skill_name" for schedule action'};let o=this.taskRepo.create(ne(t),t.platform,t.chatId,s,r,JSON.stringify(n??{}));return{success:!0,data:{taskId:o.id,description:s,skillName:r,status:o.status},display:`Background task scheduled (${o.id}): "${s}" using skill "${r}". You'll be notified when it completes.`}}listTasks(e){let t=this.getAllTasks(e);if(t.length===0)return{success:!0,data:[],display:"No active or recent background tasks."};let s={pending:"\u23F3",running:"\u25B6\uFE0F",completed:"\u2705",failed:"\u274C"},r=t.map(n=>`- ${s[n.status]??"?"} ${n.id}: "${n.description}" [${n.status}] (${n.skillName})`);return{success:!0,data:t.map(n=>({taskId:n.id,description:n.description,status:n.status,skillName:n.skillName,createdAt:n.createdAt,completedAt:n.completedAt})),display:`Background tasks:
580
603
  ${r.join(`
581
- `)}`}}cancelTask(e,t){let s=e.task_id;return!s||typeof s!="string"?{success:!1,error:'Missing required field "task_id" for cancel action'}:this.getAllTasks(t).some(i=>i.id===s)?this.taskRepo.cancel(s)?{success:!0,data:{taskId:s},display:`Background task "${s}" cancelled.`}:{success:!1,error:`Task "${s}" not found or already completed`}:{success:!1,error:`Task "${s}" not found or already completed`}}}});var rs,Vc=_(()=>{"use strict";U();Me();rs=class extends R{static{m(this,"ScheduledTaskSkill")}actionRepo;metadata={name:"scheduled_task",category:"automation",description:'Create, list, enable, disable, or delete scheduled actions that run automatically on a recurring basis. Supports cron expressions (e.g. "0 9 * * *" for daily at 9 AM), intervals (in minutes), and one-time schedules. Each scheduled action executes a skill or sends a prompt to the LLM at the configured time. Use this for time-based tasks (reports, periodic checks, reminders). For condition-based alerts ("notify me WHEN X happens"), use the watch tool instead.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","list","enable","disable","delete"],description:"The scheduled task action to perform"},name:{type:"string",description:"Name for the scheduled action (for create)"},description:{type:"string",description:"What the scheduled action does (for create)"},schedule_type:{type:"string",enum:["cron","interval","once"],description:"Type of schedule: cron expression, interval in minutes, or one-time ISO date (for create)"},schedule_value:{type:"string",description:"Schedule value: cron expression, minutes as string, or ISO date (for create)"},skill_name:{type:"string",description:"The skill to execute on schedule (for create)"},skill_input:{type:"object",description:"Input to pass to the skill (for create)"},prompt_template:{type:"string",description:"Optional LLM prompt to run instead of a skill (for create)"},action_id:{type:"string",description:"Scheduled action ID (for enable, disable, delete)"}},required:["action"]}};constructor(e){super(),this.actionRepo=e}getAllActions(e){let t=new Set,s=[];for(let r of W(e))for(let n of this.actionRepo.getByUser(r))t.has(n.id)||(t.add(n.id),s.push(n));return s}async execute(e,t){let s=e.action;switch(s){case"create":return this.createAction(e,t);case"list":return this.listActions(t);case"enable":return this.toggleAction(e,!0,t);case"disable":return this.toggleAction(e,!1,t);case"delete":return this.deleteAction(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: create, list, enable, disable, delete`}}}createAction(e,t){let s=e.name,r=e.description,n=e.schedule_type,o=e.schedule_value,i=e.skill_name,a=e.skill_input,c=e.prompt_template;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "name" for create action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "description" for create action'};if(!n||!["cron","interval","once"].includes(n))return{success:!1,error:'Missing or invalid "schedule_type". Must be "cron", "interval", or "once"'};if(!o||typeof o!="string")return{success:!1,error:'Missing required field "schedule_value" for create action'};if((!i||typeof i!="string")&&!c)return{success:!1,error:'Missing required field "skill_name" (or "prompt_template") for create action'};if(n==="interval"){let p=parseInt(o,10);if(isNaN(p)||p<=0)return{success:!1,error:"For interval schedule, value must be a positive number of minutes"}}if(n==="cron"&&o.trim().split(/\s+/).length!==5)return{success:!1,error:"Cron expression must have 5 fields: minute hour dayOfMonth month dayOfWeek"};if(n==="once"){let p=new Date(o);if(isNaN(p.getTime()))return{success:!1,error:"For once schedule, value must be a valid ISO date string"};if(p.getTime()<=Date.now())return{success:!1,error:"The scheduled time is in the past. Please specify a future time."}}let d=this.actionRepo.create({userId:ne(t),platform:t.platform,chatId:t.chatId,name:s,description:r,scheduleType:n,scheduleValue:o,skillName:i??"llm_prompt",skillInput:JSON.stringify(a??{}),promptTemplate:c}),u=n==="cron"?`cron: ${o}`:n==="interval"?`every ${o} minutes`:`once at ${o}`;return{success:!0,data:{actionId:d.id,name:s,scheduleType:n,scheduleValue:o,skillName:i},display:`Scheduled action created (${d.id}): "${s}" \u2014 ${u}, running "${i}"${d.nextRunAt?`. Next run: ${d.nextRunAt}`:""}`}}listActions(e){let t=this.getAllActions(e);if(t.length===0)return{success:!0,data:[],display:"No scheduled actions."};let s=t.map(r=>{let n=r.enabled?"\u2705":"\u23F8\uFE0F",o=r.scheduleType==="cron"?`cron: ${r.scheduleValue}`:r.scheduleType==="interval"?`every ${r.scheduleValue} min`:`once: ${r.scheduleValue}`,i=r.nextRunAt?` | next: ${r.nextRunAt}`:"";return`- ${n} ${r.id}: "${r.name}" [${o}] \u2192 ${r.skillName}${i}`});return{success:!0,data:t.map(r=>({actionId:r.id,name:r.name,scheduleType:r.scheduleType,scheduleValue:r.scheduleValue,skillName:r.skillName,enabled:r.enabled,nextRunAt:r.nextRunAt,lastRunAt:r.lastRunAt})),display:`Scheduled actions:
604
+ `)}`}}cancelTask(e,t){let s=e.task_id;return!s||typeof s!="string"?{success:!1,error:'Missing required field "task_id" for cancel action'}:this.getAllTasks(t).some(i=>i.id===s)?this.taskRepo.cancel(s)?{success:!0,data:{taskId:s},display:`Background task "${s}" cancelled.`}:{success:!1,error:`Task "${s}" not found or already completed`}:{success:!1,error:`Task "${s}" not found or already completed`}}}});var os,Jc=_(()=>{"use strict";U();Pe();os=class extends x{static{m(this,"ScheduledTaskSkill")}actionRepo;metadata={name:"scheduled_task",category:"automation",description:'Create, list, enable, disable, or delete scheduled actions that run automatically on a recurring basis. Supports cron expressions (e.g. "0 9 * * *" for daily at 9 AM), intervals (in minutes), and one-time schedules. Each scheduled action executes a skill or sends a prompt to the LLM at the configured time. Use this for time-based tasks (reports, periodic checks, reminders). For condition-based alerts ("notify me WHEN X happens"), use the watch tool instead.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","list","enable","disable","delete"],description:"The scheduled task action to perform"},name:{type:"string",description:"Name for the scheduled action (for create)"},description:{type:"string",description:"What the scheduled action does (for create)"},schedule_type:{type:"string",enum:["cron","interval","once"],description:"Type of schedule: cron expression, interval in minutes, or one-time ISO date (for create)"},schedule_value:{type:"string",description:"Schedule value: cron expression, minutes as string, or ISO date (for create)"},skill_name:{type:"string",description:"The skill to execute on schedule (for create)"},skill_input:{type:"object",description:"Input to pass to the skill (for create)"},prompt_template:{type:"string",description:"Optional LLM prompt to run instead of a skill (for create)"},action_id:{type:"string",description:"Scheduled action ID (for enable, disable, delete)"}},required:["action"]}};constructor(e){super(),this.actionRepo=e}getAllActions(e){let t=new Set,s=[];for(let r of G(e))for(let n of this.actionRepo.getByUser(r))t.has(n.id)||(t.add(n.id),s.push(n));return s}async execute(e,t){let s=e.action;switch(s){case"create":return this.createAction(e,t);case"list":return this.listActions(t);case"enable":return this.toggleAction(e,!0,t);case"disable":return this.toggleAction(e,!1,t);case"delete":return this.deleteAction(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: create, list, enable, disable, delete`}}}createAction(e,t){let s=e.name,r=e.description,n=e.schedule_type,o=e.schedule_value,i=e.skill_name,a=e.skill_input,c=e.prompt_template;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "name" for create action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "description" for create action'};if(!n||!["cron","interval","once"].includes(n))return{success:!1,error:'Missing or invalid "schedule_type". Must be "cron", "interval", or "once"'};if(!o||typeof o!="string")return{success:!1,error:'Missing required field "schedule_value" for create action'};if((!i||typeof i!="string")&&!c)return{success:!1,error:'Missing required field "skill_name" (or "prompt_template") for create action'};if(n==="interval"){let p=parseInt(o,10);if(isNaN(p)||p<=0)return{success:!1,error:"For interval schedule, value must be a positive number of minutes"}}if(n==="cron"&&o.trim().split(/\s+/).length!==5)return{success:!1,error:"Cron expression must have 5 fields: minute hour dayOfMonth month dayOfWeek"};if(n==="once"){let p=new Date(o);if(isNaN(p.getTime()))return{success:!1,error:"For once schedule, value must be a valid ISO date string"};if(p.getTime()<=Date.now())return{success:!1,error:"The scheduled time is in the past. Please specify a future time."}}let d=this.actionRepo.create({userId:ne(t),platform:t.platform,chatId:t.chatId,name:s,description:r,scheduleType:n,scheduleValue:o,skillName:i??"llm_prompt",skillInput:JSON.stringify(a??{}),promptTemplate:c}),u=n==="cron"?`cron: ${o}`:n==="interval"?`every ${o} minutes`:`once at ${o}`;return{success:!0,data:{actionId:d.id,name:s,scheduleType:n,scheduleValue:o,skillName:i},display:`Scheduled action created (${d.id}): "${s}" \u2014 ${u}, running "${i}"${d.nextRunAt?`. Next run: ${d.nextRunAt}`:""}`}}listActions(e){let t=this.getAllActions(e);if(t.length===0)return{success:!0,data:[],display:"No scheduled actions."};let s=t.map(r=>{let n=r.enabled?"\u2705":"\u23F8\uFE0F",o=r.scheduleType==="cron"?`cron: ${r.scheduleValue}`:r.scheduleType==="interval"?`every ${r.scheduleValue} min`:`once: ${r.scheduleValue}`,i=r.nextRunAt?` | next: ${r.nextRunAt}`:"";return`- ${n} ${r.id}: "${r.name}" [${o}] \u2192 ${r.skillName}${i}`});return{success:!0,data:t.map(r=>({actionId:r.id,name:r.name,scheduleType:r.scheduleType,scheduleValue:r.scheduleValue,skillName:r.skillName,enabled:r.enabled,nextRunAt:r.nextRunAt,lastRunAt:r.lastRunAt})),display:`Scheduled actions:
582
605
  ${s.join(`
583
- `)}`}}toggleAction(e,t,s){let r=e.action_id;if(!r||typeof r!="string")return{success:!1,error:`Missing required field "action_id" for ${t?"enable":"disable"} action`};let n=this.actionRepo.findById(r),o=W(s);return!n||!o.includes(n.userId)?{success:!1,error:`Scheduled action "${r}" not found`}:this.actionRepo.setEnabled(r,t)?{success:!0,data:{actionId:r,enabled:t},display:`Scheduled action "${r}" ${t?"enabled":"disabled"}.`}:{success:!1,error:`Scheduled action "${r}" not found`}}deleteAction(e,t){let s=e.action_id;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "action_id" for delete action'};let r=this.actionRepo.findById(s),n=W(t);return!r||!n.includes(r.userId)?{success:!1,error:`Scheduled action "${s}" not found`}:this.actionRepo.delete(s)?{success:!0,data:{actionId:s},display:`Scheduled action "${s}" deleted.`}:{success:!1,error:`Scheduled action "${s}" not found`}}}});var kt,ri=_(()=>{"use strict";kt=class{static{m(this,"MCPClient")}serverName;config;logger;client;transport;connected=!1;constructor(e,t,s){this.serverName=e,this.config=t,this.logger=s}async connect(){try{let{Client:e}=await import("@modelcontextprotocol/sdk/client/index.js");if(this.client=new e({name:`alfred-${this.serverName}`,version:"1.0.0"},{capabilities:{}}),this.config.command){let{StdioClientTransport:t}=await import("@modelcontextprotocol/sdk/client/stdio.js"),s={PATH:process.env.PATH??"",HOME:process.env.HOME??process.env.USERPROFILE??"",LANG:process.env.LANG??"en_US.UTF-8",NODE_ENV:process.env.NODE_ENV??"",SYSTEMROOT:process.env.SYSTEMROOT??""};if(this.config.env)for(let[r,n]of Object.entries(this.config.env))s[r]=n.replace(/\$\{(\w+)\}/g,(o,i)=>process.env[i]??"");this.transport=new t({command:this.config.command,args:this.config.args??[],env:s})}else if(this.config.url){let{SSEClientTransport:t}=await import("@modelcontextprotocol/sdk/client/sse.js");this.transport=new t(new URL(this.config.url))}else throw new Error(`MCP server "${this.serverName}": must specify either command or url`);await this.client.connect(this.transport),this.connected=!0,this.logger.info({server:this.serverName},"MCP server connected")}catch(e){throw this.logger.error({server:this.serverName,err:e},"Failed to connect MCP server"),e}}async listTools(){if(!this.connected||!this.client)return[];try{return((await this.client.listTools()).tools??[]).map(t=>({name:t.name,description:t.description,inputSchema:t.inputSchema??{type:"object",properties:{}}}))}catch(e){return this.logger.error({server:this.serverName,err:e},"Failed to list MCP tools"),[]}}async callTool(e,t){if(!this.connected||!this.client)return{content:"MCP server not connected",isError:!0};try{let s=await this.client.callTool({name:e,arguments:t});return{content:(s.content??[]).map(n=>n.text??JSON.stringify(n)).join(`
584
- `),isError:s.isError}}catch(s){return{content:`MCP tool error: ${s instanceof Error?s.message:String(s)}`,isError:!0}}}async disconnect(){if(this.transport)try{await this.transport.close?.()}catch{}this.connected=!1,this.logger.info({server:this.serverName},"MCP server disconnected")}}});var Om,vt,ni=_(()=>{"use strict";U();Om=["read","write","destructive","admin"],vt=class extends R{static{m(this,"MCPSkillAdapter")}client;serverName;toolName;metadata;constructor(e,t,s,r,n,o){super(),this.client=e,this.serverName=t,this.toolName=s;let i=o&&Om.includes(o)?o:"write";this.metadata={name:`mcp__${t}__${s}`,category:"mcp",description:`[MCP/${t}] ${r||s}`,riskLevel:i,version:"1.0.0",inputSchema:n}}async execute(e,t){let s=await this.client.callTool(this.toolName,e);return{success:s.isError!==!0,data:s.content,display:s.content,error:s.isError===!0?s.content:void 0}}}});var lr,Kc=_(()=>{"use strict";ri();ni();lr=class{static{m(this,"MCPManager")}logger;clients=[];skills=[];constructor(e){this.logger=e}async initialize(e){for(let t of e.servers)try{let s=new kt(t.name,t,this.logger.child({mcp:t.name}));await s.connect(),this.clients.push(s);let r=await s.listTools();for(let n of r){let o=new vt(s,t.name,n.name,n.description??"",n.inputSchema);this.skills.push(o)}this.logger.info({server:t.name,tools:r.length},"MCP server initialized")}catch(s){this.logger.error({server:t.name,err:s},"Failed to initialize MCP server")}}getSkills(){return this.skills}async shutdown(){for(let e of this.clients)await e.disconnect();this.clients.length=0,this.skills.length=0}}});var Xc=_(()=>{"use strict";ri();ni();Kc()});import{spawn as Pm}from"node:child_process";import Pe from"node:fs";import Ue from"node:path";import Um from"node:os";import Fm from"node:crypto";var St,oi=_(()=>{"use strict";St=class{static{m(this,"CodeExecutor")}resolveNodePath(){let e=new Set;for(let s of["pdf-parse","exceljs","pdfkit"])try{let r=va.resolve(`${s}/package.json`);e.add(Ue.dirname(Ue.dirname(r)))}catch{}try{let s=Pe.realpathSync(process.argv[1]??""),r=Ue.dirname(s),n=Ue.join(r,"node_modules");Pe.existsSync(n)&&e.add(n);let o=Ue.join(r,"..","node_modules");Pe.existsSync(o)&&e.add(Pe.realpathSync(o))}catch{}let t=Ue.join(process.cwd(),"node_modules");if(Pe.existsSync(t)&&e.add(t),process.env.NODE_PATH)for(let s of process.env.NODE_PATH.split(Ue.delimiter))s&&e.add(s);return[...e].join(Ue.delimiter)}async execute(e,t,s){let r=Math.min(s?.timeout??3e4,12e4),n=Ue.join(Um.tmpdir(),`alfred-sandbox-${Fm.randomUUID()}`);Pe.mkdirSync(n,{recursive:!0});try{let o=t==="javascript"?"js":"py",i=Ue.join(n,`script.${o}`);Pe.writeFileSync(i,e);let a=t==="javascript"?"node":process.platform==="win32"?"python":"python3",c=[i],d=Date.now();return await new Promise(u=>{let p=Pm(a,c,{cwd:n,timeout:r,env:{...process.env,NODE_ENV:"sandbox",PYTHONDONTWRITEBYTECODE:"1",...s?.env,TMPDIR:n,TEMP:n,TMP:n,NODE_PATH:this.resolveNodePath()},stdio:["pipe","pipe","pipe"]}),g="",f="";p.stdout.on("data",h=>{g+=h.toString()}),p.stderr.on("data",h=>{f+=h.toString()}),p.on("close",h=>{let y=Date.now()-d,E=[];try{let $=Pe.readdirSync(n).filter(v=>!v.startsWith("script."));for(let v of $){let P=Ue.join(n,v),x=Pe.statSync(P);if(x.isFile()&&x.size<1e7){let q=Pe.readFileSync(P),J=v.endsWith(".png")?"image/png":v.endsWith(".jpg")||v.endsWith(".jpeg")?"image/jpeg":v.endsWith(".svg")?"image/svg+xml":v.endsWith(".csv")?"text/csv":v.endsWith(".json")?"application/json":v.endsWith(".html")||v.endsWith(".htm")?"text/html":v.endsWith(".txt")?"text/plain":v.endsWith(".md")?"text/markdown":v.endsWith(".xml")?"application/xml":v.endsWith(".xlsx")?"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":v.endsWith(".xls")?"application/vnd.ms-excel":v.endsWith(".pdf")?"application/pdf":"application/octet-stream";E.push({name:v,data:q,mimeType:J})}}}catch{}u({stdout:g.slice(0,5e4),stderr:f.slice(0,1e4),exitCode:h??1,files:E.length>0?E:void 0,durationMs:y})}),p.on("error",h=>{u({stdout:"",stderr:h.message,exitCode:1,durationMs:Date.now()-d})}),p.stdin.end()})}finally{try{Pe.rmSync(n,{recursive:!0,force:!0})}catch{}}}}});var dr,Yc=_(()=>{"use strict";U();oi();dr=class extends R{static{m(this,"CodeExecutionSkill")}metadata={name:"code_sandbox",category:"automation",description:"Execute code in a sandboxed environment. Supports JavaScript (Node.js) and Python. Use for calculations, data processing, generating files (PDF, HTML, CSV, images, etc.), or testing code snippets. Code runs in an isolated temp directory with a timeout. Any files written to the working directory are automatically collected and sent to the user as attachments \u2014 do NOT use the file skill to send them afterwards. For PDF generation use pdfkit (Node.js) or reportlab/fpdf (Python). IMPORTANT: When generating large files, write compact data-driven code \u2014 define data as arrays/objects, then build the output programmatically. Never embed large HTML/text as string literals.",riskLevel:"write",version:"1.0.0",timeoutMs:12e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["run","run_with_data"],description:"Action to perform"},code:{type:"string",description:"Code to execute"},language:{type:"string",enum:["javascript","python"],description:"Programming language"},data:{type:"string",description:"Input data (available as variable INPUT_DATA, already parsed if JSON). Do NOT use os.environ or process.env \u2014 use INPUT_DATA directly."},timeout:{type:"number",description:"Timeout in ms (max 120000)"}},required:["action","code","language"]}};executor=new St;allowedLanguages;maxTimeout;constructor(e){super(),this.allowedLanguages=new Set(e?.allowedLanguages??["javascript","python"]),this.maxTimeout=e?.maxTimeoutMs??12e4}async execute(e,t){let s=e.action,r=e.code,n=e.language,o=e.data,i=Math.min(e.timeout??3e4,this.maxTimeout);if(!r)return{success:!1,error:'Missing required field "code". IMPORTANT: Do NOT embed large HTML/text as string literals \u2014 write compact code that builds content programmatically from data arrays/objects, e.g. rows.map(r => `<tr><td>${r.time}</td><td>${r.price}</td></tr>`).join(""). Keep the code short and data-driven.'};if(!n)return{success:!1,error:'Missing required field "language"'};if(!this.allowedLanguages.has(n))return{success:!1,error:`Language "${n}" is not allowed. Allowed: ${[...this.allowedLanguages].join(", ")}`};if(s==="run"&&r.length>4e3)return{success:!1,error:`Code too large (${r.length} chars, limit 4000 for action "run"). This usually means data is hardcoded in the code. Use action "run_with_data" with the data parameter instead. The data will be available as INPUT_DATA (already parsed). If you received a data reference like "result_1", pass it as the data parameter.`};let a=r;if(s==="run_with_data"&&o){let p=!1;try{JSON.parse(o),p=!0}catch{}n==="javascript"?a=p?`const INPUT_DATA = ${o};
606
+ `)}`}}toggleAction(e,t,s){let r=e.action_id;if(!r||typeof r!="string")return{success:!1,error:`Missing required field "action_id" for ${t?"enable":"disable"} action`};let n=this.actionRepo.findById(r),o=G(s);return!n||!o.includes(n.userId)?{success:!1,error:`Scheduled action "${r}" not found`}:this.actionRepo.setEnabled(r,t)?{success:!0,data:{actionId:r,enabled:t},display:`Scheduled action "${r}" ${t?"enabled":"disabled"}.`}:{success:!1,error:`Scheduled action "${r}" not found`}}deleteAction(e,t){let s=e.action_id;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "action_id" for delete action'};let r=this.actionRepo.findById(s),n=G(t);return!r||!n.includes(r.userId)?{success:!1,error:`Scheduled action "${s}" not found`}:this.actionRepo.delete(s)?{success:!0,data:{actionId:s},display:`Scheduled action "${s}" deleted.`}:{success:!1,error:`Scheduled action "${s}" not found`}}}});var vt,oi=_(()=>{"use strict";vt=class{static{m(this,"MCPClient")}serverName;config;logger;client;transport;connected=!1;constructor(e,t,s){this.serverName=e,this.config=t,this.logger=s}async connect(){try{let{Client:e}=await import("@modelcontextprotocol/sdk/client/index.js");if(this.client=new e({name:`alfred-${this.serverName}`,version:"1.0.0"},{capabilities:{}}),this.config.command){let{StdioClientTransport:t}=await import("@modelcontextprotocol/sdk/client/stdio.js"),s={PATH:process.env.PATH??"",HOME:process.env.HOME??process.env.USERPROFILE??"",LANG:process.env.LANG??"en_US.UTF-8",NODE_ENV:process.env.NODE_ENV??"",SYSTEMROOT:process.env.SYSTEMROOT??""};if(this.config.env)for(let[r,n]of Object.entries(this.config.env))s[r]=n.replace(/\$\{(\w+)\}/g,(o,i)=>process.env[i]??"");this.transport=new t({command:this.config.command,args:this.config.args??[],env:s})}else if(this.config.url){let{SSEClientTransport:t}=await import("@modelcontextprotocol/sdk/client/sse.js");this.transport=new t(new URL(this.config.url))}else throw new Error(`MCP server "${this.serverName}": must specify either command or url`);await this.client.connect(this.transport),this.connected=!0,this.logger.info({server:this.serverName},"MCP server connected")}catch(e){throw this.logger.error({server:this.serverName,err:e},"Failed to connect MCP server"),e}}async listTools(){if(!this.connected||!this.client)return[];try{return((await this.client.listTools()).tools??[]).map(t=>({name:t.name,description:t.description,inputSchema:t.inputSchema??{type:"object",properties:{}}}))}catch(e){return this.logger.error({server:this.serverName,err:e},"Failed to list MCP tools"),[]}}async callTool(e,t){if(!this.connected||!this.client)return{content:"MCP server not connected",isError:!0};try{let s=await this.client.callTool({name:e,arguments:t});return{content:(s.content??[]).map(n=>n.text??JSON.stringify(n)).join(`
607
+ `),isError:s.isError}}catch(s){return{content:`MCP tool error: ${s instanceof Error?s.message:String(s)}`,isError:!0}}}async disconnect(){if(this.transport)try{await this.transport.close?.()}catch{}this.connected=!1,this.logger.info({server:this.serverName},"MCP server disconnected")}}});var jm,St,ii=_(()=>{"use strict";U();jm=["read","write","destructive","admin"],St=class extends x{static{m(this,"MCPSkillAdapter")}client;serverName;toolName;metadata;constructor(e,t,s,r,n,o){super(),this.client=e,this.serverName=t,this.toolName=s;let i=o&&jm.includes(o)?o:"write";this.metadata={name:`mcp__${t}__${s}`,category:"mcp",description:`[MCP/${t}] ${r||s}`,riskLevel:i,version:"1.0.0",inputSchema:n}}async execute(e,t){let s=await this.client.callTool(this.toolName,e);return{success:s.isError!==!0,data:s.content,display:s.content,error:s.isError===!0?s.content:void 0}}}});var pr,Zc=_(()=>{"use strict";oi();ii();pr=class{static{m(this,"MCPManager")}logger;clients=[];skills=[];constructor(e){this.logger=e}async initialize(e){for(let t of e.servers)try{let s=new vt(t.name,t,this.logger.child({mcp:t.name}));await s.connect(),this.clients.push(s);let r=await s.listTools();for(let n of r){let o=new St(s,t.name,n.name,n.description??"",n.inputSchema);this.skills.push(o)}this.logger.info({server:t.name,tools:r.length},"MCP server initialized")}catch(s){this.logger.error({server:t.name,err:s},"Failed to initialize MCP server")}}getSkills(){return this.skills}async shutdown(){for(let e of this.clients)await e.disconnect();this.clients.length=0,this.skills.length=0}}});var Qc=_(()=>{"use strict";oi();ii();Zc()});import{spawn as Bm}from"node:child_process";import Fe from"node:fs";import je from"node:path";import Hm from"node:os";import zm from"node:crypto";var At,ai=_(()=>{"use strict";At=class{static{m(this,"CodeExecutor")}resolveNodePath(){let e=new Set;for(let s of["pdf-parse","exceljs","pdfkit"])try{let r=Ia.resolve(`${s}/package.json`);e.add(je.dirname(je.dirname(r)))}catch{}try{let s=Fe.realpathSync(process.argv[1]??""),r=je.dirname(s),n=je.join(r,"node_modules");Fe.existsSync(n)&&e.add(n);let o=je.join(r,"..","node_modules");Fe.existsSync(o)&&e.add(Fe.realpathSync(o))}catch{}let t=je.join(process.cwd(),"node_modules");if(Fe.existsSync(t)&&e.add(t),process.env.NODE_PATH)for(let s of process.env.NODE_PATH.split(je.delimiter))s&&e.add(s);return[...e].join(je.delimiter)}async execute(e,t,s){let r=Math.min(s?.timeout??3e4,12e4),n=je.join(Hm.tmpdir(),`alfred-sandbox-${zm.randomUUID()}`);Fe.mkdirSync(n,{recursive:!0});try{let o=t==="javascript"?"js":"py",i=je.join(n,`script.${o}`);Fe.writeFileSync(i,e);let a=t==="javascript"?"node":process.platform==="win32"?"python":"python3",c=[i],d=Date.now();return await new Promise(u=>{let p=Bm(a,c,{cwd:n,timeout:r,env:{...process.env,NODE_ENV:"sandbox",PYTHONDONTWRITEBYTECODE:"1",...s?.env,TMPDIR:n,TEMP:n,TMP:n,NODE_PATH:this.resolveNodePath()},stdio:["pipe","pipe","pipe"]}),f="",g="";p.stdout.on("data",h=>{f+=h.toString()}),p.stderr.on("data",h=>{g+=h.toString()}),p.on("close",h=>{let y=Date.now()-d,E=[];try{let $=Fe.readdirSync(n).filter(v=>!v.startsWith("script."));for(let v of $){let j=je.join(n,v),N=Fe.statSync(j);if(N.isFile()&&N.size<1e7){let H=Fe.readFileSync(j),D=v.endsWith(".png")?"image/png":v.endsWith(".jpg")||v.endsWith(".jpeg")?"image/jpeg":v.endsWith(".svg")?"image/svg+xml":v.endsWith(".csv")?"text/csv":v.endsWith(".json")?"application/json":v.endsWith(".html")||v.endsWith(".htm")?"text/html":v.endsWith(".txt")?"text/plain":v.endsWith(".md")?"text/markdown":v.endsWith(".xml")?"application/xml":v.endsWith(".xlsx")?"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":v.endsWith(".xls")?"application/vnd.ms-excel":v.endsWith(".pdf")?"application/pdf":"application/octet-stream";E.push({name:v,data:H,mimeType:D})}}}catch{}u({stdout:f.slice(0,5e4),stderr:g.slice(0,1e4),exitCode:h??1,files:E.length>0?E:void 0,durationMs:y})}),p.on("error",h=>{u({stdout:"",stderr:h.message,exitCode:1,durationMs:Date.now()-d})}),p.stdin.end()})}finally{try{Fe.rmSync(n,{recursive:!0,force:!0})}catch{}}}}});var hr,el=_(()=>{"use strict";U();ai();hr=class extends x{static{m(this,"CodeExecutionSkill")}metadata={name:"code_sandbox",category:"automation",description:"Execute code in a sandboxed environment. Supports JavaScript (Node.js) and Python. Use for calculations, data processing, generating files (PDF, HTML, CSV, images, etc.), or testing code snippets. Code runs in an isolated temp directory with a timeout. Any files written to the working directory are automatically collected and sent to the user as attachments \u2014 do NOT use the file skill to send them afterwards. For PDF generation use pdfkit (Node.js) or reportlab/fpdf (Python). IMPORTANT: When generating large files, write compact data-driven code \u2014 define data as arrays/objects, then build the output programmatically. Never embed large HTML/text as string literals.",riskLevel:"write",version:"1.0.0",timeoutMs:12e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["run","run_with_data"],description:"Action to perform"},code:{type:"string",description:"Code to execute"},language:{type:"string",enum:["javascript","python"],description:"Programming language"},data:{type:"string",description:"Input data (available as variable INPUT_DATA, already parsed if JSON). Do NOT use os.environ or process.env \u2014 use INPUT_DATA directly."},timeout:{type:"number",description:"Timeout in ms (max 120000)"}},required:["action","code","language"]}};executor=new At;allowedLanguages;maxTimeout;constructor(e){super(),this.allowedLanguages=new Set(e?.allowedLanguages??["javascript","python"]),this.maxTimeout=e?.maxTimeoutMs??12e4}async execute(e,t){let s=e.action,r=e.code,n=e.language,o=e.data,i=Math.min(e.timeout??3e4,this.maxTimeout);if(!r)return{success:!1,error:'Missing required field "code". IMPORTANT: Do NOT embed large HTML/text as string literals \u2014 write compact code that builds content programmatically from data arrays/objects, e.g. rows.map(r => `<tr><td>${r.time}</td><td>${r.price}</td></tr>`).join(""). Keep the code short and data-driven.'};if(!n)return{success:!1,error:'Missing required field "language"'};if(!this.allowedLanguages.has(n))return{success:!1,error:`Language "${n}" is not allowed. Allowed: ${[...this.allowedLanguages].join(", ")}`};if(s==="run"&&r.length>4e3)return{success:!1,error:`Code too large (${r.length} chars, limit 4000 for action "run"). This usually means data is hardcoded in the code. Use action "run_with_data" with the data parameter instead. The data will be available as INPUT_DATA (already parsed). If you received a data reference like "result_1", pass it as the data parameter.`};let a=r;if(s==="run_with_data"&&o){let p=!1;try{JSON.parse(o),p=!0}catch{}n==="javascript"?a=p?`const INPUT_DATA = ${o};
585
608
  ${r}`:`const INPUT_DATA = ${JSON.stringify(o)};
586
609
  ${r}`:a=p?`import json as _json
587
610
  INPUT_DATA = _json.loads(${JSON.stringify(o)})
@@ -590,7 +613,7 @@ ${r}`}let c=await this.executor.execute(a,n,{timeout:i}),d=c.files?.map(p=>({fil
590
613
  ${c.stdout}`:"",c.stderr?`Errors:
591
614
  ${c.stderr}`:"",`Exit code: ${c.exitCode}`,`Duration: ${c.durationMs}ms`,d&&d.length>0?`Files generated: ${d.map(p=>p.fileName).join(", ")}`:""].filter(Boolean).join(`
592
615
 
593
- `);return{success:c.exitCode===0,data:{stdout:c.stdout,stderr:c.stderr,exitCode:c.exitCode,durationMs:c.durationMs,fileCount:c.files?.length??0},display:u,error:c.exitCode!==0?`Code execution failed with exit code ${c.exitCode}`:void 0,attachments:d}}}});var Jc=_(()=>{"use strict";oi();Yc()});var ns,Zc=_(()=>{"use strict";U();Me();ns=class extends R{static{m(this,"DocumentSkill")}docRepo;processor;embeddingService;metadata={name:"document",category:"files",description:"Ingest, search, summarize, list, or delete documents. Supports PDF, DOCX, TXT, CSV, and Markdown files. Documents are chunked and embedded for semantic search.",riskLevel:"write",version:"1.0.0",timeoutMs:12e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["ingest","search","summarize","list","delete"],description:"Action to perform"},file_path:{type:"string",description:"Path to the file (for ingest)"},filename:{type:"string",description:"Original filename (for ingest)"},mime_type:{type:"string",description:"MIME type of the file (for ingest)"},query:{type:"string",description:"Search query (for search)"},document_id:{type:"string",description:"Document ID (for summarize, delete)"},limit:{type:"number",description:"Max results (for search, list)"}},required:["action"]}};constructor(e,t,s){super(),this.docRepo=e,this.processor=t,this.embeddingService=s}async execute(e,t){let s=e.action;switch(s){case"ingest":return this.ingest(e,t);case"search":return this.search(e,t);case"summarize":return this.summarize(e);case"list":return this.list(e,t);case"delete":return this.deleteDoc(e);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: ingest, search, summarize, list, delete`}}}async ingest(e,t){let s=e.file_path,r=e.filename,n=e.mime_type;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "file_path" for ingest action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "filename" for ingest action'};if(!n||typeof n!="string")return{success:!1,error:'Missing required field "mime_type" for ingest action'};let o=await import("node:path"),i=o.resolve(s);if(i!==o.normalize(s)&&s.includes(".."))return{success:!1,error:"Invalid file path: path traversal not allowed"};let a=i.toLowerCase();if(a.startsWith("/etc/")||a.startsWith("/proc/")||a.startsWith("/sys/")||a.startsWith("c:\\windows\\")||a.startsWith("/root/"))return{success:!1,error:"Access to system directories is not allowed"};try{let c=await this.processor.ingest(ne(t),s,r,n),d=c.existing?`Document "${r}" already ingested (${c.chunkCount} chunks). Ready for search. ID: ${c.documentId.slice(0,8)}...`:`Document "${r}" ingested successfully (${c.chunkCount} chunks). ID: ${c.documentId.slice(0,8)}...`;return{success:!0,data:c,display:d}}catch(c){return{success:!1,error:`Failed to ingest document: ${c instanceof Error?c.message:String(c)}`}}}async search(e,t){let s=e.query,r=e.limit||5;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for search action'};if(!this.embeddingService)return{success:!1,error:"Embedding service not available for document search"};let n=W(t),o=new Set,i=[];for(let d of n)for(let u of await this.embeddingService.semanticSearch(d,s,r))o.has(u.key)||(o.add(u.key),i.push(u));let a=i.filter(d=>d.category==="document");if(a.length===0)return{success:!0,data:[],display:`No document matches found for "${s}".`};let c=a.map((d,u)=>`${u+1}. (score: ${d.score.toFixed(3)}) ${d.value.slice(0,200)}${d.value.length>200?"...":""}`).join(`
616
+ `);return{success:c.exitCode===0,data:{stdout:c.stdout,stderr:c.stderr,exitCode:c.exitCode,durationMs:c.durationMs,fileCount:c.files?.length??0},display:u,error:c.exitCode!==0?`Code execution failed with exit code ${c.exitCode}`:void 0,attachments:d}}}});var tl=_(()=>{"use strict";ai();el()});var is,sl=_(()=>{"use strict";U();Pe();is=class extends x{static{m(this,"DocumentSkill")}docRepo;processor;embeddingService;metadata={name:"document",category:"files",description:"Ingest, search, summarize, list, or delete documents. Supports PDF, DOCX, TXT, CSV, and Markdown files. Documents are chunked and embedded for semantic search.",riskLevel:"write",version:"1.0.0",timeoutMs:12e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["ingest","search","summarize","list","delete"],description:"Action to perform"},file_path:{type:"string",description:"Path to the file (for ingest)"},filename:{type:"string",description:"Original filename (for ingest)"},mime_type:{type:"string",description:"MIME type of the file (for ingest)"},query:{type:"string",description:"Search query (for search)"},document_id:{type:"string",description:"Document ID (for summarize, delete)"},limit:{type:"number",description:"Max results (for search, list)"}},required:["action"]}};constructor(e,t,s){super(),this.docRepo=e,this.processor=t,this.embeddingService=s}async execute(e,t){let s=e.action;switch(s){case"ingest":return this.ingest(e,t);case"search":return this.search(e,t);case"summarize":return this.summarize(e);case"list":return this.list(e,t);case"delete":return this.deleteDoc(e);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: ingest, search, summarize, list, delete`}}}async ingest(e,t){let s=e.file_path,r=e.filename,n=e.mime_type;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "file_path" for ingest action'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "filename" for ingest action'};if(!n||typeof n!="string")return{success:!1,error:'Missing required field "mime_type" for ingest action'};let o=await import("node:path"),i=o.resolve(s);if(i!==o.normalize(s)&&s.includes(".."))return{success:!1,error:"Invalid file path: path traversal not allowed"};let a=i.toLowerCase();if(a.startsWith("/etc/")||a.startsWith("/proc/")||a.startsWith("/sys/")||a.startsWith("c:\\windows\\")||a.startsWith("/root/"))return{success:!1,error:"Access to system directories is not allowed"};try{let c=await this.processor.ingest(ne(t),s,r,n),d=c.existing?`Document "${r}" already ingested (${c.chunkCount} chunks). Ready for search. ID: ${c.documentId.slice(0,8)}...`:`Document "${r}" ingested successfully (${c.chunkCount} chunks). ID: ${c.documentId.slice(0,8)}...`;return{success:!0,data:c,display:d}}catch(c){return{success:!1,error:`Failed to ingest document: ${c instanceof Error?c.message:String(c)}`}}}async search(e,t){let s=e.query,r=e.limit||5;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "query" for search action'};if(!this.embeddingService)return{success:!1,error:"Embedding service not available for document search"};let n=G(t),o=new Set,i=[];for(let d of n)for(let u of await this.embeddingService.semanticSearch(d,s,r))o.has(u.key)||(o.add(u.key),i.push(u));let a=i.filter(d=>d.category==="document");if(a.length===0)return{success:!0,data:[],display:`No document matches found for "${s}".`};let c=a.map((d,u)=>`${u+1}. (score: ${d.score.toFixed(3)}) ${d.value.slice(0,200)}${d.value.length>200?"...":""}`).join(`
594
617
 
595
618
  `);return{success:!0,data:a,display:`Found ${a.length} relevant chunk(s):
596
619
 
@@ -600,15 +623,15 @@ ${c}`}}summarize(e){let t=e.document_id;if(!t||typeof t!="string")return{success
600
623
 
601
624
  [... truncated]`:n;return{success:!0,data:{document:s,content:a,totalChunks:r.length,truncated:i},display:`Document: **${s.filename}** (${r.length} chunks, ${s.sizeBytes} bytes)
602
625
 
603
- ${a}`}}list(e,t){let s=e.limit||50,r=W(t),n=new Set,o=[];for(let c of r)for(let d of this.docRepo.listByUser(c))n.has(d.id)||(n.add(d.id),o.push(d));let i=o.slice(0,s);if(i.length===0)return{success:!0,data:[],display:"No documents found."};let a=i.map(c=>`- **${c.filename}** [id=${c.id}] \u2014 ${c.mimeType}, ${c.chunkCount} chunks, ${c.sizeBytes} bytes`).join(`
626
+ ${a}`}}list(e,t){let s=e.limit||50,r=G(t),n=new Set,o=[];for(let c of r)for(let d of this.docRepo.listByUser(c))n.has(d.id)||(n.add(d.id),o.push(d));let i=o.slice(0,s);if(i.length===0)return{success:!0,data:[],display:"No documents found."};let a=i.map(c=>`- **${c.filename}** [id=${c.id}] \u2014 ${c.mimeType}, ${c.chunkCount} chunks, ${c.sizeBytes} bytes`).join(`
604
627
  `);return{success:!0,data:i,display:`${i.length} document(s):
605
- ${a}`}}deleteDoc(e){let t=e.document_id;if(!t||typeof t!="string")return{success:!1,error:'Missing required field "document_id" for delete action'};let s=this.docRepo.getDocument(t);return s?(this.docRepo.deleteDocument(t),{success:!0,data:{documentId:t},display:`Document "${s.filename}" deleted.`}):{success:!1,error:`Document "${t}" not found`}}}});var os,Qc=_(()=>{"use strict";U();os=class extends R{static{m(this,"TTSSkill")}synthesizer;metadata={name:"text_to_speech",category:"media",description:"Send a voice/audio message to the user. You MUST use this tool whenever the user asks you to respond as a voice message, speak, or reply with audio. Pass the full response text \u2014 it will be converted to speech and delivered as a playable voice message.",riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{text:{type:"string",description:"The text to convert to speech"}},required:["text"]}};constructor(e){super(),this.synthesizer=e}async execute(e,t){let s=e.text;if(!s)return{success:!1,error:"No text provided for speech synthesis."};try{return{success:!0,display:"Voice message sent.",attachments:[{fileName:"voice.ogg",data:await this.synthesizer.synthesize(s),mimeType:"audio/ogg"}]}}catch(r){return{success:!1,error:`Speech synthesis failed: ${r instanceof Error?r.message:String(r)}`}}}}});var is,el=_(()=>{"use strict";U();is=class extends R{static{m(this,"ImageGenerateSkill")}generator;metadata={name:"image_generate",category:"media",description:"Generate an image from a text description. Use this tool when the user asks you to create, generate, draw, or design an image or picture. Returns the generated image that will be sent to the user.",riskLevel:"read",version:"1.0.0",timeoutMs:12e4,inputSchema:{type:"object",properties:{prompt:{type:"string",description:"Detailed description of the image to generate. Be specific about style, composition, colors, and subject matter."},model:{type:"string",description:"Optional model to use (e.g. gpt-image-1, gpt-image-1-mini, gemini-2.0-flash-exp). Uses provider default if omitted."},size:{type:"string",enum:["1024x1024","1536x1024","1024x1536"],description:"Image dimensions. 1024x1024 (square, default), 1536x1024 (landscape), 1024x1536 (portrait)."},quality:{type:"string",enum:["low","medium","high"],description:"Image quality level. Higher quality takes longer and costs more."}},required:["prompt"]}};constructor(e){super(),this.generator=e}async execute(e,t){let s=e.prompt;if(!s)return{success:!1,error:"No prompt provided for image generation."};try{let r=await this.generator.generate(s,{model:e.model,size:e.size,quality:e.quality});return{success:!0,display:"Image generated.",attachments:[{fileName:"image.png",data:r.data,mimeType:r.mimeType}]}}catch(r){return{success:!1,error:`Image generation failed: ${r instanceof Error?r.message:String(r)}`}}}}});function me(l){return l==null?"-":l<1024?`${l} B`:l<1024**2?`${(l/1024).toFixed(1)} KiB`:l<1024**3?`${(l/1024**2).toFixed(1)} MiB`:`${(l/1024**3).toFixed(2)} GiB`}function ur(l){return l==null?"-":`${(l*100).toFixed(1)}%`}function fn(l){if(!l)return"-";let e=Math.floor(l/86400),t=Math.floor(l%86400/3600),s=Math.floor(l%3600/60),r=[];return e&&r.push(`${e}d`),t&&r.push(`${t}h`),r.push(`${s}m`),r.join(" ")}var gn,tl=_(()=>{"use strict";U();m(me,"bytes");m(ur,"pct");m(fn,"uptimeStr");gn=class l extends R{static{m(this,"ProxmoxSkill")}metadata={name:"proxmox",category:"infrastructure",description:'Manage Proxmox VE virtual machines, containers, and cluster. Use action "list_vms" to see VMs, "start_vm"/"shutdown_vm" to control them, "cluster_status" for health, "create_snapshot" for backups.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["cluster_status","list_nodes","node_stats","list_vms","vm_status","list_snapshots","list_storage","list_tasks","task_status","start_vm","shutdown_vm","reboot_vm","suspend_vm","resume_vm","create_snapshot","backup_vm","migrate_vm","stop_vm","delete_snapshot","rollback_snapshot"],description:"The Proxmox action to perform"},vmid:{type:"number",description:"Virtual machine / container ID"},node:{type:"string",description:"Proxmox node name (optional \u2014 resolved automatically when omitted)"},type:{type:"string",enum:["qemu","lxc"],description:"VM type filter for list_vms (default: both)"},name:{type:"string",description:"Snapshot name (for create/delete/rollback_snapshot)"},description:{type:"string",description:"Snapshot description (optional)"},target:{type:"string",description:"Target node for migration"},storage:{type:"string",description:"Storage target for backup_vm"},upid:{type:"string",description:"Task UPID for task_status"}},required:["action"]}};config;vmCache=null;static VM_CACHE_TTL=3e4;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"cluster_status":return await this.clusterStatus();case"list_nodes":return await this.listNodes();case"node_stats":return await this.nodeStats(e.node);case"list_vms":return await this.listVms(e.node,e.type);case"vm_status":return await this.vmStatus(e.vmid,e.node);case"list_snapshots":return await this.listSnapshots(e.vmid,e.node);case"list_storage":return await this.listStorage(e.node);case"list_tasks":return await this.listTasks(e.node);case"task_status":return await this.taskStatus(e.upid);case"start_vm":return await this.vmPowerAction("start",e);case"shutdown_vm":return await this.vmPowerAction("shutdown",e);case"reboot_vm":return await this.vmPowerAction("reboot",e);case"suspend_vm":return await this.vmPowerAction("suspend",e);case"resume_vm":return await this.vmPowerAction("resume",e);case"stop_vm":return await this.vmPowerAction("stop",e);case"create_snapshot":return await this.createSnapshot(e);case"backup_vm":return await this.backupVm(e);case"migrate_vm":return await this.migrateVm(e);case"delete_snapshot":return await this.deleteSnapshot(e);case"rollback_snapshot":return await this.rollbackSnapshot(e);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Proxmox API error: ${r instanceof Error?r.message:String(r)}. Check baseUrl and connectivity.`}}}async api(e,t,s){let r=`${this.config.baseUrl}/api2/json${t}`,n={Authorization:`PVEAPIToken=${this.config.tokenId}=${this.config.tokenSecret}`},o={method:e,headers:n};s&&e!=="GET"&&(n["Content-Type"]="application/json",o.body=JSON.stringify(s));let i=this.config.verifyTls===!1,a=process.env.NODE_TLS_REJECT_UNAUTHORIZED;i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let c;try{c=await fetch(r,o)}finally{i&&(a===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=a)}if(!c.ok){let u="";try{u=(await c.text()).slice(0,500)}catch{}throw new Error(`HTTP ${c.status} ${c.statusText} \u2014 ${u}`)}return(await c.json()).data}async get(e){return this.api("GET",e)}async post(e,t){return this.api("POST",e,t)}async del(e){return this.api("DELETE",e)}async resolveVm(e,t){if(t)try{return await this.get(`/nodes/${t}/qemu/${e}/status/current`),{node:t,type:"qemu"}}catch{try{return await this.get(`/nodes/${t}/lxc/${e}/status/current`),{node:t,type:"lxc"}}catch{throw new Error(`VM ${e} not found on node "${t}"`)}}let r=(await this.getAllVms()).find(n=>n.vmid===e);if(!r)throw new Error(`VM ${e} not found on any node. Use "list_vms" to see available VMs.`);return{node:r.node,type:r.type}}async getAllVms(){let e=Date.now();if(this.vmCache&&e-this.vmCache.ts<l.VM_CACHE_TTL)return this.vmCache.entries;let t=await this.get("/nodes"),s=[];for(let r of t){let[n,o]=await Promise.all([this.get(`/nodes/${r.node}/qemu`).catch(()=>[]),this.get(`/nodes/${r.node}/lxc`).catch(()=>[])]);for(let i of n)s.push({...i,node:r.node,type:"qemu"});for(let i of o)s.push({...i,node:r.node,type:"lxc"})}return this.vmCache={entries:s,ts:e},s}async clusterStatus(){let e=await this.get("/cluster/status"),t=["## Cluster Status",""],s=e.find(n=>n.type==="cluster");s&&(t.push(`**Cluster:** ${s.name}`),t.push(`**Quorum:** ${s.quorate?"Yes":"No"}`),t.push(`**Nodes:** ${s.nodes??"-"}`),t.push(`**Version:** ${s.version??"-"}`),t.push(""));let r=e.filter(n=>n.type==="node");if(r.length){t.push("| Node | Online | Level | ID |"),t.push("|------|--------|-------|----|");for(let n of r)t.push(`| ${n.name} | ${n.online?"Yes":"**No**"} | ${n.level??"-"} | ${n.nodeid??"-"} |`)}return{success:!0,data:e,display:t.join(`
606
- `)}}async listNodes(){let e=await this.get("/nodes"),t=["## Nodes","","| Node | Status | CPU | RAM Used / Total | Uptime |"];t.push("|------|--------|-----|------------------|--------|");for(let s of e){let r=typeof s.cpu=="number"?ur(s.cpu):"-",n=me(s.mem),o=me(s.maxmem);t.push(`| ${s.node} | ${s.status} | ${r} | ${n} / ${o} | ${fn(s.uptime)} |`)}return{success:!0,data:e,display:t.join(`
607
- `)}}async nodeStats(e){let t=e??this.config.defaultNode;if(!t)return{success:!1,error:'Missing "node" parameter and no defaultNode configured'};let s=await this.get(`/nodes/${t}/status`),r=s.cpu,n=s.memory,o=s.rootfs,i=s.swap,a=[`## Node: ${t}`,"",`**Uptime:** ${fn(s.uptime)}`,`**Kernel:** ${s.kversion??"-"}`,`**PVE Version:** ${s.pveversion??"-"}`,""];return r&&(a.push(`**CPU:** ${r.model??"-"} (${r.cpus??"-"} cores)`),a.push(`**CPU Usage:** ${ur(r.cpu)}`),a.push(`**Load:** ${Array.isArray(s.loadavg)?s.loadavg.join(", "):"-"}`)),n&&a.push(`**RAM:** ${me(n.used)} / ${me(n.total)} (${ur(n.used/n.total)})`),i&&a.push(`**Swap:** ${me(i.used)} / ${me(i.total)}`),o&&a.push(`**Root FS:** ${me(o.used)} / ${me(o.total)}`),{success:!0,data:s,display:a.join(`
608
- `)}}async listVms(e,t){let s;if(e){if(s=[],!t||t==="qemu"){let n=await this.get(`/nodes/${e}/qemu`).catch(()=>[]);for(let o of n)s.push({...o,node:e,type:"qemu"})}if(!t||t==="lxc"){let n=await this.get(`/nodes/${e}/lxc`).catch(()=>[]);for(let o of n)s.push({...o,node:e,type:"lxc"})}}else s=await this.getAllVms(),t&&(s=s.filter(n=>n.type===t));s.sort((n,o)=>n.vmid-o.vmid);let r=["## Virtual Machines & Containers","","| VMID | Name | Type | Node | Status | CPU | RAM Used / Max | Uptime |","|------|------|------|------|--------|-----|----------------|--------|"];for(let n of s)r.push(`| ${n.vmid} | ${n.name??"-"} | ${n.type} | ${n.node} | ${n.status??"-"} | ${ur(n.cpu)} | ${me(n.mem)} / ${me(n.maxmem)} | ${fn(n.uptime)} |`);return s.length===0&&r.push("| - | No VMs found | - | - | - | - | - | - |"),{success:!0,data:s,display:r.join(`
609
- `)}}async vmStatus(e,t){if(e==null)return{success:!1,error:'Missing required "vmid" parameter'};let s=await this.resolveVm(e,t),r=await this.get(`/nodes/${s.node}/${s.type}/${e}/status/current`),n=[`## VM ${e} (${s.type}) on ${s.node}`,"",`**Name:** ${r.name??"-"}`,`**Status:** ${r.status}`,`**CPU:** ${ur(r.cpu)} (${r.cpus??"-"} cores)`,`**RAM:** ${me(r.mem)} / ${me(r.maxmem)}`,`**Disk:** ${me(r.disk)} / ${me(r.maxdisk)}`,`**Uptime:** ${fn(r.uptime)}`,`**PID:** ${r.pid??"-"}`,`**Net In / Out:** ${me(r.netin)} / ${me(r.netout)}`];return r.lock&&n.push(`**Lock:** ${r.lock}`),{success:!0,data:r,display:n.join(`
628
+ ${a}`}}deleteDoc(e){let t=e.document_id;if(!t||typeof t!="string")return{success:!1,error:'Missing required field "document_id" for delete action'};let s=this.docRepo.getDocument(t);return s?(this.docRepo.deleteDocument(t),{success:!0,data:{documentId:t},display:`Document "${s.filename}" deleted.`}):{success:!1,error:`Document "${t}" not found`}}}});var as,rl=_(()=>{"use strict";U();as=class extends x{static{m(this,"TTSSkill")}synthesizer;metadata={name:"text_to_speech",category:"media",description:"Send a voice/audio message to the user. You MUST use this tool whenever the user asks you to respond as a voice message, speak, or reply with audio. Pass the full response text \u2014 it will be converted to speech and delivered as a playable voice message.",riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{text:{type:"string",description:"The text to convert to speech"}},required:["text"]}};constructor(e){super(),this.synthesizer=e}async execute(e,t){let s=e.text;if(!s)return{success:!1,error:"No text provided for speech synthesis."};try{return{success:!0,display:"Voice message sent.",attachments:[{fileName:"voice.ogg",data:await this.synthesizer.synthesize(s),mimeType:"audio/ogg"}]}}catch(r){return{success:!1,error:`Speech synthesis failed: ${r instanceof Error?r.message:String(r)}`}}}}});var cs,nl=_(()=>{"use strict";U();cs=class extends x{static{m(this,"ImageGenerateSkill")}generator;metadata={name:"image_generate",category:"media",description:"Generate an image from a text description. Use this tool when the user asks you to create, generate, draw, or design an image or picture. Returns the generated image that will be sent to the user.",riskLevel:"read",version:"1.0.0",timeoutMs:12e4,inputSchema:{type:"object",properties:{prompt:{type:"string",description:"Detailed description of the image to generate. Be specific about style, composition, colors, and subject matter."},model:{type:"string",description:"Optional model to use (e.g. gpt-image-1, gpt-image-1-mini, gemini-2.0-flash-exp). Uses provider default if omitted."},size:{type:"string",enum:["1024x1024","1536x1024","1024x1536"],description:"Image dimensions. 1024x1024 (square, default), 1536x1024 (landscape), 1024x1536 (portrait)."},quality:{type:"string",enum:["low","medium","high"],description:"Image quality level. Higher quality takes longer and costs more."}},required:["prompt"]}};constructor(e){super(),this.generator=e}async execute(e,t){let s=e.prompt;if(!s)return{success:!1,error:"No prompt provided for image generation."};try{let r=await this.generator.generate(s,{model:e.model,size:e.size,quality:e.quality});return{success:!0,display:"Image generated.",attachments:[{fileName:"image.png",data:r.data,mimeType:r.mimeType}]}}catch(r){return{success:!1,error:`Image generation failed: ${r instanceof Error?r.message:String(r)}`}}}}});function de(l){return l==null?"-":l<1024?`${l} B`:l<1024**2?`${(l/1024).toFixed(1)} KiB`:l<1024**3?`${(l/1024**2).toFixed(1)} MiB`:`${(l/1024**3).toFixed(2)} GiB`}function fr(l){return l==null?"-":`${(l*100).toFixed(1)}%`}function yn(l){if(!l)return"-";let e=Math.floor(l/86400),t=Math.floor(l%86400/3600),s=Math.floor(l%3600/60),r=[];return e&&r.push(`${e}d`),t&&r.push(`${t}h`),r.push(`${s}m`),r.join(" ")}var wn,ol=_(()=>{"use strict";U();m(de,"bytes");m(fr,"pct");m(yn,"uptimeStr");wn=class l extends x{static{m(this,"ProxmoxSkill")}metadata={name:"proxmox",category:"infrastructure",description:'Manage Proxmox VE virtual machines, containers, and cluster. Use action "list_vms" to see VMs, "start_vm"/"shutdown_vm" to control them, "cluster_status" for health, "create_snapshot" for backups.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["cluster_status","list_nodes","node_stats","list_vms","vm_status","list_snapshots","list_storage","list_tasks","task_status","start_vm","shutdown_vm","reboot_vm","suspend_vm","resume_vm","create_snapshot","backup_vm","migrate_vm","stop_vm","delete_snapshot","rollback_snapshot"],description:"The Proxmox action to perform"},vmid:{type:"number",description:"Virtual machine / container ID"},node:{type:"string",description:"Proxmox node name (optional \u2014 resolved automatically when omitted)"},type:{type:"string",enum:["qemu","lxc"],description:"VM type filter for list_vms (default: both)"},name:{type:"string",description:"Snapshot name (for create/delete/rollback_snapshot)"},description:{type:"string",description:"Snapshot description (optional)"},target:{type:"string",description:"Target node for migration"},storage:{type:"string",description:"Storage target for backup_vm"},upid:{type:"string",description:"Task UPID for task_status"}},required:["action"]}};config;vmCache=null;static VM_CACHE_TTL=3e4;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"cluster_status":return await this.clusterStatus();case"list_nodes":return await this.listNodes();case"node_stats":return await this.nodeStats(e.node);case"list_vms":return await this.listVms(e.node,e.type);case"vm_status":return await this.vmStatus(e.vmid,e.node);case"list_snapshots":return await this.listSnapshots(e.vmid,e.node);case"list_storage":return await this.listStorage(e.node);case"list_tasks":return await this.listTasks(e.node);case"task_status":return await this.taskStatus(e.upid);case"start_vm":return await this.vmPowerAction("start",e);case"shutdown_vm":return await this.vmPowerAction("shutdown",e);case"reboot_vm":return await this.vmPowerAction("reboot",e);case"suspend_vm":return await this.vmPowerAction("suspend",e);case"resume_vm":return await this.vmPowerAction("resume",e);case"stop_vm":return await this.vmPowerAction("stop",e);case"create_snapshot":return await this.createSnapshot(e);case"backup_vm":return await this.backupVm(e);case"migrate_vm":return await this.migrateVm(e);case"delete_snapshot":return await this.deleteSnapshot(e);case"rollback_snapshot":return await this.rollbackSnapshot(e);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Proxmox API error: ${r instanceof Error?r.message:String(r)}. Check baseUrl and connectivity.`}}}async api(e,t,s){let r=`${this.config.baseUrl}/api2/json${t}`,n={Authorization:`PVEAPIToken=${this.config.tokenId}=${this.config.tokenSecret}`},o={method:e,headers:n};s&&e!=="GET"&&(n["Content-Type"]="application/json",o.body=JSON.stringify(s));let i=this.config.verifyTls===!1,a=process.env.NODE_TLS_REJECT_UNAUTHORIZED;i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let c;try{c=await fetch(r,o)}finally{i&&(a===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=a)}if(!c.ok){let u="";try{u=(await c.text()).slice(0,500)}catch{}throw new Error(`HTTP ${c.status} ${c.statusText} \u2014 ${u}`)}return(await c.json()).data}async get(e){return this.api("GET",e)}async post(e,t){return this.api("POST",e,t)}async del(e){return this.api("DELETE",e)}async resolveVm(e,t){if(t)try{return await this.get(`/nodes/${t}/qemu/${e}/status/current`),{node:t,type:"qemu"}}catch{try{return await this.get(`/nodes/${t}/lxc/${e}/status/current`),{node:t,type:"lxc"}}catch{throw new Error(`VM ${e} not found on node "${t}"`)}}let r=(await this.getAllVms()).find(n=>n.vmid===e);if(!r)throw new Error(`VM ${e} not found on any node. Use "list_vms" to see available VMs.`);return{node:r.node,type:r.type}}async getAllVms(){let e=Date.now();if(this.vmCache&&e-this.vmCache.ts<l.VM_CACHE_TTL)return this.vmCache.entries;let t=await this.get("/nodes"),s=[];for(let r of t){let[n,o]=await Promise.all([this.get(`/nodes/${r.node}/qemu`).catch(()=>[]),this.get(`/nodes/${r.node}/lxc`).catch(()=>[])]);for(let i of n)s.push({...i,node:r.node,type:"qemu"});for(let i of o)s.push({...i,node:r.node,type:"lxc"})}return this.vmCache={entries:s,ts:e},s}async clusterStatus(){let e=await this.get("/cluster/status"),t=["## Cluster Status",""],s=e.find(n=>n.type==="cluster");s&&(t.push(`**Cluster:** ${s.name}`),t.push(`**Quorum:** ${s.quorate?"Yes":"No"}`),t.push(`**Nodes:** ${s.nodes??"-"}`),t.push(`**Version:** ${s.version??"-"}`),t.push(""));let r=e.filter(n=>n.type==="node");if(r.length){t.push("| Node | Online | Level | ID |"),t.push("|------|--------|-------|----|");for(let n of r)t.push(`| ${n.name} | ${n.online?"Yes":"**No**"} | ${n.level??"-"} | ${n.nodeid??"-"} |`)}return{success:!0,data:e,display:t.join(`
629
+ `)}}async listNodes(){let e=await this.get("/nodes"),t=["## Nodes","","| Node | Status | CPU | RAM Used / Total | Uptime |"];t.push("|------|--------|-----|------------------|--------|");for(let s of e){let r=typeof s.cpu=="number"?fr(s.cpu):"-",n=de(s.mem),o=de(s.maxmem);t.push(`| ${s.node} | ${s.status} | ${r} | ${n} / ${o} | ${yn(s.uptime)} |`)}return{success:!0,data:e,display:t.join(`
630
+ `)}}async nodeStats(e){let t=e??this.config.defaultNode;if(!t)return{success:!1,error:'Missing "node" parameter and no defaultNode configured'};let s=await this.get(`/nodes/${t}/status`),r=s.cpu,n=s.memory,o=s.rootfs,i=s.swap,a=[`## Node: ${t}`,"",`**Uptime:** ${yn(s.uptime)}`,`**Kernel:** ${s.kversion??"-"}`,`**PVE Version:** ${s.pveversion??"-"}`,""];return r&&(a.push(`**CPU:** ${r.model??"-"} (${r.cpus??"-"} cores)`),a.push(`**CPU Usage:** ${fr(r.cpu)}`),a.push(`**Load:** ${Array.isArray(s.loadavg)?s.loadavg.join(", "):"-"}`)),n&&a.push(`**RAM:** ${de(n.used)} / ${de(n.total)} (${fr(n.used/n.total)})`),i&&a.push(`**Swap:** ${de(i.used)} / ${de(i.total)}`),o&&a.push(`**Root FS:** ${de(o.used)} / ${de(o.total)}`),{success:!0,data:s,display:a.join(`
631
+ `)}}async listVms(e,t){let s;if(e){if(s=[],!t||t==="qemu"){let n=await this.get(`/nodes/${e}/qemu`).catch(()=>[]);for(let o of n)s.push({...o,node:e,type:"qemu"})}if(!t||t==="lxc"){let n=await this.get(`/nodes/${e}/lxc`).catch(()=>[]);for(let o of n)s.push({...o,node:e,type:"lxc"})}}else s=await this.getAllVms(),t&&(s=s.filter(n=>n.type===t));s.sort((n,o)=>n.vmid-o.vmid);let r=["## Virtual Machines & Containers","","| VMID | Name | Type | Node | Status | CPU | RAM Used / Max | Uptime |","|------|------|------|------|--------|-----|----------------|--------|"];for(let n of s)r.push(`| ${n.vmid} | ${n.name??"-"} | ${n.type} | ${n.node} | ${n.status??"-"} | ${fr(n.cpu)} | ${de(n.mem)} / ${de(n.maxmem)} | ${yn(n.uptime)} |`);return s.length===0&&r.push("| - | No VMs found | - | - | - | - | - | - |"),{success:!0,data:s,display:r.join(`
632
+ `)}}async vmStatus(e,t){if(e==null)return{success:!1,error:'Missing required "vmid" parameter'};let s=await this.resolveVm(e,t),r=await this.get(`/nodes/${s.node}/${s.type}/${e}/status/current`),n=[`## VM ${e} (${s.type}) on ${s.node}`,"",`**Name:** ${r.name??"-"}`,`**Status:** ${r.status}`,`**CPU:** ${fr(r.cpu)} (${r.cpus??"-"} cores)`,`**RAM:** ${de(r.mem)} / ${de(r.maxmem)}`,`**Disk:** ${de(r.disk)} / ${de(r.maxdisk)}`,`**Uptime:** ${yn(r.uptime)}`,`**PID:** ${r.pid??"-"}`,`**Net In / Out:** ${de(r.netin)} / ${de(r.netout)}`];return r.lock&&n.push(`**Lock:** ${r.lock}`),{success:!0,data:r,display:n.join(`
610
633
  `)}}async listSnapshots(e,t){if(e==null)return{success:!1,error:'Missing required "vmid" parameter'};let s=await this.resolveVm(e,t),r=await this.get(`/nodes/${s.node}/${s.type}/${e}/snapshot`),n=[`## Snapshots for VM ${e}`,"","| Name | Description | Date | Parent |","|------|-------------|------|--------|"];for(let o of r){let i=o.snaptime?new Date(o.snaptime*1e3).toISOString():"-";n.push(`| ${o.name} | ${o.description??"-"} | ${i} | ${o.parent??"-"} |`)}return{success:!0,data:r,display:n.join(`
611
- `)}}async listStorage(e){let t=e??this.config.defaultNode,s=t?`/nodes/${t}/storage`:"/storage",r=await this.get(s),n=[`## Storage${t?` (Node: ${t})`:""}`,"","| Storage | Type | Content | Used / Total | Status |","|---------|------|---------|--------------|--------|"];for(let o of r){let i=me(o.used),a=me(o.total);n.push(`| ${o.storage} | ${o.type} | ${o.content??"-"} | ${i} / ${a} | ${o.active?"active":o.enabled?"enabled":"disabled"} |`)}return{success:!0,data:r,display:n.join(`
634
+ `)}}async listStorage(e){let t=e??this.config.defaultNode,s=t?`/nodes/${t}/storage`:"/storage",r=await this.get(s),n=[`## Storage${t?` (Node: ${t})`:""}`,"","| Storage | Type | Content | Used / Total | Status |","|---------|------|---------|--------------|--------|"];for(let o of r){let i=de(o.used),a=de(o.total);n.push(`| ${o.storage} | ${o.type} | ${o.content??"-"} | ${i} / ${a} | ${o.active?"active":o.enabled?"enabled":"disabled"} |`)}return{success:!0,data:r,display:n.join(`
612
635
  `)}}async listTasks(e){let t=e??this.config.defaultNode;if(!t)return{success:!1,error:'Missing "node" parameter and no defaultNode configured'};let s=await this.get(`/nodes/${t}/tasks?limit=20`),r=[`## Recent Tasks (Node: ${t})`,"","| UPID | Type | Status | Start | User |","|------|------|--------|-------|------|"];for(let n of s){let o=n.starttime?new Date(n.starttime*1e3).toISOString():"-";r.push(`| \`${n.upid?.slice(-16)??"-"}\` | ${n.type??"-"} | ${n.status??"running"} | ${o} | ${n.user??"-"} |`)}return{success:!0,data:s,display:r.join(`
613
636
  `)}}async taskStatus(e){if(!e)return{success:!1,error:'Missing required "upid" parameter'};let t=e.split(":")[1];if(!t)return{success:!1,error:"Invalid UPID format \u2014 cannot extract node"};let s=await this.get(`/nodes/${t}/tasks/${encodeURIComponent(e)}/status`),r=["## Task Status","",`**UPID:** \`${e}\``,`**Status:** ${s.status}`,`**Type:** ${s.type??"-"}`,`**Exit Status:** ${s.exitstatus??"running"}`,`**Node:** ${t}`];return{success:!0,data:s,display:r.join(`
614
637
  `)}}async vmPowerAction(e,t){let s=t.vmid;if(s==null)return{success:!1,error:'Missing required "vmid" parameter'};let r=await this.resolveVm(s,t.node),n=await this.post(`/nodes/${r.node}/${r.type}/${s}/status/${e}`);return{success:!0,data:{vmid:s,node:r.node,type:r.type,action:e,upid:n},display:[`**${e.charAt(0).toUpperCase()+e.slice(1)}** sent to VM ${s} (${r.type}) on **${r.node}**.`,"",`UPID: \`${n}\``,"","Use `task_status` with this UPID to check progress."].join(`
@@ -617,7 +640,7 @@ ${a}`}}deleteDoc(e){let t=e.document_id;if(!t||typeof t!="string")return{success
617
640
  `)}}async migrateVm(e){let t=e.vmid,s=e.target;if(t==null)return{success:!1,error:'Missing required "vmid" parameter'};if(!s)return{success:!1,error:'Missing required "target" parameter (target node)'};let r=await this.resolveVm(t,e.node),n=await this.post(`/nodes/${r.node}/${r.type}/${t}/migrate`,{target:s,online:1});return{success:!0,data:{vmid:t,from:r.node,target:s,type:r.type,upid:n},display:[`Live migration of VM ${t} from **${r.node}** to **${s}** started.`,"",`UPID: \`${n}\``].join(`
618
641
  `)}}async deleteSnapshot(e){let t=e.vmid,s=e.name;if(t==null)return{success:!1,error:'Missing required "vmid" parameter'};if(!s)return{success:!1,error:'Missing required "name" parameter (snapshot name)'};let r=await this.resolveVm(t,e.node),n=await this.del(`/nodes/${r.node}/${r.type}/${t}/snapshot/${encodeURIComponent(s)}`);return{success:!0,data:{vmid:t,node:r.node,type:r.type,snapname:s,upid:n},display:[`Snapshot **"${s}"** deletion started for VM ${t} on **${r.node}**.`,"",`UPID: \`${n}\``].join(`
619
642
  `)}}async rollbackSnapshot(e){let t=e.vmid,s=e.name;if(t==null)return{success:!1,error:'Missing required "vmid" parameter'};if(!s)return{success:!1,error:'Missing required "name" parameter (snapshot name)'};let r=await this.resolveVm(t,e.node),n=await this.post(`/nodes/${r.node}/${r.type}/${t}/snapshot/${encodeURIComponent(s)}/rollback`);return{success:!0,data:{vmid:t,node:r.node,type:r.type,snapname:s,upid:n},display:[`Rollback to snapshot **"${s}"** started for VM ${t} on **${r.node}**.`,"",`UPID: \`${n}\``,"","**Warning:** The VM will be stopped during rollback."].join(`
620
- `)}}}});var yn,sl=_(()=>{"use strict";U();yn=class extends R{static{m(this,"UniFiSkill")}metadata={name:"unifi",category:"infrastructure",description:'Manage UniFi network devices, clients, and WLANs. Use "list_devices" to see APs/switches, "list_clients" for connected clients, "restart_device" to reboot a device, "block_client"/"unblock_client" for access control.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_devices","device_info","list_clients","client_info","list_networks","list_wlans","site_stats","dpi_stats","list_alerts","list_events","list_vouchers","restart_device","locate_device","reconnect_client","create_voucher","enable_wlan","disable_wlan","archive_alerts","block_client","unblock_client","forget_client","adopt_device"],description:"The UniFi action to perform"},mac:{type:"string",description:"MAC address of device or client (for device_info, client_info, restart_device, locate_device, reconnect_client, block_client, unblock_client, forget_client, adopt_device)"},active:{type:"boolean",description:"If true list only active clients, if false list all known clients (for list_clients, default: true)"},enabled:{type:"boolean",description:"Enable or disable device locate LED (for locate_device, default: true)"},id:{type:"string",description:"WLAN config ID (for enable_wlan, disable_wlan)"},limit:{type:"number",description:"Maximum number of items to return (for list_alerts, list_events)"},count:{type:"number",description:"Number of vouchers to create (for create_voucher, default: 1)"},duration:{type:"number",description:"Voucher validity in minutes (for create_voucher)"},bandwidth_down:{type:"number",description:"Download bandwidth limit in Kbps (for create_voucher, optional)"},bandwidth_up:{type:"number",description:"Upload bandwidth limit in Kbps (for create_voucher, optional)"},quota:{type:"number",description:"Number of uses per voucher (for create_voucher, 0 = unlimited, default: 1)"}},required:["action"]}};config;site;controllerMode;cookies=[];csrfToken;constructor(e){if(super(),!e.apiKey&&!(e.username&&e.password))throw new Error('UniFi config requires either "apiKey" or both "username" and "password".');this.config=e,this.site=e.site??"default",e.apiKey&&(this.controllerMode="unifi-os")}async execute(e,t){let s=e.action;try{switch(s){case"list_devices":return await this.listDevices();case"device_info":return await this.deviceInfo(e.mac);case"list_clients":return await this.listClients(e.active);case"client_info":return await this.clientInfo(e.mac);case"list_networks":return await this.listNetworks();case"list_wlans":return await this.listWlans();case"site_stats":return await this.siteStats();case"dpi_stats":return await this.dpiStats(e.mac);case"list_alerts":return await this.listAlerts(e.limit);case"list_events":return await this.listEvents(e.limit);case"list_vouchers":return await this.listVouchers();case"restart_device":return await this.restartDevice(e.mac);case"locate_device":return await this.locateDevice(e.mac,e.enabled);case"reconnect_client":return await this.reconnectClient(e.mac);case"create_voucher":return await this.createVoucher(e.count,e.duration,e.bandwidth_down,e.bandwidth_up,e.quota);case"enable_wlan":return await this.enableWlan(e.id);case"disable_wlan":return await this.disableWlan(e.id);case"archive_alerts":return await this.archiveAlerts();case"block_client":return await this.blockClient(e.mac);case"unblock_client":return await this.unblockClient(e.mac);case"forget_client":return await this.forgetClient(e.mac);case"adopt_device":return await this.adoptDevice(e.mac);default:return{success:!1,error:`Unknown action: ${s}`}}}catch(r){return{success:!1,error:`UniFi error: ${r instanceof Error?r.message:String(r)}`}}}apiUrl(e){let t=this.config.baseUrl.replace(/\/+$/,"");return this.controllerMode==="unifi-os"?`${t}/proxy/network/api/s/${this.site}/${e}`:`${t}/api/s/${this.site}/${e}`}async ensureAuth(){this.config.apiKey||this.cookies.length>0||await this.login()}async login(){let e=this.config.baseUrl.replace(/\/+$/,""),t=JSON.stringify({username:this.config.username,password:this.config.password}),s=await this.rawFetch(`${e}/api/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:t});if(s.status===200){this.controllerMode="unifi-os",this.storeCookies(s);return}if(s.status===404){let r=await this.rawFetch(`${e}/api/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:t});if(r.ok){this.controllerMode="classic",this.storeCookies(r);return}throw new Error(`UniFi Classic login failed (HTTP ${r.status}): ${await r.text()}`)}throw new Error(`UniFi OS login failed (HTTP ${s.status}): ${await s.text()}`)}storeCookies(e){let t=e.headers.getSetCookie?.()??[];t.length>0&&(this.cookies=t.map(r=>r.split(";")[0]));let s=e.headers.get("x-csrf-token");s&&(this.csrfToken=s)}clearSession(){this.cookies=[],this.csrfToken=void 0}async rawFetch(e,t){let s=process.env.NODE_TLS_REJECT_UNAUTHORIZED;this.config.verifyTls===!1&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");try{return await fetch(e,t)}finally{this.config.verifyTls===!1&&(s===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=s)}}async request(e,t,s){await this.ensureAuth();let r=await this.doFetch(e,t,s);if(r.status===401&&!this.config.apiKey){this.clearSession(),await this.login();let n=await this.doFetch(e,t,s);if(!n.ok)throw new Error(`UniFi API error after relogin: HTTP ${n.status} ${await n.text()}`);return this.parseResponse(n)}if(!r.ok)throw new Error(`UniFi API error: HTTP ${r.status} ${await r.text()}`);return this.parseResponse(r)}async doFetch(e,t,s){let r=this.apiUrl(t),n={"Content-Type":"application/json"};return this.config.apiKey?n["X-API-Key"]=this.config.apiKey:(this.cookies.length>0&&(n.Cookie=this.cookies.join("; ")),this.csrfToken&&(n["X-CSRF-Token"]=this.csrfToken)),this.rawFetch(r,{method:e,headers:n,body:s!==void 0?JSON.stringify(s):void 0,signal:AbortSignal.timeout(3e4)})}async parseResponse(e){let t=await e.json(),s=e.headers.get("x-csrf-token");if(s&&(this.csrfToken=s),t.meta?.rc==="error")throw new Error(`UniFi: ${t.meta.msg??"unknown error"}`);return t.data}async listDevices(){let e=await this.request("GET","stat/device"),t=(e??[]).map(r=>({name:r.name??r.hostname??r.mac,model:r.model??"unknown",ip:r.ip??"-",mac:r.mac,status:r.state===1?"online":"offline",clients:r.num_sta??0,load:r["system-stats"]?.cpu!=null?`${r["system-stats"].cpu}%`:"-"})),s=t.length===0?"No devices found.":["| Name | Model | IP | MAC | Status | Clients | CPU |","|------|-------|----|-----|--------|---------|-----|",...t.map(r=>`| ${r.name} | ${r.model} | ${r.ip} | ${r.mac} | ${r.status} | ${r.clients} | ${r.load} |`)].join(`
643
+ `)}}}});var Tn,il=_(()=>{"use strict";U();Tn=class extends x{static{m(this,"UniFiSkill")}metadata={name:"unifi",category:"infrastructure",description:'Manage UniFi network devices, clients, and WLANs. Use "list_devices" to see APs/switches, "list_clients" for connected clients, "restart_device" to reboot a device, "block_client"/"unblock_client" for access control.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_devices","device_info","list_clients","client_info","list_networks","list_wlans","site_stats","dpi_stats","list_alerts","list_events","list_vouchers","restart_device","locate_device","reconnect_client","create_voucher","enable_wlan","disable_wlan","archive_alerts","block_client","unblock_client","forget_client","adopt_device"],description:"The UniFi action to perform"},mac:{type:"string",description:"MAC address of device or client (for device_info, client_info, restart_device, locate_device, reconnect_client, block_client, unblock_client, forget_client, adopt_device)"},active:{type:"boolean",description:"If true list only active clients, if false list all known clients (for list_clients, default: true)"},enabled:{type:"boolean",description:"Enable or disable device locate LED (for locate_device, default: true)"},id:{type:"string",description:"WLAN config ID (for enable_wlan, disable_wlan)"},limit:{type:"number",description:"Maximum number of items to return (for list_alerts, list_events)"},count:{type:"number",description:"Number of vouchers to create (for create_voucher, default: 1)"},duration:{type:"number",description:"Voucher validity in minutes (for create_voucher)"},bandwidth_down:{type:"number",description:"Download bandwidth limit in Kbps (for create_voucher, optional)"},bandwidth_up:{type:"number",description:"Upload bandwidth limit in Kbps (for create_voucher, optional)"},quota:{type:"number",description:"Number of uses per voucher (for create_voucher, 0 = unlimited, default: 1)"}},required:["action"]}};config;site;controllerMode;cookies=[];csrfToken;constructor(e){if(super(),!e.apiKey&&!(e.username&&e.password))throw new Error('UniFi config requires either "apiKey" or both "username" and "password".');this.config=e,this.site=e.site??"default",e.apiKey&&(this.controllerMode="unifi-os")}async execute(e,t){let s=e.action;try{switch(s){case"list_devices":return await this.listDevices();case"device_info":return await this.deviceInfo(e.mac);case"list_clients":return await this.listClients(e.active);case"client_info":return await this.clientInfo(e.mac);case"list_networks":return await this.listNetworks();case"list_wlans":return await this.listWlans();case"site_stats":return await this.siteStats();case"dpi_stats":return await this.dpiStats(e.mac);case"list_alerts":return await this.listAlerts(e.limit);case"list_events":return await this.listEvents(e.limit);case"list_vouchers":return await this.listVouchers();case"restart_device":return await this.restartDevice(e.mac);case"locate_device":return await this.locateDevice(e.mac,e.enabled);case"reconnect_client":return await this.reconnectClient(e.mac);case"create_voucher":return await this.createVoucher(e.count,e.duration,e.bandwidth_down,e.bandwidth_up,e.quota);case"enable_wlan":return await this.enableWlan(e.id);case"disable_wlan":return await this.disableWlan(e.id);case"archive_alerts":return await this.archiveAlerts();case"block_client":return await this.blockClient(e.mac);case"unblock_client":return await this.unblockClient(e.mac);case"forget_client":return await this.forgetClient(e.mac);case"adopt_device":return await this.adoptDevice(e.mac);default:return{success:!1,error:`Unknown action: ${s}`}}}catch(r){return{success:!1,error:`UniFi error: ${r instanceof Error?r.message:String(r)}`}}}apiUrl(e){let t=this.config.baseUrl.replace(/\/+$/,"");return this.controllerMode==="unifi-os"?`${t}/proxy/network/api/s/${this.site}/${e}`:`${t}/api/s/${this.site}/${e}`}async ensureAuth(){this.config.apiKey||this.cookies.length>0||await this.login()}async login(){let e=this.config.baseUrl.replace(/\/+$/,""),t=JSON.stringify({username:this.config.username,password:this.config.password}),s=await this.rawFetch(`${e}/api/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:t});if(s.status===200){this.controllerMode="unifi-os",this.storeCookies(s);return}if(s.status===404){let r=await this.rawFetch(`${e}/api/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:t});if(r.ok){this.controllerMode="classic",this.storeCookies(r);return}throw new Error(`UniFi Classic login failed (HTTP ${r.status}): ${await r.text()}`)}throw new Error(`UniFi OS login failed (HTTP ${s.status}): ${await s.text()}`)}storeCookies(e){let t=e.headers.getSetCookie?.()??[];t.length>0&&(this.cookies=t.map(r=>r.split(";")[0]));let s=e.headers.get("x-csrf-token");s&&(this.csrfToken=s)}clearSession(){this.cookies=[],this.csrfToken=void 0}async rawFetch(e,t){let s=process.env.NODE_TLS_REJECT_UNAUTHORIZED;this.config.verifyTls===!1&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");try{return await fetch(e,t)}finally{this.config.verifyTls===!1&&(s===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=s)}}async request(e,t,s){await this.ensureAuth();let r=await this.doFetch(e,t,s);if(r.status===401&&!this.config.apiKey){this.clearSession(),await this.login();let n=await this.doFetch(e,t,s);if(!n.ok)throw new Error(`UniFi API error after relogin: HTTP ${n.status} ${await n.text()}`);return this.parseResponse(n)}if(!r.ok)throw new Error(`UniFi API error: HTTP ${r.status} ${await r.text()}`);return this.parseResponse(r)}async doFetch(e,t,s){let r=this.apiUrl(t),n={"Content-Type":"application/json"};return this.config.apiKey?n["X-API-Key"]=this.config.apiKey:(this.cookies.length>0&&(n.Cookie=this.cookies.join("; ")),this.csrfToken&&(n["X-CSRF-Token"]=this.csrfToken)),this.rawFetch(r,{method:e,headers:n,body:s!==void 0?JSON.stringify(s):void 0,signal:AbortSignal.timeout(3e4)})}async parseResponse(e){let t=await e.json(),s=e.headers.get("x-csrf-token");if(s&&(this.csrfToken=s),t.meta?.rc==="error")throw new Error(`UniFi: ${t.meta.msg??"unknown error"}`);return t.data}async listDevices(){let e=await this.request("GET","stat/device"),t=(e??[]).map(r=>({name:r.name??r.hostname??r.mac,model:r.model??"unknown",ip:r.ip??"-",mac:r.mac,status:r.state===1?"online":"offline",clients:r.num_sta??0,load:r["system-stats"]?.cpu!=null?`${r["system-stats"].cpu}%`:"-"})),s=t.length===0?"No devices found.":["| Name | Model | IP | MAC | Status | Clients | CPU |","|------|-------|----|-----|--------|---------|-----|",...t.map(r=>`| ${r.name} | ${r.model} | ${r.ip} | ${r.mac} | ${r.status} | ${r.clients} | ${r.load} |`)].join(`
621
644
  `);return{success:!0,data:e,display:s}}async deviceInfo(e){if(!e)return{success:!1,error:"mac is required for device_info."};let s=(await this.request("GET",`stat/device/${e}`))?.[0];if(!s)return{success:!1,error:`Device ${e} not found.`};let r=[`**${s.name??s.hostname??e}**`,`- Model: ${s.model??"unknown"} (${s.model_in_lts?"LTS":s.model_in_eol?"EOL":"supported"})`,`- IP: ${s.ip??"-"}`,`- MAC: ${s.mac}`,`- Status: ${s.state===1?"online":"offline"}`,`- Version: ${s.version??"-"}`,`- Uptime: ${s.uptime?this.formatUptime(s.uptime):"-"}`,`- Clients: ${s.num_sta??0}`,`- CPU: ${s["system-stats"]?.cpu??"-"}%`,`- Memory: ${s["system-stats"]?.mem??"-"}%`,`- TX bytes: ${this.formatBytes(s.tx_bytes??0)}`,`- RX bytes: ${this.formatBytes(s.rx_bytes??0)}`];return{success:!0,data:s,display:r.join(`
622
645
  `)}}async listClients(e){let t=e!==!1,s=t?"stat/sta":"rest/user",r=await this.request("GET",s),n=(r??[]).map(i=>({name:i.name??i.hostname??i.mac,ip:i.ip??i.fixed_ip??"-",mac:i.mac,network:i.essid??i.network??"-",signal:i.signal!=null?`${i.signal} dBm`:"-",tx:this.formatBytes(i.tx_bytes??0),rx:this.formatBytes(i.rx_bytes??0)})),o=n.length===0?"No clients found.":["| Name | IP | MAC | Network | Signal | TX | RX |","|------|----|-----|---------|--------|----|----|",...n.map(i=>`| ${i.name} | ${i.ip} | ${i.mac} | ${i.network} | ${i.signal} | ${i.tx} | ${i.rx} |`)].join(`
623
646
  `);return{success:!0,data:r,display:`${t?"Active":"All known"} clients (${n.length}):
@@ -633,37 +656,37 @@ ${o}`}}async clientInfo(e){if(!e)return{success:!1,error:"mac is required for cl
633
656
  `);return{success:!0,data:s,display:n}}async listEvents(e){let t=Math.min(Math.max(1,e??20),200),s=await this.request("GET",`stat/event?_limit=${t}`),r=(s??[]).slice(0,t).map(o=>({time:o.datetime??o.time??"-",type:o.key??"-",message:o.msg??"-"})),n=r.length===0?"No events.":["| Time | Type | Message |","|------|------|---------|",...r.map(o=>`| ${o.time} | ${o.type} | ${o.message} |`)].join(`
634
657
  `);return{success:!0,data:s,display:n}}async listVouchers(){let e=await this.request("GET","stat/voucher"),t=(e??[]).map(r=>({code:r.code??"-",duration:r.duration!=null?`${r.duration} min`:"-",quota:r.quota??0,used:r.used??0,created:r.create_time?new Date(r.create_time*1e3).toISOString():"-",note:r.note??"-"})),s=t.length===0?"No vouchers.":["| Code | Duration | Quota | Used | Created | Note |","|------|----------|-------|------|---------|------|",...t.map(r=>`| ${r.code} | ${r.duration} | ${r.quota} | ${r.used} | ${r.created} | ${r.note} |`)].join(`
635
658
  `);return{success:!0,data:e,display:s}}async restartDevice(e){return e?(await this.request("POST","cmd/devmgr",{cmd:"restart",mac:e}),{success:!0,data:{mac:e},display:`Restart command sent to device ${e}.`}):{success:!1,error:"mac is required for restart_device."}}async locateDevice(e,t){if(!e)return{success:!1,error:"mac is required for locate_device."};let s=t!==!1,r=s?"set-locate":"unset-locate";return await this.request("POST","cmd/devmgr",{cmd:r,mac:e}),{success:!0,data:{mac:e,enabled:s},display:`Device ${e} locate LED ${s?"enabled":"disabled"}.`}}async reconnectClient(e){return e?(await this.request("POST","cmd/stamgr",{cmd:"kick-sta",mac:e}),{success:!0,data:{mac:e},display:`Reconnect (kick) command sent to client ${e}.`}):{success:!1,error:"mac is required for reconnect_client."}}async createVoucher(e,t,s,r,n){if(!t)return{success:!1,error:"duration (minutes) is required for create_voucher."};let o={cmd:"create-voucher",n:e??1,expire:t,quota:n??1};s!=null&&(o.down=s),r!=null&&(o.up=r);let i=await this.request("POST","cmd/hotspot",o),a=i?.[0]?.create_time;return{success:!0,data:i,display:`Created ${e??1} voucher(s), duration ${t} min.${a?` Batch: ${a}`:""}
636
- Use "list_vouchers" to see the codes.`}}async enableWlan(e){return e?(await this.request("PUT",`rest/wlanconf/${e}`,{enabled:!0}),{success:!0,data:{id:e,enabled:!0},display:`WLAN ${e} enabled.`}):{success:!1,error:"id is required for enable_wlan."}}async disableWlan(e){return e?(await this.request("PUT",`rest/wlanconf/${e}`,{enabled:!1}),{success:!0,data:{id:e,enabled:!1},display:`WLAN ${e} disabled.`}):{success:!1,error:"id is required for disable_wlan."}}async archiveAlerts(){return await this.request("POST","cmd/evtmgr",{cmd:"archive-all-alarms"}),{success:!0,data:{},display:"All alerts archived."}}async blockClient(e){return e?(await this.request("POST","cmd/stamgr",{cmd:"block-sta",mac:e}),{success:!0,data:{mac:e},display:`Client ${e} blocked.`}):{success:!1,error:"mac is required for block_client."}}async unblockClient(e){return e?(await this.request("POST","cmd/stamgr",{cmd:"unblock-sta",mac:e}),{success:!0,data:{mac:e},display:`Client ${e} unblocked.`}):{success:!1,error:"mac is required for unblock_client."}}async forgetClient(e){return e?(await this.request("POST","cmd/stamgr",{cmd:"forget-sta",macs:[e]}),{success:!0,data:{mac:e},display:`Client ${e} forgotten (removed from known clients).`}):{success:!1,error:"mac is required for forget_client."}}async adoptDevice(e){return e?(await this.request("POST","cmd/devmgr",{cmd:"adopt",mac:e}),{success:!0,data:{mac:e},display:`Adopt command sent to device ${e}.`}):{success:!1,error:"mac is required for adopt_device."}}formatBytes(e){if(e===0)return"0 B";let t=["B","KB","MB","GB","TB"],s=Math.min(Math.floor(Math.log(e)/Math.log(1024)),t.length-1);return`${(e/Math.pow(1024,s)).toFixed(s===0?0:1)} ${t[s]}`}formatUptime(e){let t=Math.floor(e/86400),s=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),n=[];return t>0&&n.push(`${t}d`),s>0&&n.push(`${s}h`),n.push(`${r}m`),n.join(" ")}}});function ii(l){let e=l.match(/^(\d+)\s*(h|d|m|w)$/i);if(!e)return new Date(Date.now()-36e5).toISOString();let t=parseInt(e[1],10),s=e[2].toLowerCase(),r={m:6e4,h:36e5,d:864e5,w:6048e5}[s]??36e5;return new Date(Date.now()-t*r).toISOString()}function jm(l){let e=l.indexOf(".");if(e<1)throw new Error(`Invalid entity_id "${l}" \u2014 expected format "domain.name"`);return l.slice(0,e)}var wn,rl=_(()=>{"use strict";U();m(ii,"parsePeriod");m(jm,"extractDomain");wn=class extends R{static{m(this,"HomeAssistantSkill")}metadata={name:"homeassistant",category:"infrastructure",description:'Control Home Assistant smart home devices. Use "states" to list entities, "turn_on"/"turn_off"/"toggle" to control devices, "call_service" for advanced service calls, "history" for entity state history. Also: "areas" for rooms/zones, "presence" for who is home, "activate_scene"/"trigger_automation"/"run_script" for automations, "notify" for notifications, "calendar_events" for calendars, "template" for Jinja2 queries, "error_log" for HA logs. Config API: "create_automation"/"delete_automation", "create_script"/"delete_script", "create_scene"/"delete_scene" \u2014 create persistent automations, scripts, and scenes in HA. Use configData (JSON) with the HA automation/script/scene schema. "briefing_summary" for a compact smart home overview (open contacts, lights on, battery/SoC, energy, climate, presence). Optionally pass entities[] or domains[] to filter. "energy_stats" for energy consumption statistics over a period (today, yesterday, last_week, etc.) \u2014 auto-discovers all energy sensors and calculates kWh consumed.',riskLevel:"write",version:"2.1.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["states","state","turn_on","turn_off","toggle","call_service","services","history","logbook","config","areas","template","presence","notify","activate_scene","trigger_automation","run_script","calendar_events","error_log","create_automation","delete_automation","create_script","delete_script","create_scene","delete_scene","briefing_summary","energy_stats"],description:"The Home Assistant action to perform"},entityId:{type:"string",description:"Entity ID, e.g. light.wohnzimmer, switch.garage, sensor.temperature"},domain:{type:"string",description:'Domain filter for "states" (e.g. light, sensor, switch) or domain for "call_service"'},service:{type:"string",description:'Service name for "call_service", e.g. set_temperature, set_hvac_mode'},serviceData:{type:"string",description:'JSON string with service parameters, e.g. {"brightness": 200, "color_name": "red"}'},period:{type:"string",description:'Time period for history/logbook/energy_stats. Formats: "1h", "24h", "7d" or friendly names "today", "yesterday", "this_week", "last_week", "this_month", "last_month" (default for energy_stats: "today")'},area:{type:"string",description:"Area name or ID (for areas action)"},template:{type:"string",description:"Jinja2 template string (for template action)"},target:{type:"string",description:"Notification target (for notify action, e.g. mobile_app_pixel)"},message:{type:"string",description:"Notification message (for notify action)"},title:{type:"string",description:"Optional title (for notify action)"},startTime:{type:"string",description:"ISO datetime start (for calendar_events, default: now)"},endTime:{type:"string",description:"ISO datetime end (for calendar_events, default: +24h)"},subAction:{type:"string",enum:["trigger","enable","disable"],description:"Sub-action for trigger_automation"},variables:{type:"string",description:"JSON string with script variables (for run_script)"},configId:{type:"string",description:'Unique ID for create/delete automation, script, or scene (e.g. "notify_garage_light"). Used as the HA config entry ID.'},configData:{type:"string",description:"JSON string with the HA config object for create actions. For automations: {alias, description, trigger[], condition[], action[], mode}. For scripts: {alias, sequence[], mode}. For scenes: {name, entities: {entity_id: state}}."},entities:{type:"array",items:{type:"string"},description:'Specific entity IDs for briefing_summary (e.g. ["sensor.victron_soc", "sensor.power_consumption"])'},domains:{type:"array",items:{type:"string"},description:'Domain filters for briefing_summary (e.g. ["binary_sensor", "light", "climate"])'}},required:["action"]}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"states":return await this.getStates(e.domain);case"state":return await this.getState(e.entityId);case"turn_on":return await this.switchAction("turn_on",e.entityId,e.serviceData);case"turn_off":return await this.switchAction("turn_off",e.entityId,e.serviceData);case"toggle":return await this.switchAction("toggle",e.entityId,e.serviceData);case"call_service":return await this.callService(e.domain,e.service,e.entityId,e.serviceData);case"services":return await this.getServices();case"history":return await this.getHistory(e.entityId,e.period);case"logbook":return await this.getLogbook(e.entityId,e.period);case"config":return await this.getConfig();case"areas":return await this.getAreas(e.area);case"template":return await this.renderTemplate(e.template);case"presence":return await this.getPresence();case"notify":return await this.sendNotification(e.message,e.title,e.target);case"activate_scene":return await this.activateScene(e.entityId);case"trigger_automation":return await this.triggerAutomation(e.entityId,e.subAction);case"run_script":return await this.runScript(e.entityId,e.variables);case"calendar_events":return await this.getCalendarEvents(e.entityId,e.startTime,e.endTime);case"error_log":return await this.getErrorLog();case"create_automation":return await this.createConfig("automation",e.configId,e.configData);case"delete_automation":return await this.deleteConfig("automation",e.configId);case"create_script":return await this.createConfig("script",e.configId,e.configData);case"delete_script":return await this.deleteConfig("script",e.configId);case"create_scene":return await this.createConfig("scene",e.configId,e.configData);case"delete_scene":return await this.deleteConfig("scene",e.configId);case"briefing_summary":return await this.getBriefingSummary(e.entities,e.domains);case"energy_stats":return await this.getEnergyStats(e.period,e.entityId);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Home Assistant API error: ${r instanceof Error?r.message:String(r)}. Check baseUrl and connectivity.`}}}async api(e,t,s){let r=`${this.config.baseUrl.replace(/\/+$/,"")}${t}`,n={Authorization:`Bearer ${this.config.accessToken}`,"Content-Type":"application/json"},o={method:e,headers:n,signal:AbortSignal.timeout(15e3)};s&&e!=="GET"&&(o.body=JSON.stringify(s));let i=this.config.verifyTls===!1,a=process.env.NODE_TLS_REJECT_UNAUTHORIZED;i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let c;try{c=await fetch(r,o)}finally{i&&(a===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=a)}if(!c.ok){let d="";try{d=(await c.text()).slice(0,500)}catch{}throw new Error(`HTTP ${c.status} ${c.statusText} \u2014 ${d}`)}return await c.json()}async apiText(e,t,s){let r=`${this.config.baseUrl.replace(/\/+$/,"")}${t}`,n={Authorization:`Bearer ${this.config.accessToken}`,"Content-Type":"application/json"},o={method:e,headers:n,signal:AbortSignal.timeout(15e3)};s&&e!=="GET"&&(o.body=JSON.stringify(s));let i=this.config.verifyTls===!1,a=process.env.NODE_TLS_REJECT_UNAUTHORIZED;i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let c;try{c=await fetch(r,o)}finally{i&&(a===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=a)}if(!c.ok){let d="";try{d=(await c.text()).slice(0,500)}catch{}throw new Error(`HTTP ${c.status} ${c.statusText} \u2014 ${d}`)}return await c.text()}async getStates(e){let t=await this.api("GET","/api/states");e&&(t=t.filter(r=>r.entity_id.startsWith(`${e}.`)));let s=[`## Entities${e?` (${e})`:""}`,"","| Entity ID | State | Name | Unit |","|-----------|-------|------|------|"];for(let r of t){let n=r.attributes?.friendly_name??"-",o=r.attributes?.unit_of_measurement??"-";s.push(`| ${r.entity_id} | ${r.state} | ${n} | ${o} |`)}return t.length===0&&s.push(`| - | No entities found${e?` for domain "${e}"`:""} | - | - |`),{success:!0,data:t,display:s.join(`
659
+ Use "list_vouchers" to see the codes.`}}async enableWlan(e){return e?(await this.request("PUT",`rest/wlanconf/${e}`,{enabled:!0}),{success:!0,data:{id:e,enabled:!0},display:`WLAN ${e} enabled.`}):{success:!1,error:"id is required for enable_wlan."}}async disableWlan(e){return e?(await this.request("PUT",`rest/wlanconf/${e}`,{enabled:!1}),{success:!0,data:{id:e,enabled:!1},display:`WLAN ${e} disabled.`}):{success:!1,error:"id is required for disable_wlan."}}async archiveAlerts(){return await this.request("POST","cmd/evtmgr",{cmd:"archive-all-alarms"}),{success:!0,data:{},display:"All alerts archived."}}async blockClient(e){return e?(await this.request("POST","cmd/stamgr",{cmd:"block-sta",mac:e}),{success:!0,data:{mac:e},display:`Client ${e} blocked.`}):{success:!1,error:"mac is required for block_client."}}async unblockClient(e){return e?(await this.request("POST","cmd/stamgr",{cmd:"unblock-sta",mac:e}),{success:!0,data:{mac:e},display:`Client ${e} unblocked.`}):{success:!1,error:"mac is required for unblock_client."}}async forgetClient(e){return e?(await this.request("POST","cmd/stamgr",{cmd:"forget-sta",macs:[e]}),{success:!0,data:{mac:e},display:`Client ${e} forgotten (removed from known clients).`}):{success:!1,error:"mac is required for forget_client."}}async adoptDevice(e){return e?(await this.request("POST","cmd/devmgr",{cmd:"adopt",mac:e}),{success:!0,data:{mac:e},display:`Adopt command sent to device ${e}.`}):{success:!1,error:"mac is required for adopt_device."}}formatBytes(e){if(e===0)return"0 B";let t=["B","KB","MB","GB","TB"],s=Math.min(Math.floor(Math.log(e)/Math.log(1024)),t.length-1);return`${(e/Math.pow(1024,s)).toFixed(s===0?0:1)} ${t[s]}`}formatUptime(e){let t=Math.floor(e/86400),s=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),n=[];return t>0&&n.push(`${t}d`),s>0&&n.push(`${s}h`),n.push(`${r}m`),n.join(" ")}}});function ci(l){let e=l.match(/^(\d+)\s*(h|d|m|w)$/i);if(!e)return new Date(Date.now()-36e5).toISOString();let t=parseInt(e[1],10),s=e[2].toLowerCase(),r={m:6e4,h:36e5,d:864e5,w:6048e5}[s]??36e5;return new Date(Date.now()-t*r).toISOString()}function qm(l){let e=l.indexOf(".");if(e<1)throw new Error(`Invalid entity_id "${l}" \u2014 expected format "domain.name"`);return l.slice(0,e)}var _n,al=_(()=>{"use strict";U();m(ci,"parsePeriod");m(qm,"extractDomain");_n=class extends x{static{m(this,"HomeAssistantSkill")}metadata={name:"homeassistant",category:"infrastructure",description:'Control Home Assistant smart home devices. Use "states" to list entities, "turn_on"/"turn_off"/"toggle" to control devices, "call_service" for advanced service calls, "history" for entity state history. Also: "areas" for rooms/zones, "presence" for who is home, "activate_scene"/"trigger_automation"/"run_script" for automations, "notify" for notifications, "calendar_events" for calendars, "template" for Jinja2 queries, "error_log" for HA logs. Config API: "create_automation"/"delete_automation", "create_script"/"delete_script", "create_scene"/"delete_scene" \u2014 create persistent automations, scripts, and scenes in HA. Use configData (JSON) with the HA automation/script/scene schema. "briefing_summary" for a compact smart home overview (open contacts, lights on, battery/SoC, energy, climate, presence). Optionally pass entities[] or domains[] to filter. "energy_stats" for energy consumption statistics over a period (today, yesterday, last_week, etc.) \u2014 auto-discovers all energy sensors and calculates kWh consumed.',riskLevel:"write",version:"2.1.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["states","state","turn_on","turn_off","toggle","call_service","services","history","logbook","config","areas","template","presence","notify","activate_scene","trigger_automation","run_script","calendar_events","error_log","create_automation","delete_automation","create_script","delete_script","create_scene","delete_scene","briefing_summary","energy_stats"],description:"The Home Assistant action to perform"},entityId:{type:"string",description:"Entity ID, e.g. light.wohnzimmer, switch.garage, sensor.temperature"},domain:{type:"string",description:'Domain filter for "states" (e.g. light, sensor, switch) or domain for "call_service"'},service:{type:"string",description:'Service name for "call_service", e.g. set_temperature, set_hvac_mode'},serviceData:{type:"string",description:'JSON string with service parameters, e.g. {"brightness": 200, "color_name": "red"}'},period:{type:"string",description:'Time period for history/logbook/energy_stats. Formats: "1h", "24h", "7d" or friendly names "today", "yesterday", "this_week", "last_week", "this_month", "last_month" (default for energy_stats: "today")'},area:{type:"string",description:"Area name or ID (for areas action)"},template:{type:"string",description:"Jinja2 template string (for template action)"},target:{type:"string",description:"Notification target (for notify action, e.g. mobile_app_pixel)"},message:{type:"string",description:"Notification message (for notify action)"},title:{type:"string",description:"Optional title (for notify action)"},startTime:{type:"string",description:"ISO datetime start (for calendar_events, default: now)"},endTime:{type:"string",description:"ISO datetime end (for calendar_events, default: +24h)"},subAction:{type:"string",enum:["trigger","enable","disable"],description:"Sub-action for trigger_automation"},variables:{type:"string",description:"JSON string with script variables (for run_script)"},configId:{type:"string",description:'Unique ID for create/delete automation, script, or scene (e.g. "notify_garage_light"). Used as the HA config entry ID.'},configData:{type:"string",description:"JSON string with the HA config object for create actions. For automations: {alias, description, trigger[], condition[], action[], mode}. For scripts: {alias, sequence[], mode}. For scenes: {name, entities: {entity_id: state}}."},entities:{type:"array",items:{type:"string"},description:'Specific entity IDs for briefing_summary (e.g. ["sensor.victron_soc", "sensor.power_consumption"])'},domains:{type:"array",items:{type:"string"},description:'Domain filters for briefing_summary (e.g. ["binary_sensor", "light", "climate"])'}},required:["action"]}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"states":return await this.getStates(e.domain);case"state":return await this.getState(e.entityId);case"turn_on":return await this.switchAction("turn_on",e.entityId,e.serviceData);case"turn_off":return await this.switchAction("turn_off",e.entityId,e.serviceData);case"toggle":return await this.switchAction("toggle",e.entityId,e.serviceData);case"call_service":return await this.callService(e.domain,e.service,e.entityId,e.serviceData);case"services":return await this.getServices();case"history":return await this.getHistory(e.entityId,e.period);case"logbook":return await this.getLogbook(e.entityId,e.period);case"config":return await this.getConfig();case"areas":return await this.getAreas(e.area);case"template":return await this.renderTemplate(e.template);case"presence":return await this.getPresence();case"notify":return await this.sendNotification(e.message,e.title,e.target);case"activate_scene":return await this.activateScene(e.entityId);case"trigger_automation":return await this.triggerAutomation(e.entityId,e.subAction);case"run_script":return await this.runScript(e.entityId,e.variables);case"calendar_events":return await this.getCalendarEvents(e.entityId,e.startTime,e.endTime);case"error_log":return await this.getErrorLog();case"create_automation":return await this.createConfig("automation",e.configId,e.configData);case"delete_automation":return await this.deleteConfig("automation",e.configId);case"create_script":return await this.createConfig("script",e.configId,e.configData);case"delete_script":return await this.deleteConfig("script",e.configId);case"create_scene":return await this.createConfig("scene",e.configId,e.configData);case"delete_scene":return await this.deleteConfig("scene",e.configId);case"briefing_summary":return await this.getBriefingSummary(e.entities,e.domains);case"energy_stats":return await this.getEnergyStats(e.period,e.entityId);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Home Assistant API error: ${r instanceof Error?r.message:String(r)}. Check baseUrl and connectivity.`}}}async api(e,t,s){let r=`${this.config.baseUrl.replace(/\/+$/,"")}${t}`,n={Authorization:`Bearer ${this.config.accessToken}`,"Content-Type":"application/json"},o={method:e,headers:n,signal:AbortSignal.timeout(15e3)};s&&e!=="GET"&&(o.body=JSON.stringify(s));let i=this.config.verifyTls===!1,a=process.env.NODE_TLS_REJECT_UNAUTHORIZED;i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let c;try{c=await fetch(r,o)}finally{i&&(a===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=a)}if(!c.ok){let d="";try{d=(await c.text()).slice(0,500)}catch{}throw new Error(`HTTP ${c.status} ${c.statusText} \u2014 ${d}`)}return await c.json()}async apiText(e,t,s){let r=`${this.config.baseUrl.replace(/\/+$/,"")}${t}`,n={Authorization:`Bearer ${this.config.accessToken}`,"Content-Type":"application/json"},o={method:e,headers:n,signal:AbortSignal.timeout(15e3)};s&&e!=="GET"&&(o.body=JSON.stringify(s));let i=this.config.verifyTls===!1,a=process.env.NODE_TLS_REJECT_UNAUTHORIZED;i&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let c;try{c=await fetch(r,o)}finally{i&&(a===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=a)}if(!c.ok){let d="";try{d=(await c.text()).slice(0,500)}catch{}throw new Error(`HTTP ${c.status} ${c.statusText} \u2014 ${d}`)}return await c.text()}async getStates(e){let t=await this.api("GET","/api/states");e&&(t=t.filter(r=>r.entity_id.startsWith(`${e}.`)));let s=[`## Entities${e?` (${e})`:""}`,"","| Entity ID | State | Name | Unit |","|-----------|-------|------|------|"];for(let r of t){let n=r.attributes?.friendly_name??"-",o=r.attributes?.unit_of_measurement??"-";s.push(`| ${r.entity_id} | ${r.state} | ${n} | ${o} |`)}return t.length===0&&s.push(`| - | No entities found${e?` for domain "${e}"`:""} | - | - |`),{success:!0,data:t,display:s.join(`
637
660
  `)}}async getState(e){if(!e)return{success:!1,error:'Missing required "entityId" parameter'};let t=await this.api("GET",`/api/states/${e}`),s=t.attributes??{},r=[`## ${s.friendly_name??e}`,"",`**Entity ID:** ${t.entity_id}`,`**State:** ${t.state}`,`**Last Changed:** ${t.last_changed??"-"}`,`**Last Updated:** ${t.last_updated??"-"}`,"","### Attributes"];for(let[n,o]of Object.entries(s)){let i=typeof o=="object"?JSON.stringify(o):String(o);r.push(`- **${n}:** ${i}`)}return{success:!0,data:t,display:r.join(`
638
661
  `)}}async getServices(){let e=await this.api("GET","/api/services"),t=["## Available Services",""];for(let s of e){let r=s.domain??"unknown",n=Object.keys(s.services??{});if(n.length!==0){t.push(`### ${r}`);for(let o of n){let i=s.services[o]?.description??"";t.push(`- **${o}**${i?`: ${i}`:""}`)}t.push("")}}return{success:!0,data:e,display:t.join(`
639
- `)}}async getHistory(e,t){let s=ii(t??"1h"),r=e?`?filter_entity_id=${e}`:"",n=await this.api("GET",`/api/history/period/${s}${r}`),o=["## History",""];if(!n||n.length===0||n.every(i=>i.length===0))return o.push("No history entries found for the given period."),{success:!0,data:[],display:o.join(`
662
+ `)}}async getHistory(e,t){let s=ci(t??"1h"),r=e?`?filter_entity_id=${e}`:"",n=await this.api("GET",`/api/history/period/${s}${r}`),o=["## History",""];if(!n||n.length===0||n.every(i=>i.length===0))return o.push("No history entries found for the given period."),{success:!0,data:[],display:o.join(`
640
663
  `)};for(let i of n){if(i.length===0)continue;let a=i[0]?.entity_id??"unknown",c=i[0]?.attributes?.friendly_name??a;o.push(`### ${c} (\`${a}\`)`),o.push(""),o.push("| Time | State |"),o.push("|------|-------|");for(let d of i){let u=d.last_changed?new Date(d.last_changed).toLocaleString():"-";o.push(`| ${u} | ${d.state} |`)}o.push("")}return{success:!0,data:n,display:o.join(`
641
- `)}}async getLogbook(e,t){let s=ii(t??"1h"),r=e?`?entity=${e}`:"",n=await this.api("GET",`/api/logbook/${s}${r}`),o=["## Logbook",""];if(!n||n.length===0)return o.push("No logbook entries found for the given period."),{success:!0,data:[],display:o.join(`
664
+ `)}}async getLogbook(e,t){let s=ci(t??"1h"),r=e?`?entity=${e}`:"",n=await this.api("GET",`/api/logbook/${s}${r}`),o=["## Logbook",""];if(!n||n.length===0)return o.push("No logbook entries found for the given period."),{success:!0,data:[],display:o.join(`
642
665
  `)};o.push("| Time | Entity | Message |"),o.push("|------|--------|---------|");for(let i of n){let a=i.when?new Date(i.when).toLocaleString():"-",c=i.name??i.entity_id??"-",d=i.message??i.state??"-";o.push(`| ${a} | ${c} | ${d} |`)}return{success:!0,data:n,display:o.join(`
643
666
  `)}}async getConfig(){let e=await this.api("GET","/api/config"),t=["## Home Assistant Configuration","",`**Name:** ${e.location_name??"-"}`,`**Version:** ${e.version??"-"}`,`**Time Zone:** ${e.time_zone??"-"}`,`**Latitude:** ${e.latitude??"-"}`,`**Longitude:** ${e.longitude??"-"}`,`**Elevation:** ${e.elevation??"-"} m`,`**Unit System:** ${e.unit_system?.length?JSON.stringify(e.unit_system):"-"}`,`**Currency:** ${e.currency??"-"}`,`**Internal URL:** ${e.internal_url??"-"}`,`**External URL:** ${e.external_url??"-"}`];return e.components&&Array.isArray(e.components)&&t.push("",`**Components:** ${e.components.length} loaded`),{success:!0,data:e,display:t.join(`
644
- `)}}async switchAction(e,t,s){if(!t)return{success:!1,error:'Missing required "entityId" parameter'};let r=jm(t),n={entity_id:t};if(s)try{let c=JSON.parse(s);Object.assign(n,c)}catch{return{success:!1,error:'Invalid "serviceData" \u2014 must be valid JSON'}}let o=await this.api("POST",`/api/services/${r}/${e}`,n),i=o?.[0]?.state??"unknown",a=o?.[0]?.attributes?.friendly_name??t;return{success:!0,data:o,display:[`**${e.replace("_"," ")}** \u2192 **${a}**`,"",`New state: **${i}**`].join(`
667
+ `)}}async switchAction(e,t,s){if(!t)return{success:!1,error:'Missing required "entityId" parameter'};let r=qm(t),n={entity_id:t};if(s)try{let c=JSON.parse(s);Object.assign(n,c)}catch{return{success:!1,error:'Invalid "serviceData" \u2014 must be valid JSON'}}let o=await this.api("POST",`/api/services/${r}/${e}`,n),i=o?.[0]?.state??"unknown",a=o?.[0]?.attributes?.friendly_name??t;return{success:!0,data:o,display:[`**${e.replace("_"," ")}** \u2192 **${a}**`,"",`New state: **${i}**`].join(`
645
668
  `)}}async callService(e,t,s,r){if(!e)return{success:!1,error:'Missing required "domain" parameter for call_service'};if(!t)return{success:!1,error:'Missing required "service" parameter for call_service'};let n={};if(s&&(n.entity_id=s),r)try{let c=JSON.parse(r);Object.assign(n,c)}catch{return{success:!1,error:'Invalid "serviceData" \u2014 must be valid JSON'}}let o=await this.api("POST",`/api/services/${e}/${t}`,n),i=o?.length??0,a=[`**Service called:** \`${e}.${t}\``,s?`**Entity:** ${s}`:"",`**Affected entities:** ${i}`].filter(Boolean);if(o&&o.length>0){a.push("");for(let c of o){let d=c.attributes?.friendly_name??c.entity_id??"unknown";a.push(`- **${d}**: ${c.state}`)}}return{success:!0,data:o,display:a.join(`
646
669
  `)}}async getAreas(e){if(e){let o=`{% for eid in area_entities('${e.replace(/'/g,"\\'")}') %}{{ eid }}||{{ states(eid) }}||{{ state_attr(eid, 'friendly_name') }}
647
670
  {% endfor %}`,a=(await this.apiText("POST","/api/template",{template:o})).trim().split(`
648
- `).filter(Boolean),c=[`## Area: ${e}`,"","| Entity ID | State | Name |","|-----------|-------|------|"];for(let d of a){let[u,p,g]=d.split("||");c.push(`| ${u} | ${p} | ${g??"-"} |`)}return a.length===0&&c.push(`| - | No entities found for area "${e}" | - |`),{success:!0,data:a,display:c.join(`
671
+ `).filter(Boolean),c=[`## Area: ${e}`,"","| Entity ID | State | Name |","|-----------|-------|------|"];for(let d of a){let[u,p,f]=d.split("||");c.push(`| ${u} | ${p} | ${f??"-"} |`)}return a.length===0&&c.push(`| - | No entities found for area "${e}" | - |`),{success:!0,data:a,display:c.join(`
649
672
  `)}}let r=(await this.apiText("POST","/api/template",{template:`{% for aid in areas() %}{{ area_name(aid) }}||{{ aid }}||{{ area_entities(aid) | length }}
650
673
  {% endfor %}`})).trim().split(`
651
674
  `).filter(Boolean),n=["## Areas","","| Area Name | Area ID | Entity Count |","|-----------|---------|--------------|"];for(let o of r){let[i,a,c]=o.split("||");n.push(`| ${i} | ${a} | ${c} |`)}return r.length===0&&n.push("| - | No areas configured | - |"),{success:!0,data:r,display:n.join(`
652
675
  `)}}async renderTemplate(e){if(!e)return{success:!1,error:'Missing required "template" parameter'};let t=await this.apiText("POST","/api/template",{template:e});return{success:!0,data:t,display:t}}async getPresence(){let t=(await this.api("GET","/api/states")).filter(r=>r.entity_id.startsWith("person.")),s=["## Presence","","| Person | Status | Last Changed |","|--------|--------|--------------|"];for(let r of t){let n=r.attributes?.friendly_name??r.entity_id,o=r.state??"unknown",i=r.last_changed?new Date(r.last_changed).toLocaleString():"-";s.push(`| ${n} | ${o} | ${i} |`)}return t.length===0&&s.push("| - | No person entities found | - |"),{success:!0,data:t,display:s.join(`
653
676
  `)}}async sendNotification(e,t,s){if(!e)return{success:!1,error:'Missing required "message" parameter'};let r=s??"notify",n={message:e};return t&&(n.title=t),{success:!0,data:await this.api("POST",`/api/services/notify/${r}`,n),display:[`**Notification sent** \u2192 \`notify.${r}\``,t?`**Title:** ${t}`:"",`**Message:** ${e.slice(0,200)}${e.length>200?"\u2026":""}`].filter(Boolean).join(`
654
677
  `)}}async activateScene(e){if(!e){let n=(await this.api("GET","/api/states")).filter(i=>i.entity_id.startsWith("scene.")),o=["## Available Scenes","","| Entity ID | Name |","|-----------|------|"];for(let i of n){let a=i.attributes?.friendly_name??i.entity_id;o.push(`| ${i.entity_id} | ${a} |`)}return n.length===0&&o.push("| - | No scenes found |"),{success:!0,data:n,display:o.join(`
655
- `)}}let t=await this.api("POST","/api/services/scene/turn_on",{entity_id:e}),s=t?.[0]?.attributes?.friendly_name??e;return{success:!0,data:t,display:`**Scene activated:** ${s} (\`${e}\`)`}}async triggerAutomation(e,t){if(!e){let d=(await this.api("GET","/api/states")).filter(p=>p.entity_id.startsWith("automation.")),u=["## Automations","","| Entity ID | Name | State | Last Triggered |","|-----------|------|-------|----------------|"];for(let p of d){let g=p.attributes?.friendly_name??p.entity_id,f=p.attributes?.last_triggered?new Date(p.attributes.last_triggered).toLocaleString():"-";u.push(`| ${p.entity_id} | ${g} | ${p.state} | ${f} |`)}return d.length===0&&u.push("| - | No automations found | - | - |"),{success:!0,data:d,display:u.join(`
678
+ `)}}let t=await this.api("POST","/api/services/scene/turn_on",{entity_id:e}),s=t?.[0]?.attributes?.friendly_name??e;return{success:!0,data:t,display:`**Scene activated:** ${s} (\`${e}\`)`}}async triggerAutomation(e,t){if(!e){let d=(await this.api("GET","/api/states")).filter(p=>p.entity_id.startsWith("automation.")),u=["## Automations","","| Entity ID | Name | State | Last Triggered |","|-----------|------|-------|----------------|"];for(let p of d){let f=p.attributes?.friendly_name??p.entity_id,g=p.attributes?.last_triggered?new Date(p.attributes.last_triggered).toLocaleString():"-";u.push(`| ${p.entity_id} | ${f} | ${p.state} | ${g} |`)}return d.length===0&&u.push("| - | No automations found | - | - |"),{success:!0,data:d,display:u.join(`
656
679
  `)}}let s={trigger:"trigger",enable:"turn_on",disable:"turn_off"},r=t??"trigger",n=s[r]??"trigger",o=await this.api("POST",`/api/services/automation/${n}`,{entity_id:e}),i=o?.[0]?.attributes?.friendly_name??e,a=o?.[0]?.attributes?.last_triggered?new Date(o[0].attributes.last_triggered).toLocaleString():"-";return{success:!0,data:o,display:[`**Automation ${r}:** ${i} (\`${e}\`)`,`**Last triggered:** ${a}`].join(`
657
680
  `)}}async runScript(e,t){if(!e){let i=(await this.api("GET","/api/states")).filter(c=>c.entity_id.startsWith("script.")),a=["## Scripts","","| Entity ID | Name | State |","|-----------|------|-------|"];for(let c of i){let d=c.attributes?.friendly_name??c.entity_id;a.push(`| ${c.entity_id} | ${d} | ${c.state} |`)}return i.length===0&&a.push("| - | No scripts found | - |"),{success:!0,data:i,display:a.join(`
658
681
  `)}}let s=e.startsWith("script.")?e.slice(7):e,r={};if(t)try{r=JSON.parse(t)}catch{return{success:!1,error:'Invalid "variables" \u2014 must be valid JSON'}}return{success:!0,data:await this.api("POST",`/api/services/script/${s}`,r),display:`**Script executed:** \`script.${s}\``}}async getCalendarEvents(e,t,s){if(!e){let a=await this.api("GET","/api/calendars"),c=["## Calendars","","| Entity ID | Name |","|-----------|------|"];for(let d of a){let u=d.name??d.entity_id??"-";c.push(`| ${d.entity_id} | ${u} |`)}return a.length===0&&c.push("| - | No calendars found |"),{success:!0,data:a,display:c.join(`
659
682
  `)}}let r=t??new Date().toISOString(),n=s??new Date(Date.now()+864e5).toISOString(),o=await this.api("GET",`/api/calendars/${e}?start=${encodeURIComponent(r)}&end=${encodeURIComponent(n)}`),i=[`## Calendar Events: ${e}`,"","| Start | End | Summary | Location |","|-------|-----|---------|----------|"];for(let a of o){let c=a.start?.dateTime?new Date(a.start.dateTime).toLocaleString():a.start?.date??"-",d=a.end?.dateTime?new Date(a.end.dateTime).toLocaleString():a.end?.date??"-";i.push(`| ${c} | ${d} | ${a.summary??"-"} | ${a.location??"-"} |`)}return o.length===0&&i.push("| - | - | No events in range | - |"),{success:!0,data:o,display:i.join(`
660
683
  `)}}async getErrorLog(){let e=await this.apiText("GET","/api/error_log"),t=e.length>3e3?`\u2026${e.slice(-3e3)}`:e;return{success:!0,data:t,display:["## Error Log","","```",t,"```"].join(`
661
- `)}}async getBriefingSummary(e,t){let s=await this.api("GET","/api/states");if(e?.length){let d=new Set(e.map(p=>p.toLowerCase())),u=s.filter(p=>d.has(p.entity_id.toLowerCase()));return this.formatBriefingSummary(u)}if(t?.length){let d=new Set(t.map(p=>p.toLowerCase())),u=s.filter(p=>{let g=p.entity_id.split(".")[0];return d.has(g)});return this.formatBriefingSummary(u)}let r=[],n=[],o=[];for(let d of s){let u=d.entity_id,p=u.split(".")[0],g=d.state,f=d.attributes??{},h=f.device_class??"";if(p==="binary_sensor"){["door","window","opening","garage_door","lock","motion","occupancy","smoke","gas","moisture"].includes(h)&&g==="on"&&r.push(d);continue}if(p==="light"){if(g==="on"){let y=f.friendly_name??u;if(/^0x[a-f0-9]+$/i.test(y)||/\bLED\b/i.test(y))continue;r.push(d)}continue}if(p==="sensor"){if(g==="unavailable"||g==="unknown")continue;let E=`${u.toLowerCase()} ${(f.friendly_name??"").toLowerCase()}`;if(h==="battery"){n.push(d);continue}if(h==="power"||/power_consumption|stromverbrauch|leistung/.test(E)){o.push(d);continue}continue}if(p==="climate"){r.push(d);continue}if(p==="person"){r.push(d);continue}}n.sort((d,u)=>{let p=parseFloat(d.state)||999,g=parseFloat(u.state)||999;return p-g});let i=n.slice(0,5),a=o.filter(d=>!isNaN(parseFloat(d.state)));a.sort((d,u)=>{let p=Math.abs(parseFloat(d.state)||0);return Math.abs(parseFloat(u.state)||0)-p});let c=a.slice(0,5);return this.formatBriefingSummary(r,i,c)}formatBriefingSummary(e,t,s){let r=[...e,...t??[],...s??[]];if(r.length===0)return{success:!0,data:[],display:"Keine relevanten Smart-Home-Daten gefunden."};let n=new Map;for(let c of e){let d=c.entity_id.split(".")[0];n.has(d)||n.set(d,[]),n.get(d).push(c)}let o={binary_sensor:"Kontakte & Melder",light:"Lichter (an)",climate:"Klima",person:"Anwesenheit"},i=[];for(let[c,d]of n){let u=o[c]??c;i.push(`**${u}:**`);for(let p of d){let g=p.attributes?.friendly_name??p.entity_id,f=p.attributes?.unit_of_measurement??"",h=p.state;i.push(`- ${g}: ${h}${f?` ${f}`:""}`)}i.push("")}if(t&&t.length>0){let c=t.map(d=>`${(d.attributes?.friendly_name??d.entity_id).replace(/\s*(battery|akku|soc|ladezustand)\s*/gi," ").trim()}: ${d.state}%`);i.push(`**\u{1F50B} Akkus:** ${c.join(" | ")}`),i.push("")}if(s&&s.length>0){let c=s.map(d=>{let u=(d.attributes?.friendly_name??d.entity_id).replace(/\s*(power|leistung|stromverbrauch)\s*/gi," ").trim(),p=d.attributes?.unit_of_measurement??"W";return`${u}: ${d.state} ${p}`});i.push(`**\u26A1 Leistung:** ${c.join(" | ")}`),i.push("")}return{success:!0,data:r.map(c=>({entity_id:c.entity_id,state:c.state,friendly_name:c.attributes?.friendly_name,unit:c.attributes?.unit_of_measurement,device_class:c.attributes?.device_class})),display:i.join(`
662
- `).trim()}}async getEnergyStats(e,t){let{start:s,end:r,label:n}=this.parseEnergyPeriod(e??"today"),o;if(t?o=[t]:o=(await this.api("GET","/api/states")).filter(h=>{let y=h.attributes??{},E=y.state_class,$=y.device_class,v=(y.unit_of_measurement??"").toLowerCase();return h.entity_id.startsWith("sensor.")&&(E==="total_increasing"||E==="total")&&($==="energy"||v==="kwh"||v==="wh"||v==="mwh")}).map(h=>h.entity_id),o.length===0)return{success:!0,data:[],display:"Keine Energie-Sensoren gefunden (ben\xF6tigt state_class: total_increasing und device_class: energy)."};let i=[],a=[],c=o.join(","),d=`/api/history/period/${s}?end=${encodeURIComponent(r)}&filter_entity_id=${encodeURIComponent(c)}&minimal_response&no_attributes`,u;try{u=await this.api("GET",d)}catch{return this.getEnergyStatsViaTemplate(o,s,r,n)}for(let f of u){if(!f||f.length<2)continue;let h=f[0]?.entity_id;if(!h)continue;let y=null,E=null;for(let $ of f){let v=parseFloat($.state);isNaN(v)||(y===null&&(y=v),E=v)}if(y!==null&&E!==null){let $=E-y;$<0&&($=0);let v=await this.api("GET",`/api/states/${h}`).catch(()=>null),P=v?.attributes?.friendly_name??h,x=v?.attributes?.unit_of_measurement??"kWh",q=$,J=x;x.toLowerCase()==="wh"?(q=$/1e3,J="kWh"):x.toLowerCase()==="mwh"&&(q=$*1e3,J="kWh"),i.push({entityId:h,name:P,consumption:Math.round(q*100)/100,unit:J})}}if(i.length===0&&a.length===0)return{success:!0,data:[],display:`Keine Verbrauchsdaten f\xFCr Zeitraum "${n}" gefunden. M\xF6glicherweise liegt der Zeitraum au\xDFerhalb der History-Retention.`};let p=i.reduce((f,h)=>f+(h.unit==="kWh"?h.consumption:0),0),g=[`## Energieverbrauch: ${n}`,"","| Sensor | Verbrauch |","|--------|-----------|"];for(let f of i.sort((h,y)=>y.consumption-h.consumption))g.push(`| ${f.name} | ${f.consumption} ${f.unit} |`);return i.length>1&&g.push(`| **Gesamt** | **${Math.round(p*100)/100} kWh** |`),a.length>0&&g.push("",`Fehler bei: ${a.join(", ")}`),{success:!0,data:i,display:g.join(`
684
+ `)}}async getBriefingSummary(e,t){let s=await this.api("GET","/api/states");if(e?.length){let d=new Set(e.map(p=>p.toLowerCase())),u=s.filter(p=>d.has(p.entity_id.toLowerCase()));return this.formatBriefingSummary(u)}if(t?.length){let d=new Set(t.map(p=>p.toLowerCase())),u=s.filter(p=>{let f=p.entity_id.split(".")[0];return d.has(f)});return this.formatBriefingSummary(u)}let r=[],n=[],o=[];for(let d of s){let u=d.entity_id,p=u.split(".")[0],f=d.state,g=d.attributes??{},h=g.device_class??"";if(p==="binary_sensor"){["door","window","opening","garage_door","lock","motion","occupancy","smoke","gas","moisture"].includes(h)&&f==="on"&&r.push(d);continue}if(p==="light"){if(f==="on"){let y=g.friendly_name??u;if(/^0x[a-f0-9]+$/i.test(y)||/\bLED\b/i.test(y))continue;r.push(d)}continue}if(p==="sensor"){if(f==="unavailable"||f==="unknown")continue;let E=`${u.toLowerCase()} ${(g.friendly_name??"").toLowerCase()}`;if(h==="battery"){n.push(d);continue}if(h==="power"||/power_consumption|stromverbrauch|leistung/.test(E)){o.push(d);continue}continue}if(p==="climate"){r.push(d);continue}if(p==="person"){r.push(d);continue}}n.sort((d,u)=>{let p=parseFloat(d.state)||999,f=parseFloat(u.state)||999;return p-f});let i=n.slice(0,5),a=o.filter(d=>!isNaN(parseFloat(d.state)));a.sort((d,u)=>{let p=Math.abs(parseFloat(d.state)||0);return Math.abs(parseFloat(u.state)||0)-p});let c=a.slice(0,5);return this.formatBriefingSummary(r,i,c)}formatBriefingSummary(e,t,s){let r=[...e,...t??[],...s??[]];if(r.length===0)return{success:!0,data:[],display:"Keine relevanten Smart-Home-Daten gefunden."};let n=new Map;for(let c of e){let d=c.entity_id.split(".")[0];n.has(d)||n.set(d,[]),n.get(d).push(c)}let o={binary_sensor:"Kontakte & Melder",light:"Lichter (an)",climate:"Klima",person:"Anwesenheit"},i=[];for(let[c,d]of n){let u=o[c]??c;i.push(`**${u}:**`);for(let p of d){let f=p.attributes?.friendly_name??p.entity_id,g=p.attributes?.unit_of_measurement??"",h=p.state;i.push(`- ${f}: ${h}${g?` ${g}`:""}`)}i.push("")}if(t&&t.length>0){let c=t.map(d=>`${(d.attributes?.friendly_name??d.entity_id).replace(/\s*(battery|akku|soc|ladezustand)\s*/gi," ").trim()}: ${d.state}%`);i.push(`**\u{1F50B} Akkus:** ${c.join(" | ")}`),i.push("")}if(s&&s.length>0){let c=s.map(d=>{let u=(d.attributes?.friendly_name??d.entity_id).replace(/\s*(power|leistung|stromverbrauch)\s*/gi," ").trim(),p=d.attributes?.unit_of_measurement??"W";return`${u}: ${d.state} ${p}`});i.push(`**\u26A1 Leistung:** ${c.join(" | ")}`),i.push("")}return{success:!0,data:r.map(c=>({entity_id:c.entity_id,state:c.state,friendly_name:c.attributes?.friendly_name,unit:c.attributes?.unit_of_measurement,device_class:c.attributes?.device_class})),display:i.join(`
685
+ `).trim()}}async getEnergyStats(e,t){let{start:s,end:r,label:n}=this.parseEnergyPeriod(e??"today"),o;if(t?o=[t]:o=(await this.api("GET","/api/states")).filter(h=>{let y=h.attributes??{},E=y.state_class,$=y.device_class,v=(y.unit_of_measurement??"").toLowerCase();return h.entity_id.startsWith("sensor.")&&(E==="total_increasing"||E==="total")&&($==="energy"||v==="kwh"||v==="wh"||v==="mwh")}).map(h=>h.entity_id),o.length===0)return{success:!0,data:[],display:"Keine Energie-Sensoren gefunden (ben\xF6tigt state_class: total_increasing und device_class: energy)."};let i=[],a=[],c=o.join(","),d=`/api/history/period/${s}?end=${encodeURIComponent(r)}&filter_entity_id=${encodeURIComponent(c)}&minimal_response&no_attributes`,u;try{u=await this.api("GET",d)}catch{return this.getEnergyStatsViaTemplate(o,s,r,n)}for(let g of u){if(!g||g.length<2)continue;let h=g[0]?.entity_id;if(!h)continue;let y=null,E=null;for(let $ of g){let v=parseFloat($.state);isNaN(v)||(y===null&&(y=v),E=v)}if(y!==null&&E!==null){let $=E-y;$<0&&($=0);let v=await this.api("GET",`/api/states/${h}`).catch(()=>null),j=v?.attributes?.friendly_name??h,N=v?.attributes?.unit_of_measurement??"kWh",H=$,D=N;N.toLowerCase()==="wh"?(H=$/1e3,D="kWh"):N.toLowerCase()==="mwh"&&(H=$*1e3,D="kWh"),i.push({entityId:h,name:j,consumption:Math.round(H*100)/100,unit:D})}}if(i.length===0&&a.length===0)return{success:!0,data:[],display:`Keine Verbrauchsdaten f\xFCr Zeitraum "${n}" gefunden. M\xF6glicherweise liegt der Zeitraum au\xDFerhalb der History-Retention.`};let p=i.reduce((g,h)=>g+(h.unit==="kWh"?h.consumption:0),0),f=[`## Energieverbrauch: ${n}`,"","| Sensor | Verbrauch |","|--------|-----------|"];for(let g of i.sort((h,y)=>y.consumption-h.consumption))f.push(`| ${g.name} | ${g.consumption} ${g.unit} |`);return i.length>1&&f.push(`| **Gesamt** | **${Math.round(p*100)/100} kWh** |`),a.length>0&&f.push("",`Fehler bei: ${a.join(", ")}`),{success:!0,data:i,display:f.join(`
663
686
  `)}}async getEnergyStatsViaTemplate(e,t,s,r){let n=[`## Energie-Sensoren: ${r}`,"","*Hinweis: F\xFCr diesen Zeitraum sind keine History-Daten verf\xFCgbar. Zeige aktuelle Z\xE4hlerst\xE4nde.*","","| Sensor | Aktueller Stand | Einheit |","|--------|----------------|---------|"];for(let o of e)try{let i=await this.api("GET",`/api/states/${o}`),a=i.attributes?.friendly_name??o,c=i.attributes?.unit_of_measurement??"",d=i.state;n.push(`| ${a} | ${d} | ${c} |`)}catch{n.push(`| ${o} | Fehler | - |`)}return{success:!0,data:[],display:n.join(`
664
- `)}}parseEnergyPeriod(e){let t=new Date;switch(e.toLowerCase().replace(/\s+/g,"_")){case"today":case"heute":return{start:new Date(t.getFullYear(),t.getMonth(),t.getDate()).toISOString(),end:t.toISOString(),label:"Heute"};case"yesterday":case"gestern":{let s=new Date(t.getFullYear(),t.getMonth(),t.getDate()-1),r=new Date(t.getFullYear(),t.getMonth(),t.getDate());return{start:s.toISOString(),end:r.toISOString(),label:"Gestern"}}case"this_week":case"diese_woche":{let s=t.getDay(),r=s===0?-6:1-s;return{start:new Date(t.getFullYear(),t.getMonth(),t.getDate()+r).toISOString(),end:t.toISOString(),label:"Diese Woche"}}case"last_week":case"letzte_woche":{let s=t.getDay(),r=s===0?-6:1-s,n=new Date(t.getFullYear(),t.getMonth(),t.getDate()+r);return{start:new Date(n.getTime()-7*864e5).toISOString(),end:n.toISOString(),label:"Letzte Woche"}}case"this_month":case"dieser_monat":return{start:new Date(t.getFullYear(),t.getMonth(),1).toISOString(),end:t.toISOString(),label:"Dieser Monat"};case"last_month":case"letzter_monat":{let s=new Date(t.getFullYear(),t.getMonth()-1,1),r=new Date(t.getFullYear(),t.getMonth(),1);return{start:s.toISOString(),end:r.toISOString(),label:"Letzter Monat"}}default:return{start:ii(e),end:t.toISOString(),label:e}}}async createConfig(e,t,s){if(!t)return{success:!1,error:`Missing required "configId" for create_${e}`};if(!s)return{success:!1,error:`Missing required "configData" for create_${e}`};let r;try{r=JSON.parse(s)}catch{return{success:!1,error:'Invalid "configData" \u2014 must be valid JSON'}}return{success:!0,data:await this.apiPost(`/api/config/${e}/config/${t}`,r),display:`**${e} created:** \`${t}\`
687
+ `)}}parseEnergyPeriod(e){let t=new Date;switch(e.toLowerCase().replace(/\s+/g,"_")){case"today":case"heute":return{start:new Date(t.getFullYear(),t.getMonth(),t.getDate()).toISOString(),end:t.toISOString(),label:"Heute"};case"yesterday":case"gestern":{let s=new Date(t.getFullYear(),t.getMonth(),t.getDate()-1),r=new Date(t.getFullYear(),t.getMonth(),t.getDate());return{start:s.toISOString(),end:r.toISOString(),label:"Gestern"}}case"this_week":case"diese_woche":{let s=t.getDay(),r=s===0?-6:1-s;return{start:new Date(t.getFullYear(),t.getMonth(),t.getDate()+r).toISOString(),end:t.toISOString(),label:"Diese Woche"}}case"last_week":case"letzte_woche":{let s=t.getDay(),r=s===0?-6:1-s,n=new Date(t.getFullYear(),t.getMonth(),t.getDate()+r);return{start:new Date(n.getTime()-7*864e5).toISOString(),end:n.toISOString(),label:"Letzte Woche"}}case"this_month":case"dieser_monat":return{start:new Date(t.getFullYear(),t.getMonth(),1).toISOString(),end:t.toISOString(),label:"Dieser Monat"};case"last_month":case"letzter_monat":{let s=new Date(t.getFullYear(),t.getMonth()-1,1),r=new Date(t.getFullYear(),t.getMonth(),1);return{start:s.toISOString(),end:r.toISOString(),label:"Letzter Monat"}}default:return{start:ci(e),end:t.toISOString(),label:e}}}async createConfig(e,t,s){if(!t)return{success:!1,error:`Missing required "configId" for create_${e}`};if(!s)return{success:!1,error:`Missing required "configData" for create_${e}`};let r;try{r=JSON.parse(s)}catch{return{success:!1,error:'Invalid "configData" \u2014 must be valid JSON'}}return{success:!0,data:await this.apiPost(`/api/config/${e}/config/${t}`,r),display:`**${e} created:** \`${t}\`
665
688
 
666
- ${r.alias??r.name??t}`}}async deleteConfig(e,t){return t?(await this.apiDelete(`/api/config/${e}/config/${t}`),{success:!0,data:{deleted:t},display:`**${e} deleted:** \`${t}\``}):{success:!1,error:`Missing required "configId" for delete_${e}`}}async apiPost(e,t){let s=`${this.config.baseUrl.replace(/\/+$/,"")}${e}`,r={Authorization:`Bearer ${this.config.accessToken}`,"Content-Type":"application/json"},n=this.config.verifyTls===!1,o=process.env.NODE_TLS_REJECT_UNAUTHORIZED;n&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let i;try{i=await fetch(s,{method:"POST",headers:r,body:JSON.stringify(t),signal:AbortSignal.timeout(15e3)})}finally{n&&(o===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=o)}if(!i.ok){let a="";try{a=(await i.text()).slice(0,500)}catch{}throw new Error(`HTTP ${i.status} ${i.statusText} \u2014 ${a}`)}return await i.json()}async apiDelete(e){let t=`${this.config.baseUrl.replace(/\/+$/,"")}${e}`,s={Authorization:`Bearer ${this.config.accessToken}`},r=this.config.verifyTls===!1,n=process.env.NODE_TLS_REJECT_UNAUTHORIZED;r&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let o;try{o=await fetch(t,{method:"DELETE",headers:s,signal:AbortSignal.timeout(15e3)})}finally{r&&(n===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=n)}if(!o.ok){let i="";try{i=(await o.text()).slice(0,500)}catch{}throw new Error(`HTTP ${o.status} ${o.statusText} \u2014 ${i}`)}}}});var Fe,mr=_(()=>{"use strict";Fe=class{static{m(this,"ContactsProvider")}}});var nl={};ue(nl,{CardDAVContactsProvider:()=>Tn});var Tn,ai=_(()=>{"use strict";mr();Tn=class extends Fe{static{m(this,"CardDAVContactsProvider")}config;client;constructor(e){super(),this.config=e}async initialize(){try{let e=await import("tsdav"),{createDAVClient:t}=e;this.client=await t({serverUrl:this.config.serverUrl,credentials:{username:this.config.username,password:this.config.password},authMethod:"Basic",defaultAccountType:"carddav"})}catch(e){throw new Error(`CardDAV initialization failed: ${e instanceof Error?e.message:String(e)}`)}}async list(e,t){let s=await this.client.fetchAddressBooks();if(!s||s.length===0)return[];let r=[];for(let n of s){let o=await this.client.fetchVCards({addressBook:n});for(let i of o){let a=this.parseVCard(i.data,i.url);a&&r.push(a)}}return r.sort((n,o)=>n.displayName.localeCompare(o.displayName)),e?r.slice(0,e):r}async search(e){let t=await this.list(),s=e.toLowerCase();return t.filter(r=>r.displayName.toLowerCase().includes(s)||r.emails.some(n=>n.address.toLowerCase().includes(s))||r.phones.some(n=>n.number.includes(s)))}async get(e){return(await this.list()).find(s=>s.id===e)}async create(e){let t=await this.client.fetchAddressBooks();if(!t||t.length===0)throw new Error("No address books found");let s=`alfred-${Date.now()}@alfred`,r=this.buildVCard(s,e);await this.client.createVCard({addressBook:t[0],filename:`${s}.vcf`,vCardString:r});let n=e.displayName??([e.firstName,e.lastName].filter(Boolean).join(" ")||"Unknown");return{id:s,displayName:n,firstName:e.firstName,lastName:e.lastName,emails:e.emails??[],phones:e.phones??[],addresses:e.addresses??[],organization:e.organization,birthday:e.birthday,notes:e.notes}}async update(e,t){let s=await this.client.fetchAddressBooks();for(let r of s){let n=await this.client.fetchVCards({addressBook:r});for(let o of n)if(o.url?.includes(e)||o.data?.includes(e)){let i=this.parseVCard(o.data,o.url);if(!i)continue;let a={firstName:t.firstName??i.firstName,lastName:t.lastName??i.lastName,displayName:t.displayName??i.displayName,emails:t.emails??i.emails,phones:t.phones??i.phones,addresses:t.addresses??i.addresses,organization:t.organization??i.organization,birthday:t.birthday??i.birthday,notes:t.notes??i.notes},c=this.buildVCard(e,a);await this.client.updateVCard({vCard:{...o,data:c}});let d=a.displayName??([a.firstName,a.lastName].filter(Boolean).join(" ")||"Unknown");return{id:e,displayName:d,firstName:a.firstName,lastName:a.lastName,emails:a.emails??[],phones:a.phones??[],addresses:a.addresses??[],organization:a.organization,birthday:a.birthday,notes:a.notes}}}throw new Error(`Contact ${e} not found`)}async delete(e){let t=await this.client.fetchAddressBooks();for(let s of t){let r=await this.client.fetchVCards({addressBook:s});for(let n of r)if(n.url?.includes(e)||n.data?.includes(e)){await this.client.deleteVCard({vCard:n});return}}throw new Error(`Contact ${e} not found`)}parseVCard(e,t){let s=m(h=>e.match(new RegExp(`^${h}[;:](.*)$`,"mi"))?.[1]?.trim(),"get"),r=s("FN");if(!r)return;let n=s("N"),[o,i]=n?n.split(";"):[void 0,void 0],a=[];for(let h of e.matchAll(/^EMAIL[^:]*:(.+)$/gmi)){let y=h[0],E=h[1].trim(),$=y.match(/TYPE=([^;:,]+)/i)?.[1],v=/TYPE=pref/i.test(y);a.push({address:E,label:$,primary:v})}let c=[];for(let h of e.matchAll(/^TEL[^:]*:(.+)$/gmi)){let y=h[0],E=h[1].trim(),$=y.match(/TYPE=([^;:,]+)/i)?.[1],v=/TYPE=pref/i.test(y);c.push({number:E,label:$,primary:v})}let d=[];for(let h of e.matchAll(/^ADR[^:]*:(.+)$/gmi)){let y=h[1].split(";");d.push({street:y[2]?.trim()||void 0,city:y[3]?.trim()||void 0,region:y[4]?.trim()||void 0,postalCode:y[5]?.trim()||void 0,country:y[6]?.trim()||void 0})}let u=s("ORG")?.replace(/;.*$/,""),p=s("BDAY"),g=s("NOTE");return{id:s("UID")??t,displayName:r,firstName:i,lastName:o,emails:a,phones:c,addresses:d,organization:u,birthday:p,notes:g}}buildVCard(e,t){let s=t.displayName??([t.firstName,t.lastName].filter(Boolean).join(" ")||"Unknown"),r=`BEGIN:VCARD\r
689
+ ${r.alias??r.name??t}`}}async deleteConfig(e,t){return t?(await this.apiDelete(`/api/config/${e}/config/${t}`),{success:!0,data:{deleted:t},display:`**${e} deleted:** \`${t}\``}):{success:!1,error:`Missing required "configId" for delete_${e}`}}async apiPost(e,t){let s=`${this.config.baseUrl.replace(/\/+$/,"")}${e}`,r={Authorization:`Bearer ${this.config.accessToken}`,"Content-Type":"application/json"},n=this.config.verifyTls===!1,o=process.env.NODE_TLS_REJECT_UNAUTHORIZED;n&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let i;try{i=await fetch(s,{method:"POST",headers:r,body:JSON.stringify(t),signal:AbortSignal.timeout(15e3)})}finally{n&&(o===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=o)}if(!i.ok){let a="";try{a=(await i.text()).slice(0,500)}catch{}throw new Error(`HTTP ${i.status} ${i.statusText} \u2014 ${a}`)}return await i.json()}async apiDelete(e){let t=`${this.config.baseUrl.replace(/\/+$/,"")}${e}`,s={Authorization:`Bearer ${this.config.accessToken}`},r=this.config.verifyTls===!1,n=process.env.NODE_TLS_REJECT_UNAUTHORIZED;r&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");let o;try{o=await fetch(t,{method:"DELETE",headers:s,signal:AbortSignal.timeout(15e3)})}finally{r&&(n===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=n)}if(!o.ok){let i="";try{i=(await o.text()).slice(0,500)}catch{}throw new Error(`HTTP ${o.status} ${o.statusText} \u2014 ${i}`)}}}});var Be,gr=_(()=>{"use strict";Be=class{static{m(this,"ContactsProvider")}}});var cl={};le(cl,{CardDAVContactsProvider:()=>bn});var bn,li=_(()=>{"use strict";gr();bn=class extends Be{static{m(this,"CardDAVContactsProvider")}config;client;constructor(e){super(),this.config=e}async initialize(){try{let e=await import("tsdav"),{createDAVClient:t}=e;this.client=await t({serverUrl:this.config.serverUrl,credentials:{username:this.config.username,password:this.config.password},authMethod:"Basic",defaultAccountType:"carddav"})}catch(e){throw new Error(`CardDAV initialization failed: ${e instanceof Error?e.message:String(e)}`)}}async list(e,t){let s=await this.client.fetchAddressBooks();if(!s||s.length===0)return[];let r=[];for(let n of s){let o=await this.client.fetchVCards({addressBook:n});for(let i of o){let a=this.parseVCard(i.data,i.url);a&&r.push(a)}}return r.sort((n,o)=>n.displayName.localeCompare(o.displayName)),e?r.slice(0,e):r}async search(e){let t=await this.list(),s=e.toLowerCase();return t.filter(r=>r.displayName.toLowerCase().includes(s)||r.emails.some(n=>n.address.toLowerCase().includes(s))||r.phones.some(n=>n.number.includes(s)))}async get(e){return(await this.list()).find(s=>s.id===e)}async create(e){let t=await this.client.fetchAddressBooks();if(!t||t.length===0)throw new Error("No address books found");let s=`alfred-${Date.now()}@alfred`,r=this.buildVCard(s,e);await this.client.createVCard({addressBook:t[0],filename:`${s}.vcf`,vCardString:r});let n=e.displayName??([e.firstName,e.lastName].filter(Boolean).join(" ")||"Unknown");return{id:s,displayName:n,firstName:e.firstName,lastName:e.lastName,emails:e.emails??[],phones:e.phones??[],addresses:e.addresses??[],organization:e.organization,birthday:e.birthday,notes:e.notes}}async update(e,t){let s=await this.client.fetchAddressBooks();for(let r of s){let n=await this.client.fetchVCards({addressBook:r});for(let o of n)if(o.url?.includes(e)||o.data?.includes(e)){let i=this.parseVCard(o.data,o.url);if(!i)continue;let a={firstName:t.firstName??i.firstName,lastName:t.lastName??i.lastName,displayName:t.displayName??i.displayName,emails:t.emails??i.emails,phones:t.phones??i.phones,addresses:t.addresses??i.addresses,organization:t.organization??i.organization,birthday:t.birthday??i.birthday,notes:t.notes??i.notes},c=this.buildVCard(e,a);await this.client.updateVCard({vCard:{...o,data:c}});let d=a.displayName??([a.firstName,a.lastName].filter(Boolean).join(" ")||"Unknown");return{id:e,displayName:d,firstName:a.firstName,lastName:a.lastName,emails:a.emails??[],phones:a.phones??[],addresses:a.addresses??[],organization:a.organization,birthday:a.birthday,notes:a.notes}}}throw new Error(`Contact ${e} not found`)}async delete(e){let t=await this.client.fetchAddressBooks();for(let s of t){let r=await this.client.fetchVCards({addressBook:s});for(let n of r)if(n.url?.includes(e)||n.data?.includes(e)){await this.client.deleteVCard({vCard:n});return}}throw new Error(`Contact ${e} not found`)}parseVCard(e,t){let s=m(h=>e.match(new RegExp(`^${h}[;:](.*)$`,"mi"))?.[1]?.trim(),"get"),r=s("FN");if(!r)return;let n=s("N"),[o,i]=n?n.split(";"):[void 0,void 0],a=[];for(let h of e.matchAll(/^EMAIL[^:]*:(.+)$/gmi)){let y=h[0],E=h[1].trim(),$=y.match(/TYPE=([^;:,]+)/i)?.[1],v=/TYPE=pref/i.test(y);a.push({address:E,label:$,primary:v})}let c=[];for(let h of e.matchAll(/^TEL[^:]*:(.+)$/gmi)){let y=h[0],E=h[1].trim(),$=y.match(/TYPE=([^;:,]+)/i)?.[1],v=/TYPE=pref/i.test(y);c.push({number:E,label:$,primary:v})}let d=[];for(let h of e.matchAll(/^ADR[^:]*:(.+)$/gmi)){let y=h[1].split(";");d.push({street:y[2]?.trim()||void 0,city:y[3]?.trim()||void 0,region:y[4]?.trim()||void 0,postalCode:y[5]?.trim()||void 0,country:y[6]?.trim()||void 0})}let u=s("ORG")?.replace(/;.*$/,""),p=s("BDAY"),f=s("NOTE");return{id:s("UID")??t,displayName:r,firstName:i,lastName:o,emails:a,phones:c,addresses:d,organization:u,birthday:p,notes:f}}buildVCard(e,t){let s=t.displayName??([t.firstName,t.lastName].filter(Boolean).join(" ")||"Unknown"),r=`BEGIN:VCARD\r
667
690
  VERSION:3.0\r
668
691
  `;r+=`UID:${e}\r
669
692
  `,r+=`FN:${s}\r
@@ -675,110 +698,110 @@ VERSION:3.0\r
675
698
  `),t.birthday&&(r+=`BDAY:${t.birthday}\r
676
699
  `),t.notes&&(r+=`NOTE:${t.notes}\r
677
700
  `),r+=`END:VCARD\r
678
- `,r}}});var ol={};ue(ol,{GoogleContactsProvider:()=>bn});var _n,At,bn,ci=_(()=>{"use strict";mr();_n="names,emailAddresses,phoneNumbers,addresses,organizations,birthdays,biographies",At="https://people.googleapis.com/v1",bn=class extends Fe{static{m(this,"GoogleContactsProvider")}config;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){let e=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token"}),t=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:e.toString()});if(!t.ok)throw new Error(`Google token refresh failed: ${t.status}`);let s=await t.json();this.accessToken=s.access_token}async apiRequest(e,t,s){let r={method:e,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json"}};s&&(r.body=JSON.stringify(s));let n=await fetch(t,r);if(n.status===401){await this.refreshAccessToken();let o=await fetch(t,{...r,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json"}});if(!o.ok)throw new Error(`People API error: ${o.status}`);return o.status===204?void 0:o.json()}if(!n.ok)throw new Error(`People API error: ${n.status}`);if(n.status!==204)return n.json()}async list(e=50){let t=`${At}/people/me/connections?personFields=${_n}&pageSize=${e}`;return((await this.apiRequest("GET",t)).connections??[]).map(r=>this.mapPerson(r))}async search(e){let t=`${At}/people:searchContacts?query=${encodeURIComponent(e)}&readMask=${_n}&pageSize=30`;return((await this.apiRequest("GET",t)).results??[]).map(r=>this.mapPerson(r.person))}async get(e){try{let t=`${At}/${e}?personFields=${_n}`,s=await this.apiRequest("GET",t);return this.mapPerson(s)}catch{return}}async create(e){let t=this.buildPersonBody(e),s=await this.apiRequest("POST",`${At}/people:createContact`,t);return this.mapPerson(s)}async update(e,t){let s=await this.apiRequest("GET",`${At}/${e}?personFields=${_n}`),r=this.buildPersonBody(t);r.etag=s.etag;let o=`${At}/${e}:updateContact?updatePersonFields=names,emailAddresses,phoneNumbers,addresses,organizations,birthdays,biographies`,i=await this.apiRequest("PATCH",o,r);return this.mapPerson(i)}async delete(e){await this.apiRequest("DELETE",`${At}/${e}:deleteContact`)}mapPerson(e){let t=e.names?.[0],s=(e.emailAddresses??[]).map(c=>({address:c.value,label:c.type,primary:c.metadata?.primary??!1})),r=(e.phoneNumbers??[]).map(c=>({number:c.value,label:c.type,primary:c.metadata?.primary??!1})),n=(e.addresses??[]).map(c=>({street:c.streetAddress??void 0,city:c.city??void 0,region:c.region??void 0,postalCode:c.postalCode??void 0,country:c.country??void 0,label:c.type??void 0})),o=e.organizations?.[0]?.name,i=e.birthdays?.[0]?.date?`${e.birthdays[0].date.year??"????"}-${String(e.birthdays[0].date.month).padStart(2,"0")}-${String(e.birthdays[0].date.day).padStart(2,"0")}`:void 0,a=e.biographies?.[0]?.value;return{id:e.resourceName??e.etag??"",displayName:t?.displayName??"(No name)",firstName:t?.givenName,lastName:t?.familyName,emails:s,phones:r,addresses:n,organization:o,birthday:i,notes:a}}buildPersonBody(e){let t={};if((e.firstName!==void 0||e.lastName!==void 0||e.displayName!==void 0)&&(t.names=[{givenName:e.firstName,familyName:e.lastName,displayName:e.displayName}]),e.emails&&(t.emailAddresses=e.emails.map(s=>({value:s.address,type:s.label??"home"}))),e.phones&&(t.phoneNumbers=e.phones.map(s=>({value:s.number,type:s.label??"mobile"}))),e.addresses&&(t.addresses=e.addresses.map(s=>({streetAddress:s.street,city:s.city,region:s.region,postalCode:s.postalCode,country:s.country,type:s.label??"home"}))),e.organization&&(t.organizations=[{name:e.organization}]),e.birthday){let s=e.birthday.split("-");t.birthdays=[{date:{year:s[0]!=="????"?parseInt(s[0],10):void 0,month:parseInt(s[1],10),day:parseInt(s[2],10)}}]}return e.notes&&(t.biographies=[{value:e.notes,contentType:"TEXT_PLAIN"}]),t}}});var il={};ue(il,{MicrosoftContactsProvider:()=>En});var En,li=_(()=>{"use strict";mr();En=class extends Fe{static{m(this,"MicrosoftContactsProvider")}config;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Contacts.ReadWrite offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok)throw new Error(`Microsoft token refresh failed: ${s.status}`);let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(s.status===401){await this.refreshAccessToken();let r=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(!r.ok)throw new Error(`Graph API error: ${r.status}`);return r.status===204?void 0:r.json()}if(!s.ok)throw new Error(`Graph API error: ${s.status}`);if(s.status!==204)return s.json()}async list(e=50){return((await this.graphRequest(`/me/contacts?$top=${e}&$orderby=displayName`)).value??[]).map(s=>this.mapContact(s))}async search(e){try{return((await this.graphRequest(`/me/contacts?$search="${encodeURIComponent(e)}"`,{headers:{ConsistencyLevel:"eventual"}})).value??[]).map(s=>this.mapContact(s))}catch{return((await this.graphRequest(`/me/contacts?$filter=contains(displayName, '${encodeURIComponent(e)}')`)).value??[]).map(s=>this.mapContact(s))}}async get(e){try{let t=await this.graphRequest(`/me/contacts/${e}`);return this.mapContact(t)}catch{return}}async create(e){let t=this.buildContactBody(e),s=await this.graphRequest("/me/contacts",{method:"POST",body:JSON.stringify(t)});return this.mapContact(s)}async update(e,t){let s=this.buildContactBody(t),r=await this.graphRequest(`/me/contacts/${e}`,{method:"PATCH",body:JSON.stringify(s)});return this.mapContact(r)}async delete(e){await this.graphRequest(`/me/contacts/${e}`,{method:"DELETE"})}mapContact(e){let t=(e.emailAddresses??[]).map(o=>({address:o.address,label:o.name??void 0,primary:!1})),s=[];if(e.mobilePhone&&s.push({number:e.mobilePhone,label:"mobile"}),e.businessPhones)for(let o of e.businessPhones)s.push({number:o,label:"work"});if(e.homePhones)for(let o of e.homePhones)s.push({number:o,label:"home"});let r=[];e.homeAddress&&Object.values(e.homeAddress).some(Boolean)&&r.push({street:e.homeAddress.street??void 0,city:e.homeAddress.city??void 0,region:e.homeAddress.state??void 0,postalCode:e.homeAddress.postalCode??void 0,country:e.homeAddress.countryOrRegion??void 0,label:"home"}),e.businessAddress&&Object.values(e.businessAddress).some(Boolean)&&r.push({street:e.businessAddress.street??void 0,city:e.businessAddress.city??void 0,region:e.businessAddress.state??void 0,postalCode:e.businessAddress.postalCode??void 0,country:e.businessAddress.countryOrRegion??void 0,label:"work"});let n=e.birthday?e.birthday.slice(0,10):void 0;return{id:e.id,displayName:e.displayName??"(No name)",firstName:e.givenName??void 0,lastName:e.surname??void 0,emails:t,phones:s,addresses:r,organization:e.companyName??void 0,birthday:n,notes:e.personalNotes??void 0}}buildContactBody(e){let t={};if(e.firstName!==void 0&&(t.givenName=e.firstName),e.lastName!==void 0&&(t.surname=e.lastName),e.displayName!==void 0&&(t.displayName=e.displayName),e.emails&&(t.emailAddresses=e.emails.map(s=>({address:s.address,name:s.label??s.address}))),e.phones){let s=e.phones.find(i=>i.label==="mobile"),r=e.phones.filter(i=>i.label==="work"),n=e.phones.filter(i=>i.label==="home"),o=e.phones.filter(i=>!["mobile","work","home"].includes(i.label??""));s&&(t.mobilePhone=s.number),r.length>0&&(t.businessPhones=r.map(i=>i.number)),n.length>0&&(t.homePhones=n.map(i=>i.number)),o.length>0&&!r.length&&(t.businessPhones=o.map(i=>i.number))}if(e.addresses)for(let s of e.addresses){let r={street:s.street,city:s.city,state:s.region,postalCode:s.postalCode,countryOrRegion:s.country};s.label==="work"?t.businessAddress=r:t.homeAddress=r}return e.organization!==void 0&&(t.companyName=e.organization),e.birthday!==void 0&&(t.birthday=e.birthday),e.notes!==void 0&&(t.personalNotes=e.notes),t}}});async function di(l){switch(l.provider){case"carddav":{if(!l.carddav)throw new Error("CardDAV contacts config missing");let{CardDAVContactsProvider:e}=await Promise.resolve().then(()=>(ai(),nl)),t=new e(l.carddav);return await t.initialize(),t}case"google":{if(!l.google)throw new Error("Google contacts config missing");let{GoogleContactsProvider:e}=await Promise.resolve().then(()=>(ci(),ol)),t=new e(l.google);return await t.initialize(),t}case"microsoft":{if(!l.microsoft)throw new Error("Microsoft contacts config missing");let{MicrosoftContactsProvider:e}=await Promise.resolve().then(()=>(li(),il)),t=new e(l.microsoft);return await t.initialize(),t}default:throw new Error(`Unknown contacts provider: ${l.provider}`)}}var al=_(()=>{"use strict";m(di,"createContactsProvider")});var pr,cl=_(()=>{"use strict";U();pr=class extends R{static{m(this,"ContactsSkill")}contactsProvider;metadata={name:"contacts",category:"productivity",description:"Manage contacts. Search, view, create, update, or delete contacts.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["search","get","list","create","update","delete"],description:"The contacts action to perform"},query:{type:"string",description:"Search query (for search action)"},contactId:{type:"string",description:"Contact ID (for get/update/delete)"},firstName:{type:"string",description:"First name (for create/update)"},lastName:{type:"string",description:"Last name (for create/update)"},displayName:{type:"string",description:"Display name (for create/update)"},email:{type:"string",description:"Single email address (for create/update, shorthand)"},phone:{type:"string",description:"Single phone number (for create/update, shorthand)"},organization:{type:"string",description:"Organization / company (for create/update)"},birthday:{type:"string",description:"Birthday in YYYY-MM-DD format (for create/update)"},notes:{type:"string",description:"Notes (for create/update)"},emailAddresses:{type:"string",description:"JSON array of {address, label?, primary?} for multiple emails"},phoneNumbers:{type:"string",description:"JSON array of {number, label?, primary?} for multiple phones"},addresses:{type:"string",description:"JSON array of {street?, city?, region?, postalCode?, country?, label?}"},limit:{type:"number",description:"Maximum number of contacts to return (for list, default 50)"}},required:["action"]}};constructor(e){super(),this.contactsProvider=e}async execute(e,t){let s=e.action;switch(s){case"search":return this.searchContacts(e);case"get":return this.getContact(e);case"list":return this.listContacts(e);case"create":return this.createContact(e);case"update":return this.updateContact(e);case"delete":return this.deleteContact(e);default:return{success:!1,error:`Unknown action: "${String(s)}"`}}}async searchContacts(e){let t=e.query;if(!t)return{success:!1,error:'Missing required field "query"'};try{let s=await this.contactsProvider.search(t);if(s.length===0)return{success:!0,data:[],display:`No contacts found for "${t}".`};let r=this.formatTable(s);return{success:!0,data:s,display:`${s.length} contact(s) found:
701
+ `,r}}});var ll={};le(ll,{GoogleContactsProvider:()=>$n});var En,It,$n,di=_(()=>{"use strict";gr();En="names,emailAddresses,phoneNumbers,addresses,organizations,birthdays,biographies",It="https://people.googleapis.com/v1",$n=class extends Be{static{m(this,"GoogleContactsProvider")}config;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){let e=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token"}),t=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:e.toString()});if(!t.ok)throw new Error(`Google token refresh failed: ${t.status}`);let s=await t.json();this.accessToken=s.access_token}async apiRequest(e,t,s){let r={method:e,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json"}};s&&(r.body=JSON.stringify(s));let n=await fetch(t,r);if(n.status===401){await this.refreshAccessToken();let o=await fetch(t,{...r,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json"}});if(!o.ok)throw new Error(`People API error: ${o.status}`);return o.status===204?void 0:o.json()}if(!n.ok)throw new Error(`People API error: ${n.status}`);if(n.status!==204)return n.json()}async list(e=50){let t=`${It}/people/me/connections?personFields=${En}&pageSize=${e}`;return((await this.apiRequest("GET",t)).connections??[]).map(r=>this.mapPerson(r))}async search(e){let t=`${It}/people:searchContacts?query=${encodeURIComponent(e)}&readMask=${En}&pageSize=30`;return((await this.apiRequest("GET",t)).results??[]).map(r=>this.mapPerson(r.person))}async get(e){try{let t=`${It}/${e}?personFields=${En}`,s=await this.apiRequest("GET",t);return this.mapPerson(s)}catch{return}}async create(e){let t=this.buildPersonBody(e),s=await this.apiRequest("POST",`${It}/people:createContact`,t);return this.mapPerson(s)}async update(e,t){let s=await this.apiRequest("GET",`${It}/${e}?personFields=${En}`),r=this.buildPersonBody(t);r.etag=s.etag;let o=`${It}/${e}:updateContact?updatePersonFields=names,emailAddresses,phoneNumbers,addresses,organizations,birthdays,biographies`,i=await this.apiRequest("PATCH",o,r);return this.mapPerson(i)}async delete(e){await this.apiRequest("DELETE",`${It}/${e}:deleteContact`)}mapPerson(e){let t=e.names?.[0],s=(e.emailAddresses??[]).map(c=>({address:c.value,label:c.type,primary:c.metadata?.primary??!1})),r=(e.phoneNumbers??[]).map(c=>({number:c.value,label:c.type,primary:c.metadata?.primary??!1})),n=(e.addresses??[]).map(c=>({street:c.streetAddress??void 0,city:c.city??void 0,region:c.region??void 0,postalCode:c.postalCode??void 0,country:c.country??void 0,label:c.type??void 0})),o=e.organizations?.[0]?.name,i=e.birthdays?.[0]?.date?`${e.birthdays[0].date.year??"????"}-${String(e.birthdays[0].date.month).padStart(2,"0")}-${String(e.birthdays[0].date.day).padStart(2,"0")}`:void 0,a=e.biographies?.[0]?.value;return{id:e.resourceName??e.etag??"",displayName:t?.displayName??"(No name)",firstName:t?.givenName,lastName:t?.familyName,emails:s,phones:r,addresses:n,organization:o,birthday:i,notes:a}}buildPersonBody(e){let t={};if((e.firstName!==void 0||e.lastName!==void 0||e.displayName!==void 0)&&(t.names=[{givenName:e.firstName,familyName:e.lastName,displayName:e.displayName}]),e.emails&&(t.emailAddresses=e.emails.map(s=>({value:s.address,type:s.label??"home"}))),e.phones&&(t.phoneNumbers=e.phones.map(s=>({value:s.number,type:s.label??"mobile"}))),e.addresses&&(t.addresses=e.addresses.map(s=>({streetAddress:s.street,city:s.city,region:s.region,postalCode:s.postalCode,country:s.country,type:s.label??"home"}))),e.organization&&(t.organizations=[{name:e.organization}]),e.birthday){let s=e.birthday.split("-");t.birthdays=[{date:{year:s[0]!=="????"?parseInt(s[0],10):void 0,month:parseInt(s[1],10),day:parseInt(s[2],10)}}]}return e.notes&&(t.biographies=[{value:e.notes,contentType:"TEXT_PLAIN"}]),t}}});var dl={};le(dl,{MicrosoftContactsProvider:()=>kn});var kn,ui=_(()=>{"use strict";gr();kn=class extends Be{static{m(this,"MicrosoftContactsProvider")}config;accessToken="";constructor(e){super(),this.config=e}async initialize(){await this.refreshAccessToken()}async refreshAccessToken(){let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Contacts.ReadWrite offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok)throw new Error(`Microsoft token refresh failed: ${s.status}`);let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(s.status===401){await this.refreshAccessToken();let r=await fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}});if(!r.ok)throw new Error(`Graph API error: ${r.status}`);return r.status===204?void 0:r.json()}if(!s.ok)throw new Error(`Graph API error: ${s.status}`);if(s.status!==204)return s.json()}async list(e=50){return((await this.graphRequest(`/me/contacts?$top=${e}&$orderby=displayName`)).value??[]).map(s=>this.mapContact(s))}async search(e){try{return((await this.graphRequest(`/me/contacts?$search="${encodeURIComponent(e)}"`,{headers:{ConsistencyLevel:"eventual"}})).value??[]).map(s=>this.mapContact(s))}catch{return((await this.graphRequest(`/me/contacts?$filter=contains(displayName, '${encodeURIComponent(e)}')`)).value??[]).map(s=>this.mapContact(s))}}async get(e){try{let t=await this.graphRequest(`/me/contacts/${e}`);return this.mapContact(t)}catch{return}}async create(e){let t=this.buildContactBody(e),s=await this.graphRequest("/me/contacts",{method:"POST",body:JSON.stringify(t)});return this.mapContact(s)}async update(e,t){let s=this.buildContactBody(t),r=await this.graphRequest(`/me/contacts/${e}`,{method:"PATCH",body:JSON.stringify(s)});return this.mapContact(r)}async delete(e){await this.graphRequest(`/me/contacts/${e}`,{method:"DELETE"})}mapContact(e){let t=(e.emailAddresses??[]).map(o=>({address:o.address,label:o.name??void 0,primary:!1})),s=[];if(e.mobilePhone&&s.push({number:e.mobilePhone,label:"mobile"}),e.businessPhones)for(let o of e.businessPhones)s.push({number:o,label:"work"});if(e.homePhones)for(let o of e.homePhones)s.push({number:o,label:"home"});let r=[];e.homeAddress&&Object.values(e.homeAddress).some(Boolean)&&r.push({street:e.homeAddress.street??void 0,city:e.homeAddress.city??void 0,region:e.homeAddress.state??void 0,postalCode:e.homeAddress.postalCode??void 0,country:e.homeAddress.countryOrRegion??void 0,label:"home"}),e.businessAddress&&Object.values(e.businessAddress).some(Boolean)&&r.push({street:e.businessAddress.street??void 0,city:e.businessAddress.city??void 0,region:e.businessAddress.state??void 0,postalCode:e.businessAddress.postalCode??void 0,country:e.businessAddress.countryOrRegion??void 0,label:"work"});let n=e.birthday?e.birthday.slice(0,10):void 0;return{id:e.id,displayName:e.displayName??"(No name)",firstName:e.givenName??void 0,lastName:e.surname??void 0,emails:t,phones:s,addresses:r,organization:e.companyName??void 0,birthday:n,notes:e.personalNotes??void 0}}buildContactBody(e){let t={};if(e.firstName!==void 0&&(t.givenName=e.firstName),e.lastName!==void 0&&(t.surname=e.lastName),e.displayName!==void 0&&(t.displayName=e.displayName),e.emails&&(t.emailAddresses=e.emails.map(s=>({address:s.address,name:s.label??s.address}))),e.phones){let s=e.phones.find(i=>i.label==="mobile"),r=e.phones.filter(i=>i.label==="work"),n=e.phones.filter(i=>i.label==="home"),o=e.phones.filter(i=>!["mobile","work","home"].includes(i.label??""));s&&(t.mobilePhone=s.number),r.length>0&&(t.businessPhones=r.map(i=>i.number)),n.length>0&&(t.homePhones=n.map(i=>i.number)),o.length>0&&!r.length&&(t.businessPhones=o.map(i=>i.number))}if(e.addresses)for(let s of e.addresses){let r={street:s.street,city:s.city,state:s.region,postalCode:s.postalCode,countryOrRegion:s.country};s.label==="work"?t.businessAddress=r:t.homeAddress=r}return e.organization!==void 0&&(t.companyName=e.organization),e.birthday!==void 0&&(t.birthday=e.birthday),e.notes!==void 0&&(t.personalNotes=e.notes),t}}});async function mi(l){switch(l.provider){case"carddav":{if(!l.carddav)throw new Error("CardDAV contacts config missing");let{CardDAVContactsProvider:e}=await Promise.resolve().then(()=>(li(),cl)),t=new e(l.carddav);return await t.initialize(),t}case"google":{if(!l.google)throw new Error("Google contacts config missing");let{GoogleContactsProvider:e}=await Promise.resolve().then(()=>(di(),ll)),t=new e(l.google);return await t.initialize(),t}case"microsoft":{if(!l.microsoft)throw new Error("Microsoft contacts config missing");let{MicrosoftContactsProvider:e}=await Promise.resolve().then(()=>(ui(),dl)),t=new e(l.microsoft);return await t.initialize(),t}default:throw new Error(`Unknown contacts provider: ${l.provider}`)}}var ul=_(()=>{"use strict";m(mi,"createContactsProvider")});var yr,ml=_(()=>{"use strict";U();yr=class extends x{static{m(this,"ContactsSkill")}contactsProvider;metadata={name:"contacts",category:"productivity",description:"Manage contacts. Search, view, create, update, or delete contacts.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["search","get","list","create","update","delete"],description:"The contacts action to perform"},query:{type:"string",description:"Search query (for search action)"},contactId:{type:"string",description:"Contact ID (for get/update/delete)"},firstName:{type:"string",description:"First name (for create/update)"},lastName:{type:"string",description:"Last name (for create/update)"},displayName:{type:"string",description:"Display name (for create/update)"},email:{type:"string",description:"Single email address (for create/update, shorthand)"},phone:{type:"string",description:"Single phone number (for create/update, shorthand)"},organization:{type:"string",description:"Organization / company (for create/update)"},birthday:{type:"string",description:"Birthday in YYYY-MM-DD format (for create/update)"},notes:{type:"string",description:"Notes (for create/update)"},emailAddresses:{type:"string",description:"JSON array of {address, label?, primary?} for multiple emails"},phoneNumbers:{type:"string",description:"JSON array of {number, label?, primary?} for multiple phones"},addresses:{type:"string",description:"JSON array of {street?, city?, region?, postalCode?, country?, label?}"},limit:{type:"number",description:"Maximum number of contacts to return (for list, default 50)"}},required:["action"]}};constructor(e){super(),this.contactsProvider=e}async execute(e,t){let s=e.action;switch(s){case"search":return this.searchContacts(e);case"get":return this.getContact(e);case"list":return this.listContacts(e);case"create":return this.createContact(e);case"update":return this.updateContact(e);case"delete":return this.deleteContact(e);default:return{success:!1,error:`Unknown action: "${String(s)}"`}}}async searchContacts(e){let t=e.query;if(!t)return{success:!1,error:'Missing required field "query"'};try{let s=await this.contactsProvider.search(t);if(s.length===0)return{success:!0,data:[],display:`No contacts found for "${t}".`};let r=this.formatTable(s);return{success:!0,data:s,display:`${s.length} contact(s) found:
679
702
  ${r}`}}catch(s){return{success:!1,error:`Failed to search contacts: ${s instanceof Error?s.message:String(s)}`}}}async getContact(e){let t=e.contactId;if(!t)return{success:!1,error:'Missing required field "contactId"'};try{let s=await this.contactsProvider.get(t);if(!s)return{success:!1,error:`Contact "${t}" not found.`};let r=this.formatDetail(s);return{success:!0,data:s,display:r}}catch(s){return{success:!1,error:`Failed to get contact: ${s instanceof Error?s.message:String(s)}`}}}async listContacts(e){let t=e.limit??50;try{let s=await this.contactsProvider.list(t);if(s.length===0)return{success:!0,data:[],display:"No contacts found."};let r=this.formatTable(s);return{success:!0,data:s,display:`${s.length} contact(s):
680
703
  ${r}`}}catch(s){return{success:!1,error:`Failed to list contacts: ${s instanceof Error?s.message:String(s)}`}}}async createContact(e){try{let t=this.buildContactInput(e),s=await this.contactsProvider.create(t);return{success:!0,data:s,display:`Contact created: ${s.displayName}`}}catch(t){return{success:!1,error:`Failed to create contact: ${t instanceof Error?t.message:String(t)}`}}}async updateContact(e){let t=e.contactId;if(!t)return{success:!1,error:'Missing required field "contactId"'};try{let s=this.buildContactInput(e),r=await this.contactsProvider.update(t,s);return{success:!0,data:r,display:`Contact updated: ${r.displayName}`}}catch(s){return{success:!1,error:`Failed to update contact: ${s instanceof Error?s.message:String(s)}`}}}async deleteContact(e){let t=e.contactId;if(!t)return{success:!1,error:'Missing required field "contactId"'};try{return await this.contactsProvider.delete(t),{success:!0,data:{deleted:t},display:`Contact "${t}" deleted.`}}catch(s){return{success:!1,error:`Failed to delete contact: ${s instanceof Error?s.message:String(s)}`}}}buildContactInput(e){let t={};if(e.firstName&&(t.firstName=e.firstName),e.lastName&&(t.lastName=e.lastName),e.displayName&&(t.displayName=e.displayName),e.organization&&(t.organization=e.organization),e.birthday&&(t.birthday=e.birthday),e.notes&&(t.notes=e.notes),e.emailAddresses)try{t.emails=JSON.parse(e.emailAddresses)}catch{t.emails=[{address:e.emailAddresses}]}else e.email&&(t.emails=[{address:e.email,primary:!0}]);if(e.phoneNumbers)try{t.phones=JSON.parse(e.phoneNumbers)}catch{t.phones=[{number:e.phoneNumbers}]}else e.phone&&(t.phones=[{number:e.phone,primary:!0}]);if(e.addresses)try{t.addresses=JSON.parse(e.addresses)}catch{}return t}formatTable(e){let t=`| Name | Email | Phone |
681
704
  |------|-------|-------|`,s=e.map(r=>{let n=r.emails[0]?.address??"-",o=r.phones[0]?.number??"-";return`| ${r.displayName} | ${n} | ${o} |`});return`${t}
682
705
  ${s.join(`
683
706
  `)}`}formatDetail(e){let t=[];if(t.push(`**Name:** ${e.displayName}`),e.firstName&&t.push(`**First name:** ${e.firstName}`),e.lastName&&t.push(`**Last name:** ${e.lastName}`),e.emails.length>0&&t.push(`**Email(s):** ${e.emails.map(s=>`${s.address}${s.label?` (${s.label})`:""}`).join(", ")}`),e.phones.length>0&&t.push(`**Phone(s):** ${e.phones.map(s=>`${s.number}${s.label?` (${s.label})`:""}`).join(", ")}`),e.addresses.length>0)for(let s of e.addresses){let r=[s.street,s.city,s.region,s.postalCode,s.country].filter(Boolean);t.push(`**Address${s.label?` (${s.label})`:""}:** ${r.join(", ")}`)}return e.organization&&t.push(`**Organization:** ${e.organization}`),e.birthday&&t.push(`**Birthday:** ${e.birthday}`),e.notes&&t.push(`**Notes:** ${e.notes}`),t.push(`**ID:** ${e.id}`),t.join(`
684
- `)}}});var ll=_(()=>{"use strict";mr();ai();ci();li();al();cl()});var as,dl=_(()=>{"use strict";U();Me();as=class extends R{static{m(this,"TodoSkill")}todoRepo;metadata={name:"todo",category:"productivity",description:"Manage todo lists with multiple named lists. Actions: add, list, complete, uncomplete, delete, lists, clear.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["add","list","complete","uncomplete","delete","lists","clear"],description:"The todo action to perform"},title:{type:"string",description:"The todo title (required for add)"},list:{type:"string",description:'The list name (default: "default")'},description:{type:"string",description:"Optional description for the todo"},priority:{type:"string",enum:["low","normal","high","urgent"],description:'Priority level (default: "normal")'},dueDate:{type:"string",description:"Due date for the todo (ISO string)"},todoId:{type:"string",description:"The ID of the todo (required for complete, uncomplete, delete)"},includeCompleted:{type:"boolean",description:"Include completed todos in list output (default: false)"}},required:["action"]}};constructor(e){super(),this.todoRepo=e}async execute(e,t){let s=e.action;switch(s){case"add":return this.addTodo(e,t);case"list":return this.listTodos(e,t);case"complete":return this.completeTodo(e,t);case"uncomplete":return this.uncompleteTodo(e,t);case"delete":return this.deleteTodo(e,t);case"lists":return this.showLists(t);case"clear":return this.clearCompleted(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: add, list, complete, uncomplete, delete, lists, clear`}}}addTodo(e,t){let s=e.title;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "title" for add action'};let r=e.list??"default",n=e.description,o=e.priority,i=e.dueDate,a=this.todoRepo.add(ne(t),s,{list:r,description:n,priority:o,dueDate:i});return{success:!0,data:{todoId:a.id,title:a.title,list:a.list},display:`Todo added: "${s}"`}}listTodos(e,t){let s=e.list,r=e.includeCompleted??!1,n=new Set,o=[];for(let c of W(t))for(let d of this.todoRepo.list(c,s,r))n.has(d.id)||(n.add(d.id),o.push(d));if(o.length===0)return{success:!0,data:[],display:"No todos found."};let i=`| | Priority | Title | Due | ID |
707
+ `)}}});var pl=_(()=>{"use strict";gr();li();di();ui();ul();ml()});var ls,hl=_(()=>{"use strict";U();Pe();ls=class extends x{static{m(this,"TodoSkill")}todoRepo;metadata={name:"todo",category:"productivity",description:"Manage todo lists with multiple named lists. Actions: add, list, complete, uncomplete, delete, lists, clear.",riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["add","list","complete","uncomplete","delete","lists","clear"],description:"The todo action to perform"},title:{type:"string",description:"The todo title (required for add)"},list:{type:"string",description:'The list name (default: "default")'},description:{type:"string",description:"Optional description for the todo"},priority:{type:"string",enum:["low","normal","high","urgent"],description:'Priority level (default: "normal")'},dueDate:{type:"string",description:"Due date for the todo (ISO string)"},todoId:{type:"string",description:"The ID of the todo (required for complete, uncomplete, delete)"},includeCompleted:{type:"boolean",description:"Include completed todos in list output (default: false)"}},required:["action"]}};constructor(e){super(),this.todoRepo=e}async execute(e,t){let s=e.action;switch(s){case"add":return this.addTodo(e,t);case"list":return this.listTodos(e,t);case"complete":return this.completeTodo(e,t);case"uncomplete":return this.uncompleteTodo(e,t);case"delete":return this.deleteTodo(e,t);case"lists":return this.showLists(t);case"clear":return this.clearCompleted(e,t);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid actions: add, list, complete, uncomplete, delete, lists, clear`}}}addTodo(e,t){let s=e.title;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "title" for add action'};let r=e.list??"default",n=e.description,o=e.priority,i=e.dueDate,a=this.todoRepo.add(ne(t),s,{list:r,description:n,priority:o,dueDate:i});return{success:!0,data:{todoId:a.id,title:a.title,list:a.list},display:`Todo added: "${s}"`}}listTodos(e,t){let s=e.list,r=e.includeCompleted??!1,n=new Set,o=[];for(let c of G(t))for(let d of this.todoRepo.list(c,s,r))n.has(d.id)||(n.add(d.id),o.push(d));if(o.length===0)return{success:!0,data:[],display:"No todos found."};let i=`| | Priority | Title | Due | ID |
685
708
  |---|---|---|---|---|`,a=o.map(c=>{let d=c.completed?"\u2611":"\u2610",u=c.dueDate??"";return`| ${d} | ${c.priority} | ${c.title} | ${u} | ${c.id} |`}).join(`
686
709
  `);return{success:!0,data:o,display:`${o.length} todo(s):
687
710
  ${i}
688
- ${a}`}}completeTodo(e,t){let s=e.todoId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "todoId" for complete action'};let r=this.todoRepo.getById(s);return r?W(t).includes(r.userId)?this.todoRepo.complete(s)?{success:!0,data:{todoId:s},display:"Todo completed."}:{success:!1,error:`Todo "${s}" is already completed`}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}}uncompleteTodo(e,t){let s=e.todoId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "todoId" for uncomplete action'};let r=this.todoRepo.getById(s);return r?W(t).includes(r.userId)?this.todoRepo.uncomplete(s)?{success:!0,data:{todoId:s},display:"Todo reopened."}:{success:!1,error:`Todo "${s}" is not completed`}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}}deleteTodo(e,t){let s=e.todoId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "todoId" for delete action'};let r=this.todoRepo.getById(s);return r?W(t).includes(r.userId)?this.todoRepo.delete(s)?{success:!0,data:{todoId:s},display:"Todo deleted."}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}}showLists(e){let t=new Map;for(let o of W(e))for(let i of this.todoRepo.getLists(o)){let a=t.get(i.list);a?(a.open+=i.open,a.completed+=i.completed,a.total+=i.total):t.set(i.list,{open:i.open,completed:i.completed,total:i.total})}if(t.size===0)return{success:!0,data:[],display:"No todo lists found."};let s=[...t.entries()].map(([o,i])=>({list:o,...i})),r=`| List | Open | Completed | Total |
711
+ ${a}`}}completeTodo(e,t){let s=e.todoId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "todoId" for complete action'};let r=this.todoRepo.getById(s);return r?G(t).includes(r.userId)?this.todoRepo.complete(s)?{success:!0,data:{todoId:s},display:"Todo completed."}:{success:!1,error:`Todo "${s}" is already completed`}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}}uncompleteTodo(e,t){let s=e.todoId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "todoId" for uncomplete action'};let r=this.todoRepo.getById(s);return r?G(t).includes(r.userId)?this.todoRepo.uncomplete(s)?{success:!0,data:{todoId:s},display:"Todo reopened."}:{success:!1,error:`Todo "${s}" is not completed`}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}}deleteTodo(e,t){let s=e.todoId;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "todoId" for delete action'};let r=this.todoRepo.getById(s);return r?G(t).includes(r.userId)?this.todoRepo.delete(s)?{success:!0,data:{todoId:s},display:"Todo deleted."}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}:{success:!1,error:`Todo "${s}" not found`}}showLists(e){let t=new Map;for(let o of G(e))for(let i of this.todoRepo.getLists(o)){let a=t.get(i.list);a?(a.open+=i.open,a.completed+=i.completed,a.total+=i.total):t.set(i.list,{open:i.open,completed:i.completed,total:i.total})}if(t.size===0)return{success:!0,data:[],display:"No todo lists found."};let s=[...t.entries()].map(([o,i])=>({list:o,...i})),r=`| List | Open | Completed | Total |
689
712
  |---|---|---|---|`,n=s.map(o=>`| ${o.list} | ${o.open} | ${o.completed} | ${o.total} |`).join(`
690
713
  `);return{success:!0,data:s,display:`${s.length} list(s):
691
714
  ${r}
692
- ${n}`}}clearCompleted(e,t){let s=e.list,r=this.todoRepo.clearCompleted(ne(t),s);return{success:!0,data:{cleared:r},display:`Cleared ${r} completed todo(s).`}}}});import ul from"node:http";import ml from"node:https";import{execFile as Bm}from"node:child_process";import{promisify as Hm}from"node:util";function ui(l){return l==null||l<0?"-":l===0?"0 B":l<1024?`${l} B`:l<1024**2?`${(l/1024).toFixed(1)} KiB`:l<1024**3?`${(l/1024**2).toFixed(1)} MiB`:`${(l/1024**3).toFixed(2)} GiB`}function hl(l){return!l||l.length===0?"-":l.filter(e=>e.PublicPort||e.PrivatePort).map(e=>e.PublicPort?`${e.IP??"0.0.0.0"}:${e.PublicPort}->${e.PrivatePort}/${e.Type??"tcp"}`:`${e.PrivatePort}/${e.Type??"tcp"}`).join(", ")}function mi(l){return!l||l.length===0?"-":l.map(e=>e.replace(/^\//,"")).join(", ")}function qm(l){let e=Buffer.from(l,"binary"),t=[],s=0;for(;s<e.length;)if(s+8<=e.length){let r=e.readUInt32BE(s+4),n=e.subarray(s+8,s+8+r).toString("utf8");t.push(n),s+=8+r}else{t.push(e.subarray(s).toString("utf8"));break}return t.join("")}function zm(l){return l?new Date(l*1e3).toISOString().replace("T"," ").slice(0,19):"-"}var pl,$n,fl=_(()=>{"use strict";U();pl=Hm(Bm);m(ui,"formatBytes");m(hl,"formatPorts");m(mi,"containerName");m(qm,"stripDockerStreamHeaders");m(zm,"relativeTime");$n=class extends R{static{m(this,"DockerSkill")}metadata={name:"docker",category:"infrastructure",description:"Manage Docker containers, images, volumes, networks. Actions: containers, container, logs, start, stop, restart, images, pull_image, remove_image, networks, volumes, system_info, prune, compose_ps, compose_up, compose_down.",riskLevel:"admin",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["containers","container","logs","start","stop","restart","images","pull_image","remove_image","networks","volumes","system_info","prune","compose_ps","compose_up","compose_down"],description:"The Docker action to perform"},containerId:{type:"string",description:"Container ID or name (for container, logs, start, stop, restart)"},imageName:{type:"string",description:"Image name (for pull_image, remove_image), e.g. nginx, ghcr.io/org/app"},imageTag:{type:"string",description:"Image tag (for pull_image), default: latest"},networkId:{type:"string",description:"Network ID or name"},project:{type:"string",description:"Docker Compose project name (for compose_ps, compose_up, compose_down)"},tail:{type:"number",description:"Number of log lines to retrieve (default: 100)"}},required:["action"]}};config;defaultSocket;constructor(e){super(),this.config=e,this.defaultSocket=process.platform==="win32"?"//./pipe/docker_engine":"/var/run/docker.sock"}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"containers":return await this.listContainers();case"container":return await this.inspectContainer(e.containerId);case"logs":return await this.getLogs(e.containerId,e.tail);case"start":return await this.startContainer(e.containerId);case"stop":return await this.stopContainer(e.containerId);case"restart":return await this.restartContainer(e.containerId);case"images":return await this.listImages();case"pull_image":return await this.pullImage(e.imageName,e.imageTag);case"remove_image":return await this.removeImage(e.imageName);case"networks":return await this.listNetworks();case"volumes":return await this.listVolumes();case"system_info":return await this.getSystemInfo();case"prune":return await this.pruneAll();case"compose_ps":return await this.composePs(e.project);case"compose_up":return await this.composeUp(e.project);case"compose_down":return await this.composeDown(e.project);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Docker API error: ${r instanceof Error?r.message:String(r)}`}}}api(e,t,s){return new Promise((r,n)=>{let o={method:e,path:`/v1.45${t}`,headers:{"Content-Type":"application/json"}};if(this.config.host){let c=new URL(this.config.host);o.hostname=c.hostname,o.port=c.port}else o.socketPath=this.config.socketPath??this.defaultSocket;let a=(this.config.host?.startsWith("https")?ml:ul).request(o,c=>{let d="";c.on("data",u=>{d+=u}),c.on("end",()=>{if(c.statusCode&&c.statusCode>=400){n(new Error(`Docker API ${c.statusCode}: ${d.slice(0,500)}`));return}try{r(d?JSON.parse(d):void 0)}catch{n(new Error(`Invalid JSON from Docker API: ${d.slice(0,200)}`))}})});a.on("error",n),a.setTimeout(3e4,()=>{a.destroy(),n(new Error("Docker API timeout"))}),s&&a.write(JSON.stringify(s)),a.end()})}apiRaw(e,t,s){return new Promise((r,n)=>{let o={method:e,path:`/v1.45${t}`,headers:{"Content-Type":"application/json"}};if(this.config.host){let c=new URL(this.config.host);o.hostname=c.hostname,o.port=c.port}else o.socketPath=this.config.socketPath??this.defaultSocket;let a=(this.config.host?.startsWith("https")?ml:ul).request(o,c=>{let d=[];c.on("data",u=>{d.push(u)}),c.on("end",()=>{if(c.statusCode&&c.statusCode>=400){let u=Buffer.concat(d).toString("utf8");n(new Error(`Docker API ${c.statusCode}: ${u.slice(0,500)}`));return}r(Buffer.concat(d).toString("binary"))})});a.on("error",n),a.setTimeout(3e4,()=>{a.destroy(),n(new Error("Docker API timeout"))}),s&&a.write(JSON.stringify(s)),a.end()})}async listContainers(){let e=await this.api("GET","/containers/json?all=true"),t=["## Docker Containers","","| Name | Image | Status | Ports |","|------|-------|--------|-------|"];for(let s of e)t.push(`| ${mi(s.Names)} | ${s.Image??"-"} | ${s.Status??s.State??"-"} | ${hl(s.Ports)} |`);return e.length===0&&t.push("| - | No containers found | - | - |"),{success:!0,data:e,display:t.join(`
693
- `)}}async inspectContainer(e){if(!e)return{success:!1,error:'Missing required "containerId" parameter'};let t=await this.api("GET",`/containers/${encodeURIComponent(e)}/json`),s=t.State??{},r=t.Config??{},n=t.NetworkSettings??{},o=t.Mounts??[],a=Object.entries(n.Ports??{}).filter(([,p])=>p&&p.length>0).map(([p,g])=>`${g.map(h=>`${h.HostIp??"0.0.0.0"}:${h.HostPort}`).join(", ")} -> ${p}`),c=o.map(p=>`- ${p.Source??"-"} -> ${p.Destination??"-"} (${p.Type??"-"}, ${p.RW?"rw":"ro"})`),d=n.IPAddress||Object.values(n.Networks??{}).map(p=>p.IPAddress).filter(Boolean).join(", ")||"-",u=[`## Container: ${mi(t.Name?[t.Name]:void 0)}`,"",`**ID:** ${t.Id?.slice(0,12)??"-"}`,`**Image:** ${r.Image??"-"}`,`**Status:** ${s.Status??"-"}`,`**Started:** ${s.StartedAt??"-"}`,`**IP Address:** ${d}`,"","### Ports",a.length>0?a.map(p=>`- ${p}`).join(`
715
+ ${n}`}}clearCompleted(e,t){let s=e.list,r=this.todoRepo.clearCompleted(ne(t),s);return{success:!0,data:{cleared:r},display:`Cleared ${r} completed todo(s).`}}}});import fl from"node:http";import gl from"node:https";import{execFile as Wm}from"node:child_process";import{promisify as Gm}from"node:util";function pi(l){return l==null||l<0?"-":l===0?"0 B":l<1024?`${l} B`:l<1024**2?`${(l/1024).toFixed(1)} KiB`:l<1024**3?`${(l/1024**2).toFixed(1)} MiB`:`${(l/1024**3).toFixed(2)} GiB`}function wl(l){return!l||l.length===0?"-":l.filter(e=>e.PublicPort||e.PrivatePort).map(e=>e.PublicPort?`${e.IP??"0.0.0.0"}:${e.PublicPort}->${e.PrivatePort}/${e.Type??"tcp"}`:`${e.PrivatePort}/${e.Type??"tcp"}`).join(", ")}function hi(l){return!l||l.length===0?"-":l.map(e=>e.replace(/^\//,"")).join(", ")}function Vm(l){let e=Buffer.from(l,"binary"),t=[],s=0;for(;s<e.length;)if(s+8<=e.length){let r=e.readUInt32BE(s+4),n=e.subarray(s+8,s+8+r).toString("utf8");t.push(n),s+=8+r}else{t.push(e.subarray(s).toString("utf8"));break}return t.join("")}function Km(l){return l?new Date(l*1e3).toISOString().replace("T"," ").slice(0,19):"-"}var yl,vn,Tl=_(()=>{"use strict";U();yl=Gm(Wm);m(pi,"formatBytes");m(wl,"formatPorts");m(hi,"containerName");m(Vm,"stripDockerStreamHeaders");m(Km,"relativeTime");vn=class extends x{static{m(this,"DockerSkill")}metadata={name:"docker",category:"infrastructure",description:"Manage Docker containers, images, volumes, networks. Actions: containers, container, logs, start, stop, restart, images, pull_image, remove_image, networks, volumes, system_info, prune, compose_ps, compose_up, compose_down.",riskLevel:"admin",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["containers","container","logs","start","stop","restart","images","pull_image","remove_image","networks","volumes","system_info","prune","compose_ps","compose_up","compose_down"],description:"The Docker action to perform"},containerId:{type:"string",description:"Container ID or name (for container, logs, start, stop, restart)"},imageName:{type:"string",description:"Image name (for pull_image, remove_image), e.g. nginx, ghcr.io/org/app"},imageTag:{type:"string",description:"Image tag (for pull_image), default: latest"},networkId:{type:"string",description:"Network ID or name"},project:{type:"string",description:"Docker Compose project name (for compose_ps, compose_up, compose_down)"},tail:{type:"number",description:"Number of log lines to retrieve (default: 100)"}},required:["action"]}};config;defaultSocket;constructor(e){super(),this.config=e,this.defaultSocket=process.platform==="win32"?"//./pipe/docker_engine":"/var/run/docker.sock"}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"containers":return await this.listContainers();case"container":return await this.inspectContainer(e.containerId);case"logs":return await this.getLogs(e.containerId,e.tail);case"start":return await this.startContainer(e.containerId);case"stop":return await this.stopContainer(e.containerId);case"restart":return await this.restartContainer(e.containerId);case"images":return await this.listImages();case"pull_image":return await this.pullImage(e.imageName,e.imageTag);case"remove_image":return await this.removeImage(e.imageName);case"networks":return await this.listNetworks();case"volumes":return await this.listVolumes();case"system_info":return await this.getSystemInfo();case"prune":return await this.pruneAll();case"compose_ps":return await this.composePs(e.project);case"compose_up":return await this.composeUp(e.project);case"compose_down":return await this.composeDown(e.project);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Docker API error: ${r instanceof Error?r.message:String(r)}`}}}api(e,t,s){return new Promise((r,n)=>{let o={method:e,path:`/v1.45${t}`,headers:{"Content-Type":"application/json"}};if(this.config.host){let c=new URL(this.config.host);o.hostname=c.hostname,o.port=c.port}else o.socketPath=this.config.socketPath??this.defaultSocket;let a=(this.config.host?.startsWith("https")?gl:fl).request(o,c=>{let d="";c.on("data",u=>{d+=u}),c.on("end",()=>{if(c.statusCode&&c.statusCode>=400){n(new Error(`Docker API ${c.statusCode}: ${d.slice(0,500)}`));return}try{r(d?JSON.parse(d):void 0)}catch{n(new Error(`Invalid JSON from Docker API: ${d.slice(0,200)}`))}})});a.on("error",n),a.setTimeout(3e4,()=>{a.destroy(),n(new Error("Docker API timeout"))}),s&&a.write(JSON.stringify(s)),a.end()})}apiRaw(e,t,s){return new Promise((r,n)=>{let o={method:e,path:`/v1.45${t}`,headers:{"Content-Type":"application/json"}};if(this.config.host){let c=new URL(this.config.host);o.hostname=c.hostname,o.port=c.port}else o.socketPath=this.config.socketPath??this.defaultSocket;let a=(this.config.host?.startsWith("https")?gl:fl).request(o,c=>{let d=[];c.on("data",u=>{d.push(u)}),c.on("end",()=>{if(c.statusCode&&c.statusCode>=400){let u=Buffer.concat(d).toString("utf8");n(new Error(`Docker API ${c.statusCode}: ${u.slice(0,500)}`));return}r(Buffer.concat(d).toString("binary"))})});a.on("error",n),a.setTimeout(3e4,()=>{a.destroy(),n(new Error("Docker API timeout"))}),s&&a.write(JSON.stringify(s)),a.end()})}async listContainers(){let e=await this.api("GET","/containers/json?all=true"),t=["## Docker Containers","","| Name | Image | Status | Ports |","|------|-------|--------|-------|"];for(let s of e)t.push(`| ${hi(s.Names)} | ${s.Image??"-"} | ${s.Status??s.State??"-"} | ${wl(s.Ports)} |`);return e.length===0&&t.push("| - | No containers found | - | - |"),{success:!0,data:e,display:t.join(`
716
+ `)}}async inspectContainer(e){if(!e)return{success:!1,error:'Missing required "containerId" parameter'};let t=await this.api("GET",`/containers/${encodeURIComponent(e)}/json`),s=t.State??{},r=t.Config??{},n=t.NetworkSettings??{},o=t.Mounts??[],a=Object.entries(n.Ports??{}).filter(([,p])=>p&&p.length>0).map(([p,f])=>`${f.map(h=>`${h.HostIp??"0.0.0.0"}:${h.HostPort}`).join(", ")} -> ${p}`),c=o.map(p=>`- ${p.Source??"-"} -> ${p.Destination??"-"} (${p.Type??"-"}, ${p.RW?"rw":"ro"})`),d=n.IPAddress||Object.values(n.Networks??{}).map(p=>p.IPAddress).filter(Boolean).join(", ")||"-",u=[`## Container: ${hi(t.Name?[t.Name]:void 0)}`,"",`**ID:** ${t.Id?.slice(0,12)??"-"}`,`**Image:** ${r.Image??"-"}`,`**Status:** ${s.Status??"-"}`,`**Started:** ${s.StartedAt??"-"}`,`**IP Address:** ${d}`,"","### Ports",a.length>0?a.map(p=>`- ${p}`).join(`
694
717
  `):"- No port bindings","","### Mounts",c.length>0?c.join(`
695
718
  `):"- No mounts"];return{success:!0,data:t,display:u.join(`
696
- `)}}async getLogs(e,t){if(!e)return{success:!1,error:'Missing required "containerId" parameter'};let s=t??100,r=await this.apiRaw("GET",`/containers/${encodeURIComponent(e)}/logs?stdout=1&stderr=1&tail=${s}`),n=qm(r),o=[`## Logs: ${e} (last ${s} lines)`,"","```",n.trimEnd(),"```"];return{success:!0,data:n,display:o.join(`
697
- `)}}async startContainer(e){return e?(await this.api("POST",`/containers/${encodeURIComponent(e)}/start`),{success:!0,data:{containerId:e,action:"start"},display:`**Container started:** \`${e}\``}):{success:!1,error:'Missing required "containerId" parameter'}}async stopContainer(e){return e?(await this.api("POST",`/containers/${encodeURIComponent(e)}/stop`),{success:!0,data:{containerId:e,action:"stop"},display:`**Container stopped:** \`${e}\``}):{success:!1,error:'Missing required "containerId" parameter'}}async restartContainer(e){return e?(await this.api("POST",`/containers/${encodeURIComponent(e)}/restart`),{success:!0,data:{containerId:e,action:"restart"},display:`**Container restarted:** \`${e}\``}):{success:!1,error:'Missing required "containerId" parameter'}}async listImages(){let e=await this.api("GET","/images/json"),t=["## Docker Images","","| Repo:Tag | Size | Created |","|----------|------|---------|"];for(let s of e){let n=(s.RepoTags??["<none>:<none>"]).join(", "),o=ui(s.Size),i=zm(s.Created);t.push(`| ${n} | ${o} | ${i} |`)}return e.length===0&&t.push("| - | No images found | - |"),{success:!0,data:e,display:t.join(`
719
+ `)}}async getLogs(e,t){if(!e)return{success:!1,error:'Missing required "containerId" parameter'};let s=t??100,r=await this.apiRaw("GET",`/containers/${encodeURIComponent(e)}/logs?stdout=1&stderr=1&tail=${s}`),n=Vm(r),o=[`## Logs: ${e} (last ${s} lines)`,"","```",n.trimEnd(),"```"];return{success:!0,data:n,display:o.join(`
720
+ `)}}async startContainer(e){return e?(await this.api("POST",`/containers/${encodeURIComponent(e)}/start`),{success:!0,data:{containerId:e,action:"start"},display:`**Container started:** \`${e}\``}):{success:!1,error:'Missing required "containerId" parameter'}}async stopContainer(e){return e?(await this.api("POST",`/containers/${encodeURIComponent(e)}/stop`),{success:!0,data:{containerId:e,action:"stop"},display:`**Container stopped:** \`${e}\``}):{success:!1,error:'Missing required "containerId" parameter'}}async restartContainer(e){return e?(await this.api("POST",`/containers/${encodeURIComponent(e)}/restart`),{success:!0,data:{containerId:e,action:"restart"},display:`**Container restarted:** \`${e}\``}):{success:!1,error:'Missing required "containerId" parameter'}}async listImages(){let e=await this.api("GET","/images/json"),t=["## Docker Images","","| Repo:Tag | Size | Created |","|----------|------|---------|"];for(let s of e){let n=(s.RepoTags??["<none>:<none>"]).join(", "),o=pi(s.Size),i=Km(s.Created);t.push(`| ${n} | ${o} | ${i} |`)}return e.length===0&&t.push("| - | No images found | - |"),{success:!0,data:e,display:t.join(`
698
721
  `)}}async pullImage(e,t){if(!e)return{success:!1,error:'Missing required "imageName" parameter'};let s=t??"latest",r=await this.apiRaw("POST",`/images/create?fromImage=${encodeURIComponent(e)}&tag=${encodeURIComponent(s)}`);return{success:!0,data:{imageName:e,tag:s,raw:r.slice(0,500)},display:`**Image pulled:** \`${e}:${s}\``}}async removeImage(e){if(!e)return{success:!1,error:'Missing required "imageName" parameter'};let t=await this.api("DELETE",`/images/${encodeURIComponent(e)}`),s=(t??[]).filter(o=>o.Deleted).map(o=>o.Deleted),r=(t??[]).filter(o=>o.Untagged).map(o=>o.Untagged),n=[`**Image removed:** \`${e}\``,"",`**Untagged:** ${r.length>0?r.join(", "):"-"}`,`**Deleted layers:** ${s.length}`];return{success:!0,data:t,display:n.join(`
699
722
  `)}}async listNetworks(){let e=await this.api("GET","/networks"),t=["## Docker Networks","","| Name | Driver | Scope | Subnet |","|------|--------|-------|--------|"];for(let s of e){let n=(s.IPAM?.Config??[]).map(o=>o.Subnet).filter(Boolean).join(", ")||"-";t.push(`| ${s.Name??"-"} | ${s.Driver??"-"} | ${s.Scope??"-"} | ${n} |`)}return e.length===0&&t.push("| - | No networks found | - | - |"),{success:!0,data:e,display:t.join(`
700
723
  `)}}async listVolumes(){let t=(await this.api("GET","/volumes")).Volumes??[],s=["## Docker Volumes","","| Name | Driver | Mountpoint |","|------|--------|------------|"];for(let r of t)s.push(`| ${r.Name??"-"} | ${r.Driver??"-"} | ${r.Mountpoint??"-"} |`);return t.length===0&&s.push("| - | No volumes found | - |"),{success:!0,data:t,display:s.join(`
701
- `)}}async getSystemInfo(){let e=await this.api("GET","/info"),t=["## Docker System Info","",`**Docker Version:** ${e.ServerVersion??"-"}`,`**OS:** ${e.OperatingSystem??"-"} (${e.Architecture??"-"})`,`**Kernel:** ${e.KernelVersion??"-"}`,`**CPUs:** ${e.NCPU??"-"}`,`**Memory:** ${ui(e.MemTotal)}`,`**Containers:** ${e.Containers??"-"} (running: ${e.ContainersRunning??"-"}, paused: ${e.ContainersPaused??"-"}, stopped: ${e.ContainersStopped??"-"})`,`**Images:** ${e.Images??"-"}`,`**Storage Driver:** ${e.Driver??"-"}`,`**Docker Root Dir:** ${e.DockerRootDir??"-"}`];return{success:!0,data:e,display:t.join(`
702
- `)}}async pruneAll(){let[e,t,s,r]=await Promise.all([this.api("POST","/containers/prune"),this.api("POST","/images/prune"),this.api("POST","/volumes/prune"),this.api("POST","/networks/prune")]),n=e.ContainersDeleted?.length??0,o=t.ImagesDeleted?.length??0,i=s.VolumesDeleted?.length??0,a=r.NetworksDeleted?.length??0,c=(e.SpaceReclaimed??0)+(t.SpaceReclaimed??0)+(s.SpaceReclaimed??0),d=["## Docker Prune Results","",`**Containers removed:** ${n}`,`**Images removed:** ${o}`,`**Volumes removed:** ${i}`,`**Networks removed:** ${a}`,"",`**Total space reclaimed:** ${ui(c)}`];return{success:!0,data:{containers:e,images:t,volumes:s,networks:r},display:d.join(`
724
+ `)}}async getSystemInfo(){let e=await this.api("GET","/info"),t=["## Docker System Info","",`**Docker Version:** ${e.ServerVersion??"-"}`,`**OS:** ${e.OperatingSystem??"-"} (${e.Architecture??"-"})`,`**Kernel:** ${e.KernelVersion??"-"}`,`**CPUs:** ${e.NCPU??"-"}`,`**Memory:** ${pi(e.MemTotal)}`,`**Containers:** ${e.Containers??"-"} (running: ${e.ContainersRunning??"-"}, paused: ${e.ContainersPaused??"-"}, stopped: ${e.ContainersStopped??"-"})`,`**Images:** ${e.Images??"-"}`,`**Storage Driver:** ${e.Driver??"-"}`,`**Docker Root Dir:** ${e.DockerRootDir??"-"}`];return{success:!0,data:e,display:t.join(`
725
+ `)}}async pruneAll(){let[e,t,s,r]=await Promise.all([this.api("POST","/containers/prune"),this.api("POST","/images/prune"),this.api("POST","/volumes/prune"),this.api("POST","/networks/prune")]),n=e.ContainersDeleted?.length??0,o=t.ImagesDeleted?.length??0,i=s.VolumesDeleted?.length??0,a=r.NetworksDeleted?.length??0,c=(e.SpaceReclaimed??0)+(t.SpaceReclaimed??0)+(s.SpaceReclaimed??0),d=["## Docker Prune Results","",`**Containers removed:** ${n}`,`**Images removed:** ${o}`,`**Volumes removed:** ${i}`,`**Networks removed:** ${a}`,"",`**Total space reclaimed:** ${pi(c)}`];return{success:!0,data:{containers:e,images:t,volumes:s,networks:r},display:d.join(`
703
726
  `)}}async composePs(e){let s=(await this.api("GET","/containers/json?all=true")).filter(o=>o.Labels?.["com.docker.compose.project"]),r=new Map;for(let o of s){let i=o.Labels["com.docker.compose.project"];e&&i!==e||(r.has(i)||r.set(i,[]),r.get(i).push(o))}let n=["## Docker Compose Projects",""];if(r.size===0)return n.push(e?`No containers found for project "${e}".`:"No Compose-managed containers found."),{success:!0,data:[],display:n.join(`
704
- `)};for(let[o,i]of r){n.push(`### ${o}`),n.push(""),n.push("| Service | Name | Status | Ports |"),n.push("|---------|------|--------|-------|");for(let a of i){let c=a.Labels?.["com.docker.compose.service"]??"-";n.push(`| ${c} | ${mi(a.Names)} | ${a.Status??a.State??"-"} | ${hl(a.Ports)} |`)}n.push("")}return{success:!0,data:[...r.entries()],display:n.join(`
705
- `)}}async composeUp(e){if(!e)return{success:!1,error:'Missing required "project" parameter'};let{stdout:t,stderr:s}=await pl("docker",["compose","-p",e,"up","-d"],{timeout:12e4}),r=(t+`
727
+ `)};for(let[o,i]of r){n.push(`### ${o}`),n.push(""),n.push("| Service | Name | Status | Ports |"),n.push("|---------|------|--------|-------|");for(let a of i){let c=a.Labels?.["com.docker.compose.service"]??"-";n.push(`| ${c} | ${hi(a.Names)} | ${a.Status??a.State??"-"} | ${wl(a.Ports)} |`)}n.push("")}return{success:!0,data:[...r.entries()],display:n.join(`
728
+ `)}}async composeUp(e){if(!e)return{success:!1,error:'Missing required "project" parameter'};let{stdout:t,stderr:s}=await yl("docker",["compose","-p",e,"up","-d"],{timeout:12e4}),r=(t+`
706
729
  `+s).trim();return{success:!0,data:{project:e,output:r},display:[`**Compose up:** \`${e}\``,"","```",r,"```"].join(`
707
- `)}}async composeDown(e){if(!e)return{success:!1,error:'Missing required "project" parameter'};let{stdout:t,stderr:s}=await pl("docker",["compose","-p",e,"down"],{timeout:12e4}),r=(t+`
730
+ `)}}async composeDown(e){if(!e)return{success:!1,error:'Missing required "project" parameter'};let{stdout:t,stderr:s}=await yl("docker",["compose","-p",e,"down"],{timeout:12e4}),r=(t+`
708
731
  `+s).trim();return{success:!0,data:{project:e,output:r},display:[`**Compose down:** \`${e}\``,"","```",r,"```"].join(`
709
- `)}}}});import{readFile as gl,writeFile as yl,mkdir as wl}from"node:fs/promises";import{homedir as pi}from"node:os";import{join as hi}from"node:path";import bl from"node:crypto";function Xm(){let l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",e=bl.randomBytes(86);return Array.from(e).map(t=>l[t%l.length]).join("")}function Ym(l){return bl.createHash("sha256").update(l).digest("base64url")}function fe(l,e){return l[e]?.value??"?"}var Wm,Tl,kn,Gm,Vm,Km,_l,vn,Jm,Sn,El=_(()=>{"use strict";U();Wm="https://customer.bmwgroup.com/gcdm/oauth/device/code",Tl="https://customer.bmwgroup.com/gcdm/oauth/token",kn="https://api-cardata.bmwgroup.com",Gm="v1",Vm="authenticate_user openid cardata:api:read cardata:streaming:read",Km=5*6e4,_l="Alfred",vn=hi(pi(),".alfred","bmw-tokens.json");m(Xm,"generateCodeVerifier");m(Ym,"generateCodeChallenge");Jm=["vehicle.drivetrain.batteryManagement.header","vehicle.drivetrain.batteryManagement.maxEnergy","vehicle.drivetrain.batteryManagement.batterySizeMax","vehicle.powertrain.electric.battery.stateOfHealth.displayed","vehicle.powertrain.electric.battery.stateOfCharge.target","vehicle.drivetrain.electricEngine.remainingElectricRange","vehicle.drivetrain.electricEngine.charging.status","vehicle.drivetrain.electricEngine.charging.level","vehicle.drivetrain.electricEngine.charging.timeRemaining","vehicle.drivetrain.electricEngine.charging.timeToFullyCharged","vehicle.drivetrain.electricEngine.charging.hvStatus","vehicle.drivetrain.electricEngine.charging.method","vehicle.drivetrain.electricEngine.charging.phaseNumber","vehicle.drivetrain.electricEngine.charging.lastChargingReason","vehicle.drivetrain.electricEngine.charging.lastChargingResult","vehicle.drivetrain.electricEngine.charging.reasonChargingEnd","vehicle.powertrain.electric.battery.charging.power","vehicle.drivetrain.electricEngine.charging.acVoltage","vehicle.drivetrain.electricEngine.charging.acAmpere","vehicle.powertrain.electric.battery.charging.acLimit.selected","vehicle.powertrain.tractionBattery.charging.port.anyPosition.isPlugged","vehicle.powertrain.tractionBattery.charging.port.anyPosition.flap.isOpen","vehicle.body.chargingPort.lockedStatus","vehicle.body.chargingPort.plugEventId","vehicle.powertrain.electric.battery.preconditioning.automaticMode.statusFeedback","vehicle.powertrain.electric.battery.preconditioning.manualMode.statusFeedback","vehicle.vehicle.avgAuxPower","vehicle.trip.segment.end.drivetrain.batteryManagement.hvSoc","vehicle.trip.segment.accumulated.drivetrain.electricEngine.recuperationTotal","vehicle.vehicleIdentification.basicVehicleData"];m(fe,"tv");Sn=class extends R{static{m(this,"BMWSkill")}metadata={name:"bmw",category:"infrastructure",description:'BMW CarData \u2014 Fahrzeugdaten abrufen. "authorize" startet den Device-Auth-Flow (einmalig). "status" zeigt SoC, Reichweite, Modell, Batterie-Gesundheit. "charging" zeigt Ladestatus, Leistung, Restzeit, Ziel-SoC, Stecker. "charging_sessions" listet Lade-Sessions (from/to Zeitraum). "consumption" berechnet Durchschnittsverbrauch (kWh/100km) aus Lade-Sessions \u2014 optional mit period: "last" (letzte Fahrt), "week", "month", "year", "all" (default: month).',riskLevel:"read",version:"2.1.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["authorize","status","charging","charging_sessions","consumption"],description:"BMW CarData action"},vin:{type:"string",description:"Vehicle Identification Number (optional \u2014 uses stored VIN if omitted)"},device_code:{type:"string",description:"Device code from authorize step 1 (for polling token in step 2)"},from:{type:"string",description:"ISO date-time start for charging_sessions (required for that action)"},to:{type:"string",description:"ISO date-time end for charging_sessions (required for that action)"},period:{type:"string",enum:["last","week","month","year","all"],description:"Zeitraum f\xFCr consumption: last (letzte Fahrt), week, month, year, all (default: month)"}},required:["action"]}};config;tokens=null;cache=new Map;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"authorize":return await this.authorize(e.device_code);case"status":return await this.getStatus(e.vin);case"charging":return await this.getCharging(e.vin);case"charging_sessions":return await this.getChargingSessions(e.vin,e.from,e.to);case"consumption":return await this.getConsumption(e.vin,e.period);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`BMW API error: ${r instanceof Error?r.message:String(r)}`}}}async authorize(e){if(e)return await this.pollToken(e);let t=Xm(),s=Ym(t),r=await fetch(Wm,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.config.clientId,response_type:"device_code",code_challenge:s,code_challenge_method:"S256",scope:Vm}),signal:AbortSignal.timeout(15e3)});if(!r.ok){let i=await r.text().catch(()=>"");throw new Error(`Device code request failed: HTTP ${r.status} \u2014 ${i.slice(0,300)}`)}let n=await r.json(),o={codeVerifier:t,deviceCode:n.device_code};return await this.savePartialTokens(o),this.tokens=null,{success:!0,data:n,display:["## BMW Autorisierung","",`1. \xD6ffne: **${n.verification_uri_complete??n.verification_uri}**`,`2. Gib diesen Code ein: **${n.user_code}**`,"",`Danach ruf diese Action erneut auf mit \`device_code: "${n.device_code}"\` um den Token abzuholen.`].join(`
710
- `)}}async pollToken(e){let s=(await this.loadTokens())?.codeVerifier;if(!s)throw new Error('Kein code_verifier gefunden. Bitte zuerst "authorize" ohne device_code aufrufen.');let r=await fetch(Tl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.config.clientId,device_code:e,grant_type:"urn:ietf:params:oauth:grant-type:device_code",code_verifier:s}),signal:AbortSignal.timeout(15e3)});if(!r.ok){let p=await r.text().catch(()=>"");if(p.includes("authorization_pending"))return{success:!0,data:{status:"pending"},display:"Autorisierung noch ausstehend \u2014 bitte zuerst im Browser best\xE4tigen, dann erneut versuchen."};throw new Error(`Token poll failed: HTTP ${r.status} \u2014 ${p.slice(0,300)}`)}let n=await r.json(),o=n.access_token,i={accessToken:o,refreshToken:n.refresh_token,idToken:n.id_token,expiresAt:Date.now()+(n.expires_in??3600)*1e3,vin:"",containerId:""},a=await this.fetchVin(o);i.vin=a,await this.saveTokens(i);let c="",d="";try{c=await this.ensureContainer(o),i.containerId=c,await this.saveTokens(i)}catch(p){d=p instanceof Error?p.message:String(p)}this.tokens=i;let u=["## BMW Autorisierung erfolgreich","",`**VIN:** ${a}`];return c?(u.push(`**Container:** ${c}`),u.push("Tokens gespeichert. Du kannst jetzt Fahrzeugdaten abrufen.")):(u.push(`**Container-Fehler:** ${d}`),u.push("Tokens + VIN gespeichert, aber Container konnte nicht erstellt werden."),u.push("Erstelle den Container manuell im BMW CarData Portal oder versuche es erneut.")),{success:c!=="",data:{vin:a,containerId:c,containerError:d||void 0},display:u.join(`
711
- `)}}async fetchVin(e){let t=await fetch(`${kn}/customers/vehicles/mappings`,{headers:this.apiHeaders(e),signal:AbortSignal.timeout(15e3)});if(!t.ok)throw new Error(`Failed to fetch vehicles: HTTP ${t.status}`);let s=await t.json();if(typeof s.vin=="string")return s.vin;if(Array.isArray(s)){let r=s;return r.find(o=>o.mappingType==="PRIMARY")?.vin??r[0]?.vin??(()=>{throw new Error("No vehicles found")})()}throw new Error(`No vehicles found in account (response: ${JSON.stringify(s).slice(0,200)})`)}async ensureContainer(e){let t=await fetch(`${kn}/customers/containers`,{headers:this.apiHeaders(e),signal:AbortSignal.timeout(15e3)});if(t.ok){let n=await t.json(),i=(Array.isArray(n)?n:Array.isArray(n.containers)?n.containers:[]).find(a=>a.name===_l);if(i)return i.containerId}let s=await fetch(`${kn}/customers/containers`,{method:"POST",headers:{...this.apiHeaders(e),"Content-Type":"application/json"},body:JSON.stringify({name:_l,purpose:"Alfred AI Assistant",technicalDescriptors:Jm}),signal:AbortSignal.timeout(15e3)});if(!s.ok){let n=await s.text().catch(()=>"");throw new Error(`Container creation failed: HTTP ${s.status} \u2014 ${n.slice(0,300)}`)}return(await s.json()).containerId}apiHeaders(e){return{Authorization:`Bearer ${e}`,"x-version":Gm,Accept:"application/json"}}async apiGet(e){let t=e,s=this.cache.get(t);if(s&&Date.now()-s.ts<Km)return s.data;let r=await this.ensureToken(),n=`${kn}${e}`,o=await fetch(n,{headers:this.apiHeaders(r),signal:AbortSignal.timeout(15e3)});if(o.status===401){let a=await this.loadTokens();a&&(r=await this.refreshAccessToken(a),o=await fetch(n,{headers:this.apiHeaders(r),signal:AbortSignal.timeout(15e3)}))}if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`HTTP ${o.status} \u2014 ${a.slice(0,300)}`)}let i=await o.json();return this.cache.set(t,{data:i,ts:Date.now()}),i}async loadTokens(){if(this.tokens)return this.tokens;try{let e=await gl(vn,"utf-8");return this.tokens=JSON.parse(e),this.tokens}catch{return null}}async saveTokens(e){await wl(hi(pi(),".alfred"),{recursive:!0}),await yl(vn,JSON.stringify(e,null,2),"utf-8")}async savePartialTokens(e){await wl(hi(pi(),".alfred"),{recursive:!0});let t={};try{let s=await gl(vn,"utf-8");t=JSON.parse(s)}catch{}await yl(vn,JSON.stringify({...t,...e},null,2),"utf-8")}async ensureToken(){let e=await this.loadTokens();if(!e?.accessToken)throw new Error('Nicht autorisiert. Bitte zuerst die "authorize"-Action aufrufen, um den BMW-Account zu verbinden.');return Date.now()>e.expiresAt-6e4?await this.refreshAccessToken(e):e.accessToken}async refreshAccessToken(e){let t=await fetch(Tl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.config.clientId,grant_type:"refresh_token",refresh_token:e.refreshToken}),signal:AbortSignal.timeout(15e3)});if(!t.ok)throw this.tokens=null,new Error('Token-Refresh fehlgeschlagen. Bitte erneut "authorize" aufrufen.');let s=await t.json(),r={...e,accessToken:s.access_token,refreshToken:s.refresh_token??e.refreshToken,idToken:s.id_token??e.idToken,expiresAt:Date.now()+(s.expires_in??3600)*1e3};return delete r.codeVerifier,delete r.deviceCode,await this.saveTokens(r),this.tokens=r,r.accessToken}async resolveVin(e){if(e)return e;let t=await this.loadTokens();if(t?.vin)return t.vin;throw new Error('Keine VIN angegeben und keine gespeicherte VIN gefunden. Bitte zuerst "authorize" aufrufen.')}async resolveContainerId(){let e=await this.loadTokens();if(e?.containerId)return e.containerId;throw new Error('Kein Container gefunden. Bitte zuerst "authorize" aufrufen.')}async getStatus(e){let t=await this.resolveVin(e),s=await this.resolveContainerId(),[r,n]=await Promise.all([this.apiGet(`/customers/vehicles/${t}/telematicData?containerId=${s}`),this.apiGet(`/customers/vehicles/${t}/basicData`)]),o=r.telematicData??{},i=fe(o,"vehicle.drivetrain.batteryManagement.header"),a=fe(o,"vehicle.drivetrain.electricEngine.remainingElectricRange"),c=fe(o,"vehicle.drivetrain.batteryManagement.maxEnergy"),d=fe(o,"vehicle.powertrain.electric.battery.stateOfHealth.displayed"),p=["## BMW Fahrzeugstatus","",`**Modell:** ${n.modelName??n.model??"?"}`,`**VIN:** ${t}`,`**Ladestand (SoC):** ${i} %`,`**Elektrische Reichweite:** ${a} km`,`**Batteriekapazit\xE4t:** ${c} kWh`,`**Batterie-Gesundheit (SoH):** ${d} %`];return{success:!0,data:{telematic:o,basic:n},display:p.join(`
712
- `)}}async getCharging(e){let t=await this.resolveVin(e),s=await this.resolveContainerId(),n=(await this.apiGet(`/customers/vehicles/${t}/telematicData?containerId=${s}`)).telematicData??{},o=fe(n,"vehicle.drivetrain.electricEngine.charging.status"),i=fe(n,"vehicle.drivetrain.batteryManagement.header"),a=fe(n,"vehicle.drivetrain.electricEngine.charging.level"),c=fe(n,"vehicle.drivetrain.electricEngine.charging.timeRemaining"),d=fe(n,"vehicle.powertrain.electric.battery.charging.power"),u=fe(n,"vehicle.drivetrain.electricEngine.charging.hvStatus"),p=fe(n,"vehicle.powertrain.electric.battery.stateOfCharge.target"),g=fe(n,"vehicle.drivetrain.electricEngine.charging.acVoltage"),f=fe(n,"vehicle.drivetrain.electricEngine.charging.acAmpere"),h=fe(n,"vehicle.powertrain.tractionBattery.charging.port.anyPosition.isPlugged"),y=fe(n,"vehicle.powertrain.tractionBattery.charging.port.anyPosition.flap.isOpen"),E=fe(n,"vehicle.body.chargingPort.lockedStatus"),$=["## BMW Ladestatus","",`**Status:** ${o}`,`**Ladestand:** ${i} %`,`**Ladelevel:** ${a}`,`**Ladeleistung:** ${d} kW`,`**Restzeit:** ${c} min`,`**Ziel-SoC:** ${p} %`,`**HV-Batterie:** ${u}`,`**AC Spannung:** ${g} V`,`**AC Strom:** ${f} A`,`**Stecker eingesteckt:** ${h}`,`**Ladeklappe offen:** ${y}`,`**Ladeport-Schloss:** ${E}`];return{success:!0,data:n,display:$.join(`
713
- `)}}async getChargingSessions(e,t,s){let r=await this.resolveVin(e),n=new Date,o=s??n.toISOString(),i=t??new Date(n.getTime()-30*24*60*6e4).toISOString(),a=await this.apiGet(`/customers/vehicles/${r}/chargingHistory?from=${encodeURIComponent(i)}&to=${encodeURIComponent(o)}`),c=a.data??a.chargingSessions??[],d=[`## BMW Lade-Sessions (${i.slice(0,10)} \u2013 ${o.slice(0,10)})`,"","| Start | Ende | Dauer | Energie | Start-SoC | End-SoC | km-Stand | Ort |","|-------|------|-------|---------|-----------|---------|----------|-----|"];for(let u of c.slice(0,20)){let p=u.startTime,g=u.endTime,f=m(O=>new Date(O*1e3).toLocaleString("de-AT",{day:"2-digit",month:"2-digit",year:"2-digit",hour:"2-digit",minute:"2-digit"}),"fmtDateTime"),h=p?f(p):"-",y=g?f(g):"-",E=u.totalChargingDurationSec,$=E!=null?Math.round(E/60):"-",v=u.energyConsumedFromPowerGridKwh??"-",P=u.displayedStartSoc??"-",x=u.displayedSoc??"-",q=u.mileage!=null?`${u.mileage}`:"-",J=u.chargingLocation,oe=J?.formattedAddress??J?.streetAddress??"-";d.push(`| ${h} | ${y} | ${$} min | ${v} kWh | ${P}% | ${x}% | ${q} | ${oe} |`)}return c.length===0&&d.push("| - | - | Keine Sessions gefunden | - | - | - | - | - |"),{success:!0,data:a,display:d.join(`
714
- `)}}async getConsumption(e,t){let s=await this.resolveVin(e),r=await this.resolveContainerId(),n=new Date,i={last:7,week:7,month:30,year:365,all:730}[t??"month"]??30,a=new Date(n.getTime()-i*864e5).toISOString(),c=n.toISOString(),[d,u]=await Promise.all([this.apiGet(`/customers/vehicles/${s}/chargingHistory?from=${encodeURIComponent(a)}&to=${encodeURIComponent(c)}`),this.apiGet(`/customers/vehicles/${s}/telematicData?containerId=${r}`)]),p=d.data??d.chargingSessions??[],g=parseFloat(fe(u.telematicData??{},"vehicle.drivetrain.batteryManagement.maxEnergy"))||63;if(p.length<2)return{success:!0,data:{sessions:p.length},display:"Nicht gen\xFCgend Lade-Sessions f\xFCr Verbrauchsberechnung (min. 2 n\xF6tig)."};let f=p.filter(O=>typeof O.mileage=="number"&&typeof O.displayedStartSoc=="number"&&typeof O.displayedSoc=="number").sort((O,se)=>O.mileage-se.mileage);if(f.length<2)return{success:!0,data:{sessions:f.length},display:"Nicht gen\xFCgend Lade-Sessions mit vollst\xE4ndigen Daten."};let h=[];for(let O=1;O<f.length;O++){let se=f[O-1],$e=f[O],re=se.mileage,j=$e.mileage,ee=j-re;if(ee<=0)continue;let we=se.displayedSoc,D=$e.displayedStartSoc,B=we-D;if(B<=0)continue;let _e=B/100*g,ie=_e/ee*100,Be=$e.startTime,le=Be?new Date(Be*1e3).toLocaleDateString("de-AT"):"-";h.push({fromKm:re,toKm:j,distance:ee,socUsed:B,kWhUsed:_e,consumption:ie,date:le})}if(h.length===0)return{success:!0,data:{},display:"Keine auswertbaren Fahrtabschnitte gefunden."};if(t==="last"){let O=h[h.length-1];return{success:!0,data:O,display:["## Letzte Fahrt (gesch\xE4tzt)","",`**Datum:** ${O.date}`,`**Strecke:** ${O.distance} km`,`**Verbrauch:** ${O.consumption.toFixed(1)} kWh/100km`,`**Energie:** ${O.kWhUsed.toFixed(1)} kWh (${O.socUsed}% SoC)`,`**km-Stand:** ${O.fromKm} \u2192 ${O.toKm}`].join(`
715
- `)}}let y=h.reduce((O,se)=>O+se.distance,0),E=h.reduce((O,se)=>O+se.kWhUsed,0),$=E/y*100,v=h.map(O=>O.consumption).sort((O,se)=>O-se),P=v[0],x=v[v.length-1],q=v[Math.floor(v.length/2)],oe=[`## BMW Verbrauchsstatistik \u2014 ${{week:"Letzte Woche",month:"Letzter Monat",year:"Letztes Jahr",all:"Gesamt"}[t??"month"]??"Letzter Monat"}`,"",`**Batteriekapazit\xE4t:** ${g} kWh`,`**Ausgewertete Fahrten:** ${h.length}`,`**Gesamtstrecke:** ${y.toLocaleString("de-AT")} km`,`**Gesamtverbrauch:** ${E.toFixed(1)} kWh`,"",`**Durchschnitt:** ${$.toFixed(1)} kWh/100km`,`**Min:** ${P.toFixed(1)} kWh/100km`,`**Max:** ${x.toFixed(1)} kWh/100km`,`**Median:** ${q.toFixed(1)} kWh/100km`,"","### Einzelne Fahrten","","| Datum | Strecke | Verbrauch | Energie |","|-------|---------|-----------|---------|"];for(let O of h)oe.push(`| ${O.date} | ${O.distance} km | ${O.consumption.toFixed(1)} kWh/100km | ${O.kWhUsed.toFixed(1)} kWh |`);return{success:!0,data:{avgConsumption:$,totalDistance:y,totalKwh:E,segments:h},display:oe.join(`
716
- `)}}}});var Zm,An,$l=_(()=>{"use strict";U();Zm="https://routes.googleapis.com/directions/v2:computeRoutes",An=class extends R{static{m(this,"RoutingSkill")}metadata={name:"routing",category:"information",description:'Routenberechnung mit Live-Traffic via Google Routes API. "route" berechnet Route mit Distanz, Dauer und Dauer im aktuellen Verkehr. "departure_time" empfiehlt wann man losfahren soll, um zu einer bestimmten Zeit anzukommen. Orte als Adresse oder "lat,lng" angeben.',riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["route","departure_time"],description:"Routing action"},origin:{type:"string",description:'Start-Adresse oder "lat,lng"'},destination:{type:"string",description:'Ziel-Adresse oder "lat,lng"'},departure_time:{type:"string",description:"ISO-Zeitpunkt f\xFCr Abfahrt (optional, f\xFCr Traffic-Berechnung)"},arrival_time:{type:"string",description:"ISO-Zeitpunkt gew\xFCnschte Ankunft (f\xFCr departure_time-Action)"},travel_mode:{type:"string",enum:["DRIVE","BICYCLE","WALK","TRANSIT"],description:"Fortbewegungsart (Standard: DRIVE)"}},required:["action","origin","destination"]}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};let r=e.origin,n=e.destination;if(!r)return{success:!1,error:'Missing required field "origin"'};if(!n)return{success:!1,error:'Missing required field "destination"'};try{switch(s){case"route":return await this.computeRoute(r,n,e.departure_time,e.travel_mode);case"departure_time":return await this.computeDepartureTime(r,n,e.arrival_time,e.travel_mode);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(o){return{success:!1,error:`Google Routes API error: ${o instanceof Error?o.message:String(o)}`}}}async computeRoute(e,t,s,r){let n=this.buildRequestBody(e,t,r,s),i=(await this.callRoutesApi(n)).routes?.[0];if(!i)return{success:!1,error:"Keine Route gefunden."};let a=(i.distanceMeters/1e3).toFixed(1),c=this.parseDuration(i.duration),d=this.parseDuration(i.staticDuration),u=c-d,p=["## Route","",`**${e}** \u2192 **${t}**`,"",`**Distanz:** ${a} km`,`**Fahrzeit (aktuell):** ${this.formatMinutes(c)}`,`**Fahrzeit (ohne Verkehr):** ${this.formatMinutes(d)}`];if(u>1&&p.push(`**Verkehrsverz\xF6gerung:** +${this.formatMinutes(u)}`),s){let g=new Date(new Date(s).getTime()+c*6e4);p.push(`**Gesch\xE4tzte Ankunft:** ${g.toLocaleString("de-AT")}`)}return{success:!0,data:{distanceKm:parseFloat(a),durationMinutes:c,staticDurationMinutes:d},display:p.join(`
732
+ `)}}}});import{readFile as _l,writeFile as bl,mkdir as El}from"node:fs/promises";import{homedir as fi}from"node:os";import{join as gi}from"node:path";import vl from"node:crypto";function Qm(){let l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",e=vl.randomBytes(86);return Array.from(e).map(t=>l[t%l.length]).join("")}function ep(l){return vl.createHash("sha256").update(l).digest("base64url")}function fe(l,e){return l[e]?.value??"?"}var Xm,$l,Sn,Ym,Jm,Zm,kl,An,tp,In,Sl=_(()=>{"use strict";U();Xm="https://customer.bmwgroup.com/gcdm/oauth/device/code",$l="https://customer.bmwgroup.com/gcdm/oauth/token",Sn="https://api-cardata.bmwgroup.com",Ym="v1",Jm="authenticate_user openid cardata:api:read cardata:streaming:read",Zm=5*6e4,kl="Alfred",An=gi(fi(),".alfred","bmw-tokens.json");m(Qm,"generateCodeVerifier");m(ep,"generateCodeChallenge");tp=["vehicle.drivetrain.batteryManagement.header","vehicle.drivetrain.batteryManagement.maxEnergy","vehicle.drivetrain.batteryManagement.batterySizeMax","vehicle.powertrain.electric.battery.stateOfHealth.displayed","vehicle.powertrain.electric.battery.stateOfCharge.target","vehicle.drivetrain.electricEngine.remainingElectricRange","vehicle.drivetrain.electricEngine.charging.status","vehicle.drivetrain.electricEngine.charging.level","vehicle.drivetrain.electricEngine.charging.timeRemaining","vehicle.drivetrain.electricEngine.charging.timeToFullyCharged","vehicle.drivetrain.electricEngine.charging.hvStatus","vehicle.drivetrain.electricEngine.charging.method","vehicle.drivetrain.electricEngine.charging.phaseNumber","vehicle.drivetrain.electricEngine.charging.lastChargingReason","vehicle.drivetrain.electricEngine.charging.lastChargingResult","vehicle.drivetrain.electricEngine.charging.reasonChargingEnd","vehicle.powertrain.electric.battery.charging.power","vehicle.drivetrain.electricEngine.charging.acVoltage","vehicle.drivetrain.electricEngine.charging.acAmpere","vehicle.powertrain.electric.battery.charging.acLimit.selected","vehicle.powertrain.tractionBattery.charging.port.anyPosition.isPlugged","vehicle.powertrain.tractionBattery.charging.port.anyPosition.flap.isOpen","vehicle.body.chargingPort.lockedStatus","vehicle.body.chargingPort.plugEventId","vehicle.powertrain.electric.battery.preconditioning.automaticMode.statusFeedback","vehicle.powertrain.electric.battery.preconditioning.manualMode.statusFeedback","vehicle.vehicle.avgAuxPower","vehicle.trip.segment.end.drivetrain.batteryManagement.hvSoc","vehicle.trip.segment.accumulated.drivetrain.electricEngine.recuperationTotal","vehicle.vehicleIdentification.basicVehicleData"];m(fe,"tv");In=class extends x{static{m(this,"BMWSkill")}metadata={name:"bmw",category:"infrastructure",description:'BMW CarData \u2014 Fahrzeugdaten abrufen. "authorize" startet den Device-Auth-Flow (einmalig). "status" zeigt SoC, Reichweite, Modell, Batterie-Gesundheit. "charging" zeigt Ladestatus, Leistung, Restzeit, Ziel-SoC, Stecker. "charging_sessions" listet Lade-Sessions (from/to Zeitraum). "consumption" berechnet Durchschnittsverbrauch (kWh/100km) aus Lade-Sessions \u2014 optional mit period: "last" (letzte Fahrt), "week", "month", "year", "all" (default: month).',riskLevel:"read",version:"2.1.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["authorize","status","charging","charging_sessions","consumption"],description:"BMW CarData action"},vin:{type:"string",description:"Vehicle Identification Number (optional \u2014 uses stored VIN if omitted)"},device_code:{type:"string",description:"Device code from authorize step 1 (for polling token in step 2)"},from:{type:"string",description:"ISO date-time start for charging_sessions (required for that action)"},to:{type:"string",description:"ISO date-time end for charging_sessions (required for that action)"},period:{type:"string",enum:["last","week","month","year","all"],description:"Zeitraum f\xFCr consumption: last (letzte Fahrt), week, month, year, all (default: month)"}},required:["action"]}};config;tokens=null;cache=new Map;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"authorize":return await this.authorize(e.device_code);case"status":return await this.getStatus(e.vin);case"charging":return await this.getCharging(e.vin);case"charging_sessions":return await this.getChargingSessions(e.vin,e.from,e.to);case"consumption":return await this.getConsumption(e.vin,e.period);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`BMW API error: ${r instanceof Error?r.message:String(r)}`}}}async authorize(e){if(e)return await this.pollToken(e);let t=Qm(),s=ep(t),r=await fetch(Xm,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.config.clientId,response_type:"device_code",code_challenge:s,code_challenge_method:"S256",scope:Jm}),signal:AbortSignal.timeout(15e3)});if(!r.ok){let i=await r.text().catch(()=>"");throw new Error(`Device code request failed: HTTP ${r.status} \u2014 ${i.slice(0,300)}`)}let n=await r.json(),o={codeVerifier:t,deviceCode:n.device_code};return await this.savePartialTokens(o),this.tokens=null,{success:!0,data:n,display:["## BMW Autorisierung","",`1. \xD6ffne: **${n.verification_uri_complete??n.verification_uri}**`,`2. Gib diesen Code ein: **${n.user_code}**`,"",`Danach ruf diese Action erneut auf mit \`device_code: "${n.device_code}"\` um den Token abzuholen.`].join(`
733
+ `)}}async pollToken(e){let s=(await this.loadTokens())?.codeVerifier;if(!s)throw new Error('Kein code_verifier gefunden. Bitte zuerst "authorize" ohne device_code aufrufen.');let r=await fetch($l,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.config.clientId,device_code:e,grant_type:"urn:ietf:params:oauth:grant-type:device_code",code_verifier:s}),signal:AbortSignal.timeout(15e3)});if(!r.ok){let p=await r.text().catch(()=>"");if(p.includes("authorization_pending"))return{success:!0,data:{status:"pending"},display:"Autorisierung noch ausstehend \u2014 bitte zuerst im Browser best\xE4tigen, dann erneut versuchen."};throw new Error(`Token poll failed: HTTP ${r.status} \u2014 ${p.slice(0,300)}`)}let n=await r.json(),o=n.access_token,i={accessToken:o,refreshToken:n.refresh_token,idToken:n.id_token,expiresAt:Date.now()+(n.expires_in??3600)*1e3,vin:"",containerId:""},a=await this.fetchVin(o);i.vin=a,await this.saveTokens(i);let c="",d="";try{c=await this.ensureContainer(o),i.containerId=c,await this.saveTokens(i)}catch(p){d=p instanceof Error?p.message:String(p)}this.tokens=i;let u=["## BMW Autorisierung erfolgreich","",`**VIN:** ${a}`];return c?(u.push(`**Container:** ${c}`),u.push("Tokens gespeichert. Du kannst jetzt Fahrzeugdaten abrufen.")):(u.push(`**Container-Fehler:** ${d}`),u.push("Tokens + VIN gespeichert, aber Container konnte nicht erstellt werden."),u.push("Erstelle den Container manuell im BMW CarData Portal oder versuche es erneut.")),{success:c!=="",data:{vin:a,containerId:c,containerError:d||void 0},display:u.join(`
734
+ `)}}async fetchVin(e){let t=await fetch(`${Sn}/customers/vehicles/mappings`,{headers:this.apiHeaders(e),signal:AbortSignal.timeout(15e3)});if(!t.ok)throw new Error(`Failed to fetch vehicles: HTTP ${t.status}`);let s=await t.json();if(typeof s.vin=="string")return s.vin;if(Array.isArray(s)){let r=s;return r.find(o=>o.mappingType==="PRIMARY")?.vin??r[0]?.vin??(()=>{throw new Error("No vehicles found")})()}throw new Error(`No vehicles found in account (response: ${JSON.stringify(s).slice(0,200)})`)}async ensureContainer(e){let t=await fetch(`${Sn}/customers/containers`,{headers:this.apiHeaders(e),signal:AbortSignal.timeout(15e3)});if(t.ok){let n=await t.json(),i=(Array.isArray(n)?n:Array.isArray(n.containers)?n.containers:[]).find(a=>a.name===kl);if(i)return i.containerId}let s=await fetch(`${Sn}/customers/containers`,{method:"POST",headers:{...this.apiHeaders(e),"Content-Type":"application/json"},body:JSON.stringify({name:kl,purpose:"Alfred AI Assistant",technicalDescriptors:tp}),signal:AbortSignal.timeout(15e3)});if(!s.ok){let n=await s.text().catch(()=>"");throw new Error(`Container creation failed: HTTP ${s.status} \u2014 ${n.slice(0,300)}`)}return(await s.json()).containerId}apiHeaders(e){return{Authorization:`Bearer ${e}`,"x-version":Ym,Accept:"application/json"}}async apiGet(e){let t=e,s=this.cache.get(t);if(s&&Date.now()-s.ts<Zm)return s.data;let r=await this.ensureToken(),n=`${Sn}${e}`,o=await fetch(n,{headers:this.apiHeaders(r),signal:AbortSignal.timeout(15e3)});if(o.status===401){let a=await this.loadTokens();a&&(r=await this.refreshAccessToken(a),o=await fetch(n,{headers:this.apiHeaders(r),signal:AbortSignal.timeout(15e3)}))}if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`HTTP ${o.status} \u2014 ${a.slice(0,300)}`)}let i=await o.json();return this.cache.set(t,{data:i,ts:Date.now()}),i}async loadTokens(){if(this.tokens)return this.tokens;try{let e=await _l(An,"utf-8");return this.tokens=JSON.parse(e),this.tokens}catch{return null}}async saveTokens(e){await El(gi(fi(),".alfred"),{recursive:!0}),await bl(An,JSON.stringify(e,null,2),"utf-8")}async savePartialTokens(e){await El(gi(fi(),".alfred"),{recursive:!0});let t={};try{let s=await _l(An,"utf-8");t=JSON.parse(s)}catch{}await bl(An,JSON.stringify({...t,...e},null,2),"utf-8")}async ensureToken(){let e=await this.loadTokens();if(!e?.accessToken)throw new Error('Nicht autorisiert. Bitte zuerst die "authorize"-Action aufrufen, um den BMW-Account zu verbinden.');return Date.now()>e.expiresAt-6e4?await this.refreshAccessToken(e):e.accessToken}async refreshAccessToken(e){let t=await fetch($l,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.config.clientId,grant_type:"refresh_token",refresh_token:e.refreshToken}),signal:AbortSignal.timeout(15e3)});if(!t.ok)throw this.tokens=null,new Error('Token-Refresh fehlgeschlagen. Bitte erneut "authorize" aufrufen.');let s=await t.json(),r={...e,accessToken:s.access_token,refreshToken:s.refresh_token??e.refreshToken,idToken:s.id_token??e.idToken,expiresAt:Date.now()+(s.expires_in??3600)*1e3};return delete r.codeVerifier,delete r.deviceCode,await this.saveTokens(r),this.tokens=r,r.accessToken}async resolveVin(e){if(e)return e;let t=await this.loadTokens();if(t?.vin)return t.vin;throw new Error('Keine VIN angegeben und keine gespeicherte VIN gefunden. Bitte zuerst "authorize" aufrufen.')}async resolveContainerId(){let e=await this.loadTokens();if(e?.containerId)return e.containerId;throw new Error('Kein Container gefunden. Bitte zuerst "authorize" aufrufen.')}async getStatus(e){let t=await this.resolveVin(e),s=await this.resolveContainerId(),[r,n]=await Promise.all([this.apiGet(`/customers/vehicles/${t}/telematicData?containerId=${s}`),this.apiGet(`/customers/vehicles/${t}/basicData`)]),o=r.telematicData??{},i=fe(o,"vehicle.drivetrain.batteryManagement.header"),a=fe(o,"vehicle.drivetrain.electricEngine.remainingElectricRange"),c=fe(o,"vehicle.drivetrain.batteryManagement.maxEnergy"),d=fe(o,"vehicle.powertrain.electric.battery.stateOfHealth.displayed"),p=["## BMW Fahrzeugstatus","",`**Modell:** ${n.modelName??n.model??"?"}`,`**VIN:** ${t}`,`**Ladestand (SoC):** ${i} %`,`**Elektrische Reichweite:** ${a} km`,`**Batteriekapazit\xE4t:** ${c} kWh`,`**Batterie-Gesundheit (SoH):** ${d} %`];return{success:!0,data:{telematic:o,basic:n},display:p.join(`
735
+ `)}}async getCharging(e){let t=await this.resolveVin(e),s=await this.resolveContainerId(),n=(await this.apiGet(`/customers/vehicles/${t}/telematicData?containerId=${s}`)).telematicData??{},o=fe(n,"vehicle.drivetrain.electricEngine.charging.status"),i=fe(n,"vehicle.drivetrain.batteryManagement.header"),a=fe(n,"vehicle.drivetrain.electricEngine.charging.level"),c=fe(n,"vehicle.drivetrain.electricEngine.charging.timeRemaining"),d=fe(n,"vehicle.powertrain.electric.battery.charging.power"),u=fe(n,"vehicle.drivetrain.electricEngine.charging.hvStatus"),p=fe(n,"vehicle.powertrain.electric.battery.stateOfCharge.target"),f=fe(n,"vehicle.drivetrain.electricEngine.charging.acVoltage"),g=fe(n,"vehicle.drivetrain.electricEngine.charging.acAmpere"),h=fe(n,"vehicle.powertrain.tractionBattery.charging.port.anyPosition.isPlugged"),y=fe(n,"vehicle.powertrain.tractionBattery.charging.port.anyPosition.flap.isOpen"),E=fe(n,"vehicle.body.chargingPort.lockedStatus"),$=["## BMW Ladestatus","",`**Status:** ${o}`,`**Ladestand:** ${i} %`,`**Ladelevel:** ${a}`,`**Ladeleistung:** ${d} kW`,`**Restzeit:** ${c} min`,`**Ziel-SoC:** ${p} %`,`**HV-Batterie:** ${u}`,`**AC Spannung:** ${f} V`,`**AC Strom:** ${g} A`,`**Stecker eingesteckt:** ${h}`,`**Ladeklappe offen:** ${y}`,`**Ladeport-Schloss:** ${E}`];return{success:!0,data:n,display:$.join(`
736
+ `)}}async getChargingSessions(e,t,s){let r=await this.resolveVin(e),n=new Date,o=s??n.toISOString(),i=t??new Date(n.getTime()-30*24*60*6e4).toISOString(),a=await this.apiGet(`/customers/vehicles/${r}/chargingHistory?from=${encodeURIComponent(i)}&to=${encodeURIComponent(o)}`),c=a.data??a.chargingSessions??[],d=[`## BMW Lade-Sessions (${i.slice(0,10)} \u2013 ${o.slice(0,10)})`,"","| Start | Ende | Dauer | Energie | Start-SoC | End-SoC | km-Stand | Ort |","|-------|------|-------|---------|-----------|---------|----------|-----|"];for(let u of c.slice(0,20)){let p=u.startTime,f=u.endTime,g=m(M=>new Date(M*1e3).toLocaleString("de-AT",{day:"2-digit",month:"2-digit",year:"2-digit",hour:"2-digit",minute:"2-digit"}),"fmtDateTime"),h=p?g(p):"-",y=f?g(f):"-",E=u.totalChargingDurationSec,$=E!=null?Math.round(E/60):"-",v=u.energyConsumedFromPowerGridKwh??"-",j=u.displayedStartSoc??"-",N=u.displayedSoc??"-",H=u.mileage!=null?`${u.mileage}`:"-",D=u.chargingLocation,oe=D?.formattedAddress??D?.streetAddress??"-";d.push(`| ${h} | ${y} | ${$} min | ${v} kWh | ${j}% | ${N}% | ${H} | ${oe} |`)}return c.length===0&&d.push("| - | - | Keine Sessions gefunden | - | - | - | - | - |"),{success:!0,data:a,display:d.join(`
737
+ `)}}async getConsumption(e,t){let s=await this.resolveVin(e),r=await this.resolveContainerId(),n=new Date,i={last:7,week:7,month:30,year:365,all:730}[t??"month"]??30,a=new Date(n.getTime()-i*864e5).toISOString(),c=n.toISOString(),[d,u]=await Promise.all([this.apiGet(`/customers/vehicles/${s}/chargingHistory?from=${encodeURIComponent(a)}&to=${encodeURIComponent(c)}`),this.apiGet(`/customers/vehicles/${s}/telematicData?containerId=${r}`)]),p=d.data??d.chargingSessions??[],f=parseFloat(fe(u.telematicData??{},"vehicle.drivetrain.batteryManagement.maxEnergy"))||63;if(p.length<2)return{success:!0,data:{sessions:p.length},display:"Nicht gen\xFCgend Lade-Sessions f\xFCr Verbrauchsberechnung (min. 2 n\xF6tig)."};let g=p.filter(M=>typeof M.mileage=="number"&&typeof M.displayedStartSoc=="number"&&typeof M.displayedSoc=="number").sort((M,re)=>M.mileage-re.mileage);if(g.length<2)return{success:!0,data:{sessions:g.length},display:"Nicht gen\xFCgend Lade-Sessions mit vollst\xE4ndigen Daten."};let h=[];for(let M=1;M<g.length;M++){let re=g[M-1],ze=g[M],$e=re.mileage,se=ze.mileage,X=se-$e;if(X<=0)continue;let z=re.displayedSoc,me=ze.displayedStartSoc,xe=z-me;if(xe<=0)continue;let C=xe/100*f,B=C/X*100,pe=ze.startTime,we=pe?new Date(pe*1e3).toLocaleDateString("de-AT"):"-";h.push({fromKm:$e,toKm:se,distance:X,socUsed:xe,kWhUsed:C,consumption:B,date:we})}if(h.length===0)return{success:!0,data:{},display:"Keine auswertbaren Fahrtabschnitte gefunden."};if(t==="last"){let M=h[h.length-1];return{success:!0,data:M,display:["## Letzte Fahrt (gesch\xE4tzt)","",`**Datum:** ${M.date}`,`**Strecke:** ${M.distance} km`,`**Verbrauch:** ${M.consumption.toFixed(1)} kWh/100km`,`**Energie:** ${M.kWhUsed.toFixed(1)} kWh (${M.socUsed}% SoC)`,`**km-Stand:** ${M.fromKm} \u2192 ${M.toKm}`].join(`
738
+ `)}}let y=h.reduce((M,re)=>M+re.distance,0),E=h.reduce((M,re)=>M+re.kWhUsed,0),$=E/y*100,v=h.map(M=>M.consumption).sort((M,re)=>M-re),j=v[0],N=v[v.length-1],H=v[Math.floor(v.length/2)],oe=[`## BMW Verbrauchsstatistik \u2014 ${{week:"Letzte Woche",month:"Letzter Monat",year:"Letztes Jahr",all:"Gesamt"}[t??"month"]??"Letzter Monat"}`,"",`**Batteriekapazit\xE4t:** ${f} kWh`,`**Ausgewertete Fahrten:** ${h.length}`,`**Gesamtstrecke:** ${y.toLocaleString("de-AT")} km`,`**Gesamtverbrauch:** ${E.toFixed(1)} kWh`,"",`**Durchschnitt:** ${$.toFixed(1)} kWh/100km`,`**Min:** ${j.toFixed(1)} kWh/100km`,`**Max:** ${N.toFixed(1)} kWh/100km`,`**Median:** ${H.toFixed(1)} kWh/100km`,"","### Einzelne Fahrten","","| Datum | Strecke | Verbrauch | Energie |","|-------|---------|-----------|---------|"];for(let M of h)oe.push(`| ${M.date} | ${M.distance} km | ${M.consumption.toFixed(1)} kWh/100km | ${M.kWhUsed.toFixed(1)} kWh |`);return{success:!0,data:{avgConsumption:$,totalDistance:y,totalKwh:E,segments:h},display:oe.join(`
739
+ `)}}}});var sp,xn,Al=_(()=>{"use strict";U();sp="https://routes.googleapis.com/directions/v2:computeRoutes",xn=class extends x{static{m(this,"RoutingSkill")}metadata={name:"routing",category:"information",description:'Routenberechnung mit Live-Traffic via Google Routes API. "route" berechnet Route mit Distanz, Dauer und Dauer im aktuellen Verkehr. "departure_time" empfiehlt wann man losfahren soll, um zu einer bestimmten Zeit anzukommen. Orte als Adresse oder "lat,lng" angeben.',riskLevel:"read",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["route","departure_time"],description:"Routing action"},origin:{type:"string",description:'Start-Adresse oder "lat,lng"'},destination:{type:"string",description:'Ziel-Adresse oder "lat,lng"'},departure_time:{type:"string",description:"ISO-Zeitpunkt f\xFCr Abfahrt (optional, f\xFCr Traffic-Berechnung)"},arrival_time:{type:"string",description:"ISO-Zeitpunkt gew\xFCnschte Ankunft (f\xFCr departure_time-Action)"},travel_mode:{type:"string",enum:["DRIVE","BICYCLE","WALK","TRANSIT"],description:"Fortbewegungsart (Standard: DRIVE)"}},required:["action","origin","destination"]}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};let r=e.origin,n=e.destination;if(!r)return{success:!1,error:'Missing required field "origin"'};if(!n)return{success:!1,error:'Missing required field "destination"'};try{switch(s){case"route":return await this.computeRoute(r,n,e.departure_time,e.travel_mode);case"departure_time":return await this.computeDepartureTime(r,n,e.arrival_time,e.travel_mode);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(o){return{success:!1,error:`Google Routes API error: ${o instanceof Error?o.message:String(o)}`}}}async computeRoute(e,t,s,r){let n=this.buildRequestBody(e,t,r,s),i=(await this.callRoutesApi(n)).routes?.[0];if(!i)return{success:!1,error:"Keine Route gefunden."};let a=(i.distanceMeters/1e3).toFixed(1),c=this.parseDuration(i.duration),d=this.parseDuration(i.staticDuration),u=c-d,p=["## Route","",`**${e}** \u2192 **${t}**`,"",`**Distanz:** ${a} km`,`**Fahrzeit (aktuell):** ${this.formatMinutes(c)}`,`**Fahrzeit (ohne Verkehr):** ${this.formatMinutes(d)}`];if(u>1&&p.push(`**Verkehrsverz\xF6gerung:** +${this.formatMinutes(u)}`),s){let f=new Date(new Date(s).getTime()+c*6e4);p.push(`**Gesch\xE4tzte Ankunft:** ${f.toLocaleString("de-AT")}`)}return{success:!0,data:{distanceKm:parseFloat(a),durationMinutes:c,staticDurationMinutes:d},display:p.join(`
717
740
  `)}}async computeDepartureTime(e,t,s,r){if(!s)return{success:!1,error:'Missing required field "arrival_time" for departure_time action'};let n=this.buildRequestBody(e,t,r),i=(await this.callRoutesApi(n)).routes?.[0];if(!i)return{success:!1,error:"Keine Route gefunden."};let a=this.parseDuration(i.duration),c=Math.max(5,Math.round(a*.15)),d=new Date(s),u=new Date(d.getTime()-(a+c)*6e4),p=["## Abfahrtszeit-Empfehlung","",`**Route:** ${e} \u2192 ${t}`,`**Gew\xFCnschte Ankunft:** ${d.toLocaleString("de-AT")}`,`**Gesch\xE4tzte Fahrzeit:** ${this.formatMinutes(a)} (inkl. Verkehr)`,`**Puffer:** ${c} min`,"",`**Empfohlene Abfahrt:** ${u.toLocaleString("de-AT")}`];return{success:!0,data:{departureTime:u.toISOString(),durationMinutes:a,bufferMinutes:c},display:p.join(`
718
- `)}}buildWaypoint(e){let t=e.match(/^(-?\d+\.?\d*)\s*,\s*(-?\d+\.?\d*)$/);return t?{location:{latLng:{latitude:parseFloat(t[1]),longitude:parseFloat(t[2])}}}:{address:e}}normalizeTimestamp(e){let t=new Date(e);if(isNaN(t.getTime()))return e;if(/[Zz]|[+-]\d{2}:\d{2}$/.test(e))return t.toISOString();let s=t.getTimezoneOffset(),r=s<=0?"+":"-",n=Math.abs(s),o=String(Math.floor(n/60)).padStart(2,"0"),i=String(n%60).padStart(2,"0"),a=m(c=>String(c).padStart(2,"0"),"pad");return`${t.getFullYear()}-${a(t.getMonth()+1)}-${a(t.getDate())}T${a(t.getHours())}:${a(t.getMinutes())}:${a(t.getSeconds())}${r}${o}:${i}`}buildRequestBody(e,t,s,r,n){let o={origin:this.buildWaypoint(e),destination:this.buildWaypoint(t),travelMode:s??"DRIVE",routingPreference:"TRAFFIC_AWARE"};if(r){let i=this.normalizeTimestamp(r);new Date(i).getTime()>Date.now()+6e4&&(o.departureTime=i)}if(n){let i=this.normalizeTimestamp(n);new Date(i).getTime()>Date.now()+6e4&&(o.arrivalTime=i)}return o}async callRoutesApi(e){let t=await fetch(Zm,{method:"POST",headers:{"Content-Type":"application/json","X-Goog-Api-Key":this.config.apiKey,"X-Goog-FieldMask":"routes.duration,routes.staticDuration,routes.distanceMeters,routes.legs"},body:JSON.stringify(e),signal:AbortSignal.timeout(15e3)});if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`HTTP ${t.status} \u2014 ${s.slice(0,300)}`)}return await t.json()}parseDuration(e){let t=e?.match(/(\d+)s/);return t?Math.round(parseInt(t[1],10)/60):0}formatMinutes(e){if(e<60)return`${e} min`;let t=Math.floor(e/60),s=e%60;return s>0?`${t} h ${s} min`:`${t} h`}}});var kl,vl,Qm,ep,tp,cs,Sl,Al,Il,fi,gi,xl,In,Rl=_(()=>{"use strict";U();kl=1.5,vl=4.79,Qm=5.75,ep=1.03,tp=new Date("2026-04-01T00:00:00+02:00"),cs=1.2,Sl=.1,Al=.58,Il=.04,fi=.32,gi=1.62,xl="https://api.awattar.at/v1/marketdata",In=class extends R{static{m(this,"EnergyPriceSkill")}metadata={name:"energy_price",category:"information",description:'Strompreise (aWATTar HOURLY Tarif, EPEX Spot AT). "current" zeigt den aktuellen Strompreis mit Aufschl\xFCsselung (Marktpreis, Netzentgelte, Abgaben, Brutto). "today" zeigt alle Stundenpreise f\xFCr heute. "tomorrow" zeigt Stundenpreise f\xFCr morgen (verf\xFCgbar ab ~14:00). "cheapest" findet die g\xFCnstigsten Stunden (Standard: 3 Stunden in den n\xE4chsten 24h). "average" zeigt den Durchschnittspreis f\xFCr heute oder ein bestimmtes Datum.',riskLevel:"read",version:"1.0.0",timeoutMs:15e3,inputSchema:{type:"object",properties:{action:{type:"string",enum:["current","today","tomorrow","cheapest","average","briefing"],description:"Aktion (briefing = kompakte Tages\xFCbersicht f\xFCr Morgenbriefing)"},hours:{type:"number",description:"F\xFCr cheapest: Anzahl g\xFCnstigster Stunden (Standard: 3)"},date:{type:"string",description:"ISO-Datum f\xFCr average (Standard: heute)"}},required:["action"]}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"current":return await this.current();case"today":return await this.dayPrices("today");case"tomorrow":return await this.dayPrices("tomorrow");case"cheapest":return await this.cheapest(e.hours);case"average":return await this.average(e.date);case"briefing":return await this.briefingSummary();default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`aWATTar API error: ${r instanceof Error?r.message:String(r)}`}}}async current(){let e=Date.now(),s=(await this.fetchMarketData()).find(i=>i.start_timestamp<=e&&i.end_timestamp>e);if(!s)return{success:!1,error:"Kein Marktpreis f\xFCr die aktuelle Stunde verf\xFCgbar."};let r=this.calculatePrice(s.marketprice),o=[`## Aktueller Strompreis (${this.formatHourRange(s.start_timestamp,s.end_timestamp)})`,""];return o.push(this.formatBreakdown(r)),{success:!0,data:r,display:o.join(`
719
- `)}}async dayPrices(e){let t=new Date,s=new Date(t);e==="tomorrow"&&s.setDate(s.getDate()+1);let r=new Date(s);r.setHours(0,0,0,0);let n=new Date(r);n.setDate(n.getDate()+1);let o=await this.fetchMarketData(r.getTime(),n.getTime());if(o.length===0)return{success:!1,error:`Keine Preisdaten f\xFCr ${e==="today"?"heute":"morgen"} verf\xFCgbar.${e==="tomorrow"?" Preise f\xFCr morgen sind ab ca. 14:00 verf\xFCgbar.":""}`};let i=e==="today"?"Heute":"Morgen",a=r.toLocaleDateString("de-AT"),c=[`## Strompreise ${i} (${a})`,""];c.push("| Uhrzeit | Markt ct/kWh | Brutto ct/kWh |"),c.push("|---|---|---|");let d=1/0,u=-1/0,p=0;for(let f of o){let h=this.calculatePrice(f.marketprice),y=this.formatHourRange(f.start_timestamp,f.end_timestamp),E=this.spotCtKwh(f.marketprice);c.push(`| ${y} | ${E.toFixed(2)} | ${h.bruttoCt.toFixed(2)} |`),d=Math.min(d,h.bruttoCt),u=Math.max(u,h.bruttoCt),p+=h.bruttoCt}let g=p/o.length;return c.push(""),c.push(`**Min:** ${d.toFixed(2)} ct/kWh | **Max:** ${u.toFixed(2)} ct/kWh | **\xD8:** ${g.toFixed(2)} ct/kWh`),{success:!0,data:{entries:o.length,min:d,max:u,avg:g},display:c.join(`
741
+ `)}}buildWaypoint(e){let t=e.match(/^(-?\d+\.?\d*)\s*,\s*(-?\d+\.?\d*)$/);return t?{location:{latLng:{latitude:parseFloat(t[1]),longitude:parseFloat(t[2])}}}:{address:e}}normalizeTimestamp(e){let t=new Date(e);if(isNaN(t.getTime()))return e;if(/[Zz]|[+-]\d{2}:\d{2}$/.test(e))return t.toISOString();let s=t.getTimezoneOffset(),r=s<=0?"+":"-",n=Math.abs(s),o=String(Math.floor(n/60)).padStart(2,"0"),i=String(n%60).padStart(2,"0"),a=m(c=>String(c).padStart(2,"0"),"pad");return`${t.getFullYear()}-${a(t.getMonth()+1)}-${a(t.getDate())}T${a(t.getHours())}:${a(t.getMinutes())}:${a(t.getSeconds())}${r}${o}:${i}`}buildRequestBody(e,t,s,r,n){let o={origin:this.buildWaypoint(e),destination:this.buildWaypoint(t),travelMode:s??"DRIVE",routingPreference:"TRAFFIC_AWARE"};if(r){let i=this.normalizeTimestamp(r);new Date(i).getTime()>Date.now()+6e4&&(o.departureTime=i)}if(n){let i=this.normalizeTimestamp(n);new Date(i).getTime()>Date.now()+6e4&&(o.arrivalTime=i)}return o}async callRoutesApi(e){let t=await fetch(sp,{method:"POST",headers:{"Content-Type":"application/json","X-Goog-Api-Key":this.config.apiKey,"X-Goog-FieldMask":"routes.duration,routes.staticDuration,routes.distanceMeters,routes.legs"},body:JSON.stringify(e),signal:AbortSignal.timeout(15e3)});if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`HTTP ${t.status} \u2014 ${s.slice(0,300)}`)}return await t.json()}parseDuration(e){let t=e?.match(/(\d+)s/);return t?Math.round(parseInt(t[1],10)/60):0}formatMinutes(e){if(e<60)return`${e} min`;let t=Math.floor(e/60),s=e%60;return s>0?`${t} h ${s} min`:`${t} h`}}});var Il,xl,rp,np,op,ds,Rl,Cl,Dl,yi,wi,Ll,Rn,Nl=_(()=>{"use strict";U();Il=1.5,xl=4.79,rp=5.75,np=1.03,op=new Date("2026-04-01T00:00:00+02:00"),ds=1.2,Rl=.1,Cl=.58,Dl=.04,yi=.32,wi=1.62,Ll="https://api.awattar.at/v1/marketdata",Rn=class extends x{static{m(this,"EnergyPriceSkill")}metadata={name:"energy_price",category:"information",description:'Strompreise (aWATTar HOURLY Tarif, EPEX Spot AT). "current" zeigt den aktuellen Strompreis mit Aufschl\xFCsselung (Marktpreis, Netzentgelte, Abgaben, Brutto). "today" zeigt alle Stundenpreise f\xFCr heute. "tomorrow" zeigt Stundenpreise f\xFCr morgen (verf\xFCgbar ab ~14:00). "cheapest" findet die g\xFCnstigsten Stunden (Standard: 3 Stunden in den n\xE4chsten 24h). "average" zeigt den Durchschnittspreis f\xFCr heute oder ein bestimmtes Datum.',riskLevel:"read",version:"1.0.0",timeoutMs:15e3,inputSchema:{type:"object",properties:{action:{type:"string",enum:["current","today","tomorrow","cheapest","average","briefing"],description:"Aktion (briefing = kompakte Tages\xFCbersicht f\xFCr Morgenbriefing)"},hours:{type:"number",description:"F\xFCr cheapest: Anzahl g\xFCnstigster Stunden (Standard: 3)"},date:{type:"string",description:"ISO-Datum f\xFCr average (Standard: heute)"}},required:["action"]}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"current":return await this.current();case"today":return await this.dayPrices("today");case"tomorrow":return await this.dayPrices("tomorrow");case"cheapest":return await this.cheapest(e.hours);case"average":return await this.average(e.date);case"briefing":return await this.briefingSummary();default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`aWATTar API error: ${r instanceof Error?r.message:String(r)}`}}}async current(){let e=Date.now(),s=(await this.fetchMarketData()).find(i=>i.start_timestamp<=e&&i.end_timestamp>e);if(!s)return{success:!1,error:"Kein Marktpreis f\xFCr die aktuelle Stunde verf\xFCgbar."};let r=this.calculatePrice(s.marketprice),o=[`## Aktueller Strompreis (${this.formatHourRange(s.start_timestamp,s.end_timestamp)})`,""];return o.push(this.formatBreakdown(r)),{success:!0,data:r,display:o.join(`
742
+ `)}}async dayPrices(e){let t=new Date,s=new Date(t);e==="tomorrow"&&s.setDate(s.getDate()+1);let r=new Date(s);r.setHours(0,0,0,0);let n=new Date(r);n.setDate(n.getDate()+1);let o=await this.fetchMarketData(r.getTime(),n.getTime());if(o.length===0)return{success:!1,error:`Keine Preisdaten f\xFCr ${e==="today"?"heute":"morgen"} verf\xFCgbar.${e==="tomorrow"?" Preise f\xFCr morgen sind ab ca. 14:00 verf\xFCgbar.":""}`};let i=e==="today"?"Heute":"Morgen",a=r.toLocaleDateString("de-AT"),c=[`## Strompreise ${i} (${a})`,""];c.push("| Uhrzeit | Markt ct/kWh | Brutto ct/kWh |"),c.push("|---|---|---|");let d=1/0,u=-1/0,p=0;for(let g of o){let h=this.calculatePrice(g.marketprice),y=this.formatHourRange(g.start_timestamp,g.end_timestamp),E=this.spotCtKwh(g.marketprice);c.push(`| ${y} | ${E.toFixed(2)} | ${h.bruttoCt.toFixed(2)} |`),d=Math.min(d,h.bruttoCt),u=Math.max(u,h.bruttoCt),p+=h.bruttoCt}let f=p/o.length;return c.push(""),c.push(`**Min:** ${d.toFixed(2)} ct/kWh | **Max:** ${u.toFixed(2)} ct/kWh | **\xD8:** ${f.toFixed(2)} ct/kWh`),{success:!0,data:{entries:o.length,min:d,max:u,avg:f},display:c.join(`
720
743
  `)}}async cheapest(e){let t=e??3,s=Date.now(),r=s+24*60*60*1e3,n=await this.fetchMarketData(s,r);if(n.length===0)return{success:!1,error:"Keine Preisdaten f\xFCr die n\xE4chsten 24 Stunden verf\xFCgbar."};let o=n.map(c=>({entry:c,breakdown:this.calculatePrice(c.marketprice)}));o.sort((c,d)=>c.breakdown.bruttoCt-d.breakdown.bruttoCt);let i=o.slice(0,Math.min(t,o.length));i.sort((c,d)=>c.entry.start_timestamp-d.entry.start_timestamp);let a=[`## ${t} g\xFCnstigste Stunden (n\xE4chste 24h)`,""];for(let{entry:c,breakdown:d}of i){let u=this.formatHourRange(c.start_timestamp,c.end_timestamp),p=new Date(c.start_timestamp).toLocaleDateString("de-AT",{weekday:"short"});a.push(`- **${p} ${u}**: ${d.bruttoCt.toFixed(2)} ct/kWh brutto (Markt: ${this.spotCtKwh(c.marketprice).toFixed(2)} ct/kWh)`)}return{success:!0,data:i.map(c=>({time:this.formatHourRange(c.entry.start_timestamp,c.entry.end_timestamp),bruttoCt:c.breakdown.bruttoCt})),display:a.join(`
721
744
  `)}}async average(e){let t;e?(t=new Date(e),t.setHours(0,0,0,0)):(t=new Date,t.setHours(0,0,0,0));let s=new Date(t);s.setDate(s.getDate()+1);let r=await this.fetchMarketData(t.getTime(),s.getTime());if(r.length===0)return{success:!1,error:`Keine Preisdaten f\xFCr ${t.toLocaleDateString("de-AT")} verf\xFCgbar.`};let n=0,o=0;for(let u of r)n+=this.spotCtKwh(u.marketprice),o+=this.calculatePrice(u.marketprice).bruttoCt;let i=n/r.length,a=o/r.length,c=t.toLocaleDateString("de-AT"),d=[`## Durchschnittspreis ${c}`,"",`**\xD8 Marktpreis:** ${i.toFixed(2)} ct/kWh`,`**\xD8 Brutto-Gesamtpreis:** ${a.toFixed(2)} ct/kWh`,"",`Basierend auf ${r.length} Stundenwerten.`];return{success:!0,data:{date:c,avgSpotCt:i,avgBruttoCt:a,hours:r.length},display:d.join(`
722
- `)}}async briefingSummary(){let e=Date.now(),t=new Date;t.setHours(0,0,0,0);let s=new Date(t);s.setDate(s.getDate()+1);let r=await this.fetchMarketData(t.getTime(),s.getTime());if(r.length===0)return{success:!1,error:"Keine Preisdaten f\xFCr heute verf\xFCgbar."};let n=r.map(y=>({time:this.formatHourRange(y.start_timestamp,y.end_timestamp),bruttoCt:this.calculatePrice(y.marketprice).bruttoCt,start:y.start_timestamp,isCurrent:y.start_timestamp<=e&&y.end_timestamp>e,isPast:y.end_timestamp<=e})),o=n.find(y=>y.isCurrent),i=n.filter(y=>!y.isPast),a=n.map(y=>y.bruttoCt),c=a.reduce((y,E)=>y+E,0)/a.length,d=Math.min(...a),u=Math.max(...a),p=[...i].sort((y,E)=>y.bruttoCt-E.bruttoCt),g=p.slice(0,3),f=p.slice(-3).reverse(),h=[];o&&h.push(`**Aktuell (${o.time}):** ${o.bruttoCt.toFixed(2)} ct/kWh brutto`),h.push(`**Tagesdurchschnitt:** ${c.toFixed(2)} ct/kWh | **Min:** ${d.toFixed(2)} | **Max:** ${u.toFixed(2)}`),h.push(""),h.push("**G\xFCnstigste 3 Stunden (Rest des Tages):**");for(let y of g)h.push(`- ${y.time}: ${y.bruttoCt.toFixed(2)} ct/kWh`);h.push(""),h.push("**Teuerste 3 Stunden (Rest des Tages):**");for(let y of f)h.push(`- ${y.time}: ${y.bruttoCt.toFixed(2)} ct/kWh`);return{success:!0,data:{currentCt:o?.bruttoCt,avgCt:c,minCt:d,maxCt:u,cheapest:g.map(y=>({time:y.time,bruttoCt:y.bruttoCt})),expensive:f.map(y=>({time:y.time,bruttoCt:y.bruttoCt}))},display:h.join(`
723
- `)}}spotCtKwh(e){return e/10}calculatePrice(e){let t=this.spotCtKwh(e),s=new Date<tp,n=(s?t*ep:t)+kl,o=this.getGridCosts(),i=o.usageCt+o.lossCt,a=Sl+Al+Il,c=n+i+a,d=c*cs;return{spotCt:t,ausgleichCt:s?t*.03:0,aufschlagCt:kl,energieNettoCt:n,netznutzungCt:o.usageCt,netzverlustCt:o.lossCt,eAbgabeCt:Sl,oekoArbeitCt:Al,oekoVerlustCt:Il,gesamtNettoCt:c,ustCt:c*.2,bruttoCt:d,gridName:o.name,hasGrid:o.usageCt>0}}getGridCosts(){let e=this.config?.gridUsageCt??0,t=this.config?.gridLossCt??0,s=this.config?.gridName??"";return e>0?{name:s,usageCt:e,lossCt:t}:{name:"",usageCt:0,lossCt:0}}formatBreakdown(e){let t=["| Komponente | ct/kWh |","|---|---:|",`| EPEX Spot Marktpreis | ${e.spotCt.toFixed(2)} |`];e.ausgleichCt>0&&t.push(`| Ausgleichsenergie (3%) | ${e.ausgleichCt.toFixed(2)} |`),t.push(`| aWATTar Aufschlag | ${e.aufschlagCt.toFixed(2)} |`),t.push(`| **Energie netto** | **${e.energieNettoCt.toFixed(2)}** |`),e.hasGrid&&(t.push(`| Netznutzung (${e.gridName}) | ${e.netznutzungCt.toFixed(2)} |`),t.push(`| Netzverlust | ${e.netzverlustCt.toFixed(2)} |`)),t.push(`| Elektrizit\xE4tsabgabe | ${e.eAbgabeCt.toFixed(2)} |`),t.push(`| \xD6kostrom-F\xF6rderbeitrag | ${e.oekoArbeitCt.toFixed(2)} |`),t.push(`| \xD6kostrom-Verlust | ${e.oekoVerlustCt.toFixed(2)} |`),t.push(`| **Gesamt netto** | **${e.gesamtNettoCt.toFixed(2)}** |`),t.push(`| USt (20%) | ${e.ustCt.toFixed(2)} |`),t.push(`| **Gesamt brutto** | **${e.bruttoCt.toFixed(2)}** |`),e.hasGrid||(t.push(""),t.push("*Netzentgelte nicht inkludiert \u2014 Netzkosten via `alfred setup` oder ENV konfigurieren*"));let s=this.config?.gridCapacityFee??0,r=this.config?.gridMeterFee??0;if(t.push(""),t.push("**Fixe Monatskosten (netto \u2192 brutto):**"),t.push(`- aWATTar Grundgeb\xFChr: ${vl.toFixed(2)} \u20AC \u2192 ${Qm.toFixed(2)} \u20AC`),s>0){let i=this.config?.gridName?` (${this.config.gridName})`:"";t.push(`- Leistungspauschale${i}: ${s.toFixed(2)} \u20AC \u2192 ${(s*cs).toFixed(2)} \u20AC`)}r>0&&t.push(`- Messentgelt: ${r.toFixed(2)} \u20AC \u2192 ${(r*cs).toFixed(2)} \u20AC`),t.push(`- \xD6kostrom-F\xF6rderpauschale: ${fi.toFixed(2)} \u20AC \u2192 ${(fi*cs).toFixed(2)} \u20AC`),t.push(`- Erneuerbaren-F\xF6rderpauschale: ${gi.toFixed(2)} \u20AC \u2192 ${(gi*cs).toFixed(2)} \u20AC`);let n=vl+fi+gi+s+r,o=n*cs;return t.push(`- **Summe:** ${n.toFixed(2)} \u20AC \u2192 **${o.toFixed(2)} \u20AC brutto/Monat**`),t.join(`
724
- `)}async fetchMarketData(e,t){let s=new URLSearchParams;e!=null&&s.set("start",e.toString()),t!=null&&s.set("end",t.toString());let r=s.toString()?`${xl}?${s}`:xl,n=await fetch(r,{signal:AbortSignal.timeout(1e4)});if(!n.ok){let i=await n.text().catch(()=>"");throw new Error(`HTTP ${n.status} \u2014 ${i.slice(0,300)}`)}return(await n.json()).data||[]}formatHourRange(e,t){let s=new Date(e).toLocaleTimeString("de-AT",{hour:"2-digit",minute:"2-digit"}),r=new Date(t).toLocaleTimeString("de-AT",{hour:"2-digit",minute:"2-digit"});return`${s}\u2013${r}`}}});var ls,Cl=_(()=>{"use strict";U();ls=class extends R{static{m(this,"TransitSkill")}metadata={name:"transit_search",category:"information",description:'\xD6ffentlicher Nahverkehr in \xD6sterreich (\xD6BB, Wiener Linien, etc.). "search_stop" sucht Haltestellen nach Name. "journeys" berechnet Verbindungen zwischen zwei Orten (Name oder Stop-ID). "departures" zeigt Abfahrten an einer Haltestelle (Stop-ID erforderlich, zuerst search_stop verwenden). Deckt Busse, Stra\xDFenbahnen, U-Bahn, S-Bahn, Regionalz\xFCge und Fernz\xFCge ab.',riskLevel:"read",version:"1.0.0",timeoutMs:3e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["journeys","departures","search_stop"],description:"Transit action"},query:{type:"string",description:"Haltestellenname f\xFCr search_stop"},from:{type:"string",description:"Start-Haltestelle (Name oder ID) f\xFCr journeys"},to:{type:"string",description:"Ziel-Haltestelle (Name oder ID) f\xFCr journeys"},stop_id:{type:"string",description:"Stop-ID f\xFCr departures (von search_stop erhalten)"},departure:{type:"string",description:"ISO-Zeitpunkt f\xFCr gew\xFCnschte Abfahrt (optional)"},arrival:{type:"string",description:"ISO-Zeitpunkt f\xFCr gew\xFCnschte Ankunft (optional, nur f\xFCr journeys)"},results:{type:"number",description:"Anzahl Ergebnisse (Standard: 3)"},duration:{type:"number",description:"Zeitfenster in Minuten f\xFCr departures (Standard: 30)"}},required:["action"]}};client;constructor(e){super(),this.client=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"search_stop":return await this.searchStop(e);case"journeys":return await this.findJourneys(e);case"departures":return await this.findDepartures(e);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Transit API error: ${r instanceof Error?r.message:String(r)}`}}}async searchStop(e){let t=e.query;if(!t)return{success:!1,error:'Missing required field "query" for search_stop'};let s=await this.client.searchStops(t);if(s.length===0)return{success:!0,data:[],display:`Keine Haltestellen gefunden f\xFCr "${t}".`};let r=["## Haltestellen",""];for(let n of s.slice(0,10))r.push(`- **${n.name}** (ID: \`${n.id}\`)`);return{success:!0,data:s,display:r.join(`
745
+ `)}}async briefingSummary(){let e=Date.now(),t=new Date;t.setHours(0,0,0,0);let s=new Date(t);s.setDate(s.getDate()+1);let r=await this.fetchMarketData(t.getTime(),s.getTime());if(r.length===0)return{success:!1,error:"Keine Preisdaten f\xFCr heute verf\xFCgbar."};let n=r.map(y=>({time:this.formatHourRange(y.start_timestamp,y.end_timestamp),bruttoCt:this.calculatePrice(y.marketprice).bruttoCt,start:y.start_timestamp,isCurrent:y.start_timestamp<=e&&y.end_timestamp>e,isPast:y.end_timestamp<=e})),o=n.find(y=>y.isCurrent),i=n.filter(y=>!y.isPast),a=n.map(y=>y.bruttoCt),c=a.reduce((y,E)=>y+E,0)/a.length,d=Math.min(...a),u=Math.max(...a),p=[...i].sort((y,E)=>y.bruttoCt-E.bruttoCt),f=p.slice(0,3),g=p.slice(-3).reverse(),h=[];o&&h.push(`**Aktuell (${o.time}):** ${o.bruttoCt.toFixed(2)} ct/kWh brutto`),h.push(`**Tagesdurchschnitt:** ${c.toFixed(2)} ct/kWh | **Min:** ${d.toFixed(2)} | **Max:** ${u.toFixed(2)}`),h.push(""),h.push("**G\xFCnstigste 3 Stunden (Rest des Tages):**");for(let y of f)h.push(`- ${y.time}: ${y.bruttoCt.toFixed(2)} ct/kWh`);h.push(""),h.push("**Teuerste 3 Stunden (Rest des Tages):**");for(let y of g)h.push(`- ${y.time}: ${y.bruttoCt.toFixed(2)} ct/kWh`);return{success:!0,data:{currentCt:o?.bruttoCt,avgCt:c,minCt:d,maxCt:u,cheapest:f.map(y=>({time:y.time,bruttoCt:y.bruttoCt})),expensive:g.map(y=>({time:y.time,bruttoCt:y.bruttoCt}))},display:h.join(`
746
+ `)}}spotCtKwh(e){return e/10}calculatePrice(e){let t=this.spotCtKwh(e),s=new Date<op,n=(s?t*np:t)+Il,o=this.getGridCosts(),i=o.usageCt+o.lossCt,a=Rl+Cl+Dl,c=n+i+a,d=c*ds;return{spotCt:t,ausgleichCt:s?t*.03:0,aufschlagCt:Il,energieNettoCt:n,netznutzungCt:o.usageCt,netzverlustCt:o.lossCt,eAbgabeCt:Rl,oekoArbeitCt:Cl,oekoVerlustCt:Dl,gesamtNettoCt:c,ustCt:c*.2,bruttoCt:d,gridName:o.name,hasGrid:o.usageCt>0}}getGridCosts(){let e=this.config?.gridUsageCt??0,t=this.config?.gridLossCt??0,s=this.config?.gridName??"";return e>0?{name:s,usageCt:e,lossCt:t}:{name:"",usageCt:0,lossCt:0}}formatBreakdown(e){let t=["| Komponente | ct/kWh |","|---|---:|",`| EPEX Spot Marktpreis | ${e.spotCt.toFixed(2)} |`];e.ausgleichCt>0&&t.push(`| Ausgleichsenergie (3%) | ${e.ausgleichCt.toFixed(2)} |`),t.push(`| aWATTar Aufschlag | ${e.aufschlagCt.toFixed(2)} |`),t.push(`| **Energie netto** | **${e.energieNettoCt.toFixed(2)}** |`),e.hasGrid&&(t.push(`| Netznutzung (${e.gridName}) | ${e.netznutzungCt.toFixed(2)} |`),t.push(`| Netzverlust | ${e.netzverlustCt.toFixed(2)} |`)),t.push(`| Elektrizit\xE4tsabgabe | ${e.eAbgabeCt.toFixed(2)} |`),t.push(`| \xD6kostrom-F\xF6rderbeitrag | ${e.oekoArbeitCt.toFixed(2)} |`),t.push(`| \xD6kostrom-Verlust | ${e.oekoVerlustCt.toFixed(2)} |`),t.push(`| **Gesamt netto** | **${e.gesamtNettoCt.toFixed(2)}** |`),t.push(`| USt (20%) | ${e.ustCt.toFixed(2)} |`),t.push(`| **Gesamt brutto** | **${e.bruttoCt.toFixed(2)}** |`),e.hasGrid||(t.push(""),t.push("*Netzentgelte nicht inkludiert \u2014 Netzkosten via `alfred setup` oder ENV konfigurieren*"));let s=this.config?.gridCapacityFee??0,r=this.config?.gridMeterFee??0;if(t.push(""),t.push("**Fixe Monatskosten (netto \u2192 brutto):**"),t.push(`- aWATTar Grundgeb\xFChr: ${xl.toFixed(2)} \u20AC \u2192 ${rp.toFixed(2)} \u20AC`),s>0){let i=this.config?.gridName?` (${this.config.gridName})`:"";t.push(`- Leistungspauschale${i}: ${s.toFixed(2)} \u20AC \u2192 ${(s*ds).toFixed(2)} \u20AC`)}r>0&&t.push(`- Messentgelt: ${r.toFixed(2)} \u20AC \u2192 ${(r*ds).toFixed(2)} \u20AC`),t.push(`- \xD6kostrom-F\xF6rderpauschale: ${yi.toFixed(2)} \u20AC \u2192 ${(yi*ds).toFixed(2)} \u20AC`),t.push(`- Erneuerbaren-F\xF6rderpauschale: ${wi.toFixed(2)} \u20AC \u2192 ${(wi*ds).toFixed(2)} \u20AC`);let n=xl+yi+wi+s+r,o=n*ds;return t.push(`- **Summe:** ${n.toFixed(2)} \u20AC \u2192 **${o.toFixed(2)} \u20AC brutto/Monat**`),t.join(`
747
+ `)}async fetchMarketData(e,t){let s=new URLSearchParams;e!=null&&s.set("start",e.toString()),t!=null&&s.set("end",t.toString());let r=s.toString()?`${Ll}?${s}`:Ll,n=await fetch(r,{signal:AbortSignal.timeout(1e4)});if(!n.ok){let i=await n.text().catch(()=>"");throw new Error(`HTTP ${n.status} \u2014 ${i.slice(0,300)}`)}return(await n.json()).data||[]}formatHourRange(e,t){let s=new Date(e).toLocaleTimeString("de-AT",{hour:"2-digit",minute:"2-digit"}),r=new Date(t).toLocaleTimeString("de-AT",{hour:"2-digit",minute:"2-digit"});return`${s}\u2013${r}`}}});var us,Ml=_(()=>{"use strict";U();us=class extends x{static{m(this,"TransitSkill")}metadata={name:"transit_search",category:"information",description:'\xD6ffentlicher Nahverkehr in \xD6sterreich (\xD6BB, Wiener Linien, etc.). "search_stop" sucht Haltestellen nach Name. "journeys" berechnet Verbindungen zwischen zwei Orten (Name oder Stop-ID). "departures" zeigt Abfahrten an einer Haltestelle (Stop-ID erforderlich, zuerst search_stop verwenden). Deckt Busse, Stra\xDFenbahnen, U-Bahn, S-Bahn, Regionalz\xFCge und Fernz\xFCge ab.',riskLevel:"read",version:"1.0.0",timeoutMs:3e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["journeys","departures","search_stop"],description:"Transit action"},query:{type:"string",description:"Haltestellenname f\xFCr search_stop"},from:{type:"string",description:"Start-Haltestelle (Name oder ID) f\xFCr journeys"},to:{type:"string",description:"Ziel-Haltestelle (Name oder ID) f\xFCr journeys"},stop_id:{type:"string",description:"Stop-ID f\xFCr departures (von search_stop erhalten)"},departure:{type:"string",description:"ISO-Zeitpunkt f\xFCr gew\xFCnschte Abfahrt (optional)"},arrival:{type:"string",description:"ISO-Zeitpunkt f\xFCr gew\xFCnschte Ankunft (optional, nur f\xFCr journeys)"},results:{type:"number",description:"Anzahl Ergebnisse (Standard: 3)"},duration:{type:"number",description:"Zeitfenster in Minuten f\xFCr departures (Standard: 30)"}},required:["action"]}};client;constructor(e){super(),this.client=e}async execute(e,t){let s=e.action;if(!s)return{success:!1,error:'Missing required field "action"'};try{switch(s){case"search_stop":return await this.searchStop(e);case"journeys":return await this.findJourneys(e);case"departures":return await this.findDepartures(e);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(r){return{success:!1,error:`Transit API error: ${r instanceof Error?r.message:String(r)}`}}}async searchStop(e){let t=e.query;if(!t)return{success:!1,error:'Missing required field "query" for search_stop'};let s=await this.client.searchStops(t);if(s.length===0)return{success:!0,data:[],display:`Keine Haltestellen gefunden f\xFCr "${t}".`};let r=["## Haltestellen",""];for(let n of s.slice(0,10))r.push(`- **${n.name}** (ID: \`${n.id}\`)`);return{success:!0,data:s,display:r.join(`
725
748
  `)}}async findJourneys(e){let t=e.from,s=e.to;if(!t)return{success:!1,error:'Missing required field "from" for journeys'};if(!s)return{success:!1,error:'Missing required field "to" for journeys'};let r={};e.departure&&(r.departure=new Date(e.departure)),e.arrival&&(r.arrival=new Date(e.arrival)),e.results&&(r.results=e.results);let n=await this.client.journeys(t,s,r);if(n.length===0)return{success:!0,data:[],display:`Keine Verbindungen gefunden von ${t} nach ${s}.`};let o=[`## Verbindungen: ${t} \u2192 ${s}`,""];for(let i=0;i<n.length;i++){let a=n[i],c=this.formatTime(a.departure),d=this.formatTime(a.arrival);o.push(`### Verbindung ${i+1}: ${c} \u2192 ${d} (${a.duration} min, ${a.transfers} Umstieg${a.transfers!==1?"e":""})`);for(let u of a.legs)u.walking?o.push(` - \u{1F6B6} Fu\xDFweg \u2192 ${u.destination} (${this.formatTime(u.departure)}\u2013${this.formatTime(u.arrival)})`):o.push(` - ${u.line||u.mode} Ri. ${u.direction||u.destination}: ${u.origin} ${this.formatTime(u.departure)} \u2192 ${u.destination} ${this.formatTime(u.arrival)}`);o.push("")}return{success:!0,data:n,display:o.join(`
726
749
  `)}}async findDepartures(e){let t=e.stop_id;if(!t)return{success:!1,error:'Missing required field "stop_id" for departures. Use search_stop first to find the stop ID.'};let s={};e.departure&&(s.when=new Date(e.departure)),e.duration&&(s.duration=e.duration);let r=await this.client.departures(t,s);if(r.length===0)return{success:!0,data:[],display:"Keine Abfahrten gefunden."};let n=["## Abfahrten",""];for(let o of r){let i=this.formatTime(o.when),a=o.delay?` (+${o.delay} min)`:"",c=o.platform?` [Steig ${o.platform}]`:"";n.push(`- **${i}${a}** ${o.line} \u2192 ${o.direction}${c}`)}return{success:!0,data:r,display:n.join(`
727
- `)}}formatTime(e){try{return new Date(e).toLocaleTimeString("de-AT",{hour:"2-digit",minute:"2-digit"})}catch{return e}}}});import yi from"node:fs";import Dl from"node:path";function Ll(l){return l.length<=4?"****":"*".repeat(l.length-4)+l.slice(-4)}function sp(){let l=process.cwd();for(let e=0;e<10;e++){let t=Dl.join(l,".env");if(yi.existsSync(t))return t;let s=Dl.dirname(l);if(s===l)break;l=s}return null}var ds,us,Nl=_(()=>{"use strict";U();ds={proxmox:{label:"Proxmox VE",fields:[{env:"ALFRED_PROXMOX_BASE_URL",label:"Base URL (e.g. https://pve.local:8006)",required:!0},{env:"ALFRED_PROXMOX_TOKEN_ID",label:"API Token ID (user@realm!name)",required:!0},{env:"ALFRED_PROXMOX_TOKEN_SECRET",label:"API Token Secret",required:!0,secret:!0}]},unifi:{label:"UniFi Network",fields:[{env:"ALFRED_UNIFI_BASE_URL",label:"Base URL (e.g. https://unifi.local)",required:!0},{env:"ALFRED_UNIFI_API_KEY",label:"API Key (preferred, UniFi OS)",secret:!0},{env:"ALFRED_UNIFI_USERNAME",label:"Username (alternative to API key)"},{env:"ALFRED_UNIFI_PASSWORD",label:"Password (alternative to API key)",secret:!0},{env:"ALFRED_UNIFI_SITE",label:'Site name (default: "default")'}]},homeassistant:{label:"Home Assistant",fields:[{env:"ALFRED_HOMEASSISTANT_URL",label:"Base URL (e.g. http://homeassistant.local:8123)",required:!0},{env:"ALFRED_HOMEASSISTANT_TOKEN",label:"Long-Lived Access Token",required:!0,secret:!0}]},contacts:{label:"Contacts",fields:[{env:"ALFRED_CONTACTS_PROVIDER",label:"Provider (carddav, google, microsoft)",required:!0},{env:"ALFRED_CARDDAV_CONTACTS_SERVER_URL",label:"CardDAV Server URL (if carddav)"},{env:"ALFRED_CARDDAV_CONTACTS_USERNAME",label:"CardDAV Username (if carddav)"},{env:"ALFRED_CARDDAV_CONTACTS_PASSWORD",label:"CardDAV Password (if carddav)",secret:!0},{env:"ALFRED_GOOGLE_CONTACTS_CLIENT_ID",label:"Google Client ID (if google)",secret:!0},{env:"ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET",label:"Google Client Secret (if google)",secret:!0},{env:"ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN",label:"Google Refresh Token (if google)",secret:!0},{env:"ALFRED_MICROSOFT_CONTACTS_CLIENT_ID",label:"Microsoft Client ID (if microsoft)",secret:!0},{env:"ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET",label:"Microsoft Client Secret (if microsoft)",secret:!0},{env:"ALFRED_MICROSOFT_CONTACTS_TENANT_ID",label:"Microsoft Tenant ID (if microsoft)"},{env:"ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN",label:"Microsoft Refresh Token (if microsoft)",secret:!0}]},docker:{label:"Docker",fields:[{env:"ALFRED_DOCKER_SOCKET_PATH",label:"Docker socket path (e.g. /var/run/docker.sock)"},{env:"ALFRED_DOCKER_HOST",label:"Docker host (e.g. http://192.168.1.10:2375)"}]}},us=class extends R{static{m(this,"ConfigureSkill")}reloadCallback;setReloadCallback(e){this.reloadCallback=e}metadata={name:"configure",category:"core",description:'Configure Alfred services (Proxmox, UniFi, Home Assistant, Contacts, Docker) by writing environment variables. Use action "list_services" to see available services. Use action "show" to check current config of a service. Use action "set" to write config \u2014 provide service name and values. After setting config, the service is activated immediately \u2014 no restart needed.',riskLevel:"admin",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_services","show","set"],description:"Action to perform"},service:{type:"string",enum:["proxmox","unifi","homeassistant","contacts","docker"],description:"Service to configure (required for show/set)"},values:{type:"object",description:'Key-value pairs to set. Keys are the ENV variable names (e.g. ALFRED_PROXMOX_BASE_URL). Only for action "set".'}},required:["action"]}};async execute(e,t){let s=e.action;switch(s){case"list_services":return this.listServices();case"show":return this.showService(e.service);case"set":return this.setService(e.service,e.values);default:return{success:!1,error:`Unknown action "${s}". Use list_services, show, or set.`}}}listServices(){let e=["| Service | Status | ENV Prefix |","|---|---|---|"];for(let[t,s]of Object.entries(ds)){let n=s.fields.filter(i=>i.required).every(i=>!!process.env[i.env])?"configured":"not configured",o=`ALFRED_${t.toUpperCase()}_*`;e.push(`| ${s.label} | ${n} | \`${o}\` |`)}return{success:!0,data:Object.keys(ds),display:e.join(`
728
- `)}}showService(e){let t=ds[e];if(!t)return{success:!1,error:`Unknown service "${e}". Available: ${Object.keys(ds).join(", ")}`};let s=[`**${t.label}** configuration:
729
- `],r={};for(let n of t.fields){let o=process.env[n.env];r[n.env]=o;let i=o?n.secret?Ll(o):o:"_not set_",a=n.required?" (required)":"";s.push(`- \`${n.env}\`: ${i}${a}`)}return{success:!0,data:r,display:s.join(`
730
- `)}}async setService(e,t){let s=ds[e];if(!s)return{success:!1,error:`Unknown service "${e}". Available: ${Object.keys(ds).join(", ")}`};if(!t||Object.keys(t).length===0)return{success:!1,error:`No values provided. Pass an object with ENV variable names as keys.
750
+ `)}}formatTime(e){try{return new Date(e).toLocaleTimeString("de-AT",{hour:"2-digit",minute:"2-digit"})}catch{return e}}}});import Ti from"node:fs";import Ol from"node:path";function Pl(l){return l.length<=4?"****":"*".repeat(l.length-4)+l.slice(-4)}function ip(){let l=process.cwd();for(let e=0;e<10;e++){let t=Ol.join(l,".env");if(Ti.existsSync(t))return t;let s=Ol.dirname(l);if(s===l)break;l=s}return null}var ms,ps,Ul=_(()=>{"use strict";U();ms={proxmox:{label:"Proxmox VE",fields:[{env:"ALFRED_PROXMOX_BASE_URL",label:"Base URL (e.g. https://pve.local:8006)",required:!0},{env:"ALFRED_PROXMOX_TOKEN_ID",label:"API Token ID (user@realm!name)",required:!0},{env:"ALFRED_PROXMOX_TOKEN_SECRET",label:"API Token Secret",required:!0,secret:!0}]},unifi:{label:"UniFi Network",fields:[{env:"ALFRED_UNIFI_BASE_URL",label:"Base URL (e.g. https://unifi.local)",required:!0},{env:"ALFRED_UNIFI_API_KEY",label:"API Key (preferred, UniFi OS)",secret:!0},{env:"ALFRED_UNIFI_USERNAME",label:"Username (alternative to API key)"},{env:"ALFRED_UNIFI_PASSWORD",label:"Password (alternative to API key)",secret:!0},{env:"ALFRED_UNIFI_SITE",label:'Site name (default: "default")'}]},homeassistant:{label:"Home Assistant",fields:[{env:"ALFRED_HOMEASSISTANT_URL",label:"Base URL (e.g. http://homeassistant.local:8123)",required:!0},{env:"ALFRED_HOMEASSISTANT_TOKEN",label:"Long-Lived Access Token",required:!0,secret:!0}]},contacts:{label:"Contacts",fields:[{env:"ALFRED_CONTACTS_PROVIDER",label:"Provider (carddav, google, microsoft)",required:!0},{env:"ALFRED_CARDDAV_CONTACTS_SERVER_URL",label:"CardDAV Server URL (if carddav)"},{env:"ALFRED_CARDDAV_CONTACTS_USERNAME",label:"CardDAV Username (if carddav)"},{env:"ALFRED_CARDDAV_CONTACTS_PASSWORD",label:"CardDAV Password (if carddav)",secret:!0},{env:"ALFRED_GOOGLE_CONTACTS_CLIENT_ID",label:"Google Client ID (if google)",secret:!0},{env:"ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET",label:"Google Client Secret (if google)",secret:!0},{env:"ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN",label:"Google Refresh Token (if google)",secret:!0},{env:"ALFRED_MICROSOFT_CONTACTS_CLIENT_ID",label:"Microsoft Client ID (if microsoft)",secret:!0},{env:"ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET",label:"Microsoft Client Secret (if microsoft)",secret:!0},{env:"ALFRED_MICROSOFT_CONTACTS_TENANT_ID",label:"Microsoft Tenant ID (if microsoft)"},{env:"ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN",label:"Microsoft Refresh Token (if microsoft)",secret:!0}]},docker:{label:"Docker",fields:[{env:"ALFRED_DOCKER_SOCKET_PATH",label:"Docker socket path (e.g. /var/run/docker.sock)"},{env:"ALFRED_DOCKER_HOST",label:"Docker host (e.g. http://192.168.1.10:2375)"}]}},ps=class extends x{static{m(this,"ConfigureSkill")}reloadCallback;setReloadCallback(e){this.reloadCallback=e}metadata={name:"configure",category:"core",description:'Configure Alfred services (Proxmox, UniFi, Home Assistant, Contacts, Docker) by writing environment variables. Use action "list_services" to see available services. Use action "show" to check current config of a service. Use action "set" to write config \u2014 provide service name and values. After setting config, the service is activated immediately \u2014 no restart needed.',riskLevel:"admin",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_services","show","set"],description:"Action to perform"},service:{type:"string",enum:["proxmox","unifi","homeassistant","contacts","docker"],description:"Service to configure (required for show/set)"},values:{type:"object",description:'Key-value pairs to set. Keys are the ENV variable names (e.g. ALFRED_PROXMOX_BASE_URL). Only for action "set".'}},required:["action"]}};async execute(e,t){let s=e.action;switch(s){case"list_services":return this.listServices();case"show":return this.showService(e.service);case"set":return this.setService(e.service,e.values);default:return{success:!1,error:`Unknown action "${s}". Use list_services, show, or set.`}}}listServices(){let e=["| Service | Status | ENV Prefix |","|---|---|---|"];for(let[t,s]of Object.entries(ms)){let n=s.fields.filter(i=>i.required).every(i=>!!process.env[i.env])?"configured":"not configured",o=`ALFRED_${t.toUpperCase()}_*`;e.push(`| ${s.label} | ${n} | \`${o}\` |`)}return{success:!0,data:Object.keys(ms),display:e.join(`
751
+ `)}}showService(e){let t=ms[e];if(!t)return{success:!1,error:`Unknown service "${e}". Available: ${Object.keys(ms).join(", ")}`};let s=[`**${t.label}** configuration:
752
+ `],r={};for(let n of t.fields){let o=process.env[n.env];r[n.env]=o;let i=o?n.secret?Pl(o):o:"_not set_",a=n.required?" (required)":"";s.push(`- \`${n.env}\`: ${i}${a}`)}return{success:!0,data:r,display:s.join(`
753
+ `)}}async setService(e,t){let s=ms[e];if(!s)return{success:!1,error:`Unknown service "${e}". Available: ${Object.keys(ms).join(", ")}`};if(!t||Object.keys(t).length===0)return{success:!1,error:`No values provided. Pass an object with ENV variable names as keys.
731
754
 
732
755
  Available keys for ${s.label}:
733
756
  ${s.fields.map(d=>`- \`${d.env}\`: ${d.label}${d.required?" (required)":""}`).join(`
734
- `)}`};let r=new Set(s.fields.map(d=>d.env));for(let d of Object.keys(t))if(!r.has(d))return{success:!1,error:`Invalid key "${d}" for ${s.label}. Valid keys: ${[...r].join(", ")}`};let n=sp();if(!n)return{success:!1,error:"Could not find .env file. Run `alfred setup` first or create a .env in your project root."};let o=yi.readFileSync(n,"utf-8"),i=[];for(let[d,u]of Object.entries(t)){let p=u.replace(/\n/g,"\\n"),g=new RegExp(`^#?\\s*${d}=.*$`,"m");g.test(o)?o=o.replace(g,`${d}=${p}`):o=o.trimEnd()+`
757
+ `)}`};let r=new Set(s.fields.map(d=>d.env));for(let d of Object.keys(t))if(!r.has(d))return{success:!1,error:`Invalid key "${d}" for ${s.label}. Valid keys: ${[...r].join(", ")}`};let n=ip();if(!n)return{success:!1,error:"Could not find .env file. Run `alfred setup` first or create a .env in your project root."};let o=Ti.readFileSync(n,"utf-8"),i=[];for(let[d,u]of Object.entries(t)){let p=u.replace(/\n/g,"\\n"),f=new RegExp(`^#?\\s*${d}=.*$`,"m");f.test(o)?o=o.replace(f,`${d}=${p}`):o=o.trimEnd()+`
735
758
  ${d}=${p}
736
- `,i.push(d)}yi.writeFileSync(n,o,"utf-8");let a=s.fields.filter(d=>d.required).filter(d=>!t[d.env]&&!process.env[d.env]).map(d=>`\`${d.env}\``),c=[`Written to \`${n}\`:
737
- `,...i.map(d=>`- \`${d}\` = ${s.fields.find(u=>u.env===d)?.secret?Ll(t[d]):t[d]}`)];if(a.length>0)c.push(`
759
+ `,i.push(d)}Ti.writeFileSync(n,o,"utf-8");let a=s.fields.filter(d=>d.required).filter(d=>!t[d.env]&&!process.env[d.env]).map(d=>`\`${d.env}\``),c=[`Written to \`${n}\`:
760
+ `,...i.map(d=>`- \`${d}\` = ${s.fields.find(u=>u.env===d)?.secret?Pl(t[d]):t[d]}`)];if(a.length>0)c.push(`
738
761
  **Still missing:** ${a.join(", ")}`);else if(this.reloadCallback){let d=await this.reloadCallback(e);d.success?c.push(`
739
762
  **${s.label} wurde aktiviert.** Du kannst es jetzt sofort nutzen.`):c.push(`
740
763
  **${s.label} is fully configured.** Hot-Reload fehlgeschlagen: ${d.error??"unbekannter Fehler"}. Restart Alfred: \`alfred start\``)}else c.push(`
741
764
  **${s.label} is fully configured.** Restart Alfred to activate: \`alfred start\``);return{success:!0,data:{envPath:n,written:i},display:c.join(`
742
- `)}}};m(Ll,"maskValue");m(sp,"findEnvFile")});var xn,Ml=_(()=>{"use strict";U();xn=class extends R{static{m(this,"MonitorSkill")}metadata={name:"monitor",category:"infrastructure",description:"Deterministic infrastructure health checks without LLM. Checks Proxmox cluster, UniFi network, and Home Assistant for issues. Returns alerts only when problems are detected \u2014 empty display means all OK.",riskLevel:"read",version:"1.0.0",timeoutMs:6e4,inputSchema:{type:"object",properties:{checks:{type:"array",items:{type:"string",enum:["proxmox","unifi","homeassistant"]},description:"Which checks to run (default: all configured)"}}}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.checks,r=[],n=m(c=>!s||s.length===0||s.includes(c),"shouldRun");if(this.config.proxmox&&n("proxmox")&&r.push(this.checkProxmox()),this.config.unifi&&n("unifi")&&r.push(this.checkUnifi()),this.config.homeassistant&&n("homeassistant")&&r.push(this.checkHomeAssistant()),r.length===0)return{success:!0,display:""};let o=await Promise.allSettled(r),i=[];for(let c of o)if(c.status==="fulfilled")i.push(...c.value);else{let d=c.reason instanceof Error?c.reason.message:String(c.reason);i.push({source:"proxmox",message:`Health check failed: ${d}`})}if(i.length===0)return{success:!0,display:""};let a=["\u26A0 Infrastructure Alerts:",""];for(let c of i)a.push(`- [${c.source}] ${c.message}`);return{success:!0,display:a.join(`
743
- `)}}async checkProxmox(){let e=this.config.proxmox,t=[],s=await this.proxmoxGet(e,"/cluster/status");for(let n of s)n.type==="node"&&!n.online&&t.push({source:"proxmox",message:`Node "${n.name}" is offline`});let r=await this.proxmoxGet(e,"/cluster/resources?type=vm");for(let n of r){let o=n.name??`VMID ${n.vmid}`,i=n.status,a=n.maxdisk,c=n.disk;if(a&&a>0&&c!=null){let d=c/a*100;d>90&&t.push({source:"proxmox",message:`${o} disk usage ${d.toFixed(1)}%`})}if(i==="running"){let d=n.maxmem,u=n.mem;if(d&&d>0&&u!=null){let p=u/d*100;p>95&&t.push({source:"proxmox",message:`${o} RAM usage ${p.toFixed(1)}%`})}}}return t}async proxmoxGet(e,t){let s=`${e.baseUrl}/api2/json${t}`,r={Authorization:`PVEAPIToken=${e.tokenId}=${e.tokenSecret}`},n=await this.apiFetch(s,{method:"GET",headers:r,signal:AbortSignal.timeout(15e3)},e.verifyTls);if(!n.ok)throw new Error(`Proxmox HTTP ${n.status}: ${(await n.text()).slice(0,200)}`);return(await n.json()).data}async checkUnifi(){let e=this.config.unifi,t=[],s=e.site??"default",r,n=[],o;if(e.apiKey)r={"X-API-Key":e.apiKey,"Content-Type":"application/json"};else{let c=e.baseUrl.replace(/\/+$/,""),d=JSON.stringify({username:e.username,password:e.password}),u=await this.apiFetch(`${c}/api/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:d},e.verifyTls),p;if(u.status===200)p="unifi-os",n=(u.headers.getSetCookie?.()??[]).map(g=>g.split(";")[0]),o=u.headers.get("x-csrf-token")??void 0;else if(u.status===404){let g=await this.apiFetch(`${c}/api/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:d},e.verifyTls);if(!g.ok)throw new Error(`UniFi login failed: HTTP ${g.status}`);p="classic",n=(g.headers.getSetCookie?.()??[]).map(f=>f.split(";")[0])}else throw new Error(`UniFi login failed: HTTP ${u.status}`);r={"Content-Type":"application/json"},n.length>0&&(r.Cookie=n.join("; ")),o&&(r["X-CSRF-Token"]=o)}let i=m(c=>`${e.baseUrl.replace(/\/+$/,"")}/proxy/network/api/s/${s}/${c}`,"apiUrl"),a=m(async c=>{let d=await this.apiFetch(i(c),{method:"GET",headers:r,signal:AbortSignal.timeout(15e3)},e.verifyTls);if(!d.ok)throw new Error(`UniFi HTTP ${d.status}`);return(await d.json()).data},"unifiGet");try{let c=await a("stat/health");for(let d of c??[])d.status&&d.status!=="ok"&&t.push({source:"unifi",message:`Subsystem "${d.subsystem}" status: ${d.status}`})}catch{}try{let c=await a("stat/device");for(let d of c??[])if(d.state!==1){let u=d.name??d.hostname??d.mac??"unknown";t.push({source:"unifi",message:`Device "${u}" is not connected (state: ${d.state})`})}}catch{}try{let c=await a("rest/alarm?archived=false");c&&c.length>0&&t.push({source:"unifi",message:`${c.length} open alert(s): ${c.slice(0,3).map(d=>d.key??d.msg??"unknown").join(", ")}`})}catch{}return t}async checkHomeAssistant(){let e=this.config.homeassistant,t=[],s=await this.haGet(e,"/api/states"),r=0,n=[];for(let o of s){let i=o.entity_id;if(i.startsWith("update."))continue;if(o.state==="unavailable"&&(r++,n.length<5)){let d=o.attributes?.friendly_name??i;n.push(d)}let a=o.attributes?.device_class??"",c=o.attributes?.unit_of_measurement??"";if(i.startsWith("sensor.")&&a==="battery"&&c==="%"){let d=parseFloat(o.state);if(!isNaN(d)&&d>=0&&d<20){let u=o.attributes?.friendly_name??i;t.push({source:"homeassistant",message:`Low battery: ${u} at ${d}%`})}}}if(r>0){let o=n.join(", "),i=r>5?` (and ${r-5} more)`:"";t.push({source:"homeassistant",message:`${r} unavailable entities: ${o}${i}`})}return t}async haGet(e,t){let s=`${e.baseUrl.replace(/\/+$/,"")}${t}`,r={Authorization:`Bearer ${e.accessToken}`,"Content-Type":"application/json"},n=await this.apiFetch(s,{method:"GET",headers:r,signal:AbortSignal.timeout(15e3)},e.verifyTls);if(!n.ok)throw new Error(`HA HTTP ${n.status}: ${(await n.text()).slice(0,200)}`);return await n.json()}async apiFetch(e,t,s){let r=s===!1,n=process.env.NODE_TLS_REJECT_UNAUTHORIZED;r&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");try{return await fetch(e,t)}finally{r&&(n===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=n)}}}});var Rn,Ol=_(()=>{"use strict";U();Rn=class extends R{static{m(this,"MicrosoftTodoSkill")}config;accessToken="";metadata={name:"microsoft_todo",description:"Manage Microsoft To Do lists and tasks \u2014 list, create, complete, update and delete tasks across all lists.",version:"1.0.0",riskLevel:"write",category:"productivity",inputSchema:{type:"object",required:["action"],properties:{action:{type:"string",enum:["list_lists","list_tasks","add_task","complete_task","uncomplete_task","delete_task","update_task","create_list"],description:"Action to perform."},listId:{type:"string",description:"To Do list ID. Either listId or list (display name) is required for task actions."},list:{type:"string",description:'To Do list display name (resolved to listId automatically). E.g. "Einkaufsliste".'},taskId:{type:"string",description:"Task ID (required for complete/uncomplete/delete/update)."},title:{type:"string",description:"Task or list title (required for add_task, create_list; optional for update_task)."},body:{type:"string",description:"Task body/notes."},dueDate:{type:"string",description:"Due date in YYYY-MM-DD format."},importance:{type:"string",enum:["low","normal","high"],description:"Task importance."},includeCompleted:{type:"boolean",description:"Include completed tasks in list_tasks (default: false)."}}}};constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;try{switch(s){case"list_lists":return await this.listLists();case"list_tasks":return await this.listTasks(e);case"add_task":return await this.addTask(e);case"complete_task":return await this.completeTask(e);case"uncomplete_task":return await this.uncompleteTask(e);case"delete_task":return await this.deleteTask(e);case"update_task":return await this.updateTask(e);case"create_list":return await this.createList(e);default:return{success:!1,error:`Unknown action: ${s}`}}}catch(r){return{success:!1,error:r instanceof Error?r.message:String(r)}}}async refreshAccessToken(){let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Tasks.ReadWrite offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok)throw new Error(`Microsoft token refresh failed: ${s.status}`);let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=m(()=>fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}}),"doFetch"),r=await s();if(r.status===401){if(await this.refreshAccessToken(),r=await s(),!r.ok)throw new Error(`Graph API error: ${r.status}`)}else if(!r.ok)throw new Error(`Graph API error: ${r.status}`);if(r.status!==204)return r.json()}async resolveListId(e){if(e.listId)return e.listId;let s=(await this.graphRequest("/me/todo/lists")).value??[];if(e.list){let n=e.list.toLowerCase(),o=s.find(i=>i.displayName.toLowerCase()===n);if(!o){let i=s.map(a=>a.displayName).join(", ");throw new Error(`List "${e.list}" not found. Available lists: ${i}`)}return o.id}let r=s.find(n=>n.wellknownListName==="defaultList");if(r)return r.id;if(s.length>0)return s[0].id;throw new Error("No To Do lists found.")}async listLists(){let t=(await this.graphRequest("/me/todo/lists")).value??[],s=t.map(r=>`\u2022 **${r.displayName}**${r.wellknownListName==="defaultList"?" (Standard)":""} [listId=${r.id}]`).join(`
765
+ `)}}};m(Pl,"maskValue");m(ip,"findEnvFile")});var Cn,Fl=_(()=>{"use strict";U();Cn=class extends x{static{m(this,"MonitorSkill")}metadata={name:"monitor",category:"infrastructure",description:"Deterministic infrastructure health checks without LLM. Checks Proxmox cluster, UniFi network, and Home Assistant for issues. Returns alerts only when problems are detected \u2014 empty display means all OK.",riskLevel:"read",version:"1.0.0",timeoutMs:6e4,inputSchema:{type:"object",properties:{checks:{type:"array",items:{type:"string",enum:["proxmox","unifi","homeassistant"]},description:"Which checks to run (default: all configured)"}}}};config;constructor(e){super(),this.config=e}async execute(e,t){let s=e.checks,r=[],n=m(c=>!s||s.length===0||s.includes(c),"shouldRun");if(this.config.proxmox&&n("proxmox")&&r.push(this.checkProxmox()),this.config.unifi&&n("unifi")&&r.push(this.checkUnifi()),this.config.homeassistant&&n("homeassistant")&&r.push(this.checkHomeAssistant()),r.length===0)return{success:!0,display:""};let o=await Promise.allSettled(r),i=[];for(let c of o)if(c.status==="fulfilled")i.push(...c.value);else{let d=c.reason instanceof Error?c.reason.message:String(c.reason);i.push({source:"proxmox",message:`Health check failed: ${d}`})}if(i.length===0)return{success:!0,display:""};let a=["\u26A0 Infrastructure Alerts:",""];for(let c of i)a.push(`- [${c.source}] ${c.message}`);return{success:!0,display:a.join(`
766
+ `)}}async checkProxmox(){let e=this.config.proxmox,t=[],s=await this.proxmoxGet(e,"/cluster/status");for(let n of s)n.type==="node"&&!n.online&&t.push({source:"proxmox",message:`Node "${n.name}" is offline`});let r=await this.proxmoxGet(e,"/cluster/resources?type=vm");for(let n of r){let o=n.name??`VMID ${n.vmid}`,i=n.status,a=n.maxdisk,c=n.disk;if(a&&a>0&&c!=null){let d=c/a*100;d>90&&t.push({source:"proxmox",message:`${o} disk usage ${d.toFixed(1)}%`})}if(i==="running"){let d=n.maxmem,u=n.mem;if(d&&d>0&&u!=null){let p=u/d*100;p>95&&t.push({source:"proxmox",message:`${o} RAM usage ${p.toFixed(1)}%`})}}}return t}async proxmoxGet(e,t){let s=`${e.baseUrl}/api2/json${t}`,r={Authorization:`PVEAPIToken=${e.tokenId}=${e.tokenSecret}`},n=await this.apiFetch(s,{method:"GET",headers:r,signal:AbortSignal.timeout(15e3)},e.verifyTls);if(!n.ok)throw new Error(`Proxmox HTTP ${n.status}: ${(await n.text()).slice(0,200)}`);return(await n.json()).data}async checkUnifi(){let e=this.config.unifi,t=[],s=e.site??"default",r,n=[],o;if(e.apiKey)r={"X-API-Key":e.apiKey,"Content-Type":"application/json"};else{let c=e.baseUrl.replace(/\/+$/,""),d=JSON.stringify({username:e.username,password:e.password}),u=await this.apiFetch(`${c}/api/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:d},e.verifyTls),p;if(u.status===200)p="unifi-os",n=(u.headers.getSetCookie?.()??[]).map(f=>f.split(";")[0]),o=u.headers.get("x-csrf-token")??void 0;else if(u.status===404){let f=await this.apiFetch(`${c}/api/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:d},e.verifyTls);if(!f.ok)throw new Error(`UniFi login failed: HTTP ${f.status}`);p="classic",n=(f.headers.getSetCookie?.()??[]).map(g=>g.split(";")[0])}else throw new Error(`UniFi login failed: HTTP ${u.status}`);r={"Content-Type":"application/json"},n.length>0&&(r.Cookie=n.join("; ")),o&&(r["X-CSRF-Token"]=o)}let i=m(c=>`${e.baseUrl.replace(/\/+$/,"")}/proxy/network/api/s/${s}/${c}`,"apiUrl"),a=m(async c=>{let d=await this.apiFetch(i(c),{method:"GET",headers:r,signal:AbortSignal.timeout(15e3)},e.verifyTls);if(!d.ok)throw new Error(`UniFi HTTP ${d.status}`);return(await d.json()).data},"unifiGet");try{let c=await a("stat/health");for(let d of c??[])d.status&&d.status!=="ok"&&t.push({source:"unifi",message:`Subsystem "${d.subsystem}" status: ${d.status}`})}catch{}try{let c=await a("stat/device");for(let d of c??[])if(d.state!==1){let u=d.name??d.hostname??d.mac??"unknown";t.push({source:"unifi",message:`Device "${u}" is not connected (state: ${d.state})`})}}catch{}try{let c=await a("rest/alarm?archived=false");c&&c.length>0&&t.push({source:"unifi",message:`${c.length} open alert(s): ${c.slice(0,3).map(d=>d.key??d.msg??"unknown").join(", ")}`})}catch{}return t}async checkHomeAssistant(){let e=this.config.homeassistant,t=[],s=await this.haGet(e,"/api/states"),r=0,n=[];for(let o of s){let i=o.entity_id;if(i.startsWith("update."))continue;if(o.state==="unavailable"&&(r++,n.length<5)){let d=o.attributes?.friendly_name??i;n.push(d)}let a=o.attributes?.device_class??"",c=o.attributes?.unit_of_measurement??"";if(i.startsWith("sensor.")&&a==="battery"&&c==="%"){let d=parseFloat(o.state);if(!isNaN(d)&&d>=0&&d<20){let u=o.attributes?.friendly_name??i;t.push({source:"homeassistant",message:`Low battery: ${u} at ${d}%`})}}}if(r>0){let o=n.join(", "),i=r>5?` (and ${r-5} more)`:"";t.push({source:"homeassistant",message:`${r} unavailable entities: ${o}${i}`})}return t}async haGet(e,t){let s=`${e.baseUrl.replace(/\/+$/,"")}${t}`,r={Authorization:`Bearer ${e.accessToken}`,"Content-Type":"application/json"},n=await this.apiFetch(s,{method:"GET",headers:r,signal:AbortSignal.timeout(15e3)},e.verifyTls);if(!n.ok)throw new Error(`HA HTTP ${n.status}: ${(await n.text()).slice(0,200)}`);return await n.json()}async apiFetch(e,t,s){let r=s===!1,n=process.env.NODE_TLS_REJECT_UNAUTHORIZED;r&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0");try{return await fetch(e,t)}finally{r&&(n===void 0?delete process.env.NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED=n)}}}});var Dn,jl=_(()=>{"use strict";U();Dn=class extends x{static{m(this,"MicrosoftTodoSkill")}config;accessToken="";metadata={name:"microsoft_todo",description:"Manage Microsoft To Do lists and tasks \u2014 list, create, complete, update and delete tasks across all lists.",version:"1.0.0",riskLevel:"write",category:"productivity",inputSchema:{type:"object",required:["action"],properties:{action:{type:"string",enum:["list_lists","list_tasks","add_task","complete_task","uncomplete_task","delete_task","update_task","create_list"],description:"Action to perform."},listId:{type:"string",description:"To Do list ID. Either listId or list (display name) is required for task actions."},list:{type:"string",description:'To Do list display name (resolved to listId automatically). E.g. "Einkaufsliste".'},taskId:{type:"string",description:"Task ID (required for complete/uncomplete/delete/update)."},title:{type:"string",description:"Task or list title (required for add_task, create_list; optional for update_task)."},body:{type:"string",description:"Task body/notes."},dueDate:{type:"string",description:"Due date in YYYY-MM-DD format."},importance:{type:"string",enum:["low","normal","high"],description:"Task importance."},includeCompleted:{type:"boolean",description:"Include completed tasks in list_tasks (default: false)."}}}};constructor(e){super(),this.config=e}async execute(e,t){let s=e.action;try{switch(s){case"list_lists":return await this.listLists();case"list_tasks":return await this.listTasks(e);case"add_task":return await this.addTask(e);case"complete_task":return await this.completeTask(e);case"uncomplete_task":return await this.uncompleteTask(e);case"delete_task":return await this.deleteTask(e);case"update_task":return await this.updateTask(e);case"create_list":return await this.createList(e);default:return{success:!1,error:`Unknown action: ${s}`}}}catch(r){return{success:!1,error:r instanceof Error?r.message:String(r)}}}async refreshAccessToken(){let e=`https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/token`,t=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token",scope:"https://graph.microsoft.com/Tasks.ReadWrite offline_access"}),s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()});if(!s.ok)throw new Error(`Microsoft token refresh failed: ${s.status}`);let r=await s.json();this.accessToken=r.access_token}async graphRequest(e,t={}){let s=m(()=>fetch(`https://graph.microsoft.com/v1.0${e}`,{...t,headers:{Authorization:`Bearer ${this.accessToken}`,"Content-Type":"application/json",...t.headers}}),"doFetch"),r=await s();if(r.status===401){if(await this.refreshAccessToken(),r=await s(),!r.ok)throw new Error(`Graph API error: ${r.status}`)}else if(!r.ok)throw new Error(`Graph API error: ${r.status}`);if(r.status!==204)return r.json()}async resolveListId(e){if(e.listId)return e.listId;let s=(await this.graphRequest("/me/todo/lists")).value??[];if(e.list){let n=e.list.toLowerCase(),o=s.find(i=>i.displayName.toLowerCase()===n);if(!o){let i=s.map(a=>a.displayName).join(", ");throw new Error(`List "${e.list}" not found. Available lists: ${i}`)}return o.id}let r=s.find(n=>n.wellknownListName==="defaultList");if(r)return r.id;if(s.length>0)return s[0].id;throw new Error("No To Do lists found.")}async listLists(){let t=(await this.graphRequest("/me/todo/lists")).value??[],s=t.map(r=>`\u2022 **${r.displayName}**${r.wellknownListName==="defaultList"?" (Standard)":""} [listId=${r.id}]`).join(`
744
767
  `);return{success:!0,data:t,display:s||"Keine Listen gefunden."}}async listTasks(e){let t=await this.resolveListId(e),s=e.includeCompleted===!0,r=`/me/todo/lists/${t}/tasks`;s||(r+="?$filter=status ne 'completed'");let o=(await this.graphRequest(r)).value??[],i=o.map(a=>{let c=a.status==="completed"?"\u2611":"\u2610",d=a.importance==="high"?" \u2757":"",u=a.dueDateTime?` (f\xE4llig: ${a.dueDateTime.dateTime.slice(0,10)})`:"";return`${c} ${a.title}${d}${u} [taskId=${a.id}]`});return{success:!0,data:o,display:i.length>0?`listId=${t}
745
768
  ${i.join(`
746
- `)}`:"Keine Aufgaben in dieser Liste."}}async addTask(e){let t=await this.resolveListId(e);if(!e.title)return{success:!1,error:"title is required for add_task."};let s={title:e.title};return e.body&&(s.body={content:e.body,contentType:"text"}),e.dueDate&&(s.dueDateTime={dateTime:`${e.dueDate}T00:00:00`,timeZone:"UTC"}),e.importance&&(s.importance=e.importance),{success:!0,data:await this.graphRequest(`/me/todo/lists/${t}/tasks`,{method:"POST",body:JSON.stringify(s)}),display:`Aufgabe \u201E${e.title}" hinzugef\xFCgt.`}}async completeTask(e){let t=await this.resolveListId(e);return e.taskId?(await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"PATCH",body:JSON.stringify({status:"completed"})}),{success:!0,display:"Aufgabe als erledigt markiert."}):{success:!1,error:"taskId is required."}}async uncompleteTask(e){let t=await this.resolveListId(e);return e.taskId?(await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"PATCH",body:JSON.stringify({status:"notStarted"})}),{success:!0,display:"Aufgabe als nicht erledigt markiert."}):{success:!1,error:"taskId is required."}}async deleteTask(e){let t=await this.resolveListId(e);return e.taskId?(await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"DELETE"}),{success:!0,display:"Aufgabe gel\xF6scht."}):{success:!1,error:"taskId is required."}}async updateTask(e){let t=await this.resolveListId(e);if(!e.taskId)return{success:!1,error:"taskId is required."};let s={};return e.title&&(s.title=e.title),e.body&&(s.body={content:e.body,contentType:"text"}),e.dueDate&&(s.dueDateTime={dateTime:`${e.dueDate}T00:00:00`,timeZone:"UTC"}),e.importance&&(s.importance=e.importance),Object.keys(s).length===0?{success:!1,error:"Nothing to update \u2014 provide title, body, dueDate, or importance."}:{success:!0,data:await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"PATCH",body:JSON.stringify(s)}),display:"Aufgabe aktualisiert."}}async createList(e){return e.title?{success:!0,data:await this.graphRequest("/me/todo/lists",{method:"POST",body:JSON.stringify({displayName:e.title})}),display:`Liste \u201E${e.title}" erstellt.`}:{success:!1,error:"title is required for create_list."}}}});var Pl,ms,Ul=_(()=>{"use strict";U();Pl=["lt","gt","lte","gte","eq","neq","contains","not_contains","changed","increased","decreased"],ms=class extends R{static{m(this,"WatchSkill")}watchRepo;metadata={name:"watch",category:"automation",description:'Create and manage condition-based alerts (watches). A watch polls a skill at regular intervals, extracts a field from the result, and sends a notification when a condition is met \u2014 no LLM involved. Operators: lt, gt, lte, gte (numeric), eq, neq (string), contains, not_contains (substring), changed, increased, decreased (vs. last value). The first check stores a baseline and never triggers. Common condition_field paths by skill: energy_prices \u2192 "bruttoCt" (current price ct/kWh); bmw {action:"status"} \u2192 "telematic.CHARGING_STATUS.value", "telematic.BATTERY_SIZE_MAX.value"; todo {action:"list",list:"..."} \u2192 use "length" for item count; email {action:"inbox"} \u2192 "unreadCount" or "messages.length"; monitor \u2192 returns alerts array, use "length" with gt 0. Use scheduled_task instead when you need to run a prompt through the LLM or when the user asks for a time-based report rather than a condition-based alert.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","list","enable","disable","delete"],description:"The watch action to perform"},name:{type:"string",description:"Short description of the watch (for create)"},skill_name:{type:"string",description:'Which skill to poll, e.g. "energy_prices", "email_list", "bmw" (for create)'},skill_params:{type:"object",description:"Parameters to pass to the skill (for create)"},condition_field:{type:"string",description:'Dot-path to extract from skill result data, e.g. "brutto_ct", "items.length", "battery.level" (for create)'},condition_operator:{type:"string",enum:["lt","gt","lte","gte","eq","neq","contains","not_contains","changed","increased","decreased"],description:"Comparison operator (for create)"},condition_value:{type:["string","number"],description:"Threshold value \u2014 optional for changed/increased/decreased (for create)"},interval_minutes:{type:"number",description:"Poll interval in minutes (default 15) (for create)"},cooldown_minutes:{type:"number",description:"Minimum minutes between alerts (default 30) (for create)"},message_template:{type:"string",description:"Custom alert message (for create)"},watch_id:{type:"string",description:"Watch ID (for enable, disable, delete)"}},required:["action"]}};constructor(e){super(),this.watchRepo=e}async execute(e,t){let s=e.action;switch(s){case"create":return this.createWatch(e,t);case"list":return this.listWatches(t);case"enable":return this.toggleWatch(e,!0);case"disable":return this.toggleWatch(e,!1);case"delete":return this.deleteWatch(e);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid: create, list, enable, disable, delete`}}}createWatch(e,t){let s=e.name,r=e.skill_name,n=e.skill_params??{},o=e.condition_field,i=e.condition_operator,a=e.condition_value,c=e.interval_minutes??15,d=e.cooldown_minutes??30,u=e.message_template;if(!s)return{success:!1,error:'Missing required field "name"'};if(!r)return{success:!1,error:'Missing required field "skill_name"'};if(!o)return{success:!1,error:'Missing required field "condition_field"'};if(!i||!Pl.includes(i))return{success:!1,error:`Invalid "condition_operator". Must be one of: ${Pl.join(", ")}`};if(c<1)return{success:!1,error:"interval_minutes must be >= 1"};if(d<0)return{success:!1,error:"cooldown_minutes must be >= 0"};let p=this.watchRepo.create({chatId:t.chatId,platform:t.platform,name:s,skillName:r,skillParams:n,condition:{field:o,operator:i,value:a},intervalMinutes:c,cooldownMinutes:d,enabled:!0,messageTemplate:u});return{success:!0,data:{watchId:p.id,name:s,skillName:r,conditionField:o,conditionOperator:i,conditionValue:a,intervalMinutes:c},display:`Watch erstellt (${p.id}): "${s}" \u2014 pollt "${r}" alle ${c}min, Bedingung: ${o} ${i}${a!=null?" "+a:""}`}}listWatches(e){let t=this.watchRepo.findByChatId(e.chatId,e.platform);if(t.length===0)return{success:!0,data:[],display:"Keine Watches vorhanden."};let s=t.map(r=>{let n=r.enabled?"\u2705":"\u23F8\uFE0F",o=`${r.condition.field} ${r.condition.operator}${r.condition.value!=null?" "+r.condition.value:""}`,i=r.lastCheckedAt?` | letzter Check: ${r.lastCheckedAt}`:"";return`- ${n} ${r.id}: "${r.name}" [${r.skillName}, ${r.intervalMinutes}min] ${o}${i}`});return{success:!0,data:t.map(r=>({watchId:r.id,name:r.name,skillName:r.skillName,condition:r.condition,intervalMinutes:r.intervalMinutes,enabled:r.enabled,lastCheckedAt:r.lastCheckedAt,lastTriggeredAt:r.lastTriggeredAt})),display:`Watches:
769
+ `)}`:"Keine Aufgaben in dieser Liste."}}async addTask(e){let t=await this.resolveListId(e);if(!e.title)return{success:!1,error:"title is required for add_task."};let s={title:e.title};return e.body&&(s.body={content:e.body,contentType:"text"}),e.dueDate&&(s.dueDateTime={dateTime:`${e.dueDate}T00:00:00`,timeZone:"UTC"}),e.importance&&(s.importance=e.importance),{success:!0,data:await this.graphRequest(`/me/todo/lists/${t}/tasks`,{method:"POST",body:JSON.stringify(s)}),display:`Aufgabe \u201E${e.title}" hinzugef\xFCgt.`}}async completeTask(e){let t=await this.resolveListId(e);return e.taskId?(await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"PATCH",body:JSON.stringify({status:"completed"})}),{success:!0,display:"Aufgabe als erledigt markiert."}):{success:!1,error:"taskId is required."}}async uncompleteTask(e){let t=await this.resolveListId(e);return e.taskId?(await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"PATCH",body:JSON.stringify({status:"notStarted"})}),{success:!0,display:"Aufgabe als nicht erledigt markiert."}):{success:!1,error:"taskId is required."}}async deleteTask(e){let t=await this.resolveListId(e);return e.taskId?(await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"DELETE"}),{success:!0,display:"Aufgabe gel\xF6scht."}):{success:!1,error:"taskId is required."}}async updateTask(e){let t=await this.resolveListId(e);if(!e.taskId)return{success:!1,error:"taskId is required."};let s={};return e.title&&(s.title=e.title),e.body&&(s.body={content:e.body,contentType:"text"}),e.dueDate&&(s.dueDateTime={dateTime:`${e.dueDate}T00:00:00`,timeZone:"UTC"}),e.importance&&(s.importance=e.importance),Object.keys(s).length===0?{success:!1,error:"Nothing to update \u2014 provide title, body, dueDate, or importance."}:{success:!0,data:await this.graphRequest(`/me/todo/lists/${t}/tasks/${e.taskId}`,{method:"PATCH",body:JSON.stringify(s)}),display:"Aufgabe aktualisiert."}}async createList(e){return e.title?{success:!0,data:await this.graphRequest("/me/todo/lists",{method:"POST",body:JSON.stringify({displayName:e.title})}),display:`Liste \u201E${e.title}" erstellt.`}:{success:!1,error:"title is required for create_list."}}}});var Bl,hs,Hl=_(()=>{"use strict";U();Bl=["lt","gt","lte","gte","eq","neq","contains","not_contains","changed","increased","decreased"],hs=class extends x{static{m(this,"WatchSkill")}watchRepo;metadata={name:"watch",category:"automation",description:'Create and manage condition-based alerts (watches). A watch polls a skill at regular intervals, extracts a field from the result, and sends a notification when a condition is met \u2014 no LLM involved. Operators: lt, gt, lte, gte (numeric), eq, neq (string), contains, not_contains (substring), changed, increased, decreased (vs. last value). The first check stores a baseline and never triggers. Common condition_field paths by skill: energy_prices \u2192 "bruttoCt" (current price ct/kWh); bmw {action:"status"} \u2192 "telematic.CHARGING_STATUS.value", "telematic.BATTERY_SIZE_MAX.value"; todo {action:"list",list:"..."} \u2192 use "length" for item count; email {action:"inbox"} \u2192 "unreadCount" or "messages.length"; monitor \u2192 returns alerts array, use "length" with gt 0. Use scheduled_task instead when you need to run a prompt through the LLM or when the user asks for a time-based report rather than a condition-based alert.',riskLevel:"write",version:"1.0.0",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","list","enable","disable","delete"],description:"The watch action to perform"},name:{type:"string",description:"Short description of the watch (for create)"},skill_name:{type:"string",description:'Which skill to poll, e.g. "energy_prices", "email_list", "bmw" (for create)'},skill_params:{type:"object",description:"Parameters to pass to the skill (for create)"},condition_field:{type:"string",description:'Dot-path to extract from skill result data, e.g. "brutto_ct", "items.length", "battery.level" (for create)'},condition_operator:{type:"string",enum:["lt","gt","lte","gte","eq","neq","contains","not_contains","changed","increased","decreased"],description:"Comparison operator (for create)"},condition_value:{type:["string","number"],description:"Threshold value \u2014 optional for changed/increased/decreased (for create)"},interval_minutes:{type:"number",description:"Poll interval in minutes (default 15) (for create)"},cooldown_minutes:{type:"number",description:"Minimum minutes between alerts (default 30) (for create)"},message_template:{type:"string",description:"Custom alert message (for create)"},watch_id:{type:"string",description:"Watch ID (for enable, disable, delete)"}},required:["action"]}};constructor(e){super(),this.watchRepo=e}async execute(e,t){let s=e.action;switch(s){case"create":return this.createWatch(e,t);case"list":return this.listWatches(t);case"enable":return this.toggleWatch(e,!0);case"disable":return this.toggleWatch(e,!1);case"delete":return this.deleteWatch(e);default:return{success:!1,error:`Unknown action: "${String(s)}". Valid: create, list, enable, disable, delete`}}}createWatch(e,t){let s=e.name,r=e.skill_name,n=e.skill_params??{},o=e.condition_field,i=e.condition_operator,a=e.condition_value,c=e.interval_minutes??15,d=e.cooldown_minutes??30,u=e.message_template;if(!s)return{success:!1,error:'Missing required field "name"'};if(!r)return{success:!1,error:'Missing required field "skill_name"'};if(!o)return{success:!1,error:'Missing required field "condition_field"'};if(!i||!Bl.includes(i))return{success:!1,error:`Invalid "condition_operator". Must be one of: ${Bl.join(", ")}`};if(c<1)return{success:!1,error:"interval_minutes must be >= 1"};if(d<0)return{success:!1,error:"cooldown_minutes must be >= 0"};let p=this.watchRepo.create({chatId:t.chatId,platform:t.platform,name:s,skillName:r,skillParams:n,condition:{field:o,operator:i,value:a},intervalMinutes:c,cooldownMinutes:d,enabled:!0,messageTemplate:u});return{success:!0,data:{watchId:p.id,name:s,skillName:r,conditionField:o,conditionOperator:i,conditionValue:a,intervalMinutes:c},display:`Watch erstellt (${p.id}): "${s}" \u2014 pollt "${r}" alle ${c}min, Bedingung: ${o} ${i}${a!=null?" "+a:""}`}}listWatches(e){let t=this.watchRepo.findByChatId(e.chatId,e.platform);if(t.length===0)return{success:!0,data:[],display:"Keine Watches vorhanden."};let s=t.map(r=>{let n=r.enabled?"\u2705":"\u23F8\uFE0F",o=`${r.condition.field} ${r.condition.operator}${r.condition.value!=null?" "+r.condition.value:""}`,i=r.lastCheckedAt?` | letzter Check: ${r.lastCheckedAt}`:"";return`- ${n} ${r.id}: "${r.name}" [${r.skillName}, ${r.intervalMinutes}min] ${o}${i}`});return{success:!0,data:t.map(r=>({watchId:r.id,name:r.name,skillName:r.skillName,condition:r.condition,intervalMinutes:r.intervalMinutes,enabled:r.enabled,lastCheckedAt:r.lastCheckedAt,lastTriggeredAt:r.lastTriggeredAt})),display:`Watches:
747
770
  ${s.join(`
748
- `)}`}}toggleWatch(e,t){let s=e.watch_id;return s?this.watchRepo.toggle(s,t)?{success:!0,data:{watchId:s,enabled:t},display:`Watch "${s}" ${t?"aktiviert":"deaktiviert"}.`}:{success:!1,error:`Watch "${s}" not found`}:{success:!1,error:`Missing "watch_id" for ${t?"enable":"disable"}`}}deleteWatch(e){let t=e.watch_id;return t?this.watchRepo.delete(t)?{success:!0,data:{watchId:t},display:`Watch "${t}" gel\xF6scht.`}:{success:!1,error:`Watch "${t}" not found`}:{success:!1,error:'Missing "watch_id" for delete'}}}});var It,Cn=_(()=>{"use strict";It=class{static{m(this,"MarketplaceProvider")}}});var rp,hr,wi=_(()=>{"use strict";Cn();rp="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",hr=class extends It{static{m(this,"WillhabenProvider")}platform="willhaben";async search(e){let t=Math.min(e.rows??100,100),s=new URL("https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz");s.searchParams.set("keyword",e.query),s.searchParams.set("rows",String(t)),e.priceMin!=null&&s.searchParams.set("PRICE_FROM",String(e.priceMin)),e.priceMax!=null&&s.searchParams.set("PRICE_TO",String(e.priceMax));let r=await fetch(s.toString(),{headers:{"User-Agent":rp,Accept:"text/html"}});if(!r.ok)throw new Error(`willhaben HTTP ${r.status}`);let o=(await r.text()).match(/<script id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);if(!o)throw new Error("willhaben: __NEXT_DATA__ not found \u2014 page structure may have changed");let i=JSON.parse(o[1]),c=(i?.props?.pageProps?.searchResult?.advertSummaryList?.advertSummary??[]).map(d=>{let u=d.attributes?.attribute??[],p=m(h=>u.find(E=>E.name===h)?.values?.[0]??void 0,"attr"),g=p("PRICE"),f=g?parseFloat(g):null;return{id:String(d.id),title:p("HEADING")??d.description??"Kein Titel",price:f,currency:"EUR",condition:p("CONDITION"),location:[p("LOCATION"),p("POSTCODE")].filter(Boolean).join(" "),url:`https://www.willhaben.at/iad/object?adId=${d.id}`,imageUrl:d.advertImageList?.advertImage?.[0]?.mainImageUrl??void 0,seller:p("ORGANIZER")??void 0,publishedAt:p("PUBLISHED_String")??void 0,platform:"willhaben"}});return{listings:c,totalCount:i?.props?.pageProps?.searchResult?.rowsFound??c.length,query:e.query,platform:"willhaben"}}}});var fr,Ti=_(()=>{"use strict";Cn();fr=class extends It{static{m(this,"EbayProvider")}appId;certId;platform="ebay";tokenCache=null;constructor(e,t){super(),this.appId=e,this.certId=t}async getToken(){if(this.tokenCache&&Date.now()<this.tokenCache.expiresAt)return this.tokenCache.token;let e=Buffer.from(`${this.appId}:${this.certId}`).toString("base64"),t=await fetch("https://api.ebay.com/identity/v1/oauth2/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:`Basic ${e}`},body:"grant_type=client_credentials&scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope"});if(!t.ok)throw new Error(`eBay OAuth failed: HTTP ${t.status}`);let s=await t.json();return this.tokenCache={token:s.access_token,expiresAt:Date.now()+(s.expires_in-300)*1e3},this.tokenCache.token}async search(e){let t=await this.getToken(),s=Math.min(e.rows??50,200),r=new URL("https://api.ebay.com/buy/browse/v1/item_summary/search");if(r.searchParams.set("q",e.query),r.searchParams.set("limit",String(s)),e.priceMin!=null||e.priceMax!=null){let c=[];e.priceMin!=null&&c.push(`price:[${e.priceMin}..`),e.priceMax!=null?c.length?c[0]+=`${e.priceMax}]`:c.push(`price:[..${e.priceMax}]`):c[0]+="]",r.searchParams.set("filter",c.join(","))}let n=await fetch(r.toString(),{headers:{Authorization:`Bearer ${t}`,"X-EBAY-C-MARKETPLACE-ID":"EBAY_AT"}});if(!n.ok)throw new Error(`eBay API error: HTTP ${n.status}`);let o=await n.json(),a=(o.itemSummaries??[]).map(c=>({id:c.itemId??c.legacyItemId??"",title:c.title??"",price:c.price?.value?parseFloat(c.price.value):null,currency:c.price?.currency??"EUR",condition:c.condition??c.conditionId??void 0,location:c.itemLocation?.postalCode?`${c.itemLocation.city??""} ${c.itemLocation.postalCode}`.trim():c.itemLocation?.country??void 0,url:c.itemWebUrl??c.itemHref??"",imageUrl:c.image?.imageUrl??c.thumbnailImages?.[0]?.imageUrl??void 0,seller:c.seller?.username??void 0,publishedAt:void 0,platform:"ebay"}));return{listings:a,totalCount:o.total??a.length,query:e.query,platform:"ebay"}}}});function Fl(l){let e=[...l].sort((s,r)=>s-r),t=Math.floor(e.length/2);return e.length%2?e[t]:(e[t-1]+e[t])/2}function jl(l,e){return l==null?"k.A.":`${l.toFixed(2)} ${e}`}var gr,Bl=_(()=>{"use strict";U();wi();Ti();m(Fl,"median");m(jl,"formatPrice");gr=class extends R{static{m(this,"MarketplaceSkill")}metadata={name:"marketplace",category:"information",description:'Marktplatz-Suche auf willhaben.at und eBay. "search" listet ALLE gefundenen Inserate als strukturierte Tabelle. "compare" liefert Preisstatistik (min, max, median, avg) + g\xFCnstigste 5. willhaben funktioniert immer ohne Credentials, eBay nur mit API-Keys.',riskLevel:"read",version:"1.0.0",timeoutMs:3e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["search","compare"],description:"Aktion: search = alle Inserate auflisten, compare = Preisstatistik"},query:{type:"string",description:"Suchbegriff"},platform:{type:"string",enum:["willhaben","ebay","all"],description:"Plattform (default: willhaben)"},priceMin:{type:"number",description:"Mindestpreis EUR"},priceMax:{type:"number",description:"H\xF6chstpreis EUR"},rows:{type:"number",description:"Max Ergebnisse (default 50, max 200)"}},required:["action","query"]}};providers=[];constructor(e){super(),this.providers.push(new hr),e?.ebay?.appId&&e?.ebay?.certId&&this.providers.push(new fr(e.ebay.appId,e.ebay.certId))}async execute(e,t){let s=e.action,r=e.query,n=e.platform??"willhaben",o=e.priceMin,i=e.priceMax,a=Math.min(e.rows??50,200),c=this.getProviders(n);if(c.length===0)return{success:!1,error:"Keine Marketplace-Provider verf\xFCgbar f\xFCr diese Plattform"};switch(s){case"search":return this.handleSearch(c,{query:r,priceMin:o,priceMax:i,rows:a});case"compare":return this.handleCompare(c,{query:r,priceMin:o,priceMax:i,rows:Math.min(a,200)});default:return{success:!1,error:`Unbekannte Aktion: ${s}`}}}getProviders(e){return e==="all"?this.providers:this.providers.filter(t=>t.platform===e)}async handleSearch(e,t){let s=await this.searchAll(e,t),r=s.flatMap(i=>i.listings);if(r.length===0)return{success:!0,data:`Keine Inserate gefunden f\xFCr "${t.query}".`};let n=r.map(i=>i.price).filter(i=>i!=null),o=[];o.push(`**${r.length} Inserate** f\xFCr "${t.query}"${s.length>1?` (${s.map(i=>`${i.platform}: ${i.listings.length}`).join(", ")})`:""}
749
- `),o.push("| # | Titel | Preis | Standort | Plattform | Link |"),o.push("|---|-------|-------|----------|-----------|------|");for(let i=0;i<r.length;i++){let a=r[i],c=a.title.length>60?a.title.slice(0,57)+"...":a.title;o.push(`| ${i+1} | ${c} | ${jl(a.price,a.currency)} | ${a.location??"\u2014"} | ${a.platform} | [Link](${a.url}) |`)}return n.length>0&&(o.push(""),o.push(`**Min:** ${Math.min(...n).toFixed(2)} EUR | **Max:** ${Math.max(...n).toFixed(2)} EUR | **Median:** ${Fl(n).toFixed(2)} EUR`)),{success:!0,data:o.join(`
771
+ `)}`}}toggleWatch(e,t){let s=e.watch_id;return s?this.watchRepo.toggle(s,t)?{success:!0,data:{watchId:s,enabled:t},display:`Watch "${s}" ${t?"aktiviert":"deaktiviert"}.`}:{success:!1,error:`Watch "${s}" not found`}:{success:!1,error:`Missing "watch_id" for ${t?"enable":"disable"}`}}deleteWatch(e){let t=e.watch_id;return t?this.watchRepo.delete(t)?{success:!0,data:{watchId:t},display:`Watch "${t}" gel\xF6scht.`}:{success:!1,error:`Watch "${t}" not found`}:{success:!1,error:'Missing "watch_id" for delete'}}}});var xt,Ln=_(()=>{"use strict";xt=class{static{m(this,"MarketplaceProvider")}}});var ap,wr,_i=_(()=>{"use strict";Ln();ap="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",wr=class extends xt{static{m(this,"WillhabenProvider")}platform="willhaben";async search(e){let t=Math.min(e.rows??100,100),s=new URL("https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz");s.searchParams.set("keyword",e.query),s.searchParams.set("rows",String(t)),e.priceMin!=null&&s.searchParams.set("PRICE_FROM",String(e.priceMin)),e.priceMax!=null&&s.searchParams.set("PRICE_TO",String(e.priceMax));let r=await fetch(s.toString(),{headers:{"User-Agent":ap,Accept:"text/html"}});if(!r.ok)throw new Error(`willhaben HTTP ${r.status}`);let o=(await r.text()).match(/<script id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);if(!o)throw new Error("willhaben: __NEXT_DATA__ not found \u2014 page structure may have changed");let i=JSON.parse(o[1]),c=(i?.props?.pageProps?.searchResult?.advertSummaryList?.advertSummary??[]).map(d=>{let u=d.attributes?.attribute??[],p=m(h=>u.find(E=>E.name===h)?.values?.[0]??void 0,"attr"),f=p("PRICE"),g=f?parseFloat(f):null;return{id:String(d.id),title:p("HEADING")??d.description??"Kein Titel",price:g,currency:"EUR",condition:p("CONDITION"),location:[p("LOCATION"),p("POSTCODE")].filter(Boolean).join(" "),url:`https://www.willhaben.at/iad/object?adId=${d.id}`,imageUrl:d.advertImageList?.advertImage?.[0]?.mainImageUrl??void 0,seller:p("ORGANIZER")??void 0,publishedAt:p("PUBLISHED_String")??void 0,platform:"willhaben"}});return{listings:c,totalCount:i?.props?.pageProps?.searchResult?.rowsFound??c.length,query:e.query,platform:"willhaben"}}}});var Tr,bi=_(()=>{"use strict";Ln();Tr=class extends xt{static{m(this,"EbayProvider")}appId;certId;platform="ebay";tokenCache=null;constructor(e,t){super(),this.appId=e,this.certId=t}async getToken(){if(this.tokenCache&&Date.now()<this.tokenCache.expiresAt)return this.tokenCache.token;let e=Buffer.from(`${this.appId}:${this.certId}`).toString("base64"),t=await fetch("https://api.ebay.com/identity/v1/oauth2/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:`Basic ${e}`},body:"grant_type=client_credentials&scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope"});if(!t.ok)throw new Error(`eBay OAuth failed: HTTP ${t.status}`);let s=await t.json();return this.tokenCache={token:s.access_token,expiresAt:Date.now()+(s.expires_in-300)*1e3},this.tokenCache.token}async search(e){let t=await this.getToken(),s=Math.min(e.rows??50,200),r=new URL("https://api.ebay.com/buy/browse/v1/item_summary/search");if(r.searchParams.set("q",e.query),r.searchParams.set("limit",String(s)),e.priceMin!=null||e.priceMax!=null){let c=[];e.priceMin!=null&&c.push(`price:[${e.priceMin}..`),e.priceMax!=null?c.length?c[0]+=`${e.priceMax}]`:c.push(`price:[..${e.priceMax}]`):c[0]+="]",r.searchParams.set("filter",c.join(","))}let n=await fetch(r.toString(),{headers:{Authorization:`Bearer ${t}`,"X-EBAY-C-MARKETPLACE-ID":"EBAY_AT"}});if(!n.ok)throw new Error(`eBay API error: HTTP ${n.status}`);let o=await n.json(),a=(o.itemSummaries??[]).map(c=>({id:c.itemId??c.legacyItemId??"",title:c.title??"",price:c.price?.value?parseFloat(c.price.value):null,currency:c.price?.currency??"EUR",condition:c.condition??c.conditionId??void 0,location:c.itemLocation?.postalCode?`${c.itemLocation.city??""} ${c.itemLocation.postalCode}`.trim():c.itemLocation?.country??void 0,url:c.itemWebUrl??c.itemHref??"",imageUrl:c.image?.imageUrl??c.thumbnailImages?.[0]?.imageUrl??void 0,seller:c.seller?.username??void 0,publishedAt:void 0,platform:"ebay"}));return{listings:a,totalCount:o.total??a.length,query:e.query,platform:"ebay"}}}});function zl(l){let e=[...l].sort((s,r)=>s-r),t=Math.floor(e.length/2);return e.length%2?e[t]:(e[t-1]+e[t])/2}function ql(l,e){return l==null?"k.A.":`${l.toFixed(2)} ${e}`}var _r,Wl=_(()=>{"use strict";U();_i();bi();m(zl,"median");m(ql,"formatPrice");_r=class extends x{static{m(this,"MarketplaceSkill")}metadata={name:"marketplace",category:"information",description:'Marktplatz-Suche auf willhaben.at und eBay. "search" listet ALLE gefundenen Inserate als strukturierte Tabelle. "compare" liefert Preisstatistik (min, max, median, avg) + g\xFCnstigste 5. willhaben funktioniert immer ohne Credentials, eBay nur mit API-Keys.',riskLevel:"read",version:"1.0.0",timeoutMs:3e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["search","compare"],description:"Aktion: search = alle Inserate auflisten, compare = Preisstatistik"},query:{type:"string",description:"Suchbegriff"},platform:{type:"string",enum:["willhaben","ebay","all"],description:"Plattform (default: willhaben)"},priceMin:{type:"number",description:"Mindestpreis EUR"},priceMax:{type:"number",description:"H\xF6chstpreis EUR"},rows:{type:"number",description:"Max Ergebnisse (default 50, max 200)"}},required:["action","query"]}};providers=[];constructor(e){super(),this.providers.push(new wr),e?.ebay?.appId&&e?.ebay?.certId&&this.providers.push(new Tr(e.ebay.appId,e.ebay.certId))}async execute(e,t){let s=e.action,r=e.query,n=e.platform??"willhaben",o=e.priceMin,i=e.priceMax,a=Math.min(e.rows??50,200),c=this.getProviders(n);if(c.length===0)return{success:!1,error:"Keine Marketplace-Provider verf\xFCgbar f\xFCr diese Plattform"};switch(s){case"search":return this.handleSearch(c,{query:r,priceMin:o,priceMax:i,rows:a});case"compare":return this.handleCompare(c,{query:r,priceMin:o,priceMax:i,rows:Math.min(a,200)});default:return{success:!1,error:`Unbekannte Aktion: ${s}`}}}getProviders(e){return e==="all"?this.providers:this.providers.filter(t=>t.platform===e)}async handleSearch(e,t){let s=await this.searchAll(e,t),r=s.flatMap(i=>i.listings);if(r.length===0)return{success:!0,data:`Keine Inserate gefunden f\xFCr "${t.query}".`};let n=r.map(i=>i.price).filter(i=>i!=null),o=[];o.push(`**${r.length} Inserate** f\xFCr "${t.query}"${s.length>1?` (${s.map(i=>`${i.platform}: ${i.listings.length}`).join(", ")})`:""}
772
+ `),o.push("| # | Titel | Preis | Standort | Plattform | Link |"),o.push("|---|-------|-------|----------|-----------|------|");for(let i=0;i<r.length;i++){let a=r[i],c=a.title.length>60?a.title.slice(0,57)+"...":a.title;o.push(`| ${i+1} | ${c} | ${ql(a.price,a.currency)} | ${a.location??"\u2014"} | ${a.platform} | [Link](${a.url}) |`)}return n.length>0&&(o.push(""),o.push(`**Min:** ${Math.min(...n).toFixed(2)} EUR | **Max:** ${Math.max(...n).toFixed(2)} EUR | **Median:** ${zl(n).toFixed(2)} EUR`)),{success:!0,data:o.join(`
750
773
  `)}}async handleCompare(e,t){let r=(await this.searchAll(e,t)).flatMap(d=>d.listings),n=r.map(d=>d.price).filter(d=>d!=null);if(n.length===0)return{success:!0,data:`Keine Inserate mit Preisangabe gefunden f\xFCr "${t.query}".`};let i=r.filter(d=>d.price!=null).sort((d,u)=>d.price-u.price).slice(0,5),a=n.reduce((d,u)=>d+u,0)/n.length,c=[];c.push(`**Preisvergleich** "${t.query}" \u2014 ${r.length} Inserate (${n.length} mit Preis)
751
- `),c.push("| Statistik | Wert |"),c.push("|-----------|------|"),c.push(`| Anzahl | ${n.length} |`),c.push(`| Minimum | ${Math.min(...n).toFixed(2)} EUR |`),c.push(`| Maximum | ${Math.max(...n).toFixed(2)} EUR |`),c.push(`| Median | ${Fl(n).toFixed(2)} EUR |`),c.push(`| Durchschnitt | ${a.toFixed(2)} EUR |`),c.push(""),c.push(`**G\xFCnstigste 5:**
752
- `),c.push("| # | Titel | Preis | Standort | Plattform | Link |"),c.push("|---|-------|-------|----------|-----------|------|");for(let d=0;d<i.length;d++){let u=i[d],p=u.title.length>60?u.title.slice(0,57)+"...":u.title;c.push(`| ${d+1} | ${p} | ${jl(u.price,u.currency)} | ${u.location??"\u2014"} | ${u.platform} | [Link](${u.url}) |`)}return{success:!0,data:c.join(`
753
- `)}}async searchAll(e,t){return await Promise.all(e.map(r=>r.search(t).catch(n=>({listings:[],totalCount:0,query:t.query,platform:r.platform,error:String(n)}))))}}});var Hl=_(()=>{"use strict";Bl();Cn();wi();Ti()});function np(){let l=new Date,e=new Date(l.getFullYear(),l.getMonth(),l.getDate()),t=new Date(e.getTime()+24*60*60*1e3);return{start:e.toISOString(),end:t.toISOString()}}function op(l){let e=l.split(",").map(s=>s.trim());for(let s of e){let r=s.match(/^\d{4,5}\s+(.+)/);if(r)return r[1].trim()}return e.length>=3?e[e.length-2]:e[e.length-1]}function ip(){let l=new Date().getDay();return l>=1&&l<=5}var _i,Dn,ql=_(()=>{"use strict";U();Me();m(np,"todayRange");_i=[{name:"calendar",skill:"calendar",input:{action:"list_events"},label:"Kalender"},{name:"weather",skill:"weather",input:{},label:"Wetter"},{name:"todo",skill:"todo",input:{action:"list"},label:"Lokale Todos"},{name:"mstodo",skill:"microsoft_todo",input:{action:"list_tasks"},label:"Microsoft To Do"},{name:"email",skill:"email",input:{action:"inbox"},label:"E-Mail"},{name:"energy",skill:"energy_price",input:{action:"briefing"},label:"Strompreise"},{name:"bmw",skill:"bmw",input:{action:"status"},label:"BMW Status"},{name:"home",skill:"homeassistant",input:{action:"briefing_summary"},label:"Smart Home"},{name:"infra",skill:"monitor",input:{},label:"Infrastruktur"}];m(op,"extractCity");m(ip,"isWeekday");Dn=class extends R{static{m(this,"BriefingSkill")}skillRegistry;alfredConfig;memoryRepo;metadata={name:"briefing",category:"productivity",description:'T\xE4gliches Morgenbriefing \u2014 sammelt Daten aus mehreren Skills parallel und liefert ein strukturiertes Ergebnis: Kalender, Wetter, Todos, E-Mails, Strompreise, Auto-Status, Smart Home, Infrastruktur. Mo\u2013Fr automatisch: Pendelzeit Heim\u2192B\xFCro + BMW-Akkucheck (wenn kein ausw\xE4rtiger Termin). "run" f\xFChrt das Briefing aus (optional mit location f\xFCr Wetter). "modules" zeigt verf\xFCgbare und aktive Module.',riskLevel:"read",version:"1.1.0",timeoutMs:6e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["run","modules"],description:"run = Briefing ausf\xFChren, modules = verf\xFCgbare Module anzeigen"},location:{type:"string",description:'Ort f\xFCr Wetterabfrage (default: aus Config oder "Vienna")'},modules:{type:"array",items:{type:"string"},description:"Optionale Liste aktiver Module (default: alle verf\xFCgbaren)"}},required:["action"]}};constructor(e,t,s){super(),this.skillRegistry=e,this.alfredConfig=t,this.memoryRepo=s}async execute(e,t){let s=e.action??"run";switch(s){case"run":return this.runBriefing(e,t);case"modules":return this.showModules();default:return{success:!1,error:`Unbekannte Aktion: ${s}`}}}getAvailableModules(){return _i.filter(e=>this.skillRegistry.has(e.skill))}showModules(){let e=this.getAvailableModules(),t=_i.map(n=>`${e.some(i=>i.name===n.name)?"\u2705":"\u274C"} ${n.name} (${n.label}) \u2192 ${n.skill}`),s=this.skillRegistry.has("routing"),r=!!(this.alfredConfig.briefing?.homeAddress&&this.alfredConfig.briefing?.officeAddress);return{success:!0,data:{available:e.map(n=>n.name),all:_i.map(n=>n.name),commuteAvailable:s,commuteConfigured:r},display:`Briefing-Module:
774
+ `),c.push("| Statistik | Wert |"),c.push("|-----------|------|"),c.push(`| Anzahl | ${n.length} |`),c.push(`| Minimum | ${Math.min(...n).toFixed(2)} EUR |`),c.push(`| Maximum | ${Math.max(...n).toFixed(2)} EUR |`),c.push(`| Median | ${zl(n).toFixed(2)} EUR |`),c.push(`| Durchschnitt | ${a.toFixed(2)} EUR |`),c.push(""),c.push(`**G\xFCnstigste 5:**
775
+ `),c.push("| # | Titel | Preis | Standort | Plattform | Link |"),c.push("|---|-------|-------|----------|-----------|------|");for(let d=0;d<i.length;d++){let u=i[d],p=u.title.length>60?u.title.slice(0,57)+"...":u.title;c.push(`| ${d+1} | ${p} | ${ql(u.price,u.currency)} | ${u.location??"\u2014"} | ${u.platform} | [Link](${u.url}) |`)}return{success:!0,data:c.join(`
776
+ `)}}async searchAll(e,t){return await Promise.all(e.map(r=>r.search(t).catch(n=>({listings:[],totalCount:0,query:t.query,platform:r.platform,error:String(n)}))))}}});var Gl=_(()=>{"use strict";Wl();Ln();_i();bi()});function cp(){let l=new Date,e=new Date(l.getFullYear(),l.getMonth(),l.getDate()),t=new Date(e.getTime()+24*60*60*1e3);return{start:e.toISOString(),end:t.toISOString()}}function lp(l){let e=l.split(",").map(s=>s.trim());for(let s of e){let r=s.match(/^\d{4,5}\s+(.+)/);if(r)return r[1].trim()}return e.length>=3?e[e.length-2]:e[e.length-1]}function dp(){let l=new Date().getDay();return l>=1&&l<=5}var Ei,Nn,Vl=_(()=>{"use strict";U();Pe();m(cp,"todayRange");Ei=[{name:"calendar",skill:"calendar",input:{action:"list_events"},label:"Kalender"},{name:"weather",skill:"weather",input:{},label:"Wetter"},{name:"todo",skill:"todo",input:{action:"list"},label:"Lokale Todos"},{name:"mstodo",skill:"microsoft_todo",input:{action:"list_tasks"},label:"Microsoft To Do"},{name:"email",skill:"email",input:{action:"inbox"},label:"E-Mail"},{name:"energy",skill:"energy_price",input:{action:"briefing"},label:"Strompreise"},{name:"bmw",skill:"bmw",input:{action:"status"},label:"BMW Status"},{name:"home",skill:"homeassistant",input:{action:"briefing_summary"},label:"Smart Home"},{name:"infra",skill:"monitor",input:{},label:"Infrastruktur"}];m(lp,"extractCity");m(dp,"isWeekday");Nn=class extends x{static{m(this,"BriefingSkill")}skillRegistry;alfredConfig;memoryRepo;metadata={name:"briefing",category:"productivity",description:'T\xE4gliches Morgenbriefing \u2014 sammelt Daten aus mehreren Skills parallel und liefert ein strukturiertes Ergebnis: Kalender, Wetter, Todos, E-Mails, Strompreise, Auto-Status, Smart Home, Infrastruktur. Mo\u2013Fr automatisch: Pendelzeit Heim\u2192B\xFCro + BMW-Akkucheck (wenn kein ausw\xE4rtiger Termin). "run" f\xFChrt das Briefing aus (optional mit location f\xFCr Wetter). "modules" zeigt verf\xFCgbare und aktive Module.',riskLevel:"read",version:"1.1.0",timeoutMs:6e4,inputSchema:{type:"object",properties:{action:{type:"string",enum:["run","modules"],description:"run = Briefing ausf\xFChren, modules = verf\xFCgbare Module anzeigen"},location:{type:"string",description:'Ort f\xFCr Wetterabfrage (default: aus Config oder "Vienna")'},modules:{type:"array",items:{type:"string"},description:"Optionale Liste aktiver Module (default: alle verf\xFCgbaren)"}},required:["action"]}};constructor(e,t,s){super(),this.skillRegistry=e,this.alfredConfig=t,this.memoryRepo=s}async execute(e,t){let s=e.action??"run";switch(s){case"run":return this.runBriefing(e,t);case"modules":return this.showModules();default:return{success:!1,error:`Unbekannte Aktion: ${s}`}}}getAvailableModules(){return Ei.filter(e=>this.skillRegistry.has(e.skill))}showModules(){let e=this.getAvailableModules(),t=Ei.map(n=>`${e.some(i=>i.name===n.name)?"\u2705":"\u274C"} ${n.name} (${n.label}) \u2192 ${n.skill}`),s=this.skillRegistry.has("routing"),r=!!(this.alfredConfig.briefing?.homeAddress&&this.alfredConfig.briefing?.officeAddress);return{success:!0,data:{available:e.map(n=>n.name),all:Ei.map(n=>n.name),commuteAvailable:s,commuteConfigured:r},display:`Briefing-Module:
754
777
  ${t.join(`
755
778
  `)}
756
779
 
757
- Pendler-Check (Mo\u2013Fr): ${s?r?"\u2705 konfiguriert (Config)":"\u23F3 Adressen werden aus Memories gelesen (oder ALFRED_BRIEFING_HOME/OFFICE_ADDRESS setzen)":"\u274C Routing-Skill nicht verf\xFCgbar"}`}}async runBriefing(e,t){let{home:s}=this.resolveAddresses(t),n=(s?op(s):void 0)??this.alfredConfig.briefing?.location??e.location??"Vienna",o=e.modules,i=this.getAvailableModules();o?.length&&(i=i.filter(h=>o.includes(h.name)));let a=this.resolveHaPreferences(t),c=i.map(h=>{let y={...h.input};if(h.name==="calendar"){let{start:E,end:$}=np();y.start=E,y.end=$}return h.name==="weather"&&(y.location=n),h.name==="home"&&(a.entities?.length&&(y.entities=a.entities),a.domains?.length&&(y.domains=a.domains)),{module:h,input:y}}),d=await Promise.all(c.map(h=>this.executeModule(h.module,h.input,t))),u=await this.runCommuteCheck(d,t);u&&d.push(u);let p=new Date().toLocaleDateString("de-AT",{weekday:"long",year:"numeric",month:"long",day:"numeric"}),g=[];g.push(`**\u2600\uFE0F Morgenbriefing \u2013 ${p}**
758
- `);for(let h of d)h.success&&h.display?g.push(`**${h.label}**
759
- ${this.cleanDisplay(h.module,h.display)}`):h.success&&h.data?g.push(`**${h.label}**
760
- ${typeof h.data=="string"?h.data:JSON.stringify(h.data,null,2)}`):!h.success&&h.error&&g.push(`**${h.label}**
761
- \u26A0\uFE0F ${h.error}`);let f=this.buildHighlights(d);return f.length>0&&(g.push("---"),g.push("**Highlights:**"),g.push(f.join(`
762
- `))),{success:!0,data:d,display:g.join(`
780
+ Pendler-Check (Mo\u2013Fr): ${s?r?"\u2705 konfiguriert (Config)":"\u23F3 Adressen werden aus Memories gelesen (oder ALFRED_BRIEFING_HOME/OFFICE_ADDRESS setzen)":"\u274C Routing-Skill nicht verf\xFCgbar"}`}}async runBriefing(e,t){let{home:s}=this.resolveAddresses(t),n=(s?lp(s):void 0)??this.alfredConfig.briefing?.location??e.location??"Vienna",o=e.modules,i=this.getAvailableModules();o?.length&&(i=i.filter(h=>o.includes(h.name)));let a=this.resolveHaPreferences(t),c=i.map(h=>{let y={...h.input};if(h.name==="calendar"){let{start:E,end:$}=cp();y.start=E,y.end=$}return h.name==="weather"&&(y.location=n),h.name==="home"&&(a.entities?.length&&(y.entities=a.entities),a.domains?.length&&(y.domains=a.domains)),{module:h,input:y}}),d=await Promise.all(c.map(h=>this.executeModule(h.module,h.input,t))),u=await this.runCommuteCheck(d,t);u&&d.push(u);let p=new Date().toLocaleDateString("de-AT",{weekday:"long",year:"numeric",month:"long",day:"numeric"}),f=[];f.push(`**\u2600\uFE0F Morgenbriefing \u2013 ${p}**
781
+ `);for(let h of d)h.success&&h.display?f.push(`**${h.label}**
782
+ ${this.cleanDisplay(h.module,h.display)}`):h.success&&h.data?f.push(`**${h.label}**
783
+ ${typeof h.data=="string"?h.data:JSON.stringify(h.data,null,2)}`):!h.success&&h.error&&f.push(`**${h.label}**
784
+ \u26A0\uFE0F ${h.error}`);let g=this.buildHighlights(d);return g.length>0&&(f.push("---"),f.push("**Highlights:**"),f.push(g.join(`
785
+ `))),{success:!0,data:d,display:f.join(`
763
786
 
764
- `)}}async executeModule(e,t,s){try{let r=this.skillRegistry.get(e.skill);if(!r)return{module:e.name,label:e.label,success:!1,error:"Skill nicht gefunden"};let n=await r.execute(t,s);return{module:e.name,label:e.label,success:n.success,data:n.data,display:n.display,error:n.error}}catch(r){return{module:e.name,label:e.label,success:!1,error:String(r instanceof Error?r.message:r)}}}async runCommuteCheck(e,t){if(!ip()||!this.skillRegistry.has("routing"))return null;let{home:s,office:r}=this.resolveAddresses(t);if(!s||!r)return null;let n=e.find(u=>u.module==="calendar");if(n?.success&&n.data&&this.detectExternalAppointment(n.data))return null;let o=[];o.push(this.executeModule({name:"commute",skill:"routing",input:{},label:"Pendelzeit"},{action:"route",origin:s,destination:r},t));let a=(await Promise.all(o))[0],c=e.find(u=>u.module==="bmw"),d=[];if(a.success&&a.display?d.push(`**Route Heim \u2192 B\xFCro:**
787
+ `)}}async executeModule(e,t,s){try{let r=this.skillRegistry.get(e.skill);if(!r)return{module:e.name,label:e.label,success:!1,error:"Skill nicht gefunden"};let n=await r.execute(t,s);return{module:e.name,label:e.label,success:n.success,data:n.data,display:n.display,error:n.error}}catch(r){return{module:e.name,label:e.label,success:!1,error:String(r instanceof Error?r.message:r)}}}async runCommuteCheck(e,t){if(!dp()||!this.skillRegistry.has("routing"))return null;let{home:s,office:r}=this.resolveAddresses(t);if(!s||!r)return null;let n=e.find(u=>u.module==="calendar");if(n?.success&&n.data&&this.detectExternalAppointment(n.data))return null;let o=[];o.push(this.executeModule({name:"commute",skill:"routing",input:{},label:"Pendelzeit"},{action:"route",origin:s,destination:r},t));let a=(await Promise.all(o))[0],c=e.find(u=>u.module==="bmw"),d=[];if(a.success&&a.display?d.push(`**Route Heim \u2192 B\xFCro:**
765
788
  ${a.display}`):a.error&&d.push(`**Route:** \u26A0\uFE0F ${a.error}`),c?.success&&c.data){let u=this.extractBatteryLevel(c.data);u!=null&&u<30?d.push(`
766
789
  \u26A0\uFE0F **BMW Akku niedrig (${u}%)** \u2014 Laden vor der Fahrt empfohlen!`):u!=null&&d.push(`
767
790
  \u{1F50B} BMW Akku: ${u}% \u2014 ausreichend f\xFCr den Arbeitsweg`)}return d.length===0?null:{module:"commute",label:"Arbeitsweg (Mo\u2013Fr)",success:!0,data:{route:a.data,bmwBattery:this.extractBatteryLevel(c?.data)},display:d.join(`
768
- `)}}resolveAddresses(e){let t=this.alfredConfig.briefing?.homeAddress,s=this.alfredConfig.briefing?.officeAddress;if(!this.memoryRepo)return{home:t,office:s};let r=t,n=s;for(let o of W(e)){let i=this.memoryRepo.search(o,"adresse");for(let a of i){let c=a.key.toLowerCase(),d=a.value;!r&&/heim|home|wohn|zuhause|privat/.test(c)&&(r=d),!n&&/büro|office|arbeit|firma|work/.test(c)&&(n=d)}if(r&&n)break}return{home:r,office:n}}resolveHaPreferences(e){let t=this.alfredConfig.briefing?.homeAssistant?.entities,s=this.alfredConfig.briefing?.homeAssistant?.domains;if(t?.length||s?.length)return{entities:t,domains:s};if(!this.memoryRepo)return{};for(let r of W(e)){let n=this.memoryRepo.search(r,"briefing");for(let o of n){let i=o.key.toLowerCase();if(/ha_entit|home.?assistant.*entit|briefing.*entit/.test(i)){let a=o.value.split(/[,;]\s*/).map(c=>c.trim()).filter(Boolean);if(a.length)return{entities:a}}if(/ha_domain|home.?assistant.*domain|briefing.*domain/.test(i)){let a=o.value.split(/[,;]\s*/).map(c=>c.trim()).filter(Boolean);if(a.length)return{domains:a}}}}return{}}detectExternalAppointment(e){return Array.isArray(e)?e.some(t=>{let s=t.location??t.loc??"";return!s||typeof s!="string"||/teams|zoom|meet\.google|webex|skype/i.test(s)?!1:s.trim().length>0}):!1}cleanDisplay(e,t){let s=t;return s=s.replace(/\s*\[(?:id:)?[A-Za-z0-9+/=\-]{20,}\]/g,""),e==="email"&&(s=s.replace(/^\d+\.\s*/gm,"- ").replace(/\[UNREAD\]/g,"\u{1F4E9}").replace(/\[ATT\]/g,"\u{1F4CE}").replace(/\n\s+Date:\s+\S+/g,"")),e==="calendar"&&(s=s.replace(/^\d+\s+event\(s\):\n?/i,"")),e==="todo"&&(s=s.replace(/^\|.*\|$/gm,r=>{if(/^[\|\s\-:]+$/.test(r))return"";let n=r.split("|").map(o=>o.trim()).filter(Boolean);if(n.length>=3){let o=n[0],i=n[1]&&n[1]!==""?` [${n[1]}]`:"",a=n[2];return`${o}${i} ${a}`}return r}),s=s.replace(/^\d+\s+todo\(s\):\n?/i,""),s=s.replace(/\n{2,}/g,`
791
+ `)}}resolveAddresses(e){let t=this.alfredConfig.briefing?.homeAddress,s=this.alfredConfig.briefing?.officeAddress;if(!this.memoryRepo)return{home:t,office:s};let r=t,n=s;for(let o of G(e)){let i=this.memoryRepo.search(o,"adresse");for(let a of i){let c=a.key.toLowerCase(),d=a.value;!r&&/heim|home|wohn|zuhause|privat/.test(c)&&(r=d),!n&&/büro|office|arbeit|firma|work/.test(c)&&(n=d)}if(r&&n)break}return{home:r,office:n}}resolveHaPreferences(e){let t=this.alfredConfig.briefing?.homeAssistant?.entities,s=this.alfredConfig.briefing?.homeAssistant?.domains;if(t?.length||s?.length)return{entities:t,domains:s};if(!this.memoryRepo)return{};for(let r of G(e)){let n=this.memoryRepo.search(r,"briefing");for(let o of n){let i=o.key.toLowerCase();if(/ha_entit|home.?assistant.*entit|briefing.*entit/.test(i)){let a=o.value.split(/[,;]\s*/).map(c=>c.trim()).filter(Boolean);if(a.length)return{entities:a}}if(/ha_domain|home.?assistant.*domain|briefing.*domain/.test(i)){let a=o.value.split(/[,;]\s*/).map(c=>c.trim()).filter(Boolean);if(a.length)return{domains:a}}}}return{}}detectExternalAppointment(e){return Array.isArray(e)?e.some(t=>{let s=t.location??t.loc??"";return!s||typeof s!="string"||/teams|zoom|meet\.google|webex|skype/i.test(s)?!1:s.trim().length>0}):!1}cleanDisplay(e,t){let s=t;return s=s.replace(/\s*\[(?:id:)?[A-Za-z0-9+/=\-]{20,}\]/g,""),e==="email"&&(s=s.replace(/^\d+\.\s*/gm,"- ").replace(/\[UNREAD\]/g,"\u{1F4E9}").replace(/\[ATT\]/g,"\u{1F4CE}").replace(/\n\s+Date:\s+\S+/g,"")),e==="calendar"&&(s=s.replace(/^\d+\s+event\(s\):\n?/i,"")),e==="todo"&&(s=s.replace(/^\|.*\|$/gm,r=>{if(/^[\|\s\-:]+$/.test(r))return"";let n=r.split("|").map(o=>o.trim()).filter(Boolean);if(n.length>=3){let o=n[0],i=n[1]&&n[1]!==""?` [${n[1]}]`:"",a=n[2];return`${o}${i} ${a}`}return r}),s=s.replace(/^\d+\s+todo\(s\):\n?/i,""),s=s.replace(/\n{2,}/g,`
769
792
  `)),e==="mstodo"&&(s=s.replace(/\s*\[(?:taskId|listId)=[^\]]+\]/g,"")),s=s.replace(/^\[[\w-]+\]\s*/gm,""),s=s.replace(/\n{3,}/g,`
770
793
 
771
- `),s.trim()}buildHighlights(e){let t=[],s=e.find(i=>i.module==="bmw")?.data;if(s){let i=this.extractBatteryLevel(s);i!=null&&i<30&&t.push(`\u{1F697} BMW Akku bei ${i}% \u2014 laden empfohlen`)}let r=e.find(i=>i.module==="infra");r&&(!r.success||r.display?.includes("\u26A0\uFE0F"))&&t.push("\u{1F534} Infrastruktur-Warnung vorhanden");let n=e.find(i=>i.module==="energy")?.data;if(n){let i=n.currentCt,a=n.avgCt;i!=null&&a!=null&&i<a&&t.push(`\u26A1 Aktuell g\xFCnstiger Strom (${i} ct/kWh)`)}let o=e.find(i=>i.module==="calendar")?.data;return Array.isArray(o)&&o.length>0&&t.push(`\u{1F4C5} ${o.length} Termin(e) heute`),t}extractBatteryLevel(e){if(!e||typeof e!="object")return null;let t=e,s=t.chargingLevelPercent??t.batterySoc??t.soc??t.chargingState?.chargingLevelPercent;if(typeof s=="number")return s;if(typeof s=="string"){let r=parseFloat(s);return isNaN(r)?null:r}return null}}});import{spawn as ap}from"node:child_process";import zl from"node:fs";import Vl from"node:path";function up(l){let e={};for(let[t,s]of Object.entries(l))e[t]=s.replace(/\$\{(\w+)\}/g,(r,n)=>process.env[n]??"");return e}function mp(l,e){return l.map(t=>t.replace(/\{\{prompt\}\}/g,e))}function Ln(l){return l.length<=Wl?l:`[...truncated...]
772
- `+l.slice(-Wl)}function Gl(l){let e=new Map;function t(s){let r;try{r=zl.readdirSync(s,{withFileTypes:!0})}catch{return}for(let n of r){if(dp.has(n.name))continue;let o=Vl.join(s,n.name);if(n.isDirectory())t(o);else if(n.isFile())try{let i=zl.statSync(o);e.set(o,i.mtimeMs)}catch{}}}return m(t,"walk"),t(l),e}function pp(l,e,t){let s=[];for(let[r,n]of e){let o=l.get(r);(o===void 0||n>o)&&s.push(Vl.relative(t,r))}return s.sort()}async function yr(l,e,t={}){let s=t.cwd??l.cwd??process.cwd(),r=t.timeoutMs??l.timeoutMs??cp,n=Math.min(r,lp),o=mp(l.argsTemplate,e),i={...process.env,...l.env?up(l.env):{}},a=process.platform==="win32",c=Gl(s),d=Date.now();return new Promise(u=>{let p=ap(l.command,o,{cwd:s,env:i,shell:a,stdio:l.promptVia==="stdin"?["pipe","pipe","pipe"]:["ignore","pipe","pipe"]}),g="",f="",h=!1,y=setTimeout(()=>{h=!0,p.kill("SIGTERM"),setTimeout(()=>p.kill("SIGKILL"),5e3)},n);p.stdout?.on("data",E=>{g+=E.toString()}),p.stderr?.on("data",E=>{let $=E.toString();if(f+=$,t.onProgress){let v=$.trim().split(`
773
- `).pop();v&&t.onProgress(`[${l.name}] ${v}`)}}),l.promptVia==="stdin"&&p.stdin&&(p.stdin.write(e),p.stdin.end()),p.on("close",E=>{clearTimeout(y);let $=Date.now()-d,v=Gl(s),P=pp(c,v,s);u({stdout:Ln(g),stderr:Ln(f),exitCode:h?124:E??1,durationMs:$,modifiedFiles:P})}),p.on("error",E=>{clearTimeout(y);let $=Date.now()-d;u({stdout:Ln(g),stderr:Ln(f+`
774
- `+E.message),exitCode:127,durationMs:$,modifiedFiles:[]})})})}var cp,lp,Wl,dp,Nn=_(()=>{"use strict";cp=3e5,lp=9e5,Wl=1e5,dp=new Set([".git","node_modules",".next","dist",".cache"]);m(up,"resolveEnv");m(mp,"buildArgs");m(Ln,"truncateOutput");m(Gl,"snapshotMtimes");m(pp,"detectModifiedFiles");m(yr,"executeAgent")});import{execFile as hp}from"node:child_process";function je(l,e){return new Promise((t,s)=>{hp("git",l,{cwd:e.cwd,maxBuffer:10*1024*1024},(r,n,o)=>{if(r){let i=o?.trim()||r.message;s(new Error(`git ${l[0]} failed: ${i}`));return}t(n.trim())})})}async function Mn(l){try{let e=await je(["rev-parse","--abbrev-ref","HEAD"],l),t=await je(["status","--porcelain"],l);return{isRepo:!0,branch:e,dirty:t.length>0}}catch{return{isRepo:!1,branch:"",dirty:!1}}}async function bi(l,e){await je(["checkout","-b",l],e)}async function Ei(l){await je(["add","-A"],l)}async function $i(l,e){await je(["commit","-m",l],e);let t=await je(["rev-parse","--short","HEAD"],e),r=(await je(["diff","--stat","HEAD~1","HEAD"],e)).split(`
775
- `).length-1;return{sha:t,message:l,filesChanged:Math.max(r,0)}}async function ki(l,e,t){await je(["push","-u",l,e],t)}function vi(l){return`alfred/${l.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,60)}`}async function ps(l,e){try{return await je(["remote","get-url",l],e)}catch{return null}}function hs(l){let e=l.match(/^git@([^:]+):(.+?)(?:\.git)?$/);if(e){let s=e[1],n=e[2].split("/");if(n.length<2)return null;let o=n.pop();return{owner:n.join("/"),repo:o,baseUrl:`https://${s}`}}let t=l.match(/^https?:\/\/([^/]+)\/(.+?)(?:\.git)?$/);if(t){let s=t[1],n=t[2].split("/");if(n.length<2)return null;let o=n.pop();return{owner:n.join("/"),repo:o,baseUrl:`https://${s}`}}return null}async function wr(l){await je(["init"],l)}async function Tr(l,e,t){await je(["remote","add",l,e],t)}var Si=_(()=>{"use strict";m(je,"git");m(Mn,"gitStatus");m(bi,"gitCreateBranch");m(Ei,"gitStageAll");m($i,"gitCommit");m(ki,"gitPush");m(vi,"slugifyBranch");m(ps,"gitGetRemoteUrl");m(hs,"parseRemoteUrl");m(wr,"gitInitRepo");m(Tr,"gitAddRemote")});function fs(l){switch(l.provider){case"github":{if(!l.github)throw new Error('ForgeConfig.github is required when provider is "github"');return new Ai(l.github)}case"gitlab":{if(!l.gitlab)throw new Error('ForgeConfig.gitlab is required when provider is "gitlab"');return new Ii(l.gitlab)}default:throw new Error(`Unknown forge provider: ${l.provider}`)}}var xt,Ai,Ii,xi=_(()=>{"use strict";xt=class{static{m(this,"ForgeClient")}},Ai=class extends xt{static{m(this,"GitHubForgeClient")}config;baseUrl;constructor(e){super(),this.config=e,this.baseUrl=e.baseUrl?.replace(/\/+$/,"")??"https://api.github.com"}async createPullRequest(e,t){let s=`${this.baseUrl}/repos/${e.owner}/${e.repo}/pulls`,r=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,Accept:"application/vnd.github+json","Content-Type":"application/json"},body:JSON.stringify({title:t.title,body:t.body,head:t.head,base:t.base})});if(!r.ok){let o=await r.text();throw new Error(`GitHub PR creation failed (${r.status}): ${o}`)}let n=await r.json();return{id:n.id,url:n.html_url,number:n.number,state:n.state}}async getPipelineStatus(e,t){let s=`${this.baseUrl}/repos/${e.owner}/${e.repo}/commits/${t}/status`,r=await fetch(s,{headers:{Authorization:`Bearer ${this.config.token}`,Accept:"application/vnd.github+json"}});if(!r.ok)return{state:"unknown"};let o=(await r.json()).state;return{state:{pending:"pending",success:"success",failure:"failure",error:"failure"}[o]??"unknown"}}async createProject(e){let t=`${this.baseUrl}/user/repos`,s=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,Accept:"application/vnd.github+json","Content-Type":"application/json"},body:JSON.stringify({name:e.name,description:e.description??"",private:(e.visibility??"private")==="private"})});if(!s.ok){let n=await s.text();throw new Error(`GitHub project creation failed (${s.status}): ${n}`)}let r=await s.json();return{id:r.id,url:r.html_url,cloneUrl:r.clone_url}}},Ii=class extends xt{static{m(this,"GitLabForgeClient")}config;baseUrl;constructor(e){super(),this.config=e,this.baseUrl=e.baseUrl?.replace(/\/+$/,"")??"https://gitlab.com"}async createPullRequest(e,t){let s=encodeURIComponent(`${e.owner}/${e.repo}`),r=`${this.baseUrl}/api/v4/projects/${s}/merge_requests`,n=await fetch(r,{method:"POST",headers:{"PRIVATE-TOKEN":this.config.token,"Content-Type":"application/json"},body:JSON.stringify({title:t.title,description:t.body,source_branch:t.head,target_branch:t.base})});if(!n.ok){let i=await n.text();throw new Error(`GitLab MR creation failed (${n.status}): ${i}`)}let o=await n.json();return{id:o.id,url:o.web_url,number:o.iid,state:o.state}}async getPipelineStatus(e,t){let s=encodeURIComponent(`${e.owner}/${e.repo}`),r=`${this.baseUrl}/api/v4/projects/${s}/pipelines?ref=${encodeURIComponent(t)}&per_page=1`,n=await fetch(r,{headers:{"PRIVATE-TOKEN":this.config.token}});if(!n.ok)return{state:"unknown"};let o=await n.json();if(o.length===0)return{state:"unknown"};let i=o[0],a=i.status;return{state:{pending:"pending",running:"running",success:"success",failed:"failure",canceled:"failure"}[a]??"unknown",url:i.web_url}}async createProject(e){let t=`${this.baseUrl}/api/v4/projects`,s=await fetch(t,{method:"POST",headers:{"PRIVATE-TOKEN":this.config.token,"Content-Type":"application/json"},body:JSON.stringify({name:e.name,description:e.description??"",visibility:e.visibility??"private"})});if(!s.ok){let n=await s.text();throw new Error(`GitLab project creation failed (${s.status}): ${n}`)}let r=await s.json();return{id:r.id,url:r.web_url,cloneUrl:r.http_url_to_repo}}};m(fs,"createForgeClient")});function Kl(l,e){return l.length<=e?l:l.slice(0,e)+`
776
- [...truncated]`}function Xl(l){let e=l.replace(/^```(?:json)?\s*\n?/m,"").replace(/\n?```\s*$/m,"");return JSON.parse(e)}async function Ep(l,e,t){let r=`Available agents:
794
+ `),s.trim()}buildHighlights(e){let t=[],s=e.find(i=>i.module==="bmw")?.data;if(s){let i=this.extractBatteryLevel(s);i!=null&&i<30&&t.push(`\u{1F697} BMW Akku bei ${i}% \u2014 laden empfohlen`)}let r=e.find(i=>i.module==="infra");r&&(!r.success||r.display?.includes("\u26A0\uFE0F"))&&t.push("\u{1F534} Infrastruktur-Warnung vorhanden");let n=e.find(i=>i.module==="energy")?.data;if(n){let i=n.currentCt,a=n.avgCt;i!=null&&a!=null&&i<a&&t.push(`\u26A1 Aktuell g\xFCnstiger Strom (${i} ct/kWh)`)}let o=e.find(i=>i.module==="calendar")?.data;return Array.isArray(o)&&o.length>0&&t.push(`\u{1F4C5} ${o.length} Termin(e) heute`),t}extractBatteryLevel(e){if(!e||typeof e!="object")return null;let t=e,s=t.chargingLevelPercent??t.batterySoc??t.soc??t.chargingState?.chargingLevelPercent;if(typeof s=="number")return s;if(typeof s=="string"){let r=parseFloat(s);return isNaN(r)?null:r}return null}}});import{spawn as up}from"node:child_process";import Kl from"node:fs";import Jl from"node:path";function fp(l){let e={};for(let[t,s]of Object.entries(l))e[t]=s.replace(/\$\{(\w+)\}/g,(r,n)=>process.env[n]??"");return e}function gp(l,e){return l.map(t=>t.replace(/\{\{prompt\}\}/g,e))}function Mn(l){return l.length<=Xl?l:`[...truncated...]
795
+ `+l.slice(-Xl)}function Yl(l){let e=new Map;function t(s){let r;try{r=Kl.readdirSync(s,{withFileTypes:!0})}catch{return}for(let n of r){if(hp.has(n.name))continue;let o=Jl.join(s,n.name);if(n.isDirectory())t(o);else if(n.isFile())try{let i=Kl.statSync(o);e.set(o,i.mtimeMs)}catch{}}}return m(t,"walk"),t(l),e}function yp(l,e,t){let s=[];for(let[r,n]of e){let o=l.get(r);(o===void 0||n>o)&&s.push(Jl.relative(t,r))}return s.sort()}async function br(l,e,t={}){let s=t.cwd??l.cwd??process.cwd(),r=t.timeoutMs??l.timeoutMs??mp,n=Math.min(r,pp),o=gp(l.argsTemplate,e),i={...process.env,...l.env?fp(l.env):{}},a=process.platform==="win32",c=Yl(s),d=Date.now();return new Promise(u=>{let p=up(l.command,o,{cwd:s,env:i,shell:a,stdio:l.promptVia==="stdin"?["pipe","pipe","pipe"]:["ignore","pipe","pipe"]}),f="",g="",h=!1,y=setTimeout(()=>{h=!0,p.kill("SIGTERM"),setTimeout(()=>p.kill("SIGKILL"),5e3)},n);p.stdout?.on("data",E=>{f+=E.toString()}),p.stderr?.on("data",E=>{let $=E.toString();if(g+=$,t.onProgress){let v=$.trim().split(`
796
+ `).pop();v&&t.onProgress(`[${l.name}] ${v}`)}}),l.promptVia==="stdin"&&p.stdin&&(p.stdin.write(e),p.stdin.end()),p.on("close",E=>{clearTimeout(y);let $=Date.now()-d,v=Yl(s),j=yp(c,v,s);u({stdout:Mn(f),stderr:Mn(g),exitCode:h?124:E??1,durationMs:$,modifiedFiles:j})}),p.on("error",E=>{clearTimeout(y);let $=Date.now()-d;u({stdout:Mn(f),stderr:Mn(g+`
797
+ `+E.message),exitCode:127,durationMs:$,modifiedFiles:[]})})})}var mp,pp,Xl,hp,On=_(()=>{"use strict";mp=3e5,pp=9e5,Xl=1e5,hp=new Set([".git","node_modules",".next","dist",".cache"]);m(fp,"resolveEnv");m(gp,"buildArgs");m(Mn,"truncateOutput");m(Yl,"snapshotMtimes");m(yp,"detectModifiedFiles");m(br,"executeAgent")});import{execFile as wp}from"node:child_process";function He(l,e){return new Promise((t,s)=>{wp("git",l,{cwd:e.cwd,maxBuffer:10*1024*1024},(r,n,o)=>{if(r){let i=o?.trim()||r.message;s(new Error(`git ${l[0]} failed: ${i}`));return}t(n.trim())})})}async function Pn(l){try{let e=await He(["rev-parse","--abbrev-ref","HEAD"],l),t=await He(["status","--porcelain"],l);return{isRepo:!0,branch:e,dirty:t.length>0}}catch{return{isRepo:!1,branch:"",dirty:!1}}}async function $i(l,e){await He(["checkout","-b",l],e)}async function ki(l){await He(["add","-A"],l)}async function vi(l,e){await He(["commit","-m",l],e);let t=await He(["rev-parse","--short","HEAD"],e),r=(await He(["diff","--stat","HEAD~1","HEAD"],e)).split(`
798
+ `).length-1;return{sha:t,message:l,filesChanged:Math.max(r,0)}}async function Si(l,e,t){await He(["push","-u",l,e],t)}function Ai(l){return`alfred/${l.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,60)}`}async function fs(l,e){try{return await He(["remote","get-url",l],e)}catch{return null}}function gs(l){let e=l.match(/^git@([^:]+):(.+?)(?:\.git)?$/);if(e){let s=e[1],n=e[2].split("/");if(n.length<2)return null;let o=n.pop();return{owner:n.join("/"),repo:o,baseUrl:`https://${s}`}}let t=l.match(/^https?:\/\/([^/]+)\/(.+?)(?:\.git)?$/);if(t){let s=t[1],n=t[2].split("/");if(n.length<2)return null;let o=n.pop();return{owner:n.join("/"),repo:o,baseUrl:`https://${s}`}}return null}async function Er(l){await He(["init"],l)}async function $r(l,e,t){await He(["remote","add",l,e],t)}var Ii=_(()=>{"use strict";m(He,"git");m(Pn,"gitStatus");m($i,"gitCreateBranch");m(ki,"gitStageAll");m(vi,"gitCommit");m(Si,"gitPush");m(Ai,"slugifyBranch");m(fs,"gitGetRemoteUrl");m(gs,"parseRemoteUrl");m(Er,"gitInitRepo");m($r,"gitAddRemote")});function ys(l){switch(l.provider){case"github":{if(!l.github)throw new Error('ForgeConfig.github is required when provider is "github"');return new xi(l.github)}case"gitlab":{if(!l.gitlab)throw new Error('ForgeConfig.gitlab is required when provider is "gitlab"');return new Ri(l.gitlab)}default:throw new Error(`Unknown forge provider: ${l.provider}`)}}var Rt,xi,Ri,Ci=_(()=>{"use strict";Rt=class{static{m(this,"ForgeClient")}},xi=class extends Rt{static{m(this,"GitHubForgeClient")}config;baseUrl;constructor(e){super(),this.config=e,this.baseUrl=e.baseUrl?.replace(/\/+$/,"")??"https://api.github.com"}async createPullRequest(e,t){let s=`${this.baseUrl}/repos/${e.owner}/${e.repo}/pulls`,r=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,Accept:"application/vnd.github+json","Content-Type":"application/json"},body:JSON.stringify({title:t.title,body:t.body,head:t.head,base:t.base})});if(!r.ok){let o=await r.text();throw new Error(`GitHub PR creation failed (${r.status}): ${o}`)}let n=await r.json();return{id:n.id,url:n.html_url,number:n.number,state:n.state}}async getPipelineStatus(e,t){let s=`${this.baseUrl}/repos/${e.owner}/${e.repo}/commits/${t}/status`,r=await fetch(s,{headers:{Authorization:`Bearer ${this.config.token}`,Accept:"application/vnd.github+json"}});if(!r.ok)return{state:"unknown"};let o=(await r.json()).state;return{state:{pending:"pending",success:"success",failure:"failure",error:"failure"}[o]??"unknown"}}async createProject(e){let t=`${this.baseUrl}/user/repos`,s=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.config.token}`,Accept:"application/vnd.github+json","Content-Type":"application/json"},body:JSON.stringify({name:e.name,description:e.description??"",private:(e.visibility??"private")==="private"})});if(!s.ok){let n=await s.text();throw new Error(`GitHub project creation failed (${s.status}): ${n}`)}let r=await s.json();return{id:r.id,url:r.html_url,cloneUrl:r.clone_url}}},Ri=class extends Rt{static{m(this,"GitLabForgeClient")}config;baseUrl;constructor(e){super(),this.config=e,this.baseUrl=e.baseUrl?.replace(/\/+$/,"")??"https://gitlab.com"}async createPullRequest(e,t){let s=encodeURIComponent(`${e.owner}/${e.repo}`),r=`${this.baseUrl}/api/v4/projects/${s}/merge_requests`,n=await fetch(r,{method:"POST",headers:{"PRIVATE-TOKEN":this.config.token,"Content-Type":"application/json"},body:JSON.stringify({title:t.title,description:t.body,source_branch:t.head,target_branch:t.base})});if(!n.ok){let i=await n.text();throw new Error(`GitLab MR creation failed (${n.status}): ${i}`)}let o=await n.json();return{id:o.id,url:o.web_url,number:o.iid,state:o.state}}async getPipelineStatus(e,t){let s=encodeURIComponent(`${e.owner}/${e.repo}`),r=`${this.baseUrl}/api/v4/projects/${s}/pipelines?ref=${encodeURIComponent(t)}&per_page=1`,n=await fetch(r,{headers:{"PRIVATE-TOKEN":this.config.token}});if(!n.ok)return{state:"unknown"};let o=await n.json();if(o.length===0)return{state:"unknown"};let i=o[0],a=i.status;return{state:{pending:"pending",running:"running",success:"success",failed:"failure",canceled:"failure"}[a]??"unknown",url:i.web_url}}async createProject(e){let t=`${this.baseUrl}/api/v4/projects`,s=await fetch(t,{method:"POST",headers:{"PRIVATE-TOKEN":this.config.token,"Content-Type":"application/json"},body:JSON.stringify({name:e.name,description:e.description??"",visibility:e.visibility??"private"})});if(!s.ok){let n=await s.text();throw new Error(`GitLab project creation failed (${s.status}): ${n}`)}let r=await s.json();return{id:r.id,url:r.web_url,cloneUrl:r.http_url_to_repo}}};m(ys,"createForgeClient")});function Zl(l,e){return l.length<=e?l:l.slice(0,e)+`
799
+ [...truncated]`}function Ql(l){let e=l.replace(/^```(?:json)?\s*\n?/m,"").replace(/\n?```\s*$/m,"");return JSON.parse(e)}async function Sp(l,e,t){let r=`Available agents:
777
800
  ${e.map(a=>`- ${a.name}: command="${a.command}"`).join(`
778
801
  `)}
779
802
 
780
803
  Task:
781
- ${l}`,n=await t.complete({system:_p,messages:[{role:"user",content:r}],temperature:.2,tier:"strong"}),o=Xl(n.content);if(!Array.isArray(o.subtasks)||o.subtasks.length===0)throw new Error("LLM returned an empty plan with no subtasks");let i=new Set(e.map(a=>a.name));for(let a of o.subtasks)if(!i.has(a.agent))throw new Error(`Plan references unknown agent "${a.agent}". Available: ${[...i].join(", ")}`);return o}async function $p(l,e,t,s){let r=new Ri(t),n=[],o=l.map(async i=>{await r.acquire();try{s?.(`Running ${i.id}: ${i.description}`);let a=e.get(i.agent),c=await yr(a,i.prompt,{onProgress:s?u=>s(`[${i.id}] ${u}`):void 0}),d={subtask:i,execution:c};return n.push(d),s?.(`Completed ${i.id}: exit=${c.exitCode}, ${c.modifiedFiles.length} files modified`),d}finally{r.release()}});return await Promise.all(o),n}async function kp(l,e,t){let s=e.map(o=>{let i=Kl(o.execution.stdout,wp),a=Kl(o.execution.stderr,Tp);return[`### ${o.subtask.id} (${o.subtask.description})`,`Agent: ${o.subtask.agent}`,`Exit code: ${o.execution.exitCode}`,`Modified files: ${o.execution.modifiedFiles.join(", ")||"none"}`,i?`stdout:
804
+ ${l}`,n=await t.complete({system:kp,messages:[{role:"user",content:r}],temperature:.2,tier:"strong"}),o=Ql(n.content);if(!Array.isArray(o.subtasks)||o.subtasks.length===0)throw new Error("LLM returned an empty plan with no subtasks");let i=new Set(e.map(a=>a.name));for(let a of o.subtasks)if(!i.has(a.agent))throw new Error(`Plan references unknown agent "${a.agent}". Available: ${[...i].join(", ")}`);return o}async function Ap(l,e,t,s){let r=new Di(t),n=[],o=l.map(async i=>{await r.acquire();try{s?.(`Running ${i.id}: ${i.description}`);let a=e.get(i.agent),c=await br(a,i.prompt,{onProgress:s?u=>s(`[${i.id}] ${u}`):void 0}),d={subtask:i,execution:c};return n.push(d),s?.(`Completed ${i.id}: exit=${c.exitCode}, ${c.modifiedFiles.length} files modified`),d}finally{r.release()}});return await Promise.all(o),n}async function Ip(l,e,t){let s=e.map(o=>{let i=Zl(o.execution.stdout,Ep),a=Zl(o.execution.stderr,$p);return[`### ${o.subtask.id} (${o.subtask.description})`,`Agent: ${o.subtask.agent}`,`Exit code: ${o.execution.exitCode}`,`Modified files: ${o.execution.modifiedFiles.join(", ")||"none"}`,i?`stdout:
782
805
  ${i}`:"",a?`stderr:
783
806
  ${a}`:""].filter(Boolean).join(`
784
807
  `)}).join(`
@@ -787,11 +810,11 @@ ${a}`:""].filter(Boolean).join(`
787
810
  ${l}
788
811
 
789
812
  Results:
790
- ${s}`,n=await t.complete({system:bp,messages:[{role:"user",content:r}],temperature:.2,tier:"strong"});try{let o=Xl(n.content);return{approved:o.approved??!0,summary:o.summary??"",fixTasks:Array.isArray(o.fixTasks)?o.fixTasks:[]}}catch{return{approved:!0,summary:n.content,fixTasks:[]}}}async function Rt(l,e,t,s={}){let r=Math.min(s.maxIterations??fp,gp),n=s.maxConcurrent??yp,o=s.onProgress,i=Date.now(),a=new Map(e.map(h=>[h.name,h]));o?.("Planning subtasks...");let c=await Ep(l,e,t);o?.(`Plan created: ${c.subtasks.length} subtask(s) \u2014 ${c.reasoning}`);let d=[],u=c.subtasks,p=0,g="";for(;p<r;){p++,o?.(`Iteration ${p}: executing ${u.length} subtask(s)...`);let h=await $p(u,a,n,o);d=d.concat(h),o?.(`Iteration ${p}: validating results...`);let y=await kp(l,d,t);if(g=y.summary,y.approved||y.fixTasks.length===0)break;let E=y.fixTasks.filter($=>a.has($.agent)?!0:(o?.(`Warning: fix task "${$.id}" references unknown agent "${$.agent}", skipping`),!1));if(E.length===0)break;u=E,o?.(`Validation requested ${E.length} fix task(s), iterating...`)}let f=[...new Set(d.flatMap(h=>h.execution.modifiedFiles))].sort();return{plan:c,iterations:p,subtaskResults:d,allModifiedFiles:f,summary:g,totalDurationMs:Date.now()-i}}async function _r(l,e,t,s={}){let r=s.onProgress,n=s.cwd??process.cwd(),o={warnings:[]},i=await Mn({cwd:n});if(!i.isRepo)try{await wr({cwd:n}),i=await Mn({cwd:n}),r?.("Initialised new git repository")}catch{return o.warnings.push("Not a git repository and init failed \u2014 skipping git operations"),r?.("Warning: not a git repository, skipping git operations"),{...await Rt(l,e,t,s),git:o}}let a=null,c=await ps("origin",{cwd:n});if(c){let f=hs(c);f?(a={owner:f.owner,repo:f.repo},r?.(`Detected remote: ${f.owner}/${f.repo} (${f.baseUrl})`)):o.warnings.push(`Could not parse remote URL: ${c}`)}let d=s.forge;if(!c&&d)try{let f=fs(d),h=n.split(/[\\/]/).pop()??"alfred-project";r?.(`No remote found \u2014 creating project "${h}" on forge...`);let y=await f.createProject({name:h,visibility:"private"});await Tr("origin",y.cloneUrl,{cwd:n});let E=hs(y.cloneUrl);E&&(a={owner:E.owner,repo:E.repo}),r?.(`Project created: ${y.url} \u2014 remote "origin" set`)}catch(f){let h=f instanceof Error?f.message:String(f);o.warnings.push(`Project creation failed: ${h}`),r?.(`Warning: project creation failed \u2014 ${h}`)}let u=vi(l);try{await bi(u,{cwd:n}),o.branch=u,r?.(`Created branch: ${u}`)}catch(f){throw new Error(`Failed to create branch "${u}": ${f instanceof Error?f.message:String(f)}`)}let p=await Rt(l,e,t,s);try{await Ei({cwd:n});let f=`feat: ${l.slice(0,72)}
813
+ ${s}`,n=await t.complete({system:vp,messages:[{role:"user",content:r}],temperature:.2,tier:"strong"});try{let o=Ql(n.content);return{approved:o.approved??!0,summary:o.summary??"",fixTasks:Array.isArray(o.fixTasks)?o.fixTasks:[]}}catch{return{approved:!0,summary:n.content,fixTasks:[]}}}async function Ct(l,e,t,s={}){let r=Math.min(s.maxIterations??Tp,_p),n=s.maxConcurrent??bp,o=s.onProgress,i=Date.now(),a=new Map(e.map(h=>[h.name,h]));o?.("Planning subtasks...");let c=await Sp(l,e,t);o?.(`Plan created: ${c.subtasks.length} subtask(s) \u2014 ${c.reasoning}`);let d=[],u=c.subtasks,p=0,f="";for(;p<r;){p++,o?.(`Iteration ${p}: executing ${u.length} subtask(s)...`);let h=await Ap(u,a,n,o);d=d.concat(h),o?.(`Iteration ${p}: validating results...`);let y=await Ip(l,d,t);if(f=y.summary,y.approved||y.fixTasks.length===0)break;let E=y.fixTasks.filter($=>a.has($.agent)?!0:(o?.(`Warning: fix task "${$.id}" references unknown agent "${$.agent}", skipping`),!1));if(E.length===0)break;u=E,o?.(`Validation requested ${E.length} fix task(s), iterating...`)}let g=[...new Set(d.flatMap(h=>h.execution.modifiedFiles))].sort();return{plan:c,iterations:p,subtaskResults:d,allModifiedFiles:g,summary:f,totalDurationMs:Date.now()-i}}async function kr(l,e,t,s={}){let r=s.onProgress,n=s.cwd??process.cwd(),o={warnings:[]},i=await Pn({cwd:n});if(!i.isRepo)try{await Er({cwd:n}),i=await Pn({cwd:n}),r?.("Initialised new git repository")}catch{return o.warnings.push("Not a git repository and init failed \u2014 skipping git operations"),r?.("Warning: not a git repository, skipping git operations"),{...await Ct(l,e,t,s),git:o}}let a=null,c=await fs("origin",{cwd:n});if(c){let g=gs(c);g?(a={owner:g.owner,repo:g.repo},r?.(`Detected remote: ${g.owner}/${g.repo} (${g.baseUrl})`)):o.warnings.push(`Could not parse remote URL: ${c}`)}let d=s.forge;if(!c&&d)try{let g=ys(d),h=n.split(/[\\/]/).pop()??"alfred-project";r?.(`No remote found \u2014 creating project "${h}" on forge...`);let y=await g.createProject({name:h,visibility:"private"});await $r("origin",y.cloneUrl,{cwd:n});let E=gs(y.cloneUrl);E&&(a={owner:E.owner,repo:E.repo}),r?.(`Project created: ${y.url} \u2014 remote "origin" set`)}catch(g){let h=g instanceof Error?g.message:String(g);o.warnings.push(`Project creation failed: ${h}`),r?.(`Warning: project creation failed \u2014 ${h}`)}let u=Ai(l);try{await $i(u,{cwd:n}),o.branch=u,r?.(`Created branch: ${u}`)}catch(g){throw new Error(`Failed to create branch "${u}": ${g instanceof Error?g.message:String(g)}`)}let p=await Ct(l,e,t,s);try{await ki({cwd:n});let g=`feat: ${l.slice(0,72)}
791
814
 
792
- Orchestrated by Alfred (${p.iterations} iteration(s), ${p.allModifiedFiles.length} file(s))`,h=await $i(f,{cwd:n});o.commit=h,r?.(`Committed: ${h.sha} (${h.filesChanged} files changed)`)}catch(f){let h=f instanceof Error?f.message:String(f);return o.warnings.push(`Commit failed: ${h}`),r?.(`Warning: commit failed \u2014 ${h}`),{...p,git:o}}if(!await ps("origin",{cwd:n}))return o.warnings.push("No remote configured \u2014 skipping push and PR creation"),r?.("No remote configured, skipping push and PR"),{...p,git:o};try{await ki("origin",u,{cwd:n}),r?.(`Pushed branch: ${u}`)}catch(f){let h=f instanceof Error?f.message:String(f);return o.warnings.push(`Push failed: ${h}`),r?.(`Warning: push failed \u2014 ${h}`),{...p,git:o}}if(d&&a)try{let f=fs(d),h=s.baseBranch??d.baseBranch??"main",y=s.prTitle??`feat: ${l.slice(0,72)}`,E=["## Summary",p.summary,"",`**Iterations:** ${p.iterations}`,`**Modified files:** ${p.allModifiedFiles.length}`,p.allModifiedFiles.map(v=>`- \`${v}\``).join(`
815
+ Orchestrated by Alfred (${p.iterations} iteration(s), ${p.allModifiedFiles.length} file(s))`,h=await vi(g,{cwd:n});o.commit=h,r?.(`Committed: ${h.sha} (${h.filesChanged} files changed)`)}catch(g){let h=g instanceof Error?g.message:String(g);return o.warnings.push(`Commit failed: ${h}`),r?.(`Warning: commit failed \u2014 ${h}`),{...p,git:o}}if(!await fs("origin",{cwd:n}))return o.warnings.push("No remote configured \u2014 skipping push and PR creation"),r?.("No remote configured, skipping push and PR"),{...p,git:o};try{await Si("origin",u,{cwd:n}),r?.(`Pushed branch: ${u}`)}catch(g){let h=g instanceof Error?g.message:String(g);return o.warnings.push(`Push failed: ${h}`),r?.(`Warning: push failed \u2014 ${h}`),{...p,git:o}}if(d&&a)try{let g=ys(d),h=s.baseBranch??d.baseBranch??"main",y=s.prTitle??`feat: ${l.slice(0,72)}`,E=["## Summary",p.summary,"",`**Iterations:** ${p.iterations}`,`**Modified files:** ${p.allModifiedFiles.length}`,p.allModifiedFiles.map(v=>`- \`${v}\``).join(`
793
816
  `),"","_Automated by Alfred_"].join(`
794
- `),$=await f.createPullRequest(a,{title:y,body:E,head:u,base:h});o.pullRequest=$,r?.(`PR created: ${$.url}`)}catch(f){let h=f instanceof Error?f.message:String(f);o.warnings.push(`PR creation failed: ${h}`),r?.(`Warning: PR creation failed \u2014 ${h}`)}else d||(o.warnings.push("No forge configured \u2014 skipping PR creation"),r?.("No forge configured, skipping PR creation"));return{...p,git:o}}var fp,gp,yp,wp,Tp,_p,bp,Ri,Ci=_(()=>{"use strict";Nn();Si();xi();fp=3,gp=5,yp=3,wp=2048,Tp=1024,_p=`You are a task planner for a multi-agent coding system.
817
+ `),$=await g.createPullRequest(a,{title:y,body:E,head:u,base:h});o.pullRequest=$,r?.(`PR created: ${$.url}`)}catch(g){let h=g instanceof Error?g.message:String(g);o.warnings.push(`PR creation failed: ${h}`),r?.(`Warning: PR creation failed \u2014 ${h}`)}else d||(o.warnings.push("No forge configured \u2014 skipping PR creation"),r?.("No forge configured, skipping PR creation"));return{...p,git:o}}var Tp,_p,bp,Ep,$p,kp,vp,Di,Li=_(()=>{"use strict";On();Ii();Ci();Tp=3,_p=5,bp=3,Ep=2048,$p=1024,kp=`You are a task planner for a multi-agent coding system.
795
818
  You receive a high-level task and a list of available coding agents.
796
819
  Your job is to decompose the task into concrete subtasks, each assigned to an agent.
797
820
 
@@ -807,7 +830,7 @@ Respond with ONLY valid JSON (no markdown fences):
807
830
  "subtasks": [
808
831
  { "id": "task-1", "agent": "<agent-name>", "prompt": "<detailed prompt>", "description": "<short description>" }
809
832
  ]
810
- }`,bp=`You are a code review validator for a multi-agent coding system.
833
+ }`,vp=`You are a code review validator for a multi-agent coding system.
811
834
  You receive the original task and the results from each subtask execution.
812
835
  Your job is to determine if the task was completed successfully.
813
836
 
@@ -821,9 +844,9 @@ Respond with ONLY valid JSON (no markdown fences):
821
844
  "fixTasks": [
822
845
  { "id": "fix-1", "agent": "<agent-name>", "prompt": "<detailed fix prompt>", "description": "<short description>" }
823
846
  ]
824
- }`;m(Kl,"truncate");m(Xl,"parseJSON");Ri=class{static{m(this,"Semaphore")}max;queue=[];active=0;constructor(e){this.max=e}async acquire(){if(this.active<this.max){this.active++;return}return new Promise(e=>{this.queue.push(()=>{this.active++,e()})})}release(){this.active--;let e=this.queue.shift();e&&e()}};m(Ep,"planSubtasks");m($p,"executeSubtasksParallel");m(kp,"validateResults");m(Rt,"orchestrate");m(_r,"orchestrateWithGit")});var br,Yl=_(()=>{"use strict";U();Nn();Ci();br=class extends R{static{m(this,"CodeAgentSkill")}llm;metadata={name:"code_agent",category:"automation",description:'Run a CLI-based coding agent (e.g. Claude Code, Codex, Gemini CLI, Aider) as a subprocess. Use action "list_agents" to see available agents, "run" to execute one with a prompt, or "orchestrate" to have the LLM decompose a task into parallel subtasks. The agent runs in a specified working directory and returns stdout, stderr, exit code, duration, and a list of files it modified.',riskLevel:"admin",version:"1.0.0",timeoutMs:6e5,inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_agents","run","orchestrate"],description:"The action to perform"},task:{type:"string",description:'High-level task description for "orchestrate" action'},agents:{type:"array",items:{type:"string"},description:'Optional agent name filter for "orchestrate" (uses all agents if omitted)'},maxIterations:{type:"number",description:'Max validation iterations for "orchestrate" (1-5, default 3)'},agent:{type:"string",description:'Name of the agent to run (required for "run" action)'},prompt:{type:"string",description:'The prompt / task description to send to the agent (required for "run" action)'},cwd:{type:"string",description:"Working directory for the agent (optional, uses agent default or process.cwd())"},timeout:{type:"number",description:"Timeout in milliseconds (optional, max 900000)"},git:{type:"boolean",description:'Enable git workflow: auto-branch, commit, push, and PR creation (for "orchestrate")'},prTitle:{type:"string",description:"Custom PR/MR title (used with git=true)"},baseBranch:{type:"string",description:'Target branch for the PR/MR (default: "main")'}},required:["action"]}};agents;forgeConfig;constructor(e,t){super(),this.llm=t,this.agents=new Map(e.agents.map(s=>[s.name,s])),this.forgeConfig=e.forge}async execute(e,t){let s=e.action;switch(s){case"list_agents":return this.listAgents();case"run":return this.runAgent(e,t);case"orchestrate":return this.orchestrateTask(e,t);default:return{success:!1,error:`Unknown action "${s}". Use "list_agents", "run", or "orchestrate".`}}}listAgents(){let e=[...this.agents.values()].map(s=>({name:s.name,command:s.command,promptVia:s.promptVia??"arg",timeoutMs:s.timeoutMs})),t=e.length===0?"No code agents configured.":e.map(s=>`- **${s.name}**: \`${s.command}\` (prompt via ${s.promptVia})`).join(`
847
+ }`;m(Zl,"truncate");m(Ql,"parseJSON");Di=class{static{m(this,"Semaphore")}max;queue=[];active=0;constructor(e){this.max=e}async acquire(){if(this.active<this.max){this.active++;return}return new Promise(e=>{this.queue.push(()=>{this.active++,e()})})}release(){this.active--;let e=this.queue.shift();e&&e()}};m(Sp,"planSubtasks");m(Ap,"executeSubtasksParallel");m(Ip,"validateResults");m(Ct,"orchestrate");m(kr,"orchestrateWithGit")});var vr,ed=_(()=>{"use strict";U();On();Li();vr=class extends x{static{m(this,"CodeAgentSkill")}llm;metadata={name:"code_agent",category:"automation",description:'Run a CLI-based coding agent (e.g. Claude Code, Codex, Gemini CLI, Aider) as a subprocess. Use action "list_agents" to see available agents, "run" to execute one with a prompt, or "orchestrate" to have the LLM decompose a task into parallel subtasks. The agent runs in a specified working directory and returns stdout, stderr, exit code, duration, and a list of files it modified.',riskLevel:"admin",version:"1.0.0",timeoutMs:6e5,inputSchema:{type:"object",properties:{action:{type:"string",enum:["list_agents","run","orchestrate"],description:"The action to perform"},task:{type:"string",description:'High-level task description for "orchestrate" action'},agents:{type:"array",items:{type:"string"},description:'Optional agent name filter for "orchestrate" (uses all agents if omitted)'},maxIterations:{type:"number",description:'Max validation iterations for "orchestrate" (1-5, default 3)'},agent:{type:"string",description:'Name of the agent to run (required for "run" action)'},prompt:{type:"string",description:'The prompt / task description to send to the agent (required for "run" action)'},cwd:{type:"string",description:"Working directory for the agent (optional, uses agent default or process.cwd())"},timeout:{type:"number",description:"Timeout in milliseconds (optional, max 900000)"},git:{type:"boolean",description:'Enable git workflow: auto-branch, commit, push, and PR creation (for "orchestrate")'},prTitle:{type:"string",description:"Custom PR/MR title (used with git=true)"},baseBranch:{type:"string",description:'Target branch for the PR/MR (default: "main")'}},required:["action"]}};agents;forgeConfig;constructor(e,t){super(),this.llm=t,this.agents=new Map(e.agents.map(s=>[s.name,s])),this.forgeConfig=e.forge}async execute(e,t){let s=e.action;switch(s){case"list_agents":return this.listAgents();case"run":return this.runAgent(e,t);case"orchestrate":return this.orchestrateTask(e,t);default:return{success:!1,error:`Unknown action "${s}". Use "list_agents", "run", or "orchestrate".`}}}listAgents(){let e=[...this.agents.values()].map(s=>({name:s.name,command:s.command,promptVia:s.promptVia??"arg",timeoutMs:s.timeoutMs})),t=e.length===0?"No code agents configured.":e.map(s=>`- **${s.name}**: \`${s.command}\` (prompt via ${s.promptVia})`).join(`
825
848
  `);return{success:!0,data:{agents:e},display:`Available code agents:
826
- ${t}`}}async runAgent(e,t){let s=e.agent,r=e.prompt;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "agent"'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "prompt"'};let n=this.agents.get(s);if(!n){let d=[...this.agents.keys()].join(", ");return{success:!1,error:`Unknown agent "${s}". Available: ${d}`}}let o=typeof e.cwd=="string"?e.cwd:void 0,i=typeof e.timeout=="number"?e.timeout:void 0,a=await yr(n,r,{cwd:o,timeoutMs:i,onProgress:t.onProgress}),c=[];return a.stdout&&c.push(`**stdout:**
849
+ ${t}`}}async runAgent(e,t){let s=e.agent,r=e.prompt;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "agent"'};if(!r||typeof r!="string")return{success:!1,error:'Missing required field "prompt"'};let n=this.agents.get(s);if(!n){let d=[...this.agents.keys()].join(", ");return{success:!1,error:`Unknown agent "${s}". Available: ${d}`}}let o=typeof e.cwd=="string"?e.cwd:void 0,i=typeof e.timeout=="number"?e.timeout:void 0,a=await br(n,r,{cwd:o,timeoutMs:i,onProgress:t.onProgress}),c=[];return a.stdout&&c.push(`**stdout:**
827
850
  \`\`\`
828
851
  ${a.stdout}
829
852
  \`\`\``),a.stderr&&c.push(`**stderr:**
@@ -833,7 +856,7 @@ ${a.stderr}
833
856
  ${a.modifiedFiles.map(d=>`- ${d}`).join(`
834
857
  `)}`),{success:a.exitCode===0,data:{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode,durationMs:a.durationMs,modifiedFiles:a.modifiedFiles},display:c.join(`
835
858
 
836
- `),...a.exitCode!==0&&{error:a.exitCode===124?`Agent "${s}" timed out`:`Agent "${s}" exited with code ${a.exitCode}`}}}async orchestrateTask(e,t){if(!this.llm)return{success:!1,error:"Orchestration requires an LLM provider but none was configured."};let s=e.task;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "task" for orchestrate action.'};let r=[...this.agents.values()],n=e.agents;if(Array.isArray(n)&&n.length>0&&(r=n.map(d=>this.agents.get(d)).filter(d=>d!==void 0),r.length===0))return{success:!1,error:`None of the specified agents exist. Available: ${[...this.agents.keys()].join(", ")}`};let o=typeof e.maxIterations=="number"?e.maxIterations:void 0,i=e.git===!0,a=typeof e.prTitle=="string"?e.prTitle:void 0,c=typeof e.baseBranch=="string"?e.baseBranch:void 0;try{if(i){let u=await _r(s,r,this.llm,{maxIterations:o,onProgress:t.onProgress,forge:this.forgeConfig,prTitle:a,baseBranch:c});return this.formatGitOrchestrationResult(u)}let d=await Rt(s,r,this.llm,{maxIterations:o,onProgress:t.onProgress});return this.formatOrchestrationResult(d)}catch(d){return{success:!1,error:`Orchestration failed: ${d instanceof Error?d.message:String(d)}`}}}formatOrchestrationResult(e){let t=[];t.push(`**Orchestration completed in ${e.iterations} iteration(s)**`),t.push(`**Plan:** ${e.plan.reasoning}`),t.push(`**Duration:** ${(e.totalDurationMs/1e3).toFixed(1)}s`);for(let r of e.subtaskResults){let n=r.execution.exitCode===0?"OK":`FAIL (exit ${r.execution.exitCode})`;t.push(`- **${r.subtask.id}** [${r.subtask.agent}]: ${r.subtask.description} \u2014 ${n}`)}return e.allModifiedFiles.length>0&&t.push(`
859
+ `),...a.exitCode!==0&&{error:a.exitCode===124?`Agent "${s}" timed out`:`Agent "${s}" exited with code ${a.exitCode}`}}}async orchestrateTask(e,t){if(!this.llm)return{success:!1,error:"Orchestration requires an LLM provider but none was configured."};let s=e.task;if(!s||typeof s!="string")return{success:!1,error:'Missing required field "task" for orchestrate action.'};let r=[...this.agents.values()],n=e.agents;if(Array.isArray(n)&&n.length>0&&(r=n.map(d=>this.agents.get(d)).filter(d=>d!==void 0),r.length===0))return{success:!1,error:`None of the specified agents exist. Available: ${[...this.agents.keys()].join(", ")}`};let o=typeof e.maxIterations=="number"?e.maxIterations:void 0,i=e.git===!0,a=typeof e.prTitle=="string"?e.prTitle:void 0,c=typeof e.baseBranch=="string"?e.baseBranch:void 0;try{if(i){let u=await kr(s,r,this.llm,{maxIterations:o,onProgress:t.onProgress,forge:this.forgeConfig,prTitle:a,baseBranch:c});return this.formatGitOrchestrationResult(u)}let d=await Ct(s,r,this.llm,{maxIterations:o,onProgress:t.onProgress});return this.formatOrchestrationResult(d)}catch(d){return{success:!1,error:`Orchestration failed: ${d instanceof Error?d.message:String(d)}`}}}formatOrchestrationResult(e){let t=[];t.push(`**Orchestration completed in ${e.iterations} iteration(s)**`),t.push(`**Plan:** ${e.plan.reasoning}`),t.push(`**Duration:** ${(e.totalDurationMs/1e3).toFixed(1)}s`);for(let r of e.subtaskResults){let n=r.execution.exitCode===0?"OK":`FAIL (exit ${r.execution.exitCode})`;t.push(`- **${r.subtask.id}** [${r.subtask.agent}]: ${r.subtask.description} \u2014 ${n}`)}return e.allModifiedFiles.length>0&&t.push(`
837
860
  **Modified files:**
838
861
  ${e.allModifiedFiles.map(r=>`- ${r}`).join(`
839
862
  `)}`),e.summary&&t.push(`
@@ -842,15 +865,15 @@ ${e.allModifiedFiles.map(r=>`- ${r}`).join(`
842
865
 
843
866
  **Git:**
844
867
  ${s.join(`
845
- `)}`:"";return{...t,data:{...t.data,git:{branch:r.branch,commit:r.commit,pullRequest:r.pullRequest,warnings:r.warnings}},display:(t.display??"")+n}}}});var Jl=_(()=>{"use strict";Yl();Nn();Ci();Si();xi()});var te={};ue(te,{ActivityTracker:()=>Et,BMWSkill:()=>Sn,BackgroundTaskSkill:()=>ss,BriefingSkill:()=>Dn,BrowserSkill:()=>Qt,CalculatorSkill:()=>Ft,CalendarProvider:()=>Oe,CalendarSkill:()=>$t,ClipboardSkill:()=>Jt,CodeAgentSkill:()=>br,CodeExecutionSkill:()=>dr,CodeExecutor:()=>St,ConfigureSkill:()=>us,ContactsProvider:()=>Fe,ContactsSkill:()=>pr,CrossPlatformSkill:()=>ts,DelegateSkill:()=>Vt,DockerSkill:()=>$n,DocumentSkill:()=>ns,EmailProvider:()=>Xe,EmailSkill:()=>tt,EnergyPriceSkill:()=>In,FileSkill:()=>Xt,ForgeClient:()=>xt,HomeAssistantSkill:()=>wn,HttpSkill:()=>Kt,ImageGenerateSkill:()=>is,MCPClient:()=>kt,MCPManager:()=>lr,MCPSkillAdapter:()=>vt,MarketplaceSkill:()=>gr,MemorySkill:()=>Gt,MicrosoftTodoSkill:()=>Rn,MonitorSkill:()=>xn,NoteSkill:()=>qt,PluginLoader:()=>cn,ProfileSkill:()=>es,ProxmoxSkill:()=>gn,ReminderSkill:()=>Ht,RoutingSkill:()=>An,ScheduledTaskSkill:()=>rs,ScreenshotSkill:()=>Zt,ShellSkill:()=>Wt,Skill:()=>R,SkillRegistry:()=>Pt,SkillSandbox:()=>Ut,SystemInfoSkill:()=>jt,TTSSkill:()=>os,TodoSkill:()=>as,TransitSkill:()=>ls,UniFiSkill:()=>yn,WatchSkill:()=>ms,WeatherSkill:()=>zt,WebSearchSkill:()=>Bt,allUserIds:()=>W,createCalendarProvider:()=>cr,createContactsProvider:()=>di,createEmailProvider:()=>or,createForgeClient:()=>fs,effectiveUserId:()=>ne,gitAddRemote:()=>Tr,gitGetRemoteUrl:()=>ps,gitInitRepo:()=>wr,orchestrate:()=>Rt,orchestrateWithGit:()=>_r,parseRemoteUrl:()=>hs});var Q=_(()=>{"use strict";U();Me();uc();mc();Xo();pc();hc();fc();gc();yc();wc();Tc();Ec();$c();kc();Ic();Rc();Dc();Lc();Mc();Pc();Uc();zc();Wc();Gc();Vc();Xc();Jc();Zc();Qc();el();tl();sl();rl();ll();dl();fl();El();$l();Rl();Cl();Nl();Ml();Ol();Ul();Hl();ql();Jl()});var Er,Di=_(()=>{"use strict";Er=class{static{m(this,"ConversationManager")}conversations;constructor(e){this.conversations=e}getOrCreateConversation(e,t,s){let r=this.conversations.findByPlatformChat(e,t);return r?(this.conversations.updateTimestamp(r.id),r):this.conversations.create(e,t,s)}addMessage(e,t,s,r){return this.conversations.addMessage(e,t,s,r)}getHistory(e,t=20){return this.conversations.getMessages(e,t)}pruneMessages(e,t){return this.conversations.pruneMessages(e,t)}}});function Ye(l,e){let t;if(e.platformUserId)t=l.findOrCreate(e.platform,e.platformUserId,e.userName,e.displayName);else if(e.userId)t=l.findById(e.userId)??l.findOrCreate(e.platform,e.userId);else throw new Error("ContextSource must provide either platformUserId or userId");let s="getMasterUserId"in l?l.getMasterUserId(t.id):t.id,r=[];"getLinkedUsers"in l&&(r=l.getLinkedUsers(s).map(a=>a.platformUserId));let n;try{"getProfile"in l?n=l.getProfile(s)?.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone:n=Intl.DateTimeFormat().resolvedOptions().timeZone}catch{n=Intl.DateTimeFormat().resolvedOptions().timeZone}return{context:{userId:t.platformUserId,masterUserId:s,linkedPlatformUserIds:r,chatId:e.chatId,chatType:e.chatType,platform:e.platform,conversationId:e.conversationId??"",timezone:n},user:t,masterUserId:s,linkedPlatformUserIds:r}}var gs=_(()=>{"use strict";m(Ye,"buildSkillContext")});function Zl(l,e){let t=new Set(["core"]),s=!1;for(let[r,n]of Object.entries(vp))e.has(r)&&n.test(l)&&(t.add(r),s=!0);if(!s){let r=["productivity","information","media","automation"];for(let n of r)e.has(n)&&t.add(n)}return t}function Ql(l,e){return l.filter(t=>e.has(t.category??"core"))}var vp,ed=_(()=>{"use strict";vp={productivity:/\b(todo|note|remind|calendar|termin|event|email|e-mail|mail|contact|kontakt|briefing|morgenbriefing|tagesbriefing)\b/i,information:/\b(search|such|weather|wetter|calculat|rechn|time|date|zeit|datum|uhrzeit|system.?info|transit|bahn|zug|bus|tram|u.?bahn|s.?bahn|abfahrt|verbindung|haltestelle|öffi|fahrplan|strom|energy|preis|price|kwh|awattar|marktpreis|spot|günstig|cheapest|netzentgelt)\b/i,media:/\b(voice|stimme|tts|speak|sprech|sprich|screenshot|clipboard|zwischenablage|brows)\b/i,automation:/\b(background|hintergrund|shell|bash|cron|schedul|code.?agent|sandbox|automat|watch|alert|benachrichtig|bescheid|meld|überwach|monitor|tägliche?r?s?|stündliche?r?s?|wöchentliche?r?s?|monatliche?r?s?|jeden\s+(tag|morgen|abend|montag|dienstag|mittwoch|donnerstag|freitag|samstag|sonntag)|um\s+\d{1,2}\s*(uhr|:|h)|alle\s+\d+\s*(min|stund|sekund)|in\s+\d+\s*(minuten?|stunden?|sekunden?|hours?|minutes?|seconds?|min)|daily|hourly|weekly|every\s+(day|hour|morning|evening|night|\d+\s*min)|führ.{0,5}aus|execut|ausführ)\b/i,files:/\b(file|datei|document|dokument|pdf|http|download|upload)\b/i,infrastructure:/\b(proxmox|vm|container|docker|unifi|wifi|wlan|homeassistant|home.?assistant|smarthome|smart.?home|licht|light|schalter|switch|solar|photovoltaik|pv|wechselrichter|inverter|batterie.?speicher|wallbox|energieverbrauch|stromverbrauch|verbrauch.{0,5}kwh|einspeis|netzeinspeis|autarkie|eigenverbrauch|bmw|auto|fahrzeug|ladestand|reichweite|laden(?!e)|charging|vehicle|soc)\b/i,identity:/\b(link|verknüpf|cross.?platform|identity|identität)\b/i,mcp:/\bmcp\b/i};m(Zl,"selectCategories");m(Ql,"filterSkills")});import Li from"node:fs";import td from"node:path";var Sp,sd,Ap,rd,Ip,xp,Rp,nd,$r,Ni=_(()=>{"use strict";Vo();gs();ed();Sp=15*60*1e3,sd=50,Ap=2,rd=.85,Ip=1e5,xp=2e3,Rp=.1,nd=3,$r=class{static{m(this,"MessagePipeline")}promptBuilder;llm;conversationManager;users;logger;skillRegistry;skillSandbox;securityManager;memoryRepo;speechTranscriber;inboxPath;embeddingService;activeLearning;memoryRetriever;maxHistoryMessages;documentProcessor;activeAgents=new Map;agentIdCounter=0;constructor(e){this.llm=e.llm,this.conversationManager=e.conversationManager,this.users=e.users,this.logger=e.logger,this.skillRegistry=e.skillRegistry,this.skillSandbox=e.skillSandbox,this.securityManager=e.securityManager,this.memoryRepo=e.memoryRepo,this.speechTranscriber=e.speechTranscriber,this.inboxPath=e.inboxPath,this.embeddingService=e.embeddingService,this.activeLearning=e.activeLearning,this.memoryRetriever=e.memoryRetriever,this.maxHistoryMessages=e.maxHistoryMessages??30,this.documentProcessor=e.documentProcessor,this.promptBuilder=new tr}async process(e,t){let s=Date.now();this.logger.info({platform:e.platform,userId:e.userId,chatId:e.chatId},"Processing message");try{let{user:r,masterUserId:n,linkedPlatformUserIds:o,context:i}=Ye(this.users,{platformUserId:e.userId,platform:e.platform,chatId:e.chatId,chatType:e.chatType,userName:e.userName,displayName:e.displayName}),a=this.conversationManager.getOrCreateConversation(e.platform,e.chatId,r.id),c=e.metadata?.skipHistory?[]:this.conversationManager.getHistory(a.id,this.maxHistoryMessages);this.conversationManager.addMessage(a.id,"user",e.text);let d,u=this.isSyntheticLabel(e.text),p=e.attachments?.some(V=>V.type==="audio")??!1,g=u&&!p;if(this.memoryRetriever&&e.text&&!g)try{d=await this.memoryRetriever.retrieve(n,e.text,15,o)}catch(V){this.logger.debug({err:V},"Hybrid memory retrieval failed")}if(!d&&this.memoryRepo&&!g)try{let V=[n,...(o??[]).filter(X=>X!==n)];if(this.embeddingService&&e.text&&this.llm.supportsEmbeddings()){let X=new Set;d=[];for(let de of V)for(let ae of await this.embeddingService.semanticSearch(de,e.text,10))X.has(ae.key)||(X.add(ae.key),d.push(ae));for(let de of V)for(let ae of this.memoryRepo.getRecentForPrompt(de,5))X.has(ae.key)||(X.add(ae.key),d.push(ae))}else{let X=new Set;d=[];for(let de of V)for(let ae of this.memoryRepo.getRecentForPrompt(de,20))X.has(ae.key)||(X.add(ae.key),d.push(ae))}}catch(V){this.logger.debug({err:V},"Memory loading failed")}d&&d.length>0&&(d=this.applyMemoryBudget(d));let f;try{"getProfile"in this.users&&(f=this.users.getProfile(n),f&&!f.displayName&&(f.displayName=r.displayName??r.username))}catch(V){this.logger.debug({err:V},"Profile loading failed")}let h=i.timezone??Intl.DateTimeFormat().resolvedOptions().timeZone,y=this.skillRegistry?this.skillRegistry.getAll().map(V=>V.metadata):void 0,E=y;if(y&&e.text){let V=new Set(y.map(de=>de.category??"core")),X=Zl(e.text,V);E=Ql(y,X)}let $=E?this.promptBuilder.buildTools(E):void 0,v=this.promptBuilder.buildSystemPrompt({memories:d,skills:E,userProfile:f}),P=this.buildActiveAgentStatus();P&&(v+=`
868
+ `)}`:"";return{...t,data:{...t.data,git:{branch:r.branch,commit:r.commit,pullRequest:r.pullRequest,warnings:r.warnings}},display:(t.display??"")+n}}}});var td=_(()=>{"use strict";ed();On();Li();Ii();Ci()});var te={};le(te,{ActivityTracker:()=>$t,BMWSkill:()=>In,BackgroundTaskSkill:()=>ns,BriefingSkill:()=>Nn,BrowserSkill:()=>ts,CalculatorSkill:()=>Bt,CalendarProvider:()=>Ue,CalendarSkill:()=>kt,ClipboardSkill:()=>Qt,CodeAgentSkill:()=>vr,CodeExecutionSkill:()=>hr,CodeExecutor:()=>At,ConfigureSkill:()=>ps,ContactsProvider:()=>Be,ContactsSkill:()=>yr,CrossPlatformSkill:()=>rs,DelegateSkill:()=>Xt,DockerSkill:()=>vn,DocumentSkill:()=>is,EmailProvider:()=>Ye,EmailSkill:()=>rt,EnergyPriceSkill:()=>Rn,FileSkill:()=>Jt,ForgeClient:()=>Rt,HomeAssistantSkill:()=>_n,HttpSkill:()=>Yt,ImageGenerateSkill:()=>cs,MCPClient:()=>vt,MCPManager:()=>pr,MCPSkillAdapter:()=>St,MarketplaceSkill:()=>_r,MemorySkill:()=>Kt,MicrosoftTodoSkill:()=>Dn,MonitorSkill:()=>Cn,NoteSkill:()=>Wt,PluginLoader:()=>dn,ProfileSkill:()=>ss,ProxmoxSkill:()=>wn,ReminderSkill:()=>qt,RoutingSkill:()=>xn,ScheduledTaskSkill:()=>os,ScreenshotSkill:()=>es,ShellSkill:()=>Vt,Skill:()=>x,SkillRegistry:()=>Ft,SkillSandbox:()=>jt,SystemInfoSkill:()=>Ht,TTSSkill:()=>as,TodoSkill:()=>ls,TransitSkill:()=>us,UniFiSkill:()=>Tn,WatchSkill:()=>hs,WeatherSkill:()=>Gt,WebSearchSkill:()=>zt,allUserIds:()=>G,createCalendarProvider:()=>mr,createContactsProvider:()=>mi,createEmailProvider:()=>lr,createForgeClient:()=>ys,effectiveUserId:()=>ne,gitAddRemote:()=>$r,gitGetRemoteUrl:()=>fs,gitInitRepo:()=>Er,orchestrate:()=>Ct,orchestrateWithGit:()=>kr,parseRemoteUrl:()=>gs});var ee=_(()=>{"use strict";U();Pe();fc();gc();Jo();yc();wc();Tc();_c();bc();Ec();$c();Sc();Ac();Ic();Dc();Nc();Oc();Pc();Fc();Bc();Hc();Kc();Xc();Yc();Jc();Qc();tl();sl();rl();nl();ol();il();al();pl();hl();Tl();Sl();Al();Nl();Ml();Ul();Fl();jl();Hl();Gl();Vl();td()});var Sr,Ni=_(()=>{"use strict";Sr=class{static{m(this,"ConversationManager")}conversations;constructor(e){this.conversations=e}getOrCreateConversation(e,t,s){let r=this.conversations.findByPlatformChat(e,t);return r?(this.conversations.updateTimestamp(r.id),r):this.conversations.create(e,t,s)}addMessage(e,t,s,r){return this.conversations.addMessage(e,t,s,r)}getHistory(e,t=20){return this.conversations.getMessages(e,t)}pruneMessages(e,t){return this.conversations.pruneMessages(e,t)}}});function Je(l,e){let t;if(e.platformUserId)t=l.findOrCreate(e.platform,e.platformUserId,e.userName,e.displayName);else if(e.userId)t=l.findById(e.userId)??l.findOrCreate(e.platform,e.userId);else throw new Error("ContextSource must provide either platformUserId or userId");let s="getMasterUserId"in l?l.getMasterUserId(t.id):t.id,r=[];"getLinkedUsers"in l&&(r=l.getLinkedUsers(s).map(a=>a.platformUserId));let n;try{"getProfile"in l?n=l.getProfile(s)?.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone:n=Intl.DateTimeFormat().resolvedOptions().timeZone}catch{n=Intl.DateTimeFormat().resolvedOptions().timeZone}return{context:{userId:t.platformUserId,masterUserId:s,linkedPlatformUserIds:r,chatId:e.chatId,chatType:e.chatType,platform:e.platform,conversationId:e.conversationId??"",timezone:n},user:t,masterUserId:s,linkedPlatformUserIds:r}}var ws=_(()=>{"use strict";m(Je,"buildSkillContext")});function sd(l,e){let t=new Set(["core"]),s=!1;for(let[r,n]of Object.entries(xp))e.has(r)&&n.test(l)&&(t.add(r),s=!0);if(!s){let r=["productivity","information","media","automation"];for(let n of r)e.has(n)&&t.add(n)}return t}function rd(l,e){return l.filter(t=>e.has(t.category??"core"))}var xp,nd=_(()=>{"use strict";xp={productivity:/\b(todo|note|remind|calendar|termin|event|email|e-mail|mail|contact|kontakt|briefing|morgenbriefing|tagesbriefing)\b/i,information:/\b(search|such|weather|wetter|calculat|rechn|time|date|zeit|datum|uhrzeit|system.?info|transit|bahn|zug|bus|tram|u.?bahn|s.?bahn|abfahrt|verbindung|haltestelle|öffi|fahrplan|strom|energy|preis|price|kwh|awattar|marktpreis|spot|günstig|cheapest|netzentgelt)\b/i,media:/\b(voice|stimme|tts|speak|sprech|sprich|screenshot|clipboard|zwischenablage|brows)\b/i,automation:/\b(background|hintergrund|shell|bash|cron|schedul|code.?agent|sandbox|automat|watch|alert|benachrichtig|bescheid|meld|überwach|monitor|tägliche?r?s?|stündliche?r?s?|wöchentliche?r?s?|monatliche?r?s?|jeden\s+(tag|morgen|abend|montag|dienstag|mittwoch|donnerstag|freitag|samstag|sonntag)|um\s+\d{1,2}\s*(uhr|:|h)|alle\s+\d+\s*(min|stund|sekund)|in\s+\d+\s*(minuten?|stunden?|sekunden?|hours?|minutes?|seconds?|min)|daily|hourly|weekly|every\s+(day|hour|morning|evening|night|\d+\s*min)|führ.{0,5}aus|execut|ausführ)\b/i,files:/\b(file|datei|document|dokument|pdf|http|download|upload)\b/i,infrastructure:/\b(proxmox|vm|container|docker|unifi|wifi|wlan|homeassistant|home.?assistant|smarthome|smart.?home|licht|light|schalter|switch|solar|photovoltaik|pv|wechselrichter|inverter|batterie.?speicher|wallbox|energieverbrauch|stromverbrauch|verbrauch.{0,5}kwh|einspeis|netzeinspeis|autarkie|eigenverbrauch|bmw|auto|fahrzeug|ladestand|reichweite|laden(?!e)|charging|vehicle|soc)\b/i,identity:/\b(link|verknüpf|cross.?platform|identity|identität)\b/i,mcp:/\bmcp\b/i};m(sd,"selectCategories");m(rd,"filterSkills")});import Mi from"node:fs";import od from"node:path";var Rp,id,Cp,ad,Dp,Lp,Np,cd,Mp,Ar,Oi=_(()=>{"use strict";Xo();ws();nd();Rp=15*60*1e3,id=50,Cp=2,ad=.85,Dp=1e5,Lp=2e3,Np=.1,cd=3,Mp=6,Ar=class{static{m(this,"MessagePipeline")}promptBuilder;llm;conversationManager;users;logger;skillRegistry;skillSandbox;securityManager;memoryRepo;speechTranscriber;inboxPath;embeddingService;activeLearning;memoryRetriever;maxHistoryMessages;documentProcessor;conversationSummarizer;activeAgents=new Map;agentIdCounter=0;constructor(e){this.llm=e.llm,this.conversationManager=e.conversationManager,this.users=e.users,this.logger=e.logger,this.skillRegistry=e.skillRegistry,this.skillSandbox=e.skillSandbox,this.securityManager=e.securityManager,this.memoryRepo=e.memoryRepo,this.speechTranscriber=e.speechTranscriber,this.inboxPath=e.inboxPath,this.embeddingService=e.embeddingService,this.activeLearning=e.activeLearning,this.memoryRetriever=e.memoryRetriever,this.maxHistoryMessages=e.maxHistoryMessages??30,this.documentProcessor=e.documentProcessor,this.conversationSummarizer=e.conversationSummarizer,this.promptBuilder=new or}async process(e,t){let s=Date.now();this.logger.info({platform:e.platform,userId:e.userId,chatId:e.chatId},"Processing message");try{let{user:r,masterUserId:n,linkedPlatformUserIds:o,context:i}=Je(this.users,{platformUserId:e.userId,platform:e.platform,chatId:e.chatId,chatType:e.chatType,userName:e.userName,displayName:e.displayName}),a=this.conversationManager.getOrCreateConversation(e.platform,e.chatId,r.id),c=!e.metadata?.skipHistory&&this.conversationSummarizer?this.conversationSummarizer.getSummary(a.id):void 0,d=c?Mp:this.maxHistoryMessages,u=e.metadata?.skipHistory?[]:this.conversationManager.getHistory(a.id,d);this.conversationManager.addMessage(a.id,"user",e.text);let p,f=this.isSyntheticLabel(e.text),g=e.attachments?.some(K=>K.type==="audio")??!1,h=f&&!g;if(this.memoryRetriever&&e.text&&!h)try{p=await this.memoryRetriever.retrieve(n,e.text,15,o)}catch(K){this.logger.debug({err:K},"Hybrid memory retrieval failed")}if(!p&&this.memoryRepo&&!h)try{let K=[n,...(o??[]).filter(J=>J!==n)];if(this.embeddingService&&e.text&&this.llm.supportsEmbeddings()){let J=new Set;p=[];for(let ie of K)for(let ae of await this.embeddingService.semanticSearch(ie,e.text,10))J.has(ae.key)||(J.add(ae.key),p.push(ae));for(let ie of K)for(let ae of this.memoryRepo.getRecentForPrompt(ie,5))J.has(ae.key)||(J.add(ae.key),p.push(ae))}else{let J=new Set;p=[];for(let ie of K)for(let ae of this.memoryRepo.getRecentForPrompt(ie,20))J.has(ae.key)||(J.add(ae.key),p.push(ae))}}catch(K){this.logger.debug({err:K},"Memory loading failed")}p&&p.length>0&&(p=this.applyMemoryBudget(p));let y;try{"getProfile"in this.users&&(y=this.users.getProfile(n),y&&!y.displayName&&(y.displayName=r.displayName??r.username))}catch(K){this.logger.debug({err:K},"Profile loading failed")}let E=i.timezone??Intl.DateTimeFormat().resolvedOptions().timeZone,$=this.skillRegistry?this.skillRegistry.getAll().map(K=>K.metadata):void 0,v=$;if($&&e.text){let K=new Set($.map(ie=>ie.category??"core")),J=sd(e.text,K);v=rd($,J)}let j=v?this.promptBuilder.buildTools(v):void 0,N=this.promptBuilder.buildSystemPrompt({memories:p,skills:v,userProfile:y,conversationSummary:c?.summary}),H=this.buildActiveAgentStatus();H&&(N+=`
846
869
 
847
- `+P);let x=this.promptBuilder.buildMessages(c),q=this.collapseRepeatedToolErrors(x),J=Go(q),oe=q.reduce((V,X)=>V+_t(X),0)-J.reduce((V,X)=>V+_t(X),0);oe>100&&this.logger.debug(`Tool result trimming saved ~${oe} tokens`);let O=await this.buildUserContent(e,t);J.push({role:"user",content:O});let se=$?Ne(JSON.stringify($)):0,$e=1,re=this.trimToContextWindow(v,J,se,$e),j,ee=0,we=Date.now(),D="",B=0,_e=0,ie=0,Be=[];for(t?.("Thinking...");;){ee>0&&this.compressToolLoop(re,v,se);try{j=await this.llm.complete({messages:re,system:v,tools:$&&$.length>0?$:void 0,tier:e.metadata?.tier}),_e+=j.usage?.inputTokens??0,ie+=j.usage?.outputTokens??0}catch(ve){if((ve instanceof Error?ve.message:String(ve)).includes("prompt is too long")&&$e>.3){$e*=.5,this.logger.warn({budgetMultiplier:$e},"Prompt too long, retrimming with reduced budget"),re=this.trimToContextWindow(v,J,se,$e);continue}throw ve}if(!j.toolCalls||j.toolCalls.length===0)break;let V=Date.now()-we;if(V>=Sp){let ve=Math.round(V/6e4);this.logger.warn({iteration:ee,elapsedMin:ve,pendingToolCalls:j.toolCalls.length},"Tool loop timeout reached"),j=await this.abortToolLoop(re,j,a.id,v,`Das Zeitlimit von ${ve} Minuten f\xFCr Tool-Aufrufe wurde erreicht.`,!1,e.metadata?.tier);break}if(ee>=sd){this.logger.warn({iteration:ee,pendingToolCalls:j.toolCalls.length},"Tool loop iteration cap reached"),j=await this.abortToolLoop(re,j,a.id,v,`Das Iterationslimit von ${sd} Tool-Aufrufen wurde erreicht.`,!1,e.metadata?.tier);break}ee++,this.logger.info({iteration:ee,toolCalls:j.toolCalls.length},"Processing tool calls");let X=[];j.content&&X.push({type:"text",text:j.content});for(let ve of j.toolCalls)X.push({type:"tool_use",id:ve.id,name:ve.name,input:ve.input});re.push({role:"assistant",content:X});let de=await this.executeToolCallsParallel(j.toolCalls,{...i,conversationId:a.id,timezone:h},t),ae=de.blocks;de.attachments.length>0&&Be.push(...de.attachments),this.conversationManager.addMessage(a.id,"assistant",j.content??"",JSON.stringify(j.toolCalls)),this.conversationManager.addMessage(a.id,"user","",JSON.stringify(ae));let it=this.buildErrorSignature(ae);if(it){if(it===D?B++:(B=1,D=it),B>=Ap){this.logger.warn({iteration:ee,consecutiveErrors:B,errorSignature:it},"Tool loop aborted: same error repeated consecutively"),j=await this.abortToolLoop(re,j,a.id,v,`Der gleiche Tool-Fehler ist ${B}x hintereinander aufgetreten: "${D.slice(0,200)}". Erkl\xE4re dem User kurz was nicht funktioniert hat und schlage eine Alternative vor.`,!0,e.metadata?.tier);break}}else B=0,D="";re.push({role:"user",content:ae}),t?.("Thinking...")}let le=j.content;if(!le)for(let V=re.length-1;V>=0;V--){let X=re[V];if(X.role==="assistant"&&Array.isArray(X.content)){let de=X.content.find(ae=>ae.type==="text");if(de&&"text"in de&&de.text){le=de.text;break}}}le||(le="(no response)"),this.conversationManager.addMessage(a.id,"assistant",le),this.activeLearning&&this.activeLearning.onMessageProcessed(n,e.text,le);let jr=Date.now()-s;return this.logger.info({duration:jr,tokens:j.usage,totalTokens:{inputTokens:_e,outputTokens:ie},stopReason:j.stopReason,toolIterations:ee},"Message processed"),{text:le,attachments:Be.length>0?Be:void 0}}catch(r){throw this.logger.error({err:r},"Failed to process message"),r}}async abortToolLoop(e,t,s,r,n,o=!1,i){if(!o){let d=[];t.content&&d.push({type:"text",text:t.content});for(let u of t.toolCalls)d.push({type:"tool_use",id:u.id,name:u.name,input:u.input});e.push({role:"assistant",content:d})}let a=t.toolCalls.map(d=>({type:"tool_result",tool_use_id:d.id,content:`Error: tool loop aborted \u2014 ${n}`,is_error:!0}));e.push({role:"user",content:a}),o||this.conversationManager.addMessage(s,"assistant",t.content??"",JSON.stringify(t.toolCalls)),this.conversationManager.addMessage(s,"user","",JSON.stringify(a));let c=e[e.length-1];return c&&c.role==="user"&&Array.isArray(c.content)?c.content.push({type:"text",text:`[System: ${n} Fasse dem User kurz zusammen was du bisher geschafft hast und was noch offen ist.]`}):e.push({role:"user",content:`[System: ${n} Fasse dem User kurz zusammen was du bisher geschafft hast und was noch offen ist.]`}),await this.llm.complete({messages:e,system:r,tier:i})}buildErrorSignature(e){let t=[];for(let s of e)s.type==="tool_result"&&s.is_error&&t.push(s.content);return t.length>0?t.join("|"):""}collapseRepeatedToolErrors(e){let t=[],s=0;for(;s<e.length;){let r=e[s];if(r.role==="assistant"&&Array.isArray(r.content)&&r.content.some(n=>n.type==="tool_use")){let n=s+1<e.length?e[s+1]:null;if(n&&n.role==="user"&&Array.isArray(n.content)&&n.content.every(o=>o.type==="tool_result"&&o.is_error)){let o=this.toolPairSignature(r,n),i=1,a=s+2;for(;a+1<e.length;){let c=e[a],d=e[a+1];if(c.role==="assistant"&&d?.role==="user"&&this.toolPairSignature(c,d)===o)i++,a+=2;else break}if(i>1){t.push(r),t.push(n),t.push({role:"user",content:`[System: The above tool error repeated ${i} times with identical input. The loop was aborted.]`}),s=a;continue}}}t.push(r),s++}return t}toolPairSignature(e,t){let s=Array.isArray(e.content)?e.content.filter(n=>n.type==="tool_use").map(n=>`${n.name}:${JSON.stringify(n.input)}`).join(","):"",r=Array.isArray(t.content)?t.content.filter(n=>n.type==="tool_result").map(n=>n.content).join(","):"";return`${s}|${r}`}async executeToolCallsParallel(e,t,s){let r=[],n=m((u,p)=>{let g=p.content;if(p.attachments&&p.attachments.length>0){r.push(...p.attachments);let f=p.attachments.map(h=>h.fileName).join(", ");g+=`
870
+ `+H);let D=this.promptBuilder.buildMessages(u),oe=this.collapseRepeatedToolErrors(D),M=Ko(oe),re=oe.reduce((K,J)=>K+bt(J),0)-M.reduce((K,J)=>K+bt(J),0);re>100&&this.logger.debug(`Tool result trimming saved ~${re} tokens`);let ze=await this.buildUserContent(e,t);M.push({role:"user",content:ze});let $e=j?Oe(JSON.stringify(j)):0,se=1,X=this.trimToContextWindow(N,M,$e,se),z,me=0,xe=Date.now(),C="",B=0,pe=0,we=0,Nt=[];for(t?.("Thinking...");;){me>0&&this.compressToolLoop(X,N,$e);try{z=await this.llm.complete({messages:X,system:N,tools:j&&j.length>0?j:void 0,tier:e.metadata?.tier}),pe+=z.usage?.inputTokens??0,we+=z.usage?.outputTokens??0}catch(Te){if((Te instanceof Error?Te.message:String(Te)).includes("prompt is too long")&&se>.3){se*=.5,this.logger.warn({budgetMultiplier:se},"Prompt too long, retrimming with reduced budget"),X=this.trimToContextWindow(N,M,$e,se);continue}throw Te}if(!z.toolCalls||z.toolCalls.length===0)break;let K=Date.now()-xe;if(K>=Rp){let Te=Math.round(K/6e4);this.logger.warn({iteration:me,elapsedMin:Te,pendingToolCalls:z.toolCalls.length},"Tool loop timeout reached"),z=await this.abortToolLoop(X,z,a.id,N,`Das Zeitlimit von ${Te} Minuten f\xFCr Tool-Aufrufe wurde erreicht.`,!1,e.metadata?.tier);break}if(me>=id){this.logger.warn({iteration:me,pendingToolCalls:z.toolCalls.length},"Tool loop iteration cap reached"),z=await this.abortToolLoop(X,z,a.id,N,`Das Iterationslimit von ${id} Tool-Aufrufen wurde erreicht.`,!1,e.metadata?.tier);break}me++,this.logger.info({iteration:me,toolCalls:z.toolCalls.length},"Processing tool calls");let J=[];z.content&&J.push({type:"text",text:z.content});for(let Te of z.toolCalls)J.push({type:"tool_use",id:Te.id,name:Te.name,input:Te.input});X.push({role:"assistant",content:J});let ie=await this.executeToolCallsParallel(z.toolCalls,{...i,conversationId:a.id,timezone:E},t),ae=ie.blocks;ie.attachments.length>0&&Nt.push(...ie.attachments),this.conversationManager.addMessage(a.id,"assistant",z.content??"",JSON.stringify(z.toolCalls)),this.conversationManager.addMessage(a.id,"user","",JSON.stringify(ae));let et=this.buildErrorSignature(ae);if(et){if(et===C?B++:(B=1,C=et),B>=Cp){this.logger.warn({iteration:me,consecutiveErrors:B,errorSignature:et},"Tool loop aborted: same error repeated consecutively"),z=await this.abortToolLoop(X,z,a.id,N,`Der gleiche Tool-Fehler ist ${B}x hintereinander aufgetreten: "${C.slice(0,200)}". Erkl\xE4re dem User kurz was nicht funktioniert hat und schlage eine Alternative vor.`,!0,e.metadata?.tier);break}}else B=0,C="";X.push({role:"user",content:ae}),t?.("Thinking...")}let Re=z.content;if(!Re)for(let K=X.length-1;K>=0;K--){let J=X[K];if(J.role==="assistant"&&Array.isArray(J.content)){let ie=J.content.find(ae=>ae.type==="text");if(ie&&"text"in ie&&ie.text){Re=ie.text;break}}}if(Re||(Re="(no response)"),this.conversationManager.addMessage(a.id,"assistant",Re),this.activeLearning&&this.activeLearning.onMessageProcessed(n,e.text,Re),this.conversationSummarizer&&!e.metadata?.skipHistory){let K=u.slice(-8).map(J=>({role:J.role,content:J.content}));this.conversationSummarizer.onMessageProcessed(a.id,u.length+2,e.text,Re,K)}let bs=Date.now()-s;return this.logger.info({duration:bs,tokens:z.usage,totalTokens:{inputTokens:pe,outputTokens:we},stopReason:z.stopReason,toolIterations:me},"Message processed"),{text:Re,attachments:Nt.length>0?Nt:void 0}}catch(r){throw this.logger.error({err:r},"Failed to process message"),r}}async abortToolLoop(e,t,s,r,n,o=!1,i){if(!o){let d=[];t.content&&d.push({type:"text",text:t.content});for(let u of t.toolCalls)d.push({type:"tool_use",id:u.id,name:u.name,input:u.input});e.push({role:"assistant",content:d})}let a=t.toolCalls.map(d=>({type:"tool_result",tool_use_id:d.id,content:`Error: tool loop aborted \u2014 ${n}`,is_error:!0}));e.push({role:"user",content:a}),o||this.conversationManager.addMessage(s,"assistant",t.content??"",JSON.stringify(t.toolCalls)),this.conversationManager.addMessage(s,"user","",JSON.stringify(a));let c=e[e.length-1];return c&&c.role==="user"&&Array.isArray(c.content)?c.content.push({type:"text",text:`[System: ${n} Fasse dem User kurz zusammen was du bisher geschafft hast und was noch offen ist.]`}):e.push({role:"user",content:`[System: ${n} Fasse dem User kurz zusammen was du bisher geschafft hast und was noch offen ist.]`}),await this.llm.complete({messages:e,system:r,tier:i})}buildErrorSignature(e){let t=[];for(let s of e)s.type==="tool_result"&&s.is_error&&t.push(s.content);return t.length>0?t.join("|"):""}collapseRepeatedToolErrors(e){let t=[],s=0;for(;s<e.length;){let r=e[s];if(r.role==="assistant"&&Array.isArray(r.content)&&r.content.some(n=>n.type==="tool_use")){let n=s+1<e.length?e[s+1]:null;if(n&&n.role==="user"&&Array.isArray(n.content)&&n.content.every(o=>o.type==="tool_result"&&o.is_error)){let o=this.toolPairSignature(r,n),i=1,a=s+2;for(;a+1<e.length;){let c=e[a],d=e[a+1];if(c.role==="assistant"&&d?.role==="user"&&this.toolPairSignature(c,d)===o)i++,a+=2;else break}if(i>1){t.push(r),t.push(n),t.push({role:"user",content:`[System: The above tool error repeated ${i} times with identical input. The loop was aborted.]`}),s=a;continue}}}t.push(r),s++}return t}toolPairSignature(e,t){let s=Array.isArray(e.content)?e.content.filter(n=>n.type==="tool_use").map(n=>`${n.name}:${JSON.stringify(n.input)}`).join(","):"",r=Array.isArray(t.content)?t.content.filter(n=>n.type==="tool_result").map(n=>n.content).join(","):"";return`${s}|${r}`}async executeToolCallsParallel(e,t,s){let r=[],n=m((u,p)=>{let f=p.content;if(p.attachments&&p.attachments.length>0){r.push(...p.attachments);let g=p.attachments.map(h=>h.fileName).join(", ");f+=`
848
871
 
849
- [${p.attachments.length} Datei(en) werden dem User gesendet: ${f}]`}return{type:"tool_result",tool_use_id:u.id,content:g,is_error:p.isError}},"buildBlock");if(e.length===1){let u=e[0],p=this.getToolLabel(u.name,u.input);s?.(p);let g=await this.executeToolCall(u,t,s);return{blocks:[n(u,g)],attachments:r}}let o=3;s?.(`Running ${e.length} tools...`);let i=new Map;for(let u=0;u<e.length;u++){let p=e[u].name,g=i.get(p);g||(g=[],i.set(p,g)),g.push(u)}let a=[...i.values()].some(u=>u.length>o),c;if(a){c=new Map;let u=[...i.entries()].map(async([p,g])=>{for(let f=0;f<g.length;f+=o){let h=g.slice(f,f+o),y=await Promise.allSettled(h.map(E=>this.executeToolCall(e[E],t,s)));for(let E=0;E<h.length;E++)c.set(h[E],y[E])}});await Promise.all(u)}else{let u=await Promise.allSettled(e.map(p=>this.executeToolCall(p,t,s)));c=new Map(u.map((p,g)=>[g,p]))}return{blocks:e.map((u,p)=>{let g=c.get(p);return g.status==="fulfilled"?n(u,g.value):{type:"tool_result",tool_use_id:u.id,content:`Tool execution failed: ${g.reason}`,is_error:!0}}),attachments:r}}async executeToolCall(e,t,s){let r=this.skillRegistry?.get(e.name);if(!r)return this.logger.warn({tool:e.name},"Unknown skill requested"),{content:`Error: Unknown tool "${e.name}"`,isError:!0};if(this.securityManager){let n=this.securityManager.evaluate({userId:t.userId,action:e.name,riskLevel:r.metadata.riskLevel,platform:t.platform,chatId:t.chatId,chatType:t.chatType});if(!n.allowed)return this.logger.warn({tool:e.name,reason:n.reason,rule:n.matchedRule?.id},"Skill execution denied by security rules"),{content:`Access denied: ${n.reason}`,isError:!0}}if(this.skillSandbox){let n,o;if(e.name==="delegate"){let{ActivityTracker:a}=await Promise.resolve().then(()=>(Q(),te));n=new a(s),o=`agent-${++this.agentIdCounter}`,this.activeAgents.set(o,{chatId:t.chatId,task:String(e.input.task??"").slice(0,200),tracker:n,startedAt:Date.now()})}let i=e.name==="delegate"?{...t,tracker:n,onProgress:s}:s?{...t,onProgress:s}:t;try{let a=await this.skillSandbox.execute(r,e.input,i,void 0,n);return{content:a.display??(a.success?JSON.stringify(a.data):a.error??"Unknown error"),isError:!a.success,attachments:a.attachments}}finally{o&&this.activeAgents.delete(o)}}try{let n=await r.execute(e.input,t);return{content:n.display??(n.success?JSON.stringify(n.data):n.error??"Unknown error"),isError:!n.success,attachments:n.attachments}}catch(n){return{content:`Skill execution failed: ${n instanceof Error?n.message:String(n)}`,isError:!0}}}getToolLabel(e,t){switch(e){case"shell":return`Running: ${String(t.command??"").slice(0,60)}`;case"web_search":return`Searching: ${String(t.query??"")}`;case"email":return`Email: ${String(t.action??"")}`;case"memory":return`Memory: ${String(t.action??"")}`;case"reminder":return`Reminder: ${String(t.action??"")}`;case"calculator":return"Calculating...";case"system_info":return"Getting system info...";case"delegate":return"Delegating sub-task...";case"http":return`Fetching: ${String(t.url??"").slice(0,60)}`;case"file":return`File: ${String(t.action??"")} ${String(t.path??"").slice(0,50)}`;case"clipboard":return`Clipboard: ${String(t.action??"")}`;case"screenshot":return"Taking screenshot...";case"browser":return`Browser: ${String(t.action??"")} ${String(t.url??"").slice(0,50)}`;case"weather":return`Weather: ${String(t.location??"")}`;case"note":return`Note: ${String(t.action??"")}`;case"profile":return`Profile: ${String(t.action??"")}`;case"calendar":return`Calendar: ${String(t.action??"")}`;case"background_task":return`Background task: ${String(t.action??"")}`;case"scheduled_task":return`Scheduled task: ${String(t.action??"")}`;case"cross_platform":return`Cross-platform: ${String(t.action??"")}`;case"code_sandbox":return"Running code...";case"document":return`Document: ${String(t.action??"")}`;default:return`Using ${e}...`}}buildActiveAgentStatus(){if(this.activeAgents.size===0)return;let e=["## Currently running sub-agents"];for(let[t,s]of this.activeAgents){let r=s.tracker.getSnapshot(),n=Math.round(r.totalElapsedMs/1e3);e.push(`- **${t}**: "${s.task}"`,` Status: ${s.tracker.formatStatus()}`,` Running for ${n}s | Last activity ${Math.round(r.idleMs/1e3)}s ago`)}return e.push(""),e.push("If the user asks what you or the agent is doing, describe the above status in natural language."),e.join(`
850
- `)}applyMemoryBudget(e){let t=e.some(o=>o.score!=null&&o.score>0),s=e;t&&(s=e.filter(o=>(o.score??1)>=Rp));let r=0,n=[];for(let o of s){let i=Ne(`[${o.category}] ${o.key}: ${o.value}`);if(r+i>xp)break;r+=i,n.push(o)}return n.length<e.length&&this.logger.info({original:e.length,kept:n.length,tokenCount:r,droppedByScore:e.length-s.length},"Memory budget applied"),n}compressToolLoop(e,t,s=0){let r=this.llm.getContextWindow(),n=Math.floor(r.maxInputTokens*rd);if(Ne(t)+s+e.reduce((h,y)=>h+_t(y),0)<=n)return;let a=[];for(let h=0;h<e.length-1;h++){let y=e[h],E=e[h+1];y.role==="assistant"&&Array.isArray(y.content)&&y.content.some($=>$.type==="tool_use")&&E.role==="user"&&Array.isArray(E.content)&&E.content.some($=>$.type==="tool_result")&&a.push({start:h,end:h+1})}if(a.length<=nd)return;let c=a.length-nd,d=a.slice(0,c),u=[];for(let h of d){let y=e[h.start],E=[];if(Array.isArray(y.content))for(let P of y.content)P.type==="tool_use"&&E.push(P.name);let $=e[h.end],v="ok";Array.isArray($.content)&&$.content.some(x=>x.type==="tool_result"&&x.is_error)&&(v="error"),u.push(`- ${E.join(", ")}: ${v}`)}let p=d[0].start,f=d[d.length-1].end-p+1;e.splice(p,f,{role:"assistant",content:`[Earlier tool interactions compressed (${d.length} pairs):
872
+ [${p.attachments.length} Datei(en) werden dem User gesendet: ${g}]`}return{type:"tool_result",tool_use_id:u.id,content:f,is_error:p.isError}},"buildBlock");if(e.length===1){let u=e[0],p=this.getToolLabel(u.name,u.input);s?.(p);let f=await this.executeToolCall(u,t,s);return{blocks:[n(u,f)],attachments:r}}let o=3;s?.(`Running ${e.length} tools...`);let i=new Map;for(let u=0;u<e.length;u++){let p=e[u].name,f=i.get(p);f||(f=[],i.set(p,f)),f.push(u)}let a=[...i.values()].some(u=>u.length>o),c;if(a){c=new Map;let u=[...i.entries()].map(async([p,f])=>{for(let g=0;g<f.length;g+=o){let h=f.slice(g,g+o),y=await Promise.allSettled(h.map(E=>this.executeToolCall(e[E],t,s)));for(let E=0;E<h.length;E++)c.set(h[E],y[E])}});await Promise.all(u)}else{let u=await Promise.allSettled(e.map(p=>this.executeToolCall(p,t,s)));c=new Map(u.map((p,f)=>[f,p]))}return{blocks:e.map((u,p)=>{let f=c.get(p);return f.status==="fulfilled"?n(u,f.value):{type:"tool_result",tool_use_id:u.id,content:`Tool execution failed: ${f.reason}`,is_error:!0}}),attachments:r}}async executeToolCall(e,t,s){let r=this.skillRegistry?.get(e.name);if(!r)return this.logger.warn({tool:e.name},"Unknown skill requested"),{content:`Error: Unknown tool "${e.name}"`,isError:!0};if(this.securityManager){let n=this.securityManager.evaluate({userId:t.userId,action:e.name,riskLevel:r.metadata.riskLevel,platform:t.platform,chatId:t.chatId,chatType:t.chatType});if(!n.allowed)return this.logger.warn({tool:e.name,reason:n.reason,rule:n.matchedRule?.id},"Skill execution denied by security rules"),{content:`Access denied: ${n.reason}`,isError:!0}}if(this.skillSandbox){let n,o;if(e.name==="delegate"){let{ActivityTracker:a}=await Promise.resolve().then(()=>(ee(),te));n=new a(s),o=`agent-${++this.agentIdCounter}`,this.activeAgents.set(o,{chatId:t.chatId,task:String(e.input.task??"").slice(0,200),tracker:n,startedAt:Date.now()})}let i=e.name==="delegate"?{...t,tracker:n,onProgress:s}:s?{...t,onProgress:s}:t;try{let a=await this.skillSandbox.execute(r,e.input,i,void 0,n);return{content:a.display??(a.success?JSON.stringify(a.data):a.error??"Unknown error"),isError:!a.success,attachments:a.attachments}}finally{o&&this.activeAgents.delete(o)}}try{let n=await r.execute(e.input,t);return{content:n.display??(n.success?JSON.stringify(n.data):n.error??"Unknown error"),isError:!n.success,attachments:n.attachments}}catch(n){return{content:`Skill execution failed: ${n instanceof Error?n.message:String(n)}`,isError:!0}}}getToolLabel(e,t){switch(e){case"shell":return`Running: ${String(t.command??"").slice(0,60)}`;case"web_search":return`Searching: ${String(t.query??"")}`;case"email":return`Email: ${String(t.action??"")}`;case"memory":return`Memory: ${String(t.action??"")}`;case"reminder":return`Reminder: ${String(t.action??"")}`;case"calculator":return"Calculating...";case"system_info":return"Getting system info...";case"delegate":return"Delegating sub-task...";case"http":return`Fetching: ${String(t.url??"").slice(0,60)}`;case"file":return`File: ${String(t.action??"")} ${String(t.path??"").slice(0,50)}`;case"clipboard":return`Clipboard: ${String(t.action??"")}`;case"screenshot":return"Taking screenshot...";case"browser":return`Browser: ${String(t.action??"")} ${String(t.url??"").slice(0,50)}`;case"weather":return`Weather: ${String(t.location??"")}`;case"note":return`Note: ${String(t.action??"")}`;case"profile":return`Profile: ${String(t.action??"")}`;case"calendar":return`Calendar: ${String(t.action??"")}`;case"background_task":return`Background task: ${String(t.action??"")}`;case"scheduled_task":return`Scheduled task: ${String(t.action??"")}`;case"cross_platform":return`Cross-platform: ${String(t.action??"")}`;case"code_sandbox":return"Running code...";case"document":return`Document: ${String(t.action??"")}`;default:return`Using ${e}...`}}buildActiveAgentStatus(){if(this.activeAgents.size===0)return;let e=["## Currently running sub-agents"];for(let[t,s]of this.activeAgents){let r=s.tracker.getSnapshot(),n=Math.round(r.totalElapsedMs/1e3);e.push(`- **${t}**: "${s.task}"`,` Status: ${s.tracker.formatStatus()}`,` Running for ${n}s | Last activity ${Math.round(r.idleMs/1e3)}s ago`)}return e.push(""),e.push("If the user asks what you or the agent is doing, describe the above status in natural language."),e.join(`
873
+ `)}applyMemoryBudget(e){let t=e.some(o=>o.score!=null&&o.score>0),s=e;t&&(s=e.filter(o=>(o.score??1)>=Np));let r=0,n=[];for(let o of s){let i=Oe(`[${o.category}] ${o.key}: ${o.value}`);if(r+i>Lp)break;r+=i,n.push(o)}return n.length<e.length&&this.logger.info({original:e.length,kept:n.length,tokenCount:r,droppedByScore:e.length-s.length},"Memory budget applied"),n}compressToolLoop(e,t,s=0){let r=this.llm.getContextWindow(),n=Math.floor(r.maxInputTokens*ad);if(Oe(t)+s+e.reduce((h,y)=>h+bt(y),0)<=n)return;let a=[];for(let h=0;h<e.length-1;h++){let y=e[h],E=e[h+1];y.role==="assistant"&&Array.isArray(y.content)&&y.content.some($=>$.type==="tool_use")&&E.role==="user"&&Array.isArray(E.content)&&E.content.some($=>$.type==="tool_result")&&a.push({start:h,end:h+1})}if(a.length<=cd)return;let c=a.length-cd,d=a.slice(0,c),u=[];for(let h of d){let y=e[h.start],E=[];if(Array.isArray(y.content))for(let j of y.content)j.type==="tool_use"&&E.push(j.name);let $=e[h.end],v="ok";Array.isArray($.content)&&$.content.some(N=>N.type==="tool_result"&&N.is_error)&&(v="error"),u.push(`- ${E.join(", ")}: ${v}`)}let p=d[0].start,g=d[d.length-1].end-p+1;e.splice(p,g,{role:"assistant",content:`[Earlier tool interactions compressed (${d.length} pairs):
851
874
  ${u.join(`
852
875
  `)}
853
- ]`},{role:"user",content:"[Context compressed to fit context window. Continue with the current task.]"}),this.logger.info({compressedPairs:d.length,removedMessages:f-2},"Compressed tool loop to fit context window")}trimToContextWindow(e,t,s=0,r=1){let n=this.llm.getContextWindow(),o=Math.floor(n.maxInputTokens*rd*r),i=Ne(e)+s,a=t[t.length-1],c=_t(a),u=i+c+200+500,p=o-u;if(p<=0)return this.logger.warn({maxInputTokens:o,systemTokens:i,latestTokens:c},"Context window very tight, sending only latest message"),[a];let g=t.slice(0,-1),f=this.groupToolPairs(g),h=[];for(let $=f.length-1;$>=0;$--){let v=f[$].reduce((P,x)=>P+_t(x),0);if(v>p)break;p-=v,h.unshift(f[$])}let y=h.flat(),E=g.length-y.length;if(E>0){this.logger.info({trimmedCount:E,totalMessages:t.length,maxInputTokens:o},"Trimmed conversation history to fit context window");let $=g.slice(0,g.length-y.length),v=this.summarizeTrimmedMessages($);y.unshift({role:"user",content:`[Earlier conversation summary \u2014 ${E} messages were trimmed to fit the context window:
876
+ ]`},{role:"user",content:"[Context compressed to fit context window. Continue with the current task.]"}),this.logger.info({compressedPairs:d.length,removedMessages:g-2},"Compressed tool loop to fit context window")}trimToContextWindow(e,t,s=0,r=1){let n=this.llm.getContextWindow(),o=Math.floor(n.maxInputTokens*ad*r),i=Oe(e)+s,a=t[t.length-1],c=bt(a),u=i+c+200+500,p=o-u;if(p<=0)return this.logger.warn({maxInputTokens:o,systemTokens:i,latestTokens:c},"Context window very tight, sending only latest message"),[a];let f=t.slice(0,-1),g=this.groupToolPairs(f),h=[];for(let $=g.length-1;$>=0;$--){let v=g[$].reduce((j,N)=>j+bt(N),0);if(v>p)break;p-=v,h.unshift(g[$])}let y=h.flat(),E=f.length-y.length;if(E>0){this.logger.info({trimmedCount:E,totalMessages:t.length,maxInputTokens:o},"Trimmed conversation history to fit context window");let $=f.slice(0,f.length-y.length),v=this.summarizeTrimmedMessages($);y.unshift({role:"user",content:`[Earlier conversation summary \u2014 ${E} messages were trimmed to fit the context window:
854
877
  ${v}
855
878
 
856
879
  The conversation continues below with the most recent messages.]`})}return y.push(a),this.promptBuilder.sanitizeToolMessages(y)}summarizeTrimmedMessages(e){let t=[];for(let r of e){let n=this.extractMessageText(r);if(!n)continue;let o=n.length>150?n.slice(0,150)+"...":n,i=r.role==="user"?"User":"Assistant";if(t.push(`- ${i}: ${o}`),r.role==="assistant"&&Array.isArray(r.content)){for(let a of r.content)if(a.type==="tool_use"){let c=JSON.stringify(a.input).slice(0,80);t.push(` \u2192 Tool: ${a.name}(${c})`)}}}let s=40;if(t.length>s){let r=t.slice(0,s);return r.push(` ... and ${t.length-s} more interactions`),r.join(`
@@ -858,24 +881,24 @@ The conversation continues below with the most recent messages.]`})}return y.pus
858
881
  `)}extractMessageText(e){if(typeof e.content=="string")return e.content;if(!Array.isArray(e.content))return"";let t=[];for(let s of e.content)s.type==="text"&&s.text&&t.push(s.text);return t.join(" ")}groupToolPairs(e){let t=[],s=0;for(;s<e.length;){let r=e[s];if(r.role==="assistant"&&Array.isArray(r.content)&&r.content.some(n=>n.type==="tool_use")){let n=[r];if(s+1<e.length&&e[s+1].role==="user"){let o=e[s+1];if(Array.isArray(o.content)&&o.content.some(i=>i.type==="tool_result")){n.push(o),s+=2,t.push(n);continue}}if(n.length===1&&s+1<e.length&&e[s+1].role==="user"){n.push(e[s+1]),s+=2,t.push(n);continue}t.push(n),s++}else t.push([r]),s++}return t}async buildUserContent(e,t){let s=e.attachments?.filter(i=>i.data)??[];if(s.length===0)return e.text;let r=[];for(let i of s)if(i.type==="image"&&i.data)r.push({type:"image",source:{type:"base64",media_type:i.mimeType??"image/jpeg",data:i.data.toString("base64")}}),this.logger.info({mimeType:i.mimeType,size:i.size},"Image attached to LLM request");else if(i.type==="audio"&&i.data)if(this.speechTranscriber){t?.("Transcribing voice...");try{let a=await this.speechTranscriber.transcribe(i.data,i.mimeType??"audio/ogg"),c=e.text==="[Voice message]"?"":`${e.text}
859
882
 
860
883
  `;if(r.push({type:"text",text:`${c}[Voice transcript]: ${a}`}),this.logger.info({transcriptLength:a.length},"Voice message transcribed"),s.length===1)return r.length===1&&r[0].type==="text"?r[0].text:r}catch(a){this.logger.error({err:a},"Voice transcription failed"),r.push({type:"text",text:"[Voice message could not be transcribed]"})}}else r.push({type:"text",text:"[Voice message received but speech-to-text is not configured. Add speech config to enable transcription.]"});else if((i.type==="document"||i.type==="video"||i.type==="other")&&i.data){let a=this.saveToInbox(i);if(a){let c=this.isTextMimeType(i.mimeType),d=`[File received: "${i.fileName??"unknown"}" (${this.formatBytes(i.data.length)}, ${i.mimeType??"unknown type"})]
861
- [Saved to: ${a}]`;if(c&&i.data.length<=Ip){let u=i.data.toString("utf-8");d+=`
884
+ [Saved to: ${a}]`;if(c&&i.data.length<=Dp){let u=i.data.toString("utf-8");d+=`
862
885
  [File content]:
863
- ${u}`}if(this.documentProcessor&&this.isIngestable(i.mimeType))try{let u=await this.documentProcessor.ingest(e.userId,a,i.fileName??"unknown",i.mimeType??"application/octet-stream");if(u.existing){try{Li.unlinkSync(a)}catch{}d=`[File received: "${i.fileName??"unknown"}" (duplicate, not saved again)]
886
+ ${u}`}if(this.documentProcessor&&this.isIngestable(i.mimeType))try{let u=await this.documentProcessor.ingest(e.userId,a,i.fileName??"unknown",i.mimeType??"application/octet-stream");if(u.existing){try{Mi.unlinkSync(a)}catch{}d=`[File received: "${i.fileName??"unknown"}" (duplicate, not saved again)]
864
887
  [IMPORTANT: This document is already indexed (${u.chunkCount} chunks). To read or answer questions about it, use the "document" skill with action "search" and a relevant query. Do NOT use shell/file tools to read the PDF.]`}else d+=`
865
- [IMPORTANT: Document has been indexed (${u.chunkCount} chunks). To read or answer questions about it, use the "document" skill with action "search" and a relevant query. Do NOT use shell/file tools to read the PDF.]`}catch(u){this.logger.warn({err:u,fileName:i.fileName},"Auto-ingest failed")}r.push({type:"text",text:d}),this.logger.info({fileName:i.fileName,savedPath:a,size:i.data.length},"File saved to inbox")}}let n=this.isSyntheticLabel(e.text);e.text&&!n&&r.push({type:"text",text:e.text});let o=r.some(i=>i.type==="text");if(r.some(i=>i.type==="image")&&!o)r.push({type:"text",text:"What do you see in this image?"});else if(n&&r.some(i=>i.type==="text"&&i.text.startsWith("[File received:"))){let i=r.some(c=>c.type==="text"&&c.text.includes("document is already indexed")),a=r.some(c=>c.type==="text"&&c.text.includes("Document has been indexed"));i||a?r.push({type:"text",text:"The user sent this document. It has been automatically indexed. Acknowledge receipt and tell the user the document is ready \u2014 they can ask questions about its content anytime."}):r.push({type:"text",text:"The user sent this file without any instructions. Ask them what they would like you to do with it. Do NOT take any other actions, do NOT use any tools, and do NOT act on conversation history or memories. ONLY ask what the user wants."})}else r.length===0&&r.push({type:"text",text:e.text||"(empty message)"});return r}isSyntheticLabel(e){return e?["[Photo","[Voice message","[Video","[Document","[File","[Sticker","[Audio"].some(s=>e.startsWith(s)):!0}saveToInbox(e){if(!e.data)return;let t=this.inboxPath??td.resolve("./data/inbox");try{Li.mkdirSync(t,{recursive:!0})}catch{this.logger.error({inboxDir:t},"Cannot create inbox directory");return}let s=new Date().toISOString().replace(/[:.]/g,"-"),n=(e.fileName??`file_${s}`).replace(/[<>:"/\\|?*]/g,"_"),o=`${s}_${n}`,i=td.join(t,o);try{return Li.writeFileSync(i,e.data),i}catch(a){this.logger.error({err:a,filePath:i},"Failed to save file to inbox");return}}isIngestable(e){return e?["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword","text/plain","text/csv","text/markdown","text/html","application/json","application/xml"].includes(e)||e.startsWith("text/"):!1}isTextMimeType(e){return e?["text/","application/json","application/xml","application/javascript","application/typescript","application/x-yaml","application/yaml","application/toml","application/x-sh","application/sql","application/csv","application/x-csv"].some(s=>e.startsWith(s)):!1}formatBytes(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}}});var kr,Mi=_(()=>{"use strict";kr=class{static{m(this,"ReminderScheduler")}reminderRepo;sendMessage;logger;linkedUsers;intervalId;checkIntervalMs;constructor(e,t,s,r=15e3,n){this.reminderRepo=e,this.sendMessage=t,this.logger=s,this.linkedUsers=n,this.checkIntervalMs=r}start(){this.logger.info("Reminder scheduler started"),this.intervalId=setInterval(()=>this.checkDueReminders(),this.checkIntervalMs),this.checkDueReminders()}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=void 0),this.logger.info("Reminder scheduler stopped")}async checkDueReminders(){try{let e=this.reminderRepo.getDue();for(let t of e)try{let s=`\u23F0 Reminder: ${t.message}`;if(await this.sendMessage(t.platform,t.chatId,s),this.linkedUsers)try{let r=this.linkedUsers.getMasterUserId(t.userId),n=this.linkedUsers.getLinkedUsers(r);for(let o of n){if(o.platform===t.platform)continue;let i=this.linkedUsers.findConversation(o.platform,o.id);i&&await this.sendMessage(o.platform,i.chatId,s)}}catch(r){this.logger.debug({err:r,reminderId:t.id},"Cross-platform reminder delivery failed")}this.reminderRepo.markFired(t.id),this.logger.info({reminderId:t.id},"Reminder fired")}catch(s){this.logger.error({err:s,reminderId:t.id},"Failed to send reminder")}}catch(e){this.logger.error({err:e},"Error checking due reminders")}}}});var vr,Oi=_(()=>{"use strict";vr=class{static{m(this,"SpeechTranscriber")}logger;apiKey;baseUrl;constructor(e,t){this.logger=t,this.apiKey=e.apiKey,e.provider==="groq"?this.baseUrl=e.baseUrl??"https://api.groq.com/openai/v1":this.baseUrl=e.baseUrl??"https://api.openai.com/v1"}async transcribe(e,t){let s=this.mimeToExtension(t),r=new FormData;r.append("file",new Blob([e],{type:t}),`audio.${s}`),r.append("model","whisper-1");try{let n=await fetch(`${this.baseUrl}/audio/transcriptions`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`},body:r});if(!n.ok){let i=await n.text();throw new Error(`Whisper API ${n.status}: ${i}`)}let o=await n.json();return this.logger.info({textLength:o.text.length},"Voice transcribed"),o.text}catch(n){throw this.logger.error({err:n},"Voice transcription failed"),n}}mimeToExtension(e){return{"audio/ogg":"ogg","audio/mpeg":"mp3","audio/mp4":"m4a","audio/wav":"wav","audio/webm":"webm","audio/x-m4a":"m4a"}[e]??"ogg"}}});var Sr,Pi=_(()=>{"use strict";Sr=class{static{m(this,"SpeechSynthesizer")}logger;apiKey;baseUrl;model;voice;constructor(e,t){this.logger=t,this.apiKey=e.apiKey,this.baseUrl=e.baseUrl??"https://api.openai.com/v1",this.model=e.ttsModel??"tts-1",this.voice=e.ttsVoice??"alloy"}async synthesize(e){this.logger.info({textLength:e.length,model:this.model,voice:this.voice},"Synthesizing speech");let t=await fetch(`${this.baseUrl}/audio/speech`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:this.model,input:e,voice:this.voice,response_format:"opus"})});if(!t.ok){let r=await t.text();throw new Error(`TTS failed: ${t.status} ${r}`)}let s=Buffer.from(await t.arrayBuffer());return this.logger.info({audioBytes:s.length},"Speech synthesized"),s}}});var Ar,Ui=_(()=>{"use strict";Ar=class{static{m(this,"ImageGenerator")}config;logger;constructor(e,t){this.config=e,this.logger=t}async generate(e,t={}){return this.logger.info({provider:this.config.provider,model:t.model,size:t.size},"Generating image"),this.config.provider==="openai"?this.generateOpenAI(e,t):this.generateGoogle(e,t)}async generateOpenAI(e,t){let{default:s}=await import("openai"),r=new s({apiKey:this.config.apiKey,...this.config.baseUrl&&{baseURL:this.config.baseUrl}}),n=t.model??"gpt-image-1",i=(await r.images.generate({model:n,prompt:e,n:1,size:t.size??"1024x1024",quality:t.quality??"medium"})).data?.[0]?.b64_json;if(!i)throw new Error("OpenAI image generation returned no data");let a=Buffer.from(i,"base64");return this.logger.info({model:n,bytes:a.length},"Image generated via OpenAI"),{data:a,mimeType:"image/png"}}async generateGoogle(e,t){let{GoogleGenAI:s}=await import("@google/genai"),r=new s({apiKey:this.config.apiKey}),n=t.model??"gemini-2.0-flash-exp",a=(await r.models.generateContent({model:n,contents:[{role:"user",parts:[{text:e}]}],config:{responseModalities:["IMAGE","TEXT"]}})).candidates?.[0]?.content?.parts?.find(u=>u.inlineData?.mimeType?.startsWith("image/"));if(!a?.inlineData)throw new Error("Google image generation returned no image data");let c=Buffer.from(a.inlineData.data,"base64"),d=a.inlineData.mimeType??"image/png";return this.logger.info({model:n,bytes:c.length,mimeType:d},"Image generated via Google"),{data:c,mimeType:d}}}});var Ir,Fi=_(()=>{"use strict";Ir=class{static{m(this,"TransitClient")}logger;hafasClient;constructor(e){this.logger=e}async getClient(){if(this.hafasClient)return this.hafasClient;let{createClient:e}=await import("hafas-client"),{profile:t}=await import("hafas-client/p/oebb/index.js");return this.hafasClient=e(t,"alfred-ai-assistant"),this.hafasClient}async searchStops(e){return(await(await this.getClient()).locations(e,{results:10})).filter(r=>r.type==="stop"||r.type==="station").map(r=>({id:r.id,name:r.name,location:r.location?{latitude:r.location.latitude,longitude:r.location.longitude}:void 0}))}async journeys(e,t,s){let r=await this.getClient(),n={results:s?.results??3};return s?.departure&&(n.departure=s.departure),s?.arrival&&(n.arrival=s.arrival),((await r.journeys(e,t,n)).journeys||[]).map(i=>{let a=(i.legs||[]).map(f=>({origin:f.origin?.name||f.origin?.id||"Unknown",destination:f.destination?.name||f.destination?.id||"Unknown",departure:f.departure||f.plannedDeparture||"",arrival:f.arrival||f.plannedArrival||"",line:f.line?.name,direction:f.direction,mode:f.line?.mode||(f.walking?"walking":"unknown"),walking:f.walking===!0})),c=a[0]?.departure||"",d=a[a.length-1]?.arrival||"",u=c&&d?Math.round((new Date(d).getTime()-new Date(c).getTime())/6e4):0,p=a.filter(f=>!f.walking),g=Math.max(0,p.length-1);return{legs:a,departure:c,arrival:d,duration:u,transfers:g}})}async departures(e,t){let s=await this.getClient(),r={duration:t?.duration??30};t?.when&&(r.when=t.when);let n=await s.departures(e,r);return(n.departures||n||[]).map(o=>{let i=o.plannedWhen?new Date(o.plannedWhen):null,a=o.when?new Date(o.when):null,c=i&&a?Math.round((a.getTime()-i.getTime())/6e4):o.delay!=null?Math.round(o.delay/60):void 0;return{line:o.line?.name||o.line?.fahrtNr||"Unknown",direction:o.direction||o.destination?.name||"Unknown",when:o.when||o.plannedWhen||"",delay:c&&c>0?c:void 0,platform:o.platform||void 0}})}}});var xr,ji=_(()=>{"use strict";xr=class{static{m(this,"ResponseFormatter")}format(e,t){switch(t){case"telegram":return{text:this.toTelegramHTML(e),parseMode:"html"};case"discord":return{text:this.toMarkdown(e),parseMode:"markdown"};case"matrix":return{text:this.toTelegramHTML(e),parseMode:"html"};case"whatsapp":return{text:this.toWhatsApp(e),parseMode:"text"};case"signal":return{text:this.stripFormatting(e),parseMode:"text"};default:return{text:e,parseMode:"text"}}}toTelegramHTML(e){let t=e,s=[];t=t.replace(/```(\w*)\n([\s\S]*?)```/g,(n,o,i)=>(s.push(`<pre>${this.escapeHTML(i.trimEnd())}</pre>`),`\0CB${s.length-1}\0`));let r=[];t=t.replace(/`([^`]+)`/g,(n,o)=>(r.push(`<code>${this.escapeHTML(o)}</code>`),`\0IC${r.length-1}\0`)),t=t.replace(/^#{1,6}\s+(.+)$/gm,"<b>$1</b>"),t=t.replace(/^[-*_]{3,}\s*$/gm,""),t=t.replace(/\*\*(.+?)\*\*/g,"<b>$1</b>"),t=t.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"<i>$1</i>"),t=t.replace(/~~(.+?)~~/g,"<s>$1</s>"),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2">$1</a>'),t=t.replace(/<(\/?)(?:strong)>/gi,"<$1b>"),t=t.replace(/<(\/?)(?:em)>/gi,"<$1i>"),t=t.replace(/<(\/?)(?:del|strike)>/gi,"<$1s>");for(let n of["b","i","s","u"]){let o="";for(;o!==t;)o=t,t=this.flattenNestedTag(t,n)}return t=t.replace(/<(?!\/?(?:b|i|s|u|a|pre|code|tg-spoiler|tg-emoji|blockquote)(?:[\s>\/]|$))[^>]*>/gi,""),t=t.replace(/<(?!\/?(?:b|i|s|u|a|pre|code|tg-spoiler|tg-emoji|blockquote)(?:[\s>\/]|$))/gi,"&lt;"),t=t.replace(/\x00CB(\d+)\x00/g,(n,o)=>s[parseInt(o)]),t=t.replace(/\x00IC(\d+)\x00/g,(n,o)=>r[parseInt(o)]),t=t.replace(/\n{3,}/g,`
888
+ [IMPORTANT: Document has been indexed (${u.chunkCount} chunks). To read or answer questions about it, use the "document" skill with action "search" and a relevant query. Do NOT use shell/file tools to read the PDF.]`}catch(u){this.logger.warn({err:u,fileName:i.fileName},"Auto-ingest failed")}r.push({type:"text",text:d}),this.logger.info({fileName:i.fileName,savedPath:a,size:i.data.length},"File saved to inbox")}}let n=this.isSyntheticLabel(e.text);e.text&&!n&&r.push({type:"text",text:e.text});let o=r.some(i=>i.type==="text");if(r.some(i=>i.type==="image")&&!o)r.push({type:"text",text:"What do you see in this image?"});else if(n&&r.some(i=>i.type==="text"&&i.text.startsWith("[File received:"))){let i=r.some(c=>c.type==="text"&&c.text.includes("document is already indexed")),a=r.some(c=>c.type==="text"&&c.text.includes("Document has been indexed"));i||a?r.push({type:"text",text:"The user sent this document. It has been automatically indexed. Acknowledge receipt and tell the user the document is ready \u2014 they can ask questions about its content anytime."}):r.push({type:"text",text:"The user sent this file without any instructions. Ask them what they would like you to do with it. Do NOT take any other actions, do NOT use any tools, and do NOT act on conversation history or memories. ONLY ask what the user wants."})}else r.length===0&&r.push({type:"text",text:e.text||"(empty message)"});return r}isSyntheticLabel(e){return e?["[Photo","[Voice message","[Video","[Document","[File","[Sticker","[Audio"].some(s=>e.startsWith(s)):!0}saveToInbox(e){if(!e.data)return;let t=this.inboxPath??od.resolve("./data/inbox");try{Mi.mkdirSync(t,{recursive:!0})}catch{this.logger.error({inboxDir:t},"Cannot create inbox directory");return}let s=new Date().toISOString().replace(/[:.]/g,"-"),n=(e.fileName??`file_${s}`).replace(/[<>:"/\\|?*]/g,"_"),o=`${s}_${n}`,i=od.join(t,o);try{return Mi.writeFileSync(i,e.data),i}catch(a){this.logger.error({err:a,filePath:i},"Failed to save file to inbox");return}}isIngestable(e){return e?["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword","text/plain","text/csv","text/markdown","text/html","application/json","application/xml"].includes(e)||e.startsWith("text/"):!1}isTextMimeType(e){return e?["text/","application/json","application/xml","application/javascript","application/typescript","application/x-yaml","application/yaml","application/toml","application/x-sh","application/sql","application/csv","application/x-csv"].some(s=>e.startsWith(s)):!1}formatBytes(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}}});var Ir,Pi=_(()=>{"use strict";Ir=class{static{m(this,"ReminderScheduler")}reminderRepo;sendMessage;logger;linkedUsers;intervalId;checkIntervalMs;constructor(e,t,s,r=15e3,n){this.reminderRepo=e,this.sendMessage=t,this.logger=s,this.linkedUsers=n,this.checkIntervalMs=r}start(){this.logger.info("Reminder scheduler started"),this.intervalId=setInterval(()=>this.checkDueReminders(),this.checkIntervalMs),this.checkDueReminders()}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=void 0),this.logger.info("Reminder scheduler stopped")}async checkDueReminders(){try{let e=this.reminderRepo.getDue();for(let t of e)try{let s=`\u23F0 Reminder: ${t.message}`;if(await this.sendMessage(t.platform,t.chatId,s),this.linkedUsers)try{let r=this.linkedUsers.getMasterUserId(t.userId),n=this.linkedUsers.getLinkedUsers(r);for(let o of n){if(o.platform===t.platform)continue;let i=this.linkedUsers.findConversation(o.platform,o.id);i&&await this.sendMessage(o.platform,i.chatId,s)}}catch(r){this.logger.debug({err:r,reminderId:t.id},"Cross-platform reminder delivery failed")}this.reminderRepo.markFired(t.id),this.logger.info({reminderId:t.id},"Reminder fired")}catch(s){this.logger.error({err:s,reminderId:t.id},"Failed to send reminder")}}catch(e){this.logger.error({err:e},"Error checking due reminders")}}}});var xr,Ui=_(()=>{"use strict";xr=class{static{m(this,"SpeechTranscriber")}logger;apiKey;baseUrl;constructor(e,t){this.logger=t,this.apiKey=e.apiKey,e.provider==="groq"?this.baseUrl=e.baseUrl??"https://api.groq.com/openai/v1":this.baseUrl=e.baseUrl??"https://api.openai.com/v1"}async transcribe(e,t){let s=this.mimeToExtension(t),r=new FormData;r.append("file",new Blob([e],{type:t}),`audio.${s}`),r.append("model","whisper-1");try{let n=await fetch(`${this.baseUrl}/audio/transcriptions`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`},body:r});if(!n.ok){let i=await n.text();throw new Error(`Whisper API ${n.status}: ${i}`)}let o=await n.json();return this.logger.info({textLength:o.text.length},"Voice transcribed"),o.text}catch(n){throw this.logger.error({err:n},"Voice transcription failed"),n}}mimeToExtension(e){return{"audio/ogg":"ogg","audio/mpeg":"mp3","audio/mp4":"m4a","audio/wav":"wav","audio/webm":"webm","audio/x-m4a":"m4a"}[e]??"ogg"}}});var Rr,Fi=_(()=>{"use strict";Rr=class{static{m(this,"SpeechSynthesizer")}logger;apiKey;baseUrl;model;voice;constructor(e,t){this.logger=t,this.apiKey=e.apiKey,this.baseUrl=e.baseUrl??"https://api.openai.com/v1",this.model=e.ttsModel??"tts-1",this.voice=e.ttsVoice??"alloy"}async synthesize(e){this.logger.info({textLength:e.length,model:this.model,voice:this.voice},"Synthesizing speech");let t=await fetch(`${this.baseUrl}/audio/speech`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:this.model,input:e,voice:this.voice,response_format:"opus"})});if(!t.ok){let r=await t.text();throw new Error(`TTS failed: ${t.status} ${r}`)}let s=Buffer.from(await t.arrayBuffer());return this.logger.info({audioBytes:s.length},"Speech synthesized"),s}}});var Cr,ji=_(()=>{"use strict";Cr=class{static{m(this,"ImageGenerator")}config;logger;constructor(e,t){this.config=e,this.logger=t}async generate(e,t={}){return this.logger.info({provider:this.config.provider,model:t.model,size:t.size},"Generating image"),this.config.provider==="openai"?this.generateOpenAI(e,t):this.generateGoogle(e,t)}async generateOpenAI(e,t){let{default:s}=await import("openai"),r=new s({apiKey:this.config.apiKey,...this.config.baseUrl&&{baseURL:this.config.baseUrl}}),n=t.model??"gpt-image-1",i=(await r.images.generate({model:n,prompt:e,n:1,size:t.size??"1024x1024",quality:t.quality??"medium"})).data?.[0]?.b64_json;if(!i)throw new Error("OpenAI image generation returned no data");let a=Buffer.from(i,"base64");return this.logger.info({model:n,bytes:a.length},"Image generated via OpenAI"),{data:a,mimeType:"image/png"}}async generateGoogle(e,t){let{GoogleGenAI:s}=await import("@google/genai"),r=new s({apiKey:this.config.apiKey}),n=t.model??"gemini-2.0-flash-exp",a=(await r.models.generateContent({model:n,contents:[{role:"user",parts:[{text:e}]}],config:{responseModalities:["IMAGE","TEXT"]}})).candidates?.[0]?.content?.parts?.find(u=>u.inlineData?.mimeType?.startsWith("image/"));if(!a?.inlineData)throw new Error("Google image generation returned no image data");let c=Buffer.from(a.inlineData.data,"base64"),d=a.inlineData.mimeType??"image/png";return this.logger.info({model:n,bytes:c.length,mimeType:d},"Image generated via Google"),{data:c,mimeType:d}}}});var Dr,Bi=_(()=>{"use strict";Dr=class{static{m(this,"TransitClient")}logger;hafasClient;constructor(e){this.logger=e}async getClient(){if(this.hafasClient)return this.hafasClient;let{createClient:e}=await import("hafas-client"),{profile:t}=await import("hafas-client/p/oebb/index.js");return this.hafasClient=e(t,"alfred-ai-assistant"),this.hafasClient}async searchStops(e){return(await(await this.getClient()).locations(e,{results:10})).filter(r=>r.type==="stop"||r.type==="station").map(r=>({id:r.id,name:r.name,location:r.location?{latitude:r.location.latitude,longitude:r.location.longitude}:void 0}))}async journeys(e,t,s){let r=await this.getClient(),n={results:s?.results??3};return s?.departure&&(n.departure=s.departure),s?.arrival&&(n.arrival=s.arrival),((await r.journeys(e,t,n)).journeys||[]).map(i=>{let a=(i.legs||[]).map(g=>({origin:g.origin?.name||g.origin?.id||"Unknown",destination:g.destination?.name||g.destination?.id||"Unknown",departure:g.departure||g.plannedDeparture||"",arrival:g.arrival||g.plannedArrival||"",line:g.line?.name,direction:g.direction,mode:g.line?.mode||(g.walking?"walking":"unknown"),walking:g.walking===!0})),c=a[0]?.departure||"",d=a[a.length-1]?.arrival||"",u=c&&d?Math.round((new Date(d).getTime()-new Date(c).getTime())/6e4):0,p=a.filter(g=>!g.walking),f=Math.max(0,p.length-1);return{legs:a,departure:c,arrival:d,duration:u,transfers:f}})}async departures(e,t){let s=await this.getClient(),r={duration:t?.duration??30};t?.when&&(r.when=t.when);let n=await s.departures(e,r);return(n.departures||n||[]).map(o=>{let i=o.plannedWhen?new Date(o.plannedWhen):null,a=o.when?new Date(o.when):null,c=i&&a?Math.round((a.getTime()-i.getTime())/6e4):o.delay!=null?Math.round(o.delay/60):void 0;return{line:o.line?.name||o.line?.fahrtNr||"Unknown",direction:o.direction||o.destination?.name||"Unknown",when:o.when||o.plannedWhen||"",delay:c&&c>0?c:void 0,platform:o.platform||void 0}})}}});var Lr,Hi=_(()=>{"use strict";Lr=class{static{m(this,"ResponseFormatter")}format(e,t){switch(t){case"telegram":return{text:this.toTelegramHTML(e),parseMode:"html"};case"discord":return{text:this.toMarkdown(e),parseMode:"markdown"};case"matrix":return{text:this.toTelegramHTML(e),parseMode:"html"};case"whatsapp":return{text:this.toWhatsApp(e),parseMode:"text"};case"signal":return{text:this.stripFormatting(e),parseMode:"text"};default:return{text:e,parseMode:"text"}}}toTelegramHTML(e){let t=e,s=[];t=t.replace(/```(\w*)\n([\s\S]*?)```/g,(n,o,i)=>(s.push(`<pre>${this.escapeHTML(i.trimEnd())}</pre>`),`\0CB${s.length-1}\0`));let r=[];t=t.replace(/`([^`]+)`/g,(n,o)=>(r.push(`<code>${this.escapeHTML(o)}</code>`),`\0IC${r.length-1}\0`)),t=t.replace(/^#{1,6}\s+(.+)$/gm,"<b>$1</b>"),t=t.replace(/^[-*_]{3,}\s*$/gm,""),t=t.replace(/\*\*(.+?)\*\*/g,"<b>$1</b>"),t=t.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"<i>$1</i>"),t=t.replace(/~~(.+?)~~/g,"<s>$1</s>"),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2">$1</a>'),t=t.replace(/<(\/?)(?:strong)>/gi,"<$1b>"),t=t.replace(/<(\/?)(?:em)>/gi,"<$1i>"),t=t.replace(/<(\/?)(?:del|strike)>/gi,"<$1s>");for(let n of["b","i","s","u"]){let o="";for(;o!==t;)o=t,t=this.flattenNestedTag(t,n)}return t=t.replace(/<(?!\/?(?:b|i|s|u|a|pre|code|tg-spoiler|tg-emoji|blockquote)(?:[\s>\/]|$))[^>]*>/gi,""),t=t.replace(/<(?!\/?(?:b|i|s|u|a|pre|code|tg-spoiler|tg-emoji|blockquote)(?:[\s>\/]|$))/gi,"&lt;"),t=t.replace(/\x00CB(\d+)\x00/g,(n,o)=>s[parseInt(o)]),t=t.replace(/\x00IC(\d+)\x00/g,(n,o)=>r[parseInt(o)]),t=t.replace(/\n{3,}/g,`
866
889
 
867
- `),t}flattenNestedTag(e,t){let s=`<${t}>`,r=`</${t}>`,n=0,o="",i=0;for(;i<e.length;){if(e.slice(i,i+s.length).toLowerCase()===s){n++,n===1&&(o+=s),i+=s.length;continue}if(e.slice(i,i+r.length).toLowerCase()===r){n--,n<=0&&(o+=r,n=0),i+=r.length;continue}o+=e[i],i++}return o}toMarkdown(e){let t=e;return t=t.replace(/<\/?(?:b|strong)>/gi,"**"),t=t.replace(/<\/?(?:i|em)>/gi,"*"),t=t.replace(/<\/?(?:s|del|strike)>/gi,"~~"),t=t.replace(/<code>([^<]*)<\/code>/gi,"`$1`"),t=t.replace(/<pre>([^<]*)<\/pre>/gi,"```\n$1\n```"),t=t.replace(/<a\s+href="([^"]*)"[^>]*>([^<]*)<\/a>/gi,"[$2]($1)"),t=t.replace(/<[^>]+>/g,""),t}toWhatsApp(e){let t=e;return t=t.replace(/<\/?(?:b|strong)>/gi,"**"),t=t.replace(/<\/?(?:i|em)>/gi,"*"),t=t.replace(/<\/?(?:s|del|strike)>/gi,"~~"),t=t.replace(/<[^>]+>/g,""),t=t.replace(/^#{1,6}\s+(.+)$/gm,"*$1*"),t=t.replace(/^[-*_]{3,}\s*$/gm,""),t=t.replace(/\*\*(.+?)\*\*/g,"*$1*"),t=t.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"_$1_"),t=t.replace(/~~(.+?)~~/g,"~$1~"),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,"$1 ($2)"),t}stripFormatting(e){let t=e;return t=t.replace(/<[^>]+>/g,""),t=t.replace(/```\w*\n?/g,""),t=t.replace(/`([^`]+)`/g,"$1"),t=t.replace(/^#{1,6}\s+/gm,""),t=t.replace(/^[-*_]{3,}\s*$/gm,""),t=t.replace(/\*\*(.+?)\*\*/g,"$1"),t=t.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"$1"),t=t.replace(/~~(.+?)~~/g,"$1"),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,"$1 ($2)"),t}escapeHTML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}}});var Rr,Bi=_(()=>{"use strict";Rr=class{static{m(this,"EmbeddingService")}llm;embeddingRepo;logger;constructor(e,t,s){this.llm=e,this.embeddingRepo=t,this.logger=s}async embedAndStore(e,t,s,r){if(this.llm.supportsEmbeddings())try{let n=await this.llm.embed(t);if(!n)return;let o=this.embeddingRepo.store({userId:e,sourceType:s,sourceId:r,content:t,embedding:n.embedding,model:n.model,dimensions:n.dimensions});return this.logger.debug({userId:e,sourceType:s,sourceId:r},"Embedding stored"),o.id}catch(n){this.logger.error({err:n,userId:e,sourceType:s,sourceId:r},"Failed to embed content");return}}async semanticSearch(e,t,s=10){if(!this.llm.supportsEmbeddings())return[];try{let r=await this.llm.embed(t);if(!r)return[];let n=this.embeddingRepo.findByUser(e);if(n.length===0)return[];let o=n.map(a=>{let c=this.cosineSimilarity(r.embedding,a.embedding);return{...a,score:c}});return o.sort((a,c)=>c.score-a.score),o.slice(0,s).map(a=>({key:a.sourceId,value:a.content,category:a.sourceType,score:a.score}))}catch(r){return this.logger.error({err:r},"Semantic search failed"),[]}}cosineSimilarity(e,t){if(e.length!==t.length)return 0;let s=0,r=0,n=0;for(let i=0;i<e.length;i++)s+=e[i]*t[i],r+=e[i]*e[i],n+=t[i]*t[i];let o=Math.sqrt(r)*Math.sqrt(n);return o===0?0:s/o}}});import{createHash as Cp}from"node:crypto";var Cr,Hi=_(()=>{"use strict";Cr=class{static{m(this,"DocumentProcessor")}docRepo;embeddingService;logger;constructor(e,t,s){this.docRepo=e,this.embeddingService=t,this.logger=s}async ingest(e,t,s,r){let n=await import("node:fs"),o=n.readFileSync(t),i=Cp("sha256").update(o).digest("hex"),a=this.docRepo.findByContentHash(e,i);if(a&&a.chunkCount>0)return this.logger.info({documentId:a.id,filename:s,contentHash:i.slice(0,12)},"Document already ingested (duplicate)"),{documentId:a.id,chunkCount:a.chunkCount,existing:!0};a&&a.chunkCount===0&&(this.logger.info({documentId:a.id,filename:s},"Removing failed previous ingest attempt"),this.docRepo.deleteDocument(a.id));let c=await this.extractText(t,r),d=n.statSync(t),u=this.docRepo.createDocument(e,s,r,d.size,i),p=this.chunkText(c,500,50);for(let g=0;g<p.length;g++){let f;try{f=await this.embeddingService.embedAndStore(e,p[g],"document",`${u.id}:${g}`)}catch(h){this.logger.warn({documentId:u.id,chunkIndex:g,err:h},"Embedding failed for chunk, continuing")}this.docRepo.addChunk(u.id,g,p[g],f)}return this.docRepo.updateChunkCount(u.id,p.length),this.logger.info({documentId:u.id,filename:s,chunkCount:p.length},"Document ingested"),{documentId:u.id,chunkCount:p.length}}async extractText(e,t){let s=await import("node:fs");if(t==="application/pdf")try{let r=(await import("pdf-parse")).default,n=s.readFileSync(e);return(await r(n)).text}catch(r){throw this.logger.error({err:r},"PDF parsing failed"),new Error("Failed to parse PDF")}if(t==="application/vnd.openxmlformats-officedocument.wordprocessingml.document"||t==="application/msword")try{return(await(await import("mammoth")).extractRawText({path:e})).value}catch(r){throw this.logger.error({err:r},"DOCX parsing failed"),new Error("Failed to parse DOCX")}return s.readFileSync(e,"utf-8")}chunkText(e,t,s){let n=Math.round(t*3.5),o=Math.round(s*3.5),i=[],a=0;for(;a<e.length;){let c=a+n;if(c>=e.length){i.push(e.slice(a).trim());break}let d=Math.max(c-200,a),u=e.slice(d,c+200),p=u.lastIndexOf(`
890
+ `),t}flattenNestedTag(e,t){let s=`<${t}>`,r=`</${t}>`,n=0,o="",i=0;for(;i<e.length;){if(e.slice(i,i+s.length).toLowerCase()===s){n++,n===1&&(o+=s),i+=s.length;continue}if(e.slice(i,i+r.length).toLowerCase()===r){n--,n<=0&&(o+=r,n=0),i+=r.length;continue}o+=e[i],i++}return o}toMarkdown(e){let t=e;return t=t.replace(/<\/?(?:b|strong)>/gi,"**"),t=t.replace(/<\/?(?:i|em)>/gi,"*"),t=t.replace(/<\/?(?:s|del|strike)>/gi,"~~"),t=t.replace(/<code>([^<]*)<\/code>/gi,"`$1`"),t=t.replace(/<pre>([^<]*)<\/pre>/gi,"```\n$1\n```"),t=t.replace(/<a\s+href="([^"]*)"[^>]*>([^<]*)<\/a>/gi,"[$2]($1)"),t=t.replace(/<[^>]+>/g,""),t}toWhatsApp(e){let t=e;return t=t.replace(/<\/?(?:b|strong)>/gi,"**"),t=t.replace(/<\/?(?:i|em)>/gi,"*"),t=t.replace(/<\/?(?:s|del|strike)>/gi,"~~"),t=t.replace(/<[^>]+>/g,""),t=t.replace(/^#{1,6}\s+(.+)$/gm,"*$1*"),t=t.replace(/^[-*_]{3,}\s*$/gm,""),t=t.replace(/\*\*(.+?)\*\*/g,"*$1*"),t=t.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"_$1_"),t=t.replace(/~~(.+?)~~/g,"~$1~"),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,"$1 ($2)"),t}stripFormatting(e){let t=e;return t=t.replace(/<[^>]+>/g,""),t=t.replace(/```\w*\n?/g,""),t=t.replace(/`([^`]+)`/g,"$1"),t=t.replace(/^#{1,6}\s+/gm,""),t=t.replace(/^[-*_]{3,}\s*$/gm,""),t=t.replace(/\*\*(.+?)\*\*/g,"$1"),t=t.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"$1"),t=t.replace(/~~(.+?)~~/g,"$1"),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,"$1 ($2)"),t}escapeHTML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}}});var Nr,zi=_(()=>{"use strict";Nr=class{static{m(this,"EmbeddingService")}llm;embeddingRepo;logger;constructor(e,t,s){this.llm=e,this.embeddingRepo=t,this.logger=s}async embedAndStore(e,t,s,r){if(this.llm.supportsEmbeddings())try{let n=await this.llm.embed(t);if(!n)return;let o=this.embeddingRepo.store({userId:e,sourceType:s,sourceId:r,content:t,embedding:n.embedding,model:n.model,dimensions:n.dimensions});return this.logger.debug({userId:e,sourceType:s,sourceId:r},"Embedding stored"),o.id}catch(n){this.logger.error({err:n,userId:e,sourceType:s,sourceId:r},"Failed to embed content");return}}async semanticSearch(e,t,s=10){if(!this.llm.supportsEmbeddings())return[];try{let r=await this.llm.embed(t);if(!r)return[];let n=this.embeddingRepo.findByUser(e);if(n.length===0)return[];let o=n.map(a=>{let c=this.cosineSimilarity(r.embedding,a.embedding);return{...a,score:c}});return o.sort((a,c)=>c.score-a.score),o.slice(0,s).map(a=>({key:a.sourceId,value:a.content,category:a.sourceType,score:a.score}))}catch(r){return this.logger.error({err:r},"Semantic search failed"),[]}}cosineSimilarity(e,t){if(e.length!==t.length)return 0;let s=0,r=0,n=0;for(let i=0;i<e.length;i++)s+=e[i]*t[i],r+=e[i]*e[i],n+=t[i]*t[i];let o=Math.sqrt(r)*Math.sqrt(n);return o===0?0:s/o}}});import{createHash as Op}from"node:crypto";var Mr,qi=_(()=>{"use strict";Mr=class{static{m(this,"DocumentProcessor")}docRepo;embeddingService;logger;constructor(e,t,s){this.docRepo=e,this.embeddingService=t,this.logger=s}async ingest(e,t,s,r){let n=await import("node:fs"),o=n.readFileSync(t),i=Op("sha256").update(o).digest("hex"),a=this.docRepo.findByContentHash(e,i);if(a&&a.chunkCount>0)return this.logger.info({documentId:a.id,filename:s,contentHash:i.slice(0,12)},"Document already ingested (duplicate)"),{documentId:a.id,chunkCount:a.chunkCount,existing:!0};a&&a.chunkCount===0&&(this.logger.info({documentId:a.id,filename:s},"Removing failed previous ingest attempt"),this.docRepo.deleteDocument(a.id));let c=await this.extractText(t,r),d=n.statSync(t),u=this.docRepo.createDocument(e,s,r,d.size,i),p=this.chunkText(c,500,50);for(let f=0;f<p.length;f++){let g;try{g=await this.embeddingService.embedAndStore(e,p[f],"document",`${u.id}:${f}`)}catch(h){this.logger.warn({documentId:u.id,chunkIndex:f,err:h},"Embedding failed for chunk, continuing")}this.docRepo.addChunk(u.id,f,p[f],g)}return this.docRepo.updateChunkCount(u.id,p.length),this.logger.info({documentId:u.id,filename:s,chunkCount:p.length},"Document ingested"),{documentId:u.id,chunkCount:p.length}}async extractText(e,t){let s=await import("node:fs");if(t==="application/pdf")try{let r=(await import("pdf-parse")).default,n=s.readFileSync(e);return(await r(n)).text}catch(r){throw this.logger.error({err:r},"PDF parsing failed"),new Error("Failed to parse PDF")}if(t==="application/vnd.openxmlformats-officedocument.wordprocessingml.document"||t==="application/msword")try{return(await(await import("mammoth")).extractRawText({path:e})).value}catch(r){throw this.logger.error({err:r},"DOCX parsing failed"),new Error("Failed to parse DOCX")}return s.readFileSync(e,"utf-8")}chunkText(e,t,s){let n=Math.round(t*3.5),o=Math.round(s*3.5),i=[],a=0;for(;a<e.length;){let c=a+n;if(c>=e.length){i.push(e.slice(a).trim());break}let d=Math.max(c-200,a),u=e.slice(d,c+200),p=u.lastIndexOf(`
868
891
 
869
- `);if(p>0)c=d+p;else{let f=u.lastIndexOf(". ");f>0&&(c=d+f+1)}let g=e.slice(a,c).trim();g&&i.push(g),a=c-o}return i.filter(c=>c.length>0)}}});var Dr,qi=_(()=>{"use strict";gs();Dr=class{static{m(this,"BackgroundTaskRunner")}skillRegistry;skillSandbox;taskRepo;adapters;users;logger;pollTimer;running=0;maxConcurrent=3;pollIntervalMs=5e3;taskTimeoutMs=5*6e4;constructor(e,t,s,r,n,o){this.skillRegistry=e,this.skillSandbox=t,this.taskRepo=s,this.adapters=r,this.users=n,this.logger=o}start(){this.pollTimer=setInterval(()=>this.poll(),this.pollIntervalMs),this.logger.info("Background task runner started")}stop(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0),this.logger.info("Background task runner stopped")}async poll(){if(!(this.running>=this.maxConcurrent))try{let e=this.maxConcurrent-this.running,t=this.taskRepo.getPending(e);for(let s of t)this.running++,this.runTask(s).finally(()=>{this.running--})}catch(e){this.logger.error({err:e},"Error polling for background tasks")}}async runTask(e){this.taskRepo.updateStatus(e.id,"running");try{let t=this.skillRegistry.get(e.skillName);if(!t){this.taskRepo.updateStatus(e.id,"failed",void 0,`Unknown skill: ${e.skillName}`);return}let s;try{s=JSON.parse(e.skillInput)}catch(c){this.logger.warn({taskId:e.id,err:c},"Malformed skill input JSON"),this.taskRepo.updateStatus(e.id,"failed",void 0,"Malformed skill input JSON");return}let{context:r}=Ye(this.users,{userId:e.userId,platform:e.platform,chatId:e.chatId,chatType:"dm"}),n=new Promise((c,d)=>setTimeout(()=>d(new Error("Background task timed out")),this.taskTimeoutMs)),o=await Promise.race([this.skillSandbox.execute(t,s,r),n]),i=JSON.stringify(o.data??o.display??o.error);this.taskRepo.updateStatus(e.id,o.success?"completed":"failed",i,o.error);let a=this.adapters.get(e.platform);if(a){let c=o.success?`\u2705 Background task completed: ${e.description}
892
+ `);if(p>0)c=d+p;else{let g=u.lastIndexOf(". ");g>0&&(c=d+g+1)}let f=e.slice(a,c).trim();f&&i.push(f),a=c-o}return i.filter(c=>c.length>0)}}});var Or,Wi=_(()=>{"use strict";ws();Or=class{static{m(this,"BackgroundTaskRunner")}skillRegistry;skillSandbox;taskRepo;adapters;users;logger;pollTimer;running=0;maxConcurrent=3;pollIntervalMs=5e3;taskTimeoutMs=5*6e4;constructor(e,t,s,r,n,o){this.skillRegistry=e,this.skillSandbox=t,this.taskRepo=s,this.adapters=r,this.users=n,this.logger=o}start(){this.pollTimer=setInterval(()=>this.poll(),this.pollIntervalMs),this.logger.info("Background task runner started")}stop(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0),this.logger.info("Background task runner stopped")}async poll(){if(!(this.running>=this.maxConcurrent))try{let e=this.maxConcurrent-this.running,t=this.taskRepo.getPending(e);for(let s of t)this.running++,this.runTask(s).finally(()=>{this.running--})}catch(e){this.logger.error({err:e},"Error polling for background tasks")}}async runTask(e){this.taskRepo.updateStatus(e.id,"running");try{let t=this.skillRegistry.get(e.skillName);if(!t){this.taskRepo.updateStatus(e.id,"failed",void 0,`Unknown skill: ${e.skillName}`);return}let s;try{s=JSON.parse(e.skillInput)}catch(c){this.logger.warn({taskId:e.id,err:c},"Malformed skill input JSON"),this.taskRepo.updateStatus(e.id,"failed",void 0,"Malformed skill input JSON");return}let{context:r}=Je(this.users,{userId:e.userId,platform:e.platform,chatId:e.chatId,chatType:"dm"}),n=new Promise((c,d)=>setTimeout(()=>d(new Error("Background task timed out")),this.taskTimeoutMs)),o=await Promise.race([this.skillSandbox.execute(t,s,r),n]),i=JSON.stringify(o.data??o.display??o.error);this.taskRepo.updateStatus(e.id,o.success?"completed":"failed",i,o.error);let a=this.adapters.get(e.platform);if(a){let c=o.success?`\u2705 Background task completed: ${e.description}
870
893
 
871
894
  Result: ${o.display??JSON.stringify(o.data)}`:`\u274C Background task failed: ${e.description}
872
895
 
873
896
  Error: ${o.error}`;await a.sendMessage(e.chatId,c)}}catch(t){let s=t instanceof Error?t.message:String(t);this.taskRepo.updateStatus(e.id,"failed",void 0,s),this.logger.error({taskId:e.id,err:t},"Background task failed");let r=this.adapters.get(e.platform);r&&await r.sendMessage(e.chatId,`\u274C Background task failed: ${e.description}
874
897
 
875
- Error: ${s}`)}}}});import Dp from"node:crypto";var Lr,zi=_(()=>{"use strict";gs();Lr=class{static{m(this,"ProactiveScheduler")}actionRepo;skillRegistry;skillSandbox;llm;adapters;users;logger;pipeline;formatter;conversationManager;tickTimer;tickIntervalMs=6e4;constructor(e,t,s,r,n,o,i,a,c,d){this.actionRepo=e,this.skillRegistry=t,this.skillSandbox=s,this.llm=r,this.adapters=n,this.users=o,this.logger=i,this.pipeline=a,this.formatter=c,this.conversationManager=d}start(){this.tickTimer=setInterval(()=>this.tick(),this.tickIntervalMs),this.logger.info("Proactive scheduler started")}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.logger.info("Proactive scheduler stopped")}async tick(){try{let e=this.actionRepo.getDue();for(let t of e)try{await this.executeAction(t)}catch(s){this.logger.error({err:s,actionId:t.id},"Failed to execute scheduled action")}}catch(e){this.logger.error({err:e},"Error during proactive scheduler tick")}}async executeAction(e){let t=new Date().toISOString();this.logger.info({actionId:e.id,name:e.name},"Executing scheduled action");let s,r="text";if(e.skillName&&this.skillRegistry.has(e.skillName)){let u=this.skillRegistry.get(e.skillName);try{let p;try{p=JSON.parse(e.skillInput)}catch{p={},this.logger.warn({actionId:e.id},"Invalid skillInput JSON, using empty input")}let{context:g}=Ye(this.users,{userId:e.userId,platform:e.platform,chatId:e.chatId,chatType:"dm"}),f=await this.skillSandbox.execute(u,p,g);if(f.success){let h=f.display??JSON.stringify(f.data);if(this.formatter){let y=this.formatter.format(h,e.platform);s=y.text,r=y.parseMode}else s=h}else s=`\u274C Scheduled action "${e.name}" failed: ${f.error}`}catch(p){let g=p instanceof Error?p.message:String(p);s=`\u274C Scheduled action "${e.name}" failed: ${g}`}}else if(e.promptTemplate&&this.pipeline)try{let u=`scheduled-${e.id}`,p=this.users.findById(e.userId),g=p?.platformUserId??e.userId,f={id:`scheduled-${Dp.randomUUID()}`,platform:e.platform,chatId:u,chatType:"dm",userId:g,userName:p?.username??g,text:e.promptTemplate+"\n\n[Format: Use only Markdown (**, *, ~~, `, ```). Do NOT use HTML tags like <b>, <i>, <code>. The system converts Markdown to platform-specific formatting automatically.]",timestamp:new Date,metadata:{scheduled:!0,skipHistory:!0,tier:"fast"}},h=await this.pipeline.process(f),y=this.formatter?this.formatter.format(h.text,e.platform):{text:h.text,parseMode:"text"};s=y.text,r=y.parseMode;let E=this.adapters.get(e.platform);if(E&&h.attachments)for(let $ of h.attachments)try{let v=$.mimeType.startsWith("image/"),P=$.mimeType==="audio/ogg"||$.mimeType==="audio/opus";v?await E.sendPhoto(e.chatId,$.data,$.fileName):P?await E.sendVoice(e.chatId,$.data):await E.sendFile(e.chatId,$.data,$.fileName)}catch(v){this.logger.warn({err:v,fileName:$.fileName,actionId:e.id},"Failed to send scheduled action attachment")}}catch(u){let p=u instanceof Error?u.message:String(u);this.logger.error({actionId:e.id,err:u},"Pipeline execution failed for scheduled action"),s=`Scheduled action "${e.name}" failed: ${p}`}else if(e.promptTemplate)try{s=(await this.llm.complete({messages:[{role:"user",content:e.promptTemplate}],maxTokens:1024,tier:"fast"})).content}catch(u){let p=u instanceof Error?u.message:String(u);this.logger.error({actionId:e.id,err:u},"LLM call failed for scheduled action"),s=`Scheduled action "${e.name}" failed: ${p}`}else this.logger.warn({actionId:e.id,skillName:e.skillName},"Unknown skill for scheduled action"),s=`Scheduled action "${e.name}" failed: unknown skill "${e.skillName}"`;let n=s.trim(),o=(e.promptTemplate??"").toLowerCase(),i=/nichts|silent|no\s*output|don't\s*respond|do\s*not\s*respond/i.test(o),a=/offline|down|fehler|error|warn|critical|alert|fail|nicht\s+(erreichbar|verf[uü]gbar|gefunden|online)|ausgefallen|stopped|unreachable|unavailable|⚠|❌|🚨|🔴/i.test(n)&&!/keine\s+(probleme|fehler|auff[aä]lligkeiten)/i.test(n);if(!n||n.length<3||i&&!a)this.logger.info({actionId:e.id,name:e.name},"Scheduled action produced no actionable output \u2014 skipping notification");else{let u=this.adapters.get(e.platform);if(u)try{if(await u.sendMessage(e.chatId,s,{parseMode:r!=="text"?r:void 0}),e.promptTemplate&&this.conversationManager){let p=this.conversationManager.getOrCreateConversation(e.platform,e.chatId,e.userId),g=`[Automated Scheduled Alert: ${e.name}]
876
- ${s}`;this.conversationManager.addMessage(p.id,"assistant",g)}}catch(p){this.logger.error({err:p,actionId:e.id},"Failed to send scheduled action result")}}if(e.promptTemplate&&this.conversationManager)try{let u=`scheduled-${e.id}`,p=this.conversationManager.getOrCreateConversation(e.platform,u,e.userId);this.conversationManager.pruneMessages(p.id,20)}catch{}let d=this.calculateNextRun(e);d?this.actionRepo.updateLastRun(e.id,t,d):(this.actionRepo.updateLastRun(e.id,t,null),this.actionRepo.setEnabled(e.id,!1))}calculateNextRun(e){let t=new Date;switch(e.scheduleType){case"interval":{let s=parseInt(e.scheduleValue,10);return isNaN(s)||s<=0?null:new Date(t.getTime()+s*6e4).toISOString()}case"once":return null;case"cron":return this.getNextCronDate(e.scheduleValue,t)?.toISOString()??null;default:return null}}getNextCronDate(e,t){let s=e.trim().split(/\s+/);if(s.length!==5)return null;let r=new Date(t.getTime()+6e4);r.setSeconds(0,0);for(let n=0;n<1440;n++){if(this.matchesCron(s,r))return r;r.setTime(r.getTime()+6e4)}return null}matchesCron(e,t){let s=t.getMinutes(),r=t.getHours(),n=t.getDate(),o=t.getMonth()+1,i=t.getDay();return this.matchCronField(e[0],s)&&this.matchCronField(e[1],r)&&this.matchCronField(e[2],n)&&this.matchCronField(e[3],o)&&this.matchCronField(e[4],i)}matchCronField(e,t){if(e==="*")return!0;let s=/^\*\/(\d+)$/.exec(e);if(s){let n=parseInt(s[1],10);return t%n===0}let r=parseInt(e,10);return isNaN(r)?!1:t===r}}});function od(l,e){let t=e.split("."),s=l;for(let r of t){if(s==null)return;if(typeof s=="object"){if(r==="length"&&Array.isArray(s)){s=s.length;continue}s=s[r]}else return}return s}function id(l,e,t,s){let r=Lp(l);if(s===null)return{triggered:!1,displayValue:r};switch(e){case"lt":case"gt":case"lte":case"gte":{let n=ys(l),o=ys(t);return n===null||o===null?{triggered:!1,displayValue:r}:{triggered:e==="lt"?n<o:e==="gt"?n>o:e==="lte"?n<=o:n>=o,displayValue:r}}case"eq":return{triggered:String(l)===String(t),displayValue:r};case"neq":return{triggered:String(l)!==String(t),displayValue:r};case"contains":return{triggered:String(l).includes(String(t??"")),displayValue:r};case"not_contains":return{triggered:!String(l).includes(String(t??"")),displayValue:r};case"changed":return{triggered:JSON.stringify(l)!==JSON.stringify(s),displayValue:r};case"increased":{let n=ys(l),o=ys(s);return n===null||o===null?{triggered:!1,displayValue:r}:{triggered:n>o,displayValue:r}}case"decreased":{let n=ys(l),o=ys(s);return n===null||o===null?{triggered:!1,displayValue:r}:{triggered:n<o,displayValue:r}}default:return{triggered:!1,displayValue:r}}}function ys(l){if(typeof l=="number")return l;let e=parseFloat(String(l));return isNaN(e)?null:e}function Lp(l){return l==null?"null":typeof l=="object"?JSON.stringify(l):String(l)}var ad=_(()=>{"use strict";m(od,"extractField");m(id,"evaluateCondition");m(ys,"toNumber");m(Lp,"formatValue")});var Np,On,cd=_(()=>{"use strict";ad();gs();Np={lt:"<",gt:">",lte:"<=",gte:">=",eq:"=",neq:"!=",contains:"contains",not_contains:"not contains",changed:"changed",increased:"increased",decreased:"decreased"},On=class{static{m(this,"WatchEngine")}watchRepo;skillRegistry;skillSandbox;adapters;users;logger;timer=null;tickIntervalMs=6e4;constructor(e,t,s,r,n,o){this.watchRepo=e,this.skillRegistry=t,this.skillSandbox=s,this.adapters=r,this.users=n,this.logger=o}start(){this.timer=setInterval(()=>this.tick(),this.tickIntervalMs),this.logger.info("Watch engine started")}stop(){this.timer&&(clearInterval(this.timer),this.timer=null),this.logger.info("Watch engine stopped")}async tick(){try{let e=this.watchRepo.getDue();for(let t of e)try{await this.checkWatch(t)}catch(s){this.logger.error({err:s,watchId:t.id},"Failed to check watch")}}catch(e){this.logger.error({err:e},"Error during watch engine tick")}}async checkWatch(e){let t=new Date().toISOString();this.logger.debug({watchId:e.id,name:e.name,skill:e.skillName},"Checking watch");let s=this.skillRegistry.get(e.skillName);if(!s){this.logger.warn({watchId:e.id,skillName:e.skillName},"Unknown skill for watch"),this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:e.lastValue});return}let{context:r}=Ye(this.users,{platform:e.platform,chatId:e.chatId,chatType:"dm"}),n=await this.skillSandbox.execute(s,e.skillParams,r);if(!n.success){this.logger.warn({watchId:e.id,error:n.error},"Watch skill execution failed"),this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:e.lastValue});return}let o=od(n.data,e.condition.field),i=e.lastValue!==null?JSON.parse(e.lastValue):null,{triggered:a,displayValue:c}=id(o,e.condition.operator,e.condition.value,i),d=JSON.stringify(o);if(a&&this.isCooldownExpired(e)){let u=e.messageTemplate??this.formatAlert(e,c),p=this.adapters.get(e.platform);if(p)try{await p.sendMessage(e.chatId,u),this.logger.info({watchId:e.id,name:e.name,value:c},"Watch alert sent")}catch(g){this.logger.error({err:g,watchId:e.id},"Failed to send watch alert")}this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:d,lastTriggeredAt:t})}else this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:d})}isCooldownExpired(e){if(!e.lastTriggeredAt)return!0;let t=e.cooldownMinutes*6e4;return Date.now()-new Date(e.lastTriggeredAt).getTime()>=t}formatAlert(e,t){let s=Np[e.condition.operator]??e.condition.operator,r=e.condition.value!=null?` ${s} ${e.condition.value}`:` ${s}`;return`\u26A1 Watch Alert: ${e.name}
898
+ Error: ${s}`)}}}});import Pp from"node:crypto";var Pr,Gi=_(()=>{"use strict";ws();Pr=class{static{m(this,"ProactiveScheduler")}actionRepo;skillRegistry;skillSandbox;llm;adapters;users;logger;pipeline;formatter;conversationManager;tickTimer;tickIntervalMs=6e4;constructor(e,t,s,r,n,o,i,a,c,d){this.actionRepo=e,this.skillRegistry=t,this.skillSandbox=s,this.llm=r,this.adapters=n,this.users=o,this.logger=i,this.pipeline=a,this.formatter=c,this.conversationManager=d}start(){this.tickTimer=setInterval(()=>this.tick(),this.tickIntervalMs),this.logger.info("Proactive scheduler started")}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.logger.info("Proactive scheduler stopped")}async tick(){try{let e=this.actionRepo.getDue();for(let t of e)try{await this.executeAction(t)}catch(s){this.logger.error({err:s,actionId:t.id},"Failed to execute scheduled action")}}catch(e){this.logger.error({err:e},"Error during proactive scheduler tick")}}async executeAction(e){let t=new Date().toISOString();this.logger.info({actionId:e.id,name:e.name},"Executing scheduled action");let s,r="text";if(e.skillName&&this.skillRegistry.has(e.skillName)){let u=this.skillRegistry.get(e.skillName);try{let p;try{p=JSON.parse(e.skillInput)}catch{p={},this.logger.warn({actionId:e.id},"Invalid skillInput JSON, using empty input")}let{context:f}=Je(this.users,{userId:e.userId,platform:e.platform,chatId:e.chatId,chatType:"dm"}),g=await this.skillSandbox.execute(u,p,f);if(g.success){let h=g.display??JSON.stringify(g.data);if(this.formatter){let y=this.formatter.format(h,e.platform);s=y.text,r=y.parseMode}else s=h}else s=`\u274C Scheduled action "${e.name}" failed: ${g.error}`}catch(p){let f=p instanceof Error?p.message:String(p);s=`\u274C Scheduled action "${e.name}" failed: ${f}`}}else if(e.promptTemplate&&this.pipeline)try{let u=`scheduled-${e.id}`,p=this.users.findById(e.userId),f=p?.platformUserId??e.userId,g={id:`scheduled-${Pp.randomUUID()}`,platform:e.platform,chatId:u,chatType:"dm",userId:f,userName:p?.username??f,text:e.promptTemplate+"\n\n[Format: Use only Markdown (**, *, ~~, `, ```). Do NOT use HTML tags like <b>, <i>, <code>. The system converts Markdown to platform-specific formatting automatically.]",timestamp:new Date,metadata:{scheduled:!0,skipHistory:!0,tier:"fast"}},h=await this.pipeline.process(g),y=this.formatter?this.formatter.format(h.text,e.platform):{text:h.text,parseMode:"text"};s=y.text,r=y.parseMode;let E=this.adapters.get(e.platform);if(E&&h.attachments)for(let $ of h.attachments)try{let v=$.mimeType.startsWith("image/"),j=$.mimeType==="audio/ogg"||$.mimeType==="audio/opus";v?await E.sendPhoto(e.chatId,$.data,$.fileName):j?await E.sendVoice(e.chatId,$.data):await E.sendFile(e.chatId,$.data,$.fileName)}catch(v){this.logger.warn({err:v,fileName:$.fileName,actionId:e.id},"Failed to send scheduled action attachment")}}catch(u){let p=u instanceof Error?u.message:String(u);this.logger.error({actionId:e.id,err:u},"Pipeline execution failed for scheduled action"),s=`Scheduled action "${e.name}" failed: ${p}`}else if(e.promptTemplate)try{s=(await this.llm.complete({messages:[{role:"user",content:e.promptTemplate}],maxTokens:1024,tier:"fast"})).content}catch(u){let p=u instanceof Error?u.message:String(u);this.logger.error({actionId:e.id,err:u},"LLM call failed for scheduled action"),s=`Scheduled action "${e.name}" failed: ${p}`}else this.logger.warn({actionId:e.id,skillName:e.skillName},"Unknown skill for scheduled action"),s=`Scheduled action "${e.name}" failed: unknown skill "${e.skillName}"`;let n=s.trim(),o=(e.promptTemplate??"").toLowerCase(),i=/nichts|silent|no\s*output|don't\s*respond|do\s*not\s*respond/i.test(o),a=/offline|down|fehler|error|warn|critical|alert|fail|nicht\s+(erreichbar|verf[uü]gbar|gefunden|online)|ausgefallen|stopped|unreachable|unavailable|⚠|❌|🚨|🔴/i.test(n)&&!/keine\s+(probleme|fehler|auff[aä]lligkeiten)/i.test(n);if(!n||n.length<3||i&&!a)this.logger.info({actionId:e.id,name:e.name},"Scheduled action produced no actionable output \u2014 skipping notification");else{let u=this.adapters.get(e.platform);if(u)try{if(await u.sendMessage(e.chatId,s,{parseMode:r!=="text"?r:void 0}),e.promptTemplate&&this.conversationManager){let p=this.conversationManager.getOrCreateConversation(e.platform,e.chatId,e.userId),f=`[Automated Scheduled Alert: ${e.name}]
899
+ ${s}`;this.conversationManager.addMessage(p.id,"assistant",f)}}catch(p){this.logger.error({err:p,actionId:e.id},"Failed to send scheduled action result")}}if(e.promptTemplate&&this.conversationManager)try{let u=`scheduled-${e.id}`,p=this.conversationManager.getOrCreateConversation(e.platform,u,e.userId);this.conversationManager.pruneMessages(p.id,20)}catch{}let d=this.calculateNextRun(e);d?this.actionRepo.updateLastRun(e.id,t,d):(this.actionRepo.updateLastRun(e.id,t,null),this.actionRepo.setEnabled(e.id,!1))}calculateNextRun(e){let t=new Date;switch(e.scheduleType){case"interval":{let s=parseInt(e.scheduleValue,10);return isNaN(s)||s<=0?null:new Date(t.getTime()+s*6e4).toISOString()}case"once":return null;case"cron":return this.getNextCronDate(e.scheduleValue,t)?.toISOString()??null;default:return null}}getNextCronDate(e,t){let s=e.trim().split(/\s+/);if(s.length!==5)return null;let r=new Date(t.getTime()+6e4);r.setSeconds(0,0);for(let n=0;n<1440;n++){if(this.matchesCron(s,r))return r;r.setTime(r.getTime()+6e4)}return null}matchesCron(e,t){let s=t.getMinutes(),r=t.getHours(),n=t.getDate(),o=t.getMonth()+1,i=t.getDay();return this.matchCronField(e[0],s)&&this.matchCronField(e[1],r)&&this.matchCronField(e[2],n)&&this.matchCronField(e[3],o)&&this.matchCronField(e[4],i)}matchCronField(e,t){if(e==="*")return!0;let s=/^\*\/(\d+)$/.exec(e);if(s){let n=parseInt(s[1],10);return t%n===0}let r=parseInt(e,10);return isNaN(r)?!1:t===r}}});function ld(l,e){let t=e.split("."),s=l;for(let r of t){if(s==null)return;if(typeof s=="object"){if(r==="length"&&Array.isArray(s)){s=s.length;continue}s=s[r]}else return}return s}function dd(l,e,t,s){let r=Up(l);if(s===null)return{triggered:!1,displayValue:r};switch(e){case"lt":case"gt":case"lte":case"gte":{let n=Ts(l),o=Ts(t);return n===null||o===null?{triggered:!1,displayValue:r}:{triggered:e==="lt"?n<o:e==="gt"?n>o:e==="lte"?n<=o:n>=o,displayValue:r}}case"eq":return{triggered:String(l)===String(t),displayValue:r};case"neq":return{triggered:String(l)!==String(t),displayValue:r};case"contains":return{triggered:String(l).includes(String(t??"")),displayValue:r};case"not_contains":return{triggered:!String(l).includes(String(t??"")),displayValue:r};case"changed":return{triggered:JSON.stringify(l)!==JSON.stringify(s),displayValue:r};case"increased":{let n=Ts(l),o=Ts(s);return n===null||o===null?{triggered:!1,displayValue:r}:{triggered:n>o,displayValue:r}}case"decreased":{let n=Ts(l),o=Ts(s);return n===null||o===null?{triggered:!1,displayValue:r}:{triggered:n<o,displayValue:r}}default:return{triggered:!1,displayValue:r}}}function Ts(l){if(typeof l=="number")return l;let e=parseFloat(String(l));return isNaN(e)?null:e}function Up(l){return l==null?"null":typeof l=="object"?JSON.stringify(l):String(l)}var ud=_(()=>{"use strict";m(ld,"extractField");m(dd,"evaluateCondition");m(Ts,"toNumber");m(Up,"formatValue")});var Fp,Un,md=_(()=>{"use strict";ud();ws();Fp={lt:"<",gt:">",lte:"<=",gte:">=",eq:"=",neq:"!=",contains:"contains",not_contains:"not contains",changed:"changed",increased:"increased",decreased:"decreased"},Un=class{static{m(this,"WatchEngine")}watchRepo;skillRegistry;skillSandbox;adapters;users;logger;timer=null;tickIntervalMs=6e4;constructor(e,t,s,r,n,o){this.watchRepo=e,this.skillRegistry=t,this.skillSandbox=s,this.adapters=r,this.users=n,this.logger=o}start(){this.timer=setInterval(()=>this.tick(),this.tickIntervalMs),this.logger.info("Watch engine started")}stop(){this.timer&&(clearInterval(this.timer),this.timer=null),this.logger.info("Watch engine stopped")}async tick(){try{let e=this.watchRepo.getDue();for(let t of e)try{await this.checkWatch(t)}catch(s){this.logger.error({err:s,watchId:t.id},"Failed to check watch")}}catch(e){this.logger.error({err:e},"Error during watch engine tick")}}async checkWatch(e){let t=new Date().toISOString();this.logger.debug({watchId:e.id,name:e.name,skill:e.skillName},"Checking watch");let s=this.skillRegistry.get(e.skillName);if(!s){this.logger.warn({watchId:e.id,skillName:e.skillName},"Unknown skill for watch"),this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:e.lastValue});return}let{context:r}=Je(this.users,{platform:e.platform,chatId:e.chatId,chatType:"dm"}),n=await this.skillSandbox.execute(s,e.skillParams,r);if(!n.success){this.logger.warn({watchId:e.id,error:n.error},"Watch skill execution failed"),this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:e.lastValue});return}let o=ld(n.data,e.condition.field),i=e.lastValue!==null?JSON.parse(e.lastValue):null,{triggered:a,displayValue:c}=dd(o,e.condition.operator,e.condition.value,i),d=JSON.stringify(o);if(a&&this.isCooldownExpired(e)){let u=e.messageTemplate??this.formatAlert(e,c),p=this.adapters.get(e.platform);if(p)try{await p.sendMessage(e.chatId,u),this.logger.info({watchId:e.id,name:e.name,value:c},"Watch alert sent")}catch(f){this.logger.error({err:f,watchId:e.id},"Failed to send watch alert")}this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:d,lastTriggeredAt:t})}else this.watchRepo.updateAfterCheck(e.id,{lastCheckedAt:t,lastValue:d})}isCooldownExpired(e){if(!e.lastTriggeredAt)return!0;let t=e.cooldownMinutes*6e4;return Date.now()-new Date(e.lastTriggeredAt).getTime()>=t}formatAlert(e,t){let s=Fp[e.condition.operator]??e.condition.operator,r=e.condition.value!=null?` ${s} ${e.condition.value}`:` ${s}`;return`\u26A1 Watch Alert: ${e.name}
877
900
  Bedingung erf\xFCllt: ${e.condition.field}${r}
878
- Aktueller Wert: ${t}`}}});function Wi(l){let e=l.trim();if(e.length<10)return{level:"low",matchedPatterns:[]};for(let s of Mp)if(s.test(e))return{level:"low",matchedPatterns:[]};let t=[];for(let s of Op)for(let r of s.patterns)if(r.test(e)){t.push(s.name);break}return t.length>0?{level:"high",matchedPatterns:t}:{level:"low",matchedPatterns:[]}}var Mp,Op,Gi=_(()=>{"use strict";Mp=[/^(what|where|when|who|why|how|which|can you|could you|do you|is there|are there|was |wer |wo |wann |warum |wie |welch|kannst du|könntest du|gibt es)/i,/^(hi|hey|hello|hallo|guten (morgen|tag|abend)|moin|servus|grüß|na\b|yo\b|sup\b|good (morning|evening|night))\b/i,/^(tell me|show me|find|search|help|explain|describe|translate|summarize|zeig|such|hilf|erklär|beschreib|übersetze|fass zusammen)/i,/^(ok|okay|yes|no|ja|nein|danke|thanks|thank you|sure|alright|klar|gut|cool|nice|great|perfect|genau|stimmt|richtig)\b/i],Op=[{name:"fact:name",patterns:[/\b(my name is|i'm called|i am called|ich heiße|ich bin der|ich bin die|mein name ist)\b/i]},{name:"fact:location",patterns:[/\b(i live in|i'm from|i am from|ich wohne|ich lebe in|ich komme aus|ich bin aus)\b/i]},{name:"fact:work",patterns:[/\b(i work at|i work as|i work for|ich arbeite|mein job|mein beruf|ich bin .{0,20}(entwickler|ingenieur|lehrer|arzt|designer))\b/i]},{name:"fact:birthday",patterns:[/\b(my birthday|i was born|ich bin geboren|mein geburtstag|ich habe am .{1,15} geburtstag)\b/i]},{name:"fact:age",patterns:[/\b(i'm \d+ years|i am \d+ years|ich bin \d+ (jahre|jahr))\b/i]},{name:"preference:like",patterns:[/\b(i love|i really like|i enjoy|ich liebe|ich mag|mir gefällt|mir gefallen)\b/i]},{name:"preference:dislike",patterns:[/\b(i hate|i dislike|i can't stand|ich hasse|ich mag .{0,5} nicht|ich kann .{0,10} nicht leiden)\b/i]},{name:"preference:prefer",patterns:[/\b(i prefer|i'd rather|ich bevorzuge|ich nehme lieber|mir ist .{0,10} lieber)\b/i]},{name:"preference:favorite",patterns:[/\b(my fav|my favorite|my favourite|mein lieblings|meine lieblings|am liebsten)\b/i]},{name:"correction",patterns:[/\b(actually|actually,? (i|my|it)|eigentlich|das stimmt nicht|nein,? ich|that's not right|that's wrong|nicht ganz)\b/i]},{name:"relationship:family",patterns:[/\b(my wife|my husband|my partner|meine frau|mein mann|mein partner|meine partnerin|my daughter|my son|mein sohn|meine tochter|my kids|meine kinder|my mother|my father|meine mutter|mein vater)\b/i]},{name:"relationship:professional",patterns:[/\b(my boss|my manager|my colleague|mein chef|mein vorgesetzter|mein kollege|meine kollegin)\b/i]},{name:"relationship:social",patterns:[/\b(my friend|my best friend|mein freund|meine freundin|mein bester freund)\b/i]},{name:"fact:language",patterns:[/\b(i speak|ich spreche|my native|meine muttersprache)\b/i]},{name:"fact:education",patterns:[/\b(i studied|i went to|ich habe .{0,10} studiert|ich war auf der|mein studium)\b/i]},{name:"fact:hobby",patterns:[/\b(my hobby|i play .{0,10}(guitar|piano|football|tennis|chess)|mein hobby|ich spiele|in my free time|in meiner freizeit)\b/i]},{name:"decision",patterns:[/\b(i've decided|i decided|i will always|ich habe (mich )?entschieden|ich werde immer)\b/i]},{name:"commitment",patterns:[/\b(i always|i never|ich mache immer|ich mache nie|i'm trying to|ich versuche)\b/i]}];m(Wi,"scanSignal")});var Pp,Up,Nr,Vi=_(()=>{"use strict";Pp=["fact","preference","correction","entity","decision","relationship","principle","commitment","moment","skill"],Up=`You are a memory extraction system. Analyze the user message and extract personal facts about the USER (not about what they're asking).
901
+ Aktueller Wert: ${t}`}}});function Vi(l){let e=l.trim();if(e.length<10)return{level:"low",matchedPatterns:[]};for(let s of jp)if(s.test(e))return{level:"low",matchedPatterns:[]};let t=[];for(let s of Bp)for(let r of s.patterns)if(r.test(e)){t.push(s.name);break}return t.length>0?{level:"high",matchedPatterns:t}:{level:"low",matchedPatterns:[]}}var jp,Bp,Ki=_(()=>{"use strict";jp=[/^(what|where|when|who|why|how|which|can you|could you|do you|is there|are there|was |wer |wo |wann |warum |wie |welch|kannst du|könntest du|gibt es)/i,/^(hi|hey|hello|hallo|guten (morgen|tag|abend)|moin|servus|grüß|na\b|yo\b|sup\b|good (morning|evening|night))\b/i,/^(tell me|show me|find|search|help|explain|describe|translate|summarize|zeig|such|hilf|erklär|beschreib|übersetze|fass zusammen)/i,/^(ok|okay|yes|no|ja|nein|danke|thanks|thank you|sure|alright|klar|gut|cool|nice|great|perfect|genau|stimmt|richtig)\b/i],Bp=[{name:"fact:name",patterns:[/\b(my name is|i'm called|i am called|ich heiße|ich bin der|ich bin die|mein name ist)\b/i]},{name:"fact:location",patterns:[/\b(i live in|i'm from|i am from|ich wohne|ich lebe in|ich komme aus|ich bin aus)\b/i]},{name:"fact:work",patterns:[/\b(i work at|i work as|i work for|ich arbeite|mein job|mein beruf|ich bin .{0,20}(entwickler|ingenieur|lehrer|arzt|designer))\b/i]},{name:"fact:birthday",patterns:[/\b(my birthday|i was born|ich bin geboren|mein geburtstag|ich habe am .{1,15} geburtstag)\b/i]},{name:"fact:age",patterns:[/\b(i'm \d+ years|i am \d+ years|ich bin \d+ (jahre|jahr))\b/i]},{name:"preference:like",patterns:[/\b(i love|i really like|i enjoy|ich liebe|ich mag|mir gefällt|mir gefallen)\b/i]},{name:"preference:dislike",patterns:[/\b(i hate|i dislike|i can't stand|ich hasse|ich mag .{0,5} nicht|ich kann .{0,10} nicht leiden)\b/i]},{name:"preference:prefer",patterns:[/\b(i prefer|i'd rather|ich bevorzuge|ich nehme lieber|mir ist .{0,10} lieber)\b/i]},{name:"preference:favorite",patterns:[/\b(my fav|my favorite|my favourite|mein lieblings|meine lieblings|am liebsten)\b/i]},{name:"correction",patterns:[/\b(actually|actually,? (i|my|it)|eigentlich|das stimmt nicht|nein,? ich|that's not right|that's wrong|nicht ganz)\b/i]},{name:"relationship:family",patterns:[/\b(my wife|my husband|my partner|meine frau|mein mann|mein partner|meine partnerin|my daughter|my son|mein sohn|meine tochter|my kids|meine kinder|my mother|my father|meine mutter|mein vater)\b/i]},{name:"relationship:professional",patterns:[/\b(my boss|my manager|my colleague|mein chef|mein vorgesetzter|mein kollege|meine kollegin)\b/i]},{name:"relationship:social",patterns:[/\b(my friend|my best friend|mein freund|meine freundin|mein bester freund)\b/i]},{name:"fact:language",patterns:[/\b(i speak|ich spreche|my native|meine muttersprache)\b/i]},{name:"fact:education",patterns:[/\b(i studied|i went to|ich habe .{0,10} studiert|ich war auf der|mein studium)\b/i]},{name:"fact:hobby",patterns:[/\b(my hobby|i play .{0,10}(guitar|piano|football|tennis|chess)|mein hobby|ich spiele|in my free time|in meiner freizeit)\b/i]},{name:"decision",patterns:[/\b(i've decided|i decided|i will always|ich habe (mich )?entschieden|ich werde immer)\b/i]},{name:"commitment",patterns:[/\b(i always|i never|ich mache immer|ich mache nie|i'm trying to|ich versuche)\b/i]}];m(Vi,"scanSignal")});var Hp,zp,Ur,Xi=_(()=>{"use strict";Hp=["fact","preference","correction","entity","decision","relationship","principle","commitment","moment","skill"],zp=`You are a memory extraction system. Analyze the user message and extract personal facts about the USER (not about what they're asking).
879
902
 
880
903
  Rules:
881
904
  - Only extract information the user STATES about themselves
@@ -895,68 +918,94 @@ Extract memories from this conversation:
895
918
  User: {USER_MESSAGE}
896
919
  Assistant: {ASSISTANT_RESPONSE}
897
920
 
898
- Return ONLY a valid JSON array, no explanation:`,Nr=class{static{m(this,"MemoryExtractor")}llm;memoryRepo;logger;embeddingService;minConfidence;constructor(e,t,s,r,n=.4){this.llm=e,this.memoryRepo=t,this.logger=s,this.embeddingService=r,this.minConfidence=n}async extract(e,t,s){try{let r=Up.replace("{USER_MESSAGE}",t).replace("{ASSISTANT_RESPONSE}",s),n=await this.llm.complete({messages:[{role:"user",content:r}],temperature:.1,tier:"fast",maxTokens:1024}),o=this.parseResponse(n.content);if(o.length===0)return 0;let i=0;for(let a of o)if(!(a.confidence<this.minConfidence))try{let c=this.memoryRepo.saveWithMetadata(e,a.key,a.value,a.category,a.type,a.confidence,"auto");this.embeddingService&&this.embeddingService.embedAndStore(e,`${a.key}: ${a.value}`,"memory",c.id).catch(d=>this.logger.debug({err:d},"Auto-embed failed")),i++,this.logger.info({key:a.key,type:a.type,confidence:a.confidence},"Auto-extracted memory saved")}catch(c){this.logger.warn({err:c,key:a.key},"Failed to save extracted memory")}return i}catch(r){return this.logger.error({err:r},"Memory extraction failed"),0}}parseResponse(e){try{let t=e.match(/\[[\s\S]*\]/);if(!t)return[];let s=JSON.parse(t[0]);return Array.isArray(s)?s.filter(r=>typeof r=="object"&&r!==null).map(r=>({key:String(r.key||""),value:String(r.value||""),type:this.validateType(String(r.type||"fact")),confidence:this.clampConfidence(Number(r.confidence)||.5),category:String(r.category||"general")})).filter(r=>r.key&&r.value):[]}catch{return this.logger.debug({content:e.slice(0,200)},"Failed to parse extraction response"),[]}}validateType(e){return Pp.includes(e)?e:"fact"}clampConfidence(e){return Math.max(0,Math.min(1,e))}}});var Mr,Ki=_(()=>{"use strict";Gi();Vi();Mr=class{static{m(this,"ActiveLearningService")}extractor;logger;minMessageLength;maxExtractionsPerMinute;extractionTimestamps=new Map;constructor(e){this.logger=e.logger,this.minMessageLength=e.minMessageLength??15,this.maxExtractionsPerMinute=e.maxExtractionsPerMinute??5,this.extractor=new Nr(e.llm,e.memoryRepo,this.logger,e.embeddingService,e.minConfidence??.4)}onMessageProcessed(e,t,s){if(!t||t.length<this.minMessageLength)return;let r=Wi(t);if(r.level==="low"){this.logger.debug({signal:"low"},"Skipping extraction \u2014 low signal");return}if(!this.checkRateLimit(e)){this.logger.debug({userId:e},"Skipping extraction \u2014 rate limit reached");return}this.logger.info({signal:r.level,patterns:r.matchedPatterns},"High signal detected, triggering extraction"),this.extractor.extract(e,t,s).then(n=>{n>0&&this.logger.info({userId:e,extractedCount:n},"Auto-extraction complete")}).catch(n=>{this.logger.error({err:n},"Auto-extraction failed")})}checkRateLimit(e){let t=Date.now(),s=t-6e4,r=this.extractionTimestamps.get(e);r||(r=[],this.extractionTimestamps.set(e,r));let n=r.filter(o=>o>s);return this.extractionTimestamps.set(e,n),n.length>=this.maxExtractionsPerMinute?!1:(n.push(t),!0)}}});var Fp,jp,Bp,Hp,Or,Xi=_(()=>{"use strict";Fp=Math.LN2,jp=.3,Bp=.7,Hp=3,Or=class{static{m(this,"MemoryRetriever")}memoryRepo;logger;embeddingService;constructor(e,t,s){this.memoryRepo=e,this.logger=t,this.embeddingService=s}async retrieve(e,t,s=15,r){let n=[e];if(r)for(let o of r)o!==e&&n.push(o);try{let o=new Set,i=[];for(let h of n)for(let y of this.memoryRepo.keywordSearch(h,t,30))o.has(y.id)||(o.add(y.id),i.push(y));let a=[],c=!1;if(this.embeddingService)try{let h=new Set;for(let y of n)for(let E of await this.embeddingService.semanticSearch(y,t,30))h.has(E.key)||(h.add(E.key),a.push(E));c=a.length>0}catch(h){this.logger.debug({err:h},"Semantic search failed, falling back to keyword-only")}let d=new Map,u=i.length;for(let h=0;h<i.length;h++){let y=i[h],E=u>0?1-h/u:0,$=c?jp:1,v=this.applyBoosts(E*$,y);d.set(y.key,{memory:{key:y.key,value:y.value,category:y.category,type:y.type,score:v},score:v}),this.memoryRepo.recordAccess(y.id)}if(c)for(let h of a){let y=h.score*Bp,E=d.get(h.key);if(E)E.score+=y,E.memory.score=E.score;else{let $=this.memoryRepo.recall(e,h.key),v=this.applyBoosts(y,$||void 0);d.set(h.key,{memory:{key:h.key,value:h.value,category:h.category,type:$?.type||"general",score:v},score:v}),$&&this.memoryRepo.recordAccess($.id)}}let p=Array.from(d.values()).sort((h,y)=>y.score-h.score),g=new Map,f=[];for(let{memory:h}of p){let y=g.get(h.type)||0;if(!(y>=Hp)&&(g.set(h.type,y+1),f.push(h),f.length>=s))break}return this.logger.debug({keywordCount:i.length,semanticCount:a.length,resultCount:f.length,hasSemanticSearch:c},"Hybrid memory retrieval complete"),f}catch(o){this.logger.error({err:o},"Memory retrieval failed");let i=new Set,a=[];for(let c of n)for(let d of this.memoryRepo.getRecentForPrompt(c,s))i.has(d.key)||(i.add(d.key),a.push({key:d.key,value:d.value,category:d.category,type:d.type,score:0}));return a.slice(0,s)}}applyBoosts(e,t){let s=e;if(t){s*=.5+.5*t.confidence;let r=new Date(t.updatedAt).getTime(),n=Date.now()-r,o=Math.exp(-Fp*n/2592e6);s*=o}return s}}});import{EventEmitter as qp}from"node:events";var pe,Je=_(()=>{"use strict";pe=class extends qp{static{m(this,"MessagingAdapter")}status="disconnected";async sendPhoto(e,t,s){}async sendFile(e,t,s,r){}async sendVoice(e,t,s){}endStream(e){}getStatus(){return this.status}splitText(e,t){if(e.length<=t)return[e];let s=[],r=e;for(;r.length>0;){if(r.length<=t){s.push(r);break}let n=-1,i=r.slice(0,t).lastIndexOf(`
899
-
900
- `);if(i>0&&(n=i),n<0){let c=r.slice(0,t).match(/.*[.!?]\s/s);c&&(n=c[0].length)}n<0&&(n=t),s.push(r.slice(0,n).trimEnd()),r=r.slice(n).trimStart()}return s}}});import{Bot as zp,InputFile as Yi}from"grammy";function ld(l){if(l==="markdown")return"MarkdownV2";if(l==="html")return"HTML"}var Pn,dd=_(()=>{"use strict";Je();m(ld,"mapParseMode");Pn=class extends pe{static{m(this,"TelegramAdapter")}platform="telegram";bot;constructor(e){super(),this.bot=new zp(e)}async connect(){this.status="connecting",this.bot.on("message:text",e=>{this.emit("message",this.normalizeMessage(e.message,e.message.text))}),this.bot.on("message:photo",async e=>{let t=e.message,r=(t.caption??"")||"[Photo]",n=t.photo[t.photo.length-1],o=await this.downloadAttachment(n.file_id,"image","image/jpeg"),i=this.normalizeMessage(t,r);i.attachments=o?[o]:void 0,this.emit("message",i)}),this.bot.on("message:voice",async e=>{let t=e.message,s=await this.downloadAttachment(t.voice.file_id,"audio",t.voice.mime_type??"audio/ogg"),r=this.normalizeMessage(t,"[Voice message]");r.attachments=s?[s]:void 0,this.emit("message",r)}),this.bot.on("message:audio",async e=>{let t=e.message,r=(t.caption??"")||`[Audio: ${t.audio.file_name??"audio"}]`,n=await this.downloadAttachment(t.audio.file_id,"audio",t.audio.mime_type??"audio/mpeg"),o=this.normalizeMessage(t,r);o.attachments=n?[n]:void 0,this.emit("message",o)}),this.bot.on("message:video",async e=>{let t=e.message,r=(t.caption??"")||"[Video]",n=await this.downloadAttachment(t.video.file_id,"video",t.video.mime_type??"video/mp4"),o=this.normalizeMessage(t,r);o.attachments=n?[n]:void 0,this.emit("message",o)}),this.bot.on("message:document",async e=>{let t=e.message,s=t.document,n=(t.caption??"")||`[Document: ${s.file_name??"file"}]`,o=await this.downloadAttachment(s.file_id,"document",s.mime_type??"application/octet-stream",s.file_name),i=this.normalizeMessage(t,n);i.attachments=o?[o]:void 0,this.emit("message",i)}),this.bot.on("message:video_note",async e=>{let t=e.message,s=await this.downloadAttachment(t.video_note.file_id,"video","video/mp4"),r=this.normalizeMessage(t,"[Video note]");r.attachments=s?[s]:void 0,this.emit("message",r)}),this.bot.on("message:sticker",e=>{let t=e.message,s=t.sticker.emoji??"\u{1F3F7}\uFE0F";this.emit("message",this.normalizeMessage(t,`[Sticker: ${s}]`))}),this.bot.catch(e=>{this.emit("error",e.error)}),this.bot.start({onStart:m(()=>{this.status="connected",this.emit("connected")},"onStart")})}async disconnect(){await this.bot.stop(),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,4096),n="";for(let o of r){let i=await this.bot.api.sendMessage(Number(e),o,{reply_to_message_id:s?.replyToMessageId?Number(s.replyToMessageId):void 0,parse_mode:ld(s?.parseMode)});n=String(i.message_id)}return n}async editMessage(e,t,s,r){await this.bot.api.editMessageText(Number(e),Number(t),s,{parse_mode:ld(r?.parseMode)})}async deleteMessage(e,t){await this.bot.api.deleteMessage(Number(e),Number(t))}async sendPhoto(e,t,s){let r=await this.bot.api.sendPhoto(Number(e),new Yi(t,"image.png"),{caption:s});return String(r.message_id)}async sendFile(e,t,s,r){let n=await this.bot.api.sendDocument(Number(e),new Yi(t,s),{caption:r});return String(n.message_id)}async sendVoice(e,t,s){let r=await this.bot.api.sendVoice(Number(e),new Yi(t,"voice.ogg"),{caption:s});return String(r.message_id)}normalizeMessage(e,t){return{id:String(e.message_id),platform:"telegram",chatId:String(e.chat.id),chatType:e.chat.type==="private"?"dm":"group",userId:String(e.from.id),userName:e.from.username??String(e.from.id),displayName:[e.from.first_name,e.from.last_name].filter(Boolean).join(" "),text:t,timestamp:new Date(e.date*1e3),replyToMessageId:e.reply_to_message?String(e.reply_to_message.message_id):void 0}}async downloadAttachment(e,t,s,r){try{let o=(await this.bot.api.getFile(e)).file_path;if(!o)return;let i=`https://api.telegram.org/file/bot${this.bot.token}/${o}`,a=await fetch(i);if(!a.ok)return;let c=Buffer.from(await a.arrayBuffer());return{type:t,mimeType:s,fileName:r??o.split("/").pop(),size:c.length,data:c}}catch(n){console.error("[telegram] Failed to download file",e,n);return}}}});import{Client as Wp,GatewayIntentBits as Un,Events as Ji}from"discord.js";var Fn,ud=_(()=>{"use strict";Je();Fn=class extends pe{static{m(this,"DiscordAdapter")}platform="discord";client=null;token;constructor(e){super(),this.token=e}async connect(){this.status="connecting",this.client=new Wp({intents:[Un.Guilds,Un.GuildMessages,Un.MessageContent,Un.DirectMessages]}),this.client.on(Ji.MessageCreate,async e=>{if(!e.author.bot)try{let t=await this.downloadAttachments(e),s=e.content||this.inferTextFromAttachments(t),r={id:e.id,platform:"discord",chatId:e.channelId,chatType:e.channel.isDMBased()?"dm":"group",userId:e.author.id,userName:e.author.username,displayName:e.author.displayName,text:s,timestamp:e.createdAt,replyToMessageId:e.reference?.messageId??void 0,attachments:t.length>0?t:void 0};this.emit("message",r)}catch(t){this.emit("error",t instanceof Error?t:new Error(String(t)))}}),this.client.on(Ji.ClientReady,()=>{this.status="connected",this.emit("connected")}),this.client.on(Ji.Error,e=>{this.emit("error",e)}),await this.client.login(this.token)}async disconnect(){this.client?.destroy(),this.client=null,this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){if(!this.client)throw new Error("Client is not connected");let r=await this.client.channels.fetch(e);if(!r?.isTextBased()||!("send"in r))throw new Error(`Channel ${e} is not a text channel`);let n=this.splitText(t,2e3),o="";for(let i=0;i<n.length;i++)i===0&&s?.replyToMessageId?o=(await(await r.messages.fetch(s.replyToMessageId)).reply(n[i])).id:o=(await r.send(n[i])).id;return o}async editMessage(e,t,s,r){if(!this.client)throw new Error("Client is not connected");let n=await this.client.channels.fetch(e);if(!n?.isTextBased()||!("messages"in n))throw new Error(`Channel ${e} is not a text channel`);await(await n.messages.fetch(t)).edit(s)}async deleteMessage(e,t){if(!this.client)throw new Error("Client is not connected");let s=await this.client.channels.fetch(e);if(!s?.isTextBased()||!("messages"in s))throw new Error(`Channel ${e} is not a text channel`);await(await s.messages.fetch(t)).delete()}async sendPhoto(e,t,s){if(!this.client)return;let r=await this.client.channels.fetch(e);return!r?.isTextBased()||!("send"in r)?void 0:(await r.send({content:s,files:[{attachment:t,name:"image.png"}]})).id}async sendFile(e,t,s,r){if(!this.client)return;let n=await this.client.channels.fetch(e);return!n?.isTextBased()||!("send"in n)?void 0:(await n.send({content:r,files:[{attachment:t,name:s}]})).id}async sendVoice(e,t,s){return this.sendFile(e,t,"voice.ogg",s)}async downloadAttachments(e){let t=[],s=e.attachments;if(!s||s.size===0)return t;for(let[,r]of s)try{let n=await fetch(r.url);if(!n.ok)continue;let o=await n.arrayBuffer(),i=Buffer.from(o),a=this.classifyContentType(r.contentType);t.push({type:a,url:r.url,mimeType:r.contentType??void 0,fileName:r.name??void 0,size:r.size??i.length,data:i})}catch(n){console.error("[discord] Failed to download attachment",r.url,n)}return t}classifyContentType(e){return e?e.startsWith("image/")?"image":e.startsWith("audio/")?"audio":e.startsWith("video/")?"video":"document":"other"}inferTextFromAttachments(e){if(e.length===0)return"";let t=e.map(s=>s.type);return t.includes("image")?"[Photo]":t.includes("audio")?"[Voice message]":t.includes("video")?"[Video]":t.includes("document")?"[Document]":"[File]"}}});var jn,md=_(()=>{"use strict";Je();jn=class extends pe{static{m(this,"MatrixAdapter")}platform="matrix";client;homeserverUrl;accessToken;botUserId;constructor(e,t,s){super(),this.homeserverUrl=e.replace(/\/+$/,""),this.accessToken=t,this.botUserId=s}async connect(){this.status="connecting";let{MatrixClient:e,SimpleFsStorageProvider:t,AutojoinRoomsMixin:s}=await import("matrix-bot-sdk"),r=new t("./data/matrix-storage");this.client=new e(this.homeserverUrl,this.accessToken,r),s.setupOnClient(this.client),this.client.on("room.message",async(n,o)=>{if(o.sender===this.botUserId)return;let i=o.content?.msgtype;if(i)try{let a=await this.normalizeEvent(n,o,i);a&&this.emit("message",a)}catch(a){this.emit("error",a instanceof Error?a:new Error(String(a)))}}),await this.client.start(),this.status="connected",this.emit("connected")}async disconnect(){this.client.stop(),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,32e3),n="";for(let o of r)s?.parseMode==="html"?n=await this.client.sendEvent(e,"m.room.message",{msgtype:"m.text",body:o.replace(/<[^>]*>/g,""),format:"org.matrix.custom.html",formatted_body:o}):n=await this.client.sendText(e,o);return n}async editMessage(e,t,s,r){let n=r?.parseMode==="html",o={msgtype:"m.text",body:"* "+(n?s.replace(/<[^>]*>/g,""):s),"m.new_content":{msgtype:"m.text",body:n?s.replace(/<[^>]*>/g,""):s,...n?{format:"org.matrix.custom.html",formatted_body:s}:{}},"m.relates_to":{rel_type:"m.replace",event_id:t}};await this.client.sendEvent(e,"m.room.message",o)}async deleteMessage(e,t){await this.client.redactEvent(e,t)}async sendPhoto(e,t,s){let r=await this.client.uploadContent(t,"image/png","image.png"),n={msgtype:"m.image",body:s??"image.png",url:r,info:{mimetype:"image/png",size:t.length}};return await this.client.sendEvent(e,"m.room.message",n)}async sendFile(e,t,s,r){let n=this.guessMimeType(s),o=await this.client.uploadContent(t,n,s),i={msgtype:"m.file",body:r??s,filename:s,url:o,info:{mimetype:n,size:t.length}};return await this.client.sendEvent(e,"m.room.message",i)}async normalizeEvent(e,t,s){let r;try{r=(await this.client.getUserProfile(t.sender))?.displayname??void 0}catch{}let n={id:t.event_id,platform:"matrix",chatId:e,chatType:"group",userId:t.sender,userName:t.sender.split(":")[0].slice(1),displayName:r,timestamp:new Date(t.origin_server_ts),replyToMessageId:t.content["m.relates_to"]?.["m.in_reply_to"]?.event_id};switch(s){case"m.text":return{...n,text:t.content.body};case"m.image":{let o=await this.downloadAttachment(t.content,"image");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Photo]",attachments:o?[o]:void 0}}case"m.audio":{let o=await this.downloadAttachment(t.content,"audio");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Voice message]",attachments:o?[o]:void 0}}case"m.video":{let o=await this.downloadAttachment(t.content,"video");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Video]",attachments:o?[o]:void 0}}case"m.file":{let o=await this.downloadAttachment(t.content,"document");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Document]",attachments:o?[o]:void 0}}default:return t.content.body?{...n,text:t.content.body}:void 0}}async downloadAttachment(e,t){let s=e.url;if(!s||!s.startsWith("mxc://"))return;let r=e.info??{},n=r.mimetype,o=r.size,i=e.filename??e.body??"file",a=s.slice(6),c=[`${this.homeserverUrl}/_matrix/client/v1/media/download/${a}`,`${this.homeserverUrl}/_matrix/media/v3/download/${a}`];for(let d of c)try{let u=await fetch(d,{headers:{Authorization:`Bearer ${this.accessToken}`}});if(u.status===404)continue;if(!u.ok){console.error(`[matrix] Download failed (${u.status})`,s,d);continue}let p=await u.arrayBuffer(),g=Buffer.from(p);return{type:t,mimeType:n,fileName:i,size:o??g.length,data:g}}catch(u){console.error("[matrix] Download error",s,d,u);continue}console.error("[matrix] All download endpoints failed for",s)}guessMimeType(e){let t=e.split(".").pop()?.toLowerCase();return{pdf:"application/pdf",txt:"text/plain",json:"application/json",csv:"text/csv",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",mp3:"audio/mpeg",ogg:"audio/ogg",mp4:"video/mp4",zip:"application/zip",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[t??""]??"application/octet-stream"}}});var Bn,pd=_(()=>{"use strict";Je();Bn=class extends pe{static{m(this,"WhatsAppAdapter")}platform="whatsapp";socket;downloadMedia;dataPath;reconnectAttempts=0;reconnectTimer;constructor(e){super(),this.dataPath=e}async connect(){this.status="connecting";let{makeWASocket:e,useMultiFileAuthState:t,DisconnectReason:s,downloadMediaMessage:r}=await import("@whiskeysockets/baileys");this.downloadMedia=r;let{state:n,saveCreds:o}=await t(this.dataPath);this.socket=e({auth:n,printQRInTerminal:!0}),this.socket.ev.on("creds.update",o),this.socket.ev.on("connection.update",i=>{if(i.connection==="open"&&(this.status="connected",this.reconnectAttempts=0,this.emit("connected")),i.connection==="close"){let c=i.lastDisconnect?.error?.output?.statusCode!==s.loggedOut;if(this.status="disconnected",this.emit("disconnected"),c){let d=Math.min(1e3*Math.pow(2,this.reconnectAttempts),6e4);this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>this.connect(),d)}}}),this.socket.ev.on("messages.upsert",({messages:i,type:a})=>{if(a==="notify")for(let c of i)c.message&&(c.key.fromMe||this.processMessage(c).catch(d=>{this.emit("error",d instanceof Error?d:new Error(String(d)))}))})}async disconnect(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0),this.reconnectAttempts=0,this.socket?.end(void 0),this.socket=void 0,this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,65e3),n="";for(let o=0;o<r.length;o++)n=(await this.socket.sendMessage(e,{text:r[o]},o===0&&s?.replyToMessageId?{quoted:{key:{remoteJid:e,id:s.replyToMessageId},message:{}}}:void 0))?.key?.id??"";return n}async editMessage(e,t,s,r){await this.socket.sendMessage(e,{text:s,edit:{remoteJid:e,id:t,fromMe:!0}})}async deleteMessage(e,t){await this.socket.sendMessage(e,{delete:{remoteJid:e,id:t,fromMe:!0}})}async sendPhoto(e,t,s){return(await this.socket.sendMessage(e,{image:t,caption:s}))?.key?.id}async sendFile(e,t,s,r){return(await this.socket.sendMessage(e,{document:t,fileName:s,caption:r,mimetype:this.guessMimeType(s)}))?.key?.id}async processMessage(e){let t=e.message,s=t.conversation??t.extendedTextMessage?.text??t.imageMessage?.caption??t.videoMessage?.caption??t.documentMessage?.caption??"",r=[],n=s;if(t.imageMessage){let i=await this.downloadMediaSafe(e);if(i){let a=t.imageMessage.mimetype??"image/jpeg";r.push({type:"image",mimeType:a,fileName:t.imageMessage.fileName??`image.${a.split("/")[1]??"jpeg"}`,size:t.imageMessage.fileLength??i.length,data:i})}n||(n="[Photo]")}else if(t.audioMessage){let i=await this.downloadMediaSafe(e);if(i){let a=t.audioMessage.mimetype??"audio/ogg";r.push({type:"audio",mimeType:a,fileName:t.audioMessage.fileName??`audio.${a.split("/")[1]??"ogg"}`,size:t.audioMessage.fileLength??i.length,data:i})}n||(n="[Voice message]")}else if(t.videoMessage){let i=await this.downloadMediaSafe(e);i&&r.push({type:"video",mimeType:t.videoMessage.mimetype??"video/mp4",size:t.videoMessage.fileLength??i.length,data:i}),n||(n="[Video]")}else if(t.documentMessage){let i=await this.downloadMediaSafe(e);i&&r.push({type:"document",mimeType:t.documentMessage.mimetype??"application/octet-stream",fileName:t.documentMessage.fileName??"document",size:t.documentMessage.fileLength??i.length,data:i}),n||(n="[Document]")}else if(t.stickerMessage&&!s)return;if(!n&&r.length===0)return;let o={id:e.key.id??"",platform:"whatsapp",chatId:e.key.remoteJid??"",chatType:e.key.remoteJid?.endsWith("@g.us")?"group":"dm",userId:e.key.participant??e.key.remoteJid??"",userName:e.pushName??e.key.participant??e.key.remoteJid??"",displayName:e.pushName??void 0,text:n,timestamp:new Date(e.messageTimestamp*1e3),replyToMessageId:t.extendedTextMessage?.contextInfo?.stanzaId??void 0,attachments:r.length>0?r:void 0};this.emit("message",o)}async downloadMediaSafe(e){try{if(!this.downloadMedia)return;let t=await this.downloadMedia(e,"buffer",{});return Buffer.isBuffer(t)?t:Buffer.from(t)}catch(t){console.error("[whatsapp] Failed to download media",t);return}}guessMimeType(e){let t=e.split(".").pop()?.toLowerCase();return{pdf:"application/pdf",txt:"text/plain",json:"application/json",csv:"text/csv",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",mp3:"audio/mpeg",ogg:"audio/ogg",mp4:"video/mp4",zip:"application/zip",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[t??""]??"application/octet-stream"}}});var Hn,hd=_(()=>{"use strict";Je();Hn=class extends pe{static{m(this,"SignalAdapter")}apiUrl;phoneNumber;platform="signal";pollingInterval;constructor(e,t){super(),this.apiUrl=e,this.phoneNumber=t}async connect(){this.status="connecting";let e=3,t;for(let s=0;s<=e;s++)try{let r=await fetch(`${this.apiUrl}/v1/about`);if(!r.ok)throw new Error(`Signal API not reachable: ${r.status}`);this.pollingInterval=setInterval(()=>{this.pollMessages().catch(n=>{this.emit("error",n instanceof Error?n:new Error(String(n)))})},2e3),this.status="connected",this.emit("connected");return}catch(r){if(t=r instanceof Error?r:new Error(String(r)),s<e){let n=1e3*Math.pow(2,s);await new Promise(o=>setTimeout(o,n))}}this.status="error",this.emit("error",t)}async disconnect(){this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=void 0),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,6e3),n="";for(let o of r){let i=e.startsWith("group."),a={message:o,number:this.phoneNumber};i?a.recipients=[e.replace("group.","")]:a.recipients=[e];let c=await fetch(`${this.apiUrl}/v2/send`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!c.ok)throw new Error(`Signal send failed: ${c.status} ${await c.text()}`);let d=await c.json();n=String(d.timestamp??Date.now())}return n}async editMessage(e,t,s,r){throw new Error("Signal does not support message editing")}async deleteMessage(e,t){let s={number:this.phoneNumber,recipients:[e],timestamp:Number(t)},r=await fetch(`${this.apiUrl}/v1/deleteMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!r.ok)throw new Error(`Signal delete failed: ${r.status} ${await r.text()}`)}async pollMessages(){let e=await fetch(`${this.apiUrl}/v1/receive/${this.phoneNumber}`);if(!e.ok)return;let t=await e.json();for(let s of t){let r=s.envelope?.dataMessage;if(!r||!r.message&&(!r.attachments||r.attachments.length===0))continue;let n=s.envelope,o=r.groupInfo?.groupId?`group.${r.groupInfo.groupId}`:n.sourceNumber??n.source??"",i=[];if(r.attachments)for(let d of r.attachments){let u=await this.downloadAttachment(d);u&&i.push(u)}let a=r.message||this.inferTextFromAttachments(i)||"";if(!a&&i.length===0)continue;let c={id:String(r.timestamp??Date.now()),platform:"signal",chatId:o,chatType:r.groupInfo?"group":"dm",userId:n.sourceNumber??n.source??"",userName:n.sourceName??n.sourceNumber??n.source??"",displayName:n.sourceName,text:a,timestamp:new Date(r.timestamp??Date.now()),attachments:i.length>0?i:void 0};this.emit("message",c)}}async downloadAttachment(e){if(e.id)try{let t=await fetch(`${this.apiUrl}/v1/attachments/${e.id}`);if(!t.ok)return;let s=await t.arrayBuffer(),r=Buffer.from(s);return{type:this.classifyContentType(e.contentType),mimeType:e.contentType??void 0,fileName:e.filename??void 0,size:e.size??r.length,data:r}}catch(t){console.error("[signal] Failed to download attachment",e.id,t);return}}classifyContentType(e){return e?e.startsWith("image/")?"image":e.startsWith("audio/")?"audio":e.startsWith("video/")?"video":"document":"other"}inferTextFromAttachments(e){if(e.length===0)return"";let t=e.map(s=>s.type);return t.includes("image")?"[Photo]":t.includes("audio")?"[Voice message]":t.includes("video")?"[Video]":t.includes("document")?"[Document]":"[File]"}}});import Zi from"node:readline";var qn,fd=_(()=>{"use strict";Je();qn=class extends pe{static{m(this,"CLIAdapter")}platform="cli";rl;messageCounter=0;async connect(){this.status="connecting",this.rl=Zi.createInterface({input:process.stdin,output:process.stdout,prompt:"You: "}),console.log(`
921
+ Return ONLY a valid JSON array, no explanation:`,Ur=class{static{m(this,"MemoryExtractor")}llm;memoryRepo;logger;embeddingService;minConfidence;constructor(e,t,s,r,n=.4){this.llm=e,this.memoryRepo=t,this.logger=s,this.embeddingService=r,this.minConfidence=n}async extract(e,t,s){try{let r=zp.replace("{USER_MESSAGE}",t).replace("{ASSISTANT_RESPONSE}",s),n=await this.llm.complete({messages:[{role:"user",content:r}],temperature:.1,tier:"fast",maxTokens:1024}),o=this.parseResponse(n.content);if(o.length===0)return 0;let i=0;for(let a of o)if(!(a.confidence<this.minConfidence))try{let c=this.memoryRepo.saveWithMetadata(e,a.key,a.value,a.category,a.type,a.confidence,"auto");this.embeddingService&&this.embeddingService.embedAndStore(e,`${a.key}: ${a.value}`,"memory",c.id).catch(d=>this.logger.debug({err:d},"Auto-embed failed")),i++,this.logger.info({key:a.key,type:a.type,confidence:a.confidence},"Auto-extracted memory saved")}catch(c){this.logger.warn({err:c,key:a.key},"Failed to save extracted memory")}return i}catch(r){return this.logger.error({err:r},"Memory extraction failed"),0}}parseResponse(e){try{let t=e.match(/\[[\s\S]*\]/);if(!t)return[];let s=JSON.parse(t[0]);return Array.isArray(s)?s.filter(r=>typeof r=="object"&&r!==null).map(r=>({key:String(r.key||""),value:String(r.value||""),type:this.validateType(String(r.type||"fact")),confidence:this.clampConfidence(Number(r.confidence)||.5),category:String(r.category||"general")})).filter(r=>r.key&&r.value):[]}catch{return this.logger.debug({content:e.slice(0,200)},"Failed to parse extraction response"),[]}}validateType(e){return Hp.includes(e)?e:"fact"}clampConfidence(e){return Math.max(0,Math.min(1,e))}}});var Fr,Yi=_(()=>{"use strict";Ki();Xi();Fr=class{static{m(this,"ActiveLearningService")}extractor;logger;minMessageLength;maxExtractionsPerMinute;extractionTimestamps=new Map;constructor(e){this.logger=e.logger,this.minMessageLength=e.minMessageLength??15,this.maxExtractionsPerMinute=e.maxExtractionsPerMinute??5,this.extractor=new Ur(e.llm,e.memoryRepo,this.logger,e.embeddingService,e.minConfidence??.4)}onMessageProcessed(e,t,s){if(!t||t.length<this.minMessageLength)return;let r=Vi(t);if(r.level==="low"){this.logger.debug({signal:"low"},"Skipping extraction \u2014 low signal");return}if(!this.checkRateLimit(e)){this.logger.debug({userId:e},"Skipping extraction \u2014 rate limit reached");return}this.logger.info({signal:r.level,patterns:r.matchedPatterns},"High signal detected, triggering extraction"),this.extractor.extract(e,t,s).then(n=>{n>0&&this.logger.info({userId:e,extractedCount:n},"Auto-extraction complete")}).catch(n=>{this.logger.error({err:n},"Auto-extraction failed")})}checkRateLimit(e){let t=Date.now(),s=t-6e4,r=this.extractionTimestamps.get(e);r||(r=[],this.extractionTimestamps.set(e,r));let n=r.filter(o=>o>s);return this.extractionTimestamps.set(e,n),n.length>=this.maxExtractionsPerMinute?!1:(n.push(t),!0)}}});var qp,Wp,Gp,Vp,jr,Ji=_(()=>{"use strict";qp=Math.LN2,Wp=.3,Gp=.7,Vp=3,jr=class{static{m(this,"MemoryRetriever")}memoryRepo;logger;embeddingService;constructor(e,t,s){this.memoryRepo=e,this.logger=t,this.embeddingService=s}async retrieve(e,t,s=15,r){let n=[e];if(r)for(let o of r)o!==e&&n.push(o);try{let o=new Set,i=[];for(let h of n)for(let y of this.memoryRepo.keywordSearch(h,t,30))o.has(y.id)||(o.add(y.id),i.push(y));let a=[],c=!1;if(this.embeddingService)try{let h=new Set;for(let y of n)for(let E of await this.embeddingService.semanticSearch(y,t,30))h.has(E.key)||(h.add(E.key),a.push(E));c=a.length>0}catch(h){this.logger.debug({err:h},"Semantic search failed, falling back to keyword-only")}let d=new Map,u=i.length;for(let h=0;h<i.length;h++){let y=i[h],E=u>0?1-h/u:0,$=c?Wp:1,v=this.applyBoosts(E*$,y);d.set(y.key,{memory:{key:y.key,value:y.value,category:y.category,type:y.type,score:v},score:v}),this.memoryRepo.recordAccess(y.id)}if(c)for(let h of a){let y=h.score*Gp,E=d.get(h.key);if(E)E.score+=y,E.memory.score=E.score;else{let $=this.memoryRepo.recall(e,h.key),v=this.applyBoosts(y,$||void 0);d.set(h.key,{memory:{key:h.key,value:h.value,category:h.category,type:$?.type||"general",score:v},score:v}),$&&this.memoryRepo.recordAccess($.id)}}let p=Array.from(d.values()).sort((h,y)=>y.score-h.score),f=new Map,g=[];for(let{memory:h}of p){let y=f.get(h.type)||0;if(!(y>=Vp)&&(f.set(h.type,y+1),g.push(h),g.length>=s))break}return this.logger.debug({keywordCount:i.length,semanticCount:a.length,resultCount:g.length,hasSemanticSearch:c},"Hybrid memory retrieval complete"),g}catch(o){this.logger.error({err:o},"Memory retrieval failed");let i=new Set,a=[];for(let c of n)for(let d of this.memoryRepo.getRecentForPrompt(c,s))i.has(d.key)||(i.add(d.key),a.push({key:d.key,value:d.value,category:d.category,type:d.type,score:0}));return a.slice(0,s)}}applyBoosts(e,t){let s=e;if(t){s*=.5+.5*t.confidence;let r=new Date(t.updatedAt).getTime(),n=Date.now()-r,o=Math.exp(-qp*n/2592e6);s*=o}return s}}});var Br,Zi=_(()=>{"use strict";Br=class{static{m(this,"ConversationSummarizer")}llm;summaryRepo;logger;constructor(e,t,s){this.llm=e,this.summaryRepo=t,this.logger=s}getSummary(e){return this.summaryRepo.get(e)}onMessageProcessed(e,t,s,r,n){let o=this.summaryRepo.get(e);!o&&t<6||o&&t-o.messageCount<3||this.updateSummary(e,t,s,r,n,o).catch(i=>this.logger.warn({err:i,conversationId:e},"Failed to update conversation summary"))}async updateSummary(e,t,s,r,n,o){let i=this.buildSummaryPrompt(o?.summary,n,s,r),c=(await this.llm.complete({messages:[{role:"user",content:i}],temperature:.1,tier:"fast",maxTokens:512})).content?.trim();if(!c||c.length<10){this.logger.debug({conversationId:e},"Summary response too short, skipping upsert");return}this.summaryRepo.upsert({conversationId:e,summary:c,messageCount:t,lastUserMessage:s.slice(0,500),lastAssistantMessage:r.slice(0,500),updatedAt:new Date().toISOString()}),this.logger.debug({conversationId:e,messageCount:t,summaryLength:c.length},"Conversation summary updated")}buildSummaryPrompt(e,t,s,r){let n=`Du bist ein Zusammenfassungs-Assistent. Erstelle eine strukturierte Zusammenfassung des Gespr\xE4chsverlaufs.
922
+ `;if(e&&(n+=`
923
+ ## Bisherige Zusammenfassung
924
+ ${e}
925
+ `),t.length>0){n+=`
926
+ ## Letzte Nachrichten
927
+ `;for(let o of t){let i=o.role==="user"?"User":"Assistant";n+=`${i}: ${o.content.slice(0,300)}
928
+ `}}return n+=`
929
+ User: ${s.slice(0,500)}
930
+ `,n+=`Assistant: ${r.slice(0,500)}
931
+ `,n+=`
932
+ ## Aufgabe
933
+ ${e?"Aktualisiere die Zusammenfassung mit den neuen Nachrichten.":"Erstelle eine neue Zusammenfassung des Gespr\xE4chs."}
934
+ `,n+=`
935
+ Antworte NUR mit der Zusammenfassung in diesem Format:
936
+
937
+ **Ziel:** [Was der User erreichen m\xF6chte]
938
+ **Thema:** [Aktives Thema / Arbeitsbereich]
939
+ **Fakten:** [Wichtige technische Fakten \u2014 Stichpunkte]
940
+ **Entscheidungen:** [Getroffene Entscheidungen \u2014 Stichpunkte]
941
+ **Offen:** [Offene Fragen oder n\xE4chste Schritte \u2014 Stichpunkte]
942
+
943
+ Regeln:
944
+ - Maximal 120 W\xF6rter
945
+ - Nur relevante Fakten, kein Smalltalk
946
+ - Leere Punkte: "\u2014"
947
+ - Sprache: Deutsch (oder Sprache des Gespr\xE4chs)`,n}}});import{EventEmitter as Kp}from"node:events";var ue,Ze=_(()=>{"use strict";ue=class extends Kp{static{m(this,"MessagingAdapter")}status="disconnected";async sendPhoto(e,t,s){}async sendFile(e,t,s,r){}async sendVoice(e,t,s){}endStream(e){}getStatus(){return this.status}splitText(e,t){if(e.length<=t)return[e];let s=[],r=e;for(;r.length>0;){if(r.length<=t){s.push(r);break}let n=-1,i=r.slice(0,t).lastIndexOf(`
948
+
949
+ `);if(i>0&&(n=i),n<0){let c=r.slice(0,t).match(/.*[.!?]\s/s);c&&(n=c[0].length)}n<0&&(n=t),s.push(r.slice(0,n).trimEnd()),r=r.slice(n).trimStart()}return s}}});import{Bot as Xp,InputFile as Qi}from"grammy";function pd(l){if(l==="markdown")return"MarkdownV2";if(l==="html")return"HTML"}var Fn,hd=_(()=>{"use strict";Ze();m(pd,"mapParseMode");Fn=class extends ue{static{m(this,"TelegramAdapter")}platform="telegram";bot;constructor(e){super(),this.bot=new Xp(e)}async connect(){this.status="connecting",this.bot.on("message:text",e=>{this.emit("message",this.normalizeMessage(e.message,e.message.text))}),this.bot.on("message:photo",async e=>{let t=e.message,r=(t.caption??"")||"[Photo]",n=t.photo[t.photo.length-1],o=await this.downloadAttachment(n.file_id,"image","image/jpeg"),i=this.normalizeMessage(t,r);i.attachments=o?[o]:void 0,this.emit("message",i)}),this.bot.on("message:voice",async e=>{let t=e.message,s=await this.downloadAttachment(t.voice.file_id,"audio",t.voice.mime_type??"audio/ogg"),r=this.normalizeMessage(t,"[Voice message]");r.attachments=s?[s]:void 0,this.emit("message",r)}),this.bot.on("message:audio",async e=>{let t=e.message,r=(t.caption??"")||`[Audio: ${t.audio.file_name??"audio"}]`,n=await this.downloadAttachment(t.audio.file_id,"audio",t.audio.mime_type??"audio/mpeg"),o=this.normalizeMessage(t,r);o.attachments=n?[n]:void 0,this.emit("message",o)}),this.bot.on("message:video",async e=>{let t=e.message,r=(t.caption??"")||"[Video]",n=await this.downloadAttachment(t.video.file_id,"video",t.video.mime_type??"video/mp4"),o=this.normalizeMessage(t,r);o.attachments=n?[n]:void 0,this.emit("message",o)}),this.bot.on("message:document",async e=>{let t=e.message,s=t.document,n=(t.caption??"")||`[Document: ${s.file_name??"file"}]`,o=await this.downloadAttachment(s.file_id,"document",s.mime_type??"application/octet-stream",s.file_name),i=this.normalizeMessage(t,n);i.attachments=o?[o]:void 0,this.emit("message",i)}),this.bot.on("message:video_note",async e=>{let t=e.message,s=await this.downloadAttachment(t.video_note.file_id,"video","video/mp4"),r=this.normalizeMessage(t,"[Video note]");r.attachments=s?[s]:void 0,this.emit("message",r)}),this.bot.on("message:sticker",e=>{let t=e.message,s=t.sticker.emoji??"\u{1F3F7}\uFE0F";this.emit("message",this.normalizeMessage(t,`[Sticker: ${s}]`))}),this.bot.catch(e=>{this.emit("error",e.error)}),this.bot.start({onStart:m(()=>{this.status="connected",this.emit("connected")},"onStart")})}async disconnect(){await this.bot.stop(),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,4096),n="";for(let o of r){let i=await this.bot.api.sendMessage(Number(e),o,{reply_to_message_id:s?.replyToMessageId?Number(s.replyToMessageId):void 0,parse_mode:pd(s?.parseMode)});n=String(i.message_id)}return n}async editMessage(e,t,s,r){await this.bot.api.editMessageText(Number(e),Number(t),s,{parse_mode:pd(r?.parseMode)})}async deleteMessage(e,t){await this.bot.api.deleteMessage(Number(e),Number(t))}async sendPhoto(e,t,s){let r=await this.bot.api.sendPhoto(Number(e),new Qi(t,"image.png"),{caption:s});return String(r.message_id)}async sendFile(e,t,s,r){let n=await this.bot.api.sendDocument(Number(e),new Qi(t,s),{caption:r});return String(n.message_id)}async sendVoice(e,t,s){let r=await this.bot.api.sendVoice(Number(e),new Qi(t,"voice.ogg"),{caption:s});return String(r.message_id)}normalizeMessage(e,t){return{id:String(e.message_id),platform:"telegram",chatId:String(e.chat.id),chatType:e.chat.type==="private"?"dm":"group",userId:String(e.from.id),userName:e.from.username??String(e.from.id),displayName:[e.from.first_name,e.from.last_name].filter(Boolean).join(" "),text:t,timestamp:new Date(e.date*1e3),replyToMessageId:e.reply_to_message?String(e.reply_to_message.message_id):void 0}}async downloadAttachment(e,t,s,r){try{let o=(await this.bot.api.getFile(e)).file_path;if(!o)return;let i=`https://api.telegram.org/file/bot${this.bot.token}/${o}`,a=await fetch(i);if(!a.ok)return;let c=Buffer.from(await a.arrayBuffer());return{type:t,mimeType:s,fileName:r??o.split("/").pop(),size:c.length,data:c}}catch(n){console.error("[telegram] Failed to download file",e,n);return}}}});import{Client as Yp,GatewayIntentBits as jn,Events as ea}from"discord.js";var Bn,fd=_(()=>{"use strict";Ze();Bn=class extends ue{static{m(this,"DiscordAdapter")}platform="discord";client=null;token;constructor(e){super(),this.token=e}async connect(){this.status="connecting",this.client=new Yp({intents:[jn.Guilds,jn.GuildMessages,jn.MessageContent,jn.DirectMessages]}),this.client.on(ea.MessageCreate,async e=>{if(!e.author.bot)try{let t=await this.downloadAttachments(e),s=e.content||this.inferTextFromAttachments(t),r={id:e.id,platform:"discord",chatId:e.channelId,chatType:e.channel.isDMBased()?"dm":"group",userId:e.author.id,userName:e.author.username,displayName:e.author.displayName,text:s,timestamp:e.createdAt,replyToMessageId:e.reference?.messageId??void 0,attachments:t.length>0?t:void 0};this.emit("message",r)}catch(t){this.emit("error",t instanceof Error?t:new Error(String(t)))}}),this.client.on(ea.ClientReady,()=>{this.status="connected",this.emit("connected")}),this.client.on(ea.Error,e=>{this.emit("error",e)}),await this.client.login(this.token)}async disconnect(){this.client?.destroy(),this.client=null,this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){if(!this.client)throw new Error("Client is not connected");let r=await this.client.channels.fetch(e);if(!r?.isTextBased()||!("send"in r))throw new Error(`Channel ${e} is not a text channel`);let n=this.splitText(t,2e3),o="";for(let i=0;i<n.length;i++)i===0&&s?.replyToMessageId?o=(await(await r.messages.fetch(s.replyToMessageId)).reply(n[i])).id:o=(await r.send(n[i])).id;return o}async editMessage(e,t,s,r){if(!this.client)throw new Error("Client is not connected");let n=await this.client.channels.fetch(e);if(!n?.isTextBased()||!("messages"in n))throw new Error(`Channel ${e} is not a text channel`);await(await n.messages.fetch(t)).edit(s)}async deleteMessage(e,t){if(!this.client)throw new Error("Client is not connected");let s=await this.client.channels.fetch(e);if(!s?.isTextBased()||!("messages"in s))throw new Error(`Channel ${e} is not a text channel`);await(await s.messages.fetch(t)).delete()}async sendPhoto(e,t,s){if(!this.client)return;let r=await this.client.channels.fetch(e);return!r?.isTextBased()||!("send"in r)?void 0:(await r.send({content:s,files:[{attachment:t,name:"image.png"}]})).id}async sendFile(e,t,s,r){if(!this.client)return;let n=await this.client.channels.fetch(e);return!n?.isTextBased()||!("send"in n)?void 0:(await n.send({content:r,files:[{attachment:t,name:s}]})).id}async sendVoice(e,t,s){return this.sendFile(e,t,"voice.ogg",s)}async downloadAttachments(e){let t=[],s=e.attachments;if(!s||s.size===0)return t;for(let[,r]of s)try{let n=await fetch(r.url);if(!n.ok)continue;let o=await n.arrayBuffer(),i=Buffer.from(o),a=this.classifyContentType(r.contentType);t.push({type:a,url:r.url,mimeType:r.contentType??void 0,fileName:r.name??void 0,size:r.size??i.length,data:i})}catch(n){console.error("[discord] Failed to download attachment",r.url,n)}return t}classifyContentType(e){return e?e.startsWith("image/")?"image":e.startsWith("audio/")?"audio":e.startsWith("video/")?"video":"document":"other"}inferTextFromAttachments(e){if(e.length===0)return"";let t=e.map(s=>s.type);return t.includes("image")?"[Photo]":t.includes("audio")?"[Voice message]":t.includes("video")?"[Video]":t.includes("document")?"[Document]":"[File]"}}});var Hn,gd=_(()=>{"use strict";Ze();Hn=class extends ue{static{m(this,"MatrixAdapter")}platform="matrix";client;homeserverUrl;accessToken;botUserId;constructor(e,t,s){super(),this.homeserverUrl=e.replace(/\/+$/,""),this.accessToken=t,this.botUserId=s}async connect(){this.status="connecting";let{MatrixClient:e,SimpleFsStorageProvider:t,AutojoinRoomsMixin:s}=await import("matrix-bot-sdk"),r=new t("./data/matrix-storage");this.client=new e(this.homeserverUrl,this.accessToken,r),s.setupOnClient(this.client),this.client.on("room.message",async(n,o)=>{if(o.sender===this.botUserId)return;let i=o.content?.msgtype;if(i)try{let a=await this.normalizeEvent(n,o,i);a&&this.emit("message",a)}catch(a){this.emit("error",a instanceof Error?a:new Error(String(a)))}}),await this.client.start(),this.status="connected",this.emit("connected")}async disconnect(){this.client.stop(),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,32e3),n="";for(let o of r)s?.parseMode==="html"?n=await this.client.sendEvent(e,"m.room.message",{msgtype:"m.text",body:o.replace(/<[^>]*>/g,""),format:"org.matrix.custom.html",formatted_body:o}):n=await this.client.sendText(e,o);return n}async editMessage(e,t,s,r){let n=r?.parseMode==="html",o={msgtype:"m.text",body:"* "+(n?s.replace(/<[^>]*>/g,""):s),"m.new_content":{msgtype:"m.text",body:n?s.replace(/<[^>]*>/g,""):s,...n?{format:"org.matrix.custom.html",formatted_body:s}:{}},"m.relates_to":{rel_type:"m.replace",event_id:t}};await this.client.sendEvent(e,"m.room.message",o)}async deleteMessage(e,t){await this.client.redactEvent(e,t)}async sendPhoto(e,t,s){let r=await this.client.uploadContent(t,"image/png","image.png"),n={msgtype:"m.image",body:s??"image.png",url:r,info:{mimetype:"image/png",size:t.length}};return await this.client.sendEvent(e,"m.room.message",n)}async sendFile(e,t,s,r){let n=this.guessMimeType(s),o=await this.client.uploadContent(t,n,s),i={msgtype:"m.file",body:r??s,filename:s,url:o,info:{mimetype:n,size:t.length}};return await this.client.sendEvent(e,"m.room.message",i)}async normalizeEvent(e,t,s){let r;try{r=(await this.client.getUserProfile(t.sender))?.displayname??void 0}catch{}let n={id:t.event_id,platform:"matrix",chatId:e,chatType:"group",userId:t.sender,userName:t.sender.split(":")[0].slice(1),displayName:r,timestamp:new Date(t.origin_server_ts),replyToMessageId:t.content["m.relates_to"]?.["m.in_reply_to"]?.event_id};switch(s){case"m.text":return{...n,text:t.content.body};case"m.image":{let o=await this.downloadAttachment(t.content,"image");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Photo]",attachments:o?[o]:void 0}}case"m.audio":{let o=await this.downloadAttachment(t.content,"audio");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Voice message]",attachments:o?[o]:void 0}}case"m.video":{let o=await this.downloadAttachment(t.content,"video");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Video]",attachments:o?[o]:void 0}}case"m.file":{let o=await this.downloadAttachment(t.content,"document");return o&&t.content.body&&(o.fileName=t.content.body),{...n,text:"[Document]",attachments:o?[o]:void 0}}default:return t.content.body?{...n,text:t.content.body}:void 0}}async downloadAttachment(e,t){let s=e.url;if(!s||!s.startsWith("mxc://"))return;let r=e.info??{},n=r.mimetype,o=r.size,i=e.filename??e.body??"file",a=s.slice(6),c=[`${this.homeserverUrl}/_matrix/client/v1/media/download/${a}`,`${this.homeserverUrl}/_matrix/media/v3/download/${a}`];for(let d of c)try{let u=await fetch(d,{headers:{Authorization:`Bearer ${this.accessToken}`}});if(u.status===404)continue;if(!u.ok){console.error(`[matrix] Download failed (${u.status})`,s,d);continue}let p=await u.arrayBuffer(),f=Buffer.from(p);return{type:t,mimeType:n,fileName:i,size:o??f.length,data:f}}catch(u){console.error("[matrix] Download error",s,d,u);continue}console.error("[matrix] All download endpoints failed for",s)}guessMimeType(e){let t=e.split(".").pop()?.toLowerCase();return{pdf:"application/pdf",txt:"text/plain",json:"application/json",csv:"text/csv",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",mp3:"audio/mpeg",ogg:"audio/ogg",mp4:"video/mp4",zip:"application/zip",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[t??""]??"application/octet-stream"}}});var zn,yd=_(()=>{"use strict";Ze();zn=class extends ue{static{m(this,"WhatsAppAdapter")}platform="whatsapp";socket;downloadMedia;dataPath;reconnectAttempts=0;reconnectTimer;constructor(e){super(),this.dataPath=e}async connect(){this.status="connecting";let{makeWASocket:e,useMultiFileAuthState:t,DisconnectReason:s,downloadMediaMessage:r}=await import("@whiskeysockets/baileys");this.downloadMedia=r;let{state:n,saveCreds:o}=await t(this.dataPath);this.socket=e({auth:n,printQRInTerminal:!0}),this.socket.ev.on("creds.update",o),this.socket.ev.on("connection.update",i=>{if(i.connection==="open"&&(this.status="connected",this.reconnectAttempts=0,this.emit("connected")),i.connection==="close"){let c=i.lastDisconnect?.error?.output?.statusCode!==s.loggedOut;if(this.status="disconnected",this.emit("disconnected"),c){let d=Math.min(1e3*Math.pow(2,this.reconnectAttempts),6e4);this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>this.connect(),d)}}}),this.socket.ev.on("messages.upsert",({messages:i,type:a})=>{if(a==="notify")for(let c of i)c.message&&(c.key.fromMe||this.processMessage(c).catch(d=>{this.emit("error",d instanceof Error?d:new Error(String(d)))}))})}async disconnect(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0),this.reconnectAttempts=0,this.socket?.end(void 0),this.socket=void 0,this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,65e3),n="";for(let o=0;o<r.length;o++)n=(await this.socket.sendMessage(e,{text:r[o]},o===0&&s?.replyToMessageId?{quoted:{key:{remoteJid:e,id:s.replyToMessageId},message:{}}}:void 0))?.key?.id??"";return n}async editMessage(e,t,s,r){await this.socket.sendMessage(e,{text:s,edit:{remoteJid:e,id:t,fromMe:!0}})}async deleteMessage(e,t){await this.socket.sendMessage(e,{delete:{remoteJid:e,id:t,fromMe:!0}})}async sendPhoto(e,t,s){return(await this.socket.sendMessage(e,{image:t,caption:s}))?.key?.id}async sendFile(e,t,s,r){return(await this.socket.sendMessage(e,{document:t,fileName:s,caption:r,mimetype:this.guessMimeType(s)}))?.key?.id}async processMessage(e){let t=e.message,s=t.conversation??t.extendedTextMessage?.text??t.imageMessage?.caption??t.videoMessage?.caption??t.documentMessage?.caption??"",r=[],n=s;if(t.imageMessage){let i=await this.downloadMediaSafe(e);if(i){let a=t.imageMessage.mimetype??"image/jpeg";r.push({type:"image",mimeType:a,fileName:t.imageMessage.fileName??`image.${a.split("/")[1]??"jpeg"}`,size:t.imageMessage.fileLength??i.length,data:i})}n||(n="[Photo]")}else if(t.audioMessage){let i=await this.downloadMediaSafe(e);if(i){let a=t.audioMessage.mimetype??"audio/ogg";r.push({type:"audio",mimeType:a,fileName:t.audioMessage.fileName??`audio.${a.split("/")[1]??"ogg"}`,size:t.audioMessage.fileLength??i.length,data:i})}n||(n="[Voice message]")}else if(t.videoMessage){let i=await this.downloadMediaSafe(e);i&&r.push({type:"video",mimeType:t.videoMessage.mimetype??"video/mp4",size:t.videoMessage.fileLength??i.length,data:i}),n||(n="[Video]")}else if(t.documentMessage){let i=await this.downloadMediaSafe(e);i&&r.push({type:"document",mimeType:t.documentMessage.mimetype??"application/octet-stream",fileName:t.documentMessage.fileName??"document",size:t.documentMessage.fileLength??i.length,data:i}),n||(n="[Document]")}else if(t.stickerMessage&&!s)return;if(!n&&r.length===0)return;let o={id:e.key.id??"",platform:"whatsapp",chatId:e.key.remoteJid??"",chatType:e.key.remoteJid?.endsWith("@g.us")?"group":"dm",userId:e.key.participant??e.key.remoteJid??"",userName:e.pushName??e.key.participant??e.key.remoteJid??"",displayName:e.pushName??void 0,text:n,timestamp:new Date(e.messageTimestamp*1e3),replyToMessageId:t.extendedTextMessage?.contextInfo?.stanzaId??void 0,attachments:r.length>0?r:void 0};this.emit("message",o)}async downloadMediaSafe(e){try{if(!this.downloadMedia)return;let t=await this.downloadMedia(e,"buffer",{});return Buffer.isBuffer(t)?t:Buffer.from(t)}catch(t){console.error("[whatsapp] Failed to download media",t);return}}guessMimeType(e){let t=e.split(".").pop()?.toLowerCase();return{pdf:"application/pdf",txt:"text/plain",json:"application/json",csv:"text/csv",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",mp3:"audio/mpeg",ogg:"audio/ogg",mp4:"video/mp4",zip:"application/zip",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[t??""]??"application/octet-stream"}}});var qn,wd=_(()=>{"use strict";Ze();qn=class extends ue{static{m(this,"SignalAdapter")}apiUrl;phoneNumber;platform="signal";pollingInterval;constructor(e,t){super(),this.apiUrl=e,this.phoneNumber=t}async connect(){this.status="connecting";let e=3,t;for(let s=0;s<=e;s++)try{let r=await fetch(`${this.apiUrl}/v1/about`);if(!r.ok)throw new Error(`Signal API not reachable: ${r.status}`);this.pollingInterval=setInterval(()=>{this.pollMessages().catch(n=>{this.emit("error",n instanceof Error?n:new Error(String(n)))})},2e3),this.status="connected",this.emit("connected");return}catch(r){if(t=r instanceof Error?r:new Error(String(r)),s<e){let n=1e3*Math.pow(2,s);await new Promise(o=>setTimeout(o,n))}}this.status="error",this.emit("error",t)}async disconnect(){this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=void 0),this.status="disconnected",this.emit("disconnected")}async sendMessage(e,t,s){let r=this.splitText(t,6e3),n="";for(let o of r){let i=e.startsWith("group."),a={message:o,number:this.phoneNumber};i?a.recipients=[e.replace("group.","")]:a.recipients=[e];let c=await fetch(`${this.apiUrl}/v2/send`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!c.ok)throw new Error(`Signal send failed: ${c.status} ${await c.text()}`);let d=await c.json();n=String(d.timestamp??Date.now())}return n}async editMessage(e,t,s,r){throw new Error("Signal does not support message editing")}async deleteMessage(e,t){let s={number:this.phoneNumber,recipients:[e],timestamp:Number(t)},r=await fetch(`${this.apiUrl}/v1/deleteMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!r.ok)throw new Error(`Signal delete failed: ${r.status} ${await r.text()}`)}async pollMessages(){let e=await fetch(`${this.apiUrl}/v1/receive/${this.phoneNumber}`);if(!e.ok)return;let t=await e.json();for(let s of t){let r=s.envelope?.dataMessage;if(!r||!r.message&&(!r.attachments||r.attachments.length===0))continue;let n=s.envelope,o=r.groupInfo?.groupId?`group.${r.groupInfo.groupId}`:n.sourceNumber??n.source??"",i=[];if(r.attachments)for(let d of r.attachments){let u=await this.downloadAttachment(d);u&&i.push(u)}let a=r.message||this.inferTextFromAttachments(i)||"";if(!a&&i.length===0)continue;let c={id:String(r.timestamp??Date.now()),platform:"signal",chatId:o,chatType:r.groupInfo?"group":"dm",userId:n.sourceNumber??n.source??"",userName:n.sourceName??n.sourceNumber??n.source??"",displayName:n.sourceName,text:a,timestamp:new Date(r.timestamp??Date.now()),attachments:i.length>0?i:void 0};this.emit("message",c)}}async downloadAttachment(e){if(e.id)try{let t=await fetch(`${this.apiUrl}/v1/attachments/${e.id}`);if(!t.ok)return;let s=await t.arrayBuffer(),r=Buffer.from(s);return{type:this.classifyContentType(e.contentType),mimeType:e.contentType??void 0,fileName:e.filename??void 0,size:e.size??r.length,data:r}}catch(t){console.error("[signal] Failed to download attachment",e.id,t);return}}classifyContentType(e){return e?e.startsWith("image/")?"image":e.startsWith("audio/")?"audio":e.startsWith("video/")?"video":"document":"other"}inferTextFromAttachments(e){if(e.length===0)return"";let t=e.map(s=>s.type);return t.includes("image")?"[Photo]":t.includes("audio")?"[Voice message]":t.includes("video")?"[Video]":t.includes("document")?"[Document]":"[File]"}}});import ta from"node:readline";var Wn,Td=_(()=>{"use strict";Ze();Wn=class extends ue{static{m(this,"CLIAdapter")}platform="cli";rl;messageCounter=0;async connect(){this.status="connecting",this.rl=ta.createInterface({input:process.stdin,output:process.stdout,prompt:"You: "}),console.log(`
901
950
  Alfred Chat \u2014 type your message and press Enter. Use /quit or /exit to leave.
902
951
  `),this.rl.on("line",e=>{let t=e.trim();if(!t){this.prompt();return}if(t==="/quit"||t==="/exit"){console.log(`
903
952
  Goodbye!
904
953
  `),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(`
905
954
  Alfred: ${t}
906
- `),this.prompt(),r}async editMessage(e,t,s,r){Zi.clearLine(process.stdout,0),Zi.cursorTo(process.stdout,0),process.stdout.write(`Alfred: ${s}`)}async deleteMessage(e,t){}prompt(){this.rl?.prompt()}}});import Gp from"node:http";import Vp from"node:crypto";var zn,gd=_(()=>{"use strict";Je();zn=class extends pe{static{m(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=Gp.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-${Vp.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}
955
+ `),this.prompt(),r}async editMessage(e,t,s,r){ta.clearLine(process.stdout,0),ta.cursorTo(process.stdout,0),process.stdout.write(`Alfred: ${s}`)}async deleteMessage(e,t){}prompt(){this.rl?.prompt()}}});import Jp from"node:http";import Zp from"node:crypto";var Gn,_d=_(()=>{"use strict";Ze();Gn=class extends ue{static{m(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=Jp.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-${Zp.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}
907
956
  data: ${JSON.stringify(s)}
908
957
 
909
- `)}}});var rt={};ue(rt,{CLIAdapter:()=>qn,DiscordAdapter:()=>Fn,HttpAdapter:()=>zn,MatrixAdapter:()=>jn,MessagingAdapter:()=>pe,SignalAdapter:()=>Hn,TelegramAdapter:()=>Pn,WhatsAppAdapter:()=>Bn});var nt=_(()=>{"use strict";Je();dd();ud();md();pd();hd();fd();gd()});import Wn from"node:fs";import Gn from"node:path";import Kp from"js-yaml";var Ct,yd=_(()=>{"use strict";Do();Mo();Ve();Vo();an();Q();Di();Ni();Mi();Oi();Pi();Ui();Fi();ji();Bi();Hi();qi();zi();cd();Ki();Xi();Ct=class{static{m(this,"Alfred")}config;logger;database;pipeline;reminderScheduler;backgroundTaskRunner;proactiveScheduler;watchEngine;adapters=new Map;formatter=new xr;userRepo;skillRegistry;mcpManager;calendarSkill;constructor(e){this.config=e,this.logger=Os("alfred",e.logger.level)}async initialize(){this.logger.info("Initializing Alfred..."),this.database=new yt(this.config.storage.path);let e=this.database.getDb(),t=new Ps(e),s=new Us(e);this.userRepo=s;let r=new wt(e),n=new Fs(e),o=new js(e),i=new Bs(e),a=new Hs(e),c=new qs(e),d=new zs(e),u=new Ws(e);this.logger.info("Storage initialized");let p=new rr,g=this.loadSecurityRules();p.loadRules(g);let f=new nr(p,r,this.logger.child({component:"security"}));this.logger.info({ruleCount:g.length},"Security engine initialized");let h=Wo(this.config.llm,this.logger.child({component:"llm"}));await h.initialize();let y=new Rr(h,a,this.logger.child({component:"embeddings"})),E=this.config.activeLearning?.enabled!==!1,$,v;E&&($=new Mr({llm:h,memoryRepo:n,logger:this.logger.child({component:"active-learning"}),embeddingService:y,minMessageLength:this.config.activeLearning?.minMessageLength,minConfidence:this.config.activeLearning?.minConfidence,maxExtractionsPerMinute:this.config.activeLearning?.maxExtractionsPerMinute}),v=new Or(n,this.logger.child({component:"memory-retriever"}),y),this.logger.info("Active learning & memory retriever initialized"));let P=new Ut(this.logger.child({component:"sandbox"})),x=this.skillRegistry=new Pt;x.register(new Ft),x.register(new jt),x.register(new Bt(this.config.search?{provider:this.config.search.provider,apiKey:this.config.search.apiKey,baseUrl:this.config.search.baseUrl}:void 0)),x.register(new Ht(o)),x.register(new qt(i));let q=new Vs(e);if(x.register(new as(q)),x.register(new zt),x.register(new Wt),x.register(new Gt(n,y)),x.register(new Vt(h,x,P,f)),this.config.email?.accounts?.length){let D=new Map;for(let B of this.config.email.accounts)try{B.provider==="microsoft"&&!B.microsoft?.clientId&&this.config.calendar?.microsoft&&(B.microsoft={...this.config.calendar.microsoft});let _e=await or(B);D.set(B.name,_e),this.logger.info({account:B.name,provider:B.provider??"imap-smtp"},"Email account initialized")}catch(_e){this.logger.warn({err:_e,account:B.name},"Email account initialization failed, skipping")}x.register(D.size>0?new tt(D):new tt)}else x.register(new tt);x.register(new Kt),x.register(new Xt);let J=new us;J.setReloadCallback(D=>this.reloadService(D)),x.register(J),x.register(new Jt),x.register(new Zt),x.register(new Qt),x.register(new es(s)),x.register(new ts(s,c,this.adapters,(D,B)=>t.findByPlatformAndUser(D,B))),x.register(new ss(d)),x.register(new rs(u));let oe=new Gs(e),O=new Cr(oe,y,this.logger.child({component:"documents"}));x.register(new ns(oe,O,y));let se;if(this.config.calendar)try{let D=await cr(this.config.calendar);se=new $t(D),x.register(se),this.logger.info({provider:this.config.calendar.provider},"Calendar initialized")}catch(D){this.logger.warn({err:D},"Calendar initialization failed, continuing without calendar")}if(this.calendarSkill=se,this.config.mcp?.servers?.length){let{MCPManager:D}=await Promise.resolve().then(()=>(Q(),te));this.mcpManager=new D(this.logger.child({component:"mcp"})),await this.mcpManager.initialize(this.config.mcp);for(let B of this.mcpManager.getSkills())x.register(B);this.logger.info({mcpSkills:this.mcpManager.getSkills().length},"MCP skills registered")}if(this.config.codeSandbox?.enabled){let{CodeExecutionSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D({allowedLanguages:this.config.codeSandbox.allowedLanguages,maxTimeoutMs:this.config.codeSandbox.maxTimeoutMs})),this.logger.info("Code sandbox enabled")}if(this.config.codeAgents?.enabled){let{CodeAgentSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D({agents:this.config.codeAgents.agents,forge:this.config.codeAgents.forge},h)),this.logger.info({agents:this.config.codeAgents.agents.map(B=>B.name)},"Code agent skill enabled")}if(this.config.proxmox){let{ProxmoxSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.proxmox)),this.logger.info({baseUrl:this.config.proxmox.baseUrl},"Proxmox skill enabled")}if(this.config.unifi){let{UniFiSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.unifi)),this.logger.info({baseUrl:this.config.unifi.baseUrl},"UniFi skill enabled")}if(this.config.homeassistant){let{HomeAssistantSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.homeassistant)),this.logger.info({baseUrl:this.config.homeassistant.baseUrl},"Home Assistant skill enabled")}if(this.config.contacts)try{let{ContactsSkill:D,createContactsProvider:B}=await Promise.resolve().then(()=>(Q(),te)),_e=await B(this.config.contacts);x.register(new D(_e)),this.logger.info({provider:this.config.contacts.provider},"Contacts skill enabled")}catch(D){this.logger.warn({err:D},"Contacts initialization failed, continuing without contacts")}if(this.config.docker){let{DockerSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.docker)),this.logger.info("Docker skill enabled")}if(this.config.bmw){let{BMWSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.bmw)),this.logger.info("BMW CarData skill enabled")}if(this.config.routing){let{RoutingSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.routing)),this.logger.info("Routing skill enabled")}if(this.config.todo){let{MicrosoftTodoSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.todo)),this.logger.info("Microsoft To Do skill enabled")}if(this.config.proxmox||this.config.unifi||this.config.homeassistant){let{MonitorSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D({proxmox:this.config.proxmox,unifi:this.config.unifi,homeassistant:this.config.homeassistant})),this.logger.info("Infrastructure monitor skill enabled")}{let{EnergyPriceSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.energy)),this.logger.info({grid:this.config.energy?.gridName},"Energy price skill registered")}{let{MarketplaceSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(this.config.marketplace)),this.logger.info("Marketplace skill registered")}{let{BriefingSkill:D}=await Promise.resolve().then(()=>(Q(),te));x.register(new D(x,this.config,n)),this.logger.info("Briefing skill registered")}this.logger.info({skills:x.getAll().map(D=>D.metadata.name)},"Skills registered");let $e;if(this.config.speech?.apiKey&&($e=new vr(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 D=new Sr(this.config.speech,this.logger.child({component:"tts"}));x.register(new os(D)),this.logger.info("Text-to-speech skill registered")}let re=this.detectImageGenProvider();if(re){let D=new Ar(re,this.logger.child({component:"image-gen"}));x.register(new is(D)),this.logger.info({provider:re.provider},"Image generation skill registered")}try{let D=new Ir(this.logger.child({component:"transit"}));x.register(new ls(D)),this.logger.info("Public transit skill registered")}catch(D){this.logger.warn({err:D},"Failed to register transit skill")}let j=new Er(t),ee=Gn.resolve(Gn.dirname(this.config.storage.path),"inbox");this.pipeline=new $r({llm:h,conversationManager:j,users:s,logger:this.logger.child({component:"pipeline"}),skillRegistry:x,skillSandbox:P,securityManager:f,memoryRepo:n,speechTranscriber:$e,inboxPath:ee,embeddingService:y,activeLearning:$,memoryRetriever:v,maxHistoryMessages:this.config.conversation?.maxHistoryMessages??100,documentProcessor:O}),this.reminderScheduler=new kr(o,async(D,B,_e)=>{let ie=this.adapters.get(D);ie?await ie.sendMessage(B,_e):this.logger.warn({platform:D,chatId:B},"No adapter for reminder platform")},this.logger.child({component:"reminders"}),15e3,{getMasterUserId:m(D=>s.getMasterUserId(D),"getMasterUserId"),getLinkedUsers:m(D=>s.getLinkedUsers(D),"getLinkedUsers"),findConversation:m((D,B)=>t.findByPlatformAndUser(D,B),"findConversation")}),this.backgroundTaskRunner=new Dr(x,P,d,this.adapters,s,this.logger.child({component:"background-tasks"})),this.proactiveScheduler=new Lr(u,x,P,h,this.adapters,s,this.logger.child({component:"proactive-scheduler"}),this.pipeline,this.formatter,j);let we=new Ks(e);x.register(new ms(we)),this.watchEngine=new On(we,x,P,this.adapters,s,this.logger.child({component:"watch-engine"})),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(()=>(nt(),rt));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(()=>(nt(),rt));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(()=>(nt(),rt));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(()=>(nt(),rt));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(()=>(nt(),rt));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(()=>(nt(),rt)),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.watchEngine?.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(()=>(nt(),rt)),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.watchEngine?.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")}async reloadService(e){try{Ro();let t=new ce().loadConfig();if(this.skillRegistry.has(e)&&this.skillRegistry.unregister(e),e==="proxmox"&&t.proxmox){let{ProxmoxSkill:s}=await Promise.resolve().then(()=>(Q(),te));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(()=>(Q(),te));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(()=>(Q(),te));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(()=>(Q(),te)),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(()=>(Q(),te));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(()=>(Q(),te));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(()=>(Q(),te));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(()=>(Q(),te));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}}}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=m(async d=>{if(d!==n){n=d;try{r?await t.editMessage(s.chatId,r,d):r=await t.sendMessage(s.chatId,d)}catch(u){this.logger.debug({err:u,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;try{if(r&&e!=="api")try{await t.editMessage(s.chatId,r,a.text,c)}catch(d){this.logger.debug({err:d,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)}catch(d){this.logger.warn({err:d,chatId:s.chatId},"Formatted send failed, retrying as plain text");let u=this.formatter.format(i.text,"signal");await t.sendMessage(s.chatId,u.text)}if(i.attachments)for(let d of i.attachments)try{let u=d.mimeType.startsWith("image/"),p=d.mimeType==="audio/ogg"||d.mimeType==="audio/opus";u?await t.sendPhoto(s.chatId,d.data,d.fileName):p?await t.sendVoice(s.chatId,d.data):await t.sendFile(s.chatId,d.data,d.fileName)}catch(u){this.logger.warn({err:u,fileName:d.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")})}detectImageGenProvider(){let e=["default","strong","fast","embeddings","local"];for(let t of["openai","google"])for(let s of e){let r=this.config.llm[s];if(r?.provider===t&&r.apiKey)return{provider:t,apiKey:r.apiKey,baseUrl:r.baseUrl}}}loadSecurityRules(){let e=Gn.resolve(this.config.security.rulesPath),t=[];if(!Wn.existsSync(e))return this.logger.warn({rulesPath:e},"Security rules directory not found, using default deny"),t;if(!Wn.statSync(e).isDirectory())return this.logger.warn({rulesPath:e},"Security rules path is not a directory"),t;let r=Wn.readdirSync(e).filter(n=>n.endsWith(".yml")||n.endsWith(".yaml"));for(let n of r)try{let o=Gn.join(e,n),i=Wn.readFileSync(o,"utf-8"),a=Kp.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 wd=_(()=>{"use strict"});var Qi=_(()=>{"use strict";yd();Ni();Di();Mi();Oi();Pi();ji();Bi();qi();zi();gs();Hi();Ui();Fi();Ki();Xi();Vi();wd();Gi()});import Vn from"node:fs";import Td from"node:path";import Xp from"node:os";function _d(){try{let l=Vn.readFileSync(ea,"utf-8"),e=JSON.parse(l);if(e?.version===1&&e.providers)return e}catch{}return{version:1,providers:{}}}function bd(l){try{let e=Td.dirname(ea);Vn.existsSync(e)||Vn.mkdirSync(e,{recursive:!0}),Vn.writeFileSync(ea,JSON.stringify(l,null,2),"utf-8")}catch{}}async function Dt(l,e){let t=new AbortController,s=setTimeout(()=>t.abort(),Jp);try{return await fetch(l,{...e,signal:t.signal})}finally{clearTimeout(s)}}async function Ed(l,e,t){switch(l){case"anthropic":{let s=await Dt("https://api.anthropic.com/v1/models",{headers:{"x-api-key":e??"","anthropic-version":"2023-06-01"}});return s.ok?((await s.json()).data??[]).map(n=>({id:n.id,name:n.display_name})):[]}case"openai":{let s=t?`${t.replace(/\/+$/,"")}/models`:"https://api.openai.com/v1/models",r=await Dt(s,{headers:{Authorization:`Bearer ${e??""}`}});return r.ok?((await r.json()).data??[]).map(o=>({id:o.id})):[]}case"google":{let s=`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(e??"")}`,r=await Dt(s);return r.ok?((await r.json()).models??[]).map(o=>({id:o.name.replace(/^models\//,""),name:o.displayName})):[]}case"mistral":{let s=await Dt("https://api.mistral.ai/v1/models",{headers:{Authorization:`Bearer ${e??""}`}});return s.ok?((await s.json()).data??[]).map(n=>({id:n.id,name:n.name})):[]}case"openrouter":{let s=await Dt("https://openrouter.ai/api/v1/models",{headers:{Authorization:`Bearer ${e??""}`}});return s.ok?((await s.json()).data??[]).map(n=>({id:n.id,name:n.name})):[]}case"ollama":{let s=(t??"http://localhost:11434").replace(/\/+$/,""),r=await Dt(`${s}/api/tags`);return r.ok?((await r.json()).models??[]).map(o=>({id:o.name})):[]}case"openwebui":{let s=(t??"http://localhost:3000/api/v1").replace(/\/+$/,""),r=await Dt(`${s}/models`,{headers:{Authorization:`Bearer ${e??""}`}});return r.ok?((await r.json()).data??[]).map(o=>({id:o.id})):[]}default:return[]}}async function ta(l,e,t){let s=_d(),r=s.providers[l];if(r&&Date.now()-r.fetchedAt<Yp)return r.models;try{let n=await Ed(l,e,t);if(n.length>0)return s.providers[l]={fetchedAt:Date.now(),models:n},bd(s),n}catch{}return r?r.models:[]}function Kn(l,e,t){Ed(l,e,t).then(s=>{if(s.length>0){let r=_d();r.providers[l]={fetchedAt:Date.now(),models:s},bd(r)}}).catch(()=>{})}function sa(l,e){let t=new Set,s=[];for(let r of l)if(!t.has(r.id)){t.add(r.id);let n=e.find(o=>o.id===r.id);s.push({id:r.id,name:r.name,desc:n?.desc})}for(let r of e)t.has(r.id)||(t.add(r.id),s.push({id:r.id,desc:r.desc}));return s}var Yp,Jp,ea,ra=_(()=>{"use strict";Yp=24*60*60*1e3,Jp=5e3,ea=Td.join(Xp.homedir(),".alfred","model-cache.json");m(_d,"readCache");m(bd,"writeCache");m(Dt,"fetchWithTimeout");m(Ed,"fetchModelsFromAPI");m(ta,"getModels");m(Kn,"refreshCacheInBackground");m(sa,"mergeModels")});var $d={};ue($d,{startCommand:()=>Zp});async function Zp(){let l=new ce,e;try{e=l.loadConfig()}catch(o){console.error("Failed to load configuration:",o.message),process.exit(1)}let t=Os("cli",e.logger.level);t.info({name:e.name},"Configuration loaded");let s=new Ct(e),r=!1,n=m(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");let o=e.llm;o?.default?.provider?Kn(o.default.provider,o.default.apiKey,o.default.baseUrl):o?.provider&&Kn(o.provider,void 0,o.baseUrl);for(let i of["strong","fast"]){let a=o?.[i];a?.provider&&Kn(a.provider,a.apiKey,a.baseUrl)}}catch(o){let i=o instanceof Error?o:new Error(String(o));t.fatal({err:i},"Failed to start Alfred"),process.exit(1)}}var kd=_(()=>{"use strict";Ve();Do();Qi();ra();m(Zp,"startCommand")});var Sd={};ue(Sd,{chatCommand:()=>th});import vd from"node:http";import Pr from"node:readline";function Qp(l,e){return new Promise(t=>{let s=vd.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 eh(l,e){let t=Pr.createInterface({input:process.stdin,output:process.stdout,prompt:"You: "});console.log(`
958
+ `)}}});var ot={};le(ot,{CLIAdapter:()=>Wn,DiscordAdapter:()=>Bn,HttpAdapter:()=>Gn,MatrixAdapter:()=>Hn,MessagingAdapter:()=>ue,SignalAdapter:()=>qn,TelegramAdapter:()=>Fn,WhatsAppAdapter:()=>zn});var it=_(()=>{"use strict";Ze();hd();fd();gd();yd();wd();Td();_d()});import Vn from"node:fs";import Kn from"node:path";import Qp from"js-yaml";var Dt,bd=_(()=>{"use strict";No();Po();Ke();Xo();ln();ee();Ni();Oi();Pi();Ui();Fi();ji();Bi();Hi();zi();qi();Wi();Gi();md();Yi();Ji();Zi();Dt=class{static{m(this,"Alfred")}config;logger;database;pipeline;reminderScheduler;backgroundTaskRunner;proactiveScheduler;watchEngine;adapters=new Map;formatter=new Lr;userRepo;skillRegistry;mcpManager;calendarSkill;constructor(e){this.config=e,this.logger=Fs("alfred",e.logger.level)}async initialize(){this.logger.info("Initializing Alfred..."),this.database=new wt(this.config.storage.path);let e=this.database.getDb(),t=new js(e),s=new Bs(e);this.userRepo=s;let r=new Tt(e),n=new Hs(e),o=new zs(e),i=new qs(e),a=new Ws(e),c=new Gs(e),d=new Vs(e),u=new Ks(e);this.logger.info("Storage initialized");let p=new ar,f=this.loadSecurityRules();p.loadRules(f);let g=new cr(p,r,this.logger.child({component:"security"}));this.logger.info({ruleCount:f.length},"Security engine initialized");let h=Vo(this.config.llm,this.logger.child({component:"llm"}));await h.initialize();let y=new Nr(h,a,this.logger.child({component:"embeddings"})),E=this.config.activeLearning?.enabled!==!1,$,v;E&&($=new Fr({llm:h,memoryRepo:n,logger:this.logger.child({component:"active-learning"}),embeddingService:y,minMessageLength:this.config.activeLearning?.minMessageLength,minConfidence:this.config.activeLearning?.minConfidence,maxExtractionsPerMinute:this.config.activeLearning?.maxExtractionsPerMinute}),v=new jr(n,this.logger.child({component:"memory-retriever"}),y),this.logger.info("Active learning & memory retriever initialized"));let j=new Zs(e),N=new Br(h,j,this.logger.child({component:"summarizer"}));this.logger.info("Conversation summarizer initialized");let H=new jt(this.logger.child({component:"sandbox"})),D=this.skillRegistry=new Ft;D.register(new Bt),D.register(new Ht),D.register(new zt(this.config.search?{provider:this.config.search.provider,apiKey:this.config.search.apiKey,baseUrl:this.config.search.baseUrl}:void 0)),D.register(new qt(o)),D.register(new Wt(i));let oe=new Ys(e);if(D.register(new ls(oe)),D.register(new Gt),D.register(new Vt),D.register(new Kt(n,y)),D.register(new Xt(h,D,H,g)),this.config.email?.accounts?.length){let C=new Map;for(let B of this.config.email.accounts)try{B.provider==="microsoft"&&!B.microsoft?.clientId&&this.config.calendar?.microsoft&&(B.microsoft={...this.config.calendar.microsoft});let pe=await lr(B);C.set(B.name,pe),this.logger.info({account:B.name,provider:B.provider??"imap-smtp"},"Email account initialized")}catch(pe){this.logger.warn({err:pe,account:B.name},"Email account initialization failed, skipping")}D.register(C.size>0?new rt(C):new rt)}else D.register(new rt);D.register(new Yt),D.register(new Jt);let M=new ps;M.setReloadCallback(C=>this.reloadService(C)),D.register(M),D.register(new Qt),D.register(new es),D.register(new ts),D.register(new ss(s)),D.register(new rs(s,c,this.adapters,(C,B)=>t.findByPlatformAndUser(C,B))),D.register(new ns(d)),D.register(new os(u));let re=new Xs(e),ze=new Mr(re,y,this.logger.child({component:"documents"}));D.register(new is(re,ze,y));let $e;if(this.config.calendar)try{let C=await mr(this.config.calendar);$e=new kt(C),D.register($e),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=$e,this.config.mcp?.servers?.length){let{MCPManager:C}=await Promise.resolve().then(()=>(ee(),te));this.mcpManager=new C(this.logger.child({component:"mcp"})),await this.mcpManager.initialize(this.config.mcp);for(let B of this.mcpManager.getSkills())D.register(B);this.logger.info({mcpSkills:this.mcpManager.getSkills().length},"MCP skills registered")}if(this.config.codeSandbox?.enabled){let{CodeExecutionSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.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(()=>(ee(),te));D.register(new C({agents:this.config.codeAgents.agents,forge:this.config.codeAgents.forge},h)),this.logger.info({agents:this.config.codeAgents.agents.map(B=>B.name)},"Code agent skill enabled")}if(this.config.proxmox){let{ProxmoxSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.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(()=>(ee(),te));D.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(()=>(ee(),te));D.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:B}=await Promise.resolve().then(()=>(ee(),te)),pe=await B(this.config.contacts);D.register(new C(pe)),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(()=>(ee(),te));D.register(new C(this.config.docker)),this.logger.info("Docker skill enabled")}if(this.config.bmw){let{BMWSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.register(new C(this.config.bmw)),this.logger.info("BMW CarData skill enabled")}if(this.config.routing){let{RoutingSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.register(new C(this.config.routing)),this.logger.info("Routing skill enabled")}if(this.config.todo){let{MicrosoftTodoSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.register(new C(this.config.todo)),this.logger.info("Microsoft To Do skill enabled")}if(this.config.proxmox||this.config.unifi||this.config.homeassistant){let{MonitorSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.register(new C({proxmox:this.config.proxmox,unifi:this.config.unifi,homeassistant:this.config.homeassistant})),this.logger.info("Infrastructure monitor skill enabled")}{let{EnergyPriceSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.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(()=>(ee(),te));D.register(new C(this.config.marketplace)),this.logger.info("Marketplace skill registered")}{let{BriefingSkill:C}=await Promise.resolve().then(()=>(ee(),te));D.register(new C(D,this.config,n)),this.logger.info("Briefing skill registered")}this.logger.info({skills:D.getAll().map(C=>C.metadata.name)},"Skills registered");let se;if(this.config.speech?.apiKey&&(se=new xr(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 Rr(this.config.speech,this.logger.child({component:"tts"}));D.register(new as(C)),this.logger.info("Text-to-speech skill registered")}let X=this.detectImageGenProvider();if(X){let C=new Cr(X,this.logger.child({component:"image-gen"}));D.register(new cs(C)),this.logger.info({provider:X.provider},"Image generation skill registered")}try{let C=new Dr(this.logger.child({component:"transit"}));D.register(new us(C)),this.logger.info("Public transit skill registered")}catch(C){this.logger.warn({err:C},"Failed to register transit skill")}let z=new Sr(t),me=Kn.resolve(Kn.dirname(this.config.storage.path),"inbox");this.pipeline=new Ar({llm:h,conversationManager:z,users:s,logger:this.logger.child({component:"pipeline"}),skillRegistry:D,skillSandbox:H,securityManager:g,memoryRepo:n,speechTranscriber:se,inboxPath:me,embeddingService:y,activeLearning:$,memoryRetriever:v,maxHistoryMessages:this.config.conversation?.maxHistoryMessages??100,documentProcessor:ze,conversationSummarizer:N}),this.reminderScheduler=new Ir(o,async(C,B,pe)=>{let we=this.adapters.get(C);we?await we.sendMessage(B,pe):this.logger.warn({platform:C,chatId:B},"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,B)=>t.findByPlatformAndUser(C,B),"findConversation")}),this.backgroundTaskRunner=new Or(D,H,d,this.adapters,s,this.logger.child({component:"background-tasks"})),this.proactiveScheduler=new Pr(u,D,H,h,this.adapters,s,this.logger.child({component:"proactive-scheduler"}),this.pipeline,this.formatter,z);let xe=new Js(e);D.register(new hs(xe)),this.watchEngine=new Un(xe,D,H,this.adapters,s,this.logger.child({component:"watch-engine"})),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(()=>(it(),ot));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(()=>(it(),ot));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(()=>(it(),ot));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(()=>(it(),ot));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(()=>(it(),ot));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(()=>(it(),ot)),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.watchEngine?.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(()=>(it(),ot)),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.watchEngine?.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")}async reloadService(e){try{Do();let t=new ce().loadConfig();if(this.skillRegistry.has(e)&&this.skillRegistry.unregister(e),e==="proxmox"&&t.proxmox){let{ProxmoxSkill:s}=await Promise.resolve().then(()=>(ee(),te));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(()=>(ee(),te));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(()=>(ee(),te));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(()=>(ee(),te)),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(()=>(ee(),te));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(()=>(ee(),te));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(()=>(ee(),te));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(()=>(ee(),te));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}}}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=m(async d=>{if(d!==n){n=d;try{r?await t.editMessage(s.chatId,r,d):r=await t.sendMessage(s.chatId,d)}catch(u){this.logger.debug({err:u,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;try{if(r&&e!=="api")try{await t.editMessage(s.chatId,r,a.text,c)}catch(d){this.logger.debug({err:d,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)}catch(d){this.logger.warn({err:d,chatId:s.chatId},"Formatted send failed, retrying as plain text");let u=this.formatter.format(i.text,"signal");await t.sendMessage(s.chatId,u.text)}if(i.attachments)for(let d of i.attachments)try{let u=d.mimeType.startsWith("image/"),p=d.mimeType==="audio/ogg"||d.mimeType==="audio/opus";u?await t.sendPhoto(s.chatId,d.data,d.fileName):p?await t.sendVoice(s.chatId,d.data):await t.sendFile(s.chatId,d.data,d.fileName)}catch(u){this.logger.warn({err:u,fileName:d.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")})}detectImageGenProvider(){let e=["default","strong","fast","embeddings","local"];for(let t of["openai","google"])for(let s of e){let r=this.config.llm[s];if(r?.provider===t&&r.apiKey)return{provider:t,apiKey:r.apiKey,baseUrl:r.baseUrl}}}loadSecurityRules(){let e=Kn.resolve(this.config.security.rulesPath),t=[];if(!Vn.existsSync(e))return this.logger.warn({rulesPath:e},"Security rules directory not found, using default deny"),t;if(!Vn.statSync(e).isDirectory())return this.logger.warn({rulesPath:e},"Security rules path is not a directory"),t;let r=Vn.readdirSync(e).filter(n=>n.endsWith(".yml")||n.endsWith(".yaml"));for(let n of r)try{let o=Kn.join(e,n),i=Vn.readFileSync(o,"utf-8"),a=Qp.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 Ed=_(()=>{"use strict"});var sa=_(()=>{"use strict";bd();Oi();Ni();Pi();Ui();Fi();Hi();zi();Wi();Gi();ws();qi();ji();Bi();Yi();Ji();Xi();Ed();Ki();Zi()});import Xn from"node:fs";import $d from"node:path";import eh from"node:os";function kd(){try{let l=Xn.readFileSync(ra,"utf-8"),e=JSON.parse(l);if(e?.version===1&&e.providers)return e}catch{}return{version:1,providers:{}}}function vd(l){try{let e=$d.dirname(ra);Xn.existsSync(e)||Xn.mkdirSync(e,{recursive:!0}),Xn.writeFileSync(ra,JSON.stringify(l,null,2),"utf-8")}catch{}}async function Lt(l,e){let t=new AbortController,s=setTimeout(()=>t.abort(),sh);try{return await fetch(l,{...e,signal:t.signal})}finally{clearTimeout(s)}}async function Sd(l,e,t){switch(l){case"anthropic":{let s=await Lt("https://api.anthropic.com/v1/models",{headers:{"x-api-key":e??"","anthropic-version":"2023-06-01"}});return s.ok?((await s.json()).data??[]).map(n=>({id:n.id,name:n.display_name})):[]}case"openai":{let s=t?`${t.replace(/\/+$/,"")}/models`:"https://api.openai.com/v1/models",r=await Lt(s,{headers:{Authorization:`Bearer ${e??""}`}});return r.ok?((await r.json()).data??[]).map(o=>({id:o.id})):[]}case"google":{let s=`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(e??"")}`,r=await Lt(s);return r.ok?((await r.json()).models??[]).map(o=>({id:o.name.replace(/^models\//,""),name:o.displayName})):[]}case"mistral":{let s=await Lt("https://api.mistral.ai/v1/models",{headers:{Authorization:`Bearer ${e??""}`}});return s.ok?((await s.json()).data??[]).map(n=>({id:n.id,name:n.name})):[]}case"openrouter":{let s=await Lt("https://openrouter.ai/api/v1/models",{headers:{Authorization:`Bearer ${e??""}`}});return s.ok?((await s.json()).data??[]).map(n=>({id:n.id,name:n.name})):[]}case"ollama":{let s=(t??"http://localhost:11434").replace(/\/+$/,""),r=await Lt(`${s}/api/tags`);return r.ok?((await r.json()).models??[]).map(o=>({id:o.name})):[]}case"openwebui":{let s=(t??"http://localhost:3000/api/v1").replace(/\/+$/,""),r=await Lt(`${s}/models`,{headers:{Authorization:`Bearer ${e??""}`}});return r.ok?((await r.json()).data??[]).map(o=>({id:o.id})):[]}default:return[]}}async function na(l,e,t){let s=kd(),r=s.providers[l];if(r&&Date.now()-r.fetchedAt<th)return r.models;try{let n=await Sd(l,e,t);if(n.length>0)return s.providers[l]={fetchedAt:Date.now(),models:n},vd(s),n}catch{}return r?r.models:[]}function Yn(l,e,t){Sd(l,e,t).then(s=>{if(s.length>0){let r=kd();r.providers[l]={fetchedAt:Date.now(),models:s},vd(r)}}).catch(()=>{})}function oa(l,e){let t=new Set,s=[];for(let r of l)if(!t.has(r.id)){t.add(r.id);let n=e.find(o=>o.id===r.id);s.push({id:r.id,name:r.name,desc:n?.desc})}for(let r of e)t.has(r.id)||(t.add(r.id),s.push({id:r.id,desc:r.desc}));return s}var th,sh,ra,ia=_(()=>{"use strict";th=24*60*60*1e3,sh=5e3,ra=$d.join(eh.homedir(),".alfred","model-cache.json");m(kd,"readCache");m(vd,"writeCache");m(Lt,"fetchWithTimeout");m(Sd,"fetchModelsFromAPI");m(na,"getModels");m(Yn,"refreshCacheInBackground");m(oa,"mergeModels")});var Ad={};le(Ad,{startCommand:()=>rh});async function rh(){let l=new ce,e;try{e=l.loadConfig()}catch(o){console.error("Failed to load configuration:",o.message),process.exit(1)}let t=Fs("cli",e.logger.level);t.info({name:e.name},"Configuration loaded");let s=new Dt(e),r=!1,n=m(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");let o=e.llm;o?.default?.provider?Yn(o.default.provider,o.default.apiKey,o.default.baseUrl):o?.provider&&Yn(o.provider,void 0,o.baseUrl);for(let i of["strong","fast"]){let a=o?.[i];a?.provider&&Yn(a.provider,a.apiKey,a.baseUrl)}}catch(o){let i=o instanceof Error?o:new Error(String(o));t.fatal({err:i},"Failed to start Alfred"),process.exit(1)}}var Id=_(()=>{"use strict";Ke();No();sa();ia();m(rh,"startCommand")});var Rd={};le(Rd,{chatCommand:()=>ih});import xd from"node:http";import Hr from"node:readline";function nh(l,e){return new Promise(t=>{let s=xd.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 oh(l,e){let t=Hr.createInterface({input:process.stdin,output:process.stdout,prompt:"You: "});console.log(`
910
959
  Alfred Chat (connected to server) \u2014 type your message and press Enter. Use /quit or /exit to leave.
911
960
  `),t.prompt(),t.on("line",s=>{let r=s.trim();if(!r){t.prompt();return}(r==="/quit"||r==="/exit")&&(console.log(`
912
961
  Goodbye!
913
- `),t.close(),process.exit(0));let n=JSON.stringify({text:r,chatId:"api-chat",userId:"api-user"}),o=vd.request({hostname:l,port:e,path:"/api/message",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(n)}},i=>{let a="";i.on("data",c=>{a+=c.toString();let d=a.split(`
962
+ `),t.close(),process.exit(0));let n=JSON.stringify({text:r,chatId:"api-chat",userId:"api-user"}),o=xd.request({hostname:l,port:e,path:"/api/message",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(n)}},i=>{let a="";i.on("data",c=>{a+=c.toString();let d=a.split(`
914
963
 
915
964
  `);a=d.pop()??"";for(let u of d){let p=u.split(`
916
- `).find(g=>g.startsWith("data: "));if(p)try{let g=JSON.parse(p.slice(6));switch(g.type){case"status":Pr.clearLine(process.stdout,0),Pr.cursorTo(process.stdout,0),process.stdout.write(`Alfred: ${g.text??""}`);break;case"response":Pr.clearLine(process.stdout,0),Pr.cursorTo(process.stdout,0),process.stdout.write(`
917
- Alfred: ${g.text??""}
918
- `);break;case"attachment":{let f=g.fileName??g.attachmentType??"file";process.stdout.write(`[Attachment: ${f}]
965
+ `).find(f=>f.startsWith("data: "));if(p)try{let f=JSON.parse(p.slice(6));switch(f.type){case"status":Hr.clearLine(process.stdout,0),Hr.cursorTo(process.stdout,0),process.stdout.write(`Alfred: ${f.text??""}`);break;case"response":Hr.clearLine(process.stdout,0),Hr.cursorTo(process.stdout,0),process.stdout.write(`
966
+ Alfred: ${f.text??""}
967
+ `);break;case"attachment":{let g=f.fileName??f.attachmentType??"file";process.stdout.write(`[Attachment: ${g}]
919
968
  `);break}case"done":t.prompt();break;case"error":process.stdout.write(`
920
- Error: ${g.text??"Unknown error"}
969
+ Error: ${f.text??"Unknown error"}
921
970
  `),t.prompt();break}}catch{}}}),i.on("end",()=>{if(a.length>0){let c=a.split(`
922
971
  `).find(d=>d.startsWith("data: "));if(c)try{let d=JSON.parse(c.slice(6));(d.type==="done"||d.type==="error")&&t.prompt()}catch{}}}),i.on("error",c=>{console.error(`
923
972
  Connection error: ${c.message}`),t.prompt()})});o.on("error",i=>{console.error(`
924
- Failed to send message: ${i.message}`),t.prompt()}),o.write(n),o.end()}),t.on("close",()=>{process.exit(0)})}async function th(l){let e=new ce,t;try{t=e.loadConfig()}catch(i){console.error("Failed to load configuration:",i.message),process.exit(1)}let s=t.api?.host??"127.0.0.1",r=t.api?.port??3420;if(await Qp(s,r)){console.log(`Connected to Alfred server at ${s}:${r}`),eh(s,r);return}if(t.logger.level="warn",l.model&&(t.llm.default.model=l.model),l.tier){let i=t.llm[l.tier];i?t.llm.default=i:(console.error(`Unknown tier: ${l.tier}. Available tiers: default, strong, fast, embeddings, local`),process.exit(1))}let o=new Ct(t);try{await o.initialize(),await o.startWithCLI()}catch(i){console.error("Failed to start chat:",i.message),process.exit(1)}}var Ad=_(()=>{"use strict";Ve();Qi();m(Qp,"checkHealth");m(eh,"startClientMode");m(th,"chatCommand")});var Rd={};ue(Rd,{setupCommand:()=>mh});import{createInterface as sh}from"node:readline/promises";import{stdin as rh,stdout as nh}from"node:process";import{execFileSync as oh}from"node:child_process";import ye from"node:fs";import ih from"node:os";import ke from"node:path";import na from"js-yaml";function N(l){return`${Xn}${l}${S}`}function lh(l){return`${M}${l}${S}`}function ge(l){return`${Yn}${l}${S}`}function xd(l){return`${ah}${l}${S}`}function C(l){return`${G}${l}${S}`}function b(l){return`${ot}${l}${S}`}function Te(l){return l.length<=4?"****":"*".repeat(l.length-4)+l.slice(-4)}function dh(l){let e=process.platform==="win32",t=e?"where":"which";try{let n=oh(t,[l],{stdio:"pipe"}).toString().trim();if(n)return n.split(/\r?\n/)[0]}catch{}let s=ih.homedir(),r=e?[ke.join(s,".local","bin",`${l}.exe`),ke.join(s,"AppData","Roaming","npm",`${l}.cmd`),ke.join(s,"AppData","Roaming","npm",`${l}`)]:[ke.join(s,".local","bin",l),"/usr/local/bin/"+l,"/opt/homebrew/bin/"+l,ke.join(s,".npm-global","bin",l)];for(let n of r)try{return ye.accessSync(n,ye.constants.X_OK),n}catch{}return null}function uh(l){let e={},t={},s=!1,r=!1,n=30,o=ke.join(l,"config","default.yml");if(ye.existsSync(o))try{let p=na.load(ye.readFileSync(o,"utf-8"));p&&typeof p=="object"&&Object.assign(e,p)}catch{}let i=ke.join(l,".env");if(ye.existsSync(i))try{let p=ye.readFileSync(i,"utf-8").split(`
925
- `);for(let g of p){let f=g.trim();if(!f||f.startsWith("#"))continue;let h=f.indexOf("=");h>0&&(t[f.slice(0,h)]=f.slice(h+1))}}catch{}let a=ke.join(l,"config","rules","default-rules.yml");if(ye.existsSync(a))try{let p=na.load(ye.readFileSync(a,"utf-8"));if(p?.rules){s=p.rules.some(h=>h.id==="allow-owner-admin"&&h.effect==="allow"),p.rules.find(h=>h.id==="allow-write-for-dm"||h.id==="allow-write-all")?.id==="allow-write-all"&&(r=!0);let f=p.rules.find(h=>h.id==="rate-limit-write");f?.rateLimit?.maxInvocations&&(n=f.rateLimit.maxInvocations)}}catch{}let c=!!e.codeSandbox?.enabled,d=e.llm,u={};if(d){for(let p of["strong","fast","embeddings","local"])d[p]?.provider&&d[p]?.model&&(u[p]=d[p]);d.default?.provider&&(e.llm={...e.llm,provider:d.default.provider,model:d.default.model,baseUrl:d.default.baseUrl})}return{config:e,env:t,shellEnabled:s,writeInGroups:r,rateLimit:n,codeSandboxEnabled:c,multiModelTiers:u}}async function mh(){let l=sh({input:rh,output:nh}),e=process.cwd(),t=uh(e),s=Object.keys(t.config).length>0;try{ph(),console.log(s?`${Yn}Existing configuration found \u2014 press Enter to keep current values.${S}
926
- ${ot}Only change what you need to update.${S}
927
- `:`${Yn}Welcome to the Alfred setup wizard!${S}
928
- ${ot}This will walk you through configuring your AI assistant.${S}
929
- ${ot}Press Enter to accept defaults shown in [brackets].${S}
930
- `);let r=await F(l,"What should your bot be called?",t.config.name??"Alfred"),n=t.config.llm?.provider?Ze.findIndex(T=>T.name===t.config.llm?.provider):-1,o=n>=0?n+1:1;console.log(`
931
- ${C("Which LLM provider would you like to use?")}`);for(let T=0;T<Ze.length;T++){let k=T===n?` ${b("(current)")}`:"";console.log(` ${ge(String(T+1)+")")} ${Ze[T].label}${k}`)}let i=await Ur(l,"> ",1,Ze.length,o),a=Ze[i-1];console.log(` ${N(">")} Selected: ${C(a.label)}`);let c="",d=t.env[a.envKeyName]??"";a.needsApiKey&&(console.log(""),d?c=await F(l,`${a.name.charAt(0).toUpperCase()+a.name.slice(1)} API key`,d):c=await xe(l,`Enter your ${a.name.charAt(0).toUpperCase()+a.name.slice(1)} API key`),console.log(` ${N(">")} API key set: ${b(Te(c))}`));let u=a.baseUrl??"";if(["ollama","openwebui","openai","openrouter","google"].includes(a.name)){let k=(t.config.llm?.baseUrl??t.env.ALFRED_LLM_BASE_URL??"")||a.baseUrl||"";if(k){let I={ollama:"Ollama URL (use a remote address if Ollama runs on another machine)",openwebui:"OpenWebUI URL",openai:"OpenAI-compatible API URL (leave default for official API)",openrouter:"OpenRouter API URL",google:"Google Gemini API URL (leave default for official API)"};console.log(""),u=await F(l,I[a.name]??"API Base URL",k.replace(/\/+$/,"")),u=u.replace(/\/+$/,""),console.log(` ${N(">")} URL: ${b(u)}`)}}let g=t.config.llm?.model??a.defaultModel;console.log("");let f,h=await ta(a.name,c,u),y=sa(h,a.models??[]);if(y.length>0){console.log(`${C("Available models:")}`);for(let I=0;I<y.length;I++){let z=y[I],L=z.desc??z.name??"",H=z.id===g?` ${N("(current)")}`:"";console.log(` ${ge(`${I+1})`)} ${z.id}${L?` ${b(`\u2014 ${L}`)}`:""}${H}`)}console.log(` ${ge(`${y.length+1})`)} ${b("Other (enter manually)")}`);let T=await F(l,"Choose model","1"),k=parseInt(T,10)-1;k>=0&&k<y.length?f=y[k].id:f=await F(l,"Model ID",g)}else f=await F(l,"Which model?",g);let E=Object.keys(t.multiModelTiers).length>0,$=E?"Y/n":"y/N";console.log(`
932
- ${C("Configure additional model tiers for specialized tasks?")}`),console.log(`${b("Optional: use different models for complex tasks, quick replies, embeddings, or offline.")}`);let v=(await l.question(`${M}> ${S}${b(`[${$}] `)}`)).trim().toLowerCase(),P=v===""?E:v==="y"||v==="yes",x={};if(P){let T=[{key:"strong",label:"Strong",hint:"complex reasoning, coding, long documents",defaultModel:"claude-opus-4-20250514"},{key:"fast",label:"Fast",hint:"quick responses, simple tasks",defaultModel:"claude-haiku-4-5-20251001"},{key:"embeddings",label:"Embeddings",hint:"semantic search & memory",defaultModel:"text-embedding-3-small"},{key:"local",label:"Local",hint:"offline fallback via Ollama",defaultModel:"llama3.2"}];for(let k of T){let I=t.multiModelTiers[k.key],z=!!I?.model;console.log(`
933
- ${C(`${k.label} model`)} ${b(`(${k.hint})`)}`),z&&console.log(` ${b(`Current: ${I.provider}/${I.model}`)}`);let L=I?.provider??a.name,H=Ze.map(Ae=>Ae.name).join(", ");console.log(` ${b(`Providers: ${H}`)}`);let be=(await l.question(` ${M}Provider: ${S}${b(`[${L}] `)}`)).trim()||L;if(!be&&!z){console.log(` ${b("Skipped.")}`);continue}let K=be,Ee,De;if(K!==a.name){let Ae=I?.apiKey??t.env[`ALFRED_LLM_${k.key.toUpperCase()}_API_KEY`]??"";if(Ae?Ee=await F(l,` API key for ${K}`,Ae):(Ze.find(Ge=>Ge.name===K)?.needsApiKey??!0)&&(Ee=await xe(l,` API key for ${K}`)),["ollama","openwebui"].includes(K)){let Ge=(I?.baseUrl??"")||Ze.find(rn=>rn.name===K)?.baseUrl||"";Ge&&(De=await F(l,` ${K} URL`,Ge))}}let ft=Ee??(K===a.name?c:void 0),Ms=De??(K===a.name?u:void 0),ko=Ze.find(Ae=>Ae.name===K),vo=await ta(K,ft,Ms),et=sa(vo,ko?.models??[]),We;if(et.length>0){console.log(` ${C("Available models:")}`);for(let Nt=0;Nt<et.length;Nt++){let Ge=et[Nt],rn=Ge.desc??Ge.name??"",bu=Ge.id===I?.model?` ${N("(current)")}`:"";console.log(` ${ge(`${Nt+1})`)} ${Ge.id}${rn?` ${b(`\u2014 ${rn}`)}`:""}${bu}`)}console.log(` ${ge(`${et.length+1})`)} ${b("Other (enter manually)")}`),console.log(` ${ge("0)")} ${b("Skip this tier")}`);let Ae=(await l.question(` ${M}> ${S}${z?b(`[${I.model}] `):""}`)).trim();if(Ae==="0"){console.log(` ${b("Skipped.")}`);continue}let sn=parseInt(Ae,10)-1;sn>=0&&sn<et.length?We=et[sn].id:!Ae&&z?We=I.model:We=await F(l," Model ID",z?I.model:k.defaultModel)}else if(console.log(` ${b("Press Enter to skip.")}`),We=(await l.question(` ${M}Model: ${S}${z?b(`[${I.model}] `):""}`)).trim()||(z?I.model:""),!We){console.log(` ${b("Skipped.")}`);continue}x[k.key]={provider:K,model:We,...Ee?{apiKey:Ee}:{},...De?{baseUrl:De}:{}},console.log(` ${N(">")} ${k.label}: ${C(K)}/${C(We)}`)}Object.keys(x).length===0&&console.log(`
934
- ${b("No additional tiers configured \u2014 using single model.")}`)}else console.log(` ${b("Using single model for all tasks.")}`);let q=["brave","tavily","duckduckgo","searxng"],J=t.config.search?.provider??t.env.ALFRED_SEARCH_PROVIDER??"",oe=q.indexOf(J),O=oe>=0?oe+1:0;console.log(`
935
- ${C("Web Search provider (for searching the internet):")}`);let se=["Brave Search \u2014 recommended, free tier (2,000/month)","Tavily \u2014 built for AI agents, free tier (1,000/month)","DuckDuckGo \u2014 free, no API key needed","SearXNG \u2014 self-hosted, no API key needed"],$e=m(T=>oe===T?` ${b("(current)")}`:"","mark");console.log(` ${ge("0)")} None (disable web search)${oe===-1&&J===""?` ${b("(current)")}`:""}`);for(let T=0;T<se.length;T++)console.log(` ${ge(String(T+1)+")")} ${se[T]}${$e(T)}`);let re=await Ur(l,"> ",0,q.length,O),j,ee="",we="";if(re>=1&&re<=q.length&&(j=q[re-1]),j==="brave"){let T=t.env.ALFRED_SEARCH_API_KEY??"";T?ee=await F(l," Brave Search API key",T):(console.log(` ${b("Get your free API key at: https://brave.com/search/api/")}`),ee=await xe(l," Brave Search API key")),console.log(` ${N(">")} Brave Search: ${b(Te(ee))}`)}else if(j==="tavily"){let T=t.env.ALFRED_SEARCH_API_KEY??"";T?ee=await F(l," Tavily API key",T):(console.log(` ${b("Get your free API key at: https://tavily.com/")}`),ee=await xe(l," Tavily API key")),console.log(` ${N(">")} Tavily: ${b(Te(ee))}`)}else if(j==="duckduckgo")console.log(` ${N(">")} DuckDuckGo: ${b("no API key needed")}`);else if(j==="searxng"){let T=t.config.search?.baseUrl??t.env.ALFRED_SEARCH_BASE_URL??"http://localhost:8080";we=await F(l," SearXNG URL",T),we=we.replace(/\/+$/,""),console.log(` ${N(">")} SearXNG: ${b(we)}`)}else console.log(` ${b("Web search disabled \u2014 you can configure it later.")}`);let D=[];for(let T=0;T<ws.length;T++){let k=ws[T];t.config[k.configKey]?.enabled&&D.push(T+1)}let B=D.length>0?D.join(","):"";console.log(`
936
- ${C("Which messaging platforms do you want to enable?")}`),console.log(`${b("(Enter comma-separated numbers, e.g. 1,3)")}`);for(let T=0;T<ws.length;T++){let k=D.includes(T+1)?` ${b("(enabled)")}`:"";console.log(` ${ge(String(T+1)+")")} ${ws[T].label}${k}`)}console.log(` ${ge("0)")} None (configure later)`);let _e=(await l.question(`${M}> ${S}${B?b(`[${B}] `):""}`)).trim(),ie=[],Be=_e||B;if(Be&&Be!=="0"){let T=Be.split(",").map(k=>parseInt(k.trim(),10));for(let k of T)if(k>=1&&k<=ws.length){let I=ws[k-1];ie.includes(I)||ie.push(I)}}ie.length>0?console.log(` ${N(">")} Enabling: ${ie.map(T=>C(T.label)).join(", ")}`):console.log(` ${b("No platforms selected \u2014 you can configure them later.")}`);let le={},jr={};for(let T of ie){if(T.credentials.length===0){T.name==="whatsapp"&&console.log(`
937
- ${lh("i")} WhatsApp: a QR code will be displayed on first start.`);continue}console.log(`
938
- ${C(T.label+" configuration:")}`);let k={};for(let I of T.credentials){let z=t.env[I.envKey]??"",L;z?L=await F(l,` ${I.prompt}`,z):I.defaultValue?L=await F(l,` ${I.prompt}`,I.defaultValue):I.required?L=await xe(l,` ${I.prompt}`):(L=(await l.question(` ${I.prompt}: ${M}`)).trim(),process.stdout.write(S)),k[I.configField]=L,jr[I.envKey]=L,I.configField==="token"||I.configField==="accessToken"?console.log(` ${N(">")} Set: ${b(Te(L))}`):console.log(` ${N(">")} Set: ${b(L)}`)}le[T.configKey]=k}let V=t.config.email?.accounts??[],X=V[0],de=X?.auth?.user??t.config.email?.auth?.user??t.env.ALFRED_EMAIL_USER??"",ae=X?.provider??t.config.email?.provider??t.env.ALFRED_EMAIL_PROVIDER??"",it=V.length>0||!!de||ae==="microsoft",ve=it?"Y/n":"y/N";console.log(`
939
- ${C("Email access (read & send emails)?")}`),console.log(`${b("Works with Gmail, Outlook, Microsoft 365, or any IMAP/SMTP provider. Supports multiple accounts.")}`);let Br=(await l.question(`${M}> ${S}${b(`[${ve}] `)}`)).trim().toLowerCase(),Hr=Br===""?it:Br==="y"||Br==="yes",He=[],Vd={"gmail.com":{imap:"imap.gmail.com",smtp:"smtp.gmail.com"},"googlemail.com":{imap:"imap.gmail.com",smtp:"smtp.gmail.com"},"outlook.com":{imap:"outlook.office365.com",smtp:"smtp.office365.com"},"hotmail.com":{imap:"outlook.office365.com",smtp:"smtp.office365.com"},"live.com":{imap:"outlook.office365.com",smtp:"smtp.office365.com"},"yahoo.com":{imap:"imap.mail.yahoo.com",smtp:"smtp.mail.yahoo.com"},"icloud.com":{imap:"imap.mail.me.com",smtp:"smtp.mail.me.com"},"me.com":{imap:"imap.mail.me.com",smtp:"smtp.mail.me.com"},"gmx.de":{imap:"imap.gmx.net",smtp:"mail.gmx.net"},"gmx.net":{imap:"imap.gmx.net",smtp:"mail.gmx.net"},"web.de":{imap:"imap.web.de",smtp:"smtp.web.de"},"posteo.de":{imap:"posteo.de",smtp:"posteo.de"},"mailbox.org":{imap:"imap.mailbox.org",smtp:"smtp.mailbox.org"},"protonmail.com":{imap:"127.0.0.1",smtp:"127.0.0.1"},"proton.me":{imap:"127.0.0.1",smtp:"127.0.0.1"}},ca=m(async(T,k)=>{let I={name:T,provider:"imap-smtp",user:"",pass:"",imapHost:"",imapPort:993,smtpHost:"",smtpPort:587,msClientId:"",msClientSecret:"",msTenantId:"",msRefreshToken:""},z=k?.provider??"",L=["IMAP/SMTP (classic)","Microsoft 365 (Graph API, OAuth)"],H=z==="microsoft"?1:0;console.log("");for(let K=0;K<L.length;K++){let Ee=K===H?` ${b("(current)")}`:"";console.log(` ${ge(`${K+1})`)} ${L[K]}${Ee}`)}let Y=(await l.question(`${M}> ${S}${b(`[${H+1}] `)}`)).trim(),be=Y===""?H:parseInt(Y,10)-1;if(I.provider=be===1?"microsoft":"imap-smtp",I.provider==="microsoft"){let K=t.config.calendar?.microsoft,Ee=k?.microsoft?.clientId??t.env.ALFRED_MICROSOFT_EMAIL_CLIENT_ID??"",De=k?.microsoft?.tenantId??t.env.ALFRED_MICROSOFT_EMAIL_TENANT_ID??"",ft=k?.microsoft?.refreshToken??t.env.ALFRED_MICROSOFT_EMAIL_REFRESH_TOKEN??"";if(K&&!Ee)console.log(` ${N(">")} Microsoft Calendar already configured \u2014 credentials will be shared.`),console.log(` ${b("The same Azure App Registration will be used for Mail + Calendar.")}`),console.log(` ${b("Ensure the app has Mail.ReadWrite and Mail.Send scopes.")}`);else{console.log(` ${b("Azure Portal \u2192 App Registrations \u2192 your app \u2192 Mail.ReadWrite + Mail.Send scopes")}`),I.msClientId=await F(l," Client ID",Ee),I.msClientId||(I.msClientId=await xe(l," Client ID"));let Ms=t.env.ALFRED_MICROSOFT_EMAIL_CLIENT_SECRET??"";I.msClientSecret=await F(l," Client Secret",Ms),I.msClientSecret||(I.msClientSecret=await xe(l," Client Secret")),I.msTenantId=await F(l," Tenant ID",De),I.msTenantId||(I.msTenantId=await xe(l," Tenant ID")),console.log(` ${b("Tipp: Du kannst `alfred auth microsoft` ausf\xFChren um den Refresh Token automatisch zu holen.")}`),I.msRefreshToken=await F(l," Refresh Token",ft),I.msRefreshToken||(I.msRefreshToken=await xe(l," Refresh Token"))}console.log(` ${N(">")} Email [${T}]: Microsoft 365 (Graph API)`)}else{let K=k?.auth?.user??"";console.log(""),I.user=await F(l," Email address",K||""),I.user||(I.user=await xe(l," Email address"));let Ee=k?.auth?.pass??t.env.ALFRED_EMAIL_PASS??"";Ee?I.pass=await F(l," Password / App password",Ee):(console.log(` ${b("For Gmail: use an App Password (not your regular password)")}`),console.log(` ${b(" \u2192 Google Account \u2192 Security \u2192 2-Step \u2192 App passwords")}`),I.pass=await xe(l," Password / App password"));let De=I.user.split("@")[1]?.toLowerCase()??"",ft=Vd[De],Ms=k?.imap?.host??ft?.imap??`imap.${De}`,ko=k?.smtp?.host??ft?.smtp??`smtp.${De}`,vo=k?.imap?.port??993,et=k?.smtp?.port??587;ft&&console.log(` ${N(">")} Detected ${De} \u2014 using preset server settings`),I.imapHost=await F(l," IMAP server",Ms);let We=await F(l," IMAP port",String(vo));I.imapPort=parseInt(We,10)||993,I.smtpHost=await F(l," SMTP server",ko);let Ae=await F(l," SMTP port",String(et));I.smtpPort=parseInt(Ae,10)||587,console.log(` ${N(">")} Email [${T}]: ${b(I.user)} via ${b(I.imapHost)}`)}return I},"configureEmailAccount");if(Hr){let T=X??(t.config.email?.auth?{provider:t.config.email.provider,auth:t.config.email.auth,imap:t.config.email.imap,smtp:t.config.email.smtp,microsoft:t.config.email.microsoft}:void 0);He.push(await ca("default",T));let k=!0;for(;k;){let I=(await l.question(`
940
- ${C("Add another email account?")} ${b("[y/N]")} `)).trim().toLowerCase();if(I==="y"||I==="yes"){let L=(await l.question(` ${C("Account name:")} `)).trim()||`account${He.length+1}`,H=V.find(Y=>Y.name===L);He.push(await ca(L,H))}else k=!1}}else console.log(` ${b("Email disabled \u2014 you can configure it later.")}`);let qr=["openai","groq"],Kd=t.config.speech?.provider??t.env.ALFRED_SPEECH_PROVIDER??"",zr=qr.indexOf(Kd),Xd=zr>=0?zr+1:0;console.log(`
941
- ${C("Voice message transcription (Speech-to-Text via Whisper)?")}`),console.log(`${b("Transcribes voice messages from Telegram, Discord, etc.")}`);let la=["OpenAI Whisper \u2014 best quality","Groq Whisper \u2014 fast & free"];console.log(` ${ge("0)")} None (disable voice transcription)${zr===-1?` ${b("(current)")}`:""}`);for(let T=0;T<la.length;T++){let k=zr===T?` ${b("(current)")}`:"";console.log(` ${ge(String(T+1)+")")} ${la[T]}${k}`)}let Zn=await Ur(l,"> ",0,qr.length,Xd),qe,Qe="",Ts="";if(Zn>=1&&Zn<=qr.length&&(qe=qr[Zn-1]),qe==="openai"){let T=t.env.ALFRED_SPEECH_API_KEY??"";T?Qe=await F(l," OpenAI API key (for Whisper)",T):(console.log(` ${b("Uses your OpenAI API key for Whisper transcription.")}`),Qe=await xe(l," OpenAI API key")),console.log(` ${N(">")} OpenAI Whisper: ${b(Te(Qe))}`)}else if(qe==="groq"){let T=t.env.ALFRED_SPEECH_API_KEY??"";T?Qe=await F(l," Groq API key",T):(console.log(` ${b("Get your free API key at: https://console.groq.com/")}`),Qe=await xe(l," Groq API key"));let k=t.env.ALFRED_SPEECH_BASE_URL??"";k&&(Ts=await F(l," Groq API URL",k)),console.log(` ${N(">")} Groq Whisper: ${b(Te(Qe))}`)}else console.log(` ${b("Voice transcription disabled \u2014 you can configure it later.")}`);let _s=!1,bs="alloy";if(qe){let T=t.config.speech?.ttsEnabled??!1,k=T?"Y/n":"y/N";console.log(`
942
- ${C("Voice responses (Text-to-Speech)?")}`),console.log(`${b("Alfred can reply as a voice message when the user asks for it.")}`);let I=(await l.question(`${M}> ${S}${b(`[${k}] `)}`)).trim().toLowerCase();if(_s=I===""?T:I==="y"||I==="yes",_s){let z=["alloy","echo","fable","onyx","nova","shimmer"],L=t.config.speech?.ttsVoice??"alloy",H=z.indexOf(L),Y=H>=0?H+1:1;console.log(`
943
- ${C("Which voice?")}`);for(let K=0;K<z.length;K++){let Ee=H===K?` ${b("(current)")}`:"";console.log(` ${ge(String(K+1)+")")} ${z[K]}${Ee}`)}let be=await Ur(l," > ",1,z.length,Y);bs=z[be-1],console.log(` ${N(">")} TTS voice: ${C(bs)}`)}else console.log(` ${b("Voice responses disabled.")}`)}let Yd=t.codeSandboxEnabled?"Y/n":"y/N";console.log(`
944
- ${C("Code Sandbox (execute Python/JavaScript in a sandboxed environment)?")}`),console.log(`${b("Enables code execution for calculations, data processing, PDF generation, charts, etc.")}`);let Qn=(await l.question(`${M}> ${S}${b(`[${Yd}] `)}`)).trim().toLowerCase(),eo=Qn===""?t.codeSandboxEnabled:Qn==="y"||Qn==="yes";console.log(eo?` ${N(">")} Code Sandbox ${C("enabled")} (JavaScript + Python)`:` ${b("Code Sandbox disabled \u2014 you can enable it later in config/default.yml.")}`),console.log(`
945
- ${C("Code Agents (CLI-based coding agents for automated tasks)?")}`),console.log(`${b("Scanning for known coding agents on this system...")}`);let to=[];for(let T of Id){let k=dh(T.whichCmd);k?(to.push({...T,resolvedPath:k}),console.log(` ${N("\u2713")} ${C(T.label)} ${b(`(${k})`)}`)):console.log(` ${b("\xB7")} ${b(T.label)} ${b("\u2014 not found")}`)}let so=(t.config.codeAgents?.agents??[]).filter(T=>!Id.some(k=>k.name===T.name));for(let T of so)console.log(` ${N("\u2713")} ${C(T.name)} ${b(`(${T.command}) \u2014 from existing config`)}`);let at=[];if(to.length===0&&so.length===0)console.log(`
946
- ${b("No coding agents found. You can add them manually in config/default.yml later.")}`);else{let T=[...to.map(L=>({name:L.name,command:L.resolvedPath??L.command,argsTemplate:L.argsTemplate,promptVia:L.promptVia,label:L.label,detected:!0})),...so.map(L=>({name:L.name,command:L.command,argsTemplate:L.argsTemplate,promptVia:L.promptVia??"arg",label:L.name,detected:!1}))];console.log(`
947
- ${C("Which agents should Alfred use?")} ${b("(comma-separated, e.g. 1,2)")}`);let k=new Set((t.config.codeAgents?.agents??[]).map(L=>L.name));for(let L=0;L<T.length;L++){let H=T[L],Y=k.has(H.name)?` ${b("(current)")}`:"";console.log(` ${M}${L+1}${S}) ${H.label}${Y}`)}console.log(` ${M}0${S}) None`);let I=T.map((L,H)=>k.size>0?k.has(L.name)?String(H+1):null:L.detected?String(H+1):null).filter(Boolean).join(",")||"0",z=(await l.question(` ${M}> ${S}${b(`[${I}] `)}`)).trim()||I;z!=="0"&&(at=z.split(",").map(H=>parseInt(H.trim(),10)).filter(H=>!isNaN(H)&&H>=1&&H<=T.length).map(H=>{let Y=T[H-1];return{name:Y.name,command:Y.command,argsTemplate:Y.argsTemplate,promptVia:Y.promptVia}})),at.length>0?console.log(` ${N(">")} ${C(String(at.length))} agent(s) selected: ${at.map(L=>L.name).join(", ")}`):console.log(` ${b("No agents selected.")}`)}let ro=t.config.codeAgents?.forge,no=ro?.provider??t.env.ALFRED_FORGE_PROVIDER??"";console.log(`
948
- ${C("Forge Integration (auto-create PRs/MRs after code agent orchestration)?")}`),console.log(`${b("Connects to GitHub or GitLab to push branches and create pull/merge requests.")}`),console.log(`${b("Owner/repo are detected automatically from the git remote at runtime.")}`);let da=[{num:"1",name:"",label:"None \u2014 skip forge integration"},{num:"2",name:"github",label:"GitHub"},{num:"3",name:"gitlab",label:"GitLab"}];for(let T of da){let k=T.name===no?` ${b("(current)")}`:"";console.log(` ${M}${T.num}${S}) ${T.label}${k}`)}let ua=no==="github"?"2":no==="gitlab"?"3":"1",Jd=(await l.question(`${M}> ${S}${b(`[${ua}] `)}`)).trim()||ua,ct=da.find(T=>T.num===Jd)?.name??"",Es="",$s="";if(ct==="github"){console.log(` ${N(">")} Forge: ${C("GitHub")}`);let T=t.env.ALFRED_GITHUB_TOKEN??ro?.github?.token??"";T&&console.log(` ${b(`Current token: ${Te(T)}`)}`),console.log(` ${b("Create a token at https://github.com/settings/tokens (scope: repo)")}`),Es=(await l.question(` ${G}GitHub Token${S}: ${M}`)).trim(),process.stdout.write(S),!Es&&T&&(Es=T)}else if(ct==="gitlab"){console.log(` ${N(">")} Forge: ${C("GitLab")}`);let T=t.env.ALFRED_GITLAB_TOKEN??ro?.gitlab?.token??"";T&&console.log(` ${b(`Current token: ${Te(T)}`)}`),console.log(` ${b("Create a token at https://gitlab.com/-/user_settings/personal_access_tokens (scope: api)")}`),$s=(await l.question(` ${G}GitLab Token${S}: ${M}`)).trim(),process.stdout.write(S),!$s&&T&&($s=T)}else console.log(` ${b("Forge integration disabled \u2014 you can enable it later in config/default.yml.")}`);console.log(`
949
- ${C("Infrastructure Management (Proxmox / UniFi / Home Assistant)?")}`),console.log(`${b("Control VMs, containers, network devices, and smart home through Alfred.")}`);let ks=t.config.proxmox,oo=t.env.ALFRED_PROXMOX_BASE_URL??ks?.baseUrl??"",Zd=oo?"Y/n":"y/N",ma=(await l.question(` ${G}Enable Proxmox VE?${S} ${b(`[${Zd}]`)}: ${M}`)).trim().toLowerCase()||(oo?"y":"n");process.stdout.write(S);let Wr=ma==="y"||ma==="yes",vs="",io="",Ss="",ao=!0;if(Wr){vs=await F(l," Proxmox URL (e.g. https://pve.local:8006)",oo||"https://pve.local:8006");let T=t.env.ALFRED_PROXMOX_TOKEN_ID??ks?.tokenId??"";T&&console.log(` ${b(`Current token ID: ${T}`)}`),console.log(` ${b("Create: Datacenter \u2192 Permissions \u2192 API Tokens")}`),io=await F(l," API Token ID (user@realm!name)",T);let k=t.env.ALFRED_PROXMOX_TOKEN_SECRET??ks?.tokenSecret??"";k&&console.log(` ${b(`Current secret: ${Te(k)}`)}`),Ss=(await l.question(` ${G}API Token Secret${S}: ${M}`)).trim(),process.stdout.write(S),!Ss&&k&&(Ss=k);let I=ks?.verifyTls===!1?"y/N":"Y/n",z=(await l.question(` ${G}Verify TLS?${S} ${b(`(self-signed? \u2192 no) [${I}]`)}: ${M}`)).trim().toLowerCase()||(ks?.verifyTls===!1?"n":"y");process.stdout.write(S),ao=z==="y"||z==="yes",console.log(` ${N(">")} Proxmox: ${C(vs)} ${b(`(TLS verify: ${ao?"yes":"no"})`)}`)}else console.log(` ${b("Proxmox disabled.")}`);let lt=t.config.unifi,co=t.env.ALFRED_UNIFI_BASE_URL??lt?.baseUrl??"",Qd=co?"Y/n":"y/N",pa=(await l.question(`
950
- ${G}Enable UniFi Network?${S} ${b(`[${Qd}]`)}: ${M}`)).trim().toLowerCase()||(co?"y":"n");process.stdout.write(S);let As=pa==="y"||pa==="yes",Lt="",dt="",lo="",Is="",uo=!0;if(As){Lt=await F(l," UniFi URL (e.g. https://unifi.local)",co||"https://unifi.local"),console.log(` ${b("Auth: API Key (recommended) or Username/Password")}`);let T=t.env.ALFRED_UNIFI_API_KEY??lt?.apiKey??"",k=[{num:"1",name:"apikey",label:"API Key (UniFi OS)"},{num:"2",name:"password",label:"Username / Password"}],I=T?"1":lt?.username?"2":"1";for(let Y of k)console.log(` ${M}${Y.num}${S}) ${Y.label}`);if(((await l.question(` ${M}> ${S}${b(`[${I}] `)}`)).trim()||I)==="1")T&&console.log(` ${b(`Current key: ${Te(T)}`)}`),console.log(` ${b("Create: Settings \u2192 Admins \u2192 API Keys (UniFi OS)")}`),dt=(await l.question(` ${G}API Key${S}: ${M}`)).trim(),process.stdout.write(S),!dt&&T&&(dt=T);else{let Y=t.env.ALFRED_UNIFI_USERNAME??lt?.username??"";lo=await F(l," Username",Y||"alfred");let be=t.env.ALFRED_UNIFI_PASSWORD??lt?.password??"";be&&console.log(` ${b(`Current password: ${Te(be)}`)}`),Is=(await l.question(` ${G}Password${S}: ${M}`)).trim(),process.stdout.write(S),!Is&&be&&(Is=be)}let L=lt?.verifyTls===!1?"y/N":"Y/n",H=(await l.question(` ${G}Verify TLS?${S} ${b(`(self-signed? \u2192 no) [${L}]`)}: ${M}`)).trim().toLowerCase()||(lt?.verifyTls===!1?"n":"y");process.stdout.write(S),uo=H==="y"||H==="yes",console.log(` ${N(">")} UniFi: ${C(Lt)} ${b(`(TLS verify: ${uo?"yes":"no"})`)}`)}else console.log(` ${b("UniFi disabled.")}`);let Gr=t.config.homeassistant,mo=t.env.ALFRED_HOMEASSISTANT_URL??Gr?.baseUrl??"",eu=mo?"Y/n":"y/N",ha=(await l.question(`
951
- ${G}Enable Home Assistant?${S} ${b(`[${eu}]`)}: ${M}`)).trim().toLowerCase()||(mo?"y":"n");process.stdout.write(S);let Vr=ha==="y"||ha==="yes",xs="",Rs="",po=!0;if(Vr){xs=await F(l," Home Assistant URL (e.g. http://homeassistant.local:8123)",mo||"http://homeassistant.local:8123");let T=t.env.ALFRED_HOMEASSISTANT_TOKEN??Gr?.accessToken??"";T&&console.log(` ${b(`Current token: ${Te(T)}`)}`),console.log(` ${b("Create: Settings \u2192 Security \u2192 Long-Lived Access Tokens")}`),Rs=(await l.question(` ${G}Long-Lived Access Token${S}: ${M}`)).trim(),process.stdout.write(S),!Rs&&T&&(Rs=T);let k=Gr?.verifyTls===!1?"y/N":"Y/n",I=(await l.question(` ${G}Verify TLS?${S} ${b(`(self-signed? \u2192 no) [${k}]`)}: ${M}`)).trim().toLowerCase()||(Gr?.verifyTls===!1?"n":"y");process.stdout.write(S),po=I==="y"||I==="yes",console.log(` ${N(">")} Home Assistant: ${C(xs)} ${b(`(TLS verify: ${po?"yes":"no"})`)}`)}else console.log(` ${b("Home Assistant disabled.")}`);let Re=t.config.contacts,ho=t.env.ALFRED_CONTACTS_PROVIDER??Re?.provider??"",tu=ho?"Y/n":"y/N",fa=(await l.question(`
952
- ${G}Enable Contacts management?${S} ${b(`[${tu}]`)}: ${M}`)).trim().toLowerCase()||(ho?"y":"n");process.stdout.write(S);let Kr=fa==="y"||fa==="yes",Ce="",he={};if(Kr){let T=["carddav","google","microsoft"],k=T.indexOf(ho),I=k>=0?k+1:1;console.log(` ${ge("1)")} CardDAV (Nextcloud, Radicale, etc.)`),console.log(` ${ge("2)")} Google Contacts`),console.log(` ${ge("3)")} Microsoft 365`);let z=await Ur(l," > ",1,3,I);if(Ce=T[z-1],he.ALFRED_CONTACTS_PROVIDER=Ce,Ce==="carddav"){let L=t.env.ALFRED_CARDDAV_CONTACTS_SERVER_URL??Re?.carddav?.serverUrl??"";he.ALFRED_CARDDAV_CONTACTS_SERVER_URL=await F(l," CardDAV Server URL",L||"https://cloud.example.com/remote.php/dav");let H=t.env.ALFRED_CARDDAV_CONTACTS_USERNAME??Re?.carddav?.username??"";he.ALFRED_CARDDAV_CONTACTS_USERNAME=await F(l," Username",H);let Y=t.env.ALFRED_CARDDAV_CONTACTS_PASSWORD??Re?.carddav?.password??"";Y&&console.log(` ${b(`Current password: ${Te(Y)}`)}`);let be=(await l.question(` ${G}Password${S}: ${M}`)).trim();process.stdout.write(S),he.ALFRED_CARDDAV_CONTACTS_PASSWORD=be||Y}else if(Ce==="google"){let L=t.env.ALFRED_GOOGLE_CONTACTS_CLIENT_ID??Re?.google?.clientId??"";L&&console.log(` ${b(`Current client ID: ${Te(L)}`)}`),he.ALFRED_GOOGLE_CONTACTS_CLIENT_ID=(await l.question(` ${G}Google Client ID${S}: ${M}`)).trim()||L,process.stdout.write(S);let H=t.env.ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET??Re?.google?.clientSecret??"";he.ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET=(await l.question(` ${G}Google Client Secret${S}: ${M}`)).trim()||H,process.stdout.write(S);let Y=t.env.ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN??Re?.google?.refreshToken??"";he.ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN=(await l.question(` ${G}Refresh Token${S}: ${M}`)).trim()||Y,process.stdout.write(S)}else if(Ce==="microsoft"){let L=t.env.ALFRED_MICROSOFT_CONTACTS_CLIENT_ID??Re?.microsoft?.clientId??"";L&&console.log(` ${b(`Current client ID: ${Te(L)}`)}`),he.ALFRED_MICROSOFT_CONTACTS_CLIENT_ID=(await l.question(` ${G}Microsoft Client ID${S}: ${M}`)).trim()||L,process.stdout.write(S);let H=t.env.ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET??Re?.microsoft?.clientSecret??"";he.ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET=(await l.question(` ${G}Microsoft Client Secret${S}: ${M}`)).trim()||H,process.stdout.write(S);let Y=t.env.ALFRED_MICROSOFT_CONTACTS_TENANT_ID??Re?.microsoft?.tenantId??"";he.ALFRED_MICROSOFT_CONTACTS_TENANT_ID=await F(l," Tenant ID",Y||"common");let be=t.env.ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN??Re?.microsoft?.refreshToken??"";console.log(` ${b("Tipp: Du kannst `alfred auth microsoft` ausf\xFChren um den Refresh Token automatisch zu holen.")}`),he.ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN=(await l.question(` ${G}Refresh Token${S}: ${M}`)).trim()||be,process.stdout.write(S)}console.log(` ${N(">")} Contacts: ${C(Ce)}`)}else console.log(` ${b("Contacts disabled.")}`);let ga=t.config.docker,fo=t.env.ALFRED_DOCKER_SOCKET_PATH??ga?.socketPath??"",go=t.env.ALFRED_DOCKER_HOST??ga?.host??"",su=fo||go?"Y/n":"y/N",ya=(await l.question(`
953
- ${G}Enable Docker management?${S} ${b(`[${su}]`)}: ${M}`)).trim().toLowerCase()||(fo||go?"y":"n");process.stdout.write(S);let Xr=ya==="y"||ya==="yes",ut="",mt="";if(Xr){let T=process.platform==="win32"?"//./pipe/docker_engine":"/var/run/docker.sock";console.log(` ${b("Use socket path for local Docker, or host URL for remote.")}`),ut=await F(l," Docker socket path",fo||T);let k=(await l.question(` ${G}Docker host URL (optional, for remote)${S}: ${M}`)).trim();process.stdout.write(S),mt=k||go,console.log(` ${N(">")} Docker: ${C(mt||ut)}`)}else console.log(` ${b("Docker disabled.")}`);let ru=t.config.bmw,yo=t.env.ALFRED_BMW_CLIENT_ID??ru?.clientId??"",nu=yo?"Y/n":"y/N",wa=(await l.question(`
954
- ${G}Enable BMW CarData (vehicle status, charging)?${S} ${b(`[${nu}]`)}: ${M}`)).trim().toLowerCase()||(yo?"y":"n");process.stdout.write(S);let Yr=wa==="y"||wa==="yes",wo="";Yr?(console.log(` ${b("Setup:")}`),console.log(` ${b(" 1. \xD6ffne https://bmw-cardata.bmwgroup.com/customer")}`),console.log(` ${b(" 2. Login mit deinem MyBMW-Account")}`),console.log(` ${b(" 3. Client-ID generieren")}`),console.log(` ${b(' 4. Scope "CarData API" aktivieren (ca. 60s warten)')}`),wo=await F(l," BMW CarData Client ID",yo),console.log(` ${N(">")} BMW CarData: ${C("enabled")}`)):console.log(` ${b("BMW CarData disabled.")}`);let ou=t.config.routing,To=t.env.ALFRED_ROUTING_API_KEY??ou?.apiKey??"",iu=To?"Y/n":"y/N",Ta=(await l.question(`
955
- ${G}Enable route planning with live traffic (Google Routes)?${S} ${b(`[${iu}]`)}: ${M}`)).trim().toLowerCase()||(To?"y":"n");process.stdout.write(S);let Jr=Ta==="y"||Ta==="yes",_o="";Jr?(console.log(` ${b("Setup:")}`),console.log(` ${b(" 1. \xD6ffne https://console.cloud.google.com")}`),console.log(` ${b(" 2. Routes API aktivieren")}`),console.log(` ${b(" 3. API Key erstellen")}`),_o=await F(l," Google Maps API Key",To),console.log(` ${N(">")} Routing: ${C("enabled")}`)):console.log(` ${b("Routing disabled.")}`);let ze=t.config.energy,au=t.env.ALFRED_ENERGY_GRID_NAME??ze?.gridName??"",bo=t.env.ALFRED_ENERGY_GRID_USAGE_CT??(ze?.gridUsageCt!=null?String(ze.gridUsageCt):""),cu=t.env.ALFRED_ENERGY_GRID_LOSS_CT??(ze?.gridLossCt!=null?String(ze.gridLossCt):""),lu=t.env.ALFRED_ENERGY_GRID_CAPACITY_FEE??(ze?.gridCapacityFee!=null?String(ze.gridCapacityFee):""),du=t.env.ALFRED_ENERGY_GRID_METER_FEE??(ze?.gridMeterFee!=null?String(ze.gridMeterFee):""),uu=bo?"Y/n":"y/N",_a=(await l.question(`
956
- ${G}Enable energy prices (aWATTar HOURLY / EPEX Spot AT)?${S} ${b(`[${uu}]`)}: ${M}`)).trim().toLowerCase()||(bo?"y":"n");process.stdout.write(S);let Zr=_a==="y"||_a==="yes",pt="",ht="",Qr="",Cs="",Ds="";Zr?(console.log(` ${b('Die Werte findest du auf deiner Stromrechnung unter "Netzentgelte".')}`),console.log(` ${b("Marktpreis + aWATTar-Aufschlag + Abgaben werden automatisch berechnet.")}`),console.log(""),pt=await F(l," Netzbetreiber Name (z.B. Netz Nieder\xF6sterreich)",au),ht=await F(l," Netznutzungsentgelt (ct/kWh netto)",bo),Qr=await F(l," Netzverlustentgelt (ct/kWh netto)",cu||"0.38"),Cs=await F(l," Leistungspauschale (\u20AC/Monat netto)",lu),Ds=await F(l," Messentgelt (\u20AC/Monat netto)",du||"2.22"),console.log(` ${N(">")} Energy: ${C(pt||"enabled")} (${ht} + ${Qr} ct/kWh)`)):console.log(` ${b("Energy prices disabled (Marktpreise weiterhin ohne Netzentgelte verf\xFCgbar).")}`),console.log(`
957
- ${C("Security configuration:")}`);let ba=t.config.security?.ownerUserId??t.env.ALFRED_OWNER_USER_ID??"",Se;if(ba)Se=await F(l,"Owner user ID (for elevated permissions)",ba);else{let T=(await l.question(`${G}Owner user ID${S} ${b("(optional, for elevated permissions)")}: ${M}`)).trim();process.stdout.write(S),Se=T}let Ls=!1;if(Se){let T=t.shellEnabled?"Y/n":"y/N";console.log(""),console.log(` ${C("Enable shell access (admin commands) for the owner?")}`),console.log(` ${b("Allows Alfred to execute shell commands. Only for the owner.")}`);let k=(await l.question(` ${M}> ${S}${b(`[${T}] `)}`)).trim().toLowerCase();k===""?Ls=t.shellEnabled:Ls=k==="y"||k==="yes",console.log(Ls?` ${N(">")} Shell access ${C("enabled")} for owner ${b(Se)}`:` ${b("Shell access disabled.")}`)}let mu=t.writeInGroups?"Y/n":"y/N";console.log(""),console.log(` ${C("Allow write actions (notes, reminders, memory) in group chats?")}`),console.log(` ${b("By default, write actions are only allowed in DMs.")}`);let Eo=(await l.question(` ${M}> ${S}${b(`[${mu}] `)}`)).trim().toLowerCase(),Ns;Eo===""?Ns=t.writeInGroups:Ns=Eo==="y"||Eo==="yes",console.log(Ns?` ${N(">")} Write actions ${C("enabled")} in groups`:` ${b("Write actions only in DMs (default).")}`);let pu=t.rateLimit??30;console.log("");let hu=await F(l," Rate limit (max write actions per hour per user)",String(pu)),en=Math.max(1,parseInt(hu,10)||30);console.log(` ${N(">")} Rate limit: ${C(String(en))} per hour`),console.log(`
958
- ${C("Writing configuration files...")}`);let A=["# Alfred Environment Variables","# Generated by `alfred setup`","","# === LLM ===","",`ALFRED_LLM_PROVIDER=${a.name}`];if(c){let T=a.envKeyName||"ALFRED_OLLAMA_API_KEY";A.push(`${T}=${c}`)}if(f!==a.defaultModel&&A.push(`ALFRED_LLM_MODEL=${f}`),u&&A.push(`ALFRED_LLM_BASE_URL=${u}`),Object.keys(x).length>0){A.push("","# === Additional Model Tiers ===");for(let[T,k]of Object.entries(x)){let I=`ALFRED_LLM_${T.toUpperCase()}`;A.push(""),A.push(`${I}_PROVIDER=${k.provider}`),A.push(`${I}_MODEL=${k.model}`),k.apiKey&&A.push(`${I}_API_KEY=${k.apiKey}`),k.baseUrl&&A.push(`${I}_BASE_URL=${k.baseUrl}`)}}A.push("","# === Messaging Platforms ===","");for(let[T,k]of Object.entries(jr))A.push(`${T}=${k}`);if(A.push("","# === Web Search ===",""),j?(A.push(`ALFRED_SEARCH_PROVIDER=${j}`),ee&&A.push(`ALFRED_SEARCH_API_KEY=${ee}`),we&&A.push(`ALFRED_SEARCH_BASE_URL=${we}`)):(A.push("# ALFRED_SEARCH_PROVIDER=brave"),A.push("# ALFRED_SEARCH_API_KEY=")),A.push("","# === Email ===",""),Hr&&He.length>0){let T=He[0];T.provider==="microsoft"?(A.push("ALFRED_EMAIL_PROVIDER=microsoft"),T.msClientId?(A.push(`ALFRED_MICROSOFT_EMAIL_CLIENT_ID=${T.msClientId}`),A.push(`ALFRED_MICROSOFT_EMAIL_CLIENT_SECRET=${T.msClientSecret}`),A.push(`ALFRED_MICROSOFT_EMAIL_TENANT_ID=${T.msTenantId}`),A.push(`ALFRED_MICROSOFT_EMAIL_REFRESH_TOKEN=${T.msRefreshToken}`)):A.push("# Microsoft email credentials shared from calendar config")):(A.push(`ALFRED_EMAIL_USER=${T.user}`),A.push(`ALFRED_EMAIL_PASS=${T.pass}`)),He.length>1&&A.push("# Additional email accounts configured in config/default.yml")}else A.push("# ALFRED_EMAIL_USER="),A.push("# ALFRED_EMAIL_PASS=");if(A.push("","# === Speech ===",""),qe?(A.push(`ALFRED_SPEECH_PROVIDER=${qe}`),A.push(`ALFRED_SPEECH_API_KEY=${Qe}`),Ts&&A.push(`ALFRED_SPEECH_BASE_URL=${Ts}`),_s&&(A.push("ALFRED_TTS_ENABLED=true"),A.push(`ALFRED_TTS_VOICE=${bs}`))):(A.push("# ALFRED_SPEECH_PROVIDER=groq"),A.push("# ALFRED_SPEECH_API_KEY=")),A.push("","# === Forge (GitHub / GitLab) ===",""),ct==="github"?(A.push("ALFRED_FORGE_PROVIDER=github"),A.push(`ALFRED_GITHUB_TOKEN=${Es}`)):ct==="gitlab"?(A.push("ALFRED_FORGE_PROVIDER=gitlab"),A.push(`ALFRED_GITLAB_TOKEN=${$s}`)):(A.push("# ALFRED_FORGE_PROVIDER=github"),A.push("# ALFRED_GITHUB_TOKEN=")),A.push("","# === Infrastructure (Proxmox / UniFi / Home Assistant) ===",""),Wr?(A.push(`ALFRED_PROXMOX_BASE_URL=${vs}`),A.push(`ALFRED_PROXMOX_TOKEN_ID=${io}`),A.push(`ALFRED_PROXMOX_TOKEN_SECRET=${Ss}`)):(A.push("# ALFRED_PROXMOX_BASE_URL="),A.push("# ALFRED_PROXMOX_TOKEN_ID="),A.push("# ALFRED_PROXMOX_TOKEN_SECRET=")),As&&dt?(A.push(`ALFRED_UNIFI_BASE_URL=${Lt}`),A.push(`ALFRED_UNIFI_API_KEY=${dt}`)):As?(A.push(`ALFRED_UNIFI_BASE_URL=${Lt}`),A.push(`ALFRED_UNIFI_USERNAME=${lo}`),A.push(`ALFRED_UNIFI_PASSWORD=${Is}`)):(A.push("# ALFRED_UNIFI_BASE_URL="),A.push("# ALFRED_UNIFI_API_KEY=")),Vr?(A.push(`ALFRED_HOMEASSISTANT_URL=${xs}`),A.push(`ALFRED_HOMEASSISTANT_TOKEN=${Rs}`)):(A.push("# ALFRED_HOMEASSISTANT_URL="),A.push("# ALFRED_HOMEASSISTANT_TOKEN=")),A.push("","# === Contacts ===",""),Kr)for(let[T,k]of Object.entries(he))A.push(`${T}=${k}`);else A.push("# ALFRED_CONTACTS_PROVIDER=carddav");A.push("","# === Docker ===",""),Xr?(ut&&A.push(`ALFRED_DOCKER_SOCKET_PATH=${ut}`),mt&&A.push(`ALFRED_DOCKER_HOST=${mt}`)):(A.push("# ALFRED_DOCKER_SOCKET_PATH="),A.push("# ALFRED_DOCKER_HOST=")),A.push("","# === BMW CarData ===",""),Yr?A.push(`ALFRED_BMW_CLIENT_ID=${wo}`):A.push("# ALFRED_BMW_CLIENT_ID="),A.push("","# === Routing ===",""),Jr?A.push(`ALFRED_ROUTING_API_KEY=${_o}`):A.push("# ALFRED_ROUTING_API_KEY="),A.push("","# === Energy / aWATTar ===",""),Zr&&ht?(pt&&A.push(`ALFRED_ENERGY_GRID_NAME=${pt}`),A.push(`ALFRED_ENERGY_GRID_USAGE_CT=${ht}`),A.push(`ALFRED_ENERGY_GRID_LOSS_CT=${Qr}`),Cs&&A.push(`ALFRED_ENERGY_GRID_CAPACITY_FEE=${Cs}`),Ds&&A.push(`ALFRED_ENERGY_GRID_METER_FEE=${Ds}`)):(A.push("# ALFRED_ENERGY_GRID_NAME="),A.push("# ALFRED_ENERGY_GRID_USAGE_CT="),A.push("# ALFRED_ENERGY_GRID_LOSS_CT="),A.push("# ALFRED_ENERGY_GRID_CAPACITY_FEE="),A.push("# ALFRED_ENERGY_GRID_METER_FEE=")),A.push("","# === Security ===",""),Se?A.push(`ALFRED_OWNER_USER_ID=${Se}`):A.push("# ALFRED_OWNER_USER_ID="),A.push("");let fu=ke.join(e,".env");ye.writeFileSync(fu,A.join(`
959
- `),"utf-8"),console.log(` ${N("+")} ${b(".env")} written`);let tn=ke.join(e,"config");ye.existsSync(tn)||ye.mkdirSync(tn,{recursive:!0});let Ea={name:r,telegram:{token:le.telegram?.token??"",enabled:ie.some(T=>T.name==="telegram")},discord:{token:le.discord?.token??"",enabled:ie.some(T=>T.name==="discord")},whatsapp:{enabled:ie.some(T=>T.name==="whatsapp"),dataPath:"./data/whatsapp"},matrix:{homeserverUrl:le.matrix?.homeserverUrl??"https://matrix.org",accessToken:le.matrix?.accessToken??"",userId:le.matrix?.userId??"",enabled:ie.some(T=>T.name==="matrix")},signal:{apiUrl:le.signal?.apiUrl??"http://localhost:8080",phoneNumber:le.signal?.phoneNumber??"",enabled:ie.some(T=>T.name==="signal")},llm:Object.keys(x).length>0?{default:{provider:a.name,model:f,...u?{baseUrl:u}:{},temperature:.7,maxTokens:4096},...x}:{provider:a.name,model:f,...u?{baseUrl:u}:{},temperature:.7,maxTokens:4096},...j?{search:{provider:j,...ee?{apiKey:ee}:{},...we?{baseUrl:we}:{}}}:{},...Hr&&He.length>0?{email:{accounts:He.map(T=>T.provider==="microsoft"?{name:T.name,provider:"microsoft",...T.msClientId?{microsoft:{clientId:T.msClientId,clientSecret:T.msClientSecret,tenantId:T.msTenantId,refreshToken:T.msRefreshToken}}:{}}:{name:T.name,imap:{host:T.imapHost,port:T.imapPort,secure:T.imapPort===993},smtp:{host:T.smtpHost,port:T.smtpPort,secure:T.smtpPort===465},auth:{user:T.user,pass:T.pass}})}}:{},...qe?{speech:{provider:qe,apiKey:Qe,...Ts?{baseUrl:Ts}:{},..._s?{ttsEnabled:!0,ttsVoice:bs}:{}}}:{},...eo?{codeSandbox:{enabled:!0,allowedLanguages:["javascript","python"]}}:{},...at.length>0||ct?{codeAgents:{enabled:at.length>0,agents:at,...ct==="github"?{forge:{provider:"github",github:{token:Es}}}:ct==="gitlab"?{forge:{provider:"gitlab",gitlab:{token:$s}}}:{}}}:{},...Wr?{proxmox:{baseUrl:vs,tokenId:io,tokenSecret:Ss,verifyTls:ao}}:{},...As?{unifi:{baseUrl:Lt,...dt?{apiKey:dt}:{username:lo,password:Is},site:"default",verifyTls:uo}}:{},...Vr?{homeassistant:{baseUrl:xs,accessToken:Rs,verifyTls:po}}:{},...Kr?{contacts:{provider:Ce,...Ce==="carddav"?{carddav:{serverUrl:he.ALFRED_CARDDAV_CONTACTS_SERVER_URL,username:he.ALFRED_CARDDAV_CONTACTS_USERNAME}}:Ce==="google"?{google:{clientId:he.ALFRED_GOOGLE_CONTACTS_CLIENT_ID}}:Ce==="microsoft"?{microsoft:{clientId:he.ALFRED_MICROSOFT_CONTACTS_CLIENT_ID,tenantId:he.ALFRED_MICROSOFT_CONTACTS_TENANT_ID}}:{}}}:{},...Xr?{docker:{...ut?{socketPath:ut}:{},...mt?{host:mt}:{}}}:{},...Yr?{bmw:{clientId:wo}}:{},...Jr?{routing:{apiKey:_o}}:{},...Zr&&ht?{energy:{...pt?{gridName:pt}:{},gridUsageCt:parseFloat(ht),gridLossCt:parseFloat(Qr||"0"),...Cs?{gridCapacityFee:parseFloat(Cs)}:{},...Ds?{gridMeterFee:parseFloat(Ds)}:{}}}:{},storage:{path:"./data/alfred.db"},logger:{level:"info",pretty:!0,auditLogPath:"./data/audit.log"},security:{rulesPath:"./config/rules",defaultEffect:"deny"}};Se&&(Ea.security.ownerUserId=Se);let gu="# Alfred \u2014 Configuration\n# Generated by `alfred setup`\n# Edit manually or re-run `alfred setup` to reconfigure.\n\n"+na.dump(Ea,{lineWidth:120,noRefs:!0,sortKeys:!1}),yu=ke.join(tn,"default.yml");ye.writeFileSync(yu,gu,"utf-8"),console.log(` ${N("+")} ${b("config/default.yml")} written`);let $o=ke.join(tn,"rules");ye.existsSync($o)||ye.mkdirSync($o,{recursive:!0});let wu=Ls&&Se?`
973
+ Failed to send message: ${i.message}`),t.prompt()}),o.write(n),o.end()}),t.on("close",()=>{process.exit(0)})}async function ih(l){let e=new ce,t;try{t=e.loadConfig()}catch(i){console.error("Failed to load configuration:",i.message),process.exit(1)}let s=t.api?.host??"127.0.0.1",r=t.api?.port??3420;if(await nh(s,r)){console.log(`Connected to Alfred server at ${s}:${r}`),oh(s,r);return}if(t.logger.level="warn",l.model&&(t.llm.default.model=l.model),l.tier){let i=t.llm[l.tier];i?t.llm.default=i:(console.error(`Unknown tier: ${l.tier}. Available tiers: default, strong, fast, embeddings, local`),process.exit(1))}let o=new Dt(t);try{await o.initialize(),await o.startWithCLI()}catch(i){console.error("Failed to start chat:",i.message),process.exit(1)}}var Cd=_(()=>{"use strict";Ke();sa();m(nh,"checkHealth");m(oh,"startClientMode");m(ih,"chatCommand")});var Nd={};le(Nd,{setupCommand:()=>yh});import{createInterface as ah}from"node:readline/promises";import{stdin as ch,stdout as lh}from"node:process";import{execFileSync as dh}from"node:child_process";import ye from"node:fs";import uh from"node:os";import ke from"node:path";import aa from"js-yaml";function O(l){return`${Jn}${l}${S}`}function hh(l){return`${P}${l}${S}`}function ge(l){return`${Zn}${l}${S}`}function Ld(l){return`${mh}${l}${S}`}function R(l){return`${V}${l}${S}`}function b(l){return`${at}${l}${S}`}function _e(l){return l.length<=4?"****":"*".repeat(l.length-4)+l.slice(-4)}function fh(l){let e=process.platform==="win32",t=e?"where":"which";try{let n=dh(t,[l],{stdio:"pipe"}).toString().trim();if(n)return n.split(/\r?\n/)[0]}catch{}let s=uh.homedir(),r=e?[ke.join(s,".local","bin",`${l}.exe`),ke.join(s,"AppData","Roaming","npm",`${l}.cmd`),ke.join(s,"AppData","Roaming","npm",`${l}`)]:[ke.join(s,".local","bin",l),"/usr/local/bin/"+l,"/opt/homebrew/bin/"+l,ke.join(s,".npm-global","bin",l)];for(let n of r)try{return ye.accessSync(n,ye.constants.X_OK),n}catch{}return null}function gh(l){let e={},t={},s=!1,r=!1,n=30,o=ke.join(l,"config","default.yml");if(ye.existsSync(o))try{let p=aa.load(ye.readFileSync(o,"utf-8"));p&&typeof p=="object"&&Object.assign(e,p)}catch{}let i=ke.join(l,".env");if(ye.existsSync(i))try{let p=ye.readFileSync(i,"utf-8").split(`
974
+ `);for(let f of p){let g=f.trim();if(!g||g.startsWith("#"))continue;let h=g.indexOf("=");h>0&&(t[g.slice(0,h)]=g.slice(h+1))}}catch{}let a=ke.join(l,"config","rules","default-rules.yml");if(ye.existsSync(a))try{let p=aa.load(ye.readFileSync(a,"utf-8"));if(p?.rules){s=p.rules.some(h=>h.id==="allow-owner-admin"&&h.effect==="allow"),p.rules.find(h=>h.id==="allow-write-for-dm"||h.id==="allow-write-all")?.id==="allow-write-all"&&(r=!0);let g=p.rules.find(h=>h.id==="rate-limit-write");g?.rateLimit?.maxInvocations&&(n=g.rateLimit.maxInvocations)}}catch{}let c=!!e.codeSandbox?.enabled,d=e.llm,u={};if(d){for(let p of["strong","fast","embeddings","local"])d[p]?.provider&&d[p]?.model&&(u[p]=d[p]);d.default?.provider&&(e.llm={...e.llm,provider:d.default.provider,model:d.default.model,baseUrl:d.default.baseUrl})}return{config:e,env:t,shellEnabled:s,writeInGroups:r,rateLimit:n,codeSandboxEnabled:c,multiModelTiers:u}}async function yh(){let l=ah({input:ch,output:lh}),e=process.cwd(),t=gh(e),s=Object.keys(t.config).length>0;try{wh(),console.log(s?`${Zn}Existing configuration found \u2014 press Enter to keep current values.${S}
975
+ ${at}Only change what you need to update.${S}
976
+ `:`${Zn}Welcome to the Alfred setup wizard!${S}
977
+ ${at}This will walk you through configuring your AI assistant.${S}
978
+ ${at}Press Enter to accept defaults shown in [brackets].${S}
979
+ `);let r=await F(l,"What should your bot be called?",t.config.name??"Alfred"),n=t.config.llm?.provider?Qe.findIndex(T=>T.name===t.config.llm?.provider):-1,o=n>=0?n+1:1;console.log(`
980
+ ${R("Which LLM provider would you like to use?")}`);for(let T=0;T<Qe.length;T++){let k=T===n?` ${b("(current)")}`:"";console.log(` ${ge(String(T+1)+")")} ${Qe[T].label}${k}`)}let i=await zr(l,"> ",1,Qe.length,o),a=Qe[i-1];console.log(` ${O(">")} Selected: ${R(a.label)}`);let c="",d=t.env[a.envKeyName]??"";a.needsApiKey&&(console.log(""),d?c=await F(l,`${a.name.charAt(0).toUpperCase()+a.name.slice(1)} API key`,d):c=await Ie(l,`Enter your ${a.name.charAt(0).toUpperCase()+a.name.slice(1)} API key`),console.log(` ${O(">")} API key set: ${b(_e(c))}`));let u=a.baseUrl??"";if(["ollama","openwebui","openai","openrouter","google"].includes(a.name)){let k=(t.config.llm?.baseUrl??t.env.ALFRED_LLM_BASE_URL??"")||a.baseUrl||"";if(k){let I={ollama:"Ollama URL (use a remote address if Ollama runs on another machine)",openwebui:"OpenWebUI URL",openai:"OpenAI-compatible API URL (leave default for official API)",openrouter:"OpenRouter API URL",google:"Google Gemini API URL (leave default for official API)"};console.log(""),u=await F(l,I[a.name]??"API Base URL",k.replace(/\/+$/,"")),u=u.replace(/\/+$/,""),console.log(` ${O(">")} URL: ${b(u)}`)}}let f=t.config.llm?.model??a.defaultModel;console.log("");let g,h=await na(a.name,c,u),y=oa(h,a.models??[]);if(y.length>0){console.log(`${R("Available models:")}`);for(let I=0;I<y.length;I++){let W=y[I],L=W.desc??W.name??"",q=W.id===f?` ${O("(current)")}`:"";console.log(` ${ge(`${I+1})`)} ${W.id}${L?` ${b(`\u2014 ${L}`)}`:""}${q}`)}console.log(` ${ge(`${y.length+1})`)} ${b("Other (enter manually)")}`);let T=await F(l,"Choose model","1"),k=parseInt(T,10)-1;k>=0&&k<y.length?g=y[k].id:g=await F(l,"Model ID",f)}else g=await F(l,"Which model?",f);let E=Object.keys(t.multiModelTiers).length>0,$=E?"Y/n":"y/N";console.log(`
981
+ ${R("Configure additional model tiers for specialized tasks?")}`),console.log(`${b("Optional: use different models for complex tasks, quick replies, embeddings, or offline.")}`);let v=(await l.question(`${P}> ${S}${b(`[${$}] `)}`)).trim().toLowerCase(),j=v===""?E:v==="y"||v==="yes",N={};if(j){let T=[{key:"strong",label:"Strong",hint:"complex reasoning, coding, long documents",defaultModel:"claude-opus-4-20250514"},{key:"fast",label:"Fast",hint:"quick responses, simple tasks",defaultModel:"claude-haiku-4-5-20251001"},{key:"embeddings",label:"Embeddings",hint:"semantic search & memory",defaultModel:"text-embedding-3-small"},{key:"local",label:"Local",hint:"offline fallback via Ollama",defaultModel:"llama3.2"}];for(let k of T){let I=t.multiModelTiers[k.key],W=!!I?.model;console.log(`
982
+ ${R(`${k.label} model`)} ${b(`(${k.hint})`)}`),W&&console.log(` ${b(`Current: ${I.provider}/${I.model}`)}`);let L=I?.provider??a.name,q=Qe.map(Se=>Se.name).join(", ");console.log(` ${b(`Providers: ${q}`)}`);let be=(await l.question(` ${P}Provider: ${S}${b(`[${L}] `)}`)).trim()||L;if(!be&&!W){console.log(` ${b("Skipped.")}`);continue}let Y=be,Ee,Ne;if(Y!==a.name){let Se=I?.apiKey??t.env[`ALFRED_LLM_${k.key.toUpperCase()}_API_KEY`]??"";if(Se?Ee=await F(l,` API key for ${Y}`,Se):(Qe.find(Ve=>Ve.name===Y)?.needsApiKey??!0)&&(Ee=await Ie(l,` API key for ${Y}`)),["ollama","openwebui"].includes(Y)){let Ve=(I?.baseUrl??"")||Qe.find(on=>on.name===Y)?.baseUrl||"";Ve&&(Ne=await F(l,` ${Y} URL`,Ve))}}let gt=Ee??(Y===a.name?c:void 0),Us=Ne??(Y===a.name?u:void 0),So=Qe.find(Se=>Se.name===Y),Ao=await na(Y,gt,Us),st=oa(Ao,So?.models??[]),Ge;if(st.length>0){console.log(` ${R("Available models:")}`);for(let Ot=0;Ot<st.length;Ot++){let Ve=st[Ot],on=Ve.desc??Ve.name??"",vu=Ve.id===I?.model?` ${O("(current)")}`:"";console.log(` ${ge(`${Ot+1})`)} ${Ve.id}${on?` ${b(`\u2014 ${on}`)}`:""}${vu}`)}console.log(` ${ge(`${st.length+1})`)} ${b("Other (enter manually)")}`),console.log(` ${ge("0)")} ${b("Skip this tier")}`);let Se=(await l.question(` ${P}> ${S}${W?b(`[${I.model}] `):""}`)).trim();if(Se==="0"){console.log(` ${b("Skipped.")}`);continue}let nn=parseInt(Se,10)-1;nn>=0&&nn<st.length?Ge=st[nn].id:!Se&&W?Ge=I.model:Ge=await F(l," Model ID",W?I.model:k.defaultModel)}else if(console.log(` ${b("Press Enter to skip.")}`),Ge=(await l.question(` ${P}Model: ${S}${W?b(`[${I.model}] `):""}`)).trim()||(W?I.model:""),!Ge){console.log(` ${b("Skipped.")}`);continue}N[k.key]={provider:Y,model:Ge,...Ee?{apiKey:Ee}:{},...Ne?{baseUrl:Ne}:{}},console.log(` ${O(">")} ${k.label}: ${R(Y)}/${R(Ge)}`)}Object.keys(N).length===0&&console.log(`
983
+ ${b("No additional tiers configured \u2014 using single model.")}`)}else console.log(` ${b("Using single model for all tasks.")}`);let H=["brave","tavily","duckduckgo","searxng"],D=t.config.search?.provider??t.env.ALFRED_SEARCH_PROVIDER??"",oe=H.indexOf(D),M=oe>=0?oe+1:0;console.log(`
984
+ ${R("Web Search provider (for searching the internet):")}`);let re=["Brave Search \u2014 recommended, free tier (2,000/month)","Tavily \u2014 built for AI agents, free tier (1,000/month)","DuckDuckGo \u2014 free, no API key needed","SearXNG \u2014 self-hosted, no API key needed"],ze=m(T=>oe===T?` ${b("(current)")}`:"","mark");console.log(` ${ge("0)")} None (disable web search)${oe===-1&&D===""?` ${b("(current)")}`:""}`);for(let T=0;T<re.length;T++)console.log(` ${ge(String(T+1)+")")} ${re[T]}${ze(T)}`);let $e=await zr(l,"> ",0,H.length,M),se,X="",z="";if($e>=1&&$e<=H.length&&(se=H[$e-1]),se==="brave"){let T=t.env.ALFRED_SEARCH_API_KEY??"";T?X=await F(l," Brave Search API key",T):(console.log(` ${b("Get your free API key at: https://brave.com/search/api/")}`),X=await Ie(l," Brave Search API key")),console.log(` ${O(">")} Brave Search: ${b(_e(X))}`)}else if(se==="tavily"){let T=t.env.ALFRED_SEARCH_API_KEY??"";T?X=await F(l," Tavily API key",T):(console.log(` ${b("Get your free API key at: https://tavily.com/")}`),X=await Ie(l," Tavily API key")),console.log(` ${O(">")} Tavily: ${b(_e(X))}`)}else if(se==="duckduckgo")console.log(` ${O(">")} DuckDuckGo: ${b("no API key needed")}`);else if(se==="searxng"){let T=t.config.search?.baseUrl??t.env.ALFRED_SEARCH_BASE_URL??"http://localhost:8080";z=await F(l," SearXNG URL",T),z=z.replace(/\/+$/,""),console.log(` ${O(">")} SearXNG: ${b(z)}`)}else console.log(` ${b("Web search disabled \u2014 you can configure it later.")}`);let me=[];for(let T=0;T<_s.length;T++){let k=_s[T];t.config[k.configKey]?.enabled&&me.push(T+1)}let xe=me.length>0?me.join(","):"";console.log(`
985
+ ${R("Which messaging platforms do you want to enable?")}`),console.log(`${b("(Enter comma-separated numbers, e.g. 1,3)")}`);for(let T=0;T<_s.length;T++){let k=me.includes(T+1)?` ${b("(enabled)")}`:"";console.log(` ${ge(String(T+1)+")")} ${_s[T].label}${k}`)}console.log(` ${ge("0)")} None (configure later)`);let C=(await l.question(`${P}> ${S}${xe?b(`[${xe}] `):""}`)).trim(),B=[],pe=C||xe;if(pe&&pe!=="0"){let T=pe.split(",").map(k=>parseInt(k.trim(),10));for(let k of T)if(k>=1&&k<=_s.length){let I=_s[k-1];B.includes(I)||B.push(I)}}B.length>0?console.log(` ${O(">")} Enabling: ${B.map(T=>R(T.label)).join(", ")}`):console.log(` ${b("No platforms selected \u2014 you can configure them later.")}`);let we={},Nt={};for(let T of B){if(T.credentials.length===0){T.name==="whatsapp"&&console.log(`
986
+ ${hh("i")} WhatsApp: a QR code will be displayed on first start.`);continue}console.log(`
987
+ ${R(T.label+" configuration:")}`);let k={};for(let I of T.credentials){let W=t.env[I.envKey]??"",L;W?L=await F(l,` ${I.prompt}`,W):I.defaultValue?L=await F(l,` ${I.prompt}`,I.defaultValue):I.required?L=await Ie(l,` ${I.prompt}`):(L=(await l.question(` ${I.prompt}: ${P}`)).trim(),process.stdout.write(S)),k[I.configField]=L,Nt[I.envKey]=L,I.configField==="token"||I.configField==="accessToken"?console.log(` ${O(">")} Set: ${b(_e(L))}`):console.log(` ${O(">")} Set: ${b(L)}`)}we[T.configKey]=k}let Re=t.config.email?.accounts??[],bs=Re[0],K=bs?.auth?.user??t.config.email?.auth?.user??t.env.ALFRED_EMAIL_USER??"",J=bs?.provider??t.config.email?.provider??t.env.ALFRED_EMAIL_PROVIDER??"",ie=Re.length>0||!!K||J==="microsoft",ae=ie?"Y/n":"y/N";console.log(`
988
+ ${R("Email access (read & send emails)?")}`),console.log(`${b("Works with Gmail, Outlook, Microsoft 365, or any IMAP/SMTP provider. Supports multiple accounts.")}`);let et=(await l.question(`${P}> ${S}${b(`[${ae}] `)}`)).trim().toLowerCase(),Te=et===""?ie:et==="y"||et==="yes",Ce=[],Jd={"gmail.com":{imap:"imap.gmail.com",smtp:"smtp.gmail.com"},"googlemail.com":{imap:"imap.gmail.com",smtp:"smtp.gmail.com"},"outlook.com":{imap:"outlook.office365.com",smtp:"smtp.office365.com"},"hotmail.com":{imap:"outlook.office365.com",smtp:"smtp.office365.com"},"live.com":{imap:"outlook.office365.com",smtp:"smtp.office365.com"},"yahoo.com":{imap:"imap.mail.yahoo.com",smtp:"smtp.mail.yahoo.com"},"icloud.com":{imap:"imap.mail.me.com",smtp:"smtp.mail.me.com"},"me.com":{imap:"imap.mail.me.com",smtp:"smtp.mail.me.com"},"gmx.de":{imap:"imap.gmx.net",smtp:"mail.gmx.net"},"gmx.net":{imap:"imap.gmx.net",smtp:"mail.gmx.net"},"web.de":{imap:"imap.web.de",smtp:"smtp.web.de"},"posteo.de":{imap:"posteo.de",smtp:"posteo.de"},"mailbox.org":{imap:"imap.mailbox.org",smtp:"smtp.mailbox.org"},"protonmail.com":{imap:"127.0.0.1",smtp:"127.0.0.1"},"proton.me":{imap:"127.0.0.1",smtp:"127.0.0.1"}},ua=m(async(T,k)=>{let I={name:T,provider:"imap-smtp",user:"",pass:"",imapHost:"",imapPort:993,smtpHost:"",smtpPort:587,msClientId:"",msClientSecret:"",msTenantId:"",msRefreshToken:""},W=k?.provider??"",L=["IMAP/SMTP (classic)","Microsoft 365 (Graph API, OAuth)"],q=W==="microsoft"?1:0;console.log("");for(let Y=0;Y<L.length;Y++){let Ee=Y===q?` ${b("(current)")}`:"";console.log(` ${ge(`${Y+1})`)} ${L[Y]}${Ee}`)}let Z=(await l.question(`${P}> ${S}${b(`[${q+1}] `)}`)).trim(),be=Z===""?q:parseInt(Z,10)-1;if(I.provider=be===1?"microsoft":"imap-smtp",I.provider==="microsoft"){let Y=t.config.calendar?.microsoft,Ee=k?.microsoft?.clientId??t.env.ALFRED_MICROSOFT_EMAIL_CLIENT_ID??"",Ne=k?.microsoft?.tenantId??t.env.ALFRED_MICROSOFT_EMAIL_TENANT_ID??"",gt=k?.microsoft?.refreshToken??t.env.ALFRED_MICROSOFT_EMAIL_REFRESH_TOKEN??"";if(Y&&!Ee)console.log(` ${O(">")} Microsoft Calendar already configured \u2014 credentials will be shared.`),console.log(` ${b("The same Azure App Registration will be used for Mail + Calendar.")}`),console.log(` ${b("Ensure the app has Mail.ReadWrite and Mail.Send scopes.")}`);else{console.log(` ${b("Azure Portal \u2192 App Registrations \u2192 your app \u2192 Mail.ReadWrite + Mail.Send scopes")}`),I.msClientId=await F(l," Client ID",Ee),I.msClientId||(I.msClientId=await Ie(l," Client ID"));let Us=t.env.ALFRED_MICROSOFT_EMAIL_CLIENT_SECRET??"";I.msClientSecret=await F(l," Client Secret",Us),I.msClientSecret||(I.msClientSecret=await Ie(l," Client Secret")),I.msTenantId=await F(l," Tenant ID",Ne),I.msTenantId||(I.msTenantId=await Ie(l," Tenant ID")),console.log(` ${b("Tipp: Du kannst `alfred auth microsoft` ausf\xFChren um den Refresh Token automatisch zu holen.")}`),I.msRefreshToken=await F(l," Refresh Token",gt),I.msRefreshToken||(I.msRefreshToken=await Ie(l," Refresh Token"))}console.log(` ${O(">")} Email [${T}]: Microsoft 365 (Graph API)`)}else{let Y=k?.auth?.user??"";console.log(""),I.user=await F(l," Email address",Y||""),I.user||(I.user=await Ie(l," Email address"));let Ee=k?.auth?.pass??t.env.ALFRED_EMAIL_PASS??"";Ee?I.pass=await F(l," Password / App password",Ee):(console.log(` ${b("For Gmail: use an App Password (not your regular password)")}`),console.log(` ${b(" \u2192 Google Account \u2192 Security \u2192 2-Step \u2192 App passwords")}`),I.pass=await Ie(l," Password / App password"));let Ne=I.user.split("@")[1]?.toLowerCase()??"",gt=Jd[Ne],Us=k?.imap?.host??gt?.imap??`imap.${Ne}`,So=k?.smtp?.host??gt?.smtp??`smtp.${Ne}`,Ao=k?.imap?.port??993,st=k?.smtp?.port??587;gt&&console.log(` ${O(">")} Detected ${Ne} \u2014 using preset server settings`),I.imapHost=await F(l," IMAP server",Us);let Ge=await F(l," IMAP port",String(Ao));I.imapPort=parseInt(Ge,10)||993,I.smtpHost=await F(l," SMTP server",So);let Se=await F(l," SMTP port",String(st));I.smtpPort=parseInt(Se,10)||587,console.log(` ${O(">")} Email [${T}]: ${b(I.user)} via ${b(I.imapHost)}`)}return I},"configureEmailAccount");if(Te){let T=bs??(t.config.email?.auth?{provider:t.config.email.provider,auth:t.config.email.auth,imap:t.config.email.imap,smtp:t.config.email.smtp,microsoft:t.config.email.microsoft}:void 0);Ce.push(await ua("default",T));let k=!0;for(;k;){let I=(await l.question(`
989
+ ${R("Add another email account?")} ${b("[y/N]")} `)).trim().toLowerCase();if(I==="y"||I==="yes"){let L=(await l.question(` ${R("Account name:")} `)).trim()||`account${Ce.length+1}`,q=Re.find(Z=>Z.name===L);Ce.push(await ua(L,q))}else k=!1}}else console.log(` ${b("Email disabled \u2014 you can configure it later.")}`);let Wr=["openai","groq"],Zd=t.config.speech?.provider??t.env.ALFRED_SPEECH_PROVIDER??"",Gr=Wr.indexOf(Zd),Qd=Gr>=0?Gr+1:0;console.log(`
990
+ ${R("Voice message transcription (Speech-to-Text via Whisper)?")}`),console.log(`${b("Transcribes voice messages from Telegram, Discord, etc.")}`);let ma=["OpenAI Whisper \u2014 best quality","Groq Whisper \u2014 fast & free"];console.log(` ${ge("0)")} None (disable voice transcription)${Gr===-1?` ${b("(current)")}`:""}`);for(let T=0;T<ma.length;T++){let k=Gr===T?` ${b("(current)")}`:"";console.log(` ${ge(String(T+1)+")")} ${ma[T]}${k}`)}let eo=await zr(l,"> ",0,Wr.length,Qd),qe,tt="",Es="";if(eo>=1&&eo<=Wr.length&&(qe=Wr[eo-1]),qe==="openai"){let T=t.env.ALFRED_SPEECH_API_KEY??"";T?tt=await F(l," OpenAI API key (for Whisper)",T):(console.log(` ${b("Uses your OpenAI API key for Whisper transcription.")}`),tt=await Ie(l," OpenAI API key")),console.log(` ${O(">")} OpenAI Whisper: ${b(_e(tt))}`)}else if(qe==="groq"){let T=t.env.ALFRED_SPEECH_API_KEY??"";T?tt=await F(l," Groq API key",T):(console.log(` ${b("Get your free API key at: https://console.groq.com/")}`),tt=await Ie(l," Groq API key"));let k=t.env.ALFRED_SPEECH_BASE_URL??"";k&&(Es=await F(l," Groq API URL",k)),console.log(` ${O(">")} Groq Whisper: ${b(_e(tt))}`)}else console.log(` ${b("Voice transcription disabled \u2014 you can configure it later.")}`);let $s=!1,ks="alloy";if(qe){let T=t.config.speech?.ttsEnabled??!1,k=T?"Y/n":"y/N";console.log(`
991
+ ${R("Voice responses (Text-to-Speech)?")}`),console.log(`${b("Alfred can reply as a voice message when the user asks for it.")}`);let I=(await l.question(`${P}> ${S}${b(`[${k}] `)}`)).trim().toLowerCase();if($s=I===""?T:I==="y"||I==="yes",$s){let W=["alloy","echo","fable","onyx","nova","shimmer"],L=t.config.speech?.ttsVoice??"alloy",q=W.indexOf(L),Z=q>=0?q+1:1;console.log(`
992
+ ${R("Which voice?")}`);for(let Y=0;Y<W.length;Y++){let Ee=q===Y?` ${b("(current)")}`:"";console.log(` ${ge(String(Y+1)+")")} ${W[Y]}${Ee}`)}let be=await zr(l," > ",1,W.length,Z);ks=W[be-1],console.log(` ${O(">")} TTS voice: ${R(ks)}`)}else console.log(` ${b("Voice responses disabled.")}`)}let eu=t.codeSandboxEnabled?"Y/n":"y/N";console.log(`
993
+ ${R("Code Sandbox (execute Python/JavaScript in a sandboxed environment)?")}`),console.log(`${b("Enables code execution for calculations, data processing, PDF generation, charts, etc.")}`);let to=(await l.question(`${P}> ${S}${b(`[${eu}] `)}`)).trim().toLowerCase(),so=to===""?t.codeSandboxEnabled:to==="y"||to==="yes";console.log(so?` ${O(">")} Code Sandbox ${R("enabled")} (JavaScript + Python)`:` ${b("Code Sandbox disabled \u2014 you can enable it later in config/default.yml.")}`),console.log(`
994
+ ${R("Code Agents (CLI-based coding agents for automated tasks)?")}`),console.log(`${b("Scanning for known coding agents on this system...")}`);let ro=[];for(let T of Dd){let k=fh(T.whichCmd);k?(ro.push({...T,resolvedPath:k}),console.log(` ${O("\u2713")} ${R(T.label)} ${b(`(${k})`)}`)):console.log(` ${b("\xB7")} ${b(T.label)} ${b("\u2014 not found")}`)}let no=(t.config.codeAgents?.agents??[]).filter(T=>!Dd.some(k=>k.name===T.name));for(let T of no)console.log(` ${O("\u2713")} ${R(T.name)} ${b(`(${T.command}) \u2014 from existing config`)}`);let ct=[];if(ro.length===0&&no.length===0)console.log(`
995
+ ${b("No coding agents found. You can add them manually in config/default.yml later.")}`);else{let T=[...ro.map(L=>({name:L.name,command:L.resolvedPath??L.command,argsTemplate:L.argsTemplate,promptVia:L.promptVia,label:L.label,detected:!0})),...no.map(L=>({name:L.name,command:L.command,argsTemplate:L.argsTemplate,promptVia:L.promptVia??"arg",label:L.name,detected:!1}))];console.log(`
996
+ ${R("Which agents should Alfred use?")} ${b("(comma-separated, e.g. 1,2)")}`);let k=new Set((t.config.codeAgents?.agents??[]).map(L=>L.name));for(let L=0;L<T.length;L++){let q=T[L],Z=k.has(q.name)?` ${b("(current)")}`:"";console.log(` ${P}${L+1}${S}) ${q.label}${Z}`)}console.log(` ${P}0${S}) None`);let I=T.map((L,q)=>k.size>0?k.has(L.name)?String(q+1):null:L.detected?String(q+1):null).filter(Boolean).join(",")||"0",W=(await l.question(` ${P}> ${S}${b(`[${I}] `)}`)).trim()||I;W!=="0"&&(ct=W.split(",").map(q=>parseInt(q.trim(),10)).filter(q=>!isNaN(q)&&q>=1&&q<=T.length).map(q=>{let Z=T[q-1];return{name:Z.name,command:Z.command,argsTemplate:Z.argsTemplate,promptVia:Z.promptVia}})),ct.length>0?console.log(` ${O(">")} ${R(String(ct.length))} agent(s) selected: ${ct.map(L=>L.name).join(", ")}`):console.log(` ${b("No agents selected.")}`)}let oo=t.config.codeAgents?.forge,io=oo?.provider??t.env.ALFRED_FORGE_PROVIDER??"";console.log(`
997
+ ${R("Forge Integration (auto-create PRs/MRs after code agent orchestration)?")}`),console.log(`${b("Connects to GitHub or GitLab to push branches and create pull/merge requests.")}`),console.log(`${b("Owner/repo are detected automatically from the git remote at runtime.")}`);let pa=[{num:"1",name:"",label:"None \u2014 skip forge integration"},{num:"2",name:"github",label:"GitHub"},{num:"3",name:"gitlab",label:"GitLab"}];for(let T of pa){let k=T.name===io?` ${b("(current)")}`:"";console.log(` ${P}${T.num}${S}) ${T.label}${k}`)}let ha=io==="github"?"2":io==="gitlab"?"3":"1",tu=(await l.question(`${P}> ${S}${b(`[${ha}] `)}`)).trim()||ha,lt=pa.find(T=>T.num===tu)?.name??"",vs="",Ss="";if(lt==="github"){console.log(` ${O(">")} Forge: ${R("GitHub")}`);let T=t.env.ALFRED_GITHUB_TOKEN??oo?.github?.token??"";T&&console.log(` ${b(`Current token: ${_e(T)}`)}`),console.log(` ${b("Create a token at https://github.com/settings/tokens (scope: repo)")}`),vs=(await l.question(` ${V}GitHub Token${S}: ${P}`)).trim(),process.stdout.write(S),!vs&&T&&(vs=T)}else if(lt==="gitlab"){console.log(` ${O(">")} Forge: ${R("GitLab")}`);let T=t.env.ALFRED_GITLAB_TOKEN??oo?.gitlab?.token??"";T&&console.log(` ${b(`Current token: ${_e(T)}`)}`),console.log(` ${b("Create a token at https://gitlab.com/-/user_settings/personal_access_tokens (scope: api)")}`),Ss=(await l.question(` ${V}GitLab Token${S}: ${P}`)).trim(),process.stdout.write(S),!Ss&&T&&(Ss=T)}else console.log(` ${b("Forge integration disabled \u2014 you can enable it later in config/default.yml.")}`);console.log(`
998
+ ${R("Infrastructure Management (Proxmox / UniFi / Home Assistant)?")}`),console.log(`${b("Control VMs, containers, network devices, and smart home through Alfred.")}`);let As=t.config.proxmox,ao=t.env.ALFRED_PROXMOX_BASE_URL??As?.baseUrl??"",su=ao?"Y/n":"y/N",fa=(await l.question(` ${V}Enable Proxmox VE?${S} ${b(`[${su}]`)}: ${P}`)).trim().toLowerCase()||(ao?"y":"n");process.stdout.write(S);let Vr=fa==="y"||fa==="yes",Is="",co="",xs="",lo=!0;if(Vr){Is=await F(l," Proxmox URL (e.g. https://pve.local:8006)",ao||"https://pve.local:8006");let T=t.env.ALFRED_PROXMOX_TOKEN_ID??As?.tokenId??"";T&&console.log(` ${b(`Current token ID: ${T}`)}`),console.log(` ${b("Create: Datacenter \u2192 Permissions \u2192 API Tokens")}`),co=await F(l," API Token ID (user@realm!name)",T);let k=t.env.ALFRED_PROXMOX_TOKEN_SECRET??As?.tokenSecret??"";k&&console.log(` ${b(`Current secret: ${_e(k)}`)}`),xs=(await l.question(` ${V}API Token Secret${S}: ${P}`)).trim(),process.stdout.write(S),!xs&&k&&(xs=k);let I=As?.verifyTls===!1?"y/N":"Y/n",W=(await l.question(` ${V}Verify TLS?${S} ${b(`(self-signed? \u2192 no) [${I}]`)}: ${P}`)).trim().toLowerCase()||(As?.verifyTls===!1?"n":"y");process.stdout.write(S),lo=W==="y"||W==="yes",console.log(` ${O(">")} Proxmox: ${R(Is)} ${b(`(TLS verify: ${lo?"yes":"no"})`)}`)}else console.log(` ${b("Proxmox disabled.")}`);let dt=t.config.unifi,uo=t.env.ALFRED_UNIFI_BASE_URL??dt?.baseUrl??"",ru=uo?"Y/n":"y/N",ga=(await l.question(`
999
+ ${V}Enable UniFi Network?${S} ${b(`[${ru}]`)}: ${P}`)).trim().toLowerCase()||(uo?"y":"n");process.stdout.write(S);let Rs=ga==="y"||ga==="yes",Mt="",ut="",mo="",Cs="",po=!0;if(Rs){Mt=await F(l," UniFi URL (e.g. https://unifi.local)",uo||"https://unifi.local"),console.log(` ${b("Auth: API Key (recommended) or Username/Password")}`);let T=t.env.ALFRED_UNIFI_API_KEY??dt?.apiKey??"",k=[{num:"1",name:"apikey",label:"API Key (UniFi OS)"},{num:"2",name:"password",label:"Username / Password"}],I=T?"1":dt?.username?"2":"1";for(let Z of k)console.log(` ${P}${Z.num}${S}) ${Z.label}`);if(((await l.question(` ${P}> ${S}${b(`[${I}] `)}`)).trim()||I)==="1")T&&console.log(` ${b(`Current key: ${_e(T)}`)}`),console.log(` ${b("Create: Settings \u2192 Admins \u2192 API Keys (UniFi OS)")}`),ut=(await l.question(` ${V}API Key${S}: ${P}`)).trim(),process.stdout.write(S),!ut&&T&&(ut=T);else{let Z=t.env.ALFRED_UNIFI_USERNAME??dt?.username??"";mo=await F(l," Username",Z||"alfred");let be=t.env.ALFRED_UNIFI_PASSWORD??dt?.password??"";be&&console.log(` ${b(`Current password: ${_e(be)}`)}`),Cs=(await l.question(` ${V}Password${S}: ${P}`)).trim(),process.stdout.write(S),!Cs&&be&&(Cs=be)}let L=dt?.verifyTls===!1?"y/N":"Y/n",q=(await l.question(` ${V}Verify TLS?${S} ${b(`(self-signed? \u2192 no) [${L}]`)}: ${P}`)).trim().toLowerCase()||(dt?.verifyTls===!1?"n":"y");process.stdout.write(S),po=q==="y"||q==="yes",console.log(` ${O(">")} UniFi: ${R(Mt)} ${b(`(TLS verify: ${po?"yes":"no"})`)}`)}else console.log(` ${b("UniFi disabled.")}`);let Kr=t.config.homeassistant,ho=t.env.ALFRED_HOMEASSISTANT_URL??Kr?.baseUrl??"",nu=ho?"Y/n":"y/N",ya=(await l.question(`
1000
+ ${V}Enable Home Assistant?${S} ${b(`[${nu}]`)}: ${P}`)).trim().toLowerCase()||(ho?"y":"n");process.stdout.write(S);let Xr=ya==="y"||ya==="yes",Ds="",Ls="",fo=!0;if(Xr){Ds=await F(l," Home Assistant URL (e.g. http://homeassistant.local:8123)",ho||"http://homeassistant.local:8123");let T=t.env.ALFRED_HOMEASSISTANT_TOKEN??Kr?.accessToken??"";T&&console.log(` ${b(`Current token: ${_e(T)}`)}`),console.log(` ${b("Create: Settings \u2192 Security \u2192 Long-Lived Access Tokens")}`),Ls=(await l.question(` ${V}Long-Lived Access Token${S}: ${P}`)).trim(),process.stdout.write(S),!Ls&&T&&(Ls=T);let k=Kr?.verifyTls===!1?"y/N":"Y/n",I=(await l.question(` ${V}Verify TLS?${S} ${b(`(self-signed? \u2192 no) [${k}]`)}: ${P}`)).trim().toLowerCase()||(Kr?.verifyTls===!1?"n":"y");process.stdout.write(S),fo=I==="y"||I==="yes",console.log(` ${O(">")} Home Assistant: ${R(Ds)} ${b(`(TLS verify: ${fo?"yes":"no"})`)}`)}else console.log(` ${b("Home Assistant disabled.")}`);let De=t.config.contacts,go=t.env.ALFRED_CONTACTS_PROVIDER??De?.provider??"",ou=go?"Y/n":"y/N",wa=(await l.question(`
1001
+ ${V}Enable Contacts management?${S} ${b(`[${ou}]`)}: ${P}`)).trim().toLowerCase()||(go?"y":"n");process.stdout.write(S);let Yr=wa==="y"||wa==="yes",Le="",he={};if(Yr){let T=["carddav","google","microsoft"],k=T.indexOf(go),I=k>=0?k+1:1;console.log(` ${ge("1)")} CardDAV (Nextcloud, Radicale, etc.)`),console.log(` ${ge("2)")} Google Contacts`),console.log(` ${ge("3)")} Microsoft 365`);let W=await zr(l," > ",1,3,I);if(Le=T[W-1],he.ALFRED_CONTACTS_PROVIDER=Le,Le==="carddav"){let L=t.env.ALFRED_CARDDAV_CONTACTS_SERVER_URL??De?.carddav?.serverUrl??"";he.ALFRED_CARDDAV_CONTACTS_SERVER_URL=await F(l," CardDAV Server URL",L||"https://cloud.example.com/remote.php/dav");let q=t.env.ALFRED_CARDDAV_CONTACTS_USERNAME??De?.carddav?.username??"";he.ALFRED_CARDDAV_CONTACTS_USERNAME=await F(l," Username",q);let Z=t.env.ALFRED_CARDDAV_CONTACTS_PASSWORD??De?.carddav?.password??"";Z&&console.log(` ${b(`Current password: ${_e(Z)}`)}`);let be=(await l.question(` ${V}Password${S}: ${P}`)).trim();process.stdout.write(S),he.ALFRED_CARDDAV_CONTACTS_PASSWORD=be||Z}else if(Le==="google"){let L=t.env.ALFRED_GOOGLE_CONTACTS_CLIENT_ID??De?.google?.clientId??"";L&&console.log(` ${b(`Current client ID: ${_e(L)}`)}`),he.ALFRED_GOOGLE_CONTACTS_CLIENT_ID=(await l.question(` ${V}Google Client ID${S}: ${P}`)).trim()||L,process.stdout.write(S);let q=t.env.ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET??De?.google?.clientSecret??"";he.ALFRED_GOOGLE_CONTACTS_CLIENT_SECRET=(await l.question(` ${V}Google Client Secret${S}: ${P}`)).trim()||q,process.stdout.write(S);let Z=t.env.ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN??De?.google?.refreshToken??"";he.ALFRED_GOOGLE_CONTACTS_REFRESH_TOKEN=(await l.question(` ${V}Refresh Token${S}: ${P}`)).trim()||Z,process.stdout.write(S)}else if(Le==="microsoft"){let L=t.env.ALFRED_MICROSOFT_CONTACTS_CLIENT_ID??De?.microsoft?.clientId??"";L&&console.log(` ${b(`Current client ID: ${_e(L)}`)}`),he.ALFRED_MICROSOFT_CONTACTS_CLIENT_ID=(await l.question(` ${V}Microsoft Client ID${S}: ${P}`)).trim()||L,process.stdout.write(S);let q=t.env.ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET??De?.microsoft?.clientSecret??"";he.ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET=(await l.question(` ${V}Microsoft Client Secret${S}: ${P}`)).trim()||q,process.stdout.write(S);let Z=t.env.ALFRED_MICROSOFT_CONTACTS_TENANT_ID??De?.microsoft?.tenantId??"";he.ALFRED_MICROSOFT_CONTACTS_TENANT_ID=await F(l," Tenant ID",Z||"common");let be=t.env.ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN??De?.microsoft?.refreshToken??"";console.log(` ${b("Tipp: Du kannst `alfred auth microsoft` ausf\xFChren um den Refresh Token automatisch zu holen.")}`),he.ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN=(await l.question(` ${V}Refresh Token${S}: ${P}`)).trim()||be,process.stdout.write(S)}console.log(` ${O(">")} Contacts: ${R(Le)}`)}else console.log(` ${b("Contacts disabled.")}`);let Ta=t.config.docker,yo=t.env.ALFRED_DOCKER_SOCKET_PATH??Ta?.socketPath??"",wo=t.env.ALFRED_DOCKER_HOST??Ta?.host??"",iu=yo||wo?"Y/n":"y/N",_a=(await l.question(`
1002
+ ${V}Enable Docker management?${S} ${b(`[${iu}]`)}: ${P}`)).trim().toLowerCase()||(yo||wo?"y":"n");process.stdout.write(S);let Jr=_a==="y"||_a==="yes",mt="",pt="";if(Jr){let T=process.platform==="win32"?"//./pipe/docker_engine":"/var/run/docker.sock";console.log(` ${b("Use socket path for local Docker, or host URL for remote.")}`),mt=await F(l," Docker socket path",yo||T);let k=(await l.question(` ${V}Docker host URL (optional, for remote)${S}: ${P}`)).trim();process.stdout.write(S),pt=k||wo,console.log(` ${O(">")} Docker: ${R(pt||mt)}`)}else console.log(` ${b("Docker disabled.")}`);let au=t.config.bmw,To=t.env.ALFRED_BMW_CLIENT_ID??au?.clientId??"",cu=To?"Y/n":"y/N",ba=(await l.question(`
1003
+ ${V}Enable BMW CarData (vehicle status, charging)?${S} ${b(`[${cu}]`)}: ${P}`)).trim().toLowerCase()||(To?"y":"n");process.stdout.write(S);let Zr=ba==="y"||ba==="yes",_o="";Zr?(console.log(` ${b("Setup:")}`),console.log(` ${b(" 1. \xD6ffne https://bmw-cardata.bmwgroup.com/customer")}`),console.log(` ${b(" 2. Login mit deinem MyBMW-Account")}`),console.log(` ${b(" 3. Client-ID generieren")}`),console.log(` ${b(' 4. Scope "CarData API" aktivieren (ca. 60s warten)')}`),_o=await F(l," BMW CarData Client ID",To),console.log(` ${O(">")} BMW CarData: ${R("enabled")}`)):console.log(` ${b("BMW CarData disabled.")}`);let lu=t.config.routing,bo=t.env.ALFRED_ROUTING_API_KEY??lu?.apiKey??"",du=bo?"Y/n":"y/N",Ea=(await l.question(`
1004
+ ${V}Enable route planning with live traffic (Google Routes)?${S} ${b(`[${du}]`)}: ${P}`)).trim().toLowerCase()||(bo?"y":"n");process.stdout.write(S);let Qr=Ea==="y"||Ea==="yes",Eo="";Qr?(console.log(` ${b("Setup:")}`),console.log(` ${b(" 1. \xD6ffne https://console.cloud.google.com")}`),console.log(` ${b(" 2. Routes API aktivieren")}`),console.log(` ${b(" 3. API Key erstellen")}`),Eo=await F(l," Google Maps API Key",bo),console.log(` ${O(">")} Routing: ${R("enabled")}`)):console.log(` ${b("Routing disabled.")}`);let We=t.config.energy,uu=t.env.ALFRED_ENERGY_GRID_NAME??We?.gridName??"",$o=t.env.ALFRED_ENERGY_GRID_USAGE_CT??(We?.gridUsageCt!=null?String(We.gridUsageCt):""),mu=t.env.ALFRED_ENERGY_GRID_LOSS_CT??(We?.gridLossCt!=null?String(We.gridLossCt):""),pu=t.env.ALFRED_ENERGY_GRID_CAPACITY_FEE??(We?.gridCapacityFee!=null?String(We.gridCapacityFee):""),hu=t.env.ALFRED_ENERGY_GRID_METER_FEE??(We?.gridMeterFee!=null?String(We.gridMeterFee):""),fu=$o?"Y/n":"y/N",$a=(await l.question(`
1005
+ ${V}Enable energy prices (aWATTar HOURLY / EPEX Spot AT)?${S} ${b(`[${fu}]`)}: ${P}`)).trim().toLowerCase()||($o?"y":"n");process.stdout.write(S);let en=$a==="y"||$a==="yes",ht="",ft="",tn="",Ns="",Ms="";en?(console.log(` ${b('Die Werte findest du auf deiner Stromrechnung unter "Netzentgelte".')}`),console.log(` ${b("Marktpreis + aWATTar-Aufschlag + Abgaben werden automatisch berechnet.")}`),console.log(""),ht=await F(l," Netzbetreiber Name (z.B. Netz Nieder\xF6sterreich)",uu),ft=await F(l," Netznutzungsentgelt (ct/kWh netto)",$o),tn=await F(l," Netzverlustentgelt (ct/kWh netto)",mu||"0.38"),Ns=await F(l," Leistungspauschale (\u20AC/Monat netto)",pu),Ms=await F(l," Messentgelt (\u20AC/Monat netto)",hu||"2.22"),console.log(` ${O(">")} Energy: ${R(ht||"enabled")} (${ft} + ${tn} ct/kWh)`)):console.log(` ${b("Energy prices disabled (Marktpreise weiterhin ohne Netzentgelte verf\xFCgbar).")}`),console.log(`
1006
+ ${R("Security configuration:")}`);let ka=t.config.security?.ownerUserId??t.env.ALFRED_OWNER_USER_ID??"",ve;if(ka)ve=await F(l,"Owner user ID (for elevated permissions)",ka);else{let T=(await l.question(`${V}Owner user ID${S} ${b("(optional, for elevated permissions)")}: ${P}`)).trim();process.stdout.write(S),ve=T}let Os=!1;if(ve){let T=t.shellEnabled?"Y/n":"y/N";console.log(""),console.log(` ${R("Enable shell access (admin commands) for the owner?")}`),console.log(` ${b("Allows Alfred to execute shell commands. Only for the owner.")}`);let k=(await l.question(` ${P}> ${S}${b(`[${T}] `)}`)).trim().toLowerCase();k===""?Os=t.shellEnabled:Os=k==="y"||k==="yes",console.log(Os?` ${O(">")} Shell access ${R("enabled")} for owner ${b(ve)}`:` ${b("Shell access disabled.")}`)}let gu=t.writeInGroups?"Y/n":"y/N";console.log(""),console.log(` ${R("Allow write actions (notes, reminders, memory) in group chats?")}`),console.log(` ${b("By default, write actions are only allowed in DMs.")}`);let ko=(await l.question(` ${P}> ${S}${b(`[${gu}] `)}`)).trim().toLowerCase(),Ps;ko===""?Ps=t.writeInGroups:Ps=ko==="y"||ko==="yes",console.log(Ps?` ${O(">")} Write actions ${R("enabled")} in groups`:` ${b("Write actions only in DMs (default).")}`);let yu=t.rateLimit??30;console.log("");let wu=await F(l," Rate limit (max write actions per hour per user)",String(yu)),sn=Math.max(1,parseInt(wu,10)||30);console.log(` ${O(">")} Rate limit: ${R(String(sn))} per hour`),console.log(`
1007
+ ${R("Writing configuration files...")}`);let A=["# Alfred Environment Variables","# Generated by `alfred setup`","","# === LLM ===","",`ALFRED_LLM_PROVIDER=${a.name}`];if(c){let T=a.envKeyName||"ALFRED_OLLAMA_API_KEY";A.push(`${T}=${c}`)}if(g!==a.defaultModel&&A.push(`ALFRED_LLM_MODEL=${g}`),u&&A.push(`ALFRED_LLM_BASE_URL=${u}`),Object.keys(N).length>0){A.push("","# === Additional Model Tiers ===");for(let[T,k]of Object.entries(N)){let I=`ALFRED_LLM_${T.toUpperCase()}`;A.push(""),A.push(`${I}_PROVIDER=${k.provider}`),A.push(`${I}_MODEL=${k.model}`),k.apiKey&&A.push(`${I}_API_KEY=${k.apiKey}`),k.baseUrl&&A.push(`${I}_BASE_URL=${k.baseUrl}`)}}A.push("","# === Messaging Platforms ===","");for(let[T,k]of Object.entries(Nt))A.push(`${T}=${k}`);if(A.push("","# === Web Search ===",""),se?(A.push(`ALFRED_SEARCH_PROVIDER=${se}`),X&&A.push(`ALFRED_SEARCH_API_KEY=${X}`),z&&A.push(`ALFRED_SEARCH_BASE_URL=${z}`)):(A.push("# ALFRED_SEARCH_PROVIDER=brave"),A.push("# ALFRED_SEARCH_API_KEY=")),A.push("","# === Email ===",""),Te&&Ce.length>0){let T=Ce[0];T.provider==="microsoft"?(A.push("ALFRED_EMAIL_PROVIDER=microsoft"),T.msClientId?(A.push(`ALFRED_MICROSOFT_EMAIL_CLIENT_ID=${T.msClientId}`),A.push(`ALFRED_MICROSOFT_EMAIL_CLIENT_SECRET=${T.msClientSecret}`),A.push(`ALFRED_MICROSOFT_EMAIL_TENANT_ID=${T.msTenantId}`),A.push(`ALFRED_MICROSOFT_EMAIL_REFRESH_TOKEN=${T.msRefreshToken}`)):A.push("# Microsoft email credentials shared from calendar config")):(A.push(`ALFRED_EMAIL_USER=${T.user}`),A.push(`ALFRED_EMAIL_PASS=${T.pass}`)),Ce.length>1&&A.push("# Additional email accounts configured in config/default.yml")}else A.push("# ALFRED_EMAIL_USER="),A.push("# ALFRED_EMAIL_PASS=");if(A.push("","# === Speech ===",""),qe?(A.push(`ALFRED_SPEECH_PROVIDER=${qe}`),A.push(`ALFRED_SPEECH_API_KEY=${tt}`),Es&&A.push(`ALFRED_SPEECH_BASE_URL=${Es}`),$s&&(A.push("ALFRED_TTS_ENABLED=true"),A.push(`ALFRED_TTS_VOICE=${ks}`))):(A.push("# ALFRED_SPEECH_PROVIDER=groq"),A.push("# ALFRED_SPEECH_API_KEY=")),A.push("","# === Forge (GitHub / GitLab) ===",""),lt==="github"?(A.push("ALFRED_FORGE_PROVIDER=github"),A.push(`ALFRED_GITHUB_TOKEN=${vs}`)):lt==="gitlab"?(A.push("ALFRED_FORGE_PROVIDER=gitlab"),A.push(`ALFRED_GITLAB_TOKEN=${Ss}`)):(A.push("# ALFRED_FORGE_PROVIDER=github"),A.push("# ALFRED_GITHUB_TOKEN=")),A.push("","# === Infrastructure (Proxmox / UniFi / Home Assistant) ===",""),Vr?(A.push(`ALFRED_PROXMOX_BASE_URL=${Is}`),A.push(`ALFRED_PROXMOX_TOKEN_ID=${co}`),A.push(`ALFRED_PROXMOX_TOKEN_SECRET=${xs}`)):(A.push("# ALFRED_PROXMOX_BASE_URL="),A.push("# ALFRED_PROXMOX_TOKEN_ID="),A.push("# ALFRED_PROXMOX_TOKEN_SECRET=")),Rs&&ut?(A.push(`ALFRED_UNIFI_BASE_URL=${Mt}`),A.push(`ALFRED_UNIFI_API_KEY=${ut}`)):Rs?(A.push(`ALFRED_UNIFI_BASE_URL=${Mt}`),A.push(`ALFRED_UNIFI_USERNAME=${mo}`),A.push(`ALFRED_UNIFI_PASSWORD=${Cs}`)):(A.push("# ALFRED_UNIFI_BASE_URL="),A.push("# ALFRED_UNIFI_API_KEY=")),Xr?(A.push(`ALFRED_HOMEASSISTANT_URL=${Ds}`),A.push(`ALFRED_HOMEASSISTANT_TOKEN=${Ls}`)):(A.push("# ALFRED_HOMEASSISTANT_URL="),A.push("# ALFRED_HOMEASSISTANT_TOKEN=")),A.push("","# === Contacts ===",""),Yr)for(let[T,k]of Object.entries(he))A.push(`${T}=${k}`);else A.push("# ALFRED_CONTACTS_PROVIDER=carddav");A.push("","# === Docker ===",""),Jr?(mt&&A.push(`ALFRED_DOCKER_SOCKET_PATH=${mt}`),pt&&A.push(`ALFRED_DOCKER_HOST=${pt}`)):(A.push("# ALFRED_DOCKER_SOCKET_PATH="),A.push("# ALFRED_DOCKER_HOST=")),A.push("","# === BMW CarData ===",""),Zr?A.push(`ALFRED_BMW_CLIENT_ID=${_o}`):A.push("# ALFRED_BMW_CLIENT_ID="),A.push("","# === Routing ===",""),Qr?A.push(`ALFRED_ROUTING_API_KEY=${Eo}`):A.push("# ALFRED_ROUTING_API_KEY="),A.push("","# === Energy / aWATTar ===",""),en&&ft?(ht&&A.push(`ALFRED_ENERGY_GRID_NAME=${ht}`),A.push(`ALFRED_ENERGY_GRID_USAGE_CT=${ft}`),A.push(`ALFRED_ENERGY_GRID_LOSS_CT=${tn}`),Ns&&A.push(`ALFRED_ENERGY_GRID_CAPACITY_FEE=${Ns}`),Ms&&A.push(`ALFRED_ENERGY_GRID_METER_FEE=${Ms}`)):(A.push("# ALFRED_ENERGY_GRID_NAME="),A.push("# ALFRED_ENERGY_GRID_USAGE_CT="),A.push("# ALFRED_ENERGY_GRID_LOSS_CT="),A.push("# ALFRED_ENERGY_GRID_CAPACITY_FEE="),A.push("# ALFRED_ENERGY_GRID_METER_FEE=")),A.push("","# === Security ===",""),ve?A.push(`ALFRED_OWNER_USER_ID=${ve}`):A.push("# ALFRED_OWNER_USER_ID="),A.push("");let Tu=ke.join(e,".env");ye.writeFileSync(Tu,A.join(`
1008
+ `),"utf-8"),console.log(` ${O("+")} ${b(".env")} written`);let rn=ke.join(e,"config");ye.existsSync(rn)||ye.mkdirSync(rn,{recursive:!0});let va={name:r,telegram:{token:we.telegram?.token??"",enabled:B.some(T=>T.name==="telegram")},discord:{token:we.discord?.token??"",enabled:B.some(T=>T.name==="discord")},whatsapp:{enabled:B.some(T=>T.name==="whatsapp"),dataPath:"./data/whatsapp"},matrix:{homeserverUrl:we.matrix?.homeserverUrl??"https://matrix.org",accessToken:we.matrix?.accessToken??"",userId:we.matrix?.userId??"",enabled:B.some(T=>T.name==="matrix")},signal:{apiUrl:we.signal?.apiUrl??"http://localhost:8080",phoneNumber:we.signal?.phoneNumber??"",enabled:B.some(T=>T.name==="signal")},llm:Object.keys(N).length>0?{default:{provider:a.name,model:g,...u?{baseUrl:u}:{},temperature:.7,maxTokens:4096},...N}:{provider:a.name,model:g,...u?{baseUrl:u}:{},temperature:.7,maxTokens:4096},...se?{search:{provider:se,...X?{apiKey:X}:{},...z?{baseUrl:z}:{}}}:{},...Te&&Ce.length>0?{email:{accounts:Ce.map(T=>T.provider==="microsoft"?{name:T.name,provider:"microsoft",...T.msClientId?{microsoft:{clientId:T.msClientId,clientSecret:T.msClientSecret,tenantId:T.msTenantId,refreshToken:T.msRefreshToken}}:{}}:{name:T.name,imap:{host:T.imapHost,port:T.imapPort,secure:T.imapPort===993},smtp:{host:T.smtpHost,port:T.smtpPort,secure:T.smtpPort===465},auth:{user:T.user,pass:T.pass}})}}:{},...qe?{speech:{provider:qe,apiKey:tt,...Es?{baseUrl:Es}:{},...$s?{ttsEnabled:!0,ttsVoice:ks}:{}}}:{},...so?{codeSandbox:{enabled:!0,allowedLanguages:["javascript","python"]}}:{},...ct.length>0||lt?{codeAgents:{enabled:ct.length>0,agents:ct,...lt==="github"?{forge:{provider:"github",github:{token:vs}}}:lt==="gitlab"?{forge:{provider:"gitlab",gitlab:{token:Ss}}}:{}}}:{},...Vr?{proxmox:{baseUrl:Is,tokenId:co,tokenSecret:xs,verifyTls:lo}}:{},...Rs?{unifi:{baseUrl:Mt,...ut?{apiKey:ut}:{username:mo,password:Cs},site:"default",verifyTls:po}}:{},...Xr?{homeassistant:{baseUrl:Ds,accessToken:Ls,verifyTls:fo}}:{},...Yr?{contacts:{provider:Le,...Le==="carddav"?{carddav:{serverUrl:he.ALFRED_CARDDAV_CONTACTS_SERVER_URL,username:he.ALFRED_CARDDAV_CONTACTS_USERNAME}}:Le==="google"?{google:{clientId:he.ALFRED_GOOGLE_CONTACTS_CLIENT_ID}}:Le==="microsoft"?{microsoft:{clientId:he.ALFRED_MICROSOFT_CONTACTS_CLIENT_ID,tenantId:he.ALFRED_MICROSOFT_CONTACTS_TENANT_ID}}:{}}}:{},...Jr?{docker:{...mt?{socketPath:mt}:{},...pt?{host:pt}:{}}}:{},...Zr?{bmw:{clientId:_o}}:{},...Qr?{routing:{apiKey:Eo}}:{},...en&&ft?{energy:{...ht?{gridName:ht}:{},gridUsageCt:parseFloat(ft),gridLossCt:parseFloat(tn||"0"),...Ns?{gridCapacityFee:parseFloat(Ns)}:{},...Ms?{gridMeterFee:parseFloat(Ms)}:{}}}:{},storage:{path:"./data/alfred.db"},logger:{level:"info",pretty:!0,auditLogPath:"./data/audit.log"},security:{rulesPath:"./config/rules",defaultEffect:"deny"}};ve&&(va.security.ownerUserId=ve);let _u="# Alfred \u2014 Configuration\n# Generated by `alfred setup`\n# Edit manually or re-run `alfred setup` to reconfigure.\n\n"+aa.dump(va,{lineWidth:120,noRefs:!0,sortKeys:!1}),bu=ke.join(rn,"default.yml");ye.writeFileSync(bu,_u,"utf-8"),console.log(` ${O("+")} ${b("config/default.yml")} written`);let vo=ke.join(rn,"rules");ye.existsSync(vo)||ye.mkdirSync(vo,{recursive:!0});let Eu=Os&&ve?`
960
1009
  # Allow admin actions (shell, etc.) for the owner only
961
1010
  - id: allow-owner-admin
962
1011
  effect: allow
@@ -965,7 +1014,7 @@ ${C("Writing configuration files...")}`);let A=["# Alfred Environment Variables"
965
1014
  actions: ["*"]
966
1015
  riskLevels: [admin, destructive]
967
1016
  conditions:
968
- users: ["${Se}"]
1017
+ users: ["${ve}"]
969
1018
  `:`
970
1019
  # Allow admin actions (shell, etc.) for the owner only
971
1020
  # Uncomment and set your user ID to enable:
@@ -976,8 +1025,8 @@ ${C("Writing configuration files...")}`);let A=["# Alfred Environment Variables"
976
1025
  # actions: ["*"]
977
1026
  # riskLevels: [admin, destructive]
978
1027
  # conditions:
979
- # users: ["${Se||"YOUR_USER_ID_HERE"}"]
980
- `,Tu=`# Alfred \u2014 Default Security Rules
1028
+ # users: ["${ve||"YOUR_USER_ID_HERE"}"]
1029
+ `,$u=`# Alfred \u2014 Default Security Rules
981
1030
  # Rules are evaluated in priority order (lower number = higher priority).
982
1031
  # First matching rule wins.
983
1032
 
@@ -990,7 +1039,7 @@ rules:
990
1039
  actions: ["*"]
991
1040
  riskLevels: [read]
992
1041
 
993
- ${Ns?` # Allow write-level skills everywhere (DMs and groups)
1042
+ ${Ps?` # Allow write-level skills everywhere (DMs and groups)
994
1043
  - id: allow-write-all
995
1044
  effect: allow
996
1045
  priority: 200
@@ -1006,7 +1055,7 @@ ${Ns?` # Allow write-level skills everywhere (DMs and groups)
1006
1055
  conditions:
1007
1056
  chatType: dm`}
1008
1057
 
1009
- # Rate-limit write actions: max ${en} per hour per user
1058
+ # Rate-limit write actions: max ${sn} per hour per user
1010
1059
  - id: rate-limit-write
1011
1060
  effect: allow
1012
1061
  priority: 250
@@ -1014,9 +1063,9 @@ ${Ns?` # Allow write-level skills everywhere (DMs and groups)
1014
1063
  actions: ["*"]
1015
1064
  riskLevels: [write]
1016
1065
  rateLimit:
1017
- maxInvocations: ${en}
1066
+ maxInvocations: ${sn}
1018
1067
  windowSeconds: 3600
1019
- ${wu}
1068
+ ${Eu}
1020
1069
  # Deny destructive and admin actions by default
1021
1070
  - id: deny-destructive
1022
1071
  effect: deny
@@ -1032,24 +1081,24 @@ ${wu}
1032
1081
  scope: global
1033
1082
  actions: ["*"]
1034
1083
  riskLevels: [read, write, destructive, admin]
1035
- `,_u=ke.join($o,"default-rules.yml");ye.writeFileSync(_u,Tu,"utf-8"),console.log(` ${N("+")} ${b("config/rules/default-rules.yml")} written`);let $a=ke.join(e,"data");ye.existsSync($a)||(ye.mkdirSync($a,{recursive:!0}),console.log(` ${N("+")} ${b("data/")} directory created`)),console.log(""),console.log(`${Xn}${"=".repeat(52)}${S}`),console.log(`${Xn}${G} Setup complete!${S}`),console.log(`${Xn}${"=".repeat(52)}${S}`),console.log(""),console.log(` ${C("Bot name:")} ${r}`),console.log(` ${C("LLM default:")} ${a.name} (${f})`),c&&console.log(` ${C("API key:")} ${Te(c)}`);for(let[T,k]of Object.entries(x)){let I=T.charAt(0).toUpperCase()+T.slice(1);console.log(` ${C(`LLM ${I}:`)}${" ".repeat(Math.max(1,10-I.length))}${k.provider} (${k.model})`)}if(ie.length>0?console.log(` ${C("Platforms:")} ${ie.map(T=>T.label).join(", ")}`):console.log(` ${C("Platforms:")} none (configure later)`),j){let T={brave:"Brave Search",tavily:"Tavily",duckduckgo:"DuckDuckGo",searxng:`SearXNG (${we})`};console.log(` ${C("Web search:")} ${T[j]}`)}else console.log(` ${C("Web search:")} ${b("disabled")}`);if(Hr&&He.length>0){let T=He.map(k=>k.provider==="microsoft"?`${k.name} (Microsoft 365)${k.msClientId?"":" \u2014 shared from Calendar"}`:`${k.name} (${k.imapHost})`);console.log(` ${C("Email:")} ${T.join(", ")}`)}else console.log(` ${C("Email:")} ${b("disabled")}`);if(qe){let T={openai:"OpenAI Whisper",groq:"Groq Whisper"},k=_s?`, TTS: ${bs}`:"";console.log(` ${C("Voice:")} ${T[qe]}${k}`)}else console.log(` ${C("Voice:")} ${b("disabled")}`);console.log(` ${C("Code Sandbox:")} ${eo?N("enabled"):b("disabled")}`),Wr&&console.log(` ${C("Proxmox:")} ${N(vs)}`),As&&console.log(` ${C("UniFi:")} ${N(Lt)}`),Vr&&console.log(` ${C("Home Assist.:")} ${N(xs)}`),Kr&&console.log(` ${C("Contacts:")} ${N(Ce)}`),Xr&&console.log(` ${C("Docker:")} ${N(mt||ut)}`),Yr&&console.log(` ${C("BMW CarData:")} ${N("enabled")}`),Jr&&console.log(` ${C("Routing:")} ${N("enabled")}`),Zr&&console.log(` ${C("Energy:")} ${N(pt||"enabled")} ${b(`(${ht} ct/kWh)`)}`),Se&&(console.log(` ${C("Owner ID:")} ${Se}`),console.log(` ${C("Shell access:")} ${Ls?N("enabled"):b("disabled")}`)),console.log(` ${C("Write scope:")} ${Ns?"DMs + Groups":"DMs only"}`),console.log(` ${C("Rate limit:")} ${en}/hour per user`),console.log(""),console.log(`${Yn}Next steps:${S}`),console.log(` ${C("alfred start")} Start Alfred`),console.log(` ${C("alfred status")} Check configuration`),console.log(` ${C("alfred --help")} Show all commands`),console.log(""),console.log(`${ot}Edit ${C(".env")}${ot} or ${C("config/default.yml")}${ot} for manual configuration.${S}`),console.log("")}finally{l.close()}}async function F(l,e,t){let s=(await l.question(`${G}${e}${S} ${b(`[${t}]`)}: ${M}`)).trim();return process.stdout.write(S),s||t}async function xe(l,e){for(;;){let t=(await l.question(`${G}${e}${S}: ${M}`)).trim();if(process.stdout.write(S),t)return t;console.log(` ${xd("!")} This field is required. Please enter a value.`)}}async function Ur(l,e,t,s,r){for(;;){let n=(await l.question(`${M}${e}${S}`)).trim();if(!n)return r;let o=parseInt(n,10);if(!Number.isNaN(o)&&o>=t&&o<=s)return o;console.log(` ${xd("!")} Please enter a number between ${t} and ${s}.`)}}function ph(){console.log(`
1036
- ${ch}${G} _ _ _____ ____ _____ ____
1084
+ `,ku=ke.join(vo,"default-rules.yml");ye.writeFileSync(ku,$u,"utf-8"),console.log(` ${O("+")} ${b("config/rules/default-rules.yml")} written`);let Sa=ke.join(e,"data");ye.existsSync(Sa)||(ye.mkdirSync(Sa,{recursive:!0}),console.log(` ${O("+")} ${b("data/")} directory created`)),console.log(""),console.log(`${Jn}${"=".repeat(52)}${S}`),console.log(`${Jn}${V} Setup complete!${S}`),console.log(`${Jn}${"=".repeat(52)}${S}`),console.log(""),console.log(` ${R("Bot name:")} ${r}`),console.log(` ${R("LLM default:")} ${a.name} (${g})`),c&&console.log(` ${R("API key:")} ${_e(c)}`);for(let[T,k]of Object.entries(N)){let I=T.charAt(0).toUpperCase()+T.slice(1);console.log(` ${R(`LLM ${I}:`)}${" ".repeat(Math.max(1,10-I.length))}${k.provider} (${k.model})`)}if(B.length>0?console.log(` ${R("Platforms:")} ${B.map(T=>T.label).join(", ")}`):console.log(` ${R("Platforms:")} none (configure later)`),se){let T={brave:"Brave Search",tavily:"Tavily",duckduckgo:"DuckDuckGo",searxng:`SearXNG (${z})`};console.log(` ${R("Web search:")} ${T[se]}`)}else console.log(` ${R("Web search:")} ${b("disabled")}`);if(Te&&Ce.length>0){let T=Ce.map(k=>k.provider==="microsoft"?`${k.name} (Microsoft 365)${k.msClientId?"":" \u2014 shared from Calendar"}`:`${k.name} (${k.imapHost})`);console.log(` ${R("Email:")} ${T.join(", ")}`)}else console.log(` ${R("Email:")} ${b("disabled")}`);if(qe){let T={openai:"OpenAI Whisper",groq:"Groq Whisper"},k=$s?`, TTS: ${ks}`:"";console.log(` ${R("Voice:")} ${T[qe]}${k}`)}else console.log(` ${R("Voice:")} ${b("disabled")}`);console.log(` ${R("Code Sandbox:")} ${so?O("enabled"):b("disabled")}`),Vr&&console.log(` ${R("Proxmox:")} ${O(Is)}`),Rs&&console.log(` ${R("UniFi:")} ${O(Mt)}`),Xr&&console.log(` ${R("Home Assist.:")} ${O(Ds)}`),Yr&&console.log(` ${R("Contacts:")} ${O(Le)}`),Jr&&console.log(` ${R("Docker:")} ${O(pt||mt)}`),Zr&&console.log(` ${R("BMW CarData:")} ${O("enabled")}`),Qr&&console.log(` ${R("Routing:")} ${O("enabled")}`),en&&console.log(` ${R("Energy:")} ${O(ht||"enabled")} ${b(`(${ft} ct/kWh)`)}`),ve&&(console.log(` ${R("Owner ID:")} ${ve}`),console.log(` ${R("Shell access:")} ${Os?O("enabled"):b("disabled")}`)),console.log(` ${R("Write scope:")} ${Ps?"DMs + Groups":"DMs only"}`),console.log(` ${R("Rate limit:")} ${sn}/hour per user`),console.log(""),console.log(`${Zn}Next steps:${S}`),console.log(` ${R("alfred start")} Start Alfred`),console.log(` ${R("alfred status")} Check configuration`),console.log(` ${R("alfred --help")} Show all commands`),console.log(""),console.log(`${at}Edit ${R(".env")}${at} or ${R("config/default.yml")}${at} for manual configuration.${S}`),console.log("")}finally{l.close()}}async function F(l,e,t){let s=(await l.question(`${V}${e}${S} ${b(`[${t}]`)}: ${P}`)).trim();return process.stdout.write(S),s||t}async function Ie(l,e){for(;;){let t=(await l.question(`${V}${e}${S}: ${P}`)).trim();if(process.stdout.write(S),t)return t;console.log(` ${Ld("!")} This field is required. Please enter a value.`)}}async function zr(l,e,t,s,r){for(;;){let n=(await l.question(`${P}${e}${S}`)).trim();if(!n)return r;let o=parseInt(n,10);if(!Number.isNaN(o)&&o>=t&&o<=s)return o;console.log(` ${Ld("!")} Please enter a number between ${t} and ${s}.`)}}function wh(){console.log(`
1085
+ ${ph}${V} _ _ _____ ____ _____ ____
1037
1086
  / \\ | | | ___| _ \\| ____| _ \\
1038
1087
  / _ \\ | | | |_ | |_) | _| | | | |
1039
1088
  / ___ \\| |___| _| | _ <| |___| |_| |
1040
1089
  /_/ \\_\\_____|_| |_| \\_\\_____|____/ ${S}
1041
- ${ot} Personal AI Assistant \u2014 Setup Wizard${S}
1042
- `)}var S,G,ot,Xn,M,Yn,ah,ch,Ze,ws,Id,Cd=_(()=>{"use strict";ra();S="\x1B[0m",G="\x1B[1m",ot="\x1B[2m",Xn="\x1B[32m",M="\x1B[33m",Yn="\x1B[36m",ah="\x1B[31m",ch="\x1B[35m";m(N,"green");m(lh,"yellow");m(ge,"cyan");m(xd,"red");m(C,"bold");m(b,"dim");m(Te,"maskKey");Ze=[{name:"anthropic",label:"Anthropic (Claude) \u2014 recommended",defaultModel:"claude-sonnet-4-20250514",envKeyName:"ALFRED_ANTHROPIC_API_KEY",needsApiKey:!0,models:[{id:"claude-sonnet-4-20250514",desc:"Sonnet 4 \u2014 fast, smart, recommended"},{id:"claude-opus-4-20250514",desc:"Opus 4 \u2014 most capable, slower"},{id:"claude-haiku-4-5-20251001",desc:"Haiku 4.5 \u2014 fastest, cheapest"}]},{name:"openai",label:"OpenAI (GPT)",defaultModel:"gpt-4o",envKeyName:"ALFRED_OPENAI_API_KEY",needsApiKey:!0,models:[{id:"gpt-4o",desc:"GPT-4o \u2014 flagship, 128k context"},{id:"gpt-4o-mini",desc:"GPT-4o Mini \u2014 fast, cheap, 128k context"},{id:"o3-mini",desc:"o3-mini \u2014 reasoning, 200k context"}]},{name:"openrouter",label:"OpenRouter (multiple providers)",defaultModel:"anthropic/claude-sonnet-4-20250514",envKeyName:"ALFRED_OPENROUTER_API_KEY",needsApiKey:!0,baseUrl:"https://openrouter.ai/api/v1"},{name:"ollama",label:"Ollama (local, no API key needed)",defaultModel:"llama3.2",envKeyName:"",needsApiKey:!1,baseUrl:"http://localhost:11434"},{name:"openwebui",label:"OpenWebUI (local OpenAI-compatible UI)",defaultModel:"llama3.2",envKeyName:"ALFRED_OPENWEBUI_API_KEY",needsApiKey:!0,baseUrl:"http://localhost:3000/api/v1"},{name:"google",label:"Google (Gemini)",defaultModel:"gemini-2.0-flash",envKeyName:"ALFRED_GOOGLE_API_KEY",needsApiKey:!0,models:[{id:"gemini-2.0-flash",desc:"Flash 2.0 \u2014 fast, 1M context"},{id:"gemini-2.0-pro",desc:"Pro 2.0 \u2014 capable, 1M context"},{id:"gemini-1.5-pro",desc:"Pro 1.5 \u2014 2M context"},{id:"gemini-1.5-flash",desc:"Flash 1.5 \u2014 fast, 1M context"}]},{name:"mistral",label:"Mistral AI",defaultModel:"mistral-small-latest",envKeyName:"ALFRED_MISTRAL_API_KEY",needsApiKey:!0,models:[{id:"mistral-small-latest",desc:"Small 3.2 \u2014 fast, 128k context, best value"},{id:"mistral-medium-latest",desc:"Medium 3.1 \u2014 balanced, 128k context"},{id:"mistral-large-latest",desc:"Large 3 \u2014 flagship, 256k context"},{id:"codestral-latest",desc:"Codestral \u2014 code-optimized, 256k context"},{id:"magistral-medium-latest",desc:"Magistral Medium \u2014 reasoning, 40k context"},{id:"magistral-small-latest",desc:"Magistral Small \u2014 reasoning (light), 40k context"},{id:"ministral-8b-latest",desc:"Ministral 8B \u2014 edge/tiny, 128k context"}]}],ws=[{name:"telegram",label:"Telegram",configKey:"telegram",credentials:[{envKey:"ALFRED_TELEGRAM_TOKEN",configField:"token",prompt:"Enter your Telegram Bot token (from @BotFather)",required:!0}]},{name:"discord",label:"Discord",configKey:"discord",credentials:[{envKey:"ALFRED_DISCORD_TOKEN",configField:"token",prompt:"Enter your Discord Bot token",required:!0}]},{name:"whatsapp",label:"WhatsApp",configKey:"whatsapp",credentials:[]},{name:"matrix",label:"Matrix",configKey:"matrix",credentials:[{envKey:"ALFRED_MATRIX_HOMESERVER_URL",configField:"homeserverUrl",prompt:"Enter your Matrix homeserver URL",defaultValue:"https://matrix.org",required:!0},{envKey:"ALFRED_MATRIX_ACCESS_TOKEN",configField:"accessToken",prompt:"Enter your Matrix access token",required:!0},{envKey:"ALFRED_MATRIX_USER_ID",configField:"userId",prompt:"Enter your Matrix user ID (e.g. @bot:matrix.org)",required:!0}]},{name:"signal",label:"Signal",configKey:"signal",credentials:[{envKey:"ALFRED_SIGNAL_API_URL",configField:"apiUrl",prompt:"Enter the Signal REST API URL",defaultValue:"http://localhost:8080",required:!0},{envKey:"ALFRED_SIGNAL_PHONE_NUMBER",configField:"phoneNumber",prompt:"Enter the Signal phone number (e.g. +15551234567)",required:!0}]}];m(dh,"findCommand");Id=[{name:"claude-code",label:"Claude Code",command:"claude",argsTemplate:["-p","{{prompt}}"],promptVia:"arg",whichCmd:"claude"},{name:"codex",label:"OpenAI Codex CLI",command:"codex",argsTemplate:["{{prompt}}"],promptVia:"arg",whichCmd:"codex"},{name:"aider",label:"Aider",command:"aider",argsTemplate:["--message","{{prompt}}"],promptVia:"arg",whichCmd:"aider"},{name:"gemini",label:"Gemini CLI",command:"gemini",argsTemplate:["-p","{{prompt}}"],promptVia:"arg",whichCmd:"gemini"}];m(uh,"loadExistingConfig");m(mh,"setupCommand");m(F,"askWithDefault");m(xe,"askRequired");m(Ur,"askNumber");m(ph,"printBanner")});var Ld={};ue(Ld,{configCommand:()=>yh});function fh(l){let e=l.toLowerCase();return hh.some(t=>e.includes(t))}function gh(l){return typeof l!="string"||l.length===0?"(empty)":l.length<=8?"***":l.slice(0,4)+"..."+l.slice(-4)}function Dd(l){let e={};for(let[t,s]of Object.entries(l))fh(t)?e[t]=gh(s):s!=null&&typeof s=="object"&&!Array.isArray(s)?e[t]=Dd(s):e[t]=s;return e}async function yh(){let l=new ce,e;try{e=l.loadConfig()}catch(s){console.error("Failed to load configuration:",s.message),process.exit(1)}let t=Dd(e);console.log("Alfred \u2014 Resolved Configuration"),console.log("================================"),console.log(JSON.stringify(t,null,2))}var hh,Nd=_(()=>{"use strict";Ve();hh=["token","apikey","api_key","accesstoken","secret","password"];m(fh,"isSensitiveKey");m(gh,"redactValue");m(Dd,"redactObject");m(yh,"configCommand")});var Od={};ue(Od,{rulesCommand:()=>Th});import Jn from"node:fs";import Md from"node:path";import wh from"js-yaml";async function Th(){let l=new ce,e;try{e=l.loadConfig()}catch(a){console.error("Failed to load configuration:",a.message),process.exit(1)}let t=Md.resolve(e.security.rulesPath);if(!Jn.existsSync(t)){console.log(`Rules directory not found: ${t}`),console.log("No security rules loaded.");return}Jn.statSync(t).isDirectory()||(console.error(`Rules path is not a directory: ${t}`),process.exit(1));let r=Jn.readdirSync(t).filter(a=>a.endsWith(".yml")||a.endsWith(".yaml"));if(r.length===0){console.log(`No YAML rule files found in: ${t}`);return}let n=new bt,o=[],i=[];for(let a of r){let c=Md.join(t,a);try{let d=Jn.readFileSync(c,"utf-8"),u=wh.load(d),p=n.loadFromObject(u);o.push(...p)}catch(d){i.push(` ${a}: ${d.message}`)}}if(console.log("Alfred \u2014 Security Rules"),console.log("======================="),console.log(`Rules directory: ${t}`),console.log(`Rule files found: ${r.length}`),console.log(`Total rules loaded: ${o.length}`),console.log(""),i.length>0){console.log("Errors:");for(let a of i)console.log(a);console.log("")}if(o.length!==0){o.sort((a,c)=>a.priority-c.priority),console.log("Loaded rules (sorted by priority):"),console.log("");for(let a of o){let c=a.rateLimit?` | rate-limit: ${a.rateLimit.maxInvocations}/${a.rateLimit.windowSeconds}s`:"";console.log(` [${a.priority}] ${a.id}`),console.log(` effect: ${a.effect} | scope: ${a.scope}`),console.log(` actions: ${a.actions.join(", ")}`),console.log(` risk levels: ${a.riskLevels.join(", ")}${c}`),a.conditions&&console.log(` conditions: ${JSON.stringify(a.conditions)}`),console.log("")}}}var Pd=_(()=>{"use strict";Ve();an();m(Th,"rulesCommand")});var Ud={};ue(Ud,{statusCommand:()=>bh});import Fr from"node:fs";import oa from"node:path";import _h from"js-yaml";async function bh(){let l=new ce,e;try{e=l.loadConfig()}catch(c){console.error("Failed to load configuration:",c.message),process.exit(1)}console.log("Alfred \u2014 Status"),console.log("================"),console.log("");let t=[{name:"Telegram",enabled:e.telegram.enabled,configured:!!e.telegram.token},{name:"Discord",enabled:!!e.discord?.enabled,configured:!!e.discord?.token},{name:"WhatsApp",enabled:!!e.whatsapp?.enabled,configured:!!e.whatsapp?.dataPath},{name:"Matrix",enabled:!!e.matrix?.enabled,configured:!!e.matrix?.accessToken},{name:"Signal",enabled:!!e.signal?.enabled,configured:!!e.signal?.phoneNumber}];console.log("Messaging Adapters:");for(let c of t){let d=c.enabled?"enabled":c.configured?"configured (disabled)":"not configured",u=c.enabled?"+":"-";console.log(` [${u}] ${c.name}: ${d}`)}console.log(""),console.log("LLM Provider:");let s=e.llm.default;console.log(` Provider: ${s.provider}`),console.log(` Model: ${s.model}`),console.log(` API Key: ${s.apiKey?"set":"not set"}`),s.baseUrl&&console.log(` Base URL: ${s.baseUrl}`);for(let c of["strong","fast","embeddings","local"]){let d=e.llm[c];d&&console.log(` ${c}: ${d.provider}/${d.model}`)}console.log(""),console.log("Storage:");let r=oa.resolve(e.storage.path),n=Fr.existsSync(r);console.log(` Database: ${r}`),console.log(` Status: ${n?"exists":"not yet created"}`),console.log("");let o=oa.resolve(e.security.rulesPath),i=0,a=0;if(Fr.existsSync(o)&&Fr.statSync(o).isDirectory()){let c=Fr.readdirSync(o).filter(u=>u.endsWith(".yml")||u.endsWith(".yaml"));a=c.length;let d=new bt;for(let u of c){let p=oa.join(o,u);try{let g=Fr.readFileSync(p,"utf-8"),f=_h.load(g),h=d.loadFromObject(f);i+=h.length}catch{}}}console.log("Security:"),console.log(` Rules path: ${o}`),console.log(` Rule files: ${a}`),console.log(` Rules loaded: ${i}`),console.log(` Default effect: ${e.security.defaultEffect}`),e.security.ownerUserId&&console.log(` Owner user ID: ${e.security.ownerUserId}`),console.log(""),console.log("Logger:"),console.log(` Level: ${e.logger.level}`),console.log(` Pretty: ${e.logger.pretty}`)}var Fd=_(()=>{"use strict";Ve();an();m(bh,"statusCommand")});var Hd={};ue(Hd,{authCommand:()=>Ph});import{createServer as Eh}from"node:http";import{exec as $h}from"node:child_process";import{readFileSync as kh,writeFileSync as vh,existsSync as Sh}from"node:fs";import{createInterface as Ah}from"node:readline";import{resolve as Ih}from"node:path";async function ia(l){let e=Ah({input:process.stdin,output:process.stdout});return new Promise(t=>{e.question(l,s=>{e.close(),t(s.trim())})})}function Rh(){let l={};try{let s=new ce().loadConfig();for(let o of["email","calendar","contacts"]){let i=s[o];if(!i)continue;let a=i.microsoft;a&&(!l.clientId&&a.clientId&&(l.clientId=a.clientId),!l.clientSecret&&a.clientSecret&&(l.clientSecret=a.clientSecret),!l.tenantId&&a.tenantId&&(l.tenantId=a.tenantId))}let r=s.todo;r&&(!l.clientId&&r.clientId&&(l.clientId=r.clientId),!l.clientSecret&&r.clientSecret&&(l.clientSecret=r.clientSecret),!l.tenantId&&r.tenantId&&(l.tenantId=r.tenantId));let n=s.email;if(n?.accounts&&Array.isArray(n.accounts))for(let o of n.accounts){let i=o.microsoft;i&&(!l.clientId&&i.clientId&&(l.clientId=i.clientId),!l.clientSecret&&i.clientSecret&&(l.clientSecret=i.clientSecret),!l.tenantId&&i.tenantId&&(l.tenantId=i.tenantId))}}catch{}let e=["ALFRED_MICROSOFT_EMAIL","ALFRED_MICROSOFT_CALENDAR","ALFRED_MICROSOFT_CONTACTS","ALFRED_MICROSOFT_TODO"];for(let t of e)l.clientId||(l.clientId=process.env[`${t}_CLIENT_ID`]),l.clientSecret||(l.clientSecret=process.env[`${t}_CLIENT_SECRET`]),l.tenantId||(l.tenantId=process.env[`${t}_TENANT_ID`]);return l}async function Ch(){let l=Rh(),e=l.clientId||await ia(" Client ID: "),t=l.clientSecret||await ia(" Client Secret: "),s=l.tenantId||await ia(' Tenant ID (oder "common"): ');return(!e||!t||!s)&&(console.error("Fehler: Client ID, Client Secret und Tenant ID werden ben\xF6tigt."),process.exit(1)),{clientId:e,clientSecret:t,tenantId:s}}function Dh(l){let e=new URLSearchParams({client_id:l.clientId,response_type:"code",redirect_uri:jd,response_mode:"query",scope:Bd,prompt:"consent"});return`https://login.microsoftonline.com/${l.tenantId}/oauth2/v2.0/authorize?${e}`}async function Lh(l,e){let t=new URLSearchParams({client_id:e.clientId,client_secret:e.clientSecret,code:l,redirect_uri:jd,grant_type:"authorization_code",scope:Bd}),s=await fetch(`https://login.microsoftonline.com/${e.tenantId}/oauth2/v2.0/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t});if(!s.ok){let n=await s.text();throw new Error(`Token-Austausch fehlgeschlagen (${s.status}): ${n}`)}let r=await s.json();if(!r.refresh_token)throw new Error('Kein Refresh Token in der Antwort. Stelle sicher, dass "offline_access" als Scope erlaubt ist.');return r.refresh_token}function Nh(l){let t={win32:`start "" "${l}"`,darwin:`open "${l}"`,linux:`xdg-open "${l}"`}[process.platform];t&&$h(t,()=>{})}function Mh(l,e){let t=Ih(process.cwd(),".env"),s=[];Sh(t)&&(s=kh(t,"utf-8").split(`
1090
+ ${at} Personal AI Assistant \u2014 Setup Wizard${S}
1091
+ `)}var S,V,at,Jn,P,Zn,mh,ph,Qe,_s,Dd,Md=_(()=>{"use strict";ia();S="\x1B[0m",V="\x1B[1m",at="\x1B[2m",Jn="\x1B[32m",P="\x1B[33m",Zn="\x1B[36m",mh="\x1B[31m",ph="\x1B[35m";m(O,"green");m(hh,"yellow");m(ge,"cyan");m(Ld,"red");m(R,"bold");m(b,"dim");m(_e,"maskKey");Qe=[{name:"anthropic",label:"Anthropic (Claude) \u2014 recommended",defaultModel:"claude-sonnet-4-20250514",envKeyName:"ALFRED_ANTHROPIC_API_KEY",needsApiKey:!0,models:[{id:"claude-sonnet-4-20250514",desc:"Sonnet 4 \u2014 fast, smart, recommended"},{id:"claude-opus-4-20250514",desc:"Opus 4 \u2014 most capable, slower"},{id:"claude-haiku-4-5-20251001",desc:"Haiku 4.5 \u2014 fastest, cheapest"}]},{name:"openai",label:"OpenAI (GPT)",defaultModel:"gpt-4o",envKeyName:"ALFRED_OPENAI_API_KEY",needsApiKey:!0,models:[{id:"gpt-4o",desc:"GPT-4o \u2014 flagship, 128k context"},{id:"gpt-4o-mini",desc:"GPT-4o Mini \u2014 fast, cheap, 128k context"},{id:"o3-mini",desc:"o3-mini \u2014 reasoning, 200k context"}]},{name:"openrouter",label:"OpenRouter (multiple providers)",defaultModel:"anthropic/claude-sonnet-4-20250514",envKeyName:"ALFRED_OPENROUTER_API_KEY",needsApiKey:!0,baseUrl:"https://openrouter.ai/api/v1"},{name:"ollama",label:"Ollama (local, no API key needed)",defaultModel:"llama3.2",envKeyName:"",needsApiKey:!1,baseUrl:"http://localhost:11434"},{name:"openwebui",label:"OpenWebUI (local OpenAI-compatible UI)",defaultModel:"llama3.2",envKeyName:"ALFRED_OPENWEBUI_API_KEY",needsApiKey:!0,baseUrl:"http://localhost:3000/api/v1"},{name:"google",label:"Google (Gemini)",defaultModel:"gemini-2.0-flash",envKeyName:"ALFRED_GOOGLE_API_KEY",needsApiKey:!0,models:[{id:"gemini-2.0-flash",desc:"Flash 2.0 \u2014 fast, 1M context"},{id:"gemini-2.0-pro",desc:"Pro 2.0 \u2014 capable, 1M context"},{id:"gemini-1.5-pro",desc:"Pro 1.5 \u2014 2M context"},{id:"gemini-1.5-flash",desc:"Flash 1.5 \u2014 fast, 1M context"}]},{name:"mistral",label:"Mistral AI",defaultModel:"mistral-small-latest",envKeyName:"ALFRED_MISTRAL_API_KEY",needsApiKey:!0,models:[{id:"mistral-small-latest",desc:"Small 3.2 \u2014 fast, 128k context, best value"},{id:"mistral-medium-latest",desc:"Medium 3.1 \u2014 balanced, 128k context"},{id:"mistral-large-latest",desc:"Large 3 \u2014 flagship, 256k context"},{id:"codestral-latest",desc:"Codestral \u2014 code-optimized, 256k context"},{id:"magistral-medium-latest",desc:"Magistral Medium \u2014 reasoning, 40k context"},{id:"magistral-small-latest",desc:"Magistral Small \u2014 reasoning (light), 40k context"},{id:"ministral-8b-latest",desc:"Ministral 8B \u2014 edge/tiny, 128k context"}]}],_s=[{name:"telegram",label:"Telegram",configKey:"telegram",credentials:[{envKey:"ALFRED_TELEGRAM_TOKEN",configField:"token",prompt:"Enter your Telegram Bot token (from @BotFather)",required:!0}]},{name:"discord",label:"Discord",configKey:"discord",credentials:[{envKey:"ALFRED_DISCORD_TOKEN",configField:"token",prompt:"Enter your Discord Bot token",required:!0}]},{name:"whatsapp",label:"WhatsApp",configKey:"whatsapp",credentials:[]},{name:"matrix",label:"Matrix",configKey:"matrix",credentials:[{envKey:"ALFRED_MATRIX_HOMESERVER_URL",configField:"homeserverUrl",prompt:"Enter your Matrix homeserver URL",defaultValue:"https://matrix.org",required:!0},{envKey:"ALFRED_MATRIX_ACCESS_TOKEN",configField:"accessToken",prompt:"Enter your Matrix access token",required:!0},{envKey:"ALFRED_MATRIX_USER_ID",configField:"userId",prompt:"Enter your Matrix user ID (e.g. @bot:matrix.org)",required:!0}]},{name:"signal",label:"Signal",configKey:"signal",credentials:[{envKey:"ALFRED_SIGNAL_API_URL",configField:"apiUrl",prompt:"Enter the Signal REST API URL",defaultValue:"http://localhost:8080",required:!0},{envKey:"ALFRED_SIGNAL_PHONE_NUMBER",configField:"phoneNumber",prompt:"Enter the Signal phone number (e.g. +15551234567)",required:!0}]}];m(fh,"findCommand");Dd=[{name:"claude-code",label:"Claude Code",command:"claude",argsTemplate:["-p","{{prompt}}"],promptVia:"arg",whichCmd:"claude"},{name:"codex",label:"OpenAI Codex CLI",command:"codex",argsTemplate:["{{prompt}}"],promptVia:"arg",whichCmd:"codex"},{name:"aider",label:"Aider",command:"aider",argsTemplate:["--message","{{prompt}}"],promptVia:"arg",whichCmd:"aider"},{name:"gemini",label:"Gemini CLI",command:"gemini",argsTemplate:["-p","{{prompt}}"],promptVia:"arg",whichCmd:"gemini"}];m(gh,"loadExistingConfig");m(yh,"setupCommand");m(F,"askWithDefault");m(Ie,"askRequired");m(zr,"askNumber");m(wh,"printBanner")});var Pd={};le(Pd,{configCommand:()=>Eh});function _h(l){let e=l.toLowerCase();return Th.some(t=>e.includes(t))}function bh(l){return typeof l!="string"||l.length===0?"(empty)":l.length<=8?"***":l.slice(0,4)+"..."+l.slice(-4)}function Od(l){let e={};for(let[t,s]of Object.entries(l))_h(t)?e[t]=bh(s):s!=null&&typeof s=="object"&&!Array.isArray(s)?e[t]=Od(s):e[t]=s;return e}async function Eh(){let l=new ce,e;try{e=l.loadConfig()}catch(s){console.error("Failed to load configuration:",s.message),process.exit(1)}let t=Od(e);console.log("Alfred \u2014 Resolved Configuration"),console.log("================================"),console.log(JSON.stringify(t,null,2))}var Th,Ud=_(()=>{"use strict";Ke();Th=["token","apikey","api_key","accesstoken","secret","password"];m(_h,"isSensitiveKey");m(bh,"redactValue");m(Od,"redactObject");m(Eh,"configCommand")});var jd={};le(jd,{rulesCommand:()=>kh});import Qn from"node:fs";import Fd from"node:path";import $h from"js-yaml";async function kh(){let l=new ce,e;try{e=l.loadConfig()}catch(a){console.error("Failed to load configuration:",a.message),process.exit(1)}let t=Fd.resolve(e.security.rulesPath);if(!Qn.existsSync(t)){console.log(`Rules directory not found: ${t}`),console.log("No security rules loaded.");return}Qn.statSync(t).isDirectory()||(console.error(`Rules path is not a directory: ${t}`),process.exit(1));let r=Qn.readdirSync(t).filter(a=>a.endsWith(".yml")||a.endsWith(".yaml"));if(r.length===0){console.log(`No YAML rule files found in: ${t}`);return}let n=new Et,o=[],i=[];for(let a of r){let c=Fd.join(t,a);try{let d=Qn.readFileSync(c,"utf-8"),u=$h.load(d),p=n.loadFromObject(u);o.push(...p)}catch(d){i.push(` ${a}: ${d.message}`)}}if(console.log("Alfred \u2014 Security Rules"),console.log("======================="),console.log(`Rules directory: ${t}`),console.log(`Rule files found: ${r.length}`),console.log(`Total rules loaded: ${o.length}`),console.log(""),i.length>0){console.log("Errors:");for(let a of i)console.log(a);console.log("")}if(o.length!==0){o.sort((a,c)=>a.priority-c.priority),console.log("Loaded rules (sorted by priority):"),console.log("");for(let a of o){let c=a.rateLimit?` | rate-limit: ${a.rateLimit.maxInvocations}/${a.rateLimit.windowSeconds}s`:"";console.log(` [${a.priority}] ${a.id}`),console.log(` effect: ${a.effect} | scope: ${a.scope}`),console.log(` actions: ${a.actions.join(", ")}`),console.log(` risk levels: ${a.riskLevels.join(", ")}${c}`),a.conditions&&console.log(` conditions: ${JSON.stringify(a.conditions)}`),console.log("")}}}var Bd=_(()=>{"use strict";Ke();ln();m(kh,"rulesCommand")});var Hd={};le(Hd,{statusCommand:()=>Sh});import qr from"node:fs";import ca from"node:path";import vh from"js-yaml";async function Sh(){let l=new ce,e;try{e=l.loadConfig()}catch(c){console.error("Failed to load configuration:",c.message),process.exit(1)}console.log("Alfred \u2014 Status"),console.log("================"),console.log("");let t=[{name:"Telegram",enabled:e.telegram.enabled,configured:!!e.telegram.token},{name:"Discord",enabled:!!e.discord?.enabled,configured:!!e.discord?.token},{name:"WhatsApp",enabled:!!e.whatsapp?.enabled,configured:!!e.whatsapp?.dataPath},{name:"Matrix",enabled:!!e.matrix?.enabled,configured:!!e.matrix?.accessToken},{name:"Signal",enabled:!!e.signal?.enabled,configured:!!e.signal?.phoneNumber}];console.log("Messaging Adapters:");for(let c of t){let d=c.enabled?"enabled":c.configured?"configured (disabled)":"not configured",u=c.enabled?"+":"-";console.log(` [${u}] ${c.name}: ${d}`)}console.log(""),console.log("LLM Provider:");let s=e.llm.default;console.log(` Provider: ${s.provider}`),console.log(` Model: ${s.model}`),console.log(` API Key: ${s.apiKey?"set":"not set"}`),s.baseUrl&&console.log(` Base URL: ${s.baseUrl}`);for(let c of["strong","fast","embeddings","local"]){let d=e.llm[c];d&&console.log(` ${c}: ${d.provider}/${d.model}`)}console.log(""),console.log("Storage:");let r=ca.resolve(e.storage.path),n=qr.existsSync(r);console.log(` Database: ${r}`),console.log(` Status: ${n?"exists":"not yet created"}`),console.log("");let o=ca.resolve(e.security.rulesPath),i=0,a=0;if(qr.existsSync(o)&&qr.statSync(o).isDirectory()){let c=qr.readdirSync(o).filter(u=>u.endsWith(".yml")||u.endsWith(".yaml"));a=c.length;let d=new Et;for(let u of c){let p=ca.join(o,u);try{let f=qr.readFileSync(p,"utf-8"),g=vh.load(f),h=d.loadFromObject(g);i+=h.length}catch{}}}console.log("Security:"),console.log(` Rules path: ${o}`),console.log(` Rule files: ${a}`),console.log(` Rules loaded: ${i}`),console.log(` Default effect: ${e.security.defaultEffect}`),e.security.ownerUserId&&console.log(` Owner user ID: ${e.security.ownerUserId}`),console.log(""),console.log("Logger:"),console.log(` Level: ${e.logger.level}`),console.log(` Pretty: ${e.logger.pretty}`)}var zd=_(()=>{"use strict";Ke();ln();m(Sh,"statusCommand")});var Gd={};le(Gd,{authCommand:()=>Hh});import{createServer as Ah}from"node:http";import{exec as Ih}from"node:child_process";import{readFileSync as xh,writeFileSync as Rh,existsSync as Ch}from"node:fs";import{createInterface as Dh}from"node:readline";import{resolve as Lh}from"node:path";async function la(l){let e=Dh({input:process.stdin,output:process.stdout});return new Promise(t=>{e.question(l,s=>{e.close(),t(s.trim())})})}function Mh(){let l={};try{let s=new ce().loadConfig();for(let o of["email","calendar","contacts"]){let i=s[o];if(!i)continue;let a=i.microsoft;a&&(!l.clientId&&a.clientId&&(l.clientId=a.clientId),!l.clientSecret&&a.clientSecret&&(l.clientSecret=a.clientSecret),!l.tenantId&&a.tenantId&&(l.tenantId=a.tenantId))}let r=s.todo;r&&(!l.clientId&&r.clientId&&(l.clientId=r.clientId),!l.clientSecret&&r.clientSecret&&(l.clientSecret=r.clientSecret),!l.tenantId&&r.tenantId&&(l.tenantId=r.tenantId));let n=s.email;if(n?.accounts&&Array.isArray(n.accounts))for(let o of n.accounts){let i=o.microsoft;i&&(!l.clientId&&i.clientId&&(l.clientId=i.clientId),!l.clientSecret&&i.clientSecret&&(l.clientSecret=i.clientSecret),!l.tenantId&&i.tenantId&&(l.tenantId=i.tenantId))}}catch{}let e=["ALFRED_MICROSOFT_EMAIL","ALFRED_MICROSOFT_CALENDAR","ALFRED_MICROSOFT_CONTACTS","ALFRED_MICROSOFT_TODO"];for(let t of e)l.clientId||(l.clientId=process.env[`${t}_CLIENT_ID`]),l.clientSecret||(l.clientSecret=process.env[`${t}_CLIENT_SECRET`]),l.tenantId||(l.tenantId=process.env[`${t}_TENANT_ID`]);return l}async function Oh(){let l=Mh(),e=l.clientId||await la(" Client ID: "),t=l.clientSecret||await la(" Client Secret: "),s=l.tenantId||await la(' Tenant ID (oder "common"): ');return(!e||!t||!s)&&(console.error("Fehler: Client ID, Client Secret und Tenant ID werden ben\xF6tigt."),process.exit(1)),{clientId:e,clientSecret:t,tenantId:s}}function Ph(l){let e=new URLSearchParams({client_id:l.clientId,response_type:"code",redirect_uri:qd,response_mode:"query",scope:Wd,prompt:"consent"});return`https://login.microsoftonline.com/${l.tenantId}/oauth2/v2.0/authorize?${e}`}async function Uh(l,e){let t=new URLSearchParams({client_id:e.clientId,client_secret:e.clientSecret,code:l,redirect_uri:qd,grant_type:"authorization_code",scope:Wd}),s=await fetch(`https://login.microsoftonline.com/${e.tenantId}/oauth2/v2.0/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t});if(!s.ok){let n=await s.text();throw new Error(`Token-Austausch fehlgeschlagen (${s.status}): ${n}`)}let r=await s.json();if(!r.refresh_token)throw new Error('Kein Refresh Token in der Antwort. Stelle sicher, dass "offline_access" als Scope erlaubt ist.');return r.refresh_token}function Fh(l){let t={win32:`start "" "${l}"`,darwin:`open "${l}"`,linux:`xdg-open "${l}"`}[process.platform];t&&Ih(t,()=>{})}function jh(l,e){let t=Lh(process.cwd(),".env"),s=[];Ch(t)&&(s=xh(t,"utf-8").split(`
1043
1092
  `));let r={ALFRED_EMAIL_PROVIDER:"microsoft",ALFRED_CALENDAR_PROVIDER:"microsoft",ALFRED_CONTACTS_PROVIDER:"microsoft",ALFRED_MICROSOFT_EMAIL_CLIENT_ID:l.clientId,ALFRED_MICROSOFT_EMAIL_CLIENT_SECRET:l.clientSecret,ALFRED_MICROSOFT_EMAIL_TENANT_ID:l.tenantId,ALFRED_MICROSOFT_EMAIL_REFRESH_TOKEN:e,ALFRED_MICROSOFT_CALENDAR_CLIENT_ID:l.clientId,ALFRED_MICROSOFT_CALENDAR_CLIENT_SECRET:l.clientSecret,ALFRED_MICROSOFT_CALENDAR_TENANT_ID:l.tenantId,ALFRED_MICROSOFT_CALENDAR_REFRESH_TOKEN:e,ALFRED_MICROSOFT_CONTACTS_CLIENT_ID:l.clientId,ALFRED_MICROSOFT_CONTACTS_CLIENT_SECRET:l.clientSecret,ALFRED_MICROSOFT_CONTACTS_TENANT_ID:l.tenantId,ALFRED_MICROSOFT_CONTACTS_REFRESH_TOKEN:e,ALFRED_MICROSOFT_TODO_CLIENT_ID:l.clientId,ALFRED_MICROSOFT_TODO_CLIENT_SECRET:l.clientSecret,ALFRED_MICROSOFT_TODO_TENANT_ID:l.tenantId,ALFRED_MICROSOFT_TODO_REFRESH_TOKEN:e},n=new Set(Object.keys(r));for(let i=0;i<s.length;i++){let a=s[i].match(/^#?\s*([A-Z_]+)=/);a&&n.has(a[1])&&(s[i]=`${a[1]}=${r[a[1]]}`,n.delete(a[1]))}if(n.size>0){s.length>0&&s[s.length-1]!==""&&s.push("");for(let i of n)s.push(`${i}=${r[i]}`)}let o=s.join(`
1044
1093
  `).replace(/\n*$/,`
1045
- `);vh(t,o)}function Oh(l){return new Promise((e,t)=>{let s=Eh(async(r,n)=>{let o=new URL(r.url??"/","http://localhost:3000");if(o.pathname!=="/callback"){n.writeHead(404),n.end("Not found");return}let i=o.searchParams.get("code"),a=o.searchParams.get("error");if(a){n.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),n.end(`<h1>Fehler: ${a}</h1><p>${o.searchParams.get("error_description")??""}</p>`),s.close(),t(new Error(`OAuth-Fehler: ${a}`));return}if(!i){n.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),n.end("<h1>Fehler: Kein Auth-Code erhalten</h1>");return}try{let c=await Lh(i,l);n.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),n.end(xh),s.close(),e(c)}catch(c){n.writeHead(500,{"Content-Type":"text/html; charset=utf-8"}),n.end(`<h1>Fehler beim Token-Austausch</h1><p>${c.message}</p>`),s.close(),t(c)}});s.listen(3e3,()=>{console.log(" Callback-Server gestartet auf http://localhost:3000")}),s.on("error",r=>{t(new Error(`Server konnte nicht gestartet werden: ${r.message}`))})})}async function Ph(l){l||(console.error("Usage: alfred auth <provider>"),console.error(" Unterst\xFCtzte Provider: microsoft"),process.exit(1)),l!=="microsoft"&&(console.error(`Unbekannter Provider: ${l}`),console.error(" Unterst\xFCtzte Provider: microsoft"),process.exit(1)),console.log(""),console.log(" Microsoft 365 OAuth \u2014 Automatischer Token-Flow"),console.log(" ================================================"),console.log("");let e=await Ch(),t=Dh(e);console.log(""),console.log(" \xD6ffne diese URL im Browser:"),console.log(` ${t}`),console.log(""),Nh(t);try{let s=await Oh(e);console.log(""),console.log(" Refresh Token erhalten! Schreibe in .env ..."),Mh(e,s),console.log(" .env aktualisiert (Email, Calendar, Contacts, To Do)."),console.log(""),console.log(" Fertig! Du kannst Alfred jetzt mit Microsoft 365 nutzen."),console.log("")}catch(s){console.error(`
1046
- Fehler: ${s.message}`),process.exit(1)}process.exit(0)}var jd,Bd,xh,qd=_(()=>{"use strict";Ve();jd="http://localhost:3000/callback",Bd=["offline_access","Mail.Read","Mail.ReadWrite","Mail.Send","Contacts.Read","Contacts.ReadWrite","Calendars.Read","Calendars.ReadWrite","Tasks.ReadWrite"].join(" "),xh=`<!DOCTYPE html>
1094
+ `);Rh(t,o)}function Bh(l){return new Promise((e,t)=>{let s=Ah(async(r,n)=>{let o=new URL(r.url??"/","http://localhost:3000");if(o.pathname!=="/callback"){n.writeHead(404),n.end("Not found");return}let i=o.searchParams.get("code"),a=o.searchParams.get("error");if(a){n.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),n.end(`<h1>Fehler: ${a}</h1><p>${o.searchParams.get("error_description")??""}</p>`),s.close(),t(new Error(`OAuth-Fehler: ${a}`));return}if(!i){n.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),n.end("<h1>Fehler: Kein Auth-Code erhalten</h1>");return}try{let c=await Uh(i,l);n.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),n.end(Nh),s.close(),e(c)}catch(c){n.writeHead(500,{"Content-Type":"text/html; charset=utf-8"}),n.end(`<h1>Fehler beim Token-Austausch</h1><p>${c.message}</p>`),s.close(),t(c)}});s.listen(3e3,()=>{console.log(" Callback-Server gestartet auf http://localhost:3000")}),s.on("error",r=>{t(new Error(`Server konnte nicht gestartet werden: ${r.message}`))})})}async function Hh(l){l||(console.error("Usage: alfred auth <provider>"),console.error(" Unterst\xFCtzte Provider: microsoft"),process.exit(1)),l!=="microsoft"&&(console.error(`Unbekannter Provider: ${l}`),console.error(" Unterst\xFCtzte Provider: microsoft"),process.exit(1)),console.log(""),console.log(" Microsoft 365 OAuth \u2014 Automatischer Token-Flow"),console.log(" ================================================"),console.log("");let e=await Oh(),t=Ph(e);console.log(""),console.log(" \xD6ffne diese URL im Browser:"),console.log(` ${t}`),console.log(""),Fh(t);try{let s=await Bh(e);console.log(""),console.log(" Refresh Token erhalten! Schreibe in .env ..."),jh(e,s),console.log(" .env aktualisiert (Email, Calendar, Contacts, To Do)."),console.log(""),console.log(" Fertig! Du kannst Alfred jetzt mit Microsoft 365 nutzen."),console.log("")}catch(s){console.error(`
1095
+ Fehler: ${s.message}`),process.exit(1)}process.exit(0)}var qd,Wd,Nh,Vd=_(()=>{"use strict";Ke();qd="http://localhost:3000/callback",Wd=["offline_access","Mail.Read","Mail.ReadWrite","Mail.Send","Contacts.Read","Contacts.ReadWrite","Calendars.Read","Calendars.ReadWrite","Tasks.ReadWrite"].join(" "),Nh=`<!DOCTYPE html>
1047
1096
  <html><head><meta charset="utf-8"><title>Alfred \u2014 Auth erfolgreich</title>
1048
1097
  <style>body{font-family:system-ui,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0d1117;color:#e6edf3}
1049
1098
  .card{text-align:center;padding:2rem;border:1px solid #30363d;border-radius:12px;background:#161b22}
1050
1099
  h1{color:#3fb950;margin-bottom:.5rem}p{color:#8b949e}</style></head>
1051
- <body><div class="card"><h1>Authentifizierung erfolgreich!</h1><p>Du kannst dieses Fenster schliessen und zum Terminal zur\xFCckkehren.</p></div></body></html>`;m(ia,"askQuestion");m(Rh,"resolveCredentials");m(Ch,"ensureCredentials");m(Dh,"buildAuthUrl");m(Lh,"exchangeCode");m(Nh,"openBrowser");m(Mh,"updateEnvFile");m(Oh,"waitForCallback");m(Ph,"authCommand")});var zd={};ue(zd,{logsCommand:()=>jh});import Uh from"node:fs";import Fh from"node:path";async function jh(l){let e=new ce,t;try{t=e.loadConfig()}catch(n){console.error("Failed to load configuration:",n.message),process.exit(1)}let s=Fh.resolve(t.storage.path);if(!Uh.existsSync(s)){console.log(`Database not found at: ${s}`),console.log("No audit log entries. Alfred has not been run yet, or the database path is incorrect.");return}let r;try{r=new yt(s);let n=new wt(r.getDb()),o=n.count({}),i=n.query({limit:l});if(console.log("Alfred \u2014 Audit Log"),console.log("==================="),console.log(`Total entries: ${o}`),console.log(`Showing last ${Math.min(l,o)} entries:`),console.log(""),i.length===0){console.log("No audit log entries found.");return}for(let a of i){let c=a.timestamp.toISOString(),d=a.effect==="allow"?"ALLOW":"DENY ";console.log(` ${c} [${d}] ${a.action}`),console.log(` user: ${a.userId} | platform: ${a.platform} | risk: ${a.riskLevel}`),a.ruleId&&console.log(` rule: ${a.ruleId}`),a.chatId&&console.log(` chat: ${a.chatId}`),a.context&&console.log(` context: ${JSON.stringify(a.context)}`),console.log("")}}catch(n){console.error("Failed to read audit log:",n.message),process.exit(1)}finally{r&&r.close()}}var Wd=_(()=>{"use strict";Ve();Mo();m(jh,"logsCommand")});import{readFileSync as Bh}from"node:fs";import{fileURLToPath as Hh}from"node:url";import{dirname as qh,join as zh}from"node:path";function Wh(){try{let l=qh(Hh(import.meta.url));for(let e of["../package.json","../../package.json"])try{let t=JSON.parse(Bh(zh(l,e),"utf-8"));if(t.version)return t.version}catch{}}catch{}return"0.0.0"}m(Wh,"getVersion");var Gd=Wh(),aa=`
1052
- Alfred CLI v${Gd}
1100
+ <body><div class="card"><h1>Authentifizierung erfolgreich!</h1><p>Du kannst dieses Fenster schliessen und zum Terminal zur\xFCckkehren.</p></div></body></html>`;m(la,"askQuestion");m(Mh,"resolveCredentials");m(Oh,"ensureCredentials");m(Ph,"buildAuthUrl");m(Uh,"exchangeCode");m(Fh,"openBrowser");m(jh,"updateEnvFile");m(Bh,"waitForCallback");m(Hh,"authCommand")});var Kd={};le(Kd,{logsCommand:()=>Wh});import zh from"node:fs";import qh from"node:path";async function Wh(l){let e=new ce,t;try{t=e.loadConfig()}catch(n){console.error("Failed to load configuration:",n.message),process.exit(1)}let s=qh.resolve(t.storage.path);if(!zh.existsSync(s)){console.log(`Database not found at: ${s}`),console.log("No audit log entries. Alfred has not been run yet, or the database path is incorrect.");return}let r;try{r=new wt(s);let n=new Tt(r.getDb()),o=n.count({}),i=n.query({limit:l});if(console.log("Alfred \u2014 Audit Log"),console.log("==================="),console.log(`Total entries: ${o}`),console.log(`Showing last ${Math.min(l,o)} entries:`),console.log(""),i.length===0){console.log("No audit log entries found.");return}for(let a of i){let c=a.timestamp.toISOString(),d=a.effect==="allow"?"ALLOW":"DENY ";console.log(` ${c} [${d}] ${a.action}`),console.log(` user: ${a.userId} | platform: ${a.platform} | risk: ${a.riskLevel}`),a.ruleId&&console.log(` rule: ${a.ruleId}`),a.chatId&&console.log(` chat: ${a.chatId}`),a.context&&console.log(` context: ${JSON.stringify(a.context)}`),console.log("")}}catch(n){console.error("Failed to read audit log:",n.message),process.exit(1)}finally{r&&r.close()}}var Xd=_(()=>{"use strict";Ke();Po();m(Wh,"logsCommand")});import{readFileSync as Gh}from"node:fs";import{fileURLToPath as Vh}from"node:url";import{dirname as Kh,join as Xh}from"node:path";function Yh(){try{let l=Kh(Vh(import.meta.url));for(let e of["../package.json","../../package.json"])try{let t=JSON.parse(Gh(Xh(l,e),"utf-8"));if(t.version)return t.version}catch{}}catch{}return"0.0.0"}m(Yh,"getVersion");var Yd=Yh(),da=`
1101
+ Alfred CLI v${Yd}
1053
1102
  Personal AI Assistant
1054
1103
 
1055
1104
  Usage:
@@ -1068,4 +1117,4 @@ Commands:
1068
1117
  Options:
1069
1118
  --help, -h Show this help message
1070
1119
  --version, -v Show version number
1071
- `.trim();function Gh(l){let e=l.slice(2),t=e.length>0&&!e[0].startsWith("-")?e[0]:"",s=t?e.slice(1):e,r={},n=[],o=0;for(;o<s.length;){let i=s[o];if(i.startsWith("--")){let a=i.slice(2);o+1<s.length&&!s[o+1].startsWith("-")?(r[a]=s[o+1],o+=2):(r[a]=!0,o+=1)}else if(i.startsWith("-")&&i.length===2){let a=i.slice(1);o+1<s.length&&!s[o+1].startsWith("-")?(r[a]=s[o+1],o+=2):(r[a]=!0,o+=1)}else n.push(i),o+=1}return{command:t,flags:r,positional:n}}m(Gh,"parseArgs");async function Vh(){let l=Gh(process.argv);switch((l.flags.help||l.flags.h)&&(console.log(aa),process.exit(0)),(l.flags.version||l.flags.v)&&(console.log(`alfred v${Gd}`),process.exit(0)),l.command){case"start":{let{startCommand:e}=await Promise.resolve().then(()=>(kd(),$d));await e();break}case"chat":{let{chatCommand:e}=await Promise.resolve().then(()=>(Ad(),Sd));await e({model:typeof l.flags.model=="string"?l.flags.model:void 0,tier:typeof l.flags.tier=="string"?l.flags.tier:void 0});break}case"setup":{let{setupCommand:e}=await Promise.resolve().then(()=>(Cd(),Rd));await e();break}case"config":{let{configCommand:e}=await Promise.resolve().then(()=>(Nd(),Ld));await e();break}case"rules":{let{rulesCommand:e}=await Promise.resolve().then(()=>(Pd(),Od));await e();break}case"status":{let{statusCommand:e}=await Promise.resolve().then(()=>(Fd(),Ud));await e();break}case"auth":{let e=l.positional[0]??"",{authCommand:t}=await Promise.resolve().then(()=>(qd(),Hd));await t(e);break}case"logs":{let e=l.flags.tail,t=20;if(typeof e=="string"){let r=parseInt(e,10);(Number.isNaN(r)||r<=0)&&(console.error("Error: --tail must be a positive integer"),process.exit(1)),t=r}let{logsCommand:s}=await Promise.resolve().then(()=>(Wd(),zd));await s(t);break}case"help":console.log(aa);break;case"":console.log(aa),process.exit(0);break;default:console.error(`Unknown command: ${l.command}`),console.error(""),console.error('Run "alfred --help" for usage information.'),process.exit(1)}}m(Vh,"main");Vh().catch(l=>{console.error("Fatal error:",l),process.exit(1)});
1120
+ `.trim();function Jh(l){let e=l.slice(2),t=e.length>0&&!e[0].startsWith("-")?e[0]:"",s=t?e.slice(1):e,r={},n=[],o=0;for(;o<s.length;){let i=s[o];if(i.startsWith("--")){let a=i.slice(2);o+1<s.length&&!s[o+1].startsWith("-")?(r[a]=s[o+1],o+=2):(r[a]=!0,o+=1)}else if(i.startsWith("-")&&i.length===2){let a=i.slice(1);o+1<s.length&&!s[o+1].startsWith("-")?(r[a]=s[o+1],o+=2):(r[a]=!0,o+=1)}else n.push(i),o+=1}return{command:t,flags:r,positional:n}}m(Jh,"parseArgs");async function Zh(){let l=Jh(process.argv);switch((l.flags.help||l.flags.h)&&(console.log(da),process.exit(0)),(l.flags.version||l.flags.v)&&(console.log(`alfred v${Yd}`),process.exit(0)),l.command){case"start":{let{startCommand:e}=await Promise.resolve().then(()=>(Id(),Ad));await e();break}case"chat":{let{chatCommand:e}=await Promise.resolve().then(()=>(Cd(),Rd));await e({model:typeof l.flags.model=="string"?l.flags.model:void 0,tier:typeof l.flags.tier=="string"?l.flags.tier:void 0});break}case"setup":{let{setupCommand:e}=await Promise.resolve().then(()=>(Md(),Nd));await e();break}case"config":{let{configCommand:e}=await Promise.resolve().then(()=>(Ud(),Pd));await e();break}case"rules":{let{rulesCommand:e}=await Promise.resolve().then(()=>(Bd(),jd));await e();break}case"status":{let{statusCommand:e}=await Promise.resolve().then(()=>(zd(),Hd));await e();break}case"auth":{let e=l.positional[0]??"",{authCommand:t}=await Promise.resolve().then(()=>(Vd(),Gd));await t(e);break}case"logs":{let e=l.flags.tail,t=20;if(typeof e=="string"){let r=parseInt(e,10);(Number.isNaN(r)||r<=0)&&(console.error("Error: --tail must be a positive integer"),process.exit(1)),t=r}let{logsCommand:s}=await Promise.resolve().then(()=>(Xd(),Kd));await s(t);break}case"help":console.log(da);break;case"":console.log(da),process.exit(0);break;default:console.error(`Unknown command: ${l.command}`),console.error(""),console.error('Run "alfred --help" for usage information.'),process.exit(1)}}m(Zh,"main");Zh().catch(l=>{console.error("Fatal error:",l),process.exit(1)});