mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 18:15:23 +00:00
feat(cursor): split legacy bridge from cliproxy provider
This commit is contained in:
1 parent
1bad3b0305
commit
3b5941c60b
36 files changed
+974
-155
No files matched your search
@@ -26,6 +26,7 @@ import { getCcsDir } from '../utils/config-manager';
|
||||
import { getProfileLookupCandidates, isLegacyProfileAlias } from '../utils/profile-compat';
|
||||
import type { CLIProxyProvider } from '../cliproxy/types';
|
||||
import { CLIPROXY_PROVIDER_IDS, isCLIProxyProvider } from '../cliproxy/provider-capabilities';
|
||||
import { LEGACY_CURSOR_PROFILE_NAME } from '../cursor/constants';
|
||||
import { normalizeCopilotModelId } from '../copilot/copilot-model-normalizer';
|
||||
import type { TargetType } from '../targets/target-adapter';
|
||||
import type { ProfileType } from '../types/profile';
|
||||
@@ -296,20 +297,17 @@ class ProfileDetector {
|
||||
};
|
||||
}
|
||||
|
||||
// Priority 0.25: Check Cursor profile - local Cursor daemon runtime.
|
||||
// This keeps bare `ccs cursor` and `ccs cursor <subcommand>` bound to the
|
||||
// existing runtime/admin surface even though CLIProxy also exposes a
|
||||
// distinct provider named "cursor".
|
||||
if (profileName === 'cursor') {
|
||||
// Priority 0.25: Check explicit legacy Cursor bridge profile.
|
||||
if (profileName === LEGACY_CURSOR_PROFILE_NAME) {
|
||||
const cursorConfig = getCursorConfig();
|
||||
|
||||
if (!cursorConfig?.enabled) {
|
||||
const error = new Error(
|
||||
'Cursor profile is not enabled.\n\n' +
|
||||
'Legacy Cursor profile is not enabled.\n\n' +
|
||||
'To enable Cursor integration:\n' +
|
||||
' 1. Run: ccs cursor enable\n' +
|
||||
' 2. Import auth: ccs cursor auth\n' +
|
||||
' 3. Start daemon: ccs cursor start\n\n' +
|
||||
' 1. Run: ccs legacy cursor enable\n' +
|
||||
' 2. Import auth: ccs legacy cursor auth\n' +
|
||||
' 3. Start daemon: ccs legacy cursor start\n\n' +
|
||||
'Or manually edit ~/.ccs/config.yaml:\n' +
|
||||
' cursor:\n' +
|
||||
' enabled: true'
|
||||
@@ -322,7 +320,7 @@ class ProfileDetector {
|
||||
|
||||
return {
|
||||
type: 'cursor',
|
||||
name: 'cursor',
|
||||
name: LEGACY_CURSOR_PROFILE_NAME,
|
||||
cursorConfig,
|
||||
};
|
||||
}
|
||||
|
||||
+41
-17
@@ -65,7 +65,7 @@ import {
|
||||
resolveOfficialChannelsLaunchPlan,
|
||||
} from './channels/official-channels-runtime';
|
||||
import { getOfficialChannelReadiness } from './channels/official-channels-store';
|
||||
import { isCursorSubcommandToken, shouldUseCursorCliproxyShortcut } from './cursor/constants';
|
||||
import { isCursorSubcommandToken, LEGACY_CURSOR_PROFILE_NAME } from './cursor/constants';
|
||||
import { isCLIProxyProvider } from './cliproxy/provider-capabilities';
|
||||
|
||||
// Import centralized error handling
|
||||
@@ -149,6 +149,26 @@ function detectProfile(args: string[]): DetectedProfile {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeLegacyCursorArgs(args: string[]): string[] {
|
||||
if (args[0] === 'legacy' && args[1] === 'cursor') {
|
||||
return [LEGACY_CURSOR_PROFILE_NAME, ...args.slice(2)];
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function printCursorLegacySubcommandDeprecation(subcommand: string): void {
|
||||
console.error(
|
||||
info(`\`ccs cursor ${subcommand}\` is deprecated for the legacy Cursor IDE bridge.`)
|
||||
);
|
||||
console.error(
|
||||
info(
|
||||
`Use \`ccs legacy cursor ${subcommand}\` for the old bridge, or \`ccs cursor --auth|--accounts|--config\` for the CLIProxy provider.`
|
||||
)
|
||||
);
|
||||
console.error('');
|
||||
}
|
||||
|
||||
function resolveRuntimeReasoningFlags(
|
||||
args: string[],
|
||||
envThinkingValue: string | undefined
|
||||
@@ -343,7 +363,7 @@ async function main(): Promise<void> {
|
||||
registerTarget(new CodexAdapter());
|
||||
const cliLogger = createLogger('cli');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
let args = process.argv.slice(2);
|
||||
const isCompletionCommand = args[0] === '__complete';
|
||||
|
||||
// Initialize UI colors early to ensure consistent colored output
|
||||
@@ -419,6 +439,8 @@ async function main(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
args = normalizeLegacyCursorArgs(args);
|
||||
|
||||
cliLogger.info('command.start', 'CLI invocation started', {
|
||||
command: args[0] || 'default',
|
||||
argCount: args.length,
|
||||
@@ -489,7 +511,6 @@ async function main(): Promise<void> {
|
||||
if (
|
||||
typeof firstArg === 'string' &&
|
||||
isCLIProxyProvider(firstArg) &&
|
||||
firstArg !== 'cursor' &&
|
||||
args.length > 1 &&
|
||||
(args.includes('--help') || args.includes('-h'))
|
||||
) {
|
||||
@@ -511,9 +532,8 @@ async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: cursor command (Cursor local proxy integration)
|
||||
// Route known admin subcommands to the command handler, keep all other args as profile passthrough.
|
||||
if (firstArg === 'cursor' && args.length > 1) {
|
||||
// Special case: explicit legacy Cursor bridge namespace.
|
||||
if (firstArg === LEGACY_CURSOR_PROFILE_NAME && args.length > 1) {
|
||||
const { handleCursorCommand } = await import('./commands/cursor-command');
|
||||
const cursorToken = args[1];
|
||||
|
||||
@@ -523,6 +543,19 @@ async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility shim: old `ccs cursor <subcommand>` still forwards to the legacy bridge
|
||||
// for one migration window, but bare/positional `ccs cursor` now belongs to CLIProxy.
|
||||
if (firstArg === 'cursor' && args.length > 1) {
|
||||
const { handleCursorCommand } = await import('./commands/cursor-command');
|
||||
const cursorToken = args[1];
|
||||
|
||||
if (isCursorSubcommandToken(cursorToken) && cursorToken !== '--help' && cursorToken !== '-h') {
|
||||
printCursorLegacySubcommandDeprecation(cursorToken);
|
||||
const exitCode = await handleCursorCommand(args.slice(1));
|
||||
process.exit(exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
// First-time install: offer setup wizard for interactive users
|
||||
// Check independently of recovery status (user may have empty config.yaml)
|
||||
// Skip if headless, CI, or non-TTY environment
|
||||
@@ -551,17 +584,8 @@ async function main(): Promise<void> {
|
||||
try {
|
||||
// Detect profile (strip --target flags before profile detection)
|
||||
const cleanArgs = stripTargetFlag(args);
|
||||
const useCursorCliproxyShortcut = shouldUseCursorCliproxyShortcut(cleanArgs);
|
||||
const { profile, remainingArgs } = useCursorCliproxyShortcut
|
||||
? { profile: 'cursor', remainingArgs: cleanArgs.slice(1) }
|
||||
: detectProfile(cleanArgs);
|
||||
const profileInfo: ProfileDetectionResult = useCursorCliproxyShortcut
|
||||
? {
|
||||
type: 'cliproxy',
|
||||
name: 'cursor',
|
||||
provider: 'cursor',
|
||||
}
|
||||
: detector.detectProfileType(profile);
|
||||
const { profile, remainingArgs } = detectProfile(cleanArgs);
|
||||
const profileInfo: ProfileDetectionResult = detector.detectProfileType(profile);
|
||||
let resolvedTarget: ReturnType<typeof resolveTargetType>;
|
||||
try {
|
||||
resolvedTarget = resolveTargetType(
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
normalizeProtocol,
|
||||
CLIPROXY_DEFAULT_PORT,
|
||||
} from './port-manager';
|
||||
import { getProviderSettingsPath } from './path-resolver';
|
||||
import { migrateLegacyProviderSettingsIfNeeded } from './path-resolver';
|
||||
import {
|
||||
canonicalizeModelIdForProvider,
|
||||
MODEL_ENV_VAR_KEYS,
|
||||
@@ -465,7 +465,7 @@ export function getEffectiveEnvVars(
|
||||
}
|
||||
|
||||
// Priority 2: Default provider settings file
|
||||
const settingsPath = getProviderSettingsPath(provider);
|
||||
const settingsPath = migrateLegacyProviderSettingsIfNeeded(provider);
|
||||
|
||||
// Check for user override file
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
@@ -505,7 +505,7 @@ export function getEffectiveEnvVars(
|
||||
* Called during installation/first run
|
||||
*/
|
||||
export function ensureProviderSettings(provider: CLIProxyProvider): void {
|
||||
const settingsPath = getProviderSettingsPath(provider);
|
||||
const settingsPath = migrateLegacyProviderSettingsIfNeeded(provider);
|
||||
const defaultEnv = getClaudeEnvVars(provider);
|
||||
|
||||
const writeSettings = (settings: Record<string, unknown>): void => {
|
||||
@@ -663,7 +663,7 @@ export function getRemoteEnvVars(
|
||||
|
||||
// Priority 2: Default provider settings file (~/.ccs/{provider}.settings.json)
|
||||
if (Object.keys(userEnvVars).length === 0) {
|
||||
const settingsPath = getProviderSettingsPath(provider);
|
||||
const settingsPath = migrateLegacyProviderSettingsIfNeeded(provider);
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
try {
|
||||
const content = fs.readFileSync(settingsPath, 'utf-8');
|
||||
|
||||
@@ -16,6 +16,13 @@ export function getCliproxyDir(): string {
|
||||
return path.join(getCcsDir(), 'cliproxy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CLIProxy provider settings directory.
|
||||
*/
|
||||
export function getCliproxyProvidersDir(): string {
|
||||
return path.join(getCliproxyDir(), 'providers');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CLIProxy writable directory for logs and runtime files.
|
||||
* This directory is set as WRITABLE_PATH env var when spawning CLIProxy.
|
||||
@@ -75,5 +82,40 @@ export function getBinDir(): string {
|
||||
* Example: ~/.ccs/gemini.settings.json
|
||||
*/
|
||||
export function getProviderSettingsPath(provider: CLIProxyProvider): string {
|
||||
if (provider === 'cursor') {
|
||||
return path.join(getCliproxyProvidersDir(), `${provider}.settings.json`);
|
||||
}
|
||||
|
||||
return getLegacyProviderSettingsPath(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CLIProxy provider settings path in the dedicated cliproxy/providers namespace.
|
||||
* Used only for providers that must not collide with legacy top-level settings files.
|
||||
*/
|
||||
export function getDedicatedProviderSettingsPath(provider: CLIProxyProvider): string {
|
||||
return path.join(getCliproxyProvidersDir(), `${provider}.settings.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get legacy provider settings file path in ~/.ccs root.
|
||||
* This is kept for compatibility reads/migration of older provider settings.
|
||||
*/
|
||||
export function getLegacyProviderSettingsPath(provider: CLIProxyProvider): string {
|
||||
return path.join(getCcsDir(), `${provider}.settings.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the effective provider settings path.
|
||||
*
|
||||
* Cursor uses a dedicated cliproxy/providers namespace so it does not collide
|
||||
* with the deprecated Cursor IDE bridge raw settings file.
|
||||
*/
|
||||
export function migrateLegacyProviderSettingsIfNeeded(provider: CLIProxyProvider): string {
|
||||
if (provider !== 'cursor') {
|
||||
return getProviderSettingsPath(provider);
|
||||
}
|
||||
|
||||
const targetPath = getDedicatedProviderSettingsPath(provider);
|
||||
return targetPath;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { InteractivePrompt } from '../utils/prompt';
|
||||
import { getProviderCatalog, supportsModelConfig, ModelEntry } from './model-catalog';
|
||||
import { getProviderSettingsPath, getClaudeEnvVars } from './config-generator';
|
||||
import { getClaudeEnvVars, migrateLegacyProviderSettingsIfNeeded } from './config-generator';
|
||||
import { CLIProxyProvider } from './types';
|
||||
import { initUI, color, bold, dim, ok, info, header } from '../utils/ui';
|
||||
import { getCcsDir } from '../utils/config-manager';
|
||||
@@ -31,7 +31,7 @@ function canonicalizeModelForProvider(provider: CLIProxyProvider, model: string)
|
||||
* Check if provider has user settings configured
|
||||
*/
|
||||
export function hasUserSettings(provider: CLIProxyProvider): boolean {
|
||||
const settingsPath = getProviderSettingsPath(provider);
|
||||
const settingsPath = migrateLegacyProviderSettingsIfNeeded(provider);
|
||||
return fs.existsSync(settingsPath);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export function getCurrentModel(
|
||||
): string | undefined {
|
||||
const settingsPath = customSettingsPath
|
||||
? customSettingsPath.replace(/^~/, os.homedir())
|
||||
: getProviderSettingsPath(provider);
|
||||
: migrateLegacyProviderSettingsIfNeeded(provider);
|
||||
if (!fs.existsSync(settingsPath)) return undefined;
|
||||
|
||||
try {
|
||||
@@ -116,7 +116,7 @@ export async function configureProviderModel(
|
||||
// Use custom settings path for CLIProxy variants, otherwise use default provider path
|
||||
const settingsPath = customSettingsPath
|
||||
? customSettingsPath.replace(/^~/, os.homedir())
|
||||
: getProviderSettingsPath(provider);
|
||||
: migrateLegacyProviderSettingsIfNeeded(provider);
|
||||
|
||||
// Skip if already configured with a model (unless --config flag).
|
||||
// A settings file can exist without model env keys (e.g., hook-only writes).
|
||||
@@ -249,7 +249,7 @@ export async function showCurrentConfig(provider: CLIProxyProvider): Promise<voi
|
||||
await initUI();
|
||||
|
||||
const currentModel = getCurrentModel(provider);
|
||||
const settingsPath = getProviderSettingsPath(provider);
|
||||
const settingsPath = migrateLegacyProviderSettingsIfNeeded(provider);
|
||||
const normalizedCurrentModel = currentModel
|
||||
? canonicalizeModelForProvider(provider, currentModel)
|
||||
: undefined;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { COPILOT_SUBCOMMANDS } from '../copilot/constants';
|
||||
import { CURSOR_SUBCOMMANDS } from '../cursor/constants';
|
||||
import { CLIPROXY_PROVIDER_IDS } from '../cliproxy/provider-capabilities';
|
||||
|
||||
export type HelpTopicName = 'profiles' | 'providers' | 'kiro' | 'completion' | 'targets';
|
||||
@@ -105,7 +104,7 @@ export const ROOT_COMMAND_CATALOG: readonly RootCommandEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'cursor',
|
||||
summary: 'Run or manage the Cursor bridge',
|
||||
summary: 'Run Cursor via CLIProxy or manage Cursor provider auth',
|
||||
group: 'runtime',
|
||||
visibility: 'public',
|
||||
},
|
||||
@@ -308,7 +307,14 @@ export const COMMAND_FLAG_SUGGESTIONS: Readonly<Record<string, readonly string[]
|
||||
update: ['--force', '--beta', '--dev', '--help', '-h'],
|
||||
};
|
||||
|
||||
export const CURSOR_COMPLETION_SUBCOMMANDS = [...CURSOR_SUBCOMMANDS] as const;
|
||||
export const CURSOR_COMPLETION_SUBCOMMANDS = [
|
||||
'--auth',
|
||||
'--accounts',
|
||||
'--config',
|
||||
'--logout',
|
||||
'--help',
|
||||
'-h',
|
||||
] as const;
|
||||
export const COPILOT_COMPLETION_SUBCOMMANDS = [...COPILOT_SUBCOMMANDS, 'help'] as const;
|
||||
|
||||
export function getPublicRootCommands(): readonly RootCommandEntry[] {
|
||||
|
||||
@@ -4,6 +4,9 @@ import type { CursorConfig } from '../config/unified-config-types';
|
||||
import { getCcsDirDisplay } from '../utils/config-manager';
|
||||
import { color } from '../utils/ui';
|
||||
|
||||
const LEGACY_CURSOR_COMMAND = 'ccs legacy cursor';
|
||||
const CLIPROXY_CURSOR_COMMAND = 'ccs cursor';
|
||||
|
||||
function printLines(lines: string[]): void {
|
||||
for (const line of lines) {
|
||||
console.log(line);
|
||||
@@ -14,11 +17,11 @@ export function renderCursorHelp(): number {
|
||||
printLines([
|
||||
'Legacy Cursor Compatibility',
|
||||
'',
|
||||
'Deprecated: prefer CLIProxy-backed Cursor auth and account management.',
|
||||
'Supported auth path: ccs cursor --auth',
|
||||
'Deprecated: `ccs cursor` now belongs to the CLIProxy Cursor provider.',
|
||||
`Supported auth path: ${CLIPROXY_CURSOR_COMMAND} --auth`,
|
||||
'Supported dashboard path: ccs config -> CLIProxy -> Cursor',
|
||||
'',
|
||||
'Usage: ccs cursor <subcommand>',
|
||||
`Usage: ${LEGACY_CURSOR_COMMAND} <subcommand>`,
|
||||
'',
|
||||
'Subcommands (deprecated compatibility for the local reverse-engineered bridge):',
|
||||
' auth Import Cursor IDE authentication token (deprecated)',
|
||||
@@ -32,24 +35,24 @@ export function renderCursorHelp(): number {
|
||||
' help Show this help message',
|
||||
'',
|
||||
'Supported CLIProxy path:',
|
||||
' ccs cursor --auth # Authenticate Cursor via CLIProxy',
|
||||
' ccs cursor --accounts # Manage CLIProxy Cursor accounts',
|
||||
' ccs cursor --config # Open CLIProxy Cursor settings',
|
||||
` ${CLIPROXY_CURSOR_COMMAND} --auth # Authenticate Cursor via CLIProxy`,
|
||||
` ${CLIPROXY_CURSOR_COMMAND} --accounts # Manage CLIProxy Cursor accounts`,
|
||||
` ${CLIPROXY_CURSOR_COMMAND} --config # Open CLIProxy Cursor settings`,
|
||||
'',
|
||||
'Legacy runtime entry (deprecated compatibility):',
|
||||
' ccs cursor [claude args] # Run Claude via the local Cursor bridge',
|
||||
` ${LEGACY_CURSOR_COMMAND} [claude args] # Run Claude via the local Cursor bridge`,
|
||||
'',
|
||||
'Legacy auth options:',
|
||||
' ccs cursor auth # Auto-detect from Cursor SQLite (deprecated)',
|
||||
' ccs cursor auth --manual --token <t> --machine-id <id>',
|
||||
` ${LEGACY_CURSOR_COMMAND} auth # Auto-detect from Cursor SQLite (deprecated)`,
|
||||
` ${LEGACY_CURSOR_COMMAND} auth --manual --token <t> --machine-id <id>`,
|
||||
'',
|
||||
'Legacy bridge quick start:',
|
||||
' 1. ccs cursor enable # Deprecated compatibility: enable local bridge',
|
||||
' 2. ccs cursor auth # Deprecated compatibility: import Cursor IDE token',
|
||||
' 3. ccs cursor start # Start local daemon',
|
||||
' 4. ccs cursor probe # Verify live runtime health',
|
||||
' 5. ccs cursor "task" # Run Claude through the local bridge',
|
||||
' 6. ccs cursor status # Inspect auth/daemon wiring',
|
||||
` 1. ${LEGACY_CURSOR_COMMAND} enable # Deprecated compatibility: enable local bridge`,
|
||||
` 2. ${LEGACY_CURSOR_COMMAND} auth # Deprecated compatibility: import Cursor IDE token`,
|
||||
` 3. ${LEGACY_CURSOR_COMMAND} start # Start local daemon`,
|
||||
` 4. ${LEGACY_CURSOR_COMMAND} probe # Verify live runtime health`,
|
||||
` 5. ${LEGACY_CURSOR_COMMAND} "task" # Run Claude through the local bridge`,
|
||||
` 6. ${LEGACY_CURSOR_COMMAND} status # Inspect auth/daemon wiring`,
|
||||
'',
|
||||
'Web UI: ccs config -> Deprecated -> Cursor IDE',
|
||||
'',
|
||||
@@ -109,11 +112,13 @@ export function renderCursorStatus(
|
||||
console.log('');
|
||||
console.log('Client setup:');
|
||||
console.log(` Raw settings: ${dirDisplay}/cursor.settings.json`);
|
||||
console.log(' Runtime entry: ccs cursor [claude args] (deprecated compatibility)');
|
||||
console.log(' Supported auth: ccs cursor --auth');
|
||||
console.log(' Live probe: ccs cursor probe');
|
||||
console.log(' Status command: ccs cursor status');
|
||||
console.log(' Help command: ccs cursor help');
|
||||
console.log(
|
||||
` Runtime entry: ${LEGACY_CURSOR_COMMAND} [claude args] (deprecated compatibility)`
|
||||
);
|
||||
console.log(` Supported auth: ${CLIPROXY_CURSOR_COMMAND} --auth`);
|
||||
console.log(` Live probe: ${LEGACY_CURSOR_COMMAND} probe`);
|
||||
console.log(` Status command: ${LEGACY_CURSOR_COMMAND} status`);
|
||||
console.log(` Help command: ${LEGACY_CURSOR_COMMAND} help`);
|
||||
|
||||
if (isReady) {
|
||||
return;
|
||||
@@ -123,16 +128,16 @@ export function renderCursorStatus(
|
||||
|
||||
console.log('Next steps:');
|
||||
if (!cursorConfig.enabled) {
|
||||
console.log(' - Enable: ccs cursor enable');
|
||||
console.log(` - Enable: ${LEGACY_CURSOR_COMMAND} enable`);
|
||||
}
|
||||
if (!authStatus.authenticated || authStatus.expired) {
|
||||
console.log(' - Supported: ccs cursor --auth');
|
||||
console.log(' - Legacy auth: ccs cursor auth');
|
||||
console.log(` - Supported: ${CLIPROXY_CURSOR_COMMAND} --auth`);
|
||||
console.log(` - Legacy auth: ${LEGACY_CURSOR_COMMAND} auth`);
|
||||
}
|
||||
if (!daemonStatus.running) {
|
||||
console.log(' - Start: ccs cursor start');
|
||||
console.log(` - Start: ${LEGACY_CURSOR_COMMAND} start`);
|
||||
}
|
||||
console.log(' - Help: ccs cursor help');
|
||||
console.log(` - Help: ${LEGACY_CURSOR_COMMAND} help`);
|
||||
}
|
||||
|
||||
export function renderCursorModels(models: CursorModel[], defaultModel: string): void {
|
||||
|
||||
@@ -26,10 +26,13 @@ import {
|
||||
} from './cursor-command-display';
|
||||
import { ok, fail, info } from '../utils/ui';
|
||||
|
||||
const LEGACY_CURSOR_COMMAND = 'ccs legacy cursor';
|
||||
const CLIPROXY_CURSOR_COMMAND = 'ccs cursor';
|
||||
|
||||
function printLegacyCursorDeprecationNotice(): void {
|
||||
console.log(
|
||||
info(
|
||||
'Deprecated compatibility path. Prefer CLIProxy-backed Cursor auth via `ccs cursor --auth` or the CLIProxy dashboard.'
|
||||
`Deprecated compatibility path. \`${CLIPROXY_CURSOR_COMMAND}\` now belongs to the CLIProxy Cursor provider; use \`${LEGACY_CURSOR_COMMAND}\` for the old bridge.`
|
||||
)
|
||||
);
|
||||
console.log('');
|
||||
@@ -135,7 +138,7 @@ async function handleAuth(args: string[]): Promise<number> {
|
||||
if (!accessToken || !machineId) {
|
||||
console.error(
|
||||
fail(
|
||||
'Manual auth requires both token and machine ID.\n\nExample:\n ccs cursor auth --manual --token <token> --machine-id <machine-id>'
|
||||
`Manual auth requires both token and machine ID.\n\nExample:\n ${LEGACY_CURSOR_COMMAND} auth --manual --token <token> --machine-id <machine-id>`
|
||||
)
|
||||
);
|
||||
return 1;
|
||||
@@ -156,9 +159,9 @@ async function handleAuth(args: string[]): Promise<number> {
|
||||
console.log(ok('Cursor credentials imported (manual mode)'));
|
||||
console.log('');
|
||||
console.log('Next steps:');
|
||||
console.log(' 0. Preferred auth: ccs cursor --auth');
|
||||
console.log(' 1. Enable integration: ccs cursor enable');
|
||||
console.log(' 2. Start daemon: ccs cursor start');
|
||||
console.log(` 0. Preferred auth: ${CLIPROXY_CURSOR_COMMAND} --auth`);
|
||||
console.log(` 1. Enable integration: ${LEGACY_CURSOR_COMMAND} enable`);
|
||||
console.log(` 2. Start daemon: ${LEGACY_CURSOR_COMMAND} start`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -179,10 +182,10 @@ async function handleAuth(args: string[]): Promise<number> {
|
||||
console.log(ok('Auto-detected Cursor credentials'));
|
||||
console.log('');
|
||||
console.log('Next steps:');
|
||||
console.log(' 0. Preferred auth: ccs cursor --auth');
|
||||
console.log(' 1. Enable integration: ccs cursor enable');
|
||||
console.log(' 2. Start daemon: ccs cursor start');
|
||||
console.log(' 3. Check status: ccs cursor status');
|
||||
console.log(` 0. Preferred auth: ${CLIPROXY_CURSOR_COMMAND} --auth`);
|
||||
console.log(` 1. Enable integration: ${LEGACY_CURSOR_COMMAND} enable`);
|
||||
console.log(` 2. Start daemon: ${LEGACY_CURSOR_COMMAND} start`);
|
||||
console.log(` 3. Check status: ${LEGACY_CURSOR_COMMAND} status`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -190,7 +193,7 @@ async function handleAuth(args: string[]): Promise<number> {
|
||||
printAutoDetectFailure(autoResult);
|
||||
console.log('');
|
||||
console.log('Manual fallback:');
|
||||
console.log(' ccs cursor auth --manual --token <token> --machine-id <machine-id>');
|
||||
console.log(` ${LEGACY_CURSOR_COMMAND} auth --manual --token <token> --machine-id <machine-id>`);
|
||||
console.log('');
|
||||
|
||||
return 1;
|
||||
@@ -230,17 +233,17 @@ async function handleStart(): Promise<number> {
|
||||
const cursorConfig = getCursorConfig();
|
||||
|
||||
if (!cursorConfig.enabled) {
|
||||
console.error(fail('Cursor integration is disabled. Run: ccs cursor enable'));
|
||||
console.error(fail(`Cursor integration is disabled. Run: ${LEGACY_CURSOR_COMMAND} enable`));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const authStatus = checkAuthStatus();
|
||||
if (!authStatus.authenticated) {
|
||||
console.error(fail('Not authenticated. Run: ccs cursor auth'));
|
||||
console.error(fail(`Not authenticated. Run: ${LEGACY_CURSOR_COMMAND} auth`));
|
||||
return 1;
|
||||
}
|
||||
if (authStatus.expired) {
|
||||
console.error(fail('Credentials expired. Run: ccs cursor auth'));
|
||||
console.error(fail(`Credentials expired. Run: ${LEGACY_CURSOR_COMMAND} auth`));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -294,10 +297,10 @@ async function handleEnable(): Promise<number> {
|
||||
console.log(ok('Cursor integration enabled'));
|
||||
console.log('');
|
||||
console.log('Next steps:');
|
||||
console.log(' 0. Preferred auth: ccs cursor --auth');
|
||||
console.log(' 1. Authenticate: ccs cursor auth');
|
||||
console.log(' 2. Start daemon: ccs cursor start');
|
||||
console.log(' 3. Check status: ccs cursor status');
|
||||
console.log(` 0. Preferred auth: ${CLIPROXY_CURSOR_COMMAND} --auth`);
|
||||
console.log(` 1. Authenticate: ${LEGACY_CURSOR_COMMAND} auth`);
|
||||
console.log(` 2. Start daemon: ${LEGACY_CURSOR_COMMAND} start`);
|
||||
console.log(` 3. Check status: ${LEGACY_CURSOR_COMMAND} status`);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -344,8 +344,7 @@ export async function handleHelpRoute(
|
||||
cliproxy: async () => (await import('./cliproxy/help-subcommand')).showHelp(),
|
||||
copilot: async () =>
|
||||
process.exit(await (await import('./copilot-command')).handleCopilotCommand(['--help'])),
|
||||
cursor: async () =>
|
||||
process.exit(await (await import('./cursor-command')).handleCursorCommand(['--help'])),
|
||||
cursor: async () => await showProviderShortcutHelp('cursor', writeLine),
|
||||
docker: async () => (await import('./docker/help-subcommand')).showHelp(),
|
||||
migrate: async () => (await import('./migrate-command')).printMigrateHelp(),
|
||||
setup: async () => (await import('./setup-command')).handleSetupCommand(['--help']),
|
||||
|
||||
@@ -20,6 +20,8 @@ export const RESERVED_PROFILE_NAMES = [
|
||||
'copilot',
|
||||
// Cursor IDE (Cursor proxy daemon)
|
||||
'cursor',
|
||||
'legacy-cursor',
|
||||
'legacy',
|
||||
// CLI commands and special names
|
||||
'default',
|
||||
'config',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const LEGACY_CURSOR_PROFILE_NAME = 'legacy-cursor';
|
||||
|
||||
export const CURSOR_SUBCOMMANDS = [
|
||||
'auth',
|
||||
'status',
|
||||
|
||||
@@ -248,7 +248,7 @@ export function startCursorDaemonServer(options: DaemonRuntimeOptions): http.Ser
|
||||
|
||||
const authStatus = checkAuthStatus();
|
||||
if (!authStatus.authenticated || !authStatus.credentials) {
|
||||
const message = 'Cursor credentials not found. Run `ccs cursor auth` first.';
|
||||
const message = 'Cursor credentials not found. Run `ccs legacy cursor auth` first.';
|
||||
if (isAnthropicRoute) {
|
||||
await pipeWebResponseToNode(
|
||||
createAnthropicErrorResponse(401, 'authentication_error', message),
|
||||
@@ -266,7 +266,7 @@ export function startCursorDaemonServer(options: DaemonRuntimeOptions): http.Ser
|
||||
}
|
||||
|
||||
if (authStatus.expired) {
|
||||
const message = 'Cursor credentials expired. Run `ccs cursor auth` again.';
|
||||
const message = 'Cursor credentials expired. Run `ccs legacy cursor auth` again.';
|
||||
if (isAnthropicRoute) {
|
||||
await pipeWebResponseToNode(
|
||||
createAnthropicErrorResponse(401, 'authentication_error', message),
|
||||
|
||||
@@ -97,7 +97,7 @@ export async function executeCursorProfile(
|
||||
if (!config.enabled) {
|
||||
console.error(fail('Cursor integration is not enabled.'));
|
||||
console.error('');
|
||||
console.error('Enable it first: ccs cursor enable');
|
||||
console.error('Enable it first: ccs legacy cursor enable');
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -105,13 +105,13 @@ export async function executeCursorProfile(
|
||||
if (!authStatus.authenticated) {
|
||||
console.error(fail('Cursor credentials not found.'));
|
||||
console.error('');
|
||||
console.error('Authenticate first: ccs cursor auth');
|
||||
console.error('Authenticate first: ccs legacy cursor auth');
|
||||
return 1;
|
||||
}
|
||||
if (authStatus.expired) {
|
||||
console.error(fail('Cursor credentials have expired.'));
|
||||
console.error('');
|
||||
console.error('Refresh them with: ccs cursor auth');
|
||||
console.error('Refresh them with: ccs legacy cursor auth');
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export async function executeCursorProfile(
|
||||
console.error(fail('Cursor daemon is not running.'));
|
||||
console.error('');
|
||||
console.error('Start the daemon:');
|
||||
console.error(' ccs cursor start');
|
||||
console.error(' ccs legacy cursor start');
|
||||
console.error('Or enable auto_start in the Cursor config section.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ export async function probeCursorRuntime(config: CursorConfig): Promise<CursorPr
|
||||
stage: 'auth',
|
||||
status: 401,
|
||||
duration_ms: Date.now() - startedAt,
|
||||
message: 'Cursor credentials not found. Run `ccs cursor auth` first.',
|
||||
message: 'Cursor credentials not found. Run `ccs legacy cursor auth` first.',
|
||||
error_type: 'authentication_error',
|
||||
};
|
||||
}
|
||||
@@ -131,7 +131,7 @@ export async function probeCursorRuntime(config: CursorConfig): Promise<CursorPr
|
||||
stage: 'auth',
|
||||
status: 401,
|
||||
duration_ms: Date.now() - startedAt,
|
||||
message: 'Cursor credentials expired. Run `ccs cursor auth` again.',
|
||||
message: 'Cursor credentials expired. Run `ccs legacy cursor auth` again.',
|
||||
error_type: 'authentication_error',
|
||||
};
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export async function probeCursorRuntime(config: CursorConfig): Promise<CursorPr
|
||||
status: 503,
|
||||
duration_ms: Date.now() - startedAt,
|
||||
message:
|
||||
'Cursor daemon is not running. Start it with `ccs cursor start` or enable auto_start.',
|
||||
'Cursor daemon is not running. Start it with `ccs legacy cursor start` or enable auto_start.',
|
||||
error_type: 'daemon_not_running',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ function resolveBackend(
|
||||
};
|
||||
}
|
||||
|
||||
if (profileType === 'cursor' || profileName === 'cursor') {
|
||||
if (profileType === 'cursor') {
|
||||
return {
|
||||
backendId: null,
|
||||
backendDisplayName: null,
|
||||
|
||||
@@ -104,6 +104,7 @@ apiRoutes.use('/copilot', copilotRoutes);
|
||||
|
||||
// ==================== Cursor ====================
|
||||
apiRoutes.use('/cursor', cursorRoutes);
|
||||
apiRoutes.use('/legacy/cursor', cursorRoutes);
|
||||
|
||||
// ==================== Droid ====================
|
||||
apiRoutes.use('/droid', droidRoutes);
|
||||
|
||||
@@ -30,6 +30,7 @@ import { requireLocalAccessWhenAuthDisabled } from '../middleware/auth-middlewar
|
||||
import type { Settings } from '../../types/config';
|
||||
import type { CLIProxyProvider } from '../../cliproxy/types';
|
||||
import { mapExternalProviderName } from '../../cliproxy/provider-capabilities';
|
||||
import { migrateLegacyProviderSettingsIfNeeded } from '../../cliproxy/config/path-resolver';
|
||||
import { expandPath } from '../../utils/helpers';
|
||||
import {
|
||||
canonicalizeModelIdForProvider,
|
||||
@@ -101,6 +102,17 @@ function resolveSettingsPath(profileOrVariant: string): string {
|
||||
const ccsDir = getCcsDir();
|
||||
const resolvedCcsDir = path.resolve(ccsDir);
|
||||
|
||||
const directProvider = mapExternalProviderName(profileOrVariant);
|
||||
if (directProvider) {
|
||||
if (profileOrVariant !== directProvider) {
|
||||
return resolvePathWithin(
|
||||
resolvedCcsDir,
|
||||
path.join(resolvedCcsDir, `${profileOrVariant}.settings.json`)
|
||||
);
|
||||
}
|
||||
return path.resolve(migrateLegacyProviderSettingsIfNeeded(directProvider));
|
||||
}
|
||||
|
||||
// Check if this is a variant
|
||||
const variants = listVariants();
|
||||
const variant = variants[profileOrVariant];
|
||||
|
||||
Reference in new issue
Block a user