@mison/wecom-cleaner 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -35
- package/docs/IMPLEMENTATION_PLAN.md +7 -7
- package/docs/NON_INTERACTIVE_SPEC.md +239 -0
- package/docs/releases/v1.1.0.md +36 -0
- package/docs/releases/v1.2.0.md +33 -0
- package/native/bin/darwin-arm64/wecom-cleaner-core +0 -0
- package/native/bin/darwin-x64/wecom-cleaner-core +0 -0
- package/native/manifest.json +5 -4
- package/native/zig/src/main.zig +4 -3
- package/package.json +9 -3
- package/skills/wecom-cleaner-agent/SKILL.md +72 -0
- package/skills/wecom-cleaner-agent/agents/openai.yaml +5 -0
- package/skills/wecom-cleaner-agent/references/commands.md +100 -0
- package/skills/wecom-cleaner-agent/scripts/analysis_report.sh +275 -0
- package/skills/wecom-cleaner-agent/scripts/cleanup_monthly_report.sh +450 -0
- package/skills/wecom-cleaner-agent/scripts/doctor_report.sh +167 -0
- package/skills/wecom-cleaner-agent/scripts/recycle_maintain_report.sh +281 -0
- package/skills/wecom-cleaner-agent/scripts/restore_batch_report.sh +349 -0
- package/skills/wecom-cleaner-agent/scripts/space_governance_report.sh +401 -0
- package/src/cleanup.js +369 -0
- package/src/cli.js +2228 -172
- package/src/config.js +256 -0
- package/src/doctor.js +1 -1
- package/src/lock.js +50 -22
- package/src/native-bridge.js +16 -2
- package/src/recycle-maintenance.js +42 -1
- package/src/restore.js +196 -0
- package/src/scanner.js +69 -10
- package/src/skill-cli.js +97 -0
- package/src/skill-installer.js +76 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
cat <<'EOF'
|
|
6
|
+
用法:
|
|
7
|
+
space_governance_report.sh [--accounts all|current|id1,id2] [--tiers safe,caution,protected]
|
|
8
|
+
[--suggested-only true|false] [--allow-recent-active true|false]
|
|
9
|
+
[--targets id1,id2] [--execute true|false]
|
|
10
|
+
[--root <path>] [--state-root <path>]
|
|
11
|
+
[--external-roots-source preset|configured|auto|all]
|
|
12
|
+
|
|
13
|
+
说明:
|
|
14
|
+
- 默认只做预演(--execute false)。
|
|
15
|
+
- --execute true 且预演命中>0 时,才执行真实治理。
|
|
16
|
+
EOF
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
20
|
+
echo "错误:缺少 jq,请先安装(brew install jq)。" >&2
|
|
21
|
+
exit 2
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if ! command -v wecom-cleaner >/dev/null 2>&1; then
|
|
25
|
+
echo "错误:未找到 wecom-cleaner 命令,请先安装 @mison/wecom-cleaner。" >&2
|
|
26
|
+
exit 2
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
ACCOUNTS="all"
|
|
30
|
+
TIERS="safe,caution"
|
|
31
|
+
SUGGESTED_ONLY="true"
|
|
32
|
+
ALLOW_RECENT_ACTIVE="false"
|
|
33
|
+
TARGETS=""
|
|
34
|
+
EXECUTE="false"
|
|
35
|
+
ROOT=""
|
|
36
|
+
STATE_ROOT=""
|
|
37
|
+
EXTERNAL_ROOTS_SOURCE="all"
|
|
38
|
+
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
--accounts)
|
|
42
|
+
ACCOUNTS="${2:-all}"
|
|
43
|
+
shift 2
|
|
44
|
+
;;
|
|
45
|
+
--tiers)
|
|
46
|
+
TIERS="${2:-safe,caution}"
|
|
47
|
+
shift 2
|
|
48
|
+
;;
|
|
49
|
+
--suggested-only)
|
|
50
|
+
SUGGESTED_ONLY="${2:-true}"
|
|
51
|
+
shift 2
|
|
52
|
+
;;
|
|
53
|
+
--allow-recent-active)
|
|
54
|
+
ALLOW_RECENT_ACTIVE="${2:-false}"
|
|
55
|
+
shift 2
|
|
56
|
+
;;
|
|
57
|
+
--targets)
|
|
58
|
+
TARGETS="${2:-}"
|
|
59
|
+
shift 2
|
|
60
|
+
;;
|
|
61
|
+
--execute)
|
|
62
|
+
EXECUTE="${2:-false}"
|
|
63
|
+
shift 2
|
|
64
|
+
;;
|
|
65
|
+
--root)
|
|
66
|
+
ROOT="${2:-}"
|
|
67
|
+
shift 2
|
|
68
|
+
;;
|
|
69
|
+
--state-root)
|
|
70
|
+
STATE_ROOT="${2:-}"
|
|
71
|
+
shift 2
|
|
72
|
+
;;
|
|
73
|
+
--external-roots-source)
|
|
74
|
+
EXTERNAL_ROOTS_SOURCE="${2:-all}"
|
|
75
|
+
shift 2
|
|
76
|
+
;;
|
|
77
|
+
-h | --help)
|
|
78
|
+
usage
|
|
79
|
+
exit 0
|
|
80
|
+
;;
|
|
81
|
+
*)
|
|
82
|
+
echo "错误:未知参数 $1" >&2
|
|
83
|
+
usage
|
|
84
|
+
exit 2
|
|
85
|
+
;;
|
|
86
|
+
esac
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
case "$EXECUTE" in
|
|
90
|
+
true | false) ;;
|
|
91
|
+
*)
|
|
92
|
+
echo "错误:--execute 只能是 true 或 false" >&2
|
|
93
|
+
exit 2
|
|
94
|
+
;;
|
|
95
|
+
esac
|
|
96
|
+
|
|
97
|
+
human_bytes() {
|
|
98
|
+
local bytes="${1:-0}"
|
|
99
|
+
awk -v b="$bytes" '
|
|
100
|
+
BEGIN {
|
|
101
|
+
split("B KB MB GB TB", u, " ");
|
|
102
|
+
i=1;
|
|
103
|
+
while (b>=1024 && i<5) { b=b/1024; i++; }
|
|
104
|
+
if (i==1) printf "%d %s", b, u[i];
|
|
105
|
+
else printf "%.2f %s", b, u[i];
|
|
106
|
+
}
|
|
107
|
+
'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
short_path() {
|
|
111
|
+
local raw="${1:-}"
|
|
112
|
+
local max_len="${2:-90}"
|
|
113
|
+
if [[ "${#raw}" -le "$max_len" ]]; then
|
|
114
|
+
printf '%s' "$raw"
|
|
115
|
+
return
|
|
116
|
+
fi
|
|
117
|
+
local keep=$((max_len - 3))
|
|
118
|
+
printf '...%s' "${raw: -$keep}"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
tier_label() {
|
|
122
|
+
local tier="${1:-}"
|
|
123
|
+
case "$tier" in
|
|
124
|
+
safe) printf '%s' '安全层' ;;
|
|
125
|
+
caution) printf '%s' '谨慎层' ;;
|
|
126
|
+
protected) printf '%s' '保护层' ;;
|
|
127
|
+
*) printf '%s' "$tier" ;;
|
|
128
|
+
esac
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
account_scope_label="$ACCOUNTS"
|
|
132
|
+
if [[ "$ACCOUNTS" == "all" ]]; then
|
|
133
|
+
account_scope_label="全部账号"
|
|
134
|
+
elif [[ "$ACCOUNTS" == "current" ]]; then
|
|
135
|
+
account_scope_label="当前账号"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
PREVIEW_JSON="$(mktemp -t wecom-space-preview.XXXX.json)"
|
|
139
|
+
EXEC_JSON="$(mktemp -t wecom-space-exec.XXXX.json)"
|
|
140
|
+
VERIFY_JSON="$(mktemp -t wecom-space-verify.XXXX.json)"
|
|
141
|
+
PREVIEW_ERR="$(mktemp -t wecom-space-preview.XXXX.err)"
|
|
142
|
+
EXEC_ERR="$(mktemp -t wecom-space-exec.XXXX.err)"
|
|
143
|
+
VERIFY_ERR="$(mktemp -t wecom-space-verify.XXXX.err)"
|
|
144
|
+
trap 'rm -f "$PREVIEW_JSON" "$EXEC_JSON" "$VERIFY_JSON" "$PREVIEW_ERR" "$EXEC_ERR" "$VERIFY_ERR"' EXIT
|
|
145
|
+
|
|
146
|
+
run_cmd_to_file() {
|
|
147
|
+
local dry_run="$1"
|
|
148
|
+
local output_file="$2"
|
|
149
|
+
local err_file="$3"
|
|
150
|
+
local cmd_parts=(
|
|
151
|
+
--space-governance
|
|
152
|
+
--accounts "$ACCOUNTS"
|
|
153
|
+
--tiers "$TIERS"
|
|
154
|
+
--suggested-only "$SUGGESTED_ONLY"
|
|
155
|
+
--allow-recent-active "$ALLOW_RECENT_ACTIVE"
|
|
156
|
+
--output json
|
|
157
|
+
--dry-run "$dry_run"
|
|
158
|
+
)
|
|
159
|
+
if [[ -n "$TARGETS" ]]; then
|
|
160
|
+
cmd_parts+=(--targets "$TARGETS")
|
|
161
|
+
fi
|
|
162
|
+
if [[ -n "$ROOT" ]]; then
|
|
163
|
+
cmd_parts+=(--root "$ROOT")
|
|
164
|
+
fi
|
|
165
|
+
if [[ -n "$STATE_ROOT" ]]; then
|
|
166
|
+
cmd_parts+=(--state-root "$STATE_ROOT")
|
|
167
|
+
fi
|
|
168
|
+
if [[ -n "$EXTERNAL_ROOTS_SOURCE" ]]; then
|
|
169
|
+
cmd_parts+=(--external-roots-source "$EXTERNAL_ROOTS_SOURCE")
|
|
170
|
+
fi
|
|
171
|
+
if [[ "$dry_run" == "false" ]]; then
|
|
172
|
+
cmd_parts+=(--yes)
|
|
173
|
+
fi
|
|
174
|
+
if ! wecom-cleaner "${cmd_parts[@]}" >"$output_file" 2>"$err_file"; then
|
|
175
|
+
err_head="$(head -n 3 "$err_file" 2>/dev/null || true)"
|
|
176
|
+
echo "执行失败(dry-run=$dry_run):${err_head:-未知错误}" >&2
|
|
177
|
+
return 1
|
|
178
|
+
fi
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
run_cmd_to_file true "$PREVIEW_JSON" "$PREVIEW_ERR"
|
|
182
|
+
|
|
183
|
+
matched_targets="$(jq -r '.summary.matchedTargets // 0' "$PREVIEW_JSON")"
|
|
184
|
+
matched_bytes="$(jq -r '.summary.matchedBytes // (.data.report.matched.totalBytes // 0)' "$PREVIEW_JSON")"
|
|
185
|
+
preview_reclaimed="$(jq -r '.summary.reclaimedBytes // 0' "$PREVIEW_JSON")"
|
|
186
|
+
preview_failed="$(jq -r '.summary.failedCount // 0' "$PREVIEW_JSON")"
|
|
187
|
+
tier_count="$(jq -r '.summary.tierCount // (.data.report.matched.byTier // [] | length)' "$PREVIEW_JSON")"
|
|
188
|
+
target_type_count="$(jq -r '.summary.targetTypeCount // (.data.report.matched.byTargetType // [] | length)' "$PREVIEW_JSON")"
|
|
189
|
+
root_path_count="$(jq -r '.summary.rootPathCount // (.data.report.matched.byRoot // [] | length)' "$PREVIEW_JSON")"
|
|
190
|
+
engine="$(jq -r '.data.engineUsed // .meta.engine // "unknown"' "$PREVIEW_JSON")"
|
|
191
|
+
duration_preview="$(jq -r '.meta.durationMs // 0' "$PREVIEW_JSON")"
|
|
192
|
+
warnings_preview="$(jq -r '(.warnings // []) | length' "$PREVIEW_JSON")"
|
|
193
|
+
errors_preview="$(jq -r '(.errors // []) | length' "$PREVIEW_JSON")"
|
|
194
|
+
|
|
195
|
+
executed="false"
|
|
196
|
+
execute_success=0
|
|
197
|
+
execute_skipped=0
|
|
198
|
+
execute_failed=0
|
|
199
|
+
execute_reclaimed=0
|
|
200
|
+
execute_batch="-"
|
|
201
|
+
verify_matched="$matched_targets"
|
|
202
|
+
duration_exec=0
|
|
203
|
+
duration_verify=0
|
|
204
|
+
warnings_exec=0
|
|
205
|
+
warnings_verify=0
|
|
206
|
+
errors_exec=0
|
|
207
|
+
errors_verify=0
|
|
208
|
+
|
|
209
|
+
if [[ "$matched_targets" -gt 0 && "$EXECUTE" == "true" ]]; then
|
|
210
|
+
run_cmd_to_file false "$EXEC_JSON" "$EXEC_ERR"
|
|
211
|
+
executed="true"
|
|
212
|
+
execute_success="$(jq -r '.summary.successCount // 0' "$EXEC_JSON")"
|
|
213
|
+
execute_skipped="$(jq -r '.summary.skippedCount // 0' "$EXEC_JSON")"
|
|
214
|
+
execute_failed="$(jq -r '.summary.failedCount // 0' "$EXEC_JSON")"
|
|
215
|
+
execute_reclaimed="$(jq -r '.summary.reclaimedBytes // 0' "$EXEC_JSON")"
|
|
216
|
+
execute_batch="$(jq -r '.summary.batchId // "-"' "$EXEC_JSON")"
|
|
217
|
+
duration_exec="$(jq -r '.meta.durationMs // 0' "$EXEC_JSON")"
|
|
218
|
+
warnings_exec="$(jq -r '(.warnings // []) | length' "$EXEC_JSON")"
|
|
219
|
+
errors_exec="$(jq -r '(.errors // []) | length' "$EXEC_JSON")"
|
|
220
|
+
|
|
221
|
+
run_cmd_to_file true "$VERIFY_JSON" "$VERIFY_ERR"
|
|
222
|
+
verify_matched="$(jq -r '.summary.matchedTargets // 0' "$VERIFY_JSON")"
|
|
223
|
+
duration_verify="$(jq -r '.meta.durationMs // 0' "$VERIFY_JSON")"
|
|
224
|
+
warnings_verify="$(jq -r '(.warnings // []) | length' "$VERIFY_JSON")"
|
|
225
|
+
errors_verify="$(jq -r '(.errors // []) | length' "$VERIFY_JSON")"
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
if [[ "$executed" == "true" ]]; then
|
|
229
|
+
conclusion="已完成"
|
|
230
|
+
reason="已按授权执行全量空间治理,并完成同条件复核。"
|
|
231
|
+
elif [[ "$matched_targets" -eq 0 ]]; then
|
|
232
|
+
conclusion="无需执行"
|
|
233
|
+
reason="当前筛选条件下没有可治理目标,按安全规则未执行真实删除。"
|
|
234
|
+
else
|
|
235
|
+
conclusion="仅预演"
|
|
236
|
+
reason="已完成预演,等待你确认执行真实治理。"
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
duration_total=$((duration_preview + duration_exec + duration_verify))
|
|
240
|
+
warnings_total=$((warnings_preview + warnings_exec + warnings_verify))
|
|
241
|
+
errors_total=$((errors_preview + errors_exec + errors_verify))
|
|
242
|
+
|
|
243
|
+
printf '\n=== 全量空间治理结果(给用户)===\n'
|
|
244
|
+
if [[ "$executed" == "true" ]]; then
|
|
245
|
+
printf -- '- 已完成:已治理 %s 项空间目标,释放 %s。\n' "$execute_success" "$(human_bytes "$execute_reclaimed")"
|
|
246
|
+
elif [[ "$matched_targets" -eq 0 ]]; then
|
|
247
|
+
printf -- '- 已完成检查:当前条件下未发现可治理目标,本次未执行删除。\n'
|
|
248
|
+
else
|
|
249
|
+
printf -- '- 已完成预演:预计可治理 %s 项、释放 %s;等待确认执行。\n' "$matched_targets" "$(human_bytes "$preview_reclaimed")"
|
|
250
|
+
fi
|
|
251
|
+
printf -- '- 你的目标:按“全量空间治理”规则清理低风险缓存目录。\n'
|
|
252
|
+
|
|
253
|
+
printf '\n你关心的范围\n'
|
|
254
|
+
printf -- '- 账号:%s\n' "$account_scope_label"
|
|
255
|
+
printf -- '- 层级:%s\n' "$TIERS"
|
|
256
|
+
printf -- '- 仅建议项:%s\n' "$SUGGESTED_ONLY"
|
|
257
|
+
printf -- '- 允许近期活跃:%s\n' "$ALLOW_RECENT_ACTIVE"
|
|
258
|
+
printf -- '- 命中路径根:%s 个\n' "$root_path_count"
|
|
259
|
+
|
|
260
|
+
printf '\n治理结果总览\n'
|
|
261
|
+
printf -- '- 命中治理项:%s 项\n' "$matched_targets"
|
|
262
|
+
printf -- '- 命中体积:%s\n' "$(human_bytes "$matched_bytes")"
|
|
263
|
+
printf -- '- 预计释放:%s\n' "$(human_bytes "$preview_reclaimed")"
|
|
264
|
+
if [[ "$executed" == "true" ]]; then
|
|
265
|
+
printf -- '- 实际释放:%s\n' "$(human_bytes "$execute_reclaimed")"
|
|
266
|
+
printf -- '- 清理批次:%s(可用于恢复)\n' "$execute_batch"
|
|
267
|
+
printf -- '- 复核结果:剩余可治理 %s 项\n' "$verify_matched"
|
|
268
|
+
else
|
|
269
|
+
printf -- '- 执行状态:未执行真实治理(%s)\n' "$reason"
|
|
270
|
+
printf -- '- 复核结果:沿用预演结论(剩余可治理 %s 项)\n' "$verify_matched"
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
printf '\n按风险层级统计\n'
|
|
274
|
+
tier_rows=0
|
|
275
|
+
while IFS=$'\t' read -r tier tier_label_raw count bytes suggested_count active_count; do
|
|
276
|
+
[[ -z "${tier:-}" ]] && continue
|
|
277
|
+
if [[ -z "$tier_label_raw" || "$tier_label_raw" == "null" ]]; then
|
|
278
|
+
tier_label_raw="$(tier_label "$tier")"
|
|
279
|
+
fi
|
|
280
|
+
printf -- '- %s:%s 项,%s(建议 %s 项,近期活跃 %s 项)\n' \
|
|
281
|
+
"$tier_label_raw" "$count" "$(human_bytes "$bytes")" "$suggested_count" "$active_count"
|
|
282
|
+
tier_rows=$((tier_rows + 1))
|
|
283
|
+
done < <(
|
|
284
|
+
jq -r '.data.report.matched.byTier // [] | .[] | [(.tier // "-"), (.tierLabel // ""), ((.targetCount // 0)|tostring), ((.sizeBytes // 0)|tostring), ((.suggestedCount // 0)|tostring), ((.recentlyActiveCount // 0)|tostring)] | @tsv' \
|
|
285
|
+
"$PREVIEW_JSON"
|
|
286
|
+
)
|
|
287
|
+
if [[ "$tier_rows" -eq 0 ]]; then
|
|
288
|
+
printf -- '- 无层级数据。\n'
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
printf '\n按目标类型统计(你清理了什么)\n'
|
|
292
|
+
target_rows=0
|
|
293
|
+
while IFS=$'\t' read -r label count bytes; do
|
|
294
|
+
[[ -z "${label:-}" ]] && continue
|
|
295
|
+
printf -- '- %s:%s 项,%s\n' "$label" "$count" "$(human_bytes "$bytes")"
|
|
296
|
+
target_rows=$((target_rows + 1))
|
|
297
|
+
if [[ "$target_rows" -ge 20 ]]; then
|
|
298
|
+
break
|
|
299
|
+
fi
|
|
300
|
+
done < <(
|
|
301
|
+
jq -r '.data.report.matched.byTargetType // [] | .[] | [(.targetLabel // .targetKey // "-"), ((.targetCount // 0)|tostring), ((.sizeBytes // 0)|tostring)] | @tsv' \
|
|
302
|
+
"$PREVIEW_JSON"
|
|
303
|
+
)
|
|
304
|
+
if [[ "$target_rows" -eq 0 ]]; then
|
|
305
|
+
printf -- '- 无命中目标类型。\n'
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
printf '\n路径范围(主要治理目录)\n'
|
|
309
|
+
root_rows=0
|
|
310
|
+
while IFS=$'\t' read -r root_path count bytes root_type; do
|
|
311
|
+
[[ -z "${root_path:-}" ]] && continue
|
|
312
|
+
type_label="账号目录"
|
|
313
|
+
if [[ "$root_type" == "external" ]]; then
|
|
314
|
+
type_label="外部存储"
|
|
315
|
+
fi
|
|
316
|
+
printf -- '- [%s] %s:%s 项,%s\n' "$type_label" "$(short_path "$root_path" 88)" "$count" "$(human_bytes "$bytes")"
|
|
317
|
+
root_rows=$((root_rows + 1))
|
|
318
|
+
if [[ "$root_rows" -ge 10 ]]; then
|
|
319
|
+
break
|
|
320
|
+
fi
|
|
321
|
+
done < <(
|
|
322
|
+
jq -r '.data.report.matched.byRoot // [] | .[] | [(.rootPath // "-"), ((.targetCount // 0)|tostring), ((.sizeBytes // 0)|tostring), (.rootType // "profile")] | @tsv' \
|
|
323
|
+
"$PREVIEW_JSON"
|
|
324
|
+
)
|
|
325
|
+
if [[ "$root_rows" -eq 0 ]]; then
|
|
326
|
+
printf -- '- 无命中目录。\n'
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
printf '\n路径样例(按体积Top 10)\n'
|
|
330
|
+
top_rows=0
|
|
331
|
+
while IFS=$'\t' read -r p label tier_label_text bytes acc suggested active; do
|
|
332
|
+
[[ -z "${p:-}" ]] && continue
|
|
333
|
+
tag=""
|
|
334
|
+
if [[ "$suggested" == "true" ]]; then
|
|
335
|
+
tag="${tag}建议 "
|
|
336
|
+
fi
|
|
337
|
+
if [[ "$active" == "true" ]]; then
|
|
338
|
+
tag="${tag}活跃 "
|
|
339
|
+
fi
|
|
340
|
+
printf -- '- %s | %s | %s | %s | %s%s\n' "$label" "$tier_label_text" "$acc" "$(human_bytes "$bytes")" "$tag" "$(short_path "$p" 80)"
|
|
341
|
+
top_rows=$((top_rows + 1))
|
|
342
|
+
if [[ "$top_rows" -ge 10 ]]; then
|
|
343
|
+
break
|
|
344
|
+
fi
|
|
345
|
+
done < <(
|
|
346
|
+
jq -r '.data.report.matched.topPaths // [] | .[] | [(.path // "-"), (.targetLabel // .targetKey // "-"), (.tierLabel // .tier // "-"), (.sizeBytes // 0 | tostring), (.accountShortId // "-"), ((.suggested // false)|tostring), ((.recentlyActive // false)|tostring)] | @tsv' \
|
|
347
|
+
"$PREVIEW_JSON"
|
|
348
|
+
)
|
|
349
|
+
if [[ "$top_rows" -eq 0 ]]; then
|
|
350
|
+
printf -- '- 无路径样例。\n'
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
if [[ "$executed" == "true" ]]; then
|
|
354
|
+
printf '\n实际执行明细(按目标类型)\n'
|
|
355
|
+
exec_rows=0
|
|
356
|
+
while IFS=$'\t' read -r label s k f sbytes; do
|
|
357
|
+
[[ -z "${label:-}" ]] && continue
|
|
358
|
+
printf -- '- %s:成功 %s / 跳过 %s / 失败 %s,实际释放 %s\n' "$label" "$s" "$k" "$f" "$(human_bytes "$sbytes")"
|
|
359
|
+
exec_rows=$((exec_rows + 1))
|
|
360
|
+
if [[ "$exec_rows" -ge 20 ]]; then
|
|
361
|
+
break
|
|
362
|
+
fi
|
|
363
|
+
done < <(
|
|
364
|
+
jq -r '.data.report.executed.byCategory // [] | .[] | [(.categoryLabel // .categoryKey // "-"), ((.successCount // 0)|tostring), ((.skippedCount // 0)|tostring), ((.failedCount // 0)|tostring), ((.successBytes // 0)|tostring)] | @tsv' \
|
|
365
|
+
"$EXEC_JSON"
|
|
366
|
+
)
|
|
367
|
+
if [[ "$exec_rows" -eq 0 ]]; then
|
|
368
|
+
printf -- '- 当前未返回执行落地明细。\n'
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
printf '\n实际执行明细(按月份)\n'
|
|
372
|
+
exec_month_rows=0
|
|
373
|
+
while IFS=$'\t' read -r month s k f sbytes; do
|
|
374
|
+
[[ -z "${month:-}" ]] && continue
|
|
375
|
+
printf -- '- %s:成功 %s / 跳过 %s / 失败 %s,实际释放 %s\n' "$month" "$s" "$k" "$f" "$(human_bytes "$sbytes")"
|
|
376
|
+
exec_month_rows=$((exec_month_rows + 1))
|
|
377
|
+
if [[ "$exec_month_rows" -ge 24 ]]; then
|
|
378
|
+
break
|
|
379
|
+
fi
|
|
380
|
+
done < <(
|
|
381
|
+
jq -r '.data.report.executed.byMonth // [] | .[] | [(.monthKey // "非月份目录"), ((.successCount // 0)|tostring), ((.skippedCount // 0)|tostring), ((.failedCount // 0)|tostring), ((.successBytes // 0)|tostring)] | @tsv' \
|
|
382
|
+
"$EXEC_JSON"
|
|
383
|
+
)
|
|
384
|
+
if [[ "$exec_month_rows" -eq 0 ]]; then
|
|
385
|
+
printf -- '- 当前未返回按月份执行明细。\n'
|
|
386
|
+
fi
|
|
387
|
+
fi
|
|
388
|
+
|
|
389
|
+
printf '\n运行状态\n'
|
|
390
|
+
printf -- '- 扫描引擎:%s\n' "$engine"
|
|
391
|
+
printf -- '- 耗时:%s ms\n' "$duration_total"
|
|
392
|
+
printf -- '- 告警:%s\n' "$warnings_total"
|
|
393
|
+
printf -- '- 错误:%s\n' "$errors_total"
|
|
394
|
+
printf -- '- 预演失败项:%s\n' "$preview_failed"
|
|
395
|
+
printf -- '- 风险层级数量:%s,目标类型数量:%s\n' "$tier_count" "$target_type_count"
|
|
396
|
+
|
|
397
|
+
printf '\n指标释义\n'
|
|
398
|
+
printf -- '- 命中治理项:本次筛选条件下可处理的目录目标数量。\n'
|
|
399
|
+
printf -- '- 预计释放:预演估算可回收空间,真实执行前不会实际删除。\n'
|
|
400
|
+
printf -- '- 建议项:由策略判断为优先治理的低风险目标。\n'
|
|
401
|
+
printf -- '- 近期活跃:最近仍有访问行为的目录,默认建议跳过。\n'
|