Decouple bundled UI from runtime API and add remote instance tooling (#1228)

Add a packaged-client runtime boundary so the shared UI can talk to local,
desktop, remote, and VS Code runtimes through the right transport instead of
assuming one same-origin web server.

Centralize OpenChamber-owned API access behind RuntimeAPIs, runtimeFetch, and
runtime URL helpers, while keeping official OpenCode traffic on the SDK path.
Support runtime switching, remote host selection, desktop client credentials,
and headless connection links for pairing packaged clients with remote
OpenChamber servers.

Harden the new auth model by moving long-lived client tokens out of browser
URLs, introducing short-lived scoped URL tokens for browser-owned transports,
restricting URL-token access to explicit readable/realtime routes, and making
client-token management session-scoped or self-scoped as appropriate.

Update browser-owned assets and preview proxy flows to work with the split
runtime model, including authenticated project icons, preview token propagation,
CSP-safe preview bridge injection, and preview proxy auth that survives
short-lived URL-token expiry.

Tighten Electron security boundaries for packaged clients by gating privileged
preload state to trusted origins and requiring explicit confirmation before
connect deep-links import or switch remote runtimes.

Also refresh agent guidance and project skills so future runtime/API, auth,
preview, UI, CLI, settings, locale, and drag-to-reorder work follows the new
architecture.
This commit is contained in:
Bohdan Triapitsyn
2026-06-02 00:43:05 +03:00
committed by GitHub
parent a4314c189b
commit 2031e3b4a8
282 changed files with 16524 additions and 4259 deletions
+101
View File
@@ -27,6 +27,94 @@ export const dict: Record<I18nKey, string> = {
'layout.mainTab.files': '文件',
'layout.mainTab.terminal': '终端',
'layout.mainTab.context': '上下文',
'mobile.nav.aria': '移动导航',
'mobile.nav.changes': '更改',
'mobile.nav.settings': '设置',
'mobile.surface.closeAria': '关闭',
'mobile.header.openMenuAria': '打开菜单',
'mobile.menu.titleAria': '工作区工具',
'mobile.menu.files': '文件',
'mobile.menu.changes': '更改',
'mobile.menu.settings': '设置',
'mobile.sessions.newChatCta': '在 {project} 中新建会话',
'mobile.sessions.dateGroup.today': '今天',
'mobile.sessions.dateGroup.yesterday': '昨天',
'mobile.sessions.dateGroup.thisWeek': '本周早些时候',
'mobile.sessions.dateGroup.older': '更早',
'mobile.sessions.section.worktrees': '工作树',
'mobile.sessions.section.otherProjects': '切换项目',
'mobile.sessions.section.projects': '项目',
'mobile.sessions.empty.noProjectsTitle': '暂无项目',
'mobile.sessions.empty.noProjectsDescription': '添加项目以开始与代码对话。',
'mobile.sessions.empty.noSessionsTitle': '暂无会话',
'mobile.sessions.empty.noSessionsDescription': '开始第一次会话即可在此显示。',
'mobile.sessions.empty.searchTitle': '无结果',
'mobile.sessions.empty.searchDescription': '请尝试其他搜索词。',
'mobile.sessions.showArchived': '显示已归档 ({count})',
'mobile.sessions.hideArchived': '隐藏已归档',
'mobile.sessions.activeWorktreeAria': '活动工作树',
'mobile.sessions.activeProjectAria': '活动项目',
'mobile.sessions.startNewChat': '开始新会话',
'mobile.sessions.newChat': '新会话',
'mobile.sessions.editOrder': '重新排序项目',
'mobile.sessions.doneEditing': '完成',
'mobile.sessions.editOrderHint': '拖动手柄或使用箭头重新排序。点击对勾以完成。',
'mobile.sessions.dragHandleAria': '拖动 {label} 以重新排序',
'mobile.sessions.moveUpAria': '将 {label} 上移',
'mobile.sessions.moveDownAria': '将 {label} 下移',
'mobile.sessions.removeProjectAria': '移除 {label}',
'mobile.sessions.cancelRemoveProjectAria': '取消移除 {label}',
'mobile.sessions.confirmRemoveProject': '删除',
'mobile.sessions.confirmRemoveProjectAria': '确认移除 {label}',
'mobile.sessions.toast.projectRemoved': '已移除 {label}',
'mobile.sessions.showMore': '再显示 {count} 个',
'mobile.sessions.search.section.sessions': '会话',
'mobile.sessions.search.section.archived': '已归档',
'mobile.sessions.search.section.projects': '项目',
'mobile.sessions.clearSearchAria': '清除搜索',
'mobile.header.noProject': '选择项目',
'mobile.header.activeSession': '活动会话',
'mobile.header.noSession': '无活动会话',
'mobile.sessions.openSheetAria': '打开会话和项目',
'mobile.sessions.closeSheetAria': '关闭会话和项目',
'mobile.sessions.sheet.title': '会话',
'mobile.sessions.sheet.description': '切换项目、打开会话或开始新聊天。',
'mobile.sessions.search.placeholder': '搜索会话',
'mobile.sessions.empty': '未找到会话。',
'mobile.sessions.unassignedProject': '其他会话',
'mobile.sessions.newSessionAria': '在此项目中开始新会话',
'mobile.sessions.untitled': '未命名会话',
'mobile.sessions.project.sessionsSingle': '1 个会话',
'mobile.sessions.project.sessionsPlural': '{count} 个会话',
'mobile.files.refreshAria': '刷新文件',
'mobile.files.backToParentAria': '返回 {name}',
'mobile.files.rootDirectory': '项目文件',
'mobile.files.search.placeholder': '搜索文件',
'mobile.files.search.empty': '未找到文件。',
'mobile.files.parentDirectory': '上级目录',
'mobile.files.empty.noDirectory': '选择一个项目以浏览文件。',
'mobile.files.empty.directory': '此目录为空。',
'mobile.files.error.listFailed': '加载文件失败',
'mobile.files.error.readUnavailable': '此运行环境不支持文件预览。',
'mobile.files.file.truncated': '移动端文件预览已截断。',
'mobile.files.copyPathAria': '复制文件路径',
'mobile.files.copyContent': '复制内容',
'mobile.files.copyContentAria': '复制文件内容',
'mobile.files.toast.pathCopied': '路径已复制',
'mobile.files.toast.contentCopied': '内容已复制',
'mobile.files.toast.copyFailed': '复制失败',
'mobile.changes.placeholder.title': '更改',
'mobile.changes.placeholder.description': '工作区更改 review、sync 和 commit 操作将在这里显示。',
'mobile.changes.branchLabel': '分支:{branch}',
'mobile.changes.noRemote': '没有可用的 remote',
'mobile.changes.cleanDescription': '此 workspace 中没有已更改文件。',
'mobile.changes.diffDetail.subtitle': '只读 diff',
'mobile.changes.diffDetail.loadFailed': '加载 diff 失败',
'mobile.changes.diffDetail.missingTitle': '文件不再有更改',
'mobile.changes.diffDetail.missingDescription': '返回“更改”并刷新列表。',
'mobile.changes.diffDetail.imageUnavailable': '移动“更改”暂不支持图片 diff。',
'mobile.settings.placeholder.title': '设置',
'mobile.settings.placeholder.description': '移动连接和应用设置将在这里显示。',
'layout.rightSidebar.git': 'Git',
'layout.rightSidebar.files': '文件',
'layout.rightSidebar.context': '上下文',
@@ -1125,6 +1213,12 @@ export const dict: Record<I18nKey, string> = {
'header.services.refreshRateLimitsAria': '刷新速率限制',
'header.services.noRateLimits': '没有可用的速率限制。',
'header.services.noRateLimitsReported': '未上报速率限制。',
'header.services.remoteUpdate.title': '远程实例更新',
'header.services.remoteUpdate.checking': '正在检查更新...',
'header.services.remoteUpdate.upToDate': '此实例已是最新。',
'header.services.remoteUpdate.available': '此实例可更新到版本 {version}。',
'header.services.remoteUpdate.error': '无法检查远程实例更新',
'header.services.remoteUpdate.actions.open': '更新',
'header.services.used': '已用',
'header.services.remaining': '剩余',
'header.services.shutdownDev': '停止 OpenChamber',
@@ -2035,6 +2129,9 @@ export const dict: Record<I18nKey, string> = {
'desktopHostSwitcher.header.currentDefaultColon': '当前默认:',
'desktopHostSwitcher.status.connected': '已连接',
'desktopHostSwitcher.status.authRequired': '需要认证',
'desktopHostSwitcher.status.checking': '检查中',
'desktopHostSwitcher.status.updateRecommended': '建议更新',
'desktopHostSwitcher.status.incompatible': '不兼容',
'desktopHostSwitcher.status.wrongService': '服务不匹配',
'desktopHostSwitcher.status.unreachable': '不可达',
'desktopHostSwitcher.status.unknown': '未知',
@@ -2221,6 +2318,8 @@ export const dict: Record<I18nKey, string> = {
'onboarding.remoteConnection.actions.chooseDifferentServer': '选择其他服务器',
'onboarding.remoteConnection.actions.useLocalInstead': '改用本地',
'onboarding.remoteConnection.probe.authMessage': '服务器需要身份验证。你仍可连接,但可能需要提供凭据。',
'onboarding.remoteConnection.probe.updateRecommendedMessage': '此实例正在运行不同版本的 OpenChamber。你可以连接;如果功能异常,请更新两端应用。',
'onboarding.remoteConnection.probe.incompatibleMessage': '服务器正在运行 OpenChamber,但与此应用版本不兼容。请更新服务器上的 OpenChamber 后重试。',
'onboarding.remoteConnection.probe.wrongServiceMessage': '服务器有响应,但未运行 OpenChamber。请确认地址是否指向 OpenChamber 服务器。',
'onboarding.remoteConnection.probe.unreachableMessage': '服务器不可达。请检查网络连接并确认服务器地址。',
'onboarding.desktopRecovery.localUnavailable.title': '本地 OpenCode 不可用',
@@ -2234,6 +2333,8 @@ export const dict: Record<I18nKey, string> = {
'onboarding.desktopRecovery.remoteUnreachable.retry': '重试连接',
'onboarding.desktopRecovery.incompatibleServer.title': '服务器不兼容',
'onboarding.desktopRecovery.incompatibleServer.description': '“{host}” 上的服务器未运行 OpenChamber。请确认地址是否指向 OpenChamber 服务器。',
'onboarding.desktopRecovery.remoteIncompatible.title': '需要更新服务器',
'onboarding.desktopRecovery.remoteIncompatible.description': '“{host}” 上的 OpenChamber 服务器与此应用版本不兼容。请更新服务器上的 OpenChamber 后重试。',
'onboarding.desktopRecovery.common.useLocal': '使用本地',
'onboarding.desktopRecovery.common.useRemote': '使用远程',
'onboarding.desktopRecovery.actions.retrying': '重试中…',