@cccarv82/freya 1.0.18 → 1.0.19

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/cli/web.js +55 -3
  2. package/package.json +1 -1
package/cli/web.js CHANGED
@@ -182,6 +182,52 @@ function postTeamsWebhook(url, text) {
182
182
  return postJson(url, { text });
183
183
  }
184
184
 
185
+ function escapeJsonControlChars(jsonText) {
186
+ // Replace unescaped control chars inside JSON string literals with safe escapes.
187
+ // Handles Copilot outputs where newlines/tabs leak into string values.
188
+ const out = [];
189
+ let inString = false;
190
+ let esc = false;
191
+
192
+ for (let i = 0; i < jsonText.length; i++) {
193
+ const ch = jsonText[i];
194
+ const code = ch.charCodeAt(0);
195
+
196
+ if (esc) {
197
+ out.push(ch);
198
+ esc = false;
199
+ continue;
200
+ }
201
+
202
+ if (ch === '\\') {
203
+ out.push(ch);
204
+ esc = true;
205
+ continue;
206
+ }
207
+
208
+ if (ch === '"') {
209
+ out.push(ch);
210
+ inString = !inString;
211
+ continue;
212
+ }
213
+
214
+ if (inString) {
215
+ if (code === 10) { out.push('\\n'); continue; }
216
+ if (code === 13) { out.push('\\r'); continue; }
217
+ if (code === 9) { out.push('\\t'); continue; }
218
+ if (code >= 0 && code < 32) {
219
+ const hex = code.toString(16).padStart(2, '0');
220
+ out.push('\\u00' + hex);
221
+ continue;
222
+ }
223
+ }
224
+
225
+ out.push(ch);
226
+ }
227
+
228
+ return out.join('');
229
+ }
230
+
185
231
  async function publishRobust(webhookUrl, text, opts = {}) {
186
232
  const u = new URL(webhookUrl);
187
233
  const isDiscord = u.hostname.includes('discord.com') || u.hostname.includes('discordapp.com');
@@ -353,9 +399,7 @@ function buildHtml(safeDefault) {
353
399
  <div class="sideGroup">
354
400
  <div class="sideTitle">Workspace</div>
355
401
  <button class="btn sideBtn" onclick="pickDir()">Select workspace…</button>
356
- <button class="btn primary sideBtn" onclick="doInit()">Init workspace</button>
357
402
  <button class="btn sideBtn" onclick="doUpdate()">Update (preserve data/logs)</button>
358
- <button class="btn sideBtn" onclick="doHealth()">Health</button>
359
403
  <button class="btn sideBtn" onclick="doMigrate()">Migrate</button>
360
404
  <div style="height:10px"></div>
361
405
  <div class="help">Dica: se você já tem uma workspace antiga, use Update. Por padrão, data/logs não são sobrescritos.</div>
@@ -836,7 +880,15 @@ async function cmdWeb({ port, dir, open, dev }) {
836
880
  try {
837
881
  plan = JSON.parse(jsonText);
838
882
  } catch (e) {
839
- return safeJson(res, 400, { error: 'Plan is not valid JSON', details: e.message || String(e) });
883
+ try {
884
+ plan = JSON.parse(escapeJsonControlChars(jsonText));
885
+ } catch (e2) {
886
+ return safeJson(res, 400, {
887
+ error: 'Plan is not valid JSON',
888
+ details: (e2 && e2.message) ? e2.message : (e && e.message ? e.message : String(e)),
889
+ hint: 'O planner gerou caracteres de controle dentro de strings (ex.: quebra de linha). Reexecute o planner ou escape quebras de linha como \\n.'
890
+ });
891
+ }
840
892
  }
841
893
 
842
894
  const actions = Array.isArray(plan.actions) ? plan.actions : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",