> ## 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.

# オートメーション

> Kazzle アプリ内からジョブをスケジュール設定し、ウェブフックに応答します。

オートメーションはアプリの一部で、スケジュールに従って、または外部サービスからイベントが送信されたときに自動的に実行されます。

`kazzle.config.ts` の process コンポーネントで宣言します。1 つのコンポーネントは必要なだけ多くのトリガーを持つことができます。

## 構造

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

ここで 2 つのことが起こっています：

* `processMode` は**ライフサイクル**を選択します — 長時間実行されるサーバー、またはトリガーごとの 1 回限りの実行。
* `triggers[]` はこのコンポーネントを実行する**イベント**をリストアップします。

この 2 つの部分は独立しています。永続的なサーバーは cron を持つことができます。一時的なプロセスはウェブフックを持つことができます。ワークロードに合ったライフサイクルを選択してから、必要なだけ多くのトリガーを追加してください。

## `processMode`

| モード                  | 実行内容                                | 使用時期                                                            |
| -------------------- | ----------------------------------- | --------------------------------------------------------------- |
| `persistent` (デフォルト) | 長時間実行される HTTP サーバー。トリガーは POST されます。 | コンポーネントが既に HTTP を提供している、またはメモリ内に状態を保持している（キュー、ウェブソケット、キャッシュ）。   |
| `triggered`          | エントリスクリプトはトリガーごとにスポーンされ、終了します。      | 純粋なバックグラウンドジョブ — 夜間クリーンアップ、単一の Stripe ウェブフックハンドラーなど。アイドルサーバーなし。 |

## トリガー

各トリガーには `name`（コンポーネント内で一意）、`kind`、およびモードに応じて `schedule` および/または `path` があります。

| フィールド      | 必須条件                            | 注記                                 |
| ---------- | ------------------------------- | ---------------------------------- |
| `name`     | 常に                              | ウェブフック URL セグメントとログで使用されます。ケバブケース。 |
| `kind`     | 常に                              | `'schedule'` または `'webhook'`。      |
| `schedule` | `kind: 'schedule'` の場合          | 5 フィールド cron 式。分単位の解像度が最小です。       |
| `path`     | `processMode: 'persistent'` の場合 | トリガーが到達するサーバー上の HTTP ルート。          |

## 永続モード — サーバーへの HTTP

永続的なコンポーネントのトリガーが発火すると、Kazzle は宣言された `path` でサーバーに POST します。リクエストは以下を含みます：

| ヘッダー                                             | 内容                               |
| ------------------------------------------------ | -------------------------------- |
| `Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}` | これを検証してください。一致しない呼び出しは拒否してください。  |
| `x-kazzle-trigger-name`                          | マニフェストからのトリガーの `name`。           |
| `x-kazzle-trigger-run-id`                        | ログ相関用の不透明 ID。                    |
| `x-kazzle-triggered-by`                          | `cron` \| `webhook` \| `manual`。 |

ウェブフックトリガーの場合、元のリクエストボディは POST ボディとして転送されます。スケジュールトリガーの場合、ボディは空です。

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (persistent mode)
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 });
  },
});
```

## トリガーモード — トリガーごとの 1 回限り

`triggered` コンポーネントのトリガーが発火すると、Kazzle はエントリスクリプトを新たにスポーンし、終了を待ちます。`path` はありません。スクリプトは環境変数からどのトリガーが発火したかを学習します。

| 環境変数              | 値                                |
| ----------------- | -------------------------------- |
| `TRIGGER_NAME`    | マニフェストからのトリガーの `name`。           |
| `TRIGGERED_BY`    | `cron` \| `webhook` \| `manual`。 |
| `RUN_ID`          | ログ相関用の不透明 ID。                    |
| `WEBHOOK_PAYLOAD` | JSON ボディ（ウェブフックトリガーのみ）。          |

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (triggered mode)
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`);
```

トリガーされたコンポーネントは本番環境にアイドルマシンがありません — 呼び出しごとにスピンアップし、終了時にシャットダウンします。

## ウェブフック URL

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

`triggerName` セグメントは、そのコンポーネントの `triggers[]` 内の `kind: 'webhook'` エントリと一致する必要があります。不明なトリガー名は 404 を返します。

## スケジュール解像度

Cron 式は 5 フィールド（分、時間、日、月、曜日）で、分単位の解像度が最小です。分未満のスケジュールはマニフェスト検証時に拒否されます。

## 実行の記録方法

各トリガー発火は `process_runs` 行を `trigger_name`、`triggered_by`、`run_id`、および実行の終了ステータスで書き込みます。独自のコードからこれらをクエリするか、アプリの実行ビューで検査できます。

## クレジット不足

失敗した実行は記録およびログされますが、スケジュールは通常のペースで実行を続けます — 不安定な実行がトリガーを無効にすることはありません。実行を停止する唯一のことはクレジットです：すべてのトリガー発火はスペースの残高に対してチェックされ、スペースがクレジット不足（またはビリング設定がない）の間、実行は `402` でスキップされます。これは自己修復可能です — スケジュールは武装したままで、トップアップ後の次の発火は通常どおり実行され、手動での再開は不要です。

## 後からオートメーションを追加

シンプルなアプリはトリガーなしで開始し、後で追加できます — 日次サマリーを追加、Stripe を接続、クリーンアップを実行。コンポーネントのライフサイクル（`processMode`）とトリガー（`triggers[]`）は独立しているため、アプリの残りの部分を書き直さずに変更できます。
