Перейти до вмісту
Навігація Відкрити escЗакрити

Host API

Ця сторінка для тих, кому потрібні точні методи connectHost, обмеження і коди помилок. Про структуру теки, маніфест і встановлення читайте в Зробити розширення.

import { connectHost, HostRequestError } from "@openchamber/sdk";
const host = connectHost();

connectHost кидає HOST_UNAVAILABLE, якщо немає window. Поза OpenChamber він повертає клієнт, у якого кожен виклик відхиляється з HOST_UNAVAILABLE. Викликайте dispose(), коли прибираєте сторінку; незавершені виклики відхиляються з тим самим кодом.

Що OpenChamber вам надсилає

onReady спрацьовує з першим знімком стану і потім щоразу, коли OpenChamber його оновлює.

Це не одноразовий обробник монтування. Змонтуйте інтерфейс і зареєструйте підписки один раз; далі оновлюйте тему та контекст, не замінюючи поля й чернетки. Приклад із захистом від повторного монтування є в UI kit. Слухачі на зразок onConnection одразу передають поточне значення й можуть повторити його з новим знімком. Перед новим запитом порівнюйте поля, від яких залежать дані, й ігноруйте відповіді застарілих запитів.

ПолеЩо це
theme.modelight або dark
theme.tokensкольори застосунку (поверхні, текст, стани взаємодії, primary, success/warning/error/info), font, mono і radius. Передайте це в applyHostReady перед тим, як монтувати UI.
localeтег мови застосунку
directoryтека поточного проєкту або null
session{ id, title, busy } або null. Якщо назви немає, замість неї id сесії. busy - поточний статус. model і agent (агент OpenCode) з’являються, коли вони є в сесії.
surfacepanel на рейці, dialog у вікні прикріплення, page на повному екрані
itemдля чого відкрита ця поверхня, або null: прикріплений елемент, який клацнув користувач (ті самі поля, які ви передали в attach, включно з data), повідомлення (kind: "message") або сесія (kind: "session") з однієї з оголошених вами дій. Див. Дії.
connection{ connected, account } для вашої інтеграції
settingsзначення полів, які ви оголосили в integration.settings

Токени доступу тут ніколи не з’являються, як і в результаті request.

onDirectory, onSession, onSessionLifecycle, onConnection, onSettings і onItem віддають останнє значення, навіть якщо ви підписалися пізно, а далі спрацьовують при кожній зміні.

theme.tokens містить primaryText, successText, warningText, errorText та infoText. Ці обов’язкові поля містять обчислені хостом кольори тексту для нейтрального фону й тонованих елементів UI-набору, а не суцільних кольорових заливок. applyHostReady застосовує їх із кожним знімком стану. CSS-змінні описані в UI-наборі.

Методи

ВикликЩо робить
toast({ kind, message })показати тост у застосунку. kind - info, success або error.
openUrl(url)відкрити URL у браузері користувача
openSurface(surfaceId)перемкнути застосунок на той екран
writeClipboard(text)скопіювати текст. Від 1 до 32000 символів.
compose({ text, mode? })вставити текст у поле чату без надсилання. mode - append (типово) або replace. Від 1 до 16000 символів після обрізання пробілів.
attach({ ... })поставити чип на поле чату, туди ж, куди потрапляють елементи GitHub і Linear. Лише один чип за раз.
startSession({ ... })створити сесію з прикріпленим елементом. Повертає { sessionId, sent }. Потрібен дозвіл sessions.
prompt({ text, send? })написати в поточну сесію або надіслати в ній. Повертає { sent }. Для надсилання потрібен дозвіл prompt.
sessionLink({ ... })прикріпити елемент до поточної сесії, не створюючи нової
close()закрити вікно прикріплення. На рейці нічого не робить.
oauthStart()відкрити сторінку авторизації провайдера або сторінку Linear для інтеграції з host: { provider: "linear" }
oauthDisconnect()забути збережений токен або підключення Linear
request({ method, path, query?, body? })викликати apiOrigin вашої інтеграції з доданим токеном користувача
serviceRequest({ method, path, query?, body? })викликати локальний сервіс вашого розширення (див. GUEST_SERVICES.md)
serviceStatus()stopped, starting, ready або failed
readFile(path)прочитати текстовий файл. Повертає { content }.
writeFile(path, content)атомарно записати текстовий файл, створюючи батьківські теки. Повертає { written: true }.
listDir(path)перелічити теку. Повертає { entries: [{ name, kind }] }, kind - file, directory або other.
stat(path){ kind, size, mtime }, kind - file, directory, other або missing.
generate({ prompt, system?, maxOutputTokens? })одноразовий текст від Small Model користувача. Повертає { text }. Потребує дозволу model.
onResolve(handler)зареєструвати обробник ваших слеш-команд. Він отримує { command, args } і повертає елемент для прикріплення або null.
setBadge(count)показати число (від 0 до 999) на вашій іконці в рейці або null, щоб прибрати його

