ace-tool-rs 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +49 -60
  2. package/package.json +14 -3
  3. package/run.js +32 -524
package/README.md CHANGED
@@ -1,88 +1,77 @@
1
- # ace-tool-rs npm shim
1
+ # ace-tool-rs
2
2
 
3
- This npm package provides a convenient way to install and run `ace-tool-rs` via npm/npx.
3
+ MCP server for codebase indexing, semantic search, and prompt enhancement.
4
4
 
5
- ## How It Works
6
-
7
- This is a **shim package** that automatically downloads the appropriate pre-built binary for your platform from GitHub Releases when first run. The binary is cached locally for subsequent invocations.
8
-
9
- ## Quick Start
5
+ ## Installation
10
6
 
11
7
  ```bash
12
- # Run directly with npx (no installation needed)
13
- npx ace-tool-rs --base-url <API_URL> --token <AUTH_TOKEN>
14
-
15
- # Or install globally
8
+ # Install globally
16
9
  npm install -g ace-tool-rs
17
- ace-tool-rs --base-url <API_URL> --token <AUTH_TOKEN>
18
- ```
19
10
 
20
- ## Supported Platforms
11
+ # Or run directly with npx
12
+ npx ace-tool-rs --help
13
+ ```
21
14
 
22
- | Platform | Architecture | Status |
23
- |----------|--------------|--------|
24
- | Windows | x64 | Supported |
25
- | macOS | x64, ARM64 | Supported (universal binary) |
26
- | Linux | x64 | Supported |
15
+ ## How It Works
27
16
 
28
- ## Cache Location
17
+ This package uses platform-specific optional dependencies to provide pre-built binaries. When you install `ace-tool-rs`, npm automatically downloads the correct binary for your platform.
29
18
 
30
- Downloaded binaries are cached in platform-specific directories:
19
+ ### Supported Platforms
31
20
 
32
- | Platform | Cache Path |
33
- |----------|------------|
34
- | Windows | `%LOCALAPPDATA%\ace-tool-rs\<version>\` |
35
- | macOS | `~/Library/Caches/ace-tool-rs/<version>/` |
36
- | Linux | `$XDG_CACHE_HOME/ace-tool-rs/<version>/` or `~/.cache/ace-tool-rs/<version>/` |
21
+ | Platform | Architecture | Package |
22
+ |----------|--------------|---------|
23
+ | macOS | x64, ARM64 | `@ace-tool-rs/darwin-universal` |
24
+ | Linux | x64 | `@ace-tool-rs/linux-x64` |
25
+ | Linux | ARM64 | `@ace-tool-rs/linux-arm64` |
26
+ | Windows | x64 | `@ace-tool-rs/win32-x64` |
27
+ | Windows | ARM64 | `@ace-tool-rs/win32-arm64` |
37
28
 
38
- The cache is versioned, so upgrading the npm package will download a new binary matching that version.
29
+ ## Usage
39
30
 
40
- ## Environment Variables
31
+ ```bash
32
+ ace-tool-rs --base-url <API_URL> --token <AUTH_TOKEN>
33
+ ```
41
34
 
42
- | Variable | Description |
43
- |----------|-------------|
44
- | `GITHUB_TOKEN` | GitHub personal access token to avoid rate limits when downloading |
35
+ ## Troubleshooting
45
36
 
46
- ## Requirements
37
+ ### Binary not found
47
38
 
48
- - Node.js 14.14.0 or later
49
- - `tar` command (Linux/macOS) or PowerShell 5.0+ (Windows) for extraction
39
+ If the platform-specific package failed to install, you can install it manually:
50
40
 
51
- ## Troubleshooting
41
+ ```bash
42
+ # For Linux x64
43
+ npm install @ace-tool-rs/linux-x64
52
44
 
53
- ### Download Fails
45
+ # For macOS
46
+ npm install @ace-tool-rs/darwin-universal
54
47
 
