@tokentop/ttop 0.2.2

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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +273 -0
  3. package/bin/ttop.js +49 -0
  4. package/package.json +68 -0
  5. package/src/agents/aggregator.ts +176 -0
  6. package/src/agents/costing.ts +74 -0
  7. package/src/agents/index.ts +12 -0
  8. package/src/agents/types.ts +71 -0
  9. package/src/cli.ts +167 -0
  10. package/src/config/schema.test.ts +166 -0
  11. package/src/config/schema.ts +165 -0
  12. package/src/demo/simulator.ts +745 -0
  13. package/src/plugins/agents/index.ts +2 -0
  14. package/src/plugins/auth-sources.ts +186 -0
  15. package/src/plugins/lifecycle.ts +177 -0
  16. package/src/plugins/loader.test.ts +195 -0
  17. package/src/plugins/loader.ts +282 -0
  18. package/src/plugins/notification-bus.ts +153 -0
  19. package/src/plugins/notifications/index.ts +2 -0
  20. package/src/plugins/notifications/terminal-bell.ts +66 -0
  21. package/src/plugins/notifications/visual-flash.ts +57 -0
  22. package/src/plugins/npm-installer.ts +164 -0
  23. package/src/plugins/plugin-context-factory.ts +67 -0
  24. package/src/plugins/plugin-host.ts +205 -0
  25. package/src/plugins/providers/anthropic.ts +325 -0
  26. package/src/plugins/providers/antigravity.ts +309 -0
  27. package/src/plugins/providers/codex.ts +250 -0
  28. package/src/plugins/providers/gemini.ts +391 -0
  29. package/src/plugins/providers/github-copilot.ts +194 -0
  30. package/src/plugins/providers/index.ts +12 -0
  31. package/src/plugins/providers/minimax.ts +223 -0
  32. package/src/plugins/providers/openai-api.ts +250 -0
  33. package/src/plugins/providers/opencode-zen.ts +125 -0
  34. package/src/plugins/providers/perplexity.ts +177 -0
  35. package/src/plugins/providers/zai.ts +225 -0
  36. package/src/plugins/registry.ts +251 -0
  37. package/src/plugins/sandbox-guard.test.ts +191 -0
  38. package/src/plugins/sandbox-guard.ts +155 -0
  39. package/src/plugins/sandbox.ts +138 -0
  40. package/src/plugins/themes/catppuccin-latte.ts +45 -0
  41. package/src/plugins/themes/catppuccin-mocha.ts +45 -0
  42. package/src/plugins/themes/claude-code.ts +45 -0
  43. package/src/plugins/themes/dracula.ts +45 -0
  44. package/src/plugins/themes/github-light.ts +45 -0
  45. package/src/plugins/themes/gruvbox-dark.ts +45 -0
  46. package/src/plugins/themes/gruvbox-light.ts +45 -0
  47. package/src/plugins/themes/index.ts +15 -0
  48. package/src/plugins/themes/kanagawa.ts +45 -0
  49. package/src/plugins/themes/nord.ts +45 -0
  50. package/src/plugins/themes/one-dark.ts +45 -0
  51. package/src/plugins/themes/opencode.ts +47 -0
  52. package/src/plugins/themes/rose-pine-dawn.ts +45 -0
  53. package/src/plugins/themes/rose-pine.ts +45 -0
  54. package/src/plugins/themes/solarized-light.ts +45 -0
  55. package/src/plugins/themes/tokyo-night.ts +49 -0
  56. package/src/plugins/types/agent.ts +108 -0
  57. package/src/plugins/types/base.ts +267 -0
  58. package/src/plugins/types/index.ts +19 -0
  59. package/src/plugins/types/notification.ts +51 -0
  60. package/src/plugins/types/provider.ts +233 -0
  61. package/src/plugins/types/theme.ts +70 -0
  62. package/src/plugins/update-checker.ts +177 -0
  63. package/src/pricing/estimator.test.ts +181 -0
  64. package/src/pricing/estimator.ts +80 -0
  65. package/src/pricing/fallback.test.ts +71 -0
  66. package/src/pricing/fallback.ts +62 -0
  67. package/src/pricing/index.ts +66 -0
  68. package/src/pricing/models-dev.ts +130 -0
  69. package/src/storage/database.ts +613 -0
  70. package/src/storage/db.ts +64 -0
  71. package/src/storage/index.ts +66 -0
  72. package/src/storage/migrations/index.ts +189 -0
  73. package/src/storage/paths.ts +24 -0
  74. package/src/storage/repos/agentSessions.ts +210 -0
  75. package/src/storage/repos/providerSnapshots.ts +116 -0
  76. package/src/storage/repos/usageEvents.ts +334 -0
  77. package/src/storage/types.ts +288 -0
  78. package/src/tui/App.tsx +381 -0
  79. package/src/tui/components/CommandPalette.tsx +166 -0
  80. package/src/tui/components/DebugConsole.tsx +161 -0
  81. package/src/tui/components/DebugPanel.tsx +660 -0
  82. package/src/tui/components/FooterHints.tsx +22 -0
  83. package/src/tui/components/GhostProviderCard.tsx +45 -0
  84. package/src/tui/components/Header.tsx +125 -0
  85. package/src/tui/components/HelpOverlay.tsx +45 -0
  86. package/src/tui/components/InlineGauge.tsx +34 -0
  87. package/src/tui/components/InlineSparkline.tsx +78 -0
  88. package/src/tui/components/KpiStrip.tsx +246 -0
  89. package/src/tui/components/LimitGauge.tsx +186 -0
  90. package/src/tui/components/ModalBackdrop.tsx +42 -0
  91. package/src/tui/components/ProviderAggregateStrip.tsx +200 -0
  92. package/src/tui/components/ProviderCard.tsx +309 -0
  93. package/src/tui/components/ProviderDetailPanel.tsx +237 -0
  94. package/src/tui/components/ProviderLimitsPanel.tsx +308 -0
  95. package/src/tui/components/ProvidersList.tsx +380 -0
  96. package/src/tui/components/SessionDetailsDrawer.tsx +317 -0
  97. package/src/tui/components/SessionsTable.tsx +401 -0
  98. package/src/tui/components/SettingsModal.tsx +587 -0
  99. package/src/tui/components/SidebarBreakdown.tsx +87 -0
  100. package/src/tui/components/Skeleton.tsx +102 -0
  101. package/src/tui/components/SmartSidebar.tsx +644 -0
  102. package/src/tui/components/Sparkline.tsx +180 -0
  103. package/src/tui/components/Spinner.tsx +36 -0
  104. package/src/tui/components/StatusBar.tsx +96 -0
  105. package/src/tui/components/ThemePicker.tsx +234 -0
  106. package/src/tui/components/Toast.tsx +61 -0
  107. package/src/tui/components/UsageGauge.tsx +80 -0
  108. package/src/tui/components/index.ts +5 -0
  109. package/src/tui/contexts/AgentSessionContext.tsx +307 -0
  110. package/src/tui/contexts/ConfigContext.tsx +141 -0
  111. package/src/tui/contexts/DashboardRuntimeContext.tsx +102 -0
  112. package/src/tui/contexts/DemoModeContext.tsx +60 -0
  113. package/src/tui/contexts/DrawerContext.tsx +42 -0
  114. package/src/tui/contexts/InputContext.tsx +30 -0
  115. package/src/tui/contexts/LogContext.tsx +150 -0
  116. package/src/tui/contexts/PluginContext.tsx +424 -0
  117. package/src/tui/contexts/RealTimeActivityContext.tsx +119 -0
  118. package/src/tui/contexts/StorageContext.tsx +233 -0
  119. package/src/tui/contexts/ThemeContext.tsx +95 -0
  120. package/src/tui/contexts/TimeWindowContext.tsx +158 -0
  121. package/src/tui/contexts/ToastContext.tsx +40 -0
  122. package/src/tui/contexts/index.ts +5 -0
  123. package/src/tui/createApp.tsx +47 -0
  124. package/src/tui/debug/captureFrame.ts +262 -0
  125. package/src/tui/debug/snapshot.tsx +644 -0
  126. package/src/tui/driver/assertions.ts +279 -0
  127. package/src/tui/driver/cli.ts +717 -0
  128. package/src/tui/driver/coverage.ts +204 -0
  129. package/src/tui/driver/demo-snapshot.ts +66 -0
  130. package/src/tui/driver/diff.ts +166 -0
  131. package/src/tui/driver/driver.ts +270 -0
  132. package/src/tui/driver/index.ts +53 -0
  133. package/src/tui/driver/recorder.ts +303 -0
  134. package/src/tui/driver/test-workflow.ts +115 -0
  135. package/src/tui/driver/test.ts +47 -0
  136. package/src/tui/hooks/index.ts +20 -0
  137. package/src/tui/hooks/useAnimatedValue.ts +113 -0
  138. package/src/tui/hooks/useDashboardKeyboard.ts +341 -0
  139. package/src/tui/hooks/useDashboardState.ts +116 -0
  140. package/src/tui/hooks/useEmaActivity.ts +223 -0
  141. package/src/tui/hooks/useEntranceAnimation.ts +62 -0
  142. package/src/tui/hooks/useExitAnimation.ts +137 -0
  143. package/src/tui/hooks/usePulse.ts +138 -0
  144. package/src/tui/hooks/useSafeRenderer.ts +24 -0
  145. package/src/tui/hooks/useValueFlash.ts +111 -0
  146. package/src/tui/index.tsx +50 -0
  147. package/src/tui/utils/providerColor.ts +32 -0
  148. package/src/tui/views/Dashboard.tsx +340 -0
  149. package/src/tui/views/HistoricalTrendsView.tsx +1431 -0
  150. package/src/tui/views/ProjectsView.tsx +1277 -0
  151. package/src/tui/views/RealTimeDashboard.tsx +393 -0
  152. package/src/tui/views/SettingsView.tsx +465 -0
  153. package/src/tui/views/index.ts +2 -0
  154. package/src/types/global.d.ts +5 -0
  155. package/src/types/opentui-test-utils.d.ts +18 -0
  156. package/src/utils/clipboard.ts +118 -0
  157. package/src/utils/currency.test.ts +106 -0
  158. package/src/utils/currency.ts +79 -0
  159. package/src/version.ts +1 -0
