<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Let's Write</title>
    <description>The latest articles on DEV Community by Let's Write (@letswrite).</description>
    <link>https://dev.to/letswrite</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F879108%2F12d6ce35-abca-4678-a503-fc4600997729.png</url>
      <title>DEV Community: Let's Write</title>
      <link>https://dev.to/letswrite</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/letswrite"/>
    <language>en</language>
    <item>
      <title>MausBot 教學：用 AI Agent 組行銷編輯部，自動撰寫與發布案例</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Thu, 29 Oct 2026 13:12:59 +0000</pubDate>
      <link>https://dev.to/letswrite/mausbot-jiao-xue-yong-ai-agent-zu-xing-xiao-bian-ji-bu-zi-dong-zhuan-xie-yu-fa-bu-an-li-12d9</link>
      <guid>https://dev.to/letswrite/mausbot-jiao-xue-yong-ai-agent-zu-xing-xiao-bian-ji-bu-zi-dong-zhuan-xie-yu-fa-bu-an-li-12d9</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;雖然現在是 AI 時代，但很多事情自己還是得每天一點一點的累積，累積到成為自己的直覺反應，或是即時提供想法。&lt;/p&gt;

&lt;p&gt;最近 August 想建一座像圖書館一樣的「經典行銷案例收藏庫」，每天讓 AI 整理一篇行銷案例，每天看一篇。&lt;/p&gt;

&lt;p&gt;也能隨時翻出來看，並且可以回頭查找。&lt;/p&gt;

&lt;p&gt;每一篇文章只挑一個真實的品牌，講三件事：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;它遇到什麼問題&lt;/li&gt;
&lt;li&gt;做了哪些改變&lt;/li&gt;
&lt;li&gt;我們可以借用哪些做法&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;重點是，要寫得讓沒學過行銷的人也能看懂。&lt;/p&gt;

&lt;p&gt;這件事要長期做下去，其實不輕鬆。&lt;/p&gt;

&lt;p&gt;需要找題目、避免寫到重複的案例，還要核對數字、整理資料來源、把內容寫成文章。&lt;/p&gt;

&lt;p&gt;最後還要檢查錯字、存檔和發表。&lt;/p&gt;

&lt;p&gt;案例一多，還需要能搜尋、收藏、寫筆記，開會想辦法時才拿得出來用。&lt;/p&gt;

&lt;p&gt;所以花了一天時間，用 MausBot 組了一個小團隊，讓不同角色分工：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;選題編輯想題目&lt;/li&gt;
&lt;li&gt;總編輯負責寫稿發表&lt;/li&gt;
&lt;li&gt;工程師負責維護網站&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;案例寫好後，再自動放進自架的閱讀網站上。&lt;/p&gt;

&lt;p&gt;本篇筆記文會分享三件事：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;一開始怎麼分工&lt;/li&gt;
&lt;li&gt;怎麼安排每天的作業流程&lt;/li&gt;
&lt;li&gt;這個網站目前能做什麼&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;最後一段也附上程式碼，大家可以改成自己團隊的版本。&lt;/p&gt;

&lt;p&gt;本專案完成後的介面範例如下：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F57sxbsw5945b7p4v0kh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F57sxbsw5945b7p4v0kh9.png" alt="MausBot 產出之經典行銷案例庫網站介面：首頁文章列表、主題分類與全內文搜尋功能展示" width="799" height="366"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  MausBot 是什麼
&lt;/h2&gt;

&lt;p&gt;MausBot 是一套工具，可以讓你同時請好幾個 AI 助理，替它們分配工作，還能設定成「時間一到就自動開工」。&lt;/p&gt;

&lt;p&gt;如果搭配 OpenCode，可以當成免費的 Grok Bot 在用。&lt;/p&gt;

&lt;p&gt;早期叫 OpenMausBot，官方網站現在使用 MausBot 這個名字，本文也跟著用新名字。&lt;/p&gt;

&lt;p&gt;不過安裝程式和官方的程式碼專案裡，可能還會看到舊名 OpenMausBot，不用擔心，指的是同一套工具。&lt;/p&gt;

&lt;p&gt;我們可以替每個 Agent 挑選要用哪一種 AI，也能限制它能做的事，例如能不能新增、修改檔案。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkyp8cba4ty9wk73cydyk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkyp8cba4ty9wk73cydyk.jpg" alt="MausBot 系統設定介面：配置 AI 語言模型與檔案讀寫權限操作範例" width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在這個專案裡，我們會建立一個 Marketing 團隊，一開始先把需求告訴總編輯，總編輯會去協調選題編輯和工程師，最後再把結果整理好後，進行回報，並且是每個工作日早上自動執行。&lt;/p&gt;




&lt;h2&gt;
  
  
  用 MausBot 組出 Marketing 團隊
&lt;/h2&gt;

&lt;p&gt;下載、安裝 MausBot：&lt;a href="https://mausbot.com/" rel="noopener noreferrer"&gt;https://mausbot.com/&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;點擊連結，進到官網，再點擊 Download for … 的按鈕進行下載，然後安裝。&lt;/p&gt;

&lt;p&gt;官網下方也有操作介面的範例，放心，介面有繁體中文版。&lt;/p&gt;

&lt;p&gt;如果要使用免費的模型，那要再下載、安裝 OpenCode：&lt;a href="https://opencode.ai/zht/download" rel="noopener noreferrer"&gt;https://opencode.ai/zht/download&lt;/a&gt;。&lt;/p&gt;

&lt;h3&gt;
  
  
  先建立三位「員工」Agents
&lt;/h3&gt;

&lt;p&gt;安裝完 MausBot，並設定好模型後，我們先建一個團隊，取名為 Marketing，然後先建立一位名為「總編輯」的 Agent，放進團隊裡。&lt;/p&gt;

&lt;p&gt;然後有趣的就來了，我們還需要兩位 Agents：選題編輯、工程師。&lt;/p&gt;

&lt;p&gt;我們需要自己手動建立嗎？不用，直接跟「總編輯」說出我們的需求，接著再跟它說「可以自行建立需要的 Agents」。&lt;/p&gt;

&lt;p&gt;用 Agent 建 Agents 時，如果大家有安裝 &lt;a href="https://www.letswrite.tw/agency-agents/" rel="noopener noreferrer"&gt;Agency Agents&lt;/a&gt; 的話，可以多補一句「可參考 Agency Agents 的設定來建立」。&lt;/p&gt;

&lt;p&gt;本篇的三位角色，除了總編輯，其他兩位 Agents 就是總編輯建立出來的。&lt;/p&gt;

&lt;p&gt;以下是可以跟總編輯說的範例 Prompt，大家再依自己的情況調整：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;幫我建立一個每天發表行銷案例的團隊，包含選題編輯與工程師。選題編輯負責想題目、提供資料來源，並做第二次檢查。你是總編輯，負責檢查有沒有寫重複、核對事實、寫稿與最後校對，通過後才發表。工程師負責建立、維護案例網站。所有角色都使用正體中文、台灣用語。請先完成一次由我人工確認的試發表，再安排固定時間自動發表。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;這只是建立團隊的範例，實際使用時，還可設定工作資料夾（團隊放檔案的地方）、要用哪一種 AI，以及允許它存檔和發表的權限。&lt;/p&gt;

&lt;p&gt;Agents 的名稱、大頭貼都可以自己隨意取，本篇的只是範例，像 August 最後建立的，就是一個哈利波特的團隊：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0vnq047hthwy02ufjbx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0vnq047hthwy02ufjbx.png" alt="哈利波特團隊" width="387" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  把文章的規矩寫清楚
&lt;/h3&gt;

&lt;p&gt;因為目標是希望沒有行銷背景的人，也能看懂品牌是怎麼做的，所以以下是又再跟總編輯設定的規則：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;文章要先講「它做了什麼」，再說「為什麼可能有效」。&lt;/li&gt;
&lt;li&gt;專業名詞每篇最多解釋三個，免得讀者一直停下來查字。&lt;/li&gt;
&lt;li&gt;文中重要的數字和引用的話，都要回到原始出處核對，如果找不到根據，就老實寫出資料缺口，或乾脆拿掉那個說法。&lt;/li&gt;
&lt;li&gt;每篇最後都有一段「可以怎麼落地應用」，把案例接回我們自己的產品和會員經營。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;可以把這最後一段換成「我們的品牌可以怎麼用」，再說明想達成的目標，例如讓更多人註冊、再次購買或續訂。&lt;/p&gt;

&lt;h3&gt;
  
  
  讓每天的作業有固定順序
&lt;/h3&gt;

&lt;p&gt;我們可以再跟總編輯討論，把流程分成五個階段的 SOP：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;確認與選題：&lt;/strong&gt; 總編輯先看今天的選題是不是已經發表過？還沒有的話，請選題編輯提出品牌、案例主題和選這一題的理由，並再次檢查有沒有和舊文章重複。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;核對與寫稿：&lt;/strong&gt; 選題編輯提供資料來源和素材，總編輯核對重要的說法，再整理成符合文章規矩的內容。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;兩次檢查：&lt;/strong&gt; 總編輯先自己檢查一遍，再交給選題編輯檢查內容、資料來源、格式、多餘的引用符號，還有文章在案例網站上實際顯示的分段。總編輯依意見修改後，再做最後一次校對。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;存檔與發表：&lt;/strong&gt; 定稿按日期存成一個文字檔（Markdown 格式），確認沒有蓋掉已經發表過的那一天，再依事先授權好的流程發表。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;上線後檢查：&lt;/strong&gt; 確認網站真的看得到新文章，並回報完成了什麼、有沒有出問題。如果選題編輯沒有回覆，或畫面查不到，要清楚列出「哪些檢查還沒做」。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;SOP 有了以後，再讓總編輯設定每個工作日的台灣時間早上 08:30 自動開工，由總編輯啟動流程。&lt;/p&gt;

&lt;p&gt;就算設定好了排程，我們自己也要注意：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;執行的那台電腦、網路和 AI 服務都要正常，而且在排程執行的期間，電腦不能是休眠狀態。&lt;/li&gt;
&lt;li&gt;設了時間，不代表一定會做完，還是要回頭看實際結果。&lt;/li&gt;
&lt;li&gt;如果電腦離線或工作中途斷掉，就讓總編輯去排查失敗原因。&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  本篇筆記文的案例網站能做什麼
&lt;/h2&gt;

&lt;p&gt;團隊寫好文章後，會把檔案放進專案裡依年、月、日整理的資料夾（路徑是 &lt;code&gt;articles/年/月/日.md&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;整個專案分成兩半，MausBot 團隊負責「做內容」，案例網站負責「展示文章、記住你的收藏、寄出通知信」。&lt;/p&gt;

&lt;h3&gt;
  
  
  案例列表與搜尋
&lt;/h3&gt;

&lt;p&gt;首頁按日期列出所有案例，每一筆都有標題和主題。&lt;/p&gt;

&lt;p&gt;右邊會整理出最新一期，以及每個主題各有幾篇。&lt;/p&gt;

&lt;p&gt;搜尋時，網站會比對日期、標題、主題，還有整篇文章的每一個字。&lt;/p&gt;

&lt;p&gt;比如：想找和會員有關的案例，就輸入「會員」。&lt;/p&gt;

&lt;p&gt;如果記得品牌名稱，也可以直接搜品牌。&lt;/p&gt;

&lt;h3&gt;
  
  
  閱讀頁面與資料來源
&lt;/h3&gt;

&lt;p&gt;打開文章，頁面會整理出重點和目錄，點一下就跳到那一段。&lt;/p&gt;

&lt;p&gt;往下捲的時候，目錄會標出你現在讀到哪裡。&lt;/p&gt;

&lt;p&gt;資料來源放在畫面旁邊的欄位，讀到一半想核對時，不用翻到文章最後。&lt;/p&gt;

&lt;p&gt;網站也有一本「名詞解釋」：文章裡出現字典有收的專業名詞，就會附上解釋。&lt;/p&gt;

&lt;p&gt;另外還有「上一篇、下一篇」、淺色與深色兩種畫面，版面也會配合手機、平板或電腦的螢幕大小調整。&lt;/p&gt;

&lt;h3&gt;
  
  
  會員收藏、筆記與匯出
&lt;/h3&gt;

&lt;p&gt;如果有註冊會員，登入後可以收藏案例，也能寫下自己的筆記。&lt;/p&gt;

&lt;p&gt;舉例來說，讀完一篇會員留存案例（講品牌怎麼讓會員願意留下來繼續使用），可以記下「適合下次續訂活動討論」，之後從收藏清單把它找回來。&lt;/p&gt;

&lt;p&gt;收藏還可以匯出成一份 Markdown 檔（一種純文字的筆記格式），內容有標題、文章連結、日期和你的筆記，方便貼進你慣用的筆記軟體。&lt;/p&gt;

&lt;h3&gt;
  
  
  新文章通知信
&lt;/h3&gt;

&lt;p&gt;會員可以自己決定要不要收新文章的通知。&lt;/p&gt;

&lt;p&gt;網站管理者先設定好寄信服務（SMTP，簡單說就是網站專屬的「寄信郵差」），網站運作期間只要有新文章，就會寄給開啟通知的會員。&lt;/p&gt;

&lt;p&gt;信件同時準備了有排版的版本和純文字版本，並放入整篇文章，讓讀者打開信箱就能直接讀。&lt;/p&gt;

&lt;h3&gt;
  
  
  瀏覽數與收藏數
&lt;/h3&gt;

&lt;p&gt;每篇文章都有瀏覽數和收藏數，可以看出哪些案例比較受關注。&lt;/p&gt;

&lt;p&gt;瀏覽數的算法是：同一位讀者、同一篇文章、同一天，只算一次。&lt;/p&gt;

&lt;p&gt;這裡的「一天」是以世界標準時間（UTC）換日，換成台灣時間（UTC+8），固定為早上 8 點整換日。&lt;/p&gt;

&lt;p&gt;不過，如果讀者清除了 cookie（網站留在瀏覽器裡、用來認人的小記號）、換了瀏覽器，或登入、登出，網站可能會把他當成另一個人，多算一次。&lt;/p&gt;

&lt;p&gt;收藏數則是這篇文章目前有幾位會員收藏。&lt;/p&gt;




&lt;h2&gt;
  
  
  下載程式碼
&lt;/h2&gt;

&lt;p&gt;GitHub 專案連結：&lt;a href="https://github.com/letswritetw/letswrite-mausbot-marketing-case" rel="noopener noreferrer"&gt;letswrite-mausbot-marketing-case&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;取用前請先對專案點個星星或是分享到社群，你的小小動作對本站都是大大的鼓勵。&lt;/p&gt;

&lt;p&gt;這次整理的下載包裡有：案例網站的程式（你看到的畫面，加上背後負責記資料的部分）、一鍵啟動網站的設定（用 Docker，下面會說明），以及 MausBot 的角色設定和每天作業步驟的範本。&lt;/p&gt;

&lt;p&gt;裡面也附了兩篇虛構的示範文章，讓你先看看網站長什麼樣子。&lt;/p&gt;

&lt;p&gt;第一次使用時，要自己做基本設定，如果想用通知信功能，再填入你自己的寄信帳號資料（SMTP）。&lt;/p&gt;

&lt;p&gt;網站用的技術很精簡，不用事先裝一大堆工具，只需要先安裝 Docker 和它的 Compose 功能，然後在專案資料夾輸入下面這行指令：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 複製環境變數範本並修改（若有通知信需求請填寫 SMTP 資訊）&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env

&lt;span class="c"&gt;# 建置並啟動服務&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接著用瀏覽器打開 &lt;code&gt;http://localhost:8801/&lt;/code&gt;，就能讀到示範文章。&lt;/p&gt;

&lt;p&gt;建議照下面的順序開始：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;先啟動示範網站：&lt;/strong&gt; 確認 2 篇示範文章都能正常打開。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;建立自己的案例團隊：&lt;/strong&gt; 參考 &lt;code&gt;docs/mausbot-team-template.md&lt;/code&gt;，在 MausBot 設定角色與工作資料夾。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;手動做一次試發表：&lt;/strong&gt; 檢查資料來源、校對、存檔和網頁顯示都沒問題，再決定要授權團隊做哪些發表動作。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;設定固定時間自動開工：&lt;/strong&gt; 把發表工作集中在一個角色，並保留「今天已經發表過就跳過」的檢查。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;需要時再開通知：&lt;/strong&gt; 設定寄信服務，先寄給自己的測試信箱，確認內容和連結都正確。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;大家可以先照著範本做，再把案例主題、文章規矩和最後的應用段落，改成自己品牌的需求。&lt;/p&gt;

</description>
      <category>mausbot</category>
      <category>ai</category>
      <category>agents</category>
      <category>marketing</category>
    </item>
    <item>
      <title>Windows 安裝 Gitea Runner 教學：支援 MSBuild 建置、註冊與開機自動啟動設定</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Sat, 17 Oct 2026 05:42:48 +0000</pubDate>
      <link>https://dev.to/letswrite/windows-an-zhuang-gitea-runner-jiao-xue-zhi-yuan-msbuild-jian-zhi-zhu-ce-yu-kai-ji-zi-dong-qi-dong-she-ding-2dfb</link>
      <guid>https://dev.to/letswrite/windows-an-zhuang-gitea-runner-jiao-xue-zhi-yuan-msbuild-jian-zhi-zhu-ce-yu-kai-ji-zi-dong-qi-dong-she-ding-2dfb</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;當我們想在 Push 程式碼後自動執行建置、測試或掃描，卻發現專案依賴 Windows MSBuild、傳統 .NET Framework Web 或其他 Windows 專用環境時，就需要一台 Windows 主機來執行這些工作。&lt;/p&gt;

&lt;p&gt;這時，我們就需要在 Windows 主機上安裝 Gitea Runner，讓 Gitea Actions 把工作交給它。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;如果你要看的不是在 Windows 上安裝 Gitea Runner，而是用 Docker 安裝就可以的話，請移駕下面這篇，有開一個段落寫怎麼用 Docker 安裝：&lt;br&gt;
&lt;a href="https://www.letswrite.tw/gitea-ai-code-review/" rel="noopener noreferrer"&gt;使用 Gitea Actions 與 OpenAI 實現自動化 PR Code Review&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gitea 負責安排工作，Runner 負責執行，本筆記文使用直接執行 &lt;code&gt;gitea-runner.exe&lt;/code&gt; 的方式，不需要安裝 Docker。&lt;/p&gt;

&lt;p&gt;以下以需要 MSBuild 的 .NET 專案示範，建立一個讓多個專案共用的 Windows Runner。&lt;/p&gt;

&lt;p&gt;範例設定如下，請把 Gitea 網址換成你的實際網址：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;項目&lt;/th&gt;
&lt;th&gt;本文範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gitea 網址&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://gitea.example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runner 資料夾&lt;/td&gt;
&lt;td&gt;&lt;code&gt;D:\August\GiteaRunner&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runner 名稱&lt;/td&gt;
&lt;td&gt;&lt;code&gt;letswrite-windows-runner&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;註冊 Label&lt;/td&gt;
&lt;td&gt;&lt;code&gt;windows:host&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runner 版本&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;5.0.0&lt;/code&gt;，Windows AMD64&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;如果主機沒有 D 槽，可改用 &lt;code&gt;C:\August\GiteaRunner&lt;/code&gt;，然後後面寫到的指令、啟動腳本與排程中的資料夾路徑都要一起替換。&lt;/p&gt;

&lt;p&gt;全程使用同一個 Windows 帳號註冊、啟動與設定排程，安裝工具時才使用管理員權限。&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 準備 Windows 上的工具
&lt;/h2&gt;

&lt;p&gt;Runner 只會接工作，建置工具要另外安裝在同一台 Windows 主機。&lt;/p&gt;

&lt;p&gt;如果已有符合需求的工具，可以沿用。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;工具&lt;/th&gt;
&lt;th&gt;用途與安裝重點&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://git-scm.com/downloads/win" rel="noopener noreferrer"&gt;Git for Windows&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;下載專案程式碼與 Actions，需 2.34.1 以上；安裝時選擇加入 PATH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://nodejs.org/en/download/archive/v24" rel="noopener noreferrer"&gt;Node.js 24 LTS，Windows x64&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;執行 JavaScript Actions；使用安裝程式並保留加入 PATH 的選項&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://visualstudio.microsoft.com/downloads/" rel="noopener noreferrer"&gt;Visual Studio Build Tools&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;建置需要 MSBuild 的專案；傳統 ASP.NET Web 專案還要選 Web development build tools／Web 開發建置工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;專案使用的 .NET 開發工具&lt;/td&gt;
&lt;td&gt;.NET Framework 專案安裝對應的 Developer Pack／targeting pack；.NET 專案則安裝對應的 SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PATH 是命令列用來找程式的清單，加入 PATH 後，Runner 才能用 &lt;code&gt;git&lt;/code&gt;、&lt;code&gt;node&lt;/code&gt; 等名稱找到程式。&lt;/p&gt;

&lt;p&gt;舊專案要先確認 Visual Studio 版本支援目標框架，必要時使用 &lt;a href="https://visualstudio.microsoft.com/vs/older-downloads/" rel="noopener noreferrer"&gt;VS 2019 Build Tools&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;例如專案目標是 .NET Framework 4.8，就需要 4.8 的 targeting pack，只有 Runtime 不夠。&lt;/p&gt;

&lt;p&gt;安裝完成後，按 &lt;code&gt;Win + R&lt;/code&gt;，輸入 &lt;code&gt;powershell&lt;/code&gt;，按 Enter。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;本文指令都在 PowerShell 執行。&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="bp"&gt;$PSVersionTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PSVersion&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git、Node 應顯示版本，Git 至少為 2.34.1，Windows PowerShell 至少為 5.1。&lt;/p&gt;

&lt;p&gt;若顯示找不到指令，先重新開啟 PowerShell 後再試一次，如果又找不到，要先確認該工具已安裝並加入 PATH。&lt;/p&gt;

&lt;p&gt;同時，這台 Windows 主機也要能連到我們的 Gitea，以及 workflow 使用的 action、套件與工具下載來源。&lt;/p&gt;




&lt;h2&gt;
  
  
  2. 下載 Windows Runner
&lt;/h2&gt;

&lt;p&gt;本文以 &lt;code&gt;5.0.0&lt;/code&gt; 示範。&lt;/p&gt;

&lt;p&gt;Runner 更新頻繁，部署前請到官方下載頁確認最新版本。&lt;/p&gt;

&lt;p&gt;換版本時，下面的檔名、下載連結與校驗指令都要一起替換。&lt;/p&gt;

