DesarrolladoresSDK de TypeScript

SDK de Hostwolf Apps para JavaScript y TypeScript

@hostwolf/apps-sdk es el cliente tipado oficial de la API de Hostwolf Apps. Con él puedes desplegar y gestionar tus apps desde código: proyectos, aplicaciones, despliegues y registros, variables de entorno, bases de datos gestionadas, autoescalado, volúmenes y más.

npm install https://app.hostwolf.net/dl/hostwolf-apps-sdk-0.2.1.tgz

El SDK se sirve desde app.hostwolf.net en lugar del registro de npm. El comando de arriba lo añade a tu package.json como "@hostwolf/apps-sdk": "https://app.hostwolf.net/dl/hostwolf-apps-sdk-0.2.1.tgz", y lo importas como @hostwolf/apps-sdk. pnpm, yarn y bun aceptan la misma URL. Cada versión tiene su propia URL; consulta Versiones.

Funciona en Node 18+, Bun y Deno, y no tiene dependencias en tiempo de ejecución.

Inicio rápido#

Crea un token de API en la consola en Claves y tokens → Tokens de API y luego:

import { HostwolfApps } from '@hostwolf/apps-sdk';

const hw = new HostwolfApps({ token: process.env.HOSTWOLF_APPS_TOKEN! });

const server = (await hw.servers.purchased()).find((s) => s.kind === 'app' && s.status === 'ready')!;
const { uuid: project } = await hw.projects.create({ name: 'shop' });

const app = await hw.applications.createFromGit({
    project_uuid: project,
    environment_name: 'production',
    server_uuid: server.server_uuid!,
    git_repository: 'https://github.com/acme/shop',
    git_branch: 'main',
    build_pack: 'nixpacks',
    ports_exposes: '3000',
});
await hw.applications.setEnvs(app.uuid, { NODE_ENV: 'production' });

const deployment = await hw.deployAndWait(app.uuid);
console.log(deployment.status, await hw.applications.logs(app.uuid, { lines: 20 }));

Los recursos se identifican por su uuid. Los objetos de respuesta usan los nombres de campo propios de la API (snake_case), y sus tipos de TypeScript se exportan, p. ej. Application, Deployment y ManagedDatabase.

Opciones#

new HostwolfApps({
    token: '12|abc...',                              // required
    baseUrl: 'https://console.app.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
    language: 'de',                                  // error messages in en, de, fr, es or fi
    fetch: customFetch,                              // optional
});

Proyectos y entornos#

MétodoQué hace
projects.list() / get(uuid)Proyectos; get incluye los entornos
projects.create({ name })Proyecto nuevo con un entorno production
projects.update / deleteCambia el nombre de un proyecto o elimínalo si está vacío
projects.environments(uuid)Entornos de un proyecto
projects.environment(uuid, 'staging')Un entorno (por nombre o uuid) con sus recursos
projects.createEnvironment / updateEnvironment / deleteEnvironmentGestionar entornos
projects.sharedEnvs / environmentSharedEnvsVariables compartidas por todas las apps de un proyecto o entorno

Aplicaciones#

MétodoQué hace
applications.list({ tag })Todas las aplicaciones del equipo
applications.get(uuid)Una aplicación con sus ajustes
applications.createFromGit(input)Desde un repositorio público (nixpacks, railpack, dockerfile o static)
applications.createFromPrivateRepo(input)Desde un repositorio privado con una clave de despliegue (private_key_uuid)
applications.createFromGithubApp(input)Desde un repositorio de una GitHub App conectada (github_app_uuid); los despliegues por push funcionan sin configurar nada
applications.createFromDockerfile(input)Desde el texto de un Dockerfile, sin repositorio
applications.createFromImage(input)Desde una imagen ya construida como ghcr.io/acme/web
applications.update(uuid, changes)Nombre, dominios, puertos, rama, comandos de build e inicio, health check, límites
applications.deploy(uuid, { force })Hace el build e inicia; devuelve el deployment_uuid
applications.restart / stopReinicia sin rehacer el build, o detén
applications.logs(uuid, { lines })Salida reciente del contenedor (el token necesita read:sensitive)
applications.metrics(uuid, { minutes })Historial de CPU y memoria
applications.rollbackImages / rollback(uuid, tag)Vuelve a desplegar una imagen anterior
applications.delete(uuid, { deleteVolumes })Elimina la app (y por defecto sus volúmenes)

