Comment écrire un script Deno pour l'outil run_script
Les bonnes pratiques pour créer un script TS exécuté par l'outil run_script dans la sandbox Deno.
Contenu de la recette
Cette recette est indexée par Moulineuse et peut servir de point de départ pour vos explorations SQL, Typesense ou scripts agents.
Créer un script TypeScript pour l'outil run_script
Les scripts sont utiles assez rarement. Lorsque les recettes montrent que query_sql, search_legal_texts ou query_typesense ne suffisent pas seuls, vous pouvez utiliser l'outil run_script pour exécuter un script TypeScript dans une sandbox Deno sécurisée.
1. L'environnement d'exécution (sandbox Deno)
Votre script tourne dans un environnement strictement isolé :
- AUCUN accès au système de fichiers (
fsest interdit). - AUCUN accès réseau (
fetchest interdit). - Temps limité (le script sera tué s'il met plus de 30 secondes).
La seule manière de communiquer avec le monde extérieur et d'interroger la base PostgreSQL canutes est d'utiliser l'objet injecté tricoteuses que vous devez recevoir en paramètre de votre fonction principale.
2. Le contrat de la fonction principale
Votre code doit obligatoirement inclure une fonction asynchrone nommée agentTask(tricoteuses). C'est le point d'entrée qui sera appelé par le serveur.
async function agentTask(tricoteuses) {
// Votre logique ici...
return { message: "Succès !" } // Le résultat final à renvoyer
}
La base unique canutes expose les schémas assemblee, legifrance et senat.
Les points d'entrée utiles de tricoteuses sont :
tricoteuses.sql(query: string, params?: any[])tricoteuses.doc(params)tricoteuses.fts(query: string, options?)tricoteuses.source(packageName: string, symbol: string)
tricoteuses.doc(...) et tricoteuses.source(...) servent surtout à écrire correctement un script, en retrouvant les types, schémas et fonctions déjà disponibles dans le projet.
2 bis. Convention de sortie : préférer les URL de redirection
Quand une recette remonte une ressource identifiée par un id ou un uid compatible avec Exploratrice, préférez retourner une URL de redirection plutôt que l'identifiant brut seul.
- URL de base à utiliser :
https://www.tricoteuses.fr - Pattern recommandé :
https://www.tricoteuses.fr/redirection/{id} - Gardez l'
idbrut seulement comme champ secondaire si nécessaire pour le débogage ou les jointures
Exemple SQL :
SELECT
uid,
'https://www.tricoteuses.fr/redirection/' || uid AS redirect_url,
nom_complet
FROM assemblee.acteurs
ORDER BY nom_complet
LIMIT 20;
Exception importante : certains identifiants bruts du Sénat sont ambigus lorsqu'ils sont purement numériques. Dans ces cas, n'inventez pas d'URL de redirection par simple concaténation ; retournez soit une URL canonique déjà connue, soit l'identifiant brut avec une note expliquant l'ambiguïté.
3. Importer les types et les fonctions du projet (recommandé)
Pour écrire un script sans erreur, il est fortement recommandé d'utiliser les types TypeScript natifs du projet. Vous pouvez importer les paquets @tricoteuses/*.
Astuce : Utilisez get_typescript_types pour vérifier la définition exacte d'un type, et get_symbol_source quand vous avez besoin de reprendre une fonction TypeScript existante pour l'adapter exactement à votre besoin.
import type { Amendement, Acteur } from "@tricoteuses/assemblee"
import { capitalizeFirstLetter } from "@tricoteuses/assemblee"
// Note : certains utilitaires comme parseReference de la Tisseuse peuvent aussi être importés
4. Exemple complet : Croiser des données SQL et faire de l'algorithmique
Voici un exemple parfait de script qui extrait des assemblee.acteurs, type le retour JSON de Postgres, et structure les données :
import type { Acteur, Organe } from "@tricoteuses/assemblee"
async function agentTask(tricoteuses) {
// 1. On interroge `canutes` avec SQL
// Attention : la requête doit impérativement être un SELECT ou un WITH (lecture seule)
const sqlActeurs = `
SELECT data
FROM assemblee.acteurs
WHERE data->>'nom' = 'Dupont'
`
const rows = await tricoteuses.sql(sqlActeurs)
// 2. On type directement le JSON retourné par PostgreSQL
const acteurs: Acteur[] = rows.map((r) => r.data as Acteur)
const resultatsFinaux = []
// 3. On peut faire une boucle et requêter dynamiquement (Attention à la limite de 50 requêtes max !)
for (const acteur of acteurs) {
// Recherche des mandats pour chaque acteur
for (const mandat of acteur.mandats || []) {
if (mandat.organesRef && mandat.organesRef.length > 0) {
const organeId = mandat.organesRef[0]
// Requête SQL dans la boucle (grâce à notre architecture RPC)
const orgRows = await tricoteuses.sql(
"SELECT data FROM assemblee.organes WHERE id = $1",
[organeId],
)
if (orgRows.length > 0) {
const organe = orgRows[0].data as Organe
resultatsFinaux.push({
acteurNom: acteur.nom,
organeLibelle: organe.libelle,
})
}
}
}
}
// 4. On retourne le tableau structuré
return resultatsFinaux
}
5. Limites et Pièges à éviter
- Boucles de requêtes N+1 massives : Bien que
tricoteuses.sql(...)puisse être appelé dans une bouclefor, le serveur vous bloquera si vous effectuez plus de 50 requêtes SQL au cours du même script. Privilégiez un grosSELECT ... WHERE id IN (...)plutôt que 50 petites requêtes. - Seuls SELECT et WITH sont autorisés : Si vous tentez un
UPDATEou unDROP, la requête sera instantanément rejetée. - Le retour doit être sérialisable en JSON : Ne retournez pas de fonctions, de classes ou de
Set/Mapdans le résultat deagentTask.
À propos de ces recettes
Les recettes Moulineuse documentent des requêtes et des méthodes réutilisables pour analyser les données juridiques et parlementaires avec les outils Tricoteuses. Elles servent aussi directement au serveur MCP Moulineuse pour guider les usages et les extractions possibles.
Voir le serveur MCP Moulineuse