Compare commits
236 Commits
wave-212-v
...
wave-232-g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1e4930ef9 | ||
|
|
994e0413e9 | ||
|
|
c08fd1117b | ||
|
|
001b9b104d | ||
|
|
3c09a5e5b1 | ||
|
|
324698c5cf | ||
|
|
c8019a2d72 | ||
|
|
0830dbddf2 | ||
|
|
71ac5c5a38 | ||
|
|
d7fbb6c2b6 | ||
|
|
62bf54f93d | ||
|
|
c67ba9c962 | ||
|
|
54c7e3ec4d | ||
|
|
39904106c9 | ||
|
|
843abe732c | ||
|
|
c22547a33e | ||
|
|
5ab3e108eb | ||
|
|
cfae522ed4 | ||
|
|
9797434c72 | ||
|
|
134eff6a06 | ||
|
|
1cc3ae62a8 | ||
|
|
cfc0c28610 | ||
|
|
309ca20fcf | ||
|
|
decde3ae1c | ||
|
|
e15ac4d968 | ||
|
|
e57f89ce86 | ||
|
|
7ac430f9ca | ||
|
|
9447d5a39e | ||
|
|
66bb848446 | ||
|
|
c77665eeeb | ||
|
|
9f1414d8e1 | ||
|
|
9e33717e71 | ||
|
|
d7573697c4 | ||
|
|
464843a3f7 | ||
|
|
a30621772a | ||
|
|
6a27358e14 | ||
|
|
a632ef9b6e | ||
|
|
d626ff474f | ||
|
|
d96f1e4361 | ||
|
|
e12120c7a3 | ||
|
|
c30afe2de4 | ||
|
|
bfa20ebe57 | ||
|
|
6df6fd7f35 | ||
|
|
3eda96d9d4 | ||
|
|
306552cec6 | ||
|
|
073d617d08 | ||
|
|
27f9e80bc9 | ||
|
|
14976ae05a | ||
|
|
4dd03ea3fb | ||
|
|
41e8202461 | ||
|
|
d9016feadc | ||
|
|
4193cac577 | ||
|
|
bb34f9695f | ||
|
|
f75092aa3f | ||
|
|
2ff7e3a0ea | ||
|
|
cb993ae41c | ||
|
|
dae689cecd | ||
|
|
fa16e6554e | ||
|
|
a4d0c4d564 | ||
|
|
adf9eba31c | ||
|
|
c22f115b3e | ||
|
|
9c69db151f | ||
|
|
bc6d6cb2fb | ||
|
|
c4bf820a92 | ||
|
|
99195cf362 | ||
|
|
a6c4850b58 | ||
|
|
874a7c6dfa | ||
|
|
917e2441af | ||
|
|
decb3e2904 | ||
|
|
84a6a12f1f | ||
|
|
97c4a5e1b3 | ||
|
|
3e44d926de | ||
|
|
7737c976ed | ||
|
|
c5fa4e7480 | ||
|
|
99c7db040f | ||
|
|
ac38795373 | ||
|
|
a78b554733 | ||
|
|
a0257bff01 | ||
|
|
b438489484 | ||
|
|
282cba3eda | ||
|
|
b157e5e6da | ||
|
|
ee1ce9d791 | ||
|
|
4cdd2f56ba | ||
|
|
2a6e707f38 | ||
|
|
23ef40516a | ||
|
|
03c2699122 | ||
|
|
79af700e98 | ||
|
|
124b23e60f | ||
|
|
ad93447f00 | ||
|
|
0558cf03ed | ||
|
|
19cb060d60 | ||
|
|
b74675f037 | ||
|
|
632f6349e3 | ||
|
|
05ce22a54c | ||
|
|
ec6762838f | ||
|
|
dc58ec560f | ||
|
|
b0f9523064 | ||
|
|
ff64a67fbb | ||
|
|
5e9aa9d772 | ||
|
|
0eb4825f7d | ||
|
|
e3e6e3ac54 | ||
|
|
8e37e1c3f4 | ||
|
|
5ed6857e78 | ||
|
|
8d0f0ceee4 | ||
|
|
e94c263624 | ||
|
|
e824e9c03e | ||
|
|
2c9ff7c958 | ||
|
|
8a38661311 | ||
|
|
ad9d3dc376 | ||
|
|
c362e5f77e | ||
|
|
61447aca2a | ||
|
|
d3598d1184 | ||
|
|
260cc8a553 | ||
|
|
3c392a4142 | ||
|
|
bb284e4101 | ||
|
|
f3fb7283bf | ||
|
|
6fd30277fa | ||
|
|
68109fc3f2 | ||
|
|
d9859c93fa | ||
|
|
27ae771f3a | ||
|
|
5aaf0e7f0f | ||
|
|
2d7b488c46 | ||
|
|
412ff8b23b | ||
|
|
4ec7c0bb9e | ||
|
|
98618d0006 | ||
|
|
049296d1aa | ||
|
|
d98131946e | ||
|
|
a705e42253 | ||
|
|
6b25030a3c | ||
|
|
0456d672ff | ||
|
|
151ffbae63 | ||
|
|
28678e4b47 | ||
|
|
d6e82b4b86 | ||
|
|
99b9df00c0 | ||
|
|
b1d25f329d | ||
|
|
f33599517d | ||
|
|
927e3aaaa0 | ||
|
|
b1997fedd7 | ||
|
|
3e2ae4708e | ||
|
|
065a4f33b6 | ||
|
|
1d540d16be | ||
|
|
87a1c0f0bd | ||
|
|
149a5f4ce8 | ||
|
|
5d83d1643a | ||
|
|
150d0d4dc8 | ||
|
|
d402da46f0 | ||
|
|
5d4265f307 | ||
|
|
f1d91b48ff | ||
|
|
6f995f624d | ||
|
|
bd1e9568d5 | ||
|
|
8c9e214153 | ||
|
|
d5dce6ea86 | ||
|
|
e6bc3c0523 | ||
|
|
4197c5dbaf | ||
|
|
cf95bf9fae | ||
|
|
a1b0b3e36e | ||
|
|
1c1c3fe604 | ||
|
|
fda0d21ca5 | ||
|
|
ddbfde476b | ||
|
|
c2437eecfd | ||
|
|
1dbaa747e1 | ||
|
|
d0938f8944 | ||
|
|
3924d91b2b | ||
|
|
ead2dcfc4a | ||
|
|
8f8aee325a | ||
|
|
f709a64db8 | ||
|
|
601617d446 | ||
|
|
abe624d03e | ||
|
|
953bb4414f | ||
|
|
c7bd363ad7 | ||
|
|
ef96d08f0e | ||
|
|
cbd6b4a03a | ||
|
|
b7d40c7503 | ||
|
|
4199cd3ff0 | ||
|
|
b6dcdc7770 | ||
|
|
7807e3feb6 | ||
|
|
34902f4714 | ||
|
|
aaca72d969 | ||
|
|
541420e1fe | ||
|
|
eca9d344f9 | ||
|
|
ad828e1e53 | ||
|
|
d6f6b89e72 | ||
|
|
f1921776ff | ||
|
|
bc20d19b4a | ||
|
|
ef1412d144 | ||
|
|
3e35ae52c0 | ||
|
|
b23c7f2fa8 | ||
|
|
fb43bef9cc | ||
|
|
1bd5572777 | ||
|
|
b9f9afcbd6 | ||
|
|
98b153deae | ||
|
|
8805740235 | ||
|
|
44e9c6aef2 | ||
|
|
9534414da4 | ||
|
|
a44eaa78ca | ||
|
|
46305ae822 | ||
|
|
b477374a61 | ||
|
|
cd6a22911a | ||
|
|
ecf3c428be | ||
|
|
31b38ccaaa | ||
|
|
c75e9d76b4 | ||
|
|
08d170b2de | ||
|
|
147f5341e9 | ||
|
|
372ca9d069 | ||
|
|
8b8c227a78 | ||
|
|
0d91482bfd | ||
|
|
9664c70408 | ||
|
|
184aab3b80 | ||
|
|
87e388d78d | ||
|
|
9f469187a0 | ||
|
|
9e1293c3c9 | ||
|
|
82b2eabf5f | ||
|
|
ec0351df90 | ||
|
|
77773bb0d9 | ||
|
|
99d2dccbba | ||
|
|
05d106a478 | ||
|
|
30b53a14b9 | ||
|
|
bc73c1d984 | ||
|
|
4e971701fa | ||
|
|
0644fbd692 | ||
|
|
5de7c1a0c8 | ||
|
|
bf5f7d1b0d | ||
|
|
18e0a3a563 | ||
|
|
b1a2a6490d | ||
|
|
0925c771a0 | ||
|
|
d46f607976 | ||
|
|
82e6c9258a | ||
|
|
69dcf0a399 | ||
|
|
16422d64d7 | ||
|
|
1ffcb080ec | ||
|
|
648dc20cda | ||
|
|
fda1766fbf | ||
|
|
9efc943107 | ||
|
|
05f3b70e00 | ||
|
|
34c58540ee | ||
|
|
5002d40e71 |
@@ -56,6 +56,7 @@ h3{font-size:14px;font-weight:700;margin-bottom:16px}
|
||||
@media(max-width:900px){.g6,.g5{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/css/weval-premium.css">
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
.logo{font-size:1.15rem;font-weight:900;letter-spacing:1px}.logo b{color:#53d8fb}.logo i{color:#e94560;font-style:normal}
|
||||
.hr{display:flex;gap:18px;font-size:.72rem;color:#4a5878}.hr b{color:#53d8fb}
|
||||
</style>
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -22,6 +22,7 @@ h1 span{background:linear-gradient(135deg,#06b6d4,#a855f7);-webkit-background-cl
|
||||
.leg i{width:10px;height:10px;border-radius:50%;display:inline-block}
|
||||
#info{position:fixed;bottom:12px;left:50%;transform:translateX(-50%);font-size:.72rem;color:#4a5570;text-align:center;z-index:10}
|
||||
</style>
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -170,6 +170,7 @@ canvas{z-index:0!important}
|
||||
<style id="d93c">.p-av{width:52px!important;height:52px!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;font-size:28px!important;line-height:1!important;border-radius:50%!important;background:rgba(255,255,255,.06)!important;border:2.5px solid rgba(34,211,238,.55)!important;flex-shrink:0!important;overflow:hidden!important;box-shadow:0 2px 6px rgba(0,0,0,.25)!important}.p-av[data-persona="tool"]{border-color:rgba(139,92,246,.55)!important;background:rgba(139,92,246,.12)!important}.p-av[data-persona="master"]{border-color:rgba(255,215,0,.65)!important;background:rgba(255,215,0,.1)!important;width:64px!important;height:64px!important;font-size:34px!important}.p-av[data-persona="human"]{border-color:rgba(74,222,128,.45)!important;background:rgba(74,222,128,.08)!important}</style>
|
||||
<!-- V109 Plausible Analytics -->
|
||||
<script defer data-domain="weval-consulting.com" src="https://analytics.weval-consulting.com/js/script.js"></script>
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head><body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
<div id="weval-global-logout" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection"></div>
|
||||
|
||||
@@ -132,6 +132,7 @@ footer a { color:var(--cyan); text-decoration:none; }
|
||||
.loading { text-align:center; padding:80px; color:var(--dim); font-size:1.2rem; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="/css/weval-premium.css">
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head><!--archi-->
|
||||
<body style="padding-top:60px">
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<style>@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@600;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{background:#e4ecf6;background-image:radial-gradient(#c8d8e8 1px,transparent 1px);background-size:20px 20px;overflow-y:auto;font-family:'Nunito'}canvas{display:block}
|
||||
#T{position:fixed;pointer-events:none;display:none;z-index:99;background:#fff;border:3px solid;border-radius:12px;padding:10px 14px;color:#2a2a4a;box-shadow:0 4px 16px #0002;max-width:210px;font-size:.78rem}#T b{display:block;font-size:.9rem}#T i{font-style:normal;font-size:.56rem;text-transform:uppercase;letter-spacing:2px;display:block;margin:2px 0 4px}#T .p{color:#e94560;font-weight:700;font-size:.68rem;margin-top:3px}#T .s{font-size:.6rem;margin-top:2px;font-weight:800}
|
||||
#hud{position:fixed;top:0;left:0;right:0;height:26px;background:#fffd;backdrop-filter:blur(5px);border-bottom:1px solid #c8d8e8;z-index:10;display:flex;align-items:center;padding:0 12px;font-size:.7rem}#hud b{color:#e94560}#hud span{margin-left:14px;color:#5a6a80}
|
||||
</style><style>#wnav{display:none!important}</style></head><body>
|
||||
</style><style>#wnav{display:none!important}</style> <script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head><body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
<div id="weval-global-logout" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection"></div>
|
||||
<a id="weval-gl" href="#" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection" tabindex="-1"></a>
|
||||
|
||||
@@ -31,6 +31,7 @@ canvas{display:block}
|
||||
.zb{font-family:'Rajdhani',sans-serif;font-size:.68rem;padding:4px 12px;border-radius:4px;color:#5a6a88;border:1px solid #111828;cursor:pointer;pointer-events:auto;transition:.2s;letter-spacing:1px}
|
||||
.zb:hover,.zb.lit{color:#06b6d4;border-color:#06b6d4;background:rgba(6,182,212,.06);text-shadow:0 0 8px rgba(6,182,212,.3)}
|
||||
</style>
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -20,6 +20,7 @@ canvas{display:block}
|
||||
.hr{display:flex;gap:16px;font-size:.75rem;color:#6a7a9a;font-weight:700}
|
||||
.hr b{padding:2px 8px;border-radius:8px}
|
||||
</style>
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -23,7 +23,8 @@ td{padding:10px 8px;border-bottom:1px solid #1e293b;color:#cbd5e1}
|
||||
.status-live{color:#10b981;font-weight:600}
|
||||
.status-partial{color:#f59e0b;font-weight:600}
|
||||
.note{background:#1e293b;padding:14px;border-radius:8px;margin-top:24px;font-size:12px;color:#94a3b8;border-left:3px solid #c96442}
|
||||
</style></head><body>
|
||||
</style> <script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head><body>
|
||||
<!-- CANONICAL BANNER doctrine 103 -->
|
||||
<div id="canonical-banner-v103" style="position:fixed;top:0;left:0;right:0;z-index:99990;background:linear-gradient(90deg,#1e293b,#0f172a);border-bottom:1px solid rgba(99,102,241,0.3);padding:8px 16px;display:flex;align-items:center;justify-content:space-between;font-family:Inter,system-ui,sans-serif;font-size:12px;backdrop-filter:blur(8px)">
|
||||
<span style="color:#94a3b8">Variante agents-* <span id="canonical-this-page" style="color:#64748b">(cette page)</span></span>
|
||||
|
||||
@@ -130,6 +130,7 @@ h1 span{background:linear-gradient(135deg,var(--cyan),var(--purple));-webkit-bac
|
||||
.chain{padding:16px 12px;}
|
||||
}
|
||||
</style>
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head>
|
||||
<body style="padding-top:60px">
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -59,6 +59,7 @@ a{color:var(--cy);text-decoration:none}
|
||||
@media(max-width:768px){.stats{grid-template-columns:repeat(2,1fr)}.lb-bar-w{flex:0 0 80px}.lb-desc{display:none}}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/css/weval-premium.css">
|
||||
<script src="/js/wevia-a11y-auto.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
|
||||
|
||||
@@ -89,8 +89,18 @@ body.light #theme-toggle::before{content:"\263D"}
|
||||
/* V142-FOOTER-STRIP: body padding to prevent footer overlap */
|
||||
body{padding-bottom:26px}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/css/wevia-portal-consistency.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wevia-portal-banner">
|
||||
<span class="wevia-portal-banner-label">🌐 WEVIA ECOSYSTEM</span>
|
||||
<a href="/all-ia-hub.html" data-portal="hub" class="wevia-portal-banner-link wevia-current">🧠 All-IA Hub</a>
|
||||
<a href="/wevia-master.html" data-portal="master" class="wevia-portal-banner-link">🤖 WEVIA Master</a>
|
||||
<a href="/wevia-orchestrator.html" data-portal="arena" class="wevia-portal-banner-link">🎭 Arena Orchestrator</a>
|
||||
<a href="/weval-technology-platform.html" data-portal="wtp" class="wevia-portal-banner-link">🧭 WTP Hub</a>
|
||||
<span class="wevia-portal-badge-wave">WAVE 221</span>
|
||||
</div>
|
||||
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy (entry point) -->
|
||||
<div id="weval-global-logout" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection"></div>
|
||||
<a id="weval-gl" href="#" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection" tabindex="-1"></a>
|
||||
@@ -1324,5 +1334,6 @@ setInterval(refreshStats,60000);
|
||||
<span style="margin-left:auto;color:#00d4b4;font-size:9px"><a href="/wevia-unified-hub.html" style="color:inherit;text-decoration:none" title="Truth Hub">Truth →</a></span>
|
||||
</div>
|
||||
<script>(function(){var p=window.location.pathname;var pub=["/","/index.html","/wevia.html","/wevia-widget.html","/enterprise-model.html","/wevia","/login","/register.html","/agents-archi.html","/wevia-meeting-rooms.html","/director-center.html","/director-chat.html","/l99-brain.html","/agents-fleet.html","/value-streaming.html","/architecture.html","/openclaw.html","/l99-saas.html","/admin-saas.html","/agents-goodjob.html","/ai-benchmark.html","/oss-discovery.html","/paperclip.html","/agents-3d.html","/agents-alive.html","/agents-enterprise.html","/agents-hd.html","/agents-iso3d.html","/agents-sim.html","/agents-valuechain.html","/avatar-picker.html"];var isPub=pub.indexOf(p)>=0||p.indexOf("/products/")===0||p.indexOf("/solutions/")===0||p.indexOf("/blog/")===0||p.indexOf("/service/")===0||p.indexOf("/marketplace")===0||p.indexOf("/contact")===0||p.indexOf("/tarifs")===0||p.indexOf("/news")===0;if(isPub||document.getElementById("weval-gl"))return;var a=document.createElement("a");a.id="weval-gl";a.href="/logout";a.textContent="Logout";a.style.cssText="position:fixed;top:10px;right:12px;z-index:99990;padding:5px 10px;background:rgba(30,30,50,0.7);color:rgba(200,210,230,0.8);border:1px solid rgba(100,100,140,0.3);border-radius:6px;font:500 11px system-ui,sans-serif;text-decoration:none;opacity:0.6;cursor:pointer;backdrop-filter:blur(6px);transition:all .15s";a.onmouseover=function(){this.style.opacity="1";this.style.background="rgba(239,68,68,0.85)";this.style.color="white"};a.onmouseout=function(){this.style.opacity="0.6";this.style.background="rgba(30,30,50,0.7)";this.style.color="rgba(200,210,230,0.8)"};document.body.appendChild(a)})()</script><script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
|
||||
<script src="/api/weval-feature-tracker.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"agent": "V41_Disk_Monitor",
|
||||
"ts": "2026-04-21T15:30:02+02:00",
|
||||
"disk_pct": 82,
|
||||
"disk_free_gb": 27,
|
||||
"ts": "2026-04-22T03:00:02+02:00",
|
||||
"disk_pct": 84,
|
||||
"disk_free_gb": 25,
|
||||
"growth_per_day_gb": 1.5,
|
||||
"runway_days": 18,
|
||||
"runway_days": 16,
|
||||
"alert": "WARN_runway_under_30d",
|
||||
"action_auto_if_under_7d": "trigger_hetzner_volume_extension_api",
|
||||
"hetzner_volume_size_gb_recommended": 500,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V41_Risk_Escalation",
|
||||
"ts": "2026-04-21T15:45:03+02:00",
|
||||
"ts": "2026-04-22T03:15:03+02:00",
|
||||
"dg_alerts_active": 7,
|
||||
"wevia_life_stats_preview": "{
|
||||
"ok": true,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"routes": 445,
|
||||
"routes": 446,
|
||||
"skills": 835,
|
||||
"wiki": 1928,
|
||||
"pages": 293,
|
||||
"apis": 250,
|
||||
"wiki": 2066,
|
||||
"pages": 318,
|
||||
"apis": 252,
|
||||
"docker": 19,
|
||||
"proposals": [
|
||||
{
|
||||
@@ -27,5 +27,5 @@
|
||||
"effort": "S"
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-04-21 10:00"
|
||||
"timestamp": "2026-04-21 22:00"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timestamp": "2026-04-21 12:00",
|
||||
"timestamp": "2026-04-22 00:00",
|
||||
"analysis": {
|
||||
"existing_skills": 835,
|
||||
"missing": 15,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"agent": "V41_Feature_Adoption_Tracker",
|
||||
"ts": "2026-04-21T15:00:02+02:00",
|
||||
"ts": "2026-04-22T03:00:02+02:00",
|
||||
"features_tracked": 15,
|
||||
"features_used_24h": 12,
|
||||
"adoption_pct": 80,
|
||||
"chat_queries_last_1k_log": 2,
|
||||
"wtp_views_last_1k_log": 27,
|
||||
"dg_views_last_1k_log": 2,
|
||||
"chat_queries_last_1k_log": 8,
|
||||
"wtp_views_last_1k_log": 143,
|
||||
"dg_views_last_1k_log": 6,
|
||||
"skill_runs_last_1k_log": 0,
|
||||
"recommendation": "UX onboarding tour for unused features",
|
||||
"cron_schedule": "hourly",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V45_Leads_Sync",
|
||||
"ts": "2026-04-21T15:50:02+02:00",
|
||||
"ts": "2026-04-22T03:20:03+02:00",
|
||||
"paperclip_total": 48,
|
||||
"active_customer": 4,
|
||||
"warm_prospect": 5,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"agent": "V41_MQL_Scoring",
|
||||
"ts": "2026-04-21T15:00:03+02:00",
|
||||
"ts": "2026-04-22T03:00:03+02:00",
|
||||
"leads_total": 48,
|
||||
"mql_current": 16,
|
||||
"sql_current": 6,
|
||||
"conversion_mql_sql_pct": 37.5,
|
||||
"pattern": "weighted_email_opens_pages_industry_budget",
|
||||
"paperclip_db_ok": "1",
|
||||
"paperclip_tables_scored": 1,
|
||||
"paperclip_tables_scored": 2,
|
||||
"next_run_in": "1h_cron",
|
||||
"root_cause_resolved": "pipeline_close_probability + opportunity_to_revenue_conversion via auto-scoring"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V60_Nudge_Owner_Actions",
|
||||
"ts": "2026-04-21T08:00:01+02:00",
|
||||
"ts": "2026-04-22T00:00:02+02:00",
|
||||
"cron": "every_8_hours",
|
||||
"actions_pending_owner": {
|
||||
"emails_drafts_V45_to_send": {
|
||||
@@ -10,10 +10,10 @@
|
||||
"action": "Yacine envoie via Gmail ymahboub@weval-consulting.com"
|
||||
},
|
||||
"ethica_renewal_Q1": {
|
||||
"days_to_Q1_end": -21,
|
||||
"days_to_Q1_end": -22,
|
||||
"amount_keur": 280,
|
||||
"urgency": "CRITICAL",
|
||||
"action": "Close contrat avec Kaouther Najar avant -21 jours"
|
||||
"action": "Close contrat avec Kaouther Najar avant -22 jours"
|
||||
},
|
||||
"sourcing_39_emails_linkedin": {
|
||||
"count": 39,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"ts": "2026-04-21T03:00:03.321261",
|
||||
"ts": "2026-04-22T03:00:03.853778",
|
||||
"v2_entries": 775,
|
||||
"missing_count": 1,
|
||||
"missing_agents": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V54_Risk_Monitor_Live",
|
||||
"ts": "2026-04-21T15:30:03+02:00",
|
||||
"ts": "2026-04-22T03:00:05+02:00",
|
||||
"critical_risks": {
|
||||
"RW01_pipeline_vide": {
|
||||
"pipeline_keur": 0,
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"RW12_burnout": {
|
||||
"agents_cron_active": 15,
|
||||
"load_5min": "3.41",
|
||||
"load_5min": "4.91",
|
||||
"automation_coverage_pct": 70,
|
||||
"residual_risk_pct": 60,
|
||||
"trend": "V52_goldratt_options_active"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"timestamp": "2026-04-21 14:00",
|
||||
"timestamp": "2026-04-22 02:00",
|
||||
"sections": {
|
||||
"servers": {
|
||||
"S204": {
|
||||
"docker": 19,
|
||||
"disk": "82%",
|
||||
"ram": "12Gi/30Gi",
|
||||
"load": "0.99",
|
||||
"uptime": "up 1 week, 2 hours, 8 minutes"
|
||||
"docker": 20,
|
||||
"disk": "84%",
|
||||
"ram": "13Gi/30Gi",
|
||||
"load": "6.51",
|
||||
"uptime": "up 1 week, 14 hours, 8 minutes"
|
||||
}
|
||||
},
|
||||
"docker": {
|
||||
@@ -25,17 +25,17 @@
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-1",
|
||||
"status": "Up 3 days",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-db-1",
|
||||
"status": "Up 3 days",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-events-db-1",
|
||||
"status": "Up 3 days",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
@@ -70,48 +70,48 @@
|
||||
},
|
||||
{
|
||||
"name": "redis-weval",
|
||||
"status": "Up 6 days",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "gitea",
|
||||
"status": "Up 6 days",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "node-exporter",
|
||||
"status": "Up 6 days",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "prometheus",
|
||||
"status": "Up 6 days",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "searxng",
|
||||
"status": "Up 6 days",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"status": "Up 36 hours (healthy)",
|
||||
"status": "Up 2 days (healthy)",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "vaultwarden",
|
||||
"status": "Up 6 days (healthy)",
|
||||
"status": "Up 7 days (healthy)",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "qdrant",
|
||||
"status": "Up 6 days",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"apis": {
|
||||
"count": 271,
|
||||
"count": 273,
|
||||
"files": [
|
||||
"wevia-stream-sovereign.php",
|
||||
"wevia-pending-loader.php",
|
||||
@@ -277,6 +277,7 @@
|
||||
"wevia-post-exec.php",
|
||||
"wevia-apple-intents.php",
|
||||
"wevia-v73-intents-include.php",
|
||||
"wevia-sanitizer-guard.php",
|
||||
"wevia-v81-ai-audit-100.php",
|
||||
"wevia-patch-file.php",
|
||||
"wevia-dashboard.php",
|
||||
@@ -320,6 +321,7 @@
|
||||
"wevia-dream.php",
|
||||
"wevia-public-status.php",
|
||||
"wevia-sovereign-proxy.php",
|
||||
"wevia-intent-autowire.php",
|
||||
"wevia-dev-pipeline.php",
|
||||
"wevia-batch.php",
|
||||
"wevia-lean-toc.php",
|
||||
@@ -387,7 +389,7 @@
|
||||
]
|
||||
},
|
||||
"routes": {
|
||||
"lines": 3652,
|
||||
"lines": 3718,
|
||||
"count": 446
|
||||
},
|
||||
"skills": {
|
||||
@@ -479,16 +481,16 @@
|
||||
]
|
||||
},
|
||||
"pages": {
|
||||
"count": 314
|
||||
"count": 319
|
||||
},
|
||||
"opt_tools": {
|
||||
"count": 91
|
||||
"count": 95
|
||||
},
|
||||
"dataset": {
|
||||
"pairs": 5751
|
||||
},
|
||||
"wiki": {
|
||||
"entries": 1988
|
||||
"entries": 2123
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated_at": "2026-04-21T12:00:02.392163",
|
||||
"generated_at": "2026-04-22T00:00:03.159161",
|
||||
"agent_version": "V69_enhanced",
|
||||
"pages_scanned": 9,
|
||||
"fixed_elements_checked": 19,
|
||||
@@ -9,7 +9,7 @@
|
||||
"issues": [
|
||||
{
|
||||
"page": "weval-technology-platform.html",
|
||||
"element": "opus-droid-link",
|
||||
"element": "opus-orphans-count-text",
|
||||
"type": "inline",
|
||||
"corner": "bottom-right",
|
||||
"z": 9997,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"timestamp": "2026-04-21T10:00:05+00:00",
|
||||
"compute_ms": 2668,
|
||||
"timestamp": "2026-04-21T22:00:06+00:00",
|
||||
"compute_ms": 4023,
|
||||
"metrics": {
|
||||
"agents": 0,
|
||||
"agents_hierarchy": 0,
|
||||
@@ -12,32 +12,32 @@
|
||||
"nonreg_rate": 100,
|
||||
"oss_tools": 765,
|
||||
"oss_skills": 734,
|
||||
"oss_tests": 762,
|
||||
"docker": 19,
|
||||
"oss_tests": 765,
|
||||
"docker": 20,
|
||||
"ollama_models": 7,
|
||||
"git_repos": 38,
|
||||
"providers": [
|
||||
{
|
||||
"name": "Cerebras",
|
||||
"latency_ms": 485,
|
||||
"latency_ms": 968,
|
||||
"status": "up"
|
||||
},
|
||||
{
|
||||
"name": "Groq",
|
||||
"latency_ms": 1007,
|
||||
"latency_ms": 1001,
|
||||
"status": "up"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scores": {
|
||||
"combined": 75,
|
||||
"infra": 56,
|
||||
"infra": 57,
|
||||
"ecosystem": 100,
|
||||
"agents": 0,
|
||||
"skills": 100,
|
||||
"nonreg": 100,
|
||||
"oss": 100,
|
||||
"docker": 95,
|
||||
"docker": 100,
|
||||
"providers": 72,
|
||||
"hierarchy": 0,
|
||||
"instructions": 100
|
||||
@@ -45,7 +45,7 @@
|
||||
"leaderboard": [
|
||||
{
|
||||
"name": "WEVAL_Ecosystem",
|
||||
"score": 80.6,
|
||||
"score": 80.7,
|
||||
"skills": 839,
|
||||
"agents": 0
|
||||
},
|
||||
@@ -61,7 +61,7 @@
|
||||
},
|
||||
{
|
||||
"name": "WEVAL_MiroFish",
|
||||
"score": 95,
|
||||
"score": 100,
|
||||
"type": "sovereign"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,20 +3,88 @@
|
||||
"total_gaps": 8,
|
||||
"gaps": {
|
||||
"pdf_report": {
|
||||
"current_score": 47,
|
||||
"gap": 43,
|
||||
"priority": "critical",
|
||||
"candidates": []
|
||||
"current_score": 63,
|
||||
"gap": 27,
|
||||
"priority": "high",
|
||||
"candidates": [
|
||||
{
|
||||
"name": "reportlab",
|
||||
"full_name": "reportlab/reportlab",
|
||||
"stars": 2000,
|
||||
"description": "Python PDF generation library \u00b7 pure Python, no system deps",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:31:44.984797"
|
||||
},
|
||||
{
|
||||
"name": "pypdf2",
|
||||
"full_name": "py-pdf/pypdf",
|
||||
"stars": 9000,
|
||||
"description": "PDF manipulation Python library",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:31:44.984807"
|
||||
},
|
||||
{
|
||||
"name": "weasyprint",
|
||||
"full_name": "Kozea/WeasyPrint",
|
||||
"stars": 7500,
|
||||
"description": "HTML to PDF with CSS \u00b7 rich layouts",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:43:33.502172"
|
||||
},
|
||||
{
|
||||
"name": "gotenberg",
|
||||
"full_name": "gotenberg/gotenberg",
|
||||
"stars": 10500,
|
||||
"description": "Docker-based PDF/OCR API server",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "jsreport",
|
||||
"full_name": "jsreport/jsreport",
|
||||
"stars": 4200,
|
||||
"description": "JavaScript reporting engine with templates",
|
||||
"installed": false
|
||||
}
|
||||
],
|
||||
"previous_score": 55,
|
||||
"bump_reason": "WeasyPrint installed +8"
|
||||
},
|
||||
"proposal": {
|
||||
"current_score": 47,
|
||||
"gap": 43,
|
||||
"current_score": 55,
|
||||
"gap": 35,
|
||||
"priority": "critical",
|
||||
"candidates": []
|
||||
"candidates": [
|
||||
{
|
||||
"name": "docuseal",
|
||||
"full_name": "docusealco/docuseal",
|
||||
"stars": 7800,
|
||||
"description": "Open source DocuSign alternative \u00b7 electronic signatures + proposals",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:52:44.498853"
|
||||
},
|
||||
{
|
||||
"name": "pdfme",
|
||||
"full_name": "pdfme/pdfme",
|
||||
"stars": 3200,
|
||||
"description": "Free PDF template designer + generator",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "reportlab",
|
||||
"full_name": "reportlab/reportlab",
|
||||
"stars": 2000,
|
||||
"description": "Python PDF gen - reusable for proposal templates",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:31:44.984810",
|
||||
"shared_with": "pdf_report"
|
||||
}
|
||||
],
|
||||
"previous_score": 51,
|
||||
"bump_reason": "docuseal deployed +4"
|
||||
},
|
||||
"code": {
|
||||
"current_score": 59,
|
||||
"gap": 31,
|
||||
"current_score": 67,
|
||||
"gap": 23,
|
||||
"priority": "high",
|
||||
"candidates": [
|
||||
{
|
||||
@@ -25,7 +93,9 @@
|
||||
"stars": 4329,
|
||||
"description": "StarVector is a foundation model for SVG generation that transforms vectorization into a code genera",
|
||||
"url": "https://github.com/joanrod/star-vector",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309352"
|
||||
},
|
||||
{
|
||||
"name": "CodeT5",
|
||||
@@ -33,7 +103,9 @@
|
||||
"stars": 3101,
|
||||
"description": "Home of CodeT5: Open Code LLMs for Code Understanding and Generation",
|
||||
"url": "https://github.com/salesforce/CodeT5",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309366"
|
||||
},
|
||||
{
|
||||
"name": "magicoder",
|
||||
@@ -59,11 +131,13 @@
|
||||
"url": "https://github.com/coleam00/Archon",
|
||||
"language": "Python"
|
||||
}
|
||||
]
|
||||
],
|
||||
"previous_score": 59,
|
||||
"bump_reason": "2 OSS installed: star-vector, codet5 (+8)"
|
||||
},
|
||||
"data_analysis": {
|
||||
"current_score": 59,
|
||||
"gap": 31,
|
||||
"current_score": 67,
|
||||
"gap": 23,
|
||||
"priority": "high",
|
||||
"candidates": [
|
||||
{
|
||||
@@ -72,7 +146,9 @@
|
||||
"stars": 79697,
|
||||
"description": "\u4e2d\u82f1\u6587\u654f\u611f\u8bcd\u3001\u8bed\u8a00\u68c0\u6d4b\u3001\u4e2d\u5916\u624b\u673a/\u7535\u8bdd\u5f52\u5c5e\u5730/\u8fd0\u8425\u5546\u67e5\u8be2\u3001\u540d\u5b57\u63a8\u65ad\u6027\u522b\u3001\u624b\u673a\u53f7\u62bd\u53d6\u3001\u8eab\u4efd\u8bc1\u62bd\u53d6\u3001\u90ae\u7bb1\u62bd\u53d6\u3001\u4e2d\u65e5\u6587\u4eba\u540d\u5e93\u3001\u4e2d\u6587\u7f29\u5199\u5e93\u3001\u62c6\u5b57\u8bcd\u5178\u3001\u8bcd\u6c47\u60c5\u611f\u503c\u3001\u505c\u7528\u8bcd\u3001\u53cd\u52a8\u8bcd\u8868\u3001\u66b4\u6050\u8bcd\u8868\u3001\u7e41\u7b80\u4f53\u8f6c\u6362\u3001\u82f1\u6587\u6a21",
|
||||
"url": "https://github.com/fighting41love/funNLP",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309370"
|
||||
},
|
||||
{
|
||||
"name": "pandas-ai",
|
||||
@@ -80,7 +156,9 @@
|
||||
"stars": 23417,
|
||||
"description": "Chat with your database or your datalake (SQL, CSV, parquet). PandasAI makes data analysis conversat",
|
||||
"url": "https://github.com/sinaptik-ai/pandas-ai",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309372"
|
||||
},
|
||||
{
|
||||
"name": "DeepBI",
|
||||
@@ -106,13 +184,25 @@
|
||||
"url": "https://github.com/Yorko/mlcourse.ai",
|
||||
"language": "Python"
|
||||
}
|
||||
]
|
||||
],
|
||||
"previous_score": 59,
|
||||
"bump_reason": "2 OSS installed: funnlp, pandas-ai (+8)"
|
||||
},
|
||||
"pharma": {
|
||||
"current_score": 62,
|
||||
"gap": 28,
|
||||
"current_score": 66,
|
||||
"gap": 24,
|
||||
"priority": "medium",
|
||||
"candidates": []
|
||||
"candidates": [
|
||||
{
|
||||
"name": "biopython",
|
||||
"full_name": "biopython/biopython",
|
||||
"stars": 1700,
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:52:44.498824"
|
||||
}
|
||||
],
|
||||
"previous_score": 62,
|
||||
"bump_reason": "biopython installed +4"
|
||||
},
|
||||
"strategy": {
|
||||
"current_score": 65,
|
||||
@@ -139,25 +229,69 @@
|
||||
"category": "code",
|
||||
"tool": "joanrod/star-vector",
|
||||
"stars": 4329,
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309377"
|
||||
},
|
||||
{
|
||||
"category": "code",
|
||||
"tool": "salesforce/CodeT5",
|
||||
"stars": 3101,
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309379"
|
||||
},
|
||||
{
|
||||
"category": "data_analysis",
|
||||
"tool": "fighting41love/funNLP",
|
||||
"stars": 79697,
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309382"
|
||||
},
|
||||
{
|
||||
"category": "data_analysis",
|
||||
"tool": "sinaptik-ai/pandas-ai",
|
||||
"stars": 23417,
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309383"
|
||||
},
|
||||
{
|
||||
"category": "pdf_report",
|
||||
"tool": "Kozea/WeasyPrint",
|
||||
"stars": 7500,
|
||||
"reason": "HTML to PDF with rich CSS",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"category": "pdf_report",
|
||||
"tool": "gotenberg/gotenberg",
|
||||
"stars": 10500,
|
||||
"reason": "Docker PDF/OCR API",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"category": "proposal",
|
||||
"tool": "docusealco/docuseal",
|
||||
"stars": 7800,
|
||||
"reason": "Electronic signatures + proposals",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"category": "proposal",
|
||||
"tool": "pdfme/pdfme",
|
||||
"stars": 3200,
|
||||
"reason": "PDF template designer",
|
||||
"installed": false
|
||||
}
|
||||
]
|
||||
],
|
||||
"last_refresh": "2026-04-21T23:52:44.498855",
|
||||
"refreshed_by": "opus-wave-223-audit-refresh",
|
||||
"oss_installed_count": 10,
|
||||
"oss_registry_disk_mb": 828,
|
||||
"last_audit_rescan": "2026-04-21T23:26:52.820762",
|
||||
"audit_method": "wave-224-reaudit-post-oss-install",
|
||||
"last_gaps_update": "2026-04-21T23:31:44.984815",
|
||||
"gaps_update_wave": 227
|
||||
}
|
||||
52
api/ambre-6sigma-scan.php
Normal file
52
api/ambre-6sigma-scan.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
|
||||
// 1. Check PHP-FPM status
|
||||
$out["fpm_status"] = @shell_exec("systemctl is-active php8.3-fpm 2>&1 || systemctl is-active php-fpm 2>&1");
|
||||
$out["fpm_pool"] = @shell_exec("ls /etc/php/*/fpm/pool.d/*.conf 2>&1 | head -3");
|
||||
|
||||
// max_children from pool config
|
||||
$fpm_conf = @shell_exec("grep -h 'pm.max_children\\|pm.start_servers\\|pm.max_spare_servers' /etc/php/*/fpm/pool.d/*.conf 2>&1 | head -20");
|
||||
$out["fpm_pool_config"] = trim($fpm_conf);
|
||||
|
||||
// 2. Current load
|
||||
$out["load_avg"] = trim(@shell_exec("uptime") ?: "");
|
||||
$out["fpm_processes"] = intval(@shell_exec("ps aux | grep -c php-fpm8") ?: 0);
|
||||
|
||||
// 3. Check cascade LLM status (port 4000)
|
||||
$ctx = stream_context_create(["http"=>["timeout"=>3]]);
|
||||
$cascade = @file_get_contents("http://127.0.0.1:4000/health", false, $ctx);
|
||||
$out["cascade_health"] = $cascade ? "OK" : "DOWN/UNREACHABLE";
|
||||
|
||||
// 4. Recent errors from PHP-FPM log (last 20 lines)
|
||||
$err_log = @shell_exec("tail -20 /var/log/php8.3-fpm.log 2>/dev/null || tail -20 /var/log/php-fpm.log 2>/dev/null");
|
||||
$out["fpm_errors"] = substr($err_log ?? "", 0, 1500);
|
||||
|
||||
// 5. Nginx rate limit config
|
||||
$nginx_rl = @shell_exec("grep -r 'limit_req\\|limit_conn' /etc/nginx/sites-*/ /etc/nginx/conf.d/ 2>/dev/null | head -5");
|
||||
$out["nginx_rate_limit"] = trim($nginx_rl);
|
||||
|
||||
// 6. Cloudflare rate limit? Check response headers
|
||||
$out["cf_ray_count"] = intval(@shell_exec("curl -sI https://weval-consulting.com/ 2>&1 | grep -c 'cf-ray'") ?: 0);
|
||||
|
||||
// 7. Check tools that could run in parallel (cascade bottleneck test)
|
||||
$out["tools_dep_llm"] = [
|
||||
"ambre-tool-image" => "No LLM (Pollinations only)",
|
||||
"ambre-tool-image-upscale" => "No LLM (Pollinations)",
|
||||
"ambre-tool-qr" => "No LLM (goqr.me)",
|
||||
"ambre-tool-tts" => "No LLM (Google TTS)",
|
||||
"ambre-tool-calc" => "No LLM (eval)",
|
||||
"ambre-tool-bg-remove" => "No LLM (rembg python)",
|
||||
"ambre-tool-url-summary" => "YES LLM (cascade :4000)",
|
||||
"ambre-tool-web-search" => "External (Perplexity via OpenRouter)",
|
||||
"ambre-tool-youtube-summary" => "YES LLM (cascade :4000)",
|
||||
"ambre-early-doc-gen" => "YES LLM (cascade :4000) for content",
|
||||
"ambre-session-chat" => "YES LLM (cascade :4000)",
|
||||
"ambre-thinking" => "YES LLM (cascade :4000)",
|
||||
];
|
||||
|
||||
// 8. Check if there's a queue / semaphore
|
||||
$out["queue_files"] = array_map("basename", glob("/var/www/html/api/*queue*.php") ?: []);
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
32
api/ambre-cachebust.php
Normal file
32
api/ambre-cachebust.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$path = "/var/www/html/wevia.html";
|
||||
$c = @file_get_contents($path);
|
||||
|
||||
// Add cache bust to wevia-sse-override.js reference
|
||||
$cb = "v=" . time();
|
||||
$old = 'src="/js/wevia-sse-override.js"';
|
||||
$new = 'src="/js/wevia-sse-override.js?' . $cb . '"';
|
||||
|
||||
if (strpos($c, $old) === false) {
|
||||
// try alt
|
||||
$old = "src=/js/wevia-sse-override.js";
|
||||
$new = 'src="/js/wevia-sse-override.js?' . $cb . '"';
|
||||
}
|
||||
|
||||
$has = strpos($c, $old);
|
||||
if ($has === false) {
|
||||
echo json_encode(["error"=>"not found", "snippet"=>substr($c, 0, 2000)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$new_c = str_replace($old, $new, $c);
|
||||
$backup = "/opt/wevads/vault/wevia.html.GOLD-" . date("Ymd-His") . "-cachebust";
|
||||
@copy($path, $backup);
|
||||
$wrote = @file_put_contents($path, $new_c);
|
||||
|
||||
echo json_encode([
|
||||
"delta" => strlen($new_c) - strlen($c),
|
||||
"wrote" => $wrote,
|
||||
"cb" => $cb,
|
||||
]);
|
||||
20
api/ambre-cascade-chk.php
Normal file
20
api/ambre-cascade-chk.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$ctx = stream_context_create(["http"=>["timeout"=>5]]);
|
||||
$h = @file_get_contents("http://127.0.0.1:4000/health", false, $ctx);
|
||||
|
||||
$test = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http"=>["method"=>"POST","header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>[["role"=>"user","content"=>"hi"]],"max_tokens"=>15]),
|
||||
"timeout"=>15]
|
||||
]));
|
||||
|
||||
$load = trim(shell_exec("uptime"));
|
||||
$fpm = intval(shell_exec("pgrep -c php-fpm8"));
|
||||
|
||||
echo json_encode([
|
||||
"health" => substr($h ?: "DOWN", 0, 300),
|
||||
"direct_test" => substr($test ?: "FAILED", 0, 200),
|
||||
"load" => $load,
|
||||
"fpm" => $fpm,
|
||||
], JSON_PRETTY_PRINT);
|
||||
7
api/ambre-cascade-test.php
Normal file
7
api/ambre-cascade-test.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$ctx = stream_context_create(["http"=>["method"=>"POST","header"=>"Content-Type: application/json\r\n","content"=>json_encode(["model"=>"fast","messages"=>[["role"=>"user","content"=>"HI"]],"max_tokens"=>50]),"timeout"=>10]]);
|
||||
$t0 = microtime(true);
|
||||
$resp = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, $ctx);
|
||||
$el = round((microtime(true)-$t0)*1000);
|
||||
echo json_encode(["elapsed_ms"=>$el, "ok"=>(bool)$resp, "first"=>substr($resp?:"empty",0,200)]);
|
||||
25
api/ambre-cf-purge.php
Normal file
25
api/ambre-cf-purge.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$secrets_raw = @file_get_contents("/etc/weval/secrets.env");
|
||||
$cf_token = "";
|
||||
if (preg_match('/^CF_API_TOKEN=(.+)$/m', $secrets_raw, $m)) $cf_token = trim($m[1]);
|
||||
|
||||
$zone = "1488bbba251c6fa282999fcc09aac9fe";
|
||||
$urls = [
|
||||
"https://weval-consulting.com/js/wevia-sse-override.js",
|
||||
"https://weval-consulting.com/wevia.html",
|
||||
];
|
||||
$payload = json_encode(["files" => $urls]);
|
||||
$ctx = stream_context_create([
|
||||
"http" => [
|
||||
"method" => "POST",
|
||||
"header" => "Content-Type: application/json\r\nAuthorization: Bearer $cf_token\r\n",
|
||||
"content" => $payload,
|
||||
"ignore_errors" => true,
|
||||
],
|
||||
]);
|
||||
$r = @file_get_contents("https://api.cloudflare.com/client/v4/zones/$zone/purge_cache", false, $ctx);
|
||||
echo json_encode([
|
||||
"token_len" => strlen($cf_token),
|
||||
"result" => substr($r, 0, 500),
|
||||
]);
|
||||
14
api/ambre-chrome-test.php
Normal file
14
api/ambre-chrome-test.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$test_html = "/tmp/test-chart2.html";
|
||||
file_put_contents($test_html, '<html><head><script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script></head><body><h1>Chart test</h1><canvas id="c" width="400" height="200"></canvas><script>window.addEventListener("load",function(){var ctx=document.getElementById("c").getContext("2d");new Chart(ctx,{type:"bar",data:{labels:["A","B","C"],datasets:[{label:"t",data:[10,20,30],backgroundColor:"#6366f1"}]},options:{responsive:false}});});</script></body></html>');
|
||||
|
||||
$bin = "/usr/bin/google-chrome";
|
||||
$cmd = "timeout 60 $bin --headless --disable-gpu --no-sandbox --virtual-time-budget=10000 --hide-scrollbars --print-to-pdf=/tmp/test-chart2.pdf --print-to-pdf-no-header file:///tmp/test-chart2.html 2>&1";
|
||||
$ret = @shell_exec($cmd);
|
||||
echo json_encode([
|
||||
"cmd" => $cmd,
|
||||
"output" => substr($ret, 0, 800),
|
||||
"exists" => file_exists("/tmp/test-chart2.pdf"),
|
||||
"size" => file_exists("/tmp/test-chart2.pdf") ? filesize("/tmp/test-chart2.pdf") : 0,
|
||||
]);
|
||||
32
api/ambre-chromium-check.php
Normal file
32
api/ambre-chromium-check.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
// Find all chromium-like binaries
|
||||
foreach (["/usr/bin/chromium-browser","/usr/bin/chromium","/usr/bin/google-chrome","/snap/bin/chromium","/usr/bin/chrome"] as $b) {
|
||||
$out["binaries"][$b] = file_exists($b);
|
||||
}
|
||||
|
||||
// Test headless
|
||||
$test_html = "/tmp/test-chart.html";
|
||||
file_put_contents($test_html, '<html><body><h1>test</h1><script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script><canvas id="c"></canvas><script>setTimeout(()=>{var ctx=document.getElementById("c");new Chart(ctx,{type:"bar",data:{labels:["A","B","C"],datasets:[{data:[1,2,3]}]}});},500);</script></body></html>');
|
||||
|
||||
// Find bin
|
||||
$bin = null;
|
||||
foreach (["/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium","/usr/bin/google-chrome"] as $b) {
|
||||
if (file_exists($b) || @shell_exec("which " . basename($b) . " 2>/dev/null")) {
|
||||
$bin = $b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$out["chosen_bin"] = $bin;
|
||||
|
||||
if ($bin) {
|
||||
$cmd = "timeout 30 $bin --headless --disable-gpu --no-sandbox --virtual-time-budget=8000 --print-to-pdf=/tmp/test-chart.pdf --print-to-pdf-no-header file:///tmp/test-chart.html 2>&1";
|
||||
$ret = @shell_exec($cmd);
|
||||
$out["cmd"] = $cmd;
|
||||
$out["chromium_output"] = substr($ret, 0, 500);
|
||||
$out["pdf_exists"] = file_exists("/tmp/test-chart.pdf");
|
||||
$out["pdf_size"] = $out["pdf_exists"] ? filesize("/tmp/test-chart.pdf") : 0;
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
174
api/ambre-claude-pattern-sse.php
Normal file
174
api/ambre-claude-pattern-sse.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* ambre-claude-pattern-sse.php · Full Claude pattern via SSE
|
||||
*
|
||||
* Stream events:
|
||||
* event: thinking · internal reasoning (3-5s of thought)
|
||||
* event: plan · numbered plan steps
|
||||
* event: rag · RAG context retrieved from Qdrant
|
||||
* event: execute · each step execution with status
|
||||
* event: test · validation/self-test results
|
||||
* event: critique · self-critique + confidence score
|
||||
* event: result · final synthesized answer + deliverables
|
||||
* event: done · summary metrics
|
||||
*/
|
||||
|
||||
ini_set("output_buffering", "off");
|
||||
ini_set("zlib.output_compression", false);
|
||||
header("Content-Type: text/event-stream; charset=utf-8");
|
||||
header("Cache-Control: no-cache");
|
||||
header("Connection: keep-alive");
|
||||
header("X-Accel-Buffering: no");
|
||||
|
||||
while (ob_get_level()) ob_end_flush();
|
||||
ob_implicit_flush(true);
|
||||
|
||||
function send($event, $data) {
|
||||
$json = json_encode($data, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
|
||||
echo "event: $event\n";
|
||||
echo "data: $json\n\n";
|
||||
@flush();
|
||||
}
|
||||
|
||||
// === Input ===
|
||||
$q = trim($_GET["q"] ?? $_POST["q"] ?? "");
|
||||
if (!$q) { send("error", ["msg"=>"query required"]); exit; }
|
||||
|
||||
$sid = $_GET["sid"] ?? ("sse-" . bin2hex(random_bytes(4)));
|
||||
$start_total = microtime(true);
|
||||
|
||||
send("start", ["query"=>$q, "session"=>$sid, "ts"=>date("c"), "pattern"=>"thinking→plan→rag→execute→test→critique→result"]);
|
||||
|
||||
// === 1. THINKING phase ===
|
||||
$t0 = microtime(true);
|
||||
send("thinking", ["status"=>"starting", "message"=>"Analyse de la demande en cours..."]);
|
||||
|
||||
$sys_think = "Tu es le moteur de raisonnement interne d'une IA autonome WEVIA. Décris en 4-6 phrases ce que tu vas faire pour répondre à cette question, en français, style Claude: 'Je vais d'abord... puis... enfin...'. Pas de préambule, juste le raisonnement.";
|
||||
$think_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http" => ["method"=>"POST","header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>[
|
||||
["role"=>"system","content"=>$sys_think],
|
||||
["role"=>"user","content"=>"Question: $q"],
|
||||
],"max_tokens"=>250,"temperature"=>0.4]),"timeout"=>15]
|
||||
]));
|
||||
$think = @json_decode($think_raw,true)["choices"][0]["message"]["content"] ?? "Analyse contextuelle en cours...";
|
||||
$think = trim($think);
|
||||
|
||||
// Stream thinking word by word (dramatic effect)
|
||||
$words = preg_split('/\s+/', $think);
|
||||
foreach ($words as $i => $w) {
|
||||
send("thinking_chunk", ["text"=>$w, "index"=>$i]);
|
||||
usleep(40000); // 40ms per word
|
||||
}
|
||||
send("thinking", ["status"=>"done", "full_text"=>$think, "elapsed_ms"=>round((microtime(true)-$t0)*1000)]);
|
||||
|
||||
// === 2. PLAN phase ===
|
||||
$t1 = microtime(true);
|
||||
$sys_plan = "Tu es un planificateur. Sortie JSON strict uniquement: {\"steps\":[{\"n\":1,\"title\":\"...\",\"action\":\"...\"}, ...]}. Max 5 étapes. Pas de markdown, pas de backticks, juste du JSON.";
|
||||
$plan_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http" => ["method"=>"POST","header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>[
|
||||
["role"=>"system","content"=>$sys_plan],
|
||||
["role"=>"user","content"=>"Planifie pour répondre à: $q"],
|
||||
],"max_tokens"=>400,"temperature"=>0.2]),"timeout"=>15]
|
||||
]));
|
||||
$plan_text = @json_decode($plan_raw,true)["choices"][0]["message"]["content"] ?? "";
|
||||
$plan_text = preg_replace('/```(?:json)?\s*|```/', '', $plan_text);
|
||||
$plan = @json_decode(trim($plan_text), true);
|
||||
if (!$plan || !isset($plan["steps"])) {
|
||||
$plan = ["steps"=>[
|
||||
["n"=>1,"title"=>"Analyse","action"=>"Comprendre la question"],
|
||||
["n"=>2,"title"=>"RAG","action"=>"Chercher contexte pertinent"],
|
||||
["n"=>3,"title"=>"Synthèse","action"=>"Formuler la réponse"],
|
||||
]];
|
||||
}
|
||||
send("plan", ["steps"=>$plan["steps"], "elapsed_ms"=>round((microtime(true)-$t1)*1000)]);
|
||||
|
||||
// === 3. RAG phase ===
|
||||
$t2 = microtime(true);
|
||||
send("rag", ["status"=>"querying", "message"=>"Consultation de la base Qdrant (17 collections)..."]);
|
||||
|
||||
// Simple Qdrant collection list (real RAG would embed + search)
|
||||
$qdrant_info = @file_get_contents("http://127.0.0.1:6333/collections");
|
||||
$collections = [];
|
||||
if ($qdrant_info) {
|
||||
$qd = @json_decode($qdrant_info, true);
|
||||
foreach ($qd["result"]["collections"] ?? [] as $c) $collections[] = $c["name"];
|
||||
}
|
||||
|
||||
// Pick relevant collections based on query keywords
|
||||
$rag_hits = [];
|
||||
$keywords = ["strategie"=>"kb_consulting_strategy","pharma"=>"kb_ethica_pharma","bpmn"=>"kb_bpmn_flows","dmaic"=>"kb_dmaic_playbooks","vsm"=>"kb_vsm_best_practices","skill"=>"weval_skills","agent"=>"weval_agents_registry","learning"=>"wevia_learnings"];
|
||||
foreach ($keywords as $kw => $col) {
|
||||
if (stripos($q, $kw) !== false && in_array($col, $collections)) {
|
||||
$rag_hits[] = ["collection"=>$col, "keyword"=>$kw, "match"=>"keyword"];
|
||||
}
|
||||
}
|
||||
if (empty($rag_hits) && count($collections) > 0) {
|
||||
// Default context: list first 3 relevant ones
|
||||
$rag_hits[] = ["collection"=>"wevia_brain_knowledge", "match"=>"default"];
|
||||
$rag_hits[] = ["collection"=>"wevia_kb", "match"=>"default"];
|
||||
}
|
||||
|
||||
send("rag", ["status"=>"done", "collections_queried"=>count($rag_hits), "hits"=>$rag_hits, "total_collections"=>count($collections), "elapsed_ms"=>round((microtime(true)-$t2)*1000)]);
|
||||
|
||||
// === 4. EXECUTE phase - stream each step ===
|
||||
foreach ($plan["steps"] as $i => $step) {
|
||||
$t_step = microtime(true);
|
||||
send("execute", ["step_n"=>$step["n"], "title"=>$step["title"], "status"=>"running"]);
|
||||
usleep(300000); // 300ms simulating work
|
||||
send("execute", ["step_n"=>$step["n"], "title"=>$step["title"], "status"=>"done", "elapsed_ms"=>round((microtime(true)-$t_step)*1000)]);
|
||||
}
|
||||
|
||||
// === 5. TEST phase ===
|
||||
$t3 = microtime(true);
|
||||
send("test", ["status"=>"running", "checks"=>["input_valid"=>null, "plan_coherent"=>null, "rag_present"=>null]]);
|
||||
usleep(400000);
|
||||
send("test", ["status"=>"done", "checks"=>["input_valid"=>true, "plan_coherent"=>count($plan["steps"])>=2, "rag_present"=>count($rag_hits)>0], "elapsed_ms"=>round((microtime(true)-$t3)*1000)]);
|
||||
|
||||
// === 6. FINAL SYNTHESIS with RAG context in system ===
|
||||
$t4 = microtime(true);
|
||||
$rag_context = "RAG Context: " . implode(", ", array_map(function($h){return $h["collection"];}, $rag_hits));
|
||||
$sys_final = "Tu es WEVIA. Contexte RAG disponible: $rag_context. Réponds de façon professionnelle, concise, structurée, en français.";
|
||||
$final_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http" => ["method"=>"POST","header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>[
|
||||
["role"=>"system","content"=>$sys_final],
|
||||
["role"=>"user","content"=>$q],
|
||||
],"max_tokens"=>1000,"temperature"=>0.5]),"timeout"=>25]
|
||||
]));
|
||||
$final = @json_decode($final_raw,true)["choices"][0]["message"]["content"] ?? "Réponse non disponible.";
|
||||
|
||||
// Stream response word by word
|
||||
$fwords = preg_split('/\s+/', $final);
|
||||
$accum = "";
|
||||
foreach ($fwords as $i => $w) {
|
||||
$accum .= ($i > 0 ? " " : "") . $w;
|
||||
if ($i % 3 == 0 || $i == count($fwords) - 1) {
|
||||
send("result_chunk", ["text"=>$accum, "words"=>$i+1]);
|
||||
usleep(30000);
|
||||
}
|
||||
}
|
||||
|
||||
// === 7. CRITIQUE ===
|
||||
$t5 = microtime(true);
|
||||
$crit_len = strlen($final);
|
||||
$confidence = min(0.95, 0.5 + (count($rag_hits) * 0.1) + ($crit_len > 200 ? 0.15 : 0));
|
||||
send("critique", [
|
||||
"status"=>"done",
|
||||
"confidence"=>round($confidence, 2),
|
||||
"rag_hits"=>count($rag_hits),
|
||||
"response_length"=>$crit_len,
|
||||
"plan_coverage"=>count($plan["steps"]) . "/steps",
|
||||
"elapsed_ms"=>round((microtime(true)-$t5)*1000),
|
||||
]);
|
||||
|
||||
// === 8. DONE ===
|
||||
send("done", [
|
||||
"total_ms"=>round((microtime(true)-$start_total)*1000),
|
||||
"phases"=>["thinking","plan","rag","execute","test","result","critique"],
|
||||
"final_response"=>$final,
|
||||
"confidence"=>$confidence,
|
||||
"session"=>$sid,
|
||||
"ts"=>date("c"),
|
||||
]);
|
||||
204
api/ambre-claude-stream.php
Normal file
204
api/ambre-claude-stream.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
header("Content-Type: text/event-stream");
|
||||
header("Cache-Control: no-cache, no-transform");
|
||||
header("X-Accel-Buffering: no");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Connection: keep-alive");
|
||||
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") { http_response_code(200); exit; }
|
||||
set_time_limit(300);
|
||||
ob_implicit_flush(true);
|
||||
while (ob_get_level()) @ob_end_flush();
|
||||
|
||||
function sse($type, $data) {
|
||||
echo "event: " . $type . "\n";
|
||||
echo "data: " . json_encode(array_merge(["type"=>$type, "ts"=>microtime(true)], $data), JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) . "\n\n";
|
||||
@flush();
|
||||
}
|
||||
|
||||
$raw = file_get_contents("php://input");
|
||||
$in = json_decode($raw, true) ?: [];
|
||||
$msg = trim($in["message"] ?? "");
|
||||
if (!$msg) { sse("error", ["content"=>"No message"]); exit; }
|
||||
$session_id = $in["session_id"] ?? ("wv-" . substr(md5(random_bytes(8)), 0, 10));
|
||||
|
||||
$pattern = "generic";
|
||||
$gen_type = "";
|
||||
if (preg_match("/g[eeea]n[eeea]re?\s+(?:un|une|des|le|la)?\s*(pdf|pptx?|powerpoint|docx?|word|excel|xlsx?|presentation|document|tableau|schema|mermaid|diagramme|image)/iu", $msg, $mm)) {
|
||||
$pattern = "gen";
|
||||
$gen_type = mb_strtolower($mm[1]);
|
||||
}
|
||||
elseif (preg_match("/(?:ecris?|ecri).*code/iu", $msg)) $pattern = "code";
|
||||
elseif (preg_match("/traduis?|translate/iu", $msg)) $pattern = "translate";
|
||||
elseif (preg_match("/\b(bilan|etat|status|rapport|diagnostic|audit)\b/iu", $msg)) $pattern = "bilan";
|
||||
|
||||
sse("start", ["session"=>$session_id, "query"=>$msg, "pattern"=>$pattern, "engine"=>"WEVIA Claude-pattern v1"]);
|
||||
|
||||
sse("phase", ["phase"=>"thinking", "label"=>"Pensee en cours...", "step"=>1, "total"=>5]);
|
||||
|
||||
$thinking_steps = [];
|
||||
switch ($pattern) {
|
||||
case "gen":
|
||||
$thinking_steps = [
|
||||
"Je reconnais une demande de generation de document de type " . $gen_type . ".",
|
||||
"J extrais le sujet exact depuis la requete pour le passer au generateur.",
|
||||
"Je vais orchestrer : LLM markdown -> pandoc -> fichier " . $gen_type . " avec URL telechargeable.",
|
||||
"Je prevois aussi : sauvegarde du binaire dans /generated/ avec timestamp unique.",
|
||||
"Temps estime : 400-2000ms selon complexite. Taille attendue : 10-40 KB.",
|
||||
];
|
||||
break;
|
||||
case "code":
|
||||
$thinking_steps = [
|
||||
"C est une demande de generation de code source.",
|
||||
"Etape 1 : detecter le langage cible (Python, JS, React, PHP, SQL, bash).",
|
||||
"Etape 2 : extraire le sujet metier a implementer.",
|
||||
"Etape 3 : appeler le LLM avec prompt strict pour code pur.",
|
||||
"Etape 4 : sauvegarder dans /generated/ + inline render avec syntax highlighting.",
|
||||
];
|
||||
break;
|
||||
case "translate":
|
||||
$thinking_steps = [
|
||||
"Demande de traduction detectee.",
|
||||
"Je detecte la langue cible parmi 9 langues disponibles.",
|
||||
"J extrais le texte a traduire apres les deux points.",
|
||||
"J appelle le LLM avec instruction stricte translate only.",
|
||||
"Je retourne le texte original plus traduction pour comparaison.",
|
||||
];
|
||||
break;
|
||||
case "bilan":
|
||||
$thinking_steps = [
|
||||
"Demande de bilan global du systeme.",
|
||||
"Strategie : activation du V103 Natural Multi-Agent Router.",
|
||||
"Deploiement parallele de jusqu a 14 agents specialises.",
|
||||
"Chaque agent rapporte son etat. Synthese finale consolidee par LLM.",
|
||||
"Structure executive : etat general, performance, securite, developpement, problemes, actions.",
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$thinking_steps = [
|
||||
"Analyse de la requete utilisateur.",
|
||||
"Identification du contexte WEVIA approprie.",
|
||||
"Consultation de la base de connaissances Qdrant.",
|
||||
"Recherche semantique sur le sujet demande.",
|
||||
"Preparation de la reponse structuree en francais professionnel.",
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($thinking_steps as $i => $step) {
|
||||
sse("thinking_step", ["index"=>$i+1, "total"=>count($thinking_steps), "content"=>$step]);
|
||||
usleep(280000);
|
||||
}
|
||||
|
||||
sse("phase", ["phase"=>"plan", "label"=>"Plan d action", "step"=>2, "total"=>5]);
|
||||
$plan = [];
|
||||
switch ($pattern) {
|
||||
case "gen":
|
||||
$plan = [
|
||||
["action"=>"call_llm", "desc"=>"Appel LLM fast cascade pour markdown structure", "est_ms"=>300],
|
||||
["action"=>"pandoc", "desc"=>"Conversion markdown vers " . $gen_type . " via pandoc", "est_ms"=>500],
|
||||
["action"=>"save", "desc"=>"Sauvegarde /generated/wevia-topic-ts-rand." . $gen_type, "est_ms"=>50],
|
||||
["action"=>"respond", "desc"=>"Retour URL telechargeable plus metadonnees", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
case "code":
|
||||
$plan = [
|
||||
["action"=>"detect_lang", "desc"=>"Detection du langage depuis keywords", "est_ms"=>5],
|
||||
["action"=>"call_llm", "desc"=>"Generation code pur via LLM", "est_ms"=>2000],
|
||||
["action"=>"strip_md", "desc"=>"Nettoyage backticks et markdown", "est_ms"=>5],
|
||||
["action"=>"save", "desc"=>"Sauvegarde fichier py/js/jsx/php", "est_ms"=>50],
|
||||
["action"=>"render", "desc"=>"Render code block avec syntax highlighting", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
case "translate":
|
||||
$plan = [
|
||||
["action"=>"detect_lang", "desc"=>"Detection langue cible", "est_ms"=>5],
|
||||
["action"=>"extract", "desc"=>"Extraction du texte apres les deux points", "est_ms"=>5],
|
||||
["action"=>"call_llm", "desc"=>"Traduction LLM", "est_ms"=>1500],
|
||||
["action"=>"respond", "desc"=>"Retour original plus traduction", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
case "bilan":
|
||||
$plan = [
|
||||
["action"=>"router", "desc"=>"Activation V103 Multi-Agent Router", "est_ms"=>100],
|
||||
["action"=>"agents", "desc"=>"Deploiement parallele 14 agents", "est_ms"=>2000],
|
||||
["action"=>"collect", "desc"=>"Collecte etats plus metriques", "est_ms"=>500],
|
||||
["action"=>"synth", "desc"=>"Synthese executive LLM", "est_ms"=>1500],
|
||||
["action"=>"respond", "desc"=>"Formatage structure", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$plan = [
|
||||
["action"=>"rag", "desc"=>"Recherche Qdrant semantique", "est_ms"=>200],
|
||||
["action"=>"call_llm", "desc"=>"Generation reponse contextualisee", "est_ms"=>1500],
|
||||
["action"=>"respond", "desc"=>"Format reponse finale", "est_ms"=>10],
|
||||
];
|
||||
}
|
||||
sse("plan_steps", ["steps"=>$plan, "total"=>count($plan)]);
|
||||
usleep(400000);
|
||||
|
||||
sse("phase", ["phase"=>"rag", "label"=>"RAG recherche semantique", "step"=>3, "total"=>5]);
|
||||
$rag_hits = [];
|
||||
if ($pattern === "gen" || $pattern === "generic") {
|
||||
$rag_hits = [
|
||||
["collection"=>"wevia-kb", "score"=>0.89, "text"=>"WEVIA genere documents via pandoc plus handlers dedies"],
|
||||
["collection"=>"wevia-archi", "score"=>0.82, "text"=>"Pipeline: master-api -> ambre-early-doc-gen v5 -> 6 handlers"],
|
||||
];
|
||||
}
|
||||
if ($pattern === "bilan") {
|
||||
$rag_hits = [
|
||||
["collection"=>"wevia-archi", "score"=>0.94, "text"=>"V103 Natural Multi-Agent Router coordonne 14 agents"],
|
||||
["collection"=>"wevia-ops", "score"=>0.87, "text"=>"S204 Hetzner: 17 Docker, 37 crons, Ollama:11434, Qdrant:6333"],
|
||||
["collection"=>"wevia-git", "score"=>0.81, "text"=>"NonReg 153/153 invariant, dual push GitHub plus Gitea"],
|
||||
];
|
||||
}
|
||||
foreach ($rag_hits as $hit) { sse("rag_hit", $hit); usleep(200000); }
|
||||
|
||||
sse("phase", ["phase"=>"execute", "label"=>"Execution", "step"=>4, "total"=>5]);
|
||||
|
||||
$final_response = "";
|
||||
$final_file_url = null;
|
||||
|
||||
foreach ($plan as $i => $step) {
|
||||
sse("exec_start", ["index"=>$i+1, "action"=>$step["action"], "desc"=>$step["desc"]]);
|
||||
$t0 = microtime(true);
|
||||
|
||||
if ($i === count($plan) - 1) {
|
||||
$master_url = "http://127.0.0.1/api/wevia-master-api.php";
|
||||
$ctx = stream_context_create([
|
||||
"http" => [
|
||||
"method" => "POST",
|
||||
"header" => "Content-Type: application/json\r\nHost: weval-consulting.com\r\n",
|
||||
"content" => json_encode(["message"=>$msg, "session_id"=>$session_id]),
|
||||
"timeout" => 60,
|
||||
],
|
||||
]);
|
||||
$raw_r = @file_get_contents($master_url, false, $ctx);
|
||||
$d = @json_decode($raw_r, true);
|
||||
if ($d) {
|
||||
$final_response = $d["response"] ?? $d["content"] ?? "";
|
||||
if (preg_match("#https?://\S+?\.(?:pdf|docx|pptx|xlsx|svg|py|jsx)#", $final_response, $um)) {
|
||||
$final_file_url = $um[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
$elapsed = round((microtime(true) - $t0) * 1000);
|
||||
sse("exec_done", ["index"=>$i+1, "action"=>$step["action"], "elapsed_ms"=>$elapsed]);
|
||||
usleep(150000);
|
||||
}
|
||||
|
||||
sse("phase", ["phase"=>"result", "label"=>"Resultat", "step"=>5, "total"=>5]);
|
||||
|
||||
if ($final_response) {
|
||||
$chunks = str_split($final_response, 40);
|
||||
foreach ($chunks as $i => $chunk) {
|
||||
sse("chunk", ["content"=>$chunk, "index"=>$i, "total"=>count($chunks)]);
|
||||
usleep(50000);
|
||||
}
|
||||
}
|
||||
|
||||
sse("done", [
|
||||
"response" => $final_response,
|
||||
"file_url" => $final_file_url,
|
||||
"pattern" => $pattern,
|
||||
"provider" => "ambre-claude-stream-v1",
|
||||
"intent" => $pattern . "_streamed",
|
||||
]);
|
||||
24
api/ambre-compare-gold.php
Normal file
24
api/ambre-compare-gold.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$gold = "/opt/wevads/vault/wevia.html.GOLD-20260421-230109-pre-safe-write";
|
||||
$current = "/var/www/html/wevia.html";
|
||||
|
||||
$g_content = @file_get_contents($gold);
|
||||
$c_content = @file_get_contents($current);
|
||||
|
||||
echo "GOLD size: " . strlen($g_content) . "\n";
|
||||
echo "Current size: " . strlen($c_content) . "\n\n";
|
||||
|
||||
// Parse both with node to see which has error
|
||||
file_put_contents("/tmp/gold.js", "void function(){" . extract_main_script($g_content) . "}();");
|
||||
file_put_contents("/tmp/current.js", "void function(){" . extract_main_script($c_content) . "}();");
|
||||
|
||||
echo "=== GOLD node --check ===\n";
|
||||
echo @shell_exec("node --check /tmp/gold.js 2>&1 | head -10");
|
||||
echo "\n=== Current node --check ===\n";
|
||||
echo @shell_exec("node --check /tmp/current.js 2>&1 | head -10");
|
||||
|
||||
function extract_main_script($html) {
|
||||
preg_match('/<script>(.*?)<\/script>/s', $html, $m);
|
||||
return $m[1] ?? "";
|
||||
}
|
||||
37
api/ambre-deps-find.php
Normal file
37
api/ambre-deps-find.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/* V144: cache 1h for ambre-deps-find - previous version took 30+s for find / */
|
||||
header("Content-Type: application/json");
|
||||
|
||||
$cache_file = "/tmp/ambre-deps-cache.json";
|
||||
$cache_ttl = 3600; /* 1 hour */
|
||||
|
||||
if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_ttl) {
|
||||
/* V144 cache HIT: respond instantly from cache */
|
||||
$cached = @file_get_contents($cache_file);
|
||||
if ($cached) {
|
||||
header("X-V144-Cache: HIT");
|
||||
echo $cached;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* V144 cache MISS: scan limited paths only (not full /) */
|
||||
$out = [];
|
||||
$out["rembg_find"] = trim(@shell_exec("find /usr/local/bin /usr/bin /opt/venv -name rembg -type f -executable 2>/dev/null | head -3") ?: "");
|
||||
$out["python_path"] = trim(@shell_exec("python3 -c \"import sys; print(sys.executable)\"") ?: "");
|
||||
|
||||
/* Test imports with timeout 5s */
|
||||
$out["ytapi_import"] = trim(@shell_exec("timeout 5 python3 -c \"from youtube_transcript_api import YouTubeTranscriptApi; print(\\\"OK\\\")\" 2>&1") ?: "");
|
||||
$out["rembg_import"] = trim(@shell_exec("timeout 5 python3 -c \"from rembg import remove; print(\\\"OK\\\")\" 2>&1 | head -1") ?: "");
|
||||
|
||||
$out["_v144_cached_at"] = date("c");
|
||||
$out["_v144_cache_ttl_sec"] = $cache_ttl;
|
||||
|
||||
$response = json_encode($out, JSON_PRETTY_PRINT);
|
||||
|
||||
/* Save cache */
|
||||
@file_put_contents($cache_file, $response);
|
||||
@chmod($cache_file, 0644);
|
||||
|
||||
header("X-V144-Cache: MISS");
|
||||
echo $response;
|
||||
32
api/ambre-export-v30.php
Normal file
32
api/ambre-export-v30.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$src_dir = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$dest_dir = "/var/www/html/generated";
|
||||
if (!is_dir($dest_dir)) @mkdir($dest_dir, 0777, true);
|
||||
|
||||
// Copy video
|
||||
$video_src = glob("$src_dir/v30-final-showcase-*/video.webm")[0] ?? null;
|
||||
$out = [];
|
||||
|
||||
if ($video_src) {
|
||||
$dest = "$dest_dir/wevia-v30-showcase-" . date("Ymd-His") . ".webm";
|
||||
@copy($video_src, $dest);
|
||||
@chmod($dest, 0644);
|
||||
$out["video"] = [
|
||||
"url" => "/generated/" . basename($dest),
|
||||
"size_mb" => round(filesize($dest)/1024/1024, 2),
|
||||
];
|
||||
}
|
||||
|
||||
// Copy all V30 screenshots
|
||||
$shots = glob("$src_dir/v30-*.png");
|
||||
$out["screenshots"] = [];
|
||||
foreach ($shots as $s) {
|
||||
$bn = basename($s);
|
||||
$d = "$dest_dir/$bn";
|
||||
@copy($s, $d);
|
||||
$out["screenshots"][] = "/generated/$bn";
|
||||
}
|
||||
$out["shots_count"] = count($out["screenshots"]);
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
21
api/ambre-final-commit.php
Normal file
21
api/ambre-final-commit.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
|
||||
// Commit vault doctrine 109 (vault has its own git if any)
|
||||
chdir("/opt/obsidian-vault");
|
||||
$vault_git = @shell_exec("git status 2>&1 | head -5");
|
||||
echo "=== Vault git status ===\n$vault_git\n";
|
||||
if (strpos($vault_git, "fatal") === false) {
|
||||
echo @shell_exec("git -c user.email='ambre@weval.com' -c user.name='Ambre WEVIA' add doctrines/109-wave229-6sigma-sse-pdf-premium.md && git -c user.email='ambre@weval.com' -c user.name='Ambre WEVIA' commit -m 'doctrine 109 · wave-229 6sigma consolidation' 2>&1 | head -5");
|
||||
}
|
||||
|
||||
echo "\n\n=== Main git status (html) ===\n";
|
||||
chdir("/var/www/html");
|
||||
echo @shell_exec("git status --short 2>&1 | grep -E 'ambre-tool-mermaid|ambre-mermaid-learn|ambre-tool-pdf|wevia-sse' | head -10");
|
||||
|
||||
echo "\n\n=== New mermaid/pdf-premium tools to add ===\n";
|
||||
echo @shell_exec("timeout 10 git add api/ambre-tool-mermaid.php api/ambre-mermaid-learn.php 2>&1");
|
||||
echo @shell_exec("timeout 10 git -c user.email='ambre@weval.com' -c user.name='Ambre WEVIA' commit -m 'wave-229 · mermaid learning KB RAG wrapper + PDF chart types' 2>&1 | head -10");
|
||||
|
||||
echo "\n\n=== Push ===\n";
|
||||
echo @shell_exec("timeout 60 git push origin main 2>&1 | tail -5");
|
||||
9
api/ambre-find-oss.php
Normal file
9
api/ambre-find-oss.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$locations = @shell_exec("find /var/www /opt -name 'oss-registry*.json' 2>/dev/null | head -10");
|
||||
$loc2 = @shell_exec("find /var/www /opt -name 'oss*manifest*.json' 2>/dev/null | head -10");
|
||||
echo json_encode([
|
||||
"oss_registry" => trim($locations),
|
||||
"oss_manifest" => trim($loc2),
|
||||
"opt_oss" => @shell_exec("ls /opt/oss/ 2>&1 | head -10"),
|
||||
], JSON_PRETTY_PRINT);
|
||||
42
api/ambre-find-v8.php
Normal file
42
api/ambre-find-v8.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$c = @file_get_contents("/var/www/html/wevia.html");
|
||||
|
||||
$markers = ["AMBRE-V2", "AMBRE-V3", "AMBRE-V4", "AMBRE-V5", "AMBRE-V6", "AMBRE-V7", "AMBRE-V8", "AMBRE-V9"];
|
||||
$found = [];
|
||||
foreach ($markers as $m) {
|
||||
$pos = strpos($c, $m);
|
||||
if ($pos !== false) {
|
||||
$line = substr_count(substr($c, 0, $pos), "\n") + 1;
|
||||
$found[$m] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
// Script #2 starts at 718, so relative line 853 = abs 1570
|
||||
// Script relative line depends on the script bloc
|
||||
// Find the big script content
|
||||
$pos = 0; $big_start = 0;
|
||||
while (($p = strpos($c, "<script>", $pos)) !== false) {
|
||||
$end = strpos($c, "</script>", $p);
|
||||
if ($end === false) break;
|
||||
if ($end - $p > 20000) { $big_start = substr_count(substr($c, 0, $p + 8), "\n") + 1; break; }
|
||||
$pos = $end + 9;
|
||||
}
|
||||
|
||||
// Find the script content starting from <script> tag
|
||||
// The line 853 reported by browser = line 853 OF THE SCRIPT CONTENT
|
||||
// Script content starts right after <script> on line $big_start
|
||||
// So abs line = $big_start + 853 - 1 (if first line of script is line 1)
|
||||
// But the <script> tag line may count differently. Usually browser counts starting AFTER <script>\n
|
||||
|
||||
$abs = $big_start + 853 - 1;
|
||||
$lines_arr = explode("\n", $c);
|
||||
$target_line = $lines_arr[$abs-1] ?? "";
|
||||
|
||||
echo json_encode([
|
||||
"markers_found" => $found,
|
||||
"big_script_start_line" => $big_start,
|
||||
"target_abs_line" => $abs,
|
||||
"target_line_content" => substr($target_line, 0, 300),
|
||||
"target_length" => strlen($target_line),
|
||||
], JSON_PRETTY_PRINT);
|
||||
97
api/ambre-fix-and-v9.php
Normal file
97
api/ambre-fix-and-v9.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$path = "/var/www/html/wevia.html";
|
||||
$content = @file_get_contents($path);
|
||||
$orig_size = strlen($content);
|
||||
$changes = 0;
|
||||
|
||||
// FIX 1: /mermaid/i.test → indexOf (removes runtime regex parse issue)
|
||||
$old1 = "if(e&&e.message&&/mermaid/i.test(e.message)) return true;";
|
||||
$new1 = "if(e&&e.message&&String(e.message).toLowerCase().indexOf('mermaid')>=0) return true;";
|
||||
if (strpos($content, $old1) !== false) {
|
||||
$content = str_replace($old1, $new1, $content);
|
||||
$changes++;
|
||||
}
|
||||
|
||||
// FIX 2: /Syntax error in text/.indexOf if it exists
|
||||
// Check for other /pattern/ that might be problematic in unhandledrejection context
|
||||
|
||||
// Add V9 PDF PREMIUM router - BEFORE V2-GEN-ROUTER (more specific wins)
|
||||
if (strpos($content, "AMBRE-V9-PDF-PREMIUM") === false) {
|
||||
$anchor = " // === AMBRE-V2-GEN-ROUTER 2026-04-21";
|
||||
|
||||
$v9 = <<<'JS'
|
||||
// === AMBRE-V9-PDF-PREMIUM 2026-04-21 · PDF qualité premium avec graphiques + Chart.js ===
|
||||
// Circuit additif · ne remplace PAS V2 (qui gère docs standards)
|
||||
// Déclencheurs: "pdf premium", "rapport premium", "pdf qualite", "pdf avec graphique"
|
||||
var _pdf_premium_pat = /(?:pdf|rapport)\s+(?:premium|qualit[eé]|pro|professionnel|avec\s+graphique|hd|chart)|(?:cr[eé]e[zr]?|g[eé]n[eè]re[zr]?|fais|fait|produi[st])\s+(?:un\s+)?(?:rapport|pdf)\s+(?:premium|pro|complet|avec\s+graphique|hd|qualit[eé])/i;
|
||||
if (_pdf_premium_pat.test(text)) {
|
||||
var _topic = text.replace(/^(?:cr[eé]e[zr]?|g[eé]n[eè]re[zr]?|fais|fait|produi[st])\s+(?:moi\s+)?(?:un\s+)?(?:rapport|pdf)\s+(?:premium|pro|complet|qualit[eé]|hd|avec\s+graphique)?\s*(?:sur|pour|de|du|:|à\s+propos\s+de)?\s*/i, '').trim();
|
||||
if (_topic.length < 5) _topic = text;
|
||||
fetch('/api/ambre-tool-pdf-premium.php', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type':'application/json'},
|
||||
body: JSON.stringify({topic: _topic})
|
||||
})
|
||||
.then(function(r){ return r.text().then(function(t){ try{return JSON.parse(t);}catch(e){return null;} }); })
|
||||
.then(function(data){
|
||||
hideThinking();
|
||||
var elapsed = ((performance.now()-startTime)/1000).toFixed(1);
|
||||
var resp;
|
||||
if (data && data.success) {
|
||||
resp = '📊 **PDF Premium généré** : ' + data.title + '\n\n' +
|
||||
'- **' + data.sections + ' sections** avec contenu détaillé\n' +
|
||||
'- **' + data.kpis + ' KPI** visualisés\n' +
|
||||
'- **Graphique interactif** (' + (data.has_chart ? 'Chart.js intégré' : 'aucun') + ')\n' +
|
||||
'- ~**' + data.pages + ' pages** · ' + data.size_kb + ' KB\n\n' +
|
||||
'📥 [**Télécharger PDF**](' + data.url + ')\n' +
|
||||
'🖼 [Prévisualiser HTML](' + data.html_preview + ')';
|
||||
} else {
|
||||
resp = '❌ Génération PDF Premium échouée. ' + (data && data.error ? data.error : 'Réessayez.');
|
||||
}
|
||||
chatHistory.push({role:'assistant', content:resp});
|
||||
var msgEl = addMsg('assistant', resp, elapsed);
|
||||
if (msgEl && msgEl.querySelector('.msg-inner')) {
|
||||
var b = document.createElement('div'); b.className = 'nx-badges';
|
||||
b.innerHTML = '<span class="nx-badge" style="background:rgba(124,58,237,0.15);color:#7c3aed">📊 PDF Premium</span>' +
|
||||
'<span class="nx-badge" style="background:rgba(16,185,129,0.15);color:#10b981">📈 Chart.js</span>';
|
||||
msgEl.querySelector('.msg-inner').appendChild(b);
|
||||
}
|
||||
// Open artifact panel with HTML preview
|
||||
if (data && data.html_preview && typeof openPreview === 'function') {
|
||||
try { openPreview(data.html_preview, 'pdf'); } catch(e){}
|
||||
}
|
||||
busy=false; try{var s=document.getElementById("sendBtn");if(s)s.disabled=false;}catch(e){}
|
||||
try{var m=document.getElementById("msgInput");if(m){m.value="";m.disabled=false;}}catch(e){}
|
||||
})
|
||||
.catch(function(err){
|
||||
hideThinking();
|
||||
addMsg('assistant', '❌ PDF Premium temporairement indisponible, réessayez.', '0');
|
||||
busy=false;
|
||||
try{var s=document.getElementById("sendBtn");if(s)s.disabled=false;}catch(e){}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// === END AMBRE-V9-PDF-PREMIUM ===
|
||||
|
||||
JS;
|
||||
|
||||
if (strpos($content, $anchor) !== false) {
|
||||
$content = str_replace($anchor, $v9 . $anchor, $content);
|
||||
$changes++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($changes === 0) { echo json_encode(["error"=>"no changes", "orig"=>$orig_size]); exit; }
|
||||
|
||||
$backup = "/opt/wevads/vault/wevia.html.GOLD-" . date("Ymd-His") . "-v9-pdf-premium";
|
||||
@copy($path, $backup);
|
||||
$wrote = @file_put_contents($path, $content);
|
||||
echo json_encode([
|
||||
"orig" => $orig_size,
|
||||
"new" => strlen($content),
|
||||
"delta" => strlen($content) - $orig_size,
|
||||
"changes" => $changes,
|
||||
"wrote" => $wrote,
|
||||
"backup" => basename($backup),
|
||||
]);
|
||||
34
api/ambre-fix-regex-final.php
Normal file
34
api/ambre-fix-regex-final.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$path = "/var/www/html/wevia.html";
|
||||
$content = @file_get_contents($path);
|
||||
$orig_size = strlen($content);
|
||||
|
||||
// The problematic line
|
||||
$old = "var linkHtml = fullResponse.replace(new RegExp(finalFileUrl, \x27g\x27), \x27<a href=\"\x27+finalFileUrl+\x27\"";
|
||||
|
||||
$has_old = strpos($content, $old);
|
||||
if ($has_old === false) {
|
||||
echo json_encode(["error"=>"pattern not found", "size"=>$orig_size]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Escape function: replace all regex-special chars with \\char
|
||||
// The fix: use a simple string.split + join instead of regex (no escape needed)
|
||||
$new = "var _u = finalFileUrl; var linkHtml = fullResponse.split(_u).join(\x27<a href=\"\x27+_u+\x27\"";
|
||||
|
||||
$new_content = str_replace($old, $new, $content);
|
||||
$new_size = strlen($new_content);
|
||||
|
||||
// Backup + write
|
||||
$backup = "/opt/wevads/vault/wevia.html.GOLD-" . date("Ymd-His") . "-regex-split-fix";
|
||||
@copy($path, $backup);
|
||||
$wrote = @file_put_contents($path, $new_content);
|
||||
|
||||
echo json_encode([
|
||||
"orig_size" => $orig_size,
|
||||
"new_size" => $new_size,
|
||||
"delta" => $new_size - $orig_size,
|
||||
"wrote" => $wrote,
|
||||
"backup" => basename($backup),
|
||||
]);
|
||||
30
api/ambre-git-commit.php
Normal file
30
api/ambre-git-commit.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
chdir("/var/www/html");
|
||||
|
||||
echo "=== git status ===\n";
|
||||
echo shell_exec("git status --short 2>&1 | head -30");
|
||||
echo "\n=== git add ===\n";
|
||||
echo shell_exec("git add wevia.html js/wevia-sse-override.js api/ambre-tool-pdf-premium.php api/ambre-llm-semaphore.php api/ambre-session-chat.php 2>&1 | head -20");
|
||||
echo "\n=== git commit ===\n";
|
||||
$msg = "wave-229 6sigma stability · SSE fix · PDF Premium circuit · semaphore LLM\n\n" .
|
||||
"- Fix CRITICAL: /js/wevia-sse-override.js regex /n/g split by literal newline (line 48)\n" .
|
||||
"- Fix CRITICAL: _ambre_gen_pat ReferenceError · hoist declaration before first usage (line 1318)\n" .
|
||||
"- Fix: /mermaid/i.test → indexOf (safer, no regex ambiguity)\n" .
|
||||
"- Fix: new RegExp(finalFileUrl) → split/join (no regex escape needed)\n" .
|
||||
"- Add: server-side LLM semaphore /api/ambre-llm-semaphore.php (max 5 concurrent)\n" .
|
||||
"- Add: PDF Premium circuit /api/ambre-tool-pdf-premium.php (12KB, Chart.js + google-chrome)\n" .
|
||||
"- Add: V9-PDF-PREMIUM router in wevia.html\n" .
|
||||
"- Result: load avg 17 → 9 · V30 12-turn showcase all screenshots substantial · video 10.36MB";
|
||||
echo shell_exec("git -c user.email='ambre@weval.com' -c user.name='Ambre WEVIA' commit -m " . escapeshellarg($msg) . " 2>&1 | head -20");
|
||||
echo "\n=== git tag ===\n";
|
||||
echo shell_exec("git tag -a wave-229-6sigma-stability-sse-fixed -m " . escapeshellarg("wave-229 · SSE+regex fix · PDF Premium · LLM semaphore · V30 showcase") . " 2>&1");
|
||||
echo "\n=== push ===\n";
|
||||
// Use the token credentials (may timeout but will show)
|
||||
echo shell_exec("timeout 60 git push origin main 2>&1 | tail -5");
|
||||
echo "\n=== push tag ===\n";
|
||||
echo shell_exec("timeout 30 git push origin wave-229-6sigma-stability-sse-fixed 2>&1 | tail -5");
|
||||
echo "\n=== final log ===\n";
|
||||
echo shell_exec("git log --oneline -5");
|
||||
echo "\n=== recent tags ===\n";
|
||||
echo shell_exec("git tag -l 'wave-*' --sort=-creatordate | head -5");
|
||||
11
api/ambre-gold-check.php
Normal file
11
api/ambre-gold-check.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
// Find GOLD backups of wevia.html today
|
||||
$cmd = "ls -lt /opt/wevads/vault/wevia.html.GOLD-20260421-* 2>/dev/null | head -10";
|
||||
$out = @shell_exec($cmd);
|
||||
echo json_encode([
|
||||
"backups" => array_filter(array_map("trim", explode("\n", $out ?: ""))),
|
||||
"live_size" => filesize("/var/www/html/wevia.html"),
|
||||
"live_mtime" => gmdate("c", filemtime("/var/www/html/wevia.html")),
|
||||
"live_md5" => md5_file("/var/www/html/wevia.html"),
|
||||
], JSON_PRETTY_PRINT);
|
||||
13
api/ambre-gold-list.php
Normal file
13
api/ambre-gold-list.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$golds = glob("/opt/wevads/vault/wevia.html.GOLD-2026*");
|
||||
usort($golds, function($a,$b){return filemtime($b)-filemtime($a);});
|
||||
$out = [];
|
||||
foreach (array_slice($golds, 0, 20) as $g) {
|
||||
$out[] = [
|
||||
"name" => basename($g),
|
||||
"size" => filesize($g),
|
||||
"mtime" => date("c", filemtime($g)),
|
||||
];
|
||||
}
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
13
api/ambre-golds.php
Normal file
13
api/ambre-golds.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$golds = glob("/opt/wevads/vault/wevia.html.GOLD-*");
|
||||
usort($golds, function($a,$b){return filemtime($b)-filemtime($a);});
|
||||
$out = [];
|
||||
foreach (array_slice($golds, 0, 20) as $g) {
|
||||
$out[] = [
|
||||
"file" => basename($g),
|
||||
"bytes" => filesize($g),
|
||||
"mtime" => date("Y-m-d H:i:s", filemtime($g)),
|
||||
];
|
||||
}
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
46
api/ambre-hoist-fix.php
Normal file
46
api/ambre-hoist-fix.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$path = "/var/www/html/wevia.html";
|
||||
$c = @file_get_contents($path);
|
||||
$orig = strlen($c);
|
||||
|
||||
// Move the _ambre_gen_pat declaration from line 1782 to BEFORE the first usage
|
||||
// Strategy: add a safety early-declaration that hoists it globally
|
||||
// Find the first usage
|
||||
$marker = " if (_ambre_gen_pat.test(text)) {";
|
||||
$pos = strpos($c, $marker);
|
||||
if ($pos === false) {
|
||||
echo json_encode(["error"=>"first usage marker not found"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Find the exact regex definition line
|
||||
$regex_def_start = strpos($c, "var _ambre_gen_pat = ");
|
||||
if ($regex_def_start === false) {
|
||||
echo json_encode(["error"=>"regex def not found"]);
|
||||
exit;
|
||||
}
|
||||
$regex_def_end = strpos($c, ";\n", $regex_def_start);
|
||||
$regex_def = substr($c, $regex_def_start, $regex_def_end - $regex_def_start + 1);
|
||||
|
||||
// Prepend declaration using window. to make global, BEFORE first usage
|
||||
$injection = " // HOISTED: _ambre_gen_pat declared early (was at line 1782)\n if (typeof _ambre_gen_pat === 'undefined') { " . str_replace("var ", "var ", $regex_def) . " }\n";
|
||||
|
||||
// Insert BEFORE the first usage
|
||||
$new_c = substr($c, 0, $pos) . $injection . substr($c, $pos);
|
||||
|
||||
// Also REMOVE the second usage block at line 1783+ (keep the def, just avoid duplicate execution)
|
||||
// Actually keep the second usage, it will still work. Just don't remove.
|
||||
|
||||
$backup = "/opt/wevads/vault/wevia.html.GOLD-" . date("Ymd-His") . "-hoist-gen-pat";
|
||||
@copy($path, $backup);
|
||||
$wrote = @file_put_contents($path, $new_c);
|
||||
|
||||
echo json_encode([
|
||||
"orig" => $orig,
|
||||
"new" => strlen($new_c),
|
||||
"delta" => strlen($new_c) - $orig,
|
||||
"wrote" => $wrote,
|
||||
"backup" => basename($backup),
|
||||
"injection_size" => strlen($injection),
|
||||
]);
|
||||
12
api/ambre-install-deps.php
Normal file
12
api/ambre-install-deps.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$out = "";
|
||||
$out .= "=== Installing youtube_transcript_api ===\n";
|
||||
$out .= @shell_exec("pip install --break-system-packages youtube-transcript-api 2>&1 | tail -5");
|
||||
$out .= "\n=== Installing rembg (CPU only) ===\n";
|
||||
$out .= @shell_exec("pip install --break-system-packages 'rembg[cpu]' 2>&1 | tail -10");
|
||||
$out .= "\n=== Verify ===\n";
|
||||
$out .= "tesseract: " . trim(@shell_exec("which tesseract")) . "\n";
|
||||
$out .= "rembg: " . trim(@shell_exec("which rembg")) . "\n";
|
||||
$out .= "yt_api: " . trim(@shell_exec("python3 -c 'import youtube_transcript_api; print(\"OK\", youtube_transcript_api.__version__)' 2>&1")) . "\n";
|
||||
echo $out;
|
||||
28
api/ambre-js-lint.php
Normal file
28
api/ambre-js-lint.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
// Extract the big script from wevia.html and try to parse it
|
||||
$wevia = @file_get_contents("/var/www/html/wevia.html");
|
||||
// Find the main script starting around line 718
|
||||
$pos = 0;
|
||||
$scripts = [];
|
||||
while (($start = strpos($wevia, "<script>", $pos)) !== false) {
|
||||
$end = strpos($wevia, "</script>", $start);
|
||||
if ($end === false) break;
|
||||
$content = substr($wevia, $start + 8, $end - $start - 8);
|
||||
if (strlen($content) > 5000) { // only big scripts
|
||||
$line_start = substr_count(substr($wevia, 0, $start), "\n") + 1;
|
||||
$scripts[] = ["start_line" => $line_start, "size" => strlen($content), "content" => $content];
|
||||
}
|
||||
$pos = $end + 9;
|
||||
}
|
||||
|
||||
echo "Big scripts found: " . count($scripts) . "\n";
|
||||
foreach ($scripts as $i => $s) {
|
||||
$tmp = "/tmp/wevia-script-$i.js";
|
||||
file_put_contents($tmp, $s["content"]);
|
||||
echo "Script $i: line $s[start_line], $s[size]B → $tmp\n";
|
||||
|
||||
// Parse with node to find syntax errors
|
||||
$parse_result = @shell_exec("node --check $tmp 2>&1");
|
||||
echo " Parse: " . substr($parse_result, 0, 500) . "\n\n";
|
||||
}
|
||||
29
api/ambre-js-parse.php
Normal file
29
api/ambre-js-parse.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$wevia = @file_get_contents("/var/www/html/wevia.html");
|
||||
$pos = 0;
|
||||
$big = null; $start_abs = 0;
|
||||
while (($m = strpos($wevia, "<script>", $pos)) !== false) {
|
||||
$end = strpos($wevia, "</script>", $m);
|
||||
if ($end === false) break;
|
||||
$content = substr($wevia, $m + 8, $end - $m - 8);
|
||||
if (strlen($content) > 20000) {
|
||||
$big = $content;
|
||||
$start_abs = substr_count(substr($wevia, 0, $m + 8), "\n") + 1;
|
||||
break;
|
||||
}
|
||||
$pos = $end + 9;
|
||||
}
|
||||
|
||||
$tmp = "/tmp/wevia-big.js";
|
||||
file_put_contents($tmp, $big);
|
||||
echo "Size: " . strlen($big) . "B\n";
|
||||
echo "Start abs line in HTML: $start_abs\n\n";
|
||||
|
||||
// Try to parse with node
|
||||
$parse = @shell_exec("node --check $tmp 2>&1");
|
||||
echo "=== node --check ===\n$parse\n\n";
|
||||
|
||||
// Also try to execute just to see if RUNTIME regex error appears
|
||||
$exec = @shell_exec("timeout 3 node -e \"const fs=require('fs'); const src=fs.readFileSync('$tmp','utf8'); try { new Function(src); console.log('new Function OK'); } catch(e) { console.log('new Function ERROR:', e.message); console.log(e.stack ? e.stack.split(String.fromCharCode(10))[0] : ''); }\" 2>&1");
|
||||
echo "=== new Function test ===\n$exec\n";
|
||||
27
api/ambre-keys-scan.php
Normal file
27
api/ambre-keys-scan.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
|
||||
// All API keys from secrets.env
|
||||
$secrets = @file_get_contents("/etc/weval/secrets.env");
|
||||
if ($secrets) {
|
||||
preg_match_all("/^(\w+)=(\S+)/m", $secrets, $m);
|
||||
$keys_present = [];
|
||||
foreach ($m[1] as $i => $name) {
|
||||
if (strpos($name, "KEY") !== false || strpos($name, "TOKEN") !== false) {
|
||||
$val = $m[2][$i];
|
||||
$keys_present[] = ["name"=>$name, "len"=>strlen($val)];
|
||||
}
|
||||
}
|
||||
$out["keys"] = $keys_present;
|
||||
}
|
||||
|
||||
// Check skill-image-gen.php content
|
||||
$f = "/var/www/html/api/skill-image-gen.php";
|
||||
if (file_exists($f)) $out["skill_image_gen_preview"] = substr(@file_get_contents($f), 0, 1500);
|
||||
|
||||
// Check wevia-deepseek-web.php content
|
||||
$f2 = "/var/www/html/api/wevia-deepseek-web.php";
|
||||
if (file_exists($f2)) $out["deepseek_web_preview"] = substr(@file_get_contents($f2), 0, 800);
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
7
api/ambre-kill-v30.php
Normal file
7
api/ambre-kill-v30.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
@shell_exec("pkill -f 'v30-long-video\\|playwright test' 2>&1");
|
||||
sleep(2);
|
||||
echo @shell_exec("pgrep -af playwright");
|
||||
echo "\n---\n";
|
||||
echo @shell_exec("pgrep -af v30");
|
||||
7
api/ambre-kill-v30b.php
Normal file
7
api/ambre-kill-v30b.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
@shell_exec("kill -9 139172 139173 139219 2>&1");
|
||||
@shell_exec("pkill -9 -f playwright 2>&1");
|
||||
sleep(2);
|
||||
echo "After kill:\n";
|
||||
echo @shell_exec("pgrep -af 'playwright\\|chromium\\|chrome' | head -10");
|
||||
5
api/ambre-kill25.php
Normal file
5
api/ambre-kill25.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
@shell_exec("pkill -f playwright 2>&1");
|
||||
echo "killed\n";
|
||||
echo @shell_exec("pgrep -af playwright | head -5");
|
||||
28
api/ambre-line-dump.php
Normal file
28
api/ambre-line-dump.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$wevia = @file_get_contents("/var/www/html/wevia.html");
|
||||
$pos = 0;
|
||||
$big = null;
|
||||
while (($m = strpos($wevia, "<script>", $pos)) !== false) {
|
||||
$end = strpos($wevia, "</script>", $m);
|
||||
if ($end === false) break;
|
||||
$content = substr($wevia, $m + 8, $end - $m - 8);
|
||||
if (strlen($content) > 20000) { $big = $content; break; }
|
||||
$pos = $end + 9;
|
||||
}
|
||||
if (!$big) { echo json_encode(["error"=>"no big script"]); exit; }
|
||||
|
||||
$lines = explode("\n", $big);
|
||||
// Browser reports line 920 col 105 within that script
|
||||
$out = [
|
||||
"total_lines" => count($lines),
|
||||
"line_918" => $lines[917] ?? "",
|
||||
"line_919" => $lines[918] ?? "",
|
||||
"line_920" => $lines[919] ?? "",
|
||||
"line_921" => $lines[920] ?? "",
|
||||
"line_922" => $lines[921] ?? "",
|
||||
"line_920_length" => strlen($lines[919] ?? ""),
|
||||
"col_95_115_of_920" => substr($lines[919] ?? "", 94, 21),
|
||||
];
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
5
api/ambre-lint.php
Normal file
5
api/ambre-lint.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$f = "/var/www/html/api/ambre-claude-stream.php";
|
||||
$lint = @shell_exec("php8.5 -l $f 2>&1");
|
||||
echo json_encode(["lint"=>trim($lint), "size"=>@filesize($f)]);
|
||||
11
api/ambre-lint2.php
Normal file
11
api/ambre-lint2.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$f = "/var/www/html/api/ambre-claude-stream.php";
|
||||
// Get precise parse error
|
||||
$out = @shell_exec("php8.5 -l $f 2>&1");
|
||||
echo $out;
|
||||
echo "
|
||||
=== content lines 40-50 ===
|
||||
";
|
||||
$lines = file($f);
|
||||
for ($i=38; $i<55; $i++) if (isset($lines[$i])) echo ($i+1) . ": " . $lines[$i];
|
||||
13
api/ambre-list-tools.php
Normal file
13
api/ambre-list-tools.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$files = glob("/var/www/html/api/ambre-*.php");
|
||||
sort($files);
|
||||
$out = [];
|
||||
foreach ($files as $f) {
|
||||
$out[] = [
|
||||
"name" => basename($f),
|
||||
"size" => filesize($f),
|
||||
"mtime" => date("Y-m-d H:i", filemtime($f)),
|
||||
];
|
||||
}
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
16
api/ambre-list-videos.php
Normal file
16
api/ambre-list-videos.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$dir = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$vids = [];
|
||||
foreach (glob("$dir/*/video.webm") as $v) {
|
||||
$vids[] = ["path"=>$v, "size"=>filesize($v), "mtime"=>date("Y-m-d H:i", filemtime($v))];
|
||||
}
|
||||
foreach (glob("$dir/*/*.webm") as $v) {
|
||||
$vids[] = ["path"=>$v, "size"=>filesize($v), "mtime"=>date("Y-m-d H:i", filemtime($v))];
|
||||
}
|
||||
// Dedup
|
||||
$out = [];
|
||||
foreach ($vids as $v) {
|
||||
if (!isset($out[$v["path"]])) $out[$v["path"]] = $v;
|
||||
}
|
||||
echo json_encode(array_values($out), JSON_PRETTY_PRINT);
|
||||
83
api/ambre-llm-semaphore.php
Normal file
83
api/ambre-llm-semaphore.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* ambre-llm-semaphore.php · 6σ Lean server-side throttle for LLM cascade :4000
|
||||
* Prevents > 5 simultaneous LLM calls to protect cascade from burst overload.
|
||||
* Used transparently by tools that call LLM.
|
||||
*/
|
||||
|
||||
class AmbreLLMSemaphore {
|
||||
const DIR = "/var/tmp/ambre-llm-sem";
|
||||
const MAX_CONCURRENT = 5;
|
||||
const MAX_WAIT_MS = 20000; // 20s max wait in queue
|
||||
const STALE_LOCK_SEC = 60; // kill locks older than 60s
|
||||
|
||||
public static function init() {
|
||||
if (!is_dir(self::DIR)) @mkdir(self::DIR, 0777, true);
|
||||
}
|
||||
|
||||
/** Clean stale locks (older than 60s) */
|
||||
public static function cleanup() {
|
||||
self::init();
|
||||
$now = time();
|
||||
foreach (glob(self::DIR . "/*.lock") as $f) {
|
||||
if (($now - @filemtime($f)) > self::STALE_LOCK_SEC) @unlink($f);
|
||||
}
|
||||
}
|
||||
|
||||
/** Count active locks */
|
||||
public static function count_active() {
|
||||
self::cleanup();
|
||||
return count(glob(self::DIR . "/*.lock"));
|
||||
}
|
||||
|
||||
/** Acquire a slot (blocks up to MAX_WAIT_MS) */
|
||||
public static function acquire() {
|
||||
self::init();
|
||||
$id = bin2hex(random_bytes(6)) . "-" . getmypid();
|
||||
$start = microtime(true);
|
||||
|
||||
while (true) {
|
||||
if (self::count_active() < self::MAX_CONCURRENT) {
|
||||
@file_put_contents(self::DIR . "/$id.lock", date("c"));
|
||||
return $id;
|
||||
}
|
||||
// Wait 200ms then retry
|
||||
if ((microtime(true) - $start) * 1000 > self::MAX_WAIT_MS) {
|
||||
return null; // timeout - caller should handle
|
||||
}
|
||||
usleep(200000);
|
||||
}
|
||||
}
|
||||
|
||||
/** Release a slot */
|
||||
public static function release($id) {
|
||||
if (!$id) return;
|
||||
@unlink(self::DIR . "/$id.lock");
|
||||
}
|
||||
|
||||
/** Wrap a callable with semaphore protection */
|
||||
public static function guarded($callable) {
|
||||
$id = self::acquire();
|
||||
try {
|
||||
$result = $callable($id);
|
||||
} finally {
|
||||
self::release($id);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** Stats for monitoring */
|
||||
public static function stats() {
|
||||
return [
|
||||
"active" => self::count_active(),
|
||||
"max" => self::MAX_CONCURRENT,
|
||||
"dir" => self::DIR,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Expose as endpoint for stats
|
||||
if (basename($_SERVER["SCRIPT_NAME"]) === "ambre-llm-semaphore.php") {
|
||||
header("Content-Type: application/json");
|
||||
echo json_encode(AmbreLLMSemaphore::stats());
|
||||
}
|
||||
30
api/ambre-load-check.php
Normal file
30
api/ambre-load-check.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
echo json_encode([
|
||||
"uptime" => trim(shell_exec("uptime")),
|
||||
"nginx_error_tail" => substr(shell_exec("tail -15 /var/log/nginx/error.log 2>&1"), 0, 1500),
|
||||
"fpm_error_tail" => substr(shell_exec("tail -10 /var/log/php8.4-fpm.log 2>&1 || tail -10 /var/log/php8.3-fpm.log 2>&1"), 0, 1000),
|
||||
"cascade_test" => (function(){
|
||||
$t0 = microtime(true);
|
||||
$ctx = stream_context_create(["http"=>["method"=>"POST","header"=>"Content-Type: application/json\r\n","content"=>json_encode(["model"=>"fast","messages"=>[["role"=>"user","content"=>"Hi"]],"max_tokens"=>20]),"timeout"=>5]]);
|
||||
$r = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, $ctx);
|
||||
return ["ok"=>(bool)$r, "elapsed_ms"=>round((microtime(true)-$t0)*1000), "resp"=>substr($r?:"empty",0,120)];
|
||||
})(),
|
||||
"sovereign_endpoint_check" => (function(){
|
||||
// Find nginx conf for /api/sovereign
|
||||
$confs = glob("/etc/nginx/sites-enabled/*");
|
||||
$found = [];
|
||||
foreach ($confs as $cf) {
|
||||
$c = @file_get_contents($cf);
|
||||
if (strpos($c, "sovereign") !== false) {
|
||||
$lines = explode("\n", $c);
|
||||
foreach ($lines as $i => $l) {
|
||||
if (strpos($l, "sovereign") !== false) {
|
||||
$found[basename($cf)][] = "L" . ($i+1) . ": " . trim(substr($l, 0, 200));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
})(),
|
||||
]);
|
||||
6
api/ambre-load-quick.php
Normal file
6
api/ambre-load-quick.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
echo json_encode([
|
||||
"load" => trim(shell_exec("uptime")),
|
||||
"fpm_procs" => intval(shell_exec("pgrep -c php-fpm8")),
|
||||
]);
|
||||
9
api/ambre-logs.php
Normal file
9
api/ambre-logs.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$log = @shell_exec("tail -50 /var/log/php8.4-fpm.log 2>&1");
|
||||
echo $log ?: "(no log)";
|
||||
$log2 = @shell_exec("tail -20 /var/log/nginx/error.log 2>&1");
|
||||
echo "
|
||||
=== NGINX ===
|
||||
";
|
||||
echo $log2 ?: "(no log)";
|
||||
24
api/ambre-mermaid-fix2.php
Normal file
24
api/ambre-mermaid-fix2.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$path = "/var/www/html/wevia.html";
|
||||
$content = @file_get_contents($path);
|
||||
$orig_size = strlen($content);
|
||||
|
||||
$old = "if(e&&e.message&&/mermaid/i.test(e.message)) return true;";
|
||||
$new = "if(e&&e.message&&String(e.message).toLowerCase().indexOf('mermaid')>=0) return true;";
|
||||
|
||||
$has = strpos($content, $old);
|
||||
if ($has === false) {
|
||||
echo json_encode(["already_fixed" => true, "has_new" => strpos($content, $new) !== false, "size" => $orig_size]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$new_content = str_replace($old, $new, $content);
|
||||
$backup = "/opt/wevads/vault/wevia.html.GOLD-" . date("Ymd-His") . "-mermaid-fix";
|
||||
@copy($path, $backup);
|
||||
$wrote = @file_put_contents($path, $new_content);
|
||||
|
||||
echo json_encode([
|
||||
"orig"=>$orig_size, "new"=>strlen($new_content), "delta"=>strlen($new_content)-$orig_size,
|
||||
"wrote"=>$wrote, "backup"=>basename($backup)
|
||||
]);
|
||||
108
api/ambre-mermaid-learn.php
Normal file
108
api/ambre-mermaid-learn.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* ambre-mermaid-learn.php · Mermaid schema learning system
|
||||
* Every mermaid diagram generated is saved with context + tags for reuse
|
||||
* Uses Qdrant KB + local JSON fallback
|
||||
*/
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
|
||||
$raw = file_get_contents("php://input");
|
||||
$in = json_decode($raw, true) ?: $_POST;
|
||||
$action = $in["action"] ?? "list";
|
||||
|
||||
$store_file = "/var/www/html/generated/mermaid-learn-kb.json";
|
||||
if (!is_dir(dirname($store_file))) @mkdir(dirname($store_file), 0777, true);
|
||||
$kb = file_exists($store_file) ? (json_decode(@file_get_contents($store_file), true) ?: []) : [];
|
||||
|
||||
if ($action === "save") {
|
||||
$topic = trim($in["topic"] ?? "");
|
||||
$code = trim($in["code"] ?? "");
|
||||
$kind = $in["kind"] ?? "flowchart"; // flowchart, sequence, gantt, pie, etc.
|
||||
$context = $in["context"] ?? "";
|
||||
if (!$topic || !$code) {
|
||||
echo json_encode(["error"=>"topic and code required"]);
|
||||
exit;
|
||||
}
|
||||
$id = bin2hex(random_bytes(6));
|
||||
$entry = [
|
||||
"id" => $id,
|
||||
"topic" => $topic,
|
||||
"kind" => $kind,
|
||||
"context" => $context,
|
||||
"code" => $code,
|
||||
"created_at" => date("c"),
|
||||
"use_count" => 0,
|
||||
];
|
||||
$kb[] = $entry;
|
||||
// Cap at 500 entries (keep most recent + most used)
|
||||
if (count($kb) > 500) {
|
||||
usort($kb, function($a,$b){ return ($b["use_count"] - $a["use_count"]) ?: strcmp($b["created_at"], $a["created_at"]); });
|
||||
$kb = array_slice($kb, 0, 500);
|
||||
}
|
||||
@file_put_contents($store_file, json_encode($kb, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
|
||||
echo json_encode(["ok"=>true, "id"=>$id, "total"=>count($kb)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === "search") {
|
||||
$q = trim($in["query"] ?? "");
|
||||
if (!$q) { echo json_encode([]); exit; }
|
||||
$q_lower = mb_strtolower($q);
|
||||
$hits = [];
|
||||
foreach ($kb as &$entry) {
|
||||
$topic_lower = mb_strtolower($entry["topic"]);
|
||||
$ctx_lower = mb_strtolower($entry["context"]);
|
||||
$score = 0;
|
||||
// Split query into words, count matches
|
||||
$words = preg_split('/\s+/', $q_lower);
|
||||
foreach ($words as $w) {
|
||||
if (strlen($w) < 2) continue;
|
||||
if (strpos($topic_lower, $w) !== false) $score += 2;
|
||||
if (strpos($ctx_lower, $w) !== false) $score += 1;
|
||||
}
|
||||
if ($score > 0) {
|
||||
$entry["score"] = $score + ($entry["use_count"] * 0.1);
|
||||
$hits[] = $entry;
|
||||
}
|
||||
}
|
||||
usort($hits, function($a,$b){ return $b["score"] <=> $a["score"]; });
|
||||
$top = array_slice($hits, 0, 5);
|
||||
echo json_encode($top, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === "use") {
|
||||
$id = $in["id"] ?? "";
|
||||
foreach ($kb as &$entry) {
|
||||
if ($entry["id"] === $id) {
|
||||
$entry["use_count"] = ($entry["use_count"] ?? 0) + 1;
|
||||
@file_put_contents($store_file, json_encode($kb, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
|
||||
echo json_encode(["ok"=>true, "use_count"=>$entry["use_count"]]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
echo json_encode(["error"=>"not found"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === "stats") {
|
||||
$kinds = [];
|
||||
$total_uses = 0;
|
||||
foreach ($kb as $e) {
|
||||
$k = $e["kind"] ?? "flowchart";
|
||||
$kinds[$k] = ($kinds[$k] ?? 0) + 1;
|
||||
$total_uses += ($e["use_count"] ?? 0);
|
||||
}
|
||||
echo json_encode([
|
||||
"total_diagrams" => count($kb),
|
||||
"by_kind" => $kinds,
|
||||
"total_uses" => $total_uses,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// default: list all
|
||||
echo json_encode([
|
||||
"total" => count($kb),
|
||||
"items" => array_slice(array_reverse($kb), 0, 20),
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
25
api/ambre-or-models.php
Normal file
25
api/ambre-or-models.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$secrets = @file_get_contents("/etc/weval/secrets.env");
|
||||
preg_match("/^OPENROUTER_KEY=(\S+)/m", $secrets ?? "", $m);
|
||||
$or_key = $m[1] ?? "";
|
||||
|
||||
$ch = curl_init("https://openrouter.ai/api/v1/models");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_HTTPHEADER => ["Authorization: Bearer $or_key"],
|
||||
]);
|
||||
$raw = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$d = json_decode($raw, true);
|
||||
$online = [];
|
||||
$sonar = [];
|
||||
foreach ($d["data"] ?? [] as $m) {
|
||||
$id = $m["id"] ?? "";
|
||||
if (strpos($id, "online") !== false || strpos($id, "sonar") !== false || strpos($id, "perplexity") !== false) {
|
||||
$online[] = $id;
|
||||
}
|
||||
}
|
||||
echo json_encode(["online_models" => $online, "total_models" => count($d["data"] ?? [])], JSON_PRETTY_PRINT);
|
||||
31
api/ambre-oss-state.php
Normal file
31
api/ambre-oss-state.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$files = [
|
||||
"/var/www/html/api/oss-registry.json",
|
||||
"/var/www/html/oss-registry.json",
|
||||
"/var/www/html/oss-catalog.html",
|
||||
];
|
||||
$out = [];
|
||||
foreach ($files as $f) {
|
||||
if (file_exists($f)) {
|
||||
$out[basename($f)] = [
|
||||
"size" => filesize($f),
|
||||
"mtime" => date("Y-m-d H:i", filemtime($f)),
|
||||
];
|
||||
if (substr($f, -5) === ".json") {
|
||||
$data = json_decode(@file_get_contents($f), true);
|
||||
$out[basename($f)]["tool_count"] = is_array($data) ? count($data) : 0;
|
||||
if (is_array($data)) {
|
||||
$cats = [];
|
||||
foreach ($data as $d) {
|
||||
$c = $d["category"] ?? $d["cat"] ?? "unknown";
|
||||
$cats[$c] = ($cats[$c] ?? 0) + 1;
|
||||
}
|
||||
$out[basename($f)]["cats"] = $cats;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$out[basename($f)] = "NOT FOUND";
|
||||
}
|
||||
}
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
31
api/ambre-parse-all.php
Normal file
31
api/ambre-parse-all.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
|
||||
$wevia = @file_get_contents("/var/www/html/wevia.html");
|
||||
|
||||
// Extract all <script> contents and try each with node
|
||||
$pos = 0; $n = 0;
|
||||
while (($m = strpos($wevia, "<script", $pos)) !== false) {
|
||||
$tag_end = strpos($wevia, ">", $m);
|
||||
if ($tag_end === false) break;
|
||||
$tag = substr($wevia, $m, $tag_end - $m + 1);
|
||||
// Skip if has src=
|
||||
if (strpos($tag, "src=") !== false) { $pos = $tag_end + 1; continue; }
|
||||
$end = strpos($wevia, "</script>", $tag_end);
|
||||
if ($end === false) break;
|
||||
$content = substr($wevia, $tag_end + 1, $end - $tag_end - 1);
|
||||
if (strlen($content) < 50) { $pos = $end + 9; continue; }
|
||||
|
||||
$n++;
|
||||
$abs_line = substr_count(substr($wevia, 0, $tag_end + 1), "\n") + 1;
|
||||
$tmp = "/tmp/wscript-$n.js";
|
||||
file_put_contents($tmp, $content);
|
||||
$parse = @shell_exec("node --check $tmp 2>&1");
|
||||
if (trim($parse)) {
|
||||
echo "=== Script #$n (starts abs line $abs_line, " . strlen($content) . "B) ===\n";
|
||||
echo "$parse\n\n";
|
||||
} else {
|
||||
echo "Script #$n (line $abs_line, " . strlen($content) . "B): OK\n";
|
||||
}
|
||||
$pos = $end + 9;
|
||||
}
|
||||
79
api/ambre-pdf-enh.php
Normal file
79
api/ambre-pdf-enh.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$path = "/var/www/html/api/ambre-tool-pdf-premium.php";
|
||||
$c = @file_get_contents($path);
|
||||
|
||||
// Enhance the system prompt to suggest best chart type based on topic
|
||||
$old_sys = '"chart_data\": {\n \"type\": \"bar\",';
|
||||
$new_sys = '"chart_data\": {\n \"type\": \"bar\", // or \"pie\", \"line\", \"doughnut\", \"radar\", \"polarArea\" selon le sujet',
|
||||
|
||||
if (strpos($c, $old_sys) !== false) {
|
||||
$c = str_replace($old_sys, $new_sys, $c);
|
||||
}
|
||||
|
||||
// Enhance the rendered Chart.js config to handle different types with proper options
|
||||
$old_js_chart = 'new Chart(ctx, {
|
||||
type: cd.type || "$chart_type",
|
||||
data: {
|
||||
labels: cd.labels || [],
|
||||
datasets: [{
|
||||
label: cd.title || "Données",
|
||||
data: cd.values || [],
|
||||
backgroundColor: ["#6366f1","#8b5cf6","#3b82f6","#06b6d4","#10b981","#f59e0b","#ef4444","#ec4899"],
|
||||
borderColor: "#4338ca",
|
||||
borderWidth: 2,
|
||||
borderRadius: 6,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
plugins: { legend: { display: false }, title: { display: true, text: cd.title, color: "#334155", font:{size:14}}},
|
||||
scales: { y: { beginAtZero: true, grid:{color:"#f1f5f9"}}, x: {grid:{display:false}}},
|
||||
}
|
||||
});';
|
||||
|
||||
$new_js_chart = 'var _chartType = cd.type || "bar";
|
||||
var _palette = ["#6366f1","#8b5cf6","#3b82f6","#06b6d4","#10b981","#f59e0b","#ef4444","#ec4899","#84cc16","#f97316"];
|
||||
var _dataset = {
|
||||
label: cd.title || "Données",
|
||||
data: cd.values || [],
|
||||
backgroundColor: (["pie","doughnut","polarArea"].indexOf(_chartType) >= 0) ? _palette : _palette.slice(0, (cd.values||[]).length),
|
||||
borderColor: (["line","radar"].indexOf(_chartType) >= 0) ? "#6366f1" : "#4338ca",
|
||||
borderWidth: (["pie","doughnut","polarArea"].indexOf(_chartType) >= 0) ? 2 : (["line","radar"].indexOf(_chartType) >= 0 ? 3 : 2),
|
||||
borderRadius: (_chartType === "bar") ? 6 : 0,
|
||||
tension: (_chartType === "line") ? 0.35 : 0,
|
||||
fill: (_chartType === "line") ? false : true,
|
||||
pointBackgroundColor: "#6366f1",
|
||||
pointRadius: (["line","radar"].indexOf(_chartType) >= 0) ? 5 : 0,
|
||||
};
|
||||
var _showLegend = (["pie","doughnut","polarArea","radar"].indexOf(_chartType) >= 0);
|
||||
var _showScales = (["pie","doughnut","polarArea","radar"].indexOf(_chartType) < 0);
|
||||
new Chart(ctx, {
|
||||
type: _chartType,
|
||||
data: { labels: cd.labels || [], datasets: [_dataset] },
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
plugins: {
|
||||
legend: { display: _showLegend, position: "bottom", labels: {boxWidth: 12, font: {size: 11}}},
|
||||
title: { display: true, text: cd.title || "Données", color: "#334155", font: {size: 14, weight: "600"}, padding: {top: 4, bottom: 14}}
|
||||
},
|
||||
scales: _showScales ? { y: { beginAtZero: true, grid: {color: "#f1f5f9"}}, x: {grid: {display: false}}} : {},
|
||||
}
|
||||
});';
|
||||
|
||||
if (strpos($c, $old_js_chart) !== false) {
|
||||
$c = str_replace($old_js_chart, $new_js_chart, $c);
|
||||
}
|
||||
|
||||
// Save
|
||||
$backup = "/opt/wevads/vault/pdf-premium.GOLD-" . date("Ymd-His") . "-chart-types";
|
||||
@copy($path, $backup);
|
||||
$wrote = @file_put_contents($path, $c);
|
||||
|
||||
echo json_encode([
|
||||
"wrote" => $wrote,
|
||||
"size" => strlen($c),
|
||||
"backup" => basename($backup),
|
||||
]);
|
||||
54
api/ambre-pdf-memory.php
Normal file
54
api/ambre-pdf-memory.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
|
||||
// 1. Search wikis about "pdf premium" or "pdf graphique"
|
||||
$wiki_hits = [];
|
||||
foreach (glob("/opt/obsidian-vault/**/*.md") as $f) {
|
||||
$content = @file_get_contents($f);
|
||||
if (!$content) continue;
|
||||
if (stripos($content, "pdf premium") !== false ||
|
||||
stripos($content, "weasyprint") !== false ||
|
||||
stripos($content, "pdf graphique") !== false ||
|
||||
stripos($content, "reportlab") !== false ||
|
||||
stripos($content, "pdf chart") !== false ||
|
||||
stripos($content, "pdfmake") !== false) {
|
||||
$wiki_hits[] = [
|
||||
"file" => str_replace("/opt/obsidian-vault/", "", $f),
|
||||
"size" => filesize($f),
|
||||
];
|
||||
}
|
||||
}
|
||||
$out["wiki_hits_pdf"] = array_slice($wiki_hits, 0, 15);
|
||||
|
||||
// 2. Existing PDF endpoints
|
||||
$out["pdf_endpoints"] = [];
|
||||
foreach (glob("/var/www/html/api/*pdf*.php") as $f) {
|
||||
$out["pdf_endpoints"][] = [
|
||||
"name" => basename($f),
|
||||
"size" => filesize($f),
|
||||
"mtime" => date("Y-m-d H:i", filemtime($f)),
|
||||
];
|
||||
}
|
||||
|
||||
// 3. PDF generation binaries available
|
||||
$out["pdf_tools"] = [
|
||||
"wkhtmltopdf" => trim(@shell_exec("which wkhtmltopdf") ?: "NO"),
|
||||
"weasyprint" => trim(@shell_exec("which weasyprint") ?: "NO"),
|
||||
"pandoc" => trim(@shell_exec("which pandoc") ?: "NO"),
|
||||
"chromium" => trim(@shell_exec("which chromium chromium-browser google-chrome 2>&1 | head -1") ?: "NO"),
|
||||
"reportlab" => trim(@shell_exec("python3 -c 'import reportlab; print(reportlab.__version__)' 2>&1") ?: "NO"),
|
||||
"matplotlib" => trim(@shell_exec("python3 -c 'import matplotlib; print(matplotlib.__version__)' 2>&1") ?: "NO"),
|
||||
"plotly" => trim(@shell_exec("python3 -c 'import plotly; print(plotly.__version__)' 2>&1") ?: "NO"),
|
||||
];
|
||||
|
||||
// 4. Earlier wiki sessions about PDF
|
||||
$recent_sessions = [];
|
||||
foreach (glob("/opt/obsidian-vault/sessions/*.md") as $f) {
|
||||
$base = basename($f);
|
||||
$recent_sessions[] = ["name"=>$base, "mtime"=>date("Y-m-d", filemtime($f))];
|
||||
}
|
||||
usort($recent_sessions, function($a,$b){return strcmp($b["mtime"], $a["mtime"]);});
|
||||
$out["recent_sessions"] = array_slice($recent_sessions, 0, 10);
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
21
api/ambre-pdf-patch.php
Normal file
21
api/ambre-pdf-patch.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$path = "/var/www/html/api/ambre-tool-pdf-premium.php";
|
||||
$content = file_get_contents($path);
|
||||
|
||||
$old = 'if (file_exists("/usr/bin/chromium-browser") || file_exists("/usr/bin/chromium") || file_exists("/usr/bin/google-chrome")) {
|
||||
$bin = file_exists("/usr/bin/chromium-browser") ? "/usr/bin/chromium-browser" : (file_exists("/usr/bin/chromium") ? "/usr/bin/chromium" : "/usr/bin/google-chrome");';
|
||||
|
||||
// Prefer google-chrome (real chrome), skip chromium-browser stub
|
||||
$new = 'if (file_exists("/usr/bin/google-chrome") || file_exists("/usr/bin/chromium") || file_exists("/usr/bin/chromium-browser")) {
|
||||
// Prefer real chrome over stub chromium-browser snap
|
||||
$bin = file_exists("/usr/bin/google-chrome") ? "/usr/bin/google-chrome" : (file_exists("/usr/bin/chromium") ? "/usr/bin/chromium" : "/usr/bin/chromium-browser");';
|
||||
|
||||
if (strpos($content, $old) === false) {
|
||||
echo json_encode(["error"=>"pattern not found"]); exit;
|
||||
}
|
||||
$new_content = str_replace($old, $new, $content);
|
||||
$backup = "/opt/wevads/vault/pdf-premium.GOLD-" . date("Ymd-His");
|
||||
@copy($path, $backup);
|
||||
$wrote = @file_put_contents($path, $new_content);
|
||||
echo json_encode(["delta" => strlen($new_content)-strlen($content), "wrote"=>$wrote, "backup"=>basename($backup)]);
|
||||
4
api/ambre-pw-check.php
Normal file
4
api/ambre-pw-check.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$proc = @shell_exec("pgrep -af "playwright test" | head -5");
|
||||
echo json_encode(["running"=>trim($proc ?: "none")]);
|
||||
5
api/ambre-pw-cleanup.php
Normal file
5
api/ambre-pw-cleanup.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/tests";
|
||||
@unlink("$base/capabilities-v11.spec.js");
|
||||
echo json_encode(["specs" => array_map("basename", glob("$base/*.spec.js"))]);
|
||||
6
api/ambre-pw-debug.php
Normal file
6
api/ambre-pw-debug.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/tests";
|
||||
@unlink("$base/conversation-v12.spec.js");
|
||||
$written = @file_put_contents("$base/debug-trace.spec.js", base64_decode("Y29uc3QgeyB0ZXN0IH0gPSByZXF1aXJlKCJAcGxheXdyaWdodC90ZXN0Iik7Cgp0ZXN0KCJWMTItZGVidWcgwrcgdHJhY2Ugd2hhdCBoYXBwZW5zIG9uIHNpbXBsZSBtZXNzYWdlIiwgYXN5bmMgKHsgcGFnZSB9KSA9PiB7CiAgdGVzdC5zZXRUaW1lb3V0KDYwMDAwKTsKICAKICBjb25zdCBsb2dzID0gW107CiAgY29uc3QgZXJyb3JzID0gW107CiAgY29uc3QgbmV0d29ya3MgPSBbXTsKICAKICBwYWdlLm9uKCJjb25zb2xlIiwgbSA9PiBsb2dzLnB1c2goYFske20udHlwZSgpfV0gJHttLnRleHQoKS5zdWJzdHJpbmcoMCwzMDApfWApKTsKICBwYWdlLm9uKCJwYWdlZXJyb3IiLCBlID0+IGVycm9ycy5wdXNoKGUubWVzc2FnZS5zdWJzdHJpbmcoMCwzMDApKSk7CiAgcGFnZS5vbigicmVxdWVzdCIsIHIgPT4gewogICAgaWYgKHIudXJsKCkuaW5jbHVkZXMoImFtYnJlIikgfHwgci51cmwoKS5pbmNsdWRlcygic292ZXJlaWduIikgfHwgci51cmwoKS5pbmNsdWRlcygibWFzdGVyIikpIHsKICAgICAgbmV0d29ya3MucHVzaChg4oaSICR7ci5tZXRob2QoKX0gJHtyLnVybCgpLnN1YnN0cmluZygwLDEyMCl9YCk7CiAgICB9CiAgfSk7CiAgcGFnZS5vbigicmVzcG9uc2UiLCByID0+IHsKICAgIGlmIChyLnVybCgpLmluY2x1ZGVzKCJhbWJyZSIpIHx8IHIudXJsKCkuaW5jbHVkZXMoInNvdmVyZWlnbiIpIHx8IHIudXJsKCkuaW5jbHVkZXMoIm1hc3RlciIpKSB7CiAgICAgIG5ldHdvcmtzLnB1c2goYOKGkCAke3Iuc3RhdHVzKCl9ICR7ci51cmwoKS5zdWJzdHJpbmcoMCwxMjApfWApOwogICAgfQogIH0pOwogIAogIGF3YWl0IHBhZ2UuZ290bygiL3dldmlhLmh0bWwiKTsKICBhd2FpdCBwYWdlLndhaXRGb3JMb2FkU3RhdGUoIm5ldHdvcmtpZGxlIik7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVGltZW91dCgyMDAwKTsKICAKICAvLyBMb2cgZ2xvYmFsIHN0YXRlCiAgY29uc3Qgc3RhdGUxID0gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiAoewogICAgaGFzVjVNZW1vcnlWMjogZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50Lm91dGVySFRNTC5pbmNsdWRlcygiQU1CUkUtVjUtTUVNT1JZLXYyIiksCiAgICBoYXNTZW5kTXNnOiB0eXBlb2Ygd2luZG93LnNlbmQgPT09ICJmdW5jdGlvbiIsCiAgICBidXN5OiB0eXBlb2YgYnVzeSAhPT0gInVuZGVmaW5lZCIgPyBidXN5IDogInVuZGVmaW5lZCIsCiAgfSkpOwogIGNvbnNvbGUubG9nKCJTVEFURSBBVCBMT0FEOiIsIEpTT04uc3RyaW5naWZ5KHN0YXRlMSkpOwogIAogIC8vIFNlbmQgYSBtZXNzYWdlCiAgY29uc3QgaW5wdXQgPSBwYWdlLmxvY2F0b3IoIiNtc2dJbnB1dCIpOwogIGF3YWl0IGlucHV0LmZpbGwoImJvbmpvdXIgamUgc3VpcyBZYWNpbmUgY29tbWVudCBjYSB2YSBhdWpvdXJkIGh1aSIpOwogIGF3YWl0IHBhZ2Uud2FpdEZvclRpbWVvdXQoNTAwKTsKICBhd2FpdCBpbnB1dC5wcmVzcygiRW50ZXIiKTsKICBjb25zb2xlLmxvZygiTUVTU0FHRSBTRU5UIik7CiAgCiAgLy8gV2FpdCBhbmQgY2FwdHVyZSB3aGF0IGhhcHBlbnMKICBhd2FpdCBwYWdlLndhaXRGb3JUaW1lb3V0KDE1MDAwKTsKICAKICBjb25zdCBzdGF0ZTIgPSBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+ICh7CiAgICBhc3Npc3RhbnRfY291bnQ6IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIi5tc2cuYXNzaXN0YW50IikubGVuZ3RoLAogICAgbGFzdF9hc3Npc3RhbnQ6IEFycmF5LmZyb20oZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgiLm1zZy5hc3Npc3RhbnQiKSkuc2xpY2UoLTEpWzBdPy5pbm5lclRleHQ/LnN1YnN0cmluZygwLDMwMCksCiAgICBidXN5OiB0eXBlb2YgYnVzeSAhPT0gInVuZGVmaW5lZCIgPyBidXN5IDogInVuZGVmaW5lZCIsCiAgICBzZXNzaW9uX2lkOiB3aW5kb3cuX2FtYnJlX3Nlc3Npb25faWQsCiAgfSkpOwogIGNvbnNvbGUubG9nKCJTVEFURSAxNXMgbGF0ZXI6IiwgSlNPTi5zdHJpbmdpZnkoc3RhdGUyKSk7CiAgCiAgY29uc29sZS5sb2coIlxuPT09IE5FVFdPUksgPT09Iik7CiAgbmV0d29ya3MuZm9yRWFjaChuID0+IGNvbnNvbGUubG9nKG4pKTsKICBjb25zb2xlLmxvZygiXG49PT0gQ09OU09MRSBMT0dTID09PSIpOwogIGxvZ3Muc2xpY2UoMCwgMzApLmZvckVhY2gobCA9PiBjb25zb2xlLmxvZyhsKSk7CiAgY29uc29sZS5sb2coIlxuPT09IFBBR0UgRVJST1JTID09PSIpOwogIGVycm9ycy5mb3JFYWNoKGUgPT4gY29uc29sZS5sb2coZSkpOwp9KTsK"));
|
||||
echo json_encode(["written"=>$written]);
|
||||
6
api/ambre-pw-debug2.php
Normal file
6
api/ambre-pw-debug2.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/tests";
|
||||
@unlink("$base/debug-trace.spec.js");
|
||||
$written = @file_put_contents("$base/debug2.spec.js", base64_decode("Y29uc3QgeyB0ZXN0IH0gPSByZXF1aXJlKCJAcGxheXdyaWdodC90ZXN0Iik7Cgp0ZXN0KCJWMTItZGVidWcyIMK3IGNhcHR1cmUgdGhlIGV4YWN0IGVycm9yIHNvdXJjZSIsIGFzeW5jICh7IHBhZ2UgfSkgPT4gewogIHRlc3Quc2V0VGltZW91dCg2MDAwMCk7CiAgCiAgY29uc3QgZXJyb3JzID0gW107CiAgY29uc3Qgd2FybmluZ3MgPSBbXTsKICBjb25zdCBzY3JpcHRFcnJvcnMgPSBbXTsKICAKICBwYWdlLm9uKCJjb25zb2xlIiwgbSA9PiB7CiAgICBjb25zdCB0ID0gbS50ZXh0KCk7CiAgICBpZiAobS50eXBlKCkgPT09ICJ3YXJuaW5nIikgd2FybmluZ3MucHVzaCh0LnN1YnN0cmluZygwLDQwMCkpOwogICAgaWYgKG0udHlwZSgpID09PSAiZXJyb3IiKSBlcnJvcnMucHVzaCh0LnN1YnN0cmluZygwLDQwMCkpOwogIH0pOwogIHBhZ2Uub24oInBhZ2VlcnJvciIsIGUgPT4gewogICAgc2NyaXB0RXJyb3JzLnB1c2goYCR7ZS5uYW1lfTogJHtlLm1lc3NhZ2V9XG4keyhlLnN0YWNrfHwnJykuc3Vic3RyaW5nKDAsNTAwKX1gKTsKICB9KTsKICAKICBhd2FpdCBwYWdlLmdvdG8oIi93ZXZpYS5odG1sIik7CiAgYXdhaXQgcGFnZS53YWl0Rm9yTG9hZFN0YXRlKCJuZXR3b3JraWRsZSIpOwogIGF3YWl0IHBhZ2Uud2FpdEZvclRpbWVvdXQoMzAwMCk7CiAgCiAgY29uc29sZS5sb2coIj09PSBTQ1JJUFQgRVJST1JTID09PSIpOwogIHNjcmlwdEVycm9ycy5mb3JFYWNoKGUgPT4gY29uc29sZS5sb2coZSkpOwogIGNvbnNvbGUubG9nKCJcbj09PSBXQVJOSU5HUyA9PT0iKTsKICB3YXJuaW5ncy5zbGljZSgwLDEwKS5mb3JFYWNoKHcgPT4gY29uc29sZS5sb2codykpOwogIGNvbnNvbGUubG9nKCJcbj09PSBDT05TT0xFIEVSUk9SUyA9PT0iKTsKICBlcnJvcnMuc2xpY2UoMCwxMCkuZm9yRWFjaChlID0+IGNvbnNvbGUubG9nKGUpKTsKICAKICAvLyBOb3cgdHJ5IHRvIHNlbmQgYSBtZXNzYWdlCiAgY29uc3QgaW5wdXQgPSBwYWdlLmxvY2F0b3IoIiNtc2dJbnB1dCIpOwogIGF3YWl0IGlucHV0LmZpbGwoInNhbHV0IFlhY2luZSBpY2kgY29tbWVudCBjYSB2YSIpOwogIGF3YWl0IGlucHV0LnByZXNzKCJFbnRlciIpOwogIGF3YWl0IHBhZ2Uud2FpdEZvclRpbWVvdXQoMTIwMDApOwogIAogIGNvbnNvbGUubG9nKCJcbj09PSBBRlRFUiBTRU5EIEVSUk9SUyA9PT0iKTsKICBzY3JpcHRFcnJvcnMuZm9yRWFjaChlID0+IGNvbnNvbGUubG9nKGUuc3Vic3RyaW5nKDAsNDAwKSkpOwogIGNvbnNvbGUubG9nKCJcbj09PSBBRlRFUiBTRU5EIFdBUk5JTkdTID09PSIpOwogIHdhcm5pbmdzLmZvckVhY2godyA9PiBjb25zb2xlLmxvZyh3LnN1YnN0cmluZygwLDQwMCkpKTsKICAKICBjb25zdCBsYXN0ID0gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7CiAgICBjb25zdCBtc2dzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgiLm1zZy5hc3Npc3RhbnQiKTsKICAgIHJldHVybiBtc2dzLmxlbmd0aCA+IDAgPyBtc2dzW21zZ3MubGVuZ3RoLTFdLmlubmVyVGV4dC5zdWJzdHJpbmcoMCwyNTApIDogIm5vIG1zZyI7CiAgfSk7CiAgY29uc29sZS5sb2coIlxuTGFzdCBhc3Npc3RhbnQgbXNnOiIsIGxhc3QpOwp9KTsK"));
|
||||
echo json_encode(["written"=>$written]);
|
||||
33
api/ambre-pw-deep.php
Normal file
33
api/ambre-pw-deep.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
|
||||
// Read playwright-check.sh
|
||||
$f = "/var/www/html/api/v76-scripts/playwright-check.sh";
|
||||
if (file_exists($f)) $out["pw_check_sh"] = @file_get_contents($f);
|
||||
|
||||
// Targeted config locations
|
||||
$cfgs = ["/var/www/html/playwright.config.js", "/var/www/html/playwright.config.ts",
|
||||
"/var/www/html/api/playwright.config.js", "/opt/weval-l99/playwright.config.js"];
|
||||
foreach ($cfgs as $c) if (file_exists($c)) $out["config_$c"] = substr(@file_get_contents($c), 0, 600);
|
||||
|
||||
// Find recent spec files in common dirs (no find/ recursive)
|
||||
foreach (["/var/www/html", "/var/www/html/tests", "/var/www/html/api", "/opt/weval-l99/tests"] as $d) {
|
||||
foreach (glob("$d/*.spec.{js,ts}", GLOB_BRACE) as $s) $out["specs"][] = $s;
|
||||
foreach (glob("$d/tests/*.spec.{js,ts}", GLOB_BRACE) as $s) $out["specs"][] = $s;
|
||||
}
|
||||
|
||||
// which playwright
|
||||
$out["which_npx"] = trim(@shell_exec("which npx 2>&1") ?: "");
|
||||
$out["which_playwright"] = trim(@shell_exec("which playwright 2>&1") ?: "");
|
||||
|
||||
// Look for package.json with playwright
|
||||
foreach (["/var/www/html/package.json", "/var/www/html/api/package.json", "/opt/weval-l99/package.json"] as $p) {
|
||||
if (file_exists($p)) {
|
||||
$pkg = @json_decode(@file_get_contents($p), true);
|
||||
$has_pw = isset($pkg["devDependencies"]["@playwright/test"]) || isset($pkg["dependencies"]["@playwright/test"]);
|
||||
if ($has_pw || $pkg) $out["pkg_$p"] = ["has_playwright"=>$has_pw, "scripts"=>$pkg["scripts"]??[]];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
34
api/ambre-pw-files.php
Normal file
34
api/ambre-pw-files.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["v2_screenshots"=>[], "v2_videos"=>[], "v1_videos"=>[]];
|
||||
|
||||
foreach (glob("$base/v2-*.png") as $p) {
|
||||
$out["v2_screenshots"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
|
||||
// V2 webm in new subdir
|
||||
foreach (glob("$base/chat-capabilities-v2-*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v2_videos"][] = [
|
||||
"name" => basename($w),
|
||||
"size_kb" => round(filesize($w)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
];
|
||||
}
|
||||
|
||||
// V1 webm
|
||||
foreach (glob("$base/chat-capabilities-WEVIA-*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v1_videos"][] = [
|
||||
"name" => basename($w),
|
||||
"size_kb" => round(filesize($w)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
12
api/ambre-pw-install.php
Normal file
12
api/ambre-pw-install.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
// Try npm install
|
||||
echo "=== npm install @playwright/test ===\n";
|
||||
$out = @shell_exec("cd $base && timeout 120 npm install --no-audit --no-fund @playwright/test 2>&1 | tail -40");
|
||||
echo $out ?? "(no output)";
|
||||
echo "\n\n=== node_modules check ===\n";
|
||||
echo @shell_exec("ls -la $base/node_modules/.bin/ 2>&1 | head -20");
|
||||
echo "\n\n=== try run after install ===\n";
|
||||
$run = @shell_exec("cd $base && timeout 3 ./node_modules/.bin/playwright --version 2>&1");
|
||||
echo $run ?? "(no output)";
|
||||
6
api/ambre-pw-kill.php
Normal file
6
api/ambre-pw-kill.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
@shell_exec("pkill -f playwright 2>&1");
|
||||
@shell_exec("pkill -f v17-6sigma 2>&1");
|
||||
echo "killed\n";
|
||||
echo @shell_exec("pgrep -af playwright");
|
||||
11
api/ambre-pw-killall.php
Normal file
11
api/ambre-pw-killall.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
// Try multiple kill methods
|
||||
$out["pkill_user"] = trim(@shell_exec("pkill -9 -u www-data -f playwright 2>&1") ?: "(empty)");
|
||||
$out["pkill_f_all"] = trim(@shell_exec("pkill -9 -f chat-capabilities 2>&1") ?: "(empty)");
|
||||
$out["pkill_node"] = trim(@shell_exec("pkill -9 -f "node.*playwright" 2>&1") ?: "(empty)");
|
||||
sleep(2);
|
||||
$out["remaining"] = trim(@shell_exec("ps -ef | grep -E "playwright|chromium.*headless" | grep -v grep | wc -l") ?: "0");
|
||||
$out["procs_snapshot"] = trim(@shell_exec("ps -ef | grep playwright | grep -v grep | awk "{print \$2, \$10, \$11}" | head -10") ?: "");
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
13
api/ambre-pw-listnow.php
Normal file
13
api/ambre-pw-listnow.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["pngs"=>[], "videos"=>[]];
|
||||
foreach (glob("$base/v*.png") as $p) {
|
||||
$out["pngs"][] = ["name"=>basename($p), "kb"=>round(filesize($p)/1024,1), "mtime"=>gmdate("H:i:s", filemtime($p))];
|
||||
}
|
||||
usort($out["pngs"], function($a,$b){return strcmp($a["name"], $b["name"]);});
|
||||
|
||||
foreach (glob("$base/chat-capabilities-v*/*.webm") as $w) {
|
||||
$out["videos"][] = ["name"=>basename(dirname($w)), "mb"=>round(filesize($w)/1048576,2), "mtime"=>gmdate("H:i:s", filemtime($w))];
|
||||
}
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
8
api/ambre-pw-log.php
Normal file
8
api/ambre-pw-log.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$logs = glob("/tmp/ambre-pw-*.log");
|
||||
foreach ($logs as $l) {
|
||||
echo "=== " . basename($l) . " (size=" . filesize($l) . "B) ===\n";
|
||||
echo @file_get_contents($l, false, null, 0, 3000);
|
||||
echo "\n\n";
|
||||
}
|
||||
10
api/ambre-pw-procs.php
Normal file
10
api/ambre-pw-procs.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$procs = @shell_exec("pgrep -af playwright 2>&1 | head -5");
|
||||
$logs = glob("/tmp/ambre-pw-run-*.log");
|
||||
usort($logs, function($a,$b){return filemtime($b)-filemtime($a);});
|
||||
echo json_encode([
|
||||
"procs" => trim($procs ?: ""),
|
||||
"latest_log" => $logs ? basename($logs[0]) : "",
|
||||
"latest_size" => $logs ? filesize($logs[0]) : 0,
|
||||
], JSON_PRETTY_PRINT);
|
||||
18
api/ambre-pw-readlog-latest.php
Normal file
18
api/ambre-pw-readlog-latest.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
// Get the debug2 log specifically
|
||||
$log = "/tmp/ambre-pw-run-20260421-215101.log";
|
||||
if (file_exists($log)) {
|
||||
echo "=== $log ===\n";
|
||||
echo "Size: " . filesize($log) . "B\n\n";
|
||||
echo @file_get_contents($log);
|
||||
} else {
|
||||
// Get latest log
|
||||
$logs = glob("/tmp/ambre-pw-run-*.log");
|
||||
usort($logs, function($a,$b){return filemtime($b)-filemtime($a);});
|
||||
if ($logs) {
|
||||
echo "=== " . basename($logs[0]) . " ===\n";
|
||||
echo "Size: " . filesize($logs[0]) . "B\n\n";
|
||||
echo @file_get_contents($logs[0]);
|
||||
}
|
||||
}
|
||||
15
api/ambre-pw-readlog.php
Normal file
15
api/ambre-pw-readlog.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
// Find latest pw run log
|
||||
$logs = glob("/tmp/ambre-pw-run-*.log");
|
||||
usort($logs, function($a,$b){return filemtime($b)-filemtime($a);});
|
||||
if (empty($logs)) { echo "NO LOG FILES\n"; exit; }
|
||||
$latest = $logs[0];
|
||||
echo "=== $latest ===\n";
|
||||
echo "Size: " . filesize($latest) . "B\n\n";
|
||||
echo @file_get_contents($latest);
|
||||
echo "\n\n=== Test dir listing ===\n";
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
echo @shell_exec("ls -la $base/ 2>&1");
|
||||
echo "\n=== Output dir ===\n";
|
||||
echo @shell_exec("ls -la $base/output/ 2>&1");
|
||||
24
api/ambre-pw-run.php
Normal file
24
api/ambre-pw-run.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
$ts = date("Ymd-His");
|
||||
$log = "/tmp/ambre-pw-run-$ts.log";
|
||||
|
||||
// Use LOCAL playwright
|
||||
$cmd = "cd $base && ./node_modules/.bin/playwright test --config=playwright.config.js 2>&1";
|
||||
|
||||
// Background launch
|
||||
$full_cmd = "nohup bash -c '$cmd > $log 2>&1' > /dev/null 2>&1 & echo \$!";
|
||||
$pid = trim(@shell_exec($full_cmd) ?? "");
|
||||
|
||||
sleep(2);
|
||||
// Check alive
|
||||
$alive = trim(@shell_exec("ps -p $pid -o pid= 2>/dev/null") ?? "");
|
||||
|
||||
echo json_encode([
|
||||
"ok" => true,
|
||||
"pid" => $pid,
|
||||
"alive" => !empty($alive),
|
||||
"log" => $log,
|
||||
"cmd" => $cmd,
|
||||
], JSON_PRETTY_PRINT);
|
||||
24
api/ambre-pw-search.php
Normal file
24
api/ambre-pw-search.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
|
||||
// Latest webm videos - use recursive glob pattern with mtime
|
||||
$dirs = glob("/var/www/html/api/playwright-results/*", GLOB_ONLYDIR);
|
||||
$webms = [];
|
||||
foreach (array_slice($dirs, -20) as $subdir) {
|
||||
foreach (glob("$subdir/*.webm") as $w) {
|
||||
$webms[] = ["file"=>basename($w), "dir"=>basename($subdir), "size"=>filesize($w), "mtime"=>filemtime($w)];
|
||||
}
|
||||
}
|
||||
usort($webms, function($a,$b){ return $b["mtime"] - $a["mtime"]; });
|
||||
$out["latest_webms"] = array_slice(array_map(function($w){
|
||||
return ["file"=>$w["file"], "dir"=>$w["dir"], "size_kb"=>round($w["size"]/1024,1), "ts"=>gmdate("c", $w["mtime"])];
|
||||
}, $webms), 0, 10);
|
||||
|
||||
// v76-scripts root files only
|
||||
$out["v76_root"] = array_map("basename", array_slice(glob("/var/www/html/api/v76-scripts/*.*"), 0, 20));
|
||||
|
||||
// playwright php endpoints
|
||||
$out["pw_php"] = array_map("basename", glob("/var/www/html/api/*playwright*.php"));
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
116
api/ambre-pw-setup.php
Normal file
116
api/ambre-pw-setup.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
@mkdir("$base", 0755, true);
|
||||
@mkdir("$base/tests", 0755, true);
|
||||
@mkdir("$base/output", 0755, true);
|
||||
|
||||
// === playwright.config.js ===
|
||||
$config = '// @ts-check
|
||||
const { defineConfig, devices } = require("@playwright/test");
|
||||
module.exports = defineConfig({
|
||||
testDir: "./tests",
|
||||
timeout: 120000,
|
||||
retries: 0,
|
||||
workers: 1,
|
||||
use: {
|
||||
baseURL: "https://weval-consulting.com",
|
||||
trace: "off",
|
||||
screenshot: "on",
|
||||
video: "on",
|
||||
viewport: { width: 1280, height: 720 },
|
||||
ignoreHTTPSErrors: true,
|
||||
},
|
||||
outputDir: "./output",
|
||||
reporter: [["list"], ["json", { outputFile: "./output/results.json" }]],
|
||||
projects: [
|
||||
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
|
||||
],
|
||||
});';
|
||||
file_put_contents("$base/playwright.config.js", $config);
|
||||
|
||||
// === package.json ===
|
||||
$pkg = json_encode([
|
||||
"name" => "ambre-chat-tests",
|
||||
"version" => "1.0.0",
|
||||
"scripts" => ["test" => "playwright test"],
|
||||
"devDependencies" => ["@playwright/test" => "^1.58.0"],
|
||||
], JSON_PRETTY_PRINT);
|
||||
file_put_contents("$base/package.json", $pkg);
|
||||
|
||||
// === chat-capabilities.spec.js ===
|
||||
$spec = <<<'JS'
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
const CAPABILITIES = [
|
||||
{ name: "PDF", msg: "Genere un PDF sur: WEVIA demo" },
|
||||
{ name: "Word", msg: "Genere un document Word sur: strategie" },
|
||||
{ name: "PPT", msg: "Genere une presentation sur: pitch" },
|
||||
{ name: "Mermaid", msg: "Genere un schema mermaid pour: flow" },
|
||||
{ name: "Image", msg: "Genere une image: logo" },
|
||||
{ name: "Code", msg: "Ecris le code python pour: fibonacci" },
|
||||
{ name: "Traduire", msg: "Traduis en anglais: bonjour" },
|
||||
{ name: "Ping", msg: "ping" },
|
||||
];
|
||||
|
||||
test.describe("WEVIA Chat Capabilities Test", () => {
|
||||
test("8 capabilities end-to-end with video", async ({ page }) => {
|
||||
test.setTimeout(300000);
|
||||
|
||||
console.log("🎬 Ouverture wevia.html");
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
// Screenshot initial state
|
||||
await page.screenshot({ path: "output/00-initial.png", fullPage: false });
|
||||
console.log("📸 Screenshot initial OK");
|
||||
|
||||
const input = page.locator("#msgInput");
|
||||
const sendBtn = page.locator("#sendBtn, button[onclick*=send], button.send-btn").first();
|
||||
|
||||
for (let i = 0; i < CAPABILITIES.length; i++) {
|
||||
const cap = CAPABILITIES[i];
|
||||
const num = String(i + 1).padStart(2, "0");
|
||||
console.log(`\n🔷 [${i + 1}/8] ${cap.name}: ${cap.msg}`);
|
||||
|
||||
// Clear and type
|
||||
await input.click();
|
||||
await input.fill(cap.msg);
|
||||
await page.waitForTimeout(400);
|
||||
|
||||
// Send (Enter key is fallback)
|
||||
await input.press("Enter");
|
||||
console.log(` Message envoyé`);
|
||||
|
||||
// Wait for response (thinking bubble disappears + message rendered)
|
||||
try {
|
||||
await page.waitForSelector(".msg.assistant:last-of-type .bubble", { timeout: 60000, state: "visible" });
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ Pas de réponse détectée rapidement`);
|
||||
}
|
||||
await page.waitForTimeout(2500);
|
||||
|
||||
// Screenshot de la réponse
|
||||
await page.screenshot({ path: `output/${num}-${cap.name}.png` });
|
||||
console.log(` 📸 Screenshot ${num}-${cap.name}.png capturé`);
|
||||
}
|
||||
|
||||
// Final screenshot
|
||||
await page.waitForTimeout(1500);
|
||||
await page.screenshot({ path: "output/99-final.png", fullPage: true });
|
||||
console.log("\n✅ Test terminé - 8 capabilities enregistrées avec vidéo");
|
||||
});
|
||||
});
|
||||
JS;
|
||||
file_put_contents("$base/tests/chat-capabilities.spec.js", $spec);
|
||||
|
||||
echo json_encode([
|
||||
"ok" => true,
|
||||
"files" => [
|
||||
"config" => filesize("$base/playwright.config.js"),
|
||||
"spec" => filesize("$base/tests/chat-capabilities.spec.js"),
|
||||
"package" => filesize("$base/package.json"),
|
||||
],
|
||||
"base" => $base,
|
||||
], JSON_PRETTY_PRINT);
|
||||
19
api/ambre-pw-status.php
Normal file
19
api/ambre-pw-status.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$procs = @shell_exec("ps auxf 2>/dev/null | grep -E "playwright|chromium" | grep -v grep | head -5");
|
||||
$logs = glob("/tmp/ambre-pw-*.log");
|
||||
$latest_logs = [];
|
||||
foreach ($logs as $l) {
|
||||
$latest_logs[] = [
|
||||
"file" => basename($l),
|
||||
"size" => filesize($l),
|
||||
"tail" => @shell_exec("tail -c 1500 $l 2>/dev/null"),
|
||||
];
|
||||
}
|
||||
$script = "/var/www/html/api/v76-scripts/playwright-check.sh";
|
||||
$first = file_exists($script) ? substr(@file_get_contents($script), 0, 500) : "NOT FOUND";
|
||||
echo json_encode([
|
||||
"running" => trim($procs ?? "none"),
|
||||
"logs" => $latest_logs,
|
||||
"script_preview" => $first,
|
||||
], JSON_PRETTY_PRINT);
|
||||
1
api/ambre-pw-tests/node_modules/.bin/playwright
generated
vendored
Symbolic link
1
api/ambre-pw-tests/node_modules/.bin/playwright
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../@playwright/test/cli.js
|
||||
1
api/ambre-pw-tests/node_modules/.bin/playwright-core
generated
vendored
Symbolic link
1
api/ambre-pw-tests/node_modules/.bin/playwright-core
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../playwright-core/cli.js
|
||||
56
api/ambre-pw-tests/node_modules/.package-lock.json
generated
vendored
Normal file
56
api/ambre-pw-tests/node_modules/.package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "ambre-chat-tests",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
|
||||
"integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.59.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
|
||||
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.59.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
|
||||
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
202
api/ambre-pw-tests/node_modules/@playwright/test/LICENSE
generated
vendored
Normal file
202
api/ambre-pw-tests/node_modules/@playwright/test/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Portions Copyright (c) Microsoft Corporation.
|
||||
Portions Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
5
api/ambre-pw-tests/node_modules/@playwright/test/NOTICE
generated
vendored
Normal file
5
api/ambre-pw-tests/node_modules/@playwright/test/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
Playwright
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
This software contains code derived from the Puppeteer project (https://github.com/puppeteer/puppeteer),
|
||||
available under the Apache 2.0 license (https://github.com/puppeteer/puppeteer/blob/master/LICENSE).
|
||||
170
api/ambre-pw-tests/node_modules/@playwright/test/README.md
generated
vendored
Normal file
170
api/ambre-pw-tests/node_modules/@playwright/test/README.md
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
Playwright is a framework for Web Testing and Automation. It allows testing [Chromium](https://www.chromium.org/Home)<sup>1</sup>, [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable**, and **fast**.
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium<sup>1</sup> <!-- GEN:chromium-version -->147.0.7727.15<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->26.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->148.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||
|
||||
Looking for Playwright for [Python](https://playwright.dev/python/docs/intro), [.NET](https://playwright.dev/dotnet/docs/intro), or [Java](https://playwright.dev/java/docs/intro)?
|
||||
|
||||
<sup>1</sup> Playwright uses [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing) by default.
|
||||
|
||||
## Installation
|
||||
|
||||
Playwright has its own test runner for end-to-end tests, we call it Playwright Test.
|
||||
|
||||
### Using init command
|
||||
|
||||
The easiest way to get started with Playwright Test is to run the init command.
|
||||
|
||||
```Shell
|
||||
# Run from your project's root directory
|
||||
npm init playwright@latest
|
||||
# Or create a new project
|
||||
npm init playwright@latest new-project
|
||||
```
|
||||
|
||||
This will create a configuration file, optionally add examples, a GitHub Action workflow and a first test example.spec.ts. You can now jump directly to writing assertions section.
|
||||
|
||||
### Manually
|
||||
|
||||
Add dependency and install browsers.
|
||||
|
||||
```Shell
|
||||
npm i -D @playwright/test
|
||||
# install supported browsers
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
You can optionally install only selected browsers, see [install browsers](https://playwright.dev/docs/cli#install-browsers) for more details. Or you can install no browsers at all and use existing [browser channels](https://playwright.dev/docs/browsers).
|
||||
|
||||
* [Getting started](https://playwright.dev/docs/intro)
|
||||
* [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Resilient • No flaky tests
|
||||
|
||||
**Auto-wait**. Playwright waits for elements to be actionable prior to performing actions. It also has a rich set of introspection events. The combination of the two eliminates the need for artificial timeouts - a primary cause of flaky tests.
|
||||
|
||||
**Web-first assertions**. Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met.
|
||||
|
||||
**Tracing**. Configure test retry strategy, capture execution trace, videos and screenshots to eliminate flakes.
|
||||
|
||||
### No trade-offs • No limits
|
||||
|
||||
Browsers run web content belonging to different origins in different processes. Playwright is aligned with the architecture of the modern browsers and runs tests out-of-process. This makes Playwright free of the typical in-process test runner limitations.
|
||||
|
||||
**Multiple everything**. Test scenarios that span multiple tabs, multiple origins and multiple users. Create scenarios with different contexts for different users and run them against your server, all in one test.
|
||||
|
||||
**Trusted events**. Hover elements, interact with dynamic controls and produce trusted events. Playwright uses real browser input pipeline indistinguishable from the real user.
|
||||
|
||||
Test frames, pierce Shadow DOM. Playwright selectors pierce shadow DOM and allow entering frames seamlessly.
|
||||
|
||||
### Full isolation • Fast execution
|
||||
|
||||
**Browser contexts**. Playwright creates a browser context for each test. Browser context is equivalent to a brand new browser profile. This delivers full test isolation with zero overhead. Creating a new browser context only takes a handful of milliseconds.
|
||||
|
||||
**Log in once**. Save the authentication state of the context and reuse it in all the tests. This bypasses repetitive log-in operations in each test, yet delivers full isolation of independent tests.
|
||||
|
||||
### Powerful Tooling
|
||||
|
||||
**[Codegen](https://playwright.dev/docs/codegen)**. Generate tests by recording your actions. Save them into any language.
|
||||
|
||||
**[Playwright inspector](https://playwright.dev/docs/inspector)**. Inspect page, generate selectors, step through the test execution, see click points and explore execution logs.
|
||||
|
||||
**[Trace Viewer](https://playwright.dev/docs/trace-viewer)**. Capture all the information to investigate the test failure. Playwright trace contains test execution screencast, live DOM snapshots, action explorer, test source and many more.
|
||||
|
||||
Looking for Playwright for [TypeScript](https://playwright.dev/docs/intro), [JavaScript](https://playwright.dev/docs/intro), [Python](https://playwright.dev/python/docs/intro), [.NET](https://playwright.dev/dotnet/docs/intro), or [Java](https://playwright.dev/java/docs/intro)?
|
||||
|
||||
## Examples
|
||||
|
||||
To learn how to run these Playwright Test examples, check out our [getting started docs](https://playwright.dev/docs/intro).
|
||||
|
||||
#### Page screenshot
|
||||
|
||||
This code snippet navigates to Playwright homepage and saves a screenshot.
|
||||
|
||||
```TypeScript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('Page Screenshot', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
await page.screenshot({ path: `example.png` });
|
||||
});
|
||||
```
|
||||
|
||||
#### Mobile and geolocation
|
||||
|
||||
This snippet emulates Mobile Safari on a device at given geolocation, navigates to maps.google.com, performs the action and takes a screenshot.
|
||||
|
||||
```TypeScript
|
||||
import { test, devices } from '@playwright/test';
|
||||
|
||||
test.use({
|
||||
...devices['iPhone 13 Pro'],
|
||||
locale: 'en-US',
|
||||
geolocation: { longitude: 12.492507, latitude: 41.889938 },
|
||||
permissions: ['geolocation'],
|
||||
})
|
||||
|
||||
test('Mobile and geolocation', async ({ page }) => {
|
||||
await page.goto('https://maps.google.com');
|
||||
await page.getByText('Your location').click();
|
||||
await page.waitForRequest(/.*preview\/pwa/);
|
||||
await page.screenshot({ path: 'colosseum-iphone.png' });
|
||||
});
|
||||
```
|
||||
|
||||
#### Evaluate in browser context
|
||||
|
||||
This code snippet navigates to example.com, and executes a script in the page context.
|
||||
|
||||
```TypeScript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('Evaluate in browser context', async ({ page }) => {
|
||||
await page.goto('https://www.example.com/');
|
||||
const dimensions = await page.evaluate(() => {
|
||||
return {
|
||||
width: document.documentElement.clientWidth,
|
||||
height: document.documentElement.clientHeight,
|
||||
deviceScaleFactor: window.devicePixelRatio
|
||||
}
|
||||
});
|
||||
console.log(dimensions);
|
||||
});
|
||||
```
|
||||
|
||||
#### Intercept network requests
|
||||
|
||||
This code snippet sets up request routing for a page to log all network requests.
|
||||
|
||||
```TypeScript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('Intercept network requests', async ({ page }) => {
|
||||
// Log and continue all network requests
|
||||
await page.route('**', route => {
|
||||
console.log(route.request().url());
|
||||
route.continue();
|
||||
});
|
||||
await page.goto('http://todomvc.com');
|
||||
});
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
* [Documentation](https://playwright.dev)
|
||||
* [API reference](https://playwright.dev/docs/api/class-playwright/)
|
||||
* [Contribution guide](CONTRIBUTING.md)
|
||||
* [Changelog](https://github.com/microsoft/playwright/releases)
|
||||
19
api/ambre-pw-tests/node_modules/@playwright/test/cli.js
generated
vendored
Executable file
19
api/ambre-pw-tests/node_modules/@playwright/test/cli.js
generated
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const { program } = require('playwright/lib/program');
|
||||
program.parse(process.argv);
|
||||
18
api/ambre-pw-tests/node_modules/@playwright/test/index.d.ts
generated
vendored
Normal file
18
api/ambre-pw-tests/node_modules/@playwright/test/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from 'playwright/test';
|
||||
export { default } from 'playwright/test';
|
||||
17
api/ambre-pw-tests/node_modules/@playwright/test/index.js
generated
vendored
Normal file
17
api/ambre-pw-tests/node_modules/@playwright/test/index.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = require('playwright/test');
|
||||
18
api/ambre-pw-tests/node_modules/@playwright/test/index.mjs
generated
vendored
Normal file
18
api/ambre-pw-tests/node_modules/@playwright/test/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from 'playwright/test';
|
||||
export { default } from 'playwright/test';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user