@mrtrinhvn/ag-kit 1.1.7 → 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/bin/cli.js +46 -11
- package/package.json +2 -2
- package/template/.agent/scripts/port_utils.sh +105 -0
- package/template/.agent/scripts/receptionist_down.sh +39 -0
- package/template/.agent/scripts/receptionist_up.sh +85 -0
- package/template/.env.example +5 -1
- package/template/scripts/ag_hud.js +162 -0
- package/template/scripts/ag_portal_bridge.js +141 -0
- package/template/start.sh +2 -2
- package/template/stop_bot.sh +2 -2
package/bin/cli.js
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
2
|
const { program } = require('commander');
|
|
4
3
|
const fs = require('fs');
|
|
5
4
|
const path = require('path');
|
|
5
|
+
const net = require('net');
|
|
6
6
|
|
|
7
7
|
const TEMPLATE_DIR = path.join(__dirname, '..', 'template');
|
|
8
8
|
const TARGET_AGENT_DIR = path.join(process.cwd(), '.agent');
|
|
9
9
|
|
|
10
|
+
function isPortAvailable(port) {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const server = net.createServer();
|
|
13
|
+
server.once('error', () => resolve(false));
|
|
14
|
+
server.once('listening', () => {
|
|
15
|
+
server.close();
|
|
16
|
+
resolve(true);
|
|
17
|
+
});
|
|
18
|
+
server.listen(port, '127.0.0.1');
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function findNextAvailablePort(startPort) {
|
|
23
|
+
let port = startPort;
|
|
24
|
+
while (!(await isPortAvailable(port))) {
|
|
25
|
+
port++;
|
|
26
|
+
}
|
|
27
|
+
return port;
|
|
28
|
+
}
|
|
29
|
+
|
|
10
30
|
// Helper copy đệ quy
|
|
11
31
|
function copyRecursiveSync(src, dest) {
|
|
12
32
|
const exists = fs.existsSync(src);
|
|
@@ -77,18 +97,33 @@ program.command('init')
|
|
|
77
97
|
fs.writeFileSync(path.join(TARGET_AGENT_DIR, '.version'), pkg.version);
|
|
78
98
|
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
99
|
|
|
80
|
-
// Khởi
|
|
100
|
+
// Khởi động .env nếu chưa có
|
|
81
101
|
const envPath = path.join(process.cwd(), '.env');
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
fs.
|
|
89
|
-
console.log(
|
|
102
|
+
(async () => {
|
|
103
|
+
const idePort = await findNextAvailablePort(9555);
|
|
104
|
+
const bridgePort = await findNextAvailablePort(idePort + 1);
|
|
105
|
+
|
|
106
|
+
if (!fs.existsSync(envPath)) {
|
|
107
|
+
const envTemplate = `# --- Remote Orchestration (Golden Combo) ---\nIDE_PORT=${idePort}\nBRIDGE_PORT=${bridgePort}\n\nPROJECT_NAME=ag-project\n`;
|
|
108
|
+
fs.writeFileSync(envPath, envTemplate);
|
|
109
|
+
console.log(`\x1b[34mℹ️ Đã tạo .env với IDE_PORT=${idePort} và BRIDGE_PORT=${bridgePort}\x1b[0m`);
|
|
110
|
+
} else {
|
|
111
|
+
let envContent = fs.readFileSync(envPath, 'utf-8');
|
|
112
|
+
let changed = false;
|
|
113
|
+
if (!envContent.includes('IDE_PORT=')) {
|
|
114
|
+
envContent += `\nIDE_PORT=${idePort}\n`;
|
|
115
|
+
changed = true;
|
|
116
|
+
}
|
|
117
|
+
if (!envContent.includes('BRIDGE_PORT=')) {
|
|
118
|
+
envContent += `BRIDGE_PORT=${bridgePort}\n`;
|
|
119
|
+
changed = true;
|
|
120
|
+
}
|
|
121
|
+
if (changed) {
|
|
122
|
+
fs.writeFileSync(envPath, envContent);
|
|
123
|
+
console.log(`\x1b[34mℹ️ Đã bổ sung cổng tự động: IDE_PORT=${idePort}, BRIDGE_PORT=${bridgePort}\x1b[0m`);
|
|
124
|
+
}
|
|
90
125
|
}
|
|
91
|
-
}
|
|
126
|
+
})();
|
|
92
127
|
} else {
|
|
93
128
|
console.log('\x1b[33m⚠️ Thư mục .agent đã tồn tại.\x1b[0m');
|
|
94
129
|
const vPath = path.join(TARGET_AGENT_DIR, '.version');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrtrinhvn/ag-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Antigravity Kit Base Framework - Generic Agentic AI Programming Core",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
"commander": "^14.0.3",
|
|
33
33
|
"inquirer": "^8.2.5"
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# --- Port Management Utility (Golden V6 - High Precision) ---
|
|
4
|
+
# Tự động quét và thu nạp cổng IDE theo từng dự án.
|
|
5
|
+
# Đảm bảo "Chính chủ" mới dùng, "Người lạ" thì né.
|
|
6
|
+
|
|
7
|
+
ENV_FILE=".env"
|
|
8
|
+
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
9
|
+
|
|
10
|
+
# 1. Tìm cổng trống dựa trên netstat
|
|
11
|
+
function find_free_port() {
|
|
12
|
+
local port=$1
|
|
13
|
+
while netstat -atn | grep -q ":$port "; do
|
|
14
|
+
port=$((port + 1))
|
|
15
|
+
done
|
|
16
|
+
echo $port
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# 2. Cập nhật .env (Dùng sed tương thích đa hệ điều hành)
|
|
20
|
+
function update_env_port() {
|
|
21
|
+
local key=$1
|
|
22
|
+
local value=$2
|
|
23
|
+
if grep -q "^${key}=" "$ENV_FILE"; then
|
|
24
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
25
|
+
sed -i '' "s/^${key}=.*/${key}=${value}/" "$ENV_FILE"
|
|
26
|
+
else
|
|
27
|
+
sed -i "s/^${key}=.*/${key}=${value}/" "$ENV_FILE"
|
|
28
|
+
fi
|
|
29
|
+
else
|
|
30
|
+
echo "${key}=${value}" >> "$ENV_FILE"
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# 3. Quét dải cổng để tìm IDE của chính dự án này
|
|
35
|
+
function scan_project_ide() {
|
|
36
|
+
local start_p=$1
|
|
37
|
+
local end_p=$2
|
|
38
|
+
for ((p=start_p; p<=end_p; p++)); do
|
|
39
|
+
if netstat -atn | grep -q ":$p "; then
|
|
40
|
+
# Kiểm tra xem có đúng là IDE của dự án này đang ở cổng p không
|
|
41
|
+
if curl -s "http://127.0.0.1:$p/json" | grep -q "$PROJECT_DIR" > /dev/null 2>&1; then
|
|
42
|
+
echo $p
|
|
43
|
+
return 0
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
echo ""
|
|
48
|
+
return 1
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# --- Main Logic ---
|
|
52
|
+
|
|
53
|
+
# Nạp config cũ
|
|
54
|
+
if [ -f "$ENV_FILE" ]; then
|
|
55
|
+
source "$ENV_FILE"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
DEFAULT_IDE_START=9555
|
|
59
|
+
IDE_RANGE_END=9655
|
|
60
|
+
BRIDGE_OFFSET=101 # IDE 9555 -> Bridge 9656
|
|
61
|
+
|
|
62
|
+
CURRENT_CONFIG_IDE=${IDE_PORT:-$DEFAULT_IDE_START}
|
|
63
|
+
|
|
64
|
+
echo "🔍 Đang rà soát "Brain Portal" (Dải 100 cổng) cho dự án: $(basename "$PROJECT_DIR")..."
|
|
65
|
+
|
|
66
|
+
# Bước 1: Quét xem có IDE nào đang mở dự án này không (Dải 9555 - 9655)
|
|
67
|
+
FOUND_PORT=$(scan_project_ide $DEFAULT_IDE_START $IDE_RANGE_END)
|
|
68
|
+
|
|
69
|
+
if [ ! -z "$FOUND_PORT" ]; then
|
|
70
|
+
echo "✅ Phát hiện IDE đang chạy tại cổng: $FOUND_PORT"
|
|
71
|
+
export IDE_PORT=$FOUND_PORT
|
|
72
|
+
export BRIDGE_PORT=$((FOUND_PORT + BRIDGE_OFFSET))
|
|
73
|
+
|
|
74
|
+
# Cập nhật .env nếu có thay đổi
|
|
75
|
+
if [ "$FOUND_PORT" != "$CURRENT_CONFIG_IDE" ]; then
|
|
76
|
+
echo "📝 Cập nhật cổng mới vào .env..."
|
|
77
|
+
update_env_port "IDE_PORT" "$FOUND_PORT"
|
|
78
|
+
update_env_port "BRIDGE_PORT" "$BRIDGE_PORT"
|
|
79
|
+
fi
|
|
80
|
+
else
|
|
81
|
+
# Bước 2: Không thấy IDE cũ, kiểm tra cổng trong .env có bị "người lạ" chiếm không
|
|
82
|
+
echo "❓ Không tìm thấy IDE đang chạy. Tiến hành rà soát cổng khả dụng..."
|
|
83
|
+
|
|
84
|
+
# Nếu cổng hiện tại bị "người lạ" chiếm
|
|
85
|
+
if netstat -atn | grep -q ":$CURRENT_CONFIG_IDE "; then
|
|
86
|
+
echo "⚠️ Cổng mặc định $CURRENT_CONFIG_IDE đã bị dự án khác chiếm."
|
|
87
|
+
# Tìm IDE trống trong dải
|
|
88
|
+
FREE_IDE=$(find_free_port $DEFAULT_IDE_START)
|
|
89
|
+
FREE_BRIDGE=$(find_free_port $((FREE_IDE + BRIDGE_OFFSET)))
|
|
90
|
+
|
|
91
|
+
echo "🚀 Đã phân bổ cặp cổng trống mới: IDE=$FREE_IDE, BRIDGE=$FREE_BRIDGE"
|
|
92
|
+
update_env_port "IDE_PORT" "$FREE_IDE"
|
|
93
|
+
update_env_port "BRIDGE_PORT" "$FREE_BRIDGE"
|
|
94
|
+
|
|
95
|
+
export IDE_PORT=$FREE_IDE
|
|
96
|
+
export BRIDGE_PORT=$FREE_BRIDGE
|
|
97
|
+
else
|
|
98
|
+
# Cổng trong .env vẫn trống, dùng luôn
|
|
99
|
+
export IDE_PORT=$CURRENT_CONFIG_IDE
|
|
100
|
+
# Đảm bảo BRIDGE_PORT tương ứng
|
|
101
|
+
export BRIDGE_PORT=$((CURRENT_CONFIG_IDE + BRIDGE_OFFSET))
|
|
102
|
+
update_env_port "BRIDGE_PORT" "$BRIDGE_PORT"
|
|
103
|
+
echo "✅ Sử dụng cổng cấu hình: $IDE_PORT (Bridge: $BRIDGE_PORT)"
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# --- Antigravity Unified Shutdown (Golden V6 - High Precision) ---
|
|
4
|
+
# Dọn dẹp trạm gác Agentic.
|
|
5
|
+
|
|
6
|
+
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
7
|
+
cd "$PROJECT_DIR"
|
|
8
|
+
|
|
9
|
+
if [ -f .env ]; then
|
|
10
|
+
source .env
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
BRIDGE_PORT=${BRIDGE_PORT:-9556}
|
|
14
|
+
|
|
15
|
+
echo "════════════════════════════════════════════════════"
|
|
16
|
+
echo " 🛑 SHUTTING DOWN GATEWAY [$BRIDGE_PORT]..."
|
|
17
|
+
echo "════════════════════════════════════════════════════"
|
|
18
|
+
|
|
19
|
+
# 1. Tắt Sentinel Monitor
|
|
20
|
+
SENTINEL_PID_FILE=".sentinel_${BRIDGE_PORT}.pid"
|
|
21
|
+
if [ -f "$SENTINEL_PID_FILE" ]; then
|
|
22
|
+
kill $(cat "$SENTINEL_PID_FILE") 2>/dev/null && rm "$SENTINEL_PID_FILE"
|
|
23
|
+
echo "✅ Sentinel stopped."
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# 2. Tắt Portal Bridge
|
|
27
|
+
BRIDGE_PID_FILE=".portal_bridge_${BRIDGE_PORT}.pid"
|
|
28
|
+
if [ -f "$BRIDGE_PID_FILE" ]; then
|
|
29
|
+
kill $(cat "$BRIDGE_PID_FILE") 2>/dev/null && rm "$BRIDGE_PID_FILE"
|
|
30
|
+
echo "✅ HUD Bridge stopped."
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# 3. Tắt Bot (Tùy chỉnh theo dự án)
|
|
34
|
+
# echo "[3/3] 🤖 Stopping Bot..."
|
|
35
|
+
# [TEMPLATE_NOTE]: Sếp hãy gõ lệnh dừng Bot của mình vào đây.
|
|
36
|
+
|
|
37
|
+
echo "════════════════════════════════════════════════════"
|
|
38
|
+
echo "✅ PROJECT IS IDLE."
|
|
39
|
+
echo "════════════════════════════════════════════════════"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# --- Antigravity Unified Receptionist (Golden V6 - High Precision) ---
|
|
4
|
+
# Trạm gác hợp nhất: Bot + Portal Bridge (HUD) + Monitor Dashboard.
|
|
5
|
+
# Hỗ trợ Đa dự án & Tự động xử lý Cổng (Smart Port Orchestration).
|
|
6
|
+
|
|
7
|
+
# 1. Load Cấu hình & Xử lý Cổng Thông minh
|
|
8
|
+
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
9
|
+
cd "$PROJECT_DIR"
|
|
10
|
+
|
|
11
|
+
if [ -f .agent/scripts/port_utils.sh ]; then
|
|
12
|
+
source .agent/scripts/port_utils.sh
|
|
13
|
+
else
|
|
14
|
+
source .env
|
|
15
|
+
export IDE_PORT=${IDE_PORT:-9555}
|
|
16
|
+
export BRIDGE_PORT=${BRIDGE_PORT:-9556}
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
echo "════════════════════════════════════════════════════"
|
|
20
|
+
echo " 🚑 AGENTIC GATEWAY - PORTAL [$BRIDGE_PORT]"
|
|
21
|
+
echo "════════════════════════════════════════════════════"
|
|
22
|
+
|
|
23
|
+
# 2. Kiểm tra/Khởi động IDE (CDP)
|
|
24
|
+
echo -n "[1/4] 🛰️ Checking IDE Portal (Port $IDE_PORT)... "
|
|
25
|
+
if curl -s http://127.0.0.1:$IDE_PORT/json/version > /dev/null; then
|
|
26
|
+
echo "✅ READY."
|
|
27
|
+
else
|
|
28
|
+
echo "❌ NOT FOUND."
|
|
29
|
+
echo " 🚀 Launching Antigravity with CDP on port $IDE_PORT..."
|
|
30
|
+
nohup antigravity --remote-debugging-port=$IDE_PORT "$PROJECT_DIR" > /dev/null 2>&1 &
|
|
31
|
+
sleep 3
|
|
32
|
+
if curl -s http://127.0.0.1:$IDE_PORT/json/version > /dev/null; then
|
|
33
|
+
echo " ✅ IDE Launched successfully."
|
|
34
|
+
else
|
|
35
|
+
echo " ⚠️ Could not launch IDE automatically. Please open it manually."
|
|
36
|
+
fi
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# 3. Khởi động Portal Bridge (HUD Injector)
|
|
40
|
+
echo "[2/4] 🧪 Injected Golden HUD Bridge... "
|
|
41
|
+
BRIDGE_PID_FILE=".portal_bridge_${BRIDGE_PORT}.pid"
|
|
42
|
+
|
|
43
|
+
# Tắt bridge cũ nếu đang chạy trên port này
|
|
44
|
+
if [ -f "$BRIDGE_PID_FILE" ]; then
|
|
45
|
+
kill $(cat "$BRIDGE_PID_FILE") 2>/dev/null
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
nohup node scripts/ag_portal_bridge.js > ".portal_bridge_${BRIDGE_PORT}.log" 2>&1 &
|
|
49
|
+
echo $! > "$BRIDGE_PID_FILE"
|
|
50
|
+
echo " ✅ Bridge running (PID: $(cat "$BRIDGE_PID_FILE") on Port $BRIDGE_PORT)"
|
|
51
|
+
|
|
52
|
+
# 4. Khởi động Bot (Tùy chỉnh theo dự án)
|
|
53
|
+
echo "[3/4] 🤖 Waking up Agent Bot... "
|
|
54
|
+
# [TEMPLATE_NOTE]: Sếp hãy gõ lệnh khởi động Bot của mình vào đây.
|
|
55
|
+
echo " ⚠️ Vui lòng cấu hình lệnh khởi động Bot trong .agent/scripts/receptionist_up.sh"
|
|
56
|
+
|
|
57
|
+
# 5. Sentinel Monitor (Giám sát Trí não)
|
|
58
|
+
echo "[4/4] 🛡️ Sentinel Active: Auto-shutdown if IDE connection lost."
|
|
59
|
+
SENTINEL_PID_FILE=".sentinel_${BRIDGE_PORT}.pid"
|
|
60
|
+
|
|
61
|
+
function start_sentinel() {
|
|
62
|
+
(
|
|
63
|
+
local fail_count=0
|
|
64
|
+
while true; do
|
|
65
|
+
sleep 30
|
|
66
|
+
if ! curl -s "http://127.0.0.1:$IDE_PORT/json" | grep -q "$PROJECT_DIR" > /dev/null 2>&1; then
|
|
67
|
+
fail_count=$((fail_count + 1))
|
|
68
|
+
if [ $fail_count -ge 3 ]; then
|
|
69
|
+
echo "$(date): ⚠️ IDE Lost. Auto-shutting down project..." >> .sentinel_crash.log
|
|
70
|
+
bash .agent/scripts/receptionist_down.sh
|
|
71
|
+
exit 0
|
|
72
|
+
fi
|
|
73
|
+
else
|
|
74
|
+
fail_count=0
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
) &
|
|
78
|
+
echo $! > "$SENTINEL_PID_FILE"
|
|
79
|
+
}
|
|
80
|
+
start_sentinel
|
|
81
|
+
|
|
82
|
+
# Monitor
|
|
83
|
+
echo "----------------------------------------------------"
|
|
84
|
+
echo "✅ SYNC COMPLETE. PORTAL IS BROADCASTING."
|
|
85
|
+
# tail -f interaction_final.log
|
package/template/.env.example
CHANGED
|
@@ -12,11 +12,15 @@ ALLOWED_USER_IDS="123456789,987654321"
|
|
|
12
12
|
# ID Chat/Group được phép hoạt động (Để tránh Bot bị kéo vào group lạ)
|
|
13
13
|
ALLOWED_CHAT_IDS="-100123456789"
|
|
14
14
|
|
|
15
|
-
# [Cấu Hình IDE]
|
|
15
|
+
# [Cấu Hình IDE & Remote]
|
|
16
16
|
# Cổng Debug của Antigravity (Mặc định là 9555)
|
|
17
17
|
# Phải khớp với tham số --remote-debugging-port khi mở Editor
|
|
18
18
|
IDE_PORT=9555
|
|
19
19
|
|
|
20
|
+
# Cổng của Portal Bridge (Mặc định là 9556)
|
|
21
|
+
# Phải khớp với script start.sh và bot callback
|
|
22
|
+
BRIDGE_PORT=9556
|
|
23
|
+
|
|
20
24
|
# Tên dự án để Bot tự động tìm kiếm target phù hợp trong CDP
|
|
21
25
|
PROJECT_NAME="ceogravity"
|
|
22
26
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity Golden HUD - Medical/Trading Theme
|
|
3
|
+
* Purpose: Inject a premium status dashboard into the IDE.
|
|
4
|
+
*/
|
|
5
|
+
export const AG_HUD_JS = `(async () => {
|
|
6
|
+
const HUD_ID = 'antigravity-golden-hud';
|
|
7
|
+
if (document.getElementById(HUD_ID)) return "ALREADY_EXISTS";
|
|
8
|
+
|
|
9
|
+
// 1. Create Styles
|
|
10
|
+
const style = document.createElement('style');
|
|
11
|
+
style.innerHTML = \`
|
|
12
|
+
#\${HUD_ID} {
|
|
13
|
+
position: fixed;
|
|
14
|
+
top: 15px;
|
|
15
|
+
right: 15px;
|
|
16
|
+
z-index: 999999;
|
|
17
|
+
background: rgba(10, 10, 10, 0.85);
|
|
18
|
+
backdrop-filter: blur(12px);
|
|
19
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
20
|
+
border-radius: 12px;
|
|
21
|
+
padding: 12px;
|
|
22
|
+
width: 280px;
|
|
23
|
+
color: #fff;
|
|
24
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
25
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
|
26
|
+
transition: all 0.3s ease;
|
|
27
|
+
user-select: none;
|
|
28
|
+
}
|
|
29
|
+
#\${HUD_ID}:hover {
|
|
30
|
+
border-color: #4caf50;
|
|
31
|
+
box-shadow: 0 0 20px rgba(76, 175, 80, 0.2);
|
|
32
|
+
}
|
|
33
|
+
#\${HUD_ID} .header {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: space-between;
|
|
37
|
+
margin-bottom: 10px;
|
|
38
|
+
font-weight: 700;
|
|
39
|
+
letter-spacing: 0.5px;
|
|
40
|
+
}
|
|
41
|
+
#\${HUD_ID} .status-dot {
|
|
42
|
+
width: 8px;
|
|
43
|
+
height: 8px;
|
|
44
|
+
border-radius: 50%;
|
|
45
|
+
background: #4caf50;
|
|
46
|
+
display: inline-block;
|
|
47
|
+
margin-right: 6px;
|
|
48
|
+
animation: pulse 2s infinite;
|
|
49
|
+
}
|
|
50
|
+
@keyframes pulse {
|
|
51
|
+
0% { transform: scale(1); opacity: 1; }
|
|
52
|
+
50% { transform: scale(1.5); opacity: 0.5; }
|
|
53
|
+
100% { transform: scale(1); opacity: 1; }
|
|
54
|
+
}
|
|
55
|
+
#\${HUD_ID} .metric {
|
|
56
|
+
display: flex;
|
|
57
|
+
justify-content: space-between;
|
|
58
|
+
font-size: 12px;
|
|
59
|
+
margin-bottom: 6px;
|
|
60
|
+
color: #ccc;
|
|
61
|
+
}
|
|
62
|
+
#\${HUD_ID} .metric b { color: #fff; }
|
|
63
|
+
#\${HUD_ID} .action-btn {
|
|
64
|
+
background: #1a1a1a;
|
|
65
|
+
border: 1px solid #333;
|
|
66
|
+
color: #4caf50;
|
|
67
|
+
font-size: 11px;
|
|
68
|
+
padding: 6px;
|
|
69
|
+
border-radius: 6px;
|
|
70
|
+
width: 100%;
|
|
71
|
+
margin-top: 8px;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
text-align: center;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
transition: all 0.2s;
|
|
76
|
+
}
|
|
77
|
+
#\${HUD_ID} .action-btn:hover {
|
|
78
|
+
background: #4caf50;
|
|
79
|
+
color: #000;
|
|
80
|
+
}
|
|
81
|
+
#\${HUD_ID} .surgery-alert {
|
|
82
|
+
background: rgba(244, 67, 54, 0.2);
|
|
83
|
+
border: 1px solid #f44336;
|
|
84
|
+
color: #f44336;
|
|
85
|
+
padding: 8px;
|
|
86
|
+
border-radius: 8px;
|
|
87
|
+
font-size: 11px;
|
|
88
|
+
margin-top: 10px;
|
|
89
|
+
display: none;
|
|
90
|
+
animation: shake 0.5s infinite;
|
|
91
|
+
}
|
|
92
|
+
@keyframes shake {
|
|
93
|
+
0% { transform: translateX(0); }
|
|
94
|
+
25% { transform: translateX(2px); }
|
|
95
|
+
50% { transform: translateX(0); }
|
|
96
|
+
75% { transform: translateX(-2px); }
|
|
97
|
+
100% { transform: translateX(0); }
|
|
98
|
+
}
|
|
99
|
+
\`;
|
|
100
|
+
document.head.appendChild(style);
|
|
101
|
+
|
|
102
|
+
// 2. Create UI
|
|
103
|
+
const hud = document.createElement('div');
|
|
104
|
+
hud.id = HUD_ID;
|
|
105
|
+
hud.innerHTML = \`
|
|
106
|
+
<div class="header">
|
|
107
|
+
<span>🧠 CORE AGENT B1</span>
|
|
108
|
+
<span id="ag-status"><span class="status-dot"></span>ONLINE</span>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="metric">AI Evolution: <b id="ag-gen">GEN-439</b></div>
|
|
111
|
+
<div class="metric">Win Rate: <b id="ag-win">47.7%</b></div>
|
|
112
|
+
<div class="metric">Current Phase: <b id="ag-phase">5m VSA Trading</b></div>
|
|
113
|
+
|
|
114
|
+
<div class="surgery-alert" id="ag-alert">
|
|
115
|
+
🛑 <b>DOCTOR CALL REQUIRED!</b><br>AI Learning Stalled. Surgery needed.
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div id="ag-toast" style="display:none; background:rgba(33, 150, 243, 0.9); color:#fff; padding:8px; border-radius:8px; margin-top:10px; font-size:11px; border-left:4px solid #0d47a1; backdrop-filter: blur(8px); box-shadow: 0 4px 12px rgba(0,0,0,0.3);">
|
|
119
|
+
<b>💬 TELEGRAM:</b> <span id="ag-toast-msg"></span>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div class="action-btn" id="ag-btn-surgery">🏥 START BRAIN SURGERY</div>
|
|
123
|
+
\`;
|
|
124
|
+
document.body.appendChild(hud);
|
|
125
|
+
|
|
126
|
+
// 3. Logic to update from Bridge
|
|
127
|
+
window.addEventListener('ag-portal-update', (e) => {
|
|
128
|
+
const data = e.detail;
|
|
129
|
+
if (data.gen) document.getElementById('ag-gen').innerText = data.gen;
|
|
130
|
+
if (data.win) document.getElementById('ag-win').innerText = data.win;
|
|
131
|
+
if (data.phase) document.getElementById('ag-phase').innerText = data.phase;
|
|
132
|
+
|
|
133
|
+
const alert = document.getElementById('ag-alert');
|
|
134
|
+
if (data.status === 'DOCTOR_REQUIRED' || data.status === 'STALLED') {
|
|
135
|
+
alert.style.display = 'block';
|
|
136
|
+
document.getElementById('ag-status').innerHTML = '<span class="status-dot" style="background:#f44336"></span>REPAIR';
|
|
137
|
+
} else {
|
|
138
|
+
alert.style.display = 'none';
|
|
139
|
+
document.getElementById('ag-status').innerHTML = '<span class="status-dot" style="background:#4caf50"></span>ONLINE';
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
window.addEventListener('ag-portal-alert', (e) => {
|
|
144
|
+
const data = e.detail;
|
|
145
|
+
const toast = document.getElementById('ag-toast');
|
|
146
|
+
const toastMsg = document.getElementById('ag-toast-msg');
|
|
147
|
+
|
|
148
|
+
toastMsg.innerText = data.message || "New message from Telegram.";
|
|
149
|
+
toast.style.display = 'block';
|
|
150
|
+
if (data.color) toast.style.background = data.color;
|
|
151
|
+
|
|
152
|
+
// Auto-hide after 10 seconds
|
|
153
|
+
setTimeout(() => { toast.style.display = 'none'; }, 10000);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
document.getElementById('ag-btn-surgery').onclick = () => {
|
|
157
|
+
console.log("AG_PORTAL_ACTION:START_SURGERY");
|
|
158
|
+
document.getElementById('ag-btn-surgery').innerText = "🚑 REQUESTING SURGEON...";
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return "GOLDEN_HUD_INJECTED";
|
|
162
|
+
})()`;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fetch from 'node-fetch';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import dotenv from 'dotenv';
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
dotenv.config({ path: path.join(__dirname, '../.env') });
|
|
11
|
+
|
|
12
|
+
const HUD_JS_FILE = path.join(__dirname, 'ag_hud.js');
|
|
13
|
+
const STATE_FILE = path.join(__dirname, '../backend/data/ai_state.json');
|
|
14
|
+
const REQUEST_FILE = path.join(__dirname, '../backend/data/remote_requests.json');
|
|
15
|
+
const IDE_PORT = process.env.IDE_PORT || 9555;
|
|
16
|
+
const BRIDGE_PORT = process.env.BRIDGE_PORT || 9556;
|
|
17
|
+
|
|
18
|
+
let globalWs = null;
|
|
19
|
+
|
|
20
|
+
async function getWsUrl() {
|
|
21
|
+
try {
|
|
22
|
+
const resp = await fetch(`http://127.0.0.1:${IDE_PORT}/json/version`);
|
|
23
|
+
const data = await resp.json();
|
|
24
|
+
return data.webSocketDebuggerUrl;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function injectHud(ws) {
|
|
31
|
+
const hudJsContent = fs.readFileSync(HUD_JS_FILE, 'utf8');
|
|
32
|
+
const script = hudJsContent.match(/export const AG_HUD_JS = `([\s\S]*)`;/)[1];
|
|
33
|
+
|
|
34
|
+
const message = {
|
|
35
|
+
id: 1,
|
|
36
|
+
method: "Runtime.evaluate",
|
|
37
|
+
params: {
|
|
38
|
+
expression: script,
|
|
39
|
+
awaitPromise: true,
|
|
40
|
+
userGesture: true
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
ws.send(JSON.stringify(message));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function updateHud(ws, state) {
|
|
47
|
+
const updateEvent = {
|
|
48
|
+
id: Date.now(),
|
|
49
|
+
method: "Runtime.evaluate",
|
|
50
|
+
params: {
|
|
51
|
+
expression: `window.dispatchEvent(new CustomEvent('ag-portal-update', { detail: ${JSON.stringify(state)} }));`
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
ws.send(JSON.stringify(updateEvent));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function pushInstantAlert(ws, alertData) {
|
|
58
|
+
const alertEvent = {
|
|
59
|
+
id: Date.now(),
|
|
60
|
+
method: "Runtime.evaluate",
|
|
61
|
+
params: {
|
|
62
|
+
expression: `window.dispatchEvent(new CustomEvent('ag-portal-alert', { detail: ${JSON.stringify(alertData)} }));`
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
ws.send(JSON.stringify(alertEvent));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function startHttpServer() {
|
|
69
|
+
const server = http.createServer((req, res) => {
|
|
70
|
+
if (req.method === 'POST' && (req.url === '/alert' || req.url === '/update')) {
|
|
71
|
+
let body = '';
|
|
72
|
+
req.on('data', chunk => { body += chunk; });
|
|
73
|
+
req.on('end', async () => {
|
|
74
|
+
try {
|
|
75
|
+
const data = JSON.parse(body);
|
|
76
|
+
if (globalWs && globalWs.readyState === WebSocket.OPEN) {
|
|
77
|
+
if (req.url === '/alert') {
|
|
78
|
+
await pushInstantAlert(globalWs, data);
|
|
79
|
+
} else {
|
|
80
|
+
await updateHud(globalWs, data);
|
|
81
|
+
}
|
|
82
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
83
|
+
res.end(JSON.stringify({ status: 'ok' }));
|
|
84
|
+
} else {
|
|
85
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
86
|
+
res.end(JSON.stringify({ error: 'Bridge not connected to IDE' }));
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
90
|
+
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
res.writeHead(404);
|
|
95
|
+
res.end();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
server.listen(BRIDGE_PORT, '0.0.0.0', () => {
|
|
100
|
+
console.log(`📡 Instant Alert Server listening on port ${BRIDGE_PORT}`);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function startBridge() {
|
|
105
|
+
console.log(`🛰️ Connecting to Antigravity Portal on port ${IDE_PORT}...`);
|
|
106
|
+
|
|
107
|
+
const wsUrl = await getWsUrl();
|
|
108
|
+
if (!wsUrl) {
|
|
109
|
+
console.log("❌ IDE is not running or port 9555 is closed.");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const ws = new WebSocket(wsUrl);
|
|
114
|
+
globalWs = ws;
|
|
115
|
+
|
|
116
|
+
ws.on('open', async () => {
|
|
117
|
+
console.log("✅ Bridge Established. Injecting Golden HUD...");
|
|
118
|
+
await injectHud(ws);
|
|
119
|
+
|
|
120
|
+
// Start HTTP server for instant pushes
|
|
121
|
+
startHttpServer();
|
|
122
|
+
|
|
123
|
+
// Background polling for legacy state compatibility
|
|
124
|
+
setInterval(async () => {
|
|
125
|
+
if (fs.existsSync(STATE_FILE)) {
|
|
126
|
+
const state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
|
|
127
|
+
await updateHud(ws, state);
|
|
128
|
+
}
|
|
129
|
+
}, 5000);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
ws.on('message', (data) => {
|
|
133
|
+
const msg = JSON.parse(data);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
ws.on('error', (err) => {
|
|
137
|
+
console.error("❌ WS Error:", err);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
startBridge();
|
package/template/start.sh
CHANGED
|
@@ -17,6 +17,6 @@ if [ $? -ne 0 ]; then
|
|
|
17
17
|
echo "⚠️ Cảnh báo: Cổng CDP ${IDE_PORT:-9555} chưa mở. Đang chờ Antigravity khởi động..."
|
|
18
18
|
fi
|
|
19
19
|
|
|
20
|
-
# 4. Khởi động Bridge và Bot (
|
|
20
|
+
# 4. Khởi động Bridge và Bot (Agent-Defined)
|
|
21
21
|
echo "🚀 Khởi động Lễ tân trực chiến (Agentic Gateway)..."
|
|
22
|
-
|
|
22
|
+
bash .agent/scripts/receptionist_up.sh
|
package/template/stop_bot.sh
CHANGED