aipoch/remote-compute-ssh

Évaluez et utilisez le calcul distant SSH avant de choisir où exécuter des travaux GPU, haute mémoire, parallèles, par lots, d'inférence de modèles, de bioinformatique ou autres travaux scientifiques de longue durée ; prend en charge les commandes distantes courtes et les travaux asynchrones avec récolte et analyse automatiques.

Qu'est-ce que remote-compute-ssh ?

Ce skill fournit une interface complète pour le calcul distant via SSH, incluant l'exécution directe de commandes et la soumission de travaux Slurm. Il permet de lister les hôtes, créer des handles, exécuter des commandes courtes (`callCommand`), lire/écrire des documents de connaissance sur l'hôte, et gérer le cycle de vie complet des travaux asynchrones : soumission → sauvegarde de `job_id` → lecture d'instantanés non bloquants → récolte → analyse → publication des artefacts. Le skill est conçu pour les plateformes d'agents IA comme Claude Code, Cursor ou Codex, et fonctionne exclusivement sur le noyau REPL de contrôle (JavaScript), pas sur les noyaux Python ou R. Il offre des fonctionnalités de découverte d'hôtes, de soumission de travaux avec options d'environnement, de gestion des sorties et de respect des limites de sécurité pour la récolte de fichiers.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/aipoch/open-science/tree/HEAD/resources/skills/remote-compute-ssh

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Ce skill couvre le calcul distant via SSH, y compris l'exécution directe et la soumission Slurm : liste des hôtes, création de handles, exécution de commandes distantes courtes (callCommand), lecture/écriture de documents de connaissance sur l'hôte, et le cycle de vie complet des travaux asynchrones — soumission → sauvegarde de job_id → lecture d'instantanés non bloquants par cet ID → récolte → tour d'analyse → publication d'artefacts.

Où s'exécute host.compute : host.compute vit UNIQUEMENT sur le noyau REPL de contrôle — exécutez chaque exemple ci-dessous avec l'outil repl_execute (JavaScript), le même noyau qui héberge host.mcp. Les noyaux de données python/r n'ont PAS host.compute (SSH et approbations restent en dehors de l'espace de travail sandbox) ; l'appeler depuis une cellule python/r échouera avec host.compute is undefined.

Choisir un emplacement d'exécution

Seuls les hôtes de calcul activés pour cette session sont visibles ou appelables. Découvrez-les dans un catalogue unique ; chaque entrée a un rôle selected ou available. Un pool sélectionné non vide est une instruction d'exécution : exécutez le travail basé sur des outils sur un ou plusieurs hôtes sélectionnés selon les besoins du travail. Le pool n'a pas de priorité et n'implique pas de planification multi-hôtes automatique. Si aucun hôte n'est sélectionné, choisissez parmi les entrées disponibles. Lisez details() uniquement pour les candidats nécessitant une évaluation plus approfondie.

Ne devinez jamais et ne réutilisez jamais un identifiant de fournisseur absent du catalogue. Un utilisateur nommant un hôte désactivé ne le rend pas appelable ; expliquez qu'il doit d'abord être activé pour cette session. Si aucun hôte éligible n'est utilisable, expliquez le blocage et demandez à l'utilisateur comment procéder.

const hosts = await host.compute.listHosts()
const selectedHosts = hosts.filter((host) => host.role === 'selected')
const candidates = selectedHosts.length > 0 ? selectedHosts : hosts

Chaque élément de liste est un résumé compact avec provider_id, display_name, shape, execution_mode, status et role (last_probe_ok, probe_failed, ou not_probed). last_probe_ok signifie que la dernière sonde persistée a réussi ; elle n'affirme pas une connectivité en direct. Les documents de connaissance et les instantanés de sonde de ressources sont délibérément exclus des résultats de découverte.

Référence API

// Lister les hôtes activés de cette session en tant que catalogue compact avec rôles
const hosts = await host.compute.listHosts()

// Les noms de découverte de compatibilité restent disponibles ; les deux cachent toujours les hôtes désactivés.
const visibleHosts = await host.compute.listRegistered()
const selectedHosts = await host.compute.listPreferred()

// Créer un handle vers un hôte spécifique (pas d'appel réseau)
const c = host.compute.create('ssh:<alias>')

