Skip to content

Commit f3c07e6

Browse files
committed
sdk: Bundle ripgrep binaries instead of importing from vscode
1 parent 9880f69 commit f3c07e6

File tree

18 files changed

+1023
-16
lines changed

18 files changed

+1023
-16
lines changed

β€Žbun.lockβ€Ž

Lines changed: 11 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žsdk/package.jsonβ€Ž

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"license": "Apache-2.0",
77
"type": "module",
88
"main": "./dist/index.cjs",
9-
"module": "./dist/index.mjs",
109
"types": "./dist/index.d.ts",
1110
"exports": {
1211
".": {
@@ -25,14 +24,16 @@
2524
"scripts": {
2625
"build": "bun run scripts/build.ts",
2726
"build:types": "tsc -p tsconfig.build.json",
28-
"build:verify": "bun run build && bun run smoke-test:dist && bun run test:cjs && bun run test:esm",
27+
"build:verify": "bun run build && bun run smoke-test:dist && bun run test:cjs && bun run test:esm && bun run test:ripgrep",
2928
"smoke-test:dist": "bun run smoke-test-dist.ts",
3029
"test:cjs": "cd test/cjs-compatibility && npm install --silent && npm run test:all",
3130
"test:esm": "cd test/esm-compatibility && npm install --silent && npm run test:all",
31+
"test:ripgrep": "cd test/ripgrep-bundling && npm install --silent && npm run test:all",
3232
"clean": "rm -rf dist",
3333
"prepare-dist": "bun run scripts/publish.ts --dry-run",
3434
"publish-sdk": "bun run scripts/publish.ts --public",
3535
"publish-dry-run": "bun run build:verify && bun run scripts/publish.ts --dry-run",
36+
"fetch-ripgrep": "bun scripts/fetch-ripgrep.ts",
3637
"prepack": "bun run build",
3738
"typecheck": "tsc --noEmit -p .",
3839
"test": "bun test"
@@ -58,7 +59,6 @@
5859
"url": "https://github.com/CodebuffAI/codebuff/issues"
5960
},
6061
"dependencies": {
61-
"@vscode/ripgrep": "1.15.14",
6262
"@vscode/tree-sitter-wasm": "0.1.4",
6363
"ai": "^5.0.0",
6464
"diff": "8.0.2",
@@ -69,7 +69,9 @@
6969
"devDependencies": {
7070
"@types/bun": "^1.2.11",
7171
"@types/diff": "8.0.0",
72-
"@types/node": "22"
72+
"@types/node": "22",
73+
"adm-zip": "^0.5.12",
74+
"node-fetch": "^3.3.2"
7375
},
7476
"author": ""
7577
}

β€Žsdk/scripts/build.tsβ€Ž

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ async function build() {
8181
console.log('πŸ“‚ Copying WASM files for tree-sitter...')
8282
await copyWasmFiles()
8383

84+
console.log('πŸ“‚ Copying vendored ripgrep binaries...')
85+
await copyRipgrepVendor()
86+
8487
console.log('βœ… Build complete!')
8588
console.log(' πŸ“„ dist/index.mjs (ESM)')
8689
console.log(' πŸ“„ dist/index.cjs (CJS)')
@@ -133,6 +136,20 @@ async function copyWasmFiles() {
133136
}
134137
}
135138

139+
async function copyRipgrepVendor() {
140+
const vendorSrc = 'vendor/ripgrep'
141+
const vendorDest = 'dist/vendor/ripgrep'
142+
try {
143+
await mkdir(vendorDest, { recursive: true })
144+
await cp(vendorSrc, vendorDest, { recursive: true })
145+
console.log(' βœ“ Copied vendored ripgrep binaries')
146+
} catch (e) {
147+
console.warn(
148+
' ⚠ No vendored ripgrep found; skipping (use fetch-ripgrep.ts first)',
149+
)
150+
}
151+
}
152+
136153
if (import.meta.main) {
137154
build().catch(console.error)
138155
}

β€Žsdk/scripts/fetch-ripgrep.tsβ€Ž

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#!/usr/bin/env bun
2+
3+
import { mkdir, writeFile } from 'fs/promises'
4+
import { existsSync } from 'fs'
5+
import { join } from 'path'
6+
import AdmZip from 'adm-zip'
7+
import fetch from 'node-fetch'
8+
9+
// Ripgrep version to download
10+
const RIPGREP_VERSION = '14.1.1'
11+
12+
// Platform configurations
13+
const PLATFORMS = [
14+
{
15+
name: 'x64-win32',
16+
url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-pc-windows-msvc.zip`,
17+
executable: 'rg.exe',
18+
archivePath: `ripgrep-${RIPGREP_VERSION}-x86_64-pc-windows-msvc/rg.exe`
19+
},
20+
{
21+
name: 'x64-darwin',
22+
url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-apple-darwin.tar.gz`,
23+
executable: 'rg',
24+
archivePath: `ripgrep-${RIPGREP_VERSION}-x86_64-apple-darwin/rg`
25+
},
26+
{
27+
name: 'arm64-darwin',
28+
url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-aarch64-apple-darwin.tar.gz`,
29+
executable: 'rg',
30+
archivePath: `ripgrep-${RIPGREP_VERSION}-aarch64-apple-darwin/rg`
31+
},
32+
{
33+
name: 'x64-linux',
34+
url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl.tar.gz`,
35+
executable: 'rg',
36+
archivePath: `ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl/rg`
37+
},
38+
{
39+
name: 'arm64-linux',
40+
url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu.tar.gz`,
41+
executable: 'rg',
42+
archivePath: `ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu/rg`
43+
}
44+
]
45+
46+
async function downloadAndExtract(platform: typeof PLATFORMS[0]): Promise<void> {
47+
const vendorDir = join('vendor', 'ripgrep', platform.name)
48+
const outputPath = join(vendorDir, platform.executable)
49+
50+
// Skip if already exists
51+
if (existsSync(outputPath)) {
52+
console.log(`βœ“ ${platform.name} already exists, skipping`)
53+
return
54+
}
55+
56+
console.log(`πŸ“₯ Downloading ${platform.name}...`)
57+
58+
const response = await fetch(platform.url)
59+
if (!response.ok) {
60+
throw new Error(`Failed to download ${platform.url}: ${response.statusText}`)
61+
}
62+
63+
const arrayBuffer = await response.arrayBuffer()
64+
const buffer = Buffer.from(arrayBuffer)
65+
66+
// Create vendor directory
67+
await mkdir(vendorDir, { recursive: true })
68+
69+
if (platform.url.endsWith('.zip')) {
70+
// Handle ZIP files
71+
const zip = AdmZip(buffer)
72+
const entry = zip.getEntry(platform.archivePath)
73+
if (!entry) {
74+
throw new Error(`Could not find ${platform.archivePath} in archive`)
75+
}
76+
await writeFile(outputPath, entry.getData())
77+
} else {
78+
// Handle tar.gz files
79+
const { spawn } = require('child_process')
80+
const tempFile = join(vendorDir, 'temp.tar.gz')
81+
82+
// Write temp file
83+
await writeFile(tempFile, buffer)
84+
85+
// Extract using tar
86+
await new Promise<void>((resolve, reject) => {
87+
const tar = spawn('tar', ['-xzf', tempFile, '-C', vendorDir, '--strip-components=1', platform.archivePath], {
88+
stdio: 'inherit'
89+
})
90+
91+
tar.on('close', (code) => {
92+
if (code === 0) {
93+
resolve()
94+
} else {
95+
reject(new Error(`tar extraction failed with code ${code}`))
96+
}
97+
})
98+
99+
tar.on('error', reject)
100+
})
101+
102+
// Clean up temp file
103+
try {
104+
await Bun.file(tempFile).delete()
105+
} catch {}
106+
}
107+
108+
// Make executable on Unix systems
109+
if (platform.executable === 'rg') {
110+
const { spawn } = require('child_process')
111+
await new Promise<void>((resolve, reject) => {
112+
const chmod = spawn('chmod', ['+x', outputPath])
113+
chmod.on('close', (code) => {
114+
if (code === 0) {
115+
resolve()
116+
} else {
117+
reject(new Error(`chmod failed with code ${code}`))
118+
}
119+
})
120+
chmod.on('error', reject)
121+
})
122+
}
123+
124+
console.log(`βœ“ ${platform.name} downloaded and extracted`)
125+
}
126+
127+
async function main() {
128+
console.log(`πŸ”§ Fetching ripgrep v${RIPGREP_VERSION} binaries...`)
129+
130+
// Create base vendor directory
131+
await mkdir(join('vendor', 'ripgrep'), { recursive: true })
132+
133+
// Download all platforms in parallel
134+
await Promise.all(PLATFORMS.map(downloadAndExtract))
135+
136+
// Copy COPYING file
137+
console.log('πŸ“„ Downloading COPYING file...')
138+
const copyingResponse = await fetch(`https://raw.githubusercontent.com/BurntSushi/ripgrep/main/COPYING`)
139+
if (copyingResponse.ok) {
140+
const copyingText = await copyingResponse.text()
141+
await writeFile(join('vendor', 'ripgrep', 'COPYING'), copyingText)
142+
console.log('βœ“ COPYING file downloaded')
143+
}
144+
145+
console.log('πŸŽ‰ All ripgrep binaries downloaded successfully!')
146+
console.log('πŸ“ Files are located in vendor/ripgrep/')
147+
}
148+
149+
if (import.meta.main) {
150+
main().catch((error) => {
151+
console.error('❌ Error:', error.message)
152+
process.exit(1)
153+
})
154+
}

β€Žsdk/src/index.tsβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ export * from './client'
1010
export * from './custom-tool'
1111
export * from './run-state'
1212
export * from './websocket-client'
13+
export * from './native/ripgrep'
14+
export { ToolHelpers } from './tools'

0 commit comments

Comments
Β (0)