@gw-tools/gw 0.12.19 → 0.12.22
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/install.js +91 -34
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
* Downloads the appropriate binary for the current platform
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
chmodSync,
|
|
12
|
+
openSync,
|
|
13
|
+
fsyncSync,
|
|
14
|
+
closeSync,
|
|
15
|
+
} = require('fs');
|
|
9
16
|
const { join } = require('path');
|
|
10
17
|
const { arch, platform } = require('os');
|
|
11
18
|
const https = require('https');
|
|
@@ -39,7 +46,7 @@ function getBinaryName() {
|
|
|
39
46
|
if (!os || !cpu) {
|
|
40
47
|
console.error(
|
|
41
48
|
`Unsupported platform: ${platform()}-${arch()}\n` +
|
|
42
|
-
'Supported platforms: macOS (x64, arm64), Linux (x64, arm64), Windows (x64, arm64)'
|
|
49
|
+
'Supported platforms: macOS (x64, arm64), Linux (x64, arm64), Windows (x64, arm64)',
|
|
43
50
|
);
|
|
44
51
|
process.exit(1);
|
|
45
52
|
}
|
|
@@ -55,26 +62,43 @@ function download(url, dest) {
|
|
|
55
62
|
return new Promise((resolve, reject) => {
|
|
56
63
|
const file = createWriteStream(dest);
|
|
57
64
|
|
|
58
|
-
https
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
https
|
|
66
|
+
.get(
|
|
67
|
+
url,
|
|
68
|
+
{ headers: { 'User-Agent': 'gw-npm-installer' } },
|
|
69
|
+
(response) => {
|
|
70
|
+
// Handle redirects
|
|
71
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
72
|
+
return download(response.headers.location, dest)
|
|
73
|
+
.then(resolve)
|
|
74
|
+
.catch(reject);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (response.statusCode !== 200) {
|
|
78
|
+
reject(
|
|
79
|
+
new Error(
|
|
80
|
+
`Failed to download: ${response.statusCode} ${response.statusMessage}`,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
response.pipe(file);
|
|
87
|
+
|
|
88
|
+
file.on('finish', () => {
|
|
89
|
+
file.close((err) => {
|
|
90
|
+
if (err) {
|
|
91
|
+
reject(err);
|
|
92
|
+
} else {
|
|
93
|
+
resolve();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
.on('error', (err) => {
|
|
100
|
+
reject(err);
|
|
74
101
|
});
|
|
75
|
-
}).on('error', (err) => {
|
|
76
|
-
reject(err);
|
|
77
|
-
});
|
|
78
102
|
|
|
79
103
|
file.on('error', (err) => {
|
|
80
104
|
reject(err);
|
|
@@ -106,22 +130,34 @@ async function install() {
|
|
|
106
130
|
console.log(`Downloading from: ${downloadUrl}`);
|
|
107
131
|
await download(downloadUrl, binaryPath);
|
|
108
132
|
|
|
109
|
-
//
|
|
133
|
+
// Ensure file is fully written to disk before chmod
|
|
134
|
+
// This prevents ETXTBSY errors on Linux systems
|
|
110
135
|
if (platform() !== 'win32') {
|
|
136
|
+
const fd = openSync(binaryPath, 'r+');
|
|
137
|
+
fsyncSync(fd);
|
|
138
|
+
closeSync(fd);
|
|
139
|
+
|
|
140
|
+
// Small delay after fsync before chmod
|
|
141
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
142
|
+
|
|
111
143
|
chmodSync(binaryPath, 0o755);
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
144
|
+
|
|
145
|
+
// Additional delay after chmod to prevent ETXTBSY
|
|
146
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
115
147
|
}
|
|
116
148
|
|
|
117
149
|
console.log('✓ Installation complete!');
|
|
118
150
|
|
|
119
|
-
//
|
|
151
|
+
// Try to install shell integration, but don't fail if it errors
|
|
120
152
|
console.log('\n⚙️ Setting up shell integration...');
|
|
121
|
-
|
|
153
|
+
try {
|
|
154
|
+
await installShellIntegration(binaryPath);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.log(' Shell integration setup encountered an issue.');
|
|
157
|
+
console.log(' You can install it manually later with: gw install-shell');
|
|
158
|
+
}
|
|
122
159
|
|
|
123
160
|
console.log('\nRun "gw --help" to get started.');
|
|
124
|
-
console.log('Tip: Restart your terminal or run "source ~/.zshrc" (or ~/.bashrc) to use "gw cd"');
|
|
125
161
|
} catch (error) {
|
|
126
162
|
console.error('\n✗ Installation failed:', error.message);
|
|
127
163
|
console.error('\nYou can manually download the binary from:');
|
|
@@ -141,15 +177,34 @@ async function installShellIntegration(binaryPath, retries = 3) {
|
|
|
141
177
|
const { spawn } = require('child_process');
|
|
142
178
|
|
|
143
179
|
return new Promise((resolve) => {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
180
|
+
let child;
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
child = spawn(binaryPath, ['install-shell', '--quiet'], {
|
|
184
|
+
stdio: 'inherit',
|
|
185
|
+
});
|
|
186
|
+
} catch (err) {
|
|
187
|
+
// Catch synchronous spawn errors (e.g., ETXTBSY thrown immediately)
|
|
188
|
+
if (err.code === 'ETXTBSY' && retries > 0) {
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
installShellIntegration(binaryPath, retries - 1).then(resolve);
|
|
191
|
+
}, 200);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
console.log(
|
|
195
|
+
' (Shell integration can be installed later with: gw install-shell)',
|
|
196
|
+
);
|
|
197
|
+
resolve();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
147
200
|
|
|
148
201
|
child.on('close', (code) => {
|
|
149
202
|
if (code === 0) {
|
|
150
203
|
console.log('✓ Shell integration installed!');
|
|
151
204
|
} else {
|
|
152
|
-
console.log(
|
|
205
|
+
console.log(
|
|
206
|
+
' (Shell integration can be installed later with: gw install-shell)',
|
|
207
|
+
);
|
|
153
208
|
}
|
|
154
209
|
resolve();
|
|
155
210
|
});
|
|
@@ -157,10 +212,12 @@ async function installShellIntegration(binaryPath, retries = 3) {
|
|
|
157
212
|
child.on('error', async (err) => {
|
|
158
213
|
// Retry on ETXTBSY (text file busy) error
|
|
159
214
|
if (err.code === 'ETXTBSY' && retries > 0) {
|
|
160
|
-
await new Promise(r => setTimeout(r, 200));
|
|
215
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
161
216
|
return installShellIntegration(binaryPath, retries - 1).then(resolve);
|
|
162
217
|
}
|
|
163
|
-
console.log(
|
|
218
|
+
console.log(
|
|
219
|
+
' (Shell integration can be installed later with: gw install-shell)',
|
|
220
|
+
);
|
|
164
221
|
resolve();
|
|
165
222
|
});
|
|
166
223
|
});
|