@knpkv/agent-usage 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +129 -20
  2. package/dist/client/assets/index-BWajfpNv.js +55 -0
  3. package/dist/client/assets/{index-CUuLXODk.css → index-B_xl-_rU.css} +1 -1
  4. package/dist/client/index.html +2 -2
  5. package/dist/core/ClaudeLimitSamples.d.ts +30 -0
  6. package/dist/core/ClaudeLimitSamples.js +69 -0
  7. package/dist/core/ClaudeLimitSamples.js.map +1 -0
  8. package/dist/core/ClaudeLimits.d.ts +38 -4
  9. package/dist/core/ClaudeLimits.js +66 -19
  10. package/dist/core/ClaudeLimits.js.map +1 -1
  11. package/dist/core/ClaudeLimitsLive.d.ts +33 -7
  12. package/dist/core/ClaudeLimitsLive.js +54 -10
  13. package/dist/core/ClaudeLimitsLive.js.map +1 -1
  14. package/dist/core/Database.d.ts +9 -1
  15. package/dist/core/Database.js +6 -2
  16. package/dist/core/Database.js.map +1 -1
  17. package/dist/core/Ingest.d.ts +7 -1
  18. package/dist/core/Ingest.js +36 -6
  19. package/dist/core/Ingest.js.map +1 -1
  20. package/dist/core/Model.d.ts +19 -7
  21. package/dist/core/Model.js +15 -3
  22. package/dist/core/Model.js.map +1 -1
  23. package/dist/core/Report.js +21 -7
  24. package/dist/core/Report.js.map +1 -1
  25. package/dist/core/Store.js +14 -12
  26. package/dist/core/Store.js.map +1 -1
  27. package/dist/main.js +28 -20
  28. package/dist/main.js.map +1 -1
  29. package/dist/server/Api.d.ts +98 -62
  30. package/dist/server/Config.d.ts +22 -3
  31. package/dist/server/Config.js +66 -4
  32. package/dist/server/Config.js.map +1 -1
  33. package/dist/server/ControlSocket.d.ts +73 -0
  34. package/dist/server/ControlSocket.js +270 -0
  35. package/dist/server/ControlSocket.js.map +1 -0
  36. package/dist/server/HttpApplication.d.ts +1 -1
  37. package/dist/server/HttpApplication.js +2 -1
  38. package/dist/server/HttpApplication.js.map +1 -1
  39. package/dist/server/IngestSummary.d.ts +8 -0
  40. package/dist/server/IngestSummary.js +32 -0
  41. package/dist/server/IngestSummary.js.map +1 -0
  42. package/dist/server/Live.d.ts +4 -0
  43. package/dist/server/Live.js +49 -0
  44. package/dist/server/Live.js.map +1 -0
  45. package/dist/server/Login.d.ts +34 -0
  46. package/dist/server/Login.js +74 -0
  47. package/dist/server/Login.js.map +1 -0
  48. package/dist/server/OwnerSession.d.ts +15 -8
  49. package/dist/server/OwnerSession.js +33 -25
  50. package/dist/server/OwnerSession.js.map +1 -1
  51. package/dist/server/Runtime.d.ts +3 -1
  52. package/dist/server/Runtime.js +32 -8
  53. package/dist/server/Runtime.js.map +1 -1
  54. package/dist/server/Server.d.ts +2 -2
  55. package/dist/server/Server.js +19 -6
  56. package/dist/server/Server.js.map +1 -1
  57. package/dist/shared/contracts.d.ts +51 -6
  58. package/dist/shared/contracts.js +11 -1
  59. package/dist/shared/contracts.js.map +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +2 -2
  62. package/dist/client/assets/index-DB-VXWqy.js +0 -55
package/dist/main.js CHANGED
@@ -4,23 +4,36 @@
4
4
  *
5
5
  * - `agent-usage serve` ingests every minute, polls Claude limits every five, and prints the URL
6
6
  * that gets you in.
7
+ * - `agent-usage login [--open]` asks the running server for a fresh one-time link and prints it.
7
8
  * - `agent-usage ingest [--json]` runs one ingest pass and reports what it found.
8
9
  *
9
10
  * @module
10
11
  */
11
12
  import { NodeRuntime, NodeServices } from "@effect/platform-node";
12
- import { Console, Deferred, Effect, Fiber, Layer, Option, Schema } from "effect";
13
+ import { ConfigProvider, Console, Deferred, Effect, Fiber, Layer, Option, Schema } from "effect";
13
14
  import { Command, Flag } from "effect/cli";
14
15
  import * as Stdio from "effect/Stdio";
15
16
  import * as Stream from "effect/Stream";
16
- import { hostname } from "node:os";
17
+ import { hostname, platform, userInfo } from "node:os";
17
18
  import { databaseLayer } from "./core/Database.js";
18
19
  import { ingestOnce } from "./core/Ingest.js";
19
20
  import { loadConfig } from "./server/Config.js";
20
- import { makeOwnerSessionSecrets, ownerSessionOrigin, ownerSessionUrl } from "./server/OwnerSession.js";
21
+ import { describeIngest } from "./server/IngestSummary.js";
22
+ import { login as requestLogin } from "./server/Login.js";
23
+ import { makeOwnerSessionSecrets, ownerSessionOrigin } from "./server/OwnerSession.js";
21
24
  import { makeServer, Port, PublicOrigin } from "./server/Server.js";
22
25
  import { IngestStatus as IngestStatusSchema } from "./shared/contracts.js";
