synapse-orch-ai 1.8.2 → 1.8.4
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.
- package/backend/core/config.py +2 -2
- package/backend/core/json_store.py +2 -2
- package/backend/core/mcp_client.py +4 -4
- package/backend/core/messaging/store.py +2 -2
- package/backend/core/orchestration/state.py +7 -3
- package/backend/core/orchestration/steps.py +3 -3
- package/backend/core/personal_details.py +2 -2
- package/backend/core/react_engine.py +1 -1
- package/backend/core/routes/auth.py +20 -5
- package/backend/core/routes/data.py +6 -6
- package/backend/core/routes/import_export.py +2 -2
- package/backend/core/routes/settings.py +6 -6
- package/backend/core/routes/tools.py +1 -1
- package/backend/core/scale/context.py +1 -1
- package/backend/core/scale/sync.py +1 -1
- package/backend/core/server.py +3 -3
- package/backend/core/tools.py +1 -1
- package/backend/services/code_indexer.py +2 -2
- package/backend/services/google.py +7 -7
- package/backend/services/synthetic_data.py +1 -1
- package/backend/tools/bash.py +1 -1
- package/backend/tools/code_search.py +2 -2
- package/backend/tools/sandbox.py +1 -1
- package/backend/tools/sql_agent.py +1 -1
- package/frontend-build/.next/BUILD_ID +1 -1
- package/frontend-build/.next/build-manifest.json +3 -3
- package/frontend-build/.next/prerender-manifest.json +3 -3
- package/frontend-build/.next/server/app/_global-error.html +1 -1
- package/frontend-build/.next/server/app/_global-error.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.html +1 -1
- package/frontend-build/.next/server/app/_not-found.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/frontend-build/.next/server/app/index.html +1 -1
- package/frontend-build/.next/server/app/index.rsc +1 -1
- package/frontend-build/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/frontend-build/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/frontend-build/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/frontend-build/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/frontend-build/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/frontend-build/.next/server/app/login.html +1 -1
- package/frontend-build/.next/server/app/login.rsc +2 -2
- package/frontend-build/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/frontend-build/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/login.segments/_index.segment.rsc +1 -1
- package/frontend-build/.next/server/app/login.segments/_tree.segment.rsc +1 -1
- package/frontend-build/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/frontend-build/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/frontend-build/.next/server/chunks/[root-of-the-server]__07vf6z1._.js +1 -1
- package/frontend-build/.next/server/chunks/[root-of-the-server]__0ncun1_._.js +1 -1
- package/frontend-build/.next/server/chunks/ssr/src_app_login_page_tsx_0jt.sij._.js +1 -1
- package/frontend-build/.next/server/middleware-build-manifest.js +3 -3
- package/frontend-build/.next/server/pages/404.html +1 -1
- package/frontend-build/.next/server/pages/500.html +1 -1
- package/frontend-build/.next/server/server-reference-manifest.js +1 -1
- package/frontend-build/.next/server/server-reference-manifest.json +1 -1
- package/frontend-build/.next/static/chunks/0f8pg6_soqwt2.js +1 -0
- package/package.json +1 -1
- package/frontend-build/.next/static/chunks/0e0doloxq75td.js +0 -1
- /package/frontend-build/.next/static/{6T9aJqheS49PfQ_4MbqiJ → WjvzoOqaNaXuc-wYMNArI}/_buildManifest.js +0 -0
- /package/frontend-build/.next/static/{6T9aJqheS49PfQ_4MbqiJ → WjvzoOqaNaXuc-wYMNArI}/_clientMiddlewareManifest.js +0 -0
- /package/frontend-build/.next/static/{6T9aJqheS49PfQ_4MbqiJ → WjvzoOqaNaXuc-wYMNArI}/_ssgManifest.js +0 -0
package/backend/core/config.py
CHANGED
|
@@ -129,7 +129,7 @@ def load_settings():
|
|
|
129
129
|
file_settings = {}
|
|
130
130
|
else:
|
|
131
131
|
try:
|
|
132
|
-
with open(SETTINGS_FILE, 'r') as f:
|
|
132
|
+
with open(SETTINGS_FILE, 'r', encoding="utf-8") as f:
|
|
133
133
|
file_settings = json.load(f)
|
|
134
134
|
except Exception as e:
|
|
135
135
|
print(f"DEBUG: Error loading settings: {e}")
|
|
@@ -168,7 +168,7 @@ def get_or_create_jwt_secret() -> str:
|
|
|
168
168
|
|
|
169
169
|
if env_file.exists():
|
|
170
170
|
try:
|
|
171
|
-
for line in env_file.read_text().splitlines():
|
|
171
|
+
for line in env_file.read_text(encoding="utf-8").splitlines():
|
|
172
172
|
line = line.strip()
|
|
173
173
|
if line.startswith(f"{var}=") and len(line) > len(f"{var}="):
|
|
174
174
|
val = line.split("=", 1)[1].strip()
|
|
@@ -32,7 +32,7 @@ class JsonStore:
|
|
|
32
32
|
if not os.path.exists(self.path):
|
|
33
33
|
return self.default_factory()
|
|
34
34
|
try:
|
|
35
|
-
with open(self.path, "r") as f:
|
|
35
|
+
with open(self.path, "r", encoding="utf-8") as f:
|
|
36
36
|
data = json.load(f)
|
|
37
37
|
if self._cache_ttl > 0:
|
|
38
38
|
self._cache = data
|
|
@@ -43,7 +43,7 @@ class JsonStore:
|
|
|
43
43
|
|
|
44
44
|
def save(self, data):
|
|
45
45
|
with self._lock:
|
|
46
|
-
with open(self.path, "w") as f:
|
|
46
|
+
with open(self.path, "w", encoding="utf-8") as f:
|
|
47
47
|
json.dump(data, f, indent=4)
|
|
48
48
|
if self._cache_ttl > 0:
|
|
49
49
|
self._cache = data
|
|
@@ -69,14 +69,14 @@ class FileTokenStorage(TokenStorage):
|
|
|
69
69
|
if not os.path.exists(path):
|
|
70
70
|
return None
|
|
71
71
|
try:
|
|
72
|
-
with open(path) as f:
|
|
72
|
+
with open(path, encoding="utf-8") as f:
|
|
73
73
|
return json.load(f)
|
|
74
74
|
except Exception:
|
|
75
75
|
return None
|
|
76
76
|
|
|
77
77
|
def _write(self, path: str, data: dict):
|
|
78
78
|
os.makedirs(MCP_TOKENS_DIR, exist_ok=True)
|
|
79
|
-
with open(path, "w") as f:
|
|
79
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
80
80
|
json.dump(data, f, indent=2)
|
|
81
81
|
|
|
82
82
|
async def get_tokens(self) -> Optional[OAuthToken]:
|
|
@@ -181,7 +181,7 @@ class MCPClientManager:
|
|
|
181
181
|
if not os.path.exists(MCP_SERVERS_FILE):
|
|
182
182
|
return []
|
|
183
183
|
try:
|
|
184
|
-
with open(MCP_SERVERS_FILE) as f:
|
|
184
|
+
with open(MCP_SERVERS_FILE, encoding="utf-8") as f:
|
|
185
185
|
return json.load(f)
|
|
186
186
|
except Exception as e:
|
|
187
187
|
print(f"Error loading MCP servers config: {e}")
|
|
@@ -189,7 +189,7 @@ class MCPClientManager:
|
|
|
189
189
|
|
|
190
190
|
def save_servers(self):
|
|
191
191
|
os.makedirs(os.path.dirname(MCP_SERVERS_FILE), exist_ok=True)
|
|
192
|
-
with open(MCP_SERVERS_FILE, "w") as f:
|
|
192
|
+
with open(MCP_SERVERS_FILE, "w", encoding="utf-8") as f:
|
|
193
193
|
json.dump(self.servers_config, f, indent=4)
|
|
194
194
|
|
|
195
195
|
def _set_status(self, name: str, status: str):
|
|
@@ -17,7 +17,7 @@ def _load_raw() -> list[dict]:
|
|
|
17
17
|
if not os.path.exists(CHANNELS_FILE):
|
|
18
18
|
return []
|
|
19
19
|
try:
|
|
20
|
-
with open(CHANNELS_FILE, "r") as f:
|
|
20
|
+
with open(CHANNELS_FILE, "r", encoding="utf-8") as f:
|
|
21
21
|
data = json.load(f)
|
|
22
22
|
return data if isinstance(data, list) else []
|
|
23
23
|
except Exception:
|
|
@@ -26,7 +26,7 @@ def _load_raw() -> list[dict]:
|
|
|
26
26
|
|
|
27
27
|
def _save_raw(data: list[dict]) -> None:
|
|
28
28
|
os.makedirs(DATA_DIR, exist_ok=True)
|
|
29
|
-
with open(CHANNELS_FILE, "w") as f:
|
|
29
|
+
with open(CHANNELS_FILE, "w", encoding="utf-8") as f:
|
|
30
30
|
json.dump(data, f, indent=2)
|
|
31
31
|
|
|
32
32
|
|
|
@@ -41,7 +41,11 @@ class SharedState:
|
|
|
41
41
|
# Atomic write: write to temp, then rename
|
|
42
42
|
fd, tmp_path = tempfile.mkstemp(dir=RUNS_DIR, suffix=".tmp")
|
|
43
43
|
try:
|
|
44
|
-
|
|
44
|
+
# encoding="utf-8" is required: model_dump_json emits raw non-ASCII
|
|
45
|
+
# characters, and without an explicit encoding os.fdopen uses the
|
|
46
|
+
# platform default (cp1252 on Windows), which raises
|
|
47
|
+
# UnicodeEncodeError when a run's state contains non-ASCII text.
|
|
48
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
45
49
|
f.write(self.run.model_dump_json(indent=2))
|
|
46
50
|
os.replace(tmp_path, target)
|
|
47
51
|
except Exception:
|
|
@@ -55,7 +59,7 @@ class SharedState:
|
|
|
55
59
|
path = RUNS_DIR / f"{run_id}.json"
|
|
56
60
|
if not path.exists():
|
|
57
61
|
raise FileNotFoundError(f"No checkpoint found for run {run_id}")
|
|
58
|
-
data = json.loads(path.read_text())
|
|
62
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
59
63
|
run = OrchestrationRun.model_validate(data)
|
|
60
64
|
return cls(run)
|
|
61
65
|
|
|
@@ -66,7 +70,7 @@ class SharedState:
|
|
|
66
70
|
runs = []
|
|
67
71
|
for f in sorted(RUNS_DIR.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True)[:limit]:
|
|
68
72
|
try:
|
|
69
|
-
data = json.loads(f.read_text())
|
|
73
|
+
data = json.loads(f.read_text(encoding="utf-8"))
|
|
70
74
|
runs.append({
|
|
71
75
|
"run_id": data.get("run_id"),
|
|
72
76
|
"orchestration_id": data.get("orchestration_id"),
|
|
@@ -493,7 +493,7 @@ class ToolStepExecutor:
|
|
|
493
493
|
tmp_dir = tempfile.mkdtemp(prefix="pytool_")
|
|
494
494
|
script_path = f"{tmp_dir}/script.py"
|
|
495
495
|
try:
|
|
496
|
-
with open(script_path, "w") as f:
|
|
496
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
497
497
|
f.write(injected_code)
|
|
498
498
|
|
|
499
499
|
docker_cmd = [
|
|
@@ -1161,7 +1161,7 @@ class TransformStepExecutor:
|
|
|
1161
1161
|
tmp_dir = tempfile.mkdtemp(prefix="transform_host_")
|
|
1162
1162
|
script_path = str(Path(tmp_dir) / "transform.py")
|
|
1163
1163
|
try:
|
|
1164
|
-
with open(script_path, "w") as f:
|
|
1164
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
1165
1165
|
f.write(script_content)
|
|
1166
1166
|
|
|
1167
1167
|
proc = await asyncio.create_subprocess_exec(
|
|
@@ -1210,7 +1210,7 @@ class TransformStepExecutor:
|
|
|
1210
1210
|
tmp_dir = tempfile.mkdtemp(prefix="transform_")
|
|
1211
1211
|
script_path = f"{tmp_dir}/transform.py"
|
|
1212
1212
|
try:
|
|
1213
|
-
with open(script_path, "w") as f:
|
|
1213
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
1214
1214
|
f.write(script_content)
|
|
1215
1215
|
|
|
1216
1216
|
docker_cmd = [
|
|
@@ -29,7 +29,7 @@ def load_personal_details() -> dict:
|
|
|
29
29
|
return defaults
|
|
30
30
|
|
|
31
31
|
try:
|
|
32
|
-
with open(PERSONAL_DETAILS_FILE, "r") as f:
|
|
32
|
+
with open(PERSONAL_DETAILS_FILE, "r", encoding="utf-8") as f:
|
|
33
33
|
data = json.load(f)
|
|
34
34
|
|
|
35
35
|
# Merge root
|
|
@@ -54,7 +54,7 @@ def save_personal_details(details: dict) -> dict:
|
|
|
54
54
|
addr = d.get("address") if isinstance(d.get("address"), dict) else {}
|
|
55
55
|
normalized["address"] = {**defaults["address"], **addr}
|
|
56
56
|
|
|
57
|
-
with open(PERSONAL_DETAILS_FILE, "w") as f:
|
|
57
|
+
with open(PERSONAL_DETAILS_FILE, "w", encoding="utf-8") as f:
|
|
58
58
|
json.dump(normalized, f, indent=4)
|
|
59
59
|
|
|
60
60
|
return normalized
|
|
@@ -1208,7 +1208,7 @@ async def run_agent_step(
|
|
|
1208
1208
|
tmp_dir = _tempfile.mkdtemp(prefix="pytool_")
|
|
1209
1209
|
script_path = f"{tmp_dir}/script.py"
|
|
1210
1210
|
try:
|
|
1211
|
-
with open(script_path, "w") as _f:
|
|
1211
|
+
with open(script_path, "w", encoding="utf-8") as _f:
|
|
1212
1212
|
_f.write(injected_code)
|
|
1213
1213
|
|
|
1214
1214
|
docker_cmd = [
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
Authentication routes: Google OAuth + Synapse login gate.
|
|
3
3
|
"""
|
|
4
4
|
import os
|
|
5
|
-
from fastapi import APIRouter, HTTPException
|
|
5
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
6
6
|
from fastapi.responses import RedirectResponse, JSONResponse
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
from services.google import get_auth_url, finish_auth
|
|
9
9
|
from core.config import load_settings
|
|
10
|
-
from core.user_auth import verify_password, create_session_token
|
|
10
|
+
from core.user_auth import verify_password, create_session_token, verify_session_token
|
|
11
11
|
|
|
12
12
|
router = APIRouter()
|
|
13
13
|
|
|
@@ -37,8 +37,17 @@ class LoginRequest(BaseModel):
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
@router.get("/api/auth/status")
|
|
40
|
-
async def auth_status():
|
|
41
|
-
"""Returns whether login is enabled and
|
|
40
|
+
async def auth_status(request: Request):
|
|
41
|
+
"""Returns whether login is enabled/configured and whether THIS request's
|
|
42
|
+
session token is valid.
|
|
43
|
+
|
|
44
|
+
The proxy (frontend/src/proxy.ts) forwards the `synapse_session` cookie via
|
|
45
|
+
the `X-Synapse-Session` header. The backend is the single owner of
|
|
46
|
+
SYNAPSE_JWT_SECRET, so `authenticated` is the authoritative gate when the
|
|
47
|
+
proxy's local JWT fast-path can't verify (e.g. the frontend process has a
|
|
48
|
+
different/empty secret). `verify_session_token` never raises, so a missing
|
|
49
|
+
backend secret yields `authenticated: false` rather than a 500.
|
|
50
|
+
"""
|
|
42
51
|
s = load_settings()
|
|
43
52
|
login_enabled = s.get("login_enabled", False)
|
|
44
53
|
login_configured = bool(
|
|
@@ -46,7 +55,13 @@ async def auth_status():
|
|
|
46
55
|
and s.get("login_username")
|
|
47
56
|
and s.get("login_password_hash")
|
|
48
57
|
)
|
|
49
|
-
|
|
58
|
+
session = request.headers.get("X-Synapse-Session", "")
|
|
59
|
+
authenticated = bool(session) and verify_session_token(session) is not None
|
|
60
|
+
return {
|
|
61
|
+
"login_enabled": login_enabled,
|
|
62
|
+
"login_configured": login_configured,
|
|
63
|
+
"authenticated": authenticated,
|
|
64
|
+
}
|
|
50
65
|
|
|
51
66
|
|
|
52
67
|
@router.post("/api/auth/login")
|
|
@@ -120,7 +120,7 @@ async def _get_github_token() -> "str | None":
|
|
|
120
120
|
hosts_path = config_dir / "hosts.json"
|
|
121
121
|
if hosts_path.exists():
|
|
122
122
|
try:
|
|
123
|
-
data = _json.loads(hosts_path.read_text())
|
|
123
|
+
data = _json.loads(hosts_path.read_text(encoding="utf-8"))
|
|
124
124
|
t = (data.get("github.com", {}).get("oauth_token")
|
|
125
125
|
or data.get("github.com", {}).get("token"))
|
|
126
126
|
if t:
|
|
@@ -131,7 +131,7 @@ async def _get_github_token() -> "str | None":
|
|
|
131
131
|
config_path = Path.home() / ".copilot" / "config.json"
|
|
132
132
|
if config_path.exists():
|
|
133
133
|
try:
|
|
134
|
-
data = _json.loads(config_path.read_text())
|
|
134
|
+
data = _json.loads(config_path.read_text(encoding="utf-8"))
|
|
135
135
|
if t := (data.get("oauth_token") or data.get("token")):
|
|
136
136
|
return t
|
|
137
137
|
except Exception:
|
|
@@ -839,7 +839,7 @@ async def clear_memory_items(req: MemoryClearRequest):
|
|
|
839
839
|
repos = []
|
|
840
840
|
if os.path.exists(repos_path):
|
|
841
841
|
try:
|
|
842
|
-
with open(repos_path) as f:
|
|
842
|
+
with open(repos_path, encoding="utf-8") as f:
|
|
843
843
|
repos = json.load(f)
|
|
844
844
|
except Exception:
|
|
845
845
|
pass
|
|
@@ -849,7 +849,7 @@ async def clear_memory_items(req: MemoryClearRequest):
|
|
|
849
849
|
drop_index(repo["id"])
|
|
850
850
|
except Exception as e:
|
|
851
851
|
print(f"Error dropping index for repo {repo.get('id')}: {e}")
|
|
852
|
-
with open(repos_path, "w") as f:
|
|
852
|
+
with open(repos_path, "w", encoding="utf-8") as f:
|
|
853
853
|
json.dump([], f)
|
|
854
854
|
results["repos"] = f"Cleared {len(repos)} repo(s)"
|
|
855
855
|
|
|
@@ -859,11 +859,11 @@ async def clear_memory_items(req: MemoryClearRequest):
|
|
|
859
859
|
configs = []
|
|
860
860
|
if os.path.exists(configs_path):
|
|
861
861
|
try:
|
|
862
|
-
with open(configs_path) as f:
|
|
862
|
+
with open(configs_path, encoding="utf-8") as f:
|
|
863
863
|
configs = json.load(f)
|
|
864
864
|
except Exception:
|
|
865
865
|
pass
|
|
866
|
-
with open(configs_path, "w") as f:
|
|
866
|
+
with open(configs_path, "w", encoding="utf-8") as f:
|
|
867
867
|
json.dump([], f)
|
|
868
868
|
results["db_configs"] = f"Cleared {len(configs)} config(s)"
|
|
869
869
|
|
|
@@ -48,7 +48,7 @@ def _load_mcp_servers() -> List[dict]:
|
|
|
48
48
|
if not os.path.exists(MCP_SERVERS_FILE):
|
|
49
49
|
return []
|
|
50
50
|
try:
|
|
51
|
-
with open(MCP_SERVERS_FILE) as f:
|
|
51
|
+
with open(MCP_SERVERS_FILE, encoding="utf-8") as f:
|
|
52
52
|
return json.load(f)
|
|
53
53
|
except Exception:
|
|
54
54
|
return []
|
|
@@ -253,7 +253,7 @@ async def import_bundle(req: ImportRequest):
|
|
|
253
253
|
|
|
254
254
|
if mcp_changed:
|
|
255
255
|
os.makedirs(os.path.dirname(MCP_SERVERS_FILE), exist_ok=True)
|
|
256
|
-
with open(MCP_SERVERS_FILE, "w") as f:
|
|
256
|
+
with open(MCP_SERVERS_FILE, "w", encoding="utf-8") as f:
|
|
257
257
|
json.dump(existing_mcp, f, indent=4)
|
|
258
258
|
|
|
259
259
|
# Always sync manager with authoritative disk state and attempt connections
|
|
@@ -270,7 +270,7 @@ async def upload_google_creds(request: Request):
|
|
|
270
270
|
else:
|
|
271
271
|
parsed = data
|
|
272
272
|
|
|
273
|
-
with open(CREDENTIALS_FILE, 'w') as f:
|
|
273
|
+
with open(CREDENTIALS_FILE, 'w', encoding="utf-8") as f:
|
|
274
274
|
json.dump(parsed, f, indent=4)
|
|
275
275
|
|
|
276
276
|
return {"status": "success", "message": "Credentials saved successfully."}
|
|
@@ -290,7 +290,7 @@ async def upload_google_token(request: Request):
|
|
|
290
290
|
else:
|
|
291
291
|
parsed = data
|
|
292
292
|
|
|
293
|
-
with open(TOKEN_FILE, 'w') as f:
|
|
293
|
+
with open(TOKEN_FILE, 'w', encoding="utf-8") as f:
|
|
294
294
|
json.dump(parsed, f, indent=4)
|
|
295
295
|
|
|
296
296
|
return {"status": "success", "message": "Token saved successfully."}
|
|
@@ -308,7 +308,7 @@ async def get_config():
|
|
|
308
308
|
return {"has_credentials": False, "is_connected": False}
|
|
309
309
|
|
|
310
310
|
try:
|
|
311
|
-
with open(CREDENTIALS_FILE, 'r') as f:
|
|
311
|
+
with open(CREDENTIALS_FILE, 'r', encoding="utf-8") as f:
|
|
312
312
|
creds = json.load(f)
|
|
313
313
|
app_info = creds.get("web") or creds.get("installed", {})
|
|
314
314
|
|
|
@@ -320,7 +320,7 @@ async def get_config():
|
|
|
320
320
|
user_email = None
|
|
321
321
|
if has_token:
|
|
322
322
|
try:
|
|
323
|
-
with open(TOKEN_FILE, 'r') as tf:
|
|
323
|
+
with open(TOKEN_FILE, 'r', encoding="utf-8") as tf:
|
|
324
324
|
token_data = json.load(tf)
|
|
325
325
|
user_email = token_data.get("id_token_hint") or token_data.get("email")
|
|
326
326
|
# google-auth stores it in the token as a raw JWT — try to decode the id_token
|
|
@@ -382,7 +382,7 @@ async def get_examples():
|
|
|
382
382
|
if not os.path.exists(index_path):
|
|
383
383
|
return []
|
|
384
384
|
try:
|
|
385
|
-
with open(index_path, "r") as f:
|
|
385
|
+
with open(index_path, "r", encoding="utf-8") as f:
|
|
386
386
|
return json.load(f)
|
|
387
387
|
except Exception as e:
|
|
388
388
|
raise HTTPException(status_code=500, detail=f"Failed to load examples index: {e}")
|
|
@@ -400,7 +400,7 @@ async def get_example_bundle(example_id: str):
|
|
|
400
400
|
if not os.path.exists(bundle_path):
|
|
401
401
|
raise HTTPException(status_code=404, detail=f"Example pack '{example_id}' not found")
|
|
402
402
|
try:
|
|
403
|
-
with open(bundle_path, "r") as f:
|
|
403
|
+
with open(bundle_path, "r", encoding="utf-8") as f:
|
|
404
404
|
return json.load(f)
|
|
405
405
|
except Exception as e:
|
|
406
406
|
raise HTTPException(status_code=500, detail=f"Failed to load example bundle: {e}")
|
|
@@ -307,7 +307,7 @@ async def test_python_tool(req: PythonTestRequest):
|
|
|
307
307
|
script_path = os.path.join(tmp_dir, "script.py")
|
|
308
308
|
|
|
309
309
|
try:
|
|
310
|
-
with open(script_path, "w") as f:
|
|
310
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
311
311
|
f.write(injected_code)
|
|
312
312
|
|
|
313
313
|
cmd = [
|
|
@@ -86,7 +86,7 @@ async def resolve_mcp_servers() -> list[dict]:
|
|
|
86
86
|
try:
|
|
87
87
|
from core.mcp_client import MCP_SERVERS_FILE
|
|
88
88
|
import json
|
|
89
|
-
with open(MCP_SERVERS_FILE) as f:
|
|
89
|
+
with open(MCP_SERVERS_FILE, encoding="utf-8") as f:
|
|
90
90
|
return json.load(f)
|
|
91
91
|
except Exception:
|
|
92
92
|
return []
|
|
@@ -188,7 +188,7 @@ async def sync_mcp_servers_to_pg(
|
|
|
188
188
|
Sensitive fields (token) are stripped before storing."""
|
|
189
189
|
try:
|
|
190
190
|
from core.mcp_client import MCP_SERVERS_FILE
|
|
191
|
-
with open(MCP_SERVERS_FILE) as f:
|
|
191
|
+
with open(MCP_SERVERS_FILE, encoding="utf-8") as f:
|
|
192
192
|
items = json.load(f)
|
|
193
193
|
if not isinstance(items, list):
|
|
194
194
|
items = []
|
package/backend/core/server.py
CHANGED
|
@@ -97,7 +97,7 @@ def _get_repo_paths() -> list[str]:
|
|
|
97
97
|
if not REPOS_FILE.exists():
|
|
98
98
|
return []
|
|
99
99
|
try:
|
|
100
|
-
repos = json.loads(REPOS_FILE.read_text())
|
|
100
|
+
repos = json.loads(REPOS_FILE.read_text(encoding="utf-8"))
|
|
101
101
|
return [r["path"] for r in repos if r.get("path") and os.path.isdir(r["path"])]
|
|
102
102
|
except Exception as e:
|
|
103
103
|
print(f"Warning: Could not load repo paths: {e}")
|
|
@@ -121,7 +121,7 @@ def _get_google_oauth_env() -> dict[str, str]:
|
|
|
121
121
|
except Exception as e:
|
|
122
122
|
print(f"Warning: Token refresh at startup failed: {e}")
|
|
123
123
|
try:
|
|
124
|
-
creds = json.loads(creds_file.read_text())
|
|
124
|
+
creds = json.loads(creds_file.read_text(encoding="utf-8"))
|
|
125
125
|
installed = creds.get("installed", creds.get("web", {}))
|
|
126
126
|
client_id = installed.get("client_id", "")
|
|
127
127
|
client_secret = installed.get("client_secret", "")
|
|
@@ -139,7 +139,7 @@ def _get_google_oauth_env() -> dict[str, str]:
|
|
|
139
139
|
if token_file.exists():
|
|
140
140
|
try:
|
|
141
141
|
import base64
|
|
142
|
-
token_data = json.loads(token_file.read_text())
|
|
142
|
+
token_data = json.loads(token_file.read_text(encoding="utf-8"))
|
|
143
143
|
email = token_data.get("email")
|
|
144
144
|
if not email and token_data.get("token"):
|
|
145
145
|
id_token = token_data["token"]
|
package/backend/core/tools.py
CHANGED
|
@@ -269,7 +269,7 @@ def build_system_prompt(agent_system_template, tools_json, session_id, session_s
|
|
|
269
269
|
from core.config import TOKEN_FILE
|
|
270
270
|
import os
|
|
271
271
|
if os.path.exists(TOKEN_FILE):
|
|
272
|
-
with open(TOKEN_FILE, "r") as f:
|
|
272
|
+
with open(TOKEN_FILE, "r", encoding="utf-8") as f:
|
|
273
273
|
token_data = json.load(f)
|
|
274
274
|
google_email = token_data.get("email")
|
|
275
275
|
if not google_email and token_data.get("id_token"):
|
|
@@ -425,13 +425,13 @@ def _update_repo_status(repo_id: str, **fields):
|
|
|
425
425
|
if not os.path.exists(repos_file):
|
|
426
426
|
return
|
|
427
427
|
with _repos_lock:
|
|
428
|
-
with open(repos_file, "r") as f:
|
|
428
|
+
with open(repos_file, "r", encoding="utf-8") as f:
|
|
429
429
|
repos = json.load(f)
|
|
430
430
|
for r in repos:
|
|
431
431
|
if r["id"] == repo_id:
|
|
432
432
|
r.update(fields)
|
|
433
433
|
break
|
|
434
|
-
with open(repos_file, "w") as f:
|
|
434
|
+
with open(repos_file, "w", encoding="utf-8") as f:
|
|
435
435
|
json.dump(repos, f, indent=4)
|
|
436
436
|
|
|
437
437
|
|
|
@@ -85,10 +85,10 @@ def _sync_token_to_mcp(token_data: dict, email: str | None = None) -> None:
|
|
|
85
85
|
"""Mirror token.json into google-credentials/ so workspace-mcp sees fresh creds."""
|
|
86
86
|
try:
|
|
87
87
|
os.makedirs(GOOGLE_CREDENTIALS_DIR, exist_ok=True)
|
|
88
|
-
with open(GOOGLE_CREDENTIALS_DIR / "token.json", "w") as f:
|
|
88
|
+
with open(GOOGLE_CREDENTIALS_DIR / "token.json", "w", encoding="utf-8") as f:
|
|
89
89
|
json.dump(token_data, f, indent=2)
|
|
90
90
|
if email:
|
|
91
|
-
with open(GOOGLE_CREDENTIALS_DIR / f"{email}.json", "w") as f:
|
|
91
|
+
with open(GOOGLE_CREDENTIALS_DIR / f"{email}.json", "w", encoding="utf-8") as f:
|
|
92
92
|
json.dump(token_data, f, indent=2)
|
|
93
93
|
except Exception as e:
|
|
94
94
|
print(f"Warning: Failed to sync token to workspace-mcp dir: {e}")
|
|
@@ -108,7 +108,7 @@ def get_google_credentials():
|
|
|
108
108
|
creds.refresh(Request())
|
|
109
109
|
# Preserve fields like `email` that aren't part of creds.to_json()
|
|
110
110
|
try:
|
|
111
|
-
with open(TOKEN_FILE, "r") as f:
|
|
111
|
+
with open(TOKEN_FILE, "r", encoding="utf-8") as f:
|
|
112
112
|
prior = json.load(f)
|
|
113
113
|
except Exception:
|
|
114
114
|
prior = {}
|
|
@@ -117,7 +117,7 @@ def get_google_credentials():
|
|
|
117
117
|
merged = {**prior, **refreshed}
|
|
118
118
|
if email:
|
|
119
119
|
merged["email"] = email
|
|
120
|
-
with open(TOKEN_FILE, "w") as token:
|
|
120
|
+
with open(TOKEN_FILE, "w", encoding="utf-8") as token:
|
|
121
121
|
json.dump(merged, token, indent=2)
|
|
122
122
|
_sync_token_to_mcp(merged, email)
|
|
123
123
|
return creds
|
|
@@ -190,7 +190,7 @@ def finish_auth(code, redirect_uri):
|
|
|
190
190
|
except Exception as e:
|
|
191
191
|
print(f"Warning: Could not fetch user email after OAuth: {e}")
|
|
192
192
|
|
|
193
|
-
with open(TOKEN_FILE, "w") as token:
|
|
193
|
+
with open(TOKEN_FILE, "w", encoding="utf-8") as token:
|
|
194
194
|
json.dump(token_data, token, indent=2)
|
|
195
195
|
|
|
196
196
|
print(f"DEBUG: token.json saved to {TOKEN_FILE}")
|
|
@@ -205,12 +205,12 @@ def finish_auth(code, redirect_uri):
|
|
|
205
205
|
# 2. Save the token with the email formatted name
|
|
206
206
|
if email:
|
|
207
207
|
mcp_token_path = os.path.join(mcp_cred_dir, f"{email}.json")
|
|
208
|
-
with open(mcp_token_path, "w") as token:
|
|
208
|
+
with open(mcp_token_path, "w", encoding="utf-8") as token:
|
|
209
209
|
json.dump(token_data, token, indent=2)
|
|
210
210
|
print(f"DEBUG: Synchronized token to workspace-mcp via {mcp_token_path}")
|
|
211
211
|
# 3. Always save generic token.json just in case
|
|
212
212
|
mcp_generic_token = os.path.join(mcp_cred_dir, "token.json")
|
|
213
|
-
with open(mcp_generic_token, "w") as token:
|
|
213
|
+
with open(mcp_generic_token, "w", encoding="utf-8") as token:
|
|
214
214
|
json.dump(token_data, token, indent=2)
|
|
215
215
|
|
|
216
216
|
except Exception as e:
|
|
@@ -122,7 +122,7 @@ async def generate_synthetic_data(request: SyntheticDataRequest):
|
|
|
122
122
|
record = json.loads(json_str)
|
|
123
123
|
|
|
124
124
|
# Append to file immediatey (streaming save)
|
|
125
|
-
with open(filepath, "a") as f:
|
|
125
|
+
with open(filepath, "a", encoding="utf-8") as f:
|
|
126
126
|
f.write(json.dumps(record) + "\n")
|
|
127
127
|
|
|
128
128
|
current_job["completed"] += 1
|
package/backend/tools/bash.py
CHANGED
|
@@ -56,7 +56,7 @@ def _get_allowed_dirs() -> list[str]:
|
|
|
56
56
|
repos_file = Path(DATA_DIR) / "repos.json"
|
|
57
57
|
if repos_file.exists():
|
|
58
58
|
try:
|
|
59
|
-
repos = json.loads(repos_file.read_text())
|
|
59
|
+
repos = json.loads(repos_file.read_text(encoding="utf-8"))
|
|
60
60
|
dirs += [
|
|
61
61
|
r["path"]
|
|
62
62
|
for r in repos
|
|
@@ -20,7 +20,7 @@ def _load_repo_paths() -> dict[str, str]:
|
|
|
20
20
|
try:
|
|
21
21
|
from core.config import DATA_DIR
|
|
22
22
|
repos_file = os.path.join(DATA_DIR, "repos.json")
|
|
23
|
-
with open(repos_file) as f:
|
|
23
|
+
with open(repos_file, encoding="utf-8") as f:
|
|
24
24
|
repos = json.load(f)
|
|
25
25
|
return {r["id"]: r["path"].rstrip("/") for r in repos if r.get("id") and r.get("path")}
|
|
26
26
|
except Exception:
|
|
@@ -113,7 +113,7 @@ def _load_indexed_repo_ids() -> list[str]:
|
|
|
113
113
|
try:
|
|
114
114
|
from core.config import DATA_DIR
|
|
115
115
|
repos_file = os.path.join(DATA_DIR, "repos.json")
|
|
116
|
-
with open(repos_file) as f:
|
|
116
|
+
with open(repos_file, encoding="utf-8") as f:
|
|
117
117
|
repos = json.load(f)
|
|
118
118
|
return [r["id"] for r in repos if r.get("status") == "indexed" and r.get("id")]
|
|
119
119
|
except Exception:
|
package/backend/tools/sandbox.py
CHANGED
|
@@ -635,7 +635,7 @@ async def _handle_execute(args: dict) -> list[TextContent]:
|
|
|
635
635
|
script_path = os.path.join(tmp_dir, "script.py")
|
|
636
636
|
|
|
637
637
|
try:
|
|
638
|
-
with open(script_path, "w") as f:
|
|
638
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
639
639
|
f.write(code)
|
|
640
640
|
|
|
641
641
|
docker_cmd = _build_docker_cmd(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
WjvzoOqaNaXuc-wYMNArI
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"static/chunks/03~yq9q893hmn.js"
|
|
8
8
|
],
|
|
9
9
|
"lowPriorityFiles": [
|
|
10
|
-
"static/
|
|
11
|
-
"static/
|
|
12
|
-
"static/
|
|
10
|
+
"static/WjvzoOqaNaXuc-wYMNArI/_buildManifest.js",
|
|
11
|
+
"static/WjvzoOqaNaXuc-wYMNArI/_ssgManifest.js",
|
|
12
|
+
"static/WjvzoOqaNaXuc-wYMNArI/_clientMiddlewareManifest.js"
|
|
13
13
|
],
|
|
14
14
|
"rootMainFiles": [
|
|
15
15
|
"static/chunks/0qmh881w772ef.js",
|
|
@@ -131,8 +131,8 @@
|
|
|
131
131
|
"dynamicRoutes": {},
|
|
132
132
|
"notFoundRoutes": [],
|
|
133
133
|
"preview": {
|
|
134
|
-
"previewModeId": "
|
|
135
|
-
"previewModeSigningKey": "
|
|
136
|
-
"previewModeEncryptionKey": "
|
|
134
|
+
"previewModeId": "1c0685efa521b0dbf779b45854109763",
|
|
135
|
+
"previewModeSigningKey": "b6cc2030a87c67166aa72e4b6b47c2a8a7458d02ab4755889ca0ec472d0d48b7",
|
|
136
|
+
"previewModeEncryptionKey": "fd5063f9886ee40652ea64012174055070a38c72eee5ec20b08bfcaa2039c4b0"
|
|
137
137
|
}
|
|
138
138
|
}
|