@pranjalmandavkar/opencode-notifier 1.0.0 → 1.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 (2) hide show
  1. package/dist/index.js +2 -1163
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
- // @bun
1
+ import { createRequire } from "node:module";
2
2
  var __create = Object.create;
3
3
  var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
7
  var __toESM = (mod, isNodeMode, target) => {
9
8
  target = mod != null ? __create(__getProtoOf(mod)) : {};
@@ -16,32 +15,8 @@ var __toESM = (mod, isNodeMode, target) => {
16
15
  });
17
16
  return to;
18
17
  };
19
- var __moduleCache = /* @__PURE__ */ new WeakMap;
20
- var __toCommonJS = (from) => {
21
- var entry = __moduleCache.get(from), desc;
22
- if (entry)
23
- return entry;
24
- entry = __defProp({}, "__esModule", { value: true });
25
- if (from && typeof from === "object" || typeof from === "function")
26
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
27
- get: () => from[key],
28
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
29
- }));
30
- __moduleCache.set(from, entry);
31
- return entry;
32
- };
33
18
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __export = (target, all) => {
35
- for (var name in all)
36
- __defProp(target, name, {
37
- get: all[name],
38
- enumerable: true,
39
- configurable: true,
40
- set: (newValue) => all[name] = () => newValue
41
- });
42
- };
43
- var __esm = (fn2, res) => () => (fn2 && (res = fn2(fn2 = 0)), res);
44
- var __require = import.meta.require;
19
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
45
20
 
46
21
  // node_modules/shellwords/lib/shellwords.js
