@cccarv82/freya 1.0.17 → 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.
- package/cli/web-ui.js +33 -9
- package/cli/web.js +62 -5
- package/package.json +1 -1
package/cli/web-ui.js
CHANGED
|
@@ -205,7 +205,10 @@
|
|
|
205
205
|
body: body ? JSON.stringify(body) : undefined
|
|
206
206
|
});
|
|
207
207
|
const json = await res.json();
|
|
208
|
-
if (!res.ok)
|
|
208
|
+
if (!res.ok) {
|
|
209
|
+
const detail = json.details ? ('\n' + json.details) : '';
|
|
210
|
+
throw new Error((json.error || 'Request failed') + detail);
|
|
211
|
+
}
|
|
209
212
|
return json;
|
|
210
213
|
}
|
|
211
214
|
|
|
@@ -310,6 +313,8 @@
|
|
|
310
313
|
setOut(r.output);
|
|
311
314
|
setLast(null);
|
|
312
315
|
await refreshReports();
|
|
316
|
+
// Auto health after init
|
|
317
|
+
try { await doHealth(); } catch {}
|
|
313
318
|
setPill('ok', 'init ok');
|
|
314
319
|
} catch (e) {
|
|
315
320
|
setPill('err', 'init failed');
|
|
@@ -327,6 +332,8 @@
|
|
|
327
332
|
setOut(r.output);
|
|
328
333
|
setLast(null);
|
|
329
334
|
await refreshReports();
|
|
335
|
+
// Auto health after update
|
|
336
|
+
try { await doHealth(); } catch {}
|
|
330
337
|
setPill('ok', 'update ok');
|
|
331
338
|
} catch (e) {
|
|
332
339
|
setPill('err', 'update failed');
|
|
@@ -505,6 +512,7 @@
|
|
|
505
512
|
try {
|
|
506
513
|
if (!state.lastPlan) {
|
|
507
514
|
setPill('err', 'no plan');
|
|
515
|
+
setOut('## Apply failed\n\nNo plan available. Run **Save + Process (Agents)** first.');
|
|
508
516
|
return;
|
|
509
517
|
}
|
|
510
518
|
setPill('run', 'applying…');
|
|
@@ -522,6 +530,8 @@
|
|
|
522
530
|
setPill('ok', 'applied');
|
|
523
531
|
setTimeout(() => setPill('ok', 'idle'), 800);
|
|
524
532
|
} catch (e) {
|
|
533
|
+
const msg = (e && e.message) ? e.message : String(e);
|
|
534
|
+
setOut('## Apply failed\n\n' + msg);
|
|
525
535
|
setPill('err', 'apply failed');
|
|
526
536
|
}
|
|
527
537
|
}
|
|
@@ -531,21 +541,35 @@
|
|
|
531
541
|
$('chipPort').textContent = location.host;
|
|
532
542
|
loadLocal();
|
|
533
543
|
|
|
534
|
-
// Load persisted settings from the workspace
|
|
544
|
+
// Load persisted settings from the workspace + bootstrap (auto-init + auto-health)
|
|
535
545
|
(async () => {
|
|
546
|
+
let defaults = null;
|
|
536
547
|
try {
|
|
537
|
-
|
|
538
|
-
if (
|
|
539
|
-
$('dir').value =
|
|
540
|
-
$('sidePath').textContent =
|
|
548
|
+
defaults = await api('/api/defaults', { dir: dirOrDefault() });
|
|
549
|
+
if (defaults && defaults.workspaceDir) {
|
|
550
|
+
$('dir').value = defaults.workspaceDir;
|
|
551
|
+
$('sidePath').textContent = defaults.workspaceDir;
|
|
541
552
|
}
|
|
542
|
-
if (
|
|
543
|
-
$('discord').value =
|
|
544
|
-
$('teams').value =
|
|
553
|
+
if (defaults && defaults.settings) {
|
|
554
|
+
$('discord').value = defaults.settings.discordWebhookUrl || '';
|
|
555
|
+
$('teams').value = defaults.settings.teamsWebhookUrl || '';
|
|
545
556
|
}
|
|
546
557
|
} catch (e) {
|
|
547
558
|
// ignore
|
|
548
559
|
}
|
|
560
|
+
|
|
561
|
+
// If workspace isn't initialized yet, auto-init (reduces clicks)
|
|
562
|
+
try {
|
|
563
|
+
if (defaults && defaults.workspaceOk === false) {
|
|
564
|
+
setPill('run', 'auto-init…');
|
|
565
|
+
await doInit();
|
|
566
|
+
// After init, run health automatically
|
|
567
|
+
await doHealth();
|
|
568
|
+
}
|
|
569
|
+
} catch (e) {
|
|
570
|
+
// doInit/doHealth already surfaced errors
|
|
571
|
+
}
|
|
572
|
+
|
|
549
573
|
refreshReports();
|
|
550
574
|
})();
|
|
551
575
|
|
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>
|
|
@@ -710,7 +754,8 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
710
754
|
if (req.url === '/api/defaults') {
|
|
711
755
|
const settings = readSettings(workspaceDir);
|
|
712
756
|
const reports = listReports(workspaceDir).slice(0, 20);
|
|
713
|
-
|
|
757
|
+
const workspaceOk = looksLikeFreyaWorkspace(workspaceDir);
|
|
758
|
+
return safeJson(res, 200, { workspaceDir, workspaceOk, settings, reports });
|
|
714
759
|
}
|
|
715
760
|
|
|
716
761
|
if (req.url === '/api/settings/save') {
|
|
@@ -835,7 +880,15 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
835
880
|
try {
|
|
836
881
|
plan = JSON.parse(jsonText);
|
|
837
882
|
} catch (e) {
|
|
838
|
-
|
|
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
|
+
}
|
|
839
892
|
}
|
|
840
893
|
|
|
841
894
|
const actions = Array.isArray(plan.actions) ? plan.actions : [];
|
|
@@ -917,7 +970,11 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
917
970
|
try {
|
|
918
971
|
plan = JSON.parse(jsonText);
|
|
919
972
|
} catch (e) {
|
|
920
|
-
return safeJson(res, 400, {
|
|
973
|
+
return safeJson(res, 400, {
|
|
974
|
+
error: 'Plan is not valid JSON',
|
|
975
|
+
details: (e && e.message) ? e.message : String(e),
|
|
976
|
+
hint: 'O planner precisa retornar APENAS JSON. Se vier com texto extra, revise o prompt ou use apenas o bloco JSON.'
|
|
977
|
+
});
|
|
921
978
|
}
|
|
922
979
|
|
|
923
980
|
const actions = Array.isArray(plan.actions) ? plan.actions : [];
|