npm package: @laioutr/byos-agent
Description: BYOS deployment agent for Laioutr - executes scripts from webhook events
Latest version: 0.0.1

# @laioutr/byos-agent

BYOS deployment agent for [Laioutr BYOS (Bring Your Own Server)](<https://docs.laioutr.io/hosting/bring-your-own-server-(byos)/webhook-config>) hosting. Execute bash scripts in response to deployment webhooks.

This project serves as a reference implementation and starting point for building your own webhook handler. Use it as-is for simple deployments, or as inspiration for custom implementations.

## Installation

### As a CLI tool

```bash
# Run directly with npx
npx @laioutr/byos-agent

# Or install globally
npm install -g @laioutr/byos-agent
laioutr-byos-agent

# Or as a local dependency
npm install @laioutr/byos-agent
npx laioutr-byos-agent
```

### As a library

```bash
npm install @laioutr/byos-agent
```

## Configuration

Create a `byos-agent.config.ts` (or `.js`, `.json`) in your project root:

```typescript
import type { WebhookRunnerConfigInput } from '@laioutr/byos-agent';

export default {
  // Required: Your webhook signing secret from Laioutr
  signingSecret: 'whsec_...',

  // Required: URL of the deployed application. Can be overridden in the hosting.describe script.
  baseUrl: 'https://your-server.com',

  // Optional: Port to listen on (default: 4000)
  port: 4000,

  // Optional: Display name for your server
  name: 'My Deployment Server',

  // Optional: Shell to use for scripts (default: $SHELL or /bin/sh)
  shell: '/bin/bash',

  // Optional: Script timeout in seconds (default: 600, null for no timeout)
  timeout: 600,

  // Optional: Directory for temporary deployment files (default: OS temp dir)
  tempDir: '/var/deployments',

  // Optional: Log level (default: 'info')
  logLevel: 'debug',

  // Scripts to run for each webhook event
  scripts: {
    'hosting.deployment.created': './scripts/deploy.sh',
    'hosting.deployment.cancel': 'echo "Cancelled: $LAIOUTR_DEPLOYMENT_ID"',
    'hosting.deployment.delete': './scripts/cleanup.sh',
    'hosting.deployment.promote': './scripts/promote.sh',
    'hosting.deployment.rollback': './scripts/rollback.sh',
    'hosting.connected': 'echo "Project connected"',
    'hosting.disconnected': 'echo "Project disconnected"',
  },
} satisfies WebhookRunnerConfigInput;
```

Scripts can be file paths or inline shell commands.

## Scripts Reference

All scripts are **optional**. Configure the ones you need.

### Common Environment Variables

All scripts receive:

| Variable            | Description                                                       |
| ------------------- | ----------------------------------------------------------------- |
| `LAIOUTR_PROJECT`   | Project identifier in format `<organization-slug>/<project-slug>` |
| `LAIOUTR_EVENT`     | Event type (e.g. `hosting.deployment.created`)                    |
| `LAIOUTR_TIMESTAMP` | ISO timestamp of the event                                        |

### hosting.deployment.created

Executes when a new deployment is created. Runs in the background with status callbacks.

**Additional Environment Variables:**

| Variable                | Description                            |
| ----------------------- | -------------------------------------- |
| `LAIOUTR_DEPLOYMENT_ID` | Unique deployment ID                   |
| `LAIOUTR_ENVIRONMENT`   | Target environment (e.g. `production`) |
| `LAIOUTR_CALLBACK_URL`  | URL for status updates                 |
| `LAIOUTR_PAYLOAD_FILE`  | Path to full webhook payload JSON      |
| `LAIOUTR_FILES_DIR`     | Directory containing deployment files  |

**Working Directory:** Set to `LAIOUTR_FILES_DIR`

**Output:** The last line of stdout is parsed for:

- A URL matching `(http|https)://...` → reported as deployment URL
- The word `promoted` (optional, case-insensitive) → marks deployment as promoted

```bash
#!/bin/bash
# Example: Output URL on success
echo "https://preview-${LAIOUTR_DEPLOYMENT_ID}.example.com"

# Or mark as success and promoted to production
echo "promoted https://example.com"

# Optionally you can just mark as success and promoted without a URL. Cockpit will use the configured baseUrl.
echo "promoted"
```

If no URL is output but the script exits with code 0, the configured `baseUrl` is used.

**Status Callbacks:** Automatically sent to `LAIOUTR_CALLBACK_URL`:

| Status     | When                                 |
| ---------- | ------------------------------------ |
| `running`  | Script starts                        |
| `success`  | Exit code 0                          |
| `promoted` | Exit code 0 + "promoted" in output   |
| `error`    | Non-zero exit, timeout, or cancelled |

### hosting.deployment.cancel

Handles deployment cancellation. **Cancellation is always supported** - if a deployment is running, the process group is killed automatically. The optional script runs after termination for cleanup.

**Additional Environment Variables:**

| Variable                | Description             |
| ----------------------- | ----------------------- |
| `LAIOUTR_DEPLOYMENT_ID` | Deployment ID to cancel |

**Output:** Not parsed.

### hosting.deployment.delete

Executes when a deployment is deleted.

**Additional Environment Variables:**

| Variable                | Description             |
| ----------------------- | ----------------------- |
| `LAIOUTR_DEPLOYMENT_ID` | Deployment ID to delete |

**Output:** Not parsed.

### hosting.deployment.promote

Executes when a deployment is promoted to production.

**Additional Environment Variables:**

| Variable                | Description              |
| ----------------------- | ------------------------ |
| `LAIOUTR_DEPLOYMENT_ID` | Deployment ID to promote |

**Output:** Not parsed.

### hosting.deployment.rollback

Executes when rolling back to a previous deployment.

**Additional Environment Variables:**

| Variable                     | Description                           |
| ---------------------------- | ------------------------------------- |
| `LAIOUTR_DEPLOYMENT_ID`      | Target deployment ID                  |
| `LAIOUTR_FROM_DEPLOYMENT_ID` | Previous deployment ID (may be empty) |

**Output:** Not parsed.

### hosting.describe

Provides hosting information. Used to override the base URL dynamically.

**Output:** If the last line contains a URL, it overrides `baseUrl` in the response.

### hosting.connected / hosting.disconnected

Execute when a project connects or disconnects.

**Output:** Not parsed.

## Library Usage

The webhook router can be integrated into any server that supports [Hono](https://hono.dev) routers:

```typescript
import { Hono } from 'hono';
import { webhookRouter, loadConfig, setRuntimeConfig } from '@laioutr/byos-agent';

// Load config from byos-agent.config.ts and set it for the router
const config = await loadConfig();
setRuntimeConfig(config);

const app = new Hono();

// Mount at /webhook
app.route('/webhook', webhookRouter);

export default app;
```

## Documentation

For complete webhook configuration details, event types, and implementation guidance, see the [Laioutr BYOS Documentation](<https://docs.laioutr.io/hosting/bring-your-own-server-(byos)/webhook-config>).

## License

MIT