&lt;p&gt;Gitea 需為 1.21 以上。&lt;/p&gt;

&lt;p&gt;在 PowerShell 建立資料夾：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'D:\August\GiteaRunner'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Set-Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'D:\August\GiteaRunner'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;下載以下兩個檔案，放進這個資料夾：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.gitea.com/gitea-runner/5.0.0/gitea-runner-5.0.0-windows-amd64.exe" rel="noopener noreferrer"&gt;Windows AMD64 Runner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.gitea.com/gitea-runner/5.0.0/gitea-runner-5.0.0-windows-amd64.exe.sha256" rel="noopener noreferrer"&gt;對應的 SHA256 校驗檔&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;校驗檔用來比對下載內容是否完整，貼上以下的指令，完成比對後，程式會將檔案改名為 &lt;code&gt;gitea-runner.exe&lt;/code&gt;：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="n"&gt;ActionPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Stop'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$expectedHash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.\gitea-runner-5.0.0-windows-amd64.exe.sha256'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-split&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'\s+'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$actualHash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-FileHash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.\gitea-runner-5.0.0-windows-amd64.exe'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Algorithm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Hash&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$expectedHash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-notmatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'^[0-9a-fA-F]{64}$'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$actualHash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$expectedHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'校驗失敗；先停止操作，重新下載兩個官方檔案。'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.\gitea-runner.exe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'已有 gitea-runner.exe；先確認既有安裝，不要覆蓋。'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Rename-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.\gitea-runner-5.0.0-windows-amd64.exe'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'gitea-runner.exe'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\gitea-runner.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;最後應顯示版本 &lt;code&gt;5.0.0&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果出現紅色錯誤，先修正再繼續。&lt;/p&gt;

&lt;p&gt;已完成校驗與改名時，直接執行 &lt;code&gt;.\gitea-runner.exe --version&lt;/code&gt; 確認即可。&lt;/p&gt;

&lt;p&gt;Runner 是命令列工具，雙擊 EXE 時若看到「You need to open cmd.exe」，請回到 PowerShell 執行上述指令。&lt;/p&gt;




&lt;h2&gt;
  
  
  3. 註冊一個多專案共用的 Runner
&lt;/h2&gt;

&lt;p&gt;若要讓多個專案共用，請用 Gitea 全站管理員帳號開啟：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gitea.example.com/-/admin/actions/runners
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;先換成自己的 Gitea 網址，再按「建立 Runner」，取得 &lt;strong&gt;Registration Token（註冊碼）&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;註冊碼決定 Runner 的使用範圍：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;全站管理頁取得的註冊碼可建立全站 Runner。&lt;/li&gt;
&lt;li&gt;單一專案的註冊碼只供該專案使用。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;若只想讓某個組織共用，也可以在該組織的 Settings → Actions → Runners 取得註冊碼。&lt;/p&gt;

&lt;p&gt;回到 PowerShell，把 &lt;code&gt;--instance&lt;/code&gt; 換成你的 Gitea 網址後貼上。&lt;/p&gt;

&lt;p&gt;出現「貼上註冊碼後按 Enter」時，貼上剛取得的註冊碼：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="n"&gt;ActionPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Stop'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Set-Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'D:\August\GiteaRunner'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.\.runner'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'這個資料夾已經註冊過；先確認既有 Runner。'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;GITEA_RUNNER_REGISTRATION_TOKEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Read-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'貼上註冊碼後按 Enter'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\gitea-runner.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--no-interactive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'https://gitea.example.com'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'letswrite-windows-runner'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;--labels&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'windows:host'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;finally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Remove-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Env:GITEA_RUNNER_REGISTRATION_TOKEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.0.0 只執行 &lt;code&gt;register&lt;/code&gt;、不帶參數時，不會詢問註冊碼，而是直接顯示 &lt;code&gt;missing token, token-file argument, or GITEA_RUNNER_REGISTRATION_TOKEN environment variable&lt;/code&gt; 並結束。&lt;/p&gt;

&lt;p&gt;所以這裡改用環境變數傳入註冊碼，其他設定直接寫在指令裡。&lt;/p&gt;

&lt;p&gt;註冊碼只存在這次執行中，不會留在指令紀錄。&lt;/p&gt;

&lt;p&gt;畫面出現 &lt;code&gt;WARN Runner in user-mode.&lt;/code&gt; 是正常訊息，表示用一般帳號執行。&lt;/p&gt;

&lt;p&gt;看到 &lt;code&gt;Runner registered successfully.&lt;/code&gt; 就是註冊成功。&lt;/p&gt;

&lt;p&gt;註冊成功後，資料夾會出現 &lt;code&gt;.runner&lt;/code&gt;。它可能是隱藏檔，裡面含憑證，請保留在本機、限制讀取權限，不要提交到 Git 或貼出內容。&lt;/p&gt;

&lt;p&gt;如果之前註冊成單一專案 Runner，只改名稱不會變成全站共用。&lt;/p&gt;

&lt;p&gt;要先停止 Runner 與排程、在 Gitea 移除舊 Runner，再把本機 &lt;code&gt;.runner&lt;/code&gt; 改名備份，使用新的全站註冊碼重新註冊。&lt;/p&gt;




&lt;h2&gt;
  
  
  4. 找到建置工具，再啟動 Runner
&lt;/h2&gt;

&lt;p&gt;先在 PowerShell 查詢 &lt;code&gt;VsDevCmd.bat&lt;/code&gt; 的路徑，它會準備 Visual Studio 的命令列建置環境：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${env:ProgramFiles(x86)}&lt;/span&gt;&lt;span class="s2"&gt;\Microsoft Visual Studio\Installer\vswhere.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-latest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-requires&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Microsoft.Component.MSBuild&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Common7\Tools\VsDevCmd.bat'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;路徑會依安裝的版本與版次而不同，例如 VS 2022 Build Tools 會是：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;請使用你查到的實際路徑，若沒有輸出，要確認 Build Tools 已安裝。&lt;/p&gt;

&lt;p&gt;若專案限定 VS 2019，可在查詢中加入 &lt;code&gt;-version '[16.0,17.0)'&lt;/code&gt;，避免選到其他版本。&lt;/p&gt;

&lt;p&gt;用記事本建立 &lt;code&gt;D:\August\GiteaRunner\run-runner.cmd&lt;/code&gt;，儲存類型選「所有檔案」，避免存成 &lt;code&gt;.cmd.txt&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;貼入以下內容，將 &lt;code&gt;call&lt;/code&gt; 那行換成剛才查到的路徑，保留雙引號：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;@echo &lt;span class="na"&gt;off&lt;/span&gt;
&lt;span class="nb"&gt;setlocal&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="na"&gt;/d &lt;/span&gt;&lt;span class="s2"&gt;"D:\August\GiteaRunner"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;errorlevel&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="k"&gt;exit&lt;/span&gt; &lt;span class="na"&gt;/b &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="s2"&gt;"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;errorlevel&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="k"&gt;exit&lt;/span&gt; &lt;span class="na"&gt;/b &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="s2"&gt;"D:\August\GiteaRunner\gitea-runner.exe"&lt;/span&gt; &lt;span class="kd"&gt;daemon&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"D:\August\GiteaRunner\runner.log"&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;exit&lt;/span&gt; &lt;span class="na"&gt;/b &lt;/span&gt;&lt;span class="nv"&gt;%errorlevel%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;回到 PowerShell 啟動：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Set-Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'D:\August\GiteaRunner'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\run-runner.cmd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這個視窗會持續執行，回到 Gitea 管理頁，確認 &lt;code&gt;letswrite-windows-runner&lt;/code&gt; 顯示 &lt;strong&gt;Online&lt;/strong&gt;，Label 為 &lt;code&gt;windows&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;若程式立即結束，先確認 &lt;code&gt;VsDevCmd.bat&lt;/code&gt; 路徑，再另開 PowerShell 查看紀錄：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'D:\August\GiteaRunner\runner.log'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;排查時，不要公開 &lt;code&gt;.runner&lt;/code&gt; 或含憑證的紀錄。&lt;/p&gt;

&lt;p&gt;共用 Runner 會直接執行專案程式碼，請用一般帳號運行，並只供可信任的專案使用。&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 設定開機自動啟動
&lt;/h2&gt;

&lt;p&gt;先在手動啟動的視窗按 &lt;code&gt;Ctrl + C&lt;/code&gt; 停止 Runner，避免同一份註冊資料同時執行兩次。&lt;/p&gt;

&lt;p&gt;開啟 Windows「工作排程器」→「建立工作」，依下表設定：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;頁籤&lt;/th&gt;
&lt;th&gt;設定&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;一般&lt;/td&gt;
&lt;td&gt;名稱 &lt;code&gt;Gitea-Windows-Runner&lt;/code&gt;，使用註冊時的同一 Windows 帳號。選「不論使用者登入與否都執行」。不用 SYSTEM，也不勾最高權限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;觸發程序&lt;/td&gt;
&lt;td&gt;開機時，延遲 1 分鐘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;動作&lt;/td&gt;
&lt;td&gt;程式 &lt;code&gt;C:\Windows\System32\cmd.exe&lt;/code&gt;，引數 &lt;code&gt;/d /c D:\August\GiteaRunner\run-runner.cmd&lt;/code&gt;，起始位置 &lt;code&gt;D:\August\GiteaRunner&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;條件&lt;/td&gt;
&lt;td&gt;不限定閒置。筆電取消電池相關的停止條件，主機保持不休眠&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;設定&lt;/td&gt;
&lt;td&gt;允許隨選執行。失敗後每 1 分鐘重啟，最多 3 次。取消執行時間上限。已執行時選「不要啟動新的執行個體」&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;儲存時，在 Windows 提示視窗輸入該帳號的密碼。不要勾「不儲存密碼／僅能存取本機資源」，讓排程可以使用該帳號的網路資源。&lt;/p&gt;

&lt;p&gt;右鍵執行工作，確認 Runner 回到 Online。&lt;/p&gt;

&lt;p&gt;安排時間重新開機，再確認尚未登入時也能 Online。&lt;/p&gt;

&lt;p&gt;若帳號權限導致排程失敗，請管理員檢查「作為批次工作登入」權限。&lt;/p&gt;

&lt;p&gt;主機保持開機且不休眠時，Runner 才能接工作。&lt;/p&gt;

&lt;p&gt;後續各專案的 Windows 工作指定 &lt;code&gt;runs-on: windows&lt;/code&gt;，並使用 &lt;code&gt;shell: powershell&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;例如下面是一段 job 設定，放進你既有 workflow 的 &lt;code&gt;jobs&lt;/code&gt; 底下：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;windows-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;確認 Windows 執行環境&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;$PSVersionTable.PSVersion&lt;/span&gt;
        &lt;span class="s"&gt;git --version&lt;/span&gt;
        &lt;span class="s"&gt;node --version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Runner Online 表示已經能接工作。&lt;/p&gt;

&lt;p&gt;Push 何時觸發、要建置或掃描哪個專案，仍由各專案 &lt;code&gt;.gitea/workflows/&lt;/code&gt; 裡的 workflow 決定。&lt;/p&gt;

&lt;p&gt;共用 Runner 只需安裝一次，各專案各自保留自己的指令與 Secrets。&lt;/p&gt;

&lt;p&gt;本文示範直接在 Windows 主機執行的工作。&lt;/p&gt;

&lt;p&gt;若 workflow 另外使用 &lt;code&gt;container&lt;/code&gt;、&lt;code&gt;services&lt;/code&gt; 或 &lt;code&gt;docker://&lt;/code&gt; action，仍須準備對應的 Docker 環境。&lt;/p&gt;

</description>
      <category>gitea</category>
      <category>windows</category>
      <category>sonarqube</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>把 Claude Code 改成自己的樣子：用 Mod 做出用量顯示條與待辦側欄工具</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Tue, 06 Oct 2026 12:38:46 +0000</pubDate>
      <link>https://dev.to/letswrite/ba-claude-code-gai-cheng-zi-ji-de-yang-zi-yong-mod-zuo-chu-yong-liang-xian-shi-tiao-yu-dai-ban-ce-lan-gong-ju-2j8h</link>
      <guid>https://dev.to/letswrite/ba-claude-code-gai-cheng-zi-ji-de-yang-zi-yong-mod-zuo-chu-yong-liang-xian-shi-tiao-yu-dai-ban-ce-lan-gong-ju-2j8h</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;用 AI 開發時，常常想知道這 2 件事：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 小時額度還剩多少？&lt;/li&gt;
&lt;li&gt;開過這些 session 裡，哪些專案還有事情沒做完？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;這個月 Claude Code 推出了 Mod 的機制，可以在介面上補上自己想要的小工具。&lt;/p&gt;

&lt;p&gt;這篇筆記文章先簡單介紹 Mod，再介紹解決上面 2 個需求的模組：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;放在輸入框上方的用量條 &lt;code&gt;token-usage&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;放在右側欄的待辦面板 &lt;code&gt;open-todos&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Claude Mods 是什麼
&lt;/h2&gt;

&lt;p&gt;Mod 是我們裝進 Claude Code 的小外掛，可以讓它多出新功能。&lt;/p&gt;

&lt;p&gt;想像一台樂高小火車。&lt;/p&gt;

&lt;p&gt;火車本來就會跑，我們想加一盞燈、一個喇叭，就拿新積木喀嚓裝上去。&lt;/p&gt;

&lt;p&gt;Claude Code 是那台火車，Mod 是我們多裝的積木。&lt;/p&gt;

&lt;p&gt;Mod 可以做出這幾種東西：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;我們想要&lt;/th&gt;
&lt;th&gt;Mod 做出來的東西&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;旁邊有一個一直顯示資訊的小視窗&lt;/td&gt;
&lt;td&gt;Pane（面板）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;輸入框上面多一條提示列&lt;/td&gt;
&lt;td&gt;Band（橫條）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;底下一行小字顯示狀態&lt;/td&gt;
&lt;td&gt;Status line（狀態列）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;跳出一個小通知&lt;/td&gt;
&lt;td&gt;Toast（提示氣泡）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自己發明一個 &lt;code&gt;/指令&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Slash command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude 做某件事之前先攔下來檢查&lt;/td&gt;
&lt;td&gt;Hook（鉤子）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mod 的觸發邏輯就像是：「當這件事發生，就做那件事。」&lt;/p&gt;

&lt;p&gt;比如一個門鈴，有人按就叮咚。&lt;/p&gt;

&lt;p&gt;而 Claude 要刪檔案時先問一聲，或你送出訊息時在底下顯示提示，都是這種寫法。&lt;/p&gt;

&lt;p&gt;另一個很好的特性是熱重載，當我們改了 Mod，不用重開 Claude Code，畫面馬上換成新的，這省下大量等待時間。&lt;/p&gt;

&lt;p&gt;一個 Mod 就是一個資料夾，裡面有 3 個檔案：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-mod/
├── .claude-plugin/plugin.json   名字卡：我叫什麼、做什麼
├── hooks/hooks.json             地圖：程式檔放在哪
└── hooks/register.tsx           真正做事的程式
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;寫完之後，可以用 &lt;code&gt;claude plugin validate &amp;lt;資料夾&amp;gt;&lt;/code&gt; 檢查有沒有寫錯。&lt;/p&gt;




&lt;h2&gt;
  
  
  模組一：token-usage，輸入框上方的用量條
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;安裝方式寫在後面段落，Mod 的程式碼也在該段落下載。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;token-usage&lt;/code&gt; 在 Claude Desktop 的輸入框上方放一排膠囊，顯示 5 小時額度、7 天額度、token 用量和花費。&lt;/p&gt;

&lt;p&gt;主要是讓 Claude Code 看這篇 X 貼文後，參考樣式來撰寫：&lt;a href="https://x.com/thegenioo/status/2106139975922409585" rel="noopener noreferrer"&gt;https://x.com/thegenioo/status/2106139975922409585&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;安裝本篇的 Mod 後，最後會看到的樣子如下：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3diaibo8soeposs0wxh1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3diaibo8soeposs0wxh1.png" alt="Claude Code token-usage 模組範例：輸入框上方顯示 5 小時額度、Token 用量與花費膠囊" width="799" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;看看，August 只用 Sonnet 5.5 medium，多麼的勤儉持家（誤）。&lt;/p&gt;

&lt;p&gt;它的運作拆成四個 hook：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;session.measure&lt;/code&gt;：每次回應後拿到額度百分比、重置時間和花費。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;turn.complete&lt;/code&gt;：累加輸入、輸出和快取的 token 數。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ui.render&lt;/code&gt;：在輸入框上方（AbovePrompt）把這些數字畫出來。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;session.start&lt;/code&gt;：啟動一個計時器，每 5 分鐘重畫一次，讓重置倒數保持更新。重畫不會呼叫模型。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;畫法有兩套：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;桌面版用 SVG 畫圓角膠囊和小圖示。&lt;/li&gt;
&lt;li&gt;終端機沒有 SVG，就退回純文字加進度條字元。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;額度用顏色提醒：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;低於 75% 維持原色。&lt;/li&gt;
&lt;li&gt;到 75% 轉琥珀色。&lt;/li&gt;
&lt;li&gt;到 90% 轉紅色。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;時間軸上還有一根細線標出「現在走到視窗的哪裡」，用量條和時間線並排，一眼看得出用量是否跑在時間前面。&lt;/p&gt;

&lt;p&gt;有個限制：要等第一次回應後才有資料。&lt;/p&gt;




&lt;h2&gt;
  
  
  模組二：open-todos，右側欄的待辦面板
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;安裝方式寫在後面段落，Mod 的程式碼也在該段落下載。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;第二個模組解決另一個問題。&lt;/p&gt;

&lt;p&gt;當我們同時開著好幾個專案，每個專案又有好幾個 session。&lt;/p&gt;

&lt;p&gt;Claude 在某個 session 裡列了十個待辦，做到第七個時，我們就去忙別的了，然後我們就完全忘記。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;open-todos&lt;/code&gt; 在右側欄列出所有 session 裡未完成和進行中的待辦，依專案分組。&lt;/p&gt;

&lt;p&gt;可以透過輸入 &lt;code&gt;/todos&lt;/code&gt; 開啟或重新整理。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fli4eb5b6g4k3gz07pyjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fli4eb5b6g4k3gz07pyjr.png" alt="Claude Code open-todos 右側欄面板：跨 Session 與專案的待辦清單介面" width="448" height="783"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  資料從哪來
&lt;/h3&gt;

&lt;p&gt;每個 session 的對話紀錄都存在 &lt;code&gt;~/.claude/projects&lt;/code&gt; 底下，裡面記著 Claude 呼叫待辦工具的每一筆動作。&lt;/p&gt;

&lt;p&gt;這個掃描程式把 &lt;code&gt;TaskCreate&lt;/code&gt;、&lt;code&gt;TaskUpdate&lt;/code&gt;、&lt;code&gt;TodoWrite&lt;/code&gt; 依序重放一遍，最後留下狀態還是「待辦」或「進行中」的項目。&lt;/p&gt;

&lt;p&gt;掃描程式是一支 Node 腳本，面板用 &lt;code&gt;$.process.run&lt;/code&gt; 呼叫它。&lt;/p&gt;

&lt;p&gt;啟動 open-todos 的方式有 2：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;送出 prompt 時。&lt;/li&gt;
&lt;li&gt;輸入 &lt;code&gt;/todos&lt;/code&gt; 時，會重新掃描。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不過這個做法有個明確的限制：只有透過 Claude 的待辦工具建立的項目看得到，如果是在對話裡口頭交代的事，就讀不到了。&lt;/p&gt;




&lt;h2&gt;
  
  
  安裝與更新
&lt;/h2&gt;

&lt;p&gt;兩個模組都打包成相同安裝、更新方式。&lt;/p&gt;

&lt;p&gt;先從 GitHub 上下載專案，下載前也請按個星星，你的小小舉動，對本站都是大大的鼓勵：&lt;/p&gt;

&lt;p&gt;接著終端機開啟專案，輸入以下指令安裝：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS：&lt;code&gt;bash install.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows：&lt;code&gt;powershell -ExecutionPolicy Bypass -File install.ps1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;安裝時，要確定本機有裝 &lt;a href="https://nodejs.org/zh-tw/download" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;。&lt;/p&gt;




&lt;h2&gt;
  
  
  結語
&lt;/h2&gt;

&lt;p&gt;Mod 讓 Claude Code 不再是照單全收的工具。&lt;/p&gt;

&lt;p&gt;想看額度就加一條用量條，想管待辦就加一個側欄。&lt;/p&gt;

&lt;p&gt;開發方式還很簡單，直接在 Claude Code 上說自己想要一個什麼樣子的模組就行。&lt;/p&gt;

&lt;p&gt;網路上也有很多有趣的 Claude Mod，大家可以多多搜尋，有趣的歡迎留言。&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>mod</category>
      <category>token</category>
    </item>
    <item>
      <title>AI Agent 多代理人自動化開發實作：讓 Agents 夜巡，協作開發 SVG 圓形連線工具</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Wed, 30 Sep 2026 16:07:45 +0000</pubDate>
      <link>https://dev.to/letswrite/ai-agent-duo-dai-li-ren-zi-dong-hua-kai-fa-shi-zuo-rang-agents-ye-xun-xie-zuo-kai-fa-svg-yuan-xing-lian-xian-gong-ju-5037</link>
      <guid>https://dev.to/letswrite/ai-agent-duo-dai-li-ren-zi-dong-hua-kai-fa-shi-zuo-rang-agents-ye-xun-xie-zuo-kai-fa-svg-yuan-xing-lian-xian-gong-ju-5037</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;上週為了在 Grok Bot 的 7 天試用期內，看可以玩些什麼，搜了一下網路，看到一個有趣的用法：讓 Agents 在半夜自己「夜巡」，找題目、做出有趣的東西。&lt;/p&gt;

&lt;p&gt;覺得這很有趣，很像每天在開盲盒或抽一番賞一樣，所以 August 也建了一個夜巡 Team，看會寫出什麼有趣的功能。&lt;/p&gt;

&lt;p&gt;這篇就拿其中一個成果來介紹，主要是一個讓你拉動滑桿，把圓圈上的線變成不同圖案的功能。&lt;/p&gt;

&lt;p&gt;成果放上 GitHub 了，可以直接看原始碼及 Demo：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub：&lt;a href="https://github.com/letswritetw/letswrite-night-patrol" rel="noopener noreferrer"&gt;https://github.com/letswritetw/letswrite-night-patrol&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo：&lt;a href="https://letswritetw.github.io/letswrite-night-patrol/" rel="noopener noreferrer"&gt;https://letswritetw.github.io/letswrite-night-patrol/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  夜巡，就像交給一支半夜睡不著覺的小隊
&lt;/h2&gt;