23
- const config = loadConfig(hostname());
26
+ /** The operating system's name for the user; Claude Code's own fallback when it cannot ask. */
27
+ const osUserName = () => {
28
+ try {
29
+ return userInfo().username;
30
+ }
31
+ catch {
32
+ return "claude-code-user";
33
+ }
34
+ };
35
+ // Empty values kept, for the one variable where an empty value means something.
36
+ const config = loadConfig(hostname(), ConfigProvider.fromEnv({ preserveEmptyStrings: true }), osUserName());
24
37
  const serve = Command.make("serve", {}, Effect.fn(function* () {
25
38
  const stdio = yield* Stdio.Stdio;
26
39
  const settings = yield* config;
@@ -29,21 +42,12 @@ const serve = Command.make("serve", {}, Effect.fn(function* () {
29
42
  const security = yield* makeOwnerSessionSecrets(ownerSessionOrigin("127.0.0.1", port), Option.getOrUndefined(configuredOrigin));
30
43
  const ready = yield* Deferred.make();
31
44
  const server = yield* Layer.launch(makeServer({ config: settings, port, ready, security })).pipe(Effect.forkChild({ startImmediately: true }));
32
- yield* Effect.raceFirst(Deferred.await(ready), Fiber.join(server));
45
+ // The server only ends this race by failing; until it is listening there is no link to print.
46
+ const url = yield* Effect.raceFirst(Deferred.await(ready), Fiber.join(server).pipe(Effect.andThen(Effect.never)));
33
47
  // On stdout and nowhere else: this line is the credential, so it is never logged.
34
- yield* Stream.make(`agent usage: ${ownerSessionUrl(security.browserOrigin, security)}\n`).pipe(Stream.run(stdio.stdout()));
48
+ yield* Stream.make(`agent usage: ${url}\n`).pipe(Stream.run(stdio.stdout()));
35
49
  return yield* Fiber.join(server);
36
50
  })).pipe(Command.withDescription("Record usage continuously and serve the browser view"));
37
- const describeSource = (name, status) => {
38
- if (status.rootMissing)
39
- return `${name}: no sessions directory`;
40
- const skipped = status.skipped.unparseableLine + status.skipped.missingTimestamp + status.skipped.oversizedLine;
41
- return [
42
- `${name}: ${status.filesRead}/${status.filesScanned} files read, ${status.eventsAdded} events added`,
43
- skipped > 0 ? `, ${skipped} lines skipped` : "",
44
- status.unreadable.length > 0 ? `, ${status.unreadable.length} unreadable` : ""
45
- ].join("");
46
- };
47
51
  const encodeStatus = Schema.encodeSync(Schema.fromJsonString(IngestStatusSchema));
48
52
  const ingest = Command.make("ingest", {
49
53
  json: Flag.Boolean("json").pipe(Flag.withDescription("Print the status as one JSON value"), Flag.withDefault(false))
@@ -55,11 +59,15 @@ const ingest = Command.make("ingest", {
55
59
  Effect.provide(databaseLayer(settings.storeDirectory)));
56
60
  if (json)
57
61
  return yield* Console.log(encodeStatus(status));
58
- yield* Console.log(describeSource("claude", status.claude));
59
- yield* Console.log(describeSource("codex", status.codex));
60
- yield* Console.log(`took ${status.finishedAt - status.startedAt}ms`);
62
+ for (const line of describeIngest(status))
63
+ yield* Console.log(line);
61
64
  })).pipe(Command.withDescription("Run one ingest pass and report what it found"));
62
- const cli = Command.make("agent-usage").pipe(Command.withDescription("Claude and Codex subscription usage over time, per ticket and against limits"), Command.withSubcommands([serve, ingest]), Command.run({ version: "0.1.0" }));
65
+ const login = Command.make("login", {
66
+ open: Flag.Boolean("open").pipe(Flag.withDescription("Open the link in the browser too"), Flag.withDefault(false))
67
+ },
68
+ // The user id `login` trusts the socket to belong to; -1 (no POSIX ids) matches no owner.
69
+ ({ open }) => requestLogin(config, open, platform(), process.geteuid?.() ?? -1)).pipe(Command.withDescription("Print a fresh one-time link to the running server"));
70
+ const cli = Command.make("agent-usage").pipe(Command.withDescription("Claude and Codex subscription usage over time, per ticket and against limits"), Command.withSubcommands([serve, login, ingest]), Command.run({ version: "0.1.0" }));
63
71
  // Executable entry point: the host platform is provided once for this process.
64
72
  NodeRuntime.runMain(cli.pipe(Effect.provide(NodeServices.layer)));
65
73
  //# sourceMappingURL=main.js.map
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
@@ -30,14 +30,15 @@ declare const UsageApiGroup_base: HttpApiGroup.HttpApiGroup<"usage", HttpApiEndp
30
30
  readonly usedPercent: import("effect/Schema").Finite;
31
31
  readonly resetsAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
32
32
  }>, import("effect/Schema").TaggedStruct<"Unknown", {
33
- readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
33
+ readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "KeychainDenied", "NotSupported", "Fetch", "Parse", "NoData"]>;
34
+ readonly detail: import("effect/Schema").optionalKey<import("effect/Schema").String>;
34
35
  }>]>;
35
36
  }>>;
36
37
  }>>;
