임베딩
Electron 앱이나 Node 서비스 안에서 SNA를 실행합니다.
startSnaServer()는 standalone 서버를 관리되는 자식 프로세스로
fork합니다. 호스트에 따라 두 진입점 중 하나를 사용합니다.
import { startSnaServer } from "@sna-sdk/core/node"; // 일반 Node
import { startSnaServer } from "@sna-sdk/core/electron"; // ElectronElectron용 진입점은 asar-unpacked 경로를 해석하고, 앱이
Electron 런타임에 맞게 리빌드한 better-sqlite3 바이너리를 찾습니다.
라이프사이클
const sna = await startSnaServer({
appId: "my-electron-app",
port: 3099,
dbPath: path.join(app.getPath("userData"), "sna.db"),
maxSessions: 20,
permissionMode: "acceptEdits",
runtimePaths: {
claudeCode: "/opt/homebrew/bin/claude",
},
onLog: (line) => console.log("[sna]", line),
});
// sna.process : spawn된 ChildProcess
// sna.port : 실제 포트 (자동 선택 후)
// sna.appId : 이 서버가 만든 session에 붙는 owner ID
// sna.connection : SDK client에 넘길 connection object
// sna.stop() : graceful SIGTERMForked mode에서는 부모 프로세스와의 IPC 연결이 끊기면 child server도
종료됩니다. Token은 이 server process의 수명에 묶입니다. 긴 수명의
사용자 설정 파일에 넣지 말고 sna.connection 안에서 server handle과
함께 전달하십시오.
Electron 체크리스트
node_modules/@sna-sdk/core/**를 electron-builder의asarUnpack에 추가합니다. 런타임에서 서버의dist/를 읽을 수 있어야 합니다.- Electron 런타임용 네이티브 의존성 리빌드:
npx electron-rebuild -f -w better-sqlite3 dbPath는app.getPath("userData")하위에 둡니다. 그래야 업그레이드 후에도 SQLite 파일이 유지됩니다.- 사용자가 선택한 런타임 CLI 경로는 앱 설정에 저장하고, SNA 시작 시
runtimePaths로 넘깁니다. - 커스텀 빌드한
better_sqlite3.node를 기본 위치 밖에 둔다면nativeBinding을 넘깁니다.
Renderer에서 연결
Renderer 프로세스는 임베드된 서버를 일반 HTTP/WS 호스트처럼 사용합니다.
import { SnaClient } from "@sna-sdk/client";
const client = new SnaClient(sna.connection);<SnaProvider>는 GET /api/sna-port 헬퍼를 자동으로 사용합니다.
따라서 React 앱에서는 IPC 없이도 renderer가 포트를 찾을 수 있습니다.
Host가 같은 신뢰된 app endpoint에서 { baseUrl, authToken }을 반환하면
SnaProvider가 그 값을 connection으로 자동 사용합니다. 이 helper는 host app
경계 안에 두는 것이 안전합니다. 런타임 자체의 /api/sna-port 라우트는 보호되어
있고, 같은 token이 SSE와 WebSocket traffic도 인증합니다.