@nexus-aethra/dshell-usage 0.1.13 → 0.1.14-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/lib/client.js.map +1 -1
- package/lib/index-store.js +43 -43
- package/package.json +18 -18
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Nexus Aethra
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nexus Aethra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","useCallback","useRef","useMemo","_jsxs","_jsx"],"sources":["client/locales.js","../../../../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js","../../../../node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js","../../std/lib/contracts.js","client/heatmap.js","client/page.js","client/nav-icon.js","client/index.js"],"sourcesContent":["/**\n * `dshellUsage` namespace dictionaries, and the namespace's declaration.\n *\n * The namespace merge lives with its key set so that any module naming\n * `TranslateNS<'dshellUsage'>` or `PropsLocale<'dshellUsage'>` needs only this\n * file. Keys are lowercase and dotted: `nav.*` (the settings navigation row),\n * `range.*` (the window selector), `chart.*` (the two charts' captions),\n * `table.*` (the route breakdown), and `state.*` (empty, busy, freshness, and\n * the load failure).\n */\n/** Simplified Chinese dictionary and key-set source of truth. */\nexport const zh = {\n // The Settings navigation row this page owns.\n 'nav': '用量',\n 'title': '模型 token 消耗',\n 'subtitle': '按模型汇总每个会话上报的用量。索引建在 dshell 自己的数据目录里,首次打开会扫一遍全部历史会话,之后只补新增的部分。',\n // The window selector. `{days}` is the day count.\n 'range.7': '近 7 天',\n 'range.30': '近 30 天',\n 'range.all': '全部',\n 'range.label': '时间范围',\n 'rebuild': '重新聚合',\n 'rebuild.busy': '正在聚合…',\n 'chart.heatmap': '每日热力(近 26 周内)',\n 'chart.heatmapDay': '{day} · {total} tok · {turns} 次调用',\n 'chart.heatmapLess': '少',\n 'chart.heatmapMore': '多',\n 'chart.heatmapPeak': '最忙一天 {total} tok',\n 'chart.heatmapTruncated': '只画最近 26 周',\n 'chart.curve': '每日消耗(按模型)',\n 'chart.pie': '模型占比',\n 'summary.total': '合计',\n 'summary.uncached': '未缓存输入',\n 'summary.output': '输出',\n 'summary.cacheRead': '缓存读',\n 'summary.turns': '调用',\n 'summary.hitRate': '缓存命中率',\n 'table.title': '按模型明细',\n 'table.model': '模型',\n 'table.input': '输入',\n 'table.output': '输出',\n 'table.cacheRead': '缓存读',\n 'table.cacheWrite': '缓存写',\n 'table.turns': '调用',\n 'table.total': '合计',\n 'state.empty.title': '还没有可统计的用量',\n 'state.empty.body': '索引里没有任何一条带用量上报的模型调用。跑一次 agent 会话后再点「重新聚合」。',\n 'state.freshness': '索引更新于 {day}',\n 'state.skipped': '有 {count} 个会话的日志读不了,未计入(详情见悬停提示)。',\n 'state.scanned': '本次扫描列出 {sessions} 个会话,读取了 {read} 个,命中 {turns} 次调用。',\n 'state.error': '读取用量失败:{reason}',\n 'state.loading': '正在读取索引…',\n};\n/** English dictionary, keyed by the same set. */\nexport const en = {\n 'nav': 'Usage',\n 'title': 'Model token usage',\n 'subtitle': 'Per-model totals folded from the usage every session reports. The index lives in dshell\\'s own data directory: the first visit scans every session once, and later visits add only what is new.',\n 'range.7': 'Last 7 days',\n 'range.30': 'Last 30 days',\n 'range.all': 'All time',\n 'range.label': 'Range',\n 'rebuild': 'Rebuild',\n 'rebuild.busy': 'Rebuilding…',\n 'chart.heatmap': 'Daily heatmap (last 26 weeks)',\n 'chart.heatmapDay': '{day} · {total} tok · {turns} calls',\n 'chart.heatmapLess': 'Less',\n 'chart.heatmapMore': 'More',\n 'chart.heatmapPeak': 'Busiest day {total} tok',\n 'chart.heatmapTruncated': 'Showing the last 26 weeks',\n 'chart.curve': 'Daily usage by model',\n 'chart.pie': 'Share by model',\n 'summary.total': 'Total',\n 'summary.uncached': 'Uncached input',\n 'summary.output': 'Output',\n 'summary.cacheRead': 'Cache read',\n 'summary.turns': 'Calls',\n 'summary.hitRate': 'Cache hit rate',\n 'table.title': 'By model',\n 'table.model': 'Model',\n 'table.input': 'Input',\n 'table.output': 'Output',\n 'table.cacheRead': 'Cache read',\n 'table.cacheWrite': 'Cache write',\n 'table.turns': 'Calls',\n 'table.total': 'Total',\n 'state.empty.title': 'No usage to count yet',\n 'state.empty.body': 'The index holds no model call that reported usage. Run an agent session, then press Rebuild.',\n 'state.freshness': 'Index updated {day}',\n 'state.skipped': '{count} session logs could not be read and are not counted (hover for the reason).',\n 'state.scanned': 'This scan listed {sessions} sessions, read {read}, and counted {turns} calls.',\n 'state.error': 'Reading usage failed: {reason}',\n 'state.loading': 'Reading the index…',\n};\n//# sourceMappingURL=locales.js.map","/**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var f=require(\"react\"),k=Symbol.for(\"react.element\"),l=Symbol.for(\"react.fragment\"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};\nfunction q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=\"\"+g);void 0!==a.key&&(e=\"\"+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.min.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","/**\n * Every dshell wire contract: the `/api/dshell/*` paths and the shapes that\n * cross them.\n *\n * Layering rule for this file, and for this package as a whole: it declares\n * FACTS, not behaviour. No dsh packages, no runtime state, and no imports except\n * types from this package's own sibling modules — so both halves of every plugin\n * (host route and browser face) can import the same declaration, and a contract\n * change is a compile error on both sides instead of a silent drift. The browser\n * faces used to restate these shapes by hand precisely because there was nowhere\n * shared to put them.\n *\n * Sections below were moved verbatim from the packages' own protocol modules,\n * which now re-export from here; the package-local headers are kept because they\n * carry the reasoning for each shape.\n */\n/**\n * The file navigator's wire vocabulary, shared by the host route and the\n * browser face.\n *\n * Deliberately free of value imports: the browser half imports this module, and\n * anything it pulls in would be bundled into `client.js`.\n *\n * A path on the wire is always the CANONICAL absolute path **in the session's\n * own execution world** — the host path for a local session, the device path\n * for a device-bound one. That is the string the client shows, splits into\n * segments, trims for `..`, and hands back to list again, so both ends agree on\n * one spelling without either knowing which world produced it.\n */\n/** The exact `/api` path the file navigator talks to. */\nexport const DSHELL_FILES_PATH = '/api/dshell/files';\n// ─── files — the two-world transfer view ───\n// moved from packages/dshell/files/src/transfer-protocol.ts\n/**\n * The file-transfer wire vocabulary: one device session's two file trees, and\n * the copies between them.\n *\n * Separate from `./protocol.ts` because it is a different route with a\n * different subject. The navigator's route answers about ONE world (the\n * session's); this one is about the pair — this machine and the device the\n * session runs on — and about work that outlives one request.\n *\n * Deliberately free of value imports: the browser face imports this module, and\n * anything it pulled in would be bundled into `client.js`. `Buffer`-style host\n * types are not needed on the browser side at all.\n */\n/** The exact `/api` path the transfer view talks to. */\nexport const DSHELL_TRANSFER_PATH = '/api/dshell/transfer';\n// ─── buffer — the cross-session pipe: links, tickets, grants, transfers ───\n// moved from packages/dshell/buffer/src/protocol.ts\n/**\n * The buffer's wire vocabulary, shared by the host route and the browser face.\n *\n * Deliberately free of value imports: the browser half imports this module, and\n * anything it pulls in would be bundled into `client.js`. Identifiers that the\n * host validates against (a request `action`, a ticket `state`) are plain\n * strings here and narrowed on the host side.\n *\n * Session ids travel as plain strings rather than the branded `SessionId`\n * because this is a JSON boundary; the host converts at the edges.\n */\n/** The exact `/api` path the pipe UI talks to. */\nexport const DSHELL_BUFFER_PATH = '/api/dshell/buffer';\n/** The plugin name every buffer-authored message carries as its provenance. */\nexport const BUFFER_PLUGIN = 'dshell-buffer';\n/** States after which a ticket is settled and never changes again. */\nexport const SETTLED_STATES = ['done', 'failed', 'timeout', 'cancelled'];\n// ─── ssh — devices, bindings, their requests/responses ───\n// moved from packages/dshell/ssh/src/protocol.ts\n/**\n * dshell-ssh wire — one exact `/api/dshell/ssh` route, plus the device shapes\n * both halves agree on.\n *\n * The route carries what must not travel through the settings document: the\n * private keys themselves. A device's durable record is small JSON; its key is\n * a separate file written 0600 under `$DSH_HOME/dshell/ssh/keys/`.\n *\n * This module is imported by the browser half through the package's\n * `./protocol` subpath, so it must stay free of value imports: anything it\n * pulls in would be bundled into the client.\n */\n/** Exact `/api` route path owned by the SSH device registry. */\nexport const DSHELL_SSH_PATH = '/api/dshell/ssh';\n/** Settings namespace owned by this plugin; also the device card's slot key. */\nexport const SSH_SETTINGS_NAMESPACE = 'dshell-ssh';\n// ─── workspace — the session delete route ───\n// moved from packages/dshell/workspace/src/protocol.ts\n/**\n * dshell session-panel wire — design 4.7 follow-up.\n *\n * This route carries ONE dshell-only concern now: the history purge. Its\n * companion, the archive tag, has moved to upstream and left this contract:\n *\n * - The **archive set is upstream's.** `workspaceRegistry.archiveSession` /\n * `unarchiveSession` own it, the stock `workspace-controller` row (enabled in\n * this profile) exposes both as remote commands and pushes a\n * `{ type: 'archived' }` increment, and `ui-settings-unarchive-sessions` —\n * the settings page this profile renders — is its restore surface. dshell\n * supplies the registry's storage half and nothing else; there is no dshell\n * archive UI and no dshell archive route. See packages/dshell/workspace/\n * src/index.ts.\n * - The **history purge still has no upstream.** `SessionPersistence` is\n * append-only — create/open/stat/list and no delete — `session-controller`\n * has no delete command, and the jsonl lease documents \"Release never\n * removes\". `api-session/removed` is a `session/disposed` relay that\n * upstream's own client turns into a snapshot flag, deleting nothing.\n *\n * The purge travels over dshell's own exact route on the shared `/api` channel\n * rather than through the Typert Remote table (whose client artifacts are\n * generated from dsh's own packages).\n *\n * The path sits below the shared channel exactly like file-upload's: the\n * physical carrier applies dsh's trust and authentication policy before the\n * handler ever runs, so the route itself does no authorization.\n *\n * Shared by both halves of the package, so the constant and the shapes live\n * in one file that neither the host nor the client bundle owns.\n */\n/** Exact `/api` route path owned by the dshell session panel. */\nexport const DSHELL_SESSIONS_PATH = '/api/dshell/sessions';\n// ─── terminal-bridge — the shell's history read ───\n// moved from packages/dshell/terminal-bridge/src/route.ts\nexport const DSHELL_PTY_PATH = '/api/dshell/pty';\n// ─── terminal-bridge — the frame stream over the shared API channel ───\n/**\n * Downstream half of the frame stream: a long-lived GET whose response body is\n * newline-delimited JSON, one bridge frame per line.\n *\n * The wire itself is unchanged — these are the very frames the ws upgrade\n * carries — but a Response body is reachable from any composition the\n * `connection` service is in, while `registerUpgrade` needs `webServer`, which\n * the desktop shell deliberately does not compose. Identity is explicit because\n * an HTTP stream has no socket to key on: both halves carry the client's own\n * `clientId`, and the GET carries the bind (`sessionId` + `stream`) that a ws\n * would have sent as a frame.\n */\nexport const DSHELL_STREAM_PATH = '/api/dshell/stream';\n/**\n * Upstream half: one POST per control frame (`input`, `resize`, `signal`,\n * `agent-open`, `reconnect`), identified by the same `clientId` the GET opened\n * with. Answers with no body; the stream is the only place results appear.\n */\nexport const DSHELL_STREAM_SEND_PATH = '/api/dshell/stream/send';\n// ─── dshell's own data root, and the directory browser that picks it ───\n/**\n * Environment variable holding the directory dshell keeps its own files under.\n *\n * dshell derives every one of its paths from a harness home — `DSH_HOME` when\n * the deployment sets one, `~/.dsh` otherwise — and puts `dshell/` (device\n * registry, buffer state, session tags, mount points) and `dshell-pty/`\n * (transcripts, their timelines, the history database) beneath it.\n *\n * This variable overrides that home for dshell ALONE. The distinct name is the\n * whole point: `DSH_HOME` belongs to the harness and moving it moves sessions,\n * settings and storage too, while a reader who wants their shell transcripts on\n * another disk is not asking to relocate dsh. Two deployments set it — the\n * settings card (`dataDir` in the `dshell` namespace, applied by the host half\n * at start) and a user or unit file exporting it directly, which is what the\n * packaged builds use.\n *\n * It is read at each path resolution rather than cached, for the same reason\n * dsh's own resolution is: the value is a fact about the process, set once\n * before anything asks.\n */\nexport const DSHELL_HOME_ENV = 'DSHELL_HOME';\n/**\n * The bundle package this workspace publishes.\n *\n * Named here because the Plugins panel keys a plugin's own configuration by\n * `<bundle package>#<row id>` (see dsh's ui-plugin-manager slot contract), so a\n * package mounting a config page there has to spell the bundle it ships in —\n * and spelling it in two places is how one of them goes stale.\n */\nexport const DSHELL_BUNDLE_NAME = '@nexus-aethra/dshell-bundle';\n/**\n * Exact `/api` route path owned by dshell's directory browser.\n *\n * The settings card's data-directory field cannot be typed by everyone and\n * cannot be a native folder dialog: a browser has no access to a host path, and\n * the harness may not even be on the machine drawing the page (the desktop\n * shell and a remote `dsh web` are the same client). So the host lists its own\n * directories and the card draws them — the picker is an ordinary route on the\n * shared `/api` channel, under the same trust and authentication policy as the\n * rest of dshell's routes.\n */\nexport const DSHELL_DIRS_PATH = '/api/dshell/dirs';\n/** The cordis service name `DshellFileSystem` publishes under. */\nexport const DEVICE_FS_SERVICE = 'dshellDeviceFs';\n// ─── usage — the per-model token index the settings page charts ───\n/**\n * The usage index route: one exact `/api` endpoint behind dsh's existing trust\n * and authentication fence, in the shape every other dshell route uses.\n *\n * The page reads aggregates and triggers the scan through the same endpoint:\n * the scan is the expensive half and belongs on the host, where the session\n * events already are, so the browser asks for it rather than doing it.\n */\nexport const DSHELL_USAGE_PATH = '/api/dshell/usage';\n//# sourceMappingURL=contracts.js.map","/**\n * The heatmap's layout: a fixed calendar rectangle of day cells.\n *\n * The grid is filled the way a page is read — top to bottom, then the next\n * column — so the newest day always sits in the BOTTOM-RIGHT corner. That is\n * the shape a reader expects from a \"last 26 weeks\" block, and it is why the\n * cells are not weekday-aligned: aligning columns to weeks would leave the most\n * recent day wherever its weekday happened to fall, which reads as a hole in the\n * corner rather than as \"this is now\".\n *\n * Pure and separate from the page for the same reason the fold is separate from\n * the store: the interesting part is date arithmetic (which day a cell holds,\n * which cells the history actually covers, how a token count becomes a shade),\n * and that is testable without a browser while a chart is not.\n *\n * Days are the host's own `YYYY-MM-DD` local days, and the anchor is the host's\n * today — the index bucketed by LOCAL day when it scanned, so the grid reads\n * them as local dates too, or a bucket would slide across a cell boundary near\n * midnight.\n */\n/** How many shades a non-empty day can take, above \"nothing recorded\". */\nexport const HEAT_LEVELS = 4;\n/** The shape of the grid: columns of a week, however many weeks fit. */\nexport const HEAT_ROWS = 7;\n/** The local date of a `YYYY-MM-DD` label, at local midnight. */\nfunction dateOf(day) {\n return new Date(`${day}T00:00:00`);\n}\n/** The `YYYY-MM-DD` label of a local date, the way the index spells days. */\nfunction labelOf(date) {\n const pad = (value) => String(value).padStart(2, '0');\n return `${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n}\n/**\n * Lay the recorded days out in a full rectangle of `weeks` columns.\n *\n * The newest cell is the anchor: the last column's bottom cell holds the anchor\n * day, and every earlier cell steps back one day, so the grid reads oldest at\n * the top-left and newest at the bottom-right. The rectangle is always full —\n * cells the history does not reach are present but empty — because a grid that\n * grew and shrank with the data would move the corner as the history changed.\n *\n * @param days - one entry per recorded day, any order.\n * @param weeks - how many columns to draw.\n * @param anchor - the host's today, `YYYY-MM-DD`; the bottom-right cell.\n * @returns the columns, left to right.\n */\nexport function heatmapColumns(days, weeks, anchor) {\n const byDay = new Map(days.map(day => [day.day, day]));\n const max = Math.max(1, ...days.map(day => day.total));\n const end = dateOf(anchor);\n const columns = [];\n for (let column = 0; column < weeks; column += 1) {\n const cells = [];\n for (let row = 0; row < HEAT_ROWS; row += 1) {\n // Counted back from the anchor, so the loop's last cell is the anchor\n // itself however many columns the grid has.\n const back = (weeks - 1 - column) * HEAT_ROWS + (HEAT_ROWS - 1 - row);\n const date = new Date(end);\n date.setDate(date.getDate() - back);\n const label = labelOf(date);\n if (date.getTime() > end.getTime()) {\n cells.push(undefined);\n continue;\n }\n const recorded = byDay.get(label);\n cells.push(recorded === undefined\n ? undefined\n : {\n ...recorded,\n level: recorded.total === 0\n ? 0\n : Math.min(HEAT_LEVELS, 1 + Math.floor((recorded.total / max) * (HEAT_LEVELS - 1))),\n });\n }\n columns.push(cells);\n }\n return columns;\n}\n/**\n * Whether the history reaches further back than the grid draws.\n *\n * @param days - the same days handed to {@link heatmapColumns}.\n * @param weeks - the same width.\n * @param anchor - the same anchor.\n * @returns whether any recorded day fell off the left edge.\n */\nexport function heatmapTruncated(days, weeks, anchor) {\n if (days.length === 0)\n return false;\n const oldest = days.reduce((earliest, day) => (day.day < earliest ? day.day : earliest), days[0].day);\n const start = dateOf(anchor);\n start.setDate(start.getDate() - (weeks * HEAT_ROWS - 1));\n return dateOf(oldest).getTime() < start.getTime();\n}\n//# sourceMappingURL=heatmap.js.map","import { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\n/**\n * The usage page: one settings entry that charts what every model has cost.\n *\n * Everything on it comes from one host endpoint. `summary` reads the index,\n * `scan` extends it first — the host owns both the session events and the\n * index, so the browser asks for a rebuild rather than performing one.\n *\n * The two charts answer different questions and neither is derivable from the\n * other: the stacked area shows how the total moved over time and which route\n * moved it, and the pie shows what share each route holds across the whole\n * window. Both are hand-rolled SVG — the client bundle inlines its imports, and\n * a charting library would be the largest thing in it by an order of magnitude.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { DSHELL_USAGE_PATH } from '@nexus-aethra/dshell-std';\nimport { HEAT_LEVELS, heatmapColumns, heatmapTruncated } from './heatmap.js';\n/** A fixed palette: a route keeps its colour as the window changes. */\nconst PALETTE = [\n '#5B8FF9', '#61DDAA', '#F6BD16', '#7262FD', '#78D3F8',\n '#9661BC', '#F6903D', '#008685', '#F08BB4', '#E8684A',\n];\nconst RANGES = [\n { days: 7, key: 'range.7' },\n { days: 30, key: 'range.30' },\n { days: null, key: 'range.all' },\n];\n/** Compact token counts: the axis and the table both read better rounded. */\nconst counts = new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 });\n/** Rough sizes; the page is a settings column, not a full viewport. */\nconst CURVE = { width: 720, height: 200, padLeft: 56, padRight: 12, padTop: 12, padBottom: 24 };\nconst PIE = { size: 200, radius: 88, inner: 52 };\n/** The heatmap's grid geometry: a fixed half-year block at settings scale. */\nconst HEAT = { cell: 13, gap: 3, weeks: 26 };\n/** The heatmap's shades, from \"a little\" to \"the busiest day\", tinted like the first curve. */\nconst HEAT_SHADES = ['#5B8FF9', '#4a78e0', '#3c62c4', '#2f4da6'];\n/** Every bucket summed — the measure both charts and the table sort on. */\nfunction total(buckets) {\n return buckets.uncachedInputTokens + buckets.outputTokens + buckets.cacheReadTokens + buckets.cacheWriteTokens;\n}\n/** One model's curve: the path across the days, and a point at each day. */\nfunction linePoints(values, xs, y) {\n return values.map((value, index) => `${String(xs[index])},${String(y(value))}`).join(' ');\n}\n/**\n * One ring segment.\n *\n * A segment covering the whole circle needs its own spelling: its start and end\n * points coincide, and an arc between coincident points draws nothing — which\n * is exactly what a reader with one model would see. The full turn is two half\n * turns, outer and inner wound in opposite directions so the middle stays open.\n */\nfunction ringPath(start, end) {\n const { size, radius, inner } = PIE;\n const cx = size / 2;\n const cy = size / 2;\n const at = (r, angle) => [\n cx + r * Math.cos(angle),\n cy + r * Math.sin(angle),\n ];\n if (end - start >= Math.PI * 2 - 1e-6) {\n const outer = `M${String(cx)},${String(cy - radius)}`\n + ` A${String(radius)},${String(radius)} 0 1 1 ${String(cx)},${String(cy + radius)}`\n + ` A${String(radius)},${String(radius)} 0 1 1 ${String(cx)},${String(cy - radius)} Z`;\n const hole = `M${String(cx)},${String(cy - inner)}`\n + ` A${String(inner)},${String(inner)} 0 1 0 ${String(cx)},${String(cy + inner)}`\n + ` A${String(inner)},${String(inner)} 0 1 0 ${String(cx)},${String(cy - inner)} Z`;\n return `${outer} ${hole}`;\n }\n const [ox1, oy1] = at(radius, start);\n const [ox2, oy2] = at(radius, end);\n const [ix2, iy2] = at(inner, end);\n const [ix1, iy1] = at(inner, start);\n const large = end - start > Math.PI ? 1 : 0;\n return `M${String(ox1)},${String(oy1)}`\n + ` A${String(radius)},${String(radius)} 0 ${String(large)} 1 ${String(ox2)},${String(oy2)}`\n + ` L${String(ix2)},${String(iy2)}`\n + ` A${String(inner)},${String(inner)} 0 ${String(large)} 0 ${String(ix1)},${String(iy1)} Z`;\n}\n/** The page body. */\nexport function UsageSection({ t }) {\n const [days, setDays] = useState(7);\n const [summary, setSummary] = useState(undefined);\n const [error, setError] = useState(undefined);\n const [busy, setBusy] = useState(false);\n const [note, setNote] = useState(undefined);\n /** Sessions the last scan had to skip, so the page can say the totals are partial. */\n const [skipped, setSkipped] = useState(undefined);\n const ask = useCallback(async (action, window) => {\n setBusy(true);\n setError(undefined);\n try {\n const response = await fetch(DSHELL_USAGE_PATH, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action, days: window }),\n });\n const body = await response.json();\n if ('error' in body)\n throw new Error(body.error);\n setSummary(body);\n // The counts come from the scan's own report: how many sessions exist,\n // how many of them needed decoding, and how many turns were counted are\n // three different numbers, and none of them is derivable from the rows\n // the page is about to draw.\n setNote(action === 'scan' && body.scanned !== undefined\n ? t('state.scanned', {\n sessions: counts.format(body.scanned.sessions),\n read: counts.format(body.scanned.read),\n turns: counts.format(body.scanned.turns),\n })\n : undefined);\n // A scan that skipped a session produced partial totals, and a reader who\n // is not told reads the number as the whole. The reason travels with it.\n setSkipped(body.scanned !== undefined && body.scanned.skipped > 0\n ? { count: body.scanned.skipped, reason: body.scanned.skipReason }\n : undefined);\n }\n catch (reason) {\n setError(reason instanceof Error ? reason.message : String(reason));\n }\n finally {\n setBusy(false);\n }\n }, [t]);\n // Opening the page extends the index once — the host coalesces a burst of\n // opens behind its own interval — and a later window change only re-reads,\n // because the window is a query argument rather than a filter over one big\n // payload. Without that first scan a reader who never presses 「重新聚合」\n // would read a frozen index, which is what the page did before.\n const firstOpen = useRef(true);\n useEffect(() => {\n const action = firstOpen.current ? 'scan' : 'summary';\n firstOpen.current = false;\n void ask(action, days);\n }, [ask, days]);\n const curve = useMemo(() => {\n if (summary === undefined)\n return undefined;\n const daysList = summary.daysList;\n const models = summary.models;\n if (daysList.length === 0 || models.length === 0)\n return undefined;\n const span = CURVE.width - CURVE.padLeft - CURVE.padRight;\n // A lone day has no interval to interpolate along, so it sits in the middle\n // of the plot rather than at its left edge.\n const xs = daysList.length === 1\n ? [CURVE.padLeft + span / 2]\n : daysList.map((_, index) => CURVE.padLeft + (index * span) / (daysList.length - 1));\n const seriesValues = models.map(model => daysList.map(day => summary.byDay\n .filter(row => row.day === day && `${row.provider}/${row.model}` === model)\n .reduce((sum, row) => sum + total(row), 0)));\n // Scaled to the highest single value, not to a stacked total: these are\n // separate curves, so each one has to be readable on its own.\n const max = Math.max(1, ...seriesValues.flat());\n const y = (value) => CURVE.height - CURVE.padBottom\n - (value / max) * (CURVE.height - CURVE.padTop - CURVE.padBottom);\n return {\n xs,\n y,\n max,\n daysList,\n series: models.map((model, index) => ({\n model,\n color: PALETTE[index % PALETTE.length],\n points: linePoints(seriesValues[index], xs, y),\n markers: seriesValues[index].map((value, position) => ({ x: xs[position], y: y(value) })),\n })),\n };\n }, [summary]);\n const pie = useMemo(() => {\n if (summary === undefined)\n return undefined;\n const rows = [...summary.totals];\n const sum = rows.reduce((carry, row) => carry + total(row), 0);\n if (sum === 0)\n return undefined;\n let angle = -Math.PI / 2;\n return {\n sum,\n slices: rows.map((row, index) => {\n const share = total(row) / sum;\n const start = angle;\n const end = angle + share * Math.PI * 2;\n angle = end;\n return { row, color: PALETTE[index % PALETTE.length], share, path: ringPath(start, end) };\n }),\n };\n }, [summary]);\n /** The window's headline numbers: what the two charts are shares and shapes of. */\n const headline = useMemo(() => {\n if (summary === undefined)\n return undefined;\n const sums = summary.totals.reduce((carry, row) => ({\n uncachedInputTokens: carry.uncachedInputTokens + row.uncachedInputTokens,\n outputTokens: carry.outputTokens + row.outputTokens,\n cacheReadTokens: carry.cacheReadTokens + row.cacheReadTokens,\n cacheWriteTokens: carry.cacheWriteTokens + row.cacheWriteTokens,\n turns: carry.turns + row.turns,\n }), { uncachedInputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, turns: 0 });\n const billed = sums.uncachedInputTokens + sums.cacheReadTokens;\n return {\n ...sums,\n total: total(sums),\n // A hit rate is the one number that says whether the cache is working;\n // it is read over the input side only, which is where a cache read lands.\n hitRate: billed === 0 ? undefined : sums.cacheReadTokens / billed,\n };\n }, [summary]);\n /** The calendar grid: a fixed rectangle of weeks, newest day at the bottom right. */\n const heat = useMemo(() => {\n if (summary === undefined || summary.heat.length === 0)\n return undefined;\n const columns = heatmapColumns(summary.heat, HEAT.weeks, summary.today);\n if (columns.length === 0)\n return undefined;\n return {\n columns,\n truncated: heatmapTruncated(summary.heat, HEAT.weeks, summary.today),\n busiest: Math.max(...summary.heat.map(day => day.total)),\n };\n }, [summary]);\n return (_jsxs(\"section\", { style: { display: 'flex', flexDirection: 'column', gap: 20, padding: '4px 2px' }, children: [_jsxs(\"header\", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(\"h2\", { style: { margin: 0, fontSize: 16 }, children: t('title') }), _jsx(\"p\", { style: { margin: 0, opacity: 0.7, lineHeight: 1.6 }, children: t('subtitle') })] }), heat !== undefined && (_jsxs(\"figure\", { style: { margin: 0, display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsx(\"figcaption\", { style: { opacity: 0.8 }, children: t('chart.heatmap') }), _jsx(\"div\", { style: { display: 'flex', gap: HEAT.gap, overflowX: 'auto' }, children: heat.columns.map((column, index) => (_jsx(\"div\", { style: { display: 'grid', gridTemplateRows: `repeat(7, ${String(HEAT.cell)}px)`, gap: HEAT.gap }, children: column.map((cell, slot) => cell === undefined\n ? _jsx(\"span\", { style: { width: HEAT.cell, height: HEAT.cell, borderRadius: 3, background: 'currentColor', opacity: 0.06 } }, `empty-${String(slot)}`)\n : (_jsx(\"span\", { title: t('chart.heatmapDay', {\n day: cell.day,\n total: counts.format(cell.total),\n turns: counts.format(cell.turns),\n }), style: {\n width: HEAT.cell, height: HEAT.cell, borderRadius: 3,\n background: cell.level === 0 ? 'currentColor' : HEAT_SHADES[cell.level - 1],\n opacity: cell.level === 0 ? 0.06 : 0.35 + (cell.level / HEAT_LEVELS) * 0.65,\n } }, cell.day))) }, `column-${String(index)}`))) }), _jsxs(\"div\", { style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, opacity: 0.7 }, children: [_jsx(\"span\", { children: t('chart.heatmapLess') }), [0, 1, 2, 3, 4].map(level => (_jsx(\"span\", { style: {\n width: HEAT.cell, height: HEAT.cell, borderRadius: 3,\n background: level === 0 ? 'currentColor' : HEAT_SHADES[level - 1],\n opacity: level === 0 ? 0.06 : 0.35 + (level / HEAT_LEVELS) * 0.65,\n } }, String(level)))), _jsx(\"span\", { children: t('chart.heatmapMore') }), _jsx(\"span\", { style: { marginLeft: 6, opacity: 0.8 }, children: t('chart.heatmapPeak', { total: counts.format(heat.busiest) }) }), heat.truncated ? _jsxs(\"span\", { style: { opacity: 0.8 }, children: [\"\\u00B7 \", t('chart.heatmapTruncated')] }) : null] })] })), _jsxs(\"div\", { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }, children: [_jsx(\"span\", { style: { opacity: 0.7 }, children: t('range.label') }), RANGES.map(range => (_jsx(\"button\", { type: \"button\", onClick: () => { setDays(range.days); }, \n /* Selection is stated with weight, edge and emphasis rather than an\n inverted fill: an inverted fill needs a surface colour that reads\n against it, and inventing one goes dark-on-dark in a theme this\n page does not own. */\n style: {\n padding: '4px 12px',\n borderRadius: 999,\n cursor: 'pointer',\n border: days === range.days ? '1.5px solid currentColor' : '1px solid currentColor',\n background: 'transparent',\n color: 'inherit',\n fontWeight: days === range.days ? 600 : 400,\n opacity: days === range.days ? 1 : 0.55,\n }, children: t(range.key) }, range.key))), _jsx(\"button\", { type: \"button\", disabled: busy, onClick: () => { void ask('scan', days); }, style: { marginLeft: 'auto', padding: '4px 14px', borderRadius: 6, cursor: busy ? 'default' : 'pointer' }, children: busy ? t('rebuild.busy') : t('rebuild') })] }), summary?.scannedAt != null && (_jsxs(\"div\", { style: { opacity: 0.6, fontSize: 12 }, children: [t('state.freshness', { day: summary.scannedAt }), note === undefined ? '' : ` · ${note}`] })), skipped !== undefined && (_jsxs(\"div\", { style: {\n fontSize: 12, lineHeight: 1.6, padding: '6px 10px', borderRadius: 8,\n color: 'var(--dsw-alias-state-warning-primary, #b45309)',\n background: 'var(--dsw-alias-interactive-bg-hover-warning, rgba(245,158,11,.12))',\n }, title: skipped.reason, children: [t('state.skipped', { count: counts.format(skipped.count) }), skipped.reason === undefined ? null : (_jsxs(\"span\", { style: { opacity: 0.7 }, children: [\" \\u00B7 \", shorten(skipped.reason, 120)] }))] })), error !== undefined && (_jsx(\"div\", { style: { color: 'var(--dsh-danger, #c0392b)', fontSize: 13 }, children: t('state.error', { reason: error }) })), summary === undefined && error === undefined && _jsx(\"div\", { style: { opacity: 0.6 }, children: t('state.loading') }), headline !== undefined && headline.turns > 0 && (_jsx(\"div\", { style: { display: 'flex', gap: 18, flexWrap: 'wrap' }, children: [\n ['summary.total', counts.format(headline.total)],\n ['summary.uncached', counts.format(headline.uncachedInputTokens)],\n ['summary.output', counts.format(headline.outputTokens)],\n ['summary.cacheRead', counts.format(headline.cacheReadTokens)],\n ['summary.turns', counts.format(headline.turns)],\n ...headline.hitRate === undefined\n ? []\n : [['summary.hitRate', `${(headline.hitRate * 100).toFixed(1)}%`]],\n ].map(([key, value]) => (_jsxs(\"div\", { style: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(\"span\", { style: { fontSize: 11, opacity: 0.6 }, children: t(key) }), _jsx(\"span\", { style: { fontSize: 18, fontVariantNumeric: 'tabular-nums' }, children: value })] }, key))) })), summary !== undefined && curve === undefined && (_jsxs(\"div\", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx(\"strong\", { children: t('state.empty.title') }), _jsx(\"span\", { style: { opacity: 0.7 }, children: t('state.empty.body') })] })), curve !== undefined && (_jsxs(\"figure\", { style: { margin: 0, display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsx(\"figcaption\", { style: { opacity: 0.8 }, children: t('chart.curve') }), _jsxs(\"svg\", { viewBox: `0 0 ${String(CURVE.width)} ${String(CURVE.height)}`, style: { width: '100%', height: 'auto' }, role: \"img\", children: [[0, 0.5, 1].map(fraction => {\n const value = curve.max * fraction;\n return (_jsxs(\"g\", { children: [_jsx(\"line\", { x1: CURVE.padLeft, x2: CURVE.width - CURVE.padRight, y1: curve.y(value), y2: curve.y(value), stroke: \"currentColor\", strokeOpacity: 0.15, strokeDasharray: fraction === 0 ? undefined : '3 3' }), _jsx(\"text\", { x: CURVE.padLeft - 8, y: curve.y(value) + 4, textAnchor: \"end\", fontSize: 10, fill: \"currentColor\", fillOpacity: 0.6, children: counts.format(value) })] }, fraction));\n }), curve.series.map(series => (_jsxs(\"g\", { children: [_jsx(\"polyline\", { points: series.points, fill: \"none\", stroke: series.color, strokeWidth: 2, strokeLinejoin: \"round\" }), series.markers.map((marker, position) => (_jsx(\"circle\", { cx: marker.x, cy: marker.y, r: 3, fill: series.color }, `${series.model}-${String(position)}`)))] }, series.model))), curve.daysList.map((day, index) => (index % Math.ceil(curve.daysList.length / 8) === 0\n ? (_jsx(\"text\", { x: curve.xs[index], y: CURVE.height - 6, textAnchor: \"middle\", fontSize: 10, fill: \"currentColor\", fillOpacity: 0.6, children: day.slice(5) }, day))\n : null))] }), _jsx(Legend, { series: curve.series })] })), pie !== undefined && (_jsxs(\"figure\", { style: { margin: 0, display: 'flex', gap: 20, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx(\"figcaption\", { style: { opacity: 0.8, alignSelf: 'flex-start' }, children: t('chart.pie') }), _jsxs(\"svg\", { viewBox: `0 0 ${String(PIE.size)} ${String(PIE.size)}`, style: { width: PIE.size, height: PIE.size }, role: \"img\", children: [pie.slices.map(slice => (_jsx(\"path\", { d: slice.path, fill: slice.color, fillOpacity: 0.85, fillRule: \"evenodd\" }, `${slice.row.provider}/${slice.row.model}`))), _jsx(\"text\", { x: PIE.size / 2, y: PIE.size / 2 - 2, textAnchor: \"middle\", fontSize: 16, fill: \"currentColor\", children: counts.format(pie.sum) }), _jsx(\"text\", { x: PIE.size / 2, y: PIE.size / 2 + 14, textAnchor: \"middle\", fontSize: 10, fill: \"currentColor\", fillOpacity: 0.6, children: \"tok\" })] }), _jsxs(\"table\", { style: { borderCollapse: 'collapse', fontSize: 13 }, children: [_jsx(\"thead\", { children: _jsxs(\"tr\", { style: { opacity: 0.7 }, children: [_jsx(\"th\", { style: cellStyle('left'), children: t('table.model') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.total') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.turns') })] }) }), _jsx(\"tbody\", { children: pie.slices.map(slice => (_jsxs(\"tr\", { children: [_jsxs(\"td\", { style: cellStyle('left'), children: [_jsx(\"span\", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: slice.color, marginRight: 6 } }), slice.row.model, _jsx(\"span\", { style: { opacity: 0.5, marginLeft: 6 }, children: slice.row.provider })] }), _jsxs(\"td\", { style: cellStyle('right'), children: [counts.format(total(slice.row)), \" \", _jsxs(\"span\", { style: { opacity: 0.5 }, children: [\"(\", (slice.share * 100).toFixed(1), \"%)\"] })] }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(slice.row.turns) })] }, `${slice.row.provider}/${slice.row.model}`))) })] })] })), pie !== undefined && (_jsxs(\"details\", { children: [_jsx(\"summary\", { style: { cursor: 'pointer', opacity: 0.8 }, children: t('table.title') }), _jsxs(\"table\", { style: { borderCollapse: 'collapse', fontSize: 13, marginTop: 10, width: '100%' }, children: [_jsx(\"thead\", { children: _jsxs(\"tr\", { style: { opacity: 0.7 }, children: [_jsx(\"th\", { style: cellStyle('left'), children: t('table.model') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.input') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.output') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.cacheRead') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.cacheWrite') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.turns') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.total') })] }) }), _jsx(\"tbody\", { children: summary?.totals.map((row, index) => (_jsxs(\"tr\", { children: [_jsxs(\"td\", { style: cellStyle('left'), children: [_jsx(\"span\", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: PALETTE[index % PALETTE.length], marginRight: 6 } }), row.model, _jsx(\"span\", { style: { opacity: 0.5, marginLeft: 6 }, children: row.provider })] }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.uncachedInputTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.outputTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.cacheReadTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.cacheWriteTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.turns) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(total(row)) })] }, `${row.provider}/${row.model}`))) })] })] }))] }));\n}\n/** The curve's colour key, in the order the lines are drawn. */\nfunction Legend({ series }) {\n return (_jsx(\"div\", { style: { display: 'flex', gap: 14, flexWrap: 'wrap', fontSize: 12 }, children: series.map(entry => (_jsxs(\"span\", { style: { display: 'inline-flex', alignItems: 'center', gap: 5 }, children: [_jsx(\"span\", { style: { width: 8, height: 8, borderRadius: 2, background: entry.color } }), entry.model] }, entry.model))) }));\n}\n/** One table cell's alignment and padding, so the two tables match. */\nfunction cellStyle(align) {\n return { textAlign: align, padding: '3px 10px 3px 0' };\n}\n/**\n * Shorten a host error for a one-line note.\n *\n * A refusal names the session, the reason and the log path — useful, and far too\n * long to sit in a settings line; the full text stays in the note's `title`.\n */\nfunction shorten(text, limit) {\n return text.length <= limit ? text : `${text.slice(0, limit)}…`;\n}\n//# sourceMappingURL=page.js.map","/**\n * The usage entry's own icon in the settings navigation.\n *\n * dsh's settings shell picks a nav icon from a map of the section ids IT ships\n * (`account`, `models`, `plugins`, …) and draws a generic gear for anything\n * else, and rc.2's `settings.section` seat carries no icon of its own — so a\n * contributed section cannot ask for one and ours read as \"some settings\".\n *\n * Hence the one DOM patch in this package: find our own row by the label we\n * registered, and put a gauge where the gear was. It is the same technique\n * dshell's sidebar section uses (and the artwork is copied verbatim from\n * `@deepseek-ai/dsh-client-ui-primitives`, medium weight, 16 viewBox, 1.3px\n * stroke) because a surface mounted into a node dsh owns cannot render React\n * components into it.\n *\n * The row is re-created whenever the dialog opens, so a MutationObserver\n * watches for it; the patch is idempotent (it re-checks the label and a marker\n * attribute) and only touches the svg node React will not write to again.\n * Delete this file the day `settings.section` accepts an icon.\n */\n/**\n * The gauge artwork, verbatim from the primitives package\n * (`IconGaugeOutlineMedium`): the same three paths, in the same order.\n */\nconst GAUGE_PATHS = [\n { d: 'M3.4041 13.096C2.49514 12.187 1.87614 11.0288 1.62537 9.76798C1.37459 8.50716 1.50331 7.20028 1.99525 6.01261C2.48719 4.82494 3.32025 3.80981 4.3891 3.09557C5.45795 2.38134 6.71458 2.00008 8.0001 2C9.28563 2.00008 10.5423 2.38134 11.6111 3.09557C12.68 3.80981 13.513 4.82494 14.005 6.01261C14.4969 7.20028 14.6256 8.50716 14.3748 9.76798C14.1241 11.0288 13.5051 12.187 12.5961 13.096', stroke: true },\n { d: 'M8 8.49994L11.6114 4.88855', stroke: true },\n { d: 'M8 9.75C8.69036 9.75 9.25 9.19036 9.25 8.5C9.25 7.80964 8.69036 7.25 8 7.25C7.30964 7.25 6.75 7.80964 6.75 8.5C6.75 9.19036 7.30964 9.75 8 9.75Z', stroke: false },\n];\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n/** The marker that says a row already carries our artwork. */\nconst MARKER = 'data-dshell-usage-icon';\n/** One gauge, sized and coloured like the icons beside it. */\nfunction gauge(size, className) {\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('width', String(size));\n svg.setAttribute('height', String(size));\n svg.setAttribute('viewBox', '0 0 16 16');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke-width', '1.3');\n svg.setAttribute('aria-hidden', 'true');\n if (className !== null)\n svg.setAttribute('class', className);\n for (const spec of GAUGE_PATHS) {\n const path = document.createElementNS(SVG_NS, 'path');\n path.setAttribute('d', spec.d);\n if (spec.stroke)\n path.setAttribute('stroke', 'currentColor');\n else\n path.setAttribute('fill', 'currentColor');\n svg.append(path);\n }\n return svg;\n}\n/**\n * Keep the settings navigation's usage row drawn with the gauge.\n *\n * @param label - the section label as the shell renders it, in the live locale.\n * @returns the disposer that stops watching.\n */\nexport function watchUsageNavIcon(label) {\n if (typeof document === 'undefined')\n return () => { };\n const patch = () => {\n for (const dialog of document.querySelectorAll('[role=\"dialog\"]')) {\n for (const row of dialog.querySelectorAll('nav button')) {\n const text = row.textContent?.trim() ?? '';\n if (text !== label())\n continue;\n const svg = row.querySelector('svg');\n if (svg === null || svg.getAttribute(MARKER) !== null)\n continue;\n // React owns both children by position and neither is re-rendered while\n // the dialog lives, so the replacement is what the reader sees; the\n // pristine node would only come back with a full re-mount, which the\n // observer catches.\n const replacement = gauge(Number(svg.getAttribute('width') ?? 16) || 16, svg.getAttribute('class'));\n replacement.setAttribute(MARKER, '');\n svg.replaceWith(replacement);\n }\n }\n };\n patch();\n const observer = new MutationObserver(() => { patch(); });\n observer.observe(document.body, { childList: true, subtree: true });\n return () => { observer.disconnect(); };\n}\n//# sourceMappingURL=nav-icon.js.map","/**\n * The usage page's client face: the locale dictionaries and the Settings\n * navigation row that mounts the page.\n *\n * `settings.section` is the seat dsh's own Plugins page uses, so this page is a\n * peer of it rather than a tab inside it — the charts want the full settings\n * column, and a reader looking for \"what did this cost\" looks in the\n * navigation, not under a plugin list.\n */\nimport { en, zh } from './locales.js';\nimport { UsageSection } from './page.js';\nimport { watchUsageNavIcon } from './nav-icon.js';\n/** The locale namespace, matching the key `locales.ts` declares. */\nconst NS = 'dshellUsage';\nexport const name = '@nexus-aethra/dshell-usage/client';\nexport const inject = ['slots', 'locale'];\nexport function apply(ctx) {\n const t = ctx.locale.bind(NS);\n // Registered through an effect so a composition that unloads this plugin\n // takes its copy with it.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dshell-usage: dictionaries');\n // Ordered after the Plugins page (15) so the navigation reads\n // sessions → plugins → usage, and named with its own id so it is added beside\n // the shipped entries rather than replacing one.\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'usage',\n order: 40,\n label: () => t('nav'),\n locale: NS,\n }, UsageSection));\n // The row's own icon, because this seat carries none: dsh draws a generic\n // gear for any section id it does not ship (see nav-icon.ts).\n ctx.effect(() => watchUsageNavIcon(() => t('nav')), 'dshell-usage: navigation icon');\n}\n//# sourceMappingURL=index.js.map"],"x_google_ignoreList":[1,2],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","useCallback","useRef","useMemo","_jsxs","_jsx"],"sources":["client/locales.js","../../../../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js","../../../../node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js","../../std/lib/contracts.js","client/heatmap.js","client/page.js","client/nav-icon.js","client/index.js"],"sourcesContent":["/**\n * `dshellUsage` namespace dictionaries, and the namespace's declaration.\n *\n * The namespace merge lives with its key set so that any module naming\n * `TranslateNS<'dshellUsage'>` or `PropsLocale<'dshellUsage'>` needs only this\n * file. Keys are lowercase and dotted: `nav.*` (the settings navigation row),\n * `range.*` (the window selector), `chart.*` (the two charts' captions),\n * `table.*` (the route breakdown), and `state.*` (empty, busy, freshness, and\n * the load failure).\n */\n/** Simplified Chinese dictionary and key-set source of truth. */\nexport const zh = {\n // The Settings navigation row this page owns.\n 'nav': '用量',\n 'title': '模型 token 消耗',\n 'subtitle': '按模型汇总每个会话上报的用量。索引建在 dshell 自己的数据目录里,首次打开会扫一遍全部历史会话,之后只补新增的部分。',\n // The window selector. `{days}` is the day count.\n 'range.7': '近 7 天',\n 'range.30': '近 30 天',\n 'range.all': '全部',\n 'range.label': '时间范围',\n 'rebuild': '重新聚合',\n 'rebuild.busy': '正在聚合…',\n 'chart.heatmap': '每日热力(近 26 周内)',\n 'chart.heatmapDay': '{day} · {total} tok · {turns} 次调用',\n 'chart.heatmapLess': '少',\n 'chart.heatmapMore': '多',\n 'chart.heatmapPeak': '最忙一天 {total} tok',\n 'chart.heatmapTruncated': '只画最近 26 周',\n 'chart.curve': '每日消耗(按模型)',\n 'chart.pie': '模型占比',\n 'summary.total': '合计',\n 'summary.uncached': '未缓存输入',\n 'summary.output': '输出',\n 'summary.cacheRead': '缓存读',\n 'summary.turns': '调用',\n 'summary.hitRate': '缓存命中率',\n 'table.title': '按模型明细',\n 'table.model': '模型',\n 'table.input': '输入',\n 'table.output': '输出',\n 'table.cacheRead': '缓存读',\n 'table.cacheWrite': '缓存写',\n 'table.turns': '调用',\n 'table.total': '合计',\n 'state.empty.title': '还没有可统计的用量',\n 'state.empty.body': '索引里没有任何一条带用量上报的模型调用。跑一次 agent 会话后再点「重新聚合」。',\n 'state.freshness': '索引更新于 {day}',\n 'state.skipped': '有 {count} 个会话的日志读不了,未计入(详情见悬停提示)。',\n 'state.scanned': '本次扫描列出 {sessions} 个会话,读取了 {read} 个,命中 {turns} 次调用。',\n 'state.error': '读取用量失败:{reason}',\n 'state.loading': '正在读取索引…',\n};\n/** English dictionary, keyed by the same set. */\nexport const en = {\n 'nav': 'Usage',\n 'title': 'Model token usage',\n 'subtitle': 'Per-model totals folded from the usage every session reports. The index lives in dshell\\'s own data directory: the first visit scans every session once, and later visits add only what is new.',\n 'range.7': 'Last 7 days',\n 'range.30': 'Last 30 days',\n 'range.all': 'All time',\n 'range.label': 'Range',\n 'rebuild': 'Rebuild',\n 'rebuild.busy': 'Rebuilding…',\n 'chart.heatmap': 'Daily heatmap (last 26 weeks)',\n 'chart.heatmapDay': '{day} · {total} tok · {turns} calls',\n 'chart.heatmapLess': 'Less',\n 'chart.heatmapMore': 'More',\n 'chart.heatmapPeak': 'Busiest day {total} tok',\n 'chart.heatmapTruncated': 'Showing the last 26 weeks',\n 'chart.curve': 'Daily usage by model',\n 'chart.pie': 'Share by model',\n 'summary.total': 'Total',\n 'summary.uncached': 'Uncached input',\n 'summary.output': 'Output',\n 'summary.cacheRead': 'Cache read',\n 'summary.turns': 'Calls',\n 'summary.hitRate': 'Cache hit rate',\n 'table.title': 'By model',\n 'table.model': 'Model',\n 'table.input': 'Input',\n 'table.output': 'Output',\n 'table.cacheRead': 'Cache read',\n 'table.cacheWrite': 'Cache write',\n 'table.turns': 'Calls',\n 'table.total': 'Total',\n 'state.empty.title': 'No usage to count yet',\n 'state.empty.body': 'The index holds no model call that reported usage. Run an agent session, then press Rebuild.',\n 'state.freshness': 'Index updated {day}',\n 'state.skipped': '{count} session logs could not be read and are not counted (hover for the reason).',\n 'state.scanned': 'This scan listed {sessions} sessions, read {read}, and counted {turns} calls.',\n 'state.error': 'Reading usage failed: {reason}',\n 'state.loading': 'Reading the index…',\n};\n//# sourceMappingURL=locales.js.map","/**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var f=require(\"react\"),k=Symbol.for(\"react.element\"),l=Symbol.for(\"react.fragment\"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};\nfunction q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=\"\"+g);void 0!==a.key&&(e=\"\"+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.min.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","/**\n * Every dshell wire contract: the `/api/dshell/*` paths and the shapes that\n * cross them.\n *\n * Layering rule for this file, and for this package as a whole: it declares\n * FACTS, not behaviour. No dsh packages, no runtime state, and no imports except\n * types from this package's own sibling modules — so both halves of every plugin\n * (host route and browser face) can import the same declaration, and a contract\n * change is a compile error on both sides instead of a silent drift. The browser\n * faces used to restate these shapes by hand precisely because there was nowhere\n * shared to put them.\n *\n * Sections below were moved verbatim from the packages' own protocol modules,\n * which now re-export from here; the package-local headers are kept because they\n * carry the reasoning for each shape.\n */\n/**\n * The file navigator's wire vocabulary, shared by the host route and the\n * browser face.\n *\n * Deliberately free of value imports: the browser half imports this module, and\n * anything it pulls in would be bundled into `client.js`.\n *\n * A path on the wire is always the CANONICAL absolute path **in the session's\n * own execution world** — the host path for a local session, the device path\n * for a device-bound one. That is the string the client shows, splits into\n * segments, trims for `..`, and hands back to list again, so both ends agree on\n * one spelling without either knowing which world produced it.\n */\n/** The exact `/api` path the file navigator talks to. */\nexport const DSHELL_FILES_PATH = '/api/dshell/files';\n// ─── files — the two-world transfer view ───\n// moved from packages/dshell/files/src/transfer-protocol.ts\n/**\n * The file-transfer wire vocabulary: one device session's two file trees, and\n * the copies between them.\n *\n * Separate from `./protocol.ts` because it is a different route with a\n * different subject. The navigator's route answers about ONE world (the\n * session's); this one is about the pair — this machine and the device the\n * session runs on — and about work that outlives one request.\n *\n * Deliberately free of value imports: the browser face imports this module, and\n * anything it pulled in would be bundled into `client.js`. `Buffer`-style host\n * types are not needed on the browser side at all.\n */\n/** The exact `/api` path the transfer view talks to. */\nexport const DSHELL_TRANSFER_PATH = '/api/dshell/transfer';\n// ─── buffer — the cross-session pipe: links, tickets, grants, transfers ───\n// moved from packages/dshell/buffer/src/protocol.ts\n/**\n * The buffer's wire vocabulary, shared by the host route and the browser face.\n *\n * Deliberately free of value imports: the browser half imports this module, and\n * anything it pulls in would be bundled into `client.js`. Identifiers that the\n * host validates against (a request `action`, a ticket `state`) are plain\n * strings here and narrowed on the host side.\n *\n * Session ids travel as plain strings rather than the branded `SessionId`\n * because this is a JSON boundary; the host converts at the edges.\n */\n/** The exact `/api` path the pipe UI talks to. */\nexport const DSHELL_BUFFER_PATH = '/api/dshell/buffer';\n/** The plugin name every buffer-authored message carries as its provenance. */\nexport const BUFFER_PLUGIN = 'dshell-buffer';\n/** States after which a ticket is settled and never changes again. */\nexport const SETTLED_STATES = ['done', 'failed', 'timeout', 'cancelled'];\n// ─── ssh — devices, bindings, their requests/responses ───\n// moved from packages/dshell/ssh/src/protocol.ts\n/**\n * dshell-ssh wire — one exact `/api/dshell/ssh` route, plus the device shapes\n * both halves agree on.\n *\n * The route carries what must not travel through the settings document: the\n * private keys themselves. A device's durable record is small JSON; its key is\n * a separate file written 0600 under `$DSH_HOME/dshell/ssh/keys/`.\n *\n * This module is imported by the browser half through the package's\n * `./protocol` subpath, so it must stay free of value imports: anything it\n * pulls in would be bundled into the client.\n */\n/** Exact `/api` route path owned by the SSH device registry. */\nexport const DSHELL_SSH_PATH = '/api/dshell/ssh';\n/** Settings namespace owned by this plugin; also the device card's slot key. */\nexport const SSH_SETTINGS_NAMESPACE = 'dshell-ssh';\n// ─── terminal-bridge — the shell's history read ───\n// moved from packages/dshell/terminal-bridge/src/route.ts\nexport const DSHELL_PTY_PATH = '/api/dshell/pty';\n// ─── terminal-bridge — the frame stream over the shared API channel ───\n/**\n * Downstream half of the frame stream: a long-lived GET whose response body is\n * newline-delimited JSON, one bridge frame per line.\n *\n * The wire itself is unchanged — these are the very frames the ws upgrade\n * carries — but a Response body is reachable from any composition the\n * `connection` service is in, while `registerUpgrade` needs `webServer`, which\n * the desktop shell deliberately does not compose. Identity is explicit because\n * an HTTP stream has no socket to key on: both halves carry the client's own\n * `clientId`, and the GET carries the bind (`sessionId` + `stream`) that a ws\n * would have sent as a frame.\n */\nexport const DSHELL_STREAM_PATH = '/api/dshell/stream';\n/**\n * Upstream half: one POST per control frame (`input`, `resize`, `signal`,\n * `agent-open`, `reconnect`), identified by the same `clientId` the GET opened\n * with. Answers with no body; the stream is the only place results appear.\n */\nexport const DSHELL_STREAM_SEND_PATH = '/api/dshell/stream/send';\n// ─── dshell's own data root, and the directory browser that picks it ───\n/**\n * Environment variable holding the directory dshell keeps its own files under.\n *\n * dshell derives every one of its paths from a harness home — `DSH_HOME` when\n * the deployment sets one, `~/.dsh` otherwise — and puts `dshell/` (device\n * registry, buffer state, session tags, mount points) and `dshell-pty/`\n * (transcripts, their timelines, the history database) beneath it.\n *\n * This variable overrides that home for dshell ALONE. The distinct name is the\n * whole point: `DSH_HOME` belongs to the harness and moving it moves sessions,\n * settings and storage too, while a reader who wants their shell transcripts on\n * another disk is not asking to relocate dsh. Two deployments set it — the\n * settings card (`dataDir` in the `dshell` namespace, applied by the host half\n * at start) and a user or unit file exporting it directly, which is what the\n * packaged builds use.\n *\n * It is read at each path resolution rather than cached, for the same reason\n * dsh's own resolution is: the value is a fact about the process, set once\n * before anything asks.\n */\nexport const DSHELL_HOME_ENV = 'DSHELL_HOME';\n/**\n * The bundle package this workspace publishes.\n *\n * Named here because the Plugins panel keys a plugin's own configuration by\n * `<bundle package>#<row id>` (see dsh's ui-plugin-manager slot contract), so a\n * package mounting a config page there has to spell the bundle it ships in —\n * and spelling it in two places is how one of them goes stale.\n */\nexport const DSHELL_BUNDLE_NAME = '@nexus-aethra/dshell-bundle';\n/**\n * Exact `/api` route path owned by dshell's directory browser.\n *\n * The settings card's data-directory field cannot be typed by everyone and\n * cannot be a native folder dialog: a browser has no access to a host path, and\n * the harness may not even be on the machine drawing the page (the desktop\n * shell and a remote `dsh web` are the same client). So the host lists its own\n * directories and the card draws them — the picker is an ordinary route on the\n * shared `/api` channel, under the same trust and authentication policy as the\n * rest of dshell's routes.\n */\nexport const DSHELL_DIRS_PATH = '/api/dshell/dirs';\n/** The cordis service name `DshellFileSystem` publishes under. */\nexport const DEVICE_FS_SERVICE = 'dshellDeviceFs';\n// ─── usage — the per-model token index the settings page charts ───\n/**\n * The usage index route: one exact `/api` endpoint behind dsh's existing trust\n * and authentication fence, in the shape every other dshell route uses.\n *\n * The page reads aggregates and triggers the scan through the same endpoint:\n * the scan is the expensive half and belongs on the host, where the session\n * events already are, so the browser asks for it rather than doing it.\n */\nexport const DSHELL_USAGE_PATH = '/api/dshell/usage';\n//# sourceMappingURL=contracts.js.map","/**\n * The heatmap's layout: a fixed calendar rectangle of day cells.\n *\n * The grid is filled the way a page is read — top to bottom, then the next\n * column — so the newest day always sits in the BOTTOM-RIGHT corner. That is\n * the shape a reader expects from a \"last 26 weeks\" block, and it is why the\n * cells are not weekday-aligned: aligning columns to weeks would leave the most\n * recent day wherever its weekday happened to fall, which reads as a hole in the\n * corner rather than as \"this is now\".\n *\n * Pure and separate from the page for the same reason the fold is separate from\n * the store: the interesting part is date arithmetic (which day a cell holds,\n * which cells the history actually covers, how a token count becomes a shade),\n * and that is testable without a browser while a chart is not.\n *\n * Days are the host's own `YYYY-MM-DD` local days, and the anchor is the host's\n * today — the index bucketed by LOCAL day when it scanned, so the grid reads\n * them as local dates too, or a bucket would slide across a cell boundary near\n * midnight.\n */\n/** How many shades a non-empty day can take, above \"nothing recorded\". */\nexport const HEAT_LEVELS = 4;\n/** The shape of the grid: columns of a week, however many weeks fit. */\nexport const HEAT_ROWS = 7;\n/** The local date of a `YYYY-MM-DD` label, at local midnight. */\nfunction dateOf(day) {\n return new Date(`${day}T00:00:00`);\n}\n/** The `YYYY-MM-DD` label of a local date, the way the index spells days. */\nfunction labelOf(date) {\n const pad = (value) => String(value).padStart(2, '0');\n return `${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n}\n/**\n * Lay the recorded days out in a full rectangle of `weeks` columns.\n *\n * The newest cell is the anchor: the last column's bottom cell holds the anchor\n * day, and every earlier cell steps back one day, so the grid reads oldest at\n * the top-left and newest at the bottom-right. The rectangle is always full —\n * cells the history does not reach are present but empty — because a grid that\n * grew and shrank with the data would move the corner as the history changed.\n *\n * @param days - one entry per recorded day, any order.\n * @param weeks - how many columns to draw.\n * @param anchor - the host's today, `YYYY-MM-DD`; the bottom-right cell.\n * @returns the columns, left to right.\n */\nexport function heatmapColumns(days, weeks, anchor) {\n const byDay = new Map(days.map(day => [day.day, day]));\n const max = Math.max(1, ...days.map(day => day.total));\n const end = dateOf(anchor);\n const columns = [];\n for (let column = 0; column < weeks; column += 1) {\n const cells = [];\n for (let row = 0; row < HEAT_ROWS; row += 1) {\n // Counted back from the anchor, so the loop's last cell is the anchor\n // itself however many columns the grid has.\n const back = (weeks - 1 - column) * HEAT_ROWS + (HEAT_ROWS - 1 - row);\n const date = new Date(end);\n date.setDate(date.getDate() - back);\n const label = labelOf(date);\n if (date.getTime() > end.getTime()) {\n cells.push(undefined);\n continue;\n }\n const recorded = byDay.get(label);\n cells.push(recorded === undefined\n ? undefined\n : {\n ...recorded,\n level: recorded.total === 0\n ? 0\n : Math.min(HEAT_LEVELS, 1 + Math.floor((recorded.total / max) * (HEAT_LEVELS - 1))),\n });\n }\n columns.push(cells);\n }\n return columns;\n}\n/**\n * Whether the history reaches further back than the grid draws.\n *\n * @param days - the same days handed to {@link heatmapColumns}.\n * @param weeks - the same width.\n * @param anchor - the same anchor.\n * @returns whether any recorded day fell off the left edge.\n */\nexport function heatmapTruncated(days, weeks, anchor) {\n if (days.length === 0)\n return false;\n const oldest = days.reduce((earliest, day) => (day.day < earliest ? day.day : earliest), days[0].day);\n const start = dateOf(anchor);\n start.setDate(start.getDate() - (weeks * HEAT_ROWS - 1));\n return dateOf(oldest).getTime() < start.getTime();\n}\n//# sourceMappingURL=heatmap.js.map","import { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\n/**\n * The usage page: one settings entry that charts what every model has cost.\n *\n * Everything on it comes from one host endpoint. `summary` reads the index,\n * `scan` extends it first — the host owns both the session events and the\n * index, so the browser asks for a rebuild rather than performing one.\n *\n * The two charts answer different questions and neither is derivable from the\n * other: the stacked area shows how the total moved over time and which route\n * moved it, and the pie shows what share each route holds across the whole\n * window. Both are hand-rolled SVG — the client bundle inlines its imports, and\n * a charting library would be the largest thing in it by an order of magnitude.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { DSHELL_USAGE_PATH } from '@nexus-aethra/dshell-std';\nimport { HEAT_LEVELS, heatmapColumns, heatmapTruncated } from './heatmap.js';\n/** A fixed palette: a route keeps its colour as the window changes. */\nconst PALETTE = [\n '#5B8FF9', '#61DDAA', '#F6BD16', '#7262FD', '#78D3F8',\n '#9661BC', '#F6903D', '#008685', '#F08BB4', '#E8684A',\n];\nconst RANGES = [\n { days: 7, key: 'range.7' },\n { days: 30, key: 'range.30' },\n { days: null, key: 'range.all' },\n];\n/** Compact token counts: the axis and the table both read better rounded. */\nconst counts = new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 });\n/** Rough sizes; the page is a settings column, not a full viewport. */\nconst CURVE = { width: 720, height: 200, padLeft: 56, padRight: 12, padTop: 12, padBottom: 24 };\nconst PIE = { size: 200, radius: 88, inner: 52 };\n/** The heatmap's grid geometry: a fixed half-year block at settings scale. */\nconst HEAT = { cell: 13, gap: 3, weeks: 26 };\n/** The heatmap's shades, from \"a little\" to \"the busiest day\", tinted like the first curve. */\nconst HEAT_SHADES = ['#5B8FF9', '#4a78e0', '#3c62c4', '#2f4da6'];\n/** Every bucket summed — the measure both charts and the table sort on. */\nfunction total(buckets) {\n return buckets.uncachedInputTokens + buckets.outputTokens + buckets.cacheReadTokens + buckets.cacheWriteTokens;\n}\n/** One model's curve: the path across the days, and a point at each day. */\nfunction linePoints(values, xs, y) {\n return values.map((value, index) => `${String(xs[index])},${String(y(value))}`).join(' ');\n}\n/**\n * One ring segment.\n *\n * A segment covering the whole circle needs its own spelling: its start and end\n * points coincide, and an arc between coincident points draws nothing — which\n * is exactly what a reader with one model would see. The full turn is two half\n * turns, outer and inner wound in opposite directions so the middle stays open.\n */\nfunction ringPath(start, end) {\n const { size, radius, inner } = PIE;\n const cx = size / 2;\n const cy = size / 2;\n const at = (r, angle) => [\n cx + r * Math.cos(angle),\n cy + r * Math.sin(angle),\n ];\n if (end - start >= Math.PI * 2 - 1e-6) {\n const outer = `M${String(cx)},${String(cy - radius)}`\n + ` A${String(radius)},${String(radius)} 0 1 1 ${String(cx)},${String(cy + radius)}`\n + ` A${String(radius)},${String(radius)} 0 1 1 ${String(cx)},${String(cy - radius)} Z`;\n const hole = `M${String(cx)},${String(cy - inner)}`\n + ` A${String(inner)},${String(inner)} 0 1 0 ${String(cx)},${String(cy + inner)}`\n + ` A${String(inner)},${String(inner)} 0 1 0 ${String(cx)},${String(cy - inner)} Z`;\n return `${outer} ${hole}`;\n }\n const [ox1, oy1] = at(radius, start);\n const [ox2, oy2] = at(radius, end);\n const [ix2, iy2] = at(inner, end);\n const [ix1, iy1] = at(inner, start);\n const large = end - start > Math.PI ? 1 : 0;\n return `M${String(ox1)},${String(oy1)}`\n + ` A${String(radius)},${String(radius)} 0 ${String(large)} 1 ${String(ox2)},${String(oy2)}`\n + ` L${String(ix2)},${String(iy2)}`\n + ` A${String(inner)},${String(inner)} 0 ${String(large)} 0 ${String(ix1)},${String(iy1)} Z`;\n}\n/** The page body. */\nexport function UsageSection({ t }) {\n const [days, setDays] = useState(7);\n const [summary, setSummary] = useState(undefined);\n const [error, setError] = useState(undefined);\n const [busy, setBusy] = useState(false);\n const [note, setNote] = useState(undefined);\n /** Sessions the last scan had to skip, so the page can say the totals are partial. */\n const [skipped, setSkipped] = useState(undefined);\n const ask = useCallback(async (action, window) => {\n setBusy(true);\n setError(undefined);\n try {\n const response = await fetch(DSHELL_USAGE_PATH, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action, days: window }),\n });\n const body = await response.json();\n if ('error' in body)\n throw new Error(body.error);\n setSummary(body);\n // The counts come from the scan's own report: how many sessions exist,\n // how many of them needed decoding, and how many turns were counted are\n // three different numbers, and none of them is derivable from the rows\n // the page is about to draw.\n setNote(action === 'scan' && body.scanned !== undefined\n ? t('state.scanned', {\n sessions: counts.format(body.scanned.sessions),\n read: counts.format(body.scanned.read),\n turns: counts.format(body.scanned.turns),\n })\n : undefined);\n // A scan that skipped a session produced partial totals, and a reader who\n // is not told reads the number as the whole. The reason travels with it.\n setSkipped(body.scanned !== undefined && body.scanned.skipped > 0\n ? { count: body.scanned.skipped, reason: body.scanned.skipReason }\n : undefined);\n }\n catch (reason) {\n setError(reason instanceof Error ? reason.message : String(reason));\n }\n finally {\n setBusy(false);\n }\n }, [t]);\n // Opening the page extends the index once — the host coalesces a burst of\n // opens behind its own interval — and a later window change only re-reads,\n // because the window is a query argument rather than a filter over one big\n // payload. Without that first scan a reader who never presses 「重新聚合」\n // would read a frozen index, which is what the page did before.\n const firstOpen = useRef(true);\n useEffect(() => {\n const action = firstOpen.current ? 'scan' : 'summary';\n firstOpen.current = false;\n void ask(action, days);\n }, [ask, days]);\n const curve = useMemo(() => {\n if (summary === undefined)\n return undefined;\n const daysList = summary.daysList;\n const models = summary.models;\n if (daysList.length === 0 || models.length === 0)\n return undefined;\n const span = CURVE.width - CURVE.padLeft - CURVE.padRight;\n // A lone day has no interval to interpolate along, so it sits in the middle\n // of the plot rather than at its left edge.\n const xs = daysList.length === 1\n ? [CURVE.padLeft + span / 2]\n : daysList.map((_, index) => CURVE.padLeft + (index * span) / (daysList.length - 1));\n const seriesValues = models.map(model => daysList.map(day => summary.byDay\n .filter(row => row.day === day && `${row.provider}/${row.model}` === model)\n .reduce((sum, row) => sum + total(row), 0)));\n // Scaled to the highest single value, not to a stacked total: these are\n // separate curves, so each one has to be readable on its own.\n const max = Math.max(1, ...seriesValues.flat());\n const y = (value) => CURVE.height - CURVE.padBottom\n - (value / max) * (CURVE.height - CURVE.padTop - CURVE.padBottom);\n return {\n xs,\n y,\n max,\n daysList,\n series: models.map((model, index) => ({\n model,\n color: PALETTE[index % PALETTE.length],\n points: linePoints(seriesValues[index], xs, y),\n markers: seriesValues[index].map((value, position) => ({ x: xs[position], y: y(value) })),\n })),\n };\n }, [summary]);\n const pie = useMemo(() => {\n if (summary === undefined)\n return undefined;\n const rows = [...summary.totals];\n const sum = rows.reduce((carry, row) => carry + total(row), 0);\n if (sum === 0)\n return undefined;\n let angle = -Math.PI / 2;\n return {\n sum,\n slices: rows.map((row, index) => {\n const share = total(row) / sum;\n const start = angle;\n const end = angle + share * Math.PI * 2;\n angle = end;\n return { row, color: PALETTE[index % PALETTE.length], share, path: ringPath(start, end) };\n }),\n };\n }, [summary]);\n /** The window's headline numbers: what the two charts are shares and shapes of. */\n const headline = useMemo(() => {\n if (summary === undefined)\n return undefined;\n const sums = summary.totals.reduce((carry, row) => ({\n uncachedInputTokens: carry.uncachedInputTokens + row.uncachedInputTokens,\n outputTokens: carry.outputTokens + row.outputTokens,\n cacheReadTokens: carry.cacheReadTokens + row.cacheReadTokens,\n cacheWriteTokens: carry.cacheWriteTokens + row.cacheWriteTokens,\n turns: carry.turns + row.turns,\n }), { uncachedInputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, turns: 0 });\n const billed = sums.uncachedInputTokens + sums.cacheReadTokens;\n return {\n ...sums,\n total: total(sums),\n // A hit rate is the one number that says whether the cache is working;\n // it is read over the input side only, which is where a cache read lands.\n hitRate: billed === 0 ? undefined : sums.cacheReadTokens / billed,\n };\n }, [summary]);\n /** The calendar grid: a fixed rectangle of weeks, newest day at the bottom right. */\n const heat = useMemo(() => {\n if (summary === undefined || summary.heat.length === 0)\n return undefined;\n const columns = heatmapColumns(summary.heat, HEAT.weeks, summary.today);\n if (columns.length === 0)\n return undefined;\n return {\n columns,\n truncated: heatmapTruncated(summary.heat, HEAT.weeks, summary.today),\n busiest: Math.max(...summary.heat.map(day => day.total)),\n };\n }, [summary]);\n return (_jsxs(\"section\", { style: { display: 'flex', flexDirection: 'column', gap: 20, padding: '4px 2px' }, children: [_jsxs(\"header\", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(\"h2\", { style: { margin: 0, fontSize: 16 }, children: t('title') }), _jsx(\"p\", { style: { margin: 0, opacity: 0.7, lineHeight: 1.6 }, children: t('subtitle') })] }), heat !== undefined && (_jsxs(\"figure\", { style: { margin: 0, display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsx(\"figcaption\", { style: { opacity: 0.8 }, children: t('chart.heatmap') }), _jsx(\"div\", { style: { display: 'flex', gap: HEAT.gap, overflowX: 'auto' }, children: heat.columns.map((column, index) => (_jsx(\"div\", { style: { display: 'grid', gridTemplateRows: `repeat(7, ${String(HEAT.cell)}px)`, gap: HEAT.gap }, children: column.map((cell, slot) => cell === undefined\n ? _jsx(\"span\", { style: { width: HEAT.cell, height: HEAT.cell, borderRadius: 3, background: 'currentColor', opacity: 0.06 } }, `empty-${String(slot)}`)\n : (_jsx(\"span\", { title: t('chart.heatmapDay', {\n day: cell.day,\n total: counts.format(cell.total),\n turns: counts.format(cell.turns),\n }), style: {\n width: HEAT.cell, height: HEAT.cell, borderRadius: 3,\n background: cell.level === 0 ? 'currentColor' : HEAT_SHADES[cell.level - 1],\n opacity: cell.level === 0 ? 0.06 : 0.35 + (cell.level / HEAT_LEVELS) * 0.65,\n } }, cell.day))) }, `column-${String(index)}`))) }), _jsxs(\"div\", { style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, opacity: 0.7 }, children: [_jsx(\"span\", { children: t('chart.heatmapLess') }), [0, 1, 2, 3, 4].map(level => (_jsx(\"span\", { style: {\n width: HEAT.cell, height: HEAT.cell, borderRadius: 3,\n background: level === 0 ? 'currentColor' : HEAT_SHADES[level - 1],\n opacity: level === 0 ? 0.06 : 0.35 + (level / HEAT_LEVELS) * 0.65,\n } }, String(level)))), _jsx(\"span\", { children: t('chart.heatmapMore') }), _jsx(\"span\", { style: { marginLeft: 6, opacity: 0.8 }, children: t('chart.heatmapPeak', { total: counts.format(heat.busiest) }) }), heat.truncated ? _jsxs(\"span\", { style: { opacity: 0.8 }, children: [\"\\u00B7 \", t('chart.heatmapTruncated')] }) : null] })] })), _jsxs(\"div\", { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }, children: [_jsx(\"span\", { style: { opacity: 0.7 }, children: t('range.label') }), RANGES.map(range => (_jsx(\"button\", { type: \"button\", onClick: () => { setDays(range.days); }, \n /* Selection is stated with weight, edge and emphasis rather than an\n inverted fill: an inverted fill needs a surface colour that reads\n against it, and inventing one goes dark-on-dark in a theme this\n page does not own. */\n style: {\n padding: '4px 12px',\n borderRadius: 999,\n cursor: 'pointer',\n border: days === range.days ? '1.5px solid currentColor' : '1px solid currentColor',\n background: 'transparent',\n color: 'inherit',\n fontWeight: days === range.days ? 600 : 400,\n opacity: days === range.days ? 1 : 0.55,\n }, children: t(range.key) }, range.key))), _jsx(\"button\", { type: \"button\", disabled: busy, onClick: () => { void ask('scan', days); }, style: { marginLeft: 'auto', padding: '4px 14px', borderRadius: 6, cursor: busy ? 'default' : 'pointer' }, children: busy ? t('rebuild.busy') : t('rebuild') })] }), summary?.scannedAt != null && (_jsxs(\"div\", { style: { opacity: 0.6, fontSize: 12 }, children: [t('state.freshness', { day: summary.scannedAt }), note === undefined ? '' : ` · ${note}`] })), skipped !== undefined && (_jsxs(\"div\", { style: {\n fontSize: 12, lineHeight: 1.6, padding: '6px 10px', borderRadius: 8,\n color: 'var(--dsw-alias-state-warning-primary, #b45309)',\n background: 'var(--dsw-alias-interactive-bg-hover-warning, rgba(245,158,11,.12))',\n }, title: skipped.reason, children: [t('state.skipped', { count: counts.format(skipped.count) }), skipped.reason === undefined ? null : (_jsxs(\"span\", { style: { opacity: 0.7 }, children: [\" \\u00B7 \", shorten(skipped.reason, 120)] }))] })), error !== undefined && (_jsx(\"div\", { style: { color: 'var(--dsh-danger, #c0392b)', fontSize: 13 }, children: t('state.error', { reason: error }) })), summary === undefined && error === undefined && _jsx(\"div\", { style: { opacity: 0.6 }, children: t('state.loading') }), headline !== undefined && headline.turns > 0 && (_jsx(\"div\", { style: { display: 'flex', gap: 18, flexWrap: 'wrap' }, children: [\n ['summary.total', counts.format(headline.total)],\n ['summary.uncached', counts.format(headline.uncachedInputTokens)],\n ['summary.output', counts.format(headline.outputTokens)],\n ['summary.cacheRead', counts.format(headline.cacheReadTokens)],\n ['summary.turns', counts.format(headline.turns)],\n ...headline.hitRate === undefined\n ? []\n : [['summary.hitRate', `${(headline.hitRate * 100).toFixed(1)}%`]],\n ].map(([key, value]) => (_jsxs(\"div\", { style: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(\"span\", { style: { fontSize: 11, opacity: 0.6 }, children: t(key) }), _jsx(\"span\", { style: { fontSize: 18, fontVariantNumeric: 'tabular-nums' }, children: value })] }, key))) })), summary !== undefined && curve === undefined && (_jsxs(\"div\", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx(\"strong\", { children: t('state.empty.title') }), _jsx(\"span\", { style: { opacity: 0.7 }, children: t('state.empty.body') })] })), curve !== undefined && (_jsxs(\"figure\", { style: { margin: 0, display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsx(\"figcaption\", { style: { opacity: 0.8 }, children: t('chart.curve') }), _jsxs(\"svg\", { viewBox: `0 0 ${String(CURVE.width)} ${String(CURVE.height)}`, style: { width: '100%', height: 'auto' }, role: \"img\", children: [[0, 0.5, 1].map(fraction => {\n const value = curve.max * fraction;\n return (_jsxs(\"g\", { children: [_jsx(\"line\", { x1: CURVE.padLeft, x2: CURVE.width - CURVE.padRight, y1: curve.y(value), y2: curve.y(value), stroke: \"currentColor\", strokeOpacity: 0.15, strokeDasharray: fraction === 0 ? undefined : '3 3' }), _jsx(\"text\", { x: CURVE.padLeft - 8, y: curve.y(value) + 4, textAnchor: \"end\", fontSize: 10, fill: \"currentColor\", fillOpacity: 0.6, children: counts.format(value) })] }, fraction));\n }), curve.series.map(series => (_jsxs(\"g\", { children: [_jsx(\"polyline\", { points: series.points, fill: \"none\", stroke: series.color, strokeWidth: 2, strokeLinejoin: \"round\" }), series.markers.map((marker, position) => (_jsx(\"circle\", { cx: marker.x, cy: marker.y, r: 3, fill: series.color }, `${series.model}-${String(position)}`)))] }, series.model))), curve.daysList.map((day, index) => (index % Math.ceil(curve.daysList.length / 8) === 0\n ? (_jsx(\"text\", { x: curve.xs[index], y: CURVE.height - 6, textAnchor: \"middle\", fontSize: 10, fill: \"currentColor\", fillOpacity: 0.6, children: day.slice(5) }, day))\n : null))] }), _jsx(Legend, { series: curve.series })] })), pie !== undefined && (_jsxs(\"figure\", { style: { margin: 0, display: 'flex', gap: 20, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx(\"figcaption\", { style: { opacity: 0.8, alignSelf: 'flex-start' }, children: t('chart.pie') }), _jsxs(\"svg\", { viewBox: `0 0 ${String(PIE.size)} ${String(PIE.size)}`, style: { width: PIE.size, height: PIE.size }, role: \"img\", children: [pie.slices.map(slice => (_jsx(\"path\", { d: slice.path, fill: slice.color, fillOpacity: 0.85, fillRule: \"evenodd\" }, `${slice.row.provider}/${slice.row.model}`))), _jsx(\"text\", { x: PIE.size / 2, y: PIE.size / 2 - 2, textAnchor: \"middle\", fontSize: 16, fill: \"currentColor\", children: counts.format(pie.sum) }), _jsx(\"text\", { x: PIE.size / 2, y: PIE.size / 2 + 14, textAnchor: \"middle\", fontSize: 10, fill: \"currentColor\", fillOpacity: 0.6, children: \"tok\" })] }), _jsxs(\"table\", { style: { borderCollapse: 'collapse', fontSize: 13 }, children: [_jsx(\"thead\", { children: _jsxs(\"tr\", { style: { opacity: 0.7 }, children: [_jsx(\"th\", { style: cellStyle('left'), children: t('table.model') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.total') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.turns') })] }) }), _jsx(\"tbody\", { children: pie.slices.map(slice => (_jsxs(\"tr\", { children: [_jsxs(\"td\", { style: cellStyle('left'), children: [_jsx(\"span\", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: slice.color, marginRight: 6 } }), slice.row.model, _jsx(\"span\", { style: { opacity: 0.5, marginLeft: 6 }, children: slice.row.provider })] }), _jsxs(\"td\", { style: cellStyle('right'), children: [counts.format(total(slice.row)), \" \", _jsxs(\"span\", { style: { opacity: 0.5 }, children: [\"(\", (slice.share * 100).toFixed(1), \"%)\"] })] }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(slice.row.turns) })] }, `${slice.row.provider}/${slice.row.model}`))) })] })] })), pie !== undefined && (_jsxs(\"details\", { children: [_jsx(\"summary\", { style: { cursor: 'pointer', opacity: 0.8 }, children: t('table.title') }), _jsxs(\"table\", { style: { borderCollapse: 'collapse', fontSize: 13, marginTop: 10, width: '100%' }, children: [_jsx(\"thead\", { children: _jsxs(\"tr\", { style: { opacity: 0.7 }, children: [_jsx(\"th\", { style: cellStyle('left'), children: t('table.model') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.input') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.output') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.cacheRead') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.cacheWrite') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.turns') }), _jsx(\"th\", { style: cellStyle('right'), children: t('table.total') })] }) }), _jsx(\"tbody\", { children: summary?.totals.map((row, index) => (_jsxs(\"tr\", { children: [_jsxs(\"td\", { style: cellStyle('left'), children: [_jsx(\"span\", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: PALETTE[index % PALETTE.length], marginRight: 6 } }), row.model, _jsx(\"span\", { style: { opacity: 0.5, marginLeft: 6 }, children: row.provider })] }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.uncachedInputTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.outputTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.cacheReadTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.cacheWriteTokens) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(row.turns) }), _jsx(\"td\", { style: cellStyle('right'), children: counts.format(total(row)) })] }, `${row.provider}/${row.model}`))) })] })] }))] }));\n}\n/** The curve's colour key, in the order the lines are drawn. */\nfunction Legend({ series }) {\n return (_jsx(\"div\", { style: { display: 'flex', gap: 14, flexWrap: 'wrap', fontSize: 12 }, children: series.map(entry => (_jsxs(\"span\", { style: { display: 'inline-flex', alignItems: 'center', gap: 5 }, children: [_jsx(\"span\", { style: { width: 8, height: 8, borderRadius: 2, background: entry.color } }), entry.model] }, entry.model))) }));\n}\n/** One table cell's alignment and padding, so the two tables match. */\nfunction cellStyle(align) {\n return { textAlign: align, padding: '3px 10px 3px 0' };\n}\n/**\n * Shorten a host error for a one-line note.\n *\n * A refusal names the session, the reason and the log path — useful, and far too\n * long to sit in a settings line; the full text stays in the note's `title`.\n */\nfunction shorten(text, limit) {\n return text.length <= limit ? text : `${text.slice(0, limit)}…`;\n}\n//# sourceMappingURL=page.js.map","/**\n * The usage entry's own icon in the settings navigation.\n *\n * dsh's settings shell picks a nav icon from a map of the section ids IT ships\n * (`account`, `models`, `plugins`, …) and draws a generic gear for anything\n * else, and rc.2's `settings.section` seat carries no icon of its own — so a\n * contributed section cannot ask for one and ours read as \"some settings\".\n *\n * Hence the one DOM patch in this package: find our own row by the label we\n * registered, and put a gauge where the gear was. It is the same technique\n * dshell's sidebar section uses (and the artwork is copied verbatim from\n * `@deepseek-ai/dsh-client-ui-primitives`, medium weight, 16 viewBox, 1.3px\n * stroke) because a surface mounted into a node dsh owns cannot render React\n * components into it.\n *\n * The row is re-created whenever the dialog opens, so a MutationObserver\n * watches for it; the patch is idempotent (it re-checks the label and a marker\n * attribute) and only touches the svg node React will not write to again.\n * Delete this file the day `settings.section` accepts an icon.\n */\n/**\n * The gauge artwork, verbatim from the primitives package\n * (`IconGaugeOutlineMedium`): the same three paths, in the same order.\n */\nconst GAUGE_PATHS = [\n { d: 'M3.4041 13.096C2.49514 12.187 1.87614 11.0288 1.62537 9.76798C1.37459 8.50716 1.50331 7.20028 1.99525 6.01261C2.48719 4.82494 3.32025 3.80981 4.3891 3.09557C5.45795 2.38134 6.71458 2.00008 8.0001 2C9.28563 2.00008 10.5423 2.38134 11.6111 3.09557C12.68 3.80981 13.513 4.82494 14.005 6.01261C14.4969 7.20028 14.6256 8.50716 14.3748 9.76798C14.1241 11.0288 13.5051 12.187 12.5961 13.096', stroke: true },\n { d: 'M8 8.49994L11.6114 4.88855', stroke: true },\n { d: 'M8 9.75C8.69036 9.75 9.25 9.19036 9.25 8.5C9.25 7.80964 8.69036 7.25 8 7.25C7.30964 7.25 6.75 7.80964 6.75 8.5C6.75 9.19036 7.30964 9.75 8 9.75Z', stroke: false },\n];\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n/** The marker that says a row already carries our artwork. */\nconst MARKER = 'data-dshell-usage-icon';\n/** One gauge, sized and coloured like the icons beside it. */\nfunction gauge(size, className) {\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('width', String(size));\n svg.setAttribute('height', String(size));\n svg.setAttribute('viewBox', '0 0 16 16');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke-width', '1.3');\n svg.setAttribute('aria-hidden', 'true');\n if (className !== null)\n svg.setAttribute('class', className);\n for (const spec of GAUGE_PATHS) {\n const path = document.createElementNS(SVG_NS, 'path');\n path.setAttribute('d', spec.d);\n if (spec.stroke)\n path.setAttribute('stroke', 'currentColor');\n else\n path.setAttribute('fill', 'currentColor');\n svg.append(path);\n }\n return svg;\n}\n/**\n * Keep the settings navigation's usage row drawn with the gauge.\n *\n * @param label - the section label as the shell renders it, in the live locale.\n * @returns the disposer that stops watching.\n */\nexport function watchUsageNavIcon(label) {\n if (typeof document === 'undefined')\n return () => { };\n const patch = () => {\n for (const dialog of document.querySelectorAll('[role=\"dialog\"]')) {\n for (const row of dialog.querySelectorAll('nav button')) {\n const text = row.textContent?.trim() ?? '';\n if (text !== label())\n continue;\n const svg = row.querySelector('svg');\n if (svg === null || svg.getAttribute(MARKER) !== null)\n continue;\n // React owns both children by position and neither is re-rendered while\n // the dialog lives, so the replacement is what the reader sees; the\n // pristine node would only come back with a full re-mount, which the\n // observer catches.\n const replacement = gauge(Number(svg.getAttribute('width') ?? 16) || 16, svg.getAttribute('class'));\n replacement.setAttribute(MARKER, '');\n svg.replaceWith(replacement);\n }\n }\n };\n patch();\n const observer = new MutationObserver(() => { patch(); });\n observer.observe(document.body, { childList: true, subtree: true });\n return () => { observer.disconnect(); };\n}\n//# sourceMappingURL=nav-icon.js.map","/**\n * The usage page's client face: the locale dictionaries and the Settings\n * navigation row that mounts the page.\n *\n * `settings.section` is the seat dsh's own Plugins page uses, so this page is a\n * peer of it rather than a tab inside it — the charts want the full settings\n * column, and a reader looking for \"what did this cost\" looks in the\n * navigation, not under a plugin list.\n */\nimport { en, zh } from './locales.js';\nimport { UsageSection } from './page.js';\nimport { watchUsageNavIcon } from './nav-icon.js';\n/** The locale namespace, matching the key `locales.ts` declares. */\nconst NS = 'dshellUsage';\nexport const name = '@nexus-aethra/dshell-usage/client';\nexport const inject = ['slots', 'locale'];\nexport function apply(ctx) {\n const t = ctx.locale.bind(NS);\n // Registered through an effect so a composition that unloads this plugin\n // takes its copy with it.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dshell-usage: dictionaries');\n // Ordered after the Plugins page (15) so the navigation reads\n // sessions → plugins → usage, and named with its own id so it is added beside\n // the shipped entries rather than replacing one.\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'usage',\n order: 40,\n label: () => t('nav'),\n locale: NS,\n }, UsageSection));\n // The row's own icon, because this seat carries none: dsh draws a generic\n // gear for any section id it does not ship (see nav-icon.ts).\n ctx.effect(() => watchUsageNavIcon(() => t('nav')), 'dshell-usage: navigation icon');\n}\n//# sourceMappingURL=index.js.map"],"x_google_ignoreList":[1,2],"mappings":"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"}
|
package/lib/index-store.js
CHANGED
|
@@ -69,27 +69,27 @@ export class UsageStore {
|
|
|
69
69
|
// drop the columns it does not know, so it stops instead.
|
|
70
70
|
throw new Error(`dshell usage: index at ${this.path} has layout ${String(version)}, this build writes ${String(LAYOUT)}`);
|
|
71
71
|
}
|
|
72
|
-
this.#db.exec(`
|
|
73
|
-
CREATE TABLE usage_bucket (
|
|
74
|
-
day TEXT NOT NULL,
|
|
75
|
-
provider TEXT NOT NULL,
|
|
76
|
-
model TEXT NOT NULL,
|
|
77
|
-
uncached_input INTEGER NOT NULL DEFAULT 0,
|
|
78
|
-
output INTEGER NOT NULL DEFAULT 0,
|
|
79
|
-
cache_read INTEGER NOT NULL DEFAULT 0,
|
|
80
|
-
cache_write INTEGER NOT NULL DEFAULT 0,
|
|
81
|
-
turns INTEGER NOT NULL DEFAULT 0,
|
|
82
|
-
PRIMARY KEY (day, provider, model)
|
|
83
|
-
);
|
|
84
|
-
CREATE TABLE usage_cursor (
|
|
85
|
-
session_id TEXT NOT NULL PRIMARY KEY,
|
|
86
|
-
seq INTEGER NOT NULL
|
|
87
|
-
);
|
|
88
|
-
CREATE TABLE usage_meta (
|
|
89
|
-
key TEXT NOT NULL PRIMARY KEY,
|
|
90
|
-
value TEXT NOT NULL
|
|
91
|
-
);
|
|
92
|
-
PRAGMA user_version = ${String(LAYOUT)};
|
|
72
|
+
this.#db.exec(`
|
|
73
|
+
CREATE TABLE usage_bucket (
|
|
74
|
+
day TEXT NOT NULL,
|
|
75
|
+
provider TEXT NOT NULL,
|
|
76
|
+
model TEXT NOT NULL,
|
|
77
|
+
uncached_input INTEGER NOT NULL DEFAULT 0,
|
|
78
|
+
output INTEGER NOT NULL DEFAULT 0,
|
|
79
|
+
cache_read INTEGER NOT NULL DEFAULT 0,
|
|
80
|
+
cache_write INTEGER NOT NULL DEFAULT 0,
|
|
81
|
+
turns INTEGER NOT NULL DEFAULT 0,
|
|
82
|
+
PRIMARY KEY (day, provider, model)
|
|
83
|
+
);
|
|
84
|
+
CREATE TABLE usage_cursor (
|
|
85
|
+
session_id TEXT NOT NULL PRIMARY KEY,
|
|
86
|
+
seq INTEGER NOT NULL
|
|
87
|
+
);
|
|
88
|
+
CREATE TABLE usage_meta (
|
|
89
|
+
key TEXT NOT NULL PRIMARY KEY,
|
|
90
|
+
value TEXT NOT NULL
|
|
91
|
+
);
|
|
92
|
+
PRAGMA user_version = ${String(LAYOUT)};
|
|
93
93
|
`);
|
|
94
94
|
}
|
|
95
95
|
/** The highest seq already counted for one session, or undefined. */
|
|
@@ -111,23 +111,23 @@ export class UsageStore {
|
|
|
111
111
|
applyBatch(batch) {
|
|
112
112
|
if (batch.rows.length === 0 && batch.cursors.length === 0)
|
|
113
113
|
return;
|
|
114
|
-
const bucket = this.#db.prepare(`
|
|
115
|
-
INSERT INTO usage_bucket (day, provider, model, uncached_input, output, cache_read, cache_write, turns)
|
|
116
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
117
|
-
ON CONFLICT (day, provider, model) DO UPDATE SET
|
|
118
|
-
uncached_input = uncached_input + excluded.uncached_input,
|
|
119
|
-
output = output + excluded.output,
|
|
120
|
-
cache_read = cache_read + excluded.cache_read,
|
|
121
|
-
cache_write = cache_write + excluded.cache_write,
|
|
122
|
-
turns = turns + excluded.turns
|
|
114
|
+
const bucket = this.#db.prepare(`
|
|
115
|
+
INSERT INTO usage_bucket (day, provider, model, uncached_input, output, cache_read, cache_write, turns)
|
|
116
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
117
|
+
ON CONFLICT (day, provider, model) DO UPDATE SET
|
|
118
|
+
uncached_input = uncached_input + excluded.uncached_input,
|
|
119
|
+
output = output + excluded.output,
|
|
120
|
+
cache_read = cache_read + excluded.cache_read,
|
|
121
|
+
cache_write = cache_write + excluded.cache_write,
|
|
122
|
+
turns = turns + excluded.turns
|
|
123
123
|
`);
|
|
124
|
-
const cursor = this.#db.prepare(`
|
|
125
|
-
INSERT INTO usage_cursor (session_id, seq) VALUES (?, ?)
|
|
126
|
-
ON CONFLICT (session_id) DO UPDATE SET seq = excluded.seq
|
|
124
|
+
const cursor = this.#db.prepare(`
|
|
125
|
+
INSERT INTO usage_cursor (session_id, seq) VALUES (?, ?)
|
|
126
|
+
ON CONFLICT (session_id) DO UPDATE SET seq = excluded.seq
|
|
127
127
|
`);
|
|
128
|
-
const meta = this.#db.prepare(`
|
|
129
|
-
INSERT INTO usage_meta (key, value) VALUES ('scannedAt', ?)
|
|
130
|
-
ON CONFLICT (key) DO UPDATE SET value = excluded.value
|
|
128
|
+
const meta = this.#db.prepare(`
|
|
129
|
+
INSERT INTO usage_meta (key, value) VALUES ('scannedAt', ?)
|
|
130
|
+
ON CONFLICT (key) DO UPDATE SET value = excluded.value
|
|
131
131
|
`);
|
|
132
132
|
this.#db.exec('BEGIN');
|
|
133
133
|
try {
|
|
@@ -155,10 +155,10 @@ export class UsageStore {
|
|
|
155
155
|
const bound = days === null ? undefined : localDay(new Date(Date.now() - days * 86_400_000));
|
|
156
156
|
const where = bound === undefined ? '' : 'WHERE day >= ?';
|
|
157
157
|
const args = bound === undefined ? [] : [bound];
|
|
158
|
-
const dayRows = this.#db.prepare(`
|
|
159
|
-
SELECT day, provider, model, uncached_input, output, cache_read, cache_write, turns
|
|
160
|
-
FROM usage_bucket ${where}
|
|
161
|
-
ORDER BY day, provider, model
|
|
158
|
+
const dayRows = this.#db.prepare(`
|
|
159
|
+
SELECT day, provider, model, uncached_input, output, cache_read, cache_write, turns
|
|
160
|
+
FROM usage_bucket ${where}
|
|
161
|
+
ORDER BY day, provider, model
|
|
162
162
|
`).all(...args);
|
|
163
163
|
const byDay = dayRows.map(row => ({
|
|
164
164
|
day: String(row.day),
|
|
@@ -189,9 +189,9 @@ export class UsageStore {
|
|
|
189
189
|
// rhythm question ("which days do I burn tokens on") is not the same
|
|
190
190
|
// question as "what did this week cost", and re-shaping the calendar every
|
|
191
191
|
// time the reader moves the range would answer neither.
|
|
192
|
-
const heat = this.#db.prepare(`
|
|
193
|
-
SELECT day, sum(uncached_input + output + cache_read + cache_write) AS total, sum(turns) AS turns
|
|
194
|
-
FROM usage_bucket GROUP BY day ORDER BY day
|
|
192
|
+
const heat = this.#db.prepare(`
|
|
193
|
+
SELECT day, sum(uncached_input + output + cache_read + cache_write) AS total, sum(turns) AS turns
|
|
194
|
+
FROM usage_bucket GROUP BY day ORDER BY day
|
|
195
195
|
`).all().map(row => ({
|
|
196
196
|
day: String(row.day),
|
|
197
197
|
total: Number(row.total),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexus-aethra/dshell-usage",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14-alpha.10",
|
|
4
4
|
"description": "dshell usage: a durable per-model token index over every session's reported usage, and the settings page that charts it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,30 +34,30 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"zod": "4.4.3",
|
|
37
|
-
"@nexus-aethra/dshell-std": "^0.1.
|
|
37
|
+
"@nexus-aethra/dshell-std": "^0.1.14-alpha.10"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
41
|
-
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
42
|
-
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
43
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
44
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
45
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
46
|
-
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
47
|
-
"@deepseek-ai/dsh-session": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
48
|
-
"@deepseek-ai/dsh-session-query": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
41
|
+
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
42
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
43
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-session-query": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
49
49
|
"@deepseek-ai/schemastery": "^3.18.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
53
|
-
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
54
|
-
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
55
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
56
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
57
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
58
|
-
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
59
|
-
"@deepseek-ai/dsh-session": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
60
|
-
"@deepseek-ai/dsh-session-query": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-session-query": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
|
|
61
61
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
62
62
|
"@types/node": "^24",
|
|
63
63
|
"@types/react": "~18.3.1",
|