37
38
  readonly latest: import("effect/Schema").$Array<import("effect/Schema").Struct<{
38
39
  readonly agent: import("effect/Schema").Literals<readonly ["claude", "codex"]>;
39
40
  readonly machine: import("effect/Schema").NonEmptyString;
40
- readonly source: import("effect/Schema").Literals<readonly ["claude-oauth-usage", "codex-rollout"]>;
41
+ readonly source: import("effect/Schema").Literals<readonly ["claude-oauth-usage", "claude-statusline", "codex-rollout"]>;
41
42
  readonly label: import("effect/Schema").NonEmptyString;
42
43
  readonly windowMinutes: import("effect/Schema").NullOr<import("effect/Schema").Int>;
43
44
  readonly observedAt: import("effect/Schema").Int;
@@ -45,7 +46,8 @@ declare const UsageApiGroup_base: HttpApiGroup.HttpApiGroup<"usage", HttpApiEndp
45
46
  readonly usedPercent: import("effect/Schema").Finite;
46
47
  readonly resetsAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
47
48
  }>, import("effect/Schema").TaggedStruct<"Unknown", {
48
- readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
49
+ readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "KeychainDenied", "NotSupported", "Fetch", "Parse", "NoData"]>;
50
+ readonly detail: import("effect/Schema").optionalKey<import("effect/Schema").String>;
49
51
  }>]>;
50
52
  }>>;
51
53
  readonly balances: import("effect/Schema").$Array<import("effect/Schema").Struct<{
@@ -62,7 +64,8 @@ declare const UsageApiGroup_base: HttpApiGroup.HttpApiGroup<"usage", HttpApiEndp
62
64
  readonly credits: import("effect/Schema").Finite;
63
65
  }>, import("effect/Schema").TaggedStruct<"Unlimited", {}>, import("effect/Schema").TaggedStruct<"Exhausted", {}>, import("effect/Schema").TaggedStruct<"Disabled", {}>]>;
64
66
  }>, import("effect/Schema").TaggedStruct<"Unknown", {
65
- readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
67
+ readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "KeychainDenied", "NotSupported", "Fetch", "Parse", "NoData"]>;
68
+ readonly detail: import("effect/Schema").optionalKey<import("effect/Schema").String>;
66
69
  }>]>;
67
70
  }>>;
68
71
  }>>, import("effect/Schema").toCodecJson<typeof ApiError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"status", "GET", "/api/status", never, never, never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
@@ -100,6 +103,21 @@ declare const UsageApiGroup_base: HttpApiGroup.HttpApiGroup<"usage", HttpApiEndp
100
103
  readonly reason: import("effect/Schema").String;
101
104
  }>>;
102
105
  }>;
106
+ readonly claudeLimitSamples: import("effect/Schema").Struct<{
107
+ readonly rootMissing: import("effect/Schema").Boolean;
108
+ readonly filesScanned: import("effect/Schema").Int;
109
+ readonly filesRead: import("effect/Schema").Int;
110
+ readonly eventsAdded: import("effect/Schema").Int;
111
+ readonly skipped: import("effect/Schema").Struct<{
112
+ readonly unparseableLine: import("effect/Schema").Int;
113
+ readonly missingTimestamp: import("effect/Schema").Int;
114
+ readonly oversizedLine: import("effect/Schema").Int;
115
+ }>;
116
+ readonly unreadable: import("effect/Schema").$Array<import("effect/Schema").Struct<{
117
+ readonly fileKey: import("effect/Schema").String;
118
+ readonly reason: import("effect/Schema").String;
119
+ }>>;
120
+ }>;
103
121
  }>>;
104
122
  readonly ingestFailure: import("effect/Schema").NullOr<import("effect/Schema").String>;
105
123
  readonly limitsFailure: import("effect/Schema").NullOr<import("effect/Schema").String>;
@@ -159,59 +177,7 @@ declare const UsageApiGroup_base: HttpApiGroup.HttpApiGroup<"usage", HttpApiEndp
159
177
  }>>, import("effect/Schema").toCodecJson<typeof ApiError>, never, never>, false>;
160
178
  export declare class UsageApiGroup extends UsageApiGroup_base {
161
179
  }