Las apps se ejecutan en tus servidores de aplicaciones. server_uuid es el campo server_uuid de servers.purchased() (o el uuid de servers.list()). Omite domains para obtener una dirección *.app.hostwolf.net generada.

Variables de entorno#

MétodoQué hace
applications.envs(uuid)Variables; los valores son null salvo que el token tenga read:sensitive
applications.setEnvs(uuid, { KEY: 'value' })Crea o actualiza varias a la vez
applications.setEnv(uuid, { key, value, is_shown_once })Crea o actualiza una
applications.createEnv / updateEnvCreación estricta (409 si existe) y actualización
applications.deleteEnv(uuid, envUuid) / deleteEnvByKey(uuid, key)Elimina una variable
teams.sharedEnvs() y similaresVariables de todo el equipo, usadas como {{team.KEY}}

Los contenedores en ejecución ven las variables cambiadas tras el siguiente despliegue.

Despliegues#

const { deployment_uuid } = await hw.applications.deploy(app.uuid);
const done = await hw.deployments.wait(deployment_uuid!, {
    timeoutMs: 10 * 60_000,
    onStatus: (status) => console.log(status), // queued, in_progress, finished
});

wait y deployAndWait lanzan un HostwolfAppsError con el código deployment_failed (y las últimas líneas del registro en el mensaje), deployment_cancelled o timeout. Pasa throwOnFailure: false para recibir el despliegue fallido en su lugar.

MétodoQué hace
deployments.running()Despliegues en cola o en curso ahora, en todo el equipo
deployments.get(uuid)Un despliegue, con su registro de build en logs
parseDeploymentLogs(deployment)El registro de build como líneas ({ output, type, timestamp })
deployments.forApplication(uuid, { skip, take })Una página de los despliegues de una app, los más recientes primero
deployments.allForApplication(uuid)Iterador asíncrono sobre todos ellos
deployments.deploy({ uuid, tag, force })Despliega varias apps por uuid o etiqueta
deployments.cancel(uuid)Cancela un despliegue en cola o en curso

Servidores#

MétodoQué hace
servers.purchased() / purchasedServer(uuid)Servidores de aplicaciones y VPS que compraste: tamaño, ciudad, estado, precio y server_uuid
servers.list() / get(uuid)Servidores de aplicaciones en los que pueden ejecutarse las apps
servers.resources(uuid) / domains(uuid)Qué se ejecuta en un servidor, y sus dominios
servers.metrics(uuid, { minutes })Historial de CPU y memoria
servers.destinations(uuid)Las redes Docker de un servidor (para varios servidores)
servers.traffic(uuid, { month })Tráfico saliente de un servidor comprado y lo que lleva facturado (1 TB al mes incluido)
servers.teamTraffic({ month })Lo mismo para todos los servidores del equipo, con el total
servers.regions({ size })Ciudades con el precio mensual por tamaño y dónde puede añadir servidores el autoescalado

Comprar y cancelar servidores solo es posible en la consola, así que un token de API nunca puede comprar un servidor.

Varios servidores#

Ejecuta copias de una app en varios de tus servidores de aplicaciones. Hostwolf hace el build de la imagen una vez, inicia una copia en cada servidor y reparte la carga entre ellas.

await hw.applications.addServer(app.uuid, otherServer.server_uuid!);
await hw.deployAndWait(app.uuid);
console.log(await hw.applications.destinations(app.uuid)); // primary + copies

removeDestination(appUuid, destinationUuid) vuelve a detener la copia en ese servidor.

Bases de datos gestionadas#

PostgreSQL, MySQL/MariaDB y Redis en servidores de bases de datos de Hostwolf, separados de tus servidores de aplicaciones, con TLS, copias de seguridad nocturnas fuera del servidor y recuperación a un momento concreto para PostgreSQL.

const plans = await hw.databases.catalog();             // engines, plans, pool sizes, regions, prices
const db = await hw.databases.create({ engine: 'postgres', plan: 'small', region: 'vultr:ams' });
await hw.databases.waitUntilReady(db.uuid);
await hw.databases.attach(db.uuid, app.uuid);           // DATABASE_URL (pooled) + DATABASE_DIRECT_URL
await hw.deployAndWait(app.uuid);