55
- If automatic download fails, you can:
48
+ # For Windows x64
49
+ npm install @ace-tool-rs/win32-x64
50
+ ```
56
51
 
57
- 1. **Manual download**: Download the appropriate binary from [GitHub Releases](https://github.com/missdeer/ace-tool-rs/releases) and place it in the cache directory.
52
+ ### Alternative installation
58
53
 
59
- 2. **Install via Cargo**: If you have Rust installed:
60
- ```bash
61
- cargo install ace-tool-rs
62
- ```
54
+ If you have Rust installed, you can build from source:
63
55
 
64
- 3. **Set GITHUB_TOKEN**: If you're hitting GitHub API rate limits:
65
- ```bash
66
- export GITHUB_TOKEN=your_github_token
67
- npx ace-tool-rs --base-url <API_URL> --token <AUTH_TOKEN>
68
- ```
56
+ ```bash
57
+ cargo install ace-tool-rs
58
+ ```
69
59
 
70
- ### Binary Not Found After Extraction
60
+ ## License
71
61
 
72
- Ensure your system has the required extraction tools:
73
- - **Windows**: PowerShell 5.0+ with `Expand-Archive` cmdlet
74
- - **Linux/macOS**: `tar` command
62
+ GPL-3.0-only
75
63
 
76
- ## How the Shim Works
64
+ For commercial use, please contact missdeer@gmail.com for licensing options.
77
65
 
78
- 1. On first run, checks if the binary exists in the cache directory
79
- 2. If not found, queries GitHub API for the release matching the npm package version
80
- 3. Downloads the platform-appropriate archive (`.zip` for Windows, `.tar.gz` for others)
81
- 4. Extracts the binary to the cache directory
82
- 5. Executes the binary with all provided arguments
66
+ ## Verifying Downloads
83
67
 
84
- ## License
68
+ Each GitHub release includes a `SHA256SUMS` file for integrity verification:
85
69
 
86
- This package is part of [ace-tool-rs](https://github.com/missdeer/ace-tool-rs) and is licensed under GPL-3.0.
70
+ ```bash
71
+ # Download the binary and checksum file
72
+ curl -LO https://github.com/missdeer/ace-tool-rs/releases/latest/download/ace-tool-rs_Linux_x86_64.tar.gz
73
+ curl -LO https://github.com/missdeer/ace-tool-rs/releases/latest/download/SHA256SUMS
87
74
 
