mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 10:15:40 +00:00
refactor: enhance TypeScript types and improve delegation system
This commit is contained in:
1 parent
0139333146
commit
7a413a81b7
16 files changed
+94
-62
No files matched your search
@@ -352,7 +352,7 @@ class AuthCommands {
|
||||
const files = fs.readdirSync(sessionsDir);
|
||||
sessionCount = files.filter((f) => f.endsWith('.json')).length;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// Ignore errors counting sessions
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ class AuthCommands {
|
||||
const files = fs.readdirSync(sessionsDir);
|
||||
sessionCount = files.filter((f) => f.endsWith('.json')).length;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// Ignore errors counting sessions
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { findSimilarStrings } from '../utils/helpers';
|
||||
import { Config } from '../types';
|
||||
import { Config, Settings, ProfileMetadata } from '../types';
|
||||
|
||||
export type ProfileType = 'settings' | 'account' | 'default';
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface ProfileDetectionResult {
|
||||
type: ProfileType;
|
||||
name: string;
|
||||
settingsPath?: string;
|
||||
profile?: any;
|
||||
profile?: Settings | ProfileMetadata;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class ProfileDetector {
|
||||
/**
|
||||
* Read account-based profiles (profiles.json)
|
||||
*/
|
||||
private readProfiles(): { profiles: Record<string, any>; default?: string } {
|
||||
private readProfiles(): { profiles: Record<string, ProfileMetadata>; default?: string } {
|
||||
if (!fs.existsSync(this.profilesPath)) {
|
||||
return { profiles: {}, default: undefined };
|
||||
}
|
||||
|
||||
@@ -7,6 +7,35 @@ import * as fs from 'fs';
|
||||
import { SessionManager } from './session-manager';
|
||||
import { SettingsParser } from './settings-parser';
|
||||
|
||||
// Type definitions for delegation responses
|
||||
interface ClaudeMessage {
|
||||
type: string;
|
||||
content?: string;
|
||||
thinking?: string;
|
||||
tool_use?: {
|
||||
id: string;
|
||||
name: string;
|
||||
input: Record<string, unknown>;
|
||||
};
|
||||
tool_result?: {
|
||||
tool_use_id: string;
|
||||
content: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface PermissionDenial {
|
||||
tool_name: string;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
interface ExecutionError {
|
||||
message?: string;
|
||||
error?: string;
|
||||
type?: string;
|
||||
tool_name?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface ExecutionOptions {
|
||||
cwd?: string;
|
||||
timeout?: number;
|
||||
@@ -26,7 +55,7 @@ interface ExecutionResult {
|
||||
duration: number;
|
||||
timedOut: boolean;
|
||||
success: boolean;
|
||||
messages: any[];
|
||||
messages: ClaudeMessage[];
|
||||
sessionId?: string;
|
||||
totalCost?: number;
|
||||
numTurns?: number;
|
||||
@@ -34,8 +63,8 @@ interface ExecutionResult {
|
||||
type?: string | null;
|
||||
subtype?: string;
|
||||
durationApi?: number;
|
||||
permissionDenials?: any[];
|
||||
errors?: any[];
|
||||
permissionDenials?: PermissionDenial[];
|
||||
errors?: ExecutionError[];
|
||||
content?: string;
|
||||
}
|
||||
|
||||
@@ -45,7 +74,7 @@ interface StreamMessage {
|
||||
content?: Array<{
|
||||
type: string;
|
||||
name?: string;
|
||||
input?: any;
|
||||
input?: Record<string, unknown>;
|
||||
}>;
|
||||
};
|
||||
session_id?: string;
|
||||
@@ -54,8 +83,8 @@ interface StreamMessage {
|
||||
is_error?: boolean;
|
||||
result?: string;
|
||||
duration_api_ms?: number;
|
||||
permission_denials?: any[];
|
||||
errors?: any[];
|
||||
permission_denials?: PermissionDenial[];
|
||||
errors?: ExecutionError[];
|
||||
subtype?: string;
|
||||
}
|
||||
|
||||
@@ -282,10 +311,8 @@ export class HeadlessExecutor {
|
||||
case 'Bash':
|
||||
if (toolInput.command) {
|
||||
// Truncate long commands
|
||||
const cmd =
|
||||
toolInput.command.length > 80
|
||||
? toolInput.command.substring(0, 77) + '...'
|
||||
: toolInput.command;
|
||||
const command = toolInput.command as string;
|
||||
const cmd = command.length > 80 ? command.substring(0, 77) + '...' : command;
|
||||
verboseMsg += `: ${cmd}`;
|
||||
}
|
||||
break;
|
||||
@@ -330,10 +357,9 @@ export class HeadlessExecutor {
|
||||
if (toolInput.description) {
|
||||
verboseMsg += `: ${toolInput.description}`;
|
||||
} else if (toolInput.prompt) {
|
||||
const promptText = toolInput.prompt as string;
|
||||
const prompt =
|
||||
toolInput.prompt.length > 60
|
||||
? toolInput.prompt.substring(0, 57) + '...'
|
||||
: toolInput.prompt;
|
||||
promptText.length > 60 ? promptText.substring(0, 57) + '...' : promptText;
|
||||
verboseMsg += `: ${prompt}`;
|
||||
}
|
||||
break;
|
||||
@@ -342,7 +368,7 @@ export class HeadlessExecutor {
|
||||
if (toolInput.todos && Array.isArray(toolInput.todos)) {
|
||||
// Show in_progress task instead of just count
|
||||
const inProgressTask = toolInput.todos.find(
|
||||
(t: any) => t.status === 'in_progress'
|
||||
(t: { status: string; activeForm?: string }) => t.status === 'in_progress'
|
||||
);
|
||||
if (inProgressTask && inProgressTask.activeForm) {
|
||||
verboseMsg += `: ${inProgressTask.activeForm}`;
|
||||
@@ -564,7 +590,7 @@ export class HeadlessExecutor {
|
||||
try {
|
||||
const result = execSync('command -v claude', { encoding: 'utf8' });
|
||||
return result.trim();
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -683,7 +709,7 @@ export class HeadlessExecutor {
|
||||
timeout: 10000,
|
||||
});
|
||||
return result.success;
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,14 +33,14 @@ interface PermissionDenial {
|
||||
tool_input?: {
|
||||
command?: string;
|
||||
description?: string;
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
interface ErrorInfo {
|
||||
message?: string;
|
||||
error?: string;
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface FileChanges {
|
||||
@@ -223,7 +223,7 @@ class ResultFormatter {
|
||||
modified.push(fullPath);
|
||||
}
|
||||
}
|
||||
} catch (statError) {
|
||||
} catch (_statError) {
|
||||
// If stat fails, default to created (since we're in fallback mode)
|
||||
if (!created.includes(fullPath) && !modified.includes(fullPath)) {
|
||||
created.push(fullPath);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
interface ThinkingConfig {
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DeltaAccumulatorOptions {
|
||||
|
||||
@@ -106,7 +106,7 @@ export class GlmtProxy {
|
||||
|
||||
// Bind to 127.0.0.1:0 (random port for security + avoid conflicts)
|
||||
this.server.listen(0, '127.0.0.1', () => {
|
||||
const address = this.server!.address();
|
||||
const address = this.server?.address();
|
||||
this.port = typeof address === 'object' && address ? address.port : 0;
|
||||
// Signal parent process
|
||||
console.log(`PROXY_READY:${this.port}`);
|
||||
@@ -446,7 +446,9 @@ export class GlmtProxy {
|
||||
}
|
||||
|
||||
const parser = new SSEParser();
|
||||
const accumulator = new DeltaAccumulator(thinkingConfig);
|
||||
const accumulator = new DeltaAccumulator(
|
||||
thinkingConfig as unknown as Record<string, unknown>
|
||||
);
|
||||
|
||||
upstreamRes.on('data', (chunk: Buffer) => {
|
||||
try {
|
||||
@@ -454,7 +456,8 @@ export class GlmtProxy {
|
||||
|
||||
events.forEach((event) => {
|
||||
// Transform OpenAI delta → Anthropic events
|
||||
const anthropicEvents = this.transformer.transformDelta(event, accumulator);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const anthropicEvents = this.transformer.transformDelta(event as any, accumulator);
|
||||
|
||||
// Forward to Claude CLI with immediate flush
|
||||
anthropicEvents.forEach((evt) => {
|
||||
|
||||
@@ -796,9 +796,10 @@ export class GlmtTransformer {
|
||||
}
|
||||
|
||||
accumulator.addDelta(delta.reasoning_content);
|
||||
events.push(
|
||||
this.createThinkingDeltaEvent(accumulator.getCurrentBlock()!, delta.reasoning_content)
|
||||
);
|
||||
const currentThinkingBlock = accumulator.getCurrentBlock();
|
||||
if (currentThinkingBlock) {
|
||||
events.push(this.createThinkingDeltaEvent(currentThinkingBlock, delta.reasoning_content));
|
||||
}
|
||||
}
|
||||
|
||||
// Text content delta
|
||||
@@ -822,7 +823,10 @@ export class GlmtTransformer {
|
||||
}
|
||||
|
||||
accumulator.addDelta(delta.content);
|
||||
events.push(this.createTextDeltaEvent(accumulator.getCurrentBlock()!, delta.content));
|
||||
const currentTextBlock = accumulator.getCurrentBlock();
|
||||
if (currentTextBlock) {
|
||||
events.push(this.createTextDeltaEvent(currentTextBlock, delta.content));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for planning loop
|
||||
|
||||
@@ -23,7 +23,7 @@ interface SSEParserOptions {
|
||||
|
||||
interface SSEEvent {
|
||||
event: string;
|
||||
data: any;
|
||||
data: unknown;
|
||||
index?: number;
|
||||
id?: string;
|
||||
retry?: number;
|
||||
|
||||
@@ -26,7 +26,7 @@ let ora: (text: string) => Spinner;
|
||||
try {
|
||||
const oraModule = require('ora');
|
||||
ora = oraModule.default || oraModule;
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// ora not available, create fallback spinner that uses console.log
|
||||
ora = function (text: string): Spinner {
|
||||
return {
|
||||
@@ -211,7 +211,7 @@ class Doctor {
|
||||
status: 'OK',
|
||||
info: `v${version} (${claudeCli})`,
|
||||
});
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
spinner.fail(
|
||||
` ${'Claude CLI'.padEnd(26)}${colored('[X]', 'red')} Not found or not working`
|
||||
);
|
||||
@@ -537,7 +537,7 @@ class Doctor {
|
||||
status: 'OK',
|
||||
info: 'Write access verified',
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
spinner.fail(
|
||||
` ${'Permissions'.padEnd(26)}${colored('[X]', 'red')} Cannot write to ~/.ccs/`
|
||||
);
|
||||
@@ -696,7 +696,7 @@ class Doctor {
|
||||
if (resolved !== sharedSettings) {
|
||||
broken++;
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
broken++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class RecoveryManager {
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
JSON.parse(content); // Validate JSON
|
||||
return false; // No recovery needed
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// Corrupted - backup and recreate
|
||||
const backupPath = `${configPath}.backup.${Date.now()}`;
|
||||
fs.renameSync(configPath, backupPath);
|
||||
|
||||
@@ -64,7 +64,7 @@ class SharedManager {
|
||||
console.log(`[!] Circular symlink detected: ${target} → ${resolvedTarget}`);
|
||||
return true;
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// If can't read, assume not circular
|
||||
return false;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class SharedManager {
|
||||
continue; // Already correct
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// Continue to recreate
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class SharedManager {
|
||||
try {
|
||||
const symlinkType = item.type === 'directory' ? 'dir' : 'file';
|
||||
fs.symlinkSync(claudePath, sharedPath, symlinkType);
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// Windows fallback: copy
|
||||
if (process.platform === 'win32') {
|
||||
if (item.type === 'directory') {
|
||||
@@ -148,7 +148,7 @@ class SharedManager {
|
||||
`[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)`
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
throw _err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ class SharedManager {
|
||||
try {
|
||||
const symlinkType = item.type === 'directory' ? 'dir' : 'file';
|
||||
fs.symlinkSync(targetPath, linkPath, symlinkType);
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// Windows fallback
|
||||
if (process.platform === 'win32') {
|
||||
if (item.type === 'directory') {
|
||||
@@ -189,7 +189,7 @@ class SharedManager {
|
||||
`[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)`
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
throw _err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ class SharedManager {
|
||||
if (fs.lstatSync(commandsPath).isSymbolicLink()) {
|
||||
return; // Already migrated
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// Continue with migration
|
||||
}
|
||||
}
|
||||
@@ -268,8 +268,8 @@ class SharedManager {
|
||||
console.log(`[OK] Migrated ${item.name} to ~/.claude/${item.name}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`[!] Failed to migrate ${item.name}: ${(err as Error).message}`);
|
||||
} catch (_err) {
|
||||
console.log(`[!] Failed to migrate ${item.name}: ${(_err as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,11 +287,11 @@ class SharedManager {
|
||||
if (fs.statSync(instancePath).isDirectory()) {
|
||||
this.linkSharedDirectories(instancePath);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`[!] Failed to update instance ${instance}: ${(err as Error).message}`);
|
||||
} catch (_err) {
|
||||
console.log(`[!] Failed to update instance ${instance}: ${(_err as Error).message}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// No instances to update
|
||||
}
|
||||
}
|
||||
@@ -361,18 +361,18 @@ class SharedManager {
|
||||
try {
|
||||
fs.symlinkSync(sharedSettings, instanceSettings, 'file');
|
||||
migrated++;
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// Windows fallback
|
||||
if (process.platform === 'win32') {
|
||||
fs.copyFileSync(sharedSettings, instanceSettings);
|
||||
console.log(`[!] Symlink failed for ${instance}, copied instead`);
|
||||
migrated++;
|
||||
} else {
|
||||
throw err;
|
||||
throw _err;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`[!] Failed to migrate ${instance}: ${(err as Error).message}`);
|
||||
} catch (_err) {
|
||||
console.log(`[!] Failed to migrate ${instance}: ${(_err as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export function detectClaudeCli(): string | null {
|
||||
return claudePath;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
// Command failed - claude not in PATH
|
||||
// Fall through to return null
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ interface OraInstance {
|
||||
// Make ora optional (might not be available during npm install postinstall)
|
||||
let ora: ((text: string) => OraInstance) | null = null;
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const oraModule = require('ora');
|
||||
ora = oraModule.default || oraModule;
|
||||
} catch {
|
||||
|
||||
@@ -33,7 +33,6 @@ interface OraInstance {
|
||||
// Make ora optional (might not be available during npm install postinstall)
|
||||
let ora: ((text: string) => OraInstance) | null = null;
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const oraModule = require('ora');
|
||||
ora = oraModule.default || oraModule;
|
||||
} catch {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { Settings } from '../types';
|
||||
|
||||
interface ValidationResult {
|
||||
valid: boolean;
|
||||
@@ -41,7 +42,7 @@ export class DelegationValidator {
|
||||
}
|
||||
|
||||
// Read and parse settings.json
|
||||
let settings: any;
|
||||
let settings: Settings;
|
||||
try {
|
||||
const settingsContent = fs.readFileSync(settingsPath, 'utf8');
|
||||
settings = JSON.parse(settingsContent);
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('npm CLI', () => {
|
||||
this.timeout(5000);
|
||||
|
||||
try {
|
||||
runCli('-c', { stdio: 'pipe' });
|
||||
runCli('-c', { stdio: 'pipe', timeout: 3000 });
|
||||
} catch (e) {
|
||||
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
||||
// Should NOT show "Profile '-c' not found" error
|
||||
@@ -53,7 +53,7 @@ describe('npm CLI', () => {
|
||||
this.timeout(5000);
|
||||
|
||||
try {
|
||||
runCli('--verbose', { stdio: 'pipe' });
|
||||
runCli('--verbose', { stdio: 'pipe', timeout: 3000 });
|
||||
} catch (e) {
|
||||
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
||||
assert(!output.includes("Profile '--verbose' not found"), 'Should not treat --verbose as profile');
|
||||
@@ -75,7 +75,7 @@ describe('npm CLI', () => {
|
||||
this.timeout(5000);
|
||||
|
||||
try {
|
||||
runCli('-c --verbose', { stdio: 'pipe' });
|
||||
runCli('-c --verbose', { stdio: 'pipe', timeout: 3000 });
|
||||
} catch (e) {
|
||||
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
||||
assert(!output.includes("Profile '-c' not found"), 'Should not treat flags as profiles');
|
||||
@@ -112,7 +112,7 @@ describe('npm CLI', () => {
|
||||
this.timeout(5000);
|
||||
|
||||
try {
|
||||
runCli('glm -c', { stdio: 'pipe' });
|
||||
runCli('glm -c', { stdio: 'pipe', timeout: 3000 });
|
||||
} catch (e) {
|
||||
const output = e.stderr?.toString() || '';
|
||||
assert(!output.includes("Profile 'glm' not found"), 'GLM profile should exist');
|
||||
|
||||
Reference in new issue
Block a user