octo-agent 0.11.2

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 (319) hide show
  1. checksums.yaml +7 -0
  2. data/.clacky/skills/commit/SKILL.md +423 -0
  3. data/.clacky/skills/gem-release/SKILL.md +199 -0
  4. data/.clacky/skills/gem-release/scripts/release.sh +304 -0
  5. data/.clacky/skills/oss-upload/SKILL.md +47 -0
  6. data/.octorules +106 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +8 -0
  9. data/CHANGELOG.md +76 -0
  10. data/CODE_OF_CONDUCT.md +132 -0
  11. data/CONTRIBUTING.md +92 -0
  12. data/Dockerfile +28 -0
  13. data/LICENSE.txt +22 -0
  14. data/POSITIONING.md +46 -0
  15. data/README.md +134 -0
  16. data/README_CN.md +134 -0
  17. data/Rakefile +34 -0
  18. data/benchmark/fixtures/sample_project/Gemfile +3 -0
  19. data/benchmark/fixtures/sample_project/lib/api_handler.rb +32 -0
  20. data/benchmark/fixtures/sample_project/lib/order_calculator.rb +23 -0
  21. data/benchmark/fixtures/sample_project/lib/user_renderer.rb +20 -0
  22. data/benchmark/fixtures/sample_project/spec/order_calculator_spec.rb +20 -0
  23. data/benchmark/results/EVALUATION_REPORT.md +165 -0
  24. data/benchmark/results/baseline_20260511_174424.json +128 -0
  25. data/benchmark/results/report_20260511_175256.json +271 -0
  26. data/benchmark/results/report_20260511_175444.json +271 -0
  27. data/benchmark/results/treatment_20260511_175103.json +130 -0
  28. data/benchmark/runner.rb +441 -0
  29. data/bin/octo +7 -0
  30. data/docs/agent-first-ui-design.md +77 -0
  31. data/docs/billing-system.md +318 -0
  32. data/docs/channel-architecture.md +235 -0
  33. data/docs/engineering-article.md +343 -0
  34. data/docs/session-skill-invocation.md +69 -0
  35. data/docs/time_machine_design.md +247 -0
  36. data/docs/ui2-architecture.md +124 -0
  37. data/homebrew/README.md +96 -0
  38. data/homebrew/openocto.rb +24 -0
  39. data/lib/octo/agent/hook_manager.rb +61 -0
  40. data/lib/octo/agent/llm_caller.rb +800 -0
  41. data/lib/octo/agent/memory_updater.rb +246 -0
  42. data/lib/octo/agent/message_compressor.rb +225 -0
  43. data/lib/octo/agent/message_compressor_helper.rb +869 -0
  44. data/lib/octo/agent/next_message_suggester.rb +215 -0
  45. data/lib/octo/agent/session_serializer.rb +685 -0
  46. data/lib/octo/agent/skill_auto_creator.rb +114 -0
  47. data/lib/octo/agent/skill_evolution.rb +61 -0
  48. data/lib/octo/agent/skill_manager.rb +466 -0
  49. data/lib/octo/agent/skill_reflector.rb +89 -0
  50. data/lib/octo/agent/system_prompt_builder.rb +101 -0
  51. data/lib/octo/agent/time_machine.rb +214 -0
  52. data/lib/octo/agent/tool_executor.rb +454 -0
  53. data/lib/octo/agent/tool_registry.rb +150 -0
  54. data/lib/octo/agent.rb +2180 -0
  55. data/lib/octo/agent_config.rb +989 -0
  56. data/lib/octo/agent_profile.rb +112 -0
  57. data/lib/octo/anthropic_stream_aggregator.rb +137 -0
  58. data/lib/octo/background_task_registry.rb +324 -0
  59. data/lib/octo/banner.rb +34 -0
  60. data/lib/octo/bedrock_stream_aggregator.rb +137 -0
  61. data/lib/octo/block_font.rb +331 -0
  62. data/lib/octo/cli.rb +968 -0
  63. data/lib/octo/client.rb +623 -0
  64. data/lib/octo/default_agents/SOUL.md +3 -0
  65. data/lib/octo/default_agents/USER.md +1 -0
  66. data/lib/octo/default_agents/base_prompt.md +66 -0
  67. data/lib/octo/default_agents/coding/profile.yml +2 -0
  68. data/lib/octo/default_agents/coding/system_prompt.md +67 -0
  69. data/lib/octo/default_agents/general/profile.yml +2 -0
  70. data/lib/octo/default_agents/general/system_prompt.md +16 -0
  71. data/lib/octo/default_parsers/doc_parser.rb +69 -0
  72. data/lib/octo/default_parsers/docx_parser.rb +188 -0
  73. data/lib/octo/default_parsers/pdf_parser.rb +120 -0
  74. data/lib/octo/default_parsers/pdf_parser_ocr.py +103 -0
  75. data/lib/octo/default_parsers/pdf_parser_plumber.py +62 -0
  76. data/lib/octo/default_parsers/pptx_parser.rb +140 -0
  77. data/lib/octo/default_parsers/xlsx_parser.rb +121 -0
  78. data/lib/octo/default_skills/browser-setup/SKILL.md +426 -0
  79. data/lib/octo/default_skills/channel-manager/SKILL.md +623 -0
  80. data/lib/octo/default_skills/channel-manager/dingtalk_setup.rb +191 -0
  81. data/lib/octo/default_skills/channel-manager/discord_setup.rb +199 -0
  82. data/lib/octo/default_skills/channel-manager/feishu_setup.rb +574 -0
  83. data/lib/octo/default_skills/channel-manager/import_lark_skills.rb +97 -0
  84. data/lib/octo/default_skills/channel-manager/install_feishu_skills.rb +105 -0
  85. data/lib/octo/default_skills/channel-manager/weixin_setup.rb +274 -0
  86. data/lib/octo/default_skills/code-explorer/SKILL.md +36 -0
  87. data/lib/octo/default_skills/cron-task-creator/SKILL.md +257 -0
  88. data/lib/octo/default_skills/cron-task-creator/evals/evals.json +38 -0
  89. data/lib/octo/default_skills/onboard/SKILL.md +578 -0
  90. data/lib/octo/default_skills/onboard/scripts/import_external_skills.rb +413 -0
  91. data/lib/octo/default_skills/onboard/scripts/install_builtin_skills.rb +97 -0
  92. data/lib/octo/default_skills/persist-memory/SKILL.md +59 -0
  93. data/lib/octo/default_skills/personal-website/SKILL.md +113 -0
  94. data/lib/octo/default_skills/personal-website/publish.rb +235 -0
  95. data/lib/octo/default_skills/product-help/SKILL.md +123 -0
  96. data/lib/octo/default_skills/product-help/docs/agent-config.md +74 -0
  97. data/lib/octo/default_skills/product-help/docs/best-practices.md +49 -0
  98. data/lib/octo/default_skills/product-help/docs/browser-tool.md +53 -0
  99. data/lib/octo/default_skills/product-help/docs/built-in-skills.md +43 -0
  100. data/lib/octo/default_skills/product-help/docs/cli-reference.md +82 -0
  101. data/lib/octo/default_skills/product-help/docs/create-your-first-skill.md +47 -0
  102. data/lib/octo/default_skills/product-help/docs/faq.md +98 -0
  103. data/lib/octo/default_skills/product-help/docs/how-to-use-a-skill.md +58 -0
  104. data/lib/octo/default_skills/product-help/docs/installation.md +59 -0
  105. data/lib/octo/default_skills/product-help/docs/memory-system.md +61 -0
  106. data/lib/octo/default_skills/product-help/docs/octorules.md +62 -0
  107. data/lib/octo/default_skills/product-help/docs/session-management.md +63 -0
  108. data/lib/octo/default_skills/product-help/docs/skill-basics.md +55 -0
  109. data/lib/octo/default_skills/product-help/docs/skill-frontmatter.md +61 -0
  110. data/lib/octo/default_skills/product-help/docs/web-server.md +49 -0
  111. data/lib/octo/default_skills/product-help/docs/what-is-octo.md +37 -0
  112. data/lib/octo/default_skills/product-help/docs/windows-installation.md +36 -0
  113. data/lib/octo/default_skills/product-help/docs/writing-tips.md +53 -0
  114. data/lib/octo/default_skills/recall-memory/SKILL.md +65 -0
  115. data/lib/octo/default_skills/skill-add/SKILL.md +59 -0
  116. data/lib/octo/default_skills/skill-add/scripts/install_from_zip.rb +295 -0
  117. data/lib/octo/default_skills/skill-creator/SKILL.md +602 -0
  118. data/lib/octo/default_skills/skill-creator/agents/analyzer.md +274 -0
  119. data/lib/octo/default_skills/skill-creator/agents/comparator.md +202 -0
  120. data/lib/octo/default_skills/skill-creator/agents/grader.md +223 -0
  121. data/lib/octo/default_skills/skill-creator/eval-viewer/generate_review.py +471 -0
  122. data/lib/octo/default_skills/skill-creator/eval-viewer/viewer.html +1325 -0
  123. data/lib/octo/default_skills/skill-creator/references/schemas.md +430 -0
  124. data/lib/octo/default_skills/skill-creator/scripts/__init__.py +0 -0
  125. data/lib/octo/default_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  126. data/lib/octo/default_skills/skill-creator/scripts/generate_report.py +326 -0
  127. data/lib/octo/default_skills/skill-creator/scripts/improve_description.py +310 -0
  128. data/lib/octo/default_skills/skill-creator/scripts/quick_validate.py +103 -0
  129. data/lib/octo/default_skills/skill-creator/scripts/run_eval.py +317 -0
  130. data/lib/octo/default_skills/skill-creator/scripts/run_loop.py +331 -0
  131. data/lib/octo/default_skills/skill-creator/scripts/utils.py +47 -0
  132. data/lib/octo/default_skills/skill-creator/scripts/validate_skill_frontmatter.rb +143 -0
  133. data/lib/octo/idle_compression_timer.rb +115 -0
  134. data/lib/octo/json_ui_controller.rb +204 -0
  135. data/lib/octo/message_format/anthropic.rb +409 -0
  136. data/lib/octo/message_format/bedrock.rb +361 -0
  137. data/lib/octo/message_format/open_ai.rb +222 -0
  138. data/lib/octo/message_history.rb +373 -0
  139. data/lib/octo/openai_stream_aggregator.rb +130 -0
  140. data/lib/octo/plain_ui_controller.rb +166 -0
  141. data/lib/octo/providers.rb +534 -0
  142. data/lib/octo/server/browser_manager.rb +397 -0
  143. data/lib/octo/server/channel/adapters/base.rb +82 -0
  144. data/lib/octo/server/channel/adapters/dingtalk/adapter.rb +314 -0
  145. data/lib/octo/server/channel/adapters/dingtalk/api_client.rb +391 -0
  146. data/lib/octo/server/channel/adapters/dingtalk/stream_client.rb +203 -0
  147. data/lib/octo/server/channel/adapters/discord/adapter.rb +229 -0
  148. data/lib/octo/server/channel/adapters/discord/api_client.rb +107 -0
  149. data/lib/octo/server/channel/adapters/discord/gateway_client.rb +270 -0
  150. data/lib/octo/server/channel/adapters/feishu/adapter.rb +320 -0
  151. data/lib/octo/server/channel/adapters/feishu/bot.rb +478 -0
  152. data/lib/octo/server/channel/adapters/feishu/file_processor.rb +36 -0
  153. data/lib/octo/server/channel/adapters/feishu/message_parser.rb +129 -0
  154. data/lib/octo/server/channel/adapters/feishu/ws_client.rb +423 -0
  155. data/lib/octo/server/channel/adapters/telegram/adapter.rb +375 -0
  156. data/lib/octo/server/channel/adapters/telegram/api_client.rb +205 -0
  157. data/lib/octo/server/channel/adapters/wecom/adapter.rb +148 -0
  158. data/lib/octo/server/channel/adapters/wecom/media_downloader.rb +115 -0
  159. data/lib/octo/server/channel/adapters/wecom/ws_client.rb +395 -0
  160. data/lib/octo/server/channel/adapters/weixin/adapter.rb +692 -0
  161. data/lib/octo/server/channel/adapters/weixin/api_client.rb +402 -0
  162. data/lib/octo/server/channel/channel_config.rb +178 -0
  163. data/lib/octo/server/channel/channel_manager.rb +468 -0
  164. data/lib/octo/server/channel/channel_ui_controller.rb +224 -0
  165. data/lib/octo/server/channel.rb +33 -0
  166. data/lib/octo/server/discover.rb +77 -0
  167. data/lib/octo/server/epipe_safe_io.rb +105 -0
  168. data/lib/octo/server/http_server.rb +3554 -0
  169. data/lib/octo/server/scheduler.rb +317 -0
  170. data/lib/octo/server/server_master.rb +325 -0
  171. data/lib/octo/server/session_registry.rb +431 -0
  172. data/lib/octo/server/web_ui_controller.rb +487 -0
  173. data/lib/octo/session_manager.rb +385 -0
  174. data/lib/octo/skill.rb +466 -0
  175. data/lib/octo/skill_loader.rb +328 -0
  176. data/lib/octo/tools/base.rb +118 -0
  177. data/lib/octo/tools/browser.rb +625 -0
  178. data/lib/octo/tools/edit.rb +165 -0
  179. data/lib/octo/tools/file_reader.rb +549 -0
  180. data/lib/octo/tools/glob.rb +162 -0
  181. data/lib/octo/tools/grep.rb +356 -0
  182. data/lib/octo/tools/invoke_skill.rb +96 -0
  183. data/lib/octo/tools/list_tasks.rb +54 -0
  184. data/lib/octo/tools/redo_task.rb +41 -0
  185. data/lib/octo/tools/request_user_feedback.rb +84 -0
  186. data/lib/octo/tools/security.rb +333 -0
  187. data/lib/octo/tools/terminal/output_cleaner.rb +63 -0
  188. data/lib/octo/tools/terminal/persistent_session.rb +268 -0
  189. data/lib/octo/tools/terminal/safe_rm.sh +106 -0
  190. data/lib/octo/tools/terminal/session_manager.rb +213 -0
  191. data/lib/octo/tools/terminal.rb +1828 -0
  192. data/lib/octo/tools/todo_manager.rb +374 -0
  193. data/lib/octo/tools/trash_manager.rb +388 -0
  194. data/lib/octo/tools/undo_task.rb +35 -0
  195. data/lib/octo/tools/web_fetch.rb +242 -0
  196. data/lib/octo/tools/web_search.rb +260 -0
  197. data/lib/octo/tools/write.rb +77 -0
  198. data/lib/octo/ui2/block_font.rb +10 -0
  199. data/lib/octo/ui2/components/base_component.rb +163 -0
  200. data/lib/octo/ui2/components/command_suggestions.rb +290 -0
  201. data/lib/octo/ui2/components/common_component.rb +96 -0
  202. data/lib/octo/ui2/components/inline_input.rb +226 -0
  203. data/lib/octo/ui2/components/input_area.rb +1338 -0
  204. data/lib/octo/ui2/components/message_component.rb +99 -0
  205. data/lib/octo/ui2/components/modal_component.rb +419 -0
  206. data/lib/octo/ui2/components/todo_area.rb +149 -0
  207. data/lib/octo/ui2/components/tool_component.rb +107 -0
  208. data/lib/octo/ui2/components/welcome_banner.rb +139 -0
  209. data/lib/octo/ui2/layout_manager.rb +807 -0
  210. data/lib/octo/ui2/line_editor.rb +363 -0
  211. data/lib/octo/ui2/markdown_renderer.rb +100 -0
  212. data/lib/octo/ui2/output_buffer.rb +370 -0
  213. data/lib/octo/ui2/progress_handle.rb +362 -0
  214. data/lib/octo/ui2/progress_indicator.rb +55 -0
  215. data/lib/octo/ui2/screen_buffer.rb +273 -0
  216. data/lib/octo/ui2/terminal_detector.rb +119 -0
  217. data/lib/octo/ui2/theme_manager.rb +85 -0
  218. data/lib/octo/ui2/themes/base_theme.rb +105 -0
  219. data/lib/octo/ui2/themes/hacker_theme.rb +62 -0
  220. data/lib/octo/ui2/themes/minimal_theme.rb +56 -0
  221. data/lib/octo/ui2/thinking_verbs.rb +26 -0
  222. data/lib/octo/ui2/ui_controller.rb +1625 -0
  223. data/lib/octo/ui2/view_renderer.rb +177 -0
  224. data/lib/octo/ui2.rb +40 -0
  225. data/lib/octo/ui_interface.rb +154 -0
  226. data/lib/octo/utils/arguments_parser.rb +191 -0
  227. data/lib/octo/utils/browser_detector.rb +195 -0
  228. data/lib/octo/utils/encoding.rb +92 -0
  229. data/lib/octo/utils/environment_detector.rb +140 -0
  230. data/lib/octo/utils/file_ignore_helper.rb +170 -0
  231. data/lib/octo/utils/file_processor.rb +601 -0
  232. data/lib/octo/utils/gitignore_parser.rb +154 -0
  233. data/lib/octo/utils/limit_stack.rb +152 -0
  234. data/lib/octo/utils/logger.rb +124 -0
  235. data/lib/octo/utils/login_shell.rb +72 -0
  236. data/lib/octo/utils/model_pricing.rb +646 -0
  237. data/lib/octo/utils/parser_manager.rb +165 -0
  238. data/lib/octo/utils/path_helper.rb +15 -0
  239. data/lib/octo/utils/scripts_manager.rb +59 -0
  240. data/lib/octo/utils/string_matcher.rb +158 -0
  241. data/lib/octo/utils/trash_directory.rb +112 -0
  242. data/lib/octo/utils/workspace_rules.rb +46 -0
  243. data/lib/octo/version.rb +5 -0
  244. data/lib/octo/web/app.css +7141 -0
  245. data/lib/octo/web/app.js +543 -0
  246. data/lib/octo/web/apple-touch-icon.png +0 -0
  247. data/lib/octo/web/auth.js +150 -0
  248. data/lib/octo/web/channels.js +276 -0
  249. data/lib/octo/web/datepicker.js +205 -0
  250. data/lib/octo/web/favicon.png +0 -0
  251. data/lib/octo/web/i18n.js +1073 -0
  252. data/lib/octo/web/icon-512.png +0 -0
  253. data/lib/octo/web/icon-dark.svg +25 -0
  254. data/lib/octo/web/icon.svg +29 -0
  255. data/lib/octo/web/index.html +871 -0
  256. data/lib/octo/web/marked.min.js +69 -0
  257. data/lib/octo/web/onboard.js +491 -0
  258. data/lib/octo/web/profile.js +442 -0
  259. data/lib/octo/web/sessions.js +4421 -0
  260. data/lib/octo/web/settings.js +913 -0
  261. data/lib/octo/web/sidebar.js +32 -0
  262. data/lib/octo/web/skills.js +885 -0
  263. data/lib/octo/web/tasks.js +297 -0
  264. data/lib/octo/web/theme.js +105 -0
  265. data/lib/octo/web/trash.js +343 -0
  266. data/lib/octo/web/vendor/hljs/highlight.min.js +1244 -0
  267. data/lib/octo/web/vendor/hljs/hljs-theme.css +95 -0
  268. data/lib/octo/web/vendor/katex/auto-render.min.js +1 -0
  269. data/lib/octo/web/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  270. data/lib/octo/web/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  271. data/lib/octo/web/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  272. data/lib/octo/web/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  273. data/lib/octo/web/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  274. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  275. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  276. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  277. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  278. data/lib/octo/web/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  279. data/lib/octo/web/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  280. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  281. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  282. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  283. data/lib/octo/web/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  284. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  285. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  286. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  287. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  288. data/lib/octo/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  289. data/lib/octo/web/vendor/katex/katex.min.css +1 -0
  290. data/lib/octo/web/vendor/katex/katex.min.js +1 -0
  291. data/lib/octo/web/version.js +449 -0
  292. data/lib/octo/web/weixin-qr.html +209 -0
  293. data/lib/octo/web/ws-dispatcher.js +357 -0
  294. data/lib/octo/web/ws.js +128 -0
  295. data/lib/octo.rb +145 -0
  296. data/scripts/build/build.sh +329 -0
  297. data/scripts/build/lib/apt.sh +56 -0
  298. data/scripts/build/lib/brew.sh +89 -0
  299. data/scripts/build/lib/colors.sh +17 -0
  300. data/scripts/build/lib/gem.sh +95 -0
  301. data/scripts/build/lib/mise.sh +125 -0
  302. data/scripts/build/lib/network.sh +157 -0
  303. data/scripts/build/lib/os.sh +57 -0
  304. data/scripts/build/lib/shell.sh +37 -0
  305. data/scripts/build/src/install.sh.cc +174 -0
  306. data/scripts/build/src/install_browser.sh.cc +101 -0
  307. data/scripts/build/src/install_full.sh.cc +290 -0
  308. data/scripts/build/src/install_rails_deps.sh.cc +145 -0
  309. data/scripts/build/src/install_system_deps.sh.cc +123 -0
  310. data/scripts/build/src/uninstall.sh.cc +101 -0
  311. data/scripts/install.ps1 +532 -0
  312. data/scripts/install.sh +567 -0
  313. data/scripts/install_browser.sh +479 -0
  314. data/scripts/install_full.sh +838 -0
  315. data/scripts/install_rails_deps.sh +746 -0
  316. data/scripts/install_system_deps.sh +518 -0
  317. data/scripts/uninstall.sh +287 -0
  318. data/sig/octo.rbs +4 -0
  319. metadata +614 -0
