npm
cargo
This commit is contained in:
Hari 2026-03-25 23:18:28 -04:00 committed by GitHub
parent 425a71095a
commit 714e34ba19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 849 additions and 66 deletions

36
npm/deskctl-cli/README.md Normal file
View file

@ -0,0 +1,36 @@
# deskctl-cli
`deskctl-cli` installs the `deskctl` command for Linux X11 systems.
## Install
```bash
npm install -g deskctl-cli
```
After install, run:
```bash
deskctl --help
```
One-shot usage is also supported:
```bash
npx deskctl-cli --help
```
## Runtime Support
- Linux
- X11 session
- currently packaged release asset: `linux-x64`
`deskctl-cli` downloads the matching GitHub Release binary during install.
Unsupported targets fail during install with a clear runtime support error instead of installing a broken command.
If you want the Rust source-install path instead, use:
```bash
cargo install deskctl
```

View file

@ -0,0 +1,36 @@
#!/usr/bin/env node
const fs = require("node:fs");
const { spawn } = require("node:child_process");
const { readPackageJson, releaseTag, supportedTarget, vendorBinaryPath } = require("../scripts/support");
function main() {
const pkg = readPackageJson();
const target = supportedTarget();
const binaryPath = vendorBinaryPath(target);
if (!fs.existsSync(binaryPath)) {
console.error(
[
"deskctl binary is missing from the npm package install.",
`Expected: ${binaryPath}`,
`Package version: ${pkg.version}`,
`Release tag: ${releaseTag(pkg)}`,
"Try reinstalling deskctl-cli or check that your target is supported."
].join("\n")
);
process.exit(1);
}
const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit" });
child.on("exit", (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
return;
}
process.exit(code ?? 1);
});
}
main();

View file

@ -0,0 +1,36 @@
{
"name": "deskctl-cli",
"version": "0.1.5",
"description": "Installable deskctl CLI package for Linux X11 agents",
"license": "MIT",
"homepage": "https://github.com/harivansh-afk/deskctl",
"repository": {
"type": "git",
"url": "git+https://github.com/harivansh-afk/deskctl.git"
},
"bugs": {
"url": "https://github.com/harivansh-afk/deskctl/issues"
},
"engines": {
"node": ">=18"
},
"bin": {
"deskctl": "bin/deskctl.js"
},
"files": [
"README.md",
"bin",
"scripts"
],
"scripts": {
"postinstall": "node scripts/postinstall.js",
"validate": "node scripts/validate-package.js"
},
"keywords": [
"deskctl",
"x11",
"desktop",
"automation",
"cli"
]
}

View file

@ -0,0 +1,49 @@
const fs = require("node:fs");
const {
checksumsUrl,
checksumForAsset,
download,
ensureVendorDir,
installLocalBinary,
readPackageJson,
releaseAssetUrl,
releaseTag,
sha256,
supportedTarget,
vendorBinaryPath
} = require("./support");
async function main() {
const pkg = readPackageJson();
const target = supportedTarget();
const targetPath = vendorBinaryPath(target);
ensureVendorDir();
if (process.env.DESKCTL_BINARY_PATH) {
installLocalBinary(process.env.DESKCTL_BINARY_PATH, targetPath);
return;
}
const tag = releaseTag(pkg);
const assetUrl = releaseAssetUrl(tag, target.assetName);
const checksumText = (await download(checksumsUrl(tag))).toString("utf8");
const expectedSha = checksumForAsset(checksumText, target.assetName);
const asset = await download(assetUrl);
const actualSha = sha256(asset);
if (actualSha !== expectedSha) {
throw new Error(
`Checksum mismatch for ${target.assetName}. Expected ${expectedSha}, got ${actualSha}.`
);
}
fs.writeFileSync(targetPath, asset);
fs.chmodSync(targetPath, 0o755);
}
main().catch((error) => {
console.error(`deskctl-cli install failed: ${error.message}`);
process.exit(1);
});

View file

