@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 +23 -4
- package/cli/web.js +93 -0
- package/package.json +1 -1
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 });
|