@memtensor/memos-local-openclaw-plugin 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.env.example +6 -0
  2. package/README.md +90 -25
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +8 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/embedding/local.d.ts.map +1 -1
  7. package/dist/embedding/local.js +3 -2
  8. package/dist/embedding/local.js.map +1 -1
  9. package/dist/skill/evaluator.js +1 -1
  10. package/dist/skill/evaluator.js.map +1 -1
  11. package/dist/skill/generator.js +1 -1
  12. package/dist/skill/generator.js.map +1 -1
  13. package/dist/skill/upgrader.js +1 -1
  14. package/dist/skill/upgrader.js.map +1 -1
  15. package/dist/skill/validator.js +1 -1
  16. package/dist/skill/validator.js.map +1 -1
  17. package/dist/storage/sqlite.d.ts +4 -0
  18. package/dist/storage/sqlite.d.ts.map +1 -1
  19. package/dist/storage/sqlite.js +19 -0
  20. package/dist/storage/sqlite.js.map +1 -1
  21. package/dist/telemetry.d.ts +37 -0
  22. package/dist/telemetry.d.ts.map +1 -0
  23. package/dist/telemetry.js +179 -0
  24. package/dist/telemetry.js.map +1 -0
  25. package/dist/types.d.ts +6 -1
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/types.js.map +1 -1
  28. package/dist/viewer/html.d.ts +1 -1
  29. package/dist/viewer/html.d.ts.map +1 -1
  30. package/dist/viewer/html.js +828 -52
  31. package/dist/viewer/html.js.map +1 -1
  32. package/dist/viewer/server.d.ts +25 -1
  33. package/dist/viewer/server.d.ts.map +1 -1
  34. package/dist/viewer/server.js +807 -0
  35. package/dist/viewer/server.js.map +1 -1
  36. package/index.ts +31 -3
  37. package/openclaw.plugin.json +3 -3
  38. package/package.json +4 -3
  39. package/src/config.ts +11 -0
  40. package/src/embedding/local.ts +3 -2
  41. package/src/skill/evaluator.ts +1 -1
  42. package/src/skill/generator.ts +1 -1
  43. package/src/skill/upgrader.ts +1 -1
  44. package/src/skill/validator.ts +1 -1
  45. package/src/storage/sqlite.ts +29 -0
  46. package/src/telemetry.ts +160 -0
  47. package/src/types.ts +7 -1
  48. package/src/viewer/html.ts +828 -52
  49. package/src/viewer/server.ts +818 -1
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Telemetry module — anonymous usage analytics via PostHog.
3
+ *
4
+ * Privacy-first design:
5
+ * - Enabled by default with anonymous data only; opt-out via TELEMETRY_ENABLED=false
6
+ * - Uses a random anonymous ID persisted locally (no PII)
7
+ * - Never sends memory content, queries, or any user data
8
+ * - Only sends aggregate counts, tool names, latencies, and version info
9
+ */
10
+
11
+ import { PostHog } from "posthog-node";
12
+ import * as fs from "fs";
13
+ import * as path from "path";
14
+ import * as os from "os";
15
+ import { v4 as uuidv4 } from "uuid";
16
+ import type { Logger } from "./types";
17
+
18
+ export interface TelemetryConfig {
19
+ enabled?: boolean;
20
+ posthogApiKey?: string;
21
+ posthogHost?: string;
22
+ }
23
+
24
+ const DEFAULT_POSTHOG_API_KEY = "phc_7lae6UC5jyImDefX6uub7zCxWyswCGNoBifCKqjvDrI";
25
+ const DEFAULT_POSTHOG_HOST = "https://eu.i.posthog.com";
26
+
27
+ export class Telemetry {
28
+ private client: PostHog | null = null;
29
+ private distinctId: string;
30
+ private enabled: boolean;
31
+ private pluginVersion: string;
32
+ private log: Logger;
33
+ private dailyPingSent = false;
34
+ private dailyPingDate = "";
35
+
36
+ constructor(config: TelemetryConfig, stateDir: string, pluginVersion: string, log: Logger) {
37
+ this.log = log;
38
+ this.pluginVersion = pluginVersion;
39
+ this.enabled = config.enabled !== false;
40
+ this.distinctId = this.loadOrCreateAnonymousId(stateDir);
41
+
42
+ if (!this.enabled) {
43
+ this.log.debug("Telemetry disabled (opt-out via TELEMETRY_ENABLED=false)");
44
+ return;
45
+ }
46
+
47
+ const apiKey = config.posthogApiKey || DEFAULT_POSTHOG_API_KEY;
48
+ try {
49
+ this.client = new PostHog(apiKey, {
50
+ host: config.posthogHost || DEFAULT_POSTHOG_HOST,
51
+ flushAt: 10,
52
+ flushInterval: 30_000,
53
+ });
54
+ this.log.debug("Telemetry initialized (PostHog)");
55
+ } catch (err) {
56
+ this.log.warn(`Telemetry init failed: ${err}`);
57
+ this.enabled = false;
58
+ }
59
+ }
60
+
61
+ private loadOrCreateAnonymousId(stateDir: string): string {
62
+ const idFile = path.join(stateDir, "memos-local", ".anonymous-id");
63
+ try {
64
+ const existing = fs.readFileSync(idFile, "utf-8").trim();
65
+ if (existing.length > 10) return existing;
66
+ } catch {}
67
+
68
+ const newId = uuidv4();
69
+ try {
70
+ fs.mkdirSync(path.dirname(idFile), { recursive: true });
71
+ fs.writeFileSync(idFile, newId, "utf-8");
72
+ } catch {}
73
+ return newId;
74
+ }
75
+
76
+ private capture(event: string, properties?: Record<string, unknown>): void {
77
+ if (!this.enabled || !this.client) return;
78
+
79
+ try {
80
+ this.client.capture({
81
+ distinctId: this.distinctId,
82
+ event,
83
+ properties: {
84
+ plugin_version: this.pluginVersion,
85
+ os: os.platform(),
86
+ os_version: os.release(),
87
+ node_version: process.version,
88
+ arch: os.arch(),
89
+ ...properties,
90
+ },
91
+ });
92
+ } catch {
93
+ // best-effort, never throw
94
+ }
95
+ }
96
+
97
+ // ─── Public event methods ───
98
+
99
+ trackPluginStarted(embeddingProvider: string, summarizerProvider: string): void {
100
+ this.capture("plugin_started", {
101
+ embedding_provider: embeddingProvider,
102
+ summarizer_provider: summarizerProvider,
103
+ });
104
+ this.maybeSendDailyPing();
105
+ }
106
+
107
+ trackToolCalled(toolName: string, latencyMs: number, success: boolean): void {
108
+ this.capture("tool_called", {
109
+ tool_name: toolName,
110
+ latency_ms: Math.round(latencyMs),
111
+ success,
112
+ });
113
+ }
114
+
115
+ trackMemoryIngested(chunkCount: number): void {
116
+ this.capture("memory_ingested", {
117
+ chunk_count: chunkCount,
118
+ });
119
+ }
120
+
121
+ trackSkillInstalled(skillName: string): void {
122
+ this.capture("skill_installed", {
123
+ skill_name: skillName,
124
+ });
125
+ }
126
+
127
+ trackSkillEvolved(skillName: string, upgradeType: string): void {
128
+ this.capture("skill_evolved", {
129
+ skill_name: skillName,
130
+ upgrade_type: upgradeType,
131
+ });
132
+ }
133
+
134
+ trackViewerOpened(): void {
135
+ this.capture("viewer_opened");
136
+ }
137
+
138
+ trackAutoRecall(hitCount: number, latencyMs: number): void {
139
+ this.capture("auto_recall", {
140
+ hit_count: hitCount,
141
+ latency_ms: Math.round(latencyMs),
142
+ });
143
+ }
144
+
145
+ private maybeSendDailyPing(): void {
146
+ const today = new Date().toISOString().slice(0, 10);
147
+ if (this.dailyPingSent && this.dailyPingDate === today) return;
148
+ this.dailyPingSent = true;
149
+ this.dailyPingDate = today;
150
+ this.capture("daily_active");
151
+ }
152
+
153
+ async shutdown(): Promise<void> {
154
+ if (this.client) {
155
+ try {
156
+ await this.client.shutdown();
157
+ } catch {}
158
+ }
159
+ }
160
+ }
package/src/types.ts CHANGED
@@ -229,7 +229,12 @@ export interface SkillEvolutionConfig {
229
229
  minConfidence?: number;
230
230
  maxSkillLines?: number;
231
231
  autoInstall?: boolean;
232
- summarizer?: SummarizerConfig;
232
+ }
233
+
234
+ export interface TelemetryConfig {
235
+ enabled?: boolean;
236
+ posthogApiKey?: string;
237
+ posthogHost?: string;
233
238
  }
234
239
 
235
240
  export interface MemosLocalConfig {
@@ -254,6 +259,7 @@ export interface MemosLocalConfig {
254
259
  evidenceWrapperTag?: string;
255
260
  };
256
261
  skillEvolution?: SkillEvolutionConfig;
262
+ telemetry?: TelemetryConfig;
257
263
  }
258
264
 
259
265
  // ─── Defaults ───