Refactor readDeepseekAccountIdentity so the JSON payload parser is a pure
ts function (extractDeepseekIdentityFromPayload). The webContents IIFE now
just polls for userToken and returns the raw API JSON; payload picking lives
in ts and is unit-tested.
Tests lock the contract for both login types we observed in the wild:
- WeChat-bound account: id_profile.name = '阿白', picture present.
- Phone-only login: id_profile = null, mobile_number = '177******08',
email = '' (empty string, not null) — the case that originally regressed.
Plus regressions for the priority order (profile.name > mobile_number > email),
the all-empty fallback, and non-record payloads.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Poll the knowledge item list every 5s while any item is still pending
or processing so the UI reflects ingestion progress without manual
refresh, and exclude items that have no active chunks from group
counters so groups with no usable content do not appear populated.
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>
Scope desktop media accounts by the authenticated desktop client_id so
the same user logging in from Mac and Windows no longer sees a shared
account list. The list, identity lookup, upsert, patch, tombstone and
async health sink paths now all require matching client_id, and the
active uniqueness index moves from (workspace, platform, uid) to
(workspace, client, platform, uid).
Also strengthen the desktop client_id seed: when the OS machine id is
unavailable, fall back to a hashed fingerprint of stable hardware MAC
addresses before the random installation id, so the same hardware
keeps the same client across reinstalls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- admin-web vite dev server now proxies websockets on /api so live
features (e.g. SSE/WS) work in development.
- Tenant desktop publish compliance recheck now logs warnings when the
query, scan or row iteration fails so the silent 50118 errors can be
diagnosed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restrict account tracking, probing, and health reporting to accounts owned
by the current client (client_id match). Extend identity-match verification
to bilibili, juejin, and smzdm so session validity is confirmed locally
rather than deferred to remote health state. Server-side account view now
uses stored client_id as authoritative owner instead of presence data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- shared/cache: add Options/L1/async/metrics/prefix decorators, multi-key ops, Redis pool tuning, and JSON readthrough metrics
- worker-generate: claim tasks via DB lease + heartbeat, requeue stale queued tasks, expire dead leases with refund/cache invalidation
- tenant: version article cache keys so worker recovery invalidations propagate cleanly
- shared/config: expand Redis (pool/timeouts/TLS) and Generation (lease/recovery) configs with defaults
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add POST /api/tenant/knowledge/items/retry/:id to re-queue failed parse
tasks, plus a 20-rune cap on text item names with matching frontend
validation. Wires the retry button into the knowledge table with a
colored status tag for clearer state feedback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "platforms" stat tile was a constant (5) that never reflected what the
tenant actually had set up. Drop SupportedPlatformCount from the workspace
overview API/domain/shared-types and have admin-web compute a bound media
account count from the desktop accounts list, filtered through a new
isMediaPublishAccount helper that excludes deleted rows and AI-only
platforms (yuanbao/kimi/wenxin/deepseek/doubao/qwen).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace single Load() with a watching Store that re-reads config files
(and config.local.yaml) and fans out a ReloadEvent with a per-field diff
so consumers can decide whether the change is hot-applicable or requires
a process restart.
- Wrap llm, retrieval, vector store, and object storage clients in
Reloadable* shells so the bootstrap can swap their underlying impls when
config changes without re-instantiating handlers.
- Make jwt.Manager and ops TokenIssuer mutable under a lock so secrets and
TTLs can be rotated live; thread default plan code through a setter on
the ops AdminUserService.
- Wire ConfigStore through bootstrap and every cmd/main.go, scheduler /
worker / tenant-api / ops-api start the watcher; services and handlers
take a config.Provider so they always read current values for things
like generation.stream_enabled, scheduler dispatch, retrieval, etc.
- Switch shared/config decoding off viper to a Kratos-derived runtime
package so env placeholders (\${VAR:default}) resolve consistently and
the same source machinery powers both the loader and the watcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the domain+title fuzzy alias scoring with a canonical
candidate-key index built from published-link aliases — only exact
matches now resolve to a SaaS source, eliminating false positives
across articles sharing a host (e.g. m.163.com vs www.163.com).
Surface the summary scoping that already existed on loadCitationRanking
/ loadCitedArticles by accepting brand_id, keyword_id, ai_platform_id
and business_date on CitationSummary, plumbing them through the handler
and admin tracking view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the freeform `platforms` field on articles/tasks (parsed out of
wizard_state/input_params JSON) with `auto_publish_platforms`, resolved
by joining schedule publish accounts to platform_accounts. The migration
strips the legacy keys from existing rows so the new field is the single
source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build https://www.163.com/dy/article/<docId>.html when wangyihao tasks finish, and normalize publish-record responses + monitoring alias inputs to surface the same public URL when the doc id is known.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track an authRevision per local record so a fresh bind cancels in-flight
probes, sync server-confirmed health back into the desktop cache via
reconcileTrackedAccountRemoteState, include verified_at in the upsert
signature, and drop runtime health reports older than the stored
verified_at on the server side.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Persist host/registrable_domain/site_key on monitoring article URL
aliases, populate them on desktop-publish completion, and use them in
the citation pipeline to count how often SaaS-published content
appears as a citation source. Expose a /citation-summary endpoint with
a 7/30-day window switch and surface the new source-share metrics in
the tracking dashboard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the legacy target_platform string on schedule tasks with a
workspace-aware auto-publish payload (auto_publish + publish_account_ids
JSONB + cover_asset_url + cover_image_asset_id) and migrate the schema,
sqlc queries, generated models, domain struct, ScheduleTaskService DTOs,
and dispatch worker to round-trip the new fields. PromptRuleGeneration
gains a WithPublishJobService hook so executeGeneration can enqueue an
auto-publish job once the article is ready, and worker-generate wires
the publish-job service in. On the admin-web side, extract
PublishArticleModal's account-card builders into a shared
publish-account-cards module, rebuild GenerateTaskDrawer's schedule
mode around account selection plus a CoverPickerModal, and surface the
new "auto publish" column on ScheduleTaskTab. Shared types and i18n
strings cover the new fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tighten the analyze prompts (runtime + recommendation platform) so the
model returns 8-10 question-style search queries (哪家好/有哪些/怎么选/
价格/排名/避坑 …) instead of generic tag words, and lift the keyword cap
in normalizeAnalyzeResult from 5 to 10 to keep them. Add tests covering
the new ten-keyword window and the question-like guidance in the seeded
analyze prompt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
懂车帝 has the same cover-mandatory rule as 百家号; treat them the same
when deciding whether a publish batch must carry a cover.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logging the full candidate and selected snippet bodies per resolve was
inflating log volume without adding signal we use.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire LoginGuard into tenant-api and ops-api login flows: acquire a
permit before checking credentials, record a failure on every invalid
attempt to drive lockouts, and clear counters on success. Tenant-api
now also forwards the request IP into the service so per-IP and
IP+identifier limits actually fire under real traffic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Approving / manually binding / revoking KOL subscriptions belonged to the
operations console, not the tenant-admin role on tenant-api. Add a fresh
KolSubscriptionService + repository + handlers under the ops module with
its own list/manual-bind/approve/revoke routes, wire a Redis-backed cache
into ops-api so admin actions can invalidate tenant prompt caches, and
delete the tenant-side admin service/handler now that ops-web owns the UI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add /kol/manage/packages/:id/self-subscription POST/DELETE on the tenant
side so a KOL can grant themselves access to their own published package
without going through the marketplace approval flow. Reject self-subscribe
through the marketplace with a clear error, expose owned_by_current_tenant
on package responses, and surface a self_subscription block on KolPackage
Response. The admin-web KOL workspace gets 订阅自己 / 取消订阅 entries with
matching messaging in the marketplace detail view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a.publish_status only stored a coarse summary so the article list/detail
mis-reported as 失败 when one of several accounts failed. Compute the
effective status via a LATERAL join over latest publish_records per
platform_account_id and bucket into success / publishing / failed /
partial_success. Wire the same expression into list filtering and detail
queries, and let normalizePublishStatus / batch-status helpers retain
partial_success end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the per-row tenant+global LATERAL joins with a single CTE that
materializes filtered citations and resolves domain mappings once per
distinct host/domain/site_key triple. Reduces redundant scans of
site_domain_mappings on large run batches.
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>
Phone becomes the required, unique login identifier; email and name turn
optional. Login now accepts either via a single identifier field, refresh
re-checks tenant membership, and the dev seed provides phone numbers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Server callback now rejects succeeded submissions without a non-empty answer,
or whose answer contains [citation:N] markers without any sources. Desktop
adapters mirror this client-side: kimi returns unknown when sources arrive
without a final answer, and yuanbao returns unknown when the answer has
unresolved citation markers. Yuanbao also unwraps redirect URLs, harvests
citations from more DOM attributes and document URL fields, drops links
pointing back at yuanbao itself, and dedupes overlapping streaming
fragments. Runtime controller forwards only an explicit "succeeded" status
as success; anything else becomes failed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an ai_point_usage_logs ledger and a reserve/refund/complete pipeline
that charges AI points for article selection optimize, template analyze
/title/outline, and KOL prompt generate/optimize. Pending reservations
are reconciled when the kol-assist worker and template-assist tasks
finish or fail, so points refund automatically on errors. Workspace now
exposes the AI quota status and a paginated usage ledger.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces ai_points_monthly and ai_point_base_chars on the membership
plan policy, exposes them through TenantPlanAccess and MembershipInfo,
and seeds Pro with a 1500-point monthly allowance across the local,
server, and deploy configs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Daily monitoring tasks were pinned to a single primary client. If that
client went offline the task wedged even when another desktop client of
the same workspace was online and bound to the same account. Drop the
primary-client constraint on the materialized collect rows, and instead
pick a target per-platform from live account/client presence in Redis,
falling back to the DB client_id when the desktop client is still flagged
online. Desktop task lease for kind=monitor now matches by account
ownership in addition to target_client_id, so any client owning the
account can drain the queue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a POST /desktop/accounts/health-reports endpoint that buffers reports in
Redis, dedupes via signature, and publishes change events to a new
desktop.account.health RabbitMQ queue. A sink worker drains the queue, and
the account service overlays runtime health onto database health when serving
desktop accounts. Publish job creation now consults runtime health so a stale
DB row no longer blocks publishing once the desktop client reports it healthy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Now that desktop clients can be re-registered with a new id under the
same user, listing/searching publish tasks by client.id missed the user's
older history and the retry path forbade jobs created from a previous
client. Switch the list/count queries to join desktop_publish_jobs and
filter by created_by_user_id, and enforce the same ownership when
retrying via desktop task. Qualify column names with the t. alias so the
join doesn't break the existing ORDER BY clauses.
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>
- Walk knowledge group children recursively so selecting a parent group
pulls snippets from its descendants too.
- Filter knowledge items to status='completed' across the prompt and
active-snippet queries to avoid leaking in-progress documents.
- Dedupe vector candidates by point id and normalized text, then pick
rerank+vector results with a per-item cap so prompts stay diverse.
- Tag each rendered snippet with a [Kn] reference id for prompt citing.
- Build the rule knowledge query from task params (title/keywords/etc.)
before falling back to rule prompt content so retrieval matches the
user's actual ask.
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>
Trigger an AI optimize panel from a text selection in the KOL prompt
editor, accept a user instruction, and stream a preview before replacing
the selected fragment. Extend the shared editor AI assist panel with
boundary-aware placement so it can be hosted inside scoped surfaces, and
update the backend optimize prompt to honor the user instruction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce 仿写创作: new list and create views, backend imitation service
and prompt templates, worker task routing for imitation jobs, and one-click
rewrite triggers from question citation sources. Surface source article
URL/title on article list/detail, and restrict failed articles to delete-only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull Kimi's "搜索网页" panel results into the regular citation source
flow, drop the dedicated-citation-panel branch on the server side, and
unwrap Kimi redirect URLs so citation source items resolve to the real
target host.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Skip article alias resolution and content-citation aggregates when the
platform is qwen, and hide the content-citation badge/meta in the
tracking detail view, since qwen's source URLs are not reliable enough
to map to brand articles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a MonitoringDailyTaskWorker that materializes and dispatches
daily collect tasks from active subscription/desktop-client state,
with per-run atomic metrics and a Prometheus collector. Dashboard
and question-detail APIs now surface a platform_authorization_status
so the UI can distinguish no-desktop-client, no-authorized-platforms,
and authorized states; quota/access-state lookups resolve by request
workspace instead of tenant.
Hardens heartbeat: a desktop_client_primary_leases table gives
per-(tenant, workspace) sticky primary selection, read-mostly lease
resolution avoids a per-heartbeat transaction, and unchanged platform
access reports skip snapshot upserts outside a 10-minute refresh
window. Adds heartbeat primary/snapshot metrics exposed via
token-protected tenant-api /api/internal/metrics endpoints plus
Prometheus. Monitoring quota seed is removed from dev-seed since
daily plans now derive from desktop bindings, and the projection
uses the canonical six-platform catalog so platforms with zero
samples still render.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- desktop: new DeepSeek page adapter and monitor registry
- desktop: extract generic AI auth helpers into shared module and
skip binding when challenge signals are present
- admin-web: render DeepSeek HTML answers with inline citation anchors
and surface reference-number badges on source cards
- server: fall back to inline_links/source_panel_links and read
text/siteName fields when extracting DeepSeek citations
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- drop brand_time_buckets and sparkline/coverage UI; dashboard now returns a single day
- compute mention rate from total actual samples instead of averaging per-platform rates
- canonicalize ai_platform_id across services and aggregate platform-overview snapshots
- strip mojibake and non-answer noise from callback payloads and question detail answers
- keep Kimi citations panel-only; still store raw search_results for audit
- render linked [n] inline citations for qwen/yuanbao answers with scroll-to-source behavior
- restrict立即采集 to today's business date with a clear i18n hint
- refresh content-citations into an ant-design table with truncation and hover styling
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- reset stale in_progress tasks owned by a client on startup, offline, and lease expiry
- monitor tasks re-queue for another pick; publish tasks move to unknown for manual reconcile
- send startup=true flag on first heartbeat so the server can trigger recovery
- finalize the linked desktop task when a phase2 monitor callback arrives via desktop_tasks path
- short-circuit the desktop monitor attempt write after the callback already finalized it
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>