Tunnel Agent 0.9.1
Installation
| Platform | Installer | Portable |
|---|---|---|
| Windows x64 | Setup.exe | .zip |
| Windows arm64 | Setup.exe | .zip |
| Linux x64 | .AppImage · .deb · .rpm | — |
| Linux arm64 | .AppImage · .deb · .rpm | — |
| macOS x64 (Intel) | .pkg | .zip |
| macOS arm64 (Apple Silicon) | .pkg | .zip |
Existing installations update automatically via Velopack.
Added
- Updater-free Scoop zip (
.github/workflows/release.yml): Windows releases now also publish a plainTunnelAgent-<version>-win-x64-scoop.zip(andwin-arm64) built straight from the publish output, without the Velopack shim,Update.exeor.portablemarker. The Scoop manifest consumes this instead of the Velopack portable zip, soUpdateManager.IsInstalledisfalse, the in-app updater stays disabled, and Scoop — not Velopack — owns updates (no more self-updates writing into the Scoop directory). The hash is exposed underplatforms.<rid>.scoopinlatest.json. - Linux
.deband.rpmpackages (.github/workflows/release.yml): Linux releases now ship native.deband.rpmpackages forlinux-x64andlinux-arm64alongside the existing.AppImage. Velopack only emits AppImages, so the release build now also stages the published app under/opt/TunnelAgentwith a/usr/bin/tunnel-agentsymlink, a.desktopentry and a hicolor icon, and packages it withfpm. The release notes installation table links the new artifacts; auto-update remains AppImage/Velopack-driven (deb/rpm are managed by the system package manager).
Fixed
- Engine stuck in
Errorafter a failed start (ProcessService,EngineService,IManagedEngine,EngineErrorKind,MainWindowViewModel,ProvidersView.axaml,Resources/Strings*.resx): when CLIProxyAPI (or Perplexity) was already running in a terminal or another app, starting the managed engine failed andStartServerAsynconly allowed (re)starting fromStopped/NotInstalled, so the Start button became a no-op and the whole app had to be restarted. The Start/Restart commands now also start fromError. A port pre-flight (TcpListener) makes the port-in-use case deterministic instead of racing the foreign instance's health response (which previously surfaced a misleading "Process exited unexpectedly"). Failures now raise a localized error toast (translated for all fourteen languages via the newToast_EngineError_PortInUse/_Timeout/_LaunchFailed/_Crashedstrings) driven by a structuredEngineErrorKind; the toast re-shows on every explicit retry, and the persistentErrorstatus label gains a tooltip with the reason so the cause stays visible after the toast fades. Addedscripts/add_resx_keys.pyto insert/translate resource keys across everyStrings*.resxidempotently. - Tests overwriting the real
TUNNEL_AGENT_PERPLEXITY_TOKEN(UserEnvironmentService,PerplexityAccountCatalogServiceTests):PerplexityAccountCatalogService.SyncEnvVarAsynccallsUserEnvironmentService.Set/Remove, which on Windows persists toHKCU\Environment. Because the static facade went straight to the OS with no injection seam, everyAddAsync/SetDefaultAsync/RemoveAsyncin the catalog tests wrote real values (token-1,token-2, …) into the developer's actual user environment and most tests never cleaned up. AddedUserEnvironmentService.SetImplementation()so tests can inject an in-memory fake (restoring the previous implementation on dispose); the catalog tests now run fully isolated and no longer touch the OS environment. - Empty CLIProxy API key written to agent configs (
AgentConfigurationService,MainWindowViewModel): whenTUNNEL_AGENT_CLIPROXY_API_KEYhad no value, the Pi and OpenCode config builders still emitted the placeholder reference (${TUNNEL_AGENT_CLIPROXY_API_KEY}/{env:...}) becauseHasApiKeywas checking the env var name (ModelEntry.ApiKey), which is never empty, instead of its resolved value. The placeholder then expanded to an emptyapiKeyand the agent failed. The builders now useHasResolvedApiKey, which resolves the variable throughUserEnvironmentServiceand falls back to"no-key"only when it is genuinely unset. Additionally, the API-key reconcile inRefreshApiKeyItemsAsyncnow heals the reverse drift: if the env var is empty butproxy-config.yaml(the source of truth) hasapi-keys, the first one is adopted as the default env var, so configs reference a key the proxy actually accepts instead of falling back to"no-key"against an auth-required proxy. - Dashboard usage chart axis dates (
DashboardViewModel): the Home usage chart axis and hover labels only switched fromHH:mmto a dated format when the data span was>= 2 days. With two calendar days of data the actual min→max span fell below that threshold, so both axis ends and the tooltips showed bare times and the days were indistinguishable. The format is now chosen from the real calendar range (HH:mmwithin a day,MM-dd HH:mmacross days,yyyy-MM-ddacross months,yyyy-MMacross years), so days, months and years can be told apart.
Added
- Logs request model filter (
LogsViewModel,RequestLogEntry,LogsView.axaml,Resources/Strings*.resx): the Logs → Requests tab now exposes a model filter alongside the provider filter and shows each request asProvider · modelabove the path. Request search and CSV export also include the model.
Removed
- Aider agent (
AgentCatalog,AgentConfigurationService,Strings*.resx,README.md): removed Aider from the Agents window. Its catalog entry, the env-var config generation (AiderEnv/EnvExportRawand the"aider"branches inGenerateRaw/WriteConfigSync), and the now-unusedAgent_aider_Descriptionlocalization strings across all fourteen languages were dropped. - API-key account labels (
AppSettings,ConfigService,ProviderCatalogService,MainWindowViewModel,MainWindow.axaml): removed the per-key label feature for upstream API-key accounts. Labels were written toproxy-config.yaml, but CLIProxyAPI has no per-keylabelfield and strips it whenever it rewrites/normalizes the config (so labels silently disappeared on engine start, and were lost entirely when a native API-key provider was disabled). Since the label added no functional value, it was dropped instead of reworked: the label input is gone from the add-account and add-custom-provider dialogs,ProviderAccountSettings.Labeland the label read/write paths inConfigServiceare removed, andAddAccountAsync/AddCustomProviderAsyncno longer take a label. API-key rows now display the masked key. (Perplexity account labels are unaffected.) - Dead
CustomProviderCredentialStore(ProviderCatalogService,ConfigService): removed the unusedopenai-compat-*.jsoncredential store and its one-timeMigrateLegacyCredentialStoreAsyncmigration (which read, migrated, and deleted the legacy files), plus the unusedcredentialStoreconstructor parameter onConfigService. Credentials live inproxy-config.yaml.