mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 22:17:14 +00:00
feat(cliproxy): add Qoder as a CLIProxy provider channel
Add Qoder AI coding assistant as a supported CLIProxy provider: - Device code OAuth flow with --qoder-login flag - Plus-only backend (CLIProxyAPIPlus required) - No built-in token refresh (unsupported, like Kilo) - Auth URL: https://qoder.com/device/selectAccounts - Auth file prefix: qoder- - Token type: qoder
This commit is contained in:
@@ -306,6 +306,13 @@ export const OAUTH_CONFIGS: Record<CLIProxyProvider, ProviderOAuthConfig> = {
|
|||||||
scopes: [],
|
scopes: [],
|
||||||
authFlag: '--kilo-login',
|
authFlag: '--kilo-login',
|
||||||
},
|
},
|
||||||
|
qoder: {
|
||||||
|
provider: 'qoder',
|
||||||
|
displayName: 'Qoder',
|
||||||
|
authUrl: 'https://qoder.com/device/selectAccounts',
|
||||||
|
scopes: [],
|
||||||
|
authFlag: '--qoder-login',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -182,6 +182,18 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
|||||||
tokenTypeValues: ['kilo'],
|
tokenTypeValues: ['kilo'],
|
||||||
aliases: [],
|
aliases: [],
|
||||||
},
|
},
|
||||||
|
qoder: {
|
||||||
|
displayName: 'Qoder',
|
||||||
|
description: 'Qoder AI coding assistant',
|
||||||
|
oauthFlow: 'device_code',
|
||||||
|
callbackPort: null,
|
||||||
|
callbackProviderName: 'qoder',
|
||||||
|
authUrlProviderName: 'qoder',
|
||||||
|
refreshOwnership: 'unsupported',
|
||||||
|
authFilePrefixes: ['qoder-'],
|
||||||
|
tokenTypeValues: ['qoder'],
|
||||||
|
aliases: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CLIPROXY_PROVIDER_IDS = Object.freeze(
|
export const CLIPROXY_PROVIDER_IDS = Object.freeze(
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export type CLIProxyProvider =
|
|||||||
| 'cursor'
|
| 'cursor'
|
||||||
| 'gitlab'
|
| 'gitlab'
|
||||||
| 'codebuddy'
|
| 'codebuddy'
|
||||||
| 'kilo';
|
| 'kilo'
|
||||||
|
| 'qoder';
|
||||||
|
|
||||||
/** CLIProxy backend selection */
|
/** CLIProxy backend selection */
|
||||||
export type CLIProxyBackend = 'original' | 'plus';
|
export type CLIProxyBackend = 'original' | 'plus';
|
||||||
@@ -32,6 +33,7 @@ export const PLUS_ONLY_PROVIDERS: CLIProxyProvider[] = [
|
|||||||
'gitlab',
|
'gitlab',
|
||||||
'codebuddy',
|
'codebuddy',
|
||||||
'kilo',
|
'kilo',
|
||||||
|
'qoder',
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Model mapping for each provider */
|
/** Model mapping for each provider */
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ export const BUILTIN_PROVIDER_SHORTCUTS: readonly ShortcutEntry[] = CLIPROXY_PRO
|
|||||||
gitlab: 'GitLab Duo via CLIProxy OAuth',
|
gitlab: 'GitLab Duo via CLIProxy OAuth',
|
||||||
codebuddy: 'CodeBuddy via CLIProxy OAuth',
|
codebuddy: 'CodeBuddy via CLIProxy OAuth',
|
||||||
kilo: 'Kilo AI via CLIProxy OAuth',
|
kilo: 'Kilo AI via CLIProxy OAuth',
|
||||||
|
qoder: 'Qoder AI via CLIProxy OAuth',
|
||||||
}[name] || 'CLIProxy OAuth provider',
|
}[name] || 'CLIProxy OAuth provider',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const RESERVED_PROFILE_NAMES = [
|
|||||||
'gitlab',
|
'gitlab',
|
||||||
'codebuddy',
|
'codebuddy',
|
||||||
'kilo',
|
'kilo',
|
||||||
|
'qoder',
|
||||||
// Copilot API (GitHub Copilot proxy)
|
// Copilot API (GitHub Copilot proxy)
|
||||||
'copilot',
|
'copilot',
|
||||||
// Cursor IDE (Cursor proxy daemon)
|
// Cursor IDE (Cursor proxy daemon)
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ describe('ProfileDetector', () => {
|
|||||||
expect(detector.detectProfileType('gitlab').provider).toBe('gitlab');
|
expect(detector.detectProfileType('gitlab').provider).toBe('gitlab');
|
||||||
expect(detector.detectProfileType('codebuddy').provider).toBe('codebuddy');
|
expect(detector.detectProfileType('codebuddy').provider).toBe('codebuddy');
|
||||||
expect(detector.detectProfileType('kilo').provider).toBe('kilo');
|
expect(detector.detectProfileType('kilo').provider).toBe('kilo');
|
||||||
|
expect(detector.detectProfileType('qoder').provider).toBe('qoder');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect settings-based profile from unified config', () => {
|
it('should detect settings-based profile from unified config', () => {
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ describe('completion backend', () => {
|
|||||||
expect(values).toContain('gitlab');
|
expect(values).toContain('gitlab');
|
||||||
expect(values).toContain('codebuddy');
|
expect(values).toContain('codebuddy');
|
||||||
expect(values).toContain('kilo');
|
expect(values).toContain('kilo');
|
||||||
|
expect(values).toContain('qoder');
|
||||||
expect(values).toContain('localglm');
|
expect(values).toContain('localglm');
|
||||||
expect(values).toContain('work');
|
expect(values).toContain('work');
|
||||||
expect(values).toContain('my-codex');
|
expect(values).toContain('my-codex');
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ describe('help command parity', () => {
|
|||||||
expect(rendered.includes('gitlab')).toBe(true);
|
expect(rendered.includes('gitlab')).toBe(true);
|
||||||
expect(rendered.includes('codebuddy')).toBe(true);
|
expect(rendered.includes('codebuddy')).toBe(true);
|
||||||
expect(rendered.includes('kilo')).toBe(true);
|
expect(rendered.includes('kilo')).toBe(true);
|
||||||
|
expect(rendered.includes('qoder')).toBe(true);
|
||||||
expect(rendered.includes('--gitlab-token-login')).toBe(true);
|
expect(rendered.includes('--gitlab-token-login')).toBe(true);
|
||||||
expect(rendered.includes('--token-login')).toBe(true);
|
expect(rendered.includes('--token-login')).toBe(true);
|
||||||
expect(rendered.includes('--gitlab-url <url>')).toBe(true);
|
expect(rendered.includes('--gitlab-url <url>')).toBe(true);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ describe('cliproxy-auth-routes start-url guard', () => {
|
|||||||
"Provider 'codebuddy' uses Device Code flow"
|
"Provider 'codebuddy' uses Device Code flow"
|
||||||
);
|
);
|
||||||
expect(getStartUrlUnsupportedReason('kilo')).toContain("Provider 'kilo' uses Device Code flow");
|
expect(getStartUrlUnsupportedReason('kilo')).toContain("Provider 'kilo' uses Device Code flow");
|
||||||
|
expect(getStartUrlUnsupportedReason('qoder')).toContain("Provider 'qoder' uses Device Code flow");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows Cursor browser URL auth on start-url', () => {
|
it('allows Cursor browser URL auth on start-url', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user