Nasaawakening commited on
Commit
cfbe38d
Β·
verified Β·
1 Parent(s): e7996b9

Add Cross-Platform Zoder Terminal Agent (Termux/Linux/Mac/Win)

Browse files
Files changed (1) hide show
  1. zoder_agent.py +242 -0
zoder_agent.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ ZODER TERMINAL AGENT WRAPPER (CROSS-PLATFORM)
4
+ Supports: Termux (Android), Linux, macOS, Windows (WSL/CMD)
5
+ Created by: Komandan Nasa - Bakso Bangi Pak Romdani
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ import re
11
+ import json
12
+ import platform
13
+ import subprocess
14
+
15
+ # Try to import requests, auto-install if missing
16
+ try:
17
+ import requests
18
+ except ImportError:
19
+ print("⏳ Installing required library 'requests'...")
20
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"])
21
+ import requests
22
+
23
+ # Try to import rich for UI, fallback to plain text if missing/fails
24
+ USE_RICH = False
25
+ try:
26
+ from rich.console import Console
27
+ from rich.panel import Panel
28
+ from rich.prompt import Prompt
29
+ USE_RICH = True
30
+ except ImportError:
31
+ try:
32
+ print("⏳ Installing optional UI library 'rich'...")
33
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "rich"])
34
+ from rich.console import Console
35
+ from rich.panel import Panel
36
+ from rich.prompt import Prompt
37
+ USE_RICH = True
38
+ except:
39
+ USE_RICH = False
40
+
41
+ # ============================================================
42
+ # CONFIGURATION
43
+ # ============================================================
44
+ OLLAMA_HOST = os.environ.get("ZODER_OLLAMA_HOST", "http://localhost:11434")
45
+ MODEL_NAME = os.environ.get("ZODER_MODEL_NAME", "zoder")
46
+ HISTORY_FILE = os.path.expanduser("~/.zoder_history.json")
47
+
48
+ if USE_RICH:
49
+ console = Console()
50
+
51
+
52
+ def print_ui(text, style="default"):
53
+ """Cross-platform print function"""
54
+ if USE_RICH:
55
+ if style == "banner":
56
+ console.print(Panel.fit(text, border_style="cyan"))
57
+ elif style == "user":
58
+ console.print(f"[bold cyan]πŸ‘€ You:[/bold cyan] {text}")
59
+ elif style == "zoder":
60
+ console.print(f"[bold magenta]πŸ€– Zoder:[/bold magenta] ", end="")
61
+ elif style == "success":
62
+ console.print(f"[bold green] βœ… {text}[/bold green]")
63
+ elif style == "error":
64
+ console.print(f"[bold red] ❌ {text}[/bold red]")
65
+ elif style == "info":
66
+ console.print(f"[dim]{text}[/dim]")
67
+ else:
68
+ console.print(text)
69
+ else:
70
+ # Plain text fallback for basic terminals
71
+ if style == "banner":
72
+ print("\n" + "="*50)
73
+ print(text)
74
+ print("="*50 + "\n")
75
+ elif style == "user":
76
+ print(f"\nπŸ‘€ You: {text}")
77
+ elif style == "zoder":
78
+ print(f"\nπŸ€– Zoder: ", end="")
79
+ elif style == "success":
80
+ print(f"βœ… {text}")
81
+ elif style == "error":
82
+ print(f"❌ {text}")
83
+ else:
84
+ print(text)
85
+
86
+
87
+ def banner():
88
+ os_info = f"{platform.system()} {platform.release()}"
89
+ if "ANDROID_ROOT" in os.environ or "TERMUX_VERSION" in os.environ:
90
+ os_info = "Android Termux"
91
+
92
+ banner_text = (
93
+ "[bold cyan]πŸ€– ZODER OS AGENT v1.0[/bold cyan]\n"
94
+ "[dim]Created by Komandan Nasa - Bakso Bangi Pak Romdani[/dim]\n"
95
+ f"[dim]Model: Zoder1.0-1B | System: {os_info}[/dim]"
96
+ ) if USE_RICH else (
97
+ f"πŸ€– ZODER OS AGENT v1.0\n"
98
+ f"Created by Komandan Nasa - Bakso Bangi Pak Romdani\n"
99
+ f"Model: Zoder1.0-1B | System: {os_info}"
100
+ )
101
+ print_ui(banner_text, "banner")
102
+
103
+
104
+ def clean_markdown(text):
105
+ """Remove all markdown and HTML formatting from AI response"""
106
+ text = re.sub(r'\*\*(.*?)\*\*', r'\1', text)
107
+ text = re.sub(r'\*(.*?)\*', r'\1', text)
108
+ text = re.sub(r'__(.*?)__', r'\1', text)
109
+ text = re.sub(r'_(.*?)_', r'\1', text)
110
+ text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE)
111
+ text = re.sub(r'```[\s\S]*?```', '', text)
112
+ text = re.sub(r'`(.*?)`', r'\1', text)
113
+ text = re.sub(r'<[^>]+>', '', text)
114
+ return text.strip()
115
+
116
+
117
+ def extract_and_create_files(text):
118
+ """Detect [CREATE_FILE] tags and write files to disk"""
119
+ pattern = r'\[CREATE_FILE:\s*(.*?)\]\s*([\s\S]*?)\[/CREATE_FILE\]'
120
+ matches = re.findall(pattern, text)
121
+
122
+ clean_text = re.sub(pattern, '', text).strip()
123
+
124
+ for filename, content in matches:
125
+ filename = filename.strip()
126
+ filepath = os.path.join(os.getcwd(), filename)
127
+
128
+ try:
129
+ dir_name = os.path.dirname(filepath)
130
+ if dir_name:
131
+ os.makedirs(dir_name, exist_ok=True)
132
+
133
+ with open(filepath, 'w', encoding='utf-8') as f:
134
+ f.write(content.strip())
135
+
136
+ print_ui(f"File created: {filepath}", "success")
137
+ except Exception as e:
138
+ print_ui(f"Failed to create file {filename}: {e}", "error")
139
+
140
+ return clean_text
141
+
142
+
143
+ def load_history():
144
+ if os.path.exists(HISTORY_FILE):
145
+ try:
146
+ with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
147
+ return json.load(f)
148
+ except:
149
+ return []
150
+ return []
151
+
152
+
153
+ def save_history(history):
154
+ try:
155
+ with open(HISTORY_FILE, 'w', encoding='utf-8') as f:
156
+ json.dump(history[-20:], f, ensure_ascii=False, indent=2)
157
+ except:
158
+ pass
159
+
160
+
161
+ def chat_with_ollama(messages):
162
+ try:
163
+ response = requests.post(
164
+ f"{OLLAMA_HOST}/api/chat",
165
+ json={
166
+ "model": MODEL_NAME,
167
+ "messages": messages,
168
+ "stream": True
169
+ },
170
+ stream=True,
171
+ timeout=300
172
+ )
173
+ response.raise_for_status()
174
+ return response
175
+ except requests.exceptions.ConnectionError:
176
+ print_ui(f"Cannot connect to Ollama server at {OLLAMA_HOST}!", "error")
177
+ print_ui("Make sure Ollama is running: ollama serve", "info")
178
+ sys.exit(1)
179
+
180
+
181
+ def run_agent():
182
+ banner()
183
+ history = load_history()
184
+ print_ui("Type '/clear' to reset history, '/exit' to quit.", "info")
185
+
186
+ while True:
187
+ try:
188
+ if USE_RICH:
189
+ from rich.prompt import Prompt
190
+ user_input = Prompt.ask("[bold cyan]πŸ‘€ You[/bold cyan]")
191
+ else:
192
+ user_input = input("\nπŸ‘€ You: ")
193
+
194
+ if user_input.lower() == '/exit':
195
+ print_ui("Sampai jumpa, Komandan Nasa!", "info")
196
+ break
197
+
198
+ if user_input.lower() == '/clear':
199
+ history = []
200
+ save_history(history)
201
+ print_ui("History cleared.", "success")
202
+ continue
203
+
204
+ if not user_input.strip():
205
+ continue
206
+
207
+ history.append({"role": "user", "content": user_input})
208
+ print_ui("", "zoder")
209
+
210
+ full_response = ""
211
+ response = chat_with_ollama(history)
212
+
213
+ for line in response.iter_lines():
214
+ if line:
215
+ try:
216
+ data = json.loads(line.decode('utf-8'))
217
+ if "message" in data and "content" in data["message"]:
218
+ token = data["message"]["content"]
219
+ full_response += token
220
+ sys.stdout.write(token)
221
+ sys.stdout.flush()
222
+ except json.JSONDecodeError:
223
+ continue
224
+
225
+ print()
226
+
227
+ processed_response = clean_markdown(full_response)
228
+ final_response = extract_and_create_files(processed_response)
229
+
230
+ history.append({"role": "assistant", "content": final_response})
231
+ save_history(history)
232
+ print()
233
+
234
+ except KeyboardInterrupt:
235
+ print_ui("\nSampai jumpa, Komandan Nasa!", "info")
236
+ break
237
+ except Exception as e:
238
+ print_ui(f"Agent Error: {e}", "error")
239
+
240
+
241
+ if __name__ == "__main__":
242
+ run_agent()