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
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ jobs:
app="$(find release -type d -name 'CanvasTTY.app' -print -quit)"
test -n "$app"
codesign --verify --deep --strict --verbose=4 "$app"
- name: Smoke-test packaged macOS CLI resolution
if: runner.os == 'macOS'
run: npm run smoke:cli-resolution:mac
- name: Install AppImage runtime dependency
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libfuse2t64
Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Electron main process
- `src/main/services/browser/` contains the browser kernel. `BrowserStore` atomically persists only tab order, active tab, and safe restore URLs. `BrowserPolicyService` centralizes URL, permission, download, and upload rules; validated uploads are copied through an already-open no-follow file descriptor into private staging before Chromium sees them. `BrowserAutomationService` attaches Electron's internal debugger to the existing live tab without a remote-debugging port. `BrowserCommandDispatcher` adds revisions, revision-bound refs, mutation request deduplication, per-tab FIFO mutation lanes, bounded concurrency, typed errors, and redacted fail-closed audit for agent mutations.
- `src/main/services/agent-browser/` exposes the kernel only through an authenticated user-local Unix socket (`0600`) or Windows named pipe. The Windows pipe is created by the bundled native host with a protected DACL containing only the exact current-user SID and rejects remote clients. Each agent PTY receives a one-use bootstrap capability through its child environment. A successful authentication rotates it to a session-scoped reconnect capability held only in helper memory; duplicate bootstrap authentication is accepted only while the same `connectionId` is already live, and every capability is revoked when the PTY ends. The bundled stdio MCP helper is the only protocol adapter; no TCP listener, cookie/storage endpoint, arbitrary evaluation tool, or raw CDP surface exists.
- `TerminalManager` injects the MCP helper per launch without leaving permanent provider configuration. Claude Code and Codex receive CLI arguments; OpenCode receives a merged launch-only `OPENCODE_CONFIG_CONTENT` entry plus one scoped browser-tool permission; Kimi uses its per-run MCP configuration when supported. Older Kimi versions receive a compare-and-swap temporary CanvasTTY entry and one exact permission rule with an atomic recovery journal. Hermes receives a temporary `mcp_servers.canvastty_browser` entry in `HERMES_HOME/config.yaml` (defaulting to `~/.hermes/config.yaml` on POSIX or `%LOCALAPPDATA%\hermes\config.yaml` on Windows); sensitive capability values stay as child-environment placeholders. Temporary Kimi and Hermes configuration remains until the final owning PTY session ends, then exact original bytes are restored when safe. A journal repairs an interrupted Hermes launch at the next CanvasTTY startup, while compare-and-swap checks preserve concurrent user edits. Unrelated MCP entries, credentials, and file/shell permissions are preserved. OpenCode YOLO is a launch-only inline override, and Hermes YOLO passes its native `--yolo` flag; neither changes persistent permission settings.
- `src/main/services/cliEnvironment.ts` supplements the graphical-session `PATH` with existing per-user CLI directories before any provider process is spawned. It never reads shell startup scripts.
- `src/main/services/providerCliRegistry.ts` is the single owner of provider CLI discovery. During main-process startup it creates one immutable snapshot for Codex, Claude, Kimi, OpenCode, Hermes, and Grok Build by checking smoke-only overrides, the inherited `PATH`, platform defaults, and known per-user/provider directories in that order. Available entries retain an absolute executable, launcher kind, and supplemented child `PATH`; POSIX entries must be executable files and Windows entries must be supported native or batch launchers. `TerminalManager`, `LimitsService`, agent-browser probes, and provider smoke tests consume that same snapshot and never repeat command lookup. Missing entries produce a failed session with copyable checked-path diagnostics before PTY or temporary browser configuration creation, and the matching HOME limit stays `cli-not-found`. CanvasTTY never reads shell startup scripts, and installing or moving a CLI requires restarting the app.

