quest'n'click 4.5.0 · r1

Construite pour quest'n'click 4.5.0, révision r1. Si elle est publiée, et laquelle est la courante, est dit par publication.json.

Le SDK des connecteurs

English · Type : référence · Public : contributeur · Contexte : Reality Bridge · Statut : prête · S'applique depuis : 4.2.5

Les connecteurs (email, Telnet, SSH, Open Badges) · contrat : expérimental · vérifié par la CI (matrice de support de la 4.4.0, 2026-10-23) : Node 22 sous Ubuntu (Telnet et SSH pas sous Windows) · vérifications automatiques : jobs connectors, connector-archive-e2e ; plan de CI reality ; 3 fichiers de tests · passes humaines : connectors-real-security pas faite (#79) · limites connues : bridge-sdk-not-a-package-api

Ce que partagent tous les connecteurs (connectors/src/sdk.ts, ADR 0008) : l’interface qu’un connecteur implémente, le contexte qu’il reçoit, le vocabulaire des refus, les limites, les fonctions utilitaires, et la façon dont les connecteurs sont testés sans réseau. Pour les contributeurs qui modifient un connecteur ou en ajoutent un ; les opérateurs lisent la Vue d’ensemble des connecteurs.

Limite · bridge-sdk-not-a-package-api Le SDK n’est pas l’API publique d’un paquet : quest-n-click-connectors n’exporte que sa commande (./run), et les quatre connecteurs sont listés par identifiant dans connectors/src/registry.ts. Un nouveau connecteur est une modification du dépôt du moteur, avec son modèle de menace et ses tests, pas une extension chargée de l’extérieur.

RealityConnector

fonction apparue en 4.1.9

Forme

interface RealityConnector {
  readonly id: string;
  start(ctx: ConnectorContext): Promise<void>;
  stop(): Promise<void>;
  health(): Promise<{ ok: boolean; detail: string; since: string }>;
}

Valeurs et défaut

Contraintes

Un connecteur reçoit son entrée, la valide et la normalise, la traite comme hostile, la rattache à un joueur, lui donne une dedupeKey, et propose ; il n’exécute jamais ce qu’il lit, et n’envoie jamais au Bridge ce qu’il a vu.

Exemple minimal

Le connecteur Telnet, connectors/src/telnet/connector.ts, est le plus court des quatre.

Comportement face à un champ inconnu

Sans objet : une interface du code.

Depuis

4.1.9.

ConnectorContext

fonction apparue en 4.1.9

Forme

Fabriqué par createContext({ bridge, gameId, tenantId?, limits?, manifest, connector, write?, now?, retries?, fetch? }).

Valeurs et défaut

MembreQuoi
bridge{ url, token } : le Bridge et le Biscuit atténué du connecteur
gameId, tenantIdLe jeu ; le tenant (default)
limitsConnectorLimits
manifestLe manifeste Reality du jeu : ses signaux et ses déclarations connectors
log(event, fields?)Une ligne JSON qui ne peut pas porter de contenu (voir safeLog)
propose(signal)ProposedSignal → { ok: true, sequence, duplicate } ou { ok: false, refusal }
pair(code)Confirme un code d’appairage (le jeton doit porter pair(true)) → { ok: true, playerId } ou un refus
reject(reason, fields?)Une entrée refusée par le connecteur lui-même : comptée et journalisée
metrics(){ proposed, accepted, duplicates, refused, rejectedInputs, retries }, pour /metrics

Contraintes

propose refuse avant le Bridge un type ou un joueur qui n’est pas un identifiant, une dedupeKey de plus de 256 caractères (shape), un signal que le manifeste ne déclare pas (not-declared), une charge qui n’est pas plate ou est trop grosse, et une proposition au-delà du quota local (quota). Un appel qui dépasse son délai, perd sa connexion, reçoit une 5xx ou une acceptation sans sa séquence est renvoyé avec la même clé, retries fois (2 par défaut) ; un refus n’est jamais réessayé. Après close(), chaque appel est refusé stopped.

Exemple minimal

const r = await ctx.propose({
  source: 'telnet', kind: 'terminal.lamp', playerId, dedupeKey: dedupeKey('telnet', `${session}:${line}`),
  payload: { command: 0, line }, receivedAt: new Date().toISOString(),
});

Comportement face à un champ inconnu

Une réponse du Bridge qui n’est pas un objet JSON se lit comme {}.

Depuis

4.1.9.

ProposedSignal

fonction apparue en 4.1.9

Forme

{ source, kind, playerId, sessionId?, dedupeKey, payload, receivedAt }.

Valeurs et défaut

Contraintes

Une charge imbriquée, balisée ou trop grosse est refusée (shape, too-large).

Exemple minimal

Voir ConnectorContext.

Comportement face à un champ inconnu

Non envoyé : le SDK construit le corps destiné au Bridge à partir des seuls champs ci-dessus.

Depuis

4.1.9.

RefusalCode

fonction apparue en 4.1.9

Forme

Un vocabulaire fixe, jamais le texte libre du Bridge.

Valeurs et défaut

CodeD’où
shape, too-large, quota, not-declared, stoppedLe SDK, avant le Bridge (shape aussi pour un 400 du Bridge, too-large pour son 413)
deniedLe 401 ou le 403 du Bridge
revokedLe 401 revoked du Bridge
playerLe 404 du Bridge pour un joueur
pairingUn code mal formé ; le 404 du Bridge pour un code, 409 ou 410
pendingLe 429 pending du Bridge (l’arriéré du joueur)
bridge-quotaLes autres 429 du Bridge
timeout, unreachablePas de réponse dans timeoutMs ; pas de connexion
bridgeUn autre statut, ou une 5xx après les nouvelles tentatives

Contraintes

Comptés dans metrics().refused et journalisés en signal.refused avec le code.

Exemple minimal

if (!r.ok && r.refusal === 'pending') … (le joueur a trop de signaux en attente).

Comportement face à un champ inconnu

Un statut que la table ne nomme pas donne bridge.

Depuis

4.1.9.

ConnectorLimits

fonction apparue en 4.1.9

Forme

{ maxBytes, maxPerMinute, timeoutMs }, les limits de la configuration.

Valeurs et défaut

maxBytes 4 096 (une charge en JSON canonique, et la plus grande entrée lue d’un coup), maxPerMinute 60 (propositions par minute glissante, tous joueurs confondus ; le Bridge a son propre quota par jeton), timeoutMs 5 000 (un appel au Bridge).

Contraintes

Une limite absente garde sa valeur par défaut.

Exemple minimal

"limits": { "maxBytes": 4096, "maxPerMinute": 60, "timeoutMs": 5000 }

Comportement face à un champ inconnu

Ignoré.

Depuis

4.1.9.

Fonctions utilitaires

fonction apparue en 4.1.9

Forme

dedupeKey(source, external), canonicalJson(value), payloadProblem(payload, maxBytes), safeLog(write, connector), within(work, ms), DRAIN_MS, DEFAULT_LIMITS.

Valeurs et défaut

Contraintes

Aucune.

Exemple minimal

dedupeKey('email', messageId) est la clé du connecteur e-mail.

Comportement face à un champ inconnu

Sans objet.

Depuis

4.1.9.

Tester sans réseau

Tout dans le dépôt du moteur, une suite à la fois :

# dans le dépôt du moteur
npx vitest run tests/connectors-*.test.ts
npm run fuzz:connectors -- --cases=200 --seconds=30 --seed=1

Garantie · bridge-sdk-same-key-one-effect · référencée · exécutée sur aff6caab7514 · portée vérifiée par son auteur (IA), pas relue Deux connecteurs qui envoient chacun trois fois la même clé font une seule séquence et un seul effet logique.


Modifier la source de cette page · Signaler une erreur