@avcodes/skilled
Advanced tools
+84
-14
@@ -13,2 +13,10 @@ #!/usr/bin/env node | ||
| function rmrf(dir) { | ||
| if (fs.rmSync) { | ||
| fs.rmSync(dir, { recursive: true, force: true }); | ||
| } else if (fs.existsSync(dir)) { | ||
| fs.rmdirSync(dir, { recursive: true }); | ||
| } | ||
| } | ||
| const REPO = "av/skilled"; | ||
@@ -45,2 +53,20 @@ const BINARY = "skilled"; | ||
| if (platform === "darwin" && arch === "amd64") { | ||
| throw new Error( | ||
| "Intel Mac (darwin-amd64) prebuilt binaries are not available.\n" + | ||
| "Options:\n" + | ||
| " - Use an Apple Silicon Mac (arm64)\n" + | ||
| " - Install from source: git clone https://github.com/av/skilled && cd skilled && bun run build" | ||
| ); | ||
| } | ||
| if (platform === "windows" && arch === "arm64") { | ||
| throw new Error( | ||
| "Windows ARM64 prebuilt binaries are not available.\n" + | ||
| "Options:\n" + | ||
| " - Use Windows x64 (the x64 binary runs on ARM64 via emulation)\n" + | ||
| " - Install from source: git clone https://github.com/av/skilled && cd skilled && bun run build" | ||
| ); | ||
| } | ||
| const artifact = `${BINARY}-${platform}-${arch}`; | ||
@@ -51,8 +77,12 @@ const ext = platform === "windows" ? "zip" : "tar.gz"; | ||
| function followRedirects(url) { | ||
| function followRedirects(url, maxRedirects = 10) { | ||
| return new Promise((resolve, reject) => { | ||
| if (maxRedirects <= 0) { | ||
| reject(new Error("Too many redirects (exceeded limit of 10)")); | ||
| return; | ||
| } | ||
| https | ||
| .get(url, { headers: { "User-Agent": "skilled-npm" } }, (res) => { | ||
| if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { | ||
| followRedirects(res.headers.location).then(resolve, reject); | ||
| followRedirects(res.headers.location, maxRedirects - 1).then(resolve, reject); | ||
| } else if (res.statusCode === 200) { | ||
@@ -71,14 +101,31 @@ resolve(res); | ||
| if (platform === "windows") { | ||
| const zipPath = path.join(destDir, "download.zip"); | ||
| const fileStream = fs.createWriteStream(zipPath); | ||
| await pipelineAsync(res, fileStream); | ||
| execSync(`tar -xf "${zipPath}" -C "${destDir}"`); | ||
| fs.unlinkSync(zipPath); | ||
| } else { | ||
| const tarPath = path.join(destDir, "download.tar.gz"); | ||
| const fileStream = fs.createWriteStream(tarPath); | ||
| await pipelineAsync(res, fileStream); | ||
| execSync(`tar xzf "${tarPath}" -C "${destDir}"`); | ||
| fs.unlinkSync(tarPath); | ||
| // Extract into a temp directory first to avoid leaving corrupted binaries | ||
| // on partial failure (network drop, disk full, corrupt archive). | ||
| const tmpDir = destDir + ".tmp"; | ||
| rmrf(tmpDir); | ||
| fs.mkdirSync(tmpDir, { recursive: true }); | ||
| try { | ||
| if (platform === "windows") { | ||
| const zipPath = path.join(tmpDir, "download.zip"); | ||
| const fileStream = fs.createWriteStream(zipPath); | ||
| await pipelineAsync(res, fileStream); | ||
| execSync(`tar -xf "${zipPath}" -C "${tmpDir}"`); | ||
| fs.unlinkSync(zipPath); | ||
| } else { | ||
| const tarPath = path.join(tmpDir, "download.tar.gz"); | ||
| const fileStream = fs.createWriteStream(tarPath); | ||
| await pipelineAsync(res, fileStream); | ||
| execSync(`tar xzf "${tarPath}" -C "${tmpDir}"`); | ||
| fs.unlinkSync(tarPath); | ||
| } | ||
| // Move extracted files to final destination only after successful extraction | ||
| const files = fs.readdirSync(tmpDir); | ||
| for (const file of files) { | ||
| fs.renameSync(path.join(tmpDir, file), path.join(destDir, file)); | ||
| } | ||
| } finally { | ||
| // Clean up temp directory regardless of success/failure | ||
| rmrf(tmpDir); | ||
| } | ||
@@ -119,3 +166,26 @@ } | ||
| } | ||
| } else { | ||
| // On Windows, npm's bin shim runs `node bin/skilled` which hits the stub script. | ||
| // The actual binary is `bin/skilled.exe`. Replace the stub with a launcher that | ||
| // spawns the .exe, passing through args, stdio, and exit code. | ||
| const launcherScript = `#!/usr/bin/env node | ||
| "use strict"; | ||
| var cp = require("child_process"); | ||
| var path = require("path"); | ||
| var exe = path.join(__dirname, "${BINARY}.exe"); | ||
| var r = cp.spawnSync(exe, process.argv.slice(2), { stdio: "inherit" }); | ||
| if (r.error) { | ||
| if (r.error.code === "ENOENT") { | ||
| console.error("${BINARY}: binary not found at " + exe); | ||
| console.error("Run 'npm rebuild @avcodes/${BINARY}' or reinstall."); | ||
| } else { | ||
| console.error("${BINARY}: " + r.error.message); | ||
| } | ||
| process.exit(1); | ||
| } | ||
| process.exit(r.status !== null ? r.status : 1); | ||
| `; | ||
| const launcherPath = path.join(binDir, BINARY); | ||
| fs.writeFileSync(launcherPath, launcherScript, { mode: 0o755 }); | ||
| } | ||
@@ -122,0 +192,0 @@ console.log(`Successfully installed ${BINARY} to ${binaryPath}`); |
+1
-1
| { | ||
| "name": "@avcodes/skilled", | ||
| "version": "0.3.0", | ||
| "version": "0.3.1", | ||
| "description": "TUI dashboard for skill usage stats across AI coding tools", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
10173
33.64%167
63.73%4
33.33%3
50%