Skip to content

Commit a6d7fcc

Browse files
committed
feat: distribute as npm tarball with native Dart binary
Feat: CLI contract - Support run, run --server, help subcommands - No-args and --help show usage; invalid args exit 1 Feat: npm packaging - package.json with bin mapping to commandcode-bridge - bin/commandcode-bridge.js detects OS and spawns native binary - scripts/stage-npm-package.mjs assembles release tarball Feat: CI workflows - test.yml: dart analyze + test + smoke compile + wrapper validate - release.yml: matrix Dart compile (3 OS), package tarball, GitHub release - post-release.yml: install real tarball, smoke test, uninstall Chore: update run scripts, .gitignore Docs: README updates for npm install + CLI usage
1 parent 5fbf4a7 commit a6d7fcc

11 files changed

Lines changed: 517 additions & 17 deletions

File tree

.github/workflows/post-release.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Post-release
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
inputs:
8+
tag:
9+
description: "Tag to validate (e.g. v1.0.0). Leave empty to use the latest published release."
10+
required: false
11+
type: string
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
user-simulation:
18+
name: Simulate user install / run
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Node
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: "22"
28+
29+
- name: Resolve target tag
30+
id: resolve-tag
31+
shell: bash
32+
run: |
33+
if [ -n "${{ inputs.tag }}" ]; then
34+
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
35+
elif [ -n "${{ github.event.release.tag_name }}" ]; then
36+
echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
37+
else
38+
TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName')
39+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
40+
fi
41+
42+
- name: Download tarball
43+
shell: bash
44+
run: |
45+
TAG="${{ steps.resolve-tag.outputs.tag }}"
46+
REPO="${{ github.repository }}"
47+
gh release download "$TAG" -R "$REPO" --pattern "*.tgz"
48+
ls -la *.tgz
49+
50+
- name: Install from tarball (isolated prefix)
51+
shell: bash
52+
run: |
53+
TGZ=$(ls *.tgz | head -1)
54+
echo "Installing $TGZ..."
55+
npm install -g "./$TGZ"
56+
echo "Install complete."
57+
58+
- name: Verify command exists
59+
shell: bash
60+
run: |
61+
which commandcode-bridge
62+
commandcode-bridge help
63+
commandcode-bridge --help
64+
commandcode-bridge version 2>/dev/null || true
65+
66+
- name: Smoke test headless mode
67+
shell: bash
68+
run: |
69+
timeout 5 commandcode-bridge run --server 2>&1 || true
70+
echo "Headless mode: started successfully"
71+
72+
- name: Uninstall
73+
shell: bash
74+
run: |
75+
npm uninstall -g commandcode-bridge
76+
echo "Uninstall complete."

