1{
2 lib,
3 stdenv,
4 stdenvNoCC,
5 buildGoModule,
6 bun,
7 fetchFromGitHub,
8 makeBinaryWrapper,
9 models-dev,
10 nix-update-script,
11 testers,
12 writableTmpDirAsHomeHook,
13}:
14
15let
16 bun-target = {
17 "aarch64-darwin" = "bun-darwin-arm64";
18 "aarch64-linux" = "bun-linux-arm64";
19 "x86_64-darwin" = "bun-darwin-x64";
20 "x86_64-linux" = "bun-linux-x64";
21 };
22in
23stdenvNoCC.mkDerivation (finalAttrs: {
24 pname = "opencode";
25 version = "0.14.0";
26 src = fetchFromGitHub {
27 owner = "sst";
28 repo = "opencode";
29 tag = "v${finalAttrs.version}";
30 hash = "sha256-w+tYMt+Ucin02GuT7ET4UKFgktay5q5Wywsr+vQgLlo=";
31 };
32
33 tui = buildGoModule {
34 pname = "opencode-tui";
35 inherit (finalAttrs) version src;
36
37 modRoot = "packages/tui";
38
39 vendorHash = "sha256-H+TybeyyHTbhvTye0PCDcsWkcN8M34EJ2ddxyXEJkZI=";
40
41 subPackages = [ "cmd/opencode" ];
42
43 env.CGO_ENABLED = 0;
44
45 ldflags = [
46 "-s"
47 "-X=main.Version=${finalAttrs.version}"
48 ];
49
50 installPhase = ''
51 runHook preInstall
52
53 install -Dm755 $GOPATH/bin/opencode $out/bin/tui
54
55 runHook postInstall
56 '';
57 };
58
59 node_modules = stdenvNoCC.mkDerivation {
60 pname = "opencode-node_modules";
61 inherit (finalAttrs) version src;
62
63 impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
64 "GIT_PROXY_COMMAND"
65 "SOCKS_SERVER"
66 ];
67
68 nativeBuildInputs = [
69 bun
70 writableTmpDirAsHomeHook
71 ];
72
73 dontConfigure = true;
74
75 buildPhase = ''
76 runHook preBuild
77
78 export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
79
80 # Disable post-install scripts to avoid shebang issues
81 bun install \
82 --filter=opencode \
83 --force \
84 --ignore-scripts \
85 --no-progress
86 # Remove `--frozen-lockfile` and `--production` — they erroneously report the lockfile needs updating even though `bun install` does not change it.
87 # Related to https://github.com/oven-sh/bun/issues/19088
88 # --frozen-lockfile \
89 # --production
90
91 runHook postBuild
92 '';
93
94 installPhase = ''
95 runHook preInstall
96
97 mkdir -p $out/node_modules
98 cp -R ./node_modules $out
99
100 runHook postInstall
101 '';
102
103 # Required else we get errors that our fixed-output derivation references store paths
104 dontFixup = true;
105
106 outputHash =
107 {
108 x86_64-linux = "sha256-ZIwhqdwlUO61ZXDwgjvKTJAB312pJG8bfbIHJcjdX1w=";
109 aarch64-linux = "sha256-D0lw7fPlk86AXlAO6OCydTvUrDrQ/eWm1Q3QSApL5ic=";
110 x86_64-darwin = "sha256-tUwMQv1z0oJpr0S5n+aaaQPOoAQVqnaS7SHuTdWzrPA=";
111 aarch64-darwin = "sha256-KoZd2Plm5lnDCebMZdtCJuy1qwfFJYbeeY8ttIw7oc4=";
112 }
113 .${stdenv.hostPlatform.system};
114 outputHashAlgo = "sha256";
115 outputHashMode = "recursive";
116 };
117
118 nativeBuildInputs = [
119 bun
120 makeBinaryWrapper
121 models-dev
122 ];
123
124 patches = [
125 # Patch `packages/opencode/src/provider/models-macro.ts` to get contents of
126 # `_api.json` from the file bundled with `bun build`.
127 ./local-models-dev.patch
128 ];
129
130 configurePhase = ''
131 runHook preConfigure
132
133 cp -R ${finalAttrs.node_modules}/node_modules .
134
135 runHook postConfigure
136 '';
137
138 env.MODELS_DEV_API_JSON = "${models-dev}/dist/_api.json";
139
140 buildPhase = ''
141 runHook preBuild
142
143 bun build \
144 --define OPENCODE_TUI_PATH="'${finalAttrs.tui}/bin/tui'" \
145 --define OPENCODE_VERSION="'${finalAttrs.version}'" \
146 --compile \
147 --target=${bun-target.${stdenvNoCC.hostPlatform.system}} \
148 --outfile=opencode \
149 ./packages/opencode/src/index.ts \
150
151 runHook postBuild
152 '';
153
154 dontStrip = true;
155
156 installPhase = ''
157 runHook preInstall
158
159 install -Dm755 opencode $out/bin/opencode
160
161 runHook postInstall
162 '';
163
164 # Execution of commands using bash-tool fail on linux with
165 # Error [ERR_DLOPEN_FAILED]: libstdc++.so.6: cannot open shared object file: No such
166 # file or directory
167 # Thus, we add libstdc++.so.6 manually to LD_LIBRARY_PATH
168 postFixup = ''
169 wrapProgram $out/bin/opencode \
170 --set LD_LIBRARY_PATH "${lib.makeLibraryPath [ stdenv.cc.cc.lib ]}"
171 '';
172
173 passthru = {
174 tests.version = testers.testVersion {
175 package = finalAttrs.finalPackage;
176 command = "HOME=$(mktemp -d) opencode --version";
177 inherit (finalAttrs) version;
178 };
179 updateScript = nix-update-script {
180 extraArgs = [
181 "--subpackage"
182 "tui"
183 "--subpackage"
184 "node_modules"
185 ];
186 };
187 };
188
189 meta = {
190 description = "AI coding agent built for the terminal";
191 longDescription = ''
192 OpenCode is a terminal-based agent that can build anything.
193 It combines a TypeScript/JavaScript core with a Go-based TUI
194 to provide an interactive AI coding experience.
195 '';
196 homepage = "https://github.com/sst/opencode";
197 license = lib.licenses.mit;
198 platforms = lib.platforms.unix;
199 maintainers = with lib.maintainers; [
200 zestsystem
201 delafthi
202 ];
203 mainProgram = "opencode";
204 };
205})