Skip to content

Tunnel Agent 0.9.1

Choose a tag to compare

@github-actions github-actions released this 24 Jun 15:16
· 29 commits to main since this release

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 plain TunnelAgent-<version>-win-x64-scoop.zip (and win-arm64) built straight from the publish output, without the Velopack shim, Update.exe or .portable marker. The Scoop manifest consumes this instead of the Velopack portable zip, so UpdateManager.IsInstalled is false, 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 under platforms.<rid>.scoop in latest.json.
  • Linux .deb and .rpm packages (.github/workflows/release.yml): Linux releases now ship native .deb and .rpm packages for linux-x64 and linux-arm64 alongside the existing .AppImage. Velopack only emits AppImages, so the release build now also stages the published app under /opt/TunnelAgent with a /usr/bin/tunnel-agent symlink, a .desktop entry and a hicolor icon, and packages it with fpm. 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 Error after 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 and StartServerAsync only allowed (re)starting from Stopped/NotInstalled, so the Start button became a no-op and the whole app had to be restarted. The Start/Restart commands now also start from Error. 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 new Toast_EngineError_PortInUse/_Timeout/_LaunchFailed/_Crashed strings) driven by a structured EngineErrorKind; the toast re-shows on every explicit retry, and the persistent Error status label gains a tooltip with the reason so the cause stays visible after the toast fades. Added scripts/add_resx_keys.py to insert/translate resource keys across every Strings*.resx idempotently.
  • Tests overwriting the real TUNNEL_AGENT_PERPLEXITY_TOKEN (UserEnvironmentService, PerplexityAccountCatalogServiceTests): PerplexityAccountCatalogService.SyncEnvVarAsync calls UserEnvironmentService.Set/Remove, which on Windows persists to HKCU\Environment. Because the static facade went straight to the OS with no injection seam, every AddAsync/SetDefaultAsync/RemoveAsync in the catalog tests wrote real values (token-1, token-2, …) into the developer's actual user environment and most tests never cleaned up. Added UserEnvironmentService.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): when TUNNEL_AGENT_CLIPROXY_API_KEY had no value, the Pi and OpenCode config builders still emitted the placeholder reference (${TUNNEL_AGENT_CLIPROXY_API_KEY} / {env:...}) because HasApiKey was checking the env var name (ModelEntry.ApiKey), which is never empty, instead of its resolved value. The placeholder then expanded to an empty apiKey and the agent failed. The builders now use HasResolvedApiKey, which resolves the variable through UserEnvironmentService and falls back to "no-key" only when it is genuinely unset. Additionally, the API-key reconcile in RefreshApiKeyItemsAsync now heals the reverse drift: if the env var is empty but proxy-config.yaml (the source of truth) has api-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 from HH:mm to 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:mm within a day, MM-dd HH:mm across days, yyyy-MM-dd across months, yyyy-MM across 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 as Provider · model above 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/EnvExportRaw and the "aider" branches in GenerateRaw/WriteConfigSync), and the now-unused Agent_aider_Description localization 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 to proxy-config.yaml, but CLIProxyAPI has no per-key label field 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.Label and the label read/write paths in ConfigService are removed, and AddAccountAsync/AddCustomProviderAsync no longer take a label. API-key rows now display the masked key. (Perplexity account labels are unaffected.)
  • Dead CustomProviderCredentialStore (ProviderCatalogService, ConfigService): removed the unused openai-compat-*.json credential store and its one-time MigrateLegacyCredentialStoreAsync migration (which read, migrated, and deleted the legacy files), plus the unused credentialStore constructor parameter on ConfigService. Credentials live in proxy-config.yaml.