The primary `BrowserWindow` is created and shown with a lightweight local startup page before settings, plugins, media, and IPC services initialize. Successful initialization replaces that page with the trusted renderer; bootstrap failures replace it with a visible error page and retain a native-dialog fallback. The main process holds Electron's single-instance lock and restores/focuses the existing window when another launch is attempted.

Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Electron main process
- `src/main/services/PluginMediaService.ts` сохраняет разрешения только после нативного выбора папки, скрывает абсолютные пути, пропускает symlinks и отдаёт аудио с HTTP Range. Чтение плейлистов остаётся внутри разрешённых библиотек; ограниченная атомарная запись разрешена только в `Playlists/`.
- `src/main/services/BrowserService.ts` владеет вкладками встроенного браузера в `WebContentsView`. Удалённые страницы используют отдельный persistent partition с выключенным Node, включёнными context isolation/sandbox и отклонением website permissions по умолчанию. Это core service, а не возможность runtime-плагина.
- `TerminalManager` подмешивает MCP helper, не оставляя постоянных изменений в provider-конфигах. Claude Code и Codex получают CLI arguments, OpenCode — объединённый launch-only `OPENCODE_CONFIG_CONTENT` с одной scoped browser-tool permission, Kimi — per-run MCP config или временную запись с compare-and-swap и recovery journal для старых версий. Hermes получает временную запись `mcp_servers.canvastty_browser` в `HERMES_HOME/config.yaml` (по умолчанию `~/.hermes/config.yaml` в POSIX или `%LOCALAPPDATA%\hermes\config.yaml` в Windows); чувствительные capability-значения остаются ссылками на окружение дочернего процесса. Временная конфигурация Kimi и Hermes живёт до завершения последней владеющей PTY-сессии, после чего исходные байты точно восстанавливаются, если файл не менялся параллельно. Journal восстанавливает Hermes после прерванного запуска при следующем старте CanvasTTY, а compare-and-swap сохраняет одновременные пользовательские изменения. Сторонние MCP-записи, credentials и file/shell permissions не затрагиваются. OpenCode YOLO существует только как inline override текущего запуска, а Hermes получает штатный `--yolo`; постоянные permission-настройки не меняются.
- `src/main/services/cliEnvironment.ts` дополняет `PATH` графической сессии существующими пользовательскими каталогами CLI до запуска любого процесса провайдера. Shell startup scripts не читаются.
- `src/main/services/providerCliRegistry.ts` — единственный владелец обнаружения provider CLI. При запуске main-процесса он создаёт один неизменяемый snapshot для Codex, Claude, Kimi, OpenCode, Hermes и Grok Build, последовательно проверяя smoke-only overrides, унаследованный `PATH`, системные каталоги платформы и известные пользовательские/provider-каталоги. Доступная запись хранит абсолютный executable, тип launcher-а и дополненный дочерний `PATH`; POSIX-кандидат обязан быть исполняемым файлом, а Windows-кандидат — поддерживаемым native или batch launcher-ом. `TerminalManager`, `LimitsService`, agent-browser probes и provider smoke используют один и тот же snapshot и не повторяют поиск команды. Недоступный CLI создаёт failed-сессию с копируемой диагностикой проверенных путей до создания PTY или временной browser-конфигурации, а соответствующий HOME limit остаётся `cli-not-found`. CanvasTTY не читает shell startup scripts; после установки или перемещения CLI приложение нужно перезапустить.

Основной `BrowserWindow` создаётся и показывается с лёгкой локальной стартовой страницей до инициализации settings, plugins, media и IPC. Успешная инициализация заменяет её доверенным renderer; bootstrap failure показывает видимую error page и сохраняет fallback на native dialog. Main process удерживает single-instance lock и восстанавливает/фокусирует существующее окно при повторном запуске.

Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Electron main process
- `src/main/services/PluginMediaService.ts` 仅在原生目录选择后保存授权,隐藏绝对路径,跳过 symlink,并以 HTTP Range 提供音频。Playlist 读取限制在授权媒体库内;写入受大小限制,并且只能原子写入 `Playlists/`。
- `src/main/services/BrowserService.ts` 管理内置浏览器的 `WebContentsView` tab。远程页面使用独立 persistent partition,禁用 Node,启用 context isolation/sandbox,并默认拒绝网站权限。这是 core service,不是 runtime 插件能力。
- `TerminalManager` 注入 MCP helper 时不会留下永久的服务商配置变更。Claude Code 与 Codex 使用 CLI 参数;OpenCode 使用合并后的、仅本次启动有效的 `OPENCODE_CONFIG_CONTENT` 和一条 scoped browser-tool 权限;Kimi 使用 per-run MCP 配置,旧版本则使用带 compare-and-swap 与 recovery journal 的临时配置。Hermes 会在 `HERMES_HOME/config.yaml` 中获得临时 `mcp_servers.canvastty_browser` 配置项(POSIX 默认路径为 `~/.hermes/config.yaml`,Windows 默认路径为 `%LOCALAPPDATA%\hermes\config.yaml`),敏感 capability 值仍以子进程环境变量占位符保存。Kimi 与 Hermes 的临时配置会保留到最后一个所属 PTY 会话结束;若文件未被并发修改,则精确恢复原始字节。若 Hermes 启动意外中断,journal 会在 CanvasTTY 下次启动时修复配置,compare-and-swap 则保留用户的并发修改。其他 MCP 配置项、凭据和文件/shell 权限不会受影响。OpenCode YOLO 仅使用本次启动的 inline override,Hermes 则使用原生 `--yolo` 参数;两者都不修改持久权限设置。
- `src/main/services/cliEnvironment.ts` 会在启动任何服务商进程前,用现有用户 CLI 目录补充图形会话的 `PATH`,且不会读取 shell startup script。
- `src/main/services/providerCliRegistry.ts` 是服务商 CLI 发现的唯一职责边界。main 进程启动时,它按 smoke-only override、继承的 `PATH`、平台默认目录、已知用户/服务商目录的顺序,为 Codex、Claude、Kimi、OpenCode、Hermes 与 Grok Build 创建一个不可变快照。可用条目保存绝对 executable、launcher 类型以及补充后的子进程 `PATH`;POSIX 候选必须是可执行文件,Windows 候选必须是受支持的 native 或 batch launcher。`TerminalManager`、`LimitsService`、agent-browser probe 与 provider smoke 共用该快照,不再各自查找命令。CLI 不可用时,系统会在创建 PTY 或临时 browser 配置之前生成 failed session,并提供可复制的已检查路径诊断;对应的 HOME limit 同样保持 `cli-not-found`。CanvasTTY 不读取 shell startup script;安装或移动 CLI 后必须重启应用

