184 Commits
Author SHA1 Message Date
gongzhiyong e360959dca fix: correct agent spelling on website 2026-05-26 16:21:09 +08:00
chenchen 80d0f956d0 feat(manager): P0 device-binding signature layer for cc-haha desktop clients
Lays down the server side of a per-request Ed25519 signature scheme that
binds a token to a specific desktop install, so the bearer key can't be
extracted from ~/.claude/cc-haha/providers.json and resold. Plan lives
at ~/.claude/plans/peaceful-sprouting-crane.md.

Compatibility: legacy bare-bearer sk- callers (CLI/SDK) pass through
unchanged until P3 (30-day deadline) flips RequireGlobal=true. No
existing token rows are modified — pubkey is nullable and defaults to
null.

Pieces:
- model.Token gains DeviceId, DevicePubkey, DeviceFingerprint, DeviceName,
  DevicePlatform, DeviceAppVersion, DeviceBoundAt, DeviceLastSeenIp,
  DeviceLastUsedAt, RequireDeviceBinding, RevokedAt, RevokedReason.
  Pure additive columns, GORM AutoMigrate handles SQLite/MySQL/PG.
- common.VerifyEd25519Signature: thin wrapper around crypto/ed25519
  stdlib, used by the new middleware. No new external deps.
- service.MarkNonceUsed: Redis SETNX-based nonce store with an
  in-memory sync.Map fallback for single-instance dev. TTL = setting.
- middleware.VerifyDeviceSignatureIfRequired: wired into TokenAuth as
  a fail-fast dispatch right after model.ValidateUserToken. Verifies
  canonical = METHOD\nPATH\nTS_MS\nNONCE\nFINGERPRINT\nSHA256(BODY),
  signed as Ed25519(sha256(canonical)). 120s timestamp window, 300s
  nonce window, fingerprint stored at pair time must match the header.
