@mobility42/myai
v1.0.13
Published
Windows MyAI desktop app with local Docker service packaging.
Readme
Windows MyAI desktop app with local Docker service packaging.
| Goal | File | |------|------| | Understand purpose | README.md | | First-run WSL/Docker setup flow | docs/environment-bootstrap.md | | Service package structure | docs/service-packages.md | | Product generation engine | tools/product-generator/README.md | | UI renderer system | launcher/common/renderer/README.md | | Streaming task contract | docs/streaming-task-contract.md | | Docker deployment stability | docs/docker-deployment-stability.md | | Author a service | docs/service-authoring.md | | LNB container lifecycle | docs/lnb-container-lifecycle.md | | Service log relay | docs/service-log-relay.md | | Service delete/reset rules | docs/service-delete-reset.md | | Package build and upload | docs/packaging.md | | MSI upgrade rules | docs/msi-upgrade.md | | macOS test installer | docs/mac-installation.md | | Verification | docs/verification.md | | External PC testing | docs/external-pc-testing.md |
Requirements
- Windows 11
- .NET 8 SDK or later
- Microsoft Edge WebView2 Runtime
- Docker Desktop with Docker CLI and Docker Compose plugin
- pnpm for Market workspace tasks
Turbo Cache
Turbo is configured for local workspace task orchestration. Remote cache is not connected yet; future CI setup must document the remote cache provider, token source, and cache invalidation policy before enabling it.
Product Generator
The first-stage product generator creates scaffold service packages from product definitions:
pnpm generate-product --definition ./examples/video-wan22.json
pnpm package-product --definition ./examples/video-wan22.json
pnpm validate-product --definition ./examples/video-wan22.jsonThe command resolves ./examples/* to tools/product-generator/src/examples/* and writes scaffold files under servicePackages/{serviceId}.
UI Renderer System
Service packages must include ui.schema.json. The service origin serves the renderer shell and schema, then the renderer posts to same-origin service APIs and displays results through output.mapping. The renderer runtime is shared by generated service packages and chat-lms-common.
The renderer browser shell is built once by @myai/ui-renderer into launcher/common/renderer/dist-shell/ as browser.html plus assets/. Service packaging copies that shell into each zip and records the version in service.json.runtime.renderer and manifest.json; service-specific renderer builds and market-*.zip artifacts are not valid outputs.
Background Task Runtime
AI generation must not depend on the service page staying mounted. Services should return a taskId immediately, keep generation in a background queue, and expose task status/result endpoints. The shared renderer registers started tasks with the Windows host through WebView2, and the app stores them in %LOCALAPPDATA%\MyAI\runtime\tasks.json while polling outside the page lifecycle. LNB service names show active task counts, for example 이미지 생성(1).
Service runtimes persist task history under /data/tasks/ when using @myai/service-runtime. On service restart, completed history is kept and unfinished restored tasks are marked failed instead of silently disappearing.
When a background generation task completes and the user has not opened that service yet, the Windows LNB shows a blue dot on the service row. Opening the service marks its completed results as checked and clears the dot.
Run
npm CLI 실행
npx @mobility42/myai@mobility42/myai npm 패키지는 설치용 CLI 엔트리만 포함합니다. Windows self-contained WPF 앱의 MyAI.exe, .NET DLL, WPF DLL, WebView2 DLL, runtime/*, node_modules, src/*는 npm 패키지에 포함하지 않습니다.
CLI는 GitHub Release 최신 버전을 조회하고 myai-win-x64-{version}.zip을 다운로드해서 %LOCALAPPDATA%\Mobility42\MyAI\runtime\win-x64에 설치합니다. 설치된 버전이 최신 Release 버전과 같으면 다시 다운로드하지 않고 기존 MyAI.exe를 실행합니다.
기본 GitHub Release URL 형식:
https://github.com/hyej8155/myai/releases/latest/download/myai-win-x64-1.0.13.zip다른 GitHub 저장소를 사용하려면 MYAI_GITHUB_REPOSITORY=owner/repo 환경 변수로 지정합니다.
캐시된 npx 설치를 다시 검증하려면 먼저 npx 캐시를 지웁니다.
Remove-Item "$env:LOCALAPPDATA\npm-cache\_npx" -Recurse -Forcenpm CLI 실행은 사용자 PC에 .NET SDK, MSI 설치, GitHub 방문, 추가 설정을 요구하지 않습니다.
npm 공식 레지스트리 설치
npm install @mobility42/myai또는 pnpm을 사용할 수 있습니다.
pnpm add @mobility42/myai내부 Verdaccio 설치
pnpm add @neurondev/myai --registry=https://verdaccio.mobility42.ionpm 패키지 빌드
npm 배포 전에 CLI와 GitHub Release용 Windows zip을 생성합니다.
pnpm build이 명령은 npm 패키지용 output/build/node/cli.js와 GitHub Release 업로드용 output/packages/releases/myai-win-x64-{version}.zip을 생성합니다. npm 패키지는 output/build/node/cli.js, README.md, package.json만 포함합니다.
npm 공식 배포
npm run publish:npmpublish:npm은 package.json 버전을 기준으로 Release zip과 npm tarball을 만든 뒤 공식 npm registry에 배포합니다. 배포 전 검증은 npm run publish:npm:dry를 사용합니다.
Windows 앱 실행
dotnet restore .\launcher\apps\windows\MyAI.sln
dotnet build .\launcher\apps\windows\MyAI.sln
dotnet run --project .\launcher\apps\windows\MyAI.Windows.csproj서비스 설치 검증
test:service는 MyAI 런처에서 서비스 설치와 실행 흐름을 검증합니다. 현재 기본 검증 대상은 TripoSR 3D 서비스입니다.
pnpm run test:triposr-3dRuntime Configuration
Runtime-specific values must not be hardcoded in C# code. The launcher reads the Market URL only from a .env file with MYAI_MARKET_BASE_URL=.... macOS release bundles include that file at AI Gallery.app/Contents/Resources/.env; development builds may use repository or executable-adjacent .env files.
MYAI_MARKET_BASE_URL process/user environment variables are not used for Market API base URL selection. If .env does not contain MYAI_MARKET_BASE_URL, app startup fails with a configuration error. There is no built-in Market URL fallback.
The only supported Market URL key is MYAI_MARKET_BASE_URL:
MYAI_MARKET_BASE_URL=http://localhost:10000
# MYAI_MARKET_BASE_URL=https://myai-market.mobility42.ioOfficial publish preparation uses the same production Market URL in .env. Set
DIAGNOSTIC_UPLOAD_SECRET for the Market server and MYAI_DIAGNOSTIC_UPLOAD_SECRET
for npm CLI installer telemetry signing before building release artifacts.
Do not configure Market URL in appsettings.json and do not use marketBaseUrl. Do not hardcode localhost:10000 or production Market URLs in app code. On macOS, localhost means the Mac itself, not the Windows development PC running the Market container.
Market Docker
$env:MYAI_DATA_DIR="$env:LOCALAPPDATA\MyAI\data"
docker compose -p myai up -d --build
Invoke-WebRequest -UseBasicParsing http://localhost:10000/healthThe root docker-compose.yml is the orchestration entry point. Market container assets live under market/docker/. Docker services must be started through the root compose file unless a module compose file is being validated. Runtime data is mounted from %LOCALAPPDATA%\MyAI\data on Windows and ~/Library/Application Support/MyAI/data on macOS.
Google OAuth
Windows/macOS 런처의 Google OAuth 흐름은 브라우저에서 시작하고, 로그인 시도마다 임시 loopback callback 서버를 엽니다. Redirect URI는 http://127.0.0.1:{port}/ 형식이며, 요청마다 생성한 state 값을 Google authorize URL과 콜백에서 검증합니다.
콜백 서버는 브라우저의 /favicon.ico 같은 부가 요청을 무시하고 유효한 / 콜백만 처리합니다. 로그는 브라우저 상태 창에 다음 형식으로 OAuth 상태를 남깁니다.
[OAuth] client_id=838629218252-sahl4ndnv993blttmq6dfajasg4e4t3r.apps.googleusercontent.com
[OAuth] listener.host=127.0.0.1
[OAuth] redirectUri=http://127.0.0.1:50444/oauth/callback
[OAuth] listener.url=http://127.0.0.1:50444/oauth/callback
[OAuth] Listening on http://127.0.0.1:50444/oauth/callback
[OAuth] authorize.redirect_uri=http://127.0.0.1:50444/oauth/callback
[OAuth] Authorize redirect_uri=http://127.0.0.1:50444/oauth/callback
[OAuth] platform=windows
[OAuth] response_type=code
[OAuth] scope=openid email profile
[OAuth] prompt=select_account
[OAuth] access_type=<missing>
[OAuth] include_granted_scopes=<missing>
[OAuth] code_challenge_present=true
[OAuth] code_challenge_length=43
[OAuth] state_present=true
[OAuth] state_length=43
[OAuth] authorize_url_full_without_state_value=https://accounts.google.com/o/oauth2/v2/auth?...
[OAuth] authorize.start
[OAuth] browser_open.request_url=https://accounts.google.com/o/oauth2/v2/auth?...
[OAuth] browser_open.method=Process.Start
[OAuth] browser_open.result=success
[OAuth] callback.received
[OAuth] callback.path=/oauth/callback
[OAuth] Callback received path=/oauth/callback state=valid
[OAuth] Callback authorization code=<present>
[OAuth] token.redirect_uri=http://127.0.0.1:50444/oauth/callback
[OAuth] Code exchange redirect_uri=http://127.0.0.1:50444/oauth/callback
[OAuth] Token endpoint=https://oauth2.googleapis.com/token
[OAuth] token.exchange.start
[OAuth] Token request grant_type=authorization_code
[OAuth] Authorize scope=openid email profile
[OAuth] code_challenge_method=S256
[OAuth] Authorize code_challenge=<present length=43>
[OAuth] Token request code_verifier=<present length=86>
[OAuth] token.has_client_secret=true
[OAuth] token.exchange.success
[OAuth] Ignored request path=/favicon.ico
[OAuth] Completed and listener closedToken exchange 실패 시 Google 응답 본문을 숨기지 않고 로그와 브라우저 실패 화면에 표시합니다. 실패 로그에는 HTTP 상태, 원본 응답 JSON, error, error_description, authorize 요청의 redirect_uri, token 요청의 redirect_uri가 함께 남아야 하며 두 redirect URI는 http://127.0.0.1:{port}/ 형식으로 완전히 같아야 합니다. loopback OAuth 흐름은 S256 PKCE를 사용하므로 authorize 요청에는 code_challenge와 code_challenge_method=S256을 포함하고, token 요청에는 같은 요청에서 생성한 code_verifier를 포함합니다. 로그에는 verifier 원문을 남기지 않고 존재 여부와 길이만 기록합니다.
Market 서버는 Google OAuth 시작/콜백을 직접 처리하지 않습니다. 로그인과 로그아웃 실행 버튼은 데스크톱 앱 LNB 하단에 있으며, Market 본문과 Market WebView는 로그인 상태를 읽기 전용으로 표시합니다. macOS LNB도 Windows와 같은 순서로 로고, 서비스 추가하기, 설치된 서비스, 버전, Google 로그인 또는 계정 정보를 표시합니다. /api/auth/google/start와 /api/auth/google/callback은 desktop app 흐름으로 안내하는 비활성 endpoint입니다.
Market Service Catalog
Market UI is a launcher-owned surface. Opening http://localhost:10000 or the configured Market URL in a standalone browser shows an app-only notice and does not bind login, upload, download, refresh, or delete actions. The usable Market UI requires the AI Gallery desktop app WebView host. The Market server reads X-MyAI-Host: desktop-app on the HTML request and injects the host flag into index.html; launcher compatibility service-list and package download calls also require the same header, which the launcher and hosted Market WebView set automatically.
On macOS, the Market navigation item opens the configured MYAI_MARKET_BASE_URL inside the Avalonia NativeWebView. The macOS launcher sends X-MyAI-Host: desktop-app and X-MyAI-Machine-Name-Hash on Market WebView resource requests, and, when logged in, passes the Market bearer token through request headers and the WebView session event. After navigation, macOS also sends Market session, installed-service metadata, and system spec events to the page so the signed-out intro and signed-in service catalog render like Windows. Apple Silicon Macs do not expose dedicated VRAM; the launcher reports unified memory as the Market GPU-memory compatibility value. Intel Macs report VRAM from system_profiler SPDisplaysDataType when available. macOS service screens still do not use an in-app WebView; service runtime.openUrl targets open in the external browser.
The Windows/macOS launcher Market page uses the authenticated uploaded-service catalog:
$body = @{ email = "[email protected]" } | ConvertTo-Json
$login = Invoke-RestMethod -Method Post -Uri http://localhost:10000/api/auth/login -ContentType application/json -Body $body
Invoke-RestMethod -Headers @{ Authorization = "Bearer $($login.token)" } http://localhost:10000/api/market/servicesAdmin uploads are stored in the Market DB and are listed from GET /api/market/services. Package downloads use each returned packageUrl, normally /api/market/services/{serviceId}/download.
The Market server also exposes GET /api/services for launcher/API compatibility. It is backed by the same uploaded-service DB and does not contain a hard-coded service list:
Invoke-WebRequest -UseBasicParsing -Headers @{ "X-MyAI-Host" = "desktop-app" } http://localhost:10000/api/services
Invoke-WebRequest -UseBasicParsing -Headers @{ "X-MyAI-Host" = "desktop-app" } http://localhost:10000/api/services/{serviceId}/download -OutFile service.zipGET /api/services returns only services that an administrator uploaded into the Market DB. Downloads resolve to the stored uploaded package under the Market upload storage.
Market Card Layout
The Windows launcher Market page presents services as AI features, not model inventory. Services are grouped by the category value stored from each uploaded service package JSON. Cards show the feature name, short description, recommended computer spec, engine, and install state by default.
The Market page also exposes category filters as horizontally scrollable capsule tabs. Tab labels show the category name and service count after compatibility filtering. Category names are derived from uploaded services that can be installed on the current host OS/accelerator set, de-duplicated, and sorted ascending; static hard-coded category ordering is not used. 전체 shows every compatible service grouped by category, while a selected category tab shows only matching cards without the category header.
When there are no uploaded services, or no uploaded services are compatible with the current host OS/accelerator set, the Market page hides both category controls and the local computer environment card, then shows only the empty-service message.
For administrators, the Market header shows the signed-in account, service upload action, select-all action, and selected-delete action in the top-right toolbar. The local computer environment card shows hardware information first, then a divider, then the Market usage prompt and any performance guidance.
Model and runtime details are stored in service.json engine metadata and returned by the Market API for diagnostics and packaging. The launcher keeps engine, runtime, and install contract fields out of the main Market card UI.
The Market header shows only the AI Gallery wordmark as the visible product title. The refresh button sits next to that wordmark, reloads GET /api/market/services without leaving the Market page, and is disabled while the catalog is loading.
Installing a Market service moves the user to the installed service page as soon as the package is registered. The launcher adds the service to the LNB, opens that service view, and continues Docker build/start and model preparation in the background.
The launcher reads each installed package service.json version into AiServiceDefinition, then exposes it to the Market WebView through the myai.market.getInstalledServices request and myai.market.installedServices response. The launcher does not decide card state. Market renders 설치 when the service is not installed, 업데이트 when the installed version is missing or the uploaded version is newer than the installed version, and 설치됨 otherwise.
Docker Naming
All MyAI containers use the single Docker Compose project myai, so Docker Desktop shows one myai group with child services such as market, video-wan22, video-wan22-comfyui, and voice-qwen3-tts. Do not set fixed container_name values in compose files; Docker Compose default names avoid reinstall, update, parallel test, and orphan conflicts.
| Service | Compose project | Compose service |
|---------|-----------------|-----------------|
| Market | myai | market |
| Wan2.2 Video | myai | video-wan22 |
| Wan2.2 ComfyUI | myai | video-wan22-comfyui |
| Qwen3 TTS | myai | voice-qwen3-tts |
| Qwen-Image 2512 | myai | image-qwen-image-2512 |
| Upscayl 2.9 Image Upscale | myai | image-upscayl29 |
| Image Generate | myai | flux-image-generate |
| Image Edit | myai | flux-image-edit |
New service packages must set name: myai in docker-compose.yml and runtime.composeProjectName: myai in service.json. Stop/delete/restart logic must use docker compose -p myai -f {composeFile} with the target compose service name. --remove-orphans is not a default cleanup option because independently installed services share the same Compose project.
Containers already created under older service-scoped projects keep their old Docker Compose labels until they are stopped and recreated. The launcher uses myai for new operations after this update.
Service Packages
The app loads installed service definitions from %LOCALAPPDATA%\MyAI\config\installed-services.json.
The current file shape is { "services": [...] }. Startup and install flows also tolerate an older top-level array shape, empty files, and invalid JSON. Invalid JSON is moved aside as installed-services.invalid-YYYYMMDDHHMMSS.json and treated as an empty install list so the launcher can continue opening.
Deleting a service must remove its Docker containers, volumes, service images, installed package folder, launcher caches, and WebView origin storage. Reinstalling the same service must behave like a first install. See docs/service-delete-reset.md.
launcher/apps/windows/Services contains C# launcher service code only. Deployable service packages live under servicePackages, and the project copies those package files to the build output.
Package build scripts live inside their owning module:
servicePackages/music-ace-step15/tools/build-package.ps1
servicePackages/chat-lms-common/tools/build-packages.ps1
servicePackages/chat-lms-tinyllama/tools/build-package.ps1
servicePackages/chat-lms-phi3/tools/build-package.ps1
servicePackages/chat-lms-gemma/tools/build-package.ps1
servicePackages/voice-qwen3-tts/tools/build-package.ps1
servicePackages/image-upscayl29/tools/build-package.ps1
servicePackages/video-wan22/tools/build-package.ps1
tools/build/package-triposr-3d.ps1Generated packages are written directly under output/artifacts:
output/artifacts/chat-lms-tinyllama.zip
output/artifacts/chat-lms-phi3.zip
output/artifacts/chat-lms-gemma.zip
output/artifacts/flux-image-generate.zip
output/artifacts/flux-image-edit.zip
output/artifacts/image-upscayl29.zip
output/artifacts/music-ace-step15.zip
output/artifacts/voice-qwen3-tts.zip
output/artifacts/triposr-3d.zip
output/artifacts/video-wan22.zipMarket Docker image exports for external PC deployment are release artifacts. Create them with:
pnpm market:exportThe command builds the root compose market service and writes the latest image tar file:
output/packages/releases/myai-market-latest.tarEach zip must include docker-compose.yml, service.json, ui.schema.json, manifest.json, and a renderer shell containing browser.html plus assets/. Packaging reports are written to tests/reports/packaging/.
Verification
dotnet build .\launcher\apps\windows\MyAI.sln
docker compose -f .\servicePackages\chat-lms-tinyllama\docker\compose\docker-compose.yml configSee docs/verification.md for the full checklist.
Full local verification, including Market health/login, diagnostic upload, MSI build, and macOS publish checks:
.\tools\verify\verify-all.ps1 -Version 1.0.13macOS DMG files must be created on a Mac because DMG packaging uses hdiutil:
.\tools\installer\build-dmg.ps1 -Runtime osx-arm64 -Version 1.0.13The DMG build uses $HOME/Desktop/myai-main/.env by default and copies it into AI Gallery.app/Contents/Resources/.env. Pass -RuntimeEnvPath only for an explicit alternate .env; missing .env input fails the build.
npm CLI macOS install
On macOS Apple Silicon, npx @mobility42/myai resolves the latest GitHub Release and downloads myai-mac-arm64-{version}.dmg. The CLI opens the DMG in Finder; copy AI Gallery.app to Applications before launching it.
The macOS DMG must be uploaded to the same GitHub Release as the Windows myai-win-x64-{version}.zip asset.
