1/**
2 * CursorAccountReader - reads one range of Cursor account usage from its
3 * dashboard API. A service so tests can stand in for Cursor's API.
4 *
5 * @module provider-cursor/server/CursorAccountReader
6 */
7import * as Context from "effect/Context";
8import * as Crypto from "effect/Crypto";
9import * as Effect from "effect/Effect";
10import * as FileSystem from "effect/FileSystem";
11import * as HttpClient from "effect/http/HttpClient";
12import * as Layer from "effect/Layer";
13
14import type { CursorCredentialSource } from "./accountCache.ts";
15import { readCursorAccountUsage, type CursorAccountUsageReadResult } from "./accountUsage.ts";
16import * as CursorKeychain from "./CursorKeychain.ts";
17
18export class CursorAccountReader extends Context.Service<
19 CursorAccountReader,
20 {
21 readonly read: (
22 credentialSource: CursorCredentialSource,
23 sinceMs: number,
24 untilMs: number,
25 ) => Effect.Effect<CursorAccountUsageReadResult>;
26 }
27>()("@t3tools/provider-cursor/server/CursorAccountReader") {}
28
29/** Reads Cursor's dashboard API with the saved CLI or Keychain login. */
30export const layer = Layer.effect(
31 CursorAccountReader,
32 Effect.gen(function* () {
33 const keychain = yield* CursorKeychain.CursorKeychain;
34 const context = yield* Effect.context<
35 FileSystem.FileSystem | Crypto.Crypto | HttpClient.HttpClient
36 >();
37 return CursorAccountReader.of({
38 // Only a Keychain login asks the Keychain. The dashboard reader turns
39 // its outcome, a failure included, into the source's message.
40 read: (credentialSource, sinceMs, untilMs) =>
41 readCursorAccountUsage(credentialSource, sinceMs, untilMs, keychain.accessToken).pipe(
42 Effect.provideContext(context),
43 ),
44 });
45 }),
46);