Усі три приймають однакові поля елемента:

await host.attach({
providerId: "acme-hello",
id: "TICKET-1",
title: "Login is broken",
url: "https://example.com/TICKET-1",
});
await host.attach({
providerId: "acme-hello",
id: "!12",
title: "Fix login",
url: "https://example.com/merge_requests/12",
kind: "pull",
author: "ada",
branches: { head: "feature", base: "main" },
text: "Optional notes for the model",
});
await host.startSession({
providerId: "acme-hello",
id: "!12",
title: "Fix login",
url: "https://example.com/merge_requests/12",
kind: "pull",
worktree: true,
text: "Optional first message",
});

providerId - це id вашої панелі; OpenChamber усе одно перезаписує його вашим id. id - ваш власний ідентифікатор елемента. kind - issue (типово) або pull. text - необов’язковий контекст для моделі, від 1 до 16000 символів після обрізання пробілів. data - необов’язковий JSON на ваш розсуд (статус, коментарі, будь-що), до 16000 символів у серіалізованому вигляді. OpenChamber зберігає його разом із чипом і віддає назад без змін у ctx.item, коли користувач клацає чип; до моделі він ніколи не потрапляє.

startSession приймає projectId без перемикання поточного проєкту. Без worktree сесія створюється в цільовій директорії, true створює worktree з автоматичною назвою, { kind: "existing", directory } обирає наявний, а { kind: "new", name?, baseBranch? } задає назву й базову гілку нового. Назва також використовується для гілки. navigation за замовчуванням дорівнює "preserve"; "open" відкриває новий чат. Модель, агент і варіант першого повідомлення фіксуються на початку створення.

Результат містить sessionId, directory, sent, linked і необов’язковий worktree. sent має значення sent, no-model, skipped або failed; linked: false означає, що прикріплений елемент не зберігся. Якщо після помилки підготовки чи створення сесії залишився worktree, повертається sessionId: null та failure: "bootstrap-failed" | "session-create-failed". Перевірте результат перед повторною спробою. Очікування триває до 180 секунд; таймаут не означає скасування створення.

sessionLink прикріплює елемент до сесії, яка відкрита зараз. Немає проєкту або сесії - помилка NO_SESSION.

Обмеження, які клієнт перевіряє ще до надсилання:

ПолеМакс. символів
id128
title200
url2000
text16000
data (серіалізовано)16000
author80
кожна назва гілки200

prompt і життєвий цикл сесії

await host.prompt({ text: "Fix the login" });
await host.prompt({ text: "Fix the login", send: true });
host.onSessionLifecycle((event) => {
document.body.dataset.phase = event.phase;
});

Без send prompt замінює текст у полі чату. З send: true він надсилає повідомлення тією моделлю і тим агентом, які вибрав користувач. Розширення їх ніколи не вибирає. Немає відкритої сесії - NO_SESSION. Надсилання, поки сесія зайнята, - SESSION_BUSY; писати в поле під час зайнятості можна. Результат - { sent } з тими самими значеннями, що й у startSession.

onSessionLifecycle каже, що зараз робить сесія. phase дорівнює started, поки модель працює, completed, коли вона переходить у стан спокою, і failure при неочікуваному статусі. Пізній підписник одразу отримує поточну фазу.

Проєкти, живі стани та сховище

listProjects(), listWorktrees(projectId) і listSessions(projectId) використовують наявний дозвіл sessions та спільні стори. Запит не запускає новий обхід Git і не відкриває вміст розмов. Проєкти містять ID, назву й директорію, worktree також містять гілку та стан доступності. Сесії містять метадані, часові позначки, батьківську сесію, worktree та прикріплені елементи лише цього розширення. Відомі архівні сесії також включені.

await onProjects(listener), await onWorktrees(projectId, listener) та await onSessions(projectId, listener) повертають функцію відписки. Обробляйте помилки реєстрації. Спочатку надходить знімок стану, потім зміни. До 32 підписок на iframe; dispose(), закриття, пауза, видалення та зміна сервера звільняють їх. state має значення loading, ready або error; для сесій є також coverage за директоріями. Помилка зберігає наявні дані. Лише ready підтверджує повний результат, зокрема порожній список.

activity має значення unknown, idle, running, retrying, waiting-permission або waiting-question. outcome містить спостережене completed, failed або null, лише в пам’яті для щонайбільше 2 000 сесій. Idle після помилки зберігає її до наступного запуску. Це не означає завершення задачі. openSession(sessionId) явно відкриває чат.

