@nexus-aethra/dshell-usage 0.1.6

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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/lib/client/heatmap.js +96 -0
  3. package/lib/client/heatmap.js.map +1 -0
  4. package/lib/client/index.js +36 -0
  5. package/lib/client/index.js.map +1 -0
  6. package/lib/client/locales.js +95 -0
  7. package/lib/client/locales.js.map +1 -0
  8. package/lib/client/nav-icon.js +88 -0
  9. package/lib/client/nav-icon.js.map +1 -0
  10. package/lib/client/page.js +289 -0
  11. package/lib/client/page.js.map +1 -0
  12. package/lib/client.js +1146 -0
  13. package/lib/client.js.map +1 -0
  14. package/lib/index-store.js +250 -0
  15. package/lib/index-store.js.map +1 -0
  16. package/lib/index.js +41 -0
  17. package/lib/index.js.map +1 -0
  18. package/lib/protocol.js +9 -0
  19. package/lib/protocol.js.map +1 -0
  20. package/lib/route.js +63 -0
  21. package/lib/route.js.map +1 -0
  22. package/lib/scan.js +187 -0
  23. package/lib/scan.js.map +1 -0
  24. package/lib/types/client/heatmap.d.ts +69 -0
  25. package/lib/types/client/heatmap.d.ts.map +1 -0
  26. package/lib/types/client/index.d.ts +14 -0
  27. package/lib/types/client/index.d.ts.map +1 -0
  28. package/lib/types/client/locales.d.ts +62 -0
  29. package/lib/types/client/locales.d.ts.map +1 -0
  30. package/lib/types/client/nav-icon.d.ts +28 -0
  31. package/lib/types/client/nav-icon.d.ts.map +1 -0
  32. package/lib/types/client/page.d.ts +18 -0
  33. package/lib/types/client/page.d.ts.map +1 -0
  34. package/lib/types/index-store.d.ts +87 -0
  35. package/lib/types/index-store.d.ts.map +1 -0
  36. package/lib/types/index.d.ts +19 -0
  37. package/lib/types/index.d.ts.map +1 -0
  38. package/lib/types/protocol.d.ts +115 -0
  39. package/lib/types/protocol.d.ts.map +1 -0
  40. package/lib/types/route.d.ts +19 -0
  41. package/lib/types/route.d.ts.map +1 -0
  42. package/lib/types/scan.d.ts +94 -0
  43. package/lib/types/scan.d.ts.map +1 -0
  44. package/package.json +73 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The usage route: one exact `/api` endpoint behind dsh's existing trust and
