@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
@@ -0,0 +1,270 @@
1
+ /**
2
+ * The control socket `agent-usage login` asks the running server for a fresh one-time link over.
3
+ *
4
+ * **Mental model**
5
+ *
6
+ * - **The store directory is the door.** The socket is `serve.sock` in the store directory, which
7
+ * the database layer keeps owner-only (`0700`); the socket itself is `0600`. Reaching it already
8
+ * proves the caller is the owner, so the request needs no credential of its own.
9
+ * - **Nothing at the path is trusted.** A symlink, a file that is not a socket, or a socket another
10
+ * user owns is refused, by the server before binding and by `login` before connecting.
11
+ * - **One server per store, held by a lock the kernel keeps.** Before anything else the server
12
+ * takes an exclusive SQLite lock on `serve.lock` in the store directory and keeps it for its life;
13
+ * the operating system drops it when the process ends, however it ends. A second server finds it
14
+ * taken and refuses to start. Holding the lock, the server knows any socket at the path is one a
15
+ * dead server left behind, so it removes it and binds; no probing, no race between servers.
16
+ * - **Short paths only.** A Unix socket path is limited to about a hundred bytes. A store directory
17
+ * too deep for one still runs and still holds the lock, without `login`, and says so.
18
+ * - **One question, one answer.** The client sends `mint`; once the HTTP listener is up, the server
19
+ * mints a link through the same path as the startup link (one use, one minute) and replies with it
20
+ * as one JSON line. Both sides give up after {@link EXCHANGE_DEADLINE}.
21
+ *
22
+ * @module
23
+ */
24
+ import { NodeSocket, NodeSocketServer } from "@effect/platform-node";
25
+ import { SqliteClient } from "@effect/sql-sqlite-node";
26
+ import { PairingCode } from "@knpkv/browser-pairing/schema";
27
+ import { Duration, Effect, FileSystem, Option, Path, Predicate, Schema } from "effect";
28
+ import { prepareStoreDirectory } from "../core/Database.js";
29
+ import { isLoopbackHostname, mintBootstrapUrl } from "./OwnerSession.js";
30
+ export const SOCKET_FILE = "serve.sock";
31
+ export const LOCK_FILE = "serve.lock";
32
+ /**
33
+ * The longest socket path, in bytes, that binds everywhere this runs: macOS holds 104 bytes
34
+ * including the terminating NUL, Linux 108.
35
+ */
36
+ export const SOCKET_PATH_LIMIT = 103;
37
+ /** How long either side waits for the other before giving up on one exchange. */
38
+ export const EXCHANGE_DEADLINE = Duration.seconds(5);
39
+ /** No server is listening on this store: start `agent-usage serve`. */
40
+ export class ServerNotRunning extends Schema.TaggedError()("ServerNotRunning", {
41
+ path: Schema.String
42
+ }) {
43
+ get message() {
44
+ return `no agent-usage server is listening at ${this.path}`;
45
+ }
46
+ }
47
+ /** A server already runs on this store; a second one would fight it for the store. */
48
+ export class ServerAlreadyRunning extends Schema.TaggedError()("ServerAlreadyRunning", {
49
+ path: Schema.String
50
+ }) {
51
+ get message() {
52
+ return `agent-usage is already running on this store (${this.path}); use \`agent-usage login\` to get in`;
53
+ }
54
+ }
55
+ /** Something at the socket path is not this user's socket: a symlink, a plain file, a foreign owner. */
56
+ export class SocketPathUnsafe extends Schema.TaggedError()("SocketPathUnsafe", {
57
+ path: Schema.String,
58
+ reason: Schema.String
59
+ }) {
60
+ get message() {
61
+ return `refusing the control socket at ${this.path}: ${this.reason}`;
62
+ }
63
+ }
64
+ /** The socket or lock is there but could not be used: no permission, no answer in time, or another failure. */
65
+ export class SocketRefused extends Schema.TaggedError()("SocketRefused", {
66
+ path: Schema.String,
67
+ reason: Schema.String
68
+ }) {
69
+ get message() {
70
+ return `the control socket at ${this.path} could not be used (${this.reason})`;
71
+ }
72
+ }
73
+ /** The store directory is too deep for a Unix socket path, so this store has no `login`. */
74
+ export class SocketPathTooLong extends Schema.TaggedError()("SocketPathTooLong", {
75
+ path: Schema.String,
76
+ bytes: Schema.Number
77
+ }) {
78
+ get message() {
79
+ return `the control socket path ${this.path} is ${this.bytes} bytes, over the ${SOCKET_PATH_LIMIT}-byte limit for a Unix socket`;
80
+ }
81
+ }
82
+ /** The server answered with something that is not a link. */
83
+ export class LoginReplyInvalid extends Schema.TaggedError()("LoginReplyInvalid", {
84
+ reply: Schema.String
85
+ }) {
86
+ }
87
+ const isPairingCode = Schema.is(PairingCode);
88
+ /**
89
+ * A sign-in link as this server mints them: plain HTTP, a loopback host, and in the fragment a code
90
+ * of the shape the page will accept.
91
+ */
92
+ const isLoginUrl = (value) => {
93
+ if (!URL.canParse(value))
94
+ return false;
95
+ const url = new URL(value);
96
+ const prefix = "#bootstrap_token=";
97
+ return url.protocol === "http:" && isLoopbackHostname(url.hostname) && url.pathname === "/" &&
98
+ url.search === "" && url.hash.startsWith(prefix) && isPairingCode(url.hash.slice(prefix.length));
99
+ };
100
+ const LoginUrl = Schema.String.check(Schema.makeFilter(isLoginUrl, { expected: "a loopback link carrying a one-time code" }));
101
+ const LoginReply = Schema.fromJsonString(Schema.Struct({ url: LoginUrl }));
102
+ const encodeReply = Schema.encodeSync(LoginReply);
103
+ const decodeReply = Schema.decodeUnknownOption(LoginReply);
104
+ /** A Node system error, as far as this module reads one. */
105
+ const Errno = Schema.Struct({ code: Schema.String });
106
+ const decodeErrno = Schema.decodeUnknownOption(Errno);
107
+ /** The system error code (`ECONNREFUSED`, `EACCES`, `EEXIST`) behind a filesystem or socket failure. */
108
+ const errnoOf = (failure) => {
109
+ const cause = failure._tag === "PlatformError" ? failure.cause : failure.reason.cause;
110
+ return Option.getOrUndefined(Option.map(decodeErrno(cause), (errno) => errno.code));
111
+ };
112
+ /** `readlink` on something that is not a link fails with EINVAL; nothing else means "not a link". */
113
+ const isNotALink = (error) => errnoOf(error) === "EINVAL";
114
+ /** The socket path for a store, refused when it is too long to bind. */
115
+ const socketPathFor = Effect.fnUntraced(function* (directory) {
116
+ const path = yield* Path.Path;
117
+ const socketPath = path.join(directory, SOCKET_FILE);
118
+ const bytes = new TextEncoder().encode(socketPath).byteLength;
119
+ if (bytes > SOCKET_PATH_LIMIT)
120
+ return yield* new SocketPathTooLong({ path: socketPath, bytes });
121
+ return socketPath;
122
+ });
123
+ /**
124
+ * What is at `socketPath`: nothing, or a socket owned by `self` (this process's user id) in a store
125
+ * directory `self` also owns. Anything else is refused: another account could have bound it.
126
+ */
127
+ const inspect = Effect.fnUntraced(function* (directory, socketPath, self) {
128
+ const fs = yield* FileSystem.FileSystem;
129
+ const unsafe = (reason) => new SocketPathUnsafe({ path: socketPath, reason });
130
+ const link = yield* Effect.result(fs.readLink(socketPath));
131
+ if (link._tag === "Success")
132
+ return yield* unsafe("it is a symbolic link");
133
+ if (link.failure.reason._tag === "NotFound")
134
+ return undefined;
135
+ if (!isNotALink(link.failure))
136
+ return yield* unsafe(`it could not be inspected (${link.failure.reason._tag})`);
137
+ const [info, store] = yield* Effect.all([fs.stat(socketPath), fs.stat(directory)]).pipe(Effect.mapError((error) => unsafe(`it could not be inspected (${error.reason._tag})`)));
138
+ if (Option.getOrUndefined(store.uid) !== self)
139
+ return yield* unsafe("another user owns the store directory");
140
+ if (info.type !== "Socket")
141
+ return yield* unsafe(`it is a ${info.type}, not a socket`);
142
+ if (Option.getOrUndefined(info.uid) !== self)
143
+ return yield* unsafe("another user owns it");
144
+ return { inode: Option.getOrUndefined(info.ino) };
145
+ });
146
+ /**
147
+ * Reads from an open connection until a full line arrives or the peer closes it. The reader must be
148
+ * acquired before anything is written: acquiring it is what opens the connection.
149
+ */
150
+ const readLine = (reader) => Effect.gen(function* () {
151
+ const decoder = new TextDecoder();
152
+ let text = "";
153
+ while (!text.includes("\n")) {
154
+ const batch = yield* Effect.result(reader.pull);
155
+ if (batch._tag === "Failure") {
156
+ if (batch.failure.reason._tag === "SocketCloseError")
157
+ return text;
158
+ return yield* batch.failure;
159
+ }
160
+ for (const chunk of batch.success)
161
+ text += Predicate.isString(chunk) ? chunk : decoder.decode(chunk);
162
+ }
163
+ return text;
164
+ });
165
+ /** One connection: a `mint` request answered with a fresh link, anything else with an error. */
166
+ const answer = (secrets, listening) => (socket) => Effect.scoped(Effect.gen(function* () {
167
+ const reader = yield* socket.reader;
168
+ const write = yield* socket.writer;
169
+ const request = yield* readLine(reader);
170
+ if (request.trim() !== "mint")
171
+ return yield* write.write("{\"error\":\"unknown request\"}\n");
172
+ // Never before the HTTP listener is up: a code minted earlier would be one nobody could spend.
173
+ yield* listening;
174
+ const url = yield* mintBootstrapUrl(secrets);
175
+ yield* write.write(`${encodeReply({ url })}\n`);
176
+ })).pipe(Effect.timeout(EXCHANGE_DEADLINE), Effect.ignore);
177
+ /**
178
+ * Takes the store's exclusive lock for the life of the scope: an SQLite database opened in exclusive
179
+ * locking mode and written once, so its lock is held until the connection closes or the process
180
+ * ends. Fails with {@link ServerAlreadyRunning} when another server holds it.
181
+ */
182
+ const holdStoreLock = Effect.fnUntraced(function* (directory) {
183
+ const fs = yield* FileSystem.FileSystem;
184
+ const path = yield* Path.Path;
185
+ const lockPath = path.join(directory, LOCK_FILE);
186
+ const link = yield* Effect.result(fs.readLink(lockPath));
187
+ if (link._tag === "Success")
188
+ return yield* new SocketPathUnsafe({ path: lockPath, reason: "it is a symbolic link" });
189
+ const client = yield* SqliteClient.make({ filename: lockPath, disableWAL: true, busyTimeout: 0 });
190
+ yield* Effect.all([
191
+ client.unsafe("PRAGMA locking_mode = EXCLUSIVE"),
192
+ client.unsafe("BEGIN EXCLUSIVE"),
193
+ client.unsafe("COMMIT")
194
+ ], { discard: true }).pipe(Effect.mapError((error) => error.reason._tag === "LockTimeoutError"
195
+ ? new ServerAlreadyRunning({ path: lockPath })
196
+ : new SocketRefused({ path: lockPath, reason: error.reason._tag })));
197
+ yield* fs.chmod(lockPath, 0o600).pipe(Effect.mapError((error) => new SocketPathUnsafe({ path: lockPath, reason: error.reason._tag })));
198
+ // This process just created or opened it for writing: its owner is who this process runs as.
199
+ const info = yield* fs.stat(lockPath).pipe(Effect.mapError((error) => new SocketPathUnsafe({ path: lockPath, reason: error.reason._tag })));
200
+ const self = Option.getOrUndefined(info.uid);
201
+ if (self === undefined)
202
+ return yield* new SocketPathUnsafe({ path: lockPath, reason: "it has no owner to compare" });
203
+ return self;
204
+ });
205
+ /**
206
+ * Holds the store's lock and listens on `<directory>/serve.sock` for the life of the scope; returns
207
+ * the socket path, or nothing (and logs why) when the path is too long for a Unix socket. Requests
208
+ * wait for `listening` before a link is minted. Fails with {@link ServerAlreadyRunning} when another
209
+ * server runs on this store and {@link SocketPathUnsafe} when the path holds anything but this
210
+ * user's socket.
211
+ */
212
+ export const controlSocket = Effect.fn("ControlSocket.listen")(function* (directory, secrets, listening) {
213
+ const fs = yield* FileSystem.FileSystem;
214
+ // The directory must be the store's, checked, before its lock is taken inside it.
215
+ yield* prepareStoreDirectory(directory);
216
+ const self = yield* holdStoreLock(directory);
217
+ const located = yield* Effect.result(socketPathFor(directory));
218
+ if (located._tag === "Failure") {
219
+ yield* Effect.logWarning(`agent-usage login is unavailable: ${located.failure.message}`);
220
+ return undefined;
221
+ }
222
+ const socketPath = located.success;
223
+ // With the lock held, a socket here is one a server that died left behind.
224
+ const found = yield* inspect(directory, socketPath, self);
225
+ if (found !== undefined) {
226
+ yield* fs.remove(socketPath).pipe(Effect.mapError((error) => new SocketPathUnsafe({ path: socketPath, reason: error.reason._tag })));
227
+ }
228
+ const server = yield* NodeSocketServer.make({ path: socketPath }).pipe(Effect.mapError((error) => new SocketRefused({ path: socketPath, reason: errnoOf(error) ?? error.reason._tag })));
229
+ yield* Effect.addFinalizer(() => fs.remove(socketPath).pipe(Effect.ignore));
230
+ yield* fs.chmod(socketPath, 0o600).pipe(Effect.mapError((error) => new SocketPathUnsafe({ path: socketPath, reason: error.reason._tag })));
231
+ if ((yield* inspect(directory, socketPath, self)) === undefined) {
232
+ return yield* new SocketPathUnsafe({ path: socketPath, reason: "it vanished after binding" });
233
+ }
234
+ yield* Effect.forkScoped(server.run(answer(secrets, listening)));
235
+ return socketPath;
236
+ });
237
+ /**
238
+ * Asks the server running on this store for a fresh one-time link, trusting only a socket owned by
239
+ * `self`, the user id this process runs as. Fails with
240
+ * {@link ServerNotRunning} when nothing listens, {@link SocketPathTooLong} when the store has no
241
+ * control socket, {@link SocketPathUnsafe} when the path is not this user's socket,
242
+ * {@link SocketRefused} when the socket may not be used or does not answer within
243
+ * {@link EXCHANGE_DEADLINE}, and {@link LoginReplyInvalid} when the answer is not a link.
244
+ */
245
+ export const requestLoginUrl = Effect.fn("ControlSocket.requestLoginUrl")(function* (directory, self) {
246
+ const socketPath = yield* socketPathFor(directory);
247
+ const found = yield* inspect(directory, socketPath, self);
248
+ if (found === undefined)
249
+ return yield* new ServerNotRunning({ path: socketPath });
250
+ const reply = yield* Effect.scoped(Effect.gen(function* () {
251
+ const socket = yield* NodeSocket.makeNet({ path: socketPath });
252
+ const reader = yield* socket.reader;
253
+ const write = yield* socket.writer;
254
+ yield* write.write("mint\n");
255
+ return yield* readLine(reader);
256
+ })).pipe(Effect.mapError((error) => {
257
+ const code = errnoOf(error);
258
+ return code === "ECONNREFUSED" || code === "ENOENT"
259
+ ? new ServerNotRunning({ path: socketPath })
260
+ : new SocketRefused({ path: socketPath, reason: code ?? error.reason._tag });
261
+ }), Effect.timeoutOrElse({
262
+ duration: EXCHANGE_DEADLINE,
263
+ orElse: () => Effect.fail(new SocketRefused({ path: socketPath, reason: "the server did not answer in time" }))
264
+ }));
265
+ const decoded = decodeReply(reply.trim());
266
+ if (Option.isNone(decoded))
267
+ return yield* new LoginReplyInvalid({ reply: reply.trim().slice(0, 200) });
268
+ return decoded.value.url;
269
+ });
270
+ //# sourceMappingURL=ControlSocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ControlSocket.js","sourceRoot":"","sources":["../../src/server/ControlSocket.ts"],"names":[],"mappings":"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"}
@@ -4,4 +4,4 @@ import { HttpRouter } from "effect/http";
4
4
  /** True only when `candidate` is `root` itself or one of its path-segment descendants. */
