@oracle-agent/oracle 0.8.0 → 0.9.1

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.
@@ -1,23 +1,80 @@
1
+ #!/usr/bin/env python3
1
2
  import importlib
2
3
  import importlib.abc
3
4
  import importlib.machinery
4
- import re
5
+ import json
6
+ import os
7
+ import shutil
8
+ import subprocess
5
9
  import sys
6
10
 
7
- LOGO = (
8
- " _",
9
- " ___ _ _ __ _ __| |___",
10
- "/ _ \\ '_/ _` / _| / -_)",
11
- "\\___/_| \\__,_\\__|_\\___|",
11
+
12
+ WORDMARK = (
13
+ " ████ ",
14
+ " ░░███ ",
15
+ " ██████ ████████ ██████ ██████ ░███ ██████ ",
16
+ " ███░░███░░███░░███ ░░░░░███ ███░░███ ░███ ███░░███",
17
+ "░███ ░███ ░███ ░░░ ███████ ░███ ░░░ ░███ ░███████ ",
18
+ "░███ ░███ ░███ ███░░███ ░███ ███ ░███ ░███░░░ ",
19
+ "░░██████ █████ ░░████████░░██████ █████░░██████ ",
20
+ " ░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ",
12
21
  )
13
22
 
14
- SILENT_STDERR = {
23
+ SILENT_OUTPUT = {
15
24
  "[anthropic_billing_bypass] Bypass installed",
16
25
  "[anthropic_billing_bypass] Transport unwrap hook installed",
17
26
  }
18
27
 
19
28
 
20
- class OracleStderr:
29
+ def _oracle_chain(command):
30
+ node = os.environ.get("ORACLE_NODE_BIN")
31
+ entry = os.environ.get("ORACLE_CLI_ENTRY")
32
+ if not node or not entry:
33
+ return False, "oracle chain is unavailable in this session"
34
+
35
+ parts = command.strip().split()[1:]
36
+ args = parts or ["list"]
37
+ try:
38
+ result = subprocess.run(
39
+ [node, entry, "chain", *args],
40
+ capture_output=True,
41
+ text=True,
42
+ timeout=15,
43
+ check=False,
44
+ )
45
+ except (OSError, subprocess.TimeoutExpired) as exc:
46
+ return False, f"oracle chain failed: {exc}"
47
+
48
+ output = "\n".join(
49
+ part.strip() for part in (result.stdout, result.stderr) if part.strip()
50
+ )
51
+ if result.returncode != 0:
52
+ return False, output or "oracle chain failed"
53
+
54
+ state = subprocess.run(
55
+ [node, entry, "chain", "show", "--json"],
56
+ capture_output=True,
57
+ text=True,
58
+ timeout=15,
59
+ check=False,
60
+ )
61
+ if state.returncode in (0, 1):
62
+ try:
63
+ active = json.loads(state.stdout).get("active") or {}
64
+ if active:
65
+ os.environ["ORACLE_ACTIVE_CHAIN"] = str(active.get("key", ""))
66
+ os.environ["ORACLE_ACTIVE_CHAIN_ID"] = str(active.get("chainId", ""))
67
+ os.environ["ORACLE_ACTIVE_AGENT"] = str(active.get("agent", ""))
68
+ else:
69
+ os.environ.pop("ORACLE_ACTIVE_CHAIN", None)
70
+ os.environ.pop("ORACLE_ACTIVE_CHAIN_ID", None)
71
+ os.environ.pop("ORACLE_ACTIVE_AGENT", None)
72
+ except (TypeError, ValueError):
73
+ pass
74
+ return True, output
75
+
76
+
77
+ class OracleOutput:
21
78
  def __init__(self, wrapped):
22
79
  self.wrapped = wrapped
23
80
 
@@ -25,8 +82,12 @@ class OracleStderr:
25
82
  kept = "".join(
26
83
  line
27
84
  for line in data.splitlines(keepends=True)
28
- if line.rstrip("\r\n") not in SILENT_STDERR
85
+ if line.rstrip("\r\n") not in SILENT_OUTPUT
29
86
  )
87
+ kept = kept.replace("hermes model", "oracle model")
88
+ kept = kept.replace("hermes --resume", "oracle --resume")
89
+ kept = kept.replace("hermes -c", "oracle -c")
90
+ kept = kept.replace(" in ~/.hermes/.env", " through oracle model")
30
91
  if kept:
31
92
  self.wrapped.write(kept)
32
93
  return len(data)
@@ -39,74 +100,213 @@ class OracleStderr:
39
100
 
40
101
 
41
102
  def patch_cli(module):
42
- hermes_cli = getattr(module, "HermesCLI")
43
- original_layout = hermes_cli._build_tui_layout_children
44
- original_emit = hermes_cli._emit_stream_text
45
- original_flush = hermes_cli._flush_stream
46
- original_cprint = getattr(module, "_cprint")
47
- frame_filter = [0]
48
-
49
- def cprint(text="", *args, **kwargs):
50
- if frame_filter[0] and isinstance(text, str):
51
- plain = re.sub(r"\x1b\[[0-9;]*m", "", text).lstrip("\n")
52
- if plain.startswith("\u256d\u2500oracle") or plain.startswith("\u2570"):
53
- return None
54
- return original_cprint(text, *args, **kwargs)
55
-
56
- setattr(module, "_cprint", cprint)
103
+ cls = getattr(module, "HermesCLI", None)
104
+ if cls is None or getattr(cls, "_oracle_patched", False):
105
+ return
106
+
107
+ original_style = getattr(cls, "_build_tui_style_dict", None)
108
+ original_process_command = cls.process_command
57
109
 
58
110
  def show_banner(self):
111
+ from rich.align import Align
112
+ from rich.console import Group
113
+ from rich.panel import Panel
114
+ from rich.text import Text
115
+ from rich import box
116
+
59
117
  self.console.clear()
60
- self._console_print()
61
- for line in LOGO:
62
- self._console_print(f"[#e6edf3]{line}[/]")
63
- self._console_print()
118
+ width = shutil.get_terminal_size((100, 28)).columns
119
+ body = Text(justify="center")
120
+ if width >= 72:
121
+ colors = (
122
+ "#B8F0FF",
123
+ "#B8F0FF",
124
+ "#ACDEEF",
125
+ "#A5D9EB",
126
+ "#9FCBDD",
127
+ "#9FCBDD",
128
+ "#ACDEEF",
129
+ "#B8F0FF",
130
+ )
131
+ for index, line in enumerate(WORDMARK):
132
+ body.append(line.rstrip(), style=f"bold {colors[index]}")
133
+ if index != len(WORDMARK) - 1:
134
+ body.append("\n")
135
+ else:
136
+ body.append("oracle", style="bold #B8F0FF")
137
+
138
+ body.append("\n\nTHE FUTURE IS AGENTIC", style="bold #EAF2F8")
139
+ body.append(" / ", style="#52606D")
140
+ model = str(getattr(self, "model", "") or "choose model")
141
+ body.append(model, style="#91A2B1")
142
+
143
+ panel_width = max(36, min(width - 4, 76))
144
+ panel = Panel(
145
+ Group(Align.center(body)),
146
+ width=panel_width,
147
+ padding=(1, 2),
148
+ border_style="#3B4A56",
149
+ box=box.ROUNDED,
150
+ subtitle="[dim #6F7F8D]/model /chain /setup[/]",
151
+ subtitle_align="right",
152
+ )
153
+ self.console.print(Align.center(panel))
154
+ self.console.print()
64
155
 
65
156
  def prompt_fragments(self):
66
- if self._approval_state or getattr(self, "_slash_confirm_state", None):
67
- marker = "!"
68
- elif self._clarify_state or self._clarify_freetext:
69
- marker = "?"
70
- elif self._command_running or self._agent_running:
71
- marker = "..."
72
- else:
73
- marker = ">"
74
- return [("class:prompt", f"oracle {marker} ")]
75
-
76
- def layout_children(self, **kwargs):
77
- kwargs["status_bar"] = None
78
- kwargs["input_rule_top"] = None
79
- kwargs["input_rule_bot"] = None
80
- return original_layout(self, **kwargs)
81
-
82
- def emit_stream_text(self, text):
83
- first = not self._stream_box_opened
84
- if first:
85
- original_cprint()
86
- frame_filter[0] += 1
87
- try:
88
- return original_emit(self, text)
89
- finally:
90
- frame_filter[0] -= 1
157
+ if getattr(self, "_voice_recording", False):
158
+ return [("class:voice-recording", "recording ")]
159
+ if getattr(self, "_voice_processing", False):
160
+ return [("class:voice-processing", "transcribing ")]
161
+ if getattr(self, "_sudo_state", None) or getattr(self, "_secret_state", None):
162
+ return [("class:sudo-prompt", "secure › ")]
163
+ if getattr(self, "_approval_state", None) or getattr(self, "_slash_confirm_state", None):
164
+ return [("class:prompt-working", "confirm › ")]
165
+ if getattr(self, "_clarify_freetext", False):
166
+ return [("class:clarify-selected", "answer › ")]
167
+ if getattr(self, "_clarify_state", None):
168
+ return [("class:prompt-working", "choose › ")]
169
+ if getattr(self, "_command_running", False):
170
+ return [("class:prompt-working", "working ")]
171
+ if getattr(self, "_agent_running", False):
172
+ return [("class:prompt-working", "thinking ")]
173
+ return [("class:prompt", " Ask Oracle › ")]
91
174
 
92
- def flush_stream(self):
93
- frame_filter[0] += 1
175
+ def status_fragments(self):
176
+ if not getattr(self, "_status_bar_visible", True) or getattr(self, "_model_picker_state", None):
177
+ return []
94
178
  try:
95
- return original_flush(self)
96
- finally:
97
- frame_filter[0] -= 1
179
+ snapshot = self._get_status_bar_snapshot()
180
+ width = self._get_tui_terminal_width()
181
+ model = snapshot.get("model_short") or "model"
182
+ percent = snapshot.get("context_percent")
183
+ context = f"{percent}% context" if percent is not None else "context ready"
184
+ duration = snapshot.get("duration") or "0s"
185
+ if width < 58:
186
+ return [
187
+ ("class:status-bar", " "),
188
+ ("class:status-bar-strong", model),
189
+ ("class:status-bar-dim", f" · {duration} "),
190
+ ]
191
+ return [
192
+ ("class:status-bar", " "),
193
+ ("class:status-bar-brand", "oracle"),
194
+ ("class:status-bar-dim", " / "),
195
+ ("class:status-bar-strong", model),
196
+ ("class:status-bar-dim", " / "),
197
+ ("class:status-bar-context", context),
198
+ ("class:status-bar-dim", " / "),
199
+ ("class:status-bar-dim", f"{duration} "),
200
+ ]
201
+ except Exception:
202
+ return [("class:status-bar-brand", " oracle ")]
98
203
 
99
- hermes_cli.show_banner = show_banner
100
- hermes_cli._get_tui_prompt_fragments = prompt_fragments
101
- hermes_cli._build_tui_layout_children = layout_children
102
- hermes_cli._emit_stream_text = emit_stream_text
103
- hermes_cli._flush_stream = flush_stream
104
- tips = importlib.import_module("hermes_cli.tips")
204
+ def layout_children(
205
+ self,
206
+ *,
207
+ sudo_widget,
208
+ secret_widget,
209
+ approval_widget,
210
+ slash_confirm_widget=None,
211
+ clarify_widget,
212
+ model_picker_widget=None,
213
+ spinner_widget=None,
214
+ spacer,
215
+ status_bar,
216
+ input_rule_top,
217
+ image_bar,
218
+ input_area,
219
+ input_rule_bot,
220
+ voice_status_bar,
221
+ completions_menu,
222
+ ):
223
+ from prompt_toolkit.layout.containers import HSplit, VSplit, Window
224
+ from prompt_toolkit.widgets import Frame
225
+ from prompt_toolkit.widgets.base import Border
105
226
 
106
- def no_tip():
107
- raise LookupError("oracle quiet startup")
227
+ Border.TOP_LEFT = "╭"
228
+ Border.TOP_RIGHT = "╮"
229
+ Border.BOTTOM_LEFT = "╰"
230
+ Border.BOTTOM_RIGHT = "╯"
108
231
 
109
- setattr(tips, "get_random_tip", no_tip)
232
+ composer = VSplit(
233
+ [
234
+ Window(width=1),
235
+ Frame(
236
+ HSplit([item for item in (image_bar, input_area) if item is not None]),
237
+ style="class:oracle-composer",
238
+ ),
239
+ Window(width=1),
240
+ ]
241
+ )
242
+ return [
243
+ item
244
+ for item in (
245
+ Window(height=0),
246
+ sudo_widget,
247
+ secret_widget,
248
+ approval_widget,
249
+ slash_confirm_widget,
250
+ clarify_widget,
251
+ model_picker_widget,
252
+ spinner_widget,
253
+ spacer,
254
+ composer,
255
+ completions_menu,
256
+ voice_status_bar,
257
+ status_bar,
258
+ )
259
+ if item is not None
260
+ ]
261
+
262
+ def build_style(self):
263
+ style = original_style(self) if original_style else {}
264
+ style.update(
265
+ {
266
+ "frame": "bg:#0B0E11 #EAF2F8",
267
+ "frame.border": "#455867",
268
+ "frame.label": "bold #B8F0FF",
269
+ "oracle-composer": "bg:#0B0E11 #EAF2F8",
270
+ "input-area": "bg:#0B0E11 #EAF2F8",
271
+ "prompt": "bold #B8F0FF",
272
+ "prompt-working": "bold #91C2D7",
273
+ "status-bar": "bg:#11161B #91A2B1",
274
+ "status-bar-brand": "bg:#11161B bold #B8F0FF",
275
+ "status-bar-strong": "bg:#11161B #EAF2F8",
276
+ "status-bar-context": "bg:#11161B #A5D9EB",
277
+ "status-bar-dim": "bg:#11161B #60717F",
278
+ }
279
+ )
280
+ return style
281
+
282
+ def process_command(self, command):
283
+ parts = command.strip().lower().split(maxsplit=1)
284
+ if parts and parts[0] == "/chain":
285
+ ok, output = _oracle_chain(command)
286
+ prefix = "" if ok else "error: "
287
+ lines = output.splitlines() or [""]
288
+ for index, line in enumerate(lines):
289
+ module._cprint(f"{prefix if index == 0 else ''}{line}")
290
+ return True
291
+ return original_process_command(self, command)
292
+
293
+ setattr(cls, "show_banner", show_banner)
294
+ setattr(cls, "_get_tui_prompt_fragments", prompt_fragments)
295
+ setattr(cls, "_get_status_bar_fragments", status_fragments)
296
+ setattr(cls, "_build_tui_layout_children", layout_children)
297
+ setattr(cls, "_build_tui_style_dict", build_style)
298
+ setattr(cls, "process_command", process_command)
299
+ setattr(cls, "_oracle_patched", True)
300
+
301
+ try:
302
+ tips = importlib.import_module("hermes_cli.tips")
303
+
304
+ def no_tip():
305
+ raise LookupError("oracle quiet startup")
306
+
307
+ setattr(tips, "get_random_tip", no_tip)
308
+ except Exception:
309
+ pass
110
310
 
111
311
 
112
312
  class OracleLoader(importlib.abc.Loader):
@@ -138,9 +338,11 @@ class OracleFinder(importlib.abc.MetaPathFinder):
138
338
 
139
339
 
140
340
  def main():
141
- sys.stderr = OracleStderr(sys.stderr)
341
+ sys.stdout = OracleOutput(sys.stdout)
342
+ sys.stderr = OracleOutput(sys.stderr)
142
343
  sys.meta_path.insert(0, OracleFinder())
143
- hermes_main = getattr(importlib.import_module("hermes_cli.main"), "main")
344
+ from hermes_cli.main import main as hermes_main
345
+
144
346
  return hermes_main()
145
347
 
146
348