commons-proxy 2.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.
- package/LICENSE +21 -0
- package/README.md +757 -0
- package/bin/cli.js +146 -0
- package/package.json +97 -0
- package/public/Complaint Details.pdf +0 -0
- package/public/Cyber Crime Portal.pdf +0 -0
- package/public/app.js +229 -0
- package/public/css/src/input.css +523 -0
- package/public/css/style.css +1 -0
- package/public/favicon.png +0 -0
- package/public/index.html +549 -0
- package/public/js/components/account-manager.js +356 -0
- package/public/js/components/add-account-modal.js +414 -0
- package/public/js/components/claude-config.js +420 -0
- package/public/js/components/dashboard/charts.js +605 -0
- package/public/js/components/dashboard/filters.js +362 -0
- package/public/js/components/dashboard/stats.js +110 -0
- package/public/js/components/dashboard.js +236 -0
- package/public/js/components/logs-viewer.js +100 -0
- package/public/js/components/models.js +36 -0
- package/public/js/components/server-config.js +349 -0
- package/public/js/config/constants.js +102 -0
- package/public/js/data-store.js +375 -0
- package/public/js/settings-store.js +58 -0
- package/public/js/store.js +99 -0
- package/public/js/translations/en.js +367 -0
- package/public/js/translations/id.js +412 -0
- package/public/js/translations/pt.js +308 -0
- package/public/js/translations/tr.js +358 -0
- package/public/js/translations/zh.js +373 -0
- package/public/js/utils/account-actions.js +189 -0
- package/public/js/utils/error-handler.js +96 -0
- package/public/js/utils/model-config.js +42 -0
- package/public/js/utils/ui-logger.js +143 -0
- package/public/js/utils/validators.js +77 -0
- package/public/js/utils.js +69 -0
- package/public/proxy-server-64.png +0 -0
- package/public/views/accounts.html +361 -0
- package/public/views/dashboard.html +484 -0
- package/public/views/logs.html +97 -0
- package/public/views/models.html +331 -0
- package/public/views/settings.html +1327 -0
- package/src/account-manager/credentials.js +378 -0
- package/src/account-manager/index.js +462 -0
- package/src/account-manager/onboarding.js +112 -0
- package/src/account-manager/rate-limits.js +369 -0
- package/src/account-manager/storage.js +160 -0
- package/src/account-manager/strategies/base-strategy.js +109 -0
- package/src/account-manager/strategies/hybrid-strategy.js +339 -0
- package/src/account-manager/strategies/index.js +79 -0
- package/src/account-manager/strategies/round-robin-strategy.js +76 -0
- package/src/account-manager/strategies/sticky-strategy.js +138 -0
- package/src/account-manager/strategies/trackers/health-tracker.js +162 -0
- package/src/account-manager/strategies/trackers/index.js +9 -0
- package/src/account-manager/strategies/trackers/quota-tracker.js +120 -0
- package/src/account-manager/strategies/trackers/token-bucket-tracker.js +155 -0
- package/src/auth/database.js +169 -0
- package/src/auth/oauth.js +548 -0
- package/src/auth/token-extractor.js +117 -0
- package/src/cli/accounts.js +648 -0
- package/src/cloudcode/index.js +29 -0
- package/src/cloudcode/message-handler.js +510 -0
- package/src/cloudcode/model-api.js +248 -0
- package/src/cloudcode/rate-limit-parser.js +235 -0
- package/src/cloudcode/request-builder.js +93 -0
- package/src/cloudcode/session-manager.js +47 -0
- package/src/cloudcode/sse-parser.js +121 -0
- package/src/cloudcode/sse-streamer.js +293 -0
- package/src/cloudcode/streaming-handler.js +615 -0
- package/src/config.js +125 -0
- package/src/constants.js +407 -0
- package/src/errors.js +242 -0
- package/src/fallback-config.js +29 -0
- package/src/format/content-converter.js +193 -0
- package/src/format/index.js +20 -0
- package/src/format/request-converter.js +255 -0
- package/src/format/response-converter.js +120 -0
- package/src/format/schema-sanitizer.js +673 -0
- package/src/format/signature-cache.js +88 -0
- package/src/format/thinking-utils.js +648 -0
- package/src/index.js +148 -0
- package/src/modules/usage-stats.js +205 -0
- package/src/providers/anthropic-provider.js +258 -0
- package/src/providers/base-provider.js +157 -0
- package/src/providers/cloudcode.js +94 -0
- package/src/providers/copilot.js +399 -0
- package/src/providers/github-provider.js +287 -0
- package/src/providers/google-provider.js +192 -0
- package/src/providers/index.js +211 -0
- package/src/providers/openai-compatible.js +265 -0
- package/src/providers/openai-provider.js +271 -0
- package/src/providers/openrouter-provider.js +325 -0
- package/src/providers/setup.js +83 -0
- package/src/server.js +870 -0
- package/src/utils/claude-config.js +245 -0
- package/src/utils/helpers.js +51 -0
- package/src/utils/logger.js +142 -0
- package/src/utils/native-module-helper.js +162 -0
- package/src/webui/index.js +1134 -0
package/src/config.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { logger } from './utils/logger.js';
|
|
5
|
+
|
|
6
|
+
// Default config
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
apiKey: '',
|
|
9
|
+
webuiPassword: '',
|
|
10
|
+
debug: false,
|
|
11
|
+
logLevel: 'info',
|
|
12
|
+
maxRetries: 5,
|
|
13
|
+
retryBaseMs: 1000,
|
|
14
|
+
retryMaxMs: 30000,
|
|
15
|
+
persistTokenCache: false,
|
|
16
|
+
defaultCooldownMs: 10000, // 10 seconds
|
|
17
|
+
maxWaitBeforeErrorMs: 120000, // 2 minutes
|
|
18
|
+
maxAccounts: 10, // Maximum number of accounts allowed
|
|
19
|
+
// Rate limit handling (matches opencode-cloudcode-auth)
|
|
20
|
+
rateLimitDedupWindowMs: 2000, // 2 seconds - prevents concurrent retry storms
|
|
21
|
+
maxConsecutiveFailures: 3, // Before applying extended cooldown
|
|
22
|
+
extendedCooldownMs: 60000, // 1 minute extended cooldown
|
|
23
|
+
maxCapacityRetries: 5, // Max retries for capacity exhaustion
|
|
24
|
+
modelMapping: {},
|
|
25
|
+
// Account selection strategy configuration
|
|
26
|
+
accountSelection: {
|
|
27
|
+
strategy: 'hybrid', // 'sticky' | 'round-robin' | 'hybrid'
|
|
28
|
+
// Hybrid strategy tuning (optional - sensible defaults)
|
|
29
|
+
healthScore: {
|
|
30
|
+
initial: 70, // Starting score for new accounts
|
|
31
|
+
successReward: 1, // Points on successful request
|
|
32
|
+
rateLimitPenalty: -10, // Points on rate limit
|
|
33
|
+
failurePenalty: -20, // Points on other failures
|
|
34
|
+
recoveryPerHour: 2, // Passive recovery rate
|
|
35
|
+
minUsable: 50, // Minimum score to be selected
|
|
36
|
+
maxScore: 100 // Maximum score cap
|
|
37
|
+
},
|
|
38
|
+
tokenBucket: {
|
|
39
|
+
maxTokens: 50, // Maximum token capacity
|
|
40
|
+
tokensPerMinute: 6, // Regeneration rate
|
|
41
|
+
initialTokens: 50 // Starting tokens
|
|
42
|
+
},
|
|
43
|
+
quota: {
|
|
44
|
+
lowThreshold: 0.10, // 10% - reduce score
|
|
45
|
+
criticalThreshold: 0.05, // 5% - exclude from candidates
|
|
46
|
+
staleMs: 300000 // 5 min - max age of quota data to trust
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Config locations
|
|
52
|
+
const HOME_DIR = os.homedir();
|
|
53
|
+
const CONFIG_DIR = path.join(HOME_DIR, '.config', 'commons-proxy');
|
|
54
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
55
|
+
|
|
56
|
+
// Ensure config dir exists
|
|
57
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
58
|
+
try {
|
|
59
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
60
|
+
} catch (err) {
|
|
61
|
+
// Ignore
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Load config
|
|
66
|
+
let config = { ...DEFAULT_CONFIG };
|
|
67
|
+
|
|
68
|
+
function loadConfig() {
|
|
69
|
+
try {
|
|
70
|
+
// Env vars take precedence for initial defaults, but file overrides them if present?
|
|
71
|
+
// Usually Env > File > Default.
|
|
72
|
+
|
|
73
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
74
|
+
const fileContent = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
75
|
+
const userConfig = JSON.parse(fileContent);
|
|
76
|
+
config = { ...DEFAULT_CONFIG, ...userConfig };
|
|
77
|
+
} else {
|
|
78
|
+
// Try looking in current dir for config.json as fallback
|
|
79
|
+
const localConfigPath = path.resolve('config.json');
|
|
80
|
+
if (fs.existsSync(localConfigPath)) {
|
|
81
|
+
const fileContent = fs.readFileSync(localConfigPath, 'utf8');
|
|
82
|
+
const userConfig = JSON.parse(fileContent);
|
|
83
|
+
config = { ...DEFAULT_CONFIG, ...userConfig };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Environment overrides
|
|
88
|
+
if (process.env.API_KEY) config.apiKey = process.env.API_KEY;
|
|
89
|
+
if (process.env.WEBUI_PASSWORD) config.webuiPassword = process.env.WEBUI_PASSWORD;
|
|
90
|
+
if (process.env.DEBUG === 'true') config.debug = true;
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
logger.error('[Config] Error loading config:', error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Initial load
|
|
98
|
+
loadConfig();
|
|
99
|
+
|
|
100
|
+
export function getPublicConfig() {
|
|
101
|
+
// Create a deep copy and redact sensitive fields
|
|
102
|
+
const publicConfig = JSON.parse(JSON.stringify(config));
|
|
103
|
+
|
|
104
|
+
// Redact sensitive values
|
|
105
|
+
if (publicConfig.webuiPassword) publicConfig.webuiPassword = '********';
|
|
106
|
+
if (publicConfig.apiKey) publicConfig.apiKey = '********';
|
|
107
|
+
|
|
108
|
+
return publicConfig;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function saveConfig(updates) {
|
|
112
|
+
try {
|
|
113
|
+
// Apply updates
|
|
114
|
+
config = { ...config, ...updates };
|
|
115
|
+
|
|
116
|
+
// Save to disk
|
|
117
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
|
|
118
|
+
return true;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
logger.error('[Config] Failed to save config:', error);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { config };
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for CommonsProxy - Cloud Code API integration
|
|
3
|
+
* Based on: https://github.com/NoeFabris/opencode-cloudcode-auth
|
|
4
|
+
* Enhanced with multi-provider support inspired by opencode
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { homedir, platform, arch } from 'os';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { config } from './config.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the Cloud Code IDE database path based on the current platform.
|
|
13
|
+
* - macOS: ~/Library/Application Support/Windsurf/... (or Cursor, etc.)
|
|
14
|
+
* - Windows: ~/AppData/Roaming/Windsurf/...
|
|
15
|
+
* - Linux/other: ~/.config/Windsurf/...
|
|
16
|
+
* @returns {string} Full path to the IDE state database
|
|
17
|
+
*/
|
|
18
|
+
function getCloudCodeDbPath() {
|
|
19
|
+
const home = homedir();
|
|
20
|
+
switch (platform()) {
|
|
21
|
+
case 'darwin':
|
|
22
|
+
return join(home, 'Library/Application Support/Windsurf/User/globalStorage/state.vscdb');
|
|
23
|
+
case 'win32':
|
|
24
|
+
return join(home, 'AppData/Roaming/Windsurf/User/globalStorage/state.vscdb');
|
|
25
|
+
default: // linux, freebsd, etc.
|
|
26
|
+
return join(home, '.config/Windsurf/User/globalStorage/state.vscdb');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generate platform-specific User-Agent string.
|
|
32
|
+
* @returns {string} User-Agent in format "commons-proxy/version os/arch"
|
|
33
|
+
*/
|
|
34
|
+
function getPlatformUserAgent() {
|
|
35
|
+
const os = platform();
|
|
36
|
+
const architecture = arch();
|
|
37
|
+
return `commons-proxy/1.0.0 ${os}/${architecture}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Cloud Code API endpoints (in fallback order)
|
|
41
|
+
const CLOUDCODE_ENDPOINT_DAILY = 'https://daily-cloudcode-pa.googleapis.com';
|
|
42
|
+
const CLOUDCODE_ENDPOINT_PROD = 'https://cloudcode-pa.googleapis.com';
|
|
43
|
+
|
|
44
|
+
// Endpoint fallback order (daily → prod)
|
|
45
|
+
export const CLOUDCODE_ENDPOINT_FALLBACKS = [
|
|
46
|
+
CLOUDCODE_ENDPOINT_DAILY,
|
|
47
|
+
CLOUDCODE_ENDPOINT_PROD
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// Legacy alias for backward compatibility
|
|
51
|
+
export const ANTIGRAVITY_ENDPOINT_FALLBACKS = CLOUDCODE_ENDPOINT_FALLBACKS;
|
|
52
|
+
|
|
53
|
+
// Required headers for Cloud Code API requests
|
|
54
|
+
export const CLOUDCODE_HEADERS = {
|
|
55
|
+
'User-Agent': getPlatformUserAgent(),
|
|
56
|
+
'X-Goog-Api-Client': 'google-cloud-sdk vscode_cloudshelleditor/0.1',
|
|
57
|
+
'Client-Metadata': JSON.stringify({
|
|
58
|
+
ideType: 'IDE_UNSPECIFIED',
|
|
59
|
+
platform: 'PLATFORM_UNSPECIFIED',
|
|
60
|
+
pluginType: 'GEMINI'
|
|
61
|
+
})
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Endpoint order for loadCodeAssist (prod first)
|
|
65
|
+
// loadCodeAssist works better on prod for fresh/unprovisioned accounts
|
|
66
|
+
export const LOAD_CODE_ASSIST_ENDPOINTS = [
|
|
67
|
+
CLOUDCODE_ENDPOINT_PROD,
|
|
68
|
+
CLOUDCODE_ENDPOINT_DAILY
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// Endpoint order for onboardUser (same as generateContent fallbacks)
|
|
72
|
+
export const ONBOARD_USER_ENDPOINTS = CLOUDCODE_ENDPOINT_FALLBACKS;
|
|
73
|
+
|
|
74
|
+
// Headers for loadCodeAssist API
|
|
75
|
+
export const LOAD_CODE_ASSIST_HEADERS = CLOUDCODE_HEADERS;
|
|
76
|
+
|
|
77
|
+
// Legacy alias
|
|
78
|
+
export const ANTIGRAVITY_HEADERS = CLOUDCODE_HEADERS;
|
|
79
|
+
|
|
80
|
+
// Default project ID if none can be discovered
|
|
81
|
+
export const DEFAULT_PROJECT_ID = 'rising-fact-p41fc';
|
|
82
|
+
|
|
83
|
+
// Configurable constants - values from config.json take precedence
|
|
84
|
+
export const TOKEN_REFRESH_INTERVAL_MS = config?.tokenCacheTtlMs || (5 * 60 * 1000); // From config or 5 minutes
|
|
85
|
+
export const REQUEST_BODY_LIMIT = config?.requestBodyLimit || '50mb';
|
|
86
|
+
export const CLOUDCODE_AUTH_PORT = 9092;
|
|
87
|
+
export const ANTIGRAVITY_AUTH_PORT = CLOUDCODE_AUTH_PORT; // Legacy alias
|
|
88
|
+
export const DEFAULT_PORT = config?.port || 8080;
|
|
89
|
+
|
|
90
|
+
// Multi-account configuration
|
|
91
|
+
export const ACCOUNT_CONFIG_PATH = config?.accountConfigPath || join(
|
|
92
|
+
homedir(),
|
|
93
|
+
'.config/commons-proxy/accounts.json'
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Usage history persistence path
|
|
97
|
+
export const USAGE_HISTORY_PATH = join(
|
|
98
|
+
homedir(),
|
|
99
|
+
'.config/commons-proxy/usage-history.json'
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Cloud Code IDE database path (for legacy single-account token extraction)
|
|
103
|
+
// Uses platform-specific path detection
|
|
104
|
+
export const CLOUDCODE_DB_PATH = getCloudCodeDbPath();
|
|
105
|
+
export const ANTIGRAVITY_DB_PATH = CLOUDCODE_DB_PATH; // Legacy alias
|
|
106
|
+
|
|
107
|
+
export const DEFAULT_COOLDOWN_MS = config?.defaultCooldownMs || (10 * 1000); // From config or 10 seconds
|
|
108
|
+
export const MAX_RETRIES = config?.maxRetries || 5; // From config or 5
|
|
109
|
+
export const MAX_EMPTY_RESPONSE_RETRIES = 2; // Max retries for empty API responses (from upstream)
|
|
110
|
+
export const MAX_ACCOUNTS = config?.maxAccounts || 10; // From config or 10
|
|
111
|
+
|
|
112
|
+
// Rate limit wait thresholds
|
|
113
|
+
export const MAX_WAIT_BEFORE_ERROR_MS = config?.maxWaitBeforeErrorMs || 120000; // From config or 2 minutes
|
|
114
|
+
|
|
115
|
+
// Retry deduplication - prevents thundering herd on concurrent rate limits
|
|
116
|
+
export const RATE_LIMIT_DEDUP_WINDOW_MS = config?.rateLimitDedupWindowMs || 2000; // 2 seconds
|
|
117
|
+
export const RATE_LIMIT_STATE_RESET_MS = config?.rateLimitStateResetMs || 120000; // 2 minutes - reset consecutive429 after inactivity
|
|
118
|
+
export const FIRST_RETRY_DELAY_MS = config?.firstRetryDelayMs || 1000; // Quick 1s retry on first 429
|
|
119
|
+
export const SWITCH_ACCOUNT_DELAY_MS = config?.switchAccountDelayMs || 5000; // Delay before switching accounts
|
|
120
|
+
|
|
121
|
+
// Consecutive failure tracking - extended cooldown after repeated failures
|
|
122
|
+
export const MAX_CONSECUTIVE_FAILURES = config?.maxConsecutiveFailures || 3;
|
|
123
|
+
export const EXTENDED_COOLDOWN_MS = config?.extendedCooldownMs || 60000; // 1 minute
|
|
124
|
+
|
|
125
|
+
// Capacity exhaustion - progressive backoff tiers for model capacity issues
|
|
126
|
+
export const CAPACITY_BACKOFF_TIERS_MS = config?.capacityBackoffTiersMs || [5000, 10000, 20000, 30000, 60000];
|
|
127
|
+
export const MAX_CAPACITY_RETRIES = config?.maxCapacityRetries || 5;
|
|
128
|
+
|
|
129
|
+
// Smart backoff by error type
|
|
130
|
+
export const BACKOFF_BY_ERROR_TYPE = {
|
|
131
|
+
RATE_LIMIT_EXCEEDED: 30000, // 30 seconds
|
|
132
|
+
MODEL_CAPACITY_EXHAUSTED: 15000, // 15 seconds
|
|
133
|
+
SERVER_ERROR: 20000, // 20 seconds
|
|
134
|
+
UNKNOWN: 60000 // 1 minute
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Progressive backoff tiers for QUOTA_EXHAUSTED (60s, 5m, 30m, 2h)
|
|
138
|
+
export const QUOTA_EXHAUSTED_BACKOFF_TIERS_MS = [60000, 300000, 1800000, 7200000];
|
|
139
|
+
|
|
140
|
+
// Minimum backoff floor to prevent "Available in 0s" loops (matches opencode-cloudcode-auth)
|
|
141
|
+
export const MIN_BACKOFF_MS = 2000;
|
|
142
|
+
|
|
143
|
+
// Thinking model constants
|
|
144
|
+
export const MIN_SIGNATURE_LENGTH = 50; // Minimum valid thinking signature length
|
|
145
|
+
|
|
146
|
+
// Account selection strategies
|
|
147
|
+
export const SELECTION_STRATEGIES = ['sticky', 'round-robin', 'hybrid'];
|
|
148
|
+
export const DEFAULT_SELECTION_STRATEGY = 'hybrid';
|
|
149
|
+
|
|
150
|
+
// Strategy display labels
|
|
151
|
+
export const STRATEGY_LABELS = {
|
|
152
|
+
'sticky': 'Sticky (Cache Optimized)',
|
|
153
|
+
'round-robin': 'Round Robin (Load Balanced)',
|
|
154
|
+
'hybrid': 'Hybrid (Smart Distribution)'
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Gemini-specific limits
|
|
158
|
+
export const GEMINI_MAX_OUTPUT_TOKENS = 16384;
|
|
159
|
+
|
|
160
|
+
// Gemini signature handling
|
|
161
|
+
// Sentinel value to skip thought signature validation when Claude Code strips the field
|
|
162
|
+
// See: https://ai.google.dev/gemini-api/docs/thought-signatures
|
|
163
|
+
export const GEMINI_SKIP_SIGNATURE = 'skip_thought_signature_validator';
|
|
164
|
+
|
|
165
|
+
// Cache TTL for Gemini thoughtSignatures (2 hours)
|
|
166
|
+
export const GEMINI_SIGNATURE_CACHE_TTL_MS = 2 * 60 * 60 * 1000;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get the model family from model name (dynamic detection, no hardcoded list).
|
|
170
|
+
* @param {string} modelName - The model name from the request
|
|
171
|
+
* @returns {'claude' | 'gemini' | 'unknown'} The model family
|
|
172
|
+
*/
|
|
173
|
+
export function getModelFamily(modelName) {
|
|
174
|
+
const lower = (modelName || '').toLowerCase();
|
|
175
|
+
if (lower.includes('claude')) return 'claude';
|
|
176
|
+
if (lower.includes('gemini')) return 'gemini';
|
|
177
|
+
return 'unknown';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Check if a model supports thinking/reasoning output.
|
|
182
|
+
* @param {string} modelName - The model name from the request
|
|
183
|
+
* @returns {boolean} True if the model supports thinking blocks
|
|
184
|
+
*/
|
|
185
|
+
export function isThinkingModel(modelName) {
|
|
186
|
+
const lower = (modelName || '').toLowerCase();
|
|
187
|
+
// Claude thinking models have "thinking" in the name
|
|
188
|
+
if (lower.includes('claude') && lower.includes('thinking')) return true;
|
|
189
|
+
// Gemini thinking models: explicit "thinking" in name, OR gemini version 3+
|
|
190
|
+
if (lower.includes('gemini')) {
|
|
191
|
+
if (lower.includes('thinking')) return true;
|
|
192
|
+
// Check for gemini-3 or higher (e.g., gemini-3, gemini-3.5, gemini-4, etc.)
|
|
193
|
+
const versionMatch = lower.match(/gemini-(\d+)/);
|
|
194
|
+
if (versionMatch && parseInt(versionMatch[1], 10) >= 3) return true;
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Google OAuth configuration (from opencode-cloudcode-auth)
|
|
200
|
+
// OAuth callback port - configurable via environment variable for Windows compatibility (issue #176)
|
|
201
|
+
// Windows may reserve ports in range 49152-65535 for Hyper-V/WSL2/Docker, causing EACCES errors
|
|
202
|
+
const OAUTH_CALLBACK_PORT = parseInt(process.env.OAUTH_CALLBACK_PORT || '51121', 10);
|
|
203
|
+
const OAUTH_CALLBACK_FALLBACK_PORTS = [51122, 51123, 51124, 51125, 51126];
|
|
204
|
+
|
|
205
|
+
export const OAUTH_CONFIG = {
|
|
206
|
+
clientId: '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com',
|
|
207
|
+
clientSecret: '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',
|
|
208
|
+
authUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
209
|
+
tokenUrl: 'https://oauth2.googleapis.com/token',
|
|
210
|
+
userInfoUrl: 'https://www.googleapis.com/oauth2/v1/userinfo',
|
|
211
|
+
callbackPort: OAUTH_CALLBACK_PORT,
|
|
212
|
+
callbackFallbackPorts: OAUTH_CALLBACK_FALLBACK_PORTS,
|
|
213
|
+
scopes: [
|
|
214
|
+
'https://www.googleapis.com/auth/cloud-platform',
|
|
215
|
+
'https://www.googleapis.com/auth/userinfo.email',
|
|
216
|
+
'https://www.googleapis.com/auth/userinfo.profile',
|
|
217
|
+
'https://www.googleapis.com/auth/cclog',
|
|
218
|
+
'https://www.googleapis.com/auth/experimentsandconfigs'
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
export const OAUTH_REDIRECT_URI = `http://localhost:${OAUTH_CONFIG.callbackPort}/oauth-callback`;
|
|
222
|
+
|
|
223
|
+
// Minimal system instruction for Cloud Code API
|
|
224
|
+
// Only includes the essential identity portion to reduce token usage and improve response quality
|
|
225
|
+
// Reference: GitHub issue #76, CLIProxyAPI, gcli2api
|
|
226
|
+
export const CLOUDCODE_SYSTEM_INSTRUCTION = `You are a powerful agentic AI coding assistant. You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Absolute paths only****Proactiveness**`;
|
|
227
|
+
export const ANTIGRAVITY_SYSTEM_INSTRUCTION = CLOUDCODE_SYSTEM_INSTRUCTION; // Legacy alias
|
|
228
|
+
|
|
229
|
+
// Model fallback mapping - maps primary model to fallback when quota exhausted
|
|
230
|
+
export const MODEL_FALLBACK_MAP = {
|
|
231
|
+
'gemini-3-pro-high': 'claude-opus-4-5-thinking',
|
|
232
|
+
'gemini-3-pro-low': 'claude-sonnet-4-5',
|
|
233
|
+
'gemini-3-flash': 'claude-sonnet-4-5-thinking',
|
|
234
|
+
'claude-opus-4-5-thinking': 'gemini-3-pro-high',
|
|
235
|
+
'claude-sonnet-4-5-thinking': 'gemini-3-flash',
|
|
236
|
+
'claude-sonnet-4-5': 'gemini-3-flash'
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Default test models for each family (used by test suite)
|
|
240
|
+
export const TEST_MODELS = {
|
|
241
|
+
claude: 'claude-sonnet-4-5-thinking',
|
|
242
|
+
gemini: 'gemini-3-flash'
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Default Claude CLI presets (used by WebUI settings)
|
|
246
|
+
export const DEFAULT_PRESETS = [
|
|
247
|
+
{
|
|
248
|
+
name: 'Claude Thinking',
|
|
249
|
+
config: {
|
|
250
|
+
ANTHROPIC_AUTH_TOKEN: 'test',
|
|
251
|
+
ANTHROPIC_BASE_URL: 'http://localhost:8080',
|
|
252
|
+
ANTHROPIC_MODEL: 'claude-opus-4-5-thinking',
|
|
253
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-5-thinking',
|
|
254
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-5-thinking',
|
|
255
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-sonnet-4-5',
|
|
256
|
+
CLAUDE_CODE_SUBAGENT_MODEL: 'claude-sonnet-4-5-thinking',
|
|
257
|
+
ENABLE_EXPERIMENTAL_MCP_CLI: 'true'
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'Gemini 1M',
|
|
262
|
+
config: {
|
|
263
|
+
ANTHROPIC_AUTH_TOKEN: 'test',
|
|
264
|
+
ANTHROPIC_BASE_URL: 'http://localhost:8080',
|
|
265
|
+
ANTHROPIC_MODEL: 'gemini-3-pro-high[1m]',
|
|
266
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gemini-3-pro-high[1m]',
|
|
267
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gemini-3-flash[1m]',
|
|
268
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'gemini-3-flash[1m]',
|
|
269
|
+
CLAUDE_CODE_SUBAGENT_MODEL: 'gemini-3-flash[1m]',
|
|
270
|
+
ENABLE_EXPERIMENTAL_MCP_CLI: 'true'
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
// Provider configuration
|
|
276
|
+
export const PROVIDER_CONFIG = {
|
|
277
|
+
google: {
|
|
278
|
+
id: 'google',
|
|
279
|
+
name: 'Google Cloud Code',
|
|
280
|
+
authType: 'oauth',
|
|
281
|
+
apiEndpoint: CLOUDCODE_ENDPOINT_DAILY,
|
|
282
|
+
color: '#4285f4', // Google Blue
|
|
283
|
+
icon: 'google',
|
|
284
|
+
requiresProjectId: true
|
|
285
|
+
},
|
|
286
|
+
anthropic: {
|
|
287
|
+
id: 'anthropic',
|
|
288
|
+
name: 'Anthropic',
|
|
289
|
+
authType: 'api-key',
|
|
290
|
+
apiEndpoint: 'https://api.anthropic.com',
|
|
291
|
+
color: '#d97706', // Orange
|
|
292
|
+
icon: 'anthropic',
|
|
293
|
+
requiresProjectId: false
|
|
294
|
+
},
|
|
295
|
+
openai: {
|
|
296
|
+
id: 'openai',
|
|
297
|
+
name: 'OpenAI',
|
|
298
|
+
authType: 'api-key',
|
|
299
|
+
apiEndpoint: 'https://api.openai.com',
|
|
300
|
+
color: '#10b981', // Green
|
|
301
|
+
icon: 'openai',
|
|
302
|
+
requiresProjectId: false
|
|
303
|
+
},
|
|
304
|
+
github: {
|
|
305
|
+
id: 'github',
|
|
306
|
+
name: 'GitHub Models',
|
|
307
|
+
authType: 'pat', // Personal Access Token
|
|
308
|
+
apiEndpoint: 'https://models.inference.ai.azure.com',
|
|
309
|
+
modelsEndpoint: 'https://api.github.com/models',
|
|
310
|
+
color: '#6366f1', // Indigo
|
|
311
|
+
icon: 'github',
|
|
312
|
+
requiresProjectId: false
|
|
313
|
+
},
|
|
314
|
+
copilot: {
|
|
315
|
+
id: 'copilot',
|
|
316
|
+
name: 'GitHub Copilot',
|
|
317
|
+
authType: 'device-auth', // GitHub Device Authorization Flow
|
|
318
|
+
apiEndpoint: 'https://api.githubcopilot.com',
|
|
319
|
+
color: '#f97316', // Orange
|
|
320
|
+
icon: 'copilot',
|
|
321
|
+
requiresProjectId: false
|
|
322
|
+
},
|
|
323
|
+
openrouter: {
|
|
324
|
+
id: 'openrouter',
|
|
325
|
+
name: 'OpenRouter',
|
|
326
|
+
authType: 'api-key',
|
|
327
|
+
apiEndpoint: 'https://openrouter.ai/api/v1',
|
|
328
|
+
color: '#6d28d9', // Purple
|
|
329
|
+
icon: 'openrouter',
|
|
330
|
+
requiresProjectId: false
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// Provider display names
|
|
335
|
+
export const PROVIDER_NAMES = {
|
|
336
|
+
google: 'Google Cloud Code',
|
|
337
|
+
anthropic: 'Anthropic',
|
|
338
|
+
openai: 'OpenAI',
|
|
339
|
+
github: 'GitHub Models',
|
|
340
|
+
copilot: 'GitHub Copilot',
|
|
341
|
+
openrouter: 'OpenRouter'
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// Provider colors for UI visualization
|
|
345
|
+
export const PROVIDER_COLORS = {
|
|
346
|
+
google: '#4285f4',
|
|
347
|
+
anthropic: '#d97706',
|
|
348
|
+
openai: '#10b981',
|
|
349
|
+
github: '#6366f1',
|
|
350
|
+
copilot: '#f97316',
|
|
351
|
+
openrouter: '#6d28d9'
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
export default {
|
|
355
|
+
// New exports
|
|
356
|
+
CLOUDCODE_ENDPOINT_FALLBACKS,
|
|
357
|
+
CLOUDCODE_HEADERS,
|
|
358
|
+
CLOUDCODE_AUTH_PORT,
|
|
359
|
+
CLOUDCODE_DB_PATH,
|
|
360
|
+
CLOUDCODE_SYSTEM_INSTRUCTION,
|
|
361
|
+
// Legacy aliases for backward compatibility
|
|
362
|
+
ANTIGRAVITY_ENDPOINT_FALLBACKS,
|
|
363
|
+
ANTIGRAVITY_HEADERS,
|
|
364
|
+
ANTIGRAVITY_AUTH_PORT,
|
|
365
|
+
ANTIGRAVITY_DB_PATH,
|
|
366
|
+
ANTIGRAVITY_SYSTEM_INSTRUCTION,
|
|
367
|
+
// Common exports
|
|
368
|
+
LOAD_CODE_ASSIST_ENDPOINTS,
|
|
369
|
+
ONBOARD_USER_ENDPOINTS,
|
|
370
|
+
LOAD_CODE_ASSIST_HEADERS,
|
|
371
|
+
DEFAULT_PROJECT_ID,
|
|
372
|
+
TOKEN_REFRESH_INTERVAL_MS,
|
|
373
|
+
REQUEST_BODY_LIMIT,
|
|
374
|
+
DEFAULT_PORT,
|
|
375
|
+
ACCOUNT_CONFIG_PATH,
|
|
376
|
+
DEFAULT_COOLDOWN_MS,
|
|
377
|
+
MAX_RETRIES,
|
|
378
|
+
MAX_EMPTY_RESPONSE_RETRIES,
|
|
379
|
+
MAX_ACCOUNTS,
|
|
380
|
+
MAX_WAIT_BEFORE_ERROR_MS,
|
|
381
|
+
RATE_LIMIT_DEDUP_WINDOW_MS,
|
|
382
|
+
RATE_LIMIT_STATE_RESET_MS,
|
|
383
|
+
FIRST_RETRY_DELAY_MS,
|
|
384
|
+
SWITCH_ACCOUNT_DELAY_MS,
|
|
385
|
+
MAX_CONSECUTIVE_FAILURES,
|
|
386
|
+
EXTENDED_COOLDOWN_MS,
|
|
387
|
+
CAPACITY_BACKOFF_TIERS_MS,
|
|
388
|
+
MAX_CAPACITY_RETRIES,
|
|
389
|
+
BACKOFF_BY_ERROR_TYPE,
|
|
390
|
+
QUOTA_EXHAUSTED_BACKOFF_TIERS_MS,
|
|
391
|
+
MIN_BACKOFF_MS,
|
|
392
|
+
MIN_SIGNATURE_LENGTH,
|
|
393
|
+
GEMINI_MAX_OUTPUT_TOKENS,
|
|
394
|
+
GEMINI_SKIP_SIGNATURE,
|
|
395
|
+
GEMINI_SIGNATURE_CACHE_TTL_MS,
|
|
396
|
+
getModelFamily,
|
|
397
|
+
isThinkingModel,
|
|
398
|
+
OAUTH_CONFIG,
|
|
399
|
+
OAUTH_REDIRECT_URI,
|
|
400
|
+
STRATEGY_LABELS,
|
|
401
|
+
MODEL_FALLBACK_MAP,
|
|
402
|
+
TEST_MODELS,
|
|
403
|
+
DEFAULT_PRESETS,
|
|
404
|
+
PROVIDER_CONFIG,
|
|
405
|
+
PROVIDER_NAMES,
|
|
406
|
+
PROVIDER_COLORS
|
|
407
|
+
};
|