mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
refactor: remove CommonJS launchers and use claude command directly
Remove local Claude launcher scripts and refactor to invoke the global claude command from PATH. This simplifies the launcher logic and removes the need to maintain separate CommonJS entry points. Changes: - Delete cli/scripts/claude_local_launcher.cjs, claude_remote_launcher.cjs, and claude_version_utils.cjs - Update claudeLocal to spawn 'claude' command directly instead of using a launcher script - Remove runtimePath dependency and launcher existence checks - Add DISABLE_AUTOUPDATER environment variable for local mode - Improve error messages when claude command is not found - Add shell option for Windows compatibility in process spawning - Remove unused imports from claudeLocal and index files
This commit is contained in:
@@ -1,18 +1,12 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { resolve, join } from "node:path";
|
||||
import { mkdirSync, existsSync } from "node:fs";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { logger } from "@/ui/logger";
|
||||
import { claudeCheckSession } from "./utils/claudeCheckSession";
|
||||
import { getProjectPath } from "./utils/path";
|
||||
import { runtimePath } from "@/projectPath";
|
||||
import { systemPrompt } from "./utils/systemPrompt";
|
||||
import { withBunRuntimeEnv } from "@/utils/bunRuntime";
|
||||
|
||||
|
||||
// Get Claude CLI path from project root
|
||||
export const claudeCliPath = resolve(join(runtimePath(), 'scripts', 'claude_local_launcher.cjs'))
|
||||
|
||||
export async function claudeLocal(opts: {
|
||||
abort: AbortSignal,
|
||||
sessionId: string | null,
|
||||
@@ -79,37 +73,48 @@ export async function claudeLocal(opts: {
|
||||
args.push(...opts.claudeArgs)
|
||||
}
|
||||
|
||||
if (!claudeCliPath || !existsSync(claudeCliPath)) {
|
||||
throw new Error('Claude local launcher not found. Please ensure HAPI_PROJECT_ROOT is set correctly for development.');
|
||||
}
|
||||
|
||||
// Prepare environment variables
|
||||
// Note: Local mode uses global Claude installation with --session-id flag
|
||||
const env = {
|
||||
...process.env,
|
||||
DISABLE_AUTOUPDATER: '1',
|
||||
...opts.claudeEnvVars
|
||||
}
|
||||
|
||||
logger.debug(`[ClaudeLocal] Spawning launcher: ${claudeCliPath}`);
|
||||
logger.debug('[ClaudeLocal] Spawning claude');
|
||||
logger.debug(`[ClaudeLocal] Args: ${JSON.stringify(args)}`);
|
||||
|
||||
const child = spawn(process.execPath, [claudeCliPath, ...args], {
|
||||
const child = spawn('claude', args, {
|
||||
stdio: ['inherit', 'inherit', 'inherit'],
|
||||
signal: opts.abort,
|
||||
cwd: opts.path,
|
||||
env: withBunRuntimeEnv(env),
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
let settled = false;
|
||||
const finalize = (error?: Error) => {
|
||||
if (settled) {
|
||||
return;
|
||||
}
|
||||
settled = true;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
r();
|
||||
}
|
||||
};
|
||||
child.on('error', (error) => {
|
||||
// Ignore
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
finalize(new Error(`Failed to spawn claude: ${message}. Is Claude installed and on PATH?`));
|
||||
});
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal === 'SIGTERM' && opts.abort.aborted) {
|
||||
// Normal termination due to abort signal
|
||||
r();
|
||||
finalize();
|
||||
} else if (signal) {
|
||||
reject(new Error(`Process terminated with signal: ${signal}`));
|
||||
finalize(new Error(`Process terminated with signal: ${signal}`));
|
||||
} else {
|
||||
r();
|
||||
finalize();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { EnhancedMode, PermissionMode } from "./loop";
|
||||
import { query, type QueryOptions as Options, type SDKMessage, type SDKSystemMessage, AbortError, SDKUserMessage } from '@/claude/sdk'
|
||||
import { claudeCheckSession } from "./utils/claudeCheckSession";
|
||||
import { join, resolve } from 'node:path';
|
||||
import { runtimePath } from "@/projectPath";
|
||||
import { join } from 'node:path';
|
||||
import { parseSpecialCommand } from "@/parsers/specialCommands";
|
||||
import { logger } from "@/lib";
|
||||
import { PushableAsyncIterable } from "@/utils/PushableAsyncIterable";
|
||||
@@ -74,6 +73,7 @@ export async function claudeRemote(opts: {
|
||||
process.env[key] = value;
|
||||
});
|
||||
}
|
||||
process.env.DISABLE_AUTOUPDATER = '1';
|
||||
|
||||
// Get initial message
|
||||
const initial = await opts.nextMessage();
|
||||
@@ -121,9 +121,7 @@ export async function claudeRemote(opts: {
|
||||
canCallTool: (toolName: string, input: unknown, options: { signal: AbortSignal }) => opts.canCallTool(toolName, input, mode, options),
|
||||
executable: process.execPath,
|
||||
abort: opts.signal,
|
||||
pathToClaudeCodeExecutable: (() => {
|
||||
return resolve(join(runtimePath(), 'scripts', 'claude_remote_launcher.cjs'));
|
||||
})(),
|
||||
pathToClaudeCodeExecutable: 'claude',
|
||||
}
|
||||
|
||||
// Track thinking state
|
||||
|
||||
+3
-5
@@ -25,7 +25,6 @@ import { listDaemonSessions, stopDaemonSession } from './daemon/controlClient'
|
||||
import { handleAuthCommand } from './commands/auth'
|
||||
import { handleConnectCommand } from './commands/connect'
|
||||
import { spawnHappyCLI } from './utils/spawnHappyCLI'
|
||||
import { claudeCliPath } from './claude/claudeLocal'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { initializeToken } from './ui/tokenInit'
|
||||
import { ensureRuntimeAssets } from './runtime/assets'
|
||||
@@ -391,12 +390,11 @@ ${chalk.bold.cyan('Claude Code Options (from `claude --help`):')}
|
||||
`)
|
||||
|
||||
// Run claude --help and display its output
|
||||
// Use execFileSync with the current Node executable for cross-platform compatibility
|
||||
try {
|
||||
const claudeHelp = execFileSync(
|
||||
process.execPath,
|
||||
[claudeCliPath, '--help'],
|
||||
{ encoding: 'utf8', env: withBunRuntimeEnv() }
|
||||
'claude',
|
||||
['--help'],
|
||||
{ encoding: 'utf8', env: withBunRuntimeEnv(), shell: process.platform === 'win32' }
|
||||
)
|
||||
console.log(claudeHelp)
|
||||
} catch (e) {
|
||||
|
||||
@@ -116,9 +116,6 @@ function unpackTools(runtimeRoot: string): void {
|
||||
|
||||
function runtimeAssetsReady(runtimeRoot: string): boolean {
|
||||
const requiredScripts = [
|
||||
join(runtimeRoot, 'scripts', 'claude_local_launcher.cjs'),
|
||||
join(runtimeRoot, 'scripts', 'claude_remote_launcher.cjs'),
|
||||
join(runtimeRoot, 'scripts', 'claude_version_utils.cjs'),
|
||||
join(runtimeRoot, 'scripts', 'ripgrep_launcher.cjs')
|
||||
];
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { feature } from 'bun:bundle';
|
||||
|
||||
import claudeLocalLauncher from '../../scripts/claude_local_launcher.cjs' assert { type: 'file' };
|
||||
import claudeRemoteLauncher from '../../scripts/claude_remote_launcher.cjs' assert { type: 'file' };
|
||||
import claudeVersionUtils from '../../scripts/claude_version_utils.cjs' assert { type: 'file' };
|
||||
import ripgrepLauncher from '../../scripts/ripgrep_launcher.cjs' assert { type: 'file' };
|
||||
|
||||
import difftasticArchiveLicense from '../../tools/archives/difftastic-LICENSE' assert { type: 'file' };
|
||||
@@ -23,9 +20,6 @@ function asset(relativePath: string, sourcePath: string): EmbeddedAsset {
|
||||
}
|
||||
|
||||
const COMMON_ASSETS: EmbeddedAsset[] = [
|
||||
asset('scripts/claude_local_launcher.cjs', claudeLocalLauncher),
|
||||
asset('scripts/claude_remote_launcher.cjs', claudeRemoteLauncher),
|
||||
asset('scripts/claude_version_utils.cjs', claudeVersionUtils),
|
||||
asset('scripts/ripgrep_launcher.cjs', ripgrepLauncher),
|
||||
asset('tools/archives/difftastic-LICENSE', difftasticArchiveLicense),
|
||||
asset('tools/archives/ripgrep-LICENSE', ripgrepArchiveLicense),
|
||||
|
||||
Reference in New Issue
Block a user