# Tugas Latar Belakang

<Badge type="warning" text="Belum dirilis" />

Tugas latar belakang adalah fungsi yang terus berjalan selama Extension menyala, misalnya sinkronisasi setiap 15 menit. Tugas ini memerlukan `service` di `dozin.capabilities`. Daftarkan dengan `api.registerService(definition)`.

## ServiceDefinition

| Field | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `id` | `string` | Tidak | Default-nya nama paket. Huruf kecil, angka, dan tanda hubung. Paling banyak 8 per Extension |
| `name` | `string` | Tidak | Ditampilkan di **Pengaturan › Ekstensi**, 1–80 karakter; default-nya `id` |
| `start` | `'auto' \| 'manual'` | Tidak | Default `manual`. `auto` mulai bersama Extension setelah `secrets`-nya diisi. Pilihan pengguna yang berlaku |
| `restart` | `{ policy?, maxRetries?, backoffSeconds? }` | Tidak | `policy`: `never`, `on-failure` (default) atau `always`; `maxRetries` 0–100 (default 5); `backoffSeconds` 1–10 jeda, masing-masing 1–3600 detik (default 5, 30, 120) |
| `stopTimeoutSeconds` | `number` | Tidak | 1–30, default 10: berapa lama **Hentikan** menunggu `run` selesai |
| `run(ctx)` | function | Ya | Lihat di bawah |

Field yang tidak valid membuat seluruh tugas ditolak (`registerService` melempar `TypeError`); tidak ada yang diperbaiki untuk Anda.

## run(ctx) <Badge type="warning" text="Belum dirilis" />

Tugas itu sendiri. Berjalan sampai `ctx.signal` terpicu.

| Nama | Tipe | Deskripsi |
|---|---|---|
| `ctx` | `ServiceContext` | Field dan fungsi di bawah ini |

Mengembalikan `Promise<void>`. Selesai berarti run berakhir. Melempar error dihitung sebagai kegagalan dan dicoba ulang sesuai `restart`. Jika **Hentikan** ditekan dan `run` belum selesai dalam `stopTimeoutSeconds`, proses Extension dijalankan ulang.

## Field ServiceContext

| Field | Tipe | Deskripsi |
|---|---|---|
| `serviceId` | `string` | Key tugas: nama paket, atau `<package>.<id>` |
| `runId` | `string` | Run ini |
| `signal` | `AbortSignal` | Terpicu saat **Hentikan**, **Mulai ulang**, mematikan Extension, menghapus instalasinya, dan keluar dari Cowork. Periksa sebelum setiap permintaan |
| `env` | `Readonly<Record<string, string>>` | Nilai dari `dozin.secrets`. Tugas yang kekurangan nilai tidak akan mulai dan menampilkan "Konfigurasi belum lengkap" |
| `dataDir` | `string` | Folder data milik Extension, sekaligus folder kerjanya |

## ctx.status(text, level?, progress?) <Badge type="warning" text="Belum dirilis" />

Mengatur baris yang ditampilkan di bawah tugas di **Pengaturan › Ekstensi**.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `text` | `string` | Ya | Barisnya; paling banyak 300 karakter yang disimpan |
| `level` | `'info' \| 'warn' \| 'error'` | Tidak | Default `info` |
| `progress` | `{ done: number, total: number }` | Tidak | Ditampilkan sebagai progres |

Tidak mengembalikan apa pun. Nilai dari `dozin.secrets` diganti dengan `***`. Jangan masukkan data pribadi: baris ini terlihat oleh siapa pun yang ada di depan komputer.

```js
ctx.status('Synced 52 patients', 'info', { done: 52, total: 52 });
```

## ctx.log(text) <Badge type="warning" text="Belum dirilis" />

Menulis satu baris ke log Extension. Nilai dari `dozin.secrets` diganti dengan `***`. Tidak mengembalikan apa pun.

```js
ctx.log('round started');
```

## ctx.readJson(rel) <Badge type="warning" text="Belum dirilis" />

Membaca file JSON di dalam `dataDir`.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `rel` | `string` | Ya | Path relatif terhadap `dataDir` |

Mengembalikan `Promise<unknown>`: nilai hasil parse, atau `undefined` jika file tidak ada. Reject dengan `PATH_OUTSIDE_DATA` untuk path kosong atau path di luar `dataDir`, dan dengan error parse jika file bukan JSON yang valid.

## ctx.writeJson(rel, value) <Badge type="warning" text="Belum dirilis" />

Menulis file JSON di dalam `dataDir`, menggantinya dalam satu langkah, sehingga crash tidak pernah meninggalkan file setengah jadi.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `rel` | `string` | Ya | Path relatif terhadap `dataDir` |
| `value` | `unknown` | Ya | Apa pun yang diterima `JSON.stringify` |

Mengembalikan `Promise<void>`. Reject dengan `PATH_OUTSIDE_DATA` untuk path kosong atau path di luar `dataDir`.

Setelah sebuah run berakhir, panggilan `ctx.site` dan `ctx.agent` darinya di-reject dengan `RUN_ENDED`.

## Contoh

```js
export function activate(api) {
  api.registerService({
    id: 'sync', name: 'Sync', start: 'auto',
    async run(ctx) {
      while (!ctx.signal.aborted) {
        const state = (await ctx.readJson('state.json')) ?? { runs: 0 };
        state.runs += 1;
        await ctx.writeJson('state.json', state);
        ctx.status(`Run ${state.runs} done`);
        await new Promise((resolve) => setTimeout(resolve, 15 * 60 * 1000));
      }
    }
  });
}
```

## Terkait

- [Permintaan Situs](https://docs.micromeet.ai/id/extension-sdk/site.md): `ctx.site`
- [Percakapan Layanan](https://docs.micromeet.ai/id/extension-sdk/agent.md): `ctx.agent`
- [Entri dan Pendaftaran](https://docs.micromeet.ai/id/extension-sdk/entry.md): `registerService`