.github/workflows/release.yml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
- "[0-9]*"
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
build:
14+
name: Build (${{ matrix.os }})
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
include:
19+
- os: ubuntu-latest
20+
binary: app-linux
21+
- os: macos-latest
22+
binary: app-mac
23+
- os: windows-latest
24+
binary: app-win.exe
25+
runs-on: ${{ matrix.os }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
with:
29+
ref: ${{ github.ref_name }}
30+
31+
- uses: dart-lang/setup-dart@v1
32+
with:
33+
sdk: stable
34+
35+
- name: Install dependencies
36+
run: dart pub get
37+
38+
- name: Compile
39+
shell: bash
40+
run: dart compile exe bin/commandcode_bridge.dart -o ${{ matrix.binary }}
41+
42+
- uses: actions/upload-artifact@v4
43+
with:
44+
name: ${{ matrix.binary }}
45+
path: ${{ matrix.binary }}
46+
47+
package:
48+
name: Package tarball
49+
needs: build
50+
runs-on: ubuntu-latest
51+
if: ${{ !contains(github.ref_name, '-rc') && !contains(github.ref_name, '-beta') && !contains(github.ref_name, '-alpha') }}
52+
steps:
53+
- uses: actions/checkout@v4
54+
with:
55+
ref: ${{ github.ref_name }}
56+
57+
- uses: actions/setup-node@v4
58+
with:
59+
node-version: "22"
60+
61+
- name: Download binaries
62+
uses: actions/download-artifact@v4
63+
with:
64+
path: artifacts
65+
66+
- name: Stage binaries
67+
shell: bash
68+
run: |
69+
cp artifacts/app-linux/app-linux app-linux
70+
cp artifacts/app-mac/app-mac app-mac
71+
cp artifacts/app-win.exe/app-win.exe app-win.exe
72+
chmod 755 app-linux app-mac
73+
ls -la app-linux app-mac app-win.exe
74+
75+
- name: Package tarball
76+
shell: bash
77+
run: |
78+
node scripts/stage-npm-package.mjs --out .
79+
80+
- name: Verify tarball
81+
shell: bash
82+
run: |
83+
TGZ=$(ls commandcode-bridge-*.tgz)
84+
echo "Tarball: $TGZ"
85+
tar tzf "$TGZ"
86+
87+
- uses: actions/upload-artifact@v4
88+
with:
89+
name: tarball
90+
path: commandcode-bridge-*.tgz
91+
92+
release:
93+
name: Create GitHub release
94+
needs: [build, package]
95+
runs-on: ubuntu-latest
96+
if: ${{ !contains(github.ref_name, '-rc') && !contains(github.ref_name, '-beta') && !contains(github.ref_name, '-alpha') }}
97+
steps:
98+
- uses: actions/download-artifact@v4
99+
with:
100+
name: tarball
101+
102+
- name: Create release
103+
uses: softprops/action-gh-release@v2
104+
with:
105+
name: ${{ github.ref_name }}
106+
files: commandcode-bridge-*.tgz
107+
body: |
108+
## Installation
109+
110+
```bash
111+
# Download the .tgz asset below, then:
112+
npm install -g ./commandcode-bridge-${{ github.ref_name }}.tgz
113+
114+
# Run the bridge
115+
commandcode-bridge run # TUI mode
116+
commandcode-bridge run --server # Headless server mode
117+
```
118+
119+
## Requirements
120+
121+
- Node.js 18+ (for the npm launcher)
122+
- A Command Code account with active plan
123+
- `cmd login` to authenticate (one-time)
124+
125+
No Dart SDK needed.

.github/workflows/test.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
test:
16+
name: Test (Dart ${{ matrix.dart }} / Node ${{ matrix.node }})
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
os: [ubuntu-latest]
21+
dart: [stable]
22+
node: [22]
23+
runs-on: ${{ matrix.os }}
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- uses: dart-lang/setup-dart@v1
28+
with:
29+
sdk: ${{ matrix.dart }}
30+
31+
- uses: actions/setup-node@v4
32+
with:
33+
node-version: ${{ matrix.node }}
34+
35+
- name: Install Dart dependencies
36+
run: dart pub get
37+
38+
- name: Analyze
39+
run: dart analyze
40+
41+
- name: Run tests
42+
run: dart test
43+
44+
- name: Smoke compile
45+
run: dart compile exe bin/commandcode_bridge.dart -o app-linux
46+
47+
- name: CLI smoke test
48+
run: |
49+
./app-linux help
50+
./app-linux --help
51+
! ./app-linux foo
52+
53+
- name: Validate npm package
54+
run: npm pack --dry-run
55+
56+
- name: Validate wrapper syntax
57+
run: node --check bin/commandcode-bridge.js

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,9 @@ bin/commandcode_bridge.exe
33
.dart_tool/
44
build/
55
logs/
6-
6+
release-package/
7+
stage/
8+
*.tgz
9+
app-linux
10+
app-mac
11+
app-win.exe

README.md

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,68 @@ Turn your Command Code Go plan ($1/month) into an OpenAI-compatible API for use
1919
- **Config Persistence** -- `~/.config/commandcode-bridge/config.json` survives updates
2020
- **Cross-platform** -- Linux (primary), macOS (experimental), Windows (experimental)
2121

22-
## Prerequisites
22+
## Quick Install (no Dart SDK needed)
2323

24-
- A Command Code account with active plan
25-
- Dart SDK 3.10+ (for building from source)
24+
```bash
25+
# Download the .tgz from GitHub Releases, then:
26+
npm install -g ./commandcode-bridge-v1.0.0.tgz
27+
28+
# Run the bridge
29+
commandcode-bridge run # TUI mode
30+
commandcode-bridge run --server # Headless server mode
31+
```
32+
33+
**Requirements:** Node.js 18+, a Command Code account with active plan.
2634

2735
## Platform Support
2836

29-
| Platform | Status | Clipboard | Build |
30-
|----------|--------|-----------|-------|
31-
| Linux | Primary (fully tested) | `wl-copy` -> `xclip` -> OSC 52 | `./build` |
32-
| macOS | Experimental | `pbcopy` -> OSC 52 | `./build` |
33-
| Windows | Experimental | `clip` -> OSC 52 | `build.bat` |
37+
| Platform | Status | Clipboard | Installation |
38+
|----------|--------|-----------|-------------|
39+
| Linux | Primary (fully tested) | `wl-copy` -> `xclip` -> OSC 52 | npm tarball |
40+
| macOS | Experimental | `pbcopy` -> OSC 52 | npm tarball |
41+
| Windows | Experimental | `clip` -> OSC 52 | npm tarball |
42+
43+
## Prerequisites
44+
45+
- A Command Code account with active plan
46+
- Node.js 18+ (for npm launcher)
47+
- Dart SDK 3.10+ (only for building from source)
3448

3549
## Quick Start
3650

51+
### End-user (from npm tarball)
52+
3753
```bash
3854
# Login to Command Code (one-time)
3955
cmd login
4056

41-
# Run the bridge (Linux/macOS)
42-
./run # TUI mode (proxy auto-starts at 17077)
57+
# Run the bridge
58+
commandcode-bridge run
59+
60+
# Or headless server mode
61+
commandcode-bridge run --server
62+
63+
# Show help
64+
commandcode-bridge help
65+
```
66+
67+
### Developer (from source)
68+
69+
```bash
70+
git clone <repo-url>
71+
cd commandcode-bridge
72+
./build # dart pub get + dart compile exe
73+
./run # TUI mode
4374
./run server # Headless server mode
75+
./run help # Show help
76+
```
4477

45-
# Run the bridge (Windows)
46-
run.bat # TUI mode
47-
run.bat server # Headless server mode
78+
### Usage
79+
80+
```
81+
commandcode-bridge run Start the bridge in TUI mode
82+
commandcode-bridge run --server Start the bridge in headless server mode
83+
commandcode-bridge help Show this help screen
4884
```
4985

5086
The bridge reads credentials from `~/.commandcode/auth.json` (same file as `cmd`).

bin/commandcode-bridge.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env node
2+
import { spawn } from "node:child_process";
3+
import { existsSync } from "node:fs";
4+
import { dirname, join } from "node:path";
5+
import { fileURLToPath } from "node:url";
6+
7+
const platform = process.platform;
8+
let binaryName = "";
9+
10+
if (platform === "win32") {
11+
binaryName = "app-win.exe";
12+
} else if (platform === "darwin") {
13+
binaryName = "app-mac";
14+
} else if (platform === "linux") {
15+
binaryName = "app-linux";
16+
} else {
17+
console.error(`Unsupported operating system: ${platform}`);
18+
process.exit(1);
19+
}
20+
21+
const __dirname = dirname(dirname(fileURLToPath(import.meta.url)));
22+
const binaryPath = join(__dirname, "bin-executables", binaryName);
23+
24+
if (!existsSync(binaryPath)) {
25+
console.error(`Binary not found: ${binaryPath}`);
26+
console.error("The commandcode-bridge package appears to be corrupted or incomplete.");
27+
console.error("Try reinstalling from a fresh tarball.");
28+
process.exit(1);
29+
}
30+
31+
const child = spawn(binaryPath, process.argv.slice(2), {
32+
stdio: "inherit",
33+
shell: false,
34+
});
35+
36+
child.on("exit", (code) => {
37+
process.exit(code ?? 0);
38+
});

0 commit comments

Comments
 (0)