@kunkun.sh/kuota 0.0.0-stage → 0.1.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.
@@ -0,0 +1,309 @@
1
+ import { Schema } from "effect";
2
+ // ../core/src/types.ts
3
+ const ProviderIdSchema = Schema.Literal("codex", "claude", "antigravity");
4
+ const QuotaStatusSchema = Schema.Literal("ok", "partial", "unauthenticated", "unavailable", "unsupported", "error");
5
+ const QuotaWindowSchema = Schema.Struct({
6
+ windowId: Schema.String,
7
+ usedPercent: Schema.NullOr(Schema.Number),
8
+ remainingPercent: Schema.NullOr(Schema.Number),
9
+ resetAt: Schema.NullOr(Schema.String)
10
+ });
11
+ const QuotaSnapshotSchema = Schema.Struct({
12
+ provider: ProviderIdSchema,
13
+ status: QuotaStatusSchema,
14
+ fetchedAt: Schema.String,
15
+ source: Schema.String,
16
+ windows: Schema.Array(QuotaWindowSchema),
17
+ errorCode: Schema.NullOr(Schema.String)
18
+ });
19
+ // ../core/src/hash.ts
20
+ const K = new Uint32Array([
21
+ 1116352408,
22
+ 1899447441,
23
+ 3049323471,
24
+ 3921009573,
25
+ 961987163,
26
+ 1508970993,
27
+ 2453635748,
28
+ 2870763221,
29
+ 3624381080,
30
+ 310598401,
31
+ 607225278,
32
+ 1426881987,
33
+ 1925078388,
34
+ 2162078206,
35
+ 2614888103,
36
+ 3248222580,
37
+ 3835390401,
38
+ 4022224774,
39
+ 264347078,
40
+ 604807628,
41
+ 770255983,
42
+ 1249150122,
43
+ 1555081692,
44
+ 1996064986,
45
+ 2554220882,
46
+ 2821834349,
47
+ 2952996808,
48
+ 3210313671,
49
+ 3336571891,
50
+ 3584528711,
51
+ 113926993,
52
+ 338241895,
53
+ 666307205,
54
+ 773529912,
55
+ 1294757372,
56
+ 1396182291,
57
+ 1695183700,
58
+ 1986661051,
59
+ 2177026350,
60
+ 2456956037,
61
+ 2730485921,
62
+ 2820302411,
63
+ 3259730800,
64
+ 3345764771,
65
+ 3516065817,
66
+ 3600352804,
67
+ 4094571909,
68
+ 275423344,
69
+ 430227734,
70
+ 506948616,
71
+ 659060556,
72
+ 883997877,
73
+ 958139571,
74
+ 1322822218,
75
+ 1537002063,
76
+ 1747873779,
77
+ 1955562222,
78
+ 2024104815,
79
+ 2227730452,
80
+ 2361852424,
81
+ 2428436474,
82
+ 2756734187,
83
+ 3204031479,
84
+ 3329325298
85
+ ]);
86
+ const rotr = (x, n) => x >>> n | x << 32 - n;
87
+ /** Dependency-free SHA-256 (hex). Keeps @kuota/core free of node:crypto so it stays browser-safe. */
88
+ function sha256Hex(text) {
89
+ const data = new TextEncoder().encode(text);
90
+ const bitLength = data.length * 8;
91
+ const padded = new Uint8Array(data.length + 9 + 63 >> 6 << 6);
92
+ padded.set(data);
93
+ padded[data.length] = 128;
94
+ const view = new DataView(padded.buffer);
95
+ view.setUint32(padded.length - 8, Math.floor(bitLength / 2 ** 32));
96
+ view.setUint32(padded.length - 4, bitLength >>> 0);
97
+ const h = new Uint32Array([
98
+ 1779033703,
99
+ 3144134277,
100
+ 1013904242,
101
+ 2773480762,
102
+ 1359893119,
103
+ 2600822924,
104
+ 528734635,
105
+ 1541459225
106
+ ]);
107
+ const w = /* @__PURE__ */ new Uint32Array(64);
108
+ for (let offset = 0; offset < padded.length; offset += 64) {
109
+ for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4);
110
+ for (let i = 16; i < 64; i++) {
111
+ const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ w[i - 15] >>> 3;
112
+ const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ w[i - 2] >>> 10;
113
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
114
+ }
115
+ let [a, b, c, d, e, f, g, hh] = h;
116
+ for (let i = 0; i < 64; i++) {
117
+ const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
118
+ const ch = e & f ^ ~e & g;
119
+ const t1 = hh + s1 + ch + K[i] + w[i] >>> 0;
120
+ const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
121
+ hh = g;
122
+ g = f;
123
+ f = e;
124
+ e = d + t1 >>> 0;
125
+ d = c;
126
+ c = b;
127
+ b = a;
128
+ a = t1 + t2 >>> 0;
129
+ }
130
+ const next = [
131
+ a,
132
+ b,
133
+ c,
134
+ d,
135
+ e,
136
+ f,
137
+ g,
138
+ hh
139
+ ];
140
+ for (let i = 0; i < 8; i++) h[i] = h[i] + next[i] >>> 0;
141
+ }
142
+ return Array.from(h, (x) => x.toString(16).padStart(8, "0")).join("");
143
+ }
144
+ // ../core/src/history/schema.ts
145
+ const HOUR = 36e5;
146
+ /**
147
+ * Window cadence heuristics, derived from window ids the adapters emit. Only cadences named in
148
+ * the id (5h / weekly / daily) are trusted; everything else stays `unknown` rather than guessed.
149
+ * Codex `primary` / `secondary` follow the documented 5h / weekly convention of its app-server.
150
+ */
151
+ function windowProfile(provider, windowId, resetAt, now = /* @__PURE__ */ new Date()) {
152
+ if (provider === "codex") {
153
+ if (windowId === "primary") {
154
+ if (resetAt) {
155
+ const resetMs = new Date(resetAt).getTime();
156
+ const nowMs = now.getTime();
157
+ if (Number.isFinite(resetMs) && resetMs - nowMs > 5 * HOUR) return {
158
+ semantics: "fixed",
159
+ durationMs: 168 * HOUR
160
+ };
161
+ }
162
+ return {
163
+ semantics: "fixed",
164
+ durationMs: 5 * HOUR
165
+ };
166
+ }
167
+ if (windowId === "secondary") return {
168
+ semantics: "fixed",
169
+ durationMs: 168 * HOUR
170
+ };
171
+ }
172
+ const id = windowId.toLowerCase();
173
+ const has = (re) => re.test(id);
174
+ if (has(/(^|[.:_-])(5h|five-hour)([.:_-]|$)/)) return {
175
+ semantics: "fixed",
176
+ durationMs: 5 * HOUR
177
+ };
178
+ if (has(/(^|[.:_-])(weekly|7d|seven-day)([.:_-]|$)/)) return {
179
+ semantics: "fixed",
180
+ durationMs: 168 * HOUR
181
+ };
182
+ if (has(/(^|[.:_-])(daily|24h)([.:_-]|$)/)) return {
183
+ semantics: "fixed",
184
+ durationMs: 24 * HOUR
185
+ };
186
+ return {
187
+ semantics: "unknown",
188
+ durationMs: null
189
+ };
190
+ }
191
+ const IsoDate = Schema.String.pipe(Schema.filter((value) => Number.isFinite(Date.parse(value)) || "must be an ISO timestamp"));
192
+ const Percent = Schema.Number.pipe(Schema.filter((value) => Number.isFinite(value) && value >= 0 && value <= 100 || "0..100"));
193
+ const QuotaHistoryEventV1Schema = Schema.Struct({
194
+ schemaVersion: Schema.Literal(1),
195
+ kind: Schema.Literal("quota_observed", "quota_unavailable", "usage_observed"),
196
+ eventId: Schema.String,
197
+ observedAt: IsoDate,
198
+ recordedAt: IsoDate,
199
+ provider: Schema.String,
200
+ accountScope: Schema.NullOr(Schema.String),
201
+ deviceId: Schema.String,
202
+ windowId: Schema.String,
203
+ epochId: Schema.NullOr(Schema.String),
204
+ semantics: Schema.Literal("fixed", "rolling", "first_use", "unknown"),
205
+ startAt: Schema.NullOr(IsoDate),
206
+ resetAt: Schema.NullOr(IsoDate),
207
+ usedPercent: Schema.NullOr(Percent),
208
+ remainingPercent: Schema.NullOr(Percent),
209
+ source: Schema.String,
210
+ state: Schema.Literal("ok", "partial", "stale", "error", "unauthenticated"),
211
+ errorCode: Schema.NullOr(Schema.String),
212
+ usage: Schema.optional(Schema.Struct({
213
+ inputTokens: Schema.optional(Schema.Number),
214
+ outputTokens: Schema.optional(Schema.Number),
215
+ cacheReadTokens: Schema.optional(Schema.Number),
216
+ cacheWriteTokens: Schema.optional(Schema.Number),
217
+ model: Schema.optional(Schema.String),
218
+ estimatedUsd: Schema.optional(Schema.Number)
219
+ }))
220
+ });
221
+ const decode = Schema.decodeUnknownEither(QuotaHistoryEventV1Schema, { onExcessProperty: "error" });
222
+ /** Strict decode: unknown (possibly secret-bearing) fields are rejected, not silently kept. */
223
+ function decodeHistoryEvent(input) {
224
+ return decode(input);
225
+ }
226
+ const validPercent = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 100;
227
+ const minuteIso = (ms) => (/* @__PURE__ */ new Date(Math.round(ms / 6e4) * 6e4)).toISOString();
228
+ function makeId(parts) {
229
+ return sha256Hex(parts.map((part) => part === null ? "\0" : String(part)).join(""));
230
+ }
231
+ /** Pure normalization: one event per measurable window, or one sanitized unavailable event. */
232
+ function historyEventsFromSnapshot(snapshot, options) {
233
+ const observedMs = Date.parse(snapshot.fetchedAt);
234
+ if (!Number.isFinite(observedMs)) return [];
235
+ const observedAt = new Date(observedMs).toISOString();
236
+ const base = {
237
+ schemaVersion: 1,
238
+ observedAt,
239
+ recordedAt: options.recordedAt,
240
+ provider: snapshot.provider,
241
+ accountScope: options.accountScope,
242
+ deviceId: options.deviceId,
243
+ source: snapshot.source
244
+ };
245
+ const measurable = snapshot.windows.filter((w) => validPercent(w.usedPercent));
246
+ if (!(snapshot.status === "ok" || snapshot.status === "partial") || measurable.length === 0) {
247
+ const state = snapshot.status === "unauthenticated" ? "unauthenticated" : "error";
248
+ const errorCode = snapshot.errorCode ?? snapshot.status.toUpperCase();
249
+ return [{
250
+ ...base,
251
+ kind: "quota_unavailable",
252
+ eventId: makeId([
253
+ "quota_unavailable",
254
+ snapshot.provider,
255
+ options.accountScope,
256
+ options.deviceId,
257
+ observedAt,
258
+ state,
259
+ errorCode
260
+ ]),
261
+ windowId: "*",
262
+ epochId: null,
263
+ semantics: "unknown",
264
+ startAt: null,
265
+ resetAt: null,
266
+ usedPercent: null,
267
+ remainingPercent: null,
268
+ state,
269
+ errorCode
270
+ }];
271
+ }
272
+ const state = options.stale ? "stale" : snapshot.status === "partial" ? "partial" : "ok";
273
+ return measurable.map((window) => {
274
+ const profile = windowProfile(snapshot.provider, window.windowId, window.resetAt);
275
+ const semantics = options.semanticsForWindow?.(window.windowId) ?? profile.semantics;
276
+ const resetMs = window.resetAt === null ? NaN : Date.parse(window.resetAt);
277
+ const hasReset = Number.isFinite(resetMs);
278
+ const epochId = hasReset ? minuteIso(resetMs) : null;
279
+ const providedStart = options.startForWindow?.(window.windowId) ?? null;
280
+ const startAt = providedStart !== null && Number.isFinite(Date.parse(providedStart)) ? new Date(providedStart).toISOString() : hasReset && profile.durationMs !== null ? new Date(resetMs - profile.durationMs).toISOString() : null;
281
+ const usedPercent = window.usedPercent;
282
+ const remaining = validPercent(window.remainingPercent) ? window.remainingPercent : Math.max(0, Math.min(100, 100 - usedPercent));
283
+ return {
284
+ ...base,
285
+ kind: "quota_observed",
286
+ eventId: makeId([
287
+ "quota_observed",
288
+ snapshot.provider,
289
+ options.accountScope,
290
+ options.deviceId,
291
+ window.windowId,
292
+ epochId,
293
+ observedAt,
294
+ usedPercent,
295
+ state
296
+ ]),
297
+ windowId: window.windowId,
298
+ epochId,
299
+ semantics,
300
+ startAt,
301
+ resetAt: hasReset ? new Date(resetMs).toISOString() : null,
302
+ usedPercent,
303
+ remainingPercent: remaining,
304
+ state,
305
+ errorCode: snapshot.status === "partial" ? snapshot.errorCode : null
306
+ };
307
+ });
308
+ }
309
+ export { sha256Hex as a, QuotaStatusSchema as c, windowProfile as i, QuotaWindowSchema as l, decodeHistoryEvent as n, ProviderIdSchema as o, historyEventsFromSnapshot as r, QuotaSnapshotSchema as s, QuotaHistoryEventV1Schema as t };