mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 10:19:37 +00:00
Merge pull request #1078 from kaitranntt/kai/chore/sync-main-into-dev-260424
chore: sync main hotfixes into dev
This commit is contained in:
@@ -1,3 +1,41 @@
|
||||
## [7.73.1](https://github.com/kaitranntt/ccs/compare/v7.73.0...v7.73.1) (2026-04-22)
|
||||
|
||||
### Hotfixes
|
||||
|
||||
* **cliproxy:** fallback from deleted CLIProxyAPIPlus to original backend ([60cbdce](https://github.com/kaitranntt/ccs/commit/60cbdce83dd429aeb6df5f1439ba99edd96acc3a)), closes [#1062](https://github.com/kaitranntt/ccs/issues/1062)
|
||||
* close cliproxy plus fallback gaps ([7b1a7f3](https://github.com/kaitranntt/ccs/commit/7b1a7f3a09b5c6394897b5f95ad32bc9109e6a21))
|
||||
* preserve plus fallback state and guard variant updates ([5b5f4a5](https://github.com/kaitranntt/ccs/commit/5b5f4a5ef52f91cb6d4bc7fd3413eac714945d4a))
|
||||
|
||||
## [7.73.0](https://github.com/kaitranntt/ccs/compare/v7.72.1...v7.73.0) (2026-04-19)
|
||||
|
||||
### Features
|
||||
|
||||
* **proxy:** enhance Anthropic-to-OpenAI message transformation and schema sanitization ([2672e35](https://github.com/kaitranntt/ccs/commit/2672e35362938b054710de681760bd24c3c39e19))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **browser:** bootstrap managed attach profile setup ([b525033](https://github.com/kaitranntt/ccs/commit/b52503300b0c434fbb9d88283643d3ceaa673d44))
|
||||
* **cliproxy:** preserve legacy openai-compat connectors on restart ([16f81fc](https://github.com/kaitranntt/ccs/commit/16f81fc8a595792f1c1a6df060f88cdf28d7afb6))
|
||||
* **codex:** probe Windows cmd wrappers via cmd shell ([210ec33](https://github.com/kaitranntt/ccs/commit/210ec33c04fe88be2b651c2163f638085b1b6bd2))
|
||||
* **proxy:** harden Anthropic request transformation semantics ([ebc9219](https://github.com/kaitranntt/ccs/commit/ebc92194bb9bf2810e10f64de1fa6e599c2fa156))
|
||||
* **proxy:** preserve valid enums during schema normalization ([22ab58b](https://github.com/kaitranntt/ccs/commit/22ab58b02e5dc34f152ecce048c1fbf7d615bdfb))
|
||||
* **proxy:** restore strict Anthropic message validation ([32d6bfd](https://github.com/kaitranntt/ccs/commit/32d6bfdda7881a42a376d0973c021f0b47eed7e2))
|
||||
* **repo:** remove tracked plans workspace files ([bc4ab98](https://github.com/kaitranntt/ccs/commit/bc4ab98af41e9c9f049cc5da2e6ff356832ef14b))
|
||||
* **targets:** use cmd.exe for escaped Windows wrapper launches ([a945b0b](https://github.com/kaitranntt/ccs/commit/a945b0b1047b85a7b343dfe35d8ae9680346e25f))
|
||||
* **windows:** align escaped wrapper shell handling ([ccdd0b6](https://github.com/kaitranntt/ccs/commit/ccdd0b6e8e4185ec2b01186cfc69e28f171d0906))
|
||||
|
||||
### Tests
|
||||
|
||||
* **cliproxy:** assert routing persistence via unified config loader ([485fe4b](https://github.com/kaitranntt/ccs/commit/485fe4ba1c5878083c6e2b6a2d98b37208772a1f))
|
||||
* **cliproxy:** cover legacy connector removal cleanup ([00d902b](https://github.com/kaitranntt/ccs/commit/00d902b6e64839cd6b89446cc5dee540f2a059b8))
|
||||
* **cliproxy:** isolate routing strategy service state ([5e1e807](https://github.com/kaitranntt/ccs/commit/5e1e8070e821dbe660ea77bc96fec610937ed44a))
|
||||
* **websearch:** clear CCS_PROFILE_TYPE in SearXNG fallback spawn env ([e25d791](https://github.com/kaitranntt/ccs/commit/e25d791d9bee38e7143757d37c42bd850e471ae8))
|
||||
|
||||
### CI
|
||||
|
||||
* add concurrency group and split build/test with artifact sharing ([ad54e17](https://github.com/kaitranntt/ccs/commit/ad54e179b6a3921fd26ad3ac585f5ef965b01c1b))
|
||||
* **pre-push:** skip gate for delete-only pushes ([6236cb7](https://github.com/kaitranntt/ccs/commit/6236cb7feb15e954f7f65ae98e6fd98b323a4adb))
|
||||
|
||||
## [7.72.1](https://github.com/kaitranntt/ccs/compare/v7.72.0...v7.72.1) (2026-04-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -5,10 +5,17 @@
|
||||
* Pattern: Mirrors npm install behavior (fast check, download only when needed)
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { info, warn } from '../utils/ui';
|
||||
import { getBinDir, CLIPROXY_DEFAULT_PORT } from './config-generator';
|
||||
import { BinaryInfo, BinaryManagerConfig } from './types';
|
||||
import { BACKEND_CONFIG, DEFAULT_BACKEND, CLIPROXY_MAX_STABLE_VERSION } from './platform-detector';
|
||||
import {
|
||||
BACKEND_CONFIG,
|
||||
DEFAULT_BACKEND,
|
||||
CLIPROXY_MAX_STABLE_VERSION,
|
||||
getExecutableName,
|
||||
} from './platform-detector';
|
||||
import { stopProxy } from './services/proxy-lifecycle-service';
|
||||
import { waitForPortFree } from '../utils/port-utils';
|
||||
import { loadOrCreateUnifiedConfig } from '../config/unified-config-loader';
|
||||
@@ -16,6 +23,7 @@ import {
|
||||
UpdateCheckResult,
|
||||
checkForUpdates,
|
||||
deleteBinary,
|
||||
getVersionCachePath,
|
||||
getBinaryPath,
|
||||
isBinaryInstalled,
|
||||
getBinaryInfo,
|
||||
@@ -30,10 +38,31 @@ import {
|
||||
} from './binary';
|
||||
|
||||
import type { CLIProxyBackend } from './types';
|
||||
import { getVersionListCachePath } from './binary/version-cache';
|
||||
|
||||
export const CLIPROXY_DELETED_PLUS_REPO = 'router-for-me/CLIProxyAPIPlus';
|
||||
export const CLIPROXY_PLUS_FALLBACK_TRACKING_URL = 'https://github.com/kaitranntt/ccs/issues/1062';
|
||||
export const CLIPROXY_PLUS_FORK_URL = 'https://github.com/kaitranntt/CLIProxyAPIPlus';
|
||||
export const CLIPROXY_PLUS_TRACKING_URL = 'https://github.com/kaitranntt/ccs/issues/1065';
|
||||
|
||||
/**
|
||||
* Track whether we've already warned the user about the legacy Plus fallback
|
||||
* this process lifetime. Prevents spamming the warning on every command.
|
||||
*/
|
||||
let plusFallbackWarned = false;
|
||||
|
||||
function emitPlusFallbackWarning(): void {
|
||||
if (plusFallbackWarned) return;
|
||||
plusFallbackWarned = true;
|
||||
process.stderr.write(
|
||||
`${warn(
|
||||
'Legacy CLIProxyAPIPlus upstream repo is unavailable; local CLIProxy is falling back to ' +
|
||||
'`backend: original`. Run `ccs config` to use the maintained Plus fork. ' +
|
||||
`Tracking: ${CLIPROXY_PLUS_FALLBACK_TRACKING_URL}`
|
||||
)}\n`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track whether we've already shown the optional Plus backend notice this
|
||||
* process lifetime. Prevents spamming the message on every command.
|
||||
@@ -66,13 +95,50 @@ export function getPlusBackendUnavailableMessage(provider?: string): string {
|
||||
|
||||
interface ResolveLocalBackendOptions {
|
||||
notifyOnPlus?: boolean;
|
||||
warnOnFallback?: boolean;
|
||||
}
|
||||
|
||||
function isPlusBackendFallbackActive(): boolean {
|
||||
return (BACKEND_CONFIG.plus.repo as string) === CLIPROXY_DELETED_PLUS_REPO;
|
||||
}
|
||||
|
||||
function copyFallbackStateIfMissing(sourcePath: string, targetPath: string): void {
|
||||
if (!fs.existsSync(sourcePath) || fs.existsSync(targetPath)) return;
|
||||
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
||||
fs.copyFileSync(sourcePath, targetPath);
|
||||
}
|
||||
|
||||
export function syncPlusFallbackStateIfNeeded(configuredBackend: CLIProxyBackend): void {
|
||||
if (configuredBackend !== 'plus' || !isPlusBackendFallbackActive()) return;
|
||||
|
||||
const plusDir = getBackendBinDir('plus');
|
||||
const originalDir = getBackendBinDir('original');
|
||||
|
||||
copyFallbackStateIfMissing(
|
||||
path.join(plusDir, getExecutableName('plus')),
|
||||
path.join(originalDir, getExecutableName('original'))
|
||||
);
|
||||
copyFallbackStateIfMissing(path.join(plusDir, '.version'), path.join(originalDir, '.version'));
|
||||
copyFallbackStateIfMissing(getVersionPinPath('plus'), getVersionPinPath('original'));
|
||||
copyFallbackStateIfMissing(getVersionCachePath('plus'), getVersionCachePath('original'));
|
||||
copyFallbackStateIfMissing(getVersionListCachePath('plus'), getVersionListCachePath('original'));
|
||||
}
|
||||
|
||||
export function resolveLocalBackend(
|
||||
backend: CLIProxyBackend = DEFAULT_BACKEND,
|
||||
options: ResolveLocalBackendOptions = {}
|
||||
): CLIProxyBackend {
|
||||
if (backend === 'plus' && options.notifyOnPlus) {
|
||||
if (backend !== 'plus') return backend;
|
||||
|
||||
if (isPlusBackendFallbackActive()) {
|
||||
syncPlusFallbackStateIfNeeded(backend);
|
||||
if (options.warnOnFallback) {
|
||||
emitPlusFallbackWarning();
|
||||
}
|
||||
return 'original';
|
||||
}
|
||||
|
||||
if (options.notifyOnPlus) {
|
||||
emitPlusBackendInfo();
|
||||
}
|
||||
return backend;
|
||||
|
||||
@@ -100,6 +100,44 @@ describe('installCliproxyVersion', () => {
|
||||
expect(status.binaryPath).toContain('/plus/');
|
||||
});
|
||||
|
||||
it('copies plus binary and pin state when legacy deleted upstream fallback is active', async () => {
|
||||
const { createEmptyUnifiedConfig } = await import('../../../src/config/unified-config-types');
|
||||
const { saveUnifiedConfig } = await import('../../../src/config/unified-config-loader');
|
||||
const { savePinnedVersion } = await import('../../../src/cliproxy/binary/version-cache');
|
||||
const platformDetector = await import('../../../src/cliproxy/platform-detector');
|
||||
const binaryService = await import(
|
||||
`../../../src/cliproxy/services/binary-service?binary-service-legacy-plus-fallback=${Date.now()}`
|
||||
);
|
||||
|
||||
const plusConfig = platformDetector.BACKEND_CONFIG.plus as unknown as { repo: string };
|
||||
const originalRepo = plusConfig.repo;
|
||||
|
||||
try {
|
||||
plusConfig.repo = 'router-for-me/CLIProxyAPIPlus';
|
||||
|
||||
const config = createEmptyUnifiedConfig();
|
||||
config.cliproxy = { ...config.cliproxy, backend: 'plus' };
|
||||
saveUnifiedConfig(config);
|
||||
|
||||
const plusBinDir = path.join(tempHome, '.ccs', 'cliproxy', 'bin', 'plus');
|
||||
fs.mkdirSync(plusBinDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(plusBinDir, platformDetector.getExecutableName('plus')),
|
||||
'fake-binary'
|
||||
);
|
||||
fs.writeFileSync(path.join(plusBinDir, '.version'), '6.6.80-0');
|
||||
savePinnedVersion('6.6.80-0', 'plus');
|
||||
|
||||
const status = binaryService.getBinaryStatus();
|
||||
|
||||
expect(status.installed).toBe(true);
|
||||
expect(status.pinnedVersion).toBe('6.6.80-0');
|
||||
expect(status.binaryPath).toContain('/original/');
|
||||
} finally {
|
||||
plusConfig.repo = originalRepo;
|
||||
}
|
||||
});
|
||||
|
||||
it('attempts to stop the proxy even when there is no tracked running session', async () => {
|
||||
const calls = {
|
||||
stopProxy: 0,
|
||||
|
||||
Reference in New Issue
Block a user