@kaitranntt/ccs 4.4.0 → 5.0.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 (262) hide show
  1. package/README.md +14 -7
  2. package/VERSION +1 -1
  3. package/config/base-agy.settings.json +10 -0
  4. package/config/base-codex.settings.json +10 -0
  5. package/config/base-gemini.settings.json +10 -0
  6. package/dist/auth/auth-commands.d.ts +52 -0
  7. package/dist/auth/auth-commands.d.ts.map +1 -0
  8. package/dist/auth/auth-commands.js +479 -0
  9. package/dist/auth/auth-commands.js.map +1 -0
  10. package/dist/auth/profile-detector.d.ts +68 -0
  11. package/dist/auth/profile-detector.d.ts.map +1 -0
  12. package/dist/auth/profile-detector.js +209 -0
  13. package/dist/auth/profile-detector.js.map +1 -0
  14. package/dist/auth/profile-registry.d.ts +60 -0
  15. package/dist/auth/profile-registry.d.ts.map +1 -0
  16. package/dist/auth/profile-registry.js +188 -0
  17. package/dist/auth/profile-registry.js.map +1 -0
  18. package/dist/ccs.d.ts +10 -0
  19. package/dist/ccs.d.ts.map +1 -0
  20. package/dist/ccs.js +320 -0
  21. package/dist/ccs.js.map +1 -0
  22. package/dist/cliproxy/auth-handler.d.ts +93 -0
  23. package/dist/cliproxy/auth-handler.d.ts.map +1 -0
  24. package/dist/cliproxy/auth-handler.js +402 -0
  25. package/dist/cliproxy/auth-handler.js.map +1 -0
  26. package/dist/cliproxy/base-config-loader.d.ts +42 -0
  27. package/dist/cliproxy/base-config-loader.d.ts.map +1 -0
  28. package/dist/cliproxy/base-config-loader.js +123 -0
  29. package/dist/cliproxy/base-config-loader.js.map +1 -0
  30. package/dist/cliproxy/binary-manager.d.ts +104 -0
  31. package/dist/cliproxy/binary-manager.d.ts.map +1 -0
  32. package/dist/cliproxy/binary-manager.js +567 -0
  33. package/dist/cliproxy/binary-manager.js.map +1 -0
  34. package/dist/cliproxy/cliproxy-executor.d.ts +33 -0
  35. package/dist/cliproxy/cliproxy-executor.d.ts.map +1 -0
  36. package/dist/cliproxy/cliproxy-executor.js +297 -0
  37. package/dist/cliproxy/cliproxy-executor.js.map +1 -0
  38. package/dist/cliproxy/config-generator.d.ts +89 -0
  39. package/dist/cliproxy/config-generator.d.ts.map +1 -0
  40. package/dist/cliproxy/config-generator.js +263 -0
  41. package/dist/cliproxy/config-generator.js.map +1 -0
  42. package/dist/cliproxy/index.d.ts +13 -0
  43. package/dist/cliproxy/index.d.ts.map +1 -0
  44. package/dist/cliproxy/index.js +62 -0
  45. package/dist/cliproxy/index.js.map +1 -0
  46. package/dist/cliproxy/platform-detector.d.ts +48 -0
  47. package/dist/cliproxy/platform-detector.d.ts.map +1 -0
  48. package/dist/cliproxy/platform-detector.js +118 -0
  49. package/dist/cliproxy/platform-detector.js.map +1 -0
  50. package/dist/cliproxy/types.d.ts +169 -0
  51. package/dist/cliproxy/types.d.ts.map +1 -0
  52. package/dist/cliproxy/types.js +7 -0
  53. package/dist/cliproxy/types.js.map +1 -0
  54. package/dist/commands/doctor-command.d.ts +10 -0
  55. package/dist/commands/doctor-command.d.ts.map +1 -0
  56. package/dist/commands/doctor-command.js +44 -0
  57. package/dist/commands/doctor-command.js.map +1 -0
  58. package/dist/commands/help-command.d.ts +5 -0
  59. package/dist/commands/help-command.d.ts.map +1 -0
  60. package/dist/commands/help-command.js +104 -0
  61. package/dist/commands/help-command.js.map +1 -0
  62. package/dist/commands/install-command.d.ts +14 -0
  63. package/dist/commands/install-command.d.ts.map +1 -0
  64. package/dist/commands/install-command.js +39 -0
  65. package/dist/commands/install-command.js.map +1 -0
  66. package/dist/commands/shell-completion-command.d.ts +10 -0
  67. package/dist/commands/shell-completion-command.d.ts.map +1 -0
  68. package/dist/commands/shell-completion-command.js +85 -0
  69. package/dist/commands/shell-completion-command.js.map +1 -0
  70. package/dist/commands/sync-command.d.ts +10 -0
  71. package/dist/commands/sync-command.d.ts.map +1 -0
  72. package/dist/commands/sync-command.js +59 -0
  73. package/dist/commands/sync-command.js.map +1 -0
  74. package/dist/commands/update-command.d.ts +12 -0
  75. package/dist/commands/update-command.d.ts.map +1 -0
  76. package/dist/commands/update-command.js +295 -0
  77. package/dist/commands/update-command.js.map +1 -0
  78. package/dist/commands/version-command.d.ts +10 -0
  79. package/dist/commands/version-command.d.ts.map +1 -0
  80. package/dist/commands/version-command.js +100 -0
  81. package/dist/commands/version-command.js.map +1 -0
  82. package/dist/delegation/delegation-handler.d.ts +60 -0
  83. package/dist/delegation/delegation-handler.d.ts.map +1 -0
  84. package/dist/delegation/delegation-handler.js +174 -0
  85. package/dist/delegation/delegation-handler.js.map +1 -0
  86. package/dist/delegation/headless-executor.d.ts +114 -0
  87. package/dist/delegation/headless-executor.d.ts.map +1 -0
  88. package/dist/delegation/headless-executor.js +562 -0
  89. package/dist/delegation/headless-executor.js.map +1 -0
  90. package/dist/delegation/result-formatter.d.ts +108 -0
  91. package/dist/delegation/result-formatter.d.ts.map +1 -0
  92. package/dist/delegation/result-formatter.js +391 -0
  93. package/dist/delegation/result-formatter.js.map +1 -0
  94. package/dist/delegation/session-manager.d.ts +58 -0
  95. package/dist/delegation/session-manager.d.ts.map +1 -0
  96. package/dist/delegation/session-manager.js +153 -0
  97. package/dist/delegation/session-manager.js.map +1 -0
  98. package/dist/delegation/settings-parser.d.ts +31 -0
  99. package/dist/delegation/settings-parser.d.ts.map +1 -0
  100. package/dist/delegation/settings-parser.js +107 -0
  101. package/dist/delegation/settings-parser.js.map +1 -0
  102. package/dist/glmt/delta-accumulator.d.ts +210 -0
  103. package/dist/glmt/delta-accumulator.d.ts.map +1 -0
  104. package/dist/glmt/delta-accumulator.js +351 -0
  105. package/dist/glmt/delta-accumulator.js.map +1 -0
  106. package/dist/glmt/glmt-proxy.d.ts +72 -0
  107. package/dist/glmt/glmt-proxy.d.ts.map +1 -0
  108. package/dist/glmt/glmt-proxy.js +427 -0
  109. package/dist/glmt/glmt-proxy.js.map +1 -0
  110. package/dist/glmt/glmt-transformer.d.ts +265 -0
  111. package/dist/glmt/glmt-transformer.d.ts.map +1 -0
  112. package/dist/glmt/glmt-transformer.js +832 -0
  113. package/dist/glmt/glmt-transformer.js.map +1 -0
  114. package/dist/glmt/locale-enforcer.d.ts +38 -0
  115. package/dist/glmt/locale-enforcer.d.ts.map +1 -0
  116. package/dist/glmt/locale-enforcer.js +69 -0
  117. package/dist/glmt/locale-enforcer.js.map +1 -0
  118. package/dist/glmt/reasoning-enforcer.d.ts +52 -0
  119. package/dist/glmt/reasoning-enforcer.d.ts.map +1 -0
  120. package/dist/glmt/reasoning-enforcer.js +151 -0
  121. package/dist/glmt/reasoning-enforcer.js.map +1 -0
  122. package/dist/glmt/sse-parser.d.ts +47 -0
  123. package/dist/glmt/sse-parser.d.ts.map +1 -0
  124. package/dist/glmt/sse-parser.js +93 -0
  125. package/dist/glmt/sse-parser.js.map +1 -0
  126. package/dist/management/doctor.d.ts +104 -0
  127. package/dist/management/doctor.d.ts.map +1 -0
  128. package/dist/management/doctor.js +673 -0
  129. package/dist/management/doctor.js.map +1 -0
  130. package/dist/management/instance-manager.d.ts +57 -0
  131. package/dist/management/instance-manager.d.ts.map +1 -0
  132. package/dist/management/instance-manager.js +195 -0
  133. package/dist/management/instance-manager.js.map +1 -0
  134. package/dist/management/recovery-manager.d.ts +39 -0
  135. package/dist/management/recovery-manager.d.ts.map +1 -0
  136. package/dist/management/recovery-manager.js +141 -0
  137. package/dist/management/recovery-manager.js.map +1 -0
  138. package/dist/management/shared-manager.d.ts +47 -0
  139. package/dist/management/shared-manager.d.ts.map +1 -0
  140. package/dist/management/shared-manager.js +388 -0
  141. package/dist/management/shared-manager.js.map +1 -0
  142. package/dist/types/cli.d.ts +50 -0
  143. package/dist/types/cli.d.ts.map +1 -0
  144. package/dist/types/cli.js +16 -0
  145. package/dist/types/cli.js.map +1 -0
  146. package/dist/types/config.d.ts +51 -0
  147. package/dist/types/config.d.ts.map +1 -0
  148. package/dist/types/config.js +26 -0
  149. package/dist/types/config.js.map +1 -0
  150. package/dist/types/delegation.d.ts +61 -0
  151. package/dist/types/delegation.d.ts.map +1 -0
  152. package/dist/types/delegation.js +6 -0
  153. package/dist/types/delegation.js.map +1 -0
  154. package/dist/types/glmt.d.ts +95 -0
  155. package/dist/types/glmt.d.ts.map +1 -0
  156. package/dist/types/glmt.js +7 -0
  157. package/dist/types/glmt.js.map +1 -0
  158. package/dist/types/index.d.ts +13 -0
  159. package/dist/types/index.d.ts.map +1 -0
  160. package/dist/types/index.js +16 -0
  161. package/dist/types/index.js.map +1 -0
  162. package/dist/types/utils.d.ts +36 -0
  163. package/dist/types/utils.d.ts.map +1 -0
  164. package/dist/types/utils.js +22 -0
  165. package/dist/types/utils.js.map +1 -0
  166. package/dist/utils/claude-detector.d.ts +14 -0
  167. package/dist/utils/claude-detector.d.ts.map +1 -0
  168. package/dist/utils/claude-detector.js +112 -0
  169. package/dist/utils/claude-detector.js.map +1 -0
  170. package/dist/utils/claude-dir-installer.d.ts +46 -0
  171. package/dist/utils/claude-dir-installer.d.ts.map +1 -0
  172. package/dist/utils/claude-dir-installer.js +289 -0
  173. package/dist/utils/claude-dir-installer.js.map +1 -0
  174. package/dist/utils/claude-symlink-manager.d.ts +61 -0
  175. package/dist/utils/claude-symlink-manager.d.ts.map +1 -0
  176. package/dist/utils/claude-symlink-manager.js +291 -0
  177. package/dist/utils/claude-symlink-manager.js.map +1 -0
  178. package/dist/utils/config-manager.d.ts +32 -0
  179. package/dist/utils/config-manager.d.ts.map +1 -0
  180. package/dist/utils/config-manager.js +143 -0
  181. package/dist/utils/config-manager.js.map +1 -0
  182. package/dist/utils/delegation-validator.d.ts +39 -0
  183. package/dist/utils/delegation-validator.d.ts.map +1 -0
  184. package/dist/utils/delegation-validator.js +161 -0
  185. package/dist/utils/delegation-validator.js.map +1 -0
  186. package/dist/utils/error-codes.d.ts +36 -0
  187. package/dist/utils/error-codes.d.ts.map +1 -0
  188. package/dist/utils/error-codes.js +63 -0
  189. package/dist/utils/error-codes.js.map +1 -0
  190. package/dist/utils/error-manager.d.ts +59 -0
  191. package/dist/utils/error-manager.d.ts.map +1 -0
  192. package/dist/utils/error-manager.js +228 -0
  193. package/dist/utils/error-manager.js.map +1 -0
  194. package/dist/utils/helpers.d.ts +27 -0
  195. package/dist/utils/helpers.d.ts.map +1 -0
  196. package/dist/utils/helpers.js +150 -0
  197. package/dist/utils/helpers.js.map +1 -0
  198. package/dist/utils/package-manager-detector.d.ts +14 -0
  199. package/dist/utils/package-manager-detector.d.ts.map +1 -0
  200. package/dist/utils/package-manager-detector.js +162 -0
  201. package/dist/utils/package-manager-detector.js.map +1 -0
  202. package/dist/utils/progress-indicator.d.ts +52 -0
  203. package/dist/utils/progress-indicator.d.ts.map +1 -0
  204. package/dist/utils/progress-indicator.js +102 -0
  205. package/dist/utils/progress-indicator.js.map +1 -0
  206. package/dist/utils/prompt.d.ts +29 -0
  207. package/dist/utils/prompt.d.ts.map +1 -0
  208. package/dist/utils/prompt.js +116 -0
  209. package/dist/utils/prompt.js.map +1 -0
  210. package/dist/utils/shell-completion.d.ts +52 -0
  211. package/dist/utils/shell-completion.d.ts.map +1 -0
  212. package/dist/utils/shell-completion.js +231 -0
  213. package/dist/utils/shell-completion.js.map +1 -0
  214. package/dist/utils/shell-executor.d.ts +15 -0
  215. package/dist/utils/shell-executor.d.ts.map +1 -0
  216. package/dist/utils/shell-executor.js +57 -0
  217. package/dist/utils/shell-executor.js.map +1 -0
  218. package/dist/utils/update-checker.d.ts +48 -0
  219. package/dist/utils/update-checker.d.ts.map +1 -0
  220. package/dist/utils/update-checker.js +241 -0
  221. package/dist/utils/update-checker.js.map +1 -0
  222. package/lib/ccs +21 -1907
  223. package/lib/ccs.ps1 +26 -1800
  224. package/lib/error-codes.ps1 +2 -1
  225. package/lib/prompt.ps1 +2 -2
  226. package/package.json +31 -11
  227. package/scripts/add-shebang.js +39 -0
  228. package/scripts/bump-version.sh +25 -37
  229. package/scripts/dev-install.sh +32 -11
  230. package/scripts/postinstall.js +29 -29
  231. package/bin/auth/auth-commands.js +0 -499
  232. package/bin/auth/profile-detector.js +0 -204
  233. package/bin/auth/profile-registry.js +0 -225
  234. package/bin/ccs.js +0 -1034
  235. package/bin/delegation/README.md +0 -191
  236. package/bin/delegation/delegation-handler.js +0 -212
  237. package/bin/delegation/headless-executor.js +0 -618
  238. package/bin/delegation/result-formatter.js +0 -485
  239. package/bin/delegation/session-manager.js +0 -157
  240. package/bin/delegation/settings-parser.js +0 -109
  241. package/bin/glmt/delta-accumulator.js +0 -276
  242. package/bin/glmt/glmt-proxy.js +0 -495
  243. package/bin/glmt/glmt-transformer.js +0 -999
  244. package/bin/glmt/locale-enforcer.js +0 -72
  245. package/bin/glmt/reasoning-enforcer.js +0 -173
  246. package/bin/glmt/sse-parser.js +0 -96
  247. package/bin/management/doctor.js +0 -721
  248. package/bin/management/instance-manager.js +0 -202
  249. package/bin/management/recovery-manager.js +0 -135
  250. package/bin/management/shared-manager.js +0 -402
  251. package/bin/utils/claude-detector.js +0 -73
  252. package/bin/utils/claude-dir-installer.js +0 -283
  253. package/bin/utils/claude-symlink-manager.js +0 -289
  254. package/bin/utils/config-manager.js +0 -103
  255. package/bin/utils/delegation-validator.js +0 -154
  256. package/bin/utils/error-codes.js +0 -59
  257. package/bin/utils/error-manager.js +0 -165
  258. package/bin/utils/helpers.js +0 -136
  259. package/bin/utils/progress-indicator.js +0 -111
  260. package/bin/utils/prompt.js +0 -134
  261. package/bin/utils/shell-completion.js +0 -256
  262. package/bin/utils/update-checker.js +0 -243
