@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
package/bin/cli.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
// Read package.json for version
|
|
11
|
+
const packageJson = JSON.parse(
|
|
12
|
+
readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const command = args[0];
|
|
17
|
+
|
|
18
|
+
function showHelp() {
|
|
19
|
+
console.log(`
|
|
20
|
+
antigravity-claude-proxy v${packageJson.version}
|
|
21
|
+
|
|
22
|
+
Proxy server for using Antigravity's Claude models with Claude Code CLI.
|
|
23
|
+
|
|
24
|
+
USAGE:
|
|
25
|
+
antigravity-claude-proxy <command> [options]
|
|
26
|
+
|
|
27
|
+
COMMANDS:
|
|
28
|
+
start Start the proxy server (default port: 8080)
|
|
29
|
+
accounts Manage Google accounts (interactive)
|
|
30
|
+
accounts add Add a new Google account via OAuth
|
|
31
|
+
accounts list List all configured accounts
|
|
32
|
+
accounts remove Remove accounts interactively
|
|
33
|
+
accounts verify Verify account tokens are valid
|
|
34
|
+
accounts clear Remove all accounts
|
|
35
|
+
refresh Check and refresh account tokens
|
|
36
|
+
setup Install Claude Code CLI and create global 'proxy-claude' command
|
|
37
|
+
|
|
38
|
+
OPTIONS:
|
|
39
|
+
--help, -h Show this help message
|
|
40
|
+
--version, -v Show version number
|
|
41
|
+
|
|
42
|
+
ENVIRONMENT:
|
|
43
|
+
PORT Server port (default: 8080)
|
|
44
|
+
|
|
45
|
+
EXAMPLES:
|
|
46
|
+
antigravity-claude-proxy start
|
|
47
|
+
PORT=3000 antigravity-claude-proxy start
|
|
48
|
+
antigravity-claude-proxy accounts add
|
|
49
|
+
antigravity-claude-proxy accounts list
|
|
50
|
+
|
|
51
|
+
CONFIGURATION:
|
|
52
|
+
Claude Code CLI (~/.claude/settings.json):
|
|
53
|
+
{
|
|
54
|
+
"env": {
|
|
55
|
+
"ANTHROPIC_BASE_URL": "http://localhost:8080"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function showVersion() {
|
|
62
|
+
console.log(packageJson.version);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function main() {
|
|
66
|
+
// Handle flags
|
|
67
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
68
|
+
showHelp();
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
73
|
+
showVersion();
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Handle commands
|
|
78
|
+
switch (command) {
|
|
79
|
+
case 'setup':
|
|
80
|
+
await import('../src/cli/setup.js').then(module => module.runSetup());
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
case 'start':
|
|
84
|
+
case undefined:
|
|
85
|
+
// Default to starting the server
|
|
86
|
+
await import('../src/index.js');
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'accounts': {
|
|
90
|
+
// Pass remaining args to accounts CLI
|
|
91
|
+
const subCommand = args[1] || 'add';
|
|
92
|
+
process.argv = ['node', 'accounts-cli.js', subCommand, ...args.slice(2)];
|
|
93
|
+
await import('../src/cli/accounts.js');
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case 'refresh': {
|
|
98
|
+
// Token refresh
|
|
99
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
100
|
+
const quiet = args.includes('--quiet') || args.includes('-q');
|
|
101
|
+
const { runRefresh } = await import('../src/cli/refresh.js');
|
|
102
|
+
await runRefresh({ force, quiet });
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
case 'help':
|
|
107
|
+
showHelp();
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
case 'version':
|
|
111
|
+
showVersion();
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
default:
|
|
115
|
+
console.error(`Unknown command: ${command}`);
|
|
116
|
+
console.error('Run "antigravity-proxy --help" for usage information.');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
main().catch((err) => {
|
|
122
|
+
console.error('Error:', err.message);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kamel-ahmed/proxy-claude",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Proxy server to use Antigravity's Claude models with Claude Code CLI - run 'proxy-claude' to start",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"proxy-claude": "bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"bin",
|
|
13
|
+
"public"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build:css": "tailwindcss -i ./public/css/src/input.css -o ./public/css/style.css --minify",
|
|
17
|
+
"watch:css": "tailwindcss -i ./public/css/src/input.css -o ./public/css/style.css --watch",
|
|
18
|
+
"prepare": "npm run build:css",
|
|
19
|
+
"start": "node src/index.js",
|
|
20
|
+
"dev": "node --watch src/index.js",
|
|
21
|
+
"dev:full": "concurrently \"npm run watch:css\" \"npm run dev\"",
|
|
22
|
+
"accounts": "node src/cli/accounts.js",
|
|
23
|
+
"accounts:add": "node src/cli/accounts.js add",
|
|
24
|
+
"accounts:list": "node src/cli/accounts.js list",
|
|
25
|
+
"accounts:remove": "node src/cli/accounts.js remove",
|
|
26
|
+
"accounts:verify": "node src/cli/accounts.js verify",
|
|
27
|
+
"refresh": "node src/cli/refresh.js",
|
|
28
|
+
"refresh:force": "node src/cli/refresh.js --force",
|
|
29
|
+
"setup": "node bin/cli.js setup",
|
|
30
|
+
"test": "node tests/run-all.cjs",
|
|
31
|
+
"test:signatures": "node tests/test-thinking-signatures.cjs",
|
|
32
|
+
"test:multiturn": "node tests/test-multiturn-thinking-tools.cjs",
|
|
33
|
+
"test:streaming": "node tests/test-multiturn-thinking-tools-streaming.cjs",
|
|
34
|
+
"test:interleaved": "node tests/test-interleaved-thinking.cjs",
|
|
35
|
+
"test:images": "node tests/test-images.cjs",
|
|
36
|
+
"test:caching": "node tests/test-caching-streaming.cjs",
|
|
37
|
+
"test:crossmodel": "node tests/test-cross-model-thinking.cjs",
|
|
38
|
+
"test:oauth": "node tests/test-oauth-no-browser.cjs",
|
|
39
|
+
"test:emptyretry": "node tests/test-empty-response-retry.cjs",
|
|
40
|
+
"test:sanitizer": "node tests/test-schema-sanitizer.cjs",
|
|
41
|
+
"test:strategies": "node tests/test-strategies.cjs"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"claude",
|
|
45
|
+
"anthropic",
|
|
46
|
+
"antigravity",
|
|
47
|
+
"proxy",
|
|
48
|
+
"vertex-ai"
|
|
49
|
+
],
|
|
50
|
+
"author": "Kamel Ahmed",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/kamel-ahmed/proxy-claude.git"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/kamel-ahmed/proxy-claude#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/kamel-ahmed/proxy-claude/issues"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18.0.0"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"async-mutex": "^0.5.0",
|
|
68
|
+
"better-sqlite3": "^12.5.0",
|
|
69
|
+
"cors": "^2.8.5",
|
|
70
|
+
"express": "^4.18.2"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@tailwindcss/forms": "^0.5.7",
|
|
74
|
+
"autoprefixer": "^10.4.16",
|
|
75
|
+
"concurrently": "^8.2.2",
|
|
76
|
+
"daisyui": "^4.12.14",
|
|
77
|
+
"postcss": "^8.4.32",
|
|
78
|
+
"tailwindcss": "^3.4.0"
|
|
79
|
+
}
|
|
80
|
+
}
|
package/public/app.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity Console - Main Entry
|
|
3
|
+
*
|
|
4
|
+
* This file orchestrates Alpine.js initialization.
|
|
5
|
+
* Components are loaded via separate script files that register themselves
|
|
6
|
+
* to window.Components before this script runs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
document.addEventListener('alpine:init', () => {
|
|
10
|
+
// Register Components (loaded from separate files via window.Components)
|
|
11
|
+
Alpine.data('dashboard', window.Components.dashboard);
|
|
12
|
+
Alpine.data('models', window.Components.models);
|
|
13
|
+
Alpine.data('accountManager', window.Components.accountManager);
|
|
14
|
+
Alpine.data('claudeConfig', window.Components.claudeConfig);
|
|
15
|
+
Alpine.data('logsViewer', window.Components.logsViewer);
|
|
16
|
+
|
|
17
|
+
// View Loader Directive
|
|
18
|
+
Alpine.directive('load-view', (el, { expression }, { evaluate }) => {
|
|
19
|
+
if (!window.viewCache) window.viewCache = new Map();
|
|
20
|
+
|
|
21
|
+
// Evaluate the expression to get the actual view name (removes quotes)
|
|
22
|
+
const viewName = evaluate(expression);
|
|
23
|
+
|
|
24
|
+
if (window.viewCache.has(viewName)) {
|
|
25
|
+
el.innerHTML = window.viewCache.get(viewName);
|
|
26
|
+
Alpine.initTree(el);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fetch(`views/${viewName}.html?t=${Date.now()}`)
|
|
31
|
+
.then(response => {
|
|
32
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
33
|
+
return response.text();
|
|
34
|
+
})
|
|
35
|
+
.then(html => {
|
|
36
|
+
// Update cache (optional, or remove if we want always-fresh)
|
|
37
|
+
// keeping cache for session performance, but initial load will now bypass browser cache
|
|
38
|
+
window.viewCache.set(viewName, html);
|
|
39
|
+
el.innerHTML = html;
|
|
40
|
+
Alpine.initTree(el);
|
|
41
|
+
})
|
|
42
|
+
.catch(err => {
|
|
43
|
+
console.error('Failed to load view:', viewName, err);
|
|
44
|
+
el.innerHTML = `<div class="p-4 border border-red-500/50 bg-red-500/10 rounded-lg text-red-400 font-mono text-sm">
|
|
45
|
+
Error loading view: ${viewName}<br>
|
|
46
|
+
<span class="text-xs opacity-75">${err.message}</span>
|
|
47
|
+
</div>`;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Main App Controller
|
|
52
|
+
Alpine.data('app', () => ({
|
|
53
|
+
get connectionStatus() {
|
|
54
|
+
return Alpine.store('data')?.connectionStatus || 'connecting';
|
|
55
|
+
},
|
|
56
|
+
get loading() {
|
|
57
|
+
return Alpine.store('data')?.loading || false;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
sidebarOpen: window.innerWidth >= 1024,
|
|
61
|
+
toggleSidebar() {
|
|
62
|
+
this.sidebarOpen = !this.sidebarOpen;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
init() {
|
|
66
|
+
console.log('App controller initialized');
|
|
67
|
+
|
|
68
|
+
// Handle responsive sidebar transitions
|
|
69
|
+
let lastWidth = window.innerWidth;
|
|
70
|
+
let resizeTimeout = null;
|
|
71
|
+
|
|
72
|
+
window.addEventListener('resize', () => {
|
|
73
|
+
if (resizeTimeout) clearTimeout(resizeTimeout);
|
|
74
|
+
|
|
75
|
+
resizeTimeout = setTimeout(() => {
|
|
76
|
+
const currentWidth = window.innerWidth;
|
|
77
|
+
const lgBreakpoint = 1024;
|
|
78
|
+
|
|
79
|
+
// Desktop -> Mobile: Auto-close sidebar to prevent overlay blocking screen
|
|
80
|
+
if (lastWidth >= lgBreakpoint && currentWidth < lgBreakpoint) {
|
|
81
|
+
this.sidebarOpen = false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Mobile -> Desktop: Auto-open sidebar (restore standard desktop layout)
|
|
85
|
+
if (lastWidth < lgBreakpoint && currentWidth >= lgBreakpoint) {
|
|
86
|
+
this.sidebarOpen = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
lastWidth = currentWidth;
|
|
90
|
+
}, 150);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Theme setup
|
|
94
|
+
document.documentElement.setAttribute('data-theme', 'black');
|
|
95
|
+
document.documentElement.classList.add('dark');
|
|
96
|
+
|
|
97
|
+
// Chart Defaults
|
|
98
|
+
if (typeof Chart !== 'undefined') {
|
|
99
|
+
Chart.defaults.color = window.utils.getThemeColor('--color-text-dim');
|
|
100
|
+
Chart.defaults.borderColor = window.utils.getThemeColor('--color-space-border');
|
|
101
|
+
Chart.defaults.font.family = '"JetBrains Mono", monospace';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Start Data Polling
|
|
105
|
+
this.startAutoRefresh();
|
|
106
|
+
document.addEventListener('refresh-interval-changed', () => this.startAutoRefresh());
|
|
107
|
+
|
|
108
|
+
// Initial Fetch
|
|
109
|
+
Alpine.store('data').fetchData();
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
refreshTimer: null,
|
|
113
|
+
|
|
114
|
+
fetchData() {
|
|
115
|
+
Alpine.store('data').fetchData();
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
startAutoRefresh() {
|
|
119
|
+
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
120
|
+
const interval = parseInt(Alpine.store('settings')?.refreshInterval || 60);
|
|
121
|
+
if (interval > 0) {
|
|
122
|
+
this.refreshTimer = setInterval(() => Alpine.store('data').fetchData(), interval * 1000);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
t(key) {
|
|
127
|
+
return Alpine.store('global')?.t(key) || key;
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
async addAccountWeb(reAuthEmail = null) {
|
|
131
|
+
const password = Alpine.store('global').webuiPassword;
|
|
132
|
+
try {
|
|
133
|
+
const urlPath = reAuthEmail
|
|
134
|
+
? `/api/auth/url?email=${encodeURIComponent(reAuthEmail)}`
|
|
135
|
+
: '/api/auth/url';
|
|
136
|
+
|
|
137
|
+
const { response, newPassword } = await window.utils.request(urlPath, {}, password);
|
|
138
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
139
|
+
|
|
140
|
+
const data = await response.json();
|
|
141
|
+
|
|
142
|
+
if (data.status === 'ok') {
|
|
143
|
+
// Show info toast that OAuth is in progress
|
|
144
|
+
Alpine.store('global').showToast(Alpine.store('global').t('oauthInProgress'), 'info');
|
|
145
|
+
|
|
146
|
+
// Open OAuth window
|
|
147
|
+
const oauthWindow = window.open(data.url, 'google_oauth', 'width=600,height=700,scrollbars=yes');
|
|
148
|
+
|
|
149
|
+
// Poll for account changes instead of relying on postMessage
|
|
150
|
+
// (since OAuth callback is now on port 51121, not this server)
|
|
151
|
+
const initialAccountCount = Alpine.store('data').accounts.length;
|
|
152
|
+
let pollCount = 0;
|
|
153
|
+
const maxPolls = 60; // 2 minutes (2 second intervals)
|
|
154
|
+
let cancelled = false;
|
|
155
|
+
|
|
156
|
+
// Show progress modal
|
|
157
|
+
Alpine.store('global').oauthProgress = {
|
|
158
|
+
active: true,
|
|
159
|
+
current: 0,
|
|
160
|
+
max: maxPolls,
|
|
161
|
+
cancel: () => {
|
|
162
|
+
cancelled = true;
|
|
163
|
+
clearInterval(pollInterval);
|
|
164
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
165
|
+
Alpine.store('global').showToast(Alpine.store('global').t('oauthCancelled'), 'info');
|
|
166
|
+
if (oauthWindow && !oauthWindow.closed) {
|
|
167
|
+
oauthWindow.close();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const pollInterval = setInterval(async () => {
|
|
173
|
+
if (cancelled) {
|
|
174
|
+
clearInterval(pollInterval);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
pollCount++;
|
|
179
|
+
Alpine.store('global').oauthProgress.current = pollCount;
|
|
180
|
+
|
|
181
|
+
// Check if OAuth window was closed manually
|
|
182
|
+
if (oauthWindow && oauthWindow.closed && !cancelled) {
|
|
183
|
+
clearInterval(pollInterval);
|
|
184
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
185
|
+
Alpine.store('global').showToast(Alpine.store('global').t('oauthWindowClosed'), 'warning');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Refresh account list
|
|
190
|
+
await Alpine.store('data').fetchData();
|
|
191
|
+
|
|
192
|
+
// Check if new account was added
|
|
193
|
+
const currentAccountCount = Alpine.store('data').accounts.length;
|
|
194
|
+
if (currentAccountCount > initialAccountCount) {
|
|
195
|
+
clearInterval(pollInterval);
|
|
196
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
197
|
+
|
|
198
|
+
const actionKey = reAuthEmail ? 'accountReauthSuccess' : 'accountAddedSuccess';
|
|
199
|
+
Alpine.store('global').showToast(
|
|
200
|
+
Alpine.store('global').t(actionKey),
|
|
201
|
+
'success'
|
|
202
|
+
);
|
|
203
|
+
document.getElementById('add_account_modal')?.close();
|
|
204
|
+
|
|
205
|
+
if (oauthWindow && !oauthWindow.closed) {
|
|
206
|
+
oauthWindow.close();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Stop polling after max attempts
|
|
211
|
+
if (pollCount >= maxPolls) {
|
|
212
|
+
clearInterval(pollInterval);
|
|
213
|
+
Alpine.store('global').oauthProgress.active = false;
|
|
214
|
+
Alpine.store('global').showToast(
|
|
215
|
+
Alpine.store('global').t('oauthTimeout'),
|
|
216
|
+
'warning'
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}, 2000); // Poll every 2 seconds
|
|
220
|
+
} else {
|
|
221
|
+
Alpine.store('global').showToast(data.error || Alpine.store('global').t('failedToGetAuthUrl'), 'error');
|
|
222
|
+
}
|
|
223
|
+
} catch (e) {
|
|
224
|
+
Alpine.store('global').showToast(Alpine.store('global').t('failedToStartOAuth') + ': ' + e.message, 'error');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}));
|
|
228
|
+
});
|