@sinoia/edifice-tools
v0.3.0
Published
CLI pour piloter Édifice (intégration & gestion) — LLM-friendly, calqué sur @sinoia/hubdoc-tools. CLI scopée de l'écosystème Synapse.
Readme
@sinoia/edifice-tools (edi)
CLI pour piloter Édifice — d'abord pour l'intégration (monter des jeux de test, dérouler les flux bannette / e-invoicing / GED), puis pour des actions de gestion.
Calqué sur @sinoia/hubdoc-tools (le CLI domaine de la GED), pas sur ch (l'ops/infra CorexHub). Conçu pour être LLM-friendly (--json, erreurs sur stderr, whoami) : c'est une CLI scopée de l'écosystème Synapse — montée dans les sandboxes d'agents Claude Code, qui lisent le token délégué via EDIFICE_API_TOKEN.
Spécification : edifice#465.
Installation (dev)
npm install
npm run build # → dist/cli.js (bundle esbuild auto-suffisant)
node dist/cli.js --help
# ou en dev, sans build :
npm run dev -- --helpAuthentification
Édifice n'expose pas de device-flow OIDC (authorization_code+PKCE seulement) ; le CLI s'authentifie
via POST /api/Auth/LogIn → JWT (7 jours).
# Interactif (humain) : demande base URL, login, mot de passe masqué
edi login
# Scripté
edi login --api-url https://spirit-rec.edifice… --login user --password ****
# Agent / CI : fournir un token déjà minté
export EDIFICE_API_TOKEN=eyJ…
export EDIFICE_API_URL=https://…
edi whoami --jsonRésolution du token : EDIFICE_API_TOKEN (env) → config (~/.edifice-config.json, 0600).
Le keychain s'insérera entre les deux ultérieurement.
Commandes
| Commande | Rôle |
|----------|------|
| edi login | Auth interactive/scriptée → stocke le JWT |
| edi token set <jwt> | Enregistre un JWT déjà obtenu |
| edi whoami | Décode le JWT (Id/Email/Roles/exp), source du token, vérif serveur |
| edi banks list | Démo API v2 typée (GET /api/v2/banks) — patron des commandes v2 |
Commercialisation (lecture seule) — agent « Assistant commercial »
Bloc taillé pour l'agent IA de reporting commercial Spirit (rythme de vente, alertes sur les dates
clés, dialogue pricing). Uniquement des GET → utilisable sous --read-only.
| Commande | Endpoint | Ce qu'on y lit |
|----------|----------|----------------|
| edi projects list --launched-only [--status-id --parent-id --all] | v2 /api/v2/projects | programmes + dateLancementCommercialReelle ; --launched-only = lancés commercialement |
| edi projects show <id> | v2 /api/v2/projects/{id} | fiche programme |
| edi projects schedule <id> | /api/ProvisionalSchedules/GetAsyncByProjectId/{id} | dates clés projetées en dateInitiale / dateRecalee / dateReelle |
| edi projects price-grids <id> [--lots] [--grid-id] | v2 /api/v2/projects/{id}/price-grids | grilles ; --lots → une ligne par lot (prix, surface, typologie, saleStatusTag) |
| edi projects commercial <id> [--plan-fi-id] | /api/EtatCommercialisation/ventes/{id} | CA en stock / réservé / acté + % |
| edi projects margin <id> | /api/FinancialPlans/GetAllByProject/{id} | plans financiers + marge nette (netMargin, /CA) |
| edi projects contacts <id> | /api/ContactProjectPeoples/GetAsyncByProjectId/{id} | intervenants → destinataire de la fiche action |
| edi projects forecast <id> [--prevision-id] [--list-previsions] | /api/Tresorerie/Projet/{id} + /api/Tresorerie/Ventes/{idPrevision} | écoulement prévu vs réel (ecoulement, lotsActes, reel) |
| edi dashboard vision-globale [--all] [--position] | /api/TableauDeBord/VisionGlobaleProjet/AllerALaPosition/{position} | vue multi-programmes : lots, avancement, acté/réservé/stock, CA, marge nette HT, prix de revient, dates clés — la source la plus dense pour le rapport hebdo |
| edi dashboard vision-globale-total | …/VisionGlobaleProjet/Total | taille du périmètre visible |
| edi sales list --project-id --status-id [--all] | v2 /api/v2/sales | ventes du programme (reservationDate, saleDate) → rythme réalisé |
| edi sales lots --sale-id <id> | v2 /api/v2/sales/{id}/lots | lots d'une vente |
| edi sales statuses | /api/SaleStatus | référentiel En stock / Réservé / Acté / Option / Désisté… |
Toutes ces commandes acceptent --raw (charge utile serveur intacte, sans projection) et --json.
Points d'attention (côté API, pas côté CLI) :
dashboard vision-globaleest borné par le serveur au périmètre visible du porteur du token, aux projets racines (parentId = null) et aux statuts En cours / Prospection. Le paramètrerecherchede l'endpoint est appliqué après la pagination côté serveur : il n'est volontairement pas exposé.projects forecastsuppose qu'une prévision de trésorerie existe et soit tenue à jour sur le programme — c'est aujourd'hui la seule source du rythme de vente prévisionnel.
Appels de fonds en masse (« paquet ADF »)
Le cycle Édifice de l'appel de fonds lié à l'avancement du chantier. Les quatre dernières
commandes écrivent — elles sont refusées sous --read-only.
| Commande | Endpoint | Rôle |
|----------|----------|------|
| edi adf planning | /api/AppelDeFonds/AGrouper | échéances mûres par programme × catalogue : stade, datePrevisionnelle, appelsALancer, et l'idParametreEcheance à regrouper |
| edi adf packets | /PaquetADFALancer | paquets prêts à lancer |
| edi adf packet <id> [--buyers] | /DetailDuPaquetALancer/{id} | détail ; --buyers → une ligne par acquéreur (acquereur, idVente, pourcentage, montantTTC) |
| edi adf history [--packet-id] | /Historique, /HistoriqueParPaquet/{id} | ce qui est réellement parti : appels réussis / manquants |
| edi adf unpaid | /Impaye | appels de fonds impayés |
| edi adf create --libelle --echeances | POST /api/AppelDeFonds | crée un paquet (statut « En attente ») |
| edi adf update <id> --echeances | PATCH /api/AppelDeFonds/{id} | recompose un paquet |
| edi adf launch <id> | POST /Lancer/{id} | lancement en masse, irréversible |
| edi adf cancel <id> | PATCH /Annule/{id} | annule un paquet non lancé |
Contraintes serveur sur create : --libelle obligatoire (60 caractères max), --echeances
non vide, et toutes les échéances doivent appartenir au même projet. launch exige la
permission Corex AppelDeFond et rend { cptOK, cptKo, warnings } : un 200 ne suffit pas à
conclure que tout est parti — c'est cptKo et warnings qu'il faut lire.
Mode lecture seule
--read-only (ou EDIFICE_READ_ONLY=1) refuse localement, avant l'envoi, toute requête qui
n'est pas GET/HEAD — axios legacy, client v2 typé et multipart compris :
edi --read-only --json dashboard vision-globale --all # ok
edi --read-only sales create --data @vente.json # ✗ read_only, exit 1C'est un garde-fou client : il rend l'écriture impossible par accident (ou par dérive d'un LLM) sans changer de token. Il ne remplace pas un token à scope restreint côté Édifice.
Intégration / seeding (T2, socle — ordre DevDataSeeder)
| Commande | Endpoint |
|----------|----------|
| edi orgs list / orgs create | v2 typé (/api/v2/organizational-units) — --name/--parent-id/--data |
| edi roles list | legacy GET /api/Role/AllEnabled (roleIds) |
| edi users list / users create | legacy /api/User — create : --data (UserToAdd), appelant Administrateur |
| edi projects list / projects create | list = v2 ; create = legacy POST /api/Projects (--data) |
| edi providers list / providers create | legacy /api/Providers (list : --search <nom>) |
| edi orders create / list / validate / sign | engagements, legacy /api/Orders (create --project-id --data, sign --date) |
| edi amendments create | avenant, legacy POST /api/Amendments/{engagementId} (--data) |
| edi providers rib | RIB fournisseur — multipart (--data + --file justificatif) |
| edi ged upload\|upload-order\|upload-reservation | dépôt GED — multipart (--file + scope) |
| edi buyers list\|create | acquéreurs — v2 typé |
| edi sales list\|create\|funds-call | ventes + appel de fonds — v2 typé |
Bannette (intake e-invoice + workflow)
| Commande | Rôle |
|----------|------|
| edi bannette list / show <id> / workflow <id> | consultation |
| edi bannette invoice create --data [--file] | multipart — crée une facture et l'entre en bannette |
| edi bannette invoice update <id> --data [--file] | multipart |
| edi bannette save-presaisie\|validate-presaisie\|discard-presaisie <id> --data | pré-saisie |
| edi bannette transition <id> <action> [--data] | workflow générique (validate-n1/n2, approve, refuse, suspend, resume, dispute, reassign, confirm-not-duplicate) |
| edi bannette bap-token <id> / emit-bap <id> --data | émission du BAP |
| edi bannette change-engagement <id> --type --data | rattachement |
Brique multipart (
src/util/multipart.ts) : aplatit le JSON en champs form ASP.NET (items[0].name=…) + fichier, viafetch. Réutilisable pour le RIB fournisseur et l'upload GED (encore à câbler).
Seed — scénarios end-to-end
# Monte toute la chaîne : projet → tiers → engagement signé → facture bannette → BAP
edi seed bannette-vefa --data @scenario.json # exécute
edi seed bannette-vefa --dry-run --json # imprime le plan (10 étapes, ids threadés)
edi seed bannette-vefa --project-id 12 --data @… # réutilise un projet existantData-driven : --data fournit le corps par étape ({ project, provider, order, signature, invoice,
transitions, bap }) ; l'orchestrateur enchaîne + injecte les ids (projet→engagement→facture) +
déroule le workflow (validate-n1→n2→approve→BAP). En cas d'échec, renvoie created (partiel).
Chaque create accepte --data '<json>' ou --data @fichier.json (corps complet, robuste pour le
seeding) ; les retours sont « flatten-at-root » (.id en racine).
Global : --json (sortie machine), --read-only (refuse toute écriture), -v/--version.
Client API (T1)
- v2 (typé) :
ctx.v2= client openapi-fetch généré depuis../edifice-mono/api/specifications/api-v2-openapi.yaml(typessrc/api/v2.ts, commité). Régénérer :npm run generate:api(pure Node, pas de Java). - legacy (escape hatch) :
ctx.axiospour les endpoints hors v2 (bannette, orders, GED, e-invoice…).
Les deux portent le Bearer + la base URL résolus par buildApiContext().
Feuille de route
- T0 (ce squelette) : auth +
whoami. - T1 : client typé généré depuis
specifications/api-v2-openapi.yaml(+/swaggerlegacy). - T2 : intégration/seeding (ordre
DevDataSeeder) — orgs/users/projects/orders/bannette/… - T3 : skills Claude Code embarqués (
edifice-auth,edifice-seed) + insertion Synapse. - T4 : gestion (SEPA, mise en paiement, déversement compta).
