| name | open-notebook-feeder |
| description | 사용자의 셀프호스팅 Open Notebook 인스턴스를 조작한다. 소스 업로드·다운로드·폴링은 open-notebook-feeder CLI를, 노트북/노트/채팅/검색/인사이트/팟캐스트/transformation의 조회·생성·수정·삭제는 curl로 직접 API를 호출한다. "오픈노트북에 넣어줘", "노트 만들어줘", "검색해줘", "팟캐스트 만들어줘" 등 Open Notebook 관련 모든 요청에 트리거된다. |
open-notebook-feeder 스킬
최우선 원칙
- 역할 분담: CLI가 더 안전한 자리는 CLI, 나머지는 curl.
multipart 업로드·바이너리 다운로드·클라이언트 측 폴링은 CLI, 단순 GET·JSON POST는 curl로 직접 호출.
- URL·IP·토큰을 어디에도 적지 않는다. 명령어·코드·주석·출력 전부 금지. 항상 환경변수(
$OPEN_NOTEBOOK_URL, $OPEN_NOTEBOOK_TOKEN)로만 참조.
- 환경변수가 없으면 값을 직접 넣지 말고 멈춘다. curl이 401/연결 오류로 실패하거나 변수가 비어 있으면 사용자에게 설정을 요청하고 작업 중단. 값을 추정하거나 하드코딩 절대 금지.
환경변수 / 연결 확인
| 변수 | 필수 | 용도 |
|---|
OPEN_NOTEBOOK_URL | ✓ | 베이스 URL (끝에 /api 붙이지 말 것) |
OPEN_NOTEBOOK_TOKEN | ✓ | Bearer 토큰 |
OPEN_NOTEBOOK_DEFAULT_NOTEBOOK | | add-*의 기본 노트북 id |
환경변수는 설정되어 있다고 가정하고 바로 사용한다. 값을 출력하거나 확인하지 않는다. curl 호출이 401/연결 오류로 실패할 때만 환경변수 미설정을 안내한다.
실행: CLI는 PATH에 없음 — 반드시 절대 경로로 호출:
~/Documents/GitHub/open-notebook-feeder/open-notebook-feeder <subcommand>
CLI로 가야 하는 작업
| 작업 | CLI 커맨드 |
|---|
| 파일 소스 추가 | add-file [--notebook <id>] [--sync] <path> |
| 텍스트/링크 소스 추가 | add-text, add-link |
| 소스 처리 상태 1회 조회 | status <source-id> |
| 노트북·transformation 목록 (편의) | notebooks, transformations |
~/Documents/GitHub/open-notebook-feeder/open-notebook-feeder add-file \
--notebook notebook:xxx \
"/path/to/file.mp3"
--notebook (not --notebook-id): 노트북 ID 지정. 생략 시 $OPEN_NOTEBOOK_DEFAULT_NOTEBOOK 사용.
--sync: 서버가 처리 완료까지 인라인 대기 (기본은 async). 폴링이 필요하면 status 반복 호출 또는 GET /api/sources/{id} 루프 사용.
직접 API 호출로 처리 (curl)
curl 공통 템플릿
curl -s -H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
"$OPEN_NOTEBOOK_URL/api/..."
curl -s -X POST \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"key": "value"}' \
"$OPEN_NOTEBOOK_URL/api/..."
curl -s -X PUT \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"key": "value"}' \
"$OPEN_NOTEBOOK_URL/api/..."
curl -s -X DELETE \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
"$OPEN_NOTEBOOK_URL/api/..."
출력 파싱 및 Windows 인코딩 주의사항:
- curl은 반드시 Bash 도구에서 호출한다. PowerShell 도구에서
curl은 Invoke-WebRequest의 별칭이라 파라미터가 완전히 다르다.
- 한글이 포함된 응답을 파이프로 직접 파싱하면
UnicodeEncodeError 발생. 파일로 저장 후 처리한다.
curl -s -H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
"$OPEN_NOTEBOOK_URL/api/..." > "$TEMP/out.json"
PYTHONIOENCODING=utf-8 python -c "
import json
with open('$TEMP/out.json', encoding='utf-8') as f:
data = json.load(f)
# 처리...
"
응답이 크거나(full_text 포함 소스 등) 한글이 포함된 경우엔 파일 저장 후 Read 툴로 읽는 것이 가장 안정적이다.
엔드포인트 빠른 표
노트북 (Notebooks)
| METHOD | 경로 | 주요 body 필드 |
|---|
| GET | /api/notebooks | — |
| POST | /api/notebooks | name, description (둘 다 필수) |
| GET | /api/notebooks/{id} | — |
| PUT | /api/notebooks/{id} | name, description, archived |
| DELETE | /api/notebooks/{id} | — |
| POST | /api/notebooks/{id}/sources/{source_id} | — (소스 연결) |
| DELETE | /api/notebooks/{id}/sources/{source_id} | — (소스 연결 해제) |
POST /api/notebooks는 name만 보내면 {"detail":"There was an error parsing the body"} 응답이 온다. description은 빈 문자열이라도 함께 보내야 한다.
소스 (Sources) — 업로드는 CLI
| METHOD | 경로 | 주요 body 필드 |
|---|
| GET | /api/sources | — (쿼리: ?notebook_id=notebook:xxx 로 필터링 가능) |
| GET | /api/sources/{id} | — |
| PUT | /api/sources/{id} | title, topics |
| DELETE | /api/sources/{id} | — |
| GET | /api/sources/{id}/status | — |
| POST | /api/sources/{id}/retry | — |
| GET | /api/sources/{id}/insights | — |
| POST | /api/sources/{id}/insights | transformation_id, model_id |
노트 (Notes)
| METHOD | 경로 | 주요 body 필드 |
|---|
| GET | /api/notes | (쿼리: notebook_id) |
| POST | /api/notes | title, content, note_type, notebook_id |
| GET | /api/notes/{id} | — |
| PUT | /api/notes/{id} | title, content, note_type |
| DELETE | /api/notes/{id} | — |
인사이트 (Insights)
| METHOD | 경로 | 주요 body 필드 |
|---|
| GET | /api/insights/{id} | — |
| POST | /api/insights/{id}/save-as-note | — |
| DELETE | /api/insights/{id} | — |
인사이트 처리 방식:
- 소스 업로드 후 인사이트는 자동 생성되지 않는다.
GET /api/sources/{id}/insights가 빈 배열인 것이 정상. 인사이트가 필요하면 POST /api/sources/{id}/insights로 transformation을 명시적으로 실행해야 한다.
- 생성된 인사이트는 임베딩되어 검색·채팅에 자동으로 활용된다. 따라서
save-as-note로 노트로 복사하는 건 보통 중복일 뿐, 검색 가능성을 늘리지 않는다. 외부로 내보내거나 편집이 필요할 때만 save-as-note를 쓴다.
- transformation은 LLM 호출이라 수 분 걸린다.
POST 응답은 즉시 pending + command_id 반환. 진행 상황은 GET /api/commands/jobs/{command_id}로 확인 (Commands 섹션 참조).
- 기본 transformation 프롬프트는 소스 언어와 무관하게 영어로 인사이트를 생성할 수 있다. 한국어 결과가 필요하면 사용자에게 미리 알리고, custom transformation 사용 또는 프롬프트 수정을 안내한다.
Commands (비동기 작업 추적)
| METHOD | 경로 | 설명 |
|---|
| GET | /api/commands/jobs | 작업 목록 |
| GET | /api/commands/jobs/{job_id} | 작업 상태 조회 |
| DELETE | /api/commands/jobs/{job_id} | 작업 취소 |
status 필드: pending → running → completed/failed. 인사이트 생성, 임베딩 재구축, 팟캐스트 생성 등 비동기 LLM 작업의 진행을 추적할 때 사용.
채팅 (Chat)
| METHOD | 경로 | 주요 body 필드 |
|---|
| GET | /api/chat/sessions | — |
| POST | /api/chat/sessions | notebook_id, title |
| GET | /api/chat/sessions/{id} | — |
| DELETE | /api/chat/sessions/{id} | — |
| POST | /api/chat/execute | session_id, message, context |
| POST | /api/chat/execute/stream | session_id, message |
검색 (Search)
| METHOD | 경로 | 주요 body 필드 |
|---|
| POST | /api/search | query, type, limit, search_sources, search_notes |
| POST | /api/search/ask | question, strategy_model, answer_model |
| POST | /api/search/ask/simple | question |
Transformation
| METHOD | 경로 | 주요 body 필드 |
|---|
| GET | /api/transformations | — |
| POST | /api/transformations | name, prompt |
| GET | /api/transformations/{id} | — |
| PUT | /api/transformations/{id} | name, prompt |
| DELETE | /api/transformations/{id} | — |
| POST | /api/transformations/execute | transformation_id, input_text, model_id |
팟캐스트 (Podcasts)
| METHOD | 경로 | 주요 body 필드 |
|---|
| GET | /api/podcasts/episodes | — |
| GET | /api/podcasts/episodes/{id} | — |
| DELETE | /api/podcasts/episodes/{id} | — |
| POST | /api/podcasts/episodes/{id}/retry | — |
| POST | /api/podcasts/generate | episode_profile, speaker_profile, episode_name, notebook_id |
| GET | /api/podcasts/jobs/{job_id} | — |
Episode/Speaker 프로필
| METHOD | 경로 |
|---|
| GET | /api/episode-profiles |
| POST | /api/episode-profiles |
| GET/PUT/DELETE | /api/episode-profiles/{id} |
| GET | /api/speaker-profiles |
| POST | /api/speaker-profiles |
| GET/PUT/DELETE | /api/speaker-profiles/{id} |
기타 (희소 사용)
| 경로 | 설명 |
|---|
GET /api/models | 등록된 LLM 목록 |
GET /api/models/defaults | 기본 모델 확인 |
GET /api/config | 서버 설정 조회 |
GET /api/settings | 앱 설정 조회 |
주요 시나리오 예시
curl -s -X POST \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "요약", "content": "## 내용\n...", "note_type": "human", "notebook_id": "notebook:..."}' \
"$OPEN_NOTEBOOK_URL/api/notes"
SESSION=$(curl -s -X POST \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"notebook_id": "notebook:...", "title": "질문 세션"}' \
"$OPEN_NOTEBOOK_URL/api/chat/sessions" | python -c "import sys,json; print(json.load(sys.stdin)['id'])")
curl -s -X POST \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"session_id\": \"$SESSION\", \"message\": \"이 자료의 핵심을 요약해줘\"}" \
"$OPEN_NOTEBOOK_URL/api/chat/execute"
curl -s -X POST \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "항력 계수", "limit": 10, "search_sources": true, "search_notes": true}' \
"$OPEN_NOTEBOOK_URL/api/search"
curl -s "$TEMP/podcast_job.json" > "$TEMP/podcast_job.json" || true
curl -s -X POST \
-H "Authorization: Bearer $OPEN_NOTEBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"episode_name": "에피소드명", "notebook_id": "notebook:...", "episode_profile": "default", "speaker_profile": "default"}' \
"$OPEN_NOTEBOOK_URL/api/podcasts/generate" > "$TEMP/podcast_job.json"
판단 원칙
- 노트북 id 모를 때:
notebooks 커맨드(CLI) 또는 GET /api/notebooks로 목록 확인 후 사용자에게 물어본다. 임의 선택 금지.
- 일괄 작업: 첫 건 실행해 정상 응답 확인 후 나머지 진행. 에러 상태로 대량 전송 금지.
- transformation 선택:
transformations 커맨드로 목록 뽑아 사용자 확인 후 적용. 이름 추측 금지.
- MP3 등 STT 소스 — 업로드 응답만으로 성공이라 단정하지 않는다. 업로드 응답은
status: new (큐 진입 신호)일 뿐, 실제 처리는 비동기로 STT → 임베딩이 진행된다. STT 서비스가 죽어 있으면 status: failed + error: "Failed to transcribe audio: ..."로 끝난다. 일괄 업로드 시 응답만 보고 "다 올렸습니다" 라고 보고하면 사용자가 며칠 뒤 빈 transcript를 발견한다. 반드시 status <source-id> 또는 GET /api/sources/{id}로 status: completed + embedded_chunks > 0 확인 후 보고한다.
failed 상태의 소스 retry 한계: POST /api/sources/{id}/retry가 "Source is not associated with any notebooks"로 거부될 수 있다(서버 측 캐시·연결 상태 불일치 추정). 노트북에 다시 link해도 같은 에러가 반복되면 소스 삭제 후 재업로드로 우회한다.
- 스트리밍 채팅: curl
-N 플래그 필요 (curl -s -N -X POST ...).
금지 사항
curl ... /api/sources 같은 직접 파일 업로드 (multipart는 CLI 위임)
- URL·IP·토큰을 메시지/코드/주석에 박기
- 환경변수 없을 때 "아마 이 주소일 겁니다" 식 추측
- 이미
add-link로 실패한 사이트에 재시도 (WebFetch로 추출 후 add-text 경로로 전환)
새 엔드포인트가 필요할 때
서버의 Swagger UI: $OPEN_NOTEBOOK_URL/docs
(URL은 환경변수에서 조립 — 하드코딩 금지)