From 138371525ac92618d5e9202ed1e37848cff2d120 Mon Sep 17 00:00:00 2001
From: Haileyesus <118998054+blackmammoth@users.noreply.github.com>
Date: Mon, 6 Jul 2026 21:08:22 +0300
Subject: [PATCH] fix: address code review findings
- validate chat image attachments server-side: only files inside the
~/.cloudcli/assets upload store may reach provider file reads
- harden asset serving with nosniff and attachment disposition for
SVGs to prevent stored XSS
- show the timestamp on image-only user messages
- serialize git stage/unstage calls and defer the status re-sync so
rapid toggles can't interleave or flicker
- use a literal hex fallback for commit ref badges (alpha suffix on a
var() string produced invalid CSS)
- stop binding a URL session to a guening
project instead
- add the missing attentionRequiredIndicator key to all sidebar locales
- clean up dangling conjunctions left the
de/ru/tr/ja/zh-CN/zh-TW READMEs
---
README.de.md | 10 ++---
README.ja.md | 14 +++---
README.ru.md | 12 ++---
README.tr.md | 12 ++---
README.zh-CN.md | 16 +++----
README.zh-TW.md | 16 +++----
server/modules/assets/assets.routes.ts | 12 ++++-
.../services/chat-websocket.service.ts | 37 ++++++++++++++++
.../websocket/tests/chat-image-filter.test.ts | 44 +++++++++++++++++++
.../view/subcomponents/MessageComponent.tsx | 7 ++-
.../git-panel/view/changes/ChangesView.tsx | 38 ++++++++++++----
.../view/history/CommitHistoryItem.tsx | 4 +-
src/hooks/useProjectsState.ts | 13 +++---
src/i18n/locales/de/sidebar.json | 3 +-
src/i18n/locales/en/sidebar.json | 3 +-
src/i18n/locales/fr/sidebar.json | 3 +-
src/i18n/locales/it/sidebar.json | 3 +-
src/i18n/locales/ja/sidebar.json | 3 +-
src/i18n/locales/ko/sidebar.json | 5 ++-
src/i18n/locales/ru/sidebar.json | 3 +-
src/i18n/locales/tr/sidebar.json | 3 +-
src/i18n/locales/zh-CN/sidebar.json | 3 +-
src/i18n/locales/zh-TW/sidebar.json | 3 +-
23 files changed, 197 insertions(+), 70 deletions(-)
create mode 100644 server/modules/websocket/tests/chat-image-filter.test.ts
diff --git a/README.de.md b/README.de.md
index e028ba4d..c2ee36aa 100644
--- a/README.de.md
+++ b/README.de.md
@@ -62,7 +62,7 @@
- **Sitzungsverwaltung** – Gespräche fortsetzen, mehrere Sitzungen verwalten und Verlauf nachverfolgen
- **Plugin-System** – CloudCLI mit eigenen Plugins erweitern – neue Tabs, Backend-Dienste und Integrationen hinzufügen. [Eigenes Plugin erstellen →](https://github.com/cloudcli-ai/cloudcli-plugin-starter)
- **TaskMaster AI Integration** *(Optional)* – Erweitertes Projektmanagement mit KI-gestützter Aufgabenplanung, PRD-Parsing und Workflow-Automatisierung
-- **Modell-Kompatibilität** – Funktioniert mit Claude, GPT und (vollständige Liste unterstützter Modelle zur Laufzeit über `GET /api/providers/:provider/models`)
+- **Modell-Kompatibilität** – Funktioniert mit den Claude- und GPT-Modellfamilien (vollständige Liste unterstützter Modelle zur Laufzeit über `GET /api/providers/:provider/models`)
## Schnellstart
@@ -103,7 +103,7 @@ Agents in isolierten Sandboxes mit Hypervisor-Isolation ausführen. Standardmä
npx @cloudcli-ai/cloudcli@latest sandbox ~/my-project
```
-Unterstützt Claude Code, Codex und. Weitere Details in der [Sandbox-Dokumentation](docker/).
+Unterstützt Claude Code und Codex. Weitere Details in der [Sandbox-Dokumentation](docker/).
---
@@ -166,7 +166,7 @@ CloudCLI verfügt über ein Plugin-System, mit dem benutzerdefinierte Tabs mit e
| **[Project Stats](https://github.com/cloudcli-ai/cloudcli-plugin-starter)** | Zeigt Dateianzahl, Codezeilen, Dateityp-Aufschlüsselung, größte Dateien und zuletzt geänderte Dateien des aktuellen Projekts |
| **[Web Terminal](https://github.com/cloudcli-ai/cloudcli-plugin-terminal)** | Vollwertiges xterm.js-Terminal mit Multi-Tab-Unterstützung |
| **[Claude Watch](https://github.com/satsuki19980613/cloudcli-claude-watch)** | Überwacht lange laufende Claude-Code-Sitzungen auf Hänger und stellt Prozesssteuerungen bereit |
-| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | Erstellt arbeitsbereichsbezogene geplante Prompts und führt sie über eine lokale CLI wie Codex, Claude Code oder aus |
+| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | Erstellt arbeitsbereichsbezogene geplante Prompts und führt sie über eine lokale CLI wie Codex oder Claude Code aus |
| **[PRISM CloudCLI](https://github.com/jakeefr/cloudcli-plugin-prism)** | Sitzungsintelligenz für Claude Code in CloudCLI, inklusive Sichtbarkeit des Token-Verbrauchs |
| **[Sessions](https://github.com/strykereye2/cloudcli-plugin-session-manager)** | Aktive Claude-Code-Sitzungen anzeigen, verwalten und beenden |
| **[Token Cost Calculator](https://github.com/NightmareAway/cloudcli-plugin-token-cost-calculator)** | API-Kosten anhand von Modellpreisen und Token-Nutzung berechnen, mit Unterstützung für Preisvorlagen |
@@ -193,7 +193,7 @@ Das bedeutet in der Praxis:
- **Alle Sitzungen, nicht nur eine** – CloudCLI UI erkennt automatisch jede Sitzung aus dem `~/.claude`-Ordner. Remote Control stellt nur die einzelne aktive Sitzung bereit, um sie in der Claude Mobile App verfügbar zu machen.
- **Deine Einstellungen sind deine Einstellungen** – MCP-Server, Tool-Berechtigungen und Projektkonfiguration, die in CloudCLI UI geändert werden, werden direkt in die Claude Code-Konfiguration geschrieben und treten sofort in Kraft – und umgekehrt.
-- **Funktioniert mit mehr Agents** – Claude Code, Cursor CLI, Codex und, nicht nur Claude Code.
+- **Funktioniert mit mehr Agents** – Claude Code, Cursor CLI und Codex, nicht nur Claude Code.
- **Vollständige UI, nicht nur ein Chat-Fenster** – Datei-Explorer, Git-Integration, MCP-Verwaltung und ein Shell-Terminal sind alle eingebaut.
- **CloudCLI Cloud läuft in der Cloud** – Laptop zuklappen, der Agent läuft weiter. Kein Terminal zu überwachen, kein Rechner, der laufen muss.
@@ -202,7 +202,7 @@ Das bedeutet in der Praxis:
Muss ich ein KI-Abonnement separat bezahlen?
-Ja. CloudCLI stellt die Umgebung bereit, nicht die KI. Du bringst dein eigenes Claude-, Cursor-, Codex- oder -Abonnement mit. CloudCLI Cloud beginnt bei €7/Monat für die gehostete Umgebung zusätzlich dazu.
+Ja. CloudCLI stellt die Umgebung bereit, nicht die KI. Du bringst dein eigenes Claude-, Cursor- oder Codex-Abonnement mit. CloudCLI Cloud beginnt bei €7/Monat für die gehostete Umgebung zusätzlich dazu.
diff --git a/README.ja.md b/README.ja.md
index 8691e2eb..a4cbf2c3 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -1,7 +1,7 @@
@@ -43,7 +43,7 @@
CLI 選択
-Claude Code、、Cursor CLI、Codex から選択
+Claude Code、Cursor CLI、Codex から選択
@@ -99,7 +99,7 @@ cloudcli
npx @cloudcli-ai/cloudcli@latest sandbox ~/my-project
```
-Claude Code、Codex、 に対応。詳細は[サンドボックスのドキュメント](docker/)をご覧ください。
+Claude Code、Codex に対応。詳細は[サンドボックスのドキュメント](docker/)をご覧ください。
---
@@ -115,7 +115,7 @@ CloudCLI UI は、CloudCLI Cloud を支えるオープンソースの UI レイ
| **マシンの稼働継続** | はい | いいえ |
| **モバイルアクセス** | 同一ネットワーク内の任意のブラウザ | 任意のデバイス(ネイティブアプリも準備中) |
| **利用可能なセッション** | `~/.claude` から全セッションを自動検出 | クラウド環境内の全セッション |
-| **対応エージェント** | Claude Code、Cursor CLI、Codex、 | Claude Code、Cursor CLI、Codex、 |
+| **対応エージェント** | Claude Code、Cursor CLI、Codex | Claude Code、Cursor CLI、Codex |
| **ファイルエクスプローラとGit** | はい(UI に内蔵) | はい(UI に内蔵) |
| **MCP設定** | UI で管理し、ローカルの `~/.claude` 設定と同期 | UI で管理 |
| **IDEアクセス** | ローカル IDE | クラウド環境に接続された任意の IDE |
@@ -160,7 +160,7 @@ CloudCLI にはプラグインシステムがあり、独自のフロントエ
| **[Project Stats](https://github.com/cloudcli-ai/cloudcli-plugin-starter)** | 現在のプロジェクトについて、ファイル数、コード行数、ファイル種別の内訳、最大ファイル、最近変更されたファイルを表示 |
| **[Web Terminal](https://github.com/cloudcli-ai/cloudcli-plugin-terminal)** | 複数タブに対応した本格的な xterm.js ターミナル |
| **[Claude Watch](https://github.com/satsuki19980613/cloudcli-claude-watch)** | 長時間実行中の Claude Code セッションのハングを監視し、プロセス操作を提供 |
-| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | ワークスペース単位のスケジュール済みプロンプトを作成し、Codex、Claude Code、 などのローカル CLI で実行 |
+| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | ワークスペース単位のスケジュール済みプロンプトを作成し、Codex、Claude Code などのローカル CLI で実行 |
| **[PRISM CloudCLI](https://github.com/jakeefr/cloudcli-plugin-prism)** | CloudCLI 内で Claude Code のセッション分析を行い、トークン消費の可視化も提供 |
| **[Sessions](https://github.com/strykereye2/cloudcli-plugin-session-manager)** | アクティブな Claude Code セッションを表示、管理、終了 |
| **[Token Cost Calculator](https://github.com/NightmareAway/cloudcli-plugin-token-cost-calculator)** | モデル価格とトークン使用量から API コストを計算し、モデル価格プリセットにも対応 |
@@ -185,7 +185,7 @@ CloudCLI UI と CloudCLI Cloud は、Claude Code の横に別物として存在
- **すべてのセッションにアクセス** — CloudCLI UI は `~/.claude` フォルダのすべてのセッションを自動検出します。Remote Control は、Claude モバイルアプリで利用可能にするため、1つのアクティブセッションだけを公開します。
- **設定はあなたの設定** — CloudCLI UI で変更した MCP サーバー、ツール権限、プロジェクト構成は、Claude Code の設定に直接書き込まれて即座に反映され、その逆(Claude Code での変更が UI に反映)も同様です。
-- **対応エージェントがさらに充実** — Claude Code に加えて Cursor CLI、Codex、 にも対応しています。
+- **対応エージェントがさらに充実** — Claude Code に加えて Cursor CLI、Codex にも対応しています。
- **チャット窓だけではない完全な UI** — ファイルエクスプローラー、Git 統合、MCP 管理、シェル端末などがすべて組み込まれています。
- **CloudCLI Cloud はクラウド上で稼働** — ノートパソコンを閉じてもエージェントは動き続けます。監視が要る端末も、スリープ防止も不要です。
@@ -194,7 +194,7 @@ CloudCLI UI と CloudCLI Cloud は、Claude Code の横に別物として存在
AI のサブスクリプションは別途支払いが必要ですか?
-はい。CloudCLI は環境を提供するものであり、AI は含まれません。Claude、Cursor、Codex、または のサブスクリプションはご自身でご用意ください。CloudCLI Cloud のホスティング環境はそれに加えて月額 €7 から提供されます。
+はい。CloudCLI は環境を提供するものであり、AI は含まれません。Claude、Cursor、Codex のいずれかのサブスクリプションはご自身でご用意ください。CloudCLI Cloud のホスティング環境はそれに加えて月額 €7 から提供されます。
diff --git a/README.ru.md b/README.ru.md
index 8fb907e2..82c5fc86 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -1,7 +1,7 @@
Cloud CLI (aka Claude Code UI)
-
Десктопный и мобильный UI для Claude Code, Cursor CLI, Codex и .
Используйте локально или удалённо, чтобы просматривать активные проекты и сессии отовсюду.
+
Десктопный и мобильный UI для Claude Code, Cursor CLI, Codex.
Используйте локально или удалённо, чтобы просматривать активные проекты и сессии отовсюду.
@@ -62,7 +62,7 @@
- **Управление сессиями** - возобновляйте диалоги, управляйте несколькими сессиями и отслеживайте историю
- **Система плагинов** - расширяйте CloudCLI кастомными плагинами — добавляйте новые вкладки, бэкенд-сервисы и интеграции. [Создать свой →](https://github.com/cloudcli-ai/cloudcli-plugin-starter)
- **Интеграция с TaskMaster AI** *(опционально)* - продвинутое управление проектами с планированием задач на базе AI, разбором PRD и автоматизацией workflow
-- **Совместимость с моделями** - работает с семействами моделей Claude, GPT и (полный список поддерживаемых моделей доступен через `GET /api/providers/:provider/models`)
+- **Совместимость с моделями** - работает с семействами моделей Claude и GPT (полный список поддерживаемых моделей доступен через `GET /api/providers/:provider/models`)
## Быстрый старт
@@ -103,7 +103,7 @@ cloudcli
npx @cloudcli-ai/cloudcli@latest sandbox ~/my-project
```
-Поддерживаются Claude Code, Codex и. Подробнее в [документации sandbox](docker/).
+Поддерживаются Claude Code и Codex. Подробнее в [документации sandbox](docker/).
---
@@ -166,7 +166,7 @@ CloudCLI UI — это open source UI-слой, на котором постро
| **[Project Stats](https://github.com/cloudcli-ai/cloudcli-plugin-starter)** | Показывает количество файлов, строки кода, разбивку по типам файлов, самые большие файлы и недавно изменённые файлы для текущего проекта |
| **[Web Terminal](https://github.com/cloudcli-ai/cloudcli-plugin-terminal)** | Полноценный терминал xterm.js с поддержкой нескольких вкладок |
| **[Claude Watch](https://github.com/satsuki19980613/cloudcli-claude-watch)** | Отслеживает зависания долгих сессий Claude Code и предоставляет управление процессами |
-| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | Создаёт запланированные промпты для рабочей области и запускает их через локальную CLI, например Codex, Claude Code или |
+| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | Создаёт запланированные промпты для рабочей области и запускает их через локальную CLI, например Codex или Claude Code |
| **[PRISM CloudCLI](https://github.com/jakeefr/cloudcli-plugin-prism)** | Аналитика сессий Claude Code внутри CloudCLI, включая видимость расхода токенов |
| **[Sessions](https://github.com/strykereye2/cloudcli-plugin-session-manager)** | Просмотр, управление и завершение активных сессий Claude Code |
| **[Token Cost Calculator](https://github.com/NightmareAway/cloudcli-plugin-token-cost-calculator)** | Расчёт стоимости API по ценам моделей и использованию токенов, с поддержкой пресетов цен |
@@ -193,7 +193,7 @@ CloudCLI UI и CloudCLI Cloud расширяют Claude Code, а не работ
- **Все ваши сессии, а не одна** — CloudCLI UI автоматически находит каждую сессию из папки `~/.claude`. Remote Control предоставляет только одну активную сессию, чтобы сделать её доступной в мобильном приложении Claude.
- **Ваши настройки — это ваши настройки** — MCP-серверы, права инструментов и конфигурация проекта, изменённые в CloudCLI UI, записываются напрямую в конфиг Claude Code и вступают в силу сразу же, и наоборот.
-- **Работает с большим числом агентов** — Claude Code, Cursor CLI, Codex и, а не только Claude Code.
+- **Работает с большим числом агентов** — Claude Code, Cursor CLI и Codex, а не только Claude Code.
- **Полноценный UI, а не просто окно чата** — проводник файлов, Git-интеграция, управление MCP и shell-терминал — всё встроено.
- **CloudCLI Cloud работает в облаке** — закройте ноутбук, и агент продолжит работать. Не нужно следить за терминалом и держать машину постоянно активной.
@@ -202,7 +202,7 @@ CloudCLI UI и CloudCLI Cloud расширяют Claude Code, а не работ
Нужно ли отдельно платить за AI-подписку?
-Да. CloudCLI предоставляет среду, а не сам AI. Вы приносите свою подписку Claude, Cursor, Codex или . CloudCLI Cloud начинается от €7/месяц за хостируемую среду поверх этого.
+Да. CloudCLI предоставляет среду, а не сам AI. Вы приносите свою подписку Claude, Cursor или Codex. CloudCLI Cloud начинается от €7/месяц за хостируемую среду поверх этого.
diff --git a/README.tr.md b/README.tr.md
index 6cd11c86..24c7f06b 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -1,7 +1,7 @@
Cloud CLI (Claude Code UI olarak da bilinir)
-
Claude Code, Cursor CLI, Codex ve için masaüstü ve mobil arayüz.
Yerel ya da uzaktan kullanarak aktif projelerine ve oturumlarına her yerden erişebilirsin.
+
Claude Code, Cursor CLI, Codex için masaüstü ve mobil arayüz.
Yerel ya da uzaktan kullanarak aktif projelerine ve oturumlarına her yerden erişebilirsin.
@@ -62,7 +62,7 @@
- **Oturum Yönetimi** — Konuşmalara devam et, birden fazla oturumu yönet ve geçmişi takip et
- **Eklenti Sistemi** — CloudCLI'ı özel eklentilerle genişlet: yeni sekmeler, arka uç servisleri ve entegrasyonlar ekle. [Kendi eklentini yaz →](https://github.com/cloudcli-ai/cloudcli-plugin-starter)
- **TaskMaster AI Entegrasyonu** *(İsteğe Bağlı)* — AI destekli görev planlama, PRD ayrıştırma ve iş akışı otomasyonu ile gelişmiş proje yönetimi
-- **Model Uyumluluğu** — Claude, GPT ve model aileleriyle çalışır (desteklenen tüm modeller için `GET /api/providers/:provider/models` API'sine bak)
+- **Model Uyumluluğu** — Claude ve GPT model aileleriyle çalışır (desteklenen tüm modeller için `GET /api/providers/:provider/models` API'sine bak)
## Hızlı Başlangıç
@@ -103,7 +103,7 @@ Ajanları hipervizör seviyesinde izolasyonlu sandbox'larda çalıştır. Varsay
npx @cloudcli-ai/cloudcli@latest sandbox ~/my-project
```
-Claude Code, Codex ve destekler. Kurulum ve gelişmiş seçenekler için [sandbox dokümantasyonuna](docker/) bak.
+Claude Code ve Codex'i destekler. Kurulum ve gelişmiş seçenekler için [sandbox dokümantasyonuna](docker/) bak.
---
@@ -165,7 +165,7 @@ CloudCLI, kendi frontend UI'sı ve isteğe bağlı Node.js arka ucu olan özel s
| **[Project Stats](https://github.com/cloudcli-ai/cloudcli-plugin-starter)** | Mevcut projen için dosya sayıları, kod satırları, dosya türü dağılımı, en büyük dosyalar ve son değiştirilen dosyaları gösterir |
| **[Web Terminal](https://github.com/cloudcli-ai/cloudcli-plugin-terminal)** | Çoklu sekme destekli tam xterm.js terminali |
| **[Claude Watch](https://github.com/satsuki19980613/cloudcli-claude-watch)** | Uzun süren Claude Code oturumlarını takılmalara karşı izler ve süreç kontrolleri sunar |
-| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | Çalışma alanı kapsamlı zamanlanmış prompt'lar oluşturur ve bunları Codex, Claude Code veya gibi yerel CLI'larla çalıştırır |
+| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | Çalışma alanı kapsamlı zamanlanmış prompt'lar oluşturur ve bunları Codex veya Claude Code gibi yerel CLI'larla çalıştırır |
| **[PRISM CloudCLI](https://github.com/jakeefr/cloudcli-plugin-prism)** | CloudCLI içinde Claude Code oturum zekası ve token tüketimi görünürlüğü sağlar |
| **[Sessions](https://github.com/strykereye2/cloudcli-plugin-session-manager)** | Aktif Claude Code oturumlarını görüntülemeni, yönetmeni ve sonlandırmanı sağlar |
| **[Token Cost Calculator](https://github.com/NightmareAway/cloudcli-plugin-token-cost-calculator)** | Model fiyatları ve token kullanımından API maliyetlerini hesaplar; model fiyatı hazır ayarlarını destekler |
@@ -192,7 +192,7 @@ Pratikte bu ne demek:
- **Tek oturum değil, tüm oturumların** — CloudCLI UI, `~/.claude` klasöründeki her oturumu otomatik keşfeder. Remote Control yalnızca tek aktif oturumu Claude mobil uygulamasına açar.
- **Ayarların sana ait** — UI'da değiştirdiğin MCP sunucuları, araç izinleri ve proje yapılandırması doğrudan Claude Code yapılandırmana yazılır ve anında etkili olur; tersi de geçerli.
-- **Daha fazla ajanla çalışır** — Sadece Claude Code değil; Cursor CLI, Codex ve de.
+- **Daha fazla ajanla çalışır** — Sadece Claude Code değil; Cursor CLI ve Codex de.
- **Sadece sohbet penceresi değil, tam UI** — dosya gezgini, Git entegrasyonu, MCP yönetimi ve shell terminali hepsi yerleşik.
- **CloudCLI Cloud bulutta çalışır** — laptop'unu kapat, ajan çalışmaya devam eder. Beklemen gereken terminal yok, uyanık tutman gereken makine yok.
@@ -201,7 +201,7 @@ Pratikte bu ne demek:
AI aboneliği için ayrıca ödeme yapmam gerekiyor mu?
-Evet. CloudCLI AI'yi değil, ortamı sağlar. Kendi Claude, Cursor, Codex veya aboneliğini getirirsin. CloudCLI Cloud, barındırılan ortam için aylık 7 €'dan başlar — bunun üzerine eklenir.
+Evet. CloudCLI AI'yi değil, ortamı sağlar. Kendi Claude, Cursor veya Codex aboneliğini getirirsin. CloudCLI Cloud, barındırılan ortam için aylık 7 €'dan başlar — bunun üzerine eklenir.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 4de00ee8..9b7c71b6 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,7 +1,7 @@
@@ -43,7 +43,7 @@
CLI 选择
-在 Claude Code、、Cursor CLI 与 Codex 之间进行选择
+在 Claude Code、Cursor CLI 与 Codex 之间进行选择
@@ -60,7 +60,7 @@
- **会话管理** - 恢复对话、管理多个会话并跟踪历史记录
- **插件系统** - 通过自定义选项卡、后端服务与集成扩展 CloudCLI。 [开始构建 →](https://github.com/cloudcli-ai/cloudcli-plugin-starter)
- **TaskMaster AI 集成** *(可选)* - 结合 AI 任务规划、PRD 分析与工作流自动化,实现高级项目管理
-- **模型兼容性** - 支持 Claude、GPT、 模型家族(完整支持列表可通过 `GET /api/providers/:provider/models` 接口获取)
+- **模型兼容性** - 支持 Claude、GPT 模型家族(完整支持列表可通过 `GET /api/providers/:provider/models` 接口获取)
## 快速开始
@@ -99,7 +99,7 @@ cloudcli
npx @cloudcli-ai/cloudcli@latest sandbox ~/my-project
```
-支持 Claude Code、Codex 和。详情请参阅 [沙箱文档](docker/)。
+支持 Claude Code 和 Codex。详情请参阅 [沙箱文档](docker/)。
---
@@ -115,7 +115,7 @@ CloudCLI UI 是 CloudCLI Cloud 的开源 UI 层。你可以在本地机器上自
| **机器需保持开机吗** | 是 | 否 |
| **移动端访问** | 网络内任意浏览器 | 任意设备(原生应用即将推出) |
| **可用会话** | 自动发现 `~/.claude` 中的所有会话 | 云端环境内的会话 |
-| **支持的 Agents** | Claude Code、Cursor CLI、Codex、 | Claude Code、Cursor CLI、Codex、 |
+| **支持的 Agents** | Claude Code、Cursor CLI、Codex | Claude Code、Cursor CLI、Codex |
| **文件浏览与 Git** | 内置于 UI | 内置于 UI |
| **MCP 配置** | UI 管理,与本地 `~/.claude` 配置同步 | UI 管理 |
| **IDE 访问** | 本地 IDE | 任何连接到云环境的 IDE |
@@ -160,7 +160,7 @@ CloudCLI 配备插件系统,允许你添加带自定义前端 UI 和可选 Nod
| **[Project Stats](https://github.com/cloudcli-ai/cloudcli-plugin-starter)** | 展示当前项目的文件数、代码行数、文件类型分布、最大文件以及最近修改的文件 |
| **[Web Terminal](https://github.com/cloudcli-ai/cloudcli-plugin-terminal)** | 支持多标签页的完整 xterm.js 终端 |
| **[Claude Watch](https://github.com/satsuki19980613/cloudcli-claude-watch)** | 监控长时间运行的 Claude Code 会话是否卡住,并提供进程控制 |
-| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | 创建工作区范围的定时提示词,并通过 Codex、Claude Code 或 等本地 CLI 执行 |
+| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | 创建工作区范围的定时提示词,并通过 Codex、Claude Code 等本地 CLI 执行 |
| **[PRISM CloudCLI](https://github.com/jakeefr/cloudcli-plugin-prism)** | 在 CloudCLI 中提供 Claude Code 会话智能分析,包括 token 消耗可视化 |
| **[Sessions](https://github.com/strykereye2/cloudcli-plugin-session-manager)** | 查看、管理并终止活动的 Claude Code 会话 |
| **[Token Cost Calculator](https://github.com/NightmareAway/cloudcli-plugin-token-cost-calculator)** | 根据模型价格和 token 用量计算 API 成本,并支持模型价格预设 |
@@ -186,7 +186,7 @@ CloudCLI UI 与 CloudCLI Cloud 是对 Claude Code 的扩展,而非旁观 — M
- **覆盖全部会话** — CloudCLI UI 会自动扫描 `~/.claude` 文件夹中的每个会话。Remote Control 只暴露当前活动的会话。
- **设置统一** — 在 CloudCLI UI 中修改的 MCP、工具权限等设置会立即写入 Claude Code。
-- **支持更多 Agents** — Claude Code、Cursor CLI、Codex、。
+- **支持更多 Agents** — Claude Code、Cursor CLI、Codex。
- **完整 UI** — 除了聊天界面,还包括文件浏览器、Git 集成、MCP 管理和 Shell 终端。
- **CloudCLI Cloud 保持运行于云端** — 关闭本地设备也不会中断代理运行,无需监控终端。
@@ -195,7 +195,7 @@ CloudCLI UI 与 CloudCLI Cloud 是对 Claude Code 的扩展,而非旁观 — M
需要额外购买 AI 订阅吗?
-需要。CloudCLI 只提供环境。你仍需自行获取 Claude、Cursor、Codex 或 订阅。CloudCLI Cloud 从 €7/月起提供托管环境。
+需要。CloudCLI 只提供环境。你仍需自行获取 Claude、Cursor 或 Codex 订阅。CloudCLI Cloud 从 €7/月起提供托管环境。
diff --git a/README.zh-TW.md b/README.zh-TW.md
index bc7fd5ce..de375429 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -1,7 +1,7 @@
@@ -43,7 +43,7 @@
CLI 選擇
-在 Claude Code、、Cursor CLI 與 Codex 之間進行選擇
+在 Claude Code、Cursor CLI 與 Codex 之間進行選擇
@@ -60,7 +60,7 @@
- **工作階段管理** — 恢復對話、管理多個工作階段並追蹤歷史紀錄
- **外掛系統** — 透過自訂分頁、後端服務與整合來擴充 CloudCLI。[開始建構 →](https://github.com/cloudcli-ai/cloudcli-plugin-starter)
- **TaskMaster AI 整合** *(選用)* — 結合 AI 任務規劃、PRD 分析與工作流程自動化,實現進階專案管理
-- **模型相容性** — 支援 Claude、GPT、 模型家族(完整支援列表可透過 `GET /api/providers/:provider/models` 介面取得)
+- **模型相容性** — 支援 Claude、GPT 模型家族(完整支援列表可透過 `GET /api/providers/:provider/models` 介面取得)
## 快速開始
@@ -99,7 +99,7 @@ cloudcli
npx @cloudcli-ai/cloudcli@latest sandbox ~/my-project
```
-支援 Claude Code、Codex 和。詳情請參閱[沙箱文件](docker/)。
+支援 Claude Code 和 Codex。詳情請參閱[沙箱文件](docker/)。
---
@@ -115,7 +115,7 @@ CloudCLI UI 是 CloudCLI Cloud 的開源 UI 層。你可以在本機上自架它
| **機器需保持開機嗎** | 是 | 否 |
| **行動裝置存取** | 網路內任意瀏覽器 | 任意裝置(原生應用程式即將推出) |
| **可用工作階段** | 自動發現 `~/.claude` 中的所有工作階段 | 雲端環境內的工作階段 |
-| **支援的 Agents** | Claude Code、Cursor CLI、Codex、 | Claude Code、Cursor CLI、Codex、 |
+| **支援的 Agents** | Claude Code、Cursor CLI、Codex | Claude Code、Cursor CLI、Codex |
| **檔案瀏覽與 Git** | 內建於 UI | 內建於 UI |
| **MCP 設定** | UI 管理,與本機 `~/.claude` 設定同步 | UI 管理 |
| **IDE 存取** | 本機 IDE | 任何連線到雲端環境的 IDE |
@@ -160,7 +160,7 @@ CloudCLI 配備外掛系統,允許你新增帶有自訂前端 UI 和選用 Nod
| **[Project Stats](https://github.com/cloudcli-ai/cloudcli-plugin-starter)** | 展示目前專案的檔案數、程式碼行數、檔案類型分佈、最大檔案以及最近修改的檔案 |
| **[Web Terminal](https://github.com/cloudcli-ai/cloudcli-plugin-terminal)** | 支援多分頁的完整 xterm.js 終端機 |
| **[Claude Watch](https://github.com/satsuki19980613/cloudcli-claude-watch)** | 監控長時間執行的 Claude Code 工作階段是否卡住,並提供程序控制 |
-| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | 建立工作區範圍的排程提示詞,並透過 Codex、Claude Code 或 等本機 CLI 執行 |
+| **[CloudCLI Scheduler](https://github.com/grostim/cloudcli-cron)** | 建立工作區範圍的排程提示詞,並透過 Codex、Claude Code 等本機 CLI 執行 |
| **[PRISM CloudCLI](https://github.com/jakeefr/cloudcli-plugin-prism)** | 在 CloudCLI 中提供 Claude Code 工作階段智慧分析,包括 token 消耗可視化 |
| **[Sessions](https://github.com/strykereye2/cloudcli-plugin-session-manager)** | 檢視、管理並終止作用中的 Claude Code 工作階段 |
| **[Token Cost Calculator](https://github.com/NightmareAway/cloudcli-plugin-token-cost-calculator)** | 根據模型價格與 token 用量計算 API 成本,並支援模型價格預設 |
@@ -186,7 +186,7 @@ CloudCLI UI 與 CloudCLI Cloud 是對 Claude Code 的擴充,而非旁觀 — M
- **涵蓋全部工作階段** — CloudCLI UI 會自動掃描 `~/.claude` 資料夾中的每個工作階段。Remote Control 只暴露目前活動的工作階段。
- **設定統一** — 在 CloudCLI UI 中修改的 MCP、工具權限等設定會立即寫入 Claude Code。
-- **支援更多 Agents** — Claude Code、Cursor CLI、Codex、。
+- **支援更多 Agents** — Claude Code、Cursor CLI、Codex。
- **完整 UI** — 除了聊天介面,還包括檔案瀏覽器、Git 整合、MCP 管理和 Shell 終端機。
- **CloudCLI Cloud 持續運作於雲端** — 關閉本機裝置也不會中斷代理執行,無需監控終端機。
@@ -195,7 +195,7 @@ CloudCLI UI 與 CloudCLI Cloud 是對 Claude Code 的擴充,而非旁觀 — M
需要額外購買 AI 訂閱嗎?
-需要。CloudCLI 只提供環境。你仍需自行取得 Claude、Cursor、Codex 或 訂閱。CloudCLI Cloud 從 €7/月起提供託管環境。
+需要。CloudCLI 只提供環境。你仍需自行取得 Claude、Cursor 或 Codex 訂閱。CloudCLI Cloud 從 €7/月起提供託管環境。
diff --git a/server/modules/assets/assets.routes.ts b/server/modules/assets/assets.routes.ts
index befbf12e..f0417c92 100644
--- a/server/modules/assets/assets.routes.ts
+++ b/server/modules/assets/assets.routes.ts
@@ -79,7 +79,17 @@ router.get('/images/:filename', async (req, res) => {
return res.status(404).json({ error: 'Asset not found' });
}
- res.setHeader('Content-Type', mime.lookup(resolved) || 'application/octet-stream');
+ const contentType = mime.lookup(resolved) || 'application/octet-stream';
+ res.setHeader('Content-Type', contentType);
+ // Stored-XSS hardening: never let the browser sniff a different type, and
+ // force SVGs (which can carry scripts when rendered as a document) to
+ // download instead of rendering inline. The chat UI is unaffected — it
+ // fetches assets as blobs and shows them through
, where SVG scripts
+ // never execute.
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ if (contentType === 'image/svg+xml') {
+ res.setHeader('Content-Disposition', 'attachment');
+ }
const fileStream = fsSync.createReadStream(resolved);
fileStream.pipe(res);
fileStream.on('error', (error) => {
diff --git a/server/modules/websocket/services/chat-websocket.service.ts b/server/modules/websocket/services/chat-websocket.service.ts
index 4e676ec3..eb0f7ce1 100644
--- a/server/modules/websocket/services/chat-websocket.service.ts
+++ b/server/modules/websocket/services/chat-websocket.service.ts
@@ -1,8 +1,11 @@
+import path from 'node:path';
+
import type { WebSocket } from 'ws';
import { sessionsDb } from '@/modules/database/index.js';
import { chatRunRegistry } from '@/modules/websocket/services/chat-run-registry.service.js';
import { connectedClients, WS_OPEN_STATE } from '@/modules/websocket/services/websocket-state.service.js';
+import { getGlobalImageAssetsDir, normalizeImageDescriptors } from '@/shared/image-attachments.js';
import type {
AnyRecord,
AuthenticatedWebSocketRequest,
@@ -10,6 +13,37 @@ import type {
} from '@/shared/types.js';
import { parseIncomingJsonObject } from '@/shared/utils.js';
+/**
+ * Trust boundary for client-supplied image attachments: chat.send options come
+ * straight from the browser, and the provider runtimes read the referenced
+ * files off disk (Claude base64-encodes them into the prompt). Only images
+ * that live directly inside the global upload store (`~/.cloudcli/assets`,
+ * where POST /api/assets/images puts them) are allowed through — anything
+ * else (absolute paths elsewhere, traversal, subdirectories) is dropped.
+ *
+ * Exported for tests; `assetsRootOverride` exists only for them.
+ */
+export function filterImagesToUploadStore(images: unknown, assetsRootOverride?: string): AnyRecord[] {
+ const assetsRoot = path.resolve(assetsRootOverride ?? getGlobalImageAssetsDir());
+
+ return normalizeImageDescriptors(images).filter((descriptor) => {
+ // Relative paths are anchored in the store; absolute ones must already be in it.
+ const resolved = path.resolve(assetsRoot, descriptor.path);
+ const relative = path.relative(assetsRoot, resolved);
+ const isDirectChild =
+ relative.length > 0 &&
+ !relative.startsWith('..') &&
+ !path.isAbsolute(relative) &&
+ !relative.includes(path.sep) &&
+ !relative.includes('/');
+
+ if (!isDirectChild) {
+ console.warn(`[Chat] Dropping image outside the upload store: ${descriptor.path}`);
+ }
+ return isDirectChild;
+ });
+}
+
/**
* One provider runtime entry point. All five runtimes share this signature,
* which lets the chat handler dispatch through a provider-keyed map instead
@@ -161,6 +195,9 @@ async function handleChatSend(
// gateway writer captures and maps back to the app session id.
const runtimeOptions: AnyRecord = {
...clientOptions,
+ // Image attachments are re-validated server-side: only files inside the
+ // global upload store may reach the provider runtimes' file reads.
+ images: filterImagesToUploadStore(clientOptions.images),
sessionId: session.provider_session_id ?? undefined,
resume: Boolean(session.provider_session_id),
cwd: clientOptions.cwd ?? session.project_path ?? undefined,
diff --git a/server/modules/websocket/tests/chat-image-filter.test.ts b/server/modules/websocket/tests/chat-image-filter.test.ts
new file mode 100644
index 00000000..9b1212c4
--- /dev/null
+++ b/server/modules/websocket/tests/chat-image-filter.test.ts
@@ -0,0 +1,44 @@
+import assert from 'node:assert/strict';
+import os from 'node:os';
+import path from 'node:path';
+import test from 'node:test';
+
+import { filterImagesToUploadStore } from '@/modules/websocket/services/chat-websocket.service.js';
+
+const STORE = path.join(os.tmpdir(), 'cloudcli-assets-store');
+
+test('images inside the upload store pass through', () => {
+ const inside = path.join(STORE, 'shot.png');
+ const result = filterImagesToUploadStore(
+ [{ path: inside, name: 'shot.png', mimeType: 'image/png' }],
+ STORE,
+ );
+ assert.equal(result.length, 1);
+ assert.equal(result[0].path, inside);
+});
+
+test('bare filenames are anchored inside the store', () => {
+ const result = filterImagesToUploadStore(['shot.png'], STORE);
+ assert.equal(result.length, 1);
+});
+
+test('paths outside the store, traversal, and subdirs are dropped', () => {
+ const result = filterImagesToUploadStore(
+ [
+ { path: 'C:/Users/victim/.ssh/id_rsa' },
+ { path: '/etc/passwd' },
+ { path: '../outside.png' },
+ { path: path.join(STORE, '..', 'escaped.png') },
+ { path: path.join(STORE, 'nested', 'deep.png') },
+ { path: STORE }, // the store folder itself is not a file
+ ],
+ STORE,
+ );
+ assert.deepEqual(result, []);
+});
+
+test('malformed payloads yield no images', () => {
+ assert.deepEqual(filterImagesToUploadStore(undefined, STORE), []);
+ assert.deepEqual(filterImagesToUploadStore('nope', STORE), []);
+ assert.deepEqual(filterImagesToUploadStore([{ name: 'no-path' }, 42], STORE), []);
+});
diff --git a/src/components/chat/view/subcomponents/MessageComponent.tsx b/src/components/chat/view/subcomponents/MessageComponent.tsx
index 5d116f25..61cf8ee3 100644
--- a/src/components/chat/view/subcomponents/MessageComponent.tsx
+++ b/src/components/chat/view/subcomponents/MessageComponent.tsx
@@ -94,7 +94,7 @@ const MessageComponent = memo(({ message, prevMessage, createDiff, onFileOpen, s
projectId={selectedProject?.projectId}
/>
)}
- {(userCopyContent.trim().length > 0 || !message.images?.length) && (
+ {userCopyContent.trim().length > 0 || !message.images?.length ? (
{message.content}
@@ -106,6 +106,11 @@ const MessageComponent = memo(({ message, prevMessage, createDiff, onFileOpen, s
{formattedTime}
+ ) : (
+ /* Image-only turn: no text bubble, but the timestamp still shows */
+
+ {formattedTime}
+
)}
{!isGrouped && (
diff --git a/src/components/git-panel/view/changes/ChangesView.tsx b/src/components/git-panel/view/changes/ChangesView.tsx
index d9ebb715..d898e791 100644
--- a/src/components/git-panel/view/changes/ChangesView.tsx
+++ b/src/components/git-panel/view/changes/ChangesView.tsx
@@ -1,5 +1,5 @@
import { GitBranch, GitCommit, RefreshCw } from 'lucide-react';
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { ConfirmationRequest, FileStatusCode, GitDiffMap, GitStatusResponse } from '../../types/types';
import { getAllChangedFiles, hasChangedFiles } from '../../utils/gitPanelUtils';
import CommitComposer from './CommitComposer';
@@ -49,20 +49,40 @@ export default function ChangesView({
}: ChangesViewProps) {
const [expandedFiles, setExpandedFiles] = useState>(new Set());
const [selectedFiles, setSelectedFiles] = useState>(new Set());
+ // Stage/unstage calls in flight or queued. While > 0, status refreshes must
+ // not overwrite the optimistic selection with a snapshot that predates the
+ // later clicks.
+ const [pendingStageOps, setPendingStageOps] = useState(0);
+ // Serializes stage/unstage requests so rapid toggles cannot interleave on
+ // the server or resolve out of order.
+ const stageOpQueueRef = useRef>(Promise.resolve());
const changedFiles = useMemo(() => getAllChangedFiles(gitStatus), [gitStatus]);
const hasExpandedFiles = expandedFiles.size > 0;
+ const enqueueStageOp = useCallback((operation: () => Promise) => {
+ setPendingStageOps((count) => count + 1);
+ stageOpQueueRef.current = stageOpQueueRef.current
+ .catch(() => {}) // a failed op must not block the queue
+ .then(operation)
+ .finally(() => setPendingStageOps((count) => count - 1));
+ }, []);
+
useEffect(() => {
if (!gitStatus || gitStatus.error) {
setSelectedFiles(new Set());
return;
}
+ if (pendingStageOps > 0) {
+ return; // keep the optimistic state until the queued ops settle
+ }
+
// The Staged section mirrors the real git index reported by /status, so
- // files staged outside the app (VSCode, terminal) show up here too.
+ // files staged outside the app (VSCode, terminal) show up here too. Also
+ // re-runs when the queue drains, syncing to the final refreshed status.
setSelectedFiles(new Set(gitStatus.staged ?? []));
- }, [gitStatus]);
+ }, [gitStatus, pendingStageOps]);
useEffect(() => {
onExpandedFilesChange(hasExpandedFiles);
@@ -87,8 +107,8 @@ export default function ChangesView({
}, []);
// Staging is real: every toggle runs git add / git reset through the API.
- // The set is flipped optimistically; the status refresh triggered by the
- // API call re-syncs it from the actual index afterwards.
+ // The set is flipped optimistically; the queued API call keeps the git
+ // index in sync and the final status refresh re-syncs once the queue drains.
const toggleFileSelected = useCallback(
(filePath: string) => {
const isStaged = selectedFiles.has(filePath);
@@ -101,9 +121,9 @@ export default function ChangesView({
}
return next;
});
- void (isStaged ? onUnstageFiles([filePath]) : onStageFiles([filePath]));
+ enqueueStageOp(() => (isStaged ? onUnstageFiles([filePath]) : onStageFiles([filePath])));
},
- [onStageFiles, onUnstageFiles, selectedFiles],
+ [enqueueStageOp, onStageFiles, onUnstageFiles, selectedFiles],
);
const requestFileAction = useCallback(
@@ -209,7 +229,7 @@ export default function ChangesView({
onClick={() => {
const filesToUnstage = Array.from(selectedFiles);
setSelectedFiles(new Set());
- void onUnstageFiles(filesToUnstage);
+ enqueueStageOp(() => onUnstageFiles(filesToUnstage));
}}
className="text-xs text-primary transition-colors hover:text-primary/80"
>
@@ -246,7 +266,7 @@ export default function ChangesView({
onClick={() => {
const filesToStage = Array.from(unstagedFiles);
setSelectedFiles(new Set(changedFiles));
- void onStageFiles(filesToStage);
+ enqueueStageOp(() => onStageFiles(filesToStage));
}}
className="text-xs text-primary transition-colors hover:text-primary/80"
>
diff --git a/src/components/git-panel/view/history/CommitHistoryItem.tsx b/src/components/git-panel/view/history/CommitHistoryItem.tsx
index f07449a7..1cb5ba19 100644
--- a/src/components/git-panel/view/history/CommitHistoryItem.tsx
+++ b/src/components/git-panel/view/history/CommitHistoryItem.tsx
@@ -62,7 +62,9 @@ export default function CommitHistoryItem({
return parseCommitFiles(diff);
}, [diff]);
- const badgeColor = graphRow ? laneColor(graphRow.nodeLane) : 'var(--color-primary, #0ea5e9)';
+ // Must stay a literal hex value: RefBadge derives its HEAD tint by
+ // appending an alpha byte (`${color}22`), which breaks for var() strings.
+ const badgeColor = graphRow ? laneColor(graphRow.nodeLane) : '#0ea5e9';
return (
diff --git a/src/hooks/useProjectsState.ts b/src/hooks/useProjectsState.ts
index 4dc4aaa2..051fd6d1 100644
--- a/src/hooks/useProjectsState.ts
+++ b/src/hooks/useProjectsState.ts
@@ -810,19 +810,18 @@ export function useProjectsState({
return;
}
- const projectForSession = selectedProject || projects.find((p) => (p.sessions?.length ?? 0) > 0);
- if (!projectForSession) {
+ // Only the currently selected project may host the placeholder. Guessing
+ // another project (e.g. "first one with sessions") could bind the URL
+ // session to the wrong project — better to wait until the owning project
+ // arrives in a later `projects` payload and is matched by the loop above.
+ if (!selectedProject) {
return;
}
- if (selectedProject?.projectId !== projectForSession.projectId) {
- setSelectedProject(projectForSession);
- }
-
setSelectedSession({
id: sessionId,
__provider: readSelectedProvider(),
- __projectId: projectForSession.projectId,
+ __projectId: selectedProject.projectId,
summary: '',
});
}, [sessionId, projects, selectedProject, selectedSession?.id, selectedSession?.__provider]);
diff --git a/src/i18n/locales/de/sidebar.json b/src/i18n/locales/de/sidebar.json
index d45c372f..b5469b13 100644
--- a/src/i18n/locales/de/sidebar.json
+++ b/src/i18n/locales/de/sidebar.json
@@ -49,7 +49,8 @@
"save": "Speichern",
"cancel": "Abbrechen",
"clearSearch": "Suche leeren",
- "openCommandPalette": "Befehlspalette öffnen"
+ "openCommandPalette": "Befehlspalette öffnen",
+ "attentionRequiredIndicator": "Sitzung erfordert Aufmerksamkeit"
},
"navigation": {
"chat": "Chat",
diff --git a/src/i18n/locales/en/sidebar.json b/src/i18n/locales/en/sidebar.json
index 05783430..a1392745 100644
--- a/src/i18n/locales/en/sidebar.json
+++ b/src/i18n/locales/en/sidebar.json
@@ -49,7 +49,8 @@
"save": "Save",
"cancel": "Cancel",
"clearSearch": "Clear search",
- "openCommandPalette": "Open command palette"
+ "openCommandPalette": "Open command palette",
+ "attentionRequiredIndicator": "Session needs attention"
},
"navigation": {
"chat": "Chat",
diff --git a/src/i18n/locales/fr/sidebar.json b/src/i18n/locales/fr/sidebar.json
index 682ca437..155d1ff3 100644
--- a/src/i18n/locales/fr/sidebar.json
+++ b/src/i18n/locales/fr/sidebar.json
@@ -49,7 +49,8 @@
"save": "Enregistrer",
"cancel": "Annuler",
"clearSearch": "Effacer la recherche",
- "openCommandPalette": "Ouvrir la palette de commandes"
+ "openCommandPalette": "Ouvrir la palette de commandes",
+ "attentionRequiredIndicator": "La session nécessite votre attention"
},
"navigation": {
"chat": "Chat",
diff --git a/src/i18n/locales/it/sidebar.json b/src/i18n/locales/it/sidebar.json
index f019b216..17f5a9f4 100644
--- a/src/i18n/locales/it/sidebar.json
+++ b/src/i18n/locales/it/sidebar.json
@@ -49,7 +49,8 @@
"save": "Salva",
"cancel": "Annulla",
"clearSearch": "Cancella ricerca",
- "openCommandPalette": "Apri tavolozza comandi"
+ "openCommandPalette": "Apri tavolozza comandi",
+ "attentionRequiredIndicator": "La sessione richiede attenzione"
},
"navigation": {
"chat": "Chat",
diff --git a/src/i18n/locales/ja/sidebar.json b/src/i18n/locales/ja/sidebar.json
index e3d782ac..576ff7b5 100644
--- a/src/i18n/locales/ja/sidebar.json
+++ b/src/i18n/locales/ja/sidebar.json
@@ -48,7 +48,8 @@
"activeSessionIndicator": "最近アクティブなセッション(過去10分以内)",
"save": "保存",
"cancel": "キャンセル",
- "openCommandPalette": "コマンドパレットを開く"
+ "openCommandPalette": "コマンドパレットを開く",
+ "attentionRequiredIndicator": "セッションが対応を必要としています"
},
"navigation": {
"chat": "チャット",
diff --git a/src/i18n/locales/ko/sidebar.json b/src/i18n/locales/ko/sidebar.json
index 6eaef5b2..32635a0b 100644
--- a/src/i18n/locales/ko/sidebar.json
+++ b/src/i18n/locales/ko/sidebar.json
@@ -48,7 +48,8 @@
"activeSessionIndicator": "최근 활성 세션 (지난 10분)",
"save": "저장",
"cancel": "취소",
- "openCommandPalette": "명령 팔레트 열기"
+ "openCommandPalette": "명령 팔레트 열기",
+ "attentionRequiredIndicator": "세션에 주의가 필요합니다"
},
"navigation": {
"chat": "채팅",
@@ -128,4 +129,4 @@
"allConversationsDeleted": "프로젝트가 사이드바에서 제거됩니다. 파일, 메모리 및 세션 데이터는 보존됩니다.",
"cannotUndo": "나중에 프로젝트를 다시 추가할 수 있습니다."
}
-}
\ No newline at end of file
+}
diff --git a/src/i18n/locales/ru/sidebar.json b/src/i18n/locales/ru/sidebar.json
index 8a85cf59..1fa5135f 100644
--- a/src/i18n/locales/ru/sidebar.json
+++ b/src/i18n/locales/ru/sidebar.json
@@ -49,7 +49,8 @@
"save": "Сохранить",
"cancel": "Отмена",
"clearSearch": "Очистить поиск",
- "openCommandPalette": "Открыть палитру команд"
+ "openCommandPalette": "Открыть палитру команд",
+ "attentionRequiredIndicator": "Сессия требует внимания"
},
"navigation": {
"chat": "Чат",
diff --git a/src/i18n/locales/tr/sidebar.json b/src/i18n/locales/tr/sidebar.json
index 2f6047ef..8932e15e 100644
--- a/src/i18n/locales/tr/sidebar.json
+++ b/src/i18n/locales/tr/sidebar.json
@@ -49,7 +49,8 @@
"save": "Kaydet",
"cancel": "İptal",
"clearSearch": "Aramayı temizle",
- "openCommandPalette": "Komut paletini aç"
+ "openCommandPalette": "Komut paletini aç",
+ "attentionRequiredIndicator": "Oturum ilgi bekliyor"
},
"navigation": {
"chat": "Sohbet",
diff --git a/src/i18n/locales/zh-CN/sidebar.json b/src/i18n/locales/zh-CN/sidebar.json
index 812bbc15..e5f4e267 100644
--- a/src/i18n/locales/zh-CN/sidebar.json
+++ b/src/i18n/locales/zh-CN/sidebar.json
@@ -49,7 +49,8 @@
"save": "保存",
"cancel": "取消",
"clearSearch": "清除搜索",
- "openCommandPalette": "打开命令面板"
+ "openCommandPalette": "打开命令面板",
+ "attentionRequiredIndicator": "会话需要处理"
},
"navigation": {
"chat": "聊天",
diff --git a/src/i18n/locales/zh-TW/sidebar.json b/src/i18n/locales/zh-TW/sidebar.json
index dab8ad9d..c2491997 100644
--- a/src/i18n/locales/zh-TW/sidebar.json
+++ b/src/i18n/locales/zh-TW/sidebar.json
@@ -48,7 +48,8 @@
"save": "儲存",
"cancel": "取消",
"clearSearch": "清除搜尋",
- "openCommandPalette": "開啟指令面板"
+ "openCommandPalette": "開啟指令面板",
+ "attentionRequiredIndicator": "工作階段需要處理"
},
"navigation": {
"chat": "聊天",