@cccarv82/freya 1.0.23 → 1.0.25

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/cli/web-ui.js CHANGED
@@ -107,7 +107,14 @@
107
107
  function setOut(text) {
108
108
  state.lastText = text || '';
109
109
  const el = $('reportPreview');
110
- if (el) el.innerHTML = renderMarkdown(state.lastText);
110
+ if (!el) return;
111
+ try {
112
+ el.innerHTML = renderMarkdown(state.lastText);
113
+ } catch (e) {
114
+ // Fallback: never hide the error/output if markdown rendering breaks
115
+ try { console.error('renderMarkdown failed:', e); } catch {}
116
+ el.textContent = state.lastText;
117
+ }
111
118
  }
112
119
 
113
120
  function clearOut() {
@@ -218,10 +225,21 @@
218
225
  headers: body ? { 'Content-Type': 'application/json' } : {},
219
226
  body: body ? JSON.stringify(body) : undefined
220
227
  });
221
- const json = await res.json();
228
+
229
+ // Read as text first so we can surface errors even if backend returns HTML/plaintext
230
+ const raw = await res.text();
231
+ let json = null;
232
+ try {
233
+ json = raw ? JSON.parse(raw) : {};
234
+ } catch (e) {
235
+ const snippet = (raw || '').slice(0, 1200);
236
+ throw new Error(`Non-JSON response (${res.status} ${res.statusText}) from ${p}:\n\n${snippet}`);
237
+ }
238
+
222
239
  if (!res.ok) {
223
240
  const detail = json.details ? ('\n' + json.details) : '';
224
- throw new Error((json.error || 'Request failed') + detail);
241
+ const err = (json.error || `Request failed (${res.status})`) + detail;
242
+ throw new Error(err);
225
243
  }
226
244
  return json;
227
245
  }
@@ -341,7 +359,8 @@
341
359
  setPill('ok', 'init ok');
342
360
  } catch (e) {
343
361
  setPill('err', 'init failed');
344
- setOut(String(e && e.message ? e.message : e));
362
+ const msg = e && (e.stack || e.message) ? (e.stack || e.message) : String(e);
363
+ setOut(msg);
345
364
  }
346
365
  }
347
366
 
package/cli/web.js CHANGED
@@ -266,6 +266,65 @@ function redactSecrets(text) {
266
266
  return out;
267
267
  }
268
268
 