88
- For commercial use, please contact missdeer@gmail.com for licensing options.
75
+ # Verify the checksum
76
+ sha256sum -c SHA256SUMS --ignore-missing
77
+ ```
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "ace-tool-rs",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "MCP server for codebase indexing, semantic search, and prompt enhancement",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
5
8
  "repository": {
6
9
  "type": "git",
7
10
  "url": "git+https://github.com/missdeer/ace-tool-rs.git"
@@ -25,7 +28,8 @@
25
28
  "ace-tool-rs": "run.js"
26
29
  },
27
30
  "files": [
28
- "run.js"
31
+ "run.js",
32
+ "README.md"
29
33
  ],
30
34
  "engines": {
31
35
  "node": ">=14.14.0"
@@ -38,5 +42,12 @@
38
42
  "cpu": [
39
43
  "x64",
40
44
  "arm64"
41
- ]
45
+ ],
46
+ "optionalDependencies": {
47
+ "@ace-tool-rs/darwin-universal": "0.1.9",
48
+ "@ace-tool-rs/linux-x64": "0.1.9",
49
+ "@ace-tool-rs/linux-arm64": "0.1.9",
50
+ "@ace-tool-rs/win32-x64": "0.1.9",
51
+ "@ace-tool-rs/win32-arm64": "0.1.9"
52
+ }
42
53
  }
package/run.js CHANGED
@@ -2,537 +2,48 @@
2
2
 
3
3
  const { spawn } = require("child_process");
4
4
  const path = require("path");
5
- const fs = require("fs");
6
- const https = require("https");
7
5
  const os = require("os");
8
- const crypto = require("crypto");
9
6
 
10
- const PACKAGE_NAME = "ace-tool-rs";
11
- const REPO_OWNER = "missdeer";
12
- const REPO_NAME = "ace-tool-rs";
13
- const MAX_REDIRECTS = 10;
14
- const REQUEST_TIMEOUT = 60000; // 60 seconds
15
- const MAX_RETRIES = 3;
16
- const RETRY_DELAY = 1000; // 1 second
17
-
18
- // Helper for retry with exponential backoff
19
- async function withRetry(fn, retries = MAX_RETRIES) {
20
- let lastError;
21
- for (let attempt = 0; attempt < retries; attempt++) {
22
- try {
23
- return await fn();
24
- } catch (err) {
25
- lastError = err;
26
- // Don't retry on non-retryable errors
27
- if (
28
- err.message.includes("Unsupported") ||
29
- err.message.includes("rate limit") ||
30
- err.message.includes("404")
31
- ) {
32
- throw err;
33
- }
34
- if (attempt < retries - 1) {
35
- const delay = RETRY_DELAY * Math.pow(2, attempt);
36
- await new Promise((resolve) => setTimeout(resolve, delay));
37
- }
38
- }
39
- }
40
- throw lastError;
41
- }
42
-
43
- // Cache package version
44
- let cachedVersion = null;
45
-
46
- // Get package version from package.json
47
- function getPackageVersion() {
48
- if (cachedVersion === null) {
49
- cachedVersion = require("./package.json").version;
50
- }
51
- return cachedVersion;
52
- }
53
-
54
- // Get cache directory based on OS
55
- function getCacheDir() {
56
- const homeDir = os.homedir();
57
- const version = getPackageVersion();
58
- let baseDir;
59
-
60
- switch (process.platform) {
61
- case "win32":
62
- baseDir = path.join(
63
- process.env.LOCALAPPDATA || path.join(homeDir, "AppData", "Local"),
64
- PACKAGE_NAME
65
- );
66
- break;
67
- case "darwin":
68
- baseDir = path.join(homeDir, "Library", "Caches", PACKAGE_NAME);
69
- break;
70
- default:
71
- baseDir = path.join(
72
- process.env.XDG_CACHE_HOME || path.join(homeDir, ".cache"),
73
- PACKAGE_NAME
74
- );
75
- }
76
-
77
- // Include version in cache path to handle upgrades
78
- return path.join(baseDir, version);
79
- }
80
-
81
- // Get asset name based on platform (matching release.yml)
82
- function getAssetName() {
83
- const platform = process.platform;
84
- const arch = process.arch;
85
-
86
- switch (platform) {
87
- case "darwin":
88
- // macOS uses universal binary (supports both x64 and arm64)
89
- return "ace-tool-rs_Darwin_universal.tar.gz";
90
- case "linux":
91
- if (arch === "x64") {
92
- return "ace-tool-rs_Linux_x86_64.tar.gz";
93
- } else if (arch === "arm64") {
94
- return "ace-tool-rs_Linux_aarch64.tar.gz";
95
- }
96
- throw new Error(
97
- `Unsupported architecture: ${arch} on Linux. Only x64 and arm64 are supported.`
98
- );
99
- case "win32":
100
- if (arch === "x64") {
101
- return "ace-tool-rs_Windows_x86_64.zip";
102
- } else if (arch === "arm64") {
103
- return "ace-tool-rs_Windows_aarch64.zip";
104
- }
105
- throw new Error(
106
- `Unsupported architecture: ${arch} on Windows. Only x64 and arm64 are supported.`
107
- );
108
- default:
109
- throw new Error(`Unsupported platform: ${platform}`);
110
- }
111
- }
112
-
113
- function getBinaryName() {
114
- return process.platform === "win32" ? `${PACKAGE_NAME}.exe` : PACKAGE_NAME;
115
- }
116
-
117
- function httpsGet(url, options = {}, redirectCount = 0) {
118
- return new Promise((resolve, reject) => {
119
- if (redirectCount > MAX_REDIRECTS) {
120
- reject(new Error("Too many redirects"));
121
- return;
122
- }
123
-
124
- const req = https.get(url, options, (res) => {
125
- // Handle redirects
126
- if (
127
- res.statusCode >= 300 &&
128
- res.statusCode < 400 &&
129
- res.headers.location
130
- ) {
131
- // Consume response to free up connection
132
- res.resume();
133
- const redirectUrl = res.headers.location;
134
- // Only follow HTTPS redirects
135
- if (!redirectUrl.startsWith("https://")) {
136
- reject(new Error(`Insecure redirect to: ${redirectUrl}`));
137
- return;
138
- }
139
- httpsGet(redirectUrl, options, redirectCount + 1)
140
- .then(resolve)
141
- .catch(reject);
142
- return;
143
- }
144
-
145
- if (res.statusCode === 403) {
146
- res.resume();
147
- reject(
148
- new Error(
149
- "GitHub API rate limit exceeded. Please try again later or set GITHUB_TOKEN environment variable."
150
- )
151
- );
152
- return;
153
- }
154
-
155
- if (res.statusCode !== 200) {
156
- res.resume();
157
- reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
158
- return;
159
- }
160
-
161
- const chunks = [];
162
- res.on("data", (chunk) => chunks.push(chunk));
163
- res.on("end", () => resolve(Buffer.concat(chunks)));
164
- res.on("error", reject);
165
- });
166
-
167
- req.on("error", reject);
168
- req.setTimeout(REQUEST_TIMEOUT, () => {
169
- req.destroy();
170
- reject(new Error("Request timeout"));
171
- });
172
- });
173
- }
174
-
175
- // Parse JSON with helpful error message
176
- function parseJSON(data, context) {
177
- try {
178
- return JSON.parse(data.toString());
179
- } catch (err) {
180
- const preview = data.toString().slice(0, 200);
181
- throw new Error(
182
- `Failed to parse ${context} response. GitHub may be experiencing issues. Response preview: ${preview}`
183
- );
184
- }
185
- }
186
-
187
- async function getReleaseByTag(version) {
188
- const tag = `v${version}`;
189
- const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${tag}`;
190
- const options = {
191
- headers: {
192
- "User-Agent": PACKAGE_NAME,
193
- Accept: "application/vnd.github.v3+json",
194
- ...(process.env.GITHUB_TOKEN && {
195
- Authorization: `token ${process.env.GITHUB_TOKEN}`,
196
- }),
197
- },
198
- };
199
-
200
- try {
201
- const data = await httpsGet(url, options);
202
- return parseJSON(data, "release");
203
- } catch (error) {
204
- // If the specific version tag doesn't exist, fall back to latest
205
- if (error.message.includes("404")) {
206
- console.log(
207
- `Release v${version} not found, falling back to latest release...`
208
- );
209
- return getLatestRelease();
210
- }
211
- throw error;
212
- }
213
- }
214
-
215
- async function getLatestRelease() {
216
- const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`;
217
- const options = {
218
- headers: {
219
- "User-Agent": PACKAGE_NAME,
220
- Accept: "application/vnd.github.v3+json",
221
- ...(process.env.GITHUB_TOKEN && {
222
- Authorization: `token ${process.env.GITHUB_TOKEN}`,
223
- }),
224
- },
225
- };
226
-
227
- const data = await httpsGet(url, options);
228
- return parseJSON(data, "latest release");
229
- }
230
-
231
- function downloadToFile(url, destPath, options = {}, redirectCount = 0) {
232
- return new Promise((resolve, reject) => {
233
- if (redirectCount > MAX_REDIRECTS) {
234
- reject(new Error("Too many redirects"));
235
- return;
236
- }
237
-
238
- const file = fs.createWriteStream(destPath);
239
- const req = https.get(url, options, (res) => {
240
- // Handle redirects
241
- if (
242
- res.statusCode >= 300 &&
243
- res.statusCode < 400 &&
244
- res.headers.location
245
- ) {
246
- res.resume(); // Consume response
247
- file.close(() => {
248
- try {
249
- fs.unlinkSync(destPath);
250
- } catch {}
251
- const redirectUrl = res.headers.location;
252
- if (!redirectUrl.startsWith("https://")) {
253
- reject(new Error(`Insecure redirect to: ${redirectUrl}`));
254
- return;
255
- }
256
- downloadToFile(redirectUrl, destPath, options, redirectCount + 1)
257
- .then(resolve)
258
- .catch(reject);
259
- });
260
- return;
261
- }
262
-
263
- if (res.statusCode !== 200) {
264
- res.resume(); // Consume response
265
- file.close(() => {
266
- try {
267
- fs.unlinkSync(destPath);
268
- } catch {}
269
- reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
270
- });
271
- return;
272
- }
273
-
274
- res.pipe(file);
275
- file.on("finish", () => {
276
- file.close(() => resolve());
277
- });
278
- file.on("error", (err) => {
279
- file.close(() => {
280
- try {
281
- fs.unlinkSync(destPath);
282
- } catch {}
283
- reject(err);
284
- });
285
- });
286
- });
287
-
288
- req.on("error", (err) => {
289
- file.close(() => {
290
- try {
291
- fs.unlinkSync(destPath);
292
- } catch {}
293
- reject(err);
294
- });
295
- });
296
-
297
- req.setTimeout(REQUEST_TIMEOUT, () => {
298
- req.destroy();
299
- file.close(() => {
300
- try {
301
- fs.unlinkSync(destPath);
302
- } catch {}
303
- reject(new Error("Download timeout"));
304
- });
305
- });
306
- });
307
- }
308
-
309
- async function extractTarGz(archivePath, destDir) {
310
- return new Promise((resolve, reject) => {
311
- const tar = spawn("tar", ["-xzf", archivePath, "-C", destDir], {
312
- stdio: "inherit",
313
- });
314
- tar.on("close", (code) => {
315
- if (code === 0) resolve();
316
- else reject(new Error(`tar exited with code ${code}`));
317
- });
318
- tar.on("error", (err) => {
319
- if (err.code === "ENOENT") {
320
- reject(new Error("tar command not found. Please install tar."));
321
- } else {
322
- reject(err);
323
- }
324
- });
325
- });
326
- }
327
-
328
- async function extractZip(archivePath, destDir) {
329
- return new Promise((resolve, reject) => {
330
- // Escape paths for PowerShell: escape backticks and single quotes
331
- const escapePath = (p) => p.replace(/`/g, "``").replace(/'/g, "''");
332
- const unzipProcess = spawn(
333
- "powershell",
334
- [
335
- "-NoProfile",
336
- "-ExecutionPolicy",
337
- "Bypass",
338
- "-Command",
339
- `Expand-Archive -LiteralPath '${escapePath(archivePath)}' -DestinationPath '${escapePath(destDir)}' -Force`,
340
- ],
341
- { stdio: "inherit" }
342
- );
343
- unzipProcess.on("close", (code) => {
344
- if (code === 0) resolve();
345
- else reject(new Error(`PowerShell Expand-Archive exited with code ${code}`));
346
- });
347
- unzipProcess.on("error", (err) => {
348
- if (err.code === "ENOENT") {
349
- reject(new Error("PowerShell not found. Please install PowerShell 5.0+."));
350
- } else {
351
- reject(err);
352
- }
353
- });
354
- });
355
- }
356
-
357
- // Move file with fallback for cross-device moves
358
- function moveFile(src, dest) {
359
- try {
360
- fs.renameSync(src, dest);
361
- } catch (err) {
362
- if (err.code === "EXDEV") {
363
- // Cross-device move: copy + delete
364
- fs.copyFileSync(src, dest);
365
- fs.unlinkSync(src);
366
- } else {
367
- throw err;
368
- }
369
- }
370
- }
371
-
372
- // Create a lock file to prevent concurrent downloads
373
- function acquireLock(lockPath) {
374
- try {
375
- fs.writeFileSync(lockPath, process.pid.toString(), { flag: "wx" });
376
- return true;
377
- } catch (err) {
378
- if (err.code === "EEXIST") {
379
- // Check if the process that created the lock is still running
380
- try {
381
- const pid = parseInt(fs.readFileSync(lockPath, "utf8"), 10);
382
- try {
383
- process.kill(pid, 0); // Check if process exists
384
- return false; // Process is still running
385
- } catch {
386
- // Process is not running, remove stale lock
387
- fs.unlinkSync(lockPath);
388
- return acquireLock(lockPath);
389
- }
390
- } catch {
391
- return false;
392
- }
393
- }
394
- throw err;
395
- }
396
- }
397
-
398
- function releaseLock(lockPath) {
399
- try {
400
- fs.unlinkSync(lockPath);
401
- } catch {
402
- // Ignore errors
403
- }
404
- }
405
-
406
- async function downloadAndExtract(cacheDir) {
407
- const assetName = getAssetName();
408
- const binaryName = getBinaryName();
409
- const binaryPath = path.join(cacheDir, binaryName);
410
- const lockPath = path.join(cacheDir, ".lock");
411
- const tempId = crypto.randomBytes(8).toString("hex");
412
-
413
- // Ensure cache directory exists
414
- fs.mkdirSync(cacheDir, { recursive: true });
415
-
416
- // Try to acquire lock
417
- if (!acquireLock(lockPath)) {
418
- // Wait for other process to complete
419
- console.log("Another process is downloading, waiting...");
420
- let attempts = 0;
421
- while (!fs.existsSync(binaryPath) && attempts < 60) {
422
- await new Promise((resolve) => setTimeout(resolve, 1000));
423
- attempts++;
424
- }
425
- if (fs.existsSync(binaryPath)) {
426
- return binaryPath;
427
- }
428
- throw new Error("Timeout waiting for download to complete");
7
+ const PLATFORMS = {
8
+ "darwin-x64": "@ace-tool-rs/darwin-universal",
9
+ "darwin-arm64": "@ace-tool-rs/darwin-universal",
10
+ "linux-x64": "@ace-tool-rs/linux-x64",
11
+ "linux-arm64": "@ace-tool-rs/linux-arm64",
12
+ "win32-x64": "@ace-tool-rs/win32-x64",
13
+ "win32-arm64": "@ace-tool-rs/win32-arm64",
14
+ };
15
+
16
+ function getBinaryPath() {
17
+ const platformKey = `${process.platform}-${process.arch}`;
18
+ const pkgName = PLATFORMS[platformKey];
19
+
20
+ if (!pkgName) {
21
+ console.error(`Unsupported platform: ${process.platform}-${process.arch}`);
22
+ console.error("Supported platforms: " + Object.keys(PLATFORMS).join(", "));
23
+ process.exit(1);
429
24
  }
430
25
 
431
26
  try {
432
- // Double check after acquiring lock
433
- if (fs.existsSync(binaryPath)) {
434
- return binaryPath;
435
- }
436
-
437
- // Get release for the specific version (with retry)
438
- const version = getPackageVersion();
439
- console.log(`Downloading ${PACKAGE_NAME} v${version}...`);
440
-
441
- const release = await withRetry(() => getReleaseByTag(version));
442
- const asset = release.assets.find((a) => a.name === assetName);
443
-
444
- if (!asset) {
445
- const availableAssets = release.assets.map((a) => a.name).join(", ");
446
- throw new Error(
447
- `No matching asset found: ${assetName}. Available: ${availableAssets}`
448
- );
449
- }
450
-
451
- // Download to temporary file first
452
- const tempArchive = path.join(cacheDir, `${tempId}-${assetName}`);
453
- const tempExtractDir = path.join(cacheDir, `${tempId}-extract`);
454
-
455
- const downloadOptions = {
456
- headers: {
457
- "User-Agent": PACKAGE_NAME,
458
- Accept: "application/octet-stream",
459
- ...(process.env.GITHUB_TOKEN && {
460
- Authorization: `token ${process.env.GITHUB_TOKEN}`,
461
- }),
462
- },
463
- };
464
-
465
- await withRetry(() =>
466
- downloadToFile(asset.browser_download_url, tempArchive, downloadOptions)
467
- );
468
-
469
- // Extract to temporary directory
470
- console.log("Extracting...");
471
- fs.mkdirSync(tempExtractDir, { recursive: true });
472
-
473
- if (assetName.endsWith(".zip")) {
474
- await extractZip(tempArchive, tempExtractDir);
475
- } else {
476
- await extractTarGz(tempArchive, tempExtractDir);
477
- }
478
-
479
- // Find the binary in the extracted directory
480
- const extractedBinary = path.join(tempExtractDir, binaryName);
481
- if (!fs.existsSync(extractedBinary)) {
482
- throw new Error(
483
- `Binary not found in archive. Expected: ${binaryName} in extracted contents.`
484
- );
485
- }
486
-
487
- // Atomic move to final location (with cross-device fallback)
488
- moveFile(extractedBinary, binaryPath);
489
-
490
- // Make binary executable on Unix
491
- if (process.platform !== "win32") {
492
- fs.chmodSync(binaryPath, 0o755);
493
- }
494
-
495
- // Clean up
496
- fs.unlinkSync(tempArchive);
497
- fs.rmSync(tempExtractDir, { recursive: true, force: true });
498
-
499
- console.log(`Installed ${PACKAGE_NAME} to ${binaryPath}`);
500
- return binaryPath;
501
- } catch (error) {
502
- console.error(`Failed to download ${PACKAGE_NAME}: ${error.message}`);
27
+ const pkgPath = require.resolve(`${pkgName}/package.json`);
28
+ const binName = process.platform === "win32" ? "ace-tool-rs.exe" : "ace-tool-rs";
29
+ return path.join(path.dirname(pkgPath), "bin", binName);
30
+ } catch (e) {
31
+ console.error(`Failed to find platform package: ${pkgName}`);
32
+ console.error("This may happen if npm failed to install the optional dependency.");
503
33
  console.error("");
504
- console.error("You can install manually:");
505
- console.error(
506
- " 1. Download from https://github.com/missdeer/ace-tool-rs/releases"
507
- );
508
- console.error(` 2. Place binary at: ${binaryPath}`);
34
+ console.error("Try reinstalling:");
35
+ console.error(" npm install ace-tool-rs");
509
36
  console.error("");
510
- console.error("Or install via cargo:");
511
- console.error(" cargo install ace-tool-rs");
37
+ console.error("Or install the platform package directly:");
38
+ console.error(` npm install ${pkgName}`);
512
39
  process.exit(1);
513
- } finally {
514
- releaseLock(lockPath);
515
40
  }
516
41
  }
517
42
 
518
- async function run() {
519
- const cacheDir = getCacheDir();
520
- const binaryName = getBinaryName();
521
- const binaryPath = path.join(cacheDir, binaryName);
522
-
523
- // Check if binary exists in cache
524
- if (!fs.existsSync(binaryPath)) {
525
- await downloadAndExtract(cacheDir);
526
- }
527
-
528
- // Final check that binary exists
529
- if (!fs.existsSync(binaryPath)) {
530
- console.error(`Binary not found at ${binaryPath}`);
531
- process.exit(1);
532
- }
533
-
534
- // Run the binary with all arguments
43
+ function run() {
44
+ const binaryPath = getBinaryPath();
535
45
  const args = process.argv.slice(2);
46
+
536
47
  const child = spawn(binaryPath, args, {
537
48
  stdio: "inherit",
538
49
  env: process.env,
@@ -549,7 +60,7 @@ async function run() {
549
60
  });
550
61
 
551
62
  child.on("error", (error) => {
552
- console.error(`Failed to start ${PACKAGE_NAME}: ${error.message}`);
63
+ console.error(`Failed to start ace-tool-rs: ${error.message}`);
553
64
  process.exit(1);
554
65
  });
555
66
 
@@ -561,7 +72,4 @@ async function run() {
561
72
  });
562
73
  }
563
74
 
564
- run().catch((error) => {
565
- console.error(error);
566
- process.exit(1);
567
- });
75
+ run();