@@ -0,0 +1,125 @@
1
+ # mise.sh — install mise, Ruby, and Node via mise
2
+ # Depends-On: colors.sh os.sh
3
+ # Requires-Vars: $SHELL_RC $USE_CN_MIRRORS $CURRENT_SHELL $MISE_INSTALL_URL $NODE_MIRROR_URL $RUBY_VERSION_SPEC $CN_RUBY_PRECOMPILED_URL
4
+ # Sets-Vars: $MISE_BIN
5
+ # Include via: @include lib/mise.sh
6
+
7
+ # --------------------------------------------------------------------------
8
+ # Internal helper — locate the mise binary
9
+ # --------------------------------------------------------------------------
10
+ _mise_bin() {
11
+ if [ -x "$HOME/.local/bin/mise" ]; then
12
+ echo "$HOME/.local/bin/mise"
13
+ elif command_exists mise; then
14
+ command -v mise
15
+ else
16
+ echo ""
17
+ fi
18
+ }
19
+
20
+ # --------------------------------------------------------------------------
21
+ # ensure_mise — install mise if missing, activate it in the current session
22
+ # --------------------------------------------------------------------------
23
+ ensure_mise() {
24
+ local mise
25
+ mise=$(_mise_bin)
26
+
27
+ if [ -n "$mise" ]; then
28
+ print_success "mise already installed: $($mise --version 2>/dev/null || echo 'n/a')"
29
+ export PATH="$HOME/.local/bin:$PATH"
30
+ eval "$($mise activate bash 2>/dev/null)" 2>/dev/null || true
31
+ MISE_BIN="$mise"
32
+ return 0
33
+ fi
34
+
35
+ print_step "Installing mise..."
36
+ if curl -fsSL "$MISE_INSTALL_URL" | sh; then
37
+ export PATH="$HOME/.local/bin:$PATH"
38
+ eval "$(~/.local/bin/mise activate bash 2>/dev/null)" 2>/dev/null || true
39
+
40
+ # Persist mise activation to shell rc
41
+ local init_line='eval "$(~/.local/bin/mise activate '"$CURRENT_SHELL"')"'
42
+ if ! grep -q "mise activate" "$SHELL_RC" 2>/dev/null; then
43
+ echo "$init_line" >> "$SHELL_RC"
44
+ print_info "Added mise activation to $SHELL_RC"
45
+ fi
46
+
47
+ MISE_BIN="$HOME/.local/bin/mise"
48
+ print_success "mise installed"
49
+ else
50
+ print_error "Failed to install mise"
51
+ return 1
52
+ fi
53
+ }
54
+
55
+ # --------------------------------------------------------------------------
56
+ # install_ruby_via_mise — install Ruby via mise
57
+ # CN mode: precompiled binary from oss.1024code.com
58
+ # Global: mise default (precompiled where available)
59
+ # --------------------------------------------------------------------------
60
+ install_ruby_via_mise() {
61
+ local mise="${MISE_BIN:-$(_mise_bin)}"
62
+ if [ -z "$mise" ]; then
63
+ print_error "mise not found — call ensure_mise first"
64
+ return 1
65
+ fi
66
+
67
+ print_info "Installing Ruby via mise ($RUBY_VERSION_SPEC)..."
68
+
69
+ if [ "$USE_CN_MIRRORS" = true ]; then
70
+ "$mise" settings ruby.compile=false 2>/dev/null || true
71
+ "$mise" settings ruby.precompiled_url="$CN_RUBY_PRECOMPILED_URL" 2>/dev/null || true
72
+ print_info "Using precompiled Ruby from CN CDN"
73
+ else
74
+ # Enable precompiled binaries globally (mise supports this on common platforms)
75
+ "$mise" settings ruby.compile=false 2>/dev/null || true
76
+ "$mise" settings unset ruby.precompiled_url 2>/dev/null || true
77
+ fi
78
+
79
+ if "$mise" use -g "$RUBY_VERSION_SPEC"; then
80
+ eval "$($mise activate bash 2>/dev/null)" 2>/dev/null || true
81
+ local installed
82
+ installed=$(ruby -e 'puts RUBY_VERSION' 2>/dev/null || echo "unknown")
83
+ print_success "Ruby $installed installed"
84
+ else
85
+ print_error "Failed to install Ruby via mise"
86
+ return 1
87
+ fi
88
+ }
89
+
90
+ # --------------------------------------------------------------------------
91
+ # install_node_via_mise — install Node.js 22 via mise
92
+ # --------------------------------------------------------------------------
93
+ install_node_via_mise() {
94
+ local mise="${MISE_BIN:-$(_mise_bin)}"
95
+ if [ -z "$mise" ]; then
96
+ print_error "mise not found — call ensure_mise first"
97
+ return 1
98
+ fi
99
+
100
+ # Skip if compatible Node is already active
101
+ if command_exists node; then
102
+ local ver major
103
+ ver=$(node --version 2>/dev/null | sed 's/v//')
104
+ major="${ver%%.*}"
105
+ if [ "${major:-0}" -ge 22 ] 2>/dev/null; then
106
+ print_success "Node.js v${ver} already satisfies >= 22 — skipping"
107
+ return 0
108
+ fi
109
+ fi
110
+
111
+ # Apply CN Node mirror if needed
112
+ if [ "$USE_CN_MIRRORS" = true ] && [ -n "$NODE_MIRROR_URL" ]; then
113
+ "$mise" settings node.mirror_url="$NODE_MIRROR_URL" 2>/dev/null || true
114
+ print_info "Node mirror → ${NODE_MIRROR_URL}"
115
+ fi
116
+
117
+ print_info "Installing Node.js 22 via mise..."
118
+ if "$mise" use -g node@22; then
119
+ eval "$($mise activate bash 2>/dev/null)" 2>/dev/null || true
120
+ print_success "Node.js $(node --version 2>/dev/null) installed"
121
+ else
122
+ print_error "Failed to install Node.js via mise"
123
+ return 1
124
+ fi
125
+ }
@@ -0,0 +1,157 @@
1
+ # network.sh — network region detection, mirror variables, URL probing
2
+ # Depends-On: colors.sh
3
+ # Requires-Vars: (none)
4
+ # Sets-Vars: $USE_CN_MIRRORS $NETWORK_REGION $CN_CDN_BASE_URL $CN_ALIYUN_MIRROR $CN_RUBYGEMS_URL $CN_NODE_MIRROR_URL $CN_NPM_REGISTRY $CN_MISE_INSTALL_URL $CN_RUBY_PRECOMPILED_URL $MISE_INSTALL_URL $NODE_MIRROR_URL $NPM_REGISTRY_URL $RUBY_VERSION_SPEC $DEFAULT_RUBYGEMS_URL $DEFAULT_MISE_INSTALL_URL $DEFAULT_NPM_REGISTRY
5
+ # Include via: @include lib/network.sh
6
+
7
+ # --------------------------------------------------------------------------
8
+ # Mirror variables — overridden by detect_network_region()
9
+ # --------------------------------------------------------------------------
10
+ SLOW_THRESHOLD_MS=5000
11
+ NETWORK_REGION="global" # china | global | unknown
12
+ USE_CN_MIRRORS=false
13
+
14
+ GITHUB_RAW_BASE_URL="https://raw.githubusercontent.com"
15
+ DEFAULT_RUBYGEMS_URL="https://rubygems.org"
16
+ DEFAULT_NPM_REGISTRY="https://registry.npmjs.org"
17
+ DEFAULT_MISE_INSTALL_URL="https://mise.run"
18
+
19
+ CN_CDN_BASE_URL="https://oss.1024code.com"
20
+ CN_ALIYUN_MIRROR="https://mirrors.aliyun.com"
21
+ CN_MISE_INSTALL_URL="${CN_CDN_BASE_URL}/mise.sh"
22
+ CN_RUBY_PRECOMPILED_URL="${CN_CDN_BASE_URL}/ruby/ruby-{version}.{platform}.tar.gz"
23
+ CN_RUBYGEMS_URL="${CN_ALIYUN_MIRROR}/rubygems/"
24
+ CN_NPM_REGISTRY="https://registry.npmmirror.com"
25
+ CN_NODE_MIRROR_URL="https://cdn.npmmirror.com/binaries/node/"
26
+ CN_GEM_BASE_URL="${CN_CDN_BASE_URL}/octo"
27
+ CN_GEM_LATEST_URL="${CN_GEM_BASE_URL}/latest.txt"
28
+
29
+ # Active values (set by detect_network_region)
30
+ MISE_INSTALL_URL="$DEFAULT_MISE_INSTALL_URL"
31
+ RUBYGEMS_INSTALL_URL="$DEFAULT_RUBYGEMS_URL"
32
+ NPM_REGISTRY_URL="$DEFAULT_NPM_REGISTRY"
33
+ NODE_MIRROR_URL="" # empty = mise default (nodejs.org)
34
+ RUBY_VERSION_SPEC="ruby@3" # CN mode pins to a specific precompiled build
35
+
36
+ # --------------------------------------------------------------------------
37
+ # Internal probe helpers
38
+ # --------------------------------------------------------------------------
39
+
40
+ # Probe a single URL; echoes round-trip time in ms, or "timeout"
41
+ _probe_url() {
42
+ local url="$1"
43
+ local out http_code total_time
44
+ out=$(curl -s -o /dev/null -w "%{http_code} %{time_total}" \
45
+ --connect-timeout 5 --max-time 5 "$url" 2>/dev/null) || true
46
+ http_code="${out%% *}"
47
+ total_time="${out#* }"
48
+ if [ -z "$http_code" ] || [ "$http_code" = "000" ] || [ "$http_code" = "$out" ]; then
49
+ echo "timeout"; return
50
+ fi
51
+ awk -v s="$total_time" 'BEGIN { printf "%d", s * 1000 }'
52
+ }
53
+
54
+ # Returns 0 (true) if result is slow or unreachable
55
+ _is_slow_or_unreachable() {
56
+ local r="$1"
57
+ [ "$r" = "timeout" ] && return 0
58
+ [ "${r:-9999}" -ge "$SLOW_THRESHOLD_MS" ] 2>/dev/null
59
+ }
60
+
61
+ _format_probe_time() {
62
+ local r="$1"
63
+ [ "$r" = "timeout" ] && echo "timeout" && return
64
+ awk -v ms="$r" 'BEGIN { printf "%.1fs", ms / 1000 }'
65
+ }
66
+
67
+ _print_probe_result() {
68
+ local label="$1" result="$2"
69
+ if [ "$result" = "timeout" ]; then
70
+ print_warning "UNREACHABLE ${label}"
71
+ elif _is_slow_or_unreachable "$result"; then
72
+ print_warning "SLOW ($(_format_probe_time "$result")) ${label}"
73
+ else
74
+ print_success "OK ($(_format_probe_time "$result")) ${label}"
75
+ fi
76
+ }
77
+
78
+ # Probe URL up to max_retries times; returns first fast result or last result
79
+ _probe_url_with_retry() {
80
+ local url="$1" max="${2:-2}" result
81
+ for _ in $(seq 1 "$max"); do
82
+ result=$(_probe_url "$url")
83
+ ! _is_slow_or_unreachable "$result" && { echo "$result"; return 0; }
84
+ done
85
+ echo "$result"
86
+ }
87
+
88
+ # --------------------------------------------------------------------------
89
+ # detect_network_region — sets USE_CN_MIRRORS and active mirror variables
90
+ # --------------------------------------------------------------------------
91
+ detect_network_region() {
92
+ print_step "Network pre-flight check..."
93
+ echo ""
94
+
95
+ local google_result baidu_result
96
+ google_result=$(_probe_url "https://www.google.com")
97
+ baidu_result=$(_probe_url "https://www.baidu.com")
98
+
99
+ _print_probe_result "google.com" "$google_result"
100
+ _print_probe_result "baidu.com" "$baidu_result"
101
+
102
+ local google_ok=false baidu_ok=false
103
+ ! _is_slow_or_unreachable "$google_result" && google_ok=true
104
+ ! _is_slow_or_unreachable "$baidu_result" && baidu_ok=true
105
+
106
+ if [ "$google_ok" = true ]; then
107
+ NETWORK_REGION="global"
108
+ print_success "Region: global"
109
+ elif [ "$baidu_ok" = true ]; then
110
+ NETWORK_REGION="china"
111
+ print_success "Region: china"
112
+ else
113
+ NETWORK_REGION="unknown"
114
+ print_warning "Region: unknown (both unreachable)"
115
+ fi
116
+ echo ""
117
+
118
+ if [ "$NETWORK_REGION" = "china" ]; then
119
+ local cdn_result mirror_result
120
+ cdn_result=$(_probe_url_with_retry "$CN_MISE_INSTALL_URL")
121
+ mirror_result=$(_probe_url_with_retry "$CN_RUBYGEMS_URL")
122
+
123
+ _print_probe_result "CN CDN (mise/Ruby)" "$cdn_result"
124
+ _print_probe_result "Aliyun (gem)" "$mirror_result"
125
+
126
+ local cdn_ok=false mirror_ok=false
127
+ ! _is_slow_or_unreachable "$cdn_result" && cdn_ok=true
128
+ ! _is_slow_or_unreachable "$mirror_result" && mirror_ok=true
129
+
130
+ if [ "$cdn_ok" = true ] || [ "$mirror_ok" = true ]; then
131
+ USE_CN_MIRRORS=true
132
+ MISE_INSTALL_URL="$CN_MISE_INSTALL_URL"
133
+ RUBYGEMS_INSTALL_URL="$CN_RUBYGEMS_URL"
134
+ NPM_REGISTRY_URL="$CN_NPM_REGISTRY"
135
+ NODE_MIRROR_URL="$CN_NODE_MIRROR_URL"
136
+ RUBY_VERSION_SPEC="ruby@3.4.8"
137
+ print_info "CN mirrors applied"
138
+ else
139
+ print_warning "CN mirrors unreachable — falling back to global sources"
140
+ fi
141
+ else
142
+ local rubygems_result mise_result
143
+ rubygems_result=$(_probe_url_with_retry "$DEFAULT_RUBYGEMS_URL")
144
+ mise_result=$(_probe_url_with_retry "$DEFAULT_MISE_INSTALL_URL")
145
+
146
+ _print_probe_result "RubyGems" "$rubygems_result"
147
+ _print_probe_result "mise.run" "$mise_result"
148
+
149
+ _is_slow_or_unreachable "$rubygems_result" && print_warning "RubyGems is slow/unreachable."
150
+ _is_slow_or_unreachable "$mise_result" && print_warning "mise.run is slow/unreachable."
151
+
152
+ USE_CN_MIRRORS=false
153
+ RUBY_VERSION_SPEC="ruby@3"
154
+ fi
155
+
156
+ echo ""
157
+ }
@@ -0,0 +1,57 @@
1
+ # os.sh — OS/distro detection, command_exists, version_ge
2
+ # Depends-On: colors.sh
3
+ # Requires-Vars: (none)
4
+ # Sets-Vars: $OS $DISTRO
5
+ # Include via: @include lib/os.sh
6
+
7
+ # Sets OS (macOS | Linux | Windows | Unknown) and DISTRO (ubuntu | debian | …)
8
+ detect_os() {
9
+ case "$(uname -s)" in
10
+ Linux*) OS=Linux ;;
11
+ Darwin*) OS=macOS ;;
12
+ CYGWIN*) OS=Windows ;;
13
+ MINGW*) OS=Windows ;;
14
+ *) OS=Unknown ;;
15
+ esac
16
+ print_info "Detected OS: $OS"
17
+
18
+ if [ "$OS" = "Linux" ] && [ -f /etc/os-release ]; then
19
+ # shellcheck source=/dev/null
20
+ . /etc/os-release
21
+ DISTRO=$ID
22
+ print_info "Detected Linux distribution: $DISTRO"
23
+ else
24
+ DISTRO=unknown
25
+ fi
26
+ }
27
+
28
+ # Returns 0 if the given command is on PATH
29
+ command_exists() { command -v "$1" >/dev/null 2>&1; }
30
+
31
+ # Boolean helpers — use these in business logic instead of inline string comparisons
32
+ is_macos() { [ "$OS" = "macOS" ]; }
33
+ is_linux_apt() { [ "$OS" = "Linux" ] && { [ "$DISTRO" = "ubuntu" ] || [ "$DISTRO" = "debian" ]; }; }
34
+
35
+ # Returns 0 (true) if $1 >= $2 (semantic version comparison)
36
+ version_ge() { printf '%s\n%s\n' "$2" "$1" | sort -V -C; }
37
+
38
+ # Assert that the current OS/distro is supported (macOS or Ubuntu/Debian).
39
+ # Optional $1: hint message printed on failure (e.g. manual install instructions).
40
+ # Exits with code 1 on unsupported OS or distro.
41
+ assert_supported_os() {
42
+ local hint="${1:-}"
43
+ if [ "$OS" = "Linux" ]; then
44
+ if [ "$DISTRO" = "ubuntu" ] || [ "$DISTRO" = "debian" ]; then
45
+ return 0
46
+ fi
47
+ print_error "Unsupported Linux distribution: $DISTRO"
48
+ [ -n "$hint" ] && print_info "$hint"
49
+ exit 1
50
+ elif [ "$OS" = "macOS" ]; then
51
+ return 0
52
+ else
53
+ print_error "Unsupported OS: $OS"
54
+ [ -n "$hint" ] && print_info "$hint"
55
+ exit 1
56
+ fi
57
+ }
@@ -0,0 +1,37 @@
1
+ # shell.sh — detect current interactive shell and its rc file
2
+ # Depends-On: colors.sh os.sh
3
+ # Requires-Vars: (none)
4
+ # Sets-Vars: $CURRENT_SHELL $SHELL_RC
5
+ # Include via: @include lib/shell.sh
6
+
7
+ # Sets CURRENT_SHELL (zsh | bash | fish) and SHELL_RC (path to rc file)
8
+ detect_shell() {
9
+ local shell_name
10
+ shell_name=$(basename "$SHELL")
11
+
12
+ case "$shell_name" in
13
+ zsh)
14
+ CURRENT_SHELL="zsh"
15
+ SHELL_RC="$HOME/.zshrc"
16
+ ;;
17
+ fish)
18
+ CURRENT_SHELL="fish"
19
+ SHELL_RC="$HOME/.config/fish/config.fish"
20
+ ;;
21
+ bash)
22
+ CURRENT_SHELL="bash"
23
+ # macOS uses ~/.bash_profile; Linux uses ~/.bashrc
24
+ if is_macos; then
25
+ SHELL_RC="$HOME/.bash_profile"
26
+ else
27
+ SHELL_RC="$HOME/.bashrc"
28
+ fi
29
+ ;;
30
+ *)
31
+ CURRENT_SHELL="bash"
32
+ SHELL_RC="$HOME/.bashrc"
33
+ ;;
34
+ esac
35
+
36
+ print_info "Detected shell: $CURRENT_SHELL (rc file: $SHELL_RC)"
37
+ }
@@ -0,0 +1,174 @@
1
+ #!/bin/bash
2
+ # install.sh — OpenClacky installer
3
+ # Generated from scripts/build/src/install.sh.cc — DO NOT EDIT DIRECTLY
4
+
5
+ set -e
6
+
7
+ # Brand configuration (populated by --brand-name / --command flags)
8
+ BRAND_NAME=""
9
+ BRAND_COMMAND=""
10
+
11
+ @include lib/colors.sh
12
+ @include lib/os.sh
13
+ @include lib/shell.sh
14
+ @include lib/network.sh
15
+ @include lib/apt.sh
16
+
17
+ @include lib/gem.sh
18
+
19
+ # --------------------------------------------------------------------------
20
+ # Ensure Ruby >= 2.6 is available
21
+ # macOS: uses system Ruby or user-installed Ruby
22
+ # Linux: tries apt first; if missing or too old, prints manual install hint
23
+ # --------------------------------------------------------------------------
24
+ check_ruby() {
25
+ command_exists ruby || return 1
26
+ local ver; ver=$(ruby -e 'puts RUBY_VERSION' 2>/dev/null)
27
+ version_ge "$ver" "2.6.0" && { print_success "Ruby $ver — OK"; return 0; }
28
+ print_warning "Ruby $ver too old (need >= 2.6.0)"; return 1
29
+ }
30
+
31
+ ensure_ruby() {
32
+ print_step "Checking Ruby..."
33
+ check_ruby && return 0
34
+
35
+ if is_linux_apt; then
36
+ print_info "Installing Ruby via apt..."
37
+ sudo apt-get install -y ruby ruby-dev 2>/dev/null && check_ruby && return 0
38
+ print_warning "apt Ruby install failed or version too old"
39
+ fi
40
+
41
+ return 1
42
+ }
43
+
44
+ # --------------------------------------------------------------------------
45
+ # gem install openclacky
46
+ # --------------------------------------------------------------------------
47
+ install_via_gem() {
48
+ print_step "Installing ${DISPLAY_NAME} via gem..."
49
+ configure_gem_source
50
+ setup_gem_home
51
+
52
+ local target source_args=()
53
+ if [ "$USE_CN_MIRRORS" = true ]; then
54
+ print_info "Fetching latest version from OSS..."
55
+ local cn_version; cn_version=$(curl -fsSL "$CN_GEM_LATEST_URL" | tr -d '[:space:]')
56
+ print_info "Latest version: ${cn_version}"
57
+ local gem_url="${CN_GEM_BASE_URL}/openclacky-${cn_version}.gem"
58
+ local gem_file="/tmp/openclacky-${cn_version}.gem"
59
+ print_info "Downloading openclacky-${cn_version}.gem..."
60
+ curl -fsSL "$gem_url" -o "$gem_file"
61
+ target="$gem_file"
62
+ source_args=(--source "$CN_RUBYGEMS_URL")
63
+ else
64
+ target="openclacky"
65
+ fi
66
+
67
+ # macOS system Ruby 2.6 has a buggy gem resolver that fails on rouge 4.x.
68
+ # Pre-install a 2.6-compatible rouge to avoid resolver failure.
69
+ local ruby_ver; ruby_ver=$(ruby -e 'puts RUBY_VERSION' 2>/dev/null)
70
+ if [[ "$ruby_ver" == 2.6.* ]]; then
71
+ print_warning "Ruby 2.6 detected — pinning rouge 3.30.0 first"
72
+ gem install rouge -v 3.30.0 --no-document "${source_args[@]}" || { print_error "gem install rouge failed"; return 1; }
73
+ fi
74
+
75
+ if gem install "$target" --no-document "${source_args[@]}"; then
76
+ print_success "${DISPLAY_NAME} installed successfully!"
77
+ return 0
78
+ fi
79
+
80
+ print_error "gem install failed"; return 1
81
+ }
82
+
83
+ # --------------------------------------------------------------------------
84
+ # Parse CLI args
85
+ # --------------------------------------------------------------------------
86
+ parse_args() {
87
+ for arg in "$0" "$@"; do
88
+ case "$arg" in
89
+ --brand-name=*) BRAND_NAME="${arg#--brand-name=}" ;;
90
+ --command=*) BRAND_COMMAND="${arg#--command=}" ;;
91
+ esac
92
+ done
93
+ DISPLAY_NAME="${BRAND_NAME:-OpenClacky}"
94
+ }
95
+
96
+ # --------------------------------------------------------------------------
97
+ # Brand wrapper setup
98
+ # --------------------------------------------------------------------------
99
+ setup_brand() {
100
+ [ -z "$BRAND_NAME" ] && return 0
101
+ local clacky_dir="$HOME/.clacky"
102
+ local brand_file="$clacky_dir/brand.yml"
103
+ mkdir -p "$clacky_dir"
104
+ print_step "Configuring brand: $BRAND_NAME"
105
+ cat > "$brand_file" <<YAML
106
+ product_name: "${BRAND_NAME}"
107
+ package_name: "${BRAND_COMMAND}"
108
+ YAML
109
+ print_success "Brand config written to $brand_file"
110
+
111
+ if [ -n "$BRAND_COMMAND" ]; then
112
+ local clacky_bin bin_dir
113
+ clacky_bin=$(command -v openclacky 2>/dev/null || true)
114
+ if [ -n "$clacky_bin" ]; then
115
+ bin_dir=$(dirname "$clacky_bin")
116
+ else
117
+ print_warning "openclacky binary not found in PATH; skipping wrapper install"
118
+ return 0
119
+ fi
120
+ local wrapper="$bin_dir/$BRAND_COMMAND"
121
+ cat > "$wrapper" <<WRAPPER
122
+ #!/bin/sh
123
+ exec openclacky "\$@"
124
+ WRAPPER
125
+ chmod +x "$wrapper"
126
+ print_success "Wrapper installed: $wrapper"
127
+ fi
128
+ }
129
+
130
+ # --------------------------------------------------------------------------
131
+ # Post-install info
132
+ # --------------------------------------------------------------------------
133
+ show_post_install_info() {
134
+ local cmd="${BRAND_COMMAND:-openclacky}"
135
+ echo ""
136
+ echo -e " ${GREEN}${DISPLAY_NAME} installed successfully!${NC}"
137
+ echo ""
138
+ echo " Reload your shell:"
139
+ echo -e " ${YELLOW}source ${SHELL_RC}${NC}"
140
+ echo ""
141
+ echo -e " ${GREEN}Web UI${NC} (recommended):"
142
+ echo " $cmd server"
143
+ echo " Open http://localhost:7070"
144
+ echo ""
145
+ echo -e " ${GREEN}Terminal${NC}:"
146
+ echo " $cmd"
147
+ echo ""
148
+ }
149
+
150
+ # --------------------------------------------------------------------------
151
+ # Main
152
+ # --------------------------------------------------------------------------
153
+ main() {
154
+ parse_args "$@"
155
+ echo ""
156
+ echo "${DISPLAY_NAME} Installation"
157
+ echo ""
158
+
159
+ detect_os
160
+ detect_shell
161
+ detect_network_region
162
+
163
+ assert_supported_os "Please install Ruby >= 2.6.0 manually and run: gem install openclacky"
164
+
165
+ if [ "$OS" = "Linux" ]; then
166
+ setup_apt_mirror
167
+ fi
168
+
169
+ ensure_ruby || { print_error "Could not install a compatible Ruby"; exit 1; }
170
+ install_via_gem && { setup_brand; show_post_install_info; exit 0; }
171
+ print_error "Failed to install ${DISPLAY_NAME}"; exit 1
172
+ }
173
+
174
+ main "$@"
@@ -0,0 +1,101 @@
1
+ #!/bin/bash
2
+ # install_browser.sh — install Node.js + chrome-devtools-mcp for browser automation
3
+ # Generated from scripts/build/src/install_browser.sh.cc — DO NOT EDIT DIRECTLY
4
+
5
+ set -e
6
+
7
+ @include lib/colors.sh
8
+ @include lib/os.sh
9
+ @include lib/shell.sh
10
+ @include lib/network.sh
11
+ @include lib/mise.sh
12
+
13
+ # Chrome DMG — update CHROME_VERSION when re-uploading a newer build to OSS
14
+ CHROME_VERSION="134"
15
+ DEFAULT_CHROME_DMG_URL="https://dl.google.com/chrome/mac/universal/stable/GGRO/googlechrome.dmg"
16
+ CN_CHROME_DMG_URL="${CN_CDN_BASE_URL}/browsers/googlechrome-mac-${CHROME_VERSION}.dmg"
17
+
18
+ # --------------------------------------------------------------------------
19
+ # Ensure Chrome is installed (macOS only)
20
+ # Downloads DMG to Desktop, opens it, then exits with instructions.
21
+ # Re-run the script after Chrome is installed.
22
+ # --------------------------------------------------------------------------
23
+ ensure_chrome_macos() {
24
+ print_step "Checking Google Chrome..."
25
+
26
+ if [ -d "/Applications/Google Chrome.app" ] || [ -d "$HOME/Applications/Google Chrome.app" ]; then
27
+ print_success "Google Chrome already installed"
28
+ return 0
29
+ fi
30
+
31
+ print_warning "Google Chrome not found — downloading..."
32
+ local dmg_url="$DEFAULT_CHROME_DMG_URL"
33
+ if [ "$USE_CN_MIRRORS" = true ]; then
34
+ dmg_url="$CN_CHROME_DMG_URL"
35
+ print_info "Using OSS mirror (Chrome ${CHROME_VERSION})"
36
+ else
37
+ print_info "Using official Google download"
38
+ fi
39
+
40
+ local dmg_path="$HOME/Desktop/googlechrome.dmg"
41
+ print_info "Downloading Chrome (~238 MB) to Desktop..."
42
+ curl -L --progress-bar "$dmg_url" -o "$dmg_path" || {
43
+ print_error "Download failed"
44
+ print_info "Please download manually: ${dmg_url}"
45
+ exit 1
46
+ }
47
+
48
+ print_success "Downloaded: ${dmg_path}"
49
+ print_info "Opening DMG installer..."
50
+ open "$dmg_path"
51
+ echo ""
52
+ print_info "Drag 'Google Chrome' to Applications, then re-run this script."
53
+ exit 0
54
+ }
55
+
56
+ # --------------------------------------------------------------------------
57
+ # install_chrome_devtools_mcp — npm global install
58
+ # --------------------------------------------------------------------------
59
+ install_chrome_devtools_mcp() {
60
+ print_step "Installing chrome-devtools-mcp..."
61
+
62
+ if [ "$USE_CN_MIRRORS" = true ]; then
63
+ npm config set registry "$NPM_REGISTRY_URL" 2>/dev/null || true
64
+ print_info "npm registry → ${NPM_REGISTRY_URL}"
65
+ fi
66
+
67
+ if npm install -g chrome-devtools-mcp@latest 2>/dev/null; then
68
+ print_success "chrome-devtools-mcp installed"
69
+ else
70
+ print_error "chrome-devtools-mcp installation failed"
71
+ print_info "Try manually: npm install -g chrome-devtools-mcp@latest"
72
+ return 1
73
+ fi
74
+ }
75
+
76
+ # --------------------------------------------------------------------------
77
+ # Main
78
+ # --------------------------------------------------------------------------
79
+ main() {
80
+ echo ""
81
+ echo "Browser Automation Setup"
82
+ echo "========================"
83
+
84
+ detect_os
85
+ detect_shell
86
+ detect_network_region
87
+
88
+ is_macos && ensure_chrome_macos
89
+
90
+ print_step "Checking Node.js..."
91
+ ensure_mise || exit 1
92
+ install_node_via_mise || exit 1
93
+
94
+ install_chrome_devtools_mcp || exit 1
95
+
96
+ echo ""
97
+ print_success "Done. Browser automation is ready."
98
+ echo ""
99
+ }
100
+
101
+ main "$@"