2025年9月2日 星期二
C++/Python3/Go/Rust 關於 list/array/vector 的 sort 在設計上的不同差異
2025年6月30日 星期一
演算法的逆襲:為何 O(N log N) 的排序,有時竟能擊敗 O(N) 的雜湊?
2025年6月29日 星期日
在 Rust 上又遇到了一次跟 C++ 一樣的問題
透過 BROWSER 環境變數在 headless server 上搞定 Gemini CLI 的 Google 登入
嘿,各位開發者!如果你也嘗試在沒有圖形介面的伺服器上使用 npm install -g @google/gemini-cli,並希望透過 Google 帳號登入,你可能遇到了一個小麻煩:「沒有網址讓我連過去認證!」
別擔心,這篇文章將帶你一步步解決這個問題,讓你能在伺服器上順利使用 Gemini CLI。
問題根源:無頭環境的挑戰
當你在本地電腦使用 Google 登入時,CLI 會自動開啟瀏覽器讓你完成認證。但在伺服器這種「無頭 (headless)」環境,它根本沒辦法開啟瀏覽器。這導致了認證流程的中斷。
我們要做的,就是**「欺騙」CLI**,讓它以為有個瀏覽器會幫它處理網址,但實際上我們只是把認證網址攔截下來,然後手動到自己的電腦上完成登入。
步驟一:讓 CLI「吐出」認證網址
首先,我們需要一個簡單的 Shell 指令碼,來捕捉 Gemini CLI 嘗試開啟的認證網址。
-
建立 url-logger.sh 指令碼:
在你的伺服器上,建立一個新檔案,例如放在 /usr/local/bin/url-logger.sh。
nano /usr/local/bin/url-logger.sh
然後貼上以下內容:
#!/bin/bash # 這個指令碼會將 Gemini CLI 嘗試開啟的網址記錄下來 # 認證網址將被寫入這個檔案 URL_FILE="$HOME/gemini_auth_url.txt" # 確保檔案存在 touch "$URL_FILE" # 將網址和時間戳記附加到檔案中 echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$URL_FILE" # 同時印出到終端機,方便查看 echo "URL would be opened: $1"
-
賦予執行權限:
讓這個指令碼可執行。
chmod +x /usr/local/bin/url-logger.sh
步驟二:設定 BROWSER 環境變數
接下來,我們要告訴系統,當任何程式(包括 Gemini CLI)嘗試開啟瀏覽器時,都執行我們剛才建立的 url-logger.sh 指令碼。
-
編輯你的 Shell 設定檔:
這通常是 ~/.bashrc (如果你用 Bash) 或 ~/.zshrc (如果你用 Zsh)。
# 例如: nano ~/.bashrc
-
添加 BROWSER 環境變數:
在檔案的任何位置,加入這行。請確保路徑是 url-logger.sh 的實際存放位置。
export BROWSER=/usr/local/bin/url-logger.sh
-
儲存並重新載入設定:
儲存檔案後,執行 source ~/.bashrc (或 source ~/.zshrc),或者直接關閉並重新開啟你的 SSH 連線,讓設定生效。
步驟三:啟動 Gemini CLI 並獲取認證網址
現在,萬事俱備!我們可以啟動 Gemini CLI 並開始認證流程了。
-
啟動 Gemini CLI:
gemini
進入互動式介面後,輸入 /auth,然後選擇 "Login with Google"。
-
檢查認證網址檔案:
此時,gemini 不會開啟任何視窗,而是把認證網址寫入到我們指定的檔案中。你可以用 cat 命令查看:
cat "$HOME/gemini_auth_url.txt"
你會看到一串類似 https://accounts.google.com/o/oauth2/v2/auth?client_id=... 的超長網址。請完整複製這串網址!
步驟四:在本地電腦完成 Google 認證
現在,輪到你的本地電腦出場了!
-
在本地瀏覽器開啟網址:
把你剛才從伺服器上複製的完整認證網址,貼到你本地電腦的網頁瀏覽器中,然後打開它。
-
完成 Google 登入和授權:
按照 Google 頁面的指示,用你的 Google 帳號登入並授權。完成後,瀏覽器會嘗試跳轉到一個類似 http://localhost:PORT/oauth2callback?code=YOUR_CODE... 的網址。這個頁面會顯示「此網站無法連線」之類的錯誤,這是完全正常的!
步驟五:將 localhost 回調傳回伺服器
這是最關鍵的一步!我們要將本地瀏覽器嘗試跳轉的 localhost 網址(即使它報錯了),再傳回給伺服器上的 gemini CLI。
-
複製 localhost 回調網址:
從你本地瀏覽器的網址列上,完整複製那個 http://localhost:PORT/oauth2callback?... 的網址。務必包含所有的參數!
-
在伺服器上執行 curl 命令:
回到你的伺服器終端機,執行以下 curl 命令,將雙引號內的內容替換成你剛才複製的完整 localhost 網址:
curl "http://localhost:YOUR_PORT/oauth2callback?code=YOUR_CODE&scope=..."
例如:
curl "http://localhost:21165/oauth2callback?code=4/0xxxxxxxxxxxxxxxxx&scope=https://www.googleapis.com/auth/userinfo.email..."
執行完 curl 後,你會看到伺服器上的 gemini CLI 互動介面會顯示認證成功的訊息。恭喜你!
搞定!還有更好的選擇嗎?
你已經成功在無頭伺服器上搞定了 Gemini CLI 的 Google 帳號登入!這證明了只要掌握方法,多麼棘手的環境問題都能克服。
不過,對於長期或自動化用途,我仍然會強烈推薦使用服務帳戶 (Service Account) 進行認證。服務帳戶提供更高的安全性,而且完全不需要人工介入,對於伺服器應用來說是更優雅的解決方案。你可以參考官方文件或相關教學,了解如何建立和使用 Google Cloud 服務帳戶金鑰。
希望這篇文章對你有幫助!Happy coding!
以上內容由 Gemini AI 整理 Gemini CLI 伺服器設定指南 對話內容產出。
2024年12月15日 星期日
C++17 好用的 tuple 的 auto 解構
#include <iostream>
#include <vector>
#include <tuple>
using namespace std;
int main()
{
vector<tuple<int,int,double>> box;
for (int i{1}; i < 10; ++i)
box.push_back(make_tuple(i, i*i, (double)i/(i*i)));
for (auto [idx, square, value]: box)
cout << idx << " " << square << " " << value << endl;
while (!box.empty()) {
auto [idx, square, value] = box.back();
box.pop_back();
cout << idx << " " << square << " " << value << endl;
}
return 0;
}
2024年11月29日 星期五
C++ 的 std::string::size() 使用上要小心的地方
std::string::size() 的回傳會是 size_t 這樣的無號正整數或零的型別。
我在刷 LeetCode 的題目時寫出了這樣的程式碼。
class Solution { public: int strStr(string haystack, string needle) { for (int i = 0; i <= haystack.size() - needle.size(); i++) { // ... } return -1; } };
當 needle 比 haystack 還要長時,其運算結果原以為會是負數,但是因為無號正整數或零的型別,就會被轉成正整數,導致 for 迴圈中會透過 i 去使用到 haystack 或是 needle 以外的記憶體位址,導致程式崩潰。
${shlibs:Depends} 使用上的一些陷阱
其實 0~git202411270842.3c1cdd3 這樣的 Debian Version 會等於 0~git202411270842.3c1cdd3-0
$ dpkg --compare-versions 0~git202411270842.3c1cdd3-0 eq 0~git202411270842.3c1cdd3; echo $? 0
於是 0~git202411270842.3c1cdd3-0~ 這樣的 Debian Version 就會小於 0~git202411270842.3c1cdd3
$ dpkg --compare-versions 0~git202411270842.3c1cdd3-0~ lt 0~git202411270842.3c1cdd3; echo $? 0
例如套件 A 使用了 0~git202411270842.3c1cdd3-0~ 這樣的版號。
陷阱來自於在套件 B 的 debian/control 裡面相依性使用 ${shlibs:Depends} 時,只會找到套件 A 的 upstream version 而已,然後會自動代入 (>= 0~git202411270842.3c1cdd3) 這樣的版號相依。
於是在套件 B 的 Debian packaging 打包後,就會遇到明明套件 B 的 Debian source packages 可以成功編譯成 Debian binary packages,但是卻無法安裝使用的情況。
$ sudo apt install libcamhal-ipu6ep Reading package lists... Done Building dependency tree... Done Reading state information... Done Some packages could not be installed. This may mean that you have requested an impossible situation or if you are using the unstable distribution that some required packages have not yet been created or been moved out of Incoming. The following information may help to resolve the situation: The following packages have unmet dependencies: libcamhal-ipu6ep : Depends: libbroxton-ia-pal-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libgcss-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-aiq-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-aiqb-parser-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-bcomp-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-cca-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-cmc-parser-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-coordinate-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-dvs-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-emd-decoder-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-exc-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-isp-bxt-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-lard-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-log-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-ltm-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-mkn-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed Depends: libia-nvm-ipu6ep0 (>= 0~git202411270842.3c1cdd3) but it is not going to be installed E: Unable to correct problems, you have held broken packages.
至於解決方法就是避免使用比 0~git202411270842.3c1cdd3-0 還要小的版號,像是可以改用 0~git202411270842.3c1cdd3-1~ 這樣的版號就不會產生問題了。
$ dpkg --compare-versions 0~git202411270842.3c1cdd3-1~ gt 0~git202411270842.3c1cdd3; echo $? 0
以上是測試打包 ppa:oem-solutions-group/intel-ipu6 時的一些心得感想。