@stevezhou/sisu 0.3.14 → 0.3.15

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 CHANGED
@@ -13,7 +13,7 @@ sisu login
13
13
  sisu
14
14
  ```
15
15
 
16
- `npm install -g` is a small JS package. postinstall fetches the stamped SiSu TUI pager for **this package version** into `~/.sisu/bin` when a prebuilt exists. GitHub Release tags ship `darwin-arm64`, `linux-x64`, and `linux-arm64`. `darwin-x64` is opt-in (`workflow_dispatch` with `platforms` containing `darwin-x64`) and often missing; platforms without a binary, or a missing GitHub Release asset, keep the Node TUI.
16
+ Postinstall links `sisu` into `~/.local/bin` (Debian/login PATH) and prints an `export PATH=...` line if this shell still cannot see the command. `npm install -g` is a small JS package. It also fetches the stamped SiSu TUI pager for **this package version** into `~/.sisu/bin` when a prebuilt exists. GitHub Release tags ship `darwin-arm64`, `linux-x64`, and `linux-arm64`. `darwin-x64` is opt-in (`workflow_dispatch` with `platforms` containing `darwin-x64`) and often missing; platforms without a binary, or a missing GitHub Release asset, keep the Node TUI.
17
17
 
18
18
  Requires Node.js 20 or newer. `npx sisu` works without a global install.
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stevezhou/sisu",
3
- "version": "0.3.14",
3
+ "version": "0.3.15",
4
4
  "description": "SiSu CLI — 思溯 / SiSu · 思有所溯",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://www.sisu.chat",
@@ -26,7 +26,8 @@
26
26
  "NOTICE",
27
27
  "third_party/grok-build",
28
28
  "scripts/postinstall.js",
29
- "scripts/install-pager.js"
29
+ "scripts/install-pager.js",
30
+ "scripts/ensure-cli-path.js"
30
31
  ],
31
32
  "publishConfig": {
32
33
  "access": "public"
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ /** After `npm i -g`, put `sisu` on a PATH users actually have.
3
+ * npm's global bin is often missing from Debian/login PATH.
4
+ */
5
+ const fs = require('fs')
6
+ const os = require('os')
7
+ const path = require('path')
8
+
9
+ function pathDirs(pathEnv = process.env.PATH || '') {
10
+ return pathEnv.split(path.delimiter).filter(Boolean)
11
+ }
12
+
13
+ function pathContains(dir, pathEnv = process.env.PATH || '') {
14
+ const target = path.resolve(dir)
15
+ return pathDirs(pathEnv).some((entry) => {
16
+ try {
17
+ return path.resolve(entry) === target
18
+ } catch {
19
+ return false
20
+ }
21
+ })
22
+ }
23
+
24
+ function globalSisuBin(options = {}) {
25
+ const name = options.platform === 'win32' ? 'sisu.cmd' : 'sisu'
26
+ const prefix = options.prefix || process.env.npm_config_prefix || ''
27
+ const fromPrefix = prefix
28
+ ? (options.platform === 'win32' ? path.join(prefix, name) : path.join(prefix, 'bin', name))
29
+ : ''
30
+ const fromLayout = path.resolve(
31
+ options.packageRoot || path.join(__dirname, '..'),
32
+ '..',
33
+ '..',
34
+ options.platform === 'win32' ? name : path.join('bin', name),
35
+ )
36
+ const exists = options.exists || ((file) => fs.existsSync(file))
37
+ for (const candidate of [fromPrefix, fromLayout]) {
38
+ if (candidate && exists(candidate)) return candidate
39
+ }
40
+ return fromPrefix || fromLayout
41
+ }
42
+
43
+ function userLocalBin(home = os.homedir()) {
44
+ return path.join(home, '.local', 'bin')
45
+ }
46
+
47
+ function ensureUserShim(target, options = {}) {
48
+ if ((options.platform || process.platform) === 'win32') return null
49
+ if (!target) return null
50
+ const dir = userLocalBin(options.home || os.homedir())
51
+ const dest = path.join(dir, 'sisu')
52
+ fs.mkdirSync(dir, { recursive: true, mode: 0o755 })
53
+ try {
54
+ if (fs.lstatSync(dest)) fs.unlinkSync(dest)
55
+ } catch {
56
+ // missing
57
+ }
58
+ fs.symlinkSync(path.resolve(target), dest)
59
+ try {
60
+ fs.chmodSync(dest, 0o755)
61
+ } catch {
62
+ // symlink mode follows the target on most POSIX
63
+ }
64
+ return dest
65
+ }
66
+
67
+ function pathHint(npmBin, localBin, pathEnv = process.env.PATH || '') {
68
+ const dirs = []
69
+ if (localBin && !pathContains(localBin, pathEnv)) dirs.push(localBin)
70
+ if (npmBin && !pathContains(npmBin, pathEnv)) dirs.push(npmBin)
71
+ if (!dirs.length) return ''
72
+ return `export PATH="${dirs.join(':')}:$PATH" && hash -r`
73
+ }
74
+
75
+ function installCliPath(options = {}) {
76
+ const writes = options.write || ((text) => process.stdout.write(text))
77
+ const platform = options.platform || process.platform
78
+ const bin = globalSisuBin(options)
79
+ const npmBinDir = bin ? path.dirname(bin) : ''
80
+ let shim = null
81
+ try {
82
+ shim = ensureUserShim(bin, options)
83
+ } catch (error) {
84
+ writes(`sisu: could not link ~/.local/bin/sisu (${error instanceof Error ? error.message : String(error)})\n`)
85
+ }
86
+ const localDir = shim ? path.dirname(shim) : userLocalBin(options.home)
87
+ const hint = pathHint(npmBinDir, localDir, options.pathEnv)
88
+ if (bin) writes(`sisu: command -> ${shim || bin}\n`)
89
+ if (hint) {
90
+ writes('sisu: if `sisu` is not found in this shell, run:\n')
91
+ writes(` ${hint}\n`)
92
+ }
93
+ return { bin, shim, hint }
94
+ }
95
+
96
+ module.exports = {
97
+ ensureUserShim,
98
+ globalSisuBin,
99
+ installCliPath,
100
+ pathContains,
101
+ pathHint,
102
+ userLocalBin,
103
+ }
104
+
105
+ if (require.main === module) {
106
+ installCliPath()
107
+ }
@@ -3,6 +3,13 @@
3
3
  * Never fail `npm install` — Node TUI still works without the binary.
4
4
  */
5
5
  const { installPager } = require('./install-pager')
6
+ const { installCliPath } = require('./ensure-cli-path')
7
+
8
+ try {
9
+ installCliPath()
10
+ } catch (error) {
11
+ process.stdout.write(`sisu: path setup skipped (${error instanceof Error ? error.message : String(error)})\n`)
12
+ }
6
13
 
7
14
  installPager().then(
8
15
  (result) => {