offrouter-core 0.1.0 → 0.2.0
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/package.json +1 -1
- package/src/auth/credential-chain.test.ts +124 -0
- package/src/auth/credential-chain.ts +55 -4
- package/src/auth/index.ts +14 -1
- package/src/auth/keychain.test.ts +154 -1
- package/src/auth/keychain.ts +111 -0
- package/src/auth/oauth-pkce.test.ts +147 -2
- package/src/auth/oauth-pkce.ts +175 -13
- package/src/auth/oauth-server.test.ts +83 -0
- package/src/auth/oauth-server.ts +296 -0
- package/src/config.test.ts +16 -0
- package/src/config.ts +4 -0
- package/src/index.ts +17 -0
- package/src/usage-file.test.ts +243 -0
- package/src/usage-file.ts +435 -0
- package/src/usage.ts +44 -17
package/src/usage.ts
CHANGED
|
@@ -118,6 +118,25 @@ export interface AccountUsageSnapshot {
|
|
|
118
118
|
percentRemaining?: number;
|
|
119
119
|
nearLimit: boolean;
|
|
120
120
|
subscriptionStatus: SubscriptionStatus;
|
|
121
|
+
/**
|
|
122
|
+
* Derived account health from quota usage: "dead" when exhausted,
|
|
123
|
+
* "degraded" when near-limit, otherwise "healthy". Useful for
|
|
124
|
+
* persisted stores that need a stable, restorable health signal.
|
|
125
|
+
*/
|
|
126
|
+
health?: ProviderHealth;
|
|
127
|
+
updatedAt: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Minimal persisted document for a single account's quota usage.
|
|
132
|
+
* Shared between the in-memory and file-backed usage stores so snapshot
|
|
133
|
+
* math stays in one place.
|
|
134
|
+
*/
|
|
135
|
+
export interface AccountUsageDoc {
|
|
136
|
+
providerId: string;
|
|
137
|
+
accountId: string;
|
|
138
|
+
used: number;
|
|
139
|
+
limit?: number;
|
|
121
140
|
updatedAt: string;
|
|
122
141
|
}
|
|
123
142
|
|
|
@@ -576,7 +595,7 @@ export class InMemoryUsageStore implements UsageStore {
|
|
|
576
595
|
if (input.used !== undefined) state.used = input.used;
|
|
577
596
|
if (input.limit !== undefined) state.limit = input.limit;
|
|
578
597
|
state.updatedAt = at.toISOString();
|
|
579
|
-
return
|
|
598
|
+
return computeAccountSnapshot(state);
|
|
580
599
|
});
|
|
581
600
|
}
|
|
582
601
|
|
|
@@ -589,7 +608,7 @@ export class InMemoryUsageStore implements UsageStore {
|
|
|
589
608
|
const key = `${providerId}:${accountId}`;
|
|
590
609
|
const state = this.#accounts.get(key);
|
|
591
610
|
if (!state) return false;
|
|
592
|
-
return
|
|
611
|
+
return computeAccountSnapshot(state, threshold).nearLimit;
|
|
593
612
|
});
|
|
594
613
|
}
|
|
595
614
|
|
|
@@ -597,7 +616,7 @@ export class InMemoryUsageStore implements UsageStore {
|
|
|
597
616
|
return this.#exclusive(() => {
|
|
598
617
|
const out: AccountUsageSnapshot[] = [];
|
|
599
618
|
for (const state of this.#accounts.values()) {
|
|
600
|
-
const snap =
|
|
619
|
+
const snap = computeAccountSnapshot(state, threshold);
|
|
601
620
|
if (snap.nearLimit) out.push(snap);
|
|
602
621
|
}
|
|
603
622
|
return out.sort((a, b) => {
|
|
@@ -615,7 +634,7 @@ export class InMemoryUsageStore implements UsageStore {
|
|
|
615
634
|
const key = `${providerId}:${accountId}`;
|
|
616
635
|
const state = this.#accounts.get(key);
|
|
617
636
|
if (!state) return undefined;
|
|
618
|
-
return
|
|
637
|
+
return computeAccountSnapshot(state);
|
|
619
638
|
});
|
|
620
639
|
}
|
|
621
640
|
|
|
@@ -623,7 +642,7 @@ export class InMemoryUsageStore implements UsageStore {
|
|
|
623
642
|
return this.#exclusive(() => {
|
|
624
643
|
const out: AccountUsageSnapshot[] = [];
|
|
625
644
|
for (const state of this.#accounts.values()) {
|
|
626
|
-
out.push(
|
|
645
|
+
out.push(computeAccountSnapshot(state));
|
|
627
646
|
}
|
|
628
647
|
return out.sort((a, b) => {
|
|
629
648
|
if (a.providerId !== b.providerId) return a.providerId.localeCompare(b.providerId);
|
|
@@ -763,38 +782,46 @@ function toSnapshot(state: InternalProviderState): ProviderUsageSnapshot {
|
|
|
763
782
|
};
|
|
764
783
|
}
|
|
765
784
|
|
|
766
|
-
function
|
|
767
|
-
|
|
768
|
-
threshold
|
|
785
|
+
export function computeAccountSnapshot(
|
|
786
|
+
doc: AccountUsageDoc,
|
|
787
|
+
threshold: number = DEFAULT_NEAR_LIMIT_THRESHOLD,
|
|
769
788
|
): AccountUsageSnapshot {
|
|
770
|
-
const
|
|
771
|
-
const
|
|
772
|
-
const
|
|
773
|
-
|
|
789
|
+
const limit = doc.limit;
|
|
790
|
+
const used = doc.used;
|
|
791
|
+
const remaining =
|
|
792
|
+
limit === undefined ? undefined : Math.max(0, limit - used);
|
|
774
793
|
let status: SubscriptionStatus;
|
|
775
794
|
let nearLimit = false;
|
|
795
|
+
let health: ProviderHealth;
|
|
776
796
|
if (limit === undefined) {
|
|
777
797
|
status = "unknown";
|
|
798
|
+
health = "healthy";
|
|
778
799
|
} else if (used >= limit) {
|
|
779
800
|
status = "exhausted";
|
|
780
|
-
|
|
801
|
+
health = "dead";
|
|
802
|
+
} else if ((limit - used) / limit < threshold) {
|
|
781
803
|
status = "near-limit";
|
|
782
804
|
nearLimit = true;
|
|
805
|
+
health = "degraded";
|
|
783
806
|
} else {
|
|
784
807
|
status = "active";
|
|
808
|
+
health = "healthy";
|
|
785
809
|
}
|
|
786
810
|
const percentRemaining =
|
|
787
|
-
limit === undefined
|
|
811
|
+
limit === undefined
|
|
812
|
+
? undefined
|
|
813
|
+
: Math.round(((limit - used) / limit) * 100);
|
|
788
814
|
return {
|
|
789
|
-
providerId:
|
|
790
|
-
accountId:
|
|
815
|
+
providerId: doc.providerId,
|
|
816
|
+
accountId: doc.accountId,
|
|
791
817
|
used,
|
|
792
818
|
limit,
|
|
793
819
|
remaining,
|
|
794
820
|
percentRemaining,
|
|
795
821
|
nearLimit,
|
|
796
822
|
subscriptionStatus: status,
|
|
797
|
-
|
|
823
|
+
health,
|
|
824
|
+
updatedAt: doc.updatedAt,
|
|
798
825
|
};
|
|
799
826
|
}
|
|
800
827
|
|