mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-20 11:42:23 +02:00
[eric] app builder: fix windows-built apps failing by always creating the workspace .env (the windows build dropped the .env.example it seeds from) and writing app files as utf-8 (windows cp1252 choked on arrows and chess pieces); stays on 1.1.69 to rebuild only the windows installer
This commit is contained in:
@@ -230,7 +230,7 @@ def ensure_webapp_workspace_seeded_and_registered(
|
||||
from backend.apps.outputs.runtime import _find_free_port
|
||||
frontend_port = _find_free_port()
|
||||
seed_webapp_template_workspace(folder, frontend_port)
|
||||
with open(os.path.join(folder, "SKILL.md"), "w") as f:
|
||||
with open(os.path.join(folder, "SKILL.md"), "w", encoding="utf-8") as f:
|
||||
f.write(load_app_builder_skill())
|
||||
existing = [o for o in _load_all() if o.workspace_id == workspace_id]
|
||||
if existing:
|
||||
@@ -311,11 +311,11 @@ async def seed_workspace(body: WorkspaceSeedRequest):
|
||||
# SKILL.md still goes in workspace root; agent reads it for
|
||||
# context. Live content (user-editable via Skills page) is
|
||||
# injected into the system prompt regardless.
|
||||
with open(os.path.join(folder, "SKILL.md"), "w") as f:
|
||||
with open(os.path.join(folder, "SKILL.md"), "w", encoding="utf-8") as f:
|
||||
f.write(load_app_builder_skill())
|
||||
meta = body.meta or {}
|
||||
if body.meta and not already_seeded:
|
||||
with open(os.path.join(folder, "meta.json"), "w") as f:
|
||||
with open(os.path.join(folder, "meta.json"), "w", encoding="utf-8") as f:
|
||||
json.dump(body.meta, f, indent=2)
|
||||
# Create (or look up) the Output record so the app appears in
|
||||
# the Apps sidebar the moment the user kicks off generation.
|
||||
@@ -360,12 +360,12 @@ async def seed_workspace(body: WorkspaceSeedRequest):
|
||||
if not full_path.startswith(os.path.normpath(folder)):
|
||||
continue
|
||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||
with open(full_path, "w") as f:
|
||||
with open(full_path, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
else:
|
||||
for rel_path, content in VIEW_TEMPLATE_FILES.items():
|
||||
full_path = os.path.join(folder, rel_path)
|
||||
with open(full_path, "w") as f:
|
||||
with open(full_path, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
# Seed the workspace's SKILL.md with the LIVE skill content so an
|
||||
@@ -374,11 +374,11 @@ async def seed_workspace(body: WorkspaceSeedRequest):
|
||||
# already-seeded workspaces (the system-prompt injection in
|
||||
# agent_manager reads live, so the agent always has the latest
|
||||
# rules regardless of this on-disk copy).
|
||||
with open(os.path.join(folder, "SKILL.md"), "w") as f:
|
||||
with open(os.path.join(folder, "SKILL.md"), "w", encoding="utf-8") as f:
|
||||
f.write(load_app_builder_skill())
|
||||
|
||||
if body.meta:
|
||||
with open(os.path.join(folder, "meta.json"), "w") as f:
|
||||
with open(os.path.join(folder, "meta.json"), "w", encoding="utf-8") as f:
|
||||
json.dump(body.meta, f, indent=2)
|
||||
|
||||
return {"path": os.path.abspath(folder), "template_mode": "flat"}
|
||||
@@ -494,7 +494,7 @@ async def write_workspace_file(workspace_id: str, filepath: str, body: dict):
|
||||
if full_path != folder_norm and not full_path.startswith(folder_norm + os.sep):
|
||||
raise HTTPException(status_code=403, detail="Path traversal not allowed")
|
||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||
with open(full_path, "w") as f:
|
||||
with open(full_path, "w", encoding="utf-8") as f:
|
||||
f.write(body.get("content", ""))
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@@ -467,7 +467,7 @@ def _ensure_warm_python_venv() -> str | None:
|
||||
logger.warning("warm-venv pip install failed: %s", r.stderr[-1500:])
|
||||
return None
|
||||
|
||||
with open(sentinel, "w") as fh:
|
||||
with open(sentinel, "w", encoding="utf-8") as fh:
|
||||
fh.write("ok\n")
|
||||
logger.info("webapp-template: warm backend venv ready at %s", venv_dir)
|
||||
return venv_dir
|
||||
@@ -568,6 +568,16 @@ def seed_webapp_template_workspace(workspace_dir: str, frontend_port: int) -> No
|
||||
src_example = os.path.join(WEBAPP_TEMPLATE_DIR, ".env.example")
|
||||
if os.path.exists(src_example):
|
||||
shutil.copyfile(src_example, env_path)
|
||||
else:
|
||||
# .env.example can be absent from a packaged build whose copy step
|
||||
# stripped dotfiles (the Windows build's recursive '.env.*' exclude did
|
||||
# exactly this). Write the default directly so the workspace always has
|
||||
# a .env with BACKEND_PORT=NONE; without it run.sh sees no BACKEND_PORT,
|
||||
# takes the backend branch, and dies on a backend that isn't there,
|
||||
# leaving the app stuck on the splash. Mac was unaffected because its
|
||||
# build anchors the exclude and ships .env.example.
|
||||
with open(env_path, "w", encoding="utf-8") as f:
|
||||
f.write("BACKEND_PORT=NONE\nFRONTEND_PORT=4949\n")
|
||||
|
||||
_patch_env_port(env_path, "FRONTEND_PORT", str(frontend_port))
|
||||
_patch_env_port(env_example_path, "FRONTEND_PORT", str(frontend_port))
|
||||
|
||||
@@ -39,8 +39,14 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ "${BACKEND_PORT}" == "NONE" ]]; then
|
||||
echo "BACKEND_PORT=NONE — running frontend only (no backend)."
|
||||
if [[ "${BACKEND_PORT}" == "NONE" || -z "${BACKEND_PORT}" || ! -f "$ROOT_DIR/backend/run.sh" ]]; then
|
||||
# Frontend-only is the safe default: BACKEND_PORT=NONE (frontend-only app),
|
||||
# OR unset/empty (e.g. .env missing — never start a backend that isn't
|
||||
# configured), OR there is genuinely no backend/run.sh to run. Without the
|
||||
# last two guards an unset BACKEND_PORT fell through to the backend branch
|
||||
# and `bash backend/run.sh` died with "No such file or directory", tearing
|
||||
# the whole app down before the frontend could show.
|
||||
echo "Running frontend only (no backend configured)."
|
||||
echo ""
|
||||
|
||||
bash "$ROOT_DIR/frontend/run.sh" 2>&1 | awk '{printf "\033[32m[frontend]\033[0m %s\n", $0; fflush()}' &
|
||||
|
||||
@@ -329,6 +329,20 @@ function Copy-Excluded($Source, $Dest, $Exclude) {
|
||||
Copy-Excluded `
|
||||
(Join-Path $ProjectRoot 'backend') (Join-Path $Staging 'backend') `
|
||||
@{ Dirs = @('__pycache__','.venv','data','uv-bin','tests'); Files = @('*.pyc','.env','.env.*') }
|
||||
# The '.env.*' exclude above is recursive, so it also strips the vendored
|
||||
# webapp_template/.env.example that seed_workspace copies into each new app's
|
||||
# .env (BACKEND_PORT=NONE). The mac build anchors its exclude to avoid this;
|
||||
# here we restore the one file. Without it, Windows-built apps seed with no
|
||||
# .env, run.sh takes the backend branch, and the app dies on a missing backend.
|
||||
# (seed_workspace also now writes a default .env when this is absent, but
|
||||
# shipping it keeps the template snapshot complete and matches mac.)
|
||||
$EnvExampleSrc = Join-Path $ProjectRoot 'backend\apps\outputs\webapp_template\.env.example'
|
||||
$EnvExampleDst = Join-Path $Staging 'backend\apps\outputs\webapp_template\.env.example'
|
||||
if (Test-Path $EnvExampleSrc) {
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path $EnvExampleDst -Parent) | Out-Null
|
||||
Copy-Item -Force $EnvExampleSrc $EnvExampleDst
|
||||
Write-Host "Restored webapp_template/.env.example (stripped by the .env.* exclude)"
|
||||
}
|
||||
# data: backend/config/paths.py points DATA_ROOT at %APPDATA%/OpenSwarm/data in
|
||||
# packaged mode and no code seeds from the bundle, so the entire shipped
|
||||
# backend/data/ tree was dead weight (and was leaking the dev machine's
|
||||
|
||||
Reference in New Issue
Block a user