@tokentop/ttop 0.4.0 → 0.5.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.
Files changed (75) hide show
  1. package/package.json +9 -4
  2. package/src/agents/aggregator.ts +8 -1
  3. package/src/agents/types.ts +3 -0
  4. package/src/cli.ts +2 -2
  5. package/src/demo/simulator.ts +1 -1
  6. package/src/plugins/agents/index.ts +2 -0
  7. package/src/plugins/auth-sources.ts +2 -3
  8. package/src/plugins/lifecycle.ts +1 -1
  9. package/src/plugins/loader.test.ts +1 -1
  10. package/src/plugins/loader.ts +15 -7
  11. package/src/plugins/notification-bus.ts +5 -2
  12. package/src/plugins/notifications/terminal-bell.ts +1 -1
  13. package/src/plugins/notifications/visual-flash.ts +1 -1
  14. package/src/plugins/plugin-context-factory.ts +4 -19
  15. package/src/plugins/providers/anthropic.ts +2 -2
  16. package/src/plugins/providers/antigravity.ts +1 -1
  17. package/src/plugins/providers/chutes.ts +189 -0
  18. package/src/plugins/providers/codex.ts +1 -1
  19. package/src/plugins/providers/gemini.ts +1 -1
  20. package/src/plugins/providers/github-copilot.ts +1 -1
  21. package/src/plugins/providers/index.ts +1 -0
  22. package/src/plugins/providers/minimax.ts +1 -1
  23. package/src/plugins/providers/openai-api.ts +1 -1
  24. package/src/plugins/providers/opencode-zen.ts +1 -1
  25. package/src/plugins/providers/perplexity.ts +1 -1
  26. package/src/plugins/providers/zai.ts +1 -1
  27. package/src/plugins/registry.ts +9 -9
  28. package/src/plugins/sandbox-guard.test.ts +1 -1
  29. package/src/plugins/sandbox-guard.ts +1 -1
  30. package/src/plugins/sandbox.ts +1 -1
  31. package/src/plugins/themes/catppuccin-latte.ts +1 -1
  32. package/src/plugins/themes/catppuccin-mocha.ts +1 -1
  33. package/src/plugins/themes/claude-code.ts +1 -1
  34. package/src/plugins/themes/dracula.ts +1 -1
  35. package/src/plugins/themes/github-light.ts +1 -1
  36. package/src/plugins/themes/gruvbox-dark.ts +1 -1
  37. package/src/plugins/themes/gruvbox-light.ts +1 -1
  38. package/src/plugins/themes/kanagawa.ts +1 -1
  39. package/src/plugins/themes/nord.ts +1 -1
  40. package/src/plugins/themes/one-dark.ts +1 -1
  41. package/src/plugins/themes/opencode.ts +1 -1
  42. package/src/plugins/themes/rose-pine-dawn.ts +1 -1
  43. package/src/plugins/themes/rose-pine.ts +1 -1
  44. package/src/plugins/themes/solarized-light.ts +1 -1
  45. package/src/plugins/themes/tokyo-night.ts +1 -1
  46. package/src/plugins/update-checker.ts +2 -2
  47. package/src/pricing/estimator.test.ts +1 -1
  48. package/src/pricing/estimator.ts +1 -1
  49. package/src/pricing/fallback.ts +1 -1
  50. package/src/pricing/index.ts +1 -1
  51. package/src/pricing/models-dev.ts +1 -1
  52. package/src/storage/index.ts +6 -0
  53. package/src/storage/migrations/index.ts +17 -0
  54. package/src/storage/repos/pluginStorage.ts +35 -0
  55. package/src/tui/App.tsx +1 -1
  56. package/src/tui/components/ProviderCard.tsx +1 -1
  57. package/src/tui/components/ProviderDetailPanel.tsx +1 -1
  58. package/src/tui/components/SessionDetailsDrawer.tsx +10 -0
  59. package/src/tui/components/SessionsTable.tsx +9 -2
  60. package/src/tui/components/SettingsModal.tsx +1 -1
  61. package/src/tui/components/ThemePicker.tsx +1 -1
  62. package/src/tui/contexts/AgentSessionContext.tsx +1 -1
  63. package/src/tui/contexts/PluginContext.tsx +8 -4
  64. package/src/tui/contexts/RealTimeActivityContext.tsx +33 -20
  65. package/src/tui/contexts/ThemeContext.tsx +3 -3
  66. package/src/tui/hooks/useSessionEnrichedProviders.ts +1 -1
  67. package/src/tui/views/SettingsView.tsx +1 -1
  68. package/src/version.ts +1 -1
  69. package/src/plugins/types/agent.ts +0 -108
  70. package/src/plugins/types/base.ts +0 -278
  71. package/src/plugins/types/index.ts +0 -19
  72. package/src/plugins/types/notification.ts +0 -51
  73. package/src/plugins/types/provider.ts +0 -237
  74. package/src/plugins/types/theme.ts +0 -78
  75. package/src/storage/database.ts +0 -623
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const kanagawaTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const nordTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const oneDarkTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const opencodeTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const rosePineDawnTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const rosePineTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const solarizedLightTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -1,4 +1,4 @@
1
- import type { ThemePlugin } from "../types/theme.ts";
1
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export const tokyoNightTheme: ThemePlugin = {
4
4
  apiVersion: 2,
@@ -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 "./types/base.ts";
12
+ import type { PluginType } from "@tokentop/plugin-sdk";
13
13
 
14
14
  // ---------------------------------------------------------------------------
15
15
  // Types
@@ -60,7 +60,7 @@ 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("%40", "@")}`;
63
+ const url = `https://registry.npmjs.org/${encodeURIComponent(packageName).replaceAll("%40", "@")}`;
64
64
  const res = await fetch(url, {
65
65
  headers: { Accept: "application/vnd.npm.install-v1+json" },
66
66
  signal: AbortSignal.timeout(8_000),
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import type { ModelPricing } from "@/plugins/types/provider.ts";
2
+ import type { ModelPricing } from "@tokentop/plugin-sdk";
3
3
  import { estimateCost, estimateSessionCost, formatCost, formatTokenCount } from "./estimator.ts";
4
4
 
5
5
  describe("estimateCost()", () => {
@@ -1,4 +1,4 @@
1
- import type { CostBreakdown, ModelPricing } from "@/plugins/types/provider.ts";
1
+ import type { CostBreakdown, ModelPricing } from "@tokentop/plugin-sdk";
2
2
 
3
3
  export interface TokenUsage {
4
4
  input: number;
@@ -1,4 +1,4 @@
1
- import type { ModelPricing } from "@/plugins/types/provider.ts";
1
+ import type { ModelPricing } from "@tokentop/plugin-sdk";
2
2
 
3
3
  //TODO: get more up to date fallback pricing
4
4
  export const FALLBACK_PRICING: Record<string, Record<string, ModelPricing>> = {
@@ -1,4 +1,4 @@
1
- import type { ModelPricing, ProviderPlugin, ProviderPricing } from "@/plugins/types/provider.ts";
1
+ import type { ModelPricing, ProviderPlugin, ProviderPricing } from "@tokentop/plugin-sdk";
2
2
  import { getFallbackPricing, getFallbackProviderPricing } from "./fallback.ts";
3
3
  import {
4
4
  clearCache as clearModelsDevCache,
@@ -1,4 +1,4 @@
1
- import type { ModelPricing } from "@/plugins/types/provider.ts";
1
+ import type { ModelPricing } from "@tokentop/plugin-sdk";
2
2
 
3
3
  const MODELS_DEV_API = "https://models.dev/api.json";
4
4
  const CACHE_TTL_MS = 3600000;
@@ -15,6 +15,12 @@ export {
15
15
  insertAgentSessionSnapshot,
16
16
  upsertAgentSession,
17
17
  } from "./repos/agentSessions.ts";
18
+ export {
19
+ pluginStorageDelete,
20
+ pluginStorageGet,
21
+ pluginStorageHas,
22
+ pluginStorageSet,
23
+ } from "./repos/pluginStorage.ts";
18
24
  export {
19
25
  getLatestProviderSnapshot,
20
26
  insertProviderSnapshot,
@@ -164,6 +164,23 @@ export const migrations: Migration[] = [
164
164
  `);
165
165
  },
166
166
  },
167
+ {
168
+ version: 2,
169
+ up: (db: Database) => {
170
+ db.exec(`
171
+ CREATE TABLE IF NOT EXISTS plugin_storage (
172
+ plugin_id TEXT NOT NULL,
173
+ key TEXT NOT NULL,
174
+ value TEXT NOT NULL,
175
+ updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
176
+ PRIMARY KEY (plugin_id, key)
177
+ );
178
+
179
+ CREATE INDEX IF NOT EXISTS idx_plugin_storage_plugin
180
+ ON plugin_storage(plugin_id);
181
+ `);
182
+ },
183
+ },
167
184
  ];
168
185
 
169
186
  export function getCurrentVersion(db: Database): number {
@@ -0,0 +1,35 @@
1
+ import { getDatabase } from "../db.ts";
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Plugin KV storage
5
+ // ---------------------------------------------------------------------------
6
+
7
+ export function pluginStorageGet(pluginId: string, key: string): string | null {
8
+ const db = getDatabase();
9
+ const row = db
10
+ .prepare("SELECT value FROM plugin_storage WHERE plugin_id = ? AND key = ?")
11
+ .get(pluginId, key) as { value: string } | null;
12
+ return row?.value ?? null;
13
+ }
14
+
15
+ export function pluginStorageSet(pluginId: string, key: string, value: string): void {
16
+ const db = getDatabase();
17
+ db.prepare(
18
+ `INSERT INTO plugin_storage (plugin_id, key, value, updated_at)
19
+ VALUES (?, ?, ?, ?)
20
+ ON CONFLICT(plugin_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,
21
+ ).run(pluginId, key, value, Math.floor(Date.now() / 1000));
22
+ }
23
+
24
+ export function pluginStorageDelete(pluginId: string, key: string): void {
25
+ const db = getDatabase();
26
+ db.prepare("DELETE FROM plugin_storage WHERE plugin_id = ? AND key = ?").run(pluginId, key);
27
+ }
28
+
29
+ export function pluginStorageHas(pluginId: string, key: string): boolean {
30
+ const db = getDatabase();
31
+ const row = db
32
+ .prepare("SELECT 1 FROM plugin_storage WHERE plugin_id = ? AND key = ?")
33
+ .get(pluginId, key);
34
+ return row !== null && row !== undefined;
35
+ }
package/src/tui/App.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useKeyboard } from "@opentui/react";
2
+ import type { ThemePlugin } from "@tokentop/plugin-sdk";
2
3
  import { useCallback, useEffect, useMemo, useState } from "react";
3
4
  import * as builtinThemeExports from "@/plugins/themes/index.ts";
4
- import type { ThemePlugin } from "@/plugins/types/theme.ts";
5
5
  import { resolveTheme, ThemeProvider, useColors, useTheme } from "./contexts/ThemeContext.tsx";
6
6
  import {
7
7
  type BurstRecorder,
@@ -1,7 +1,7 @@
1
1
  import type { BoxRenderable } from "@opentui/core";
2
2
  import { useTerminalDimensions } from "@opentui/react";
3
+ import type { ProviderUsageData } from "@tokentop/plugin-sdk";
3
4
  import { forwardRef, useCallback, useEffect, useState } from "react";
4
- import type { ProviderUsageData } from "@/plugins/types/provider.ts";
5
5
  import { useColors } from "../contexts/ThemeContext.tsx";
6
6
  import { SkeletonProviderContent } from "./Skeleton.tsx";
7
7
  import { useSpinner } from "./Spinner.tsx";
@@ -1,4 +1,4 @@
1
- import type { UsageLimit } from "@/plugins/types/provider.ts";
1
+ import type { UsageLimit } from "@tokentop/plugin-sdk";
2
2
  import type { ProviderState } from "../contexts/PluginContext.tsx";
3
3
  import { useColors } from "../contexts/ThemeContext.tsx";
4
4
  import { Sparkline } from "./Sparkline.tsx";
@@ -314,6 +314,16 @@ export function SessionDetailsDrawer({ session, onClose: _onClose }: SessionDeta
314
314
  </text>
315
315
  </box>
316
316
 
317
+ {session.metadata?.isEstimated === true && (
318
+ <box flexDirection="row" height={1} marginBottom={0}>
319
+ <text height={1} overflow="hidden">
320
+ <span fg={colors.warning}>
321
+ ≈ Token data is estimated — server enrichment pending
322
+ </span>
323
+ </text>
324
+ </box>
325
+ )}
326
+
317
327
  {hasCacheData && (
318
328
  <box flexDirection="row" height={1} marginBottom={0}>
319
329
  <text height={1} overflow="hidden">
@@ -154,6 +154,7 @@ const SessionRow = memo(function SessionRow({
154
154
  const effectiveTokens = session.totals.input + session.totals.output;
155
155
  const cacheRead = session.totals.cacheRead ?? 0;
156
156
  const hasCacheData = cacheRead > 0;
157
+ const isEstimated = session.metadata?.isEstimated === true;
157
158
  const costUsd = session.totalCostUsd ?? 0;
158
159
 
159
160
  const animatedTokens = useAnimatedValue(effectiveTokens, { durationMs: 300, precision: 0 });
@@ -260,7 +261,10 @@ const SessionRow = memo(function SessionRow({
260
261
  {String(session.requestCount).padStart(4)}
261
262
  </text>
262
263
  <text width={14} height={1} {...bgProp}>
263
- <span fg={tokenColor}>{formatTokensVal(animatedTokens).padStart(7)}</span>
264
+ <span fg={tokenColor}>
265
+ {isEstimated ? "≈" : " "}
266
+ {formatTokensVal(animatedTokens).padStart(7)}
267
+ </span>
264
268
  <span fg={colors.textMuted}>
265
269
  {hasCacheData ? ` ↯${formatCacheBadge(cacheRead)}` : ""}
266
270
  </span>
@@ -315,7 +319,10 @@ const SessionRow = memo(function SessionRow({
315
319
  : modelWithCount.padEnd(narrowModelMaxChars)}
316
320
  </text>
317
321
  <text width={narrowTokenW} height={1} {...bgProp}>
318
- <span fg={tokenColor}>{formatTokensVal(animatedTokens).padStart(7)}</span>
322
+ <span fg={tokenColor}>
323
+ {isEstimated ? "≈" : " "}
324
+ {formatTokensVal(animatedTokens).padStart(7)}
325
+ </span>
319
326
  <span fg={colors.textMuted}>
320
327
  {hasCacheData && showCacheBadge ? ` ↯${formatCacheBadge(cacheRead)}` : ""}
321
328
  </span>
@@ -1,7 +1,7 @@
1
1
  import { useKeyboard, useTerminalDimensions } from "@opentui/react";
2
+ import type { ConfigField, PluginType } from "@tokentop/plugin-sdk";
2
3
  import { useCallback, useEffect, useMemo, useState } from "react";
3
4
  import type { AppConfig, SparklineOrientation, SparklineStyle } from "@/config/schema.ts";
4
- import type { ConfigField, PluginType } from "@/plugins/types/base.ts";
5
5
  import { formatBudgetDisplay, parseCurrencyInput } from "@/utils/currency.ts";
6
6
  import { useConfig } from "../contexts/ConfigContext.tsx";
7
7
  import { useDemoMode } from "../contexts/DemoModeContext.tsx";
@@ -1,6 +1,6 @@
1
1
  import { useKeyboard } from "@opentui/react";
2
+ import type { ColorSchemePreference, ThemePlugin } from "@tokentop/plugin-sdk";
2
3
  import { useEffect, useMemo, useState } from "react";
3
- import type { ColorSchemePreference, ThemePlugin } from "@/plugins/types/theme.ts";
4
4
  import { useColors } from "../contexts/ThemeContext.tsx";
5
5
 
6
6
  interface ThemePickerProps {
@@ -1,3 +1,4 @@
1
+ import type { AgentPlugin, SessionParseOptions } from "@tokentop/plugin-sdk";
1
2
  import {
2
3
  createContext,
3
4
  type ReactNode,
@@ -13,7 +14,6 @@ import type { AgentId, AgentInfo, AgentSessionAggregate } from "@/agents/types.t
13
14
  import { createPluginContext } from "@/plugins/plugin-context-factory.ts";
14
15
  import { pluginRegistry } from "@/plugins/registry.ts";
15
16
  import { createPluginLogger, createSandboxedHttpClient } from "@/plugins/sandbox.ts";
16
- import type { AgentPlugin, SessionParseOptions } from "@/plugins/types/agent.ts";
17
17
  import { persistSessions } from "@/storage/persistence-service.ts";
18
18
  import type { PricingSource } from "@/storage/types.ts";
19
19
  import { useDemoMode } from "./DemoModeContext.tsx";
@@ -1,3 +1,11 @@
1
+ import type {
2
+ AgentPlugin,
3
+ Credentials,
4
+ NotificationPlugin,
5
+ ProviderPlugin,
6
+ ProviderUsageData,
7
+ ThemePlugin,
8
+ } from "@tokentop/plugin-sdk";
1
9
  import {
2
10
  createContext,
3
11
  type ReactNode,
@@ -14,10 +22,6 @@ import { safeInvoke, safeInvokeSync } from "@/plugins/plugin-host.ts";
14
22
  import { pluginRegistry } from "@/plugins/registry.ts";
15
23
  import { createPluginLogger, createSandboxedHttpClient } from "@/plugins/sandbox.ts";
16
24
  import { installGlobalFetchGuard, runInPluginGuard } from "@/plugins/sandbox-guard.ts";
17
- import type { AgentPlugin } from "@/plugins/types/agent.ts";
18
- import type { NotificationPlugin } from "@/plugins/types/notification.ts";
19
- import type { Credentials, ProviderPlugin, ProviderUsageData } from "@/plugins/types/provider.ts";
20
- import type { ThemePlugin } from "@/plugins/types/theme.ts";
21
25
  import { initPricingFromPlugins } from "@/pricing/index.ts";
22
26
  import type { ProviderSnapshotInsert } from "@/storage/types.ts";
23
27
  import { useConfig } from "./ConfigContext.tsx";
@@ -1,3 +1,4 @@
1
+ import type { ActivityUpdate } from "@tokentop/plugin-sdk";
1
2
  import {
2
3
  createContext,
3
4
  type ReactNode,
@@ -9,7 +10,6 @@ import {
9
10
  } from "react";
10
11
  import { createPluginContext } from "@/plugins/plugin-context-factory.ts";
11
12
  import { pluginRegistry } from "@/plugins/registry.ts";
12
- import type { ActivityUpdate } from "@/plugins/types/agent.ts";
13
13
  import { useDemoMode } from "./DemoModeContext.tsx";
14
14
  import { useLogs } from "./LogContext.tsx";
15
15
  import { usePlugins } from "./PluginContext.tsx";
@@ -50,25 +50,38 @@ export function RealTimeActivityProvider({ children }: { children: ReactNode })
50
50
  };
51
51
  }, []);
52
52
 
53
- const handleActivityUpdate = useCallback(
54
- (update: ActivityUpdate) => {
55
- const key = `${update.sessionId}:${update.messageId}`;
56
- const prev = lastSeenRef.current.get(key);
57
- const prevTokens = prev?.tokens ?? 0;
58
- const newTokens = update.tokens.input + update.tokens.output + (update.tokens.reasoning ?? 0);
59
- const delta = Math.max(0, newTokens - prevTokens);
60
-
61
- if (delta > 0) {
62
- lastSeenRef.current.set(key, { tokens: newTokens, updatedAt: Date.now() });
63
- setLastActivityAt(update.timestamp);
64
- debug(`Real-time activity: +${delta} tokens`, { sessionId: update.sessionId }, "realtime");
65
-
66
- for (const listener of listenersRef.current) {
67
- listener(delta, update.timestamp);
68
- }
53
+ // Store the actual handler logic in a ref so it always has access to the
54
+ // latest `debug` without changing callback identity. This prevents the
55
+ // useEffect below from re-running on every render (which would trigger
56
+ // stopActivityWatch/startActivityWatch churn on every plugin).
57
+ const handleActivityUpdateRef = useRef<(update: ActivityUpdate) => void>(() => {});
58
+ handleActivityUpdateRef.current = (update: ActivityUpdate) => {
59
+ const key = `${update.sessionId}:${update.messageId}`;
60
+ const prev = lastSeenRef.current.get(key);
61
+ const prevTokens = prev?.tokens ?? 0;
62
+ const input = update.tokens.input ?? 0;
63
+ const output = update.tokens.output ?? 0;
64
+ const reasoning = update.tokens.reasoning ?? 0;
65
+ const newTokens = input + output + reasoning;
66
+ if (!Number.isFinite(newTokens)) return;
67
+ const delta = Math.max(0, newTokens - prevTokens);
68
+
69
+ if (delta > 0) {
70
+ lastSeenRef.current.set(key, { tokens: newTokens, updatedAt: Date.now() });
71
+ setLastActivityAt(update.timestamp);
72
+ debug(`Real-time activity: +${delta} tokens`, { sessionId: update.sessionId }, "realtime");
73
+
74
+ for (const listener of listenersRef.current) {
75
+ listener(delta, update.timestamp);
69
76
  }
70
- },
71
- [debug],
77
+ }
78
+ };
79
+
80
+ // Stable wrapper — identity never changes, so the useEffect below only
81
+ // runs once per plugin initialization (not on every debug/info change).
82
+ const handleActivityUpdate = useCallback(
83
+ (update: ActivityUpdate) => handleActivityUpdateRef.current(update),
84
+ [],
72
85
  );
73
86
 
74
87
  useEffect(() => {
@@ -106,7 +119,7 @@ export function RealTimeActivityProvider({ children }: { children: ReactNode })
106
119
  cleanupRef.current = null;
107
120
  }
108
121
  };
109
- }, [pluginsInitialized, demoMode, handleActivityUpdate, info]);
122
+ }, [pluginsInitialized, demoMode, handleActivityUpdate]);
110
123
 
111
124
  useEffect(() => {
112
125
  const interval = setInterval(() => {
@@ -1,12 +1,12 @@
1
- import { createContext, type ReactNode, useContext, useState } from "react";
2
- import { tokyoNightTheme } from "@/plugins/themes/tokyo-night.ts";
3
1
  import type {
4
2
  ColorScheme,
5
3
  ColorSchemePreference,
6
4
  ThemeColors,
7
5
  ThemeComponents,
8
6
  ThemePlugin,
9
- } from "@/plugins/types/theme.ts";
7
+ } from "@tokentop/plugin-sdk";
8
+ import { createContext, type ReactNode, useContext, useState } from "react";
9
+ import { tokyoNightTheme } from "@/plugins/themes/tokyo-night.ts";
10
10
 
11
11
  interface ThemeContextValue {
12
12
  theme: ThemePlugin;
@@ -1,6 +1,6 @@
1
+ import type { ProviderUsageData } from "@tokentop/plugin-sdk";
1
2
  import { useMemo } from "react";
2
3
  import type { AgentSessionAggregate } from "@/agents/types.ts";
3
- import type { ProviderUsageData } from "@/plugins/types/provider.ts";
4
4
  import type { ProviderState } from "../contexts/PluginContext.tsx";
5
5
 
6
6
  interface ProviderSessionTotals {
@@ -1,7 +1,7 @@
1
1
  import { useKeyboard, useTerminalDimensions } from "@opentui/react";
2
+ import type { ConfigField } from "@tokentop/plugin-sdk";
2
3
  import { useCallback, useMemo, useState } from "react";
3
4
  import type { AppConfig } from "@/config/schema.ts";
4
- import type { ConfigField } from "@/plugins/types/base.ts";
5
5
  import { useConfig } from "../contexts/ConfigContext.tsx";
6
6
  import { useDemoMode } from "../contexts/DemoModeContext.tsx";
7
7
  import { usePlugins } from "../contexts/PluginContext.tsx";
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.4.0"; // x-release-please-version
1
+ export const VERSION = "0.5.0"; // x-release-please-version
@@ -1,108 +0,0 @@
1
- import { z } from "zod";
2
- import type { BasePlugin, PluginHttpClient, PluginLogger } from "./base.ts";
3
- import type { Credentials, OAuthCredentials, PluginContext } from "./provider.ts";
4
-
5
- export const AgentCapabilitiesSchema = z.object({
6
- sessionParsing: z.boolean(),
7
- authReading: z.boolean(),
8
- realTimeTracking: z.boolean(),
9
- multiProvider: z.boolean(),
10
- });
11
-
12
- export type AgentCapabilities = z.infer<typeof AgentCapabilitiesSchema>;
13
-
14
- export interface AgentConfig {
15
- /** Display name for this coding agent (e.g. "OpenCode", "Cursor"). */
16
- name: string;
17
- /** CLI command that launches the agent (for display/detection). */
18
- command?: string;
19
- /** Path to the agent's config directory (for display/debugging). */
20
- configPath?: string;
21
- /** Path to the agent's session storage. */
22
- sessionPath?: string;
23
- /** Path to the agent's auth file (for credential reading). */
24
- authPath?: string;
25
- }
26
-
27
- export interface AgentCredentials {
28
- providers: Record<string, Credentials | undefined>;
29
- oauth?: Record<string, OAuthCredentials>;
30
- }
31
-
32
- export interface AgentProviderConfig {
33
- id: string;
34
- name: string;
35
- configured: boolean;
36
- enabled?: boolean;
37
- }
38
-
39
- export interface SessionParseOptions {
40
- sessionId?: string;
41
- timePeriod?: "session" | "daily" | "weekly" | "monthly";
42
- limit?: number;
43
- /** Epoch ms — only return sessions updated after this timestamp. */
44
- since?: number;
45
- }
46
-
47
- export interface SessionUsageData {
48
- sessionId: string;
49
- sessionName?: string;
50
- providerId: string;
51
- modelId: string;
52
- tokens: {
53
- input: number;
54
- output: number;
55
- cacheRead?: number;
56
- cacheWrite?: number;
57
- };
58
- timestamp: number;
59
- sessionUpdatedAt?: number;
60
- projectPath?: string;
61
- cost?: number;
62
- }
63
-
64
- export interface AgentFetchContext {
65
- readonly http: PluginHttpClient;
66
- readonly logger: PluginLogger;
67
- readonly config: Record<string, unknown>;
68
- readonly signal: AbortSignal;
69
- }
70
-
71
- export interface ActivityUpdate {
72
- sessionId: string;
73
- messageId: string;
74
- tokens: {
75
- input: number;
76
- output: number;
77
- reasoning?: number;
78
- cacheRead?: number;
79
- cacheWrite?: number;
80
- };
81
- timestamp: number;
82
- }
83
-
84
- export type ActivityCallback = (update: ActivityUpdate) => void;
85
-
86
- export interface AgentPlugin extends BasePlugin {
87
- readonly type: "agent";
88
- readonly agent: AgentConfig;
89
- readonly capabilities: AgentCapabilities;
90
-
91
- /** Check if this coding agent is installed on the user's machine. */
92
- isInstalled(ctx: PluginContext): Promise<boolean>;
93
-
94
- /** Read credentials that the coding agent has stored. */
95
- readCredentials?(ctx: AgentFetchContext): Promise<AgentCredentials>;
96
-
97
- /** Parse session usage data from the agent's local storage. */
98
- parseSessions(options: SessionParseOptions, ctx: AgentFetchContext): Promise<SessionUsageData[]>;
99
-
100
- /** List which model providers this agent is configured to use. */
101
- getProviders?(ctx: AgentFetchContext): Promise<AgentProviderConfig[]>;
102
-
103
- /** Start watching for real-time activity updates. */
104
- startActivityWatch?(ctx: PluginContext, callback: ActivityCallback): void;
105
-
106
- /** Stop watching for real-time activity updates. */
107
- stopActivityWatch?(ctx: PluginContext): void;
108
- }