umadev 1.0.8 → 1.0.9

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/bin/cli.js +49 -19
  2. package/package.json +7 -7
package/bin/cli.js CHANGED
@@ -128,6 +128,26 @@ function modelPresent(dir) {
128
128
  }
129
129
  });
130
130
  }
131
+ // Render one frame of the download progress bar (in place, via \r). Block-glyph
132
+ // bar + percent + downloaded/total + live speed; ANSI-colored only on a TTY so a
133
+ // piped/redirected install stays clean.
134
+ function drawBar(label, got, total, startTime) {
135
+ const tty = process.stderr.isTTY;
136
+ const c = (code) => (tty ? '\x1b[' + code + 'm' : '');
137
+ const w = 22;
138
+ const ratio = total > 0 ? Math.min(1, got / total) : 0;
139
+ const fill = Math.round(ratio * w);
140
+ const bar = c('38;5;45') + '█'.repeat(fill) + c('0') + c('38;5;238') + '░'.repeat(w - fill) + c('0');
141
+ const pct = String(Math.floor(ratio * 100)).padStart(3);
142
+ const mb = (got / 1048576).toFixed(1);
143
+ const tot = (total / 1048576).toFixed(0);
144
+ const sec = (Date.now() - startTime) / 1000;
145
+ const spd = sec > 0.3 ? (got / 1048576 / sec).toFixed(1) + ' MB/s' : '…';
146
+ process.stderr.write(
147
+ '\r ' + c('1') + label + c('0') + ' ' + bar + ' ' + c('1') + pct + '%' + c('0') +
148
+ c('2') + ' · ' + mb + '/' + tot + ' MB · ' + spd + c('0') + ' ',
149
+ );
150
+ }
131
151
  // Download one URL to `dest`, following redirects (GitHub → CDN), drawing a
132
152
  // progress bar when `withBar`. Resolves on success, rejects on any error.
133
153
  function downloadTo(url, dest, withBar, label) {
@@ -149,22 +169,24 @@ function downloadTo(url, dest, withBar, label) {
149
169
  const total = parseInt(res.headers['content-length'] || '0', 10);
150
170
  let got = 0;
151
171
  let lastPct = -1;
172
+ let lastDraw = 0;
173
+ const startTime = Date.now();
152
174
  const tmp = dest + '.part';
153
175
  const out = fs.createWriteStream(tmp);
176
+ // Draw the bar at 0% the instant the response starts — on a slow link the
177
+ // first 1% can take a while, and a silent gap reads as "stuck / failed".
178
+ if (withBar && total > 0) drawBar(label, 0, total, startTime);
154
179
  res.on('data', (chunk) => {
155
180
  got += chunk.length;
156
181
  if (withBar && total > 0) {
182
+ const now = Date.now();
157
183
  const pct = Math.floor((got / total) * 100);
158
- if (pct !== lastPct) {
184
+ // Redraw on each new percent OR every ~250ms — keeps the live speed
185
+ // ticking even while a single percent of a 224MB file streams in.
186
+ if (pct !== lastPct || now - lastDraw > 250) {
159
187
  lastPct = pct;
160
- const w = 24;
161
- const fill = Math.round((pct / 100) * w);
162
- const bar = '#'.repeat(fill) + '-'.repeat(w - fill);
163
- const mb = (got / 1048576).toFixed(0);
164
- const tot = (total / 1048576).toFixed(0);
165
- process.stderr.write(
166
- '\r ' + label + ' [' + bar + '] ' + pct + '% (' + mb + '/' + tot + ' MB)',
167
- );
188
+ lastDraw = now;
189
+ drawBar(label, got, total, startTime);
168
190
  }
169
191
  }
170
192
  });
@@ -177,7 +199,10 @@ function downloadTo(url, dest, withBar, label) {
177
199
  } catch (er) {
178
200
  return reject(er);
179
201
  }
180
- if (withBar) process.stderr.write('\n');
202
+ if (withBar && total > 0) {
203
+ drawBar(label, total, total, startTime);
204
+ process.stderr.write('\n');
205
+ }
181
206
  resolve();
182
207
  }),
183
208
  );
