@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.
Files changed (53) hide show
  1. package/dist/antigravity.d.ts +50 -1
  2. package/dist/antigravity.d.ts.map +1 -1
  3. package/dist/antigravity.js +150 -19
  4. package/dist/antigravity.js.map +1 -1
  5. package/dist/claude.d.ts +72 -6
  6. package/dist/claude.d.ts.map +1 -1
  7. package/dist/claude.js +467 -35
  8. package/dist/claude.js.map +1 -1
  9. package/dist/codex.d.ts +23 -1
  10. package/dist/codex.d.ts.map +1 -1
  11. package/dist/codex.js +112 -13
  12. package/dist/codex.js.map +1 -1
  13. package/dist/contract-gate.d.ts +56 -0
  14. package/dist/contract-gate.d.ts.map +1 -0
  15. package/dist/contract-gate.js +100 -0
  16. package/dist/contract-gate.js.map +1 -0
  17. package/dist/fixtures.d.ts +274 -2
  18. package/dist/fixtures.d.ts.map +1 -1
  19. package/dist/fixtures.js +947 -17
  20. package/dist/fixtures.js.map +1 -1
  21. package/dist/gemini-cli.d.ts +13 -0
  22. package/dist/gemini-cli.d.ts.map +1 -0
  23. package/dist/gemini-cli.js +86 -0
  24. package/dist/gemini-cli.js.map +1 -0
  25. package/dist/grok.d.ts +13 -0
  26. package/dist/grok.d.ts.map +1 -0
  27. package/dist/grok.js +143 -0
  28. package/dist/grok.js.map +1 -0
  29. package/dist/index.d.ts +4 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +10 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/kimi.d.ts +13 -0
  34. package/dist/kimi.d.ts.map +1 -0
  35. package/dist/kimi.js +141 -0
  36. package/dist/kimi.js.map +1 -0
  37. package/dist/manual.d.ts +3 -1
  38. package/dist/manual.d.ts.map +1 -1
  39. package/dist/manual.js +7 -3
  40. package/dist/manual.js.map +1 -1
  41. package/dist/opencode.d.ts +103 -1
  42. package/dist/opencode.d.ts.map +1 -1
  43. package/dist/opencode.js +428 -21
  44. package/dist/opencode.js.map +1 -1
  45. package/dist/openrouter.d.ts +3 -1
  46. package/dist/openrouter.d.ts.map +1 -1
  47. package/dist/openrouter.js +7 -3
  48. package/dist/openrouter.js.map +1 -1
  49. package/dist/shared.d.ts +105 -1
  50. package/dist/shared.d.ts.map +1 -1
  51. package/dist/shared.js +190 -0
  52. package/dist/shared.js.map +1 -1
  53. package/package.json +4 -3
