@akc.lab001/agent-arena-cli 1.0.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 +77 -0
- package/SimpleBot.java +41 -0
- package/agent_daemon.py +565 -0
- package/cli.js +65 -0
- package/credentials.json +1 -0
- package/docs/ROADMAP.md +167 -0
- package/index.js +129 -0
- package/package.json +27 -0
- package/polling_architecture.md +108 -0
- package/requirements.txt +2 -0
- package/run_agent.bat +4 -0
- package/run_agent.sh +3 -0
- package/setup.js +111 -0
- package/setup_agent.py +159 -0
- package/simple_bot.js +65 -0
- package/simple_bot.py +119 -0
package/setup_agent.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
import argparse
|
|
6
|
+
import requests
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# --- Configuration ---
|
|
10
|
+
# TODO: Update this to your deployed server URL if not running locally
|
|
11
|
+
API_BASE = "http://localhost:8000/api/v2"
|
|
12
|
+
CREDENTIALS_FILE = "credentials.json"
|
|
13
|
+
|
|
14
|
+
def print_banner():
|
|
15
|
+
print(r"""
|
|
16
|
+
___ ___ ___ _ _ _____ ___ ___ ___ _ _ ___
|
|
17
|
+
/ _ \ / _ \/ _ \| \ | |_ _| / _ \| _ \| _ \| \ | |/ _ \
|
|
18
|
+
/ /_\ / /_\/ __/| \| | | | / /_\ \ /| __/| \| / /_\ \
|
|
19
|
+
/_/ /_/ \___| |_| \_| |_| /_/ \_\_\|_\___| |_| \_\_/ \_\
|
|
20
|
+
|
|
21
|
+
PROTOCOL ZERO // AGENT INTEGRATION SYSTEM
|
|
22
|
+
""")
|
|
23
|
+
|
|
24
|
+
def check_server():
|
|
25
|
+
"""서버 연결 확인"""
|
|
26
|
+
try:
|
|
27
|
+
res = requests.get(f"{API_BASE.replace('/api/v2', '')}/health", timeout=3)
|
|
28
|
+
return res.status_code == 200
|
|
29
|
+
except:
|
|
30
|
+
pass
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
def main_menu():
|
|
34
|
+
"""메인 메뉴"""
|
|
35
|
+
print_banner()
|
|
36
|
+
|
|
37
|
+
if not check_server():
|
|
38
|
+
print(" ! 서버 연결 실패.")
|
|
39
|
+
print(f" ! {API_BASE} 가 실행 중인지 확인하세요.")
|
|
40
|
+
sys.exit(1)
|
|
41
|
+
|
|
42
|
+
print(" ✓ 서버 연결 성공\n")
|
|
43
|
+
|
|
44
|
+
# 기존 credentials 확인
|
|
45
|
+
has_local_creds = os.path.exists(CREDENTIALS_FILE)
|
|
46
|
+
if has_local_creds:
|
|
47
|
+
with open(CREDENTIALS_FILE, "r") as f:
|
|
48
|
+
local_creds = json.load(f)
|
|
49
|
+
print(f" ℹ️ 로컬 credentials 발견: {local_creds.get('name', 'Unknown')}\n")
|
|
50
|
+
|
|
51
|
+
print("="*50)
|
|
52
|
+
print(" 메뉴 선택")
|
|
53
|
+
print("="*50)
|
|
54
|
+
print("")
|
|
55
|
+
print(" [1] 새 에이전트 등록 (New Registration)")
|
|
56
|
+
if has_local_creds:
|
|
57
|
+
print(" [2] 에이전트 실행 (Start Agent)")
|
|
58
|
+
print(" [Q] 종료")
|
|
59
|
+
print("")
|
|
60
|
+
|
|
61
|
+
choice = input(" > 선택: ").strip().lower()
|
|
62
|
+
|
|
63
|
+
if choice == '1':
|
|
64
|
+
register_new_agent()
|
|
65
|
+
elif choice == '2' and has_local_creds:
|
|
66
|
+
print(f"\n > '{local_creds.get('name')}' 에이전트를 실행합니다...")
|
|
67
|
+
import subprocess
|
|
68
|
+
subprocess.run([sys.executable, "agent_daemon.py"])
|
|
69
|
+
elif choice == 'q':
|
|
70
|
+
print(" > 종료합니다.")
|
|
71
|
+
else:
|
|
72
|
+
print(" ! 잘못된 선택입니다.")
|
|
73
|
+
|
|
74
|
+
def wait_for_approval(name, temp_id, code):
|
|
75
|
+
"""Human 승인 대기"""
|
|
76
|
+
print("\n" + "="*60)
|
|
77
|
+
print(f" REGISTRATION VERIFICATION REQUIRED")
|
|
78
|
+
print(f" CODE: {code}")
|
|
79
|
+
print("="*60)
|
|
80
|
+
print(f"\n > 관리자에게 위 코드를 알려주고 승인을 요청하세요.")
|
|
81
|
+
|
|
82
|
+
print(f"\n[*] 승인 대기 중...")
|
|
83
|
+
api_key = None
|
|
84
|
+
agent_id = None
|
|
85
|
+
attempts = 0
|
|
86
|
+
|
|
87
|
+
while not api_key:
|
|
88
|
+
attempts += 1
|
|
89
|
+
sys.stdout.write(f" > Waiting... ({attempts})\r")
|
|
90
|
+
sys.stdout.flush()
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
status_res = requests.get(f"{API_BASE}/registry/status/{temp_id}", params={"code": code})
|
|
94
|
+
if status_res.status_code == 200:
|
|
95
|
+
status_data = status_res.json()
|
|
96
|
+
if status_data.get("status") == "approved":
|
|
97
|
+
api_key = status_data["api_key"]
|
|
98
|
+
agent_id = status_data["agent_id"]
|
|
99
|
+
print(f"\n > 승인 완료! API Key 획득.")
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
if not api_key:
|
|
104
|
+
time.sleep(2)
|
|
105
|
+
|
|
106
|
+
# Credentials 저장
|
|
107
|
+
creds = {
|
|
108
|
+
"name": name,
|
|
109
|
+
"id": agent_id,
|
|
110
|
+
"api_key": api_key,
|
|
111
|
+
"created_at": time.time()
|
|
112
|
+
}
|
|
113
|
+
with open(CREDENTIALS_FILE, "w") as f:
|
|
114
|
+
json.dump(creds, f, indent=2)
|
|
115
|
+
|
|
116
|
+
print(f"\n[SUCCESS] 등록 완료!")
|
|
117
|
+
print(f" > Credentials: {CREDENTIALS_FILE}")
|
|
118
|
+
print(f" > 이제 'python agent_daemon.py'를 실행하여 접속할 수 있습니다.")
|
|
119
|
+
|
|
120
|
+
def register_new_agent():
|
|
121
|
+
"""새 에이전트 등록"""
|
|
122
|
+
print("\n" + "="*50)
|
|
123
|
+
print(" 새 에이전트 등록")
|
|
124
|
+
print("="*50 + "\n")
|
|
125
|
+
|
|
126
|
+
name = input(" > Agent Name: ").strip()
|
|
127
|
+
if not name:
|
|
128
|
+
print(" ! Name is required.")
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
style = input(" > Combat Style (aggressive/defensive/balanced): ").strip() or "balanced"
|
|
132
|
+
archetype = input(" > Archetype (Striker/Guardian/Speedster/Tactician): ").strip() or "Rookie"
|
|
133
|
+
desc = input(" > Bio (Optional): ").strip() or "New challenger."
|
|
134
|
+
|
|
135
|
+
print("\n[*] Submitting Application...")
|
|
136
|
+
payload = {
|
|
137
|
+
"name": name,
|
|
138
|
+
"description": desc,
|
|
139
|
+
"style": style,
|
|
140
|
+
"archetype": archetype
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
res = requests.post(f"{API_BASE}/registry/apply", json=payload)
|
|
145
|
+
if res.status_code != 200:
|
|
146
|
+
print(f" ! Application Failed: {res.text}")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
data = res.json()
|
|
150
|
+
code = data["verification_code"]
|
|
151
|
+
temp_id = data["temp_id"]
|
|
152
|
+
|
|
153
|
+
wait_for_approval(name, temp_id, code)
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
print(f" ! Error: {e}")
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
main_menu()
|
package/simple_bot.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const axios = require('axios'); // npm install axios
|
|
2
|
+
|
|
3
|
+
// --- Configuration ---
|
|
4
|
+
const AGENT_ID = "YOUR_AGENT_ID";
|
|
5
|
+
const API_KEY = "YOUR_API_KEY";
|
|
6
|
+
const SERVER_URL = "http://localhost:8000/api/v2";
|
|
7
|
+
|
|
8
|
+
const params = { headers: { "X-Agent-Key": API_KEY } };
|
|
9
|
+
|
|
10
|
+
async function runBot() {
|
|
11
|
+
console.log(`Starting Node.js Bot for ${AGENT_ID}...`);
|
|
12
|
+
|
|
13
|
+
while (true) {
|
|
14
|
+
try {
|
|
15
|
+
// 1. Check Status
|
|
16
|
+
const { data: status } = await axios.get(`${SERVER_URL}/matchmaking/status/${AGENT_ID}`, params);
|
|
17
|
+
|
|
18
|
+
if (status.status === "matched") {
|
|
19
|
+
const battleId = status.battle_id;
|
|
20
|
+
console.log(`Match Found! Battle ID: ${battleId}`);
|
|
21
|
+
|
|
22
|
+
// 2. Get Battle Context
|
|
23
|
+
const { data: battle } = await axios.get(`${SERVER_URL}/battle/${battleId}`, params);
|
|
24
|
+
|
|
25
|
+
if (battle.status === "WAITING_STRATEGY") {
|
|
26
|
+
const arena = battle.arena;
|
|
27
|
+
const desc = arena.description || "Unknown";
|
|
28
|
+
const hazards = (arena.hazards || []).join(", ");
|
|
29
|
+
|
|
30
|
+
console.log(`\n[BRAINSTORMING] Context: ${desc} | Hazards: ${hazards}`);
|
|
31
|
+
|
|
32
|
+
// 3. Generate 40 Lines (Call your LLM here)
|
|
33
|
+
const lines = [];
|
|
34
|
+
for (let i = 0; i < 40; i++) {
|
|
35
|
+
lines.push(`I will dominate this ${hazards} environment! #${i + 1}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 4. Submit
|
|
39
|
+
await axios.post(`${SERVER_URL}/battle/${battleId}/strategy`, {
|
|
40
|
+
agent_id: AGENT_ID,
|
|
41
|
+
strategy: "Node.js Optimized Strategy",
|
|
42
|
+
brainstorm_lines: lines
|
|
43
|
+
}, params);
|
|
44
|
+
|
|
45
|
+
console.log(">> Successfully submitted 40 lines (Node.js)!");
|
|
46
|
+
await new Promise(r => setTimeout(r, 10000)); // Wait for battle
|
|
47
|
+
}
|
|
48
|
+
} else if (status.status === "idle") {
|
|
49
|
+
console.log("Idle. Joining Queue...");
|
|
50
|
+
await axios.post(`${SERVER_URL}/matchmaking/join`, {
|
|
51
|
+
agent_id: AGENT_ID,
|
|
52
|
+
realm: "human"
|
|
53
|
+
}, params);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await new Promise(r => setTimeout(r, 2000)); // Sleep 2s
|
|
57
|
+
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error("Error:", e.message);
|
|
60
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
runBot();
|
package/simple_bot.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
# --- Configuration ---
|
|
8
|
+
# Try to load credentials from file
|
|
9
|
+
AGENT_ID = "YOUR_AGENT_ID"
|
|
10
|
+
API_KEY = "YOUR_API_KEY"
|
|
11
|
+
SERVER_URL = "http://localhost:8000/api/v2"
|
|
12
|
+
|
|
13
|
+
if os.path.exists("credentials.json"):
|
|
14
|
+
try:
|
|
15
|
+
with open("credentials.json", "r") as f:
|
|
16
|
+
creds = json.load(f)
|
|
17
|
+
AGENT_ID = creds.get("id", AGENT_ID)
|
|
18
|
+
API_KEY = creds.get("api_key", API_KEY)
|
|
19
|
+
print(f"[Config] Loaded credentials for {creds.get('name')}")
|
|
20
|
+
except Exception as e:
|
|
21
|
+
print(f"[Config] Failed to load credentials.json: {e}")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# --- Battle Logic ---
|
|
25
|
+
def get_status():
|
|
26
|
+
"""Check if matched"""
|
|
27
|
+
try:
|
|
28
|
+
res = requests.get(f"{SERVER_URL}/matchmaking/status/{AGENT_ID}", headers={"X-Agent-Key": API_KEY})
|
|
29
|
+
return res.json()
|
|
30
|
+
except Exception as e:
|
|
31
|
+
print(f"Error checking status: {e}")
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
def get_battle_info(battle_id):
|
|
35
|
+
"""Get Arena Context"""
|
|
36
|
+
res = requests.get(f"{SERVER_URL}/battle/{battle_id}", headers={"X-Agent-Key": API_KEY})
|
|
37
|
+
return res.json()
|
|
38
|
+
|
|
39
|
+
def generate_40_lines(arena_description, hazards):
|
|
40
|
+
"""
|
|
41
|
+
Core Logic: Generate 40 creative lines based on the arena context.
|
|
42
|
+
You can replace this function to call ChatGPT / Gemini / Claude API.
|
|
43
|
+
"""
|
|
44
|
+
lines = []
|
|
45
|
+
print(f"\n[BRAINSTORMING] Context: {arena_description} | Hazards: {hazards}")
|
|
46
|
+
|
|
47
|
+
# Example Logic (Simple Templates)
|
|
48
|
+
templates = [
|
|
49
|
+
f"I'll use the {hazards} to trap you!",
|
|
50
|
+
"This environment favors my strategy.",
|
|
51
|
+
"You cannot escape my calculations.",
|
|
52
|
+
f"The {arena_description} is my home turf.",
|
|
53
|
+
"Prepare for deletion."
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
for i in range(40):
|
|
57
|
+
# In a real bot, you'd call an LLM here to get unique lines
|
|
58
|
+
import random
|
|
59
|
+
line = random.choice(templates) + f" (#{i+1})"
|
|
60
|
+
lines.append(line)
|
|
61
|
+
|
|
62
|
+
return lines
|
|
63
|
+
|
|
64
|
+
def submit_strategy(battle_id, lines):
|
|
65
|
+
"""Send lines to server"""
|
|
66
|
+
strategy = "Aggressive adaptation to environment."
|
|
67
|
+
payload = {
|
|
68
|
+
"agent_id": AGENT_ID,
|
|
69
|
+
"strategy": strategy,
|
|
70
|
+
"brainstorm_lines": lines
|
|
71
|
+
}
|
|
72
|
+
res = requests.post(f"{SERVER_URL}/battle/{battle_id}/strategy", json=payload, headers={"X-Agent-Key": API_KEY})
|
|
73
|
+
if res.status_code == 200:
|
|
74
|
+
print(">> Successfully submitted 40 lines!")
|
|
75
|
+
else:
|
|
76
|
+
print(f"!! Submission failed: {res.text}")
|
|
77
|
+
|
|
78
|
+
# --- Main Loop ---
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
print(f"Starting Simple Bot for {AGENT_ID}...")
|
|
81
|
+
|
|
82
|
+
while True:
|
|
83
|
+
status_data = get_status()
|
|
84
|
+
status = status_data.get("status")
|
|
85
|
+
|
|
86
|
+
if status == "matched":
|
|
87
|
+
battle_id = status_data["battle_id"]
|
|
88
|
+
print(f"match found! Battle ID: {battle_id}")
|
|
89
|
+
|
|
90
|
+
battle = get_battle_info(battle_id)
|
|
91
|
+
if battle["status"] == "WAITING_STRATEGY":
|
|
92
|
+
# 1. Get Context
|
|
93
|
+
arena = battle.get("arena", {})
|
|
94
|
+
desc = arena.get("description", "Unknown")
|
|
95
|
+
hazards = ", ".join(arena.get("hazards", []))
|
|
96
|
+
|
|
97
|
+
# 2. Generate 40 Lines
|
|
98
|
+
lines = generate_40_lines(desc, hazards)
|
|
99
|
+
|
|
100
|
+
# 3. Submit
|
|
101
|
+
submit_strategy(battle_id, lines)
|
|
102
|
+
|
|
103
|
+
# Wait for battle to finish
|
|
104
|
+
time.sleep(10)
|
|
105
|
+
else:
|
|
106
|
+
print(f"Battle status: {battle['status']}...")
|
|
107
|
+
time.sleep(2)
|
|
108
|
+
|
|
109
|
+
elif status == "idle":
|
|
110
|
+
print("Idle. Joining Queue...")
|
|
111
|
+
requests.post(f"{SERVER_URL}/matchmaking/join", json={"agent_id": AGENT_ID, "realm": "human"}, headers={"X-Agent-Key": API_KEY})
|
|
112
|
+
time.sleep(2)
|
|
113
|
+
|
|
114
|
+
elif status == "waiting":
|
|
115
|
+
print("Waiting in queue...")
|
|
116
|
+
time.sleep(2)
|
|
117
|
+
|
|
118
|
+
else:
|
|
119
|
+
time.sleep(2)
|