FR-190 · 定版交付
已合回 feature/FR-172,未 push;套件 jedi-agent-protocol 0.6.1、jedi-remote-agent 1.18.3 已在 Nexus;出貨基線已含本案 migration;STG/POC 未上
Agent 主控台的權限從 7 個大範圔改成 26 顆能力點:11 個主控台功能,每個功能依「檢視、設定、執行、下載」分開給。管理員在主系統角色設定頁「Agent」組的「Agent 主控台」子頁逐顆勾,或按「比照主系統功能帶入」一次帶入。使用者進主控台時主系統把他持有的顆寫進入場券,Agent 照票開頁面、擋 API;斷線用的離線操作者名單每次心跳依當下持有的顆重算。主系統、jedi-remote-agent、Agent 三邊都讀 jedi-agent-protocol 的同一份清單。
Agent 主控台權限變成 26 顆主系統能力點 agent.<功能>.<動作>,角色頁逐顆給;簽票與離線名單照既有管道把持有的顆帶到 Agent;Agent 版本舊時丟掉不認得的顆、照常登入並提示;主系統派下來的掃描紀錄歸「掃描 · 檢視」。
本案動到四個程式 repo 與兩類文件,另外重產了出貨基線。契約(清單本身)在 jedi-agent-protocol;主系統這邊是資料遷移、端點守門與角色頁;Agent 這邊是驗票與路由守門;前端分主控台與主站兩塊。下表列每個 repo 交付了什麼、落在哪些檔。
| Repo | 內容 | 位置 |
|---|---|---|
| jedi-agent-protocol 0.6.1 | console_scopes 模組:26 顆清單(資源、動作、中英顯示名、離線可下放旗標、對應主系統功能)、OFFLINE_GRANTABLE(24 顆)、MIRRORS(15 條)、scope_of()/capability_of() 互轉、CAPABILITY_MODULE='agent'、LICENSE_MODULE='remote-agent-manage'、HOME_ROUTE='remote-agent-console';錯誤碼 TICKET_SCOPE_EMPTY;三邊鏡像測試 |
jedi_agent_protocol/console_scopes.py、error_codes.py、tests/test_console_scopes.py、tests/test_mirror.py |
| jedi-remote-agent 1.18.3 | CAPABILITIES(4 顆管理用+26 顆主控台)、簽票範圍表、離線名單範圍表三張表由 console_scopes 推導;舊 7 顆退場,新顆 legacy_name 記舊名 |
jedi_remote_agent/plugin/contract.py |
| 主系統 BE | 主線 migration:ui_routes 子頁 remote-agent-console、舊 7 顆字面持有者展開到新顆(含 pattern 角色與客戶自建角色)、刪舊顆與三張關聯表的列、角色範本「稽核員」預帶 agent.scan.read/agent.scan.download;_ADMIN_CAPABILITIES 全部換 agent.*,執行紀錄下載依紀錄種類細判;權限矩陣產生器把 protocol 清單內的顆視為「Agent 端有守」;唯讀端點 GET /roles/<uid>/agent-console-mirror;守衛測試「出貨 seed 的萬用字元 pattern 不得命中 agent.*」 |
scripts/sql/2026-11-10-fr190-agent-console-capabilities.sql、core/plugins/remote_agent.py、core/plugins/_agent_local_runs.py、scripts/deliverables/gen_permission_matrix.py、api/access/routes/agent_console_mirror_route.py(19 行)、app/access/service/agent_console_mirror_service.py(24 行)、test/test_fr190_seed_patterns_exclude_agent_module.py |
| Agent | CONSOLE_SCOPES 改 import 契約;驗票把不認得的範圔丟掉並回數量,全部不認得才 403;/api/me 多回 dropped_scopes;73 條主控台路由守門一對一換成 26 顆短名;執行紀錄不分來源歸 scan.read;全 blueprint 單一 scope 對表守衛 |
guidant_agent/shared/constants.py、sync/domain/agent_auth.py、identity/app/session.py、console/api/*.py、test/test_fr190_scope_table.py |
| 主系統 FE | 主控台:router.js 每頁 scope 與 13 個 view 的按鈕判斷換 26 顆短名、頂部舊版 Agent 橫幅;主站:角色樹新增 agent 模組 26 顆標籤與說明、動作順序加 download、「Agent 主控台」子頁列的「比照主系統功能帶入」鈕、離線操作者對話框權限標籤、Agent 管理頁按鈕換顆、權限常數檔重產 |
console/router.js、console/Shell.vue、src/config/locales/i18n/*/permission-tree.json、src/composables/usePermissionGrouping.js、src/components/access/PermissionMenuTree.vue、src/composables/useAgentConsoleMirror.js、src/config/capabilities.js |
| 文件 | 頁面 SPEC 三頁、Agent 使用手冊權限節 | docs/spec-site/current/evidence/agent-console.md §3、evidence/remote-agent-manage.md、system-admin/access-role-detail.md §4.2;docs/user-manual/agent/13-standalone-operation.md「角色權限:26 項」 |
| 出貨基線 | 188 基線庫已套本案 migration;02-schema.sql、04-seed-core.sql、99-stamp.sql 重產;本機乾淨 postgres:16 完整 init.sh 裝驗通過 |
scripts/init/ |
能力點全名 agent.<功能>.<動作>;入場券與 Agent 守門用去掉 agent. 的 <功能>.<動作>。模組 agent 同時是角色樹第一層的分組名。清單的單一來源是 jedi_agent_protocol.console_scopes,三邊都從那裡讀,不各自硬寫。
| 功能 | read(檢視) | update(設定) | execute(執行) | download(下載) | 離線可下放 |
|---|---|---|---|---|---|
files 檔案 |
清單、預覽、上傳者清單 | 證據檔原檔 | 是 | ||
scan 掃描 |
工具、目標、基準、紀錄(含主系統派工) | 執行、取消、重送結果 | 報告原檔、工具 log | 是 | |
netdiag 網路除錯 |
目標、紀錄、預估、報告預覽 | 執行、重送 | 除錯報告 | 是 | |
logs 日誌 |
查日誌 | 匯出 | 是 | ||
tools 工具設定 |
看已設定(不回密碼) | 改帳密、測試連線、清除 | 是 | ||
ldap LDAP |
看設定 | 改來源、改本機、測試、重新信任 | 是 | ||
keys 金鑰 |
看狀態 | 換發 | 是 | ||
license 授權 |
看狀態 | 上傳、線上啟用 | 是 | ||
ops 維運 |
服務狀態、離線登入設定、儲存設定 | 改名稱、主系統位址、儲存、離線登入;補更新弱點庫/基準/目標 | 重啟 | 診斷包 | 是(含重啟) |
update 更新 |
版本、檢查新版 | 線上換版、離線上傳換版 | 否 | ||
setup 連線設定 |
註冊狀態 | 註冊、探測、換主系統 | 是 |
共 26 顆;離線操作者可下放 24 顆,只有 update.read、update.execute 不下放(換版不可逆,斷線時不該有人換版;重啟可逆、斷線卡住要能現場重啟,所以下放)。
模組為什麼是 agent 而不是 detection:出貨 seed 的系統角色帶萬用字元 pattern——稽核主管 detection.*.*,稽核員、一般成員、唯讀 detection.*.read。能力點若放在 detection 底下,新裝客戶的稽核主管升級後自動持有全部 26 顆(含檔案下載、重啟、換版),唯讀角色自動持有 11 顆檢視。agent 是新模組,沒有任何既有 pattern 命中。DEV 的角色沒有這些 pattern,在 DEV 驗不出來,所以守衛測試掃 seed 檔本身。
權限從角色設定頁到 Agent 主控台走兩條路:使用者在線時點「進入主控台」拿入場券(即時路),斷線時用離線操作者名單登入(離線路)。兩條路都讀同一份角色持有,本案沒有新增任何同步機制,只把寫進票與名單的範圍從 7 個換成 26 個。下圖是完整順序。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
autonumber
actor M as 管理員
participant H as 主系統 API
actor U as 使用者(瀏覽器)
participant A as Agent(:8444 主控台)
M->>H: 角色頁「Agent 主控台」子頁勾顆,或按「比照主系統功能帶入」
H->>H: GET /roles/{uid}/agent-console-mirror 依 MIRRORS 算出該加的顆(只算不寫)
M->>H: 儲存角色
U->>H: POST /remote-agents/{uid}/console-ticket
H->>H: 逐顆查使用者持有的 26 顆,寫進票 scope;一顆都沒有回 403
H-->>U: 五分鐘入場券(RS256,scope 原樣 26 個短名字串)
U->>A: 帶票登入
A->>A: 範圍過濾:認得的留下、不認得的丟掉並計數;全部不認得回 403 TICKET_SCOPE_EMPTY
A-->>U: session;/api/me 回 scope 與 dropped_scopes,前端依 *.read 開分頁、依 update/execute/download 開按鈕
A->>H: 心跳
H->>H: 離線操作者名單:每人依當下持有的顆算可下放範圍(24 顆內)
H-->>A: 整份名單;縮權的人踢掉已登入 session
第 5 步的「持有」由 jedi-iam 展開服務算,含萬用字元 pattern(*.*.* 的系統管理員自然持有 26 顆)。第 8 步丟掉不認得的範圍只會讓人少拿權限、不會多拿,所以主系統可以先升、Agent 慢慢跟;舊 Agent 收到新顆名會全部丟掉,等同登入失敗。反向不相容:新 Agent 不認舊的 7 個範圍名,舊主系統發的票在新 Agent 上全丟、回 403,升級順序必須主系統先。第 11 步的名單只決定「能不能離線登入這台」,範圔永遠跟著角色,主系統改角色下一次心跳就生效。
scan.download/netdiag.download 任一,route 內再依紀錄種類細判一次。ops.execute;換發憑證→keys.update;代設工具帳密→tools.update;補更新基準→ops.update。Agent 管理頁本身的新增、檢視、修改、刪除仍是 detection.remote-agent.* 四顆,不動。scan.read 看、scan.download 帶走;網路除錯紀錄歸 netdiag.*。MIRRORS 15 條把對應顆勾起來,管理員可再取消,要按儲存才寫入。對照:檢測工具的檢視/修改→工具設定的檢視/設定;掃描設定檔檢視→掃描檢視、修改→掃描執行;LDAP 設定→LDAP;平台授權→授權;儲存設定→維運檢視/設定;產品更新→更新;Agent 管理的修改→金鑰設定+維運執行;Agent 管理的新增→連線設定;主系統證據下載→檔案下載。日誌與網路除錯沒有對應的主系統功能,不會被帶入。configure→工具、LDAP、金鑰、授權、維運、連線六組的檢視+設定共 14 顆再加掃描檢視;browse-files→檔案檢視、日誌檢視;run-local-scan→掃描檢視+執行、日誌檢視;net-diag→除錯檢視+執行、日誌檢視;download-files→檔案、掃描、除錯、日誌四顆下載,ops.download(診斷包)只給同時持 configure 者;restart→ops.execute;upgrade→更新兩顆。萬用字元持有者不展開。展開後刪舊 7 顆與 role_capabilities、route_capabilities、role_capability_patterns 的對應列。*.*.*;稽核員 agent.scan.read、agent.scan.download;檔案下載四顆與更新兩顆不預帶給任何非管理員角色。scope 是 26 個短名的字串陣列,原樣寫,約 2.5 KB;Agent 登入入口 body 上限 8 KB。AGENT_CONSOLE_SCOPE_DENIED;票上範圍全部不認得 403 TICKET_SCOPE_EMPTY;主系統一顆都沒有簽不到票 403 AGENT_CONSOLE_NO_SCOPE。require_session(<短名>) 與主系統 _ADMIN_CAPABILITIES;直接打 API 一樣被擋。自動測試(四 repo 工作區,合回前;protocol 鏡像測試在合回後對主線再跑一次):
| 範圍 | 結果 |
|---|---|
| jedi-agent-protocol | 96 條綠,含三邊鏡像(remote-agent 不再硬寫舊名、Agent CONSOLE_SCOPES 改 import、主系統 _ADMIN_CAPABILITIES 無舊名) |
| jedi-remote-agent | 606 條綠 |
| Agent | 全套 2542 條綠;test_fr190_scope_table.py 對 26 顆各建單一 scope session 逐支打全部主控台路由,放行集合等於對照表 |
| 主系統 BE | 守衛 test_fr190_seed_patterns_exclude_agent_module、test_legacy_capability_name_references、test_module_boundaries 綠;基線重產後 test_plugin_capabilities_are_seeded、test_seed_capabilities_are_all_accounted_for 綠 |
| 主系統 FE | 權限相關 5 檔 74 條綠;主控台與主站 vite build 過;改動檔無 border-left |
DEV 資料庫(migration 套用後):capabilities 中 module='agent' 26 顆、舊 7 顆 0;ui_routes 有 remote-agent-console(tree_page、非選單)綁 26 顆;role_capabilities、route_capabilities 無孤兒;原持 configure 等舊顆的角色已展開(例如 IT 執行人員持 21 顆、系統管理員 26 顆)。
主系統端點實打(HEAD 起在 8123):系統管理員簽票 200、票帶 26 個範圍;只持 agent.scan.download+detection.remote-agent.read 的角色對掃描報告過守門、對除錯報告 403。
出貨基線:188 基線庫套本案 migration 後重產三份產物;本機乾淨 postgres:16 跑完整 init.sh(198 表、271 條 RLS policy、cm_app 缺權限 0),agent 顆 26、舊顆 0、子頁綁 26、基線 marker 在、稽核員預帶 2、非系統角色萬用字元命中 agent.* 為 0。
未實機驗證:Agent 的新票登入 200、舊票 403 是用 Flask 測試 client 打的,沒另起 Agent 程序;掃描報告下載沒拿到 200(DEV 的 Agent 離線,過守門後連不上);角色頁子頁、帶入鈕、主控台側欄與橫幅沒有人在瀏覽器實機點過。
三件事還沒做,讀本頁時不要把它們當成已完成。
畫面手測。角色頁「Agent」組底下的子頁 26 顆與「比照主系統功能帶入」鈕、主控台側欄依權限顯示、舊版 Agent 的頂部橫幅,全部只有自動測試驗過,沒有人在瀏覽器實機點過。DEV 的 Agent 要重起到含本案的版本才看得到。由決策者接。
push 與上版。四個 repo 的 feature/FR-172 都有本案的 commit,尚未 push;STG 與 POC 都沒上。上版順序必須主系統先、Agent 後,因為新 Agent 不認舊的 7 個範圍名。由決策者接。
主線本來就失敗的一條守衛。test_fr132_capability_home_route 的 test_dev_every_capability_has_a_home 列出 13 顆 proj.* 專案角色能力點沒有 home_route,與本案無關,由 FR-194 處理。
本頁只寫交付了什麼與現在長什麼樣。為什麼這樣決定、過程中發生什麼、怎麼操作,分別在設計、歷史、手冊三類文件裡,照下表找。
| 你想知道 | 看哪份 |
|---|---|
為什麼拆 26 顆、為什麼模組是 agent、被排除了哪些做法 |
design.md §3、docs/analysis/2026-11-10-agent-console-capability-granularity.md |
| 討論過程、對照表怎麼來的 | discussion.md |
| 畫面長什麼樣(審稿用解釋版,非產品文案) | prototype/index.html |
| 使用者操作怎麼做 | 手冊 docs/user-manual/agent/13-standalone-operation.md「角色權限:26 項」 |
| 頁面行為規格 | SPEC evidence/agent-console.md §3、evidence/remote-agent-manage.md、system-admin/access-role-detail.md §4.2 |
| 每張卡做了什麼、commits、驗收結果 | handoff/2026-11-10-FR-190-SUMMARY.md、README.md |
| 拆卡怎麼排批 | handoff/cards.py |