47
22
  var require_shellwords = __commonJS((exports) => {
@@ -3751,866 +3726,6 @@ var require_node_notifier = __commonJS((exports, module) => {
3751
3726
  module.exports.Growl = Growl;
3752
3727
  });
3753
3728
 
3754
- // src/analytics/database.ts
3755
- import Database from "bun:sqlite";
3756
- import { homedir as homedir2 } from "os";
3757
- import { join as join3 } from "path";
3758
- import { existsSync as existsSync3 } from "fs";
3759
-
3760
- class AnalyticsDatabase {
3761
- db;
3762
- dbPath;
3763
- constructor() {
3764
- const configDir = join3(homedir2(), ".config", "opencode");
3765
- this.dbPath = join3(configDir, "opencode-notifier-analytics.db");
3766
- this.db = new Database(this.dbPath);
3767
- this.initializeSchema();
3768
- }
3769
- initializeSchema() {
3770
- this.db.exec("PRAGMA foreign_keys = ON");
3771
- this.db.exec(`
3772
- CREATE TABLE IF NOT EXISTS sessions (
3773
- id TEXT PRIMARY KEY,
3774
- projectName TEXT,
3775
- startTime TEXT NOT NULL,
3776
- endTime TEXT,
3777
- duration INTEGER,
3778
- completed INTEGER DEFAULT 0,
3779
- errorCount INTEGER DEFAULT 0,
3780
- toolsUsed TEXT,
3781
- createdAt TEXT NOT NULL
3782
- )
3783
- `);
3784
- this.db.exec(`
3785
- CREATE TABLE IF NOT EXISTS events (
3786
- id TEXT PRIMARY KEY,
3787
- sessionId TEXT NOT NULL,
3788
- eventType TEXT NOT NULL,
3789
- timestamp TEXT NOT NULL,
3790
- duration INTEGER,
3791
- errorMessage TEXT,
3792
- toolName TEXT,
3793
- metadata TEXT,
3794
- createdAt TEXT NOT NULL,
3795
- FOREIGN KEY (sessionId) REFERENCES sessions(id)
3796
- )
3797
- `);
3798
- this.db.exec(`
3799
- CREATE TABLE IF NOT EXISTS metrics_cache (
3800
- id TEXT PRIMARY KEY,
3801
- period TEXT NOT NULL,
3802
- dateKey TEXT NOT NULL,
3803
- eventType TEXT,
3804
- totalCount INTEGER,
3805
- errorCount INTEGER,
3806
- avgDuration INTEGER,
3807
- completedCount INTEGER,
3808
- permissions INTEGER,
3809
- questions INTEGER,
3810
- data TEXT,
3811
- createdAt TEXT NOT NULL,
3812
- updatedAt TEXT NOT NULL,
3813
- UNIQUE(period, dateKey, eventType)
3814
- )
3815
- `);
3816
- this.db.exec(`
3817
- CREATE INDEX IF NOT EXISTS idx_events_sessionId ON events(sessionId);
3818
- CREATE INDEX IF NOT EXISTS idx_events_eventType ON events(eventType);
3819
- CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
3820
- CREATE INDEX IF NOT EXISTS idx_sessions_createdAt ON sessions(createdAt);
3821
- CREATE INDEX IF NOT EXISTS idx_metrics_cache_dateKey ON metrics_cache(dateKey);
3822
- `);
3823
- }
3824
- getDatabase() {
3825
- return this.db;
3826
- }
3827
- getDbPath() {
3828
- return this.dbPath;
3829
- }
3830
- exists() {
3831
- return existsSync3(this.dbPath);
3832
- }
3833
- close() {
3834
- try {
3835
- this.db.close();
3836
- } catch (error) {
3837
- console.error("[Analytics] Error closing database:", error);
3838
- }
3839
- }
3840
- getSize() {
3841
- try {
3842
- const stat = __require("fs").statSync(this.dbPath);
3843
- return stat.size;
3844
- } catch {
3845
- return 0;
3846
- }
3847
- }
3848
- }
3849
- function getAnalyticsDatabase() {
3850
- if (!analyticsDbInstance) {
3851
- analyticsDbInstance = new AnalyticsDatabase;
3852
- }
3853
- return analyticsDbInstance;
3854
- }
3855
- var analyticsDbInstance = null, analyticsDb;
3856
- var init_database = __esm(() => {
3857
- analyticsDb = getAnalyticsDatabase();
3858
- });
3859
-
3860
- // src/analytics/tracker.ts
3861
- var exports_tracker = {};
3862
- __export(exports_tracker, {
3863
- getEventTracker: () => getEventTracker,
3864
- eventTracker: () => eventTracker,
3865
- EventTracker: () => EventTracker
3866
- });
3867
- import { randomUUID } from "crypto";
3868
-
3869
- class EventTracker {
3870
- db = getAnalyticsDatabase();
3871
- currentSession = null;
3872
- startSession(projectName = null) {
3873
- const sessionId = randomUUID();
3874
- const now = new Date().toISOString();
3875
- const stmt = this.db.getDatabase().prepare(`
3876
- INSERT INTO sessions (id, projectName, startTime, completed, errorCount, createdAt)
3877
- VALUES (?, ?, ?, ?, ?, ?)
3878
- `);
3879
- stmt.run(sessionId, projectName || null, now, 0, 0, now);
3880
- this.currentSession = {
3881
- id: sessionId,
3882
- projectName: projectName || null,
3883
- startTime: new Date,
3884
- completed: false,
3885
- errorCount: 0,
3886
- toolsUsed: []
3887
- };
3888
- return sessionId;
3889
- }
3890
- endSession(completed = true) {
3891
- if (!this.currentSession)
3892
- return;
3893
- const now = new Date().toISOString();
3894
- const duration = Date.now() - this.currentSession.startTime.getTime();
3895
- const toolsUsedJson = this.currentSession.toolsUsed?.length ? JSON.stringify(this.currentSession.toolsUsed) : null;
3896
- const stmt = this.db.getDatabase().prepare(`
3897
- UPDATE sessions
3898
- SET endTime = ?, duration = ?, completed = ?, toolsUsed = ?
3899
- WHERE id = ?
3900
- `);
3901
- stmt.run(now, duration, completed ? 1 : 0, toolsUsedJson, this.currentSession.id);
3902
- this.currentSession = null;
3903
- }
3904
- trackEvent(eventType, errorMessage, toolName, metadata) {
3905
- if (!this.currentSession) {
3906
- console.warn("[Analytics] No active session for event tracking");
3907
- return;
3908
- }
3909
- const eventId = randomUUID();
3910
- const now = new Date().toISOString();
3911
- if (eventType === "error") {
3912
- this.currentSession.errorCount++;
3913
- const updateStmt = this.db.getDatabase().prepare(`
3914
- UPDATE sessions SET errorCount = errorCount + 1 WHERE id = ?
3915
- `);
3916
- updateStmt.run(this.currentSession.id);
3917
- }
3918
- if (toolName && eventType !== "complete") {
3919
- if (!this.currentSession.toolsUsed) {
3920
- this.currentSession.toolsUsed = [];
3921
- }
3922
- if (!this.currentSession.toolsUsed.includes(toolName)) {
3923
- this.currentSession.toolsUsed.push(toolName);
3924
- }
3925
- }
3926
- const insertStmt = this.db.getDatabase().prepare(`
3927
- INSERT INTO events (id, sessionId, eventType, timestamp, errorMessage, toolName, metadata, createdAt)
3928
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
3929
- `);
3930
- insertStmt.run(eventId, this.currentSession.id, eventType, now, errorMessage || null, toolName || null, metadata ? JSON.stringify(metadata) : null, now);
3931
- }
3932
- getCurrentSession() {
3933
- return this.currentSession;
3934
- }
3935
- getSession(sessionId) {
3936
- const stmt = this.db.getDatabase().prepare(`
3937
- SELECT id, projectName, startTime, endTime, duration, completed, errorCount, toolsUsed, createdAt
3938
- FROM sessions
3939
- WHERE id = ?
3940
- `);
3941
- const row = stmt.get(sessionId);
3942
- if (!row)
3943
- return null;
3944
- return {
3945
- id: row.id,
3946
- projectName: row.projectName,
3947
- startTime: new Date(row.startTime),
3948
- endTime: row.endTime ? new Date(row.endTime) : undefined,
3949
- duration: row.duration !== null && row.duration !== undefined ? row.duration : undefined,
3950
- completed: row.completed === 1,
3951
- errorCount: row.errorCount,
3952
- toolsUsed: row.toolsUsed ? JSON.parse(row.toolsUsed) : []
3953
- };
3954
- }
3955
- getAllSessions() {
3956
- const stmt = this.db.getDatabase().prepare(`
3957
- SELECT id, projectName, startTime, endTime, duration, completed, errorCount, toolsUsed, createdAt
3958
- FROM sessions
3959
- ORDER BY createdAt DESC
3960
- `);
3961
- const rows = stmt.all();
3962
- return rows.map((row) => ({
3963
- id: row.id,
3964
- projectName: row.projectName,
3965
- startTime: new Date(row.startTime),
3966
- endTime: row.endTime ? new Date(row.endTime) : undefined,
3967
- duration: row.duration || undefined,
3968
- completed: row.completed === 1,
3969
- errorCount: row.errorCount,
3970
- toolsUsed: row.toolsUsed ? JSON.parse(row.toolsUsed) : []
3971
- }));
3972
- }
3973
- getSessionEvents(sessionId) {
3974
- const stmt = this.db.getDatabase().prepare(`
3975
- SELECT id, sessionId, eventType, timestamp, duration, errorMessage, toolName, metadata, createdAt
3976
- FROM events
3977
- WHERE sessionId = ?
3978
- ORDER BY timestamp ASC
3979
- `);
3980
- const rows = stmt.all(sessionId);
3981
- return rows.map((row) => ({
3982
- id: row.id,
3983
- sessionId: row.sessionId,
3984
- eventType: row.eventType,
3985
- timestamp: new Date(row.timestamp),
3986
- duration: row.duration || undefined,
3987
- errorMessage: row.errorMessage || undefined,
3988
- toolName: row.toolName || undefined,
3989
- metadata: row.metadata ? JSON.parse(row.metadata) : undefined
3990
- }));
3991
- }
3992
- clearCurrentSession() {
3993
- this.currentSession = null;
3994
- }
3995
- }
3996
- function getEventTracker() {
3997
- if (!eventTrackerInstance) {
3998
- eventTrackerInstance = new EventTracker;
3999
- }
4000
- return eventTrackerInstance;
4001
- }
4002
- var eventTrackerInstance = null, eventTracker;
4003
- var init_tracker = __esm(() => {
4004
- init_database();
4005
- eventTracker = getEventTracker();
4006
- });
4007
-
4008
- // src/analytics/aggregator.ts
4009
- var exports_aggregator = {};
4010
- __export(exports_aggregator, {
4011
- metricsAggregator: () => metricsAggregator,
4012
- getMetricsAggregator: () => getMetricsAggregator,
4013
- MetricsAggregator: () => MetricsAggregator
4014
- });
4015
- import { randomUUID as randomUUID2 } from "crypto";
4016
-
4017
- class MetricsAggregator {
4018
- db = getAnalyticsDatabase();
4019
- computeDailyMetrics(dateKey) {
4020
- const database = this.db.getDatabase();
4021
- const now = new Date().toISOString();
4022
- try {
4023
- const eventStmt = database.prepare(`
4024
- SELECT
4025
- eventType,
4026
- COUNT(*) as count,
4027
- AVG(CAST(duration AS REAL)) as avgDuration
4028
- FROM events
4029
- WHERE DATE(timestamp) = ?
4030
- GROUP BY eventType
4031
- `);
4032
- const eventResults = eventStmt.all(dateKey);
4033
- const sessionsStmt = database.prepare(`
4034
- SELECT COUNT(*) as count FROM sessions
4035
- WHERE DATE(createdAt) = ? AND completed = 1
4036
- `);
4037
- const sessionsResult = sessionsStmt.get(dateKey);
4038
- let totalCount = 0;
4039
- let totalErrorCount = 0;
4040
- let totalDuration = 0;
4041
- let permissionCount = 0;
4042
- let questionCount = 0;
4043
- let completeCount = 0;
4044
- let errorCount = 0;
4045
- for (const row of eventResults) {
4046
- const count = row.count;
4047
- const eventType = row.eventType;
4048
- const avgDuration2 = row.avgDuration || 0;
4049
- totalCount += count;
4050
- totalDuration += avgDuration2 * count;
4051
- switch (eventType) {
4052
- case "permission":
4053
- permissionCount += count;
4054
- break;
4055
- case "question":
4056
- questionCount += count;
4057
- break;
4058
- case "complete":
4059
- completeCount += count;
4060
- break;
4061
- case "error":
4062
- errorCount += count;
4063
- totalErrorCount += count;
4064
- break;
4065
- }
4066
- }
4067
- const avgDuration = totalCount > 0 ? Math.round(totalDuration / totalCount) : 0;
4068
- const completedCount = sessionsResult?.count || 0;
4069
- const upsertStmt = database.prepare(`
4070
- INSERT INTO metrics_cache (
4071
- id, period, dateKey, eventType, totalCount, errorCount, avgDuration,
4072
- completedCount, permissions, questions, data, createdAt, updatedAt
4073
- )
4074
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4075
- ON CONFLICT(period, dateKey, eventType) DO UPDATE SET
4076
- totalCount = excluded.totalCount,
4077
- errorCount = excluded.errorCount,
4078
- avgDuration = excluded.avgDuration,
4079
- completedCount = excluded.completedCount,
4080
- permissions = excluded.permissions,
4081
- questions = excluded.questions,
4082
- data = excluded.data,
4083
- updatedAt = excluded.updatedAt
4084
- `);
4085
- const aggregateData = {
4086
- totalEvents: totalCount,
4087
- totalErrors: totalErrorCount,
4088
- completedSessions: completedCount,
4089
- byType: {
4090
- permission: permissionCount,
4091
- question: questionCount,
4092
- complete: completeCount,
4093
- error: errorCount
4094
- }
4095
- };
4096
- upsertStmt.run(`${dateKey}-all`, "daily", dateKey, null, totalCount, totalErrorCount, avgDuration, completedCount, permissionCount, questionCount, JSON.stringify(aggregateData), now, now);
4097
- for (const row of eventResults) {
4098
- const eventType = row.eventType;
4099
- const count = row.count;
4100
- const avgDurationValue = row.avgDuration || 0;
4101
- const eventTypeId = randomUUID2();
4102
- upsertStmt.run(eventTypeId, "daily", dateKey, eventType, count, eventType === "error" ? count : 0, Math.round(avgDurationValue), eventType === "complete" ? completedCount : 0, eventType === "permission" ? count : 0, eventType === "question" ? count : 0, JSON.stringify({ count, avgDuration: Math.round(avgDurationValue) }), now, now);
4103
- }
4104
- } catch (error) {
4105
- console.error(`[Analytics] Error computing daily metrics for ${dateKey}:`, error);
4106
- }
4107
- }
4108
- computeWeeklyMetrics(yearWeek) {
4109
- const database = this.db.getDatabase();
4110
- const now = new Date().toISOString();
4111
- try {
4112
- const eventStmt = database.prepare(`
4113
- SELECT
4114
- eventType,
4115
- COUNT(*) as count,
4116
- AVG(CAST(duration AS REAL)) as avgDuration
4117
- FROM events
4118
- WHERE strftime('%G-W%V', timestamp) = ?
4119
- GROUP BY eventType
4120
- `);
4121
- const eventResults = eventStmt.all(yearWeek);
4122
- const sessionsStmt = database.prepare(`
4123
- SELECT COUNT(*) as count FROM sessions
4124
- WHERE strftime('%G-W%V', createdAt) = ? AND completed = 1
4125
- `);
4126
- const sessionsResult = sessionsStmt.get(yearWeek);
4127
- let totalCount = 0;
4128
- let totalErrorCount = 0;
4129
- let totalDuration = 0;
4130
- let permissionCount = 0;
4131
- let questionCount = 0;
4132
- let completeCount = 0;
4133
- let errorCount = 0;
4134
- for (const row of eventResults) {
4135
- const count = row.count;
4136
- const eventType = row.eventType;
4137
- const avgDuration2 = row.avgDuration || 0;
4138
- totalCount += count;
4139
- totalDuration += avgDuration2 * count;
4140
- switch (eventType) {
4141
- case "permission":
4142
- permissionCount += count;
4143
- break;
4144
- case "question":
4145
- questionCount += count;
4146
- break;
4147
- case "complete":
4148
- completeCount += count;
4149
- break;
4150
- case "error":
4151
- errorCount += count;
4152
- totalErrorCount += count;
4153
- break;
4154
- }
4155
- }
4156
- const avgDuration = totalCount > 0 ? Math.round(totalDuration / totalCount) : 0;
4157
- const completedCount = sessionsResult?.count || 0;
4158
- const upsertStmt = database.prepare(`
4159
- INSERT INTO metrics_cache (
4160
- id, period, dateKey, eventType, totalCount, errorCount, avgDuration,
4161
- completedCount, permissions, questions, data, createdAt, updatedAt
4162
- )
4163
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4164
- ON CONFLICT(period, dateKey, eventType) DO UPDATE SET
4165
- totalCount = excluded.totalCount,
4166
- errorCount = excluded.errorCount,
4167
- avgDuration = excluded.avgDuration,
4168
- completedCount = excluded.completedCount,
4169
- permissions = excluded.permissions,
4170
- questions = excluded.questions,
4171
- data = excluded.data,
4172
- updatedAt = excluded.updatedAt
4173
- `);
4174
- const aggregateData = {
4175
- totalEvents: totalCount,
4176
- totalErrors: totalErrorCount,
4177
- completedSessions: completedCount,
4178
- byType: {
4179
- permission: permissionCount,
4180
- question: questionCount,
4181
- complete: completeCount,
4182
- error: errorCount
4183
- }
4184
- };
4185
- upsertStmt.run(`${yearWeek}-all`, "weekly", yearWeek, null, totalCount, totalErrorCount, avgDuration, completedCount, permissionCount, questionCount, JSON.stringify(aggregateData), now, now);
4186
- } catch (error) {
4187
- console.error(`[Analytics] Error computing weekly metrics for ${yearWeek}:`, error);
4188
- }
4189
- }
4190
- getDailyReport(startDate, endDate) {
4191
- const stmt = this.db.getDatabase().prepare(`
4192
- SELECT
4193
- id, period, dateKey, eventType, totalCount, errorCount, avgDuration,
4194
- completedCount, permissions, questions, data, createdAt, updatedAt
4195
- FROM metrics_cache
4196
- WHERE period = 'daily' AND dateKey BETWEEN ? AND ? AND eventType IS NULL
4197
- ORDER BY dateKey DESC
4198
- `);
4199
- const rows = stmt.all(startDate, endDate);
4200
- return rows.map((row) => this.rowToEntry(row));
4201
- }
4202
- getWeeklyReport(startWeek, endWeek) {
4203
- const stmt = this.db.getDatabase().prepare(`
4204
- SELECT
4205
- id, period, dateKey, eventType, totalCount, errorCount, avgDuration,
4206
- completedCount, permissions, questions, data, createdAt, updatedAt
4207
- FROM metrics_cache
4208
- WHERE period = 'weekly' AND dateKey BETWEEN ? AND ? AND eventType IS NULL
4209
- ORDER BY dateKey DESC
4210
- `);
4211
- const rows = stmt.all(startWeek, endWeek);
4212
- return rows.map((row) => this.rowToEntry(row));
4213
- }
4214
- refreshMetrics() {
4215
- const isoString = new Date().toISOString();
4216
- const today = isoString.substring(0, 10);
4217
- const now = new Date;
4218
- const isoWeek = this.getISOWeek(now);
4219
- this.computeDailyMetrics(today);
4220
- this.computeWeeklyMetrics(isoWeek);
4221
- }
4222
- getISOWeek(date) {
4223
- const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
4224
- const dayNum = d.getUTCDay() || 7;
4225
- d.setUTCDate(d.getUTCDate() + 4 - dayNum);
4226
- const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
4227
- const weekNum = Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
4228
- return `${d.getUTCFullYear()}-W${String(weekNum).padStart(2, "0")}`;
4229
- }
4230
- rowToEntry(row) {
4231
- return {
4232
- id: row.id,
4233
- period: row.period,
4234
- dateKey: row.dateKey,
4235
- eventType: row.eventType || undefined,
4236
- totalCount: row.totalCount,
4237
- errorCount: row.errorCount,
4238
- avgDuration: row.avgDuration,
4239
- completedCount: row.completedCount,
4240
- breakdown: {
4241
- permissions: row.permissions || 0,
4242
- questions: row.questions || 0,
4243
- errors: row.errorCount || 0,
4244
- completes: row.completedCount || 0
4245
- },
4246
- createdAt: new Date(row.createdAt),
4247
- updatedAt: new Date(row.updatedAt)
4248
- };
4249
- }
4250
- }
4251
- function getMetricsAggregator() {
4252
- if (!metricsAggregatorInstance) {
4253
- metricsAggregatorInstance = new MetricsAggregator;
4254
- }
4255
- return metricsAggregatorInstance;
4256
- }
4257
- var metricsAggregatorInstance = null, metricsAggregator;
4258
- var init_aggregator = __esm(() => {
4259
- init_database();
4260
- metricsAggregator = getMetricsAggregator();
4261
- });
4262
-
4263
- // src/analytics/exporter.ts
4264
- class ReportExporter {
4265
- db = getAnalyticsDatabase();
4266
- getSummaryReport(days = 7) {
4267
- const database = this.db.getDatabase();
4268
- const isoString = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
4269
- const startDate = isoString.substring(0, 10);
4270
- const totalStmt = database.prepare(`
4271
- SELECT COUNT(*) as count FROM events WHERE DATE(timestamp) >= ?
4272
- `);
4273
- const totalResult = totalStmt.get(startDate);
4274
- const totalEvents = totalResult?.count || 0;
4275
- const errorStmt = database.prepare(`
4276
- SELECT COUNT(*) as count FROM events
4277
- WHERE DATE(timestamp) >= ? AND eventType = 'error'
4278
- `);
4279
- const errorResult = errorStmt.get(startDate);
4280
- const errorCount = errorResult?.count || 0;
4281
- const sessionsStmt = database.prepare(`
4282
- SELECT COUNT(*) as count FROM sessions WHERE DATE(createdAt) >= ?
4283
- `);
4284
- const sessionsResult = sessionsStmt.get(startDate);
4285
- const totalSessions = sessionsResult?.count || 0;
4286
- const completedStmt = database.prepare(`
4287
- SELECT COUNT(*) as count FROM sessions
4288
- WHERE DATE(createdAt) >= ? AND completed = 1
4289
- `);
4290
- const completedResult = completedStmt.get(startDate);
4291
- const completedSessions = completedResult?.count || 0;
4292
- const byTypeStmt = database.prepare(`
4293
- SELECT eventType, COUNT(*) as count FROM events
4294
- WHERE DATE(timestamp) >= ?
4295
- GROUP BY eventType
4296
- `);
4297
- const byTypeResults = byTypeStmt.all(startDate);
4298
- const timelineStmt = database.prepare(`
4299
- SELECT DATE(timestamp) as date, COUNT(*) as count,
4300
- SUM(CASE WHEN eventType = 'error' THEN 1 ELSE 0 END) as errors,
4301
- SUM(CASE WHEN eventType = 'complete' THEN 1 ELSE 0 END) as completions
4302
- FROM events
4303
- WHERE DATE(timestamp) >= ?
4304
- GROUP BY date
4305
- ORDER BY date DESC
4306
- `);
4307
- const timelineResults = timelineStmt.all(startDate);
4308
- const topErrorsStmt = database.prepare(`
4309
- SELECT errorMessage, COUNT(*) as count FROM events
4310
- WHERE DATE(timestamp) >= ? AND eventType = 'error' AND errorMessage IS NOT NULL
4311
- GROUP BY errorMessage
4312
- ORDER BY count DESC
4313
- LIMIT 5
4314
- `);
4315
- const topErrorsResults = topErrorsStmt.all(startDate);
4316
- const errorRate = totalEvents > 0 ? errorCount / totalEvents : 0;
4317
- const avgErrorsPerSession = totalSessions > 0 ? errorCount / totalSessions : 0;
4318
- return {
4319
- summary: {
4320
- period: `Last ${days} days`,
4321
- totalEvents,
4322
- totalSessions,
4323
- completedSessions,
4324
- errorRate,
4325
- avgDuration: 0,
4326
- avgErrorsPerSession
4327
- },
4328
- byType: Object.fromEntries(byTypeResults.map((r) => [r.eventType, r.count])),
4329
- timeline: timelineResults.map((r) => ({
4330
- date: r.date,
4331
- count: r.count,
4332
- errors: r.errors || 0,
4333
- completions: r.completions || 0
4334
- })),
4335
- topErrors: topErrorsResults.map((r) => ({
4336
- message: r.errorMessage || "Unknown error",
4337
- count: r.count
4338
- }))
4339
- };
4340
- }
4341
- exportJSON(days = 7) {
4342
- const report = this.getSummaryReport(days);
4343
- return JSON.stringify(report, null, 2);
4344
- }
4345
- exportCSV(days = 7) {
4346
- const report = this.getSummaryReport(days);
4347
- let csv = `Date,Event Count,Errors,Completions
4348
- `;
4349
- for (const entry of report.timeline) {
4350
- csv += `${entry.date},${entry.count},${entry.errors},${entry.completions}
4351
- `;
4352
- }
4353
- return csv;
4354
- }
4355
- exportHTML(days = 7) {
4356
- const report = this.getSummaryReport(days);
4357
- const errorRatePercent = (report.summary.errorRate * 100).toFixed(2);
4358
- return `<!DOCTYPE html>
4359
- <html lang="en">
4360
- <head>
4361
- <meta charset="UTF-8">
4362
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
4363
- <title>OpenCode Analytics Report</title>
4364
- <script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
4365
- <style>
4366
- * { margin: 0; padding: 0; box-sizing: border-box; }
4367
- body {
4368
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
4369
- background: #f5f5f5;
4370
- padding: 20px;
4371
- line-height: 1.6;
4372
- }
4373
- .container { max-width: 1000px; margin: 0 auto; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 30px; }
4374
- h1 { color: #333; margin-bottom: 10px; border-bottom: 3px solid #4CAF50; padding-bottom: 10px; }
4375
- h2 { color: #555; margin-top: 30px; margin-bottom: 15px; font-size: 1.3em; }
4376
- .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 30px; }
4377
- .summary-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; }
4378
- .summary-card.success { background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); }
4379
- .summary-card.error { background: linear-gradient(135deg, #f44336 0%, #da190b 100%); }
4380
- .summary-card.info { background: linear-gradient(135deg, #2196F3 0%, #0b7dda 100%); }
4381
- .summary-card h3 { font-size: 0.9em; opacity: 0.9; margin-bottom: 8px; text-transform: uppercase; }
4382
- .summary-card .value { font-size: 2em; font-weight: bold; }
4383
- table { width: 100%; border-collapse: collapse; margin: 15px 0; }
4384
- th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
4385
- th { background-color: #4CAF50; color: white; }
4386
- tr:nth-child(even) { background-color: #f9f9f9; }
4387
- .chart-container { position: relative; height: 300px; margin: 30px 0; }
4388
- .footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; font-size: 0.9em; }
4389
- </style>
4390
- </head>
4391
- <body>
4392
- <div class="container">
4393
- <h1>OpenCode Analytics Report</h1>
4394
- <p style="color: #666;">Period: ${report.summary.period} | Generated: ${new Date().toLocaleString()}</p>
4395
-
4396
- <div class="summary">
4397
- <div class="summary-card success">
4398
- <h3>Total Sessions</h3>
4399
- <div class="value">${report.summary.totalSessions}</div>
4400
- </div>
4401
- <div class="summary-card success">
4402
- <h3>Completed</h3>
4403
- <div class="value">${report.summary.completedSessions}</div>
4404
- </div>
4405
- <div class="summary-card info">
4406
- <h3>Total Events</h3>
4407
- <div class="value">${report.summary.totalEvents}</div>
4408
- </div>
4409
- <div class="summary-card error">
4410
- <h3>Error Rate</h3>
4411
- <div class="value">${errorRatePercent}%</div>
4412
- </div>
4413
- </div>
4414
-
4415
- <h2>Events by Type</h2>
4416
- <table>
4417
- <tr>
4418
- <th>Event Type</th>
4419
- <th>Count</th>
4420
- <th>Percentage</th>
4421
- </tr>
4422
- ${Object.entries(report.byType).map(([type, count]) => {
4423
- const percentage = report.summary.totalEvents > 0 ? (count / report.summary.totalEvents * 100).toFixed(1) : "0.0";
4424
- return `<tr><td>${type}</td><td>${count}</td><td>${percentage}%</td></tr>`;
4425
- }).join("")}
4426
- </table>
4427
-
4428
- ${report.topErrors && report.topErrors.length > 0 ? `
4429
- <h2>Top Errors</h2>
4430
- <table>
4431
- <tr>
4432
- <th>Error Message</th>
4433
- <th>Count</th>
4434
- </tr>
4435
- ${report.topErrors.map((err) => `<tr><td>${err.message}</td><td>${err.count}</td></tr>`).join("")}
4436
- </table>
4437
- ` : ""}
4438
-
4439
- <h2>Timeline</h2>
4440
- <div class="chart-container">
4441
- <canvas id="timelineChart"></canvas>
4442
- </div>
4443
-
4444
- <table>
4445
- <tr>
4446
- <th>Date</th>
4447
- <th>Event Count</th>
4448
- <th>Errors</th>
4449
- <th>Completions</th>
4450
- </tr>
4451
- ${report.timeline.map((entry) => `<tr><td>${entry.date}</td><td>${entry.count}</td><td>${entry.errors}</td><td>${entry.completions}</td></tr>`).join("")}
4452
- </table>
4453
-
4454
- <div class="footer">
4455
- <p>This report was generated by OpenCode Notifier Analytics.</p>
4456
- <p>All data is stored locally and never transmitted externally.</p>
4457
- </div>
4458
- </div>
4459
-
4460
- <script>
4461
- const ctx = document.getElementById('timelineChart').getContext('2d');
4462
- const timelineData = ${JSON.stringify(report.timeline)};
4463
-
4464
- new Chart(ctx, {
4465
- type: 'line',
4466
- data: {
4467
- labels: timelineData.map(d => d.date),
4468
- datasets: [
4469
- {
4470
- label: 'Total Events',
4471
- data: timelineData.map(d => d.count),
4472
- borderColor: '#2196F3',
4473
- backgroundColor: 'rgba(33, 150, 243, 0.1)',
4474
- tension: 0.4
4475
- },
4476
- {
4477
- label: 'Errors',
4478
- data: timelineData.map(d => d.errors),
4479
- borderColor: '#f44336',
4480
- backgroundColor: 'rgba(244, 67, 54, 0.1)',
4481
- tension: 0.4
4482
- },
4483
- {
4484
- label: 'Completions',
4485
- data: timelineData.map(d => d.completions),
4486
- borderColor: '#4CAF50',
4487
- backgroundColor: 'rgba(76, 175, 80, 0.1)',
4488
- tension: 0.4
4489
- }
4490
- ]
4491
- },
4492
- options: {
4493
- responsive: true,
4494
- maintainAspectRatio: false,
4495
- plugins: {
4496
- legend: {
4497
- position: 'top',
4498
- },
4499
- title: {
4500
- display: true,
4501
- text: 'Event Timeline'
4502
- }
4503
- },
4504
- scales: {
4505
- y: {
4506
- beginAtZero: true
4507
- }
4508
- }
4509
- }
4510
- });
4511
- </script>
4512
- </body>
4513
- </html>`;
4514
- }
4515
- }
4516
- function getReportExporter() {
4517
- if (!reportExporterInstance) {
4518
- reportExporterInstance = new ReportExporter;
4519
- }
4520
- return reportExporterInstance;
4521
- }
4522
- var reportExporterInstance = null, reportExporter;
4523
- var init_exporter = __esm(() => {
4524
- init_database();
4525
- reportExporter = getReportExporter();
4526
- });
4527
-
4528
- // src/analytics/cleaner.ts
4529
- var exports_cleaner = {};
4530
- __export(exports_cleaner, {
4531
- getDataCleaner: () => getDataCleaner,
4532
- dataCleaner: () => dataCleaner,
4533
- DataCleaner: () => DataCleaner
4534
- });
4535
-
4536
- class DataCleaner {
4537
- db = getAnalyticsDatabase();
4538
- cleanupOldData(retentionDays) {
4539
- const database = this.db.getDatabase();
4540
- const cutoffDate = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000).toISOString().substring(0, 10);
4541
- try {
4542
- const deleteEventsStmt = database.prepare(`
4543
- DELETE FROM events WHERE DATE(createdAt) < ?
4544
- `);
4545
- const deletedEvents = deleteEventsStmt.run(cutoffDate);
4546
- const deleteSessionsStmt = database.prepare(`
4547
- DELETE FROM sessions WHERE DATE(createdAt) < ?
4548
- `);
4549
- const deletedSessions = deleteSessionsStmt.run(cutoffDate);
4550
- console.log(`[Analytics] Cleaned up data older than ${cutoffDate}`);
4551
- } catch (error) {
4552
- console.error("[Analytics] Error cleaning up old data:", error);
4553
- }
4554
- }
4555
- deleteAllData() {
4556
- const database = this.db.getDatabase();
4557
- try {
4558
- database.exec("DELETE FROM events");
4559
- database.exec("DELETE FROM sessions");
4560
- database.exec("DELETE FROM metrics_cache");
4561
- console.log("[Analytics] All analytics data deleted");
4562
- } catch (error) {
4563
- console.error("[Analytics] Error deleting all data:", error);
4564
- }
4565
- }
4566
- optimizeDatabase() {
4567
- const database = this.db.getDatabase();
4568
- try {
4569
- database.exec("VACUUM");
4570
- console.log("[Analytics] Database optimized");
4571
- } catch (error) {
4572
- console.error("[Analytics] Error optimizing database:", error);
4573
- }
4574
- }
4575
- getStats() {
4576
- const database = this.db.getDatabase();
4577
- try {
4578
- const sessionStmt = database.prepare("SELECT COUNT(*) as count FROM sessions");
4579
- const sessionCount = sessionStmt.get()?.count || 0;
4580
- const eventStmt = database.prepare("SELECT COUNT(*) as count FROM events");
4581
- const eventCount = eventStmt.get()?.count || 0;
4582
- const cacheStmt = database.prepare("SELECT COUNT(*) as count FROM metrics_cache");
4583
- const cacheEntries = cacheStmt.get()?.count || 0;
4584
- const databaseSizeBytes = this.db.getSize();
4585
- return {
4586
- sessionCount,
4587
- eventCount,
4588
- cacheEntries,
4589
- databaseSizeBytes
4590
- };
4591
- } catch (error) {
4592
- console.error("[Analytics] Error getting database stats:", error);
4593
- return {
4594
- sessionCount: 0,
4595
- eventCount: 0,
4596
- cacheEntries: 0,
4597
- databaseSizeBytes: 0
4598
- };
4599
- }
4600
- }
4601
- }
4602
- function getDataCleaner() {
4603
- if (!dataCleanerInstance) {
4604
- dataCleanerInstance = new DataCleaner;
4605
- }
4606
- return dataCleanerInstance;
4607
- }
4608
- var dataCleanerInstance = null, dataCleaner;
4609
- var init_cleaner = __esm(() => {
4610
- init_database();
4611
- dataCleaner = getDataCleaner();
4612
- });
4613
-
4614
3729
  // src/index.ts
4615
3730
  import { basename } from "path";
4616
3731
 
@@ -4644,16 +3759,6 @@ var DEFAULT_CONFIG = {
4644
3759
  complete: null,
4645
3760
  error: null,
4646
3761
  question: null
4647
- },
4648
- analytics: {
4649
- enabled: true,
4650
- trackSessions: true,
4651
- trackEvents: true,
4652
- trackTools: false,
4653
- retentionDays: 30,
4654
- autoCleanup: true,
4655
- cacheMetrics: true,
4656
- cachePeriods: ["daily", "weekly"]
4657
3762
  }
4658
3763
  };
