Skip to content

Commit 101f09d

Browse files
authored
Add single-file executable using bun (agentclientprotocol#332)
This adds a single-file executable using bun. The trick is to use a separate flag for ACP `--acp` and spawn the regular `cli.js` bundled by the Agent SDK if no flags are passed. This was, we can pass ourselves as pathToClaudeCodeExecutable.
1 parent d36204f commit 101f09d

4 files changed

Lines changed: 119 additions & 9 deletions

File tree

.github/workflows/publish.yml

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,81 @@
1-
name: Publish Package to npmjs
1+
name: Publish and Release
22
on:
33
push:
44
tags:
55
- "v*.*.*"
6+
workflow_dispatch:
7+
68
jobs:
9+
build-binaries:
10+
name: Build (${{ matrix.target }})
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
timeout-minutes: 15
15+
16+
strategy:
17+
fail-fast: true
18+
matrix:
19+
include:
20+
- target: bun-linux-x64
21+
artifact: claude-agent-acp-linux-x64
22+
archive: claude-agent-acp-linux-x64.tar.gz
23+
- target: bun-linux-arm64
24+
artifact: claude-agent-acp-linux-arm64
25+
archive: claude-agent-acp-linux-arm64.tar.gz
26+
- target: bun-linux-x64-musl
27+
artifact: claude-agent-acp-linux-x64-musl
28+
archive: claude-agent-acp-linux-x64-musl.tar.gz
29+
- target: bun-linux-arm64-musl
30+
artifact: claude-agent-acp-linux-arm64-musl
31+
archive: claude-agent-acp-linux-arm64-musl.tar.gz
32+
- target: bun-darwin-x64
33+
artifact: claude-agent-acp-darwin-x64
34+
archive: claude-agent-acp-darwin-x64.zip
35+
- target: bun-darwin-arm64
36+
artifact: claude-agent-acp-darwin-arm64
37+
archive: claude-agent-acp-darwin-arm64.zip
38+
- target: bun-windows-x64
39+
artifact: claude-agent-acp-windows-x64.exe
40+
archive: claude-agent-acp-windows-x64.zip
41+
42+
steps:
43+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
44+
45+
- uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3
46+
with:
47+
bun-version: latest
48+
49+
- name: Install dependencies
50+
run: bun install
51+
52+
- name: Build binary
53+
run: |
54+
bun build src/static-entry.ts \
55+
--compile \
56+
--target ${{ matrix.target }} \
57+
--define 'process.env.CLAUDE_AGENT_ACP_IS_SINGLE_FILE_BUN="true"' \
58+
--outfile ${{ matrix.artifact }}
59+
60+
- name: Compress binary
61+
run: |
62+
if [[ "${{ matrix.archive }}" == *.zip ]]; then
63+
zip ${{ matrix.archive }} ${{ matrix.artifact }}
64+
else
65+
tar czf ${{ matrix.archive }} ${{ matrix.artifact }}
66+
fi
67+
68+
- name: Upload artifact
69+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
70+
with:
71+
name: ${{ matrix.archive }}
72+
path: ${{ matrix.archive }}
73+
774
publish-npm:
75+
name: Publish to npm
876
runs-on: ubuntu-latest
77+
needs: [build-binaries]
78+
if: startsWith(github.ref, 'refs/tags/')
979
environment: release # Optional: for enhanced security
1080
permissions:
1181
contents: read
@@ -22,15 +92,26 @@ jobs:
2292
- run: npm ci
2393
- run: npm run build
2494
- run: npm publish
95+
2596
release:
97+
name: Create Release
2698
runs-on: ubuntu-latest
27-
needs: [publish-npm]
99+
needs: [build-binaries, publish-npm]
100+
if: startsWith(github.ref, 'refs/tags/')
28101
permissions:
29102
contents: write
103+
30104
steps:
31105
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
106+
107+
- name: Download all artifacts
108+
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
109+
with:
110+
path: artifacts
111+
merge-multiple: true
112+
32113
- name: Create Release
33-
id: create_release
34114
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
35115
with:
36116
generate_release_notes: true
117+
files: artifacts/*

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ You can then use `claude-agent-acp` as a regular ACP agent:
4848
ANTHROPIC_API_KEY=sk-... claude-agent-acp
4949
```
5050

51+
#### Single-file executable
52+
53+
Pre-built single-file binaries are available on the [Releases](https://github.com/zed-industries/claude-agent-acp/releases) page for Linux, macOS, and Windows.
54+
These binaries bundle everything needed and don't require Node.js.
55+
5156
## License
5257

5358
Apache-2.0

src/acp-agent.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ export type ToolUseCache = {
230230
};
231231
};
232232

233+
function isStaticBinary(): boolean {
234+
return process.env.CLAUDE_AGENT_ACP_IS_SINGLE_FILE_BUN !== undefined;
235+
}
236+
233237
// Bypass Permissions doesn't work if we are a root/sudo user
234238
const IS_ROOT = (process.geteuid?.() ?? process.getuid?.()) === 0;
235239
const ALLOW_BYPASS = !IS_ROOT || !!process.env.IS_SANDBOX;
@@ -264,12 +268,22 @@ export class ClaudeAcpAgent implements Agent {
264268

265269
// If client supports terminal-auth capability, use that instead.
266270
if (request.clientCapabilities?._meta?.["terminal-auth"] === true) {
267-
const cliPath = fileURLToPath(import.meta.resolve("@anthropic-ai/claude-agent-sdk/cli.js"));
271+
let command: string;
272+
let args: string[];
273+
274+
if (isStaticBinary()) {
275+
command = process.execPath;
276+
args = ["--cli"];
277+
} else {
278+
const cliPath = fileURLToPath(import.meta.resolve("@anthropic-ai/claude-agent-sdk/cli.js"));
279+
command = "node";
280+
args = [cliPath];
281+
}
268282

269283
authMethod._meta = {
270284
"terminal-auth": {
271-
command: "node",
272-
args: [cliPath],
285+
command,
286+
args,
273287
label: "Claude Login",
274288
},
275289
};
@@ -1231,9 +1245,12 @@ export class ClaudeAcpAgent implements Agent {
12311245
// note: although not documented by the types, passing an absolute path
12321246
// here works to find zed's managed node version.
12331247
executable: process.execPath as any,
1234-
...(process.env.CLAUDE_CODE_EXECUTABLE && {
1235-
pathToClaudeCodeExecutable: process.env.CLAUDE_CODE_EXECUTABLE,
1236-
}),
1248+
executableArgs: isStaticBinary() ? ["--cli"] : undefined,
1249+
...(process.env.CLAUDE_CODE_EXECUTABLE
1250+
? { pathToClaudeCodeExecutable: process.env.CLAUDE_CODE_EXECUTABLE }
1251+
: isStaticBinary()
1252+
? { pathToClaudeCodeExecutable: process.execPath }
1253+
: {}),
12371254
disallowedTools,
12381255
tools: { type: "preset", preset: "claude_code" },
12391256
hooks: {

src/static-entry.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
if (process.argv.includes("--cli")) {
2+
process.argv = process.argv.filter((arg) => arg !== "--cli");
3+
// @ts-expect-error -- no types
4+
await import("@anthropic-ai/claude-agent-sdk/cli.js");
5+
} else {
6+
await import("./index.js");
7+
}

0 commit comments

Comments
 (0)