@tokentop/ttop 0.2.2 → 0.3.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 (158) hide show
  1. package/README.md +2 -5
  2. package/package.json +8 -5
  3. package/src/agents/aggregator.ts +39 -19
  4. package/src/agents/costing.ts +11 -9
  5. package/src/agents/index.ts +7 -9
  6. package/src/agents/types.ts +2 -2
  7. package/src/cli.ts +28 -23
  8. package/src/config/schema.test.ts +42 -40
  9. package/src/config/schema.ts +26 -26
  10. package/src/demo/simulator.ts +161 -122
  11. package/src/plugins/agents/index.ts +2 -2
  12. package/src/plugins/auth-sources.ts +42 -38
  13. package/src/plugins/lifecycle.ts +32 -32
  14. package/src/plugins/loader.test.ts +49 -49
  15. package/src/plugins/loader.ts +67 -63
  16. package/src/plugins/notification-bus.ts +21 -16
  17. package/src/plugins/notifications/index.ts +2 -2
  18. package/src/plugins/notifications/terminal-bell.ts +29 -23
  19. package/src/plugins/notifications/visual-flash.ts +21 -19
  20. package/src/plugins/npm-installer.ts +23 -25
  21. package/src/plugins/plugin-context-factory.ts +10 -10
  22. package/src/plugins/plugin-host.ts +12 -5
  23. package/src/plugins/providers/anthropic.ts +64 -57
  24. package/src/plugins/providers/antigravity.ts +81 -69
  25. package/src/plugins/providers/codex.ts +42 -42
  26. package/src/plugins/providers/gemini.ts +106 -87
  27. package/src/plugins/providers/github-copilot.ts +49 -45
  28. package/src/plugins/providers/index.ts +10 -12
  29. package/src/plugins/providers/minimax.ts +50 -44
  30. package/src/plugins/providers/openai-api.ts +82 -62
  31. package/src/plugins/providers/opencode-zen.ts +46 -39
  32. package/src/plugins/providers/perplexity.ts +55 -46
  33. package/src/plugins/providers/zai.ts +66 -57
  34. package/src/plugins/registry.ts +47 -43
  35. package/src/plugins/sandbox-guard.test.ts +45 -48
  36. package/src/plugins/sandbox-guard.ts +20 -25
  37. package/src/plugins/sandbox.ts +26 -48
  38. package/src/plugins/themes/catppuccin-latte.ts +29 -29
  39. package/src/plugins/themes/catppuccin-mocha.ts +29 -29
  40. package/src/plugins/themes/claude-code.ts +29 -29
  41. package/src/plugins/themes/dracula.ts +29 -29
  42. package/src/plugins/themes/github-light.ts +29 -29
  43. package/src/plugins/themes/gruvbox-dark.ts +29 -29
  44. package/src/plugins/themes/gruvbox-light.ts +29 -29
  45. package/src/plugins/themes/index.ts +15 -15
  46. package/src/plugins/themes/kanagawa.ts +29 -29
  47. package/src/plugins/themes/nord.ts +29 -29
  48. package/src/plugins/themes/one-dark.ts +29 -29
  49. package/src/plugins/themes/opencode.ts +31 -31
  50. package/src/plugins/themes/rose-pine-dawn.ts +29 -29
  51. package/src/plugins/themes/rose-pine.ts +29 -29
  52. package/src/plugins/themes/solarized-light.ts +29 -29
  53. package/src/plugins/themes/tokyo-night.ts +29 -29
  54. package/src/plugins/types/agent.ts +5 -5
  55. package/src/plugins/types/base.ts +35 -24
  56. package/src/plugins/types/index.ts +9 -9
  57. package/src/plugins/types/notification.ts +12 -12
  58. package/src/plugins/types/provider.ts +12 -8
  59. package/src/plugins/types/theme.ts +31 -23
  60. package/src/plugins/update-checker.ts +6 -6
  61. package/src/pricing/estimator.test.ts +39 -44
  62. package/src/pricing/estimator.ts +7 -13
  63. package/src/pricing/fallback.test.ts +30 -34
  64. package/src/pricing/fallback.ts +54 -27
  65. package/src/pricing/index.ts +18 -12
  66. package/src/pricing/models-dev.ts +6 -6
  67. package/src/storage/database.ts +56 -46
  68. package/src/storage/db.ts +12 -12
  69. package/src/storage/index.ts +44 -45
  70. package/src/storage/migrations/index.ts +2 -2
  71. package/src/storage/paths.ts +13 -13
  72. package/src/storage/repos/agentSessions.ts +33 -23
  73. package/src/storage/repos/providerSnapshots.ts +5 -5
  74. package/src/storage/repos/usageEvents.ts +40 -28
  75. package/src/storage/types.ts +5 -5
  76. package/src/tui/App.tsx +180 -137
  77. package/src/tui/components/CommandPalette.tsx +46 -34
  78. package/src/tui/components/DebugConsole.tsx +39 -33
  79. package/src/tui/components/DebugPanel.tsx +295 -177
  80. package/src/tui/components/FooterHints.tsx +2 -2
  81. package/src/tui/components/GhostProviderCard.tsx +37 -35
  82. package/src/tui/components/Header.tsx +68 -35
  83. package/src/tui/components/HelpOverlay.tsx +99 -29
  84. package/src/tui/components/InlineGauge.tsx +10 -7
  85. package/src/tui/components/InlineSparkline.tsx +17 -11
  86. package/src/tui/components/KpiStrip.tsx +128 -98
  87. package/src/tui/components/LimitGauge.tsx +96 -59
  88. package/src/tui/components/ModalBackdrop.tsx +4 -4
  89. package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
  90. package/src/tui/components/ProviderCard.tsx +232 -226
  91. package/src/tui/components/ProviderDetailPanel.tsx +133 -58
  92. package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
  93. package/src/tui/components/ProvidersList.tsx +215 -126
  94. package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
  95. package/src/tui/components/SessionsTable.tsx +313 -166
  96. package/src/tui/components/SettingsModal.tsx +231 -214
  97. package/src/tui/components/SidebarBreakdown.tsx +30 -16
  98. package/src/tui/components/Skeleton.tsx +24 -17
  99. package/src/tui/components/SmartSidebar.tsx +360 -258
  100. package/src/tui/components/Sparkline.tsx +64 -52
  101. package/src/tui/components/Spinner.tsx +3 -3
  102. package/src/tui/components/StatusBar.tsx +27 -27
  103. package/src/tui/components/ThemePicker.tsx +67 -66
  104. package/src/tui/components/Toast.tsx +9 -6
  105. package/src/tui/components/UsageGauge.tsx +10 -13
  106. package/src/tui/components/index.ts +5 -5
  107. package/src/tui/contexts/AgentSessionContext.tsx +193 -144
  108. package/src/tui/contexts/ConfigContext.tsx +15 -13
  109. package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
  110. package/src/tui/contexts/DemoModeContext.tsx +22 -15
  111. package/src/tui/contexts/DrawerContext.tsx +11 -9
  112. package/src/tui/contexts/InputContext.tsx +2 -2
  113. package/src/tui/contexts/LogContext.tsx +62 -47
  114. package/src/tui/contexts/PluginContext.tsx +232 -183
  115. package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
  116. package/src/tui/contexts/StorageContext.tsx +151 -117
  117. package/src/tui/contexts/ThemeContext.tsx +21 -16
  118. package/src/tui/contexts/TimeWindowContext.tsx +72 -61
  119. package/src/tui/contexts/ToastContext.tsx +10 -7
  120. package/src/tui/contexts/index.ts +9 -5
  121. package/src/tui/createApp.tsx +8 -12
  122. package/src/tui/debug/captureFrame.ts +25 -28
  123. package/src/tui/debug/snapshot.tsx +179 -162
  124. package/src/tui/driver/assertions.ts +30 -22
  125. package/src/tui/driver/cli.ts +231 -221
  126. package/src/tui/driver/coverage.ts +27 -44
  127. package/src/tui/driver/demo-snapshot.ts +33 -33
  128. package/src/tui/driver/diff.ts +26 -32
  129. package/src/tui/driver/driver.ts +43 -48
  130. package/src/tui/driver/index.ts +36 -40
  131. package/src/tui/driver/recorder.ts +37 -48
  132. package/src/tui/driver/test-workflow.ts +51 -51
  133. package/src/tui/driver/test.ts +25 -25
  134. package/src/tui/hooks/index.ts +24 -20
  135. package/src/tui/hooks/useAnimatedValue.ts +13 -18
  136. package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
  137. package/src/tui/hooks/useDashboardState.ts +26 -24
  138. package/src/tui/hooks/useEmaActivity.ts +82 -74
  139. package/src/tui/hooks/useEntranceAnimation.ts +11 -11
  140. package/src/tui/hooks/useExitAnimation.ts +3 -3
  141. package/src/tui/hooks/usePulse.ts +4 -4
  142. package/src/tui/hooks/useSafeRenderer.ts +5 -5
  143. package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
  144. package/src/tui/hooks/useValueFlash.ts +10 -14
  145. package/src/tui/index.tsx +29 -6
  146. package/src/tui/shutdown.ts +19 -0
  147. package/src/tui/utils/providerColor.ts +1 -1
  148. package/src/tui/views/Dashboard.tsx +127 -102
  149. package/src/tui/views/HistoricalTrendsView.tsx +579 -250
  150. package/src/tui/views/ProjectsView.tsx +466 -237
  151. package/src/tui/views/RealTimeDashboard.tsx +150 -111
  152. package/src/tui/views/SettingsView.tsx +222 -191
  153. package/src/tui/views/index.ts +2 -2
  154. package/src/types/opentui-test-utils.d.ts +7 -7
  155. package/src/utils/clipboard.ts +29 -29
  156. package/src/utils/currency.test.ts +53 -53
  157. package/src/utils/currency.ts +35 -32
  158. package/src/version.ts +1 -1
