@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.
- package/dist/index.js +2 -1163
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
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
|
|
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.
|
|
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",
|