@astratra/centrale

中文文档
1.11.0发布于 2026/11/05MITTypeScriptES Module零依赖近 30 天下载 2734

Paquet de collecte pour un tableau de bord de surveillance auto-hébergé : erreurs, vues, usages, sessions et appels d'IA, masqués à la source ; client des réglages, bilans et décisions de la conscience d'une IA, journal des exécutions d'agents ; propositi

原始使用文档
文档目录16 节
  1. Installation
  2. Exemple minimal
  3. Configuration
  4. Tests automatisés : rien n'est envoyé
  5. Bruits des extensions du navigateur
  6. Les 7 entrées
  7. Passerelle des clés ( /passerelle )
  8. Journal des agents ( /agents )
  9. Conscience d'une IA ( /conscience )
  10. Gouvernance des actions de l'IA sur les données ( /gouverna…
  11. Ce qui est capté
  12. Cartes de code
  13. Vie privée
  14. Ce que le paquet ne fait pas
  15. Licence
  16. Tests

@astratra/centrale

English | Français

Le paquet de collecte d'un tableau de bord de surveillance auto-hébergé : erreurs, plantages, vues, usages, sessions et appels d'IA, masqués à la source puis envoyés par lots au backend.

  • Aucune dépendance à l'exécution, aucun autre paquet Astratra.
  • Ne lève jamais chez l'appelant et ne bloque jamais le projet hôte.
  • Écrit en TypeScript, publié en JavaScript avec ses types (dist/, construit par npm run build avant chaque publication).
  • Porte le contrat d'envoi (@astratra/centrale/envoi) et le masquage (@astratra/centrale/masquage) : le backend du tableau de bord les reprend d'ici, il n'y a qu'une seule source.

Installation

npm install @astratra/centrale

Exemple minimal

import { creerCentrale } from '@astratra/centrale';

const centrale = creerCentrale({
  cle: 'clj_pub_…',                          // clé publique du projet
  adresse: 'https://centrale.exemple.fr',    // backend du tableau de bord
  environnement: 'production',
  version: '2.14.0'
});

centrale.vue('/eleve/123');                  // enregistré comme « /eleve/:id »
centrale.usage('export_pdf', { pages: 3 });
try {
  // …
} catch (e) {
  centrale.erreur(e);
}
await centrale.vider();                      // envoie ce qui reste en file ; ne rejette jamais

Pour Node, le navigateur, Expo ou la mesure des appels d'IA, utilisez le point d'entrée dédié (tableau plus bas).

Configuration

Seuls cle, adresse et environnement sont obligatoires.

