Tworzenie rozszerzenia
Użyj @openchamber/sdk, aby dodać własny panel do OpenChamber. Rozszerzenie to mała strona internetowa, którą OpenChamber pokazuje na prawym pasku bocznym. Komunikuje się z aplikacją przez connectHost(): może odczytać bieżący projekt i sesję, pokazywać toasty, wstawiać tekst do pola czatu, dołączać zadanie do sesji, a za zgodą użytkownika także uruchamiać sesje i wysyłać prompty.
Rozszerzenia działają w OpenChamber web i desktop. VS Code i wersja mobilna jeszcze ich nie ładują.
Czym jest rozszerzenie
Folder z trzema plikami:
package.jsonz blokiemopenchamber(manifest)panel/index.html, strona, którą pokazuje OpenChamberpanel/main.js, Twój skrypt, zbudowany do jednego klasycznego pliku (IIFE, nie moduł ES, bo strona ładuje się w iframe z sandboxem)
OpenChamber nigdy nie kompiluje Twojego kodu. Dostarczaj gotowy, zbudowany plik .js. SDK zawiera polecenie do bundlowania; zadziała też każdy inny bundler, który wypuszcza IIFE.
npm install @openchamber/sdkbunx openchamber-guest-bundle panel/main.ts panel/main.jsPolecenie bundlowania działa na Bun. Bez Buna użyj esbuild albo dowolnego innego bundlera z --format=iife --platform=browser.
Kompletne, działające rozszerzenie, wszystkie trzy pliki, znajdziesz na stronie Przykład. Skopiuj je i zmień API oraz listę.
Wskaż main.js w index.html zwykłym <script src="main.js"></script>.
Instalacja w trakcie pracy
- Uruchom OpenChamber w wersji web lub desktop.
- Otwórz Ustawienia → Rozszerzenia.
- Wklej bezwzględną ścieżkę do swojego folderu i kliknij Dodaj.
OpenChamber odczytuje manifest i pokazuje okno z listą tego, o co rozszerzenie prosi (zobacz Uprawnienia). Zatwierdź, a Twoja ikona pojawi się na pasku bocznym. Kliknij ją i Twoja strona się załaduje.
Możesz też dodać lokalny plik .zip albo link https do repozytorium git lub pliku zip. Tylko instalacja z git może się aktualizować: podnieś version w package.json, wypchnij zmiany, a OpenChamber sprawdzi to wkrótce po otwarciu i co kilka godzin, powiadomi użytkownika raz i zaproponuje aktualizację w Ustawienia → Rozszerzenia. Link do openchamber://extensions/install?url=<encoded repository URL> na dowolnej stronie otwiera aplikację desktopową z tym repozytorium w oknie dodawania, więc możesz zaoferować przycisk instalacji. #tag albo #branch w adresie URL przypina, co jest śledzone. Te są kopiowane do folderu danych OpenChamber i uruchamiane z tej kopii, więc dostarczaj zbudowane pliki, a nie node_modules ani źródła TypeScript. Usunięcie rozszerzenia usuwa kopię. Instalacja z folderu działa bezpośrednio z Twojego folderu, więc możesz edytować, przebudowywać i przeładowywać.
Manifest
{ "name": "@acme/hello", "version": "1.0.0", "openchamber": { "apiVersion": 1, "engines": { "openchamber": ">=1.24.0" }, "id": "acme-hello", "name": "Hello", "icon": "window", "contributes": { "panel": { "entry": "panel/index.html" }, "attach": "dialog", "page": true, "capabilities": ["prompt", "sessions"], "integration": { "name": "Acme", "description": "Tasks from Acme", "token": { "apiOrigin": "https://api.acme.example", "account": { "path": "/me", "name": "login" }, "scheme": "bearer" }, "settings": [{ "id": "list-id", "label": "List ID" }] } } }}version jest wymagane i musi być zgodne z semver. Ustawienia → Rozszerzenia pokazuje je na karcie.
apiVersion to 1. OpenChamber odrzuca każdą inną wartość.
engines.openchamber jest opcjonalne. Podaj najstarszą wersję OpenChamber, z którą działa Twoje rozszerzenie, jako 1.24.0 lub >=1.24.0. Starsze wersje odmówią instalacji zamiast zawieść później.
id, name i icon identyfikują rozszerzenie. id jest w kebab-case i musi być unikalne wśród zainstalowanych rozszerzeń. icon to nazwa ikony Remixicon (RiWindowLine staje się window) albo plik SVG w Twoim folderze, na przykład icon.svg. contributes.panel to panel rozszerzenia na pasku bocznym: jego entry to plik HTML w Twoim folderze. Rozszerzenie bez panelu na pasku bocznym pomija panel. Starsze pakiety trzymają id, name i icon w panel i to nadal działa.
contributes.attach jest opcjonalne. Dodaje Twoje rozszerzenie do menu + obok pola czatu, żeby użytkownik mógł wybrać zadanie i dołączyć je do sesji.
"dialog"otwiera Twoją stronę w oknietruelub"panel"otwiera zamiast tego panel na pasku bocznym{ "mode": "dialog", "entry": "panel/attach.html" }otwiera w oknie osobną Twoją stronę, żeby wybór nie musiał dzielić kodu z panelem na pasku bocznym- bez tego klucza rozszerzenie nie pojawia się w tym menu
ctx.surface ma wartość panel, dialog, page, status lub block. Kliknięcie dołączonego elementu przekazuje jego dane w ctx.item; w pozostałych przypadkach jest to null.
contributes.page: true udostępnia panel na pełnym ekranie w menu Strony rozszerzeń nad listą sesji. { "entry": "panel/page.html", "title": "Board" } wskazuje osobny HTML i opcjonalny tytuł. panel.entry jest wymagane, a izolacja i uprawnienia pozostają te same. Stronę otwiera tylko użytkownik. Przeładowanie, wstrzymanie, usunięcie lub zmiana serwera zamyka ją.
Tablica może używać host.storage oraz list projektów, worktree i sesji ze stanami na żywo. startSession może wskazać inny projekt bez zamykania tablicy. Zobacz Host API.
contributes.integration jest opcjonalne. Dodaje kartę w Ustawienia → Integracje, gdzie użytkownik podłącza konto. Zobacz Konta i sieć.
contributes.service jest opcjonalne. Deklaruje usługę lokalną (proces, który OpenChamber uruchamia obok rozszerzenia) do rzeczy, do których strona internetowa nie ma dostępu, na przykład socketu Dockera. Ten proces działa z pełnym dostępem użytkownika i bez piaskownicy, dlatego okno zatwierdzania o nim ostrzega; deklaruj taką usługę tylko wtedy, gdy strona nie jest w stanie wykonać tego zadania. Zobacz plik GUEST_SERVICES.md w pakiecie. Usługa z provides: ["browser"] może też zastąpić przeglądarkę agenta: odpowiada na działania browser.* na serwerze, więc agenci przeglądają bez otwartej aplikacji desktopowej, a użytkownik wybiera ją w Ustawienia → Narzędzia OpenChamber. Taka usługa nie potrzebuje panelu. Usługa może też dowiadywać się, co dzieje się w sesjach użytkownika: zadeklaruj obok niej contributes.sessionEvents, a OpenChamber wysyła do usługi każdą turę, która się kończy albo kończy się błędem, oraz każde uprawnienie i pytanie, o które prosi agent, ze wszystkich projektów i bez otwartego okna. Zobacz Zdarzenia sesji. A usługa z provides: ["notifications"] dostarcza powiadomienia użytkownika obok komputera i telefonu: na Slack, Matrix albo na własny serwer; użytkownik podłącza konto w Ustawienia → Integracje, a token zostaje w OpenChamber. Zobacz Kanały powiadomień. Usługa z provides: ["tracker"] jest trackerem elementów pracy obok Linear, z kartą konta pod Kontami rozszerzeń; zobacz Trackery. A usługa z provides: ["quota"] raportuje zużycie obok wbudowanych dostawców, odczytując dane logowania, które OpenCode przechowuje dla dostawców wymienionych w manifeście; zobacz Dostawcy zużycia.
Uprawnienia
Rysowanie panelu i odczyt bieżącej sesji nie wymagają żadnej zgody. Wszystko, co działa w imieniu użytkownika, już tak. Wymień te rzeczy w contributes.capabilities:
| Uprawnienie | Na co pozwala |
|---|---|
prompt | wysyłanie wiadomości do sesji użytkownika (prompt({ send: true }), startSession z text) |
sessions | listy projektów, worktree i stanów sesji; tworzenie i otwieranie sesji w zarejestrowanych projektach |
files | odczyt i zapis plików w otwartym projekcie (readFile, writeFile, listDir, stat ze ścieżką względną) |
model | jednorazowe generowanie tekstu za pomocą Small Model użytkownika (generate), poza jakąkolwiek sesją |
Jedenaście kolejnych jest dodawanych automatycznie: network, gdy deklarujesz integration, service, gdy deklarujesz service, filesystem, gdy deklarujesz wzorce filesystem, origins, gdy deklarujesz origins, conversation, gdy akcja sesji prosi o wiadomości (zobacz Akcje, polecenia i odznaka), transcript, gdy sessionEvents lub sessionControl prosi o content (zobacz Zdarzenia sesji), sessionControl, gdy Twoja usługa działa na sesjach (zobacz Wywoływanie OpenChamber), sessionAnswers, gdy odpowiada w imieniu użytkownika na żądania uprawnień i formularze agenta (sessionControl: { answers: true }), instructions, gdy uzupełnia to, co czytają agenci, oraz skills, gdy deklarujesz contributes.skills, folder pakietu ze skillami, które docierają do każdego agenta, dopóki rozszerzenie jest włączone, oraz opencodePlugin, gdy deklarujesz contributes.opencodePlugin (patrz niżej).
Użytkownik widzi pełną listę raz, przy instalacji rozszerzenia, i albo ją zatwierdza, albo usuwa rozszerzenie. Jeśli nowa wersja prosi o więcej, okno pojawia się ponownie. Wywołanie, które wymaga niezatwierdzonego uprawnienia, kończy się błędem NOT_GRANTED.
Plugin OpenCode w Twoim pakiecie
Rozszerzenie może dostarczać plugin OpenCode, więc ludzie instalują jedną rzecz zamiast dwóch. Zadeklaruj "opencodePlugin": "plugin", folder w pakiecie z index.js lub server.js w katalogu głównym. OpenCode nie czyta main, więc dla pliku wejściowego w innym miejscu dodaj do package.json tego folderu name i exports. Plik wejściowy eksportuje domyślnie { id, setup }, kształt, który ładuje OpenCode; niczego nie importuje, bo OpenCode ładuje go zwykłym import() z Twojego folderu, gdzie nic nie jest zainstalowane. input narzędzia to zwykły JSON Schema, a jego execute odpowiada { content }.
{ "contributes": { "panel": { "id": "teapot-tools", "name": "Teapot Tools", "icon": "cup" }, "opencodePlugin": "plugin" }}Okno zatwierdzania dostaje jeszcze jeden wiersz, Dodawanie pluginu do OpenCode, który mówi wprost, że kod pluginu działa wewnątrz OpenCode z pełnymi uprawnieniami użytkownika. Po zezwoleniu OpenChamber wpisuje Twój folder do konfiguracji OpenCode, którą zarządza, tego samego pliku, który niesie contributes.skills; OpenCode uruchomiony przez OpenChamber obserwuje ten plik i ładuje plugin w ciągu kilku sekund. Wyłączenie lub usunięcie rozszerzenia zabiera go tą samą drogą. Do własnego opencode.json użytkownika nic nie jest zapisywane: ten plik należy do użytkownika, a plugin w nim przeżyłby Twoje rozszerzenie i działał w każdym OpenCode na komputerze.
Tam, gdzie ten plik nie dociera do OpenCode, ekran rozszerzenia mówi o tym i oferuje Dodaj do globalnej konfiguracji, co zapisuje Twój folder do globalnej konfiguracji OpenCode użytkownika jako wpis, który od tej chwili należy do niego. Dzieje się tak, gdy OpenChamber nie uruchomił OpenCode, z którym rozmawia, albo gdy środowisko serwera ustawia własne OPENCODE_CONFIG (plugin ładuje się wtedy przy następnym uruchomieniu OpenCode). OpenCode na innym komputerze nie może załadować folderu z tego komputera; ekran mówi o tym i niczego nie oferuje. Plugin, który się nie ładuje, OpenCode zgłasza w Ustawieniach → Pluginy, gdy ma tam wpis; Twoje rozszerzenie działa dalej w każdym przypadku. Instalacja odrzuca pakiet, którego folder pluginu nie istnieje albo którego package.json nie wskazuje pliku wejściowego.
VS Code i aplikacje mobilne nie ładują rozszerzeń, więc żaden plugin tam nie trafia. Sesja w izolowanej przestrzeni uruchamia własne OpenCode, które nigdy nie czyta konfiguracji hosta. Tryb enterprise traktuje plugin jak lokalną usługę: instaluje się tylko z repozytorium wskazanego przez administratora. SDK ma działający przykład, examples/opencode-plugin.
Generowanie tekstu
Dzięki uprawnieniu model host.generate prosi Small Model użytkownika o jednorazową odpowiedź: podsumowanie, tytuł, szkic. Nic nie trafia do sesji, historia nie jest zapisywana, a rozszerzenie nigdy nie wybiera dostawcy. OpenChamber używa tego samego modelu, którego używa do własnej pracy w tle, wybranego w Ustawienia → Sesje → Small Model, albo małego modelu dostawcy, z którym użytkownik akurat pracuje, albo domyślnego modelu z jego konfiguracji OpenCode.
const { text } = await host.generate({ prompt: task.description, system: "Write a one-line summary. Return only the summary.", maxOutputTokens: 200,});Gdy żaden model nie jest dostępny, wywołanie kończy się błędem NO_MODEL; model, który zwrócił błąd, to MODEL_FAILED. Prompty są ograniczone do 64 000 znaków, a wywołanie czeka maksymalnie 90 sekund. Użytkownik widzi to uprawnienie jako „Użyj swojego Small Model” i kosztuje go to tokeny, więc trzymaj prompty krótkie i wywołuj to przy kliknięciu, a nie przy każdym naciśnięciu klawisza.
Konta i sieć
Twoja strona działa w sandboxie i nie może sama łączyć się z siecią: skrypty, style, obrazy, czcionki i multimedia ładują się tylko z Twojego pakietu, a fetch sięga tylko do plików Twojego pakietu. Dołącz czcionki i obrazy do pakietu, zamiast ładować je z CDN.
Aby rozmawiać z zewnętrzną usługą, zadeklaruj integration, a OpenChamber wykona wywołania za Ciebie przez host.request, dołączając token użytkownika. Token nigdy nie trafia do Twojej strony.
Dla wszystkiego innego, czego Twoja strona potrzebuje bezpośrednio, np. czcionek lub obrazów z CDN albo publicznego API, wymień do 8 originów https:
"origins": ["https://fonts.example.com"]Użytkownik widzi je w oknie zatwierdzania jako adresy, do których Twoje rozszerzenie może wysyłać dane i z których może je odbierać, i zatwierdza je ponownie, gdy aktualizacja doda nowy. Zatwierdzone originy są otwarte dla fetch, obrazów, czcionek, stylów i multimediów, ale nigdy dla skryptów: kod musi być dostarczony w Twoim pakiecie. Origin strony to null, więc aby odczytać odpowiedź fetch, serwer musi zezwolić na CORS dla tego originu.
W trybie enterprise rozszerzenie z origins, integration albo service instaluje się tylko z repozytorium Git, które administrator umieścił na liście. Jeśli Twoje rozszerzenie może działać bez nich, takie zespoły nadal mogą je zainstalować. Aby tworzyć rozszerzenie na takiej maszynie, poproś administratora o allowLocalExtensions, dzięki czemu zainstalujesz je z jego folderu.
token: użytkownik wkleja token API na karcie w Ustawienia → Integracje.apiOriginto jedyny origin, któryrequestmoże wywołać.accountjest opcjonalne: ścieżka GET i nazwa pola, żeby karta mogła pokazać, kto jest podłączony.schemeokreśla, jak token jest wysyłany; zobacz tabelę poniżej. Sprawdź w dokumentacji API dostawcy, jakiego nagłówka oczekuje.oauth: użytkownik wkleja client id, a OpenChamber przeprowadza proces autoryzacji. WymagaauthorizeUrl,tokenUrliapiOrigin.host: { "provider": "linear" }: używa konta Linear już podłączonego w OpenChamber. Client id nie jest potrzebne.
Co wysyła każdy scheme:
scheme | Nagłówek wysyłany przez OpenChamber | Kiedy używać |
|---|---|---|
| pominięty | Authorization: <token> | API dokumentuje goły token w nagłówku |
"bearer" | Authorization: Bearer <token> | API dokumentuje token bearer lub personal access token |
"basic" | Authorization: Basic base64(username:token) | API dokumentuje HTTP Basic auth z nazwą użytkownika (często e-mail) i tokenem API; karta prosi o oba, a usernameLabel nazywa pierwsze pole |
settings dodaje do karty zwykłe pola tekstowe. Ich wartości trafiają do ctx.settings. Pole z "secret": true jest maskowane na karcie i nigdy nie trafia na stronę; Twoja usługa odczytuje je przez readSettings().
Pliki
Twoja strona nie może sama dotykać dysku. OpenChamber czyta i zapisuje za nią, w granicach zatwierdzonych przez użytkownika.
- Ścieżka względna (
README.md,src/index.ts,.) oznacza otwarty projekt. Wymaga uprawnieniafiles. Brak otwartego projektu toNO_DIRECTORY. - Ścieżka zaczynająca się od
/lub~/oznacza dowolne inne miejsce. Musi pasować do jednego ze wzorców zadeklarowanych wcontributes.filesystem, a użytkownik widzi dokładnie te wzorce w oknie zatwierdzania:
"filesystem": ["~/.config/opencode/opencode.json", "~/notes/**"]Wzorce używają * dla jednego segmentu ścieżki i ** dla dowolnej głębokości. Wszystko poza nimi to BAD_PATH, nawet po zatwierdzeniu. .. nigdy nie jest dozwolone.
const config = await host.readFile("~/.config/opencode/opencode.json");await host.writeFile("~/.config/opencode/opencode.json", nextJson);const { entries } = await host.listDir(".");Zapisy są atomowe: OpenChamber zapisuje plik tymczasowy i zmienia jego nazwę, więc czytający nigdy nie zobaczy pliku zapisanego do połowy. Pliki są czytane i zapisywane jako tekst UTF-8, do 2 MB.
Sekcja w Work Status
contributes.statusSection dodaje twoją własną sekcję do panelu Work Status obok czatu. Użytkownicy włączają ją, wyłączają i przesuwają na liście sekcji tego panelu, tak jak wbudowane sekcje.
"statusSection": { "entry": "status/index.html", "title": "Recent commits", "height": 160 }trueużywapanel.entry.entryto osobna strona HTML w twoim folderze. Z nią nie potrzebujeszpanel.entry, więc rozszerzenie może być tylko tą sekcją, bez ikony na pasku bocznym.titlezastępuje nazwę panelu w nagłówku sekcji, do 60 znaków.heightto początkowa wysokość w pikselach, od 24 do 320 (domyślnie 120). Wywołajhost.setHeight(), gdy zmieni się rozmiar treści.
Twoja strona widzi ctx.surface jako status. OpenChamber ładuje ją tylko wtedy, gdy panel Work Status jest widoczny, a twoja sekcja rozwinięta, i zwalnia ją, gdy użytkownik zwinie sekcję, więc nie trzymaj tam niczego, czego nie da się odtworzyć. Rozszerzenie z samą sekcją może używać uprawnień, usługi, integracji i plików, ale nie page, attach, actions ani commands. Tylko web i desktop. Przykład git-graph-status to kompletna sekcja.
Edytor dla twojego typu pliku
contributes.fileEditors otwiera pliki w twojej stronie zamiast w edytorze tekstu. Gdy użytkownik otworzy pasujący plik w Files, OpenChamber pokaże tam twój edytor, z tym samym przełącznikiem źródła, przyciskiem pełnego ekranu i przyciskiem zapisu, co wbudowane podglądy.
"fileEditors": [ { "id": "canvas", "title": "Excalidraw", "match": ["*.excalidraw", "*.excalidraw.md"], "entry": "editor/index.html" }]matchporównuje tylko nazwy plików, bez rozróżniania wielkości liter.*pasuje do dowolnego ciągu znaków, a?do jednego znaku. Wzorzec nie może zawierać/ani składać się wyłącznie z symboli wieloznacznych.entryto strona HTML w twoim folderze. Nie potrzebujeszpanel.entry, więc rozszerzenie może być tylko edytorem, bez ikony na pasku bocznym.contentto"text"(domyślnie) albo"binary", patrz niżej.- Do 8 edytorów na rozszerzenie, każdy z maksymalnie 16 wzorcami.
Plikiem zarządza OpenChamber. Twoja strona nigdy go nie czyta ani nie zapisuje i nie potrzebuje do tego żadnego uprawnienia. OpenChamber przekazuje ci tekst, prosi o zmieniony tekst przy zapisie i obsługuje Cmd/Ctrl+S, automatyczny zapis, pytanie o niezapisane zmiany, końce linii oraz zmiany wprowadzone na dysku:
import { connectHost, createFileSaveTracker } from "@openchamber/sdk";
const host = connectHost();let tracker = createFileSaveTracker(null);
host.onFileOpen((file) => { if (file.encoding !== "text") return; // edytor tekstu zawsze dostaje tekst load(file.content); // a także file.path, file.name, file.readOnly tracker = createFileSaveTracker(versionOf(currentText()));});
// Wywołuj to po każdej zmianie. `edited` wstrzymuje automatyczny zapis, dopóki użytkownik nie przestanie.const changed = () => host.reportFileChange(tracker.observe(versionOf(currentText())));
host.onFileSnapshot(() => ({ content: currentText(), version: versionOf(currentText()) }));
// Zmiany wprowadzone w trakcie zapisu przez OpenChamber zostawiają plik niezapisany.host.onFileSaved((version) => host.reportFileChange({ dirty: tracker.markSaved(version), edited: false }));version to dowolny krótki ciąg znaków, który zmienia się razem z dokumentem, na przykład hash. Jeśli plik nie nadaje się do otwarcia w twoim edytorze, wywołaj host.reportFileUnsupported(), a OpenChamber pokaże jego źródło z komunikatem. Pliki większe niż 20 MB zostają w widoku źródła.
Pliki binarne
Ustaw "content": "binary", żeby otwierać pliki, które nie są tekstem, na przykład arkusze kalkulacyjne, dokumenty czy obrazy. Twój edytor dostaje bajty pliku i oddaje bajty; cała reszta działa tak samo.
host.onFileOpen((file) => { if (file.encoding !== "binary") return; open(file.bytes); // Uint8Array, dokładnie taki jak na dysku});
host.onFileSnapshot(() => ({ bytes: currentBytes(), version: versionOf(currentBytes()) }));Edytor binarny może przejąć każdy plik pasujący do jego wzorców, także te, które OpenChamber sam pokazuje w podglądzie, jak obrazy i pliki PDF. Nie ma widoku źródła, a gdy użytkownik przełącza go na pełny ekran lub z niego wychodzi, mając niezapisane zmiany, OpenChamber najpierw je zapisuje. Odczyt i zapis formatu należy do twojego edytora: OpenChamber zapisuje dokładnie te bajty, które zwrócisz.
Twoja strona widzi ctx.surface jako file. OpenChamber przeładowuje ją i ponownie przekazuje plik, gdy plik zmieni się na dysku, użytkownik odrzuci zmiany albo edytor przejdzie na pełny ekran lub z niego wyjdzie. Wygrywa pierwsze włączone rozszerzenie z pasującym wzorcem, przed własnymi podglądami OpenChamber. Rozszerzenie z samym edytorem może używać uprawnień, usługi, integracji i plików, ale nie page, attach, actions ani commands. Tylko web i desktop. Przykład checklist-editor to kompletny edytor.
Akcje, polecenia i odznaka
Poza paskiem bocznym i menu + rozszerzenie może pojawić się w trzech kolejnych miejscach. Wszystkie przekazują rozszerzeniu item tak samo jak kliknięcie chipa.
Akcje na wiadomościach i sesjach. Zadeklaruj pozycje menu, a OpenChamber pokaże je obok wbudowanych:
"actions": [ { "id": "create-task", "label": "Create task", "icon": "add-line", "where": "message", "roles": ["assistant"] }, { "id": "summarize", "label": "Summarize session", "where": "session", "payload": ["messages"] }]where przyjmuje też selection (tekst zaznaczony w wiadomości), file (wiersz w Files) i diff (zaznaczone wiersze pliku lub diffa). Ich elementy mają kind o tej samej nazwie: selection zawiera tekst, file ścieżkę, diff ścieżkę i zakres wierszy. Przy zaznaczeniu display określa, jak wygląda akcja: label (domyślnie), icon z etykietą w podpowiedzi albo both.
Akcja na wiadomości otwiera Twoje rozszerzenie z ctx.item o kind: "message": id i tytuł sesji, folder projektu, id wiadomości, jej rola i tekst. Akcja na sesji otwiera je z kind: "session" oraz id, tytułem i folderem sesji. Dodaj "payload": ["messages"], a element niesie też całą rozmowę, od najstarszej wiadomości, ten sam tekst, który daje eksport do Markdown. To wymaga uprawnienia conversation, które użytkownik widzi jako osobny wiersz w oknie zgody. item.action mówi, którą pozycję kliknięto.
Polecenia z ukośnikiem, które dołączają. Zadeklaruj polecenie i obsłuż je na stronie:
"commands": [{ "name": "task", "description": "Attach a task by id" }]host.onResolve(async ({ command, args }) => { const task = await findTask(args.trim()); return task ? { providerId: "acme-tasks", id: task.id, title: task.title, url: task.url } : null;});Gdy użytkownik wpisze /task ABC-12 w polu czatu, OpenChamber prosi Twoje rozszerzenie o rozwiązanie go i dołącza to, co zwrócisz, jako chip, niczego nie otwierając. Zwróć null dla “nic nie znaleziono”. Jeśli Twój panel jest zamknięty, OpenChamber ładuje go w tle na potrzeby tego wywołania. Nazwa, której OpenChamber lub OpenCode już używa, jest ignorowana.
Odznaka na ikonie na pasku bocznym. host.setBadge(3) stawia kropkę na Twojej ikonie, a liczbę pokazuje w jej podpowiedzi, na przykład otwarte zadania; host.setBadge(null) ją usuwa. Otwarcie panelu też ją usuwa.
Twoje narzędzia w czacie
Gdy Twoja wtyczka OpenCode lub serwer MCP dodaje narzędzie, czat pokazuje jego wywołania z ogólną ikoną i surowym wynikiem. Zamiast tego zadeklaruj, jak mają wyglądać, bez kodu:
"tools": [ { "match": "mcp.jira.*", "name": "Jira", "icon": "task-line", "title": "{input.key}", "subtitle": "{output.status}", "output": "table", "columns": ["key", "summary", "status"] }]match to nazwa narzędzia w takiej postaci, w jakiej zgłasza ją OpenCode; * na końcu dopasowuje prefiks. icon to nazwa ikony Remixicon albo plik .svg w Twoim pakiecie, tak jak panel.icon. title i subtitle to szablony nad input, output i metadata wywołania. output wybiera sposób renderowania treści: text, json (drzewo), markdown, code z language, table z columns (wiersze pochodzą z tablicy wyniku lub z output.items) albo auto dla domyślnego. Dokładny match wygrywa z symbolem wieloznacznym, a przy remisie wygrywa pierwsze zainstalowane rozszerzenie. Żadna zgoda nie jest potrzebna: to zmienia tylko sposób rysowania danych, które już są w czacie.
Rozszerzenie, które tylko stylizuje narzędzia, nie potrzebuje żadnej strony: pomiń panel.entry, a nie będzie miało ikony na pasku bocznym, tylko kartę w Ustawienia → Rozszerzenia. Bez strony może deklarować tools i nic więcej. Drugi wyjątek to sekcja Work Status lub edytor plików z własnym entry, patrz wyżej.
Pierwsze linie w panelu
import { connectHost } from "@openchamber/sdk";
const host = connectHost();
host.onReady((ctx) => { document.body.dataset.theme = ctx.theme.mode; document.body.dataset.surface = ctx.surface;});connectHost działa tylko wewnątrz OpenChamber. Po otwarciu jako zwykły plik każde wywołanie jest odrzucane z HOST_UNAVAILABLE.
Najpierw zbuduj panel z gotowych elementów @openchamber/sdk/ui: przycisków, pól, list rozwijanych, zakładek, list i innych, ostylowanych kolorami i czcionkami aplikacji, żeby panel wyglądał jak część OpenChamber. Własny HTML i CSS pisz tylko dla tego, czego w zestawie brakuje. Zobacz Zestaw UI.
Zobacz też
- Rozszerzenia o instalacji przez SSH i wyborze tożsamości Git serwera
- API hosta z każdą metodą
connectHost, jej limitami i kodami błędów - Zestaw UI z gotowymi elementami
- Przykład z kompletnym rozszerzeniem w trzech plikach do skopiowania