moondesk 0.11.8 → 0.12.0

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.
@@ -103,10 +103,10 @@ function cleanupOldBinaryVersions(options = {}) {
103
103
  return { removed, skipped };
104
104
  }
105
105
 
106
- function reportDownloadProgress(callback, downloadedBytes, totalBytes) {
106
+ function reportDownloadProgress(callback, downloadedBytes, totalBytes, done = false) {
107
107
  if (typeof callback !== "function") return;
108
108
  try {
109
- callback({ downloadedBytes, totalBytes });
109
+ callback({ downloadedBytes, totalBytes, done });
110
110
  } catch {
111
111
  // Download reporting is best-effort and must never make a verified install fail.
112
112
  }
@@ -126,7 +126,7 @@ async function readResponseBuffer(response, url, maxBytes, onProgress) {
126
126
  if (buffer.length > maxBytes) {
127
127
  throw new Error(`${url} exceeded the ${maxBytes}-byte download limit`);
128
128
  }
129
- reportDownloadProgress(onProgress, buffer.length, totalBytes);
129
+ reportDownloadProgress(onProgress, buffer.length, totalBytes, true);
130
130
  return buffer;
131
131
  }
132
132
 
@@ -154,6 +154,7 @@ async function readResponseBuffer(response, url, maxBytes, onProgress) {
154
154
  reader.releaseLock?.();
155
155
  }
156
156
 
157
+ reportDownloadProgress(onProgress, downloadedBytes, totalBytes, true);
157
158
  return Buffer.concat(chunks, downloadedBytes);
158
159
  }
159
160
 
@@ -436,43 +437,59 @@ function formatMiB(bytes) {
436
437
  return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
437
438
  }
438
439
 
439
- function createDownloadProgressReporter(logger = console) {
440
- let started = false;
441
- let lastPercentBucket = 0;
440
+ function formatProgressBar(percent, width = 10) {
441
+ const clampedPercent = Math.max(0, Math.min(100, percent));
442
+ const filled = Math.round((clampedPercent / 100) * width);
443
+ return `[${"█".repeat(filled)}${"░".repeat(width - filled)}]`;
444
+ }
445
+
446
+ function createDownloadProgressReporter(writer = process.stderr) {
447
+ let lastPercentBucket = -10;
442
448
  let lastUnknownBytes = 0;
449
+ let lastLineLength = 0;
450
+ const percentReportStep = 10;
443
451
  const unknownReportStep = 5 * 1024 * 1024;
444
452
 
445
- // Progress is diagnostic output: keep stdout reserved for native command results.
446
- return ({ downloadedBytes, totalBytes }) => {
447
- if (!started) {
448
- started = true;
449
- if (Number.isFinite(totalBytes) && totalBytes > 0) {
450
- logger.warn?.(
451
- `Downloading MoonDesk ${version} native binary (${formatMiB(totalBytes)})...`,
452
- );
453
- } else {
454
- logger.warn?.(`Downloading MoonDesk ${version} native binary...`);
455
- }
453
+ const render = (line, done) => {
454
+ if (!writer || typeof writer.write !== "function") return;
455
+
456
+ // A redirected stderr cannot repaint one terminal row. Emit only the completed state there so
457
+ // logs stay compact; interactive terminals get the live in-place progress experience.
458
+ if (writer.isTTY !== true) {
459
+ if (done) writer.write(`${line}\n`);
460
+ return;
456
461
  }
457
462
 
458
- if (!Number.isFinite(downloadedBytes) || downloadedBytes <= 0) return;
463
+ const padding = " ".repeat(Math.max(0, lastLineLength - line.length));
464
+ writer.write(`\r${line}${padding}${done ? "\n" : ""}`);
465
+ lastLineLength = done ? 0 : line.length;
466
+ };
467
+
468
+ // Progress is diagnostic stderr output: stdout stays reserved for native command results.
469
+ return ({ downloadedBytes, totalBytes, done = false }) => {
470
+ if (!Number.isFinite(downloadedBytes) || downloadedBytes < 0) return;
459
471
 
460
472
  if (Number.isFinite(totalBytes) && totalBytes > 0) {
461
473
  const percent = Math.min(100, Math.floor((downloadedBytes / totalBytes) * 100));
462
- const bucket = percent === 100 ? 100 : Math.floor(percent / 25) * 25;
463
- if (bucket >= 25 && bucket > lastPercentBucket) {
464
- lastPercentBucket = bucket;
465
- logger.warn?.(
466
- `MoonDesk ${version} native binary download: ${bucket}% (${formatMiB(Math.min(downloadedBytes, totalBytes))} / ${formatMiB(totalBytes)})`,
474
+ const bucket = percent === 100 ? 100 : Math.floor(percent / percentReportStep) * percentReportStep;
475
+ if (bucket > lastPercentBucket || done) {
476
+ lastPercentBucket = Math.max(lastPercentBucket, bucket);
477
+ const transferred = formatMiB(Math.min(downloadedBytes, totalBytes));
478
+ render(
479
+ `MoonDesk ${formatProgressBar(bucket)} ${String(bucket).padStart(3)}% ${transferred} / ${formatMiB(totalBytes)}`,
480
+ done,
467
481
  );
468
482
  }
469
483
  return;
470
484
  }
471
485
 
472
- if (downloadedBytes - lastUnknownBytes >= unknownReportStep) {
486
+ if (downloadedBytes === 0 || downloadedBytes - lastUnknownBytes >= unknownReportStep || done) {
473
487
  lastUnknownBytes = downloadedBytes;
474
- logger.warn?.(
475
- `MoonDesk ${version} native binary download: ${formatMiB(downloadedBytes)} downloaded...`,
488
+ render(
489
+ done
490
+ ? `MoonDesk [██████████] done ${formatMiB(downloadedBytes)}`
491
+ : `MoonDesk [░░░░░░░░░░] downloading… ${formatMiB(downloadedBytes)}`,
492
+ done,
476
493
  );
477
494
  }
478
495
  };
@@ -488,7 +505,7 @@ module.exports = {
488
505
  };
489
506
 
490
507
  if (require.main === module) {
491
- ensureBinary({ onDownloadProgress: createDownloadProgressReporter(console) })
508
+ ensureBinary({ onDownloadProgress: createDownloadProgressReporter() })
492
509
  .then((binaryPath) => {
493
510
  console.log(`MoonDesk native binary ready at ${binaryPath}`);
494
511
  })
package/npm/moondesk.js CHANGED
@@ -181,7 +181,7 @@ async function orchestrate(options = {}) {
181
181
  let binaryPath;
182
182
  try {
183
183
  binaryPath = await ensureBinaryImpl({
184
- onDownloadProgress: createDownloadProgressReporter(logger),
184
+ onDownloadProgress: createDownloadProgressReporter(options.progressWriter ?? process.stderr),
185
185
  });
186
186
  } catch (error) {
187
187
  stopUpdateMonitor();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moondesk",
3
- "version": "0.11.8",
3
+ "version": "0.12.0",
4
4
  "description": "MoonDesk — use ChatGPT Chat as a local coding agent, by Shattermoon.",
5
5
  "author": "Shattermoon",
6
6
  "license": "MIT",