# Browser

Dengan `browser` di `dozin.capabilities`, Connector atau tugas mendapat `ctx.browser` untuk membaca dan bertindak di halaman web di tab Cowork. Hanya tab yang diserahkan pengguna yang bisa dipakai, dan jika `dozin.origins` diberikan, hanya halaman di situs-situs itu.

## ctx.browser.adoptTab(tabId)

Mengambil kendali sebuah tab. Cowork menyerahkan `tabId` ke Extension saat pengguna memintanya mengambil alih sebuah halaman; Extension tidak bisa memilih tab sendiri.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `tabId` | `string` | Ya | Tab yang diserahkan Cowork |

Mengembalikan `Promise<{ tabId, url, documentId }>`. `documentId` berubah setiap kali halaman memuat dokumen baru. Paling banyak 8 tab dalam satu waktu.

```js
const tab = await ctx.browser.adoptTab(tabId);
ctx.log(`watching ${tab.url}`);
```

## ctx.browser.releaseTab(tabId)

Mengembalikan tab. Skrip yang diinjeksikan dan audionya berhenti.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `tabId` | `string` | Ya | Tab yang diambil alih Extension ini |

Mengembalikan `Promise<void>`.

```js
await ctx.browser.releaseTab(tabId);
```

## ctx.browser.evaluate(tabId, script)

Menjalankan skrip sekali di halaman dan mengembalikan hasilnya.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `tabId` | `string` | Ya | Tab yang sudah diambil alih |
| `script.source` | `string` | Ya | Source sebuah fungsi, paling banyak 64 KB; fungsi itu menerima `args` |
| `script.args` | `unknown` | Tidak | Diberikan sebagai JSON, tidak ditempelkan ke source |

Mengembalikan `Promise<unknown>`: hasilnya, sebagai JSON, paling banyak 64 KB.

```js
const title = await ctx.browser.evaluate(tabId, { source: '(args) => document.title' });
```

## ctx.browser.inject(tabId, script)

Memasang skrip yang tetap berada di halaman dan bisa mengirim pesan balik.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `tabId` | `string` | Ya | Tab yang sudah diambil alih |
| `script.id` | `string` | Ya | 1–80 huruf, angka, `_` atau `-`; unik di tab ini |
| `script.source` | `string` | Ya | Sebuah fungsi `(args, { emit, onDispose }) => cleanup?`, paling banyak 64 KB; `emit(data)` tiba sebagai event `message` |
| `script.args` | `unknown` | Tidak | Diberikan sebagai JSON |

Mengembalikan `Promise<void>`. Paling banyak 16 skrip per tab.

```js
await ctx.browser.inject(tabId, {
  id: 'chat-watch',
  source: `(args, { emit }) => {
    const observer = new MutationObserver(() => emit({ count: document.querySelectorAll(args.selector).length }));
    observer.observe(document.body, { childList: true, subtree: true });
    return () => observer.disconnect();
  }`,
  args: { selector: '.message' }
});
```

## ctx.browser.uninject(tabId, injectionId)

Menghapus skrip yang diinjeksikan dan menjalankan pembersihannya.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `tabId` | `string` | Ya | Tab yang sudah diambil alih |
| `injectionId` | `string` | Ya | `id` yang diberikan ke `inject` |

Mengembalikan `Promise<void>`.

```js
await ctx.browser.uninject(tabId, 'chat-watch');
```

## ctx.browser.captureAudio(tabId)

Juga memerlukan `audio`. Mulai menangkap suara tab.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `tabId` | `string` | Ya | Tab yang sudah diambil alih dan selesai dimuat |

Mengembalikan `Promise<AudioStream>`:

| Field | Tipe | Deskripsi |
|---|---|---|
| `tabId` · `url` · `documentId` | | Tabnya, seperti di `adoptTab` |
| `streamId` | `string` | Penangkapan ini |
| `frames` | `AsyncIterable<{ streamId, sequence, timestamp, sampleRate, channels, pcm: Float32Array }>` | Suara, potongan demi potongan |
| `ended` | `Promise` | Selesai saat penangkapan berakhir |
| `stop()` | `() => Promise<void>` | Menghentikannya |

Satu penangkapan per tab dalam satu waktu.

```js
const stream = await ctx.browser.captureAudio(tabId);
for await (const chunk of stream.frames) handle(chunk.pcm, chunk.sampleRate);
```

## ctx.browser.stopAudio(tabId)

Menghentikan penangkapan suara tab.

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `tabId` | `string` | Ya | Tab dengan penangkapan yang sedang berjalan |

Mengembalikan `Promise<void>`.

```js
await ctx.browser.stopAudio(tabId);
```

## ctx.browser.onEvent(listener)

| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| `listener` | `(event) => void` | Ya | Dipanggil untuk setiap event dari tab milik Extension |

Mengembalikan `() => void` untuk berhenti mendengarkan. Setiap event membawa `tabId`, `url`, dan `documentId`, serta sebuah `type`:

| `type` | Field tambahan | Kapan |
|---|---|---|
| `url-changed` · `navigation-start` · `ready` | — | Halaman berpindah atau selesai dimuat |
| `closed` · `crashed` | — | Tab sudah tidak ada |
| `injected` | `injectionId` | Sebuah skrip sudah dipasang |
| `message` | `injectionId`, `data` | Skrip yang diinjeksikan memanggil `emit(data)` |
| `error` | `code` | Ada yang gagal di tab |
| `audio-ended` | `streamId`, `lastSequence` | Sebuah penangkapan berakhir |

```js
const off = ctx.browser.onEvent((event) => {
  if (event.type === 'message') ctx.emit({ text: String(event.data.count) });
  if (event.type === 'closed') off();
});
```

## Error

Fungsi-fungsi ini reject dengan salah satu kode berikut:

| Kode | Arti |
|---|---|
| `capability_denied` | `browser` (atau `audio`) tidak dideklarasikan |
| `invalid_tab` · `tab_not_granted` · `tab_not_owned` | ID tab tidak valid, Cowork tidak menyerahkan tab ini, atau tab sudah dikembalikan |
| `origin_not_allowed` | Situs halaman tidak ada di `dozin.origins` |
| `tab_limit` · `injection_limit` | Lebih dari 8 tab atau 16 skrip |
| `tab_unavailable` · `document_not_ready` · `stale_document` | Tab sudah ditutup, masih memuat, atau sudah memuat halaman lain |
| `invalid_script` · `invalid_injection_id` · `injection_exists` · `invalid_json` · `result_too_large` | Input atau output tidak valid |
| `audio_already_started` · `audio_transport_unavailable` · `invalid_audio_request` | Penangkapan audio tidak bisa dimulai |
| `session_closed` · `browser_operation_failed` | Extension sedang berhenti, atau Cowork tidak bisa menyelesaikan panggilan |

## Terkait

- [Injection](https://docs.micromeet.ai/id/extend/injections.md): Skrip di halaman web
- [Connector](https://docs.micromeet.ai/id/extension-sdk/connector.md): Tempat `ctx.browser` paling sering dipakai
