kagi-cli 0.1.4

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,19 @@
1
+ # kagi-cli
2
+
3
+ This package installs the native `kagi` CLI binary for the current platform.
4
+
5
+ Global install examples:
6
+
7
+ ```bash
8
+ npm install -g kagi-cli
9
+ pnpm add -g kagi-cli
10
+ bun add -g kagi-cli
11
+ ```
12
+
13
+ Then run:
14
+
15
+ ```bash
16
+ kagi --help
17
+ ```
18
+
19
+ The package name is `kagi-cli`, but the installed command is always `kagi`.
package/bin/kagi.cjs ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("node:child_process");
4
+ const { ensureInstalled, getBinaryPath } = require("../lib/install.cjs");
5
+
6
+ async function main() {
7
+ await ensureInstalled({ quiet: false });
8
+
9
+ const child = spawn(getBinaryPath(), process.argv.slice(2), {
10
+ stdio: "inherit",
11
+ });
12
+
13
+ child.on("exit", (code, signal) => {
14
+ if (signal) {
15
+ process.kill(process.pid, signal);
16
+ return;
17
+ }
18
+
19
+ process.exit(code ?? 0);
20
+ });
21
+
22
+ child.on("error", (error) => {
23
+ console.error(`failed to start kagi: ${error.message}`);
24
+ process.exit(1);
25
+ });
26
+ }
27
+
28
+ main().catch((error) => {
29
+ console.error(error instanceof Error ? error.message : String(error));
30
+ process.exit(1);
31
+ });
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("node:fs");
4
+ const fsp = require("node:fs/promises");
5
+ const https = require("node:https");
6
+ const path = require("node:path");
7
+
8
+ const packageRoot = path.resolve(__dirname, "..");
9
+ const packageJson = require(path.join(packageRoot, "package.json"));
10
+
11
+ const binaryBaseName = process.platform === "win32" ? "kagi.exe" : "kagi";
12
+ const releaseTag = `v${packageJson.version}`;
13
+ const repo = "Microck/kagi-cli";
14
+
15
+ function detectTarget() {
16
+ const os = process.platform;
17
+ const arch = process.arch;
18
+
19
+ const archPart =
20
+ arch === "x64" ? "x86_64" :
21
+ arch === "arm64" ? "aarch64" :
22
+ null;
23
+
24
+ if (!archPart) {
25
+ throw new Error(`unsupported CPU architecture for kagi: ${arch}`);
26
+ }
27
+
28
+ const osPart =
29
+ os === "linux" ? "unknown-linux-gnu" :
30
+ os === "darwin" ? "apple-darwin" :
31
+ os === "win32" ? "pc-windows-msvc" :
32
+ null;
33
+
34
+ if (!osPart) {
35
+ throw new Error(`unsupported operating system for kagi: ${os}`);
36
+ }
37
+
38
+ return `${archPart}-${osPart}`;
39
+ }
40
+
41
+ function getBinaryPath() {
42
+ return path.join(packageRoot, "vendor", detectTarget(), binaryBaseName);
43
+ }
44
+
45
+ function getAssetFileName() {
46
+ return `${binaryBaseName.replace(/\.exe$/, "")}-${releaseTag}-${detectTarget()}${process.platform === "win32" ? ".exe" : ""}`;
47
+ }
48
+
49
+ function getAssetUrl() {
50
+ return `https://github.com/${repo}/releases/download/${releaseTag}/${getAssetFileName()}`;
51
+ }
52
+
53
+ function download(url, destination) {
54
+ return new Promise((resolve, reject) => {
55
+ const request = https.get(
56
+ url,
57
+ {
58
+ headers: {
59
+ "User-Agent": "kagi-cli-installer",
60
+ "Accept": "application/octet-stream",
61
+ },
62
+ },
63
+ (response) => {
64
+ if (
65
+ response.statusCode &&
66
+ response.statusCode >= 300 &&
67
+ response.statusCode < 400 &&
68
+ response.headers.location
69
+ ) {
70
+ response.resume();
71
+ download(response.headers.location, destination).then(resolve, reject);
72
+ return;
73
+ }
74
+
75
+ if (response.statusCode !== 200) {
76
+ response.resume();
77
+ reject(
78
+ new Error(
79
+ `failed to download ${url} - HTTP ${response.statusCode ?? "unknown"}`
80
+ )
81
+ );
82
+ return;
83
+ }
84
+
85
+ const file = fs.createWriteStream(destination, { mode: 0o755 });
86
+ response.pipe(file);
87
+
88
+ file.on("finish", () => {
89
+ file.close(resolve);
90
+ });
91
+
92
+ file.on("error", (error) => {
93
+ reject(error);
94
+ });
95
+ }
96
+ );
97
+
98
+ request.on("error", reject);
99
+ });
100
+ }
101
+
102
+ async function ensureInstalled({ quiet }) {
103
+ const binaryPath = getBinaryPath();
104
+
105
+ try {
106
+ await fsp.access(binaryPath, fs.constants.X_OK);
107
+ return binaryPath;
108
+ } catch {
109
+ const vendorDir = path.dirname(binaryPath);
110
+ const tempPath = `${binaryPath}.tmp`;
111
+ const assetUrl = getAssetUrl();
112
+
113
+ if (!quiet) {
114
+ console.error(`Downloading native kagi binary from ${assetUrl}`);
115
+ }
116
+
117
+ await fsp.mkdir(vendorDir, { recursive: true });
118
+ await download(assetUrl, tempPath);
119
+ await fsp.rename(tempPath, binaryPath);
120
+
121
+ if (process.platform !== "win32") {
122
+ await fsp.chmod(binaryPath, 0o755);
123
+ }
124
+
125
+ return binaryPath;
126
+ }
127
+ }
128
+
129
+ if (require.main === module) {
130
+ ensureInstalled({ quiet: false }).catch((error) => {
131
+ console.error(error instanceof Error ? error.message : String(error));
132
+ process.exit(1);
133
+ });
134
+ }
135
+
136
+ module.exports = {
137
+ detectTarget,
138
+ ensureInstalled,
139
+ getAssetFileName,
140
+ getAssetUrl,
141
+ getBinaryPath,
142
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "kagi-cli",
3
+ "version": "0.1.4",
4
+ "description": "Node wrapper that installs the native kagi CLI binary",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Microck/kagi-cli.git"
9
+ },
10
+ "homepage": "https://github.com/Microck/kagi-cli#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/Microck/kagi-cli/issues"
13
+ },
14
+ "bin": {
15
+ "kagi": "bin/kagi.cjs"
16
+ },
17
+ "files": [
18
+ "bin",
19
+ "lib",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "postinstall": "node lib/install.cjs"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "provenance": true
28
+ },
29
+ "keywords": [
30
+ "kagi",
31
+ "cli",
32
+ "search",
33
+ "terminal",
34
+ "rust"
35
+ ],
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "preferGlobal": true
40
+ }