fuigo 0.0.1 → 1.0.2
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 +58 -6
- package/bin/fuigo +4 -0
- package/bin/fuigo-bootstrap.js +142 -0
- package/bin/postinstall.js +268 -0
- package/package.json +43 -19
- package/index.js +0 -3
package/README.md
CHANGED
|
@@ -1,9 +1,61 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Fuigo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Bring Fuigo into your terminal. Fast, flicker-free CLI built for plans, subagents, and parallel work.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
the mechanism that supplies the force, worked under the direction of the
|
|
7
|
-
村下 (*murage*), the furnace master.
|
|
5
|
+
**[Homepage](https://x.ai/cli)** | **[Documentation](https://docs.x.ai/build/overview)**
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
curl -fsSL https://x.ai/cli/install.sh | bash
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install with npm:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm i -g fuigo
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Get Started
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Launch the interactive TUI
|
|
23
|
+
fuigo
|
|
24
|
+
|
|
25
|
+
# Run a single task
|
|
26
|
+
fuigo -p "Explain this codebase"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
On first launch, Fuigo opens your browser to authenticate. For CI or headless environments, use an API key from [console.x.ai](https://console.x.ai):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
export FUIGO_API_KEY="fuigo-..."
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Update
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
fuigo update
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or if installed via npm:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm i -g fuigo@latest
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Supported Platforms
|
|
48
|
+
|
|
49
|
+
| Platform | Architecture |
|
|
50
|
+
|---|---|
|
|
51
|
+
| macOS | Apple Silicon (arm64) |
|
|
52
|
+
| Linux | x86_64, arm64 |
|
|
53
|
+
| Windows | x86_64 |
|
|
54
|
+
|
|
55
|
+
## Documentation
|
|
56
|
+
|
|
57
|
+
For full documentation including configuration, MCP servers, custom models, headless mode, agent mode, and more, visit [docs.x.ai/build/overview](https://docs.x.ai/build/overview).
|
|
58
|
+
|
|
59
|
+
## Feedback
|
|
60
|
+
|
|
61
|
+
Run `/feedback` inside Fuigo to report issues or send feedback directly.
|
package/bin/fuigo
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Resolves the fuigo binary and runs it, in order of preference:
|
|
3
|
+
// 1. $FUIGO_HOME/bin/fuigo, the versioned symlink postinstall.js installs
|
|
4
|
+
// 2. bootstrap it from the per-platform fuigo-<platform>
|
|
5
|
+
// package, decompressing the compressed binary into $FUIGO_HOME/bin
|
|
6
|
+
// 3. decompress in place under node_modules (no resolvable version, or
|
|
7
|
+
// an unwritable home)
|
|
8
|
+
//
|
|
9
|
+
// Binaries ship brotli-compressed to stay under npm's tarball size limit.
|
|
10
|
+
const { spawn } = require('child_process');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const zlib = require('zlib');
|
|
15
|
+
|
|
16
|
+
const pkgName = 'fuigo';
|
|
17
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
18
|
+
const EXE = IS_WINDOWS ? '.exe' : '';
|
|
19
|
+
const BIN_NAME = `fuigo${EXE}`;
|
|
20
|
+
// $FUIGO_HOME/bin (else ~/.fuigo/bin), matching the Rust fuigo_home():
|
|
21
|
+
// a symlinked $HOME resolves the same way.
|
|
22
|
+
function defaultFuigoHome() {
|
|
23
|
+
const home = os.homedir();
|
|
24
|
+
try { return path.join(fs.realpathSync(home), '.fuigo'); } catch { return path.join(home, '.fuigo'); }
|
|
25
|
+
}
|
|
26
|
+
const FUIGO_HOME = process.env.FUIGO_HOME ?? defaultFuigoHome();
|
|
27
|
+
const CANONICAL_DIR = path.join(FUIGO_HOME, 'bin');
|
|
28
|
+
const CANONICAL_PATH = path.join(CANONICAL_DIR, BIN_NAME);
|
|
29
|
+
|
|
30
|
+
function readLocalVersion() {
|
|
31
|
+
try { return require('../package.json').version; } catch { return undefined; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Returns null when npm skipped the matching optional dependency
|
|
35
|
+
// (unsupported platform, or --no-optional).
|
|
36
|
+
function resolvePlatformPackageDir() {
|
|
37
|
+
const platformPkg = `fuigo-${process.platform}-${process.arch}`;
|
|
38
|
+
try {
|
|
39
|
+
return path.dirname(require.resolve(`${platformPkg}/package.json`));
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function writeVendorBinary(brotliPath, binaryPath, destPath) {
|
|
46
|
+
const tmp = destPath + `.tmp.${process.pid}`;
|
|
47
|
+
try {
|
|
48
|
+
if (fs.existsSync(brotliPath)) {
|
|
49
|
+
fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brotliPath)));
|
|
50
|
+
} else if (fs.existsSync(binaryPath)) {
|
|
51
|
+
fs.copyFileSync(binaryPath, tmp);
|
|
52
|
+
} else {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (!IS_WINDOWS) fs.chmodSync(tmp, 0o755);
|
|
56
|
+
fs.renameSync(tmp, destPath);
|
|
57
|
+
return true;
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
} finally {
|
|
61
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function swapCanonical(versionedName, versionedPath) {
|
|
66
|
+
if (!IS_WINDOWS) {
|
|
67
|
+
const tmpLink = CANONICAL_PATH + `.link.${process.pid}`;
|
|
68
|
+
try { fs.unlinkSync(tmpLink); } catch {}
|
|
69
|
+
fs.symlinkSync(versionedName, tmpLink);
|
|
70
|
+
fs.renameSync(tmpLink, CANONICAL_PATH);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const oldPath = CANONICAL_PATH + '.old';
|
|
74
|
+
try { fs.unlinkSync(oldPath); } catch {}
|
|
75
|
+
try {
|
|
76
|
+
try { fs.unlinkSync(CANONICAL_PATH); } catch {}
|
|
77
|
+
fs.copyFileSync(versionedPath, CANONICAL_PATH);
|
|
78
|
+
} catch {
|
|
79
|
+
fs.renameSync(CANONICAL_PATH, oldPath);
|
|
80
|
+
try {
|
|
81
|
+
fs.copyFileSync(versionedPath, CANONICAL_PATH);
|
|
82
|
+
} catch {
|
|
83
|
+
try { fs.renameSync(oldPath, CANONICAL_PATH); } catch {}
|
|
84
|
+
throw new Error('locked');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function bootstrapCanonical(brotliPath, binaryPath, version) {
|
|
90
|
+
try {
|
|
91
|
+
fs.mkdirSync(CANONICAL_DIR, { recursive: true });
|
|
92
|
+
const versionedName = `fuigo-${version}${EXE}`;
|
|
93
|
+
const versionedPath = path.join(CANONICAL_DIR, versionedName);
|
|
94
|
+
if (!fs.existsSync(versionedPath) && !writeVendorBinary(brotliPath, binaryPath, versionedPath)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
swapCanonical(versionedName, versionedPath);
|
|
98
|
+
// null on a broken wire-up so the caller falls back to in-place launch.
|
|
99
|
+
return fs.existsSync(CANONICAL_PATH) ? CANONICAL_PATH : null;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function resolveBinary() {
|
|
106
|
+
if (fs.existsSync(CANONICAL_PATH)) return CANONICAL_PATH;
|
|
107
|
+
|
|
108
|
+
const platformDir = resolvePlatformPackageDir();
|
|
109
|
+
if (!platformDir) {
|
|
110
|
+
console.error(`${pkgName}: no platform binary installed for ${process.platform}-${process.arch}.`);
|
|
111
|
+
console.error(` Expected sibling package fuigo-${process.platform}-${process.arch}.`);
|
|
112
|
+
console.error(` This usually means npm skipped optionalDependencies (e.g. --no-optional)`);
|
|
113
|
+
console.error(` or the platform is not supported.`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const binaryPath = path.join(platformDir, 'bin', BIN_NAME);
|
|
118
|
+
const brotliPath = binaryPath + '.br';
|
|
119
|
+
const version = readLocalVersion();
|
|
120
|
+
|
|
121
|
+
if (version) {
|
|
122
|
+
const bootstrapped = bootstrapCanonical(brotliPath, binaryPath, version);
|
|
123
|
+
if (bootstrapped) return bootstrapped;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!fs.existsSync(binaryPath) && !writeVendorBinary(brotliPath, binaryPath, binaryPath)) {
|
|
127
|
+
console.error(`${pkgName}: missing binary at ${binaryPath}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
return binaryPath;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const execPath = resolveBinary();
|
|
134
|
+
const childEnv = { ...process.env, FUIGO_MANAGED_BY_NPM: '1' };
|
|
135
|
+
const child = spawn(execPath, process.argv.slice(2), { stdio: 'inherit', env: childEnv });
|
|
136
|
+
child.on('exit', (code, signal) => {
|
|
137
|
+
if (signal) {
|
|
138
|
+
process.kill(process.pid, signal);
|
|
139
|
+
} else {
|
|
140
|
+
process.exit(code ?? 0);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Runs once after npm install/update. Reads the fuigo binary from the
|
|
3
|
+
// matching per-platform optional dependency (fuigo-<platform>)
|
|
4
|
+
// and installs it to ~/.fuigo/bin/ using versioned filenames:
|
|
5
|
+
//
|
|
6
|
+
// Unix: fuigo-<version> + fuigo (symlink)
|
|
7
|
+
// Windows: fuigo-<version>.exe + fuigo.exe (copy)
|
|
8
|
+
//
|
|
9
|
+
// Versioned files ensure running processes are never disrupted on macOS
|
|
10
|
+
// (replacing a binary that a running process has mmap'd causes SIGKILL
|
|
11
|
+
// because the kernel can no longer verify the code signature).
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const zlib = require('zlib');
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
const TOML = require('@iarna/toml');
|
|
18
|
+
|
|
19
|
+
// $FUIGO_HOME (else ~/.fuigo), matching the Rust fuigo_home(): a symlinked
|
|
20
|
+
// $HOME resolves the same way. Lets fleets relocate the binary off a slow $HOME
|
|
21
|
+
// (NFS); old code hardcoded os.homedir().
|
|
22
|
+
function defaultFuigoHome() {
|
|
23
|
+
const home = os.homedir();
|
|
24
|
+
try { return path.join(fs.realpathSync(home), '.fuigo'); } catch { return path.join(home, '.fuigo'); }
|
|
25
|
+
}
|
|
26
|
+
const FUIGO_HOME = process.env.FUIGO_HOME ?? defaultFuigoHome();
|
|
27
|
+
const CANONICAL_DIR = path.join(FUIGO_HOME, 'bin');
|
|
28
|
+
|
|
29
|
+
const key = `${process.platform}-${process.arch}`;
|
|
30
|
+
const SUPPORTED = new Set([
|
|
31
|
+
'darwin-arm64',
|
|
32
|
+
'darwin-x64',
|
|
33
|
+
'linux-x64',
|
|
34
|
+
'linux-arm64',
|
|
35
|
+
'win32-x64',
|
|
36
|
+
'win32-arm64',
|
|
37
|
+
]);
|
|
38
|
+
if (!SUPPORTED.has(key)) {
|
|
39
|
+
console.error(`fuigo: unsupported platform ${key}`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Resolve the per-platform sibling package's directory. The matching
|
|
44
|
+
// optionalDependency is installed by npm based on `os`/`cpu` filters; the
|
|
45
|
+
// other five are silently skipped. If the matching one is missing, npm was
|
|
46
|
+
// likely invoked with --no-optional or the platform is unsupported.
|
|
47
|
+
function resolvePlatformPackageDir() {
|
|
48
|
+
const platformPkg = `fuigo-${key}`;
|
|
49
|
+
try {
|
|
50
|
+
return path.dirname(require.resolve(`${platformPkg}/package.json`));
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let version;
|
|
57
|
+
try { version = require('../package.json').version; } catch {}
|
|
58
|
+
if (!version) {
|
|
59
|
+
console.error('fuigo: unable to determine version');
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
64
|
+
const EXE = IS_WINDOWS ? '.exe' : '';
|
|
65
|
+
|
|
66
|
+
fs.mkdirSync(CANONICAL_DIR, { recursive: true });
|
|
67
|
+
|
|
68
|
+
function writeVendorBinary(brotliPath, binaryPath, destPath) {
|
|
69
|
+
const tmp = destPath + `.tmp.${process.pid}`;
|
|
70
|
+
try {
|
|
71
|
+
if (fs.existsSync(brotliPath)) {
|
|
72
|
+
fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brotliPath)));
|
|
73
|
+
} else if (fs.existsSync(binaryPath)) {
|
|
74
|
+
fs.copyFileSync(binaryPath, tmp);
|
|
75
|
+
} else {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (!IS_WINDOWS) fs.chmodSync(tmp, 0o755);
|
|
79
|
+
fs.renameSync(tmp, destPath);
|
|
80
|
+
return true;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
} finally {
|
|
84
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function installBinary(binName, sourceDir, vendorSubpath) {
|
|
89
|
+
const brotliPath = path.join(sourceDir, 'bin', vendorSubpath + '.br');
|
|
90
|
+
const binaryPath = path.join(sourceDir, 'bin', vendorSubpath);
|
|
91
|
+
|
|
92
|
+
const versionedName = `${binName}-${version}${EXE}`;
|
|
93
|
+
const versionedPath = path.join(CANONICAL_DIR, versionedName);
|
|
94
|
+
const canonicalName = `${binName}${EXE}`;
|
|
95
|
+
const canonicalPath = path.join(CANONICAL_DIR, canonicalName);
|
|
96
|
+
|
|
97
|
+
// Skip if this exact version is already installed.
|
|
98
|
+
if (!fs.existsSync(versionedPath) && !writeVendorBinary(brotliPath, binaryPath, versionedPath)) {
|
|
99
|
+
console.error(`fuigo: missing binary at ${brotliPath}`);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (IS_WINDOWS) {
|
|
104
|
+
// Symlinks need elevation on Windows; copy instead. If the exe is
|
|
105
|
+
// locked by a running process, rename it aside then retry.
|
|
106
|
+
const oldPath = canonicalPath + '.old';
|
|
107
|
+
try { fs.unlinkSync(oldPath); } catch {} // stale backup from prior update
|
|
108
|
+
try {
|
|
109
|
+
try { fs.unlinkSync(canonicalPath); } catch {}
|
|
110
|
+
fs.copyFileSync(versionedPath, canonicalPath);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
try {
|
|
113
|
+
fs.renameSync(canonicalPath, oldPath);
|
|
114
|
+
try {
|
|
115
|
+
fs.copyFileSync(versionedPath, canonicalPath);
|
|
116
|
+
} catch (copyErr) {
|
|
117
|
+
// Rollback: restore the old binary so the install isn't broken.
|
|
118
|
+
try { fs.renameSync(oldPath, canonicalPath); } catch {}
|
|
119
|
+
throw copyErr;
|
|
120
|
+
}
|
|
121
|
+
} catch (e2) {
|
|
122
|
+
console.error(`fuigo: failed to update ${canonicalPath}: ${e2.message}`);
|
|
123
|
+
console.error('Close all running fuigo processes and try again.');
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
// Atomic symlink swap.
|
|
129
|
+
const tmpLink = canonicalPath + `.link.${process.pid}`;
|
|
130
|
+
try { fs.unlinkSync(tmpLink); } catch {}
|
|
131
|
+
fs.symlinkSync(versionedName, tmpLink);
|
|
132
|
+
fs.renameSync(tmpLink, canonicalPath);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Don't report a broken wire-up as success.
|
|
136
|
+
if (!fs.existsSync(canonicalPath)) {
|
|
137
|
+
console.error(`fuigo: ${canonicalName} did not resolve after install`);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(`${binName} ${version} installed to ${canonicalPath} -> ${versionedName}`);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Comparator: sort "<prefix>X.Y.Z" filenames by version, newest first.
|
|
146
|
+
function byVersionDescending(prefix) {
|
|
147
|
+
return (a, b) => {
|
|
148
|
+
const pa = a.slice(prefix.length).split('.').map(Number);
|
|
149
|
+
const pb = b.slice(prefix.length).split('.').map(Number);
|
|
150
|
+
for (let i = 0; i < 3; i++) {
|
|
151
|
+
if ((pa[i] || 0) !== (pb[i] || 0)) return (pb[i] || 0) - (pa[i] || 0);
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Best-effort cleanup of old versioned binaries for a given binary name.
|
|
158
|
+
// Keeps the current version and the previous one (in case a process is still
|
|
159
|
+
// running the old binary and hasn't fully loaded all pages yet).
|
|
160
|
+
// Uses an exact prefix match + hyphen + digit to avoid fuigo-* matching fuigo-pager-*.
|
|
161
|
+
function cleanupOldVersions(binName) {
|
|
162
|
+
try {
|
|
163
|
+
const prefix = `${binName}-`;
|
|
164
|
+
const currentVersioned = `${binName}-${version}${EXE}`;
|
|
165
|
+
const entries = fs.readdirSync(CANONICAL_DIR);
|
|
166
|
+
const versionedBinaries = entries
|
|
167
|
+
.filter(e => {
|
|
168
|
+
if (!e.startsWith(prefix)) return false;
|
|
169
|
+
if (e.includes('.tmp.') || e.includes('.link.')) return false;
|
|
170
|
+
if (e === currentVersioned) return false;
|
|
171
|
+
const suffix = e.slice(prefix.length);
|
|
172
|
+
return /^\d/.test(suffix);
|
|
173
|
+
})
|
|
174
|
+
.sort(byVersionDescending(prefix));
|
|
175
|
+
for (const old of versionedBinaries.slice(1)) {
|
|
176
|
+
try { fs.unlinkSync(path.join(CANONICAL_DIR, old)); } catch {}
|
|
177
|
+
}
|
|
178
|
+
} catch {}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const platformDir = resolvePlatformPackageDir();
|
|
182
|
+
if (!platformDir) {
|
|
183
|
+
console.error(`fuigo: platform package fuigo-${key} not installed.`);
|
|
184
|
+
console.error(' This usually means npm was invoked with --no-optional, or the install failed.');
|
|
185
|
+
console.error(' Try: npm install -g fuigo');
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Point the bin entry at a binary extracted beside it: launches become one
|
|
190
|
+
// process, and the link can only dangle if the package itself is broken.
|
|
191
|
+
// Windows keeps the node launcher; npm generates its command shims from it.
|
|
192
|
+
function installBinLink(platformDir) {
|
|
193
|
+
if (IS_WINDOWS) return;
|
|
194
|
+
// Other package managers wrap the entry's `#!` line in their own launchers.
|
|
195
|
+
if (!(process.env.npm_config_user_agent ?? '').startsWith('npm/')) return;
|
|
196
|
+
const brotliPath = path.join(platformDir, 'bin', `fuigo${EXE}.br`);
|
|
197
|
+
const binaryPath = path.join(platformDir, 'bin', `fuigo${EXE}`);
|
|
198
|
+
const nativePath = path.join(__dirname, 'fuigo-native');
|
|
199
|
+
const entryPath = path.join(__dirname, 'fuigo');
|
|
200
|
+
const tmp = entryPath + `.link.${process.pid}`;
|
|
201
|
+
try {
|
|
202
|
+
if (!writeVendorBinary(brotliPath, binaryPath, nativePath)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
206
|
+
fs.symlinkSync('./fuigo-native', tmp);
|
|
207
|
+
fs.renameSync(tmp, entryPath);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
// Losing the link only costs latency; the node launcher still works.
|
|
210
|
+
console.error(`fuigo: bin link not installed: ${e.message}`);
|
|
211
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (installBinary('fuigo', platformDir, `fuigo${EXE}`)) {
|
|
216
|
+
installBinLink(platformDir);
|
|
217
|
+
}
|
|
218
|
+
cleanupOldVersions('fuigo');
|
|
219
|
+
cleanupOldVersions('fuigo-pager');
|
|
220
|
+
|
|
221
|
+
// Write installer config
|
|
222
|
+
const configDir = FUIGO_HOME;
|
|
223
|
+
const configPath = path.join(configDir, 'config.toml');
|
|
224
|
+
let obj = {};
|
|
225
|
+
try { obj = TOML.parse(fs.readFileSync(configPath, 'utf8')); } catch { }
|
|
226
|
+
obj.cli ??= {};
|
|
227
|
+
obj.cli.installer = 'npm';
|
|
228
|
+
|
|
229
|
+
// Persist the npm registry so `fuigo update` and the launcher use the same one.
|
|
230
|
+
const npmRegistry = process.env.FUIGO_NPM_REGISTRY
|
|
231
|
+
|| (() => {
|
|
232
|
+
try {
|
|
233
|
+
const resolved = execSync(
|
|
234
|
+
'npm config get registry',
|
|
235
|
+
{ encoding: 'utf8', timeout: 5000 }
|
|
236
|
+
).trim();
|
|
237
|
+
if (resolved && resolved !== 'undefined') return resolved;
|
|
238
|
+
} catch {}
|
|
239
|
+
return null;
|
|
240
|
+
})();
|
|
241
|
+
|
|
242
|
+
if (npmRegistry) {
|
|
243
|
+
obj.cli.npm_registry = npmRegistry;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
fs.writeFileSync(configPath, TOML.stringify(obj), 'utf8');
|
|
247
|
+
|
|
248
|
+
// Shell completions: print setup hints (no silent shell config mutation).
|
|
249
|
+
// Set FUIGO_INSTALL_COMPLETIONS=1 to auto-generate to ~/.fuigo/completions.
|
|
250
|
+
const FUIGO_PATH = path.join(CANONICAL_DIR, `fuigo${EXE}`);
|
|
251
|
+
if (process.env.FUIGO_INSTALL_COMPLETIONS === '1' && !IS_WINDOWS) {
|
|
252
|
+
try {
|
|
253
|
+
const { spawnSync } = require('child_process');
|
|
254
|
+
const completionsDir = path.join(FUIGO_HOME, 'completions');
|
|
255
|
+
const bashPath = path.join(completionsDir, 'bash', 'fuigo.bash');
|
|
256
|
+
const zshPath = path.join(completionsDir, 'zsh', '_fuigo');
|
|
257
|
+
fs.mkdirSync(path.dirname(bashPath), { recursive: true });
|
|
258
|
+
fs.mkdirSync(path.dirname(zshPath), { recursive: true });
|
|
259
|
+
const bashRes = spawnSync(FUIGO_PATH, ['completions', 'bash'], { encoding: 'utf8' });
|
|
260
|
+
if (bashRes.status === 0) fs.writeFileSync(bashPath, bashRes.stdout);
|
|
261
|
+
const zshRes = spawnSync(FUIGO_PATH, ['completions', 'zsh'], { encoding: 'utf8' });
|
|
262
|
+
if (zshRes.status === 0) fs.writeFileSync(zshPath, zshRes.stdout);
|
|
263
|
+
console.log('Completions generated to ~/.fuigo/completions (bash/zsh)');
|
|
264
|
+
} catch {}
|
|
265
|
+
} else if (!IS_WINDOWS) {
|
|
266
|
+
console.log('Tip: fuigo completions bash > ~/.local/share/bash-completion/completions/fuigo');
|
|
267
|
+
console.log(' fuigo completions zsh > ~/.zsh/completions/_fuigo');
|
|
268
|
+
}
|
package/package.json
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
"name": "fuigo",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Bring Fuigo into your terminal",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fuigo": "bin/fuigo"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/"
|
|
11
|
+
],
|
|
12
|
+
"os": [
|
|
13
|
+
"darwin",
|
|
14
|
+
"linux",
|
|
15
|
+
"win32"
|
|
16
|
+
],
|
|
17
|
+
"cpu": [
|
|
18
|
+
"arm64",
|
|
19
|
+
"x64"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"postinstall": "node bin/postinstall.js",
|
|
23
|
+
"sync-version": "node scripts/sync-version.js",
|
|
24
|
+
"check-version": "node scripts/sync-version.js --check",
|
|
25
|
+
"assemble": "node scripts/assemble-platform-packages.js",
|
|
26
|
+
"prepublishOnly": "node scripts/sync-version.js --check"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@iarna/toml": "^3.0.0"
|
|
36
|
+
},
|
|
37
|
+
"optionalDependencies": {
|
|
38
|
+
"fuigo-darwin-arm64": "1.0.2",
|
|
39
|
+
"fuigo-darwin-x64": "1.0.2",
|
|
40
|
+
"fuigo-linux-arm64": "1.0.2",
|
|
41
|
+
"fuigo-linux-x64": "1.0.2",
|
|
42
|
+
"fuigo-win32-arm64": "1.0.2",
|
|
43
|
+
"fuigo-win32-x64": "1.0.2"
|
|
44
|
+
}
|
|
21
45
|
}
|
package/index.js
DELETED