Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion docs/oliveyoung-free-relay.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# 올리브영 무료 브라우저 릴레이

## 2026-09-28 운영 연결과 용량 보호

Mac GUI 중계 → Tunnel → Access Service Auth → 운영 Worker 연결에서 상품·매장·재고와 MCP 상품 조회를 확인했습니다. 무인증은 Access403, Access 인증만 있는 요청은 릴레이401로 거절합니다. 설치 호스트가 재부팅되면 GUI 로그인이 필요합니다. 설치용 Cloudflare API 토큰의 만료와 상시 실행용 Tunnel/Access 자격증명의 수명은 별개입니다.

재고 보강은 요청당 최대 5개 상품에 적용하고 상품 2개씩 처리합니다. 더 큰 `stockCheckLimit`도 5로 제한합니다. 확인하지 못한 상품의 온라인 재고를 매장 재고로 바꾸지 않으며 `stockCheckedCount`/`stockUncheckedCount`로 구분합니다. 여러 사용자 요청을 합친 전체 중계 용량과 쿼터는 아래 제한을 따릅니다.

릴레이 HTTP429·502·503은 API 상태 및 `diagnostics.upstreamStatus`에 보존합니다. timeout은504, 네트워크/잘못된 응답은502로 분류하며 `retryable`을 함께 제공합니다. 인증/리다이렉트/응답 형식 오류는 자동 재시도 대상으로 표시하지 않습니다. MCP는 `isError`와 text의 JSON 오류 진단을 반환하고 성공 스키마용 `structuredContent`는 넣지 않습니다. 응답 원문과 인증 토큰은 오류에 포함하지 않습니다. 자동 재시도나 유료 대체 경로는 추가하지 않았습니다.

## 2026-09-21 Windows 로컬 검증

