- Workflow n8n tdfInZns7cqo8fU2 actif (15 min): snapshot HA compact -> prompt FR -> LLM local Spark (modèle dynamique via /v1/models) avec fallback OpenRouter claude-haiku-4.5 (credential chiffré, clé hors git) -> validation stricte (enums, bornes, rejet total si invalide) -> helpers advisor_* + divergence + compteurs accord/divergence + logbook. - GARANTIE OMBRE structurelle: écritures limitées à advisor_*/logbook, vérifiée par tests/advisor/run.mjs (16 tests, bloquants au push). - Kill-switch input_boolean.evcs_advisor_enabled. - Extension R2 multi-workflows (lib-node-map WORKFLOWS[], build/extract/push par fichier, n8n-create.mjs, gen-advisor-workflow.mjs). - Helpers HA via package NFS + carte dashboard EVCS Master (backup fait). - 1re reco réelle: auto_solar_plus, cible 35%, accord avec le réel. - Fix sécurité n8n-push: plus de fallback d'id implicite vers le workflow EVCS.
47 lines
1.9 KiB
JavaScript
47 lines
1.9 KiB
JavaScript
#!/usr/bin/env node
|
|
// R2 — BUILD : src/ → workflows JSON (src = SOURCE DE VÉRITÉ, src gagne).
|
|
// Usage : node tools/n8n-build.mjs [--check] [fichierWorkflow]
|
|
// --check : vérifie la synchro sans écrire (exit 1 si désynchronisé)
|
|
// fichierWorkflow : limite à un workflow (sinon tous ceux du node-map)
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { WORKFLOWS, getCode, setCode, findWorkflowByFile } from './lib-node-map.mjs';
|
|
|
|
const CHECK = process.argv.includes('--check');
|
|
const fileArg = process.argv.slice(2).find(a => !a.startsWith('--'));
|
|
const targets = fileArg ? [findWorkflowByFile(fileArg)].filter(Boolean) : WORKFLOWS;
|
|
if (fileArg && !targets.length) {
|
|
console.log(`(${fileArg} non enregistré dans le node-map : rien à builder)`);
|
|
process.exit(0);
|
|
}
|
|
|
|
let totalChanges = 0;
|
|
for (const w of targets) {
|
|
if (!fs.existsSync(w.workflowFile)) {
|
|
console.log(`(skip ${w.id}: ${w.workflowFile} absent)`);
|
|
continue;
|
|
}
|
|
const wf = JSON.parse(fs.readFileSync(w.workflowFile, 'utf8'));
|
|
let changes = 0;
|
|
for (const { file, node, kind } of w.nodes) {
|
|
const srcPath = path.join(w.srcDir, file);
|
|
if (!fs.existsSync(srcPath)) throw new Error(`Fichier source manquant: ${srcPath}`);
|
|
const srcCode = fs.readFileSync(srcPath, 'utf8');
|
|
const n = wf.nodes.find(x => x.name === node);
|
|
if (!n) throw new Error(`Node introuvable dans ${w.workflowFile}: ${node}`);
|
|
if (getCode(n, kind) !== srcCode) {
|
|
changes++;
|
|
console.log(`${CHECK ? 'DÉSYNCHRONISÉ' : 'inject'}: ${w.srcDir}/${file} → "${node}"`);
|
|
if (!CHECK) setCode(n, kind, srcCode);
|
|
}
|
|
}
|
|
if (!CHECK && changes) {
|
|
fs.writeFileSync(w.workflowFile, JSON.stringify(wf, null, 2) + '\n');
|
|
}
|
|
totalChanges += changes;
|
|
console.log(`${w.id}: ${changes ? changes + ' node(s) ' + (CHECK ? 'désynchronisé(s)' : 'injecté(s)') : 'synchronisé'}`);
|
|
}
|
|
|
|
if (CHECK) process.exit(totalChanges ? 1 : 0);
|