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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "conn",
"source": "./plugin",
"description": "Conn: MCP server + working-etiquette skill.",
"version": "0.8.5"
"version": "0.8.6"
}
]
}
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## Unreleased

## 0.8.6 — Preview · 2026-09-22

- Keep terminal dimensions and shell output in the same order when an approval bar opens and closes, including in a fixed window. This fixes reproduced stale sizes and cursor disagreement after delayed resizes and shell redraws while retaining the existing motion.
- Preserve JSON and other output exactly: a response without a trailing newline can share its last line with the next shell prompt. Regression coverage distinguishes this from lost newlines and incorrect wrapping.

한국어: 창을 고정한 상태에서 승인 바가 열리고 닫힐 때도 터미널 크기와 셸 출력을 같은 순서로 반영합니다. 모션은 유지하면서 지연된 크기 변경·화면 재출력으로 크기와 커서가 어긋나는 재현된 결함을 수정했습니다. JSON 등 실제 출력은 그대로 유지하며, 끝에 개행이 없어 프롬프트가 붙는 경우와 줄바꿈 오류를 구분해 검사합니다.

Validation: 259 Rust tests, 55 frontend tests, real SSH browser regressions and the 12-stage collaboration context harness passed. The same product code passed native Apple Silicon macOS 27.0 checks in an isolated ad-hoc debug app with real WebKit, Tauri IPC and loopback SSH: 18 approval cycles, including 12 rapid denials, and 38 full visible-grid/cursor comparisons passed. Human editing/takeover, session allowance, shell preservation and output-ending checks also passed. [Verification report](docs/ssh-input-review-results.md#native-macos-verification-of-pr-64).

Remaining coverage: ResizeObserver warnings remain (24 during the rapid-denial cycles), without persistent disagreement at the checked post-motion points. The user's actual remote environment, physical-keyboard IME and separate canonical `/bin/sh` wrapped-text behavior remain unverified or unresolved. Signed release artifacts and installed-app upgrades are separate from these debug-app results.

한국어: Rust 259개·프런트엔드 55개·실제 SSH 브라우저 회귀·협업 맥락 12단계를 통과했습니다. 같은 제품 코드를 Apple Silicon macOS 27.0의 별도 네이티브 디버그 앱과 실제 SSH에서도 확인했습니다. 승인 동작 18회(빠른 거부 12회 포함), 전체 화면·커서 비교 38회, 사람의 편집·제어권 회수, 세션 허용, 기존 셸 유지와 개행 구분이 통과했습니다. 빠른 거부 중 ResizeObserver 경고 24건은 남았지만 확인한 모션 종료 지점에서는 지속되는 불일치가 없었습니다. 사용자 실제 원격 환경·물리 키보드 IME·별도 canonical `/bin/sh` 현상은 미검증 또는 미해결이며, 배포 파일 서명·공증과 설치 앱 업데이트 검증은 별개입니다. [검증 보고서](docs/ssh-input-review-results.ko.md#pr-64-네이티브-macos-검증).

## 0.8.5 — Preview · 2026-09-22

- Restore policy-based Autopilot in POSIX SSH: ordinary commands run, risky commands request approval, and deny rules remain blocked. Remote paths are never inspected as host files.
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ members = ["crates/core", "crates/cli", "crates/frontend", "crates/browser-harne
default-members = ["crates/core", "crates/cli", "crates/frontend", "crates/browser-harness"]

[workspace.package]
version = "0.8.5"
version = "0.8.6"
edition = "2021"
license = "MIT"
repository = "https://github.com/eggp-dev/conn"

[workspace.dependencies]
conn-core = { path = "crates/core", version = "0.8.5" }
conn-frontend = { path = "crates/frontend", version = "0.8.5" }
conn-core = { path = "crates/core", version = "0.8.6" }
conn-frontend = { path = "crates/frontend", version = "0.8.6" }
tokio = { version = "1", features = ["rt-multi-thread", "net", "io-util", "sync", "time", "macros", "signal"] }
portable-pty = "0.8"
vt100 = { path = "vendor/vt100", version = "0.15.2" }
Expand Down
10 changes: 5 additions & 5 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ curl -fsSL https://conn.eggp.dev/install.sh | sh

Rust나 Node.js는 필요 없습니다. 에이전트 연결 도구가 함께 들어 있습니다. 두 방법 모두 아래 다운로드와 같은 서명된 릴리스 파일을 설치하며, 이후에는 앱 안에서 업데이트됩니다. Windows는 설치 파일을 사용하세요.

| 운영체제 | v0.8.5 프리뷰 |
| 운영체제 | v0.8.6 프리뷰 |
|---|---|
| macOS · Apple Silicon | [Mac용 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [설치 파일 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [.deb 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.AppImage) |
| macOS · Apple Silicon | [Mac용 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [설치 파일 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [.deb 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.AppImage) |

Mac 배포 파일은 서명·공증되어 있습니다. Windows 프리뷰는 무서명이며, Linux는 Ubuntu 24.04에서 빌드하며, AppImage는 앱 안에서 업데이트되고 `.deb`는 새 패키지를 설치해 업데이트합니다. Intel Mac 배포는 잠시 중단했습니다. [설치·업데이트 안내](docs/getting-started.ko.md) · [전체 파일·체크섬](https://github.com/eggp-dev/conn/releases/tag/v0.8.5)
Mac 배포 파일은 서명·공증되어 있습니다. Windows 프리뷰는 무서명이며, Linux는 Ubuntu 24.04에서 빌드하며, AppImage는 앱 안에서 업데이트되고 `.deb`는 새 패키지를 설치해 업데이트합니다. Intel Mac 배포는 잠시 중단했습니다. [설치·업데이트 안내](docs/getting-started.ko.md) · [전체 파일·체크섬](https://github.com/eggp-dev/conn/releases/tag/v0.8.6)

## 처음 5분

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ curl -fsSL https://conn.eggp.dev/install.sh | sh

No Rust or Node.js required; the agent connector is included. Both install the same signed release files as the downloads below, and the app updates itself afterwards. Windows: use the installer.

| Platform | v0.8.5 preview |
| Platform | v0.8.6 preview |
|---|---|
| macOS · Apple Silicon | [Download for Mac](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [Download installer](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [Download .deb](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.AppImage) |
| macOS · Apple Silicon | [Download for Mac](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [Download installer](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [Download .deb](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.AppImage) |

Mac downloads are signed and notarized. Windows previews are unsigned. Linux builds use Ubuntu 24.04, and the AppImage updates in-app while the `.deb` updates by installing a new package; Intel Mac distribution is paused. [Installation and updates](docs/getting-started.md) · [All assets and checksums](https://github.com/eggp-dev/conn/releases/tag/v0.8.5)
Mac downloads are signed and notarized. Windows previews are unsigned. Linux builds use Ubuntu 24.04, and the AppImage updates in-app while the `.deb` updates by installing a new package; Intel Mac distribution is paused. [Installation and updates](docs/getting-started.md) · [All assets and checksums](https://github.com/eggp-dev/conn/releases/tag/v0.8.6)

## First five minutes

Expand Down
10 changes: 9 additions & 1 deletion crates/core/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ pub struct OutputFrame {
pub data: Vec<u8>,
pub output_seq: u64,
pub generation: u64,
pub size: crate::screen::Size,
}
pub type OutputFrameSink = Box<dyn Fn(&OutputFrame) + Send + Sync>;

Expand Down Expand Up @@ -851,9 +852,13 @@ impl Session {

fn write_output(&mut self, bytes: &[u8]) {
if bytes.is_empty() { return; }
self.emit_output_frame(bytes);
}

fn emit_output_frame(&mut self, bytes: &[u8]) {
self.output_seq = self.output_seq.saturating_add(1);
if let Some(sink) = &self.output_frame_sink {
sink(&OutputFrame { data: bytes.to_vec(), output_seq: self.output_seq, generation: self.surface_generation });
sink(&OutputFrame { data: bytes.to_vec(), output_seq: self.output_seq, generation: self.surface_generation, size: self.screen.size() });
}
}

Expand Down Expand Up @@ -1025,6 +1030,9 @@ impl Session {
pub fn resize(&mut self, rows: u16, cols: u16) {
self.screen.resize(rows, cols);
self.screen_dirty = true;
// Order renderer dimensions with PTY bytes under the same session lock.
// An IPC acknowledgement alone can overtake output event delivery.
self.emit_output_frame(&[]);
if let Some(m) = &self.master {
let _ = m.resize(PtySize { rows, cols, pixel_width: 0, pixel_height: 0 });
}
Expand Down
2 changes: 1 addition & 1 deletion crates/frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ fn terminal_output(app: AppHandle, session: String) -> conn_core::session::Outpu
use base64::Engine as _;
if let Some(state) = app.state.upgrade() {
let data = base64::engine::general_purpose::STANDARD.encode(&frame.data);
let value = json!({"session":session,"data":data,"outputSeq":frame.output_seq,"generation":frame.generation});
let value = json!({"session":session,"data":data,"outputSeq":frame.output_seq,"generation":frame.generation,"size":frame.size});
let mut output = state.output.lock();
match output.get_mut(&session) {
Some(Some(buffer)) => {
Expand Down
21 changes: 21 additions & 0 deletions crates/frontend/tests/surface_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ fn native_output_is_sequenced_and_renderer_cannot_inject_observations() {
assert_eq!(h.invoke("status",json!({"session":id})).unwrap()["surfaceAvailable"],true);
}
#[test]
fn resize_is_ordered_with_owner_output_even_when_the_shell_is_silent() {
use base64::Engine as _;
let (_dir,h,output,id)=setup();
h.invoke("resize",json!({"session":id,"rows":27,"cols":104})).unwrap();
let marker=output.lock().unwrap().iter().rposition(|v|v["data"]==""&&v["size"]==json!({"rows":27,"cols":104})).expect("silent resize frame");
h.invoke("input",json!({"session":id,"data":"printf '\\nSYNTHETIC_RESIZE_OUTPUT\\n'\n"})).unwrap();
let deadline=Instant::now()+Duration::from_secs(4);
loop {
let entries=output.lock().unwrap();
let bytes:Vec<u8>=entries[marker+1..].iter().flat_map(|v|base64::engine::general_purpose::STANDARD.decode(v["data"].as_str().unwrap()).unwrap()).collect();
if String::from_utf8_lossy(&bytes).contains("SYNTHETIC_RESIZE_OUTPUT") {
assert!(entries[marker..].iter().all(|v|v["size"]==json!({"rows":27,"cols":104})));
assert!(entries.windows(2).all(|w|w[0]["outputSeq"].as_u64()<w[1]["outputSeq"].as_u64()));
break;
}
drop(entries);assert!(Instant::now()<deadline);std::thread::sleep(Duration::from_millis(10));
}
h.invoke("resize",json!({"session":id,"rows":38,"cols":104})).unwrap();
assert!(output.lock().unwrap().iter().any(|v|v["data"]==""&&v["size"]==json!({"rows":38,"cols":104})));
}
#[test]
fn sharing_changes_keep_same_process_and_private_input_is_not_retroactive() {
let (dir,h,_output,id)=setup();
h.invoke("set_sharing",json!({"session":id,"shared":false,"connectionIds":[]})).unwrap();
Expand Down
7 changes: 5 additions & 2 deletions docs/browser-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ already be listening on `127.0.0.1:22222`; the test accepts only its synthetic
password and pinned known-hosts file. It authenticates through the same visible PTY
and uses remote Bash with `LC_ALL=C.UTF-8` and `TERM=xterm-256color`.

Coverage includes long ASCII/Unicode input, settings modal open/close without PTY
resizing, a long risky command held for approval while resizing the window, the
Coverage includes long ASCII/Unicode input, short/wrapped JSON with no trailing newline versus
LF/CRLF (including prompt placement), settings modal open/close without PTY
resizing, intentionally delayed resizes to expose native IPC and output ordering, and fixed-window
approval open/deny with actual terminal motion and a delayed dock shrink. It also checks pane coverage
at several emulated pixel densities, a long risky command held for approval while resizing the window, the
session-allow button and its limited scope, a separate risk still requesting approval,
policy denial, human cursor movement/insertion, takeover, and return from SSH to the
original local shell. No privileged command is approved. This is Linux Chromium
Expand Down
20 changes: 10 additions & 10 deletions docs/getting-started.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,37 @@ brew install --cask eggp-dev/tap/conn
curl -fsSL https://conn.eggp.dev/install.sh | sh # Linux x86_64
```

cask는 서명·공증된 DMG를 설치하며 릴리스와 함께 공개된 SHA-256으로 확인합니다. Linux 스크립트는 AppImage를 내려받아 릴리스의 `SHA256SUMS`와 대조한 뒤 내 계정에만 설치합니다(`~/.local/bin`, 앱 메뉴 항목, sudo 불필요). 체크섬이 맞지 않으면 설치하지 않습니다. `CONN_VERSION=v0.8.5`로 버전을 고정할 수 있습니다. AppImage는 FUSE 2가 필요합니다. Ubuntu 24.04 이상에서는 `sudo apt install libfuse2t64`를 실행하세요. 두 방법 모두 이후에는 앱 안에서 업데이트됩니다. 셸에 넘기기 전에 스크립트를 읽어 보셔도 좋습니다: [`scripts/install.sh`](../scripts/install.sh).
cask는 서명·공증된 DMG를 설치하며 릴리스와 함께 공개된 SHA-256으로 확인합니다. Linux 스크립트는 AppImage를 내려받아 릴리스의 `SHA256SUMS`와 대조한 뒤 내 계정에만 설치합니다(`~/.local/bin`, 앱 메뉴 항목, sudo 불필요). 체크섬이 맞지 않으면 설치하지 않습니다. `CONN_VERSION=v0.8.6`로 버전을 고정할 수 있습니다. AppImage는 FUSE 2가 필요합니다. Ubuntu 24.04 이상에서는 `sudo apt install libfuse2t64`를 실행하세요. 두 방법 모두 이후에는 앱 안에서 업데이트됩니다. 셸에 넘기기 전에 스크립트를 읽어 보셔도 좋습니다: [`scripts/install.sh`](../scripts/install.sh).

직접 파일을 받으려면:

OS와 CPU에 맞는 [v0.8.5 프리뷰](https://github.com/eggp-dev/conn/releases/tag/v0.8.5)를 받으세요. 실제 서명 상태와 실행 검증 결과는 릴리스 안내에서 확인할 수 있습니다.
OS와 CPU에 맞는 [v0.8.6 프리뷰](https://github.com/eggp-dev/conn/releases/tag/v0.8.6)를 받으세요. 실제 서명 상태와 실행 검증 결과는 릴리스 안내에서 확인할 수 있습니다.

| 플랫폼 | 다운로드 | 설치 |
|---|---|---|
| Ubuntu x64 | [`.deb`](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.deb) | 아래 명령으로 설치한 뒤 앱 목록에서 Conn을 엽니다. |
| Ubuntu x64 | [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.AppImage) | 실행 권한을 준 뒤 파일을 엽니다. |
| macOS Apple Silicon | [`.dmg`](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-aarch64-apple-darwin-desktop.dmg) | DMG를 열고 Conn을 Applications로 옮긴 뒤 그곳에서 실행합니다. |
| Windows x64 | [설치 `.exe`](https://github.com/eggp-dev/conn/releases/download/v0.8.5/conn-v0.8.5-x86_64-pc-windows-msvc-setup.exe) | 설치 파일을 실행하고 시작 메뉴에서 Conn을 엽니다. |
| Ubuntu x64 | [`.deb`](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.deb) | 아래 명령으로 설치한 뒤 앱 목록에서 Conn을 엽니다. |
| Ubuntu x64 | [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.AppImage) | 실행 권한을 준 뒤 파일을 엽니다. |
| macOS Apple Silicon | [`.dmg`](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-aarch64-apple-darwin-desktop.dmg) | DMG를 열고 Conn을 Applications로 옮긴 뒤 그곳에서 실행합니다. |
| Windows x64 | [설치 `.exe`](https://github.com/eggp-dev/conn/releases/download/v0.8.6/conn-v0.8.6-x86_64-pc-windows-msvc-setup.exe) | 설치 파일을 실행하고 시작 메뉴에서 Conn을 엽니다. |

새 릴리스의 Intel Mac 배포는 잠시 중단합니다. 기존 Intel 파일은 [이전 릴리스](https://github.com/eggp-dev/conn/releases)에 유지하며 Intel Mac에 Apple Silicon 파일을 설치하지 마세요.

Ubuntu에서는 다운로드한 폴더에서 실행합니다.

```sh
sudo apt install ./conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.deb
sudo apt install ./conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.deb
```

AppImage를 선택했다면 다음과 같이 실행합니다.

```sh
chmod +x ./conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.AppImage
./conn-v0.8.5-x86_64-unknown-linux-gnu-desktop.AppImage
chmod +x ./conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.AppImage
./conn-v0.8.6-x86_64-unknown-linux-gnu-desktop.AppImage
```

Linux 빌드 대상은 Ubuntu 24.04·26.04 x64이며 AppImage에도 시스템 라이브러리가 필요합니다. Windows 프리뷰는 의도적으로 무서명 배포하므로 SmartScreen·알 수 없는 배포자 경고가 나올 수 있고 관리되는 PC에서는 설치가 차단될 수 있습니다. Mac 공개 파일은 릴리스의 Developer ID 서명·공증 검사를 통과해야 합니다. 각 대상의 범위는 [플랫폼 지원](platform-support.ko.md)을 참고하세요.

원하면 파일과 함께 [SHA256SUMS](https://github.com/eggp-dev/conn/releases/download/v0.8.5/SHA256SUMS)를 받으세요. Linux는 `sha256sum --ignore-missing -c SHA256SUMS`, macOS는 `shasum -a 256 <파일>` 결과와 해당 줄 비교, Windows는 `Get-FileHash <파일> -Algorithm SHA256`을 사용합니다. 체크섬은 파일 손상을 확인하며 코드 서명과는 별개입니다.
원하면 파일과 함께 [SHA256SUMS](https://github.com/eggp-dev/conn/releases/download/v0.8.6/SHA256SUMS)를 받으세요. Linux는 `sha256sum --ignore-missing -c SHA256SUMS`, macOS는 `shasum -a 256 <파일>` 결과와 해당 줄 비교, Windows는 `Get-FileHash <파일> -Algorithm SHA256`을 사용합니다. 체크섬은 파일 손상을 확인하며 코드 서명과는 별개입니다.

## 2. 터미널 설정

Expand Down
Loading