mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 20:17:45 +00:00
150 lines
5.2 KiB
TypeScript
150 lines
5.2 KiB
TypeScript
/**
|
|
* Create Command Handler
|
|
*
|
|
* Creates a new profile and prompts for login in an isolated Claude instance.
|
|
*/
|
|
|
|
import { spawn, ChildProcess } from 'child_process';
|
|
import { initUI, header, color, fail, warn, info, infoBox, warnBox } from '../../utils/ui';
|
|
import { getClaudeCliInfo } from '../../utils/claude-detector';
|
|
import { escapeShellArg, stripClaudeCodeEnv } from '../../utils/shell-executor';
|
|
import { isUnifiedMode } from '../../config/unified-config-loader';
|
|
import { exitWithError } from '../../errors';
|
|
import { ExitCode } from '../../errors/exit-codes';
|
|
import { CommandContext, parseArgs } from './types';
|
|
|
|
/**
|
|
* Handle the create command
|
|
*/
|
|
export async function handleCreate(ctx: CommandContext, args: string[]): Promise<void> {
|
|
await initUI();
|
|
const { profileName, force } = parseArgs(args);
|
|
|
|
if (!profileName) {
|
|
console.log(fail('Profile name is required'));
|
|
console.log('');
|
|
console.log(`Usage: ${color('ccs auth create <profile> [--force]', 'command')}`);
|
|
console.log('');
|
|
console.log('Example:');
|
|
console.log(` ${color('ccs auth create work', 'command')}`);
|
|
exitWithError('Profile name is required', ExitCode.PROFILE_ERROR);
|
|
}
|
|
|
|
// Check if profile already exists (check both legacy and unified)
|
|
const existsLegacy = ctx.registry.hasProfile(profileName);
|
|
const existsUnified = ctx.registry.hasAccountUnified(profileName);
|
|
if (!force && (existsLegacy || existsUnified)) {
|
|
console.log(fail(`Profile already exists: ${profileName}`));
|
|
console.log(` Use ${color('--force', 'command')} to overwrite`);
|
|
exitWithError(`Profile already exists: ${profileName}`, ExitCode.PROFILE_ERROR);
|
|
}
|
|
|
|
try {
|
|
// Create instance directory
|
|
console.log(info(`Creating profile: ${profileName}`));
|
|
const instancePath = await ctx.instanceMgr.ensureInstance(profileName);
|
|
|
|
// Create/update profile entry based on config mode
|
|
if (isUnifiedMode()) {
|
|
// Use unified config (config.yaml)
|
|
if (existsUnified) {
|
|
ctx.registry.touchAccountUnified(profileName);
|
|
} else {
|
|
ctx.registry.createAccountUnified(profileName);
|
|
}
|
|
} else {
|
|
// Use legacy profiles.json
|
|
if (existsLegacy) {
|
|
ctx.registry.updateProfile(profileName, {
|
|
type: 'account',
|
|
});
|
|
} else {
|
|
ctx.registry.createProfile(profileName, {
|
|
type: 'account',
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log(info(`Instance directory: ${instancePath}`));
|
|
console.log('');
|
|
console.log(warn('Starting Claude in isolated instance...'));
|
|
console.log(warn('You will be prompted to login with your account.'));
|
|
console.log('');
|
|
|
|
// Detect Claude CLI
|
|
const claudeInfo = getClaudeCliInfo();
|
|
if (!claudeInfo) {
|
|
console.log(fail('Claude CLI not found'));
|
|
console.log('');
|
|
console.log('Please install Claude CLI first:');
|
|
console.log(` ${color('https://claude.ai/download', 'path')}`);
|
|
exitWithError('Claude CLI not found', ExitCode.BINARY_ERROR);
|
|
}
|
|
|
|
const { path: claudeCli, needsShell } = claudeInfo;
|
|
const childEnv = stripClaudeCodeEnv({ ...process.env, CLAUDE_CONFIG_DIR: instancePath });
|
|
|
|
// Execute Claude in isolated instance (will auto-prompt for login if no credentials)
|
|
// On Windows, .cmd/.bat/.ps1 files need shell: true to execute properly
|
|
let child: ChildProcess;
|
|
if (needsShell) {
|
|
const cmdString = escapeShellArg(claudeCli);
|
|
child = spawn(cmdString, {
|
|
stdio: 'inherit',
|
|
windowsHide: true,
|
|
shell: true,
|
|
env: childEnv,
|
|
});
|
|
} else {
|
|
child = spawn(claudeCli, [], {
|
|
stdio: 'inherit',
|
|
windowsHide: true,
|
|
env: childEnv,
|
|
});
|
|
}
|
|
|
|
child.on('exit', (code: number | null) => {
|
|
if (code === 0) {
|
|
console.log('');
|
|
console.log(
|
|
infoBox(
|
|
`Profile: ${profileName}\n` + `Instance: ${instancePath}\n` + `Type: account`,
|
|
'Profile Created'
|
|
)
|
|
);
|
|
console.log('');
|
|
console.log(header('Usage'));
|
|
console.log(` ${color(`ccs ${profileName} "your prompt here"`, 'command')}`);
|
|
console.log('');
|
|
console.log(
|
|
warnBox(
|
|
`Running the command below will SWITCH your default\n` +
|
|
`CCS account to "${profileName}". After this, running\n` +
|
|
`"ccs" without a profile name will use this account.\n\n` +
|
|
` ${color(`ccs auth default ${profileName}`, 'command')}\n\n` +
|
|
`To restore the original default, run:\n` +
|
|
` ${color('ccs auth reset-default', 'command')}`,
|
|
'Set as Default?'
|
|
)
|
|
);
|
|
console.log('');
|
|
process.exit(0);
|
|
} else {
|
|
console.log('');
|
|
console.log(fail('Login failed or cancelled'));
|
|
console.log('');
|
|
console.log('To retry:');
|
|
console.log(` ${color(`ccs auth create ${profileName} --force`, 'command')}`);
|
|
console.log('');
|
|
exitWithError('Login failed or cancelled', ExitCode.AUTH_ERROR);
|
|
}
|
|
});
|
|
|
|
child.on('error', (err: Error) => {
|
|
exitWithError(`Failed to execute Claude CLI: ${err.message}`, ExitCode.BINARY_ERROR);
|
|
});
|
|
} catch (error) {
|
|
exitWithError(`Failed to create profile: ${(error as Error).message}`, ExitCode.GENERAL_ERROR);
|
|
}
|
|
}
|