主 `BrowserWindow` 在 settings、plugins、media 和 IPC 服务初始化之前创建并显示轻量本地启动页。初始化成功后替换为可信 renderer;bootstrap 失败后替换为可见错误页,并保留原生对话框 fallback。主进程持有 Electron single-instance lock;再次启动时恢复并聚焦已有窗口。

Expand Down
2 changes: 1 addition & 1 deletion docs/UI_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This contract preserves the approved MVP concept and prevents feature ownership

- HOME uses fixed-size `82 × 72` logical cells with `18px` gaps so enlarging the page never makes existing widgets smaller. Fresh profiles start at `16 × 12`, while the original arrangement remains in the upper-left `12 × 8` area and leaves explicit spare space for plugins. Its persisted boundary and cell grid are shown only in Edit HOME, where the bottom-right corner resizes the area up to the generous `48 × 36` safety ceiling.
- The wide left tile contains the user-selected subset of the real Codex, Claude, Kimi, OpenCode Go, and Grok Build limit rows; all five are visible by default. Visible rows divide the tile height equally, while four or five rows switch to a compact density that keeps every icon, countdown, and usage rail inside the default tile. An empty selection shows an explicit empty state. Display selection does not disable background limit reads and never offers providers without a real adapter. Each row prefers the provider's longest real default quota window (the weekly window when exposed), and falls back to another real window only when needed. It shows the countdown to that window's `resetsAt` and the matching usage rail. Values below one day use `HH:MM`; longer values use `Nд HHч`/`Nd HHh`. Window length stays in accessible metadata; unavailable data is labeled and has no fake reset or percentage. A short yellow rail means the last valid snapshot is shown because its latest refresh failed; it is not selection or usage progress.
- The right tile is the only session list. Its viewport shows three rows and scrolls when more real sessions exist; it never discards rows. Each row shows provider mark, localized semantic state, and identity. Session duration and limit-style progress rails never appear here.
- The right tile is the only session list. Its viewport shows three rows and scrolls when more real sessions exist; it never discards rows. Each row shows provider mark, localized semantic state, and identity. Hovering a failed row's error mark exposes its bounded, terminal-control-free final PTY output, including a supplied traceback, in an accessible panel; its Copy action copies that displayed output only. When the PTY emitted no visible output, the panel instead states that fact with the actual exit code. It never invents a reason or derives the state from terminal text. Session duration and limit-style progress rails never appear here.
- The clock is the dominant middle tile and renders only `HH:MM`. The adjacent media tile is an autonomous widget: click to pick or replace an image/GIF; remove from the widget itself.
- The bottom dock always contains Terminal and Browser, plus the agent providers enabled in Settings. Its equal-width columns are derived from the buttons that are actually visible, so disabling an agent never leaves an empty slot. All current agents are enabled for fresh and migrated profiles; hiding a launcher never stops or removes an existing session. Settings is a separate tile. Browser opens or focuses the one built-in browser card and never launches an external browser. Agent badges never overlay the Browser launcher icon.
- Every default tile except Settings may be hidden. Settings remains the recovery entry point. Edit HOME mode shows the full cell grid and exact HOME boundary, hides all terminal and canvas-plugin windows, and keeps its changes as a draft until Save. Tiles move without overlap and may temporarily cross any HOME edge; Save is disabled until every tile is fully inside. Every tile edge and corner resizes it while preserving the opposite edge, with visible cues only at the top-left and bottom-right corners. The boundary can grow or shrink without crossing placed widgets. Adding a widget automatically grows HOME when the current boundary is full.
Expand Down
Loading