@@ -0,0 +1,106 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import {
3
+ formatBudgetDisplay,
4
+ formatCurrency,
5
+ isValidCurrencyInput,
6
+ parseCurrencyInput,
7
+ } from './currency.ts';
8
+
9
+ describe('parseCurrencyInput', () => {
10
+ test('returns null for empty or whitespace input', () => {
11
+ expect(parseCurrencyInput('')).toBeNull();
12
+ expect(parseCurrencyInput(' ')).toBeNull();
13
+ });
14
+
15
+ test('parses US format with thousands and decimal separators', () => {
16
+ expect(parseCurrencyInput('1,234.56')).toBe(1234.56);
17
+ });
18
+
19
+ test('parses European format with period thousands and comma decimal', () => {
20
+ expect(parseCurrencyInput('1.234,56')).toBe(1234.56);
21
+ });
22
+
23
+ test('parses plain numbers', () => {
24
+ expect(parseCurrencyInput('42.50')).toBe(42.5);
25
+ });
26
+
27
+ test('strips supported currency symbols before parsing', () => {
28
+ expect(parseCurrencyInput('$100')).toBe(100);
29
+ expect(parseCurrencyInput('€100')).toBe(100);
30
+ expect(parseCurrencyInput('£100')).toBe(100);
31
+ });
32
+
33
+ test('treats comma as decimal separator when not thousands grouping', () => {
34
+ expect(parseCurrencyInput('10,50')).toBe(10.5);
35
+ });
36
+
37
+ test('treats comma as thousands separator with 3 digits after comma', () => {
38
+ expect(parseCurrencyInput('1,234')).toBe(1234);
39
+ });
40
+
41
+ test('returns null for invalid input', () => {
42
+ expect(parseCurrencyInput('abc')).toBeNull();
43
+ });
44
+
45
+ test('rounds parsed values to 2 decimal places', () => {
46
+ const parsed = parseCurrencyInput('10.129');
47
+ expect(parsed).toBe(Math.round(10.129 * 100) / 100);
48
+ });
49
+ });
50
+
51
+ describe('formatCurrency', () => {
52
+ test('returns None for null input', () => {
53
+ expect(formatCurrency(null)).toBe('None');
54
+ });
55
+
56
+ test('formats compact values >= 1000 as k notation', () => {
57
+ expect(formatCurrency(1234, { compact: true })).toBe('$1.2k');
58
+ });
59
+
60
+ test('formats compact values >= 100 as rounded integer', () => {
61
+ expect(formatCurrency(234.4, { compact: true })).toBe('$234');
62
+ });
63
+
64
+ test('formats compact values >= 10 with one decimal place', () => {
65
+ expect(formatCurrency(42.34, { compact: true })).toBe('$42.3');
66
+ });
67
+
68
+ test('formats compact values < 10 with two decimal places', () => {
69
+ expect(formatCurrency(9.5, { compact: true })).toBe('$9.50');
70
+ });
71
+
72
+ test('formats non-compact values with locale separators and two decimals', () => {
73
+ expect(formatCurrency(1234.5)).toBe('$1,234.50');
74
+ });
75
+
76
+ test('omits currency symbol when showSymbol is false', () => {
77
+ expect(formatCurrency(1234.5, { showSymbol: false })).toBe('1,234.50');
78
+ expect(formatCurrency(1234, { compact: true, showSymbol: false })).toBe('1.2k');
79
+ });
80
+ });
81
+
82
+ describe('formatBudgetDisplay', () => {
83
+ test('returns None for null input', () => {
84
+ expect(formatBudgetDisplay(null)).toBe('None');
85
+ });
86
+
87
+ test('delegates formatting to formatCurrency for numeric values', () => {
88
+ expect(formatBudgetDisplay(1234.5)).toBe(formatCurrency(1234.5));
89
+ });
90
+ });
91
+
92
+ describe('isValidCurrencyInput', () => {
93
+ test('returns true for empty input (represents no value)', () => {
94
+ expect(isValidCurrencyInput('')).toBe(true);
95
+ expect(isValidCurrencyInput(' ')).toBe(true);
96
+ });
97
+
98
+ test('returns true for valid numeric input', () => {
99
+ expect(isValidCurrencyInput('42.50')).toBe(true);
100
+ expect(isValidCurrencyInput('1.234,56')).toBe(true);
101
+ });
102
+
103
+ test('returns false for invalid input', () => {
104
+ expect(isValidCurrencyInput('abc')).toBe(false);
105
+ });
106
+ });
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Parse a currency input string that may use either . or , as decimal separator.
3
+ * Handles US format (1,234.56) and European format (1.234,56).
4
+ */
5
+ export function parseCurrencyInput(input: string): number | null {
6
+ if (!input || input.trim() === '') {
7
+ return null;
8
+ }
9
+
10
+ let normalized = input.trim().replace(/[$€£]/g, '').replace(/\s/g, '');
11
+
12
+ const hasComma = normalized.includes(',');
13
+ const hasPeriod = normalized.includes('.');
14
+
15
+ if (hasComma && hasPeriod) {
16
+ const lastComma = normalized.lastIndexOf(',');
17
+ const lastPeriod = normalized.lastIndexOf('.');
18
+
19
+ if (lastComma > lastPeriod) {
20
+ // European: 1.234,56 → remove periods, comma becomes decimal
21
+ normalized = normalized.replace(/\./g, '').replace(',', '.');
22
+ } else {
23
+ // US: 1,234.56 → remove commas
24
+ normalized = normalized.replace(/,/g, '');
25
+ }
26
+ } else if (hasComma) {
27
+ const parts = normalized.split(',');
28
+ // 3 digits after comma = thousands separator (1,234), otherwise decimal (10,50)
29
+ if (parts.length === 2 && parts[1]!.length === 3 && parts[0]!.length > 0) {
30
+ normalized = normalized.replace(',', '');
31
+ } else {
32
+ normalized = normalized.replace(',', '.');
33
+ }
34
+ }
35
+
36
+ const parsed = parseFloat(normalized);
37
+
38
+ if (isNaN(parsed) || !isFinite(parsed)) {
39
+ return null;
40
+ }
41
+
42
+ return Math.round(parsed * 100) / 100;
43
+ }
44
+
45
+ export function formatCurrency(value: number | null, options?: {
46
+ compact?: boolean;
47
+ showSymbol?: boolean;
48
+ }): string {
49
+ if (value === null) {
50
+ return 'None';
51
+ }
52
+
53
+ const { compact = false, showSymbol = true } = options ?? {};
54
+ const symbol = showSymbol ? '$' : '';
55
+
56
+ if (compact) {
57
+ if (value >= 1000) return `${symbol}${(value / 1000).toFixed(1)}k`;
58
+ if (value >= 100) return `${symbol}${Math.round(value)}`;
59
+ if (value >= 10) return `${symbol}${value.toFixed(1)}`;
60
+ return `${symbol}${value.toFixed(2)}`;
61
+ }
62
+
63
+ return `${symbol}${value.toLocaleString('en-US', {
64
+ minimumFractionDigits: 2,
65
+ maximumFractionDigits: 2
66
+ })}`;
67
+ }
68
+
69
+ export function formatBudgetDisplay(value: number | null): string {
70
+ if (value === null) {
71
+ return 'None';
72
+ }
73
+ return formatCurrency(value);
74
+ }
75
+
76
+ export function isValidCurrencyInput(input: string): boolean {
77
+ if (input.trim() === '') return true;
78
+ return parseCurrencyInput(input) !== null;
79
+ }
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const VERSION = '0.2.2'; // x-release-please-version