Commit Graph
540 Commits
Author SHA1 Message Date
kimandClaude Code bff18650a5 feat: 작업실 채팅 세션을 프로젝트별로 분리
기존엔 앱 전체 공용 세션(ws_main/랜덤 id) 하나를 써서 프로젝트를 바꿔도
옛 프로젝트의 대화가 그대로 남았다. sessionId를 'ws_'+projectId로 결정적
파생하도록 바꿔 프로젝트마다 독립된 대화를 갖고, 새로고침/재접속에도 같은
프로젝트의 대화를 이어받는다. ws_ 접두어는 유지(tool-scope 도구 개방 +
메인 사이드바 제외 근거). 프로젝트 전환/생성/삭제 시 채팅창을 해당
세션 기록으로 리셋(refreshChatForProject), 연속 전환 레이스는 시퀀스
가드로 처리. 앱세션 저장소(app-sessions.json) 의존 제거.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 19:39:10 +09:00
kimandClaude Code 22cb075835 fix: 작업실 앱 버그 4종 — STL선택 프로젝트 오염, 부품 필드 인젝션, 파일탭 스테일, parts 섀도잉
- switchProject/createProject/deleteProject에서 stlSelection 초기화 누락 →
  프로젝트 전환 후 슬라이서 전송 시 옛 프로젝트 relPath가 현재 프로젝트
  경로로 재해석되어 같은 이름의 다른 파일이 슬라이싱될 수 있던 버그
- renderParts에서 status/qty/unitPrice 이스케이프 누락(채팅 도구가 임의
  문자열 쓰면 HTML 주입 경로) → escAttr 적용
- 프로젝트 생성/삭제 시 파일 탭이 열려있으면 loadFiles 미호출로 옛
  프로젝트 파일 목록 남던 버그 → switchProject와 동일 갱신 추가
- callChat 내 SSE 파싱 변수 parts가 전역 부품 배열 parts를 섀도잉 →
  sseParts로 개명

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 19:36:01 +09:00
kimandClaude Code 54263ae0b4 fix: 작업실 앱 6종 버그 수정 — renderMd 코드/리스트, softRefresh 덮어쓰기, escJs 인젝션, 테마 아이콘
renderMd: 코드펜스 Placeholder 추출→연속 목록 ul 그룹화→단락 분할→블록태그 p
래핑 제외→Placeholder 복원 순으로 재작성. softRefreshWorkshop 추가(저장 대기
플러시 후 변경시에만 재렌더, expandedParts/notes 보존). escJs 추가해 삭제/토글
onclick 인젝션 방지. 테마 토글 아이콘(🌙/☀️) 동기화.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 19:28:54 +09:00
kimandClaude Code 7587adb624 feat: 작업실 CAD 도구 3종 업그레이드 — 불리언연산·체적/밀폐분석·파라메트릭 컴파일
stl_cad: cut/boolean 액션 추가, info에 체적·밀폐(manifold)·퇴화삼각형·단위힌트,
render 멀티뷰(views), add_hole 관통모드(through). scad_to_stl: defines(임의 변수
오버라이드)·preview·PART 자동목록. write_scad: 검증 STL 분석해 치수/체적/단위힌트
보고, 빈 형상을 문법오류로 오분류하던 것 수정. core에 analyzeStl 추가(기존
readStlBBox는 라우트 호환상 유지). build-tools.ts 프롬프트 스키마 사본 동기화.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 19:28:18 +09:00
kimandClaude Code 703ea0f671 fix: stl_cad/scad_to_stl/write_scad를 작업실 세션 전용으로 제한
사용자 요청 "작업실 전용으로 하자" — 지금까지 이 셋은 키워드 게이트
(hasStlCadKeyword)라 메인 채팅이든 다른 앱이든 메시지에 "stl"/"cad" 등만
있으면 어디서든 호출 가능했음. isWorkshopAppSession(ws_ 세션) 게이트로
교체 — 이제 워크숍 세션에서만 뜨고, 메인 채팅에선 같은 키워드를 써도 안 뜸.
k2-app.html의 "🔧 STL 확인" 웹 UI 패널은 routes-cad.ts 직접 호출이라 이
게이트와 무관, 영향 없음.

더는 안 쓰는 hasStlCadKeyword/stlCadPattern 정리. lastAssistantMsg 필드는
남겨둠(다른 재시도-유지 게이트가 필요해지면 재사용 가능, 비용 없음).

