gitcast 1.0.9__tar.gz → 1.0.11__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.11/.env.example +40 -0
- {gitcast-1.0.9/gitcast.egg-info → gitcast-1.0.11}/PKG-INFO +1 -1
- {gitcast-1.0.9 → gitcast-1.0.11}/api/routes.py +15 -24
- {gitcast-1.0.9 → gitcast-1.0.11}/api/server.py +20 -9
- gitcast-1.0.11/cli/.env.example +40 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/cli/gitcast.py +18 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/config/settings.py +23 -3
- {gitcast-1.0.9 → gitcast-1.0.11}/core/hotkey.py +1 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/pypi-errors.md +8 -0
- {gitcast-1.0.9 → gitcast-1.0.11/gitcast.egg-info}/PKG-INFO +1 -1
- {gitcast-1.0.9 → gitcast-1.0.11}/setup.py +1 -1
- {gitcast-1.0.9 → gitcast-1.0.11}/web/index.html +38 -6
- gitcast-1.0.9/.env.example +0 -26
- gitcast-1.0.9/cli/.env.example +0 -26
- {gitcast-1.0.9 → gitcast-1.0.11}/LICENSE +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/MANIFEST.in +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/README.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ai/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ai/formatter.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ai/generator.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ai/prompts.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ai/viral_patterns.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/analytics.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/auth.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/auth_middleware.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/auth_routes.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/monitoring.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/payload.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/ratelimit.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/api/validators.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/assets/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/assets/favicon-16x16.png +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/assets/favicon-32x32.png +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/assets/favicon-64x64.png +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/assets/favicon.ico +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/assets/icon.png +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/cli/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/config/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/capture.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/codebase_reader.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/framing.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/log_stream.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/ocr.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/screenshot_session.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/security.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/tray.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/core/trigger.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/api-keys.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/article-generator.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/capture-flow.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/changelog.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/contributing.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/dashboard.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/insights.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/introduction.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/post-formats.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/providers.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/quickstart.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/docs/sprint-mode.md +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/gitcast.egg-info/SOURCES.txt +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/gitcast.egg-info/dependency_links.txt +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/gitcast.egg-info/entry_points.txt +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/gitcast.egg-info/requires.txt +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/gitcast.egg-info/top_level.txt +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/publisher/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/publisher/clipboard.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/publisher/twitter.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/pyproject.toml +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/requirements.txt +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/setup.cfg +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/cleanup.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/engagement.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/insights.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/key_manager.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/logger.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/metrics.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/sprint.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/streak.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/supabase_client.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/storage/tone_memory.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/tests/test_keys.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/tests/test_ocr_fix.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ui/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ui/popup.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/ui/review.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/web/__init__.py +0 -0
- {gitcast-1.0.9 → gitcast-1.0.11}/web/landing.html +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────
|
|
2
|
+
# YOUR API KEYS (optional)
|
|
3
|
+
# Gitcast works out of the box with shared keys.
|
|
4
|
+
# Add your own keys for higher rate limits.
|
|
5
|
+
# Your key always takes priority over shared key.
|
|
6
|
+
# ─────────────────────────────────────────────
|
|
7
|
+
# Groq — primary AI inference
|
|
8
|
+
GROQ_API_KEY=
|
|
9
|
+
|
|
10
|
+
# DeepSeek & Moonshot — specialized/free-tier fallbacks
|
|
11
|
+
DEEPSEEK_API_KEY=
|
|
12
|
+
MOONSHOT_API_KEY=
|
|
13
|
+
CEREBRAS_API_KEY=
|
|
14
|
+
OPENROUTER_API_KEY=
|
|
15
|
+
|
|
16
|
+
# Gemini — vision fallback (when OCR confidence is too low)
|
|
17
|
+
GEMINI_API_KEY=
|
|
18
|
+
|
|
19
|
+
# Optional model overrides
|
|
20
|
+
GROQ_MODEL=llama-3.3-70b-versatile
|
|
21
|
+
DEEPSEEK_MODEL=deepseek-chat
|
|
22
|
+
MOONSHOT_MODEL=moonshot-v1-8k
|
|
23
|
+
GEMINI_MODEL=gemini-2.0-flash
|
|
24
|
+
CEREBRAS_MODEL=llama-3.3-70b
|
|
25
|
+
OPENROUTER_MODEL=meta-llama/llama-3.3-70b-instruct:free
|
|
26
|
+
|
|
27
|
+
# X (Twitter) API v2 — all five are required for posting
|
|
28
|
+
TWITTER_API_KEY=your_twitter_api_key_here
|
|
29
|
+
TWITTER_API_SECRET=your_twitter_api_secret_here
|
|
30
|
+
TWITTER_ACCESS_TOKEN=your_twitter_access_token_here
|
|
31
|
+
TWITTER_ACCESS_SECRET=your_twitter_access_secret_here
|
|
32
|
+
TWITTER_BEARER_TOKEN=your_twitter_bearer_token_here
|
|
33
|
+
|
|
34
|
+
# ─────────────────────────────────────────────
|
|
35
|
+
# DO NOT EDIT — Maintainer base keys
|
|
36
|
+
# These are set automatically by the package
|
|
37
|
+
# ─────────────────────────────────────────────
|
|
38
|
+
GITCAST_BASE_GROQ_KEY=
|
|
39
|
+
GITCAST_BASE_DEEPSEEK_KEY=
|
|
40
|
+
GITCAST_BASE_GEMINI_KEY=
|
|
@@ -542,32 +542,23 @@ def _update_settings_for_user(user_id: str, values: dict) -> dict:
|
|
|
542
542
|
|
|
543
543
|
@router.get("/keys/status")
|
|
544
544
|
def get_ai_keys_status(user_id: str = Depends(get_current_user)):
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
for provider, env_name in API_KEY_ENV_MAP.items()
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
response = (
|
|
557
|
-
get_client()
|
|
558
|
-
.table("api_keys")
|
|
559
|
-
.select("provider,key_preview,last_used_at,created_at")
|
|
560
|
-
.eq("user_id", user_id)
|
|
561
|
-
.execute()
|
|
545
|
+
from config.settings import (
|
|
546
|
+
GROQ_API_KEY,
|
|
547
|
+
DEEPSEEK_API_KEY,
|
|
548
|
+
GEMINI_API_KEY,
|
|
549
|
+
MOONSHOT_API_KEY,
|
|
550
|
+
CEREBRAS_API_KEY,
|
|
551
|
+
OPENROUTER_API_KEY,
|
|
552
|
+
USING_BASE_KEYS,
|
|
562
553
|
)
|
|
563
|
-
saved = {row["provider"]: row for row in response.data or []}
|
|
564
554
|
return {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
555
|
+
"groq": bool(GROQ_API_KEY),
|
|
556
|
+
"deepseek": bool(DEEPSEEK_API_KEY),
|
|
557
|
+
"gemini": bool(GEMINI_API_KEY),
|
|
558
|
+
"kimi": bool(MOONSHOT_API_KEY),
|
|
559
|
+
"cerebras": bool(CEREBRAS_API_KEY),
|
|
560
|
+
"openrouter": bool(OPENROUTER_API_KEY),
|
|
561
|
+
"using_base_keys": USING_BASE_KEYS,
|
|
571
562
|
}
|
|
572
563
|
|
|
573
564
|
|
|
@@ -11,7 +11,7 @@ from api.auth_routes import router as auth_router
|
|
|
11
11
|
from config.settings import missing_api_keys, BASE_DIR, STORAGE_DIR, CONFIG_DIR
|
|
12
12
|
from api.monitoring import init_sentry
|
|
13
13
|
from api.ratelimit import limiter
|
|
14
|
-
from api.auth import get_token
|
|
14
|
+
from api.auth import get_token as get_auth_token
|
|
15
15
|
|
|
16
16
|
# ── App setup ─────────────────────────────────────────────────────────────────
|
|
17
17
|
|
|
@@ -86,7 +86,7 @@ app.include_router(auth_router, prefix="/auth")
|
|
|
86
86
|
|
|
87
87
|
@app.on_event("startup")
|
|
88
88
|
def startup_event():
|
|
89
|
-
print(f"\n[Auth] Session Token: {
|
|
89
|
+
print(f"\n[Auth] Session Token: {get_auth_token()}")
|
|
90
90
|
print("[Server] Starting Gitcast API on http://127.0.0.1:8000")
|
|
91
91
|
|
|
92
92
|
|
|
@@ -126,12 +126,23 @@ def health_check():
|
|
|
126
126
|
|
|
127
127
|
# ── Auth token retrieval (Localhost only) ───────────────────────────────────────
|
|
128
128
|
|
|
129
|
+
def is_localhost(host: str) -> bool:
|
|
130
|
+
return host in [
|
|
131
|
+
"127.0.0.1",
|
|
132
|
+
"::1",
|
|
133
|
+
"localhost",
|
|
134
|
+
"::ffff:127.0.0.1",
|
|
135
|
+
]
|
|
136
|
+
|
|
129
137
|
@app.get("/api/token")
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
async def get_token(request: Request):
|
|
139
|
+
host = request.client.host \
|
|
140
|
+
if request.client else ""
|
|
141
|
+
if not is_localhost(host):
|
|
142
|
+
raise HTTPException(status_code=403,
|
|
143
|
+
detail="localhost only")
|
|
144
|
+
from api.auth import get_token as _get_token
|
|
145
|
+
return {"token": _get_token()}
|
|
135
146
|
|
|
136
147
|
|
|
137
148
|
# ── Entry point ───────────────────────────────────────────────────────────────
|
|
@@ -141,11 +152,11 @@ def start_server():
|
|
|
141
152
|
# Write session token to config/session_token.txt
|
|
142
153
|
try:
|
|
143
154
|
token_file = CONFIG_DIR / "session_token.txt"
|
|
144
|
-
token_file.write_text(
|
|
155
|
+
token_file.write_text(get_auth_token(), encoding="utf-8")
|
|
145
156
|
except Exception as e:
|
|
146
157
|
print(f"[Server] Failed to write session token to file: {e}")
|
|
147
158
|
|
|
148
|
-
print(f"\n[Auth] Session Token: {
|
|
159
|
+
print(f"\n[Auth] Session Token: {get_auth_token()}")
|
|
149
160
|
print("[Server] Starting Gitcast API on http://127.0.0.1:8000")
|
|
150
161
|
uvicorn.run(
|
|
151
162
|
"api.server:app",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────
|
|
2
|
+
# YOUR API KEYS (optional)
|
|
3
|
+
# Gitcast works out of the box with shared keys.
|
|
4
|
+
# Add your own keys for higher rate limits.
|
|
5
|
+
# Your key always takes priority over shared key.
|
|
6
|
+
# ─────────────────────────────────────────────
|
|
7
|
+
# Groq — primary AI inference
|
|
8
|
+
GROQ_API_KEY=
|
|
9
|
+
|
|
10
|
+
# DeepSeek & Moonshot — specialized/free-tier fallbacks
|
|
11
|
+
DEEPSEEK_API_KEY=
|
|
12
|
+
MOONSHOT_API_KEY=
|
|
13
|
+
CEREBRAS_API_KEY=
|
|
14
|
+
OPENROUTER_API_KEY=
|
|
15
|
+
|
|
16
|
+
# Gemini — vision fallback (when OCR confidence is too low)
|
|
17
|
+
GEMINI_API_KEY=
|
|
18
|
+
|
|
19
|
+
# Optional model overrides
|
|
20
|
+
GROQ_MODEL=llama-3.3-70b-versatile
|
|
21
|
+
DEEPSEEK_MODEL=deepseek-chat
|
|
22
|
+
MOONSHOT_MODEL=moonshot-v1-8k
|
|
23
|
+
GEMINI_MODEL=gemini-2.0-flash
|
|
24
|
+
CEREBRAS_MODEL=llama-3.3-70b
|
|
25
|
+
OPENROUTER_MODEL=meta-llama/llama-3.3-70b-instruct:free
|
|
26
|
+
|
|
27
|
+
# X (Twitter) API v2 — all five are required for posting
|
|
28
|
+
TWITTER_API_KEY=your_twitter_api_key_here
|
|
29
|
+
TWITTER_API_SECRET=your_twitter_api_secret_here
|
|
30
|
+
TWITTER_ACCESS_TOKEN=your_twitter_access_token_here
|
|
31
|
+
TWITTER_ACCESS_SECRET=your_twitter_access_secret_here
|
|
32
|
+
TWITTER_BEARER_TOKEN=your_twitter_bearer_token_here
|
|
33
|
+
|
|
34
|
+
# ─────────────────────────────────────────────
|
|
35
|
+
# DO NOT EDIT — Maintainer base keys
|
|
36
|
+
# These are set automatically by the package
|
|
37
|
+
# ─────────────────────────────────────────────
|
|
38
|
+
GITCAST_BASE_GROQ_KEY=
|
|
39
|
+
GITCAST_BASE_DEEPSEEK_KEY=
|
|
40
|
+
GITCAST_BASE_GEMINI_KEY=
|
|
@@ -67,6 +67,24 @@ def main():
|
|
|
67
67
|
# open browser
|
|
68
68
|
webbrowser.open("http://localhost:8000")
|
|
69
69
|
|
|
70
|
+
# ASCII Logo
|
|
71
|
+
print(" ██████╗ ██╗████████╗ ██████╗ █████╗ ███████╗████████╗")
|
|
72
|
+
print("██╔════╝ ██║╚══██╔══╝██╔════╝ ██╔══██╗ ██╔════╝╚══██╔══╝")
|
|
73
|
+
print("██║ ███╗██║ ██║ ██║ ███████║ ███████╗ ██║ ")
|
|
74
|
+
print("██║ ██║██║ ██║ ██║ ██╔══██║ ╚════██║ ██║ ")
|
|
75
|
+
print("╚██████╔╝██║ ██║ ╚██████╗ ██║ ██║ ███████║ ██║ ")
|
|
76
|
+
print(" ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ")
|
|
77
|
+
|
|
78
|
+
from config.settings import USING_BASE_KEYS
|
|
79
|
+
|
|
80
|
+
if USING_BASE_KEYS:
|
|
81
|
+
print("> [OK] using Gitcast shared API key")
|
|
82
|
+
print("> // want your own key? run: "
|
|
83
|
+
"gitcast --setup")
|
|
84
|
+
print("> // get free key: console.groq.com")
|
|
85
|
+
else:
|
|
86
|
+
print("> [OK] using your configured API keys")
|
|
87
|
+
|
|
70
88
|
print("[OK] server running at http://localhost:8000")
|
|
71
89
|
print("[OK] browser opened")
|
|
72
90
|
|
|
@@ -84,13 +84,16 @@ API_KEY_ENV_MAP = {
|
|
|
84
84
|
"kimi": "MOONSHOT_API_KEY",
|
|
85
85
|
"cerebras": "CEREBRAS_API_KEY",
|
|
86
86
|
"openrouter": "OPENROUTER_API_KEY",
|
|
87
|
+
"deepseek": "DEEPSEEK_API_KEY",
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
GROQ_API_KEY = ""
|
|
91
|
+
DEEPSEEK_API_KEY = ""
|
|
90
92
|
MOONSHOT_API_KEY = ""
|
|
91
93
|
GEMINI_API_KEY = ""
|
|
92
94
|
CEREBRAS_API_KEY = ""
|
|
93
95
|
OPENROUTER_API_KEY = ""
|
|
96
|
+
USING_BASE_KEYS = False
|
|
94
97
|
GROQ_MODEL = ""
|
|
95
98
|
MOONSHOT_MODEL = ""
|
|
96
99
|
GEMINI_MODEL = ""
|
|
@@ -100,10 +103,12 @@ OPENROUTER_MODEL = ""
|
|
|
100
103
|
|
|
101
104
|
def reload_api_keys() -> None:
|
|
102
105
|
global GROQ_API_KEY
|
|
106
|
+
global DEEPSEEK_API_KEY
|
|
103
107
|
global MOONSHOT_API_KEY
|
|
104
108
|
global GEMINI_API_KEY
|
|
105
109
|
global CEREBRAS_API_KEY
|
|
106
110
|
global OPENROUTER_API_KEY
|
|
111
|
+
global USING_BASE_KEYS
|
|
107
112
|
global GROQ_MODEL
|
|
108
113
|
global MOONSHOT_MODEL
|
|
109
114
|
global GEMINI_MODEL
|
|
@@ -116,11 +121,26 @@ def reload_api_keys() -> None:
|
|
|
116
121
|
global SUPABASE_JWT_AUDIENCE
|
|
117
122
|
|
|
118
123
|
load_all_dotenvs(override=True)
|
|
119
|
-
|
|
124
|
+
|
|
125
|
+
# Base keys — maintainer's keys as default fallback
|
|
126
|
+
# Users can override by setting their own in .env
|
|
127
|
+
_BASE_GROQ = os.getenv("GITCAST_BASE_GROQ_KEY", "")
|
|
128
|
+
_BASE_DEEPSEEK = os.getenv("GITCAST_BASE_DEEPSEEK_KEY", "")
|
|
129
|
+
_BASE_GEMINI = os.getenv("GITCAST_BASE_GEMINI_KEY", "")
|
|
130
|
+
|
|
131
|
+
# User key takes priority, falls back to base key
|
|
132
|
+
GROQ_API_KEY = os.getenv("GROQ_API_KEY") or _BASE_GROQ
|
|
133
|
+
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY") or _BASE_DEEPSEEK
|
|
134
|
+
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") or _BASE_GEMINI
|
|
120
135
|
MOONSHOT_API_KEY = os.getenv("MOONSHOT_API_KEY", "")
|
|
121
|
-
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "")
|
|
122
|
-
CEREBRAS_API_KEY = os.getenv("CEREBRAS_API_KEY", "")
|
|
123
136
|
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
|
|
137
|
+
CEREBRAS_API_KEY = os.getenv("CEREBRAS_API_KEY", "")
|
|
138
|
+
|
|
139
|
+
# Flag so the app knows which mode it's in
|
|
140
|
+
USING_BASE_KEYS = (
|
|
141
|
+
not os.getenv("GROQ_API_KEY") and bool(_BASE_GROQ)
|
|
142
|
+
)
|
|
143
|
+
|
|
124
144
|
GROQ_MODEL = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
|
|
125
145
|
MOONSHOT_MODEL = os.getenv("MOONSHOT_MODEL", "moonshot-v1-8k")
|
|
126
146
|
GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.0-flash")
|
|
@@ -80,4 +80,12 @@ This document records the issues faced during the testing, packaging, and execut
|
|
|
80
80
|
```
|
|
81
81
|
* **Status**: Logged and waiting for future triage. The application starts, but key bindings or endpoints triggers are failing to generate the actual post output.
|
|
82
82
|
|
|
83
|
+
### Issue 2.4: 401 Unauthorized API Errors and Failure to Generate Posts on Directory/Venv Switch (gitcast==1.0.10)
|
|
84
|
+
* **Symptom**: Running `gitcast` from a different project directory (`snakegame`) using a different virtual environment leads to `api/keys/status:1 Failed to load resource: 401 (Unauthorized)` in the browser console, and post generation does not trigger.
|
|
85
|
+
* **Root Cause**:
|
|
86
|
+
1. **Token Authentication Bypass Failure**: The browser's attempt to retrieve the active session token from `/api/token` returns `403 Forbidden` if the request host is mapped as an IPv4-mapped IPv6 address (e.g. `::ffff:127.0.0.1`). The frontend then falls back to using the old `localStorage` token from a previous run (e.g. in `context-engine`), which the FastAPI backend rejects with a `401 Unauthorized` status.
|
|
87
|
+
2. **Wrong Working Directory Resolution**: If the target directory (`snakegame`) is not a Git repository, `detect_working_directory()` falls back to candidates in the list, matching the parent `context-engine` directory. Diffs and history are scanned from the wrong project, preventing post generation for the active context.
|
|
88
|
+
* **Status**: Logged in detail in [pypit-error.md](file:///mnt/c/Users/USER/Documents/context-engine/pypit-error.md) and awaiting resolution.
|
|
89
|
+
|
|
90
|
+
|
|
83
91
|
|
|
@@ -51,7 +51,7 @@ if os.path.exists(requirements_path):
|
|
|
51
51
|
|
|
52
52
|
setup(
|
|
53
53
|
name="gitcast",
|
|
54
|
-
version="1.0.
|
|
54
|
+
version="1.0.11",
|
|
55
55
|
description="git diff → published post. under 60 seconds.",
|
|
56
56
|
long_description=long_description,
|
|
57
57
|
long_description_content_type="text/markdown",
|
|
@@ -1984,8 +1984,11 @@
|
|
|
1984
1984
|
<span class="provider-name">OPENROUTER</span>
|
|
1985
1985
|
<span class="provider-status" id="status-openrouter">[ACTIVE]</span>
|
|
1986
1986
|
</div>
|
|
1987
|
-
|
|
1988
|
-
|
|
1987
|
+
</div>
|
|
1988
|
+
<div id="shared-key-note" class="shared-key-note hidden" style="font-size: 10px; color: #ffb800; margin-top: 8px; font-family: var(--font-mono);">
|
|
1989
|
+
// using shared key · add yours: gitcast --setup
|
|
1990
|
+
</div>
|
|
1991
|
+
</div>
|
|
1989
1992
|
|
|
1990
1993
|
<div class="sidebar-spacer"></div>
|
|
1991
1994
|
<div class="sidebar-auth-container">
|
|
@@ -2653,20 +2656,49 @@
|
|
|
2653
2656
|
if (keys) {
|
|
2654
2657
|
state.keys = keys;
|
|
2655
2658
|
const onlineProviders = [];
|
|
2659
|
+
const usingBaseKeys = !!keys.using_base_keys;
|
|
2660
|
+
|
|
2656
2661
|
KEY_PROVIDERS.forEach(k => {
|
|
2657
2662
|
const el = document.getElementById(`l-${k}`);
|
|
2658
2663
|
const lbl = document.getElementById(`status-${k}`);
|
|
2659
2664
|
const active = !!keys[k];
|
|
2660
2665
|
|
|
2661
|
-
if (el)
|
|
2666
|
+
if (el) {
|
|
2667
|
+
el.classList.toggle('online', active);
|
|
2668
|
+
if (active) {
|
|
2669
|
+
if (k === 'groq' && usingBaseKeys) {
|
|
2670
|
+
el.style.color = '#ffb800'; // Amber for shared key
|
|
2671
|
+
} else {
|
|
2672
|
+
el.style.color = ''; // Use CSS default
|
|
2673
|
+
}
|
|
2674
|
+
} else {
|
|
2675
|
+
el.style.color = '';
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2662
2678
|
if (lbl) {
|
|
2663
|
-
|
|
2664
|
-
|
|
2679
|
+
if (k === 'groq' && usingBaseKeys) {
|
|
2680
|
+
lbl.innerText = '[SHARED]';
|
|
2681
|
+
lbl.style.color = '#ffb800';
|
|
2682
|
+
} else {
|
|
2683
|
+
lbl.innerText = active ? '[ACTIVE]' : '[MISSING]';
|
|
2684
|
+
lbl.style.color = active ? 'var(--accent-green)' : 'var(--text-secondary)';
|
|
2685
|
+
}
|
|
2665
2686
|
}
|
|
2666
2687
|
if (active) {
|
|
2667
2688
|
onlineProviders.push(k.toUpperCase());
|
|
2668
2689
|
}
|
|
2669
2690
|
});
|
|
2691
|
+
|
|
2692
|
+
// Toggle note display
|
|
2693
|
+
const noteEl = document.getElementById('shared-key-note');
|
|
2694
|
+
if (noteEl) {
|
|
2695
|
+
if (usingBaseKeys) {
|
|
2696
|
+
noteEl.classList.remove('hidden');
|
|
2697
|
+
} else {
|
|
2698
|
+
noteEl.classList.add('hidden');
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2670
2702
|
document.getElementById('status-bar-providers').innerText = onlineProviders.length > 0 ? `[${onlineProviders.join('/')}]` : '[NO_AI]';
|
|
2671
2703
|
}
|
|
2672
2704
|
const guide = await api('/api/keys/guide');
|
|
@@ -3626,7 +3658,7 @@
|
|
|
3626
3658
|
function renderApiKeyRows() {
|
|
3627
3659
|
return KEY_PROVIDERS.map(provider => {
|
|
3628
3660
|
const guide = state.keyGuide[provider] || {};
|
|
3629
|
-
const active = !!state.keys[provider]?.configured;
|
|
3661
|
+
const active = state.keys[provider]?.configured !== undefined ? !!state.keys[provider]?.configured : !!state.keys[provider];
|
|
3630
3662
|
const status = active ? '[ACTIVE ✓]' : '[NOT_CONFIGURED]';
|
|
3631
3663
|
const statusClass = active ? 'active' : 'missing';
|
|
3632
3664
|
const preview = active ? '[●●●●●●●●...set]' : '[no_key_saved]';
|
gitcast-1.0.9/.env.example
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Groq — primary AI inference
|
|
2
|
-
GROQ_API_KEY=your_groq_api_key_here
|
|
3
|
-
|
|
4
|
-
# DeepSeek & Moonshot — specialized/free-tier fallbacks
|
|
5
|
-
DEEPSEEK_API_KEY=your_deepseek_api_key_here
|
|
6
|
-
MOONSHOT_API_KEY=your_moonshot_api_key_here
|
|
7
|
-
CEREBRAS_API_KEY=your_cerebras_api_key_here
|
|
8
|
-
OPENROUTER_API_KEY=your_openrouter_api_key_here
|
|
9
|
-
|
|
10
|
-
# Gemini — vision fallback (when OCR confidence is too low)
|
|
11
|
-
GEMINI_API_KEY=your_gemini_api_key_here
|
|
12
|
-
|
|
13
|
-
# Optional model overrides
|
|
14
|
-
GROQ_MODEL=llama-3.3-70b-versatile
|
|
15
|
-
DEEPSEEK_MODEL=deepseek-chat
|
|
16
|
-
MOONSHOT_MODEL=moonshot-v1-8k
|
|
17
|
-
GEMINI_MODEL=gemini-2.0-flash
|
|
18
|
-
CEREBRAS_MODEL=llama-3.3-70b
|
|
19
|
-
OPENROUTER_MODEL=meta-llama/llama-3.3-70b-instruct:free
|
|
20
|
-
|
|
21
|
-
# X (Twitter) API v2 — all five are required for posting
|
|
22
|
-
TWITTER_API_KEY=your_twitter_api_key_here
|
|
23
|
-
TWITTER_API_SECRET=your_twitter_api_secret_here
|
|
24
|
-
TWITTER_ACCESS_TOKEN=your_twitter_access_token_here
|
|
25
|
-
TWITTER_ACCESS_SECRET=your_twitter_access_secret_here
|
|
26
|
-
TWITTER_BEARER_TOKEN=your_twitter_bearer_token_here
|
gitcast-1.0.9/cli/.env.example
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Groq — primary AI inference
|
|
2
|
-
GROQ_API_KEY=your_groq_api_key_here
|
|
3
|
-
|
|
4
|
-
# DeepSeek & Moonshot — specialized/free-tier fallbacks
|
|
5
|
-
DEEPSEEK_API_KEY=your_deepseek_api_key_here
|
|
6
|
-
MOONSHOT_API_KEY=your_moonshot_api_key_here
|
|
7
|
-
CEREBRAS_API_KEY=your_cerebras_api_key_here
|
|
8
|
-
OPENROUTER_API_KEY=your_openrouter_api_key_here
|
|
9
|
-
|
|
10
|
-
# Gemini — vision fallback (when OCR confidence is too low)
|
|
11
|
-
GEMINI_API_KEY=your_gemini_api_key_here
|
|
12
|
-
|
|
13
|
-
# Optional model overrides
|
|
14
|
-
GROQ_MODEL=llama-3.3-70b-versatile
|
|
15
|
-
DEEPSEEK_MODEL=deepseek-chat
|
|
16
|
-
MOONSHOT_MODEL=moonshot-v1-8k
|
|
17
|
-
GEMINI_MODEL=gemini-2.0-flash
|
|
18
|
-
CEREBRAS_MODEL=llama-3.3-70b
|
|
19
|
-
OPENROUTER_MODEL=meta-llama/llama-3.3-70b-instruct:free
|
|
20
|
-
|
|
21
|
-
# X (Twitter) API v2 — all five are required for posting
|
|
22
|
-
TWITTER_API_KEY=your_twitter_api_key_here
|
|
23
|
-
TWITTER_API_SECRET=your_twitter_api_secret_here
|
|
24
|
-
TWITTER_ACCESS_TOKEN=your_twitter_access_token_here
|
|
25
|
-
TWITTER_ACCESS_SECRET=your_twitter_access_secret_here
|
|
26
|
-
TWITTER_BEARER_TOKEN=your_twitter_bearer_token_here
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|