@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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +622 -0
  3. package/bin/cli.js +124 -0
  4. package/package.json +80 -0
  5. package/public/app.js +228 -0
  6. package/public/css/src/input.css +523 -0
  7. package/public/css/style.css +1 -0
  8. package/public/favicon.svg +10 -0
  9. package/public/index.html +381 -0
  10. package/public/js/components/account-manager.js +245 -0
  11. package/public/js/components/claude-config.js +420 -0
  12. package/public/js/components/dashboard/charts.js +589 -0
  13. package/public/js/components/dashboard/filters.js +362 -0
  14. package/public/js/components/dashboard/stats.js +110 -0
  15. package/public/js/components/dashboard.js +236 -0
  16. package/public/js/components/logs-viewer.js +100 -0
  17. package/public/js/components/models.js +36 -0
  18. package/public/js/components/server-config.js +349 -0
  19. package/public/js/config/constants.js +102 -0
  20. package/public/js/data-store.js +386 -0
  21. package/public/js/settings-store.js +58 -0
  22. package/public/js/store.js +78 -0
  23. package/public/js/translations/en.js +351 -0
  24. package/public/js/translations/id.js +396 -0
  25. package/public/js/translations/pt.js +287 -0
  26. package/public/js/translations/tr.js +342 -0
  27. package/public/js/translations/zh.js +357 -0
  28. package/public/js/utils/account-actions.js +189 -0
  29. package/public/js/utils/error-handler.js +96 -0
  30. package/public/js/utils/model-config.js +42 -0
  31. package/public/js/utils/validators.js +77 -0
  32. package/public/js/utils.js +69 -0
  33. package/public/views/accounts.html +329 -0
  34. package/public/views/dashboard.html +484 -0
  35. package/public/views/logs.html +97 -0
  36. package/public/views/models.html +331 -0
  37. package/public/views/settings.html +1329 -0
  38. package/src/account-manager/credentials.js +243 -0
  39. package/src/account-manager/index.js +380 -0
  40. package/src/account-manager/onboarding.js +117 -0
  41. package/src/account-manager/rate-limits.js +237 -0
  42. package/src/account-manager/storage.js +136 -0
  43. package/src/account-manager/strategies/base-strategy.js +104 -0
  44. package/src/account-manager/strategies/hybrid-strategy.js +195 -0
  45. package/src/account-manager/strategies/index.js +79 -0
  46. package/src/account-manager/strategies/round-robin-strategy.js +76 -0
  47. package/src/account-manager/strategies/sticky-strategy.js +138 -0
  48. package/src/account-manager/strategies/trackers/health-tracker.js +162 -0
  49. package/src/account-manager/strategies/trackers/index.js +8 -0
  50. package/src/account-manager/strategies/trackers/token-bucket-tracker.js +121 -0
  51. package/src/auth/database.js +169 -0
  52. package/src/auth/oauth.js +419 -0
  53. package/src/auth/token-extractor.js +117 -0
  54. package/src/cli/accounts.js +512 -0
  55. package/src/cli/refresh.js +201 -0
  56. package/src/cli/setup.js +338 -0
  57. package/src/cloudcode/index.js +29 -0
  58. package/src/cloudcode/message-handler.js +386 -0
  59. package/src/cloudcode/model-api.js +248 -0
  60. package/src/cloudcode/rate-limit-parser.js +181 -0
  61. package/src/cloudcode/request-builder.js +93 -0
  62. package/src/cloudcode/session-manager.js +47 -0
  63. package/src/cloudcode/sse-parser.js +121 -0
  64. package/src/cloudcode/sse-streamer.js +293 -0
  65. package/src/cloudcode/streaming-handler.js +492 -0
  66. package/src/config.js +107 -0
  67. package/src/constants.js +278 -0
  68. package/src/errors.js +238 -0
  69. package/src/fallback-config.js +29 -0
  70. package/src/format/content-converter.js +193 -0
  71. package/src/format/index.js +20 -0
  72. package/src/format/request-converter.js +248 -0
  73. package/src/format/response-converter.js +120 -0
  74. package/src/format/schema-sanitizer.js +673 -0
  75. package/src/format/signature-cache.js +88 -0
  76. package/src/format/thinking-utils.js +558 -0
  77. package/src/index.js +146 -0
  78. package/src/modules/usage-stats.js +205 -0
  79. package/src/server.js +861 -0
  80. package/src/utils/claude-config.js +245 -0
  81. package/src/utils/helpers.js +51 -0
  82. package/src/utils/logger.js +142 -0
  83. package/src/utils/native-module-helper.js +162 -0
  84. 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
+ });
@@ -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
+ };