auto-sync-1530

This commit is contained in:
opus
2026-04-20 15:30:03 +02:00
parent 929c1ed438
commit aec0e09d8b
17 changed files with 602 additions and 2258 deletions

View File

@@ -1,6 +1,6 @@
{
"agent": "V41_Disk_Monitor",
"ts": "2026-04-20T15:00:02+02:00",
"ts": "2026-04-20T15:30:01+02:00",
"disk_pct": 78,
"disk_free_gb": 33,
"growth_per_day_gb": 1.5,

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"generated_at": "2026-04-20T15:20:01.934005",
"generated_at": "2026-04-20T15:30:02.315451",
"stats": {
"total": 30,
"total": 32,
"pending": 20,
"kaouther_surfaced": 18,
"chrome_surfaced": 2,

View File

@@ -5,8 +5,10 @@
"command": "python3 \/tmp\/v92_selenium_biz.py",
"cmd": "python3 \/tmp\/v92_selenium_biz.py",
"priority": "high",
"status": "dispatched",
"status": "failed_timeout",
"created": "2026-04-20T13:16:19+00:00",
"created_by": "blade-control-ui",
"dispatched_at": "2026-04-20T13:16:29+00:00"
"dispatched_at": "2026-04-20T13:16:29+00:00",
"failed_at": "2026-04-20T13:30:01+00:00",
"error": "Agent Blade did not callback task_done within 10min"
}

View File

@@ -5,8 +5,10 @@
"command": "# V92 WEVIA Business Scenario Test - opens tabs in authenticated Chrome session\n$urls = @(\n 'https:\/\/weval-consulting.com\/weval-technology-platform.html',\n 'https:\/\/weval-consulting.com\/wevia-em-big4.html',\n 'https:\/\/weval-consulting.com\/enterprise-model.html',\n 'https:\/\/weval-consulting.com\/business-kpi-dashboard.php',\n 'https:\/\/weval-consulting.com\/wevia-master.html',\n 'https:\/\/weval-consulting.com\/wevia-admin-crm.php'\n)\nforeach ($u in $urls) {\n Start-Process chrome -ArgumentList $u\n Start-Sleep -Seconds 2\n}\nNew-BurntToastNotification -Text 'WEVIA V92', 'Business scenario 6 tabs opened auth Chrome' -ErrorAction SilentlyContinue\nWrite-Host \"V92 scenario started at $(Get-Date)\"",
"cmd": "# V92 WEVIA Business Scenario Test - opens tabs in authenticated Chrome session\n$urls = @(\n 'https:\/\/weval-consulting.com\/weval-technology-platform.html',\n 'https:\/\/weval-consulting.com\/wevia-em-big4.html',\n 'https:\/\/weval-consulting.com\/enterprise-model.html',\n 'https:\/\/weval-consulting.com\/business-kpi-dashboard.php',\n 'https:\/\/weval-consulting.com\/wevia-master.html',\n 'https:\/\/weval-consulting.com\/wevia-admin-crm.php'\n)\nforeach ($u in $urls) {\n Start-Process chrome -ArgumentList $u\n Start-Sleep -Seconds 2\n}\nNew-BurntToastNotification -Text 'WEVIA V92', 'Business scenario 6 tabs opened auth Chrome' -ErrorAction SilentlyContinue\nWrite-Host \"V92 scenario started at $(Get-Date)\"",
"priority": "high",
"status": "dispatched",
"status": "failed_timeout",
"created": "2026-04-20T13:17:05+00:00",
"created_by": "blade-control-ui",
"dispatched_at": "2026-04-20T13:17:06+00:00"
"dispatched_at": "2026-04-20T13:17:06+00:00",
"failed_at": "2026-04-20T13:30:01+00:00",
"error": "Agent Blade did not callback task_done within 10min"
}

View File

@@ -1,7 +1,7 @@
{
"id": "v92_em_verify_20260420_1518",
"created_at": "2026-04-20T15:18:00+02:00",
"status": "dispatched",
"status": "failed_timeout",
"type": "playwright_e2e",
"title": "V92 E2E Enterprise Model + Big4 post-fix V91",
"description": "Selenium Chrome Blade validation enterprise-model.html TDZ fix V91. Prendre screenshots + v\u00e9rifier dots\/agents rendus via canvas (pas DOM).",
@@ -32,5 +32,7 @@
},
"priority": "high",
"source": "opus_v92",
"dispatched_at": "2026-04-20T13:17:51+00:00"
"dispatched_at": "2026-04-20T13:17:51+00:00",
"failed_at": "2026-04-20T13:30:01+00:00",
"error": "Agent Blade did not callback task_done within 10min"
}

