@hienlh/ppm 0.8.21 → 0.8.22
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.22] - 2026-03-24
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **OAuth pre-flight token refresh**: Check token freshness before each SDK query and refresh proactively if expired or expiring within 60s
|
|
7
|
+
- **Auto-refresh on startup**: Run token refresh check immediately on server start instead of waiting 5 minutes for first interval
|
|
8
|
+
- **OAuth auto-refresh on auth failure**: Automatically refresh expired OAuth token and retry when SDK returns `authentication_failed`, instead of just showing error to user
|
|
9
|
+
|
|
3
10
|
## [0.8.21] - 2026-03-24
|
|
4
11
|
|
|
5
12
|
### Fixed
|
package/package.json
CHANGED
|
@@ -545,7 +545,7 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
545
545
|
// Account-based auth injection (multi-account mode)
|
|
546
546
|
// Fallback to existing env (ANTHROPIC_API_KEY) when no accounts configured.
|
|
547
547
|
const accountsEnabled = accountSelector.isEnabled();
|
|
548
|
-
|
|
548
|
+
let account = accountsEnabled ? accountSelector.next() : null;
|
|
549
549
|
if (accountsEnabled && !account) {
|
|
550
550
|
// All accounts in DB but none usable
|
|
551
551
|
const reason = accountSelector.lastFailReason;
|
|
@@ -557,7 +557,12 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
557
557
|
yield { type: "done" as const, sessionId, resultSubtype: "error_auth" };
|
|
558
558
|
return;
|
|
559
559
|
}
|
|
560
|
+
// Pre-flight: ensure OAuth token is fresh before sending to SDK
|
|
560
561
|
if (account) {
|
|
562
|
+
const fresh = await accountService.ensureFreshToken(account.id);
|
|
563
|
+
if (fresh) {
|
|
564
|
+
account = fresh;
|
|
565
|
+
}
|
|
561
566
|
console.log(`[sdk] Using account ${account.id} (${account.email ?? "no-email"})`);
|
|
562
567
|
yield { type: "account_info" as const, accountId: account.id, accountLabel: account.label ?? account.email ?? "Unknown" };
|
|
563
568
|
}
|
|
@@ -116,6 +116,29 @@ class AccountService {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Ensure the access token for an OAuth account is still fresh.
|
|
121
|
+
* If it's expired or about to expire (within 60s), refresh it proactively.
|
|
122
|
+
* Returns the refreshed account with fresh tokens, or null if refresh failed.
|
|
123
|
+
*/
|
|
124
|
+
async ensureFreshToken(id: string): Promise<AccountWithTokens | null> {
|
|
125
|
+
const acc = this.getWithTokens(id);
|
|
126
|
+
if (!acc) return null;
|
|
127
|
+
// Only OAuth tokens need refresh
|
|
128
|
+
if (!acc.accessToken.startsWith("sk-ant-oat")) return acc;
|
|
129
|
+
if (!acc.expiresAt) return acc;
|
|
130
|
+
const nowS = Math.floor(Date.now() / 1000);
|
|
131
|
+
if (acc.expiresAt - nowS > 60) return acc; // still fresh
|
|
132
|
+
try {
|
|
133
|
+
console.log(`[accounts] Pre-flight refresh for ${acc.email ?? id} (expires in ${acc.expiresAt - nowS}s)`);
|
|
134
|
+
await this.refreshAccessToken(id);
|
|
135
|
+
return this.getWithTokens(id);
|
|
136
|
+
} catch (e) {
|
|
137
|
+
console.error(`[accounts] Pre-flight refresh failed for ${id}:`, e);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
119
142
|
/** Find existing account by email or profile UUID */
|
|
120
143
|
private findDuplicate(email?: string | null, profileData?: OAuthProfileData | null): Account | null {
|
|
121
144
|
if (!email && !profileData?.account?.uuid) return null;
|
|
@@ -586,7 +609,7 @@ class AccountService {
|
|
|
586
609
|
const CHECK_INTERVAL_MS = 5 * 60_000;
|
|
587
610
|
const REFRESH_BUFFER_S = 5 * 60;
|
|
588
611
|
|
|
589
|
-
|
|
612
|
+
const refreshExpiring = async () => {
|
|
590
613
|
const accounts = this.list();
|
|
591
614
|
const nowS = Math.floor(Date.now() / 1000);
|
|
592
615
|
for (const acc of accounts) {
|
|
@@ -600,7 +623,11 @@ class AccountService {
|
|
|
600
623
|
console.error(`[accounts] Auto-refresh failed for ${acc.id}:`, e);
|
|
601
624
|
}
|
|
602
625
|
}
|
|
603
|
-
}
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
// Run immediately on startup, then every 5 minutes
|
|
629
|
+
refreshExpiring().catch(() => {});
|
|
630
|
+
this.refreshTimer = setInterval(refreshExpiring, CHECK_INTERVAL_MS);
|
|
604
631
|
|
|
605
632
|
if (typeof this.refreshTimer === "object" && this.refreshTimer !== null && "unref" in this.refreshTimer) {
|
|
606
633
|
(this.refreshTimer as NodeJS.Timeout).unref();
|