162
- declare const AgentUsageApi_base: HttpApi.HttpApi<"AgentUsageApi", HttpApiGroup.HttpApiGroup<"usage", HttpApiEndpoint.HttpApiEndpoint<"usage", "GET", "/api/usage", never, import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
163
- readonly from: import("effect/Schema").compose<import("effect/Schema").Int, import("effect/Schema").NumberFromString>;
164
- readonly to: import("effect/Schema").compose<import("effect/Schema").Int, import("effect/Schema").NumberFromString>;
165
- readonly timeZone: import("effect/Schema").String;
166
- readonly bucket: import("effect/Schema").Literals<readonly ["hour", "day", "week"]>;
167
- readonly agent: import("effect/Schema").Literals<readonly ["all", "claude", "codex"]>;
168
- }>>, never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
169
- readonly periods: import("effect/Schema").$Array<import("effect/Schema").Struct<{
170
- readonly key: import("effect/Schema").String;
171
- readonly start: import("effect/Schema").Int;
172
- }>>;
173
- readonly bookings: import("effect/Schema").$Array<import("effect/Schema").Struct<{
174
- readonly id: import("effect/Schema").String;
175
- readonly booking: import("effect/Schema").Union<readonly [import("effect/Schema").TaggedStruct<"Ticket", {
176
- readonly key: import("effect/Schema").String;
177
- }>, import("effect/Schema").TaggedStruct<"Repo", {
178
- readonly name: import("effect/Schema").String;
179
- }>]>;
180
- readonly title: import("effect/Schema").NullOr<import("effect/Schema").Union<readonly [import("effect/Schema").TaggedStruct<"Known", {
181
- readonly summary: import("effect/Schema").String;
182
- }>, import("effect/Schema").TaggedStruct<"Unknown", {
183
- readonly reason: import("effect/Schema").Literals<readonly ["NotFound", "NotLookedUp"]>;
184
- }>]>>;
185
- readonly agents: import("effect/Schema").$Array<import("effect/Schema").Literals<readonly ["claude", "codex"]>>;
186
- readonly requests: import("effect/Schema").Int;
187
- readonly tokens: import("effect/Schema").Struct<{
188
- readonly input: import("effect/Schema").Int;
189
- readonly output: import("effect/Schema").Int;
190
- readonly reasoning: import("effect/Schema").Int;
191
- readonly cacheRead: import("effect/Schema").Int;
192
- readonly cacheWrite5m: import("effect/Schema").Int;
193
- readonly cacheWrite1h: import("effect/Schema").Int;
194
- }>;
195
- readonly costUsd: import("effect/Schema").Finite;
196
- readonly unpricedTokens: import("effect/Schema").Int;
197
- readonly unpricedModels: import("effect/Schema").$Array<import("effect/Schema").String>;
198
- }>>;
199
- readonly cells: import("effect/Schema").$Array<import("effect/Schema").Struct<{
200
- readonly period: import("effect/Schema").Int;
201
- readonly booking: import("effect/Schema").String;
202
- readonly tokens: import("effect/Schema").Int;
203
- readonly costUsd: import("effect/Schema").Finite;
204
- readonly unpricedTokens: import("effect/Schema").Int;
205
- }>>;
206
- readonly unpriced: import("effect/Schema").Struct<{
207
- readonly tokens: import("effect/Schema").Int;
208
- readonly models: import("effect/Schema").$Array<import("effect/Schema").String>;
209
- }>;
210
- readonly ignoredKeys: import("effect/Schema").$Array<import("effect/Schema").Struct<{
211
- readonly prefix: import("effect/Schema").String;
212
- readonly requests: import("effect/Schema").Int;
213
- }>>;
214
- }>>, import("effect/Schema").toCodecJson<typeof ApiError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"limits", "GET", "/api/limits", never, import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
180
+ declare const AgentUsageApi_base: HttpApi.HttpApi<"AgentUsageApi", HttpApiGroup.HttpApiGroup<"usage", HttpApiEndpoint.HttpApiEndpoint<"limits", "GET", "/api/limits", never, import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
215
181
  readonly from: import("effect/Schema").compose<import("effect/Schema").Int, import("effect/Schema").NumberFromString>;
216
182
  readonly to: import("effect/Schema").compose<import("effect/Schema").Int, import("effect/Schema").NumberFromString>;
217
183
  }>>, never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
@@ -225,14 +191,15 @@ declare const AgentUsageApi_base: HttpApi.HttpApi<"AgentUsageApi", HttpApiGroup.
225
191
  readonly usedPercent: import("effect/Schema").Finite;
226
192
  readonly resetsAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
227
193
  }>, import("effect/Schema").TaggedStruct<"Unknown", {
228
- readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
194
+ readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "KeychainDenied", "NotSupported", "Fetch", "Parse", "NoData"]>;
195
+ readonly detail: import("effect/Schema").optionalKey<import("effect/Schema").String>;
229
196
  }>]>;
230
197
  }>>;
231
198
  }>>;
232
199
  readonly latest: import("effect/Schema").$Array<import("effect/Schema").Struct<{
233
200
  readonly agent: import("effect/Schema").Literals<readonly ["claude", "codex"]>;
234
201
  readonly machine: import("effect/Schema").NonEmptyString;
235
- readonly source: import("effect/Schema").Literals<readonly ["claude-oauth-usage", "codex-rollout"]>;
202
+ readonly source: import("effect/Schema").Literals<readonly ["claude-oauth-usage", "claude-statusline", "codex-rollout"]>;
236
203
  readonly label: import("effect/Schema").NonEmptyString;
237
204
  readonly windowMinutes: import("effect/Schema").NullOr<import("effect/Schema").Int>;
238
205
  readonly observedAt: import("effect/Schema").Int;
@@ -240,7 +207,8 @@ declare const AgentUsageApi_base: HttpApi.HttpApi<"AgentUsageApi", HttpApiGroup.
240
207
  readonly usedPercent: import("effect/Schema").Finite;
241
208
  readonly resetsAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
242
209
  }>, import("effect/Schema").TaggedStruct<"Unknown", {
243
- readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
210
+ readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "KeychainDenied", "NotSupported", "Fetch", "Parse", "NoData"]>;
211
+ readonly detail: import("effect/Schema").optionalKey<import("effect/Schema").String>;
244
212
  }>]>;
245
213
  }>>;
246
214
  readonly balances: import("effect/Schema").$Array<import("effect/Schema").Struct<{
@@ -257,7 +225,8 @@ declare const AgentUsageApi_base: HttpApi.HttpApi<"AgentUsageApi", HttpApiGroup.
257
225
  readonly credits: import("effect/Schema").Finite;
258
226
  }>, import("effect/Schema").TaggedStruct<"Unlimited", {}>, import("effect/Schema").TaggedStruct<"Exhausted", {}>, import("effect/Schema").TaggedStruct<"Disabled", {}>]>;
