agentlab 0.2.2 → 0.2.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.
package/README.md CHANGED
@@ -15,7 +15,8 @@ agentlab
15
15
 
16
16
  Run `agentlab` from any directory. The app opens to the project picker and waits for you to choose a
17
17
  folder. On first launch, the npm package downloads the matching AgentLab executable from GitHub and
18
- caches it. Later launches use the cached executable.
18
+ caches it. The installer shows the percentage, downloaded size, and download speed while it works.
19
+ Later launches use the cached executable.
19
20
 
20
21
  ## Requirements
21
22
 
package/dist/cache.js CHANGED
@@ -32,6 +32,7 @@ export async function ensureCachedBinary(manifest, key, options = {}) {
32
32
  const fetchImplementation = options.fetch ?? globalThis.fetch;
33
33
  const downloadUrl = releaseDownloadUrl(manifest, key);
34
34
  try {
35
+ options.onDownloadProgress?.({ downloadedBytes: 0, totalBytes: target.size });
35
36
  const response = await fetchImplementation(downloadUrl, {
36
37
  redirect: "follow",
37
38
  signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MILLISECONDS)
@@ -62,6 +63,7 @@ export async function ensureCachedBinary(manifest, key, options = {}) {
62
63
  }
63
64
  hash.update(chunk);
64
65
  await writeAll(file, chunk);
66
+ options.onDownloadProgress?.({ downloadedBytes: size, totalBytes: target.size });
65
67
  }
66
68
  await file.sync();
67
69
  }
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import { readFile } from "node:fs/promises";
4
4
  import { dirname, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { ensureCachedBinary, resolveCacheRoot } from "./cache.js";
7
+ import { InstallProgressReporter } from "./install-progress.js";
7
8
  import { currentRuntimePlatform, parseReleaseManifest, resolveRuntimeTarget } from "./manifest.js";
8
9
  const REGISTRY_LATEST_URL = "https://registry.npmjs.org/agentlab/latest";
9
10
  const STABLE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
@@ -32,10 +33,28 @@ export async function runLauncher(args, options = {}) {
32
33
  }
33
34
  const runtime = options.runtime ?? currentRuntimePlatform();
34
35
  const target = resolveRuntimeTarget(runtime);
35
- const binary = await ensureCachedBinary(manifest, target, {
36
- cacheRoot: options.cacheRoot ?? resolveCacheRoot(environment),
37
- fetch: options.fetch
36
+ const stderr = options.stderr ?? ((message) => process.stderr.write(message));
37
+ const progress = new InstallProgressReporter({
38
+ isTTY: options.stderrIsTTY ?? (options.stderr === undefined && process.stderr.isTTY),
39
+ target,
40
+ version: packageVersion,
41
+ write: stderr
38
42
  });
43
+ let binary;
44
+ try {
45
+ binary = await ensureCachedBinary(manifest, target, {
46
+ cacheRoot: options.cacheRoot ?? resolveCacheRoot(environment),
47
+ fetch: options.fetch,
48
+ onDownloadProgress: (downloadProgress) => {
49
+ progress.report(downloadProgress);
50
+ }
51
+ });
52
+ }
53
+ catch (error) {
54
+ progress.clear();
55
+ throw error;
56
+ }
57
+ progress.complete();
39
58
  return execute(binary, args, {
40
59
  ...environment,
41
60
  AGENTLAB_INSTALL_METHOD: "npm",
@@ -0,0 +1,82 @@
1
+ import { performance } from "node:perf_hooks";
2
+ const BAR_WIDTH = 16;
3
+ const BYTES_PER_MEGABYTE = 1_000_000;
4
+ const MINIMUM_RENDER_INTERVAL_MILLISECONDS = 100;
5
+ const CLEAR_LINE = "\r\u001B[2K";
6
+ const targetLabels = {
7
+ "linux-x64": "Linux x64",
8
+ "mac-arm64": "macOS arm64"
9
+ };
10
+ export class InstallProgressReporter {
11
+ isTTY;
12
+ now;
13
+ targetLabel;
14
+ version;
15
+ write;
16
+ active = false;
17
+ finished = false;
18
+ lastRenderAt;
19
+ startedAt;
20
+ constructor(options) {
21
+ this.isTTY = options.isTTY;
22
+ this.now = options.now ?? (() => performance.now());
23
+ this.targetLabel = targetLabels[options.target];
24
+ this.version = options.version;
25
+ this.write = options.write;
26
+ }
27
+ report(progress) {
28
+ if (this.finished)
29
+ return;
30
+ const now = this.now();
31
+ if (!this.active) {
32
+ this.active = true;
33
+ this.startedAt = now;
34
+ if (!this.isTTY) {
35
+ this.write(`Downloading AgentLab ${this.version} for ${this.targetLabel} (${formatMegabytes(progress.totalBytes)} MB)...\n`);
36
+ return;
37
+ }
38
+ }
39
+ if (!this.isTTY)
40
+ return;
41
+ const complete = progress.downloadedBytes >= progress.totalBytes;
42
+ if (!complete &&
43
+ this.lastRenderAt !== undefined &&
44
+ now - this.lastRenderAt < MINIMUM_RENDER_INTERVAL_MILLISECONDS) {
45
+ return;
46
+ }
47
+ this.lastRenderAt = now;
48
+ this.write(`${CLEAR_LINE}${renderProgress(progress, now - (this.startedAt ?? now), this.version)}`);
49
+ }
50
+ complete() {
51
+ if (!this.active || this.finished)
52
+ return;
53
+ this.finished = true;
54
+ if (this.isTTY)
55
+ this.write(CLEAR_LINE);
56
+ const marker = this.isTTY ? "✓ " : "";
57
+ this.write(`${marker}AgentLab ${this.version} installed for ${this.targetLabel}.\n`);
58
+ this.write("Starting AgentLab...\n");
59
+ }
60
+ clear() {
61
+ if (!this.active || this.finished)
62
+ return;
63
+ this.finished = true;
64
+ if (this.isTTY)
65
+ this.write(CLEAR_LINE);
66
+ }
67
+ }
68
+ function renderProgress(progress, elapsedMilliseconds, version) {
69
+ const ratio = clamp(progress.downloadedBytes / progress.totalBytes, 0, 1);
70
+ const filled = ratio === 1 ? BAR_WIDTH : Math.floor(ratio * BAR_WIDTH);
71
+ const bar = `${"█".repeat(filled)}${"░".repeat(BAR_WIDTH - filled)}`;
72
+ const percent = Math.floor(ratio * 100);
73
+ const elapsedSeconds = Math.max(elapsedMilliseconds / 1_000, 0.001);
74
+ const megabytesPerSecond = progress.downloadedBytes / BYTES_PER_MEGABYTE / elapsedSeconds;
75
+ return `Downloading AgentLab ${version} [${bar}] ${String(percent).padStart(3)}% ${formatMegabytes(progress.downloadedBytes)}/${formatMegabytes(progress.totalBytes)} MB ${megabytesPerSecond.toFixed(1)} MB/s`;
76
+ }
77
+ function formatMegabytes(bytes) {
78
+ return (bytes / BYTES_PER_MEGABYTE).toFixed(1);
79
+ }
80
+ function clamp(value, minimum, maximum) {
81
+ return Math.min(Math.max(value, minimum), maximum);
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentlab",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Run Codex, Claude Code, and OpenCode in one local terminal, with one captain per project.",
5
5
  "keywords": [
6
6
  "ai",
@@ -2,15 +2,15 @@
2
2
  "repository": "RiadMefti/agentlab",
3
3
  "targets": {
4
4
  "linux-x64": {
5
- "asset": "agentlab-v0.2.2-linux-x64",
6
- "sha256": "1a0188a0bd416244dbc771d0ad6fff37ec5555c99977cd1607a541c1061708af",
5
+ "asset": "agentlab-v0.2.4-linux-x64",
6
+ "sha256": "bd3a71fb616f3dee3d4209264e4b7318bad0dda89559a51c4f0631d1563a082e",
7
7
  "size": 116298952
8
8
  },
9
9
  "mac-arm64": {
10
- "asset": "agentlab-v0.2.2-mac-arm64",
11
- "sha256": "0568c53801d701f016006eb89877fd7b1ad7355415bc38928faa3c15bd718aea",
10
+ "asset": "agentlab-v0.2.4-mac-arm64",
11
+ "sha256": "2ed9413662f1441ebe69c6fb4101e4e179446605186f650e180624e9e369089f",
12
12
  "size": 77351282
13
13
  }
14
14
  },
15
- "version": "0.2.2"
15
+ "version": "0.2.4"
16
16
  }