View File

@@ -1,281 +0,0 @@
{
"ts": "2026-04-20T13:24:16+00:00",
"server": "s204",
"s204": {
"load": 2.58,
"uptime": "2026-04-14 11:51:24",
"ram_total_mb": 31335,
"ram_used_mb": 12402,
"ram_free_mb": 18932,
"disk_total": "150G",
"disk_used": "112G",
"disk_free": "33G",
"disk_pct": "78%",
"fpm_workers": 121,
"docker_containers": 19,
"cpu_cores": 8
},
"s95": {
"load": 0.25,
"disk_pct": "82%",
"status": "UP",
"ram_total_mb": 15610,
"ram_free_mb": 11779
},
"pmta": [
{
"name": "SER6",
"ip": "110.239.84.121",
"status": "DOWN"
},
{
"name": "SER7",
"ip": "110.239.65.64",
"status": "DOWN"
},
{
"name": "SER8",
"ip": "182.160.55.107",
"status": "DOWN"
},
{
"name": "SER9",
"ip": "110.239.86.68",
"status": "DOWN"
}
],
"assets": {
"html_pages": 287,
"php_apis": 751,
"wiki_entries": 1798,
"vault_doctrines": 59,
"vault_sessions": 89,
"vault_decisions": 12
},
"tools": {
"total": 626,
"registry_version": "?"
},
"sovereign": {
"status": "UP",
"providers": [
"Cerebras-fast",
"Cerebras-think",
"Groq",
"Cloudflare-AI",
"Gemini",
"SambaNova",
"NVIDIA-NIM",
"Mistral",
"Groq-OSS",
"HF-Space",
"HF-Router",
"OpenRouter",
"GitHub-Models"
],
"active": 13,
"total": 13,
"primary": "Cerebras-fast",
"cost": "0€"
},
"ethica": {
"total_hcps": 161730,
"with_email": 110441,
"with_phone": 155145,
"gap_email": 51289,
"pct_email": 68.3,
"pct_phone": 95.9,
"by_country": [
{
"country": "DZ",
"hcps": 122337,
"with_email": 78357,
"with_tel": 119394,
"pct_email": 64.1,
"pct_tel": 97.6
},
{
"country": "MA",
"hcps": 19720,
"with_email": 15067,
"with_tel": 18733,
"pct_email": 76.4,
"pct_tel": 95
},
{
"country": "TN",
"hcps": 17794,
"with_email": 15138,
"with_tel": 17018,
"pct_email": 85.1,
"pct_tel": 95.6
},
{
"country": "INTL",
"hcps": 1879,
"with_email": 1879,
"with_tel": 0,
"pct_email": 100,
"pct_tel": 0
}
]
},
"docker": [
{
"name": "loki",
"status": "Up 4 days",
"ports": ""
},
{
"name": "listmonk",
"status": "Up 4 days",
"ports": ""
},
{
"name": "plausible-plausible-1",
"status": "Up 2 days",
"ports": ""
},
{
"name": "plausible-plausible-db-1",
"status": "Up 2 days",
"ports": ""
},
{
"name": "plausible-plausible-events-db-1",
"status": "Up 2 days",
"ports": ""
},
{
"name": "n8n-docker-n8n-1",
"status": "Up 4 days",
"ports": ""
},
{
"name": "mattermost-docker-mm-db-1",
"status": "Up 4 days",
"ports": ""
},
{
"name": "mattermost-docker-mattermost-1",
"status": "Up 4 days (healthy)",
"ports": ""
},
{
"name": "twenty",
"status": "Up 4 days",
"ports": ""
},
{
"name": "twenty-redis",
"status": "Up 4 days",
"ports": ""
},
{
"name": "langfuse",
"status": "Up 4 days",
"ports": ""
},
{
"name": "redis-weval",
"status": "Up 5 days",
"ports": ""
},
{
"name": "gitea",
"status": "Up 5 days",
"ports": ""
},
{
"name": "node-exporter",
"status": "Up 5 days",
"ports": ""
},
{
"name": "prometheus",
"status": "Up 5 days",
"ports": ""
},
{
"name": "searxng",
"status": "Up 5 days",
"ports": ""
},
{
"name": "uptime-kuma",
"status": "Up 13 hours (healthy)",
"ports": ""
},
{
"name": "vaultwarden",
"status": "Up 5 days (healthy)",
"ports": ""
},
{
"name": "qdrant",
"status": "Up 5 days",
"ports": ""
}
],
"crons": {
"active": 35
},
"git": {
"head": "08eccabef auto-sync-opus46",
"dirty": 10,
"status": "DIRTY"
},
"nonreg": {
"total": 153,
"passed": 153,
"score": "100%"
},
"services": [
{
"name": "DeerFlow",
"port": 3002,
"status": "UP"
},
{
"name": "DeerFlow API",
"port": 8001,
"status": "UP"
},
{
"name": "Qdrant",
"port": 6333,
"status": "UP"
},
{
"name": "Ollama",
"port": 11434,
"status": "UP"
},
{
"name": "Redis",
"port": 6379,
"status": "UP"
},
{
"name": "Sovereign",
"port": 4000,
"status": "UP"
},
{
"name": "SearXNG",
"port": 8080,
"status": "UP"
}
],
"whisper": {
"binary": "COMPILED",
"model": "142MB"
},
"grand_total": 3540,
"health": {
"score": 5,
"max": 6,
"pct": 83
},
"elapsed_ms": 9349
}