259
227
  }>, import("effect/Schema").TaggedStruct<"Unknown", {
260
- readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
228
+ readonly reason: import("effect/Schema").Literals<readonly ["NoAuth", "AuthExpired", "KeychainDenied", "NotSupported", "Fetch", "Parse", "NoData"]>;
229
+ readonly detail: import("effect/Schema").optionalKey<import("effect/Schema").String>;
261
230
  }>]>;
262
231
  }>>;
263
232
  }>>, import("effect/Schema").toCodecJson<typeof ApiError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"status", "GET", "/api/status", never, never, never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
@@ -295,10 +264,77 @@ declare const AgentUsageApi_base: HttpApi.HttpApi<"AgentUsageApi", HttpApiGroup.
295
264
  readonly reason: import("effect/Schema").String;
296
265
  }>>;
297
266
  }>;
267
+ readonly claudeLimitSamples: import("effect/Schema").Struct<{
268
+ readonly rootMissing: import("effect/Schema").Boolean;
269
+ readonly filesScanned: import("effect/Schema").Int;
270
+ readonly filesRead: import("effect/Schema").Int;
271
+ readonly eventsAdded: import("effect/Schema").Int;
272
+ readonly skipped: import("effect/Schema").Struct<{
273
+ readonly unparseableLine: import("effect/Schema").Int;
274
+ readonly missingTimestamp: import("effect/Schema").Int;
275
+ readonly oversizedLine: import("effect/Schema").Int;
276
+ }>;
277
+ readonly unreadable: import("effect/Schema").$Array<import("effect/Schema").Struct<{
278
+ readonly fileKey: import("effect/Schema").String;
279
+ readonly reason: import("effect/Schema").String;
280
+ }>>;
281
+ }>;
298
282
  }>>;
299
283
  readonly ingestFailure: import("effect/Schema").NullOr<import("effect/Schema").String>;
300
284
  readonly limitsFailure: import("effect/Schema").NullOr<import("effect/Schema").String>;
301
285
  readonly ticketLookupFailures: import("effect/Schema").$Array<import("effect/Schema").String>;
302
- }>>, never, OwnerSessionAuth, never>, false>>;
286
+ }>>, never, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"usage", "GET", "/api/usage", never, import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
287
+ readonly from: import("effect/Schema").compose<import("effect/Schema").Int, import("effect/Schema").NumberFromString>;
288
+ readonly to: import("effect/Schema").compose<import("effect/Schema").Int, import("effect/Schema").NumberFromString>;
289
+ readonly timeZone: import("effect/Schema").String;
290
+ readonly bucket: import("effect/Schema").Literals<readonly ["hour", "day", "week"]>;
291
+ readonly agent: import("effect/Schema").Literals<readonly ["all", "claude", "codex"]>;
292
+ }>>, never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
293
+ readonly periods: import("effect/Schema").$Array<import("effect/Schema").Struct<{
294
+ readonly key: import("effect/Schema").String;
295
+ readonly start: import("effect/Schema").Int;
296
+ }>>;
297
+ readonly bookings: import("effect/Schema").$Array<import("effect/Schema").Struct<{
298
+ readonly id: import("effect/Schema").String;
299
+ readonly booking: import("effect/Schema").Union<readonly [import("effect/Schema").TaggedStruct<"Ticket", {
300
+ readonly key: import("effect/Schema").String;
301
+ }>, import("effect/Schema").TaggedStruct<"Repo", {
302
+ readonly name: import("effect/Schema").String;
303
+ }>]>;
304
+ readonly title: import("effect/Schema").NullOr<import("effect/Schema").Union<readonly [import("effect/Schema").TaggedStruct<"Known", {
305
+ readonly summary: import("effect/Schema").String;
306
+ }>, import("effect/Schema").TaggedStruct<"Unknown", {
307
+ readonly reason: import("effect/Schema").Literals<readonly ["NotFound", "NotLookedUp"]>;
308
+ }>]>>;
309
+ readonly agents: import("effect/Schema").$Array<import("effect/Schema").Literals<readonly ["claude", "codex"]>>;
310
+ readonly requests: import("effect/Schema").Int;
311
+ readonly tokens: import("effect/Schema").Struct<{
312
+ readonly input: import("effect/Schema").Int;
313
+ readonly output: import("effect/Schema").Int;
314
+ readonly reasoning: import("effect/Schema").Int;
315
+ readonly cacheRead: import("effect/Schema").Int;
316
+ readonly cacheWrite5m: import("effect/Schema").Int;
317
+ readonly cacheWrite1h: import("effect/Schema").Int;
318
+ }>;
319
+ readonly costUsd: import("effect/Schema").Finite;
320
+ readonly unpricedTokens: import("effect/Schema").Int;
321
+ readonly unpricedModels: import("effect/Schema").$Array<import("effect/Schema").String>;
322
+ }>>;
323
+ readonly cells: import("effect/Schema").$Array<import("effect/Schema").Struct<{
324
+ readonly period: import("effect/Schema").Int;
325
+ readonly booking: import("effect/Schema").String;
326
+ readonly tokens: import("effect/Schema").Int;
327
+ readonly costUsd: import("effect/Schema").Finite;
328
+ readonly unpricedTokens: import("effect/Schema").Int;
329
+ }>>;
330
+ readonly unpriced: import("effect/Schema").Struct<{
331
+ readonly tokens: import("effect/Schema").Int;
332
+ readonly models: import("effect/Schema").$Array<import("effect/Schema").String>;
333
+ }>;
334
+ readonly ignoredKeys: import("effect/Schema").$Array<import("effect/Schema").Struct<{
335
+ readonly prefix: import("effect/Schema").String;
336
+ readonly requests: import("effect/Schema").Int;
337
+ }>>;
338
+ }>>, import("effect/Schema").toCodecJson<typeof ApiError>, OwnerSessionAuth, never>, false>>;
303
339
  export declare class AgentUsageApi extends AgentUsageApi_base {
304
340
  }