4659
3764
  function getConfigPath() {
@@ -4875,235 +3980,6 @@ async function playSound(event, customPath) {
4875
3980
  } catch {}
4876
3981
  }
4877
3982
 
4878
- // src/analytics/index.ts
4879
- init_database();
4880
- init_tracker();
4881
- init_aggregator();
4882
- init_exporter();
4883
- init_cleaner();
4884
-
4885
- // src/analytics/cli.ts
4886
- import { writeFileSync, mkdirSync } from "fs";
4887
- function getAnalyticsReportText(days = 7) {
4888
- const exporter = getReportExporter();
4889
- const report = exporter.getSummaryReport(days);
4890
- let output = `
4891
- `;
4892
- output += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
4893
- `;
4894
- output += `\u2551 OpenCode Analytics Report \u2551
4895
- `;
4896
- output += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
4897
-
4898
- `;
4899
- output += `\uD83D\uDCCA Period: ${report.summary.period}
4900
- `;
4901
- output += `Generated: ${new Date().toLocaleString()}
4902
-
4903
- `;
4904
- output += `\uD83D\uDCC8 Summary Statistics:
4905
- `;
4906
- output += ` \u2022 Total Sessions: ${report.summary.totalSessions}
4907
- `;
4908
- output += ` \u2022 Completed Sessions: ${report.summary.completedSessions}
4909
- `;
4910
- output += ` \u2022 Total Events: ${report.summary.totalEvents}
4911
- `;
4912
- output += ` \u2022 Error Rate: ${(report.summary.errorRate * 100).toFixed(2)}%
4913
- `;
4914
- output += ` \u2022 Avg Errors per Session: ${report.summary.avgErrorsPerSession.toFixed(2)}
4915
-
4916
- `;
4917
- output += `\uD83C\uDFF7\uFE0F Events by Type:
4918
- `;
4919
- for (const [type, count] of Object.entries(report.byType)) {
4920
- const percentage = report.summary.totalEvents > 0 ? (count / report.summary.totalEvents * 100).toFixed(1) : "0.0";
4921
- output += ` \u2022 ${type.padEnd(12)} : ${count.toString().padStart(4)} (${percentage}%)
4922
- `;
4923
- }
4924
- if (report.topErrors && report.topErrors.length > 0) {
4925
- output += `
4926
- \u26A0\uFE0F Top Errors:
4927
- `;
4928
- report.topErrors.slice(0, 5).forEach((err, idx) => {
4929
- output += ` ${idx + 1}. ${err.message} (${err.count} times)
4930
- `;
4931
- });
4932
- }
4933
- output += `
4934
- `;
4935
- return output;
4936
- }
4937
- function exportAnalyticsToFile(outputPath, days = 7, format = "json") {
4938
- const exporter = getReportExporter();
4939
- let content;
4940
- switch (format) {
4941
- case "json":
4942
- content = exporter.exportJSON(days);
4943
- break;
4944
- case "csv":
4945
- content = exporter.exportCSV(days);
4946
- break;
4947
- case "html":
4948
- content = exporter.exportHTML(days);
4949
- break;
4950
- default:
4951
- content = exporter.exportJSON(days);
4952
- }
4953
- const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
4954
- mkdirSync(dir, { recursive: true });
4955
- writeFileSync(outputPath, content, "utf-8");
4956
- return `\u2705 Report exported to: ${outputPath}`;
4957
- }
4958
- function getAnalyticsStatsText() {
4959
- const cleaner = getDataCleaner();
4960
- const stats = cleaner.getStats();
4961
- let output = `
4962
- `;
4963
- output += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
4964
- `;
4965
- output += `\u2551 Analytics Database Statistics \u2551
4966
- `;
4967
- output += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
4968
-
4969
- `;
4970
- output += `\uD83D\uDCCA Data Storage:
4971
- `;
4972
- output += ` \u2022 Total Sessions: ${stats.sessionCount}
4973
- `;
4974
- output += ` \u2022 Total Events: ${stats.eventCount}
4975
- `;
4976
- output += ` \u2022 Cached Metrics: ${stats.cacheEntries}
4977
- `;
4978
- output += ` \u2022 Database Size: ${formatBytes(stats.databaseSizeBytes)}
4979
-
4980
- `;
4981
- return output;
4982
- }
4983
- function printAnalyticsOverview(days = 7) {
4984
- console.log(getAnalyticsReportText(days));
4985
- console.log(getAnalyticsStatsText());
4986
- }
4987
- function cleanupOldData(retentionDays = 30) {
4988
- const cleaner = getDataCleaner();
4989
- const statsBefore = cleaner.getStats();
4990
- cleaner.cleanupOldData(retentionDays);
4991
- const statsAfter = cleaner.getStats();
4992
- const deletedSessions = statsBefore.sessionCount - statsAfter.sessionCount;
4993
- const deletedEvents = statsBefore.eventCount - statsAfter.eventCount;
4994
- return `\u2705 Cleanup completed:
4995
- \u2022 Deleted ${deletedSessions} sessions
4996
- \u2022 Deleted ${deletedEvents} events
4997
- \u2022 Remaining data: ${statsAfter.sessionCount} sessions, ${statsAfter.eventCount} events`;
4998
- }
4999
- function deleteAllAnalytics(confirm = false) {
5000
- if (!confirm) {
5001
- return "\u26A0\uFE0F To delete all analytics data, call with confirm=true";
5002
- }
5003
- const cleaner = getDataCleaner();
5004
- const statsBefore = cleaner.getStats();
5005
- cleaner.deleteAllData();
5006
- return `\u2705 All analytics data deleted:
5007
- \u2022 Deleted ${statsBefore.sessionCount} sessions
5008
- \u2022 Deleted ${statsBefore.eventCount} events
5009
- \u2022 Deleted ${statsBefore.cacheEntries} cache entries`;
5010
- }
5011
- function optimizeDatabase() {
5012
- const cleaner = getDataCleaner();
5013
- const sizeBefore = cleaner.getStats().databaseSizeBytes;
5014
- cleaner.optimizeDatabase();
5015
- const sizeAfter = cleaner.getStats().databaseSizeBytes;
5016
- const savedBytes = sizeBefore - sizeAfter;
5017
- return `\u2705 Database optimized:
5018
- \u2022 Size before: ${formatBytes(sizeBefore)}
5019
- \u2022 Size after: ${formatBytes(sizeAfter)}
5020
- \u2022 Freed space: ${formatBytes(Math.max(0, savedBytes))}`;
5021
- }
5022
- function getCurrentSessionInfo() {
5023
- const tracker = getEventTracker();
5024
- const session = tracker.getCurrentSession();
5025
- if (!session) {
5026
- return "\u2139\uFE0F No active analytics session";
5027
- }
5028
- const duration = session.endTime ? new Date(session.endTime).getTime() - new Date(session.startTime).getTime() : Date.now() - new Date(session.startTime).getTime();
5029
- let output = `
5030
- `;
5031
- output += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
5032
- `;
5033
- output += `\u2551 Current Analytics Session \u2551
5034
- `;
5035
- output += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
5036
-
5037
- `;
5038
- output += `Session ID: ${session.id}
5039
- `;
5040
- output += `Project: ${session.projectName || "Unknown"}
5041
- `;
5042
- output += `Started: ${new Date(session.startTime).toLocaleString()}
5043
- `;
5044
- output += `Duration: ${formatDuration(duration)}
5045
- `;
5046
- output += `Errors: ${session.errorCount}
5047
- `;
5048
- if (session.toolsUsed && session.toolsUsed.length > 0) {
5049
- output += `Tools Used: ${session.toolsUsed.join(", ")}
5050
- `;
5051
- }
5052
- output += `
5053
- `;
5054
- return output;
5055
- }
5056
- function refreshMetrics() {
5057
- const aggregator = getMetricsAggregator();
5058
- aggregator.refreshMetrics();
5059
- const cleaner = getDataCleaner();
5060
- const stats = cleaner.getStats();
5061
- return `\u2705 Metrics refreshed:
5062
- \u2022 Cached entries: ${stats.cacheEntries}
5063
- \u2022 Sessions: ${stats.sessionCount}
5064
- \u2022 Events: ${stats.eventCount}`;
5065
- }
5066
- function formatBytes(bytes) {
5067
- if (bytes === 0)
5068
- return "0 Bytes";
5069
- const k = 1024;
5070
- const sizes = ["Bytes", "KB", "MB", "GB"];
5071
- const i = Math.floor(Math.log(bytes) / Math.log(k));
5072
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
5073
- }
5074
- function formatDuration(ms) {
5075
- const seconds = Math.floor(ms / 1000 % 60);
5076
- const minutes = Math.floor(ms / (1000 * 60) % 60);
5077
- const hours = Math.floor(ms / (1000 * 60 * 60) % 24);
5078
- if (hours > 0) {
5079
- return `${hours}h ${minutes}m ${seconds}s`;
5080
- } else if (minutes > 0) {
5081
- return `${minutes}m ${seconds}s`;
5082
- } else {
5083
- return `${seconds}s`;
5084
- }
5085
- }
5086
-
5087
- // src/analytics/index.ts
5088
- function initializeAnalytics(projectName = null) {
5089
- const { eventTracker: eventTracker2 } = (init_tracker(), __toCommonJS(exports_tracker));
5090
- return eventTracker2.startSession(projectName);
5091
- }
5092
- function finalizeAnalytics(completed = true) {
5093
- const { eventTracker: eventTracker2, metricsAggregator: metricsAggregator2 } = (init_tracker(), __toCommonJS(exports_tracker));
5094
- init_aggregator();
5095
- eventTracker2.endSession(completed);
5096
- metricsAggregator2.refreshMetrics();
5097
- }
5098
- function trackAnalyticsEvent(eventType, errorMessage, toolName, metadata) {
5099
- const { eventTracker: eventTracker2 } = (init_tracker(), __toCommonJS(exports_tracker));
5100
- eventTracker2.trackEvent(eventType, errorMessage, toolName, metadata);
5101
- }
5102
- function cleanupAnalyticsData(retentionDays = 30) {
5103
- const { dataCleaner: dataCleaner2 } = (init_cleaner(), __toCommonJS(exports_cleaner));
5104
- dataCleaner2.cleanupOldData(retentionDays);
5105
- }
5106
-
5107
3983
  // src/index.ts
