@tokentop/ttop 0.2.2 → 0.4.0
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/README.md +2 -5
- package/package.json +8 -5
- package/src/agents/aggregator.ts +39 -19
- package/src/agents/costing.ts +11 -9
- package/src/agents/index.ts +7 -9
- package/src/agents/types.ts +2 -2
- package/src/cli.ts +28 -23
- package/src/config/schema.test.ts +42 -40
- package/src/config/schema.ts +26 -26
- package/src/demo/simulator.ts +161 -122
- package/src/plugins/agents/index.ts +2 -2
- package/src/plugins/auth-sources.ts +42 -38
- package/src/plugins/lifecycle.ts +42 -38
- package/src/plugins/loader.test.ts +49 -49
- package/src/plugins/loader.ts +67 -63
- package/src/plugins/notification-bus.ts +21 -16
- package/src/plugins/notifications/index.ts +2 -2
- package/src/plugins/notifications/terminal-bell.ts +36 -23
- package/src/plugins/notifications/visual-flash.ts +31 -19
- package/src/plugins/npm-installer.ts +23 -25
- package/src/plugins/plugin-context-factory.ts +10 -10
- package/src/plugins/plugin-host.ts +12 -5
- package/src/plugins/providers/anthropic.ts +64 -57
- package/src/plugins/providers/antigravity.ts +81 -69
- package/src/plugins/providers/codex.ts +42 -42
- package/src/plugins/providers/gemini.ts +106 -87
- package/src/plugins/providers/github-copilot.ts +49 -45
- package/src/plugins/providers/index.ts +10 -12
- package/src/plugins/providers/minimax.ts +50 -44
- package/src/plugins/providers/openai-api.ts +82 -62
- package/src/plugins/providers/opencode-zen.ts +46 -39
- package/src/plugins/providers/perplexity.ts +55 -46
- package/src/plugins/providers/zai.ts +68 -57
- package/src/plugins/registry.ts +80 -71
- package/src/plugins/sandbox-guard.test.ts +45 -48
- package/src/plugins/sandbox-guard.ts +20 -25
- package/src/plugins/sandbox.ts +26 -48
- package/src/plugins/themes/catppuccin-latte.ts +29 -29
- package/src/plugins/themes/catppuccin-mocha.ts +29 -29
- package/src/plugins/themes/claude-code.ts +29 -29
- package/src/plugins/themes/dracula.ts +29 -29
- package/src/plugins/themes/github-light.ts +29 -29
- package/src/plugins/themes/gruvbox-dark.ts +29 -29
- package/src/plugins/themes/gruvbox-light.ts +29 -29
- package/src/plugins/themes/index.ts +15 -15
- package/src/plugins/themes/kanagawa.ts +29 -29
- package/src/plugins/themes/nord.ts +29 -29
- package/src/plugins/themes/one-dark.ts +29 -29
- package/src/plugins/themes/opencode.ts +31 -31
- package/src/plugins/themes/rose-pine-dawn.ts +29 -29
- package/src/plugins/themes/rose-pine.ts +29 -29
- package/src/plugins/themes/solarized-light.ts +29 -29
- package/src/plugins/themes/tokyo-night.ts +29 -29
- package/src/plugins/types/agent.ts +5 -5
- package/src/plugins/types/base.ts +35 -24
- package/src/plugins/types/index.ts +9 -9
- package/src/plugins/types/notification.ts +12 -12
- package/src/plugins/types/provider.ts +12 -8
- package/src/plugins/types/theme.ts +31 -23
- package/src/plugins/update-checker.ts +6 -6
- package/src/pricing/estimator.test.ts +39 -44
- package/src/pricing/estimator.ts +7 -13
- package/src/pricing/fallback.test.ts +30 -34
- package/src/pricing/fallback.ts +54 -27
- package/src/pricing/index.ts +18 -12
- package/src/pricing/models-dev.ts +6 -6
- package/src/storage/database.ts +56 -46
- package/src/storage/db.ts +12 -12
- package/src/storage/index.ts +44 -45
- package/src/storage/migrations/index.ts +2 -2
- package/src/storage/paths.ts +13 -13
- package/src/storage/persistence-service.ts +206 -0
- package/src/storage/repos/agentSessions.ts +33 -23
- package/src/storage/repos/providerSnapshots.ts +5 -5
- package/src/storage/repos/usageEvents.ts +40 -28
- package/src/storage/retention.ts +146 -0
- package/src/storage/types.ts +5 -5
- package/src/tui/App.tsx +180 -137
- package/src/tui/components/CommandPalette.tsx +46 -34
- package/src/tui/components/DebugConsole.tsx +39 -33
- package/src/tui/components/DebugPanel.tsx +314 -183
- package/src/tui/components/FooterHints.tsx +2 -2
- package/src/tui/components/GhostProviderCard.tsx +37 -35
- package/src/tui/components/Header.tsx +68 -35
- package/src/tui/components/HelpOverlay.tsx +99 -29
- package/src/tui/components/InlineGauge.tsx +10 -7
- package/src/tui/components/InlineSparkline.tsx +17 -11
- package/src/tui/components/KpiStrip.tsx +128 -98
- package/src/tui/components/LimitGauge.tsx +96 -59
- package/src/tui/components/ModalBackdrop.tsx +4 -4
- package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
- package/src/tui/components/ProviderCard.tsx +232 -226
- package/src/tui/components/ProviderDetailPanel.tsx +133 -58
- package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
- package/src/tui/components/ProvidersList.tsx +215 -126
- package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
- package/src/tui/components/SessionsTable.tsx +320 -169
- package/src/tui/components/SettingsModal.tsx +593 -269
- package/src/tui/components/SidebarBreakdown.tsx +30 -16
- package/src/tui/components/Skeleton.tsx +24 -17
- package/src/tui/components/SmartSidebar.tsx +360 -258
- package/src/tui/components/Sparkline.tsx +64 -52
- package/src/tui/components/Spinner.tsx +3 -3
- package/src/tui/components/StatusBar.tsx +27 -27
- package/src/tui/components/ThemePicker.tsx +67 -66
- package/src/tui/components/Toast.tsx +9 -6
- package/src/tui/components/UsageGauge.tsx +10 -13
- package/src/tui/components/index.ts +5 -5
- package/src/tui/contexts/AgentSessionContext.tsx +164 -151
- package/src/tui/contexts/ConfigContext.tsx +15 -13
- package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
- package/src/tui/contexts/DemoModeContext.tsx +22 -15
- package/src/tui/contexts/DrawerContext.tsx +11 -9
- package/src/tui/contexts/InputContext.tsx +2 -2
- package/src/tui/contexts/LogContext.tsx +62 -47
- package/src/tui/contexts/PluginContext.tsx +238 -183
- package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
- package/src/tui/contexts/StorageContext.tsx +95 -149
- package/src/tui/contexts/ThemeContext.tsx +21 -16
- package/src/tui/contexts/TimeWindowContext.tsx +72 -61
- package/src/tui/contexts/ToastContext.tsx +10 -7
- package/src/tui/contexts/index.ts +9 -5
- package/src/tui/createApp.tsx +8 -12
- package/src/tui/debug/captureFrame.ts +25 -28
- package/src/tui/debug/snapshot.tsx +179 -162
- package/src/tui/driver/assertions.ts +30 -22
- package/src/tui/driver/cli.ts +231 -221
- package/src/tui/driver/coverage.ts +27 -44
- package/src/tui/driver/demo-snapshot.ts +33 -33
- package/src/tui/driver/diff.ts +26 -32
- package/src/tui/driver/driver.ts +43 -48
- package/src/tui/driver/index.ts +36 -40
- package/src/tui/driver/recorder.ts +37 -48
- package/src/tui/driver/test-workflow.ts +51 -51
- package/src/tui/driver/test.ts +25 -25
- package/src/tui/hooks/index.ts +24 -20
- package/src/tui/hooks/useAnimatedValue.ts +13 -18
- package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
- package/src/tui/hooks/useDashboardState.ts +26 -24
- package/src/tui/hooks/useEmaActivity.ts +82 -74
- package/src/tui/hooks/useEntranceAnimation.ts +11 -11
- package/src/tui/hooks/useExitAnimation.ts +7 -3
- package/src/tui/hooks/usePulse.ts +4 -4
- package/src/tui/hooks/useSafeRenderer.ts +5 -5
- package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
- package/src/tui/hooks/useValueFlash.ts +10 -14
- package/src/tui/index.tsx +29 -6
- package/src/tui/shutdown.ts +19 -0
- package/src/tui/utils/providerColor.ts +1 -1
- package/src/tui/views/Dashboard.tsx +164 -102
- package/src/tui/views/HistoricalTrendsView.tsx +579 -250
- package/src/tui/views/ProjectsView.tsx +495 -243
- package/src/tui/views/RealTimeDashboard.tsx +176 -116
- package/src/tui/views/SettingsView.tsx +225 -189
- package/src/tui/views/index.ts +2 -2
- package/src/types/opentui-test-utils.d.ts +7 -7
- package/src/utils/clipboard.ts +29 -29
- package/src/utils/currency.test.ts +53 -53
- package/src/utils/currency.ts +35 -32
- package/src/version.ts +1 -1
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import type { ThemePlugin } from
|
|
1
|
+
import type { ThemePlugin } from "../types/theme.ts";
|
|
2
2
|
|
|
3
3
|
export const tokyoNightTheme: ThemePlugin = {
|
|
4
4
|
apiVersion: 2,
|
|
5
|
-
id:
|
|
6
|
-
type:
|
|
7
|
-
name:
|
|
8
|
-
family:
|
|
9
|
-
version:
|
|
5
|
+
id: "tokyo-night",
|
|
6
|
+
type: "theme",
|
|
7
|
+
name: "Tokyo Night",
|
|
8
|
+
family: "tokyo-night",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
permissions: {},
|
|
11
11
|
|
|
12
|
-
colorScheme:
|
|
12
|
+
colorScheme: "dark",
|
|
13
13
|
|
|
14
14
|
colors: {
|
|
15
|
-
background:
|
|
16
|
-
foreground:
|
|
17
|
-
text:
|
|
18
|
-
textMuted:
|
|
19
|
-
textSubtle:
|
|
20
|
-
primary:
|
|
21
|
-
secondary:
|
|
22
|
-
accent:
|
|
23
|
-
success:
|
|
24
|
-
warning:
|
|
25
|
-
error:
|
|
26
|
-
info:
|
|
27
|
-
border:
|
|
28
|
-
borderMuted:
|
|
29
|
-
selection:
|
|
30
|
-
highlight:
|
|
31
|
-
gaugeBackground:
|
|
32
|
-
gaugeFill:
|
|
33
|
-
gaugeWarning:
|
|
34
|
-
gaugeDanger:
|
|
15
|
+
background: "#1a1b26",
|
|
16
|
+
foreground: "#24283b",
|
|
17
|
+
text: "#c0caf5",
|
|
18
|
+
textMuted: "#737aa2",
|
|
19
|
+
textSubtle: "#565f89",
|
|
20
|
+
primary: "#7aa2f7",
|
|
21
|
+
secondary: "#bb9af7",
|
|
22
|
+
accent: "#7dcfff",
|
|
23
|
+
success: "#9ece6a",
|
|
24
|
+
warning: "#e0af68",
|
|
25
|
+
error: "#f7768e",
|
|
26
|
+
info: "#7aa2f7",
|
|
27
|
+
border: "#414868",
|
|
28
|
+
borderMuted: "#292e42",
|
|
29
|
+
selection: "#33467c",
|
|
30
|
+
highlight: "#2f3549",
|
|
31
|
+
gaugeBackground: "#24283b",
|
|
32
|
+
gaugeFill: "#7aa2f7",
|
|
33
|
+
gaugeWarning: "#e0af68",
|
|
34
|
+
gaugeDanger: "#f7768e",
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
components: {
|
|
38
38
|
header: {
|
|
39
|
-
background:
|
|
39
|
+
background: "#16161e",
|
|
40
40
|
},
|
|
41
41
|
statusBar: {
|
|
42
|
-
background:
|
|
42
|
+
background: "#1f2335",
|
|
43
43
|
},
|
|
44
44
|
gauge: {
|
|
45
45
|
height: 1,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import type { BasePlugin, PluginHttpClient, PluginLogger } from
|
|
3
|
-
import type { Credentials, OAuthCredentials, PluginContext } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { BasePlugin, PluginHttpClient, PluginLogger } from "./base.ts";
|
|
3
|
+
import type { Credentials, OAuthCredentials, PluginContext } from "./provider.ts";
|
|
4
4
|
|
|
5
5
|
export const AgentCapabilitiesSchema = z.object({
|
|
6
6
|
sessionParsing: z.boolean(),
|
|
@@ -38,7 +38,7 @@ export interface AgentProviderConfig {
|
|
|
38
38
|
|
|
39
39
|
export interface SessionParseOptions {
|
|
40
40
|
sessionId?: string;
|
|
41
|
-
timePeriod?:
|
|
41
|
+
timePeriod?: "session" | "daily" | "weekly" | "monthly";
|
|
42
42
|
limit?: number;
|
|
43
43
|
/** Epoch ms — only return sessions updated after this timestamp. */
|
|
44
44
|
since?: number;
|
|
@@ -84,7 +84,7 @@ export interface ActivityUpdate {
|
|
|
84
84
|
export type ActivityCallback = (update: ActivityUpdate) => void;
|
|
85
85
|
|
|
86
86
|
export interface AgentPlugin extends BasePlugin {
|
|
87
|
-
readonly type:
|
|
87
|
+
readonly type: "agent";
|
|
88
88
|
readonly agent: AgentConfig;
|
|
89
89
|
readonly capabilities: AgentCapabilities;
|
|
90
90
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* authoritative contract for community developers; core adapts to match.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { z } from
|
|
8
|
+
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
// Identity
|
|
@@ -14,7 +14,7 @@ import { z } from 'zod';
|
|
|
14
14
|
/**
|
|
15
15
|
* Plugin type discriminator
|
|
16
16
|
*/
|
|
17
|
-
export type PluginType =
|
|
17
|
+
export type PluginType = "provider" | "agent" | "theme" | "notification";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Current API contract version.
|
|
@@ -30,23 +30,31 @@ export const CURRENT_API_VERSION = 2;
|
|
|
30
30
|
* Plugin permissions schema - defines what a plugin can access
|
|
31
31
|
*/
|
|
32
32
|
export const PluginPermissionsSchema = z.object({
|
|
33
|
-
network: z
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
network: z
|
|
34
|
+
.object({
|
|
35
|
+
enabled: z.boolean(),
|
|
36
|
+
allowedDomains: z.array(z.string()).optional(),
|
|
37
|
+
})
|
|
38
|
+
.optional(),
|
|
39
|
+
filesystem: z
|
|
40
|
+
.object({
|
|
41
|
+
read: z.boolean().optional(),
|
|
42
|
+
write: z.boolean().optional(),
|
|
43
|
+
paths: z.array(z.string()).optional(),
|
|
44
|
+
})
|
|
45
|
+
.optional(),
|
|
46
|
+
env: z
|
|
47
|
+
.object({
|
|
48
|
+
read: z.boolean().optional(),
|
|
49
|
+
vars: z.array(z.string()).optional(),
|
|
50
|
+
})
|
|
51
|
+
.optional(),
|
|
52
|
+
system: z
|
|
53
|
+
.object({
|
|
54
|
+
notifications: z.boolean().optional(),
|
|
55
|
+
clipboard: z.boolean().optional(),
|
|
56
|
+
})
|
|
57
|
+
.optional(),
|
|
50
58
|
});
|
|
51
59
|
|
|
52
60
|
export type PluginPermissions = z.infer<typeof PluginPermissionsSchema>;
|
|
@@ -95,7 +103,7 @@ export type PluginMeta = z.infer<typeof PluginMetaSchema>;
|
|
|
95
103
|
*/
|
|
96
104
|
export interface ConfigField {
|
|
97
105
|
/** Data type of the setting value. */
|
|
98
|
-
type:
|
|
106
|
+
type: "string" | "number" | "boolean" | "select";
|
|
99
107
|
/** Label shown in the settings UI. */
|
|
100
108
|
label?: string;
|
|
101
109
|
/** Help text shown below the field. */
|
|
@@ -197,7 +205,10 @@ export interface BasePlugin {
|
|
|
197
205
|
* Called when the user changes this plugin's configuration.
|
|
198
206
|
* Receive the new validated config values.
|
|
199
207
|
*/
|
|
200
|
-
onConfigChange?(
|
|
208
|
+
onConfigChange?(
|
|
209
|
+
config: Record<string, unknown>,
|
|
210
|
+
ctx: PluginLifecycleContext,
|
|
211
|
+
): Promise<void> | void;
|
|
201
212
|
}
|
|
202
213
|
|
|
203
214
|
// ---------------------------------------------------------------------------
|
|
@@ -245,7 +256,7 @@ export interface PluginLoadResult<T extends BasePlugin = BasePlugin> {
|
|
|
245
256
|
success: boolean;
|
|
246
257
|
plugin?: T;
|
|
247
258
|
error?: string;
|
|
248
|
-
source:
|
|
259
|
+
source: "builtin" | "local" | "npm";
|
|
249
260
|
}
|
|
250
261
|
|
|
251
262
|
// ---------------------------------------------------------------------------
|
|
@@ -259,9 +270,9 @@ export class PluginPermissionError extends Error {
|
|
|
259
270
|
constructor(
|
|
260
271
|
public readonly pluginId: string,
|
|
261
272
|
public readonly permission: keyof PluginPermissions,
|
|
262
|
-
message: string
|
|
273
|
+
message: string,
|
|
263
274
|
) {
|
|
264
275
|
super(`Plugin "${pluginId}" permission denied: ${message}`);
|
|
265
|
-
this.name =
|
|
276
|
+
this.name = "PluginPermissionError";
|
|
266
277
|
}
|
|
267
278
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
1
|
+
export * from "./agent.ts";
|
|
2
|
+
export * from "./base.ts";
|
|
3
|
+
export * from "./notification.ts";
|
|
4
|
+
export * from "./provider.ts";
|
|
5
|
+
export * from "./theme.ts";
|
|
6
6
|
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
9
|
-
import type {
|
|
10
|
-
import type {
|
|
7
|
+
import type { AgentPlugin } from "./agent.ts";
|
|
8
|
+
import type { NotificationPlugin } from "./notification.ts";
|
|
9
|
+
import type { ProviderPlugin } from "./provider.ts";
|
|
10
|
+
import type { ThemePlugin } from "./theme.ts";
|
|
11
11
|
|
|
12
12
|
export type AnyPlugin = ProviderPlugin | AgentPlugin | ThemePlugin | NotificationPlugin;
|
|
13
13
|
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import type { BasePlugin, PluginLogger } from
|
|
1
|
+
import type { BasePlugin, PluginLogger } from "./base.ts";
|
|
2
2
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Notification Events
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
|
|
7
|
-
export type NotificationSeverity =
|
|
7
|
+
export type NotificationSeverity = "info" | "warning" | "critical";
|
|
8
8
|
|
|
9
9
|
export type NotificationEventType =
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
10
|
+
| "budget.thresholdCrossed"
|
|
11
|
+
| "budget.limitReached"
|
|
12
|
+
| "provider.fetchFailed"
|
|
13
|
+
| "provider.limitReached"
|
|
14
|
+
| "provider.recovered"
|
|
15
|
+
| "plugin.crashed"
|
|
16
|
+
| "plugin.disabled"
|
|
17
|
+
| "app.started"
|
|
18
|
+
| "app.updated";
|
|
19
19
|
|
|
20
20
|
export interface NotificationEvent {
|
|
21
21
|
type: NotificationEventType;
|
|
@@ -41,7 +41,7 @@ export interface NotificationContext {
|
|
|
41
41
|
// ---------------------------------------------------------------------------
|
|
42
42
|
|
|
43
43
|
export interface NotificationPlugin extends BasePlugin {
|
|
44
|
-
readonly type:
|
|
44
|
+
readonly type: "notification";
|
|
45
45
|
|
|
46
46
|
initialize(ctx: NotificationContext): Promise<void>;
|
|
47
47
|
notify(ctx: NotificationContext, event: NotificationEvent): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BasePlugin, PluginHttpClient, PluginLogger } from
|
|
1
|
+
import type { BasePlugin, PluginHttpClient, PluginLogger } from "./base.ts";
|
|
2
2
|
|
|
3
3
|
export interface ProviderCapabilities {
|
|
4
4
|
usageLimits: boolean;
|
|
@@ -15,7 +15,7 @@ export interface Credentials {
|
|
|
15
15
|
apiKey?: string;
|
|
16
16
|
oauth?: OAuthCredentials;
|
|
17
17
|
groupId?: string;
|
|
18
|
-
source:
|
|
18
|
+
source: "env" | "opencode" | "external" | "config";
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export interface OAuthCredentials {
|
|
@@ -40,7 +40,7 @@ export interface RefreshedCredentials {
|
|
|
40
40
|
export interface CredentialResult {
|
|
41
41
|
ok: boolean;
|
|
42
42
|
credentials?: Credentials;
|
|
43
|
-
reason?:
|
|
43
|
+
reason?: "missing" | "expired" | "invalid" | "error";
|
|
44
44
|
message?: string;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -49,7 +49,7 @@ export interface CredentialResult {
|
|
|
49
49
|
* Used by AuthSources.opencode.getProviderEntry().
|
|
50
50
|
*/
|
|
51
51
|
export interface OpenCodeAuthEntry {
|
|
52
|
-
type:
|
|
52
|
+
type: "api" | "oauth" | "codex" | "github" | "wellknown";
|
|
53
53
|
key?: string;
|
|
54
54
|
access?: string;
|
|
55
55
|
refresh?: string;
|
|
@@ -89,7 +89,7 @@ export interface AuthSources {
|
|
|
89
89
|
|
|
90
90
|
/** Platform information for cross-platform credential path resolution. */
|
|
91
91
|
platform: {
|
|
92
|
-
os:
|
|
92
|
+
os: "darwin" | "linux" | "win32";
|
|
93
93
|
homedir: string;
|
|
94
94
|
arch: string;
|
|
95
95
|
};
|
|
@@ -197,7 +197,11 @@ export interface ProviderUsageData {
|
|
|
197
197
|
cost?: {
|
|
198
198
|
actual?: CostBreakdown;
|
|
199
199
|
estimated?: CostBreakdown;
|
|
200
|
-
|
|
200
|
+
/** Session-derived daily cost (today only). Set by enrichment hook. */
|
|
201
|
+
estimatedDaily?: CostBreakdown;
|
|
202
|
+
/** Session-derived monthly cost (MTD). Set by enrichment hook. */
|
|
203
|
+
estimatedMonthly?: CostBreakdown;
|
|
204
|
+
source: "api" | "estimated";
|
|
201
205
|
};
|
|
202
206
|
fetchedAt: number;
|
|
203
207
|
error?: string;
|
|
@@ -214,7 +218,7 @@ export interface ProviderFetchContext {
|
|
|
214
218
|
readonly config: Record<string, unknown>;
|
|
215
219
|
readonly signal: AbortSignal;
|
|
216
220
|
readonly options?: {
|
|
217
|
-
timePeriod?:
|
|
221
|
+
timePeriod?: "session" | "daily" | "weekly" | "monthly";
|
|
218
222
|
sessionId?: string;
|
|
219
223
|
};
|
|
220
224
|
}
|
|
@@ -224,7 +228,7 @@ export interface ProviderFetchContext {
|
|
|
224
228
|
// ---------------------------------------------------------------------------
|
|
225
229
|
|
|
226
230
|
export interface ProviderPlugin extends BasePlugin {
|
|
227
|
-
readonly type:
|
|
231
|
+
readonly type: "provider";
|
|
228
232
|
readonly capabilities: ProviderCapabilities;
|
|
229
233
|
readonly auth: ProviderAuth;
|
|
230
234
|
readonly pricing?: ProviderPricing;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import type { BasePlugin } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { BasePlugin } from "./base.ts";
|
|
3
3
|
|
|
4
4
|
export const ThemeColorsSchema = z.object({
|
|
5
5
|
background: z.string(),
|
|
@@ -27,34 +27,42 @@ export const ThemeColorsSchema = z.object({
|
|
|
27
27
|
export type ThemeColors = z.infer<typeof ThemeColorsSchema>;
|
|
28
28
|
|
|
29
29
|
export const ThemeComponentsSchema = z.object({
|
|
30
|
-
header: z
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
30
|
+
header: z
|
|
31
|
+
.object({
|
|
32
|
+
background: z.string().optional(),
|
|
33
|
+
foreground: z.string().optional(),
|
|
34
|
+
titleColor: z.string().optional(),
|
|
35
|
+
titleAccentColor: z.string().optional(),
|
|
36
|
+
})
|
|
37
|
+
.optional(),
|
|
38
|
+
statusBar: z
|
|
39
|
+
.object({
|
|
40
|
+
background: z.string().optional(),
|
|
41
|
+
foreground: z.string().optional(),
|
|
42
|
+
})
|
|
43
|
+
.optional(),
|
|
44
|
+
commandPalette: z
|
|
45
|
+
.object({
|
|
46
|
+
background: z.string().optional(),
|
|
47
|
+
border: z.string().optional(),
|
|
48
|
+
})
|
|
49
|
+
.optional(),
|
|
50
|
+
gauge: z
|
|
51
|
+
.object({
|
|
52
|
+
height: z.number().optional(),
|
|
53
|
+
borderRadius: z.number().optional(),
|
|
54
|
+
})
|
|
55
|
+
.optional(),
|
|
48
56
|
});
|
|
49
57
|
|
|
50
58
|
export type ThemeComponents = z.infer<typeof ThemeComponentsSchema>;
|
|
51
59
|
|
|
52
|
-
export type ColorScheme =
|
|
60
|
+
export type ColorScheme = "light" | "dark";
|
|
53
61
|
|
|
54
|
-
export type ColorSchemePreference =
|
|
62
|
+
export type ColorSchemePreference = "auto" | "light" | "dark";
|
|
55
63
|
|
|
56
64
|
export interface ThemePlugin extends BasePlugin {
|
|
57
|
-
readonly type:
|
|
65
|
+
readonly type: "theme";
|
|
58
66
|
readonly family: string;
|
|
59
67
|
readonly colorScheme: ColorScheme;
|
|
60
68
|
readonly colors: ThemeColors;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* skipped because they have no registry entry to compare against.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { PluginType } from
|
|
12
|
+
import type { PluginType } from "./types/base.ts";
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
// Types
|
|
@@ -33,7 +33,7 @@ export interface PluginUpdateInfo {
|
|
|
33
33
|
// ---------------------------------------------------------------------------
|
|
34
34
|
|
|
35
35
|
function parseVersion(v: string): [number, number, number] | null {
|
|
36
|
-
const clean = v.replace(/^v/,
|
|
36
|
+
const clean = v.replace(/^v/, "");
|
|
37
37
|
const match = clean.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
38
38
|
if (!match) return null;
|
|
39
39
|
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
@@ -60,9 +60,9 @@ function isNewer(current: string, latest: string): boolean {
|
|
|
60
60
|
* Uses the abbreviated metadata endpoint for minimal payload.
|
|
61
61
|
*/
|
|
62
62
|
async function fetchLatestVersion(packageName: string): Promise<string> {
|
|
63
|
-
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName).replace(
|
|
63
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName).replace("%40", "@")}`;
|
|
64
64
|
const res = await fetch(url, {
|
|
65
|
-
headers: { Accept:
|
|
65
|
+
headers: { Accept: "application/vnd.npm.install-v1+json" },
|
|
66
66
|
signal: AbortSignal.timeout(8_000),
|
|
67
67
|
});
|
|
68
68
|
|
|
@@ -70,8 +70,8 @@ async function fetchLatestVersion(packageName: string): Promise<string> {
|
|
|
70
70
|
throw new Error(`npm registry returned ${res.status} for ${packageName}`);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const data = (await res.json()) as {
|
|
74
|
-
const latest = data?.[
|
|
73
|
+
const data = (await res.json()) as { "dist-tags"?: { latest?: string } };
|
|
74
|
+
const latest = data?.["dist-tags"]?.latest;
|
|
75
75
|
if (!latest) {
|
|
76
76
|
throw new Error(`No dist-tags.latest found for ${packageName}`);
|
|
77
77
|
}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { describe, expect, test } from
|
|
2
|
-
import type { ModelPricing } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
formatTokenCount,
|
|
8
|
-
} from './estimator.ts';
|
|
9
|
-
|
|
10
|
-
describe('estimateCost()', () => {
|
|
11
|
-
test('calculates input and output costs for a typical coding session', () => {
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { ModelPricing } from "@/plugins/types/provider.ts";
|
|
3
|
+
import { estimateCost, estimateSessionCost, formatCost, formatTokenCount } from "./estimator.ts";
|
|
4
|
+
|
|
5
|
+
describe("estimateCost()", () => {
|
|
6
|
+
test("calculates input and output costs for a typical coding session", () => {
|
|
12
7
|
const usage = { input: 150_000, output: 50_000 };
|
|
13
8
|
const pricing: ModelPricing = { input: 3, output: 15 };
|
|
14
9
|
|
|
@@ -16,11 +11,11 @@ describe('estimateCost()', () => {
|
|
|
16
11
|
total: 1.2,
|
|
17
12
|
input: 0.45,
|
|
18
13
|
output: 0.75,
|
|
19
|
-
currency:
|
|
14
|
+
currency: "USD",
|
|
20
15
|
});
|
|
21
16
|
});
|
|
22
17
|
|
|
23
|
-
test(
|
|
18
|
+
test("includes cache read and write costs when both usage and pricing are present", () => {
|
|
24
19
|
const usage = { input: 100_000, output: 25_000, cacheRead: 80_000, cacheWrite: 40_000 };
|
|
25
20
|
const pricing: ModelPricing = {
|
|
26
21
|
input: 3,
|
|
@@ -35,11 +30,11 @@ describe('estimateCost()', () => {
|
|
|
35
30
|
output: 0.375,
|
|
36
31
|
cacheRead: 0.024,
|
|
37
32
|
cacheWrite: 0.15,
|
|
38
|
-
currency:
|
|
33
|
+
currency: "USD",
|
|
39
34
|
});
|
|
40
35
|
});
|
|
41
36
|
|
|
42
|
-
test(
|
|
37
|
+
test("does not include cache fields when cache pricing is unavailable", () => {
|
|
43
38
|
const usage = { input: 10_000, output: 20_000, cacheRead: 5_000, cacheWrite: 7_000 };
|
|
44
39
|
const pricing: ModelPricing = { input: 2, output: 8 };
|
|
45
40
|
|
|
@@ -47,11 +42,11 @@ describe('estimateCost()', () => {
|
|
|
47
42
|
total: 0.18,
|
|
48
43
|
input: 0.02,
|
|
49
44
|
output: 0.16,
|
|
50
|
-
currency:
|
|
45
|
+
currency: "USD",
|
|
51
46
|
});
|
|
52
47
|
});
|
|
53
48
|
|
|
54
|
-
test(
|
|
49
|
+
test("returns zero cost for zero token usage", () => {
|
|
55
50
|
const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
56
51
|
const pricing: ModelPricing = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
|
|
57
52
|
|
|
@@ -59,11 +54,11 @@ describe('estimateCost()', () => {
|
|
|
59
54
|
total: 0,
|
|
60
55
|
input: 0,
|
|
61
56
|
output: 0,
|
|
62
|
-
currency:
|
|
57
|
+
currency: "USD",
|
|
63
58
|
});
|
|
64
59
|
});
|
|
65
60
|
|
|
66
|
-
test(
|
|
61
|
+
test("handles very large token counts correctly", () => {
|
|
67
62
|
const usage = {
|
|
68
63
|
input: 123_456_789,
|
|
69
64
|
output: 987_654_321,
|
|
@@ -83,11 +78,11 @@ describe('estimateCost()', () => {
|
|
|
83
78
|
output: 9876.54321,
|
|
84
79
|
cacheRead: 5.555556,
|
|
85
80
|
cacheWrite: 44.444444,
|
|
86
|
-
currency:
|
|
81
|
+
currency: "USD",
|
|
87
82
|
});
|
|
88
83
|
});
|
|
89
84
|
|
|
90
|
-
test(
|
|
85
|
+
test("rounds costs to six decimal places", () => {
|
|
91
86
|
const usage = { input: 1, output: 1 };
|
|
92
87
|
const pricing: ModelPricing = { input: 1.234567, output: 2.345678 };
|
|
93
88
|
|
|
@@ -95,13 +90,13 @@ describe('estimateCost()', () => {
|
|
|
95
90
|
total: 0.000004,
|
|
96
91
|
input: 0.000001,
|
|
97
92
|
output: 0.000002,
|
|
98
|
-
currency:
|
|
93
|
+
currency: "USD",
|
|
99
94
|
});
|
|
100
95
|
});
|
|
101
96
|
});
|
|
102
97
|
|
|
103
|
-
describe(
|
|
104
|
-
test(
|
|
98
|
+
describe("estimateSessionCost()", () => {
|
|
99
|
+
test("aggregates multiple sessions before estimating cost", () => {
|
|
105
100
|
const sessions = [
|
|
106
101
|
{ input: 100_000, output: 20_000, cacheRead: 10_000, cacheWrite: 5_000 },
|
|
107
102
|
{ input: 50_000, output: 10_000, cacheRead: 5_000, cacheWrite: 10_000 },
|
|
@@ -120,22 +115,22 @@ describe('estimateSessionCost()', () => {
|
|
|
120
115
|
output: 0.525,
|
|
121
116
|
cacheRead: 0.0045,
|
|
122
117
|
cacheWrite: 0.05625,
|
|
123
|
-
currency:
|
|
118
|
+
currency: "USD",
|
|
124
119
|
});
|
|
125
120
|
});
|
|
126
121
|
|
|
127
|
-
test(
|
|
122
|
+
test("returns zero cost for an empty session list", () => {
|
|
128
123
|
const pricing: ModelPricing = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
|
|
129
124
|
|
|
130
125
|
expect(estimateSessionCost([], pricing)).toEqual({
|
|
131
126
|
total: 0,
|
|
132
127
|
input: 0,
|
|
133
128
|
output: 0,
|
|
134
|
-
currency:
|
|
129
|
+
currency: "USD",
|
|
135
130
|
});
|
|
136
131
|
});
|
|
137
132
|
|
|
138
|
-
test(
|
|
133
|
+
test("matches estimateCost for a single session", () => {
|
|
139
134
|
const session = { input: 150_000, output: 50_000, cacheRead: 80_000, cacheWrite: 40_000 };
|
|
140
135
|
const pricing: ModelPricing = {
|
|
141
136
|
input: 3,
|
|
@@ -148,34 +143,34 @@ describe('estimateSessionCost()', () => {
|
|
|
148
143
|
});
|
|
149
144
|
});
|
|
150
145
|
|
|
151
|
-
describe(
|
|
152
|
-
test(
|
|
153
|
-
expect(formatCost(0.009876)).toBe(
|
|
146
|
+
describe("formatCost()", () => {
|
|
147
|
+
test("formats USD costs below $0.01 with 4 decimal places", () => {
|
|
148
|
+
expect(formatCost(0.009876)).toBe("$0.0099");
|
|
154
149
|
});
|
|
155
150
|
|
|
156
|
-
test(
|
|
157
|
-
expect(formatCost(0.45678)).toBe(
|
|
151
|
+
test("formats USD costs below $1 with 3 decimal places", () => {
|
|
152
|
+
expect(formatCost(0.45678)).toBe("$0.457");
|
|
158
153
|
});
|
|
159
154
|
|
|
160
|
-
test(
|
|
161
|
-
expect(formatCost(12.3456)).toBe(
|
|
155
|
+
test("formats USD costs at or above $1 with 2 decimal places", () => {
|
|
156
|
+
expect(formatCost(12.3456)).toBe("$12.35");
|
|
162
157
|
});
|
|
163
158
|
|
|
164
|
-
test(
|
|
165
|
-
expect(formatCost(12.3456,
|
|
159
|
+
test("formats non-USD currency with 4 decimals and currency suffix", () => {
|
|
160
|
+
expect(formatCost(12.3456, "EUR")).toBe("12.3456 EUR");
|
|
166
161
|
});
|
|
167
162
|
});
|
|
168
163
|
|
|
169
|
-
describe(
|
|
170
|
-
test(
|
|
171
|
-
expect(formatTokenCount(999)).toBe(
|
|
164
|
+
describe("formatTokenCount()", () => {
|
|
165
|
+
test("returns raw token count for values below 1K", () => {
|
|
166
|
+
expect(formatTokenCount(999)).toBe("999");
|
|
172
167
|
});
|
|
173
168
|
|
|
174
|
-
test(
|
|
175
|
-
expect(formatTokenCount(12_500)).toBe(
|
|
169
|
+
test("formats token count in K for values below 1M", () => {
|
|
170
|
+
expect(formatTokenCount(12_500)).toBe("12.5K");
|
|
176
171
|
});
|
|
177
172
|
|
|
178
|
-
test(
|
|
179
|
-
expect(formatTokenCount(2_345_678)).toBe(
|
|
173
|
+
test("formats token count in M for values at or above 1M", () => {
|
|
174
|
+
expect(formatTokenCount(2_345_678)).toBe("2.35M");
|
|
180
175
|
});
|
|
181
176
|
});
|