railwise-ai 1.2.30 → 1.2.32

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/railwise CHANGED
@@ -160,7 +160,15 @@ function findBinary(startDir) {
160
160
  }
161
161
  }
162
162
 
163
- const resolved = findBinary(scriptDir)
163
+ function findVendor() {
164
+ const root = path.resolve(scriptDir, "..")
165
+ for (const name of names) {
166
+ const candidate = path.join(root, "vendor", name, "bin", binary)
167
+ if (fs.existsSync(candidate)) return candidate
168
+ }
169
+ }
170
+
171
+ const resolved = findBinary(scriptDir) || findVendor()
164
172
  if (!resolved) {
165
173
  // Fallback: run from source via bun for local development
166
174
  const srcEntry = path.resolve(scriptDir, "..", "src", "index.ts")
package/package.json CHANGED
@@ -1,25 +1,24 @@
1
1
  {
2
2
  "name": "railwise-ai",
3
3
  "bin": {
4
- "railwise": "./bin/railwise",
5
- "rw": "./bin/railwise"
4
+ "railwise": "./bin/railwise"
6
5
  },
7
6
  "scripts": {
8
7
  "postinstall": "node ./postinstall.mjs"
9
8
  },
10
- "version": "1.2.30",
9
+ "version": "1.2.32",
11
10
  "license": "MIT",
12
11
  "repository": {
13
12
  "type": "git",
14
- "url": "https://github.com/railwise-cn/RAILWISE-CLI"
13
+ "url": "git+https://github.com/railwise-cn/RAILWISE-CLI.git"
15
14
  },
16
15
  "optionalDependencies": {
17
- "railwise-darwin-x64": "1.2.30",
18
- "railwise-linux-x64-baseline-musl": "1.2.30",
19
- "railwise-linux-x64-baseline": "1.2.30",
20
- "railwise-linux-arm64": "1.2.30",
21
- "railwise-darwin-arm64": "1.2.30",
22
- "railwise-windows-x64": "1.2.30",
23
- "railwise-linux-x64": "1.2.30"
16
+ "railwise-linux-arm64": "1.2.32",
17
+ "railwise-darwin-x64": "1.2.32",
18
+ "railwise-windows-x64": "1.2.32",
19
+ "railwise-linux-x64": "1.2.32",
20
+ "railwise-linux-x64-baseline": "1.2.32",
21
+ "railwise-darwin-arm64": "1.2.32",
22
+ "railwise-linux-x64-baseline-musl": "1.2.32"
24
23
  }
25
24
  }
package/postinstall.mjs CHANGED
@@ -1,125 +1,219 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import fs from "fs"
4
- import path from "path"
5
4
  import os from "os"
6
- import { fileURLToPath } from "url"
5
+ import path from "path"
6
+ import { spawnSync } from "child_process"
7
7
  import { createRequire } from "module"
8
+ import { fileURLToPath } from "url"
8
9
 
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
10
11
  const require = createRequire(import.meta.url)
12
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"))
13
+
14
+ function platform() {
15
+ if (os.platform() === "darwin") return "darwin"
16
+ if (os.platform() === "linux") return "linux"
17
+ if (os.platform() === "win32") return "windows"
18
+ return os.platform()
19
+ }
20
+
21
+ function arch() {
22
+ if (os.arch() === "x64") return "x64"
23
+ if (os.arch() === "arm64") return "arm64"
24
+ if (os.arch() === "arm") return "arm"
25
+ return os.arch()
26
+ }
27
+
28
+ const system = platform()
29
+ const cpu = arch()
30
+ const base = `railwise-${system}-${cpu}`
31
+ const binary = system === "windows" ? "railwise.exe" : "railwise"
11
32
 
12
- function detectPlatformAndArch() {
13
- // Map platform names
14
- let platform
15
- switch (os.platform()) {
16
- case "darwin":
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
33
+ function supportsAvx2() {
34
+ if (cpu !== "x64") return false
35
+
36
+ if (system === "linux") {
37
+ if (!fs.existsSync("/proc/cpuinfo")) return false
38
+ return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
39
+ }
40
+
41
+ if (system === "darwin") {
42
+ const result = spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
43
+ encoding: "utf8",
44
+ timeout: 1500,
45
+ })
46
+ if (result.status !== 0) return false
47
+ return (result.stdout || "").trim() === "1"
28
48
  }
29
49
 
30
- // Map architecture names
31
- let arch
32
- switch (os.arch()) {
33
- case "x64":
34
- arch = "x64"
35
- break
36
- case "arm64":
37
- arch = "arm64"
38
- break
39
- case "arm":
40
- arch = "arm"
41
- break
42
- default:
43
- arch = os.arch()
44
- break
50
+ if (system === "windows") {
51
+ const cmd =
52
+ '(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
53
+ return ["powershell.exe", "pwsh.exe", "pwsh", "powershell"].some((exe) => {
54
+ const result = spawnSync(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd], {
55
+ encoding: "utf8",
56
+ timeout: 3000,
57
+ windowsHide: true,
58
+ })
59
+ if (result.status !== 0) return false
60
+ return ["true", "1"].includes((result.stdout || "").trim().toLowerCase())
61
+ })
45
62
  }
46
63
 
47
- return { platform, arch }
64
+ return false
65
+ }
66
+
67
+ function musl() {
68
+ if (system !== "linux") return false
69
+ if (fs.existsSync("/etc/alpine-release")) return true
70
+ const result = spawnSync("ldd", ["--version"], { encoding: "utf8" })
71
+ return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
48
72
  }
49
73
 
50
- function findBinary() {
51
- const { platform, arch } = detectPlatformAndArch()
52
- const packageName = `railwise-${platform}-${arch}`
53
- const binaryName = platform === "windows" ? "railwise.exe" : "railwise"
74
+ function names() {
75
+ const baseline = cpu === "x64" && !supportsAvx2()
54
76
 
55
- try {
56
- // Use require.resolve to find the package
57
- const packageJsonPath = require.resolve(`${packageName}/package.json`)
58
- const packageDir = path.dirname(packageJsonPath)
59
- const binaryPath = path.join(packageDir, "bin", binaryName)
77
+ if (system === "linux") {
78
+ if (musl()) {
79
+ if (cpu === "x64") {
80
+ if (baseline) return [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
81
+ return [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
82
+ }
83
+ return [`${base}-musl`, base]
84
+ }
60
85
 
61
- if (!fs.existsSync(binaryPath)) {
62
- throw new Error(`Binary not found at ${binaryPath}`)
86
+ if (cpu === "x64") {
87
+ if (baseline) return [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
88
+ return [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
63
89
  }
90
+ return [base, `${base}-musl`]
91
+ }
64
92
 
65
- return { binaryPath, binaryName }
66
- } catch (error) {
67
- throw new Error(`Could not find package ${packageName}: ${error.message}`)
93
+ if (cpu === "x64") {
94
+ if (baseline) return [`${base}-baseline`, base]
95
+ return [base, `${base}-baseline`]
68
96
  }
97
+ return [base]
69
98
  }
70
99
 
71
- function prepareBinDirectory(binaryName) {
72
- const binDir = path.join(__dirname, "bin")
73
- const targetPath = path.join(binDir, binaryName)
100
+ function optional(name) {
101
+ const file = require.resolve(`${name}/package.json`)
102
+ const dir = path.dirname(file)
103
+ const found = path.join(dir, "bin", binary)
104
+ if (!fs.existsSync(found)) throw new Error(`Binary not found at ${found}`)
105
+ return found
106
+ }
107
+
108
+ function vendor(name) {
109
+ return path.join(__dirname, "vendor", name, "bin", binary)
110
+ }
111
+
112
+ function run(exe, args) {
113
+ const result = spawnSync(exe, args, { stdio: "inherit", windowsHide: true })
114
+ if (result.error) throw result.error
115
+ if (result.status !== 0) throw new Error(`${exe} exited with ${result.status}`)
116
+ }
74
117
 
75
- // Ensure bin directory exists
76
- if (!fs.existsSync(binDir)) {
77
- fs.mkdirSync(binDir, { recursive: true })
118
+ function quote(value) {
119
+ return `'${value.replace(/'/g, "''")}'`
120
+ }
121
+
122
+ function extract(file, dir) {
123
+ if (file.endsWith(".tar.gz")) {
124
+ run("tar", ["-xzf", file, "-C", dir])
125
+ return
78
126
  }
79
127
 
80
- // Remove existing binary/symlink if it exists
81
- if (fs.existsSync(targetPath)) {
82
- fs.unlinkSync(targetPath)
128
+ if (system === "windows") {
129
+ const cmd = `Expand-Archive -Path ${quote(file)} -DestinationPath ${quote(dir)} -Force`
130
+ const exe = ["powershell.exe", "pwsh.exe", "pwsh", "powershell"].find((item) => {
131
+ const result = spawnSync(item, ["-NoProfile", "-NonInteractive", "-Command", "$PSVersionTable.PSVersion"], {
132
+ stdio: "ignore",
133
+ windowsHide: true,
134
+ })
135
+ return result.status === 0
136
+ })
137
+ if (!exe) throw new Error("PowerShell is required to extract the Windows binary fallback")
138
+ run(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd])
139
+ return
83
140
  }
84
141
 
85
- return { binDir, targetPath }
142
+ run("unzip", ["-oq", file, "-d", dir])
86
143
  }
87
144
 
88
- function symlinkBinary(sourcePath, binaryName) {
89
- const { targetPath } = prepareBinDirectory(binaryName)
90
-
91
- fs.symlinkSync(sourcePath, targetPath)
92
- console.log(`railwise binary symlinked: ${targetPath} -> ${sourcePath}`)
145
+ function timeout() {
146
+ const value = Number(process.env.RAILWISE_DOWNLOAD_TIMEOUT_MS || 60000)
147
+ if (Number.isFinite(value) && value > 0) return value
148
+ return 60000
149
+ }
93
150
 
94
- // Verify the file exists after operation
95
- if (!fs.existsSync(targetPath)) {
96
- throw new Error(`Failed to symlink binary to ${targetPath}`)
151
+ async function download(name) {
152
+ const ext = system === "linux" ? ".tar.gz" : ".zip"
153
+ const asset = `${name}${ext}`
154
+ const base = (
155
+ process.env.RAILWISE_RELEASE_BASE_URL ||
156
+ `https://github.com/railwise-cn/RAILWISE-CLI/releases/download/v${pkg.version}`
157
+ ).replace(/\/+$/, "")
158
+ const url = `${base}/${asset}`
159
+ const controller = new AbortController()
160
+ const timer = setTimeout(() => controller.abort(), timeout())
161
+ console.log(`Downloading ${asset} from ${base}`)
162
+
163
+ const tmp = path.join(os.tmpdir(), `${asset}.${process.pid}`)
164
+ const dir = path.join(__dirname, "vendor", name, "bin")
165
+ try {
166
+ const res = await fetch(url, {
167
+ signal: controller.signal,
168
+ headers: { "user-agent": `railwise-postinstall/${pkg.version}` },
169
+ })
170
+ if (!res.ok) throw new Error(`Download failed for ${asset}: ${res.status} ${res.statusText}`)
171
+
172
+ fs.rmSync(path.dirname(dir), { recursive: true, force: true })
173
+ fs.mkdirSync(dir, { recursive: true })
174
+ fs.writeFileSync(tmp, Buffer.from(await res.arrayBuffer()))
175
+ extract(tmp, dir)
176
+ } finally {
177
+ clearTimeout(timer)
178
+ fs.rmSync(tmp, { force: true })
97
179
  }
180
+
181
+ const found = vendor(name)
182
+ if (!fs.existsSync(found)) throw new Error(`Downloaded asset did not contain ${binary}`)
183
+ if (system !== "windows") fs.chmodSync(found, 0o755)
184
+ return found
98
185
  }
99
186
 
100
187
  async function main() {
101
- try {
102
- if (os.platform() === "win32") {
103
- // On Windows, the .exe is already included in the package and bin field points to it
104
- // No postinstall setup needed
105
- console.log("Windows detected: binary setup not needed (using packaged .exe)")
106
- return
188
+ const items = names()
189
+ const installed = items.flatMap((name) => {
190
+ try {
191
+ return [optional(name)]
192
+ } catch {
193
+ return []
107
194
  }
108
-
109
- // On non-Windows platforms, just verify the binary package exists
110
- // Don't replace the wrapper script - it handles binary execution
111
- const { binaryPath } = findBinary()
112
- console.log(`Platform binary verified at: ${binaryPath}`)
113
- console.log("Wrapper script will handle binary execution")
114
- } catch (error) {
115
- console.error("Failed to setup railwise binary:", error.message)
116
- process.exit(1)
195
+ })[0]
196
+ if (installed) {
197
+ console.log(`Platform binary verified at: ${installed}`)
198
+ return
117
199
  }
118
- }
119
200
 
120
- try {
121
- main()
122
- } catch (error) {
123
- console.error("Postinstall script error:", error.message)
124
- process.exit(0)
201
+ const found = await items.reduce(async (prev, name) => {
202
+ const value = await prev
203
+ if (value) return value
204
+ try {
205
+ return await download(name)
206
+ } catch (error) {
207
+ console.warn(`Could not install fallback binary ${name}: ${error instanceof Error ? error.message : String(error)}`)
208
+ return undefined
209
+ }
210
+ }, Promise.resolve(undefined))
211
+
212
+ if (!found) throw new Error(`Could not install a RAILWISE binary for ${system}/${cpu}`)
213
+ console.log(`Platform binary downloaded to: ${found}`)
125
214
  }
215
+
216
+ main().catch((error) => {
217
+ console.error("Failed to setup railwise binary:", error instanceof Error ? error.message : String(error))
218
+ process.exit(1)
219
+ })