Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 4fd195a

Browse files
authored
Add ripgrep binary download script and fix t.replace issue (#919)
1 parent 1bf5b5c commit 4fd195a

8 files changed

Lines changed: 204 additions & 176 deletions

File tree

‎apps/twig/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"test": "vitest run",
2626
"test:e2e": "playwright test --config=tests/e2e/playwright.config.ts",
2727
"test:e2e:headed": "playwright test --config=tests/e2e/playwright.config.ts --headed",
28-
"postinstall": "cd ../.. && npx @electron/rebuild -f -m node_modules/node-pty || true && bash apps/twig/scripts/patch-electron-name.sh && node apps/twig/scripts/download-codex-acp.mjs",
28+
"postinstall": "cd ../.. && npx @electron/rebuild -f -m node_modules/node-pty || true && bash apps/twig/scripts/patch-electron-name.sh && node apps/twig/scripts/download-binaries.mjs",
2929
"storybook": "storybook dev -p 6006",
3030
"build-storybook": "storybook build"
3131
},
4.32 MB
Binary file not shown.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/usr/bin/env node
2+
3+
import { execSync } from "node:child_process";
4+
import {
5+
chmodSync,
6+
createWriteStream,
7+
existsSync,
8+
mkdirSync,
9+
rmSync,
10+
} from "node:fs";
11+
import { dirname, join } from "node:path";
12+
import { pipeline } from "node:stream/promises";
13+
import { fileURLToPath } from "node:url";
14+
import { extract } from "tar";
15+
16+
const __dirname = dirname(fileURLToPath(import.meta.url));
17+
const DEST_DIR = join(__dirname, "..", "resources", "codex-acp");
18+
19+
const BINARIES = [
20+
{
21+
name: "codex-acp",
22+
version: "0.9.1",
23+
getUrl: (version, target) => {
24+
const ext = target.includes("windows") ? "zip" : "tar.gz";
25+
return `https://github.com/zed-industries/codex-acp/releases/download/v${version}/codex-acp-${version}-${target}.${ext}`;
26+
},
27+
getTarget: () => {
28+
const { platform, arch } = process;
29+
const targets = {
30+
darwin: { arm64: "aarch64-apple-darwin", x64: "x86_64-apple-darwin" },
31+
linux: {
32+
arm64: "aarch64-unknown-linux-gnu",
33+
x64: "x86_64-unknown-linux-gnu",
34+
},
35+
win32: {
36+
arm64: "aarch64-pc-windows-msvc",
37+
x64: "x86_64-pc-windows-msvc",
38+
},
39+
};
40+
const platformTargets = targets[platform];
41+
if (!platformTargets)
42+
throw new Error(`Unsupported platform: ${platform}`);
43+
const target = platformTargets[arch];
44+
if (!target) throw new Error(`Unsupported arch: ${arch}`);
45+
return target;
46+
},
47+
},
48+
{
49+
name: "rg",
50+
version: "15.0.0",
51+
getUrl: (version, target) => {
52+
const ext = target.includes("windows") ? "zip" : "tar.gz";
53+
return `https://github.com/microsoft/ripgrep-prebuilt/releases/download/v${version}/ripgrep-v${version}-${target}.${ext}`;
54+
},
55+
getTarget: () => {
56+
const { platform, arch } = process;
57+
const targets = {
58+
darwin: { arm64: "aarch64-apple-darwin", x64: "x86_64-apple-darwin" },
59+
linux: {
60+
arm64: "aarch64-unknown-linux-musl",
61+
x64: "x86_64-unknown-linux-musl",
62+
},
63+
win32: {
64+
arm64: "aarch64-pc-windows-msvc",
65+
x64: "x86_64-pc-windows-msvc",
66+
},
67+
};
68+
const platformTargets = targets[platform];
69+
if (!platformTargets)
70+
throw new Error(`Unsupported platform: ${platform}`);
71+
const target = platformTargets[arch];
72+
if (!target) throw new Error(`Unsupported arch: ${arch}`);
73+
return target;
74+
},
75+
},
76+
];
77+
78+
async function downloadFile(url, destPath) {
79+
console.log(` Downloading: ${url}`);
80+
const response = await fetch(url, { redirect: "follow" });
81+
if (!response.ok) {
82+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
83+
}
84+
await pipeline(response.body, createWriteStream(destPath));
85+
console.log(` Saved to: ${destPath}`);
86+
}
87+
88+
async function extractArchive(archivePath, destDir) {
89+
console.log(` Extracting: ${archivePath}`);
90+
if (archivePath.endsWith(".zip")) {
91+
const { default: AdmZip } = await import("adm-zip");
92+
new AdmZip(archivePath).extractAllTo(destDir, true);
93+
} else {
94+
await extract({ file: archivePath, cwd: destDir });
95+
}
96+
}
97+
98+
function signForMacOS(binaryPath) {
99+
console.log(` Signing: ${binaryPath}`);
100+
try {
101+
execSync(`xattr -cr "${binaryPath}"`, { stdio: "pipe" });
102+
} catch {}
103+
execSync(`codesign --force --sign - "${binaryPath}"`, { stdio: "pipe" });
104+
}
105+
106+
async function downloadBinary(binary) {
107+
const binaryName =
108+
process.platform === "win32" ? `${binary.name}.exe` : binary.name;
109+
const binaryPath = join(DEST_DIR, binaryName);
110+
111+
console.log(`\n[${binary.name}] v${binary.version}`);
112+
113+
if (existsSync(binaryPath)) {
114+
console.log(` Already exists: ${binaryPath}`);
115+
return;
116+
}
117+
118+
const target = binary.getTarget();
119+
const url = binary.getUrl(binary.version, target);
120+
const archiveName = `${binary.name}-archive${url.endsWith(".zip") ? ".zip" : ".tar.gz"}`;
121+
const archivePath = join(DEST_DIR, archiveName);
122+
123+
console.log(` Platform: ${process.platform}/${process.arch} -> ${target}`);
124+
125+
await downloadFile(url, archivePath);
126+
await extractArchive(archivePath, DEST_DIR);
127+
rmSync(archivePath);
128+
129+
if (!existsSync(binaryPath)) {
130+
throw new Error(`Binary not found after extraction: ${binaryPath}`);
131+
}
132+
133+
if (process.platform !== "win32") {
134+
chmodSync(binaryPath, 0o755);
135+
}
136+
137+
if (process.platform === "darwin") {
138+
signForMacOS(binaryPath);
139+
}
140+
141+
console.log(` Ready: ${binaryPath}`);
142+
}
143+
144+
async function main() {
145+
console.log("Downloading binaries...");
146+
console.log(`Destination: ${DEST_DIR}`);
147+
148+
if (!existsSync(DEST_DIR)) {
149+
mkdirSync(DEST_DIR, { recursive: true });
150+
}
151+
152+
for (const binary of BINARIES) {
153+
await downloadBinary(binary);
154+
}
155+
156+
console.log("\nDone.");
157+
}
158+
159+
main().catch((err) => {
160+
console.error("\nFailed:", err.message);
161+
process.exit(1);
162+
});

