refactor(agents): remove Cursor CLI support

This commit is contained in:
lzt404 committed 2026-09-23 02:47:28 +08:00
1 parent c3ea47a895
commit 0bb6eab4cb
16 files changed
+24 -321

No files matched your search

-1
View File
@@ -101,7 +101,6 @@ OpenAI、Claude、Gemini、およびその他の互換形式の間で変換で
- ZCode
- WorkBuddy / WorkBuddy AI
- Antigravity CLI
- Cursor CLI
- Kimi Code
- Grok Build
-3
View File
@@ -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.
-1
View File
@@ -110,7 +110,6 @@ API 接入页面按照协议或 Provider 管理上游 API 凭证和服务地址
- ZCode
- WorkBuddy / WorkBuddy AI
- Antigravity CLI
- Cursor CLI
- Kimi Code
- Grok Build
-2
View File
@@ -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::*;
-5
View File
@@ -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()
-123
View File
@@ -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);
}
}
-28
View File
@@ -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,
-3
View File
@@ -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}")),
}
}
-5
View File
@@ -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

-2
View File
@@ -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 のカスタムモデルを設定',
-2
View File
@@ -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',
-2
View File
@@ -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
View File
@@ -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}
+1 -1
View File
@@ -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 },
-87
View File
@@ -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);
});
+1 -1
View File
@@ -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>={};