openlimiter 1.2.0 → 1.3.1
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/README.md +45 -16
- package/dist/bin.js +53 -4
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +110 -11
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1064 -64
- package/dist/cli.js.map +1 -1
- package/dist/codex-device-login.d.ts +87 -0
- package/dist/codex-device-login.d.ts.map +1 -0
- package/dist/codex-device-login.js +291 -0
- package/dist/codex-device-login.js.map +1 -0
- package/dist/config.d.ts +44 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +129 -7
- package/dist/config.js.map +1 -1
- package/dist/hub-auth.d.ts +137 -0
- package/dist/hub-auth.d.ts.map +1 -0
- package/dist/hub-auth.js +280 -0
- package/dist/hub-auth.js.map +1 -0
- package/dist/hub-sync.d.ts +144 -0
- package/dist/hub-sync.d.ts.map +1 -0
- package/dist/hub-sync.js +370 -0
- package/dist/hub-sync.js.map +1 -0
- package/dist/hub.d.ts +60 -0
- package/dist/hub.d.ts.map +1 -0
- package/dist/hub.js +178 -0
- package/dist/hub.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/ingest.d.ts +26 -0
- package/dist/ingest.d.ts.map +1 -1
- package/dist/ingest.js +116 -1
- package/dist/ingest.js.map +1 -1
- package/dist/qr.d.ts.map +1 -1
- package/dist/qr.js +1 -0
- package/dist/qr.js.map +1 -1
- package/dist/session.d.ts +46 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +121 -0
- package/dist/session.js.map +1 -0
- package/dist/statusline.d.ts +14 -0
- package/dist/statusline.d.ts.map +1 -1
- package/dist/statusline.js +258 -19
- package/dist/statusline.js.map +1 -1
- package/dist/terminal.d.ts +109 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +974 -0
- package/dist/terminal.js.map +1 -0
- package/package.json +4 -4
- package/dist/serve.d.ts +0 -170
- package/dist/serve.d.ts.map +0 -1
- package/dist/serve.js +0 -533
- package/dist/serve.js.map +0 -1
package/dist/hub-sync.js
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Building, sending and resuming one sync upload.
|
|
3
|
+
*
|
|
4
|
+
* The envelope shape is the version 2 contract the hub reads, the same one
|
|
5
|
+
* `packages/core/fixtures/sync-envelope-v2.json` shows and the desktop's own
|
|
6
|
+
* Rust implementation (`apps/desktop/src-tauri/src/account.rs`) already
|
|
7
|
+
* speaks. That file is read only reference here: this module is its own
|
|
8
|
+
* implementation of the same cursor rules, in this package's own vocabulary,
|
|
9
|
+
* because the CLI has no keyring and no long lived process to carry a
|
|
10
|
+
* resumable upload in memory between runs. The cursor lives on disk instead,
|
|
11
|
+
* exactly like the config and the session do.
|
|
12
|
+
*
|
|
13
|
+
* Three rules carry the whole contract. The sequence is a compare and swap
|
|
14
|
+
* against the hub's own cursor: `previous_sequence` is what this device
|
|
15
|
+
* believes the hub holds, and `sequence` is one past it. An upload whose
|
|
16
|
+
* reply never arrived is retried as the SAME upload, same event id and same
|
|
17
|
+
* bytes, because the hub deduplicates on the event id and a retry carrying
|
|
18
|
+
* different bytes under it would be refused rather than deduplicated. And a
|
|
19
|
+
* conflict that names the hub's own current sequence is adopted once and
|
|
20
|
+
* retried, never fought.
|
|
21
|
+
*/
|
|
22
|
+
import { randomUUID, createHash } from "node:crypto";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { canonicalJson, freshness, readJsonFileSafely, resolveStateDirectory, writeFileAtomically } from "@openlimiter/core";
|
|
25
|
+
import { parseHubJson, syncSnapshotsRequest } from "./hub.js";
|
|
26
|
+
export const SYNC_CURSOR_FILE_NAME = "openlimiter-sync-cursor.json";
|
|
27
|
+
/** The contract version this build speaks, and nothing else. */
|
|
28
|
+
export const SYNC_SCHEMA_VERSION = 2;
|
|
29
|
+
/**
|
|
30
|
+
* What this build calls itself on the wire.
|
|
31
|
+
*
|
|
32
|
+
* A constant, not a read of `package.json`: this module ships inside a
|
|
33
|
+
* published package, and a runtime file read is one more thing that can fail
|
|
34
|
+
* inside a command a status line depends on. See `acquire/identity.ts` in the
|
|
35
|
+
* core package for the same choice, made for the same reason.
|
|
36
|
+
*/
|
|
37
|
+
export const SYNC_CLIENT_VERSION = "1.3.1";
|
|
38
|
+
/** Most rows one envelope may carry, usage and spend counted together. */
|
|
39
|
+
export const SYNC_MAX_ROWS = 128;
|
|
40
|
+
/** How long an unresolved upload may still be retried as itself. */
|
|
41
|
+
export const SYNC_RESUME_WINDOW_HOURS = 23;
|
|
42
|
+
const ACCOUNT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/u;
|
|
43
|
+
/** The meter a budget reading uses, PERCENT unit and all: never a usage window. */
|
|
44
|
+
const EXCLUDED_USAGE_METER = "API_BUDGET_PERCENT";
|
|
45
|
+
/**
|
|
46
|
+
* A row's account id, exactly as the desktop's own cache reader decides it.
|
|
47
|
+
*
|
|
48
|
+
* See `usage_samples_from_cache` in account.rs: absent is "default", the
|
|
49
|
+
* ordinary single account case, but present and not shaped like an account id
|
|
50
|
+
* is not quietly corrected to "default" either, because merging a malformed
|
|
51
|
+
* id into the shared default bucket is its own wrong reading. Null here means
|
|
52
|
+
* the row this id belongs to is dropped, not defaulted.
|
|
53
|
+
*/
|
|
54
|
+
function accountIdOf(snapshot) {
|
|
55
|
+
if (snapshot.accountId === undefined)
|
|
56
|
+
return "default";
|
|
57
|
+
return ACCOUNT_ID_PATTERN.test(snapshot.accountId) ? snapshot.accountId : null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Every usage row this device would upload, from the cache it already writes
|
|
61
|
+
* for the terminal and the tray.
|
|
62
|
+
*
|
|
63
|
+
* Only `PERCENT` rows are usage: a credit or token count is not a usage
|
|
64
|
+
* fraction and does not belong beside one. `API_BUDGET_PERCENT` is excluded
|
|
65
|
+
* too, porting the same rule `usage_samples_from_cache` in account.rs reads
|
|
66
|
+
* its own cache by: it is a budget reading at PERCENT unit, not a usage
|
|
67
|
+
* window, and sending it is exactly what gets a whole envelope rejected
|
|
68
|
+
* rather than the one row dropped, since the hub does not recognise it as
|
|
69
|
+
* usage at all. The Rust reader excludes the Moonshot provider by name too,
|
|
70
|
+
* as a second, belt and suspenders check; nothing here reproduces that one,
|
|
71
|
+
* because it cannot fire on this side of the boundary. Moonshot's own budget
|
|
72
|
+
* reading already carries provider `KIMI` in this build's own vocabulary
|
|
73
|
+
* (`PROVIDER_CODES` in types.ts has no `MOONSHOT` at all, and
|
|
74
|
+
* `normalizeMeter` refuses the whole reading for any provider string outside
|
|
75
|
+
* that set before a `Snapshot` is ever built), so the meter check above is
|
|
76
|
+
* this side's whole answer, not half of one. A row that fails a bound,
|
|
77
|
+
* including an unreadable account id, is dropped rather than repaired,
|
|
78
|
+
* matching the same rule the desktop reads its own cache by.
|
|
79
|
+
*/
|
|
80
|
+
export function usageSamplesFromSnapshots(snapshots, now) {
|
|
81
|
+
const rows = [];
|
|
82
|
+
for (const snapshot of snapshots) {
|
|
83
|
+
if (snapshot.unit !== "PERCENT")
|
|
84
|
+
continue;
|
|
85
|
+
if (snapshot.meter === EXCLUDED_USAGE_METER)
|
|
86
|
+
continue;
|
|
87
|
+
if (!Number.isFinite(snapshot.value) || snapshot.value < 0 || snapshot.value > 100)
|
|
88
|
+
continue;
|
|
89
|
+
const accountId = accountIdOf(snapshot);
|
|
90
|
+
if (accountId === null)
|
|
91
|
+
continue;
|
|
92
|
+
rows.push({
|
|
93
|
+
account_id: accountId,
|
|
94
|
+
provider: snapshot.provider,
|
|
95
|
+
meter: snapshot.meter,
|
|
96
|
+
window_id: snapshot.meter,
|
|
97
|
+
usage_percent: snapshot.value,
|
|
98
|
+
reset_at: snapshot.resetAt,
|
|
99
|
+
observed_at: snapshot.observedAt,
|
|
100
|
+
stale: freshness(snapshot.observedAt, snapshot.expiresAt, now) !== "fresh"
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return rows.slice(0, SYNC_MAX_ROWS);
|
|
104
|
+
}
|
|
105
|
+
/** A deterministic, opaque row identifier, stable across resends. */
|
|
106
|
+
function stableSourceId(seed) {
|
|
107
|
+
const hash = createHash("sha256").update(seed).digest("hex");
|
|
108
|
+
return [hash.slice(0, 8), hash.slice(8, 12), hash.slice(12, 16), hash.slice(16, 20), hash.slice(20, 32)]
|
|
109
|
+
.join("-");
|
|
110
|
+
}
|
|
111
|
+
function monthStartDate(observedAt) {
|
|
112
|
+
const parsed = new Date(observedAt);
|
|
113
|
+
if (Number.isNaN(parsed.getTime()))
|
|
114
|
+
return observedAt.slice(0, 10);
|
|
115
|
+
return new Date(Date.UTC(parsed.getUTCFullYear(), parsed.getUTCMonth(), 1))
|
|
116
|
+
.toISOString()
|
|
117
|
+
.slice(0, 10);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Every spend row the cache has enough to build.
|
|
121
|
+
*
|
|
122
|
+
* This build tracks a point in time reading, not a month to date total with a
|
|
123
|
+
* forecast: those live in the desktop's dedicated spend engine, which this
|
|
124
|
+
* package does not have. So every row this function builds states its period
|
|
125
|
+
* honestly as "the start of this month through the moment of this upload"
|
|
126
|
+
* and never claims a forecast or a completed period it did not compute.
|
|
127
|
+
* `usedAmount`, `limitAmount` and `currency` travel together or not at all
|
|
128
|
+
* (see `Snapshot` in the core package), which is what this reads as "the
|
|
129
|
+
* cache has one".
|
|
130
|
+
*/
|
|
131
|
+
/** The providers the hub accepts an API spend sample for (mirrors API_PROVIDERS in the server contract); every other provider's spend stays local, otherwise one row would void the whole envelope. */
|
|
132
|
+
export const SYNC_API_SPEND_PROVIDERS = new Set(["OPENAI", "ANTHROPIC", "XAI", "OPENROUTER"]);
|
|
133
|
+
export function apiSpendSamplesFromSnapshots(snapshots, envelopeObservedAt) {
|
|
134
|
+
const rows = [];
|
|
135
|
+
for (const snapshot of snapshots) {
|
|
136
|
+
if (!SYNC_API_SPEND_PROVIDERS.has(snapshot.provider))
|
|
137
|
+
continue;
|
|
138
|
+
if (snapshot.usedAmount === undefined ||
|
|
139
|
+
snapshot.limitAmount === undefined ||
|
|
140
|
+
snapshot.currency === undefined) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!Number.isFinite(snapshot.usedAmount) || snapshot.usedAmount < 0)
|
|
144
|
+
continue;
|
|
145
|
+
if (!Number.isFinite(snapshot.limitAmount) || snapshot.limitAmount < 0)
|
|
146
|
+
continue;
|
|
147
|
+
const accountId = accountIdOf(snapshot);
|
|
148
|
+
if (accountId === null)
|
|
149
|
+
continue;
|
|
150
|
+
rows.push({
|
|
151
|
+
source_id: stableSourceId(snapshot.provider + ":" + accountId),
|
|
152
|
+
account_id: accountId,
|
|
153
|
+
provider: snapshot.provider,
|
|
154
|
+
key_label: snapshot.accountLabel ?? accountId,
|
|
155
|
+
month: monthStartDate(snapshot.observedAt),
|
|
156
|
+
spend_usd: snapshot.usedAmount,
|
|
157
|
+
budget_usd: snapshot.limitAmount,
|
|
158
|
+
source_period: [monthStartDate(snapshot.observedAt) + "T00:00:00.000Z", envelopeObservedAt],
|
|
159
|
+
currency_source: "PROVIDER_USD",
|
|
160
|
+
raw_unit_scale: 1,
|
|
161
|
+
forecast_date: null,
|
|
162
|
+
forecast_input: null,
|
|
163
|
+
period_complete: false
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return rows.slice(0, SYNC_MAX_ROWS);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* One envelope from one set of readings.
|
|
170
|
+
*
|
|
171
|
+
* A reading observed after the envelope itself, which a clock stepping
|
|
172
|
+
* backwards between the cache write and this call would produce, is pulled
|
|
173
|
+
* back to the envelope's own timestamp rather than dropped. See
|
|
174
|
+
* `build_envelope` in `account.rs` for the same rule.
|
|
175
|
+
*/
|
|
176
|
+
export function buildSyncEnvelope(identity, usageSamples, apiSpendSamples) {
|
|
177
|
+
const ceiling = Date.parse(identity.observedAt);
|
|
178
|
+
const usage = usageSamples.map((row) => {
|
|
179
|
+
const observed = Date.parse(row.observed_at);
|
|
180
|
+
const later = !Number.isFinite(observed) || (Number.isFinite(ceiling) && observed > ceiling);
|
|
181
|
+
return later ? { ...row, observed_at: identity.observedAt } : row;
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
event_id: identity.eventId,
|
|
185
|
+
device_id: identity.deviceId,
|
|
186
|
+
previous_sequence: identity.previousSequence,
|
|
187
|
+
sequence: identity.previousSequence + 1,
|
|
188
|
+
observed_at: identity.observedAt,
|
|
189
|
+
client_version: SYNC_CLIENT_VERSION,
|
|
190
|
+
schema_version: SYNC_SCHEMA_VERSION,
|
|
191
|
+
usage_samples: usage,
|
|
192
|
+
api_spend_samples: apiSpendSamples
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function envelopeDigest(envelope) {
|
|
196
|
+
return createHash("sha256").update(canonicalJson(envelope)).digest("hex");
|
|
197
|
+
}
|
|
198
|
+
function isRecord(value) {
|
|
199
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
200
|
+
}
|
|
201
|
+
function normalizeCursor(value, deviceId) {
|
|
202
|
+
const empty = { version: 1, deviceId, sequence: 0 };
|
|
203
|
+
if (!isRecord(value))
|
|
204
|
+
return empty;
|
|
205
|
+
if (value["version"] !== 1 || value["deviceId"] !== deviceId)
|
|
206
|
+
return empty;
|
|
207
|
+
const sequence = value["sequence"];
|
|
208
|
+
if (typeof sequence !== "number" || !Number.isInteger(sequence) || sequence < 0)
|
|
209
|
+
return empty;
|
|
210
|
+
const pendingEventId = value["pendingEventId"];
|
|
211
|
+
const pendingSequence = value["pendingSequence"];
|
|
212
|
+
const pendingObservedAt = value["pendingObservedAt"];
|
|
213
|
+
const pendingDigest = value["pendingDigest"];
|
|
214
|
+
const pendingComplete = typeof pendingEventId === "string" &&
|
|
215
|
+
typeof pendingSequence === "number" &&
|
|
216
|
+
typeof pendingObservedAt === "string" &&
|
|
217
|
+
typeof pendingDigest === "string";
|
|
218
|
+
if (!pendingComplete)
|
|
219
|
+
return { version: 1, deviceId, sequence };
|
|
220
|
+
return {
|
|
221
|
+
version: 1,
|
|
222
|
+
deviceId,
|
|
223
|
+
sequence,
|
|
224
|
+
pendingEventId,
|
|
225
|
+
pendingSequence,
|
|
226
|
+
pendingObservedAt,
|
|
227
|
+
pendingDigest
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
async function loadCursor(directory, deviceId) {
|
|
231
|
+
const result = await readJsonFileSafely(path.join(directory, SYNC_CURSOR_FILE_NAME));
|
|
232
|
+
return normalizeCursor(result.ok ? result.value : null, deviceId);
|
|
233
|
+
}
|
|
234
|
+
async function saveCursor(directory, cursor) {
|
|
235
|
+
await writeFileAtomically(path.join(directory, SYNC_CURSOR_FILE_NAME), canonicalJson(cursor));
|
|
236
|
+
}
|
|
237
|
+
function cursorAwaiting(cursor, envelope) {
|
|
238
|
+
return {
|
|
239
|
+
version: 1,
|
|
240
|
+
deviceId: cursor.deviceId,
|
|
241
|
+
sequence: cursor.sequence,
|
|
242
|
+
pendingEventId: envelope.event_id,
|
|
243
|
+
pendingSequence: envelope.sequence,
|
|
244
|
+
pendingObservedAt: envelope.observed_at,
|
|
245
|
+
pendingDigest: envelopeDigest(envelope)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function cursorSettled(cursor, sequence) {
|
|
249
|
+
return { version: 1, deviceId: cursor.deviceId, sequence };
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The upload still in flight, when nothing about the readings has changed.
|
|
253
|
+
*
|
|
254
|
+
* See `resumable_upload` in `account.rs`: the same event id, the same
|
|
255
|
+
* sequence and the same bytes are what the hub deduplicates on, so a retry
|
|
256
|
+
* has to rebuild the identical envelope or mint a fresh event id instead of
|
|
257
|
+
* reusing this one under different bytes.
|
|
258
|
+
*/
|
|
259
|
+
function resumableEnvelope(cursor, usageSamples, apiSpendSamples, now) {
|
|
260
|
+
if (cursor.pendingEventId === undefined ||
|
|
261
|
+
cursor.pendingSequence === undefined ||
|
|
262
|
+
cursor.pendingObservedAt === undefined ||
|
|
263
|
+
cursor.pendingDigest === undefined) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
if (cursor.pendingSequence !== cursor.sequence + 1)
|
|
267
|
+
return null;
|
|
268
|
+
const observedAtMs = Date.parse(cursor.pendingObservedAt);
|
|
269
|
+
const nowMs = Date.parse(now);
|
|
270
|
+
if (!Number.isFinite(observedAtMs) || !Number.isFinite(nowMs))
|
|
271
|
+
return null;
|
|
272
|
+
if (nowMs - observedAtMs >= SYNC_RESUME_WINDOW_HOURS * 3_600_000)
|
|
273
|
+
return null;
|
|
274
|
+
const candidate = buildSyncEnvelope({
|
|
275
|
+
deviceId: cursor.deviceId,
|
|
276
|
+
eventId: cursor.pendingEventId,
|
|
277
|
+
previousSequence: cursor.sequence,
|
|
278
|
+
observedAt: cursor.pendingObservedAt
|
|
279
|
+
}, usageSamples, apiSpendSamples);
|
|
280
|
+
return envelopeDigest(candidate) === cursor.pendingDigest ? candidate : null;
|
|
281
|
+
}
|
|
282
|
+
function classifySyncReply(status, body, sentSequence) {
|
|
283
|
+
if (status === 200 && body["accepted"] === true) {
|
|
284
|
+
const sequence = typeof body["sequence"] === "number" ? body["sequence"] : sentSequence;
|
|
285
|
+
const tier = typeof body["tier"] === "string" ? body["tier"] : null;
|
|
286
|
+
return { kind: "accepted", sequence, tier };
|
|
287
|
+
}
|
|
288
|
+
if (status === 409) {
|
|
289
|
+
const currentSequence = body["current_sequence"];
|
|
290
|
+
return typeof currentSequence === "number"
|
|
291
|
+
? { kind: "reconcile", sequence: currentSequence }
|
|
292
|
+
: { kind: "rejected" };
|
|
293
|
+
}
|
|
294
|
+
if (status >= 400 && status < 500)
|
|
295
|
+
return { kind: "rejected" };
|
|
296
|
+
return { kind: "unavailable" };
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* One upload, on the version 2 contract, with the same three way ending the
|
|
300
|
+
* hub's cursor rule requires: accepted advances the cursor, a reply that
|
|
301
|
+
* never arrived leaves the same upload pending for the next run, and a
|
|
302
|
+
* conflict names the hub's own sequence and is retried once.
|
|
303
|
+
*/
|
|
304
|
+
export async function runSync(options) {
|
|
305
|
+
const usageSamples = usageSamplesFromSnapshots(options.snapshots, options.now);
|
|
306
|
+
const room = Math.max(0, SYNC_MAX_ROWS - usageSamples.length);
|
|
307
|
+
const apiSpendSamples = apiSpendSamplesFromSnapshots(options.snapshots, options.now).slice(0, room);
|
|
308
|
+
if (usageSamples.length === 0 && apiSpendSamples.length === 0) {
|
|
309
|
+
return { kind: "nothing_to_sync" };
|
|
310
|
+
}
|
|
311
|
+
let cursor = await loadCursor(options.directory, options.deviceId);
|
|
312
|
+
let resumed = resumableEnvelope(cursor, usageSamples, apiSpendSamples, options.now);
|
|
313
|
+
for (let round = 0; round < 2; round += 1) {
|
|
314
|
+
const envelope = resumed ?? buildSyncEnvelope({
|
|
315
|
+
deviceId: options.deviceId,
|
|
316
|
+
eventId: randomUUID(),
|
|
317
|
+
previousSequence: cursor.sequence,
|
|
318
|
+
observedAt: options.now
|
|
319
|
+
}, usageSamples, apiSpendSamples);
|
|
320
|
+
resumed = null;
|
|
321
|
+
cursor = cursorAwaiting(cursor, envelope);
|
|
322
|
+
await saveCursor(options.directory, cursor);
|
|
323
|
+
const request = syncSnapshotsRequest(options.environment, options.token, envelope);
|
|
324
|
+
if (request === null)
|
|
325
|
+
return { kind: "rejected" };
|
|
326
|
+
let reply;
|
|
327
|
+
try {
|
|
328
|
+
reply = await options.transport(request);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
try {
|
|
332
|
+
/* The first failure is a lost reply, not a refusal, so the exact
|
|
333
|
+
same bytes go again under the same event id. */
|
|
334
|
+
reply = await options.transport(request);
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
return { kind: "unavailable" };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (reply.status === 401)
|
|
341
|
+
return { kind: "revoked" };
|
|
342
|
+
const outcome = classifySyncReply(reply.status, parseHubJson(reply.body) ?? {}, envelope.sequence);
|
|
343
|
+
if (outcome.kind === "accepted") {
|
|
344
|
+
cursor = cursorSettled(cursor, outcome.sequence);
|
|
345
|
+
await saveCursor(options.directory, cursor);
|
|
346
|
+
return { kind: "accepted", rows: usageSamples.length + apiSpendSamples.length, tier: outcome.tier };
|
|
347
|
+
}
|
|
348
|
+
if (outcome.kind === "reconcile") {
|
|
349
|
+
cursor = cursorSettled(cursor, outcome.sequence);
|
|
350
|
+
await saveCursor(options.directory, cursor);
|
|
351
|
+
if (round === 0)
|
|
352
|
+
continue;
|
|
353
|
+
return { kind: "rejected" };
|
|
354
|
+
}
|
|
355
|
+
if (outcome.kind === "rejected") {
|
|
356
|
+
cursor = cursorSettled(cursor, cursor.sequence);
|
|
357
|
+
await saveCursor(options.directory, cursor);
|
|
358
|
+
return { kind: "rejected" };
|
|
359
|
+
}
|
|
360
|
+
/* Unavailable: the cursor stays exactly as awaiting left it, so the next
|
|
361
|
+
run resumes this same upload. */
|
|
362
|
+
return { kind: "unavailable" };
|
|
363
|
+
}
|
|
364
|
+
return { kind: "unavailable" };
|
|
365
|
+
}
|
|
366
|
+
/** The state directory's default, exported so a caller need not repeat it. */
|
|
367
|
+
export function defaultSyncDirectory() {
|
|
368
|
+
return resolveStateDirectory();
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=hub-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-sync.js","sourceRoot":"","sources":["../src/hub-sync.ts"],"names":[],"mappings":"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"}
|
package/dist/hub.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** The project this build talks to, absent a build time override. */
|
|
2
|
+
export declare const DEFAULT_HUB_URL = "https://dsaonzonizvxtxgclwud.supabase.co";
|
|
3
|
+
/** The hub's publishable key: public by design, shipped in every client, overridable through OPENLIMITER_SUPABASE_ANON_KEY. */
|
|
4
|
+
export declare const DEFAULT_HUB_PUBLISHABLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRzYW9uem9uaXp2eHR4Z2Nsd3VkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODczMzQ2NjUsImV4cCI6MjEwMjkxMDY2NX0._HoDeczRScWuN5B_xXUlPFfxGbGj6Je3UodVgVsXjQA";
|
|
5
|
+
/** One request's total budget, connect to last body byte. */
|
|
6
|
+
export declare const HUB_TIMEOUT_MILLISECONDS = 15000;
|
|
7
|
+
/** Largest response body accepted before this build stops reading it. */
|
|
8
|
+
export declare const MAX_HUB_RESPONSE_BYTES = 1048576;
|
|
9
|
+
/** Largest request body this build will ever send to the hub. */
|
|
10
|
+
export declare const MAX_HUB_REQUEST_BYTES = 131072;
|
|
11
|
+
/**
|
|
12
|
+
* The hub's base address: the project host by default, or any address an
|
|
13
|
+
* override explicitly names, https only either way.
|
|
14
|
+
*
|
|
15
|
+
* An unset override is the project host, always https because the constant
|
|
16
|
+
* above is. A set override is honoured for whatever host it names, which is
|
|
17
|
+
* what a non production build or a test needs, but never over plain http: a
|
|
18
|
+
* device flow's code, a sync upload and a renewal's refresh credential all
|
|
19
|
+
* travel here, and an override that quietly downgraded any one of those to
|
|
20
|
+
* http would be a credential leak nobody asked for. An override that fails
|
|
21
|
+
* this bar, http or simply unreadable as a URL, is treated the same as one
|
|
22
|
+
* that was never set.
|
|
23
|
+
*/
|
|
24
|
+
export declare function hubBaseUrl(environment: Readonly<Record<string, string | undefined>>): string;
|
|
25
|
+
/** The publishable key: the environment override when set, the shipped default otherwise, and the literal off disables the hub. */
|
|
26
|
+
export declare function hubAnonKey(environment: Readonly<Record<string, string | undefined>>): string;
|
|
27
|
+
/** Whether the hub has enough configuration to be asked anything at all. */
|
|
28
|
+
export declare function hubConfigured(environment: Readonly<Record<string, string | undefined>>): boolean;
|
|
29
|
+
export type HubEndpointId = "cli_login" | "grant_renew" | "sync_snapshots";
|
|
30
|
+
export interface HubRequest {
|
|
31
|
+
readonly endpoint: HubEndpointId;
|
|
32
|
+
readonly url: string;
|
|
33
|
+
readonly method: "POST";
|
|
34
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
35
|
+
readonly body: string;
|
|
36
|
+
}
|
|
37
|
+
export interface HubReply {
|
|
38
|
+
readonly status: number;
|
|
39
|
+
readonly body: string;
|
|
40
|
+
}
|
|
41
|
+
export type HubTransport = (request: HubRequest) => Promise<HubReply>;
|
|
42
|
+
/** The device flow's first call: mint a user code and a device code. */
|
|
43
|
+
export declare function cliLoginStartRequest(environment: Readonly<Record<string, string | undefined>>): HubRequest | null;
|
|
44
|
+
/** The device flow's poll: ask whether somebody has approved the code yet. */
|
|
45
|
+
export declare function cliLoginPollRequest(environment: Readonly<Record<string, string | undefined>>, deviceCode: string): HubRequest | null;
|
|
46
|
+
/** Trade a refresh credential for a new token, rotating the credential. */
|
|
47
|
+
export declare function grantRenewRequest(environment: Readonly<Record<string, string | undefined>>, refreshCredential: string): HubRequest | null;
|
|
48
|
+
/** Upload one sync envelope under the signed in account. */
|
|
49
|
+
export declare function syncSnapshotsRequest(environment: Readonly<Record<string, string | undefined>>, token: string, envelope: unknown): HubRequest | null;
|
|
50
|
+
/**
|
|
51
|
+
* The transport the product uses, built on the runtime's own fetch.
|
|
52
|
+
*
|
|
53
|
+
* Redirects are refused, the body is bounded before it is decoded, and the
|
|
54
|
+
* whole request lives under one deadline, exactly as the acquisition
|
|
55
|
+
* transport in the core package does it.
|
|
56
|
+
*/
|
|
57
|
+
export declare function createFetchHubTransport(fetchImplementation?: typeof globalThis.fetch): HubTransport;
|
|
58
|
+
/** Parse a hub response body as JSON, or hand back nothing readable. */
|
|
59
|
+
export declare function parseHubJson(body: string): Record<string, unknown> | null;
|
|
60
|
+
//# sourceMappingURL=hub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"AAmBA,qEAAqE;AACrE,eAAO,MAAM,eAAe,6CAA6C,CAAC;AAE1E,+HAA+H;AAC/H,eAAO,MAAM,2BAA2B,qNAAqN,CAAC;AAE9P,6DAA6D;AAC7D,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,UAAY,CAAC;AAEhD,iEAAiE;AACjE,eAAO,MAAM,qBAAqB,SAAU,CAAC;AAU7C;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,MAAM,CAS5F;AAED,mIAAmI;AACnI,wBAAgB,UAAU,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,MAAM,CAI5F;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,OAAO,CAEhG;AAED,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAQ3E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AA+BtE,wEAAwE;AACxE,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GACxD,UAAU,GAAG,IAAI,CAEnB;AAKD,8EAA8E;AAC9E,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EACzD,UAAU,EAAE,MAAM,GACjB,UAAU,GAAG,IAAI,CAGnB;AAKD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EACzD,iBAAiB,EAAE,MAAM,GACxB,UAAU,GAAG,IAAI,CAMnB;AAKD,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EACzD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,GAChB,UAAU,GAAG,IAAI,CAGnB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,mBAAmB,GAAE,OAAO,UAAU,CAAC,KAAwB,GAC9D,YAAY,CAwBd;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAUzE"}
|
package/dist/hub.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one hosted surface the CLI ever talks to: OpenLimiter's own hub.
|
|
3
|
+
*
|
|
4
|
+
* Closed the same way the acquisition transport in the core package is closed:
|
|
5
|
+
* one function per endpoint, a fixed method and a fixed shape, and a transport
|
|
6
|
+
* that is handed a request built from that list rather than a caller supplied
|
|
7
|
+
* URL. Nothing here reaches a provider's own API; that is the acquisition
|
|
8
|
+
* path's job. This is the sign in, the token renewal and the sync upload, and
|
|
9
|
+
* nothing else.
|
|
10
|
+
*
|
|
11
|
+
* The hub's address is a public project reference, not a secret, and travels
|
|
12
|
+
* as a constant. The anon key is a Supabase publishable key, which is meant to
|
|
13
|
+
* ship inside a client, but this build was not handed a literal value for it,
|
|
14
|
+
* so it is read from the environment at run time. A build with no key
|
|
15
|
+
* configured answers every hub command with "not configured" rather than
|
|
16
|
+
* sending a request with an empty credential.
|
|
17
|
+
*/
|
|
18
|
+
import { OPENLIMITER_USER_AGENT } from "@openlimiter/core";
|
|
19
|
+
/** The project this build talks to, absent a build time override. */
|
|
20
|
+
export const DEFAULT_HUB_URL = "https://dsaonzonizvxtxgclwud.supabase.co";
|
|
21
|
+
/** The hub's publishable key: public by design, shipped in every client, overridable through OPENLIMITER_SUPABASE_ANON_KEY. */
|
|
22
|
+
export const DEFAULT_HUB_PUBLISHABLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRzYW9uem9uaXp2eHR4Z2Nsd3VkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODczMzQ2NjUsImV4cCI6MjEwMjkxMDY2NX0._HoDeczRScWuN5B_xXUlPFfxGbGj6Je3UodVgVsXjQA";
|
|
23
|
+
/** One request's total budget, connect to last body byte. */
|
|
24
|
+
export const HUB_TIMEOUT_MILLISECONDS = 15_000;
|
|
25
|
+
/** Largest response body accepted before this build stops reading it. */
|
|
26
|
+
export const MAX_HUB_RESPONSE_BYTES = 1_048_576;
|
|
27
|
+
/** Largest request body this build will ever send to the hub. */
|
|
28
|
+
export const MAX_HUB_REQUEST_BYTES = 131_072;
|
|
29
|
+
function envValue(environment, key) {
|
|
30
|
+
const value = environment[key];
|
|
31
|
+
return value === undefined ? "" : value;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The hub's base address: the project host by default, or any address an
|
|
35
|
+
* override explicitly names, https only either way.
|
|
36
|
+
*
|
|
37
|
+
* An unset override is the project host, always https because the constant
|
|
38
|
+
* above is. A set override is honoured for whatever host it names, which is
|
|
39
|
+
* what a non production build or a test needs, but never over plain http: a
|
|
40
|
+
* device flow's code, a sync upload and a renewal's refresh credential all
|
|
41
|
+
* travel here, and an override that quietly downgraded any one of those to
|
|
42
|
+
* http would be a credential leak nobody asked for. An override that fails
|
|
43
|
+
* this bar, http or simply unreadable as a URL, is treated the same as one
|
|
44
|
+
* that was never set.
|
|
45
|
+
*/
|
|
46
|
+
export function hubBaseUrl(environment) {
|
|
47
|
+
const configured = envValue(environment, "OPENLIMITER_SUPABASE_URL");
|
|
48
|
+
if (configured === "")
|
|
49
|
+
return DEFAULT_HUB_URL;
|
|
50
|
+
try {
|
|
51
|
+
if (new URL(configured).protocol !== "https:")
|
|
52
|
+
return DEFAULT_HUB_URL;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return DEFAULT_HUB_URL;
|
|
56
|
+
}
|
|
57
|
+
return configured;
|
|
58
|
+
}
|
|
59
|
+
/** The publishable key: the environment override when set, the shipped default otherwise, and the literal off disables the hub. */
|
|
60
|
+
export function hubAnonKey(environment) {
|
|
61
|
+
const configured = envValue(environment, "OPENLIMITER_SUPABASE_ANON_KEY");
|
|
62
|
+
if (configured === "off")
|
|
63
|
+
return "";
|
|
64
|
+
return configured === "" ? DEFAULT_HUB_PUBLISHABLE_KEY : configured;
|
|
65
|
+
}
|
|
66
|
+
/** Whether the hub has enough configuration to be asked anything at all. */
|
|
67
|
+
export function hubConfigured(environment) {
|
|
68
|
+
return hubAnonKey(environment).length > 0;
|
|
69
|
+
}
|
|
70
|
+
const HUB_FUNCTION_PATHS = {
|
|
71
|
+
cli_login: "/functions/v1/cli-login",
|
|
72
|
+
grant_renew: "/functions/v1/pro-service",
|
|
73
|
+
sync_snapshots: "/functions/v1/sync-snapshots"
|
|
74
|
+
};
|
|
75
|
+
function baseHeaders(anonKey) {
|
|
76
|
+
return {
|
|
77
|
+
apikey: anonKey,
|
|
78
|
+
"content-type": "application/json",
|
|
79
|
+
accept: "application/json",
|
|
80
|
+
"user-agent": OPENLIMITER_USER_AGENT
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function buildRequest(environment, endpoint, body, bearer) {
|
|
84
|
+
if (!hubConfigured(environment))
|
|
85
|
+
return null;
|
|
86
|
+
const encoded = JSON.stringify(body);
|
|
87
|
+
if (encoded.length > MAX_HUB_REQUEST_BYTES)
|
|
88
|
+
return null;
|
|
89
|
+
const headers = baseHeaders(hubAnonKey(environment));
|
|
90
|
+
if (bearer !== undefined)
|
|
91
|
+
headers["authorization"] = "Bearer " + bearer;
|
|
92
|
+
return {
|
|
93
|
+
endpoint,
|
|
94
|
+
url: hubBaseUrl(environment).replace(/\/$/u, "") + HUB_FUNCTION_PATHS[endpoint],
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers,
|
|
97
|
+
body: encoded
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** The device flow's first call: mint a user code and a device code. */
|
|
101
|
+
export function cliLoginStartRequest(environment) {
|
|
102
|
+
return buildRequest(environment, "cli_login", { action: "start" });
|
|
103
|
+
}
|
|
104
|
+
/** Shape of a device code this build will still poll with. */
|
|
105
|
+
const DEVICE_CODE_PATTERN = /^[A-Za-z0-9_.-]{8,256}$/u;
|
|
106
|
+
/** The device flow's poll: ask whether somebody has approved the code yet. */
|
|
107
|
+
export function cliLoginPollRequest(environment, deviceCode) {
|
|
108
|
+
if (!DEVICE_CODE_PATTERN.test(deviceCode))
|
|
109
|
+
return null;
|
|
110
|
+
return buildRequest(environment, "cli_login", { action: "poll", device_code: deviceCode });
|
|
111
|
+
}
|
|
112
|
+
/** Shape of a refresh credential this build will still spend. */
|
|
113
|
+
const REFRESH_CREDENTIAL_PATTERN = /^[\x21-\x7E]{16,4096}$/u;
|
|
114
|
+
/** Trade a refresh credential for a new token, rotating the credential. */
|
|
115
|
+
export function grantRenewRequest(environment, refreshCredential) {
|
|
116
|
+
if (!REFRESH_CREDENTIAL_PATTERN.test(refreshCredential))
|
|
117
|
+
return null;
|
|
118
|
+
return buildRequest(environment, "grant_renew", {
|
|
119
|
+
action: "grant_renew",
|
|
120
|
+
refresh_credential: refreshCredential
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/** Shape of a bearer token this build will still send. */
|
|
124
|
+
const BEARER_TOKEN_PATTERN = /^[\x21-\x7E]{16,32768}$/u;
|
|
125
|
+
/** Upload one sync envelope under the signed in account. */
|
|
126
|
+
export function syncSnapshotsRequest(environment, token, envelope) {
|
|
127
|
+
if (!BEARER_TOKEN_PATTERN.test(token))
|
|
128
|
+
return null;
|
|
129
|
+
return buildRequest(environment, "sync_snapshots", envelope, token);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The transport the product uses, built on the runtime's own fetch.
|
|
133
|
+
*
|
|
134
|
+
* Redirects are refused, the body is bounded before it is decoded, and the
|
|
135
|
+
* whole request lives under one deadline, exactly as the acquisition
|
|
136
|
+
* transport in the core package does it.
|
|
137
|
+
*/
|
|
138
|
+
export function createFetchHubTransport(fetchImplementation = globalThis.fetch) {
|
|
139
|
+
return async (request) => {
|
|
140
|
+
const controller = new AbortController();
|
|
141
|
+
const timer = setTimeout(() => controller.abort(), HUB_TIMEOUT_MILLISECONDS);
|
|
142
|
+
try {
|
|
143
|
+
const response = await fetchImplementation(request.url, {
|
|
144
|
+
method: request.method,
|
|
145
|
+
headers: { ...request.headers },
|
|
146
|
+
redirect: "error",
|
|
147
|
+
signal: controller.signal,
|
|
148
|
+
body: request.body
|
|
149
|
+
});
|
|
150
|
+
const buffer = await response.arrayBuffer();
|
|
151
|
+
if (buffer.byteLength > MAX_HUB_RESPONSE_BYTES) {
|
|
152
|
+
return { status: response.status, body: "" };
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
status: response.status,
|
|
156
|
+
body: new TextDecoder("utf-8", { fatal: false }).decode(buffer)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
clearTimeout(timer);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/** Parse a hub response body as JSON, or hand back nothing readable. */
|
|
165
|
+
export function parseHubJson(body) {
|
|
166
|
+
if (body.length === 0 || body.length > MAX_HUB_RESPONSE_BYTES)
|
|
167
|
+
return null;
|
|
168
|
+
try {
|
|
169
|
+
const parsed = JSON.parse(body);
|
|
170
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
171
|
+
? parsed
|
|
172
|
+
: null;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=hub.js.map
|
package/dist/hub.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub.js","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"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"}
|