mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-03 04:17:54 +00:00
fix(env): address all PR review feedback
- Add settings profiles to zsh env completion (was proxy-only) - Document intentional ANTHROPIC_MODEL omission in transformToOpenAI - Use getCcsDir() in error hint instead of hardcoded ~/.ccs/ - Export parseFlag and add 5 unit tests for flag parsing - Add fish and PowerShell single-quote escaping tests
This commit is contained in:
@@ -130,7 +130,7 @@ _ccs() {
|
|||||||
'--format[Output format]:format:(openai anthropic raw)' \
|
'--format[Output format]:format:(openai anthropic raw)' \
|
||||||
'--shell[Shell syntax]:shell:(bash fish powershell)' \
|
'--shell[Shell syntax]:shell:(bash fish powershell)' \
|
||||||
'(- *)'{-h,--help}'[Show help]' \
|
'(- *)'{-h,--help}'[Show help]' \
|
||||||
'1:profile:($proxy_profiles)'
|
'1:profile:($proxy_profiles ${(k)settings_profiles_described})'
|
||||||
;;
|
;;
|
||||||
gemini|codex|agy|qwen)
|
gemini|codex|agy|qwen)
|
||||||
_arguments \
|
_arguments \
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { getEffectiveEnvVars } from '../cliproxy/config/env-builder';
|
|||||||
import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager';
|
import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager';
|
||||||
import { isUnifiedMode, loadUnifiedConfig } from '../config/unified-config-loader';
|
import { isUnifiedMode, loadUnifiedConfig } from '../config/unified-config-loader';
|
||||||
import { expandPath } from '../utils/helpers';
|
import { expandPath } from '../utils/helpers';
|
||||||
|
import { getCcsDir } from '../utils/config-manager';
|
||||||
|
|
||||||
type ShellType = 'bash' | 'fish' | 'powershell';
|
type ShellType = 'bash' | 'fish' | 'powershell';
|
||||||
type OutputFormat = 'openai' | 'anthropic' | 'raw';
|
type OutputFormat = 'openai' | 'anthropic' | 'raw';
|
||||||
@@ -46,7 +47,9 @@ export function formatExportLine(shell: ShellType, key: string, value: string):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Map Anthropic env vars to OpenAI-compatible format */
|
/** Map Anthropic env vars to OpenAI-compatible format.
|
||||||
|
* ANTHROPIC_MODEL is intentionally omitted — the proxy endpoint handles
|
||||||
|
* model routing, so OPENAI_MODEL is unnecessary for OpenAI-compatible tools. */
|
||||||
export function transformToOpenAI(envVars: Record<string, string>): Record<string, string> {
|
export function transformToOpenAI(envVars: Record<string, string>): Record<string, string> {
|
||||||
const baseUrl = envVars['ANTHROPIC_BASE_URL'] || '';
|
const baseUrl = envVars['ANTHROPIC_BASE_URL'] || '';
|
||||||
const apiKey = envVars['ANTHROPIC_AUTH_TOKEN'] || '';
|
const apiKey = envVars['ANTHROPIC_AUTH_TOKEN'] || '';
|
||||||
@@ -58,7 +61,7 @@ export function transformToOpenAI(envVars: Record<string, string>): Record<strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Parse --key=value or --key value style args */
|
/** Parse --key=value or --key value style args */
|
||||||
function parseFlag(args: string[], flag: string): string | undefined {
|
export function parseFlag(args: string[], flag: string): string | undefined {
|
||||||
// --flag=value style
|
// --flag=value style
|
||||||
const eqMatch = args.find((a) => a.startsWith(`--${flag}=`));
|
const eqMatch = args.find((a) => a.startsWith(`--${flag}=`));
|
||||||
if (eqMatch) return eqMatch.split('=').slice(1).join('=');
|
if (eqMatch) return eqMatch.split('=').slice(1).join('=');
|
||||||
@@ -190,7 +193,7 @@ export async function handleEnvCommand(args: string[]): Promise<void> {
|
|||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
console.error(fail(`Profile '${profile}' not found.`));
|
console.error(fail(`Profile '${profile}' not found.`));
|
||||||
console.error(dim(' Available CLIProxy profiles: ' + CLIPROXY_PROFILES.join(', ')));
|
console.error(dim(' Available CLIProxy profiles: ' + CLIPROXY_PROFILES.join(', ')));
|
||||||
console.error(dim(' Check ~/.ccs/config.yaml for custom profiles.'));
|
console.error(dim(` Check ${getCcsDir()}/config.yaml for custom profiles.`));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
envVars = resolved;
|
envVars = resolved;
|
||||||
|
|||||||
@@ -4,7 +4,12 @@
|
|||||||
* Tests pure utility functions: detectShell, formatExportLine, transformToOpenAI
|
* Tests pure utility functions: detectShell, formatExportLine, transformToOpenAI
|
||||||
*/
|
*/
|
||||||
import { describe, it, expect, afterEach } from 'bun:test';
|
import { describe, it, expect, afterEach } from 'bun:test';
|
||||||
import { detectShell, formatExportLine, transformToOpenAI } from '../../../src/commands/env-command';
|
import {
|
||||||
|
detectShell,
|
||||||
|
formatExportLine,
|
||||||
|
transformToOpenAI,
|
||||||
|
parseFlag,
|
||||||
|
} from '../../../src/commands/env-command';
|
||||||
|
|
||||||
describe('env-command', () => {
|
describe('env-command', () => {
|
||||||
describe('detectShell', () => {
|
describe('detectShell', () => {
|
||||||
@@ -91,6 +96,16 @@ describe('env-command', () => {
|
|||||||
"export TOKEN='safe$(whoami)'"
|
"export TOKEN='safe$(whoami)'"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('escapes single quotes in fish values', () => {
|
||||||
|
expect(formatExportLine('fish', 'VAL', "it's here")).toBe("set -gx VAL 'it\\'s here'");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('escapes single quotes in powershell values', () => {
|
||||||
|
expect(formatExportLine('powershell', 'VAL', "it's here")).toBe(
|
||||||
|
"$env:VAL = 'it''s here'"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('transformToOpenAI', () => {
|
describe('transformToOpenAI', () => {
|
||||||
@@ -131,4 +146,26 @@ describe('env-command', () => {
|
|||||||
expect(result['ANTHROPIC_MAX_TOKENS']).toBeUndefined();
|
expect(result['ANTHROPIC_MAX_TOKENS']).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parseFlag', () => {
|
||||||
|
it('parses --flag=value style', () => {
|
||||||
|
expect(parseFlag(['--format=openai'], 'format')).toBe('openai');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses --flag value style', () => {
|
||||||
|
expect(parseFlag(['--format', 'openai'], 'format')).toBe('openai');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles values containing =', () => {
|
||||||
|
expect(parseFlag(['--format=key=val=ue'], 'format')).toBe('key=val=ue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined for missing flag', () => {
|
||||||
|
expect(parseFlag(['--shell', 'bash'], 'format')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not consume next flag as value', () => {
|
||||||
|
expect(parseFlag(['--format', '--shell'], 'format')).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user