seedcode-cli 6.1.5__py3-none-any.whl

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.
Files changed (114) hide show
  1. seedcode/__init__.py +14 -0
  2. seedcode/__main__.py +12 -0
  3. seedcode/app.py +508 -0
  4. seedcode/apps/__init__.py +32 -0
  5. seedcode/apps/discovery.py +241 -0
  6. seedcode/apps/installer.py +164 -0
  7. seedcode/apps/launcher.py +156 -0
  8. seedcode/apps/verifier.py +119 -0
  9. seedcode/assets/logo.txt +15 -0
  10. seedcode/cli.py +95 -0
  11. seedcode/commands/__init__.py +81 -0
  12. seedcode/commands/about.py +34 -0
  13. seedcode/commands/agent.py +94 -0
  14. seedcode/commands/assist.py +201 -0
  15. seedcode/commands/clear.py +20 -0
  16. seedcode/commands/desktop.py +104 -0
  17. seedcode/commands/doctor.py +152 -0
  18. seedcode/commands/help.py +61 -0
  19. seedcode/commands/history.py +365 -0
  20. seedcode/commands/palette.py +100 -0
  21. seedcode/commands/provider.py +451 -0
  22. seedcode/commands/theme.py +76 -0
  23. seedcode/computer/__init__.py +98 -0
  24. seedcode/computer/browser.py +276 -0
  25. seedcode/computer/browser_cdp.py +567 -0
  26. seedcode/computer/browser_engine.py +546 -0
  27. seedcode/computer/browser_extract.py +301 -0
  28. seedcode/computer/browser_popups.py +329 -0
  29. seedcode/computer/browser_selenium.py +209 -0
  30. seedcode/computer/browser_skills.py +245 -0
  31. seedcode/computer/catalog.py +200 -0
  32. seedcode/computer/controller.py +324 -0
  33. seedcode/computer/dispatcher.py +272 -0
  34. seedcode/computer/dpi.py +185 -0
  35. seedcode/computer/engine.py +105 -0
  36. seedcode/computer/keyboard.py +101 -0
  37. seedcode/computer/logbook.py +104 -0
  38. seedcode/computer/mouse.py +48 -0
  39. seedcode/computer/ocr.py +213 -0
  40. seedcode/computer/operator_skills.py +577 -0
  41. seedcode/computer/permissions.py +203 -0
  42. seedcode/computer/recovery.py +115 -0
  43. seedcode/computer/registry.py +107 -0
  44. seedcode/computer/resolver.py +434 -0
  45. seedcode/computer/screen.py +130 -0
  46. seedcode/computer/screen_state.py +412 -0
  47. seedcode/computer/selfguard.py +197 -0
  48. seedcode/computer/semantic.py +100 -0
  49. seedcode/computer/skills.py +139 -0
  50. seedcode/computer/state.py +199 -0
  51. seedcode/computer/verifier.py +177 -0
  52. seedcode/computer/vision.py +327 -0
  53. seedcode/computer/windows.py +217 -0
  54. seedcode/config/__init__.py +8 -0
  55. seedcode/config/defaults.py +22 -0
  56. seedcode/config/manager.py +62 -0
  57. seedcode/core/__init__.py +31 -0
  58. seedcode/core/agent.py +534 -0
  59. seedcode/core/chat.py +128 -0
  60. seedcode/core/client.py +9 -0
  61. seedcode/core/errors.py +199 -0
  62. seedcode/core/identity.py +66 -0
  63. seedcode/core/identity_store.py +119 -0
  64. seedcode/core/lifecycle.py +240 -0
  65. seedcode/core/limits.py +35 -0
  66. seedcode/core/models.py +347 -0
  67. seedcode/core/project.py +96 -0
  68. seedcode/core/providers/__init__.py +58 -0
  69. seedcode/core/providers/aerolink.py +324 -0
  70. seedcode/core/providers/base.py +230 -0
  71. seedcode/core/providers/freemodel.py +931 -0
  72. seedcode/core/providers/ollama.py +262 -0
  73. seedcode/core/providers/openrouter.py +393 -0
  74. seedcode/core/streaming.py +21 -0
  75. seedcode/memory/__init__.py +8 -0
  76. seedcode/memory/manager.py +47 -0
  77. seedcode/memory/storage.py +38 -0
  78. seedcode/memory/store.py +257 -0
  79. seedcode/tools/__init__.py +35 -0
  80. seedcode/tools/base.py +179 -0
  81. seedcode/tools/desktop.py +371 -0
  82. seedcode/tools/filesystem.py +309 -0
  83. seedcode/tools/git.py +72 -0
  84. seedcode/tools/patch.py +170 -0
  85. seedcode/tools/permissions.py +288 -0
  86. seedcode/tools/search.py +137 -0
  87. seedcode/tools/terminal.py +200 -0
  88. seedcode/tools/textio.py +59 -0
  89. seedcode/ui/__init__.py +164 -0
  90. seedcode/ui/badges.py +64 -0
  91. seedcode/ui/banner.py +78 -0
  92. seedcode/ui/dashboard.py +197 -0
  93. seedcode/ui/dialog.py +62 -0
  94. seedcode/ui/fuzzy.py +128 -0
  95. seedcode/ui/layout.py +54 -0
  96. seedcode/ui/menu.py +61 -0
  97. seedcode/ui/palette.py +40 -0
  98. seedcode/ui/progress.py +41 -0
  99. seedcode/ui/prompts.py +16 -0
  100. seedcode/ui/renderer.py +36 -0
  101. seedcode/ui/searchbox.py +70 -0
  102. seedcode/ui/selector.py +514 -0
  103. seedcode/ui/statusbar.py +38 -0
  104. seedcode/ui/textbox.py +61 -0
  105. seedcode/ui/theme.py +204 -0
  106. seedcode/ui/tree.py +91 -0
  107. seedcode/utils/__init__.py +22 -0
  108. seedcode/utils/helpers.py +97 -0
  109. seedcode/utils/logger.py +65 -0
  110. seedcode_cli-6.1.5.dist-info/METADATA +368 -0
  111. seedcode_cli-6.1.5.dist-info/RECORD +114 -0
  112. seedcode_cli-6.1.5.dist-info/WHEEL +4 -0
  113. seedcode_cli-6.1.5.dist-info/entry_points.txt +2 -0
  114. seedcode_cli-6.1.5.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,276 @@