Windows 11의 설치된 Chrome을 별도 임시 컨텍스트로 실행하여 기존 `createBrowserRunner`와 `createOliveyoungRelay`를 검증했습니다. 개인 프로필·로그인 정보는 사용하지 않았습니다. Node 클라이언트가 `127.0.0.1`의 임시 포트에 생성한 인증 릴레이를 거쳐 브라우저에서 조회하는 전체 경로를 확인했습니다.
Expand Down Expand Up @@ -27,6 +35,7 @@ Workers가 지원하지 않는 `redirect: 'error'` 대신 `manual`을 사용하
export OY_RELAY_TOKEN='<전용 임의 토큰>'
# 선택 사항: 기본 포트 4319, Playwright 기본 Chromium 사용
export OY_RELAY_PORT=4319
export OY_RELAY_STATE_DIR="$HOME/Library/Application Support/DaisoRelay/state"
# export OY_BROWSER_EXECUTABLE='/absolute/path/to/chromium'
npx tsx scripts/relay/start.ts
```
Expand Down Expand Up @@ -77,7 +86,7 @@ GitHub Actions의 `Sync Worker Secrets`는 위 네 OY secrets를 동기화하며

## 상시 운영 보강

중계는 한 브라우저·한 컨텍스트·한 페이지를 재사용합니다. 요청마다 탭을 만들지 않습니다. 추가 페이지는 즉시 회수하며 회수 실패·페이지 crash·Node 측 watchdog timeout에는 소유한 브라우저 세션을 폐기합니다. 200회 조회 또는 30분 사용 후 유휴 경계에서 정리하고 다음 요청에 새 세션을 만듭니다.
중계는 한 브라우저·한 컨텍스트·한 페이지를 재사용합니다. 요청마다 탭을 만들지 않습니다. 추가 페이지는 즉시 회수하며 회수 실패·페이지 crash·Node 측 watchdog timeout에는 소유한 브라우저 세션을 폐기합니다. 200회 조회 또는 30분 사용 후 유휴 경계에서 정리하고 교체 세션을 한 번 미리 준비합니다. RSS 한도 회수도 같은 준비 경로를 사용합니다. 이전 소유 프로세스의 회수가 완료된 다음 새 브라우저를 띄우므로 브라우저 두 개를 겹쳐 유지하지 않습니다. 준비 중 health 상태는 `starting`입니다. 준비 실패 시 무한 재시도하지 않고 다음 명시적 요청을 기다립니다. 준비 중 도착한 요청은 여전히 준비 시간을 기다릴 수 있으므로 항상 15초 이내 응답을 보장하는 것은 아닙니다.

브라우저는 별도 guard 프로세스가 소유합니다. 중계 부모가 SIGKILL로 종료되어도 IPC 단절을 감지해 회수합니다. PID·생성 시각·프로세스 그룹을 확인한 소유 프로세스만 종료합니다. macOS에서 별도 그룹으로 분리되는 crashpad 보조 프로세스도 실행별 고유 경로로 식별해 메모리 집계와 회수에 포함합니다. 브라우저에는 GUI 실행에 필요한 환경만 전달하고 운영 토큰은 제외합니다. 정상 종료가 지연되면 확인된 그룹을 강제 종료하고 실제 소멸을 확인합니다. 개인 Chrome을 프로세스 이름으로 일괄 종료하지 않습니다. guard까지 강제 종료되거나 소유권 확인이 실패한 경우 소유 마커를 유지하여 재시작을 차단합니다. 이 경우 운영자가 잔존 프로세스를 확인해야 합니다. 소유 확인 없이 마커 파일을 지우면 안 됩니다.

Expand All @@ -89,6 +98,10 @@ GitHub Actions의 `Sync Worker Secrets`는 위 네 OY secrets를 동기화하며

Cloudflare Access 사용 시 Worker secrets에 `OY_ACCESS_CLIENT_ID`와 `OY_ACCESS_CLIENT_SECRET`을 함께 설정합니다. 기존 `OY_RELAY_TOKEN`과 별개입니다. 사용자 도구 입력으로 전달하거나 덮어쓸 수 없습니다. Access 비밀이 다른 주소로 전달되지 않도록 리다이렉트를 거절합니다. Access 정책은 해당 서비스 토큰만 허용하는 Service Auth로 구성하며, Tunnel은 localhost:4319와 최종 404 경로만 연결합니다. 브라우저 디버깅 주소와 개인 네트워크 경로는 연결하지 않습니다.

### 실행 계정 선택

전용 표준 계정은 개인 파일과 실행 권한을 분리하는 권장 구성입니다. 운영자가 격리 차이를 이해하고 선택하면 현재 로그인 계정의 사용자 LaunchAgent와 사용자 Library 경로로 실행할 수도 있습니다. 이 경우 개인 브라우저 프로필을 사용하지 않아도 프로세스의 파일 접근 권한은 해당 계정과 같습니다. 아래 설치 도구는 전용 계정용이며 현재 계정 설치를 자동 수행하지 않습니다.

### macOS 전용 계정 설치

시스템 설정에서 일반 사용자 계정 `daisorelay`를 만듭니다. 관리자 계정은 설치 스크립트가 거절합니다. 코드와 전용 브라우저는 `/Library/Application Support/DaisoRelay`에 root 소유로 설치하고, 토큰과 상태는 전용 계정 홈에만 둡니다. 자동 로그인은 설정하지 않습니다. 계정 생성과 최초 GUI 로그인은 사용자가 직접 수행해야 합니다.
Expand Down
14 changes: 14 additions & 0 deletions docs/superpowers/plans/2026-09-28-dtryx-comparison.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 디트릭스 원격 비교 — 2026-09-28

프로덕션을 바꾸지 않고 인증 토큰이 필요한 임시 Worker를 만들었다. 임의 URL은 받지 않으며 443/30443과 극장000072/000067만 허용했다. compatibility_date2025-01-23, 동일 헤더·cache:no-store·6초 제한, 요청 순서를 교대로 적용했다.

| 회차 | 극장 | 443 | 30443 |
|---|---|---|---|
| 1 | 000072 | 6000ms timeout | 6000ms timeout |
| 2 | 000067 | 199ms 성공/14건 | 6000ms timeout |
| 3 | 000072 | 6000ms timeout | 6000ms timeout |
| 4 | 000067 | 6000ms timeout | 6000ms timeout |

443 1/4, 30443 0/4다. 한 번의 성공은 지속적 복구나 포트 원인을 입증하지 않는다. 실패는 모두 응답 헤더 이전이었다. 9/19·9/21 양 포트 실패 기록과 일치하며 #188을 영구 복구로 반영하지 않는다. 모든 시험 뒤 임시 Worker를 삭제했다. 새 배포의 라우트 전파 전404가 발생한 최초 시도도 finally에서 삭제한 뒤, 인증 거절401로 준비를 확인하도록 보강해 위 실험을 수행했다.

별도 현 운영 좌석 조회3회는 503/15.65s, 503/15.65s, 200/0.82s였다. 오류를200으로 바꾸거나 15초 제한을 늘리는 변경은 하지 않았다. 다음 단계는 업스트림의 Cloudflare 출발 연결 로그와 동일 시각 TCP/TLS/헤더 지연을 대조하는 것이다.
15 changes: 15 additions & 0 deletions docs/superpowers/plans/2026-09-28-maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 9월 28일 유지보수 실행 계획

## 승인된 목표
올리브영 중계의 요청 용량과 원인 진단, 브라우저 회수 후 첫 요청을 보강하고 열린 의존성 PR을 반영한다. 디트릭스는 통제된 원격 시험에서 입증된 수정만 적용한다. 유료 Zyte는 비활성 상태를 유지한다.

## 변경 단위와 검증
- [x] client.ts 및 관련 도구/핸들러: 상품별 재고 요청 동시성을 제한하고 확인 개수 상한을 적용. 실패가 온라인 재고와 혼동되지 않도록 기존 unchecked 정보를 유지. 대량 입력과 병렬 활성 개수 테스트 먼저 작성.
- [x] transport.ts/core errors: 릴레이 429/502/503과 timeout/네트워크를 원인·HTTP 상태·재시도 가능 여부로 보존. API/MCP 기존 오류 계약을 유지하고 회귀 테스트 추가.
- [x] relay lifecycle: 30분/200회/RSS 회수 뒤 요청이 냉간 기동 전체를 기다리지 않도록 유휴 시 준비. 닫기·강제종료·중복 준비·실패 루프를 시험. 한 브라우저/한 탭과 guard 소유 회수 유지.
- [x] PR193~199 lockfile 갱신 통합. 전체 check/build/coverage100%/audit 및 Wrangler dry-run.
- [x] Dtryx443/30443 원격 비교와 기존 관측 대조. 근거가 없으면 #188을 보류하고 결과만 기록.
- [ ] 독립 코드 검토, GitHub CI 이후 통합 PR 병합·Worker 배포·릴리스. hm 설치본을 안전하게 교체하고 실제 MCP·재고·Access 보호·프로세스 회수 확인.

## 경계
KV/유료 API/무제한 재시도는 추가하지 않는다. 로컬 구동 파일은 사용자 Library의 배포 복사본이며 작업 worktree와 분리한다. 원본 main은 결과 통합 전 유지한다. 이슈/기여자 대화는 별도 메시지 전송 승인 없이 게시하지 않는다.
Loading
Loading