const config = {
  cle: 'clj_pub_…',                       // clé publique : envoyer seulement
  adresse: 'https://centrale.exemple.fr',
  environnement: 'production',            // 'production' | 'preproduction' | 'developpement'
  version: '2.14.0',
  sel: 'sel-du-projet',                   // pour hacher l'identifiant d'utilisateur
  noms: ['Dupont'],                       // noms propres à masquer partout
  echantillonnage: { vues: 0.5, usages: 1, ia: 1 }, // jamais pour les erreurs et sessions
  strict: false,                          // true : ni utilisateur, ni appareil précis
  desactive: false,                       // true : coupe tout (refus)
  ignorer: ['ResizeObserver loop'],       // erreurs à ne jamais envoyer (texte contenu ou expression)
  debug: false                            // true : console.warn des erreurs internes
};
OptionRôle
cleClé publique du projet (clj_pub_…) : elle sert seulement à envoyer.
adresseAdresse du backend.
environnement'production', 'preproduction' ou 'developpement'.
versionVersion de l'app ou du serveur.
selSel servant à hacher l'identifiant d'utilisateur.
nomsNoms propres à masquer partout.
echantillonnageTaux de 0 à 1 pour vues, usages et ia ; jamais appliqué aux erreurs ni aux sessions.
strictNi utilisateur, ni appareil précis.
desactiveCoupe tout : l'instance ne fait rien.
ignorerErreurs à ne jamais envoyer, d'après leur message : un texte contenu dans le message, ou une expression. S'ajoute aux bruits des extensions.
bruitsExternestrue par défaut : les bruits connus des extensions du navigateur (BRUITS_EXTERNES) et les erreurs dont toute la pile est dans une extension (chrome-extension://, safari-web-extension://…) ne partent pas. false pour les garder.
debugAffiche les erreurs internes avec console.warn.
robotsNavigateur seulement. false par défaut : un navigateur piloté par un robot (navigator.webdriver, c'est-à-dire Playwright, Selenium, Puppeteer) ne collecte rien. true pour collecter quand même (tester la collecte elle-même).

Tests automatisés : rien n'est envoyé

Depuis la 1.5.0, demarrer de /navigateur rend une instance inactive (centrale.actif === false) quand navigator.webdriver vaut true : les tests de bout en bout lancés contre un déploiement ne remplissent plus le tableau de bord de fausses erreurs. Pour vérifier la collecte dans un test Playwright, demande-la :

const centrale = demarrer({ ...config, robots: true });

Bruits des extensions du navigateur

Depuis la 1.9.0, les erreurs qui viennent d'une extension et non de l'app (« Invalid call to runtime.sendMessage(). Tab not found. » de Safari iOS, « Extension context invalidated. », etc.) sont écartées avant la file : elles ne partent ni dans les lots, ni dans l'envoi final par balise quand la page se ferme. La liste est exportée (BRUITS_EXTERNES) ; ignorer y ajoute les messages propres à l'app.

Les 7 entrées

EntréeUsage
@astratra/centralecreerCentrale(config, transport?) : erreur(e, extra?), vue(nom), usage(nom, props?), ia({…}), session(etat), miette(categorie, message, donnees?), definirUtilisateur(id), vider(). Le transport (fetch par défaut) est injectable.
@astratra/centrale/nodedemarrer(config) ; app.use(centrale.intergiciel()) en tête, app.use(centrale.gestionnaireErreurs()) en fin.
@astratra/centrale/navigateurdemarrer(config) au chargement de l'app web.
@astratra/centrale/expodemarrer({ ...config, plateforme: Platform.OS, version, build, miseAJour, os, appareil, appState: AppState, stockage }), puis centrale.ecran(nom) depuis la navigation. stockage (AsyncStorage, expo-file-system…) garde l'état entre deux lancements.
@astratra/centrale/iamesurerAppelIA({ fournisseur, modele, outil?, jetons?, estRepli? }, () => appel()).
@astratra/centrale/consciencecreerConscienceCentrale({ adresse, jeton }) : réglages de la conscience d'une IA, bilan du matin, décisions humaines (section plus bas).
@astratra/centrale/gouvernancecreerGouvernanceCentrale({ adresse, jeton }) : propositions d'actions de l'IA sur les données, règles, validations signées (section plus bas).
import { demarrer } from '@astratra/centrale/node';
const centrale = demarrer(config);
app.use(centrale.intergiciel());
// … routes …
app.use(centrale.gestionnaireErreurs());

Passerelle des clés (/passerelle)

Une app n'y garde qu'un jeton de projet (CENTRALE_PASSERELLE_JETON, clj_pas_…) et l'adresse de Centrale (CENTRALE_ADRESSE) : Centrale appelle les fournisseurs avec les clés de son coffre et compte la consommation par projet.

import { image, lirePasserelle, modeleDiscussion, passerelleDabord, plongements, rechercher, voix } from '@astratra/centrale/passerelle';
const passerelle = lirePasserelle(process.env);          // null si non réglée : l'app garde ses clés directes
const modele = passerelle && modeleDiscussion(passerelle); // { url, baseUrl, apiKey, model: 'auto', thoughtSignatures: true } au format OpenAI, à mettre en tête du relais
const resultats = await passerelleDabord(passerelle, (p) => rechercher(p, { requete: 'météo Paris' }), () => rechercheDirecte());
// Une image (Cloudflare Workers AI, compte et clé du coffre du projet, périmètre « images » du jeton), depuis 1.8.0 :
const dessin = await passerelleDabord(passerelle, (p) => image(p, { texte: 'Un phare au lever du jour', modele: '@cf/black-forest-labs/flux-1-schnell' }), () => dessinDirect());
// { octets, type: 'image/png', modele } — le modèle dit si Centrale s'est repliée sur Flux 1 Schnell

Une panne de la passerelle (injoignable, 5xx, 429, 401/403, service non configuré) lève PasserelleIndisponible : passerelleDabord passe alors aux clés directes. Une requête refusée pour elle-même (400, 413) lève RequetePasserelleRefusee, sans repli.

Journal des agents (/agents)

Une IA y envoie chacune de ses exécutions (tours de modèle, outils appelés, jetons, durée, coût, statut, actions proposées) au tableau de bord : POST /v1/agents, avec la clé publique du projet (X-Centrale-Cle), 100 exécutions par lot au plus.

import { construireExecution, creerJournalAgents } from '@astratra/centrale/agents';

const journal = creerJournalAgents({ adresse: process.env.CENTRALE_ADRESSE!, cle: process.env.CENTRALE_CLE! });

await journal.envoyer([construireExecution({
  identifiant: 'run-123', agent: 'Elsa', mode: 'discussion', utilisateur: idDuCompte,   // identifiant opaque
  debut, fin, statut: 'ok',                                                              // ok | erreur | interrompue | en_attente_confirmation
  tours: [{ modele: 'modele-a', dureeMs: 900, jetonsEntree: 1200, jetonsSortie: 80, cout: 0.002 }],
  outils: [{ outil: 'lire_stock', succes: true, dureeMs: 25 }],
  actions: [{ outil: 'rembourser', etat: 'proposee' }]
})]);
  • construireExecution(bilan, { inclureContenu? }) : fonction pure. Sans contenu par défaut : ni question, ni réponse, ni arguments, ni résultats d'outils, même si le bilan les porte ; l'erreur est un code court (ia_indisponible), jamais un message. inclureContenu: true les joint (masqués avant l'envoi) ; à n'activer que pour un usage qui l'exige.
  • creerJournalAgents({ adresse, cle, fetch? }) : envoyer(executions) valide en local (une clé inconnue, un statut, un type d'étape ou un état d'action hors liste refuse l'exécution ; étapes, sources et actions coupées aux bornes du format ; dates à la seconde, Z), masque les textes, met en file et envoie. vider(), enAttente(), arreter().
  • La file est en mémoire (1 000 exécutions au plus). Un échec réseau, un 5xx ou un 429 (Retry-After respecté) remet le lot en file avec un recul progressif (2 s, doublé jusqu'à 5 min) ; un refus 4xx abandonne le lot en silence. Ne lève jamais ; debug: true explique les refus.

Conscience d'une IA (/conscience)

Le tableau de bord règle la conscience d'une IA (allumée, arrêt d'urgence, approbateurs, réflexion automatique, cycles par jour), reçoit son bilan du matin et ses propositions en attente, et enregistre les décisions humaines. Il n'applique jamais rien : l'IA lit ses réglages et les décisions, puis les soumet à son propre gouverneur. L'authentification se fait par le jeton de projet (Authorization: Bearer …).

import { creerConscienceCentrale } from '@astratra/centrale/conscience';

const tableau = creerConscienceCentrale({ adresse: 'https://centrale.exemple.fr', jeton: process.env.JETON_PROJET! });

const reglages = await tableau.lireReglages();              // gardés avec leur ETag : un 304 rend la copie
await tableau.envoyerBilan(await governor.morningReport({ organisationId, since }),
  await governor.listProposals({ organisationId, status: 'pending_approval' }));
const decisions = await tableau.lireDecisions();             // celles qui ne sont pas encore accusées
await tableau.accuserDecisions(resultats);                   // applied, already_applied, refused (+ code)
MéthodeRouteRôle
lireReglages()GET /v1/conscience/settingsRéglages (conscience-settings/1), avec If-None-Match ; lève ErreurConscience si le tableau de bord ne répond pas
envoyerBilan(rapport, propositions?)POST /v1/conscience/reportBilan et liste complète des propositions en attente, réduits par resumerBilan
lireDecisions({ toutes? })GET /v1/conscience/decisionsDécisions non accusées (avec toutes : toutes, curseur en mémoire)
accuserDecision(id, { result, code })POST /v1/conscience/decisions/:id/ackCe que le gouverneur a fait de la décision
accuserDecisions(resultats)idemAccuse une liste ; un résultat failed n'est pas accusé et revient au passage suivant

resumerBilan(rapport, propositions?) ne garde que des identifiants, des codes (answer_style, tool_reliability…) et des nombres : ni question, ni réponse, ni note, ni identifiant de décision, ni objet tests, ni plan de retour arrière, ni propriétaire, ni identité d'une personne qui propose. Une chaîne que le masquage changerait est retirée. Le tableau de bord vérifie à nouveau et refuse tout envoi qui contient autre chose.

Les erreurs sont des ErreurConscience : statut HTTP (ou null hors ligne) et code (invalid_api_key, sensitive_data, reseau…).

Gouvernance des actions de l'IA sur les données (/gouvernance)

L'IA d'une app propose une action sur ses données ; l'app calcule l'aperçu exact, l'envoie au tableau de bord, qui prévient la bonne personne et rend une validation signée (sa passkey, plus la signature Ed25519 du tableau de bord). L'app revérifie et exécute elle-même : le tableau de bord ne reçoit jamais d'accès à une base. Ce client va avec @astratra/gouvernance, qu'il n'importe pas : on lui passe la gouvernance.

import { creerGouvernanceCentrale } from '@astratra/centrale/gouvernance';

const tableau = creerGouvernanceCentrale({ adresse: 'https://tableau.exemple.fr', jeton: process.env.JETON_PROJET! });
// Une fois, à l'installation : lireConfiance() donne les clés publiques à ÉPINGLER dans la configuration de l'app.
const gouvernance = createDataGovernance({ /* … */ trust: confianceEpinglee, remoteRules: () => tableau.lireRegles() });

const vue = await gouvernance.propose({ /* … */ });
await tableau.envoyerProposition(vue);                       // en attente, ou déjà exécutée (libre avec avis)
setInterval(() => tableau.synchroniser(gouvernance, { organisationId }).catch(() => {}), 15_000);
MéthodeRouteRôle
lireRegles()GET /v1/gouvernance/rulesRègles d'autonomie (gouvernance-regles/1), avec If-None-Match ; à passer en remoteRules
lireConfiance()GET /v1/gouvernance/trustClés publiques du tableau de bord et des approbateurs (gouvernance-confiance/1), à épingler, pas à relire à chaque tour
envoyerProposition(vue)POST /v1/gouvernance/proposalsLa proposition canonique (empreinte comprise), son explication et son état ; rejouable
signalerEtat(id, vue)POST /v1/gouvernance/proposals/:id/statusExécutée, annulée, données changées…
lireValidations({ toutes? })GET /v1/gouvernance/validationsValidations signées pas encore accusées
accuserValidation(id, vue)POST /v1/gouvernance/validations/:id/ackCe que la gouvernance en a fait
synchroniser(gouvernance, { organisationId })les deux précédentesApplique chaque validation et accuse le résultat ; une validation dont l'application lève n'est pas accusée et revient

Seuls un état connu, un code court et un nombre de lignes repartent (etatDe). Les erreurs sont des ErreurGouvernance (statut, code).

Ce qui est capté

  • Node : exceptions non attrapées (enregistrées en fatal, puis comportement normal de Node : pile et code 1), promesses rejetées sans gestionnaire (le plantage par défaut est conservé), requêtes Express (identifiant X-Request-Id, route en gabarit, durée, statut, miette), erreurs Express (statut ≥ 500 : erreur, sinon avertissement), vidage à la sortie (beforeExit, SIGTERM, SIGINT, 2 s au plus).
  • Navigateur : error et unhandledrejection (sans remplacer window.onerror), vues SPA (pushState, replaceState, popstate) en gabarit, temps de chargement de la première page, sessions (début à l'ouverture, fin quand la page est cachée), envoi final par navigator.sendBeacon avec ?cle=.
  • Expo : ErrorUtils.setGlobalHandler (fatale : plantage, vidage puis gestionnaire précédent), sessions liées à AppState (fin après 30 s en arrière-plan), écrans. signalerPlantageNatif(rapport) attend le futur module natif (MetricKit, raison de dernière fermeture Android).
  • Surveillance renforcée (navigateur et Expo, /vigilance, désactivable option par option) :
    • requêtes fetch dans le fil d'Ariane (gabarit, statut, durée) ; pas de réponse, 5xx, 408 et 429 signalés (RequeteSansReponse, RequeteEchouee) ; les envois à la Centrale et les annulations ne comptent pas (requetes) ;
    • console.error signalé (une fois par message toutes les 10 s), console.warn dans le fil d'Ariane (console) ;
    • les vérifications de connexion des systèmes et des bibliothèques (generate_204 de Google, captive.apple.com, msftconnecttest.com, detectportal.firefox.com…) restent dans le fil d'Ariane mais ne sont jamais signalées : leur échec dit seulement que le réseau manque (estVerificationDeConnexion(adresse)) ;
    • gardien du fil principal : interface bloquée plus de 2 s signalée (AppBloquee, erreur au-delà de 5 s), en pause en arrière-plan et, sur Expo, dès l'état inactive (centre de contrôle, sélecteur d'apps, verrouillage en cours) (blocages). Un retard de la minuterie de plus de 60 s n'est pas un blocage mais une pause de l'app (téléphone verrouillé, onglet endormi : JavaScript a été suspendu avant d'apprendre le passage en arrière-plan) : seulement une miette « Pause de l'app N s », sans erreur. Ce plafond se règle par plafondMs en appelant surveillerBlocages(centrale, { plafondMs }) de @astratra/centrale/vigilance ;
    • Expo : promesses rejetées sans catch (Hermes, promesses), alerte mémoire d'iOS (MemoireSaturee, au plus une fois par minute), premier plan, arrière-plan et lancement dans le fil d'Ariane ;
    • avec un stockage : le fil d'Ariane, l'écran et la session ouverte sont gardés au fil de l'eau ; au lancement suivant, une session restée ouverte est close « anormale » (ou « plantage » après une erreur fatale), et une fermeture au premier plan est signalée (FermetureInattendue, avec le fil d'Ariane et l'écran). Navigateur : à réserver aux apps ouvertes dans un seul onglet.
  • IA : fournisseur, modèle, outil, durée, jetons, résultat (ok, erreur, quota, repli). Jamais le contenu.

Envoi : lots de 100 événements au plus (1 Mio au plus), toutes les 10 s ou dès 50 événements ; file de 500 événements (les plus vieux usages partent d'abord, jamais une erreur avant un usage) ; recul progressif (2 s, 4 s… plafonné à 5 min) ; Retry-After respecté sur 429 ; abandon silencieux sur 400, 401, 413 ; hors ligne, la file attend.

Cartes de code

Si le build est passé par centrale injecter (paquet @astratra/centrale-cli), chaque cadre de pile reçoit le debugId de son fichier (lu dans globalThis.__centraleDebugIds), et l'erreur aussi quand toute la pile vient d'un seul fichier. Le backend s'en sert pour retrouver la vraie ligne. Rien à configurer.

Vie privée

  • Aucun cookie ; aucun stockage local sans stockage (il ne garde que le fil d'Ariane déjà masqué, l'écran et l'identifiant de session).
  • Masquage avant tout envoi : e-mails, téléphones, cartes, IBAN, clés, jetons, mots de passe, et les noms fournis.
  • Adresses ramenées à leur gabarit (/eleve/123?token=x devient /eleve/:id).
  • Utilisateur : seulement une empreinte SHA-256 salée, jamais l'identifiant brut.
  • Mode strict : ni utilisateur, ni appareil précis.
  • Usages : propriétés simples seulement (texte court, nombre, booléen).

Ce que le paquet ne fait pas

  • Il n'héberge ni backend ni tableau de bord : il envoie les événements à l'adresse que vous indiquez.
  • Il ne capte pas le contenu des appels d'IA, ni les cookies.
  • Les rapports de plantage natifs (iOS, Android) ne sont pas encore captés : signalerPlantageNatif attend un module natif.

Licence

MIT.

Tests

npm test -w @astratra/centrale
npx tsc -p packages/centrale/tsconfig.json --noEmit

版本与发布记录

@astratra/centrale 共发布 16 个版本,首次发布于 2026/10/08;过去 12 个月发布了 16 个版本。

版本发布日期距今
1.11.0latest2026/11/052 天前
1.10.02026/10/309 天前
1.9.02026/10/2711 天前
1.8.02026/10/2217 天前
1.7.02026/10/1820 天前
1.6.02026/10/1721 天前

关于 @astratra/centrale 的常见问题

@astratra/centrale 是什么?

Paquet de collecte pour un tableau de bord de surveillance auto-hébergé : erreurs, vues, usages, sessions et appels d'IA, masqués à la source ; client des réglages, bilans et décisions de la conscience d'une IA, journal des exécutions d'agents ; propositi

如何安装 @astratra/centrale?

在项目目录运行 npm install @astratra/centrale,或使用 pnpm add / yarn add / bun add。

@astratra/centrale 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@astratra/centrale 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@astratra/centrale 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@astratra/centrale 可以商用吗?

@astratra/centrale 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@astratra/centrale 的下载量有多少?

根据 npm 官方下载统计,@astratra/centrale 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,734 次,日均约 91 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@astratra/centrale 还在维护吗?

最新版本 1.11.0 发布于 2026/11/05(2 天前);至今共发布 16 个版本,其中过去 12 个月发布了 16 个;首次发布于 2026/10/08。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。