5
5
  export declare const isWithinDirectory: (path: Path.Path, root: string, candidate: string) => boolean;
6
6
  /** Supply the store, runtime state and owner secrets; listener and platform belong to the executable. */
7
- export declare const application: Layer.Layer<never, never, FileSystem.FileSystem | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpRouter.HttpRouter | import("./OwnerSession.js").OwnerSessionSecrets | Path.Path | HttpRouter.Request<"Error", import("effect/PlatformError").BadArgument> | HttpRouter.Request<"Error", import("effect/http/HttpBody").HttpBodyError> | HttpRouter.Request<"Error", import("effect/PlatformError").PlatformError> | HttpRouter.Request<"Requires", FileSystem.FileSystem> | HttpRouter.Request<"Requires", import("./OwnerSession.js").OwnerSessionSecrets> | HttpRouter.Request<"Requires", Path.Path> | import("./Runtime.js").RuntimeState | import("../core/Store.js").UsageStore>;
7
+ export declare const application: Layer.Layer<never, never, FileSystem.FileSystem | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpRouter.HttpRouter | import("./OwnerSession.js").OwnerSessionSecrets | Path.Path | HttpRouter.Request<"Error", import("effect/PlatformError").BadArgument> | HttpRouter.Request<"Error", import("effect/http/HttpBody").HttpBodyError> | HttpRouter.Request<"Error", import("effect/PlatformError").PlatformError> | HttpRouter.Request<"Error", import("effect/socket/Socket").SocketError> | HttpRouter.Request<"Requires", FileSystem.FileSystem> | HttpRouter.Request<"Requires", import("./OwnerSession.js").OwnerSessionSecrets> | HttpRouter.Request<"Requires", Path.Path> | HttpRouter.Request<"Requires", import("./Runtime.js").RuntimeState> | import("./Runtime.js").RuntimeState | import("../core/Store.js").UsageStore>;
@@ -4,6 +4,7 @@ import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
4
4
  import { HttpApiBuilder } from "effect/http-api";
