컨텐츠로 건너뛰기
이동 열기 esc닫기

Host API

connectHost의 정확한 메서드, 제한, 오류 코드가 필요할 때 이 페이지를 사용하세요. 폴더 구조, 매니페스트, 설치는 확장 만들기에서 시작하세요.

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

connectHostwindow가 없으면 HOST_UNAVAILABLE을 throw합니다. OpenChamber 밖에서는 모든 호출이 HOST_UNAVAILABLE로 reject되는 클라이언트를 반환합니다. 페이지를 정리할 때는 dispose()를 호출하세요. 진행 중인 호출은 같은 코드로 reject됩니다.

OpenChamber가 보내주는 것

onReady는 첫 스냅샷과 함께 실행되고, OpenChamber가 스냅샷을 새로 고칠 때마다 다시 실행됩니다.

한 번만 실행되는 마운트 콜백이 아닙니다. UI 마운트와 구독 등록은 한 번만 하고, 이후에는 입력과 초안을 교체하지 않고 테마와 컨텍스트를 갱신하세요. UI kit의 가드 예제를 참고하세요. onConnection 같은 리스너는 현재 값을 즉시 전달하며 새 스냅샷에서 같은 값을 다시 전달할 수 있습니다. 새 요청 전에 필요한 필드를 비교하고, 이전 요청의 오래된 응답은 무시하세요.

필드내용
theme.modelight 또는 dark
theme.tokens앱의 색상(표면, 텍스트, 상호작용 상태, primary, success/warning/error/info), font, mono, radius. UI를 마운트하기 전에 applyHostReady에 넘기세요.
locale앱 언어 태그
directory현재 프로젝트 디렉터리, 또는 null
session{ id, title, busy } 또는 null. 제목이 없으면 세션 id를 사용합니다. busy는 실시간 상태입니다. 세션에 있으면 modelagent(OpenCode 에이전트)도 포함됩니다.
surface레일은 panel, 첨부 창은 dialog, 전체 화면은 page
item이 화면이 무엇 때문에 열렸는지, 또는 null. 사용자가 클릭한 첨부 항목(attach에 넘긴 것과 같은 필드, data 포함), 또는 선언한 작업에서 온 메시지(kind: "message")나 세션(kind: "session"). 작업 참고.
connection여러분의 통합에 대한 { connected, account }
settingsintegration.settings에 선언한 입력란의 값

액세스 토큰은 여기에도, request 결과에도 절대 나타나지 않습니다.

onDirectory, onSession, onSessionLifecycle, onConnection, onSettings, onItem은 늦게 구독해도 최신 값을 다시 보내주고, 이후 값이 바뀔 때마다 계속 실행됩니다.

theme.tokens에는 primaryText, successText, warningText, errorText, infoText가 포함됩니다. 이 필수 필드에는 호스트가 계산한 텍스트 색상이 들어 있습니다. 중립적인 배경과 UI 키트의 옅은 색상 컨트롤용이며, 진한 단색 배경용은 아닙니다. applyHostReady가 매 스냅샷에 이 값을 적용합니다. CSS 변수는 UI 키트를 참고하세요.

메서드

호출동작
toast({ kind, message })앱에 토스트를 표시합니다. kindinfo, success, error 중 하나입니다.
openUrl(url)사용자의 브라우저에서 URL 열기
openSurface(surfaceId)앱을 해당 화면으로 전환
writeClipboard(text)텍스트를 복사합니다. 1자에서 32000자.
compose({ text, mode? })보내지 않고 채팅창에 텍스트를 넣습니다. modeappend(기본값) 또는 replace. 트림 후 1자에서 16000자.
attach({ ... })채팅창에 칩을 붙입니다. GitHub와 Linear 항목이 놓이는 곳과 같은 위치입니다. 칩은 한 번에 하나만.
startSession({ ... })해당 항목을 첨부한 세션을 만듭니다. { sessionId, sent }를 반환합니다. sessions 권한이 필요합니다.
prompt({ text, send? })현재 세션에 쓰거나 보냅니다. { sent }를 반환합니다. 보내려면 prompt 권한이 필요합니다.
sessionLink({ ... })세션을 새로 만들지 않고 현재 세션에 항목 첨부
close()첨부 창을 닫습니다. 레일에서는 아무것도 하지 않습니다.
oauthStart()제공자의 인증 페이지를 엽니다. host: { provider: "linear" } 통합이면 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 }] }를 반환하며, kindfile, directory, other 중 하나.
stat(path){ kind, size, mtime }. kindfile, 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는 항목에 대한 여러분 자신의 식별자입니다. kindissue(기본값) 또는 pull입니다. text는 모델에 주는 선택적 컨텍스트로, 트림 후 1자에서 16000자입니다. data는 여러분 마음대로 쓰는 선택적 JSON으로(상태, 댓글, 무엇이든), 직렬화했을 때 16000자까지입니다. OpenChamber는 이를 칩과 함께 저장하고, 사용자가 칩을 클릭하면 ctx.item으로 그대로 돌려줍니다. 모델에는 절대 전달되지 않습니다.

