@howar31/slk 0.1.0
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/install.js +110 -0
- package/package.json +53 -0
- package/platform.js +57 -0
- package/run.js +33 -0
package/install.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const { pipeline } = require("stream/promises");
|
|
9
|
+
const { createWriteStream, mkdirSync, rmSync } = require("fs");
|
|
10
|
+
const { spawnSync } = require("child_process");
|
|
11
|
+
const { Readable } = require("stream");
|
|
12
|
+
const { getPlatform } = require("./platform");
|
|
13
|
+
|
|
14
|
+
const INSTALL_DIR = path.join(__dirname, "bin");
|
|
15
|
+
|
|
16
|
+
function downloadUrl(version, artifact) {
|
|
17
|
+
return `https://github.com/howar31/slk/releases/download/v${version}/${artifact}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function checksumUrl(version) {
|
|
21
|
+
return `https://github.com/howar31/slk/releases/download/v${version}/checksums.txt`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function download(url, dest) {
|
|
25
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
|
|
28
|
+
}
|
|
29
|
+
if (!res.body) {
|
|
30
|
+
throw new Error(`Failed to download ${url}: empty body`);
|
|
31
|
+
}
|
|
32
|
+
const file = createWriteStream(dest);
|
|
33
|
+
await pipeline(Readable.fromWeb(res.body), file);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function extractTarGz(archivePath, destDir) {
|
|
37
|
+
const r = spawnSync("tar", ["xf", archivePath, "-C", destDir], { stdio: "pipe" });
|
|
38
|
+
if (r.status !== 0) {
|
|
39
|
+
throw new Error(`tar failed: ${(r.stderr || "").toString()}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function sha256(filePath) {
|
|
44
|
+
const buf = fs.readFileSync(filePath);
|
|
45
|
+
return crypto.createHash("sha256").update(buf).digest("hex").toLowerCase();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function lookupChecksum(checksumsFile, artifact) {
|
|
49
|
+
const lines = fs.readFileSync(checksumsFile, "utf8").split(/\r?\n/);
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
const m = line.trim().match(/^([0-9a-f]+)\s+(\S+)$/i);
|
|
52
|
+
if (m && m[2] === artifact) return m[1].toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Checksum for ${artifact} not found in checksums.txt`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function main() {
|
|
58
|
+
const platform = getPlatform();
|
|
59
|
+
const { version } = require("./package.json");
|
|
60
|
+
|
|
61
|
+
const binPath = path.join(INSTALL_DIR, platform.binary);
|
|
62
|
+
const versionFile = path.join(INSTALL_DIR, ".version");
|
|
63
|
+
if (fs.existsSync(binPath) && fs.existsSync(versionFile)) {
|
|
64
|
+
const installed = fs.readFileSync(versionFile, "utf8").trim();
|
|
65
|
+
if (installed === version) {
|
|
66
|
+
console.error(`slk v${version} is already installed, skipping.`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
console.error(`Upgrading slk from v${installed} to v${version}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(INSTALL_DIR)) rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
73
|
+
mkdirSync(INSTALL_DIR, { recursive: true });
|
|
74
|
+
|
|
75
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "slk-"));
|
|
76
|
+
const tarPath = path.join(tmpDir, platform.artifact);
|
|
77
|
+
const sumsPath = path.join(tmpDir, "checksums.txt");
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const tarUrl = downloadUrl(version, platform.artifact);
|
|
81
|
+
const sumsUrlStr = checksumUrl(version);
|
|
82
|
+
console.error(`Downloading ${tarUrl}`);
|
|
83
|
+
await download(tarUrl, tarPath);
|
|
84
|
+
console.error(`Downloading ${sumsUrlStr}`);
|
|
85
|
+
await download(sumsUrlStr, sumsPath);
|
|
86
|
+
|
|
87
|
+
const expected = lookupChecksum(sumsPath, platform.artifact);
|
|
88
|
+
const actual = sha256(tarPath);
|
|
89
|
+
if (expected !== actual) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`SHA256 mismatch for ${platform.artifact}\n expected: ${expected}\n actual: ${actual}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
console.error("Checksum verified.");
|
|
95
|
+
|
|
96
|
+
console.error(`Extracting to ${INSTALL_DIR}`);
|
|
97
|
+
extractTarGz(tarPath, INSTALL_DIR);
|
|
98
|
+
fs.chmodSync(binPath, 0o755);
|
|
99
|
+
|
|
100
|
+
fs.writeFileSync(versionFile, version);
|
|
101
|
+
console.error(`slk v${version} installed.`);
|
|
102
|
+
} finally {
|
|
103
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main().catch((err) => {
|
|
108
|
+
console.error(`Error installing slk: ${err.message}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@howar31/slk",
|
|
3
|
+
"description": "Agent-facing Slack CLI for AI agents.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Howar31",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/howar31/slk.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/howar31/slk",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/howar31/slk/issues"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"slk": "run.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"postinstall": "node install.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"preferUnplugged": true,
|
|
25
|
+
"keywords": [
|
|
26
|
+
"cli",
|
|
27
|
+
"slack",
|
|
28
|
+
"agent",
|
|
29
|
+
"ai-agent",
|
|
30
|
+
"automation"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"supportedPlatforms": {
|
|
36
|
+
"darwin-arm64": {
|
|
37
|
+
"artifact": "slk_darwin_arm64.tar.gz",
|
|
38
|
+
"binary": "slk"
|
|
39
|
+
},
|
|
40
|
+
"darwin-x64": {
|
|
41
|
+
"artifact": "slk_darwin_amd64.tar.gz",
|
|
42
|
+
"binary": "slk"
|
|
43
|
+
},
|
|
44
|
+
"linux-arm64": {
|
|
45
|
+
"artifact": "slk_linux_arm64.tar.gz",
|
|
46
|
+
"binary": "slk"
|
|
47
|
+
},
|
|
48
|
+
"linux-x64": {
|
|
49
|
+
"artifact": "slk_linux_amd64.tar.gz",
|
|
50
|
+
"binary": "slk"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
package/platform.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const { supportedPlatforms } = require("./package.json");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Map Node.js os.type() and os.arch() to a key in supportedPlatforms.
|
|
8
|
+
*/
|
|
9
|
+
function getPlatformKey() {
|
|
10
|
+
const rawOs = os.type();
|
|
11
|
+
const rawArch = os.arch();
|
|
12
|
+
|
|
13
|
+
let osType;
|
|
14
|
+
switch (rawOs) {
|
|
15
|
+
case "Darwin":
|
|
16
|
+
osType = "darwin";
|
|
17
|
+
break;
|
|
18
|
+
case "Linux":
|
|
19
|
+
osType = "linux";
|
|
20
|
+
break;
|
|
21
|
+
default:
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Unsupported operating system: ${rawOs}. ` +
|
|
24
|
+
`slk currently ships binaries for darwin and linux only.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let arch;
|
|
29
|
+
switch (rawArch) {
|
|
30
|
+
case "x64":
|
|
31
|
+
arch = "x64";
|
|
32
|
+
break;
|
|
33
|
+
case "arm64":
|
|
34
|
+
arch = "arm64";
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Unsupported architecture: ${rawArch}. ` +
|
|
39
|
+
`slk currently ships binaries for arm64 and x64 only.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const key = `${osType}-${arch}`;
|
|
44
|
+
if (!supportedPlatforms[key]) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Unsupported platform: ${key}. ` +
|
|
47
|
+
`Supported: ${Object.keys(supportedPlatforms).join(", ")}`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getPlatform() {
|
|
54
|
+
return supportedPlatforms[getPlatformKey()];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { getPlatform, getPlatformKey };
|
package/run.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const { spawnSync } = require("child_process");
|
|
7
|
+
const { getPlatform } = require("./platform");
|
|
8
|
+
|
|
9
|
+
const platform = getPlatform();
|
|
10
|
+
const binPath = path.join(__dirname, "bin", platform.binary);
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(binPath)) {
|
|
13
|
+
console.error(`slk binary not found at ${binPath}\nRunning install...`);
|
|
14
|
+
const installResult = spawnSync(process.execPath, [path.join(__dirname, "install.js")], {
|
|
15
|
+
cwd: __dirname,
|
|
16
|
+
stdio: "inherit",
|
|
17
|
+
});
|
|
18
|
+
if (installResult.status !== 0) {
|
|
19
|
+
process.exit(installResult.status ?? 1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const result = spawnSync(binPath, process.argv.slice(2), {
|
|
24
|
+
cwd: process.cwd(),
|
|
25
|
+
stdio: "inherit",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (result.error) {
|
|
29
|
+
console.error(`Error running slk: ${result.error.message}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
process.exit(result.status ?? 1);
|