@awiki/cli 0.0.1-beta.2 → 0.0.1-beta.4

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.
Files changed (2) hide show
  1. package/package.json +4 -3
  2. package/scripts/install.js +82 -36
package/package.json CHANGED
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "@awiki/cli",
3
- "version": "0.0.1-beta.2",
3
+ "version": "0.0.1-beta.4",
4
4
  "description": "Awiki command-line interface (awiki-cli)",
5
5
  "engines": {
6
6
  "node": ">=18"
7
7
  },
8
8
  "awikiCli": {
9
- "minSupportedVersion": "0.0.1-beta.2"
9
+ "minSupportedVersion": "0.0.1-beta.4"
10
10
  },
11
11
  "bin": {
12
12
  "awiki-cli": "scripts/run.js"
13
13
  },
14
14
  "scripts": {
15
- "install-binary": "node scripts/install.js"
15
+ "install-binary": "node scripts/install.js",
16
+ "postinstall": "node scripts/install.js"
16
17
  }
17
18
  }
@@ -4,7 +4,6 @@
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const os = require('os');
7
- const https = require('https');
8
7
  const { spawn } = require('child_process');
9
8
 
10
9
  function mapPlatform() {
@@ -36,53 +35,73 @@ function getDownloadUrl(version, osName, arch) {
36
35
  const fileName = `${archiveBaseName}.${ext}`;
37
36
 
38
37
  const mirror = (process.env.AWIKI_CLI_DOWNLOAD_MIRROR || '').trim();
39
- const base = mirror || 'https://github.com/AgentConnect/awiki-cli/releases/download';
40
- const baseNoSlash = base.replace(/\/+$/, '');
38
+ const mirrorBase = mirror ? mirror.replace(/\/+$/, '') : '';
39
+ const githubBase = 'https://github.com/AgentConnect/awiki-cli/releases/download'.replace(/\/+$/, '');
41
40
  const tag = `v${version}`;
42
41
 
42
+ const urls = [];
43
+ // If a mirror is configured, try it first.
44
+ if (mirrorBase) {
45
+ urls.push(`${mirrorBase}/${tag}/${fileName}`);
46
+ }
47
+ // Always fall back to GitHub.
48
+ urls.push(`${githubBase}/${tag}/${fileName}`);
49
+
43
50
  return {
44
- url: `${baseNoSlash}/${tag}/${fileName}`,
51
+ urls,
45
52
  fileName,
46
53
  };
47
54
  }
48
55
 
49
56
  function download(url, destPath) {
50
57
  return new Promise((resolve, reject) => {
51
- const file = fs.createWriteStream(destPath);
52
- let finished = false;
53
-
54
- const req = https.get(url, res => {
55
- if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
56
- // handle redirect
57
- res.destroy();
58
- file.close(() => fs.unlink(destPath, () => {
59
- download(res.headers.location, destPath).then(resolve, reject);
60
- }));
61
- return;
62
- }
63
-
64
- if (res.statusCode !== 200) {
65
- res.resume();
66
- file.close(() => fs.unlink(destPath, () => {
67
- reject(new Error(`Download failed with status code ${res.statusCode}`));
68
- }));
69
- return;
70
- }
58
+ const curlCmd = process.env.AWIKI_CLI_CURL || 'curl';
59
+ const isWindows = process.platform === 'win32';
60
+ const args = [];
61
+
62
+ if (isWindows) {
63
+ // On Windows, avoid CRYPT_E_REVOCATION_OFFLINE errors when the
64
+ // certificate revocation list server is unreachable.
65
+ args.push('--ssl-revoke-best-effort');
66
+ }
71
67
 
72
- res.pipe(file);
73
- file.on('finish', () => {
74
- finished = true;
75
- file.close(resolve);
76
- });
68
+ args.push(
69
+ '--fail',
70
+ '--location',
71
+ '--silent',
72
+ '--show-error',
73
+ '--connect-timeout',
74
+ '10',
75
+ '--max-time',
76
+ '60',
77
+ '--output',
78
+ destPath,
79
+ url
80
+ );
81
+
82
+ const child = spawn(curlCmd, args, { stdio: ['ignore', 'ignore', 'pipe'] });
83
+ let stderr = '';
84
+
85
+ child.stderr.on('data', chunk => {
86
+ stderr += chunk.toString();
77
87
  });
78
88
 
79
- req.on('error', err => {
80
- if (!finished) {
81
- file.close(() => fs.unlink(destPath, () => reject(err)));
89
+ child.on('error', err => {
90
+ if (err && err.code === 'ENOENT') {
91
+ reject(new Error('curl not found. Please install curl or set AWIKI_CLI_CURL to a valid curl binary.'));
82
92
  } else {
83
93
  reject(err);
84
94
  }
85
95
  });
96
+
97
+ child.on('exit', code => {
98
+ if (code === 0) {
99
+ resolve();
100
+ } else {
101
+ const msg = stderr.trim() || `curl exited with code ${code}`;
102
+ reject(new Error(msg));
103
+ }
104
+ });
86
105
  });
87
106
  }
88
107
 
@@ -126,7 +145,7 @@ async function main() {
126
145
  const version = getVersion(pkg);
127
146
  const osName = mapPlatform();
128
147
  const arch = mapArch();
129
- const { url, fileName } = getDownloadUrl(version, osName, arch);
148
+ const { urls, fileName } = getDownloadUrl(version, osName, arch);
130
149
 
131
150
  const binDir = path.join(rootDir, 'bin');
132
151
  ensureDir(binDir);
@@ -134,11 +153,33 @@ async function main() {
134
153
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'awiki-cli-'));
135
154
  const archivePath = path.join(tmpDir, fileName);
136
155
 
137
- console.log(`Downloading awiki-cli ${version} for ${osName}/${arch} from ${url} ...`);
138
- await download(url, archivePath);
156
+ let lastError;
157
+ for (const url of urls) {
158
+ console.log(`Downloading awiki-cli ${version} for ${osName}/${arch} from ${url} ...`);
159
+ try {
160
+ await download(url, archivePath);
161
+ lastError = undefined;
162
+ break;
163
+ } catch (err) {
164
+ lastError = err;
165
+ console.error(`[awiki-cli] Download failed from ${url}: ${err.message}`);
166
+ }
167
+ }
168
+
169
+ if (lastError) {
170
+ throw lastError;
171
+ }
139
172
 
140
173
  console.log(`Extracting to ${binDir} ...`);
141
- await extractArchive(archivePath, binDir, osName);
174
+ try {
175
+ await extractArchive(archivePath, binDir, osName);
176
+ } finally {
177
+ try {
178
+ fs.rmSync(tmpDir, { recursive: true, force: true });
179
+ } catch (e) {
180
+ // best effort cleanup
181
+ }
182
+ }
142
183
 
143
184
  const exeName = osName === 'windows' ? 'awiki-cli.exe' : 'awiki-cli';
144
185
  const exePath = path.join(binDir, exeName);
@@ -157,6 +198,11 @@ async function main() {
157
198
  if (require.main === module) {
158
199
  main().catch(err => {
159
200
  console.error(`[awiki-cli] Failed to install binary: ${err.message}`);
201
+ console.error(
202
+ '\nIf you are behind a firewall or using a restricted network, you can:\n' +
203
+ ' - Set AWIKI_CLI_DOWNLOAD_MIRROR to a reachable HTTPS base URL,\n' +
204
+ ' - Or manually download the archive and extract it into the awiki-cli bin directory.\n'
205
+ );
160
206
  process.exit(1);
161
207
  });
162
208
  }