startSessionprojectId로 현재 화면을 전환하지 않고 대상 프로젝트를 지정합니다. worktree 생략은 대상 디렉터리, true는 자동 이름의 새 worktree, { kind: "existing", directory }는 기존 worktree, { kind: "new", name?, baseBranch? }는 이름과 기준 브랜치를 지정하는 새 worktree입니다. 이름은 브랜치에도 사용됩니다. navigation 기본값은 "preserve"이며 "open"은 새 채팅을 엽니다. 첫 메시지의 모델, 에이전트, 변형은 시작 시 확정됩니다.

결과는 sessionId, directory, sent, linked와 선택적 worktree입니다. sentsent, no-model, skipped, failed이며 linked: false는 항목 저장 실패입니다. 준비 또는 세션 생성 실패 후 worktree가 남으면 sessionId: nullfailure: "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입니다. 늦게 등록한 리스너도 현재 phase를 바로 받습니다.

프로젝트, 실시간 상태와 저장

listProjects(), listWorktrees(projectId), listSessions(projectId)는 기존 sessions 권한으로 공유 스토어를 읽습니다. 호출마다 Git을 조회하거나 대화 내용을 공개하지 않습니다. 프로젝트 ID, 이름, 디렉터리와 worktree의 브랜치, 사용 가능 상태를 제공합니다. 세션에는 날짜, 부모 ID, worktree, 해당 확장 프로그램의 첨부 항목이 포함됩니다. 알려진 보관 세션도 포함됩니다.

await onProjects(listener), await onWorktrees(projectId, listener), await onSessions(projectId, listener)는 구독 해제 함수를 반환합니다. 등록 오류를 처리하세요. 초기 스냅샷 다음에 변경 사항이 전달됩니다. 프레임당 최대32개이며 dispose(), 닫기, 일시 중지, 제거, 서버 전환 시 해제됩니다. stateloading, ready, error이고 세션에는 디렉터리별 coverage가 있습니다. 오류 시 데이터를 유지하며 완전한 빈 목록은 ready만 보장합니다.

activityunknown, 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자, 값은 UTF-8 기준64 KiB, 전체는2 MiB와2,000키까지입니다. 프로젝트별 데이터는 키에 프로젝트 ID를 넣으세요. 쓰기는 직렬화되고 원자적으로 처리되며 실패 시 기존 데이터를 유지합니다. 확장 프로그램 제거 시 삭제됩니다.

request

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

path/로 시작하고 스킴이나 호스트를 포함하지 않습니다. OpenChamber가 매니페스트의 apiOrigin과 결합하고 Authorization 헤더를 추가합니다. 토큰 통합은 붙여넣은 토큰을 그대로 보내거나, 매니페스트에 token.scheme: "bearer"가 설정되어 있으면 Bearer <token>으로, "basic"이 설정되어 있으면 Basic base64(username:token)으로 보냅니다. OAuth와 Linear 통합은 항상 Bearer를 보냅니다. 결과는 { status, body }입니다. 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" });

pathrequest와 같은 규칙을 따릅니다. 로컬 서비스를 선언하면 설치 시 사용자가 승인하는 권한에 service가 추가됩니다. 승인 전에는 모든 serviceRequestNO_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은 최대 8,000자, maxOutputTokens는 1에서 4,000입니다. 세션에는 아무것도 들어가지 않고 기록도 남지 않으며, 확장이 모델을 고르는 일도 없습니다. 호출은 최대 90초까지 기다립니다. 쓸 수 있는 Small Model이 없으면 NO_MODEL이고, 모델 오류는 MODEL_FAILED입니다. 텍스트 생성을 참고하세요.

오류 코드

실패한 호출은 HostRequestError를 throw합니다. code는 다음 중 하나이며, message에 자세한 내용이 있습니다.

코드발생 시점
HOST_UNAVAILABLEwindow가 없거나, OpenChamber 안이 아니거나, dispose()가 실행됨
HOST_TIMEOUT20초 동안 응답 없음
HOST_REJECTEDOpenChamber가 거부했거나, 이 SDK가 모르는 코드로 응답함
DISCONNECTED통합에 대한 토큰이나 Linear 연결이 없음
BAD_PATHpath 형식이 잘못되었거나 허용된 origin을 벗어나려 함
NO_INTEGRATION매니페스트에 integration이 없음
NO_SESSION열린 세션 없이 prompt 또는 sessionLink 호출
SESSION_BUSY세션이 바쁜 동안 prompt({ send: true }) 호출
DISABLED사용자가 설정 → 확장 에서 확장을 일시 중지함
NO_SERVICE로컬 서비스가 선언되지 않았거나, 승인되지 않았거나, 실행 중이 아님
NOT_GRANTED이 호출에 필요한 권한을 사용자가 승인하지 않음
SERVICE_FAILED로컬 서비스가 충돌했거나 ready 상태가 되지 못함
NO_DIRECTORY열린 프로젝트 없이 상대 파일 경로를 사용함
NOT_FOUND존재하지 않는 파일에 대한 readFile
FILE_TOO_LARGE읽기 또는 쓰기 시 파일 내용이 제한을 넘음
DENIED운영 체제가 파일 작업을 거부함
NO_MODEL사용할 수 있는 Small Model 없이 generate 호출
MODEL_FAILEDSmall Model이 오류를 반환함

관련 문서