@@ -1,495 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const http = require('http');
5
- const https = require('https');
6
- const GlmtTransformer = require('./glmt-transformer');
7
- const SSEParser = require('./sse-parser');
8
- const DeltaAccumulator = require('./delta-accumulator');
9
-
10
- /**
11
- * GlmtProxy - Embedded HTTP proxy for GLM thinking support
12
- *
13
- * Architecture:
14
- * - Intercepts Claude CLI → Z.AI calls
15
- * - Transforms Anthropic format → OpenAI format
16
- * - Converts reasoning_content → thinking blocks
17
- * - Supports both streaming and buffered modes
18
- *
19
- * Lifecycle:
20
- * - Spawned by bin/ccs.js when 'glmt' profile detected
21
- * - Binds to 127.0.0.1:random_port (security + avoid conflicts)
22
- * - Terminates when parent process exits
23
- *
24
- * Debugging:
25
- * - Verbose: Pass --verbose to see request/response logs
26
- * - Debug: Set CCS_DEBUG=1 to write logs to ~/.ccs/logs/
27
- *
28
- * Usage:
29
- * const proxy = new GlmtProxy({ verbose: true });
30
- * await proxy.start();
31
- */
32
- class GlmtProxy {
33
- constructor(config = {}) {
34
- this.transformer = new GlmtTransformer({
35
- verbose: config.verbose,
36
- debugLog: config.debugLog || process.env.CCS_DEBUG === '1' || process.env.CCS_DEBUG_LOG === '1'
37
- });
38
- // Use ANTHROPIC_BASE_URL from environment (set by settings.json) or fallback to Z.AI default
39
- this.upstreamUrl = process.env.ANTHROPIC_BASE_URL || 'https://api.z.ai/api/coding/paas/v4/chat/completions';
40
- this.server = null;
41
- this.port = null;
42
- this.verbose = config.verbose || false;
43
- this.timeout = config.timeout || 120000; // 120s default
44
- }
45
-
46
- /**
47
- * Start HTTP server on random port
48
- * @returns {Promise<number>} Port number
49
- */
50
- async start() {
51
- return new Promise((resolve, reject) => {
52
- this.server = http.createServer((req, res) => {
53
- this.handleRequest(req, res);
54
- });
55
-
56
- // Bind to 127.0.0.1:0 (random port for security + avoid conflicts)
57
- this.server.listen(0, '127.0.0.1', () => {
58
- this.port = this.server.address().port;
59
- // Signal parent process
60
- console.log(`PROXY_READY:${this.port}`);
61
-
62
- // Info message (only show in verbose mode)
63
- if (this.verbose) {
64
- console.error(`[glmt] Proxy listening on port ${this.port} (streaming with auto-fallback)`);
65
- }
66
-
67
- // Debug mode notice
68
- if (this.transformer.debugLog) {
69
- console.error(`[glmt] Debug logging enabled: ${this.transformer.debugLogDir}`);
70
- console.error(`[glmt] WARNING: Debug logs contain full request/response data`);
71
- }
72
-
73
- this.log(`Verbose logging enabled`);
74
- resolve(this.port);
75
- });
76
-
77
- this.server.on('error', (error) => {
78
- console.error('[glmt-proxy] Server error:', error);
79
- reject(error);
80
- });
81
- });
82
- }
83
-
84
- /**
85
- * Handle incoming HTTP request
86
- * @param {http.IncomingMessage} req - Request
87
- * @param {http.ServerResponse} res - Response
88
- */
89
- async handleRequest(req, res) {
90
- const startTime = Date.now();
91
- this.log(`Request: ${req.method} ${req.url}`);
92
-
93
- try {
94
- // Only accept POST requests
95
- if (req.method !== 'POST') {
96
- res.writeHead(405, { 'Content-Type': 'application/json' });
97
- res.end(JSON.stringify({ error: 'Method not allowed' }));
98
- return;
99
- }
100
-
101
- // Read request body
102
- const body = await this._readBody(req);
103
- this.log(`Request body size: ${body.length} bytes`);
104
-
105
- // Parse JSON with error handling
106
- let anthropicRequest;
107
- try {
108
- anthropicRequest = JSON.parse(body);
109
- } catch (jsonError) {
110
- res.writeHead(400, { 'Content-Type': 'application/json' });
111
- res.end(JSON.stringify({
112
- error: {
113
- type: 'invalid_request_error',
114
- message: 'Invalid JSON in request body: ' + jsonError.message
115
- }
116
- }));
117
- return;
118
- }
119
-
120
- // Log thinking parameter for debugging
121
- if (anthropicRequest.thinking) {
122
- this.log(`Request contains thinking parameter: ${JSON.stringify(anthropicRequest.thinking)}`);
123
- } else {
124
- this.log(`Request does NOT contain thinking parameter (will use message tags or default)`);
125
- }
126
-
127
- // Try streaming first (default), fallback to buffered on error
128
- const useStreaming = anthropicRequest.stream !== false;
129
-
130
- if (useStreaming) {
131
- try {
132
- await this._handleStreamingRequest(req, res, anthropicRequest, startTime);
133
- } catch (streamError) {
134
- this.log(`Streaming failed: ${streamError.message}, retrying buffered mode`);
135
- try {
136
- await this._handleBufferedRequest(req, res, anthropicRequest, startTime);
137
- } catch (bufferedError) {
138
- // Both modes failed, propagate error
139
- throw bufferedError;
140
- }
141
- }
142
- } else {
143
- await this._handleBufferedRequest(req, res, anthropicRequest, startTime);
144
- }
145
-
146
- } catch (error) {
147
- console.error('[glmt-proxy] Request error:', error.message);
148
- const duration = Date.now() - startTime;
149
- this.log(`Request failed after ${duration}ms: ${error.message}`);
150
-
151
- res.writeHead(500, { 'Content-Type': 'application/json' });
152
- res.end(JSON.stringify({
153
- error: {
154
- type: 'proxy_error',
155
- message: error.message
156
- }
157
- }));
158
- }
159
- }
160
-
161
- /**
162
- * Handle buffered (non-streaming) request
163
- * @private
164
- */
165
- async _handleBufferedRequest(req, res, anthropicRequest, startTime) {
166
- // Transform to OpenAI format
167
- const { openaiRequest, thinkingConfig } =
168
- this.transformer.transformRequest(anthropicRequest);
169
-
170
- this.log(`Transformed request, thinking: ${thinkingConfig.thinking}`);
171
-
172
- // Forward to Z.AI
173
- const openaiResponse = await this._forwardToUpstream(
174
- openaiRequest,
175
- req.headers
176
- );
177
-
178
- this.log(`Received response from upstream`);
179
-
180
- // Transform back to Anthropic format
181
- const anthropicResponse = this.transformer.transformResponse(
182
- openaiResponse,
183
- thinkingConfig
184
- );
185
-
186
- // Return to Claude CLI
187
- res.writeHead(200, {
188
- 'Content-Type': 'application/json',
189
- 'Access-Control-Allow-Origin': '*'
190
- });
191
- res.end(JSON.stringify(anthropicResponse));
192
-
193
- const duration = Date.now() - startTime;
194
- this.log(`Request completed in ${duration}ms`);
195
- }
196
-
197
- /**
198
- * Handle streaming request
199
- * @private
200
- */
201
- async _handleStreamingRequest(req, res, anthropicRequest, startTime) {
202
- this.log('Using streaming mode');
203
-
204
- // Transform request
205
- const { openaiRequest, thinkingConfig } =
206
- this.transformer.transformRequest(anthropicRequest);
207
-
208
- // Force streaming
209
- openaiRequest.stream = true;
210
-
211
- // Set SSE headers
212
- res.writeHead(200, {
213
- 'Content-Type': 'text/event-stream',
214
- 'Cache-Control': 'no-cache',
215
- 'Connection': 'keep-alive',
216
- 'Access-Control-Allow-Origin': '*',
217
- 'X-Accel-Buffering': 'no' // Disable proxy buffering
218
- });
219
-
220
- // Disable Nagle's algorithm to prevent buffering at socket level
221
- if (res.socket) {
222
- res.socket.setNoDelay(true);
223
- }
224
-
225
- this.log('Starting SSE stream to Claude CLI (socket buffering disabled)');
226
-
227
- // Forward and stream
228
- await this._forwardAndStreamUpstream(
229
- openaiRequest,
230
- req.headers,
231
- res,
232
- thinkingConfig,
233
- startTime
234
- );
235
- }
236
-
237
- /**
238
- * Read request body
239
- * @param {http.IncomingMessage} req - Request
240
- * @returns {Promise<string>} Body content
241
- * @private
242
- */
243
- _readBody(req) {
244
- return new Promise((resolve, reject) => {
245
- const chunks = [];
246
- const maxSize = 10 * 1024 * 1024; // 10MB limit
247
- let totalSize = 0;
248
-
249
- req.on('data', chunk => {
250
- totalSize += chunk.length;
251
- if (totalSize > maxSize) {
252
- reject(new Error('Request body too large (max 10MB)'));
253
- return;
254
- }
255
- chunks.push(chunk);
256
- });
257
-
258
- req.on('end', () => resolve(Buffer.concat(chunks).toString()));
259
- req.on('error', reject);
260
- });
261
- }
262
-
263
- /**
264
- * Forward request to Z.AI upstream
265
- * @param {Object} openaiRequest - OpenAI format request
266
- * @param {Object} originalHeaders - Original request headers
267
- * @returns {Promise<Object>} OpenAI response
268
- * @private
269
- */
270
- _forwardToUpstream(openaiRequest, originalHeaders) {
271
- return new Promise((resolve, reject) => {
272
- const url = new URL(this.upstreamUrl);
273
- const requestBody = JSON.stringify(openaiRequest);
274
-
275
- const options = {
276
- hostname: url.hostname,
277
- port: url.port || 443,
278
- path: url.pathname || '/api/coding/paas/v4/chat/completions',
279
- method: 'POST',
280
- headers: {
281
- 'Content-Type': 'application/json',
282
- 'Content-Length': Buffer.byteLength(requestBody),
283
- // Preserve auth header from original request
284
- 'Authorization': originalHeaders['authorization'] || '',
285
- 'User-Agent': 'CCS-GLMT-Proxy/1.0'
286
- }
287
- };
288
-
289
- // Debug logging
290
- this.log(`Forwarding to: ${url.hostname}${url.pathname}`);
291
-
292
- // Set timeout
293
- const timeoutHandle = setTimeout(() => {
294
- req.destroy();
295
- reject(new Error('Upstream request timeout'));
296
- }, this.timeout);
297
-
298
- const req = https.request(options, (res) => {
299
- clearTimeout(timeoutHandle);
300
-
301
- const chunks = [];
302
- res.on('data', chunk => chunks.push(chunk));
303
-
304
- res.on('end', () => {
305
- try {
306
- const body = Buffer.concat(chunks).toString();
307
- this.log(`Upstream response size: ${body.length} bytes`);
308
-
309
- // Check for non-200 status
310
- if (res.statusCode !== 200) {
311
- reject(new Error(
312
- `Upstream error: ${res.statusCode} ${res.statusMessage}\n${body}`
313
- ));
314
- return;
315
- }
316
-
317
- const response = JSON.parse(body);
318
- resolve(response);
319
- } catch (error) {
320
- reject(new Error('Invalid JSON from upstream: ' + error.message));
321
- }
322
- });
323
- });
324
-
325
- req.on('error', (error) => {
326
- clearTimeout(timeoutHandle);
327
- reject(error);
328
- });
329
-
330
- req.write(requestBody);
331
- req.end();
332
- });
333
- }
334
-
335
- /**
336
- * Forward request to Z.AI and stream response
337
- * @param {Object} openaiRequest - OpenAI format request
338
- * @param {Object} originalHeaders - Original request headers
339
- * @param {http.ServerResponse} clientRes - Response to Claude CLI
340
- * @param {Object} thinkingConfig - Thinking configuration
341
- * @param {number} startTime - Request start time
342
- * @returns {Promise<void>}
343
- * @private
344
- */
345
- async _forwardAndStreamUpstream(openaiRequest, originalHeaders, clientRes, thinkingConfig, startTime) {
346
- return new Promise((resolve, reject) => {
347
- const url = new URL(this.upstreamUrl);
348
- const requestBody = JSON.stringify(openaiRequest);
349
-
350
- const options = {
351
- hostname: url.hostname,
352
- port: url.port || 443,
353
- path: url.pathname || '/api/coding/paas/v4/chat/completions',
354
- method: 'POST',
355
- headers: {
356
- 'Content-Type': 'application/json',
357
- 'Content-Length': Buffer.byteLength(requestBody),
358
- 'Authorization': originalHeaders['authorization'] || '',
359
- 'User-Agent': 'CCS-GLMT-Proxy/1.0',
360
- 'Accept': 'text/event-stream'
361
- }
362
- };
363
-
364
- this.log(`Forwarding streaming request to: ${url.hostname}${url.pathname}`);
365
-
366
- // C-03 Fix: Apply timeout to streaming requests
367
- const timeoutHandle = setTimeout(() => {
368
- req.destroy();
369
- reject(new Error(`Streaming request timeout after ${this.timeout}ms`));
370
- }, this.timeout);
371
-
372
- const req = https.request(options, (upstreamRes) => {
373
- clearTimeout(timeoutHandle);
374
- if (upstreamRes.statusCode !== 200) {
375
- let body = '';
376
- upstreamRes.on('data', chunk => body += chunk);
377
- upstreamRes.on('end', () => {
378
- reject(new Error(`Upstream error: ${upstreamRes.statusCode}\n${body}`));
379
- });
380
- return;
381
- }
382
-
383
- const parser = new SSEParser();
384
- const accumulator = new DeltaAccumulator(thinkingConfig);
385
-
386
- upstreamRes.on('data', (chunk) => {
387
- try {
388
- const events = parser.parse(chunk);
389
-
390
- events.forEach(event => {
391
- // Transform OpenAI delta → Anthropic events
392
- const anthropicEvents = this.transformer.transformDelta(event, accumulator);
393
-
394
- // Forward to Claude CLI with immediate flush
395
- anthropicEvents.forEach(evt => {
396
- const eventLine = `event: ${evt.event}\n`;
397
- const dataLine = `data: ${JSON.stringify(evt.data)}\n\n`;
398
- clientRes.write(eventLine + dataLine);
399
-
400
- // Flush immediately if method available (HTTP/2 or custom servers)
401
- if (typeof clientRes.flush === 'function') {
402
- clientRes.flush();
403
- }
404
- });
405
- });
406
- } catch (error) {
407
- this.log(`Error processing chunk: ${error.message}`);
408
- }
409
- });
410
-
411
- upstreamRes.on('end', () => {
412
- const duration = Date.now() - startTime;
413
- this.log(`Streaming completed in ${duration}ms`);
414
- clientRes.end();
415
- resolve();
416
- });
417
-
418
- upstreamRes.on('error', (error) => {
419
- clearTimeout(timeoutHandle);
420
- this.log(`Upstream stream error: ${error.message}`);
421
- clientRes.write(`event: error\n`);
422
- clientRes.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
423
- clientRes.end();
424
- reject(error);
425
- });
426
- });
427
-
428
- req.on('error', (error) => {
429
- clearTimeout(timeoutHandle);
430
- this.log(`Request error: ${error.message}`);
431
- clientRes.write(`event: error\n`);
432
- clientRes.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
433
- clientRes.end();
434
- reject(error);
435
- });
436
-
437
- req.write(requestBody);
438
- req.end();
439
- });
440
- }
441
-
442
- /**
443
- * Stop proxy server
444
- */
445
- stop() {
446
- if (this.server) {
447
- this.log('Stopping proxy server');
448
- this.server.close();
449
- }
450
- }
451
-
452
- /**
453
- * Log message if verbose
454
- * @param {string} message - Message to log
455
- * @private
456
- */
457
- log(message) {
458
- if (this.verbose) {
459
- console.error(`[glmt-proxy] ${message}`);
460
- }
461
- }
462
- }
463
-
464
- // Main entry point
465
- if (require.main === module) {
466
- const args = process.argv.slice(2);
467
- const verbose = args.includes('--verbose') || args.includes('-v');
468
-
469
- const proxy = new GlmtProxy({ verbose });
470
-
471
- proxy.start().catch(error => {
472
- console.error('[glmt-proxy] Failed to start:', error);
473
- process.exit(1);
474
- });
475
-
476
- // Cleanup on signals
477
- process.on('SIGTERM', () => {
478
- proxy.stop();
479
- process.exit(0);
480
- });
481
-
482
- process.on('SIGINT', () => {
483
- proxy.stop();
484
- process.exit(0);
485
- });
486
-
487
- // Keep process alive
488
- process.on('uncaughtException', (error) => {
489
- console.error('[glmt-proxy] Uncaught exception:', error);
490
- proxy.stop();
491
- process.exit(1);
492
- });
493
- }
494
-
495
- module.exports = GlmtProxy;