@openlimiter/connectors 0.2.0 → 1.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/dist/antigravity.d.ts +50 -1
- package/dist/antigravity.d.ts.map +1 -1
- package/dist/antigravity.js +150 -19
- package/dist/antigravity.js.map +1 -1
- package/dist/claude.d.ts +72 -6
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +467 -35
- package/dist/claude.js.map +1 -1
- package/dist/codex.d.ts +23 -1
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +112 -13
- package/dist/codex.js.map +1 -1
- package/dist/contract-gate.d.ts +56 -0
- package/dist/contract-gate.d.ts.map +1 -0
- package/dist/contract-gate.js +100 -0
- package/dist/contract-gate.js.map +1 -0
- package/dist/fixtures.d.ts +274 -2
- package/dist/fixtures.d.ts.map +1 -1
- package/dist/fixtures.js +947 -17
- package/dist/fixtures.js.map +1 -1
- package/dist/gemini-cli.d.ts +13 -0
- package/dist/gemini-cli.d.ts.map +1 -0
- package/dist/gemini-cli.js +86 -0
- package/dist/gemini-cli.js.map +1 -0
- package/dist/grok.d.ts +13 -0
- package/dist/grok.d.ts.map +1 -0
- package/dist/grok.js +143 -0
- package/dist/grok.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/kimi.d.ts +13 -0
- package/dist/kimi.d.ts.map +1 -0
- package/dist/kimi.js +141 -0
- package/dist/kimi.js.map +1 -0
- package/dist/manual.d.ts +3 -1
- package/dist/manual.d.ts.map +1 -1
- package/dist/manual.js +7 -3
- package/dist/manual.js.map +1 -1
- package/dist/opencode.d.ts +103 -1
- package/dist/opencode.d.ts.map +1 -1
- package/dist/opencode.js +428 -21
- package/dist/opencode.js.map +1 -1
- package/dist/openrouter.d.ts +3 -1
- package/dist/openrouter.d.ts.map +1 -1
- package/dist/openrouter.js +7 -3
- package/dist/openrouter.js.map +1 -1
- package/dist/shared.d.ts +105 -1
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +190 -0
- package/dist/shared.js.map +1 -1
- package/package.json +4 -3
package/dist/openrouter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { boundedNumber, rawMeter, record, shortExpiry } from "./shared.js";
|
|
1
|
+
import { boundedNumber, connectorConnection, rawMeter, record, shortExpiry } from "./shared.js";
|
|
2
2
|
export const openrouterLabels = {
|
|
3
3
|
credentialOrigin: "user-key",
|
|
4
4
|
dataInterfaceStatus: "documented-api",
|
|
@@ -48,8 +48,11 @@ export function parseOpenrouterPayload(payload, now) {
|
|
|
48
48
|
amounts: { usedAmount: usage, limitAmount: credits, currency: "USD" }
|
|
49
49
|
})];
|
|
50
50
|
}
|
|
51
|
+
/** No local application owns this key, so no instruction names one. */
|
|
52
|
+
export const OPENROUTER_TOOL = null;
|
|
51
53
|
export const openrouterConnector = {
|
|
52
54
|
id: "openrouter",
|
|
55
|
+
encoding: "json",
|
|
53
56
|
displayName: "OpenRouter",
|
|
54
57
|
labels: openrouterLabels,
|
|
55
58
|
detect(environment) {
|
|
@@ -57,9 +60,10 @@ export const openrouterConnector = {
|
|
|
57
60
|
},
|
|
58
61
|
async read(context) {
|
|
59
62
|
const meters = parseOpenrouterPayload(context.payload, context.now);
|
|
63
|
+
const connection = connectorConnection(meters !== null, context.payload, OPENROUTER_TOOL);
|
|
60
64
|
return meters === null
|
|
61
|
-
? { ok: false, reason: "unknown" }
|
|
62
|
-
: { ok: true, meters };
|
|
65
|
+
? { ok: false, reason: "unknown", connection }
|
|
66
|
+
: { ok: true, meters, connection };
|
|
63
67
|
}
|
|
64
68
|
};
|
|
65
69
|
//# sourceMappingURL=openrouter.js.map
|
package/dist/openrouter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../src/openrouter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../src/openrouter.ts"],"names":[],"mappings":"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"}
|
package/dist/shared.d.ts
CHANGED
|
@@ -1,7 +1,111 @@
|
|
|
1
|
-
import type { ConnectorLabels, ProviderCode, RawMeter, SnapshotAmounts, SnapshotPrecision, SnapshotSource, SnapshotWindow } from "@openlimiter/core";
|
|
1
|
+
import type { ConnectionStatus, ConnectionTool, ConnectorLabels, ProviderCode, RawMeter, SnapshotAmounts, SnapshotPrecision, SnapshotSource, SnapshotWindow } from "@openlimiter/core";
|
|
2
|
+
/**
|
|
3
|
+
* The connection lifecycle a reader reports beside its meters.
|
|
4
|
+
*
|
|
5
|
+
* Three outcomes, and the difference between the last two is the whole reason
|
|
6
|
+
* this exists. Nothing arrived at all is a tool that has not run yet, which is
|
|
7
|
+
* a person's next step and not a fault. Something arrived and this build could
|
|
8
|
+
* not read it is a FAULT, and it is ours rather than the provider's, which is
|
|
9
|
+
* exactly the failure the Claude statusline contract mismatch was: a healthy
|
|
10
|
+
* account, a well formed payload, and a parser that answered null in silence.
|
|
11
|
+
*
|
|
12
|
+
* `tool` names the local application that owns the credential, so an
|
|
13
|
+
* instruction can say which window to open. It is never a value a payload
|
|
14
|
+
* supplied, and no instruction it produces is something this app does by
|
|
15
|
+
* itself: OpenLimiter reads tokens and refreshes none of them.
|
|
16
|
+
*/
|
|
17
|
+
export declare function connectorConnection(parsed: boolean, payload: unknown, tool: ConnectionTool): ConnectionStatus;
|
|
2
18
|
export declare function record(value: unknown): Record<string, unknown> | null;
|
|
3
19
|
export declare function boundedNumber(value: unknown, maximum?: number): number | null;
|
|
4
20
|
export declare function futureInstant(value: unknown, now: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Largest epoch value that is still read as seconds.
|
|
23
|
+
*
|
|
24
|
+
* A number this size in seconds is the year 33658, and the same number read as
|
|
25
|
+
* milliseconds is 2001. Anything at or above it is therefore a millisecond
|
|
26
|
+
* stamp handed to a seconds field, which is the single most likely way this
|
|
27
|
+
* conversion gets a reset instant wrong, so it is refused rather than turned
|
|
28
|
+
* into a date thirty thousand years away.
|
|
29
|
+
*/
|
|
30
|
+
export declare const MAX_EPOCH_SECONDS = 1000000000000;
|
|
31
|
+
/**
|
|
32
|
+
* Slack allowed between a machine's clock and a provider's clock.
|
|
33
|
+
*
|
|
34
|
+
* An hour covers a wrong time zone offset applied to a timestamp, which is the
|
|
35
|
+
* realistic way two correct systems disagree, without widening the plausibility
|
|
36
|
+
* bound below into something that accepts nonsense.
|
|
37
|
+
*/
|
|
38
|
+
export declare const CLOCK_SKEW_SECONDS = 3600;
|
|
39
|
+
/**
|
|
40
|
+
* The furthest ahead a window of a given length may plausibly reset.
|
|
41
|
+
*
|
|
42
|
+
* A rolling window cannot reset more than one window from now, so twice the
|
|
43
|
+
* window is already generous, and the skew allowance sits on top of that. This
|
|
44
|
+
* is what stops a five hour meter from claiming it resets in 2038: such a value
|
|
45
|
+
* is not a reset, it is a corrupt field, a different unit, or a different
|
|
46
|
+
* meaning, and rendering a twelve year countdown beside a five hour window
|
|
47
|
+
* would be the product lying with a straight face.
|
|
48
|
+
*/
|
|
49
|
+
export declare function plausibleResetHorizon(windowSeconds: number): number;
|
|
50
|
+
/**
|
|
51
|
+
* Read a reset instant stated as Unix epoch seconds.
|
|
52
|
+
*
|
|
53
|
+
* Claude Code states its reset times this way. The value must be a finite
|
|
54
|
+
* number, must be in seconds rather than milliseconds, must still be in the
|
|
55
|
+
* future against the supplied clock, because a window that has already reset
|
|
56
|
+
* says nothing about the window running now, and must be close enough to now to
|
|
57
|
+
* belong to the window it claims to describe. Nothing is repaired: a value that
|
|
58
|
+
* fails any of these is null and the caller drops that reading alone.
|
|
59
|
+
*
|
|
60
|
+
* `maxAheadSeconds` is the plausibility bound and is optional only so the
|
|
61
|
+
* helper stays usable for a source with no window length to reason from.
|
|
62
|
+
*/
|
|
63
|
+
export declare function futureInstantFromEpochSeconds(value: unknown, now: string, maxAheadSeconds?: number): string | null;
|
|
64
|
+
/**
|
|
65
|
+
* A fraction of a whole, as Google's quota summary states remaining quota.
|
|
66
|
+
*
|
|
67
|
+
* Zero and one are both real answers, so the bound is inclusive. A boolean is
|
|
68
|
+
* refused explicitly: JavaScript will happily do arithmetic on `true`, and a
|
|
69
|
+
* `remainingFraction` of `true` would otherwise become a usage of zero percent,
|
|
70
|
+
* which is the most dangerous wrong answer this product can give.
|
|
71
|
+
*/
|
|
72
|
+
export declare function boundedFraction(value: unknown): number | null;
|
|
73
|
+
/**
|
|
74
|
+
* A whole positive count of seconds, when a provider states a window length.
|
|
75
|
+
*
|
|
76
|
+
* Bounded above by a year, because a rolling window longer than that is not a
|
|
77
|
+
* subscription window, it is a misread field or a different unit.
|
|
78
|
+
*/
|
|
79
|
+
export declare const MAX_WINDOW_SECONDS = 31536000;
|
|
80
|
+
export declare function windowSeconds(value: unknown): number | null;
|
|
81
|
+
/**
|
|
82
|
+
* Read a reset instant stated as an RFC3339 timestamp.
|
|
83
|
+
*
|
|
84
|
+
* Google's quota summary states reset times this way, and the Antigravity
|
|
85
|
+
* client writes them with SEVEN fractional digits, which `Date.parse` on some
|
|
86
|
+
* runtimes reads and others do not. The fraction is normalised to three digits
|
|
87
|
+
* before parsing, which is the one repair made anywhere in this file, and it is
|
|
88
|
+
* a repair of ENCODING rather than of value: the instant it names is unchanged.
|
|
89
|
+
* Recorded 2026-08-07 against a real credential and a real response.
|
|
90
|
+
*
|
|
91
|
+
* An offsetless stamp is read as UTC explicitly. Both observed sources carry an
|
|
92
|
+
* offset today, so this is a guard rather than a fix, and it exists because the
|
|
93
|
+
* alternative is silent: a naive stamp read as local time moves a reset by the
|
|
94
|
+
* machine's offset instead of failing.
|
|
95
|
+
*/
|
|
96
|
+
export declare function futureInstantFromRfc3339(value: unknown, now: string, maxAheadSeconds?: number): string | null;
|
|
97
|
+
/**
|
|
98
|
+
* Seconds from a duration a page spelled out in words, or null.
|
|
99
|
+
*
|
|
100
|
+
* OpenCode's workspace page prints "Resets in 5 days 20 hours" rather than a
|
|
101
|
+
* timestamp, so the only reset available is a countdown rendered for a human.
|
|
102
|
+
* Only consecutive number and unit pairs are read, and an empty or zero total
|
|
103
|
+
* is null rather than now: a reset of zero seconds would render as a countdown
|
|
104
|
+
* that has already finished.
|
|
105
|
+
*/
|
|
106
|
+
export declare function durationSecondsFromWords(text: string): number | null;
|
|
107
|
+
/** An instant this many seconds after the supplied clock, or null. */
|
|
108
|
+
export declare function instantAfter(now: string, seconds: number): string | null;
|
|
5
109
|
export declare function shortExpiry(now: string): string | null;
|
|
6
110
|
export declare function rawMeter(input: {
|
|
7
111
|
provider: ProviderCode;
|
package/dist/shared.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACf,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,cAAc,GACnB,gBAAgB,CAMlB;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAIrE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,SAAM,GAAG,MAAM,GAAG,IAAI,CAO1E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOxE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,gBAAO,CAAC;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AAExC;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,MAAM,EACX,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,GAAG,IAAI,CAef;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG7D;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,WAAa,CAAC;AAE7C,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG3D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,MAAM,EACX,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,GAAG,IAAI,CAuBf;AAUD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAapE;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKxE;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAC9B,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,CAAC;IACxB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B,GAAG,QAAQ,CAGX"}
|
package/dist/shared.js
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
import { connectionStatus } from "@openlimiter/core";
|
|
2
|
+
/**
|
|
3
|
+
* The connection lifecycle a reader reports beside its meters.
|
|
4
|
+
*
|
|
5
|
+
* Three outcomes, and the difference between the last two is the whole reason
|
|
6
|
+
* this exists. Nothing arrived at all is a tool that has not run yet, which is
|
|
7
|
+
* a person's next step and not a fault. Something arrived and this build could
|
|
8
|
+
* not read it is a FAULT, and it is ours rather than the provider's, which is
|
|
9
|
+
* exactly the failure the Claude statusline contract mismatch was: a healthy
|
|
10
|
+
* account, a well formed payload, and a parser that answered null in silence.
|
|
11
|
+
*
|
|
12
|
+
* `tool` names the local application that owns the credential, so an
|
|
13
|
+
* instruction can say which window to open. It is never a value a payload
|
|
14
|
+
* supplied, and no instruction it produces is something this app does by
|
|
15
|
+
* itself: OpenLimiter reads tokens and refreshes none of them.
|
|
16
|
+
*/
|
|
17
|
+
export function connectorConnection(parsed, payload, tool) {
|
|
18
|
+
if (parsed)
|
|
19
|
+
return connectionStatus("CONNECTED", null, tool);
|
|
20
|
+
if (payload === undefined || payload === null) {
|
|
21
|
+
return connectionStatus("DETECTED", "tool_not_running", tool);
|
|
22
|
+
}
|
|
23
|
+
return connectionStatus("ERROR", "shape_mismatch", tool);
|
|
24
|
+
}
|
|
1
25
|
export function record(value) {
|
|
2
26
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
3
27
|
? value
|
|
@@ -20,6 +44,172 @@ export function futureInstant(value, now) {
|
|
|
20
44
|
? new Date(parsed).toISOString()
|
|
21
45
|
: null;
|
|
22
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Largest epoch value that is still read as seconds.
|
|
49
|
+
*
|
|
50
|
+
* A number this size in seconds is the year 33658, and the same number read as
|
|
51
|
+
* milliseconds is 2001. Anything at or above it is therefore a millisecond
|
|
52
|
+
* stamp handed to a seconds field, which is the single most likely way this
|
|
53
|
+
* conversion gets a reset instant wrong, so it is refused rather than turned
|
|
54
|
+
* into a date thirty thousand years away.
|
|
55
|
+
*/
|
|
56
|
+
export const MAX_EPOCH_SECONDS = 1e12;
|
|
57
|
+
/**
|
|
58
|
+
* Slack allowed between a machine's clock and a provider's clock.
|
|
59
|
+
*
|
|
60
|
+
* An hour covers a wrong time zone offset applied to a timestamp, which is the
|
|
61
|
+
* realistic way two correct systems disagree, without widening the plausibility
|
|
62
|
+
* bound below into something that accepts nonsense.
|
|
63
|
+
*/
|
|
64
|
+
export const CLOCK_SKEW_SECONDS = 3_600;
|
|
65
|
+
/**
|
|
66
|
+
* The furthest ahead a window of a given length may plausibly reset.
|
|
67
|
+
*
|
|
68
|
+
* A rolling window cannot reset more than one window from now, so twice the
|
|
69
|
+
* window is already generous, and the skew allowance sits on top of that. This
|
|
70
|
+
* is what stops a five hour meter from claiming it resets in 2038: such a value
|
|
71
|
+
* is not a reset, it is a corrupt field, a different unit, or a different
|
|
72
|
+
* meaning, and rendering a twelve year countdown beside a five hour window
|
|
73
|
+
* would be the product lying with a straight face.
|
|
74
|
+
*/
|
|
75
|
+
export function plausibleResetHorizon(windowSeconds) {
|
|
76
|
+
return windowSeconds * 2 + CLOCK_SKEW_SECONDS;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Read a reset instant stated as Unix epoch seconds.
|
|
80
|
+
*
|
|
81
|
+
* Claude Code states its reset times this way. The value must be a finite
|
|
82
|
+
* number, must be in seconds rather than milliseconds, must still be in the
|
|
83
|
+
* future against the supplied clock, because a window that has already reset
|
|
84
|
+
* says nothing about the window running now, and must be close enough to now to
|
|
85
|
+
* belong to the window it claims to describe. Nothing is repaired: a value that
|
|
86
|
+
* fails any of these is null and the caller drops that reading alone.
|
|
87
|
+
*
|
|
88
|
+
* `maxAheadSeconds` is the plausibility bound and is optional only so the
|
|
89
|
+
* helper stays usable for a source with no window length to reason from.
|
|
90
|
+
*/
|
|
91
|
+
export function futureInstantFromEpochSeconds(value, now, maxAheadSeconds) {
|
|
92
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
93
|
+
return null;
|
|
94
|
+
if (value <= 0 || value >= MAX_EPOCH_SECONDS)
|
|
95
|
+
return null;
|
|
96
|
+
const current = Date.parse(now);
|
|
97
|
+
if (!Number.isFinite(current))
|
|
98
|
+
return null;
|
|
99
|
+
const milliseconds = Math.round(value * 1_000);
|
|
100
|
+
if (milliseconds <= current)
|
|
101
|
+
return null;
|
|
102
|
+
if (typeof maxAheadSeconds === "number" &&
|
|
103
|
+
Number.isFinite(maxAheadSeconds) &&
|
|
104
|
+
maxAheadSeconds > 0 &&
|
|
105
|
+
milliseconds > current + maxAheadSeconds * 1_000)
|
|
106
|
+
return null;
|
|
107
|
+
const instant = new Date(milliseconds);
|
|
108
|
+
return Number.isFinite(instant.getTime()) ? instant.toISOString() : null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* A fraction of a whole, as Google's quota summary states remaining quota.
|
|
112
|
+
*
|
|
113
|
+
* Zero and one are both real answers, so the bound is inclusive. A boolean is
|
|
114
|
+
* refused explicitly: JavaScript will happily do arithmetic on `true`, and a
|
|
115
|
+
* `remainingFraction` of `true` would otherwise become a usage of zero percent,
|
|
116
|
+
* which is the most dangerous wrong answer this product can give.
|
|
117
|
+
*/
|
|
118
|
+
export function boundedFraction(value) {
|
|
119
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
120
|
+
return null;
|
|
121
|
+
return value >= 0 && value <= 1 ? value : null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A whole positive count of seconds, when a provider states a window length.
|
|
125
|
+
*
|
|
126
|
+
* Bounded above by a year, because a rolling window longer than that is not a
|
|
127
|
+
* subscription window, it is a misread field or a different unit.
|
|
128
|
+
*/
|
|
129
|
+
export const MAX_WINDOW_SECONDS = 31_536_000;
|
|
130
|
+
export function windowSeconds(value) {
|
|
131
|
+
if (typeof value !== "number" || !Number.isInteger(value))
|
|
132
|
+
return null;
|
|
133
|
+
return value > 0 && value <= MAX_WINDOW_SECONDS ? value : null;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Read a reset instant stated as an RFC3339 timestamp.
|
|
137
|
+
*
|
|
138
|
+
* Google's quota summary states reset times this way, and the Antigravity
|
|
139
|
+
* client writes them with SEVEN fractional digits, which `Date.parse` on some
|
|
140
|
+
* runtimes reads and others do not. The fraction is normalised to three digits
|
|
141
|
+
* before parsing, which is the one repair made anywhere in this file, and it is
|
|
142
|
+
* a repair of ENCODING rather than of value: the instant it names is unchanged.
|
|
143
|
+
* Recorded 2026-08-07 against a real credential and a real response.
|
|
144
|
+
*
|
|
145
|
+
* An offsetless stamp is read as UTC explicitly. Both observed sources carry an
|
|
146
|
+
* offset today, so this is a guard rather than a fix, and it exists because the
|
|
147
|
+
* alternative is silent: a naive stamp read as local time moves a reset by the
|
|
148
|
+
* machine's offset instead of failing.
|
|
149
|
+
*/
|
|
150
|
+
export function futureInstantFromRfc3339(value, now, maxAheadSeconds) {
|
|
151
|
+
if (typeof value !== "string" || value.length > 64)
|
|
152
|
+
return null;
|
|
153
|
+
const trimmed = value.trim();
|
|
154
|
+
/* Three digits is what every runtime agrees on; the rest are dropped, not
|
|
155
|
+
rounded, because sub millisecond precision on a quota reset is noise. */
|
|
156
|
+
const normalized = trimmed.replace(/\.(\d{1,9})/u, (_match, digits) => "." + (digits + "000").slice(0, 3));
|
|
157
|
+
const withZone = /(?:Z|[+-]\d{2}:?\d{2})$/u.test(normalized)
|
|
158
|
+
? normalized
|
|
159
|
+
: normalized + "Z";
|
|
160
|
+
const parsed = Date.parse(withZone);
|
|
161
|
+
const current = Date.parse(now);
|
|
162
|
+
if (!Number.isFinite(parsed) || !Number.isFinite(current))
|
|
163
|
+
return null;
|
|
164
|
+
if (parsed <= current)
|
|
165
|
+
return null;
|
|
166
|
+
if (typeof maxAheadSeconds === "number" &&
|
|
167
|
+
Number.isFinite(maxAheadSeconds) &&
|
|
168
|
+
maxAheadSeconds > 0 &&
|
|
169
|
+
parsed > current + maxAheadSeconds * 1_000)
|
|
170
|
+
return null;
|
|
171
|
+
return new Date(parsed).toISOString();
|
|
172
|
+
}
|
|
173
|
+
/** How many seconds each unit a provider spells out is worth. */
|
|
174
|
+
const DURATION_UNITS = {
|
|
175
|
+
day: 86_400,
|
|
176
|
+
hour: 3_600,
|
|
177
|
+
minute: 60,
|
|
178
|
+
second: 1
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Seconds from a duration a page spelled out in words, or null.
|
|
182
|
+
*
|
|
183
|
+
* OpenCode's workspace page prints "Resets in 5 days 20 hours" rather than a
|
|
184
|
+
* timestamp, so the only reset available is a countdown rendered for a human.
|
|
185
|
+
* Only consecutive number and unit pairs are read, and an empty or zero total
|
|
186
|
+
* is null rather than now: a reset of zero seconds would render as a countdown
|
|
187
|
+
* that has already finished.
|
|
188
|
+
*/
|
|
189
|
+
export function durationSecondsFromWords(text) {
|
|
190
|
+
let total = 0;
|
|
191
|
+
let seen = false;
|
|
192
|
+
const pattern = /(\d{1,6})\s*(day|hour|minute|second)s?/giu;
|
|
193
|
+
for (const match of text.matchAll(pattern)) {
|
|
194
|
+
const count = Number.parseInt(match[1] ?? "", 10);
|
|
195
|
+
const unit = DURATION_UNITS[(match[2] ?? "").toLowerCase()];
|
|
196
|
+
if (!Number.isFinite(count) || unit === undefined)
|
|
197
|
+
return null;
|
|
198
|
+
total += count * unit;
|
|
199
|
+
seen = true;
|
|
200
|
+
}
|
|
201
|
+
if (!seen || total <= 0 || total > MAX_WINDOW_SECONDS)
|
|
202
|
+
return null;
|
|
203
|
+
return total;
|
|
204
|
+
}
|
|
205
|
+
/** An instant this many seconds after the supplied clock, or null. */
|
|
206
|
+
export function instantAfter(now, seconds) {
|
|
207
|
+
const current = Date.parse(now);
|
|
208
|
+
if (!Number.isFinite(current))
|
|
209
|
+
return null;
|
|
210
|
+
const instant = new Date(current + seconds * 1_000);
|
|
211
|
+
return Number.isFinite(instant.getTime()) ? instant.toISOString() : null;
|
|
212
|
+
}
|
|
23
213
|
export function shortExpiry(now) {
|
|
24
214
|
const parsed = Date.parse(now);
|
|
25
215
|
return Number.isFinite(parsed) ? new Date(parsed + 60_000).toISOString() : null;
|
package/dist/shared.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openlimiter/connectors",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Provider connectors that collect quota and usage data for OpenLimiter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"NOTICE"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@openlimiter/core": "
|
|
22
|
+
"@openlimiter/core": "1.2.0"
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
"node": ">=24"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
|
-
"access": "public"
|
|
48
|
+
"access": "public",
|
|
49
|
+
"provenance": true
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
51
52
|
"build": "tsc -p tsconfig.json",
|