‎apps/twig/scripts/download-codex-acp.mjs‎

Lines changed: 0 additions & 148 deletions
This file was deleted.

‎apps/twig/src/renderer/features/sessions/components/session-update/toolCallUtils.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,13 @@ export function truncateText(
9494
maxLength: number,
9595
ellipsis = "…",
9696
): string {
97+
if (typeof text !== "string") return String(text);
9798
if (text.length <= maxLength) return text;
9899
return `${text.slice(0, maxLength)}${ellipsis}`;
99100
}
100101

101102
export function getFilename(path: string): string {
103+
if (typeof path !== "string") return String(path);
102104
return path.split("/").pop() ?? path;
103105
}
104106

‎apps/twig/src/renderer/utils/path.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export function expandTildePath(path: string): string {
2+
if (typeof path !== "string") return String(path);
23
if (!path.startsWith("~")) return path;
34
// In renderer context, we can't access process.env directly
45
// For now, return the path as-is since the main process will handle expansion
@@ -8,7 +9,7 @@ export function expandTildePath(path: string): string {
89
}
910

1011
export function compactHomePath(text: string): string {
11-
// Replace all occurrences of home directory patterns with ~
12+
if (typeof text !== "string") return String(text);
1213
return text
1314
.replace(/\/Users\/[^/\s]+/g, "~")
1415
.replace(/\/home\/[^/\s]+/g, "~");

0 commit comments

Comments
 (0)