rird 1.0.200

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 (350) hide show
  1. package/AGENTS.md +27 -0
  2. package/Dockerfile +18 -0
  3. package/README.md +15 -0
  4. package/bin/opencode +336 -0
  5. package/bin/pty-wrapper.js +285 -0
  6. package/bunfig.toml +4 -0
  7. package/facebook_ads_library.png +0 -0
  8. package/nul`nif +0 -0
  9. package/package.json +111 -0
  10. package/parsers-config.ts +239 -0
  11. package/rird-1.0.199.tgz +0 -0
  12. package/script/build-windows.ts +54 -0
  13. package/script/build.ts +167 -0
  14. package/script/postinstall.mjs +544 -0
  15. package/script/publish-registries.ts +187 -0
  16. package/script/publish.ts +72 -0
  17. package/script/schema.ts +47 -0
  18. package/src/acp/README.md +164 -0
  19. package/src/acp/agent.ts +1063 -0
  20. package/src/acp/session.ts +101 -0
  21. package/src/acp/types.ts +22 -0
  22. package/src/agent/agent.ts +367 -0
  23. package/src/agent/generate.txt +75 -0
  24. package/src/agent/prompt/compaction.txt +12 -0
  25. package/src/agent/prompt/explore.txt +18 -0
  26. package/src/agent/prompt/summary.txt +10 -0
  27. package/src/agent/prompt/title.txt +36 -0
  28. package/src/auth/index.ts +70 -0
  29. package/src/bun/index.ts +114 -0
  30. package/src/bus/bus-event.ts +43 -0
  31. package/src/bus/global.ts +10 -0
  32. package/src/bus/index.ts +105 -0
  33. package/src/cli/bootstrap.ts +17 -0
  34. package/src/cli/cmd/acp.ts +88 -0
  35. package/src/cli/cmd/agent.ts +256 -0
  36. package/src/cli/cmd/auth.ts +391 -0
  37. package/src/cli/cmd/cmd.ts +7 -0
  38. package/src/cli/cmd/debug/config.ts +15 -0
  39. package/src/cli/cmd/debug/file.ts +91 -0
  40. package/src/cli/cmd/debug/index.ts +43 -0
  41. package/src/cli/cmd/debug/lsp.ts +48 -0
  42. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  43. package/src/cli/cmd/debug/scrap.ts +15 -0
  44. package/src/cli/cmd/debug/skill.ts +15 -0
  45. package/src/cli/cmd/debug/snapshot.ts +48 -0
  46. package/src/cli/cmd/export.ts +88 -0
  47. package/src/cli/cmd/generate.ts +38 -0
  48. package/src/cli/cmd/github.ts +1400 -0
  49. package/src/cli/cmd/import.ts +98 -0
  50. package/src/cli/cmd/mcp.ts +654 -0
  51. package/src/cli/cmd/models.ts +77 -0
  52. package/src/cli/cmd/pr.ts +112 -0
  53. package/src/cli/cmd/run.ts +368 -0
  54. package/src/cli/cmd/serve.ts +31 -0
  55. package/src/cli/cmd/session.ts +106 -0
  56. package/src/cli/cmd/stats.ts +298 -0
  57. package/src/cli/cmd/tui/app.tsx +696 -0
  58. package/src/cli/cmd/tui/attach.ts +30 -0
  59. package/src/cli/cmd/tui/component/border.tsx +21 -0
  60. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  61. package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
  62. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  63. package/src/cli/cmd/tui/component/dialog-model.tsx +245 -0
  64. package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
  65. package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
  66. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  67. package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
  68. package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
  69. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  70. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  71. package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
  72. package/src/cli/cmd/tui/component/logo.tsx +35 -0
  73. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
  74. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  75. package/src/cli/cmd/tui/component/prompt/index.tsx +1090 -0
  76. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  77. package/src/cli/cmd/tui/component/tips.ts +27 -0
  78. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  79. package/src/cli/cmd/tui/context/args.tsx +14 -0
  80. package/src/cli/cmd/tui/context/directory.ts +13 -0
  81. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  82. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  83. package/src/cli/cmd/tui/context/keybind.tsx +101 -0
  84. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  85. package/src/cli/cmd/tui/context/local.tsx +354 -0
  86. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  87. package/src/cli/cmd/tui/context/route.tsx +46 -0
  88. package/src/cli/cmd/tui/context/sdk.tsx +74 -0
  89. package/src/cli/cmd/tui/context/sync.tsx +372 -0
  90. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  91. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  92. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  93. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  94. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  95. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  96. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  97. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  98. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  99. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  100. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  101. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  102. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  103. package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
  104. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  105. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  106. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  107. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  108. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  109. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  110. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  111. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  112. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  113. package/src/cli/cmd/tui/context/theme/rird.json +245 -0
  114. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  115. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  116. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  117. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  118. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  119. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  120. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  121. package/src/cli/cmd/tui/context/theme.tsx +1109 -0
  122. package/src/cli/cmd/tui/event.ts +40 -0
  123. package/src/cli/cmd/tui/routes/home.tsx +138 -0
  124. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
  125. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
  126. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  127. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  128. package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
  129. package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
  130. package/src/cli/cmd/tui/routes/session/index.tsx +1864 -0
  131. package/src/cli/cmd/tui/routes/session/sidebar.tsx +318 -0
  132. package/src/cli/cmd/tui/spawn.ts +60 -0
  133. package/src/cli/cmd/tui/thread.ts +142 -0
  134. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  135. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  136. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  137. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
  138. package/src/cli/cmd/tui/ui/dialog-select.tsx +332 -0
  139. package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
  140. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  141. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  142. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  143. package/src/cli/cmd/tui/util/editor.ts +32 -0
  144. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  145. package/src/cli/cmd/tui/worker.ts +63 -0
  146. package/src/cli/cmd/uninstall.ts +344 -0
  147. package/src/cli/cmd/upgrade.ts +100 -0
  148. package/src/cli/cmd/web.ts +84 -0
  149. package/src/cli/error.ts +56 -0
  150. package/src/cli/ui.ts +84 -0
  151. package/src/cli/upgrade.ts +25 -0
  152. package/src/command/index.ts +80 -0
  153. package/src/command/template/initialize.txt +10 -0
  154. package/src/command/template/review.txt +97 -0
  155. package/src/config/config.ts +995 -0
  156. package/src/config/markdown.ts +41 -0
  157. package/src/env/index.ts +26 -0
  158. package/src/file/ignore.ts +83 -0
  159. package/src/file/index.ts +328 -0
  160. package/src/file/ripgrep.ts +393 -0
  161. package/src/file/time.ts +64 -0
  162. package/src/file/watcher.ts +103 -0
  163. package/src/flag/flag.ts +46 -0
  164. package/src/format/formatter.ts +315 -0
  165. package/src/format/index.ts +137 -0
  166. package/src/global/index.ts +52 -0
  167. package/src/id/id.ts +73 -0
  168. package/src/ide/index.ts +76 -0
  169. package/src/index.ts +240 -0
  170. package/src/installation/index.ts +239 -0
  171. package/src/lsp/client.ts +229 -0
  172. package/src/lsp/index.ts +485 -0
  173. package/src/lsp/language.ts +116 -0
  174. package/src/lsp/server.ts +1895 -0
  175. package/src/mcp/auth.ts +135 -0
  176. package/src/mcp/index.ts +690 -0
  177. package/src/mcp/oauth-callback.ts +200 -0
  178. package/src/mcp/oauth-provider.ts +154 -0
  179. package/src/patch/index.ts +622 -0
  180. package/src/permission/index.ts +199 -0
  181. package/src/plugin/index.ts +91 -0
  182. package/src/project/bootstrap.ts +31 -0
  183. package/src/project/instance.ts +78 -0
  184. package/src/project/project.ts +221 -0
  185. package/src/project/state.ts +65 -0
  186. package/src/project/vcs.ts +76 -0
  187. package/src/provider/auth.ts +143 -0
  188. package/src/provider/models-macro.ts +11 -0
  189. package/src/provider/models.ts +106 -0
  190. package/src/provider/provider.ts +1071 -0
  191. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  192. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  193. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  194. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  195. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  196. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  197. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  198. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  199. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  200. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  201. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  202. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  203. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  204. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  205. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  206. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  207. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  208. package/src/provider/transform.ts +455 -0
  209. package/src/pty/index.ts +231 -0
  210. package/src/security/guardrails.test.ts +341 -0
  211. package/src/security/guardrails.ts +558 -0
  212. package/src/security/index.ts +19 -0
  213. package/src/server/error.ts +36 -0
  214. package/src/server/project.ts +79 -0
  215. package/src/server/server.ts +2642 -0
  216. package/src/server/tui.ts +71 -0
  217. package/src/session/compaction.ts +223 -0
  218. package/src/session/index.ts +461 -0
  219. package/src/session/llm.ts +201 -0
  220. package/src/session/message-v2.ts +690 -0
  221. package/src/session/message.ts +189 -0
  222. package/src/session/processor.ts +409 -0
  223. package/src/session/prompt/act-switch.txt +5 -0
  224. package/src/session/prompt/anthropic-20250930.txt +166 -0
  225. package/src/session/prompt/anthropic.txt +85 -0
  226. package/src/session/prompt/anthropic_spoof.txt +1 -0
  227. package/src/session/prompt/beast.txt +103 -0
  228. package/src/session/prompt/codex.txt +304 -0
  229. package/src/session/prompt/copilot-gpt-5.txt +138 -0
  230. package/src/session/prompt/gemini.txt +85 -0
  231. package/src/session/prompt/max-steps.txt +16 -0
  232. package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
  233. package/src/session/prompt/plan.txt +24 -0
  234. package/src/session/prompt/polaris.txt +84 -0
  235. package/src/session/prompt/qwen.txt +106 -0
  236. package/src/session/prompt.ts +1509 -0
  237. package/src/session/retry.ts +86 -0
  238. package/src/session/revert.ts +108 -0
  239. package/src/session/sensitive-filter.test.ts +327 -0
  240. package/src/session/sensitive-filter.ts +466 -0
  241. package/src/session/status.ts +76 -0
  242. package/src/session/summary.ts +194 -0
  243. package/src/session/system.ts +120 -0
  244. package/src/session/todo.ts +37 -0
  245. package/src/share/share-next.ts +194 -0
  246. package/src/share/share.ts +87 -0
  247. package/src/shell/shell.ts +67 -0
  248. package/src/skill/index.ts +1 -0
  249. package/src/skill/skill.ts +83 -0
  250. package/src/snapshot/index.ts +197 -0
  251. package/src/storage/storage.ts +226 -0
  252. package/src/tests/agent.test.ts +308 -0
  253. package/src/tests/build-guards.test.ts +267 -0
  254. package/src/tests/config.test.ts +664 -0
  255. package/src/tests/tool-registry.test.ts +589 -0
  256. package/src/tool/bash.ts +317 -0
  257. package/src/tool/bash.txt +158 -0
  258. package/src/tool/batch.ts +175 -0
  259. package/src/tool/batch.txt +24 -0
  260. package/src/tool/codesearch.ts +168 -0
  261. package/src/tool/codesearch.txt +12 -0
  262. package/src/tool/edit.ts +675 -0
  263. package/src/tool/edit.txt +10 -0
  264. package/src/tool/glob.ts +65 -0
  265. package/src/tool/glob.txt +6 -0
  266. package/src/tool/grep.ts +121 -0
  267. package/src/tool/grep.txt +8 -0
  268. package/src/tool/invalid.ts +17 -0
  269. package/src/tool/ls.ts +110 -0
  270. package/src/tool/ls.txt +1 -0
  271. package/src/tool/lsp-diagnostics.ts +26 -0
  272. package/src/tool/lsp-diagnostics.txt +1 -0
  273. package/src/tool/lsp-hover.ts +31 -0
  274. package/src/tool/lsp-hover.txt +1 -0
  275. package/src/tool/lsp.ts +87 -0
  276. package/src/tool/lsp.txt +19 -0
  277. package/src/tool/multiedit.ts +46 -0
  278. package/src/tool/multiedit.txt +41 -0
  279. package/src/tool/patch.ts +233 -0
  280. package/src/tool/patch.txt +1 -0
  281. package/src/tool/read.ts +219 -0
  282. package/src/tool/read.txt +12 -0
  283. package/src/tool/registry.ts +162 -0
  284. package/src/tool/skill.ts +100 -0
  285. package/src/tool/task.ts +136 -0
  286. package/src/tool/task.txt +51 -0
  287. package/src/tool/todo.ts +39 -0
  288. package/src/tool/todoread.txt +14 -0
  289. package/src/tool/todowrite.txt +167 -0
  290. package/src/tool/tool.ts +71 -0
  291. package/src/tool/webfetch.ts +198 -0
  292. package/src/tool/webfetch.txt +13 -0
  293. package/src/tool/websearch.ts +180 -0
  294. package/src/tool/websearch.txt +11 -0
  295. package/src/tool/write.ts +110 -0
  296. package/src/tool/write.txt +8 -0
  297. package/src/util/archive.ts +16 -0
  298. package/src/util/color.ts +19 -0
  299. package/src/util/context.ts +25 -0
  300. package/src/util/defer.ts +12 -0
  301. package/src/util/eventloop.ts +20 -0
  302. package/src/util/filesystem.ts +83 -0
  303. package/src/util/fn.ts +11 -0
  304. package/src/util/iife.ts +3 -0
  305. package/src/util/keybind.ts +102 -0
  306. package/src/util/lazy.ts +11 -0
  307. package/src/util/license.ts +325 -0
  308. package/src/util/locale.ts +81 -0
  309. package/src/util/lock.ts +98 -0
  310. package/src/util/log.ts +180 -0
  311. package/src/util/queue.ts +32 -0
  312. package/src/util/rpc.ts +42 -0
  313. package/src/util/scrap.ts +10 -0
  314. package/src/util/signal.ts +12 -0
  315. package/src/util/timeout.ts +14 -0
  316. package/src/util/token.ts +7 -0
  317. package/src/util/wildcard.ts +54 -0
  318. package/sst-env.d.ts +9 -0
  319. package/test/agent/agent.test.ts +146 -0
  320. package/test/bun.test.ts +53 -0
  321. package/test/cli/github-remote.test.ts +80 -0
  322. package/test/config/agent-color.test.ts +66 -0
  323. package/test/config/config.test.ts +535 -0
  324. package/test/config/markdown.test.ts +89 -0
  325. package/test/file/ignore.test.ts +10 -0
  326. package/test/fixture/fixture.ts +36 -0
  327. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  328. package/test/ide/ide.test.ts +82 -0
  329. package/test/keybind.test.ts +421 -0
  330. package/test/lsp/client.test.ts +95 -0
  331. package/test/mcp/headers.test.ts +153 -0
  332. package/test/patch/patch.test.ts +348 -0
  333. package/test/preload.ts +57 -0
  334. package/test/project/project.test.ts +72 -0
  335. package/test/provider/provider.test.ts +1809 -0
  336. package/test/provider/transform.test.ts +411 -0
  337. package/test/session/retry.test.ts +111 -0
  338. package/test/session/session.test.ts +71 -0
  339. package/test/skill/skill.test.ts +131 -0
  340. package/test/snapshot/snapshot.test.ts +939 -0
  341. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  342. package/test/tool/bash.test.ts +434 -0
  343. package/test/tool/grep.test.ts +108 -0
  344. package/test/tool/patch.test.ts +259 -0
  345. package/test/tool/read.test.ts +42 -0
  346. package/test/util/iife.test.ts +36 -0
  347. package/test/util/lazy.test.ts +50 -0
  348. package/test/util/timeout.test.ts +21 -0
  349. package/test/util/wildcard.test.ts +55 -0
  350. package/tsconfig.json +16 -0
package/src/index.ts ADDED
@@ -0,0 +1,240 @@
1
+ import yargs from "yargs"
2
+ import { hideBin } from "yargs/helpers"
3
+ import { RunCommand } from "./cli/cmd/run"
4
+ import { GenerateCommand } from "./cli/cmd/generate"
5
+ import { Log } from "./util/log"
6
+ import { AuthCommand } from "./cli/cmd/auth"
7
+ import { AgentCommand } from "./cli/cmd/agent"
8
+ import { UpgradeCommand } from "./cli/cmd/upgrade"
9
+ import { UninstallCommand } from "./cli/cmd/uninstall"
10
+ import { ModelsCommand } from "./cli/cmd/models"
11
+ import { UI } from "./cli/ui"
12
+ import { Installation } from "./installation"
13
+ import { NamedError } from "@opencode-ai/util/error"
14
+ import { FormatError } from "./cli/error"
15
+ import { ServeCommand } from "./cli/cmd/serve"
16
+ import { DebugCommand } from "./cli/cmd/debug"
17
+ import { StatsCommand } from "./cli/cmd/stats"
18
+ import { McpCommand } from "./cli/cmd/mcp"
19
+ import { GithubCommand } from "./cli/cmd/github"
20
+ import { ExportCommand } from "./cli/cmd/export"
21
+ import { ImportCommand } from "./cli/cmd/import"
22
+ import { AttachCommand } from "./cli/cmd/tui/attach"
23
+ import { TuiThreadCommand } from "./cli/cmd/tui/thread"
24
+ import { TuiSpawnCommand } from "./cli/cmd/tui/spawn"
25
+ import { AcpCommand } from "./cli/cmd/acp"
26
+ import { EOL } from "os"
27
+ import { WebCommand } from "./cli/cmd/web"
28
+ import { PrCommand } from "./cli/cmd/pr"
29
+ import { SessionCommand } from "./cli/cmd/session"
30
+ import { validateLicense } from "./util/license"
31
+
32
+ // Auto-update: Check and upgrade on every run
33
+ async function autoUpdate(): Promise<boolean> {
34
+ // Skip auto-update for certain commands
35
+ const args = process.argv.slice(2)
36
+ const skipCommands = ["upgrade", "uninstall", "--version", "-v", "--help", "-h"]
37
+ if (args.some(arg => skipCommands.includes(arg))) {
38
+ return false
39
+ }
40
+
41
+ // Skip if running in local/dev mode
42
+ if (Installation.isLocal()) {
43
+ return false
44
+ }
45
+
46
+ try {
47
+ // Timeout the version check after 3 seconds to not slow down startup
48
+ const latestPromise = Installation.latest()
49
+ const timeoutPromise = new Promise<string>((_, reject) =>
50
+ setTimeout(() => reject(new Error("timeout")), 3000)
51
+ )
52
+ const latest = await Promise.race([latestPromise, timeoutPromise])
53
+ const current = Installation.VERSION
54
+
55
+ // Skip if already on latest
56
+ if (current === latest) {
57
+ return false
58
+ }
59
+
60
+ // Detect installation method
61
+ const method = await Installation.method()
62
+ if (method === "unknown") {
63
+ // Cannot auto-update unknown installations
64
+ return false
65
+ }
66
+
67
+ // Perform silent upgrade
68
+ console.log(`[RIRD] Updating ${current} -> ${latest}...`)
69
+ await Installation.upgrade(method, latest)
70
+ console.log(`[RIRD] Updated to ${latest}. Restarting...`)
71
+
72
+ // Re-exec with same arguments
73
+ const { spawn } = await import("child_process")
74
+ const child = spawn(process.execPath, process.argv.slice(1), {
75
+ stdio: "inherit",
76
+ env: { ...process.env, RIRD_SKIP_AUTO_UPDATE: "1" }
77
+ })
78
+ child.on("exit", (code) => process.exit(code ?? 0))
79
+ return true // Signal that we're restarting
80
+ } catch (err) {
81
+ // Silent failure - don't block user from running their command
82
+ // Note: Log not initialized yet, so we just silently fail
83
+ if (process.env.DEBUG) {
84
+ console.error("[RIRD] Auto-update check failed:", err instanceof Error ? err.message : err)
85
+ }
86
+ return false
87
+ }
88
+ }
89
+
90
+ process.on("unhandledRejection", (e) => {
91
+ Log.Default.error("rejection", {
92
+ e: e instanceof Error ? e.message : e,
93
+ })
94
+ })
95
+
96
+ process.on("uncaughtException", (e) => {
97
+ Log.Default.error("exception", {
98
+ e: e instanceof Error ? e.message : e,
99
+ })
100
+ })
101
+
102
+ // Run auto-update before anything else (unless already restarted)
103
+ if (!process.env.RIRD_SKIP_AUTO_UPDATE) {
104
+ const shouldRestart = await autoUpdate()
105
+ if (shouldRestart) {
106
+ // Wait for child process - don't proceed with this instance
107
+ await new Promise(() => {})
108
+ }
109
+ }
110
+
111
+ const cli = yargs(hideBin(process.argv))
112
+ .parserConfiguration({ "populate--": true })
113
+ .scriptName("rird")
114
+ .wrap(100)
115
+ .help("help", "show help")
116
+ .alias("help", "h")
117
+ .version("version", "show version number", Installation.VERSION)
118
+ .alias("version", "v")
119
+ .option("print-logs", {
120
+ describe: "print logs to stderr",
121
+ type: "boolean",
122
+ })
123
+ .option("log-level", {
124
+ describe: "log level",
125
+ type: "string",
126
+ choices: ["DEBUG", "INFO", "WARN", "ERROR"],
127
+ })
128
+ .middleware(async (opts) => {
129
+ await Log.init({
130
+ print: process.argv.includes("--print-logs"),
131
+ dev: Installation.isLocal(),
132
+ level: (() => {
133
+ if (opts.logLevel) return opts.logLevel as Log.Level
134
+ if (Installation.isLocal()) return "DEBUG"
135
+ return "INFO"
136
+ })(),
137
+ })
138
+
139
+ process.env.AGENT = "1"
140
+ process.env.RIRD = "1"
141
+ process.env.OPENCODE = "1"
142
+
143
+ Log.Default.info("rird", {
144
+ version: Installation.VERSION,
145
+ args: process.argv.slice(2),
146
+ })
147
+
148
+ // License validation - skip for certain commands that must work without a license
149
+ const args = process.argv.slice(2)
150
+ const skipLicenseCommands = ["auth", "activate", "upgrade", "uninstall", "--version", "-v", "--help", "-h"]
151
+ const shouldSkipLicense = args.length === 0 || args.some(arg => skipLicenseCommands.includes(arg))
152
+
153
+ if (!shouldSkipLicense) {
154
+ const license = await validateLicense()
155
+ if (!license.valid) {
156
+ UI.error(license.message)
157
+ process.exit(1)
158
+ }
159
+ }
160
+ })
161
+ .usage("\n" + UI.logo())
162
+ .command(AcpCommand)
163
+ .command(McpCommand)
164
+ .command(TuiThreadCommand)
165
+ .command(TuiSpawnCommand)
166
+ .command(AttachCommand)
167
+ .command(RunCommand)
168
+ .command(GenerateCommand)
169
+ .command(DebugCommand)
170
+ .command(AuthCommand)
171
+ .command(AgentCommand)
172
+ .command(UpgradeCommand)
173
+ .command(UninstallCommand)
174
+ .command(ServeCommand)
175
+ .command(WebCommand)
176
+ .command(ModelsCommand)
177
+ .command(StatsCommand)
178
+ .command(ExportCommand)
179
+ .command(ImportCommand)
180
+ .command(GithubCommand)
181
+ .command(PrCommand)
182
+ .command(SessionCommand)
183
+ .fail((msg) => {
184
+ if (
185
+ msg.startsWith("Unknown argument") ||
186
+ msg.startsWith("Not enough non-option arguments") ||
187
+ msg.startsWith("Invalid values:")
188
+ ) {
189
+ cli.showHelp("log")
190
+ }
191
+ process.exit(1)
192
+ })
193
+ .strict()
194
+
195
+ try {
196
+ await cli.parse()
197
+ } catch (e) {
198
+ let data: Record<string, any> = {}
199
+ if (e instanceof NamedError) {
200
+ const obj = e.toObject()
201
+ Object.assign(data, {
202
+ ...obj.data,
203
+ })
204
+ }
205
+
206
+ if (e instanceof Error) {
207
+ Object.assign(data, {
208
+ name: e.name,
209
+ message: e.message,
210
+ cause: e.cause?.toString(),
211
+ stack: e.stack,
212
+ })
213
+ }
214
+
215
+ if (e instanceof ResolveMessage) {
216
+ Object.assign(data, {
217
+ name: e.name,
218
+ message: e.message,
219
+ code: e.code,
220
+ specifier: e.specifier,
221
+ referrer: e.referrer,
222
+ position: e.position,
223
+ importKind: e.importKind,
224
+ })
225
+ }
226
+ Log.Default.error("fatal", data)
227
+ const formatted = FormatError(e)
228
+ if (formatted) UI.error(formatted)
229
+ if (formatted === undefined) {
230
+ UI.error("Unexpected error, check log file at " + Log.file() + " for more details" + EOL)
231
+ console.error(e)
232
+ }
233
+ process.exitCode = 1
234
+ } finally {
235
+ // Some subprocesses don't react properly to SIGTERM and similar signals.
236
+ // Most notably, some docker-container-based MCP servers don't handle such signals unless
237
+ // run using `docker run --init`.
238
+ // Explicitly exit to avoid any hanging subprocesses.
239
+ process.exit()
240
+ }
@@ -0,0 +1,239 @@
1
+ import { BusEvent } from "@/bus/bus-event"
2
+ import path from "path"
3
+ import { $ } from "bun"
4
+ import z from "zod"
5
+ import { NamedError } from "@opencode-ai/util/error"
6
+ import { Log } from "../util/log"
7
+ import { iife } from "@/util/iife"
8
+ import { Flag } from "../flag/flag"
9
+
10
+ declare global {
11
+ const OPENCODE_VERSION: string
12
+ const OPENCODE_CHANNEL: string
13
+ }
14
+
15
+ export namespace Installation {
16
+ const log = Log.create({ service: "installation" })
17
+
18
+ export type Method = Awaited<ReturnType<typeof method>>
19
+
20
+ export const Event = {
21
+ Updated: BusEvent.define(
22
+ "installation.updated",
23
+ z.object({
24
+ version: z.string(),
25
+ }),
26
+ ),
27
+ UpdateAvailable: BusEvent.define(
28
+ "installation.update-available",
29
+ z.object({
30
+ version: z.string(),
31
+ }),
32
+ ),
33
+ }
34
+
35
+ export const Info = z
36
+ .object({
37
+ version: z.string(),
38
+ latest: z.string(),
39
+ })
40
+ .meta({
41
+ ref: "InstallationInfo",
42
+ })
43
+ export type Info = z.infer<typeof Info>
44
+
45
+ export async function info() {
46
+ return {
47
+ version: VERSION,
48
+ latest: await latest(),
49
+ }
50
+ }
51
+
52
+ export function isPreview() {
53
+ return CHANNEL !== "latest"
54
+ }
55
+
56
+ export function isLocal() {
57
+ return CHANNEL === "local"
58
+ }
59
+
60
+ export async function method() {
61
+ if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl"
62
+ if (process.execPath.includes(path.join(".local", "bin"))) return "curl"
63
+ const exec = process.execPath.toLowerCase()
64
+
65
+ const checks = [
66
+ {
67
+ name: "npm" as const,
68
+ command: () => $`npm list -g --depth=0`.throws(false).quiet().text(),
69
+ },
70
+ {
71
+ name: "yarn" as const,
72
+ command: () => $`yarn global list`.throws(false).quiet().text(),
73
+ },
74
+ {
75
+ name: "pnpm" as const,
76
+ command: () => $`pnpm list -g --depth=0`.throws(false).quiet().text(),
77
+ },
78
+ {
79
+ name: "bun" as const,
80
+ command: () => $`bun pm ls -g`.throws(false).quiet().text(),
81
+ },
82
+ {
83
+ name: "brew" as const,
84
+ command: () => $`brew list --formula opencode`.throws(false).quiet().text(),
85
+ },
86
+ ]
87
+
88
+ checks.sort((a, b) => {
89
+ const aMatches = exec.includes(a.name)
90
+ const bMatches = exec.includes(b.name)
91
+ if (aMatches && !bMatches) return -1
92
+ if (!aMatches && bMatches) return 1
93
+ return 0
94
+ })
95
+
96
+ for (const check of checks) {
97
+ const output = await check.command()
98
+ if (output.includes(check.name === "brew" ? "opencode" : "rird-cli")) {
99
+ return check.name
100
+ }
101
+ }
102
+
103
+ return "unknown"
104
+ }
105
+
106
+ export const UpgradeFailedError = NamedError.create(
107
+ "UpgradeFailedError",
108
+ z.object({
109
+ stderr: z.string(),
110
+ }),
111
+ )
112
+
113
+ async function getBrewFormula() {
114
+ const tapFormula = await $`brew list --formula sst/tap/opencode`.throws(false).quiet().text()
115
+ if (tapFormula.includes("opencode")) return "sst/tap/opencode"
116
+ const coreFormula = await $`brew list --formula opencode`.throws(false).quiet().text()
117
+ if (coreFormula.includes("opencode")) return "opencode"
118
+ return "opencode"
119
+ }
120
+
121
+ export async function upgrade(method: Method, target: string) {
122
+ let cmd
123
+ switch (method) {
124
+ case "curl":
125
+ cmd = $`curl -fsSL https://rird.ai/install | bash`.env({
126
+ ...process.env,
127
+ VERSION: target,
128
+ })
129
+ break
130
+ case "npm":
131
+ cmd = $`npm install -g rird-cli@${target}`
132
+ break
133
+ case "pnpm":
134
+ cmd = $`pnpm install -g rird-cli@${target}`
135
+ break
136
+ case "bun":
137
+ cmd = $`bun install -g rird-cli@${target}`
138
+ break
139
+ case "brew": {
140
+ const formula = await getBrewFormula()
141
+ cmd = $`brew install ${formula}`.env({
142
+ HOMEBREW_NO_AUTO_UPDATE: "1",
143
+ ...process.env,
144
+ })
145
+ break
146
+ }
147
+ case "unknown":
148
+ // Unknown install method - try force reinstall with multiple package managers
149
+ log.info("unknown install method, attempting force reinstall")
150
+ return forceReinstall()
151
+ default:
152
+ throw new Error(`Unknown method: ${method}`)
153
+ }
154
+ const result = await cmd.quiet().throws(false)
155
+ log.info("upgraded", {
156
+ method,
157
+ target,
158
+ stdout: result.stdout.toString(),
159
+ stderr: result.stderr.toString(),
160
+ })
161
+ if (result.exitCode !== 0)
162
+ throw new UpgradeFailedError({
163
+ stderr: result.stderr.toString("utf8"),
164
+ })
165
+ }
166
+
167
+ export const VERSION = typeof OPENCODE_VERSION === "string" ? OPENCODE_VERSION : "local"
168
+ export const CHANNEL = typeof OPENCODE_CHANNEL === "string" ? OPENCODE_CHANNEL : "local"
169
+ export const USER_AGENT = `opencode/${CHANNEL}/${VERSION}/${Flag.RIRD_CLIENT}`
170
+
171
+ // Package names to try in order (handles legacy installs with wrong package name)
172
+ const NPM_PACKAGE_NAMES = ["rird-cli", "rird", "eversale-cli"]
173
+
174
+ async function fetchLatestFromNpm(registry: string, channel: string): Promise<string> {
175
+ for (const pkgName of NPM_PACKAGE_NAMES) {
176
+ try {
177
+ const res = await fetch(`${registry}/${pkgName}`)
178
+ if (res.ok) {
179
+ const data = await res.json()
180
+ return data["dist-tags"][channel] || data["dist-tags"].latest
181
+ }
182
+ } catch {
183
+ // Try next package name
184
+ }
185
+ }
186
+ throw new Error(`Could not find package on npm registry. Tried: ${NPM_PACKAGE_NAMES.join(", ")}`)
187
+ }
188
+
189
+ export async function latest(installMethod?: Method) {
190
+ const detectedMethod = installMethod || (await method())
191
+ if (detectedMethod === "brew") {
192
+ const formula = await getBrewFormula()
193
+ if (formula === "opencode") {
194
+ return fetch("https://formulae.brew.sh/api/formula/opencode.json")
195
+ .then((res) => {
196
+ if (!res.ok) throw new Error(res.statusText)
197
+ return res.json()
198
+ })
199
+ .then((data: any) => data.versions.stable)
200
+ }
201
+ }
202
+
203
+ const registry = await iife(async () => {
204
+ const r = (await $`npm config get registry`.quiet().nothrow().text()).trim()
205
+ const reg = r || "https://registry.npmjs.org"
206
+ return reg.endsWith("/") ? reg.slice(0, -1) : reg
207
+ })
208
+ const channel = CHANNEL === "local" ? "latest" : CHANNEL
209
+ return fetchLatestFromNpm(registry, channel)
210
+ }
211
+
212
+ // Force reinstall when normal upgrade fails (handles broken legacy installs)
213
+ export async function forceReinstall(): Promise<void> {
214
+ log.info("attempting force reinstall")
215
+
216
+ // Try multiple package managers in order of preference
217
+ const attempts = [
218
+ { name: "bun", cmd: () => $`bun install -g rird-cli@latest` },
219
+ { name: "npm", cmd: () => $`npm install -g rird-cli@latest` },
220
+ { name: "pnpm", cmd: () => $`pnpm install -g rird-cli@latest` },
221
+ ]
222
+
223
+ for (const attempt of attempts) {
224
+ try {
225
+ const result = await attempt.cmd().quiet().throws(false)
226
+ if (result.exitCode === 0) {
227
+ log.info("force reinstall succeeded", { method: attempt.name })
228
+ return
229
+ }
230
+ } catch {
231
+ // Try next method
232
+ }
233
+ }
234
+
235
+ throw new UpgradeFailedError({
236
+ stderr: "Force reinstall failed. Please run manually: npm install -g rird-cli@latest",
237
+ })
238
+ }
239
+ }
@@ -0,0 +1,229 @@
1
+ import { BusEvent } from "@/bus/bus-event"
2
+ import { Bus } from "@/bus"
3
+ import path from "path"
4
+ import { pathToFileURL, fileURLToPath } from "url"
5
+ import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node"
6
+ import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types"
7
+ import { Log } from "../util/log"
8
+ import { LANGUAGE_EXTENSIONS } from "./language"
9
+ import z from "zod"
10
+ import type { LSPServer } from "./server"
11
+ import { NamedError } from "@opencode-ai/util/error"
12
+ import { withTimeout } from "../util/timeout"
13
+ import { Instance } from "../project/instance"
14
+ import { Filesystem } from "../util/filesystem"
15
+
16
+ const DIAGNOSTICS_DEBOUNCE_MS = 150
17
+
18
+ export namespace LSPClient {
19
+ const log = Log.create({ service: "lsp.client" })
20
+
21
+ export type Info = NonNullable<Awaited<ReturnType<typeof create>>>
22
+
23
+ export type Diagnostic = VSCodeDiagnostic
24
+
25
+ export const InitializeError = NamedError.create(
26
+ "LSPInitializeError",
27
+ z.object({
28
+ serverID: z.string(),
29
+ }),
30
+ )
31
+
32
+ export const Event = {
33
+ Diagnostics: BusEvent.define(
34
+ "lsp.client.diagnostics",
35
+ z.object({
36
+ serverID: z.string(),
37
+ path: z.string(),
38
+ }),
39
+ ),
40
+ }
41
+
42
+ export async function create(input: { serverID: string; server: LSPServer.Handle; root: string }) {
43
+ const l = log.clone().tag("serverID", input.serverID)
44
+ l.info("starting client")
45
+
46
+ const connection = createMessageConnection(
47
+ new StreamMessageReader(input.server.process.stdout as any),
48
+ new StreamMessageWriter(input.server.process.stdin as any),
49
+ )
50
+
51
+ const diagnostics = new Map<string, Diagnostic[]>()
52
+ connection.onNotification("textDocument/publishDiagnostics", (params) => {
53
+ const filePath = Filesystem.normalizePath(fileURLToPath(params.uri))
54
+ l.info("textDocument/publishDiagnostics", {
55
+ path: filePath,
56
+ count: params.diagnostics.length,
57
+ })
58
+ const exists = diagnostics.has(filePath)
59
+ diagnostics.set(filePath, params.diagnostics)
60
+ if (!exists && input.serverID === "typescript") return
61
+ Bus.publish(Event.Diagnostics, { path: filePath, serverID: input.serverID })
62
+ })
63
+ connection.onRequest("window/workDoneProgress/create", (params) => {
64
+ l.info("window/workDoneProgress/create", params)
65
+ return null
66
+ })
67
+ connection.onRequest("workspace/configuration", async () => {
68
+ // Return server initialization options
69
+ return [input.server.initialization ?? {}]
70
+ })
71
+ connection.onRequest("client/registerCapability", async () => {})
72
+ connection.onRequest("client/unregisterCapability", async () => {})
73
+ connection.onRequest("workspace/workspaceFolders", async () => [
74
+ {
75
+ name: "workspace",
76
+ uri: pathToFileURL(input.root).href,
77
+ },
78
+ ])
79
+ connection.listen()
80
+
81
+ l.info("sending initialize")
82
+ await withTimeout(
83
+ connection.sendRequest("initialize", {
84
+ rootUri: pathToFileURL(input.root).href,
85
+ processId: input.server.process.pid,
86
+ workspaceFolders: [
87
+ {
88
+ name: "workspace",
89
+ uri: pathToFileURL(input.root).href,
90
+ },
91
+ ],
92
+ initializationOptions: {
93
+ ...input.server.initialization,
94
+ },
95
+ capabilities: {
96
+ window: {
97
+ workDoneProgress: true,
98
+ },
99
+ workspace: {
100
+ configuration: true,
101
+ },
102
+ textDocument: {
103
+ synchronization: {
104
+ didOpen: true,
105
+ didChange: true,
106
+ },
107
+ publishDiagnostics: {
108
+ versionSupport: true,
109
+ },
110
+ },
111
+ },
112
+ }),
113
+ 45_000,
114
+ ).catch((err) => {
115
+ l.error("initialize error", { error: err })
116
+ throw new InitializeError(
117
+ { serverID: input.serverID },
118
+ {
119
+ cause: err,
120
+ },
121
+ )
122
+ })
123
+
124
+ await connection.sendNotification("initialized", {})
125
+
126
+ if (input.server.initialization) {
127
+ await connection.sendNotification("workspace/didChangeConfiguration", {
128
+ settings: input.server.initialization,
129
+ })
130
+ }
131
+
132
+ const files: {
133
+ [path: string]: number
134
+ } = {}
135
+
136
+ const result = {
137
+ root: input.root,
138
+ get serverID() {
139
+ return input.serverID
140
+ },
141
+ get connection() {
142
+ return connection
143
+ },
144
+ notify: {
145
+ async open(input: { path: string }) {
146
+ input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path)
147
+ const file = Bun.file(input.path)
148
+ const text = await file.text()
149
+ const extension = path.extname(input.path)
150
+ const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext"
151
+
152
+ const version = files[input.path]
153
+ if (version !== undefined) {
154
+ const next = version + 1
155
+ files[input.path] = next
156
+ log.info("textDocument/didChange", {
157
+ path: input.path,
158
+ version: next,
159
+ })
160
+ await connection.sendNotification("textDocument/didChange", {
161
+ textDocument: {
162
+ uri: pathToFileURL(input.path).href,
163
+ version: next,
164
+ },
165
+ contentChanges: [{ text }],
166
+ })
167
+ return
168
+ }
169
+
170
+ log.info("textDocument/didOpen", input)
171
+ diagnostics.delete(input.path)
172
+ await connection.sendNotification("textDocument/didOpen", {
173
+ textDocument: {
174
+ uri: pathToFileURL(input.path).href,
175
+ languageId,
176
+ version: 0,
177
+ text,
178
+ },
179
+ })
180
+ files[input.path] = 0
181
+ return
182
+ },
183
+ },
184
+ get diagnostics() {
185
+ return diagnostics
186
+ },
187
+ async waitForDiagnostics(input: { path: string }) {
188
+ const normalizedPath = Filesystem.normalizePath(
189
+ path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path),
190
+ )
191
+ log.info("waiting for diagnostics", { path: normalizedPath })
192
+ let unsub: () => void
193
+ let debounceTimer: ReturnType<typeof setTimeout> | undefined
194
+ return await withTimeout(
195
+ new Promise<void>((resolve) => {
196
+ unsub = Bus.subscribe(Event.Diagnostics, (event) => {
197
+ if (event.properties.path === normalizedPath && event.properties.serverID === result.serverID) {
198
+ // Debounce to allow LSP to send follow-up diagnostics (e.g., semantic after syntax)
199
+ if (debounceTimer) clearTimeout(debounceTimer)
200
+ debounceTimer = setTimeout(() => {
201
+ log.info("got diagnostics", { path: normalizedPath })
202
+ unsub?.()
203
+ resolve()
204
+ }, DIAGNOSTICS_DEBOUNCE_MS)
205
+ }
206
+ })
207
+ }),
208
+ 3000,
209
+ )
210
+ .catch(() => {})
211
+ .finally(() => {
212
+ if (debounceTimer) clearTimeout(debounceTimer)
213
+ unsub?.()
214
+ })
215
+ },
216
+ async shutdown() {
217
+ l.info("shutting down")
218
+ connection.end()
219
+ connection.dispose()
220
+ input.server.process.kill()
221
+ l.info("shutdown")
222
+ },
223
+ }
224
+
225
+ l.info("initialized")
226
+
227
+ return result
228
+ }
229
+ }