Open History to see the last commit
1import * as Schema from "effect/Schema";
2
3import {
4 ForwardCompatibleArray,
5 IsoDateTime,
6 NonNegativeInt,
7 TrimmedNonEmptyString,
8} from "./baseSchemas.ts";
9import { ProviderDriverKind, ProviderInstanceId } from "./providerInstance.ts";
10import { UsageLimitSourceId } from "./usageLimitSourceId.ts";
11
12/** Stable per-provider ids let sparse updates merge with full probes. */
13export const ServerProviderUsageWindow = Schema.Struct({
14 id: TrimmedNonEmptyString,
15 kind: Schema.Literals(["session", "weekly", "monthly", "other"]),
16 label: TrimmedNonEmptyString,
17 usedPercent: Schema.Number.check(Schema.isBetween({ minimum: 0, maximum: 100 })),
18 resetsAt: Schema.optional(IsoDateTime),
19 windowDurationMins: Schema.optional(NonNegativeInt),
20});
21export type ServerProviderUsageWindow = typeof ServerProviderUsageWindow.Type;
22
23/** One kind of banked reset, such as Claude's weekly reset or its Fable-only reset. */
24export const ServerProviderResetCreditOption = Schema.Struct({
25 creditId: TrimmedNonEmptyString,
26 label: TrimmedNonEmptyString,
27 description: TrimmedNonEmptyString,
28 availableCount: NonNegativeInt,
29 expiresAt: Schema.optional(IsoDateTime),
30});
31export type ServerProviderResetCreditOption = typeof ServerProviderResetCreditOption.Type;
32
33export const ServerProviderResetCredits = Schema.Struct({
34 availableCount: NonNegativeInt,
35 nextExpiresAt: Schema.optional(IsoDateTime),
36 /** Pins hub redemption to the displayed credit, including retries from another client. */
37 nextCreditId: Schema.optional(TrimmedNonEmptyString),
38 /** Present when resets differ in what they refill; redeem one by its `creditId`. */
39 options: Schema.optional(ForwardCompatibleArray(ServerProviderResetCreditOption)),
40});
41export type ServerProviderResetCredits = typeof ServerProviderResetCredits.Type;
42
43/** Clear bars for unavailable accounts; retain last good bars after a failed probe. */
44export const ServerProviderUsageLimits = Schema.Struct({
45 checkedAt: IsoDateTime,
46 windows: ForwardCompatibleArray(ServerProviderUsageWindow),
47 credentialFingerprint: Schema.optional(TrimmedNonEmptyString),
48 resetCredits: Schema.optional(ServerProviderResetCredits),
49 externalUsage: Schema.optional(
50 Schema.Struct({
51 label: TrimmedNonEmptyString,
52 url: TrimmedNonEmptyString,
53 }),
54 ),
55 unavailable: Schema.optional(
56 Schema.Struct({
57 reason: Schema.Literals(["unsupported", "probeFailed"]),
58 message: Schema.optional(TrimmedNonEmptyString),
59 }),
60 ),
61});
62export type ServerProviderUsageLimits = typeof ServerProviderUsageLimits.Type;
63
64/** Partial updates merge windows by id; omitted windows remain unchanged. */
65export const ProviderUsageLimitsUpdate = Schema.Struct({
66 windows: Schema.Array(ServerProviderUsageWindow),
67});
68export type ProviderUsageLimitsUpdate = typeof ProviderUsageLimitsUpdate.Type;
69
70export const UsageLimitSourceAccount = Schema.Struct({
71 id: TrimmedNonEmptyString,
72 driver: ProviderDriverKind,
73 email: Schema.optional(TrimmedNonEmptyString),
74 plan: Schema.optional(TrimmedNonEmptyString),
75 workspaceId: Schema.optional(TrimmedNonEmptyString),
76 usageLimits: ServerProviderUsageLimits,
77});
78export type UsageLimitSourceAccount = typeof UsageLimitSourceAccount.Type;
79
80export const UsageLimitSourceSnapshot = Schema.Struct({
81 id: UsageLimitSourceId,
82 kind: Schema.Literal("cliproxy"),
83 label: TrimmedNonEmptyString,
84 checkedAt: IsoDateTime,
85 accounts: ForwardCompatibleArray(UsageLimitSourceAccount),
86 error: Schema.optional(TrimmedNonEmptyString),
87});
88export type UsageLimitSourceSnapshot = typeof UsageLimitSourceSnapshot.Type;
89
90export const UsageLimitSourceSnapshots = ForwardCompatibleArray(UsageLimitSourceSnapshot);
91export type UsageLimitSourceSnapshots = typeof UsageLimitSourceSnapshots.Type;
92
93export const UsageLimitSourceConsumeResetCreditInput = Schema.Struct({
94 sourceId: UsageLimitSourceId,
95 accountId: TrimmedNonEmptyString,
96 creditId: TrimmedNonEmptyString,
97});
98export type UsageLimitSourceConsumeResetCreditInput =
99 typeof UsageLimitSourceConsumeResetCreditInput.Type;
100
101export const ProviderConsumeResetCreditInput = Schema.Union([
102 Schema.Struct({
103 instanceId: ProviderInstanceId,
104 creditId: Schema.optional(TrimmedNonEmptyString),
105 }),
106 UsageLimitSourceConsumeResetCreditInput,
107]);
108export type ProviderConsumeResetCreditInput = typeof ProviderConsumeResetCreditInput.Type;
109
110export class UsageLimitSourceError extends Schema.TaggedError<UsageLimitSourceError>()(
111 "UsageLimitSourceError",
112 { detail: Schema.String },
113) {
114 override get message(): string {
115 return this.detail;
116 }
117}
118
119export const ProviderConsumeResetCreditOutcome = Schema.Literals([
120 "reset",
121 "nothingToReset",
122 "noCredit",
123 "alreadyRedeemed",
124]);
125export type ProviderConsumeResetCreditOutcome = typeof ProviderConsumeResetCreditOutcome.Type;
126
127export const ProviderConsumeResetCreditResult = Schema.Struct({
128 outcome: ProviderConsumeResetCreditOutcome,
129 warning: Schema.optional(TrimmedNonEmptyString),
130});
131export type ProviderConsumeResetCreditResult = typeof ProviderConsumeResetCreditResult.Type;
132
133export const UsageLimitsReport = Schema.Struct({
134 createdAt: IsoDateTime,
135 accounts: Schema.Array(
136 Schema.Struct({
137 id: TrimmedNonEmptyString,
138 driver: ProviderDriverKind,
139 label: TrimmedNonEmptyString,
140 plan: Schema.optional(TrimmedNonEmptyString),
141 email: Schema.optional(TrimmedNonEmptyString),
142 sourceLabel: Schema.optional(TrimmedNonEmptyString),
143 instanceId: Schema.optional(ProviderInstanceId),
144 resetCreditInput: Schema.optional(ProviderConsumeResetCreditInput),
145 displayName: Schema.optional(Schema.String),
146 accentColor: Schema.optional(Schema.String),
147 limits: ServerProviderUsageLimits,
148 }),
149 ),
150 notices: Schema.Array(Schema.String),
151});
152export type UsageLimitsReport = typeof UsageLimitsReport.Type;