Business calls (heartbeat, account sync, lease/pull/resume tasks,
preempt monitoring lease) used to short-circuit any 401 into
handleAuthExpired, which tore down the runtime and switched the
window to login. That stole the screen on transient or per-request
auth glitches and conflicted with the renderer-side proactive token
renewal.
Now business 401s fall through to the existing warn/danger activity
log paths and runtime keeps running. The login redirect is owned
solely by the renderer's renewAuthenticatedSession → on refresh /
rotate failure → forceLogoutAfterRenewFailure path. Server-side
revocation via error code 40991 is unaffected.
Removed the now-orphan plumbing: handleAuthExpired,
isApiClientError helper, emitRuntimeAuthExpired /
onRuntimeAuthExpired events, and forceLoginWindowForAuthExpired
listener registration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expose tenant-authenticated publish task list and retry endpoints so the
admin web can show desktop publish state without an Electron bridge, and
register a `shengxintui://` deep-link so the page can hand off to the
desktop client workbench.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Increase default main window size from 1320x860 to 1420x960
- Use transparent titlebar overlay for all window modes
- Remove hardcoded CONFIG_PATH from dev-ops-api make target
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactors openAtLogin and keepRunningInBackground to make the macOS/Windows
divergence explicit instead of leaning on Electron's cross-platform
abstraction:
- openAtLogin on Windows now passes path: process.execPath and args: []
so the registry HKCU Run key always points at the right binary.
- keepRunningInBackground on Windows shows a one-shot tray balloon ("省心
推已最小化到托盘…") so first-time users notice the app is still alive;
persisted via a private hasShownBackgroundBalloon flag so it never fires
again. macOS preserves the dock icon and stays silent.
- Sets AppUserModelID early on Windows so tray.displayBalloon's Win10+
Toast routing actually fires.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sets the settings window parent on creation and re-applies it on reveal
so it stays modal to the main window on win32.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add creation promise guards (mainWindowCreatePromise etc.) so concurrent
ensureXxxWindow() calls share a single in-flight BrowserWindow, avoiding
duplicate windows on rapid IPC
- Add windowModeSwitchQueue to serialize desktop:set-window-mode calls and
the initial startup switch, preventing races when login/logout fire quickly
- Fix retireInactiveAppWindows: also retire existing login windows when
switching to login mode, so stale login windows don't accumulate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add appTitleBarStyle() and appTitleBarOverlay() helpers for cross-platform title bar
- Use titleBarOverlay on Windows/Linux so system caption buttons render correctly
- Disable minimize button on login window (non-resizable, no need to minimize)
- Detect platform at renderer startup and set data-platform on <html> for CSS targeting
- DesktopShell: flip drag bar layout — full-width minus caption area on Win/Linux, traffic-light gap on macOS
- LoginView: move top-bar to Win/Linux caption area (top: 34px), mirror to left on macOS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add suppressNativeWindowMenu() to hide native menu bar on all windows (Win/Linux only)
- Set autoHideMenuBar: true on BrowserWindow creation as belt-and-suspenders
- Import Menu from electron/main to support menu suppression
- Login form: rename field from email to identifier, support phone number input
- Migrate saved credential key from geo_rankly_saved_email to geo_rankly_saved_identifier with fallback
- Update login error message: "邮箱或密码错误" → "账号或密码错误"
- Remove unset ELECTRON_RUN_AS_NODE from dev script (not needed on Windows)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Track each window's renderer mode in a WeakMap (and fall back to
parsing ?window= from the URL) so retireInactiveAppWindows can
destroy stale login/main/settings windows when we switch modes,
not just the previous one of the canonical pair.
- Replace closeWindowAfterIpcReturn with retireWindowAfterIpcReturn
(hides immediately, then destroys instead of close()) so the IPC
reply still reaches the caller before teardown.
- Add waitForWindowReady() with a 1.2s ceiling and call it from
revealWindow so we don't show a half-loaded shell while
switchWindowMode is running. Pass the source window from the IPC
event into switchWindowMode so the closing target is the actual
caller, not a guess based on mode.
- Silence console.* and skip the observed-fetch install in packaged
runtime, and only auto-openDevTools in dev.
- Eagerly import LoginView in App.vue instead of defineAsyncComponent
to remove the boot flash on the login window.
- package.json: prefix package:* with `pnpm run build` and add
package:win and package:linux for parity.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update server titles, web/desktop window/tray/document titles, login
copy, and electron build metadata to use the Chinese product name 省心推.
Drop the runtime locale switcher and en-US import — the app now ships
zh-CN only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Open a dedicated settings BrowserWindow with general / login / diagnostics / about tabs
- Persist openAtLogin and keepRunningInBackground via desktop-settings.json; hide main window instead of quitting when background mode is on
- Default API base URL now points to api.shengxintui.com; LoginView and SettingsView share a single constant
- Restore wangyihao session cookies on probe and silent refresh, and accept the legacy http://mp.163.com console origin
- Drop the standalone diagnostics view in favor of the settings tab
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spawn a separate window per mode keyed by a ?window= query param so each surface keeps its size, devtools, and renderer state independently. Logout now persists the cleared session and switches windows before awaiting the runtime release, avoiding a stale main-window flash on the way out.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why: avoid showing accounts as not-live during stale-window probes; keep publish gating consistent with runtime view.
- collapse FIRST_PROBE window so initial probe runs immediately
- skip due-probe selection when a probe is already queued/in-flight
- mirror auth-state→health projection on both client report and tenant ingest paths
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace serial probe-on-lock with a bounded probe queue (concurrency 3) that
adds a "queued" probe state, manual cooldown, and retry backoff. After each
probe the runtime debounces a batched POST to the new
/desktop/accounts/health-reports endpoint so the server learns about live,
expired, and risk transitions without waiting for a re-bind. Adds an
account-scoped IPC (probeRuntimeAccount + runtimeAccountSnapshot) so the
renderer can refresh a single row instead of replaying the full snapshot,
and exposes the resulting "重新校验" action in AccountsView/AiPlatformsView.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generate the desktop client_id deterministically from OS machine identifiers
(IOPlatformUUID / MachineGuid / /etc/machine-id) scoped per
tenant+workspace+user, with a persisted UUID fallback when the OS lookup
fails. The renderer now requests the id over a new IPC bridge instead of
keeping a localStorage UUID, so reinstalls and storage clears no longer
fork into duplicate clients. Server enforces the id as required and
rejects empty/malformed UUIDs. Also harden bootstrap reveal flow and
single-instance exit so a second launch focuses the existing window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Periodically polls runtime snapshot for accounts in expired/revoked/challenge_required states and reflects the count via tray badge, macOS dock badge, and Windows overlay icon so users see authentication problems without opening the app.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the 1.8s polling interval with a 650ms tick plus a debounced
navigation-driven detect, listen for page-title-updated to catch SPA
transitions, and flush the session asynchronously. The bind handler
now optimistically seeds the runtime account list via
noteRuntimeAccountBound and fires the full server refresh in the
background, so the UI reflects the new binding without waiting on a
round-trip.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires up Baijiahao (百家号) and Jianshu (简书) as first-class desktop
publish targets, with risk-control prompts surfaced in the runtime
controller and a normalized error message in publish records. Adds
external-link buttons in the publish management table, an asset
format conversion endpoint for cover image compatibility, and
reorders publish-status display priority so failures take precedence
over partial successes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the nested WebContentsView and host the renderer on the main
BrowserWindow's own webContents. Removes the manual bounds sync plumbing
and the extra webContents lifecycle that was only ever a child of the
main window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- implement kimi/yuanbao monitor adapters with dedicated citation panel detection
- detect Kimi session via kimi-auth cookie during account binding
- harden hidden Playwright and bound windows with skipTaskbar/focusable/hiddenInMissionControl to stop stealing focus
- tag hidden bootstrap windows with a unique token so CDP retention resolves the correct page
- enable yuanbao/kimi/wenxin platforms in dev-seed and default monitoring quota
- update kimi loginUrl to www.kimi.com
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace SSE /desktop/events with priority AMQP dispatch for monitoring
runs, and add phase1/phase2 infrastructure so desktop workers can lease,
resume, report, skip, and cancel monitoring tasks over the existing
dispatch WebSocket.
- Add monitoring collect outbox worker and phase2 desktop task fields
- Add /desktop/monitoring/tasks/{lease,resume,result,skip,cancel} routes
- Introduce execution-devtools and network-observer on desktop runtime
- Refactor runtime-controller, LoginView, and doubao adapter for the new flow
- Add channelName column to tracking views
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move monitor-task scheduling authority onto the client with a durable
file-backed queue that survives restart, drops stale cross-day tasks,
enforces per-platform serialism, and adapts global concurrency from
Electron process metrics. Publish tasks keep their existing FIFO.
Add a hidden Playwright CDP manager that attaches to Electron Chromium
on account session partitions, lets adapters opt into `executionMode:
"playwright"`, and leaves the existing hidden WebContentsView path in
place for current adapters.
Introduce an account-health subsystem with silent probes, projected
health/auth states, and IPC invalidation events so the renderer can
show accurate auth/probe status and verification timestamps.
Server-side, derive and forward title/business_date/scheduler_group_key/
question_text metadata on desktop task events so the local scheduler
can defer same-question fan-out before leasing.
Renderer was registering clients with navigator.platform ("MacIntel") and
a hardcoded "renderer-preview" cpu_arch. Source device info from the main
process (os.hostname + process.platform/arch) via a new device-info IPC,
and let heartbeats always send the authoritative runtime values so stale
registrations self-heal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace single-shot acquireHotView with retainHotView/releaseHotView
plus a minute-level reaper that closes views idle beyond 5 minutes and
never evicts a view while it still has live retainers. Wrap adapter
calls in try/finally so a failing publish/query still releases the
view. Sample process metrics every minute and expose hot-view policy
and process snapshot via the runtime snapshot for diagnostics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plan 0 (workspaces): add workspaces + workspace_memberships schema, extend
JWT/Actor/claims with primary_workspace_id, seed default workspace per tenant,
thread workspace_id through tenant monitoring quota.
Plan A (desktop skeleton): new Electron app (apps/desktop-client) with main/
preload/renderer, shared Vue component package (packages/ui-shared), and server
surface — desktop client registration + token rotation + heartbeat, SSE task
event stream, desktop accounts/tasks/content handlers, publish job endpoint,
and supporting repositories, services, sqlc queries, and migrations.
Hard cutover per plan: remove browser-extension monitoring callback endpoints,
stub legacy media API in admin-web, and delete monitoring_callback_handler.go.