@loreai/gateway 0.12.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.
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@loreai/gateway",
3
+ "version": "0.12.0",
4
+ "type": "module",
5
+ "license": "FSL-1.1-Apache-2.0",
6
+ "description": "Lore as a transparent LLM proxy — context management for any AI coding client",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "bun": "./src/index.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "bin": {
16
+ "lore-gateway": "./dist/index.js"
17
+ },
18
+ "scripts": {
19
+ "typecheck": "tsc --noEmit",
20
+ "build": "bun run script/build.ts",
21
+ "start": "bun run src/index.ts"
22
+ },
23
+ "dependencies": {
24
+ "@loreai/core": "0.12.0"
25
+ },
26
+ "files": [
27
+ "src/",
28
+ "dist/",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "engines": {
33
+ "bun": ">=1.2.0"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/BYK/loreai.git",
38
+ "directory": "packages/gateway"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "keywords": [
44
+ "lore",
45
+ "gateway",
46
+ "proxy",
47
+ "llm",
48
+ "context-management",
49
+ "anthropic",
50
+ "openai"
51
+ ],
52
+ "author": "BYK"
53
+ }
package/src/auth.ts ADDED
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Gateway authentication: typed credentials, per-session registry, and
3
+ * two-level lookup for background workers.
4
+ *
5
+ * Replaces the bare `lastSeenApiKey` string with a typed `AuthCredential`
6
+ * that supports both API-key (`x-api-key`) and OAuth Bearer token
7
+ * (`Authorization: Bearer`) authentication schemes.
8
+ *
9
+ * The per-session registry ensures background workers (distillation,
10
+ * curation, batch queue) use the correct credential for their session
11
+ * even when multiple clients are connected simultaneously.
12
+ */
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // AuthCredential type
16
+ // ---------------------------------------------------------------------------
17
+
18
+ /** Auth credential — either an API key or an OAuth bearer token. */
19
+ export type AuthCredential =
20
+ | { scheme: "api-key"; value: string }
21
+ | { scheme: "bearer"; value: string };
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Header extraction / formatting
25
+ // ---------------------------------------------------------------------------
26
+
27
+ /**
28
+ * Extract auth from request headers.
29
+ *
30
+ * Prefers `x-api-key` (Anthropic SDK default), falls back to
31
+ * `Authorization: Bearer` (OAuth / Claude Code subscriptions).
32
+ * Returns `null` if neither is present.
33
+ */
34
+ export function extractAuth(
35
+ headers: Record<string, string>,
36
+ ): AuthCredential | null {
37
+ const apiKey = headers["x-api-key"] || headers["X-Api-Key"];
38
+ if (apiKey) return { scheme: "api-key", value: apiKey };
39
+
40
+ const authHeader =
41
+ headers["authorization"] || headers["Authorization"];
42
+ if (authHeader) {
43
+ const match = /^Bearer\s+(\S+)$/i.exec(authHeader);
44
+ if (match) return { scheme: "bearer", value: match[1] };
45
+ }
46
+
47
+ return null;
48
+ }
49
+
50
+ /**
51
+ * Format credential as the appropriate HTTP header(s).
52
+ *
53
+ * - `api-key` → `{ "x-api-key": value }`
54
+ * - `bearer` → `{ "Authorization": "Bearer <value>" }`
55
+ */
56
+ export function authHeaders(cred: AuthCredential): Record<string, string> {
57
+ switch (cred.scheme) {
58
+ case "api-key":
59
+ return { "x-api-key": cred.value };
60
+ case "bearer":
61
+ return { Authorization: `Bearer ${cred.value}` };
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Non-sensitive suffix for fingerprinting — last 8 chars of credential value.
67
+ *
68
+ * Used to differentiate sessions that share the same first message but use
69
+ * different API keys or OAuth tokens. The suffix alone cannot reconstruct
70
+ * the full credential.
71
+ */
72
+ export function authFingerprint(cred: AuthCredential): string {
73
+ return cred.value.slice(-8);
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Per-session registry
78
+ // ---------------------------------------------------------------------------
79
+
80
+ const sessionAuth = new Map<string, AuthCredential>();
81
+
82
+ export function setSessionAuth(
83
+ sessionID: string,
84
+ cred: AuthCredential,
85
+ ): void {
86
+ sessionAuth.set(sessionID, cred);
87
+ }
88
+
89
+ export function getSessionAuth(
90
+ sessionID: string,
91
+ ): AuthCredential | null {
92
+ return sessionAuth.get(sessionID) ?? null;
93
+ }
94
+
95
+ /** Delete a session's credential (for future eviction). */
96
+ export function deleteSessionAuth(sessionID: string): void {
97
+ sessionAuth.delete(sessionID);
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Global fallback (replaces lastSeenApiKey)
102
+ // ---------------------------------------------------------------------------
103
+
104
+ let lastSeenAuth: AuthCredential | null = null;
105
+
106
+ export function setLastSeenAuth(cred: AuthCredential): void {
107
+ lastSeenAuth = cred;
108
+ }
109
+
110
+ export function getLastSeenAuth(): AuthCredential | null {
111
+ return lastSeenAuth;
112
+ }
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Two-level lookup
116
+ // ---------------------------------------------------------------------------
117
+
118
+ /**
119
+ * Resolve auth credentials for a given session.
120
+ *
121
+ * 1. If `sessionID` is provided, check the per-session registry first.
122
+ * 2. Fall back to the global `lastSeenAuth` (for cold-start or callers
123
+ * that don't pass a session ID).
124
+ */
125
+ export function resolveAuth(
126
+ sessionID?: string,
127
+ ): AuthCredential | null {
128
+ if (sessionID) {
129
+ const cred = getSessionAuth(sessionID);
130
+ if (cred) return cred;
131
+ }
132
+ return getLastSeenAuth();
133
+ }