@openlimiter/core 0.1.0 → 0.4.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.
@@ -1,4 +1,4 @@
1
- import { PROVIDER_CODES } from "./types.js";
1
+ import { ACCOUNT_ID_PATTERN, MAX_SNAPSHOT_AMOUNT, PROVIDER_CODES, SNAPSHOT_OBSERVED_VIA, SNAPSHOT_SOURCE_KINDS, UNKNOWN_PROVENANCE } from "./types.js";
2
2
  const units = new Set(["PERCENT", "CREDITS", "TOKENS", "REQUESTS"]);
3
3
  const sources = new Set([
4
4
  "native_payload",
@@ -8,7 +8,10 @@ const sources = new Set([
8
8
  "manual_entry"
9
9
  ]);
10
10
  const precisions = new Set(["exact", "estimated", "manual"]);
11
+ const currencies = new Set(["USD"]);
11
12
  const providerCodes = new Set(PROVIDER_CODES);
13
+ const sourceKinds = new Set(SNAPSHOT_SOURCE_KINDS);
14
+ const observedVia = new Set(SNAPSHOT_OBSERVED_VIA);
12
15
  const safeMeter = /^[A-Z][A-Z0-9_]{0,31}$/u;
13
16
  function isRecord(value) {
14
17
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -64,6 +67,77 @@ function normalizeLabels(value) {
64
67
  verification: "UNVERIFIED"
65
68
  };
66
69
  }
70
+ /**
71
+ * Validate the money amounts, as one unit or not at all.
72
+ *
73
+ * They arrive together or they do not arrive, and they leave together or they
74
+ * do not leave. Any one of them being absent, out of range, the wrong type, or
75
+ * inconsistent with the other drops all three, which is what keeps a card from
76
+ * ever printing a spend with no plan size beside it. The percentage on the same
77
+ * reading is untouched by this: a provider that misreports its money has not
78
+ * misreported how full the meter is.
79
+ */
80
+ function normalizeAmounts(raw) {
81
+ const used = raw.usedAmount;
82
+ const limit = raw.limitAmount;
83
+ const currency = raw.currency;
84
+ if (used === undefined && limit === undefined && currency === undefined)
85
+ return null;
86
+ if (typeof used !== "number" || typeof limit !== "number")
87
+ return null;
88
+ if (typeof currency !== "string" || !currencies.has(currency)) {
89
+ return null;
90
+ }
91
+ if (!Number.isFinite(used) || !Number.isFinite(limit))
92
+ return null;
93
+ if (used < 0 || limit < 0)
94
+ return null;
95
+ if (used > limit)
96
+ return null;
97
+ if (used > MAX_SNAPSHOT_AMOUNT || limit > MAX_SNAPSHOT_AMOUNT)
98
+ return null;
99
+ return { usedAmount: used, limitAmount: limit, currency: currency };
100
+ }
101
+ /**
102
+ * Read the account this reading belongs to.
103
+ *
104
+ * Absent is the ordinary case and means one unnamed account. Present but
105
+ * malformed is the dangerous case, because an identity we cannot trust would
106
+ * quietly split or merge a user's meters in the cache, so that row is refused
107
+ * outright rather than stripped back to the unnamed account.
108
+ */
109
+ function readAccountId(value) {
110
+ if (value === undefined)
111
+ return { ok: true, accountId: null };
112
+ if (typeof value !== "string" || !ACCOUNT_ID_PATTERN.test(value))
113
+ return { ok: false };
114
+ return { ok: true, accountId: value };
115
+ }
116
+ /**
117
+ * Read how the observation arrived.
118
+ *
119
+ * The opposite call to the one above, and deliberately so. Provenance is a
120
+ * label on a reading, not part of its identity, so a provenance we cannot read
121
+ * costs the reading nothing: it becomes unknown and the number survives. Either
122
+ * field failing makes the whole thing unknown, because half a provenance would
123
+ * invite a surface to describe a source it cannot actually vouch for.
124
+ */
125
+ function readProvenance(value) {
126
+ if (value === undefined)
127
+ return null;
128
+ if (!isRecord(value))
129
+ return UNKNOWN_PROVENANCE;
130
+ const kind = value["sourceKind"];
131
+ const via = value["observedVia"];
132
+ if (typeof kind !== "string" || !sourceKinds.has(kind))
133
+ return UNKNOWN_PROVENANCE;
134
+ if (typeof via !== "string" || !observedVia.has(via))
135
+ return UNKNOWN_PROVENANCE;
136
+ return {
137
+ sourceKind: kind,
138
+ observedVia: via
139
+ };
140
+ }
67
141
  export function normalizeMeter(raw) {
68
142
  if (typeof raw.provider !== "string" || !providerCodes.has(raw.provider))
69
143
  return null;
@@ -94,6 +168,11 @@ export function normalizeMeter(raw) {
94
168
  return null;
95
169
  if (Date.parse(raw.expiresAt) < Date.parse(raw.observedAt))
96
170
  return null;
171
+ const account = readAccountId(raw.accountId);
172
+ if (!account.ok)
173
+ return null;
174
+ const provenance = readProvenance(raw.provenance);
175
+ const amounts = normalizeAmounts(raw);
97
176
  return {
98
177
  provider: raw.provider,
99
178
  meter: raw.meter,
@@ -105,7 +184,10 @@ export function normalizeMeter(raw) {
105
184
  precision: raw.precision,
106
185
  observedAt: raw.observedAt,
107
186
  expiresAt: raw.expiresAt,
108
- labels
187
+ labels,
188
+ ...(amounts === null ? {} : amounts),
189
+ ...(account.accountId === null ? {} : { accountId: account.accountId }),
190
+ ...(provenance === null ? {} : { provenance })
109
191
  };
110
192
  }
111
193
  export function normalizeMeters(rawMeters) {
@@ -114,6 +196,23 @@ export function normalizeMeters(rawMeters) {
114
196
  return normalized === null ? [] : [normalized];
115
197
  });
116
198
  }
199
+ export function normalizeMetersReport(rawMeters) {
200
+ const snapshots = [];
201
+ const rejected = new Set();
202
+ let dropped = 0;
203
+ for (const raw of rawMeters) {
204
+ const normalized = normalizeMeter(raw);
205
+ if (normalized !== null) {
206
+ snapshots.push(normalized);
207
+ continue;
208
+ }
209
+ dropped += 1;
210
+ if (typeof raw.provider === "string" && providerCodes.has(raw.provider)) {
211
+ rejected.add(raw.provider);
212
+ }
213
+ }
214
+ return { snapshots, rejected: [...rejected], dropped };
215
+ }
117
216
  function sortDeep(value) {
118
217
  if (Array.isArray(value))
119
218
  return value.map(sortDeep);
@@ -1 +1 @@
1
- {"version":3,"file":"normalizer.js","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"normalizer.js","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * When a connection may next ask its provider a question.
3
+ *
4
+ * Background collection is the difference between a quota monitor and a file
5
+ * viewer, and a collector that retries a failing provider on a fixed interval
6
+ * is how a monitor gets an account rate limited. The whole policy lives here as
7
+ * arithmetic over injected values: the caller supplies the clock and the random
8
+ * source, so a schedule is exactly reproducible in a test and identical on all
9
+ * three operating systems.
10
+ */
11
+ /** Longest our own backoff will ever grow to on its own. */
12
+ export declare const REFRESH_BACKOFF_CEILING_SECONDS = 3600;
13
+ /**
14
+ * Longest a provider may push us out with Retry-After.
15
+ *
16
+ * The header is honoured because the provider knows its own limits better than
17
+ * we do, but a header is still input from outside, and a day is already far
18
+ * past the point where a person would reconnect by hand.
19
+ */
20
+ export declare const REFRESH_RETRY_AFTER_CEILING_SECONDS = 86400;
21
+ /** How far either side of the computed delay the jitter may move it. */
22
+ export declare const REFRESH_JITTER_RATIO = 0.2;
23
+ /** The reader supplied sentinel for a connection that only refreshes by hand. */
24
+ export declare const SCHEDULE_EXEMPT_BASE_SECONDS = 0;
25
+ /** Missing wire cadence fails closed to the most conservative live default. */
26
+ export declare const REFRESH_FALLBACK_BASE_SECONDS = 300;
27
+ /** A stable scheduled instant for a connection exempt from background reads. */
28
+ export declare const SCHEDULE_EXEMPT_INSTANT = "9999-12-31T23:59:59.999Z";
29
+ export interface RefreshInput {
30
+ /**
31
+ * Consecutive failures behind this schedule. Zero means the last attempt
32
+ * succeeded, so the delay is the plain interval with jitter on it.
33
+ */
34
+ attempt: number;
35
+ /** The interval this connection uses when nothing is failing, in seconds. */
36
+ baseSeconds: number;
37
+ /** Retry-After in seconds when the provider sent one, otherwise null. */
38
+ retryAfterSeconds?: number | null;
39
+ /** Injected uniform random in the range zero to one. */
40
+ random: () => number;
41
+ }
42
+ /**
43
+ * The delay before the next attempt, in seconds.
44
+ *
45
+ * Doubling per consecutive failure, capped at an hour, then jittered by up to
46
+ * a fifth either way so a hundred installations that all failed at the same
47
+ * moment do not all come back at the same moment.
48
+ *
49
+ * The order matters and is the whole subtlety here. Jitter is applied to OUR
50
+ * backoff and to nothing else, and the provider's Retry-After is then taken as
51
+ * a floor underneath the jittered result. Jittering a Retry-After instead would
52
+ * subtract up to a fifth from an instruction the provider gave us: a header
53
+ * asking for a day back would come back as nineteen hours and knock on the door
54
+ * almost five hours early, which is precisely the behaviour that gets an account
55
+ * limited harder. Jitter exists to spread our own retries, so it may only ever
56
+ * move our own number. A Retry-After shorter than our backoff changes nothing,
57
+ * because a provider cannot talk us out of backing off.
58
+ */
59
+ export declare function refreshDelaySeconds(input: RefreshInput): number;
60
+ /**
61
+ * The instant of the next attempt, or null when the supplied clock is not a
62
+ * readable instant. Nothing is guessed from a clock we cannot read.
63
+ */
64
+ export declare function nextRefreshAt(now: string, input: RefreshInput): string | null;
65
+ /**
66
+ * Whether a scheduled instant has arrived.
67
+ *
68
+ * A schedule we cannot read counts as due. The alternative is a connection that
69
+ * never refreshes again because one unreadable value got written once, and the
70
+ * first successful attempt replaces it with a readable one. A clock we cannot
71
+ * read is the opposite case and counts as not due, because with no present
72
+ * moment there is nothing to compare against.
73
+ */
74
+ export declare function isDue(nextAt: string | null, now: string): boolean;
75
+ //# sourceMappingURL=schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../src/schedule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAErD;;;;;;GAMG;AACH,eAAO,MAAM,mCAAmC,QAAS,CAAC;AAE1D,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,+EAA+E;AAC/E,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,gFAAgF;AAChF,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,wDAAwD;IACxD,MAAM,EAAE,MAAM,MAAM,CAAC;CACtB;AAuCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAY/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAOjE"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * When a connection may next ask its provider a question.
3
+ *
4
+ * Background collection is the difference between a quota monitor and a file
5
+ * viewer, and a collector that retries a failing provider on a fixed interval
6
+ * is how a monitor gets an account rate limited. The whole policy lives here as
7
+ * arithmetic over injected values: the caller supplies the clock and the random
8
+ * source, so a schedule is exactly reproducible in a test and identical on all
9
+ * three operating systems.
10
+ */
11
+ /** Longest our own backoff will ever grow to on its own. */
12
+ export const REFRESH_BACKOFF_CEILING_SECONDS = 3_600;
13
+ /**
14
+ * Longest a provider may push us out with Retry-After.
15
+ *
16
+ * The header is honoured because the provider knows its own limits better than
17
+ * we do, but a header is still input from outside, and a day is already far
18
+ * past the point where a person would reconnect by hand.
19
+ */
20
+ export const REFRESH_RETRY_AFTER_CEILING_SECONDS = 86_400;
21
+ /** How far either side of the computed delay the jitter may move it. */
22
+ export const REFRESH_JITTER_RATIO = 0.2;
23
+ /** The reader supplied sentinel for a connection that only refreshes by hand. */
24
+ export const SCHEDULE_EXEMPT_BASE_SECONDS = 0;
25
+ /** Missing wire cadence fails closed to the most conservative live default. */
26
+ export const REFRESH_FALLBACK_BASE_SECONDS = 300;
27
+ /** A stable scheduled instant for a connection exempt from background reads. */
28
+ export const SCHEDULE_EXEMPT_INSTANT = "9999-12-31T23:59:59.999Z";
29
+ function usableRandom(random) {
30
+ let value;
31
+ try {
32
+ value = random();
33
+ }
34
+ catch {
35
+ return 0.5;
36
+ }
37
+ if (typeof value !== "number" || !Number.isFinite(value))
38
+ return 0.5;
39
+ return Math.min(Math.max(value, 0), 1);
40
+ }
41
+ function usableBase(baseSeconds) {
42
+ if (!Number.isFinite(baseSeconds) || baseSeconds < 0) {
43
+ return REFRESH_FALLBACK_BASE_SECONDS;
44
+ }
45
+ if (baseSeconds === SCHEDULE_EXEMPT_BASE_SECONDS) {
46
+ return Number.POSITIVE_INFINITY;
47
+ }
48
+ return Math.min(baseSeconds, REFRESH_BACKOFF_CEILING_SECONDS);
49
+ }
50
+ function usableAttempt(attempt) {
51
+ if (!Number.isFinite(attempt) || attempt <= 0)
52
+ return 0;
53
+ /*
54
+ * The exponent is clamped before it is used rather than after. Two to the
55
+ * power of a large attempt count overflows to Infinity long before the
56
+ * ceiling below could catch it, and Infinity times a jitter factor is not a
57
+ * date.
58
+ */
59
+ return Math.min(Math.floor(attempt), 32);
60
+ }
61
+ function usableRetryAfter(value) {
62
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
63
+ return null;
64
+ return Math.min(value, REFRESH_RETRY_AFTER_CEILING_SECONDS);
65
+ }
66
+ /**
67
+ * The delay before the next attempt, in seconds.
68
+ *
69
+ * Doubling per consecutive failure, capped at an hour, then jittered by up to
70
+ * a fifth either way so a hundred installations that all failed at the same
71
+ * moment do not all come back at the same moment.
72
+ *
73
+ * The order matters and is the whole subtlety here. Jitter is applied to OUR
74
+ * backoff and to nothing else, and the provider's Retry-After is then taken as
75
+ * a floor underneath the jittered result. Jittering a Retry-After instead would
76
+ * subtract up to a fifth from an instruction the provider gave us: a header
77
+ * asking for a day back would come back as nineteen hours and knock on the door
78
+ * almost five hours early, which is precisely the behaviour that gets an account
79
+ * limited harder. Jitter exists to spread our own retries, so it may only ever
80
+ * move our own number. A Retry-After shorter than our backoff changes nothing,
81
+ * because a provider cannot talk us out of backing off.
82
+ */
83
+ export function refreshDelaySeconds(input) {
84
+ const base = usableBase(input.baseSeconds);
85
+ if (base === Number.POSITIVE_INFINITY)
86
+ return base;
87
+ const attempt = usableAttempt(input.attempt);
88
+ const backoff = Math.min(base * Math.pow(2, attempt), REFRESH_BACKOFF_CEILING_SECONDS);
89
+ const jitter = 1 + (usableRandom(input.random) * 2 - 1) * REFRESH_JITTER_RATIO;
90
+ const jittered = backoff * jitter;
91
+ const retryAfter = usableRetryAfter(input.retryAfterSeconds);
92
+ return retryAfter === null ? jittered : Math.max(jittered, retryAfter);
93
+ }
94
+ /**
95
+ * The instant of the next attempt, or null when the supplied clock is not a
96
+ * readable instant. Nothing is guessed from a clock we cannot read.
97
+ */
98
+ export function nextRefreshAt(now, input) {
99
+ const current = Date.parse(now);
100
+ if (!Number.isFinite(current))
101
+ return null;
102
+ if (input.baseSeconds === SCHEDULE_EXEMPT_BASE_SECONDS) {
103
+ return SCHEDULE_EXEMPT_INSTANT;
104
+ }
105
+ const milliseconds = current + Math.round(refreshDelaySeconds(input) * 1_000);
106
+ if (!Number.isFinite(milliseconds))
107
+ return null;
108
+ const next = new Date(milliseconds);
109
+ return Number.isFinite(next.getTime()) ? next.toISOString() : null;
110
+ }
111
+ /**
112
+ * Whether a scheduled instant has arrived.
113
+ *
114
+ * A schedule we cannot read counts as due. The alternative is a connection that
115
+ * never refreshes again because one unreadable value got written once, and the
116
+ * first successful attempt replaces it with a readable one. A clock we cannot
117
+ * read is the opposite case and counts as not due, because with no present
118
+ * moment there is nothing to compare against.
119
+ */
120
+ export function isDue(nextAt, now) {
121
+ const current = Date.parse(now);
122
+ if (!Number.isFinite(current))
123
+ return false;
124
+ if (nextAt === null)
125
+ return true;
126
+ const scheduled = Date.parse(nextAt);
127
+ if (!Number.isFinite(scheduled))
128
+ return true;
129
+ return current >= scheduled;
130
+ }
131
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../src/schedule.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -8,6 +8,70 @@ export interface SnapshotWindow {
8
8
  kind: "rolling" | "fixed" | "lifetime" | "unknown";
9
9
  durationSeconds?: number;
10
10
  }
11
+ /**
12
+ * The one currency an amount may be stated in, for now.
13
+ *
14
+ * A literal rather than a string keeps a provider from writing its own text
15
+ * into a field a human reads. When a second currency is genuinely supported it
16
+ * is added here and the normalizer's set below grows with it.
17
+ */
18
+ export type SnapshotCurrency = "USD";
19
+ /** Largest money amount a reading may carry. Above this it is not a plan. */
20
+ export declare const MAX_SNAPSHOT_AMOUNT = 1000000;
21
+ /**
22
+ * What a credit based plan has spent, and out of how much.
23
+ *
24
+ * Optional everywhere and travelling as one unit: a reading either carries all
25
+ * three of these or none of them, so a surface can never print a used figure
26
+ * with no limit beside it. A percentage never depends on them, which is why a
27
+ * pair that fails validation is dropped while the percentage survives.
28
+ */
29
+ export interface SnapshotAmounts {
30
+ usedAmount: number;
31
+ limitAmount: number;
32
+ currency: SnapshotCurrency;
33
+ }
34
+ /**
35
+ * How an observation reached us, stated in our own vocabulary.
36
+ *
37
+ * A provider tells us what its meter reads. It never tells us how we read it,
38
+ * so this field is written by OpenLimiter and only ever holds one of the values
39
+ * below. A human surface can then say "this came from your local Claude Code"
40
+ * or "this came from an official API" without a provider being able to write
41
+ * that sentence for us.
42
+ */
43
+ export declare const SNAPSHOT_SOURCE_KINDS: readonly ["statusline_payload", "explicit_ingest", "manual_document", "remote_api", "unknown"];
44
+ export type SnapshotSourceKind = (typeof SNAPSHOT_SOURCE_KINDS)[number];
45
+ /**
46
+ * Which reader carried the observation. Closed for the same reason as above.
47
+ *
48
+ * The first three name a concrete path this product actually has, because a
49
+ * source chip saying "your Claude Code statusline" and one saying "you imported
50
+ * this" are the difference between a live reading and a paste, and a generic
51
+ * reader kind cannot tell a person which they are looking at. The rest are the
52
+ * reader kinds the connection architecture defines, kept for paths that exist
53
+ * on paper before they exist in code.
54
+ */
55
+ export declare const SNAPSHOT_OBSERVED_VIA: readonly ["claude_code_statusline", "ingest_command", "manual_json", "local_event", "local_file", "local_command", "remote_http", "user_entry", "unknown"];
56
+ export type SnapshotObservedVia = (typeof SNAPSHOT_OBSERVED_VIA)[number];
57
+ export interface SnapshotProvenance {
58
+ sourceKind: SnapshotSourceKind;
59
+ observedVia: SnapshotObservedVia;
60
+ }
61
+ /**
62
+ * What a reading's provenance becomes when the stated provenance cannot be
63
+ * believed. The reading itself still stands: how a number arrived is a separate
64
+ * question from whether the number is in range.
65
+ */
66
+ export declare const UNKNOWN_PROVENANCE: SnapshotProvenance;
67
+ /**
68
+ * Shape of an account identifier, when a source names one.
69
+ *
70
+ * Lowercase, digits and hyphens, never a space, so it can be joined into a cache
71
+ * identity with a space separator and never collide. It is an opaque local alias
72
+ * and is not required to be an account name any provider would recognise.
73
+ */
74
+ export declare const ACCOUNT_ID_PATTERN: RegExp;
11
75
  export interface ConnectorLabels {
12
76
  credentialOrigin: "official-local-tool" | "user-key" | "browser-session" | "user-entered";
13
77
  dataInterfaceStatus: "native-statusline-payload" | "documented-api" | "internal-endpoint" | "authenticated-scrape" | "manual";
@@ -26,6 +90,20 @@ export interface Snapshot {
26
90
  observedAt: string;
27
91
  expiresAt: string;
28
92
  labels: ConnectorLabels;
93
+ /** Present only when the provider's own documented payload carried money. */
94
+ usedAmount?: number;
95
+ limitAmount?: number;
96
+ currency?: SnapshotCurrency;
97
+ /**
98
+ * Which account this reading belongs to, when a source names one.
99
+ *
100
+ * Absent means one unnamed account, which is every reading written before
101
+ * multiple accounts existed. Absent is not the same as an account called
102
+ * "default": a row without this field keeps the identity it always had.
103
+ */
104
+ accountId?: string;
105
+ /** How the reading arrived. Absent means it was never recorded. */
106
+ provenance?: SnapshotProvenance;
29
107
  }
30
108
  export interface RawMeter {
31
109
  provider: unknown;
@@ -39,6 +117,11 @@ export interface RawMeter {
39
117
  observedAt: unknown;
40
118
  expiresAt: unknown;
41
119
  labels: unknown;
120
+ usedAmount?: unknown;
121
+ limitAmount?: unknown;
122
+ currency?: unknown;
123
+ accountId?: unknown;
124
+ provenance?: unknown;
42
125
  }
43
126
  export interface ConnectorReadContext {
44
127
  payload?: unknown;
@@ -52,10 +135,23 @@ export type ConnectorResult = {
52
135
  ok: false;
53
136
  reason: "unknown" | "unavailable" | "not_configured";
54
137
  };
138
+ /**
139
+ * What a connector's payload IS, before its parser sees it.
140
+ *
141
+ * Almost every provider answers JSON. OpenCode answers a logged in HTML page,
142
+ * because it publishes no usage interface at all. That difference cannot live
143
+ * only in the reader that happens to know about it: the desktop pipeline, the
144
+ * ingest command and the web engine all hand payloads to parsers, and any one
145
+ * of them that assumes JSON silently breaks the text reader. So the connector
146
+ * declares it, and every payload boundary asks.
147
+ */
148
+ export type ConnectorEncoding = "json" | "text";
55
149
  export interface ConnectorContract {
56
150
  readonly id: Lowercase<ProviderCode>;
57
151
  readonly displayName: string;
58
152
  readonly labels: ConnectorLabels;
153
+ /** Whether this connector's parser wants parsed JSON or the raw text. */
154
+ readonly encoding: ConnectorEncoding;
59
155
  detect(environment: Readonly<Record<string, string | undefined>>): boolean;
60
156
  read(context: ConnectorReadContext): Promise<ConnectorResult>;
61
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -6,4 +6,60 @@ export const PROVIDER_CODES = [
6
6
  "OPENCODE",
7
7
  "MANUAL"
8
8
  ];
9
+ /** Largest money amount a reading may carry. Above this it is not a plan. */
10
+ export const MAX_SNAPSHOT_AMOUNT = 1_000_000;
11
+ /**
12
+ * How an observation reached us, stated in our own vocabulary.
13
+ *
14
+ * A provider tells us what its meter reads. It never tells us how we read it,
15
+ * so this field is written by OpenLimiter and only ever holds one of the values
16
+ * below. A human surface can then say "this came from your local Claude Code"
17
+ * or "this came from an official API" without a provider being able to write
18
+ * that sentence for us.
19
+ */
20
+ export const SNAPSHOT_SOURCE_KINDS = [
21
+ "statusline_payload",
22
+ "explicit_ingest",
23
+ "manual_document",
24
+ "remote_api",
25
+ "unknown"
26
+ ];
27
+ /**
28
+ * Which reader carried the observation. Closed for the same reason as above.
29
+ *
30
+ * The first three name a concrete path this product actually has, because a
31
+ * source chip saying "your Claude Code statusline" and one saying "you imported
32
+ * this" are the difference between a live reading and a paste, and a generic
33
+ * reader kind cannot tell a person which they are looking at. The rest are the
34
+ * reader kinds the connection architecture defines, kept for paths that exist
35
+ * on paper before they exist in code.
36
+ */
37
+ export const SNAPSHOT_OBSERVED_VIA = [
38
+ "claude_code_statusline",
39
+ "ingest_command",
40
+ "manual_json",
41
+ "local_event",
42
+ "local_file",
43
+ "local_command",
44
+ "remote_http",
45
+ "user_entry",
46
+ "unknown"
47
+ ];
48
+ /**
49
+ * What a reading's provenance becomes when the stated provenance cannot be
50
+ * believed. The reading itself still stands: how a number arrived is a separate
51
+ * question from whether the number is in range.
52
+ */
53
+ export const UNKNOWN_PROVENANCE = {
54
+ sourceKind: "unknown",
55
+ observedVia: "unknown"
56
+ };
57
+ /**
58
+ * Shape of an account identifier, when a source names one.
59
+ *
60
+ * Lowercase, digits and hyphens, never a space, so it can be joined into a cache
61
+ * identity with a space separator and never collide. It is an opaque local alias
62
+ * and is not required to be an account name any provider would recognise.
63
+ */
64
+ export const ACCOUNT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/u;
9
65
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,aAAa;IACb,UAAU;IACV,QAAQ;CACA,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,aAAa;IACb,UAAU;IACV,QAAQ;CACA,CAAC;AA2BX,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAgB7C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,oBAAoB;IACpB,iBAAiB;IACjB,iBAAiB;IACjB,YAAY;IACZ,SAAS;CACD,CAAC;AAIX;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,wBAAwB;IACxB,gBAAgB;IAChB,aAAa;IACb,aAAa;IACb,YAAY;IACZ,eAAe;IACf,aAAa;IACb,YAAY;IACZ,SAAS;CACD,CAAC;AASX;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAuB;IACpD,UAAU,EAAE,SAAS;IACrB,WAAW,EAAE,SAAS;CACvB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,4BAA4B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlimiter/core",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "Shared types, quota models, cache handling, and forecasts for OpenLimiter.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",