@hienlh/ppm 0.8.22 → 0.8.23

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.23] - 2026-03-24
4
+
5
+ ### Fixed
6
+ - **Stop disabling accounts on background refresh failure**: Startup and background auto-refresh no longer disable accounts when refresh fails — only actual query-time failures disable accounts. Prevents all accounts being disabled after server restart.
7
+ - **Better refresh error logging**: Log response body from Anthropic when token refresh fails (was only logging status code)
8
+
3
9
  ## [0.8.22] - 2026-03-24
4
10
 
5
11
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.8.22",
3
+ "version": "0.8.23",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -131,7 +131,7 @@ class AccountService {
131
131
  if (acc.expiresAt - nowS > 60) return acc; // still fresh
132
132
  try {
133
133
  console.log(`[accounts] Pre-flight refresh for ${acc.email ?? id} (expires in ${acc.expiresAt - nowS}s)`);
134
- await this.refreshAccessToken(id);
134
+ await this.refreshAccessToken(id, false);
135
135
  return this.getWithTokens(id);
136
136
  } catch (e) {
137
137
  console.error(`[accounts] Pre-flight refresh failed for ${id}:`, e);
@@ -502,7 +502,12 @@ class AccountService {
502
502
  };
503
503
  }
504
504
 
505
- async refreshAccessToken(accountId: string): Promise<void> {
505
+ /**
506
+ * Refresh an OAuth access token using the stored refresh token.
507
+ * @param disableOnFail - if true, disable the account when refresh fails (default: true).
508
+ * Background/startup refresh should pass false to avoid disabling accounts prematurely.
509
+ */
510
+ async refreshAccessToken(accountId: string, disableOnFail = true): Promise<void> {
506
511
  const account = this.getWithTokens(accountId);
507
512
  if (!account) throw new Error(`Account ${accountId} not found`);
508
513
  const res = await fetch(OAUTH_TOKEN_URL, {
@@ -515,14 +520,19 @@ class AccountService {
515
520
  }),
516
521
  });
517
522
  if (!res.ok) {
518
- this.setDisabled(accountId);
519
- throw new Error(`Token refresh failed for account ${accountId}: ${res.status}`);
523
+ const errorBody = await res.text().catch(() => "");
524
+ console.error(`[accounts] Refresh failed for ${accountId}: ${res.status} ${errorBody}`);
525
+ if (disableOnFail) {
526
+ this.setDisabled(accountId);
527
+ }
528
+ throw new Error(`Token refresh failed for account ${accountId}: ${res.status} ${errorBody}`);
520
529
  }
521
530
  const data = await res.json() as {
522
531
  access_token: string;
523
532
  refresh_token?: string;
524
533
  expires_in: number;
525
534
  };
535
+ console.log(`[accounts] Token refreshed for ${account.email ?? accountId} (expires_in=${data.expires_in}s, new_refresh=${!!data.refresh_token})`);
526
536
  this.updateTokens(
527
537
  accountId,
528
538
  data.access_token,
@@ -618,7 +628,7 @@ class AccountService {
618
628
  if (acc.expiresAt - nowS > REFRESH_BUFFER_S) continue;
619
629
  console.log(`[accounts] Auto-refreshing token for ${acc.email ?? acc.id}`);
620
630
  try {
621
- await this.refreshAccessToken(acc.id);
631
+ await this.refreshAccessToken(acc.id, false);
622
632
  } catch (e) {
623
633
  console.error(`[accounts] Auto-refresh failed for ${acc.id}:`, e);
624
634
  }