@hienlh/ppm 0.8.25 → 0.8.27
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,12 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [0.8.
|
|
3
|
+
## [0.8.27] - 2026-03-24
|
|
4
4
|
|
|
5
5
|
### Changed
|
|
6
6
|
- **Temporary export/import**: Exported accounts no longer include refresh tokens — imported accounts are temporary (~1h access-only). Prevents token rotation conflicts between machines.
|
|
7
7
|
- **Temporary account UI**: Shows "Temporary" badge for accounts without refresh token, "Expired" badge when expired. Expired temporary accounts cannot be re-enabled.
|
|
8
8
|
- **Auto-cleanup**: Expired temporary accounts (no refresh token) are automatically deleted after 7 days.
|
|
9
9
|
- **Export warning**: Export dialog now explains that exported accounts are temporary and the importing machine should login directly for permanent access.
|
|
10
|
+
- **Invalid refresh token cleanup**: When refresh fails with `invalid_grant`, clears the refresh token so the account becomes temporary (same lifecycle rules apply: can't re-enable when expired, auto-deleted after 7 days)
|
|
11
|
+
- **Skip expired accounts in usage**: Expired temporary accounts are excluded from usage polling and usage panel — no wasted API calls or UI clutter
|
|
10
12
|
|
|
11
13
|
## [0.8.24] - 2026-03-24
|
|
12
14
|
|
package/package.json
CHANGED
|
@@ -530,6 +530,11 @@ class AccountService {
|
|
|
530
530
|
if (!res.ok) {
|
|
531
531
|
const errorBody = await res.text().catch(() => "");
|
|
532
532
|
console.error(`[accounts] Refresh failed for ${accountId}: ${res.status} ${errorBody}`);
|
|
533
|
+
// invalid_grant = refresh token permanently dead → clear it so account becomes temporary
|
|
534
|
+
if (errorBody.includes("invalid_grant")) {
|
|
535
|
+
console.log(`[accounts] Clearing invalid refresh token for ${account.email ?? accountId} — account is now temporary`);
|
|
536
|
+
updateAccount(accountId, { refresh_token: encrypt("") });
|
|
537
|
+
}
|
|
533
538
|
if (disableOnFail) {
|
|
534
539
|
this.setDisabled(accountId);
|
|
535
540
|
}
|
|
@@ -169,8 +169,11 @@ function persistIfChanged(data: ClaudeUsage, accountId: string | null): void {
|
|
|
169
169
|
|
|
170
170
|
async function fetchAllAccountUsages(): Promise<void> {
|
|
171
171
|
const accounts = accountService.list();
|
|
172
|
+
const nowS = Math.floor(Date.now() / 1000);
|
|
172
173
|
for (const acc of accounts) {
|
|
173
174
|
if (acc.status === "disabled") continue;
|
|
175
|
+
// Skip expired temporary accounts (no refresh token)
|
|
176
|
+
if (!accountService.hasRefreshToken(acc.id) && acc.expiresAt && acc.expiresAt < nowS) continue;
|
|
174
177
|
const withTokens = accountService.getWithTokens(acc.id);
|
|
175
178
|
if (!withTokens) continue;
|
|
176
179
|
const token = withTokens.accessToken;
|
|
@@ -247,9 +250,14 @@ export function getUsageForAccount(accountId: string): ClaudeUsage {
|
|
|
247
250
|
return row ? snapshotToUsage(row) : {};
|
|
248
251
|
}
|
|
249
252
|
|
|
250
|
-
/** Get usage for all accounts */
|
|
253
|
+
/** Get usage for all accounts (excludes expired temporary accounts) */
|
|
251
254
|
export function getAllAccountUsages(): AccountUsageEntry[] {
|
|
252
|
-
const
|
|
255
|
+
const nowS = Math.floor(Date.now() / 1000);
|
|
256
|
+
const accounts = accountService.list().filter(acc => {
|
|
257
|
+
// Exclude expired accounts without refresh token (temporary/invalid)
|
|
258
|
+
if (!accountService.hasRefreshToken(acc.id) && acc.expiresAt && acc.expiresAt < nowS) return false;
|
|
259
|
+
return true;
|
|
260
|
+
});
|
|
253
261
|
const snapshots = getAllLatestSnapshots();
|
|
254
262
|
const snapshotMap = new Map(snapshots.map(s => [s.account_id, s]));
|
|
255
263
|
return accounts.map(acc => {
|