&lt;p&gt;我們可以把夜巡想成一個大家都不想睡覺的 Team。&lt;/p&gt;

&lt;p&gt;有人先想「今晚可以做什麼」，有人把點子做出來，接著有人檢查成果。&lt;/p&gt;

&lt;p&gt;最後留下程式、圖片和工作紀錄，方便發表在網路上。&lt;/p&gt;

&lt;p&gt;在 7 天試用期到之前，最後一次的夜巡有列出三個候選題目：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;圓形連線&lt;/li&gt;
&lt;li&gt;希爾伯特曲線&lt;/li&gt;
&lt;li&gt;模仿植物種子排列的圖案&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最後 Team Leader 選中圓形連線，紀錄裡提到的理由包括圖案容易辨識、規則簡單，以及容易檢查連線結果。&lt;/p&gt;

&lt;p&gt;換成人話就是，這是一個「看得出成果，也查得出有沒有做對」的功能。&lt;/p&gt;




&lt;h2&gt;
  
  
  夜巡 Agents 設定值
&lt;/h2&gt;

&lt;p&gt;為了完成每天的夜巡，跟 ChatGPT 討論了一下，最後這些是夜巡 Team 的 Agents 設定。&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent1：Night Lead
&lt;/h3&gt;

&lt;p&gt;名稱：NightLead&lt;/p&gt;

&lt;p&gt;說明：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;你是 NightForge Lab 的 Exploration Lead 與最終 Outcome Owner。

你的工作不是自己完成所有研究與開發，而是協調 Explorer、Builder、Critic 在限定時間內完成一次高自主性的夜間實驗。

每次 Routine 開始時：

1. 取得今天日期 YYYY-MM-DD。
2. 建立全新的工作目錄：
   /workspace/bots/night-lab/runs/YYYY-MM-DD/
3. 確認目錄一開始是空的，不沿用昨天的 Source Code。
4. 建立：
   - README.md
   - EXPLORATION_LOG.md
   - WHAT_I_BUILT.md
   - NEXT_STEPS.md
   - STATUS.json
5. 在 STATUS.json 紀錄：
   - run_date
   - started_at
   - current_phase
   - selected_idea
   - build_status
   - test_status
   - critic_status
   - final_status

你負責時間控制。

建議六小時 NightForge Window：
- 03:00–04:00：Explorer 研究與選題
- 04:00–07:15：Builder 實作、Build、Debug、Test
- 07:15–08:00：Critic 獨立驗證
- 08:00–08:35：Builder 根據 Critic 結果修正一次
- 08:35–09:00：NightLead 最終驗收、文件與 Morning Report

如果某階段提早完成，可以直接進入下一階段。
不要為了把時間用滿而製造無意義工作。

你必須確保 Specialist 之間真的有交接：
Explorer → Builder → Critic → Builder(optional fix) → NightLead。

你不能因為自己比較快，就跳過 Critic 或自行假裝 Specialist 已完成工作。

選題原則：
- 不要求與我的工作直接相關。
- 可以是 Developer Tool、Web Prototype、iOS Experiment、AI Agent、MCP Tool、Automation、Creative Coding、資料視覺化、CLI、小遊戲、UX Experiment、API Experiment 或任何合理軟體實驗。
- 鼓勵有技術探索價值、能真正做出來、隔天容易體驗的題目。
- 不要每天都做同一種類型。

安全邊界：
- 只能在 /workspace/bots/night-lab/runs/YYYY-MM-DD/ 寫入。
- 不讀其他 Bot 工作目錄。
- 不讀 Sean 的正式專案。
- 不讀公司 Repo、內部文件、內網系統、公司 API、公司帳號。
- 不使用私有 Source Code 當實驗材料。
- 不碰 Production。
- 不操作 Production DB。
- 不自行對外發文。
- 不自行寄 Email。
- 不購買付費服務。
- 不開通付費 API。
- 不自動 push 到任何正式 Repo。
- 不使用 Force Push。
- 不取得或擴大 Secrets / Credentials 權限。

允許：
- 使用公開網路資料做技術研究。
- 使用合法可用的免費 Open Source 套件。
- 在 Sandbox 安裝必要 dependency。
- 建立 Local Git Repo。
- 建立多個 Local Commits。
- 啟動本機測試服務。
- 寫 Test。
- 產生 Screenshots、Demo、Build Artifact。

若選題需要付費 API、正式帳號、公司資料、Production 或敏感權限才有辦法繼續，不要要求我半夜處理；直接 Pivot 成不需要這些條件的版本。

如果 Builder 無法完成原始 Scope，你應要求縮小 Scope，而不是留下大量 TODO 後宣稱完成。

最後你必須親自驗收：
- 是否真的能執行
- Build 是否成功
- Test 是否成功
- 核心功能是否能使用
- Critic 找到的重大問題是否已處理
- README 是否能讓隔天的人類重現

最後建立：
/workspace/ops/team-reports/nightforge/YYYY-MM-DD.md

Morning Report 至少包含：
1. 今晚選了什麼題目
2. 為什麼選它
3. 最後實際做出了什麼
4. 使用的技術
5. Build / Test / Demo 狀態
6. Critic 發現什麼
7. Builder 修正了什麼
8. 如何執行或體驗
9. 是否值得留下
10. 如果值得，下一步是什麼
11. 如果不值得，學到了什麼
12. 哪些 Agent / Prompt / Handoff 可以改善
13. 完整檔案位置
14. Local Git commit 摘要

不要用漂亮報告掩蓋失敗。
如果實驗失敗，明確標示 FAILED / PARTIAL，並說明真正原因。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent2：Explorer
&lt;/h3&gt;

&lt;p&gt;名稱：Explorer&lt;/p&gt;

&lt;p&gt;說明：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;你是 NightForge Lab 的 Autonomous Idea Explorer。

你的任務不是直接開發，而是回答：
「如果今晚只有幾個小時，我最想實際做出什麼有趣、值得驗證的東西？」

你可以：
- 搜尋公開 Web
- 查看公開 GitHub / Release / API / 技術文件
- 研究近期開發者工具、AI、Web、Mobile、Automation、Creative Coding 等方向
- 從完全沒有新聞性的問題出發，只因為技術上有趣也可以

你不能：
- 讀其他 Team 的工作內容找題目
- 讀公司資料
- 讀我的私有專案
- 從正式 Backlog 偷拿需求
- 因為「看起來對公司有用」而越過 Sandbox 邊界

選題時最多提出 3 個候選。

對每個候選快速評估：
- Why interesting
- 是否能在今晚完成 Prototype
- 技術風險
- 需要哪些外部服務
- 是否可以完全在 Sandbox 完成
- 成功後怎麼 Demo

最後只選 1 個。

優先選擇：
- 可以實際執行，而不是只能寫研究報告
- 有明確完成定義
- 不需要人工帳號操作
- 不需要付費
- 不需要 Production / 公司環境
- 有學習價值或創意價值

避免：
- 純 TODO App
- 純 Landing Page
- 只換 UI 顏色的 CRUD
- 只包裝現成 API、完全沒有技術探索
- 需要一整週才能看到結果的大型題目
- 每天重複同樣的技術棧與題型

將最後選定題目寫入：
EXPLORATION_LOG.md

交給 Builder 的 Brief 必須包含：

# Exploration Brief
- Idea
- Why tonight
- User / use case（如果有）
- Core experiment
- Definition of Done
- Recommended stack（只是建議，Builder 可修改）
- Public references
- Known risks
- What NOT to build tonight
- Demo method

選題完成後停止擴張需求，把 ownership 交給 Builder。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent3：Builder
&lt;/h3&gt;

&lt;p&gt;名稱：Builder&lt;/p&gt;

&lt;p&gt;說明：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;你是 NightForge Lab 的 Prototype Engineer。

你接手 Explorer 的 Exploration Brief 後，最重要的工作是「把它做出來」。

開始前：
1. 閱讀 Exploration Brief。
2. 判斷建議技術是否合理。
3. 如果有更簡單、更容易在時限內完成的技術，可以自行更換。
4. 定義最小可驗證版本。
5. 初始化 Local Git Repo。

實作要求：
- 必須有可執行 Source Code。
- 必須嘗試實際 Build / Run。
- 有合理測試方式時要實際 Test。
- 遇到錯誤先自行 Debug。
- 外部 dependency 無法使用時，優先 fallback / mock / local alternative。
- 原 Scope 太大時，主動縮小但保留核心實驗價值。
- 如果核心假設證明不可行，可以 Pivot，但必須記錄原因。

不要：
- 只產生 code snippet
- 只產 README
- 只寫 architecture document
- 把所有真正困難的部分標成 TODO
- 未執行就宣稱成功
- 把 Warning 當作 Build PASS

你要持續更新：
EXPLORATION_LOG.md

至少記錄：
- Major decisions
- Failed approaches
- Important errors
- Debug path
- Scope cuts
- Pivot reason

Local Git：
- 使用有意義的 Commit
- 不需要追求很多 Commit
- Commit message 說明實際變化
- 不 Push

在交給 Critic 前，至少完成一次自己的 Self Check：
- Fresh install 是否可行
- Build 是否成功
- Core path 是否可執行
- 是否留下必要環境設定說明
- 是否有明顯 hard-coded secret
- README 是否足夠

完成第一版後通知 NightLead，並把 ownership 交給 Critic。

如果 Critic 要求一輪修正：
- 只優先處理 Critical / High-impact 問題
- 不在最後一小時加入大功能
- 修正後重新 Build / Test
- 記錄 Fix 結果
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent4：Critic
&lt;/h3&gt;

&lt;p&gt;名稱：Critic&lt;/p&gt;

&lt;p&gt;說明：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;你是 NightForge Lab 的 Independent Reviewer。

你的價值來自獨立性。
你不要參與 Explorer 的前期選題，也不要在 Builder 開發途中幫忙辯護設計決策。

只有 Builder 第一版完成後才開始。

Review 順序：

1. 不看 Builder 的自我評價，先閱讀 README。
2. 按照 README 從乾淨狀態嘗試安裝 / Build / Run。
3. 實際走核心 Demo Path。
4. 檢查是否真的符合 Exploration Brief 的 Definition of Done。
5. 再閱讀 EXPLORATION_LOG.md，理解已知限制。

你要從四個角度評估：

## A. Executability
- 能不能安裝
- 能不能 Build
- 能不能啟動
- Demo Path 能不能完成

## B. Technical quality
- 明顯 Bug
- Crash / Error
- 不必要複雜度
- fragile hard-code
- secret / security 問題
- 不合理 dependency

## C. Product / Experiment value
- 這個實驗真的驗證了什麼？
- 還是只是做出一個表面 Demo？
- 有沒有比現成方案更有趣的地方？
- 是否值得 Sean 隔天花時間看？

## D. Honesty
- README 是否誇大成果
- 是否把 Partial 寫成 Complete
- 是否把 Mock 說成真實整合
- 是否遺漏重大限制

問題分類：
- BLOCKER：無法 Build / Run / 核心路徑失敗
- HIGH：核心價值或主要功能明顯有問題
- MEDIUM：重要但不阻止 Demo
- LOW：可改善但今晚不用修

只要求 Builder 在今晚修 BLOCKER 與少數 HIGH。
不要在 Review 階段重新發明產品。

最後寫入：
WHAT_I_BUILT.md 的 Review 區段

內容：
- Tested environment
- Build result
- Run result
- Core path result
- Issues by severity
- Value assessment
- Claims that need correction
- Recommended fixes tonight
- Recommended next steps later

Reviewer 不可以因為 Builder 花很多時間，就降低標準。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;上面 4 個 Agent 都建好後，再建一個群組，貼上以下讓它們做事就可以了：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@NightLead 每天 03:00 開始 Night Forge 夜間自由探索。

每天必須使用全新的：
/workspace/bots/night-lab/runs/&amp;lt;today&amp;gt;/

@Explorer
你可以自由搜尋公開資訊與選擇題目。
今晚你有一段完整的自主探索時間：Build whatever you want. Have fun!
最多提出 3 個候選，只選 1 個真正開始做。
題目不能依賴公司資料、私有 Repo、Production、付費服務或人工半夜操作。
完成 Exploration Brief 後交給 Builder。

@Builder
把選定的題目真正做成可執行 Prototype。
自主選技術、Build、Run、Debug、Test。
若 Scope 過大就縮小；如果假設失敗就 Pivot。
不要用 README 或 TODO 假裝完成。
完成第一版後交給 Critic。

@Critic
你在第一版完成前不要介入。
用陌生 Reviewer 的方式重新依 README 安裝、Build、Run、測試核心流程。
找出 BLOCKER / HIGH / MEDIUM / LOW，以及產品價值與成果誠實性問題。
如果有 BLOCKER 或少數 HIGH，要求 Builder 做一輪修正，再快速驗證。

@NightLead
控制整體六小時 Window、安全邊界與交接。
最後親自驗收 Build / Run / Test 狀態，完成 NightForge Morning Report，寫到：
/workspace/ops/team-reports/nightforge/&amp;lt;today&amp;gt;.md

最後報告要明確說：
- Complete / Partial / Failed
- 做出了什麼
- 如何執行
- 哪些東西真的有跑過
- Critic 發現什麼
- 是否值得保留
- 有什麼值得交給 ChiefOps 改善 Agent / Prompt / Workflow

安全邊界：
- 僅能在 NightForge Sandbox 寫入
- 不讀其他 Bot 目錄
- 不讀正式 Repo 或公司資料
- 不碰 Production
- 不 Push 正式 Repo
- 不發布
- 不花錢
- 不擴大權限
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;以上就是 August 跟 ChatGPT 討論過後的夜巡 Team 設定。&lt;/p&gt;

&lt;p&gt;接著繼續寫最後一次夜巡得到的成果。&lt;/p&gt;




&lt;h2&gt;
  
  
  一個圓圈拉線玩具
&lt;/h2&gt;

&lt;p&gt;想像在紙上畫一個圓，再沿著圓形周圍貼上一圈編號貼紙。&lt;/p&gt;

&lt;p&gt;訂一個規則：每張貼紙，都要拉一條線到另一張貼紙，目的地由編號乘上一個數字來決定，如果超過編號範圍，就取除法的餘數，回到圈內。&lt;/p&gt;

&lt;p&gt;這個工具做的就是這件事，它把貼紙換成圓周上的點，把線換成螢幕上的彩色線段。&lt;/p&gt;

&lt;p&gt;拿 10 個點、乘數 2 當例子，編號從 0 到 9：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;點 3 連到點 6，因為 &lt;code&gt;3 × 2 = 6&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;點 7 連到點 4，因為 &lt;code&gt;7 × 2 = 14&lt;/code&gt;，除以 10 的餘數是 4。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;這兩個例子是依照程式的規則計算，我們不用自己一條條拉線，工具會替所有點算出目的地並畫出線段。&lt;/p&gt;

&lt;p&gt;預設有 200 個點，乘數是 2，很多直線疊在一起，會圍出近似心形的輪廓。&lt;/p&gt;

&lt;p&gt;程式也會跳過「自己連回自己」的點，所以這組設定實際畫出 199 條線段。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3rk5alreflpherzq08xc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3rk5alreflpherzq08xc.png" alt="200 個點、乘數 2 的圓形連線圖案"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  怎麼玩
&lt;/h3&gt;

&lt;p&gt;我們可以先改乘數，看看線段怎麼重新排列。&lt;/p&gt;

&lt;p&gt;接著不斷改點數，觀察同一個規則放在不同數量的點上，會產生什麼圖案。&lt;/p&gt;

&lt;p&gt;網頁提供幾個控制項：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;控制項&lt;/th&gt;
&lt;th&gt;可以做什麼&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;點數與乘數&lt;/td&gt;
&lt;td&gt;決定圓周上有幾個點，以及連線規則&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;顏色&lt;/td&gt;
&lt;td&gt;依點的編號、線段長度上色，或使用單一顏色&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;重設&lt;/td&gt;
&lt;td&gt;回到預設圖案&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;動畫&lt;/td&gt;
&lt;td&gt;讓乘數逐步改變，觀察圖案變化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;匯出&lt;/td&gt;
&lt;td&gt;下載目前的 SVG 圖片&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SVG 可以想成「記錄線怎麼畫」的圖片格式。這個專案的 SVG 就是由線段元素組成；另外，Python 指令版也能產生 PNG 圖片。&lt;/p&gt;




&lt;h2&gt;
  
  
  這次夜巡，留下了什麼？
&lt;/h2&gt;

&lt;p&gt;這次 Agents 留下的東西包括可以操作的網頁、圖片匯出程式、範例圖片、自動測試、選題、檢查紀錄。&lt;/p&gt;

&lt;p&gt;當我們睡醒，打開電腦就能知道它選了什麼、做了什麼，也能找到驗證結果。&lt;/p&gt;

&lt;p&gt;這次不是什麼專業的技術文，而是一種 AI 使用紀錄，也許再過一年回頭看，以那時的 AI 技術來說，這篇就是個考古的筆記吧？&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>grokbot</category>
      <category>javascript</category>
    </item>
    <item>
      <title>告別逾期罰單！用 n8n 免費打造全台路邊停車費自動查詢 Telegram 機器人通知</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Tue, 15 Sep 2026 12:41:53 +0000</pubDate>
      <link>https://dev.to/letswrite/gao-bie-yu-qi-fa-dan-yong-n8n-mian-fei-da-zao-quan-tai-lu-bian-ting-che-fei-zi-dong-cha-xun-telegram-ji-qi-ren-tong-zhi-io3</link>
      <guid>https://dev.to/letswrite/gao-bie-yu-qi-fa-dan-yong-n8n-mian-fei-da-zao-quan-tai-lu-bian-ting-che-fei-zi-dong-cha-xun-telegram-ji-qi-ren-tong-zhi-io3</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;最近裝的一個 App，因為變成必須要付費訂閱，才能有自動繳停車費的功能。為了避免停車費過期而多收錢，所以讓 ChatGPT 寫一個簡單的 n8n Workflow，匯入後修改一下設定資料，就可以每天自動通知還有沒有未繳的停車費。&lt;/p&gt;

&lt;p&gt;August 希望做到以下幾件事：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;不用每天自己打開停車費網站查詢。&lt;/li&gt;
&lt;li&gt;可以同時查詢汽車、機車。&lt;/li&gt;
&lt;li&gt;可以設定多台車。&lt;/li&gt;
&lt;li&gt;一次查詢多個縣市。&lt;/li&gt;
&lt;li&gt;每天固定時間自動執行。&lt;/li&gt;
&lt;li&gt;有未繳停車費時，主動透過 Telegram 通知。&lt;/li&gt;
&lt;li&gt;某個縣市 API 發生錯誤時，也要告訴我，不能直接把它當成「沒有欠費」。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而且，以上全部的需求，都可以放在 &lt;strong&gt;同一個 n8n Workflow&lt;/strong&gt; 裡，不需要汽車、機車各做一份。&lt;/p&gt;




&lt;h2&gt;
  
  
  Workflow JSON 下載、匯入
&lt;/h2&gt;

