Guide du SDK OOMOL Connector
@oomol-lab/connector est le client TypeScript pour la passerelle OOMOL Connector. Utilisez-le pour ajouter rapidement des intégrations d’apps au backend de votre produit sans gérer vous-même l’infrastructure connector. Votre code appelle la passerelle hébergée d’OOMOL via le SDK ; OAuth, le renouvellement des tokens et le stockage des identifiants provider restent côté passerelle.
Le package est léger et sans dépendance ; le SDK construit la requête et analyse la réponse. Les appels comme gmail.search_threads, slack.post_message et notion.append_block ne nécessitent ni codegen ni CLI.
Sujets principaux :
- Installer le SDK, obtenir une API key et effectuer votre premier appel.
- Les cinq mots qui décrivent tout le modèle : gateway, provider, action, connection, organization.
- Les deux chemins d’appel (chaîne dynamique et namespace) et la lecture des métadonnées d’exécution.
- Activer des types précis par action sans codegen.
- Transmettre des endpoints non modélisés, inspecter le catalog et lister les apps connectées.
- Utiliser
ProjectConnectorpour connecter des comptes au nom de vos utilisateurs finaux et exécuter des actions pour eux. - Utiliser
OpenConnectorpour exécuter les mêmes actions sur un runtime Connector open source que vous hébergez vous-même. - La forme des erreurs, celles qui sont réessayables, et comment définir le scope, le timeout et l’annulation des appels.
Installation
npm install @oomol-lab/connector # ou : bun add / pnpm add / yarn add
Nécessite Node ≥ 18 pour fetch et AbortController intégrés. Le SDK ne publie que dist, n’a aucune dépendance runtime et déclare sideEffects: false, ce qui permet un tree-shaking propre. Il fonctionne dans Node, Bun, Deno, les runtimes edge et les apps basées navigateur avec un fetch standard ; dans le navigateur, injectez l’API key depuis l’hôte.
Obtenir une API key
Vous avez besoin d’une API key personnelle OOMOL Connector, de la forme api_…. Créez-la dans OOMOL Console :
https://console.oomol.com/api-key
Définissez-la comme variable d’environnement. Ce guide utilise OOMOL_API_KEY partout. La passerelle autorise chaque requête et reçoit la clé sous la forme Authorization: Bearer <apiKey>.
Une clé personnelle
api_…exécute les actions sur vos propres connections. Pour connecter des comptes pour vos utilisateurs finaux, utilisez une clé project distincte (oo_proj_…) et le clientProjectConnector; voir Connecter des comptes pour vos utilisateurs. Pour auto-héberger le runtime au lieu d’utiliser la passerelle hébergée, le serveur open source utilise son propre runtime token optionnel (oct_…) et le clientOpenConnector; voir Runtime auto-hébergé.
Démarrage rapide
Construisez un client, puis appelez une action. Les deux formes ci-dessous sont équivalentes.
import { Connector } from "@oomol-lab/connector";
const oomol = new Connector({ apiKey: process.env.OOMOL_API_KEY! });
// Chemin 1 : chaîne dynamique. Appelable pour n'importe quel action id.
const { threads } = await oomol.execute("gmail.search_threads", { query: "from:boss" });
// Chemin 2 : syntaxe namespace. Même appel en dessous.
const result = await oomol.gmail.search_threads({ query: "is:unread" });
execute renvoie directement la sortie de l’action. Si vous voulez aussi les métadonnées d’exécution, utilisez executeRaw :
const raw = await oomol.executeRaw("gmail.search_threads", { query: "from:ceo" });
raw.data; // la même valeur que celle renvoyée par execute()
raw.executionId; // id d'exécution attribué par le serveur (utile pour support / corrélation de logs)
raw.actionId; // action id renvoyé en écho
raw.message; // message lisible de l'enveloppe de succès
Le flux d’appel central repose sur execute et executeRaw.
Concepts
Le modèle du SDK a cinq concepts principaux. L’authentification, les identifiants et les appels provider sont gérés par la passerelle :
| Terme | Définition |
|---|---|
| Gateway | Le service OOMOL Connector hébergé auquel ce client parle. Il conserve les identifiants, effectue les appels provider réels et renvoie une enveloppe uniforme. Le SDK n’exécute aucune logique d’intégration localement. |
| Provider / service | Une API tierce (gmail, slack, github, notion, …). C’est le préfixe <service> d’un action id. |
| Action | Une opération sur un provider, identifiée par "<service>.<action>" (par exemple gmail.search_threads). Les actions sont fournies par la passerelle et appelées par le client. |
| Connection | Un identifiant stocké et déjà autorisé pour un provider. Vous ne touchez jamais les tokens ; vous indiquez quelle connection utiliser via connectionName. OAuth et le cycle de vie des identifiants relèvent de la passerelle. |
| Organization | Scope de tenant optionnel, envoyé dans l’en-tête x-oo-organization-name. |
Le même vocabulaire se retrouve dans toute l’interface du SDK : un action id est toujours "<service>.<action>", connectionName sélectionne une connection stockée et, pour le client project, externalUserId identifie l’un de vos utilisateurs finaux.
Opérations courantes
| Besoin | Utiliser | Notes |
|---|---|---|
| Exécuter une action modélisée | execute / executeRaw | L’appel typé en une ligne. executeRaw renvoie aussi { executionId, actionId, message }. |
| Appeler un endpoint qui n’est pas encore modélisé comme action | proxy | Transmission vers l’API amont, avec les identifiants de la connection injectés par la passerelle. |
| Fournir des actions à un LLM / construire des formulaires dynamiques | catalog | JSON Schema runtime (2020-12) pour n’importe quelle action ou provider. |
| Découvrir ce qui est connecté | apps.list | Liste en lecture seule des connections déjà liées. |
| Laisser vos utilisateurs connecter leurs comptes | ProjectConnector | Client distinct, scopé par project, pour connecter des comptes au nom de vos utilisateurs finaux et exécuter des actions pour eux. |
La couverture des providers et actions est fournie par la passerelle. Elle prend actuellement en charge 600+ providers et continue de s’étendre ; découvrez-la au runtime avec oomol.catalog.providers().
Types précis (optionnel)
Le chemin par chaîne dynamique compile pour n’importe quel actionId. Par défaut, chaque action utilise des types souples (Record<string, any> en entrée et en sortie), ce qui permet d’appeler immédiatement les nouvelles actions. Pour obtenir des types précis d’entrée/sortie par action avec la JSDoc, installez le package de types compagnon et ajoutez un import à effet de bord par provider utilisé :
import { Connector } from "@oomol-lab/connector";
import "@oomol-lab/connector-types/gmail"; // types précis + JSDoc pour gmail.*
import "@oomol-lab/connector-types/slack"; // …et slack.*
const oomol = new Connector({ apiKey: process.env.OOMOL_API_KEY! });
await oomol.gmail.search_threads({ query: "from:boss" }); // entrée + sortie maintenant précises
await oomol.notion.append_block({ pageId, text }); // notion non importé → appel toujours souple
npm install -D @oomol-lab/connector-types
Pas de codegen, pas de fichiers générés à commit, pas de CLI. Les actions enregistrées obtiennent l’autocomplétion littérale et des types exacts d’entrée/sortie ; les actions non enregistrées se dégradent en Record<string, any>. Si le package de types est en retard sur le backend, les nouvelles actions restent appelables via le fallback souple. Le runtime principal ne dépend pas du package de types.
Nécessite
moduleResolutiondéfini surbundler,node16ounodenextpour que les imports de sous-chemins (@oomol-lab/connector-types/gmail) se résolvent. Consultez le dépôt@oomol-lab/connector-typespour les détails de configuration.
Configuration
Tous les champs sauf apiKey sont optionnels :
new Connector({
apiKey: process.env.OOMOL_API_KEY!, // obligatoire
baseUrl: "https://connector.oomol.com/v1", // valeur par défaut
organization: "acme", // org par défaut → x-oo-organization-name
connectionName: "work", // connection par défaut (préférer par appel / using())
timeoutMs: 30_000, // timeout par requête par défaut
maxRetries: 2, // par défaut ; retente 429 / 5xx / réseau avec backoff + jitter
fetch: customFetch, // injection pour tests / proxies / tracing
});
| Champ | Défaut | Notes |
|---|---|---|
apiKey | — | Obligatoire. Envoyé sous la forme Authorization: Bearer <apiKey>. |
baseUrl | https://connector.oomol.com/v1 | Remplacement manuel uniquement ; il n’y a pas de bascule automatique basée sur l’environnement. |
organization | — | Tenant sous lequel l’appel s’exécute. |
connectionName | — | Connection stockée à utiliser quand un provider en a plusieurs. Utilisez-la comme défaut client pour les configurations simples ; avec plusieurs connections, définissez-la par appel ou via using(). |
timeoutMs | 30_000 | Timeout par requête, en millisecondes. |
maxRetries | 2 | Retente sur 429 / 5xx / erreurs réseau, avec backoff exponentiel et jitter. |
fetch | fetch global | Injecte un fetch personnalisé pour les tests, agents proxy ou tracing. |
Scopes et options par appel
Trois couches sont résolues dans cet ordre de priorité : options par appel > scope using() > valeurs par défaut du client.
using() renvoie un sous-client scopé immuable qui fusionne les valeurs par défaut données ; le client d’origine reste intact :
const work = oomol.using({ connectionName: "work", organization: "acme" });
await work.gmail.search_threads({ query: "label:urgent" }); // s'exécute sous "work" / "acme"
Les options par appel ne s’appliquent qu’à cet appel et ont la priorité la plus élevée :
await oomol.execute(
"gmail.search_threads",
{ query: "from:ceo" },
{
organization: "acme", // remplace l'org pour cet appel
connectionName: "alt", // choisit une autre connection pour cet appel
timeoutMs: 10_000, // timeout plus strict pour cet appel
retries: 0, // désactive les retries pour cet appel
signal: controller.signal, // transmet un AbortSignal
},
);
connectionName se résout avec les mêmes couches. Il est transporté sur le fil comme en-tête x-oo-connector-alias (le champ passerelle s’appelle alias) ; l’interface du SDK utilise connectionName.
Les trois clients en bref
Le package exporte trois clients : un pour les connections personnelles, un pour les connections d’utilisateurs finaux sous un project, et un pour un runtime que vous hébergez vous-même. Ils partagent le transport et le modèle d’erreur ; leurs identifiants, méthodes et types restent séparés.
Connector | ProjectConnector | OpenConnector | |
|---|---|---|---|
| Auth | clé personnelle api_… | clé project oo_proj_… | runtime token optionnel oct_… |
| Parle à | passerelle hébergée d’OOMOL | passerelle hébergée d’OOMOL | runtime open source que vous exécutez |
| Agit sur | vos propres connections | les connections de vos utilisateurs finaux | vos propres connections sur votre serveur |
| Identifie un utilisateur | — | externalUserId (choisi par vous) | — |
| Interface | execute, executeRaw, proxy, catalog, apps, namespaces | connect.*, waitForConnection, execute, executeRaw, forUser | execute, executeRaw, catalog, apps, health, namespaces |
| Usage | appeler les providers que vous avez connectés | construire un produit SaaS où chaque utilisateur relie ses propres comptes | auto-héberger tout le runtime (localhost, Docker, votre infra) |
Cette section commence avec le Connector personnel. ProjectConnector est couvert dans Connecter des comptes pour vos utilisateurs, et OpenConnector dans Runtime auto-hébergé.
Proxy : appeler un endpoint sans action
Quand la passerelle n’a pas modélisé un endpoint comme action, appelez-le directement avec proxy. La passerelle injecte toujours les identifiants de la connection ; la requête et la réponse conservent la forme de l’API amont.
// GET typé. NOTE : le champ est `endpoint`, PAS `path`.
const repos = await oomol.proxy<Array<{ name: string }>>("github", {
endpoint: "/user/repos",
method: "GET",
query: { per_page: 5, sort: "updated" },
});
repos.status; // statut HTTP amont
repos.data.map((r) => r.name);
// POST avec body et en-têtes amont (ils vont au provider, pas à la passerelle).
await oomol.proxy("github", {
endpoint: "/repos/acme/widgets/issues",
method: "POST",
headers: { "X-GitHub-Api-Version": "2022-11-28" },
body: { title: "Tracking issue", labels: ["chore"] },
});
endpoint accepte un chemin (résolu contre la base URL du provider) ou une URL complète, utile pour les providers avec des hôtes régionaux comme https://eu.posthog.com/api/.... method est l’une de GET | POST | PUT | PATCH | DELETE. La réponse est { status, headers, data }. Le body proxy est strict côté backend : les clés de premier niveau inconnues sont rejetées avec invalid_input.
Catalog : inspecter providers et actions
Le catalog fournit des métadonnées runtime en lecture seule pour les formulaires dynamiques, la validation et les définitions d’outils LLM. Les schemas d’entrée/sortie utilisent JSON Schema (2020-12) et sont indépendants du package de types compile-time.
// Liste les providers ; possibilité de réduire côté serveur.
const all = await oomol.catalog.providers(); // tous les providers
const mailish = await oomol.catalog.providers({ q: "mail" }); // recherche texte libre → ?q=
const some = await oomol.catalog.providers({ service: ["gmail", "slack"] }); // restriction → ?service=…
// Toutes les actions d'un service.
const actions = await oomol.catalog.actions("gmail");
// Métadonnées complètes d'une action, avec JSON Schema runtime.
const meta = await oomol.catalog.action("gmail.search_threads");
meta.name; // nom lisible
meta.requiredScopes;// scopes OAuth nécessaires à l'action
meta.inputSchema; // JSON Schema (2020-12) pour l'entrée
meta.outputSchema; // JSON Schema (2020-12) pour la sortie
Chaque provider porte { service, displayName, iconUrl, homepageUrl, categories, authTypes }.
Apps : lister vos comptes connectés
apps.list() renvoie une vue en lecture seule des connections que la passerelle détient déjà pour vous. La création et la suppression de connections se font dans Console.
const apps = await oomol.apps.list();
for (const app of apps) {
// { id, service, status, connectionName, … } ; connectionName vaut null quand il n'est pas défini.
console.log(`${app.service}: id=${app.id} status=${app.status} connectionName=${app.connectionName}`);
}
// Ciblez une connection précise en repassant son connectionName comme sélecteur par appel.
const work = apps.find((a) => a.connectionName === "work");
if (work) {
await oomol.execute("gmail.search_threads", { query: "is:unread" }, { connectionName: "work" });
}
Gestion des erreurs
Les échecs lancent une ConnectorError typée. L’annulation par l’appelant (un AbortSignal interrompu) rejette avec l’AbortError standard, pour pouvoir la traiter séparément des erreurs passerelle ou transport.
import { Connector, ConnectorError, isRetryable } from "@oomol-lab/connector";
try {
await oomol.slack.post_message({ channel: "#general", text: "shipped" });
} catch (err) {
if (err instanceof ConnectorError) {
err.code; // union discriminable, p. ex. "rate_limited", "credential_expired"
err.status; // statut HTTP (0 pour les erreurs côté client / réseau)
err.requestId; // id de corrélation d'échec
err.actionId; // si applicable
err.executionId; // si applicable
err.data; // body de réponse amont, p. ex. sur provider_error
if (isRetryable(err)) {
// 429 / 5xx / réseau / rate_limited / proxy_upstream_timeout / request_in_progress
}
} else {
throw err; // non-ConnectorError, p. ex. AbortError venant d'une annulation appelant ; relancer
}
}
err.code est une union ouverte : les codes backend connus obtiennent l’autocomplétion, et les nouveaux codes backend passent quand même comme chaînes. Gardez une branche par défaut lors du traitement. Codes courants, regroupés :
| Groupe | Codes |
|---|---|
| Entrée / requête | invalid_input, invalid_request_payload, invalid_request_signature |
| App / provider | app_not_found, app_not_ready, app_auth_type_mismatch, provider_not_found, provider_not_configured, provider_config_not_found, provider_error |
| Identifiant / auth | credential_expired, scope_missing, user_oauth_client_required |
| Sélection de connection | connection_ambiguous, connection_account_conflict, connection_alias_conflict, connection_request_not_found, connected_account_not_found |
| Proxy | proxy_not_supported, proxy_upstream_error, proxy_upstream_timeout, proxy_response_too_large |
| Limite / concurrence | rate_limited, request_in_progress, request_key_conflict, request_key_used |
| Client uniquement (status 0, aucune requête envoyée ou échec de transport) | client_invalid_request, client_timeout, client_network_error, client_wait_timeout |
isRetryable(err) renvoie true pour rate_limited, proxy_upstream_timeout, request_in_progress, HTTP 429, tout 5xx et les échecs de transport (status 0). Il renvoie false pour les erreurs de validation client (client_invalid_request) et le plafond de waitForConnection (client_wait_timeout) ; ces cas nécessitent généralement de modifier l’appel ou le flux d’attente.
Annulation et timeouts
Transmettez un AbortSignal pour annuler ; définissez timeoutMs pour borner un appel unique. La couche de retry intégrée gère les échecs transitoires. Utilisez retries: 0 pour une tentative unique déterministe.
const controller = new AbortController();
setTimeout(() => controller.abort(), 50);
try {
await oomol.execute("gmail.search_threads", { query: "huge" }, { signal: controller.signal });
} catch (err) {
(err as Error).name; // "AbortError"
}
Connecter des comptes pour vos utilisateurs
Connector exécute des actions sur vos propres connections. ProjectConnector sert aux produits SaaS : vos utilisateurs finaux relient leurs propres comptes Gmail / Slack / GitHub / … via votre app, et votre backend exécute des actions pour eux. C’est le modèle d’auth gérée utilisé par des produits comme Composio et Pipedream Connect.
Avec l’auth gérée, les identifiants ne passent jamais par votre application ni par aucun modèle. Votre utilisateur autorise sur une page hébergée par la passerelle ; la passerelle stocke l’identifiant et renouvelle le token automatiquement. Votre code ne conserve que des identifiants opaques.
Identifiant de l’utilisateur final
externalUserId est la clé d’isolation utilisateur pour le client project. Vous la choisissez, généralement depuis votre propre base d’utilisateurs. Les opérations project comme connecter un compte, attendre une connection et exécuter une action sont scopées par cette valeur. Passez toujours le même externalUserId et la passerelle garde les connections de chaque utilisateur isolées.
Construire le client project
ProjectConnector est un client séparé, construit avec une API key project (oo_proj_…). Il expose uniquement les opérations scopées project et n’inclut pas l’interface execute / namespace / proxy / catalog / apps du client personnel.
import { ProjectConnector } from "@oomol-lab/connector";
const project = new ProjectConnector({ apiKey: process.env.OOMOL_PROJECT_API_KEY! }); // oo_proj_...
La configuration Console vient d’abord. Avant que votre backend ne connecte des comptes, un administrateur crée un project, une provider config et une API key project dans OOMOL Console. La configuration ponctuelle et le flux REST backend correspondant sont couverts dans le guide SaaS OOMOL Connector. Ce SDK est l’enveloppe typée autour de la même API runtime.
OAuth : créer un lien, puis attendre la fin
OAuth comporte deux étapes : créer une demande de connection en attente, envoyer l’utilisateur s’autoriser, puis attendre la fin.
// 1. Créer une demande de connection en attente pour l'un de vos utilisateurs.
const request = await project.connect.oauth("user_42", {
service: "gmail",
connectionName: "work", // nom à attribuer ; réutilisez-le ensuite pour cibler ce compte
returnUri: "https://app.example.com/connected", // où la passerelle renvoie l'utilisateur après le callback
});
// 2. Envoyer votre utilisateur vers la page d'autorisation du provider.
redirectUserTo(request.authorizationUrl);
// 3. Poller jusqu'à ce que l'utilisateur termine (ou que cela échoue / expire). Renvoie la demande finale.
const connected = await project.waitForConnection(request);
connected.status; // "connected" | "failed" | "expired"
connected.connectedAccountId; // id du compte stocké une fois connecté
authorizationUrl ouvre d’abord une page d’entrée hébergée par Connector qui nomme votre project et le provider que l’utilisateur va autoriser, puis envoie l’utilisateur vers la page OAuth du provider.

