seo-intel 1.5.39 → 1.5.45
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/CHANGELOG.md +76 -0
- package/analyses/blog-draft/prescorer.js +17 -0
- package/analyses/loop/orchestrator.js +179 -0
- package/cli.js +197 -6
- package/crawler/html-extract.js +127 -0
- package/crawler/light.js +169 -0
- package/db/db.js +66 -0
- package/lib/cron.js +108 -0
- package/lib/gate.js +33 -1
- package/lib/intel.js +9 -3
- package/mcp/server.js +172 -17
- package/package.json +1 -1
- package/reports/generate-html.js +42 -404
- package/setup/web-routes.js +39 -0
- package/setup/wizard.html +73 -0
package/setup/web-routes.js
CHANGED
|
@@ -128,6 +128,17 @@ export function handleSetupRequest(req, res, url) {
|
|
|
128
128
|
return true;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// GET /api/setup/cron — read notify-cron install state
|
|
132
|
+
if (path === '/api/setup/cron' && method === 'GET') {
|
|
133
|
+
handleCronStatus(req, res);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// POST /api/setup/cron — install / remove
|
|
137
|
+
if (path === '/api/setup/cron' && method === 'POST') {
|
|
138
|
+
handleCronAction(req, res);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
131
142
|
// POST /api/setup/save-env — save a key to .env
|
|
132
143
|
if (path === '/api/setup/save-env' && method === 'POST') {
|
|
133
144
|
handleSaveEnv(req, res);
|
|
@@ -341,6 +352,34 @@ async function handlePingOllama(req, res) {
|
|
|
341
352
|
}
|
|
342
353
|
}
|
|
343
354
|
|
|
355
|
+
async function handleCronStatus(req, res) {
|
|
356
|
+
try {
|
|
357
|
+
const { getNotifyCronStatus } = await import('../lib/cron.js');
|
|
358
|
+
jsonResponse(res, getNotifyCronStatus());
|
|
359
|
+
} catch (err) {
|
|
360
|
+
jsonResponse(res, { error: err.message }, 500);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function handleCronAction(req, res) {
|
|
365
|
+
try {
|
|
366
|
+
const body = await readBody(req);
|
|
367
|
+
const { action, schedule, openOnFire } = body || {};
|
|
368
|
+
const { installNotifyCron, removeNotifyCron, getNotifyCronStatus } = await import('../lib/cron.js');
|
|
369
|
+
if (action === 'install') {
|
|
370
|
+
const result = installNotifyCron({ schedule, openOnFire: !!openOnFire });
|
|
371
|
+
jsonResponse(res, { ...result, status: getNotifyCronStatus() });
|
|
372
|
+
} else if (action === 'remove') {
|
|
373
|
+
const result = removeNotifyCron();
|
|
374
|
+
jsonResponse(res, { ...result, status: getNotifyCronStatus() });
|
|
375
|
+
} else {
|
|
376
|
+
jsonResponse(res, { error: 'action must be "install" or "remove"' }, 400);
|
|
377
|
+
}
|
|
378
|
+
} catch (err) {
|
|
379
|
+
jsonResponse(res, { error: err.message }, 500);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
344
383
|
async function handleSaveEnv(req, res) {
|
|
345
384
|
try {
|
|
346
385
|
const body = await readBody(req);
|
package/setup/wizard.html
CHANGED
|
@@ -1862,6 +1862,21 @@ input::placeholder {
|
|
|
1862
1862
|
<!-- Populated by JS -->
|
|
1863
1863
|
</table>
|
|
1864
1864
|
|
|
1865
|
+
<!-- ── Daily notification scheduler (v1.5.40) ────────────────────────── -->
|
|
1866
|
+
<div id="notifyCronCard" style="margin-top:20px; padding:14px 16px; background:var(--bg-elevated); border:1px solid rgba(59,130,246,0.18); border-radius:var(--radius); display:flex; gap:14px; align-items:center; flex-wrap:wrap;">
|
|
1867
|
+
<i class="fa-solid fa-bell" style="font-size:1rem; color:#3b82f6;"></i>
|
|
1868
|
+
<div style="flex:1; min-width:200px;">
|
|
1869
|
+
<div style="font-size:0.8rem; font-weight:600; color:var(--text-primary); margin-bottom:2px;">Daily problem notifications</div>
|
|
1870
|
+
<div id="notifyCronHint" style="font-size:0.66rem; color:var(--text-muted); line-height:1.4;">
|
|
1871
|
+
Fires a native macOS / Linux notification each morning for projects with pending criticals or warns.
|
|
1872
|
+
</div>
|
|
1873
|
+
</div>
|
|
1874
|
+
<div id="notifyCronStatus" style="font-size:0.66rem; color:var(--text-muted);">…</div>
|
|
1875
|
+
<button id="notifyCronToggleBtn" class="btn btn-sm" onclick="toggleNotifyCron()" style="padding:5px 12px; font-size:0.7rem; min-width:90px;">
|
|
1876
|
+
<i class="fa-solid fa-spinner fa-spin"></i>
|
|
1877
|
+
</button>
|
|
1878
|
+
</div>
|
|
1879
|
+
|
|
1865
1880
|
<h3 style="font-family:var(--font-display); font-size:0.85rem; color:var(--text-primary); margin: 20px 0 12px; font-weight:600;">
|
|
1866
1881
|
<i class="fa-solid fa-rocket" style="color:var(--accent-gold); margin-right:6px;"></i> What's Next
|
|
1867
1882
|
</h3>
|
|
@@ -3409,6 +3424,64 @@ input::placeholder {
|
|
|
3409
3424
|
}
|
|
3410
3425
|
|
|
3411
3426
|
goToStep(6);
|
|
3427
|
+
loadNotifyCronStatus();
|
|
3428
|
+
};
|
|
3429
|
+
|
|
3430
|
+
// ── Daily notification cron (v1.5.40) ──────────────────────────────────
|
|
3431
|
+
let _cronState = null;
|
|
3432
|
+
async function loadNotifyCronStatus() {
|
|
3433
|
+
const statusEl = document.getElementById('notifyCronStatus');
|
|
3434
|
+
const btn = document.getElementById('notifyCronToggleBtn');
|
|
3435
|
+
const hint = document.getElementById('notifyCronHint');
|
|
3436
|
+
if (!statusEl || !btn) return;
|
|
3437
|
+
try {
|
|
3438
|
+
const s = await API.get('/api/setup/cron');
|
|
3439
|
+
_cronState = s;
|
|
3440
|
+
if (s.platform === 'win32') {
|
|
3441
|
+
statusEl.textContent = 'Windows: use Task Scheduler';
|
|
3442
|
+
btn.disabled = true;
|
|
3443
|
+
btn.innerHTML = 'N/A';
|
|
3444
|
+
hint.textContent = 'Cron auto-install is macOS / Linux only — set up a daily Task Scheduler entry manually.';
|
|
3445
|
+
return;
|
|
3446
|
+
}
|
|
3447
|
+
if (s.installed) {
|
|
3448
|
+
statusEl.innerHTML = `<span style="color:var(--color-success);"><i class="fa-solid fa-check"></i> Active · ${s.schedule}</span>`;
|
|
3449
|
+
btn.innerHTML = '<i class="fa-solid fa-bell-slash"></i> Disable';
|
|
3450
|
+
btn.style.borderColor = 'rgba(244,123,93,0.4)';
|
|
3451
|
+
btn.style.color = '#f47b5d';
|
|
3452
|
+
} else {
|
|
3453
|
+
statusEl.innerHTML = `<span style="color:var(--text-muted);">Not scheduled</span>`;
|
|
3454
|
+
btn.innerHTML = '<i class="fa-solid fa-bell"></i> Enable';
|
|
3455
|
+
btn.style.borderColor = 'rgba(59,130,246,0.4)';
|
|
3456
|
+
btn.style.color = '#3b82f6';
|
|
3457
|
+
}
|
|
3458
|
+
btn.disabled = false;
|
|
3459
|
+
} catch (err) {
|
|
3460
|
+
statusEl.textContent = 'Status check failed';
|
|
3461
|
+
btn.disabled = true;
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3465
|
+
window.toggleNotifyCron = async function() {
|
|
3466
|
+
const btn = document.getElementById('notifyCronToggleBtn');
|
|
3467
|
+
if (!btn || btn.disabled) return;
|
|
3468
|
+
btn.disabled = true;
|
|
3469
|
+
btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
|
|
3470
|
+
const action = _cronState?.installed ? 'remove' : 'install';
|
|
3471
|
+
try {
|
|
3472
|
+
const r = await API.post('/api/setup/cron', { action, schedule: '0 9 * * *' });
|
|
3473
|
+
if (r.ok === false && r.error) {
|
|
3474
|
+
document.getElementById('notifyCronStatus').textContent = 'Error: ' + r.error;
|
|
3475
|
+
btn.disabled = false;
|
|
3476
|
+
btn.innerHTML = '<i class="fa-solid fa-triangle-exclamation"></i> Retry';
|
|
3477
|
+
return;
|
|
3478
|
+
}
|
|
3479
|
+
await loadNotifyCronStatus();
|
|
3480
|
+
} catch (err) {
|
|
3481
|
+
document.getElementById('notifyCronStatus').textContent = 'Error: ' + err.message;
|
|
3482
|
+
btn.disabled = false;
|
|
3483
|
+
btn.innerHTML = '<i class="fa-solid fa-triangle-exclamation"></i> Retry';
|
|
3484
|
+
}
|
|
3412
3485
|
};
|
|
3413
3486
|
|
|
3414
3487
|
window.copyCli = function() {
|