SDK TypeScript / JavaScript
Le SDK client officiel TypeScript / JavaScript pour Scrydon — authentification OAuth/PKCE et surface typée pour les workflows, la connaissance, le stockage, les complétions LLM et les webhooks
@scrydon/sdk est le SDK client officiel pour les applications navigateur, TanStack Start / Next.js et Node.js. Il authentifie les utilisateurs finaux via OAuth 2.0 avec PKCE et expose des modules typés pour les workflows, les complétions LLM gouvernées, la connaissance, le stockage et les webhooks, ainsi que la surface SDK data / ai / action de la plateforme.
Ce SDK est destiné aux applications first-party agissant au nom d'un utilisateur (flux OAuth). Pour une exécution serveur-à-serveur avec une clé API statique — backends, tâches cron, CI — appelez l'API de workflow directement avec x-api-key (voir Exécution).
Installation
bun add @scrydon/sdknpm install @scrydon/sdkyarn add @scrydon/sdkDémarrage rapide
import { ScrydonClient } from '@scrydon/sdk'
const client = new ScrydonClient({
baseUrl: 'https://app.example.com', // sign-in and the Platform API
agenticUrl: 'https://app.example.com/agentic', // workflows, automations, models, …
clientId: 'your-oauth-client-id',
redirectUri: 'https://your-app.com/auth/callback',
})
// 1. Start the OAuth/PKCE flow — returns the authorization URL
const authUrl = await client.auth.signIn()
window.location.assign(authUrl)
// 2. On your callback page, exchange the code for a session
const session = await client.auth.handleCallback() // reads window.location by default
// 3. Call typed APIs
const result = await client.workflows.trigger({
workflowId: 'wf_…',
inputs: { message: 'Hello, world!' },
})
console.log(result.executionId, result.status, result.outputs)Configuration
Un déploiement sert le SDK depuis deux endroits ; le client prend donc deux URL :
| Champ | Ce qu'il atteint | Routage en sous-chemin (par défaut) | Routage en sous-domaine |
|---|---|---|---|
baseUrl | La connexion (/api/auth) et l'API Platform (/api/platform/v1 : client.data, client.ai, client.action, client.extensions, client.tools, client.chat.completions, client.knowledge.query) | https://app.example.com | https://auth.example.com |
agenticUrl | L'API Agentic : workflows, automatisations, blocs, modèles, stockage, ontologie et les autres méthodes de connaissance | https://app.example.com/agentic | https://agentic.example.com |
agenticUrl est facultatif. Sans lui, les requêtes Agentic partent vers baseUrl, ce qui
ne fonctionne que si une même adresse sert les deux, par exemple un proxy placé devant votre déploiement.
| Champ | Obligatoire | Signification |
|---|---|---|
baseUrl | oui | L'origine du déploiement, ci-dessus |
agenticUrl | non | L'API Agentic, ci-dessus |
clientId | oui | L'identifiant de votre client OAuth |
redirectUri | oui | L'adresse de retour après la connexion ; elle doit être enregistrée sur le client OAuth |
environmentId | non | Envoyé en ?environmentId= sur chaque requête Agentic et sur client.chat.completions |
Authentification — OAuth 2.0 + PKCE
Le SDK utilise le flux PKCE pour être sécurisé dans les navigateurs sans secret client. Le vérificateur est conservé dans sessionStorage (navigateur) ou en mémoire (autres environnements).
auth.signIn()
const authUrl = await client.auth.signIn()
// Redirect the user to authUrl. After consent they come back to redirectUri
// with ?code=… and ?state=… in the query string.auth.handleCallback(callbackUrl?)
// On the callback route — defaults to window.location.href
const session = await client.auth.handleCallback()
// session: { user: { id, email, name? }, accessToken, expiresAt }auth.getSession()
const session = await client.auth.getSession() // null if signed out / expiredauth.signOut()
await client.auth.signOut()auth.onAuthStateChange(cb)
const unsubscribe = client.auth.onAuthStateChange((session) => {
if (session) {
console.log('signed in as', session.user.email)
} else {
console.log('signed out')
}
})
// Later: unsubscribe()client.workflows
trigger(options) — synchrone
const result = await client.workflows.trigger({
workflowId: 'wf_abc',
inputs: { message: 'Hello' },
})
// result: { executionId, status, outputs? }POST /api/v1/workflows/{workflowId}/trigger. Idéal pour les workflows courts qui répondent en ligne.
triggerAsync(options) — déclenchement et attente
const { executionId } = await client.workflows.triggerAsync({
workflowId: 'wf_abc',
inputs: { message: 'Long task' },
})
// Poll for status when you're ready
const result = await client.workflows.getStatus(executionId)
// result: { executionId, status: 'running' | 'completed' | 'failed', outputs? }POST /api/v1/workflows/{workflowId}/trigger?async=true. À utiliser pour les workflows de longue durée.
getStatus(executionId)
const result = await client.workflows.getStatus(executionId)GET /api/v1/workflows/executions/{executionId}.
client.chat
Complétions LLM gouvernées compatibles OpenAI. Le SDK ne crée pas de session publique de chat de workflow ; envoyez l'historique dans messages et conservez-le dans votre application si nécessaire.
const stream = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: 'Summarize Q3' }],
stream: true,
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta.content ?? '')
}Consultez le guide de migration du SDK 1.0 si vous utilisiez auparavant client.chat.stream ou useChatStream.
client.knowledge
const hits = await client.knowledge.query({
question: 'What is our refund policy?',
topK: 5,
})
// hits: Array<{ content, source, score }>
await client.knowledge.ingest({
documents: [file1, file2],
collection: 'policies',
})client.ontology
Des couches géographiques prêtes pour la carte, projetées depuis votre
ontologie — une couche par type d'objet dont les instances portent des
coordonnées (latitude/lat, longitude/lng/lon). Omettez toutes les
options pour découvrir chaque type cartographiable dans vos ontologies
lisibles ; passez objectTypes pour restreindre.
const layers = await client.ontology.queryGeo({
objectTypes: ['Aircraft', 'MilitaryBase'], // optional — omit to discover
ontologySlug: 'my-domain', // optional — omit to search all
limit: 300, // max instances per type (1–1000)
})
// layers: Array<{ objectTypeSlug, label, features }>
// features: Array<{ id, lat, lng, label, properties }>La géo est une projection dédiée plutôt qu'une requête d'instances générique : l'extraction des coordonnées, la validation des plages et la traversée des bindings s'exécutent côté serveur, le client reçoit donc des features prêtes à rendre.
client.storage
const { url } = await client.storage.upload(file, { path: 'reports/q3.pdf' })
const directLink = await client.storage.getUrl('reports/q3.pdf')client.webhooks
Un émetteur d'événements en processus simple pour livrer les payloads webhook aux abonnés de votre application.
const off = client.webhooks.on('workflow.completed', (data) => {
console.log('workflow completed', data)
})
// Later: off()SDK plateforme — client.data, client.ai, client.action
Le client expose le ScrydonSDK typé de la plateforme :
client.data.knowledge.search(...),client.data.knowledge.memex.query(...),client.data.storage.read(...),client.data.memory.add(...)— lecture / écriture de la surface de données de l'espace de travailclient.ai.llm.complete(...)— appels LLM résolus par capacitéclient.action.workflows.execute(...),client.action.email.send(...),client.action.sms.send(...),client.action.functions.execute(...),client.action.collaboration.post(...)— actions côté serveur
Consultez @scrydon/sdk/types pour l'ensemble complet des types de plateforme.
Le SDK Plateforme porte des surfaces au-delà des quatre exposées sur
ScrydonClient. Construisez le client complet avec createScrydonSDK depuis
@scrydon/sdk/platform pour les atteindre — par exemple platform.geo.*,
documenté dans Geo. client.action.collaboration.post est documenté dans
Collaboration : elle ne publie que depuis une
exécution automatisée (un workflow, un process flow ou un tour Cortex), et elle
répond CAPABILITY_NOT_CONFIGURED là où le service de messagerie Cortex ne
tourne pas (voir Disponibilité).
Borner la réponse — outputConstraints
Demander « une page » à un modèle dans l'invite est une requête, pas une limite.
outputConstraints sur client.ai.llm.complete(...) et sur son flux en est la
forme contraignante : la plateforme insère les limites dans l'invite, mesure la
réponse et rapporte ce qu'elle a mesuré.
const result = await client.ai.llm.complete({
messages: [{ role: "user", content: "Résume l'appel d'offres joint." }],
outputConstraints: { maxWords: 300 },
});
result.constraintReport;
// { format: "text", breached: false, repaired: true, repair: "accepted",
// measurements: [{ measure: "words", limit: 300, measured: 287, breached: false }] }| Champ | Borne |
|---|---|
maxWords | Mots. Les idéogrammes CJK et les kana comptent chacun pour un mot ; la syntaxe Markdown ne compte pas. |
maxCharacters | Points de code Unicode. |
maxTokens | Jetons estimés (ceil(caractères / 4)). Le maxTokens de premier niveau reste la coupure stricte du fournisseur ; celui-ci est mesuré et réparé. |
maxListItems | Éléments de la plus longue liste (liste Markdown ou tableau JSON). |
maxSections | Titres Markdown, ou propriétés de premier niveau d'un objet JSON. |
onBreach | "repair" (par défaut) ou "report". |
Déclarez au moins une limite. Les champs inconnus sont refusés avec INVALID_REQUEST.
Un dépassement ne fait jamais échouer l'appel. Une réponse plus longue est moins bonne, mais reste une réponse :
- Unaire. Avec
onBreach: "repair", une réponse qui dépasse une limite passe une fois par une condensation. La réécriture n'est retenue que si elle est plus courte, ne s'est pas effondrée et n'invente pas de citations ; elle est gouvernée par la DLP et la modération comme toute autre sortie. Dans ce casrepairedvauttrue. Sinon, ou si aucune réécriture ne tient dans la fenêtre de contexte du modèle, vous recevez le premier jet avecbreached: true. - Flux. Le texte a déjà été livré à l'appelant : un flux est mesuré à la
fin et jamais réparé. Le rapport arrive sur l'événement terminal
completeavecrepair: "skipped". - Ce qui est mesuré. Le rapport compte toujours le texte que vous recevez,
après la DLP. Si la DLP a masqué une partie de la réponse, les mesures
portent sur le texte masqué, pas sur le premier jet du modèle.
repairdécrit toujours le premier jet : un masquage peut donc donnerbreached: trueavecrepair: "not_needed". - Réponses JSON. Les mots sont comptés sur les seules valeurs de type
chaîne. Une réparation doit renvoyer le même JSON dont seule la prose est
raccourcie : les mêmes clés à tous les niveaux, chaque nombre, booléen et
valeur d'un seul mot (un verdict, un identifiant) inchangé, et des éléments
de tableau supprimés uniquement si
maxListItemsétait dépassé. Sinon la réécriture est rejetée et vous recevez le premier jet. Comme une réparation conserve toutes les clés, un objet JSON qui dépassemaxSectionsn'est jamais réparé : le rapport indiquerepair: "skipped"avecrepairSkipReason: "json_structure", et aucun appel de réparation n'a lieu.
Pour compter un texte comme le fait la plateforme, par exemple un texte modifié
ensuite par une personne, utilisez measureLlmOutput(text, constraints) de
@scrydon/sdk/platform.
Le code d'extension transmet le même champ via ctx.ai.llm.complete(...), typé
LLMOutputConstraints depuis @scrydon/sdk-authoring/extensions/platform, et
reçoit le même constraintReport.
Un appel de plateforme en échec lève une PlatformApiError depuis @scrydon/sdk/platform.
Branchez votre logique sur son code et sur retryable, en comparant avec la constante indexée
PLATFORM_ERROR_CODE (PLATFORM_ERROR_CODE.CAPABILITY_NOT_CONFIGURED)
plutôt qu'en retapant la chaîne : une clé mal orthographiée échoue à la vérification de types, une
chaîne mal orthographiée ne correspond jamais. Lorsqu'un même code couvre plusieurs causes,
details.reason nomme la cause avec une valeur de PLATFORM_ERROR_REASONS :
code | details.reason | Signification |
|---|---|---|
INVALID_REQUEST | AUDIO_REFERENCE_NOT_FOUND | Une clé audioRef ne désigne aucun objet dans le stockage de votre organisation. |
INVALID_REQUEST | AUDIO_REFERENCE_OUT_OF_SCOPE | Un audioRef désigne un autre contexte, espace de travail ou environnement. |
INVALID_REQUEST | AUDIO_REFERENCE_TOO_LARGE | L'audio référencé dépasse ce qu'une seule référence peut porter (64 Mio). |
INTERNAL_ERROR | STREAM_ENDED_WITHOUT_TERMINAL | Le flux du fournisseur s'est arrêté sans se terminer ; considérez la sortie comme tronquée. |
Les trois raisons liées à l'audio sont retryable: false. Renvoyer la même référence
relit le même état.
Résultat d'exécution de workflow — la classification du run enfant
Lorsqu'un run de workflow lit des données classifiées, la plateforme suit un
marquage de classification pour ce run. Le résultat d'une exécution de workflow porte le
marquage sur lequel le run enfant s'est terminé : un appelant qui exécute un workflow ne peut donc pas recevoir
ses données sans leur classification. Le résultat de
client.action.workflow.execute(...) (ActionWorkflowExecuteResult) déclare
les deux champs, et le client rejette un marquage mal formé au lieu de le transmettre
tel quel :
| Champ | Forme | Signification |
|---|---|---|
classification | { schemeId: string; rank: number } (optionnel) | La classification la plus élevée que le run enfant a lue, et jamais inférieure au marquage avec lequel il a démarré. Absent uniquement lorsque l'enfant a démarré sans marquage et n'a rien lu de classifié. |
classificationSchemesMixed | boolean (optionnel) | true lorsque le run a lu des données relevant de plus d'un schéma de classification. |
- L'enfant démarre au marquage du run appelant. Il ne peut pas écrire de données en dessous de la classification que son appelant a déjà lue.
- Un enfant en échec renvoie tout de même son marquage, avec ses sorties partielles et son message d'erreur.
- Un enfant qui a planté, a été arrêté ou n'avait pas terminé ne renvoie aucune sortie et un message d'erreur fixe. Son marquage est au moins celui avec lequel il a démarré.
- Si la plateforme ne peut pas lire le marquage enregistré de l'enfant, le résultat
revient avec
success: falseet sans sortie, plutôt que comme des données non classifiées. - Un enfant démarré avec un marquage doit répondre avec un marquage. Si le service de
workflow répond sans marquage, l'appel échoue avec
PROVIDER_CONTRACT_VIOLATION(HTTP 502) et les sorties ne sont pas renvoyées. Cela ne peut se produire que pendant le déploiement d'une mise à niveau de la plateforme. L'enfant s'est tout de même exécuté : ne réessayez donc pas l'appel à l'aveugle.
Délais et annulation
Chaque appel à la plateforme est borné. Sans réglage, un appel reçoit
PLATFORM_DEFAULT_DEADLINE_MS (120 secondes) — la même borne que la plateforme
impose à l'exécution de l'opération : le SDK n'abandonne donc jamais un travail que
la plateforme poursuit, et n'attend jamais un travail qu'elle a déjà interrompu.
Une fois le délai écoulé, l'appel est rejeté avec PlatformApiError de code
DEADLINE_EXCEEDED (retryable: true ; ne relancez une action non idempotente
qu'avec une idempotencyKey).
// Abandonner un appel plus tôt que le délai par défaut :
const answer = await client.ai.llm.complete({ messages }, { timeoutMs: 30_000 })
// Annuler plus tôt. Un signal ne fait que raccourcir un appel ; il ne retire jamais le délai.
const controller = new AbortController()
const pending = client.ai.llm.complete({ messages }, { signal: controller.signal })
controller.abort()La plateforme exécute toujours chaque opération pendant au plus sa propre borne de
120 secondes : un timeoutMs supérieur au délai par défaut ne fait qu'attendre plus
longtemps son propre DEADLINE_EXCEEDED ; il n'accorde pas plus de temps à l'opération.
Un appel unaire doit répondre — statut et corps — dans son délai. Un flux
(client.ai.llm.stream) doit s'ouvrir dans ce délai ; une fois ouvert, il se
poursuit jusqu'à son événement final complete ou error, de sorte qu'une longue
génération n'est pas coupée par le délai. La plateforme termine chaque flux par cet
événement final, et votre signal annule toujours un flux ouvert à tout moment.
Un transport que vous construisez vous-même accepte un délai par défaut pour tous
ses appels : createHttpPlatformTransport({ baseUrl, timeoutMs }).
Découvrir les options d'une capacité
client.extensions.capabilities.list(...) renvoie tous les fournisseurs que
votre organisation a activés pour une capacité, avec leurs modèles
sélectionnables :
const llm = await client.extensions.capabilities.list({ capability: "llm" });
llm.category; // "model" — selection descends to a model
llm.providers; // [{ providedId, extensionId, slug, name, icon?, models: [...] }]Les modèles sont déjà fusionnés : un fournisseur qui déclare un catalogue fixe et un fournisseur qui découvre ses déploiements à l'exécution ont la même forme de réponse : vous n'avez donc jamais à créer de branche selon le type de fournisseur auquel vous vous adressez.
Lorsque Platform n'a pas pu découvrir les modèles d'un fournisseur, celui-ci porte un
discoveryFailure avec un code stable (un PlatformErrorCode) et un message
que vous pouvez afficher. Le code indique si attendre peut aider :
CAPABILITY_NOT_CONFIGURED signifie que la connexion elle-même est incorrecte, par exemple un
point de terminaison dont le certificat TLS ne peut pas être vérifié, et seule une modification de la connexion
résout le problème, tandis que PROVIDER_UNAVAILABLE ou DEADLINE_EXCEEDED signale une indisponibilité qu'il vaut la peine de
réessayer. Ses models se limitent alors au catalogue fixe, qui est vide
pour un fournisseur reposant entièrement sur la découverte. Un tableau models vide sans
discoveryFailure signifie que le fournisseur a été joint et n'expose rien :
for (const provider of llm.providers) {
if (provider.discoveryFailure) {
console.warn(`${provider.name}: ${provider.discoveryFailure.message}`);
}
}Un discoveryFailure peut aussi porter un suggestedFix : un field de la
configuration de la connexion et l'URL https (value) qui corrigerait l'échec,
telle que l'extension l'a proposée. Platform ne l'inclut qu'après avoir vérifié la
valeur par rapport aux champs de point de terminaison de l'extension et à la liste
d'autorisation de sortie de votre organisation. L'appliquer est une mise à jour
ordinaire de la connexion, faite par un administrateur et vérifiée à nouveau par Platform.
Chaque modèle contient son id natif au fournisseur, son slug de modèle et un
selectionId canonique accepté par la sélection et l'exécution Platform. La
présentation du fournisseur est représentée une seule fois par provider.slug
et l'éventuel provider.icon, au lieu d'être copiée dans chaque modèle. Un objet
metadata optionnel conserve les champs propres à la capacité, au catalogue ou
à la découverte. Pour les LLM, il peut aussi contenir contextWindow, pricing
et les capabilities effectives après combinaison des valeurs par défaut du
fournisseur et des surcharges du modèle. Ces champs connus sont typés par le
contrat exporté CapabilityListModelMetadata ; des métadonnées JSON
supplémentaires restent disponibles pour les champs propres à une capacité ou à
une intégration. Chaque clé de métadonnées reste optionnelle.
Utilisez la projection du SDK lorsqu'un sélecteur a besoin d'une liste plate et déterministe :
import { flattenCapabilityModelSelections } from "@scrydon/sdk/platform";
const selections = flattenCapabilityModelSelections(llm);
selections[0]?.selectionId; // persist or submit this valueLa category indique comment sélectionner la capacité :
| Catégorie | Signification | models |
|---|---|---|
model | sélectionner un fournisseur et un modèle (LLM, embeddings, voix, OCR, image, vidéo, modération) | présent |
general | sélectionner un fournisseur ; rien n'est sélectionnable en dessous (recherche web) | absent |
Passez applyAllowlist: false pour voir l'ensemble complet avant l'application
de la liste d'autorisation des modèles de votre organisation — utile pour créer
une interface d'administration de cette liste. Passez refresh: true pour
ignorer le cache de découverte.
Les modèles découverts sont mis en cache par connexion. Juste après une mise à niveau de Scrydon, une liste peut brièvement renvoyer les modèles découverts avant la mise à niveau, pendant que Platform les redécouvre en arrière-plan. Une modification de la connexion elle-même (son point de terminaison ou ses identifiants) n'est jamais servie depuis le cache. Les résultats mis en cache restent soumis à la politique de protection des données en vigueur pour l'appelant. L'annulation d'une requête ou l'expiration de son délai n'empêche pas les autres appelants de découvrir les modèles disponibles via la même connexion.
Cette opération diffère de
client.extensions.capabilities.resolve(...), qui renvoie la cible unique qui
servirait la capacité à cet instant. La résolution d'un modèle conserve le
model natif au fournisseur et inclut son selectionId canonique ; les
capacités sans modèle omettent les deux champs. Utilisez resolve pour savoir
ce qui sera exécuté et list pour proposer un choix. Les consommateurs n'ont
pas à analyser ni à qualifier les identifiants de modèle de ces opérations.
Pour stt, resolve accepte aussi un sttArm facultatif — "batch" ou
"streaming", la branche sur laquelle vous allez lancer l'appel. Passez-le
lorsque vous fixez un provider sans model : la plateforme renvoie alors le
modèle de ce fournisseur qui sert cette branche, ou refuse avec
CAPABILITY_NOT_CONFIGURED, exactement comme le ferait l'appel lui-même. Elle
ne substitue jamais un autre fournisseur. sttArm est refusé pour toute
capacité autre que stt.
const stt = await client.extensions.capabilities.resolve({
capability: "stt",
provider: "azure-foundry",
sttArm: "batch",
});Erreurs
import {
ScrydonAuthError,
ScrydonForbiddenError,
ScrydonRateLimitError,
ScrydonValidationError,
ScrydonServerError,
ScrydonError,
} from '@scrydon/sdk'
try {
await client.workflows.trigger({ workflowId: 'wf_abc' })
} catch (err) {
if (err instanceof ScrydonAuthError) {
// 401 — token expired or invalid; redirect to signIn()
} else if (err instanceof ScrydonForbiddenError) {
// 403 — user lacks permission for this workflow
} else if (err instanceof ScrydonRateLimitError) {
// 429 — err.retryAfter seconds
} else if (err instanceof ScrydonValidationError) {
// 400 — err.details has the Zod issues
} else if (err instanceof ScrydonServerError) {
// 5xx — retry with backoff
} else if (err instanceof ScrydonError) {
// Other 4xx — err.code, err.status
}
}429 parce que la plateforme est à pleine capacité. Chaque appel à l'API de
la plateforme (/api/platform/v1/…), chaque appel d'outil MCP et chaque tour du
routeur LLM puisent dans un même nombre borné d'appels ouverts par pod API
Platform. Lorsque ce budget est plein, l'appel est refusé avant tout travail :
429 avec un en-tête Retry-After et l'erreur RATE_LIMITED,
details.reason: "platform_in_flight_cap". Lorsque le pod est plus qu'à moitié plein et partagé avec d'autres
organisations, une organisation ne peut pas en prendre plus d'un quart : un
locataire très actif est refusé avec
details.reason: "platform_org_in_flight_share" avant d'évincer les autres ;
une organisation seule sur le pod peut tout utiliser sauf quatre emplacements,
gardés libres pour un nouvel arrivant. Les appels encore en cours
d'autorisation puisent dans une réserve distincte de même taille : un nouvel
arrivant entre donc, sauf si un locataire maintient autant d'appels en cours
d'autorisation au même moment. Si le pod se remplit pendant l'autorisation de
votre appel, il est refusé avec platform_in_flight_cap après l'autorisation,
toujours avant toute exécution. Attendez retryAfter secondes puis
réessayez ; rien n'a été exécuté ni facturé. Un client MCP reçoit le même refus
sous la forme d'une erreur JSON-RPC -32603 (réessayable) dont
data.scrydon.code vaut RATE_LIMITED. Des 429 persistants signifient que le
déploiement manque de capacité ; adressez-vous à votre administrateur. Un corps
de requête qui arrive trop lentement (par défaut, moins de 64 Kio/s après les
10 premières secondes ; votre administrateur peut abaisser les deux) est refusé
400 avec details.reason: "body_read_too_slow" : renvoyez-le.
React
@scrydon/sdk/react partage un client et sa session dans toute votre arborescence de composants :
import { ScrydonClient } from '@scrydon/sdk'
import { ScrydonProvider, useScrydon, useScrydonAuth } from '@scrydon/sdk/react'
const client = new ScrydonClient({ baseUrl, agenticUrl, clientId, redirectUri })
function App() {
return (
<ScrydonProvider client={client}>
<Page />
</ScrydonProvider>
)
}
function Page() {
const { user, isAuthenticated, isLoading, signIn, signOut } = useScrydonAuth()
const { client } = useScrydon()
if (isLoading) return null
if (!isAuthenticated) return <button onClick={signIn}>Sign in</button>
return (
<button onClick={() => client.workflows.trigger({ workflowId: 'wf_abc', inputs: { message: 'Hello' } })}>
Run workflow as {user?.email}
</button>
)
}Route de callback TanStack Start / Next.js
// /auth/callback page
import { ScrydonClient } from '@scrydon/sdk'
const client = new ScrydonClient({
baseUrl: import.meta.env.VITE_SCRYDON_BASE_URL,
agenticUrl: import.meta.env.VITE_SCRYDON_AGENTIC_URL,
clientId: import.meta.env.VITE_SCRYDON_CLIENT_ID,
redirectUri: `${window.location.origin}/auth/callback`,
})
export function CallbackPage() {
useEffect(() => {
client.auth
.handleCallback()
.then(() => router.navigate({ to: '/' }))
.catch((err) => console.error('OAuth callback failed', err))
}, [])
return <p>Signing you in…</p>
}Backend / serveur-à-serveur
@scrydon/sdk est conçu autour d'un flux OAuth utilisateur final. Pour les processus backend qui ont besoin d'exécuter des workflows au nom de la plateforme (cron, CI, services internes), appelez l'API directement :
curl -X POST "https://app.example.com/agentic/api/workflows/{workflowId}/execute" \
-H "x-api-key: $SCRYDON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input": "…"}'Les clés API sont émises lors du flux de déploiement (voir Exécution). Elles sont l'outil approprié pour les contextes serveur sans utilisateur final.
Prérequis
- Node.js 18+ (pour les globaux fetch + atob)
- TypeScript 5.0+ pour les types complets
- Un environnement de type navigateur pour le module React
Licence
Apache-2.0