2026-04-05 14:33:52 +08:00

208 lines
4.7 KiB
TypeScript

import { chmodSync, existsSync, mkdirSync } from 'fs'
import { dirname } from 'path'
const pkg = await Bun.file(new URL('../package.json', import.meta.url)).json() as {
name: string
version: string
}
const args = process.argv.slice(2)
const compile = args.includes('--compile')
const dev = args.includes('--dev')
const fullExperimentalFeatures = [
'AGENT_MEMORY_SNAPSHOT',
'AGENT_TRIGGERS',
'AGENT_TRIGGERS_REMOTE',
'AWAY_SUMMARY',
'BASH_CLASSIFIER',
'BRIDGE_MODE',
'BUILTIN_EXPLORE_PLAN_AGENTS',
'CACHED_MICROCOMPACT',
'CCR_AUTO_CONNECT',
'CCR_MIRROR',
'CCR_REMOTE_SETUP',
'COMPACTION_REMINDERS',
'CONNECTOR_TEXT',
'EXTRACT_MEMORIES',
'HISTORY_PICKER',
'HOOK_PROMPTS',
'KAIROS_BRIEF',
'KAIROS_CHANNELS',
'LODESTONE',
'MCP_RICH_OUTPUT',
'MESSAGE_ACTIONS',
'NATIVE_CLIPBOARD_IMAGE',
'NEW_INIT',
'POWERSHELL_AUTO_MODE',
'PROMPT_CACHE_BREAK_DETECTION',
'QUICK_SEARCH',
'SHOT_STATS',
'TEAMMEM',
'TOKEN_BUDGET',
'TREE_SITTER_BASH',
'TREE_SITTER_BASH_SHADOW',
'ULTRAPLAN',
'ULTRATHINK',
'UNATTENDED_RETRY',
'VERIFICATION_AGENT',
'VOICE_MODE',
] as const
function runCommand(cmd: string[]): string | null {
const proc = Bun.spawnSync({
cmd,
cwd: process.cwd(),
stdout: 'pipe',
stderr: 'pipe',
})
if (proc.exitCode !== 0) {
return null
}
return new TextDecoder().decode(proc.stdout).trim() || null
}
function getDevVersion(baseVersion: string): string {
const timestamp = new Date().toISOString()
const date = timestamp.slice(0, 10).replaceAll('-', '')
const time = timestamp.slice(11, 19).replaceAll(':', '')
const sha = runCommand(['git', 'rev-parse', '--short=8', 'HEAD']) ?? 'unknown'
return `${baseVersion}-dev.${date}.t${time}.sha${sha}`
}
function getVersionChangelog(): string {
return (
runCommand(['git', 'log', '--format=%h %s', '-20']) ??
'Local development build'
)
}
const defaultFeatures = ['VOICE_MODE']
const featureSet = new Set(defaultFeatures)
for (let i = 0; i < args.length; i += 1) {
const arg = args[i]
if (arg === '--feature-set' && args[i + 1]) {
if (args[i + 1] === 'dev-full') {
for (const feature of fullExperimentalFeatures) {
featureSet.add(feature)
}
}
i += 1
continue
}
if (arg === '--feature-set=dev-full') {
for (const feature of fullExperimentalFeatures) {
featureSet.add(feature)
}
continue
}
if (arg === '--feature' && args[i + 1]) {
featureSet.add(args[i + 1]!)
i += 1
continue
}
if (arg.startsWith('--feature=')) {
featureSet.add(arg.slice('--feature='.length))
}
}
const features = [...featureSet]
const outfile = compile
? dev
? './dist/cli-dev'
: './dist/cli'
: dev
? './cli-dev'
: './cli'
const buildTime = new Date().toISOString()
const version = dev ? getDevVersion(pkg.version) : pkg.version
const outDir = dirname(outfile)
if (outDir !== '.') {
mkdirSync(outDir, { recursive: true })
}
const externals = [
'@ant/*',
'audio-capture-napi',
'image-processor-napi',
'modifiers-napi',
'url-handler-napi',
]
const defines = {
'process.env.USER_TYPE': JSON.stringify('external'),
'process.env.CLAUDE_CODE_FORCE_FULL_LOGO': JSON.stringify('true'),
...(dev
? { 'process.env.NODE_ENV': JSON.stringify('development') }
: {}),
...(dev
? {
'process.env.CLAUDE_CODE_EXPERIMENTAL_BUILD': JSON.stringify('true'),
}
: {}),
'process.env.CLAUDE_CODE_VERIFY_PLAN': JSON.stringify('false'),
'process.env.CCR_FORCE_BUNDLE': JSON.stringify('true'),
'MACRO.VERSION': JSON.stringify(version),
'MACRO.BUILD_TIME': JSON.stringify(buildTime),
'MACRO.PACKAGE_URL': JSON.stringify(pkg.name),
'MACRO.NATIVE_PACKAGE_URL': 'undefined',
'MACRO.FEEDBACK_CHANNEL': JSON.stringify('github'),
'MACRO.ISSUES_EXPLAINER': JSON.stringify(
'This reconstructed source snapshot does not include Anthropic internal issue routing.',
),
'MACRO.VERSION_CHANGELOG': JSON.stringify(
dev ? getVersionChangelog() : 'https://github.com/paoloanzn/claude-code',
),
} as const
const cmd = [
'bun',
'build',
'./src/entrypoints/cli.tsx',
'--compile',
'--target',
'bun',
'--format',
'esm',
'--outfile',
outfile,
'--minify',
'--bytecode',
'--packages',
'bundle',
'--conditions',
'bun',
]
for (const external of externals) {
cmd.push('--external', external)
}
for (const feature of features) {
cmd.push(`--feature=${feature}`)
}
for (const [key, value] of Object.entries(defines)) {
cmd.push('--define', `${key}=${value}`)
}
const proc = Bun.spawnSync({
cmd,
cwd: process.cwd(),
stdout: 'inherit',
stderr: 'inherit',
})
if (proc.exitCode !== 0) {
process.exit(proc.exitCode ?? 1)
}
if (existsSync(outfile)) {
chmodSync(outfile, 0o755)
}
console.log(`Built ${outfile}`)