@cortexkit/opencode-openai-auth 1.3.0 → 1.5.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/bin.js +8 -8
- package/dist/{codex-request-B9D0e97Q.d.ts → codex-request-BjGT9yWg.d.ts} +36 -28
- package/dist/index-2p3gcjsa.js +69 -0
- package/dist/{index-m3ctrabv.js → index-2rw81dtm.js} +1 -1
- package/dist/index-2yszc75z.js +2 -0
- package/dist/{index-gn2b097f.js → index-3y453wg4.js} +7 -7
- package/dist/{index-bc89akak.js → index-4w0nsq5s.js} +1 -1
- package/dist/index-6fdmqk6p.js +90 -0
- package/dist/{index-xwvze96g.js → index-7vkn2e46.js} +1 -1
- package/dist/{index-2pgkrebq.js → index-djnfq652.js} +2 -2
- package/dist/{index-tddr876f.js → index-e416r885.js} +2 -2
- package/dist/index-fztcacjr.js +65 -0
- package/dist/index-gmmakhbt.js +2 -0
- package/dist/{index-ftkhhr6d.js → index-krcqvyqf.js} +1 -1
- package/dist/index-mhfja8yr.js +2 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.js +1 -1
- package/dist/{model-BdITTmeb.d.ts → model-BMq5SCUB.d.ts} +1 -1
- package/dist/{port-file-DbHIVC9a.d.ts → port-file-CjMFUWcX.d.ts} +1 -1
- package/dist/{protocol-MysYPAho.d.ts → protocol-BC3ndToM.d.ts} +1 -1
- package/dist/rpc/port-file.d.ts +1 -1
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +1 -1
- package/dist/rpc/protocol.js +1 -1
- package/dist/rpc/rpc-client.d.ts +3 -3
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/{sidebar-state-CVSok7zv.d.ts → sidebar-state-DfYyqiOH.d.ts} +55 -7
- package/dist/sidebar-state.d.ts +2 -2
- package/dist/sidebar-state.js +1 -1
- package/dist/token-util-ed4az5gg.js +1 -0
- package/dist/{token-z6qq7a1d.js → token-ycw7dyev.js} +1 -1
- package/dist/tui-preferences.js +1 -1
- package/dist/tui.d.ts +30 -3
- package/dist/v2/server.d.ts +2 -2
- package/dist/v2/server.js +2 -2
- package/package.json +5 -5
- package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +50 -5
- package/src/tui-compiled/raw/shared/0f77ebc3c914c9c6-custody.js +70 -1
- package/src/tui-compiled/raw/shared/169b2b4bf30909bc-consumer.js +104 -62
- package/src/tui-compiled/raw/shared/2577ec69f9973ae9-quota-normalize.js +45 -1
- package/src/tui-compiled/raw/shared/30f5bc480f31f51c-roster.js +172 -43
- package/src/tui-compiled/raw/shared/375d9cca2def037d-private-files.js +49 -3
- package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +3 -1
- package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +7 -9
- package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +2 -0
- package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +17 -3
- package/src/tui-compiled/raw/shared/754d6017f8e419cf-builtins.js +95 -13
- package/src/tui-compiled/raw/shared/81dddd203f1b92a3-quota-writer.js +253 -0
- package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +31 -6
- package/src/tui-compiled/raw/shared/abd062d633e29fd2-pool-routing.js +116 -12
- package/src/tui-compiled/raw/shared/b9588ad4725c1d10-enrollment.js +27 -0
- package/src/tui-compiled/raw/shared/bfbdd0f9aae56b47-pool-quota.js +15 -3
- package/src/tui-compiled/raw/shared/d0f0b6be200311af-insula.js +64 -2
- package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +2 -2
- package/src/tui-compiled/raw/shared/dbc61cd405941828-accounts.js +5 -4
- package/src/tui-compiled/raw/shared/dc9290c2a7552ba6-watch.js +2 -1
- package/src/tui-compiled/raw/shared/e8d3c8da776ef9cd-index.js +2 -1
- package/src/tui-compiled/raw/sidebar-state.js +32 -3
- package/src/tui-compiled/raw/tui/v2-dialog.js +2 -1
- package/src/tui-compiled/raw/tui.tsx +170 -26
- package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +50 -5
- package/src/tui-compiled/runtime/shared/0f77ebc3c914c9c6-custody.js +70 -1
- package/src/tui-compiled/runtime/shared/169b2b4bf30909bc-consumer.js +104 -62
- package/src/tui-compiled/runtime/shared/2577ec69f9973ae9-quota-normalize.js +45 -1
- package/src/tui-compiled/runtime/shared/30f5bc480f31f51c-roster.js +172 -43
- package/src/tui-compiled/runtime/shared/375d9cca2def037d-private-files.js +49 -3
- package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +3 -1
- package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +7 -9
- package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +2 -0
- package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +17 -3
- package/src/tui-compiled/runtime/shared/754d6017f8e419cf-builtins.js +95 -13
- package/src/tui-compiled/runtime/shared/81dddd203f1b92a3-quota-writer.js +253 -0
- package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +31 -6
- package/src/tui-compiled/runtime/shared/abd062d633e29fd2-pool-routing.js +116 -12
- package/src/tui-compiled/runtime/shared/b9588ad4725c1d10-enrollment.js +27 -0
- package/src/tui-compiled/runtime/shared/bfbdd0f9aae56b47-pool-quota.js +15 -3
- package/src/tui-compiled/runtime/shared/d0f0b6be200311af-insula.js +64 -2
- package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +2 -2
- package/src/tui-compiled/runtime/shared/dbc61cd405941828-accounts.js +5 -4
- package/src/tui-compiled/runtime/shared/dc9290c2a7552ba6-watch.js +2 -1
- package/src/tui-compiled/runtime/shared/e8d3c8da776ef9cd-index.js +2 -1
- package/src/tui-compiled/runtime/sidebar-state.js +32 -3
- package/src/tui-compiled/runtime/tui/v2-dialog.js +2 -1
- package/src/tui-compiled/runtime/tui.js +350 -219
- package/dist/index-1mbgsxsr.js +0 -2
- package/dist/index-4v2fs42x.js +0 -65
- package/dist/index-8x1aj7q3.js +0 -2
- package/dist/index-ex6nx3mq.js +0 -2
- package/dist/index-psjyexj3.js +0 -69
- package/dist/index-tn8x5tz8.js +0 -90
- package/dist/token-util-cf0nyh7n.js +0 -1
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
// reset clock. It is a tri-state: absent (never reported), a reading, or
|
|
15
15
|
// cleared (the provider reported that no budget exists).
|
|
16
16
|
//
|
|
17
|
+
// Provider-named counters and the prepaid credits record sit beside them too,
|
|
18
|
+
// each judged by its own observation time rather than the limits'.
|
|
19
|
+
//
|
|
17
20
|
// The map is plain JSON so the store can persist it without interpreting it.
|
|
18
21
|
/** The scope of a limit that binds every request regardless of model. */
|
|
19
22
|
export const ALL_SCOPE = 'all';
|
|
@@ -93,6 +96,18 @@ function isQuotaCounter(value) {
|
|
|
93
96
|
export function isQuotaCounters(value) {
|
|
94
97
|
return isRecord(value) && Object.values(value).every(isQuotaCounter);
|
|
95
98
|
}
|
|
99
|
+
function isBoolean(value) {
|
|
100
|
+
return typeof value === 'boolean';
|
|
101
|
+
}
|
|
102
|
+
/** True when `value` is a well-formed prepaid-credits record. */
|
|
103
|
+
export function isQuotaCredits(value) {
|
|
104
|
+
return (isRecord(value) &&
|
|
105
|
+
typeof value.hasCredits === 'boolean' &&
|
|
106
|
+
typeof value.unlimited === 'boolean' &&
|
|
107
|
+
optional(value, 'balance', isFiniteNumber) &&
|
|
108
|
+
optional(value, 'overageLimitReached', isBoolean) &&
|
|
109
|
+
isFiniteNumber(value.checkedAt));
|
|
110
|
+
}
|
|
96
111
|
/** Keep usable counters without letting one malformed count hide a quota map. */
|
|
97
112
|
export function readQuotaCounters(value) {
|
|
98
113
|
if (!isRecord(value))
|
|
@@ -102,20 +117,29 @@ export function readQuotaCounters(value) {
|
|
|
102
117
|
? undefined
|
|
103
118
|
: Object.fromEntries(entries);
|
|
104
119
|
}
|
|
105
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Decode persisted quota, dropping malformed counters or a malformed credits
|
|
122
|
+
* record but not limit or budget evidence.
|
|
123
|
+
*/
|
|
106
124
|
export function decodeQuotaMap(value) {
|
|
107
125
|
// A valid map is returned as the same object, so stored quota keeps the
|
|
108
|
-
// identity it had before counters existed
|
|
109
|
-
// counter is copied
|
|
126
|
+
// identity it had before counters and credits existed. A map with a
|
|
127
|
+
// malformed counter or credits record is copied with those malformed
|
|
128
|
+
// fields left out.
|
|
110
129
|
if (isQuotaMap(value))
|
|
111
130
|
return value;
|
|
112
131
|
if (!isRecord(value))
|
|
113
132
|
return undefined;
|
|
114
|
-
const { counters: rawCounters, ...rest } = value;
|
|
133
|
+
const { counters: rawCounters, credits: rawCredits, ...rest } = value;
|
|
115
134
|
if (!isQuotaMap(rest))
|
|
116
135
|
return undefined;
|
|
117
136
|
const counters = readQuotaCounters(rawCounters);
|
|
118
|
-
|
|
137
|
+
const credits = isQuotaCredits(rawCredits) ? rawCredits : undefined;
|
|
138
|
+
return {
|
|
139
|
+
...rest,
|
|
140
|
+
...(counters === undefined ? {} : { counters }),
|
|
141
|
+
...(credits === undefined ? {} : { credits }),
|
|
142
|
+
};
|
|
119
143
|
}
|
|
120
144
|
/**
|
|
121
145
|
* True when `value` is a well-formed quota map. Unrecognised top-level keys
|
|
@@ -136,5 +160,6 @@ export function isQuotaMap(value) {
|
|
|
136
160
|
seen.add(key);
|
|
137
161
|
}
|
|
138
162
|
return (optional(value, 'budget', isCreditBudgetEntry) &&
|
|
139
|
-
optional(value, 'counters', isQuotaCounters)
|
|
163
|
+
optional(value, 'counters', isQuotaCounters) &&
|
|
164
|
+
optional(value, 'credits', isQuotaCredits));
|
|
140
165
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
budgetExhaustedResetAt,
|
|
3
|
+
isQuotaCredits,
|
|
4
|
+
projectQuota
|
|
5
|
+
} from "./d5a8f1061ee2f114-index.js";
|
|
2
6
|
import {
|
|
3
7
|
admit,
|
|
4
8
|
decideStickyBreak,
|
|
5
9
|
orderForPlacement,
|
|
10
|
+
QUOTA_STALENESS_MS,
|
|
6
11
|
routeOrdered,
|
|
7
12
|
routeSticky
|
|
8
13
|
} from "./3e7d45a77c6ad4d5-index.js";
|
|
@@ -65,6 +70,70 @@ export function waitForFirstQuotaReading(input, waitMs = FIRST_QUOTA_READING_WAI
|
|
|
65
70
|
function isConfirmedExhaustion(refusal) {
|
|
66
71
|
return refusal.reason === "exhausted" || refusal.reason === "budget-spent";
|
|
67
72
|
}
|
|
73
|
+
export function creditsUsable(credits, now) {
|
|
74
|
+
if (!isQuotaCredits(credits))
|
|
75
|
+
return !1;
|
|
76
|
+
const record = credits;
|
|
77
|
+
if (!(now - record.checkedAt <= QUOTA_STALENESS_MS))
|
|
78
|
+
return !1;
|
|
79
|
+
if (record.overageLimitReached === !0)
|
|
80
|
+
return !1;
|
|
81
|
+
if (record.unlimited)
|
|
82
|
+
return !0;
|
|
83
|
+
return record.hasCredits && (record.balance === void 0 || record.balance > 0);
|
|
84
|
+
}
|
|
85
|
+
function creditsTiers(rows, input, admission) {
|
|
86
|
+
const passes = (id) => input.killswitch.get(id) !== !1, free = new Set(admission.admitted.filter((row) => row.lastPath !== !0 && passes(row.id)).map((row) => row.id)), refusals = new Map(admission.refused.map((refusal) => [refusal.id, refusal])), credits = rows.filter((row) => {
|
|
87
|
+
if (row.kind !== "oauth" || !passes(row.id))
|
|
88
|
+
return !1;
|
|
89
|
+
if (refusals.get(row.id)?.reason !== "exhausted")
|
|
90
|
+
return !1;
|
|
91
|
+
const projection = projectQuota(row.quota);
|
|
92
|
+
if (budgetExhaustedResetAt(projection, input.now))
|
|
93
|
+
return !1;
|
|
94
|
+
return creditsUsable(projection.credits, input.now);
|
|
95
|
+
}).map((row) => row.id);
|
|
96
|
+
return { free, credits };
|
|
97
|
+
}
|
|
98
|
+
function admitQuietly(input, rows = input.rows) {
|
|
99
|
+
return admit({
|
|
100
|
+
rows,
|
|
101
|
+
now: input.now,
|
|
102
|
+
rateLimitMarks: input.rateLimitMarks,
|
|
103
|
+
refreshBackoff: input.refreshBackoff,
|
|
104
|
+
requestPull: () => {}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
export function creditsFallbackIds(input) {
|
|
108
|
+
return [...creditsTiers(input.rows, input, admitQuietly(input)).credits];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class CreditsServing {
|
|
112
|
+
#ids = new Set;
|
|
113
|
+
served(input, id, log) {
|
|
114
|
+
const admission = admitQuietly(input), tiers = creditsTiers(input.rows, input, admission), credits = new Set(tiers.credits);
|
|
115
|
+
if (tiers.free.has(id)) {
|
|
116
|
+
this.#ids.clear();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const marked of [...this.#ids])
|
|
120
|
+
if (!credits.has(marked))
|
|
121
|
+
this.#ids.delete(marked);
|
|
122
|
+
if (!credits.has(id) || this.#ids.has(id))
|
|
123
|
+
return;
|
|
124
|
+
this.#ids.add(id);
|
|
125
|
+
log?.info("serving requests on prepaid credits", {
|
|
126
|
+
accountId: id,
|
|
127
|
+
reason: "free quota used up"
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
has(id) {
|
|
131
|
+
return this.#ids.has(id);
|
|
132
|
+
}
|
|
133
|
+
ids() {
|
|
134
|
+
return new Set(this.#ids);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
68
137
|
export function blockFor(id, admission, killswitch) {
|
|
69
138
|
if (id === void 0)
|
|
70
139
|
return { reason: "no-credential" };
|
|
@@ -97,11 +166,13 @@ export function planOrdered(input) {
|
|
|
97
166
|
refreshBackoff: input.refreshBackoff,
|
|
98
167
|
now: input.now,
|
|
99
168
|
requestPull: input.requestPull
|
|
100
|
-
}), ids = orderForPlacement(input.rows.map((row) => row.id), placement, mainId), confirmed = new Set(route.admission.refused.filter((refusal) => isConfirmedExhaustion(refusal) && input.killswitch.get(refusal.id) !== !1).map((refusal) => refusal.id)), lastPath = ids.filter((id) => confirmed.has(id));
|
|
169
|
+
}), ids = orderForPlacement(input.rows.map((row) => row.id), placement, mainId), confirmed = new Set(route.admission.refused.filter((refusal) => isConfirmedExhaustion(refusal) && input.killswitch.get(refusal.id) !== !1).map((refusal) => refusal.id)), lastPath = ids.filter((id) => confirmed.has(id)), tiers = creditsTiers(input.rows, input, route.admission), free = route.order.filter((id) => tiers.free.has(id)), credits = ids.filter((id) => tiers.credits.includes(id));
|
|
101
170
|
if (!replayable) {
|
|
102
|
-
const hasMain = ids.includes(mainId), target = hasMain ? mainId : route.order[0] ?? lastPath[0];
|
|
171
|
+
const hasMain = ids.includes(mainId), target = hasMain ? mainId : free[0] ?? credits[0] ?? route.order[0] ?? lastPath[0];
|
|
103
172
|
if (target !== void 0 && route.order.includes(target))
|
|
104
173
|
return { kind: "send", order: [target], lastPath: !1 };
|
|
174
|
+
if (target !== void 0 && credits.includes(target))
|
|
175
|
+
return { kind: "send", order: [target], lastPath: !1 };
|
|
105
176
|
if (target !== void 0 && confirmed.has(target))
|
|
106
177
|
return { kind: "send", order: [target], lastPath: !0 };
|
|
107
178
|
return {
|
|
@@ -109,6 +180,16 @@ export function planOrdered(input) {
|
|
|
109
180
|
block: hasMain ? blockFor(mainId, route.admission, input.killswitch) : routeBlock(ids, route.admission, input.killswitch)
|
|
110
181
|
};
|
|
111
182
|
}
|
|
183
|
+
if (free.length > 0)
|
|
184
|
+
return { kind: "send", order: free, lastPath: !1 };
|
|
185
|
+
if (credits.length > 0) {
|
|
186
|
+
const rest = route.order.length > 0 ? route.order : lastPath;
|
|
187
|
+
return {
|
|
188
|
+
kind: "send",
|
|
189
|
+
order: [...credits, ...rest.filter((id) => !credits.includes(id))],
|
|
190
|
+
lastPath: !1
|
|
191
|
+
};
|
|
192
|
+
}
|
|
112
193
|
if (route.order.length > 0)
|
|
113
194
|
return { kind: "send", order: route.order, lastPath: !1 };
|
|
114
195
|
if (lastPath.length > 0)
|
|
@@ -160,23 +241,46 @@ function routePoolSticky(input, options, extra) {
|
|
|
160
241
|
});
|
|
161
242
|
}
|
|
162
243
|
export function selectStickyRow(input, options, exclude, pendingBytes) {
|
|
163
|
-
const route = routePoolSticky(input, options, { exclude, pendingBytes })
|
|
164
|
-
if (route.outcome !== "dispatch" || route.source === "pin")
|
|
165
|
-
return;
|
|
166
|
-
return {
|
|
244
|
+
const route = routePoolSticky(input, options, { exclude, pendingBytes }), rows = input.rows.filter((row) => !exclude.has(row.id)), tiers = creditsTiers(rows, input, route.admission), selected = route.outcome === "dispatch" && route.source !== "pin" ? {
|
|
167
245
|
accountId: route.accountId,
|
|
168
246
|
source: route.source,
|
|
169
247
|
...route.quotaCheckedAt === void 0 ? {} : { quotaCheckedAt: route.quotaCheckedAt }
|
|
248
|
+
} : void 0;
|
|
249
|
+
if (selected && tiers.free.has(selected.accountId))
|
|
250
|
+
return selected;
|
|
251
|
+
const credits = tiers.credits[0];
|
|
252
|
+
if (credits === void 0)
|
|
253
|
+
return selected;
|
|
254
|
+
const checkedAt = projectQuota(rows.find((row) => row.id === credits)?.quota).checkedAt;
|
|
255
|
+
return {
|
|
256
|
+
accountId: credits,
|
|
257
|
+
source: "mode-fallback",
|
|
258
|
+
...checkedAt === void 0 ? {} : { quotaCheckedAt: checkedAt }
|
|
170
259
|
};
|
|
171
260
|
}
|
|
172
261
|
export function routePinnedRow(input, options, id) {
|
|
173
|
-
const killed = input.killswitch.get(id) === !1,
|
|
262
|
+
const killed = input.killswitch.get(id) === !1, move = {
|
|
263
|
+
kind: "move",
|
|
264
|
+
reason: killed ? "killswitch" : "exhausted"
|
|
265
|
+
}, route = routePoolSticky(input, options, { pin: id }), tiers = creditsTiers(input.rows, input, route.admission), onCredits = tiers.credits.includes(id), othersOnCredits = tiers.credits.some((other) => other !== id);
|
|
174
266
|
if (route.outcome === "dispatch") {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
267
|
+
const toLastPath = !tiers.free.has(route.accountId);
|
|
268
|
+
if (route.pin.action === "assign") {
|
|
269
|
+
if (toLastPath && onCredits)
|
|
270
|
+
return { kind: "serve" };
|
|
271
|
+
return move;
|
|
272
|
+
}
|
|
273
|
+
if (route.accountId !== id)
|
|
274
|
+
return { kind: "detour" };
|
|
275
|
+
if (toLastPath && othersOnCredits)
|
|
276
|
+
return move;
|
|
277
|
+
return { kind: "serve" };
|
|
178
278
|
}
|
|
179
|
-
const refusal = route.admission.refused.find((entry) => entry.id === id);
|
|
279
|
+
const refusal = route.admission.refused.find((entry) => entry.id === id), confirmed = killed || refusal !== void 0 && isConfirmedExhaustion(refusal);
|
|
280
|
+
if (onCredits)
|
|
281
|
+
return { kind: "serve" };
|
|
282
|
+
if (othersOnCredits)
|
|
283
|
+
return confirmed ? move : { kind: "detour" };
|
|
180
284
|
if (!killed && refusal && isConfirmedExhaustion(refusal))
|
|
181
285
|
return { kind: "last-path" };
|
|
182
286
|
return { kind: "none" };
|
|
@@ -10,12 +10,22 @@ const ENROLLMENT_SCHEMA = 1;
|
|
|
10
10
|
const ENROLLMENT_FILE_MAX_BYTES = 16 * 1024;
|
|
11
11
|
const ENROLLMENT_LOCK_TTL_MS = 30_000;
|
|
12
12
|
const TOKEN_RE = /^[0-9a-f]{64}$/;
|
|
13
|
+
/**
|
|
14
|
+
* The code a vault without the enrollment ceremony answers a proposal with.
|
|
15
|
+
* Setup reports it as "this vault cannot connect plugins" instead of waiting.
|
|
16
|
+
*/
|
|
17
|
+
export const UNSUPPORTED_ENROLLMENT_CODE = 'unknown_method';
|
|
13
18
|
/**
|
|
14
19
|
* Codes the vault's closed enrollment-refusal vocabulary marks permanent. The
|
|
15
20
|
* client labels some module error frames transient/retry regardless, so the
|
|
16
21
|
* producer's code takes precedence over the client's action for these.
|
|
17
22
|
*/
|
|
18
23
|
export const TERMINAL_ENROLLMENT_CODES = new Set([
|
|
24
|
+
// The daemon's answer to an operation it does not implement: a vault built
|
|
25
|
+
// before the enrollment ceremony. It arrives as a transport Error frame with
|
|
26
|
+
// no class, which claustrum-client before 0.13.0 labels transient/retry, but
|
|
27
|
+
// asking again can never succeed.
|
|
28
|
+
UNSUPPORTED_ENROLLMENT_CODE,
|
|
19
29
|
'invalid_params',
|
|
20
30
|
'pending_exists',
|
|
21
31
|
'not_found',
|
|
@@ -638,6 +648,23 @@ export class ClaustrumEnrollmentManager {
|
|
|
638
648
|
retryCode: refusal.code,
|
|
639
649
|
};
|
|
640
650
|
}
|
|
651
|
+
// A vault without the ceremony cannot hold a request for this
|
|
652
|
+
// proposal, now or from an earlier run (the state has no request
|
|
653
|
+
// id). So nothing is blocked and nothing is in progress: remove the
|
|
654
|
+
// pending state, still under the ceremony lock, so status reads as
|
|
655
|
+
// "not enrolled" and a reset or a later run starts afresh.
|
|
656
|
+
if (refusal.code === UNSUPPORTED_ENROLLMENT_CODE) {
|
|
657
|
+
await lock.assertOwned();
|
|
658
|
+
await unlink(this.#paths.statePath).catch((cause) => {
|
|
659
|
+
if (cause.code !== 'ENOENT')
|
|
660
|
+
throw cause;
|
|
661
|
+
});
|
|
662
|
+
return {
|
|
663
|
+
state: 'unavailable',
|
|
664
|
+
proposedName: state.proposedName,
|
|
665
|
+
code: refusal.code,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
641
668
|
return this.#block(state, refusal.code);
|
|
642
669
|
}
|
|
643
670
|
}
|
|
@@ -42,6 +42,17 @@ function budgetFor(snapshot) {
|
|
|
42
42
|
return { kind: "cleared" };
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
+
function prepaidCreditsFor(value, checkedAt) {
|
|
46
|
+
if (!isRecord(value) || typeof value.hasCredits !== "boolean" || typeof value.unlimited !== "boolean")
|
|
47
|
+
return;
|
|
48
|
+
return {
|
|
49
|
+
hasCredits: value.hasCredits,
|
|
50
|
+
unlimited: value.unlimited,
|
|
51
|
+
...finite(value.balance) && value.balance >= 0 ? { balance: value.balance } : {},
|
|
52
|
+
...typeof value.overageLimitReached === "boolean" ? { overageLimitReached: value.overageLimitReached } : {},
|
|
53
|
+
checkedAt
|
|
54
|
+
};
|
|
55
|
+
}
|
|
45
56
|
export function observationFromSnapshot(snapshot, checkedAt, complete) {
|
|
46
57
|
if (!isRecord(snapshot) || !finite(checkedAt))
|
|
47
58
|
return;
|
|
@@ -53,15 +64,16 @@ export function observationFromSnapshot(snapshot, checkedAt, complete) {
|
|
|
53
64
|
else if (complete)
|
|
54
65
|
coverage.push({ label });
|
|
55
66
|
}
|
|
56
|
-
const budget = budgetFor(snapshot), credits = snapshot.resetCreditsAvailable, counters = typeof credits === "number" && Number.isSafeInteger(credits) && credits >= 0 ? { resetCredits: { value: credits, checkedAt } } : void 0;
|
|
57
|
-
if (readings.length === 0 && coverage.length === 0 && !budget && !counters)
|
|
67
|
+
const budget = budgetFor(snapshot), credits = snapshot.resetCreditsAvailable, counters = typeof credits === "number" && Number.isSafeInteger(credits) && credits >= 0 ? { resetCredits: { value: credits, checkedAt } } : void 0, prepaid = prepaidCreditsFor(snapshot.credits, checkedAt);
|
|
68
|
+
if (readings.length === 0 && coverage.length === 0 && !budget && !counters && !prepaid)
|
|
58
69
|
return;
|
|
59
70
|
return {
|
|
60
71
|
checkedAt,
|
|
61
72
|
...readings.length > 0 ? { readings } : {},
|
|
62
73
|
...coverage.length > 0 ? { coverage } : {},
|
|
63
74
|
...budget ? { budget } : {},
|
|
64
|
-
...counters ? { counters } : {}
|
|
75
|
+
...counters ? { counters } : {},
|
|
76
|
+
...prepaid ? { credits: prepaid } : {}
|
|
65
77
|
};
|
|
66
78
|
}
|
|
67
79
|
export function windowsFromQuotaMap(map) {
|
|
@@ -128,6 +128,9 @@ export function insulaUsageTransport(options) {
|
|
|
128
128
|
}
|
|
129
129
|
const MINUTE = 60_000;
|
|
130
130
|
const MAX_AGE = 4 * MINUTE;
|
|
131
|
+
// consumer-contract.md:331 refetches a slot after 60 s. Two such intervals
|
|
132
|
+
// bound our willingness to wait, not a claim that insula completed its sweep.
|
|
133
|
+
const COLD_WAIT = 2 * MINUTE;
|
|
131
134
|
const sources = new Map();
|
|
132
135
|
// Keep only the minute budget after the last owner closes, so project churn
|
|
133
136
|
// cannot reset the bound. Expired budgets are pruned on the next tick.
|
|
@@ -170,6 +173,9 @@ export class InsulaQuotaSource {
|
|
|
170
173
|
transport;
|
|
171
174
|
key;
|
|
172
175
|
#entries = [];
|
|
176
|
+
#available = false;
|
|
177
|
+
#completeProviders = new Set();
|
|
178
|
+
#coldSince = new Map();
|
|
173
179
|
/** Every usage.get, polled or watch-driven, shares this one slot. */
|
|
174
180
|
#reads = new CoalescedRead(async () => {
|
|
175
181
|
try {
|
|
@@ -178,13 +184,25 @@ export class InsulaQuotaSource {
|
|
|
178
184
|
!Array.isArray(reply.result) ||
|
|
179
185
|
reply.error !== undefined)
|
|
180
186
|
throw new Error('unavailable insula usage');
|
|
181
|
-
// Each response is a snapshot, not a delta.
|
|
182
|
-
// neither pending nor the entry count
|
|
187
|
+
// Each response is a snapshot, not a delta. Only completeProviders
|
|
188
|
+
// asserts completeness; neither pending nor the entry count does.
|
|
183
189
|
this.#entries = reply.result.filter((entry) => record(entry) && typeof entry.provider === 'string');
|
|
190
|
+
this.#available =
|
|
191
|
+
this.#entries.length === reply.result.length &&
|
|
192
|
+
(reply.completeProviders === undefined ||
|
|
193
|
+
(Array.isArray(reply.completeProviders) &&
|
|
194
|
+
reply.completeProviders.every((value) => typeof value === 'string')));
|
|
195
|
+
this.#completeProviders = new Set(this.#available && Array.isArray(reply.completeProviders)
|
|
196
|
+
? reply.completeProviders
|
|
197
|
+
: []);
|
|
198
|
+
for (const provider of this.#completeProviders)
|
|
199
|
+
this.#coldSince.delete(provider);
|
|
184
200
|
}
|
|
185
201
|
catch {
|
|
186
202
|
// Refusal is absence, even if the previous reading was still young.
|
|
187
203
|
this.#entries = [];
|
|
204
|
+
this.#available = false;
|
|
205
|
+
this.#completeProviders.clear();
|
|
188
206
|
}
|
|
189
207
|
}, () => { });
|
|
190
208
|
#reader = new ChangeNoticeReader();
|
|
@@ -391,6 +409,50 @@ export class InsulaQuotaSource {
|
|
|
391
409
|
}
|
|
392
410
|
return undefined;
|
|
393
411
|
}
|
|
412
|
+
/** Cache-only background lookup; current() retains its original miss behavior. */
|
|
413
|
+
lookup(provider, accountId, now) {
|
|
414
|
+
if (!this.#available)
|
|
415
|
+
return { miss: 'unreachable' };
|
|
416
|
+
const entry = this.#entries.find((value) => value.provider === provider &&
|
|
417
|
+
typeof value.account === 'string' &&
|
|
418
|
+
value.account.trim() === accountId);
|
|
419
|
+
// An unlabelled error describes the provider's credential path, not an
|
|
420
|
+
// unrelated labelled account. Never borrow another account's verdict.
|
|
421
|
+
const errorClass = (entry ??
|
|
422
|
+
this.#entries.find((value) => value.provider === provider &&
|
|
423
|
+
value.account === undefined &&
|
|
424
|
+
value.errorClass !== undefined))?.errorClass;
|
|
425
|
+
if (errorClass === 'upstream_failed')
|
|
426
|
+
return { miss: 'upstream-failed' };
|
|
427
|
+
if (errorClass === 'credential_absent' ||
|
|
428
|
+
errorClass === 'credential_unusable' ||
|
|
429
|
+
errorClass === 'credential_rejected')
|
|
430
|
+
return { miss: 'credential' };
|
|
431
|
+
if (errorClass !== undefined)
|
|
432
|
+
return { miss: 'unreachable' };
|
|
433
|
+
if (entry) {
|
|
434
|
+
const fetchedAt = typeof entry.fetchedAt === 'string'
|
|
435
|
+
? Date.parse(entry.fetchedAt)
|
|
436
|
+
: Number.NaN;
|
|
437
|
+
if (!record(entry.usage) || !Number.isFinite(fetchedAt))
|
|
438
|
+
return { miss: 'unreachable' };
|
|
439
|
+
const reading = {
|
|
440
|
+
entry,
|
|
441
|
+
context: {
|
|
442
|
+
fetchedAt,
|
|
443
|
+
...(typeof entry.source === 'string' && { source: entry.source }),
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
return now >= fetchedAt + MAX_AGE || entry.stale !== undefined
|
|
447
|
+
? { miss: 'stale', reading }
|
|
448
|
+
: { reading };
|
|
449
|
+
}
|
|
450
|
+
if (this.#completeProviders.has(provider))
|
|
451
|
+
return { miss: 'absent' };
|
|
452
|
+
const since = this.#coldSince.get(provider) ?? now;
|
|
453
|
+
this.#coldSince.set(provider, since);
|
|
454
|
+
return { miss: now - since >= COLD_WAIT ? 'unreachable' : 'cold' };
|
|
455
|
+
}
|
|
394
456
|
}
|
|
395
457
|
export function acquireInsulaQuotaSource(transport, tokenPath) {
|
|
396
458
|
const key = JSON.stringify([
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { quotaCodec } from "./053cc17b5419261e-codec.js";
|
|
2
|
-
export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, quotaWindowParts, } from "./0332701351b3eeec-format.js";
|
|
3
|
-
export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from "./905bffc0220f43d0-map.js";
|
|
2
|
+
export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaCreditsText, quotaTextParts, quotaWindowName, quotaWindowParts, } from "./0332701351b3eeec-format.js";
|
|
3
|
+
export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaCredits, isQuotaMap, } from "./905bffc0220f43d0-map.js";
|
|
4
4
|
export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from "./4f36d333d081d040-merge.js";
|
|
5
5
|
export { budgetExhaustedResetAt, futureResetAt, projectQuota, readsExhausted, } from "./39c3cb05738fcf6d-projection.js";
|
|
@@ -154,13 +154,14 @@ function normalizeQuota(value) {
|
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
156
|
const credits = value.credits;
|
|
157
|
-
if (isRecord(credits) && typeof credits.hasCredits === "boolean" && typeof credits.unlimited === "boolean" && typeof credits.overageLimitReached === "boolean") {
|
|
158
|
-
const balance = Number(credits.balance);
|
|
157
|
+
if (isRecord(credits) && typeof credits.hasCredits === "boolean" && typeof credits.unlimited === "boolean" && (credits.overageLimitReached === void 0 || typeof credits.overageLimitReached === "boolean")) {
|
|
158
|
+
const balance = Number(credits.balance), checkedAt = typeof credits.checkedAt === "number" ? credits.checkedAt : Number.NaN;
|
|
159
159
|
quota.credits = {
|
|
160
160
|
hasCredits: credits.hasCredits,
|
|
161
161
|
unlimited: credits.unlimited,
|
|
162
|
-
overageLimitReached: credits.overageLimitReached,
|
|
163
|
-
...Number.isFinite(balance) ? { balance } : {}
|
|
162
|
+
...typeof credits.overageLimitReached === "boolean" ? { overageLimitReached: credits.overageLimitReached } : {},
|
|
163
|
+
...Number.isFinite(balance) ? { balance } : {},
|
|
164
|
+
...Number.isFinite(checkedAt) ? { checkedAt } : {}
|
|
164
165
|
};
|
|
165
166
|
}
|
|
166
167
|
return Object.keys(quota).length ? quota : void 0;
|
|
@@ -109,8 +109,9 @@ export class ScopedRosterWatch {
|
|
|
109
109
|
this.#reads = new CoalescedRead(async () => {
|
|
110
110
|
const force = this.#force;
|
|
111
111
|
this.#force = false;
|
|
112
|
+
// A read is only ever requested by a notice, which set #latestView.
|
|
112
113
|
if (force || this.#latestView !== this.#options.view())
|
|
113
|
-
await this.#options.discover();
|
|
114
|
+
await this.#options.discover(this.#latestView ?? '');
|
|
114
115
|
}, (error) => {
|
|
115
116
|
if (!this.#stopped)
|
|
116
117
|
this.#options.onError?.(error);
|
|
@@ -14,7 +14,8 @@ export { ClaustrumConsumerError, } from "./ca0fccaba1d87af4-errors.js";
|
|
|
14
14
|
export { assertHostSlotMatchesMode, assertNotCustodyPlaceholder, CUSTODY_PLACEHOLDER_PREFIX, classifyHostSlot, custodyPlaceholder, custodyPlaceholderKey, isCustodyPlaceholder, isCustodyPlaceholderValue, } from "./d9d2892a4c371301-host-slot.js";
|
|
15
15
|
export { acquireInsulaQuotaSource, } from "./d0f0b6be200311af-insula.js";
|
|
16
16
|
export { acceptAccount, declineAccount, isDeclined, } from "./7c140680fc81be92-interlock.js";
|
|
17
|
-
export {
|
|
17
|
+
export { drainVaultQuota } from "./81dddd203f1b92a3-quota-writer.js";
|
|
18
|
+
export { acceptVaultRoute, DEFAULT_ROUTE_PREFIX, declineVaultRoute, mutateVaultRoster, projectVaultRoster, readVaultRoster, recordVaultAccountQuota, recordVaultQuota, refreshVaultRoster, reorderVaultRoster, resolveVaultPrimary, vaultRoutingRows, } from "./30f5bc480f31f51c-roster.js";
|
|
18
19
|
export { ChangeNoticeReader, RESUBSCRIBE_BACKOFF_MS, SAFETY_POLL_MS, ScopedRosterWatch, WATCH_RETRY_MS, } from "./dc9290c2a7552ba6-watch.js";
|
|
19
20
|
/**
|
|
20
21
|
* Connect the client that lists and fetches this consumer's vault credentials
|
|
@@ -2,13 +2,35 @@ import {
|
|
|
2
2
|
formatQuota,
|
|
3
3
|
isQuotaStale,
|
|
4
4
|
projectQuota,
|
|
5
|
+
quotaCreditsText,
|
|
5
6
|
quotaWindowName
|
|
6
7
|
} from "./shared/d5a8f1061ee2f114-index.js";
|
|
7
8
|
import { orderForPlacement } from "./shared/3e7d45a77c6ad4d5-index.js";
|
|
8
9
|
import {
|
|
10
|
+
creditsUsable,
|
|
9
11
|
orderedPlacement,
|
|
10
12
|
vaultMainRouteId
|
|
11
13
|
} from "./shared/abd062d633e29fd2-pool-routing.js";
|
|
14
|
+
export function accountCreditsText(quota) {
|
|
15
|
+
const credits = quota?.credits;
|
|
16
|
+
if (credits === null || typeof credits !== "object")
|
|
17
|
+
return;
|
|
18
|
+
const { hasCredits, unlimited, balance } = credits;
|
|
19
|
+
if (typeof hasCredits !== "boolean" || typeof unlimited !== "boolean")
|
|
20
|
+
return;
|
|
21
|
+
return quotaCreditsText({
|
|
22
|
+
hasCredits,
|
|
23
|
+
unlimited,
|
|
24
|
+
...typeof balance === "number" ? { balance } : {}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function accountServesOnCredits(quota, now = Date.now()) {
|
|
28
|
+
if (!exhaustedQuotaResetAt(quota, now))
|
|
29
|
+
return !1;
|
|
30
|
+
if (spendControlExhaustedResetAt(quota, now))
|
|
31
|
+
return !1;
|
|
32
|
+
return creditsUsable(quota?.credits, now);
|
|
33
|
+
}
|
|
12
34
|
const QUOTA_WINDOW_KEYS = ["primary", "secondary"], LEGACY_WINDOW_MINUTES = {
|
|
13
35
|
primary: 300,
|
|
14
36
|
secondary: 10080
|
|
@@ -153,7 +175,7 @@ export function normalizeSidebarState(raw) {
|
|
|
153
175
|
};
|
|
154
176
|
} else
|
|
155
177
|
main = { quota: null, killed: !1 };
|
|
156
|
-
const fallbacks = normalizeAccountList(r.fallbacks), vaultAccounts = Array.isArray(r.vaultAccounts) ? normalizeAccountList(r.vaultAccounts) : void 0, activeId = typeof r.activeId === "string" ? r.activeId : void 0, route = typeof r.route === "string" ? r.route : DEFAULT_SIDEBAR_STATE.route, lastUpdated = typeof r.lastUpdated === "number" ? r.lastUpdated : 0, planType = typeof r.planType === "string" ? r.planType : void 0,
|
|
178
|
+
const fallbacks = normalizeAccountList(r.fallbacks), vaultAccounts = Array.isArray(r.vaultAccounts) ? normalizeAccountList(r.vaultAccounts) : void 0, activeId = typeof r.activeId === "string" ? r.activeId : void 0, route = typeof r.route === "string" ? r.route : DEFAULT_SIDEBAR_STATE.route, lastUpdated = typeof r.lastUpdated === "number" ? r.lastUpdated : 0, planType = typeof r.planType === "string" ? r.planType : void 0, activeRouting = normalizeActiveRouting(r.activeRouting), stickyAssignments = normalizeStickyAssignments(r.stickyAssignments);
|
|
157
179
|
return {
|
|
158
180
|
main,
|
|
159
181
|
fallbacks,
|
|
@@ -163,7 +185,6 @@ export function normalizeSidebarState(raw) {
|
|
|
163
185
|
...activeRouting !== void 0 ? { activeRouting } : {},
|
|
164
186
|
...stickyAssignments !== void 0 ? { stickyAssignments } : {},
|
|
165
187
|
...planType !== void 0 ? { planType } : {},
|
|
166
|
-
...credits !== void 0 ? { credits } : {},
|
|
167
188
|
...r.accountPool === !0 ? { accountPool: !0 } : {},
|
|
168
189
|
...vaultAccounts !== void 0 ? { vaultAccounts } : {}
|
|
169
190
|
};
|
|
@@ -491,12 +512,18 @@ function mergeSpendControl(fresher, older) {
|
|
|
491
512
|
return;
|
|
492
513
|
return older.spendControl;
|
|
493
514
|
}
|
|
515
|
+
function fresherCredits(incoming, existing) {
|
|
516
|
+
if (!incoming || !existing)
|
|
517
|
+
return incoming ?? existing;
|
|
518
|
+
const at = (credits) => typeof credits.checkedAt === "number" && Number.isFinite(credits.checkedAt) ? credits.checkedAt : Number.NEGATIVE_INFINITY;
|
|
519
|
+
return at(existing) > at(incoming) ? existing : incoming;
|
|
520
|
+
}
|
|
494
521
|
function mergeQuotaByWindow(incoming, existing) {
|
|
495
522
|
if (!incoming)
|
|
496
523
|
return existing;
|
|
497
524
|
if (!existing)
|
|
498
525
|
return incoming;
|
|
499
|
-
const incomingAt = latestQuotaCheckedAt(incoming), existingAt = latestQuotaCheckedAt(existing), existingSnapshotIsFresher = existingAt !== void 0 && (incomingAt === void 0 || existingAt > incomingAt), primary = freshestWindow(incoming.primary, existing.primary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), secondary = freshestWindow(incoming.secondary, existing.secondary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), spendControl = existingSnapshotIsFresher ? mergeSpendControl(existing, incoming) : mergeSpendControl(incoming, existing), spendControlCleared = spendControl === void 0 && (incoming.spendControlCleared === !0 || existing.spendControlCleared === !0);
|
|
526
|
+
const incomingAt = latestQuotaCheckedAt(incoming), existingAt = latestQuotaCheckedAt(existing), existingSnapshotIsFresher = existingAt !== void 0 && (incomingAt === void 0 || existingAt > incomingAt), primary = freshestWindow(incoming.primary, existing.primary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), secondary = freshestWindow(incoming.secondary, existing.secondary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), spendControl = existingSnapshotIsFresher ? mergeSpendControl(existing, incoming) : mergeSpendControl(incoming, existing), spendControlCleared = spendControl === void 0 && (incoming.spendControlCleared === !0 || existing.spendControlCleared === !0), credits = fresherCredits(incoming.credits, existing.credits);
|
|
500
527
|
let checkedAt;
|
|
501
528
|
for (const stamp of [
|
|
502
529
|
finiteWindowCheckedAt(primary),
|
|
@@ -507,6 +534,7 @@ function mergeQuotaByWindow(incoming, existing) {
|
|
|
507
534
|
const {
|
|
508
535
|
spendControl: _incomingSpendControl,
|
|
509
536
|
spendControlCleared: _incomingCleared,
|
|
537
|
+
credits: _incomingCredits,
|
|
510
538
|
...incomingRest
|
|
511
539
|
} = incoming;
|
|
512
540
|
return {
|
|
@@ -515,6 +543,7 @@ function mergeQuotaByWindow(incoming, existing) {
|
|
|
515
543
|
secondary,
|
|
516
544
|
...spendControl !== void 0 ? { spendControl } : {},
|
|
517
545
|
...spendControlCleared ? { spendControlCleared: !0 } : {},
|
|
546
|
+
...credits !== void 0 ? { credits } : {},
|
|
518
547
|
checkedAt: checkedAt ?? incoming.checkedAt
|
|
519
548
|
};
|
|
520
549
|
}
|
|
@@ -22,7 +22,8 @@ export async function openControlDialog(dialog, initial, apply, active = () => !
|
|
|
22
22
|
const choices = [
|
|
23
23
|
...section.items.filter((item) => item.actions.length > 0).map((item) => ({
|
|
24
24
|
title: item.label,
|
|
25
|
-
description: inlineDescription(item.
|
|
25
|
+
description: inlineDescription(item.detail),
|
|
26
|
+
...item.status ? { footer: item.status } : {},
|
|
26
27
|
value: { itemId: item.id, actions: item.actions }
|
|
27
28
|
})),
|
|
28
29
|
...section.actions.map((action) => ({
|