Skip to content

fix: OpenCode SQLite 토큰 사용량 파싱 지원 - #22

Merged
kobenlys merged 10 commits into
TOKENPHAGE:developfrom
haazz:feature/opencode-sqlite-storage
Sep 3, 2026
Merged

kobenlys merged 10 commits into
TOKENPHAGE:developfrom
haazz:feature/opencode-sqlite-storage

Conversation

@haazz

@haazz haazz commented Aug 4, 2026 •

Copy link
Copy Markdown

🏷️ 작업 유형

  • 🐛 버그 픽스
  • ✨ 개발
  • 📝 문서 추가 및 수정
  • ♻️ 리팩터링
  • 🔧 ETC

📋 변경 사항

  • 최신 OpenCode가 토큰 기록을 storage/message, storage/part JSON 대신 opencode.db에 저장하는 변경을 반영했습니다.
  • opencode.db가 있으면 외부 App(opencode CLI) 호출 없이 better-sqlite3로 read-only 직접 조회합니다. (feat: Hermes Agent 토큰 사용량 집계 지원 #20 DB 조회 방식과 정렬)
    • OpenCode의 설치 여부·버전·오작동이 tokenphage sync 동작에 영향을 주지 않습니다.
  • 사용량을 로컬 날짜와 모델별로 집계하고 기존 토큰 필드 형식으로 변환합니다.
  • DB가 없는 구버전에서는 기존 JSON 파일 파서를 그대로 사용합니다.
  • DB와 기존 JSON이 함께 있을 때는 마이그레이션된 기록의 이중 집계를 방지하기 위해 DB를 우선합니다.

#21

🔁 리뷰 반영 (2차)

AS-IS : Tokenphage → OpenCode(App) → opencode.db
TO-BE : Tokenphage → opencode.db

  • src/parsers/opencode.js의 execFile('opencode', ['db', ...]) 경로를 제거했습니다.

  • parseOpencodeDatabase(dbPath)로 조회 대상 경로를 명시적으로 받습니다.

  • src/parser.js는 후보 디렉터리에서 처음 찾은 opencode.db 하나만 읽습니다(first-wins).

  • tokens가 객체인 step-finish part만 집계하도록 WHERE 조건을 좁혔습니다 (JSON null tokens가 0 레코드로 새던 문제 제거).

  • CLI stub 대신 실제 SQLite fixture로 DB 경로 테스트를 재작성했습니다.

  • 실패 격리: parseAll이 Promise.all로 파서들을 묶기 때문에 OpenCode 조회가 throw하면 claude/codex 결과까지 버려지고 sync 전체가 실패합니다. 어떤 실패도 예외를 올리지 않고 빈 목록을 반환하도록 격리했습니다 (feat: Hermes Agent 토큰 사용량 집계 지원 #20 parseHermesSessions와 동일 계약).

  • 경고는 조치가 필요한 경우에만: OpenCode를 쓰지 않는 사용자가 노이즈를 보지 않도록 좁혔습니다.

    상황 동작
    파일 없음 (OpenCode 미사용) 조용히 스킵 — parseAll이 glob으로 찾았을 때만 DB를 읽으므로 애초에 호출되지 않습니다
    테이블 없는 빈 DB (삭제 후 잔존) 조용히 스킵
    손상·권한 거부 (파일은 존재) 경고 — 사용량이 누락되는 중
    message/part만 없음 (스키마 변경) 경고 — 집계가 0으로 새는 것을 조용히 넘기지 않음

✅ 테스트

  • npm test: 184개 테스트 통과
  • npm run test:coverage: 커버리지 기준 통과 (src/parsers/opencode.js 100% lines / 100% functions)
  • git diff --check: 통과
  • 격리 실전 검증: 임시 XDG_DATA_HOME에 4가지 상태(파일 없음 / 빈 DB / 손상 / 스키마 변경)의 opencode.db를 두고 parseAll 실행 → 전 케이스에서 Claude 레코드는 정상 집계되고, 경고는 손상·스키마 변경 2건에서만 출력됨

kobenlys and others added 5 commits July 12, 2026 19:54
release: 2026.7.1-BETA — Linux auto-sync 지원 및 config·에러 견고화
release: 2026.7.2-BETA — update-check -BETA 감지 수정
release: develop → main (2026.8.1)
docs: 이슈 템플릿 main 반영
@kobenlys

kobenlys commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

PR 감사합니다

  • Target Branch를 develop 으로 변경해주세요

검토 결과 전달드립니다.

AS-IS : Tokenphage → OpenCode(App) → opencode.db
TO-BE : Tokenphage → opencode.db

OpenCode의 상태(오작동, 삭제, 등)에 따라 Tokenphage sync 동작에
영향이 가지 않도록 변경되어야 합니다.

#20 db조회 방식 참고

해당 지점

src/parsers/opencode.js:67 ~ 93 외부 App을 호출하여 데이터를 받아옴


그 외 코드컨밴션 및 테스트코드 작성된 부분은 좋습니다 LGTM

Comment thread test/parser-opencode.test.js Outdated

test('parseOpencodeRows_모델또는날짜가없는행은제외', () => {
const entries = parseOpencodeRows([
{ time_created: TS_MS, model: null, output: 999 },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USAGE_QUERY가 SELECT하는 별칭은 7개입니다 (src/parsers/opencode.js:12-18).

​ date, model, input, output, reasoning, cache_read, cache_write ​

time_created: TS_MS는 운영환경에서 조회될 수 없는 값입니다

테스트 의도와 코드는 좋으니 운영에서 사용되는 데이터 형식으로 변경해주시면 좋을것같습니다 LGTM

@haazz
haazz changed the base branch from main to develop August 18, 2026 04:04
kobenlys and others added 4 commits August 23, 2026 12:16
docs: 배지 예시를 마크다운 스니펫으로 교체하고 grass 테마 추가
opencode.db(SQLite)를 외부 App 호출 없이 직접 읽기 위해 필요하다.
동일 목적으로 SQLite를 조회하는 #20과 버전(^11.10.0)을 맞춘다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y47Mk5FmHKctpTjcFZ45Ar
리뷰 반영. `opencode` CLI를 execFile로 호출해 사용량을 받아오던 경로를
걷어내고 opencode.db를 read-only로 직접 조회한다(TOKENPHAGE#20 DB 조회 방식과 정렬).

AS-IS : Tokenphage -> OpenCode(App) -> opencode.db
TO-BE : Tokenphage -> opencode.db

- parseOpencodeDatabase(dbPath)로 조회 대상 경로를 명시적으로 받는다.
  parser.js는 후보 디렉터리에서 처음 찾은 opencode.db 하나만 읽는다(first-wins).
- OpenCode 상태가 sync를 막지 않도록 실패를 격리한다. parseAll이 파서들을
  Promise.all로 묶기 때문에 여기서 throw하면 claude/codex 결과까지 버려진다.
  어떤 실패도 예외를 올리지 않고 빈 목록을 반환한다.
- 경고는 조치가 필요한 경우로 한정한다. 파일 없음·빈 DB(삭제 후 잔존)는
  집계할 기록이 없을 뿐이므로 조용히 넘어가고, 파일이 남아 있는데 읽기가
  실패했거나(손상·권한) message/part 테이블만 없으면(스키마 변경) 알린다.
- tokens가 객체인 step-finish part만 집계하도록 WHERE 조건을 좁혔다.
  JSON null tokens가 json_type에서 'null'을 돌려줘 0 레코드로 새던 문제를 막는다.
- CLI stub 대신 실제 SQLite fixture로 테스트를 재작성하고,
  console.warn을 가로채 경고 발생 여부까지 단언한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y47Mk5FmHKctpTjcFZ45Ar
@haazz
haazz force-pushed the feature/opencode-sqlite-storage branch from 5f9b0bc to 775f5c9 Compare August 25, 2026 23:17
리뷰 반영. USAGE_QUERY는 date·model·input·output·reasoning·cache_read·
cache_write 7개 별칭만 SELECT하는데, time_created를 받는 경로가 코드와
테스트에 남아 있어 운영에서 나올 수 없는 데이터 형식을 다루고 있었다.

- entryOfRow에서 localDateOf(row.time_created) fallback을 없앤다. 쿼리가
  time_created를 내보내지 않으므로 이 분기는 항상 undefined를 받는 죽은
  경로였고, 의도(날짜를 못 만들면 제외)를 재시도처럼 읽히게 만들었다.
  date는 strftime 결과라 'YYYY-MM-DD' 아니면 NULL이므로 그것만 검사한다.
- parseOpencodeRows 제외 테스트의 행을 운영 형식(date 별칭)으로 바꾼다.
- m.time_created가 NULL이면 strftime이 NULL을 돌려주는 실제 동작을
  SQLite fixture 테스트로 고정한다. 위 제외 규칙이 쿼리 결과와 맞물려
  동작하는지를 stub이 아니라 실제 DB로 검증한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014feReoHMBpj1XPiPHgHQfM
@haazz
haazz force-pushed the feature/opencode-sqlite-storage branch from 2fc1393 to ad07270 Compare August 26, 2026 23:06
@kobenlys
kobenlys merged commit e91db8f into TOKENPHAGE:develop Sep 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants