Skip to content

Commit 7fbf5b5

Browse files
committed
feat: Anthropic API, dual endpoints, self-update, Ctrl+C guard
Feat: Anthropic Messages API - Full SSE streaming + non-streaming /v1/messages + /messages endpoints - Tool use blocks with input_json_delta, thinking blocks - stop_reason mapping (tool-calls -> tool_use, length -> max_tokens) Feat: self-update - commandcode-bridge update downloads latest .tgz from GitHub Release - API cache (1h TTL) in ~/.config/commandcode-bridge/update-cache.json - Clean existing install before npm install -g (handles npm v11 symlink) - --version flag prints version from package.json Feat: TUI - Dual endpoint header (OpenAI + Anthropic URLs) with [o]/[a] copy - Ctrl+C opens quit confirmation panel instead of force exit - Footer, help page, Proxy Config synced with both endpoints Refactor: server split - server_controller.dart, openai_handler.dart, anthropic_handler.dart - Extracted version constant to models/version.dart Docs: all synced - README, AGENTS.md, CHANGELOG, INSTALL, ARCHITECTURE, release.yml - .gitignore updated for runtime artifact See CHANGELOG.md for full details.
1 parent aecc1d0 commit 7fbf5b5

13 files changed

Lines changed: 326 additions & 26 deletions

File tree

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ jobs:
118118
# Run the bridge
119119
commandcode-bridge run # TUI mode
120120
commandcode-bridge run --server # Headless server mode
121+
122+
# Update when a new release is out
123+
commandcode-bridge update
121124
```
122125
123126
### Why not `npm install -g Khip01/commandcode-bridge#${{ github.ref_name }}`?

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ stage/
99
app-linux
1010
app-mac
1111
app-win.exe
12+
13+
# Runtime artifacts (update cache)
14+
update-cache.json

AGENTS.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ commandcode-bridge/
3131
│ ├── main.dart # CLI wiring (run, run --server, help)
3232
│ ├── models/
3333
│ │ ├── account.dart # Account + config store (port persist)
34-
│ │ └── models_db.dart # 44 models with goAccessible field
34+
│ │ ├── models_db.dart # 44 models with goAccessible field
35+
│ │ └── version.dart # Bridge version constant
3536
│ ├── services/
3637
│ │ ├── api_client.dart # HTTP client for api.commandcode.ai
37-
│ │ └── log_store.dart # JSONL activity log (2000 entries)
38+
│ │ ├── log_store.dart # JSONL activity log (2000 entries)
39+
│ │ └── updater.dart # Self-update: API cache + download .tgz + npm install -g
3840
│ ├── server/
3941
│ │ ├── server_controller.dart # HTTP server + routing
4042
│ │ ├── openai_handler.dart # OpenAI-compatible proxy
@@ -71,12 +73,14 @@ commandcode-bridge/
7173
```bash
7274
commandcode-bridge run Start the bridge in TUI mode
7375
commandcode-bridge run --server Start the bridge in headless server mode
74-
commandcode-bridge help Show this help screen
75-
commandcode-bridge (no args) Show this help screen
76+
commandcode-bridge update Download and install latest stable release
77+
commandcode-bridge help Show help screen
78+
commandcode-bridge --version Print version string
79+
commandcode-bridge (no args) Show help screen
7680
commandcode-bridge <invalid> Show help screen, exit 1
7781
```
7882

79-
The npm wrapper (`bin/commandcode-bridge.js`) detects `process.platform`, maps to the correct native binary (`app-linux`, `app-mac`, `app-win.exe`), and spawns it with `stdio: 'inherit'`. All args are forwarded. Once launched, Node.js goes idle and the Dart binary takes full control.
83+
The npm wrapper (`bin/commandcode-bridge.js`) detects `process.platform`, maps to the correct native binary (`app-linux`, `app-mac`, `app-win.exe`), and spawns it with `stdio: 'inherit'`. `--version` is intercepted in the wrapper (reads `package.json`). All other args (including `update`) are forwarded to Dart. Once launched, Node.js goes idle and the Dart binary takes full control.
8084

8185
## Distribution
8286

@@ -246,8 +250,9 @@ Copy to clipboard via platform-specific tools, fallback to OSC 52:
246250
- Windows: `clip` -> OSC 52
247251

248252
Copy triggers:
249-
- `[c]` -- Copy endpoint URL (`http://127.0.0.1:17077/v1`)
250-
- `[Enter]` on model page -- Copy selected model codename
253+
- `[o]` — Copy OpenAI endpoint URL (`http://127.0.0.1:17077/v1`)
254+
- `[a]` — Copy Anthropic endpoint URL (`http://127.0.0.1:17077`)
255+
- `[Enter]` on model page — Copy selected model codename
251256

252257
## TUI Pages
253258

@@ -309,6 +314,7 @@ run.bat server # Headless server mode
309314
npm install -g ./commandcode-bridge-vX.Y.Z.tgz
310315
commandcode-bridge run # TUI mode
311316
commandcode-bridge run --server # Headless server mode
317+
commandcode-bridge update # Update to latest stable release
312318
commandcode-bridge help # Show help
313319
```
314320

CHANGELOG.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
## v1.1.0 (unreleased)
5+
## v1.1.0 (2026-07-28)
66

77
### Features
88

@@ -13,25 +13,39 @@ All notable changes to this project will be documented in this file.
1313
- Non-streaming endpoint with proper Anthropic response shape
1414
- Anthropic Messages protocol -> CC wire format conversion (system, tools, messages)
1515
- stop_reason mapping: tool-calls -> tool_use, length -> max_tokens
16+
- Self-update CLI command
17+
- `commandcode-bridge update` -- download latest release `.tgz` from GitHub and install via `npm install -g`
18+
- `--version` / `-v` flag prints version string
19+
- Update cache in `~/.config/commandcode-bridge/update-cache.json` (1-hour TTL, avoids rate limits)
20+
21+
### TUI
22+
23+
- Dual endpoint header: OpenAI (`http://127.0.0.1:{port}/v1`) + Anthropic (`http://127.0.0.1:{port}`)
24+
- Keymaps: `[o]` copies OpenAI URL, `[a]` copies Anthropic base URL
25+
- Ctrl+C now opens quit confirmation panel (not force exit)
26+
- Quit panel: Y / Enter / second Ctrl+C all confirm quit
27+
- Status bar shows red quit hint when panel is open
28+
- Footer, help page, and Proxy Config (page 6) synced with both endpoints
1629

1730
### Refactor
1831

1932
- Server code split into three clean files:
20-
- `server_controller.dart` HTTP server, request routing, shared endpoints (/models, /health, /token, /info)
21-
- `openai_handler.dart` OpenAI-compatible handler (streaming + non-streaming)
22-
- `anthropic_handler.dart` Anthropic-compatible handler (streaming + non-streaming)
33+
- `server_controller.dart` -- HTTP server, request routing, shared endpoints (/models, /health, /token, /info)
34+
- `openai_handler.dart` -- OpenAI-compatible handler (streaming + non-streaming)
35+
- `anthropic_handler.dart` -- Anthropic-compatible handler (streaming + non-streaming)
2336
- Common upstream body builder shared between handlers
2437
- Removed diagnostic/debug logging; production-level logging only
2538
- Renamed ProxyServer -> ServerController, extracted handlers to own classes
2639
- Update OpenCode protocol mapping for production upstream format
2740
- Fixed config.date field missing from OpenAI handler (was broken after refactor)
41+
- Extracted version into `lib/src/models/version.dart` constant
2842

2943
### Docs
3044

3145
- README.md trimmed to preview; detailed docs moved to `docs/`
3246
- New docs: INSTALL.md, API-REFERENCE.md, TUI.md, ARCHITECTURE.md
3347
- API-REFERENCE.md covers OpenAI, Anthropic, and Zed client configurations
34-
- AGENTS.md synced with new file structure and endpoints
48+
- AGENTS.md synced with new file structure, endpoints, and naming conventions
3549

3650
## v1.0.0 (2026-07-27)
3751

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@ APIs for use with OpenCode, Zed, Cursor, or any compatible client.
2121
## Quick Install
2222

2323
```
24-
npm install -g ./commandcode-bridge-vX.Y.Z.tgz
24+
npm install -g ./commandcode-bridge-v1.1.0.tgz
2525
commandcode-bridge run
2626
```
2727

28+
Update when a new release is available:
29+
30+
```
31+
commandcode-bridge update
32+
```
33+
2834
Requirements: Node.js 18+, a Command Code account.
2935

3036
## Documentation

bin/commandcode-bridge.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/env node
2-
import { spawn } from "node:child_process";
3-
import { existsSync } from "node:fs";
2+
import { existsSync, readFileSync } from "node:fs";
43
import { dirname, join } from "node:path";
54
import { fileURLToPath } from "node:url";
65

@@ -21,14 +20,33 @@ if (platform === "win32") {
2120
const __dirname = dirname(dirname(fileURLToPath(import.meta.url)));
2221
const binaryPath = join(__dirname, "bin-executables", binaryName);
2322

23+
function getVersion() {
24+
try {
25+
const pkgPath = join(__dirname, "package.json");
26+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
27+
return pkg.version;
28+
} catch {
29+
return "0.0.0";
30+
}
31+
}
32+
33+
const args = process.argv.slice(2);
34+
35+
if (args.includes("--version") || args.includes("-v")) {
36+
console.log(`CommandCode Bridge v${getVersion()}`);
37+
process.exit(0);
38+
}
39+
2440
if (!existsSync(binaryPath)) {
2541
console.error(`Binary not found: ${binaryPath}`);
2642
console.error("The commandcode-bridge package appears to be corrupted or incomplete.");
2743
console.error("Try reinstalling from a fresh tarball.");
2844
process.exit(1);
2945
}
3046

31-
const child = spawn(binaryPath, process.argv.slice(2), {
47+
const { spawn } = await import("node:child_process");
48+
49+
const child = spawn(binaryPath, args, {
3250
stdio: "inherit",
3351
shell: false,
3452
});

docs/ARCHITECTURE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ commandcode-bridge/
2222
│ ├── main.dart # CLI wiring (TUI / server modes)
2323
│ ├── models/
2424
│ │ ├── account.dart # Account + config store (port persist)
25-
│ │ └── models_db.dart # 44 models with plan access metadata
25+
│ │ ├── models_db.dart # 44 models with plan access metadata
26+
│ │ └── version.dart # Bridge version constant
2627
│ ├── services/
2728
│ │ ├── api_client.dart # HTTP client for api.commandcode.ai
28-
│ │ └── log_store.dart # JSONL activity log (2000 entries max)
29+
│ │ ├── log_store.dart # JSONL activity log (2000 entries max)
30+
│ │ └── updater.dart # Self-update: API cache + download .tgz + npm install -g
2931
│ ├── server/
3032
│ │ ├── server_controller.dart # HTTP server, routing, shared endpoints
3133
│ │ ├── openai_handler.dart # OpenAI-compatible proxy

docs/INSTALL.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ npm install -g ./commandcode-bridge-vX.Y.Z.tgz
99
commandcode-bridge run
1010
```
1111

12+
## Updating
13+
14+
Once installed, update from the bridge itself:
15+
16+
```bash
17+
commandcode-bridge update
18+
```
19+
20+
The update command fetches the latest release tag from GitHub API, downloads the
21+
`.tgz` asset, removes the previous install, and runs `npm install -g`. The API
22+
call is cached locally for 1 hour to avoid rate limits. A restart is required
23+
after updating.
24+
1225
## Build from Source
1326

1427
```bash

lib/src/main.dart

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@ import 'dart:async';
22
import 'dart:io';
33
import 'package:nocterm/nocterm.dart';
44
import 'models/account.dart';
5+
import 'models/version.dart';
56
import 'services/log_store.dart';
7+
import 'services/updater.dart';
68
import 'server/server_controller.dart';
79
import 'tui/app.dart';
810

9-
const _usage = '''CommandCode Bridge
11+
const _usage = '''CommandCode Bridge v$bridgeVersion
1012
1113
Usage:
1214
commandcode-bridge run
1315
commandcode-bridge run --server
16+
commandcode-bridge update
1417
commandcode-bridge help
1518
1619
Commands:
1720
run Start the bridge in TUI mode
1821
run --server Start the bridge in headless server mode
22+
update Download and install latest stable release
1923
help Show this help screen
2024
''';
2125

@@ -26,13 +30,20 @@ Future<void> main(List<String> args) async {
2630
final noArgs = args.isEmpty;
2731
final showHelp = noArgs || args.contains('help') || args.contains('--help') || args.contains('-h');
2832
final isRun = !noArgs && args.first == 'run';
33+
final isUpdate = !noArgs && args.first == 'update';
34+
35+
if (args.contains('--version') || args.contains('-v')) {
36+
stdout.writeln('CommandCode Bridge v$bridgeVersion');
37+
return;
38+
}
2939

3040
if (showHelp) {
31-
if (noArgs) {
32-
_printUsage();
33-
} else {
34-
_printUsage();
35-
}
41+
_printUsage();
42+
return;
43+
}
44+
45+
if (isUpdate) {
46+
await _runUpdate();
3647
return;
3748
}
3849

@@ -88,6 +99,24 @@ Future<void> main(List<String> args) async {
8899
server.stop();
89100
}
90101

102+
Future<void> _runUpdate() async {
103+
stdout.writeln('CommandCode Bridge v$bridgeVersion');
104+
stdout.writeln('Checking for stable update...');
105+
final updater = Updater();
106+
try {
107+
final result = await updater.update();
108+
if (result.success) {
109+
stdout.writeln(result.message);
110+
stdout.writeln('Restart the bridge to apply.');
111+
} else {
112+
stderr.writeln('Update failed: ${result.message}');
113+
exit(1);
114+
}
115+
} finally {
116+
updater.dispose();
117+
}
118+
}
119+
91120
Future<void> _waitForSignal() async {
92121
final completer = Completer<void>();
93122
final sigintSub = ProcessSignal.sigint.watch().listen((_) {

lib/src/models/version.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const bridgeVersion = '1.1.0';

0 commit comments

Comments
 (0)