Add optional name, email, avatar, and denchOrgId fields to telemetry.json. When present, all telemetry layers (CLI, web server, web client, OpenClaw plugin) call PostHog identify() with $name, $email, $avatar, and dench_org_id person properties. Remove $process_person_profile:false from all layers so every install gets a PostHog person profile. Enable session replay with masking controlled by privacy mode (all text/inputs masked when on, nothing masked when off).
106 lines
2.6 KiB
TypeScript
106 lines
2.6 KiB
TypeScript
const DEFAULT_HOST = "https://us.i.posthog.com";
|
|
const FLUSH_INTERVAL_MS = 15_000;
|
|
const FLUSH_AT = 10;
|
|
|
|
export interface CaptureEvent {
|
|
distinctId: string;
|
|
event: string;
|
|
properties?: Record<string, unknown>;
|
|
}
|
|
|
|
/**
|
|
* Minimal PostHog client using the HTTP capture API directly.
|
|
* Zero npm dependencies -- uses built-in fetch (Node 18+).
|
|
*/
|
|
export class PostHogClient {
|
|
private apiKey: string;
|
|
private host: string;
|
|
private globalProperties: Record<string, unknown>;
|
|
private queue: Array<Record<string, unknown>> = [];
|
|
private timer: ReturnType<typeof setInterval> | null = null;
|
|
|
|
constructor(apiKey: string, host?: string, globalProperties?: Record<string, unknown>) {
|
|
this.apiKey = apiKey;
|
|
this.host = (host || DEFAULT_HOST).replace(/\/$/, "");
|
|
this.globalProperties = globalProperties ?? {};
|
|
this.timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
if (this.timer.unref) this.timer.unref();
|
|
}
|
|
|
|
capture(event: CaptureEvent): void {
|
|
this.queue.push({
|
|
event: event.event,
|
|
distinct_id: event.distinctId,
|
|
properties: {
|
|
...this.globalProperties,
|
|
...event.properties,
|
|
$lib: "denchclaw-posthog-plugin",
|
|
},
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
|
|
if (this.queue.length >= FLUSH_AT) {
|
|
this.flush();
|
|
}
|
|
}
|
|
|
|
flush(): void {
|
|
if (this.queue.length === 0) return;
|
|
|
|
const batch = this.queue.splice(0);
|
|
const body = JSON.stringify({
|
|
api_key: this.apiKey,
|
|
batch,
|
|
});
|
|
|
|
fetch(`${this.host}/batch/`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body,
|
|
}).catch(() => {
|
|
// Fail silently -- telemetry should never block the gateway.
|
|
});
|
|
}
|
|
|
|
identify(distinctId: string, properties: Record<string, unknown>): void {
|
|
this.queue.push({
|
|
event: "$identify",
|
|
distinct_id: distinctId,
|
|
properties: {
|
|
...this.globalProperties,
|
|
$set: properties,
|
|
$lib: "denchclaw-posthog-plugin",
|
|
},
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
|
|
if (this.queue.length >= FLUSH_AT) {
|
|
this.flush();
|
|
}
|
|
}
|
|
|
|
async shutdown(): Promise<void> {
|
|
if (this.timer) {
|
|
clearInterval(this.timer);
|
|
this.timer = null;
|
|
}
|
|
this.flush();
|
|
}
|
|
}
|
|
|
|
export function createPostHogClient(
|
|
apiKey: string,
|
|
host?: string,
|
|
globalProperties?: Record<string, unknown>,
|
|
): PostHogClient {
|
|
return new PostHogClient(apiKey, host, globalProperties);
|
|
}
|
|
|
|
export async function shutdownPostHogClient(client: PostHogClient): Promise<void> {
|
|
try {
|
|
await client.shutdown();
|
|
} catch {
|
|
// Non-fatal.
|
|
}
|
|
}
|