View File

@@ -0,0 +1,77 @@
{
"enterprise_model": {
"diag": {
"AG_total": 739,
"AG_with_name": 739,
"AG_anonymous": 0,
"AG_dead": 167,
"AG_no_real_actions": 177,
"AG_no_skill": 7,
"DP_total": 27,
"DP_empty": 2,
"DP_empty_names": [
"meet",
"lean"
],
"sample_dead": [
{
"n": "ACT Business",
"rm": "dead",
"d": "general"
},
{
"n": "DocSpecialist",
"rm": "dead",
"d": "general"
},
{
"n": "Persona SAPConsultan",
"rm": "dead",
"d": "general"
},
{
"n": "SC Brainstorming",
"rm": "dead",
"d": "general"
},
{
"n": "SC BusinessPanel",
"rm": "dead",
"d": "general"
}
]
},
"js_errors": []
},
"em_big4": {
"diag": {
"url": "https://weval-consulting.com/login?r=/wevia-em-big4.html",
"title": "WEVAL — Login",
"body_len": 89,
"has_auth": true
},
"js_errors": []
},
"agents_archi": {
"diag": {
"url": "https://weval-consulting.com/login?r=/agents-archi.html",
"title": "WEVAL — Login",
"body_len": 89,
"canvas_count": 0,
"nodes_visible": 0,
"has_auth": true
},
"js_errors": []
},
"value_streaming": {
"diag": {
"url": "https://weval-consulting.com/login?r=/value-streaming.html",
"title": "WEVAL — Login",
"body_len": 89,
"canvas_count": 0,
"svg_count": 0,
"has_auth": true
},
"js_errors": []
}
}

View File

@@ -0,0 +1,75 @@
{
"ts": "2026-04-20T13:29:08.604Z",
"test": "V93b Simple Video Business",
"results": [
{
"name": "enterprise-model",
"status": "OK",
"title": "WEVAL Enterprise Model",
"url": "https://weval-consulting.com/enterprise-model.html",
"body_len": 429,
"canvas": 1,
"svg": 0,
"ag_defined": false,
"ag_count": 0
},
{
"name": "wevia-em-big4",
"status": "OK",
"title": "WEVAL — Login",
"url": "https://weval-consulting.com/login?r=/wevia-em-big4.html",
"body_len": 89,
"canvas": 0,
"svg": 0,
"ag_defined": false,
"ag_count": 0
},
{
"name": "business-kpi",
"status": "OK",
"title": "V83 Business KPI Dashboard — SaaS Ready",
"url": "https://weval-consulting.com/business-kpi-dashboard.php",
"body_len": 5566,
"canvas": 0,
"svg": 57,
"ag_defined": false,
"ag_count": 0
},
{
"name": "wevia-master",
"status": "OK",
"title": "WEVAL — Login",
"url": "https://weval-consulting.com/login?r=/wevia-master.html",
"body_len": 89,
"canvas": 0,
"svg": 0,
"ag_defined": false,
"ag_count": 0
},
{
"name": "crm",
"status": "OK",
"title": "WEVAL CRM — Deal Tracker",
"url": "https://weval-consulting.com/crm.html",
"body_len": 757,
"canvas": 0,
"svg": 0,
"ag_defined": false,
"ag_count": 0
},
{
"name": "wtp",
"status": "OK",
"title": "WEVAL — Connexion",
"url": "https://weval-consulting.com/login.html?from=%2Fweval-technology-platform.html",
"body_len": 250,
"canvas": 0,
"svg": 0,
"ag_defined": false,
"ag_count": 0
}
],
"errs": [],
"ok": 6,
"fail": 0
}

View File

