@appchy/jarvis 0.1.7 → 0.1.8
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/bin.js +103 -3
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2519,17 +2519,77 @@ function createUpstreamClient(config) {
|
|
|
2519
2519
|
let ws = null;
|
|
2520
2520
|
let messageHandler = null;
|
|
2521
2521
|
let reconnectTimer = null;
|
|
2522
|
+
let refreshTimer = null;
|
|
2522
2523
|
let closed = false;
|
|
2523
2524
|
let authFailures = 0;
|
|
2525
|
+
let retries = 0;
|
|
2526
|
+
let currentToken = config.token;
|
|
2524
2527
|
const MAX_AUTH_RETRIES = 3;
|
|
2528
|
+
const MAX_RETRIES = 3;
|
|
2529
|
+
const REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
2530
|
+
function getRefreshUrl() {
|
|
2531
|
+
if (config.refreshUrl) return config.refreshUrl;
|
|
2532
|
+
if (!config.refreshToken) return null;
|
|
2533
|
+
return config.apiUrl.replace(/^wss:/, "https:").replace(/^ws:/, "http:") + "/refresh";
|
|
2534
|
+
}
|
|
2535
|
+
function getTokenExpiry(token) {
|
|
2536
|
+
try {
|
|
2537
|
+
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
|
|
2538
|
+
return payload.exp ?? null;
|
|
2539
|
+
} catch {
|
|
2540
|
+
return null;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
function scheduleRefresh() {
|
|
2544
|
+
if (refreshTimer) clearTimeout(refreshTimer);
|
|
2545
|
+
if (!config.refreshToken) return;
|
|
2546
|
+
const exp = getTokenExpiry(currentToken);
|
|
2547
|
+
if (!exp) return;
|
|
2548
|
+
const msUntilExpiry = exp * 1e3 - Date.now();
|
|
2549
|
+
const refreshIn = Math.max(msUntilExpiry - REFRESH_BUFFER_MS, 0);
|
|
2550
|
+
logger.sys.info("[Upstream] Token refresh scheduled", {
|
|
2551
|
+
expiresIn: `${Math.round(msUntilExpiry / 1e3)}s`,
|
|
2552
|
+
refreshIn: `${Math.round(refreshIn / 1e3)}s`
|
|
2553
|
+
});
|
|
2554
|
+
refreshTimer = setTimeout(refreshToken, refreshIn);
|
|
2555
|
+
}
|
|
2556
|
+
async function refreshToken() {
|
|
2557
|
+
const url = getRefreshUrl();
|
|
2558
|
+
if (!url || !config.refreshToken) return false;
|
|
2559
|
+
try {
|
|
2560
|
+
logger.sys.info("[Upstream] Refreshing token...");
|
|
2561
|
+
const res = await fetch(url, {
|
|
2562
|
+
method: "POST",
|
|
2563
|
+
headers: { "Content-Type": "application/json" },
|
|
2564
|
+
body: JSON.stringify({ refreshToken: config.refreshToken })
|
|
2565
|
+
});
|
|
2566
|
+
if (!res.ok) {
|
|
2567
|
+
logger.sys.error("[Upstream] Token refresh failed", { status: res.status });
|
|
2568
|
+
return false;
|
|
2569
|
+
}
|
|
2570
|
+
const { token } = await res.json();
|
|
2571
|
+
currentToken = token;
|
|
2572
|
+
logger.sys.info("[Upstream] Token refreshed successfully");
|
|
2573
|
+
ws?.close();
|
|
2574
|
+
scheduleRefresh();
|
|
2575
|
+
return true;
|
|
2576
|
+
} catch (err) {
|
|
2577
|
+
logger.sys.error("[Upstream] Token refresh error", {
|
|
2578
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2579
|
+
});
|
|
2580
|
+
return false;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2525
2583
|
function connect() {
|
|
2526
2584
|
if (closed) return;
|
|
2527
2585
|
ws = new WebSocket2(config.apiUrl, {
|
|
2528
|
-
headers: { authorization: `Bearer ${
|
|
2586
|
+
headers: { authorization: `Bearer ${currentToken}` }
|
|
2529
2587
|
});
|
|
2530
2588
|
ws.on("open", () => {
|
|
2531
2589
|
authFailures = 0;
|
|
2590
|
+
retries = 0;
|
|
2532
2591
|
logger.sys.info("[Upstream] Connected to cloud", { apiUrl: config.apiUrl });
|
|
2592
|
+
scheduleRefresh();
|
|
2533
2593
|
});
|
|
2534
2594
|
ws.on("message", (raw) => {
|
|
2535
2595
|
try {
|
|
@@ -2545,6 +2605,24 @@ function createUpstreamClient(config) {
|
|
|
2545
2605
|
ws.on("close", (code) => {
|
|
2546
2606
|
if (closed) return;
|
|
2547
2607
|
if (code === 4001 || code === 4003) {
|
|
2608
|
+
if (config.refreshToken) {
|
|
2609
|
+
refreshToken().then((ok) => {
|
|
2610
|
+
if (ok) {
|
|
2611
|
+
reconnectTimer = setTimeout(connect, 1e3);
|
|
2612
|
+
} else {
|
|
2613
|
+
authFailures++;
|
|
2614
|
+
if (authFailures >= MAX_AUTH_RETRIES) {
|
|
2615
|
+
logger.sys.error("[Upstream] Auth failed after max retries, giving up", {
|
|
2616
|
+
code,
|
|
2617
|
+
attempts: authFailures
|
|
2618
|
+
});
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
reconnectTimer = setTimeout(connect, 3e3);
|
|
2622
|
+
}
|
|
2623
|
+
});
|
|
2624
|
+
return;
|
|
2625
|
+
}
|
|
2548
2626
|
authFailures++;
|
|
2549
2627
|
if (authFailures >= MAX_AUTH_RETRIES) {
|
|
2550
2628
|
logger.sys.error("[Upstream] Auth failed after max retries, giving up", {
|
|
@@ -2558,8 +2636,21 @@ function createUpstreamClient(config) {
|
|
|
2558
2636
|
attempt: authFailures,
|
|
2559
2637
|
maxRetries: MAX_AUTH_RETRIES
|
|
2560
2638
|
});
|
|
2639
|
+
reconnectTimer = setTimeout(connect, 3e3);
|
|
2640
|
+
return;
|
|
2561
2641
|
}
|
|
2562
|
-
|
|
2642
|
+
retries++;
|
|
2643
|
+
if (retries >= MAX_RETRIES) {
|
|
2644
|
+
logger.sys.error("[Upstream] Max reconnect attempts reached, giving up", {
|
|
2645
|
+
attempts: retries
|
|
2646
|
+
});
|
|
2647
|
+
return;
|
|
2648
|
+
}
|
|
2649
|
+
logger.sys.info("[Upstream] Disconnected, reconnecting in 3s...", {
|
|
2650
|
+
code,
|
|
2651
|
+
attempt: retries,
|
|
2652
|
+
maxRetries: MAX_RETRIES
|
|
2653
|
+
});
|
|
2563
2654
|
reconnectTimer = setTimeout(connect, 3e3);
|
|
2564
2655
|
});
|
|
2565
2656
|
ws.on("error", (err) => {
|
|
@@ -2582,6 +2673,7 @@ function createUpstreamClient(config) {
|
|
|
2582
2673
|
function close() {
|
|
2583
2674
|
closed = true;
|
|
2584
2675
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
2676
|
+
if (refreshTimer) clearTimeout(refreshTimer);
|
|
2585
2677
|
ws?.close();
|
|
2586
2678
|
}
|
|
2587
2679
|
return { connect, send, onMessage, isConnected, close };
|
|
@@ -4722,6 +4814,7 @@ function createCli() {
|
|
|
4722
4814
|
...existing,
|
|
4723
4815
|
apiUrl: parsed.apiUrl,
|
|
4724
4816
|
token: parsed.jwt,
|
|
4817
|
+
refreshToken: parsed.refreshToken,
|
|
4725
4818
|
userId: parsed.userId,
|
|
4726
4819
|
envId: parsed.envId,
|
|
4727
4820
|
...opts.workspace ? { workspacePath: opts.workspace } : {},
|
|
@@ -4761,7 +4854,14 @@ function createCli() {
|
|
|
4761
4854
|
anthropicApiKey,
|
|
4762
4855
|
useSubscription: !anthropicApiKey,
|
|
4763
4856
|
userId,
|
|
4764
|
-
upstream:
|
|
4857
|
+
upstream: (() => {
|
|
4858
|
+
const apiUrl = process.env.JARVIS_UPSTREAM_URL ?? config?.apiUrl;
|
|
4859
|
+
const token = process.env.JARVIS_UPSTREAM_TOKEN ?? config?.token;
|
|
4860
|
+
if (opts.upstream && apiUrl && token) {
|
|
4861
|
+
return { apiUrl, token, refreshToken: config?.refreshToken };
|
|
4862
|
+
}
|
|
4863
|
+
return void 0;
|
|
4864
|
+
})()
|
|
4765
4865
|
});
|
|
4766
4866
|
return;
|
|
4767
4867
|
}
|