@neurocode-ai/desktop 1.18.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +19 -0
  3. package/electron-builder.config.test.ts +48 -0
  4. package/electron-builder.config.ts +146 -0
  5. package/electron.vite.config.ts +106 -0
  6. package/icons/README.md +14 -0
  7. package/icons/beta/128x128.png +0 -0
  8. package/icons/beta/128x128@2x.png +0 -0
  9. package/icons/beta/32x32.png +0 -0
  10. package/icons/beta/64x64.png +0 -0
  11. package/icons/beta/Square107x107Logo.png +0 -0
  12. package/icons/beta/Square142x142Logo.png +0 -0
  13. package/icons/beta/Square150x150Logo.png +0 -0
  14. package/icons/beta/Square284x284Logo.png +0 -0
  15. package/icons/beta/Square30x30Logo.png +0 -0
  16. package/icons/beta/Square310x310Logo.png +0 -0
  17. package/icons/beta/Square44x44Logo.png +0 -0
  18. package/icons/beta/Square71x71Logo.png +0 -0
  19. package/icons/beta/Square89x89Logo.png +0 -0
  20. package/icons/beta/StoreLogo.png +0 -0
  21. package/icons/beta/android/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  22. package/icons/beta/android/mipmap-hdpi/ic_launcher.png +0 -0
  23. package/icons/beta/android/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  24. package/icons/beta/android/mipmap-hdpi/ic_launcher_round.png +0 -0
  25. package/icons/beta/android/mipmap-mdpi/ic_launcher.png +0 -0
  26. package/icons/beta/android/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  27. package/icons/beta/android/mipmap-mdpi/ic_launcher_round.png +0 -0
  28. package/icons/beta/android/mipmap-xhdpi/ic_launcher.png +0 -0
  29. package/icons/beta/android/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  30. package/icons/beta/android/mipmap-xhdpi/ic_launcher_round.png +0 -0
  31. package/icons/beta/android/mipmap-xxhdpi/ic_launcher.png +0 -0
  32. package/icons/beta/android/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  33. package/icons/beta/android/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  34. package/icons/beta/android/mipmap-xxxhdpi/ic_launcher.png +0 -0
  35. package/icons/beta/android/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  36. package/icons/beta/android/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  37. package/icons/beta/android/values/ic_launcher_background.xml +4 -0
  38. package/icons/beta/dock.png +0 -0
  39. package/icons/beta/icon.icns +0 -0
  40. package/icons/beta/icon.ico +0 -0
  41. package/icons/beta/icon.png +0 -0
  42. package/icons/beta/ios/AppIcon-20x20@1x.png +0 -0
  43. package/icons/beta/ios/AppIcon-20x20@2x-1.png +0 -0
  44. package/icons/beta/ios/AppIcon-20x20@2x.png +0 -0
  45. package/icons/beta/ios/AppIcon-20x20@3x.png +0 -0
  46. package/icons/beta/ios/AppIcon-29x29@1x.png +0 -0
  47. package/icons/beta/ios/AppIcon-29x29@2x-1.png +0 -0
  48. package/icons/beta/ios/AppIcon-29x29@2x.png +0 -0
  49. package/icons/beta/ios/AppIcon-29x29@3x.png +0 -0
  50. package/icons/beta/ios/AppIcon-40x40@1x.png +0 -0
  51. package/icons/beta/ios/AppIcon-40x40@2x-1.png +0 -0
  52. package/icons/beta/ios/AppIcon-40x40@2x.png +0 -0
  53. package/icons/beta/ios/AppIcon-40x40@3x.png +0 -0
  54. package/icons/beta/ios/AppIcon-512@2x.png +0 -0
  55. package/icons/beta/ios/AppIcon-60x60@2x.png +0 -0
  56. package/icons/beta/ios/AppIcon-60x60@3x.png +0 -0
  57. package/icons/beta/ios/AppIcon-76x76@1x.png +0 -0
  58. package/icons/beta/ios/AppIcon-76x76@2x.png +0 -0
  59. package/icons/beta/ios/AppIcon-83.5x83.5@2x.png +0 -0
  60. package/icons/dev/128x128.png +0 -0
  61. package/icons/dev/128x128@2x.png +0 -0
  62. package/icons/dev/32x32.png +0 -0
  63. package/icons/dev/64x64.png +0 -0
  64. package/icons/dev/Square107x107Logo.png +0 -0
  65. package/icons/dev/Square142x142Logo.png +0 -0
  66. package/icons/dev/Square150x150Logo.png +0 -0
  67. package/icons/dev/Square284x284Logo.png +0 -0
  68. package/icons/dev/Square30x30Logo.png +0 -0
  69. package/icons/dev/Square310x310Logo.png +0 -0
  70. package/icons/dev/Square44x44Logo.png +0 -0
  71. package/icons/dev/Square71x71Logo.png +0 -0
  72. package/icons/dev/Square89x89Logo.png +0 -0
  73. package/icons/dev/StoreLogo.png +0 -0
  74. package/icons/dev/android/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  75. package/icons/dev/android/mipmap-hdpi/ic_launcher.png +0 -0
  76. package/icons/dev/android/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  77. package/icons/dev/android/mipmap-hdpi/ic_launcher_round.png +0 -0
  78. package/icons/dev/android/mipmap-mdpi/ic_launcher.png +0 -0
  79. package/icons/dev/android/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  80. package/icons/dev/android/mipmap-mdpi/ic_launcher_round.png +0 -0
  81. package/icons/dev/android/mipmap-xhdpi/ic_launcher.png +0 -0
  82. package/icons/dev/android/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  83. package/icons/dev/android/mipmap-xhdpi/ic_launcher_round.png +0 -0
  84. package/icons/dev/android/mipmap-xxhdpi/ic_launcher.png +0 -0
  85. package/icons/dev/android/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  86. package/icons/dev/android/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  87. package/icons/dev/android/mipmap-xxxhdpi/ic_launcher.png +0 -0
  88. package/icons/dev/android/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  89. package/icons/dev/android/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  90. package/icons/dev/android/values/ic_launcher_background.xml +4 -0
  91. package/icons/dev/dock.png +0 -0
  92. package/icons/dev/icon.icns +0 -0
  93. package/icons/dev/icon.ico +0 -0
  94. package/icons/dev/icon.png +0 -0
  95. package/icons/dev/ios/AppIcon-20x20@1x.png +0 -0
  96. package/icons/dev/ios/AppIcon-20x20@2x-1.png +0 -0
  97. package/icons/dev/ios/AppIcon-20x20@2x.png +0 -0
  98. package/icons/dev/ios/AppIcon-20x20@3x.png +0 -0
  99. package/icons/dev/ios/AppIcon-29x29@1x.png +0 -0
  100. package/icons/dev/ios/AppIcon-29x29@2x-1.png +0 -0
  101. package/icons/dev/ios/AppIcon-29x29@2x.png +0 -0
  102. package/icons/dev/ios/AppIcon-29x29@3x.png +0 -0
  103. package/icons/dev/ios/AppIcon-40x40@1x.png +0 -0
  104. package/icons/dev/ios/AppIcon-40x40@2x-1.png +0 -0
  105. package/icons/dev/ios/AppIcon-40x40@2x.png +0 -0
  106. package/icons/dev/ios/AppIcon-40x40@3x.png +0 -0
  107. package/icons/dev/ios/AppIcon-512@2x.png +0 -0
  108. package/icons/dev/ios/AppIcon-60x60@2x.png +0 -0
  109. package/icons/dev/ios/AppIcon-60x60@3x.png +0 -0
  110. package/icons/dev/ios/AppIcon-76x76@1x.png +0 -0
  111. package/icons/dev/ios/AppIcon-76x76@2x.png +0 -0
  112. package/icons/dev/ios/AppIcon-83.5x83.5@2x.png +0 -0
  113. package/icons/prod/128x128.png +0 -0
  114. package/icons/prod/128x128@2x.png +0 -0
  115. package/icons/prod/32x32.png +0 -0
  116. package/icons/prod/64x64.png +0 -0
  117. package/icons/prod/Square107x107Logo.png +0 -0
  118. package/icons/prod/Square142x142Logo.png +0 -0
  119. package/icons/prod/Square150x150Logo.png +0 -0
  120. package/icons/prod/Square284x284Logo.png +0 -0
  121. package/icons/prod/Square30x30Logo.png +0 -0
  122. package/icons/prod/Square310x310Logo.png +0 -0
  123. package/icons/prod/Square44x44Logo.png +0 -0
  124. package/icons/prod/Square71x71Logo.png +0 -0
  125. package/icons/prod/Square89x89Logo.png +0 -0
  126. package/icons/prod/StoreLogo.png +0 -0
  127. package/icons/prod/android/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  128. package/icons/prod/android/mipmap-hdpi/ic_launcher.png +0 -0
  129. package/icons/prod/android/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  130. package/icons/prod/android/mipmap-hdpi/ic_launcher_round.png +0 -0
  131. package/icons/prod/android/mipmap-mdpi/ic_launcher.png +0 -0
  132. package/icons/prod/android/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  133. package/icons/prod/android/mipmap-mdpi/ic_launcher_round.png +0 -0
  134. package/icons/prod/android/mipmap-xhdpi/ic_launcher.png +0 -0
  135. package/icons/prod/android/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  136. package/icons/prod/android/mipmap-xhdpi/ic_launcher_round.png +0 -0
  137. package/icons/prod/android/mipmap-xxhdpi/ic_launcher.png +0 -0
  138. package/icons/prod/android/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  139. package/icons/prod/android/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  140. package/icons/prod/android/mipmap-xxxhdpi/ic_launcher.png +0 -0
  141. package/icons/prod/android/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  142. package/icons/prod/android/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  143. package/icons/prod/android/values/ic_launcher_background.xml +4 -0
  144. package/icons/prod/dock.png +0 -0
  145. package/icons/prod/icon.icns +0 -0
  146. package/icons/prod/icon.ico +0 -0
  147. package/icons/prod/icon.png +0 -0
  148. package/icons/prod/ios/AppIcon-20x20@1x.png +0 -0
  149. package/icons/prod/ios/AppIcon-20x20@2x-1.png +0 -0
  150. package/icons/prod/ios/AppIcon-20x20@2x.png +0 -0
  151. package/icons/prod/ios/AppIcon-20x20@3x.png +0 -0
  152. package/icons/prod/ios/AppIcon-29x29@1x.png +0 -0
  153. package/icons/prod/ios/AppIcon-29x29@2x-1.png +0 -0
  154. package/icons/prod/ios/AppIcon-29x29@2x.png +0 -0
  155. package/icons/prod/ios/AppIcon-29x29@3x.png +0 -0
  156. package/icons/prod/ios/AppIcon-40x40@1x.png +0 -0
  157. package/icons/prod/ios/AppIcon-40x40@2x-1.png +0 -0
  158. package/icons/prod/ios/AppIcon-40x40@2x.png +0 -0
  159. package/icons/prod/ios/AppIcon-40x40@3x.png +0 -0
  160. package/icons/prod/ios/AppIcon-512@2x.png +0 -0
  161. package/icons/prod/ios/AppIcon-60x60@2x.png +0 -0
  162. package/icons/prod/ios/AppIcon-60x60@3x.png +0 -0
  163. package/icons/prod/ios/AppIcon-76x76@1x.png +0 -0
  164. package/icons/prod/ios/AppIcon-76x76@2x.png +0 -0
  165. package/icons/prod/ios/AppIcon-83.5x83.5@2x.png +0 -0
  166. package/package.json +75 -0
  167. package/resources/entitlements.plist +18 -0
  168. package/resources/linux/opencode-desktop.desktop +10 -0
  169. package/scripts/copy-bundles.ts +12 -0
  170. package/scripts/copy-icons.ts +12 -0
  171. package/scripts/copy-metainfo.ts +47 -0
  172. package/scripts/finalize-latest-json.ts +219 -0
  173. package/scripts/finalize-latest-yml.ts +124 -0
  174. package/scripts/prebuild.ts +10 -0
  175. package/scripts/predev.ts +5 -0
  176. package/scripts/prepare.ts +9 -0
  177. package/scripts/utils.ts +77 -0
  178. package/src/main/apps.ts +136 -0
  179. package/src/main/attachment-picker.test.ts +87 -0
  180. package/src/main/attachment-picker.ts +56 -0
  181. package/src/main/constants.ts +7 -0
  182. package/src/main/debug.ts +95 -0
  183. package/src/main/desktop-menu-actions.ts +84 -0
  184. package/src/main/env.d.ts +19 -0
  185. package/src/main/index.test.ts +37 -0
  186. package/src/main/index.ts +399 -0
  187. package/src/main/initialization.ts +6 -0
  188. package/src/main/install-state.test.ts +19 -0
  189. package/src/main/install-state.ts +8 -0
  190. package/src/main/ipc.ts +274 -0
  191. package/src/main/logging.ts +205 -0
  192. package/src/main/markdown.ts +16 -0
  193. package/src/main/menu.ts +67 -0
  194. package/src/main/migrate.ts +91 -0
  195. package/src/main/onboarding.ts +45 -0
  196. package/src/main/server.ts +238 -0
  197. package/src/main/shell-env.test.ts +50 -0
  198. package/src/main/shell-env.ts +101 -0
  199. package/src/main/sidecar.ts +157 -0
  200. package/src/main/store-cleanup.test.ts +93 -0
  201. package/src/main/store-cleanup.ts +94 -0
  202. package/src/main/store-keys.ts +7 -0
  203. package/src/main/store.ts +35 -0
  204. package/src/main/unresponsive.ts +70 -0
  205. package/src/main/updater-controller.test.ts +111 -0
  206. package/src/main/updater-controller.ts +97 -0
  207. package/src/main/updater-subscriptions.test.ts +16 -0
  208. package/src/main/updater-subscriptions.ts +20 -0
  209. package/src/main/updater.ts +85 -0
  210. package/src/main/window-registry.test.ts +91 -0
  211. package/src/main/window-registry.ts +47 -0
  212. package/src/main/window-state.ts +29 -0
  213. package/src/main/windows.ts +496 -0
  214. package/src/main/wsl/ipc.ts +103 -0
  215. package/src/main/wsl/policy.ts +33 -0
  216. package/src/main/wsl/runtime.ts +400 -0
  217. package/src/main/wsl/servers.test.ts +231 -0
  218. package/src/main/wsl/servers.ts +521 -0
  219. package/src/main/wsl/sidecar.ts +129 -0
  220. package/src/main/wsl/startup.ts +31 -0
  221. package/src/preload/index.ts +128 -0
  222. package/src/preload/types.ts +109 -0
  223. package/src/renderer/cli.ts +12 -0
  224. package/src/renderer/env.d.ts +10 -0
  225. package/src/renderer/html.test.ts +62 -0
  226. package/src/renderer/i18n/ar.ts +26 -0
  227. package/src/renderer/i18n/br.ts +27 -0
  228. package/src/renderer/i18n/bs.ts +28 -0
  229. package/src/renderer/i18n/da.ts +28 -0
  230. package/src/renderer/i18n/de.ts +28 -0
  231. package/src/renderer/i18n/en.ts +27 -0
  232. package/src/renderer/i18n/es.ts +27 -0
  233. package/src/renderer/i18n/fr.ts +28 -0
  234. package/src/renderer/i18n/index.ts +194 -0
  235. package/src/renderer/i18n/ja.ts +28 -0
  236. package/src/renderer/i18n/ko.ts +27 -0
  237. package/src/renderer/i18n/no.ts +28 -0
  238. package/src/renderer/i18n/pl.ts +28 -0
  239. package/src/renderer/i18n/ru.ts +27 -0
  240. package/src/renderer/i18n/uk.ts +28 -0
  241. package/src/renderer/i18n/zh.ts +26 -0
  242. package/src/renderer/i18n/zht.ts +26 -0
  243. package/src/renderer/index.html +21 -0
  244. package/src/renderer/index.tsx +464 -0
  245. package/src/renderer/initialization.test.ts +73 -0
  246. package/src/renderer/initialization.ts +22 -0
  247. package/src/renderer/onboarding.tsx +53 -0
  248. package/src/renderer/styles.css +0 -0
  249. package/src/renderer/webview-zoom.ts +138 -0
  250. package/src/renderer/wsl/connections.test.ts +43 -0
  251. package/src/renderer/wsl/connections.ts +28 -0
  252. package/sst-env.d.ts +10 -0
  253. package/tsconfig.json +23 -0
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
4
+ <background android:drawable="@color/ic_launcher_background"/>
5
+ </adaptive-icon>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <color name="ic_launcher_background">#fff</color>
4
+ </resources>
Binary file
Binary file
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@neurocode-ai/desktop",
3
+ "version": "1.18.8",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "homepage": "https://opencode.ai",
7
+ "author": {
8
+ "name": "OpenCode",
9
+ "email": "hello@opencode.ai"
10
+ },
11
+ "scripts": {
12
+ "typecheck": "tsgo -b",
13
+ "predev": "bun ./scripts/predev.ts",
14
+ "dev": "electron-vite dev",
15
+ "prebuild": "bun ./scripts/prebuild.ts",
16
+ "build": "electron-vite build",
17
+ "preview": "electron-vite preview",
18
+ "package": "electron-builder --config electron-builder.config.ts",
19
+ "package:mac": "electron-builder --mac --config electron-builder.config.ts",
20
+ "package:win": "electron-builder --win --config electron-builder.config.ts",
21
+ "package:linux": "electron-builder --linux --config electron-builder.config.ts",
22
+ "native:build": "bun install --cwd native"
23
+ },
24
+ "main": "./out/main/index.js",
25
+ "dependencies": {
26
+ "@zip.js/zip.js": "2.7.62",
27
+ "effect": "4.0.0-beta.83",
28
+ "electron-context-menu": "4.1.2",
29
+ "electron-log": "^5",
30
+ "electron-store": "11.0.2",
31
+ "electron-updater": "6.8.9",
32
+ "electron-window-state": "^5.0.3",
33
+ "marked": "^15"
34
+ },
35
+ "devDependencies": {
36
+ "@actions/artifact": "4.0.0",
37
+ "@lydell/node-pty": "1.2.0-beta.12",
38
+ "@neurocode-ai/app": "1.18.8",
39
+ "@neurocode-ai/ui": "1.18.8",
40
+ "@sentry/solid": "10.36.0",
41
+ "@sentry/vite-plugin": "4.6.0",
42
+ "@solid-primitives/i18n": "2.2.1",
43
+ "@solid-primitives/storage": "4.3.3",
44
+ "@solidjs/meta": "0.29.4",
45
+ "@solidjs/router": "0.15.4",
46
+ "@types/bun": "1.3.13",
47
+ "@types/node": "24.12.2",
48
+ "@typescript/native-preview": "7.0.0-dev.20251207.1",
49
+ "@valibot/to-json-schema": "1.6.0",
50
+ "electron": "42.3.3",
51
+ "electron-builder": "26.15.2",
52
+ "electron-vite": "^5",
53
+ "solid-js": "1.9.10",
54
+ "sury": "11.0.0-alpha.4",
55
+ "typescript": "~5.6.2",
56
+ "vite": "7.1.4",
57
+ "zod-openapi": "5.4.6"
58
+ },
59
+ "optionalDependencies": {
60
+ "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12",
61
+ "@lydell/node-pty-darwin-x64": "1.2.0-beta.12",
62
+ "@lydell/node-pty-linux-arm64": "1.2.0-beta.12",
63
+ "@lydell/node-pty-linux-x64": "1.2.0-beta.12",
64
+ "@lydell/node-pty-win32-arm64": "1.2.0-beta.12",
65
+ "@lydell/node-pty-win32-x64": "1.2.0-beta.12",
66
+ "@parcel/watcher-darwin-arm64": "2.5.1",
67
+ "@parcel/watcher-darwin-x64": "2.5.1",
68
+ "@parcel/watcher-linux-arm64-glibc": "2.5.1",
69
+ "@parcel/watcher-linux-arm64-musl": "2.5.1",
70
+ "@parcel/watcher-linux-x64-glibc": "2.5.1",
71
+ "@parcel/watcher-linux-x64-musl": "2.5.1",
72
+ "@parcel/watcher-win32-arm64": "2.5.1",
73
+ "@parcel/watcher-win32-x64": "2.5.1"
74
+ }
75
+ }
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.security.cs.allow-jit</key>
6
+ <true/>
7
+ <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
8
+ <true/>
9
+ <key>com.apple.security.cs.disable-executable-page-protection</key>
10
+ <true/>
11
+ <key>com.apple.security.cs.allow-dyld-environment-variables</key>
12
+ <true/>
13
+ <key>com.apple.security.cs.disable-library-validation</key>
14
+ <true/>
15
+ <key>com.apple.security.device.audio-input</key>
16
+ <true/>
17
+ </dict>
18
+ </plist>
@@ -0,0 +1,10 @@
1
+ [Desktop Entry]
2
+ Name=OpenCode
3
+ Exec=/opt/OpenCode/ai.opencode.desktop %U
4
+ Terminal=false
5
+ Type=Application
6
+ Icon=ai.opencode.desktop
7
+ StartupWMClass=ai.opencode.desktop
8
+ NoDisplay=true
9
+ Comment=Open source AI coding agent
10
+ Categories=Development;
@@ -0,0 +1,12 @@
1
+ import { $ } from "bun"
2
+ import * as path from "node:path"
3
+
4
+ import { RUST_TARGET } from "./utils"
5
+
6
+ if (!RUST_TARGET) throw new Error("RUST_TARGET not defined")
7
+
8
+ const BUNDLE_DIR = "dist"
9
+ const BUNDLES_OUT_DIR = path.join(process.cwd(), "dist/bundles")
10
+
11
+ await $`mkdir -p ${BUNDLES_OUT_DIR}`
12
+ await $`cp -r ${BUNDLE_DIR}/* ${BUNDLES_OUT_DIR}`
@@ -0,0 +1,12 @@
1
+ import { $ } from "bun"
2
+ import { resolveChannel } from "./utils"
3
+
4
+ const arg = process.argv[2]
5
+ const channel = arg === "dev" || arg === "beta" || arg === "prod" ? arg : resolveChannel()
6
+
7
+ const src = `./icons/${channel}`
8
+ const dest = "resources/icons"
9
+
10
+ await $`rm -rf ${dest}`
11
+ await $`cp -R ${src} ${dest}`
12
+ console.log(`Copied ${channel} icons from ${src} to ${dest}`)
@@ -0,0 +1,47 @@
1
+ import { resolveChannel } from "./utils"
2
+
3
+ const arg = process.argv[2]
4
+ const channel = arg === "dev" || arg === "beta" || arg === "prod" ? arg : resolveChannel()
5
+
6
+ const appId = channel === "prod" ? "ai.opencode.desktop" : `ai.opencode.desktop.${channel}`
7
+ const productName = channel === "prod" ? "OpenCode" : `OpenCode ${channel.charAt(0).toUpperCase() + channel.slice(1)}`
8
+ const summary = `Open source AI coding agent${channel !== "prod" ? ` (${channel})` : ""}`
9
+
10
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
11
+ <component type="desktop-application">
12
+ <id>${appId}</id>
13
+
14
+ <metadata_license>CC0-1.0</metadata_license>
15
+ <project_license>MIT</project_license>
16
+
17
+ <name>${productName}</name>
18
+ <summary>${summary}</summary>
19
+
20
+ <developer id="ly.anoma">
21
+ <name>Anomaly Innovations Inc.</name>
22
+ </developer>
23
+
24
+ <description>
25
+ <p>
26
+ OpenCode is an open source agent that helps you write and run code with any AI model.
27
+ </p>
28
+ </description>
29
+
30
+ <launchable type="desktop-id">${appId}.desktop</launchable>
31
+
32
+ <content_rating type="oars-1.1" />
33
+
34
+ <url type="bugtracker">https://github.com/bjlamprea12-wq/NeuroAgent/issues</url>
35
+ <url type="homepage">https://opencode.ai</url>
36
+ <url type="vcs-browser">https://github.com/bjlamprea12-wq/NeuroAgent</url>
37
+
38
+ <screenshots>
39
+ <screenshot type="default">
40
+ <image>https://raw.githubusercontent.com/bjlamprea12-wq/NeuroAgent/b75d4d1c5ec449585d515c756fc81f080a157a9a/packages/web/src/assets/lander/screenshot.png</image>
41
+ </screenshot>
42
+ </screenshots>
43
+ </component>
44
+ `
45
+
46
+ await Bun.write(`resources/${appId}.metainfo.xml`, xml)
47
+ console.log(`Generated metainfo for ${channel} at resources/${appId}.metainfo.xml`)
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { $ } from "bun"
4
+ import path from "node:path"
5
+ import { parseArgs } from "node:util"
6
+
7
+ const { values } = parseArgs({
8
+ args: Bun.argv.slice(2),
9
+ options: {
10
+ "dry-run": { type: "boolean", default: false },
11
+ },
12
+ })
13
+
14
+ const dryRun = values["dry-run"]
15
+
16
+ const repo = process.env.GH_REPO
17
+ if (!repo) throw new Error("GH_REPO is required")
18
+
19
+ const releaseId = process.env.OPENCODE_RELEASE
20
+ if (!releaseId) throw new Error("OPENCODE_RELEASE is required")
21
+
22
+ const version = process.env.OPENCODE_VERSION
23
+ if (!version) throw new Error("OPENCODE_VERSION is required")
24
+
25
+ const dir = process.env.LATEST_YML_DIR
26
+ if (!dir) throw new Error("LATEST_YML_DIR is required")
27
+ const root = dir
28
+
29
+ const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN
30
+ if (!token) throw new Error("GH_TOKEN or GITHUB_TOKEN is required")
31
+
32
+ const rel = await fetch(`https://api.github.com/repos/${repo}/releases/${releaseId}`, {
33
+ headers: {
34
+ Authorization: `token ${token}`,
35
+ Accept: "application/vnd.github+json",
36
+ },
37
+ })
38
+
39
+ if (!rel.ok) {
40
+ throw new Error(`Failed to fetch release: ${rel.status} ${rel.statusText}`)
41
+ }
42
+
43
+ type Asset = {
44
+ name: string
45
+ url: string
46
+ }
47
+
48
+ type Release = {
49
+ assets?: Asset[]
50
+ }
51
+
52
+ const assets = ((await rel.json()) as Release).assets ?? []
53
+ const amap = new Map(assets.map((item) => [item.name, item]))
54
+
55
+ type Item = {
56
+ url: string
57
+ }
58
+
59
+ type Yml = {
60
+ version: string
61
+ files: Item[]
62
+ }
63
+
64
+ function parse(text: string): Yml {
65
+ const lines = text.split("\n")
66
+ let version = ""
67
+ const files: Item[] = []
68
+ let url = ""
69
+
70
+ const flush = () => {
71
+ if (!url) return
72
+ files.push({ url })
73
+ url = ""
74
+ }
75
+
76
+ for (const line of lines) {
77
+ const trim = line.trim()
78
+ if (line.startsWith("version:")) {
79
+ version = line.slice("version:".length).trim()
80
+ continue
81
+ }
82
+ if (trim.startsWith("- url:")) {
83
+ flush()
84
+ url = trim.slice("- url:".length).trim()
85
+ continue
86
+ }
87
+ const indented = line.startsWith(" ") || line.startsWith("\t")
88
+ if (!indented) flush()
89
+ }
90
+ flush()
91
+
92
+ return { version, files }
93
+ }
94
+
95
+ async function read(sub: string, file: string) {
96
+ const item = Bun.file(path.join(root, sub, file))
97
+ if (!(await item.exists())) return undefined
98
+ return parse(await item.text())
99
+ }
100
+
101
+ function pick(list: Item[], exts: string[]) {
102
+ for (const ext of exts) {
103
+ const found = list.find((item) => item.url.split("?")[0]?.toLowerCase().endsWith(ext))
104
+ if (found) return found.url
105
+ }
106
+ }
107
+
108
+ function link(raw: string) {
109
+ if (raw.startsWith("https://") || raw.startsWith("http://")) return raw
110
+ return `https://github.com/${repo}/releases/download/v${version}/${raw}`
111
+ }
112
+
113
+ async function sign(url: string, key: string) {
114
+ const name = decodeURIComponent(new URL(url).pathname.split("/").pop() ?? key)
115
+ const asset = amap.get(name)
116
+ const res = await fetch(asset?.url ?? url, {
117
+ headers: {
118
+ Authorization: `token ${token}`,
119
+ ...(asset ? { Accept: "application/octet-stream" } : {}),
120
+ },
121
+ })
122
+ if (!res.ok) {
123
+ throw new Error(`Failed to fetch file ${name}: ${res.status} ${res.statusText} (${asset?.url ?? url})`)
124
+ }
125
+
126
+ const tmp = process.env.RUNNER_TEMP ?? "/tmp"
127
+ const file = path.join(tmp, name)
128
+ await Bun.write(file, await res.arrayBuffer())
129
+ await $`bunx @tauri-apps/cli signer sign ${file}`
130
+ const sigFile = Bun.file(`${file}.sig`)
131
+ if (!(await sigFile.exists())) throw new Error(`Signature file not found for ${name}`)
132
+ return (await sigFile.text()).trim()
133
+ }
134
+
135
+ const add = async (data: Record<string, { url: string; signature: string }>, key: string, raw: string | undefined) => {
136
+ if (!raw) return
137
+ if (data[key]) return
138
+ const url = link(raw)
139
+ data[key] = { url, signature: await sign(url, key) }
140
+ }
141
+
142
+ const alias = (data: Record<string, { url: string; signature: string }>, key: string, src: string) => {
143
+ if (data[key]) return
144
+ if (!data[src]) return
145
+ data[key] = data[src]
146
+ }
147
+
148
+ const winx = await read("latest-yml-x86_64-pc-windows-msvc", "latest.yml")
149
+ const wina = await read("latest-yml-aarch64-pc-windows-msvc", "latest.yml")
150
+ const macx = await read("latest-yml-x86_64-apple-darwin", "latest-mac.yml")
151
+ const maca = await read("latest-yml-aarch64-apple-darwin", "latest-mac.yml")
152
+ const linx = await read("latest-yml-x86_64-unknown-linux-gnu", "latest-linux.yml")
153
+ const lina = await read("latest-yml-aarch64-unknown-linux-gnu", "latest-linux-arm64.yml")
154
+
155
+ const yver = winx?.version ?? wina?.version ?? macx?.version ?? maca?.version ?? linx?.version ?? lina?.version
156
+ if (yver && yver !== version) throw new Error(`latest.yml version mismatch: expected ${version}, got ${yver}`)
157
+
158
+ const out: Record<string, { url: string; signature: string }> = {}
159
+
160
+ const winxexe = pick(winx?.files ?? [], [".exe"])
161
+ const winaexe = pick(wina?.files ?? [], [".exe"])
162
+
163
+ const macxTarGz = "opencode-desktop-mac-x64.app.tar.gz"
164
+ const macaTarGz = "opencode-desktop-mac-arm64.app.tar.gz"
165
+
166
+ const linxDeb = pick(linx?.files ?? [], [".deb"])
167
+ const linxRpm = pick(linx?.files ?? [], [".rpm"])
168
+ const linxAppImage = pick(linx?.files ?? [], [".appimage"])
169
+ const linaDeb = pick(lina?.files ?? [], [".deb"])
170
+ const linaRpm = pick(lina?.files ?? [], [".rpm"])
171
+ const linaAppImage = pick(lina?.files ?? [], [".appimage"])
172
+
173
+ await add(out, "windows-x86_64-nsis", winxexe)
174
+ await add(out, "windows-aarch64-nsis", winaexe)
175
+ await add(out, "darwin-x86_64-app", macxTarGz)
176
+ await add(out, "darwin-aarch64-app", macaTarGz)
177
+
178
+ await add(out, "linux-x86_64-deb", linxDeb)
179
+ await add(out, "linux-x86_64-rpm", linxRpm)
180
+ await add(out, "linux-x86_64-appimage", linxAppImage)
181
+ await add(out, "linux-aarch64-deb", linaDeb)
182
+ await add(out, "linux-aarch64-rpm", linaRpm)
183
+ await add(out, "linux-aarch64-appimage", linaAppImage)
184
+
185
+ alias(out, "windows-x86_64", "windows-x86_64-nsis")
186
+ alias(out, "windows-aarch64", "windows-aarch64-nsis")
187
+ alias(out, "darwin-x86_64", "darwin-x86_64-app")
188
+ alias(out, "darwin-aarch64", "darwin-aarch64-app")
189
+ alias(out, "linux-x86_64", "linux-x86_64-deb")
190
+ alias(out, "linux-aarch64", "linux-aarch64-deb")
191
+
192
+ const platforms = Object.fromEntries(
193
+ Object.keys(out)
194
+ .sort()
195
+ .map((key) => [key, out[key]]),
196
+ )
197
+
198
+ if (!Object.keys(platforms).length) throw new Error("No updater files found in latest.yml artifacts")
199
+
200
+ const data = {
201
+ version,
202
+ notes: "",
203
+ pub_date: new Date().toISOString(),
204
+ platforms,
205
+ }
206
+
207
+ const tmp = process.env.RUNNER_TEMP ?? "/tmp"
208
+ const file = path.join(tmp, "latest.json")
209
+ await Bun.write(file, JSON.stringify(data, null, 2))
210
+
211
+ const tag = `v${version}`
212
+
213
+ if (dryRun) {
214
+ console.log(`dry-run: wrote latest.json for ${tag} to ${file}`)
215
+ process.exit(0)
216
+ }
217
+ await $`gh release upload ${tag} ${file} --clobber --repo ${repo}`
218
+
219
+ console.log(`finalized latest.json for ${tag}`)
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { $ } from "bun"
4
+ import path from "path"
5
+
6
+ const dir = process.env.LATEST_YML_DIR!
7
+ if (!dir) throw new Error("LATEST_YML_DIR is required")
8
+
9
+ const repo = process.env.GH_REPO
10
+ if (!repo) throw new Error("GH_REPO is required")
11
+
12
+ const version = process.env.OPENCODE_VERSION
13
+ if (!version) throw new Error("OPENCODE_VERSION is required")
14
+
15
+ type FileEntry = {
16
+ url: string
17
+ sha512: string
18
+ size: number
19
+ blockMapSize?: number
20
+ }
21
+
22
+ type LatestYml = {
23
+ version: string
24
+ files: FileEntry[]
25
+ releaseDate: string
26
+ }
27
+
28
+ function parse(content: string): LatestYml {
29
+ const lines = content.split("\n")
30
+ let version = ""
31
+ let releaseDate = ""
32
+ const files: FileEntry[] = []
33
+ let current: Partial<FileEntry> | undefined
34
+
35
+ const flush = () => {
36
+ if (current?.url && current.sha512 && current.size) files.push(current as FileEntry)
37
+ current = undefined
38
+ }
39
+
40
+ for (const line of lines) {
41
+ const indented = line.startsWith(" ") || line.startsWith(" -")
42
+ if (line.startsWith("version:")) version = line.slice("version:".length).trim()
43
+ else if (line.startsWith("releaseDate:"))
44
+ releaseDate = line.slice("releaseDate:".length).trim().replace(/^'|'$/g, "")
45
+ else if (line.trim().startsWith("- url:")) {
46
+ flush()
47
+ current = { url: line.trim().slice("- url:".length).trim() }
48
+ } else if (indented && current && line.trim().startsWith("sha512:"))
49
+ current.sha512 = line.trim().slice("sha512:".length).trim()
50
+ else if (indented && current && line.trim().startsWith("size:"))
51
+ current.size = Number(line.trim().slice("size:".length).trim())
52
+ else if (indented && current && line.trim().startsWith("blockMapSize:"))
53
+ current.blockMapSize = Number(line.trim().slice("blockMapSize:".length).trim())
54
+ else if (!indented && current) flush()
55
+ }
56
+ flush()
57
+
58
+ return { version, files, releaseDate }
59
+ }
60
+
61
+ function serialize(data: LatestYml) {
62
+ const lines = [`version: ${data.version}`, "files:"]
63
+ for (const file of data.files) {
64
+ lines.push(` - url: ${file.url}`)
65
+ lines.push(` sha512: ${file.sha512}`)
66
+ lines.push(` size: ${file.size}`)
67
+ if (file.blockMapSize) lines.push(` blockMapSize: ${file.blockMapSize}`)
68
+ }
69
+ lines.push(`releaseDate: '${data.releaseDate}'`)
70
+ return lines.join("\n") + "\n"
71
+ }
72
+
73
+ async function read(subdir: string, filename: string): Promise<LatestYml | undefined> {
74
+ const file = Bun.file(path.join(dir, subdir, filename))
75
+ if (!(await file.exists())) return undefined
76
+ return parse(await file.text())
77
+ }
78
+
79
+ const output: Record<string, string> = {}
80
+
81
+ // Windows: merge arm64 + x64 into single file
82
+ const winX64 = await read("latest-yml-x86_64-pc-windows-msvc", "latest.yml")
83
+ const winArm64 = await read("latest-yml-aarch64-pc-windows-msvc", "latest.yml")
84
+ if (winX64 || winArm64) {
85
+ const base = winArm64 ?? winX64!
86
+ output["latest.yml"] = serialize({
87
+ version: base.version,
88
+ files: [...(winArm64?.files ?? []), ...(winX64?.files ?? [])],
89
+ releaseDate: base.releaseDate,
90
+ })
91
+ }
92
+
93
+ // Linux x64: pass through
94
+ const linuxX64 = await read("latest-yml-x86_64-unknown-linux-gnu", "latest-linux.yml")
95
+ if (linuxX64) output["latest-linux.yml"] = serialize(linuxX64)
96
+
97
+ // Linux arm64: pass through
98
+ const linuxArm64 = await read("latest-yml-aarch64-unknown-linux-gnu", "latest-linux-arm64.yml")
99
+ if (linuxArm64) output["latest-linux-arm64.yml"] = serialize(linuxArm64)
100
+
101
+ // macOS: merge arm64 + x64 into single file
102
+ const macX64 = await read("latest-yml-x86_64-apple-darwin", "latest-mac.yml")
103
+ const macArm64 = await read("latest-yml-aarch64-apple-darwin", "latest-mac.yml")
104
+ if (macX64 || macArm64) {
105
+ const base = macArm64 ?? macX64!
106
+ output["latest-mac.yml"] = serialize({
107
+ version: base.version,
108
+ files: [...(macArm64?.files ?? []), ...(macX64?.files ?? [])],
109
+ releaseDate: base.releaseDate,
110
+ })
111
+ }
112
+
113
+ // Upload to release
114
+ const tag = `v${version}`
115
+ const tmp = process.env.RUNNER_TEMP ?? "/tmp"
116
+
117
+ for (const [filename, content] of Object.entries(output)) {
118
+ const filepath = path.join(tmp, filename)
119
+ await Bun.write(filepath, content)
120
+ await $`gh release upload ${tag} ${filepath} --clobber --repo ${repo}`
121
+ console.log(`uploaded ${filename}`)
122
+ }
123
+
124
+ console.log("finalized latest yml files")