refactor(agents): remove Cursor CLI support
This commit is contained in:
1 parent
c3ea47a895
commit
0bb6eab4cb
16 files changed
+24
-321
No files matched your search
@@ -101,7 +101,6 @@ OpenAI、Claude、Gemini、およびその他の互換形式の間で変換で
|
||||
- ZCode
|
||||
- WorkBuddy / WorkBuddy AI
|
||||
- Antigravity CLI
|
||||
- Cursor CLI
|
||||
- Kimi Code
|
||||
- Grok Build
|
||||
|
||||
|
||||
@@ -119,7 +119,6 @@ proxy. Supported clients include:
|
||||
- ZCode
|
||||
- WorkBuddy / WorkBuddy AI
|
||||
- Antigravity CLI
|
||||
- Cursor CLI
|
||||
- Kimi Code
|
||||
- Grok Build
|
||||
|
||||
@@ -129,8 +128,6 @@ previous configuration.
|
||||
|
||||
Antigravity CLI connects through CPA's Gemini-compatible API. Launch the CLI from CPA to supply its endpoint and API key for that process. Running `agy` directly requires setting those environment variables yourself.
|
||||
|
||||
Cursor CLI has a separate entry. It supports local installation detection and launch with terminal and working directory selection, and uses Cursor’s native configuration and account system rather than local CPA model routing.
|
||||
|
||||
## Additional Capabilities
|
||||
|
||||
- Manage core settings, API keys, remote management credentials, and routing strategy.
|
||||
|
||||
@@ -110,7 +110,6 @@ API 接入页面按照协议或 Provider 管理上游 API 凭证和服务地址
|
||||
- ZCode
|
||||
- WorkBuddy / WorkBuddy AI
|
||||
- Antigravity CLI
|
||||
- Cursor CLI
|
||||
- Kimi Code
|
||||
- Grok Build
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ mod templates;
|
||||
mod transactions;
|
||||
mod workbuddy;
|
||||
mod antigravity;
|
||||
mod cursor;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows_probe;
|
||||
pub(crate) use backups::*;
|
||||
@@ -27,6 +26,5 @@ pub(crate) use templates::*;
|
||||
pub(crate) use transactions::*;
|
||||
pub(crate) use workbuddy::*;
|
||||
pub(crate) use antigravity::*;
|
||||
pub(crate) use cursor::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) use windows_probe::*;
|
||||
@@ -9,7 +9,6 @@ static CODEX_CATALOG_REFRESH: tokio::sync::Notify = tokio::sync::Notify::const_n
|
||||
enum AgentStatusDetectionTarget {
|
||||
Client(AgentClient),
|
||||
PiProvider,
|
||||
Cursor(CursorClient),
|
||||
}
|
||||
|
||||
pub(crate) fn inspect_agent_config_statuses(
|
||||
@@ -32,7 +31,6 @@ pub(crate) fn inspect_agent_config_statuses(
|
||||
AgentStatusDetectionTarget::Client(AgentClient::ZCode),
|
||||
AgentStatusDetectionTarget::Client(AgentClient::WorkBuddy),
|
||||
AgentStatusDetectionTarget::Client(AgentClient::AntigravityCli),
|
||||
AgentStatusDetectionTarget::Cursor(CursorClient::Cli),
|
||||
AgentStatusDetectionTarget::Client(AgentClient::KimiCode),
|
||||
AgentStatusDetectionTarget::Client(AgentClient::GrokBuild),
|
||||
AgentStatusDetectionTarget::PiProvider,
|
||||
@@ -61,9 +59,6 @@ pub(crate) fn inspect_agent_config_statuses(
|
||||
AgentStatusDetectionTarget::PiProvider => {
|
||||
inspect_pi_provider_status(home, config.port, api_key)
|
||||
}
|
||||
AgentStatusDetectionTarget::Cursor(client) => {
|
||||
inspect_cursor_client(client, home)
|
||||
}
|
||||
};
|
||||
results
|
||||
.lock()
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum CursorClient {
|
||||
Cli,
|
||||
}
|
||||
|
||||
impl CursorClient {
|
||||
pub(crate) fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"cursor-cli" => Some(Self::Cli),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn id(self) -> &'static str {
|
||||
"cursor-cli"
|
||||
}
|
||||
|
||||
pub(crate) fn name(self) -> &'static str {
|
||||
"Cursor CLI"
|
||||
}
|
||||
|
||||
pub(crate) fn target(self) -> &'static str {
|
||||
"cli"
|
||||
}
|
||||
|
||||
pub(crate) fn executable(self, home: &Path) -> Option<PathBuf> {
|
||||
find_cursor_cli(home)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_cursor_cli(home: &Path) -> Option<PathBuf> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let directory = agent_configuration_environment("LOCALAPPDATA")
|
||||
.unwrap_or_else(|| home.join("AppData/Local"))
|
||||
.join("cursor-agent");
|
||||
for name in ["cursor-agent.exe", "cursor-agent.cmd", "agent.exe", "agent.cmd"] {
|
||||
let candidate = directory.join(name);
|
||||
if candidate.is_file() {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
find_named_agent_executable(home, &["cursor-agent"])
|
||||
}
|
||||
|
||||
pub(crate) fn inspect_cursor_client(client: CursorClient, home: &Path) -> AgentConfigStatus {
|
||||
let executable = client.executable(home);
|
||||
let version = executable.as_deref().and_then(|path| read_agent_version(path, home));
|
||||
cursor_status(client, executable.as_deref(), version)
|
||||
}
|
||||
|
||||
fn cursor_status(client: CursorClient, executable: Option<&Path>, version: Option<String>) -> AgentConfigStatus {
|
||||
AgentConfigStatus {
|
||||
id: client.id().into(),
|
||||
name: client.name().into(),
|
||||
supported_platform: true,
|
||||
installed: executable.is_some(),
|
||||
plugin_installed: true,
|
||||
launch_targets: executable.map(|path| AgentLaunchTarget {
|
||||
id: client.target().into(),
|
||||
label: client.name().into(),
|
||||
detail: path_to_string(path),
|
||||
}).into_iter().collect(),
|
||||
cli_version: version.clone(),
|
||||
app_version: None,
|
||||
version,
|
||||
plugin_version: None,
|
||||
config_paths: Vec::new(),
|
||||
config_exists: false,
|
||||
config_valid: true,
|
||||
configured: false,
|
||||
configuration_synchronized: false,
|
||||
connection_state: "unsupported".into(),
|
||||
current_model: None,
|
||||
oauth_configuration: false,
|
||||
codex_native_oauth: false,
|
||||
modification_enabled: false,
|
||||
modification_state: "unconfigured".into(),
|
||||
backup_available: false,
|
||||
applied_model: None,
|
||||
claude_code_model_mappings: None,
|
||||
claude_desktop_model_mappings: None,
|
||||
warnings: Vec::new(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cursor_cli_has_a_native_launch_target_and_no_cpa_state() {
|
||||
assert_eq!(CursorClient::parse("cursor-cli"), Some(CursorClient::Cli));
|
||||
assert_eq!(CursorClient::parse("cursor-ide"), None);
|
||||
let status = cursor_status(CursorClient::Cli, Some(Path::new("/installed/client")), Some("1.2.3".into()));
|
||||
assert_eq!(status.id, "cursor-cli");
|
||||
assert!(status.installed);
|
||||
assert_eq!(status.launch_targets.len(), 1);
|
||||
assert_eq!(status.launch_targets[0].id, "cli");
|
||||
assert_eq!(status.connection_state, "unsupported");
|
||||
assert!(!status.configured);
|
||||
assert!(!status.modification_enabled);
|
||||
assert!(!status.configuration_synchronized);
|
||||
assert!(status.config_paths.is_empty());
|
||||
assert_eq!(status.cli_version.as_deref(), Some("1.2.3"));
|
||||
assert!(status.app_version.is_none());
|
||||
let missing = cursor_status(CursorClient::Cli, None, None);
|
||||
assert!(!missing.installed);
|
||||
assert!(missing.launch_targets.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursor_clients_cannot_enter_managed_configuration_operations() {
|
||||
for id in ["cursor-ide", "cursor-cli", " CURSOR-CLI "] {
|
||||
assert!(AgentClient::parse(id).unwrap_err().contains("CPA"));
|
||||
}
|
||||
assert_eq!(CursorClient::parse("codex"), None);
|
||||
}
|
||||
}
|
||||
@@ -165,31 +165,6 @@ pub(crate) fn launch_agent(
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(cursor_client) = CursorClient::parse(&client) {
|
||||
if requested_target.is_some_and(|target| target != cursor_client.target()) {
|
||||
return Err(format!(
|
||||
"{} 不支持 {} 启动方式",
|
||||
cursor_client.name(),
|
||||
requested_target.unwrap_or_default()
|
||||
));
|
||||
}
|
||||
let status = inspect_cursor_client(cursor_client, &home);
|
||||
if !status.installed {
|
||||
return Err(format!("未检测到 {},请先安装并重新检测", cursor_client.name()));
|
||||
}
|
||||
let executable = find_cursor_cli(&home)
|
||||
.ok_or_else(|| "未找到 Cursor CLI 可执行文件".to_string())?;
|
||||
let launch_directory = resolve_launch_directory(working_directory.as_deref(), &home)?;
|
||||
return launch_cli_agent(
|
||||
&executable,
|
||||
cursor_client.name(),
|
||||
&launch_directory,
|
||||
&[],
|
||||
&[],
|
||||
&terminal,
|
||||
);
|
||||
}
|
||||
|
||||
let client = AgentClient::parse(&client)?;
|
||||
if !client.supported_platform() {
|
||||
return Err(format!("当前平台不支持启动 {}", client.name()));
|
||||
@@ -727,9 +702,6 @@ pub(crate) async fn restart_opencode_app(app: tauri::AppHandle) -> Result<(), St
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn restart_agent_app(app: tauri::AppHandle, client: String) -> Result<(), String> {
|
||||
if client.trim().eq_ignore_ascii_case("cursor-cli") {
|
||||
return Err("Cursor CLI 不支持桌面应用重启".to_string());
|
||||
}
|
||||
let client = AgentClient::parse(&client)?;
|
||||
if !matches!(
|
||||
client,
|
||||
|
||||
@@ -1390,9 +1390,6 @@ impl AgentClient {
|
||||
"antigravity-cli" => Ok(Self::AntigravityCli),
|
||||
"kimi-code" => Ok(Self::KimiCode),
|
||||
"grok-build" => Ok(Self::GrokBuild),
|
||||
"cursor-ide" | "cursor-cli" => {
|
||||
Err(format!("Cursor 客户端暂不支持 CPA 托管配置: {value}"))
|
||||
}
|
||||
_ => Err(format!("不支持的智能体客户端: {value}")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.8416 1.387C23.4132 1.0062 24.1735 1.44515 24.1295 2.13055L22.6326 25.4656C22.5957 26.0405 21.8646 26.2609 21.5162 25.8022L15.0114 17.2395C14.8429 17.0177 14.5923 16.873 14.316 16.838L3.6481 15.486C3.07656 15.4136 2.9019 14.6703 3.38136 14.3509L22.8416 1.387Z" fill="#1E1E1E"/>
|
||||
<path d="M1.79102 15.2782L24.2386 0.457031L14.6808 17.0116L1.79102 15.2782Z" fill="#000000"/>
|
||||
<path d="M22.6278 27.3078L24.2394 0.457031L14.6816 17.0116L22.6278 27.3078Z" fill="#777777"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 587 B |
@@ -937,8 +937,6 @@ export const jaOverrides = {
|
||||
'agents.description.openclaw': 'CPA モデルプロバイダーと既定モデルを使用',
|
||||
'agents.description.hermes': 'CPA カスタムプロバイダーを使用',
|
||||
'agents.description.deepseekHarness': 'Harness のネイティブ DeepSeek 設定と分離された OpenAI 互換ルートで CPA を使用',
|
||||
'agents.description.cursorCli': 'Cursor CLI (cursor-agent) の検出とネイティブ起動に対応',
|
||||
'agents.modify.cursorCliHint': 'Cursor CLI は cursor-agent 経由で起動され、~/.cursor/cli-config.json で設定されます。CPA ではローカルインストールの検出と、任意のディレクトリやターミナル選択からの起動をサポートしています。',
|
||||
'agents.description.antigravityCli': 'CPA の Gemini 互換 API で Antigravity CLI を設定',
|
||||
'agents.modify.antigravityCliHint': '設定適用後はこのページの起動ボタンを使用してください。CPA が CLI プロセスに接続先とキーを渡します。agy を直接実行する場合は環境変数の設定が必要です。',
|
||||
'agents.description.workbuddy': 'CPA の OpenAI Chat Completions API で WorkBuddy のカスタムモデルを設定',
|
||||
|
||||
@@ -937,8 +937,6 @@ export const en: Record<MessageKey, string> = {
|
||||
'agents.description.openclaw': 'Uses the CPA model provider and default model',
|
||||
'agents.description.hermes': 'Uses the CPA custom provider',
|
||||
'agents.description.deepseekHarness': 'Uses CPA through an isolated, OpenAI-compatible Harness route',
|
||||
'agents.description.cursorCli': 'Supports detection and native launch for Cursor CLI (cursor-agent)',
|
||||
'agents.modify.cursorCliHint': 'Cursor CLI runs via cursor-agent and is configured in ~/.cursor/cli-config.json. CPA supports local installation detection and launch with custom terminal and working directory.',
|
||||
'agents.description.antigravityCli': 'Configure Antigravity CLI with CPA’s Gemini-compatible API',
|
||||
'agents.modify.antigravityCliHint': 'Use Launch on this page after applying configuration. CPA supplies the endpoint and key to that CLI process. Running agy directly requires setting the environment variables yourself.',
|
||||
'agents.description.workbuddy': 'Configures a WorkBuddy custom model using the CPA OpenAI Chat Completions API',
|
||||
|
||||
@@ -936,8 +936,6 @@ export const zhCN = {
|
||||
'agents.description.openclaw': '使用 CPA 模型供应商和默认模型',
|
||||
'agents.description.hermes': '使用 CPA 自定义提供方',
|
||||
'agents.description.deepseekHarness': '通过通用 OpenAI 兼容路由使用 CPA,并与 Harness 原生 DeepSeek 配置隔离',
|
||||
'agents.description.cursorCli': '支持 Cursor CLI (cursor-agent) 的检测与原生启动',
|
||||
'agents.modify.cursorCliHint': 'Cursor CLI 通过 cursor-agent 运行,使用 ~/.cursor/cli-config.json 配置。CPA 支持本地安装检测,并支持自定义终端与工作目录启动。',
|
||||
'agents.description.antigravityCli': '使用 CPA 的 Gemini 兼容接口配置 Antigravity CLI',
|
||||
'agents.modify.antigravityCliHint': '应用后请使用此页的启动按钮,CPA 会为本次 CLI 进程注入地址和密钥。直接运行 agy 需要自行设置环境变量。',
|
||||
'agents.description.workbuddy': '使用 CPA 的 OpenAI Chat Completions 接口配置 WorkBuddy 自定义模型',
|
||||
|
||||
+22
-55
@@ -38,7 +38,6 @@ import piIcon from '../assets/icons/pi-logo-on-light.svg';
|
||||
import zcodeIcon from '../assets/icons/zcode.png';
|
||||
import workbuddyIcon from '../assets/icons/workbuddy.png';
|
||||
import antigravityIcon from '../assets/icons/antigravity.svg';
|
||||
import cursorIcon from '../assets/icons/cursor.svg';
|
||||
import {
|
||||
agentModelAlias,
|
||||
filterAgentModels,
|
||||
@@ -84,7 +83,6 @@ type AgentClientId =
|
||||
| 'zcode'
|
||||
| 'workbuddy'
|
||||
| 'antigravity-cli'
|
||||
| 'cursor-cli'
|
||||
| 'kimi-code'
|
||||
| 'grok-build'
|
||||
| 'pi';
|
||||
@@ -234,7 +232,7 @@ type AgentDefinition = {
|
||||
name: string;
|
||||
icon?: string;
|
||||
Icon?: ComponentType<{ size?: number; 'aria-hidden'?: boolean }>;
|
||||
descriptionKey: 'agents.description.claudeCode' | 'agents.description.claudeDesktop' | 'agents.description.codex' | 'agents.description.opencode' | 'agents.description.openclaw' | 'agents.description.hermes' | 'agents.description.deepseekHarness' | 'agents.description.zcode' | 'agents.description.workbuddy' | 'agents.description.antigravityCli' | 'agents.description.cursorCli' | 'agents.description.kimiCode' | 'agents.description.grokBuild' | 'agents.description.pi';
|
||||
descriptionKey: 'agents.description.claudeCode' | 'agents.description.claudeDesktop' | 'agents.description.codex' | 'agents.description.opencode' | 'agents.description.openclaw' | 'agents.description.hermes' | 'agents.description.deepseekHarness' | 'agents.description.zcode' | 'agents.description.workbuddy' | 'agents.description.antigravityCli' | 'agents.description.kimiCode' | 'agents.description.grokBuild' | 'agents.description.pi';
|
||||
};
|
||||
|
||||
type AgentSubpageId = 'core' | 'management' | 'sessions';
|
||||
@@ -289,7 +287,6 @@ const agentDefinitions: AgentDefinition[] = [
|
||||
descriptionKey: 'agents.description.grokBuild',
|
||||
},
|
||||
{ id: 'antigravity-cli', name: 'Antigravity CLI', icon: antigravityIcon, descriptionKey: 'agents.description.antigravityCli' },
|
||||
{ id: 'cursor-cli', name: 'Cursor CLI', icon: cursorIcon, descriptionKey: 'agents.description.cursorCli' },
|
||||
{
|
||||
id: 'workbuddy',
|
||||
name: 'WorkBuddy',
|
||||
@@ -812,11 +809,8 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
const [viewStateByClient, setViewStateByClient] = useState(() => agentViewStateCache);
|
||||
const viewMode = embedded ? 'embedded' : 'full';
|
||||
const viewState = viewStateByClient[viewMode][selected] ?? DEFAULT_AGENT_VIEW_STATE;
|
||||
const isCursorClient = selected === 'cursor-cli';
|
||||
const activeSubpage = isCursorClient
|
||||
? DEFAULT_AGENT_SUBPAGE
|
||||
: viewState.subpage === 'sessions' && (embedded || selected !== 'codex')
|
||||
? DEFAULT_AGENT_SUBPAGE : viewState.subpage;
|
||||
const activeSubpage = viewState.subpage === 'sessions' && (embedded || selected !== 'codex')
|
||||
? DEFAULT_AGENT_SUBPAGE : viewState.subpage;
|
||||
const updateViewState = (patch: Partial<AgentViewState>) => {
|
||||
setViewStateByClient((current) => {
|
||||
const next = {
|
||||
@@ -938,12 +932,6 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
}, []);
|
||||
|
||||
const loadModels = useCallback(async (client: AgentClientId, preferredModel = '') => {
|
||||
if (client === 'cursor-cli') {
|
||||
setModelLoading(false);
|
||||
setModelError('');
|
||||
setModels([]);
|
||||
return;
|
||||
}
|
||||
const requestId = modelRequestRef.current + 1;
|
||||
modelRequestRef.current = requestId;
|
||||
setModelLoading(true);
|
||||
@@ -2096,13 +2084,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
|
||||
<MessageNotice message={activeStatus?.error || activeStatus?.warnings.join(';')} tone={activeStatus?.error ? 'error' : 'info'} />
|
||||
|
||||
{isCursorClient ? (
|
||||
<div className="agent-unmanaged-notice">
|
||||
<p className="agent-model-hint">
|
||||
{t('agents.modify.cursorCliHint')}
|
||||
</p>
|
||||
</div>
|
||||
) : selected === 'claude-desktop' ? desktopModelEditor : <div className="agent-minimal-field">
|
||||
{selected === 'claude-desktop' ? desktopModelEditor : <div className="agent-minimal-field">
|
||||
<label htmlFor="embedded-agent-model">{t(isDeepSeekHarnessClient ? 'agents.harness.defaultModel' : 'agents.useModel')}</label>
|
||||
<AgentModelPicker
|
||||
models={isClaudeModelMappingClient ? claudeMappingModels : models}
|
||||
@@ -2118,25 +2100,23 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
|
||||
{isDeepSeekHarnessClient ? <p className="agent-model-hint">{t('agents.harness.defaultHint')}</p> : null}
|
||||
|
||||
{!isCursorClient ? (
|
||||
<div className="agent-save-bar">
|
||||
{configurationFeedback}
|
||||
<div className={`agent-save-actions${!isPiClient ? " agent-codex-save-actions" : ""}`}>
|
||||
{closeConfigurationButton}
|
||||
<button
|
||||
type="button"
|
||||
className="primary-button"
|
||||
onClick={applySelectedConfiguration}
|
||||
disabled={busy || !canEnable || configurationWriteBlocked}
|
||||
>
|
||||
{['apply', 'install-pi', 'repair-pi'].includes(busyAction ?? '') ? <LoaderCircle size={16} className="spin" /> : null}
|
||||
{isPiClient
|
||||
? activeStatus?.pluginInstalled ? configurationActionLabel : t('agents.pi.install')
|
||||
: configurationActionLabel}
|
||||
</button>
|
||||
</div>
|
||||
<div className="agent-save-bar">
|
||||
{configurationFeedback}
|
||||
<div className={`agent-save-actions${!isPiClient ? " agent-codex-save-actions" : ""}`}>
|
||||
{closeConfigurationButton}
|
||||
<button
|
||||
type="button"
|
||||
className="primary-button"
|
||||
onClick={applySelectedConfiguration}
|
||||
disabled={busy || !canEnable || configurationWriteBlocked}
|
||||
>
|
||||
{['apply', 'install-pi', 'repair-pi'].includes(busyAction ?? '') ? <LoaderCircle size={16} className="spin" /> : null}
|
||||
{isPiClient
|
||||
? activeStatus?.pluginInstalled ? configurationActionLabel : t('agents.pi.install')
|
||||
: configurationActionLabel}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
) : null}
|
||||
@@ -2189,7 +2169,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<span>{selected === 'cursor-cli' ? t('agents.cliVersion') : t('agents.clientVersion')}</span>
|
||||
<span>{t('agents.clientVersion')}</span>
|
||||
<strong title={activeStatus?.version ?? undefined}>{activeStatus?.version ?? t('agents.notFetched')}</strong>
|
||||
</div>
|
||||
)}
|
||||
@@ -2197,18 +2177,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
|
||||
<MessageNotice message={activeStatus?.error || activeStatus?.warnings.join(';')} tone={activeStatus?.error ? 'error' : 'info'} />
|
||||
|
||||
{isCursorClient ? (
|
||||
<section className="agent-core-setting-section">
|
||||
<div className="agent-section-heading">
|
||||
<div><strong>{t('agents.description.cursorCli')}</strong></div>
|
||||
</div>
|
||||
<p className="agent-model-hint">
|
||||
{t('agents.modify.cursorCliHint')}
|
||||
</p>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{!isCursorClient && !isClaudeModelMappingClient ? (
|
||||
{!isClaudeModelMappingClient ? (
|
||||
<section className="agent-core-setting-section agent-model-section">
|
||||
<div className="agent-section-heading">
|
||||
<div><strong>{t(isDeepSeekHarnessClient ? 'agents.harness.defaultModel' : 'agents.useModel')}</strong></div>
|
||||
@@ -2438,7 +2407,6 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{!isCursorClient ? (
|
||||
<div className="agent-save-bar">
|
||||
{configurationFeedback}
|
||||
<div className={`agent-save-actions${!isPiClient ? " agent-codex-save-actions" : ""}`}>
|
||||
@@ -2451,7 +2419,6 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -3,7 +3,7 @@ const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const base = 'http://127.0.0.1:1421';
|
||||
const clients = ['codex', 'claude-desktop', 'deepseek-harness', 'zcode', 'cursor-cli'];
|
||||
const clients = ['codex', 'claude-desktop', 'deepseek-harness', 'zcode'];
|
||||
const desktopViewports = [
|
||||
{ width: 1280, height: 700 },
|
||||
{ width: 1280, height: 941 },
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ channel: 'msedge', headless: true });
|
||||
const screenshots = path.join(os.tmpdir(), 'cpa-cursor-ui-review');
|
||||
fs.mkdirSync(screenshots, { recursive: true });
|
||||
try {
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 900 } });
|
||||
const errors = [];
|
||||
page.on('pageerror', error => errors.push(String(error)));
|
||||
|
||||
const open = async (query = '') => {
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem('cpa-gui.agent-launch-directory-history.v1', JSON.stringify({
|
||||
'cursor-cli': ['C:\\test\\workspace']
|
||||
}));
|
||||
});
|
||||
await page.goto('http://localhost:1421/tests/fixtures/agent-backups.html?' + query);
|
||||
await page.waitForFunction(() => {
|
||||
const refresh = document.querySelector('.agent-header-actions button');
|
||||
return refresh && !refresh.disabled
|
||||
&& window.fixtureCalls.some(call => call.cmd === 'get_agent_config_statuses');
|
||||
});
|
||||
};
|
||||
|
||||
const button = name => page.getByRole('button', { name, exact: true });
|
||||
const calls = cmd => page.evaluate(cmd => window.fixtureCalls.filter(call => call.cmd === cmd), cmd);
|
||||
|
||||
const BTN_MANUAL_BACKUP = '\u624b\u52a8\u5907\u4efd';
|
||||
const BTN_RESTORE_BACKUP = '\u6062\u590d\u5907\u4efd';
|
||||
const BTN_APPLY_TEMPLATE = '\u5e94\u7528 ezcpa \u6a21\u677f';
|
||||
const BTN_UPDATE_CONFIG = '\u66f4\u65b0\u914d\u7f6e';
|
||||
const BTN_CLEAR_CONFIG = '\u6e05\u7a7a\u914d\u7f6e';
|
||||
const BTN_LAUNCH_CLI = '\u542f\u52a8 CLI';
|
||||
|
||||
for (const mode of ['', 'embedded&']) {
|
||||
await open(mode + 'client=cursor-cli');
|
||||
|
||||
assert.equal(await page.locator('.agent-list-items').getByText('Cursor IDE').count(), 0);
|
||||
assert.ok(await page.locator('.agent-list-items').getByText('Cursor CLI').isVisible());
|
||||
|
||||
assert.equal(await page.locator('.agent-model-trigger').count(), 0);
|
||||
assert.equal(await page.locator('.agent-model-search').count(), 0);
|
||||
assert.equal(await button(BTN_MANUAL_BACKUP).count(), 0);
|
||||
assert.equal(await button(BTN_RESTORE_BACKUP).count(), 0);
|
||||
assert.equal(await button(BTN_APPLY_TEMPLATE).count(), 0);
|
||||
assert.equal(await button(BTN_UPDATE_CONFIG).count(), 0);
|
||||
assert.equal(await button(BTN_CLEAR_CONFIG).count(), 0);
|
||||
|
||||
const cliModelCalls = (await calls('get_agent_models')).filter(c => c.args && c.args.client === 'cursor-cli');
|
||||
assert.equal(cliModelCalls.length, 0, 'Cursor CLI must not fetch CPA models');
|
||||
|
||||
assert.ok(await button(BTN_LAUNCH_CLI).isVisible());
|
||||
assert.equal(await page.getByRole('button', { name: '\u91cd\u542f App', exact: true }).count(), 0);
|
||||
|
||||
await button(BTN_LAUNCH_CLI).click();
|
||||
const dialog = page.locator('.agent-launch-directory-dialog');
|
||||
await dialog.waitFor();
|
||||
assert.ok(await dialog.isVisible());
|
||||
|
||||
const dialogLaunchBtn = dialog.locator('button.primary-button');
|
||||
await dialogLaunchBtn.click();
|
||||
await dialog.waitFor({ state: 'hidden' });
|
||||
|
||||
await page.waitForFunction(() => {
|
||||
return window.fixtureCalls.some(call => call.cmd === 'launch_agent' && call.args && call.args.client === 'cursor-cli');
|
||||
});
|
||||
const cliLaunchCalls = (await calls('launch_agent')).filter(c => c.args && c.args.client === 'cursor-cli');
|
||||
assert.ok(cliLaunchCalls.length > 0);
|
||||
assert.equal(cliLaunchCalls[cliLaunchCalls.length - 1].args.target, 'cli');
|
||||
|
||||
await page.screenshot({ path: path.join(screenshots, 'cursor-' + (mode ? 'embedded' : 'full') + '.png'), fullPage: true });
|
||||
}
|
||||
|
||||
assert.equal(errors.length, 0, 'Page errors: ' + errors.join(', '));
|
||||
console.log('Cursor agents UI test passed successfully in both full and embedded modes!');
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
})().catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Vendored
+1
-1
@@ -9,7 +9,7 @@ if(params.has('reset-selections'))localStorage.removeItem('cpa-gui.agent-model-s
|
||||
localStorage.setItem('easy-cli-proxy-api.locale',params.get('locale') || 'zh-CN');
|
||||
document.documentElement.dataset.theme = params.get('theme') || 'light';
|
||||
localStorage.setItem('cpa-gui.agent-selected-client.v1', params.get('client') || 'codex');
|
||||
const ids = ['claude-code','claude-desktop','codex','opencode','openclaw','hermes','deepseek-harness','antigravity-cli','cursor-cli','workbuddy','zcode','kimi-code','grok-build','pi'];
|
||||
const ids = ['claude-code','claude-desktop','codex','opencode','openclaw','hermes','deepseek-harness','antigravity-cli','workbuddy','zcode','kimi-code','grok-build','pi'];
|
||||
let count=0; let backupCount=0; const backups:any[]=[]; let currentModel=params.has('fresh')?null:'gpt-one';
|
||||
let nativeOauth=params.has('native-oauth'); let nativeSwitchCount=0; let codexClosed=false; let closeCount=0;
|
||||
let currentOauth=false; const currentMappings:Record<string,any>={};
|
||||
|
||||
Reference in new issue
Block a user