AIencoder commited on
Commit
4952e75
·
verified ·
1 Parent(s): 49b6de2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +15 -52
app.py CHANGED
@@ -93,7 +93,6 @@ class PTYTerminal:
93
  # Child process — become the shell
94
  os.close(pid)
95
  os.setsid()
96
- # Set slave as controlling terminal
97
  slave_fd = fd
98
  fcntl.ioctl(slave_fd, termios.TIOCSCTTY, 0)
99
  os.dup2(slave_fd, 0)
@@ -102,21 +101,18 @@ class PTYTerminal:
102
  if slave_fd > 2:
103
  os.close(slave_fd)
104
  env = os.environ.copy()
105
- env["TERM"] = "dumb" # Prevent color codes / fancy prompts
106
- env["PS1"] = "$ " # Simple prompt
107
  env["DEBIAN_FRONTEND"] = "noninteractive"
108
  os.execvpe("/bin/bash", ["/bin/bash", "--norc", "--noprofile", "-i"], env)
109
  else:
110
  # Parent process
111
  os.close(fd)
112
  self.master_fd = pid
113
- # Set non-blocking reads
114
  flags = fcntl.fcntl(self.master_fd, fcntl.F_GETFL)
115
  fcntl.fcntl(self.master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
116
- # Set terminal size (cols x rows)
117
  winsize = struct.pack("HHHH", 40, 120, 0, 0)
118
  fcntl.ioctl(self.master_fd, termios.TIOCSWINSZ, winsize)
119
- # Drain initial bash output
120
  time.sleep(0.3)
121
  self._read_output()
122
  except Exception as e:
@@ -139,7 +135,7 @@ class PTYTerminal:
139
  chunk = os.read(self.master_fd, 4096)
140
  if chunk:
141
  output.append(chunk.decode("utf-8", errors="replace"))
142
- deadline = time.time() + 0.15 # Extend deadline while data flows
143
  else:
144
  break
145
  else:
@@ -152,7 +148,6 @@ class PTYTerminal:
152
  def _clean_output(self, text: str) -> str:
153
  """Strip ANSI escape codes and control characters."""
154
  cleaned = self.STRIP_ANSI.sub("", text)
155
- # Remove null bytes and other control chars (keep newlines/tabs)
156
  cleaned = "".join(ch for ch in cleaned if ch == "\n" or ch == "\t" or (ord(ch) >= 32))
157
  return cleaned
158
 
@@ -185,53 +180,46 @@ class PTYTerminal:
185
 
186
  self._append_line(f"$ {cmd}")
187
 
188
- # Write the command to bash
189
  try:
190
  os.write(self.master_fd, (cmd + "\n").encode())
191
  except OSError as e:
192
  self._append_line(f"[Write Error] {e}")
193
  return self.get_log()
194
 
195
- # Determine how long to wait based on command type
196
  parts = cmd.split()
197
  base = parts[0].lower() if parts else ""
198
  long_commands = ["pip", "pip3", "npm", "npx", "apt-get", "apt", "git", "wget",
199
  "curl", "make", "cmake", "cargo", "yarn", "conda"]
200
 
201
  if base in long_commands:
202
- wait_time = 180 # 3 minutes for installs
203
  elif base in ("python", "python3", "node"):
204
  wait_time = 60
205
  else:
206
  wait_time = 15
207
 
208
- # Read output with smart timeout — stop early when command finishes
209
  all_output = []
210
  start = time.time()
211
  idle_count = 0
212
-
213
  while time.time() - start < wait_time:
214
  chunk = self._read_output(0.3)
215
  if chunk:
216
  idle_count = 0
217
  all_output.append(chunk)
218
- # Check if we see our prompt back (command finished)
219
  combined = "".join(all_output)
220
  if combined.rstrip().endswith("$"):
221
  break
222
  else:
223
  idle_count += 1
224
- # For short commands, bail after a brief idle
225
  if base not in long_commands and idle_count >= 3:
226
  break
227
- # For long commands, bail after longer idle
228
  if base in long_commands and idle_count >= 10:
229
  break
230
 
231
  raw = "".join(all_output)
232
  cleaned = self._clean_output(raw)
233
 
234
- # Remove the echoed command from output (bash echoes what you type)
235
  lines = cleaned.split("\n")
236
  filtered = []
237
  skip_echo = True
@@ -240,7 +228,6 @@ class PTYTerminal:
240
  if skip_echo and stripped == cmd.strip():
241
  skip_echo = False
242
  continue
243
- # Skip bare prompt lines
244
  if stripped == "$" or stripped == "$ ":
245
  continue
246
  filtered.append(line)
@@ -252,11 +239,10 @@ class PTYTerminal:
252
  return self.get_log()
253
 
254
  def run_editor_code(self, code: str) -> str:
255
- """Execute code from the editor by writing it to a temp file and running it."""
256
  with self.lock:
257
  self._append_line("$ python [editor]")
258
-
259
- # Write code to a temp file and run it through the PTY
260
  tmp_path = "/tmp/_axon_editor_run.py"
261
  try:
262
  with open(tmp_path, "w") as f:
@@ -266,20 +252,17 @@ class PTYTerminal:
266
  self._append_line(f"[Error] Could not write temp file: {e}")
267
  return self.get_log()
268
 
269
- # Run through the shell (but bypass the echo/prompt logic — use subprocess for cleaner output)
270
  try:
271
  result = subprocess.run(
272
  [sys.executable, tmp_path],
273
  capture_output=True, text=True, timeout=30
274
  )
275
- if result.stdout.strip():
276
- with self.lock:
277
  self._append_line(result.stdout.rstrip())
278
- if result.stderr.strip():
279
- with self.lock:
280
  self._append_line(result.stderr.rstrip())
281
- if not result.stdout.strip() and not result.stderr.strip():
282
- with self.lock:
283
  self._append_line("(No output)")
284
  except subprocess.TimeoutExpired:
285
  with self.lock:
@@ -430,7 +413,6 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
430
  with gr.Tabs() as main_tabs:
431
  with gr.Tab("Editor", id="editor-tab"):
432
  with gr.Row(equal_height=True):
433
- # Sidebar
434
  with gr.Column(scale=1, min_width=200):
435
  gr.Markdown("### 📁 Explorer")
436
  file_list = gr.Dropdown(
@@ -447,7 +429,6 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
447
  placeholder="script.py", label="New File", container=False
448
  )
449
 
450
- # Editor
451
  with gr.Column(scale=4):
452
  editor = gr.Code(
453
  value=fs.get_current_file_content(),
@@ -491,7 +472,7 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
491
 
492
  with gr.Tab("AI Chat", id="chat-tab"):
493
  chat_history = gr.Chatbot(
494
- label="Axon AI", height=300, type="messages"
495
  )
496
  with gr.Row():
497
  chat_input = gr.Textbox(
@@ -525,7 +506,7 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
525
  diff_modified_state = gr.State("")
526
  diff_mode_state = gr.State(False)
527
 
528
- # --- Handler Functions ---
529
  def update_file(content):
530
  fs.save_file(content)
531
  return fs.get_current_file_content()
@@ -586,7 +567,6 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
586
  file_list.change(load_file, file_list, [editor, current_file_state])
587
  new_file_btn.click(create_file, new_file_name, [file_list, editor])
588
 
589
- # Terminal
590
  terminal_input.submit(
591
  terminal_command, terminal_input, [terminal_output, terminal_input]
592
  )
@@ -595,11 +575,9 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
595
  )
596
  clear_btn.click(clear_terminal_fn, None, terminal_output)
597
 
598
- # Run button
599
  run_btn.click(run_editor_wrapper, editor, terminal_output)
600
  complete_btn.click(complete_wrapper, editor, editor)
601
 
602
- # AI features → Diff tab
603
  explain_btn.click(
604
  explain_wrapper,
605
  editor,
@@ -615,30 +593,15 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
615
  chat_input.submit(
616
  generate_wrapper,
617
  [chat_input, chat_history],
618
- [
619
- diff_view,
620
- diff_original_state,
621
- diff_modified_state,
622
- diff_mode_state,
623
- chat_history,
624
- chat_input,
625
- ],
626
  ).then(lambda: gr.Tabs(selected="diff-tab"), None, main_tabs)
627
 
628
  send_btn.click(
629
  generate_wrapper,
630
  [chat_input, chat_history],
631
- [
632
- diff_view,
633
- diff_original_state,
634
- diff_modified_state,
635
- diff_mode_state,
636
- chat_history,
637
- chat_input,
638
- ],
639
  ).then(lambda: gr.Tabs(selected="diff-tab"), None, main_tabs)
640
 
641
- # Diff actions
642
  apply_btn.click(
643
  lambda mod: (mod, False), diff_modified_state, [editor, diff_mode_state]
644
  ).then(lambda: gr.Tabs(selected="editor-tab"), None, main_tabs)
 
93
  # Child process — become the shell
94
  os.close(pid)
95
  os.setsid()
 
96
  slave_fd = fd
97
  fcntl.ioctl(slave_fd, termios.TIOCSCTTY, 0)
98
  os.dup2(slave_fd, 0)
 
101
  if slave_fd > 2:
102
  os.close(slave_fd)
103
  env = os.environ.copy()
104
+ env["TERM"] = "dumb"
105
+ env["PS1"] = "$ "
106
  env["DEBIAN_FRONTEND"] = "noninteractive"
107
  os.execvpe("/bin/bash", ["/bin/bash", "--norc", "--noprofile", "-i"], env)
108
  else:
109
  # Parent process
110
  os.close(fd)
111
  self.master_fd = pid
 
112
  flags = fcntl.fcntl(self.master_fd, fcntl.F_GETFL)
113
  fcntl.fcntl(self.master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
 
114
  winsize = struct.pack("HHHH", 40, 120, 0, 0)
115
  fcntl.ioctl(self.master_fd, termios.TIOCSWINSZ, winsize)
 
116
  time.sleep(0.3)
117
  self._read_output()
118
  except Exception as e:
 
135
  chunk = os.read(self.master_fd, 4096)
136
  if chunk:
137
  output.append(chunk.decode("utf-8", errors="replace"))
138
+ deadline = time.time() + 0.15
139
  else:
140
  break
141
  else:
 
148
  def _clean_output(self, text: str) -> str:
149
  """Strip ANSI escape codes and control characters."""
150
  cleaned = self.STRIP_ANSI.sub("", text)
 
151
  cleaned = "".join(ch for ch in cleaned if ch == "\n" or ch == "\t" or (ord(ch) >= 32))
152
  return cleaned
153
 
 
180
 
181
  self._append_line(f"$ {cmd}")
182
 
 
183
  try:
184
  os.write(self.master_fd, (cmd + "\n").encode())
185
  except OSError as e:
186
  self._append_line(f"[Write Error] {e}")
187
  return self.get_log()
188
 
 
189
  parts = cmd.split()
190
  base = parts[0].lower() if parts else ""
191
  long_commands = ["pip", "pip3", "npm", "npx", "apt-get", "apt", "git", "wget",
192
  "curl", "make", "cmake", "cargo", "yarn", "conda"]
193
 
194
  if base in long_commands:
195
+ wait_time = 180
196
  elif base in ("python", "python3", "node"):
197
  wait_time = 60
198
  else:
199
  wait_time = 15
200
 
 
201
  all_output = []
202
  start = time.time()
203
  idle_count = 0
204
+
205
  while time.time() - start < wait_time:
206
  chunk = self._read_output(0.3)
207
  if chunk:
208
  idle_count = 0
209
  all_output.append(chunk)
 
210
  combined = "".join(all_output)
211
  if combined.rstrip().endswith("$"):
212
  break
213
  else:
214
  idle_count += 1
 
215
  if base not in long_commands and idle_count >= 3:
216
  break
 
217
  if base in long_commands and idle_count >= 10:
218
  break
219
 
220
  raw = "".join(all_output)
221
  cleaned = self._clean_output(raw)
222
 
 
223
  lines = cleaned.split("\n")
224
  filtered = []
225
  skip_echo = True
 
228
  if skip_echo and stripped == cmd.strip():
229
  skip_echo = False
230
  continue
 
231
  if stripped == "$" or stripped == "$ ":
232
  continue
233
  filtered.append(line)
 
239
  return self.get_log()
240
 
241
  def run_editor_code(self, code: str) -> str:
242
+ """Execute code from the editor by writing to a temp file."""
243
  with self.lock:
244
  self._append_line("$ python [editor]")
245
+
 
246
  tmp_path = "/tmp/_axon_editor_run.py"
247
  try:
248
  with open(tmp_path, "w") as f:
 
252
  self._append_line(f"[Error] Could not write temp file: {e}")
253
  return self.get_log()
254
 
 
255
  try:
256
  result = subprocess.run(
257
  [sys.executable, tmp_path],
258
  capture_output=True, text=True, timeout=30
259
  )
260
+ with self.lock:
261
+ if result.stdout.strip():
262
  self._append_line(result.stdout.rstrip())
263
+ if result.stderr.strip():
 
264
  self._append_line(result.stderr.rstrip())
265
+ if not result.stdout.strip() and not result.stderr.strip():
 
266
  self._append_line("(No output)")
267
  except subprocess.TimeoutExpired:
268
  with self.lock:
 
413
  with gr.Tabs() as main_tabs:
414
  with gr.Tab("Editor", id="editor-tab"):
415
  with gr.Row(equal_height=True):
 
416
  with gr.Column(scale=1, min_width=200):
417
  gr.Markdown("### 📁 Explorer")
418
  file_list = gr.Dropdown(
 
429
  placeholder="script.py", label="New File", container=False
430
  )
431
 
 
432
  with gr.Column(scale=4):
433
  editor = gr.Code(
434
  value=fs.get_current_file_content(),
 
472
 
473
  with gr.Tab("AI Chat", id="chat-tab"):
474
  chat_history = gr.Chatbot(
475
+ label="Axon AI", height=300
476
  )
477
  with gr.Row():
478
  chat_input = gr.Textbox(
 
506
  diff_modified_state = gr.State("")
507
  diff_mode_state = gr.State(False)
508
 
509
+ # --- Handlers ---
510
  def update_file(content):
511
  fs.save_file(content)
512
  return fs.get_current_file_content()
 
567
  file_list.change(load_file, file_list, [editor, current_file_state])
568
  new_file_btn.click(create_file, new_file_name, [file_list, editor])
569
 
 
570
  terminal_input.submit(
571
  terminal_command, terminal_input, [terminal_output, terminal_input]
572
  )
 
575
  )
576
  clear_btn.click(clear_terminal_fn, None, terminal_output)
577
 
 
578
  run_btn.click(run_editor_wrapper, editor, terminal_output)
579
  complete_btn.click(complete_wrapper, editor, editor)
580
 
 
581
  explain_btn.click(
582
  explain_wrapper,
583
  editor,
 
593
  chat_input.submit(
594
  generate_wrapper,
595
  [chat_input, chat_history],
596
+ [diff_view, diff_original_state, diff_modified_state, diff_mode_state, chat_history, chat_input],
 
 
 
 
 
 
 
597
  ).then(lambda: gr.Tabs(selected="diff-tab"), None, main_tabs)
598
 
599
  send_btn.click(
600
  generate_wrapper,
601
  [chat_input, chat_history],
602
+ [diff_view, diff_original_state, diff_modified_state, diff_mode_state, chat_history, chat_input],
 
 
 
 
 
 
 
603
  ).then(lambda: gr.Tabs(selected="diff-tab"), None, main_tabs)
604
 
 
605
  apply_btn.click(
606
  lambda mod: (mod, False), diff_modified_state, [editor, diff_mode_state]
607
  ).then(lambda: gr.Tabs(selected="editor-tab"), None, main_tabs)