feat: add explicit droid runtime alias surface

This commit is contained in:
Tam Nhu Tran committed 2026-03-23 13:44:32 -04:00
1 parent 19c1e1da4f
commit 160be319d0
17 files changed
+229 -85

No files matched your search

+19 -10
View File
@@ -194,16 +194,25 @@ ccs api export glm --out ./glm.ccs-profile.json # Export for cross-device trans
ccs api import ./glm.ccs-profile.json # Import exported profile bundle ccs api import ./glm.ccs-profile.json # Import exported profile bundle
``` ```
### Droid Alias (`argv[0]` pattern) ### Runtime Aliases (`argv[0]` pattern)
By default, invoking CCS as `ccsd` auto-selects the Droid target: Built-in Droid runtime aliases:
```bash ```bash
ln -s "$(command -v ccs)" /usr/local/bin/ccs-droid
ln -s "$(command -v ccs)" /usr/local/bin/ccsd ln -s "$(command -v ccs)" /usr/local/bin/ccsd
ccsd glm ccs-droid glm # explicit alias
ccsd glm # legacy shortcut
``` ```
Need additional alias names? Set `CCS_DROID_ALIASES` as a comma-separated list (for example: `CCS_DROID_ALIASES=ccs-droid,mydroid`). Need additional alias names? Use `CCS_TARGET_ALIASES` (preferred) or keep using
legacy `CCS_DROID_ALIASES`:
```bash
CCS_TARGET_ALIASES='droid=mydroid,team-droid'
# Legacy fallback still supported:
CCS_DROID_ALIASES='mydroid,team-droid'
```
For Factory BYOK compatibility, CCS also stores a per-profile Droid provider hint For Factory BYOK compatibility, CCS also stores a per-profile Droid provider hint
(`CCS_DROID_PROVIDER`) using one of: (`CCS_DROID_PROVIDER`) using one of:
@@ -217,9 +226,9 @@ which Droid treats as queued prompt text.
CCS supports structural Droid command passthrough after profile selection: CCS supports structural Droid command passthrough after profile selection:
```bash ```bash
ccsd codex exec --skip-permissions-unsafe "fix failing tests" ccs-droid codex exec --skip-permissions-unsafe "fix failing tests"
ccsd codex --skip-permissions-unsafe "fix failing tests" # auto-routed to: droid exec ... ccs-droid codex --skip-permissions-unsafe "fix failing tests" # auto-routed to: droid exec ...
ccsd codex -m custom:gpt-5.3-codex "fix failing tests" # short exec flags auto-routed too ccs-droid codex -m custom:gpt-5.3-codex "fix failing tests" # short exec flags auto-routed too
``` ```
If you pass exec-only flags without a prompt (for example `--skip-permissions-unsafe`), If you pass exec-only flags without a prompt (for example `--skip-permissions-unsafe`),
@@ -245,10 +254,10 @@ ccs cliproxy create mycodex --provider codex --target droid
Built-in CLIProxy providers also work with Droid alias/target override: Built-in CLIProxy providers also work with Droid alias/target override:
```bash ```bash
ccsd codex ccs-droid codex
ccsd agy ccs-droid agy
ccs codex --target droid ccs codex --target droid
ccsd codex exec --auto high "triage this bug report" ccs-droid codex exec --auto high "triage this bug report"
``` ```
Dashboard parity: Dashboard parity:
+2 -1
View File
@@ -200,7 +200,8 @@ Resolves which adapter to use via `resolveTargetType()`:
↓ ↓
2. Profile config: profileConfig.target field 2. Profile config: profileConfig.target field
↓ ↓
3. argv[0] detection (busybox pattern): 3. argv[0] detection (runtime alias pattern):
- ccs-droid → droid
- ccsd → droid - ccsd → droid
- ccs → default - ccs → default
↓ ↓
+1 -1
View File
@@ -246,7 +246,7 @@ The targets module provides an extensible interface for dispatching profiles to
2. **Target Resolution** - Priority order: 2. **Target Resolution** - Priority order:
- `--target <cli>` flag (CLI argument) - `--target <cli>` flag (CLI argument)
- Per-profile `target` field (from config.yaml) - Per-profile `target` field (from config.yaml)
- `argv[0]` detection (busybox pattern: `ccsd` → droid) - `argv[0]` detection (runtime alias pattern: `ccs-droid` / `ccsd` → droid)
- Default: `claude` - Default: `claude`
3. **Implementations:** 3. **Implementations:**
+5 -4
View File
@@ -86,11 +86,12 @@ Spawn Target Process
- Spawns: `droid -m custom:ccs-<profile> <args>` - Spawns: `droid -m custom:ccs-<profile> <args>`
- Model config includes baseUrl, apiKey, provider - Model config includes baseUrl, apiKey, provider
**Binary alias pattern (busybox-style):** **Runtime alias pattern (argv[0]-style):**
``` ```
ccs → Target: claude (default) ccs → Target: claude (default)
ccsd → Target: droid (auto-selected via argv[0]) ccs-droid → Target: droid (explicit alias)
ccsd → Target: droid (legacy shortcut)
``` ```
For details on the adapter architecture, see [Target Adapters](./target-adapters.md). For details on the adapter architecture, see [Target Adapters](./target-adapters.md).
@@ -392,7 +393,7 @@ See [Provider Flows](./provider-flows.md) → Authentication Flow section.
| |
+---> Creates symlink: ccs --> dist/ccs.js +---> Creates symlink: ccs --> dist/ccs.js
| |
+---> Binary alias: ccsd → ccs (auto-selects droid target) +---> Runtime aliases: ccs-droid / ccsd → ccs (auto-select droid target)
| |
+---> First run creates: ~/.ccs/ +---> First run creates: ~/.ccs/
``` ```
+17 -8
View File
@@ -79,8 +79,9 @@ CCS resolves which adapter to use via priority-ordered checks:
glm: glm:
target: droid target: droid
3. argv[0] detection (busybox pattern) — binary name mapping 3. argv[0] detection (runtime alias pattern) — binary name mapping
└─ ccsd (symlink/batch file) → droid └─ ccs-droid (explicit alias) → droid
└─ ccsd (legacy shortcut) → droid
└─ ccs (regular command) → default └─ ccs (regular command) → default
4. Fallback: 'claude' — lowest priority 4. Fallback: 'claude' — lowest priority
@@ -351,22 +352,30 @@ ccs --target droid glm
(credentials loaded from ~/.factory/settings.json) (credentials loaded from ~/.factory/settings.json)
``` ```
### Binary Alias Pattern ### Runtime Alias Pattern
```bash ```bash
# Create alias/symlink to auto-select droid target # Create aliases/symlinks to auto-select droid target
# Built-in alias: ccsd ln -s /path/to/ccs /path/to/ccs-droid
ln -s /path/to/ccs /path/to/ccsd ln -s /path/to/ccs /path/to/ccsd
# Usage # Usage
ccs-droid glm
→ Target: droid (detected from argv[0])
→ droid -m custom:ccs-glm "args..."
# Legacy shortcut still works
ccsd glm ccsd glm
→ Target: droid (detected from argv[0]) → Target: droid (detected from argv[0])
→ droid -m custom:ccs-glm "args..." → droid -m custom:ccs-glm "args..."
``` ```
On Windows, `ccsd.cmd`, `ccsd.bat`, `ccsd.ps1`, and `ccsd.exe` wrappers are also recognized. On Windows, `ccs-droid.cmd`, `ccsd.cmd`, `ccsd.bat`, `ccsd.ps1`, and `ccsd.exe` wrappers are also recognized.
Additional alias names can be configured at runtime via `CCS_DROID_ALIASES` (comma-separated). Example: `CCS_DROID_ALIASES=ccs-droid,mydroid`. Additional alias names can be configured at runtime via `CCS_TARGET_ALIASES`
(preferred, `target=alias1,alias2;...`) or legacy `CCS_DROID_ALIASES`
(comma-separated). Example:
`CCS_TARGET_ALIASES=droid=mydroid,team-droid`
--- ---
@@ -612,7 +621,7 @@ ccs --target claude help
ccs --target droid help ccs --target droid help
# Test argv[0] detection # Test argv[0] detection
ccsd help ccs-droid help
``` ```
--- ---
+1
View File
@@ -27,6 +27,7 @@
"types": "dist/ccs.d.ts", "types": "dist/ccs.d.ts",
"bin": { "bin": {
"ccs": "dist/ccs.js", "ccs": "dist/ccs.js",
"ccs-droid": "dist/ccs.js",
"ccsd": "dist/ccs.js" "ccsd": "dist/ccs.js"
}, },
"files": [ "files": [
+14 -4
View File
@@ -366,12 +366,18 @@ export async function handleApiCreateCommand(args: string[]): Promise<void> {
` ${color(`ccs ${result.name} "your prompt"`, 'command')} ${dim('# uses droid by default')}` ` ${color(`ccs ${result.name} "your prompt"`, 'command')} ${dim('# uses droid by default')}`
); );
console.log( console.log(
` ${color(`ccsd ${result.name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}` ` ${color(`ccs-droid ${result.name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}`
);
console.log(
` ${color(`ccsd ${result.name} "your prompt"`, 'command')} ${dim('# legacy shortcut')}`
); );
} else { } else {
console.log(` ${color(`ccs ${result.name} "your prompt"`, 'command')}`); console.log(` ${color(`ccs ${result.name} "your prompt"`, 'command')}`);
console.log( console.log(
` ${color(`ccs ${result.name} --target droid "your prompt"`, 'command')} ${dim('# optional target override')}` ` ${color(`ccs-droid ${result.name} "your prompt"`, 'command')} ${dim('# explicit one-off droid alias')}`
);
console.log(
` ${color(`ccs ${result.name} --target droid "your prompt"`, 'command')} ${dim('# target flag alternative')}`
); );
} }
console.log(''); console.log('');
@@ -447,8 +453,9 @@ export async function handleApiCreateCommand(args: string[]): Promise<void> {
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}`
); );
console.log( console.log(
` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}`
); );
console.log(` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# legacy shortcut')}`);
console.log( console.log(
` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}` ` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}`
); );
@@ -457,7 +464,10 @@ export async function handleApiCreateCommand(args: string[]): Promise<void> {
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}`
); );
console.log( console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# run on droid for this call')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit one-off droid alias')}`
);
console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# target flag alternative')}`
); );
} }
console.log(''); console.log('');
+22 -6
View File
@@ -290,7 +290,10 @@ export async function handleCreate(
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}`
); );
console.log( console.log(
` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}`
);
console.log(
` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# legacy shortcut')}`
); );
console.log( console.log(
` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}` ` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}`
@@ -300,7 +303,10 @@ export async function handleCreate(
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}`
); );
console.log( console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# run on droid for this call')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit one-off droid alias')}`
);
console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# target flag alternative')}`
); );
} }
console.log(''); console.log('');
@@ -452,8 +458,9 @@ export async function handleCreate(
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}`
); );
console.log( console.log(
` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}`
); );
console.log(` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# legacy shortcut')}`);
console.log( console.log(
` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}` ` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}`
); );
@@ -462,7 +469,10 @@ export async function handleCreate(
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}`
); );
console.log( console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# run on droid for this call')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit one-off droid alias')}`
);
console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# target flag alternative')}`
); );
} }
console.log(''); console.log('');
@@ -697,7 +707,10 @@ export async function handleEdit(
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses droid by default')}`
); );
console.log( console.log(
` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit droid alias')}`
);
console.log(
` ${color(`ccsd ${name} "your prompt"`, 'command')} ${dim('# legacy shortcut')}`
); );
console.log( console.log(
` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}` ` ${color(`ccs ${name} --target claude "your prompt"`, 'command')} ${dim('# override to Claude')}`
@@ -707,7 +720,10 @@ export async function handleEdit(
` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}` ` ${color(`ccs ${name} "your prompt"`, 'command')} ${dim('# uses claude by default')}`
); );
console.log( console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# run on droid for this call')}` ` ${color(`ccs-droid ${name} "your prompt"`, 'command')} ${dim('# explicit one-off droid alias')}`
);
console.log(
` ${color(`ccs ${name} --target droid "your prompt"`, 'command')} ${dim('# target flag alternative')}`
); );
} }
console.log(''); console.log('');
+9 -7
View File
@@ -355,22 +355,24 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim();
// Aliases // Aliases
printSubSection('Aliases', [ printSubSection('Aliases', [
['ccsd <profile> [args]', 'Shorthand for: ccs <profile> --target droid'], ['ccs-droid <profile> [args]', 'Explicit Droid runtime alias'],
['ccsd <profile> [args]', 'Legacy shortcut for: ccs-droid <profile> [args]'],
]); ]);
// Multi-target examples // Multi-target examples
printSubSection('Multi-Target', [ printSubSection('Multi-Target', [
['ccs glm --target droid', 'Run GLM profile on Droid CLI'], ['ccs glm --target droid', 'Run GLM profile on Droid CLI'],
['ccsd glm', 'Same as above (alias)'], ['ccs-droid glm', 'Same as above (explicit alias)'],
['ccsd codex', 'Run built-in CLIProxy Codex profile on Droid'], ['ccsd glm', 'Legacy shortcut for ccs-droid'],
['ccsd agy', 'Run built-in CLIProxy Antigravity profile on Droid'], ['ccs-droid codex', 'Run built-in CLIProxy Codex profile on Droid'],
['ccs-droid agy', 'Run built-in CLIProxy Antigravity profile on Droid'],
[ [
'ccsd codex exec --skip-permissions-unsafe "fix failing tests"', 'ccs-droid codex exec --skip-permissions-unsafe "fix failing tests"',
'Pass through Droid exec mode', 'Pass through Droid exec mode',
], ],
['ccsd codex -m custom:gpt-5.3-codex "fix failing tests"', 'Auto-routes short exec flags'], ['ccs-droid codex -m custom:gpt-5.3-codex "fix failing tests"', 'Auto-routes short exec flags'],
[ [
'ccsd codex --skip-permissions-unsafe "fix failing tests"', 'ccs-droid codex --skip-permissions-unsafe "fix failing tests"',
'Auto-routes to Droid exec when exec-only flags are detected', 'Auto-routes to Droid exec when exec-only flags are detected',
], ],
[ [
+61 -12
View File
@@ -12,27 +12,76 @@ import * as path from 'path';
import { TargetType } from './target-adapter'; import { TargetType } from './target-adapter';
/** /**
* Map of binary names to target types (busybox pattern). * Built-in argv[0] aliases for explicit runtime entrypoints.
* When CCS is invoked as `ccsd`, it auto-selects the droid target. * `ccs-droid` is the transparent alias; `ccsd` remains as a legacy shortcut.
*/ */
const ARGV0_TARGET_MAP: Record<string, TargetType> = { const BUILTIN_ARGV0_TARGET_MAP: Record<string, TargetType> = {
'ccs-droid': 'droid',
ccsd: 'droid', ccsd: 'droid',
}; };
const ALIAS_NAME_REGEX = /^[a-z0-9._-]+$/; const ALIAS_NAME_REGEX = /^[a-z0-9._-]+$/;
const GENERIC_TARGET_ALIAS_ENV_VAR = 'CCS_TARGET_ALIASES';
const LEGACY_TARGET_ALIAS_ENV_VARS: Partial<Record<TargetType, string>> = {
droid: 'CCS_DROID_ALIASES',
};
function buildArgv0TargetMap(): Record<string, TargetType> { function addAliasToMap(map: Record<string, TargetType>, alias: string, target: TargetType): void {
const map: Record<string, TargetType> = { ...ARGV0_TARGET_MAP }; const normalizedAlias = alias.trim().toLowerCase();
const envAliases = process.env['CCS_DROID_ALIASES']; if (!normalizedAlias || !ALIAS_NAME_REGEX.test(normalizedAlias)) {
if (!envAliases) { return;
return map;
} }
for (const rawAlias of envAliases.split(',')) { map[normalizedAlias] = target;
const alias = rawAlias.trim().toLowerCase(); }
if (!alias || !ALIAS_NAME_REGEX.test(alias)) {
function addAliasListToMap(
map: Record<string, TargetType>,
target: TargetType,
rawAliases: string
): void {
for (const rawAlias of rawAliases.split(',')) {
addAliasToMap(map, rawAlias, target);
}
}
function parseGenericTargetAliasConfig(map: Record<string, TargetType>, rawConfig: string): void {
for (const rawEntry of rawConfig.split(';')) {
const entry = rawEntry.trim();
if (!entry) {
continue; continue;
} }
map[alias] = 'droid';
const separatorIndex = entry.indexOf('=');
if (separatorIndex <= 0 || separatorIndex === entry.length - 1) {
continue;
}
const rawTarget = entry.slice(0, separatorIndex).trim().toLowerCase();
const rawAliases = entry.slice(separatorIndex + 1).trim();
if (!rawAliases || !isValidTarget(rawTarget)) {
continue;
}
addAliasListToMap(map, rawTarget, rawAliases);
}
}
function buildArgv0TargetMap(): Record<string, TargetType> {
const map: Record<string, TargetType> = { ...BUILTIN_ARGV0_TARGET_MAP };
const genericAliasConfig = process.env[GENERIC_TARGET_ALIAS_ENV_VAR];
if (genericAliasConfig) {
parseGenericTargetAliasConfig(map, genericAliasConfig);
}
for (const [target, envVar] of Object.entries(LEGACY_TARGET_ALIAS_ENV_VARS) as Array<
[TargetType, string]
>) {
const rawAliases = process.env[envVar];
if (!rawAliases) {
continue;
}
addAliasListToMap(map, target, rawAliases);
} }
return map; return map;
@@ -62,11 +62,22 @@ describe('ccsd alias integration', () => {
expect(path.basename(argvPath)).toBe('ccsd'); expect(path.basename(argvPath)).toBe('ccsd');
}); });
it('should preserve ccs-droid symlink basename in argv[1] under node', () => {
if (process.platform === 'win32') {
return;
}
const argvPath = probeArgvPath('ccs-droid');
expect(path.basename(argvPath)).toBe('ccs-droid');
});
it('should preserve extension-style alias basenames for wrapper compatibility', () => { it('should preserve extension-style alias basenames for wrapper compatibility', () => {
const cmdArgvPath = probeArgvPathDirect('ccsd.cmd'); const cmdArgvPath = probeArgvPathDirect('ccsd.cmd');
const ps1ArgvPath = probeArgvPathDirect('ccsd.ps1'); const ps1ArgvPath = probeArgvPathDirect('ccsd.ps1');
const explicitCmdArgvPath = probeArgvPathDirect('ccs-droid.cmd');
expect(path.basename(cmdArgvPath)).toBe('ccsd.cmd'); expect(path.basename(cmdArgvPath)).toBe('ccsd.cmd');
expect(path.basename(ps1ArgvPath)).toBe('ccsd.ps1'); expect(path.basename(ps1ArgvPath)).toBe('ccsd.ps1');
expect(path.basename(explicitCmdArgvPath)).toBe('ccs-droid.cmd');
}); });
}); });
@@ -7,6 +7,7 @@ import { resolveTargetType, stripTargetFlag } from '../../../src/targets/target-
describe('resolveTargetType', () => { describe('resolveTargetType', () => {
const originalArgv = process.argv; const originalArgv = process.argv;
const originalDroidAliases = process.env.CCS_DROID_ALIASES; const originalDroidAliases = process.env.CCS_DROID_ALIASES;
const originalTargetAliases = process.env.CCS_TARGET_ALIASES;
afterEach(() => { afterEach(() => {
process.argv = originalArgv; process.argv = originalArgv;
@@ -15,6 +16,12 @@ describe('resolveTargetType', () => {
} else { } else {
process.env.CCS_DROID_ALIASES = originalDroidAliases; process.env.CCS_DROID_ALIASES = originalDroidAliases;
} }
if (originalTargetAliases === undefined) {
delete process.env.CCS_TARGET_ALIASES;
} else {
process.env.CCS_TARGET_ALIASES = originalTargetAliases;
}
}); });
it('should return claude as default', () => { it('should return claude as default', () => {
@@ -52,6 +59,23 @@ describe('resolveTargetType', () => {
expect(resolveTargetType([])).toBe('droid'); expect(resolveTargetType([])).toBe('droid');
}); });
it('should detect built-in ccs-droid argv[0] alias', () => {
process.argv = ['node', 'ccs-droid'];
expect(resolveTargetType([])).toBe('droid');
});
it('should detect custom target aliases from CCS_TARGET_ALIASES', () => {
process.env.CCS_TARGET_ALIASES = 'droid=droidx,my-droid';
process.argv = ['node', 'my-droid'];
expect(resolveTargetType([])).toBe('droid');
});
it('should ignore unsupported targets in CCS_TARGET_ALIASES', () => {
process.env.CCS_TARGET_ALIASES = 'codex=ccsx;droid=ccs-droid-custom';
process.argv = ['node', 'ccsx'];
expect(resolveTargetType([])).toBe('claude');
});
it('should detect custom argv[0] aliases from CCS_DROID_ALIASES', () => { it('should detect custom argv[0] aliases from CCS_DROID_ALIASES', () => {
process.env.CCS_DROID_ALIASES = 'droidx,my-droid'; process.env.CCS_DROID_ALIASES = 'droidx,my-droid';
process.argv = ['node', 'my-droid']; process.argv = ['node', 'my-droid'];
@@ -75,6 +99,11 @@ describe('resolveTargetType', () => {
expect(resolveTargetType([])).toBe('droid'); expect(resolveTargetType([])).toBe('droid');
}); });
it('should strip .cmd extension on built-in explicit alias', () => {
process.argv = ['node', 'ccs-droid.cmd'];
expect(resolveTargetType([])).toBe('droid');
});
it('should strip .bat extension on Windows argv[0]', () => { it('should strip .bat extension on Windows argv[0]', () => {
process.argv = ['node', 'ccsd.bat']; process.argv = ['node', 'ccsd.bat'];
expect(resolveTargetType([])).toBe('droid'); expect(resolveTargetType([])).toBe('droid');
@@ -95,6 +124,11 @@ describe('resolveTargetType', () => {
expect(resolveTargetType([])).toBe('droid'); expect(resolveTargetType([])).toBe('droid');
}); });
it('should handle full path argv[0] for ccs-droid', () => {
process.argv = ['node', '/usr/local/bin/ccs-droid'];
expect(resolveTargetType([])).toBe('droid');
});
it('should prioritize --target over argv[0]', () => { it('should prioritize --target over argv[0]', () => {
process.argv = ['node', 'ccsd']; process.argv = ['node', 'ccsd'];
expect(resolveTargetType(['--target', 'claude'])).toBe('claude'); expect(resolveTargetType(['--target', 'claude'])).toBe('claude');
@@ -90,11 +90,7 @@ export function ProviderInfoTab({
<UsageCommand label="Run with prompt" command={`ccs ${provider} "your prompt"`} /> <UsageCommand label="Run with prompt" command={`ccs ${provider} "your prompt"`} />
<UsageCommand <UsageCommand
label={isDroidTarget ? 'Droid alias (explicit)' : 'Run on Droid'} label={isDroidTarget ? 'Droid alias (explicit)' : 'Run on Droid'}
command={ command={`ccs-droid ${provider} "your prompt"`}
isDroidTarget
? `ccsd ${provider} "your prompt"`
: `ccs ${provider} --target droid "your prompt"`
}
/> />
<UsageCommand <UsageCommand
label={isDroidTarget ? 'Override to Claude' : 'Override target'} label={isDroidTarget ? 'Override to Claude' : 'Override target'}
@@ -90,16 +90,10 @@ export function InfoSection({ profileName, target, data }: InfoSectionProps) {
</Label> </Label>
<div className="mt-1 flex gap-2"> <div className="mt-1 flex gap-2">
<code className="flex-1 px-2 py-1.5 bg-muted rounded text-xs font-mono truncate"> <code className="flex-1 px-2 py-1.5 bg-muted rounded text-xs font-mono truncate">
{isDroidTarget {`ccs-droid ${profileName} "prompt"`}
? `ccsd ${profileName} "prompt"`
: `ccs ${profileName} --target droid "prompt"`}
</code> </code>
<CopyButton <CopyButton
value={ value={`ccs-droid ${profileName} "prompt"`}
isDroidTarget
? `ccsd ${profileName} "prompt"`
: `ccs ${profileName} --target droid "prompt"`
}
size="icon" size="icon"
className="h-6 w-6" className="h-6 w-6"
/> />
@@ -524,11 +524,25 @@ export function ProfileCreateDialog({
</SelectContent> </SelectContent>
</Select> </Select>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
Run with{' '} {targetValue === 'droid' ? (
<code className="bg-muted px-1 rounded text-[10px]"> <>
{targetValue === 'droid' ? 'ccsd' : 'ccs'} Preferred explicit alias:{' '}
</code>{' '} <code className="bg-muted px-1 rounded text-[10px]">ccs-droid</code>.
by default. You can still override each run with{' '} </>
) : (
<>
Run with <code className="bg-muted px-1 rounded text-[10px]">ccs</code> by
default.
</>
)}
{targetValue === 'droid' ? (
<>
{' '}
Legacy <code className="bg-muted px-1 rounded text-[10px]">ccsd</code> still
works for Droid.
</>
) : null}{' '}
You can still override each run with{' '}
<code className="bg-muted px-1 rounded text-[10px]">--target</code>. <code className="bg-muted px-1 rounded text-[10px]">--target</code>.
</p> </p>
</div> </div>
+3 -6
View File
@@ -1451,10 +1451,8 @@ const resources = {
openrouterModelsBadge: '{{modelCountLabel}} 个 OpenRouter 模型', openrouterModelsBadge: '{{modelCountLabel}} 个 OpenRouter 模型',
runtimeProviderBadge: '运行时提供商设置', runtimeProviderBadge: '运行时提供商设置',
runtimeProviderTitle: '打开 AI Providers', runtimeProviderTitle: '打开 AI Providers',
runtimeProviderDescription: runtimeProviderDescription: '先管理共享的提供商密钥和运行时连接器,再创建独立的 API 配置。',
'先管理共享的提供商密钥和运行时连接器,再创建独立的 API 配置。', runtimeProviderFeatureConnectors: 'Gemini、Codex、Claude、Vertex 以及 OpenAI 兼容连接器',
runtimeProviderFeatureConnectors:
'Gemini、Codex、Claude、Vertex 以及 OpenAI 兼容连接器',
runtimeProviderFeatureSecrets: '共享密钥与单个 API 配置分离存放', runtimeProviderFeatureSecrets: '共享密钥与单个 API 配置分离存放',
runtimeProviderFooter: '当多个配置需要共享同一提供商设置时最适合使用。', runtimeProviderFooter: '当多个配置需要共享同一提供商设置时最适合使用。',
createOpenRouterProfile: '创建 OpenRouter 配置', createOpenRouterProfile: '创建 OpenRouter 配置',
@@ -3837,8 +3835,7 @@ const resources = {
runtimeProviderTitle: 'AI Providers を開く', runtimeProviderTitle: 'AI Providers を開く',
runtimeProviderDescription: runtimeProviderDescription:
'個別の API プロファイルを作成する前に、共有プロバイダーキーとランタイムコネクタを管理します。', '個別の API プロファイルを作成する前に、共有プロバイダーキーとランタイムコネクタを管理します。',
runtimeProviderFeatureConnectors: runtimeProviderFeatureConnectors: 'Gemini、Codex、Claude、Vertex、OpenAI 互換コネクタ',
'Gemini、Codex、Claude、Vertex、OpenAI 互換コネクタ',
runtimeProviderFeatureSecrets: runtimeProviderFeatureSecrets:
'共有シークレットを個別の API プロファイルから切り離して管理', '共有シークレットを個別の API プロファイルから切り離して管理',
runtimeProviderFooter: runtimeProviderFooter:
+8 -8
View File
@@ -68,7 +68,7 @@ export const SUPPORT_NOTICES: SupportNotice[] = [
highlights: [ highlights: [
'Set default target to Droid when creating or editing API Profiles.', 'Set default target to Droid when creating or editing API Profiles.',
'Set default target to Droid for CLIProxy variants, including Codex and Antigravity flows.', 'Set default target to Droid for CLIProxy variants, including Codex and Antigravity flows.',
'Use ccsd alias or --target droid for one-off target overrides.', 'Use ccs-droid as the explicit alias, with ccsd kept as the legacy shortcut.',
], ],
actions: [ actions: [
{ {
@@ -88,11 +88,11 @@ export const SUPPORT_NOTICES: SupportNotice[] = [
path: '/cliproxy', path: '/cliproxy',
}, },
{ {
id: 'copy-ccsd-command', id: 'copy-ccs-droid-command',
label: 'Run once with Droid alias', label: 'Run once with explicit Droid alias',
description: 'Use ccsd to force Droid target with your current profile.', description: 'Use ccs-droid to force the Droid target with your current profile.',
type: 'command', type: 'command',
command: 'ccsd glm', command: 'ccs-droid glm',
}, },
{ {
id: 'copy-target-override', id: 'copy-target-override',
@@ -107,7 +107,7 @@ export const SUPPORT_NOTICES: SupportNotice[] = [
{ label: 'CLIProxy', path: '/cliproxy' }, { label: 'CLIProxy', path: '/cliproxy' },
], ],
commands: [ commands: [
'ccsd glm', 'ccs-droid glm',
'ccs codex --target droid "your prompt"', 'ccs codex --target droid "your prompt"',
'ccs cliproxy create mycodex --provider codex --target droid', 'ccs cliproxy create mycodex --provider codex --target droid',
], ],
@@ -182,8 +182,8 @@ export const CLI_SUPPORT_ENTRIES: CliSupportEntry[] = [
{ label: 'API Profiles', path: '/providers' }, { label: 'API Profiles', path: '/providers' },
{ label: 'CLIProxy', path: '/cliproxy' }, { label: 'CLIProxy', path: '/cliproxy' },
], ],
commands: ['ccsd glm', 'ccs km --target droid', 'ccs codex --target droid'], commands: ['ccs-droid glm', 'ccs km --target droid', 'ccs codex --target droid'],
notes: 'Use ccsd alias for automatic Droid target selection.', notes: 'Use ccs-droid as the explicit runtime alias. Legacy ccsd still works.',
}, },
{ {
id: 'codex-cliproxy', id: 'codex-cliproxy',