@@ -197,12 +222,15 @@ function releaseBases(version) {
197
222
  if (process.env.UMADEV_MODEL_BASE_URL) {
198
223
  return [process.env.UMADEV_MODEL_BASE_URL.replace(/\/+$/, '')];
199
224
  }
225
+ // GitHub Release ships the quantized fp16 model (~224MB, smaller). HuggingFace
226
+ // and its China mirror hf-mirror.com serve the upstream f32 model (~448MB —
227
+ // bigger, but the candle loader handles either). hf-mirror is the FAST + reliable
228
+ // source inside mainland China, where github.com's release CDN is slow and the
229
+ // community GitHub proxies are flaky for release-asset URLs.
200
230
  const gh = 'https://github.com/umacloud/umadev/releases/download/v' + version;
201
- const mirrors = [
202
- 'https://ghproxy.net/' + gh,
203
- 'https://ghfast.top/' + gh,
204
- 'https://gh-proxy.com/' + gh,
205
- ];
231
+ const ghProxies = ['https://ghproxy.net/' + gh, 'https://ghfast.top/' + gh];
232
+ const hf = 'https://huggingface.co/intfloat/multilingual-e5-small/resolve/main';
233
+ const hfMirror = 'https://hf-mirror.com/intfloat/multilingual-e5-small/resolve/main';
206
234
  let cn = false;
207
235
  try {
208
236
  const opts = Intl.DateTimeFormat().resolvedOptions();
@@ -212,9 +240,11 @@ function releaseBases(version) {
212
240
  /Shanghai|Chongqing|Urumqi|Harbin|Hong_Kong|Macau/.test(tz) ||
213
241
  /zh[_-]?(CN|Hans)/i.test(loc);
214
242
  } catch (_) {
215
- /* default to direct-first */
243
+ /* default to international order */
216
244
  }
217
- return cn ? [...mirrors, gh] : [gh, ...mirrors];
245
+ // China: hf-mirror first (fast + reliable in CN), then GitHub proxies + direct.
246
+ // International: GitHub Release first (smaller fp16), then HuggingFace + mirror.
247
+ return cn ? [hfMirror, ...ghProxies, gh, hf] : [gh, hf, hfMirror, ...ghProxies];
218
248
  }
219
249
  // Try each base for `name` in order; resolve on first success, throw the last
220
250
  // error if all fail. A China mirror can cover a blocked github.com (or vice
@@ -244,13 +274,13 @@ async function ensureModel() {
244
274
  try {
245
275
  fs.mkdirSync(dir, { recursive: true });
246
276
  process.stderr.write(
247
- '\n 本地向量检索模型缺失,正在下载 (multilingual-e5-small · fp16 · ~224MB)…\n',
277
+ '\n 本地向量检索模型缺失,正在下载 multilingual-e5-small(国内自动走镜像)…\n',
248
278
  );
249
279
  process.stderr.write(
250
280
  ' 一次性下载;之后完全本地、运行时无需联网。失败不影响使用(降级为 BM25)。\n',
251
281
  );
252
282
  await downloadFile(bases, 'config.json', path.join(dir, 'config.json'), false, '');
253
- await downloadFile(bases, 'tokenizer.json', path.join(dir, 'tokenizer.json'), false, '');
283
+ await downloadFile(bases, 'tokenizer.json', path.join(dir, 'tokenizer.json'), true, '下载分词器 ');
254
284
  await downloadFile(
255
285
  bases,
256
286
  'model.safetensors',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umadev",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "A project-director Agent for AI coding hosts — drives your logged-in Claude Code / Codex through a 9-phase commercial delivery pipeline with governance. No API key needed.",
5
5
  "keywords": [
6
6
  "ai",
@@ -37,11 +37,11 @@
37
37
  "node": ">=18"
38
38
  },
39
39
  "optionalDependencies": {
40
- "@umacloud/cli-darwin-arm64": "1.0.8",
41
- "@umacloud/cli-darwin-x64": "1.0.8",
42
- "@umacloud/cli-linux-x64": "1.0.8",
43
- "@umacloud/cli-linux-arm64": "1.0.8",
44
- "@umacloud/cli-win32-x64": "1.0.8",
45
- "@umacloud/knowledge": "1.0.8"
40
+ "@umacloud/cli-darwin-arm64": "1.0.9",
41
+ "@umacloud/cli-darwin-x64": "1.0.9",
42
+ "@umacloud/cli-linux-x64": "1.0.9",
43
+ "@umacloud/cli-linux-arm64": "1.0.9",
44
+ "@umacloud/cli-win32-x64": "1.0.9",
45
+ "@umacloud/knowledge": "1.0.9"
46
46
  }
47
47
  }