SNA

임베딩

Electron 앱이나 Node 서비스 안에서 SNA를 실행합니다.

startSnaServer()는 standalone 서버를 관리되는 자식 프로세스로 fork합니다. 호스트에 따라 두 진입점 중 하나를 사용합니다.

import { startSnaServer } from "@sna-sdk/core/node";      // 일반 Node
import { startSnaServer } from "@sna-sdk/core/electron";  // Electron

Electron용 진입점은 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 SIGTERM

Forked 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
  • dbPathapp.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도 인증합니다.

목차