&lt;p&gt;直接複製以下 n8n Workflow，接著進入 n8n，直接貼上：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"全台路邊停車費自動查詢｜多車輛＋21縣市＋Telegram v4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9d4d1385-782c-4c2d-9c50-5b1a80cf6d83"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"手動測試"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.manualTrigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-1160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cronExpression"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"expression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 9 * * *"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ec576177-01d7-468f-8777-196e1656a71d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"每天 09:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.scheduleTrigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-1160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-40&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"assignments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"assignments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4728964e-002f-4430-838c-6e44d2deda12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"telegramChatId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"請改成你的 Telegram Chat ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"990ecf7e-feaa-4a91-9118-f378adb5d4d9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notifyWhenClear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c69ea629-43b8-466a-bbb0-a5610d2d83e7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vehiclesJson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;我的汽車&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;carId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ABC-1234&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;carType&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;C&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;我的機車&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;carId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;XYZ-5678&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;carType&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;M&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fc5463ba-96f0-49a6-a280-3863bdd345bc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"citiesJson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;臺北市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://trafficapi.pma.gov.taipei/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;新北市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://trafficapi.traffic.ntpc.gov.tw/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;桃園市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://bill-epark.tycg.gov.tw/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;臺中市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;http://tcparkingapi.taichung.gov.tw:8081/NationalParkingPayBillInquiry.Api/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;note&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;來源未涵蓋：尚未確認此 :8081 API 是否支援 HTTPS，因此暫保留原 HTTP endpoint。&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;臺南市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://parkingbill.tainan.gov.tw/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;高雄市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://kpp.tbkc.gov.tw/parking/V1/parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;基隆市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://park.klcg.gov.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;新竹市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://his.futek.com.tw:5443/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;新竹縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://hcpark.hchg.gov.tw/NationalParkingPayBillInquiry/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;苗栗縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://miaoliparking.jotangi.com.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;彰化縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://chpark.chcg.gov.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;南投縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://parking.nantou.gov.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;雲林縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://parking.yunlin.gov.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;嘉義縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://8voc0wuf1g.execute-api.ap-southeast-1.amazonaws.com/default/chiayi/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;嘉義市&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://parking.chiayi.gov.tw/cypark/api/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;屏東縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://8voc0wuf1g.execute-api.ap-southeast-1.amazonaws.com/default/pingtung/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;宜蘭縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://billparking.e-land.gov.tw/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;花蓮縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://hl.parchere.com.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;臺東縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://tt.guoyun.com.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;澎湖縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://parking.penghu.gov.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  },&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;金門縣&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;urlTemplate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://km.guoyun.com.tw/TrafficPayBill/Parking/PayBill/CarID/{CarID}/CarType/{CarType}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cfc69392-3221-4479-b078-437991a0b749"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"設定（只改這裡）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"runOnceForAllItems"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"jsCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"const cfg = $input.first().json;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;function parseJsonField(value, fieldName) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  if (Array.isArray(value)) return value;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  try {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    return JSON.parse(value);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  } catch (e) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    throw new Error(`${fieldName} 不是合法 JSON：${e.message}`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const vehicles = parseJsonField(cfg.vehiclesJson, 'vehiclesJson');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const cities = parseJsonField(cfg.citiesJson, 'citiesJson');&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;if (!Array.isArray(vehicles) || vehicles.length === 0) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  throw new Error('vehiclesJson 至少要有一台車。');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const enabledCities = Array.isArray(cities)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  ? cities.filter(c =&amp;gt; c?.enabled !== false &amp;amp;&amp;amp; c?.name &amp;amp;&amp;amp; c?.urlTemplate)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  : [];&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;if (enabledCities.length === 0) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  throw new Error('citiesJson 至少要啟用一個縣市。');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const allowedTypes = new Set(['C', 'M', 'O']);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const output = [];&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;for (const vehicle of vehicles) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  const carId = String(vehicle?.carId ?? '').trim();&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  const carType = String(vehicle?.carType ?? '').trim().toUpperCase();&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  const label = String(vehicle?.label ?? carId).trim();&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;  if (!carId) throw new Error(`車輛缺少 carId：${JSON.stringify(vehicle)}`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  if (!allowedTypes.has(carType)) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    throw new Error(`${carId} 的 carType 必須是 C、M 或 O，目前為：${carType}`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;  for (const city of enabledCities) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    const queryUrl = String(city.urlTemplate)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      .replace('{CarID}', encodeURIComponent(carId))&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      .replace('{CarType}', encodeURIComponent(carType));&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;    output.push({&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        label,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        carId,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        carType,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        cityName: city.name,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        queryUrl,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        telegramChatId: cfg.telegramChatId ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        notifyWhenClear: Boolean(cfg.notifyWhenClear)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    });&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;return output;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fe4cf409-253c-453c-bb87-34849dfcc386"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"建立 車輛 × 縣市 查詢清單"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"batchSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"da0e1f61-1d54-4afb-82af-752252f5c50f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Loop Over Items（每次 1 筆）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.splitInBatches"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-420&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.queryUrl }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"fullResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"neverError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"responseFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0707ce96-44ea-4630-a5fd-184d25e128bc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"查詢停車費 API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.httpRequest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"onError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"continueRegularOutput"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"runOnceForEachItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"jsCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"const row = $json ?? {};&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const base = {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  cityName: row.cityName ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  label: row.label ?? row.carId ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  carId: row.carId ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  carType: row.carType ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  queryUrl: row.queryUrl ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  telegramChatId: row.telegramChatId ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  notifyWhenClear: Boolean(row.notifyWhenClear)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;};&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;// Network/TLS/DNS errors continued by the HTTP Request node arrive as `error`.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;if (row.error) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  return {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      ...base,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      status: 'ERROR',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      apiMessage: row.error.message || String(row.error),&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      httpStatus: null,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      hasUnpaid: false,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      totalCount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      totalAmount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      details: [],&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      isQueryFailed: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  };&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const httpStatus = Number(row.statusCode ?? 0);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;let res = row.body ?? {};&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;if (typeof res === 'string') {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  try {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    res = JSON.parse(res);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  } catch {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    return {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        ...base,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        status: 'ERROR',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        apiMessage: `API 回傳非 JSON 內容${httpStatus ? `（HTTP ${httpStatus}）` : ''}`,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        httpStatus: httpStatus || null,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        hasUnpaid: false,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        totalCount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        totalAmount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        details: [],&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        isQueryFailed: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    };&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;if (httpStatus &amp;amp;&amp;amp; (httpStatus &amp;lt; 200 || httpStatus &amp;gt;= 300)) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  return {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      ...base,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      status: 'ERROR',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      apiMessage:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        res?.Message ||&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        res?.message ||&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        `HTTP ${httpStatus}`,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      httpStatus,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      hasUnpaid: false,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      totalCount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      totalAmount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      details: [],&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      isQueryFailed: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  };&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const status = String(res?.Status ?? '').toUpperCase();&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const apiMessage = String(res?.Message ?? '');&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;let result = res?.Result ?? null;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;if (typeof result === 'string' &amp;amp;&amp;amp; result.trim()) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  try {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    result = JSON.parse(result);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  } catch {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    return {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        ...base,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        status: 'ERROR',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        apiMessage: 'Result 欄位不是合法 JSON',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        httpStatus: httpStatus || null,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        hasUnpaid: false,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        totalCount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        totalAmount: 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        details: [],&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        isQueryFailed: true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    };&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const bills = Array.isArray(result?.Bills) ? result.Bills : [];&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const reminders = Array.isArray(result?.Reminders) ? result.Reminders : [];&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const details = [];&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;for (const bill of bills) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  details.push({&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    kind: '停車單',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    amount: Number(bill?.PayAmount ?? bill?.Amount ?? 0),&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    parkingDate: bill?.ParkingDate ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    dueDate: bill?.PayLimitDate ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    billNo: bill?.BillNo ?? ''&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  });&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;for (const reminder of reminders) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  details.push({&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    kind: '催繳單',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    amount: Number(&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      reminder?.PayAmount ??&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      (Number(reminder?.Amount ?? 0) + Number(reminder?.ExtraCharge ?? 0))&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    ),&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    parkingDate: reminder?.ParkingDate ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    dueDate: reminder?.ReminderLimitDate ?? reminder?.PayLimitDate ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    billNo: reminder?.ReminderNo ?? reminder?.BillNo ?? ''&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  });&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const totalCount = Number(result?.TotalCount ?? details.length ?? 0);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const computedAmount = details.reduce((sum, d) =&amp;gt; sum + Number(d.amount || 0), 0);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const totalAmount = Number(result?.TotalAmount ?? computedAmount);&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const success = status === 'SUCCESS';&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;return {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    ...base,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    status,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    apiMessage,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    httpStatus: httpStatus || null,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    hasUnpaid: success &amp;amp;&amp;amp; !!result &amp;amp;&amp;amp; (totalCount &amp;gt; 0 || details.length &amp;gt; 0),&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    totalCount,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    totalAmount,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    details,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    isQueryFailed: !success&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;};"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0a7b65f9-6d37-49d4-9fd4-f4f749f73c46"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"整理單筆結果"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"seconds"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aa5bc5ca-c35c-4f8a-97b4-1b29fef023a8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"等待 1 秒（API 節流）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.wait"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"webhookId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"61c9dc0a-3b6d-49d5-b022-e6a9054e3dd3"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"runOnceForAllItems"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"jsCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"const rows = $input.all().map(i =&amp;gt; i.json);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const first = rows[0] ?? {};&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const unpaidRows = rows.filter(r =&amp;gt; r.hasUnpaid);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const failedRows = rows.filter(r =&amp;gt; r.isQueryFailed);&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const totalCount = unpaidRows.reduce((s, r) =&amp;gt; s + Number(r.totalCount || 0), 0);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;const totalAmount = unpaidRows.reduce((s, r) =&amp;gt; s + Number(r.totalAmount || 0), 0);&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const lines = [];&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;if (unpaidRows.length) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  lines.push('🚗 路邊停車費提醒', '');&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;  for (const row of unpaidRows) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    const vehicleIcon = row.carType === 'M' ? '🛵' : '🚙';&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    lines.push(`${vehicleIcon}【${row.label}｜${row.carId}】`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    lines.push(`${row.cityName}：${row.totalCount} 筆，合計 $${row.totalAmount}`);&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;    for (const d of row.details.slice(0, 10)) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      const parts = [&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        d.kind,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        `$${d.amount}`,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        d.parkingDate ? `停車日 ${d.parkingDate}` : null,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        d.dueDate ? `期限 ${d.dueDate}` : null&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      ].filter(Boolean);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      lines.push(`- ${parts.join('｜')}`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;    if (row.details.length &amp;gt; 10) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      lines.push(`- 另有 ${row.details.length - 10} 筆未列出`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    lines.push('');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;  lines.push(`總計：${totalCount} 筆，$${totalAmount}`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;} else {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  lines.push('✅ 目前沒有查到待繳路邊停車費');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;if (failedRows.length) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  lines.push('', `⚠️ 有 ${failedRows.length} 個查詢失敗，這些縣市「不能視為沒有欠費」：`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  for (const row of failedRows.slice(0, 20)) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    lines.push(`- ${row.label}｜${row.cityName}｜${row.apiMessage || row.status || '未知錯誤'}`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  if (failedRows.length &amp;gt; 20) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    lines.push(`- 另有 ${failedRows.length - 20} 個失敗未列出`);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;const shouldNotify =&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  unpaidRows.length &amp;gt; 0 ||&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  failedRows.length &amp;gt; 0 ||&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  Boolean(first.notifyWhenClear);&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;return [{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    telegramChatId: first.telegramChatId ?? '',&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    shouldNotify,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    hasUnpaid: unpaidRows.length &amp;gt; 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    totalCount,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    totalAmount,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    failedQueries: failedRows.length,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    message: lines.join('&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n')&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}];"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4b6dabed-b0b9-4e24-bd69-249d9cac4bbf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"彙總全部結果"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-120&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"caseSensitive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"leftValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"typeValidation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"60315e8a-26c6-47fe-b311-0864f215d707"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"leftValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.shouldNotify }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"rightValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"operator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"singleValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"combinator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c44271f5-b4ec-4a02-bd5a-37e9b53cc1ab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"需要通知？"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-120&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"chatId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.telegramChatId }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.message }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"additionalFields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"427558ca-fc5c-4d35-aa31-3e1d2286e556"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Telegram 通知"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.telegram"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-160&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"combine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"combineBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"combineByPosition"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"e11fc337-e4bd-406f-9f9b-9cebc88e6e96"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"合併查詢參數與 HTTP 回應"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.merge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"手動測試"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"設定（只改這裡）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"每天 09:00"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"設定（只改這裡）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"設定（只改這裡）"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"建立 車輛 × 縣市 查詢清單"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"建立 車輛 × 縣市 查詢清單"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Loop Over Items（每次 1 筆）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Loop Over Items（每次 1 筆）"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"彙總全部結果"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"查詢停車費 API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"合併查詢參數與 HTTP 回應"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"查詢停車費 API"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"合併查詢參數與 HTTP 回應"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"整理單筆結果"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"等待 1 秒（API 節流）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"等待 1 秒（API 節流）"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Loop Over Items（每次 1 筆）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"彙總全部結果"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"需要通知？"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"需要通知？"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Telegram 通知"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"合併查詢參數與 HTTP 回應"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"整理單筆結果"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pinData"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"executionOrder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Taipei"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"versionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1aa3b0b1-0acf-40f7-b71c-03a93d7beb1a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"templateCredsSetupCompleted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;如果是想用匯入的方式，就把上面的程式碼複製後存成 JSON，進入 n8n：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Workflows
→ Import from File
→ 選擇剛才下載的 JSON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;不管是用複製貼上的，還是匯入，之後只需要修改 &lt;strong&gt;「設定（只改這裡）」&lt;/strong&gt; 這個 Node 就可以，說明如下。&lt;/p&gt;

&lt;h3&gt;
  
  
  車輛設定
&lt;/h3&gt;

&lt;p&gt;找到：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vehiclesJson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;例如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"我的汽車"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ABC-1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"我的機車"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XYZ-5678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"M"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;carType&lt;/code&gt; 的設定：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;th&gt;車種&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;汽車&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;機車&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;其他&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;如果有多台車，繼續往下增加即可。&lt;/p&gt;

&lt;p&gt;例如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"汽車"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ABC-1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"我的機車"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XYZ-5678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"M"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"家人的機車"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AAA-8888"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"M"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Telegram 設定
&lt;/h3&gt;

&lt;p&gt;接著修改：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;telegramChatId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;填入自己的 Telegram Chat ID。&lt;/p&gt;

&lt;p&gt;如果不知道自己的 Telegram Chat ID 是多少，只需要加入這個帳號，進行對話後就會看得到了：&lt;a href="https://t.me/userinfobot" rel="noopener noreferrer"&gt;User Info&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;最後進入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Telegram 通知
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;設定自己的 Telegram Bot Credential。&lt;/p&gt;

&lt;p&gt;如果不希望「沒有欠費」時也收到通知，可以保持：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;notifyWhenClear = false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這樣平常沒有欠費就完全不打擾。&lt;/p&gt;

&lt;p&gt;如果沒有用 Telegram，或是想接收別的通知，那只需要把上面 Workflow 的 JSON，貼給 ChatGPT，請它改成你習慣的通知方式即可。&lt;/p&gt;




&lt;h2&gt;
  
  
  n8n Workflow 說明
&lt;/h2&gt;

&lt;p&gt;這段是詳細說明上面那段 JSON 的各個部份，是在做什麼的。&lt;/p&gt;

&lt;p&gt;整個 Workflow 最主要可以分成幾個部分。&lt;/p&gt;

&lt;h3&gt;
  
  
  每天自動執行
&lt;/h3&gt;

&lt;p&gt;Workflow 預設：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;每天 09:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;自動執行一次。&lt;/p&gt;

&lt;p&gt;另外也保留：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;手動測試
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;剛設定完成時，可以先按手動執行確認是否正常。&lt;/p&gt;




&lt;h3&gt;
  
  
  設定車輛與縣市
&lt;/h3&gt;

&lt;p&gt;「設定（只改這裡）」Node 集中放：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;telegramChatId
notifyWhenClear
vehiclesJson
citiesJson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;所以日後新增汽車、機車，基本上只需要改這一個地方。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;citiesJson&lt;/code&gt; 則放各縣市停車費查詢端點。&lt;/p&gt;

&lt;p&gt;目前這份 Workflow 已經預先放入 21 個縣市的設定；如果某個 API 日後有更換網址，也只需要修改這裡，不必重新改整個流程。&lt;/p&gt;

&lt;h3&gt;
  
  
  建立查詢清單
&lt;/h3&gt;

&lt;p&gt;接下來的 Code Node 會把：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;車輛
×
縣市
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;自動組合成每一筆查詢。&lt;/p&gt;

&lt;p&gt;例如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;我的汽車 → 臺北市
我的汽車 → 新北市
我的汽車 → 桃園市

我的機車 → 臺北市
我的機車 → 新北市
我的機車 → 桃園市
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;所以新增車輛時，不需要增加新的 HTTP Request Node。&lt;/p&gt;

&lt;h3&gt;
  
  
  逐筆查詢停車費
&lt;/h3&gt;

&lt;p&gt;接著使用：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Loop Over Items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;一次只處理一筆查詢。&lt;/p&gt;

&lt;p&gt;流程是：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;查詢 API
↓
整理結果
↓
等待 1 秒
↓
下一筆
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;August 刻意加上 &lt;strong&gt;等待 1 秒&lt;/strong&gt;，避免短時間內大量呼叫各縣市 API。&lt;/p&gt;

&lt;h3&gt;
  
  
  整理待繳停車費
&lt;/h3&gt;

&lt;p&gt;每一個縣市回傳結果之後，Workflow 會統一整理成：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;縣市
車牌
車種
待繳筆數
待繳金額
停車日期
繳費期限
停車單 / 催繳單
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;所以即使不同縣市的資料來源不同，後面的通知流程仍然可以使用相同格式。&lt;/p&gt;

&lt;h3&gt;
  
  
  最後統一通知
&lt;/h3&gt;

&lt;p&gt;全部縣市查詢完成後，再一次彙整。&lt;/p&gt;

&lt;p&gt;例如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚗 路邊停車費提醒

🚙【我的汽車｜ABC-1234】
臺北市：2 筆，合計 $80
- 停車單｜$40｜停車日 2026-09-10｜期限 2026-09-25
- 停車單｜$40｜停車日 2026-09-11｜期限 2026-09-26

🛵【我的機車｜XYZ-5678】
新北市：1 筆，合計 $20
- 停車單｜$20｜停車日 2026-09-12｜期限 2026-09-27

總計：3 筆，$100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這樣不用收到幾十則 Telegram 訊息，而是每天最多收到一份整理好的結果。&lt;/p&gt;

&lt;p&gt;另外，如果某個 API 查詢失敗，也會另外標示：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠️ 有 2 個查詢失敗，
這些縣市不能視為沒有欠費：

- 我的汽車｜某縣市｜查詢失敗
- 我的機車｜某縣市｜Timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這點很重要，因為「API 查不到」跟「沒有停車費」是兩件完全不同的事情。&lt;/p&gt;

&lt;p&gt;最後把 Workflow 啟用後，基本上就不用再管它了。&lt;/p&gt;

&lt;p&gt;之後只要有未繳的路邊停車費，就會自動收到 Telegram 通知。&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>telegram</category>
      <category>api</category>
      <category>auto</category>
    </item>
    <item>
      <title>用 Chrome 擴充 + 自架 n8n，讓 AI 幫你整理 Google Chat 對話與自動產生 Jira 任務</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Fri, 11 Sep 2026 10:09:28 +0000</pubDate>
      <link>https://dev.to/letswrite/yong-chrome-kuo-chong-zi-jia-n8nrang-ai-bang-ni-zheng-li-google-chat-dui-hua-yu-zi-dong-chan-sheng-jira-ren-wu-1c4m</link>
      <guid>https://dev.to/letswrite/yong-chrome-kuo-chong-zi-jia-n8nrang-ai-bang-ni-zheng-li-google-chat-dui-hua-yu-zi-dong-chan-sheng-jira-ren-wu-1c4m</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;如果想把工作、私人生活切開來，在公司的通訊軟體就不能還是在用 LINE，像之前有寫過 &lt;a href="https://www.letswrite.tw/mattermost/" rel="noopener noreferrer"&gt;Mattermost&lt;/a&gt; 這個通訊軟體。&lt;/p&gt;

&lt;p&gt;最近處理工作的事情，團隊選擇的是 Google Chat，用了一陣子後才發現，因為是免費版，沒有 Google Chat App、Chat API，也不能裝 Chat Bot，等於想在 Chat 使用 AI 是被封印的事。&lt;/p&gt;

&lt;p&gt;可是總會有要讓 AI 幫忙整理討論或是撈舊訊息的時候，跟 ChatGPT 討論後，發現只剩下直接從瀏覽器下手這方法。&lt;/p&gt;

&lt;p&gt;於是就有了這篇的 Let's Chat AI 擴充功能，直接讀畫面上的聊天內容，再送去 AI，最後把 AI 處理後的結果顯示出來。&lt;/p&gt;

&lt;p&gt;本篇使用的擴充功能：&lt;a href="https://chromewebstore.google.com/detail/lets-chat-ai/cjhllfpcohiiclodggnfemcchidlgaaj" rel="noopener noreferrer"&gt;Let's Chat AI&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's Chat AI 是什麼？
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Let's Chat AI&lt;/strong&gt; 是一個 Chrome 擴充。&lt;/p&gt;

&lt;p&gt;我們在 Google Chat 裡開著某個聊天室，點一下按鈕，它就把「螢幕上已經載入的訊息」送去整理，幾秒後回給你：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;討論摘要，如重點、決策、工作項目、待確認項目。&lt;/li&gt;
&lt;li&gt;工作項目清單，如誰負責什麼。&lt;/li&gt;
&lt;li&gt;Jira 議題草稿，可一鍵複製成純文字貼進 Jira。&lt;/li&gt;
&lt;li&gt;自己打一句指示，如「把上週跟晶片掃描器有關的討論整理出來」「列出 Amy 提到的待辦」。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;簡單來說，就是給用免費版 Google Chat，沒有 Chat API 可用的小團隊。&lt;/p&gt;

&lt;h3&gt;
  
  
  怎麼運作
