@kaitranntt/ccs 4.4.0 → 5.0.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 +98 -7
- package/VERSION +1 -1
- package/config/base-agy.settings.json +10 -0
- package/config/base-codex.settings.json +10 -0
- package/config/base-gemini.settings.json +10 -0
- package/dist/auth/auth-commands.d.ts +52 -0
- package/dist/auth/auth-commands.d.ts.map +1 -0
- package/dist/auth/auth-commands.js +479 -0
- package/dist/auth/auth-commands.js.map +1 -0
- package/dist/auth/profile-detector.d.ts +68 -0
- package/dist/auth/profile-detector.d.ts.map +1 -0
- package/dist/auth/profile-detector.js +209 -0
- package/dist/auth/profile-detector.js.map +1 -0
- package/dist/auth/profile-registry.d.ts +60 -0
- package/dist/auth/profile-registry.d.ts.map +1 -0
- package/dist/auth/profile-registry.js +188 -0
- package/dist/auth/profile-registry.js.map +1 -0
- package/dist/ccs.d.ts +10 -0
- package/dist/ccs.d.ts.map +1 -0
- package/dist/ccs.js +320 -0
- package/dist/ccs.js.map +1 -0
- package/dist/cliproxy/auth-handler.d.ts +95 -0
- package/dist/cliproxy/auth-handler.d.ts.map +1 -0
- package/dist/cliproxy/auth-handler.js +443 -0
- package/dist/cliproxy/auth-handler.js.map +1 -0
- package/dist/cliproxy/base-config-loader.d.ts +42 -0
- package/dist/cliproxy/base-config-loader.d.ts.map +1 -0
- package/dist/cliproxy/base-config-loader.js +123 -0
- package/dist/cliproxy/base-config-loader.js.map +1 -0
- package/dist/cliproxy/binary-manager.d.ts +104 -0
- package/dist/cliproxy/binary-manager.d.ts.map +1 -0
- package/dist/cliproxy/binary-manager.js +567 -0
- package/dist/cliproxy/binary-manager.js.map +1 -0
- package/dist/cliproxy/cliproxy-executor.d.ts +33 -0
- package/dist/cliproxy/cliproxy-executor.d.ts.map +1 -0
- package/dist/cliproxy/cliproxy-executor.js +297 -0
- package/dist/cliproxy/cliproxy-executor.js.map +1 -0
- package/dist/cliproxy/config-generator.d.ts +89 -0
- package/dist/cliproxy/config-generator.d.ts.map +1 -0
- package/dist/cliproxy/config-generator.js +263 -0
- package/dist/cliproxy/config-generator.js.map +1 -0
- package/dist/cliproxy/index.d.ts +13 -0
- package/dist/cliproxy/index.d.ts.map +1 -0
- package/dist/cliproxy/index.js +62 -0
- package/dist/cliproxy/index.js.map +1 -0
- package/dist/cliproxy/platform-detector.d.ts +48 -0
- package/dist/cliproxy/platform-detector.d.ts.map +1 -0
- package/dist/cliproxy/platform-detector.js +118 -0
- package/dist/cliproxy/platform-detector.js.map +1 -0
- package/dist/cliproxy/types.d.ts +169 -0
- package/dist/cliproxy/types.d.ts.map +1 -0
- package/dist/cliproxy/types.js +7 -0
- package/dist/cliproxy/types.js.map +1 -0
- package/dist/commands/doctor-command.d.ts +10 -0
- package/dist/commands/doctor-command.d.ts.map +1 -0
- package/dist/commands/doctor-command.js +44 -0
- package/dist/commands/doctor-command.js.map +1 -0
- package/dist/commands/help-command.d.ts +5 -0
- package/dist/commands/help-command.d.ts.map +1 -0
- package/dist/commands/help-command.js +104 -0
- package/dist/commands/help-command.js.map +1 -0
- package/dist/commands/install-command.d.ts +14 -0
- package/dist/commands/install-command.d.ts.map +1 -0
- package/dist/commands/install-command.js +39 -0
- package/dist/commands/install-command.js.map +1 -0
- package/dist/commands/shell-completion-command.d.ts +10 -0
- package/dist/commands/shell-completion-command.d.ts.map +1 -0
- package/dist/commands/shell-completion-command.js +85 -0
- package/dist/commands/shell-completion-command.js.map +1 -0
- package/dist/commands/sync-command.d.ts +10 -0
- package/dist/commands/sync-command.d.ts.map +1 -0
- package/dist/commands/sync-command.js +59 -0
- package/dist/commands/sync-command.js.map +1 -0
- package/dist/commands/update-command.d.ts +12 -0
- package/dist/commands/update-command.d.ts.map +1 -0
- package/dist/commands/update-command.js +295 -0
- package/dist/commands/update-command.js.map +1 -0
- package/dist/commands/version-command.d.ts +10 -0
- package/dist/commands/version-command.d.ts.map +1 -0
- package/dist/commands/version-command.js +100 -0
- package/dist/commands/version-command.js.map +1 -0
- package/dist/delegation/delegation-handler.d.ts +60 -0
- package/dist/delegation/delegation-handler.d.ts.map +1 -0
- package/dist/delegation/delegation-handler.js +174 -0
- package/dist/delegation/delegation-handler.js.map +1 -0
- package/dist/delegation/headless-executor.d.ts +114 -0
- package/dist/delegation/headless-executor.d.ts.map +1 -0
- package/dist/delegation/headless-executor.js +562 -0
- package/dist/delegation/headless-executor.js.map +1 -0
- package/dist/delegation/result-formatter.d.ts +108 -0
- package/dist/delegation/result-formatter.d.ts.map +1 -0
- package/dist/delegation/result-formatter.js +391 -0
- package/dist/delegation/result-formatter.js.map +1 -0
- package/dist/delegation/session-manager.d.ts +58 -0
- package/dist/delegation/session-manager.d.ts.map +1 -0
- package/dist/delegation/session-manager.js +153 -0
- package/dist/delegation/session-manager.js.map +1 -0
- package/dist/delegation/settings-parser.d.ts +31 -0
- package/dist/delegation/settings-parser.d.ts.map +1 -0
- package/dist/delegation/settings-parser.js +107 -0
- package/dist/delegation/settings-parser.js.map +1 -0
- package/dist/glmt/delta-accumulator.d.ts +210 -0
- package/dist/glmt/delta-accumulator.d.ts.map +1 -0
- package/dist/glmt/delta-accumulator.js +351 -0
- package/dist/glmt/delta-accumulator.js.map +1 -0
- package/dist/glmt/glmt-proxy.d.ts +72 -0
- package/dist/glmt/glmt-proxy.d.ts.map +1 -0
- package/dist/glmt/glmt-proxy.js +427 -0
- package/dist/glmt/glmt-proxy.js.map +1 -0
- package/dist/glmt/glmt-transformer.d.ts +265 -0
- package/dist/glmt/glmt-transformer.d.ts.map +1 -0
- package/dist/glmt/glmt-transformer.js +832 -0
- package/dist/glmt/glmt-transformer.js.map +1 -0
- package/dist/glmt/locale-enforcer.d.ts +38 -0
- package/dist/glmt/locale-enforcer.d.ts.map +1 -0
- package/dist/glmt/locale-enforcer.js +69 -0
- package/dist/glmt/locale-enforcer.js.map +1 -0
- package/dist/glmt/reasoning-enforcer.d.ts +52 -0
- package/dist/glmt/reasoning-enforcer.d.ts.map +1 -0
- package/dist/glmt/reasoning-enforcer.js +151 -0
- package/dist/glmt/reasoning-enforcer.js.map +1 -0
- package/dist/glmt/sse-parser.d.ts +47 -0
- package/dist/glmt/sse-parser.d.ts.map +1 -0
- package/dist/glmt/sse-parser.js +93 -0
- package/dist/glmt/sse-parser.js.map +1 -0
- package/dist/management/doctor.d.ts +104 -0
- package/dist/management/doctor.d.ts.map +1 -0
- package/dist/management/doctor.js +673 -0
- package/dist/management/doctor.js.map +1 -0
- package/dist/management/instance-manager.d.ts +57 -0
- package/dist/management/instance-manager.d.ts.map +1 -0
- package/dist/management/instance-manager.js +195 -0
- package/dist/management/instance-manager.js.map +1 -0
- package/dist/management/recovery-manager.d.ts +39 -0
- package/dist/management/recovery-manager.d.ts.map +1 -0
- package/dist/management/recovery-manager.js +141 -0
- package/dist/management/recovery-manager.js.map +1 -0
- package/dist/management/shared-manager.d.ts +47 -0
- package/dist/management/shared-manager.d.ts.map +1 -0
- package/dist/management/shared-manager.js +388 -0
- package/dist/management/shared-manager.js.map +1 -0
- package/dist/types/cli.d.ts +50 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/cli.js +16 -0
- package/dist/types/cli.js.map +1 -0
- package/dist/types/config.d.ts +51 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +26 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/delegation.d.ts +61 -0
- package/dist/types/delegation.d.ts.map +1 -0
- package/dist/types/delegation.js +6 -0
- package/dist/types/delegation.js.map +1 -0
- package/dist/types/glmt.d.ts +95 -0
- package/dist/types/glmt.d.ts.map +1 -0
- package/dist/types/glmt.js +7 -0
- package/dist/types/glmt.js.map +1 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +16 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/utils.d.ts +36 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/utils.js +22 -0
- package/dist/types/utils.js.map +1 -0
- package/dist/utils/claude-detector.d.ts +14 -0
- package/dist/utils/claude-detector.d.ts.map +1 -0
- package/dist/utils/claude-detector.js +112 -0
- package/dist/utils/claude-detector.js.map +1 -0
- package/dist/utils/claude-dir-installer.d.ts +46 -0
- package/dist/utils/claude-dir-installer.d.ts.map +1 -0
- package/dist/utils/claude-dir-installer.js +289 -0
- package/dist/utils/claude-dir-installer.js.map +1 -0
- package/dist/utils/claude-symlink-manager.d.ts +61 -0
- package/dist/utils/claude-symlink-manager.d.ts.map +1 -0
- package/dist/utils/claude-symlink-manager.js +291 -0
- package/dist/utils/claude-symlink-manager.js.map +1 -0
- package/dist/utils/config-manager.d.ts +32 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +143 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/delegation-validator.d.ts +39 -0
- package/dist/utils/delegation-validator.d.ts.map +1 -0
- package/dist/utils/delegation-validator.js +161 -0
- package/dist/utils/delegation-validator.js.map +1 -0
- package/dist/utils/error-codes.d.ts +36 -0
- package/dist/utils/error-codes.d.ts.map +1 -0
- package/dist/utils/error-codes.js +63 -0
- package/dist/utils/error-codes.js.map +1 -0
- package/dist/utils/error-manager.d.ts +59 -0
- package/dist/utils/error-manager.d.ts.map +1 -0
- package/dist/utils/error-manager.js +228 -0
- package/dist/utils/error-manager.js.map +1 -0
- package/dist/utils/helpers.d.ts +27 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +150 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/package-manager-detector.d.ts +14 -0
- package/dist/utils/package-manager-detector.d.ts.map +1 -0
- package/dist/utils/package-manager-detector.js +162 -0
- package/dist/utils/package-manager-detector.js.map +1 -0
- package/dist/utils/progress-indicator.d.ts +52 -0
- package/dist/utils/progress-indicator.d.ts.map +1 -0
- package/dist/utils/progress-indicator.js +102 -0
- package/dist/utils/progress-indicator.js.map +1 -0
- package/dist/utils/prompt.d.ts +29 -0
- package/dist/utils/prompt.d.ts.map +1 -0
- package/dist/utils/prompt.js +116 -0
- package/dist/utils/prompt.js.map +1 -0
- package/dist/utils/shell-completion.d.ts +52 -0
- package/dist/utils/shell-completion.d.ts.map +1 -0
- package/dist/utils/shell-completion.js +231 -0
- package/dist/utils/shell-completion.js.map +1 -0
- package/dist/utils/shell-executor.d.ts +15 -0
- package/dist/utils/shell-executor.d.ts.map +1 -0
- package/dist/utils/shell-executor.js +57 -0
- package/dist/utils/shell-executor.js.map +1 -0
- package/dist/utils/update-checker.d.ts +48 -0
- package/dist/utils/update-checker.d.ts.map +1 -0
- package/dist/utils/update-checker.js +241 -0
- package/dist/utils/update-checker.js.map +1 -0
- package/lib/ccs +21 -1907
- package/lib/ccs.ps1 +26 -1800
- package/lib/error-codes.ps1 +2 -1
- package/lib/prompt.ps1 +2 -2
- package/package.json +31 -11
- package/scripts/add-shebang.js +39 -0
- package/scripts/bump-version.sh +25 -37
- package/scripts/dev-install.sh +32 -11
- package/scripts/postinstall.js +29 -29
- package/bin/auth/auth-commands.js +0 -499
- package/bin/auth/profile-detector.js +0 -204
- package/bin/auth/profile-registry.js +0 -225
- package/bin/ccs.js +0 -1034
- package/bin/delegation/README.md +0 -191
- package/bin/delegation/delegation-handler.js +0 -212
- package/bin/delegation/headless-executor.js +0 -618
- package/bin/delegation/result-formatter.js +0 -485
- package/bin/delegation/session-manager.js +0 -157
- package/bin/delegation/settings-parser.js +0 -109
- package/bin/glmt/delta-accumulator.js +0 -276
- package/bin/glmt/glmt-proxy.js +0 -495
- package/bin/glmt/glmt-transformer.js +0 -999
- package/bin/glmt/locale-enforcer.js +0 -72
- package/bin/glmt/reasoning-enforcer.js +0 -173
- package/bin/glmt/sse-parser.js +0 -96
- package/bin/management/doctor.js +0 -721
- package/bin/management/instance-manager.js +0 -202
- package/bin/management/recovery-manager.js +0 -135
- package/bin/management/shared-manager.js +0 -402
- package/bin/utils/claude-detector.js +0 -73
- package/bin/utils/claude-dir-installer.js +0 -283
- package/bin/utils/claude-symlink-manager.js +0 -289
- package/bin/utils/config-manager.js +0 -103
- package/bin/utils/delegation-validator.js +0 -154
- package/bin/utils/error-codes.js +0 -59
- package/bin/utils/error-manager.js +0 -165
- package/bin/utils/helpers.js +0 -136
- package/bin/utils/progress-indicator.js +0 -111
- package/bin/utils/prompt.js +0 -134
- package/bin/utils/shell-completion.js +0 -256
- package/bin/utils/update-checker.js +0 -243
package/bin/glmt/glmt-proxy.js
DELETED
|
@@ -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;
|