@@ -1,37 +1,34 @@
1
- import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
2
  import {
3
3
  deepFreeze,
4
4
  getActivePluginGuard,
5
5
  installGlobalFetchGuard,
6
6
  runInPluginGuard,
7
- } from './sandbox-guard.ts';
8
- import { PluginPermissionError } from './types/base.ts';
7
+ } from "./sandbox-guard.ts";
8
+ import { PluginPermissionError } from "./types/base.ts";
9
9
 
10
10
  const originalFetch = globalThis.fetch;
11
11
  const fetchCalls: string[] = [];
12
12
 
13
13
  const mockFetch: typeof fetch = Object.assign(
14
14
  (input: string | URL | Request, _init?: RequestInit): Promise<Response> => {
15
- const url = typeof input === 'string'
16
- ? input
17
- : input instanceof URL
18
- ? input.toString()
19
- : input.url;
15
+ const url =
16
+ typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
20
17
  fetchCalls.push(url);
21
- return Promise.resolve(new Response('ok', { status: 200 }));
18
+ return Promise.resolve(new Response("ok", { status: 200 }));
22
19
  },
23
20
  { preconnect: originalFetch.preconnect },
24
21
  );
25
22
 
26
- describe('deepFreeze', () => {
27
- test('freezes a plain object', () => {
23
+ describe("deepFreeze", () => {
24
+ test("freezes a plain object", () => {
28
25
  const obj = { a: 1 };
29
26
  deepFreeze(obj);
30
27
 
31
28
  expect(Object.isFrozen(obj)).toBe(true);
32
29
  });
33
30
 
34
- test('freezes nested objects recursively', () => {
31
+ test("freezes nested objects recursively", () => {
35
32
  const obj = {
36
33
  top: {
37
34
  child: {
@@ -47,19 +44,19 @@ describe('deepFreeze', () => {
47
44
  expect(Object.isFrozen(obj.top.child)).toBe(true);
48
45
  });
49
46
 
50
- test('returns the same reference', () => {
47
+ test("returns the same reference", () => {
51
48
  const obj = { nested: { value: 1 } };
52
49
  const frozen = deepFreeze(obj);
53
50
 
54
51
  expect(frozen).toBe(obj);
55
52
  });
56
53
 
57
- test('handles null and undefined gracefully', () => {
54
+ test("handles null and undefined gracefully", () => {
58
55
  expect(deepFreeze(null)).toBeNull();
59
56
  expect(deepFreeze(undefined)).toBeUndefined();
60
57
  });
61
58
 
62
- test('handles circular references without infinite loop', () => {
59
+ test("handles circular references without infinite loop", () => {
63
60
  const obj: { self?: unknown; nested: { parent?: unknown } } = {
64
61
  nested: {},
65
62
  };
@@ -73,7 +70,7 @@ describe('deepFreeze', () => {
73
70
  expect(Object.isFrozen(obj.nested)).toBe(true);
74
71
  });
75
72
 
76
- test('handles already-frozen objects', () => {
73
+ test("handles already-frozen objects", () => {
77
74
  const obj = Object.freeze({ already: true });
78
75
 
79
76
  const frozen = deepFreeze(obj);
@@ -83,44 +80,44 @@ describe('deepFreeze', () => {
83
80
  });
84
81
  });
85
82
 
86
- describe('runInPluginGuard and getActivePluginGuard', () => {
87
- test('returns undefined outside guard', () => {
83
+ describe("runInPluginGuard and getActivePluginGuard", () => {
84
+ test("returns undefined outside guard", () => {
88
85
  expect(getActivePluginGuard()).toBeUndefined();
89
86
  });
90
87
 
91
- test('returns plugin context inside guard', () => {
92
- const context = runInPluginGuard('plugin-a', {}, () => getActivePluginGuard());
88
+ test("returns plugin context inside guard", () => {
89
+ const context = runInPluginGuard("plugin-a", {}, () => getActivePluginGuard());
93
90
 
94
91
  expect(context).toBeDefined();
95
- expect(context?.pluginId).toBe('plugin-a');
92
+ expect(context?.pluginId).toBe("plugin-a");
96
93
  });
97
94
 
98
- test('preserves context across async/await', async () => {
99
- const context = await runInPluginGuard('plugin-async', {}, async () => {
95
+ test("preserves context across async/await", async () => {
96
+ const context = await runInPluginGuard("plugin-async", {}, async () => {
100
97
  await Promise.resolve();
101
98
  return getActivePluginGuard();
102
99
  });
103
100
 
104
101
  expect(context).toBeDefined();
105
- expect(context?.pluginId).toBe('plugin-async');
102
+ expect(context?.pluginId).toBe("plugin-async");
106
103
  });
107
104
 
108
- test('nested guards use the innermost context', () => {
109
- const contexts = runInPluginGuard('outer-plugin', {}, () => {
105
+ test("nested guards use the innermost context", () => {
106
+ const contexts = runInPluginGuard("outer-plugin", {}, () => {
110
107
  const outerBefore = getActivePluginGuard();
111
- const inner = runInPluginGuard('inner-plugin', {}, () => getActivePluginGuard());
108
+ const inner = runInPluginGuard("inner-plugin", {}, () => getActivePluginGuard());
112
109
  const outerAfter = getActivePluginGuard();
113
110
 
114
111
  return { outerBefore, inner, outerAfter };
115
112
  });
116
113
 
117
- expect(contexts.outerBefore?.pluginId).toBe('outer-plugin');
118
- expect(contexts.inner?.pluginId).toBe('inner-plugin');
119
- expect(contexts.outerAfter?.pluginId).toBe('outer-plugin');
114
+ expect(contexts.outerBefore?.pluginId).toBe("outer-plugin");
115
+ expect(contexts.inner?.pluginId).toBe("inner-plugin");
116
+ expect(contexts.outerAfter?.pluginId).toBe("outer-plugin");
120
117
  });
121
118
  });
122
119
 
123
- describe('installGlobalFetchGuard', () => {
120
+ describe("installGlobalFetchGuard", () => {
124
121
  beforeAll(() => {
125
122
  globalThis.fetch = mockFetch;
126
123
  installGlobalFetchGuard();
@@ -130,62 +127,62 @@ describe('installGlobalFetchGuard', () => {
130
127
  globalThis.fetch = originalFetch;
131
128
  });
132
129
 
133
- test('outside guard, fetch calls pass through', async () => {
130
+ test("outside guard, fetch calls pass through", async () => {
134
131
  fetchCalls.length = 0;
135
132
 
136
- const response = await fetch('https://outside-guard.example');
133
+ const response = await fetch("https://outside-guard.example");
137
134
 
138
135
  expect(response.status).toBe(200);
139
136
  expect(fetchCalls).toHaveLength(1);
140
- expect(fetchCalls[0]).toContain('outside-guard.example');
137
+ expect(fetchCalls[0]).toContain("outside-guard.example");
141
138
  });
142
139
 
143
- test('inside guard with no network permission throws PluginPermissionError', () => {
140
+ test("inside guard with no network permission throws PluginPermissionError", () => {
144
141
  expect(() => {
145
- runInPluginGuard('plugin-no-network', {}, () => {
146
- void fetch('https://blocked.example');
142
+ runInPluginGuard("plugin-no-network", {}, () => {
143
+ void fetch("https://blocked.example");
147
144
  });
148
145
  }).toThrow(PluginPermissionError);
149
146
  });
150
147
 
151
- test('inside guard with allowedDomains blocks unlisted domains', () => {
148
+ test("inside guard with allowedDomains blocks unlisted domains", () => {
152
149
  expect(() => {
153
150
  runInPluginGuard(
154
- 'plugin-allowlist',
151
+ "plugin-allowlist",
155
152
  {
156
153
  network: {
157
154
  enabled: true,
158
- allowedDomains: ['allowed.example'],
155
+ allowedDomains: ["allowed.example"],
159
156
  },
160
157
  },
161
158
  () => {
162
- void fetch('https://not-allowed.example/path');
159
+ void fetch("https://not-allowed.example/path");
163
160
  },
164
161
  );
165
162
  }).toThrow(PluginPermissionError);
166
163
  });
167
164
 
168
- test('inside guard with allowedDomains allows listed domains and subdomains', async () => {
165
+ test("inside guard with allowedDomains allows listed domains and subdomains", async () => {
169
166
  fetchCalls.length = 0;
170
167
 
171
168
  const response = await runInPluginGuard(
172
- 'plugin-allowlist',
169
+ "plugin-allowlist",
173
170
  {
174
171
  network: {
175
172
  enabled: true,
176
- allowedDomains: ['allowed.example'],
173
+ allowedDomains: ["allowed.example"],
177
174
  },
178
175
  },
179
176
  async () => {
180
- const rootResponse = await fetch('https://allowed.example/path');
181
- await fetch('https://api.allowed.example/path');
177
+ const rootResponse = await fetch("https://allowed.example/path");
178
+ await fetch("https://api.allowed.example/path");
182
179
  return rootResponse;
183
180
  },
184
181
  );
185
182
 
186
183
  expect(response.status).toBe(200);
187
184
  expect(fetchCalls).toHaveLength(2);
188
- expect(fetchCalls[0]).toContain('allowed.example/path');
189
- expect(fetchCalls[1]).toContain('api.allowed.example/path');
185
+ expect(fetchCalls[0]).toContain("allowed.example/path");
186
+ expect(fetchCalls[1]).toContain("api.allowed.example/path");
190
187
  });
191
188
  });
@@ -11,9 +11,9 @@
11
11
  * 2. **Deep-freeze** — prevents plugins from mutating the PluginContext.
12
12
  */
13
13
 
14
- import { AsyncLocalStorage } from 'async_hooks';
15
- import { PluginPermissionError, type PluginPermissions } from './types/base.ts';
16
- import { createPluginLogger } from './sandbox.ts';
14
+ import { AsyncLocalStorage } from "async_hooks";
15
+ import { createPluginLogger } from "./sandbox.ts";
16
+ import { PluginPermissionError, type PluginPermissions } from "./types/base.ts";
17
17
 
18
18
  // ---------------------------------------------------------------------------
19
19
  // Plugin execution context (AsyncLocalStorage)
@@ -68,10 +68,7 @@ export function installGlobalFetchGuard(): void {
68
68
  const originalFetch = globalThis.fetch;
69
69
 
70
70
  const guardedFetch: typeof fetch = Object.assign(
71
- function guardedFetch(
72
- input: string | URL | Request,
73
- init?: RequestInit,
74
- ): Promise<Response> {
71
+ function guardedFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {
75
72
  const guard = pluginGuardStorage.getStore();
76
73
 
77
74
  if (!guard) {
@@ -82,34 +79,32 @@ export function installGlobalFetchGuard(): void {
82
79
 
83
80
  if (!permissions.network?.enabled) {
84
81
  const log = createPluginLogger(pluginId);
85
- log.error('Network access blocked — plugin has not declared network permissions');
86
- throw new PluginPermissionError(
87
- pluginId,
88
- 'network',
89
- 'Network access not permitted',
90
- );
82
+ log.error("Network access blocked — plugin has not declared network permissions");
83
+ throw new PluginPermissionError(pluginId, "network", "Network access not permitted");
91
84
  }
92
85
 
93
86
  const allowedDomains = permissions.network.allowedDomains;
94
87
  if (allowedDomains && allowedDomains.length > 0) {
95
- const url = typeof input === 'string'
96
- ? new URL(input)
97
- : input instanceof URL
98
- ? input
99
- : new URL(input.url);
88
+ const url =
89
+ typeof input === "string"
90
+ ? new URL(input)
91
+ : input instanceof URL
92
+ ? input
93
+ : new URL(input.url);
100
94
 
101
95
  const isAllowed = allowedDomains.some(
102
- (domain) =>
103
- url.hostname === domain || url.hostname.endsWith(`.${domain}`),
96
+ (domain) => url.hostname === domain || url.hostname.endsWith(`.${domain}`),
104
97
  );
105
98
 
106
99
  if (!isAllowed) {
107
100
  const log = createPluginLogger(pluginId);
108
- log.error(`Network blocked: domain "${url.hostname}" not in allowlist [${allowedDomains.join(', ')}]`);
101
+ log.error(
102
+ `Network blocked: domain "${url.hostname}" not in allowlist [${allowedDomains.join(", ")}]`,
103
+ );
109
104
  throw new PluginPermissionError(
110
105
  pluginId,
111
- 'network',
112
- `Domain "${url.hostname}" not in allowlist: ${allowedDomains.join(', ')}`,
106
+ "network",
107
+ `Domain "${url.hostname}" not in allowlist: ${allowedDomains.join(", ")}`,
113
108
  );
114
109
  }
115
110
  }
@@ -136,7 +131,7 @@ const frozenObjects = new WeakSet<object>();
136
131
  */
137
132
  export function deepFreeze<T>(obj: T): T {
138
133
  if (obj === null || obj === undefined) return obj;
139
- if (typeof obj !== 'object') return obj;
134
+ if (typeof obj !== "object") return obj;
140
135
 
141
136
  const o = obj as object;
142
137
 
@@ -146,7 +141,7 @@ export function deepFreeze<T>(obj: T): T {
146
141
  Object.freeze(o);
147
142
 
148
143
  for (const value of Object.values(o)) {
149
- if (value !== null && typeof value === 'object') {
144
+ if (value !== null && typeof value === "object") {
150
145
  deepFreeze(value);
151
146
  }
152
147
  }
@@ -1,22 +1,18 @@
1
1
  import {
2
- PluginPermissionError,
3
- type PluginPermissions,
4
2
  type PluginHttpClient,
5
3
  type PluginLogger,
6
- } from './types/index.ts';
4
+ PluginPermissionError,
5
+ type PluginPermissions,
6
+ } from "./types/index.ts";
7
7
 
8
8
  export function createSandboxedHttpClient(
9
9
  pluginId: string,
10
- permissions: PluginPermissions
10
+ permissions: PluginPermissions,
11
11
  ): PluginHttpClient {
12
12
  return {
13
13
  async fetch(url: string, init?: RequestInit): Promise<Response> {
14
14
  if (!permissions.network?.enabled) {
15
- throw new PluginPermissionError(
16
- pluginId,
17
- 'network',
18
- 'Network access not permitted'
19
- );
15
+ throw new PluginPermissionError(pluginId, "network", "Network access not permitted");
20
16
  }
21
17
 
22
18
  const urlObj = new URL(url);
@@ -24,16 +20,14 @@ export function createSandboxedHttpClient(
24
20
 
25
21
  if (allowedDomains && allowedDomains.length > 0) {
26
22
  const isAllowed = allowedDomains.some(
27
- (domain) =>
28
- urlObj.hostname === domain ||
29
- urlObj.hostname.endsWith(`.${domain}`)
23
+ (domain) => urlObj.hostname === domain || urlObj.hostname.endsWith(`.${domain}`),
30
24
  );
31
25
 
32
26
  if (!isAllowed) {
33
27
  throw new PluginPermissionError(
34
28
  pluginId,
35
- 'network',
36
- `Domain "${urlObj.hostname}" not in allowlist: ${allowedDomains.join(', ')}`
29
+ "network",
30
+ `Domain "${urlObj.hostname}" not in allowlist: ${allowedDomains.join(", ")}`,
37
31
  );
38
32
  }
39
33
  }
@@ -49,17 +43,17 @@ export function createPluginLogger(pluginId: string): PluginLogger {
49
43
  return {
50
44
  debug(message: string, data?: Record<string, unknown>) {
51
45
  if (process.env.DEBUG) {
52
- console.debug(prefix, message, data ?? '');
46
+ console.debug(prefix, message, data ?? "");
53
47
  }
54
48
  },
55
49
  info(message: string, data?: Record<string, unknown>) {
56
- console.info(prefix, message, data ?? '');
50
+ console.info(prefix, message, data ?? "");
57
51
  },
58
52
  warn(message: string, data?: Record<string, unknown>) {
59
- console.warn(prefix, message, data ?? '');
53
+ console.warn(prefix, message, data ?? "");
60
54
  },
61
55
  error(message: string, data?: Record<string, unknown>) {
62
- console.error(prefix, message, data ?? '');
56
+ console.error(prefix, message, data ?? "");
63
57
  },
64
58
  };
65
59
  }
@@ -67,22 +61,18 @@ export function createPluginLogger(pluginId: string): PluginLogger {
67
61
  export function validateEnvAccess(
68
62
  pluginId: string,
69
63
  permissions: PluginPermissions,
70
- varName: string
64
+ varName: string,
71
65
  ): void {
72
66
  if (!permissions.env?.read) {
73
- throw new PluginPermissionError(
74
- pluginId,
75
- 'env',
76
- 'Environment variable access not permitted'
77
- );
67
+ throw new PluginPermissionError(pluginId, "env", "Environment variable access not permitted");
78
68
  }
79
69
 
80
70
  const allowedVars = permissions.env.vars;
81
71
  if (allowedVars && allowedVars.length > 0 && !allowedVars.includes(varName)) {
82
72
  throw new PluginPermissionError(
83
73
  pluginId,
84
- 'env',
85
- `Environment variable "${varName}" not in allowlist`
74
+ "env",
75
+ `Environment variable "${varName}" not in allowlist`,
86
76
  );
87
77
  }
88
78
  }
@@ -91,48 +81,36 @@ export function validateFilesystemAccess(
91
81
  pluginId: string,
92
82
  permissions: PluginPermissions,
93
83
  path: string,
94
- mode: 'read' | 'write'
84
+ mode: "read" | "write",
95
85
  ): void {
96
86
  const fsPerms = permissions.filesystem;
97
87
 
98
88
  if (!fsPerms) {
99
- throw new PluginPermissionError(
100
- pluginId,
101
- 'filesystem',
102
- 'Filesystem access not permitted'
103
- );
89
+ throw new PluginPermissionError(pluginId, "filesystem", "Filesystem access not permitted");
104
90
  }
105
91
 
106
- if (mode === 'read' && !fsPerms.read) {
107
- throw new PluginPermissionError(
108
- pluginId,
109
- 'filesystem',
110
- 'Filesystem read access not permitted'
111
- );
92
+ if (mode === "read" && !fsPerms.read) {
93
+ throw new PluginPermissionError(pluginId, "filesystem", "Filesystem read access not permitted");
112
94
  }
113
95
 
114
- if (mode === 'write' && !fsPerms.write) {
96
+ if (mode === "write" && !fsPerms.write) {
115
97
  throw new PluginPermissionError(
116
98
  pluginId,
117
- 'filesystem',
118
- 'Filesystem write access not permitted'
99
+ "filesystem",
100
+ "Filesystem write access not permitted",
119
101
  );
120
102
  }
121
103
 
122
104
  const allowedPaths = fsPerms.paths;
123
105
  if (allowedPaths && allowedPaths.length > 0) {
124
- const normalizedPath = path.replace(/^~/, process.env.HOME ?? '');
106
+ const normalizedPath = path.replace(/^~/, process.env.HOME ?? "");
125
107
  const isAllowed = allowedPaths.some((allowed) => {
126
- const normalizedAllowed = allowed.replace(/^~/, process.env.HOME ?? '');
108
+ const normalizedAllowed = allowed.replace(/^~/, process.env.HOME ?? "");
127
109
  return normalizedPath.startsWith(normalizedAllowed);
128
110
  });
129
111
 
130
112
  if (!isAllowed) {
131
- throw new PluginPermissionError(
132
- pluginId,
133
- 'filesystem',
134
- `Path "${path}" not in allowlist`
135
- );
113
+ throw new PluginPermissionError(pluginId, "filesystem", `Path "${path}" not in allowlist`);
136
114
  }
137
115
  }
138
116
  }
@@ -1,45 +1,45 @@
1
- import type { ThemePlugin } from '../types/theme.ts';
1
+ import type { ThemePlugin } from "../types/theme.ts";
2
2
 
3
3
  export const catppuccinLatteTheme: ThemePlugin = {
4
4
  apiVersion: 2,
5
- id: 'catppuccin-latte',
6
- type: 'theme',
7
- name: 'Catppuccin Latte',
8
- family: 'catppuccin',
9
- version: '1.0.0',
5
+ id: "catppuccin-latte",
6
+ type: "theme",
7
+ name: "Catppuccin Latte",
8
+ family: "catppuccin",
9
+ version: "1.0.0",
10
10
  permissions: {},
11
11
 
12
- colorScheme: 'light',
12
+ colorScheme: "light",
13
13
 
14
14
  colors: {
15
- background: '#eff1f5',
16
- foreground: '#e6e9ef',
17
- text: '#4c4f69',
18
- textMuted: '#6c6f85',
19
- textSubtle: '#9ca0b0',
20
- primary: '#1e66f5',
21
- secondary: '#8839ef',
22
- accent: '#179299',
23
- success: '#40a02b',
24
- warning: '#df8e1d',
25
- error: '#d20f39',
26
- info: '#04a5e5',
27
- border: '#bcc0cc',
28
- borderMuted: '#ccd0da',
29
- selection: '#dce0e8',
30
- highlight: '#e6e9ef',
31
- gaugeBackground: '#ccd0da',
32
- gaugeFill: '#1e66f5',
33
- gaugeWarning: '#df8e1d',
34
- gaugeDanger: '#d20f39',
15
+ background: "#eff1f5",
16
+ foreground: "#e6e9ef",
17
+ text: "#4c4f69",
18
+ textMuted: "#6c6f85",
19
+ textSubtle: "#9ca0b0",
20
+ primary: "#1e66f5",
21
+ secondary: "#8839ef",
22
+ accent: "#179299",
23
+ success: "#40a02b",
24
+ warning: "#df8e1d",
25
+ error: "#d20f39",
26
+ info: "#04a5e5",
27
+ border: "#bcc0cc",
28
+ borderMuted: "#ccd0da",
29
+ selection: "#dce0e8",
30
+ highlight: "#e6e9ef",
31
+ gaugeBackground: "#ccd0da",
32
+ gaugeFill: "#1e66f5",
33
+ gaugeWarning: "#df8e1d",
34
+ gaugeDanger: "#d20f39",
35
35
  },
36
36
 
37
37
  components: {
38
38
  header: {
39
- background: '#e6e9ef',
39
+ background: "#e6e9ef",
40
40
  },
41
41
  statusBar: {
42
- background: '#dce0e8',
42
+ background: "#dce0e8",
43
43
  },
44
44
  },
45
45
  };
@@ -1,45 +1,45 @@
1
- import type { ThemePlugin } from '../types/theme.ts';
1
+ import type { ThemePlugin } from "../types/theme.ts";
2
2
 
3
3
  export const catppuccinMochaTheme: ThemePlugin = {
4
4
  apiVersion: 2,
5
- id: 'catppuccin-mocha',
6
- type: 'theme',
7
- name: 'Catppuccin Mocha',
8
- family: 'catppuccin',
9
- version: '1.0.0',
5
+ id: "catppuccin-mocha",
6
+ type: "theme",
7
+ name: "Catppuccin Mocha",
8
+ family: "catppuccin",
9
+ version: "1.0.0",
10
10
  permissions: {},
11
11
 
12
- colorScheme: 'dark',
12
+ colorScheme: "dark",
13
13
 
14
14
  colors: {
15
- background: '#1e1e2e',
16
- foreground: '#313244',
17
- text: '#cdd6f4',
18
- textMuted: '#a6adc8',
19
- textSubtle: '#6c7086',
20
- primary: '#89b4fa',
21
- secondary: '#cba6f7',
22
- accent: '#94e2d5',
23
- success: '#a6e3a1',
24
- warning: '#f9e2af',
25
- error: '#f38ba8',
26
- info: '#89dceb',
27
- border: '#45475a',
28
- borderMuted: '#313244',
29
- selection: '#45475a',
30
- highlight: '#313244',
31
- gaugeBackground: '#313244',
32
- gaugeFill: '#89b4fa',
33
- gaugeWarning: '#f9e2af',
34
- gaugeDanger: '#f38ba8',
15
+ background: "#1e1e2e",
16
+ foreground: "#313244",
17
+ text: "#cdd6f4",
18
+ textMuted: "#a6adc8",
19
+ textSubtle: "#6c7086",
20
+ primary: "#89b4fa",
21
+ secondary: "#cba6f7",
22
+ accent: "#94e2d5",
23
+ success: "#a6e3a1",
24
+ warning: "#f9e2af",
25
+ error: "#f38ba8",
26
+ info: "#89dceb",
27
+ border: "#45475a",
28
+ borderMuted: "#313244",
29
+ selection: "#45475a",
30
+ highlight: "#313244",
31
+ gaugeBackground: "#313244",
32
+ gaugeFill: "#89b4fa",
33
+ gaugeWarning: "#f9e2af",
34
+ gaugeDanger: "#f38ba8",
35
35
  },
36
36
 
37
37
  components: {
38
38
  header: {
39
- background: '#181825',
39
+ background: "#181825",
40
40
  },
41
41
  statusBar: {
42
- background: '#11111b',
42
+ background: "#11111b",
43
43
  },
44
44
  },
45
45
  };