DéveloppeursSDK TypeScript
SDK Hostwolf pour JavaScript & TypeScript
@hostwolf/sdk est le client typé officiel de l’API Hostwolf. Il vous permet de piloter vos serveurs de jeu par le code : alimentation, console, fichiers, sauvegardes, mods, joueurs, planifications et plus encore.
npm install https://hostwolf.net/dl/hostwolf-sdk-0.1.8.tgz
Le SDK est distribué depuis hostwolf.net plutôt que depuis le registre npm. La commande ci-dessus l’ajoute à votre package.json sous la forme "@hostwolf/sdk": "https://hostwolf.net/dl/hostwolf-sdk-0.1.8.tgz", et vous l’importez en tant que @hostwolf/sdk. pnpm, yarn et bun acceptent la même URL. Chaque version a sa propre URL ; voir Versions.
Il fonctionne avec Node 18+, Bun et Deno, sans aucune dépendance à l’exécution. Pour la console en direct avec une version de Node antérieure à 22, lancez aussi npm install ws.
Voir aussi : Référence de l’API REST · Serveur MCP · Journal des modifications · Obtenir une clé API
Démarrage rapide#
Créez une clé API sur panel.hostwolf.net/account/api, puis :
import { Hostwolf } from '@hostwolf/sdk';
const hw = new Hostwolf({ apiKey: process.env.HOSTWOLF_API_KEY! });
const { data: servers } = await hw.servers.list();
for (const s of servers) {
const res = await hw.servers.resources(s.identifier);
console.log(s.name, s.egg_name, res.current_state);
}
const id = servers[0].identifier;
await hw.servers.command(id, 'say Backup in 1 minute');
await hw.backups.create(id, { name: 'before-update' });
console.log(await hw.tail(id, { lines: 20 }));
Chaque méthode serveur prend comme premier argument l’identifier court du serveur (les 8 caractères de son URL dans le panel) ou son UUID. Les objets de réponse utilisent les noms de champs de l’API (snake_case). Leurs types TypeScript sont exportés, par ex. Server, Backup et FileObject.
Options#
new Hostwolf({
apiKey: 'ptlc_...', // required
baseUrl: 'https://panel.hostwolf.net', // default
timeoutMs: 30_000, // per request
maxRetries: 2, // 429s, and 502/503/504 on GET
maxRetryWaitMs: 10_000, // longer Retry-After waits fail fast instead
fetch: customFetch, // optional
WebSocket: customWebSocket, // optional, for the console
});
Serveurs#
REST : Endpoints des serveurs
| Méthode | Ce qu’elle fait |
|---|---|
servers.list({ page, per_page }) | Une page de vos serveurs : { data, pagination } |
servers.listAll() | Itérateur asynchrone sur tous les serveurs |
servers.get(id, ['egg', 'subusers']) | Détails du serveur, avec allocations et variables de démarrage |
servers.resources(id) | État en direct, CPU, mémoire, disque, réseau, temps de fonctionnement |
servers.power(id, signal) / start / stop / restart / kill | Actions d’alimentation |
servers.command(id, command) | Exécuter une commande dans la console |
servers.activity(id) | Journal d’activité (paginé) |
servers.rename(id, name, description?) | Renommer |
servers.reinstall(id) | Relancer le script d’installation |
servers.setDockerImage(id, image) | Change l’image d’exécution (par ex. la version de Java) |
servers.websocket(id) | Jeton de console et URL du socket (la console ci-dessous s’en sert) |
servers.waitForState(id, 'running', { timeoutMs }) | Attend que le serveur atteigne un état, par ex. après start(). Renvoie ses ressources |
await hw.servers.start(id);
await hw.servers.waitForState(id, 'running'); // default timeout 5 minutes
const line = await hw.waitForConsole(id, /There are \d+ of a max/, { send: 'list' });
Console#
const c = await hw.console(id).connect();
c.on('output', (line) => console.log(line));
c.on('stats', (s) => console.log(s.state, s.memory_bytes));
c.requestLogs(); // replay recent output
c.sendCommand('list');
// later
c.close();
// Or just fetch the recent lines (ANSI colours stripped):
const lines = await hw.tail(id, { lines: 50 });
// Or wait for one line: send a command and get its reply.
const reply = await hw.waitForConsole(id, /There are \d+ of a max/, { send: 'list', timeoutMs: 20_000 });
waitForConsole(id, pattern, { send, includeRecent, timeoutMs }) renvoie la première ligne qui correspond à une chaîne ou à une RegExp. includeRecent cherche aussi dans le journal récent, et l’appel échoue avec le code Timeout après timeoutMs (30 secondes par défaut).
La console se connecte directement au nœud qui exécute votre serveur et renouvelle son jeton automatiquement.
Événements :
readyetcloseoutputstatusstatsinstall outputdaemon messageetdaemon errorerror
Fichiers#
REST : Endpoints des fichiers
await hw.files.list(id, '/plugins');
const props = await hw.files.read(id, 'server.properties');
await hw.files.write(id, 'server.properties', props.replace('pvp=true', 'pvp=false'));
await hw.files.rename(id, '/', [{ from: 'world', to: 'world-old' }]);
await hw.files.delete(id, '/logs', ['old.log']);
await hw.files.createFolder(id, '/', 'maps');
await hw.files.compress(id, '/', ['world']); // returns the archive
await hw.files.decompress(id, '/', 'world.tar.gz');
await hw.files.pull(id, 'https://example.com/map.zip', { directory: '/maps' });
await hw.files.exists(id, 'plugins/LuckPerms.jar'); // true or false
// Binary files: upload and download go straight to the server's node.
await hw.files.upload(id, '/plugins', jarBytes, 'MyPlugin.jar'); // Blob, ArrayBuffer, Uint8Array or string
const buf = await hw.files.download(id, 'world.tar.gz'); // ArrayBuffer, in memory
// Or work with the signed URLs yourself, e.g. to stream a big file:
const url = await hw.files.downloadUrl(id, 'world.tar.gz');
const uploadUrl = await hw.files.uploadUrl(id); // POST multipart "files" to `${uploadUrl}&directory=/maps`
Lire un fichier inexistant lève une HostwolfError avec le statut 404 et le code NotFound. Également disponibles : files.copy(id, path) et files.chmod(id, root, [{ file, mode }]).
Sauvegardes#
REST : Endpoints des sauvegardes
const { data } = await hw.backups.list(id);
const b = await hw.backups.create(id, { name: 'nightly', is_locked: false });
await hw.backups.waitUntilDone(id, b.uuid); // polls until finished; rejects with code BackupFailed if it failed
await hw.backups.get(id, b.uuid);
await hw.backups.restore(id, b.uuid, { truncate: false });
await hw.backups.toggleLock(id, b.uuid);
await hw.backups.downloadUrl(id, b.uuid);
await hw.backups.delete(id, b.uuid);
await hw.backups.smartState(id);
await hw.backups.setSmart(id, true); // automatic backups before risky changes
Vous pouvez créer 2 sauvegardes par tranche de 10 minutes. Au-delà, create() échoue tout de suite avec error.retryAfter renseigné.
Bases de données#
REST : Endpoints des bases de données · Guide : Utiliser une base de données MySQL avec votre serveur
Chaque serveur dispose de 5 bases de données compatibles MySQL au maximum sur sa propre machine. La première démarre aussi le service de bases de données, ce qui prend environ 40 secondes : l’API répond 202 et create() renvoie un résultat en attente, sauf si vous passez wait.
import { isDatabasePending } from '@hostwolf/sdk';
// Simplest: wait until the database exists (up to 2 minutes), then use it
const db = await hw.databases.create(id, 'luckperms', { wait: true });
console.log(db.host.address, db.name, db.username, db.relationships?.password?.password);
// Or handle the pending answer yourself
const r = await hw.databases.create(id, 'coreprotect');
if (isDatabasePending(r)) {
const ready = await hw.databases.waitUntilReady(id, 'coreprotect', { timeoutMs: 120_000 });
}
await hw.databases.service(id); // { status, local_address, public_address, other_servers }
await hw.databases.setRemoteAccess(id, db.id, { remoteIps: ['203.0.113.7'], anywhere: false });
await hw.databases.rotatePassword(id, db.id);
const dump = await hw.databases.export(id, db.id); // .sql.gz bytes
await hw.databases.import(id, db.id, dump, { filename: 'luckperms.sql.gz' });
await hw.databases.delete(id, db.id);
Les serveurs de jeu se connectent à db.host (172.18.0.1:3306). db.name porte un préfixe, par exemple s12_luckperms ; waitUntilReady() prend le nom que vous avez demandé. Les autres méthodes prennent db.id. waitUntilReady() échoue avec le code DatabaseSetupFailed si le service n’a pas pu démarrer, ou avec Timeout. Vous pouvez créer 2 bases de données par minute, et en exporter ou importer 6 par minute.
Jeu, joueurs et paramètres#
await hw.game.versions(id); // current + available versions
await hw.game.setVersion(id, '1.21.4'); // switches and reinstalls; { resetWorld, force } for downgrades
await hw.game.settings(id); // friendly settings, grouped by file ({ supported: false } on Java Minecraft)
await hw.game.updateSettings(id, { difficulty: 'hard' }, true);
await hw.game.diagnostics(id); // "Fix my server" checks
await hw.players.info(id); // online count (+ names on Minecraft)
await hw.players.list(id); // live list via RCON/query
await hw.players.kick(id, 'Steve');
await hw.players.ban(id, { steamid: '7656...' });
await hw.players.unban(id, 'Steve');
await hw.players.broadcast(id, 'Restarting in 5 minutes');
Mods, plugins et modpacks#
REST : Endpoints mods et modpacks
const profile = await hw.addons.profile(id); // sources, install dir, frameworks
const { items } = await hw.addons.search(id, 'modrinth', 'worldedit');
await hw.addons.install(id, 'modrinth', items[0].id); // newest compatible version
const installed = await hw.addons.installed(id, { checkUpdates: true });
await hw.addons.uninstall(id, installed.addons[0].id);
const packs = await hw.modpacks.search(id, 'cobblemon');
await hw.modpacks.install(id, packs.items[0].id, { freshWorld: false });
Autres appels d’addons :
addons.versions(id, source, project): les versions, marquées compatibles ou non.addons.installFeatured(id, project): addons mis en avant en un clic, depuisprofile.featured.addons.upload(id, blob, name): quandprofile.uploadest défini. Sinon, placez les fichiers dansprofile.directoryavecfiles.upload().addons.removeManual(id, file): retire un fichier que vous avez ajouté vous-même.addons.setFramework(id, framework): par ex. Oxide ou Carbon sur Rust.addons.workshop/setWorkshop/workshopPreview(jeux avec Steam Workshop).addons.cs2Framework(id, { repair }): Metamod + CounterStrikeSharp sur CS2.modpacks.current/versions/remove.
Plus#
| Espace de noms | Méthodes |
|---|---|
startup | get(id), setVariable(id, key, value) |
schedules | list, get, create, update, execute, delete, createTask, updateTask, deleteTask |
databases | list(id, { includePassword }), create, waitUntilReady, service, setRemoteAccess, rotatePassword, export, import, delete. Voir Bases de données |
network | allocations, createAllocation, setNotes, setPrimary, deleteAllocation |
users | list, get, create(id, publicId, permissions), update, delete, permissions(). On ajoute un compte par son identifiant public (HW-XXXXXX), pas par e-mail |
notifications | Alertes Discord par serveur : get, update, test |
domain | Domaine personnalisé : get, check, set, remove |
mcNetwork | Réseaux Velocity/BungeeCord : get, link, unlink, installFabricProxy |
account | get, updateEmail, updatePassword, updateProfile, updateLanguage(locale, currency?), uploadAvatar, deleteAvatar, activity, getNotifications, updateNotifications, testNotification, getCurseforgeKey, setCurseforgeKey, getFactorioCredentials, setFactorioCredentials |
account.apiKeys | list, create(description, allowedIps?) (renvoie la clé complète dans token, une seule fois), delete(identifier) |
account.sshKeys | list, create(name, publicKey), delete(fingerprint) |
store | Lecture seule : catalog(), orders(), billing() |
Commander, résilier et payer des serveurs ne se fait que dans le panel : une clé API ne peut donc jamais dépenser d’argent.
Pagination#
servers.list, backups.list et les méthodes activity renvoient { data, pagination }. Pour parcourir tous les serveurs :
import { collect } from '@hostwolf/sdk';
const all = await collect(hw.servers.listAll());
Erreurs#
Les appels qui échouent lèvent une HostwolfError.
import { HostwolfError } from '@hostwolf/sdk';
try {
await hw.game.setVersion(id, '1.16.5');
} catch (e) {
if (e instanceof HostwolfError && e.code === 'downgrade_requires_reset') {
// older versions can't load a newer world: retry with { resetWorld: true } or { force: true }
}
}
Elle possède ces propriétés :
status: le statut HTTP, ou 0 pour les erreurs réseau et les délais dépassés.code: le code d’erreur. En plus des codes de l’API, le SDK utiliseNetworkError(pas de réponse ou délai dépassé),Timeout(un assistantwaitFor…ouwaitUntil…a abandonné),BackupFailed,DatabaseSetupFailed,DatabaseNotCreatedetNotFound(un fichier manquant).retryAfter: sur un 429, le nombre de secondes avant que l’action soit à nouveau autorisée. Les attentes courtes sont relancées pour vous ; les plus longues (comme la limite de 2 sauvegardes par 10 minutes) échouent tout de suite avec cette valeur.message: une description lisible.errors: la liste brute des erreurs.body: le contenu de la réponse.- Indicateurs pratiques :
isNotFound,isRateLimitedetisValidation.
Les plus courants :
401: la clé est erronée, incomplète ou révoquée.403avecThis IP address (…) does not have permission: la clé a une liste d’adresses IP autorisées qui n’inclut pas la vôtre.409 downgrade_requires_resetet422 unknown_version: renvoyés pargame.setVersion().429: une limite de requêtes ; voirretryAfter.- Une écriture qui a dépassé le délai (statut 0) peut tout de même aboutir sur le panneau : vérifiez avant de la relancer.
Requêtes brutes#
Si un endpoint n’a pas encore de méthode, appelez-le directement :
await hw.http.get(`/servers/${id}/players`);
await hw.http.post(`/servers/${id}/command`, { command: 'list' });
Les chemins sont relatifs à /api/client, et les réponses sont déballées comme pour les méthodes fournies. Tous les endpoints sont dans la référence de l’API.
Versions#
| Fichier | Utilisation |
|---|---|
https://hostwolf.net/dl/hostwolf-sdk- | Une version figée. Utilisez-la dans package.json. |
https://hostwolf.net/dl/hostwolf-sdk.tgz | Toujours la version la plus récente. Les gestionnaires de paquets mettent en cache par URL, préférez donc les URL versionnées. |
https://hostwolf.net/dl/index.json | Versions actuelles, avec les empreintes SHA-256 et d’intégrité npm. |
Pour mettre à jour, installez l’URL versionnée plus récente.
Licence#
MIT