@yixinkj/cli 1.0.15-beta.10 → 1.0.15-beta.11

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 (3) hide show
  1. package/README.md +16 -4
  2. package/index.js +44 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -29,7 +29,7 @@ yixin --ads --period last-week
29
29
  yixin --coffee-prospector --mode fetch --name "Roaster" --website "https://example.com" --country 美国
30
30
  ```
31
31
 
32
- 首次运行会从公开 GitHub Release 下载二进制到 `~/.yixin/bin`。
32
+ 首次运行会优先从阿里云 OSS 下载二进制到 `~/.yixin/bin`;OSS 不可用时自动回退到公开 GitHub Release。
33
33
 
34
34
  macOS 下下载完成后会自动处理每个二进制:
35
35
 
@@ -53,8 +53,8 @@ macOS 下下载完成后会自动处理每个二进制:
53
53
  ```text
54
54
  YIXIN_PHONE 可选。授权手机号;未设置时 CLI 会读取 ~/.yixin/config.json,仍没有则交互式询问。
55
55
  YIXIN_AUTH_URL 可选。授权服务地址,默认已内置。
56
- YIXIN_RELEASE_REPO 可选。默认 yixinkj/cli-releases。
57
- YIXIN_RELEASE_BASE_URL 可选。覆盖二进制下载基础 URL。
56
+ YIXIN_RELEASE_REPO 可选。GitHub 备用源仓库,默认 yixinkj/cli-releases。
57
+ YIXIN_RELEASE_BASE_URL 可选。完全覆盖默认下载源;设置后不再自动回退。
58
58
  YIXIN_GITHUB_TOKEN 可选。下载私有 Release 时使用;正式公开分发通常不需要。
59
59
  TAVILY_API_KEY coffee-prospector fetch 可选。用于官网相关链接搜索。
60
60
  TAVILY_SEARCH_URL coffee-prospector fetch 可选。覆盖 Tavily 兼容搜索接口地址。
@@ -69,7 +69,19 @@ COFFEE_PROSPECTOR_CRM_PROFILE 可选。直接指定发信账号窗口标
69
69
 
70
70
  ## 二进制发布包
71
71
 
72
- 默认从 `yixinkj/cli-releases` 下载与 npm 包版本一致的 release asset:
72
+ 默认优先从杭州 OSS 下载与 npm 包版本一致的 release asset:
73
+
74
+ ```text
75
+ https://yixin-cli-releases-8488540.oss-cn-hangzhou.aliyuncs.com/releases/v{version}/{archive}
76
+ ```
77
+
78
+ OSS 下载失败后自动回退:
79
+
80
+ ```text
81
+ https://github.com/yixinkj/cli-releases/releases/download/v{version}/{archive}
82
+ ```
83
+
84
+ 平台压缩包:
73
85
 
74
86
  - `darwin-arm64` -> `yixin-mac-arm64.tar.gz`
75
87
  - `win32-x64` -> `yixin-win32-x64.zip`
package/index.js CHANGED
@@ -15,6 +15,8 @@ const registry = require('./skills.json');
15
15
 
16
16
  const VERSION = packageJson.version;
17
17
  const RELEASE_REPO = process.env.YIXIN_RELEASE_REPO || 'yixinkj/cli-releases';
18
+ const OSS_RELEASE_BASE_URL =
19
+ 'https://yixin-cli-releases-8488540.oss-cn-hangzhou.aliyuncs.com/releases';
18
20
  const CACHE_ROOT = path.join(os.homedir(), '.yixin');
19
21
  const BIN_DIR = path.join(CACHE_ROOT, 'bin');
20
22
  const VERSION_FILE = path.join(CACHE_ROOT, 'version');
@@ -81,11 +83,14 @@ function isInstalled(target) {
81
83
  return target.binaries.every((name) => fs.existsSync(path.join(BIN_DIR, name)));
82
84
  }
83
85
 
