authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-20 11:43:39-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-20 11:43:39-07:00
logef2569196f164df1096ebd8c614fafa0ebc43be9
tree2ee90805e63d9649f8cafa958804e19bfe8c84c8
parent34cef6202b6c2eb0594df0eca412b36daf819ba2
signature Signed by PGP key B5690EEEBB952194

Prefer Claude user ID for telemetry identity (#1249)


1 files changed, 24 insertions(+), 1 deletions(-)

apps/server/src/telemetry/Identify.ts+24-1
......@@ -9,6 +9,10 @@ const CodexAuthJsonSchema = Schema.Struct({
99 }),
1010});
1111
12const ClaudeJsonSchema = Schema.Struct({
13 userID: Schema.String,
14});
15
1216class IdentifyUserError extends Schema.TaggedErrorClass<IdentifyUserError>()("IdentifyUserError", {
1317 message: Schema.String,
1418 cause: Schema.optional(Schema.Defect),
......@@ -37,6 +41,19 @@ const getCodexAccountId = Effect.gen(function* () {
3741 return authJson.tokens.account_id;
3842});
3943
44const getClaudeUserId = Effect.gen(function* () {
45 const fileSystem = yield* FileSystem.FileSystem;
46 const path = yield* Path.Path;
47
48 const claudeJsonPath = path.join(homedir(), ".claude.json");
49 const claudeJson = yield* Effect.flatMap(
50 fileSystem.readFileString(claudeJsonPath),
51 Schema.decodeEffect(Schema.fromJsonString(ClaudeJsonSchema)),
52 );
53
54 return claudeJson.userID;
55});
56
4057const upsertAnonymousId = Effect.gen(function* () {
4158 const fileSystem = yield* FileSystem.FileSystem;
4259 const path = yield* Path.Path;
......@@ -59,7 +76,8 @@ const upsertAnonymousId = Effect.gen(function* () {
5976/**
6077 * getTelemetryIdentifier - Users are "identified" by finding the first match of the following, then hashing the value.
6178 * 1. ~/.codex/auth.json tokens.account_id
62 * 2. ~/.t3/telemetry/anonymous-id
79 * 2. ~/.claude.json userID
80 * 3. ~/.t3/telemetry/anonymous-id
6381 */
6482export const getTelemetryIdentifier = Effect.gen(function* () {
6583 const codexAccountId = yield* Effect.result(getCodexAccountId);
......@@ -67,6 +85,11 @@ export const getTelemetryIdentifier = Effect.gen(function* () {
6785 return yield* hash(codexAccountId.success);
6886 }
6987
88 const claudeUserId = yield* Effect.result(getClaudeUserId);
89 if (claudeUserId._tag === "Success") {
90 return yield* hash(claudeUserId.success);
91 }
92
7093 const anonymousId = yield* Effect.result(upsertAnonymousId);
7194 if (anonymousId._tag === "Success") {
7295 return yield* hash(anonymousId.success);