- controller.PairDevice / ListUserDevices / RenameUserDevice /
  RevokeUserDevice, mounted at /api/devices/* behind UserAuth().
  PairDevice enforces 5-per-user cap and returns the raw sk- once,
  to be stored in the client's OS keychain (not providers.json).
- operation_setting.DeviceBindingSetting: MaxDevicesPerUser=5,
  TimestampWindowMs=120000, NonceTTLSec=300, RequireGlobal=false.

Tests:
- common/crypto_test.go covers round-trip + tamper + malformed inputs.
- middleware/device_signature_test.go covers all error-path branches
  (expired ts, wrong sig, tampered body, fingerprint mismatch, replay,
  revoked, missing headers, legacy fallthrough).
- testdata/device_signature_vectors.json is the cross-language contract
  Rust+TS sides will load to assert byte-identical canonical strings.

Untouched but reserved for follow-up phases:
- Anomaly detection / IP-diversity flagging (P1)
- 30-day deprecation banner + email notifications (P2)
- Hard cutover RequireGlobal=true (P3, day 31)
2026-05-20 12:10:57 +08:00
chenchen 9d50c09c62 feat(manager-web): add contact email november@taijiaicloud.com to footer
Surface a clickable mailto link in the page footer so users on
code.xinghanlab.com have a clear contact path without digging into
docs. Lives in the bottom border-separated band alongside the
'Platform Console' label.

i18n: 'Contact' key added to en (Contact) and zh (联系我们).
2026-05-19 22:33:46 +08:00
chenchen 0941463962 chore: gitignore heicode-winos-release/ (separate Win release repo) 2026-05-19 18:05:09 +08:00
chenchen 15e26d415e feat: split Win/Mac updater feeds + new heicodeblob win container
Win release artifacts now live in their own Azure Blob container
(heicodeblob/win/) — Manager download page and Tauri auto-updater both
point at win/updater/win-latest.json. Mac stays on the existing custom
sha256-verified mac-latest.json until the Mac CI/CD pipeline migrates
to Tauri minisign format.

Also fixes the in-app balance pill: /v1/dashboard/billing/{subscription,usage}
on the Manager accepts the `sk-` channel token, /api/user/self does not.
Switched the proxy in heicode-auth to use the billing endpoints so the
BalanceBar actually renders real remaining/used quota.

- cc-haha/desktop/src-tauri/tauri.conf.json: updater endpoint → win container
- cc-haha/src/server/api/heicode-auth.ts: balance via /v1/dashboard/billing
- cc-haha/desktop version bump 0.2.4 → 0.2.5 (next Win release)
- cc-haha/.gitignore: exclude installer artifacts (msi, dmg, sig, …)
- heicode/controller/desktop_download.go: dual-feed (Win Tauri + Mac custom)
2026-05-19 17:03:15 +08:00
chenchenandClaude Opus 4.6 c10a8e2cb1 fix: balance API always failing — swap primary/fallback order
The balance endpoint was trying /api/user/heicode/balance on
mcp-server first, but that route doesn't exist on the Manager
backend, so it always 404'd. The fallback to /api/user/self
only ran if apiKey was present but the guard was nested behind
the mcpAuth check.

Swap the order: try /api/user/self with apiKey first (always
works after login), fall back to mcp-server path second.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-19 12:19:37 +08:00
chenchenandClaude Opus 4.6 21fccd7eaa release: desktop 0.2.4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-19 11:58:48 +08:00
chenchenandClaude Opus 4.6 6242bda36b fix: updater progress jumping and click-twice issues
- Guard installUpdate() with isDownloading flag to prevent concurrent downloads
- Deduplicate checkForUpdates() with checkInProgress promise
- Track monotonic progress (peakPercent never decreases)
- Reduce startup check delay from 5s to 1s
- Bump desktop to 0.2.4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-19 11:26:35 +08:00
chenchen ded1b5b4c0 release: desktop 0.2.3 - fix Opus 4.7 thinking, fix chat merge bug 2026-05-19 11:13:39 +08:00
chenchen f72a7e6d87 fix: prevent cross-turn assistant message merging in chat UI
appendAssistantTextMessage was merging consecutive assistant_text messages
regardless of whether they belonged to different conversation turns. Added
turnComplete flag that gets set on message_complete, error, and status(idle)
events, preventing subsequent turns from merging into the previous response.
2026-05-18 23:03:17 +08:00
chenchen 5e362d6965 feat: remove API terminal demo from Manager home page 2026-05-18 22:27:28 +08:00
chenchen cb8e5a430d fix: convert thinking.type=enabled to adaptive for Opus 4.7 in ClaudeHelper
Claude Code sends Anthropic format requests directly to /v1/messages with
thinking.type=enabled. ClaudeHelper was forwarding this unchanged to
upstream, but Opus 4.7 rejects thinking.type=enabled and requires
thinking.type=adaptive with output_config.effort instead.
2026-05-18 22:27:22 +08:00
chenchen ab36157ea4 Merge remote-tracking branch 'taijibaga/main' 2026-05-18 21:54:22 +08:00
chenchenandClaude Opus 4.6 21545c7d7f fix: use thinking.adaptive for Opus 4.7 in ReasoningEffort and Reasoning paths
Opus 4.7 rejects thinking.type="enabled". The effort-suffix and
-thinking suffix paths already handled this, but the ReasoningEffort
and Reasoning parameter paths still sent "enabled". Now all paths
use "adaptive" + output_config.effort for Opus 4.7.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 21:51:06 +08:00
gongzhiyongandOmX 3ac920a493 Route website registration to CodeGW signup
Website visitors now have a single registration path through the CodeGW sign-up page, while the header no longer presents a separate login action beside registration. Static export output is included so the repository matches the deployed Azure Static Web Apps artifact.

Constraint: Website must use the CodeGW sign-up URL and remove the header login button.
Rejected: Keeping the separate Agnet login URL | it sends registration traffic to the wrong surface.
Confidence: high
Scope-risk: narrow
Directive: Keep public website registration links pointed at https://code.xinghanlab.com/sign-up unless product routing changes.
Tested: pnpm run build; Azure Static Web Apps production deploy; live curl checks for sign-up URL and removed login link.
Not-tested: Browser visual pass after deployment.
Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-18 21:48:47 +08:00
chenchen 3728dc0e89 Revert "fix: route all providers through proxy to patch thinking.type for Claude 4"
This reverts commit a3cddb89c6.
2026-05-18 21:47:43 +08:00
chenchenandClaude Opus 4.6 a3cddb89c6 fix: route all providers through proxy to patch thinking.type for Claude 4
Opus 4.7 requires thinking.type="adaptive" instead of "enabled".
The CLI sends "enabled" which upstream APIs reject. Now all providers
(including Anthropic-format) route through the proxy, which patches
the thinking parameter before forwarding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 21:42:46 +08:00
chenchen e3b1c3525b Merge remote-tracking branch 'taijibaga/main'
# Conflicts:
#	cc-haha/desktop/src-tauri/Cargo.lock
2026-05-18 21:14:23 +08:00
chenchenandClaude Opus 4.6 d87c6f6ac3 chore: bump desktop version to 0.2.2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 21:13:49 +08:00
chenchen 5386bf7bf0 更新 README.md 2026-05-18 12:46:27 +00:00
chenchenandClaude Opus 4.6 8adce51961 fix: add missing fetch timeouts across server API layer
Prevent potential hangs from fetch calls without AbortSignal:
- heicode-auth.ts: token exchange (15s)
- heicode-tasks.ts: forwardJson upstream calls (60s)
- createDirectConnectSession.ts: session creation (15s)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 18:44:41 +08:00
chenchenandClaude Opus 4.6 bbe5c8866b fix: increase proxy stream timeout from 30s to 10min
The 30-second AbortSignal.timeout on streaming fetch requests was
causing long-running tool calls (file editing, code search) to be
silently killed. The UI would show the task as still running but
no data was flowing, making it appear stuck indefinitely.

- Increase fetch timeout to 600s (10 min) for both stream and
  non-stream requests
- Add 5-minute per-chunk timeout in both stream parsers so a
  truly dead upstream is detected and surfaced as an error
  instead of hanging forever

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 18:33:47 +08:00
chenchenandClaude Opus 4.6 9a3597c923 feat: add registration link on sign-in page
Login page now shows "Don't have an account? Create a new account"
link to /sign-up when registration is enabled, replacing the old
"Contact tenant operator" mailto link.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 18:20:54 +08:00
chenchenandClaude Opus 4.6 854c86879e chore: bump version to 1.2.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 18:15:05 +08:00
chenchenandClaude Opus 4.6 202605116b chore: bump version to 1.1.0-default-user-role
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 18:01:50 +08:00
chenchenandClaude Opus 4.6 330b99061c style: upgrade sign-up page UI to match sign-in page aesthetics
- Add brand gradient title, status badge, and descriptive subtitle
- Polished form inputs: h-12 rounded-xl with icons matching sign-in
- Password fields in 2-column grid layout on desktop
- Brand gradient submit button with hover/active animations
- Styled verification code row with matching heights
- Consistent uppercase tracking labels throughout
- "Already registered?" section with link matching sign-in's footer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 17:58:04 +08:00
chenchenandClaude Opus 4.6 e6e80c6a18 feat: route registration through 太极 AI PAD API
Registration now goes through the Agnet auth proxy (/api/heicode-auth/*)
instead of the local Manager API. Email verification is always required
(太极 mandates it). After successful registration, tokens are used
directly to establish the Manager session without a redundant login call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 17:24:39 +08:00
chenchenandClaude Opus 4.6 853e3b4ca9 feat: minimize to system tray on window close instead of quitting
When user clicks the X button, the app now hides to the system tray
instead of exiting. Users can restore the window by clicking the tray
icon or selecting "显示 Heicode" from the tray menu. To fully quit,
use "退出 Heicode" from the tray menu.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 16:35:34 +08:00
chenchenandClaude Opus 4.6 bda36c44be fix: safe type assertions across OAuth, model Scan, relay, and middleware
- OAuth: safe type assertions for session state and affiliate code
  (oauth.go, github.go, discord.go, linuxdo.go, oidc.go)
- Model: Scan() methods handle string values from DB drivers, not just []byte
  (channel.go ChannelInfo, task.go Properties/TaskPrivateData)
- Model: safe type assertion in CleanupChannelPollingLocks sync.Map iteration
- Relay: safe type assertions in audio_handler, AWS InvokeModel,
  ollama ConvertClaudeRequest, claude stop sequences, zhipu token cache
- Service: fix slice bounds panic in Gemini->OpenAI stop sequences conversion
- Service: safe type assertion in CleanupFileSources middleware
- Middleware: add missing c.Abort() in turnstile session save failure
- Middleware: safe type assertion in distributor channelId
- Middleware: safe int comparison in auth helper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 16:21:34 +08:00
gongzhiyong f5e5ce37de Reopen launch popup on Chinese switch 2026-05-18 15:05:00 +08:00
gongzhiyong c41733e6d9 Add launch announcement popup 2026-05-18 14:59:41 +08:00
gongzhiyong 23d9e6b34d Add website registration links 2026-05-18 14:45:31 +08:00
gongzhiyong dd79f41a69 Restore website from xiaohei main 2026-05-18 14:14:32 +08:00
gongzhiyong 8135798e9e chore: configure macOS Developer ID signing 2026-05-18 13:33:32 +08:00
chenchenandClaude Opus 4.6 f43aa269d6 fix: enforce Rule 1 JSON wrappers across 80+ files, fix 6 bugs
- Replace all encoding/json direct calls with common.Marshal/Unmarshal/DecodeJson per Rule 1
- Fix Dify nil pointer dereference on remote image upload (relay-dify.go)
- Fix Claude relay file content type detection for text/* and PDF (relay-claude.go)
- Fix unsafe type assertions in Claude relay and Vertex GetModelRegion
- Fix StreamScanner unconditionally resetting pre-existing StreamStatus
- Add inferMimeTypeFromFilename() for proper MIME type handling in DTO
- Fix Mac build script hardcoded DMG version (now reads from tauri.conf.json)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 12:39:02 +08:00
chenchenandClaude Opus 4.6 16410270a1 fix: AWS Nova empty content panic + DTO Rule 6 pointer types
- relay/channel/aws/relay-aws.go: add bounds check before accessing
  Content[0] — empty response array caused index-out-of-range panic
- dto/embedding.go: convert Seed, TopK, NumPredict, NumCtx from int
  to *int so explicit zero values survive omitempty marshaling (Rule 6)
- dto/video.go: convert Fps, Seed, N from int to *int (Rule 6)
- dto/suno.go: convert ContinueAt from float64 to *float64 (Rule 6)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 23:17:17 +08:00
chenchenandClaude Opus 4.6 e31fe390f3 fix: use common.Marshal/Unmarshal per Rule 1 + telegram null safety
- model/user.go: replace json.Unmarshal/Marshal with common.* wrapper
  functions as required by project Rule 1 (3 occurrences)
- relay/channel/claude/relay-claude.go: replace 3 json.* calls with
  common.* (tool call args unmarshal, response marshal)
- relay/channel/gemini/relay-gemini.go: replace 5 json.* calls with
  common.* (content parsing, function args, response marshal)
- adapters/telegram/index.ts: add optional chaining on callback query
  message.chat.id and null coalescing on message.text to prevent crash
  when callback message is undefined

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 23:09:32 +08:00
chenchenandClaude Opus 4.6 aa52b1265b fix: Windows path bugs, null safety, and URL encoding across client + manager
Client (cc-haha):
- server/api/sessions.ts: use path.basename() instead of split('/').pop()
  for extracting project/repo names on Windows
- server/api/filesystem.ts: use os.tmpdir() and os.homedir() instead of
  hardcoded '/tmp' and process.env.HOME which don't exist on Windows
- utils/plugins/pluginVersioning.ts: split on /[/\]/ for Windows paths
- utils/plugins/loadPluginCommands.ts: handle backslash separators in
  plugin namespace construction
- cli/handlers/autoMode.ts: add optional chaining on response.content
  to prevent crash when API returns null content

Manager (heicode):
- auth/api.ts: fix status always returning 1 regardless of active state
  (was `? 1 : 1`, now `? 1 : 2`)
- users/api.ts, redemption-codes/api.ts, profile/api.ts: use
  URLSearchParams for query string encoding to prevent breakage with
  special characters in search keywords and email addresses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 22:59:26 +08:00
chenchenandClaude Opus 4.6 a3c6261d47 fix: safe type assertions in auth middleware + decode error check in password reset
- middleware/auth.go: session.Get() returns interface{} which can be nil;
  use safe type assertions with ok checks to prevent panics on corrupted sessions
- controller/misc.go: replace json.NewDecoder with common.DecodeJson per project
  convention, add error check before using decoded struct
- handle-server-error.ts: add optional chaining on error.response.data to prevent
  crash when response body is undefined

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 22:19:31 +08:00
chenchenandClaude Opus 4.6 ecaa107009 chore(release): bump Manager VERSION to 1.1.1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 21:31:00 +08:00
chenchenandClaude Opus 4.6 f88f6a8c46 release: 0.2.1 — Windows path fix + version bump (Win & Mac)
Bump version to 0.2.1 across tauri.conf.json, package.json,
Cargo.toml, updater manifest, and Mac fallback URL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 19:03:32 +08:00
chenchenandClaude Opus 4.6 1090cd0809 fix(desktop): Windows path separator handling across 10 components
On Windows, Tauri returns paths with backslash separators. Multiple
components used .split('/') to extract filenames/segments, which
returned the entire path as a single element on Windows. Changed all
instances to .split(/[/\]/) to handle both Unix and Windows paths.

Affected: ProjectContextChip, ToolCallBlock, PermissionDialog,
ToolCallGroup, FileSearchMenu, InlineImageGallery,
LocalSlashCommandPanel, ProjectFilter, StatusBar, DirectoryPicker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 18:57:40 +08:00
chenchenandClaude Opus 4.6 a4e566019a fix(desktop): correct @ file search replacement in EmptySession
Same bug as ChatInput — inserting filename at cursor without removing
the @filter trigger text, producing @foofile.ts instead of file.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 13:38:59 +08:00
chenchenandClaude Opus 4.6 2172045a7a fix(desktop): correct @ file search replacement in ChatInput
The onSelect handler inserted the filename at cursor position without
removing the @filter trigger text, producing "@foofilename.ts" instead
of replacing the whole "@foo" with "filename.ts".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 13:18:15 +08:00
chenchenandClaude Opus 4.6 20ca3421ab fix(desktop): prevent empty blocks rendering in chat UI
- Guard empty thinking events from creating blank ThinkingBlock rows
- Skip empty assistant_text from history loading
- Hide ToolResultBlock when content is empty (non-error)
- Add component-level null returns as safety net

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 13:11:37 +08:00
chenchenandClaude Opus 4.6 838f3717e5 release: 0.2.0 — CJK font consistency + remove stale upstream references
- Fix CJK font rendering: add PingFang SC, Microsoft YaHei, Noto Sans CJK SC
  fallbacks to all CSS font stacks (headline, body, label, mono)
- Clear docs_link default (was pointing to upstream docs)
- Remove user-facing "NewAPI" text from en/zh i18n strings
- Bump desktop version to 0.2.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-17 11:45:51 +08:00
chenchenandClaude Opus 4.6 460bdcc951 fix: Manager favicon/logo + ripgrep sidecar detection (#433 #208)
Manager web:
- Replace favicon.ico (both default & classic themes) with new H icon
- Compact heicode-logo.svg from 204KB base64 blob to 5KB
- Replace classic theme logo.png (was still old NewAPI icon)

Client (cc-haha):
- Fix isInBundledMode() to detect Bun-compiled sidecars that have no
  explicit embeddedFiles — checks process.execPath basename instead
- Add well-known ripgrep install paths (/opt/homebrew/bin, /usr/local/bin,
  ~/.cargo/bin, etc.) as fallback when PATH is incomplete in Tauri sidecar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 16:23:15 +08:00
chenchenandClaude Opus 4.7 3e7ed0202d fix(manager): real logo PNG + Mac fallback is now a .dmg
- assets/logo.tsx: drop the hand-drawn "H" path SVG and render
  /logo.png (already swapped to the new gradient icon when we
  refreshed branding). One source asset across desktop + favicon +
  Manager web.
- desktop_download.go: Mac fallback URL points at HeiCode_0.1.9_
  aarch64.dmg now (drag-to-Applications installer), not the
  .app.tar.gz updater payload that confuses end users.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:13:52 +08:00
chenchenandClaude Opus 4.7 ac1aa87312 release: 0.1.10 — balance widget actually shows + avatar ring
Balance pill was rendering nothing for admin/root accounts because
mcp-server's §4 returns HEICODE_USER_NOT_FOUND for users that never
came through from-agnet onboarding. Fall back to Heicode NewAPI's
own /api/user/self when that happens; reshape into the same envelope
so the UI is path-agnostic.

New balanceStore (zustand) — single polling loop, BalanceBar +
avatar ring share it. AppShell starts it once auth bootstraps.
BalanceBar now shows a "loading…" placeholder on first fetch so
the widget is visible from frame one.

SidebarUserCard avatar wears an SVG ring whose arc length tracks
remaining/(remaining+used) and color hits the same green→amber→red
thresholds as the bar.

Manager: /desktop-client drops the manifest-notes wall of text, the
old HEICODE_DESKTOP_FILE_* subtitle goes away, and a Mac fallback
entry is always spliced in when the live manifest is Win-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:44:13 +08:00
chenchenandClaude Opus 4.7 47a74eaf03 release: 0.1.9 — balance progress bar + roll up of 0.1.6-0.1.8
Today's 0.1.6/0.1.7/0.1.8 all built but never shipped a manifest
(some were superseded mid-iteration; 0.1.8 had Win binary on blob
but Mac wasn't ready). 0.1.9 ships the full stack as one release:

- BalanceBar under the composer now has a real progress bar (was
  text only). Fill = remaining/(remaining+used); color shifts
  green → amber → red below 30%/10%.
- (from 0.1.8) AskUserQuestion early-return moved below all hooks
  so the render order is stable across input mutations.
- (from 0.1.8) chatStore content_delta throttle is now per-session
  (Map<sessionId, {pending, timer}>); no more cross-tab text bleed.
- (from 0.1.8) endpoints array trimmed to blob-only — SWA URL gone
  so a fallback failure no longer flashes a third-party domain.
- (from 0.1.7) new app icon — already on disk in icons/ + public/.
- (from 0.1.6) Manager desktop_download.go reads blob manifest so
  the /desktop-client page tracks releases without env wrangling.
- Build pipeline: `tauri build --bundles nsis` is the release path
  (skips MSI/WiX, ~2-3 min/build saved). sccache wired into
  ~/.cargo/config.toml; next build is the first with warm cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:18:12 +08:00