feat: Add plugins — catalogue on/off à effet réel (v7.58.0, phase 8/8)
- app/services/plugins.py + migration 35 : table plugins (slug, name,
description, enabled) pré-remplie avec 3 modules câblés — web-tools,
web-clipper, automations ; ligne absente = activé (défaut sûr)
- automations OFF → dépendance FastAPI posée à l'include_router dans main.py
(aucun router touché) → toutes les routes /workspace/automations* refusées +
garde de tick du scheduler en arrière-plan
- web-clipper OFF → GET /extensions et tout /api/v2/web-clipper/* refusés
- web-tools OFF → web_search et fetch_url retirés du schéma ET de execute()
via ToolRegistry._all() : le LLM ne les voit plus
- UI rendue côté serveur : global Jinja plugin_enabled(slug) — nav
« Extensions » / « Automations » en {% if %} (absentes du DOM), sections
conditionnées en x-show dans settings.html
- menu + : l'entrée « Add plugins » devient vivante (fini disabled:true) —
liste des 3 plugins avec bascule, GET/PATCH /api/agent/plugins[/slug]
(slug inconnu → 404, 401 sans session)
- tests : tests/test_v758_plugins.py (10 tests) — routes refusées (302 hors
/api, 404 JSON pour /api*), outils retirés, nav disparue, persistance,
câblage ; assertions disabled:true == 0 dans les tests des phases 1/3/4/5/7
- livraison : VERSION + app/main = 7.58.0, OpenAPI 525 chemins, CHANGELOG,
ROADMAP phase 8 cochée (menu + complet), avenant phase 8 (docs)
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
{key:'canvases', icon:'🎨', label:'Design System – Canevas',
|
||||
sub:'Galerie de canevas : créer, insérer, enregistrer', action:'canvases', chev:true},
|
||||
{key:'plugins', icon:'🧩', label:'Add plugins',
|
||||
sub:'Modules on/off — bientôt (phase 8)', disabled:true},
|
||||
sub:'Modules on/off — effet réel : routes, UI, outils', action:'plugins', chev:true},
|
||||
{key:'memory', icon:'🧠', label:'Mémoire (on/off)',
|
||||
sub:'Résumés des échanges injectés à chaque run', action:'memory'}
|
||||
];
|
||||
@@ -74,7 +74,7 @@
|
||||
skillForm: {id: null, name: '', description: '', prompt_template: ''},
|
||||
galleryFilter: '',
|
||||
canvases: [], canvas: null,
|
||||
connectors: [], connector: null,
|
||||
connectors: [], connector: null, plugins: [],
|
||||
connectorForm: {name: '', url: '', secret: '', kind: 'custom',
|
||||
auth: 'bearer', hint: ''},
|
||||
cmdFocus: -1, slashDismiss: false,
|
||||
@@ -1148,6 +1148,7 @@
|
||||
if(s === 'connector-new') return 'Ajouter un connecteur';
|
||||
if(s === 'canvases') return 'Design System – Canevas';
|
||||
if(s === 'canvas') return this.canvas ? this.canvas.name : 'Canevas';
|
||||
if(s === 'plugins') return 'Add plugins';
|
||||
return 'Ajouter au contexte';
|
||||
},
|
||||
get plusItems(){
|
||||
@@ -1163,6 +1164,7 @@
|
||||
if(s === 'connector-new') return [];
|
||||
if(s === 'canvases') return this._canvasItems();
|
||||
if(s === 'canvas') return this._canvasActionItems();
|
||||
if(s === 'plugins') return this._pluginItems();
|
||||
if(s === 'root'){
|
||||
// l'entrée Mémoire reflète l'état réel du toggle de la conversation
|
||||
var on = this.memoryOn;
|
||||
@@ -1274,6 +1276,39 @@
|
||||
var tools = (c.kind === 'mcp' && c.tools_count) ? ' · ' + c.tools_count + ' outil(s)' : '';
|
||||
return mark + (c.detail || c.status) + tools + (c.enabled ? '' : ' · désactivé');
|
||||
},
|
||||
// ── Plugins de l'instance (v7.58.0) ──
|
||||
fetchPlugins(){
|
||||
var self = this;
|
||||
fetch('/api/agent/plugins').then(function(r){ return r.json(); })
|
||||
.then(function(d){ self.plugins = (d && d.plugins) || []; })
|
||||
.catch(function(){ self.plugins = []; });
|
||||
},
|
||||
_pluginItems(){
|
||||
var out = (this.plugins || []).map(function(pl){
|
||||
return {key:'pl-' + pl.slug, icon: pl.enabled ? '✅' : '⛔',
|
||||
label: pl.name,
|
||||
sub: (pl.enabled ? 'Activé' : 'Désactivé') + ' — ' + pl.description,
|
||||
action:'plugin-toggle', slug: pl.slug};
|
||||
});
|
||||
if(!out.length) out.push({key:'pl-none', _sep:'Aucun plugin'});
|
||||
out.push({key:'pl-hint', _sep:
|
||||
'OFF = routes refusées, UI masquée, outils retirés'});
|
||||
return out;
|
||||
},
|
||||
pluginToggle(slug){
|
||||
var self = this;
|
||||
var pl = (this.plugins || []).filter(function(x){ return x.slug === slug; })[0];
|
||||
if(!pl) return;
|
||||
fetch('/api/agent/plugins/' + encodeURIComponent(slug), {
|
||||
method:'PATCH', headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'},
|
||||
body: JSON.stringify({enabled: !pl.enabled})
|
||||
}).then(function(r){ return r.json().then(function(d){ return {ok:r.ok, d:d}; }); })
|
||||
.then(function(res){
|
||||
if(!res.ok){ self.toast((res.d && res.d.detail) || 'Bascule impossible.', true); return; }
|
||||
self.toast(res.d.enabled ? 'Plugin activé.' : 'Plugin désactivé.');
|
||||
self.fetchPlugins();
|
||||
}).catch(function(){ self.toast('Bascule impossible (réseau).', true); });
|
||||
},
|
||||
_connectorItems(){
|
||||
var self = this;
|
||||
var out = (this.connectors || []).map(function(c){
|
||||
@@ -1579,6 +1614,10 @@
|
||||
auth:'bearer', hint:''};
|
||||
this.plusSection = 'connector-new'; this.plusFocus = -1; return;
|
||||
}
|
||||
if(it.action === 'plugins'){
|
||||
this.fetchPlugins(); this.plusSection = 'plugins'; this.plusFocus = 0; return;
|
||||
}
|
||||
if(it.action === 'plugin-toggle'){ this.pluginToggle(it.slug); return; }
|
||||
if(it.action === 'connector-probe'){ this.connectorProbe(); return; }
|
||||
if(it.action === 'connector-toggle'){ this.connectorToggle(); return; }
|
||||
if(it.action === 'connector-delete'){ this.connectorDelete(); return; }
|
||||
|
||||
Reference in New Issue
Block a user