Compare commits

..
8 Commits
Author SHA1 Message Date
leehc991028andClaude Fable 5.1 823292cbb6 docs: Windows msi 빌드 팀원 가이드 — 준비물·서버 주소·빌드·설치·핫키·막힐 때
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 20:39:43 +09:00
leehc991028andClaude Fable 5.1 acb6cefbcc fix(tauri): 맥 번들에 http 허용(ATS) — 서버가 http 라 앱이 요청을 못 보내던 것
macOS 는 앱 안 비암호화 요청을 기본 차단. src-tauri/Info.plist 로 NSAllowsArbitraryLoads 를
번들에 합침. Windows 는 무관.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 20:36:19 +09:00
leehc991028andClaude Fable 5.1 5cb7d74efa chore(tauri): 0.1.4 — 우리 서버(20.200.170.37:8080)용 빌드
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 20:33:35 +09:00
lee-hyeon-cheolandClaude Fable 5.1 996f1c853e docs(orders): 2026-09-22 정리 — 한 것·다음 할 일·접속 메모
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 18:34:04 +09:00
lee-hyeon-cheolandClaude Fable 5.1 526f4e4545 chore: 작업로그·문서 갱신 (9/22 Phoenix·배포 스크립트·우리 서버)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 18:33:04 +09:00
lee-hyeon-cheolandClaude Fable 5.1 8a7df11d9c docs: 우리 서버 배포 경로·Phoenix 주소·NSG 메모
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 18:17:39 +09:00
lee-hyeon-cheolandClaude Fable 5.1 9c064615a9 fix(deploy): 컨테이너→같은 서버 Phoenix 는 host.docker.internal(host-gateway) — 공인 IP 는 컨테이너 안에서 안 닿음
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 18:15:42 +09:00
lee-hyeon-cheolandClaude Fable 5.1 ea1fd350da feat(deploy): 도커 배포(deploy/docker: compose+Dockerfile+deploy.sh) — 우리 환경용. 렌더러가 FabriX/OpenRouter 템플릿 자동 선택, 호스트 변수화. bare deploy.sh 는 git pull 포함. Phoenix 도커 compose
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 18:09:48 +09:00
20 changed files with 386 additions and 14 deletions
+1 -1
View File
@@ -485,7 +485,7 @@ dependencies = [
[[package]] [[package]]
name = "codeassist-tauri" name = "codeassist-tauri"
version = "0.1.3" version = "0.1.4"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"png 0.17.16", "png 0.17.16",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "codeassist-tauri" name = "codeassist-tauri"
version = "0.1.3" version = "0.1.4"
description = "CodeAssist 표준 Rust/Tauri 데스크톱 앱" description = "CodeAssist 표준 Rust/Tauri 데스크톱 앱"
authors = ["justdodev"] authors = ["justdodev"]
edition = "2021" edition = "2021"
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 맥 전용. macOS 는 앱 안 http(비암호화) 요청을 기본 차단(ATS)하는데, 우리 서버·고객사 서버가
http 라 앱이 로그인조차 못 함. Tauri 가 이 파일을 번들 Info.plist 에 합쳐 넣음. Windows 는 무관. -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "CodeAssist", "productName": "CodeAssist",
"version": "0.1.3", "version": "0.1.4",
"identifier": "com.codeassist.app", "identifier": "com.codeassist.app",
"build": { "build": {
"beforeDevCommand": { "cwd": "../../2_frontend", "script": "npm run dev" }, "beforeDevCommand": { "cwd": "../../2_frontend", "script": "npm run dev" },
+2
View File
@@ -13,3 +13,5 @@ deploy/langfuse/docker-compose.override.yml
# render_opencode.py 가 환경마다 만드는 파일 — 서버마다 다름 # render_opencode.py 가 환경마다 만드는 파일 — 서버마다 다름
opencode/opencode.json opencode/opencode.json
deploy/docker/.env
deploy/phoenix/.env
+2
View File
@@ -4,6 +4,8 @@
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")" cd "$(dirname "$0")"
export PYTHONUTF8=1 export PYTHONUTF8=1
# git 안이면 최신으로(고객사: /www/abap-ito). 로컬 수정이 있어 못 당기면 멈춤 — 렌더 산출물은 git 밖이라 안 걸림
if git rev-parse --show-toplevel >/dev/null 2>&1; then git -C "$(git rev-parse --show-toplevel)" pull --ff-only; fi
# 필수 키 비어 있으면 여기서 멈춤 — 비어 있는 채로 띄우면 채팅 때 "누락" 에러가 나서 원인 찾기 어려움 # 필수 키 비어 있으면 여기서 멈춤 — 비어 있는 채로 띄우면 채팅 때 "누락" 에러가 나서 원인 찾기 어려움
for k in AAF_FABRIX_BASE_URL AAF_FABRIX_CLIENT_KEY AAF_FABRIX_OPENAPI_TOKEN DB_HOST; do for k in AAF_FABRIX_BASE_URL AAF_FABRIX_CLIENT_KEY AAF_FABRIX_OPENAPI_TOKEN DB_HOST; do
grep -qE "^$k=.+" .env || { echo "!! .env 에 $k 가 비어 있음 — 채우고 다시"; exit 1; } grep -qE "^$k=.+" .env || { echo "!! .env 에 $k 가 비어 있음 — 채우고 다시"; exit 1; }
@@ -0,0 +1,37 @@
# CodeAssist 도커 배포 .env — 이 폴더에 .env 로 복사해서 채움. be 컨테이너 환경변수로 그대로 들어감.
# 바깥 포트 (앱이 http://<서버>:<BE_PORT>/api/v1 로 붙음)
BE_PORT=8080
# LLM — 우리 환경은 OpenRouter 직접(고객사 FabriX 키 AAF_* 는 비움 → 렌더러가 OpenRouter 템플릿을 고름)
OPENROUTER_API_KEY=
OPENCODE_VISION_MODEL=openrouter/google/gemma-4-31b-it
# 토큰 서명 키 (비우면 data/.auth_secret 자동 생성)
AUTH_SECRET=
# DB — 비우면 SQLite(be_data 볼륨). 팀 PostgreSQL 쓰려면 채움
DB_HOST=
DB_PORT=5432
DB_USER=
DB_PASSWORD=
DB_NAME=
DB_SCHEMA=codeassist
DB_SSLMODE=prefer
# 앱(Tauri)에서 오는 요청 허용 origin
CORS_ORIGINS=http://tauri.localhost,tauri://localhost,http://localhost:15173
# 채팅 타임아웃(초)
STREAM_FIRST_EVENT_TIMEOUT_S=300
STREAM_TURN_TIMEOUT_S=600
# SAP 조회 MCP — 같은 서버의 abap-opencode compose 에 mcp 컨테이너가 있으면 그 주소. 비우면 끔
MCP_HOST=mcp
MCP_PORT=3200
MCP_API_KEY=
# 관측(Phoenix) — deploy/phoenix 로 띄운 주소. 비우면 안 보냄
PHOENIX_HOST=http://host.docker.internal:6006 # 같은 서버 Phoenix. 다른 서버면 그 사설 IP
TRACE_PROJECT=codeassist
PHOENIX_UI_PASSWORD=
@@ -0,0 +1,12 @@
# CodeAssist 백엔드 — Django(ASGI/uvicorn). 기동 때 migrate → OpenCode 설정 렌더 → 서버.
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONUTF8=1 PYTHONUNBUFFERED=1 DJANGO_SETTINGS_MODULE=config.settings
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY manage.py ./
COPY config ./config
COPY apps ./apps
COPY common ./common
# opencode/ 는 compose 가 bind 로 끼움(/app/opencode) — 렌더 결과 opencode.json 을 opencode 컨테이너도 같은 폴더로 봄
CMD ["sh", "-c", "python manage.py migrate --noinput && ([ -f seed/snippets.db ] && python manage.py import_snippets seed/snippets.db || true) && python opencode/render_opencode.py && uvicorn config.asgi:application --host 0.0.0.0 --port 8080"]
@@ -0,0 +1,9 @@
# OpenCode 헤드리스 서버 — 고객사 opencode-bin 과 같은 1.18.6 으로 고정
FROM node:22-bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl git ripgrep \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g --no-audit --no-fund opencode-ai@1.18.6
WORKDIR /workspace
EXPOSE 4096
# .opencode/tools 의 커스텀 도구가 @opencode-ai/plugin 을 씀 — bind 된 workspace 라 기동 때 설치(있으면 건너뜀)
CMD ["sh", "-c", "cd /workspace/.opencode && npm install --no-audit --no-fund --silent >/dev/null 2>&1; cd /workspace && opencode serve --hostname 0.0.0.0 --port 4096"]
+35
View File
@@ -0,0 +1,35 @@
# CodeAssist 백엔드 — 도커 배포 (우리 환경용)
고객사(도커 없음)는 `5_django_backend/deploy.sh`(bare) 를 씀. 여긴 도커 되는 서버(20.200.170.37 등)용.
둘 다 같은 저장소 구조를 쓰고, 배포 스크립트만 다름:
| 환경 | 스크립트 | 하는 일 |
|---|---|---|
| 우리(도커) | `deploy/docker/deploy.sh [up\|update\|stop\|logs\|status]` | (update 면 git pull) → 이미지 빌드 → be·opencode 기동 → 헬스 확인 |
| 고객사(bare) | `deploy.sh` | git pull → .env 키 보정 → migrate → 렌더 → opencode-bin·uvicorn 재기동 → 헬스 |
## 처음 한 번
```bash
cd <저장소>/code-assistant/5_django_backend/deploy/docker # 고객사 repo 구조 기준. 우리 git 이면 5_django_backend/deploy/docker
cp .env.example .env && vi .env # OPENROUTER_API_KEY, PHOENIX_HOST 정도만 채우면 됨
./deploy.sh
```
컨테이너 2개: `be`(Django, 바깥 `BE_PORT`=8080) + `opencode`(OpenCode 1.18.6). 저장소의 `opencode/` 폴더를 둘 다 bind 로 봐서
AGENTS.md·위키·커스텀 도구가 그대로 먹고, be 가 렌더한 `opencode.json` 을 opencode 가 바로 읽음.
## 이후
```bash
./deploy.sh update # 최신 코드로 갱신
./deploy.sh status # 상태 + 헬스
./deploy.sh logs
```
## 알아둘 것
- LLM: `.env``AAF_FABRIX_*` 가 없으면 렌더러가 OpenRouter 템플릿(`opencode/opencode.openrouter.json.tmpl`)을 고름. 고객사는 FabriX 템플릿.
- DB: `DB_HOST` 비우면 SQLite(`be_data` 볼륨). 팀 PostgreSQL 쓰려면 DB_* 채움(스키마 `codeassist`).
- SAP 조회(MCP): 같은 서버에 abap-opencode compose 가 떠 있으면 그 `mcp` 컨테이너를 쓸 수 있음 — 두 compose 를 같은 네트워크로 묶어야 함(아직 안 함). 비우면 꺼짐.
- 관측: `PHOENIX_HOST=http://host.docker.internal:6006`(같은 서버; 공인 IP 는 컨테이너 안에서 안 닿음) (`deploy/phoenix/docker-compose.yml` 로 띄운 것). 프로젝트 `codeassist`.
- 앱(Tauri) 쪽은 `2_frontend/.env.production``VITE_API_BASE_URL` 을 이 서버로 바꿔 빌드해야 붙음.
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# CodeAssist 도커 배포 — 우리 환경용. 이거 하나면 됨. (ABAP_OPENCODE web/deploy/deploy.sh 와 같은 명령 모양)
#
# ./deploy.sh 빌드 + 기동 (떠 있으면 갱신)
# ./deploy.sh update git pull 까지 하고 갱신
# ./deploy.sh stop 종료 (데이터 볼륨은 남음)
# ./deploy.sh logs 로그
# ./deploy.sh status 상태
#
# 전제: 이 폴더에 .env (cp .env.example .env 후 채움)
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"; cd "$HERE"
CMD="${1:-up}"
command -v docker >/dev/null || { echo "docker 없음"; exit 1; }
docker compose version >/dev/null 2>&1 || { echo "docker compose v2 없음"; exit 1; }
[ -f .env ] || { echo ".env 없음 — cp .env.example .env 후 채워"; exit 1; }
set -a; . ./.env; set +a
PORT="${BE_PORT:-8080}"; BASE="http://localhost:${PORT}/api/v1"
case "$CMD" in
stop) docker compose down; echo "종료(볼륨 유지)"; exit 0 ;;
logs) docker compose logs -f --tail=100; exit 0 ;;
status) docker compose ps; curl -fsS -m 5 "$BASE/health" && echo || echo "헬스 응답 없음"; exit 0 ;;
update) git -C "$HERE/../../.." pull --ff-only || { echo "git pull 실패 — 로컬 변경 확인"; exit 1; } ;;
up) ;;
*) echo "모르는 명령: $CMD (up|update|stop|logs|status)"; exit 1 ;;
esac
[ -n "${OPENROUTER_API_KEY:-}${AAF_FABRIX_BASE_URL:-}" ] || { echo ".env 에 OPENROUTER_API_KEY(또는 AAF_FABRIX_*) 가 비어 있음"; exit 1; }
echo "-- 이미지 빌드 + 기동"
docker compose up -d --build
echo "-- 기동 확인(최대 2분)"
for _ in $(seq 1 60); do curl -fsS -m 3 "$BASE/health" >/dev/null 2>&1 && break; sleep 2; done
curl -fsS -m 5 "$BASE/health" || { echo "안 떴음 → ./deploy.sh logs"; docker compose ps; exit 1; }
echo
echo "-- OpenCode 설정(렌더 결과) 모델:"; grep -oE '"model": *"[^"]+"' ../../opencode/opencode.json || true
echo "-- 앱 주소: http://$(hostname -I 2>/dev/null | awk '{print $1}'):${PORT}/api/v1"
@@ -0,0 +1,37 @@
# CodeAssist 백엔드 — 우리 환경(도커)용. 고객사(도커 없음)는 5_django_backend/deploy.sh.
# 사용: 이 폴더에서 cp .env.example .env 채우고 ./deploy.sh
# 밖으로 여는 건 be(:8080 → BE_PORT) 하나. 앱(Tauri)이 http://<서버>:<BE_PORT>/api/v1 로 붙음.
name: codeassist
services:
be:
build: { context: ../.., dockerfile: deploy/docker/Dockerfile.be }
ports:
- "${BE_PORT:-8080}:8080"
env_file: .env
environment:
OPENCODE_BASE_URL: http://opencode:4096
OPENCODE_DIRECTORY: /workspace # OpenCode 컨테이너가 보는 workspace 경로 — 세션이 여기 생김
BACKEND_HOST: be # 렌더된 opencode.json 의 게이트웨이 주소(고객사 모드일 때)
BACKEND_PORT: "8080"
MCP_HOST: ${MCP_HOST:-mcp}
extra_hosts:
- "host.docker.internal:host-gateway" # 같은 서버의 Phoenix(:6006) 를 host.docker.internal 로 — 공인 IP 는 컨테이너 안에서 안 닿음(Azure)
volumes:
- ../../opencode:/app/opencode # AGENTS.md·.opencode·wiki·렌더 결과(opencode.json)
- be_data:/app/data # SQLite(DB_HOST 비울 때)·auth secret
depends_on: [opencode]
restart: unless-stopped
opencode:
build: { context: ../.., dockerfile: deploy/docker/Dockerfile.opencode }
environment:
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
volumes:
- ../../opencode:/workspace
- oc_data:/root/.local/share/opencode # 세션·메시지 저장
restart: unless-stopped
volumes:
be_data:
oc_data:
@@ -0,0 +1,22 @@
# Phoenix — 우리 환경(도커)용. 한 대에 하나 띄우고 CodeAssist·ABAP_OPENCODE 둘 다 여기로 보냄(프로젝트로 나뉨).
# 고객사(도커 없음)는 같은 폴더의 run.sh(pip 방식).
# docker compose up -d → http://<서버>:6006
name: phoenix
services:
phoenix:
image: arizephoenix/phoenix:latest
ports:
- "${PHOENIX_PORT:-6006}:6006"
environment:
PHOENIX_WORKING_DIR: /data
PHOENIX_TELEMETRY_ENABLED: "false"
# 로그인 켜려면 둘 다 채움(첫 관리자 admin@localhost / PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD). 켜면 수신에도 API 키 필요 — 모듈 헤더 한 줄 추가해야 함
# PHOENIX_ENABLE_AUTH: "true"
# PHOENIX_SECRET: ${PHOENIX_SECRET}
volumes:
- phoenix_data:/data
restart: unless-stopped
volumes:
phoenix_data:
@@ -6,7 +6,7 @@
"npm": "@ai-sdk/openai-compatible", "npm": "@ai-sdk/openai-compatible",
"name": "FabriX", "name": "FabriX",
"options": { "options": {
"baseURL": "http://127.0.0.1:${BACKEND_PORT}/api/ito", "baseURL": "http://${BACKEND_HOST}:${BACKEND_PORT}/api/ito",
"apiKey": "${AAF_GATEWAY_KEY}" "apiKey": "${AAF_GATEWAY_KEY}"
}, },
"models": ${AAF_FABRIX_MODELS_JSON} "models": ${AAF_FABRIX_MODELS_JSON}
@@ -18,7 +18,7 @@
"mcp": { "mcp": {
"sap-icf": { "sap-icf": {
"type": "remote", "type": "remote",
"url": "http://127.0.0.1:${MCP_PORT}/mcp", "url": "http://${MCP_HOST}:${MCP_PORT}/mcp",
"headers": { "Authorization": "Bearer ${MCP_API_KEY}" }, "headers": { "Authorization": "Bearer ${MCP_API_KEY}" },
"enabled": ${SAP_MCP_ENABLED} "enabled": ${SAP_MCP_ENABLED}
} }
@@ -0,0 +1,23 @@
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"openrouter": {
"options": { "apiKey": "{env:OPENROUTER_API_KEY}" },
"models": {
"z-ai/glm-5.2": { "name": "GLM 5.2" },
"google/gemma-4-31b-it": { "name": "Gemma4", "attachment": true, "modalities": { "input": ["text", "image"], "output": ["text"] } }
}
}
},
"model": "openrouter/z-ai/glm-5.2",
"tools": { "question": false },
"permission": { "external_directory": "allow" },
"mcp": {
"sap-icf": {
"type": "remote",
"url": "http://${MCP_HOST}:${MCP_PORT}/mcp",
"headers": { "Authorization": "Bearer ${MCP_API_KEY}" },
"enabled": ${SAP_MCP_ENABLED}
}
}
}
+8 -4
View File
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""고객사(FabriX)용 opencode.json 만들기 — 템플릿의 ${이름} 을 .env 값으로 채움. """opencode.json 만들기 — 템플릿의 ${이름} 을 .env 값으로 채움.
python opencode/render_opencode.py # ../.env 읽어서 opencode/opencode.json 덮어씀 python opencode/render_opencode.py # ../.env 읽어서 opencode/opencode.json 덮어씀
python opencode/render_opencode.py --check # 쓰진 않고 결과만 출력 python opencode/render_opencode.py --check # 쓰진 않고 결과만 출력
개발(OpenRouter)로 돌아가려면 `git checkout opencode/opencode.json`. 템플릿 고르기: AAF_FABRIX_BASE_URL 이 있으면 고객사(FabriX 게이트웨이), 없으면 OpenRouter(우리 환경·로컬).
호스트: BACKEND_HOST/MCP_HOST 기본 127.0.0.1, 도커 compose 에선 be / mcp 컨테이너 이름.
ABAP_OPENCODE web/deploy/bare/render-opencode.py 와 같은 규칙: ABAP_OPENCODE web/deploy/bare/render-opencode.py 와 같은 규칙:
AAF_FABRIX_MODELS="605:Gemma4,339:GaussO Flash,581:GaussO Think" → 모델 목록. 키가 곧 x-llm-model-id. AAF_FABRIX_MODELS="605:Gemma4,339:GaussO Flash,581:GaussO Think" → 모델 목록. 키가 곧 x-llm-model-id.
비어 있으면 AAF_FABRIX_MODEL_ID 하나(이름 FabriX). 기본 모델은 목록의 첫 번째. 비어 있으면 AAF_FABRIX_MODEL_ID 하나(이름 FabriX). 기본 모델은 목록의 첫 번째.
@@ -34,7 +35,7 @@ def load_env(path: Path) -> dict[str, str]:
k, v = line.split("=", 1) k, v = line.split("=", 1)
v = v.split(" #", 1)[0].strip().strip("\"'") # 줄 끝 주석은 두 칸 띄고 # v = v.split(" #", 1)[0].strip().strip("\"'") # 줄 끝 주석은 두 칸 띄고 #
out[k.strip()] = v out[k.strip()] = v
out.update({k: v for k, v in os.environ.items() if k.startswith(("AAF_", "BACKEND_PORT", "MCP_", "OPENCODE_"))}) out.update({k: v for k, v in os.environ.items() if k.startswith(("AAF_", "BACKEND_", "MCP_", "OPENCODE_", "OPENROUTER_"))})
return out return out
@@ -53,7 +54,9 @@ def render(env: dict[str, str]) -> str:
return m return m
values["AAF_FABRIX_MODELS_JSON"] = json.dumps({mid: _model(mid, name) for mid, name in models.items()}, ensure_ascii=False) values["AAF_FABRIX_MODELS_JSON"] = json.dumps({mid: _model(mid, name) for mid, name in models.items()}, ensure_ascii=False)
values["AAF_FABRIX_DEFAULT_MODEL"] = next(iter(models)) values["AAF_FABRIX_DEFAULT_MODEL"] = next(iter(models))
values.setdefault("BACKEND_HOST", "127.0.0.1")
values.setdefault("BACKEND_PORT", "8001") values.setdefault("BACKEND_PORT", "8001")
values.setdefault("MCP_HOST", "127.0.0.1")
values.setdefault("MCP_PORT", "8888") values.setdefault("MCP_PORT", "8888")
# SAP MCP: MCP_API_KEY 가 있으면 켬. 없으면 끔(로컬 개발) # SAP MCP: MCP_API_KEY 가 있으면 켬. 없으면 끔(로컬 개발)
values["SAP_MCP_ENABLED"] = "true" if values.get("MCP_API_KEY") else "false" values["SAP_MCP_ENABLED"] = "true" if values.get("MCP_API_KEY") else "false"
@@ -67,7 +70,8 @@ def render(env: dict[str, str]) -> str:
missing.append(m.group(1)) missing.append(m.group(1))
return v return v
text = re.sub(r"\$\{(\w+)\}", fill, (HERE / "opencode.fabrix.json.tmpl").read_text(encoding="utf-8")) tmpl = "opencode.fabrix.json.tmpl" if env.get("AAF_FABRIX_BASE_URL") else "opencode.openrouter.json.tmpl"
text = re.sub(r"\$\{(\w+)\}", fill, (HERE / tmpl).read_text(encoding="utf-8"))
if missing: if missing:
print("경고: 비어 있는 값 —", ", ".join(sorted(set(missing))), file=sys.stderr) print("경고: 비어 있는 값 —", ", ".join(sorted(set(missing))), file=sys.stderr)
return text return text
+27
View File
@@ -0,0 +1,27 @@
# 2026-09-22 — 한 것 / 다음에 이어서
## 오늘 한 것
- **고객사 repo 정리**: GHE 단일 repo 를 `abap-specgen/`(ABAP_OPENCODE) + `code-assistant/`(CODE_ASSISTANT) 두 폴더로. -13 bare 런타임 이동, -12 는 clone 으로 전환. → `docs/tech/customer-repo-layout.md`
- **채팅 UI**: 생각 과정(추론·도구 스텝) 접이식 + "생각하는 중/작성 중 · N초" 상태줄 하나. 앱 0.1.3.
- **위키 우선 규칙**: `opencode/wiki/`(팀원 인덱싱 결과) 먼저 읽고, SAP(MCP)은 빠진 부분만. 도구가 workspace 밖 파일 읽다 멈추던 것(`external_directory: allow`) 고침.
- **LLM 관측**: Langfuse 는 고객사(컨테이너 안, docker 불가)라 접고 **Phoenix**. 전송 모듈(`apps/gateway/langfuse.py` = ABAP_OPENCODE `common/langfuse.py`) OTLP protobuf. 한 줄 = 질문(답·토큰·시간), 트리 = 도구 호출. → `docs/tech/observability-phoenix.md`
- 고객사 -12: Phoenix pip 로 8915, 화면은 `http://10.196.81.34:8914/phoenix/`(비번 codeassist2026). -13 은 8914 경유로 전송. 둘 다 확인됨.
- 우리 서버 20.200.170.37: Phoenix 도커(:6006) + CodeAssist 도커(:8080) + ABAP_OPENCODE 갱신. 컨테이너→Phoenix 는 `host.docker.internal`.
- **배포 스크립트 환경별 하나씩**: 도커 `deploy/docker/deploy.sh`(CodeAssist) / `web/deploy/deploy.sh`(ABAP_OPENCODE), bare `deploy.sh`(git pull 포함) / `web/deploy/bare/deploy.sh`. 렌더러가 FabriX/OpenRouter 템플릿 자동 선택.
- 양쪽 git(sqnotes) 전부 push 됨. 고객사 GHE 에도 code-assistant 최신 반영됨(Phoenix 중계까지).
## 다음에 이어서
1. **Azure NSG 8080·6006 인바운드** — 인프라 개발자에게 요청해둠. 열리면:
- `curl http://20.200.170.37:8080/api/v1/health`, 브라우저 `http://20.200.170.37:6006`
- 앱을 우리 서버용으로 빌드: `2_frontend/.env.production``VITE_API_BASE_URL=http://20.200.170.37:8080/api/v1`, Tauri 빌드(버전 올려서)
- 테스트 계정 `t@x.com / pw1234` 는 지우거나 바꿀 것
2. 우리 서버 CodeAssist 의 SAP MCP 는 꺼져 있음(`MCP_API_KEY` 비움). 쓰려면 abap-opencode compose 의 `mcp` 컨테이너와 네트워크 묶기.
3. 고객사: `abap_index` 도구 켜기(팀원 인덱스 서버 :8100 뜨면 `tools.off → tools`). 위키 `index.md` 는 팀원이 새로 뽑아주면 교체.
4. 로컬 정리: Langfuse 도커 6개(`5_django_backend/deploy/langfuse` 에서 `docker compose down -v`), `phoenix-test` 컨테이너, `codeassist` compose — 안 쓰면 내리기.
5. 밀린 것: 옛 Tauri 로컬 SQLite 스니펫 명령 제거, 이미지 재진입 표시, 자동 업데이트(tauri-plugin-updater), WBS 잔여(스니펫 검색 규칙·에러로그·매뉴얼).
## 접속 메모
- 우리 서버: `ssh pwc-rnd-aide-lab`(edge_user, pem 은 `z_pem_key/`). 클론 `~/codeassist`, `~/abap-opencode`.
- 고객사: -12 `ssh -p 8912`, -13 `ssh -p 8916` @10.196.81.34. repo `/www/abap-ito`.
+9 -5
View File
@@ -23,12 +23,16 @@
- `.env` 는 폴더마다 따로. 둘 다 gitignore. **커밋 전 `git status --short | grep '\.env$'` 가 비어야 함.** - `.env` 는 폴더마다 따로. 둘 다 gitignore. **커밋 전 `git status --short | grep '\.env$'` 가 비어야 함.**
- `-12`(code-assistant) 와 `-13`(abap-specgen) 은 서로 통신 안 함. 각자 GHE 만 봄. - `-12`(code-assistant) 와 `-13`(abap-specgen) 은 서로 통신 안 함. 각자 GHE 만 봄.
## 배포 루틴 ## 배포 루틴 — 환경별 스크립트 하나씩
| VM | 명령 | | | 고객사(도커 없음, bare) | 우리 환경 20.200.170.37 (도커) |
|---|---| |---|---|---|
| -12 (code-assistant, 포트 8914→8080) | `cd /www/abap-ito && git pull && cd code-assistant/5_django_backend && bash deploy.sh` | | code-assistant | `cd /www/abap-ito/code-assistant/5_django_backend && bash deploy.sh` (안에서 git pull) | `cd ~/codeassist/5_django_backend/deploy/docker && ./deploy.sh update` |
| -13 (abap-specgen, 포트 8918) | `cd /www/abap-ito && git pull && bash abap-specgen/web/deploy/bare/run.sh restart` | | abap-specgen | `bash /www/abap-ito/abap-specgen/web/deploy/bare/deploy.sh` (git pull → pip → restart) | `cd ~/abap-opencode/web/deploy && bash deploy.sh update` |
| Phoenix | `deploy/phoenix/run.sh` (pip, -12 안 8915) | `cd ~/codeassist/5_django_backend/deploy/phoenix && docker compose up -d` (`:6006`, 둘이 공유) |
우리 서버(20.200.170.37, `ssh pwc-rnd-aide-lab`, edge_user)는 고객사 repo 구조가 아니라 **우리 git 두 개를 각각 clone** 해둠: `~/abap-opencode`(ABAP_OPENCODE main), `~/codeassist`(CODE_ASSISTANT master).
컨테이너에서 같은 서버 Phoenix 는 `PHOENIX_HOST=http://host.docker.internal:6006`(compose 가 host-gateway 매핑). 바깥 포트 8080(CodeAssist)·6006(Phoenix)은 Azure NSG 에서 열어야 접근됨(2026-09-22 현재 80/22 만 열림).
-13 에서 `git pull` 이 "local changes would be overwritten" 로 막히면 서버에서 직접 고친 파일이 있는 것: -13 에서 `git pull` 이 "local changes would be overwritten" 로 막히면 서버에서 직접 고친 파일이 있는 것:
`git diff > /tmp/p.patch && git checkout -- . && git pull && git apply /tmp/p.patch`. `git diff > /tmp/p.patch && git checkout -- . && git pull && git apply /tmp/p.patch`.
+80
View File
@@ -0,0 +1,80 @@
# Windows 설치본(msi) 만들기 — 팀원용 가이드
우리 팀 서버(20.200.170.37) 에 붙는 CodeAssist 앱을 Windows PC 에서 빌드하는 순서. 2026-09-22 기준, 앱 버전 0.1.4.
전부 PowerShell 에서 (Git Bash 는 Rust 링커가 꼬임 — 아래 "막히면").
## 0. 한 번만 깔 것
| 뭐 | 어디서 | 확인 |
|---|---|---|
| Git | https://git-scm.com | `git --version` |
| Node.js 22 LTS | https://nodejs.org | `node --version` → v22.x |
| Rust (rustup, MSVC) | https://rustup.rs → `rustup-init.exe` 기본값으로 | `cargo --version` |
| Visual Studio Build Tools | https://visualstudio.microsoft.com/visual-cpp-build-tools/ → **"C++ 데스크톱 개발"** 워크로드 체크 | Rust 가 링크할 때 필요 |
| WebView2 런타임 | Win11 은 기본 있음. Win10 은 https://developer.microsoft.com/microsoft-edge/webview2/ | 없으면 앱 창이 안 뜸 |
## 1. 코드 받기
```powershell
git clone https://git.sqnotes.com/20260807_OPENCODE_TEMP/CODE_ASSISTANT.git
cd CODE_ASSISTANT
git pull # 이미 받아뒀으면
```
## 2. 서버 주소 넣기 (파일 하나)
`2_frontend\.env.production` 파일을 **새로 만들고** 아래 한 줄만:
```
VITE_API_BASE_URL=http://20.200.170.37:8080/api/v1
```
이 파일은 git 에 안 올라감(PC 마다 다름). 고객사용을 만들 땐 이 주소만 고객사 서버로 바꿔서 빌드하면 됨.
## 3. 빌드
```powershell
cd 2_frontend
npm ci
cd ..\4_rust_tauri
npm ci
npx tauri build
```
처음엔 Rust 크레이트 컴파일 때문에 10~15분. 두 번째부턴 1~2분.
**결과물**: `4_rust_tauri\src-tauri\target\release\bundle\`
- `msi\CodeAssist_0.1.4_x64_en-US.msi` ← 이걸 배포
- `nsis\CodeAssist_0.1.4_x64-setup.exe` (같은 내용, exe 설치본)
## 4. 설치하고 확인
1. msi 더블클릭 → 설치 → 트레이에 CodeAssist 아이콘.
2. 창 열리면 로그인: `admin@codeassist.local` / `test1234` (또는 `guest@codeassist.local` / 같은 비번).
3. 아무 질문이나 하나 보내서 답이 오면 끝.
핫키:
| 키 | 뭐 |
|---|---|
| Ctrl+Alt+W | 챗 열기/숨기기 |
| Ctrl+Alt+Q | 스니펫 팔레트 (Enter = 직전 앱에 붙여넣기) |
| Ctrl+Alt+A | 맨 앞 창 통째 캡처 → 새 대화 |
| Ctrl+Alt+S | 드래그 영역 캡처 → 새 대화 |
| Ctrl+Alt+Z | 맨 앞 창 캡처 → 마지막 대화에 이어서 |
| Ctrl+Alt+X | 드래그 영역 캡처 → 마지막 대화에 이어서 |
## 막히면
- **`link.exe` 어쩌고 하며 실패** → Git Bash 에서 돌린 것. PowerShell 로.
- **`error: linker 'link.exe' not found`** → Build Tools 의 "C++ 데스크톱 개발" 이 안 깔린 것. 깔고 PowerShell 새로 열기.
- **설치했는데 옛 버전 그대로** → Windows 는 같은 버전 msi 를 건너뜀. `4_rust_tauri\src-tauri\tauri.conf.json``Cargo.toml` 의 version 을 올리고(예: 0.1.5) 다시 빌드. 올린 건 커밋해서 푸시.
- **로그인이 안 됨 / 화면이 빈 채로** → `.env.production` 을 안 만들었거나 오타. 2번 다시. 그리고 브라우저에서 `http://20.200.170.37:8080/api/v1/health` 가 열리는지 확인(사내망이면 방화벽).
- **핫키가 안 먹음** → 다른 프로그램이 같은 키를 먼저 잡은 것. 트레이 아이콘 우클릭 → 열기 로는 됨.
- **답변이 안 오고 error** → 서버 쪽 문제. 관측 화면 `http://20.200.170.37:6006` 에서 트레이스 확인하거나 나한테.
## 참고
- 맥 빌드본은 `.env.production` 외에 `src-tauri/Info.plist`(http 허용) 가 필요한데 이미 repo 에 있음. Windows 는 무관.
- 빌드 결과물(`target/`, `dist/`)은 git 에 안 올림. msi 만 팀 채널로 공유.
+26
View File
@@ -0,0 +1,26 @@
# 작업로그 · 2026-09-22 (화)
| 시간 | 내용 |
|------|------|
| 10:14 | 고객사 repo 를 abap-specgen/ + code-assistant/ 두 폴더로 정리, -13 bare 런타임 이동·-12 clone 전환. 구조·배포 루틴을 docs/tech/customer-repo-layout.md 로 |
| 10:47 | 생각하는 중/작성 중 이중 표시 → 하단 상태줄 하나로 합침(첫 글자 전엔 생각하는 중, 후엔 작성 중), 0.1.3 |
| 10:49 | 0.1.3 msi 빌드, txt 4조각 |
| 13:44 | Langfuse 연동 코드 — 채팅 턴 trace + FabriX 호출 generation 을 HTTP 로 전송(SDK 없이), 테스트 4개. deploy/langfuse 에 compose·env·오프라인 반입 절차 |
| 13:53 | 로컬에 Langfuse v4 도커로 띄워 실측 — 옛 ingestion API 는 거부돼 OTLP JSON 으로 바꿈, trace·generation 저장 확인 |
| 14:01 | Langfuse 에 빈 input 트레이스 잔뜩 — pytest 가 로컬 .env 보고 진짜 쏜 것. 테스트 기본 차단, 게이트웨이 루트에 질문/답 표시 |
| 14:06 | Langfuse 트레이스 모양 바꿈 — 루트가 곧 채팅(질문·답·토큰), 밑에 도구 호출들이 트리로 |
| 14:14 | ABAP_OPENCODE 에도 Langfuse — 워처가 session.idle 마다 턴 하나(질문·답·토큰) + 도구 트리 전송, 로컬 Langfuse 로 확인 |
| 14:25 | Langfuse 이미지 6개를 오프라인 반입용 tar.gz 하나로 묶음(Desktop) |
| 15:39 | -12 는 컨테이너 안이라 도커 불가(unshare 막힘) → Langfuse 접고 Phoenix(pip)로. 전송 모듈에 OpenInference 속성 추가, py3.12 wheel 155개 묶음, deploy/phoenix 절차 |
| 15:43 | Phoenix 가 JSON 안 받아(415) protobuf 변환 넣음 — 로컬 Phoenix 에 두 프로젝트(codeassist/abap-specgen) trace 저장 확인 |
| 16:26 | 고객사 -12 에 Phoenix(pip) 띄우고 CodeAssist 백엔드 연결 — 실제 채팅 trace 확인. 바깥 8913/8915 는 -12 로 안 이어져 UI 는 ssh 터널로 |
| 16:34 | Phoenix 화면을 8914 뒤 /phoenix/ 로 중계(Basic 잠금) — 8913/8915 는 -12 로 안 이어져서. 로컬 e2e 통과 |
| 17:15 | 고객사 Phoenix 화면 8914/phoenix/ 로 열림 확인(Basic 잠금). run.sh 옛 프로세스 안 죽던 버그 수정 |
| 17:36 | -13 ABAP_OPENCODE 도 Phoenix 연결(8914 중계 경유) — abap-specgen 프로젝트 trace 확인. DB_SSLMODE 는 env 로 |
| 17:39 | Phoenix 관측 정리 문서(docs/tech/observability-phoenix.md) — 결정·트레이스 모양·실측·오프라인 설치 요령 |
| 18:09 | CodeAssist 도커 배포 세트(compose·Dockerfile·deploy.sh) 만들고 로컬에서 채팅·Phoenix 까지 e2e 확인. ABAP_OPENCODE 엔 bare/deploy.sh 추가 |
| 18:19 | 우리 서버(20.200.170.37)에 Phoenix(도커)·CodeAssist 백엔드(도커) 띄우고 ABAP_OPENCODE 갱신 — CodeAssist 채팅→Phoenix 확인, 워처→Phoenix 연결 확인. 바깥 8080/6006 은 NSG 미개방 |
| 18:34 | 오늘 정리: docs/orders/20260922.md(한 것·다음 할 일·접속 메모) 작성, 양쪽 git 전부 push |
| 20:32 | 우리 서버(20.200.170.37:8080) 앱 빌드 준비 — 8080·6006 바깥에서 200 확인, .env.production 에 서버 API 주소, 빌드 결과에 박히는 것·CORS(tauri origin) 허용 확인. 서버 테스트 계정 t@x.com 삭제(세션 2개 포함). 버전 0.1.4 로 올려 맥 빌드 |
| 20:36 | 맥 빌드 앱이 서버에 요청을 못 보내던 원인 — macOS ATS 가 http 차단. src-tauri/Info.plist 에 NSAllowsArbitraryLoads 넣어 재빌드, 앱 요청이 서버 로그에 찍히는 것 확인(0.1.4 dmg) |
| 20:39 | Windows msi 빌드 팀원 가이드(docs/tech/windows-build-guide.md) — 준비물·.env.production·빌드·설치·핫키표·막힐 때. 맥 크로스 빌드는 접음 |