Merge remote-tracking branch 'origin/main'
Some checks failed
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Linux os:ubuntu-latest rust-target:x86_64-unknown-linux-gnu]) (push) Has been cancelled
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Windows os:windows-latest rust-target:x86_64-pc-windows-msvc]) (push) Has been cancelled
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:macOS os:macos-latest rust-target:x86_64-apple-darwin]) (push) Has been cancelled
Build Test / Build Test Summary (push) Has been cancelled

This commit is contained in:
2025-07-04 14:17:56 +08:00
7 changed files with 805 additions and 1 deletions

5
.gitignore vendored
View File

@@ -12,6 +12,9 @@ dist
dist-ssr
*.local
# Tauri binaries (built executables)
src-tauri/binaries/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
@@ -27,4 +30,4 @@ temp_lib/
.cursor/
AGENTS.md
CLAUDE.md
*_TASK.md
*_TASK.md

67
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,67 @@
# Welcome Contributors
We welcome contributions to enhance Claudia's capabilities and improve its performance. To report bugs, create a [GitHub issue](https://github.com/getAsterisk/claudia/issues).
> Before contributing, read through the existing issues and pull requests to see if someone else is already working on something similar. That way you can avoid duplicating efforts.
To contribute, please follow these steps:
1. Fork the Claudia repository on GitHub.
2. Create a new branch for your feature or bug fix.
3. Make your changes and ensure that the code passes all tests.
4. Submit a pull request describing your changes and their benefits.
## Pull Request Guidelines
When submitting a pull request, please follow these guidelines:
1. **Title**: Please include following prefixes:
- `Feature:` for new features
- `Fix:` for bug fixes
- `Docs:` for documentation changes
- `Refactor:` for code refactoring
- `Improve:` for performance improvements
- `Other:` for other changes
For example:
- `Feature: added custom agent timeout configuration`
- `Fix: resolved session list scrolling issue`
2. **Description**: Provide a clear and detailed description of your changes in the pull request. Explain the problem you are solving, the approach you took, and any potential side effects or limitations of your changes.
3. **Documentation**: Update the relevant documentation to reflect your changes. This includes the README file, code comments, and any other relevant documentation.
4. **Dependencies**: If your changes require new dependencies, ensure that they are properly documented and added to the `package.json` or `Cargo.toml` files.
5. If the pull request does not meet the above guidelines, it may be closed without merging.
**Note**: Please ensure that you have the latest version of the code before creating a pull request. If you have an existing fork, just sync your fork with the latest version of the Claudia repository.
## Coding Standards
### Frontend (React/TypeScript)
- Use TypeScript for all new code
- Follow functional components with hooks
- Use Tailwind CSS for styling
- Add JSDoc comments for exported functions and components
### Backend (Rust)
- Follow Rust standard conventions
- Use `cargo fmt` for formatting
- Use `cargo clippy` for linting
- Handle all `Result` types explicitly
- Add comprehensive documentation with `///` comments
### Security Requirements
- Validate all inputs from the frontend
- Use prepared statements for database operations
- Never log sensitive data (tokens, passwords, etc.)
- Use secure defaults for all configurations
## Testing
- Add tests for new functionality
- Ensure all existing tests pass
- Run `cargo test` for Rust code
- Test the application manually before submitting
Please adhere to the coding conventions, maintain clear documentation, and provide thorough testing for your contributions.

View File

@@ -6,6 +6,11 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"build:executables": "bun run scripts/fetch-and-build.js",
"build:executables:current": "bun run scripts/fetch-and-build.js current",
"build:executables:linux": "bun run scripts/fetch-and-build.js linux",
"build:executables:macos": "bun run scripts/fetch-and-build.js macos",
"build:executables:windows": "bun run scripts/fetch-and-build.js windows",
"preview": "vite preview",
"tauri": "tauri"
},

84
scripts/README.md Normal file
View File

