# install_agent 智慧化重構 設計書(定稿+實作記錄) 20260801

> **[2026-09-10 後續修正 — 排程建立機制大改]**
> MAKE_TASKS 的 STK_Agent_Watchdog / STK_Agent_Boot / STK_HiInput 不再用 schtasks(/it /rp),
> 改用 PowerShell `Register-ScheduledTask -LogonType Interactive`(=> InteractiveToken =>
> agent 跑在使用者 Session 1、免密碼、提權跨使用者也建得起來)+ `-AllowStartIfOnBatteries`。
> 原因:(1) 加 /rp 會讓 LogonType 變 InteractiveTokenOrPassword、agent 落 Session 0(殭屍);
> (2) schtasks 預設 DisallowStartIfOnBatteries=true 會害電池/UPS 機排程永不觸發(Last Result 0x41303)。
> 同時移除 MODE_FULL 密碼段(691-692)的 /rp 排程重建。詳見 UpdateList.md 2026-09-10。

## 目標
選 [1] -> 輸站號 -> 全自動(清舊+判斷+布局+啟動);工作站/RDS主機/遷移三場景共用一套邏輯。

## 核心地基:RDS_MODE 單一 KEY 驅動一切
- 使用者只改該席位 stk.ini 的 RDS_MODE 一個 KEY -> install_agent 自動組 STK_PATH、自動選排程布局;
  STK32/Agent 讀同一 KEY 自切 LOC_*(RdsRemapLoc/ReadLocSys,已完成)。
- 無雞生蛋:install_agent 讀「固定 UNC」%SRC_ROOT%\%STATION_NAME%\stk.ini;
  RDS 主機上 C:\STK\S00X 與 \\S001\stk\S00X 是同一份實體,兩模式皆讀得到同一份。

## 三決策定稿
1. STK_PATH 保留 Enter 確認畫面(預設值依 RDS_MODE 自動變:=1 -> C:\STK\S00X / =0 -> UNC)。
2. 架構 B(映射啟動器):席位排程不綁人。
   - 一個通用群組排程 STK_RDS_Launcher(XML 匯入,主體 BUILTIN\Users SID S-1-5-32-545,
     任何使用者登入觸發+每 1 分鐘重複,以登入者身分在其 session 執行;
     schtasks 純參數表達不了群組主體,故 XML)。
   - C:\STK\rds_usermap.ini([Users] 帳號=席位;主機本機讀=登入不依賴網路,W:\ 可遠端改)。
   - C:\STK\rds_launcher.bat/.vbs:查 %USERNAME% -> 席位 -> 起 C:\StkAgent\<席位>\watchdog_user.bat。
   - ★離職換人:只改 usermap 一行,零重裝零孤兒(排程/檔案全不動)。
   - 混合部署安全:舊固定名排程「只有 Run As User=自己」才刪(別席位還跑 =0 不誤傷)。
3. /retire 靜默參數:原工作站退役(kill+刪排程+HKCU Run;檔案保留),Commander 可遠端觸發;
   防呆:偵測到 C:\STK\rds_launcher.vbs(=RDS 主機)即 ABORT。

## 實作內容(E1-E9,基底=.bak_persession_20260730_0930 乾淨版,TN_SFX 方案全面由架構 B 取代)
- E1 /retire 參數派發
- E2 MODE_FULL 站號後讀 RDS_MODE(findstr /B /I 固定 UNC)
- E3 DEF_PATH 依 RDS_MODE 自動組(保留 Enter 確認)
- E4 MODE_FULL kill 段 per-session 化(=1 PowerShell SessionId 只殺自己 session;=0 原樣 kill 全機)
- E5 :SELFHEAL 重讀 RDS_MODE(selfheal/migrate 路徑沒過 MODE_FULL)
- E6 :SELFHEAL 排程分流(=0 原固定名排程一字不改;=1 :SH_RDS_TASKS = usermap 登記+
     自有舊排程升級清理+產 launcher bat/vbs+產 XML+schtasks /create /xml+立即啟動)
- E7 =1 跳過 autologon 設定(本機席位無 UNC 斷線疑慮)
- E8 :MODE_RETIRE(含 RDS 主機防呆)
- E9 uninstall_agent.bat 補刪 STK_Agent_Boot(原漏)

## Feature Flag 保證
RDS_MODE=0:E2/E3 提示不同外,路徑預設/kill/排程/autologon 全部走原路=現況 100% 不變。

