# node — Node Agent 디바이스당 1개 실행되는 IOP 노드 에이전트. 실제 AI 모델 추론 어댑터를 관리하고, TCP + Protobuf + mTLS 소켓을 통해 요청을 처리한다. ## 내부 구조 (Hexagonal Architecture) ``` cmd/node/ — CLI 진입점 (cobra) internal/ bootstrap/ — fx 의존성 주입 모듈 node/ — 핵심 노드 서비스 (transport.Handler 구현) runtime/ — 도메인 타입: ExecutionSpec, RuntimeEvent, 인터페이스 router/ — RunRequest → ExecutionSpec 라우팅 transport/ — TCP 서버, 세션 관리, 프레임 코덱 adapters/ mock/ — 에코 테스트 어댑터 ollama/ — Ollama API 어댑터 (TODO) vllm/ — vLLM OpenAI-compatible 어댑터 (TODO) cli/ — CLI 프로세스 어댑터 (claude/gemini/codex/opencode) store/ — SQLite 실행 이력 ``` ## 실행 ```bash # 빌드 go build -o bin/node ./apps/node/cmd/node # 버전 확인 ./bin/node version # 설정 확인 ./bin/node config print --config configs/node.yaml # 서버 실행 ./bin/node serve --config configs/node.yaml ``` 원격 edge에 붙는 수동 테스트는 `configs/node.yaml`의 `transport.edge_addr`와 `transport.token`을 먼저 맞춘 뒤 repo root에서 실행한다. ```bash ./bin/node.sh ``` edge의 기본 설정은 cli adapter의 `claude` profile을 사용한다. PATH에 `claude` 명령이 있어야 하며, 각 실행 요청은 `claude -p --dangerously-skip-permissions --output-format stream-json --include-partial-messages --verbose ` one-shot process로 처리된다 (token-level streaming). edge에서 실행 요청을 받으면 node는 해당 입력을 Claude process의 prompt 인자로 전달하고, stream-json delta를 토큰 단위로 emit한다. ```text [edge-message] hello [node-event] start run_id=manual-... [node-event] complete run_id=manual-... detail="cli execution complete" [node-message] ``` ## Logical Session (transport 1개 · session 여러 개) edge-node transport 연결은 **호스트당 1개**를 유지한다. 그 연결 위에서 CLI adapter는 `session_id`가 다른 여러 장수 worker process를 독립적으로 관리한다. | 개념 | 설명 | |---|---| | transport 연결 | edge-node 호스트 쌍당 1개 TCP 연결 | | logical session | `(adapter, model, session_id)` 로 식별되는 장수 worker process | | run | session 위에서 실행되는 단일 요청 | **cancel vs terminate:** - `CancelAction_CANCEL_RUN` (기본값): 현재 실행 중인 run만 중단. session process는 살아있다. - `CancelAction_TERMINATE_SESSION`: session process를 명시적으로 종료. 이후 같은 `session_id`로의 요청은 새 process를 만들거나(`CREATE_IF_MISSING`) 에러를 반환한다(`REQUIRE_EXISTING`). **session_mode:** - `RUN_SESSION_MODE_CREATE_IF_MISSING` (기본값): session이 없으면 새로 생성. - `RUN_SESSION_MODE_REQUIRE_EXISTING`: session이 없으면 에러 반환. 새 process 생성 금지. ## 어댑터 | 어댑터 | 설명 | 상태 | |---|---|---| | `mock` | 입력 에코, 스트리밍 테스트용 | 구현 완료 | | `ollama` | 로컬 Ollama 서버 연동 | TODO | | `vllm` | vLLM OpenAI-compatible API | TODO | | `cli` | claude/gemini/codex/opencode CLI 실행 | 구현 완료 | `claude`, `gemini`, `codex`처럼 기본이 interactive TUI인 CLI는 이 파이프라인에서 non-interactive 모드로 설정해야 한다. 예: `claude -p --dangerously-skip-permissions`, `gemini --approval-mode yolo -p `, `codex exec --dangerously-bypass-approvals-and-sandbox`. ## Transport - TCP 4-byte length-prefix framing - JSON-encoded Envelope (TODO: protobuf 교체) - mTLS (configs/node.yaml에서 활성화) - 하트비트: 30초 간격