269
+ function debugLogPath(workspaceDir) {
270
+ const dir = path.join(workspaceDir, '.debuglogs');
271
+ try { fs.mkdirSync(dir, { recursive: true }); } catch {}
272
+ return path.join(dir, 'debug.jsonl');
273
+ }
274
+
275
+ function safeString(v, max = 2000) {
276
+ const t = String(v == null ? '' : v);
277
+ return t.length > max ? (t.slice(0, max) + '…') : t;
278
+ }
279
+
280
+ function summarizePayload(payload) {
281
+ if (!payload || typeof payload !== 'object') return null;
282
+ const keys = Object.keys(payload);
283
+ const summary = { keys };
284
+
285
+ if (typeof payload.text === 'string') {
286
+ const red = redactSecrets(payload.text);
287
+ summary.textLen = payload.text.length;
288
+ summary.textSha1 = sha1(red);
289
+ }
290
+
291
+ if (typeof payload.plan === 'string') {
292
+ summary.planLen = payload.plan.length;
293
+ summary.planSha1 = sha1(payload.plan);
294
+ }
295
+
296
+ if (typeof payload.webhookUrl === 'string') {
297
+ summary.webhookHost = (() => { try { return new URL(payload.webhookUrl).host; } catch { return null; } })();
298
+ }
299
+
300
+ if (typeof payload.relPath === 'string') summary.relPath = payload.relPath;
301
+ if (typeof payload.script === 'string') summary.script = payload.script;
302
+
303
+ return summary;
304
+ }
305
+
306
+ function rotateDebugLog(filePath, maxBytes = 5 * 1024 * 1024) {
307
+ try {
308
+ if (!fs.existsSync(filePath)) return;
309
+ const st = fs.statSync(filePath);
310
+ if (st.size <= maxBytes) return;
311
+ const dir = path.dirname(filePath);
312
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
313
+ const rotated = path.join(dir, 'debug-' + stamp + '.jsonl');
314
+ fs.renameSync(filePath, rotated);
315
+ } catch {}
316
+ }
317
+
318
+ function writeDebugEvent(workspaceDir, event) {
319
+ try {
320
+ if (!workspaceDir) return;
321
+ const filePath = debugLogPath(workspaceDir);
322
+ rotateDebugLog(filePath);
323
+ const line = JSON.stringify({ ts: new Date().toISOString(), ...event });
324
+ fs.appendFileSync(filePath, line + '\n', 'utf8');
325
+ } catch {}
326
+ }
327
+
269
328
  async function publishRobust(webhookUrl, text, opts = {}) {
270
329
  const u = new URL(webhookUrl);
271
330
  const isDiscord = u.hostname.includes('discord.com') || u.hostname.includes('discordapp.com');
@@ -282,6 +341,25 @@ async function publishRobust(webhookUrl, text, opts = {}) {
282
341
 
283
342
  function safeJson(res, code, obj) {
284
343
  const body = JSON.stringify(obj);
344
+
345
+ try {
346
+ const dbg = res && res.__freyaDebug;
347
+ if (dbg && dbg.workspaceDir) {
348
+ const error = obj && (obj.error || obj.details)
349
+ ? safeString(redactSecrets(JSON.stringify({ error: obj.error, details: obj.details })), 1600)
350
+ : null;
351
+ writeDebugEvent(dbg.workspaceDir, {
352
+ type: 'http_response',
353
+ reqId: dbg.reqId,
354
+ method: dbg.method,
355
+ url: dbg.url,
356
+ status: code,
357
+ bytes: Buffer.byteLength(body),
358
+ error
359
+ });
360
+ }
361
+ } catch {}
362
+
285
363
  res.writeHead(code, {
286
364
  'Content-Type': 'application/json; charset=utf-8',
287
365
  'Cache-Control': 'no-store',
@@ -766,10 +844,13 @@ async function cmdWeb({ port, dir, open, dev }) {
766
844
  const host = '127.0.0.1';
767
845
 
768
846
  const server = http.createServer(async (req, res) => {
847
+ const reqId = Math.random().toString(16).slice(2) + Date.now().toString(16);
848
+ res.__freyaDebug = { reqId, method: req.method, url: req.url || '' };
769
849
  try {
770
850
  if (!req.url) return safeJson(res, 404, { error: 'Not found' });
771
851
 
772
852
  if (req.method === 'GET' && req.url === '/') {
853
+ try { res.__freyaDebug.workspaceDir = normalizeWorkspaceDir(dir || './freya'); } catch {}
773
854
  const body = html(dir || './freya');
774
855
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
775
856
  res.end(body);
@@ -797,6 +878,18 @@ async function cmdWeb({ port, dir, open, dev }) {
797
878
  const requestedDir = payload.dir || dir || './freya';
798
879
  const workspaceDir = normalizeWorkspaceDir(requestedDir);
799
880
 
881
+ // debug logging (always on)
882
+ try {
883
+ res.__freyaDebug.workspaceDir = workspaceDir;
884
+ writeDebugEvent(workspaceDir, {
885
+ type: 'http_request',
886
+ reqId,
887
+ method: req.method,
888
+ url: req.url,
889
+ payload: summarizePayload(payload)
890
+ });
891
+ } catch {}
892
+
800
893
  if (req.url === '/api/pick-dir') {
801
894
  const picked = await pickDirectoryNative();
802
895
  return safeJson(res, 200, { dir: picked });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",