Una base de datos se factura por hora en tu suscripción de servidor desde el momento en que se crea. create envía una Idempotency-Key, así que una llamada repetida nunca crea una segunda base de datos; pasa la tuya con { idempotencyKey }.

connection(uuid) devuelve dos URLs. url pasa por el pooler de conexiones (PgBouncer en el 6432, ProxySQL en el 6033): úsala en tu app. direct_url llega directamente a la base de datos: úsala para migraciones, LISTEN/NOTIFY, ajustes de sesión y transacciones largas. Redis tiene una sola URL.

MétodoQué hace
databases.list() / get(uuid)Tus bases de datos con motor, plan, región, estado, precio, límites, upgrades y advice
databases.connection(uuid)Las URLs con pool y directa con la contraseña (el token necesita read:sensitive)
databases.attach(uuid, appUuid, { envKey, overwrite })Guarda la URL con pool como DATABASE_URL y la directa como DATABASE_DIRECT_URL (Redis: REDIS_URL). Si la app ya las tiene con otro valor, lanza conflict con error.conflicts y error.suggestedEnvKey; pasa envKey u overwrite: true
databases.upgrade(uuid, plan, { confirm: true })Pasa a un plan más grande de upgrades; la URL sigue siendo la misma
databases.setEvictionPolicy(uuid, policy)Redis: allkeys-lru para una caché, noeviction para colas (se aplica en vivo)
databases.backup(uuid)Inicia ahora una copia de seguridad extra
databases.delete(uuid)Elimínala y detén la facturación; soporte puede restaurarla durante 7 días

upgrade sin { confirm: true } no cambia nada: lanza un HostwolfAppsError con el código confirm_required, y error.quote contiene el nuevo precio mensual para que lo muestres antes.

Autoescalado#

const { policy, blocker, status } = await hw.autoscaling.get(app.uuid);
await hw.autoscaling.enable(app.uuid, { min_copies: 1, max_copies: 4, monthly_cap_usd: 40 });

El autoescalado añade servidores de aplicaciones cerca de tus visitantes cuando la CPU se mantiene alta, y los quita cuando baja. Los servidores añadidos se facturan por hora al precio normal, hasta monthly_cap_usd. blocker dice por qué aún no se puede activar (por ejemplo, no hay ninguna suscripción de servidor activa). Los campos que omitas conservan su valor; disable(uuid) lo desactiva. cities lista los ids de región que acepta allowed_cities para la app, con el precio por hora allí; servers.regions() lista todas las ciudades.

Volúmenes de almacenamiento#

Almacenamiento en bloque para tus servidores de aplicaciones y VPS: de 10 a 1000 GB, montado en /mnt/volume-<id> en el servidor y facturado por hora. Las llamadas que aumentan la factura necesitan { confirm: true }; sin él no cambian nada y lanzan el código confirm_required con el precio en error.quote.

const offer = await hw.volumes.offer(server.uuid, { sizeGb: 50 });   // sizes allowed here and the price
const volume = await hw.volumes.create(server.uuid, { size_gb: 50, autogrow_max_gb: 200 }, { confirm: true });
await hw.volumes.waitFor(volume.id);                                  // mounted in about a minute
await hw.volumes.resize(server.uuid, volume.id, 100, { confirm: true });
MétodoQué hace
volumes.list(serverUuid) / listAll({ status }) / get(id)Los volúmenes de un servidor, o todos los volúmenes del equipo (status: 'detached' para los desvinculados)
volumes.offer(serverUuid, { sizeGb })Si la ciudad del servidor vende volúmenes, los tamaños y un precio
volumes.create(serverUuid, { size_gb, backups, autogrow_max_gb }, { confirm })Añade un volumen (copias de seguridad activadas por defecto)
volumes.resize / setBackups / setAutogrow / updateAmpliar (nunca reducir), activar o desactivar las copias de seguridad, fijar el límite de crecimiento automático
volumes.detach(serverUuid, id)Desmóntalo y desvincúlalo; conserva sus archivos y se sigue facturando
volumes.attach(id, serverUuid, { confirm })Vincula un volumen desvinculado a un servidor de la misma ciudad
volumes.backups(id) / backupNow(serverUuid, id)Copias de seguridad que se pueden restaurar, o una ahora
volumes.restore(serverUuid, id, backupId, 'same' | 'new', { confirm })Restaura en el propio volumen o en uno nuevo
volumes.restoreDetached(id, backupId, serverUuid, { confirm })Restaura la copia de seguridad de un volumen desvinculado en un volumen nuevo
volumes.delete(id, { confirm: true })Elimínalo junto con sus archivos

