gitcast 1.0.4__tar.gz → 1.0.5__tar.gz
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.
- {gitcast-1.0.4 → gitcast-1.0.5}/.env.example +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/LICENSE +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/MANIFEST.in +0 -0
- {gitcast-1.0.4/gitcast.egg-info → gitcast-1.0.5}/PKG-INFO +50 -77
- {gitcast-1.0.4 → gitcast-1.0.5}/README.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/ai/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/ai/formatter.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/ai/generator.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/ai/prompts.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/ai/viral_patterns.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/analytics.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/auth.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/auth_middleware.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/auth_routes.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/monitoring.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/payload.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/ratelimit.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/routes.py +2 -1
- {gitcast-1.0.4 → gitcast-1.0.5}/api/server.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/api/validators.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/assets/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/assets/favicon-16x16.png +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/assets/favicon-32x32.png +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/assets/favicon-64x64.png +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/assets/favicon.ico +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/assets/icon.png +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/cli/.env.example +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/cli/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/cli/gitcast.py +2 -2
- {gitcast-1.0.4 → gitcast-1.0.5}/config/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/config/settings.py +59 -4
- {gitcast-1.0.4 → gitcast-1.0.5}/core/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/capture.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/codebase_reader.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/framing.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/hotkey.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/log_stream.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/ocr.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/screenshot_session.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/security.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/core/tray.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/api-keys.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/article-generator.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/capture-flow.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/changelog.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/contributing.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/dashboard.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/insights.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/introduction.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/post-formats.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/providers.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/quickstart.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/docs/sprint-mode.md +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5/gitcast.egg-info}/PKG-INFO +50 -77
- {gitcast-1.0.4 → gitcast-1.0.5}/gitcast.egg-info/SOURCES.txt +0 -2
- {gitcast-1.0.4 → gitcast-1.0.5}/gitcast.egg-info/entry_points.txt +1 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/publisher/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/publisher/clipboard.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/publisher/twitter.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/pyproject.toml +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/requirements.txt +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/setup.cfg +4 -4
- {gitcast-1.0.4 → gitcast-1.0.5}/setup.py +1 -1
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/cleanup.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/engagement.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/insights.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/key_manager.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/logger.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/metrics.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/sprint.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/streak.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/supabase_client.py +14 -4
- {gitcast-1.0.4 → gitcast-1.0.5}/storage/tone_memory.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/ui/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/web/__init__.py +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/web/index.html +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/web/landing.html +0 -0
- gitcast-1.0.4/tests/test_keys.py +0 -86
- gitcast-1.0.4/tests/test_ocr_fix.py +0 -77
- {gitcast-1.0.4 → gitcast-1.0.5}/gitcast.egg-info/dependency_links.txt +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/gitcast.egg-info/requires.txt +0 -0
- {gitcast-1.0.4 → gitcast-1.0.5}/gitcast.egg-info/top_level.txt +0 -0
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,77 +1,50 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name: gitcast
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Summary: git diff → published post. under 60 seconds.
|
|
5
|
-
Home-page: https://github.com/drizzy765/gitcast
|
|
6
|
-
Author: Timilehin Agoro
|
|
7
|
-
Author-email: agorotimilehi05@gmail.com
|
|
8
|
-
License: MIT
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
pip install gitcast
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Setup
|
|
55
|
-
|
|
56
|
-
1. Install Tesseract OCR:
|
|
57
|
-
- Windows: https://github.com/UB-Mannheim/tesseract/wiki
|
|
58
|
-
- Mac: `brew install tesseract`
|
|
59
|
-
- Linux: `sudo apt install tesseract-ocr`
|
|
60
|
-
|
|
61
|
-
2. Add your API keys:
|
|
62
|
-
```bash
|
|
63
|
-
gitcast --setup
|
|
64
|
-
```
|
|
65
|
-
(opens `.env` file for editing)
|
|
66
|
-
|
|
67
|
-
3. Run:
|
|
68
|
-
```bash
|
|
69
|
-
gitcast
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Dashboard opens at http://127.0.0.1:8000
|
|
73
|
-
Press Ctrl+Shift+P from anywhere to capture.
|
|
74
|
-
|
|
75
|
-
## Stack
|
|
76
|
-
- Python 3.11+
|
|
77
|
-
- FastAPI, pynput, mss, Tesseract, Groq API, Tweepy
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gitcast
|
|
3
|
+
Version: 1.0.5
|
|
4
|
+
Summary: git diff → published post. under 60 seconds.
|
|
5
|
+
Home-page: https://github.com/drizzy765/gitcast
|
|
6
|
+
Author: Timilehin Agoro
|
|
7
|
+
Author-email: agorotimilehi05@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
|
|
14
|
+
# Gitcast
|
|
15
|
+
|
|
16
|
+
On-demand, privacy-first desktop utility that captures active window context,
|
|
17
|
+
extracts code changes, and generates platform-ready X (Twitter) posts with one keystroke.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install gitcast
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
|
|
27
|
+
1. Install Tesseract OCR:
|
|
28
|
+
- Windows: https://github.com/UB-Mannheim/tesseract/wiki
|
|
29
|
+
- Mac: `brew install tesseract`
|
|
30
|
+
- Linux: `sudo apt install tesseract-ocr`
|
|
31
|
+
|
|
32
|
+
2. Add your API keys:
|
|
33
|
+
```bash
|
|
34
|
+
gitcast --setup
|
|
35
|
+
```
|
|
36
|
+
(opens `.env` file for editing)
|
|
37
|
+
|
|
38
|
+
3. Run:
|
|
39
|
+
```bash
|
|
40
|
+
gitcast
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Dashboard opens at http://127.0.0.1:8000
|
|
44
|
+
Press Ctrl+Shift+P from anywhere to capture.
|
|
45
|
+
|
|
46
|
+
## Stack
|
|
47
|
+
- Python 3.11+
|
|
48
|
+
- FastAPI, pynput, mss, Tesseract, Groq API, Tweepy
|
|
49
|
+
|
|
50
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -7,8 +7,8 @@ import webbrowser
|
|
|
7
7
|
|
|
8
8
|
def main():
|
|
9
9
|
if "--setup" in sys.argv:
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
from config.settings import get_active_env_path
|
|
11
|
+
env_path = str(get_active_env_path(for_write=True))
|
|
12
12
|
example_path = os.path.join(
|
|
13
13
|
os.path.dirname(__file__), '..', '.env.example')
|
|
14
14
|
if not os.path.exists(example_path):
|
|
File without changes
|
|
@@ -5,11 +5,55 @@ from pathlib import Path
|
|
|
5
5
|
from dotenv import load_dotenv
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
import sys
|
|
9
|
+
|
|
8
10
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
9
11
|
CONFIG_DIR = BASE_DIR / "config"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
def get_env_paths() -> list:
|
|
14
|
+
paths = []
|
|
15
|
+
# 1. Custom env path
|
|
16
|
+
custom_path = os.getenv("GITCAST_ENV_PATH")
|
|
17
|
+
if custom_path:
|
|
18
|
+
paths.append(Path(custom_path))
|
|
19
|
+
# 2. Current working directory
|
|
20
|
+
paths.append(Path(os.getcwd()) / ".env")
|
|
21
|
+
# 3. Virtual environment parent
|
|
22
|
+
if hasattr(sys, "prefix") and sys.prefix:
|
|
23
|
+
paths.append(Path(sys.prefix).parent / ".env")
|
|
24
|
+
# 4. User home directories
|
|
25
|
+
paths.append(Path.home() / ".gitcast" / ".env")
|
|
26
|
+
paths.append(Path.home() / ".gitcast.env")
|
|
27
|
+
# 5. Base directory
|
|
28
|
+
paths.append(BASE_DIR / ".env")
|
|
29
|
+
return [p for p in paths if p]
|
|
30
|
+
|
|
31
|
+
def load_all_dotenvs(override: bool = False) -> None:
|
|
32
|
+
paths = get_env_paths()
|
|
33
|
+
if override:
|
|
34
|
+
for p in reversed(paths):
|
|
35
|
+
if p.exists() and p.is_file():
|
|
36
|
+
load_dotenv(p, override=True)
|
|
37
|
+
else:
|
|
38
|
+
for p in paths:
|
|
39
|
+
if p.exists() and p.is_file():
|
|
40
|
+
load_dotenv(p, override=False)
|
|
41
|
+
|
|
42
|
+
def get_active_env_path(for_write: bool = False) -> Path:
|
|
43
|
+
paths = get_env_paths()
|
|
44
|
+
for p in paths:
|
|
45
|
+
if p.exists() and p.is_file():
|
|
46
|
+
return p
|
|
47
|
+
if for_write:
|
|
48
|
+
home_env = Path.home() / ".gitcast" / ".env"
|
|
49
|
+
try:
|
|
50
|
+
home_env.parent.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
return home_env
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
return BASE_DIR / ".env"
|
|
55
|
+
|
|
56
|
+
load_all_dotenvs()
|
|
13
57
|
STORAGE_DIR = BASE_DIR / "storage" / "data"
|
|
14
58
|
POSTHOG_API_KEY = os.getenv("POSTHOG_API_KEY", "")
|
|
15
59
|
SENTRY_DSN = os.getenv("SENTRY_DSN", "")
|
|
@@ -65,8 +109,13 @@ def reload_api_keys() -> None:
|
|
|
65
109
|
global GEMINI_MODEL
|
|
66
110
|
global CEREBRAS_MODEL
|
|
67
111
|
global OPENROUTER_MODEL
|
|
112
|
+
global SUPABASE_URL
|
|
113
|
+
global SUPABASE_SERVICE_KEY
|
|
114
|
+
global SUPABASE_ANON_KEY
|
|
115
|
+
global SUPABASE_JWT_SECRET
|
|
116
|
+
global SUPABASE_JWT_AUDIENCE
|
|
68
117
|
|
|
69
|
-
|
|
118
|
+
load_all_dotenvs(override=True)
|
|
70
119
|
GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
|
|
71
120
|
MOONSHOT_API_KEY = os.getenv("MOONSHOT_API_KEY", "")
|
|
72
121
|
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "")
|
|
@@ -77,6 +126,12 @@ def reload_api_keys() -> None:
|
|
|
77
126
|
GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.0-flash")
|
|
78
127
|
CEREBRAS_MODEL = os.getenv("CEREBRAS_MODEL", "gpt-oss-120b")
|
|
79
128
|
OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "qwen/qwen3-coder:free")
|
|
129
|
+
|
|
130
|
+
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
|
|
131
|
+
SUPABASE_SERVICE_KEY = os.getenv("SUPABASE_SERVICE_KEY", "")
|
|
132
|
+
SUPABASE_ANON_KEY = os.getenv("SUPABASE_ANON_KEY", "")
|
|
133
|
+
SUPABASE_JWT_SECRET = os.getenv("SUPABASE_JWT_SECRET", "")
|
|
134
|
+
SUPABASE_JWT_AUDIENCE = os.getenv("SUPABASE_JWT_AUDIENCE", "authenticated")
|
|
80
135
|
|
|
81
136
|
|
|
82
137
|
reload_api_keys()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,77 +1,50 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name: gitcast
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Summary: git diff → published post. under 60 seconds.
|
|
5
|
-
Home-page: https://github.com/drizzy765/gitcast
|
|
6
|
-
Author: Timilehin Agoro
|
|
7
|
-
Author-email: agorotimilehi05@gmail.com
|
|
8
|
-
License: MIT
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
pip install gitcast
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Setup
|
|
55
|
-
|
|
56
|
-
1. Install Tesseract OCR:
|
|
57
|
-
- Windows: https://github.com/UB-Mannheim/tesseract/wiki
|
|
58
|
-
- Mac: `brew install tesseract`
|
|
59
|
-
- Linux: `sudo apt install tesseract-ocr`
|
|
60
|
-
|
|
61
|
-
2. Add your API keys:
|
|
62
|
-
```bash
|
|
63
|
-
gitcast --setup
|
|
64
|
-
```
|
|
65
|
-
(opens `.env` file for editing)
|
|
66
|
-
|
|
67
|
-
3. Run:
|
|
68
|
-
```bash
|
|
69
|
-
gitcast
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Dashboard opens at http://127.0.0.1:8000
|
|
73
|
-
Press Ctrl+Shift+P from anywhere to capture.
|
|
74
|
-
|
|
75
|
-
## Stack
|
|
76
|
-
- Python 3.11+
|
|
77
|
-
- FastAPI, pynput, mss, Tesseract, Groq API, Tweepy
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gitcast
|
|
3
|
+
Version: 1.0.5
|
|
4
|
+
Summary: git diff → published post. under 60 seconds.
|
|
5
|
+
Home-page: https://github.com/drizzy765/gitcast
|
|
6
|
+
Author: Timilehin Agoro
|
|
7
|
+
Author-email: agorotimilehi05@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
|
|
14
|
+
# Gitcast
|
|
15
|
+
|
|
16
|
+
On-demand, privacy-first desktop utility that captures active window context,
|
|
17
|
+
extracts code changes, and generates platform-ready X (Twitter) posts with one keystroke.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install gitcast
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
|
|
27
|
+
1. Install Tesseract OCR:
|
|
28
|
+
- Windows: https://github.com/UB-Mannheim/tesseract/wiki
|
|
29
|
+
- Mac: `brew install tesseract`
|
|
30
|
+
- Linux: `sudo apt install tesseract-ocr`
|
|
31
|
+
|
|
32
|
+
2. Add your API keys:
|
|
33
|
+
```bash
|
|
34
|
+
gitcast --setup
|
|
35
|
+
```
|
|
36
|
+
(opens `.env` file for editing)
|
|
37
|
+
|
|
38
|
+
3. Run:
|
|
39
|
+
```bash
|
|
40
|
+
gitcast
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Dashboard opens at http://127.0.0.1:8000
|
|
44
|
+
Press Ctrl+Shift+P from anywhere to capture.
|
|
45
|
+
|
|
46
|
+
## Stack
|
|
47
|
+
- Python 3.11+
|
|
48
|
+
- FastAPI, pynput, mss, Tesseract, Groq API, Tweepy
|
|
49
|
+
|
|
50
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[egg_info]
|
|
2
|
-
tag_build =
|
|
3
|
-
tag_date = 0
|
|
4
|
-
|
|
1
|
+
[egg_info]
|
|
2
|
+
tag_build =
|
|
3
|
+
tag_date = 0
|
|
4
|
+
|
|
@@ -19,7 +19,7 @@ if os.path.exists(requirements_path):
|
|
|
19
19
|
|
|
20
20
|
setup(
|
|
21
21
|
name="gitcast",
|
|
22
|
-
version="1.0.
|
|
22
|
+
version="1.0.5",
|
|
23
23
|
description="git diff → published post. under 60 seconds.",
|
|
24
24
|
long_description=long_description,
|
|
25
25
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
from supabase import Client, create_client, ClientOptions
|
|
3
|
-
|
|
3
|
+
import config.settings
|
|
4
4
|
|
|
5
5
|
_thread_local = threading.local()
|
|
6
6
|
|
|
7
7
|
def get_client() -> Client:
|
|
8
|
-
|
|
8
|
+
url = getattr(config.settings, "SUPABASE_URL", "")
|
|
9
|
+
key = getattr(config.settings, "SUPABASE_SERVICE_KEY", "")
|
|
10
|
+
if not url or not key:
|
|
9
11
|
raise RuntimeError("SUPABASE_URL and SUPABASE_SERVICE_KEY must be configured")
|
|
10
12
|
|
|
13
|
+
# Check if config changed and recreate if needed
|
|
14
|
+
if hasattr(_thread_local, "client"):
|
|
15
|
+
if getattr(_thread_local, "client_url", None) != url or getattr(_thread_local, "client_key", None) != key:
|
|
16
|
+
if hasattr(_thread_local, "client"):
|
|
17
|
+
delattr(_thread_local, "client")
|
|
18
|
+
|
|
11
19
|
if not hasattr(_thread_local, "client"):
|
|
12
20
|
_thread_local.client = create_client(
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
url,
|
|
22
|
+
key,
|
|
15
23
|
options=ClientOptions(
|
|
16
24
|
postgrest_client_timeout=10,
|
|
17
25
|
storage_client_timeout=10
|
|
18
26
|
)
|
|
19
27
|
)
|
|
28
|
+
_thread_local.client_url = url
|
|
29
|
+
_thread_local.client_key = key
|
|
20
30
|
return _thread_local.client
|
|
21
31
|
|
|
22
32
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
gitcast-1.0.4/tests/test_keys.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import httpx
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
# Add project root to path so we can import config
|
|
8
|
-
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
|
9
|
-
|
|
10
|
-
from config.settings import (
|
|
11
|
-
GROQ_API_KEY, GEMINI_API_KEY,
|
|
12
|
-
MOONSHOT_API_KEY, CEREBRAS_API_KEY, OPENROUTER_API_KEY
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
async def test_provider(name, url, headers, body, is_gemini=False):
|
|
16
|
-
print(f"Testing {name.upper()}...", end=" ", flush=True)
|
|
17
|
-
async with httpx.AsyncClient(timeout=10) as client:
|
|
18
|
-
try:
|
|
19
|
-
if is_gemini:
|
|
20
|
-
res = await client.post(f"{url}?key={GEMINI_API_KEY}", json=body)
|
|
21
|
-
else:
|
|
22
|
-
res = await client.post(url, headers=headers, json=body)
|
|
23
|
-
|
|
24
|
-
if res.status_code == 200:
|
|
25
|
-
print("✅ WORKING")
|
|
26
|
-
return True
|
|
27
|
-
else:
|
|
28
|
-
print(f"❌ FAILED ({res.status_code})")
|
|
29
|
-
try:
|
|
30
|
-
error_msg = res.json().get('error', {}).get('message', res.text[:100])
|
|
31
|
-
print(f" Reason: {error_msg}")
|
|
32
|
-
except:
|
|
33
|
-
print(f" Reason: {res.text[:100]}")
|
|
34
|
-
return False
|
|
35
|
-
except Exception as e:
|
|
36
|
-
print(f"❌ ERROR: {str(e)}")
|
|
37
|
-
return False
|
|
38
|
-
|
|
39
|
-
async def main():
|
|
40
|
-
print("=== Context Engine API Key Connectivity Test ===\n")
|
|
41
|
-
|
|
42
|
-
# 1. Groq
|
|
43
|
-
await test_provider(
|
|
44
|
-
"Groq",
|
|
45
|
-
"https://api.groq.com/openai/v1/chat/completions",
|
|
46
|
-
{"Authorization": f"Bearer {GROQ_API_KEY}"},
|
|
47
|
-
{"model": "llama-3.3-70b-versatile", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5}
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# 2. OpenRouter
|
|
51
|
-
await test_provider(
|
|
52
|
-
"OpenRouter",
|
|
53
|
-
"https://openrouter.ai/api/v1/chat/completions",
|
|
54
|
-
{"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
|
|
55
|
-
{"model": "qwen/qwen3-coder:free", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5}
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
# 3. Kimi (Moonshot)
|
|
59
|
-
await test_provider(
|
|
60
|
-
"Moonshot (Kimi)",
|
|
61
|
-
"https://api.moonshot.cn/v1/chat/completions",
|
|
62
|
-
{"Authorization": f"Bearer {MOONSHOT_API_KEY}"},
|
|
63
|
-
{"model": "moonshot-v1-8k", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5}
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
# 4. Cerebras
|
|
67
|
-
await test_provider(
|
|
68
|
-
"Cerebras",
|
|
69
|
-
"https://api.cerebras.ai/v1/chat/completions",
|
|
70
|
-
{"Authorization": f"Bearer {CEREBRAS_API_KEY}"},
|
|
71
|
-
{"model": "llama3.3-70b", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5}
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
# 5. Gemini
|
|
75
|
-
await test_provider(
|
|
76
|
-
"Gemini",
|
|
77
|
-
"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent",
|
|
78
|
-
{},
|
|
79
|
-
{"contents": [{"parts": [{"text": "hi"}]}]},
|
|
80
|
-
is_gemini=True
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
print("\nTest complete.")
|
|
84
|
-
|
|
85
|
-
if __name__ == "__main__":
|
|
86
|
-
asyncio.run(main())
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import asyncio
|
|
3
|
-
import sys
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
|
7
|
-
|
|
8
|
-
from api.payload import build_payload
|
|
9
|
-
from api.routes import generate_article, ArticleGenerateRequest
|
|
10
|
-
from config.settings import CURRENT_DRAFT
|
|
11
|
-
from fastapi import HTTPException
|
|
12
|
-
|
|
13
|
-
async def test_ocr_text_in_payload():
|
|
14
|
-
print("Testing ocr_text in build_payload...")
|
|
15
|
-
ocr_result = {"text": "Detected Text", "confidence": 0.9}
|
|
16
|
-
capture_result = {
|
|
17
|
-
"screenshot": {"path": "test.png", "timestamp": "123"},
|
|
18
|
-
"git_diff": {"diff": "some diff", "success": True}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
payload = build_payload(
|
|
22
|
-
raw_thought="my thought",
|
|
23
|
-
ocr_result=ocr_result,
|
|
24
|
-
capture_result=capture_result
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
assert "ocr_text" in payload
|
|
28
|
-
assert payload["ocr_text"] == "Detected Text"
|
|
29
|
-
print("✅ build_payload includes ocr_text")
|
|
30
|
-
|
|
31
|
-
async def test_generate_article_robustness():
|
|
32
|
-
print("Testing generate_article robustness with missing ocr_text in draft...")
|
|
33
|
-
original_draft = CURRENT_DRAFT.read_text(encoding="utf-8") if CURRENT_DRAFT.exists() else None
|
|
34
|
-
# Mock CURRENT_DRAFT
|
|
35
|
-
draft_data = {
|
|
36
|
-
"payload": {
|
|
37
|
-
"user_message": "hello",
|
|
38
|
-
# "ocr_text" is missing
|
|
39
|
-
"git_diff": "diff"
|
|
40
|
-
},
|
|
41
|
-
"variations": {},
|
|
42
|
-
"timestamp": "123",
|
|
43
|
-
"status": "ready"
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
with open(CURRENT_DRAFT, "w", encoding="utf-8") as f:
|
|
48
|
-
json.dump(draft_data, f)
|
|
49
|
-
|
|
50
|
-
request = ArticleGenerateRequest(include_codebase=False)
|
|
51
|
-
|
|
52
|
-
try:
|
|
53
|
-
# This will still try to call AI, but we want to see if it passes the user_msg construction
|
|
54
|
-
# We can't easily mock the AI call here without more setup,
|
|
55
|
-
# but the KeyError would happen BEFORE the AI call.
|
|
56
|
-
|
|
57
|
-
# To avoid actual AI call, we might just check the logic in routes.py
|
|
58
|
-
# by calling the function and expecting it to fail at AI call but NOT with KeyError.
|
|
59
|
-
await generate_article(request)
|
|
60
|
-
except HTTPException as e:
|
|
61
|
-
if "Article generation failed" in e.detail:
|
|
62
|
-
print("✅ generate_article passed KeyError (failed at AI call as expected)")
|
|
63
|
-
else:
|
|
64
|
-
print(f"❌ generate_article failed with: {e.detail}")
|
|
65
|
-
except KeyError as e:
|
|
66
|
-
print(f"❌ generate_article still has KeyError: {e}")
|
|
67
|
-
except Exception as e:
|
|
68
|
-
print(f"✅ generate_article passed KeyError (failed with: {e})")
|
|
69
|
-
finally:
|
|
70
|
-
if original_draft is None:
|
|
71
|
-
CURRENT_DRAFT.unlink(missing_ok=True)
|
|
72
|
-
else:
|
|
73
|
-
CURRENT_DRAFT.write_text(original_draft, encoding="utf-8")
|
|
74
|
-
|
|
75
|
-
if __name__ == "__main__":
|
|
76
|
-
asyncio.run(test_ocr_text_in_payload())
|
|
77
|
-
asyncio.run(test_generate_article_robustness())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|