@mrtrinhvn/ag-kit 1.1.6 → 1.1.7
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/bin/cli.js +45 -13
- package/package.json +1 -1
- package/template/.agent/knowledge/tele-agentic-standard.md +19 -21
- package/template/.agent/skills/knowledge-management/SKILL.md +1 -1
- package/template/.agent/skills/telegram-agentic-gateway/SKILL.md +61 -44
- package/template/start.sh +16 -19
- package/template/stop_bot.sh +5 -13
package/bin/cli.js
CHANGED
|
@@ -75,7 +75,7 @@ program.command('init')
|
|
|
75
75
|
copyRecursiveSync(srcAgent, TARGET_AGENT_DIR);
|
|
76
76
|
// Ghi lại version vào .agent/.version
|
|
77
77
|
fs.writeFileSync(path.join(TARGET_AGENT_DIR, '.version'), pkg.version);
|
|
78
|
-
console.log(
|
|
78
|
+
console.log(`\x1b[32m✅ Đã cài ráp thành công thư mục .agent (phiên bản v${pkg.version}) vào Project!\x1b[0m`);
|
|
79
79
|
|
|
80
80
|
// Khởi tạo .env nếu chưa có
|
|
81
81
|
const envPath = path.join(process.cwd(), '.env');
|
|
@@ -97,7 +97,7 @@ program.command('init')
|
|
|
97
97
|
if (installedVersion !== pkg.version) {
|
|
98
98
|
console.log(`\x1b[35m👉 Phát hiện phiên bản cũ (v${installedVersion}). Hãy dùng lệnh "ag-kit update" để cập nhật lên v${pkg.version}.\x1b[0m`);
|
|
99
99
|
} else {
|
|
100
|
-
console.log(
|
|
100
|
+
console.log(`\x1b[34mℹ️ Bạn đang sử dụng phiên bản khớp với bộ công cụ (v${pkg.version}).\x1b[0m`);
|
|
101
101
|
}
|
|
102
102
|
} else {
|
|
103
103
|
console.log('\x1b[35m👉 Hãy dùng lệnh "ag-kit update" để đồng bộ cấu trúc mới.\x1b[0m');
|
|
@@ -112,11 +112,12 @@ program.command('update')
|
|
|
112
112
|
.action(() => {
|
|
113
113
|
console.log('\x1b[36m🔄 Đang cập nhật siêu luân xa Lập trình (AG-Kit)...\x1b[0m');
|
|
114
114
|
const srcAgent = path.join(TEMPLATE_DIR, '.agent');
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const vPath = path.join(TARGET_AGENT_DIR, '.version');
|
|
116
|
+
let oldVersion = '0.0.0';
|
|
117
|
+
if (fs.existsSync(vPath)) {
|
|
118
|
+
oldVersion = fs.readFileSync(vPath, 'utf-8').trim();
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
+
|
|
120
121
|
// Chỉ cập nhật não bộ chuẩn (Agents, Skills, Workflows, Scripts, Rules)
|
|
121
122
|
// Tôn trọng dữ liệu Knowledge của dự án đang có
|
|
122
123
|
['agents', 'skills', 'workflows', 'scripts', 'rules'].forEach(folder => {
|
|
@@ -129,8 +130,9 @@ program.command('update')
|
|
|
129
130
|
|
|
130
131
|
migrateLegacyMemories();
|
|
131
132
|
// Cập nhật version vào .agent/.version
|
|
132
|
-
fs.writeFileSync(
|
|
133
|
-
console.log(
|
|
133
|
+
fs.writeFileSync(vPath, pkg.version);
|
|
134
|
+
console.log(`\x1b[32m✅ Cập nhật thành công! Phiên bản hiện tại: v${oldVersion} -> v${pkg.version}\x1b[0m`);
|
|
135
|
+
console.log('\x1b[34m⚡️ Toàn bộ cấu trúc kỹ năng đã được đồng bộ.\x1b[0m');
|
|
134
136
|
});
|
|
135
137
|
|
|
136
138
|
program.command('check')
|
|
@@ -243,10 +245,13 @@ program.command('status')
|
|
|
243
245
|
});
|
|
244
246
|
|
|
245
247
|
|
|
246
|
-
|
|
248
|
+
async function runInteractiveMenu() {
|
|
247
249
|
const inquirer = require('inquirer');
|
|
250
|
+
console.clear();
|
|
248
251
|
console.log('\x1b[35m\n🧠 ANTIGRAVITY KIT - BẢNG ĐIỀU KHIỂN TÁC VỤ\x1b[0m');
|
|
249
|
-
|
|
252
|
+
console.log('\x1b[34m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m');
|
|
253
|
+
|
|
254
|
+
const { action } = await inquirer.prompt([
|
|
250
255
|
{
|
|
251
256
|
type: 'list',
|
|
252
257
|
name: 'action',
|
|
@@ -260,10 +265,37 @@ if (process.argv.length <= 2) {
|
|
|
260
265
|
{ name: '❌ Thoát', value: 'exit' }
|
|
261
266
|
]
|
|
262
267
|
}
|
|
263
|
-
])
|
|
264
|
-
|
|
265
|
-
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
if (action === 'exit') {
|
|
271
|
+
console.log('\x1b[36m👋 Tạm biệt! Hợp tác vui vẻ.\x1b[0m\n');
|
|
272
|
+
process.exit(0);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Thực thi command
|
|
276
|
+
console.log('\n\x1b[34m--- ĐANG THỰC THI ---\x1b[0m');
|
|
277
|
+
|
|
278
|
+
// Chúng ta parse lại command với action đã chọn
|
|
279
|
+
// Lưu ý: commander parse() trả về promise với parseAsync
|
|
280
|
+
await program.parseAsync([process.argv[0], process.argv[1], action]);
|
|
281
|
+
|
|
282
|
+
console.log('\n\x1b[34m--- HOÀN TẤT TÁC VỤ ---\x1b[0m');
|
|
283
|
+
|
|
284
|
+
const { resume } = await inquirer.prompt([
|
|
285
|
+
{
|
|
286
|
+
type: 'input',
|
|
287
|
+
name: 'resume',
|
|
288
|
+
message: 'Nhấn Enter để quay lại Menu quản trị...'
|
|
266
289
|
}
|
|
290
|
+
]);
|
|
291
|
+
|
|
292
|
+
return runInteractiveMenu();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (process.argv.length <= 2) {
|
|
296
|
+
runInteractiveMenu().catch(err => {
|
|
297
|
+
console.error('\x1b[31m❌ Lỗi thực thi Menu:\x1b[0m', err);
|
|
298
|
+
process.exit(1);
|
|
267
299
|
});
|
|
268
300
|
} else {
|
|
269
301
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
# TIÊU CHUẨN GIAO TIẾP TẬP TRUNG (TELE-AGENTIC STANDARD)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Hệ thống kế thừa tinh hoa từ các tiêu chuẩn điều khiển từ xa: **Remoat**, **LazyGravity** và tối ưu token **VFS**.
|
|
4
4
|
|
|
5
5
|
## 📡 KIẾN TRÚC "COMBO VÀNG" (GOLDEN COMBO)
|
|
6
6
|
|
|
7
|
-
Hệ thống được vận hành bởi 3 trụ cột:
|
|
8
|
-
1. **IDE
|
|
9
|
-
2. **Telegram
|
|
10
|
-
3. **CDP Bridge
|
|
7
|
+
Hệ thống được vận hành bởi 3 trụ cột (Nguồn: [Remoat Strategy](https://github.com/optimistengineer/Remoat)):
|
|
8
|
+
1. **IDE Participant**: Antigravity đóng vai trò là thực thể thực thi duy nhất, khởi động ở chế độ Debug (`--remote-debugging-port=9555`).
|
|
9
|
+
2. **Telegram Gateway**: Cổng giao tiếp tập trung qua Bot & Group Topics (Nguồn: [LazyGravity Patterns](https://github.com/tokyoweb3/LazyGravity)).
|
|
10
|
+
3. **CDP Bridge**: Cầu nối WebSocket kết nối "Lễ tân" (Bot) và "Quản đốc" (IDE).
|
|
11
11
|
|
|
12
|
-
### 1. Phân Lập Trí Nhớ
|
|
13
|
-
|
|
14
|
-
- **Topic 🔴 Surgery (Phẫu thuật)**: Chuyên dụng cho các lệnh `/heal` và workflow `@brain-surgeon`.
|
|
15
|
-
- **Topic 🟢 Signals (Tín hiệu)**: Hiển thị lệnh mua/bán từ AI.
|
|
16
|
-
- **Topic 🔵 General (Tổng quan)**: Quản trị hệ thống, lệnh `/status`.
|
|
12
|
+
### 1. Phân Lập Trí Nhớ (Topic Memory)
|
|
13
|
+
Sử dụng Telegram Topics để cô lập bối cảnh công việc. Lựa chọn Topic phù hợp giúp Agent không bị "loạn não" giữa các Task khác nhau.
|
|
17
14
|
|
|
18
|
-
### 2. Giao Diện
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- **Doctor Call**: Nút bấm kích hoạt Logic tự chữa lành.
|
|
15
|
+
### 2. Giao Diện Premium (Aesthetics)
|
|
16
|
+
Dựa trên [awesome-grammY](https://github.com/grammyjs/awesome-grammY), giao diện phải đủ sang trọng (Premium) và rõ ràng:
|
|
17
|
+
- **Dashboard**: Lệnh `/status` hiển thị bảng sức khỏe AI chuyên sâu.
|
|
18
|
+
- **Micro-interactions**: Nút bấm Inline với ID ngắn (ShortID) để quản trị nhanh trên Mobile.
|
|
23
19
|
|
|
24
|
-
## 🛠️
|
|
20
|
+
## 🛠️ CHIẾN LƯỢC TOÀN DIỆN (STRATEGIC PROTOCOL)
|
|
25
21
|
|
|
26
|
-
1. **
|
|
27
|
-
2. **
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
1. **Tiết kiệm Token (VFS Protocol)**: PHẢI sử dụng công cụ [vfs](https://github.com/TrNgTien/vfs) để quét cấu trúc mã nguồn trước khi đọc file. Kỹ thuật này giúp tiết kiệm 98% chi phí vận hành.
|
|
23
|
+
2. **Model Selection (Local vs Cloud)**:
|
|
24
|
+
- Sử dụng **Local Models** (Ollama Host) cho các bước phân tích, tìm lỗi và lập phác đồ (Regent mode).
|
|
25
|
+
- Sử dụng **Cloud Models** (Claude/Gemini) cho bước ghi mã nguồn (Implementation) và giải quyết logic cực khó.
|
|
26
|
+
3. **Scripts Đồng Bộ**: Hệ thống phải đi kèm bộ đôi `start.sh` (khởi chạy bot + debugger) và `stop_bot.sh` (tắt an toàn).
|
|
30
27
|
|
|
31
28
|
---
|
|
32
|
-
*Tham chiếu: .agent/skills/telegram-agentic-gateway*
|
|
29
|
+
*Tham chiếu Skill: .agent/skills/telegram-agentic-gateway*
|
|
30
|
+
*Nguồn gốc Kiến thức: Remoat, LazyGravity, VFS, grammY.*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: knowledge-management
|
|
3
|
-
description: Hệ thống tư duy và giao thức duy trì tri thức (Systems Thinking & Knowledge Retention).
|
|
3
|
+
description: Hệ thống tư duy và giao thức duy trì tri thức (Systems Thinking & Knowledge Retention). **QUY TẮC CỐI THƯỢNG: Khi cập nhật ag-kit, phải đồng bộ sang thư mục `template/`. Khi cập nhật Skills, BẮT BUỘC phải đảm bảo tính toàn vẹn cấu trúc (Indexing A, B, C... không được nhảy cóc).**
|
|
4
4
|
allowed-tools: Read, Write, Glob, Grep
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -1,50 +1,67 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: telegram-agentic-gateway
|
|
3
|
-
description: Tiêu chuẩn Giao tiếp Telegram (Telegram Gateway) cho các Agentic Projects.
|
|
3
|
+
description: Tiêu chuẩn Giao tiếp Telegram (Telegram Gateway) cho các Agentic Projects. Cổng điều phối hợp nhất IDE, Bot và Ký ức.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# KỸ NĂNG: XÂY DỰNG TRẠM ĐIỀU HÀNH TELEGRAM (Telegram Agentic Gateway)
|
|
7
7
|
|
|
8
|
-
Khi
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
## PHẦN
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
8
|
+
Khi triển khai "Cổng điều khiển Telegram", bạn (AI) phải thiết kế một `TelegramGateway` chuyên nghiệp, tuân thủ nghiêm ngặt cấu trúc **7 Phôi (7 Layers)** dưới đây. Tuyệt đối không được bỏ sót hoặc làm xáo trộn thứ tự các mục khi cập nhật.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## PHẦN A: BỘ LỆNH QUẢN TRỊ SINH TỒN (Core Commands)
|
|
13
|
+
Mọi Gateway phải hỗ trợ các lệnh điều khiển hệ thống sau:
|
|
14
|
+
1. **`/start`**: Kiểm tra kết nối. Trả về thông số `IDE_PORT` (CDP), trạng thái Node/Ollama và phiên bản Agent.
|
|
15
|
+
2. **`/status` (Dashboard)**: Hiển thị bảng điều khiển Premium về sức khỏe AI, các Agent đang chạy ngầm và Task hiện tại.
|
|
16
|
+
3. **`/model`**: Menu chọn Model linh hoạt. Cho phép chuyển đổi giữa **Local Ollama** (tiết kiệm token) và **Cloud models** (tác vụ phức tạp).
|
|
17
|
+
4. **`/screenshot` (Visual Support)**: Chụp màn hình IDE qua CDP (`Page.captureScreenshot`).
|
|
18
|
+
5. **`/heal` / `/healplan`**: Luồng tự chữa lành (Self-Healing) tích hợp phê duyệt người dùng.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## PHẦN B: TRÍ NHỚ PHÂN NHIỆM (Context Isolation)
|
|
23
|
+
Kế thừa từ tiêu chuẩn **LazyGravity**, bắt buộc cô lập trí nhớ dựa trên **Telegram Topics** (Threads):
|
|
24
|
+
- Trích xuất `msg.message_thread_id` để định danh file context (Vd: `topic_123.json`).
|
|
25
|
+
- Mỗi Topic là một bộ não làm việc riêng biệt, ngăn chặn hiện tượng chập cheng ký ức giữa các tác vụ khác nhau.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## PHẦN C: GIAO DIỆN PREMIUM (UI/UX Standards)
|
|
30
|
+
Tuân thủ tiêu chuẩn thẩm mỹ của [awesome-grammY](https://github.com/grammyjs/awesome-grammY):
|
|
31
|
+
- **Cấu trúc khối**: Sử dụng `<b>` cho Header, ký tự `━━━━━` làm phân cách.
|
|
32
|
+
- **Emoji Logic**: ✅ Active, 🚨 Warning, 🛠️ Debugging, 🏥 Surgery.
|
|
33
|
+
- **ShortID Mapping**: Vì Telegram giới hạn callback data (64 bytes), hãy map ID dài vào biến tạm và nhả ra mã ngắn (Vd: `sess_1`).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## PHẦN D: CHIẾN LƯỢC TIẾT KIỆM TOKEN (Model & VFS Strategy)
|
|
38
|
+
Tối ưu chi phí vận hành thông qua Token Efficiency:
|
|
39
|
+
1. **VFS First (Virtual File System)**: Tuyệt đối không đọc file thô hàng ngàn dòng. PHẢI dùng kỹ năng `vfs-assistant` để quét chữ ký (signatures) trước. (Nguồn: [vfs](https://github.com/TrNgTien/vfs)).
|
|
40
|
+
2. **Regent Agent (Chế độ Nhiếp chính)**: Sử dụng các model Local (Ollama) cho các bước phân tích bối cảnh, refactor nhỏ. Chỉ dùng Cloud cho bước Implementation cuối cùng.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## PHẦN E: CỔNG PORTAL IDE & CDP (Golden Loop Participant)
|
|
45
|
+
Biến IDE thành một thực thể tham gia trực tiếp dự án (Participant):
|
|
46
|
+
1. **Remoat Strategy**: IDE Antigravity **BẮT BUỘC** khởi động với `--remote-debugging-port=9555`.
|
|
47
|
+
2. **HUD Injection**: Tiêm `ag_hud.js` vào IDE qua CDP để hiển thị chỉ số sức khỏe AI (Gen, WinRate) trực quan trong không gian code.
|
|
48
|
+
3. **Bidirectional Sync**: Đồng bộ trạng thái từ Bot -> IDE (HUD) và ngược lại một cách liên tục.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## PHẦN F: THIẾT LẬP HẠ TẦNG (Deployment & Scripts)
|
|
53
|
+
1. **Group Creation**: Tạo Telegram Group, thêm Bot làm Admin, bật tính năng Multi-Topics.
|
|
54
|
+
2. **Environment (.env)**: Phải cấu hình thống nhất `IDE_PORT`, `TELEGRAM_BOT_TOKEN`, `ALLOWED_USER_IDS` và `OLLAMA_HOST`.
|
|
55
|
+
3. **Standard Scripts**: Sử dụng bộ đôi `start.sh` (khởi chạy bot + gateway) và `stop_bot.sh` (tắt an toàn).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## PHẦN G: NGUỒN GỐC TRI THỨC (Knowledge Roots)
|
|
60
|
+
Mọi kỹ năng đều có nguồn gốc rõ ràng, không được làm mất gốc kiến thức:
|
|
61
|
+
- **[Remoat](https://github.com/optimistengineer/Remoat)**: Remote Management & CDP Control.
|
|
62
|
+
- **[LazyGravity](https://github.com/tokyoweb3/LazyGravity)**: Project Coordination & Session Isolation.
|
|
63
|
+
- **[vfs](https://github.com/TrNgTien/vfs)**: Token Efficiency standard.
|
|
64
|
+
- **[grammY Aesthetics](https://github.com/grammyjs/awesome-grammY)**: UI/UX standards.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
*(Ghi chú cho AI)*: Khi cập nhật Template, BẮT BUỘC phải kiểm tra toàn vẹn cấu trúc (A -> G), không được tạo các mục nhảy cóc.
|
package/template/start.sh
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# start.sh - Khởi động Cổng Agentic (Remoat + LazyGravity standard)
|
|
2
3
|
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
# 1. Load configuration
|
|
8
|
-
if [ -f .env ]; then
|
|
9
|
-
export $(grep -v '^#' .env | xargs)
|
|
10
|
-
else
|
|
11
|
-
echo "❌ Lỗi: Không tìm thấy file .env. Vui lòng tạo từ .env.example"
|
|
12
|
-
exit 1
|
|
4
|
+
# 1. Kiểm tra .env
|
|
5
|
+
if [ ! -f .env ]; then
|
|
6
|
+
echo "❌ Lỗi: Không tìm thấy file .env. Vui lòng tạo dựa trên .env.example"
|
|
7
|
+
exit 1
|
|
13
8
|
fi
|
|
14
9
|
|
|
15
|
-
# 2.
|
|
16
|
-
|
|
17
|
-
PROJECT_NAME=${PROJECT_NAME:-"ceogravity"}
|
|
10
|
+
# 2. Đọc cấu hình
|
|
11
|
+
source .env
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
# 3. Khởi động IDE ở chế độ Debug (nếu chưa chạy)
|
|
14
|
+
# Lưu ý: User nên chạy ./launch_remote_ide.sh trước hoặc script này sẽ kiểm tra port
|
|
15
|
+
nc -z localhost ${IDE_PORT:-9555}
|
|
16
|
+
if [ $? -ne 0 ]; then
|
|
17
|
+
echo "⚠️ Cảnh báo: Cổng CDP ${IDE_PORT:-9555} chưa mở. Đang chờ Antigravity khởi động..."
|
|
18
|
+
fi
|
|
22
19
|
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
# 4. Khởi động Bridge và Bot (Sử dụng Lễ tân/Receptionist pattern)
|
|
21
|
+
echo "🚀 Khởi động Lễ tân trực chiến (Agentic Gateway)..."
|
|
22
|
+
./receptionist_up.sh
|
package/template/stop_bot.sh
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# stop_bot.sh - Hạ cánh an toàn (Remoat + LazyGravity standard)
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
# --------------------------------------------------
|
|
4
|
+
echo "🛑 Đang tắt toàn bộ trạm điều hành Agentic..."
|
|
5
5
|
|
|
6
|
-
#
|
|
7
|
-
|
|
6
|
+
# Gọi script hạ cánh tiêu chuẩn
|
|
7
|
+
./receptionist_down.sh
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
PIDS=$(pgrep -f "tsx src/index.ts")
|
|
11
|
-
|
|
12
|
-
if [ -z "$PIDS" ]; then
|
|
13
|
-
echo "⚠️ Không tìm thấy tiến trình Bot đang chạy."
|
|
14
|
-
else
|
|
15
|
-
kill $PIDS
|
|
16
|
-
echo "✅ Đã dừng Bot (PIDs: $PIDS)"
|
|
17
|
-
fi
|
|
9
|
+
echo "✅ Đã dọn dẹp xong."
|