Commit Graph
135 Commits
Author SHA1 Message Date
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
chenchenandClaude Opus 4.7 dea6d251a4 fix: 0.1.8 — hook-order in AskUserQuestion + per-session stream buffer
P0: AskUserQuestion.tsx had `if (questions.length === 0) return null`
sitting between the `useState` calls and the `useMemo` calls below.
On any render where parseInput(input) flipped between empty and
non-empty (e.g. streaming permission_request input mutates) React
threw "Rendered more hooks than during the previous render". Moved
the early return after every hook call.

P1: chatStore.ts had `pendingDelta` + `flushTimer` at module scope,
shared across every active session. When two sessions streamed at
the same time (e.g. user has a team-member tab open alongside their
own), session B's content_delta would queue onto the same module
buffer as session A; whichever flush timer fired first emptied the
buffer into its own session. Result: text leaked between
conversations.

Replaced with a `Map<sessionId, { pending, timer }>` so each stream
owns its own throttle buffer. `consumePendingDelta(sessionId)` and
`dropBuffer(sessionId)` keep the API similar to before; nine
callsites updated.

Both issues caught by the bug-audit agent run after 0.1.7 build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:17:22 +08:00
chenchenandClaude Opus 4.7 de239c32d6 release: 0.1.7 — new H glyph icon (desktop + Manager web) + manifest-driven Manager downloads
Visual brand refresh: replaced the desktop client + Manager web logos
with the new glossy gradient H glyph.