waitForConnection poll jusqu’à ce que la demande quitte l’état initiated, puis la renvoie. Si l’utilisateur ne termine pas l’autorisation, la demande devient naturellement expired. Si maxWaitMs (par défaut 600_000 ms, correspondant à l’expiration de la demande) s’écoule d’abord, une ConnectorError avec le code client_wait_timeout est lancée ; un signal interrompu rejette avec l’AbortError standard.
Quand votre returnUri est appelé, la passerelle ajoute des paramètres query que vous pouvez lire sur votre page de callback :
status=success
service=gmail
providerConfigId=pc-1
externalUserId=user_42
connectedAccountId=ca-1
…ou, en cas d’annulation / erreur provider :
status=error
code=<connector-error-code>
message=<human-readable-message>
API key / identifiant personnalisé : synchrone
Les connections par API key et identifiant personnalisé renvoient un compte immédiatement. Seul OAuth nécessite waitForConnection.
// La clé amont propre à l'utilisateur final (p. ex. une clé OpenAI sk-…), pas votre clé oo_proj_.
const account = await project.connect.apiKey("user_42", { service: "openai", apiKey: "sk-..." });
account.available; // si le compte peut exécuter des actions maintenant
// Champs d'identifiants propres au provider, validés par la passerelle contre la provider config.
await project.connect.customCredential("user_42", {
service: "jira",
values: { email: "user@acme.com", token: "..." },
});
Chaque appel connect.* identifie le provider par exactement un de service ou providerConfigId. Utilisez providerConfigId quand un project a plusieurs configs pour le même service ; service est le cas simple.
Exécuter au nom de l’utilisateur
// Le provider service est déduit du préfixe actionId ("gmail").
// Sans connectionName / connectedAccountId, le dernier compte actif de l'utilisateur est utilisé.
const out = await project.execute(
"user_42",
"gmail.search_threads",
{ query: "is:unread" },
{ connectionName: "work" },
);
Priorité de sélection du compte : connectedAccountId (un compte précis) prime sur connectionName (un compte par son nom) ; sans les deux, la passerelle utilise le dernier compte actif de l’utilisateur pour ce provider. project.executeRaw renvoie la même enveloppe { data, executionId, actionId, message } que le client personnel.
Scoper sur un utilisateur
forUser lie une fois externalUserId pour éviter de répéter l’id dans les appels suivants :
const user = project.forUser("user_42");
await user.connect.oauth({ service: "slack" });
const slack = await user.waitForConnection(/* la demande ci-dessus */ "req-id");
await user.execute("slack.post_message", { channel: "#general", text: "shipped" });
project.execute réutilise le même registre @oomol-lab/connector-types que le chemin personnel : les providers importés obtiennent des entrées/sorties précises, et les autres restent appelables de manière souple.
Cycle de vie des connections
| Objet | Quand vous l’obtenez | Champs clés |
|---|---|---|
ConnectionRequest | renvoyé par connect.oauth, relu via getConnectionRequest / waitForConnection | id, status (initiated → connected / failed / expired), authorizationUrl, connectedAccountId, externalUserId, connectionName, expiresAt |
ConnectedAccount | renvoyé de façon synchrone par connect.apiKey / connect.customCredential ; pointé par une demande OAuth terminée | id / connectedAccountId, status (active, reauth_required, error, disconnected), available, externalUserId, connectionName, service |
available vaut true uniquement quand tout ce qui est nécessaire à l’exécution est en place : la provider config est active, le compte est actif, l’app sous-jacente est active et un identifiant existe. Les deux champs status sont des unions ouvertes ; gardez une branche par défaut pour gérer les nouveaux statuts backend.
Vous venez de Composio / Pipedream ?
| Composio / Pipedream | @oomol-lab/connector |
|---|---|
userId / external_user_id | externalUserId |
connectedAccounts.initiate / createConnectToken (OAuth) | project.connect.oauth |
connectedAccounts.initiate + AuthScheme.APIKey | project.connect.apiKey |
waitForConnection() | project.waitForConnection() |
tools.execute(slug, { userId, arguments }) | project.execute(externalUserId, actionId, input) |
composio.getEntity(userId) | project.forUser(externalUserId) |
Runtime auto-hébergé
Vous exécutez vous-même le serveur Connector open source, sur localhost, dans Docker ou sur votre propre infra ? OpenConnector est le client personnel pour ce runtime. Il reflète l’interface Connector (les deux chemins d’appel, catalog, apps) en la pointant vers le serveur que vous exécutez, de sorte que le code déjà écrit change très peu. Mettre le serveur en route est un sujet séparé ; voir le guide d’auto-hébergement OpenConnector.
Le runtime open source est l’équivalent auto-hébergeable du produit personnel : un serveur, un utilisateur, des actions exécutées sur les propres connections de cet utilisateur. Il n’y a ni organization ni modèle d’utilisateur final ici ; le serveur est à vous.
import { OpenConnector } from "@oomol-lab/connector";
const open = new OpenConnector(); // par défaut http://localhost:3000 ; une instance neuve ne demande pas d'auth
await open.execute("hackernews.get_top_stories", {}); // chemin 1 — chaîne dynamique
await open.gmail.search_threads({ query: "from:boss" }); // chemin 2 — syntaxe namespace, mêmes types de registre
await open.catalog.search("send email", { limit: 5 }); // extensions catalog : search, services
await open.apps.list(); // vue en lecture seule des connections du runtime
Les deux chemins d’appel et le workflow de types précis sont identiques au Connector personnel ; les mêmes imports à effet de bord @oomol-lab/connector-types s’appliquent.
Pointer vers votre serveur
baseUrl est l’origin du serveur, pas une URL /v1 ; le client ajoute lui-même le préfixe de chemin. L’auth utilise un seul runtime token optionnel (oct_…), créé dans la console web du runtime (onglet Access). Une instance neuve sans token répond sans token ; dès qu’un token existe, le serveur l’impose.
const open = new OpenConnector({
baseUrl: "https://connect.internal.example.com", // ORIGIN du serveur — pas une URL /v1
runtimeToken: process.env.OOMOL_CONNECT_RUNTIME_TOKEN, // oct_… ; omettre tant que l'instance n'a pas de token
connectionName: "work", // connection par défaut optionnelle au niveau client
});
Chaque champ est optionnel. timeoutMs, maxRetries et fetch se comportent exactement comme sur le client hébergé.
Interface propre au runtime
OpenConnector ajoute quelques membres absents du client hébergé, et retire ceux qui ne s’appliquent pas :
await open.health(); // { ok, runtime } — sonde de connectivité / auth
await open.catalog.services(); // tous les service id qui ont des actions
await open.catalog.search("top stories", { limit: 3 }); // classe les actions par pertinence en texte libre
await open.apps.listByService("github"); // connections d'un service
await open.apps.authenticated(["github", "notion"]); // lesquels ont un VRAI identifiant stocké
Il n’y a pas de proxy ni de using(). La sélection de connection n’a que deux couches : un connectionName par appel remplace le défaut au niveau client, et sans les deux le runtime revient à sa connection "default".
La gestion se fait dans la console web, pas dans le SDK. Créer des connections, configurer des clients OAuth et créer des runtime tokens relève de l’administration serveur, volontairement en dehors de ce SDK.
OpenConnectorne fait que consommer ce que la console a configuré ; le guide d’auto-hébergement couvre cet aspect. Comme sur le client hébergé, un service id qui entre en collision avec un nom de membre (execute/executeRaw/health/catalog/apps) continue de fonctionner viaexecute("<service>.<action>", …); seule sa syntaxe namespace est masquée.
Tour complet exécutable — examples/open.ts.
Référence
Connector (clé personnelle api_…)
new Connector(config: ClientConfig)
oomol.execute(actionId, input, options?) // → sortie de l'action
oomol.executeRaw(actionId, input, options?) // → { data, executionId, actionId, message }
oomol.<service>.<action>(input, options?) // syntaxe namespace pour execute
oomol.using(scope) // → sous-client scopé immuable
oomol.proxy(service, { endpoint, method, query?, headers?, body? }, options?) // → { status, headers, data }
oomol.catalog.action(actionId, options?) // → ActionMetadata
oomol.catalog.actions(service, options?) // → ActionMetadata[]
oomol.catalog.providers(query?, options?) // → ProviderMetadata[] query: { service?: string[]; q?: string }
oomol.apps.list(options?) // → ConnectedApp[]
ProjectConnector (clé project oo_proj_…)
new ProjectConnector(config: ProjectConnectorConfig)
project.connect.oauth(externalUserId, input, options?) // → ConnectionRequest (en attente)
project.connect.apiKey(externalUserId, input, options?) // → ConnectedAccount (synchrone)
project.connect.customCredential(externalUserId, input, options?) // → ConnectedAccount (synchrone)
project.getConnectionRequest(connectionRequestId, options?) // → ConnectionRequest
project.waitForConnection(requestOrId, options?) // → ConnectionRequest options: { pollIntervalMs?, maxWaitMs?, signal?, timeoutMs? }
project.execute(externalUserId, actionId, input, options?) // → sortie de l'action
project.executeRaw(externalUserId, actionId, input, options?) // → { data, executionId, actionId, message }
project.forUser(externalUserId) // → ProjectUser (mêmes méthodes, id lié)
L’entrée connect.* est { service | providerConfigId } & { connectionName?, … } (exactement un de service / providerConfigId). Les options execute ajoutent { providerConfigId?, service?, connectedAccountId?, connectionName? }.
OpenConnector (runtime auto-hébergé, token oct_… optionnel)
new OpenConnector(config?: OpenConnectorConfig) // tous les champs sont optionnels ; baseUrl vaut par défaut http://localhost:3000
open.execute(actionId, input, options?) // → sortie de l'action
open.executeRaw(actionId, input, options?) // → { data, executionId, actionId, message }
open.<service>.<action>(input, options?) // syntaxe namespace pour execute
open.health(options?) // → { ok, runtime }
open.catalog.action(actionId, options?) // → OpenActionMetadata
open.catalog.actions(service, options?) // → OpenActionMetadata[]
open.catalog.services(options?) // → string[] (service ids qui ont des actions)
open.catalog.providers(query?, options?) // → ProviderMetadata[]
open.catalog.search(q, query?, options?) // → OpenActionSearchResult[] query: { service?, limit? }
open.apps.list(options?) // → ConnectedApp[]
open.apps.listByService(service, options?) // → ConnectedApp[]
open.apps.authenticated(services, options?) // → string[] (services avec un vrai identifiant)
options est { connectionName?, signal?, timeoutMs?, retries? } (pas de organization, pas de using()). config ajoute { baseUrl?, runtimeToken?, connectionName?, timeoutMs?, maxRetries?, fetch? }.
Exports
import {
Connector,
ProjectConnector,
OpenConnector,
ConnectorError,
isRetryable,
} from "@oomol-lab/connector";
import type {
ClientConfig, CallOptions, ScopeOptions, RawResult,
ProxyRequest, ProxyResponse, ProxyMethod,
CatalogApi, ActionMetadata, ProviderMetadata, ProviderQuery,
AppsApi, ConnectedApp,
ConnectorErrorCode,
ProjectConnectorConfig, ProjectCallOptions, ProjectExecuteOptions,
ConnectionRequest, ConnectedAccount, ProviderSelector,
OAuthConnectInput, ApiKeyConnectInput, CustomCredentialConnectInput,
OpenConnectorConfig, OpenConnectorApi, OpenCallOptions, OpenExecuteOptions,
OpenCatalogApi, OpenAppsApi, OpenHealth,
OpenActionMetadata, OpenActionFollowUp, OpenActionAsyncLifecycle,
OpenActionSearchResult, OpenSearchQuery,
} from "@oomol-lab/connector";
Des exemples exécutables et type-checkés se trouvent dans le répertoire examples/ du dépôt.
Licence
MIT, voir le dépôt connector-sdk.