feat: v0.2.0 — TS 重构 + P0/P1 全面升级 - #1
Merged
Merged
Conversation
- drop assets/ from the files whitelist (v0.1.0 shipped ~766KB of images) - point README images at GitHub raw URLs so the npm page still renders - add scripts/check-pack.mjs guard + prepublishOnly hook: fail publish when the tarball contains assets/ or any image file
- package.json: zod@4 runtime dep, @deepseek-ai/* rc.6 type devDeps, cordis 4.0.1, build/typecheck/test scripts, exports.types - esbuild build pipeline + ModuleLoader wrapper + node --test runner - src/shared: wire contract (extended, v0.1.0 shapes preserved), UTC day keys, four disjoint buckets, hit-rate, formatting (zh 亿/万, en K/M/B) - tests: 19 cases locking aggregation/formatting semantics
Host (src/host):
- projection.ts: per-session persisted projection reducer — four disjoint
buckets, last-marker seed boundary (fork dedup preserved), dual-source
model/provider attribution, per-step provisional→authoritative replacement
(retry dedup fixes v0.1.0 double counting), compaction attribution,
llm/retry counting, UTC day keys, same-reference contract
- projection-unit.ts: registered unit (zod schema, stateVersion 1)
- aggregate.ts: cross-session merge → Overview (recent-30d window, session
ranking, coverage, providers)
- scan.ts: v0.1.0 full-rescan fallback through the SAME reducer with
seedLength-derived boundary + coverage counters (no silent catch{continue})
- index.ts: fail-soft mode detection (projection → scan → none), SWR cache,
keep-warm, loopback RPC, warmup
Client (src/client, TSX components, single files < 300 lines):
- i18n zh/en via DSH locale service with machine-readable error codes
- KPI 4 cards incl. cache hit rate (P0-②), coverage card (P0-③), top
sessions (P0-④), providers card (P1-⑦), export menu JSON/CSV with
injection guard + BOM (P1-⑧), localStorage SWR cache + ErrorBoundary +
freshness five states (P1-⑨), UTC heatmap/bars/donut
- ModuleLoader wrapper + esbuild pipeline; output contract identical to
v0.1.0 (verified by simulated loader: {apply, inject: [slots,connection,locale]})
Tests: 42 (usage/format/projection/aggregate/export)
… update - docs/P2-decisions.md: all four P2 items no-go with prerequisites and recommended versions (v0.3.0 candidates) - .github/workflows: ci.yml (typecheck/build/test/check-pack) + publish.yml (OIDC Trusted Publishing, provenance, tag↔version guard, pack gate; no NODE_AUTH_TOKEN — OIDC only) - README.md / README.zh-CN.md: new features (hit rate, coverage, sessions, providers, export), UTC timezone declaration, TS implementation table - AGENTS.md: real commands, shared-reducer data path, v0.2.0 pitfalls (§6.4) - DECISIONS.md: D1-D12 decision log with acceptance evidence - host: local cordis interval augmentation (no runtime devDep import) - lib/: rebuilt artifacts
Field issue: the page showed raw keys (nav.label, kpi.total ...) because the
dictionaries were registered under 'zh-CN'/'en-US' while the DSH locale
runtime ships LOCALE_IDS = ['zh','en'] — translate() then failed loud and
returned the key itself, which bypassed the old truthy-key fallback.
- register(NS, { zh: zhCN, en: enUS }) — mirrors the framework's own form
- treat translate() returning the key itself as a miss, then fall back to
the local dictionaries; interpolation stays our single source
- i18n.locale is now a getter (field snapshot stayed stale after switches)
- ride locale.subscribe(update) (locale switches + late dict registrations
both bump revision); ctx.on('locale/change') kept as backup wiring
- tests/locales.test.ts: fake runtime reproducing real semantics — 7 cases
locking zh/en resolution, switch propagation and no-raw-key guarantee
zh 'providers.title' was half-English ('Provider 用量'). The DSH GUI's own
models page translates Provider as '提供方' (dsh-client-ui-settings-models),
but that term was flagged as awkward; use '服务商用量' instead.
- modelRows now carries each row's four disjoint buckets; the merged 'other' row sums the remaining models' buckets (tests/model-rows.test.ts, 4 cases) - ModelDonut list gains a header row (model/tokens/share/hit rate) and a per-model hit-rate column + tooltip line, same formula as the KPI card - settings-nav entry icon is shell-hardcoded (navIcon: only models / agent-presets / plugins have custom icons, everything else falls back to the gear — identical in rc.6 and upstream master), so a bar-chart icon is added to the page header instead (the plugin-side-controllable spot) - zh/en dicts: donut.model/donut.tokens/donut.hitRate
… summary The panel ran in scan fallback (no usagePanel rows in the projection cache); these logs pinpoint which projection service the host cannot see.
…agent) Field evidence: of the 32 sessions with usage in the user's corpus, 29 are subagent sessions (delegationDepth >= 1) and only 3 are main sessions — which explains why the sidebar (which does not surface subagent sessions) shows far fewer sessions than the KPI card. - CoverageStats: usageSessionsMain / usageSessionsSubagent (sessions with counted usage, split by header.delegationDepth; each subagent session counts as one, tokens stay merged into the shared buckets) - SessionSummary.depth: the ranking card tags each row 主会话/子代理 - OVERVIEW_VERSION 3 (invalidates older cached payloads) - coverage card line: 有用量会话:主 X · 子代理 Y - tests: depth plumbing through mergeSessionValue/finalizeOverview
…e wipe
- Remove the standalone 统计覆盖度 card (CoverageCard.tsx, its CSS and the
12 coverage.* i18n keys). The host still computes and ships
payload.coverage, so the data path is unchanged.
- Fold the coverage counts into the 会话数量 KPI card as a secondary line:
"总会话 {total} · 有用量会话:主 {main} · 子代理 {subagent}" (en mirror).
The headline stays the sessions-with-usage count (allTime.sessionCount).
- Heatmap now wipes in left-to-right, mirroring the promo GIF: each week
column fades in at w*0.018s delay over 0.45s (~0.9s total for 26 weeks).
Blanks opt out via .dsw-ust-heat-blank; the effect is disabled under
prefers-reduced-motion.
- Sync README.md / README.zh-CN.md and the wrap-client bundle header;
rebuild lib/client.js. typecheck + 53 tests + check-pack all green.
tsconfig.host.json declares "types": ["node"], which requires the @types/node package. It was never declared as a dependency, so the build only passed when @types/node happened to be hoisted in an ancestor node_modules (as on my machine). On CI's clean `npm ci` there is no ancestor node_modules, so tsc failed with: error TS2688: Cannot find type definition file for 'node'. This broke every CI run since the TS migration. Add @types/node (^22.10.0, matching the Node 22 CI runner and `engines: node >=20`) to devDependencies. Verified in an isolated clean checkout with no ancestor node_modules: npm ci + typecheck + build + test (53) + check-pack all pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更摘要
dsh-usage-panelv0.2.0 迭代:JS → TypeScript 全面重构 + P0/P1 全面升级(依据iteration-strategy.md,竞品分析驱动的补短板/吸长处,main 保持冻结,本分支为 worktree 产物)。src/shared | src/host | src/client三层,strict +noUncheckedIndexedAccess;esbuild 构建 + ModuleLoader 包装;运行时产物契约与 v0.1.0 完全一致(模拟 loader 验证{apply, inject: ['slots','connection','locale']});exports.types已提供。ctx.sessionProjections注册,stateVersion校验)——重试同一步消息不再重复累计(authoritative 替换 provisional)、compaction 独立归因、llm/retry独立计数、reasoning 不重复计、四桶互斥记账;forksession/end-seed种子去重保留;sessionQuery全量重扫降级为回退路径,两路共用同一纯函数 reducer。catch{continue});④ 会话用量排行(Top 10,标题折叠);⑤ zh/en i18n(DSH locale 服务,host 只返回机器可读错误码)。ci.yml(typecheck→build→test→check-pack);publish.yml(OIDC Trusted Publishing + provenance + tag↔版本校验 + 打包门禁)。P0/P1 完成清单(测试证据)
tests/usage.test.ts:dayKeyUTC 跨时区用例tests/usage.test.ts:hitRate 用例(四桶互斥契约)tests/aggregate.test.ts:finalizeOverview coverage 断言tests/aggregate.test.ts:topSessions 排序 + 标题断言src/client/locales.ts全量词典 + label thunk + locale/change 订阅tests/projection.test.ts:14 用例(种子去重/归因/重试替换/压缩/step 提交)tests/projection.test.tsbyProvider +aggregate.test.tsproviders 断言tests/export.test.ts:注入防护/RFC4180/BOMsrc/client/api.tsisUsable +boundary.tsx+ freshness 五态{name,inject,apply}与 client{apply,inject}导出核对验证输出:
npm run typecheck✓ ·npm run build✓(干净重建)·npm test42/42 ✓ ·npm run check-pack✓。npm 打包门禁
tarball 实测清单(16 文件,无任何静态资产):
lib/(js + d.ts)、cordis.patch.yml、README.md、README.zh-CN.md、LICENSE、package.json。scripts/check-pack.mjs挂prepublishOnly+ CI,任何图片进包即失败。决策文档
docs/P2-decisions.md。DECISIONS.md。dsh-usage-panel→ Access:为仓库配置 Trusted Publishing(OIDC publisher)。npm version 0.2.0→ 合并回 main → push tagv0.2.0(publish.yml 会校验 tag 与 package.json 版本一致并执行 test/build/check-pack 后发布,无需本地 token)。npm publish、npm version、打 tag。