실측: 메인채팅 세션은 "scad를 stl로" 요청해도 세 도구 다 false, 워크숍
세션은 키워드 없는 "안녕"에도 세 도구 다 true 확인.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 17:21:22 +09:00
kimandClaude Code bb6a64bff4 feat: write_scad 도구 추가 + 전체조립/부품 저장위치 분리 + PART 오타 흡수
write_scad — .scad 신규작성/수정 전용 도구(사용자 요청: "그래도 툴 하나
만들어주자"). python_eval의 open(path,'w') 우회는 os.makedirs가 막혀있어
폴더 없으면 실패하는 등 신뢰도가 낮았음. 저장 직후 실제 OpenSCAD 컴파일까지
해서 문법오류/빈 형상을 그 자리에서 알려주고 모델이 고쳐서 재시도하게 함.
registry.ts+build-tools.ts(실제 스키마) 둘 다 등록, tool-scope.ts 게이트 추가.
직접 테스트(정상/문법오류)+실채팅 테스트 통과.

PART 값 오타 흡수 — stl_cad의 compile 액션과 scad_to_stl 둘 다: scad 파일에
없는 PART 문자열을 넘기면 OpenSCAD가 에러 없이 조용히 빈 STL을 만드는 함정이
있었음(실측: 워크숍 채팅이 "tilt_arm"을 "Tilt Arm"/"tiltarm"/"tilt-arm"으로
반복해서 틀려 빈 STL만 생성). scad 소스에서 PART 분기 문자열을 미리 뽑아
대소문자/공백/하이픈 차이를 정규화해서 매칭하고, 그래도 안 맞으면 openscad를
돌리지도 않고 사용 가능한 PART 값 목록을 바로 에러로 돌려줌.

전체조립 vs 부품 저장위치 분리 — handle-chat.ts의 workshopProjectCtx 힌트:
part 지정해서 부품 하나만 만들 때는 CAD/print/에, part 없이 전체조립본
(시각화용, 인쇄 대상 아님) 만들 때는 CAD/ 바로 밑에 저장하도록 구분.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 17:17:58 +09:00
kimandClaude Code 7485192e4d fix: 작업실 채팅의 scad→STL 변환을 끝까지 신뢰성 있게 만듦
하나의 요청("scad를 STL로 만들어줘")을 실제로 안정적으로 처리하기까지
여러 겹의 문제가 있었음 — 전부 실측(ws_ 세션 실제 호출)으로 확인하고 고침.

1. 진짜 원인: build-tools.ts에 stl_cad의 완전히 별개인 낡은 하드코딩
   스키마 사본이 있어서, stl-cad.ts의 jsonSchema를 아무리 고쳐도 모델에게
   전달 안 되고 있었음(registry.ts 등록과 별개로 이 파일도 갱신해야 함).

2. 액션 enum(stl_cad의 action="compile") 선택 자체가 경량모델에 어려운
   것으로 3연속 실측 확인 — 전용 단일목적 도구 scad_to_stl 신설(scad_path/
   output_path/part만 받음, 선택지 없앰). tool-scope.ts에 게이트 추가.

3. python_eval의 subprocess 차단 메시지에 대안 도구 힌트 추가 — 처음엔
   워드바운더리(\b) 정규식이 pattern.source 문자열 자체와 안 맞는 버그로
   한 번도 안 붙었던 것도 같이 고침.

4. hasStlCadKeyword가 짧은 재시도("다시 시도해봐")에서 원래 요청이 2턴
   윈도우 밖으로 밀려나며 도구가 사라지는 문제 — 직전 어시스턴트 답변도
   같이 보게 함(lastAssistantMsg를 tool-scope.ts까지 스레딩).

5. AUTO-RECOVER가 scad_to_stl의 정상 결과("200.0×110.0×277.5 mm", 방금
   만든 파일 실측치)를 "검증 안 된 스펙 주장"으로 오판해 웹서치를 강제
   재프롬프트하던 것 — retry-decisions.ts의 GROUNDING_TOOL_PATTERN에 추가.
   이 패턴이 "검색결과 읽는 법" 안내 트리거와 공유돼있어서, CAD 결과에
   그 안내가 붙어 모델이 엉뚱하게 답하는 부작용도 같이 분리해서 막음.

6. 워크숍 세션 컨텍스트(handle-chat.ts workshopProjectCtx) 확장: 활성
   프로젝트 안 scad 파일이 하나뿐이면 되묻지 않고 자동 사용, STL
   저장경로는 원본 scad 옆 print/ 서브폴더로 유도(CAD/print=인쇄용,
   CAD/reference=참고용 관례 유지).

7. workshop-app.html: 채팅으로 새 파일이 생겨도 파일탭이 이미 열려있으면
   자동 새로고침 안 되던 것 수정.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 17:05:45 +09:00
kimandClaude Code 71afd45959 fix: 앱 채팅 "대화 지우기" 버튼이 실제로는 안 지워지던 버그
DELETE /api/chat/sessions/:id가 세션 파일만 unlink하고 session.ts의
in-memory Map은 그대로 둬서, 같은 세션ID를 계속 재사용하는 앱(투자자·의사·
날씨·회계사·변호사·마음·작업실·탐정, 총 8개)에서 삭제 후 같은 세션에 새
메시지를 보내면 다음 saveSession() 때 메모리에 남아있던 옛 대화가 파일로
그대로 부활했음.

파일 삭제 후 clearHistory()(메모리+파일 둘 다 제대로 비우는 기존 함수)도
호출하도록 수정. 실제로 메시지→삭제→같은 세션 새 메시지 시나리오로 재현
검증(부활 안 됨 확인).

워크숍 앱 채팅 헤더 CSS도 방어적으로 수정 — 삭제 버튼에 flex-shrink:0이
없어서 좁은 폭에서 라벨/모델pill에 밀려 안 보일 수 있던 것 고침.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 16:19:35 +09:00
kimandClaude Code a2439583b3 feat: stl_cad 도구에 scad→STL 컴파일 액션 추가
기존엔 info/render/add_hole만 있어서 기존 STL만 다뤘고, .scad 소스를 STL로
직접 컴파일하는 기능이 없었음 — 워크숍 채팅이 ".scad를 STL로 만들어달라"는
요청에 "제가 직접 하는 기능은 없다"고 정확히 답했던 이유.

compile 액션 추가: scad_path(.scad 소스) + part(선택, PART 변수 있는 scad용
-D 'PART="..."' 전달) + output_path로 openscad CLI 호출, 바이너리 STL 생성.
기존 runOpenscad() 헬퍼 재사용.

DIY 스캐너 프로젝트(scanner-diy-rig.scad)에도 PART 분기 추가하고 이 기능으로
프린트 부품 4종(base_plate/turntable/post/tilt_arm) 실제 생성 — 전부 230mm
베드 이내 확인.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 16:19:27 +09:00
kimandClaude Code 1f87651812 fix: 작업실 채팅 크로스 프로젝트 파일 노출 코드 백스톱
이전 커밋(8866266)의 시스템 프롬프트 지시만으로는 절반만 먹혔음 — 경량모델이
"다른 프로젝트는 후보로 내놓지 말 것" 지시를 받고도 참고용으로 계속 언급함
(실측 확인). 로컬모델은 멀리 있는 프롬프트 지시보다 데이터 바로 옆 신호를
더 잘 따르는 기존 패턴(groundingResultReadingRule)과 동일한 원리로 코드
백스톱을 추가.

- getWorkshopCrossProjectWarning(): ws_ 세션에서 도구 인자/결과/데이터에
  활성 프로젝트가 아닌 workshop/<id>/ 경로가 섞여 있으면 경고를 결과 텍스트
  맨 앞에 직접 붙임(완전 차단은 아님 — 사용자가 명시적으로 다른 프로젝트나
  전체 워크스페이스를 요청하면 정상 동작)
- list 도구만이 아니라 python_eval(os.listdir 등, default 분기 공유)과
  shell 양쪽에 적용 — list만 막았더니 모델이 python_eval로 똑같이 다른
  프로젝트를 들여다보는 것을 확인해서 범위를 넓힘

3가지 시나리오로 실측 검증: 다른 프로젝트 명시 요청(정상 응답) / 전체
워크스페이스 명시 요청(정상 응답) / 모호한 질문(현재 프로젝트만 응답,
원래 버그 시나리오 해결).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 15:56:19 +09:00
kimandClaude Code 88662663b1 fix: 작업실 채팅에 현재 프로젝트 컨텍스트 주입
ws_ 세션(작업실 앱 전용 채팅)이 "지금 어떤 프로젝트를 보고 있는지" 정보를
시스템 프롬프트에 전혀 못 받고 있었음 — tool-scope.ts의 isWorkshopAppSession은
"작업실 세션이다"만 판별하지 활성 프로젝트 ID는 안 넘김.

그 결과 모델이 STL/scad 파일을 찾을 때 워크스페이스 전체를 뒤져서 다른
프로젝트 파일까지 후보로 내놓는 혼동이 있었음(예: proj_scan_dental_diy
채팅에서 STL 요청했는데 proj_scan_dental 쪽 scad까지 같이 나열).

workshop.json의 activeProjectId를 읽어 "지금 프로젝트는 이거고 파일은
workshop/<id>/ 아래만 볼 것"을 callerContext에 주입하도록 수정.
실제 ws_ 세션으로 테스트해 크로스 프로젝트 혼동 없어진 것 확인.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 15:45:19 +09:00
kimandClaude Code c79364312c feat: 작업실 앱 파일 탭에 K2 슬라이서 연동
- slicer-view.js: ?load=<경로1,경로2,...> URL 파라미터 지원 추가 —
  cad-editor-view.js의 ?path= 패턴과 동일한 취지로, 페이지 진입 시
  지정한 워크스페이스 STL들을 /api/cad/file로 받아 자동으로
  슬라이서 판에 배치한다
- workshop-app.html: 파일 탭 STL 카드에 다중선택 체크박스 추가 +
  "선택 STL 슬라이서로" 툴바 버튼 — 체크한 부품 STL들을 K2 슬라이서
  팝업(slicer-app.html)에 한 번에 올려 같은 판에서 배치/슬라이싱

기존 slicer-app.html/PrusaSlicer 파이프라인은 그대로 재사용, 새 백엔드
라우트 없이 routes-cad.ts의 /api/cad/file만 재사용.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 14:28:49 +09:00
kimandClaude Code 80ea4322bc feat: 작업실 앱에 프로젝트별 파일 브라우저 + K2 CAD 편집기 연동
- routes-workshop.ts: 파일 목록/업로드/폴더생성/삭제 API 추가
  (case-storage.ts 재사용, appType='workshop', caseId=projectId)
- workshop-app.html: 파일 탭 추가 — 드래그앤드롭 업로드, 폴더 그룹핑,
  사진 썸네일, STL 클릭 시 Three.js CAD 편집기 팝업으로 연동
- cad-editor-view.js: ?path= 쿼리로 특정 STL 자동 로드 지원
- server.ts: registerWorkshopRoutes에 isPathInsideDir 전달
- stl-cad-core.ts: 평면뷰 렌더 카메라가 원점이 아닌 바운딩박스
  중심을 look-at하도록 수정 — 원점에서 떨어진 모델 위쪽 잘림 해결

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 13:23:05 +09:00
kimandClaude Sonnet 5 bdf87d824e fix: 작업실/의사 앱 세션이 메인 채팅 사이드바에 노출되던 버그 수정
APP_SESSION_PREFIXES 목록에 ws_(workshop-app.html)와 dr_(doctor-app.html)가
빠져 있어서 두 앱의 전용 채팅 세션이 메인 채팅 세션 목록에 그대로 섞여
보이고 있었음. 실제 8개 앱 HTML의 세션 프리픽스와 대조해서 누락분 추가.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 22:18:26 +09:00
kimandClaude Sonnet 5 5424583b9f feat: 검색 지속성 일반 규칙 추가 — 클로드 검색 노하우 이식
한 번 검색해서 끝내지 말고(문구 바꿔 재검색), 스니펫이 부실하면 web_fetch로
실제 페이지를 읽으라는 일반 규칙(searchPersistenceRule)을 system-prompt에
추가. 기존 fileSearchRule은 겹치는 부분 덜어내고 파일검색 고유 내용(저장소
이름, 다운로드 버튼 차단은 정상)만 남김.

실측: K2 배기 덕트 STL 검색 재현 시 이번엔 검색 1회로 Printables/MakerWorld
실제 모델 3종+링크를 찾아 답변(이전엔 1회 검색 후 포기). 일반 쿼리("오늘
환율")는 과다검색 없이 기존과 동일하게 2회로 정상 답변 — 회귀 없음 확인.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 21:54:59 +09:00
kimandClaude Sonnet 5 dc76158c6f feat: 작업실 채팅에 경량 모델 pill 추가
메인 채팅의 전체 설정 모달을 그대로 들여오는 대신, 같은 백엔드
엔드포인트(/api/settings/model, /api/settings/provider, /api/models/test)만
재사용해 채팅 헤더에 작은 pill+드롭다운을 추가. 클릭하면 ollama provider가
보유한 로컬+클라우드 모델 태그 목록을 보여주고 선택 시 전역 models.primary를
바꾼다. curl로 GET/POST 전 구간 실측 확인(목록 12개 모델 조회, 전환→원복).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 21:50:40 +09:00
kimandClaude Sonnet 5 3330910c3b fix: 파일/모델 검색 요청에 다중 쿼리 시도 가이드 추가
작업실 채팅(gemma4)이 K2 배기 덕트 STL을 검색 1회만 하고 포기하는 걸
로그로 확인(SEARCH-ABANDONMENT 가드가 재시도시켜도 결론은 동일). STL/3MF
등 다운로드형 파일을 찾는 요청에서는 검색어 2-3종 + 알려진 저장소
(Printables/Thingiverse/MakerWorld/Creality Cloud) 명시 검색을 시도하고,
직접 파일 링크를 못 찾아도 모델 페이지 URL을 주는 걸 성공으로 취급하도록
system-prompt에 메시지 게이트 규칙 추가.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 21:28:01 +09:00
kimandClaude Code c54c07338a refactor: K2 3MF 지원을 패스스루 대신 서버 변환 방식으로 전환
3MF를 미리보기 없이 원본 그대로 보내는 대신, PrusaSlicer --export-stl로
서버에서 STL로 변환한 뒤 기존 STL 파이프라인(미리보기+다중배치편집)에
그대로 태우도록 개선. 사용자 쪽에서도 '그냥 STL로 변환하면 안 되나' 제안.

- routes-k2.ts: POST /api/k2/convert-3mf-to-stl 신규(임시파일→변환→응답,
  cleanupTemp로 정리)
- slicer-view.js: 파일선택 시 .3mf면 변환API 먼저 태운 뒤 기존
  loadModel()/addInstance()로 합류 — STL과 완전히 동일하게 취급됨.
  이전 커밋의 '미리보기 없는 패스스루'(pending3mf 상태/분기) 전부 제거.

print3d_model 채팅도구의 .3mf 다운로드 확장자 인식은 유지(변경 없음).

E2E 검증: 변환 API 단독 호출로 3MF→STL 정상 확인(삼각형 수 일치).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 20:43:58 +09:00
kimandClaude Code b2285a8059 feat: K2 슬라이서에 3MF 파일 지원 추가
PrusaSlicer CLI가 3MF를 네이티브로 지원해서(확장자로 자동판별) 서버는
format 쿼리파라미터(stl 기본값, 3mf)로 저장 확장자만 바꾸면 됨.

- routes-k2.ts: /api/k2/slice에 format 파라미터 추가
- print3d.ts: stl_url 확장자가 .3mf면 3mf로 다운로드/슬라이스
- slicer-view.js: 파일선택 .3mf 허용. Three.js STLLoader로는 3MF를
  못 읽으므로 미리보기/배치편집 없이 원본 그대로 패스스루(arrange=1,
  STL 다중 인스턴스 편집과 상호배타적)

E2E 검증: 3MF 큐브 슬라이스 200 성공, 기존 STL 경로 회귀 없음 확인.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 20:39:23 +09:00
kimandClaude Code af6627f0ce feat: print3d_model에 작업실 프로젝트 하위폴더 저장 옵션 추가
project_name을 주면 print3d/<프로젝트명>/ 하위폴더에 STL/gcode를 저장해
'작업실' 대시보드 프로젝트와 파일을 묶는다. 프로젝트명 매칭은
workshop-project.ts의 부분일치 규칙 재사용(0건/2건 이상이면 에러).
project_name 생략 시 기존처럼 공용 print3d/ 폴더(변경 없음).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 22:50:00 +09:00
kimandClaude Code 77c573f4a5 feat: "STL 확인" → Three.js 3D STL 편집기로 전환
- 신규 팝업 cad-editor-app.html + cad-editor-view.js: Three.js 3D 뷰포트(원점 그리드+축),
  워크스페이스 STL 로드/측정, 변환(이동·회전·배율·뒤집기·거울·중앙정렬), 구멍(표면 클릭 픽),
  박스 컷, 두 STL 합/차/교집합, 새 파일로 저장.
- 좌표계: 모델 원본 프레임 유지(슬라이서처럼 베드 중앙정렬 안 함). STL Z-up↔three Y-up
  매핑 = rotateX(-90°), 역매핑 STL=(threeX,-threeZ,threeY). 변환은 클라이언트 bake, boolean은
  bake→서버 OpenSCAD→결과 바이트 재로드(변환 리셋).
- stl-cad-core: applyBooleanOp(op∈hole/cut/union/difference/intersection) 추가, addHoleToStl를
  그 위의 얇은 래퍼로 리팩터(채팅도구 하위호환 유지).
- routes-cad: GET /api/cad/file(원본 다운로드, dotfiles:allow), POST /api/cad/save(bake된 STL 저장),
  POST /api/cad/apply(바디=primary 바이트, ?op=&params, ?second=) 추가. 기존 list/info/render/
  add-hole은 건드리지 않음.
- k2-app: "🔧 STL 확인" 버튼 → "🔧 STL 편집" 팝업(openCadEditorPopup). 옛 PNG 모달 마크업·JS·CSS 제거.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:53:24 +09:00
kimandClaude Code 25a7fbb71d feat: k2 슬라이서에 STL 복수 로드 + 위아래 뒤집기 추가
- 파일 입력 multiple: 여러 STL을 한 번에 불러와 각각 별도 모델(색으로 구분)로 등록,
  인스턴스 1개씩 배치. 모델/인스턴스 구조 분리(userData.modelIdx)로 서로 다른 기하 공존.
- ↕뒤집기 버튼: X축 180° 회전 후 reLiftInstance로 바닥이 베드에 붙도록 position.y 보정.
  bake 시 mesh.matrix(전체 회전·배율·liftY 포함)를 정점에 적용 → 뒤집기 상태까지 보존.
- 전체삭제 버튼 추가. 복제는 회전·뒤집기·배율 전부 복사.
- 기존 좌표계 규약(three Z = BED - bedY) 유지, 다중 모델에서도 인스턴스·툴패스 정렬 유지.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:38:10 +09:00
kimandClaude Code fedc03d5f4 feat: k2 슬라이서에 Cura식 모델 배치(복제/이동/회전/배율) 추가
단일 stlMesh 대신 인스턴스 모델 도입 — baseGeo 공유 위에 다중 THREE.Mesh를
position/rotation/scale로 배치. 뷰에서 클릭 선택·드래그 이동, 사이드 패널에서
숫자 입력(X/Y/회전/배율) + 복제/↺90/↻90/중앙/자동배치/삭제 버튼.

슬라이스 시 모든 인스턴스를 단일 바이너리 STL로 구워(bake) 전송 — 각 정점을
베드 좌표(bedX=threeX, bedY=BED-threeZ, 높이=threeY)로 변환. 서버 /api/k2/slice
에 arrange=0 추가 → PrusaSlicer --dont-arrange 로 원본 XY 보존(기존 --duplicate
대체, --merge 세그폴트 회피).

실측 검증: 큐브 STL 0..20 을 arrange=0 슬라이스 → 베드 0..20 유지(재중앙화 ❌).
2-큐브 합본 STL → 두 클러스터가 각각 지정 베드 좌표에 배치됨 확인.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:29:05 +09:00
kimandClaude Code d93e6bcdf9 fix: 슬라이서 툴패스가 빌드플레이트 중심에서 벗어나던 문제 수정
PrusaSlicer가 모델을 베드 중심(130,130)에 두고 gcode 좌표를 베드 좌표계
(0~260) 그대로 내보내는데, 렌더링에서 거기에 +BED/2(+130)를 또 더해서 모델이
(260,260) 모서리 쪽으로 밀려 보였음("중심에서 벗어나 있네").

수정: gcode X → three X 오프셋 없이 그대로, gcode Z(높이) → three Y,
gcode Y → three Z = BED - gcodeY. Z를 뒤집는 건 STL 미리보기(rotateX -90)가
STL +Y를 three -Z로 보내는 것과 정렬하기 위함. 실측: 모델 three X/Z 둘 다
130.00 중심, 0~260 베드 안. STL 미리보기와 동일 위치로 겹침.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:19:31 +09:00
kimandClaude Code 9a0026b225 refactor: k2 슬라이서를 k2-app 오버레이 → 전용 팝업창으로 분리
"🔪 슬라이싱" 버튼이 k2-app 위에 오버레이를 띄우던 방식을 window.open() 전용
팝업창(slicer-app.html)으로 분리. k2-app(카메라+Fluidd 모니터)과 슬라이서
(3D 편집 도구) 역할 분리 + 오버레이 블랙스크린 원인 해결.

- 블랙스크린 원인: 옛 방식은 #slicer-overlay 가 display:none 이다가 .show로
  토글되는데, 그 직후 initScene이 실행되면서 뷰포트 clientWidth/Height가 0이라
  renderer.setSize가 스킵→캔버스 0size. 전용 팝업은 #slicer-root 가 처음부터
  position:fixed;inset:0 이라 크기가 바로 잡힘.
- web-ui/html/slicer-app.html 신규: Three.js+슬라이서 모듈만 로드하는 풀뷰포트
  팝업 페이지. 인증은 같은 오리진 smallclaw_session 쿠키가 팝업에 자동 상속.
- slicer-view.js: openSlicer/closeSlicer 오버레이 토글 → initSlicer 자동 초기화
  (DOMContentLoaded). .sv-shell을 #slicer-root에 빌드. 캔버스 CSS width/height
  100%로 셀 채우도록 수정. ✕버튼=window.close().
- k2-app.html: #slicer-overlay div·Three.js 스크립트 제거, 버튼→openSlicerPopup().
  CAD 모달·카메라·Fluidd iframe은 그대로.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:13:21 +09:00
kimandClaude Code 8fbf581ca1 feat: k2-app Cura 스타일 3D 슬라이싱 GUI (Three.js 툴패스 레이어뷰)
폼 기반 프로필 에디터를 CuraEngine GUI처럼 풀뷰포트 3D 뷰로 교체.
Three.js r128 UMD 벤더(/vendor/three/)로 260³ 빌드 플레이트 위 STL을
3D로 띄우고, 슬라이스 후 gcode를 파싱해 ;TYPE:별 툴패스를 레이어별로 시각화.
슬라이스↔업로드 흐름을 Cura식으로 분리(slice≠전송) — 슬라이스만 하고
툴패스 검토 뒤 명시적으로 업로드/출력 시작.

- web-ui/js/k2/slicer-view.js: 3D 씬 + STL 로드 + gcode 툴패스 파서 + 레이어 슬라이더
  + 프로파일 패널(스키마·오버라이드·저장) 이전. ;LAYER_CHANGE 마커로 레이어 경계
  감지(Z변화 감지는 start_gcode 프라이밍 Z3→Z0.2→Z1이 가짜 층을 만드는 걸 회피).
- routes-k2.ts: /api/k2/slice 를 upload=0 기본(슬라이스만)으로 변경,
  POST /api/k2/upload·GET /api/k2/gcode/:filename 신규.
  gcode 라우트의 res.sendFile에 {dotfiles:'allow'} 추가 — 워크스페이스가
  .smallclaw(도트파일 디렉토리) 아래라 send 라이브러리 기본값 'ignore'가
  404 "Not Found"로 씹어버리는 걸 해결.
- k2-slicer-core.ts: parsePrusaFilament(mm/cm3) 추가.
- k2-app.html: 슬라이싱 모달→#slicer-overlay 컨테이너로 교체, Three.js 스크립트 로드.
  CAD 모달·카메라·Fluidd iframe은 그대로 유지.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:08:23 +09:00
kimandClaude Code 5920540689 feat: k2-app 슬라이싱 풀 프로필 에디터 추가
단순 6파라미터 모달을 PrusaSlicer --load <ini> 기반 풀 에디터로 교체.
베이스 ini(K2_BASE_INI, 약 60키 큐레이션)를 TS 문자열로 임베드하고
buildProfileIni가 오버라이드 머지 + canonical start_gcode(13줄 K2 프라이밍
시퀀스, placeholder 온도 치환)를 강제 주입 — k2_config.ini 원본의 3줄
start_gcode를 그대로 쓰면 프라이밍이 날아가 출력이 망가지는 회귀 회피.

웹 UI: 9개 섹션(기본/소재·온도/서포트/브림/속도/팬/표면/리트랙션/고급)
약 40파라미터 편집 + 프로파일 저장/불러오기/삭제. 속도·소재 프리셋 연동.

라우트: /api/k2/profile/base·/profiles CRUD + /api/k2/slice?profile= 변경.
print3d_model 채팅도구는 기존 buildBaseArgs 경로 유지(동작 변경 회피).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 20:47:31 +09:00
kimandClaude Sonnet 5 65d9325f8b feat: k2-app에 STL 확인 웹 UI 추가(드롭다운+미리보기+구멍뚫기)
채팅으로 좌표를 말로 불러주는 게 너무 번거롭다는 피드백으로, stl_cad
채팅도구와 같은 기능을 드롭다운+버튼 UI로도 쓸 수 있게 함.

- stl-cad-core.ts: stl-cad.ts에서 OpenSCAD 실행/STL 파싱/렌더링 로직을
  추출한 공용 모듈 + 워크스페이스 내 STL 파일 재귀 검색(findStlFiles)
  추가. stl-cad.ts(채팅도구)와 routes-cad.ts가 공유
- routes-cad.ts: GET /api/cad/list(STL 목록), /api/cad/info(치수),
  /api/cad/render(PNG 스트리밍), POST /api/cad/add-hole
- k2-app.html: "🔧 STL 확인" 모달 — 파일 드롭다운 선택 → 7방향 뷰 버튼 →
  실시간 렌더 미리보기+치수, 접이식 "구멍 뚫기" 폼(x/y/z/지름/길이/축).
  브라우저로 실제 렌더링/각도전환 확인함

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 20:21:02 +09:00
kimandClaude Sonnet 5 c6e11302d5 feat: stl_cad 채팅도구 추가 — OpenSCAD 기반 STL 확인/렌더링/구멍뚫기
"작업실"의 다음 단계로 남겨뒀던 CAD 연동을 시작. FreeCAD 대신 OpenSCAD를
백엔드로 씀 — 텍스트 스크립트 기반 CSG라 AI가 파라메트릭 스크립트를 짜서
바로 실행하는 이 워크플로에 훨씬 적합함(GUI/Python API가 무거운 FreeCAD
대비). 오늘 ESP32 카메라 하우징 렌즈 소켓 위치를 실제로 이 방식(OpenSCAD
--camera 렌더 + 메시 좌표 분석)으로 조사해본 뒤 정식 도구로 정리했다.

- info: 바운딩박스/치수/삼각형수 조회(바이너리 STL 직접 파싱, 의존성 없음)
- render: iso/top/front 등 각도로 PNG 렌더링, 모델이 직접 보게 함
  (buildImageMarkdown 재사용). QT_QPA_PLATFORM=offscreen으로 헤드리스
  렌더 확인(xvfb 불필요)
- add_hole: 지정 좌표에 원통 구멍을 뚫어 새 STL로 저장(difference()).
  테스트 큐브로는 정상 작동 확인했으나, 오늘 다룬 ESP32 STL 자체가
  CGAL이 못 받아들이는 미세 메시 결함이 있어(admesh로도 복구 안 됨)
  그 파일에서는 실패 — 원인이 도구가 아니라 소스 STL 품질 문제임을
  확인함

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 20:10:14 +09:00
kimandClaude Sonnet 5 7ec72e776a refactor: "로봇 프로젝트" 단일 대시보드를 여러 프로젝트를 오가는 "작업실"로 일반화
로봇 프로젝트 하나만 다루던 대시보드/도구를 로봇 외 다른 메이커 프로젝트
(3D프린팅, 밀링 등)도 같은 틀로 관리할 수 있게 재설계. 사용자가 이름을
"작업실"로 정함.

- workshop-storage.ts(구 robot-storage.ts): 단일 문서 대신
  {projects:[], activeProjectId} 구조. 기존 실사용 중이던
  robot-project.json이 있으면 빈 시드로 덮어쓰지 않고 그 실제 데이터를
  그대로 첫 프로젝트("로봇 프로젝트")로 마이그레이션
- routes-workshop.ts(구 routes-robot.ts): GET/PUT /api/workshop/data
- workshop-project.ts(구 robot-project.ts) 채팅도구: project_name 파라미터
  추가(생략시 활성 프로젝트 대상), list_projects/create_project/
  set_active_project 액션 신설
- workshop-app.html(구 robot-app.html): 헤더에 프로젝트 선택 드롭다운 +
  새 프로젝트/삭제 버튼 추가. 장비(K2/밀링) 탭은 프로젝트와 무관하게
  전역 유지
- 세션 접두사 rb_→ws_(tool-scope.ts의 isWorkshopAppSession), 도구명
  robot_project→workshop_project
- 마이그레이션+새 프로젝트 생성/전환/삭제+채팅 연동까지 브라우저로 실제
  확인함

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 19:04:49 +09:00
kimandClaude Sonnet 5 4911ab005a feat: robot_project 도구에 add_link/delete_link 액션 추가
부품 카드에 추가한 참고링크(구매처/대체품/업그레이드/레포 등, 076a53b)를
이제 채팅으로도 붙이고 뗄 수 있다 — "이거 어디서 팔아?" 하고 물어서
web_search로 찾은 뒤 이 액션으로 바로 그 부품에 링크를 붙이는 흐름을
위한 것. delete_link는 라벨 일부일치로 찾는다(다른 액션들과 동일 패턴).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 18:12:31 +09:00
kimandClaude Sonnet 5 076a53b8de feat: 로봇 부품 카드에 참고링크(구매처/대체품/업그레이드/레포 등) 추가
각 부품 카드에 라벨+URL 쌍을 여러 개 붙일 수 있는 "정보" 섹션 추가 —
파는 곳, 대체품, 업그레이드 옵션, 관련 GitHub 레포 등 무엇이든 자유롭게
기록. URL을 입력하면 옆에 ↗ 열기 버튼이 자동으로 뜬다(타이핑 중 포커스가
끊기지 않게 그 버튼만 DOM에서 직접 갱신, 전체 리렌더 안 함).

- robot-storage.ts: RobotPart에 links: RobotPartLink[] 필드 추가(기존
  저장된 데이터는 링크 없이 시작, 별도 마이그레이션 불필요)
- robot-project.ts: add_part 기본값에 links:[] 추가, get 응답에 부품별
  링크 목록도 같이 표시
- robot-app.html: 카드 펼침 영역에 라벨/URL 입력 행 + 추가/삭제 버튼

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 18:06:28 +09:00
kimandClaude Sonnet 5 68c1ae9b21 fix: K2 원격 출력 시작 시 필라멘트 삽입 확인 안내 추가
K2 콤보 CFS 시스템은 출력 시작할 때 터치스크린에서 "필라멘트 삽입"을 직접
눌러 확인해야 실제 급지가 되는데, API로 원격 시작하면 아무도 그 확인을
못 눌러줘서 압출이 안 되고 Klipper 안전장치로 히터가 자동으로 꺼지는 걸
오늘 실제로 겪음(2026-09-20). 웹 UI 슬라이싱 모달과 print3d_model 채팅도구
둘 다 출력 시작 성공 메시지에 "터치스크린 확인하라"는 안내를 추가.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 17:55:19 +09:00
kimandClaude Sonnet 5 164ce6dcc1 feat: k2-app 웹 UI에 STL 슬라이싱 기능 추가
로컬 PC의 STL 파일을 채팅 없이 K2 앱에서 바로 업로드→슬라이싱→K2 업로드까지
할 수 있는 모달 추가. print3d_model 채팅도구와 로직을 공유하도록
k2-slicer-core.ts로 공통 부분(프린터 프로파일, PrusaSlicer 실행, Moonraker
업로드, 시간 파싱)을 분리했다.

- k2-slicer-core.ts: print3d.ts에서 추출한 공용 슬라이싱 로직 + 오늘 실측한
  FAST_SPEED_ARGS(퍼리미터/인필/트래블 속도업, 브릿지는 유지)
- routes-k2.ts: POST /api/k2/slice(STL 원본 바이트 바디+쿼리스트링 설정,
  --duplicate로 동일 파츠 복제 지원 — 서로 다른 파츠 합치기(--merge)는 이
  헤드리스 환경에서 세그폴트 나서 미지원), POST /api/k2/print-start(Moonraker
  프록시, mixed-content 회피)
- k2-app.html: 헤더에 "🔪 슬라이싱" 버튼 → 모달(파일업로드/수량/소재/레이어
  높이/인필/속도/서포트) → 결과+출력시작 버튼. 실제 파일 업로드로
  슬라이싱→K2업로드까지 브라우저에서 확인함(출력 시작은 실제 프린트 중이라
  테스트 안 함)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 17:13:13 +09:00
kimandClaude Sonnet 5 9dfef8b568 refactor: robot-app 부품 리스트를 표에서 펼침형 카드로 변경
행마다 항상 다 펼쳐져 있던 표 대신, 이름/상태뱃지/소계만 보이는 헤더를
클릭하면 수량·단가·상태·메모 편집 필드가 펼쳐지는 카드로 교체. 펼침
상태는 화면 전용(서버 저장 안 함)이라 다시 그려도 유지됨.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 14:58:38 +09:00
kimandClaude Sonnet 5 54285c8a4c feat: robot-app에 내장 채팅 패널 추가 (doctor/lawyer 앱과 같은 사이드 패널 패턴)
부품 리스트를 보면서 바로 옆에서 로봇 프로젝트 얘기를 할 수 있게 리사이즈
가능한 채팅 패널을 대시보드에 붙임. 세션ID를 "rb_"로 시작하게 해서
tool-scope.ts가 이 세션 안에서는 키워드 매칭 없이 robot_project 도구를
항상 열어주도록 예외 처리(doctor_app의 'dr_'/lawyer_app의 'lw_'와 동일한
패턴) — "이 부품 지워줘"처럼 짧게 말해도 바로 동작한다.

채팅 응답이 끝나면 대시보드(부품표/예산/작업)를 자동으로 다시 불러와서
채팅으로 바꾼 내용이 즉시 화면에 반영되게 함.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 14:48:50 +09:00
kimandClaude Sonnet 5 e3d5da38cf feat: robot_project 전용 채팅도구 추가 — 로봇 프로젝트 대시보드를 채팅으로 편집
로봇 프로젝트("메카넘휠" 이동로봇) 대화에서 부품 상태 변경/추가/삭제, 작업
완료 체크, 새 단계/작업 추가, 메모 추가를 채팅으로 바로 할 수 있게 하는
도구. robot-app.html 대시보드와 같은 저장소(robot-storage.ts로 분리)를
쓰므로 채팅에서 바꾸면 대시보드에도, 대시보드에서 바꾸면 다음 채팅
조회에도 즉시 반영된다.

- robot-storage.ts: routes-robot.ts에서 인라인으로 있던 저장 로직을
  분리(case-storage.ts와 같은 목적) — 라우트와 도구가 공유
- robot-project.ts: get/add_part/update_part/delete_part/add_task/
  set_task_done/add_phase/add_note 8개 액션, 이름 일부일치 검색이라
  내부 id를 몰라도 채팅으로 지칭 가능. 물리적 동작이 없어 confirm 불필요
- registry.ts/build-tools.ts/tool-scope.ts 3곳 등록. 게이트 키워드는
  "로봇청소기"(기존 Valetudo 대화)와 안 겹치게 프로젝트 고유 어휘로만 매칭
- 실제 채팅으로 "예산 얼마야?+메카넘휠 주문완료로 바꿔줘" 동시 요청 테스트,
  정확한 응답+저장 확인

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 14:16:11 +09:00
kimandClaude Sonnet 5 b358cbc968 feat: robot-app에 K2/밀링 장비 연동 위젯(장비 탭) 추가
로봇 프로젝트 대시보드에 실제 공작 장비(K2 3D프린터, CNC 밀링 프린터실
PTZ카메라) 상태를 보는 "장비" 탭을 추가. 우선 가벼운 주기적 스냅샷 방식으로
구현(실시간 WebRTC 상시연결은 자원 부담 커서 보류, 추후 업그레이드 검토).

- routes-k2.ts: GET /api/k2/status — Moonraker print_stats/virtual_sdcard를
  요약해서 반환(온라인여부/상태/파일명/진행률/경과시간)
- nvr.ts: GET /api/nvr/frame/:channelId — go2rtc(127.0.0.1:1985)의
  frame.jpeg를 프록시(밀링 PTZ카메라, channelId=1000 "프린터실"), go2rtc
  포트를 브라우저에 직접 노출하지 않음
- robot-app.html: "장비" 탭 — K2는 20초 간격으로 짧게 WebRTC 연결→ontrack에서
  캔버스 캡처→즉시 종료(상시 연결 안 함), 밀링은 위 프록시로 img 새로고침.
  "장비" 탭을 보고 있을 때만 폴링, 다른 탭으로 나가면 타이머 정지

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 14:07:54 +09:00
kimandClaude Sonnet 5 9641f689b8 feat: 로봇 프로젝트 관리 대시보드(robot-app) 추가
메카넘휠 이동로봇 프로젝트(파이5+RPLidar, 추후 팔 추가)의 부품리스트/
단계별 작업/예산을 관리하는 웹앱. doctor-app의 GET/PUT 단일문서 패턴을
재사용하되 케이스 목록 없이 단일 프로젝트 문서로 단순화했고, 첫 로드시
project_robot_dog.md 메모 기준 실제 진행상황을 기본 시드로 반환한다.

- src/gateway/routes/routes-robot.ts: GET/PUT /api/robot/project,
  사용자별 workspace/.smallclaw/robot-project.json에 원자적 저장
- web-ui/html/robot-app.html: 부품/작업/메모 3탭, 실시간 예산 요약,
  디바운스 자동저장
- index.html 🏠 홈 드롭다운에 🤖 로봇 링크 추가

장비연동/전용챗봇/외부데이터연동/CAD플러그인은 다음 단계로 보류.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 13:46:37 +09:00
kimandClaude Sonnet 5 cd4843d480 refactor: print3d_model 슬라이서를 CuraEngine에서 PrusaSlicer로 교체
CuraEngine CLI는 구조적으로 예상 출력시간 헤더가 항상 더미값(6666)만 남는
한계가 있어(33b5a25 참고) PrusaSlicer(apt, gcode-flavor=klipper 네이티브
지원)로 교체. DISPLAY 없이도 --export-gcode가 완전 헤드리스로 동작함을
확인했고, 슬라이싱 후 파일에 실제 "estimated printing time"이 직접
기록되므로 stderr 파싱 없이 파일에서 바로 읽으면 됨. K2 프로파일/시작-매크로
값은 그대로 유지. 더 이상 쓰지 않는 resources/cura/*.def.json 제거.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 13:29:05 +09:00
kimandClaude Sonnet 5 33b5a258b2 fix: print3d_model 예상 출력시간이 항상 6666초로 나오는 CuraEngine CLI 버그 수정
CuraEngine 5.0.0 소스 확인 결과, CommandLine 백엔드는 isSequential()이 항상
true라 슬라이싱 "시작 전"에 G-code 헤더를 미리 써버리고(processStartingCode
-> getFileHeader, print_time 인자 없음) 끝난 뒤 되돌아가 실제값으로 덮어쓰는
코드가 어디에도 없다. 그래서 ;TIME: 헤더는 항상 gcodeExport.cpp에 하드코딩된
더미값(6666)만 남는다 — 확률적 버그가 아니라 CLI 모드의 구조적 한계였음.

실제 값은 슬라이싱 완료 후 stderr(verbose 로그)에 "Print time (s): N"으로만
찍히므로, G-code 파일 헤더 대신 stderr를 파싱하도록 변경.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 13:16:34 +09:00
kim fdbed6266e feat: STL→K2 프린터 자동 슬라이싱/출력 채팅 도구(print3d_model) 추가
CuraEngine(apt cura-engine, 클로서버)로 STL을 슬라이싱해 K2 콤보(Moonraker)에
업로드하는 도구. action="prepare"(다운로드+슬라이싱+업로드, 출력은 안 함)와
action="start"(confirm=true 필수, 실제 출력 시작)로 분리 — 재료/시간 소모되는
물리적 동작이라 사용자 확인 없이는 출력 시작 안 되게 함.

프린터 프로파일(빌드볼륨/노즐/리트랙션/온도/START_PRINT·END_PRINT 매크로 호출)은
추측이 아니라 K2에 이미 있던 Creality Print 슬라이싱 샘플 gcode 헤더에서 실측값
그대로 추출. fdmprinter.def.json/fdmextruder.def.json은 설치된 CuraEngine
5.0.0과 버전 맞춰서 받아야 함(main 브랜치 최신판 쓰면 설정 스키마 불일치로
슬라이싱이 즉시 실패함 — resources/cura/에 버전 고정해서 커밋).

tool-scope.ts에 3D프린트 관련 키워드 게이트 추가(다른 니치 도구들과 동일 패턴).
end-to-end 테스트 완료(3DBenchy STL로 다운로드→슬라이싱→K2 업로드 확인, 테스트
파일은 삭제함).
2026-09-20 00:19:02 +09:00
kim 101a673827 fix: K2 카메라 연결 지연(20~30초) 수정 — ICE gathering 완전종료 대신 1.5초 바운드
onicecandidate가 ev.candidate===null(전체 gathering 종료)까지 기다린 뒤에만 오퍼를
보내고 있었는데, 이 네트워크에서 그 신호가 20~30초씩 걸림(가상 인터페이스들 순회+
타임아웃으로 추정). 실제 필요한 STUN srflx 후보는 1~2초면 잡히므로, 완전종료를
기다리지 않고 1.5초 타임아웃 뒤 그때까지 모인 후보로 바로 오퍼를 보내도록 변경.
브라우저 실측: 연결까지 20~30초 → 3초 이내로 단축.
2026-09-19 23:55:22 +09:00
kim 5eed244340 fix: NVR go2rtc WS mixed-content, K2 재연결 레이스, 시그널링 타임아웃 + WS 프록시 공통화
- nvr-app.html: go2rtc WS URL을 페이지 오리진 기준 상대경로로 (하드코딩된 ws://LAN IP가
  HTTPS 페이지에서 mixed-content로 막히던 문제)
- k2-app.html: 카메라 재연결 시 오래된 연결의 실패 콜백이 새 연결 상태를 덮어쓰던 레이스 수정
- routes-k2.ts: K2 시그널링 fetch에 타임아웃 추가(프린터 응답없음 시 무한대기 방지)
- nvr.ts/routes-comfyui.ts/routes-android.ts에 중복돼있던 raw WebSocket 업그레이드
  프록시(connect→pipe→cleanup)를 ws-proxy-util.ts의 공통 헬퍼로 통합
2026-09-19 23:46:53 +09:00
kimandClaude Sonnet 5 d65e8d958d feat: K2/밀링 앱을 NPM 서브도메인(HTTPS)으로 전환, 홈 드롭다운 재추가
print.applecherry.net(K2 콤보 Fluidd) · mill.applecherry.net(CNC 밀링
OctoPrint)로 NPM 프록시+Let's Encrypt 설정 완료. 앱들이 이제 HTTPS라
mixed content 경고 없이 홈 드롭다운에 다시 노출.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PppKcQmeTJDQCpfcgKietu
2026-09-19 16:39:30 +09:00
kimandClaude Sonnet 5 f13b40712c feat: 복원된 옛 홈페이지(애플체리, XpressEngine) 앱 추가
트루나스 Plex/applecherry에 있던 옛 XpressEngine(XE 1.11.6) 사이트를
PHP7.4+MariaDB 도커 앱으로 복원하고 home.applecherry.net(NPM+Let's
Encrypt)으로 서빙. 홈클로 홈 드롭다운에 iframe 래퍼 앱으로 추가.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PppKcQmeTJDQCpfcgKietu
2026-09-19 11:24:36 +09:00
kimandClaude Sonnet 5 72d4ac62ee fix: K2 콤보 카메라 STUN 서버 복원 (host candidate만으론 응답 없음)
STUN을 빼고 host candidate만 쓰면 DTLS가 failed로 끝나고 프린터에서
데이터가 전혀 안 옴(bytesReceived 0) — 원인은 불명이나 재현됨. 크리얼리티
데모 코드처럼 STUN을 유지해야 정상 연결됨(다소 느리지만 20초 내외).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PppKcQmeTJDQCpfcgKietu
2026-09-18 23:17:57 +09:00
kimandClaude Sonnet 5 a8149c136a feat: 프린터실 카메라 웹코덱(WebRTC/MSE) 전환 + K2 콤보 카메라 프록시, 옛 3D프린터 앱 제거
- go2rtc가 CORS/Origin 체크로 브라우저 직접접속을 막던 문제 해결: 클라이언트
  스크립트를 벤더링해 같은 오리진으로 서빙하고, WS는 게이트웨이 프록시
  (attachGo2rtcWsProxy)가 Origin을 다시 써서 우회. CCTV 앱의 프린터실 카메라
  라이브뷰가 MJPEG 대신 WebRTC/MSE를 쓰도록 전환(대역폭 절감).
- go2rtc mjpeg 트랜스코더가 멈추는 경우를 감지해 자동 재기동하는
  /api/nvr/mjpeg-health 엔드포인트 추가.
- 신규 3D프린터(K2 콤보, Klipper 스탠드얼론)의 내장 WebRTC 카메라 시그널링을
  프록시하는 /api/k2/webrtc-signal 추가 — 홈클로가 HTTPS로 서빙되는데 카메라가
  평문 HTTP만 지원해 브라우저 mixed-content 차단을 우회하기 위함.
- 옛 3D프린터(단종된 Anet A8 Plus, OctoPrint) 앱과 홈 드롭다운 링크 제거.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PppKcQmeTJDQCpfcgKietu
2026-09-18 22:40:10 +09:00
kimandClaude Sonnet 5 e7a86d6f2d fix: go2rtc mjpeg 트랜스코더 멈춤 자동 감지/복구 엔드포인트 추가
프린터실 카메라의 go2rtc mjpeg 가상 스트림이 카메라 wifi 순단 등으로
멈추면 뷰어가 재연결해도 같은 멈춘 프로세스에 붙어 계속 안 나오는
문제가 있었음. /api/nvr/mjpeg-health가 컨트롤 탭 로드 시 go2rtc
스트림 상태를 확인해 정체된 트랜스코더만 재시작한다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PppKcQmeTJDQCpfcgKietu
2026-09-18 14:27:38 +09:00
kimandClaude Sonnet 5 d34dd10ba7 feat: 독립 카메라에 ONVIF PTZ 지원 추가 (프린터실 카메라 등록)
extra-cameras API/UI에 ptz·onvifPort 필드 추가하고, 프린터실 머큐시스 MC230
카메라를 PTZ 지원 독립 카메라로 등록.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PppKcQmeTJDQCpfcgKietu
2026-09-18 00:31:38 +09:00