refactor: remove thinking state tracking from local launcher

This commit is contained in:
weishu
2025-12-23 08:25:44 +08:00
parent 93810689ad
commit 0128792495
3 changed files with 4 additions and 160 deletions
+2 -68
View File
@@ -1,73 +1,7 @@
const fs = require('fs');
// Disable autoupdater (never works really)
// Disable autoupdater
process.env.DISABLE_AUTOUPDATER = '1';
// Helper to write JSON messages to fd 3
function writeMessage(message) {
try {
fs.writeSync(3, JSON.stringify(message) + '\n');
} catch (err) {
// fd 3 not available, ignore
}
}
// Intercept fetch to track thinking state
const originalFetch = global.fetch;
let fetchCounter = 0;
global.fetch = function(...args) {
const id = ++fetchCounter;
const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';
const method = args[1]?.method || 'GET';
// Parse URL for privacy
let hostname = '';
let path = '';
try {
const urlObj = new URL(url, 'http://localhost');
hostname = urlObj.hostname;
path = urlObj.pathname;
} catch (e) {
// If URL parsing fails, use defaults
hostname = 'unknown';
path = url;
}
// Send fetch start event
writeMessage({
type: 'fetch-start',
id,
hostname,
path,
method,
timestamp: Date.now()
});
// Execute the original fetch immediately
const fetchPromise = originalFetch(...args);
// Attach handlers to send fetch end event
const sendEnd = () => {
writeMessage({
type: 'fetch-end',
id,
timestamp: Date.now()
});
};
// Send end event on both success and failure
fetchPromise.then(sendEnd, sendEnd);
// Return the original promise unchanged
return fetchPromise;
};
// Preserve fetch properties
Object.defineProperty(global.fetch, 'name', { value: 'fetch' });
Object.defineProperty(global.fetch, 'length', { value: originalFetch.length });
// Import global Claude Code CLI
const { getClaudeCliPath, runClaudeCli } = require('./claude_version_utils.cjs');
runClaudeCli(getClaudeCliPath());
runClaudeCli(getClaudeCliPath());
+1 -90
View File
@@ -1,6 +1,5 @@
import { spawn } from "node:child_process";
import { resolve, join } from "node:path";
import { createInterface } from "node:readline";
import { mkdirSync, existsSync } from "node:fs";
import { randomUUID } from "node:crypto";
import { logger } from "@/ui/logger";
@@ -20,7 +19,6 @@ export async function claudeLocal(opts: {
mcpServers?: Record<string, any>,
path: string,
onSessionFound: (id: string) => void,
onThinkingChange?: (thinking: boolean) => void,
claudeEnvVars?: Record<string, string>,
claudeArgs?: string[]
allowedTools?: string[]
@@ -51,19 +49,6 @@ export async function claudeLocal(opts: {
opts.onSessionFound(startFrom!);
}
// Thinking state
let thinking = false;
let stopThinkingTimeout: NodeJS.Timeout | null = null;
const updateThinking = (newThinking: boolean) => {
if (thinking !== newThinking) {
thinking = newThinking;
logger.debug(`[ClaudeLocal] Thinking state changed to: ${thinking}`);
if (opts.onThinkingChange) {
opts.onThinkingChange(thinking);
}
}
};
// Spawn the process
try {
// Start the interactive process
@@ -100,7 +85,6 @@ export async function claudeLocal(opts: {
// Prepare environment variables
// Note: Local mode uses global Claude installation with --session-id flag
// Launcher only intercepts fetch for thinking state tracking
const env = {
...process.env,
...opts.claudeEnvVars
@@ -110,79 +94,11 @@ export async function claudeLocal(opts: {
logger.debug(`[ClaudeLocal] Args: ${JSON.stringify(args)}`);
const child = spawn(process.execPath, [claudeCliPath, ...args], {
stdio: ['inherit', 'inherit', 'inherit', 'pipe'],
stdio: ['inherit', 'inherit', 'inherit'],
signal: opts.abort,
cwd: opts.path,
env: withBunRuntimeEnv(env),
});
// Listen to the custom fd (fd 3) for thinking state tracking
if (child.stdio[3]) {
const rl = createInterface({
input: child.stdio[3] as any,
crlfDelay: Infinity
});
// Track active fetches for thinking state
const activeFetches = new Map<number, { hostname: string, path: string, startTime: number }>();
rl.on('line', (line) => {
try {
const message = JSON.parse(line);
switch (message.type) {
case 'fetch-start':
activeFetches.set(message.id, {
hostname: message.hostname,
path: message.path,
startTime: message.timestamp
});
// Clear any pending stop timeout
if (stopThinkingTimeout) {
clearTimeout(stopThinkingTimeout);
stopThinkingTimeout = null;
}
// Start thinking
updateThinking(true);
break;
case 'fetch-end':
activeFetches.delete(message.id);
// Stop thinking when no active fetches
if (activeFetches.size === 0 && thinking && !stopThinkingTimeout) {
stopThinkingTimeout = setTimeout(() => {
if (activeFetches.size === 0) {
updateThinking(false);
}
stopThinkingTimeout = null;
}, 500); // Small delay to avoid flickering
}
break;
default:
logger.debug(`[ClaudeLocal] Unknown message type: ${message.type}`);
}
} catch (e) {
// Not JSON, ignore (could be other output)
logger.debug(`[ClaudeLocal] Non-JSON line from fd3: ${line}`);
}
});
rl.on('error', (err) => {
console.error('Error reading from fd 3:', err);
});
// Cleanup on child exit
child.on('exit', () => {
if (stopThinkingTimeout) {
clearTimeout(stopThinkingTimeout);
}
updateThinking(false);
});
}
child.on('error', (error) => {
// Ignore
});
@@ -199,11 +115,6 @@ export async function claudeLocal(opts: {
});
} finally {
process.stdin.resume();
if (stopThinkingTimeout) {
clearTimeout(stopThinkingTimeout);
stopThinkingTimeout = null;
}
updateThinking(false);
}
return effectiveSessionId;
+1 -2
View File
@@ -95,7 +95,6 @@ export async function claudeLocalLauncher(session: Session): Promise<'switch' |
path: session.path,
sessionId: session.sessionId,
onSessionFound: handleSessionStart,
onThinkingChange: session.onThinkingChange,
abort: processAbortController.signal,
claudeEnvVars: session.claudeEnvVars,
claudeArgs: session.claudeArgs,
@@ -139,4 +138,4 @@ export async function claudeLocalLauncher(session: Session): Promise<'switch' |
// Return
return exitReason || 'exit';
}
}