agentlab 0.2.1 → 0.2.3

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
@@ -1,19 +1,74 @@
1
1
  # AgentLab
2
2
 
3
- One captain for all your coding agents in a fast local terminal UI.
3
+ One captain per project folder, with Codex, Claude Code, and OpenCode in one local terminal.
4
+
5
+ AgentLab lets you switch between projects and run multiple coding-agent sessions while keeping their
6
+ real CLIs accessible. Each project points to a folder on your machine. It has one captain and any
7
+ number of workers.
8
+
9
+ ## Install
4
10
 
5
11
  ```bash
6
12
  npm install --global agentlab
7
13
  agentlab
8
14
  ```
9
15
 
10
- The npm launcher downloads the matching AgentLab executable on first use, verifies its
11
- package-pinned size and SHA-256 digest, and caches it by version. It rechecks the cached digest
12
- before execution. Linux x64 with glibc and Apple-silicon macOS are supported. AgentLab requires
13
- `tmux` and at least one authenticated provider CLI: Codex, Claude Code, or OpenCode.
16
+ Run `agentlab` from any directory. The app opens to the project picker and waits for you to choose a
17
+ folder. On first launch, the npm package downloads the matching AgentLab executable from GitHub and
18
+ caches it. The installer shows the percentage, downloaded size, and download speed while it works.
19
+ Later launches use the cached executable.
20
+
21
+ ## Requirements
22
+
23
+ - Linux x64 with glibc, or macOS on Apple silicon
24
+ - Node.js 20 or newer
25
+ - `tmux`
26
+ - At least one installed and authenticated provider CLI: `codex`, `claude`, or `opencode`
27
+ - A terminal at least 90 columns by 18 rows
28
+
29
+ Install tmux with `brew install tmux` on macOS or your Linux distribution's package manager.
30
+
31
+ ## Start a project
32
+
33
+ 1. Run `agentlab`.
34
+ 2. Press `Alt+N` and enter the path to an existing folder. The folder can be anywhere on your
35
+ machine.
36
+ 3. Name the project and choose its captain provider, model, and reasoning level.
37
+ 4. Add workers with `Alt+W` when the captain needs more sessions.
38
+ 5. Switch projects from the left sidebar. Each project keeps its captain and workers together.
39
+
40
+ Removing a project stops its managed sessions and forgets it in AgentLab. The project folder and its
41
+ files stay untouched.
42
+
43
+ ## Keys
44
+
45
+ | Key | Action |
46
+ | ------------------------- | --------------------------------------------- |
47
+ | `Alt+1`, `Alt+2`, `Alt+3` | Focus projects, terminal, or agents |
48
+ | `Up`, `Down`, `Enter` | Navigate a focused sidebar and enter terminal |
49
+ | `Alt+N` | Add a project folder |
50
+ | `Alt+W` | Add a worker to the selected project |
51
+ | `Delete` | Remove the selected project or worker |
52
+ | `Alt+C` | Copy the terminal selection |
53
+ | `Alt+Q` | Quit AgentLab |
54
+
55
+ ## Updates
56
+
57
+ AgentLab updates only when you ask it to:
58
+
59
+ ```bash
60
+ agentlab update --check
61
+ agentlab update
62
+ ```
63
+
64
+ Normal startup does not check for updates.
65
+
66
+ ## Local data
14
67
 
15
- Use `agentlab update` for an explicit update or `agentlab update --check` to check without changing
16
- the installation. Normal cached startup performs no update request.
68
+ AgentLab opens no network listener. It stores the project list in a local SQLite database, keeps
69
+ provider credentials in each CLI's existing authentication store, and runs agent sessions through
70
+ tmux.
17
71
 
18
- See [the AgentLab repository](https://github.com/RiadMefti/agentlab) for usage, release checksums,
19
- SBOMs, and build provenance.
72
+ Read the [documentation](https://github.com/RiadMefti/agentlab#readme), browse
73
+ [releases](https://github.com/RiadMefti/agentlab/releases), or
74
+ [report a problem](https://github.com/RiadMefti/agentlab/issues).
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,7 +1,18 @@
1
1
  {
2
2
  "name": "agentlab",
3
- "version": "0.2.1",
4
- "description": "One captain for all your coding agents in a fast local terminal UI.",
3
+ "version": "0.2.3",
4
+ "description": "Run Codex, Claude Code, and OpenCode in one local terminal, with one captain per project.",
5
+ "keywords": [
6
+ "ai",
7
+ "coding-agents",
8
+ "codex",
9
+ "claude-code",
10
+ "opencode",
11
+ "multi-agent",
12
+ "terminal",
13
+ "tui",
14
+ "tmux"
15
+ ],
5
16
  "type": "module",
6
17
  "bin": {
7
18
  "agentlab": "dist/agentlab.js"
@@ -21,12 +32,13 @@
21
32
  "license": "MIT",
22
33
  "repository": {
23
34
  "type": "git",
24
- "url": "git+https://github.com/RiadMefti/agentlab.git"
35
+ "url": "git+https://github.com/RiadMefti/agentlab.git",
36
+ "directory": "packages/launcher"
25
37
  },
26
38
  "bugs": {
27
39
  "url": "https://github.com/RiadMefti/agentlab/issues"
28
40
  },
29
- "homepage": "https://github.com/RiadMefti/agentlab#readme",
41
+ "homepage": "https://github.com/RiadMefti/agentlab",
30
42
  "publishConfig": {
31
43
  "access": "public"
32
44
  }
@@ -2,15 +2,15 @@
2
2
  "repository": "RiadMefti/agentlab",
3
3
  "targets": {
4
4
  "linux-x64": {
5
- "asset": "agentlab-v0.2.1-linux-x64",
6
- "sha256": "a05e7c4a02a33fe4859afa1af84ae247f17b1c7f04e7c0926cd5ca8bc2fc13a9",
5
+ "asset": "agentlab-v0.2.3-linux-x64",
6
+ "sha256": "c4da3d5e1d5191a3316fc4c6db4d40b73dabb8eb882354fa67c918097944b5ec",
7
7
  "size": 116298952
8
8
  },
9
9
  "mac-arm64": {
10
- "asset": "agentlab-v0.2.1-mac-arm64",
11
- "sha256": "c12654c6a41fe72170b4cc720316eefec1f1af6ce7518e661cca4e3900360552",
10
+ "asset": "agentlab-v0.2.3-mac-arm64",
11
+ "sha256": "b6ebe1fc40338c16a9fbfe7da7cc0b79c9a93d516387f61a0cdc4a3fae30ca7c",
12
12
  "size": 77351282
13
13
  }
14
14
  },
15
- "version": "0.2.1"
15
+ "version": "0.2.3"
16
16
  }