ltcai 0.1.20 → 0.1.23
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/README.md +210 -260
- package/bin/ltcai.js +27 -0
- package/docs/CHANGELOG.md +133 -0
- package/docs/images/logo.svg +33 -0
- package/docs/images/screenshot-admin.png +0 -0
- package/docs/images/screenshot-chat.png +0 -0
- package/docs/images/screenshot-graph.png +0 -0
- package/ltcai_cli.py +30 -1
- package/package.json +1 -1
- package/server.py +360 -33
- package/static/account.html +0 -4
- package/static/admin.html +1 -7
- package/static/chat.html +59 -58
- package/static/graph.html +1 -6
- package/tests/unit/test_security.py +68 -0
- package/tests/unit/test_setup_wizard.py +35 -0
- package/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/integration/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/integration/__pycache__/test_api.cpython-314-pytest-9.0.3.pyc +0 -0
- package/tests/unit/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/unit/__pycache__/test_security.cpython-314-pytest-9.0.3.pyc +0 -0
- package/tests/unit/__pycache__/test_tools.cpython-314-pytest-9.0.3.pyc +0 -0
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,138 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.23] - 2026-05-24
|
|
4
|
+
|
|
5
|
+
### Discord 권한 알림 시스템
|
|
6
|
+
|
|
7
|
+
- **`GET /permissions/pending`** — 대기 중인 파일 접근 권한 요청 목록 (관리자)
|
|
8
|
+
- **`POST /permissions/approve/{token}`** — 권한 승인 (관리자 세션 또는 `LATTICEAI_PERMISSION_SECRET`)
|
|
9
|
+
- **`POST /permissions/deny/{token}`** — 권한 거부/취소
|
|
10
|
+
- **`GET /permissions/status/{token}`** — 승인 상태 폴링 (AI 에이전트용)
|
|
11
|
+
- 권한 토큰 기본값 `approved: False` — 명시적 승인 전까지 파일 접근 불가
|
|
12
|
+
- `~/.ltcai/permission_queue.json` — 서버가 기록, Claude Code Discord 플러그인이 읽어 알림 전송
|
|
13
|
+
- `LATTICEAI_PERMISSION_SECRET` 환경변수 — 모니터 스크립트가 세션 없이 approve/deny 호출 가능
|
|
14
|
+
- `perm_monitor.py` — 권한 목록 조회·승인·거부 CLI 도우미 (`list` / `approve TOKEN` / `deny TOKEN` / `discord-msg`)
|
|
15
|
+
- Discord에서 `승인 <토큰앞8자>` / `거부 <토큰앞8자>` 로 파일 접근 제어 가능
|
|
16
|
+
|
|
17
|
+
### 리포지터리 UX 개선
|
|
18
|
+
|
|
19
|
+
- **영어 README** 전면 재작성 — 한국어는 접을 수 있는 `<details>` 섹션으로 이동
|
|
20
|
+
- **SVG 로고** 추가 (`docs/images/logo.svg`)
|
|
21
|
+
- **경쟁 제품 비교표** — Lattice AI vs Open WebUI · Continue.dev · GitHub Copilot
|
|
22
|
+
- **실제 UI 스크린샷 3장** — Chat UI · Admin Dashboard · Data Graph (Playwright 2x 캡처)
|
|
23
|
+
- **VS Code 익스텐션 카테고리** `Other` → `AI, Machine Learning, Chat, Other`
|
|
24
|
+
- **VS Code 익스텐션 키워드** 8개 → 16개 (copilot, apple-silicon, groq, graph-rag 등)
|
|
25
|
+
- **VS Code 익스텐션 README** 전면 재작성 (기능표, 비교표, 모델 목록)
|
|
26
|
+
- 구버전 `.tgz` / `.vsix` 빌드 파일 삭제
|
|
27
|
+
|
|
28
|
+
### Release
|
|
29
|
+
- 배포 버전을 `0.1.23`으로 상향
|
|
30
|
+
- 대상 채널: `npm` · `PyPI` · `VS Code Marketplace` · `Open VSX`
|
|
31
|
+
|
|
32
|
+
## [0.1.22] - 2026-05-24
|
|
33
|
+
|
|
34
|
+
### 리포지터리 UX 개선 — 다운로드 유입 최적화
|
|
35
|
+
|
|
36
|
+
#### README 전면 재작성
|
|
37
|
+
- **영어 메인 문서** — 한국어는 접을 수 있는 `<details>` 섹션으로 이동 (국제 유입 대응)
|
|
38
|
+
- **SVG 로고 추가** (`docs/images/logo.svg`) — 인디고→시안 그라디언트 래티스 그리드 아이콘
|
|
39
|
+
- **경쟁 제품 비교표** — Lattice AI vs Open WebUI · Continue.dev · GitHub Copilot 10개 기준 비교
|
|
40
|
+
- **PyPI 월간 다운로드 수 배지** 추가 (신뢰도 지표)
|
|
41
|
+
- 기능 · 보안 · API · 트러블슈팅 섹션을 표(table) 형식으로 정리 (가독성 향상)
|
|
42
|
+
|
|
43
|
+
#### 실제 UI 스크린샷 자동 캡처
|
|
44
|
+
- `docs/images/screenshot-chat.png` — 웹 채팅 UI (사이드바, 모델/파이프라인/VPC 카드)
|
|
45
|
+
- `docs/images/screenshot-admin.png` — 어드민 대시보드 + Audit & Data Governance 섹션
|
|
46
|
+
- `docs/images/screenshot-graph.png` — Data Graph 시각화 (299 노드, 443 엣지)
|
|
47
|
+
- README 상단에 3단 그리드 스크린샷 테이블 추가
|
|
48
|
+
- `scripts/take_screenshots.js` — Playwright Chromium 헤드리스 캡처 스크립트 (2x 레티나)
|
|
49
|
+
|
|
50
|
+
#### VS Code 익스텐션 메타데이터 개선
|
|
51
|
+
- **카테고리** `Other` → `AI, Machine Learning, Chat, Other` (Marketplace 검색 노출 증가)
|
|
52
|
+
- **키워드** 8개 → 16개 추가 (`copilot`, `apple-silicon`, `groq`, `graph-rag` 등)
|
|
53
|
+
- **설명 문구** 구체화 — 핵심 차별점(MLX, MCP, Graph RAG, zero telemetry) 명시
|
|
54
|
+
- **익스텐션 README 전면 재작성** — 기능표 · 빠른 시작 · 단축키 · 지원 모델 · 설정 · 비교표 포함
|
|
55
|
+
|
|
56
|
+
#### 리포지터리 정리
|
|
57
|
+
- 루트 및 `vscode-extension/`의 구버전 `.tgz` / `.vsix` 빌드 파일 삭제
|
|
58
|
+
|
|
59
|
+
### Release preparation
|
|
60
|
+
|
|
61
|
+
- 배포 버전을 `0.1.22`로 상향
|
|
62
|
+
- `package.json`
|
|
63
|
+
- `pyproject.toml`
|
|
64
|
+
- `vscode-extension/package.json`
|
|
65
|
+
- npm / PyPI / VS Code Marketplace / Open VSX 배포 전 빌드 산출물 생성
|
|
66
|
+
|
|
67
|
+
### Verification
|
|
68
|
+
|
|
69
|
+
- Python compile check 통과
|
|
70
|
+
- unit tests 통과
|
|
71
|
+
- root npm package 생성
|
|
72
|
+
- Python wheel / sdist 생성
|
|
73
|
+
- VS Code / Open VSX용 VSIX 생성
|
|
74
|
+
|
|
75
|
+
## [0.1.21] - 2026-05-24
|
|
76
|
+
|
|
77
|
+
### Setup Wizard — 자동 설치 · 연결 · 검증 · 복구
|
|
78
|
+
|
|
79
|
+
- **구성요소 자동 감지** — Homebrew, Python, Git, Node/npm, Ollama, LM Studio, Tesseract, MLX 계열 탐지
|
|
80
|
+
- `COMMON_PATH_DIRS` 확장: `/opt/homebrew/bin`, `~/.local/bin`, `~/.latticeai/bin` 등 자동 포함
|
|
81
|
+
- `PACKAGE_MODULES` 맵으로 pip 패키지 → import 이름 변환 (mlx-lm, mlx-vlm, openai-whisper 등)
|
|
82
|
+
- **공식 다운로드 연결** — 자동 설치 실패 시 OS별 공식 페이지(`OFFICIAL_DOWNLOADS`) 자동 오픈
|
|
83
|
+
- **설치 완료 자동 감지** — binary / Python 모듈 재탐색 폴링으로 설치 완료 감지
|
|
84
|
+
- **환경 변수 / PATH 자동 세팅** — PATH 누락 디렉토리를 `.env`의 `LATTICEAI_EXTRA_PATH`에 자동 저장
|
|
85
|
+
- `_update_env_file()` 헬퍼로 `.env` 파일 안전 갱신 (중복 없이 key 업데이트)
|
|
86
|
+
- **동작 테스트** — binary는 `--version`, Python 패키지는 `import` smoke test
|
|
87
|
+
- **실패 시 자동 복구** — PATH 재보정, pip 재시도, brew 실패 시 공식 다운로드 fallback
|
|
88
|
+
|
|
89
|
+
### 보안 강화 — 로컬 파일 접근 승인 시스템
|
|
90
|
+
|
|
91
|
+
- **토큰 기반 로컬 파일 승인** — `_local_permission_response()` / `_require_local_approval()`
|
|
92
|
+
- 5분(300초) TTL 만료 토큰으로 read / write / list 각 액션을 별도 승인
|
|
93
|
+
- write 승인 시 `content_hash`(SHA-256)로 내용 위변조 방지
|
|
94
|
+
- 만료 토큰 자동 정리(lazy GC)
|
|
95
|
+
- Discord permission monitor 또는 웹 UI 승인 후에만 토큰 활성화
|
|
96
|
+
- **로컬 파일 미리보기 보호** — `/local/serve`, `/tools/read_document`, `/tools/pdf_pages`도 서버 발급 approval token 없이는 로컬 절대 경로를 열지 않도록 변경
|
|
97
|
+
- **workspace 정적 노출 제거** — `/agent-files` `StaticFiles` mount 제거, 인증이 있는 다운로드 라우트만 사용
|
|
98
|
+
- **세션 토큰 저장 강화** — 로그인 응답 body에서 bearer token 제거, 웹 UI는 HttpOnly cookie 기반 인증만 사용
|
|
99
|
+
- `static/account.html`, `static/chat.html`, `static/admin.html`, `static/graph.html`의 `localStorage` 세션 토큰 의존 제거
|
|
100
|
+
- **loopback 감지** — `_host_is_loopback()` + `ipaddress` 표준 라이브러리로 네트워크 노출 여부 판단
|
|
101
|
+
- `REQUIRE_AUTH` 기본값: 퍼블릭 모드 또는 네트워크 노출 시 `true` 자동 적용
|
|
102
|
+
- `OPEN_REGISTRATION`: 네트워크 노출/퍼블릭 모드에서 기본 `false` (초대 코드 필요)
|
|
103
|
+
- **CORS 세밀 제어** — wildcard credential CORS 대신 `LATTICEAI_CORS_ALLOWED_ORIGINS` 환경변수로 허용 출처 추가 설정 가능
|
|
104
|
+
- **파일 자동 주입(opt-in)** — `LATTICEAI_AUTO_READ_CHAT_PATHS=true` 설정 시에만 채팅 메시지의 로컬 경로를 컨텍스트에 주입 (기본 OFF — 클라우드 모델 파일 누출 방지)
|
|
105
|
+
|
|
106
|
+
### 어드민 대시보드 — Audit & Data Governance
|
|
107
|
+
|
|
108
|
+
- **감사 로그 섹션** — 사용자별 AI 사용량, 업로드 문서 수, 민감정보 감지, clear/delete 이벤트, 최근 감사 이벤트 표시
|
|
109
|
+
- **데이터 보존 정책** — `/clear`, `/clear_all`, 대화 삭제는 화면 정리만 수행; Data Graph / RAG / 감사 로그는 보존
|
|
110
|
+
- clear 동작을 `ClearEvent` 노드로 그래프에 기록 (언제 누가 clear 했는지 감사 추적)
|
|
111
|
+
- **민감정보 검사** — 문서 업로드 텍스트를 감사 로그에 기록
|
|
112
|
+
|
|
113
|
+
### Graph RAG / Data Graph
|
|
114
|
+
|
|
115
|
+
- **한국어 단어 검색 개선** — 2글자 키워드(`문서`, `모델` 등) RAG 검색 누락 문제 수정
|
|
116
|
+
- **`graph.html` 독립 페이지 유지** — 채팅 사이드바 `Data Graph` 버튼으로 연결, New Chat 버튼은 대화 검색 아래로 이동
|
|
117
|
+
|
|
118
|
+
### CLI / Node.js 래퍼
|
|
119
|
+
|
|
120
|
+
- `ltcai_cli.py` — `doctor` 명령어에 확장된 구성요소 탐지 통합
|
|
121
|
+
- `bin/ltcai.js` — Node.js 래퍼 PATH 보정 로직 개선
|
|
122
|
+
|
|
123
|
+
### 테스트
|
|
124
|
+
|
|
125
|
+
- `tests/unit/test_security.py` — loopback 감지, 로컬 파일 접근 approval token, write content hash 검증 추가
|
|
126
|
+
- `tests/unit/test_setup_wizard.py` — 자동 설정 구성요소 감지와 PATH 보정 검증 추가
|
|
127
|
+
|
|
128
|
+
### 환경변수 추가 (`.env.example`)
|
|
129
|
+
|
|
130
|
+
| 변수 | 기본값 | 설명 |
|
|
131
|
+
|------|--------|------|
|
|
132
|
+
| `LATTICEAI_AUTO_READ_CHAT_PATHS` | `false` | 채팅 메시지 내 로컬 경로 자동 주입 |
|
|
133
|
+
| `LATTICEAI_CORS_ALLOWED_ORIGINS` | `` | 추가 허용 CORS 출처 (콤마 구분) |
|
|
134
|
+
| `LATTICEAI_EXTRA_PATH` | `` | 추가 PATH 디렉토리 (Setup Wizard 자동 기록) |
|
|
135
|
+
|
|
3
136
|
## [0.1.20] - 2026-05-23
|
|
4
137
|
|
|
5
138
|
### Release
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 60" width="240" height="60">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" style="stop-color:#6366f1"/>
|
|
5
|
+
<stop offset="100%" style="stop-color:#06b6d4"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<!-- Lattice grid icon -->
|
|
9
|
+
<g transform="translate(8,10)">
|
|
10
|
+
<!-- dots -->
|
|
11
|
+
<circle cx="0" cy="0" r="3" fill="url(#g)" opacity="0.9"/>
|
|
12
|
+
<circle cx="16" cy="0" r="3" fill="url(#g)" opacity="0.9"/>
|
|
13
|
+
<circle cx="32" cy="0" r="3" fill="url(#g)" opacity="0.9"/>
|
|
14
|
+
<circle cx="0" cy="16" r="3" fill="url(#g)" opacity="0.9"/>
|
|
15
|
+
<circle cx="16" cy="16" r="4" fill="url(#g)"/>
|
|
16
|
+
<circle cx="32" cy="16" r="3" fill="url(#g)" opacity="0.9"/>
|
|
17
|
+
<circle cx="0" cy="32" r="3" fill="url(#g)" opacity="0.9"/>
|
|
18
|
+
<circle cx="16" cy="32" r="3" fill="url(#g)" opacity="0.9"/>
|
|
19
|
+
<circle cx="32" cy="32" r="3" fill="url(#g)" opacity="0.9"/>
|
|
20
|
+
<!-- lines -->
|
|
21
|
+
<line x1="0" y1="0" x2="32" y2="0" stroke="url(#g)" stroke-width="1.2" opacity="0.4"/>
|
|
22
|
+
<line x1="0" y1="16" x2="32" y2="16" stroke="url(#g)" stroke-width="1.2" opacity="0.4"/>
|
|
23
|
+
<line x1="0" y1="32" x2="32" y2="32" stroke="url(#g)" stroke-width="1.2" opacity="0.4"/>
|
|
24
|
+
<line x1="0" y1="0" x2="0" y2="32" stroke="url(#g)" stroke-width="1.2" opacity="0.4"/>
|
|
25
|
+
<line x1="16" y1="0" x2="16" y2="32" stroke="url(#g)" stroke-width="1.2" opacity="0.4"/>
|
|
26
|
+
<line x1="32" y1="0" x2="32" y2="32" stroke="url(#g)" stroke-width="1.2" opacity="0.4"/>
|
|
27
|
+
<!-- diagonals -->
|
|
28
|
+
<line x1="0" y1="0" x2="32" y2="32" stroke="url(#g)" stroke-width="1" opacity="0.25"/>
|
|
29
|
+
<line x1="32" y1="0" x2="0" y2="32" stroke="url(#g)" stroke-width="1" opacity="0.25"/>
|
|
30
|
+
</g>
|
|
31
|
+
<!-- Text -->
|
|
32
|
+
<text x="58" y="34" font-family="'SF Pro Display','Segoe UI',system-ui,sans-serif" font-size="26" font-weight="700" fill="url(#g)" letter-spacing="-0.5">Lattice AI</text>
|
|
33
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/ltcai_cli.py
CHANGED
|
@@ -18,6 +18,32 @@ import urllib.request
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
def _load_env_file(path: Path) -> None:
|
|
22
|
+
if not path.exists():
|
|
23
|
+
return
|
|
24
|
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
25
|
+
line = raw_line.strip()
|
|
26
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
27
|
+
continue
|
|
28
|
+
key, value = line.split("=", 1)
|
|
29
|
+
key = key.strip()
|
|
30
|
+
value = value.strip().strip('"').strip("'")
|
|
31
|
+
if key and key not in os.environ:
|
|
32
|
+
os.environ[key] = value
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _apply_extra_path() -> None:
|
|
36
|
+
extra = os.getenv("LATTICEAI_EXTRA_PATH", "")
|
|
37
|
+
if not extra:
|
|
38
|
+
return
|
|
39
|
+
current = [p for p in os.environ.get("PATH", "").split(os.pathsep) if p]
|
|
40
|
+
for item in reversed([p for p in extra.split(os.pathsep) if p]):
|
|
41
|
+
expanded = str(Path(item).expanduser())
|
|
42
|
+
if Path(expanded).exists() and expanded not in current:
|
|
43
|
+
current.insert(0, expanded)
|
|
44
|
+
os.environ["PATH"] = os.pathsep.join(current)
|
|
45
|
+
|
|
46
|
+
|
|
21
47
|
def _has_module(name: str) -> bool:
|
|
22
48
|
return importlib.util.find_spec(name) is not None
|
|
23
49
|
|
|
@@ -200,6 +226,10 @@ def _start_tunnel(port: int) -> str | None:
|
|
|
200
226
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
201
227
|
|
|
202
228
|
def main() -> None:
|
|
229
|
+
app_dir = Path(__file__).resolve().parent
|
|
230
|
+
_load_env_file(app_dir / ".env")
|
|
231
|
+
_apply_extra_path()
|
|
232
|
+
|
|
203
233
|
parser = argparse.ArgumentParser(prog="LTCAI", description="Run the Lattice AI local server.")
|
|
204
234
|
subparsers = parser.add_subparsers(dest="command")
|
|
205
235
|
subparsers.add_parser("doctor", help="Check local runtime dependencies and configuration.")
|
|
@@ -216,7 +246,6 @@ def main() -> None:
|
|
|
216
246
|
if args.command == "doctor":
|
|
217
247
|
raise SystemExit(doctor())
|
|
218
248
|
|
|
219
|
-
app_dir = Path(__file__).resolve().parent
|
|
220
249
|
os.chdir(app_dir)
|
|
221
250
|
|
|
222
251
|
# --tunnel forces 0.0.0.0 so cloudflared can reach the server
|