@@ -4,18 +4,32 @@
4
4
  * - `AGENT_USAGE_HOME` — the store directory (default `~/.local/share/agent-usage`)
5
5
  * - `AGENT_USAGE_MACHINE` — the Machine name (default: the short, lower-cased hostname)
6
6
  * - `CLAUDE_CONFIG_DIR` / `CODEX_HOME` — the agents' own roots (default `~/.claude`, `~/.codex`)
7
+ * - `CLAUDE_SECURESTORAGE_CONFIG_DIR` — where Claude Code keeps its credentials when it differs from
8
+ * the config directory; with `CLAUDE_CONFIG_DIR` it decides the macOS Keychain item's name
9
+ * - `AGENT_USAGE_CLAUDE_LIMITS` — the Claude limit samples claude-statusline appends (default
10
+ * `${XDG_STATE_HOME:-~/.local/state}/agent-usage/claude-limits.jsonl`)
7
11
  * - `AGENT_USAGE_PROJECTS` — extra Known Projects, comma-separated (`RPS,ABC`), for projects typed in
8
12
  * sessions before any branch named them
9
13
  *
10
14
  * @module
11
15
  */
12
- import { Config, Effect, Path } from "effect";
16
+ import { Config, ConfigProvider, Crypto, Effect, Path } from "effect";
13
17
  import type { SourceRoots } from "../core/Ingest.js";
18
+ /** Where Claude Code keeps the OAuth credentials a limit poll reads. */
19
+ export interface ClaudeCredentialsLocation {
20
+ /** Linux: `<secure storage dir>/.credentials.json`. */
21
+ readonly file: string;
22
+ /** macOS: the login Keychain item's service name… */
23
+ readonly keychainService: string;
24
+ /** …and its account, the user name. */
25
+ readonly keychainAccount: string;
26
+ }
14
27
  export interface AgentUsageConfig {
15
28
  readonly storeDirectory: string;
16
29
  /** Known Projects from configuration, added to those branches and paths name. */
17
30
  readonly projects: ReadonlyArray<string>;
18
31
  readonly claudeConfigDir: string;
32
+ readonly claudeCredentials: ClaudeCredentialsLocation;
19
33
  readonly roots: SourceRoots;
20
34
  }
21
35
  /**
@@ -23,5 +37,10 @@ export interface AgentUsageConfig {
23
37
  * `Example-MacBook.local` and `Example-MacBook` name the same Machine.
24
38
  */
25
39
  export declare const machineName: (hostname: string) => string;
26
- /** Reads the configuration; `hostname` comes from the executable, the only place that may ask. */
27
- export declare const loadConfig: (hostname: string) => Effect.Effect<AgentUsageConfig, Config.ConfigError, Path.Path>;
40
+ /**
41
+ * Reads the configuration; `hostname` comes from the executable, the only place that may ask.
42
+ * `exactEnvironment` is the environment with empty values kept, which the executable builds; it
43
+ * is read only where an empty value means something (`CLAUDE_SECURESTORAGE_CONFIG_DIR`).
44
+ * `osUser` is the operating system's name for the user, the Keychain account when `USER` is unset.
45
+ */
46
+ export declare const loadConfig: (hostname: string, exactEnvironment?: ConfigProvider.ConfigProvider, osUser?: string) => Effect.Effect<AgentUsageConfig, Config.ConfigError | import("effect/PlatformError").PlatformError, Crypto.Crypto | Path.Path>;
@@ -4,12 +4,49 @@
4
4
  * - `AGENT_USAGE_HOME` — the store directory (default `~/.local/share/agent-usage`)
5
5
  * - `AGENT_USAGE_MACHINE` — the Machine name (default: the short, lower-cased hostname)
6
6
  * - `CLAUDE_CONFIG_DIR` / `CODEX_HOME` — the agents' own roots (default `~/.claude`, `~/.codex`)
7
+ * - `CLAUDE_SECURESTORAGE_CONFIG_DIR` — where Claude Code keeps its credentials when it differs from
8
+ * the config directory; with `CLAUDE_CONFIG_DIR` it decides the macOS Keychain item's name
9
+ * - `AGENT_USAGE_CLAUDE_LIMITS` — the Claude limit samples claude-statusline appends (default
10
+ * `${XDG_STATE_HOME:-~/.local/state}/agent-usage/claude-limits.jsonl`)
7
11
  * - `AGENT_USAGE_PROJECTS` — extra Known Projects, comma-separated (`RPS,ABC`), for projects typed in
8
12
  * sessions before any branch named them
9
13
  *
10
14
  * @module
11
15
  */
