ai-wrapped 0.0.1

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 (64) hide show
  1. package/.0spec/config.toml +50 -0
  2. package/.0spec/flows/ai-stats-build.toml +291 -0
  3. package/.0spec/flows/ai-stats-fix.toml +285 -0
  4. package/.0spec/flows/ai-stats.toml +400 -0
  5. package/.github/workflows/publish.yml +28 -0
  6. package/CLAUDE.md +111 -0
  7. package/README.md +64 -0
  8. package/bun.lock +635 -0
  9. package/electrobun.config.ts +25 -0
  10. package/package.json +36 -0
  11. package/public/tray-icon.png +0 -0
  12. package/src/bun/aggregator.test.ts +49 -0
  13. package/src/bun/aggregator.ts +130 -0
  14. package/src/bun/discovery/claude.ts +18 -0
  15. package/src/bun/discovery/codex.ts +20 -0
  16. package/src/bun/discovery/copilot.ts +13 -0
  17. package/src/bun/discovery/droid.ts +13 -0
  18. package/src/bun/discovery/gemini.ts +13 -0
  19. package/src/bun/discovery/index.ts +28 -0
  20. package/src/bun/discovery/opencode.ts +13 -0
  21. package/src/bun/discovery/types.ts +13 -0
  22. package/src/bun/discovery/utils.ts +48 -0
  23. package/src/bun/index.ts +722 -0
  24. package/src/bun/normalizer.test.ts +101 -0
  25. package/src/bun/normalizer.ts +454 -0
  26. package/src/bun/parsers/claude.ts +234 -0
  27. package/src/bun/parsers/codex.test.ts +180 -0
  28. package/src/bun/parsers/codex.ts +435 -0
  29. package/src/bun/parsers/copilot.ts +4 -0
  30. package/src/bun/parsers/droid.ts +4 -0
  31. package/src/bun/parsers/gemini.ts +4 -0
  32. package/src/bun/parsers/generic.test.ts +97 -0
  33. package/src/bun/parsers/generic.ts +260 -0
  34. package/src/bun/parsers/index.ts +37 -0
  35. package/src/bun/parsers/opencode.ts +4 -0
  36. package/src/bun/parsers/types.ts +23 -0
  37. package/src/bun/pricing.ts +52 -0
  38. package/src/bun/scan.ts +77 -0
  39. package/src/bun/session-schema.ts +1 -0
  40. package/src/bun/store.ts +283 -0
  41. package/src/mainview/App.tsx +42 -0
  42. package/src/mainview/components/AgentBadge.tsx +17 -0
  43. package/src/mainview/components/Dashboard.tsx +229 -0
  44. package/src/mainview/components/DashboardCharts.tsx +499 -0
  45. package/src/mainview/components/EmptyState.tsx +17 -0
  46. package/src/mainview/components/Sidebar.tsx +30 -0
  47. package/src/mainview/components/StatsCards.tsx +118 -0
  48. package/src/mainview/hooks/useDashboardData.ts +315 -0
  49. package/src/mainview/hooks/useRPC.ts +29 -0
  50. package/src/mainview/index.css +195 -0
  51. package/src/mainview/index.html +12 -0
  52. package/src/mainview/index.ts +12 -0
  53. package/src/mainview/lib/constants.ts +32 -0
  54. package/src/mainview/lib/formatters.ts +82 -0
  55. package/src/shared/constants.ts +1 -0
  56. package/src/shared/schema.ts +71 -0
  57. package/src/shared/session-types.ts +61 -0
  58. package/src/shared/types.ts +59 -0
  59. package/src/types/electrobun-bun.d.ts +117 -0
  60. package/src/types/electrobun-root.d.ts +3 -0
  61. package/src/types/electrobun-view.d.ts +38 -0
  62. package/tsconfig.json +18 -0
  63. package/tsconfig.typecheck.json +11 -0
  64. package/vite.config.ts +23 -0
