Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
verify:
strategy:
matrix:
include:
- os: ubuntu-latest
target: bun-linux-x64
- os: ubuntu-24.04-arm
target: bun-linux-arm64
- os: macos-14
target: bun-darwin-arm64
- os: macos-15-intel
target: bun-darwin-x64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
submodules: true
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.10
- run: bun install --frozen-lockfile
- run: bun run verify:pins
- run: bun run test
- run: bun build --compile --minify --target=${{ matrix.target }} --outfile=dist/alab src/cli.ts
- run: bun run test:binary
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: alab-${{ matrix.target }}
path: dist/alab
if-no-files-found: error
65 changes: 65 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: read

jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-latest
target: bun-linux-x64
- os: ubuntu-24.04-arm
target: bun-linux-arm64
- os: macos-14
target: bun-darwin-arm64
- os: macos-15-intel
target: bun-darwin-x64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
submodules: true
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.10
- run: bun install --frozen-lockfile
- run: bun run verify:pins
- run: bun run test
- run: bun build --compile --minify --target=${{ matrix.target }} --outfile=dist/alab src/cli.ts
- run: bun run test:binary
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: alab-${{ matrix.target }}
path: dist/alab
if-no-files-found: error

release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts
- name: Package exact tested binaries
run: |
mkdir -p dist staging
for artifact_dir in artifacts/alab-bun-*; do
target="${artifact_dir##*/alab-bun-}"
install -m 0755 "$artifact_dir/alab" staging/alab
touch -d '@0' staging/alab
tar --sort=name --format=ustar --owner=0 --group=0 --numeric-owner --mtime='@0' -C staging -cf - alab | gzip -n > "dist/alab-$target.tar.gz"
done
LC_ALL=C sha256sum dist/*.tar.gz > dist/SHA256SUMS
- uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
files: |
dist/*.tar.gz
dist/SHA256SUMS
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist/
node_modules/
.DS_Store
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "vendor/pages"]
path = vendor/pages
url = https://github.com/agentlab-in/pages.git
26 changes: 26 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Alab CLI

This repository owns command routing, exact tool pins, combined executable builds, and release automation for the `alab` CLI.

## Boundaries

- Tool repositories own their implementations. Do not fork tool behavior here.
- Integrations must use an exact version and commit SHA.
- A released artifact must not download a runtime or helper executable when used.
- Preserve child command arguments, standard input, standard output, standard error, and exit status.
- Add only platforms exercised by CI with a real compiled executable.
- Do not publish releases or tags without explicit authorization.

## Workflow

Use Bun 1.3.10 for dependency installation, tests, and compilation. After changing a pin or router, run:

```bash
bun install --frozen-lockfile
bun run verify:pins
bun run test
bun run build
bun run test:binary
```

Run Git commands from this repository. Preserve unrelated work. Never commit credentials, generated `dist/` files, or local Alab state.
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
# Alab CLI

Alab is the self-contained command line entry point for AgentLab tools.
Alab is one self-contained executable for AgentLab tools. The current release integrates [AgentLab Pages](https://github.com/agentlab-in/pages), a static site publisher backed by your own Cloudflare account.

The first integrated tool is [AgentLab Pages](https://github.com/agentlab-in/pages). Downloadable binaries and usage documentation are being prepared.
No separate Node.js, npm, Bun, Python, Go, Wrangler, or helper CLI installation is required to run a downloaded binary.

## Install

Download the archive matching your system from the repository's Releases page, verify it with `SHA256SUMS`, extract `alab`, and put it on your `PATH`.

Supported release targets:

| Operating system | CPU |
| --- | --- |
| macOS | Apple silicon, Intel |
| Linux | x86-64, ARM64 |

Windows is not currently built or tested.

```bash
alab --version
alab pages --help
alab pages setup
alab pages put ./site
```

`alab pages setup` guides Cloudflare API token validation, account selection, and project creation or reuse. Pages maintains authoritative state on one machine. A repeated `put` of the same directory retains its ID and URL. Cloudflare's default URL is used unless configuration explicitly supplies another base URL.

See [USAGE.md](./USAGE.md) for setup, command behavior, and limitations. See [docs/integration.md](./docs/integration.md) for the internal tool interface and update procedure.

## Build

The repository pins Bun and every tool revision. Bun is a build dependency only.

```bash
bun install --frozen-lockfile
bun run verify:pins
bun run test
bun run build
bun run test:binary
```

`bun run build:all` cross-compiles the four declared release targets into `dist/`.

Every CI run retains its four verified platform binaries as downloadable workflow artifacts. Tagged releases package the same target set into archives with a `SHA256SUMS` manifest.

## Security

Every published page is public. The optional Pages root password only obscures the root listing and does not protect direct page URLs. Never publish credentials or private content.

## License

MIT
56 changes: 56 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Usage

## Consumer usage

### Prerequisites

Downloaded Alab binaries have no language runtime or helper CLI prerequisite. Publishing requires a Cloudflare account and an API token with `Account`, `Cloudflare Pages`, `Edit` permission.

### Installation

Download the archive for your operating system and CPU from Releases. Verify the archive against `SHA256SUMS`, extract it, and place `alab` in a directory on `PATH`.

### Configuration

Run:

```bash
alab pages setup
```

The command links to Cloudflare's token creation page, accepts token input without echoing it, validates access, selects an account, and creates or reuses the configured Pages project. On macOS, the token is stored in Keychain. Other settings and Linux token storage use files under `~/.alab/` with restricted permissions.

For noninteractive use, provide `CLOUDFLARE_API_TOKEN` and the required setup options. Missing input fails instead of waiting for a prompt.

### Commands

```text
alab pages setup [options]
alab pages put [dir] [--id <id>] [--dry-run] [--skip-deploy] [--json]
alab pages remove [id] [--dry-run] [--skip-deploy] [--json]
alab pages read [id] [--dir <path>] [--json]
alab pages list [--json]
alab pages open [id]
alab pages info
```

`delete` aliases `remove`, and `ls` aliases `list`.

`put` copies a static directory into the local full snapshot and deploys it. Repeating it for the same directory uses `.alab/pages.json` to retain the same ID and URL. `remove` deletes the locally known page and redeploys the remaining snapshot. A deployment failure does not roll back the preceding local change, so retry the same command after fixing access.

`--dry-run` skips Cloudflare deployment but still changes local state. `--skip-deploy` changes only local state. Use isolated `ALAB_HOME` and `ALAB_PAGES_CONTENT` directories for experiments.

### Availability and limitations

Release automation builds macOS binaries for Apple silicon and Intel, plus Linux binaries for x86-64 and ARM64. CI runs the compiled command smoke test natively on all four operating system and CPU combinations.

Pages accepts ready-made static files only. It does not build frameworks, run servers, recover state across machines, download deployed pages, or manage custom domains. The local machine is authoritative. Every page URL is public.

## Enterprise usage

Alab can be integrated into internal agent workflows that need a stable, inspectable command surface for publishing static artifacts through organization-owned Cloudflare infrastructure.

If you want to deploy this internally, integrate it with your existing systems,
adapt it to your environment, or discuss adjacent engineering work, book a call:

https://cal.com/harshitsinghbhandari/30min
29 changes: 29 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions docs/integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Tool integration interface

## Current interface

The router imports `runCli(argv)` from the exact Pages `src/program.ts` revision and calls it in the same process when the first argument is `pages`. Pages receives the original `process.argv` array.

This preserves every remaining argument and option, standard input for hidden interactive prompts, standard output including JSON, standard error and subprocess progress, the working directory and environment, and success or failure through the process exit status.

No extraction is used. Bun embeds the router, Pages source, and JavaScript dependencies in the executable. Pages v2.0.0 deploys through the Cloudflare HTTP API and does not invoke Wrangler.

## Pin and integrity policy

`vendor/pages` is a Git submodule pinned by the parent commit. `pins.json` repeats its version, repository, commit SHA, and expected `package.json` SHA-256. `scripts/verify-pins.ts` rejects any mismatch before build or release.

JavaScript packages use exact versions and a committed Bun lockfile. The release workflow uses a pinned Bun version and emits `SHA256SUMS` for downloadable archives.

## Updating Pages

1. Fetch tags inside `vendor/pages`.
2. Check out the intended immutable release commit.
3. Update the Pages version, commit, and package hash in `pins.json`.
4. Review the Pages CLI entry point and dependencies for interface or runtime changes.
5. Run every command in `AGENTS.md`, including the compiled binary smoke test.
6. Update public docs and the Alab version when behavior changes.
7. Commit the submodule pointer, pins, lockfile changes, code, and docs together.

Never pin a moving branch or fetch tool code at runtime.
28 changes: 28 additions & 0 deletions docs/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Alab Pages integration specification

## Outcome

Distribute one `alab` executable per supported operating system and CPU. The executable routes `alab pages ...` to the pinned Pages implementation without requiring or downloading a runtime or helper CLI.

## Decisions

- Bun compile produces native executables and is used only in development and CI.
- Pages v2.0.0 plus its side-effect-free integration entry point is pinned as a Git submodule at merged commit `c6a71e826bfecb9f7c65c88c1f1d28ba23f72bd8`.
- Runtime dependencies are exact versions in `package.json` and `bun.lock`.
- Pages executes in-process. Arguments and process streams remain unchanged. Its rejection reaches one top-level error handler and sets exit code 1.
- `alab --version` reports the router version and every integrated tool version and SHA.
- Releases contain compressed binaries and a `SHA256SUMS` integrity manifest.
- CI runs native binary smoke tests on macOS ARM64, macOS Intel, Linux x86-64, and Linux ARM64.

## Excluded

Review integration, a plugin system, Pages recovery or pull behavior, framework builds, and custom-domain management are not part of this integration.

## Completion checks

1. Verify the Pages submodule SHA and package version.
2. Run router tests.
3. Compile the host executable.
4. Run Pages help, info, put, read, list, update, and remove against isolated temporary state with development tools absent from `PATH`.
5. Build all release targets and produce deterministic artifact names and checksums.
6. Retain each natively tested binary as a downloadable CI artifact.
23 changes: 23 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@agentlab/alab-cli",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"build": "bun build --compile --minify --outfile dist/alab src/cli.ts",
"build:all": "bun scripts/build-all.ts",
"test": "bun test tests/router.test.ts",
"test:binary": "bash scripts/smoke-binary.sh dist/alab",
"verify:pins": "bun scripts/verify-pins.ts"
},
"dependencies": {
"@noble/hashes": "2.4.0",
"commander": "13.1.0"
},
"devDependencies": {
"@types/bun": "1.3.10"
},
"engines": {
"bun": "1.3.10"
}
}
8 changes: 8 additions & 0 deletions pins.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"pages": {
"version": "2.0.0",
"repository": "https://github.com/agentlab-in/pages.git",
"commit": "c6a71e826bfecb9f7c65c88c1f1d28ba23f72bd8",
"packageSha256": "33359d14b47a323d4447890b4e5edf8700b25956e1a46f3843c6d0ab52287125"
}
}
16 changes: 16 additions & 0 deletions scripts/build-all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { mkdirSync, rmSync } from "node:fs";
import { resolve } from "node:path";
import { spawnSync } from "node:child_process";

const root = resolve(import.meta.dir, "..");
const dist = resolve(root, "dist");
const targets = ["bun-darwin-arm64", "bun-darwin-x64", "bun-linux-x64", "bun-linux-arm64"];

rmSync(dist, { recursive: true, force: true });
mkdirSync(dist, { recursive: true });

for (const target of targets) {
const name = target.replace(/^bun-/, "");
const result = spawnSync("bun", ["build", "--compile", "--minify", `--target=${target}`, `--outfile=${resolve(dist, `alab-${name}`)}`, resolve(root, "src/cli.ts")], { stdio: "inherit" });
if (result.status !== 0) process.exit(result.status ?? 1);
}
Loading
Loading