refactor: enhance TypeScript types and improve delegation system

This commit is contained in:
kaitranntt committed 2025-11-27 09:25:12 -05:00
1 parent 0139333146
commit 7a413a81b7
16 files changed
+94 -62

No files matched your search

+2 -2
View File
@@ -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
}
+3 -3
View File
@@ -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 };
}
+42 -16
View File
@@ -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;
}
}
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -15,7 +15,7 @@
*/
interface ThinkingConfig {
[key: string]: any;
[key: string]: unknown;
}
interface DeltaAccumulatorOptions {
+6 -3
View File
@@ -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) => {
+8 -4
View File
@@ -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
+1 -1
View File
@@ -23,7 +23,7 @@ interface SSEParserOptions {
interface SSEEvent {
event: string;
data: any;
data: unknown;
index?: number;
id?: string;
retry?: number;
+4 -4
View File
@@ -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++;
}
}
+1 -1
View File
@@ -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);
+16 -16
View File
@@ -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}`);
}
}
+1 -1
View File
@@ -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
}
-1
View File
@@ -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 {
-1
View File
@@ -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 {
+2 -1
View File
@@ -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);
+4 -4
View File
@@ -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');