5108
3984
  function getNotificationTitle(config, projectName) {
5109
3985
  if (config.showProjectName && projectName) {
@@ -5127,70 +4003,33 @@ async function handleEvent(config, eventType, projectName) {
5127
4003
  var NotifierPlugin = async ({ project, client, $, directory, worktree }) => {
5128
4004
  const config = loadConfig();
5129
4005
  const projectName = directory ? basename(directory) : null;
5130
- let sessionId = null;
5131
- if (config.analytics?.enabled) {
5132
- sessionId = initializeAnalytics(projectName);
5133
- if (config.analytics?.autoCleanup) {
5134
- cleanupAnalyticsData(config.analytics?.retentionDays || 30);
5135
- }
5136
- }
5137
4006
  return {
5138
4007
  event: async ({ event }) => {
5139
4008
  if (event.type === "permission.updated") {
5140
4009
  await handleEvent(config, "permission", projectName);
5141
- if (config.analytics?.enabled && config.analytics?.trackEvents) {
5142
- trackAnalyticsEvent("permission");
5143
- }
5144
4010
  }
5145
4011
  if (event.type === "permission.asked") {
5146
4012
  await handleEvent(config, "permission", projectName);
5147
- if (config.analytics?.enabled && config.analytics?.trackEvents) {
5148
- trackAnalyticsEvent("permission");
5149
- }
5150
4013
  }
5151
4014
  if (event.type === "session.idle") {
5152
4015
  await handleEvent(config, "complete", projectName);
5153
- if (config.analytics?.enabled && config.analytics?.trackEvents) {
5154
- trackAnalyticsEvent("complete");
5155
- finalizeAnalytics(true);
5156
- }
5157
4016
  }
5158
4017
  if (event.type === "session.error") {
5159
- const errorMsg = event.error?.message || event.message || "Unknown error";
5160
4018
  await handleEvent(config, "error", projectName);
5161
- if (config.analytics?.enabled && config.analytics?.trackEvents) {
5162
- trackAnalyticsEvent("error", errorMsg);
5163
- }
5164
4019
  }
5165
4020
  },
5166
4021
  "permission.ask": async () => {
5167
4022
  await handleEvent(config, "permission", projectName);
5168
- if (config.analytics?.enabled && config.analytics?.trackEvents) {
5169
- trackAnalyticsEvent("permission");
5170
- }
5171
4023
  },
5172
4024
  "tool.execute.before": async (input, output) => {
5173
4025
  if (input.tool === "question") {
5174
4026
  await handleEvent(config, "question", projectName);
5175
- if (config.analytics?.enabled && config.analytics?.trackEvents) {
5176
- const toolName = config.analytics?.trackTools ? input.tool : undefined;
5177
- trackAnalyticsEvent("question", undefined, toolName);
5178
- }
5179
4027
  }
5180
4028
  }
5181
4029
  };
5182
4030
  };
5183
4031
  var src_default = NotifierPlugin;
5184
4032
  export {
5185
- refreshMetrics,
5186
- printAnalyticsOverview,
5187
- optimizeDatabase,
5188
- getCurrentSessionInfo,
5189
- getAnalyticsStatsText,
5190
- getAnalyticsReportText,
5191
- exportAnalyticsToFile,
5192
- deleteAllAnalytics,
5193
4033
  src_default as default,
5194
- cleanupOldData,
5195
4034
  NotifierPlugin
5196
4035
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pranjalmandavkar/opencode-notifier",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Opencode Plugin that sends system notification when permission is needed, generation is complete or error occurs in session",
5
5
  "author": "MandavkarPranjal",
6
6
  "license": "MIT",