feat: harden local shell, SFTP, and desktop session reliability

Finder-launched terminals now inherit a login PATH so tools like ollama resolve, destructive SFTP/editor actions require confirmation, and terminal search plus window dragging work again. SSH forwards and RDP sessions keep listeners and survive reconnect without FastPath crashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-16 16:08:11 +08:00
1 parent 188f3556ed
commit 08696896f9
223 files changed
+13493 -3423

No files matched your search

+107 -32
View File
@@ -78,7 +78,7 @@
"closeTab": "현재 탭 닫기",
"closeAllTabs": "모든 탭 닫기",
"portForward": "새 포트 포워딩",
"upgradePro": "OpsXShell Pro 업그레이드",
"upgradePro": "OpsXShell 프로로 업그레이드",
"editSession": "세션 편집: {{name}}"
}
},
@@ -90,6 +90,8 @@
"terminal": "터미널",
"batch": "일괄 실행",
"newTerminal": "새 터미널",
"closeAria": "{{title}} 닫기",
"listAria": "열린 탭",
"kind": {
"terminal": "터미널",
"sftp": "SFTP",
@@ -105,7 +107,8 @@
"closeRight": "오른쪽 탭 닫기",
"closeLeft": "왼쪽 탭 닫기",
"closeAll": "모두 닫기",
"clone": "탭 복제"
"clone": "탭 복제",
"openSftp": "SFTP 열기"
}
},
"sidebar": {
@@ -149,9 +152,9 @@
"disconnect": "연결 끊기"
},
"pro": {
"upgrade": "Pro 업그레이드",
"upgrade": "프로로 업그레이드",
"feature": "Pro 기능",
"unlock": "{{feature}} 잠금 해제",
"unlock": "프로 고급 기능 잠금 해제",
"comingSoon": "출시 예정",
"activateSuccess": "활성화됨",
"activateFailed": "활성화 실패",
@@ -160,28 +163,33 @@
"invalidLicense": "활성화 실패: 잘못된 라이선스",
"goUpgrade": "업그레이드",
"haveLicense": "라이선스가 있나요? 활성화",
"title": "OpsXShell Pro · Pro 운영 기능 잠금 해제",
"subtitleComingSoon": "{{feature}} 출시 예정 · 조기 액세스는 Pro 업그레이드",
"subtitleUnlock": "잠금 해제 중: {{feature}}",
"subtitleDefault": "포워딩, 프록시, 시리얼, AI, 일괄 실행, 브로드캐스트",
"title": "OpsXShell · 프로 잠금 해제",
"subtitleComingSoon": "고급 기능 곧 제공 · 프로로 먼저 체험",
"subtitleUnlock": "고급 기능을 잠금 해제하는 중",
"subtitleDefault": "포트 포워딩, 터미널 AI, 일괄 운영, 호스트 목록, 원격 데스크톱 등 프로 기능",
"activated": "Pro 활성화됨",
"expiresAt": " · {{date}}까지 유효",
"lifetime": " · 평생 라이선스",
"oneTimeFee": "1회 구매 · 이 기기에서 평생 사용 · 구독 없음",
"oneTimeFee": "무료 / 프로 연간 / 프로 영구",
"pasteLicense": "라이선스 붙여넣기",
"activateButton": "활성화",
"clearLicense": "로컬 라이선스 삭제",
"paymentNote": "결제는 Waffo가 처리합니다. 이 앱은 카드 정보를 수집하지 않습니다.",
"testDeviceId": "테스트 환경 · 기기 ID: {{deviceId}}",
"later": "나중에 하기",
"viewPro": "Pro 보기",
"wallComingSoonSuffix": " · 출시 예정",
"wallUnlock": "{{feature}} 잠금 해제",
"wallMessageComingSoon": "이 Pro 기능은 아직 개발 중입니다. 조기 액세스를 위해 업그레이드하세요.",
"wallMessageDefault": "이 기능은 OpsXShell Pro가 필요합니다. 이 기기에서 사용하려면 업그레이드하세요.",
"viewPro": "프로 요금제 보기",
"wallComingSoonSuffix": "곧 제공",
"wallUnlock": "프로 고급 기능 잠금 해제",
"wallMessageComingSoon": "이 기능은 OpsXShell 프로에 포함되며 아직 준비 중입니다. 프로 연간 또는 영구로 먼저 체험하세요.",
"wallMessageDefault": "포트 포워딩, 터미널 AI, 일괄 실행, 호스트 목록, 원격 데스크톱은 프로에 포함됩니다. 프로 연간 또는 영구로 이 기기에서 사용하세요.",
"checkoutUnavailable": "결제를 사용할 수 없습니다 ({{status}})",
"emailLabel": "이메일",
"emailRequired": "결제 전에 이메일을 입력하세요",
"claimPending": "라이선스를 받는 중…",
"invalidKeyFormat": "키 형식이 올바르지 않습니다. LIC- 라이선스 키를 붙여넣으세요",
"accountEmail": "계정: {{email}}",
"freeTier": "무료",
"proTier": "Pro",
"proTier": "프로",
"freeItems": {
"ssh": "SSH 터미널",
"sftp": "SFTP 파일 관리자",
@@ -195,14 +203,35 @@
"serial": "시리얼 터미널",
"ai": "터미널 AI 어시스턴트",
"batch": "일괄 실행 / 브로드캐스트 동기화",
"broadcast": "브로드캐스트 입력 동기화",
"importExport": "호스트 일괄 가져오기/내보내기",
"desktop": "RDP / VNC 원격 데스크톱"
}
"broadcast": "브로드캐스트 동기 입력",
"importExport": "호스트 목록 일괄 가져오기/내보내기",
"desktop": "원격 데스크톱 (RDP / VNC)"
},
"planYearly": "프로 연간",
"planYearlyDesc": "¥99 / 년 · 기기 2대",
"planPerpetual": "프로 영구",
"planPerpetualDesc": "¥199 · 기기 5대 · 평생",
"planTrial": "프로 체험",
"devices": "최대 {{count}}대",
"goUpgradeYearly": "프로 연간 구매",
"goUpgradePerpetual": "프로 영구 구매",
"activatedYearly": "프로 연간 활성화됨",
"activatedPerpetual": "프로 영구 활성화됨",
"activatedTrial": "프로 체험 활성화됨",
"choosePlan": "선택: 무료 / 프로 연간 / 프로 영구",
"planFreePrice": "¥0",
"planFreeDesc": "SSH · SFTP · 로컬 터미널",
"planFreeCta": "현재 요금제",
"planIncludesPro": "모든 프로 기능 포함",
"planFree": "무료",
"subtitleUnlockDetail": "관련 기능: {{feature}}",
"wallUnlockComingSoon": "프로 고급 기능 곧 제공",
"wallMessageComingSoonFeature": "「{{feature}}」는 프로 고급 기능이며 아직 준비 중입니다. 프로 연간 또는 영구로 먼저 체험하세요.",
"wallMessageFeature": "「{{feature}}」는 프로 고급 기능입니다. 프로 연간 또는 영구로 이 기기에서 사용하세요."
},
"launcher": {
"title": "빠른 실행",
"description": "세션, 로컬 셸 및 기기 콘솔",
"description": "세션, 로컬 Shell, 데스크톱, 시리얼",
"cards": {
"quickConnect": {
"title": "빠른 연결",
@@ -215,6 +244,14 @@
"serial": {
"title": "시리얼 터미널",
"desc": "보레이트 및 포트 설정"
},
"rdp": {
"title": "RDP 데스크톱",
"desc": "원격 Windows"
},
"vnc": {
"title": "VNC 데스크톱",
"desc": "원격 그래픽"
}
},
"sectionLocalShell": "로컬 셸",
@@ -229,6 +266,7 @@
"forwardOpenRunning": "SOCKS5 실행 중 (:{{port}}) · 클릭하여 관리 · 우클릭으로 복사",
"forwardStarted": "포워딩 시작됨",
"forwardStartedDynamic": "SOCKS5 시작됨: socks5://127.0.0.1:{{port}}",
"forwardResumed": "포트 포워딩 {{count}}개를 복구했습니다",
"socksStartFailed": "시작 실패: {{message}}",
"socksStopFailed": "중지 실패: {{message}}",
"socksCopied": "socks5://127.0.0.1:{{port}} 복사됨"
@@ -249,7 +287,7 @@
"typeDynamic": "동적",
"running": "실행 중",
"socksProxy": "SOCKS5: 127.0.0.1:{{port}}",
"remoteToLocal": "원격 {{remote}} → 로컬 {{local}}",
"remoteToLocal": "원격 {{bind}}:{{remote}} → {{host}}:{{local}}",
"stats": "{{connections}}개 연결 · ↑{{up}} · ↓{{down}}",
"copyProxy": "프록시 복사",
"copyRule": "규칙 복제",
@@ -259,15 +297,15 @@
"delete": "삭제",
"editTitle": "포워딩 편집",
"addTitle": "포워딩 추가",
"typeLocalDesc": "로컬 → 원격",
"typeRemoteDesc": "원격 → 로컬",
"typeLocalDesc": "원격 서비스 접근",
"typeRemoteDesc": "로컬 서비스 노출",
"typeDynamicDesc": "SOCKS5",
"selectType": "유형",
"drawerSubtitle": "현재 SSH 세션을 통한 터널",
"ruleFields": "세부 정보",
"dynamicHint": "127.0.0.1:1080의 로컬 SOCKS5. 트래픽은 이 SSH 서버의 공인 IP를 통해 나갑니다.",
"dynamicSingleHint": "SSH 호스트당 하나의 동적 규칙, 고정 포트 1080.",
"dynamicPortHint": "If the port is busy, pick another local port (e.g. 1081).",
"dynamicHint": "로컬 SOCKS5(기본 :1080). 트래픽은 현재 SSH 출구를 통해 나갑니다.",
"dynamicSingleHint": "SSH 호스트당 동적 규칙 1개. 중지 후 포트 변경.",
"dynamicPortHint": "사용 중이면 다른 포트(예: 1081).",
"ruleList": "규칙",
"ruleListEmpty": "아직 로컬/원격 규칙이 없습니다",
"addSection": "새로 추가",
@@ -301,17 +339,30 @@
"internet": "인터넷",
"localCaption": "로컬: 원격 포트를 로컬 포트인 것처럼 접근합니다.",
"remoteCaption": "원격: 원격 접근을 로컬 서비스로 포워딩합니다.",
"dynamicCaption": "동적: 이 컴퓨터가 현재 SSH 서버의 공인 IP를 통해 나가는 SOCKS5 프록시가 됩니다."
"dynamicCaption": "동적: 로컬 SOCKS5, 현재 SSH 출구로 외부 접속."
},
"ruleName": "이름",
"ruleNamePlaceholder": "예: MySQL",
"localListenPort": "로컬 수신 포트",
"localListenPortHint": "기본 localhost — ssh -L 포트:… 와 같음. 보통 포트만 입력.",
"localBindAdvanced": "고급: 로컬 바인드 변경",
"localTargetPort": "로컬 대상 포트",
"remoteTargetPort": "원격 대상 포트",
"remoteTargetPort": "대상 포트",
"remoteListenPort": "원격 수신 포트",
"remoteListenPortHint": "기본은 원격 localhost — ssh -R 포트:… 와 같습니다. 보통 포트만 입력하세요.",
"remoteListenAddress": "원격 수신",
"remoteListenAddressPlaceholder": "127.0.0.1:18080",
"remoteListenAddressHint": "127.0.0.1 = 원격 본기만 / 0.0.0.0 = 원격 LAN(GatewayPorts 필요).",
"errorRemoteListenAddress": "유효한 원격 수신 주소 입력(예: 127.0.0.1:18080)",
"localTargetAddress": "로컬 / LAN 대상",
"localTargetAddressPlaceholder": "127.0.0.1:8080",
"localTargetAddressHint": "로컬/LAN 서비스로 전달 (예: 127.0.0.1:8080). 0.0.0.0 금지.",
"viaSessionRemote": "{{target}} 에서 수신 (기본 localhost)",
"errorRemoteListenPort": "유효한 원격 수신 포트(1–65535)를 입력하세요",
"errorLocalTargetAddress": "유효한 로컬/LAN 대상 입력(예: 127.0.0.1:8080)",
"portPlaceholder": "예: 3306",
"remoteHost": "원격 호스트",
"remoteHostPlaceholder": "localhost 또는 IP",
"remoteHost": "대상 호스트",
"remoteHostPlaceholder": "127.0.0.1",
"socksRuleName": "SOCKS5 프록시",
"defaultLocalName": "로컬 포워딩",
"defaultRemoteName": "원격 포워딩",
@@ -325,11 +376,35 @@
"deleteConfirm": "이 규칙을 삭제하시겠습니까?",
"errorNamePort": "이름과 유효한 포트(1–65535)를 입력하세요",
"errorTargetPort": "유효한 대상 포트(1–65535)를 입력하세요",
"errorRemoteHost": "원격 호스트를 입력하세요",
"errorRemoteHost": "대상 호스트를 입력하세요(SSH 서버 기준)",
"errorNotConnected": "연결되지 않음",
"errorStartFailed": "시작 실패",
"errorStopFailed": "중지 실패",
"errorSocksRunning": "SOCKS5가 이미 실행 중입니다",
"copySuffix": "복사"
"copySuffix": "복사",
"remoteBindHint": "0.0.0.0 은 GatewayPorts 필요. 없으면 종종 127.0.0.1 만 바인드. 대상에 0.0.0.0 금지.",
"localToRemote": "로컬 {{bind}}:{{local}} → 원격 {{host}}:{{remote}}",
"localToRemotePort": "로컬 :{{local}} → 원격 {{host}}:{{remote}}",
"remoteHostHint": "SSH 서버 기준. 본기는 127.0.0.1, LAN은 도달 가능한 IP. 0.0.0.0 불가.",
"localBindHost": "로컬 바인드",
"localBindHostPlaceholder": "127.0.0.1",
"localBindHostHint": "기본은 이 기기만. 0.0.0.0 또는 LAN IP로 네트워크 접근 허용.",
"errorTargetHostInvalid": "대상은 0.0.0.0 불가 — 127.0.0.1 또는 도달 가능한 IP",
"listenAddress": "로컬 수신",
"listenAddressPlaceholder": "127.0.0.1:8080",
"listenAddressHint": "기본은 이 기기만. 0.0.0.0이면 LAN 접근 가능.",
"targetAddress": "원격 대상",
"targetAddressPlaceholder": "127.0.0.1:80",
"targetAddressHint": "SSH 서버에서 본 주소. 본기 서비스는 127.0.0.1:포트.",
"errorListenAddress": "유효한 수신 주소 입력(예: 127.0.0.1:8080)",
"errorTargetAddress": "유효한 대상 입력(예: 127.0.0.1:80)",
"targetAddressHintVia": "{{target}} 경유. 본기 서비스는 127.0.0.1:포트(새 원격 포트 생성 안 함).",
"viaSession": "세션 경유 {{target}}",
"remoteToLocalPort": "원격 :{{remote}} → {{host}}:{{local}}",
"remoteBindAdvanced": "고급: 원격 바인드 변경",
"remoteBindHost": "원격 바인드",
"remoteBindHostPlaceholder": "127.0.0.1",
"remoteBindHostHint": "기본은 원격 호스트만. 0.0.0.0 은 GatewayPorts 필요. 없으면 대부분 sshd 가 여전히 127.0.0.1 만 바인드합니다.",
"remoteGatewayPortsWarning": "원격 0.0.0.0 수신을 요청했습니다. GatewayPorts 가 없으면 sshd 가 조용히 127.0.0.1 만 바인드하는 경우가 많습니다."
}
}
+11 -4
View File
@@ -15,10 +15,12 @@
"passwordOptional": "비밀번호 (선택 사항)",
"width": "너비",
"height": "높이",
"rdpFootnote": "네이티브 IronRDP: TCP → X.224 → TLS → CredSSP/NLA → MCS; 45초 핸드셰이크 시간 초과. 개발 중 인증서 확인은 완화됩니다.",
"vncFootnote": "네이티브 vnc-rs RFB(Tight JPEG 디코딩); 클립보드는 UTF-8로 전송됩니다(매우 오래된 서버는 텍스트가 깨질 수 있음).",
"rdpFootnote": "전체 화면 또는 최대화 시 현재 창 영역에 맞춰 원격 해상도를 동적으로 요청합니다. 연결 전에 초기 해상도를 선택할 수도 있습니다. 인증서 검증은 개발 중에는 완화되어 있습니다.",
"vncFootnote": "대부분 VNC 서버는 원격 해상도가 고정됩니다. 전체 화면은 로컬 확대만 하며, 연결 시 선호 크기는 참고용입니다. 클립보드는 UTF-8로 전송됩니다.",
"errorHostRequired": "호스트 주소를 입력하세요",
"errorUsernameRequired": "사용자 이름을 입력하세요"
"errorUsernameRequired": "사용자 이름을 입력하세요",
"resolution": "해상도",
"resolutionCustom": "사용자 지정"
},
"toolbar": {
"pasteClipboard": "로컬 클립보드를 원격에 붙여넣기",
@@ -28,7 +30,12 @@
"openShareDir": "공유 폴더를 로컬에서 열기 (다운로드)",
"openShareDirAria": "공유 폴더 열기",
"disconnect": "연결 끊기",
"disconnectAria": "연결 끊기"
"disconnectAria": "연결 끊기",
"fullscreen": "전체 화면",
"fullscreenAria": "전체 화면으로 전환",
"exitFullscreen": "전체 화면 종료",
"exitFullscreenAria": "전체 화면 종료",
"exitFullscreenHint": "Esc / F11로 종료"
},
"status": {
"connecting": "연결 중…",
+16 -3
View File
@@ -39,7 +39,9 @@
"closed": "연결이 닫혔습니다",
"dnsFailed": "호스트를 확인할 수 없습니다",
"refused": "연결이 거부되었습니다",
"disconnected": "연결 끊김"
"disconnected": "연결 끊김",
"keepaliveFailed": "SSH keep-alive가 연속 실패하여 연결이 닫혔습니다",
"idleKicked": "원격 Shell이 유휴 시간 초과로 종료되었습니다(TMOUT)"
},
"auth": {
"failed": "인증 실패. 사용자 이름과 비밀번호를 확인하세요.",
@@ -68,7 +70,10 @@
"forward": {
"failed": "포트 포워딩 설정 실패",
"bindFailed": "포트 바인딩 실패",
"alreadyInUse": "포트가 이미 사용 중입니다"
"alreadyInUse": "로컬 포트가 이미 사용 중입니다. 실행 중인 포워딩이나 다른 프로세스를 중지한 뒤 다시 시작하세요.",
"remoteDenied": "서버가 원격 포트 포워딩을 거부했습니다(AllowTcpForwarding / PermitListen, 포트 사용 중 또는 바인드 제한). 원격은 127.0.0.1에서만 수신합니다.",
"remoteDeniedPrivileged": "특권 수신 포트(<1024)가 거부되었습니다. ≥1024를 사용하거나 root로 실행하고 AllowTcpForwarding / PermitListen을 확인하세요.",
"invalidTargetHost": "대상은 0.0.0.0일 수 없습니다. 127.0.0.1 또는 도달 가능한 내부 IP를 사용하세요"
},
"sftp": {
"connectFailed": "SFTP 연결 실패",
@@ -80,7 +85,15 @@
"writeFailed": "저장 실패",
"renameFailed": "이름 변경 실패",
"chmodFailed": "권한 업데이트 실패",
"apiUnavailable": "SFTP API를 사용할 수 없음"
"apiUnavailable": "SFTP API를 사용할 수 없음",
"channelFailed": "SFTP 채널을 열 수 없습니다(서버 제한 또는 세션 바쁨). 다시 연결하세요",
"sessionGone": "SFTP 세션이 종료되었습니다. 다시 연결하세요",
"busy": "파일 전송 중입니다. 완료 후 다시 시도하세요.",
"editTooLarge": "파일이 너무 큽니다(텍스트 편집 한도 약 2MB). 다운로드 후 로컬에서 여세요.",
"editBinary": "이진 파일로 보이며 텍스트 편집기로 열 수 없습니다",
"editNotUtf8": "원격 파일이 유효한 UTF-8 텍스트가 아닙니다",
"transferCancelled": "전송이 취소되었습니다",
"diskFull": "디스크 공간 부족(로컬 또는 원격)"
},
"session": {
"notFound": "세션을 찾을 수 없음",
+6 -4
View File
@@ -69,17 +69,17 @@
"confirm": "삭제"
},
"welcome": {
"tagline": "원격 터미널 · SFTP · 포트 포워딩",
"tagline": "간단 실용 · 터미널 · 파일 · 데스크톱",
"sectionStart": "시작하기",
"sectionDesktop": "원격 데스크톱",
"sectionRecent": "최근 세션",
"quickConnect": {
"title": "빠른 연결",
"desc": "1회성 SSH; 라이브러리에 저장되지 않음"
"desc": "임시 SSH — 세션 목록에 저장하지 않음"
},
"newSession": {
"title": "새 세션",
"desc": "호스트 저장; 사이드바에서 더블클릭하여 연결"
"desc": "호스트 저장 — 사이드바에서 더블클릭으로 연결"
},
"localShell": {
"title": "로컬 셸",
@@ -167,7 +167,9 @@
"jumpHost": "점프 호스트 (선택 사항)",
"jumpNone": "점프 호스트 없음",
"jumpNoneAlt": "점프 호스트 없음",
"jumpHint": "저장된 세션 하나를 점프 호스트로 사용할 수 있습니다"
"jumpHint": "저장된 세션 하나를 점프 호스트로 사용할 수 있습니다",
"resolution": "해상도",
"resolutionCustom": "사용자 지정"
},
"batch": {
"title": "일괄 실행",
+51 -19
View File
@@ -1,9 +1,7 @@
{
"nav": {
"general": "워크스페이스",
"generalDesc": "레이아웃 및 리소스",
"appearance": "외관",
"appearanceDesc": "테마 및 언어",
"general": "일반",
"generalDesc": "테마, 언어, 레이아웃",
"terminal": "터미널",
"terminalDesc": "글꼴 및 색상",
"shortcuts": "단축키",
@@ -15,11 +13,13 @@
"ai": "AI 모델",
"aiDesc": "AI 제공자",
"about": "정보",
"aboutDesc": "버전 및 데이터"
"aboutDesc": "버전 및 데이터",
"account": "계정",
"accountDesc": "이메일, 라이선스, 기기"
},
"title": "설정",
"search": {
"placeholder": "설정 검색..."
"placeholder": "설정 검색…"
},
"language": {
"label": "언어",
@@ -89,7 +89,7 @@
"ssh": {
"connection": "연결",
"keepAlive": "연결 유지",
"keepAliveDesc": "연결 끊김을 줄이기 위해 유휴 시 keep-alive 전송",
"keepAliveDesc": "유휴 시 SSH keep-alive를 보냅니다. 연속 실패 후에만 연결을 끊습니다",
"autoReconnect": "자동 재연결",
"autoReconnectDesc": "예기치 않은 연결 끊김 후 자동으로 재시도",
"timeout": "연결 시간 초과",
@@ -101,8 +101,10 @@
"maxConnectionsDesc": "SSH, SFTP 및 일괄 연결에 공유되는 상한",
"maxBackground": "백그라운드 연결 제한",
"maxBackgroundDesc": "제한 초과 시 가장 오래전 사용한 연결 제거",
"suspend": "백그라운드 일시 중단",
"suspendDesc": "탭이 백그라운드에 있을 때 활성 채널을 해제하기까지의 시간(분)"
"suspend": "백그라운드 일시 중지",
"suspendDesc": "유휴 백그라운드 탭을 몇 분 후 일시 중지할지(활성 Shell/SFTP는 유지)",
"keepAliveInterval": "유지 간격(초)",
"keepAliveIntervalDesc": "서버 ping 주기(ClientAliveInterval과 유사). 불안정한 네트워크에서는 15초 이하 권장"
},
"security": {
"credentials": "자격 증명 보호",
@@ -119,7 +121,7 @@
"clearConfirm": "모든 알려진 호스트 키를 지우시겠습니까? 다음 연결 시 다시 확인 메시지가 표시됩니다."
},
"about": {
"tagline": "가벼운 네이티브 SSH / SFTP 운영 터미널",
"tagline": "간단하고 실용적인 네이티브 운영 터미널",
"appVersion": "앱 v{{version}}",
"copyVersion": "버전 정보 복사",
"runtime": "런타임",
@@ -127,8 +129,9 @@
"localData": "로컬 데이터",
"sessionsSettings": "세션 및 설정",
"masterKeyPath": "마스터 키",
"privacyNote": "비밀번호와 키 암호는 Rust에서 AES-256-GCM으로 암호화됩니다. OpsXShell은 터미널 명령 히스토리를 저장하지 않습니다.",
"license": "MIT 라이선스 · Rust · XTerm.js"
"privacyNote": "비밀번호·키 암호는 Rust에서 AES-256-GCM으로 암호화합니다. 명령 기록·감사 로그는 저장하지 않습니다.",
"license": "MIT 라이선스 · Rust · XTerm.js",
"logPath": "앱 로그"
},
"ai": {
"presets": "제공자 프리셋",
@@ -162,13 +165,42 @@
"apiKeySaveFailed": "API 키 저장 실패",
"providerNotFound": "제공자를 찾을 수 없음",
"modelCatalogReadFailed": "모델 카탈로그 읽기 실패",
"catalogUnavailable": "models.dev를 사용할 수 없습니다"
"catalogUnavailable": "models.dev를 사용할 수 없습니다",
"family": {
"openai_compatible": "OpenAI 호환",
"standalone": "독립 프로토콜",
"plan": "구독 플랜",
"local": "자체 호스팅",
"custom": "사용자 지정"
}
},
"family": {
"openai_compatible": "OpenAI 호환",
"standalone": "────────",
"plan": "플랜",
"local": "셀프 호스팅",
"custom": "사용자 지정"
"account": {
"title": "Account",
"email": "Email",
"emailDesc": "Used for checkout and license claims",
"plan": "Current plan",
"planFree": "Free",
"planYearly": "프로 연간",
"planPerpetual": "프로 영구",
"planTrial": "프로 체험",
"expires": "Expires {{date}}",
"lifetime": "Lifetime",
"devices": "Seats: up to {{count}} devices",
"deviceId": "This device ID",
"saveEmail": "Save email",
"activate": "Activate license",
"pasteLicense": "Paste LIC- license key",
"clear": "Clear local license",
"deactivate": "Deactivate this device",
"upgrade": "Upgrade",
"saved": "Saved",
"activateOk": "Activated",
"deviceList": "바인딩된 기기",
"loadingDevices": "기기 목록 불러오는 중…",
"noDevices": "바인딩된 기기 없음",
"thisDevice": "이 기기",
"removeDevice": "해제",
"deviceRemoved": "기기 바인딩이 해제됨",
"seatFull": "기기 좌석이 가득 찼습니다. 새 기기에서 활성화하기 전에 다른 기기를 해제하세요."
}
}
+54 -24
View File
@@ -3,8 +3,9 @@
"local": "로컬",
"remote": "원격",
"notConnectedTitle": "원격 서버에 연결되지 않음",
"notConnectedDesc": "SFTP를 사용하려면 먼저 SSH 세션을 연결하세요",
"ariaLabel": "{{title}} 파일 패널"
"notConnectedDesc": "먼저 SSH에 연결한 뒤 전송·편집",
"ariaLabel": "{{title}} 파일 패널",
"reconnectResumeDesc": "연결이 끊겼습니다. 다시 연결하면 {{path}}(으)로 복귀합니다"
},
"connection": {
"selectServer": "서버 선택",
@@ -12,7 +13,9 @@
"createSessionHint": "먼저 사이드바에서 세션을 생성하세요",
"reconnect": "재연결",
"notConnected": "연결되지 않음",
"connecting": "연결 중..."
"connecting": "연결 중...",
"resumeHint": "다시 연결하면 {{path}}(으)로 복귀합니다",
"reconnectingResume": "{{path}} 복원하며 다시 연결 중…"
},
"errors": {
"notConnected": "SFTP가 연결되지 않았습니다",
@@ -23,19 +26,14 @@
"selectDownloadFiles": "먼저 다운로드할 파일을 선택하세요",
"uploadFailed": "업로드 실패",
"downloadFailed": "다운로드 실패",
"fileError": "{{fileName}}: {{message}}"
"fileError": "{{fileName}}: {{message}}",
"mkdirFailed": "폴더 만들기 실패"
},
"toolbar": {
"upload": "업로드",
"uploadAria": "업로드",
"download": "다운로드",
"downloadAria": "다운로드",
"downloadNeedSelection": "먼저 원격 파일을 선택하세요",
"downloadHint": "선택한 원격 파일 다운로드",
"refresh": "새로고침",
"refreshAria": "새로고침",
"refreshAria": "이 패널 새로고침",
"newFolder": "새 폴더",
"newFolderAria": "새 폴더"
"newFolderAria": "이 패널에 새 폴더 만들기"
},
"sort": {
"name": "이름",
@@ -55,7 +53,8 @@
"hideHidden": "도트파일 숨기기 (.xxx)",
"showHiddenAria": "숨김 파일 표시",
"hideHiddenAria": "도트파일 숨기기",
"searchAria": "{{title}}에서 검색"
"searchAria": "{{title}}에서 검색",
"upAtRoot": "이미 루트입니다"
},
"status": {
"loading": "불러오는 중...",
@@ -63,7 +62,8 @@
"filtered": "{{shown}} / {{total}} 표시 중",
"hiddenCount": "전체 {{total}}개 항목 ({{hidden}}개 숨김)",
"total": "{{count}}개 항목",
"filterHint": "필터: {{query}}"
"filterHint": "필터: {{query}}",
"refreshing": "새로 고치는 중..."
},
"empty": {
"dirTitle": "빈 디렉터리",
@@ -99,7 +99,6 @@
"download": "다운로드",
"edit": "편집",
"rename": "이름 변경",
"copyPath": "경로 복사",
"properties": "속성",
"permissions": "권한",
"delete": "삭제"
@@ -116,7 +115,18 @@
"chmodTitle": "권한 편집",
"chmodApply": "적용",
"pickUploadTitle": "업로드할 파일 또는 폴더 선택",
"pickUploadRzTitle": "업로드할 파일 선택 (rz)"
"pickUploadRzTitle": "업로드할 파일 선택 (rz)",
"overwriteTitle": "기존 파일을 덮어쓸까요?",
"overwriteConfirm": "덮어쓰기",
"overwriteSkip": "건너뛰기",
"overwriteOneRemote": "원격에 「{{name}}」이(가) 있습니다. 덮어쓸까요?",
"overwriteOneLocal": "로컬에 「{{name}}」이(가) 있습니다. 덮어쓸까요?",
"overwriteManyRemote": "원격에 같은 이름 {{count}}개가 있습니다. 모두 덮어쓸까요?",
"overwriteManyLocal": "로컬에 같은 이름 {{count}}개가 있습니다. 모두 덮어쓸까요?",
"overwriteMore": "…외 {{count}}개",
"deleteTitle": "삭제 확인",
"deleteMessage": "{{count}}개 항목을 삭제합니다: {{names}}",
"deleteRecursiveWarn": "폴더가 포함되어 있습니다. 재귀 삭제되며 되돌릴 수 없습니다."
},
"properties": {
"name": "이름",
@@ -152,27 +162,41 @@
"statusDone": "완료",
"statusFailed": "실패",
"statusCancelled": "취소됨",
"dropUpload": "{{path}}에 업로드",
"dropDownload": "{{path}}에 다운로드"
"dropUpload": "{{path}}(으)로 업로드하려면 여기에 놓으세요",
"dropDownload": "{{path}}(으)로 다운로드하려면 여기에 놓으세요",
"railAria": "로컬과 원격 간 전송",
"railUpload": "선택한 로컬 파일 업로드(선택 없으면 파일 선택)",
"railDownload": "선택한 원격 파일을 로컬로 다운로드",
"railUploadAria": "원격으로 업로드",
"railDownloadAria": "로컬로 다운로드",
"railDownloadNeedSelection": "먼저 원격 파일을 선택하세요",
"retry": "다시 시도"
},
"editor": {
"readFailed": "읽기 실패",
"saveFailed": "저장 실패",
"unsavedClose": "저장되지 않은 변경 사항이 있습니다. 그래도 닫으시겠습니까?",
"unsavedTitle": "저장되지 않은 변경 사항",
"discardAndClose": "버리고 닫기",
"reloadTitle": "다시 로드",
"discardAndReload": "버리고 다시 로드",
"saving": "저장 중...",
"save": "저장",
"saved": "파일 저장됨",
"loadingEditor": "에디터 로드 중...",
"footer": "UTF-8 · Monaco · ⌘/Ctrl+S 저장 · 최대 ~2MB",
"loadBlocked": "파일 내용을 불러오지 못했습니다",
"footer": "UTF-8 · ⌘/Ctrl+S 저장 · Esc 닫기 · 최대 약 2MB",
"charCount": "{{count}}자",
"unsavedMark": " · 저장 안 됨",
"savedMark": " · 저장됨"
"savedMark": " · 저장됨",
"title": "{{name}} 편집",
"dirtyBadge": "저장 안 됨",
"reload": "다시 불러오기",
"unsavedReload": "저장되지 않은 변경을 버리고 다시 불러올까요?"
},
"toast": {
"renamed": "{{name}}(으)로 이름 변경됨",
"renameFailed": "이름 변경 실패",
"pathCopied": "경로가 클립보드에 복사됨",
"pathCopyFailed": "경로 복사 실패",
"chmodUpdated": "권한이 {{mode}}(으)로 업데이트됨",
"chmodFailed": "권한 업데이트 실패",
"uploadPartial": "{{action}} 완료 {{completed}}, 실패 {{failed}}: {{detail}}",
@@ -180,7 +204,13 @@
"uploadAction": "업로드",
"downloadAction": "다운로드",
"noFolderUpload": "폴더 업로드 실패",
"noDragPath": "드래그 앤 드롭 경로를 사용할 수 없습니다; 업로드를 사용하세요",
"noFolderRecursive": "재귀 폴더 전송 실패; 재시도하거나 업로드를 사용하세요"
"noDragPath": "이 환경에서는 드래그 경로를 제공하지 않습니다. 가운데 → 또는 파일 선택을 사용하세요.",
"noFolderRecursive": "폴더 전송에 실패했습니다. 다시 시도하거나 가운데 → / 파일 선택을 사용하세요.",
"dropFailed": "끌어다 놓기 실패: 파일 정보를 읽지 못했습니다. 다시 시도하거나 가운데 화살표를 사용하세요.",
"folderCreated": "폴더 {{name}} 생성됨",
"allSkippedExisting": "이미 있는 파일을 모두 건너뛰었습니다",
"skippedExisting": "기존 파일 {{count}}개를 건너뛰었습니다",
"deleteFailed": "삭제 실패",
"deleted": "삭제됨"
}
}
+3 -2
View File
@@ -34,7 +34,7 @@
"reopenShell": "셸 다시 열기",
"reopening": "여는 중…",
"disconnected": "연결 끊김",
"disconnectedHint": "SSH 세션이 종료되었거나 네트워크가 끊겼습니다. 아래를 클릭하여 재연결하세요.",
"disconnectedHint": "SSH 세션이 종료되었거나 네트워크가 끊겼습니다. 아래를 누르면 다시 연결해 작업을 이어갑니다.",
"reconnect": "재연결",
"suspended": "연결 일시 중단됨",
"suspendedHint": "오랫동안 활동이 없어 리소스 절약을 위해 연결이 일시 중단되었습니다",
@@ -70,7 +70,8 @@
"splitRight": "오른쪽으로 분할",
"splitDown": "아래로 분할",
"maximizePane": "현재 창 최대화",
"restoreLayout": "분할 레이아웃 복원"
"restoreLayout": "분할 레이아웃 복원",
"openSftp": "SFTP 열기"
},
"search": {
"placeholder": "검색...",