API hôte
Utilisez cette page quand vous avez besoin des méthodes exactes de connectHost, des limites et des codes d’erreur. Pour la structure du dossier, le manifeste et l’installation, commencez par Créer une extension.
import { connectHost, HostRequestError } from "@openchamber/sdk";
const host = connectHost();connectHost lève HOST_UNAVAILABLE quand il n’y a pas de window. En dehors d’OpenChamber, il renvoie un client dont chaque appel est rejeté avec HOST_UNAVAILABLE. Appelez dispose() quand vous démontez la page ; les appels encore en cours sont rejetés avec le même code.
Ce qu’OpenChamber vous envoie
onReady se déclenche avec le premier instantané, puis à chaque fois qu’OpenChamber le rafraîchit.
Ce n’est pas un callback de montage unique. Montez l’interface et inscrivez les abonnements une fois, puis actualisez le thème et le contexte sans remplacer les champs ni les brouillons. Consultez l’exemple protégé dans UI kit. Les listeners comme onConnection livrent immédiatement la valeur actuelle et peuvent la répéter avec un nouvel instantané. Comparez les champs utiles avant une nouvelle requête et ignorez les réponses des requêtes remplacées.
| Champ | Ce que c’est |
|---|---|
theme.mode | light ou dark |
theme.tokens | les couleurs de l’application (surfaces, texte, états d’interaction, primary, success/warning/error/info), font, mono et radius. Passez-le à applyHostReady avant de monter l’UI. |
locale | le tag de langue de l’application |
directory | le dossier du projet en cours, ou null |
session | { id, title, busy } ou null. Le titre retombe sur l’id de session. busy est le statut en direct. model et agent (l’agent OpenCode) apparaissent quand la session les a. |
surface | panel dans la barre latérale, dialog dans la fenêtre d’attache, page en plein écran |
item | ce pour quoi cette surface a été ouverte, ou null : l’élément attaché sur lequel l’utilisateur a cliqué (les mêmes champs que vous avez passés à attach, y compris data), un message (kind: "message") ou une session (kind: "session") venant d’une de vos actions déclarées. Voir Actions. |
connection | { connected, account } pour votre intégration |
settings | les valeurs des champs déclarés dans integration.settings |
Les tokens d’accès n’apparaissent jamais ici ni dans un résultat de request.
onDirectory, onSession, onSessionLifecycle, onConnection, onSettings et onItem rejouent la dernière valeur quand vous vous abonnez tard, puis continuent à se déclencher à chaque changement.
theme.tokens contient primaryText, successText, warningText, errorText et infoText. Ces champs obligatoires contiennent les couleurs de texte calculées par l’hôte pour les fonds neutres et les contrôles légèrement teintés du kit UI, pas pour les aplats de couleur vive. applyHostReady les applique à chaque instantané. Consultez le kit UI pour les variables CSS.
Méthodes
| Appel | Ce qu’il fait |
|---|---|
toast({ kind, message }) | affiche un toast dans l’application. kind est info, success ou error. |
openUrl(url) | ouvre une URL dans le navigateur de l’utilisateur |
openSurface(surfaceId) | bascule l’application sur cet écran |
writeClipboard(text) | copie du texte. 1 à 32000 caractères. |
compose({ text, mode? }) | insère du texte dans la zone de chat sans l’envoyer. mode est append (par défaut) ou replace. 1 à 16000 caractères après trim. |
attach({ ... }) | pose une puce sur la zone de chat, au même endroit que les éléments GitHub et Linear. Une seule puce à la fois. |
startSession({ ... }) | crée une session avec cet élément attaché. Renvoie { sessionId, sent }. Nécessite la capacité sessions. |
prompt({ text, send? }) | écrit dans la session en cours, ou y envoie un message. Renvoie { sent }. L’envoi nécessite la capacité prompt. |
sessionLink({ ... }) | attache un élément à la session en cours sans en créer une |
close() | ferme la fenêtre d’attache. Ne fait rien dans la barre latérale. |
oauthStart() | ouvre la page d’autorisation du fournisseur, ou celle de Linear pour une intégration host: { provider: "linear" } |
oauthDisconnect() | oublie le token stocké, ou la connexion Linear |
request({ method, path, query?, body? }) | appelle l’apiOrigin de votre intégration avec le token de l’utilisateur attaché |
serviceRequest({ method, path, query?, body? }) | appelle le service local de votre extension (voir GUEST_SERVICES.md) |
serviceStatus() | stopped, starting, ready ou failed |
readFile(path) | lit un fichier texte. Renvoie { content }. |
writeFile(path, content) | écrit un fichier texte de façon atomique, en créant les dossiers parents. Renvoie { written: true }. |
listDir(path) | liste un dossier. Renvoie { entries: [{ name, kind }] }, kind vaut file, directory ou other. |
stat(path) | { kind, size, mtime }, kind vaut file, directory, other ou missing. |
generate({ prompt, system?, maxOutputTokens? }) | texte ponctuel du Small Model de l’utilisateur. Renvoie { text }. Nécessite la capacité model. |
onResolve(handler) | enregistre le gestionnaire de vos commandes slash. Il reçoit { command, args } et renvoie un élément à attacher ou null. |
setBadge(count) | affiche un nombre (0 à 999) sur votre icône de la barre latérale, ou null pour l’effacer |
attach, startSession et sessionLink
Les trois prennent les mêmes champs d’élément :
await host.attach({ providerId: "acme-hello", id: "TICKET-1", title: "Login is broken", url: "https://example.com/TICKET-1",});
await host.attach({ providerId: "acme-hello", id: "!12", title: "Fix login", url: "https://example.com/merge_requests/12", kind: "pull", author: "ada", branches: { head: "feature", base: "main" }, text: "Optional notes for the model",});
await host.startSession({ providerId: "acme-hello", id: "!12", title: "Fix login", url: "https://example.com/merge_requests/12", kind: "pull", worktree: true, text: "Optional first message",});providerId est l’id de votre panneau ; OpenChamber l’écrase de toute façon avec votre id. id est votre propre identifiant pour l’élément. kind est issue (par défaut) ou pull. text est un contexte optionnel pour le modèle, 1 à 16000 caractères après trim. data est un JSON optionnel à vous (statut, commentaires, n’importe quoi), jusqu’à 16000 caractères une fois sérialisé. OpenChamber le stocke avec la puce et vous le rend tel quel dans ctx.item quand l’utilisateur clique sur la puce ; il n’atteint jamais le modèle.
startSession accepte projectId sans changer le projet actif. Omettez worktree pour le dossier cible, utilisez true pour un worktree généré, { kind: "existing", directory } pour un existant ou { kind: "new", name?, baseBranch? } pour un nouveau. Le nom désigne la branche et le worktree. navigation vaut "preserve" par défaut ; "open" ouvre le nouveau chat. Le modèle, l’agent et la variante du premier message sont capturés au démarrage.
Le résultat contient sessionId, directory, sent, linked et éventuellement worktree. sent vaut sent, no-model, skipped ou failed ; linked: false indique que l’élément n’a pas été enregistré. Si un worktree reste après un échec de préparation ou de création de session, sessionId vaut null et failure vaut bootstrap-failed ou session-create-failed. Vérifiez ce résultat avant de réessayer. Le délai maximal est de 180 secondes ; une expiration ne prouve pas une annulation.
sessionLink attache l’élément à la session ouverte en ce moment. Pas de projet ou pas de session donne une erreur NO_SESSION.
Limites que le client applique avant tout envoi :
| Champ | Caractères max |
|---|---|
id | 128 |
title | 200 |
url | 2000 |
text | 16000 |
data (sérialisé) | 16000 |
author | 80 |
| chaque nom de branche | 200 |
prompt et cycle de vie de la session
await host.prompt({ text: "Fix the login" });await host.prompt({ text: "Fix the login", send: true });
host.onSessionLifecycle((event) => { document.body.dataset.phase = event.phase;});Sans send, prompt remplace le texte de la zone de chat. Avec send: true, il envoie le message avec le modèle et l’agent choisis par l’utilisateur. L’extension ne les choisit jamais. Pas de session ouverte donne NO_SESSION. Envoyer pendant que la session est occupée donne SESSION_BUSY ; écrire dans la zone fonctionne même quand elle est occupée. Le résultat est { sent } avec les mêmes valeurs que startSession.
onSessionLifecycle vous dit ce que fait la session. phase est started pendant que le modèle travaille, completed quand elle redevient inactive, et failure sur un statut inattendu. Un écouteur tardif reçoit tout de suite la phase courante.
Projets, sessions en direct et stockage
listProjects(), listWorktrees(projectId) et listSessions(projectId) utilisent la permission sessions et les stores partagés, sans parcours Git par appel ni contenu des conversations. Les projets exposent ID, nom et dossier ; les worktrees ajoutent branche et disponibilité. Les sessions incluent métadonnées, dates, parent, worktree et seulement les éléments attachés par cette extension. Les sessions archivées connues sont incluses.
await onProjects(listener), await onWorktrees(projectId, listener) et await onSessions(projectId, listener) renvoient une fonction de désabonnement. Interceptez les erreurs d’inscription. Un premier instantané précède les changements. Maximum 32 abonnements par cadre ; dispose(), fermeture, pause, suppression et changement de serveur les libèrent. state vaut loading, ready ou error, avec coverage par dossier pour les sessions. Une erreur conserve les données disponibles. Seul ready confirme une liste vide complète.
activity vaut unknown, idle, running, retrying, waiting-permission ou waiting-question. outcome vaut completed, failed ou null, uniquement d’après les événements observés, en mémoire pour 2 000 sessions au maximum. Idle après une erreur conserve cet échec jusqu’au prochain lancement. Cela ne termine pas votre tâche. openSession(sessionId) ouvre explicitement le chat.
host.storage.get(key), set(key, value), delete(key) et keys() conservent votre JSON sans permission supplémentaire. Une clé absente donne undefined, null enregistré reste null. Clés de 1 à 128 caractères, valeur de 64 KiB UTF-8 maximum, ensemble de 2 MiB et 2 000 clés maximum. Incluez l’ID du projet dans la clé pour des données propres à un projet. Les écritures sont sérialisées et atomiques ; les erreurs préservent les données. La désinstallation supprime ce stockage sur le serveur connecté.
request
const user = await host.request({ method: "GET", path: "/api/v2/user" });path commence par / et n’a ni schéma ni hôte ; OpenChamber le joint à l’apiOrigin de votre manifeste et ajoute l’en-tête Authorization. Les intégrations par token envoient le token collé tel quel, sous la forme Bearer <token> quand le manifeste définit token.scheme: "bearer", ou sous la forme Basic base64(username:token) quand il définit "basic". Les intégrations OAuth et Linear envoient toujours Bearer. Le résultat est { status, body }. Le corps est du texte ; parsez le JSON vous-même.
Une intégration Linear peut aussi appeler GET /api/linear/issues/get, auquel OpenChamber répond depuis sa propre route Linear.
Pas de réponse en 20 secondes donne HOST_TIMEOUT.
try { await host.request({ method: "GET", path: "/api/v2/user" });} catch (error) { if (error instanceof HostRequestError && error.code === "DISCONNECTED") { await host.oauthStart(); }}serviceRequest
Quand le manifeste déclare contributes.service, OpenChamber démarre ce processus et votre page lui parle via le même type d’appel. La page n’ouvre jamais le socket elle-même.
const status = await host.serviceStatus();const result = await host.serviceRequest({ method: "GET", path: "/containers" });path suit les mêmes règles que request. Déclarer un service local ajoute service aux capacités que l’utilisateur approuve à l’installation ; tant que ce n’est pas fait, chaque serviceRequest donne NO_SERVICE. Contrat complet : le fichier GUEST_SERVICES.md du package.
Fichiers
Un chemin relatif se trouve dans le projet ouvert et nécessite la capacité files. Un chemin qui commence par / ou ~/ doit correspondre à un motif contributes.filesystem déclaré et nécessite la capacité filesystem. Voir Créer une extension pour les règles.
const { content } = await host.readFile("package.json");await host.writeFile("notes/today.md", "# Today\n");const { entries } = await host.listDir("src");const info = await host.stat("~/.config/opencode/opencode.json");Limites : chemin jusqu’à 1024 caractères, contenu jusqu’à 2 000 000 caractères, listing jusqu’à 2000 entrées. Au-delà de la limite de contenu, c’est FILE_TOO_LARGE.
generate
const { text } = await host.generate({ prompt: notes, system: "Summarize in one sentence." });prompt fait de 1 à 64 000 caractères après trim, system jusqu’à 8 000, maxOutputTokens de 1 à 4 000. Rien n’entre dans une session et aucun historique n’est conservé ; l’extension ne choisit jamais le modèle. L’appel attend jusqu’à 90 secondes. Aucun Small Model utilisable donne NO_MODEL ; une erreur du modèle donne MODEL_FAILED. Voir Générer du texte.
Codes d’erreur
Les appels en échec lèvent HostRequestError. code est l’un des suivants ; message en dit plus.
| Code | Quand |
|---|---|
HOST_UNAVAILABLE | pas de window, pas à l’intérieur d’OpenChamber, ou dispose() a été appelé |
HOST_TIMEOUT | pas de réponse pendant 20 secondes |
HOST_REJECTED | OpenChamber a refusé, ou a répondu avec un code que ce SDK ne connaît pas |
DISCONNECTED | pas de token ni de connexion Linear pour votre intégration |
BAD_PATH | path était mal formé ou tentait de sortir de l’origine autorisée |
NO_INTEGRATION | le manifeste n’a pas d’integration |
NO_SESSION | prompt ou sessionLink sans session ouverte |
SESSION_BUSY | prompt({ send: true }) pendant que la session était occupée |
DISABLED | l’utilisateur a mis l’extension en pause dans Paramètres → Extensions |
NO_SERVICE | pas de service local déclaré, non approuvé, ou pas en cours d’exécution |
NOT_GRANTED | l’utilisateur n’a pas approuvé la capacité dont cet appel a besoin |
SERVICE_FAILED | le service local a planté ou n’est jamais devenu prêt |
NO_DIRECTORY | un chemin de fichier relatif a été utilisé sans projet ouvert |
NOT_FOUND | readFile sur un fichier qui n’existe pas |
FILE_TOO_LARGE | contenu du fichier au-delà de la limite, en lecture ou en écriture |
DENIED | le système d’exploitation a refusé l’opération sur le fichier |
NO_MODEL | generate sans Small Model disponible |
MODEL_FAILED | le Small Model a renvoyé une erreur |
Voir aussi
- Créer une extension pour le dossier, le manifeste, l’installation et les capacités
- Kit UI pour les boutons, champs, listes et les autres composants