@madh-io/alfred-ai 0.9.76 → 0.9.77

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 +2 -2
  2. package/package.json +1 -1
package/bundle/index.js CHANGED
@@ -628,8 +628,8 @@ ${o}`}}clearCompleted(e,t){let s=e.list,r=this.todoRepo.clearCompleted(Z(t),s);r
628
628
  `)}}async getCharging(e){let t=await this.resolveVin(e),s=await this.resolveContainerId(),o=(await this.apiGet(`/customers/vehicles/${t}/telematicData?containerId=${s}`)).telematicData??{},n=ue(o,"vehicle.drivetrain.electricEngine.charging.status"),i=ue(o,"vehicle.drivetrain.batteryManagement.header"),a=ue(o,"vehicle.drivetrain.electricEngine.charging.level"),l=ue(o,"vehicle.drivetrain.electricEngine.charging.timeRemaining"),d=ue(o,"vehicle.powertrain.electric.battery.charging.power"),m=ue(o,"vehicle.drivetrain.electricEngine.charging.hvStatus"),p=ue(o,"vehicle.powertrain.electric.battery.stateOfCharge.target"),_=ue(o,"vehicle.drivetrain.electricEngine.charging.acVoltage"),y=ue(o,"vehicle.drivetrain.electricEngine.charging.acAmpere"),g=ue(o,"vehicle.powertrain.tractionBattery.charging.port.anyPosition.isPlugged"),b=ue(o,"vehicle.powertrain.tractionBattery.charging.port.anyPosition.flap.isOpen"),S=ue(o,"vehicle.body.chargingPort.lockedStatus"),A=["## BMW Ladestatus","",`**Status:** ${n}`,`**Ladestand:** ${i} %`,`**Ladelevel:** ${a}`,`**Ladeleistung:** ${d} kW`,`**Restzeit:** ${l} min`,`**Ziel-SoC:** ${p} %`,`**HV-Batterie:** ${m}`,`**AC Spannung:** ${_} V`,`**AC Strom:** ${y} A`,`**Stecker eingesteckt:** ${g}`,`**Ladeklappe offen:** ${b}`,`**Ladeport-Schloss:** ${S}`];return{success:!0,data:o,display:A.join(`
629
629
  `)}}async getChargingSessions(e,t,s){let r=await this.resolveVin(e),o=new Date,n=s??o.toISOString(),i=t??new Date(o.getTime()-30*24*60*6e4).toISOString(),a=await this.apiGet(`/customers/vehicles/${r}/chargingHistory?from=${encodeURIComponent(i)}&to=${encodeURIComponent(n)}`),l=a.data??a.chargingSessions??[],d=[`## BMW Lade-Sessions (${i.slice(0,10)} \u2013 ${n.slice(0,10)})`,"","| Datum | Dauer | Energie | Start-SoC | End-SoC |","|-------|-------|---------|-----------|---------|"];for(let m of l.slice(0,20)){let p=m.startTime,_=p?new Date(p).toLocaleDateString("de-AT"):"-",y=m.totalChargingDurationSec,g=y!=null?Math.round(y/60):"-",b=m.energyConsumedFromPowerGridKwh??"-",S=m.displayedStartSoc??"-",A=m.displayedSoc??"-";d.push(`| ${_} | ${g} min | ${b} kWh | ${S}% | ${A}% |`)}return l.length===0&&d.push("| - | Keine Sessions gefunden | - | - | - |"),{success:!0,data:a,display:d.join(`
630
630
  `)}}}});var Uu,ro,Pc=T(()=>{"use strict";F();Uu="https://routes.googleapis.com/directions/v2:computeRoutes",ro=class extends x{static{u(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,o=e.destination;if(!r)return{success:!1,error:'Missing required field "origin"'};if(!o)return{success:!1,error:'Missing required field "destination"'};try{switch(s){case"route":return await this.computeRoute(r,o,e.departure_time,e.travel_mode);case"departure_time":return await this.computeDepartureTime(r,o,e.arrival_time,e.travel_mode);default:return{success:!1,error:`Unknown action "${s}"`}}}catch(n){return{success:!1,error:`Google Routes API error: ${n instanceof Error?n.message:String(n)}`}}}async computeRoute(e,t,s,r){let o=this.buildRequestBody(e,t,r,s),i=(await this.callRoutesApi(o)).routes?.[0];if(!i)return{success:!1,error:"Keine Route gefunden."};let a=(i.distanceMeters/1e3).toFixed(1),l=this.parseDuration(i.duration),d=this.parseDuration(i.staticDuration),m=l-d,p=["## Route","",`**${e}** \u2192 **${t}**`,"",`**Distanz:** ${a} km`,`**Fahrzeit (aktuell):** ${this.formatMinutes(l)}`,`**Fahrzeit (ohne Verkehr):** ${this.formatMinutes(d)}`];if(m>1&&p.push(`**Verkehrsverz\xF6gerung:** +${this.formatMinutes(m)}`),s){let _=new Date(new Date(s).getTime()+l*6e4);p.push(`**Gesch\xE4tzte Ankunft:** ${_.toLocaleString("de-AT")}`)}return{success:!0,data:{distanceKm:parseFloat(a),durationMinutes:l,staticDurationMinutes:d},display:p.join(`
631
- `)}}async computeDepartureTime(e,t,s,r){if(!s)return{success:!1,error:'Missing required field "arrival_time" for departure_time action'};let o=this.buildRequestBody(e,t,r,new Date().toISOString()),i=(await this.callRoutesApi(o)).routes?.[0];if(!i)return{success:!1,error:"Keine Route gefunden."};let a=this.parseDuration(i.duration),l=Math.max(5,Math.round(a*.15)),d=new Date(s),m=new Date(d.getTime()-(a+l)*6e4),p=["## Abfahrtszeit-Empfehlung","",`**Route:** ${e} \u2192 ${t}`,`**Gew\xFCnschte Ankunft:** ${d.toLocaleString("de-AT")}`,`**Gesch\xE4tzte Fahrzeit:** ${this.formatMinutes(a)} (inkl. Verkehr)`,`**Puffer:** ${l} min`,"",`**Empfohlene Abfahrt:** ${m.toLocaleString("de-AT")}`];return{success:!0,data:{departureTime:m.toISOString(),durationMinutes:a,bufferMinutes:l},display:p.join(`
632
- `)}}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?"+":"-",o=Math.abs(s),n=String(Math.floor(o/60)).padStart(2,"0"),i=String(o%60).padStart(2,"0"),a=u(l=>String(l).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}${n}:${i}`}buildRequestBody(e,t,s,r,o){let n={origin:this.buildWaypoint(e),destination:this.buildWaypoint(t),travelMode:s??"DRIVE",routingPreference:"TRAFFIC_AWARE"};return r&&(n.departureTime=this.normalizeTimestamp(r)),o&&(n.arrivalTime=this.normalizeTimestamp(o)),n}async callRoutesApi(e){let t=await fetch(Uu,{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`}}});import jn from"node:fs";import Uc from"node:path";function Fc(c){return c.length<=4?"****":"*".repeat(c.length-4)+c.slice(-4)}function Fu(){let c=process.cwd();for(let e=0;e<10;e++){let t=Uc.join(c,".env");if(jn.existsSync(t))return t;let s=Uc.dirname(c);if(s===c)break;c=s}return null}var Zt,Qt,jc=T(()=>{"use strict";F();Zt={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)"}]}},Qt=class extends x{static{u(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(Zt)){let o=s.fields.filter(i=>i.required).every(i=>!!process.env[i.env])?"configured":"not configured",n=`ALFRED_${t.toUpperCase()}_*`;e.push(`| ${s.label} | ${o} | \`${n}\` |`)}return{success:!0,data:Object.keys(Zt),display:e.join(`
631
+ `)}}async computeDepartureTime(e,t,s,r){if(!s)return{success:!1,error:'Missing required field "arrival_time" for departure_time action'};let o=this.buildRequestBody(e,t,r),i=(await this.callRoutesApi(o)).routes?.[0];if(!i)return{success:!1,error:"Keine Route gefunden."};let a=this.parseDuration(i.duration),l=Math.max(5,Math.round(a*.15)),d=new Date(s),m=new Date(d.getTime()-(a+l)*6e4),p=["## Abfahrtszeit-Empfehlung","",`**Route:** ${e} \u2192 ${t}`,`**Gew\xFCnschte Ankunft:** ${d.toLocaleString("de-AT")}`,`**Gesch\xE4tzte Fahrzeit:** ${this.formatMinutes(a)} (inkl. Verkehr)`,`**Puffer:** ${l} min`,"",`**Empfohlene Abfahrt:** ${m.toLocaleString("de-AT")}`];return{success:!0,data:{departureTime:m.toISOString(),durationMinutes:a,bufferMinutes:l},display:p.join(`
632
+ `)}}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?"+":"-",o=Math.abs(s),n=String(Math.floor(o/60)).padStart(2,"0"),i=String(o%60).padStart(2,"0"),a=u(l=>String(l).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}${n}:${i}`}buildRequestBody(e,t,s,r,o){let n={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&&(n.departureTime=i)}if(o){let i=this.normalizeTimestamp(o);new Date(i).getTime()>Date.now()+6e4&&(n.arrivalTime=i)}return n}async callRoutesApi(e){let t=await fetch(Uu,{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`}}});import jn from"node:fs";import Uc from"node:path";function Fc(c){return c.length<=4?"****":"*".repeat(c.length-4)+c.slice(-4)}function Fu(){let c=process.cwd();for(let e=0;e<10;e++){let t=Uc.join(c,".env");if(jn.existsSync(t))return t;let s=Uc.dirname(c);if(s===c)break;c=s}return null}var Zt,Qt,jc=T(()=>{"use strict";F();Zt={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)"}]}},Qt=class extends x{static{u(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(Zt)){let o=s.fields.filter(i=>i.required).every(i=>!!process.env[i.env])?"configured":"not configured",n=`ALFRED_${t.toUpperCase()}_*`;e.push(`| ${s.label} | ${o} | \`${n}\` |`)}return{success:!0,data:Object.keys(Zt),display:e.join(`
633
633
  `)}}showService(e){let t=Zt[e];if(!t)return{success:!1,error:`Unknown service "${e}". Available: ${Object.keys(Zt).join(", ")}`};let s=[`**${t.label}** configuration:
634
634
  `],r={};for(let o of t.fields){let n=process.env[o.env];r[o.env]=n;let i=n?o.secret?Fc(n):n:"_not set_",a=o.required?" (required)":"";s.push(`- \`${o.env}\`: ${i}${a}`)}return{success:!0,data:r,display:s.join(`
635
635
  `)}}async setService(e,t){let s=Zt[e];if(!s)return{success:!1,error:`Unknown service "${e}". Available: ${Object.keys(Zt).join(", ")}`};if(!t||Object.keys(t).length===0)return{success:!1,error:`No values provided. Pass an object with ENV variable names as keys.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madh-io/alfred-ai",
3
- "version": "0.9.76",
3
+ "version": "0.9.77",
4
4
  "description": "Alfred — Personal AI Assistant across Telegram, Discord, WhatsApp, Matrix & Signal",
5
5
  "type": "module",
6
6
  "bin": {