host.storage.get(key), set(key, value), delete(key) і keys() зберігають власний JSON на підключеному сервері без додаткового дозволу. Відсутній ключ повертає undefined, збережений null залишається null. Ключі містять від 1 до 128 символів, значення займає до 64 KiB UTF-8, увесь простір до 2 MiB та 2 000 ключів. Для даних проєкту додайте його ID у ключ. Записи послідовні й атомарні; помилка зберігає попередні дані. Видалення розширення видаляє його сховище.

request

const user = await host.request({ method: "GET", path: "/api/v2/user" });

path починається з / і не містить схеми чи хоста; OpenChamber приклеює його до apiOrigin з вашого маніфесту і додає заголовок Authorization. Інтеграції з токеном надсилають вставлений токен як є, як Bearer <token>, якщо в маніфесті token.scheme: "bearer", або як Basic base64(username:token), якщо там "basic". OAuth- і Linear-інтеграції завжди надсилають Bearer. Результат - { status, body }. Тіло приходить текстом; JSON розбирайте самі.

Інтеграція з Linear може також викликати GET /api/linear/issues/get, на який OpenChamber відповідає зі свого маршруту Linear.

Немає відповіді протягом 20 секунд - HOST_TIMEOUT.

try {
await host.request({ method: "GET", path: "/api/v2/user" });
} catch (error) {
if (error instanceof HostRequestError && error.code === "DISCONNECTED") {
await host.oauthStart();
}
}

serviceRequest

Якщо в маніфесті оголошено contributes.service, OpenChamber запускає той процес, а ваша сторінка спілкується з ним через такий самий виклик. Сторінка ніколи не відкриває сокет сама.

const status = await host.serviceStatus();
const result = await host.serviceRequest({ method: "GET", path: "/containers" });

path підкоряється тим самим правилам, що й у request. Оголошення сервісу додає service до дозволів, які користувач підтверджує під час встановлення; до того кожен serviceRequest - NO_SERVICE. Повний контракт: файл пакета GUEST_SERVICES.md.

Файли

Відносний шлях лежить усередині відкритого проєкту і потребує дозволу files. Шлях, що починається з / або ~/, має збігатися з оголошеним шаблоном contributes.filesystem і потребує дозволу filesystem. Правила див. у Зробити розширення.

const { content } = await host.readFile("package.json");
await host.writeFile("notes/today.md", "# Today\n");
const { entries } = await host.listDir("src");
const info = await host.stat("~/.config/opencode/opencode.json");

Обмеження: шлях до 1024 символів, вміст до 2,000,000 символів, список до 2000 записів. Перевищення обмеження вмісту - FILE_TOO_LARGE.

generate

const { text } = await host.generate({ prompt: notes, system: "Summarize in one sentence." });

prompt - від 1 до 64 000 символів після обрізання, system - до 8000, maxOutputTokens - від 1 до 4000. Нічого не потрапляє в сесію, історія не зберігається; розширення ніколи не обирає модель. Виклик чекає до 90 секунд. Відсутність придатного Small Model - це NO_MODEL; помилка моделі - MODEL_FAILED. Див. Генерація тексту.

Коди помилок

Невдалі виклики кидають HostRequestError. code - один із цих; message пояснює докладніше.

КодКоли
HOST_UNAVAILABLEнемає window, сторінка не всередині OpenChamber або вже викликано dispose()
HOST_TIMEOUTнемає відповіді 20 секунд
HOST_REJECTEDOpenChamber відмовив або відповів кодом, якого цей SDK не знає
DISCONNECTEDдля вашої інтеграції немає токена або підключення Linear
BAD_PATHpath некоректний або намагався вийти за дозволене джерело
NO_INTEGRATIONу маніфесті немає integration
NO_SESSIONprompt або sessionLink без відкритої сесії
SESSION_BUSYprompt({ send: true }), поки сесія була зайнята
DISABLEDкористувач призупинив розширення в Налаштування → Розширення
NO_SERVICEсервіс не оголошено, не підтверджено або не запущено
NOT_GRANTEDкористувач не підтвердив дозвіл, потрібний для цього виклику
SERVICE_FAILEDсервіс упав або так і не став готовим
NO_DIRECTORYвідносний шлях до файлу використано без відкритого проєкту
NOT_FOUNDreadFile файлу, якого не існує
FILE_TOO_LARGEвміст файлу перевищує обмеження, під час читання або запису
DENIEDопераційна система відмовила у файловій операції
NO_MODELgenerate без доступного Small Model
MODEL_FAILEDSmall Model повернула помилку

Дивіться також