mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(telemetry): carry app_version on instance_started (#681)
app_version was attached only to feedback events; every other allowlist stripped it, so the install base could not be segmented by release. One property on the once-per-boot census event covers it. Fixes #674.
This commit is contained in:
+1
-1
@@ -27,7 +27,7 @@ Emitted from `apps/api` through `trackEvent()`; properties are filtered by `anal
|
||||
|
||||
| Event | Fires when | Key properties |
|
||||
| --- | --- | --- |
|
||||
| `instance_started` | Once per boot | `arch`, `os_platform`, `deploy_mode`, `gpu_present` |
|
||||
| `instance_started` | Once per boot | `arch`, `os_platform`, `deploy_mode`, `gpu_present`, `app_version` |
|
||||
| `auth_login` | A login succeeds | `method` (`password` or `oidc`) |
|
||||
| `auth_login_failed` | A login attempt fails | `method` (`password` or `oidc`) |
|
||||
| `tool_used` | A tool job finishes | `tool_id`, `status`, `duration_ms`, `category`, `is_ai_tool`, `is_batch`, `input_format`, `output_format`, `bytes_in`, `bytes_out`, `execution_hint`, `error_code`, `error_kind` |
|
||||
|
||||
@@ -26,7 +26,7 @@ const ALLOWED: Record<string, ReadonlySet<string>> = {
|
||||
"status",
|
||||
]),
|
||||
ai_bundle_action: new Set(["bundle_id", "action", "duration_ms"]),
|
||||
instance_started: new Set(["arch", "os_platform", "deploy_mode", "gpu_present"]),
|
||||
instance_started: new Set(["arch", "os_platform", "deploy_mode", "gpu_present", "app_version"]),
|
||||
auth_login: new Set(["method"]),
|
||||
auth_login_failed: new Set(["method"]),
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import type { InstanceStartedProperties } from "@snapotter/shared";
|
||||
import { APP_VERSION, type InstanceStartedProperties } from "@snapotter/shared";
|
||||
import { deployMode } from "./deploy-mode.js";
|
||||
|
||||
// Facts about the running instance, shipped once at boot as the
|
||||
@@ -19,5 +19,6 @@ export function gatherSystemProperties(): InstanceStartedProperties {
|
||||
os_platform: os.platform(),
|
||||
deploy_mode: deployMode(),
|
||||
gpu_present: existsSync("/dev/nvidia0"),
|
||||
app_version: APP_VERSION,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ export interface InstanceStartedProperties {
|
||||
os_platform: string;
|
||||
deploy_mode: "embedded" | "external" | "native";
|
||||
gpu_present: boolean;
|
||||
/** APP_VERSION at boot; the only event property that segments the install base by release. */
|
||||
app_version: string;
|
||||
}
|
||||
|
||||
export interface EditorToolUsedProperties {
|
||||
|
||||
@@ -59,6 +59,7 @@ describe("sanitizeEventProperties", () => {
|
||||
os_platform: "linux",
|
||||
deploy_mode: "embedded",
|
||||
gpu_present: false,
|
||||
app_version: "2.2.0",
|
||||
hostname: "leaked-hostname",
|
||||
});
|
||||
expect(out).toEqual({
|
||||
@@ -66,6 +67,7 @@ describe("sanitizeEventProperties", () => {
|
||||
os_platform: "linux",
|
||||
deploy_mode: "embedded",
|
||||
gpu_present: false,
|
||||
app_version: "2.2.0",
|
||||
});
|
||||
expect(out).not.toHaveProperty("hostname");
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ vi.mock("../../../apps/api/src/lib/deploy-mode.js", () => ({
|
||||
deployMode: mockDeployMode,
|
||||
}));
|
||||
|
||||
import { APP_VERSION } from "@snapotter/shared";
|
||||
import { gatherSystemProperties } from "../../../apps/api/src/lib/system-info.js";
|
||||
|
||||
describe("gatherSystemProperties", () => {
|
||||
@@ -47,6 +48,12 @@ describe("gatherSystemProperties", () => {
|
||||
expect(gatherSystemProperties().gpu_present).toBe(false);
|
||||
});
|
||||
|
||||
it("carries the app version so release adoption is measurable", () => {
|
||||
mockDeployMode.mockReturnValue("embedded");
|
||||
mockExistsSync.mockReturnValue(false);
|
||||
expect(gatherSystemProperties().app_version).toBe(APP_VERSION);
|
||||
});
|
||||
|
||||
it("passes through deploy mode and os platform", () => {
|
||||
mockDeployMode.mockReturnValue("embedded");
|
||||
mockExistsSync.mockReturnValue(false);
|
||||
|
||||
Reference in New Issue
Block a user