// Exécuter une commande distante courte (lève une exception sur approval_denied / host_unreachable / timeout)
const result = await c.callCommand('<commande shell>', '<intention en une ligne pour la carte d\'approbation>', {
  loginShell: true, // défaut : true — exécute les profils de connexion, puis ~/.bashrc lisible, avant cette commande
  timeoutSeconds: 60 // optionnel — l'hôte applique son propre défaut (60s) lorsqu'il est omis
})
// result → { exit_code, stdout, stderr, truncated }

// Lire les instructions d'opération persistées et l'instantané indépendant de la sonde de ressources.
// doc est toujours le texte exact sauvegardé (y compris '' avant que les instructions ne soient sauvegardées).
// probe est explicitement null lorsque cet hôte n'a jamais été sondé.
const info = await host.compute.details('ssh:<alias>', { mode: 'read' })

// Ajouter une note au document de connaissance persistant de l'hôte (écriture par agent ; limite de 32 Ko appliquée).
// Append ne modifie que doc ; il ne copie ni ne modifie jamais les observations de sonde.
await host.compute.details('ssh:<alias>', {
  mode: 'append',
  text: '\n## Note\nmot clé appris X le <date>'
})

// Alternativement, remplacer l'intégralité du document de connaissance de l'hôte. Relire avant de remplacer,
// surtout si vous avez ajouté ci-dessus : oldText doit correspondre exactement au document persisté.
const latest = await host.compute.details('ssh:<alias>', { mode: 'read' })
await host.compute.details('ssh:<alias>', {
  mode: 'replace',
  text: '<nouveau document complet>',
  oldText: latest.doc
})

Traitez doc comme instructions d'opération et connaissance durable de l'hôte. Traitez probe comme une observation datée : les valeurs des ressources peuvent changer, et la détection d'un planificateur n'autorise pas à soumettre un travail ni à sélectionner un compte, une partition ou une file d'attente. En cas d'incompatibilité de document ou d'erreur details_conflict, relisez et fusionnez votre brouillon avec le document le plus récent avant de réessayer. Un rafraîchissement de sonde uniquement sur les ressources ne change pas doc et ne provoque pas de conflit de remplacement. Après avoir écrit, relisez pour vérifier le contenu sauvegardé. Un résultat d'ajout ou de remplacement réussi est uniquement { ok: true }. Le résultat d'écriture ne contient pas doc ou probe ; relisez toujours pour vérifier le doc exact persisté plutôt que de lire les champs du résultat d'écriture.

Avec loginShell: true, les profils de connexion Bash distants s'exécutent d'abord, puis Open-Science tente de trouver la source de ~/.bashrc lorsqu'elle est lisible. Un .bashrc peut délibérément retourner tôt pour les shells non interactifs, donc les variables déclarées après une telle garde ne sont pas disponibles. Un .bashrc manquant est une opération nulle. Définissez loginShell: false pour exécuter la commande sans aucune étape d'initialisation. Les échecs d'initialisation sont signalés via le comportement normal de résultat/erreur de la commande.

Référence API (travaux asynchrones)

Utilisez submitJob pour les calculs de longue durée (minutes à heures). Il retourne immédiatement avec un job_id ; le travail s'exécute sur l'hôte distant en arrière-plan. Lorsque le travail se termine, l'application récolte automatiquement les sorties et initie un nouveau tour d'analyse si vous n'avez pas déjà lu le résultat final. Sauvegardez le job_id exact du résultat de soumission dans votre contexte de travail ; il n'y a intentionnellement aucune analyse historique de travaux pour le redécouvrir. Les lectures de statut et de résultat utilisent uniquement cet ID sauvegardé et retournent des instantanés locaux non bloquants.

Pour une entrée locale, src est relatif à l'espace de travail de la session de l'agent — le même espace de travail utilisé par les outils et outils d'écriture de fichiers. Écrivez un script ou une petite entrée générée là, puis passez son chemin relatif. Open Science prend des instantanés des entrées acceptées avant l'approbation et l'envoi. Ne passez pas de chemins absolus locaux arbitraires ou ne copiez pas de fichiers dans des répertoires notebooks/... gérés par l'application. Un src absolu est valide uniquement lorsqu'il s'agit du chemin exact retourné par host.artifactPath(versionId) ou d'un chemin d'entrée de session enregistré déjà fourni dans le contexte du notebook.

// Réutiliser les `candidates` sélectionnées ci-dessus dans le catalogue de session.