Cuando se elimina un servidor, sus volúmenes se desvinculan y se conservan 7 días, sin coste (kept_until); mientras tanto, vincula uno a otro servidor de la misma ciudad, o se eliminará.

Volúmenes de apps#

MétodoQué hace
applications.storages(uuid)Volúmenes persistentes y archivos montados
applications.createStorage(uuid, { type: 'persistent', name, mount_path })Añade un volumen (vuelve a desplegar para montarlo)
applications.updateStorage / deleteStorageCambia o elimina una
applications.setVolumeBackup(uuid, storageUuid, { frequency: 'daily' })Haz copias de seguridad de un volumen según una programación
applications.runVolumeBackup / deleteVolumeBackupHaz una copia ahora o elimina la programación

Equipos#

MétodoQué hace
teams.current() / members()El equipo del token y sus miembros
teams.update({ name })Cambia el nombre del equipo
teams.auditEvents({ page }) / allAuditEvents()El registro de auditoría del equipo (administradores y propietarios)

Paginación#

Las listas que la API pagina vienen de dos formas: un método devuelve una sola página, y un método all… devuelve un iterador asíncrono sobre todos los elementos. collect convierte un iterador en un array:

import { collect } from '@hostwolf/apps-sdk';

const deployments = await collect(hw.deployments.allForApplication(app.uuid), 100);
for await (const event of hw.teams.allAuditEvents({ action: 'deploy' })) console.log(event.description);

Errores#

Las llamadas fallidas lanzan un HostwolfAppsError.

import { HostwolfAppsError } from '@hostwolf/apps-sdk';
try {
    await hw.applications.createFromDockerfile({ project_uuid, server_uuid, dockerfile });
} catch (e) {
    if (e instanceof HostwolfAppsError && e.isValidation) console.log(e.errors); // { field: ['message'] }
    else throw e;
}

Tiene estas propiedades:

  • status: el estado HTTP, o 0 para errores de red, tiempos de espera agotados y despliegues fallidos.
  • code: un código corto como validation_failed, unauthenticated, forbidden, not_found, rate_limited, confirm_required, deployment_failed o timeout.
  • quote: con confirm_required, el precio que ha calculado la API (muéstralo y luego vuelve a llamar con { confirm: true }).
  • retryAfter: en un 429, los segundos que faltan para poder repetir la llamada. Las esperas cortas se reintentan por ti; las más largas fallan al momento con este campo definido.
  • message: una descripción legible, incluidos los errores por campo.
  • errors: errores por campo de un 422, como { field: ['message'] }.
  • body: el cuerpo de la respuesta. Un 403 por algo que Hostwolf hace por ti (como añadir servidores) incluye un enlace docs.
  • Indicadores rápidos: isNotFound, isRateLimited, isValidation, isUnauthorized e isConfirmRequired.

Peticiones directas#

Si un endpoint aún no tiene método, llámalo directamente:

await hw.http.get(`/applications/${uuid}/scheduled-tasks`);
await hw.http.post(`/applications/${uuid}/scheduled-tasks`, { name: 'cleanup', command: 'npm run cleanup', frequency: '0 3 * * *' });

Las rutas son relativas a /api/v1. Consulta la referencia de la API para ver todos los endpoints.

Versiones#

ArchivoUso
https://app.hostwolf.net/dl/hostwolf-apps-sdk-.tgzUna versión fija. Úsala en package.json.
https://app.hostwolf.net/dl/hostwolf-apps-sdk.tgzSiempre la versión más reciente. Los gestores de paquetes guardan en caché por URL, así que es mejor usar URLs con versión.
https://app.hostwolf.net/dl/index.jsonVersiones actuales, con hashes SHA-256 y de integridad de npm.

Para actualizar, instala la URL con la versión más nueva.

Licencia#

MIT

¿Atascado, o te falta un endpoint? Escríbenos en app.hostwolf.net/contact o a [email protected].