feat(cliproxy): add explicit start and restart commands

This commit is contained in:
Tam Nhu Tran committed 2026-02-14 09:41:40 +07:00
1 parent 2fe8f2b4ca
commit 8395364970
7 files changed
+118 -4

No files matched your search

+9
View File
@@ -229,6 +229,15 @@ ccs cliproxy doctor # Check quota status for all agy accounts
**Auto-Failover**: When an Antigravity account runs out of quota, CCS automatically switches to another account with remaining capacity. Shared GCP project accounts are excluded (pooled quota). **Auto-Failover**: When an Antigravity account runs out of quota, CCS automatically switches to another account with remaining capacity. Shared GCP project accounts are excluded (pooled quota).
### CLIProxy Lifecycle
```bash
ccs cliproxy start # Start CLIProxy background service
ccs cliproxy status # Check running status
ccs cliproxy restart # Restart CLIProxy service
ccs cliproxy stop # Stop running CLIProxy service
```
<br> <br>
## Configuration ## Configuration
+2
View File
@@ -22,10 +22,12 @@ export {
export { export {
getProxyStatus, getProxyStatus,
stopProxy, stopProxy,
startProxy,
isProxyRunning, isProxyRunning,
getActiveSessionCount, getActiveSessionCount,
type ProxyStatusResult, type ProxyStatusResult,
type StopProxyResult, type StopProxyResult,
type StartProxyResult,
} from './proxy-lifecycle-service'; } from './proxy-lifecycle-service';
// Binary management // Binary management
@@ -2,13 +2,15 @@
* CLIProxy Proxy Lifecycle Service * CLIProxy Proxy Lifecycle Service
* *
* Handles start/stop/status operations for CLIProxy instances. * Handles start/stop/status operations for CLIProxy instances.
* Delegates to session-tracker for actual process management. * Delegates to session-tracker and service-manager for actual process management.
*/ */
import { import {
stopProxy as stopProxySession, stopProxy as stopProxySession,
getProxyStatus as getProxyStatusSession, getProxyStatus as getProxyStatusSession,
} from '../session-tracker'; } from '../session-tracker';
import { ensureCliproxyService } from '../service-manager';
import { CLIPROXY_DEFAULT_PORT } from '../config-generator';
/** Proxy status result */ /** Proxy status result */
export interface ProxyStatusResult { export interface ProxyStatusResult {
@@ -27,6 +29,15 @@ export interface StopProxyResult {
error?: string; error?: string;
} }
/** Start proxy result */
export interface StartProxyResult {
started: boolean;
alreadyRunning: boolean;
port: number;
configRegenerated?: boolean;
error?: string;
}
/** /**
* Get current proxy status * Get current proxy status
*/ */
@@ -41,6 +52,16 @@ export async function stopProxy(): Promise<StopProxyResult> {
return stopProxySession(); return stopProxySession();
} }
/**
* Start CLIProxy service (or reuse existing running instance)
*/
export async function startProxy(
port: number = CLIPROXY_DEFAULT_PORT,
verbose: boolean = false
): Promise<StartProxyResult> {
return ensureCliproxyService(port, verbose);
}
/** /**
* Check if proxy is currently running * Check if proxy is currently running
*/ */
+2
View File
@@ -61,6 +61,8 @@ export async function showHelp(): Promise<void> {
[ [
'Proxy Lifecycle:', 'Proxy Lifecycle:',
[ [
['start', 'Start CLIProxy instance in background'],
['restart', 'Restart CLIProxy instance'],
['status', 'Show running CLIProxy status'], ['status', 'Show running CLIProxy status'],
['stop', 'Stop running CLIProxy instance'], ['stop', 'Stop running CLIProxy instance'],
['doctor', 'Quota diagnostics and shared project detection'], ['doctor', 'Quota diagnostics and shared project detection'],
+16 -1
View File
@@ -20,7 +20,12 @@ import {
handleResumeAccount, handleResumeAccount,
} from './quota-subcommand'; } from './quota-subcommand';
import { handleCreate, handleRemove, handleEdit } from './variant-subcommand'; import { handleCreate, handleRemove, handleEdit } from './variant-subcommand';
import { handleProxyStatus, handleStop } from './proxy-lifecycle-subcommand'; import {
handleProxyStatus,
handleStart,
handleStop,
handleRestart,
} from './proxy-lifecycle-subcommand';
import { showStatus, handleInstallVersion, handleInstallLatest } from './install-subcommand'; import { showStatus, handleInstallVersion, handleInstallLatest } from './install-subcommand';
import { showHelp } from './help-subcommand'; import { showHelp } from './help-subcommand';
import { import {
@@ -198,11 +203,21 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
} }
// Proxy lifecycle commands // Proxy lifecycle commands
if (command === 'start') {
await handleStart(verbose);
return;
}
if (command === 'stop') { if (command === 'stop') {
await handleStop(); await handleStop();
return; return;
} }
if (command === 'restart') {
await handleRestart(verbose);
return;
}
if (command === 'status') { if (command === 'status') {
await handleProxyStatus(); await handleProxyStatus();
return; return;
@@ -2,15 +2,68 @@
* CLIProxy Lifecycle Management * CLIProxy Lifecycle Management
* *
* Handles: * Handles:
* - ccs cliproxy start
* - ccs cliproxy restart
* - ccs cliproxy status * - ccs cliproxy status
* - ccs cliproxy stop * - ccs cliproxy stop
*/ */
import { initUI, header, color, dim, ok, warn, info } from '../../utils/ui'; import { initUI, header, color, dim, ok, warn, info } from '../../utils/ui';
import { getProxyStatus, stopProxy } from '../../cliproxy/services'; import { getProxyStatus, startProxy, stopProxy } from '../../cliproxy/services';
import { detectRunningProxy } from '../../cliproxy/proxy-detector'; import { detectRunningProxy } from '../../cliproxy/proxy-detector';
import { CLIPROXY_DEFAULT_PORT } from '../../cliproxy/config-generator'; import { CLIPROXY_DEFAULT_PORT } from '../../cliproxy/config-generator';
export async function handleStart(verbose = false): Promise<void> {
await initUI();
console.log(header('Start CLIProxy'));
console.log('');
const result = await startProxy(CLIPROXY_DEFAULT_PORT, verbose);
if (result.started) {
if (result.alreadyRunning) {
console.log(info(`CLIProxy already running on port ${result.port}`));
if (result.configRegenerated) {
console.log(warn('Config updated - restart CLIProxy to apply changes'));
}
} else {
console.log(ok(`CLIProxy started on port ${result.port}`));
}
console.log(dim('To stop: ccs cliproxy stop'));
} else {
console.log(warn(result.error || 'Failed to start CLIProxy'));
}
console.log('');
}
export async function handleRestart(verbose = false): Promise<void> {
await initUI();
console.log(header('Restart CLIProxy'));
console.log('');
const stopResult = await stopProxy();
if (stopResult.stopped) {
console.log(ok(`CLIProxy stopped (PID ${stopResult.pid})`));
} else if (stopResult.error === 'No active CLIProxy session found') {
console.log(info('No active CLIProxy session found, starting a new instance'));
} else {
console.log(warn(stopResult.error || 'Failed to stop existing CLIProxy'));
console.log(info('Attempting to start a fresh instance...'));
}
const startResult = await startProxy(CLIPROXY_DEFAULT_PORT, verbose);
if (startResult.started) {
if (startResult.alreadyRunning) {
console.log(info(`CLIProxy already running on port ${startResult.port}`));
} else {
console.log(ok(`CLIProxy started on port ${startResult.port}`));
}
} else {
console.log(warn(startResult.error || 'Failed to restart CLIProxy'));
}
console.log('');
}
export async function handleProxyStatus(): Promise<void> { export async function handleProxyStatus(): Promise<void> {
await initUI(); await initUI();
console.log(header('CLIProxy Status')); console.log(header('CLIProxy Status'));
+13 -1
View File
@@ -491,6 +491,18 @@ describe('CLIProxy Command - Proxy Lifecycle', () => {
}); });
describe('Command Routing', () => { describe('Command Routing', () => {
it('routes "start" subcommand correctly', () => {
const args = ['cliproxy', 'start'];
const subcommand = args[1];
assert.strictEqual(subcommand, 'start');
});
it('routes "restart" subcommand correctly', () => {
const args = ['cliproxy', 'restart'];
const subcommand = args[1];
assert.strictEqual(subcommand, 'restart');
});
it('routes "stop" subcommand correctly', () => { it('routes "stop" subcommand correctly', () => {
const args = ['cliproxy', 'stop']; const args = ['cliproxy', 'stop'];
const subcommand = args[1]; const subcommand = args[1];
@@ -504,7 +516,7 @@ describe('CLIProxy Command - Proxy Lifecycle', () => {
}); });
it('handles unknown subcommand', () => { it('handles unknown subcommand', () => {
const validSubcommands = ['stop', 'status', 'create', 'list', 'remove']; const validSubcommands = ['start', 'restart', 'stop', 'status', 'create', 'list', 'remove'];
const unknownCommand = 'invalid'; const unknownCommand = 'invalid';
assert.strictEqual(validSubcommands.includes(unknownCommand), false); assert.strictEqual(validSubcommands.includes(unknownCommand), false);
}); });