feat(agents): add Cursor CLI support and remove Antigravity and Cursor IDE integrations

This commit is contained in:
lzt404 committed 2026-09-21 21:58:01 +08:00
1 parent 84c29782b0
commit 8901eff9bb
24 files changed
+530 -634

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
<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>

After

Width:  |  Height:  |  Size: 587 B

+2 -2
View File
@@ -937,9 +937,9 @@ export const jaOverrides = {
'agents.description.openclaw': 'CPA モデルプロバイダーと既定モデルを使用',
'agents.description.hermes': 'CPA カスタムプロバイダーを使用',
'agents.description.deepseekHarness': 'Harness のネイティブ DeepSeek 設定と分離された OpenAI 互換ルートで CPA を使用',
'agents.description.antigravityIde': 'CPA の Gemini 互換 API で Antigravity IDE を設定',
'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.antigravityIdeHint': '設定の適用・解除後は IDE を再起動してください。リクエストにはここで選択した CPA モデルが使用され、IDE 内のモデル表示名は変わりません。初回は IDE へのログインが必要です。',
'agents.modify.antigravityCliHint': '設定適用後はこのページの起動ボタンを使用してください。CPA が CLI プロセスに接続先とキーを渡します。agy を直接実行する場合は環境変数の設定が必要です。',
'agents.description.workbuddy': 'CPA の OpenAI Chat Completions API で WorkBuddy のカスタムモデルを設定',
'agents.modify.workbuddyHint': '適用後、WorkBuddy で「CPA:モデル名」を選択してください。表示されない場合は App を再起動してください。既存の会話のモデルは自動で切り替わりません。',
+2 -2
View File
@@ -937,9 +937,9 @@ 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.antigravityIde': 'Configure Antigravity IDE with CPA’s Gemini-compatible API',
'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.antigravityIdeHint': 'Restart the IDE after applying or disabling configuration. Requests use the CPA model selected here; model labels inside the IDE stay unchanged. Initial IDE sign-in is still required.',
'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',
'agents.modify.workbuddyHint': 'After applying, select “CPA:model name” in WorkBuddy. Restart the app if it is missing; existing conversations keep their selected model.',
+2 -2
View File
@@ -936,9 +936,9 @@ export const zhCN = {
'agents.description.openclaw': '使用 CPA 模型供应商和默认模型',
'agents.description.hermes': '使用 CPA 自定义提供方',
'agents.description.deepseekHarness': '通过通用 OpenAI 兼容路由使用 CPA,并与 Harness 原生 DeepSeek 配置隔离',
'agents.description.antigravityIde': '使用 CPA 的 Gemini 兼容接口配置 Antigravity IDE',
'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.antigravityIdeHint': '应用或关闭配置后请重启 IDE。模型请求使用此处选择的 CPA 模型;IDE 内的模型标签保持原样。首次使用仍需完成 IDE 登录。',
'agents.modify.antigravityCliHint': '应用后请使用此页的启动按钮,CPA 会为本次 CLI 进程注入地址和密钥。直接运行 agy 需要自行设置环境变量。',
'agents.description.workbuddy': '使用 CPA 的 OpenAI Chat Completions 接口配置 WorkBuddy 自定义模型',
'agents.modify.workbuddyHint': '应用后,在 WorkBuddy 的模型选择器中选择「CPA:模型名」。若未显示,请重启 App;已有会话的模型不会自动切换。',
+76 -29
View File
@@ -38,6 +38,7 @@ 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,
@@ -82,8 +83,8 @@ type AgentClientId =
| 'deepseek-harness'
| 'zcode'
| 'workbuddy'
| 'antigravity-ide'
| 'antigravity-cli'
| 'cursor-cli'
| 'kimi-code'
| 'grok-build'
| 'pi';
@@ -233,7 +234,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.antigravityIde' | 'agents.description.antigravityCli' | '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.cursorCli' | 'agents.description.kimiCode' | 'agents.description.grokBuild' | 'agents.description.pi';
};
type AgentSubpageId = 'core' | 'management' | 'sessions';
@@ -287,8 +288,8 @@ const agentDefinitions: AgentDefinition[] = [
icon: grokIcon,
descriptionKey: 'agents.description.grokBuild',
},
{ id: 'antigravity-ide', name: 'Antigravity IDE', icon: antigravityIcon, descriptionKey: 'agents.description.antigravityIde' },
{ 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',
@@ -329,6 +330,21 @@ const agentSubpages: AgentSubpageDefinition[] = [
{
id: 'management',
labelKey: 'agents.tabs.management',
clients: [
'claude-code',
'claude-desktop',
'codex',
'opencode',
'openclaw',
'hermes',
'deepseek-harness',
'zcode',
'workbuddy',
'antigravity-cli',
'kimi-code',
'grok-build',
'pi',
],
},
{
id: 'sessions',
@@ -796,8 +812,11 @@ 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 activeSubpage = viewState.subpage === 'sessions' && (embedded || selected !== 'codex')
? DEFAULT_AGENT_SUBPAGE : viewState.subpage;
const isCursorClient = selected === 'cursor-cli';
const activeSubpage = isCursorClient
? DEFAULT_AGENT_SUBPAGE
: viewState.subpage === 'sessions' && (embedded || selected !== 'codex')
? DEFAULT_AGENT_SUBPAGE : viewState.subpage;
const updateViewState = (patch: Partial<AgentViewState>) => {
setViewStateByClient((current) => {
const next = {
@@ -919,6 +938,12 @@ 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);
@@ -1223,7 +1248,6 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
const modificationDescription = activeStatus?.modificationState === 'invalid'
? t('agents.modify.invalid')
: nativeOauth ? t('agents.nativeOAuth.activeHint')
: selected === 'antigravity-ide' ? t('agents.modify.antigravityIdeHint')
: selected === 'antigravity-cli' ? t('agents.modify.antigravityCliHint')
: selected === 'workbuddy'
? t('agents.modify.workbuddyHint')
@@ -1748,7 +1772,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
};
const restartDesktopApp = async () => {
if (!['codex', 'opencode', 'claude-desktop', 'zcode', 'workbuddy', 'antigravity-ide'].includes(selected)) return;
if (!['codex', 'opencode', 'claude-desktop', 'zcode', 'workbuddy'].includes(selected)) return;
setBusyAction('restart-app');
setLaunchError('');
try {
@@ -2030,7 +2054,8 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
</aside>
<section className="panel agent-config-panel">
<div className="agent-subpage-tabs" role="tablist" aria-label={t('agents.tabs.label')}>
{availableSubpages.length > 1 ? (
<div className="agent-subpage-tabs" role="tablist" aria-label={t('agents.tabs.label')}>
{availableSubpages.map((subpage) => (
<button
type="button"
@@ -2058,6 +2083,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
</button>
))}
</div>
) : null}
{embedded && activeSubpage === 'core' ? (
<div className="agent-minimal-config" id="agent-subpage-panel-core" role="tabpanel" aria-labelledby="agent-subpage-tab-core">
<div className="agent-minimal-client-summary">
@@ -2073,7 +2099,13 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
<MessageNotice message={activeStatus?.error || activeStatus?.warnings.join(';')} tone={activeStatus?.error ? 'error' : 'info'} />
{selected === 'claude-desktop' ? desktopModelEditor : <div className="agent-minimal-field">
{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">
<label htmlFor="embedded-agent-model">{t(isDeepSeekHarnessClient ? 'agents.harness.defaultModel' : 'agents.useModel')}</label>
<AgentModelPicker
models={isClaudeModelMappingClient ? claudeMappingModels : models}
@@ -2089,23 +2121,25 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
{isDeepSeekHarnessClient ? <p className="agent-model-hint">{t('agents.harness.defaultHint')}</p> : null}
<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>
{!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>
</div>
) : null}
</div>
) : null}
@@ -2158,7 +2192,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
</>
) : (
<div>
<span>{t('agents.clientVersion')}</span>
<span>{selected === 'cursor-cli' ? t('agents.cliVersion') : t('agents.clientVersion')}</span>
<strong title={activeStatus?.version ?? undefined}>{activeStatus?.version ?? t('agents.notFetched')}</strong>
</div>
)}
@@ -2166,7 +2200,18 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
<MessageNotice message={activeStatus?.error || activeStatus?.warnings.join(';')} tone={activeStatus?.error ? 'error' : 'info'} />
{!isClaudeModelMappingClient ? (
{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 ? (
<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>
@@ -2396,9 +2441,10 @@ 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' : ''}`}>
<div className={`agent-save-actions${!isPiClient ? " agent-codex-save-actions" : ""}`}>
{closeConfigurationButton}
<button type="button" className="primary-button" onClick={applySelectedConfiguration}
disabled={busy || !canEnable || configurationWriteBlocked}
@@ -2408,6 +2454,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
</button>
</div>
</div>
) : null}
</div>
) : null}
@@ -2443,7 +2490,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
<MessageNotice tone="success" message={!configurationErrorMessage ? configurationNotice || clearNotice : null}
onDismiss={() => { setConfigurationNotice(''); setClearNotice(''); }} />
{activeSubpage === 'core' ? <AgentRunControls name={activeDefinition.name} dualTargets={hasIndependentCliAndApp}
desktop={hasIndependentCliAndApp || selected === 'claude-desktop' || selected === 'zcode' || selected === 'workbuddy' || selected === 'antigravity-ide'}
desktop={hasIndependentCliAndApp || selected === 'claude-desktop' || selected === 'zcode' || selected === 'workbuddy'}
targets={activeLaunchTargets} enabled={launchEnabled} busyAction={busyAction}
harness={isDeepSeekHarnessClient ? deepSeekHarnessProcessStatus : null}
onLaunch={(target) => void launchAgent(target)} onRestart={() => void restartDesktopApp()}