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.
- seedcode/__init__.py +14 -0
- seedcode/__main__.py +12 -0
- seedcode/app.py +508 -0
- seedcode/apps/__init__.py +32 -0
- seedcode/apps/discovery.py +241 -0
- seedcode/apps/installer.py +164 -0
- seedcode/apps/launcher.py +156 -0
- seedcode/apps/verifier.py +119 -0
- seedcode/assets/logo.txt +15 -0
- seedcode/cli.py +95 -0
- seedcode/commands/__init__.py +81 -0
- seedcode/commands/about.py +34 -0
- seedcode/commands/agent.py +94 -0
- seedcode/commands/assist.py +201 -0
- seedcode/commands/clear.py +20 -0
- seedcode/commands/desktop.py +104 -0
- seedcode/commands/doctor.py +152 -0
- seedcode/commands/help.py +61 -0
- seedcode/commands/history.py +365 -0
- seedcode/commands/palette.py +100 -0
- seedcode/commands/provider.py +451 -0
- seedcode/commands/theme.py +76 -0
- seedcode/computer/__init__.py +98 -0
- seedcode/computer/browser.py +276 -0
- seedcode/computer/browser_cdp.py +567 -0
- seedcode/computer/browser_engine.py +546 -0
- seedcode/computer/browser_extract.py +301 -0
- seedcode/computer/browser_popups.py +329 -0
- seedcode/computer/browser_selenium.py +209 -0
- seedcode/computer/browser_skills.py +245 -0
- seedcode/computer/catalog.py +200 -0
- seedcode/computer/controller.py +324 -0
- seedcode/computer/dispatcher.py +272 -0
- seedcode/computer/dpi.py +185 -0
- seedcode/computer/engine.py +105 -0
- seedcode/computer/keyboard.py +101 -0
- seedcode/computer/logbook.py +104 -0
- seedcode/computer/mouse.py +48 -0
- seedcode/computer/ocr.py +213 -0
- seedcode/computer/operator_skills.py +577 -0
- seedcode/computer/permissions.py +203 -0
- seedcode/computer/recovery.py +115 -0
- seedcode/computer/registry.py +107 -0
- seedcode/computer/resolver.py +434 -0
- seedcode/computer/screen.py +130 -0
- seedcode/computer/screen_state.py +412 -0
- seedcode/computer/selfguard.py +197 -0
- seedcode/computer/semantic.py +100 -0
- seedcode/computer/skills.py +139 -0
- seedcode/computer/state.py +199 -0
- seedcode/computer/verifier.py +177 -0
- seedcode/computer/vision.py +327 -0
- seedcode/computer/windows.py +217 -0
- seedcode/config/__init__.py +8 -0
- seedcode/config/defaults.py +22 -0
- seedcode/config/manager.py +62 -0
- seedcode/core/__init__.py +31 -0
- seedcode/core/agent.py +534 -0
- seedcode/core/chat.py +128 -0
- seedcode/core/client.py +9 -0
- seedcode/core/errors.py +199 -0
- seedcode/core/identity.py +66 -0
- seedcode/core/identity_store.py +119 -0
- seedcode/core/lifecycle.py +240 -0
- seedcode/core/limits.py +35 -0
- seedcode/core/models.py +347 -0
- seedcode/core/project.py +96 -0
- seedcode/core/providers/__init__.py +58 -0
- seedcode/core/providers/aerolink.py +324 -0
- seedcode/core/providers/base.py +230 -0
- seedcode/core/providers/freemodel.py +931 -0
- seedcode/core/providers/ollama.py +262 -0
- seedcode/core/providers/openrouter.py +393 -0
- seedcode/core/streaming.py +21 -0
- seedcode/memory/__init__.py +8 -0
- seedcode/memory/manager.py +47 -0
- seedcode/memory/storage.py +38 -0
- seedcode/memory/store.py +257 -0
- seedcode/tools/__init__.py +35 -0
- seedcode/tools/base.py +179 -0
- seedcode/tools/desktop.py +371 -0
- seedcode/tools/filesystem.py +309 -0
- seedcode/tools/git.py +72 -0
- seedcode/tools/patch.py +170 -0
- seedcode/tools/permissions.py +288 -0
- seedcode/tools/search.py +137 -0
- seedcode/tools/terminal.py +200 -0
- seedcode/tools/textio.py +59 -0
- seedcode/ui/__init__.py +164 -0
- seedcode/ui/badges.py +64 -0
- seedcode/ui/banner.py +78 -0
- seedcode/ui/dashboard.py +197 -0
- seedcode/ui/dialog.py +62 -0
- seedcode/ui/fuzzy.py +128 -0
- seedcode/ui/layout.py +54 -0
- seedcode/ui/menu.py +61 -0
- seedcode/ui/palette.py +40 -0
- seedcode/ui/progress.py +41 -0
- seedcode/ui/prompts.py +16 -0
- seedcode/ui/renderer.py +36 -0
- seedcode/ui/searchbox.py +70 -0
- seedcode/ui/selector.py +514 -0
- seedcode/ui/statusbar.py +38 -0
- seedcode/ui/textbox.py +61 -0
- seedcode/ui/theme.py +204 -0
- seedcode/ui/tree.py +91 -0
- seedcode/utils/__init__.py +22 -0
- seedcode/utils/helpers.py +97 -0
- seedcode/utils/logger.py +65 -0
- seedcode_cli-6.1.5.dist-info/METADATA +368 -0
- seedcode_cli-6.1.5.dist-info/RECORD +114 -0
- seedcode_cli-6.1.5.dist-info/WHEEL +4 -0
- seedcode_cli-6.1.5.dist-info/entry_points.txt +2 -0
- seedcode_cli-6.1.5.dist-info/licenses/LICENSE +21 -0
seedcode/computer/ocr.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""OCR provisioning: the text-recognition tier, configured for the user.
|
|
2
|
+
|
|
3
|
+
OCR used to be the weakest link in the detection ladder. ``pytesseract`` is
|
|
4
|
+
only a thin wrapper around the ``tesseract`` executable, and nothing shipped
|
|
5
|
+
that executable — so on a normal machine the OCR tier silently did nothing, and
|
|
6
|
+
a workflow that reached it failed with "OCR is not installed". Worse, the old
|
|
7
|
+
availability check asked only whether the *Python wrapper* imported, so Seed
|
|
8
|
+
Code would report OCR as available while every call to it failed.
|
|
9
|
+
|
|
10
|
+
This module fixes both halves:
|
|
11
|
+
|
|
12
|
+
* :func:`tesseract_path` finds the engine across every way Seed Code can be
|
|
13
|
+
installed — bundled next to the frozen exe, unpacked inside it, dropped in
|
|
14
|
+
the user's data directory, or already on PATH — and :func:`configure` points
|
|
15
|
+
``pytesseract`` at whatever it found.
|
|
16
|
+
* :func:`available` reports the truth: the wrapper **and** a working binary.
|
|
17
|
+
|
|
18
|
+
The user is never asked to install anything: the Windows installer ships the
|
|
19
|
+
engine (see ``scripts/windows/setup.iss``) and :func:`configure` runs
|
|
20
|
+
automatically the first time the OCR tier is used.
|
|
21
|
+
|
|
22
|
+
Deterministic and offline. No AI, and no network access — Seed Code never
|
|
23
|
+
downloads a dependency behind the user's back.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import os
|
|
29
|
+
import shutil
|
|
30
|
+
import subprocess
|
|
31
|
+
import sys
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
from ..utils.helpers import app_dir, bundled_path, install_dir
|
|
35
|
+
|
|
36
|
+
# Where the engine lives relative to each install shape. Ordered by
|
|
37
|
+
# trustworthiness: something we shipped beats whatever happens to be on PATH,
|
|
38
|
+
# so behaviour cannot drift with an unrelated system install.
|
|
39
|
+
_SUBDIR = "tesseract"
|
|
40
|
+
_EXE = "tesseract.exe" if os.name == "nt" else "tesseract"
|
|
41
|
+
|
|
42
|
+
# Env var lets a user or CI pin a specific engine explicitly.
|
|
43
|
+
_ENV_VARS = ("SEEDCODE_TESSERACT", "TESSERACT_CMD")
|
|
44
|
+
|
|
45
|
+
# Probing the binary costs a subprocess launch; the answer cannot change within
|
|
46
|
+
# a session, so it is computed once.
|
|
47
|
+
_probe_cache: "tuple[str, bool] | None" = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def candidate_paths() -> list[Path]:
|
|
51
|
+
"""Every location the engine may live, in resolution order."""
|
|
52
|
+
paths: list[Path] = []
|
|
53
|
+
for var in _ENV_VARS:
|
|
54
|
+
value = os.environ.get(var, "").strip()
|
|
55
|
+
if value:
|
|
56
|
+
paths.append(Path(value))
|
|
57
|
+
# Beside the installed exe (how the Windows installer ships it).
|
|
58
|
+
paths.append(install_dir() / _SUBDIR / _EXE)
|
|
59
|
+
# Unpacked from the frozen bundle, or inside the source package.
|
|
60
|
+
paths.append(bundled_path(_SUBDIR, _EXE))
|
|
61
|
+
# The user's own data directory — the sideload location.
|
|
62
|
+
paths.append(app_dir() / _SUBDIR / _EXE)
|
|
63
|
+
if os.name == "nt":
|
|
64
|
+
# A normal system-wide Tesseract install.
|
|
65
|
+
for root in (
|
|
66
|
+
os.environ.get("ProgramFiles", r"C:\Program Files"),
|
|
67
|
+
os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)"),
|
|
68
|
+
os.environ.get("LOCALAPPDATA", ""),
|
|
69
|
+
):
|
|
70
|
+
if root:
|
|
71
|
+
paths.append(Path(root) / "Tesseract-OCR" / _EXE)
|
|
72
|
+
return paths
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def tesseract_path() -> str | None:
|
|
76
|
+
"""The path to a usable tesseract executable, or None if there is none."""
|
|
77
|
+
for path in candidate_paths():
|
|
78
|
+
try:
|
|
79
|
+
if path.is_file():
|
|
80
|
+
return str(path)
|
|
81
|
+
except OSError:
|
|
82
|
+
continue
|
|
83
|
+
found = shutil.which("tesseract")
|
|
84
|
+
return found or None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def tessdata_path(exe: str | None = None) -> str | None:
|
|
88
|
+
"""The language-data directory matching ``exe``, if it ships alongside."""
|
|
89
|
+
if exe is None:
|
|
90
|
+
exe = tesseract_path()
|
|
91
|
+
if not exe:
|
|
92
|
+
return None
|
|
93
|
+
candidate = Path(exe).parent / "tessdata"
|
|
94
|
+
try:
|
|
95
|
+
return str(candidate) if candidate.is_dir() else None
|
|
96
|
+
except OSError:
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def configure() -> str | None:
|
|
101
|
+
"""Point ``pytesseract`` at the resolved engine. Returns the path used.
|
|
102
|
+
|
|
103
|
+
Safe and cheap to call repeatedly — every OCR entry point calls it first so
|
|
104
|
+
the engine is configured on first use with no setup step from the user.
|
|
105
|
+
"""
|
|
106
|
+
exe = tesseract_path()
|
|
107
|
+
if not exe:
|
|
108
|
+
return None
|
|
109
|
+
try:
|
|
110
|
+
import pytesseract
|
|
111
|
+
except ImportError:
|
|
112
|
+
return None
|
|
113
|
+
pytesseract.pytesseract.tesseract_cmd = exe
|
|
114
|
+
# Bundled builds need TESSDATA_PREFIX or the engine cannot find eng.traineddata.
|
|
115
|
+
data = tessdata_path(exe)
|
|
116
|
+
if data and not os.environ.get("TESSDATA_PREFIX"):
|
|
117
|
+
os.environ["TESSDATA_PREFIX"] = data
|
|
118
|
+
return exe
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def wrapper_installed() -> bool:
|
|
122
|
+
"""Whether the ``pytesseract`` Python package is importable."""
|
|
123
|
+
import importlib.util
|
|
124
|
+
|
|
125
|
+
return importlib.util.find_spec("pytesseract") is not None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def engine_works(exe: str | None = None) -> bool:
|
|
129
|
+
"""Whether the engine actually runs (cached for the session).
|
|
130
|
+
|
|
131
|
+
A file existing is not proof it executes — a partial install, a blocked
|
|
132
|
+
binary, or a missing VC runtime all produce a present-but-broken exe. The
|
|
133
|
+
honest check is to run it.
|
|
134
|
+
"""
|
|
135
|
+
global _probe_cache
|
|
136
|
+
if exe is None:
|
|
137
|
+
exe = tesseract_path()
|
|
138
|
+
if not exe:
|
|
139
|
+
return False
|
|
140
|
+
if _probe_cache is not None and _probe_cache[0] == exe:
|
|
141
|
+
return _probe_cache[1]
|
|
142
|
+
ok = False
|
|
143
|
+
try:
|
|
144
|
+
proc = subprocess.run(
|
|
145
|
+
[exe, "--version"],
|
|
146
|
+
capture_output=True,
|
|
147
|
+
text=True,
|
|
148
|
+
timeout=10,
|
|
149
|
+
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0,
|
|
150
|
+
)
|
|
151
|
+
ok = proc.returncode == 0 and "tesseract" in (proc.stdout + proc.stderr).lower()
|
|
152
|
+
except (OSError, subprocess.SubprocessError):
|
|
153
|
+
ok = False
|
|
154
|
+
_probe_cache = (exe, ok)
|
|
155
|
+
return ok
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def available() -> bool:
|
|
159
|
+
"""Whether OCR can genuinely run: wrapper present *and* engine working."""
|
|
160
|
+
if not wrapper_installed():
|
|
161
|
+
return False
|
|
162
|
+
return engine_works(configure())
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def status() -> tuple[bool, str]:
|
|
166
|
+
"""(ok, human-readable detail) for /doctor and the capability tables."""
|
|
167
|
+
if not wrapper_installed():
|
|
168
|
+
return False, (
|
|
169
|
+
"the pytesseract package is missing — reinstall Seed Code, or run: "
|
|
170
|
+
"pip install seedcode-cli[desktop]"
|
|
171
|
+
)
|
|
172
|
+
exe = configure()
|
|
173
|
+
if not exe:
|
|
174
|
+
return False, (
|
|
175
|
+
"the Tesseract engine was not found. It ships with the Seed Code "
|
|
176
|
+
"installer; reinstall, or set SEEDCODE_TESSERACT to a tesseract "
|
|
177
|
+
f"executable. Looked in: {_searched()}"
|
|
178
|
+
)
|
|
179
|
+
if not engine_works(exe):
|
|
180
|
+
return False, f"found {exe} but it did not run (damaged or blocked install)"
|
|
181
|
+
return True, exe
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def version() -> str:
|
|
185
|
+
"""The engine's reported version, or an empty string."""
|
|
186
|
+
exe = tesseract_path()
|
|
187
|
+
if not exe:
|
|
188
|
+
return ""
|
|
189
|
+
try:
|
|
190
|
+
proc = subprocess.run(
|
|
191
|
+
[exe, "--version"], capture_output=True, text=True, timeout=10,
|
|
192
|
+
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0,
|
|
193
|
+
)
|
|
194
|
+
except (OSError, subprocess.SubprocessError):
|
|
195
|
+
return ""
|
|
196
|
+
first = (proc.stdout or proc.stderr or "").strip().splitlines()
|
|
197
|
+
return first[0] if first else ""
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def reset_cache() -> None:
|
|
201
|
+
"""Forget the probe result (tests, and after an install repairs itself)."""
|
|
202
|
+
global _probe_cache
|
|
203
|
+
_probe_cache = None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _searched() -> str:
|
|
207
|
+
"""A short, readable list of the places the engine was looked for."""
|
|
208
|
+
seen: list[str] = []
|
|
209
|
+
for path in candidate_paths()[:4]:
|
|
210
|
+
text = str(path)
|
|
211
|
+
if text not in seen:
|
|
212
|
+
seen.append(text)
|
|
213
|
+
return "; ".join(seen)
|