@@ -1,7 +1,7 @@
{
"ok": true,
"version": "V83-business-kpi",
"ts": "2026-04-20T13:24:40+00:00",
"ts": "2026-04-20T13:29:06+00:00",
"summary": {
"total_categories": 7,
"total_kpis": 56,

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

107
api/v93_deep_em_audit.js Normal file
View File

@@ -0,0 +1,107 @@
const { chromium } = require('playwright');
const fs = require('fs');
(async () => {
const browser = await chromium.launch({ headless: true });
const ctx = await browser.newContext({
viewport: { width: 1920, height: 1080 },
recordVideo: { dir: '/tmp/v93-videos/' }
});
const page = await ctx.newPage();
await page.addInitScript(() => {
try { localStorage.setItem('weval_internal', 'yacine-2026'); } catch(e){}
});
const errs = [];
page.on('pageerror', e => errs.push(`pageerror: ${e.message.substring(0,150)}`));
page.on('console', m => { if (m.type() === 'error') errs.push(`console: ${m.text().substring(0,150)}`); });
const results = {};
// TEST 1: Enterprise-model - deep audit visuel
await page.goto('https://weval-consulting.com/enterprise-model.html', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(10000); // Longer wait for full render
const emDiag = await page.evaluate(() => {
const agentsWithName = typeof AG !== 'undefined' ? AG.filter(a => a && a.n && a.n.trim()) : [];
const agentsAnon = typeof AG !== 'undefined' ? AG.filter(a => !a || !a.n || !a.n.trim() || a.n === 'Sync') : [];
const deadAgents = typeof AG !== 'undefined' ? AG.filter(a => a && a.rm === 'dead') : [];
const agentsNoActions = typeof AG !== 'undefined' ? AG.filter(a => a && (!a.act || a.act.length === 0 || (a.act[0]==='Sync' && a.act.length===4))) : [];
const agentsNoSkill = typeof AG !== 'undefined' ? AG.filter(a => a && (!a.d || a.d === 'general' || a.d === 'Paperclip')) : [];
// DP stats
const dps = typeof DP !== 'undefined' ? DP : [];
const emptyDps = dps.filter(d => typeof AG !== 'undefined' ? AG.filter(a => a.rm === d.id).length === 0 : true);
return {
AG_total: typeof AG !== 'undefined' ? AG.length : 0,
AG_with_name: agentsWithName.length,
AG_anonymous: agentsAnon.length,
AG_dead: deadAgents.length,
AG_no_real_actions: agentsNoActions.length,
AG_no_skill: agentsNoSkill.length,
DP_total: dps.length,
DP_empty: emptyDps.length,
DP_empty_names: emptyDps.map(d => d.id).slice(0, 5),
sample_dead: deadAgents.slice(0, 5).map(a => ({ n: a.n, rm: a.rm, d: a.d }))
};
});
results.enterprise_model = { diag: emDiag, js_errors: [...errs] };
errs.length = 0;
// TEST 2: wevia-em-big4
await page.goto('https://weval-consulting.com/wevia-em-big4.html', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(5000);
const big4Diag = await page.evaluate(() => {
return {
url: location.href,
title: document.title,
body_len: document.body.innerText.length,
has_auth: location.href.includes('login'),
};
});
results.em_big4 = { diag: big4Diag, js_errors: [...errs] };
errs.length = 0;
// TEST 3: agents-archi
await page.goto('https://weval-consulting.com/agents-archi.html', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(5000);
const archDiag = await page.evaluate(() => {
const canvases = document.querySelectorAll('canvas');
const nodes = document.querySelectorAll('.node, [class*="agent-node"], .agent-dot, circle');
return {
url: location.href,
title: document.title,
body_len: document.body.innerText.length,
canvas_count: canvases.length,
nodes_visible: nodes.length,
has_auth: location.href.includes('login'),
};
});
await page.screenshot({ path: '/tmp/v93-agents-archi.png', fullPage: true });
results.agents_archi = { diag: archDiag, js_errors: [...errs] };
errs.length = 0;
// TEST 4: value-streaming
await page.goto('https://weval-consulting.com/value-streaming.html', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(5000);
const vsDiag = await page.evaluate(() => {
const canvases = document.querySelectorAll('canvas');
const svgs = document.querySelectorAll('svg');
return {
url: location.href,
title: document.title,
body_len: document.body.innerText.length,
canvas_count: canvases.length,
svg_count: svgs.length,
has_auth: location.href.includes('login'),
};
});
await page.screenshot({ path: '/tmp/v93-value-streaming.png', fullPage: true });
results.value_streaming = { diag: vsDiag, js_errors: [...errs] };
await ctx.close();
await browser.close();
fs.writeFileSync('/var/www/html/api/playwright-v93-deep-audit.json', JSON.stringify(results, null, 2));
console.log(JSON.stringify(results, null, 2));
})();

65
api/v93_simple.js Normal file
View File

@@ -0,0 +1,65 @@
const { chromium } = require('playwright');
const fs = require('fs');
(async () => {
console.log('V93 starting...');
const browser = await chromium.launch({ headless: true });
const ctx = await browser.newContext({
viewport: { width: 1920, height: 1080 },
recordVideo: { dir: '/tmp/v93b-videos/', size: { width: 1920, height: 1080 } }
});
const page = await ctx.newPage();
await page.addInitScript(() => {
try { localStorage.setItem('weval_internal', 'yacine-2026'); } catch(e){}
});
const results = [];
const errs = [];
page.on('pageerror', e => errs.push(e.message.substring(0,150)));
const urls = [
{ n: 'enterprise-model', u: 'https://weval-consulting.com/enterprise-model.html' },
{ n: 'wevia-em-big4', u: 'https://weval-consulting.com/wevia-em-big4.html' },
{ n: 'business-kpi', u: 'https://weval-consulting.com/business-kpi-dashboard.php' },
{ n: 'wevia-master', u: 'https://weval-consulting.com/wevia-master.html' },
{ n: 'crm', u: 'https://weval-consulting.com/crm.html' },
{ n: 'wtp', u: 'https://weval-consulting.com/weval-technology-platform.html' },
];
for (const target of urls) {
try {
console.log(`${target.n}`);
await page.goto(target.u, { waitUntil: 'load', timeout: 20000 });
await page.waitForTimeout(3000);
const diag = await page.evaluate(() => ({
title: document.title.substring(0, 60),
url: location.href,
body_len: document.body.innerText.length,
canvas: document.querySelectorAll('canvas').length,
svg: document.querySelectorAll('svg').length,
ag_defined: typeof window.AG !== 'undefined',
ag_count: typeof window.AG !== 'undefined' ? window.AG.length : 0,
}));
await page.screenshot({ path: `/tmp/v93b-${target.n}.png`, fullPage: false });
results.push({ name: target.n, status: 'OK', ...diag });
} catch (e) {
results.push({ name: target.n, status: 'FAIL', err: e.message.substring(0, 150) });
}
}
await ctx.close();
await browser.close();
const summary = {
ts: new Date().toISOString(),
test: 'V93b Simple Video Business',
results,
errs,
ok: results.filter(r => r.status === 'OK').length,
fail: results.filter(r => r.status === 'FAIL').length,
};
fs.writeFileSync('/var/www/html/api/playwright-v93b-business.json', JSON.stringify(summary, null, 2));
console.log(`DONE: ${summary.ok}/${results.length} errs=${errs.length}`);
})();

View File

@@ -398,7 +398,7 @@ let AG=[{n:'WEVIA Master',rm:'ceo',d:'Head of AI',p:'Orchestrator',sk:'#ffd700',
{n:'ISV Resilience Agent',rm:'fin',d:'Finance',p:'ISV',sk:'#22d3ee',hc:'#0891b2',F:1,wv:1,re:'🤖',savings:'95k/an',pp:'PP043',act:['WEVAL','Agent','Autonome','Savings']},
{n:'Multi-Entity Consolidator SMB',rm:'fin',d:'Finance',p:'SMBConsol',sk:'#22d3ee',hc:'#0891b2',F:1,wv:1,re:'🤖',savings:'140k/an',pp:'PP037',act:['WEVAL','Agent','Autonome','Savings']}];
AG=AG.filter(function(a){return a&&a.n;});
AG=AG.filter(function(a){return a&&a.n&&a.rm!=='dead';});/* V93 hide dead agents (was 167 invisible blocks) */
// V91 TDZ fix: DP filter moved here (after AG declaration) - was at line 90
DP=DP.filter(function(d){var c=AG.filter(function(a){return a.rm===d.id;}).length;return c>0||["meet","lean"].indexOf(d.id)>=0;});
// Tasks are now per-agent in act[]

261
linkedin-control-v98.html Normal file
View File

@@ -0,0 +1,261 @@
<!DOCTYPE html>
<html lang="fr"><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>LinkedIn Full Auto V98 - WEVAL</title>
<style>
:root{--bg:#0a0e1a;--bg2:#141b2d;--bg3:#1e2740;--fg:#e8ecf4;--t2:#9aa5c0;--t3:#5f6b85;--brd:rgba(255,255,255,.08);--gold:#d4af37;--em:#10b981;--cy:#06b6d4;--am:#f59e0b;--co:#ef4444;--vi:#8b5cf6;--sa:#3b82f6;}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--fg);font-family:'SF Pro Display',system-ui,sans-serif;min-height:100vh;padding:20px}
.hdr{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--brd)}
.hdr h1{font-size:24px}.hdr h1 span{color:var(--gold)}
.sub{color:var(--t2);font-size:12px;margin-top:4px}
.badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:10px;margin-left:6px}
.badge.ok{background:var(--em);color:#000}.badge.err{background:var(--co);color:#fff}.badge.warn{background:var(--am);color:#000}
.btn{padding:8px 14px;background:var(--bg3);border:1px solid var(--brd);border-radius:6px;color:var(--fg);cursor:pointer;font-size:12px;font-weight:500;text-decoration:none;display:inline-block}
.btn:hover{background:var(--gold);color:#000}
.btn.primary{background:var(--gold);color:#000}
.btn.success{background:var(--em);color:#000}.btn.danger{background:var(--co);color:#fff}
.btns{display:flex;gap:8px;flex-wrap:wrap}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-bottom:20px}
.mc{background:var(--bg2);border:1px solid var(--brd);border-left:3px solid var(--gold);padding:12px;border-radius:8px}
.mc-l{font-size:9px;color:var(--t2);text-transform:uppercase;letter-spacing:1px}
.mc-v{font-size:24px;font-weight:700;margin:4px 0}
.mc-s{font-size:10px;color:var(--em)}
.mc.v{border-left-color:var(--vi)}.mc.c{border-left-color:var(--cy)}.mc.e{border-left-color:var(--em)}.mc.a{border-left-color:var(--am)}.mc.r{border-left-color:var(--co)}
.cols{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px}
@media(max-width:1100px){.cols{grid-template-columns:1fr}}
.col{background:var(--bg2);border:1px solid var(--brd);border-radius:10px;padding:14px;max-height:650px;overflow-y:auto}
.col h2{font-size:12px;font-weight:700;color:var(--t2);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:12px;display:flex;justify-content:space-between}
.col h2 .count{background:var(--bg3);padding:2px 8px;border-radius:10px;color:var(--gold);font-size:10px}
.post{background:var(--bg3);border:1px solid var(--brd);border-radius:8px;padding:11px;margin-bottom:10px}
.post-h{display:flex;justify-content:space-between;font-size:10px;color:var(--t3);margin-bottom:6px}
.post-h .theme{color:var(--cy);font-weight:600;text-transform:uppercase}
.post-body{font-size:11px;line-height:1.5;max-height:160px;overflow-y:auto;background:var(--bg);padding:8px;border-radius:4px;margin-bottom:8px;white-space:pre-wrap}
.post-meta{display:flex;gap:8px;font-size:9px;color:var(--t3);margin-bottom:8px;flex-wrap:wrap}
.post-actions{display:flex;gap:4px;flex-wrap:wrap}
.post-actions button{padding:4px 9px;border:1px solid var(--brd);border-radius:4px;font-size:10px;cursor:pointer;background:var(--bg);color:var(--t2)}
.post-actions button.approve{background:var(--em);color:#fff;border-color:var(--em)}
.post-actions button.auto{background:var(--gold);color:#000;border-color:var(--gold);font-weight:600}
.post-actions button.reject{background:var(--co);color:#fff;border-color:var(--co)}
.spnr{display:inline-block;width:12px;height:12px;border:2px solid var(--gold);border-top:2px solid transparent;border-radius:50%;animation:sp 1s linear infinite;vertical-align:middle}
@keyframes sp{to{transform:rotate(360deg)}}
.empty{text-align:center;color:var(--t3);padding:30px;font-size:12px;font-style:italic}
.session-box{background:var(--bg2);border:1px solid var(--brd);border-radius:10px;padding:16px;margin-bottom:20px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px}
.session-status{display:flex;align-items:center;gap:10px;font-size:13px}
.session-status.logged{color:var(--em)}.session-status.expired{color:var(--co)}.session-status.missing{color:var(--am)}
.log-panel{background:var(--bg2);border:1px solid var(--brd);border-radius:8px;padding:12px;margin-top:20px}
.log-panel h2{font-size:11px;color:var(--t2);margin-bottom:8px}
.log-panel pre{font-size:10px;max-height:200px;overflow-y:auto;background:var(--bg);padding:8px;border-radius:4px;white-space:pre-wrap;color:var(--t2)}
</style></head><body>
<div class="hdr">
<div>
<h1>LinkedIn <span>Full Auto V98</span> <span id="sessionBadge" class="badge warn">Session: checking</span></h1>
<div class="sub">Browser-piloted by WEVIA Master · Playwright Chromium persistent · Doctrine #100 · Cron */20min</div>
</div>
<div class="btns">
<a href="/linkedin-automation-v96.html" class="btn">V96 Generator</a>
<a href="/linkedin-control-v97.html" class="btn">V97 Control</a>
<a href="/weval-technology-platform.html" class="btn">WTP</a>
<a href="/wevia-master.html" class="btn primary">WEVIA Chat</a>
</div>
</div>
<div class="session-box">
<div class="session-status" id="sessionStatus">⟳ Checking browser session...</div>
<div class="btns">
<button class="btn" onclick="injectSession()">💉 Inject LI_AT cookie</button>
<button class="btn" onclick="checkSession()">🔍 Check session</button>
<button class="btn success" onclick="triggerBrowserCron()">⚡ Trigger Browser Publish Now</button>
</div>
</div>
<div class="grid" id="kpiGrid"></div>
<div class="btns" style="margin-bottom:20px">
<button class="btn primary" onclick="gen('wevia_sovereign_ai')">🤖 Generate WEVIA</button>
<button class="btn primary" onclick="gen('ethica_hcp')">💊 Ethica</button>
<button class="btn primary" onclick="gen('vistex_sap')">🏢 Vistex</button>
<button class="btn primary" onclick="gen('case_study')">📊 Case</button>
<button class="btn" onclick="loadAll()">🔄 Refresh</button>
</div>
<div class="cols">
<div class="col">
<h2>📝 Drafts <span id="draftsCount" class="count">0</span></h2>
<div id="drafts"></div>
</div>
<div class="col">
<h2>⏰ Scheduled <span id="schedCount" class="count">0</span></h2>
<div id="scheduled"></div>
</div>
<div class="col">
<h2>✅ Published <span id="pubCount" class="count">0</span></h2>
<div id="published"></div>
</div>
</div>
<div class="log-panel">
<h2>V97/V98 Cron Log</h2>
<pre id="log">Loading...</pre>
</div>
<script>
const API='/api/v97-linkedin-control.php';
const V96='/api/v96-linkedin-automation.php';
async function checkSession(){
const status=document.getElementById('sessionStatus');
const badge=document.getElementById('sessionBadge');
status.innerHTML='⟳ Running Playwright check (may take 30-60s)...';
status.className='session-status';
try{
const r=await fetch(API+'?action=browser_session_status');
const s=await r.json();
if(s.session_exists){
status.innerHTML='✅ Session file present · age '+s.age_hours+'h · last '+s.last_update;
status.className='session-status logged';
badge.textContent='Session: exists';badge.className='badge ok';
}else{
status.innerHTML='⚠️ No session cookies · inject LI_AT from /etc/weval/secrets.env (see below)';
status.className='session-status missing';
badge.textContent='Session: missing';badge.className='badge warn';
}
}catch(e){
status.innerHTML='❌ Error: '+e.message;
status.className='session-status expired';
badge.textContent='Session: error';badge.className='badge err';
}
}
async function injectSession(){
const status=document.getElementById('sessionStatus');
status.innerHTML='⟳ Injecting LI_AT cookie from secrets.env...';
const r=await fetch(API+'?action=browser_inject_session');
const d=await r.json();
if(d.ok && d.logged_in){
status.innerHTML='✅ Browser session OK · logged in as Weval';
status.className='session-status logged';
document.getElementById('sessionBadge').textContent='Session: ready';
document.getElementById('sessionBadge').className='badge ok';
}else if(d.err){
status.innerHTML='⚠️ '+d.err+' — '+(d.instruction||'Add LI_AT=... to /etc/weval/secrets.env');
status.className='session-status missing';
}else{
status.innerHTML='❌ '+JSON.stringify(d);
}
checkSession();
}
async function triggerBrowserCron(){
const b=event.target;const o=b.innerHTML;
b.innerHTML='<span class="spnr"></span> Running browser publish (up to 3min)';b.disabled=true;
const r=await fetch(API+'?action=browser_publish_due');
const d=await r.json();
alert('Browser cron: '+JSON.stringify(d,null,2));
b.innerHTML=o;b.disabled=false;
loadAll();
}
async function loadOverview(){
const r=await fetch(API+'?action=overview');const d=await r.json();
const st=d.page_stats||{};
document.getElementById('kpiGrid').innerHTML=[
['Drafts','v',d.queue_drafts,'pending approval'],
['Scheduled','a',d.scheduled,'cron */20min'],
['Published','e',d.published_count,'today: '+d.published_today],
['Followers','s',st.followers||0,'+'+(st.last_7d_new_followers||0)+' 7d'],
['Impressions 7d','e',st.last_7d_post_impressions||0,'+'+(st.pct_growth_impressions||0)+'%'],
['Pixel','c',d.pixel_hits_month||0,'LinkedIn ref'],
].map(([l,c,v,sub])=>`<div class="mc ${c}"><div class="mc-l">${l}</div><div class="mc-v">${v}</div><div class="mc-s">${sub}</div></div>`).join('');
}
async function loadQueues(){
const r=await fetch(API+'?action=all_queues');const d=await r.json();
const drafts=(d.queue_drafts||[]).reverse();
document.getElementById('draftsCount').textContent=drafts.length;
document.getElementById('drafts').innerHTML=drafts.length?drafts.map(p=>renderPost(p,'draft')).join(''):'<div class="empty">No drafts</div>';
const sched=d.scheduled||[];
document.getElementById('schedCount').textContent=sched.length;
document.getElementById('scheduled').innerHTML=sched.length?sched.map(p=>renderPost(p,'scheduled')).join(''):'<div class="empty">Nothing scheduled</div>';
const pub=(d.published||[]).slice().reverse().slice(0,10);
document.getElementById('pubCount').textContent=(d.published||[]).length;
document.getElementById('published').innerHTML=pub.length?pub.map(p=>renderPost(p,'published')).join(''):'<div class="empty">No published yet</div>';
}
function renderPost(p,col){
const body=(p.post||'').replace(/</g,'&lt;');
const status=p.status||'draft';
let actions='';
if(col==='draft'){
actions=`
<button class="auto" onclick="browserPub('${p.id}')">🤖 Publish via Browser</button>
<button class="approve" onclick="act('approve','${p.id}')">✓ Approve</button>
<button onclick="schedulePrompt('${p.id}')">⏰ Schedule</button>
<button class="reject" onclick="act('reject','${p.id}')">🗑</button>
`;
}else if(col==='scheduled'){
actions=`<span style="color:var(--am);font-size:10px">⏰ ${new Date(p.scheduled_at).toLocaleString('fr')} · Auto-publish via browser</span>`;
}else{
actions=`<span style="color:var(--em);font-size:10px">✅ ${new Date(p.published_at).toLocaleString('fr')} via ${p.published_via||'?'}</span>`;
if(p.screenshot){actions+=` · <a href="${p.screenshot.replace('/var/www/html','')}" target="_blank" style="color:var(--cy);font-size:10px">📷 proof</a>`;}
}
return `<div class="post" id="p-${p.id}">
<div class="post-h"><span class="theme">${p.theme||'?'}</span><span>${p.chars||0}${p.metrics_count||0}${p.hashtags_count||0}#</span></div>
<div class="post-body">${body}</div>
<div class="post-actions">${actions}</div>
</div>`;
}
async function browserPub(id){
const b=event.target;const o=b.innerHTML;
b.innerHTML='<span class="spnr"></span> Publishing via browser';b.disabled=true;
const r=await fetch(API+'?action=browser_publish_id&id='+id);
const d=await r.json();
if(d.results && d.results[0] && d.results[0].ok){
alert('✅ Published! Screenshot: '+d.results[0].screenshot_proof);
}else{
alert('❌ '+JSON.stringify(d,null,2));
}
b.innerHTML=o;b.disabled=false;
loadAll();
}
async function act(action,id){
if(action==='reject'&&!confirm('Delete?'))return;
const r=await fetch(API+'?action='+action+'&id='+id);
const d=await r.json();
if(d.ok){loadAll();}else{alert('Failed: '+JSON.stringify(d));}
}
async function schedulePrompt(id){
const hrs=prompt('Schedule in hours from now?','2');
if(!hrs)return;
const when=new Date(Date.now()+parseInt(hrs)*3600*1000).toISOString();
const r=await fetch(API+'?action=schedule&id='+id+'&when='+encodeURIComponent(when));
const d=await r.json();
if(d.ok){loadAll();}else{alert('Failed');}
}
async function gen(theme){
const b=event.target;const o=b.innerHTML;
b.innerHTML='<span class="spnr"></span> Generating';b.disabled=true;
await fetch(V96+'?action=generate_post&theme='+theme);
b.innerHTML=o;b.disabled=false;
loadAll();
}
async function loadLog(){
try{
const r=await fetch(API+'?action=log');const d=await r.json();
document.getElementById('log').textContent=d.log||'(no runs yet)';
}catch(e){}
}
async function loadAll(){await loadOverview();await loadQueues();await loadLog();}
checkSession();
loadAll();
setInterval(loadAll,30000);
</script>
</body></html>