1
+ """Default-browser URL handler — the low-level navigation driver.
2
+
3
+ Seed Code drives the **user's own default browser** — the one already signed
4
+ in, themed, and trusted — rather than spinning up a throwaway Selenium
5
+ instance. This module is the thin part of that: handing a URL to the OS
6
+ (``webbrowser`` / ``os.startfile``), which honours whatever Chromium- or
7
+ Gecko-based browser the user set as default (Chrome, Edge, Firefox, Brave,
8
+ Opera, Vivaldi, …). No WebDriver, no driver downloads.
9
+
10
+ **Workflows do not live here.** Anything a user would recognise as a task —
11
+ "play this song", "search YouTube", "switch tabs" — belongs to
12
+ :mod:`.browser_engine`, which owns the full procedure including popup
13
+ dismissal, result selection, and verification, and is exposed to the AI as
14
+ skills in :mod:`.browser_skills`. This module is one of the mechanisms that
15
+ engine uses.
16
+
17
+ Selenium remains available as an *optional* fallback (see
18
+ :mod:`.browser_selenium`) for the rare task needing a scripted throwaway
19
+ profile; it is never the primary mechanism and is not on the AI's surface.
20
+
21
+ State (the last URL we opened, the browser family) is tracked so the verifier
22
+ and StateManager can confirm outcomes. This module is offline and holds no AI
23
+ code.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import os
29
+ import sys
30
+ import webbrowser
31
+ from dataclasses import dataclass
32
+ from urllib.parse import quote_plus
33
+
34
+ # The last URL we asked the OS to open — the best-effort "current URL" the
35
+ # verifier checks, since reading the live address bar requires UIA and may miss.
36
+ _last_url: str | None = None
37
+
38
+
39
+ class BrowserError(Exception):
40
+ """A default-browser action failed; the message is fed back to the model."""
41
+
42
+
43
+ @dataclass(slots=True)
44
+ class BrowserIdentity:
45
+ """The resolved default browser."""
46
+
47
+ name: str # human name, e.g. "Google Chrome"
48
+ family: str # normalized family: chrome|edge|firefox|brave|opera|other
49
+ prog_id: str # the raw Windows ProgId (empty off-Windows)
50
+
51
+ def describe(self) -> str:
52
+ return self.name
53
+
54
+
55
+ # Windows ProgId → (display name, family). Matched by prefix so versioned
56
+ # Firefox ProgIds (``FirefoxURL-<hash>``) and channel variants still resolve.
57
+ _PROGID_MAP: tuple[tuple[str, str, str], ...] = (
58
+ ("ChromeHTML", "Google Chrome", "chrome"),
59
+ ("MSEdgeHTM", "Microsoft Edge", "edge"),
60
+ ("MSEdgeMHT", "Microsoft Edge", "edge"),
61
+ ("FirefoxURL", "Mozilla Firefox", "firefox"),
62
+ ("BraveHTML", "Brave", "brave"),
63
+ ("BraveFile", "Brave", "brave"),
64
+ ("OperaStable", "Opera", "opera"),
65
+ ("Opera", "Opera", "opera"),
66
+ ("VivaldiHTM", "Vivaldi", "other"),
67
+ ("IE.HTTP", "Internet Explorer", "other"),
68
+ ("AppXq", "Microsoft Edge", "edge"), # Edge legacy AppX ProgIds
69
+ )
70
+
71
+ SEARCH_ENGINES = {
72
+ "google": "https://www.google.com/search?q={q}",
73
+ "bing": "https://www.bing.com/search?q={q}",
74
+ "duckduckgo": "https://duckduckgo.com/?q={q}",
75
+ "youtube": "https://www.youtube.com/results?search_query={q}",
76
+ }
77
+
78
+
79
+ def is_available() -> tuple[bool, str]:
80
+ """Whether default-browser control can run here.
81
+
82
+ A default browser effectively always exists, so this is about the platform
83
+ having a usable URL handler. Always true in practice; kept as a tuple to
84
+ mirror the other drivers' availability contract.
85
+ """
86
+ return True, ""
87
+
88
+
89
+ def default_browser() -> BrowserIdentity:
90
+ """Resolve the user's current default browser."""
91
+ if sys.platform == "win32":
92
+ prog_id = _win_default_progid()
93
+ if prog_id:
94
+ for prefix, name, family in _PROGID_MAP:
95
+ if prog_id.lower().startswith(prefix.lower()):
96
+ return BrowserIdentity(name=name, family=family, prog_id=prog_id)
97
+ return BrowserIdentity(name=prog_id, family="other", prog_id=prog_id)
98
+ # Non-Windows or unknown: report what the stdlib will actually use.
99
+ try:
100
+ controller = webbrowser.get()
101
+ name = getattr(controller, "name", "") or type(controller).__name__
102
+ except Exception:
103
+ name = "system default"
104
+ return BrowserIdentity(name=name or "system default", family="other", prog_id="")
105
+
106
+
107
+ def _win_default_progid() -> str:
108
+ """Read the HTTPS URL-association ProgId from the registry (empty on error)."""
109
+ try:
110
+ import winreg
111
+
112
+ key_path = (
113
+ r"Software\Microsoft\Windows\Shell\Associations"
114
+ r"\UrlAssociations\https\UserChoice"
115
+ )
116
+ with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path) as key:
117
+ prog_id, _type = winreg.QueryValueEx(key, "ProgId")
118
+ return str(prog_id or "")
119
+ except (OSError, ValueError, ImportError):
120
+ return ""
121
+
122
+
123
+ def _normalize_url(url: str) -> str:
124
+ url = (url or "").strip()
125
+ if not url:
126
+ return "about:blank"
127
+ if not url.startswith(("http://", "https://", "file://", "about:", "ftp://")):
128
+ url = "https://" + url
129
+ return url
130
+
131
+
132
+ def navigate(url: str, *, new_window: bool = False) -> str:
133
+ """Open ``url`` in the user's default browser (a new tab by default)."""
134
+ global _last_url
135
+ target = _normalize_url(url)
136
+ browser = default_browser()
137
+ opened = _open(target, new_window=new_window)
138
+ if not opened:
139
+ raise BrowserError(
140
+ f"Could not hand '{target}' to the default browser ({browser.name})."
141
+ )
142
+ _last_url = target
143
+ return f"Opened {target} in {browser.name} (default browser)."
144
+
145
+
146
+ def _open(target: str, *, new_window: bool) -> bool:
147
+ """Hand a URL to the OS default handler. Returns whether it was accepted."""
148
+ # webbrowser.open honours the OS default and returns success; new=2 asks
149
+ # for a new tab, new=1 a new window.
150
+ try:
151
+ if webbrowser.open(target, new=(1 if new_window else 2)):
152
+ return True
153
+ except Exception:
154
+ pass
155
+ # Fallback: ShellExecute the URL directly (Windows) — same default handler.
156
+ if sys.platform == "win32":
157
+ try:
158
+ os.startfile(target) # noqa: S606 — deliberate shell-open of a URL
159
+ return True
160
+ except OSError:
161
+ return False
162
+ return False
163
+
164
+
165
+ def open_new_tab(url: str) -> str:
166
+ """Open a URL in a new tab of the default browser."""
167
+ return navigate(url, new_window=False)
168
+
169
+
170
+ def search(query: str, engine: str = "google") -> str:
171
+ """Run a web search in the default browser using ``engine``."""
172
+ query = (query or "").strip()
173
+ if not query:
174
+ raise BrowserError("A search query is required.")
175
+ template = SEARCH_ENGINES.get(engine.lower().strip(), SEARCH_ENGINES["google"])
176
+ return navigate(template.format(q=quote_plus(query)))
177
+
178
+
179
+ def get_page_info() -> str:
180
+ """Best-effort current page: the active browser window title + last URL.
181
+
182
+ Reading the live URL from the address bar needs UIA and is not always
183
+ exposed; the last URL we opened is a reliable floor the verifier can use.
184
+ """
185
+ title = _active_browser_title()
186
+ lines = []
187
+ if title:
188
+ lines.append(f"Title: {title}")
189
+ if _last_url:
190
+ lines.append(f"URL: {_last_url}")
191
+ if not lines:
192
+ return "No browser page information available."
193
+ return "\n".join(lines)
194
+
195
+
196
+ def _active_browser_title() -> str:
197
+ """Title of a visible browser window, if one is open."""
198
+ try:
199
+ from . import windows as windows_driver
200
+
201
+ browser = default_browser()
202
+ # Chromium/Gecko windows end with the browser's name, e.g.
203
+ # "YouTube — Google Chrome". Match on family/name tokens.
204
+ tokens = {browser.name.lower(), browser.family.lower(),
205
+ "chrome", "edge", "firefox", "brave", "opera", "mozilla"}
206
+ for w in windows_driver.list_windows():
207
+ low = (w.title or "").lower()
208
+ if any(tok and tok in low for tok in tokens):
209
+ return w.title
210
+ except Exception:
211
+ pass
212
+ return ""
213
+
214
+
215
+ def current_url() -> str | None:
216
+ """The last URL Seed Code opened in the default browser (state tracking)."""
217
+ return _last_url
218
+
219
+
220
+ def get_current_browser() -> str:
221
+ """The default browser's display name."""
222
+ return default_browser().name
223
+
224
+
225
+ def close_browser() -> str:
226
+ """Close a visible browser window (best-effort, graceful)."""
227
+ global _last_url
228
+ title = _active_browser_title()
229
+ if not title:
230
+ _last_url = None
231
+ return "No browser window found to close."
232
+ try:
233
+ from . import windows as windows_driver
234
+
235
+ windows_driver.close_window(title)
236
+ except Exception as exc:
237
+ raise BrowserError(f"Could not close the browser window: {exc}")
238
+ _last_url = None
239
+ return f"Closed browser window '{title}'."
240
+
241
+
242
+ # --- optional Selenium fallback (DOM-level control) --------------------------
243
+ # These exist for the rare task that needs a scripted, throwaway browser
244
+ # profile. They are NOT the primary path and are not reachable from the AI:
245
+ # web automation goes through the Browser Engine's skills (youtube_play,
246
+ # open_url, …), which drive the user's real, signed-in browser.
247
+
248
+ def _selenium():
249
+ from . import browser_selenium
250
+
251
+ ok, reason = browser_selenium.is_available()
252
+ if not ok:
253
+ raise BrowserError(
254
+ f"DOM-level browser control needs the optional Selenium extra ({reason}). "
255
+ "For essentially every task, prefer the browser skills instead "
256
+ "(youtube_play, google_search, open_url, …) — they drive the user's "
257
+ "own browser, handle popups, and verify the result, with no extra "
258
+ "install. ui_click is not available on browser windows."
259
+ )
260
+ return browser_selenium
261
+
262
+
263
+ def click_element(selector: str, selector_type: str = "css") -> str:
264
+ return _selenium().click_element(selector, selector_type)
265
+
266
+
267
+ def type_in_element(selector: str, text: str, selector_type: str = "css") -> str:
268
+ return _selenium().type_in_element(selector, text, selector_type)
269
+
270
+
271
+ def find_elements(selector: str, selector_type: str = "css") -> str:
272
+ return _selenium().find_elements(selector, selector_type)
273
+
274
+
275
+ def execute_script(script: str) -> str:
276
+ return _selenium().execute_script(script)