@@ -1,4 +1,24 @@
1
- import type { ConnectorContract, RawMeter } from "@openlimiter/core";
1
+ import type { ConnectionStatus, ConnectionTool, ConnectorContract, RawMeter } from "@openlimiter/core";
2
+ /**
3
+ * The OpenCode usage reader, and the least trustworthy one in the product.
4
+ *
5
+ * OpenCode publishes no usage interface at all. More than twenty candidate
6
+ * routes were probed on 2026-08-03 and the API key opens none of them: the plan
7
+ * percentages exist only inside the HTML of a logged in workspace page, server
8
+ * rendered. So this parser reads a PAGE, not a payload, and that is why its
9
+ * labels say browser-session, authenticated-scrape and automationRisk high, and
10
+ * why they do not improve when a capture lands. They describe the method, not
11
+ * the amount of evidence behind it.
12
+ *
13
+ * The reader used to parse `usage.percent` out of a JSON object, a shape from
14
+ * an early prototype that no OpenCode surface has ever produced.
15
+ *
16
+ * Two things make reading HTML survivable rather than reckless. Windows are
17
+ * matched by their LABEL and never by position, so a reordered page degrades to
18
+ * unknown instead of quietly reporting the monthly figure as the weekly one.
19
+ * And all three labels must be found: a page that has changed enough to lose
20
+ * one of them has changed enough not to be trusted for the other two.
21
+ */
2
22
  export declare const opencodeLabels: {
3
23
  readonly credentialOrigin: "browser-session";
4
24
  readonly dataInterfaceStatus: "authenticated-scrape";
@@ -6,12 +26,94 @@ export declare const opencodeLabels: {
6
26
  readonly verification: "UNVERIFIED";
7
27
  };
8
28
  export declare const opencodeHonesty: "UNVERIFIED_AUTHENTICATED_SCRAPE_HIGH_RISK";
29
+ /**
30
+ * BETA, and stated rather than implied.
31
+ *
32
+ * Every other reader in this package reads an interface: documented, private,
33
+ * or declared in a vendor's own client source, but an interface. This one reads
34
+ * a page that was designed for a person's eyes, whose wording nobody promised
35
+ * and which has already been renamed once underneath it. That is a different
36
+ * class of thing, and a surface that shows it beside the others has to be able
37
+ * to say so.
38
+ */
39
+ export declare const opencodeMaturity: "beta";
9
40
  export declare const opencodeInput: {
10
41
  readonly kind: "authenticated_page_payload";
11
42
  readonly pathTemplate: "{browserSession}/usage";
12
43
  readonly readMode: "read_only";
13
44
  readonly honesty: "UNVERIFIED_AUTHENTICATED_SCRAPE_HIGH_RISK";
45
+ readonly maturity: "beta";
14
46
  };
47
+ /**
48
+ * What this reader reads: text, not JSON.
49
+ *
50
+ * The collection pipeline keys on this. A body for this reader is handed over
51
+ * as the raw string it arrived as, because running it through a JSON parser
52
+ * first would turn every real response into a parse failure.
53
+ */
54
+ export declare const opencodeEncoding: "text";
55
+ /**
56
+ * How far this reader will search for a window's own container, in characters.
57
+ *
58
+ * A bound on WORK, not a boundary. The boundary is structural: see
59
+ * `windowContainer`. A character count alone was tried and it does not close
60
+ * the hole, because "the first percentage within two thousand characters after
61
+ * the label" still reaches a footer when the window's own percentage is absent.
62
+ * That is the difference between bounding how far a reader wanders and
63
+ * bounding where it is allowed to look at all.
64
+ */
65
+ export declare const OPENCODE_MAX_SEGMENT_CHARS = 2000;
66
+ /**
67
+ * Largest page this reader will look at, in characters.
68
+ *
69
+ * The transport already bounds the response at one mebibyte. This is the second
70
+ * bound, on the work done with it, so a page that is within the transport's
71
+ * limit still cannot make the label scan expensive.
72
+ */
73
+ export declare const OPENCODE_MAX_PAGE_CHARS = 1048576;
74
+ export type OpencodeMeter = "FIVE_HOUR" | "SEVEN_DAY" | "MONTHLY";
75
+ /**
76
+ * The cadences the workspace page renders, and how each one is recognised.
77
+ *
78
+ * This used to be three exact phrases, all three required, and the page renamed
79
+ * them. Three exact strings is the most brittle possible way to read a document
80
+ * whose wording nobody promised us, and the failure was total: one renamed
81
+ * heading and the whole reader went dark on a page still rendering all three
82
+ * figures.
83
+ *
84
+ * So a cadence is now recognised by the WORD that names it, case insensitively,
85
+ * with the wordings seen so far and the obvious neighbours of each. Structure
86
+ * still does the real work: a match only produces a reading if it sits in a
87
+ * container that holds a percentage and names no other cadence, which is the
88
+ * guard that stops a heading from borrowing the figure beside it.
89
+ */
90
+ export declare const OPENCODE_WINDOWS: readonly {
91
+ readonly meter: OpencodeMeter;
92
+ readonly seconds: number;
93
+ /** Every wording this cadence has been seen under, matched case blind. */
94
+ readonly pattern: RegExp;
95
+ }[];
96
+ /**
97
+ * Every provider window, including the monthly quota.
98
+ *
99
+ * The shared policy still derives the binding window by choosing the highest
100
+ * percentage. Keeping the three source windows lets every surface state which
101
+ * cadence a number belongs to instead of hiding the monthly value inside one
102
+ * synthetic primary meter.
103
+ */
15
104
  export declare function parseOpencodePayload(payload: unknown, now: string): RawMeter[] | null;
105
+ /** The local application that owns this browser session. */
106
+ export declare const OPENCODE_TOOL: ConnectionTool;
107
+ /**
108
+ * The connection this reader reports, which is not the generic one.
109
+ *
110
+ * A page it cannot read has two very different causes, and they need two
111
+ * different sentences. A logged out workspace renders a sign in form, and the
112
+ * fix is one click in a window the person already has open. Anything else is a
113
+ * layout this build no longer understands, which is ours to fix, and the honest
114
+ * instruction is to reconnect and tell us if it keeps happening. Neither one
115
+ * throws, and neither one is silence.
116
+ */
117
+ export declare function opencodeConnection(parsed: boolean, payload: unknown): ConnectionStatus;
16
118
  export declare const opencodeConnector: ConnectorContract;
17
119
  //# sourceMappingURL=opencode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EAGjB,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAG3B,eAAO,MAAM,cAAc;;;;;CAKS,CAAC;AAErC,eAAO,MAAM,eAAe,EAAG,2CAAoD,CAAC;AAEpF,eAAO,MAAM,aAAa;;;;;CAKhB,CAAC;AAEX,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAmBrF;AAED,eAAO,MAAM,iBAAiB,EAAE,iBAa/B,CAAC"}
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EAIjB,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAQ3B;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,cAAc;;;;;CAKS,CAAC;AAErC,eAAO,MAAM,eAAe,EAAG,2CAAoD,CAAC;AAEpF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,QAA8C,CAAC;AAE5E,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAG,MAAe,CAAC;AAEhD;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AA6ChD;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,UAAY,CAAC;AAEjD,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;AAElE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS;IACtC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,EAgBA,CAAC;AAqPF;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAkCrF;AAED,4DAA4D;AAC5D,eAAO,MAAM,aAAa,EAAE,cAA2B,CAAC;AAExD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAmBtF;AAED,eAAO,MAAM,iBAAiB,EAAE,iBAgB/B,CAAC"}
package/dist/opencode.js CHANGED
@@ -1,4 +1,26 @@
1
- import { boundedNumber, futureInstant, rawMeter, record, shortExpiry } from "./shared.js";
1
+ // This interface is UNOFFICIAL and may break.
2
+ import { connectionStatus } from "@openlimiter/core";
3
+ import { durationSecondsFromWords, instantAfter, rawMeter, shortExpiry } from "./shared.js";
4
+ /**
5
+ * The OpenCode usage reader, and the least trustworthy one in the product.
6
+ *
7
+ * OpenCode publishes no usage interface at all. More than twenty candidate
8
+ * routes were probed on 2026-08-03 and the API key opens none of them: the plan
9
+ * percentages exist only inside the HTML of a logged in workspace page, server
10
+ * rendered. So this parser reads a PAGE, not a payload, and that is why its
11
+ * labels say browser-session, authenticated-scrape and automationRisk high, and
12
+ * why they do not improve when a capture lands. They describe the method, not
13
+ * the amount of evidence behind it.
14
+ *
15
+ * The reader used to parse `usage.percent` out of a JSON object, a shape from
16
+ * an early prototype that no OpenCode surface has ever produced.
17
+ *
18
+ * Two things make reading HTML survivable rather than reckless. Windows are
19
+ * matched by their LABEL and never by position, so a reordered page degrades to
20
+ * unknown instead of quietly reporting the monthly figure as the weekly one.
21
+ * And all three labels must be found: a page that has changed enough to lose
22
+ * one of them has changed enough not to be trusted for the other two.
23
+ */
2
24
  export const opencodeLabels = {
3
25
  credentialOrigin: "browser-session",
4
26
  dataInterfaceStatus: "authenticated-scrape",
@@ -6,45 +28,430 @@ export const opencodeLabels = {
6
28
  verification: "UNVERIFIED"
7
29
  };
8
30
  export const opencodeHonesty = "UNVERIFIED_AUTHENTICATED_SCRAPE_HIGH_RISK";
31
+ /**
32
+ * BETA, and stated rather than implied.
33
+ *
34
+ * Every other reader in this package reads an interface: documented, private,
35
+ * or declared in a vendor's own client source, but an interface. This one reads
36
+ * a page that was designed for a person's eyes, whose wording nobody promised
37
+ * and which has already been renamed once underneath it. That is a different
38
+ * class of thing, and a surface that shows it beside the others has to be able
39
+ * to say so.
40
+ */
41
+ export const opencodeMaturity = "beta";
9
42
  export const opencodeInput = {
10
43
  kind: "authenticated_page_payload",
11
44
  pathTemplate: "{browserSession}/usage",
12
45
  readMode: "read_only",
13
- honesty: opencodeHonesty
46
+ honesty: opencodeHonesty,
47
+ maturity: opencodeMaturity
14
48
  };
49
+ /**
50
+ * What this reader reads: text, not JSON.
51
+ *
52
+ * The collection pipeline keys on this. A body for this reader is handed over
53
+ * as the raw string it arrived as, because running it through a JSON parser
54
+ * first would turn every real response into a parse failure.
55
+ */
56
+ export const opencodeEncoding = "text";
57
+ /**
58
+ * How far this reader will search for a window's own container, in characters.
59
+ *
60
+ * A bound on WORK, not a boundary. The boundary is structural: see
61
+ * `windowContainer`. A character count alone was tried and it does not close
62
+ * the hole, because "the first percentage within two thousand characters after
63
+ * the label" still reaches a footer when the window's own percentage is absent.
64
+ * That is the difference between bounding how far a reader wanders and
65
+ * bounding where it is allowed to look at all.
66
+ */
67
+ export const OPENCODE_MAX_SEGMENT_CHARS = 2_000;
68
+ /**
69
+ * Elements that never close, so a scan must not wait for a closing tag.
70
+ *
71
+ * Not exhaustive HTML: exhaustive for what a rendered page puts inside a meter
72
+ * block. An element outside this list that never closes makes the container
73
+ * scan run past its own end, and the scan answers null rather than guessing,
74
+ * which is the direction that costs a reading instead of inventing one.
75
+ */
76
+ const VOID_ELEMENTS = new Set([
77
+ "area", "base", "br", "col", "embed", "hr", "img", "input",
78
+ "link", "meta", "param", "source", "track", "wbr"
79
+ ]);
80
+ /** Every tag in a slice of text, in order. Comments are not tags. */
81
+ function tagsIn(html, from, to) {
82
+ const tags = [];
83
+ const pattern = /<!--[\s\S]*?-->|<\s*(\/?)\s*([a-zA-Z][a-zA-Z0-9-]*)([^>]*)>/gu;
84
+ const slice = html.slice(from, to);
85
+ for (const match of slice.matchAll(pattern)) {
86
+ /* A comment matched the first alternative and has no tag name. */
87
+ if (match[2] === undefined)
88
+ continue;
89
+ const name = match[2].toLowerCase();
90
+ const attributes = match[3] ?? "";
91
+ tags.push({
92
+ at: from + (match.index ?? 0),
93
+ end: from + (match.index ?? 0) + match[0].length,
94
+ name,
95
+ closing: match[1] === "/",
96
+ selfClosing: attributes.trimEnd().endsWith("/") || VOID_ELEMENTS.has(name)
97
+ });
98
+ }
99
+ return tags;
100
+ }
101
+ /**
102
+ * Largest page this reader will look at, in characters.
103
+ *
104
+ * The transport already bounds the response at one mebibyte. This is the second
105
+ * bound, on the work done with it, so a page that is within the transport's
106
+ * limit still cannot make the label scan expensive.
107
+ */
108
+ export const OPENCODE_MAX_PAGE_CHARS = 1_048_576;
109
+ /**
110
+ * The cadences the workspace page renders, and how each one is recognised.
111
+ *
112
+ * This used to be three exact phrases, all three required, and the page renamed
113
+ * them. Three exact strings is the most brittle possible way to read a document
114
+ * whose wording nobody promised us, and the failure was total: one renamed
115
+ * heading and the whole reader went dark on a page still rendering all three
116
+ * figures.
117
+ *
118
+ * So a cadence is now recognised by the WORD that names it, case insensitively,
119
+ * with the wordings seen so far and the obvious neighbours of each. Structure
120
+ * still does the real work: a match only produces a reading if it sits in a
121
+ * container that holds a percentage and names no other cadence, which is the
122
+ * guard that stops a heading from borrowing the figure beside it.
123
+ */
124
+ export const OPENCODE_WINDOWS = [
125
+ {
126
+ meter: "FIVE_HOUR",
127
+ seconds: 18_000,
128
+ pattern: /\b(?:rolling|session|5[\s-]*hour|five[\s-]*hour)\b/giu
129
+ },
130
+ {
131
+ meter: "SEVEN_DAY",
132
+ seconds: 604_800,
133
+ pattern: /\b(?:weekly|week|7[\s-]*day|seven[\s-]*day)\b/giu
134
+ },
135
+ {
136
+ meter: "MONTHLY",
137
+ seconds: 2_592_000,
138
+ pattern: /\b(?:monthly|month|30[\s-]*day|thirty[\s-]*day)\b/giu
139
+ }
140
+ ];
141
+ /**
142
+ * What a logged out workspace page says instead of a meter.
143
+ *
144
+ * Read only to tell a person WHICH thing went wrong. A page this reader cannot
145
+ * read is either a layout change or a session that ended, and those need
146
+ * different sentences: one is ours to fix and the other is one click.
147
+ */
148
+ const SIGNED_OUT = /\b(?:sign[\s-]?in|sign[\s-]?up|log[\s-]?in|create an account)\b/iu;
149
+ /**
150
+ * Markup and hydration comments, removed so text can be matched.
151
+ *
152
+ * The page is server rendered by a framework that sprays hydration markers
153
+ * through the text, so it renders "Resets in<!--/--> <!--$-->5 days<!--/-->".
154
+ * Matching against raw HTML silently loses every reset time, which is a missing
155
+ * countdown rather than a wrong number, but missing for an invisible reason is
156
+ * still the worst kind of missing. Verified against the live page 2026-08-03.
157
+ */
158
+ const MARKUP = /<!--[\s\S]*?-->|<[^>]*>/gu;
159
+ /**
160
+ * A percentage as the page prints it.
161
+ *
162
+ * The decimal part is optional because the page has printed both "92%" and
163
+ * "92.4%", and a reader that only knows whole numbers reads the second as 92
164
+ * or as nothing at all depending on where the match lands. The number pattern
165
+ * is what this reader trusts; the wording around it is not.
166
+ */
167
+ const PERCENT = /(\d{1,3}(?:\.\d{1,2})?)\s*%/u;
168
+ /**
169
+ * A countdown as the page prints it.
170
+ *
171
+ * Anchored on the verb rather than one exact phrase, because "Resets in",
172
+ * "Renews in" and "Refreshes in" all name the same thing and the page has used
173
+ * more than one. It still consumes only consecutive number and unit pairs, so
174
+ * it cannot wander into the next window's duration if a boundary ever moves.
175
+ */
176
+ const RESETS_IN = /\b(?:resets?|renews?|refreshes|refresh)\b(?:\s+in)?[:\s]\s*((?:\d{1,6}\s*(?:day|hour|minute|second)s?\s*)+)/iu;
177
+ function flatten(fragment) {
178
+ return fragment.replace(MARKUP, " ").split(/\s+/u).filter(Boolean).join(" ");
179
+ }
180
+ /** How many ancestors of a label this reader will consider. */
181
+ const MAX_CONTAINER_DEPTH = 6;
182
+ /**
183
+ * The element that encloses a position, as a half open range, or null.
184
+ *
185
+ * Found by walking backwards to the nearest element still open at that point,
186
+ * then forwards to its own closing tag. Both walks are bounded, and every
187
+ * failure answers null: an unbalanced page is one this reader declines rather
188
+ * than one it reads approximately.
189
+ */
190
+ function enclosingElement(html, at) {
191
+ const searchFrom = Math.max(0, at - OPENCODE_MAX_SEGMENT_CHARS);
192
+ const before = tagsIn(html, searchFrom, at);
193
+ /* Backwards: a closing tag means a sibling element already finished, so the
194
+ next opening tag going left belongs to it and not to us. */
195
+ let pendingCloses = 0;
196
+ let container = null;
197
+ for (let index = before.length - 1; index >= 0; index -= 1) {
198
+ const tag = before[index];
199
+ if (tag.selfClosing)
200
+ continue;
201
+ if (tag.closing) {
202
+ pendingCloses += 1;
203
+ continue;
204
+ }
205
+ if (pendingCloses > 0) {
206
+ pendingCloses -= 1;
207
+ continue;
208
+ }
209
+ container = tag;
210
+ break;
211
+ }
212
+ if (container === null)
213
+ return null;
214
+ /* Forwards from just inside the container to its own close, counting depth so
215
+ a nested element of the same name cannot end the region early. */
216
+ const searchTo = Math.min(html.length, container.end + OPENCODE_MAX_SEGMENT_CHARS);
217
+ let depth = 0;
218
+ for (const tag of tagsIn(html, container.end, searchTo)) {
219
+ if (tag.selfClosing)
220
+ continue;
221
+ if (!tag.closing) {
222
+ depth += 1;
223
+ continue;
224
+ }
225
+ if (depth === 0) {
226
+ return tag.name === container.name
227
+ ? { openedAt: container.at, from: container.end, to: tag.at }
228
+ /* A closing tag for something else pops above our container, which
229
+ means the page is unbalanced here. Decline it. */
230
+ : null;
231
+ }
232
+ depth -= 1;
233
+ }
234
+ return null;
235
+ }
236
+ /**
237
+ * The region a window's reading may be read from, or null.
238
+ *
239
+ * This is the fix for the final segment attack, and the reason a character count
240
+ * could not be. A window's figure is not inside the heading that names it: it is
241
+ * a SIBLING of that heading, in a shared block. So the readable region is an
242
+ * ancestor of the label, and the right ancestor is the nearest one that holds a
243
+ * percentage while naming no other window.
244
+ *
245
+ * Both halves of that rule carry weight. Requiring a percentage is what walks
246
+ * past the bare heading element. Refusing a region that names another window is
247
+ * what stops the walk at the meter block instead of continuing up to the page
248
+ * body, which contains every window's figure and would let any of them stand in
249
+ * for any other. When no ancestor satisfies both, there is no region, and a
250
+ * window with no region is refused rather than approximated: that is the case
251
+ * the audit named, a monthly block that rendered no figure and a footer a few
252
+ * characters later that did.
253
+ */
254
+ function windowRegion(html, labelAt, otherLabels) {
255
+ let position = labelAt;
256
+ for (let level = 0; level < MAX_CONTAINER_DEPTH; level += 1) {
257
+ const element = enclosingElement(html, position);
258
+ if (element === null)
259
+ return null;
260
+ const segment = flatten(html.slice(element.from, element.to));
261
+ if (otherLabels.some((pattern) => matches(pattern, segment)))
262
+ return null;
263
+ if (PERCENT.test(segment))
264
+ return segment;
265
+ /* No figure at this level, so try the block above it. */
266
+ position = element.openedAt;
267
+ }
268
+ return null;
269
+ }
270
+ /**
271
+ * Whether a position sits inside a tag rather than in text a person reads.
272
+ *
273
+ * A cadence word in `class="monthly-card"` is markup, not a heading, and
274
+ * treating it as one would put a label in a place the reader then has to
275
+ * resolve a container for. Cheap and exact enough: the last angle bracket
276
+ * before the position decides.
277
+ */
278
+ function insideTag(html, at) {
279
+ return html.lastIndexOf("<", at) > html.lastIndexOf(">", at);
280
+ }
281
+ /** Whether a pattern matches, without carrying lastIndex between calls. */
282
+ function matches(pattern, text) {
283
+ pattern.lastIndex = 0;
284
+ return pattern.test(text);
285
+ }
286
+ /**
287
+ * Where each cadence is named on the page, when it is named exactly once.
288
+ *
289
+ * A cadence named nowhere is a window this page is not rendering. A cadence
290
+ * named twice is two candidate containers and no way to know which one is the
291
+ * meter, so it is ambiguous. Both cost that cadence and nothing else: losing
292
+ * one bar is a smaller failure than drawing a bar with somebody else's number
293
+ * in it, and losing all three because one heading was renamed, which is what
294
+ * this reader used to do, is the largest failure of the three.
295
+ */
296
+ function findLabels(html) {
297
+ const hits = [];
298
+ for (const window of OPENCODE_WINDOWS) {
299
+ window.pattern.lastIndex = 0;
300
+ const positions = [];
301
+ for (const match of html.matchAll(window.pattern)) {
302
+ const at = match.index ?? -1;
303
+ if (at < 0 || insideTag(html, at))
304
+ continue;
305
+ positions.push(at);
306
+ /* Two is already ambiguous, so there is no reason to scan a large page
307
+ looking for a third. */
308
+ if (positions.length > 1)
309
+ break;
310
+ }
311
+ if (positions.length !== 1)
312
+ continue;
313
+ hits.push({
314
+ at: positions[0] ?? 0,
315
+ meter: window.meter,
316
+ seconds: window.seconds,
317
+ others: OPENCODE_WINDOWS
318
+ .filter((other) => other.meter !== window.meter)
319
+ .map((other) => other.pattern)
320
+ });
321
+ }
322
+ return hits.sort((left, right) => left.at - right.at);
323
+ }
324
+ /**
325
+ * Every window this page states, or null when it states none this reader can
326
+ * believe.
327
+ *
328
+ * Each window's figure is read out of its own CONTAINER and nowhere else, so a
329
+ * percentage can only ever be attributed to the block that rendered it. A
330
+ * window whose container cannot be resolved, or holds no percentage, or holds
331
+ * an impossible one, is dropped alone: the page is a rendered document rather
332
+ * than a contract, and one block changing shape is not a reason to discard the
333
+ * blocks that did not.
334
+ */
335
+ function parseWindows(html, now) {
336
+ const windows = [];
337
+ for (const hit of findLabels(html)) {
338
+ const segment = windowRegion(html, hit.at, hit.others);
339
+ if (segment === null)
340
+ continue;
341
+ const percentMatch = PERCENT.exec(segment);
342
+ if (percentMatch === null)
343
+ continue;
344
+ const percent = Number.parseFloat(percentMatch[1] ?? "");
345
+ if (!Number.isFinite(percent) || percent < 0 || percent > 100)
346
+ continue;
347
+ /* The countdown is optional, because the page omits it on a window that has
348
+ no reset pending. A missing countdown costs the countdown and nothing
349
+ else: the percentage beside it was still rendered by the provider. */
350
+ const resetMatch = RESETS_IN.exec(segment);
351
+ const seconds = resetMatch === null ? null : durationSecondsFromWords(resetMatch[1] ?? "");
352
+ windows.push({
353
+ meter: hit.meter,
354
+ percent,
355
+ seconds: hit.seconds,
356
+ resetAt: seconds === null ? null : instantAfter(now, seconds)
357
+ });
358
+ }
359
+ return windows.length === 0 ? null : windows;
360
+ }
361
+ /**
362
+ * Every provider window, including the monthly quota.
363
+ *
364
+ * The shared policy still derives the binding window by choosing the highest
365
+ * percentage. Keeping the three source windows lets every surface state which
366
+ * cadence a number belongs to instead of hiding the monthly value inside one
367
+ * synthetic primary meter.
368
+ */
15
369
  export function parseOpencodePayload(payload, now) {
16
- const root = record(payload);
17
- const usage = record(root?.["usage"]);
18
- const percent = boundedNumber(usage?.["percent"]);
19
- const resetAt = futureInstant(usage?.["reset_at"], now);
370
+ if (typeof payload !== "string")
371
+ return null;
372
+ if (payload.length === 0 || payload.length > OPENCODE_MAX_PAGE_CHARS)
373
+ return null;
374
+ /*
375
+ * Fail soft, and mean it. This is the only reader in the package pointed at a
376
+ * rendered document rather than an interface, so it is the only one where an
377
+ * unforeseen shape can reach code that was not written for it. A thrown error
378
+ * here would take down whatever was collecting, which would turn one changed
379
+ * page into a broken product, so the whole scan answers null instead and the
380
+ * connection below says what a person should do about it.
381
+ */
382
+ let windows = null;
383
+ try {
384
+ windows = parseWindows(payload, now);
385
+ }
386
+ catch {
387
+ return null;
388
+ }
389
+ if (windows === null || windows.length === 0)
390
+ return null;
20
391
  const expiresAt = shortExpiry(now);
21
- if (percent === null || resetAt === null || expiresAt === null)
392
+ if (expiresAt === null)
22
393
  return null;
23
- return [rawMeter({
24
- provider: "OPENCODE",
25
- meter: "PRIMARY",
26
- value: percent,
27
- window: { kind: "fixed" },
28
- resetAt,
29
- source: "authenticated_page",
30
- precision: "estimated",
31
- observedAt: now,
32
- expiresAt,
33
- labels: opencodeLabels
34
- })];
394
+ return windows.map((window) => rawMeter({
395
+ provider: "OPENCODE",
396
+ meter: window.meter,
397
+ value: window.percent,
398
+ window: { kind: "rolling", durationSeconds: window.seconds },
399
+ resetAt: window.resetAt,
400
+ source: "authenticated_page",
401
+ precision: "estimated",
402
+ observedAt: now,
403
+ expiresAt,
404
+ labels: opencodeLabels
405
+ }));
406
+ }
407
+ /** The local application that owns this browser session. */
408
+ export const OPENCODE_TOOL = "OpenCode";
409
+ /**
410
+ * The connection this reader reports, which is not the generic one.
411
+ *
412
+ * A page it cannot read has two very different causes, and they need two
413
+ * different sentences. A logged out workspace renders a sign in form, and the
414
+ * fix is one click in a window the person already has open. Anything else is a
415
+ * layout this build no longer understands, which is ours to fix, and the honest
416
+ * instruction is to reconnect and tell us if it keeps happening. Neither one
417
+ * throws, and neither one is silence.
418
+ */
419
+ export function opencodeConnection(parsed, payload) {
420
+ /*
421
+ * The signed out marker is read BEFORE the parse result, and the order is the
422
+ * whole point. A page asking somebody to sign in is a page whose numbers are
423
+ * leftovers, and this reader can still find a percentage in leftovers, so
424
+ * asking "did it parse" first reports a live looking meter behind an account
425
+ * nobody is signed into. That is the exact claim this product exists to stop.
426
+ */
427
+ if (typeof payload === "string" && SIGNED_OUT.test(payload)) {
428
+ return connectionStatus("AUTH_EXPIRED", "token_expired", OPENCODE_TOOL);
429
+ }
430
+ if (parsed)
431
+ return connectionStatus("CONNECTED", null, OPENCODE_TOOL);
432
+ /* Nothing arrived at all, which is a session that has not been opened yet
433
+ rather than a fault. Anything that DID arrive and could not be read is a
434
+ fault, whatever type it turned out to be. */
435
+ if (payload === undefined || payload === null) {
436
+ return connectionStatus("DETECTED", "tool_not_running", OPENCODE_TOOL);
437
+ }
438
+ return connectionStatus("ERROR", "shape_mismatch", OPENCODE_TOOL);
35
439
  }
36
440
  export const opencodeConnector = {
37
441
  id: "opencode",
38
442
  displayName: "OpenCode",
443
+ encoding: "text",
444
+ maturity: opencodeMaturity,
39
445
  labels: opencodeLabels,
40
446
  detect(environment) {
41
447
  return environment["OPENCODE_SESSION_PRESENT"] === "1";
42
448
  },
43
449
  async read(context) {
44
450
  const meters = parseOpencodePayload(context.payload, context.now);
451
+ const connection = opencodeConnection(meters !== null, context.payload);
45
452
  return meters === null
46
- ? { ok: false, reason: "unknown" }
47
- : { ok: true, meters };
453
+ ? { ok: false, reason: "unknown", connection }
454
+ : { ok: true, meters, connection };
48
455
  }
49
456
  };
50
457
  //# sourceMappingURL=opencode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1F,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,gBAAgB,EAAE,iBAAiB;IACnC,mBAAmB,EAAE,sBAAsB;IAC3C,cAAc,EAAE,MAAM;IACtB,YAAY,EAAE,YAAY;CACQ,CAAC;AAErC,MAAM,CAAC,MAAM,eAAe,GAAG,2CAAoD,CAAC;AAEpF,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,4BAA4B;IAClC,YAAY,EAAE,wBAAwB;IACtC,QAAQ,EAAE,WAAW;IACrB,OAAO,EAAE,eAAe;CAChB,CAAC;AAEX,MAAM,UAAU,oBAAoB,CAAC,OAAgB,EAAE,GAAW;IAChE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5E,OAAO,CAAC,QAAQ,CAAC;YACf,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;YACzB,OAAO;YACP,MAAM,EAAE,oBAAoB;YAC5B,SAAS,EAAE,WAAW;YACtB,UAAU,EAAE,GAAG;YACf,SAAS;YACT,MAAM,EAAE,cAAc;SACvB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,EAAE,EAAE,UAAU;IACd,WAAW,EAAE,UAAU;IACvB,MAAM,EAAE,cAAc;IACtB,MAAM,CAAC,WAAW;QAChB,OAAO,WAAW,CAAC,0BAA0B,CAAC,KAAK,GAAG,CAAC;IACzD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,OAAO;QAChB,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAClE,OAAO,MAAM,KAAK,IAAI;YACpB,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE;YAClC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC3B,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ConnectorContract, RawMeter } from "@openlimiter/core";
1
+ import type { ConnectionTool, ConnectorContract, RawMeter } from "@openlimiter/core";
2
2
  export declare const openrouterLabels: {
3
3
  readonly credentialOrigin: "user-key";
4
4
  readonly dataInterfaceStatus: "documented-api";
@@ -13,5 +13,7 @@ export declare const openrouterCredential: {
13
13
  readonly readMode: "read_only";
14
14
  };
15
15
  export declare function parseOpenrouterPayload(payload: unknown, now: string): RawMeter[] | null;
16
+ /** No local application owns this key, so no instruction names one. */
17
+ export declare const OPENROUTER_TOOL: ConnectionTool;
16
18
  export declare const openrouterConnector: ConnectorContract;
17
19
  //# sourceMappingURL=openrouter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../src/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAGjB,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAG3B,eAAO,MAAM,gBAAgB;;;;;CAKO,CAAC;AAErC,eAAO,MAAM,oBAAoB;;;;;;CAMvB,CAAC;AAEX,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAmCvF;AAED,eAAO,MAAM,mBAAmB,EAAE,iBAajC,CAAC"}
1
+ {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../src/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAGjB,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAS3B,eAAO,MAAM,gBAAgB;;;;;CAKO,CAAC;AAErC,eAAO,MAAM,oBAAoB;;;;;;CAMvB,CAAC;AAEX,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAmCvF;AAED,uEAAuE;AACvE,eAAO,MAAM,eAAe,EAAE,cAAqB,CAAC;AAEpD,eAAO,MAAM,mBAAmB,EAAE,iBAmBjC,CAAC"}