authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-05 18:03:54-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-05 23:40:49-08:00
log477b4f9d0ec7e7a48cb74357ea00ecf7bddfc62f
tree9ab54bd81a91cda272bd193f8a0ae5e1aaee94f7
parentd5d88270f0d9cdc9797a650ed6ccace28cb1dc39

Wire telemetry in main and store anonymous ID in state dir

- move `AnalyticsServiceLayerLive` from CLI runtime setup into `main` server layer - persist telemetry `anonymous-id` under configured `stateDir` instead of home directory - add `FetchHttpClient.layer` to `main.test.ts` test layer wiring

4 files changed, 7 insertions(+), 3 deletions(-)

apps/server/src/index.ts-2
...@@ -9,12 +9,10 @@ import { Command } from "effect/unstable/cli";...@@ -9,12 +9,10 @@ import { Command } from "effect/unstable/cli";
9import { version } from "../package.json" with { type: "json" };9import { version } from "../package.json" with { type: "json" };
10import { ServerLive } from "./wsServer";10import { ServerLive } from "./wsServer";
11import { NetService } from "@t3tools/shared/Net";11import { NetService } from "@t3tools/shared/Net";
12import { AnalyticsServiceLayerLive } from "./telemetry/Layers/AnalyticsService";
13import { FetchHttpClient } from "effect/unstable/http";12import { FetchHttpClient } from "effect/unstable/http";
1413
15const RuntimeLayer = Layer.empty.pipe(14const RuntimeLayer = Layer.empty.pipe(
16 Layer.provideMerge(CliConfig.layer),15 Layer.provideMerge(CliConfig.layer),
17 Layer.provideMerge(AnalyticsServiceLayerLive),
18 Layer.provideMerge(ServerLive),16 Layer.provideMerge(ServerLive),
19 Layer.provideMerge(OpenLive),17 Layer.provideMerge(OpenLive),
20 Layer.provideMerge(NetService.layer),18 Layer.provideMerge(NetService.layer),
apps/server/src/main.test.ts+2
...@@ -5,6 +5,7 @@ import * as ConfigProvider from "effect/ConfigProvider";...@@ -5,6 +5,7 @@ import * as ConfigProvider from "effect/ConfigProvider";
5import * as Effect from "effect/Effect";5import * as Effect from "effect/Effect";
6import * as Layer from "effect/Layer";6import * as Layer from "effect/Layer";
7import * as Command from "effect/unstable/cli/Command";7import * as Command from "effect/unstable/cli/Command";
8import { FetchHttpClient } from "effect/unstable/http";
8import { beforeEach } from "vitest";9import { beforeEach } from "vitest";
9import { NetService } from "@t3tools/shared/Net";10import { NetService } from "@t3tools/shared/Net";
1011
...@@ -52,6 +53,7 @@ const testLayer = Layer.mergeAll(...@@ -52,6 +53,7 @@ const testLayer = Layer.mergeAll(
52 record: () => Effect.void,53 record: () => Effect.void,
53 flush: Effect.void,54 flush: Effect.void,
54 }),55 }),
56 FetchHttpClient.layer,
55 NodeServices.layer,57 NodeServices.layer,
56);58);
5759
apps/server/src/main.ts+2
...@@ -23,6 +23,7 @@ import { makeServerProviderLayer, makeServerRuntimeServicesLayer } from "./serve...@@ -23,6 +23,7 @@ import { makeServerProviderLayer, makeServerRuntimeServicesLayer } from "./serve
23import { ProviderHealthLive } from "./provider/Layers/ProviderHealth";23import { ProviderHealthLive } from "./provider/Layers/ProviderHealth";
24import { Server } from "./wsServer";24import { Server } from "./wsServer";
25import { ServerLoggerLive } from "./serverLogger";25import { ServerLoggerLive } from "./serverLogger";
26import { AnalyticsServiceLayerLive } from "./telemetry/Layers/AnalyticsService";
2627
27export class StartupError extends Data.TaggedError("StartupError")<{28export class StartupError extends Data.TaggedError("StartupError")<{
28 readonly message: string;29 readonly message: string;
...@@ -197,6 +198,7 @@ const LayerLive = (input: CliInput) =>...@@ -197,6 +198,7 @@ const LayerLive = (input: CliInput) =>
197 Layer.provideMerge(ProviderHealthLive),198 Layer.provideMerge(ProviderHealthLive),
198 Layer.provideMerge(SqlitePersistence.layerConfig),199 Layer.provideMerge(SqlitePersistence.layerConfig),
199 Layer.provideMerge(ServerLoggerLive),200 Layer.provideMerge(ServerLoggerLive),
201 Layer.provideMerge(AnalyticsServiceLayerLive),
200 Layer.provideMerge(ServerConfigLive(input)),202 Layer.provideMerge(ServerConfigLive(input)),
201 );203 );
202204
apps/server/src/telemetry/Identify.ts+3-1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1import { Effect, FileSystem, Path, Random, Schema } from "effect";1import { Effect, FileSystem, Path, Random, Schema } from "effect";
2import * as Crypto from "node:crypto";2import * as Crypto from "node:crypto";
3import { homedir } from "node:os";3import { homedir } from "node:os";
4import { ServerConfig } from "../config";
45
5const CodexAuthJsonSchema = Schema.Struct({6const CodexAuthJsonSchema = Schema.Struct({
6 tokens: Schema.Struct({7 tokens: Schema.Struct({
...@@ -39,8 +40,9 @@ const getCodexAccountId = Effect.gen(function* () {...@@ -39,8 +40,9 @@ const getCodexAccountId = Effect.gen(function* () {
39const upsertAnonymousId = Effect.gen(function* () {40const upsertAnonymousId = Effect.gen(function* () {
40 const fileSystem = yield* FileSystem.FileSystem;41 const fileSystem = yield* FileSystem.FileSystem;
41 const path = yield* Path.Path;42 const path = yield* Path.Path;
43 const serverConfig = yield* ServerConfig;
4244
43 const anonymousIdPath = path.join(homedir(), ".t3", "telemetry", "anonymous-id");45 const anonymousIdPath = path.join(serverConfig.stateDir, "anonymous-id");
44 const anonymousId = yield* fileSystem.readFileString(anonymousIdPath).pipe(46 const anonymousId = yield* fileSystem.readFileString(anonymousIdPath).pipe(
45 Effect.catch(() =>47 Effect.catch(() =>
46 Effect.gen(function* () {48 Effect.gen(function* () {