claude-sdk-proxy 3.0.0 → 3.1.1

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/README.md CHANGED
@@ -300,6 +300,10 @@ Use your endpoint in any Anthropic-compatible chat app:
300
300
  | **API Key** | Your `CLAUDE_PROXY_API_KEY` value |
301
301
  | **Model** | `claude-opus-4-6`, `claude-sonnet-4-6`, or `claude-haiku-4-5-20251001` |
302
302
 
303
+ ## Attribution
304
+
305
+ **Built on** [rynfar/opencode-claude-max-proxy](https://github.com/rynfar/opencode-claude-max-proxy) — the original concept and foundation for this proxy implementation. This fork expands the idea into a full mock API endpoint with Anthropic and OpenAI compatibility.
306
+
303
307
  ## License
304
308
 
305
309
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-sdk-proxy",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "Anthropic Messages API proxy backed by Claude Agent SDK — use Claude Max with any API client",
5
5
  "type": "module",
6
6
  "main": "./src/proxy/server.ts",
package/src/logger.ts CHANGED
@@ -1,13 +1,132 @@
1
- const shouldLog = () =>
1
+ import { mkdirSync, appendFileSync, existsSync } from "fs"
2
+ import { join } from "path"
3
+
4
+ // ── Configuration ────────────────────────────────────────────────────────────
5
+
6
+ const LOG_DIR = process.env.CLAUDE_PROXY_LOG_DIR ?? "/tmp/claude-proxy"
7
+ const LOG_LEVEL_ENV = (process.env.CLAUDE_PROXY_LOG_LEVEL ?? "info").toLowerCase()
8
+ const IS_DEBUG =
2
9
  process.env["CLAUDE_PROXY_DEBUG"] === "1" ||
3
10
  process.env["OPENCODE_CLAUDE_PROVIDER_DEBUG"] === "1"
4
11
 
5
- export const claudeLog = (message: string, extra?: Record<string, unknown>) => {
6
- if (!shouldLog()) return
7
- const ts = new Date().toISOString()
8
- const parts = [`[${ts}] [claude-sdk-proxy]`, message]
9
- if (extra && Object.keys(extra).length > 0) {
10
- parts.push(JSON.stringify(extra))
12
+ const LEVELS = { error: 0, warn: 1, info: 2, debug: 3 } as const
13
+ type LogLevel = keyof typeof LEVELS
14
+
15
+ const currentLevel: number = IS_DEBUG
16
+ ? LEVELS.debug
17
+ : LEVELS[LOG_LEVEL_ENV as LogLevel] ?? LEVELS.info
18
+
19
+ // ── File output ──────────────────────────────────────────────────────────────
20
+
21
+ let currentDateStr = ""
22
+ let currentLogPath = ""
23
+
24
+ function ensureLogDir() {
25
+ try {
26
+ if (!existsSync(LOG_DIR)) mkdirSync(LOG_DIR, { recursive: true })
27
+ const errDir = join(LOG_DIR, "errors")
28
+ if (!existsSync(errDir)) mkdirSync(errDir, { recursive: true })
29
+ } catch {}
30
+ }
31
+
32
+ ensureLogDir()
33
+
34
+ function getLogPath(): string {
35
+ const dateStr = new Date().toISOString().slice(0, 10) // YYYY-MM-DD
36
+ if (dateStr !== currentDateStr) {
37
+ currentDateStr = dateStr
38
+ currentLogPath = join(LOG_DIR, `proxy-${dateStr}.log`)
11
39
  }
12
- console.debug(parts.join(" "))
40
+ return currentLogPath
13
41
  }
42
+
43
+ function writeToFile(line: string) {
44
+ try {
45
+ appendFileSync(getLogPath(), line + "\n")
46
+ } catch {}
47
+ }
48
+
49
+ // ── Structured JSON logging ──────────────────────────────────────────────────
50
+ // Every log line is a single JSON object — parseable by jq, greppable by reqId.
51
+ //
52
+ // Example:
53
+ // {"ts":"2026-02-23T15:30:00.000Z","level":"info","event":"proxy.request","reqId":"req_abc","model":"haiku"}
54
+ // {"ts":"2026-02-23T15:30:05.000Z","level":"error","event":"proxy.sdk.error","reqId":"req_abc","error":"timeout"}
55
+
56
+ export interface LogEntry {
57
+ ts: string
58
+ level: LogLevel
59
+ event: string
60
+ reqId?: string
61
+ [key: string]: unknown
62
+ }
63
+
64
+ function emit(level: LogLevel, event: string, data?: Record<string, unknown>) {
65
+ if (LEVELS[level] > currentLevel) return
66
+
67
+ const entry: LogEntry = {
68
+ ts: new Date().toISOString(),
69
+ level,
70
+ event,
71
+ ...data,
72
+ }
73
+
74
+ const line = JSON.stringify(entry)
75
+
76
+ // Always write to stderr (captured by journalctl)
77
+ console.error(line)
78
+
79
+ // Always write to file (persists for post-mortem)
80
+ writeToFile(line)
81
+ }
82
+
83
+ // ── Public API ───────────────────────────────────────────────────────────────
84
+
85
+ /** Log an error — always emitted. Use for failures, crashes, unexpected states. */
86
+ export function logError(event: string, data?: Record<string, unknown>) {
87
+ emit("error", event, data)
88
+ }
89
+
90
+ /** Log a warning — always emitted. Use for degraded states, retries, slow operations. */
91
+ export function logWarn(event: string, data?: Record<string, unknown>) {
92
+ emit("warn", event, data)
93
+ }
94
+
95
+ /** Log info — always emitted. Use for request lifecycle events. */
96
+ export function logInfo(event: string, data?: Record<string, unknown>) {
97
+ emit("info", event, data)
98
+ }
99
+
100
+ /** Log debug — only when CLAUDE_PROXY_DEBUG=1. Use for verbose details. */
101
+ export function logDebug(event: string, data?: Record<string, unknown>) {
102
+ emit("debug", event, data)
103
+ }
104
+
105
+ /** Write an error dump file for a specific request. Returns the file path. */
106
+ export function dumpError(reqId: string, data: Record<string, unknown>): string {
107
+ const errDir = join(LOG_DIR, "errors")
108
+ const path = join(errDir, `${reqId}.json`)
109
+ try {
110
+ if (!existsSync(errDir)) mkdirSync(errDir, { recursive: true })
111
+ const content = JSON.stringify({ ts: new Date().toISOString(), reqId, ...data }, null, 2)
112
+ appendFileSync(path, content)
113
+ } catch (e) {
114
+ logError("logger.dump_failed", { reqId, path, error: String(e) })
115
+ }
116
+ return path
117
+ }
118
+
119
+ // ── Legacy API (backward-compatible) ─────────────────────────────────────────
120
+ // These are used by existing code. They map to the new structured logging.
121
+
122
+ /** @deprecated Use logInfo instead */
123
+ export const claudeLog = (message: string, extra?: Record<string, unknown>) => {
124
+ logInfo(message, extra)
125
+ }
126
+
127
+ /** @deprecated Use logDebug instead */
128
+ export const claudeDebug = (message: string, extra?: Record<string, unknown>) => {
129
+ logDebug(message, extra)
130
+ }
131
+
132
+ export { LOG_DIR }