@shunirr/cc-glm 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/proxy/server.ts","../../src/proxy/router.ts","../../src/config/loader.ts","../../src/proxy/transform.ts","../../src/proxy/signature-store.ts"],"sourcesContent":["/**\n * HTTP Proxy Server for Claude Code\n * Routes requests to Anthropic API or z.ai based on model name\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse, type Server } from \"node:http\";\nimport { request as httpsRequest } from \"node:https\";\nimport { request as httpRequest } from \"node:http\";\nimport type { Config } from \"../config/types.js\";\nimport type { Route } from \"./types.js\";\nimport { selectRoute, parseRequestBody, parseRequestBodyAsObject } from \"./router.js\";\nimport { loadConfig } from \"../config/loader.js\";\nimport { transformThinkingBlocks, shouldTransformResponse, shouldTransformRequest, extractAndRecordSignatures, sanitizeContentBlocksWithStore } from \"./transform.js\";\nimport { SignatureStore } from \"./signature-store.js\";\n\n/**\n * Hop-by-hop headers that should not be forwarded per RFC 7230\n * Also includes headers that could cause security issues\n */\nconst HOP_BY_HOP_HEADERS = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"te\",\n \"trailers\",\n \"transfer-encoding\",\n \"upgrade\",\n \"proxy-connection\",\n]);\n\n/**\n * Headers that should be removed to prevent spoofing (RFC 7239 and security)\n */\nconst SECURITY_HEADERS_TO_REMOVE = new Set([\n \"x-forwarded-for\",\n \"x-forwarded-host\",\n \"x-forwarded-proto\",\n \"x-forwarded-port\",\n \"x-real-ip\",\n \"forwarded\",\n]);\n\n/**\n * Maximum request body size (10MB)\n */\nconst MAX_BODY_SIZE = 10 * 1024 * 1024;\n\n/**\n * Maximum transform response size (50MB) to prevent memory DoS\n */\nconst MAX_TRANSFORM_SIZE = 50 * 1024 * 1024;\n\n/**\n * Upstream request timeout (30 seconds)\n */\nconst UPSTREAM_TIMEOUT_MS = 30_000;\n\n/** Create and start the proxy server */\nexport function createProxyServer(config: Config): Server {\n // Create signature store with configured max size\n const maxSize = config.signatureStore?.maxSize;\n const signatureStore = new SignatureStore(maxSize);\n console.log(`Signature store initialized with max size: ${maxSize ?? 1000}`);\n\n const server = createServer(async (req, res) => {\n await handleRequest(req, res, config, signatureStore);\n });\n\n const { port, host } = config.proxy;\n server.listen(port, host, () => {\n console.log(`Claude Router Proxy on :${port}`);\n console.log(` anthropic -> ${config.upstream.anthropic.url}`);\n console.log(` zai -> ${config.upstream.zai.url}`);\n if (config.routing.rules.length > 0) {\n console.log(` routing rules: ${config.routing.rules.length}, default: ${config.routing.default}`);\n }\n });\n\n return server;\n}\n\n/** Handle incoming HTTP request */\nasync function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n config: Config,\n signatureStore: SignatureStore\n): Promise<void> {\n const reqId = Date.now().toString(36);\n let requestBody: Buffer | null = null;\n let proxyReq: ReturnType<typeof httpsRequest | typeof httpRequest> | null = null;\n let isAborted = false;\n\n // Register abort handlers early to catch early disconnects\n const onAborted = () => {\n console.log(`[${reqId}] Client aborted`);\n isAborted = true;\n if (proxyReq) {\n proxyReq.destroy();\n }\n };\n\n const onError = (err: Error) => {\n console.error(`[${reqId}] Request error: ${err.message}`);\n isAborted = true;\n if (proxyReq) {\n proxyReq.destroy();\n }\n };\n\n req.once(\"aborted\", onAborted);\n req.once(\"error\", onError);\n\n try {\n // Determine if we should buffer the body based on headers\n const method = req.method ?? \"GET\";\n const needsBody = hasRequestBody(req);\n\n // Parse model from request body if needed\n let target: Route;\n let model = \"no-model\";\n let bodyWasRewritten = false;\n\n if (needsBody) {\n // Buffer request body with size limit\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n for await (const chunk of req) {\n if (isAborted) return;\n totalSize += (chunk as Buffer).length;\n if (totalSize > MAX_BODY_SIZE) {\n if (!res.headersSent) {\n res.writeHead(413, { \"content-type\": \"application/json\" });\n }\n res.end(JSON.stringify({ error: \"payload_too_large\", message: \"Request body exceeds maximum size\" }));\n return;\n }\n chunks.push(chunk as Buffer);\n }\n if (isAborted) return;\n requestBody = Buffer.concat(chunks);\n\n const parsed = parseRequestBody(requestBody);\n if (parsed) {\n model = parsed.model || \"no-model\";\n }\n target = selectRoute(parsed?.model, config);\n } else {\n target = selectRoute(undefined, config);\n }\n\n // Rewrite model name in request body if route specifies a different model\n let forwardBody: Buffer;\n if (needsBody && requestBody && target.model) {\n const bodyObj = parseRequestBodyAsObject(requestBody);\n if (bodyObj) {\n bodyObj.model = target.model;\n forwardBody = Buffer.from(JSON.stringify(bodyObj));\n bodyWasRewritten = true;\n console.log(`[${reqId}] model rewrite: ${model} -> ${target.model}`);\n } else {\n forwardBody = requestBody;\n }\n } else {\n forwardBody = (needsBody && requestBody) || Buffer.alloc(0);\n }\n\n // Sanitize content blocks for Anthropic API\n // Removes z.ai specific fields from thinking blocks in message history\n // Converts z.ai-origin thinking blocks to text blocks (unrecorded signatures)\n if (needsBody && forwardBody.length > 0) {\n const contentType = req.headers[\"content-type\"];\n if (shouldTransformRequest(contentType, target.name)) {\n const originalBody = forwardBody.toString();\n const sanitized = sanitizeContentBlocksWithStore(originalBody, signatureStore);\n if (sanitized !== originalBody) {\n forwardBody = Buffer.from(sanitized);\n bodyWasRewritten = true;\n console.log(`[${reqId}] sanitized request content blocks for Anthropic`);\n }\n }\n }\n\n // Build upstream URL (handle undefined req.url)\n const reqUrl = req.url ?? \"/\";\n const baseUrl = new URL(target.url);\n const basePath = baseUrl.pathname.replace(/\\/$/, \"\");\n const upstreamUrl = new URL(basePath + reqUrl, baseUrl.origin);\n const isHttps = upstreamUrl.protocol === \"https:\";\n const doRequest = isHttps ? httpsRequest : httpRequest;\n\n console.log(`[${reqId}] ${method} ${reqUrl} model=${model} -> ${target.name}`);\n\n // Prepare headers with proper filtering\n const forwardHeaders = buildForwardHeaders(req.headers, target, forwardBody, bodyWasRewritten);\n\n // Forward request\n proxyReq = doRequest(\n upstreamUrl,\n { method, headers: forwardHeaders, timeout: UPSTREAM_TIMEOUT_MS },\n (proxyRes) => {\n if (isAborted) {\n proxyRes.destroy();\n return;\n }\n\n console.log(`[${reqId}] <- ${proxyRes.statusCode}`);\n\n // Check if we need to transform the response\n const contentType = proxyRes.headers[\"content-type\"];\n const needsTransform = shouldTransformResponse(contentType, target.name);\n const isAnthropic = target.name === \"anthropic\";\n const needsSignatureExtraction = isAnthropic && contentType?.includes(\"application/json\");\n\n // Build response headers, removing hop-by-hop headers\n // When buffering response (for transform or signature extraction), remove transfer-encoding\n const needsBuffering = needsTransform || !!needsSignatureExtraction;\n const resHeaders = buildResponseHeaders(proxyRes.headers, needsBuffering);\n\n if (needsTransform || needsSignatureExtraction) {\n // Buffer the response for transformation with size limit\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n proxyRes.on(\"data\", (chunk) => {\n if (isAborted) return;\n totalSize += chunk.length;\n if (totalSize > MAX_TRANSFORM_SIZE) {\n console.error(`[${reqId}] Transform buffer exceeded limit`);\n proxyRes.destroy();\n if (!res.headersSent) {\n res.writeHead(502, { \"content-type\": \"application/json\" });\n }\n res.end(JSON.stringify({ error: \"transform_error\", message: \"Response too large to transform\" }));\n return;\n }\n chunks.push(chunk);\n });\n\n proxyRes.on(\"end\", () => {\n req.off(\"aborted\", onAborted);\n req.off(\"error\", onError);\n if (isAborted) return;\n try {\n const body = Buffer.concat(chunks).toString();\n let processed = body;\n\n // Extract signatures from Anthropic responses\n if (isAnthropic) {\n processed = extractAndRecordSignatures(body, signatureStore);\n }\n\n // Transform z.ai responses\n if (needsTransform) {\n processed = transformThinkingBlocks(processed);\n }\n\n resHeaders[\"content-length\"] = String(Buffer.byteLength(processed));\n res.writeHead(proxyRes.statusCode || 200, resHeaders);\n res.end(processed);\n } catch (err) {\n const error = err as Error;\n console.error(`[${reqId}] Transform error: ${error.message}`);\n if (!res.headersSent) {\n res.writeHead(502, { \"content-type\": \"application/json\" });\n }\n res.end(JSON.stringify({ error: \"transform_error\", message: error.message }));\n }\n });\n } else {\n req.off(\"aborted\", onAborted);\n req.off(\"error\", onError);\n // Stream response directly without transformation\n res.writeHead(proxyRes.statusCode || 200, resHeaders);\n proxyRes.pipe(res);\n }\n }\n );\n\n // Set up timeout for upstream request\n if (proxyReq.setTimeout) {\n proxyReq.setTimeout(UPSTREAM_TIMEOUT_MS, () => {\n console.error(`[${reqId}] Upstream timeout`);\n isAborted = true;\n proxyReq?.destroy();\n req.off(\"aborted\", onAborted);\n req.off(\"error\", onError);\n if (!res.headersSent) {\n res.writeHead(504, { \"content-type\": \"application/json\" });\n }\n res.end(JSON.stringify({ error: \"gateway_timeout\", message: \"Upstream timeout\" }));\n });\n }\n\n // Handle upstream request errors\n proxyReq.on(\"error\", (err) => {\n if (isAborted) return;\n console.error(`[${reqId}] Upstream error: ${err.message}`);\n req.off(\"aborted\", onAborted);\n req.off(\"error\", onError);\n if (!res.headersSent) {\n res.writeHead(502, { \"content-type\": \"application/json\" });\n }\n res.end(JSON.stringify({ error: \"proxy_error\", message: err.message }));\n });\n\n // Handle proxy response errors\n proxyReq.on(\"response\", (proxyRes) => {\n proxyRes.on(\"error\", (err) => {\n console.error(`[${reqId}] Response error: ${err.message}`);\n if (!res.writableEnded) {\n res.end();\n }\n });\n });\n\n // Send body if present\n if (forwardBody && forwardBody.length > 0) {\n proxyReq.write(forwardBody);\n }\n proxyReq.end();\n } catch (err) {\n const error = err as Error;\n console.error(`[${reqId}] ERROR: ${error.message}`);\n if (proxyReq) {\n proxyReq.destroy();\n }\n req.off(\"aborted\", onAborted);\n req.off(\"error\", onError);\n if (!res.headersSent) {\n res.writeHead(502, { \"content-type\": \"application/json\" });\n }\n res.end(JSON.stringify({ error: \"proxy_error\", message: error.message }));\n }\n}\n\n/**\n * Determine if request has a body based on method and headers\n */\nfunction hasRequestBody(req: IncomingMessage): boolean {\n const method = (req.method ?? \"GET\").toUpperCase();\n\n // Methods that typically have a body\n const bodyMethods = new Set([\"POST\", \"PUT\", \"PATCH\"]);\n\n if (bodyMethods.has(method)) {\n return true;\n }\n\n // Check for content-length or transfer-encoding headers\n // Some APIs use body with DELETE or other methods\n const contentLength = req.headers[\"content-length\"];\n const transferEncoding = req.headers[\"transfer-encoding\"];\n\n return (\n (contentLength && parseInt(contentLength, 10) > 0) ||\n !!transferEncoding\n );\n}\n\n/**\n * Parse Connection header to extract additional hop-by-hop header names\n * per RFC 7230 Section 6.1\n */\nfunction parseConnectionHeader(connection: string | string[] | undefined): Set<string> {\n const additionalHeaders = new Set<string>();\n\n if (!connection) return additionalHeaders;\n\n // Handle both string and array cases\n const values = Array.isArray(connection) ? connection : [connection];\n\n for (const value of values) {\n if (typeof value === \"string\") {\n // Split by comma and trim each header name\n const headers = value.split(\",\").map((h) => h.trim().toLowerCase());\n for (const h of headers) {\n if (h) additionalHeaders.add(h);\n }\n }\n }\n\n return additionalHeaders;\n}\n\n/**\n * Build forwarded request headers with proper filtering\n * Removes hop-by-hop headers and security-sensitive headers\n * Recalculates content-length if body was rewritten\n */\nfunction buildForwardHeaders(\n reqHeaders: IncomingMessage[\"headers\"],\n target: Route,\n forwardBody: Buffer,\n bodyWasRewritten: boolean\n): Record<string, string | string[]> {\n const forwardHeaders: Record<string, string | string[]> = {};\n\n // Get additional hop-by-hop headers from Connection header\n const connectionHeaders = parseConnectionHeader(reqHeaders[\"connection\"]);\n\n for (const [key, value] of Object.entries(reqHeaders)) {\n const keyLower = key.toLowerCase();\n\n // Skip hop-by-hop headers (standard ones)\n if (HOP_BY_HOP_HEADERS.has(keyLower)) {\n continue;\n }\n\n // Skip headers specified in Connection header\n if (connectionHeaders.has(keyLower)) {\n continue;\n }\n\n // Skip security headers that could spoof origin\n if (SECURITY_HEADERS_TO_REMOVE.has(keyLower)) {\n continue;\n }\n\n // Skip host header (will be set by upstream URL)\n if (keyLower === \"host\") {\n continue;\n }\n\n // Recalculate content-length if body was rewritten\n if (keyLower === \"content-length\" && bodyWasRewritten) {\n forwardHeaders[key] = String(Buffer.byteLength(forwardBody));\n continue;\n }\n\n if (value !== undefined) {\n forwardHeaders[key] = value;\n }\n }\n\n // Override accept-encoding to disable compression\n forwardHeaders[\"accept-encoding\"] = \"identity\";\n\n // Set content-length if body was rewritten and original didn't have it\n if (bodyWasRewritten && !forwardHeaders[\"content-length\"]) {\n forwardHeaders[\"content-length\"] = String(Buffer.byteLength(forwardBody));\n }\n\n // Replace authorization for z.ai\n // Always delete authorization when routing to z.ai to prevent OAuth token leakage\n if (target.name === \"zai\") {\n delete forwardHeaders[\"authorization\"];\n if (target.apiKey) {\n forwardHeaders[\"x-api-key\"] = target.apiKey;\n }\n }\n\n return forwardHeaders;\n}\n\n/**\n * Build response headers, removing hop-by-hop headers\n * When transforming, transfer-encoding and content-encoding are removed\n * since we're returning a transformed (uncompressed) response\n */\nfunction buildResponseHeaders(\n proxyHeaders: IncomingMessage[\"headers\"],\n isTransforming: boolean\n): Record<string, string | string[]> {\n const resHeaders: Record<string, string | string[]> = {};\n\n // Get additional hop-by-hop headers from Connection header\n const connectionHeaders = parseConnectionHeader(proxyHeaders[\"connection\"]);\n\n for (const [key, value] of Object.entries(proxyHeaders)) {\n const keyLower = key.toLowerCase();\n\n // Skip hop-by-hop headers (standard ones)\n if (HOP_BY_HOP_HEADERS.has(keyLower)) {\n continue;\n }\n\n // Skip headers specified in Connection header\n if (connectionHeaders.has(keyLower)) {\n continue;\n }\n\n // When transforming, remove transfer-encoding and content-encoding\n // since we're returning a new (uncompressed) response\n if (isTransforming) {\n if (keyLower === \"transfer-encoding\" || keyLower === \"content-encoding\") {\n continue;\n }\n }\n\n if (value !== undefined) {\n resHeaders[key] = value;\n }\n }\n\n return resHeaders;\n}\n\n/** Export for standalone usage */\nexport async function startProxy(config: Config): Promise<Server> {\n return createProxyServer(config);\n}\n\n// Start proxy if run directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n loadConfig()\n .then((config) => createProxyServer(config))\n .catch((err) => {\n console.error(\"Failed to start proxy:\", err);\n process.exit(1);\n });\n}\n","/**\n * Model-based routing logic\n * Routes requests to upstream based on config routing rules\n */\n\nimport type { Config } from \"../config/types.js\";\nimport type { Route } from \"./types.js\";\n\n// Valid upstream names\nconst VALID_UPSTREAMS = new Set([\"anthropic\", \"zai\"]);\n\n/**\n * Convert a glob-style pattern to a RegExp\n * Only supports `*` as wildcard (matches any characters)\n */\nfunction globToRegExp(pattern: string): RegExp {\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const regexStr = \"^\" + escaped.replace(/\\*/g, \".*\") + \"$\";\n return new RegExp(regexStr);\n}\n\n/**\n * Validate upstream name\n */\nexport function isValidUpstream(name: string): boolean {\n return VALID_UPSTREAMS.has(name);\n}\n\n/**\n * Select upstream route based on model name and config routing rules\n * Rules are evaluated top-to-bottom, first match wins\n *\n * If model is undefined, only rules with match=\"*\" will be applied.\n * Otherwise, falls back to default upstream.\n */\nexport function selectRoute(model: string | undefined, config: Config): Route {\n const modelToMatch = model ?? \"\";\n\n for (const rule of config.routing.rules) {\n // Validate upstream name at runtime\n if (!isValidUpstream(rule.upstream)) {\n console.warn(`Invalid upstream name in routing rule: ${rule.upstream}`);\n continue;\n }\n\n // Try to match the pattern against the model (or empty string for model-less requests)\n if (globToRegExp(rule.match).test(modelToMatch)) {\n if (rule.upstream === \"anthropic\") {\n return {\n name: \"anthropic\",\n url: config.upstream.anthropic.url,\n model: rule.model,\n };\n } else {\n // zai\n return {\n name: \"zai\",\n url: config.upstream.zai.url,\n apiKey: config.upstream.zai.apiKey,\n model: rule.model,\n };\n }\n }\n }\n\n // Fall back to default upstream\n const defaultName = config.routing.default;\n\n // Validate default upstream name\n if (!isValidUpstream(defaultName)) {\n console.warn(`Invalid default upstream name: ${defaultName}, falling back to anthropic`);\n return {\n name: \"anthropic\",\n url: config.upstream.anthropic.url,\n };\n }\n\n if (defaultName === \"anthropic\") {\n return {\n name: \"anthropic\",\n url: config.upstream.anthropic.url,\n };\n } else {\n return {\n name: \"zai\",\n url: config.upstream.zai.url,\n apiKey: config.upstream.zai.apiKey,\n };\n }\n}\n\n/**\n * Parse model name from request body\n * @param body - Request body as buffer\n * @returns Parsed body with model field, or null if parsing fails\n */\nexport function parseRequestBody(body: Buffer): { model?: string } | null {\n try {\n const parsed = JSON.parse(body.toString()) as { model?: string };\n return parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse request body and validate it's an object\n * Returns null if body is not an object or is null/array\n */\nexport function parseRequestBodyAsObject(body: Buffer): Record<string, unknown> | null {\n try {\n const parsed = JSON.parse(body.toString());\n // Ensure parsed value is a non-null object and not an array\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * Configuration loader for cc-glm\n * Handles YAML parsing, environment variable expansion, and validation\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type {\n Config,\n ProxyConfig,\n UpstreamConfig,\n LifecycleConfig,\n LoggingConfig,\n RoutingConfig,\n SignatureStoreConfig,\n ClaudeConfig,\n RawConfig,\n} from \"./types.js\";\n\n// Valid upstream names\nconst VALID_UPSTREAMS = new Set([\"anthropic\", \"zai\"]);\n\n// Valid log levels\nconst VALID_LOG_LEVELS = new Set([\"debug\", \"info\", \"warn\", \"error\"]);\n\n/** Default configuration values */\nconst DEFAULTS: Config = {\n proxy: {\n port: 8787,\n host: \"127.0.0.1\",\n },\n upstream: {\n anthropic: {\n url: \"https://api.anthropic.com\",\n },\n zai: {\n url: \"https://api.z.ai/api/anthropic\",\n apiKey: \"\",\n },\n },\n lifecycle: {\n stopGraceSeconds: 8,\n startWaitSeconds: 8,\n stateDir: `${process.env.TMPDIR ?? \"/tmp\"}/claude-code-proxy`,\n },\n logging: {\n level: \"info\",\n },\n routing: {\n rules: [],\n default: \"anthropic\",\n },\n claude: { path: \"\" },\n};\n\n/**\n * Expand environment variables in a string\n * Supports ${VAR} and ${VAR:-default} syntax\n */\nfunction expandEnvVars(str: string): string {\n if (typeof str !== \"string\") return str;\n return str.replace(/\\$\\{([^}:]+)(:-([^}]*))?\\}/g, (_, name, _defaultValue, defaultValue) => {\n return process.env[name] ?? defaultValue ?? \"\";\n });\n}\n\n/**\n * Get the default configuration file path\n */\nexport function getDefaultConfigPath(): string {\n const home = process.env.HOME ?? \"\";\n return join(home, \".config\", \"cc-glm\", \"config.yml\");\n}\n\n/**\n * Load configuration from a YAML file\n */\nexport async function loadConfig(filePath: string = getDefaultConfigPath()): Promise<Config> {\n let raw: RawConfig = {};\n\n // Load from file if exists\n if (existsSync(filePath)) {\n try {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = parseYaml(content) as RawConfig;\n if (parsed && typeof parsed === \"object\") {\n raw = parsed;\n }\n } catch (error) {\n throw new Error(`Failed to parse config file at ${filePath}: ${error}`);\n }\n }\n\n // Merge with defaults and validate\n return mergeAndValidateConfig(raw);\n}\n\n/**\n * Merge raw config with defaults, validate, and apply environment variable expansion\n */\nfunction mergeAndValidateConfig(raw: RawConfig): Config {\n const config: Config = {\n proxy: mergeProxyConfig(raw.proxy),\n upstream: mergeUpstreamConfig(raw.upstream),\n lifecycle: mergeLifecycleConfig(raw.lifecycle),\n logging: mergeLoggingConfig(raw.logging),\n routing: mergeRoutingConfig(raw.routing),\n signatureStore: mergeSignatureStoreConfig(raw.signature_store),\n claude: mergeClaudeConfig(raw.claude),\n };\n\n // Validate configuration\n validateConfig(config);\n\n return config;\n}\n\nfunction mergeProxyConfig(raw?: Partial<ProxyConfig>): ProxyConfig {\n const rawPort = raw?.port;\n\n // Validate port is a valid number\n let port: number;\n if (typeof rawPort === \"number\" && Number.isFinite(rawPort) && !isNaN(rawPort)) {\n port = rawPort;\n } else {\n port = DEFAULTS.proxy.port;\n }\n\n // Validate port is in valid range and is an integer\n if (!Number.isInteger(port)) {\n throw new Error(`Invalid port: ${port}. Must be an integer.`);\n }\n if (port < 1 || port > 65535) {\n throw new Error(`Invalid port: ${port}. Must be between 1 and 65535.`);\n }\n\n // Validate host is a string\n const rawHost = raw?.host;\n const host = typeof rawHost === \"string\" ? rawHost : DEFAULTS.proxy.host;\n if (typeof host !== \"string\" || host.length === 0) {\n throw new Error(`Invalid host: must be a non-empty string.`);\n }\n\n return {\n host,\n port,\n };\n}\n\nfunction mergeUpstreamConfig(raw?: Partial<UpstreamConfig>): UpstreamConfig {\n const rawAnthropic = raw?.anthropic;\n const rawZai = raw?.zai;\n\n return {\n anthropic: {\n url: rawAnthropic?.url ? expandEnvVars(rawAnthropic.url) : DEFAULTS.upstream.anthropic.url,\n },\n zai: {\n url: rawZai?.url ? expandEnvVars(rawZai.url) : DEFAULTS.upstream.zai.url,\n apiKey:\n expandEnvVars(rawZai?.apiKey ?? \"\") ||\n process.env.ZAI_API_KEY ||\n DEFAULTS.upstream.zai.apiKey,\n },\n };\n}\n\nfunction mergeLifecycleConfig(raw?: Partial<LifecycleConfig>): LifecycleConfig {\n // Use user-provided stateDir or default\n const stateDir = raw?.stateDir ?? DEFAULTS.lifecycle.stateDir;\n const expandedStateDir = expandEnvVars(typeof stateDir === \"string\" ? stateDir : DEFAULTS.lifecycle.stateDir);\n\n // Only fall back to default if expansion resulted in empty string\n // Otherwise respect the user's configuration\n const finalStateDir = expandedStateDir || `${process.env.TMPDIR || \"/tmp\"}/claude-code-proxy`;\n\n // Validate stateDir is a non-empty string\n if (typeof finalStateDir !== \"string\" || finalStateDir.length === 0) {\n throw new Error(`Invalid stateDir: must be a non-empty string.`);\n }\n\n // Validate numeric values\n const rawStopGrace = raw?.stopGraceSeconds;\n const rawStartWait = raw?.startWaitSeconds;\n\n // Validate stopGraceSeconds\n let stopGraceSeconds: number;\n if (typeof rawStopGrace === \"number\" && Number.isFinite(rawStopGrace) && !isNaN(rawStopGrace)) {\n stopGraceSeconds = rawStopGrace;\n } else {\n stopGraceSeconds = DEFAULTS.lifecycle.stopGraceSeconds;\n }\n\n if (!Number.isInteger(stopGraceSeconds)) {\n throw new Error(`Invalid stopGraceSeconds: ${stopGraceSeconds}. Must be an integer.`);\n }\n if (stopGraceSeconds < 0 || stopGraceSeconds > 300) {\n throw new Error(`Invalid stopGraceSeconds: ${stopGraceSeconds}. Must be between 0 and 300.`);\n }\n\n // Validate startWaitSeconds\n let startWaitSeconds: number;\n if (typeof rawStartWait === \"number\" && Number.isFinite(rawStartWait) && !isNaN(rawStartWait)) {\n startWaitSeconds = rawStartWait;\n } else {\n startWaitSeconds = DEFAULTS.lifecycle.startWaitSeconds;\n }\n\n if (!Number.isInteger(startWaitSeconds)) {\n throw new Error(`Invalid startWaitSeconds: ${startWaitSeconds}. Must be an integer.`);\n }\n if (startWaitSeconds < 1 || startWaitSeconds > 60) {\n throw new Error(`Invalid startWaitSeconds: ${startWaitSeconds}. Must be between 1 and 60.`);\n }\n\n return {\n stopGraceSeconds,\n startWaitSeconds,\n stateDir: finalStateDir,\n };\n}\n\nfunction mergeLoggingConfig(raw?: Partial<LoggingConfig>): LoggingConfig {\n const level = raw?.level ?? DEFAULTS.logging.level;\n\n // Validate log level\n if (!VALID_LOG_LEVELS.has(level)) {\n throw new Error(`Invalid logging level: ${level}. Must be one of: ${Array.from(VALID_LOG_LEVELS).join(\", \")}`);\n }\n\n return {\n level,\n };\n}\n\nfunction mergeRoutingConfig(raw?: Partial<RoutingConfig>): RoutingConfig {\n const rules = raw?.rules ?? DEFAULTS.routing.rules;\n const defaultUpstream = raw?.default ?? DEFAULTS.routing.default;\n\n // Validate rules is an array\n if (!Array.isArray(rules)) {\n throw new Error(`Invalid routing.rules: must be an array, got ${typeof rules}`);\n }\n\n // Validate each rule\n for (let i = 0; i < rules.length; i++) {\n const rule = rules[i];\n if (!rule || typeof rule !== \"object\") {\n throw new Error(`Invalid routing rule at index ${i}: must be an object`);\n }\n if (typeof rule.match !== \"string\") {\n throw new Error(`Invalid routing rule at index ${i}: match must be a string`);\n }\n if (typeof rule.upstream !== \"string\") {\n throw new Error(`Invalid routing rule at index ${i}: upstream must be a string`);\n }\n if (!VALID_UPSTREAMS.has(rule.upstream)) {\n throw new Error(\n `Invalid routing rule at index ${i}: upstream \"${rule.upstream}\" is not valid. Must be one of: ${Array.from(VALID_UPSTREAMS).join(\", \")}`\n );\n }\n if (rule.model !== undefined && typeof rule.model !== \"string\") {\n throw new Error(`Invalid routing rule at index ${i}: model must be a string if provided`);\n }\n }\n\n // Validate default upstream\n if (!VALID_UPSTREAMS.has(defaultUpstream)) {\n throw new Error(\n `Invalid routing.default: \"${defaultUpstream}\" is not valid. Must be one of: ${Array.from(VALID_UPSTREAMS).join(\", \")}`\n );\n }\n\n return {\n rules,\n default: defaultUpstream,\n };\n}\n\nfunction mergeSignatureStoreConfig(raw?: Partial<SignatureStoreConfig>): SignatureStoreConfig {\n const DEFAULT_MAX_SIZE = 1000;\n const rawMaxSize = raw?.maxSize;\n\n let maxSize: number;\n if (typeof rawMaxSize === \"number\" && Number.isFinite(rawMaxSize) && !isNaN(rawMaxSize)) {\n maxSize = rawMaxSize;\n } else {\n maxSize = DEFAULT_MAX_SIZE;\n }\n\n if (!Number.isInteger(maxSize)) {\n throw new Error(`Invalid signatureStore.maxSize: ${maxSize}. Must be an integer.`);\n }\n if (maxSize < 1 || maxSize > 100000) {\n throw new Error(`Invalid signatureStore.maxSize: ${maxSize}. Must be between 1 and 100000.`);\n }\n\n return { maxSize };\n}\n\nfunction mergeClaudeConfig(raw?: Partial<ClaudeConfig>): ClaudeConfig {\n const path = raw?.path ?? DEFAULTS.claude.path;\n if (typeof path !== \"string\") {\n throw new Error(`Invalid claude.path: must be a string`);\n }\n return { path };\n}\n\n/**\n * Validate the complete configuration\n */\nfunction validateConfig(config: Config): void {\n // Validate URLs\n try {\n new URL(config.upstream.anthropic.url);\n } catch {\n throw new Error(`Invalid anthropic URL: ${config.upstream.anthropic.url}`);\n }\n\n try {\n new URL(config.upstream.zai.url);\n } catch {\n throw new Error(`Invalid zai URL: ${config.upstream.zai.url}`);\n }\n\n // Warn if zai API key is empty\n if (!config.upstream.zai.apiKey) {\n console.warn(\"Warning: zai API key is not set. Requests to z.ai will fail without ZAI_API_KEY.\");\n }\n}\n","/**\n * Response transformation utilities\n * Handles conversion between different API response formats\n */\n\nimport type { ContentBlock, MessageRequestBody, Message } from \"./types.js\";\nimport type { SignatureStore } from \"./signature-store.js\";\n\n/**\n * Sanitize request body content blocks for Anthropic API\n * Removes z.ai specific fields from thinking blocks in message history\n */\nexport function sanitizeContentBlocks(requestBody: string): string {\n try {\n const parsed = JSON.parse(requestBody) as MessageRequestBody;\n\n // Check if we have messages array to process\n if (!parsed.messages || !Array.isArray(parsed.messages)) {\n return requestBody;\n }\n\n let sanitized = false;\n\n // Process each message\n const newMessages = parsed.messages.map((msg) => {\n const result = sanitizeMessage(msg);\n if (result !== msg) {\n sanitized = true;\n }\n return result;\n });\n\n if (sanitized) {\n parsed.messages = newMessages;\n return JSON.stringify(parsed);\n }\n\n return requestBody;\n } catch {\n // Not JSON or parse error, return as-is\n return requestBody;\n }\n}\n\n/**\n * Sanitize a single message by processing its content blocks\n */\nfunction sanitizeMessage(message: Message): Message {\n // If content is a string, no processing needed\n if (typeof message.content === \"string\") {\n return message;\n }\n\n // If content is not an array, return as-is\n if (!Array.isArray(message.content)) {\n return message;\n }\n\n // Create a shallow copy to detect changes\n let wasModified = false;\n const newContent: ContentBlock[] = [];\n\n for (const block of message.content) {\n const sanitized = sanitizeContentBlock(block);\n if (sanitized !== block) {\n wasModified = true;\n }\n newContent.push(sanitized);\n }\n\n if (wasModified) {\n return { ...message, content: newContent };\n }\n\n return message;\n}\n\n/**\n * Sanitize a single content block\n */\nfunction sanitizeContentBlock(block: ContentBlock): ContentBlock {\n // Handle thinking blocks - remove z.ai specific fields\n if (block.type === \"thinking\") {\n return sanitizeThinkingBlock(block);\n }\n\n // Handle tool_result blocks which may contain nested content\n if (block.type === \"tool_result\") {\n const content = block.content;\n if (Array.isArray(content)) {\n let wasModified = false;\n const newContent: ContentBlock[] = [];\n\n for (const nestedBlock of content) {\n const sanitized = sanitizeContentBlock(nestedBlock);\n if (sanitized !== nestedBlock) {\n wasModified = true;\n }\n newContent.push(sanitized);\n }\n\n if (wasModified) {\n return { ...block, content: newContent };\n }\n }\n }\n\n return block;\n}\n\n/**\n * Sanitize a thinking block by removing z.ai specific fields\n * Handles thinking field as string or object, converting to content field\n * Anthropic API expects: { type: \"thinking\", content: \"...\" }\n *\n * Handles nested structures like:\n * - { type: \"thinking\", thinking: \"...\" }\n * - { type: \"thinking\", thinking: { text: \"...\", signature: \"...\" } }\n * - { type: \"thinking\", thinking: { thinking: \"...\", signature: \"...\" } }\n * - { type: \"thinking\", content: \"...\", thinking: { thinking: \"...\", signature: \"...\" } }\n */\nfunction sanitizeThinkingBlock(block: ContentBlock): ContentBlock {\n const newBlock: ContentBlock = { type: \"thinking\" };\n\n // Step 1: Extract content from thinking field first (before copying other fields)\n let contentFromThinking: string | null = null;\n\n if (typeof block.thinking === \"string\") {\n contentFromThinking = block.thinking;\n } else if (typeof block.thinking === \"object\" && block.thinking !== null) {\n const thinkingObj = block.thinking as Record<string, unknown>;\n // Try multiple possible properties in order of preference\n if (typeof thinkingObj.content === \"string\") {\n contentFromThinking = thinkingObj.content;\n } else if (typeof thinkingObj.thinking === \"string\") {\n contentFromThinking = thinkingObj.thinking;\n } else if (typeof thinkingObj.text === \"string\") {\n contentFromThinking = thinkingObj.text;\n } else {\n contentFromThinking = JSON.stringify(thinkingObj);\n }\n }\n\n // Step 2: Copy only safe fields (whitelist approach)\n // Only copy fields that are safe for Anthropic API\n const SAFE_THINKING_FIELDS = [\"content\", \"cache_control\"];\n for (const [key, value] of Object.entries(block)) {\n if (SAFE_THINKING_FIELDS.includes(key)) {\n newBlock[key] = value;\n }\n }\n\n // Step 3: Always use content from thinking field if available (overwrites existing content)\n // This ensures we always use the latest thinking content, not cached old content\n if (contentFromThinking !== null) {\n newBlock.content = contentFromThinking;\n }\n\n // Step 4: Ensure content field exists (required by Anthropic API)\n if (!newBlock.content) {\n newBlock.content = \"\";\n }\n\n // Step 5: Remove invalid fields (ensure thinking and signature are always removed)\n delete newBlock.signature;\n delete newBlock.thinking;\n\n return newBlock;\n}\n\n/**\n * Check if a request should be transformed\n * Only transform requests to Anthropic upstream\n */\nexport function shouldTransformRequest(\n contentType: string | undefined,\n upstream: string\n): boolean {\n if (upstream !== \"anthropic\") return false;\n if (!contentType) return false;\n return contentType.includes(\"application/json\");\n}\n\n/**\n * Extract and record signatures from Anthropic response\n * Processes thinking blocks in the response and stores their signatures\n * for later identification of Anthropic-generated content\n *\n * @param responseBody - The response body string from Anthropic\n * @param store - The SignatureStore to record signatures in\n * @returns The original response body (unchanged)\n */\nexport function extractAndRecordSignatures(\n responseBody: string,\n store: SignatureStore\n): string {\n try {\n const parsed = JSON.parse(responseBody) as AnthropicMessageResponse;\n\n // Handle responses with content array\n if (Array.isArray(parsed.content)) {\n for (const block of parsed.content) {\n if (block.type === \"thinking\" && typeof block.signature === \"string\" && block.signature) {\n // Record the signature from Anthropic's thinking block\n store.add(block.signature);\n }\n }\n }\n\n return responseBody;\n } catch {\n // Not JSON or parse error, return as-is\n return responseBody;\n }\n}\n\n/**\n * Sanitize request body content blocks for Anthropic API with signature checking\n * Converts thinking blocks with unrecorded signatures (z.ai origin) to text blocks\n * Preserves thinking blocks with recorded signatures (Anthropic origin)\n *\n * @param requestBody - The request body string\n * @param store - The SignatureStore to check signatures against\n * @returns The sanitized request body string\n */\nexport function sanitizeContentBlocksWithStore(\n requestBody: string,\n store: SignatureStore\n): string {\n try {\n const parsed = JSON.parse(requestBody) as MessageRequestBody;\n\n // Check if we have messages array to process\n if (!parsed.messages || !Array.isArray(parsed.messages)) {\n return requestBody;\n }\n\n let sanitized = false;\n\n // Process each message\n const newMessages = parsed.messages.map((msg) => {\n const result = sanitizeMessageWithStore(msg, store);\n if (result !== msg) {\n sanitized = true;\n }\n return result;\n });\n\n if (sanitized) {\n parsed.messages = newMessages;\n return JSON.stringify(parsed);\n }\n\n return requestBody;\n } catch {\n // Not JSON or parse error, return as-is\n return requestBody;\n }\n}\n\n/**\n * Sanitize a single message with signature checking\n * Converts unrecorded thinking blocks to text blocks\n */\nfunction sanitizeMessageWithStore(message: Message, store: SignatureStore): Message {\n // If content is a string, no processing needed\n if (typeof message.content === \"string\") {\n return message;\n }\n\n // If content is not an array, return as-is\n if (!Array.isArray(message.content)) {\n return message;\n }\n\n // Create a shallow copy to detect changes\n let wasModified = false;\n const newContent: ContentBlock[] = [];\n\n for (const block of message.content) {\n const sanitized = sanitizeContentBlockWithStore(block, store);\n if (sanitized !== block) {\n wasModified = true;\n }\n newContent.push(sanitized);\n }\n\n if (wasModified) {\n return { ...message, content: newContent };\n }\n\n return message;\n}\n\n/**\n * Sanitize a single content block with signature checking\n * Converts thinking blocks with unrecorded signatures to text blocks\n */\nfunction sanitizeContentBlockWithStore(\n block: ContentBlock,\n store: SignatureStore\n): ContentBlock {\n // Handle thinking blocks - check signature\n if (block.type === \"thinking\") {\n const signature = block.signature;\n\n // Check if signature is recorded (Anthropic origin)\n if (typeof signature === \"string\" && signature && store.has(signature)) {\n // This is an Anthropic-generated thinking block, return as-is\n // Don't modify anything - Anthropic needs to verify the signature\n return block;\n } else {\n // This is a z.ai-origin thinking block, convert to text\n return convertThinkingToText(block);\n }\n }\n\n // Handle tool_result blocks which may contain nested content\n if (block.type === \"tool_result\") {\n const content = block.content;\n if (Array.isArray(content)) {\n let wasModified = false;\n const newContent: ContentBlock[] = [];\n\n for (const nestedBlock of content) {\n const sanitized = sanitizeContentBlockWithStore(nestedBlock, store);\n if (sanitized !== nestedBlock) {\n wasModified = true;\n }\n newContent.push(sanitized);\n }\n\n if (wasModified) {\n return { ...block, content: newContent };\n }\n }\n }\n\n return block;\n}\n\n/**\n * Convert a thinking block to a text block\n * Extracts the thinking content and wraps it in a text block with a prefix\n */\nfunction convertThinkingToText(block: ContentBlock): ContentBlock {\n // Extract thinking content\n let thinkingText = \"\";\n\n if (typeof block.thinking === \"string\") {\n thinkingText = block.thinking;\n } else if (typeof block.content === \"string\") {\n thinkingText = block.content;\n } else if (typeof block.thinking === \"object\" && block.thinking !== null) {\n const thinkingObj = block.thinking as Record<string, unknown>;\n if (typeof thinkingObj.content === \"string\") {\n thinkingText = thinkingObj.content;\n } else if (typeof thinkingObj.thinking === \"string\") {\n thinkingText = thinkingObj.thinking;\n } else if (typeof thinkingObj.text === \"string\") {\n thinkingText = thinkingObj.text;\n } else {\n thinkingText = JSON.stringify(thinkingObj);\n }\n } else if (typeof block.content === \"object\" && block.content !== null) {\n const contentObj = block.content as Record<string, unknown>;\n if (typeof contentObj.text === \"string\") {\n thinkingText = contentObj.text;\n } else {\n thinkingText = JSON.stringify(contentObj);\n }\n }\n\n // Create text block with XML tag wrapper\n return {\n type: \"text\",\n text: `<previous-glm-reasoning>\\n${thinkingText}\\n</previous-glm-reasoning>`,\n };\n}\n\ninterface AnthropicMessageResponse {\n id?: string;\n type?: string;\n role?: string;\n content: ContentBlock[] | string;\n model?: string;\n stop_reason?: string;\n usage?: {\n input_tokens: number;\n output_tokens: number;\n };\n [key: string]: unknown;\n}\n\n/**\n * Transform z.ai thinking blocks to Anthropic-compatible format\n * Handles invalid signature fields and format differences\n *\n * Handles various z.ai thinking block formats:\n * - { type: \"thinking\", thinking: \"...\" }\n * - { type: \"thinking\", thinking: { text: \"...\", signature: \"...\" } }\n * - { type: \"thinking\", thinking: { thinking: \"...\", signature: \"...\" } }\n */\nexport function transformThinkingBlocks(response: string): string {\n try {\n const parsed = JSON.parse(response) as AnthropicMessageResponse;\n\n // Handle responses with content array\n if (Array.isArray(parsed.content)) {\n let transformed = false;\n const newContent: ContentBlock[] = [];\n\n for (const block of parsed.content) {\n if (block.type === \"thinking\") {\n // Transform thinking block to Anthropic-compatible format\n const newBlock: ContentBlock = { type: \"thinking\" };\n\n // Copy safe fields - prioritize existing content field\n if (typeof block.content === \"string\") {\n newBlock.content = block.content;\n } else if (typeof block.thinking === \"string\") {\n newBlock.content = block.thinking;\n } else if (typeof block.thinking === \"object\" && block.thinking !== null) {\n // thinking is an object - extract from nested properties\n const thinkingObj = block.thinking as Record<string, unknown>;\n // Try multiple possible properties in order of preference\n if (typeof thinkingObj.content === \"string\") {\n newBlock.content = thinkingObj.content;\n } else if (typeof thinkingObj.thinking === \"string\") {\n // Handle nested thinking.thinking structure\n newBlock.content = thinkingObj.thinking;\n } else if (typeof thinkingObj.text === \"string\") {\n newBlock.content = thinkingObj.text;\n } else {\n // Stringify the object as fallback\n newBlock.content = JSON.stringify(thinkingObj);\n }\n } else {\n // No content found, use empty string\n newBlock.content = \"\";\n }\n\n // Only include valid Anthropic fields\n // Anthropic expects: { type: \"thinking\", content: \"...\" }\n // Remove z.ai specific fields like invalid signature\n newContent.push(newBlock);\n transformed = true;\n } else {\n // Keep non-thinking blocks as-is\n newContent.push(block);\n }\n }\n\n if (transformed) {\n parsed.content = newContent;\n return JSON.stringify(parsed);\n }\n }\n\n // Handle responses with content as text (unlikely for messages API)\n if (typeof parsed.content === \"string\") {\n // Remove thinking tags if present in text content\n let content = parsed.content;\n // Remove <thinking>...</thinking> tags with any attributes and surrounding whitespace\n content = content.replace(/\\s*<thinking[^>]*>[\\s\\S]*?<\\/thinking>\\s*/gi, \"\");\n content = content.replace(/\\s*<thinking[^>]*>[\\s\\S]*?$/gi, \"\"); // unclosed tag\n // Trim leading/trailing whitespace after removal\n content = content.trim();\n if (content !== parsed.content) {\n parsed.content = content;\n return JSON.stringify(parsed);\n }\n }\n\n return response;\n } catch {\n // Not JSON or parse error, return as-is\n return response;\n }\n}\n\n/**\n * Check if a response should be transformed\n * Only transform responses from z.ai upstream\n */\nexport function shouldTransformResponse(\n contentType: string | undefined,\n upstream: string\n): boolean {\n if (upstream !== \"zai\") return false;\n if (!contentType) return false;\n return contentType.includes(\"application/json\");\n}\n","/**\n * Signature Store for tracking Anthropic thinking block signatures\n * Uses Map insertion order for O(1) LRU eviction\n */\n\n/** Default maximum number of signatures to store */\nconst DEFAULT_MAX_SIZE = 1000;\n\n/**\n * Signature Store using Map-based LRU cache\n * Tracks signatures from Anthropic thinking blocks to distinguish them from z.ai blocks\n *\n * Uses Map's insertion order guarantee: iterating a Map yields entries in insertion order.\n * By deleting and re-setting an entry on access, the entry moves to the end (most recent).\n * The first entry in iteration order is always the least recently used.\n */\nexport class SignatureStore {\n private cache: Map<string, true>;\n private maxSize: number;\n\n /**\n * Create a new SignatureStore\n * @param maxSize - Maximum number of signatures to store (default: 1000, min: 1)\n */\n constructor(maxSize: number = DEFAULT_MAX_SIZE) {\n if (!Number.isInteger(maxSize) || maxSize < 1) {\n this.maxSize = DEFAULT_MAX_SIZE;\n } else {\n this.maxSize = maxSize;\n }\n this.cache = new Map();\n }\n\n /**\n * Add a signature to the store\n * If the store is full, removes the least recently used entry\n * @param signature - The signature string to store\n */\n add(signature: string): void {\n if (typeof signature !== \"string\" || signature === \"\") {\n return;\n }\n\n // Delete first to update insertion order (move to end)\n if (this.cache.has(signature)) {\n this.cache.delete(signature);\n } else if (this.cache.size >= this.maxSize) {\n // Remove least recently used entry (first in Map iteration order)\n const lruKey = this.cache.keys().next().value;\n if (lruKey !== undefined) {\n this.cache.delete(lruKey);\n }\n }\n\n this.cache.set(signature, true);\n }\n\n /**\n * Check if a signature exists in the store\n * Marks the signature as recently used if found\n * @param signature - The signature string to check\n * @returns true if the signature is in the store\n */\n has(signature: string): boolean {\n if (typeof signature !== \"string\" || signature === \"\") {\n return false;\n }\n\n if (this.cache.has(signature)) {\n // Move to end of Map (most recently used)\n this.cache.delete(signature);\n this.cache.set(signature, true);\n return true;\n }\n return false;\n }\n\n /**\n * Get the current number of signatures in the store\n * @returns The number of stored signatures\n */\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * Clear all signatures from the store\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get all stored signatures (for testing/debugging)\n * @returns Array of all signatures in the store\n */\n getAllSignatures(): string[] {\n return Array.from(this.cache.keys());\n }\n}\n"],"mappings":"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","names":["VALID_UPSTREAMS","DEFAULT_MAX_SIZE"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@shunirr/cc-glm",
3
+ "version": "0.1.0",
4
+ "description": "Claude Code proxy for switching between Anthropic API and z.ai GLM",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "cc-glm": "dist/bin/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch",
17
+ "test": "vitest",
18
+ "test:run": "vitest run",
19
+ "lint": "tsc --noEmit"
20
+ },
21
+ "keywords": [
22
+ "claude",
23
+ "claude-code",
24
+ "proxy",
25
+ "anthropic",
26
+ "glm",
27
+ "z-ai"
28
+ ],
29
+ "author": "shunirr",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/shunirr/cc-glm.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/shunirr/cc-glm/issues"
37
+ },
38
+ "homepage": "https://github.com/shunirr/cc-glm#readme",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "dependencies": {
43
+ "yaml": "^2.5.0",
44
+ "chalk": "^5.3.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^20.11.0",
48
+ "typescript": "^5.3.0",
49
+ "tsup": "^8.0.0",
50
+ "vitest": "^1.2.0"
51
+ },
52
+ "engines": {
53
+ "node": ">=18.0.0"
54
+ }
55
+ }