feat(role): 新增閒置小睡整理
This commit is contained in:
+48
-3
@@ -4,7 +4,7 @@
|
||||
// inbox,(2) 產生 SessionStart 要注入的記憶區塊,(3) 睡眠整理時輸出待整理
|
||||
// 素材並套用整理結果(NREM 鞏固/REM 整合、分類、去重、標籤、總結、
|
||||
// 優先度、關聯、壓縮歸檔),(4) 依使用頻率與優先度遺忘日常與其他類記憶。
|
||||
// 更新時間:2026/07/28 13:50:43
|
||||
// 更新時間:2026/07/28 14:36:00
|
||||
// 相依:Node.js 標準庫。
|
||||
// 退出碼:0 成功;1 無內容可處理;2 參數錯誤。呼叫端(hook)一律不得因此中斷。
|
||||
// ==============================================================================
|
||||
@@ -537,7 +537,13 @@ function cmdLoad(args) {
|
||||
if (digest) blocks.push(`> 上次睡眠摘要:${digest}`);
|
||||
|
||||
const pending = listInbox(args.role).length;
|
||||
if (pending) blocks.push(`> 尚有 ${pending} 則未整理記憶,將於下次睡眠時段歸檔。`);
|
||||
if (pending) {
|
||||
if (pending >= args.batch) {
|
||||
blocks.push(`> 尚有 ${pending} 則未整理記憶,已達一批睡眠整理量;請以角色語氣主動提醒使用者「想睡覺」或需要整理記憶。這是建議整理/歸檔的提醒,不代表停止協助。`);
|
||||
} else {
|
||||
blocks.push(`> 尚有 ${pending} 則未整理記憶,將於下次睡眠時段歸檔。`);
|
||||
}
|
||||
}
|
||||
if (!blocks.length) return 1;
|
||||
|
||||
let text = blocks.join("\n\n");
|
||||
@@ -776,6 +782,7 @@ function cmdStats(args) {
|
||||
rows.push("");
|
||||
rows.push(`- 記憶目錄:${memoryRoot(args.role)}`);
|
||||
rows.push(`- 個人記憶同意狀態:${consentStatusValue(args.role)}`);
|
||||
rows.push(`- 上次互動:${state.last_activity || "尚未記錄"}`);
|
||||
rows.push(`- 上次睡眠整理:${state.last_sleep || "尚未整理"}`);
|
||||
rows.push(`- 上次睡眠摘要:${state.last_sleep_digest || "尚無"}`);
|
||||
rows.push(`- 上次遺忘:${state.last_forget || "尚未執行"}`);
|
||||
@@ -790,6 +797,14 @@ function cmdMarkSleep(args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function cmdMarkActivity(args) {
|
||||
const patch = { last_activity: nowStamp(), last_activity_epoch: nowEpoch() };
|
||||
if (args.project) patch.last_activity_project = args.project;
|
||||
writeState(args.role, patch);
|
||||
process.stdout.write("已更新上次互動時間");
|
||||
return 0;
|
||||
}
|
||||
|
||||
function cmdNeedSleep(args) {
|
||||
if (!listInbox(args.role).length) {
|
||||
process.stdout.write("no");
|
||||
@@ -804,6 +819,30 @@ function cmdNeedSleep(args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function cmdNeedNap(args) {
|
||||
const pending = listInbox(args.role).length;
|
||||
if (pending < args.minInbox) {
|
||||
process.stdout.write("no");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const state = readState(args.role);
|
||||
const lastEpoch = Number.parseInt(state.last_activity_epoch || "", 10);
|
||||
let lastMs = Number.isFinite(lastEpoch) && lastEpoch > 0 ? lastEpoch * 1000 : null;
|
||||
if (lastMs === null) {
|
||||
const last = parseStamp(state.last_activity);
|
||||
lastMs = last ? last.getTime() : null;
|
||||
}
|
||||
if (lastMs === null) {
|
||||
process.stdout.write("no");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const idleMinutes = (Date.now() - lastMs) / 60000;
|
||||
process.stdout.write(idleMinutes >= args.idleMinutes ? "yes" : "no");
|
||||
return 0;
|
||||
}
|
||||
|
||||
function cmdConsent(args) {
|
||||
const value = String(args.value || "").trim().toLowerCase();
|
||||
const normalized = {
|
||||
@@ -892,7 +931,7 @@ function requireRole(args) {
|
||||
function main(argv) {
|
||||
const args = parseArgs(argv);
|
||||
if (!args.command || args.command === "-h" || args.command === "--help") {
|
||||
process.stdout.write(`用法:memory.js <子命令> [參數]\n\n子命令:write、seed、load、collect、apply、forget、stats、mark-sleep、need-sleep、consent、consent-status\n`);
|
||||
process.stdout.write(`用法:memory.js <子命令> [參數]\n\n子命令:write、seed、load、collect、apply、forget、stats、mark-sleep、mark-activity、need-sleep、need-nap、consent、consent-status\n`);
|
||||
return 0;
|
||||
}
|
||||
if (!requireRole(args)) return 2;
|
||||
@@ -903,6 +942,8 @@ function main(argv) {
|
||||
args.fullMinPriority = Number.parseInt(args.fullMinPriority || "", 10);
|
||||
args.digestMinPriority = Number.parseInt(args.digestMinPriority || "", 10);
|
||||
args.hours = Number.parseFloat(args.hours || "");
|
||||
args.idleMinutes = Number.parseFloat(args.idleMinutes || "");
|
||||
args.minInbox = Number.parseInt(args.minInbox || "", 10);
|
||||
|
||||
if (!Number.isFinite(args.limit)) args.limit = args.command === "load" ? envInt("ROLE_LOAD_LIMIT", DEFAULT_LOAD_LIMIT) : envInt("ROLE_SLEEP_COLLECT_LIMIT", COLLECT_LIMIT);
|
||||
if (!Number.isFinite(args.batch)) args.batch = envInt("ROLE_SLEEP_BATCH", SLEEP_BATCH);
|
||||
@@ -910,6 +951,8 @@ function main(argv) {
|
||||
if (!Number.isFinite(args.fullMinPriority)) args.fullMinPriority = envInt("ROLE_LOAD_FULL_MIN_PRIORITY", DEFAULT_FULL_MIN_PRIORITY);
|
||||
if (!Number.isFinite(args.digestMinPriority)) args.digestMinPriority = envInt("ROLE_LOAD_DIGEST_MIN_PRIORITY", DEFAULT_DIGEST_MIN_PRIORITY);
|
||||
if (!Number.isFinite(args.hours)) args.hours = 20.0;
|
||||
if (!Number.isFinite(args.idleMinutes)) args.idleMinutes = 45.0;
|
||||
if (!Number.isFinite(args.minInbox)) args.minInbox = 3;
|
||||
args.category ||= "important";
|
||||
args.tags ||= "";
|
||||
args.source ||= "";
|
||||
@@ -929,7 +972,9 @@ function main(argv) {
|
||||
forget: cmdForget,
|
||||
stats: cmdStats,
|
||||
"mark-sleep": cmdMarkSleep,
|
||||
"mark-activity": cmdMarkActivity,
|
||||
"need-sleep": cmdNeedSleep,
|
||||
"need-nap": cmdNeedNap,
|
||||
consent: cmdConsent,
|
||||
"consent-status": cmdConsentStatus,
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# 值得記錄時才呼叫 headless CLI 輕量濃縮成一則 inbox 記憶(粗分類/總結/
|
||||
# 標籤/優先度/關聯/要點)→ 機密遮蔽 → 寫入 .memory/<角色>/inbox/,
|
||||
# 等待睡眠時段做完整 NREM/REM 整理。睡眠時段雖不載入角色,對話仍照常記錄。
|
||||
# 更新時間:2026/07/28 13:50:43
|
||||
# 更新時間:2026/07/28 14:36:00
|
||||
# 相依:bash、node、任一 headless CLI、同目錄的 role_lib.sh/memory.js/transcript.js。
|
||||
# 機密:濃縮提示詞明令不得輸出憑證與個資,寫檔前再以 transcript.js redact 遮蔽一次。
|
||||
# 退出碼:一律 0 —— hook 絕不可阻斷使用者流程。
|
||||
@@ -23,9 +23,6 @@ ROLE="$(role_resolve_name)"
|
||||
[ -n "$ROLE" ] || role_quit "未指定角色,略過記憶記錄"
|
||||
[ -f "$(role_file "$ROLE")" ] || role_quit "找不到角色定義檔,略過記憶記錄" "WRN"
|
||||
|
||||
CLI="$(role_select_cli)" || exit 0
|
||||
[ -n "$CLI" ] || exit 0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 讀取 hook 傳入的 JSON(session_id/transcript_path/cwd/stop_hook_active)
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -52,6 +49,8 @@ EOF_HOOK
|
||||
|
||||
[ "$STOP_ACTIVE" = "1" ] && role_quit "stop_hook_active 為 true,避免迴圈不重複記錄"
|
||||
role_in_scope "$HOOK_CWD" || role_quit "cwd 不在 ROLE_SCOPE 範圍內:${HOOK_CWD}"
|
||||
PROJECT="$(role_project_name "$HOOK_CWD")"
|
||||
node "${SCRIPT_DIR}/memory.js" mark-activity --role "$ROLE" --project "$PROJECT" >/dev/null 2>&1 || true
|
||||
|
||||
if [ ! -f "$TRANSCRIPT_PATH" ] && [ -n "${CODEX_THREAD_ID:-}" ]; then
|
||||
TRANSCRIPT_PATH="$(find "${HOME}/.codex/sessions" -type f -name "*${CODEX_THREAD_ID}.jsonl" -print -quit 2>/dev/null)"
|
||||
@@ -73,7 +72,6 @@ if printf '%s' "$USER_TURN" | grep -qiE '個人資料|個資|偏好|記憶|記
|
||||
fi
|
||||
fi
|
||||
|
||||
PROJECT="$(role_project_name "$HOOK_CWD")"
|
||||
CAPTURE_MIN_CHARS="${ROLE_CAPTURE_MIN_CHARS:-240}"
|
||||
CAPTURE_TIMEOUT="${ROLE_CAPTURE_TIMEOUT:-25}"
|
||||
|
||||
@@ -85,6 +83,9 @@ if [ "${#TURN}" -lt "$CAPTURE_MIN_CHARS" ] && ! printf '%s' "$TURN" | grep -qiE
|
||||
role_quit "本輪低於記憶長度門檻且無明確記憶線索,略過記錄"
|
||||
fi
|
||||
|
||||
CLI="$(role_select_cli)" || exit 0
|
||||
[ -n "$CLI" ] || exit 0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 濃縮:產出一則輕量 inbox 記憶,交由 memory.js 落檔;完整整理留到睡眠週期
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
+100
-11
@@ -4,9 +4,10 @@
|
||||
# 先檢查是否有 AI 正在運行,沒有才進入睡眠並整理記憶:
|
||||
# NREM 鞏固(分類/去噪/去重/合併/優先度)→ REM 整合(跨記憶
|
||||
# 連結/抽象化/提取線索)→ 壓縮歸檔 → 日常與其他依使用頻率與優先度遺忘。
|
||||
# 另提供 --catchup(cron 未執行時的補跑)、--force(手動立即整理)、
|
||||
# --install-cron/--remove-cron(排程安裝與移除)、--status(狀態)。
|
||||
# 更新時間:2026/07/28 12:59:41
|
||||
# 另提供 --nap(CLI 閒置時的小睡整理)、--catchup(cron 未執行時的補跑)、
|
||||
# --force(手動立即整理)、--install-cron/--remove-cron(排程安裝與移除)、
|
||||
# --status(狀態)。
|
||||
# 更新時間:2026/07/28 14:36:00
|
||||
# 相依:bash、node、任一 headless CLI、crontab(僅排程安裝需要)、
|
||||
# 同目錄的 role_lib.sh 與 memory.js。
|
||||
# 退出碼:0 成功或無事可做;1 參數錯誤或整理失敗(cron 觸發時不影響使用者)。
|
||||
@@ -18,6 +19,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
. "${SCRIPT_DIR}/role_lib.sh"
|
||||
|
||||
CRON_MARKER="# jsc-role-sleep"
|
||||
NAP_CRON_MARKER="# jsc-role-nap"
|
||||
SLEEP_TIMEOUT="${ROLE_SLEEP_TIMEOUT:-180}"
|
||||
SLEEP_OUTPUT_LIMIT="${ROLE_SLEEP_OUTPUT_LIMIT:-8000}"
|
||||
|
||||
@@ -27,6 +29,7 @@ usage() {
|
||||
用法:role_sleep.sh <模式>
|
||||
|
||||
--run cron 觸發:在睡眠時段內且無 AI 運行時整理記憶
|
||||
--nap 小睡觸發:CLI 閒置一段時間且 inbox 達門檻時整理記憶
|
||||
--catchup 補跑:cron 未執行時,由 SessionStart hook 於背景呼叫
|
||||
--force 立即整理一次(忽略時段與 AI 運行檢查)
|
||||
--install-cron 安裝/更新睡眠排程(每小時檢查一次)
|
||||
@@ -42,6 +45,50 @@ require_role() {
|
||||
[ -f "$(role_file "$ROLE")" ] || { role_log "WRN" "找不到角色定義檔:$(role_file "$ROLE")"; exit 1; }
|
||||
}
|
||||
|
||||
positive_int_or_default() {
|
||||
# 讀取正整數環境變數;未設定或不合法時使用預設值
|
||||
local value="$1" fallback="$2" min="${3:-1}" max="${4:-}"
|
||||
case "$value" in
|
||||
""|*[!0-9]*) printf '%s' "$fallback"; return 0 ;;
|
||||
esac
|
||||
[ "$value" -lt "$min" ] && { printf '%s' "$fallback"; return 0; }
|
||||
if [ -n "$max" ] && [ "$value" -gt "$max" ]; then
|
||||
printf '%s' "$fallback"
|
||||
return 0
|
||||
fi
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
nap_enabled() {
|
||||
# 小睡預設啟用;設 ROLE_NAP_ENABLED=0/false/no 可關閉
|
||||
case "${ROLE_NAP_ENABLED:-1}" in
|
||||
0|false|no) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
nap_idle_minutes() { positive_int_or_default "${ROLE_NAP_IDLE_MINUTES:-}" 45 1; }
|
||||
nap_min_inbox() { positive_int_or_default "${ROLE_NAP_MIN_INBOX:-}" 3 1; }
|
||||
nap_interval_minutes() { positive_int_or_default "${ROLE_NAP_INTERVAL_MINUTES:-}" 10 1 59; }
|
||||
|
||||
role_sleep_child_running() {
|
||||
# 小睡只避開整理用的 headless 子 CLI;互動式 CLI 閒置時仍可小睡
|
||||
local pid cmd self="$$"
|
||||
for pid in $(pgrep -f '(^|/)(claude|codex|agy|opencode|copilot)([[:space:]]|$)' 2>/dev/null); do
|
||||
if [ "$pid" = "$self" ] || [ "$pid" = "$PPID" ]; then
|
||||
continue
|
||||
fi
|
||||
cmd="$(ps -o args= -p "$pid" 2>/dev/null)"
|
||||
case "$cmd" in
|
||||
*role_sleep.sh*|*role_capture.sh*|*role_load.sh*|*pgrep*) continue ;;
|
||||
esac
|
||||
if printf '%s' "$cmd" | grep -Eq '(^|/)(codex[[:space:]]+exec|claude([[:space:]].*)?[[:space:]]+-p|agy([[:space:]].*)?[[:space:]]+-p|opencode[[:space:]]+run|copilot([[:space:]].*)?[[:space:]]+-p)'; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 整理主流程
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -170,20 +217,32 @@ cron_path() {
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
cron_line() {
|
||||
# 組出 crontab 條目:睡眠時段內每小時檢查一次
|
||||
cron_env_prefix() {
|
||||
# 組出 cron 需要的精簡環境變數
|
||||
local env_prefix="PATH=$(cron_quote "$(cron_path)")"
|
||||
local var
|
||||
for var in ROLE_ENABLED ROLE_NAME ROLE_HOME ROLE_MEMORY_HOME ROLE_CLI ROLE_MODEL ROLE_SLEEP_START ROLE_SLEEP_END ROLE_SCOPE; do
|
||||
for var in ROLE_ENABLED ROLE_NAME ROLE_HOME ROLE_MEMORY_HOME ROLE_CLI ROLE_MODEL ROLE_SLEEP_START ROLE_SLEEP_END ROLE_SCOPE ROLE_NAP_ENABLED ROLE_NAP_IDLE_MINUTES ROLE_NAP_MIN_INBOX ROLE_NAP_INTERVAL_MINUTES; do
|
||||
if [ -n "${!var:-}" ]; then
|
||||
env_prefix="${env_prefix} ${var}=$(cron_quote "${!var}")"
|
||||
fi
|
||||
done
|
||||
printf '%s' "$env_prefix"
|
||||
}
|
||||
|
||||
cron_line() {
|
||||
# 組出 crontab 條目:睡眠時段內每小時檢查一次
|
||||
printf '0 %s * * * %s %s --run >> %s 2>&1 %s\n' \
|
||||
"$(cron_hours)" "$env_prefix" "$(cron_quote "${SCRIPT_DIR}/role_sleep.sh")" \
|
||||
"$(cron_hours)" "$(cron_env_prefix)" "$(cron_quote "${SCRIPT_DIR}/role_sleep.sh")" \
|
||||
"$(cron_quote "$(sleep_log_path)")" "$CRON_MARKER"
|
||||
}
|
||||
|
||||
nap_cron_line() {
|
||||
# 組出小睡 crontab 條目:全天依間隔檢查閒置狀態
|
||||
printf '*/%s * * * * %s %s --nap >> %s 2>&1 %s\n' \
|
||||
"$(nap_interval_minutes)" "$(cron_env_prefix)" "$(cron_quote "${SCRIPT_DIR}/role_sleep.sh")" \
|
||||
"$(cron_quote "$(sleep_log_path)")" "$NAP_CRON_MARKER"
|
||||
}
|
||||
|
||||
cron_hours() {
|
||||
# 依睡眠時段換算 cron 小時欄位(每小時檢查一次,讓 AI 運行中的情況能在下個小時重試)
|
||||
local start end hour hours=""
|
||||
@@ -209,10 +268,19 @@ install_cron() {
|
||||
command -v crontab >/dev/null 2>&1 || { role_log "ERR" "找不到 crontab,無法安裝排程"; return 1; }
|
||||
mkdir -p "$(role_home)" 2>/dev/null
|
||||
local current new
|
||||
current="$(crontab -l 2>/dev/null | grep -v -F "$CRON_MARKER")"
|
||||
new="$(printf '%s\n%s' "$current" "$(cron_line)" | sed '/^$/d')"
|
||||
current="$(crontab -l 2>/dev/null | grep -v -F "$CRON_MARKER" | grep -v -F "$NAP_CRON_MARKER")"
|
||||
if nap_enabled; then
|
||||
new="$(printf '%s\n%s\n%s' "$current" "$(cron_line)" "$(nap_cron_line)" | sed '/^$/d')"
|
||||
else
|
||||
new="$(printf '%s\n%s' "$current" "$(cron_line)" | sed '/^$/d')"
|
||||
fi
|
||||
printf '%s\n' "$new" | crontab - || { role_log "ERR" "寫入 crontab 失敗"; return 1; }
|
||||
role_log "INF" "已安裝睡眠排程:每日 $(cron_hours) 時整點檢查(角色 ${ROLE},時段 $(role_sleep_start)–$(role_sleep_end))"
|
||||
if nap_enabled; then
|
||||
role_log "INF" "已安裝小睡排程:每 $(nap_interval_minutes) 分鐘檢查,閒置滿 $(nap_idle_minutes) 分鐘且 inbox ≥ $(nap_min_inbox) 則時整理"
|
||||
else
|
||||
role_log "INF" "小睡排程已停用(ROLE_NAP_ENABLED=${ROLE_NAP_ENABLED:-1})"
|
||||
fi
|
||||
role_log "INF" "排程輸出:$(sleep_log_path)"
|
||||
if ! pgrep -x cron >/dev/null 2>&1 && ! pgrep -x crond >/dev/null 2>&1; then
|
||||
role_log "WRN" "系統 cron 服務未執行(WSL 常見),排程不會觸發;SessionStart 的背景補跑仍會運作"
|
||||
@@ -223,15 +291,16 @@ install_cron() {
|
||||
remove_cron() {
|
||||
# 移除本 skill 安裝的排程條目
|
||||
command -v crontab >/dev/null 2>&1 || { role_log "ERR" "找不到 crontab"; return 1; }
|
||||
crontab -l 2>/dev/null | grep -v -F "$CRON_MARKER" | crontab -
|
||||
crontab -l 2>/dev/null | grep -v -F "$CRON_MARKER" | grep -v -F "$NAP_CRON_MARKER" | crontab -
|
||||
role_log "INF" "已移除睡眠排程"
|
||||
return 0
|
||||
}
|
||||
|
||||
show_status() {
|
||||
# 以表格輸出目前角色與記憶狀態(供 skill 的 --status 使用)
|
||||
local cron_state="未安裝" cron_service="未執行" window="否"
|
||||
local cron_state="未安裝" nap_state="未安裝" cron_service="未執行" window="否"
|
||||
crontab -l 2>/dev/null | grep -qF "$CRON_MARKER" && cron_state="已安裝"
|
||||
crontab -l 2>/dev/null | grep -qF "$NAP_CRON_MARKER" && nap_state="已安裝"
|
||||
{ pgrep -x cron >/dev/null 2>&1 || pgrep -x crond >/dev/null 2>&1; } && cron_service="執行中"
|
||||
role_in_sleep_window && window="是"
|
||||
printf '| 項目 | 值 |\n| --- | --- |\n'
|
||||
@@ -240,6 +309,9 @@ show_status() {
|
||||
printf '| 睡眠時段 | %s–%s |\n' "$(role_sleep_start)" "$(role_sleep_end)"
|
||||
printf '| 目前是否睡眠中 | %s |\n' "$window"
|
||||
printf '| cron 排程 | %s |\n' "$cron_state"
|
||||
printf '| 小睡排程 | %s |\n' "$nap_state"
|
||||
printf '| 小睡啟用 | %s |\n' "$(nap_enabled && printf '是' || printf '否')"
|
||||
printf '| 小睡條件 | 閒置 ≥ %s 分鐘,待整理 ≥ %s 則,每 %s 分鐘檢查 |\n' "$(nap_idle_minutes)" "$(nap_min_inbox)" "$(nap_interval_minutes)"
|
||||
printf '| cron 服務 | %s |\n' "$cron_service"
|
||||
printf '| 摘要 CLI | %s |\n' "$(role_select_cli 2>/dev/null || printf '找不到可用 CLI')"
|
||||
printf '\n'
|
||||
@@ -266,6 +338,23 @@ case "$MODE" in
|
||||
fi
|
||||
sleep_cycle "cron"
|
||||
;;
|
||||
--nap)
|
||||
role_enabled || exit 0
|
||||
require_role
|
||||
if ! nap_enabled; then
|
||||
role_log "DBG" "小睡已停用(ROLE_NAP_ENABLED=${ROLE_NAP_ENABLED:-1}),略過"
|
||||
exit 0
|
||||
fi
|
||||
if role_sleep_child_running; then
|
||||
role_log "INF" "偵測到整理用 headless CLI 正在運行,本次小睡略過"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$(node "${SCRIPT_DIR}/memory.js" need-nap --role "$ROLE" --idle-minutes "$(nap_idle_minutes)" --min-inbox "$(nap_min_inbox)" 2>/dev/null)" != "yes" ]; then
|
||||
role_log "DBG" "尚未達小睡條件(閒置滿 $(nap_idle_minutes) 分鐘且 inbox ≥ $(nap_min_inbox) 則),略過"
|
||||
exit 0
|
||||
fi
|
||||
sleep_cycle "小睡"
|
||||
;;
|
||||
--catchup)
|
||||
role_enabled || exit 0
|
||||
require_role
|
||||
|
||||
Reference in New Issue
Block a user