daybrain 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/daybrain.js +18 -0
- package/dist/aw.d.ts +30 -0
- package/dist/aw.d.ts.map +1 -0
- package/dist/aw.js +145 -0
- package/dist/aw.js.map +1 -0
- package/dist/baseline.d.ts +31 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +130 -0
- package/dist/baseline.js.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +139 -0
- package/dist/config.js.map +1 -0
- package/dist/db.d.ts +72 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +261 -0
- package/dist/db.js.map +1 -0
- package/dist/http.d.ts +5 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +411 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +908 -0
- package/dist/index.js.map +1 -0
- package/dist/insights.d.ts +68 -0
- package/dist/insights.d.ts.map +1 -0
- package/dist/insights.js +523 -0
- package/dist/insights.js.map +1 -0
- package/dist/onboard.d.ts +2 -0
- package/dist/onboard.d.ts.map +1 -0
- package/dist/onboard.js +121 -0
- package/dist/onboard.js.map +1 -0
- package/dist/scheduler.d.ts +8 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +214 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/screenpipe.d.ts +25 -0
- package/dist/screenpipe.d.ts.map +1 -0
- package/dist/screenpipe.js +71 -0
- package/dist/screenpipe.js.map +1 -0
- package/dist/transport.d.ts +12 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +152 -0
- package/dist/transport.js.map +1 -0
- package/dist/watcher.d.ts +15 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +173 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +58 -0
- package/watcher.py +139 -0
package/dist/db.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getDb = getDb;
|
|
7
|
+
exports.insertRawEvents = insertRawEvents;
|
|
8
|
+
exports.getRawEvents = getRawEvents;
|
|
9
|
+
exports.insertInsight = insertInsight;
|
|
10
|
+
exports.insertInsights = insertInsights;
|
|
11
|
+
exports.getInsights = getInsights;
|
|
12
|
+
exports.updateInsightStatus = updateInsightStatus;
|
|
13
|
+
exports.markInsightPushed = markInsightPushed;
|
|
14
|
+
exports.upsertDailySummary = upsertDailySummary;
|
|
15
|
+
exports.getDailySummary = getDailySummary;
|
|
16
|
+
exports.getRecentDailySummaries = getRecentDailySummaries;
|
|
17
|
+
exports.closeDb = closeDb;
|
|
18
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const config_1 = require("./config");
|
|
21
|
+
let db = null;
|
|
22
|
+
function getDb() {
|
|
23
|
+
if (db)
|
|
24
|
+
return db;
|
|
25
|
+
const dataDir = (0, config_1.getDataDir)();
|
|
26
|
+
const dbPath = path_1.default.join(dataDir, 'opencontext.db');
|
|
27
|
+
db = new better_sqlite3_1.default(dbPath);
|
|
28
|
+
db.pragma('journal_mode = WAL');
|
|
29
|
+
db.pragma('busy_timeout = 5000');
|
|
30
|
+
initializeSchema(db);
|
|
31
|
+
return db;
|
|
32
|
+
}
|
|
33
|
+
function initializeSchema(database) {
|
|
34
|
+
database.exec(`
|
|
35
|
+
CREATE TABLE IF NOT EXISTS raw_events (
|
|
36
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37
|
+
source TEXT NOT NULL,
|
|
38
|
+
bucket_id TEXT NOT NULL,
|
|
39
|
+
timestamp TEXT NOT NULL,
|
|
40
|
+
duration REAL NOT NULL DEFAULT 0,
|
|
41
|
+
app TEXT NOT NULL DEFAULT '',
|
|
42
|
+
title TEXT NOT NULL DEFAULT '',
|
|
43
|
+
url TEXT NOT NULL DEFAULT '',
|
|
44
|
+
raw_data TEXT NOT NULL DEFAULT '{}',
|
|
45
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_raw_events_dedup
|
|
49
|
+
ON raw_events(timestamp, source, bucket_id, app, title);
|
|
50
|
+
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_raw_events_timestamp ON raw_events(timestamp);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_raw_events_app ON raw_events(app);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_raw_events_source ON raw_events(source);
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS insights (
|
|
56
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
57
|
+
type TEXT NOT NULL,
|
|
58
|
+
period_start TEXT NOT NULL,
|
|
59
|
+
period_end TEXT NOT NULL,
|
|
60
|
+
title TEXT NOT NULL,
|
|
61
|
+
description TEXT NOT NULL DEFAULT '',
|
|
62
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
63
|
+
evidence TEXT NOT NULL DEFAULT '[]',
|
|
64
|
+
action_text TEXT NOT NULL DEFAULT '',
|
|
65
|
+
status TEXT NOT NULL DEFAULT 'new',
|
|
66
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
67
|
+
pushed_to TEXT,
|
|
68
|
+
pushed_at TEXT
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_insights_dedup
|
|
72
|
+
ON insights(type, title, period_start);
|
|
73
|
+
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_insights_type ON insights(type);
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_insights_period ON insights(period_start, period_end);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_insights_created ON insights(created_at);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_insights_status ON insights(status);
|
|
78
|
+
|
|
79
|
+
CREATE TABLE IF NOT EXISTS daily_summaries (
|
|
80
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
81
|
+
date TEXT NOT NULL UNIQUE,
|
|
82
|
+
total_active_time REAL NOT NULL DEFAULT 0,
|
|
83
|
+
top_apps TEXT NOT NULL DEFAULT '[]',
|
|
84
|
+
switch_count INTEGER NOT NULL DEFAULT 0,
|
|
85
|
+
insight_count INTEGER NOT NULL DEFAULT 0,
|
|
86
|
+
raw_summary TEXT NOT NULL DEFAULT '',
|
|
87
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
CREATE TABLE IF NOT EXISTS config (
|
|
91
|
+
key TEXT PRIMARY KEY,
|
|
92
|
+
value TEXT NOT NULL,
|
|
93
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
94
|
+
);
|
|
95
|
+
`);
|
|
96
|
+
}
|
|
97
|
+
function insertRawEvents(events) {
|
|
98
|
+
const database = getDb();
|
|
99
|
+
const stmt = database.prepare(`
|
|
100
|
+
INSERT INTO raw_events (source, bucket_id, timestamp, duration, app, title, url, raw_data)
|
|
101
|
+
VALUES (@source, @bucket_id, @timestamp, @duration, @app, @title, @url, @raw_data)
|
|
102
|
+
`);
|
|
103
|
+
const insertMany = database.transaction((evts) => {
|
|
104
|
+
let count = 0;
|
|
105
|
+
for (const evt of evts) {
|
|
106
|
+
try {
|
|
107
|
+
stmt.run(evt);
|
|
108
|
+
count++;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// duplicate event, skip silently
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return count;
|
|
115
|
+
});
|
|
116
|
+
return insertMany(events);
|
|
117
|
+
}
|
|
118
|
+
function getRawEvents(options) {
|
|
119
|
+
const database = getDb();
|
|
120
|
+
const conditions = [];
|
|
121
|
+
const values = [];
|
|
122
|
+
if (options.periodStart) {
|
|
123
|
+
conditions.push('timestamp >= ?');
|
|
124
|
+
values.push(options.periodStart);
|
|
125
|
+
}
|
|
126
|
+
if (options.periodEnd) {
|
|
127
|
+
conditions.push('timestamp <= ?');
|
|
128
|
+
values.push(options.periodEnd);
|
|
129
|
+
}
|
|
130
|
+
if (options.source) {
|
|
131
|
+
conditions.push('source = ?');
|
|
132
|
+
values.push(options.source);
|
|
133
|
+
}
|
|
134
|
+
if (options.app) {
|
|
135
|
+
conditions.push('app LIKE ?');
|
|
136
|
+
values.push(`%${options.app}%`);
|
|
137
|
+
}
|
|
138
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
139
|
+
const limit = Math.min(options.limit || 1000, 5000);
|
|
140
|
+
const offset = options.offset || 0;
|
|
141
|
+
return database
|
|
142
|
+
.prepare(`SELECT * FROM raw_events ${where} ORDER BY timestamp DESC LIMIT ? OFFSET ?`)
|
|
143
|
+
.all(...values, limit, offset);
|
|
144
|
+
}
|
|
145
|
+
function insertInsight(insight) {
|
|
146
|
+
const database = getDb();
|
|
147
|
+
try {
|
|
148
|
+
const result = database
|
|
149
|
+
.prepare(`INSERT INTO insights (type, period_start, period_end, title, description, confidence, evidence, action_text)
|
|
150
|
+
VALUES (@type, @period_start, @period_end, @title, @description, @confidence, @evidence, @action_text)`)
|
|
151
|
+
.run(insight);
|
|
152
|
+
return database
|
|
153
|
+
.prepare('SELECT * FROM insights WHERE id = ?')
|
|
154
|
+
.get(result.lastInsertRowid);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null; // duplicate insight
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function insertInsights(insights) {
|
|
161
|
+
const database = getDb();
|
|
162
|
+
const stmt = database.prepare(`
|
|
163
|
+
INSERT INTO insights (type, period_start, period_end, title, description, confidence, evidence, action_text)
|
|
164
|
+
VALUES (@type, @period_start, @period_end, @title, @description, @confidence, @evidence, @action_text)
|
|
165
|
+
`);
|
|
166
|
+
const insertMany = database.transaction((items) => {
|
|
167
|
+
let count = 0;
|
|
168
|
+
for (const item of items) {
|
|
169
|
+
try {
|
|
170
|
+
stmt.run(item);
|
|
171
|
+
count++;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// duplicate insight, skip silently
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return count;
|
|
178
|
+
});
|
|
179
|
+
return insertMany(insights);
|
|
180
|
+
}
|
|
181
|
+
function getInsights(options) {
|
|
182
|
+
const database = getDb();
|
|
183
|
+
const conditions = [];
|
|
184
|
+
const values = [];
|
|
185
|
+
if (options.type) {
|
|
186
|
+
conditions.push('type = ?');
|
|
187
|
+
values.push(options.type);
|
|
188
|
+
}
|
|
189
|
+
if (options.periodStart) {
|
|
190
|
+
conditions.push('period_start >= ?');
|
|
191
|
+
values.push(options.periodStart);
|
|
192
|
+
}
|
|
193
|
+
if (options.periodEnd) {
|
|
194
|
+
conditions.push('period_end <= ?');
|
|
195
|
+
values.push(options.periodEnd);
|
|
196
|
+
}
|
|
197
|
+
if (options.minConfidence) {
|
|
198
|
+
conditions.push('confidence >= ?');
|
|
199
|
+
values.push(options.minConfidence);
|
|
200
|
+
}
|
|
201
|
+
if (!options.includePushed) {
|
|
202
|
+
conditions.push('pushed_to IS NULL');
|
|
203
|
+
}
|
|
204
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
205
|
+
const limit = options.limit || 50;
|
|
206
|
+
return database
|
|
207
|
+
.prepare(`SELECT * FROM insights ${where} ORDER BY confidence DESC, created_at DESC LIMIT ?`)
|
|
208
|
+
.all(...values, limit);
|
|
209
|
+
}
|
|
210
|
+
function updateInsightStatus(id, status) {
|
|
211
|
+
const database = getDb();
|
|
212
|
+
database
|
|
213
|
+
.prepare("UPDATE insights SET status = ? WHERE id = ?")
|
|
214
|
+
.run(status, id);
|
|
215
|
+
}
|
|
216
|
+
function markInsightPushed(id, target) {
|
|
217
|
+
const database = getDb();
|
|
218
|
+
const insight = database.prepare('SELECT * FROM insights WHERE id = ?').get(id);
|
|
219
|
+
if (!insight)
|
|
220
|
+
return;
|
|
221
|
+
const existing = insight.pushed_to ? insight.pushed_to.split(',').map((s) => s.trim()) : [];
|
|
222
|
+
if (!existing.includes(target)) {
|
|
223
|
+
existing.push(target);
|
|
224
|
+
}
|
|
225
|
+
database
|
|
226
|
+
.prepare('UPDATE insights SET pushed_to = ?, pushed_at = datetime(\'now\') WHERE id = ?')
|
|
227
|
+
.run(existing.join(','), id);
|
|
228
|
+
}
|
|
229
|
+
function upsertDailySummary(summary) {
|
|
230
|
+
const database = getDb();
|
|
231
|
+
database
|
|
232
|
+
.prepare(`INSERT INTO daily_summaries (date, total_active_time, top_apps, switch_count, insight_count, raw_summary)
|
|
233
|
+
VALUES (@date, @total_active_time, @top_apps, @switch_count, @insight_count, @raw_summary)
|
|
234
|
+
ON CONFLICT(date) DO UPDATE SET
|
|
235
|
+
total_active_time = excluded.total_active_time,
|
|
236
|
+
top_apps = excluded.top_apps,
|
|
237
|
+
switch_count = excluded.switch_count,
|
|
238
|
+
insight_count = daily_summaries.insight_count + excluded.insight_count,
|
|
239
|
+
raw_summary = excluded.raw_summary,
|
|
240
|
+
created_at = datetime('now')`)
|
|
241
|
+
.run(summary);
|
|
242
|
+
}
|
|
243
|
+
function getDailySummary(date) {
|
|
244
|
+
const database = getDb();
|
|
245
|
+
return database
|
|
246
|
+
.prepare('SELECT * FROM daily_summaries WHERE date = ?')
|
|
247
|
+
.get(date);
|
|
248
|
+
}
|
|
249
|
+
function getRecentDailySummaries(days) {
|
|
250
|
+
const database = getDb();
|
|
251
|
+
return database
|
|
252
|
+
.prepare(`SELECT * FROM daily_summaries WHERE date >= date('now', ?) ORDER BY date DESC`)
|
|
253
|
+
.all(`-${days} days`);
|
|
254
|
+
}
|
|
255
|
+
function closeDb() {
|
|
256
|
+
if (db) {
|
|
257
|
+
db.close();
|
|
258
|
+
db = null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,UAAU,QAAQ,CAAC;AAEzB,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAmDnD;AAED,wBAAgB,cAAc,IAAI,IAAI,CAKrC;AA2VD,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.EVENT_PORT = void 0;
|
|
37
|
+
exports.startHttpServer = startHttpServer;
|
|
38
|
+
exports.stopHttpServer = stopHttpServer;
|
|
39
|
+
const http = __importStar(require("http"));
|
|
40
|
+
const db_1 = require("./db");
|
|
41
|
+
const insights_1 = require("./insights");
|
|
42
|
+
const baseline_1 = require("./baseline");
|
|
43
|
+
let httpServer = null;
|
|
44
|
+
const EVENT_PORT = 19840;
|
|
45
|
+
exports.EVENT_PORT = EVENT_PORT;
|
|
46
|
+
function startHttpServer(port) {
|
|
47
|
+
if (httpServer)
|
|
48
|
+
return;
|
|
49
|
+
const listenPort = port || EVENT_PORT;
|
|
50
|
+
httpServer = http.createServer(async (req, res) => {
|
|
51
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
52
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
53
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
54
|
+
if (req.method === 'OPTIONS') {
|
|
55
|
+
res.writeHead(204);
|
|
56
|
+
res.end();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const url = new URL(req.url || '/', `http://localhost:${listenPort}`);
|
|
61
|
+
switch (url.pathname) {
|
|
62
|
+
case '/health':
|
|
63
|
+
return handleHealth(res);
|
|
64
|
+
case '/events':
|
|
65
|
+
return await handlePostEvents(req, res);
|
|
66
|
+
case '/summary':
|
|
67
|
+
return await handleGetSummary(url, res);
|
|
68
|
+
case '/insights':
|
|
69
|
+
return await handleGetInsights(url, res);
|
|
70
|
+
case '/context':
|
|
71
|
+
return await handleGetContext(res);
|
|
72
|
+
case '/baseline':
|
|
73
|
+
return await handleGetBaseline(res);
|
|
74
|
+
case '/insight-status':
|
|
75
|
+
return await handleUpdateInsightStatus(req, res);
|
|
76
|
+
case '/sync/push':
|
|
77
|
+
return await handleSyncPush(req, res);
|
|
78
|
+
case '/sync/pull':
|
|
79
|
+
return await handleSyncPull(req, res);
|
|
80
|
+
default:
|
|
81
|
+
res.writeHead(404);
|
|
82
|
+
res.end(JSON.stringify({ error: 'not found' }));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
res.writeHead(500);
|
|
87
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
httpServer.listen(listenPort, '127.0.0.1', () => {
|
|
91
|
+
console.error(`[daybrain] HTTP API on http://127.0.0.1:${listenPort} (for browser extension)`);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function stopHttpServer() {
|
|
95
|
+
if (httpServer) {
|
|
96
|
+
httpServer.close();
|
|
97
|
+
httpServer = null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function handleHealth(res) {
|
|
101
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
102
|
+
res.end(JSON.stringify({ status: 'ok', version: '0.2.0' }));
|
|
103
|
+
}
|
|
104
|
+
async function handlePostEvents(req, res) {
|
|
105
|
+
const body = await readBody(req);
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(body);
|
|
108
|
+
if (Array.isArray(parsed)) {
|
|
109
|
+
const events = parsed.map((e) => ({
|
|
110
|
+
source: 'native',
|
|
111
|
+
bucket_id: 'browser-extension',
|
|
112
|
+
timestamp: e.timestamp || new Date().toISOString(),
|
|
113
|
+
duration: e.duration || 0,
|
|
114
|
+
app: e.app || 'Chrome',
|
|
115
|
+
title: e.title || '',
|
|
116
|
+
url: e.url || '',
|
|
117
|
+
raw_data: JSON.stringify(e),
|
|
118
|
+
}));
|
|
119
|
+
const count = (0, db_1.insertRawEvents)(events);
|
|
120
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
121
|
+
res.end(JSON.stringify({ stored: count }));
|
|
122
|
+
}
|
|
123
|
+
else if (parsed.url || parsed.title) {
|
|
124
|
+
const count = (0, db_1.insertRawEvents)([{
|
|
125
|
+
source: 'native',
|
|
126
|
+
bucket_id: 'browser-extension',
|
|
127
|
+
timestamp: parsed.timestamp || new Date().toISOString(),
|
|
128
|
+
duration: parsed.duration || 0,
|
|
129
|
+
app: parsed.app || 'Chrome',
|
|
130
|
+
title: parsed.title || '',
|
|
131
|
+
url: parsed.url || '',
|
|
132
|
+
raw_data: JSON.stringify(parsed),
|
|
133
|
+
}]);
|
|
134
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
135
|
+
res.end(JSON.stringify({ stored: count }));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
res.writeHead(400);
|
|
139
|
+
res.end(JSON.stringify({ error: 'missing url or title' }));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
res.writeHead(400);
|
|
144
|
+
res.end(JSON.stringify({ error: 'invalid JSON body' }));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function handleGetSummary(url, res) {
|
|
148
|
+
const dateParam = url.searchParams.get('date');
|
|
149
|
+
const now = new Date();
|
|
150
|
+
let startOfDay;
|
|
151
|
+
let endOfDay;
|
|
152
|
+
if (dateParam && /^\d{4}-\d{2}-\d{2}$/.test(dateParam)) {
|
|
153
|
+
const [y, m, d] = dateParam.split('-').map(Number);
|
|
154
|
+
startOfDay = new Date(y, m - 1, d, 0, 0, 0, 0);
|
|
155
|
+
endOfDay = new Date(y, m - 1, d, 23, 59, 59, 999);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
|
159
|
+
endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
|
160
|
+
}
|
|
161
|
+
const dateStr = `${startOfDay.getFullYear()}-${String(startOfDay.getMonth() + 1).padStart(2, '0')}-${String(startOfDay.getDate()).padStart(2, '0')}`;
|
|
162
|
+
const summary = (0, db_1.getDailySummary)(dateStr);
|
|
163
|
+
const events = (0, db_1.getRawEvents)({
|
|
164
|
+
periodStart: startOfDay.toISOString(),
|
|
165
|
+
periodEnd: endOfDay.toISOString(),
|
|
166
|
+
limit: 5000,
|
|
167
|
+
});
|
|
168
|
+
if (!summary) {
|
|
169
|
+
const engine = (0, insights_1.createInsightEngine)();
|
|
170
|
+
const events = (0, db_1.getRawEvents)({
|
|
171
|
+
periodStart: startOfDay.toISOString(),
|
|
172
|
+
periodEnd: endOfDay.toISOString(),
|
|
173
|
+
limit: 5000,
|
|
174
|
+
});
|
|
175
|
+
if (events.length === 0) {
|
|
176
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
177
|
+
res.end(JSON.stringify({
|
|
178
|
+
date: dateStr,
|
|
179
|
+
active_time: 0,
|
|
180
|
+
top_apps: [],
|
|
181
|
+
insights: [],
|
|
182
|
+
message: 'No activity recorded yet today.',
|
|
183
|
+
}));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const mapped = events.map((e) => ({
|
|
187
|
+
timestamp: e.timestamp,
|
|
188
|
+
duration: e.duration,
|
|
189
|
+
data: { app: e.app, title: e.title, url: e.url },
|
|
190
|
+
}));
|
|
191
|
+
const result = engine.runFullAnalysis(mapped);
|
|
192
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
193
|
+
res.end(JSON.stringify({
|
|
194
|
+
date: dateStr,
|
|
195
|
+
active_time: Math.round(result.totalActiveTime / 6) / 10,
|
|
196
|
+
top_apps: result.appSummaries.slice(0, 8).map((a) => ({
|
|
197
|
+
app: a.app,
|
|
198
|
+
minutes: Math.round(a.totalDuration / 6) / 10,
|
|
199
|
+
percentage: Math.round(a.percentage),
|
|
200
|
+
})),
|
|
201
|
+
switch_count: result.contextSwitches.reduce((s, c) => s + c.count, 0),
|
|
202
|
+
insights: result.insights.slice(0, 6).map((i, idx) => ({
|
|
203
|
+
id: idx,
|
|
204
|
+
type: i.type,
|
|
205
|
+
title: i.title,
|
|
206
|
+
description: i.description,
|
|
207
|
+
confidence: Math.round(i.confidence * 100),
|
|
208
|
+
action_text: i.action_text,
|
|
209
|
+
})),
|
|
210
|
+
raw_summary: result.summary,
|
|
211
|
+
}));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const storedInsights = (0, db_1.getInsights)({
|
|
215
|
+
periodStart: dateStr,
|
|
216
|
+
periodEnd: dateStr + 'T23:59:59.999Z',
|
|
217
|
+
limit: 10,
|
|
218
|
+
});
|
|
219
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
220
|
+
res.end(JSON.stringify({
|
|
221
|
+
date: dateStr,
|
|
222
|
+
active_time: Math.round(summary.total_active_time / 6) / 10,
|
|
223
|
+
top_apps: JSON.parse(summary.top_apps || '[]'),
|
|
224
|
+
switch_count: summary.switch_count,
|
|
225
|
+
insights: storedInsights.map((i) => ({
|
|
226
|
+
id: i.id,
|
|
227
|
+
type: i.type,
|
|
228
|
+
title: i.title,
|
|
229
|
+
description: i.description,
|
|
230
|
+
confidence: Math.round(i.confidence * 100),
|
|
231
|
+
action_text: i.action_text,
|
|
232
|
+
status: i.status,
|
|
233
|
+
})),
|
|
234
|
+
raw_summary: summary.raw_summary,
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
async function handleGetInsights(url, res) {
|
|
238
|
+
const stored = (0, db_1.getInsights)({
|
|
239
|
+
limit: 20,
|
|
240
|
+
includePushed: true,
|
|
241
|
+
});
|
|
242
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
243
|
+
res.end(JSON.stringify({
|
|
244
|
+
insights: stored.map((i) => ({
|
|
245
|
+
id: i.id,
|
|
246
|
+
type: i.type,
|
|
247
|
+
title: i.title,
|
|
248
|
+
description: i.description,
|
|
249
|
+
confidence: Math.round(i.confidence * 100),
|
|
250
|
+
action_text: i.action_text,
|
|
251
|
+
created_at: i.created_at,
|
|
252
|
+
})),
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
async function handleGetContext(res) {
|
|
256
|
+
const now = new Date();
|
|
257
|
+
const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
258
|
+
const summary = (0, db_1.getDailySummary)(dateStr);
|
|
259
|
+
const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
|
260
|
+
const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
|
261
|
+
const storedInsights = (0, db_1.getInsights)({
|
|
262
|
+
periodStart: dateStr,
|
|
263
|
+
periodEnd: dateStr + 'T23:59:59.999Z',
|
|
264
|
+
limit: 10,
|
|
265
|
+
});
|
|
266
|
+
let totalMinutes;
|
|
267
|
+
let topApps;
|
|
268
|
+
let insightsList = storedInsights;
|
|
269
|
+
if (summary) {
|
|
270
|
+
totalMinutes = Math.round(summary.total_active_time / 60);
|
|
271
|
+
topApps = JSON.parse(summary.top_apps || '[]');
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
const events = (0, db_1.getRawEvents)({
|
|
275
|
+
periodStart: startOfDay.toISOString(),
|
|
276
|
+
periodEnd: endOfDay.toISOString(),
|
|
277
|
+
limit: 5000,
|
|
278
|
+
});
|
|
279
|
+
if (events.length > 0) {
|
|
280
|
+
const engine = (0, insights_1.createInsightEngine)();
|
|
281
|
+
const mapped = events.map((e) => ({
|
|
282
|
+
timestamp: e.timestamp,
|
|
283
|
+
duration: e.duration,
|
|
284
|
+
data: { app: e.app, title: e.title, url: e.url },
|
|
285
|
+
}));
|
|
286
|
+
const result = engine.runFullAnalysis(mapped);
|
|
287
|
+
totalMinutes = Math.round(result.totalActiveTime / 60);
|
|
288
|
+
topApps = result.appSummaries.slice(0, 6).map((a) => ({
|
|
289
|
+
app: a.app,
|
|
290
|
+
minutes: Math.round(a.totalDuration / 6) / 10,
|
|
291
|
+
}));
|
|
292
|
+
if (insightsList.length === 0) {
|
|
293
|
+
insightsList = result.insights.map((i) => ({
|
|
294
|
+
...i,
|
|
295
|
+
created_at: new Date().toISOString(),
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
totalMinutes = 0;
|
|
301
|
+
topApps = [];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
let text = `Here is my activity context for today:\n\n`;
|
|
305
|
+
if (totalMinutes > 0) {
|
|
306
|
+
text += `I've been active for ${totalMinutes} minutes across ${topApps.length} applications.\n\n`;
|
|
307
|
+
text += `Top apps:\n`;
|
|
308
|
+
for (const a of topApps.slice(0, 6)) {
|
|
309
|
+
text += `- ${a.app}: ${a.minutes} min\n`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
text += `No activity recorded yet today.\n`;
|
|
314
|
+
}
|
|
315
|
+
if (storedInsights.length > 0) {
|
|
316
|
+
text += `\nDetected insights:\n`;
|
|
317
|
+
for (const i of storedInsights.slice(0, 5)) {
|
|
318
|
+
text += `- [${i.type}] ${i.title}\n ${i.description}\n`;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
text += `\n(Generated by DayBrain — local AI memory engine)`;
|
|
322
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
323
|
+
res.end(JSON.stringify({ text, total_minutes: totalMinutes, insights: storedInsights.length }));
|
|
324
|
+
}
|
|
325
|
+
async function handleGetBaseline(res) {
|
|
326
|
+
const now = new Date();
|
|
327
|
+
const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
|
328
|
+
const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
|
329
|
+
const events = (0, db_1.getRawEvents)({
|
|
330
|
+
periodStart: startOfDay.toISOString(),
|
|
331
|
+
periodEnd: endOfDay.toISOString(),
|
|
332
|
+
limit: 10000,
|
|
333
|
+
});
|
|
334
|
+
if (events.length === 0) {
|
|
335
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
336
|
+
res.end(JSON.stringify({ anomalies: [], message: 'No activity today yet.' }));
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const mapped = events.map((e) => ({
|
|
340
|
+
timestamp: e.timestamp,
|
|
341
|
+
duration: e.duration,
|
|
342
|
+
data: { app: e.app, title: e.title, url: e.url },
|
|
343
|
+
}));
|
|
344
|
+
const engine = (0, insights_1.createInsightEngine)();
|
|
345
|
+
const baseline = (0, baseline_1.generateBaseline)(mapped, engine);
|
|
346
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
347
|
+
res.end(JSON.stringify(baseline));
|
|
348
|
+
}
|
|
349
|
+
async function handleUpdateInsightStatus(req, res) {
|
|
350
|
+
const body = await readBody(req);
|
|
351
|
+
try {
|
|
352
|
+
const { id, status } = JSON.parse(body);
|
|
353
|
+
if (!id || !status) {
|
|
354
|
+
res.writeHead(400);
|
|
355
|
+
res.end(JSON.stringify({ error: 'id and status required' }));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
(0, db_1.updateInsightStatus)(Number(id), String(status));
|
|
359
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
360
|
+
res.end(JSON.stringify({ ok: true }));
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
res.writeHead(400);
|
|
364
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async function handleSyncPush(req, res) {
|
|
368
|
+
const body = await readBody(req);
|
|
369
|
+
try {
|
|
370
|
+
const parsed = JSON.parse(body);
|
|
371
|
+
const deviceId = req.headers['x-daybrain-device'] || 'remote';
|
|
372
|
+
const events = Array.isArray(parsed) ? parsed : [parsed];
|
|
373
|
+
const rawEvents = events.map((e) => ({
|
|
374
|
+
source: 'native',
|
|
375
|
+
bucket_id: `sync-${deviceId}`,
|
|
376
|
+
timestamp: e.timestamp || new Date().toISOString(),
|
|
377
|
+
duration: e.duration || 0,
|
|
378
|
+
app: e.app || 'Unknown',
|
|
379
|
+
title: e.title || '',
|
|
380
|
+
url: e.url || '',
|
|
381
|
+
raw_data: JSON.stringify({ ...e, deviceId }),
|
|
382
|
+
}));
|
|
383
|
+
const count = (0, db_1.insertRawEvents)(rawEvents);
|
|
384
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
385
|
+
res.end(JSON.stringify({ stored: count, device: deviceId }));
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
res.writeHead(400);
|
|
389
|
+
res.end(JSON.stringify({ error: 'invalid JSON body' }));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
async function handleSyncPull(req, res) {
|
|
393
|
+
const url = new URL(req.url || '/', `http://localhost`);
|
|
394
|
+
const since = url.searchParams.get('since') || new Date(Date.now() - 86400000).toISOString();
|
|
395
|
+
const limit = Math.min(Number(url.searchParams.get('limit')) || 1000, 5000);
|
|
396
|
+
const events = (0, db_1.getRawEvents)({
|
|
397
|
+
periodStart: since,
|
|
398
|
+
limit,
|
|
399
|
+
});
|
|
400
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
401
|
+
res.end(JSON.stringify({ events, count: events.length, since }));
|
|
402
|
+
}
|
|
403
|
+
function readBody(req) {
|
|
404
|
+
return new Promise((resolve, reject) => {
|
|
405
|
+
let data = '';
|
|
406
|
+
req.on('data', chunk => { data += chunk; });
|
|
407
|
+
req.on('end', () => resolve(data));
|
|
408
|
+
req.on('error', reject);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
//# sourceMappingURL=http.js.map
|