5
5
  import { AgentUsageApi } from "./Api.js";
6
6
  import { UsageLive } from "./Handlers.js";
7
+ import { LiveRouter } from "./Live.js";
7
8
  import { ownerSessionAuthLayer, OwnerSessionBootstrapRouter } from "./OwnerSession.js";
8
9
  const mimeTypes = new Map([
9
10
  [".css", "text/css"],
@@ -66,5 +67,5 @@ const StaticRouter = HttpRouter.use((router) => router.add("GET", "/*", serveSta
66
67
  const NoStore = HttpRouter.middleware((httpEffect) => Effect.map(httpEffect, HttpServerResponse.setHeader("cache-control", "private, no-store")));
67
68
  const ApiRoutes = HttpApiBuilder.layer(AgentUsageApi).pipe(Layer.provide(UsageLive), Layer.provide(ownerSessionAuthLayer), Layer.provide(NoStore.layer));
68
69
  /** Supply the store, runtime state and owner secrets; listener and platform belong to the executable. */
69
- export const application = Layer.mergeAll(ApiRoutes, OwnerSessionBootstrapRouter, StaticRouter).pipe(Layer.orDie);
70
+ export const application = Layer.mergeAll(ApiRoutes, OwnerSessionBootstrapRouter, LiveRouter, StaticRouter).pipe(Layer.orDie);
70
71
  //# sourceMappingURL=HttpApplication.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HttpApplication.js","sourceRoot":"","sources":["../../src/server/HttpApplication.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HttpApplication.js","sourceRoot":"","sources":["../../src/server/HttpApplication.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The plain-text summary `agent-usage ingest` prints after one pass: a line per source, then how
3
+ * long the pass took.
4
+ *
5
+ * @module
6
+ */
7
+ import type { IngestStatus } from "../core/Ingest.js";
8
+ export declare const describeIngest: (status: IngestStatus) => ReadonlyArray<string>;
@@ -0,0 +1,32 @@
1
+ const skippedLines = (status) => status.skipped.unparseableLine + status.skipped.missingTimestamp + status.skipped.oversizedLine;
2
+ const describeSource = (name, status) => {
3
+ if (status.rootMissing)
4
+ return `${name}: no sessions directory`;
5
+ const skipped = skippedLines(status);
6
+ return [
7
+ `${name}: ${status.filesRead}/${status.filesScanned} files read, ${status.eventsAdded} events added`,
8
+ skipped > 0 ? `, ${skipped} lines skipped` : "",
9
+ status.unreadable.length > 0 ? `, ${status.unreadable.length} unreadable` : ""
10
+ ].join("");
11
+ };
12
+ /** The claude-statusline limit log: absent until claude-statusline has written one. */
13
+ const describeLimitLog = (status) => {
14
+ if (status.rootMissing)
15
+ return "claude-statusline limits: none logged yet";
16
+ if (status.filesRead === 0 && status.unreadable.length > 0) {
17
+ return `claude-statusline limits: unreadable (${status.unreadable.map((entry) => entry.reason).join(", ")})`;
18
+ }
19
+ const skipped = skippedLines(status);
20
+ return [
21
+ "claude-statusline limits: read",
22
+ skipped > 0 ? `, ${skipped} lines skipped` : "",
23
+ status.unreadable.length > 0 ? `, unreadable (${status.unreadable.map((entry) => entry.reason).join(", ")})` : ""
24
+ ].join("");
25
+ };
26
+ export const describeIngest = (status) => [
27
+ describeSource("claude", status.claude),
28
+ describeSource("codex", status.codex),
29
+ describeLimitLog(status.claudeLimitSamples),
30
+ `took ${status.finishedAt - status.startedAt}ms`
31
+ ];
32
+ //# sourceMappingURL=IngestSummary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IngestSummary.js","sourceRoot":"","sources":["../../src/server/IngestSummary.ts"],"names":[],"mappings":"AAQA,MAAM,YAAY,GAAG,CAAC,MAAoB,EAAU,EAAE,CACpD,MAAM,CAAC,OAAO,CAAC,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAA;AAEjG,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,MAAoB,EAAU,EAAE;IACpE,IAAI,MAAM,CAAC,WAAW;QAAE,OAAO,GAAG,IAAI,yBAAyB,CAAA;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;IACpC,OAAO;QACL,GAAG,IAAI,KAAK,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,YAAY,gBAAgB,MAAM,CAAC,WAAW,eAAe;QACpG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,gBAAgB,CAAC,CAAC,CAAC,EAAE;QAC/C,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,UAAU,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,EAAE;KAC/E,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACZ,CAAC,CAAA;AAED,uFAAuF;AACvF,MAAM,gBAAgB,GAAG,CAAC,MAAoB,EAAU,EAAE;IACxD,IAAI,MAAM,CAAC,WAAW;QAAE,OAAO,2CAA2C,CAAA;IAC1E,IAAI,MAAM,CAAC,SAAS,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3D,OAAO,yCAAyC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;IAC9G,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;IACpC,OAAO;QACL,gCAAgC;QAChC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,gBAAgB,CAAC,CAAC,CAAC,EAAE;QAC/C,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;KAClH,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACZ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAoB,EAAyB,EAAE,CAAC;IAC7E,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;IACvC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;IACrC,gBAAgB,CAAC,MAAM,CAAC,kBAAkB,CAAC;IAC3C,QAAQ,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,SAAS,IAAI;CACjD,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { HttpRouter } from "effect/http";
2
+ import { OwnerSessionSecrets } from "./OwnerSession.js";
3
+ import { RuntimeState } from "./Runtime.js";
4
+ export declare const LiveRouter: import("effect/Layer").Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request<"Error", import("effect/socket/Socket").SocketError> | HttpRouter.Request<"Requires", OwnerSessionSecrets> | HttpRouter.Request<"Requires", RuntimeState>>;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The live-updates socket: `GET /api/live` upgrades to a WebSocket that sends the current
3
+ * {@link LiveVersions} and then every change, so the page refetches a read only when it moved.
4
+ *
5
+ * **Mental model**
6
+ *
7
+ * - **The same door as every read.** The upgrade is admitted by the owner-session check the API
8
+ * uses, unchanged: the session cookie, the page's own origin, Fetch Metadata, a read method.
9
+ * Anything else is refused before the socket exists.
10
+ * - **Versions, not data.** A message names which reads moved; the page fetches them through the
11
+ * ordinary authenticated routes, so the socket adds no second path to the data.
12
+ * - **Moved after commit.** The runtime moves a counter once the store holds the change, so a
13
+ * refetch triggered by a message always sees it.
14
+ *
15
+ * @module
16
+ */
17
+ import { Effect, Schema, Stream, SubscriptionRef } from "effect";
18
+ import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
19
+ import { LiveVersions } from "../shared/contracts.js";
20
+ import { authorizeOwnerRequest, OwnerSessionSecrets } from "./OwnerSession.js";
21
+ import { RuntimeState } from "./Runtime.js";
22
+ const encodeVersions = Schema.encodeSync(Schema.fromJsonString(LiveVersions));
23
+ const refusal = (status, message) => HttpServerResponse.text(message, { status, headers: { "cache-control": "private, no-store" } });
24
+ const live = Effect.gen(function* () {
25
+ const request = yield* HttpServerRequest.HttpServerRequest;
26
+ const secrets = yield* OwnerSessionSecrets;
27
+ const state = yield* RuntimeState;
28
+ const admitted = yield* Effect.result(authorizeOwnerRequest({
29
+ credential: request.cookies["agent_usage_owner"] ?? "",
30
+ fetchSite: request.headers["sec-fetch-site"],
31
+ method: request.method,
32
+ origin: request.headers.origin
33
+ }, secrets));
34
+ if (admitted._tag === "Failure") {
35
+ return admitted.failure._tag === "UnauthorizedApiError"
36
+ ? refusal(401, admitted.failure.message)
37
+ : refusal(403, admitted.failure.message);
38
+ }
39
+ const socket = yield* request.upgrade;
40
+ const write = yield* socket.writer;
41
+ const reader = yield* socket.reader;
42
+ // Messages from the page are not expected; reading only notices that it went away.
43
+ const closed = Effect.forever(reader.pull).pipe(Effect.ignore);
44
+ const pushing = SubscriptionRef.changes(state.versions).pipe(Stream.runForEach((versions) => write.write(encodeVersions(versions))), Effect.ignore);
45
+ yield* Effect.raceFirst(pushing, closed);
46
+ return HttpServerResponse.empty();
47
+ }).pipe(Effect.scoped, Effect.catchTag("HttpServerError", () => Effect.succeed(refusal(403, "Not a WebSocket upgrade"))));
48
+ export const LiveRouter = HttpRouter.use((router) => router.add("GET", "/api/live", live));
49
+ //# sourceMappingURL=Live.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Live.js","sourceRoot":"","sources":["../../src/server/Live.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `agent-usage login`: a fresh one-time link from the running server, printed and optionally opened.
3
+ *
4
+ * The link goes to stdout and nowhere else, like the startup link: it is the credential. Failures
5
+ * go to stderr as one sentence saying what to do, and end the process with a nonzero exit.
6
+ *
7
+ * @module
8
+ */
9
+ import { Effect, Runtime, Schema } from "effect";
10
+ import { ChildProcessSpawner } from "effect/process";
11
+ import { type LoginReplyInvalid, type ServerNotRunning, type SocketPathTooLong, type SocketPathUnsafe, type SocketRefused } from "./ControlSocket.js";
12
+ export type LoginFailure = ServerNotRunning | SocketPathTooLong | SocketPathUnsafe | SocketRefused | LoginReplyInvalid;
13
+ declare const LoginFailed_base: Schema.Class<LoginFailed, Schema.TaggedStruct<"LoginFailed", {
14
+ readonly reason: Schema.String;
15
+ }>, import("effect/Cause").YieldableError>;
16
+ /** The failure, already explained on stderr: the runtime exits nonzero without logging it again. */
17
+ export declare class LoginFailed extends LoginFailed_base {
18
+ readonly [Runtime.errorReported] = false;
19
+ }
20
+ /** One sentence per failure, naming what to do about it. */
21
+ export declare const describeLoginFailure: (failure: LoginFailure) => string;
22
+ /** The command that opens a URL in the default browser, where there is one this tool knows. */
23
+ export declare const openerFor: (platform: string) => "open" | "xdg-open" | undefined;
24
+ /**
25
+ * Reads the store directory from `config`, asks the server running there for a link, prints it, and
26
+ * opens it when asked. Every failure, an unreadable configuration included, goes to stderr only, so
27
+ * stdout carries the link or nothing.
28
+ */
29
+ export declare const login: <E extends {
30
+ readonly message: string;
31
+ }, R>(config: Effect.Effect<{
32
+ readonly storeDirectory: string;
33
+ }, E, R>, open: boolean, platform: string, self: number) => Effect.Effect<void, LoginFailed, R | ChildProcessSpawner.ChildProcessSpawner | import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
34
+ export {};
@@ -0,0 +1,74 @@
1
+ /**
2
+ * `agent-usage login`: a fresh one-time link from the running server, printed and optionally opened.
3
+ *
4
+ * The link goes to stdout and nowhere else, like the startup link: it is the credential. Failures
5
+ * go to stderr as one sentence saying what to do, and end the process with a nonzero exit.
6
+ *
7
+ * @module
8
+ */
9
+ import { Console, Effect, Runtime, Schema } from "effect";
10
+ import { ChildProcess, ChildProcessSpawner } from "effect/process";
11
+ import { requestLoginUrl } from "./ControlSocket.js";
12
+ /** The failure, already explained on stderr: the runtime exits nonzero without logging it again. */
13
+ export class LoginFailed extends Schema.TaggedError()("LoginFailed", {
14
+ reason: Schema.String
15
+ }) {
16
+ [Runtime.errorReported] = false;
17
+ }
18
+ /** One sentence per failure, naming what to do about it. */
19
+ export const describeLoginFailure = (failure) => {
20
+ switch (failure._tag) {
21
+ case "ServerNotRunning":
22
+ return "agent-usage is not running on this store. Start it with `agent-usage serve` (or its service), then run login again. If it is running, it is an older version without login: restart it.";
23
+ case "SocketPathUnsafe":
24
+ return `agent-usage refused its control socket at ${failure.path}: ${failure.reason}. Remove it and restart the server.`;
25
+ case "SocketRefused":
26
+ return `agent-usage's control socket at ${failure.path} could not be used (${failure.reason}).`;
27
+ case "SocketPathTooLong":
28
+ return `${failure.message}, so this store has no login. Set AGENT_USAGE_HOME to a shorter path.`;
29
+ case "LoginReplyInvalid":
30
+ return "the running agent-usage did not answer with a link; is it an older version? Restart it.";
31
+ }
32
+ };
33
+ /** The command that opens a URL in the default browser, where there is one this tool knows. */
34
+ export const openerFor = (platform) => platform === "darwin" ? "open" : platform === "linux" ? "xdg-open" : undefined;
35
+ /**
36
+ * Opens `url` in the browser. Reports a failure on stderr and carries on: the link is printed
37
+ * either way. The link is on the opener's command line while it runs, visible to this machine's
38
+ * process list for that moment; it is spent on first use and expires within the minute.
39
+ */
40
+ const openInBrowser = Effect.fnUntraced(function* (url, platform) {
41
+ const opener = openerFor(platform);
42
+ if (opener === undefined) {
43
+ return yield* Console.error(`agent-usage: cannot open a browser on ${platform}; open the link above.`);
44
+ }
45
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
46
+ // In this process's group, not one of its own: the opener's group is killed when it is reaped,
47
+ // which would take the browser it just started with it. Its output is not ours to hold open
48
+ // either: the browser inherits it and would keep login waiting for as long as it runs.
49
+ const opening = ChildProcess.make(opener, [url], {
50
+ detached: false,
51
+ stdin: "ignore",
52
+ stdout: "ignore",
53
+ stderr: "ignore"
54
+ });
55
+ const code = yield* spawner.exitCode(opening).pipe(Effect.result);
56
+ if (code._tag === "Failure" || code.success !== 0) {
57
+ yield* Console.error(`agent-usage: ${opener} could not open the link; open it yourself.`);
58
+ }
59
+ });
60
+ /** Explains `failure` on stderr, then fails without the runtime reporting it again. */
61
+ const reported = (sentence, reason) => Console.error(`agent-usage: ${sentence}`).pipe(Effect.andThen(Effect.fail(new LoginFailed({ reason }))));
62
+ /**
63
+ * Reads the store directory from `config`, asks the server running there for a link, prints it, and
64
+ * opens it when asked. Every failure, an unreadable configuration included, goes to stderr only, so
65
+ * stdout carries the link or nothing.
66
+ */
67
+ export const login = Effect.fn("Login.login")(function* (config, open, platform, self) {
68
+ const settings = yield* config.pipe(Effect.catch((error) => reported(`its configuration could not be read: ${error.message}`, "ConfigError")));
69
+ const url = yield* requestLoginUrl(settings.storeDirectory, self).pipe(Effect.catch((failure) => reported(describeLoginFailure(failure), failure._tag)));
70
+ yield* Console.log(url);
71
+ if (open)
72
+ yield* openInBrowser(url, platform);
73
+ });
74
+ //# sourceMappingURL=Login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Login.js","sourceRoot":"","sources":["../../src/server/Login.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,12 @@ import type { PairingCode, SessionToken } from "@knpkv/browser-pairing/schema";
2
2
  import { Context, Effect, Layer, Redacted, Ref, Schema } from "effect";
3
3
  import { HttpRouter } from "effect/http";
4
4
  import { ForbiddenApiError, OwnerSessionAuth, UnauthorizedApiError } from "./Api.js";
5
+ /** One minted code: what to compare, until when, and whether it has been spent. */
6
+ export interface BootstrapCode {
7
+ readonly token: Redacted.Redacted<PairingCode>;
8
+ readonly expiresAt: number;
9
+ readonly available: boolean;
10
+ }
5
11
  export interface BootstrapAttemptState {
6
12
  readonly failedAttempts: number;
7
13
  readonly inFlight: number;
@@ -14,10 +20,9 @@ export interface OwnerSessionSecretsContract {
14
20
  * dev server when it proxies in front of it, because that is the origin the page runs on.
15
21
  */
16
22
  readonly browserOrigin: string;
17
- readonly bootstrapAvailable: Ref.Ref<boolean>;
23
+ /** The current one-time code, or none before the first mint. */
24
+ readonly bootstrap: Ref.Ref<BootstrapCode | undefined>;
18
25
  readonly bootstrapAttemptState: Ref.Ref<BootstrapAttemptState>;
19
- readonly bootstrapExpiresAtMillis: Ref.Ref<number | undefined>;
20
- readonly bootstrapToken: Redacted.Redacted<PairingCode>;
21
26
  readonly ownerToken: Redacted.Redacted<SessionToken>;
22
27
  }
23
28
  declare const OwnerSessionSecrets_base: Context.ServiceClass<OwnerSessionSecrets, "@knpkv/agent-usage/OwnerSessionSecrets", OwnerSessionSecretsContract>;
@@ -35,13 +40,15 @@ export declare const requireLoopbackOrigin: (origin: string) => Effect.Effect<st
35
40
  /** The advertised origin: the bound server, or the dev server that proxies to it. */
36
41
  export declare const resolvePublicOrigin: (configuredOrigin: string | undefined, authorityOrigin: string) => Effect.Effect<string, UnsafeServerOriginError, never>;
37
42
  export declare const makeOwnerSessionSecrets: (authorityOrigin: string, configuredPublicOrigin?: string | undefined) => Effect.Effect<OwnerSessionSecretsContract, import("@knpkv/browser-pairing").BrowserPairingError | UnsafeServerOriginError, import("effect/Crypto").Crypto>;
38
- /** Start the bootstrap clock. Called once the server is actually listening, never before. */
39
- export declare const activateOwnerSessionBootstrap: (secrets: OwnerSessionSecretsContract) => Effect.Effect<void, import("@knpkv/browser-pairing").BrowserPairingError, never>;
40
43
  /**
41
- * The URL to open. The code rides in the fragment, which browsers do not send to servers and
42
- * proxies do not log, and the page strips it from the address bar once it has been spent.
44
+ * Mints a fresh one-time code and returns the URL that carries it: usable once, for a minute. The
45
+ * startup link and every `agent-usage login` link come from here; a newer code replaces any unspent
46
+ * one and reopens an exchange that guesses had closed. Call it only once the server is listening.
47
+ *
48
+ * The code rides in the fragment, which browsers do not send to servers and proxies do not log,
49
+ * and the page strips it from the address bar once it has been spent.
43
50
  */
44
- export declare const ownerSessionUrl: (publicOrigin: string, secrets: Pick<OwnerSessionSecretsContract, "bootstrapToken">) => string;
51
+ export declare const mintBootstrapUrl: (secrets: OwnerSessionSecretsContract) => Effect.Effect<string, import("@knpkv/browser-pairing").BrowserPairingError, import("effect/Crypto").Crypto>;
45
52
  export declare const ownerSessionCookie: (secrets: Pick<OwnerSessionSecretsContract, "ownerToken">) => string;
46
53
  interface OwnerRequest {
47
54
  readonly credential: string;