img-share Skill
브라우저 스크린샷 또는 로컬 이미지를 채팅창 내에서 클릭 가능한 큰 이미지(썸네일 + 원본 보기)로 만들고, 휴대폰/다른 기기에서도 접근할 수 있게 합니다. Linux, macOS, WSL, Windows 네이티브 등 모든 플랫폼에서 사용 가능합니다.
whenToUse (언제 트리거되는가)
- 사용자가 "어떤 웹사이트/페이지를 캡처해서 보여달라"고 요청할 때;
- 어떤 이미지를 채팅창에 붙여넣고 "휴대폰에서도 보고 확대해서 세부 내용을 볼 수 있게" 하고 싶을 때;
- 작업 공간의 이미지를 클릭 가능한 큰 이미지가 포함된 채팅 내장 이미지로 만들고 싶을 때.
의존성
| 의존성 | 용도 | 설치 방법 |
|---|---|---|
| web-access skill | 스크린샷 (CDP 기반 브라우저 제어) | 설치 필요: https://github.com/eze-is/web-access |
| python3/python | 오케스트레이터 (serve.py) | 시스템 기본 제공 |
| cloudflared | 공용 터널 폴백 | mac: brew install cloudflared; win: winget install Cloudflare.cloudflared; linux: 공식 README 참조; 또는 CLOUDFLARED=/경로 설정 |
| ffmpeg (선택 사항) | 여러 이미지를 그리드로 합성 | 시스템 설치 (scripts/montage.sh에서 사용) |
실행 단계
-
호스트 접근성 판단 (필수, 가장 먼저 수행) — 이미지 URL의 기준 주소와 공용 터널 추가 개설 여부를 결정합니다. 이미지 주소는 호스트 주소를 따라야 하며, 단순히 "동작하게" 하려고 호스트와 다른 출처의 터널을 임의로 열지 마십시오:
판단 결론 (조치) 호스트가 이미 터널링/공용 접근 가능(사용자 휴대폰이 이 주소로 세션에 접속 중)이고, 정적 이미지 경로(예: /img/*)를 노출함이미지를 호스트 정적 디렉토리에 넣고 호스트 공용 주소를 삽입하며, 새 터널을 열지 않음 호스트가 공용 접근 가능하지만 정적 이미지 경로를 노출하지 않음 자동으로 폴백하지 않음: 먼저 사용자에게 "호스트에 정적 이미지 매핑을 구성해야 함"을 알림; 사용자가 동의한 경우에만 "명시적 독립 터널 폴백"을 진행 호스트가 localhost 전용 (예: DSH_WEB_URL=http://127.0.0.1:*, 공용 아님)이미지는 로컬/로컬 네트워크 주소만 가지며, 휴대폰은 원래 이 세션에 접근할 수 없음 — 먼저 사용자에게 DSH를 공용으로 열도록 요청하거나, 사용자 동의 하에 명시적 폴백을 진행; localhost/127.0.0.1 주소를 삽입하고 휴대폰에서 볼 수 있다고 주장하지 마십시오 감지 불가 / 불확실 추측하지 말고 사용자에게 먼저 물어보십시오: "휴대폰으로 이 세션에 접속할 때 어떤 주소(공용 도메인 또는 localhost)를 사용하시나요?" 그런 다음 위 표에 따라 실행 감지 방법: 환경 변수 읽기 (
DSH_WEB_URL,DSH_PUBLIC_BASE,DSH_IMG_BASE등), 호스트 정적 라우팅 설정 확인; 값을 얻을 수 없으면 "호스트가 제공하지 않음"으로 간주하고 위 표에 따라 "사용자에게 물어보기/알리기"를 진행하며, 조용히 다른 터널을 열지 마십시오.호스트 계약 (사전 약속, 현재 DSH 호스트는 아직 제공하지 않음): 호스트가
DSH_PUBLIC_BASE/DSH_IMG_BASE또는 이에 상응하는 정적 경로 선언을 노출하는 경우, agent는 반드시 이미지를 호스트 도메인 아래에 우선 배치해야 합니다; 제공되지 않을 때는 존재한다고 가정하지 마십시오. -
스크린샷 (필요한 경우): web-access skill을 사용하여
/screenshot으로 페이지를 PNG로 렌더링합니다.- CDP/Chromium이 제한된 환경(네트워크 차단/프로세스 충돌)에서 실행되지 않는 경우, 클라우드 스크린샷으로 폴백: thum.io / mshots / microlink 등을 사용하며
?wait=N&width=W매개변수를 포함하고, 반환된 이미지에서 "흰 픽셀이 아닌 비율이 임계값 초과, 색상 클러스터 수 > N"을 검증하여 빈 화면이 아닌 경우에만 사용합니다.
- CDP/Chromium이 제한된 환경(네트워크 차단/프로세스 충돌)에서 실행되지 않는 경우, 클라우드 스크린샷으로 폴백: thum.io / mshots / microlink 등을 사용하며
-
이미지 저장: 작업 공간에 저장합니다 (예:
img/x.png, 파일명은 ASCII/공백 없음). -
공유 (0단계 판단 결과에 따라 둘 중 하나 선택):
- 호스트 경로 (0단계에서 호스트 재사용 가능으로 판단된 경우): 이미지를 호스트가 노출한 정적 디렉토리에 넣고
https://<호스트 도메인>/img/x.png를 삽입합니다; 독립 터널을 열지 마십시오. - 명시적 폴백 (0단계에서 폴백이 필요하고 사용자가 동의한 경우): 이 파일 하나만 공유하고 전체 디렉토리는 열지 않습니다; 이미지 서버[루프백 전용]+공용 터널을 시작하고, 종단 간 검증을 수행한 후 PUBLIC_BASE를 출력합니다:
bash <skill>/scripts/setup.sh <img>/x.png # Unix/WSL python <skill>/scripts/serve.py --share <img>/x.png # Windows 네이티브
- 호스트 경로 (0단계에서 호스트 재사용 가능으로 판단된 경우): 이미지를 호스트가 노출한 정적 디렉토리에 넣고
-
응답에 삽입 (썸네일=원본 이미지 URL, 클릭 시 원본 전체 화면):
[](https://<기준 주소>/x.png)기준 주소는 반드시 0단계에서 판단된 결과(호스트 공용 주소 또는 사용자가 동의한 명시적 터널 주소)여야 합니다; localhost/127.0.0.1을 삽입하고 휴대폰에서 볼 수 있다고 주장하지 마십시오. 사용자에게 알림: 채팅창에 썸네일이 표시되고, 클릭하면 원본 이미지가 열립니다; 휴대폰에서도 동일한 공용 주소로 접근 가능합니다(DSH가 공용 마크다운 이미지를 렌더링할 수 있음이 확인됨).
-
공유 완료 → 터널 제거 (명시적 폴백 시나리오, 장기간 노출 방지):
bash <skill>/scripts/teardown.sh # 이미지 서버+터널 중지 및 정리
실패 처리
- 호스트 접근성 판단 불가: 추측하지 말고 조용히 다른 터널을 열지 마십시오; 먼저 사용자에게 휴대폰으로 이 세션에 접속할 때 사용하는 주소를 물어보십시오 (0단계 참조).
- 호스트가 공용이지만 정적 이미지 경로 없음: 사용자에게 정적 매핑을 구성하도록 요청하거나, 동의를 얻은 후 명시적 폴백을 진행하고 해당 터널이 호스트와 다른 출처임을 설명하십시오.
- 의존성 누락: serve.py가 "cloudflared/python 누락" 오류를 보고하면 지침에 따라 설치하거나
CLOUDFLARED를 설정하십시오; 스크린샷에 web-access가 없으면 클라우드 스크린샷으로 폴백(위 참조)하거나 사용자에게 이미지를 업로드하도록 요청하십시오. - 포트 점유: serve.py가 자동으로 사용 가능한 포트를 선택합니다.
- 터널 도메인을 가져올 수 없음 (네트워크 느림/Cloudflare 연결 끊김): serve.py가 폴링하여 재시도합니다; 그래도 실패하면 "네트워크에서 터널을 생성할 수 없음"을 알리고 로컬/로컬 네트워크 전용으로 폴백합니다(로컬 주소 제공, 휴대폰 시나리오의 경우 먼저 호스트/서비스를 공용으로 접근 가능하게 해야 한다고 설명).
- 종단 간 검증 실패: serve.py가 실제 파일명을 curl로 검증합니다(200이어야 성공); 실패 시 즉시 중단하여 "작동하는 것처럼 보이지만 실제로는 404"인 상황을 방지합니다.
- 도메인 변경: 빠른 터널은 재시작 시 도메인이 변경되어 이전에 삽입된 URL이 작동하지 않습니다; 호스트 경로에는 이 문제가 없습니다; 폴백 시나리오의 장기 안정성을 위해서는 명명된 터널/고정 도메인을 사용하십시오.
환경 자체 점검 (Agent가 먼저 수행)
시작하기 전에 탐지: 외부 네트워크 접근 가능 여부 (curl로 공용 주소), 브라우저 프로세스 생성 가능 여부 (CDP), 호스트가 공용 접근 가능한지 여부 (0단계 참조). 외부 네트워크 접근과 CDP가 모두 불가능 → 클라우드 스크린샷 + 더 높은 권한 필요 알림을 진행하고, 사용자에게 "작동하는 것처럼 보이지만 실제로는 깨진" 이미지를 제공하지 마십시오.
보안 (기본 동작, 알림이 아님)
- 지정된 파일만 공유: serve.py는 공유할 파일을 임의의 이름을 가진 하위 디렉토리로 복사한 후 서비스를 시작하며, 절대 전체 img/ 디렉토리를 열지 않습니다 (그렇지 않으면 터널 URL로 해당 디렉토리의 모든 파일을 나열/다운로드할 수 있음); 호스트 정적 디렉토리에도 공유할 파일만 배치합니다.
- 이미지 서버는 127.0.0.1에만 바인딩됩니다 (로컬 네트워크에서 직접 접근 불가); 공용 터널 URL에는 인증이 없습니다 — 공유 직후 teardown하십시오.
- 더 엄격하게 하려면: URL에 임의 토큰을 추가하거나 단기 터널을 사용하십시오; 민감한 이미지를 넣지 마십시오.
- 모든 이미지를 공용에 노출하지 마십시오: "이번에 공유할 한/몇 개의 이미지"만 공개적으로 접근 가능한 디렉토리/터널에 나타나도록 하십시오.
호스트 터널 우선 (판단은 실행 단계 0단계 참조)
- 이미지 주소는 호스트 주소를 따라야 함: 호스트가 이미 터널링/공용화된 경우, 이미지는 호스트와 동일한 도메인(예: 정적
/img/*경로) 아래에 있어야 합니다 — 사용자 휴대폰에서 세션에 접속하면 즉시 로드되며, 호스트와 다른 출처의 임시 터널을 추가로 열 필요가 없으며 열어서도 안 됩니다. - 호스트가 dsh-trust-proxy 등을 통해
/img/정적 규칙을 노출하는 경우, 이미지 URL 형식은https://<DSH 도메인>/img/x.png이며, 여러 장치에서 사용 가능하고 추가 터널이 필요 없습니다. - serve.py의 독립 cloudflared 터널은 이식 가능한 폴백(모든 머신에서 실행 가능)일 뿐입니다: 0단계에서 "호스트를 공용으로 재사용할 수 없음"으로 판단되고 사용자가 동의한 경우에만 사용하며, 그 한계(호스트와 다른 출처, 빠른 터널 재시작 시 도메인 변경)를 명확히 알립니다.
경계 조건
- 프로세스는 상주/제거 가능해야 함: serve.py는 teardown을 위해 PID를 기록합니다; DSH 세션 내 서비스는 세션을 넘어 지속되어야 합니다(관리형 백그라운드 + teardown으로 명시적 정리 사용).
- 파일명: ASCII 사용, 공백 없음; 스크립트는 파일 존재 여부를 검증하며, 한글/공백은 URL 인코딩으로 처리되지만 사용하지 않는 것이 좋습니다.
- 작업 공간 차이: 서로 다른 세션은 서로 다른 작업 공간을 가지므로, 해당 세션 경로에 따라 공유합니다; serve.py는 시스템 임시 디렉토리를 사용하며 작업 공간에 의존하지 않습니다.