deskctl-cli 0.1.5

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.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # deskctl-cli
2
+
3
+ `deskctl-cli` installs the `deskctl` command for Linux X11 systems.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g deskctl-cli
9
+ ```
10
+
11
+ After install, run:
12
+
13
+ ```bash
14
+ deskctl --help
15
+ ```
16
+
17
+ One-shot usage is also supported:
18
+
19
+ ```bash
20
+ npx deskctl-cli --help
21
+ ```
22
+
23
+ ## Runtime Support
24
+
25
+ - Linux
26
+ - X11 session
27
+ - currently packaged release asset: `linux-x64`
28
+
29
+ `deskctl-cli` downloads the matching GitHub Release binary during install.
30
+ Unsupported targets fail during install with a clear runtime support error instead of installing a broken command.
31
+
32
+ If you want the Rust source-install path instead, use:
33
+
34
+ ```bash
35
+ cargo install deskctl
36
+ ```
package/bin/deskctl.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("node:fs");
4
+ const { spawn } = require("node:child_process");
5
+
6
+ const { readPackageJson, releaseTag, supportedTarget, vendorBinaryPath } = require("../scripts/support");
7
+
8
+ function main() {
9
+ const pkg = readPackageJson();
10
+ const target = supportedTarget();
11
+ const binaryPath = vendorBinaryPath(target);
12
+
13
+ if (!fs.existsSync(binaryPath)) {
14
+ console.error(
15
+ [
16
+ "deskctl binary is missing from the npm package install.",
17
+ `Expected: ${binaryPath}`,
18
+ `Package version: ${pkg.version}`,
19
+ `Release tag: ${releaseTag(pkg)}`,
20
+ "Try reinstalling deskctl-cli or check that your target is supported."
21
+ ].join("\n")
22
+ );
23
+ process.exit(1);
24
+ }
25
+
26
+ const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit" });
27
+ child.on("exit", (code, signal) => {
28
+ if (signal) {
29
+ process.kill(process.pid, signal);
30
+ return;
31
+ }
32
+ process.exit(code ?? 1);
33
+ });
34
+ }
35
+
36
+ main();
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "deskctl-cli",
3
+ "version": "0.1.5",
4
+ "description": "Installable deskctl CLI package for Linux X11 agents",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/harivansh-afk/deskctl",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/harivansh-afk/deskctl.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/harivansh-afk/deskctl/issues"
13
+ },
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "bin": {
18
+ "deskctl": "bin/deskctl.js"
19
+ },
20
+ "files": [
21
+ "README.md",
22
+ "bin",
23
+ "scripts"
24
+ ],
25
+ "scripts": {
26
+ "postinstall": "node scripts/postinstall.js",
27
+ "validate": "node scripts/validate-package.js"
28
+ },
29
+ "keywords": [
30
+ "deskctl",
31
+ "x11",
32
+ "desktop",
33
+ "automation",
34
+ "cli"
35
+ ]
36
+ }
@@ -0,0 +1,49 @@
1
+ const fs = require("node:fs");
2
+
3
+ const {
4
+ checksumsUrl,
5
+ checksumForAsset,
6
+ download,
7
+ ensureVendorDir,
8
+ installLocalBinary,
9
+ readPackageJson,
10
+ releaseAssetUrl,
11
+ releaseTag,
12
+ sha256,
13
+ supportedTarget,
14
+ vendorBinaryPath
15
+ } = require("./support");
16
+
17
+ async function main() {
18
+ const pkg = readPackageJson();
19
+ const target = supportedTarget();
20
+ const targetPath = vendorBinaryPath(target);
21
+
22
+ ensureVendorDir();
23
+
24
+ if (process.env.DESKCTL_BINARY_PATH) {
25
+ installLocalBinary(process.env.DESKCTL_BINARY_PATH, targetPath);
26
+ return;
27
+ }
28
+
29
+ const tag = releaseTag(pkg);
30
+ const assetUrl = releaseAssetUrl(tag, target.assetName);
31
+ const checksumText = (await download(checksumsUrl(tag))).toString("utf8");
32
+ const expectedSha = checksumForAsset(checksumText, target.assetName);
33
+ const asset = await download(assetUrl);
34
+ const actualSha = sha256(asset);
35
+
36
+ if (actualSha !== expectedSha) {
37
+ throw new Error(
38
+ `Checksum mismatch for ${target.assetName}. Expected ${expectedSha}, got ${actualSha}.`
39
+ );
40
+ }
41
+
42
+ fs.writeFileSync(targetPath, asset);
43
+ fs.chmodSync(targetPath, 0o755);
44
+ }
45
+
46
+ main().catch((error) => {
47
+ console.error(`deskctl-cli install failed: ${error.message}`);
48
+ process.exit(1);
49
+ });
@@ -0,0 +1,120 @@
1
+ const crypto = require("node:crypto");
2
+ const fs = require("node:fs");
3
+ const path = require("node:path");
4
+ const https = require("node:https");
5
+
6
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
7
+ const VENDOR_DIR = path.join(PACKAGE_ROOT, "vendor");
8
+ const PACKAGE_JSON = path.join(PACKAGE_ROOT, "package.json");
9
+
10
+ function readPackageJson() {
11
+ return JSON.parse(fs.readFileSync(PACKAGE_JSON, "utf8"));
12
+ }
13
+
14
+ function releaseTag(pkg) {
15
+ return process.env.DESKCTL_RELEASE_TAG || `v${pkg.version}`;
16
+ }
17
+
18
+ function supportedTarget(platform = process.platform, arch = process.arch) {
19
+ if (platform === "linux" && arch === "x64") {
20
+ return {
21
+ platform,
22
+ arch,
23
+ assetName: "deskctl-linux-x86_64",
24
+ binaryName: "deskctl-linux-x86_64"
25
+ };
26
+ }
27
+
28
+ throw new Error(
29
+ `deskctl-cli currently supports linux-x64 only. Received ${platform}-${arch}.`
30
+ );
31
+ }
32
+
33
+ function vendorBinaryPath(target) {
34
+ return path.join(VENDOR_DIR, target.binaryName);
35
+ }
36
+
37
+ function releaseBaseUrl(tag) {
38
+ return (
39
+ process.env.DESKCTL_RELEASE_BASE_URL ||
40
+ `https://github.com/harivansh-afk/deskctl/releases/download/${tag}`
41
+ );
42
+ }
43
+
44
+ function releaseAssetUrl(tag, assetName) {
45
+ return process.env.DESKCTL_DOWNLOAD_URL || `${releaseBaseUrl(tag)}/${assetName}`;
46
+ }
47
+
48
+ function checksumsUrl(tag) {
49
+ return `${releaseBaseUrl(tag)}/checksums.txt`;
50
+ }
51
+
52
+ function ensureVendorDir() {
53
+ fs.mkdirSync(VENDOR_DIR, { recursive: true });
54
+ }
55
+
56
+ function checksumForAsset(contents, assetName) {
57
+ const line = contents
58
+ .split("\n")
59
+ .map((value) => value.trim())
60
+ .find((value) => value.endsWith(` ${assetName}`) || value.endsWith(` *${assetName}`));
61
+
62
+ if (!line) {
63
+ throw new Error(`Could not find checksum entry for ${assetName}.`);
64
+ }
65
+
66
+ return line.split(/\s+/)[0];
67
+ }
68
+
69
+ function sha256(buffer) {
70
+ return crypto.createHash("sha256").update(buffer).digest("hex");
71
+ }
72
+
73
+ function download(url) {
74
+ return new Promise((resolve, reject) => {
75
+ https
76
+ .get(url, (response) => {
77
+ if (
78
+ response.statusCode &&
79
+ response.statusCode >= 300 &&
80
+ response.statusCode < 400 &&
81
+ response.headers.location
82
+ ) {
83
+ response.resume();
84
+ resolve(download(response.headers.location));
85
+ return;
86
+ }
87
+
88
+ if (response.statusCode !== 200) {
89
+ reject(new Error(`Download failed for ${url}: HTTP ${response.statusCode}`));
90
+ return;
91
+ }
92
+
93
+ const chunks = [];
94
+ response.on("data", (chunk) => chunks.push(chunk));
95
+ response.on("end", () => resolve(Buffer.concat(chunks)));
96
+ })
97
+ .on("error", reject);
98
+ });
99
+ }
100
+
101
+ function installLocalBinary(sourcePath, targetPath) {
102
+ fs.copyFileSync(sourcePath, targetPath);
103
+ fs.chmodSync(targetPath, 0o755);
104
+ }
105
+
106
+ module.exports = {
107
+ PACKAGE_ROOT,
108
+ VENDOR_DIR,
109
+ checksumsUrl,
110
+ checksumForAsset,
111
+ download,
112
+ ensureVendorDir,
113
+ installLocalBinary,
114
+ readPackageJson,
115
+ releaseAssetUrl,
116
+ releaseTag,
117
+ sha256,
118
+ supportedTarget,
119
+ vendorBinaryPath
120
+ };
@@ -0,0 +1,40 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+
4
+ const { readPackageJson, supportedTarget, vendorBinaryPath } = require("./support");
5
+
6
+ function readCargoVersion() {
7
+ const cargoToml = fs.readFileSync(
8
+ path.resolve(__dirname, "..", "..", "..", "Cargo.toml"),
9
+ "utf8"
10
+ );
11
+ const match = cargoToml.match(/^version = "([^"]+)"/m);
12
+ if (!match) {
13
+ throw new Error("Could not determine Cargo.toml version.");
14
+ }
15
+ return match[1];
16
+ }
17
+
18
+ function main() {
19
+ const pkg = readPackageJson();
20
+ const cargoVersion = readCargoVersion();
21
+
22
+ if (pkg.version !== cargoVersion) {
23
+ throw new Error(
24
+ `Version mismatch: npm package is ${pkg.version}, Cargo.toml is ${cargoVersion}.`
25
+ );
26
+ }
27
+
28
+ if (pkg.bin?.deskctl !== "bin/deskctl.js") {
29
+ throw new Error("deskctl-cli must expose the deskctl bin entrypoint.");
30
+ }
31
+
32
+ const target = supportedTarget("linux", "x64");
33
+ const targetPath = vendorBinaryPath(target);
34
+ const vendorDir = path.dirname(targetPath);
35
+ if (!vendorDir.endsWith(path.join("deskctl-cli", "vendor"))) {
36
+ throw new Error("Vendor binary directory resolved unexpectedly.");
37
+ }
38
+ }
39
+
40
+ main();