neoagent 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -1047,6 +1047,67 @@ ruby <file>.rb # Ruby
1047
1047
  },
1048
1048
 
1049
1049
  // ── MAKER ────────────────────────────────────────────────────────────────────
1050
+ {
1051
+ id: 'psa-car-controller',
1052
+ name: 'PSA Car Controller',
1053
+ description: 'Control and query a local psa_car_controller instance for vehicle status, charging, climate, locks, lights and trips.',
1054
+ category: 'maker',
1055
+ icon: '🚗',
1056
+ content: `---
1057
+ name: psa-car-controller
1058
+ description: Control and query a local psa_car_controller instance for vehicle status, charging, climate, locks, lights and trips
1059
+ trigger: When the user asks about a Peugeot, Citroen, Opel, Vauxhall or DS vehicle connected through psa_car_controller, including status, charging, preconditioning, locks, horn, lights, trips, charging sessions, battery SOH or settings
1060
+ category: maker
1061
+ icon: 🚗
1062
+ enabled: true
1063
+ ---
1064
+
1065
+ # PSA Car Controller
1066
+
1067
+ Use the local [flobz/psa_car_controller](https://github.com/flobz/psa_car_controller) HTTP API. Default base URL: \`http://localhost:5005\`. Only use another host if the user explicitly gives one.
1068
+
1069
+ ## Request rules
1070
+
1071
+ - Use \`http_request\` when available; otherwise use \`curl\`.
1072
+ - Default to JSON output and show the exact endpoint you called.
1073
+ - If the user does not provide a VIN, call \`GET /settings\` first and infer it from the configured vehicle when possible. If there are multiple vehicles or no VIN is present, ask for the VIN.
1074
+ - For read-only status requests, prefer cache when freshness is not important: \`GET /get_vehicleinfo/<VIN>?from_cache=1\`.
1075
+ - For live status, use \`GET /get_vehicleinfo/<VIN>\`. If the user wants a refresh from the car first, call \`GET /wakeup/<VIN>\`, wait briefly, then fetch status.
1076
+ - For state-changing actions that could be safety-sensitive (unlock, horn, lights, climate, charge stop/start), make sure the user intent is explicit before calling them.
1077
+ - If changing settings via \`/settings/<section>\`, mention that the app needs a restart afterward.
1078
+
1079
+ ## Supported endpoints
1080
+
1081
+ - Vehicle state: \`GET /get_vehicleinfo/<VIN>\`
1082
+ - Cached vehicle state: \`GET /get_vehicleinfo/<VIN>?from_cache=1\`
1083
+ - Wake up / refresh state: \`GET /wakeup/<VIN>\`
1084
+ - Start or stop preconditioning: \`GET /preconditioning/<VIN>/1\` or \`/0\`
1085
+ - Start or stop charge immediately: \`GET /charge_now/<VIN>/1\` or \`/0\`
1086
+ - Set charge stop hour: \`GET /charge_control?vin=<VIN>&hour=<H>&minute=<M>\`
1087
+ - Set charge threshold percentage: \`GET /charge_control?vin=<VIN>&percentage=<PERCENT>\`
1088
+ - Set scheduled charge hour: \`GET /charge_hour?vin=<VIN>&hour=<H>&minute=<M>\`
1089
+ - Honk horn: \`GET /horn/<VIN>/<COUNT>\`
1090
+ - Flash lights: \`GET /lights/<VIN>/<DURATION>\`
1091
+ - Lock or unlock doors: \`GET /lock_door/<VIN>/1\` or \`/0\`
1092
+ - Battery SOH: \`GET /battery/soh/<VIN>\`
1093
+ - Charging sessions: \`GET /vehicles/chargings\`
1094
+ - Trips: \`GET /vehicles/trips\`
1095
+ - Dashboard / root UI: \`GET /\`
1096
+ - Read settings: \`GET /settings\`
1097
+ - Update settings: \`GET /settings/<section>?key=value\`
1098
+
1099
+ ## Response format
1100
+
1101
+ Reply with:
1102
+ - action performed
1103
+ - endpoint used
1104
+ - status/result
1105
+ - key fields from the JSON response
1106
+ - any follow-up note, such as restart needed for settings changes
1107
+
1108
+ If the API returns an error, include the response body and suggest the next useful check, usually \`/settings\` or a VIN validation.`
1109
+ },
1110
+
1050
1111
  {
1051
1112
  id: 'bambu-studio-cli',
1052
1113
  name: 'BambuStudio CLI',
@@ -124,28 +124,35 @@ async function startServices(app, io) {
124
124
  if (msg.platform !== 'discord' && msg.platform !== 'telegram') {
125
125
  const whitelistRow = db.prepare('SELECT value FROM user_settings WHERE user_id = ? AND key = ?')
126
126
  .get(userId, `platform_whitelist_${msg.platform}`);
127
+ const normalize = msg.platform === 'whatsapp'
128
+ ? normalizeWhatsAppId
129
+ : (id) => String(id || '').replace(/[^0-9+]/g, '');
130
+
131
+ let whitelist = [];
127
132
  if (whitelistRow) {
128
133
  try {
129
- const whitelist = JSON.parse(whitelistRow.value);
130
- if (Array.isArray(whitelist) && whitelist.length > 0) {
131
- const normalize = msg.platform === 'whatsapp'
132
- ? normalizeWhatsAppId
133
- : (id) => String(id || '').replace(/[^0-9+]/g, '');
134
- const senderNorm = normalize(msg.sender || msg.chatId);
135
- const allowed = whitelist.some((n) => normalize(n) === senderNorm);
136
- if (!allowed) {
137
- console.log(`[Messaging] Blocked ${msg.platform} message from ${msg.sender} (not in whitelist)`);
138
- io.to(`user:${userId}`).emit('messaging:blocked_sender', {
139
- platform: msg.platform,
140
- sender: msg.sender,
141
- chatId: msg.chatId,
142
- senderName: msg.senderName || null
143
- });
144
- return;
145
- }
146
- }
134
+ const parsed = JSON.parse(whitelistRow.value);
135
+ if (Array.isArray(parsed)) whitelist = parsed;
147
136
  } catch { }
148
137
  }
138
+
139
+ const enforceEmptyWhitelist = msg.platform === 'whatsapp';
140
+ const shouldCheckWhitelist = whitelist.length > 0 || enforceEmptyWhitelist;
141
+
142
+ if (shouldCheckWhitelist) {
143
+ const senderNorm = normalize(msg.sender || msg.chatId);
144
+ const allowed = whitelist.some((n) => normalize(n) === senderNorm);
145
+ if (!allowed) {
146
+ console.log(`[Messaging] Blocked ${msg.platform} message from ${msg.sender} (not in whitelist)`);
147
+ io.to(`user:${userId}`).emit('messaging:blocked_sender', {
148
+ platform: msg.platform,
149
+ sender: msg.sender,
150
+ chatId: msg.chatId,
151
+ senderName: msg.senderName || null
152
+ });
153
+ return;
154
+ }
155
+ }
149
156
  }
150
157
 
151
158
  const upsertSetting = db.prepare('INSERT OR REPLACE INTO user_settings (user_id, key, value) VALUES (?, ?, ?)');