mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 18:15:48 +00:00
feat: add explicit droid runtime alias surface
This commit is contained in:
1 parent
19c1e1da4f
commit
160be319d0
17 files changed
+229
-85
No files matched your search
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
↓
|
↓
|
||||||
|
|||||||
@@ -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:**
|
||||||
|
|||||||
@@ -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/
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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('');
|
||||||
|
|||||||
@@ -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('');
|
||||||
|
|||||||
@@ -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',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in new issue
Block a user