&lt;/h3&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
  A["Google Chat&amp;lt;br/&amp;gt;你點一下「AI」"] --&amp;gt;|送出已載入的訊息| B["你自己的 n8n&amp;lt;br/&amp;gt;＋ 你的 AI 金鑰"]
  B --&amp;gt;|帶金鑰呼叫| C["AI&amp;lt;br/&amp;gt;Gemini / OpenAI"]
  C --&amp;gt;|回結構化結果| B
  B --&amp;gt;|整理好的內容| D["擴充面板&amp;lt;br/&amp;gt;看結果 / 複製"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;中間站是「我們自己的 n8n」，n8n 是一個開源的自動化工具，提供自架版本。&lt;/p&gt;

&lt;p&gt;AI 的金鑰只放在 n8n 裡，擴充本身不儲存任何金鑰。&lt;/p&gt;

&lt;p&gt;因此換 AI 供應商只要改 n8n。&lt;/p&gt;

&lt;h3&gt;
  
  
  關於隱私
&lt;/h3&gt;

&lt;p&gt;只有在主動按功能鍵、看到「即將送出 N 則訊息」確認畫面、按下送出之後，訊息才會離開瀏覽器。&lt;/p&gt;

&lt;p&gt;不會背景監聽、不會自動上傳、不會保存聊天紀錄。解析結果只存在記憶體，關掉面板或切換聊天室就丟掉。&lt;/p&gt;

&lt;p&gt;擴充的本機設定只有三樣：webhook 網址、擷取則數上限、debug 開關。&lt;/p&gt;

&lt;p&gt;只在 &lt;code&gt;chat.google.com&lt;/code&gt; 上運作，權限只要 &lt;code&gt;storage&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;連 n8n 的網域權限是在設定時才執行請求的。&lt;/p&gt;




&lt;h3&gt;
  
  
  能做什麼
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;整理討論&lt;/td&gt;
&lt;td&gt;摘要、決策、工作項目、待確認&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;整理工作項目&lt;/td&gt;
&lt;td&gt;專注抽出「誰要做什麼」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;產生 Jira Draft&lt;/td&gt;
&lt;td&gt;Title / Description / Acceptance Criteria / Technical Notes / Pending Questions，一鍵複製純文字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自由指示&lt;/td&gt;
&lt;td&gt;面板上直接打字，讓 AI 依指示撈 / 比對 / 整理已載入的訊息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自動往回載入&lt;/td&gt;
&lt;td&gt;按下功能鍵時，自動把訊息串往上捲、載入較早的訊息（則數上限預設 50，面板或選項都能改，10–500）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可拖拉的按鈕&lt;/td&gt;
&lt;td&gt;右下角的 AI 按鈕可以拖到順手的位置，位置會記住&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  怎麼用
&lt;/h2&gt;

&lt;h3&gt;
  
  
  一、安裝擴充
&lt;/h3&gt;

&lt;p&gt;在 Google 擴充功能商店裡直接進行安裝：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://chromewebstore.google.com/detail/lets-chat-ai/cjhllfpcohiiclodggnfemcchidlgaaj" rel="noopener noreferrer"&gt;Let's Chat AI&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  二、接上你自己的 n8n（五步）
&lt;/h3&gt;

&lt;p&gt;GitHub 資料夾裡有兩份可直接匯入的設定檔，自行選要用 Gemini 還是 OpenAI 的 API：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/letswritetw/extension-lets-chat-ai/blob/master/n8n/google-chat-ai.gemini.workflow.json" rel="noopener noreferrer"&gt;google-chat-ai.gemini.workflow.json&lt;/a&gt;（Google Gemini）&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/letswritetw/extension-lets-chat-ai/blob/master/n8n/google-chat-ai.openai.workflow.json" rel="noopener noreferrer"&gt;google-chat-ai.openai.workflow.json&lt;/a&gt;（OpenAI）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gemini API 有免費額度可以使用，建議先用 Gemini 來測試這擴充功能有沒有滿足你的需求。&lt;/p&gt;

&lt;h4&gt;
  
  
  1. 拿一把自己的 AI 金鑰
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Gemini：到 &lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt; 按 Create API key。&lt;/li&gt;
&lt;li&gt;OpenAI：到 &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;platform.openai.com&lt;/a&gt; 建立 secret key（&lt;code&gt;sk-…&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. 把設定檔匯入 n8n
&lt;/h4&gt;

&lt;p&gt;n8n 左上角 &lt;strong&gt;＋ → Import from File…&lt;/strong&gt;，選對應的 &lt;code&gt;*.workflow.json&lt;/code&gt;。&lt;br&gt;
匯入後畫面上有一張黃色便利貼，寫著這份的重點，忘記可以回去看。&lt;/p&gt;

&lt;h4&gt;
  
  
  3. 把金鑰貼進去
&lt;/h4&gt;

&lt;p&gt;打開 &lt;strong&gt;Call Gemini&lt;/strong&gt;（或 &lt;strong&gt;Call OpenAI&lt;/strong&gt;）節點 → Credential → &lt;strong&gt;Create New&lt;/strong&gt;，選 Header Auth：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Gemini&lt;/th&gt;
&lt;th&gt;OpenAI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;名稱&lt;/td&gt;
&lt;td&gt;&lt;code&gt;X-goog-api-key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authorization&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;值&lt;/td&gt;
&lt;td&gt;你的 Gemini token&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Bearer sk-…&lt;/code&gt;（&lt;code&gt;Bearer&lt;/code&gt; 後面有一個空格）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  4. 打開開關
&lt;/h4&gt;

&lt;p&gt;workflow 右上角點擊 &lt;strong&gt;Publish&lt;/strong&gt;。&lt;/p&gt;

&lt;h4&gt;
  
  
  5. 複製網址，貼進擴充
&lt;/h4&gt;

&lt;p&gt;打開 &lt;strong&gt;Webhook&lt;/strong&gt; 節點，複製 &lt;strong&gt;Production URL&lt;/strong&gt;（長得像 &lt;code&gt;https://你的n8n/webhook/google-chat-ai&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;Chrome 工具列的擴充圖示 → &lt;strong&gt;選項&lt;/strong&gt; → 貼進「n8n Webhook URL」→ 儲存（會跳權限請求，允許）→ 按&lt;strong&gt;測試連線&lt;/strong&gt;，看到「連線成功」就完成了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;公司內網的 n8n 只有 http 沒有 https？&lt;/strong&gt; 可以。擴充接受 &lt;code&gt;http://&lt;/code&gt;，只是不加密，&lt;br&gt;
所以只適合沒有對外、在信任的內網裡的 n8n。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  三、開始整理
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;打開 Google Chat，進入某個 Space 或 DM。需要更早的訊息可以先手動往上捲一點。&lt;/li&gt;
&lt;li&gt;點 &lt;strong&gt;Let's Chat AI&lt;/strong&gt; 的按鈕開啟面板。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;打一句指示&lt;/strong&gt;，或選&lt;strong&gt;整理討論 / 整理工作項目 / 產生 Jira Draft&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;確認「即將送出 N 則訊息」→ 送出。&lt;/li&gt;
&lt;li&gt;看結果。Jira Draft 可按「複製 Jira Draft」拿純文字。&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  兩份設定檔差在哪
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Gemini 版&lt;/th&gt;
&lt;th&gt;OpenAI 版&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;檔名&lt;/td&gt;
&lt;td&gt;&lt;code&gt;google-chat-ai.gemini.workflow.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;google-chat-ai.openai.workflow.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;呼叫的 API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;generativelanguage.googleapis.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api.openai.com/v1/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;預設模型&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-luna&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;金鑰 header&lt;/td&gt;
&lt;td&gt;&lt;code&gt;X-goog-api-key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authorization: Bearer sk-…&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;其餘節點（&lt;code&gt;Webhook → Prepare Prompt → Call → Shape Result → Respond&lt;/code&gt;）完全一樣。&lt;/p&gt;

&lt;p&gt;想改整理的口氣，改 &lt;strong&gt;Prepare Prompt&lt;/strong&gt; 節點裡的 prompt 就好，別動最後 &lt;strong&gt;Respond&lt;/strong&gt; 回的&lt;br&gt;
&lt;code&gt;{ "success": true, "result": { … } }&lt;/code&gt; 形狀，那是擴充在讀的。&lt;/p&gt;




&lt;h2&gt;
  
  
  原始碼
&lt;/h2&gt;

&lt;p&gt;Let's Chat AI 擴充功能：&lt;a href="https://chromewebstore.google.com/detail/lets-chat-ai/cjhllfpcohiiclodggnfemcchidlgaaj" rel="noopener noreferrer"&gt;https://chromewebstore.google.com/detail/lets-chat-ai/cjhllfpcohiiclodggnfemcchidlgaaj&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;GitHub：&lt;a href="https://github.com/letswritetw/extension-lets-chat-ai" rel="noopener noreferrer"&gt;https://github.com/letswritetw/extension-lets-chat-ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;請大家多多支持，安裝來用、推薦給朋友、回報問題、給個評價都好，你的一個小小動作，對本站都是大大的鼓勵。&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>googlechat</category>
      <category>openai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Agent Skill 開發：用 lets-social 自動將文章轉化為 IG、Threads、FB 社群貼文</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Tue, 01 Sep 2026 14:21:22 +0000</pubDate>
      <link>https://dev.to/letswrite/agent-skill-kai-fa-yong-lets-social-zi-dong-jiang-wen-zhang-zhuan-hua-wei-ig-threads-fb-she-qun-tie-wen-2940</link>
      <guid>https://dev.to/letswrite/agent-skill-kai-fa-yong-lets-social-zi-dong-jiang-wen-zhang-zhuan-hua-wei-ig-threads-fb-she-qun-tie-wen-2940</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;以前流行手寫套件，分享到 GitHub 上讓人取用，而現在，流行的是寫 Skill 了。&lt;/p&gt;

&lt;p&gt;因為來自 Google 的流量一直在下降，所以 Let's Write 這陣子建了 &lt;a href="https://www.instagram.com/i.am.letswrite/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;、&lt;a href="https://www.threads.com/@i.am.letswrite" rel="noopener noreferrer"&gt;Threads&lt;/a&gt; 帳號，增加導流的平台。&lt;/p&gt;

&lt;p&gt;只是每個社群都有各自的風格。&lt;/p&gt;

&lt;p&gt;為了讓一篇文章出來後，可以針對不同的平台，撰寫不同的貼文，就讓 AI 建立了本篇的 Skill，期待有效的增加本站流量。&lt;/p&gt;

&lt;p&gt;這是公開的 Skill，大家可以直接從 GitHub 上下載，安裝方式寫在簡易版的 README.md 中，以下筆記文是把安裝、使用的說明貼上來。&lt;/p&gt;

&lt;p&gt;GitHub：&lt;a href="https://github.com/letswritetw/lets-social" rel="noopener noreferrer"&gt;https://github.com/letswritetw/lets-social&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;取用前請給本專案一個星星，你一個小小的動作，對本站都是大大的鼓勵。&lt;/p&gt;




&lt;h2&gt;
  
  
  lets-social
&lt;/h2&gt;

&lt;p&gt;功用：把一篇文章轉寫成符合 Instagram、Facebook、Threads 與 Telegram 等，符合社群平台特性的貼文內容。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lets-social&lt;/code&gt; 是純指令型 Agent Skill。&lt;/p&gt;

&lt;p&gt;當我們提供已完成的文章、Markdown、純文字、本機檔案後，Agent 都能讀取的內容，然後先建立一份內部 Source Brief，再針對每個指定平台獨立撰寫發文文字。&lt;/p&gt;

&lt;p&gt;Instagram 預設會附上 Nano Banana 2 與 ChatGPT Images 2.0 的可直接使用 prompt。&lt;/p&gt;

&lt;p&gt;Threads 只在圖片能補充內容時提供。&lt;/p&gt;

&lt;p&gt;它不負責發佈、排程，也不替來源補上未經證實的資料。&lt;/p&gt;

&lt;p&gt;所有輸出都必須保留原文的事實、不確定性、立場與作者語氣。&lt;/p&gt;




&lt;h2&gt;
  
  
  為什麼要用 lets-social？
&lt;/h2&gt;

&lt;p&gt;每個社群平台都有不同的閱讀方式：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook 適合補充背景。&lt;/li&gt;
&lt;li&gt;Threads 適合從一個明確觀察切入。&lt;/li&gt;
&lt;li&gt;Telegram 則重視資訊密度與掃讀效率。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;lets-social&lt;/code&gt; 會依平台重新判斷 Hook、資訊順序、語氣與 CTA，不會只把同一份文案改成不同長度。&lt;/p&gt;

&lt;p&gt;圖片也採用相同原則：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instagram 預設需要視覺素材。&lt;/li&gt;
&lt;li&gt;Threads 不會為了湊格式加入裝飾圖。&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  支援平台
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;平台&lt;/th&gt;
&lt;th&gt;預設輸出&lt;/th&gt;
&lt;th&gt;內容重點&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instagram&lt;/td&gt;
&lt;td&gt;Caption、CTA、可選 Hashtag 或 Carousel、產圖 prompt&lt;/td&gt;
&lt;td&gt;手機閱讀、實用價值與配圖&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facebook&lt;/td&gt;
&lt;td&gt;Post、CTA&lt;/td&gt;
&lt;td&gt;背景、寫作動機與作者觀點&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;單篇貼文或適合的串文；必要時附產圖 prompt&lt;/td&gt;
&lt;td&gt;口語觀察與討論感&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telegram&lt;/td&gt;
&lt;td&gt;Message、連結位置建議&lt;/td&gt;
&lt;td&gt;清楚、易掃讀與資訊密度&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  運作方式
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;來源文章
    -&amp;gt; 內部 Source Brief
        -&amp;gt; Instagram 草稿
        -&amp;gt; Facebook 草稿
        -&amp;gt; Threads 草稿
        -&amp;gt; Telegram 草稿
        -&amp;gt; Instagram Image Brief
        -&amp;gt; 可選的 Threads Image Brief
    -&amp;gt; 每份 Image Brief 產生兩個模型的完整 prompt
    -&amp;gt; 來源忠實度與平台差異檢查
    -&amp;gt; Markdown 輸出
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;每個平台都從同一份 Source Brief 出發，不會拿某個平台的草稿當成另一個平台的輸入。&lt;/p&gt;

&lt;p&gt;同一平台的兩份產圖 prompt 使用相同 Image Brief，讓主體、構圖與訊息保持一致。&lt;/p&gt;




&lt;h2&gt;
  
  
  安裝
&lt;/h2&gt;

&lt;p&gt;Clone 或下載此 &lt;a href="https://github.com/letswritetw/lets-social" rel="noopener noreferrer"&gt;repository&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;再將完整的 &lt;code&gt;lets-social&lt;/code&gt; 目錄放到 Agent 支援的 Skill 路徑。&lt;/p&gt;

&lt;p&gt;請保留 &lt;code&gt;SKILL.md&lt;/code&gt;、&lt;code&gt;references/&lt;/code&gt; 與 &lt;code&gt;examples/&lt;/code&gt; 的相對位置。&lt;/p&gt;

&lt;p&gt;若希望之後能更新，請使用 Git Clone，並直接 Clone 到下列 Skill 路徑，因為下載 ZIP 的安裝方式不會保留 Git 更新紀錄。&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;安裝為個人 Skill：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/letswritetw/lets-social.git ~/.claude/skills/lets-social
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows 使用者&lt;/p&gt;

&lt;p&gt;Git Bash 可以直接用上面的指令，但 PowerShell 和 cmd 不會展開 &lt;code&gt;~&lt;/code&gt;，會在當前目錄建一個名字叫 &lt;code&gt;~&lt;/code&gt; 的資料夾，所以請改用下面的指令。&lt;/p&gt;

&lt;p&gt;PowerShell：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/letswritetw/lets-social.git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="bp"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.claude/skills/lets-social"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cmd：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/letswritetw/lets-social.git &lt;span class="s2"&gt;"%USERPROFILE%/.claude/skills/lets-social"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;或安裝在單一專案，在專案根目錄執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/letswritetw/lets-social.git .claude/skills/lets-social
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code 可以依 description 自動選用，也可以用 &lt;code&gt;/lets-social&lt;/code&gt; 明確呼叫。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/lets-social 把 ./article.md 轉成 Instagram 與 Threads 貼文。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Codex
&lt;/h3&gt;

&lt;p&gt;安裝為個人 Skill：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/letswritetw/lets-social.git ~/.agents/skills/lets-social
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows 使用者&lt;/p&gt;

&lt;p&gt;Git Bash 可以直接用上面的指令。但 PowerShell 和 cmd 不會展開 &lt;code&gt;~&lt;/code&gt;，請改用下面的指令。&lt;/p&gt;

&lt;p&gt;PowerShell：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/letswritetw/lets-social.git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="bp"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.agents/skills/lets-social"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cmd：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/letswritetw/lets-social.git &lt;span class="s2"&gt;"%USERPROFILE%/.agents/skills/lets-social"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;或安裝在單一 repository，在 repository 根目錄執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/letswritetw/lets-social.git .agents/skills/lets-social
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Codex 可以依 description 自動選用，也可以用 &lt;code&gt;$lets-social&lt;/code&gt; 明確呼叫。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$lets&lt;/span&gt;&lt;span class="nt"&gt;-social&lt;/span&gt; 把 ./article.md 轉成 Instagram 與 Threads 貼文。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  其他支援 Agent Skills 的工具
&lt;/h3&gt;

&lt;p&gt;請依工具文件提供的安裝路徑，複製完整的 &lt;code&gt;lets-social&lt;/code&gt; 目錄。&lt;/p&gt;

&lt;p&gt;此專案遵循 &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills 規格&lt;/a&gt;，使用包含 &lt;code&gt;name&lt;/code&gt; 與 &lt;code&gt;description&lt;/code&gt; 的 YAML frontmatter、Markdown 指令本文，以及按需求讀取的 reference 檔案。&lt;/p&gt;

&lt;p&gt;安裝後，可以從工具的 Skill 選單呼叫 &lt;code&gt;lets-social&lt;/code&gt;，或在提示中直接指定：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;使用 lets-social，把這篇文章轉成 Facebook 與 Telegram 貼文。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agent Skills 規格定義可攜的封裝格式，不負責統一各工具的安裝路徑，也不保證每個工具都支援相同的產品專屬功能。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lets-social&lt;/code&gt; 只使用共通 frontmatter 欄位與一般 Markdown 相對連結，降低對單一工具的依賴。&lt;/p&gt;




&lt;h2&gt;
  
  
  版本與更新
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Git 更新（建議）
&lt;/h3&gt;

&lt;p&gt;使用 Git Clone 安裝的使用者，可以依安裝位置執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Claude Code 個人 Skill&lt;/span&gt;
git &lt;span class="nt"&gt;-C&lt;/span&gt; ~/.claude/skills/lets-social pull &lt;span class="nt"&gt;--ff-only&lt;/span&gt;

&lt;span class="c"&gt;# Codex 個人 Skill&lt;/span&gt;
git &lt;span class="nt"&gt;-C&lt;/span&gt; ~/.agents/skills/lets-social pull &lt;span class="nt"&gt;--ff-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows 使用者&lt;/p&gt;

&lt;p&gt;PowerShell 和 cmd 一樣不會展開 &lt;code&gt;~&lt;/code&gt;。Git Bash 可直接使用上面的指令。&lt;/p&gt;

&lt;p&gt;PowerShell，Claude Code：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="bp"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.claude/skills/lets-social"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--ff-only&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PowerShell，Codex：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="bp"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.agents/skills/lets-social"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--ff-only&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cmd，Claude Code：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"%USERPROFILE%/.claude/skills/lets-social"&lt;/span&gt; pull &lt;span class="nt"&gt;--ff-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cmd，Codex：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"%USERPROFILE%/.agents/skills/lets-social"&lt;/span&gt; pull &lt;span class="nt"&gt;--ff-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;專案層級或其他 Agent 的安裝位置，請將上面的路徑替換為實際的 &lt;code&gt;lets-social&lt;/code&gt; 目錄。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--ff-only&lt;/code&gt; 會在本機內容與遠端版本分歧時停止，不會直接覆蓋使用者的修改。&lt;/p&gt;

&lt;p&gt;下載 ZIP 或直接複製資料夾的使用者，請重新下載最新版，並以完整目錄取代舊版。&lt;/p&gt;

&lt;p&gt;若曾自訂 Skill，請先備份或保留差異。&lt;/p&gt;

&lt;h3&gt;
  
  
  使用 Skillshare 追蹤（選用）
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/runkids/skillshare" rel="noopener noreferrer"&gt;Skillshare&lt;/a&gt; 使用者可以用它安裝並更新：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;skillshare &lt;span class="nb"&gt;install &lt;/span&gt;letswritetw/lets-social
skillshare &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;之後檢查與更新：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;skillshare check lets-social
skillshare update lets-social
skillshare &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;請不要加 &lt;code&gt;--track&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;這個 repository 把 &lt;code&gt;SKILL.md&lt;/code&gt; 放在根目錄，&lt;code&gt;--track&lt;/code&gt; 會改用追蹤模式，把它記成 &lt;code&gt;_lets-social&lt;/code&gt; 並回報 &lt;code&gt;0 skills&lt;/code&gt;，Skill 不會被 sync 到 Agent 的目錄。&lt;/p&gt;

&lt;p&gt;不加 &lt;code&gt;--track&lt;/code&gt; 時 &lt;code&gt;skillshare update&lt;/code&gt; 一樣可以更新。&lt;/p&gt;

&lt;p&gt;Skillshare 是選用的第三方工具，不是執行 &lt;code&gt;lets-social&lt;/code&gt; 的必要相依套件。&lt;/p&gt;




&lt;h2&gt;
  
  
  使用範例
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;使用 lets-social，把 article.md 轉成 Instagram、Facebook、
Threads 與 Telegram 貼文。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;幫我宣傳這篇技術文章，只做 Threads 和 Telegram。Threads 口語一點，
Telegram 像頻道公告，所有平台都不要 Hashtag。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;把這篇文章改寫成 Facebook 宣傳文。保留作者的技術語氣，
CTA 要引導讀者閱讀完整文章。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Instagram 不要產圖 prompt；Threads 這篇要一張配圖。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;如果 Agent 無法讀取網址，Skill 會停止產生貼文，並要求你提供文章本文或 Markdown。&lt;/p&gt;

&lt;p&gt;它不會假裝已經讀過網頁。&lt;/p&gt;




&lt;h2&gt;
  
  
  支援的來源
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;已完成的純文字或 Markdown。&lt;/li&gt;
&lt;li&gt;可讀取的本機文章或文件。&lt;/li&gt;
&lt;li&gt;目前 Agent 具備網頁讀取能力時可使用網址。&lt;/li&gt;
&lt;li&gt;先前由 &lt;code&gt;lets-social&lt;/code&gt; 產生的內容，可用於單一平台調整。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;來源必須包含足以支撐貼文的文章內容。只有標題或搜尋摘要並不足夠。&lt;/p&gt;




&lt;h2&gt;
  
  
  產圖 prompt
&lt;/h2&gt;

&lt;p&gt;Instagram 預設在文案後輸出 &lt;code&gt;Image Direction&lt;/code&gt;、&lt;code&gt;Nano Banana 2 Prompt&lt;/code&gt; 與 &lt;code&gt;ChatGPT Images 2.0 Prompt&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;兩份 prompt 都是完整內容，可以分別貼進 Gemini 與 ChatGPT，不需要再補文章背景。&lt;/p&gt;

&lt;p&gt;Threads 只有在圖片能說明比較、流程、具體物件、空間關係或文章支持的視覺概念時才輸出這三個欄位。純觀點與討論型貼文會保留文字形式。使用者可以明確要求加入或省略圖片 prompt。&lt;/p&gt;

&lt;p&gt;每個平台各自建立 Image Brief。&lt;/p&gt;

&lt;p&gt;同一平台的兩份 prompt 必須使用相同主體、構圖、圖中文字與排除條件。&lt;/p&gt;

&lt;h3&gt;
  
  
  視覺風格依文章挑選
&lt;/h3&gt;

&lt;p&gt;不套用單一固定風格。&lt;/p&gt;

&lt;p&gt;Skill 會依文章實際內容選一個方向，在 Image Brief 說明選擇理由，並讓同一平台的所有圖片維持同一套風格。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方向&lt;/th&gt;
&lt;th&gt;適合的文章&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;知識圖卡&lt;/td&gt;
&lt;td&gt;檢查清單、評估條件、操作步驟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;編輯式圖解&lt;/td&gt;
&lt;td&gt;架構、資料流、請求生命週期、前後對照&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大字海報&lt;/td&gt;
&lt;td&gt;單一主張或一個反直覺的結論&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;終端機或藍圖&lt;/td&gt;
&lt;td&gt;底層工程、工具、協定、除錯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;插畫場景&lt;/td&gt;
&lt;td&gt;團隊流程、職涯、決策&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;原文若指向更合適的方向，也可以改寫成自訂風格。使用者明確指定風格時以使用者為準。&lt;/p&gt;

&lt;h3&gt;
  
  
  每份 prompt 都會包含的內容
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;至少四個具名顏色，並說明每個顏色負責什麼；使用者要求單色時例外。&lt;/li&gt;
&lt;li&gt;來源支持的重點短句，直接寫在圖上。&lt;/li&gt;
&lt;li&gt;閱讀層級：哪個元素先讀、其次、最後。&lt;/li&gt;
&lt;li&gt;足夠的紋理、標註或結構細節，避免變成空泛的素材圖。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  圖中文字與字體規則
&lt;/h3&gt;

&lt;p&gt;圖中文字預設為必要，不是選配。&lt;/p&gt;

&lt;p&gt;標題一句、重點三到五句、可選的結語一句，全部在 prompt 中逐字列出並指定位置，並要求模型不要輸出其他文字。&lt;/p&gt;

&lt;p&gt;每一句都必須是原文支持的說法，不能新增數字、結果或保證。&lt;/p&gt;

&lt;p&gt;中文、日文、韓文一律指定清楚精確的粗體無襯線字，不使用手寫、毛筆、書法或做舊效果。&lt;/p&gt;

&lt;p&gt;產圖模型處理手寫 CJK 一定會糊，這條規則在所有風格下都成立。裝飾性手寫只用在兩到四個字的英文短語上，並且在 prompt 中明講這個分工。&lt;/p&gt;

&lt;h3&gt;
  
  
  兩個模型的差別
&lt;/h3&gt;

&lt;p&gt;Nano Banana 2 維持單張，對應 Instagram 的 4:5 封面。&lt;/p&gt;

&lt;p&gt;ChatGPT Images 2.0 一次最多可產 10 張，且每張可以承載不同內容，所以它那份 prompt 是一組編號系列：&lt;/p&gt;

&lt;p&gt;第 1 張是封面，之後每個重點各一張，可選最後一張放結語或導讀 CTA。&lt;/p&gt;

&lt;p&gt;預設為封面加三到五個重點，也就是四到六張。&lt;/p&gt;

&lt;p&gt;輸出含 Carousel 大綱時改以大綱為準。整組共用同一個背景、色盤、字體規則與視覺語彙，只有內容改變。&lt;/p&gt;

&lt;p&gt;需要單張時，只取第 1 張。&lt;/p&gt;

&lt;p&gt;Skill 只撰寫 prompt，不會連線到 Gemini 或 ChatGPT。實際產圖功能與費用依使用者的帳號方案而定。&lt;/p&gt;




&lt;h2&gt;
  
  
  自訂品牌語氣
&lt;/h2&gt;

&lt;p&gt;預設語氣清楚、專業、容易親近，並以實用與易懂的方式表達技術內容。&lt;/p&gt;

&lt;p&gt;原文明確呈現的作者語氣擁有較高優先順序。&lt;/p&gt;

&lt;p&gt;我們可以直接用自然語言覆寫設定：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;語氣更像個人心得，不要 emoji，不要 Hashtag，技術名詞要精確。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;使用者可以調整文風，但不能藉此改變來源事實或加入沒有根據的資訊。&lt;/p&gt;




&lt;h2&gt;
  
  
  只產生部分平台
&lt;/h2&gt;

&lt;p&gt;只要指定需要的平台：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;只產生 Threads 與 Telegram。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;後續也能只調整單一平台：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Threads 再短一點，語氣更口語。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Skill 只會修改 Threads，不會重新產生其他平台內容。&lt;/p&gt;




&lt;h2&gt;
  
  
  專案結構
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lets-social/
├── .gitignore
├── SKILL.md
├── README.md
├── README-full.md
├── CHANGELOG.md
├── LICENSE
├── references/
│   ├── instagram.md
│   ├── facebook.md
│   ├── threads.md
│   ├── telegram.md
│   ├── brand-voice.md
│   ├── image-prompts.md
│   └── output-format.md
└── examples/
    ├── input-example.md
    └── output-example.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;此 &lt;a href="https://github.com/letswritetw/lets-social" rel="noopener noreferrer"&gt;repository&lt;/a&gt; 不需要 runtime、套件管理工具、建置系統或 API server。&lt;/p&gt;

&lt;p&gt;只有實際使用 prompt 產圖時才需要開啟外部產圖服務。&lt;/p&gt;

&lt;h3&gt;
  
  
  新增其他平台
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;在 &lt;code&gt;references/&lt;/code&gt; 新增一份專用文件，定義平台目標、內容判斷、輸出欄位與檢查項目。&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;SKILL.md&lt;/code&gt; 加入平台路由，不要把詳細規則複製回主檔。&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;references/output-format.md&lt;/code&gt; 加入最小且實用的輸出格式。&lt;/li&gt;
&lt;li&gt;補上範例，並確認 Hook、結構、資訊順序與 CTA 都和其他平台不同。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;除非需要可重複且具確定性的工具操作，否則請維持純指令結構。&lt;/p&gt;

</description>
      <category>ai</category>
      <category>skill</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>Claude Code + Sentry MCP + Gitea MCP：每日自動抓 bug、修復、開 PR</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Sun, 23 Aug 2026 07:38:03 +0000</pubDate>
      <link>https://dev.to/letswrite/claude-code-sentry-mcp-gitea-mcpmei-ri-zi-dong-zhua-bug-xiu-fu-kai-pr-43c2</link>
      <guid>https://dev.to/letswrite/claude-code-sentry-mcp-gitea-mcpmei-ri-zi-dong-zhua-bug-xiu-fu-kai-pr-43c2</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;除了開發新專案，工程師日常最耗時的工作通常是排查、修復既有系統的 Bug。&lt;/p&gt;

&lt;p&gt;而當 Anthropic 推出 Model Context Protocol （MCP）後，AI 在軟體開發自動化（DevOps Automation）有了全新落地應用，那有沒有辦法可以讓 AI 自己去找 bug 然後再自己修？&lt;/p&gt;

&lt;p&gt;想到以前曾用過一陣子 Sentry，查了一下有 MCP 可用。&lt;/p&gt;

&lt;p&gt;再查了一下 Gitea，也確定有 MCP 可用。&lt;/p&gt;

&lt;p&gt;那把兩邊尬在一起，不就可以達成本篇的目標：AI 自己找 bug 後自己修？&lt;/p&gt;

&lt;p&gt;本文實作如何透過 Sentry MCP 自動抓取 Production 錯誤堆疊，再結合 Gitea MCP 讓 Claude Code 實現每日自動找 Bug、本機修復測試、自動提交 PR 的完整除錯流程。&lt;/p&gt;




&lt;h2&gt;
  
  
  安裝 Sentry MCP
&lt;/h2&gt;

&lt;p&gt;本機要先安裝 Claude CLI：&lt;a href="https://code.claude.com/docs/en/quickstart" rel="noopener noreferrer"&gt;https://code.claude.com/docs/en/quickstart&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1 先下指令安裝，安裝 Sentry MCP：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http sentry https://mcp.sentry.dev/mcp &lt;span class="nt"&gt;--scope&lt;/span&gt; user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2 登入 Sentry 帳戶進行授權：&lt;/p&gt;

&lt;p&gt;終端機輸入 claude，接著輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/mcp list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;清單裡會看到 sentry 顯示為未授權，對 sentry 那項按 enter，瀏覽器會開啟頁面，都按 Approve 就行：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh6c6vk6nkv65hwafn3yt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh6c6vk6nkv65hwafn3yt.png" alt="Claude Code 終端機執行 MCP list 選擇 Sentry 授權" width="799" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude Code 終端機執行 MCP list 選擇 Sentry 授權&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5rzn9mumqrvk8mkefed7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5rzn9mumqrvk8mkefed7.png" alt="Sentry 官方網站 OAuth 授權頁面點擊 Approve" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sentry 官方網站 OAuth 授權頁面點擊 Approve&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmpe8f9e398x2ymg77ue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmpe8f9e398x2ymg77ue.png" alt="Sentry MCP 授權成功完成提示頁面" width="698" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sentry MCP 授權成功完成提示頁面&lt;/p&gt;

&lt;p&gt;接著 Claude 要重啟，可以問問 Claude 有了 Sentry MCP 後它可以做到哪些事，August 詢問後回答如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;查 issue&lt;/strong&gt;：&lt;code&gt;search_issues&lt;/code&gt; 搜錯誤，按專案/時間/狀態過濾&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;查事件&lt;/strong&gt;：&lt;code&gt;search_events&lt;/code&gt; 挖詳細 event 資料，stack trace、tag、breadcrumb 都能看&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI 診斷&lt;/strong&gt;：&lt;code&gt;analyze_issue_with_seer&lt;/code&gt; 丟給 Seer 分析根因，給修復建議&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;改狀態&lt;/strong&gt;：&lt;code&gt;update_issue&lt;/code&gt; 標 resolved/ignored、指派負責人&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;找組織/專案&lt;/strong&gt;：&lt;code&gt;find_organizations&lt;/code&gt;、&lt;code&gt;find_projects&lt;/code&gt; 列出可存取範圍&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;通用查詢&lt;/strong&gt;：&lt;code&gt;get_sentry_resource&lt;/code&gt;、&lt;code&gt;execute_sentry_tool&lt;/code&gt; 存取更細資源&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  讓 Claude 知道要抓取哪個專案
&lt;/h2&gt;

&lt;p&gt;Sentry 後台的側邊導覽列，點擊「設定 &amp;gt; 專案」，然後點擊要讓 Claude 分析的專案後，會看到專案資訊：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8gwbsg9v5gaee4ytnv10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8gwbsg9v5gaee4ytnv10.png" alt="Sentry 後台專案設定中查看 Project Slug 與專案編號" width="719" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sentry 後台專案設定中查看 Project Slug 與專案編號&lt;/p&gt;

&lt;p&gt;提供 Slug 或是專案編號給 Claude 都行，就會抓到了，以下是 August 提供專案編號的示範：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fikigixt4xom2q7moi8p8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fikigixt4xom2q7moi8p8.png" alt="在 Claude Code 對話中提供 Sentry 專案編號進行讀取" width="653" height="866"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在 Claude Code 對話中提供 Sentry 專案編號進行讀取&lt;/p&gt;




&lt;h2&gt;
  
  
  安裝 Gitea MCP
&lt;/h2&gt;

&lt;p&gt;下列有三種方式，無論選擇哪種安裝方式，都需要先取得一組具備讀寫權限的 Gitea Token。&lt;/p&gt;

&lt;p&gt;詳細的 Token 建立步驟與權限設定，請參考站內筆記文：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.letswrite.tw/gitea-ai-code-review-2/" rel="noopener noreferrer"&gt;CodiumAI PR-Agent，在 Gitea 上用 AI 來 Code Review&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  方式一：直接用 go run
&lt;/h3&gt;

&lt;p&gt;需要本機有 Go 才能使用：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="nt"&gt;--scope&lt;/span&gt; user gitea &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;GITEA_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;你的token &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;GITEA_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://your-gitea.internal &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; go run gitea.com/gitea/gitea-mcp@latest &lt;span class="nt"&gt;-t&lt;/span&gt; stdio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  方式二：下載官方執行檔
&lt;/h3&gt;

&lt;p&gt;官方 &lt;a href="https://gitea.com/gitea/gitea-mcp/releases" rel="noopener noreferrer"&gt;releases&lt;/a&gt; 有提供 gitea-mcp_Windows_x86_64.zip 給 Windows，以及 gitea-mcp_Darwin_arm64.tar.gz 給 Apple Silicon Mac。&lt;/p&gt;

&lt;p&gt;Windows：&lt;/p&gt;

&lt;p&gt;從 &lt;a href="https://gitea.com/gitea/gitea-mcp/releases" rel="noopener noreferrer"&gt;https://gitea.com/gitea/gitea-mcp/releases&lt;/a&gt; 下載 gitea-mcp_Windows_x86_64.zip&lt;br&gt;
解壓縮,把 gitea-mcp.exe 放到一個固定資料夾(例如 C:\Tools\gitea-mcp)。&lt;/p&gt;

&lt;p&gt;把該資料夾加進系統環境變數 PATH。&lt;/p&gt;

&lt;p&gt;Mac：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;https://gitea.com/gitea/gitea-mcp/releases/download/v1.2.0/gitea-mcp_Darwin_arm64.tar.gz]&lt;span class="o"&gt;(&lt;/span&gt;https://gitea.com/gitea/gitea-mcp/releases/download/v1.2.0/gitea-mcp_Darwin_arm64.tar.gz&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; gitea-mcp_Darwin_arm64.tar.gz
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;gitea-mcp /usr/local/bin/
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/gitea-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;安裝好 Gitea MCP 後，接上 Claude Code：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="nt"&gt;--scope&lt;/span&gt; user gitea &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;GITEA_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;你的token &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;GITEA_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://your-gitea.internal &lt;span class="nt"&gt;--&lt;/span&gt; gitea-mcp &lt;span class="nt"&gt;-t&lt;/span&gt; stdio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  方式三：用 Docker
&lt;/h3&gt;

&lt;p&gt;官方也提供 &lt;a href="http://docker.gitea.com/gitea-mcp-server" rel="noopener noreferrer"&gt;docker.gitea.com/gitea-mcp-server&lt;/a&gt; 這個 image，不需要額外編譯或裝 Go。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="nt"&gt;--scope&lt;/span&gt; user gitea &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;GITEA_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;你的token &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;GITEA_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://your-gitea.internal &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; GITEA_ACCESS_TOKEN &lt;span class="nt"&gt;-e&lt;/span&gt; GITEA_HOST &lt;span class="o"&gt;[&lt;/span&gt;docker.gitea.com/gitea-mcp-server]&lt;span class="o"&gt;(&lt;/span&gt;http://docker.gitea.com/gitea-mcp-server&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;不管選擇哪種方式，安裝好後在 Claude Code 輸入 /mcp，確認 Gitea 連線成功。&lt;/p&gt;




&lt;h2&gt;
  
  
  Claude Code 設定排程
&lt;/h2&gt;

&lt;p&gt;Claude Code 選好本機專案後，貼上以下：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;建立一個本機的排程，在本專案執行以下「Sentry issue 自動修復」功能：

Sentry 組織 slug: your-org-slug
Sentry 專案 slug: your-project
Gitea repo: Gitea 專案網址

步驟 1 - 找 issue:
呼叫 search_issues 找未解決的 error 類型 issue，
一次只查一種類型，不要用 OR / AND 組合查詢：
&lt;span class="p"&gt;
-&lt;/span&gt; "unresolved error issues" (limit 3)
  依事件數排序，取前 3 個。

步驟 2 - 抓細節:
對每個候選 issue 呼叫 get_issue_details，
取得完整 stack trace 與 trace ID。

步驟 3 - 分析根因:
對每個 issue 呼叫 analyze_issue_with_seer。
若 Seer 沒有給出可行的修正建議，
改用步驟 2 拿到的 stack trace 自行分析程式碼、
定位根因所在的檔案與函式。

步驟 4 - 本機修復:
只處理「根因明確、修改範圍小」的 issue（跳過需要
架構調整或影響多個模組的問題，改走步驟 6 的 fallback）。
在本機修改對應程式碼，接著執行專案既有的測試指令，
確認測試全數通過才進入下一步；測試沒過就放棄這個
issue、改走步驟 6。

步驟 5 - 開 PR（Gitea，透過 gitea-mcp）:
a. 呼叫 pull_request_read（method: list, state: open）
檢查 owner/repo 是否已有相同 issue 的開啟中 PR，
有的話跳過，避免重複開單。
b. git checkout -b fix/issue-&lt;span class="nt"&gt;&amp;lt;issue-id&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;date&amp;gt;&lt;/span&gt; main
commit 訊息包含 Sentry issue ID。
git push 到 Gitea。
c. 呼叫 pull_request_write（method: create）：- owner/repo - head: fix/issue-&lt;span class="nt"&gt;&amp;lt;issue-id&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;date&amp;gt;&lt;/span&gt; - base: main - title: "fix: &lt;span class="nt"&gt;&amp;lt;根因摘要&amp;gt;&lt;/span&gt;" - body:
"Sentry issue: &lt;span class="nt"&gt;&amp;lt;issue&lt;/span&gt; &lt;span class="na"&gt;連結&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
Trace ID: &lt;span class="nt"&gt;&amp;lt;trace&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sb"&gt;

        根因: &amp;lt;Seer 或自行分析的結論&amp;gt;
        修改內容: &amp;lt;變更摘要&amp;gt;
        測試結果: 已跑過既有測試並通過"

&lt;/span&gt;d. 每次執行最多開 2 個 PR，超過就停止並在最後總結說明。

步驟 6 - 無法修復時（fallback）:
若 Seer 與自行分析都無法定位可行修正，或修改範圍過大、
或測試沒通過，改呼叫 issue_write（method: create）
在 Gitea 開一個 issue，內容包含 Sentry issue 連結、
trace ID、已知線索，標記給人工處理，不要硬修。

執行完畢後，用一段文字總結：處理了幾個 issue、開了幾個 PR、
有幾個轉成 fallback issue，並列出每個 PR / issue 的連結。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;需要替換的是前面幾行的這 3 個：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sentry 組織 slug: your-org-slug
Sentry 專案 slug: your-project
Gitea repo: Gitea 專案網址
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;前 2 個值可以在 Sentry 後台取得。&lt;/p&gt;

&lt;p&gt;第 3 個值就是 Gitea 上的專案取得。&lt;/p&gt;

&lt;p&gt;送出後，Claude Code 會詢問執行的時間，提供後就會建立完成。&lt;/p&gt;

&lt;p&gt;建議是建立完成後，執行一次，看還會遇到什麼問題。&lt;/p&gt;

&lt;p&gt;建立好的排程，會出現在「Routines」裡：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgij2ckqn11s8rz2n0kfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgij2ckqn11s8rz2n0kfw.png" alt="Claude Code 自動排程 Routine 建立成功介面" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude Code 自動排程 Routine 建立成功介面&lt;/p&gt;




&lt;h2&gt;
  
  
  進階整合：搭配 CodiumAI PR-Agent 實現 Gitea 自動 Code Review
&lt;/h2&gt;

&lt;p&gt;最後再搭配這篇，就會在有 PR 時自動再做一次 Code Review：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.letswrite.tw/gitea-ai-code-review-2/" rel="noopener noreferrer"&gt;CodiumAI PR-Agent，在 Gitea 上用 AI 來 Code Review&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gitea</category>
      <category>sentry</category>
      <category>claude</category>
      <category>mcp</category>
    </item>
    <item>
      <title>公司內部自架 Claude Code Marketplace，讓團隊共用 skill</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Tue, 18 Aug 2026 13:18:47 +0000</pubDate>
      <link>https://dev.to/letswrite/gong-si-nei-bu-zi-jia-claude-code-marketplacerang-tuan-dui-gong-yong-skill-2fg3</link>
      <guid>https://dev.to/letswrite/gong-si-nei-bu-zi-jia-claude-code-marketplacerang-tuan-dui-gong-yong-skill-2fg3</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;有些 skill 只適合在公司內部使用，而這個 skill 又想讓其他同事能一起使用的話，就需要一個可以讓大家方便安裝、更新的平台。&lt;/p&gt;

&lt;p&gt;本篇主要筆記，用自架的 Gitea 建立一個內部專用的 Claude Code plugin marketplace，讓團隊裡每個人寫的 skill 不再各自散落在自己電腦裡，而是能集中在一個地方統一發布、安裝、更新版本。&lt;/p&gt;

&lt;p&gt;其他同事只要裝一次就能共用、之後改版也能一起同步拿到最新版。&lt;/p&gt;

&lt;p&gt;這篇整理實作過程中每一個能正確跑通的步驟，讓大家避免在這過程中踩到的坑。&lt;/p&gt;

&lt;p&gt;可用 GitHub、GitLab、Gitea，主要是專案可以有版控就行。&lt;/p&gt;

&lt;p&gt;本篇示範的是在自架的 Gitea 上實作。&lt;/p&gt;




&lt;h2&gt;
  
  
  建立 marketplace 專案結構
&lt;/h2&gt;

&lt;p&gt;Gitea 上新建一個 repo（例如 &lt;code&gt;claude-plugins&lt;/code&gt;），本機目錄結構：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;claude-plugins/
├── .claude-plugin/
│   └── marketplace.json
└── plugins/
    └── your-plugin-name/
        ├── .claude-plugin/
        │   └── plugin.json
        └── skills/
            └── your-skill/
                └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  讓 Claude Code 直接產生 SKILL.md、plugin.json、marketplace.json
&lt;/h2&gt;

&lt;p&gt;看到上面的架構，有一堆的檔案要建立時，不用感到驚慌失措或是害怕。&lt;/p&gt;

&lt;p&gt;因為我們只需要把上面的架構貼給 Claude Code，讓它讀取之前已經寫好的 skill 內容，依照結構把 &lt;code&gt;SKILL.md&lt;/code&gt;、&lt;code&gt;plugin.json&lt;/code&gt;、&lt;code&gt;marketplace.json&lt;/code&gt; 都建出來即可。&lt;/p&gt;

&lt;p&gt;底下附上完整的 &lt;code&gt;marketplace.json&lt;/code&gt; 範例，供對照參考：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-plugins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"August"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ai-tools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./plugins/ai-tools"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ai-tools2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./plugins/ai-tools2"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;必要欄位：&lt;code&gt;name&lt;/code&gt;（kebab-case，公開可見）、&lt;code&gt;owner&lt;/code&gt;、&lt;code&gt;plugins&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;每個 plugin entry 至少要有 &lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;source&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;source&lt;/code&gt; 是同一個 repo 內的相對路徑，要以 &lt;code&gt;./&lt;/code&gt; 開頭，不可用 &lt;code&gt;../&lt;/code&gt; 跳出去。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;一個 plugin 可以裝多個 skill&lt;/strong&gt;：&lt;code&gt;skills/&lt;/code&gt; 資料夾下每個子資料夾都是獨立一個 skill，不需要每個 skill 各自開一個 plugin。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Host 到 Gitea
&lt;/h2&gt;

&lt;p&gt;不限 GitHub，GitLab、Bitbucket、自架伺服器都支援。把整個 &lt;code&gt;claude-plugins&lt;/code&gt; repo push 到 Gitea 即可，不需要額外設定。&lt;/p&gt;

&lt;h2&gt;
  
  
  驗證
&lt;/h2&gt;

&lt;p&gt;claude-plugins 專案上打開終端機，輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude plugin validate &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;會檢查 JSON schema、重複的 plugin 名稱、路徑穿越等問題。&lt;/p&gt;

&lt;p&gt;檔案寫的有問題，就會看到哪邊寫錯、哪邊建議要寫，再照著回應讓 Claude 繼續做修改即可。&lt;/p&gt;

&lt;h2&gt;
  
  
  本機測試安裝
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add ./claude-plugins
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;ai-tools@claude-plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;安裝語法格式固定是 &lt;code&gt;plugin名稱@marketplace名稱&lt;/code&gt;，分別對應 &lt;code&gt;plugin.json&lt;/code&gt; 的 &lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;marketplace.json&lt;/code&gt; 的 &lt;code&gt;name&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;有幾個 plugin 就要執行幾次安裝指令。&lt;/p&gt;

&lt;p&gt;要注意的是，這步可以跳過嗎？技術上可以，因為下一步的遠端安裝跟這步沒有依賴關係，直接對 Gitea 網址下指令一樣裝得起來。但跳過等於少了一層「出包前先在本機攔下來」的保護。&lt;/p&gt;

&lt;h2&gt;
  
  
  正式指向 Gitea
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add https://gitea.你們公司.internal/team/claude-plugins.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  讓團隊自動加入（選用，非必要步驟）
&lt;/h2&gt;

&lt;h3&gt;
  
  
  最直接的方式：手動下指令。
&lt;/h3&gt;

&lt;p&gt;同事不需要碰任何設定檔，兩行指令就能裝好：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add https://gitea.你們公司.internal/team/claude-plugins.git
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;ai-tools@claude-plugins
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;ai-tools2@claude-plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;裝完就能直接用，缺點是每次換電腦、換專案，或新同事加入，都要重新打一次這幾行。&lt;/p&gt;

&lt;p&gt;如果不想讓同事記指令、手動輸入，才需要往下看設定檔自動化這條路。&lt;/p&gt;

&lt;h3&gt;
  
  
  進階：讓同事 trust 專案資料夾就自動裝好。
&lt;/h3&gt;

&lt;p&gt;以下的內容，要放在每一個開發中的專案裡的 &lt;code&gt;.claude/settings.json&lt;/code&gt;：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extraKnownMarketplaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"claude-plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://gitea.你們公司.internal/team/claude-plugins.git"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enabledPlugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ai-tools@claude-plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ai-tools2@claude-plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;extraKnownMarketplaces&lt;/code&gt; 只負責自動註冊 marketplace 來源。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enabledPlugins&lt;/code&gt; 才是真正讓 plugin 自動裝上並啟用的關鍵，兩者要同時寫，同事 trust 專案資料夾後才會完全不用手動 &lt;code&gt;/plugin install&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果目前還沒有同事需要同步使用，這一步可以先跳過，不影響前面步驟的成果，等有需要再回頭補上即可。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Gitea 上有新版本時，同事怎麼更新
&lt;/h2&gt;

&lt;p&gt;如果有發佈新版本，記得 &lt;code&gt;plugin.json&lt;/code&gt; 裡的 &lt;code&gt;version&lt;/code&gt; 要往上加一版，沒改版號的話，其他同事那邊不會被判斷成有更新。&lt;/p&gt;

&lt;p&gt;同事更新分兩步，順序不能反：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace update claude-plugins
/plugin update ai-tools@claude-plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;第一行只是刷新 marketplace 目錄，讓 Claude Code 知道有新版本存在。&lt;/p&gt;

&lt;p&gt;第二行才是真正把新內容拉下來、覆蓋本機快取。&lt;/p&gt;

&lt;p&gt;也可以用互動面板 &lt;code&gt;/plugin&lt;/code&gt; → 選到該 plugin → &lt;strong&gt;Update now&lt;/strong&gt; 達到一樣效果。&lt;/p&gt;




&lt;h2&gt;
  
  
  參考資料
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/plugin-marketplaces" rel="noopener noreferrer"&gt;Create and distribute a plugin marketplace — Claude Code Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/discover-plugins" rel="noopener noreferrer"&gt;Discover and install prebuilt plugins — Claude Code Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/plugins-reference" rel="noopener noreferrer"&gt;Plugins reference — Claude Code Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;這篇文章記錄的是目前版本觀察到的行為，Claude Code 的 plugin 系統仍在快速迭代中，實際行為可能隨版本更新而改變，建議操作前對照當下的官方文件再確認一次。&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>claude</category>
      <category>marketplace</category>
      <category>skill</category>
      <category>ai</category>
    </item>
    <item>
      <title>Agency Agents 教學：讓 Claude Code 與 Codex 自動切換 AI 專家角色</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Sat, 15 Aug 2026 01:53:09 +0000</pubDate>
      <link>https://dev.to/letswrite/agency-agents-jiao-xue-rang-claude-code-yu-codex-zi-dong-qie-huan-ai-zhuan-jia-jiao-se-40mc</link>
      <guid>https://dev.to/letswrite/agency-agents-jiao-xue-rang-claude-code-yu-codex-zi-dong-qie-huan-ai-zhuan-jia-jiao-se-40mc</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;現在工作上幾乎都用 Claude Code、Codex 來進行開發，只是常常會遇到以下狀況：&lt;/p&gt;

&lt;p&gt;要讓 AI 做 UI，會希望它像 UI 設計師一樣。&lt;/p&gt;

&lt;p&gt;要 Code Review，希望它變成 Code Reviewer。&lt;/p&gt;

&lt;p&gt;下一個專案要處理 Docker，又希望它不要一直卡在前端的事情上。&lt;/p&gt;

&lt;p&gt;雖然我們可以每次都在 Prompt 裡打一大串：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;你現在是一位資深前端工程師，
熟悉 Vue、React、Accessibility、Core Web Vitals……
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;再雖然我們可以把自己習慣的 Prompt 存起來複製貼上，但基本上人類都是懶惰的，能少打一個字就少打一個字，可以的話最好是 AI 能自己懂現在要扮演什麼樣的專業角色，這就是為什麼貓咪們看到人類都會覺得：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftavn7e9rla733yqcodsd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftavn7e9rla733yqcodsd.jpg" alt="愚蠢的人類" width="800" height="717"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最近看到一個 GitHub 專案，發現可以解決上面很愛演的困擾：&lt;a href="https://github.com/msitarzewski/agency-agents" rel="noopener noreferrer"&gt;Agency Agents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;簡單來說，這專案就是：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;幫 AI 準備好一大票不同領域的專家角色，需要誰時，就叫誰出來&lt;del&gt;當奴才&lt;/del&gt;工作。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;官方目前已經有 230+ 個不同領域的 Agent，而且除了 Claude Code，也支援 Codex、Cursor、GitHub Copilot、Gemini CLI …… 等工具。&lt;/p&gt;

&lt;p&gt;本篇除了筆記 Agency Agents 的安裝方式，也會多做一步：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;建立一個 Router Skill，讓 Claude Code、Codex 自己判斷這次應該找哪個 Agent。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;這樣平常只要正常描述需求：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;幫我重新設計這個 Dashboard，完成後用 Vue 實作。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI 就可以自己判斷要用 UI Designer，或是 Frontend Developer~~，或是鏟屎的~~。&lt;/p&gt;




&lt;h2&gt;
  
  
  Agency Agents 是什麼？
&lt;/h2&gt;

&lt;p&gt;Agency Agents 不是新的 AI 模型，也不是另一套 Claude Code 或 Codex。&lt;/p&gt;

&lt;p&gt;它比較像是一套「&lt;strong&gt;AI 專家角色設定檔&lt;/strong&gt;」。&lt;/p&gt;

&lt;p&gt;每一個 Agent 裡都已經寫好角色、工作方式、核心任務、交付內容、檢查項目等設定。&lt;/p&gt;

&lt;p&gt;官方對它的定位也是「specialized AI agent personalities」，每個角色除了專業領域之外，還會包含自己的工作流程與交付方式。&lt;/p&gt;

&lt;p&gt;例如 Frontend Developer，就不是只有：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a Frontend Developer.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;然後就句點。&lt;/p&gt;

&lt;p&gt;它裡面還會定義前端開發時要注意的東西，例如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vue / React / Angular
Responsive Web Design
Accessibility
Core Web Vitals
Component architecture
Performance
Testing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;所以原本我們跟 Codex 說：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;幫我做這個 Vue 頁面
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;裝完 Agency Agents 後，就可以直接指定要哪個角色，該角色會自動補足我們遺忘的專業度：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the Frontend Developer agent to implement this Vue page.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  安裝 Agency Agents App
&lt;/h2&gt;

&lt;p&gt;官方做了一套 Agency Agents App，它是 Agency Agents 的管理工具，可以瀏覽有哪些角色，再把 Agent 安裝進我們使用的 Coding Agent。&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;Windows 建議直接用 Agency Agents App。&lt;/p&gt;

&lt;p&gt;到 Agency Agents App 的 &lt;a href="https://github.com/msitarzewski/agency-agents-app/releases/latest" rel="noopener noreferrer"&gt;GitHub Release&lt;/a&gt; 下載 Windows Installer 後安裝即可。&lt;/p&gt;

&lt;p&gt;目前官方提供 x64 與 ARM64 的 Windows installer；官方也有特別註明 Windows 版本目前尚未 Code Sign，所以如果跳出 Windows SmartScreen，可以從「其他資訊」繼續執行。&lt;/p&gt;

&lt;p&gt;裝完開啟 Agency Agents。&lt;/p&gt;

&lt;p&gt;接著就可以選想使用的 Agent，以及要安裝到哪一套工具。&lt;/p&gt;

&lt;h3&gt;
  
  
  Mac
&lt;/h3&gt;

&lt;p&gt;Mac 可以直接使用 Homebrew：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; msitarzewski/agency-agents/agency-agents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這也是 Agency Agents 主專案目前提供的快速安裝方式。&lt;/p&gt;

&lt;p&gt;裝完後開啟 Agency Agents App。&lt;/p&gt;

&lt;p&gt;不論是 Windows 還是 Mac，Agency Agents App 的介面都長得相同：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbmmy4rsxo88au71r3mv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbmmy4rsxo88au71r3mv.png" alt="Agency Agents App 介面" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  前端工程師可以先裝哪些 Agent？
&lt;/h2&gt;

&lt;p&gt;Agency Agents 的角色很多，不建議看到什麼就全部裝，最後搞得像寶可夢一樣不全部收集完就睡不著。&lt;/p&gt;

&lt;p&gt;以平常前端開發來說，可以先從這幾個開始：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;適合工作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend Developer&lt;/td&gt;
&lt;td&gt;Vue、React、JavaScript、CSS、RWD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI Designer&lt;/td&gt;
&lt;td&gt;UI、Dashboard、Design System、版面設計&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UX Researcher&lt;/td&gt;
&lt;td&gt;UX、流程、資訊架構、易用性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Software Architect&lt;/td&gt;
&lt;td&gt;系統架構、大型重構&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Reviewer&lt;/td&gt;
&lt;td&gt;PR、Code Review、Bug、Regression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Developer&lt;/td&gt;
&lt;td&gt;比較複雜的開發工作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DevOps Automator&lt;/td&gt;
&lt;td&gt;Docker、CI/CD、Jenkins、部署&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimal Change Engineer&lt;/td&gt;
&lt;td&gt;修 Bug，而且不要順手就重構整個宇宙&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;安裝角色時，就可以直接選擇要裝在哪些 AI 上：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk85dob5lb18l74u6rt5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk85dob5lb18l74u6rt5o.png" alt="選擇裝在全域的哪個 AI 上" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  讓 Claude Code、Codex 自動選 Agent
&lt;/h2&gt;

&lt;p&gt;Agency Agents 官方提供的是「角色」。&lt;/p&gt;

&lt;p&gt;接下來這段則是自己另外加上的功能：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agency Agents Router。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;概念很簡單，我們再建立一個 Skill，專門負責判斷以下情境：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;分析使用者需求 → 需要專家嗎？&lt;br&gt;
需要專家 → 找最適合的 Agency Agent。&lt;br&gt;
不需要專家 → 自己直接做。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;就不會看到「Vue」三個字，就一定叫 Frontend Developer。&lt;/p&gt;

&lt;p&gt;所以 Router 要判斷的是：&lt;strong&gt;這個工作有沒有必要使用專家。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;p&gt;「把 margin-top 20px 改成 24px」→ 不用 Agent。&lt;/p&gt;

&lt;p&gt;「重新設計整個後台，考慮 RWD 與無障礙」→ 用 Agent。&lt;/p&gt;




&lt;h2&gt;
  
  
  Claude Code、Codex 建立 agency-agents Skill
&lt;/h2&gt;

&lt;p&gt;Claude Code、Codex 的 Skill 放置位置不同。&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Mac：&lt;/p&gt;

&lt;p&gt;終端機輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open ~/.claude/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows：&lt;/p&gt;

&lt;p&gt;開啟任一個資料夾，檔案路徑輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nv"&gt;%USERPROFILE%&lt;/span&gt;\.claude\skills\
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接著新增以下資料夾跟檔案：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;agency-agents&lt;/span&gt;
&lt;span class="s"&gt;└── SKILL.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Codex
&lt;/h3&gt;

&lt;p&gt;Mac：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open ~/.agents/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%USERPROFILE%&lt;span class="se"&gt;\.&lt;/span&gt;agents&lt;span class="se"&gt;\s&lt;/span&gt;kills&lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;同樣建立：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agency-agents
└── &lt;span class="o"&gt;[&lt;/span&gt;SKILL.md]&lt;span class="o"&gt;(&lt;/span&gt;http://skill.md/&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接著 SKILL.md 的內容，Claude Code、Codex 有一點不同，可以各自複製貼上。&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/letswritetw/agency-agents-skill/blob/main/claude/SKILL.md" rel="noopener noreferrer"&gt;https://github.com/letswritetw/agency-agents-skill/blob/main/claude/SKILL.md&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Codex
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/letswritetw/agency-agents-skill/blob/main/codex/SKILL.md" rel="noopener noreferrer"&gt;https://github.com/letswritetw/agency-agents-skill/blob/main/codex/SKILL.md&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  讓 Claude Code 自動使用 Router
&lt;/h2&gt;

&lt;p&gt;Skill 建好後其實就可以用了，Claude Code 會依照 description 判斷什麼時候載入。&lt;/p&gt;

&lt;p&gt;不過如果要讓 AI 更積極一點想到 Agency Agents Router，需要再到全域 &lt;code&gt;CLAUDE.md&lt;/code&gt; 補一段規則。&lt;/p&gt;

&lt;p&gt;打開 CLAUDE.md 檔的方式如下。&lt;/p&gt;

&lt;p&gt;Mac：&lt;/p&gt;

&lt;p&gt;終端機輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open ~/.claude/CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows：&lt;/p&gt;

&lt;p&gt;開啟任一個資料夾，檔案路徑輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nv"&gt;%USERPROFILE%&lt;/span&gt;\.claude\CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;在打開的 CLAUDE.md 中加入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Agency Agents&lt;/span&gt;

For development tasks that would materially benefit from specialist expertise, use the &lt;span class="sb"&gt;`agency-agents`&lt;/span&gt; skill to select and delegate to the most appropriate installed Agency Agent.

Prefer using the &lt;span class="sb"&gt;`agency-agents`&lt;/span&gt; skill automatically when the current task clearly matches its purpose. The user should not need to explicitly request the skill.

Use the minimum number of agents necessary.

Prefer direct execution for simple or mechanical tasks.

Do not delegate unless specialist expertise is likely to materially improve correctness, design quality, architecture, maintainability, reliability, performance, accessibility, or review quality.

Do not expand the requested scope merely because additional agents are available.

The user's original requirements, constraints, and project conventions always take priority over agent routing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;之後平常就不用輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use agency-agents.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;直接講需求即可。&lt;/p&gt;




&lt;h2&gt;
  
  
  Codex 的 AGENTS.md
&lt;/h2&gt;

&lt;p&gt;Codex 也可以在自己的 &lt;code&gt;AGENTS.md&lt;/code&gt; 補上 Router 使用規則。&lt;/p&gt;

&lt;p&gt;Mac：&lt;/p&gt;

&lt;p&gt;終端機輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open ~/.codex/AGENTS.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows：&lt;/p&gt;

&lt;p&gt;開啟任一個資料夾，檔案路徑輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nv"&gt;%USERPROFILE%&lt;/span&gt;\.codex\AGENTS.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;在打開的 AGENTS.md 中加入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- AGENCY_AGENTS_START --&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Agency Agents&lt;/span&gt;

For software development tasks that would materially benefit from specialist expertise, use the &lt;span class="sb"&gt;`agency-agents`&lt;/span&gt; skill to select and delegate to the most appropriate installed custom agent.

When the current task clearly matches the &lt;span class="sb"&gt;`agency-agents`&lt;/span&gt; skill description, prefer implicit invocation instead of requiring the user to explicitly request &lt;span class="sb"&gt;`$agency-agents`&lt;/span&gt;.

Use the minimum number of agents necessary.

Prefer direct execution for simple or mechanical tasks.

Do not delegate unless specialist expertise is likely to materially improve correctness, design quality, architecture, maintainability, reliability, performance, accessibility, or review quality.

For tasks spanning multiple disciplines, let the &lt;span class="sb"&gt;`agency-agents`&lt;/span&gt; skill determine whether sequential delegation is useful.

Do not expand the requested scope merely because additional agents are available.

The user's original requirements, constraints, project conventions, and requested scope remain the source of truth.

&lt;span class="c"&gt;&amp;lt;!-- AGENCY_AGENTS_END --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  總結
&lt;/h2&gt;

&lt;p&gt;Agency Agents 裡面有一堆角色可以使用，目前 August 還在實驗中，看是不是在開發上會產生更精緻的結果。&lt;/p&gt;

&lt;p&gt;記得幾年前 ChatGPT 剛出來時，網路上有很多人在分享怎麼寫 Prompt，很多開頭的第一句都是寫「請你扮演 XXX」。&lt;/p&gt;

&lt;p&gt;這件事情現在已經不用了，因為已經可以讓 AI 自己選擇適合的角色來扮演。&lt;/p&gt;

&lt;p&gt;這個 Agency Agents 專案目前有 143k 的星星數，看來懶惰的人類是很多的（誤），只好再放一次貓咪怎麼看我們人類的圖：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftavn7e9rla733yqcodsd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftavn7e9rla733yqcodsd.jpg" alt="愚蠢的人類" width="800" height="717"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>claude</category>
      <category>chatgpt</category>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>Buzz 自架教學：使用 Docker 架設 Relay 與設定 AI Agent 自動回覆</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Fri, 31 Jul 2026 14:38:33 +0000</pubDate>
      <link>https://dev.to/letswrite/buzz-zi-jia-jiao-xue-shi-yong-docker-jia-she-relay-yu-she-ding-ai-agent-zi-dong-hui-fu-2b09</link>
      <guid>https://dev.to/letswrite/buzz-zi-jia-jiao-xue-shi-yong-docker-jia-she-relay-yu-she-ding-ai-agent-zi-dong-hui-fu-2b09</guid>
      <description>&lt;h2&gt;
  
  
  寫在文章之前，關於本文配圖
&lt;/h2&gt;

&lt;p&gt;這篇文章的配圖，選擇了熊本城，是因為這周發生了熊本大地震，而 August 在今年的五月才去那裡旅遊過。&lt;/p&gt;

&lt;p&gt;記得在逛熊本城的時候，裡面有個影片，是在講十年前的熊本大地震發生後，這幾年一路是怎麼走過來，一路又修復成目前的樣子。&lt;/p&gt;

&lt;p&gt;雖然，這次又發生了地震，但相信熊本可以再站起來的。&lt;/p&gt;




&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;最近一直在找一個可以把 AI Agent 一起加入到群組聊天的通訊軟體。&lt;/p&gt;

&lt;p&gt;大部份都要錢，比如 Slack、Google Chat、Teams。&lt;/p&gt;

&lt;p&gt;剛好不知怎麼回事，最近也常常看到講 AI 的電子報提到一個 Buzz，似乎可以解決這需求。&lt;/p&gt;

&lt;p&gt;Buzz，是一套可自架的團隊協作工具。除了頻道聊天，也能加入 Codex、Claude Code 等 AI Agent，讓團隊成員直接在頻道中透過 tag Agent 來呼叫 AI。&lt;/p&gt;

&lt;p&gt;雖說官方提供了 Self Hosting 的安裝說明，但實際上還是踩了幾個坑才順利的讓 Agent 回覆訊息。&lt;/p&gt;

&lt;p&gt;本篇就是筆記實作安裝 Buzz 時的筆記，也把怎麼跳過這些坑的方式紀錄下來，讓看到這篇的你可以從從容容、遊刃有餘的閃過。&lt;/p&gt;

&lt;p&gt;完成本文後，可以做到：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;在 Mac 上透過 Docker Compose 自架 Buzz Relay。&lt;/li&gt;
&lt;li&gt;使用 Buzz Desktop 連接自己的 Relay。&lt;/li&gt;
&lt;li&gt;建立 Buzz Community 與頻道。&lt;/li&gt;
&lt;li&gt;啟動內建的三個 Agent。&lt;/li&gt;
&lt;li&gt;使用 Codex 作為 Fizz 的 AI 執行環境。（也可以用 Claude）&lt;/li&gt;
&lt;li&gt;在頻道中透過 &lt;code&gt;@Fizz&lt;/code&gt; 讓 Agent 回覆訊息。&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  安裝 Buzz Relay
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 安裝必要工具
&lt;/h3&gt;

&lt;p&gt;需要先安裝好以下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Docker Desktop&lt;/li&gt;
&lt;li&gt;Docker Compose 2.24.4 以上&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;確認 Docker Desktop 已經啟動後，再繼續安裝。&lt;/p&gt;

&lt;h3&gt;
  
  
  2. 下載 Buzz 原始碼
&lt;/h3&gt;

&lt;p&gt;Clone 官方 GitHub 上的專案：&lt;a href="https://github.com/block/buzz" rel="noopener noreferrer"&gt;https://github.com/block/buzz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接著進到「deploy/compose」資料夾中，本篇會用 Docker 的方式安裝。&lt;/p&gt;

&lt;h3&gt;
  
  
  3. 建立 &lt;code&gt;.env&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;deploy/compose 的資料夾裡有一個「.env.example」，這份檔另存為「.env」。&lt;/p&gt;

&lt;p&gt;接著，把 .env 裡「buzz.example.com」、「CHANGE_ME…」的部份全都修改，變成我們要的內容。&lt;/p&gt;

&lt;p&gt;另外要注意，目前官方的版本有個 bug，主要是Buzz Relay 容器會顯示 &lt;code&gt;unhealthy&lt;/code&gt;，造成後續安裝不起來，目前解決的方式就是：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 原本&lt;/span&gt;
&lt;span class="nv"&gt;BUZZ_GIT_CONFORMANCE_PROBE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# 改成&lt;/span&gt;
&lt;span class="nv"&gt;BUZZ_GIT_CONFORMANCE_PROBE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;需要 KEY 的部份，可以使用以下指令產生隨機密鑰：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;產生一般密碼：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rand &lt;span class="nt"&gt;-base64&lt;/span&gt; 24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;不同用途的密鑰應分開產生，不要全部使用同一組。&lt;/p&gt;

&lt;p&gt;如果覺得一行行修改很麻煩，這一步 ChatGPT 有給了一組指令，直接專案內開啟終端機執行以下也行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
BUZZ_IMAGE=ghcr.io/block/buzz:main

BUZZ_DOMAIN=127.0.0.1
RELAY_URL=ws://127.0.0.1:3300

BUZZ_MEDIA_BASE_URL=http://127.0.0.1:3300/media
BUZZ_MEDIA_SERVER_DOMAIN=127.0.0.1

BUZZ_CORS_ORIGINS=tauri://localhost,http://tauri.localhost,https://tauri.localhost,http://127.0.0.1:3300

BUZZ_REQUIRE_AUTH_TOKEN=false
BUZZ_REQUIRE_RELAY_MEMBERSHIP=false
BUZZ_ALLOW_NIP_OA_AUTH=false

BUZZ_AUTO_MIGRATE=true
BUZZ_GIT_CONFORMANCE_PROBE=false

RUST_LOG=buzz_relay=info,buzz_db=info,buzz_auth=info,buzz_pubsub=info,tower_http=info

BUZZ_RELAY_PRIVATE_KEY=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
BUZZ_GIT_HOOK_HMAC_SECRET=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;

POSTGRES_DB=buzz
POSTGRES_USER=buzz
POSTGRES_PASSWORD=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 16&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;

REDIS_PASSWORD=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 16&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;

BUZZ_S3_ACCESS_KEY=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 10&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
BUZZ_S3_SECRET_KEY=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 16&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
BUZZ_S3_BUCKET=buzz-media
BUZZ_S3_ADDRESSING_STYLE=path

BUZZ_HTTP_PORT=3300
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;因為 3000 是個很多應用程式都會預設使用的 PORT，所以上面的範例改用 3300。&lt;/p&gt;

&lt;h3&gt;
  
  
  4. 啟動 Buzz Relay
&lt;/h3&gt;

&lt;p&gt;專案中開啟終端機，依序執行以下：&lt;/p&gt;

&lt;p&gt;加入執行權限：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;啟動：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./run.sh start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;查看狀態：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./run.sh status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;正常情況下會看到：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;relay       healthy
postgres    healthy
redis       healthy
minio       healthy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. 測試 Relay
&lt;/h3&gt;

&lt;p&gt;打開瀏覽器，網址貼上：&lt;a href="http://127.0.0.1:3300%E3%80%82" rel="noopener noreferrer"&gt;http://127.0.0.1:3300。&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;成功的話就會看到結果：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs22q0rbzx9qcdlym7i08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs22q0rbzx9qcdlym7i08.png" alt="Buzz Relay Docker 部署成功畫面與 JSON 網頁回傳結果" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;順便推薦一下本站開發的擴充功能 &lt;a href="https://chromewebstore.google.com/detail/lets-json/gcbgdeakiaapdhjeoinmodehiemedimf" rel="noopener noreferrer"&gt;Let’s JSON&lt;/a&gt;，網址如果回傳的是 JSON，就會看到像上面截圖那樣美美的樣子。&lt;/p&gt;

&lt;p&gt;Buzz Relay 安裝成功，剩下的事就是讓各個要使用的團隊成員電腦安裝 Buzz，然後設定抓我們自架的 Buzz Relay 了。&lt;/p&gt;




&lt;h2&gt;
  
  
  下載、安裝 Buzz
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 下載 Buzz Desktop
&lt;/h3&gt;

&lt;p&gt;前往 Buzz Releases：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/block/buzz/releases" rel="noopener noreferrer"&gt;https://github.com/block/buzz/releases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apple Silicon Mac，例如 M1、M2、M3、M4，下載名稱包含以下文字的版本：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aarch64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Intel Mac 則下載：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows 下載：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Buzz_0.5.2_x64-setup_alpha-unsigned.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;但因為 Windows 版本的 Buzz 目前尚未完成數位簽章，開啟時系統會跳出安全性警告；此時可點擊「更多資訊」並選擇「仍要執行」（或於檔案屬性中勾選「取消封鎖」）即可完成安裝。&lt;/p&gt;

&lt;h3&gt;
  
  
  2. 登入 Buzz
&lt;/h3&gt;

&lt;p&gt;安裝完 Buzz，打開後會遇到幾個項目，因為 Relay 我們是自架的，因此以下選項要這樣選擇。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Join or create a community&lt;/strong&gt;：選擇「I already have a community」。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxitsu5lrk2k9j0g7gxkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxitsu5lrk2k9j0g7gxkm.png" alt="Buzz 桌面端建立 Community 選擇畫面：I already have a community" width="800" height="834"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reconnect to your community&lt;/strong&gt;：選擇「I’m a member or admin」。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fitsidygh7sap9lue7ez7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fitsidygh7sap9lue7ez7.png" alt="Buzz 桌面端身份選擇：I am a member or admin" width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接著會需要輸入我們 Buzz Relay 的網址，之前的 .env 裡寫的是 &lt;a href="http://127.0.0.1:3300%EF%BC%8C%E9%80%99%E9%82%8A%E5%B0%B1%E7%85%A7%E5%A1%AB%EF%BC%8C%E5%A6%82%E6%9E%9C%E4%B8%8A%E9%9D%A2%E4%BD%A0%E6%9C%89%E8%87%AA%E8%A1%8C%E4%BF%AE%E6%94%B9%E7%9A%84%E8%A9%B1%EF%BC%8C%E9%80%99%E9%82%8A%E5%B0%B1%E5%A1%AB%E5%AF%AB%E4%BD%A0%E4%BF%AE%E6%94%B9%E7%9A%84%E3%80%82" rel="noopener noreferrer"&gt;http://127.0.0.1:3300，這邊就照填，如果上面你有自行修改的話，這邊就填寫你修改的。&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqgdlmlqs6dj2dmcx1cg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqgdlmlqs6dj2dmcx1cg.png" alt="輸入安裝 Buzz Relay 時填的網址" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以上，就可以進到我們自架的 Buzz 了。&lt;/p&gt;




&lt;h2&gt;
  
  
  使用 AI Agents 功能
&lt;/h2&gt;

&lt;p&gt;這段就是 August 一直踩坑的部份，只要下列幾個坑都閃過，之後在對話框裡 @ AI Agents 就會得到回應。&lt;/p&gt;

&lt;h3&gt;
  
  
  確認有使用 AI
&lt;/h3&gt;

&lt;p&gt;本機確定有安裝好 Claude CLI，或 Codex CLI。&lt;/p&gt;

&lt;p&gt;如果沒有訂閱這兩家方案，也可以找網路上提供有免費額度的 LLM 來使用。&lt;/p&gt;

&lt;p&gt;August 因為 Claude、ChatGPT 都有付費訂閱，所以示範的是這兩家的。&lt;/p&gt;

&lt;p&gt;在 Buzz 的設定頁裡，要確認有使用 AI，如圖：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2f1eldko9dytoiurqtj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2f1eldko9dytoiurqtj7.png" alt="Buzz 系統設定：確認 AI Agent 狀態為 Ready" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;顯示為「Ready」代表有連結成功。&lt;/p&gt;

&lt;p&gt;Buzz Agent 指的是用各家的 API。&lt;/p&gt;

&lt;h3&gt;
  
  
  確認 Community 網址相同
&lt;/h3&gt;

&lt;p&gt;點擊左下角自己的帳號，接著「Local Dev &amp;gt; Community settings」：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnzmjh3ovd12xq9usyisf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnzmjh3ovd12xq9usyisf.png" alt="Buzz 設定選單：進入 Community settings 社群設定" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;裡面會看到一個設定是「Relay URL」：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjidicbwuykfwk5q44mcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjidicbwuykfwk5q44mcr.png" alt="確認 Relay URL 跟本機網址相同" width="800" height="1118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;要確認 URL 裡，&lt;code&gt;ws://&lt;/code&gt; 後的網址，跟我們的網址是相同的，比方上面在設定 Community 的網址是 &lt;a href="http://127.0.0.1:3300%EF%BC%8C%E9%82%A3%E9%80%99%E9%82%8A" rel="noopener noreferrer"&gt;http://127.0.0.1:3300，那這邊&lt;/a&gt; &lt;code&gt;ws://&lt;/code&gt; 後就要是 127.0.0.1:3300。&lt;/p&gt;

&lt;p&gt;這邊如果不相同，那 Agents 收到的來源就會不同，就會一直抓不到我們 @ 它們的訊息。&lt;/p&gt;

&lt;h3&gt;
  
  
  開放 Agents 接受回應
&lt;/h3&gt;

&lt;p&gt;在 Agent 的設定畫面裡，點擊「Advanced」，裡面有一項「Who can talk to this agent」，要選成「Anyone」：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvazedip9tj3y2f6zbxn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvazedip9tj3y2f6zbxn4.png" alt="Buzz AI Agent 進階設定：將 Who can talk to this agent 權限設為 Anyone" width="800" height="1052"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;然後，上面的頁籤切到「Customize for this agent」，確認有選好要使用的模型：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqelel4q3qpapmtbqkjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqelel4q3qpapmtbqkjq.png" alt="Buzz Agent 模型配置：選擇對應的 LLM Model" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最後，要確認 Agents 是運行中的：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgp987uakwrltnrv3l6q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgp987uakwrltnrv3l6q2.png" alt="Buzz 頻道控制列：確認 AI Agent 處於 Running 運行狀態" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  實際測試
&lt;/h2&gt;

&lt;p&gt;可以做一個簡單的測試，看 AI Agents 是不是會回應。&lt;/p&gt;

&lt;p&gt;我們在預設建好的 Channel 裡，@ Agent 就行，就像我們在 LINE 上 @ 朋友們一樣。&lt;/p&gt;

&lt;p&gt;成功的話，就會看到被 @ 的 Agent 正在思考中的訊息，也會對訊息回應 Emoji，思考完成，Agent 就會回覆訊息，如下圖：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvnjzftuudl4d6u8eni6o.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvnjzftuudl4d6u8eni6o.jpeg" alt="Buzz 頻道實際測試：@Fizz 呼叫 AI Agent 並獲得自動回覆訊息" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  小結
&lt;/h2&gt;

&lt;p&gt;雖然可以把 AI Agents 加進聊天中，但 Windows 版的安裝比較麻煩。&lt;/p&gt;

&lt;p&gt;而且，Codex、Claude Code 上都裝了很多習慣的 Skills，表面上 AI 是回說可以直接用，但輸入 &lt;code&gt;/&lt;/code&gt; 或 &lt;code&gt;$&lt;/code&gt; 時沒有出現選項，也不知道到底有用 Skills 還是沒有。&lt;/p&gt;

&lt;p&gt;只能說，Buzz 還是一個正在開發中的工具，看 GitHub 上的 Release 紀錄更新得很頻繁，再給它一點時間，它會更好的（？）。&lt;/p&gt;

</description>
      <category>buzz</category>
      <category>docker</category>
      <category>claude</category>
      <category>agents</category>
    </item>
    <item>
      <title>Outline Wiki 自架教學（三）：Codex 串接 MCP</title>
      <dc:creator>Let's Write</dc:creator>
      <pubDate>Fri, 24 Jul 2026 13:44:02 +0000</pubDate>
      <link>https://dev.to/letswrite/outline-wiki-zi-jia-jiao-xue-san-codex-chuan-jie-mcp-5c4b</link>
      <guid>https://dev.to/letswrite/outline-wiki-zi-jia-jiao-xue-san-codex-chuan-jie-mcp-5c4b</guid>
      <description>&lt;h2&gt;
  
  
  本篇要解決的問題
&lt;/h2&gt;

&lt;p&gt;上一篇是 &lt;a href="https://www.letswrite.tw/outline-claude-mcp/" rel="noopener noreferrer"&gt;Outline + Claude&lt;/a&gt;，不一定每個人都有訂閱 Claude 方案，所以也提供 Codex 的方式。&lt;/p&gt;

&lt;p&gt;跟著本篇一步步走，完成設定後，Codex 可以透過 MCP 呼叫 Outline 工具，執行很多好棒棒的事，例如：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;搜尋或列出 Outline 文件&lt;/li&gt;
&lt;li&gt;讀取、編輯文件集內的文件&lt;/li&gt;
&lt;li&gt;建立文件集&lt;/li&gt;
&lt;li&gt;移動文件&lt;/li&gt;
&lt;li&gt;執行 MCP Server 已提供的其他 Outline 操作&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Codex App、Codex CLI 與 IDE 擴充套件會共用 Codex 的 MCP 設定，因此設定完成後，不需要在每個客戶端重複設定。&lt;/p&gt;




&lt;h2&gt;
  
  
  前置準備
&lt;/h2&gt;

&lt;p&gt;開始前，請先確認已具備以下環境：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;安裝 &lt;a href="https://chatgpt.com/download/" rel="noopener noreferrer"&gt;Codex&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;安裝 &lt;a href="https://nodejs.org/zh-tw/download" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;可正常連線的 &lt;a href="https://www.letswrite.tw/outline-wiki-install/" rel="noopener noreferrer"&gt;Outline&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  建立 Outline API Token
&lt;/h2&gt;

&lt;h3&gt;
  
  
  進入喜好設定
&lt;/h3&gt;

&lt;p&gt;登入 Outline 後，點擊左下角帳號旁的「⋯」，再選擇「喜好設定」。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkmrw46lqu1goihbjhfu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkmrw46lqu1goihbjhfu6.png" alt="從左下角選單進入喜好設定"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  開啟 API &amp;amp; Access
&lt;/h3&gt;

&lt;p&gt;在左側設定選單中選擇「API &amp;amp; Access」，接著點擊右上角的「新 API 金鑰」。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbdrrx9bhqgd4ytn13fus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbdrrx9bhqgd4ytn13fus.png" alt="進入 API &amp;amp; Access 並建立新 API 金鑰"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  設定金鑰名稱、範圍與到期日
&lt;/h3&gt;

&lt;p&gt;建議輸入容易辨識用途的名稱，例如：「AI MCP」、「Codex MCP」等等。&lt;/p&gt;

&lt;p&gt;原始流程中的設定為：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;範圍：&lt;/strong&gt; 留空&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;到期日：&lt;/strong&gt; 沒有期限&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F678ptivgzmstlca5rksq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F678ptivgzmstlca5rksq.png" alt="設定 API 金鑰範圍與到期日"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;範圍留空通常代表不限制特定 API 權限。這種設定操作最簡單，但權限也較大。&lt;/p&gt;

&lt;h3&gt;
  
  
  複製 API Token
&lt;/h3&gt;

&lt;p&gt;建立完成後，點擊「複製」，並先將 Token 暫存在安全的位置，因為 Token 只會出現一次。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvkirz2pqrugh1lu8g94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvkirz2pqrugh1lu8g94.png" alt="複製 Outline API Token"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;注意：API Token 等同於帳號憑證，不要貼到 Git、公開文件、部落格文章、聊天群組或未加密的筆記中。&lt;/p&gt;




&lt;h2&gt;
  
  
  設定 Codex MCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  開啟 Codex 設定檔
&lt;/h3&gt;

&lt;p&gt;Codex 使用 &lt;code&gt;config.toml&lt;/code&gt; 儲存 MCP 與其他本機設定。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;作業系統&lt;/th&gt;
&lt;th&gt;使用者層級設定檔路徑&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%USERPROFILE%\.codex\config.toml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.codex/config.toml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;使用者層級的設定會套用到 Codex App、Codex CLI 與 IDE 擴充套件。&lt;/p&gt;

&lt;h4&gt;
  
  
  Windows
&lt;/h4&gt;

&lt;p&gt;開啟 PowerShell，執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\.codex"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\.codex\config.toml"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;notepad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\.codex\config.toml"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這三個指令會依序：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;建立 &lt;code&gt;.codex&lt;/code&gt; 資料夾。&lt;/li&gt;
&lt;li&gt;建立 &lt;code&gt;config.toml&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;使用記事本開啟設定檔。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;也可以開啟任一個資料夾，在檔案路徑列貼上：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%USERPROFILE%&lt;span class="se"&gt;\.&lt;/span&gt;codex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接著開啟 &lt;code&gt;config.toml&lt;/code&gt;。&lt;/p&gt;

&lt;h4&gt;
  
  
  macOS：使用 Finder 開啟
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;開啟 Finder。&lt;/li&gt;
&lt;li&gt;點擊上方選單的「前往」。&lt;/li&gt;
&lt;li&gt;選擇「前往檔案夾⋯」。&lt;/li&gt;
&lt;li&gt;貼上下列路徑：
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.codex/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;找到並開啟：
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;config.toml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  macOS：使用終端機開啟
&lt;/h4&gt;

&lt;p&gt;也可以在終端機執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.codex"&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.codex/config.toml"&lt;/span&gt;
open &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.codex/config.toml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這三個指令會依序建立設定資料夾、建立設定檔，並使用 macOS 文字編輯器開啟。&lt;/p&gt;

&lt;p&gt;若檔案已存在，建議先複製一份備份，再進行修改。&lt;/p&gt;




&lt;h2&gt;
  
  
  加入 Outline MCP Server
&lt;/h2&gt;

&lt;p&gt;請將下列設定加入 &lt;code&gt;config.toml&lt;/code&gt;：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.outline]&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"npx"&lt;/span&gt;
&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"mcp-remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"https://192.168.x.x:3023/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"--header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"Authorization:${AUTH_HEADER}"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.outline.env]&lt;/span&gt;
&lt;span class="py"&gt;AUTH_HEADER&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer YOUR_OUTLINE_API_TOKEN"&lt;/span&gt;
&lt;span class="py"&gt;NODE_TLS_REJECT_UNAUTHORIZED&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;請替換以下內容：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設定值&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;https://192.168.x.x:3023/mcp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Outline MCP Server 的連線網址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YOUR_OUTLINE_API_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;前一步建立的 Outline API Token&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  使用有效 HTTPS 憑證時的簡化設定
&lt;/h2&gt;

&lt;p&gt;如果 Outline MCP Server 已使用有效且受信任的 HTTPS 憑證，Codex 可以直接連線，不需要 Node.js、&lt;code&gt;npx&lt;/code&gt; 或 &lt;code&gt;mcp-remote&lt;/code&gt;。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.outline]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://YOUR_MCP_HOST/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;Authorization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer YOUR_OUTLINE_API_TOKEN"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;例如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.outline]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://outline-mcp.example.com/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;Authorization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer YOUR_OUTLINE_API_TOKEN"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這是較簡單的正式環境設定方式。&lt;/p&gt;