@@ -0,0 +1,84 @@
# Build Scripts
This directory contains scripts for building Claude Code executables for all supported platforms.
## Scripts
### `fetch-and-build.js`
Main build script that:
1. Downloads the `@anthropic-ai/claude-code` package from npm
2. Extracts and copies required files (cli.js, yoga.wasm, vendor/)
3. Builds executables for specified platforms
4. Cleans up temporary files
**Usage:**
```bash
# Build for all platforms
bun run scripts/fetch-and-build.js
# Build for specific platform
bun run scripts/fetch-and-build.js linux
bun run scripts/fetch-and-build.js macos
bun run scripts/fetch-and-build.js windows
bun run scripts/fetch-and-build.js current # Current platform only
```
### `build-executables.js`
Low-level script that builds executables from existing source files. This is called automatically by `fetch-and-build.js`.
### `prepare-bundle-native.js`
Prepares the CLI source for bundling by embedding assets using Bun's native embedding features.
## NPM Scripts
The following npm scripts are available in `package.json`:
```bash
# Build executables for all platforms
npm run build:executables
# Build for specific platforms
npm run build:executables:current
npm run build:executables:linux
npm run build:executables:macos
npm run build:executables:windows
```
## Output
All executables are created in the `src-tauri/binaries/` directory with the following naming convention:
### Linux Executables
- `claude-code-linux-x64` - Standard Linux x64 (glibc)
- `claude-code-linux-x64-modern` - Modern CPUs (AVX2+)
- `claude-code-linux-x64-baseline` - Older CPUs (pre-2013)
- `claude-code-linux-arm64` - ARM64 Linux
- `claude-code-linux-x64-musl` - Alpine Linux (musl)
- `claude-code-linux-x64-musl-modern` - Alpine + modern CPUs
- `claude-code-linux-x64-musl-baseline` - Alpine + older CPUs
- `claude-code-linux-arm64-musl` - ARM64 Alpine
### macOS Executables
- `claude-code-macos-x64` - Intel Mac
- `claude-code-macos-x64-modern` - Intel Mac (modern CPUs)
- `claude-code-macos-x64-baseline` - Intel Mac (older CPUs)
- `claude-code-macos-arm64` - Apple Silicon Mac
### Windows Executables
- `claude-code-windows-x64.exe` - Windows x64
- `claude-code-windows-x64-modern.exe` - Windows x64 (modern CPUs)
- `claude-code-windows-x64-baseline.exe` - Windows x64 (older CPUs)
## Features
- **Embedded Assets**: All executables include embedded yoga.wasm and ripgrep binaries
- **Optimizations**: Built with minification and sourcemaps
- **Cross-platform**: Supports all major operating systems and architectures
- **CPU Variants**: Modern variants for newer CPUs (2013+), baseline for compatibility
- **Self-contained**: No external dependencies required at runtime
## Requirements
- **Bun**: Required for building (uses Bun's native compilation features)
- **npm**: Used to download the Claude Code package
- **tar**: For extracting the package (standard on Unix systems)

215
scripts/build-executables.js Executable file
View File

@@ -0,0 +1,215 @@
#!/usr/bin/env bun
/**
* Build script for creating single-file executables from Claude Code package
* Uses Bun's native embedding features with all optimizations enabled
*
* Output files follow Tauri sidecar triple naming convention: name-platform-architecture
* Examples: claude-code-x86_64-apple-darwin, claude-code-aarch64-unknown-linux-gnu
*
* Usage:
* bun run build-executables.js # Build all platforms
* bun run build-executables.js linux # Build Linux executables only
* bun run build-executables.js macos # Build macOS executables only
* bun run build-executables.js windows # Build Windows executables only
* bun run build-executables.js current # Build for current platform only
*/
import { spawn } from 'child_process';
import { mkdir, rm } from 'fs/promises';
import { existsSync } from 'fs';
import { join } from 'path';
// All supported targets with proper output names
const PLATFORMS = {
linux: [
// Linux x64 - glibc
{ target: 'bun-linux-x64', output: 'claude-code-x86_64-unknown-linux-gnu' },
{ target: 'bun-linux-x64-modern', output: 'claude-code-modern-x86_64-unknown-linux-gnu' },
{ target: 'bun-linux-x64-baseline', output: 'claude-code-baseline-x86_64-unknown-linux-gnu' },
// Linux ARM64 - glibc
{ target: 'bun-linux-arm64', output: 'claude-code-aarch64-unknown-linux-gnu' },
// Linux x64 - musl (Alpine Linux, etc.)
{ target: 'bun-linux-x64-musl', output: 'claude-code-x86_64-unknown-linux-musl' },
{ target: 'bun-linux-x64-musl-modern', output: 'claude-code-modern-x86_64-unknown-linux-musl' },
{ target: 'bun-linux-x64-musl-baseline', output: 'claude-code-baseline-x86_64-unknown-linux-musl' },
// Linux ARM64 - musl
{ target: 'bun-linux-arm64-musl', output: 'claude-code-aarch64-unknown-linux-musl' }
],
macos: [
// macOS x64
{ target: 'bun-darwin-x64', output: 'claude-code-x86_64-apple-darwin' },
{ target: 'bun-darwin-x64-modern', output: 'claude-code-modern-x86_64-apple-darwin' },
{ target: 'bun-darwin-x64-baseline', output: 'claude-code-baseline-x86_64-apple-darwin' },
// macOS ARM64 (Apple Silicon)
{ target: 'bun-darwin-arm64', output: 'claude-code-aarch64-apple-darwin' }
],
windows: [
// Windows x64
{ target: 'bun-windows-x64', output: 'claude-code-x86_64-pc-windows-msvc.exe' },
{ target: 'bun-windows-x64-modern', output: 'claude-code-modern-x86_64-pc-windows-msvc.exe' },
{ target: 'bun-windows-x64-baseline', output: 'claude-code-baseline-x86_64-pc-windows-msvc.exe' }
]
};
async function runCommand(command, args) {
return new Promise((resolve, reject) => {
console.log(`Running: ${command} ${args.join(' ')}`);
const child = spawn(command, args, { stdio: 'inherit' });
child.on('error', reject);
child.on('exit', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Command failed with exit code ${code}`));
}
});
});
}
async function prepareBundle() {
console.log('\nPreparing bundle with native Bun embedding...');
await runCommand('bun', ['run', 'scripts/prepare-bundle-native.js']);
}
async function buildExecutable(target, output) {
console.log(`\nBuilding ${output}...`);
const startTime = Date.now();
try {
await runCommand('bun', [
'build',
'--compile',
'--minify', // Optimize size
'--sourcemap', // Embed sourcemap for debugging
// '--bytecode', // Commented out - experimental feature that often fails
`--target=${target}`,
'./cli-native-bundled.js',
`--outfile=src-tauri/binaries/${output}`
]);
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
console.log(`✓ Built ${output} in ${elapsed}s`);
} catch (error) {
// If compilation fails, throw the error
throw error;
}
}
async function cleanupBundledFile() {
const filesToClean = ['./cli-bundled.js', './cli-native-bundled.js'];
for (const file of filesToClean) {
if (existsSync(file)) {
await rm(file);
}
}
console.log('\n✓ Cleaned up temporary files');
}
async function getCurrentPlatform() {
const arch = process.arch === 'x64' ? 'x86_64' : 'aarch64';
let targetTriple;
if (process.platform === 'darwin') {
targetTriple = `${arch}-apple-darwin`;
} else if (process.platform === 'linux') {
targetTriple = `${arch}-unknown-linux-gnu`;
} else if (process.platform === 'win32') {
targetTriple = `${arch}-pc-windows-msvc`;
} else {
throw new Error(`Unsupported platform: ${process.platform}`);
}
return targetTriple;
}
async function main() {
const arg = process.argv[2];
// Create src-tauri/binaries directory if it doesn't exist
if (!existsSync('src-tauri/binaries')) {
await mkdir('src-tauri/binaries', { recursive: true });
}
let platformsToBuild = [];
if (!arg || arg === 'all') {
// Build all platforms
platformsToBuild = [
...PLATFORMS.linux,
...PLATFORMS.macos,
...PLATFORMS.windows
];
} else if (arg === 'linux') {
platformsToBuild = PLATFORMS.linux;
} else if (arg === 'macos' || arg === 'darwin') {
platformsToBuild = PLATFORMS.macos;
} else if (arg === 'windows' || arg === 'win32') {
platformsToBuild = PLATFORMS.windows;
} else if (arg === 'current') {
// Build only for current platform
const currentTargetTriple = await getCurrentPlatform();
const allPlatforms = [
...PLATFORMS.linux,
...PLATFORMS.macos,
...PLATFORMS.windows
];
// Find platform by matching the target triple in the output name
const current = allPlatforms.find(p => p.output.includes(currentTargetTriple));
if (current) {
platformsToBuild = [current];
} else {
console.error(`Current platform ${currentTargetTriple} not found in build targets`);
process.exit(1);
}
} else {
console.error(`Unknown argument: ${arg}`);
console.error('Usage: bun run build-executables.js [all|linux|macos|windows|current]');
process.exit(1);
}
console.log(`Building ${platformsToBuild.length} executable(s) with full optimizations...`);
console.log('Optimizations enabled: --minify --sourcemap');
const startTime = Date.now();
try {
// Prepare the bundle once with native embedding
await prepareBundle();
// Build executables sequentially to avoid resource conflicts
let successCount = 0;
for (const platform of platformsToBuild) {
try {
await buildExecutable(platform.target, platform.output);
successCount++;
} catch (error) {
console.error(`Failed to build ${platform.output}:`, error.message);
}
}
const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1);
console.log(`\n✅ Successfully built ${successCount}/${platformsToBuild.length} executables in ${totalElapsed}s`);
console.log('\nExecutables are available in the src-tauri/binaries/ directory');
console.log('\nNotes:');
console.log('- All executables include embedded assets (yoga.wasm, ripgrep binaries)');
console.log('- File names follow Tauri sidecar triple naming convention (name-platform-architecture)');
console.log('- Modern variants require CPUs from 2013+ (AVX2 support)');
console.log('- Baseline variants support older CPUs (pre-2013)');
console.log('- Musl variants are for Alpine Linux and similar distributions');
console.log('- All executables are optimized with minification and sourcemaps');
} finally {
// Clean up temporary files
await cleanupBundledFile();
}
}
main().catch(error => {
console.error('Build failed:', error);
process.exit(1);
});

285
scripts/fetch-and-build.js Executable file
View File

@@ -0,0 +1,285 @@
#!/usr/bin/env bun
/**
* Fetch Claude Code package from npm and build executables for all platforms
*
* This script:
* 1. Downloads the @anthropic-ai/claude-code package from npm
* 2. Extracts it to a temporary directory
* 3. Runs the build-executables script to create binaries for all platforms
* 4. Cleans up temporary files
*
* Usage:
* bun run fetch-and-build.js [platform]
*
* Where platform can be: all, linux, macos, windows, current
*/
import { spawn } from 'child_process';
import { mkdir, rm, readdir, copyFile, access } from 'fs/promises';
import { existsSync } from 'fs';
import { join, resolve } from 'path';
/**
* Execute a shell command and return a promise
* @param {string} command - The command to execute
* @param {string[]} args - Command arguments
* @param {object} options - Spawn options
* @returns {Promise<void>}
*/
async function runCommand(command, args = [], options = {}) {
return new Promise((resolve, reject) => {
console.log(`Running: ${command} ${args.join(' ')}`);
const child = spawn(command, args, {
stdio: 'inherit',
shell: process.platform === 'win32',
...options
});
child.on('error', (error) => {
console.error(`Failed to execute command: ${error.message}`);
reject(error);
});
child.on('exit', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Command failed with exit code ${code}`));
}
});
});
}
/**
* Check if a file or directory exists
* @param {string} path - Path to check
* @returns {Promise<boolean>}
*/
async function pathExists(path) {
try {
await access(path);
return true;
} catch {
return false;
}
}
/**
* Download and extract the Claude Code package from npm
* @returns {Promise<string>} - Path to the extracted package directory
*/
async function fetchClaudeCodePackage() {
console.log('\n📦 Fetching @anthropic-ai/claude-code package from npm...');
const tempDir = resolve('./temp-claude-package');
const packageDir = join(tempDir, 'package');
try {
// Clean up any existing temp directory
if (await pathExists(tempDir)) {
console.log('Cleaning up existing temp directory...');
await rm(tempDir, { recursive: true, force: true });
}
// Create temp directory
await mkdir(tempDir, { recursive: true });
// Download the package tarball
console.log('Downloading package tarball...');
await runCommand('npm', ['pack', '@anthropic-ai/claude-code'], {
cwd: tempDir
});
// Find the downloaded tarball
const files = await readdir(tempDir);
const tarball = files.find(file => file.startsWith('anthropic-ai-claude-code-') && file.endsWith('.tgz'));
if (!tarball) {
throw new Error('Failed to find downloaded tarball');
}
console.log(`Found tarball: ${tarball}`);
// Extract the tarball
console.log('Extracting package...');
await runCommand('tar', ['-xzf', tarball], {
cwd: tempDir
});
// Verify extraction
if (!(await pathExists(packageDir))) {
throw new Error('Package extraction failed - package directory not found');
}
console.log(`✓ Package extracted to: ${packageDir}`);
return packageDir;
} catch (error) {
// Clean up on error
if (await pathExists(tempDir)) {
await rm(tempDir, { recursive: true, force: true });
}
throw error;
}
}
/**
* Copy required files from the Claude Code package to current directory
* @param {string} packageDir - Path to the extracted package directory
*/
async function copyRequiredFiles(packageDir) {
console.log('\n📋 Copying required files from Claude Code package...');
const filesToCopy = [
'cli.js',
'yoga.wasm'
];
const directoriesToCopy = [
'vendor'
];
// Copy individual files
for (const file of filesToCopy) {
const srcPath = join(packageDir, file);
const destPath = resolve(file);
if (await pathExists(srcPath)) {
console.log(`Copying ${file}...`);
await copyFile(srcPath, destPath);
} else {
console.warn(`Warning: ${file} not found in package`);
}
}
// Copy directories recursively
for (const dir of directoriesToCopy) {
const srcPath = join(packageDir, dir);
const destPath = resolve(dir);
if (await pathExists(srcPath)) {
console.log(`Copying ${dir}/ directory...`);
// Remove existing directory if it exists
if (await pathExists(destPath)) {
await rm(destPath, { recursive: true, force: true });
}
// Copy directory recursively using cp command
await runCommand('cp', ['-r', srcPath, destPath]);
} else {
console.warn(`Warning: ${dir}/ directory not found in package`);
}
}
console.log('✓ Required files copied successfully');
}
/**
* Clean up temporary files and directories
* @param {string} packageDir - Path to the package directory to clean up
*/
async function cleanup(packageDir) {
console.log('\n🧹 Cleaning up temporary files...');
const tempDir = resolve('./temp-claude-package');
try {
if (await pathExists(tempDir)) {
await rm(tempDir, { recursive: true, force: true });
console.log('✓ Temporary package directory cleaned up');
}
// Clean up copied files that are no longer needed
const filesToCleanup = [
'./cli.js',
'./cli-bundled.js',
'./cli-native-bundled.js',
'./yoga.wasm'
];
for (const file of filesToCleanup) {
if (await pathExists(file)) {
await rm(file);
}
}
// Clean up vendor directory
const vendorDir = './vendor';
if (await pathExists(vendorDir)) {
await rm(vendorDir, { recursive: true, force: true });
}
console.log('✓ Cleanup completed');
} catch (error) {
console.warn(`Warning: Cleanup failed: ${error.message}`);
}
}
/**
* Build executables for the specified platform(s)
* @param {string} platform - Platform to build for (all, linux, macos, windows, current)
*/
async function buildExecutables(platform = 'all') {
console.log(`\n🔨 Building executables for platform: ${platform}`);
// Ensure src-tauri/binaries directory exists
if (!await pathExists('./src-tauri/binaries')) {
await mkdir('./src-tauri/binaries', { recursive: true });
}
// Run the build-executables script
const args = platform === 'all' ? [] : [platform];
await runCommand('bun', ['run', './scripts/build-executables.js', ...args]);
}
/**
* Main execution function
*/
async function main() {
const platform = process.argv[2] || 'all';
const validPlatforms = ['all', 'linux', 'macos', 'darwin', 'windows', 'win32', 'current'];
if (!validPlatforms.includes(platform)) {
console.error(`Invalid platform: ${platform}`);
console.error(`Valid platforms: ${validPlatforms.join(', ')}`);
process.exit(1);
}
console.log('🚀 Starting Claude Code fetch and build process...');
console.log(`Target platform: ${platform}`);
const startTime = Date.now();
let packageDir;
try {
// Step 1: Fetch and extract the package
packageDir = await fetchClaudeCodePackage();
// Step 2: Copy required files
await copyRequiredFiles(packageDir);
// Step 3: Build executables
await buildExecutables(platform);
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
console.log(`\n✅ Build process completed successfully in ${totalTime}s`);
console.log('\n📁 Executables are available in the src-tauri/binaries/ directory');
} catch (error) {
console.error(`\n❌ Build process failed: ${error.message}`);
process.exit(1);
} finally {
// Always clean up, even if there was an error
if (packageDir) {
await cleanup(packageDir);
}
}
}
// Run the main function
main().catch(error => {
console.error('Unexpected error:', error);
process.exit(1);
});

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env bun
/**
* Prepare the CLI for bundling using Bun's native embedding features
* This modifies the source to use embedded files directly
*/
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { join } from 'path';
// Read the original CLI file
const cliPath = './cli.js';
let cliContent = readFileSync(cliPath, 'utf-8');
console.log('Preparing CLI for native Bun embedding...');
// 1. Build list of embedded imports based on what files actually exist
const embeddedImports = [];
const embeddedFilesMapping = [];
// Define all possible ripgrep files
const ripgrepFiles = [
{ path: './vendor/ripgrep/arm64-darwin/rg', var: '__embeddedRgDarwinArm64' },
{ path: './vendor/ripgrep/arm64-darwin/ripgrep.node', var: '__embeddedRgNodeDarwinArm64' },
{ path: './vendor/ripgrep/arm64-linux/rg', var: '__embeddedRgLinuxArm64' },
{ path: './vendor/ripgrep/arm64-linux/ripgrep.node', var: '__embeddedRgNodeLinuxArm64' },
{ path: './vendor/ripgrep/x64-darwin/rg', var: '__embeddedRgDarwinX64' },
{ path: './vendor/ripgrep/x64-darwin/ripgrep.node', var: '__embeddedRgNodeDarwinX64' },
{ path: './vendor/ripgrep/x64-linux/rg', var: '__embeddedRgLinuxX64' },
{ path: './vendor/ripgrep/x64-linux/ripgrep.node', var: '__embeddedRgNodeLinuxX64' },
{ path: './vendor/ripgrep/x64-win32/rg.exe', var: '__embeddedRgWin32' },
{ path: './vendor/ripgrep/x64-win32/ripgrep.node', var: '__embeddedRgNodeWin32' },
];
// Always include yoga.wasm
if (existsSync('./yoga.wasm')) {
embeddedImports.push('import __embeddedYogaWasm from "./yoga.wasm" with { type: "file" };');
embeddedFilesMapping.push(" 'yoga.wasm': __embeddedYogaWasm,");
} else {
console.error('Warning: yoga.wasm not found');
}
// Only import ripgrep files that exist
for (const file of ripgrepFiles) {
if (existsSync(file.path)) {
embeddedImports.push(`import ${file.var} from "${file.path}" with { type: "file" };`);
const key = file.path.replace('./', '');
embeddedFilesMapping.push(` '${key}': ${file.var},`);
}
}
const embeddedCode = `
// Embedded files using Bun's native embedding
${embeddedImports.join('\n')}
const __embeddedFiles = {
${embeddedFilesMapping.join('\n')}
};
`;
// Add imports after the shebang
const shebangMatch = cliContent.match(/^#!.*\n/);
if (shebangMatch) {
cliContent = shebangMatch[0] + embeddedCode + cliContent.substring(shebangMatch[0].length);
} else {
cliContent = embeddedCode + cliContent;
}
// 2. Replace yoga.wasm loading - handle top-level await properly
// Original: var k81=await nUA(await VP9(CP9(import.meta.url).resolve("./yoga.wasm")));
// Since this uses top-level await, we need to preserve that structure
const yogaLoadPattern = /var k81=await nUA\(await VP9\(CP9\(import\.meta\.url\)\.resolve\("\.\/yoga\.wasm"\)\)\);/;
// Use an IIFE to handle the async loading
const yogaLoadReplacement = `var k81=await(async()=>{return await nUA(await Bun.file(__embeddedYogaWasm).arrayBuffer())})();`;
if (yogaLoadPattern.test(cliContent)) {
cliContent = cliContent.replace(yogaLoadPattern, yogaLoadReplacement);
console.log('✓ Replaced yoga.wasm loading with embedded version');
} else {
console.error('Warning: Could not find yoga.wasm loading pattern');
// Try a more general pattern
const generalYogaPattern = /var\s+(\w+)\s*=\s*await\s+nUA\s*\(\s*await\s+VP9\s*\([^)]+\.resolve\s*\(\s*["']\.\/yoga\.wasm["']\s*\)\s*\)\s*\)/;
if (generalYogaPattern.test(cliContent)) {
cliContent = cliContent.replace(generalYogaPattern, (match, varName) => {
return `var ${varName}=await(async()=>{return await nUA(await Bun.file(__embeddedYogaWasm).arrayBuffer())})()`;
});
console.log('✓ Replaced yoga.wasm loading with embedded version (general pattern)');
}
}
// 3. Replace ripgrep path resolution
// Add check for embedded files in the ripgrep resolver
const ripgrepPattern = /let B=Db\.resolve\(et9,"vendor","ripgrep"\);/;
const ripgrepReplacement = `
if(process.env.CLAUDE_CODE_BUNDLED || typeof __embeddedFiles !== 'undefined'){
const platform = process.platform === "win32" ? "x64-win32" : \`\${process.arch}-\${process.platform}\`;
const rgKey = \`vendor/ripgrep/\${platform}/rg\${process.platform === "win32" ? ".exe" : ""}\`;
if(__embeddedFiles[rgKey]) return __embeddedFiles[rgKey];
}
let B=Db.resolve(et9,"vendor","ripgrep");`;
if (ripgrepPattern.test(cliContent)) {
cliContent = cliContent.replace(ripgrepPattern, ripgrepReplacement);
console.log('✓ Added embedded file handling for ripgrep');
}
// 4. Replace ripgrep.node loading - handle the entire if-else structure
// Look for the complete if-else pattern where B is assigned
const ripgrepNodePattern = /if\(typeof Bun!=="undefined"&&Bun\.embeddedFiles\?\.length>0\)B="\.\/ripgrep\.node";else/;
const ripgrepNodeReplacement = `if(typeof Bun!=="undefined"&&Bun.embeddedFiles?.length>0)B=(()=>{
const platform = process.platform === "win32" ? "x64-win32" : \`\${process.arch}-\${process.platform}\`;
const nodeKey = \`vendor/ripgrep/\${platform}/ripgrep.node\`;
return __embeddedFiles[nodeKey] || "./ripgrep.node";
})();else`;
if (ripgrepNodePattern.test(cliContent)) {
cliContent = cliContent.replace(ripgrepNodePattern, ripgrepNodeReplacement);
console.log('✓ Added embedded file handling for ripgrep.node');
} else {
// Fallback to simpler pattern if the exact pattern doesn't match
const simplePattern = /B="\.\/ripgrep\.node"/;
if (simplePattern.test(cliContent)) {
cliContent = cliContent.replace(simplePattern, `B=(()=>{
const platform = process.platform === "win32" ? "x64-win32" : \`\${process.arch}-\${process.platform}\`;
const nodeKey = \`vendor/ripgrep/\${platform}/ripgrep.node\`;
return __embeddedFiles[nodeKey] || "./ripgrep.node";
})()`);
console.log('✓ Added embedded file handling for ripgrep.node (fallback pattern)');
}
}
// Set bundled mode indicator
cliContent = cliContent.replace(
/process\.env\.CLAUDE_CODE_ENTRYPOINT="cli"/,
'process.env.CLAUDE_CODE_ENTRYPOINT="cli";process.env.CLAUDE_CODE_BUNDLED="1"'
);
// Write the modified content
const outputPath = './cli-native-bundled.js';
writeFileSync(outputPath, cliContent);
console.log(`\n✅ Created ${outputPath} ready for bundling with native embedding`);
console.log('\nNow you can run:');
console.log(` bun build --compile --minify ./cli-native-bundled.js --outfile dist/claude-code`);