Desktop (cc-haha):
- src-tauri/icons/*: regenerated via `tauri icon` from new-icon.png
  (center-cropped 1024 square). Updates icon.ico/icns/png + 8 Windows
  Square*Logo sizes + iOS + Android mipmaps.
- public/app-icon.png + app-icon.svg: replaced. SVG is now a wrapper
  around the embedded PNG so existing <img src="…app-icon.svg"> refs
  keep working without recoloring tooling.
- Version bumped to 0.1.7 across tauri.conf.json, package.json,
  Cargo.toml.

Manager web (heicode/web/default):
- public/logo.png: 256x256 of the new glyph.
- public/favicon.ico: multi-size ico (16/32/48/64/128/256).
- public/heicode-logo.svg: same SVG-wraps-PNG trick as desktop.

Backend (heicode/controller/desktop_download.go):
- Already redeployed earlier today — the Manager web "Heicode 桌面客户端"
  page now sources its download URLs from the Azure Blob updater manifest
  instead of VM-local files. This is the fix for "still downloading
  Heicode_0.1.0_windows_x64_msi.msi"; the page will reflect 0.1.7 the
  moment the manifest publishes below.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:37:18 +08:00
chenchenandClaude Opus 4.7 02866a8a33 release: 0.1.6 — balance pill under composer + Manager download fix
cc-haha desktop (0.1.6):
- New sidecar route `/api/heicode-auth/balance` proxies mcp-server
  §4 `/api/user/heicode/balance` using the active provider's stored
  access token. Returns null silently on 401 / network failure so
  the UI doesn't flash error strips.
- New BalanceBar component renders a compact pill right below the
  ChatInput: `[wallet icon] $X.XX 剩余 · 已用 $Y.YY · N 次`. Polls
  every 60s. Hidden when not logged in.
- Quota → USD display uses NewAPI convention (500_000 units = $1).

heicode Manager (Go controller):
- `GetDesktopDownloads` rewritten to pull from the same Azure Blob
  updater manifest the in-app updater uses (`heicodeblob/.../
  updater/latest.json`). 5-min in-process cache; stale-on-error
  fallback. Stops the Manager web from showing stale
  `Heicode_0.1.0_x64-setup.exe` after fresh releases.
- `DownloadDesktopFile` kept for back-compat — it now 302s to the
  manifest's blob URL instead of streaming a VM-local file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:22:15 +08:00
chenchenandClaude Opus 4.7 da80b97f54 release: 0.1.5 — hide IM adapter UI (out of product scope)
Per product-package design docs, IM integration (Lark / Feishu /
Telegram) is not a Heicode surface. Hide UI without ripping out
plumbing — keep AdapterSettings.tsx + adapterStore + types in the
source tree so re-enabling is a one-flag flip.

- Settings.tsx: drop the "IM 接入" tab button (`'adapters'` route
  still resolves internally if someone forces it, but no nav).
- NewTaskModal.tsx: remove the IM notification channel pickers
  (Feishu / Telegram checkboxes + "no channel configured" warning).
  Existing task `notification` config is preserved on edit; it's
  just no longer mutable from this UI.
- Unused imports (useEffect, useAdapterStore) cleaned to satisfy
  TS strict mode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:13:05 +08:00
chenchenandClaude Opus 4.7 6c477881c4 release: 0.1.4 — sidebar time filter + clickable user card
- Sidebar search row gains a recency-filter chip (All / 1d / 3d /
  7d). Active filter highlights brand color; selection persists in
  localStorage (`cc-haha.sidebar.timeFilter`). Default `all` keeps
  current behavior.
- SidebarUserCard becomes a button — clicking the avatar/name pill
  opens https://code.xinghanlab.com/ in the system browser via
  @tauri-apps/plugin-shell, with window.open fallback for dev builds.
  Collapsed-sidebar avatar circle also clickable.
- i18n: `sidebar.timeFilter.title/all` + `sidebar.userCard.openProfile`
  added in zh + en.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:53:22 +08:00
chenchenandClaude Opus 4.7 66b47f61be release: 0.1.3 — periodic update check + sidebar 1/3/7/all groups
- updateStore: arm a 2-hour periodic checkForUpdates after the
  startup pass so users who keep the client open for days still
  see new releases without restarting. Idempotent, silent — only
  the top-right popup surface fires.
- Sidebar: bucket session history into Past 24h / Past 3 days /
  Past 7 days / All older (was Today/Yesterday/7d/30d/Older).
  Rolling windows from `Date.now()` instead of calendar bounds.
- i18n: replace `today/yesterday/last7days/last30days` keys with
  `within1day/within3days/within7days`; ScheduledTasksList and
  ScheduledTasksEmpty migrated to the new keys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:23:48 +08:00
chenchen f888163ba7 release: bump to 0.1.2 (Mac+Win re-release including About cleanup) 2026-05-13 14:39:02 +08:00
chenchenandClaude Opus 4.7 50830c2393 feat(desktop): 0.1.1 — UX fixes + multiSelect + clean About + version bump
Roll-up of tonight's desktop fixes shipped as 0.1.1:

- PermissionDialog: cap diff/command preview at max-h-360 so the
  allow/deny buttons stay above the fold on huge writes (user
  reported scrolling 900 lines to find the buttons).
- SessionTaskBar + cliTaskStore: hover row shows ✓ manual-close
  button for tasks the agent forgot to mark completed.
- AskUserQuestion: honor schema's `multiSelect: true` — toggle
  membership across options, switch round → square indicator,
  show "可多选" hint, join answers with comma.
- HeicodeLoginPage: removed dead `'official'` filter that blocked
  the typescript build (legacy provider id no longer in the union).
- Settings About: removed the third-party social-media block and
  unused openUrl helper.
- i18n: replaced misleading "GitHub Releases" wording with
  "Heicode 官方更新源" / "Heicode update source" — actual channel
  is Azure Blob (msi/updater/latest.json) per tauri.conf.json.
- release-desktop.mjs: az invocations now run with shell:true so
  the Windows `az.cmd` resolves; also uploads the manifest to
  blob (primary endpoint) as the script finishes.
- tauri.conf.json + package.json + Cargo.toml: version bumped to
  0.1.1.
- website/public/updater/latest.json: now reflects 0.1.1 + new
  signed NSIS URL (mirrored to blob by the release script).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:34:26 +08:00
chenchenandClaude Opus 4.7 8d317b2eef release: desktop 0.1.0
First populated updater manifest. Points at the signed NSIS bundle
hosted on Azure Blob (heicodeblob/msi). Smoke-verified: HTTP 200 on
both .exe and .exe.sig public URLs.

Notes: §4 wallet usage+logs live, StreamingIndicator now shows
active-tool description, updater channel end-to-end exercised.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:58:33 +08:00
chenchenandClaude Opus 4.7 739354e786 feat(desktop): rotate updater key + concrete release pipeline
- pubkey: rotated to the keypair stored at
  C:\Users\陈晨\.heicode-updater\heicode_updater.key (private side
  is the user's; only the pubkey ships in tauri.conf.json).
- scripts/release-desktop.mjs: one-shot release helper —
  uploads the signed bundle artifacts to Azure Blob (account
  heicodeblob, container msi, public-blob-read) and rewrites
  website/public/updater/latest.json to point at the new URLs.
- UPDATER.md: rewritten with the concrete URLs, container, key
  paths, and step-by-step commands. No more generic placeholders.

Azure Blob setup (done out-of-band, not in this commit):
- Storage account heicodeblob set allowBlobPublicAccess=true
- Container msi set to public-blob read
- Smoke-tested: https://heicodeblob.blob.core.windows.net/msi/<x>
  returns 200 anonymously.

The actual Azure connection string + private-key path live in
scripts/.env.release, which is gitignored under .env.* and was
verified excluded before this commit.

NOTE: pubkey was rotated. Any MSI already in the wild signed by
the *previous* key cannot self-update to this signing chain —
those users need a fresh manual install. This is acceptable for
pre-GA where no public release exists yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:50:14 +08:00
chenchenandClaude Opus 4.7 49be79fb9a feat(desktop,website): wire updater endpoint + release tooling
- tauri.conf.json: endpoints now points at the website-hosted
  manifest (https://<azure-swa>/updater/latest.json) so the
  already-built UpdateChecker actually has somewhere to check.
- website/public/updater/latest.json: placeholder manifest with
  empty platforms map (clients will see "up to date" until a real
  release ships).
- scripts/build-updater-manifest.mjs: helper that ingests signed
  Tauri bundle artifacts and emits the manifest, so future releases
  are one node command instead of hand-edited JSON.
- scripts/UPDATER.md: step-by-step for every release — what to set,
  what to upload, where to commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:18:51 +08:00
chenchenandClaude Opus 4.7 c17be9ef3b feat(desktop): StreamingIndicator surfaces active tool + target
Replace the random spinner verb with a friendly description of the
tool currently running — "Reading foo.ts", "Editing bar.tsx",
"Running npm", "Searching <pattern>". Falls back to the verb / chatState
label when no tool is active.

Why: users were seeing "Percolating... 51s" with no idea whether the
agent was stuck or genuinely working. The chatStore already tracks
activeToolName + streamingToolInput, the indicator just wasn't using
either.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:08:47 +08:00
chenchenandClaude Opus 4.7 2052b5b140 feat(web,docs): adaptive task polling + contract patch list
- task-card-view: refetchInterval now adapts to state — 3s for
  running/awaiting_approval, stop on completed/failed, 15s otherwise.
  Approval requests now surface within 3s instead of up to 15.
- docs: collect every Manager-side compat patch (deeplink mapping,
  redact fallback, polling-vs-SSE) so mcp-server team can fold them
  back into the contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:49:42 +08:00
chenchenandClaude Opus 4.7 1796f04fa8 feat(web): wallet shows mcp-server §4 usage + recent requests
Add HeicodeUsageCard between balance and subscription plans:
- 14-day usage sparkline from /api/user/heicode/usage
- Last 6 requests from /api/user/heicode/logs

Both endpoints come from the product-package §4 contract (mcp-server),
so the figures match what the desktop sidecar sees.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:41:30 +08:00
chenchenandClaude Opus 4.7 ddc32466c2 feat(web): wallet balance reads mcp-server §4 first
Try getHeicodeBalance() (mcp-server /api/user/balance per product-package §4)
before falling back to local /api/user/self. Aligns Wallet page with the
Heicode product-package contract so balance numbers come from the same source
the desktop sidecar uses.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:56:47 +08:00
chenchenandClaude Opus 4.7 4c263040c9 feat(manager): /sk-sources now uses mcp-server P1 ResourceBinding (§2)
docs/Heicode-对接进度与待办.md §7.5 point 7 named the Manager team as
responsible for wiring P1 resource UI to mcp-server (§2 ResourceBinding,
§3 ResourceGrant) — without this, the deeplink that the desktop client
puts on the task card (`/manager/resources?from=task`) ends in a 404.

Changes:

1. lib/heicode-mcp.ts: typed wrappers for the 9 P1 endpoints
   - §2 ResourceBinding: list / get / create / update / revoke
   - §3 ResourceGrant: list / get / create / revoke
   - Field shape verified against live mcp-server with test account
     55@55.com — 7 smoke cases pass including the 422 sensitive-keyword
     enforcement and the §3 subset rule.

2. features/agnet-console/pages.tsx AgnetSKSourcesPage rewritten to
   read /api/resources (filtered to status=active) instead of the
   legacy Heicode-local git_sources controller:
   - Card 1 代码        = resources filter type='git'
   - Card 2 文档SK      = resources filter type∈{sk,project_doc}
   - Card 3 云账号      = resources filter type∈{cloud_account,
                          cloud_resource}, "auto-discovery coming soon"
                          hint shown when empty (current state)
   - Card 4 推荐摘要    = unchanged

3. Advanced sheet form rewritten for mcp-server ResourceBinding shape:
   {type, name, external_ref, metadata, permission_scope, constraints,
    secret_ref, status}. Old (provider, repo_url, ref, paths, usage,
    tenant_id) maps in:
     name          → name
     repo_url      → external_ref
     provider      → metadata.provider
     ref           → metadata.default_branch + constraints.ref
     paths         → constraints.allowed_paths (comma-joined)
     usage         → type ('git'/'sk'/'project_doc')
     tenant_id     → dropped (server uses auth.user_id)
     —             → permission_scope ['repo:read'] minimal default
     —             → secret_ref blank for now (server fills once
                     OpenBao Secret Broker lands per §2.1 TODO)

   Form also surfaces the §2.1 422 RESOURCE_GRANT_SECRET_REJECTED
   server-side error to the user.

4. Removed unused imports (GitSource{,Payload,Usage}, createGitSource,
   deleteGitSource, listGitSources) — legacy git_sources controller is
   still in the Go backend for now but the Manager no longer consumes it.

5. RecommendationSummaryDialog now takes ResourceBinding[] for project /
   sk source counters instead of GitSource[].

Smoke verified end-to-end against live mcp-server:
  list / create (incl. metadata+constraints+permission_scope) / get /
  delete-binding all 200 with expected shapes; 422 secret rejection
  fires on metadata.{name containing 'token'}; §3 subset rule on
  allowed_actions outside binding.permission_scope returns
  RESOURCE_GRANT_INVALID.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:33:18 +08:00
chenchenandClaude Opus 4.7 ab53f34334 fix(tasks): align HeicodeTaskCard fields with live mcp-server shape
End-to-end smoke against mcp-server (test account 55@55.com) revealed
the actual task.card shape:

  { goal: string,
    scope: string[],
    generated_artifacts: string[],
    manager_actions: Array<{label, deeplink}> }

My earlier TS types and TaskCardView assumed
{objective, first_version_scope, auto_generated, pending_context} —
keys that don't exist in the real response. Tasks created via the
intent flow would have rendered with empty bullet lists.

Changes:
- lib/heicode-mcp.ts: rewrite HeicodeTaskCard to the live shape, add
  HeicodeManagerAction type
- features/tasks/task-card-view.tsx:
  - read card.goal / card.scope / card.generated_artifacts
  - new readManagerActions() helper renders mcp-server's
    {label, deeplink} buttons in place of the hardcoded action row,
    with normalizeDeeplink() mapping /manager/resources →
    /sk-sources etc. to Manager-side routes
  - dropped pending_context (no such field); follow-up "Pending
    context" footnote is now a plain explanatory line per docs §10

Smoke verified end-to-end:
  login (POST /api/auth/login) → 200 + JWT
  intent (POST /api/user/tasks/intent) → 200 + configuring task
  list   (GET /api/user/tasks)         → items shape matches type
  detail (GET /api/user/tasks/{id})    → follow-ups parse correctly
  answer × 2 (POST .../answer)         → state machine flips to running,
                                          card materialises with the 4
                                          actual fields above
  audit  (GET /api/agnet/audit-logs)   → {items, total, next_cursor}
  agnet  (GET /api/agnet/deployments)  → {items, total}
  balance (GET /api/user/heicode/balance) → HEICODE_USER_NOT_FOUND
    (test account, expected; my code catches this and returns null)

UTF-8 body through the proxy works fine (earlier "parse body" error
was a Windows shell quoting issue, not a proxy bug).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:46:27 +08:00
chenchenandClaude Opus 4.7 a1529f18c1 feat(manager): wire UI to mcp-server contract instead of local controllers
After reading the Heicode-接口契约文档 v2.2 in
~/Desktop/taijigit/taiji-AI-PAD/Docs/ the right data sources are clear:

  §1 /api/auth/*           — already wired (features/auth/api.ts)
  §4 /api/user/heicode/*   — model balance + usage + logs
  §5 /api/agnet/*          — Agnet platform stub (deployments / audit / etc)
  §6 /api/user/tasks/*     — HeicodeTask intent → followups → card

The first wave UI work (commit 2df233b) used Heicode-local controllers
as the data source (AgnetDeployment.orchestration_plan as a stand-in
for the task object). That was wrong — the contract document is clear
that HeicodeTask (§6) is the canonical user-facing task object, and the
mcp-server stub at §5 is the canonical deployment source.

This commit redirects the data plumbing without touching the UI shells:

1. New lib/heicode-mcp.ts — typed client that calls mcp-server through
   the existing same-origin /api/heicode-auth/* proxy. Implements the
   subset of §4/§5/§6 the Manager UI needs:
     createTaskFromIntent / listHeicodeTasks / getHeicodeTask / answer
     getHeicodeBalance / getHeicodeModels / getHeicodeUsage / getHeicodeLogs
     listMcpAgnetDeployments / listMcpAuditLogs

2. HomeHero (features/dashboard/components/home-hero.tsx):
   - Idea input now POSTs /api/user/tasks/intent and routes the user
     to /tasks/$id once the server returns the new task with its first
     round of follow-ups. Previously it only stashed the idea in
     localStorage which the docs §10 didn't actually require.
   - ContinueTasks + TodayFocus now consume listHeicodeTasks output
     (HeicodeTask.status / status_caption / updated_at:ms) instead of
     AgnetDeployment shape.

3. TaskCardView (features/tasks/task-card-view.tsx):
   - Reads getHeicodeTask(id) from mcp-server (refetch every 15s).
   - When status=configuring renders the open follow-ups from the most
     recent heicode thread entry as clickable option chips; clicking
     POSTs answer to /api/user/tasks/$id/answer and the server-side
     state machine advances. high-risk options get a red badge per §6.
   - When status=running (followups answered, card materialised) the
     four blocks docs §10 任务卡 mandates are rendered from task.card:
     目标 / 第一版范围 / 自动生成 / 待确认上下文.

4. AgnetAuditPage (features/agnet-console/pages.tsx):
   - Switched queryFn from local getAgnetAuditLogs to mcp-server
     listMcpAuditLogs. The redacted-card renderer already accepts any
     {resource_id, allowed_actions, constraints, secret_ref} shape so
     no UI change needed; banner still announces no plaintext.

Notes:
- /wallet refactor to §4 deferred — it pulls multiple legacy series
  from the local NewAPI controllers and the rewrite is a separate
  pass. Manager users see local data for now; the call is identical
  shape so swap is mechanical once we get there.
- Local TS check clean. Not deployed.
- Earlier 2df233b's UI structures (HomeHero shape, TaskCard layout,
  recommendation dialog, audit redacted view) stay verbatim — only
  the data fetching layer moved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:33:01 +08:00
chenchenandClaude Opus 4.7 2df233b982 feat(manager): align Manager UI with product-package docs §10/§11/§13
Five gaps closed against the updated product-package spec
(http://gitee.ath.cx:3000/xiaohei/heicode/src/branch/main/docs/product-package):

P1. /dashboard hero rewritten per §10 §"初始首页"
    features/dashboard/components/home-hero.tsx replaces the technical
    CockpitView with four blocks the spec mandates: 主输入 / 继续任务 /
    今日焦点 / 辅助入口. Main input is "你想把什么想法变成可以上线的软件?".
    Submit only stashes the idea to localStorage + toast — the actual task
    conversation belongs in the desktop client per §13 §5.1.

P2. /tasks/$id TaskCard route per §10 §"任务卡" + §11 §3
    features/tasks/task-card-view.tsx renders one AgnetDeployment as the
    user-facing task object: 目标 / 第一版范围 / 自动生成 / 待确认上下文 +
    Manager 辅助按钮. Linked from Home hero's 继续任务 list.

P3. /sk-sources 推荐摘要 dialog per §10 §"推荐确认卡"
    features/agnet-console/pages.tsx RecommendationSummaryDialog. Five
    blocks (本次会做 / 本次允许使用 / 本次不会做 / 高危规则 / 预计消耗) +
    Launch Agnet button with "参数由 Heicode 自动生成" caption. No JSON
    editor, no permission manifest — §10 高级展开禁令.

P4. /audit redacted card view per §10 §"任务用量与审计" + §6
    features/agnet-console/pages.tsx AgnetAuditPage. Old裸 table replaced
    with脱敏 cards exposing only the fields docs allows: resource_id /
    resource_type / allowed_actions / constraints / secret_ref. Helper
    function maskIfSecret() catches any stray plaintext credential the
    backend might leak. Banner says explicitly "明文密钥从不展示".

P5. Login screen filters Claude Official provider per §8
    cc-haha/desktop/src/components/login/HeicodeLoginPage.tsx hides the
    legacy `official` preset so the login carries Heicode brand alone.

i18n fix (BIG):
    i18next defaults to `defaultNS = 'translation'`. The earlier custom
    keys had been written to the JSON root, NOT into translation, so
    every t('Preparation checklist') was returning the English key as
    fallback all along. Moved 67 orphan keys (zh+en, both files) into
    the translation namespace where they're actually resolvable. Verified
    by loading i18next + zh.json in bun and confirming all keys resolve
    to the expected Chinese strings.

Cache-busting from earlier session (already deployed via SFTP, never
committed): index.html / constants.ts / footer.tsx now hold the
?v=h-glass-2 suffixed asset URLs in git, so future docker rebuilds
preserve them.

Per user directive: tested locally only (TS check clean, i18next
resolves correctly). NOT deploying to the VM in this commit — user
asked to keep production untouched until they verify the changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:01:13 +08:00
chenchenandClaude Opus 4.7 54d6a67ecf feat(client): logged-in user card in sidebar (always visible)
The earlier UserPill landed in the TitleBar tab strip, but the main
desktop layout (HeicodeTasksHome shell) renders its own chrome without
that strip — so the user info ended up invisible despite mcpAuth
having the right data after a2deeb6. Pull the same identity (avatar
gradient + name + email + role badge) straight into the bottom of
Sidebar.tsx, right above the logout/settings rows, so it's visible
on every screen regardless of which top bar is mounted.

Two layouts:
- expanded: 36px gradient avatar + name + email + admin/root badge
- collapsed: 36px avatar circle only, title tooltip carries the email

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:10:17 +08:00
chenchenandClaude Opus 4.7 da218ebbe5 fix(client): CLI launcher recognises new heicode-sidecar binary name
resolveBundledCliPathFromExecPath / resolveClaudeCliLauncher hardcoded
startsWith('claude-sidecar') / startsWith('claude-server') checks. After
the 3a358ba rebrand the bundled binary is heicode-sidecar-*.exe — the
match fails, the function returns null, and resolveCliArgs falls back
to the Windows --preload script branch which spawns the bun-compiled
sidecar with arguments it doesn't understand. The process exits 2
silently (empty stderr), surfacing in the UI as:

    Error: CLI 进程启动失败。
    CLI exited during startup with code 2.

Symptom user report: pilac69779@codoteam.com freshly registered, OAuth
login succeeded but every chat attempt failed at sidecar startup.

Fix: accept both `heicode-` and legacy `claude-` prefixes for sidecar /
server / cli binaries so MSI #15+ launchers work AND
CLAUDE_CLI_PATH=/path/to/legacy/claude-sidecar still works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 21:13:43 +08:00
chenchenandClaude Opus 4.7 a2deeb61b0 fix(client+server): OAuth login path now surfaces user identity in desktop TitleBar
Root cause of the recurring "I don't see the logged-in user info in the
desktop client" complaint: the credentials-login path builds an mcpAuth
record with email / displayName / role from the mcp-server /api/auth/login
response and the TitleBar UserPill renders fine. But the OAuth-login path
(the typical browser-redirect flow) only received an sk- API key in the
callback query string — no user fields. So:

  status.user                   == null
  UserPill: if (!user) return null
  → blank space where the user pill should be.

Fix on backend (heicode_oauth.go HeicodeOAuthAuthorize):
- After issuing the sk- token, load the authenticated user from the
  session and embed email / name / role (root|admin|user) / channel_id /
  user_id as query params on the redirect URI.

Fix on client (cc-haha/src/server/api/heicode-auth.ts handleOAuthCallback):
- Read those query params (pickUserFromQuery), build an mcpAuth record
  (buildMcpAuthFromOAuthQuery), and pass it through loginAndActivate the
  same way the credentials path does. The accessToken slot holds the sk-
  key as a placeholder — OAuth flow doesn't deliver a refreshable JWT
  pair, and this mcpAuth exists purely to surface identity on the
  TitleBar.

After this, OAuth-route users see the same gradient-avatar pill with
their email / name / role badge that credentials-route users already see.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 20:44:33 +08:00
chenchenandClaude Opus 4.7 b91a68fe2b ops(deploy): default Azure VM deploy to pull from heicode-win remote
bin/azure_vm_deploy.sh pulled from `origin` (xiaohei/heicode.git) when
GIT_REF was set. Production VM now tracks heicode-win/main and that's
where the rebrand + bug fixes ship — pulling from origin would silently
revert all of it on the next deploy. New GIT_REMOTE env var (defaults
to heicode-win) lets operators override for one-off cherry-pick deploys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:25:57 +08:00
chenchenandClaude Opus 4.7 37de6575df fix(server): backport two prod hot-patches that kept getting wiped
1. /api/heicode-auth/* proxy: CriticalRateLimit (20/20min) → GlobalAPIRateLimit
   (180/180s). The Heicode external-identity proxy is hit on every page
   render for /me + /refresh plus the login burst — CriticalRateLimit is
   sized for sensitive ops (password reset, 2FA) and trips at ~5 quick
   page loads, returning 429 to a normal user. APIM upstream rate-limits
   itself, so a second tight layer here adds no security and just
   manufactures 429s.

2. JIT-create user group: seed "default" instead of me.Data.ChannelID.
   Companion to 578a68f which only patched the every-login overwrite
   path. New users (yj2824269760@gmail.com et al, JIT-created after
   578a68f) still landed in a UUID group → empty /v1/models response →
   desktop client showed the static 3-Claude fallback list.

Both fixes were applied on the production VM directly today (sed +
python patch) — committing them so the next docker rebuild on VM keeps
them instead of reverting to the buggy file via git checkout.

DB hot-fix already applied: 6 affected users moved to group=default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:14:35 +08:00
chenchenandClaude Opus 4.7 dc33522a84 fix(client): tauri.conf externalBin claude-sidecar → heicode-sidecar (missed in 3a358ba — broke MSI #13 build)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:05:58 +08:00
chenchenandClaude Opus 4.7 3a358ba5e1 refactor(client): full Claude→Heicode rebrand across desktop runtime
Beyond the i18n strings rebrand (b55d103) this pass cleans up everything
that still surfaced "Claude" in the built artifact:

Theme tokens (cc-haha/desktop/src/theme/globals.css)
- light-mode text-selection-bg rgba(197,165,114,*) → rgba(123,107,227,*)
  (selecting ANY text in light mode used to paint a gold highlight)
- dark + light diff-highlight-bg / -gutter switched from gold to brand
  violet rgba(123,107,227,*) — keeps semantic "highlight" without leaving
  the brand

Sidecar binary rename: claude-sidecar → heicode-sidecar
- desktop/sidecars/claude-sidecar.ts renamed (git mv)
- internal log prefix strings (12 occurrences) updated
- desktop/scripts/build-sidecars.ts: entrypoint / outfileBase / productName
  ('Heicode Sidecar') / publisher ('Heicode') — these last two are embedded
  in the .exe metadata you see in File Properties
- src-tauri/capabilities/default.json: 9 binaries/claude-sidecar references
  rewritten + description "Default capabilities for Heicode Desktop"
- src-tauri/src/lib.rs: 2 .sidecar("claude-sidecar") + 3 packaged sidecar
  exe filename strings + 1 doc comment
- desktop/sidecars/launcherRouting{,.test}.ts

After this rebuild, Task Manager shows heicode-sidecar-*.exe child
processes (was claude-sidecar-*.exe) and right-click → Properties on
that binary shows "Heicode Sidecar" / "Heicode" instead of "Claude Code".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:56:03 +08:00
chenchenandClaude Opus 4.7 b6607c9a3e fix(client): light theme stuck on old gold palette
[data-theme=light] block in globals.css still defined --color-primary as
#8E7547 with gold gradient buttons and gold focus rings. Dark mode was
correctly on the brand violet #7B6BE3 since the Iris overhaul, but the
moment a user switched to (or was OS-defaulted to) light theme they got
the legacy gold mark on login button, focus ring, sidebar active state,
shadow-button-primary, etc.

Rewrote the light override to mirror the dark brand palette:
- --color-primary #7B6BE3, primary-container #5B4FB8, fixed #9A8DEC
- --color-secondary #6B7CE0 (was slate #5A6B82)
- --gradient-btn-primary 3-stop violet→blue (matches dark mode + Manager)
- --gradient-brand-wordmark also added in light scope (was inheriting
  but explicit avoids cascade surprises)
- All rgba(142,117,71,*) shadows / borders rewritten to rgba(123,107,227,*)

Code-syntax / diff highlight golds left alone (those tint code text, not
brand UI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:27:45 +08:00
chenchenandClaude Opus 4.7 88c5b4a285 feat(manager-web): align login + header with Heicode brand violet
Theme tokens (theme.css)
- --primary retuned to the brand violet oklch(0.58 0.17 278) ≈ #7B6BE3
  (was a saturated pink oklch ... 286 that read off-brand against the H
  glass logo)
- New tokens: --brand-from #B888E5, --brand #7B6BE3, --brand-to #6B7CE0,
  --gradient-brand (135deg three-stop), --gradient-brand-btn for primary
  CTAs. Both light and dark modes hold the same brand identity.

Login page (auth-layout.tsx + sign-in/index.tsx + user-auth-form.tsx)
- Inlined H glass mark SVG replaces the abstract ShieldCheck pictogram
- Brand wordmark uses gradient text-fill so the word "Heicode" reads as
  the same gradient as the logo
- "Tenant access" pill and h2 heading both pick up brand violet via
  border / bg / gradient text
- Sign-in button switches from solid var(--primary) to the three-stop
  --gradient-brand-btn with violet drop shadow + lift-on-hover

Header user info (profile-dropdown.tsx)
- The right-side trigger used to be a bare 36px avatar — invisible user
  identity unless you click. Now it is a pill: gradient-filled avatar
  initials + display name + email + role badge, always visible on >=sm
- Avatar fallback fills with --gradient-brand so even pre-image, the
  user pill carries Heicode color identity

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:24:04 +08:00
chenchenandClaude Opus 4.7 b55d103ea3 feat(client): rebrand user-facing 'Claude' strings → 'Heicode'
Permission prompts, chat placeholder, empty state, computer-use
approval, MCP / Agents / Skills / Plugins / Computer Use settings
descriptions all said "Claude" — the desktop client is a Heicode
product, not Claude Code. 23 strings in zh + en updated.

Protected (deliberately kept):
- settings.claudeOfficialLogin.* — feature literally signs into Claude.ai
- 'Claude Official' / 'Claude.ai' — upstream provider names
- ~/.claude/* file paths — actual filesystem locations the CLI reads
- claude-plugins-official — github org id in plugin marketplace
- '(Claude' in loggedInPrefix — formats real upstream session subtype

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:05:31 +08:00
chenchenandClaude Opus 4.7 f650462f17 feat(manager-web): drop third-party model brand grid from landing
Per docs §10 "禁止普通用户体验出现:模型供应商配置 / 模型提供方
选择" — the dashboard landing hero showed "OpenAI / Claude /
Gemini / DeepSeek / Qwen / Llama" as a marketing matrix, which
leaks upstream provider branding into the user-facing surface.

Replaced with Heicode capability tags (通用 / 长上下文 / 推理 /
代码 / 多模态 / 高性价比). This is the landing the user lands on
after login; admins still see real upstream provider names in
the system-settings → models tabs (those are technically the
channel protocol names and removing them would mislead admins).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:30:58 +08:00
chenchenandClaude Opus 4.7 7c3ecbcefc feat(manager): align with product-package docs §10/§11
CORS unblock — add /api/heicode-auth/*proxyPath backend proxy to
HEICODE_AUTH_BASE_URL. Frontend defaults to same-origin path so
the browser never hits APIM directly.

Sidebar — replace backend jargon (Git sources / Deployments /
Events / Wallet / Available models / Profile) with the user-facing
labels docs §10 mandates: 总览 / 准备清单 / 任务总览 / 审计 /
模型与余额 / 客户端 / 账号安全.

/sk-sources rewritten as 4-card preparation wizard with progress
meter; full Git form moves into a 高级补充 sheet. Drops JSON
editor, permission manifest, snapshots and resource-grant pills.

/deployments simplified to 任务总览: objective + status + last
update. Drops risk / budget / scope / secret_ref pills and the
RunDetailPanel; manifest details only in audit/advanced views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:01:14 +08:00
chenchenandClaude Opus 4.7 afceb7cc2e feat(manager-web): expand favicon ladder + hide unused 2FA/Passkey UI
index.html: add png/ico/apple-touch favicon links, og:image,
and brand theme-color (#7B6BE3) so all icon surfaces use the
H glass mark.

Hide unused auth UI:
- profile page: drop PasskeyCard + TwoFACard
- system-settings/auth: drop Passkey Authentication section

These features aren't part of the Heicode platform flow (auth
is delegated to the identity service / SSO); leaving them in
the UI confuses tenants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 10:52:28 +08:00
chenchenandClaude Opus 4.7 c47db748e5 feat(manager-web): swap logo to H glass mark
Match Heicode desktop client app icon: violet→blue gradient
rounded-square with white H. Replaces favicon.ico, logo.png
(used in footer / system info), heicode-logo.svg (browser
favicon link), and the inline Logo component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 18:49:51 +08:00
chenchenandClaude Opus 4.7 5fc78f28c1 feat(client): Wave 2 — premium polish for chat, sidebar, task home surfaces
Wave 1 was design tokens + login screen. Visible-but-subtle. Wave 2
ships the meaningful in-app changes a user actually sees after they
log in. Aggressive enough that the post-login surface feels like a
2024-vintage product, not a 2018 utility.

User chat bubble (src/components/chat/UserMessage.tsx)
  - Background: violet-tinted vertical gradient
    (rgba(123,107,227,0.10 → 0.04)) over surface-user-msg
  - 1px violet-18% border + soft violet-8% drop shadow
  - Asymmetric corner (18/4/18/18) preserved
  - Effect: user messages read distinctly from assistant messages
    at a glance, no need to look at the avatar.

Assistant chat bubble (src/components/chat/AssistantMessage.tsx)
  - Top-down white wash (rgba 0.025 → 0) on container surface
  - Two-stop shadow (1px ambient + 4px lift) so the bubble lifts
    off the page instead of disappearing into the chat background
  - Border tightened to full --color-border (was 60% opacity)

Glass-panel composer surface (src/theme/globals.css)
  - Top violet wash (5% → 0% over 35%) over glass surface
  - Inset top hairline (white 4%) for lit-from-above feel
  - Hover: violet-18% border tint
  - Focus-within: violet-45% border + 3px violet-18% halo + dropdown
    shadow, so typing feels obviously "on"
  - Smooth 200ms transitions on border + shadow

Sidebar active session (src/components/layout/Sidebar.tsx)
  - Left edge: 0.5px × 20px violet→blue gradient rail with 8px
    violet glow — instant focus anchor
  - Active dot: 8px violet glow shadow
  - Active row: 1px violet-24% border + inset top hairline
  - Inactive: hover lifts text to primary (was secondary→primary on
    hover, now also adds violet-6% bg tint via token change)

Tasks home intent box + recent task cards + workspace reply box
(src/pages/HeicodeTasksHome.tsx)
  - Intent box: violet wash gradient + transition + hover/focus
    border tint
  - Recent task cards: hover -translate-y-px lift + violet-32%
    border + violet-10% drop shadow → clearly clickable
  - Workspace reply box: same violet wash + focus halo

Type-check: bunx tsc -b --noEmit clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 18:09:22 +08:00
chenchenandClaude Opus 4.7 463f9be2db feat(client): premium UI overhaul — Iris design system + single-instance lock
Production-grade visual upgrade. Pulls the violet→blue gradient from
the H glass app icon up into a coherent design system: brand color,
focus rings, button gradients, sidebar accents, selection highlight,
shadow tints all derive from the same palette. Result: login surface
no longer clashes with the app icon, post-login UI feels like a
single product instead of a Tauri shell.

Changes
-------

src/theme/globals.css — Iris design tokens:
  - --color-primary #C5A572 (gold) → #7B6BE3 (violet, from logo)
  - --color-secondary slate → #6B7CE0 (blue from logo)
  - --color-tertiary gold → #9A8DEC (lighter violet)
  - Surface stack rebased to slight cool/blue undertone (#13131A,
    #181820, …) so violet accents pop without clashing
  - Borders softened: --color-border 0.08 → 0.06, separator 0.06 → 0.04
  - Radii loosened: sm 4 → 6, md 8 → 10, lg 12 → 14, xl 16 → 18
  - --gradient-btn-primary: 3-stop violet→primary→blue (replaces flat
    gold gradient); CTA glow shadow tinted violet
  - --gradient-brand-wordmark exposed as a token so wordmark / avatar
    gradient stays consistent across surfaces
  - --shadow-dropdown: 3-stop layered, dark stop tinted violet so
    floating menus feel of-a-piece with brand
  - --shadow-focus-ring: violet 55% so input focus is visible
  - --color-selection-bg: violet 32%
  - Sidebar item hover/active: rgba violet base instead of plain white
  - Sidebar panel backdrop: 2-stop radial (violet top-left + blue
    bottom-right) replacing plain gold radial
  - Type scale tokens: --text-xxs … --text-display
  - Body font-feature-settings: 'kern','liga','cv11' +
    text-rendering: optimizeLegibility for crisp Inter / Manrope
  - .tabular-nums utility for status counters

src/components/login/HeicodeLoginPage.tsx — login redesign:
  - Embeds /app-icon.png (the H glass logo) above wordmark
  - Drop-shadow violet-tinted so icon feels continuous with backdrop
  - Layered backdrop: violet halo top, blue glow bottom, faint SVG
    grain texture for tactile premium feel
  - Wordmark + tagline use --gradient-brand-wordmark token
  - Card max-width tightened to sm (was md) for taller, more focused
    composition (Linear / Vercel / Raycast desktop login style)
  - Chrome strip: blurred translucent surface

src/components/login/ProviderLoginCard.tsx:
  - Card gets faint violet→neutral surface gradient (lifts off backdrop
    without heavy 1px border)
  - Top hairline accent (centered violet line) at card top edge
  - CTA button uses --gradient-btn-primary; hover glow stronger;
    active scale 0.99 microinteraction
  - Removed hardcoded rgba(197,165,114,…) gold artifact

src/components/layout/TitleBar.tsx:
  - UserPill avatar gradient now uses --gradient-brand-wordmark token
    (was inline hardcoded violet) so future palette tweaks propagate

src-tauri (Cargo.toml + src/lib.rs):
  - Add tauri-plugin-single-instance and register it as the FIRST
    plugin in the Builder chain. Subsequent Heicode launches focus
    the existing window via show_main_window() instead of spawning
    a new sidecar / new server port. Fixes "every shortcut click
    starts a new instance" behavior.

Verification
------------
  - bunx tsc -b --noEmit (desktop)         clean
  - cargo check (src-tauri)                 clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:38:31 +08:00
chenchenandClaude Opus 4.7 6cc981d366 fix(server/web): SPA navigate() to backend bridge URLs renders 404
When the desktop one-click login flow opens
  /heicode/oauth/authorize?state=...&redirect_uri=...
in the browser and the user isn't signed in, Manager renders the
"please log in" bridge page that links to /sign-in?redirect=<authUrl>.
After login, useAuthRedirect's `handleLoginSuccess` calls TanStack
Router's `navigate({ to: targetPath })` to send the user back to
that authorize URL.

But TanStack Router only knows about React routes; backend bridges
(`/heicode/oauth/...`, `/api/...`) have no matching route, so the
SPA renders 404. The user has to manually re-enter the URL, at
which point the backend handles it and 302s to the loopback
callback. This produced the 500 → 404 → success symptom users hit
on first-time desktop login.

Fix: detect backend prefixes (`/heicode/oauth/`, `/api/`) and use
`window.location.assign()` to force a full-page navigation so the
server gets the request directly. React-route paths still go
through `navigate()` as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:29:01 +08:00
chenchenandClaude Opus 4.7 6ca8bf42d6 feat(client): match login color to logo + show user pill in title bar
#1 Login wordmark color
   The big "HEICODE" wordmark on the login page was rendered in the
   gold brand color (#C5A572), which clashed visually with the
   purple→blue glassy H app icon shown in the window/taskbar/dock.
   Now the wordmark uses the same linear-gradient(135deg, #B888E5,
   #6B7CE0) as the logo so the login surface matches the icon.
   Backdrop radial glow tinted to match. Other surfaces keep gold
   primary so buttons/accents stay consistent app-wide.

#3 User pill in TitleBar
   Logged-in users had no in-app affordance to confirm "I'm signed
   in as X" or to know which account they're using. Add a pill on
   the right of TitleBar showing initial avatar (logo gradient) +
   display name + admin/root role badge.

   Wiring:
   - cc-haha/src/server/types/provider.ts: McpAuth gains optional
     email / displayName / role fields. Backwards-compatible (all
     optional, existing saved providers stay valid).
   - cc-haha/src/server/api/heicode-auth.ts: handleLoginWithCredentials
     now reads user.{name|display_name, email, role} from the
     /api/auth/login response and persists them on the provider's
     mcpAuth record. /api/heicode-auth/status returns a `user` field
     synthesized from the active provider.
   - cc-haha/desktop/src/api/heicodeAuth.ts: HeicodeAuthStatus type
     gains the `user` shape.
   - cc-haha/desktop/src/components/layout/TitleBar.tsx: new UserPill
     component reads from useHeicodeAuthStore; hidden until status
     loads so the bar doesn't flicker on boot.

Type-check: bunx tsc -b --noEmit clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:52:36 +08:00
chenchenandClaude Opus 4.7 2db20738d7 fix(client): regen all PNG icons as RGBA so macOS Tauri bundler accepts them
Tauri's macOS bundler validates that every PNG referenced from
src-tauri/icons/ is RGBA. Our previous rebuild-icons.py wrote RGB PNGs
because the trim/pad pipeline lives in RGB to keep transparent edges
out of the glassy logo. macOS build failed with:

    error: proc macro panicked
       --> src/lib.rs:1073:16
    message: icon /.../desktop/src-tauri/icons/32x32.png is not RGBA

Fix: scripts/rebuild-icons.py now does `.convert("RGBA")` on every
PNG output before saving. Alpha is fully opaque (255) — the rounded
glass edges retain their look on any background since we paint on
solid white in load_trimmed_square. Same files keep working for
Windows .ico embedding (which accepts RGBA fine).

Regenerate all 17 PNG outputs (32/64/128/128@2x/icon.png + 9 Square*
+ StoreLogo + public/app-icon.png) and the derived .ico/.icns.

Mac side: pull this commit and re-run `bun run build:macos-arm64`.
Windows side: no change in behavior (Tauri's NSIS bundler doesn't
care about PNG color mode).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:25:26 +08:00
chenchenandClaude Opus 4.7 58f0a312ef fix(client): suppress run/stop button flicker during CLI restart transitions
ModelSelector and PermissionMode changes trigger a server-side CLI
restart (handler.ts:521 restartSessionWithRuntimeConfig). The server
emits status:'thinking' with verb 'Switching provider and model...' or
'Restarting session...' to keep the spinner spinning during the ~3s
restart window. ChatInput's isActive=`chatState !== 'idle' && hasMessages`
guard let the run button flash to a red 「stop」 affordance during that
window for any session with prior messages — confusing because hitting
stop has nothing to interrupt.

Add a statusVerb prefix check so isActive stays false during these
system transitions:

  isSystemRestartTransition =
    statusVerb.startsWith('Switching provider and model') ||
    statusVerb.startsWith('Restarting session')

  isActive = chatState !== 'idle' && hasMessages && !isSystemRestartTransition

Match by prefix so any future suffix (e.g. " (CLI starting...)") still
trips the guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:10:58 +08:00
chenchenandClaude Opus 4.7 30056136ea fix(client): 6 desktop bugs reported in real-world testing
#1 — Untitled session can't be deleted
   src/server/services/sessionService.ts:deleteSession is now idempotent.
   Previously placeholder sessions whose JSONL file hadn't been flushed
   yet would 404 the delete and stay stuck in the sidebar list. Now we
   succeed silently if the file's gone (and treat ENOENT during unlink
   the same way), matching how the frontend already optimistically
   removes the row.

#2 — Run button turns red 「stop」 on idle when switching model
   desktop/src/components/chat/ChatInput.tsx — gate isActive on
   hasMessages. ModelSelector / runtime config changes briefly flip
   chatState off-idle (CLI reconnect / startup). Without messages
   there's nothing to stop, so the button should stay disabled
   gradient, not turn into a red stop affordance.

#3 — Draft input bleeds across session switches
   desktop/src/components/chat/ChatInput.tsx — ChatInput is mounted
   once at app shell level; switching tabs doesn't re-mount it, so
   the local `input` useState carried over. Add a useEffect keyed on
   activeTabId that resets input + attachments + open menus + filter
   buffers. composerPrefill path (rewind) keeps owning its own reset
   via the existing prefill effect.

#4 — Close (×) minimized to tray instead of quitting
   desktop/src-tauri/src/lib.rs — drop the prevent_close + hide
   pathway on the main window's CloseRequested. Close now actually
   quits; users who want to keep the app running can minimize via
   the existing window controls. Tray icon stays available for
   re-open + explicit quit.

#5 — Tray menu hardcoded "Claude Code Haha"
   desktop/src-tauri/src/lib.rs — rename tray menu items, tray
   tooltip, and macOS app submenu to "Heicode" / "显示 Heicode" /
   "退出 Heicode" / "关于 Heicode".

#6 — Skills page silently empty when one source crashes
   src/server/api/skills.ts:listSkills uses Promise.allSettled so a
   single failed source (user / project / plugin) returns a partial
   list + structured errors[] instead of tanking the whole response.
   desktop/src/api/skills.ts + stores/skillStore.ts thread the
   errors through; SkillList only shows the hard-error wall when
   skills.length === 0.

Verification:
  bunx tsc -b --noEmit (desktop)        — clean
  bunx tsc --noEmit (cc-haha root)      — clean
  cargo check (src-tauri)               — clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:48:40 +08:00
chenchenandClaude Opus 4.7 b845a08840 feat(client): slice 15 — SSE subscription for approvals + task events
Wire mcp-server's §7.8.3 single SSE channel (GET /api/user/events/stream,
shipped 2026-05-08 in image heicode-7.8.3-sse-v2-20260508) into the client:

  cc-haha/src/server/api/heicode-tasks.ts
    + GET  /api/heicode-tasks/events/stream      (long-lived SSE proxy)
    + GET  /api/heicode-tasks/approvals          (startup pull)
    + POST /api/heicode-tasks/approvals/:id/decision

  cc-haha/desktop/src/lib/heicodeEventsClient.ts (new)
    EventSource wrapper with exponential backoff reconnect (1s..30s) and
    a 60s heartbeat watchdog that force-reconnects on stream silence.

  cc-haha/desktop/src/lib/heicodeEventsRouter.ts (new)
    Routes the 5 event names (approval.requested, approval.resolved,
    task.status_changed, task.execution_progress, heartbeat) into the
    matching stores. Ignores unknown events so future server-side adds
    don't crash the client.

  cc-haha/desktop/src/stores/heicodeTaskStore.ts
    + applyStatusChange(taskId, status, caption?)
    + applySubStepProgress(taskId, subStepId, status, caption?)

  cc-haha/desktop/src/main.tsx
    Subscribe to auth store → start/stop the stream as loggedIn flips.

Type-check: bunx tsc -b --noEmit clean.
Bun bundle: bun build src/server/api/heicode-tasks.ts → 86 modules, 0.55 MB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:19:47 +08:00
chenchenandClaude Opus 4.7 bb3c2c707b feat(client): one-click Manager OAuth login
Replace the email + password form on the desktop login card with a single
"一键登录 Heicode Manager" button that drives the existing OAuth bridge:

  desktop click
    → POST /api/heicode-auth/oauth/start (server stages state + builds
      authorize URL pointing at https://code.xinghanlab.com/heicode/oauth/authorize)
    → Tauri shell.open() the authorize URL in the system browser
    → user signs in via Manager (which now also routes /sign-in?redirect=...)
    → Manager 302s back to http://127.0.0.1:<port>/api/heicode-auth/oauth/callback?token=sk-…
    → callback handler activates the provider; status flips loggedIn=true
    → AppShell unmounts the login page

The OAuth start/callback endpoints already existed (handleOAuthStart /
handleOAuthCallback) so this is a UI-only swap; no auth-store changes.
loginWithCredentials remains exported in case we ever need a fallback,
but it's no longer wired into any UI surface.

i18n: tweak login.oauth.button to "一键登录 Heicode Manager", add
login.oauth.waiting for the polling state.

Aligns with upstream xiaohei/heicode commits 5bd8276 / e60e74b /
34a87a4 (Manager-as-only-identity) without breaking the slice 11-14
heicode-tasks proxy that still depends on the provider abstraction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:34:05 +08:00
xiaoheiandchenchen 87b30b8fa1 fix: 修复登录时 /v1/models 空列表 panic 导致 500
- heicode/controller/model.go: Anthropic ListModels case 对空模型列表
  做边界保护,避免 index out of range panic -> HTTP 500
- cc-haha/src/server/services/providerService.ts: 模型探活不再发送
  anthropic-version header,统一走 OpenAI 兼容路径返回 {data:[]}
- heicode/controller/heicode_oauth.go: OAuth 未登录重定向指向 /sign-in
- heicode/deploy/nginx/heicode-gateway.conf: 3000 端口 server block
  补充 /models -> /v1/models 兼容路由
2026-05-08 20:19:21 +08:00
chenchenandClaude Opus 4.7 578a68f006 fix(server): stop overwriting users.group with Agnet channelId on every login
NewAPI's `users.group` is the local model-access bucket — it must match a
row in the `abilities` / `channels` group column to expose any models. The
Agnet channelId returned by mcp-server's /api/auth/me is a cross-platform
identity that almost never matches a NewAPI-side group, so blindly assigning
it on every login left users with `data: []` from /v1/models and the
desktop client silently fell back to the static 3-Claude default list.

Symptoms fixed: 4 users (xiaohei, 55@55.com, uwktn, test1) had UUID groups
with zero abilities, so /v1/models returned empty for them. cc-haha desktop
falls back to preset.defaultModels, hiding the 28 real models the channels
expose under group=default.

Change: drop the unconditional overwrite branch. The JIT-create path above
still seeds group from channelId on first login (kept for backward
compat), but admin-set group on existing users is preserved. mcp-server
already tracks Agnet channelId separately (see markBillingProviderNewapi),
so we don't need to mirror it into NewAPI's users.group anymore.

DB hot-fix already applied: 4 affected users moved to group=default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 19:05:33 +08:00
chenchenandClaude Opus 4.7 fc2c811e93 feat(server): notify mcp-server billing_provider=newapi after Agnet user sync
Implements Heicode's choice (b) for §7.7.1 ②: in syncLocalUserFromAgnet,
fire-and-forget PUT mcp-server's internal /api/auth/internal/billing-provider
{email, billing_provider:"newapi"} so mcp-server's User table billing_provider
column lands as 'newapi' for users that came in via Heicode Manager (vs the
default 'litellm' for native taijiagent users).

- Goroutine: never blocks login on this side-effect; mcp-server endpoint is
  idempotent so retries from repeat logins are harmless.
- Token via env MCP_SERVER_INTERNAL_TOKEN (K8s/compose secret); empty env
  silently skips (dev-friendly).
- Reuses agnetHTTPClient + common.Marshal + common.GetUUID per repo
  conventions.

Spec: docs/Heicode-对接进度与待办.md §7.8.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:36:24 +08:00
chenchenandClaude Opus 4.7 2f75588ed1 feat(server): expose desktop-downloads file route to TokenOrUserAuth
Add userRoute.GET /api/user/desktop-downloads/file/:platform with
TokenOrUserAuth middleware. A plain browser <a href> can hit this with
just the session cookie; New-Api-User header isn't required (the SPA's
axios layer still injects it for the metadata endpoint on selfRoute).

Closes the 401 "无权进行此操作,未提供 New-Api-User" case from the
Heicode Manager desktop installer download path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:40:40 +08:00
chenchenandClaude Opus 4.7 15ac00130a feat(client): slices 8-14 — exec/delivery/audit panels + §6 task orchestration wiring
Wireframe coverage (product-package 11-product-prototype-wireframes.md):
  §8  ExecutionFeedbackPanel — sub-steps + sk_tool_calls + events + artifacts (Slice 8)
  §10 DeliveryResultPanel — deliverables + quality + next-actions (Slice 9)
  §audit TaskDetailDrawer — usage / resources / approvals / security tabs (Slice 10)

§6 task orchestration wired (mcp-server contract v2.0):
  Slice 11 — heicode-tasks proxy (5 routes) + typed client + store mode
             (mock | live | loading | error) + ModePill + optimistic updates
  Slices 12/13/14 — lazy-load /execution, /delivery, /audit?tab=... on panel
             mount with shouldFetchPanel gate (skips for seed mock ids)

§7.8.5 deeplink: lib/managerLink.ts centralizes path→URL resolution so the
Manager domain (currently code.xinghanlab.com) can be flipped in one line.
Wires onClick into manager_actions / openManager / deliverable buttons.

Type-check: bunx tsc -b --noEmit clean.
Bun bundle: bun build src/server/api/heicode-tasks.ts → 86 modules, 0.55 MB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:39:47 +08:00
chenchenandClaude Opus 4.7 9ffe261374 feat(client): refresh brand to Heicode H logo + align macOS build script
- Replace logo source with new glassy gradient H (heicode-logo.jpg)
- Regen all icon sizes via scripts/rebuild-icons.py (PIL + ICO + ICNS):
  32/64/128/128@2x/icon.png + Square{30,44,71,89,107,142,150,284,310}+StoreLogo
  + multi-res icon.ico + icon.icns
- Update public/app-icon.png to 1024×1024 derived
- Align cc-haha/desktop/scripts/build-macos-arm64.sh to Heicode branding:
  APP_BUNDLE_NAME=HeiCode.app, APP_BUNDLE_ID=com.heicode.desktop,
  DMG volume=Heicode, default DMG=Heicode_0.1.0_aarch64.dmg,
  cleanup paths use heicode-desktop crate name (mirrors Cargo.toml rename)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:38:36 +08:00
chenchenandClaude Opus 4.7 a268079751 feat(client): slice 7 — task driving cabin (intent → followups → task card)
Per docs/product-package/10-frontend-detail-spec.md and the wireframes
in 11-product-prototype-wireframes.md §2-§3, the client's first-class
surface is no longer "code-companion empty state" but a Heicode task
driving cabin: input an idea, answer Heicode's follow-ups, see the
auto-generated task card, hand off to Manager for resource binding /
deployment.

This slice ships the surface as a mock skeleton — the data layer
(useHeicodeTaskStore) is seeded with two demo tasks so the wireframe
can be reviewed end-to-end before backend wiring lands.

New files:
  - stores/heicodeTaskStore.ts
      HeicodeTask shape (id / name / status / status_caption / thread /
      card), HeicodeTaskStatus enum, ChatTurn (user | heicode), and
      FollowupQuestion (with optional 'high-risk' option flag).
      Actions: submitIntent, openTask, closeTask, answerFollowup,
      appendMessage. Two seeded tasks ("小团队任务管理 SaaS" running
      with full task card; "企业微信通知集成" awaiting approval).

  - pages/HeicodeTasksHome.tsx
      Two layouts behind a single route. When currentTaskId is null
      we render the Home (wireframe §2):
        - Header line "当前任务:未选择"
        - Big intent prompt + textarea + Send (⌘/Ctrl+Enter shortcut)
        - Recent tasks grid (status pill + caption + relative time)
        - Manager auxiliary footer hint
      When a task is open we render the Workspace (wireframe §3):
        - Header with back button + task name + status pill
        - Conversation thread (user bubble right-aligned, Heicode
          left-aligned with "H" avatar; follow-up questions render
          as chip groups with high-risk dot indicators)
        - Reply textarea at bottom
        - Right-side TaskCardPanel (lg breakpoint+) with goal /
          scope / generated-artifacts / Manager actions / footer
          buttons (修改目标 / 去 Manager 准备)

Plumbing:
  - tabStore.ts: HEICODE_TASKS_TAB_ID + 'heicode_tasks' TabType,
    treated like settings/scheduled in dedupe rules
  - Sidebar.tsx: new "我的任务" entry between "新建会话" and
    "定时任务" with a target icon
  - ContentRouter.tsx: route 'heicode_tasks' → HeicodeTasksHome
  - i18n: tasks.* (~24 keys per locale) + sidebar.heicodeTasks

Backend wiring TODO: when mcp-server publishes the task-orchestration
contract, swap submitIntent / answerFollowup / appendMessage for real
calls and keep the same shapes. Status updates can come in via SSE
or polling and be merged onto useHeicodeTaskStore.

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