@kamel-ahmed/proxy-claude 1.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 +622 -0
- package/bin/cli.js +124 -0
- package/package.json +80 -0
- package/public/app.js +228 -0
- package/public/css/src/input.css +523 -0
- package/public/css/style.css +1 -0
- package/public/favicon.svg +10 -0
- package/public/index.html +381 -0
- package/public/js/components/account-manager.js +245 -0
- package/public/js/components/claude-config.js +420 -0
- package/public/js/components/dashboard/charts.js +589 -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 +386 -0
- package/public/js/settings-store.js +58 -0
- package/public/js/store.js +78 -0
- package/public/js/translations/en.js +351 -0
- package/public/js/translations/id.js +396 -0
- package/public/js/translations/pt.js +287 -0
- package/public/js/translations/tr.js +342 -0
- package/public/js/translations/zh.js +357 -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/validators.js +77 -0
- package/public/js/utils.js +69 -0
- package/public/views/accounts.html +329 -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 +1329 -0
- package/src/account-manager/credentials.js +243 -0
- package/src/account-manager/index.js +380 -0
- package/src/account-manager/onboarding.js +117 -0
- package/src/account-manager/rate-limits.js +237 -0
- package/src/account-manager/storage.js +136 -0
- package/src/account-manager/strategies/base-strategy.js +104 -0
- package/src/account-manager/strategies/hybrid-strategy.js +195 -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 +8 -0
- package/src/account-manager/strategies/trackers/token-bucket-tracker.js +121 -0
- package/src/auth/database.js +169 -0
- package/src/auth/oauth.js +419 -0
- package/src/auth/token-extractor.js +117 -0
- package/src/cli/accounts.js +512 -0
- package/src/cli/refresh.js +201 -0
- package/src/cli/setup.js +338 -0
- package/src/cloudcode/index.js +29 -0
- package/src/cloudcode/message-handler.js +386 -0
- package/src/cloudcode/model-api.js +248 -0
- package/src/cloudcode/rate-limit-parser.js +181 -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 +492 -0
- package/src/config.js +107 -0
- package/src/constants.js +278 -0
- package/src/errors.js +238 -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 +248 -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 +558 -0
- package/src/index.js +146 -0
- package/src/modules/usage-stats.js +205 -0
- package/src/server.js +861 -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 +707 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Token Refresh CLI
|
|
5
|
+
*
|
|
6
|
+
* Checks and refreshes OAuth tokens for all configured accounts.
|
|
7
|
+
* Can be run manually or scheduled as a cron job.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
11
|
+
import { ACCOUNT_CONFIG_PATH } from '../constants.js';
|
|
12
|
+
import { refreshAccessToken } from '../auth/oauth.js';
|
|
13
|
+
import { logger } from '../utils/logger.js';
|
|
14
|
+
|
|
15
|
+
const COLORS = {
|
|
16
|
+
RESET: '\x1b[0m',
|
|
17
|
+
GREEN: '\x1b[32m',
|
|
18
|
+
RED: '\x1b[31m',
|
|
19
|
+
YELLOW: '\x1b[33m',
|
|
20
|
+
BLUE: '\x1b[34m',
|
|
21
|
+
CYAN: '\x1b[36m'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load accounts from config file
|
|
26
|
+
*/
|
|
27
|
+
function loadAccounts() {
|
|
28
|
+
if (!existsSync(ACCOUNT_CONFIG_PATH)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const data = readFileSync(ACCOUNT_CONFIG_PATH, 'utf-8');
|
|
33
|
+
return JSON.parse(data);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Save accounts to config file
|
|
41
|
+
*/
|
|
42
|
+
function saveAccounts(config) {
|
|
43
|
+
writeFileSync(ACCOUNT_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if a token needs refresh (expires within threshold)
|
|
48
|
+
* @param {object} account - Account object
|
|
49
|
+
* @param {number} thresholdMs - Refresh if expiring within this time (default: 5 minutes)
|
|
50
|
+
*/
|
|
51
|
+
function needsRefresh(account, thresholdMs = 5 * 60 * 1000) {
|
|
52
|
+
if (!account.tokenExpiresAt) {
|
|
53
|
+
// No expiry info, assume it needs refresh
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
const expiresAt = new Date(account.tokenExpiresAt).getTime();
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
return (expiresAt - now) < thresholdMs;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Refresh a single account's token
|
|
63
|
+
*/
|
|
64
|
+
async function refreshAccount(account) {
|
|
65
|
+
if (!account.refreshToken) {
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
error: 'No refresh token available'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const result = await refreshAccessToken(account.refreshToken);
|
|
74
|
+
return {
|
|
75
|
+
success: true,
|
|
76
|
+
accessToken: result.accessToken,
|
|
77
|
+
expiresIn: result.expiresIn
|
|
78
|
+
};
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
error: err.message
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Main refresh function
|
|
89
|
+
*/
|
|
90
|
+
export async function runRefresh(options = {}) {
|
|
91
|
+
const { force = false, quiet = false } = options;
|
|
92
|
+
|
|
93
|
+
const log = quiet ? () => {} : console.log;
|
|
94
|
+
|
|
95
|
+
log(`${COLORS.BLUE}╔════════════════════════════════════════╗${COLORS.RESET}`);
|
|
96
|
+
log(`${COLORS.BLUE}║ Antigravity Proxy Token Refresh ║${COLORS.RESET}`);
|
|
97
|
+
log(`${COLORS.BLUE}╚════════════════════════════════════════╝${COLORS.RESET}`);
|
|
98
|
+
log('');
|
|
99
|
+
|
|
100
|
+
const config = loadAccounts();
|
|
101
|
+
|
|
102
|
+
if (!config || !config.accounts || config.accounts.length === 0) {
|
|
103
|
+
log(`${COLORS.YELLOW}No accounts configured.${COLORS.RESET}`);
|
|
104
|
+
log(`Run 'proxy-claude accounts add' to add an account.`);
|
|
105
|
+
return { refreshed: 0, failed: 0, skipped: 0 };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const accounts = config.accounts;
|
|
109
|
+
let refreshed = 0;
|
|
110
|
+
let failed = 0;
|
|
111
|
+
let skipped = 0;
|
|
112
|
+
const invalidAccounts = [];
|
|
113
|
+
|
|
114
|
+
log(`Found ${accounts.length} account(s). Checking tokens...\n`);
|
|
115
|
+
|
|
116
|
+
for (let i = 0; i < accounts.length; i++) {
|
|
117
|
+
const account = accounts[i];
|
|
118
|
+
const email = account.email || `Account ${i + 1}`;
|
|
119
|
+
|
|
120
|
+
process.stdout.write(` ${email}: `);
|
|
121
|
+
|
|
122
|
+
// Check if refresh is needed
|
|
123
|
+
if (!force && !needsRefresh(account)) {
|
|
124
|
+
const expiresIn = account.tokenExpiresAt
|
|
125
|
+
? Math.round((new Date(account.tokenExpiresAt).getTime() - Date.now()) / 60000)
|
|
126
|
+
: '?';
|
|
127
|
+
log(`${COLORS.GREEN}✓ Valid (expires in ${expiresIn} min)${COLORS.RESET}`);
|
|
128
|
+
skipped++;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Attempt refresh
|
|
133
|
+
const result = await refreshAccount(account);
|
|
134
|
+
|
|
135
|
+
if (result.success) {
|
|
136
|
+
// Update account with new token
|
|
137
|
+
accounts[i].accessToken = result.accessToken;
|
|
138
|
+
accounts[i].tokenExpiresAt = new Date(Date.now() + result.expiresIn * 1000).toISOString();
|
|
139
|
+
accounts[i].isInvalid = false;
|
|
140
|
+
accounts[i].invalidReason = null;
|
|
141
|
+
|
|
142
|
+
log(`${COLORS.GREEN}✓ Refreshed (expires in ${Math.round(result.expiresIn / 60)} min)${COLORS.RESET}`);
|
|
143
|
+
refreshed++;
|
|
144
|
+
} else {
|
|
145
|
+
log(`${COLORS.RED}✗ Failed: ${result.error}${COLORS.RESET}`);
|
|
146
|
+
|
|
147
|
+
// Mark as invalid if refresh token is bad
|
|
148
|
+
if (result.error.includes('invalid_grant') || result.error.includes('Bad Request')) {
|
|
149
|
+
accounts[i].isInvalid = true;
|
|
150
|
+
accounts[i].invalidReason = 'Refresh token expired or revoked';
|
|
151
|
+
invalidAccounts.push(email);
|
|
152
|
+
}
|
|
153
|
+
failed++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Save updated config
|
|
158
|
+
config.accounts = accounts;
|
|
159
|
+
saveAccounts(config);
|
|
160
|
+
|
|
161
|
+
log('');
|
|
162
|
+
log(`${COLORS.CYAN}Summary:${COLORS.RESET}`);
|
|
163
|
+
log(` Refreshed: ${COLORS.GREEN}${refreshed}${COLORS.RESET}`);
|
|
164
|
+
log(` Skipped: ${COLORS.BLUE}${skipped}${COLORS.RESET} (still valid)`);
|
|
165
|
+
log(` Failed: ${COLORS.RED}${failed}${COLORS.RESET}`);
|
|
166
|
+
|
|
167
|
+
if (invalidAccounts.length > 0) {
|
|
168
|
+
log('');
|
|
169
|
+
log(`${COLORS.RED}⚠ The following accounts need to be re-added:${COLORS.RESET}`);
|
|
170
|
+
invalidAccounts.forEach(email => {
|
|
171
|
+
log(` - ${email}`);
|
|
172
|
+
});
|
|
173
|
+
log('');
|
|
174
|
+
log(`Run 'proxy-claude accounts add' to re-authenticate.`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { refreshed, failed, skipped, invalidAccounts };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Run if called directly
|
|
181
|
+
const args = process.argv.slice(2);
|
|
182
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
183
|
+
console.log(`
|
|
184
|
+
Usage: proxy-claude refresh [options]
|
|
185
|
+
|
|
186
|
+
Options:
|
|
187
|
+
--force, -f Force refresh all tokens, even if not expired
|
|
188
|
+
--quiet, -q Suppress output (for cron jobs)
|
|
189
|
+
--help, -h Show this help
|
|
190
|
+
`);
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
195
|
+
const quiet = args.includes('--quiet') || args.includes('-q');
|
|
196
|
+
|
|
197
|
+
runRefresh({ force, quiet }).then(result => {
|
|
198
|
+
if (result.failed > 0) {
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
});
|
package/src/cli/setup.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { DEFAULT_PORT } from '../constants.js';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
const PROXY_ROOT = path.resolve(__dirname, '../..');
|
|
12
|
+
|
|
13
|
+
export async function runSetup() {
|
|
14
|
+
logger.info('Starting Antigravity Claude Proxy setup...');
|
|
15
|
+
|
|
16
|
+
// 1. Install Claude Code CLI
|
|
17
|
+
await installClaudeCode();
|
|
18
|
+
|
|
19
|
+
// 2. Install this package globally (so proxy-claude uses local code)
|
|
20
|
+
await installProxyGlobally();
|
|
21
|
+
|
|
22
|
+
// 3. Configure Environment/Script
|
|
23
|
+
await createProxyLauncher();
|
|
24
|
+
|
|
25
|
+
logger.success('Setup complete!');
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log('You can now run "proxy-claude" from anywhere to start the proxy and Claude Code seamlessly.');
|
|
28
|
+
console.log('');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function installClaudeCode() {
|
|
32
|
+
try {
|
|
33
|
+
// Check if claude is installed
|
|
34
|
+
try {
|
|
35
|
+
execSync('claude --version', { stdio: 'ignore' });
|
|
36
|
+
logger.info('Claude Code CLI is already installed.');
|
|
37
|
+
} catch (e) {
|
|
38
|
+
// If checking version fails, it might not be installed or return non-zero
|
|
39
|
+
throw new Error('Not found');
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
logger.info('Installing Claude Code CLI...');
|
|
43
|
+
try {
|
|
44
|
+
execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit' });
|
|
45
|
+
logger.success('Claude Code CLI installed successfully.');
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.error('Failed to install Claude Code CLI. Please install it manually: npm install -g @anthropic-ai/claude-code');
|
|
48
|
+
// We don't exit here, maybe they want the script anyway
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function installProxyGlobally() {
|
|
54
|
+
// Skip global install - too complex with build steps
|
|
55
|
+
// Instead, proxy-claude will use npx or check for local project
|
|
56
|
+
logger.info('Skipping global install (use npm link if needed).');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function createProxyLauncher() {
|
|
60
|
+
// Generate the script content.
|
|
61
|
+
// IMPORTANT: We escape ${} for Bash variables as \${} so JS doesn't parse them.
|
|
62
|
+
// We escape backslashes for colors as \\033 so they write correctly to the file.
|
|
63
|
+
|
|
64
|
+
// Embed the local project path so it can be used for development
|
|
65
|
+
const localProjectPath = PROXY_ROOT;
|
|
66
|
+
|
|
67
|
+
const scriptContent = `#!/bin/bash
|
|
68
|
+
# proxy-claude - Full CLI for Antigravity Claude Proxy
|
|
69
|
+
# This script provides complete control over the proxy and Claude Code.
|
|
70
|
+
|
|
71
|
+
VERSION="1.0.0"
|
|
72
|
+
PORT=\${PORT:-${DEFAULT_PORT || 8080}}
|
|
73
|
+
PROXY_PID=""
|
|
74
|
+
LOCAL_PROJECT="${localProjectPath}"
|
|
75
|
+
|
|
76
|
+
# Colors for output
|
|
77
|
+
RED='\\033[0;31m'
|
|
78
|
+
GREEN='\\033[0;32m'
|
|
79
|
+
YELLOW='\\033[1;33m'
|
|
80
|
+
BLUE='\\033[0;34m'
|
|
81
|
+
NC='\\033[0m' # No Color
|
|
82
|
+
|
|
83
|
+
# Function to cleanup background process on exit
|
|
84
|
+
cleanup() {
|
|
85
|
+
if [ -n "$PROXY_PID" ]; then
|
|
86
|
+
echo ""
|
|
87
|
+
echo -e "\${YELLOW}Stopping Antigravity Claude Proxy...\${NC}"
|
|
88
|
+
kill $PROXY_PID 2>/dev/null
|
|
89
|
+
wait $PROXY_PID 2>/dev/null
|
|
90
|
+
echo -e "\${GREEN}✓ Proxy stopped\${NC}"
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Function to start proxy and wait for it
|
|
95
|
+
start_proxy() {
|
|
96
|
+
echo -e "\${BLUE}Starting Antigravity Claude Proxy on port $PORT...\${NC}"
|
|
97
|
+
|
|
98
|
+
if command -v antigravity-claude-proxy &> /dev/null; then
|
|
99
|
+
antigravity-claude-proxy start > /tmp/antigravity-proxy.log 2>&1 &
|
|
100
|
+
else
|
|
101
|
+
npx antigravity-claude-proxy@latest start > /tmp/antigravity-proxy.log 2>&1 &
|
|
102
|
+
fi
|
|
103
|
+
PROXY_PID=$!
|
|
104
|
+
|
|
105
|
+
echo "Waiting for proxy to be ready..."
|
|
106
|
+
MAX_RETRIES=60
|
|
107
|
+
count=0
|
|
108
|
+
while ! nc -z localhost $PORT >/dev/null 2>&1; do
|
|
109
|
+
sleep 0.5
|
|
110
|
+
count=$((count+1))
|
|
111
|
+
if [ $count -ge $MAX_RETRIES ]; then
|
|
112
|
+
echo -e "\${RED}Error: Proxy failed to start within 30 seconds.\${NC}"
|
|
113
|
+
echo "Check logs: /tmp/antigravity-proxy.log"
|
|
114
|
+
tail -n 20 /tmp/antigravity-proxy.log
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
done
|
|
118
|
+
echo -e "\${GREEN}✓ Proxy is ready on port $PORT!\${NC}"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Function to run the proxy command
|
|
122
|
+
run_proxy_cmd() {
|
|
123
|
+
# Prefer local project if it exists (for development)
|
|
124
|
+
if [ -d "$LOCAL_PROJECT" ] && [ -f "$LOCAL_PROJECT/src/index.js" ]; then
|
|
125
|
+
node "$LOCAL_PROJECT/bin/cli.js" "$@"
|
|
126
|
+
elif command -v proxy-claude &> /dev/null; then
|
|
127
|
+
proxy-claude "$@"
|
|
128
|
+
else
|
|
129
|
+
npx @kamel-ahmed/proxy-claude@latest "$@"
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
show_help() {
|
|
134
|
+
echo ""
|
|
135
|
+
echo -e "\${BLUE}proxy-claude\${NC} v$VERSION - Antigravity Claude Proxy CLI"
|
|
136
|
+
echo ""
|
|
137
|
+
echo "USAGE:"
|
|
138
|
+
echo " proxy-claude [command] [options]"
|
|
139
|
+
echo ""
|
|
140
|
+
echo "COMMANDS:"
|
|
141
|
+
echo " (default) Start proxy + launch Claude Code (interactive)"
|
|
142
|
+
echo " web Start proxy with web dashboard only"
|
|
143
|
+
echo " start Start proxy server only (foreground)"
|
|
144
|
+
echo " accounts [action] Manage Google accounts (add/list/remove/verify/clear)"
|
|
145
|
+
echo " refresh Check and refresh account tokens"
|
|
146
|
+
echo " status Check if proxy is running"
|
|
147
|
+
echo " stop Stop running proxy server"
|
|
148
|
+
echo " logs View proxy logs"
|
|
149
|
+
echo " uninstall Remove proxy-claude from system"
|
|
150
|
+
echo " help Show this help message"
|
|
151
|
+
echo ""
|
|
152
|
+
echo "OPTIONS:"
|
|
153
|
+
echo " --port, -p <port> Set custom port (default: 8080)"
|
|
154
|
+
echo " --version, -v Show version"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "EXAMPLES:"
|
|
157
|
+
echo " proxy-claude # Start proxy + Claude"
|
|
158
|
+
echo " proxy-claude web # Start web dashboard"
|
|
159
|
+
echo " proxy-claude accounts add # Add Google account"
|
|
160
|
+
echo " proxy-claude refresh # Check and refresh tokens"
|
|
161
|
+
echo " proxy-claude refresh --force # Force refresh all tokens"
|
|
162
|
+
echo " PORT=3000 proxy-claude # Use custom port"
|
|
163
|
+
echo " proxy-claude --port 3000 web # Web on custom port"
|
|
164
|
+
echo ""
|
|
165
|
+
echo "WEB DASHBOARD:"
|
|
166
|
+
echo " http://localhost:$PORT"
|
|
167
|
+
echo ""
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Parse global options
|
|
171
|
+
while [[ "$1" == --* ]] || [[ "$1" == -* ]]; do
|
|
172
|
+
case "$1" in
|
|
173
|
+
--port|-p)
|
|
174
|
+
PORT="$2"
|
|
175
|
+
shift 2
|
|
176
|
+
;;
|
|
177
|
+
--version|-v)
|
|
178
|
+
echo "proxy-claude v$VERSION"
|
|
179
|
+
exit 0
|
|
180
|
+
;;
|
|
181
|
+
--help|-h)
|
|
182
|
+
show_help
|
|
183
|
+
exit 0
|
|
184
|
+
;;
|
|
185
|
+
*)
|
|
186
|
+
break
|
|
187
|
+
;;
|
|
188
|
+
esac
|
|
189
|
+
done
|
|
190
|
+
|
|
191
|
+
COMMAND="\${1:-}"
|
|
192
|
+
shift 2>/dev/null || true
|
|
193
|
+
|
|
194
|
+
case "$COMMAND" in
|
|
195
|
+
""|"claude")
|
|
196
|
+
# Default: Start proxy + Claude
|
|
197
|
+
trap cleanup EXIT INT TERM
|
|
198
|
+
start_proxy
|
|
199
|
+
echo ""
|
|
200
|
+
if command -v claude &> /dev/null; then
|
|
201
|
+
export ANTHROPIC_BASE_URL="http://localhost:$PORT"
|
|
202
|
+
export ANTHROPIC_API_KEY="dummy"
|
|
203
|
+
claude "$@"
|
|
204
|
+
else
|
|
205
|
+
echo -e "\${RED}Error: 'claude' command not found.\${NC}"
|
|
206
|
+
echo "Install it with: npm install -g @anthropic-ai/claude-code"
|
|
207
|
+
exit 1
|
|
208
|
+
fi
|
|
209
|
+
;;
|
|
210
|
+
|
|
211
|
+
"web")
|
|
212
|
+
# Start proxy with web dashboard only (foreground)
|
|
213
|
+
echo -e "\${BLUE}Starting Antigravity Claude Proxy with Web Dashboard...\${NC}"
|
|
214
|
+
echo -e "Web UI: \${GREEN}http://localhost:$PORT\${NC}"
|
|
215
|
+
echo ""
|
|
216
|
+
echo "Press Ctrl+C to stop"
|
|
217
|
+
echo ""
|
|
218
|
+
run_proxy_cmd start
|
|
219
|
+
;;
|
|
220
|
+
|
|
221
|
+
"start")
|
|
222
|
+
# Start proxy only (foreground)
|
|
223
|
+
echo -e "\${BLUE}Starting proxy server...\${NC}"
|
|
224
|
+
run_proxy_cmd start
|
|
225
|
+
;;
|
|
226
|
+
|
|
227
|
+
"accounts")
|
|
228
|
+
# Account management
|
|
229
|
+
run_proxy_cmd accounts "$@"
|
|
230
|
+
;;
|
|
231
|
+
|
|
232
|
+
"refresh")
|
|
233
|
+
# Token refresh - run inline since npm package may not have this yet
|
|
234
|
+
CONFIG_FILE="$HOME/.config/antigravity-proxy/accounts.json"
|
|
235
|
+
|
|
236
|
+
if [ ! -f "$CONFIG_FILE" ]; then
|
|
237
|
+
echo -e "\${YELLOW}No accounts configured.\${NC}"
|
|
238
|
+
echo "Run 'proxy-claude accounts add' to add an account."
|
|
239
|
+
exit 0
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
# Try to run via the package first, fall back to verify which does similar
|
|
243
|
+
if run_proxy_cmd refresh "$@" 2>/dev/null; then
|
|
244
|
+
exit 0
|
|
245
|
+
else
|
|
246
|
+
echo "Running token verification..."
|
|
247
|
+
run_proxy_cmd accounts verify
|
|
248
|
+
fi
|
|
249
|
+
;;
|
|
250
|
+
|
|
251
|
+
"status")
|
|
252
|
+
# Check if proxy is running
|
|
253
|
+
if nc -z localhost $PORT >/dev/null 2>&1; then
|
|
254
|
+
echo -e "\${GREEN}✓ Proxy is running on port $PORT\${NC}"
|
|
255
|
+
# Try to get health info
|
|
256
|
+
curl -s "http://localhost:$PORT/health" 2>/dev/null || true
|
|
257
|
+
else
|
|
258
|
+
echo -e "\${YELLOW}✗ Proxy is not running on port $PORT\${NC}"
|
|
259
|
+
fi
|
|
260
|
+
;;
|
|
261
|
+
|
|
262
|
+
"stop")
|
|
263
|
+
# Stop running proxy
|
|
264
|
+
echo "Stopping proxy on port $PORT..."
|
|
265
|
+
# Find and kill process listening on the port
|
|
266
|
+
PID=$(lsof -ti tcp:$PORT 2>/dev/null)
|
|
267
|
+
if [ -n "$PID" ]; then
|
|
268
|
+
kill $PID 2>/dev/null
|
|
269
|
+
echo -e "\${GREEN}✓ Proxy stopped (PID: $PID)\${NC}"
|
|
270
|
+
else
|
|
271
|
+
echo -e "\${YELLOW}No proxy found running on port $PORT\${NC}"
|
|
272
|
+
fi
|
|
273
|
+
;;
|
|
274
|
+
|
|
275
|
+
"logs")
|
|
276
|
+
# View logs
|
|
277
|
+
if [ -f /tmp/antigravity-proxy.log ]; then
|
|
278
|
+
echo -e "\${BLUE}=== Proxy Logs ===\${NC}"
|
|
279
|
+
tail -f /tmp/antigravity-proxy.log
|
|
280
|
+
else
|
|
281
|
+
echo "No log file found at /tmp/antigravity-proxy.log"
|
|
282
|
+
fi
|
|
283
|
+
;;
|
|
284
|
+
|
|
285
|
+
"uninstall")
|
|
286
|
+
# Uninstall proxy-claude
|
|
287
|
+
echo -e "\${YELLOW}Uninstalling proxy-claude...\${NC}"
|
|
288
|
+
if [ -f /usr/local/bin/proxy-claude ]; then
|
|
289
|
+
sudo rm /usr/local/bin/proxy-claude
|
|
290
|
+
echo -e "\${GREEN}✓ Removed /usr/local/bin/proxy-claude\${NC}"
|
|
291
|
+
else
|
|
292
|
+
echo "proxy-claude not found in /usr/local/bin"
|
|
293
|
+
fi
|
|
294
|
+
echo ""
|
|
295
|
+
echo "To also uninstall the npm package (if installed globally):"
|
|
296
|
+
echo " npm uninstall -g antigravity-claude-proxy"
|
|
297
|
+
;;
|
|
298
|
+
|
|
299
|
+
"help"|"--help"|"-h")
|
|
300
|
+
show_help
|
|
301
|
+
;;
|
|
302
|
+
|
|
303
|
+
*)
|
|
304
|
+
echo -e "\${RED}Unknown command: $COMMAND\${NC}"
|
|
305
|
+
echo "Run 'proxy-claude help' for usage"
|
|
306
|
+
exit 1
|
|
307
|
+
;;
|
|
308
|
+
esac
|
|
309
|
+
`;
|
|
310
|
+
|
|
311
|
+
// Try to install it to /usr/local/bin directly if possible
|
|
312
|
+
const targetPath = '/usr/local/bin/proxy-claude';
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
// Try writing directly (might fail if not sudo)
|
|
316
|
+
fs.writeFileSync(targetPath, scriptContent);
|
|
317
|
+
fs.chmodSync(targetPath, '755');
|
|
318
|
+
logger.success(`Installed global command: ${targetPath}`);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
// Fallback: Write to local and try to move with sudo, or just tell user what to do
|
|
321
|
+
if (err.code === 'EACCES') {
|
|
322
|
+
logger.warn('Could not write directly to /usr/local/bin. Trying with sudo...');
|
|
323
|
+
const tempPath = path.join(os.tmpdir(), 'proxy-claude');
|
|
324
|
+
fs.writeFileSync(tempPath, scriptContent);
|
|
325
|
+
fs.chmodSync(tempPath, '755');
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
execSync(`sudo mv "${tempPath}" "${targetPath}"`, { stdio: 'inherit' });
|
|
329
|
+
logger.success(`Installed global command: ${targetPath}`);
|
|
330
|
+
} catch (sudoErr) {
|
|
331
|
+
logger.error('Failed to install global command. Please run the setup with sudo or move the file manually.');
|
|
332
|
+
console.log(`Command to run: sudo mv "${tempPath}" "${targetPath}"`);
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
logger.error(`Failed to create launcher: ${err.message}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud Code Client for Antigravity
|
|
3
|
+
*
|
|
4
|
+
* Communicates with Google's Cloud Code internal API using the
|
|
5
|
+
* v1internal:streamGenerateContent endpoint with proper request wrapping.
|
|
6
|
+
*
|
|
7
|
+
* Supports multi-account load balancing with automatic failover.
|
|
8
|
+
*
|
|
9
|
+
* Based on: https://github.com/NoeFabris/opencode-antigravity-auth
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Re-export public API
|
|
13
|
+
export { sendMessage } from './message-handler.js';
|
|
14
|
+
export { sendMessageStream } from './streaming-handler.js';
|
|
15
|
+
export { listModels, fetchAvailableModels, getModelQuotas, getSubscriptionTier } from './model-api.js';
|
|
16
|
+
|
|
17
|
+
// Default export for backwards compatibility
|
|
18
|
+
import { sendMessage } from './message-handler.js';
|
|
19
|
+
import { sendMessageStream } from './streaming-handler.js';
|
|
20
|
+
import { listModels, fetchAvailableModels, getModelQuotas, getSubscriptionTier } from './model-api.js';
|
|
21
|
+
|
|
22
|
+
export default {
|
|
23
|
+
sendMessage,
|
|
24
|
+
sendMessageStream,
|
|
25
|
+
listModels,
|
|
26
|
+
fetchAvailableModels,
|
|
27
|
+
getModelQuotas,
|
|
28
|
+
getSubscriptionTier
|
|
29
|
+
};
|