100xprompt-cli 0.1.5
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/100xprompt.js +69 -0
- package/package.json +23 -0
- package/postinstall.mjs +366 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
|
|
6
|
+
function getPlatformPackageName() {
|
|
7
|
+
const platform = process.platform
|
|
8
|
+
const arch = process.arch
|
|
9
|
+
const isMusl = process.report?.getReport?.()?.header?.glibcVersionRuntime === undefined
|
|
10
|
+
|
|
11
|
+
let osName
|
|
12
|
+
if (platform === 'darwin') osName = 'darwin'
|
|
13
|
+
else if (platform === 'linux') osName = 'linux'
|
|
14
|
+
else if (platform === 'win32') osName = 'windows'
|
|
15
|
+
|
|
16
|
+
let pkgName = `@100xprompt/cli-${osName}-${arch}`
|
|
17
|
+
if (platform === 'linux' && isMusl) {
|
|
18
|
+
pkgName = `@100xprompt/cli-linux-${arch}-musl`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return pkgName
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function findBinary() {
|
|
25
|
+
const pkgName = getPlatformPackageName()
|
|
26
|
+
const possiblePaths = [
|
|
27
|
+
path.join(__dirname, '..', '..', pkgName, 'bin', '100xprompt'),
|
|
28
|
+
path.join(__dirname, '..', '..', pkgName, 'bin', '100xprompt.exe'),
|
|
29
|
+
path.join(__dirname, '..', '..', 'node_modules', pkgName, 'bin', '100xprompt'),
|
|
30
|
+
path.join(__dirname, '..', '..', 'node_modules', pkgName, 'bin', '100xprompt.exe'),
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
for (const p of possiblePaths) {
|
|
34
|
+
if (fs.existsSync(p)) return p
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const pkgPath = require.resolve(pkgName + '/package.json')
|
|
39
|
+
const binPath = path.join(path.dirname(pkgPath), 'bin', '100xprompt')
|
|
40
|
+
if (fs.existsSync(binPath)) return binPath
|
|
41
|
+
if (fs.existsSync(binPath + '.exe')) return binPath + '.exe'
|
|
42
|
+
} catch {}
|
|
43
|
+
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const binaryPath = findBinary()
|
|
48
|
+
if (!binaryPath) {
|
|
49
|
+
console.error(`Could not find binary for platform: ${getPlatformPackageName()}`)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
54
|
+
stdio: 'inherit',
|
|
55
|
+
env: process.env
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
child.on('error', (err) => {
|
|
59
|
+
console.error('[100XPrompt] Failed to start:', err.message)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
child.on('exit', (code, signal) => {
|
|
64
|
+
if (signal) {
|
|
65
|
+
console.error(`[100XPrompt] Process was killed with signal: ${signal}`)
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
68
|
+
process.exit(code || 0)
|
|
69
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "100xprompt-cli",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"bin": {
|
|
5
|
+
"100xprompt": "bin/100xprompt.js"
|
|
6
|
+
},
|
|
7
|
+
"scripts": {
|
|
8
|
+
"postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
|
|
9
|
+
},
|
|
10
|
+
"optionalDependencies": {
|
|
11
|
+
"@100xprompt/cli-linux-arm64": "0.1.5",
|
|
12
|
+
"@100xprompt/cli-linux-x64": "0.1.5",
|
|
13
|
+
"@100xprompt/cli-linux-x64-baseline": "0.1.5",
|
|
14
|
+
"@100xprompt/cli-linux-arm64-musl": "0.1.5",
|
|
15
|
+
"@100xprompt/cli-linux-x64-musl": "0.1.5",
|
|
16
|
+
"@100xprompt/cli-linux-x64-baseline-musl": "0.1.5",
|
|
17
|
+
"@100xprompt/cli-darwin-arm64": "0.1.5",
|
|
18
|
+
"@100xprompt/cli-darwin-x64": "0.1.5",
|
|
19
|
+
"@100xprompt/cli-darwin-x64-baseline": "0.1.5",
|
|
20
|
+
"@100xprompt/cli-windows-x64": "0.1.5",
|
|
21
|
+
"@100xprompt/cli-windows-x64-baseline": "0.1.5"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs"
|
|
4
|
+
import path from "path"
|
|
5
|
+
import os from "os"
|
|
6
|
+
import { execFileSync } from "child_process"
|
|
7
|
+
import { fileURLToPath } from "url"
|
|
8
|
+
import { createRequire } from "module"
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
+
const require = createRequire(import.meta.url)
|
|
12
|
+
|
|
13
|
+
function detectPlatformAndArch() {
|
|
14
|
+
// Map platform names
|
|
15
|
+
let platform
|
|
16
|
+
switch (os.platform()) {
|
|
17
|
+
case "darwin":
|
|
18
|
+
platform = "darwin"
|
|
19
|
+
break
|
|
20
|
+
case "linux":
|
|
21
|
+
platform = "linux"
|
|
22
|
+
break
|
|
23
|
+
case "win32":
|
|
24
|
+
platform = "windows"
|
|
25
|
+
break
|
|
26
|
+
default:
|
|
27
|
+
platform = os.platform()
|
|
28
|
+
break
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Map architecture names
|
|
32
|
+
let arch
|
|
33
|
+
switch (os.arch()) {
|
|
34
|
+
case "x64":
|
|
35
|
+
arch = "x64"
|
|
36
|
+
break
|
|
37
|
+
case "arm64":
|
|
38
|
+
arch = "arm64"
|
|
39
|
+
break
|
|
40
|
+
case "arm":
|
|
41
|
+
arch = "arm"
|
|
42
|
+
break
|
|
43
|
+
default:
|
|
44
|
+
arch = os.arch()
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// glibc vs musl matters for the Linux build variant. When the runtime report
|
|
49
|
+
// exposes no glibc version, we're on a musl libc (Alpine, etc).
|
|
50
|
+
let isMusl = false
|
|
51
|
+
if (platform === "linux") {
|
|
52
|
+
try {
|
|
53
|
+
isMusl = process.report?.getReport?.()?.header?.glibcVersionRuntime === undefined
|
|
54
|
+
} catch {
|
|
55
|
+
isMusl = false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { platform, arch, isMusl }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Candidate platform-package names, in preference order. Mirrors the runtime
|
|
63
|
+
// wrapper (bin/100xprompt.js) and also probes the `-baseline` variant as a
|
|
64
|
+
// fallback for older CPUs without AVX2.
|
|
65
|
+
function candidatePackageNames() {
|
|
66
|
+
const { platform, arch, isMusl } = detectPlatformAndArch()
|
|
67
|
+
const base = `@100xprompt/cli-${platform}-${arch}`
|
|
68
|
+
const names = []
|
|
69
|
+
if (platform === "linux" && isMusl) {
|
|
70
|
+
names.push(`${base}-musl`, `${base}-baseline-musl`)
|
|
71
|
+
}
|
|
72
|
+
names.push(base, `${base}-baseline`)
|
|
73
|
+
return names
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function findBinary() {
|
|
77
|
+
const { platform } = detectPlatformAndArch()
|
|
78
|
+
const binaryName = platform === "windows" ? "100xprompt.exe" : "100xprompt"
|
|
79
|
+
|
|
80
|
+
for (const packageName of candidatePackageNames()) {
|
|
81
|
+
// Probe common install layouts (sibling package, hoisted node_modules,
|
|
82
|
+
// and require.resolve) so we work under npm/bun/pnpm/yarn and global installs.
|
|
83
|
+
const candidates = []
|
|
84
|
+
try {
|
|
85
|
+
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
|
86
|
+
candidates.push(path.join(path.dirname(packageJsonPath), "bin", binaryName))
|
|
87
|
+
} catch {
|
|
88
|
+
/* not resolvable from here — fall through to path probing */
|
|
89
|
+
}
|
|
90
|
+
candidates.push(
|
|
91
|
+
path.join(__dirname, "..", packageName, "bin", binaryName),
|
|
92
|
+
path.join(__dirname, "node_modules", packageName, "bin", binaryName),
|
|
93
|
+
path.join(__dirname, "..", "node_modules", packageName, "bin", binaryName),
|
|
94
|
+
path.join(__dirname, "..", "..", packageName, "bin", binaryName),
|
|
95
|
+
path.join(__dirname, "..", "..", "node_modules", packageName, "bin", binaryName),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
for (const binaryPath of candidates) {
|
|
99
|
+
if (fs.existsSync(binaryPath)) {
|
|
100
|
+
return { binaryPath, binaryName }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Could not find platform binary (tried: ${candidatePackageNames().join(", ")})`,
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// The bun-compiled binary must be executable for the wrapper's spawn() to run it
|
|
111
|
+
// without the user ever needing sudo. npm/tar can drop the +x bit on some
|
|
112
|
+
// installs, so re-assert it here. Best-effort: never throw.
|
|
113
|
+
function ensureExecutable(binaryPath) {
|
|
114
|
+
try {
|
|
115
|
+
fs.chmodSync(binaryPath, 0o755)
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.warn(`Could not set executable bit on ${binaryPath}: ${err.message}`)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// macOS kills unsigned/foreign-signed binaries with SIGKILL. Ad-hoc re-sign the
|
|
122
|
+
// binary in place so it runs without Gatekeeper intervention (and without sudo).
|
|
123
|
+
// Best-effort: codesign may be absent or the file read-only — never fail install.
|
|
124
|
+
function codesignDarwin(binaryPath) {
|
|
125
|
+
if (os.platform() !== "darwin") return
|
|
126
|
+
try {
|
|
127
|
+
execFileSync("codesign", ["--remove-signature", binaryPath], { stdio: "ignore" })
|
|
128
|
+
} catch {
|
|
129
|
+
/* nothing to remove — fine */
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
execFileSync(
|
|
133
|
+
"codesign",
|
|
134
|
+
["--sign", "-", "--force", "--preserve-metadata=entitlements,requirements,flags,runtime", binaryPath],
|
|
135
|
+
{ stdio: "ignore" },
|
|
136
|
+
)
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.warn(`Could not ad-hoc sign ${binaryPath}: ${err.message}`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function prepareBinDirectory(binaryName) {
|
|
143
|
+
const binDir = path.join(__dirname, "bin")
|
|
144
|
+
const targetPath = path.join(binDir, binaryName)
|
|
145
|
+
|
|
146
|
+
// Ensure bin directory exists
|
|
147
|
+
if (!fs.existsSync(binDir)) {
|
|
148
|
+
fs.mkdirSync(binDir, { recursive: true })
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Remove existing binary/symlink if it exists
|
|
152
|
+
if (fs.existsSync(targetPath) || fs.lstatSync(targetPath, { throwIfNoEntry: false })) {
|
|
153
|
+
try {
|
|
154
|
+
fs.unlinkSync(targetPath)
|
|
155
|
+
} catch {
|
|
156
|
+
/* ignore — overwritten below */
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { binDir, targetPath }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Make the platform binary reachable at <pkg>/bin/<name>. Prefer a symlink; if
|
|
164
|
+
// the filesystem/permissions reject symlinks (Windows without dev mode, locked
|
|
165
|
+
// dirs), copy instead. The runtime wrapper resolves the binary on its own, so a
|
|
166
|
+
// failure here is non-fatal and must never break the npm install.
|
|
167
|
+
function linkBinary(sourcePath, binaryName) {
|
|
168
|
+
let targetPath
|
|
169
|
+
try {
|
|
170
|
+
;({ targetPath } = prepareBinDirectory(binaryName))
|
|
171
|
+
} catch (err) {
|
|
172
|
+
console.warn(`Could not prepare bin directory: ${err.message}`)
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
fs.symlinkSync(sourcePath, targetPath)
|
|
178
|
+
console.log(`100xprompt binary symlinked: ${targetPath} -> ${sourcePath}`)
|
|
179
|
+
} catch (symlinkErr) {
|
|
180
|
+
try {
|
|
181
|
+
fs.copyFileSync(sourcePath, targetPath)
|
|
182
|
+
ensureExecutable(targetPath)
|
|
183
|
+
console.log(`100xprompt binary copied: ${targetPath}`)
|
|
184
|
+
} catch (copyErr) {
|
|
185
|
+
// Wrapper still resolves the binary directly; just warn.
|
|
186
|
+
console.warn(`Could not link binary (symlink: ${symlinkErr.message}; copy: ${copyErr.message})`)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function createConfig() {
|
|
192
|
+
const home = os.homedir()
|
|
193
|
+
// Primary: XDG config path (what the app reads via xdg-basedir)
|
|
194
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config")
|
|
195
|
+
const configDir = path.join(xdgConfigHome, "100xprompt")
|
|
196
|
+
const configPath = path.join(configDir, "100xprompt.json")
|
|
197
|
+
// Legacy fallback: also check ~/.100xprompt
|
|
198
|
+
const legacyConfigDir = path.join(home, ".100xprompt")
|
|
199
|
+
const legacyConfigPath = path.join(legacyConfigDir, "100xprompt.json")
|
|
200
|
+
|
|
201
|
+
const defaultConfig = {
|
|
202
|
+
$schema: "https://proxy.100xprompt.com/config.json",
|
|
203
|
+
compaction: {
|
|
204
|
+
auto: {
|
|
205
|
+
enabled: true,
|
|
206
|
+
// Base auto-compaction threshold. The code uses model-aware thresholds:
|
|
207
|
+
// Pro ~160k, Flash ~500k; this value is the fallback for other models.
|
|
208
|
+
compactionThreshold: 160000,
|
|
209
|
+
tailPreserveTurns: 5,
|
|
210
|
+
},
|
|
211
|
+
micro: {
|
|
212
|
+
enabled: true,
|
|
213
|
+
hotTailCount: 5,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
// Determine which config path to use:
|
|
220
|
+
// If legacy path exists but XDG path doesn't, use legacy for validation/merge
|
|
221
|
+
// Otherwise prefer XDG path (the app reads from both)
|
|
222
|
+
let activeConfigDir = configDir
|
|
223
|
+
let activeConfigPath = configPath
|
|
224
|
+
if (fs.existsSync(legacyConfigPath) && !fs.existsSync(configPath)) {
|
|
225
|
+
activeConfigDir = legacyConfigDir
|
|
226
|
+
activeConfigPath = legacyConfigPath
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!fs.existsSync(activeConfigDir)) {
|
|
230
|
+
fs.mkdirSync(activeConfigDir, { recursive: true })
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (fs.existsSync(activeConfigPath)) {
|
|
234
|
+
// Validate existing config and merge missing defaults
|
|
235
|
+
try {
|
|
236
|
+
const raw = fs.readFileSync(activeConfigPath, "utf-8")
|
|
237
|
+
const existing = JSON.parse(raw)
|
|
238
|
+
|
|
239
|
+
let needsUpdate = false
|
|
240
|
+
|
|
241
|
+
// Ensure compaction section exists with all required fields
|
|
242
|
+
if (!existing.compaction) {
|
|
243
|
+
existing.compaction = defaultConfig.compaction
|
|
244
|
+
needsUpdate = true
|
|
245
|
+
} else {
|
|
246
|
+
if (!existing.compaction.auto) {
|
|
247
|
+
existing.compaction.auto = defaultConfig.compaction.auto
|
|
248
|
+
needsUpdate = true
|
|
249
|
+
} else {
|
|
250
|
+
// Merge individual auto fields if missing
|
|
251
|
+
for (const [key, value] of Object.entries(defaultConfig.compaction.auto)) {
|
|
252
|
+
if (existing.compaction.auto[key] === undefined) {
|
|
253
|
+
existing.compaction.auto[key] = value
|
|
254
|
+
needsUpdate = true
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (!existing.compaction.micro) {
|
|
259
|
+
existing.compaction.micro = defaultConfig.compaction.micro
|
|
260
|
+
needsUpdate = true
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (!existing.$schema) {
|
|
265
|
+
existing.$schema = defaultConfig.$schema
|
|
266
|
+
needsUpdate = true
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (needsUpdate) {
|
|
270
|
+
fs.writeFileSync(activeConfigPath, JSON.stringify(existing, null, 2))
|
|
271
|
+
try { fs.chmodSync(activeConfigPath, 0o644) } catch { }
|
|
272
|
+
console.log(`Config updated with missing defaults at ${activeConfigPath}`)
|
|
273
|
+
} else {
|
|
274
|
+
console.log(`Config already exists and is valid at ${activeConfigPath}, skipping.`)
|
|
275
|
+
}
|
|
276
|
+
} catch (parseError) {
|
|
277
|
+
// Existing file is corrupt — back it up and recreate
|
|
278
|
+
const backupPath = activeConfigPath + ".bak"
|
|
279
|
+
console.warn(`Config at ${activeConfigPath} is corrupt, backing up to ${backupPath} and recreating.`)
|
|
280
|
+
try { fs.copyFileSync(activeConfigPath, backupPath) } catch { }
|
|
281
|
+
fs.writeFileSync(activeConfigPath, JSON.stringify(defaultConfig, null, 2))
|
|
282
|
+
try { fs.chmodSync(activeConfigPath, 0o644) } catch { }
|
|
283
|
+
console.log(`Default config recreated at ${activeConfigPath}`)
|
|
284
|
+
}
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
fs.writeFileSync(activeConfigPath, JSON.stringify(defaultConfig, null, 2))
|
|
289
|
+
try { fs.chmodSync(activeConfigPath, 0o644) } catch { }
|
|
290
|
+
console.log(`Default config created at ${activeConfigPath}`)
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.warn(`Failed to create default config: ${error.message}`)
|
|
293
|
+
// Don't fail postinstall just because config creation failed
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function buildBanner(color) {
|
|
298
|
+
const p = (code, s) => (color ? `\x1b[${code}m${s}\x1b[0m` : s)
|
|
299
|
+
const bold = (s) => p("1", s)
|
|
300
|
+
const dim = (s) => p("2", s)
|
|
301
|
+
const accent = (s) => p("38;5;215", s) // soft orange
|
|
302
|
+
const violet = (s) => p("38;5;141", s)
|
|
303
|
+
const cyan = (s) => p("38;5;117", s)
|
|
304
|
+
const green = (s) => p("38;5;114", s)
|
|
305
|
+
const pink = (s) => p("38;5;213", s)
|
|
306
|
+
|
|
307
|
+
const rule = violet(" " + "━".repeat(48))
|
|
308
|
+
const cmd = color ? `\x1b[1m\x1b[30m\x1b[48;5;215m 100xprompt \x1b[0m` : "100xprompt"
|
|
309
|
+
|
|
310
|
+
return [
|
|
311
|
+
"",
|
|
312
|
+
rule,
|
|
313
|
+
"",
|
|
314
|
+
` ${accent("✦")} ${bold("100XPrompt Pro")} ${dim("· AI coding assistant in your terminal")}`,
|
|
315
|
+
"",
|
|
316
|
+
` ${green("▸")} Run ${cmd} ${dim("to launch")}`,
|
|
317
|
+
` ${dim(" then type your first prompt and hit enter")}`,
|
|
318
|
+
"",
|
|
319
|
+
` ${dim("made with")} ${pink("♥")} ${dim("by the")} ${cyan("100xprompt")} ${dim("team")}`,
|
|
320
|
+
"",
|
|
321
|
+
rule,
|
|
322
|
+
"",
|
|
323
|
+
"",
|
|
324
|
+
].join("\n")
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function printBanner() {
|
|
328
|
+
// npm hides postinstall stdout by default, so write straight to the controlling
|
|
329
|
+
// terminal (/dev/tty) — that bypasses npm's capture and shows the banner on install.
|
|
330
|
+
try {
|
|
331
|
+
fs.writeFileSync("/dev/tty", buildBanner(true))
|
|
332
|
+
} catch {
|
|
333
|
+
process.stdout.write(buildBanner(!!process.stdout.isTTY))
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function main() {
|
|
338
|
+
// Binary setup is best-effort: the runtime wrapper (bin/100xprompt.js) resolves
|
|
339
|
+
// and launches the platform binary on its own, so postinstall must NEVER fail
|
|
340
|
+
// the npm install — a non-zero exit is exactly what pushes users to retry with
|
|
341
|
+
// sudo. We only normalize permissions/signing so the binary runs unprivileged.
|
|
342
|
+
try {
|
|
343
|
+
const { binaryPath, binaryName } = findBinary()
|
|
344
|
+
ensureExecutable(binaryPath) // fixes "permission denied" → no sudo needed to run
|
|
345
|
+
codesignDarwin(binaryPath) // prevents macOS SIGKILL of the unsigned binary
|
|
346
|
+
linkBinary(binaryPath, binaryName)
|
|
347
|
+
} catch (error) {
|
|
348
|
+
// Don't fail — the wrapper still finds the binary at runtime.
|
|
349
|
+
console.warn(`100xprompt binary setup skipped: ${error.message}`)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
createConfig()
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.warn(`Config setup skipped: ${error.message}`)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
printBanner()
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
main().catch((error) => {
|
|
362
|
+
// Always exit 0: a failed postinstall blocks the whole install and is the
|
|
363
|
+
// reason users reach for sudo. Never let that happen.
|
|
364
|
+
console.warn("Postinstall completed with warnings:", error?.message ?? error)
|
|
365
|
+
process.exit(0)
|
|
366
|
+
})
|