antri_cli 1.57.50 → 1.57.52
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/dist/cli/shortcuts.d.ts.map +1 -1
- package/dist/cli/shortcuts.js +39 -7
- package/dist/cli/shortcuts.js.map +1 -1
- package/dist/core/config.d.ts +5 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +64 -2
- package/dist/core/config.js.map +1 -1
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js +43 -14
- package/dist/core/tools.js.map +1 -1
- package/dist/core/updater.d.ts +1 -1
- package/dist/core/updater.js +1 -1
- package/dist/desktop/public/app.js +362 -0
- package/dist/desktop/public/index.html +77 -2
- package/dist/desktop/public/style.css +97 -0
- package/dist/desktop/server.d.ts +12 -1
- package/dist/desktop/server.d.ts.map +1 -1
- package/dist/desktop/server.js +160 -28
- package/dist/desktop/server.js.map +1 -1
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/gemini.d.ts +6 -0
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +37 -2
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +14 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +3 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +18 -1
- package/dist/providers/openai.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<header class="app-header">
|
|
16
16
|
<div class="brand-zone">
|
|
17
17
|
<span class="logo-text">ANTRI</span>
|
|
18
|
-
<span class="version-tag">v1.57.
|
|
18
|
+
<span class="version-tag">v1.57.51</span>
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
<!-- Mode Toggle -->
|
|
@@ -106,6 +106,9 @@
|
|
|
106
106
|
<button class="nav-item" onclick="showTab('suggestions')">
|
|
107
107
|
<span class="nav-label">💡 Suggestions & Ideas</span>
|
|
108
108
|
</button>
|
|
109
|
+
<button class="nav-item" onclick="showTab('settings')">
|
|
110
|
+
<span class="nav-label">⚙️ Settings & Cloud Run</span>
|
|
111
|
+
</button>
|
|
109
112
|
|
|
110
113
|
<div class="sidebar-footer">
|
|
111
114
|
<div class="sync-status">
|
|
@@ -890,6 +893,73 @@
|
|
|
890
893
|
</div>
|
|
891
894
|
</section>
|
|
892
895
|
|
|
896
|
+
<!-- TAB: SETTINGS & GOOGLE CLOUD RUN -->
|
|
897
|
+
<section id="tab-settings" class="tab-panel">
|
|
898
|
+
<div class="panel-header">
|
|
899
|
+
<div>
|
|
900
|
+
<h2>⚙️ Cloud Run & Multi-Key Settings</h2>
|
|
901
|
+
<p class="subtitle">Configure Google Cloud Run backend endpoints, multi-API key auto-rotation pools, and platform sync.</p>
|
|
902
|
+
</div>
|
|
903
|
+
</div>
|
|
904
|
+
|
|
905
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(400px, 1fr));gap:20px;margin-top:16px;">
|
|
906
|
+
<!-- CARD 1: Google Cloud Run Backend Endpoint -->
|
|
907
|
+
<div class="card" style="background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:20px;">
|
|
908
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;">
|
|
909
|
+
<h3 style="margin:0;font-size:16px;color:var(--text);display:flex;align-items:center;gap:8px;">
|
|
910
|
+
<span>☁️</span> Google Cloud Run Backend (.run.app)
|
|
911
|
+
</h3>
|
|
912
|
+
<span id="cloud-status-badge" class="badge badge-idle" style="padding:4px 8px;font-size:11px;">Local Node</span>
|
|
913
|
+
</div>
|
|
914
|
+
<p style="font-size:13px;color:var(--text-muted);margin-bottom:16px;">
|
|
915
|
+
Connect ANTRI Desktop & CLI to your Google Cloud Run container. Automatically inherits Gemini 3.7 / 3.5 models and tools.
|
|
916
|
+
</p>
|
|
917
|
+
<div style="margin-bottom:12px;">
|
|
918
|
+
<label style="display:block;font-size:12px;font-weight:600;margin-bottom:6px;">Cloud Run URL</label>
|
|
919
|
+
<input type="text" id="settings-backend-url" class="modal-input" placeholder="https://antri-backend-xxxxx-uc.a.run.app" />
|
|
920
|
+
</div>
|
|
921
|
+
<div style="display:flex;gap:8px;">
|
|
922
|
+
<button class="action-btn" onclick="saveCloudBackendUrl()" style="flex:1;">⚡ Connect Cloud Run</button>
|
|
923
|
+
<button class="action-btn-secondary" onclick="testCloudBackendHealth()">🔍 Test Health</button>
|
|
924
|
+
</div>
|
|
925
|
+
<div id="cloud-health-output" style="margin-top:12px;font-size:12px;display:none;padding:8px 12px;border-radius:6px;background:rgba(255,255,255,0.03);border:1px solid var(--border);"></div>
|
|
926
|
+
</div>
|
|
927
|
+
|
|
928
|
+
<!-- CARD 2: Multi-API Key Auto-Rotation Pool -->
|
|
929
|
+
<div class="card" style="background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:20px;">
|
|
930
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;">
|
|
931
|
+
<h3 style="margin:0;font-size:16px;color:var(--text);display:flex;align-items:center;gap:8px;">
|
|
932
|
+
<span>🔑</span> Multi-API Key Auto-Rotation Pool
|
|
933
|
+
</h3>
|
|
934
|
+
<span id="keys-pool-count" class="badge" style="background:#7c3aed;color:#fff;padding:4px 8px;font-size:11px;">1 Key Active</span>
|
|
935
|
+
</div>
|
|
936
|
+
<p style="font-size:13px;color:var(--text-muted);margin-bottom:16px;">
|
|
937
|
+
Provide 3-4 API keys. When one key reaches rate limits (429) or runs out of credits, ANTRI seamlessly spins up the next key with zero downtime.
|
|
938
|
+
</p>
|
|
939
|
+
<div style="margin-bottom:12px;">
|
|
940
|
+
<label style="display:block;font-size:12px;font-weight:600;margin-bottom:6px;">Select Provider</label>
|
|
941
|
+
<select id="settings-key-provider" class="modal-input" onchange="onSettingsProviderChange()">
|
|
942
|
+
<option value="gemini">Google Gemini (Recommended)</option>
|
|
943
|
+
<option value="openai">OpenAI</option>
|
|
944
|
+
<option value="anthropic">Anthropic</option>
|
|
945
|
+
<option value="cerebras">Cerebras</option>
|
|
946
|
+
<option value="cohere">Cohere</option>
|
|
947
|
+
<option value="deepseek">DeepSeek</option>
|
|
948
|
+
<option value="nvidia_nim">NVIDIA NIM</option>
|
|
949
|
+
</select>
|
|
950
|
+
</div>
|
|
951
|
+
<div style="margin-bottom:12px;">
|
|
952
|
+
<label style="display:block;font-size:12px;font-weight:600;margin-bottom:6px;">API Key Pool (Paste 1-4 keys, comma or newline separated)</label>
|
|
953
|
+
<textarea id="settings-keys-textarea" class="modal-input" rows="3" placeholder="AIzaSyKey1..., AIzaSyKey2..., AIzaSyKey3..."></textarea>
|
|
954
|
+
</div>
|
|
955
|
+
<div style="display:flex;gap:8px;">
|
|
956
|
+
<button class="action-btn" onclick="saveApiKeyPool()" style="flex:1;">💾 Save Key Pool</button>
|
|
957
|
+
<button class="action-btn-secondary" onclick="rotateKeyPoolManually()">🔄 Rotate Key</button>
|
|
958
|
+
</div>
|
|
959
|
+
</div>
|
|
960
|
+
</div>
|
|
961
|
+
</section>
|
|
962
|
+
|
|
893
963
|
</main>
|
|
894
964
|
</div>
|
|
895
965
|
|
|
@@ -929,10 +999,15 @@
|
|
|
929
999
|
<label style="display:block;font-size:12px;font-weight:600;margin-bottom:6px;">Email Address</label>
|
|
930
1000
|
<input type="email" id="modal-email-input" class="modal-input" placeholder="user@gmail.com" />
|
|
931
1001
|
</div>
|
|
932
|
-
<div style="margin-bottom:
|
|
1002
|
+
<div style="margin-bottom:12px;">
|
|
933
1003
|
<label style="display:block;font-size:12px;font-weight:600;margin-bottom:6px;">Password</label>
|
|
934
1004
|
<input type="password" id="modal-pass-input" class="modal-input" placeholder="••••••••" />
|
|
935
1005
|
</div>
|
|
1006
|
+
<div style="margin-bottom:16px;">
|
|
1007
|
+
<label style="display:block;font-size:12px;font-weight:600;margin-bottom:6px;">Google Cloud Run Backend URL (Optional)</label>
|
|
1008
|
+
<input type="text" id="modal-backend-url-input" class="modal-input" placeholder="https://antri-backend-xxxxx-uc.a.run.app" />
|
|
1009
|
+
<span style="font-size:11px;color:var(--text-muted);display:block;margin-top:4px;">When set, automatically auto-configures API & LLM models via your Cloud Run container.</span>
|
|
1010
|
+
</div>
|
|
936
1011
|
<button class="action-btn" style="width:100%;margin-bottom:8px;" onclick="submitDesktopLogin()">Sign In / Register</button>
|
|
937
1012
|
</div>
|
|
938
1013
|
<div id="auth-logged-view" style="display:none;text-align:center;padding:12px 0;">
|
|
@@ -1427,6 +1427,103 @@ body.antri-app {
|
|
|
1427
1427
|
color: #fff;
|
|
1428
1428
|
}
|
|
1429
1429
|
|
|
1430
|
+
/* Global Floating Permission Request Banner (for CLI & Desktop) */
|
|
1431
|
+
.global-permission-overlay {
|
|
1432
|
+
position: fixed;
|
|
1433
|
+
top: 24px;
|
|
1434
|
+
right: 24px;
|
|
1435
|
+
z-index: 10000;
|
|
1436
|
+
max-width: 440px;
|
|
1437
|
+
width: calc(100vw - 48px);
|
|
1438
|
+
display: flex;
|
|
1439
|
+
flex-direction: column;
|
|
1440
|
+
gap: 12px;
|
|
1441
|
+
pointer-events: none;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
.global-permission-card {
|
|
1445
|
+
pointer-events: auto;
|
|
1446
|
+
background: rgba(18, 18, 24, 0.95);
|
|
1447
|
+
border: 1.5px solid #f59e0b;
|
|
1448
|
+
border-radius: 12px;
|
|
1449
|
+
padding: 16px;
|
|
1450
|
+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.7), 0 0 24px rgba(245, 158, 11, 0.25);
|
|
1451
|
+
backdrop-filter: blur(20px);
|
|
1452
|
+
color: #f1f5f9;
|
|
1453
|
+
animation: slideInRight 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
@keyframes slideInRight {
|
|
1457
|
+
from { transform: translateX(100%); opacity: 0; }
|
|
1458
|
+
to { transform: translateX(0); opacity: 1; }
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
.global-permission-card .perm-header {
|
|
1462
|
+
display: flex;
|
|
1463
|
+
align-items: center;
|
|
1464
|
+
gap: 8px;
|
|
1465
|
+
font-weight: 800;
|
|
1466
|
+
font-size: 12px;
|
|
1467
|
+
color: #f59e0b;
|
|
1468
|
+
text-transform: uppercase;
|
|
1469
|
+
letter-spacing: 0.05em;
|
|
1470
|
+
margin-bottom: 6px;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.global-permission-card .perm-source {
|
|
1474
|
+
font-size: 10px;
|
|
1475
|
+
font-weight: 700;
|
|
1476
|
+
padding: 2px 6px;
|
|
1477
|
+
border-radius: 4px;
|
|
1478
|
+
background: rgba(245, 158, 11, 0.2);
|
|
1479
|
+
color: #fbbf24;
|
|
1480
|
+
margin-left: auto;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
.global-permission-card .perm-tool-name {
|
|
1484
|
+
font-family: var(--font-mono, monospace);
|
|
1485
|
+
font-weight: 700;
|
|
1486
|
+
color: #38bdf8;
|
|
1487
|
+
font-size: 14px;
|
|
1488
|
+
margin-bottom: 6px;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
.global-permission-card .perm-args-box {
|
|
1492
|
+
background: #09090f;
|
|
1493
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
1494
|
+
border-radius: 8px;
|
|
1495
|
+
padding: 8px 10px;
|
|
1496
|
+
font-family: var(--font-mono, monospace);
|
|
1497
|
+
font-size: 11px;
|
|
1498
|
+
color: #94a3b8;
|
|
1499
|
+
max-height: 90px;
|
|
1500
|
+
overflow-y: auto;
|
|
1501
|
+
word-break: break-all;
|
|
1502
|
+
white-space: pre-wrap;
|
|
1503
|
+
margin-bottom: 12px;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
.global-permission-card .perm-actions {
|
|
1507
|
+
display: flex;
|
|
1508
|
+
gap: 8px;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
.global-permission-card .perm-actions button {
|
|
1512
|
+
flex: 1;
|
|
1513
|
+
padding: 8px 10px;
|
|
1514
|
+
border-radius: 6px;
|
|
1515
|
+
font-size: 12px;
|
|
1516
|
+
font-weight: 700;
|
|
1517
|
+
cursor: pointer;
|
|
1518
|
+
border: none;
|
|
1519
|
+
transition: transform 0.1s, opacity 0.15s;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
.global-permission-card .perm-actions button:hover {
|
|
1523
|
+
opacity: 0.9;
|
|
1524
|
+
transform: translateY(-1px);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1430
1527
|
/* Artifacts Hub & Interactive Cards */
|
|
1431
1528
|
.artifacts-container {
|
|
1432
1529
|
display: flex;
|
package/dist/desktop/server.d.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Real-Time Desktop Permission Bridge
|
|
3
|
+
* Allows CLI and Desktop to seamlessly share tool authorization dialogs.
|
|
4
|
+
*/
|
|
5
|
+
export declare class DesktopPermissionBridge {
|
|
6
|
+
private static sseClients;
|
|
7
|
+
private static pendingPermissions;
|
|
8
|
+
static registerClient(sender: (event: string, data: any) => void): () => void;
|
|
9
|
+
static hasActiveClients(): boolean;
|
|
10
|
+
static requestPermission(name: string, args: Record<string, any>, source?: 'cli' | 'desktop'): Promise<boolean>;
|
|
11
|
+
static resolvePermission(requestId: string, allowed: boolean, alwaysAllow?: boolean): boolean;
|
|
12
|
+
}
|
|
1
13
|
export declare class DesktopServer {
|
|
2
14
|
private server;
|
|
3
15
|
private port;
|
|
4
16
|
private activeAgent;
|
|
5
|
-
private pendingPermissions;
|
|
6
17
|
private currentSseSender;
|
|
7
18
|
constructor();
|
|
8
19
|
private setupPermissionHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/desktop/server.ts"],"names":[],"mappings":"AAuCA,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,IAAI,CAA4C;IACxD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/desktop/server.ts"],"names":[],"mappings":"AAuCA;;;GAGG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAiD;IAC1E,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAiD;WAEpE,cAAc,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;WAOtE,gBAAgB,IAAI,OAAO;WAI3B,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAE,KAAK,GAAG,SAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;WAyB/G,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO;CAiBrG;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,IAAI,CAA4C;IACxD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,gBAAgB,CAAqD;;IAO7E,OAAO,CAAC,sBAAsB;IAUjB,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAkGxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAUpB,SAAS;WAu8BH,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBnD"}
|
package/dist/desktop/server.js
CHANGED
|
@@ -34,11 +34,68 @@ function getPublicDir() {
|
|
|
34
34
|
}
|
|
35
35
|
return path.join(__dirname, 'public');
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Global Real-Time Desktop Permission Bridge
|
|
39
|
+
* Allows CLI and Desktop to seamlessly share tool authorization dialogs.
|
|
40
|
+
*/
|
|
41
|
+
export class DesktopPermissionBridge {
|
|
42
|
+
static sseClients = new Set();
|
|
43
|
+
static pendingPermissions = new Map();
|
|
44
|
+
static registerClient(sender) {
|
|
45
|
+
this.sseClients.add(sender);
|
|
46
|
+
return () => {
|
|
47
|
+
this.sseClients.delete(sender);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
static hasActiveClients() {
|
|
51
|
+
return this.sseClients.size > 0;
|
|
52
|
+
}
|
|
53
|
+
static requestPermission(name, args, source = 'cli') {
|
|
54
|
+
const reqId = `perm_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
55
|
+
for (const sender of this.sseClients) {
|
|
56
|
+
try {
|
|
57
|
+
sender('permission_request', {
|
|
58
|
+
requestId: reqId,
|
|
59
|
+
name,
|
|
60
|
+
args,
|
|
61
|
+
source,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (_) { }
|
|
65
|
+
}
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
this.pendingPermissions.set(reqId, resolve);
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
if (this.pendingPermissions.has(reqId)) {
|
|
70
|
+
this.pendingPermissions.delete(reqId);
|
|
71
|
+
resolve(false);
|
|
72
|
+
}
|
|
73
|
+
}, 120000);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
static resolvePermission(requestId, allowed, alwaysAllow) {
|
|
77
|
+
if (alwaysAllow) {
|
|
78
|
+
configManager.setAlwaysAllow(true);
|
|
79
|
+
}
|
|
80
|
+
const resolver = this.pendingPermissions.get(requestId);
|
|
81
|
+
if (resolver) {
|
|
82
|
+
this.pendingPermissions.delete(requestId);
|
|
83
|
+
resolver(allowed);
|
|
84
|
+
for (const sender of this.sseClients) {
|
|
85
|
+
try {
|
|
86
|
+
sender('permission_resolved', { requestId, allowed });
|
|
87
|
+
}
|
|
88
|
+
catch (_) { }
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
37
95
|
export class DesktopServer {
|
|
38
96
|
server = null;
|
|
39
97
|
port = parseInt(process.env.PORT || '3456', 10);
|
|
40
98
|
activeAgent;
|
|
41
|
-
pendingPermissions = new Map();
|
|
42
99
|
currentSseSender = null;
|
|
43
100
|
constructor() {
|
|
44
101
|
this.activeAgent = new AntriAgent(configManager.get());
|
|
@@ -51,23 +108,7 @@ export class DesktopServer {
|
|
|
51
108
|
return true;
|
|
52
109
|
if (!ToolExecutor.isSensitive(name))
|
|
53
110
|
return true;
|
|
54
|
-
|
|
55
|
-
if (this.currentSseSender) {
|
|
56
|
-
this.currentSseSender('permission_request', {
|
|
57
|
-
requestId: reqId,
|
|
58
|
-
name,
|
|
59
|
-
args,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
return new Promise((resolve) => {
|
|
63
|
-
this.pendingPermissions.set(reqId, resolve);
|
|
64
|
-
setTimeout(() => {
|
|
65
|
-
if (this.pendingPermissions.has(reqId)) {
|
|
66
|
-
this.pendingPermissions.delete(reqId);
|
|
67
|
-
resolve(false);
|
|
68
|
-
}
|
|
69
|
-
}, 90000);
|
|
70
|
-
});
|
|
111
|
+
return DesktopPermissionBridge.requestPermission(name, args, 'desktop');
|
|
71
112
|
});
|
|
72
113
|
}
|
|
73
114
|
async start() {
|
|
@@ -464,20 +505,38 @@ export class DesktopServer {
|
|
|
464
505
|
}
|
|
465
506
|
return;
|
|
466
507
|
}
|
|
508
|
+
// GET /api/events (Global Real-Time Desktop SSE Event Stream)
|
|
509
|
+
if (pathname === '/api/events' && req.method === 'GET') {
|
|
510
|
+
res.writeHead(200, {
|
|
511
|
+
'Content-Type': 'text/event-stream',
|
|
512
|
+
'Cache-Control': 'no-cache',
|
|
513
|
+
Connection: 'keep-alive',
|
|
514
|
+
});
|
|
515
|
+
const sendEvent = (event, data) => {
|
|
516
|
+
res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
517
|
+
};
|
|
518
|
+
const unregister = DesktopPermissionBridge.registerClient(sendEvent);
|
|
519
|
+
sendEvent('connected', { timestamp: Date.now() });
|
|
520
|
+
const heartbeat = setInterval(() => {
|
|
521
|
+
try {
|
|
522
|
+
res.write(': keepalive\n\n');
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
clearInterval(heartbeat);
|
|
526
|
+
}
|
|
527
|
+
}, 15000);
|
|
528
|
+
req.on('close', () => {
|
|
529
|
+
clearInterval(heartbeat);
|
|
530
|
+
unregister();
|
|
531
|
+
});
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
467
534
|
// POST /api/permission/response (Desktop tool confirmation response)
|
|
468
535
|
if (pathname === '/api/permission/response' && req.method === 'POST') {
|
|
469
536
|
const { requestId, allowed, alwaysAllow } = payload;
|
|
470
|
-
|
|
471
|
-
configManager.setAlwaysAllow(true);
|
|
472
|
-
this.activeAgent.updateConfig(configManager.get());
|
|
473
|
-
}
|
|
474
|
-
const resolver = this.pendingPermissions.get(requestId);
|
|
475
|
-
if (resolver) {
|
|
476
|
-
this.pendingPermissions.delete(requestId);
|
|
477
|
-
resolver(!!allowed);
|
|
478
|
-
}
|
|
537
|
+
const handled = DesktopPermissionBridge.resolvePermission(requestId, !!allowed, !!alwaysAllow);
|
|
479
538
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
480
|
-
res.end(JSON.stringify({ success:
|
|
539
|
+
res.end(JSON.stringify({ success: handled }));
|
|
481
540
|
return;
|
|
482
541
|
}
|
|
483
542
|
// POST /api/chat (Real-Time SSE Token Streaming)
|
|
@@ -931,6 +990,79 @@ export class DesktopServer {
|
|
|
931
990
|
res.end(JSON.stringify({ output: result }));
|
|
932
991
|
return;
|
|
933
992
|
}
|
|
993
|
+
// POST /api/config/backend (Configure Cloud Run endpoint and auto-configure)
|
|
994
|
+
if (pathname === '/api/config/backend' && req.method === 'POST') {
|
|
995
|
+
const url = (payload.url || '').trim();
|
|
996
|
+
configManager.setBackendUrl(url);
|
|
997
|
+
this.activeAgent.updateConfig(configManager.get());
|
|
998
|
+
let healthData = null;
|
|
999
|
+
let healthOk = false;
|
|
1000
|
+
if (url) {
|
|
1001
|
+
try {
|
|
1002
|
+
const resp = await fetch(`${url.replace(/\/$/, '')}/api/health`);
|
|
1003
|
+
if (resp.ok) {
|
|
1004
|
+
healthData = await resp.json();
|
|
1005
|
+
healthOk = true;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
catch (_) { }
|
|
1009
|
+
}
|
|
1010
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1011
|
+
res.end(JSON.stringify({ success: true, backendUrl: url, healthOk, health: healthData }));
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
// POST /api/config/keys (Save Multi-Key API Key Pool with auto-rotation)
|
|
1015
|
+
if (pathname === '/api/config/keys' && req.method === 'POST') {
|
|
1016
|
+
const provider = payload.provider || config.provider;
|
|
1017
|
+
const rawKeys = payload.keys || '';
|
|
1018
|
+
configManager.setApiKey(provider, rawKeys);
|
|
1019
|
+
this.activeAgent.updateConfig(configManager.get());
|
|
1020
|
+
const keysList = configManager.getApiKeysList(provider);
|
|
1021
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1022
|
+
res.end(JSON.stringify({ success: true, provider, keysCount: keysList.length, activeKey: keysList[0] || '' }));
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
// POST /api/config/key-rotate (Rotate to next key in pool)
|
|
1026
|
+
if (pathname === '/api/config/key-rotate' && req.method === 'POST') {
|
|
1027
|
+
const provider = payload.provider || config.provider;
|
|
1028
|
+
const nextKey = configManager.rotateApiKey(provider);
|
|
1029
|
+
this.activeAgent.updateConfig(configManager.get());
|
|
1030
|
+
const keysList = configManager.getApiKeysList(provider);
|
|
1031
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1032
|
+
res.end(JSON.stringify({ success: true, provider, activeKey: nextKey, totalKeys: keysList.length }));
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
// POST /api/auth/login (Desktop Login & optional Cloud Run auto-config)
|
|
1036
|
+
if (pathname === '/api/auth/login' && req.method === 'POST') {
|
|
1037
|
+
const { AuthManager } = await import('../cloud/auth.js');
|
|
1038
|
+
const email = payload.email || '';
|
|
1039
|
+
const password = payload.password || 'password123';
|
|
1040
|
+
const backendUrl = (payload.backendUrl || '').trim();
|
|
1041
|
+
if (backendUrl) {
|
|
1042
|
+
configManager.setBackendUrl(backendUrl);
|
|
1043
|
+
}
|
|
1044
|
+
let authRes = await AuthManager.login(email, password);
|
|
1045
|
+
if (!authRes.success) {
|
|
1046
|
+
authRes = await AuthManager.register(email, password);
|
|
1047
|
+
}
|
|
1048
|
+
if (authRes.success) {
|
|
1049
|
+
configManager.reloadForUser();
|
|
1050
|
+
this.activeAgent.updateConfig(configManager.get());
|
|
1051
|
+
}
|
|
1052
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1053
|
+
res.end(JSON.stringify(authRes));
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
// POST /api/auth/logout (Desktop Logout)
|
|
1057
|
+
if (pathname === '/api/auth/logout' && req.method === 'POST') {
|
|
1058
|
+
const { AuthManager } = await import('../cloud/auth.js');
|
|
1059
|
+
AuthManager.logout();
|
|
1060
|
+
configManager.reloadForUser('default_user');
|
|
1061
|
+
this.activeAgent.updateConfig(configManager.get());
|
|
1062
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1063
|
+
res.end(JSON.stringify({ success: true }));
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
934
1066
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
935
1067
|
res.end(JSON.stringify({ error: 'Endpoint not found' }));
|
|
936
1068
|
}
|