@ -0,0 +1,120 @@
const crypto = require("node:crypto");
const fs = require("node:fs");
const path = require("node:path");
const https = require("node:https");
const PACKAGE_ROOT = path.resolve(__dirname, "..");
const VENDOR_DIR = path.join(PACKAGE_ROOT, "vendor");
const PACKAGE_JSON = path.join(PACKAGE_ROOT, "package.json");
function readPackageJson() {
return JSON.parse(fs.readFileSync(PACKAGE_JSON, "utf8"));
}
function releaseTag(pkg) {
return process.env.DESKCTL_RELEASE_TAG || `v${pkg.version}`;
}
function supportedTarget(platform = process.platform, arch = process.arch) {
if (platform === "linux" && arch === "x64") {
return {
platform,
arch,
assetName: "deskctl-linux-x86_64",
binaryName: "deskctl-linux-x86_64"
};
}
throw new Error(
`deskctl-cli currently supports linux-x64 only. Received ${platform}-${arch}.`
);
}
function vendorBinaryPath(target) {
return path.join(VENDOR_DIR, target.binaryName);
}
function releaseBaseUrl(tag) {
return (
process.env.DESKCTL_RELEASE_BASE_URL ||
`https://github.com/harivansh-afk/deskctl/releases/download/${tag}`
);
}
function releaseAssetUrl(tag, assetName) {
return process.env.DESKCTL_DOWNLOAD_URL || `${releaseBaseUrl(tag)}/${assetName}`;
}
function checksumsUrl(tag) {
return `${releaseBaseUrl(tag)}/checksums.txt`;
}
function ensureVendorDir() {
fs.mkdirSync(VENDOR_DIR, { recursive: true });
}
function checksumForAsset(contents, assetName) {
const line = contents
.split("\n")
.map((value) => value.trim())
.find((value) => value.endsWith(` ${assetName}`) || value.endsWith(` *${assetName}`));
if (!line) {
throw new Error(`Could not find checksum entry for ${assetName}.`);
}
return line.split(/\s+/)[0];
}
function sha256(buffer) {
return crypto.createHash("sha256").update(buffer).digest("hex");
}
function download(url) {
return new Promise((resolve, reject) => {
https
.get(url, (response) => {
if (
response.statusCode &&
response.statusCode >= 300 &&
response.statusCode < 400 &&
response.headers.location
) {
response.resume();
resolve(download(response.headers.location));
return;
}
if (response.statusCode !== 200) {
reject(new Error(`Download failed for ${url}: HTTP ${response.statusCode}`));
return;
}
const chunks = [];
response.on("data", (chunk) => chunks.push(chunk));
response.on("end", () => resolve(Buffer.concat(chunks)));
})
.on("error", reject);
});
}
function installLocalBinary(sourcePath, targetPath) {
fs.copyFileSync(sourcePath, targetPath);
fs.chmodSync(targetPath, 0o755);
}
module.exports = {
PACKAGE_ROOT,
VENDOR_DIR,
checksumsUrl,
checksumForAsset,
download,
ensureVendorDir,
installLocalBinary,
readPackageJson,
releaseAssetUrl,
releaseTag,
sha256,
supportedTarget,
vendorBinaryPath
};

View file

@ -0,0 +1,40 @@
const fs = require("node:fs");
const path = require("node:path");
const { readPackageJson, supportedTarget, vendorBinaryPath } = require("./support");
function readCargoVersion() {
const cargoToml = fs.readFileSync(
path.resolve(__dirname, "..", "..", "..", "Cargo.toml"),
"utf8"
);
const match = cargoToml.match(/^version = "([^"]+)"/m);
if (!match) {
throw new Error("Could not determine Cargo.toml version.");
}
return match[1];
}
function main() {
const pkg = readPackageJson();
const cargoVersion = readCargoVersion();
if (pkg.version !== cargoVersion) {
throw new Error(
`Version mismatch: npm package is ${pkg.version}, Cargo.toml is ${cargoVersion}.`
);
}
if (pkg.bin?.deskctl !== "bin/deskctl.js") {
throw new Error("deskctl-cli must expose the deskctl bin entrypoint.");
}
const target = supportedTarget("linux", "x64");
const targetPath = vendorBinaryPath(target);
const vendorDir = path.dirname(targetPath);
if (!vendorDir.endsWith(path.join("deskctl-cli", "vendor"))) {
throw new Error("Vendor binary directory resolved unexpectedly.");
}
}
main();