// Soumettre un travail non bloquant — retourne immédiatement après l'approbation de l'utilisateur.
// Le mode d'exécution configuré de l'hôte de calcul sélectionne SSH direct ou Slurm.
const c = host.compute.create('ssh:<alias>')
const job = await c.submitJob(
  '<intention en une ligne pour la carte d\'approbation>', // affiché dans la carte d'approbation
  '<commande shell>', // commande à exécuter à distance
  {
    environment: 'protein-gpu', // nom logique optionnel ; voir Activation d'environnement ci-dessous
    timeoutSeconds: 3600, // optionnel ; défaut 24 h, max 7 jours
    inputs: [
      { src: 'in.dat', dstFilename: 'in.dat' }, // mettre en scène un fichier de l'espace de travail de la session de l'agent
      { remotePath: 'ssh:<alias>/<chemin_abs>' } // lier un fichier distant (pas de transfert)
    ],
    outputs: [
      '*.result', // en vedette (visibilité par défaut)
      { glob: '*.json', visibility: 'featured' }, // explicitement en vedette
      { glob: '*.log', visibility: 'hidden' }, // masqué (diagnostic, pas affiché dans la carte)
      { glob: 'checkpoints/**', residency: 'remote' } // laisser à distance — enregistré dans left_on_remote
    ],
    harvest: {
      exclude: ['work/**'], // ne jamais récolter ces chemins
      maxFileMb: 100, // maximum absolu par fichier (100 Mio)
      maxTotalMb: 500 // maximum absolu par travail, y compris stdout/stderr (500 Mio)
    }
  }
)
// job → { job_id, provider_id, status: 'submitted' | 'queued', remote_workdir }
const savedJobId = job.job_id // conserver cet identifiant exact pour l'étape dépendante ultérieure
return { ...job, job_id: savedJobId }

Lire un instantané de travail sauvegardé

Utilisez l'ID sauvegardé lorsque l'état ou le résultat du travail est pertinent. .status() et .result() sont des lectures locales non bloquantes dans tous les états ; ni l'un ni l'autre n'attend la fin, ne déclenche SSH, ni ne démarre une autre récolte. .result() inclut également les listes de fichiers récoltés. Les deux appels signalent follow_up_delivery. Une lecture finale .result() retourne suppressed lorsqu'elle empêche la repli, ou committed lorsque ce repli a déjà franchi sa barrière d'envoi. Un instantané .status() reste pending car il omet les listes de fichiers récoltés. Utilisez l'ID exact de la soumission plutôt que de rechercher d'anciens travaux.

const snapshot = await c.attachJob(savedJobId).result()
if (!snapshot.result_final) {
  return {
    job_id: savedJobId,
    status: snapshot.status,
    result_final: false,
    follow_up_delivery: snapshot.follow_up_delivery
  }
}
return snapshot

Ne considérez que result_final: true comme le résultat final. Cela signifie que la réponse automatique est prête : success, failed et timeout peuvent encore attendre la récolte locale, tandis que cancelled et error sont définitifs sans attendre la collecte partielle des sorties. output_files contient uniquement les fichiers effectivement disponibles localement ; une liste vide ne prouve pas l'absence de sorties distantes. L'application gère le polling du fournisseur et la récolte en arrière-plan. Un résultat final non lu est délivré lors d'un tour d'agent ultérieur. Un instantané final .result() signale follow_up_delivery: 'suppressed' lorsqu'il supprime ce repli, ou committed si la livraison automatique a déjà gagné la course et reste faisant autorité. .status() ne consomme jamais le résultat complet.

SSH direct ou Slurm

Le mode d'exécution configuré de l'hôte de calcul sélectionne comment chaque travail est lancé. direct_ssh exécute la commande comme un processus détaché sur la cible SSH. slurm la soumet avec sbatch ; placez les #SBATCH directives requises du cluster en haut de command. Open-Science possède la soumission, le polling du statut du planificateur, l'annulation et la récolte. N'appelez pas sbatch, squeue ou scancel autour de submitJob vous-même.

Pour une erreur de statut Slurm ou une annulation échouée, lisez Dépannage Slurm. Dans ce chemin exceptionnel, compute.callCommand peut inspecter le même travail et ses sorties et effectuer une récupération dans le cadre de autorisation de tâche existante de l'utilisateur. Gardez la soumission ordinaire sur l'API Compute.

