KehittäjätTypeScript-SDK

Hostwolf-SDK JavaScriptille & TypeScriptille

@hostwolf/sdk on Hostwolf-rajapinnan virallinen tyypitetty asiakaskirjasto. Sillä ohjaat pelipalvelimiasi koodista: virta, konsoli, tiedostot, varmuuskopiot, modit, pelaajat, ajastukset ja paljon muuta.

npm install https://hostwolf.net/dl/hostwolf-sdk-0.1.8.tgz

SDK jaetaan osoitteesta hostwolf.net npm-rekisterin sijaan. Yllä oleva komento lisää sen tiedostoosi package.json muodossa "@hostwolf/sdk": "https://hostwolf.net/dl/hostwolf-sdk-0.1.8.tgz", ja tuot sen nimellä @hostwolf/sdk. pnpm, yarn ja bun hyväksyvät saman URL-osoitteen. Jokaisella versiolla on oma URL; katso Versiot.

Se toimii Node 18+:lla, Bunilla ja Denolla, eikä sillä ole ajonaikaisia riippuvuuksia. Reaaliaikaista konsolia varten aja Node-versioissa ennen 22:ta myös npm install ws.

Pikaopas#

Luo API-avain osoitteessa panel.hostwolf.net/account/api ja sitten:

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 }));

Jokainen palvelinmetodi ottaa ensimmäiseksi argumentiksi palvelimen lyhyen identifier-tunnuksen (sen paneelin URL-osoitteen 8 merkkiä) tai sen UUID:n. Vastausoliot käyttävät rajapinnan omia kenttien nimiä (snake_case). Niiden TypeScript-tyypit viedään, esim. Server, Backup ja FileObject.

Asetukset#

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
});

Palvelimet#

MetodiMitä se tekee
servers.list({ page, per_page })Sivu palvelimistasi: { data, pagination }
servers.listAll()Asynkroninen iteraattori kaikkien palvelinten yli
servers.get(id, ['egg', 'subusers'])Palvelimen tiedot varauksineen ja käynnistysmuuttujineen
servers.resources(id)Reaaliaikainen tila, suoritin, muisti, levy, verkko, käyntiaika
servers.power(id, signal) / start / stop / restart / killVirtatoiminnot
servers.command(id, command)Aja konsolikomento
servers.activity(id)Tapahtumaloki (sivutettu)
servers.rename(id, name, description?)Nimeä uudelleen
servers.reinstall(id)Aja asennusskripti uudelleen
servers.setDockerImage(id, image)Vaihda ajonaikainen image (esim. Java-versio)
servers.websocket(id)Konsolin tunniste ja socketin URL (alla oleva konsoli käyttää niitä)
servers.waitForState(id, 'running', { timeoutMs })Odottaa, kunnes palvelin on tietyssä tilassa, esim. start()-kutsun jälkeen. Palauttaa sen resurssit
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' });

Konsoli#

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 }) palauttaa ensimmäisen rivin, joka vastaa merkkijonoa tai RegExpiä. includeRecent hakee myös tuoreesta lokista, ja kutsu epäonnistuu koodilla Timeout, kun timeoutMs (oletus 30 sekuntia) kuluu.

Konsoli yhdistää suoraan nodeen, jossa palvelimesi toimii, ja uusii tokeninsa automaattisesti.

Tapahtumat:

  • ready ja close
  • output
  • status
  • stats
  • install output
  • daemon message ja daemon error
  • error