## 已知事項
- MODE_UPDATE(/silent)的 kill 仍全機(該路徑無站號情境;RDS 主機上靜默更新會殺全席位 Agent,
  排程 1 分鐘內自動拉回;後續可再 per-session 化)。
- 群組排程 RunLevel=LeastPrivilege(群組主體無法 HIGHEST);Agent 席位功能(截圖/relay)不需管理權限。
- 舊 TN_SFX 版(20260730)已部署到測試環境 master;本版上傳後覆蓋。

## 踩坑(本次)
- kill block 檔內出現 2 次(MODE_FULL/MODE_UPDATE),anchor 不可假設唯一,只改第一處。
- bat `if cond cmd1 & cmd2` 無括號時 cmd2 無條件執行 -> /retire 防呆行必須加括號。
- 插入 bat 內容一律純 ASCII(encode('ascii') 當安全網)+全檔 BIG5 trail 0x5C/0x7C 掃描前後比對。


---

# 附錄 v3(20260801 增補定案+實作):自我維護 + 零接觸模式切換

## ① 用法說明畫面
選單顯示 RDS_MODE 用法摘要(=0 網路端/=1 RDS主機端/切換=只改 KEY 下次登入自動重佈局)。

## ② 單一入口分派
永遠只跑 INSTALL_AGENT.BAT;輸站號後若存在 install_agent_S00X.bat(站台客製)自動改跑它,
否則走本體。(目前無任何客製檔=一律本體;為未來留鉤子)

## ③ ★改 KEY 自動重置(零接觸模式切換)
- 暗記 = C:\StkAgent\<席位>\.applied_mode(install_agent 佈局完成時寫入 0/1)。
- watchdog_user.bat 每次執行(登入時+每分鐘)比對「席位 stk.ini 現值 vs 暗記」:
  相同->正常看門;現值=1 且 STK_PATH 是網路(工作站)->★自動退役(席位已搬去 RDS,
  雙 Agent 衝突自動消失);其他不同->自動 install_agent /selfheal 靜默重佈局。
- 升級相容:無暗記第一次視同現狀已符只補寫,不突然重佈局。
- 效果:管理員只改 stk.ini RDS_MODE 一個 KEY,主機端下次登入自動建新布局、
  原工作站自動退役,全程零手動。

## ④ 修正:selfheal 讀 ini fallback + 席位 hint
- :SELFHEAL 讀 %STK_PATH%\stk.ini 讀不到(agent 家目錄無 stk.ini)fallback C:\STK\<席位>\stk.ini。
- MODE_SELFHEAL 支援 IA_SEAT_HINT 環境變數(watchdog reconcile 指定席位目錄,免瞎找)。

## ⑤ rds_usermap 自我維護(首次登入認領席位)
- launcher:map 有我->直接起席位;=NONE->靜默跳過(admin 類帳號);沒有我->跳一次性
  認領視窗(rds_claim.bat,顯示現有 map):輸席位=認領(★自動刪除該席位原佔用者行=
  一席一人,離職交接新人自助完成)/Enter=記 NONE 不再問;lock 防每分鐘重複跳窗;
  認領不存在的席位會 FAIL 提示。
- 人事異動連管理員改 map 都免了(仍可手動改,W:\rds_usermap.ini 任何站台可編)。

## ⑥ ★watchdog session 域化(RDS 多席位不互殺)
watchdog_user.bat 原 tasklist/taskkill 全機掃描,多席位主機會互相誤判(CNT>1 永遠
不健康->每分鐘 kill 全機重啟)。修:.applied_mode=1 時取自己 SessionId,
全部 tasklist/taskkill 加 /FI "SESSION eq N"(tasklist x7 + taskkill x3);
=0 時 TFILT 空=行為原樣(Feature Flag)。

## 驗證(v3)
- emit 段抽出本機真實執行:launcher 17 行逐字相同、claim 30 行關鍵行全對(跳脫層實證)。
- 結構 label/goto 成對;插入內容純 ASCII;BIG5 危險 trail 未增加。

## 踩坑(v3)
- anchor `if "%STK_PATH%"=="" call :FIND_STK_PATH` 檔內 2 次,改用前一行唯一 anchor。
- emit 跳脫系統化:^& ^| ^< ^> %%,由 python 產生器統一處理,再以真實執行比對驗證。