Lisez listHosts() pour le mode configuré et details() pour les directives spécifiques au fournisseur ; n'essayez pas de remplacer le mode par travail ou de l'inférer uniquement à partir de la charge de travail. Si Slurm est indisponible ou rejette le script, signalez l'erreur retournée et l'étape concrète suivante (par exemple, ajoutez une directive de compte ou de partition). Ne réexécutez pas silencieusement la charge de travail directement sur un nœud de connexion.

Open-Science accepte les directives de travail uniques ordinaires telles que partition, compte, CPU, mémoire et GPU. Définissez timeoutSeconds pour la durée d'exécution de la charge de travail. Vous pouvez définir la limite d'allocation du planificateur avec une directive #SBATCH --time=valeur ; lorsqu'elle est absente, Open-Science dérive une limite d'allocation par défaut à partir de timeoutSeconds. Open-Science possède le nom du travail, le répertoire de travail, stdout et stderr directives. Évitez les tableaux de travaux car un travail Open-Science suit un travail planificateur et une récolte de sortie. Soumettez les travaux indépendants comme des travaux séparés et utilisez la limite de concurrence de session lorsque c'est nécessaire.

Pour Slurm, demandez des ressources avec une directive #SBATCH --option=valeur par ligne (ou un indicateur sans valeur tel que #SBATCH --exclusive). L'option resources héritée est des métadonnées descriptives ; elle n'alloue pas de CPU, mémoire ou GPU. timeoutSeconds limite la durée d'exécution de la charge de travail, pas l'attente dans la file ; #SBATCH --time définit la limite d'allocation du planificateur. Aucun des deux n'est une promesse de temps de démarrage dans la file.

Les instantanés non bloquants de status() et result() du travail incluent scheduler_job_id lorsqu'il est connu, error: { code, message } en cas d'erreur au niveau du travail, et last_poll_error lorsque l'observation ou la récupération de soumission nécessite de l'attention. Une raison en attente ou une ligne de comptabilité retardée ne signifie pas que la charge de travail a échoué. Si une soumission n'est pas confirmée, utilisez l'identité de travail signalée et les diagnostics du fournisseur avant de décider si vous devez soumettre à nouveau ; Open-Science ne soumet pas automatiquement un doublon.

Activation d'environnement

La valeur optionnelle environment est un nom logique, pas une commande shell. Open-Science sourcr ~/.open-science/environments/<nom>.sh avant la charge de travail pour les travaux directs et Slurm. Les noms sont de 1 à 64 lettres, chiffres, points, underscores ou traits d'union et doivent commencer par une lettre ou un chiffre. Le fichier et chaque chemin de logiciel/cache qu'il référence doivent être visibles sur le nœud d'exécution.

Si une soumission signale que ce fichier d'activation est manquant, chargez le Skill de Configuration d'Environnement Compute pour préparer des instructions exactes de configuration, réparation et suppression pour l'utilisateur ou l'administrateur de l'hôte à exécuter en dehors d'Open-Science. Validez l'activation gérée par l'utilisateur après qu'il applique le plan, puis réessayez. Ne devinez pas un nom conda, n'ajoutez pas d'installation en ligne au travail scientifique, et ne cachez pas l'activation dans .bashrc. Omettez environment lorsque la commande utilise délibérément l'environnement par défaut de l'hôte.

Limites de sécurité de la récolte

  • Les fichiers de sortie déclarés sont sélectionnés avant stdout et stderr ; les journaux utilisent le budget restant par travail.
  • L'application rejette les limites fournies par le modèle supérieures à 100 Mio par fichier ou 500 Mio par travail.
  • La récolte préserve également un espace disque local libre fixe de 2 Gio. Les fichiers qui ne tiennent pas restent distants.

Limites de comportement

  • Pendant que le travail s'exécute : la conversation est ouverte. L'utilisateur peut envoyer des messages ; vous pouvez gérer d'autres tâches. Chaque requête de statut/résultat retourne immédiatement avec l'instantané local actuel.
  • Lorsque le travail se termine : si vous n'avez pas activement lu son résultat final, l'application initie un nouveau tour d'analyse automatiquement. Vous ne déclenchez pas ce repli.

Vérifier le statut du travail (lecture non bloquante, à des fins informatives)

// Lecture DB non bloquante — pas de SSH. Utilisez-la si vous avez besoin d'un instantané de statut en cours de conversation.
const handle = c.attachJob(job.job_id)
const s = await handle.status()
// s → {
//   job_id, scheduler_job_id?, status, result_final, cancellation?, exit_code,
//   error?, last

…

[tronqué pour la traduction]

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills associés