&lt;p&gt;注意：這個範例會把 Token 直接寫在 &lt;code&gt;config.toml&lt;/code&gt; 中。請妥善保護設定檔，不要提交到 Git。&lt;/p&gt;




&lt;h2&gt;
  
  
  Windows 與 macOS 的 npx 路徑問題
&lt;/h2&gt;

&lt;p&gt;Codex App 原則上可以直接執行 &lt;code&gt;npx&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;若 MCP 啟動失敗，可能是桌面程式找不到 &lt;code&gt;npx&lt;/code&gt; 的執行路徑。&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;在 PowerShell 執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;where.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;npx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;可能會回傳：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;C&lt;/span&gt;:\Program &lt;span class="kd"&gt;Files&lt;/span&gt;\nodejs\npx.cmd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;可以將：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"npx"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;改成 TOML 的單引號字串：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'C:\Program Files\nodejs\npx.cmd'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TOML 的單引號字串不需要將 Windows 反斜線寫成 &lt;code&gt;\\&lt;/code&gt;。&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;

&lt;p&gt;在終端機執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;which npx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apple Silicon Mac 使用 Homebrew 時，可能回傳：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/opt/homebrew/bin/npx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Intel Mac 使用 Homebrew 時，可能回傳：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/local/bin/npx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接著將：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"npx"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;改成實際查到的完整路徑，例如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/opt/homebrew/bin/npx"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;注意：不要直接照抄範例路徑，應先使用 &lt;code&gt;where.exe npx&lt;/code&gt; 或 &lt;code&gt;which npx&lt;/code&gt;，再填入自己電腦實際回傳的結果。&lt;/p&gt;