3
+ * authentication fence, mirroring the pipe and files routes' shape.
4
+ *
5
+ * Both halves of the feature go through it. Reading is the common case; the
6
+ * scan is offered here too because it is the host that owns the session events
7
+ * and the index, so the browser asks for one rather than doing one.
8
+ */
9
+ import type { ConnectionFetchRoute } from '@deepseek-ai/dsh-client-connection';
10
+ import type { UsageStore } from './index-store.js';
11
+ import type { UsageScanner } from './scan.js';
12
+ /** What the route needs from the plugin that owns it. */
13
+ export interface UsageRouteDeps {
14
+ readonly store: UsageStore;
15
+ readonly scanner: UsageScanner;
16
+ }
17
+ /** Bind the route to the index and the scanner. */
18
+ export declare function createUsageRoute(deps: UsageRouteDeps): ConnectionFetchRoute;
19
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/route.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAG9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE7C,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAC/B;AAgBD,mDAAmD;AACnD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,oBAAoB,CAsC3E"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The scan: walk every session's durable log, count the token usage each model
3
+ * turn reported, and add it to the index.
4
+ *
5
+ * Where the numbers come from. dsh attaches provider-reported accounting to the
6
+ * `assistant/message` event itself — `data.usage` — and the same event's
7
+ * `data.message.source` names the route that produced it. Usage and route
8
+ * therefore travel together on one event and nothing has to be paired up or
9
+ * inferred, which is what makes a scan over history as reliable as one over a
10
+ * live session.
11
+ *
12
+ * Incremental by session. The index keeps the highest seq counted for each
13
+ * session, and a session whose log has not grown is skipped after a cheap
14
+ * metadata listing rather than a full read. A session that has grown is read
15
+ * whole and filtered down to the events past its cursor, so a rescan costs one
16
+ * decode per touched session and nothing for the rest.
17
+ *
18
+ * Writes are batched, never per event and never per session. A scan accumulates
19
+ * its rows in memory and hands them to the store once, and {@link UsageScanner.schedule}
20
+ * coalesces concurrent callers behind a single flight plus a minimum interval,
21
+ * so a page opened repeatedly still produces at most one transaction per
22
+ * interval rather than one per open.
23
+ */
24
+ import type { SessionEvent } from '@deepseek-ai/dsh-session/types';
25
+ import type { UsageBuckets, UsageKey } from './protocol.js';
26
+ import { type UsageStore } from './index-store.js';
27
+ /**
28
+ * The slice of `ctx.sessionQuery` a scan uses, typed structurally so this
29
+ * package does not depend on the query engine's full surface.
30
+ */
31
+ export interface UsageSessionQuery {
32
+ listSessions: (signal?: AbortSignal) => Promise<readonly {
33
+ header: {
34
+ id: string;
35
+ };
36
+ }[]>;
37
+ listEvents: (sessionId: string) => Promise<readonly {
38
+ seq: number;
39
+ }[]>;
40
+ readSession: (sessionId: string) => Promise<{
41
+ events: readonly SessionEvent[];
42
+ }>;
43
+ }
44
+ /** Scan pacing. Both are deliberate: the host must not write per turn. */
45
+ export interface UsageScanPolicy {
46
+ /** Triggers inside this window share one scan. */
47
+ readonly minIntervalMs: number;
48
+ /** How long a scan will wait for a burst of triggers to settle. */
49
+ readonly debounceMs: number;
50
+ }
51
+ /** Defaults: a page can be reopened freely without a second disk write. */
52
+ export declare const DEFAULT_SCAN_POLICY: UsageScanPolicy;
53
+ /** One counted turn, before it is folded into a bucket. */
54
+ interface CountedTurn extends UsageBuckets {
55
+ readonly day: string;
56
+ readonly provider: string;
57
+ readonly model: string;
58
+ }
59
+ /** The route and usage one `assistant/message` carries, or undefined. */
60
+ export declare function usageOf(event: SessionEvent): CountedTurn | undefined;
61
+ /** Fold counted turns into one row per (day, route). */
62
+ export declare function foldTurns(turns: readonly CountedTurn[]): (UsageKey & UsageBuckets)[];
63
+ /** What one session contributed, for the caller's log line. */
64
+ export interface ScanOutcome {
65
+ readonly sessions: number;
66
+ readonly read: number;
67
+ readonly turns: number;
68
+ /** Sessions whose log could not be decoded, so their usage is not counted. */
69
+ readonly skipped: number;
70
+ /** Why the first skipped session was skipped; absent when nothing was. */
71
+ readonly skipReason: string | undefined;
72
+ readonly written: boolean;
73
+ }
74
+ /** Serialises scans and holds the write-behind window. */
75
+ export declare class UsageScanner {
76
+ #private;
77
+ private readonly store;
78
+ private readonly query;
79
+ private readonly policy;
80
+ constructor(store: UsageStore, query: UsageSessionQuery, policy?: UsageScanPolicy);
81
+ /**
82
+ * Ask for a scan, without waiting for one that is already running.
83
+ *
84
+ * A caller inside the minimum interval is answered with what the last scan
85
+ * did, so the page never blocks on a second pass it does not need.
86
+ */
87
+ schedule(): Promise<ScanOutcome>;
88
+ /** Scan now, ignoring pacing. The page's explicit rebuild action. */
89
+ rescan(): Promise<ScanOutcome>;
90
+ /** Stop a waiting trigger; the plugin's teardown path. */
91
+ dispose(): void;
92
+ }
93
+ export {};
94
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE5D;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS;QAAE,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,EAAE,CAAC,CAAA;IACtF,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAA;IACtE,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;KAAE,CAAC,CAAA;CACjF;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,EAAE,eAA4D,CAAA;AAE9F,2DAA2D;AAC3D,UAAU,WAAY,SAAQ,YAAY;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,GAAG,SAAS,CAqBpE;AAcD,wDAAwD;AACxD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,CAAC,QAAQ,GAAG,YAAY,CAAC,EAAE,CAyBpF;AAED,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED,0DAA0D;AAC1D,qBAAa,YAAY;;IAMrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,iBAAiB,EACxB,MAAM,GAAE,eAAqC;IAGhE;;;;;OAKG;IACG,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAoBtC,qEAAqE;IAC/D,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC;IAIpC,0DAA0D;IAC1D,OAAO,IAAI,IAAI;CA2DhB"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@nexus-aethra/dshell-usage",
3
+ "version": "0.1.6",
4
+ "description": "dshell usage: a durable per-model token index over every session's reported usage, and the settings page that charts it.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Nexus-Aethra/Nexus-Terminal.git",
9
+ "directory": "packages/dshell/usage"
10
+ },
11
+ "type": "module",
12
+ "main": "lib/index.js",
13
+ "types": "lib/types/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./lib/types/index.d.ts",
17
+ "default": "./lib/index.js"
18
+ },
19
+ "./client": {
20
+ "types": "./lib/types/client/index.d.ts",
21
+ "default": "./lib/client.js"
22
+ }
23
+ },
24
+ "dsh": {
25
+ "client": {
26
+ "platform": "web"
27
+ }
28
+ },
29
+ "files": [
30
+ "lib"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "zod": "4.4.3",
37
+ "@nexus-aethra/dshell-std": "^0.1.6"
38
+ },
39
+ "peerDependencies": {
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",
42
+ "@deepseek-ai/dsh-client-locale": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
43
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
44
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
45
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
46
+ "@deepseek-ai/dsh-home-paths": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
47
+ "@deepseek-ai/dsh-session": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
48
+ "@deepseek-ai/dsh-session-query": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
49
+ "@deepseek-ai/schemastery": "^3.18.2"
50
+ },
51
+ "devDependencies": {
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",
54
+ "@deepseek-ai/dsh-client-locale": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
55
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
56
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
57
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
58
+ "@deepseek-ai/dsh-home-paths": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
59
+ "@deepseek-ai/dsh-session": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
60
+ "@deepseek-ai/dsh-session-query": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
61
+ "@deepseek-ai/schemastery": "^3.18.2",
62
+ "@types/node": "^24",
63
+ "@types/react": "~18.3.1",
64
+ "react": "^18.2.0",
65
+ "tsdown": "^0.22.14",
66
+ "typescript": "^6.0.3"
67
+ },
68
+ "scripts": {
69
+ "build": "tsc -p tsconfig.json --emitDeclarationOnly false && tsdown --config-loader tsx",
70
+ "build:host": "tsc -p tsconfig.json --emitDeclarationOnly false",
71
+ "build:client": "tsdown --config-loader tsx"
72
+ }
73
+ }