nikcli-ai 1.209.0 → 1.211.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/bin/nikcli +113 -17
- package/package.json +13 -13
- package/postinstall.mjs +178 -106
package/bin/nikcli
CHANGED
|
@@ -36,28 +36,111 @@ const archMap = {
|
|
|
36
36
|
arm: "arm",
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
platform = os.platform()
|
|
42
|
-
}
|
|
43
|
-
let arch = archMap[os.arch()]
|
|
44
|
-
if (!arch) {
|
|
45
|
-
arch = os.arch()
|
|
46
|
-
}
|
|
39
|
+
const platform = platformMap[os.platform()] || os.platform()
|
|
40
|
+
const arch = archMap[os.arch()] || os.arch()
|
|
47
41
|
const base = "nikcli-ai-" + platform + "-" + arch
|
|
48
42
|
const binary = platform === "windows" ? "nikcli.exe" : "nikcli"
|
|
49
43
|
|
|
44
|
+
// Fast path: postinstall drops the native binary right next to this wrapper.
|
|
45
|
+
const materialized = path.join(scriptDir, platform === "windows" ? "nikcli.exe" : "nikcli-bin")
|
|
46
|
+
if (fs.existsSync(materialized)) {
|
|
47
|
+
run(materialized)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function supportsAvx2() {
|
|
51
|
+
if (arch !== "x64") return false
|
|
52
|
+
if (platform === "linux") {
|
|
53
|
+
try {
|
|
54
|
+
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (platform === "darwin") {
|
|
60
|
+
try {
|
|
61
|
+
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
|
|
62
|
+
encoding: "utf8",
|
|
63
|
+
timeout: 1500,
|
|
64
|
+
})
|
|
65
|
+
return result.status === 0 && (result.stdout || "").trim() === "1"
|
|
66
|
+
} catch (e) {
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (platform === "windows") {
|
|
71
|
+
// PF_AVX2_INSTRUCTIONS_AVAILABLE == 40
|
|
72
|
+
const command =
|
|
73
|
+
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
|
74
|
+
const executables = ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]
|
|
75
|
+
for (let i = 0; i < executables.length; i++) {
|
|
76
|
+
try {
|
|
77
|
+
const result = childProcess.spawnSync(executables[i], ["-NoProfile", "-NonInteractive", "-Command", command], {
|
|
78
|
+
encoding: "utf8",
|
|
79
|
+
timeout: 3000,
|
|
80
|
+
windowsHide: true,
|
|
81
|
+
})
|
|
82
|
+
if (result.status !== 0) continue
|
|
83
|
+
const output = (result.stdout || "").trim().toLowerCase()
|
|
84
|
+
if (output === "true" || output === "1") return true
|
|
85
|
+
if (output === "false" || output === "0") return false
|
|
86
|
+
} catch (e) {
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isMusl() {
|
|
95
|
+
if (platform !== "linux") return false
|
|
96
|
+
try {
|
|
97
|
+
if (fs.existsSync("/etc/alpine-release")) return true
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// ignore
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
|
103
|
+
return ((result.stdout || "") + (result.stderr || "")).toLowerCase().indexOf("musl") !== -1
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Ordered best → worst. A prefix match is not enough: on x64 it would happily
|
|
110
|
+
// pick a `-musl` or `-baseline` build depending on readdir order, and a glibc
|
|
111
|
+
// binary on musl (or an AVX2 binary on a CPU without it) does not merely run
|
|
112
|
+
// slower — it fails to start.
|
|
113
|
+
function packageNames() {
|
|
114
|
+
const baseline = arch === "x64" && !supportsAvx2()
|
|
115
|
+
if (platform === "linux") {
|
|
116
|
+
if (isMusl()) {
|
|
117
|
+
if (arch === "x64") {
|
|
118
|
+
return baseline
|
|
119
|
+
? [base + "-baseline-musl", base + "-musl", base + "-baseline", base]
|
|
120
|
+
: [base + "-musl", base + "-baseline-musl", base, base + "-baseline"]
|
|
121
|
+
}
|
|
122
|
+
return [base + "-musl", base]
|
|
123
|
+
}
|
|
124
|
+
if (arch === "x64") {
|
|
125
|
+
return baseline
|
|
126
|
+
? [base + "-baseline", base, base + "-baseline-musl", base + "-musl"]
|
|
127
|
+
: [base, base + "-baseline", base + "-musl", base + "-baseline-musl"]
|
|
128
|
+
}
|
|
129
|
+
return [base, base + "-musl"]
|
|
130
|
+
}
|
|
131
|
+
if (arch === "x64") return baseline ? [base + "-baseline", base] : [base, base + "-baseline"]
|
|
132
|
+
return [base]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const candidates = packageNames()
|
|
136
|
+
|
|
50
137
|
function findBinary(startDir) {
|
|
51
138
|
let current = startDir
|
|
52
139
|
for (;;) {
|
|
53
140
|
const modules = path.join(current, "node_modules")
|
|
54
141
|
if (fs.existsSync(modules)) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!entry.startsWith(base)) {
|
|
58
|
-
continue
|
|
59
|
-
}
|
|
60
|
-
const candidate = path.join(modules, entry, "bin", binary)
|
|
142
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
143
|
+
const candidate = path.join(modules, candidates[i], "bin", binary)
|
|
61
144
|
if (fs.existsSync(candidate)) {
|
|
62
145
|
return candidate
|
|
63
146
|
}
|
|
@@ -71,12 +154,25 @@ function findBinary(startDir) {
|
|
|
71
154
|
}
|
|
72
155
|
}
|
|
73
156
|
|
|
74
|
-
|
|
157
|
+
// Covers layouts the upward walk misses (pnpm's virtual store, yarn PnP).
|
|
158
|
+
function resolveBinary() {
|
|
159
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
160
|
+
try {
|
|
161
|
+
const manifest = require.resolve(candidates[i] + "/package.json")
|
|
162
|
+
const resolved = path.join(path.dirname(manifest), "bin", binary)
|
|
163
|
+
if (fs.existsSync(resolved)) return resolved
|
|
164
|
+
} catch (e) {
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const resolved = findBinary(scriptDir) || resolveBinary()
|
|
75
171
|
if (!resolved) {
|
|
76
172
|
console.error(
|
|
77
173
|
'It seems that your package manager failed to install the right version of the nikcli CLI for your platform. You can try manually installing the "' +
|
|
78
|
-
|
|
79
|
-
'" package',
|
|
174
|
+
candidates[0] +
|
|
175
|
+
'" package, or reinstall with the standalone installer: https://nikcli.store/install',
|
|
80
176
|
)
|
|
81
177
|
process.exit(1)
|
|
82
178
|
}
|
package/package.json
CHANGED
|
@@ -6,19 +6,19 @@
|
|
|
6
6
|
"scripts": {
|
|
7
7
|
"postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.211.0",
|
|
10
10
|
"optionalDependencies": {
|
|
11
|
-
"nikcli-ai-linux-arm64": "1.
|
|
12
|
-
"nikcli-ai-linux-x64": "1.
|
|
13
|
-
"nikcli-ai-linux-x64-baseline": "1.
|
|
14
|
-
"nikcli-ai-linux-arm64-musl": "1.
|
|
15
|
-
"nikcli-ai-linux-x64-musl": "1.
|
|
16
|
-
"nikcli-ai-linux-x64-baseline-musl": "1.
|
|
17
|
-
"nikcli-ai-darwin-arm64": "1.
|
|
18
|
-
"nikcli-ai-darwin-x64": "1.
|
|
19
|
-
"nikcli-ai-darwin-x64-baseline": "1.
|
|
20
|
-
"nikcli-ai-windows-arm64": "1.
|
|
21
|
-
"nikcli-ai-windows-x64": "1.
|
|
22
|
-
"nikcli-ai-windows-x64-baseline": "1.
|
|
11
|
+
"nikcli-ai-linux-arm64": "1.211.0",
|
|
12
|
+
"nikcli-ai-linux-x64": "1.211.0",
|
|
13
|
+
"nikcli-ai-linux-x64-baseline": "1.211.0",
|
|
14
|
+
"nikcli-ai-linux-arm64-musl": "1.211.0",
|
|
15
|
+
"nikcli-ai-linux-x64-musl": "1.211.0",
|
|
16
|
+
"nikcli-ai-linux-x64-baseline-musl": "1.211.0",
|
|
17
|
+
"nikcli-ai-darwin-arm64": "1.211.0",
|
|
18
|
+
"nikcli-ai-darwin-x64": "1.211.0",
|
|
19
|
+
"nikcli-ai-darwin-x64-baseline": "1.211.0",
|
|
20
|
+
"nikcli-ai-windows-arm64": "1.211.0",
|
|
21
|
+
"nikcli-ai-windows-x64": "1.211.0",
|
|
22
|
+
"nikcli-ai-windows-x64-baseline": "1.211.0"
|
|
23
23
|
}
|
|
24
24
|
}
|
package/postinstall.mjs
CHANGED
|
@@ -1,144 +1,216 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Materializes the correct per-platform nikcli binary next to the `bin/nikcli`
|
|
4
|
+
// wrapper so the wrapper can exec it directly.
|
|
5
|
+
//
|
|
6
|
+
// This is best-effort by design: the wrapper resolves the binary at runtime too,
|
|
7
|
+
// so a failure here (pnpm without postinstall, --ignore-scripts, yarn PnP, npm's
|
|
8
|
+
// optionalDependencies bug) must never fail the install. We exit 0 and let the
|
|
9
|
+
// wrapper try again later.
|
|
10
|
+
|
|
3
11
|
import fs from "fs"
|
|
4
|
-
import path from "path"
|
|
5
12
|
import os from "os"
|
|
6
|
-
import
|
|
7
|
-
import
|
|
13
|
+
import path from "path"
|
|
14
|
+
import childProcess from "child_process"
|
|
8
15
|
import { createRequire } from "module"
|
|
16
|
+
import { fileURLToPath } from "url"
|
|
9
17
|
|
|
10
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
19
|
const require = createRequire(import.meta.url)
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
platform = "darwin"
|
|
18
|
-
break
|
|
19
|
-
case "linux":
|
|
20
|
-
platform = "linux"
|
|
21
|
-
break
|
|
22
|
-
case "win32":
|
|
23
|
-
platform = "windows"
|
|
24
|
-
break
|
|
25
|
-
default:
|
|
26
|
-
platform = os.platform()
|
|
27
|
-
break
|
|
28
|
-
}
|
|
21
|
+
// Published layout puts this file at the package root; in the repo it lives in
|
|
22
|
+
// `script/`. Resolve the package root from whichever one actually has a manifest.
|
|
23
|
+
const packageRoot = fs.existsSync(path.join(__dirname, "package.json")) ? __dirname : path.join(__dirname, "..")
|
|
24
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"))
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
case "x64":
|
|
33
|
-
arch = "x64"
|
|
34
|
-
break
|
|
35
|
-
case "arm64":
|
|
36
|
-
arch = "arm64"
|
|
37
|
-
break
|
|
38
|
-
default:
|
|
39
|
-
arch = os.arch()
|
|
40
|
-
break
|
|
41
|
-
}
|
|
26
|
+
const platformMap = { darwin: "darwin", linux: "linux", win32: "windows" }
|
|
27
|
+
const archMap = { x64: "x64", arm64: "arm64", arm: "arm" }
|
|
42
28
|
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
const platform = platformMap[os.platform()] ?? os.platform()
|
|
30
|
+
const arch = archMap[os.arch()] ?? os.arch()
|
|
31
|
+
const base = `nikcli-ai-${platform}-${arch}`
|
|
32
|
+
// Bun's `--compile` appends `.exe` on Windows targets, so that is the name inside
|
|
33
|
+
// the per-platform package on Windows and the bare name everywhere else.
|
|
34
|
+
const sourceBinary = platform === "windows" ? "nikcli.exe" : "nikcli"
|
|
35
|
+
// Never overwrite `bin/nikcli` — that file IS the wrapper this package declares
|
|
36
|
+
// in its `bin` field. We drop the native binary beside it instead.
|
|
37
|
+
const targetBinary = path.join(packageRoot, "bin", platform === "windows" ? "nikcli.exe" : "nikcli-bin")
|
|
45
38
|
|
|
46
|
-
function
|
|
47
|
-
if (
|
|
48
|
-
try {
|
|
49
|
-
const ldd = execSync("ldd --version 2>&1", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
|
|
50
|
-
if (ldd.toLowerCase().includes("musl")) return true
|
|
51
|
-
} catch {}
|
|
52
|
-
return fs.existsSync("/etc/alpine-release")
|
|
53
|
-
}
|
|
39
|
+
function supportsAvx2() {
|
|
40
|
+
if (arch !== "x64") return false
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
if (os.platform() === "linux") {
|
|
42
|
+
if (platform === "linux") {
|
|
57
43
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
44
|
+
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
|
45
|
+
} catch {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
62
48
|
}
|
|
63
|
-
|
|
49
|
+
|
|
50
|
+
if (platform === "darwin") {
|
|
64
51
|
try {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
|
|
53
|
+
encoding: "utf8",
|
|
54
|
+
timeout: 1500,
|
|
55
|
+
})
|
|
56
|
+
if (result.status !== 0) return false
|
|
57
|
+
return (result.stdout || "").trim() === "1"
|
|
58
|
+
} catch {
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (platform === "windows") {
|
|
64
|
+
// PF_AVX2_INSTRUCTIONS_AVAILABLE == 40
|
|
65
|
+
const command =
|
|
66
|
+
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
|
67
|
+
|
|
68
|
+
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
|
69
|
+
try {
|
|
70
|
+
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
timeout: 3000,
|
|
73
|
+
windowsHide: true,
|
|
74
|
+
})
|
|
75
|
+
if (result.status !== 0) continue
|
|
76
|
+
const output = (result.stdout || "").trim().toLowerCase()
|
|
77
|
+
if (output === "true" || output === "1") return true
|
|
78
|
+
if (output === "false" || output === "0") return false
|
|
79
|
+
} catch {
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
}
|
|
68
83
|
}
|
|
84
|
+
|
|
69
85
|
return false
|
|
70
86
|
}
|
|
71
87
|
|
|
72
|
-
function
|
|
88
|
+
function isMusl() {
|
|
89
|
+
if (platform !== "linux") return false
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
if (fs.existsSync("/etc/alpine-release")) return true
|
|
93
|
+
} catch {
|
|
94
|
+
// Ignore filesystem probes blocked by the host.
|
|
95
|
+
}
|
|
96
|
+
|
|
73
97
|
try {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
const binaryPath = path.join(packageDir, "bin", binaryName)
|
|
77
|
-
if (!fs.existsSync(binaryPath)) return null
|
|
78
|
-
return binaryPath
|
|
98
|
+
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
|
99
|
+
return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
|
|
79
100
|
} catch {
|
|
80
|
-
return
|
|
101
|
+
return false
|
|
81
102
|
}
|
|
82
103
|
}
|
|
83
104
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// Final fallbacks (skip musl/baseline restriction)
|
|
98
|
-
if (isMusl) candidates.push(`nikcli-ai-${platform}-${arch}`)
|
|
99
|
-
if (isBaseline) candidates.push(`nikcli-ai-${platform}-${arch}`)
|
|
100
|
-
|
|
101
|
-
for (const name of candidates) {
|
|
102
|
-
const found = tryResolvePackage(name, binaryName)
|
|
103
|
-
if (found) {
|
|
104
|
-
console.log(`nikcli: resolved binary from ${name}`)
|
|
105
|
-
return { binaryPath: found, binaryName }
|
|
105
|
+
// Ordered best → worst. A baseline binary runs on an AVX2 machine (just slower),
|
|
106
|
+
// so it stays in the list as a fallback; the reverse is a SIGILL crash, which is
|
|
107
|
+
// why the baseline probe must be right on every platform.
|
|
108
|
+
function packageNames() {
|
|
109
|
+
const baseline = arch === "x64" && !supportsAvx2()
|
|
110
|
+
|
|
111
|
+
if (platform === "linux") {
|
|
112
|
+
if (isMusl()) {
|
|
113
|
+
if (arch === "x64")
|
|
114
|
+
return baseline
|
|
115
|
+
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
|
116
|
+
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
|
117
|
+
return [`${base}-musl`, base]
|
|
106
118
|
}
|
|
119
|
+
|
|
120
|
+
if (arch === "x64")
|
|
121
|
+
return baseline
|
|
122
|
+
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
|
123
|
+
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
|
124
|
+
return [base, `${base}-musl`]
|
|
107
125
|
}
|
|
108
126
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
if (arch === "x64") return baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]
|
|
128
|
+
return [base]
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function resolveBinary(name) {
|
|
132
|
+
const packageJsonPath = require.resolve(`${name}/package.json`)
|
|
133
|
+
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", sourceBinary)
|
|
134
|
+
if (!fs.existsSync(binaryPath)) throw new Error(`Binary not found at ${binaryPath}`)
|
|
135
|
+
return binaryPath
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function copyBinary(source, target) {
|
|
139
|
+
if (!fs.existsSync(source)) throw new Error(`Binary not found at ${source}`)
|
|
140
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
141
|
+
if (fs.existsSync(target)) fs.unlinkSync(target)
|
|
142
|
+
try {
|
|
143
|
+
// Hardlink when we can: no second copy of a ~100MB binary on disk.
|
|
144
|
+
fs.linkSync(source, target)
|
|
145
|
+
} catch {
|
|
146
|
+
fs.copyFileSync(source, target)
|
|
147
|
+
}
|
|
148
|
+
fs.chmodSync(target, 0o755)
|
|
114
149
|
}
|
|
115
150
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
151
|
+
// npm has a long-standing bug where optionalDependencies are skipped when a
|
|
152
|
+
// lockfile was produced on another platform. Fetching the one package we need
|
|
153
|
+
// into a temp prefix recovers from it without touching the user's tree.
|
|
154
|
+
function installPackage(name) {
|
|
155
|
+
const version = packageJson.optionalDependencies?.[name]
|
|
156
|
+
if (!version) return false
|
|
157
|
+
|
|
158
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), "nikcli-install-"))
|
|
159
|
+
try {
|
|
160
|
+
const result = childProcess.spawnSync(
|
|
161
|
+
"npm",
|
|
162
|
+
["install", "--ignore-scripts", "--no-save", "--loglevel=error", "--prefix", temp, `${name}@${version}`],
|
|
163
|
+
// npm is npm.cmd on Windows, so it needs a shell to be spawnable.
|
|
164
|
+
// Output stays quiet: we print our own diagnostic if every candidate fails.
|
|
165
|
+
{ stdio: "ignore", windowsHide: true, shell: process.platform === "win32" },
|
|
166
|
+
)
|
|
167
|
+
if (result.status !== 0) return false
|
|
168
|
+
copyBinary(path.join(temp, "node_modules", name, "bin", sourceBinary), targetBinary)
|
|
169
|
+
return true
|
|
170
|
+
} catch {
|
|
171
|
+
return false
|
|
172
|
+
} finally {
|
|
173
|
+
fs.rmSync(temp, { recursive: true, force: true })
|
|
174
|
+
}
|
|
122
175
|
}
|
|
123
176
|
|
|
124
|
-
function
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
177
|
+
function verifyBinary() {
|
|
178
|
+
const result = childProcess.spawnSync(targetBinary, ["--version"], {
|
|
179
|
+
encoding: "utf8",
|
|
180
|
+
stdio: "ignore",
|
|
181
|
+
windowsHide: true,
|
|
182
|
+
timeout: 30000,
|
|
183
|
+
})
|
|
184
|
+
return result.status === 0
|
|
129
185
|
}
|
|
130
186
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
187
|
+
function main() {
|
|
188
|
+
const names = packageNames()
|
|
189
|
+
for (const name of names) {
|
|
190
|
+
try {
|
|
191
|
+
copyBinary(resolveBinary(name), targetBinary)
|
|
192
|
+
if (verifyBinary()) {
|
|
193
|
+
console.log(`nikcli: using ${name}`)
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
if (installPackage(name) && verifyBinary()) {
|
|
198
|
+
console.log(`nikcli: using ${name} (fetched separately)`)
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
}
|
|
135
202
|
}
|
|
136
203
|
|
|
137
|
-
|
|
138
|
-
|
|
204
|
+
throw new Error(
|
|
205
|
+
`could not resolve a native binary for ${platform}-${arch}. Tried: ${names.join(", ")}.\n` +
|
|
206
|
+
`The 'nikcli' command will try again at runtime. To fix it now, install one of those packages manually,\n` +
|
|
207
|
+
`or use the standalone installer: https://nikcli.store/install`,
|
|
208
|
+
)
|
|
139
209
|
}
|
|
140
210
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
211
|
+
try {
|
|
212
|
+
main()
|
|
213
|
+
} catch (error) {
|
|
214
|
+
// Non-fatal on purpose: the wrapper resolves the binary at runtime as well.
|
|
215
|
+
console.warn(`nikcli postinstall: ${error.message}`)
|
|
216
|
+
}
|