@oh-my-pi/pi-ai 13.16.2 → 13.16.3
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 +10 -0
- package/package.json +2 -2
- package/src/auth-storage.ts +69 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [13.16.3] - 2026-03-28
|
|
6
|
+
### Changed
|
|
7
|
+
|
|
8
|
+
- Modified OAuth credential saving to preserve unrelated identities instead of replacing all credentials for a provider
|
|
9
|
+
- Updated credential identity resolution to use provider context for more accurate email deduplication
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed OAuth credential updates to replace matching credentials in-place rather than creating disabled rows, preventing unbounded accumulation of soft-deleted credentials
|
|
14
|
+
|
|
5
15
|
## [13.15.0] - 2026-03-23
|
|
6
16
|
|
|
7
17
|
### Added
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "13.16.
|
|
4
|
+
"version": "13.16.3",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@aws-sdk/client-bedrock-runtime": "^3",
|
|
42
42
|
"@bufbuild/protobuf": "^2.11",
|
|
43
43
|
"@google/genai": "^1.43",
|
|
44
|
-
"@oh-my-pi/pi-utils": "13.16.
|
|
44
|
+
"@oh-my-pi/pi-utils": "13.16.3",
|
|
45
45
|
"@sinclair/typebox": "^0.34",
|
|
46
46
|
"@smithy/node-http-handler": "^4.4",
|
|
47
47
|
"ajv": "^8.18",
|
package/src/auth-storage.ts
CHANGED
|
@@ -656,6 +656,15 @@ export class AuthStorage {
|
|
|
656
656
|
this.#resetProviderAssignments(provider);
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
+
async #upsertOAuthCredential(provider: string, credential: OAuthCredential): Promise<void> {
|
|
660
|
+
const stored = this.#store.upsertAuthCredentialForProvider(provider, credential);
|
|
661
|
+
this.#setStoredCredentials(
|
|
662
|
+
provider,
|
|
663
|
+
stored.map(record => ({ id: record.id, credential: record.credential })),
|
|
664
|
+
);
|
|
665
|
+
this.#resetProviderAssignments(provider);
|
|
666
|
+
}
|
|
667
|
+
|
|
659
668
|
/**
|
|
660
669
|
* Remove credential for a provider.
|
|
661
670
|
*/
|
|
@@ -945,12 +954,7 @@ export class AuthStorage {
|
|
|
945
954
|
}
|
|
946
955
|
}
|
|
947
956
|
const newCredential: OAuthCredential = { type: "oauth", ...credentials };
|
|
948
|
-
|
|
949
|
-
if (existing.length === 0) {
|
|
950
|
-
await this.set(provider, newCredential);
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
|
-
await this.set(provider, [...existing, newCredential]);
|
|
957
|
+
await this.#upsertOAuthCredential(provider, newCredential);
|
|
954
958
|
}
|
|
955
959
|
|
|
956
960
|
/**
|
|
@@ -1975,7 +1979,7 @@ function normalizeStoredIdentityKey(identityKey: string | null | undefined): str
|
|
|
1975
1979
|
return normalized && normalized.length > 0 ? normalized : null;
|
|
1976
1980
|
}
|
|
1977
1981
|
|
|
1978
|
-
function serializeCredential(credential: AuthCredential): SerializedCredentialRecord | null {
|
|
1982
|
+
function serializeCredential(provider: string, credential: AuthCredential): SerializedCredentialRecord | null {
|
|
1979
1983
|
if (credential.type === "api_key") {
|
|
1980
1984
|
return {
|
|
1981
1985
|
credentialType: "api_key",
|
|
@@ -1988,7 +1992,7 @@ function serializeCredential(credential: AuthCredential): SerializedCredentialRe
|
|
|
1988
1992
|
return {
|
|
1989
1993
|
credentialType: "oauth",
|
|
1990
1994
|
data: JSON.stringify(rest),
|
|
1991
|
-
identityKey: resolveCredentialIdentityKey(
|
|
1995
|
+
identityKey: resolveCredentialIdentityKey(provider, credential),
|
|
1992
1996
|
};
|
|
1993
1997
|
}
|
|
1994
1998
|
return null;
|
|
@@ -2423,7 +2427,7 @@ export class AuthCredentialStore {
|
|
|
2423
2427
|
const matchedExistingIds = new Set<number>();
|
|
2424
2428
|
|
|
2425
2429
|
for (const credential of items) {
|
|
2426
|
-
const serialized = serializeCredential(credential);
|
|
2430
|
+
const serialized = serializeCredential(providerName, credential);
|
|
2427
2431
|
if (!serialized) continue;
|
|
2428
2432
|
const match = existing.find(
|
|
2429
2433
|
entry =>
|
|
@@ -2461,6 +2465,53 @@ export class AuthCredentialStore {
|
|
|
2461
2465
|
return result;
|
|
2462
2466
|
}
|
|
2463
2467
|
|
|
2468
|
+
upsertAuthCredentialForProvider(provider: string, credential: AuthCredential): StoredAuthCredential[] {
|
|
2469
|
+
const upsert = this.#db.transaction((providerName: string, item: AuthCredential) => {
|
|
2470
|
+
const serialized = serializeCredential(providerName, item);
|
|
2471
|
+
if (!serialized) return this.listAuthCredentials(providerName);
|
|
2472
|
+
const existingRows = this.#listActiveByProviderStmt.all(providerName) as AuthRow[];
|
|
2473
|
+
const existing = existingRows.map(row => ({
|
|
2474
|
+
id: row.id,
|
|
2475
|
+
credential: deserializeCredential(row),
|
|
2476
|
+
identityKey: resolveRowCredentialIdentityKey(providerName, row),
|
|
2477
|
+
}));
|
|
2478
|
+
|
|
2479
|
+
let targetId: number | null = null;
|
|
2480
|
+
for (const row of existing) {
|
|
2481
|
+
if (!matchesReplacementCredential(providerName, row.credential, row.identityKey, item)) continue;
|
|
2482
|
+
if (targetId === null) {
|
|
2483
|
+
targetId = row.id;
|
|
2484
|
+
this.#updateStmt.run(serialized.credentialType, serialized.data, serialized.identityKey, row.id);
|
|
2485
|
+
continue;
|
|
2486
|
+
}
|
|
2487
|
+
this.#deleteStmt.run("replaced by newer credential", row.id);
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
if (targetId === null) {
|
|
2491
|
+
const row = this.#insertStmt.get(
|
|
2492
|
+
providerName,
|
|
2493
|
+
serialized.credentialType,
|
|
2494
|
+
serialized.data,
|
|
2495
|
+
serialized.identityKey,
|
|
2496
|
+
) as { id?: number } | undefined;
|
|
2497
|
+
targetId = row?.id ?? null;
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
const activeRows = this.#listActiveByProviderStmt.all(providerName) as AuthRow[];
|
|
2501
|
+
const result: StoredAuthCredential[] = [];
|
|
2502
|
+
for (const row of activeRows) {
|
|
2503
|
+
const activeCredential = deserializeCredential(row);
|
|
2504
|
+
if (!activeCredential) continue;
|
|
2505
|
+
result.push(toStoredAuthCredential(row, activeCredential));
|
|
2506
|
+
}
|
|
2507
|
+
return result;
|
|
2508
|
+
});
|
|
2509
|
+
|
|
2510
|
+
const result = upsert(provider, credential);
|
|
2511
|
+
this.#purgeSupersededDisabledRows(provider, result);
|
|
2512
|
+
return result;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2464
2515
|
/**
|
|
2465
2516
|
* Hard-deletes disabled rows for a provider when an active row with the same identity exists.
|
|
2466
2517
|
* This prevents unbounded accumulation of soft-deleted credentials while preserving
|
|
@@ -2488,15 +2539,16 @@ export class AuthCredentialStore {
|
|
|
2488
2539
|
}
|
|
2489
2540
|
|
|
2490
2541
|
updateAuthCredential(id: number, credential: AuthCredential): void {
|
|
2491
|
-
const serialized = serializeCredential(credential);
|
|
2492
|
-
if (!serialized) return;
|
|
2493
2542
|
try {
|
|
2494
|
-
this.#updateStmt.run(serialized.credentialType, serialized.data, serialized.identityKey, id);
|
|
2495
2543
|
const providerRow = this.#db.prepare("SELECT provider FROM auth_credentials WHERE id = ?").get(id) as
|
|
2496
2544
|
| { provider?: string }
|
|
2497
2545
|
| undefined;
|
|
2498
|
-
|
|
2499
|
-
|
|
2546
|
+
const provider = providerRow?.provider ?? "";
|
|
2547
|
+
const serialized = serializeCredential(provider, credential);
|
|
2548
|
+
if (!serialized) return;
|
|
2549
|
+
this.#updateStmt.run(serialized.credentialType, serialized.data, serialized.identityKey, id);
|
|
2550
|
+
if (provider) {
|
|
2551
|
+
this.#purgeSupersededDisabledRows(provider, this.listAuthCredentials(provider));
|
|
2500
2552
|
}
|
|
2501
2553
|
} catch {
|
|
2502
2554
|
// Ignore update failures
|
|
@@ -2547,11 +2599,12 @@ export class AuthCredentialStore {
|
|
|
2547
2599
|
// ─── Convenience methods for CLI ────────────────────────────────────────
|
|
2548
2600
|
|
|
2549
2601
|
/**
|
|
2550
|
-
* Save OAuth credentials for a provider
|
|
2602
|
+
* Save OAuth credentials for a provider.
|
|
2603
|
+
* Preserves unrelated identities and replaces only the matching credential.
|
|
2551
2604
|
*/
|
|
2552
2605
|
saveOAuth(provider: string, credentials: OAuthCredentials): void {
|
|
2553
2606
|
const credential: AuthCredential = { type: "oauth", ...credentials };
|
|
2554
|
-
this.
|
|
2607
|
+
this.upsertAuthCredentialForProvider(provider, credential);
|
|
2555
2608
|
}
|
|
2556
2609
|
|
|
2557
2610
|
/**
|