> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kazzle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Automatyzacje

> Planuj zadania i reaguj na webhooki z poziomu aplikacji Kazzle.

Automatyzacje to części aplikacji, które działają samodzielnie — zgodnie z harmonogramem lub gdy usługa zewnętna wyśle zdarzenie.

Deklarujesz je w komponencie process w `kazzle.config.ts`. Jeden komponent może mieć tyle wyzwalaczy, ile potrzebujesz.

## Struktura

```ts theme={"theme":"material-theme-darker"}
{
  name: 'events',
  type: 'process',
  path: './components/events/index.ts',
  processMode: 'persistent', // lub 'triggered'
  triggers: [
    { name: 'daily-digest', kind: 'schedule', schedule: '0 9 * * *', path: '/cron/daily-digest' },
    { name: 'stripe',       kind: 'webhook',                          path: '/webhook/stripe' },
  ],
}
```

Tutaj zachodzą dwie rzeczy:

* `processMode` określa **cykl życia** — długotrwały serwer lub jednorazowe uruchomienie na wyzwalacz.
* `triggers[]` wymienia **zdarzenia**, które powinny uruchomić ten komponent.

Te dwie części są niezależne. Serwer persistent może mieć cron. Proces efemeryczny może mieć webhook. Wybierz cykl życia pasujący do obciążenia, a następnie dołącz tyle wyzwalaczy, ile chcesz.

## `processMode`

| Tryb                     | Co się uruchamia                                             | Kiedy używać                                                                                                  |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `persistent` (domyślnie) | Długotrwały serwer HTTP. Wyzwalacze są do niego POSTowane.   | Komponent już obsługuje HTTP lub przechowuje stan w pamięci (kolejki, websockety, cache'e).                   |
| `triggered`              | Skrypt wejściowy jest uruchamiany na wyzwalacz i kończy się. | Czyste zadania w tle — nocne czyszczenie, obsługa pojedynczego webhooka Stripe itp. Bez bezczynnych serwerów. |

## Wyzwalacze

Każdy wyzwalacz ma `name` (unikalną w obrębie komponentu), `kind` i — w zależności od trybu — `schedule` i/lub `path`.

| Pole       | Kiedy wymagane                  | Uwagi                                                         |
| ---------- | ------------------------------- | ------------------------------------------------------------- |
| `name`     | zawsze                          | Używane jako segment URL webhooka i w logach. Kebab-case.     |
| `kind`     | zawsze                          | `'schedule'` lub `'webhook'`.                                 |
| `schedule` | gdy `kind: 'schedule'`          | Wyrażenie cron z 5 polami. Rozdzielczość minutowa to minimum. |
| `path`     | gdy `processMode: 'persistent'` | Trasa HTTP na serwerze, gdzie ląduje wyzwalacz.               |

## Tryb persistent — HTTP do serwera

Gdy wyzwalacz uruchamia się dla komponentu persistent, Kazzle POSTuje do serwera na zadeklarowanej `path`. Żądanie zawiera:

| Nagłówek                                         | Co ci mówi                                             |
| ------------------------------------------------ | ------------------------------------------------------ |
| `Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}` | Zwaliduj to. Odrzuć wywołania, które się nie zgadzają. |
| `x-kazzle-trigger-name`                          | `name` wyzwalacza z manifestu.                         |
| `x-kazzle-trigger-run-id`                        | Nieprzezroczysty ID do korelacji logów.                |
| `x-kazzle-triggered-by`                          | `cron` \| `webhook` \| `manual`.                       |

W przypadku wyzwalaczy webhook, oryginalna treść żądania jest przekazywana jako treść POST. W przypadku wyzwalaczy schedule treść jest pusta.

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (tryb persistent)
const TRIGGER_SECRET = process.env.KAZZLE_TRIGGER_SECRET ?? '';

Bun.serve({
  port: Number(process.env.PORT),
  hostname: process.env.HOST,
  async fetch(req) {
    const url = new URL(req.url);

    if (req.method === 'POST' && url.pathname === '/cron/daily-digest') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      await sendDigest();
      return Response.json({ ok: true });
    }

    if (req.method === 'POST' && url.pathname === '/webhook/stripe') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      const event = await req.json();
      await handleStripe(event);
      return Response.json({ ok: true });
    }

    return new Response('not found', { status: 404 });
  },
});
```

## Tryb triggered — jednorazowo na wyzwalacz

Gdy wyzwalacz uruchamia się dla komponentu `triggered`, Kazzle uruchamia skrypt wejściowy od nowa i czeka na jego zakończenie. Nie ma `path`; skrypt dowiaduje się, który wyzwalacz się uruchomił, z zmiennych środowiskowych.

| Zmienna środowiskowa | Wartość                                 |
| -------------------- | --------------------------------------- |
| `TRIGGER_NAME`       | `name` wyzwalacza z manifestu.          |
| `TRIGGERED_BY`       | `cron` \| `webhook` \| `manual`.        |
| `RUN_ID`             | Nieprzezroczysty ID do korelacji logów. |
| `WEBHOOK_PAYLOAD`    | Treść JSON (tylko wyzwalacze webhook).  |

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (tryb triggered)
const trigger = process.env.TRIGGER_NAME;
const runId = process.env.RUN_ID;

if (trigger === 'daily-digest') {
  await sendDigest();
} else if (trigger === 'stripe') {
  const event = JSON.parse(process.env.WEBHOOK_PAYLOAD ?? '{}');
  await handleStripe(event);
}

console.log(`run ${runId} done`);
```

Komponenty triggered nie mają bezczynnych maszyn w produkcji — uruchamiają się na każde wywołanie i zamykają się przy wyjściu.

## Adresy URL webhooków

```text theme={"theme":"material-theme-darker"}
POST https://api.kazzle.app/webhooks/{spaceId}/{appId}/{componentName}/{triggerName}
```

Segment `triggerName` musi odpowiadać wpisowi `kind: 'webhook'` w `triggers[]` tego komponentu. Nieznane nazwy wyzwalaczy zwracają 404.

## Rozdzielczość harmonogramu

Wyrażenia cron mają 5 pól (minuta, godzina, dzień miesiąca, miesiąc, dzień tygodnia) i rozdzielczość minutowa to minimum. Harmonogramy poniżej minuty są odrzucane podczas walidacji manifestu.

## Jak są rejestrowane uruchomienia

Każde uruchomienie wyzwalacza zapisuje wiersz `process_runs` z `trigger_name`, `triggered_by`, `run_id` i statusem wyjścia uruchomienia. Możesz je odpytać z własnego kodu lub sprawdzić w widoku uruchomień aplikacji.

## Brak kredytów

Nieudane uruchomienie jest rejestrowane i logowane, ale harmonogram nadal działa w normalnym tempie — niestabilne uruchomienie nigdy nie wyłącza wyzwalacza. Jedyną rzeczą, która zatrzymuje uruchomienie, są kredyty: każde uruchomienie wyzwalacza jest sprawdzane względem salda Space'u, a gdy Space nie ma kredytów (lub nie ma skonfigurowanego rozliczenia), uruchomienia są pomijane z kodem `402`. To się samo naprawia — harmonogram pozostaje aktywny i następne uruchomienie po uzupełnieniu kredytów działa normalnie, bez ręcznego wznowienia.

## Dodawanie automatyzacji później

Prosta aplikacja może zacząć bez wyzwalaczy i zyskać je później — dodaj codzienne podsumowanie, połącz Stripe, uruchom czyszczenie. Cykl życia komponentu (`processMode`) i wyzwalacze (`triggers[]`) są niezależne, więc możesz je zmienić bez przepisywania reszty aplikacji.