Tiedostot#

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`

Olemattoman tiedoston lukeminen heittää virheen HostwolfError tilalla 404 ja koodilla NotFound. Lisäksi saatavilla: files.copy(id, path) ja files.chmod(id, root, [{ file, mode }]).

Varmuuskopiot#

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

Voit luoda 2 varmuuskopiota 10 minuutissa. Sen yli create() epäonnistuu heti, ja error.retryAfter on asetettu.

Tietokannat#

Jokaisella palvelimella on omalla koneellaan enintään 5 MySQL-yhteensopivaa tietokantaa. Ensimmäinen käynnistää myös tietokantapalvelun, mikä kestää noin 40 sekuntia: API vastaa 202 ja create() palauttaa odottavan tuloksen, ellet anna asetusta 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);

Pelipalvelimet yhdistävät osoitteeseen db.host (172.18.0.1:3306). db.name sisältää etuliitteen, esimerkiksi s12_luckperms; waitUntilReady() ottaa pyytämäsi nimen. Muut metodit ottavat db.id:n. waitUntilReady() epäonnistuu koodilla DatabaseSetupFailed, jos palvelu ei käynnistynyt, tai koodilla Timeout. Voit luoda 2 tietokantaa minuutissa ja viedä tai tuoda 6 minuutissa.

Peli, pelaajat ja asetukset#

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');

Modit, lisäosat ja modipaketit#

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 });

Muut lisäosakutsut:

  • addons.versions(id, source, project): versiot merkittyinä yhteensopiviksi tai ei.
  • addons.installFeatured(id, project): suositellut lisäosat yhdellä napsautuksella, listasta profile.featured.
  • addons.upload(id, blob, name): kun profile.upload on asetettu. Muuten vie tiedostot kansioon profile.directory kutsulla files.upload().
  • addons.removeManual(id, file): poistaa itse lisäämäsi tiedoston.
  • addons.setFramework(id, framework): esim. Oxide tai Carbon Rustissa.
  • addons.workshop / setWorkshop / workshopPreview (Steam Workshopia käyttävät pelit).
  • addons.cs2Framework(id, { repair }): Metamod + CounterStrikeSharp CS2:ssa.
  • modpacks.current / versions / remove.

Lisää#

NimiavaruusMetodit
startupget(id), setVariable(id, key, value)
scheduleslist, get, create, update, execute, delete, createTask, updateTask, deleteTask
databaseslist(id, { includePassword }), create, waitUntilReady, service, setRemoteAccess, rotatePassword, export, import, delete. Katso Tietokannat
networkallocations, createAllocation, setNotes, setPrimary, deleteAllocation
userslist, get, create(id, publicId, permissions), update, delete, permissions(). Tilit lisätään julkisella tunnuksella (HW-XXXXXX), ei sähköpostilla
notificationsPalvelinkohtaiset Discord-ilmoitukset: get, update, test
domainOma verkkotunnus: get, check, set, remove
mcNetworkVelocity/BungeeCord-verkot: get, link, unlink, installFabricProxy
accountget, updateEmail, updatePassword, updateProfile, updateLanguage(locale, currency?), uploadAvatar, deleteAvatar, activity, getNotifications, updateNotifications, testNotification, getCurseforgeKey, setCurseforgeKey, getFactorioCredentials, setFactorioCredentials
account.apiKeyslist, create(description, allowedIps?) (palauttaa koko avaimen kentässä token, vain kerran), delete(identifier)
account.sshKeyslist, create(name, publicKey), delete(fingerprint)
storeVain luku: catalog(), orders(), billing()

Palvelinten tilaaminen, peruminen ja maksaminen onnistuu vain paneelissa, joten API-avain ei voi koskaan käyttää rahaa.

Sivutus#

servers.list, backups.list ja activity-metodit palauttavat { data, pagination }. Näin käyt läpi kaikki palvelimet:

import { collect } from '@hostwolf/sdk';
const all = await collect(hw.servers.listAll());

Virheet#

Epäonnistuneet kutsut heittävät HostwolfError-virheen.

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 }
    }
}

Sillä on nämä ominaisuudet:

  • status: HTTP-tila, tai 0 verkkovirheissä ja aikakatkaisuissa.
  • code: virhekoodi. API:n koodien lisäksi SDK käyttää koodeja NetworkError (ei vastausta tai aikakatkaisu), Timeout (waitFor…- tai waitUntil…-apufunktio luovutti), BackupFailed, DatabaseSetupFailed, DatabaseNotCreated ja NotFound (puuttuva tiedosto).
  • retryAfter: 429-virheessä sekunnit siihen, kun toiminto on taas sallittu. Lyhyet odotukset yritetään uudelleen puolestasi; pidemmät (kuten 2 varmuuskopiota 10 minuutissa) epäonnistuvat heti ja asettavat tämän arvon.
  • message: luettava kuvaus.
  • errors: virheiden raakalista.
  • body: vastauksen sisältö.
  • Pikatarkistukset: isNotFound, isRateLimited ja isValidation.

Yleisimmät:

  • 401: avain on väärä, vajaa tai peruttu.
  • 403 ja This IP address (…) does not have permission: avaimella on sallittujen IP-osoitteiden lista, jossa osoitettasi ei ole.
  • 409 downgrade_requires_reset ja 422 unknown_version: kutsusta game.setVersion().
  • 429: pyyntöraja; katso retryAfter.
  • Aikakatkaistu kirjoitus (tila 0) voi silti valmistua paneelissa, joten tarkista ennen kuin yrität uudelleen.

Suorat pyynnöt#

Jos päätepisteelle ei vielä ole metodia, kutsu sitä suoraan:

await hw.http.get(`/servers/${id}/players`);
await hw.http.post(`/servers/${id}/command`, { command: 'list' });

Polut ovat suhteessa osoitteeseen /api/client, ja vastaukset puretaan samoin kuin valmiissa metodeissa. Kaikki päätepisteet löytyvät rajapinnan viitteestä.

Versiot#

TiedostoKäyttö
https://hostwolf.net/dl/hostwolf-sdk-.tgzKiinteä versio. Käytä tätä tiedostossa package.json.
https://hostwolf.net/dl/hostwolf-sdk.tgzAina uusin versio. Paketinhallinnat tallentavat välimuistiin URL-osoitteen mukaan, joten käytä mieluummin versioituja URL-osoitteita.
https://hostwolf.net/dl/index.jsonNykyiset versiot SHA-256- ja npm-eheystiivisteineen.

Päivitä asentamalla uudempi versioitu URL.

Lisenssi#

MIT

Jumissa, tai puuttuuko päätepiste? Kerro meille osoitteessa game.hostwolf.net/contact tai support@hostwolf.net.