@@ -0,0 +1,71 @@
1
+ export type SessionSource = "claude" | "codex" | "gemini" | "opencode" | "droid" | "copilot";
2
+
3
+ export const SESSION_SOURCES: SessionSource[] = ["claude", "codex", "gemini", "opencode", "droid", "copilot"];
4
+
5
+ export interface TokenUsage {
6
+ inputTokens: number;
7
+ outputTokens: number;
8
+ cacheReadTokens: number;
9
+ cacheWriteTokens: number;
10
+ reasoningTokens: number;
11
+ }
12
+
13
+ export type { Session, SessionEvent, SessionEventKind, SessionFilters, SessionSortBy, SessionSortDir } from "./session-types";
14
+
15
+ export const EMPTY_TOKEN_USAGE: TokenUsage = {
16
+ inputTokens: 0,
17
+ outputTokens: 0,
18
+ cacheReadTokens: 0,
19
+ cacheWriteTokens: 0,
20
+ reasoningTokens: 0,
21
+ };
22
+
23
+ export interface DailyAggregate {
24
+ date: string;
25
+ source: SessionSource | "all";
26
+ model: string | "all";
27
+ sessionCount: number;
28
+ messageCount: number;
29
+ toolCallCount: number;
30
+ tokens: TokenUsage;
31
+ costUsd: number;
32
+ totalDurationMs: number;
33
+ }
34
+
35
+ export interface DashboardSummary {
36
+ totals: {
37
+ sessions: number;
38
+ events: number;
39
+ messages: number;
40
+ toolCalls: number;
41
+ tokens: TokenUsage;
42
+ costUsd: number;
43
+ durationMs: number;
44
+ };
45
+ byAgent: Record<
46
+ SessionSource,
47
+ {
48
+ sessions: number;
49
+ events: number;
50
+ tokens: TokenUsage;
51
+ costUsd: number;
52
+ }
53
+ >;
54
+ byModel: Array<{
55
+ model: string;
56
+ sessions: number;
57
+ tokens: TokenUsage;
58
+ costUsd: number;
59
+ }>;
60
+ dailyTimeline: DailyAggregate[];
61
+ topRepos: Array<{ repo: string; sessions: number; costUsd: number }>;
62
+ topTools: Array<{ tool: string; count: number }>;
63
+ }
64
+
65
+ export interface TrayStats {
66
+ todayTokens: number;
67
+ todayCost: number;
68
+ todaySessions: number;
69
+ todayEvents: number;
70
+ activeSessions: number;
71
+ }
@@ -0,0 +1,61 @@
1
+ import type { SessionSource, TokenUsage } from "./schema";
2
+
3
+ export type SessionEventKind = "user" | "assistant" | "tool_call" | "tool_result" | "error" | "meta";
4
+
5
+ export interface SessionEvent {
6
+ id: string;
7
+ sessionId: string;
8
+ kind: SessionEventKind;
9
+ timestamp: string | null;
10
+ role: string | null;
11
+ text: string | null;
12
+ toolName: string | null;
13
+ toolInput: string | null;
14
+ toolOutput: string | null;
15
+ model: string | null;
16
+ parentId: string | null;
17
+ messageId: string | null;
18
+ isDelta: boolean;
19
+ tokens: TokenUsage | null;
20
+ costUsd: number | null;
21
+ }
22
+
23
+ export interface Session {
24
+ id: string;
25
+ source: SessionSource;
26
+ filePath: string;
27
+ fileSizeBytes: number;
28
+ startTime: string | null;
29
+ endTime: string | null;
30
+ durationMs: number | null;
31
+ title: string | null;
32
+ model: string | null;
33
+ cwd: string | null;
34
+ repoName: string | null;
35
+ gitBranch: string | null;
36
+ cliVersion: string | null;
37
+ eventCount: number;
38
+ messageCount: number;
39
+ totalTokens: TokenUsage;
40
+ totalCostUsd: number | null;
41
+ toolCallCount: number;
42
+ isHousekeeping: boolean;
43
+ parsedAt: string;
44
+ }
45
+
46
+ export type SessionSortBy = "date" | "cost" | "tokens" | "duration";
47
+ export type SessionSortDir = "asc" | "desc";
48
+
49
+ export interface SessionFilters {
50
+ query: string;
51
+ sources: SessionSource[];
52
+ models: string[];
53
+ dateFrom: string | null;
54
+ dateTo: string | null;
55
+ repoName: string | null;
56
+ minCost: number | null;
57
+ sortBy: SessionSortBy;
58
+ sortDir: SessionSortDir;
59
+ offset: number;
60
+ limit: number;
61
+ }
@@ -0,0 +1,59 @@
1
+ import type { RPCSchema } from "electrobun/bun";
2
+ import type { DailyAggregate, DashboardSummary, SessionSource, TrayStats } from "./schema";
3
+
4
+ export interface AppSettings {
5
+ scanOnLaunch: boolean;
6
+ scanIntervalMinutes: number;
7
+ theme: "system" | "light" | "dark";
8
+ customPaths: Partial<Record<SessionSource, string>>;
9
+ }
10
+
11
+ export type AIStatsRPC = {
12
+ bun: RPCSchema<{
13
+ requests: {
14
+ getDashboardSummary: {
15
+ params: { dateFrom?: string; dateTo?: string };
16
+ response: DashboardSummary;
17
+ };
18
+ getDailyTimeline: {
19
+ params: { dateFrom: string; dateTo: string; source?: SessionSource };
20
+ response: DailyAggregate[];
21
+ };
22
+ triggerScan: {
23
+ params: { fullScan?: boolean };
24
+ response: { scanned: number; total: number };
25
+ };
26
+ getScanStatus: {
27
+ params: {};
28
+ response: { isScanning: boolean; lastScanAt: string | null; sessionCount: number };
29
+ };
30
+ getTrayStats: {
31
+ params: {};
32
+ response: TrayStats;
33
+ };
34
+ getSettings: {
35
+ params: {};
36
+ response: AppSettings;
37
+ };
38
+ updateSettings: {
39
+ params: Partial<AppSettings>;
40
+ response: boolean;
41
+ };
42
+ };
43
+ messages: {
44
+ log: { msg: string; level?: "info" | "warn" | "error" };
45
+ };
46
+ }>;
47
+
48
+ webview: RPCSchema<{
49
+ requests: {};
50
+ messages: {
51
+ sessionsUpdated: { scanResult: { scanned: number; total: number } };
52
+ scanProgress: { phase: string; current: number; total: number };
53
+ scanStarted: {};
54
+ scanCompleted: { scanned: number; total: number };
55
+ navigate: { view: "dashboard" | "settings" };
56
+ themeChanged: { theme: AppSettings["theme"] };
57
+ };
58
+ }>;
59
+ };
@@ -0,0 +1,117 @@
1
+ declare module "electrobun/bun" {
2
+ type AnyRequests = Record<string, { params: unknown; response: unknown }>;
3
+ type AnyMessages = Record<string, unknown>;
4
+
5
+ export type RPCSchema<T extends { requests?: unknown; messages?: unknown }> = {
6
+ requests: T extends { requests: infer R } ? R : AnyRequests;
7
+ messages: T extends { messages: infer M } ? M : AnyMessages;
8
+ };
9
+
10
+ export interface ElectrobunRPCSchema {
11
+ bun: { requests: AnyRequests; messages: AnyMessages };
12
+ webview: { requests: AnyRequests; messages: AnyMessages };
13
+ }
14
+
15
+ type Side = "bun" | "webview";
16
+ type OtherSide<S extends Side> = S extends "bun" ? "webview" : "bun";
17
+
18
+ type RequestHandlers<Requests extends AnyRequests> = {
19
+ [K in keyof Requests]?: (
20
+ params: Requests[K]["params"],
21
+ ) => Requests[K]["response"] | Promise<Requests[K]["response"]>;
22
+ };
23
+
24
+ type MessageHandlers<Messages extends AnyMessages> = {
25
+ [K in keyof Messages]?: (payload: Messages[K]) => void;
26
+ } & {
27
+ "*"?:
28
+ | ((messageName: keyof Messages, payload: Messages[keyof Messages]) => void)
29
+ | undefined;
30
+ };
31
+
32
+ type RPCInstance<Schema extends ElectrobunRPCSchema, S extends Side> = {
33
+ request: {
34
+ [K in keyof Schema[OtherSide<S>]["requests"]]: (
35
+ params: Schema[OtherSide<S>]["requests"][K]["params"],
36
+ ) => Promise<Schema[OtherSide<S>]["requests"][K]["response"]>;
37
+ };
38
+ send: {
39
+ [K in keyof Schema[OtherSide<S>]["messages"]]: (
40
+ payload: Schema[OtherSide<S>]["messages"][K],
41
+ ) => void;
42
+ };
43
+ addMessageListener: (
44
+ message: keyof Schema[S]["messages"] | "*",
45
+ listener: (...args: unknown[]) => void,
46
+ ) => void;
47
+ removeMessageListener: (
48
+ message: keyof Schema[S]["messages"] | "*",
49
+ listener: (...args: unknown[]) => void,
50
+ ) => void;
51
+ };
52
+
53
+ export type MenuItemConfig = {
54
+ type?: string;
55
+ label?: string;
56
+ tooltip?: string;
57
+ action?: string;
58
+ role?: string;
59
+ data?: unknown;
60
+ submenu?: Array<MenuItemConfig>;
61
+ enabled?: boolean;
62
+ checked?: boolean;
63
+ hidden?: boolean;
64
+ accelerator?: string;
65
+ };
66
+
67
+ export type ApplicationMenuItemConfig = MenuItemConfig;
68
+
69
+ export class BrowserView {
70
+ static defineRPC<Schema extends ElectrobunRPCSchema>(config: {
71
+ handlers: {
72
+ requests?: RequestHandlers<Schema["bun"]["requests"]>;
73
+ messages?: MessageHandlers<Schema["bun"]["messages"]>;
74
+ };
75
+ maxRequestTime?: number;
76
+ }): RPCInstance<Schema, "bun">;
77
+ }
78
+
79
+ export class BrowserWindow<T = unknown> {
80
+ constructor(options?: Record<string, unknown>);
81
+ readonly webview: { rpc?: T };
82
+ show(): void;
83
+ focus(): void;
84
+ minimize(): void;
85
+ unminimize(): void;
86
+ isMinimized(): boolean;
87
+ on(name: string, handler: (event: unknown) => void): void;
88
+ }
89
+
90
+ export class Tray {
91
+ constructor(options?: {
92
+ title?: string;
93
+ image?: string;
94
+ template?: boolean;
95
+ width?: number;
96
+ height?: number;
97
+ });
98
+ setTitle(title: string): void;
99
+ setImage(path: string): void;
100
+ setMenu(menu: Array<MenuItemConfig>): void;
101
+ on(name: "tray-clicked", handler: (event: unknown) => void): void;
102
+ remove(): void;
103
+ }
104
+
105
+ export const ApplicationMenu: {
106
+ setApplicationMenu: (menu: Array<ApplicationMenuItemConfig>) => void;
107
+ on: (name: "application-menu-clicked", handler: (event: unknown) => void) => void;
108
+ };
109
+
110
+ export const Utils: {
111
+ quit: () => void;
112
+ };
113
+
114
+ export const PATHS: {
115
+ VIEWS_FOLDER: string;
116
+ };
117
+ }
@@ -0,0 +1,3 @@
1
+ declare module "electrobun" {
2
+ export type ElectrobunConfig = Record<string, unknown>;
3
+ }
@@ -0,0 +1,38 @@
1
+ declare module "electrobun/view" {
2
+ import type { ElectrobunRPCSchema } from "electrobun/bun";
3
+
4
+ type RPCInstance<Schema extends ElectrobunRPCSchema> = {
5
+ request: {
6
+ [K in keyof Schema["bun"]["requests"]]: (
7
+ params: Schema["bun"]["requests"][K]["params"],
8
+ ) => Promise<Schema["bun"]["requests"][K]["response"]>;
9
+ };
10
+ send: {
11
+ [K in keyof Schema["bun"]["messages"]]: (
12
+ payload: Schema["bun"]["messages"][K],
13
+ ) => void;
14
+ };
15
+ addMessageListener: (
16
+ message: keyof Schema["webview"]["messages"] | "*",
17
+ listener: (...args: unknown[]) => void,
18
+ ) => void;
19
+ removeMessageListener: (
20
+ message: keyof Schema["webview"]["messages"] | "*",
21
+ listener: (...args: unknown[]) => void,
22
+ ) => void;
23
+ };
24
+
25
+ export class Electroview<Schema extends ElectrobunRPCSchema = ElectrobunRPCSchema> {
26
+ constructor(config: {
27
+ rpc: RPCInstance<Schema>;
28
+ });
29
+
30
+ static defineRPC<Schema extends ElectrobunRPCSchema>(config: {
31
+ handlers: {
32
+ requests?: Record<string, (params: unknown) => unknown>;
33
+ messages?: Record<string, (payload: unknown) => void>;
34
+ };
35
+ maxRequestTime?: number;
36
+ }): RPCInstance<Schema>;
37
+ }
38
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "noEmit": true,
10
+ "skipLibCheck": true,
11
+ "baseUrl": ".",
12
+ "paths": {
13
+ "@shared/*": ["src/shared/*"]
14
+ },
15
+ "types": ["bun"]
16
+ },
17
+ "include": ["src", "electrobun.config.ts", "vite.config.ts"]
18
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "paths": {
5
+ "@shared/*": ["src/shared/*"],
6
+ "electrobun": ["src/types/electrobun-root.d.ts"],
7
+ "electrobun/bun": ["src/types/electrobun-bun.d.ts"],
8
+ "electrobun/view": ["src/types/electrobun-view.d.ts"]
9
+ }
10
+ }
11
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import tailwindcss from "@tailwindcss/vite";
4
+ import { fileURLToPath, URL } from "node:url";
5
+
6
+ export default defineConfig({
7
+ plugins: [react(), tailwindcss()],
8
+ root: "src/mainview",
9
+ publicDir: "../../public",
10
+ build: {
11
+ outDir: "../../dist",
12
+ emptyOutDir: true,
13
+ },
14
+ server: {
15
+ port: 5173,
16
+ strictPort: true,
17
+ },
18
+ resolve: {
19
+ alias: {
20
+ "@shared": fileURLToPath(new URL("./src/shared", import.meta.url)),
21
+ },
22
+ },
23
+ });