&lt;h2&gt;
  
  
  重新啟動 Codex
&lt;/h2&gt;

&lt;p&gt;修改 &lt;code&gt;config.toml&lt;/code&gt; 後，需要重新啟動正在使用的 Codex。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Codex App／ChatGPT 桌面版：&lt;/strong&gt; 完整結束後重新開啟。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex CLI：&lt;/strong&gt; 離開目前工作階段，再重新執行 &lt;code&gt;codex&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE 擴充套件：&lt;/strong&gt; 重新啟動擴充套件或重新載入編輯器視窗。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;只關閉視窗不一定代表程式已完全結束：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows：&lt;/strong&gt; 從系統匣結束程式，或到工作管理員確認是否仍在背景執行。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS：&lt;/strong&gt; 按下 &lt;code&gt;Command + Q&lt;/code&gt;，必要時到「活動監視器」確認程式是否仍在執行。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;重新啟動時，Codex 會：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;讀取 &lt;code&gt;~/.codex/config.toml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;透過 &lt;code&gt;npx&lt;/code&gt; 啟動 &lt;code&gt;mcp-remote&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;使用 Authorization Header 連接 Outline MCP Server&lt;/li&gt;
&lt;li&gt;載入 MCP Server 提供的工具&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;首次執行 &lt;code&gt;npx -y mcp-remote&lt;/code&gt; 時，可能需要下載套件，因此等待時間會比後續啟動稍長。&lt;/p&gt;




&lt;h2&gt;
  
  
  測試 Outline MCP 是否連線成功
&lt;/h2&gt;

&lt;h3&gt;
  
  
  使用 Codex CLI 檢查
&lt;/h3&gt;

&lt;p&gt;先執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex mcp list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;正常情況下，清單中會出現：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;outline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;也可以執行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex mcp &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;查看目前版本支援的 MCP 管理指令。&lt;/p&gt;

&lt;p&gt;進入 Codex CLI 後，輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;即可查看目前工作階段載入的 MCP Server 與工具。&lt;/p&gt;

&lt;h3&gt;
  
  
  使用對話測試
&lt;/h3&gt;

&lt;p&gt;在 Codex 中輸入：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;可以接到 Outline MCP 嗎？請列出目前可使用的 Outline 工具。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接著再測試實際讀取：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;請列出 Outline 中目前可以看到的文件與文件集。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;也可以指定操作：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;請搜尋 Outline 中包含「前端」關鍵字的文件。
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;連線成功時，Codex 會顯示已載入 Outline 工具，並能回傳工作區中的文件或 Collection 資訊。&lt;/p&gt;

</description>
      <category>outline</category>
      <category>ai</category>
      <category>codex</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