12
- import { Config, Effect, Path, Schema, SchemaTransformation } from "effect";
16
+ import { Config, ConfigProvider, Crypto, Effect, Path, Schema, SchemaTransformation } from "effect";
17
+ import { Hex } from "effect/encoding";
18
+ const KEYCHAIN_SERVICE = "Claude Code-credentials";
19
+ /**
20
+ * Where Claude Code (2.1.289) keeps its credentials, named the way it names them. The secure-storage
21
+ * directory is `CLAUDE_SECURESTORAGE_CONFIG_DIR` when set (empty meaning the default `~/.claude`),
22
+ * else the config directory. The Keychain item is `Claude Code-credentials`, suffixed with the first
23
+ * eight hex digits of the directory's SHA-256 whenever a non-default directory was chosen, under the
24
+ * user's account. Asking for the bare name with a custom directory reads another account's item.
25
+ *
26
+ * An explicitly empty `CLAUDE_SECURESTORAGE_CONFIG_DIR` means the default directory and the bare
27
+ * item name, as in Claude Code, so it is read from an environment that keeps empty values.
28
+ */
29
+ const claudeCredentialsLocation = (options) => Effect.gen(function* () {
30
+ const path = yield* Path.Path;
31
+ const digests = yield* Crypto.Crypto;
32
+ const unsuffixed = options.secureStorageDir === undefined
33
+ ? !options.configDirChosen
34
+ : options.secureStorageDir === "";
35
+ const directory = (options.secureStorageDir === undefined
36
+ ? options.claudeConfigDir
37
+ : options.secureStorageDir === ""
38
+ ? path.join(options.home, ".claude")
39
+ : options.secureStorageDir).normalize("NFC");
40
+ const suffix = unsuffixed
41
+ ? ""
42
+ : `-${Hex.encode(yield* digests.digest("SHA-256", new TextEncoder().encode(directory))).slice(0, 8)}`;
43
+ const location = {
44
+ file: path.join(directory, ".credentials.json"),
45
+ keychainService: `${KEYCHAIN_SERVICE}${suffix}`,
46
+ keychainAccount: options.user
47
+ };
48
+ return location;
49
+ });
13
50
  /**
14
51
  * The Machine name a hostname gives: the part before the first dot, lower-cased, so macOS's
15
52
  * `Example-MacBook.local` and `Example-MacBook` name the same Machine.
@@ -25,22 +62,47 @@ const Projects = Schema.String.pipe(Schema.decodeTo(Schema.Array(ProjectKey), Sc
25
62
  decode: (raw) => raw.split(",").map((entry) => entry.trim()).filter((entry) => entry !== ""),
26
63
  encode: (keys) => keys.join(",")
27
64
  })));
28
- /** Reads the configuration; `hostname` comes from the executable, the only place that may ask. */
29
- export const loadConfig = (hostname) => Effect.gen(function* () {
65
+ /**
66
+ * Reads the configuration; `hostname` comes from the executable, the only place that may ask.
67
+ * `exactEnvironment` is the environment with empty values kept, which the executable builds; it
68
+ * is read only where an empty value means something (`CLAUDE_SECURESTORAGE_CONFIG_DIR`).
69
+ * `osUser` is the operating system's name for the user, the Keychain account when `USER` is unset.
70
+ */
71
+ export const loadConfig = (hostname, exactEnvironment, osUser = "claude-code-user") => Effect.gen(function* () {
30
72
  const path = yield* Path.Path;
31
73
  const home = yield* Config.String("HOME");
32
74
  const storeDirectory = yield* Config.String("AGENT_USAGE_HOME").pipe(Config.withDefault(path.join(home, ".local", "share", "agent-usage")));
33
75
  const machine = yield* Config.NonEmptyString("AGENT_USAGE_MACHINE").pipe(Config.withDefault(machineName(hostname)));
34
- const claudeConfigDir = yield* Config.String("CLAUDE_CONFIG_DIR").pipe(Config.withDefault(path.join(home, ".claude")));
76
+ const chosenConfigDir = yield* Config.option(Config.String("CLAUDE_CONFIG_DIR"));
77
+ const claudeConfigDir = chosenConfigDir._tag === "Some" ? chosenConfigDir.value : path.join(home, ".claude");
78
+ const secureStorageRead = Config.option(Config.String("CLAUDE_SECURESTORAGE_CONFIG_DIR"));
79
+ const secureStorageDir = yield* (exactEnvironment === undefined
80
+ ? secureStorageRead
81
+ : secureStorageRead.pipe(Effect.provideService(ConfigProvider.ConfigProvider, exactEnvironment)));
82
+ // The Keychain account, as Claude Code names it: USER, else the OS user, kept only when it is a
83
+ // plain name.
84
+ const named = yield* Config.String("USER").pipe(Config.withDefault(osUser));
85
+ const user = /^[a-zA-Z0-9._-]+$/u.test(named) ? named : "claude-code-user";
86
+ const claudeCredentials = yield* claudeCredentialsLocation({
87
+ home,
88
+ user,
89
+ claudeConfigDir,
90
+ configDirChosen: chosenConfigDir._tag === "Some",
91
+ secureStorageDir: secureStorageDir._tag === "Some" ? secureStorageDir.value : undefined
92
+ });
93
+ const stateHome = yield* Config.String("XDG_STATE_HOME").pipe(Config.withDefault(path.join(home, ".local", "state")));
94
+ const claudeLimitSamples = yield* Config.String("AGENT_USAGE_CLAUDE_LIMITS").pipe(Config.withDefault(path.join(stateHome, "agent-usage", "claude-limits.jsonl")));
35
95
  const codexHome = yield* Config.String("CODEX_HOME").pipe(Config.withDefault(path.join(home, ".codex")));
36
96
  const projects = yield* Config.schema(Projects, "AGENT_USAGE_PROJECTS").pipe(Config.withDefault([]));
37
97
  const config = {
38
98
  storeDirectory,
39
99
  projects,
40
100
  claudeConfigDir,
101
+ claudeCredentials,
41
102
  roots: {
42
103
  claudeProjects: path.join(claudeConfigDir, "projects"),
43
104
  codexHome,
105
+ claudeLimitSamples,
44
106
  machine
45
107
  }
46
108
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Config.js","sourceRoot":"","sources":["../../src/server/Config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Config.js","sourceRoot":"","sources":["../../src/server/Config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { Duration, Effect, FileSystem, Path, Schema } from "effect";
2
+ import { type OwnerSessionSecretsContract } from "./OwnerSession.js";
3
+ export declare const SOCKET_FILE = "serve.sock";
4
+ export declare const LOCK_FILE = "serve.lock";
5
+ /**
6
+ * The longest socket path, in bytes, that binds everywhere this runs: macOS holds 104 bytes
7
+ * including the terminating NUL, Linux 108.
8
+ */
9
+ export declare const SOCKET_PATH_LIMIT = 103;
10
+ /** How long either side waits for the other before giving up on one exchange. */
11
+ export declare const EXCHANGE_DEADLINE: Duration.Duration;
12
+ declare const ServerNotRunning_base: Schema.Class<ServerNotRunning, Schema.TaggedStruct<"ServerNotRunning", {
13
+ readonly path: Schema.String;
14
+ }>, import("effect/Cause").YieldableError>;
15
+ /** No server is listening on this store: start `agent-usage serve`. */
16
+ export declare class ServerNotRunning extends ServerNotRunning_base {
17
+ get message(): string;
18
+ }
19
+ declare const ServerAlreadyRunning_base: Schema.Class<ServerAlreadyRunning, Schema.TaggedStruct<"ServerAlreadyRunning", {
20
+ readonly path: Schema.String;
21
+ }>, import("effect/Cause").YieldableError>;
22
+ /** A server already runs on this store; a second one would fight it for the store. */
23
+ export declare class ServerAlreadyRunning extends ServerAlreadyRunning_base {
24
+ get message(): string;
25
+ }
26
+ declare const SocketPathUnsafe_base: Schema.Class<SocketPathUnsafe, Schema.TaggedStruct<"SocketPathUnsafe", {
27
+ readonly path: Schema.String;
28
+ readonly reason: Schema.String;
29
+ }>, import("effect/Cause").YieldableError>;
30
+ /** Something at the socket path is not this user's socket: a symlink, a plain file, a foreign owner. */
31
+ export declare class SocketPathUnsafe extends SocketPathUnsafe_base {
32
+ get message(): string;
33
+ }
34
+ declare const SocketRefused_base: Schema.Class<SocketRefused, Schema.TaggedStruct<"SocketRefused", {
35
+ readonly path: Schema.String;
36
+ readonly reason: Schema.String;
37
+ }>, import("effect/Cause").YieldableError>;
38
+ /** The socket or lock is there but could not be used: no permission, no answer in time, or another failure. */
39
+ export declare class SocketRefused extends SocketRefused_base {
40
+ get message(): string;
41
+ }
42
+ declare const SocketPathTooLong_base: Schema.Class<SocketPathTooLong, Schema.TaggedStruct<"SocketPathTooLong", {
43
+ readonly path: Schema.String;
44
+ readonly bytes: Schema.Number;
45
+ }>, import("effect/Cause").YieldableError>;
46
+ /** The store directory is too deep for a Unix socket path, so this store has no `login`. */
47
+ export declare class SocketPathTooLong extends SocketPathTooLong_base {
48
+ get message(): string;
49
+ }
50
+ declare const LoginReplyInvalid_base: Schema.Class<LoginReplyInvalid, Schema.TaggedStruct<"LoginReplyInvalid", {
51
+ readonly reply: Schema.String;
52
+ }>, import("effect/Cause").YieldableError>;
53
+ /** The server answered with something that is not a link. */
54
+ export declare class LoginReplyInvalid extends LoginReplyInvalid_base {
55
+ }
56
+ /**
57
+ * Holds the store's lock and listens on `<directory>/serve.sock` for the life of the scope; returns
58
+ * the socket path, or nothing (and logs why) when the path is too long for a Unix socket. Requests
59
+ * wait for `listening` before a link is minted. Fails with {@link ServerAlreadyRunning} when another
60
+ * server runs on this store and {@link SocketPathUnsafe} when the path holds anything but this
61
+ * user's socket.
62
+ */
63
+ export declare const controlSocket: (directory: string, secrets: OwnerSessionSecretsContract, listening: Effect.Effect<void, never, never>) => Effect.Effect<string | undefined, ServerAlreadyRunning | SocketPathUnsafe | SocketRefused | import("../core/Store.js").StoreError, import("effect/Crypto").Crypto | FileSystem.FileSystem | Path.Path | import("effect/reactivity/Reactivity").Reactivity | import("effect/Scope").Scope>;
64
+ /**
65
+ * Asks the server running on this store for a fresh one-time link, trusting only a socket owned by
66
+ * `self`, the user id this process runs as. Fails with
67
+ * {@link ServerNotRunning} when nothing listens, {@link SocketPathTooLong} when the store has no
68
+ * control socket, {@link SocketPathUnsafe} when the path is not this user's socket,
69
+ * {@link SocketRefused} when the socket may not be used or does not answer within
70
+ * {@link EXCHANGE_DEADLINE}, and {@link LoginReplyInvalid} when the answer is not a link.
71
+ */
72
+ export declare const requestLoginUrl: (directory: string, self: number) => Effect.Effect<string, LoginReplyInvalid | ServerNotRunning | SocketPathTooLong | SocketPathUnsafe | SocketRefused, FileSystem.FileSystem | Path.Path>;
73
+ export {};