84
- function releaseUrl(target) {
86
+ function releaseUrls(target) {
85
87
  if (process.env.YIXIN_RELEASE_BASE_URL) {
86
- return `${process.env.YIXIN_RELEASE_BASE_URL.replace(/\/$/, '')}/${target.archive}`;
88
+ return [`${process.env.YIXIN_RELEASE_BASE_URL.replace(/\/$/, '')}/${target.archive}`];
87
89
  }
88
- return `https://github.com/${RELEASE_REPO}/releases/download/v${VERSION}/${target.archive}`;
90
+ return [
91
+ `${OSS_RELEASE_BASE_URL}/v${VERSION}/${target.archive}`,
92
+ `https://github.com/${RELEASE_REPO}/releases/download/v${VERSION}/${target.archive}`
93
+ ];
89
94
  }
90
95
 
91
96
  function downloadFile(url, outputPath, redirectsLeft = 5) {
@@ -100,7 +105,7 @@ function downloadFile(url, outputPath, redirectsLeft = 5) {
100
105
  headers.Authorization = `Bearer ${process.env.YIXIN_GITHUB_TOKEN}`;
101
106
  }
102
107
 
103
- https.get(url, { headers }, (response) => {
108
+ const request = https.get(url, { headers }, (response) => {
104
109
  if ([301, 302, 303, 307, 308].includes(response.statusCode)) {
105
110
  response.resume();
106
111
  if (redirectsLeft <= 0) {
@@ -125,12 +130,44 @@ function downloadFile(url, outputPath, redirectsLeft = 5) {
125
130
 
126
131
  const file = fs.createWriteStream(outputPath);
127
132
  response.pipe(file);
133
+ response.on('aborted', () => {
134
+ file.destroy();
135
+ reject(new Error(`下载连接意外中断:${url}`));
136
+ });
137
+ response.on('error', (error) => {
138
+ file.destroy();
139
+ reject(error);
140
+ });
128
141
  file.on('finish', () => file.close(resolve));
129
142
  file.on('error', reject);
130
- }).on('error', reject);
143
+ });
144
+ request.setTimeout(30000, () => {
145
+ request.destroy(new Error(`下载超时:${url}`));
146
+ });
147
+ request.on('error', reject);
131
148
  });
132
149
  }
133
150
 
151
+ async function downloadFromSources(urls, outputPath) {
152
+ const failures = [];
153
+ for (let index = 0; index < urls.length; index += 1) {
154
+ const url = urls[index];
155
+ try {
156
+ fs.rmSync(outputPath, { force: true });
157
+ console.error(`[yixin] 正在下载 ${url}`);
158
+ await downloadFile(url, outputPath);
159
+ return;
160
+ } catch (error) {
161
+ fs.rmSync(outputPath, { force: true });
162
+ failures.push(`${url}: ${error.message}`);
163
+ if (index + 1 < urls.length) {
164
+ console.error(`[yixin] 当前下载源失败,正在切换备用源:${error.message}`);
165
+ }
166
+ }
167
+ }
168
+ throw new Error(`所有二进制下载源均失败:\n${failures.join('\n')}`);
169
+ }
170
+
134
171
  function runCommand(command, args) {
135
172
  const result = spawnSync(command, args, {
136
173
  encoding: 'utf8',
@@ -211,12 +248,11 @@ async function ensureInstalled(target) {
211
248
 
212
249
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'yixin-cli-'));
213
250
  const archivePath = path.join(tempDir, target.archive);
214
- const url = releaseUrl(target);
251
+ const urls = releaseUrls(target);
215
252
 
216
253
  try {
217
254
  console.error(`[yixin] 正在安装 ${target.id} 二进制,版本 ${VERSION}`);
218
- console.error(`[yixin] 正在下载 ${url}`);
219
- await downloadFile(url, archivePath);
255
+ await downloadFromSources(urls, archivePath);
220
256
  extractArchive(archivePath, target);
221
257
  prepareInstalledBinaries(target);
222
258
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yixinkj/cli",
3
- "version": "1.0.15-beta.10",
3
+ "version": "1.0.15-beta.11",
4
4
  "description": "Yixin native CLI launcher.",
5
5
  "type": "module",
6
6
  "bin": {