@hienlh/ppm 0.9.75 → 0.9.76

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,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.76] - 2026-04-09
4
+
5
+ ### Fixed
6
+ - **Concurrent token refresh race**: Multiple sessions starting near-simultaneously all refreshed the same token. Removed redundant in-memory freshness check — `ensureFreshToken()` now re-reads from DB, so concurrent sessions see the already-refreshed token.
7
+
3
8
  ## [0.9.75] - 2026-04-09
4
9
 
5
10
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.9.75",
3
+ "version": "0.9.76",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -688,18 +688,9 @@ export class ClaudeAgentSdkProvider implements AIProvider {
688
688
  const expiresIn = account.expiresAt ? account.expiresAt - nowS : null;
689
689
  console.log(`[sdk] Using account ${account.id} (${account.email ?? "no-email"}) token_expires_in=${expiresIn}s`);
690
690
 
691
- // Check if token needs refresh
692
- const isOAuth = account.accessToken.startsWith("sk-ant-oat");
693
- const needsRefresh = isOAuth && account.expiresAt && (account.expiresAt - nowS <= 3600);
694
-
695
- if (!needsRefresh) {
696
- // Token fresh or API key — proceed
697
- yield { type: "account_info" as const, accountId: account.id, accountLabel };
698
- break;
699
- }
700
-
701
- // Token expiring — attempt refresh
702
- yield { type: "status_update" as const, phase: "refreshing" as const, message: `Refreshing token for ${accountLabel}...`, accountLabel };
691
+ // ensureFreshToken re-reads DB (picks up concurrent refreshes) and
692
+ // only refreshes if truly needed — safe to call unconditionally.
693
+ yield { type: "status_update" as const, phase: "refreshing" as const, message: `Checking token for ${accountLabel}...`, accountLabel };
703
694
  const fresh = await accountService.ensureFreshToken(account.id);
704
695
 
705
696
  if (fresh) {