feat: v2.2.64 — LM Studio 모델 발견/에러 표시 + macOS 셸 호환성

- LM Studio 모델 dropdown을 SDK system.listDownloadedModels('llm') 으로
  조회하도록 변경. REST /v1/models 는 JIT 옵션이 꺼져 있으면 로드된 모델만
  반환하여 macOS 환경에서 dropdown 이 비거나 fallback 한 줄만 남던 문제 해결.
  SDK 실패 시 REST 로 자동 fallback.
- LM Studio 로드/언로드 실패를 readyBar 의 영속 segment 로 표시. 모델을
  다시 선택하면 clearLmStudioError() 로 해제.
- src/security.ts: PowerShell '&&' rewrite 를 win32 에서만 수행. macOS/Linux
  에서는 'if (\$?) { ... }' 가 zsh/bash 문법 오류라 명령 자체가 깨졌음.
- src/utils.ts: system prompt 에 OS 별 [ENVIRONMENT] 블록 동적 주입
  (셸/경로 스타일/체이닝 연산자). 'cd E:\\... ; ...' 같은 Windows 전용
  예시를 macOS 에서 그대로 따라하던 회귀 차단.
- 테스트 mock 에 listDownloaded() 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
g1nation
2026-05-23 09:37:29 +09:00
parent 49f941386f
commit 36db170844
15 changed files with 168 additions and 37 deletions
+21
View File
@@ -7,6 +7,14 @@ export interface ILMStudioClient {
listLoaded(): Promise<string[]>;
/** Like listLoaded() but caches the result for `ttlMs` to avoid hammering the SDK. */
listLoadedCached(ttlMs?: number): Promise<string[]>;
/**
* List every LLM the user has downloaded into LM Studio, regardless of
* whether it is currently loaded. Returns the SDK `modelKey` of each entry —
* the exact identifier `llm.load()` accepts. Use this for the dropdown so
* the list does not depend on LM Studio's JIT setting (REST `/v1/models`
* only returns loaded models when JIT is off).
*/
listDownloaded(): Promise<string[]>;
/**
* Resolve a chat-ready handle for an already-loaded (or just-loaded) model.
*
@@ -117,6 +125,19 @@ export class LMStudioClient implements ILMStudioClient {
}
}
async listDownloaded(): Promise<string[]> {
try {
const items: any[] = await this.getSdk().system.listDownloadedModels('llm');
return items
.map((m) => m?.modelKey ?? null)
.filter((k): k is string => typeof k === 'string' && k.length > 0);
} catch (e: any) {
const msg = e?.message ?? String(e);
logError('Failed to list downloaded LM Studio models.', { error: msg });
return [];
}
}
async getModelHandle(modelKey: string, options?: { refresh?: boolean }): Promise<LLM> {
try {
if (options?.refresh) {